Onboarding Interfaces with netbox

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.

Michael Schwartzkopff , 21 Nov 2020