Onboarding new devices is a nice new feature of netbox. But the onboarding does not include interfaces. In this article I want to present a method to read out the interfaces of devices and add them to netbox automatically. It uses ansible, but plain python with napalm as abstraction layer to the devices also should do the job.
The ansible Playbook
The basic idea is that a program gets the list of interfaces from the new device and feeds this back to netbox. I have configured my ansible so that it uses netbox as inventory. If netbox finished onboarding a new device, it obviously knows about it and will appear in the inventory of ansible.
So let’s start with a playbook that gathers information about the interfaces of devices. Since this depends on the manufacturer, I have two playbooks. One for Cisco IOS and one for Juniper junos.
Ciscos Interfaces
The playbook for cisco is called cisco_ios_interfaces.yaml
:
- name: Gather interface info from Cisco and update netbox
netbox.netbox.netbox_device_interface:
netbox_url: http://127.0.0.1:8001
netbox_token: "{{ netbox_token }}"
data:
device: "{{ inventory_hostname }}"
description: "{{ item.value.description }}"
name: "{{ item.key }}"
type: 1000Base-T (1GE)
enabled: "{{ item.value.operstatus == 'up' }}"
loop: "{{ ansible_facts.net_interfaces | dict2items }}"
when: item.value.type is match ("Gigabit Ethernet")
delegate_to: localhost
The playbook loops over all interfaces in the ansible facts of the device that are of type "Gigabit Ethernet" and updates netbox with these information.
Junipers Interfaces
For compareison I have the playbook juniper_junos_interfaces.yaml
:
- name: Gather interface information from Juniper ans update netbox
netbox.netbox.netbox_device_interface:
netbox_url: http://127.0.0.1:8001
netbox_token: "{{ netbox_token }}"
data:
device: "{{ inventory_hostname }}"
name: "{{ item.key }}"
type: 1000Base-T (1GE)
loop: "{{ ansible_facts.net_interfaces | dict2items }}"
when: item.key is match ("ge-")
delegate_to: localhost
Again I look over all interfaces from the ansible facts and filter for gigabit ethernet interfaces.
Meta Playbook
All these device specific playbooks are called by a single meta playbook:
- name: Get interfaces into netbox
hosts: device_roles_network
tasks:
- name: Include specific task
include: "{{ platforms[0] }}_interfaces.yaml"
- name: Activate device
netbox.netbox.netbox_device:
netbox_url: http://127.0.0.1:8001
netbox_token: 3fd01c4da99abc95c10c07f3bb32abb1208d2f37
data:
name: "{{ inventory_hostname }}"
status: Active
state: present
delegate_to: localhost
This playbook can be invoked from the command line, will get the inventory from netbox and then trigger the update of the interface information depending on the manufacturer and the operation system of the device.
After the update of the interface information I also set the devices status to
Active
. My onboarding sets new devices to Staged
to mark these new devices.
After gathering the interface information I can activate the devices in
netbox and start to deploy the config.
Automate the Data Gathering
Running the playbook manually every time I onboard a new device would contradict the whole idea of automation, i.e. to have as few human administrative actions as possible. netbox offers a solution to automate this task called webhooks.
A webhook is an action that can be triggered after netbox took some action. In
my case, I start the webhook onboard_interfaces
every time, a device was
created or updated. The URL is http://127.0.0.1:5000/interfaces
. To that web
application I pass the information about the device in the body of the POST
request. The template for the body is:
{"device": "{{ data['name'] }}" }
All these data can be entered in the webhook configuration.
The Web Application
Of course I need a web application that listens to port 5000, accepts the
information about the new device and starts the ansible run to get the interface
information into netbox. With the flask
framework this task is quite simple.
The program is written in python:
#!/bin/python
from flask import Flask, jsonify
from flask import abort
from flask import make_response
from flask import request
import os
import subprocess
app = Flask(__name__)
@app.errorhandler(404)
def not_found(error):
return make_response(jsonify({'error': 'Not found'}), 404)
# Just for completeness GET also implemented
@app.route('/interfaces', methods=['GET'])
def onboard_interfaces():
output = subprocess.run(["ansible-playbook", "-l", device, "/home/ansible/get_interface.yaml"])
return jsonify({'OK': 'ok'}), 201
@app.route('/interfaces', methods=['POST'])
def onboard_int():
if not request.json or not 'device' in request.json:
abort(404)
device = request.json.get('device')
output = subprocess.run(["ansible-playbook", "-l", device, "/home/ansible/get_interface.yaml"])
return jsonify({'OK': 'ok'}), 201
if __name__ == '__main__':
app.run(debug=True)
Putting it all together, running an onboaring task in netbox will create a new device. This creation of a device will trigger the webhook that in turn will run the ansible playbook to update the interface information in netbox.
Please mail me if you have any further questions ms@sys4.de.