How to use Netbox API to get data from a local script?

881 views Asked by At

Similar to This, I've installed NetBox via docker containers. I wrote some python code(API) which take an IP range(local of course) as input and scan the range via Nmap python module and provide the result nice and neat as output.

What I wanna do is I want NetBox to have a section that I can place an IP range as an input, then it contacts my API and takes the result back and store them in its database (suppose results are organized)

For example say I have a local subnet 192.168.104.0/24 and there are several devices(each has attributs like MAC address, hostname, whether a vm or not...) I give this range to Netbox and it places these devices in the database using the result my API provides for it.

1

There are 1 answers

1
Scraps23 On

You can achieve this by utilizing NetBox's custom script feature.

These scripts are written in Python, so you can move your nmap feature into this script. It would also be callable via NetBox's API at <netbox-url>/api/extra/scripts/<<script-file-name>.<script-class-name>

Below is a functional example that I've tested in my development NetBox environment. Note that it includes logic for grabbing hostnames embedded in it.

from extras.scripts import Script, StringVar
from ipam.models import IPAddress
import nmap
import re

class IPAddressScanner(Script):
    target_range = StringVar(
        regex='^(?:\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
    )
    class Meta:
        name = "IP Address Scanner"
        description = "Uses the Python NMAP module to scan and import hostnames to NetBox"
        field_order = ['target_range']


    def run(self, data, commit):
        prefix = re.search(r'\d{1,2}$', data['target_range']).group()
        
        nm = nmap.PortScannerYield()
        for host, response in nm.scan(hosts=data['target_range'], arguments='-sL'):
            hostname = response['scan'][host]['hostnames'][0]['name']
            
            if hostname:
                cidr = f'{host}/{prefix}'
                try:
                    IPAddress.objects.get(address=cidr)
                    self.log_info(f'{host}: Existing object found.')
                except IPAddress.DoesNotExist:
                    new_address = IPAddress(
                        address = cidr,
                        dns_name = hostname,
                        description = f'Automatically pulled by {self.Meta.name}',
                    )
                    if commit:
                        new_address.save()


You can take this a step further by adding a custom webhook to your NetBox application so that it performs this against every newly-added IP address range. I'll include the specifics to get it working below, every other field is up to you to decide.

Content types=IPAM>Prefix
Events=[Creations]
URL = https://<<netbox>>/api/extras/scripts/<<script-file-name>.<script-class-name>/
Note the does not include the file extension
HTTP method=POST
HTTP content type=application/json
Additional headers=Authorization: Token <api-token>
Body template={"data": {"target_range": "{{ data.prefix }}"},"commit": true}

Things to note:

  • Make sure you install python-nmap in the NetBox venv and add it to /opt/netbox/local_requirements.txt
  • Make sure the hostname you use in the webhook is accessible from NetBox itself
  • NMAP is sloww, be prepared to wait