Exploring network device configuration deployment using Nornir and NetBox
Requirement:
Automated network configuration deployment that is capable of using NetBox as host inventory
Assumptions:
- Knowledge of basic python for network automation
- Existing network inventory records are already populated in NetBox (v3.4.2)
- Existing python virtual environment (python3.9)
- Existing Windows/Linux jumphost that runs the python virtual environment that have SSH access to the network devices
Goals:
- Fetch network devices inventory records from NetBox
- Filter the fetched network devices inventory records from NetBox
- Store the provided username and password for network device SSH access
- Categorize the network device based on platform record from NetBox
- Connect to a network device and collect the network device facts
- Connect to a network device and push network configuration
Task 1: Install the following python packages in your Linux jumphost's python virtual environment
nornir==3.3.0 nornir-napalm==0.3.0 nornir-netbox==0.3.0 nornir-utils==0.2.0
nornir - nornir is a pure Python automation framework intented to be used directly from Python
nornir-napalm - nornir plugin for interacting withh devices using napalm library
nornir-netbox - nornir inventory plugin for NetBox
nornir-utils - collection of simple plugins for nornir
Task 2: Configure the Nornir config file
config.yaml
---
inventory:
plugin: NetBoxInventory2
options:
nb_url: '{Your netbox URL}'
nb_token: '{Your netbox account token}'
ssl_verify: False
Task 3: Configure the python script for goal #1
from nornir import InitNornir
nr = InitNornir(config_file="config.yaml")
You can verify if the inventory records was pulled successfully by accessing the data inside
nr
variable.print(nr.inventory.hosts)
{'R1': Host: R1, 'R2': Host: R2, 'SW01': Host: SW01}
Goal #1 completed.
Task 4: Configure the python script for goal #2
Filter the fetched network devices inventory records from NetBox
from nornir.core.filter import F
nr_filtered = nr.filter(
F(
platform = "my-router-platform",
site__slug__contains = "my-site",
status__value__contains = "active",
)
)
You can verify if the inventory records was filtered successfully by accessing the data inside
nr_filtered
variable. The record should only show those network devices that has been configured in NetBox with all the filter parameters - platform, site and status.print(nr_filtered.inventory.hosts)
{'R1': Host: R1, 'R2': Host: R2}
Goal #2 completed.
Task 5: Configure the python script for goal #3
Store the provided username and password for network device SSH access.
There are two ways (from what I currently know of) of storing username and password credentials to the Nornir inventory records. Either we can store it in the inventory record global default parameters or alternatively store it in inventory per host parameters. For this task, we will store is as global default parameter as we have a uniform access credential to all the network devices.
nr_filtered.inventory.defaults.username = '{Your username in clear text format}' nr_filtered.inventory.defaults.password = '{Your password in clear text format}'
Goal #3 completed.
Task 6: Configure the python script for goal #4
Categorize the network device based on platform record from NetBox
This task is related to the nornir_napalm plugin we have installed for accessing the network devices. As we are using napalm library to access the network devices, it will require us to specify the platform name that is supported by the napalm library format for identifying the network driver to be used.
There are currently two ways I can think of right now to specify the platform parameter. Either in NetBox device object platform parameter or within the python code by using filtering method. I will do the later option as I have already an existing platform record which is not in napalm supported format.
For this task, we will update the per host device inventory parameter
nr_filtered.inventory.hosts['R1'].platform
with the new value in napalm supported format. You might wonder why I didn't update the global inventory defaults parameters which is technically possible. The reason is that the nornir's per host device inventory record was already updated with a value that was referenced from the NetBox's device object platform record. Updating only the global default parameter will not update the per host device parameters. We will perform an iteration as we will have multiple list of devices to be updated.for i,k in nr_filtered.inventory.hosts.items():
k.platform = "{Please update accordingly based on your platform type - ios|iosxr|junos|nxos_ssh}"
Goal #4 completed.
Task 7: Configure the python script for goal #5
Connect to a network device and collect the network device facts
result = nr_filtered.run(
napalm_get,
getters=["get_facts"],
)
You can verify the result by either printing the
result
variable or accessing the data inside it.result['R1'].result['get_facts']['serial_number']
If successful, you should then be able to see the actual serial number of the network device with hostname "R1".
Goal #5 completed.
Task 8: Configure the python script for goal #6
Connect to a network device and push network configuration.
For this task, we will just do a simple network configuration of updating an interface description just to verify the functionality of our script.
from nornir_utils.plugins.functions import print_result
config_data = [
"!",
"interface gigabitethernet0/1",
" description Nornir automated interface description updating",
"!",
]
result = nr.run(
napalm_configure,
configuration = "\n".join(config_data),
replace = False,
dry_run = False,
)
print_result(result)
The result should show some output like this:
* R1 ** changed : True ***********************************************
vvvv napalm_configure ** changed : True vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
interface gigabitethernet0/1
description Nornir automated interface description updating
^^^^ END napalm_configure ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can then log in to the network device and verify that the interface description for GigabitEthernet0/1 was updated accordingly.
Goal #6 completed.
Consolidated script below for reference:
config.yaml
---
inventory:
plugin: NetBoxInventory2
options:
nb_url: '{Your netbox URL}'
nb_token: '{Your netbox account token}'
ssl_verify: False
nornir_config_update.py
from nornir import InitNornir
from nornir.core.filter import F
from nornir_utils.plugins.functions import print_result
nr = InitNornir(config_file="config.yaml")
nr_filtered = nr.filter(
F(
platform = "my-router-platform",
site__slug__contains = "my-site",
status__value__contains = "active",
)
)
nr_filtered.inventory.defaults.username = '{Your username in clear text format}'
nr_filtered.inventory.defaults.password = '{Your password in clear text format}'
for i,k in nr_filtered.inventory.hosts.items():
k.platform = "{Please update accordingly based on your platform type - ios|iosxr|junos|nxos_ssh}"
config_data = [
"!",
"interface gigabitethernet0/1",
" description Nornir automated interface description updating",
"!",
]
result = nr.run(
napalm_configure,
configuration = "\n".join(config_data),
replace = False,
dry_run = False,
)
print_result(result)