Performing a traceroute is a very common task for network operations.

In this post I share a simple solution I implemented to make it easier to read the command output.

Table of Contents

Problem

I have noticed many requests to the NetOps team for clarification of traceroute output. The Service Desk team does not understand the traceroute between sites on the client’s network.

Solution

Being a big fan of NetBox I onboarded the client’s network some time ago, most of the network prefixes and IP addresses are already in the database.

Leveraging the database of IPs and names, I created a simple script to collect all IP entries in Netbox that have a non-empty DNS field.

The result is used to compile the hosts file of the local machine so you can see the names and IPs in the traceroute.

Script

The script is a mix of Python and bash. First, a Python script reads all the IP addresses in NetBox that have a non-empty DNS value and writes the output to hosts.netbox.

Then the Python script is executed by a bash script that takes care of updating the local hosts file.


flowchart LR; A ==> |read IP/names| D[NETBOX] A[START] ==>|write to| B[hosts.netbox] B ==> |merge to| C["/etc/hosts"]

Bash

Let’s start exporting NetBox IP, API and the name of the VRF:

export NBURL=https://localhost/
export NBAPI=01234567890123456789
export NBVRF=NAME-OF-VRF

The bash script:

  • lines 5 to 11 create a backup of the current hosts file, if not already exists
  • line 14 executes the Python script to collect the IP/name pairs and saves them in hosts.netbox
  • lines 17 to 28 merge the original *hosts.old file with hosts.netbox into hosts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash
set -e

# Check if hosts.old does not exist
if [ ! -f /etc/hosts.old ]; then
    # Copy hosts to hosts.old
    sudo sh -c 'cp /etc/hosts /etc/hosts.old'
    echo "hosts file has been copied to hosts.old"
else
    echo "hosts.old already exists"
fi

echo "running netbox import..."
python3 getNetBoxIPnames.py
echo "done"

if [ -f /etc/hosts.old ]; then
    if [ -f hosts.netbox ]; then
        # Copy hosts to hosts.old
        echo "merging to hosts..."
        sudo sh -c 'cat hosts.netbox >> /etc/hosts'
        echo "OK netbox hosts merged to hosts"
    else
        echo "FAIL hosts.netbox missing"
    fi
else
    echo "FAIL hosts.old missing"
fi

Python

Notice on line 17 the use of the REST api filter dns_name__empty=False .

The script getNetBoxIPnames.py requires pynetbox module.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import pynetbox
import urllib3
import os
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# set env vars before executing the script
netbox_url = os.environ.get('NBURL',"")
api_token  = os.environ.get('NBAPI',"")
vrf_name   = os.environ.get('NBVRF',"") # the name of the VRF in string
if not (netbox_url and api_token and vrf_name):
    raise Exception("missing NBURL/NBAPINBVRF env vars")
nb = pynetbox.api(netbox_url, token=api_token)
nb.http_session.verify = False
f = open("hosts.netbox", "w")
vrf = nb.ipam.vrfs.get(name=vrf_name)
if vrf:
    # Get all IP addresses in the VRF with a DNS name
    ip_addresses = nb.ipam.ip_addresses.filter(vrf_id=vrf.id, dns_name__empty=False)
    print(f"collected {len(ip_addresses)} ip/names from netbox")
    for ip in ip_addresses:
        f.write(f"{ip.address.split('/')[0]} {ip.dns_name}\n")
else:
    print(f"FAIL: VRF '{vrf_name}' not found")
    f.close()
    sys.exit(1)
f.close()

Run it

Export the necessary data:

export NBURL=https://localhost/
export NBAPI=01234567890123456789
export NBVRF=MY-WONDERFUL-VRF

Run the script with sudo, it needs permission to write the hosts file:

./getNBnames.sh

Now run your favorite traceroute tool (mine is mtr ) and enjoy the output (obfuscated).

 mtr -b 9.9.9.9
 
 Host                                                          
 1. _gateway (10.x.x.129)                                  
 2. (waiting for reply)
 3. it-dc1-aci-l3out (10.199.199.1)                                  
 4. it-dc1-wsw01-vlan199 (10.x.x.60)              
 5. it-dc1-fw-int-1-vdom-vpn-nat (10.x.x.251)                     
 6. it-dc1-fw--vdom-frontend-p2p-to-vdom-vpn-nat (192.x.x.130)
 7. it-dc1-sw-fe-2 (192.x.x.3)                      
 8. ce-isp1-internet-rtr-2 (212.x.x.29)           
 9. pe-isp1-internet (.x.x.129)                                    
10. rtr1.mix-it.net (217.x.x.51)                           
11. rtr2 (109.x.x.196)                        
12. dns9.quad9.net (9.9.9.9)    

Final notes

I recommend using explicit, long DNS names that include the host name and interface, sometimes with a short description to help ServiceDesk/NetOps teams.

If the DNS field is already used and must remain within defined standards, NetBox supports the use of custom fields .

DNS limits (according to RFC 1035 ):

  • labels: 63 octets or less
  • names: 255 octets or less

There is plenty of room for self-explaining entries.

Where’s the code?

The code is available in GitHub . Enjoy!

Feedback

I’m always looking to improve and would love your feedback. If you spot any errors, have suggestions, or just want to share your thoughts, reach out to me directly on X.

Your input helps me make this blog better for everyone!

Support Ifconfig.it

If you enjoy the technical content I share and find it valuable, consider supporting the blog.

PAYPAL.ME