Intro to Network Automation with Python and Netmiko

In this post we will take a look at automating a simple network process using Kirk Byers’ python library Netmiko. Netmiko is a multivendor library that simplifies the process of creating ssh connections to different network devices.

Note: this exercise assumes you have some basic python knowledge and basic networking knowledge.

We will perform a simple exercise where we configure a pair of Cisco routers to become OSPF neighbors in the same area. We will be using EVE-NG for our lab environment. If you do not have a network emulator, here is a link which provides various methods on how to install EVE-NG:

http://eve-ng.net/index.php/documentation/howto-s/78-howto-eve-ng-ova-and-iso-installation-video

Lab topology

We are going to create an OSPF adjacency between RouterA and RouterB on their GigabitEthernet0/1 interfaces using python and the netmiko library.

Below are the steps we need to take to accomplish our goal:

  1. Get the IP address of the Gi0/1 interface on both routers
  2. Start an OSPF process on each router
  3. Advertise the address of each router’s respective Gi0/1 interface to members of a certain OSPF area.

Let’s get into it.

Install Netmiko

First off, make sure you install netmiko. Execute the below statement in a terminal.

pip install netmiko

Creating a connection to the device.

Now it’s time to begin coding. Before we get into any of the network configuration aspects of the lab, we first must be able to connect to our device. Here’s where Netmiko comes in. The main component of Netmiko is a ConnectHandler object which acts as an “ssh connection” to the device.

The ConnectHandler object requires some minimum information.

  • ‘device_type’ variable to handle vendor-specific login banners, prompts, etc
  • ‘host’ or ‘ip’ variable to be used to identify the device
  • ‘username’, ‘password’, and ‘secret’ for device credentials (NOTE: ‘secret’ refers to the enable password)

To do this in python, we first create a dictionary type with the above information. For our case, an example dictionary is below.

device_config = {
“device_type”: “cisco_ios”,
“ip”: “192.168.18.143”,
“username”: “jrecchia”,
“password”: “password”,
“secret”: “password”
}

Now we use this dictionary to get our Connection Object.

from netmiko import ConnectHandler
connection = ConnectHandler(**device_config)

Get the IP address of the interface

Next we need to get the IP address of each Gi0/1 interface. We are going to use the Connection object’s ‘send_command’ method and a little bit of regex to do this.

The ‘send_command’ method sends a string as a command and returns the output. We will send the command “show ip interface <interface-name> | i Internet address”. We will then use a library called regex to return only the IP address.

import redef get_interface_ip(connection, iface):
#Write out the command substituting the interface name where appropriate
cmd = "show ip interface {} | i Internet address".format(iface)
interface_info = connection.send_command(cmd)
#Trim leading/trailing whitespace
interface_info = interface_info.strip()
#Use regex to obtain the ip address from the output of the above command
ip_address_re = r'Internet address is (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/\d+'
match = re.match(ip_address_re, interface_info)
if match:
return match.group(1) #return the first group of the regex
else:
return None

Configure OSPF and advertise interfaces

The next part of the program is to configure OSPF and advertise the IP address we just got on Gi0/1. Again, we will do this using the Connection object’s ‘send_command’ and ‘send_command_timing’ methods.

def configure_ospf(connection, interface_ip):
#Check if connection is config mode
if connection.check_config_mode() == False:
connection.config_mode()
#Start ospf process
connection.send_command_timing("router ospf 1") #Note: use send_command_timing whenever the router prompt changes
#Advertise the interface
cmd = "network {} 255.255.255.255 area 0".format(interface_ip)
connection.send_command(cmd)
#Exit config mode
connection.exit_config_mode()

Verify the change was made

To make sure we actually modified the device configuration, we will use ‘send_command_timing’ to display the OSPF configuration in the running-config.

def verify_ospf_config(connection):
# Print out OSPF section of running-config
output = connection.send_command_timing("show running-config | s ospf")
print("OSPF Config\n-------------------------------")
print(output)
print("-------------------------------")

Additionally, after the change is made to both routers, we can use the ‘show ip ospf neighbors’ command to view a router’s current OSPF neighbors. We will not go into that in this post, but it could be a good exercise for you to do on your own.

Putting it all together

We combine all of the functions defined above into one function that executes our original 3 steps.

  1. Get the IP address of the Gi0/1 interface on both routers
  2. Start an OSPF process on each router
  3. Advertise the address of each router’s respective Gi0/1 interface to members of a certain OSPF area.
def advertise_ospf_interface(device_config):
# Create connection
connection = ConnectHandler(**device_config)
# Enter enable mode
connection.enable()
# Get ip address of an interface
interface_ip = get_interface_ip(connection, 'Gi0/1')
print("We will be advertising {} on {}".format(interface_ip, device_config["ip"]))
# Start ospf and advertise interface
configure_ospf(connection, interface_ip)
print("Configured OSPF on {}".format(device_config["ip"]))
# Verify config
verify_ospf_config(connection)
# Close the connection
connection.disconnect()
device1_config = {
"device_type": "cisco_ios",
"ip": "192.168.18.143",
"username": "jrecchia",
"password": "password",
"secret": "password"
}
device2_config = {
"device_type": "cisco_ios",
"ip": "192.168.18.142",
"username": "jrecchia",
"password": "password",
"secret": "password"
}
def main():
routers = []
routers.append(device1_config)
routers.append(device2_config)
for rtr in routers:
advertise_ospf_interface(rtr)

We combined steps 2 and 3 into the same function but I hope you get the picture.

We then set up a main function to add the 2 router configs to a list and then loop through that list executing our advertise_ospf_interface on each router in the list.

Results

Below is the output of our python script.

jrecchia@ubuntu:~/Network Automation Labs/Lab 1$ python main.py 
We will be advertising 1.1.1.1 on 192.168.18.143
Configured OSPF on 192.168.18.143
OSPF Config
-------------------------------
router ospf 1
network 1.1.1.1 0.0.0.0 area 0
-------------------------------
We will be advertising 1.1.1.2 on 192.168.18.142
Configured OSPF on 192.168.18.142
OSPF Config
-------------------------------
router ospf 1
network 1.1.1.2 0.0.0.0 area 0
-------------------------------

The entire code can be found at https://github.com/jrecchia1029/Network-Automation-Labs/tree/master/Lab%201

Remember, this is just a simple example of how you can use python to automate different network processes. There are many other libraries out there that you can use in conjunction with netmiko that can allow you to do just about anything you can think of at even faster speeds. Some to take a look at are threading, queues, asyncio, napalm, and textfsm.

Below is the link to the Netmiko github page.