The Problem

Due to some new environments that we have been spinning up at work, I have been labing a lot of new tech out recently. Because of this, I found myself typing out many commands on the CLI or having to re-type out configurations from scratch if my lab misbehaved. To reduce the time spent on the tedious, I decided to write up a quick script to manage my infrastructure through static files.

The Topology

I will leverage a pretty basic setup to outline the idea, but you can get rather complicated with this basic script since NAPALM supports a whole slew of device types. Below we have three routers, with router-A having a leg to both router-B and router-C. Router-A is also running BGP between itself and each directly attached neighbor. The management interfaces on each Arista switch leg out locally to my home network, which allows me to access each vEOS natively via ssh.

Topology

The Objective

Instead of having the configuration on the device be the source of truth, it will be a set of flat files on my computer. I should then be able to “push” these configuration files to the devices removing the need for any manual CLI work. Below is a simple diagram of the workflow that I imagine when setting up and managing a device in the lab.

Topology

The Script

First we’ll want to import the get_network driver from NAPALM

from napalm import get_network_driver

Then we will define our list of devices. We could get a little clever here and leverage nornir or netbox but for the sake of a MVP we’ll just have a list of dictionaries.

devices = [
      {'name': 'router-A', 'host': '192.168.86.201'},
      {'name': 'router-B', 'host': '192.168.86.202'},
      {'name': 'router-C', 'host': '192.168.86.203'},
]

Once we have the dictionary all we need to do is loop through it and apply the local file to each respective device.

for device in devices:
      driver = get_network_driver('eos')
      active_device = driver(device['host'], 'admin', 'password')
      active_device.open()
      active_device.load_replace_candidate(filename=f"configurations/{device['name']}.conf")
      compare_result = active_device.compare_config()

      if compare_result == '':
            active_device.discard_config()
      else:
            active_device.commit_config()
      active_device.close()

As you can see, the logic is pretty quick and dirty. First, we open up a connection to the device. Then we proceed to stage the configuration file by loading it to the device. We then check to see if there are any changes. If the string is not empty, we commit the configuration; otherwise, we discard it. And finally, we close the session to the device.

The load_replace_candidate() method will return a string with the changes to the configuration; if there are no changes, it will return an empty string.

Output

Below is an output from running the script. I have added some print statments to log the output.

(arista) mbarry@desktop:~/projects/automation$ python deployment.py
******************************************************************
Connectiong to router-A...
Loading configuration...
Configuration changes....

@@ -23,6 +23,8 @@
    ip address 192.168.0.1/30
 !
 interface Ethernet3
+   no switchport
+   ip address 10.1.1.1/30
 !
 interface Ethernet4
 !
Commiting configuration....
COMPLETED


******************************************************************
Connectiong to router-B...
Loading configuration...
<<<<<<<<<<<<<NO CHANGES>>>>>>>>>>>>


******************************************************************
Connectiong to router-C...
Loading configuration...
<<<<<<<<<<<<<NO CHANGES>>>>>>>>>>>>

We can see I added some IP address configuration to router-A on interface Ethernet3 and made no changes to router-B or router-C.

Summary

Nothing too complicated above, but it allows me to iterate through my labs a bit quicker. Plus if you start storing the configuration in a git repo and wrap it in a CI/CD pipeline with pre and post validations you have yourself a genuine Infrastructure-As-Code deployment.

Resources

Code: https://github.com/barryCrunch/lab-automation-demo

Napalm Documentation: https://napalm.readthedocs.io/en/latest/index.html