Use of a native PYTHON automation framework for multivendor environments.
As a network engineer one of my tasks is to periodically collect and update information (such as serial numbers, license status, interfaces details, version of OS, etc) from every network device.
The task is fairly easy if there are few network devices in the environment, but as the number of network devices grow the task becomes cumbersome and error prone. I usually have to collect information from hundreds of devices and the task takes several days when doing it manually in each platform. I started to use network automation techniques for these sorts of things achieving these same tasks only in a few minutes.
Fortunately, nowadays there are several mature solutions that can help us to automate these tedious tasks. In this article I will present an open-source framework called Nornir. This framework is based in python, and has become so efficient that many vendors recognize the value of Nornir, and encourage their usage.
It should be noted that there is always a learning curve when trying new things. There is a learning curve even when learning to ride a bicycle and only after trying lots of times then riding a bicycle gets "easy". When learning it's also important to start with small tasks that are very simple to repeat, test and control. There are many good Nornir tutorials all over the internet but in my opinion the examples could be simpler, and my aim in this article is to make it simpler for you.
TIP 1:
There are 2 major versions of Python: Python 2.x and Python 3.x.? Python 2.x is deprecated. So, you should always use Python 3.x. As of June 2022, the latest stable version is Python 3.10.
TIP 2:
It's recommended to use a Linux OS to learn and use automation. Many Linux OS already have Python 3.x installed. I will use Ubuntu 18 in the following examples.
TIP 3:
"pip" command is the Python package manager. There is a very big Python ecosystem, and many Python modules are installed with "pip" command. In many Linux OS distributions (including Ubuntu 18) the "pip" command is already installed.
Basic Nornir installation only needs the following couple of “pip install” commands:?
auben@ubuntu18:~$ pip install nornir
auben@ubuntu18:~$ pip install nornir_utils
This basic installation ships with a default plugin called SimpleInventory where we can add all devices we need to manage. I will provide an example to use the inventory in just a minute, but first we need to install one extra plugin to connect to the devices. Nornir framework has several plugins like Netmiko and Napalm that can be used to connect to the devices. In this article I chose to use Netmiko due to its simplicity, so let's install it with the following “pip install” command:
auben@ubuntu18:~$ pip install nornir_netmiko
Let’s create a local directory called “nornir-tutorial” to store all the files used in the following examples.
auben@ubuntu18:~$ mkdir nornir-tutorial
auben@ubuntu18:~$ cd nornir-tutorial
auben@ubuntu18:~/nornir-tutorial$?
“nornir-tutorial” directory is empty. There are many ways to create a text file in Linux (using “vi” command, using “nano” command, copying from elsewhere using SCP, etc). As creating a text file is out of scope I will show you the text files already created as needed in the following examples.
TIP 4:
"YAML" is a well known and very popular text file format used in lots of IT tools. YAML files are very easy to read. Nornir uses “YAML” files to define the inventory.?
Example 1: Simple inventory and sending a command to devices in inventory
I created "hosts.yaml" file in YAML format. This exact filename is needed to define the devices in Nornir inventory. This example has only 1 device called "linux01" which has several information fields. In particular the "hostname" field should carry an IP address or a resolvable name. The other fields (username, password, platform) are self-explanatory. Let's show the content of our inventory:
auben@ubuntu18:~/nornir-tutorial$ more hosts.yaml
---
linux01:
??hostname: 127.0.0.1
??username: auben
??password: auben123
??platform: linux
Now let’s review my Python script to use Nornir.
The InitNornir function initializes Nornir with default values and also initializes the inventory. The result of the InitNornir function is store in the “inv” variable.
The “inv.run” method runs a task called “netmiko_send_command” to send a command via SSH using netmiko. The command is "cat /etc/lsb-release". The result is stored in the “result” variable and printed to screen.
auben@ubuntu18:~/nornir-tutorial$ more example01.py
from nornir import InitNornir
from nornir_utils.plugins.functions import print_result
from nornir_netmiko import netmiko_send_command
inv = InitNornir()
result = inv.run(
????task=netmiko_send_command,
????command_string="cat /etc/lsb-release")
)
print_result(result)
Execute the script and see the results
auben@ubuntu18:~/nornir-tutorial$ python example01.py
netmiko_send_command************************************************************
* linux01 ** changed : False ***************************************************
vvvv netmiko_send_command ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.5 LTS"
^^^^ END netmiko_send_command ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Example 2: Using filters to send a command to only some devices in the inventory.?
In this example the inventory has 2 devices. The devices are called "router01" and "linux01".? "router01" has the “platform” field with value "juniper_junos" to indicate it is using Junos operating system.
auben@ubuntu18:~/nornir-tutorial$ more hosts.yaml
---
router01:
??hostname: 192.168.0.115
??username: auben
??password: aubenrouter
??platform: juniper_junos
linux01:
??hostname: 127.0.0.1
??username: auben
??password: aubenlinux
??platform: linux
The “filter” method is used to select only some devices from the inventory.
The “inv_linux” variable stores all linux devices as selected by inv.filter(platform="linux").
The “inv_junos” variable stores all junos devices as selected by inv.filter(platform="juniper_junos").
Then the “inv_linux.run” method is used to send the command “cat /etc/lsb-release” to all linux devices.
The “inv_junos.run” method is used to send the command “show system information” to all junos devices.
auben@ubuntu18:~/nornir-tutorial$ more example02.py
from nornir import InitNornir
from nornir_utils.plugins.functions import print_result
from nornir_netmiko import netmiko_send_command
inv = InitNornir()
inv_linux = inv.filter(platform="linux")
inv_junos = inv.filter(platform="juniper_junos")
result_linux = inv_linux.run( task=netmiko_send_command , command_string="cat /etc/lsb-release" )
result_junos = inv_junos.run( task=netmiko_send_command , command_string="show system information" )
print_result(result_linux)?
print_result(result_junos)?
Execute the script and see the results
auben@ubuntu18:~/nornir-tutorial$ python example02.py
netmiko_send_command************************************************************
领英推荐
* linux01 ** changed : False ***************************************************
vvvv netmiko_send_command ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.5 LTS"
^^^^ END netmiko_send_command ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netmiko_send_command************************************************************
* router01 ** changed : False **************************************************
vvvv netmiko_send_command ** changed : False vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
Model: mx960
Family: junos
Junos: 19.4R3-S7.3
Hostname: router01
^^^^ END netmiko_send_command ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Example 3: Using defaults for all inventory devices and pushing configuration to devices.
In the previous example all devices had the same username. To avoid repetition Nornir can use a “defaults.yaml” file to include information that applies to all devices. In this example the “username” field with value “auben” applies to all devices.
auben@ubuntu18:~/nornir-tutorial$ more defaults.yaml
---
username: auben
I deleted “username” field in all devices in “hosts.yaml”.
auben@ubuntu18:~/nornir-tutorial$ more hosts.yaml
---
router01:
??hostname: 192.168.0.115
??password: aubenrouter
??platform: juniper_junos
linux01:
??hostname: 127.0.0.1
??password: aubenlinux
??platform: linux
The “inv_junos” variable stores all junos devices selected by the “inv.filter” method.
The “my_config” variable is a list and every element in the list is a configuration command.
Then the “inv.junos.run” method runs a task called “netmiko_send_config” to push the configuration defined in the “my_config” variable. The result is stored in “result_junos” variable
auben@ubuntu18:~/nornir-tutorial$ more example03.py
from nornir import InitNornir
from nornir_utils.plugins.functions import print_result
from nornir_netmiko import netmiko_send_command , netmiko_send_config
inv = InitNornir()
inv_junos = inv.filter(platform="juniper_junos")
my_config = [
??"set system host-name EXAMPLE03" ,
??"set interfaces ge-0/0/0.0 family inet address 10.10.10.10/24" ,
??"commit"
]
result_junos = inv_junos.run( task=netmiko_send_config , config_commands= my_config )
print_result(result_junos)
Execute the script and see the results. Please notice the hostname has changed from router01 to EXAMPLE03.
auben@ubuntu18:~/nornir-tutorial$ python example03.py?
netmiko_send_config*************************************************************
* router01 ** changed : True ***************************************************
vvvv netmiko_send_config ** changed : True vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv INFO
configure?
Entering configuration mode
The configuration has been changed but not committed
[edit]
auben@router01# set system host-name EXAMPLE03?
[edit]
auben@router01# set interfaces ge-0/0/0.0 family inet address 10.10.10.10/24?
[edit]
auben@MX# commit?
commit complete
[edit]
auben@EXAMPLE03# exit configuration-mode?
Exiting configuration mode
auben@EXAMPLE03>?
^^^^ END netmiko_send_config ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
CONCLUSION:
It is not needed to be a Python expert to use Nornir. Nornir scripts are easy to read and understand. By default it uses multithreading which makes Nornir a fast and highly scalable tool.
Nornir supports multivendor environments through the use of plugins like Netmiko and Napalm. All these features make Nornir a very powerful tool at your disposal.
HELPFUL LINKS:
Official Documentation
Great tutorial by Kirk Byers
You can learn more about YAML here
Netmiko supported devices
Author: Eduardo Aliaga
Telecommunication engineer. Latin-American expert in networking technologies for Service Provider, Enterprise and Security markets. Committed to constant training regarding networking specifications, focusing mainly on high-scale networks design, support, deployment and consultancy.