NAPALM Cisco Devices

Introduction

In this article we are going to see the functions and methods of NAPALM to collect data in Cisco IOS and IOS-XR routers. We will write multiple Python scripts to collect data from a network, which you can use to create reports or investigate a problem. This is a continuation of the article in which we introduced NAPALM and it is part of the adventure in which I want to show you how you can implement Network Programmability and Automation with Python and the NAPALM library.

The main content is in the following video. So start here so that you can understand some sections of the article.

English Version Video Link:

NAPALM CLI

The NAPALM installation comes with a tool that allows us to use NAPALM directly from the command line. Its use is quite simple and we can see how it is used in the command’s help menu. We execute the following command to see this menu:

napalm --help

Command output:

(napalm_practice) malvarez@Coding-Networks:~$ napalm --help
usage: napalm [-h] [--user USER] [--password PASSWORD] --vendor VENDOR
              [--optional_args OPTIONAL_ARGS] [--debug]
              hostname {configure,call,validate} ...

Command line tool to handle configuration on devices using NAPALM.The script
will print the diff on the screen

positional arguments:
  hostname              Host where you want to deploy the configuration.

optional arguments:
  -h, --help            show this help message and exit
  --user USER, -u USER  User for authenticating to the host. Default: user
                        running the script.
  --password PASSWORD, -p PASSWORD
                        Password for authenticating to the host.If you do not
                        provide a password in the CLI you will be prompted.
  --vendor VENDOR, -v VENDOR
                        Host Operating System.
  --optional_args OPTIONAL_ARGS, -o OPTIONAL_ARGS
                        String with comma separated key=value pairs passed via
                        optional_args to the driver.
  --debug               Enables debug mode; more verbosity.

actions:
  {configure,call,validate}
    configure           Perform a configuration operation
    call                Call a napalm method
    validate            Validate configuration/state

Automate all the things!!!

From this tool it is possible to execute configuration operations on the devices, validate configurations or states and make calls to the NAPALM methods to obtain data from the devices.

These actions are carried out by passing to the command the user arguments and the password of the device to which we are going to connect, the operating system, the IP or name of the equipment and finally the name of the method.

Let’s see an example:

napalm --user codingnetworks --password Coding.Networks1 --vendor ios IOS-R2 call get_interfaces

Command output:

{
    "GigabitEthernet0/0": {
        "is_enabled": true,
        "is_up": true,
        "description": "to_IOS-SW2 Gi0/1",
        "mac_address": "50:00:00:01:00:00",
        "last_flapped": -1.0,
        "mtu": 1500,
        "speed": 1000
    },
    "GigabitEthernet0/0.30": {
        "is_enabled": true,
        "is_up": true,
        "description": "to_IOS-SW2 Gi0/1 Vlan 30",
        "mac_address": "50:00:00:01:00:00",
        "last_flapped": -1.0,
        "mtu": 1500,
        "speed": 1000
    },
    "GigabitEthernet0/1": {
        "is_enabled": false,
        "is_up": false,
        "description": "",
        "mac_address": "50:00:00:01:00:01",
        "last_flapped": -1.0,
        "mtu": 1500,
        "speed": 1000
    },

NAPALM Python

It is in this section where we will begin to see and enjoy the benefits of using NAPALM for programmability and network automation.

NAPALM Python Devices information table

We are going to write a script that connects to all the devices on our network and prints information such as the hostname, manufacturer, model, time they have in operation and the serial number of each one in a table. Below the code:

import napalm
from tabulate import tabulate
def main():
    driver_ios = napalm.get_network_driver("ios")
    driver_iosxr = napalm.get_network_driver("iosxr")
    device_list = [["ios-sw2","ios", "switch"],["ios-r2", "ios", "router"],
    ["ios-r3", "ios", "router"],["iosxr-r1", "iosxr", "router"],["ios-sw1", "ios", "switch"]]
    network_devices = []
    for device in device_list:
        if device[1] == "ios":
            network_devices.append(
                            driver_ios(
                            hostname = device[0],
                            username = "codingnetworks",
                            password = "Coding.Networks1"
                            )
                              )
        elif device[1] == "iosxr":
            network_devices.append(
                            driver_iosxr(
                            hostname = device[0],
                            username = "codingnetworks",
                            password = "Coding.Networks1"
                            )
                              )
    devices_table = [["hostname", "vendor", "model", "uptime", "serial_number"]]
    for device in network_devices:
        print("Connecting to {} ...".format(device.hostname))
        device.open()
        print("Getting device facts")
        device_facts = device.get_facts()
        devices_table.append([device_facts["hostname"],
                              device_facts["vendor"],
                              device_facts["model"],
                              device_facts["uptime"],
                              device_facts["serial_number"]
                              ])
        device.close()
        print("Done.")
    print(tabulate(devices_table, headers="firstrow"))
if __name__ == '__main__':
    main()

In the video you will find the explanation of this code.

Let’s see the output of the Script:

Connecting to ios-sw2 ...
Getting device facts
Done.
Connecting to ios-r2 ...
Getting device facts
Done.
Connecting to ios-r3 ...
Getting device facts
Done.
Connecting to iosxr-r1 ...
Getting device facts
'list' object has no attribute 'xpath'
'list' object has no attribute 'xpath'
'list' object has no attribute 'xpath'
Done.
Connecting to ios-sw1 ...
Getting device facts
Done.
hostname    vendor    model      uptime  serial_number
----------  --------  -------  --------  ---------------------
IOS-SW2     Cisco     IOSv        34020  9162JOW336J
IOS-R2      Cisco     IOSv        34020  9MJNBPR604WW2WN1QPABR
IOS-R3      Cisco     IOSv        34080  9BO8LXD6KLX8ZSB7M0PPF
IOSXR-R1    Cisco                 34146
IOS-SW1     Cisco     IOSv        34080  9CGHIOOXX08

NAPALM Python Interfaces Table

We are going to modify the code above to add other functionality. This functionality will allow the script to print a table with all the interfaces of each device with information about each one such as its status, description, mtu, among others. Below the code:

import napalm
from tabulate import tabulate
def main():
    driver_ios = napalm.get_network_driver("ios")
    driver_iosxr = napalm.get_network_driver("iosxr")
    device_list = [["ios-sw2","ios", "switch"],["ios-r2", "ios", "router"],
    ["ios-r3", "ios", "router"],["iosxr-r1", "iosxr", "router"],["ios-sw1", "ios", "switch"]]
    network_devices = []
    for device in device_list:
        if device[1] == "ios":
            network_devices.append(
                            driver_ios(
                            hostname = device[0],
                            username = "codingnetworks",
                            password = "Coding.Networks1"
                            )
                              )
        elif device[1] == "iosxr":
            network_devices.append(
                            driver_iosxr(
                            hostname = device[0],
                            username = "codingnetworks",
                            password = "Coding.Networks1"
                            )
                              )
    devices_table = [["hostname", "vendor", "model", "uptime", "serial_number"]]
    devices_table_int = [["hostname","interface","is_up", "is_enabled", "description", "speed", "mtu", "mac_address"]]
    for device in network_devices:
        print("Connecting to {} ...".format(device.hostname))
        device.open()
        print("Getting device facts")
        device_facts = device.get_facts()
        devices_table.append([device_facts["hostname"],
                              device_facts["vendor"],
                              device_facts["model"],
                              device_facts["uptime"],
                              device_facts["serial_number"]
                              ])
        print("Getting device interfaces")
        device_interfaces = device.get_interfaces()
        for interface in device_interfaces:
            devices_table_int.append([device_facts["hostname"],
                                  interface,
                                  device_interfaces[interface]['is_up'],
                                  device_interfaces[interface]['is_enabled'],
                                  device_interfaces[interface]['description'],
                                  device_interfaces[interface]['speed'],
                                  device_interfaces[interface]['mtu'],
                                  device_interfaces[interface]['mac_address']
        ])
        device.close()
        print("Done.")
    print(tabulate(devices_table, headers="firstrow"))
    print()
    print(tabulate(devices_table_int, headers="firstrow"))
if __name__ == '__main__':
    main()

In the video you will find the explanation of this code.

Let’s see the output of this code:

Connecting to ios-sw2 ...
Getting device facts
Getting device interfaces
Done.
Connecting to ios-r2 ...
Getting device facts
Getting device interfaces
Done.
Connecting to ios-r3 ...
Getting device facts
Getting device interfaces
Done.
Connecting to iosxr-r1 ...
Getting device facts
'list' object has no attribute 'xpath'
'list' object has no attribute 'xpath'
'list' object has no attribute 'xpath'
Getting device interfaces
Done.
Connecting to ios-sw1 ...
Getting device facts
Getting device interfaces
Done.
hostname    vendor    model      uptime  serial_number
----------  --------  -------  --------  ---------------------
IOS-SW2     Cisco     IOSv        34080  9162JOW336J
IOS-R2      Cisco     IOSv        34140  9MJNBPR604WW2WN1QPABR
IOS-R3      Cisco     IOSv        34140  9BO8LXD6KLX8ZSB7M0PPF
IOSXR-R1    Cisco                 34223
IOS-SW1     Cisco     IOSv        34140  9CGHIOOXX08

hostname    interface                  is_up    is_enabled    description                       speed    mtu  mac_address
----------  -------------------------  -------  ------------  ------------------------------  -------  -----  -----------------
IOS-SW2     GigabitEthernet0/0         True     True                                             1000   1500  50:00:00:03:00:00
IOS-SW2     GigabitEthernet0/1         True     True          Connected to IOSXR-R1 L2-Trunk     1000   1500  50:00:00:03:00:01
IOS-SW2     GigabitEthernet0/2         True     True                                             1000   1500  50:00:00:03:00:02
IOS-SW2     GigabitEthernet0/3         True     True          to_PC_1                            1000   1500  50:00:00:03:00:03
IOS-SW2     GigabitEthernet1/0         True     True                                             1000   1500  50:00:00:03:00:04
IOS-SW2     GigabitEthernet1/1         True     True                                             1000   1500  50:00:00:03:00:05
IOS-SW2     GigabitEthernet1/2         True     True                                             1000   1500  50:00:00:03:00:06
IOS-SW2     GigabitEthernet1/3         True     True                                             1000   1500  50:00:00:03:00:07
IOS-SW2     Vlan10                     True     True          Management 10.10.10.10/24          1000   1500  50:00:00:03:80:0A
IOS-R2      GigabitEthernet0/0         True     True          to_IOS-SW2 Gi0/1                   1000   1500  50:00:00:01:00:00
IOS-R2      GigabitEthernet0/0.30      True     True          to_IOS-SW2 Gi0/1 Vlan 30           1000   1500  50:00:00:01:00:00
IOS-R2      GigabitEthernet0/1         False    False                                            1000   1500  50:00:00:01:00:01
IOS-R2      GigabitEthernet0/2         True     True          to_IOSXR-R1 Gi0/0/0/2 External     1000   1500  50:00:00:01:00:02
IOS-R2      GigabitEthernet0/3         False    False                                            1000   1500  50:00:00:01:00:03
IOS-R2      Loopback0                  True     True                                             8000   1514
IOS-R3      GigabitEthernet0/0         True     True          to_IOSXR-R1 Gi0/0/0/0 External     1000   1500  50:00:00:02:00:00
IOS-R3      GigabitEthernet0/1         False    False                                            1000   1500  50:00:00:02:00:01
....

NAPALM Python BGP Table

Again we are going to modify our code to add another functionality. This functionality is to print in a table all the BGP neighbors of each routers, including how many routes they are advertising and how many routes they are receiving. Below the code:

import napalm
from tabulate import tabulate
def main():
    driver_ios = napalm.get_network_driver("ios")
    driver_iosxr = napalm.get_network_driver("iosxr")
    device_list = [["ios-sw2","ios", "switch"],["ios-r2", "ios", "router"],
    ["ios-r3", "ios", "router"],["iosxr-r1", "iosxr", "router"],["ios-sw1", "ios", "switch"]]
    network_devices = []
    for device in device_list:
        if device[1] == "ios":
            network_devices.append(
                            driver_ios(
                            hostname = device[0],
                            username = "codingnetworks",
                            password = "Coding.Networks1"
                            )
                              )
        elif device[1] == "iosxr":
            network_devices.append(
                            driver_iosxr(
                            hostname = device[0],
                            username = "codingnetworks",
                            password = "Coding.Networks1"
                            )
                              )
    devices_table = [["hostname", "vendor", "model", "uptime", "serial_number"]]
    devices_table_int = [["hostname","interface","is_up", "is_enabled", "description", "speed", "mtu", "mac_address"]]
    devices_table_bgp = [["hostname", "neighbor", "remote-as", "status", "sent prefixes", "received prefixes"]]
    for device in network_devices:
        print("Connecting to {} ...".format(device.hostname))
        device.open()
        print("Getting device facts")
        device_facts = device.get_facts()
        devices_table.append([device_facts["hostname"],
                              device_facts["vendor"],
                              device_facts["model"],
                              device_facts["uptime"],
                              device_facts["serial_number"]
                              ])
        print("Getting device interfaces")
        device_interfaces = device.get_interfaces()
        for interface in device_interfaces:
            devices_table_int.append([device_facts["hostname"],
                                  interface,
                                  device_interfaces[interface]['is_up'],
                                  device_interfaces[interface]['is_enabled'],
                                  device_interfaces[interface]['description'],
                                  device_interfaces[interface]['speed'],
                                  device_interfaces[interface]['mtu'],
                                  device_interfaces[interface]['mac_address']
        ])
        if not "SW" in device_facts["hostname"]:
            print("Getting device BGP Neighbors")
            device_bgp_peers = device.get_bgp_neighbors()
            address_fam = "ipv4 unicast"
            if "IOSXR" in device_facts["hostname"]:
                address_fam = "ipv4"
            for bgp_neighbor in device_bgp_peers['global']['peers']:
                devices_table_bgp.append([device_facts["hostname"],
                                          bgp_neighbor,
                                          device_bgp_peers['global']['peers'][bgp_neighbor]['remote_as'],
                                          device_bgp_peers['global']['peers'][bgp_neighbor]['is_up'],
                                          device_bgp_peers['global']['peers'][bgp_neighbor]['address_family'][address_fam]['sent_prefixes'],
                                          device_bgp_peers['global']['peers'][bgp_neighbor]['address_family'][address_fam]['received_prefixes']
                                     ])
        device.close()
        print("Done.")
    print(tabulate(devices_table, headers="firstrow"))
    print()
    print(tabulate(devices_table_int, headers="firstrow"))
    print()
    print(tabulate(devices_table_bgp, headers="firstrow"))
if __name__ == '__main__':
    main()

Output when running the code:

Connecting to ios-sw2 ...
Getting device facts
Getting device interfaces
Done.
Connecting to ios-r2 ...
Getting device facts
Getting device interfaces
Done.
Connecting to ios-r3 ...
Getting device facts
Getting device interfaces
Done.
Connecting to iosxr-r1 ...
Getting device facts
'list' object has no attribute 'xpath'
'list' object has no attribute 'xpath'
'list' object has no attribute 'xpath'
Getting device interfaces
Done.
Connecting to ios-sw1 ...
Getting device facts
Getting device interfaces
Done.
hostname    vendor    model      uptime  serial_number
----------  --------  -------  --------  ---------------------
IOS-SW2     Cisco     IOSv        34080  9162JOW336J
IOS-R2      Cisco     IOSv        34140  9MJNBPR604WW2WN1QPABR
IOS-R3      Cisco     IOSv        34140  9BO8LXD6KLX8ZSB7M0PPF
IOSXR-R1    Cisco                 34223
IOS-SW1     Cisco     IOSv        34140  9CGHIOOXX08

hostname    interface                  is_up    is_enabled    description                       speed    mtu  mac_address
----------  -------------------------  -------  ------------  ------------------------------  -------  -----  -----------------
IOS-SW2     GigabitEthernet0/0         True     True                                             1000   1500  50:00:00:03:00:00
IOS-SW2     GigabitEthernet0/1         True     True          Connected to IOSXR-R1 L2-Trunk     1000   1500  50:00:00:03:00:01
IOS-SW2     GigabitEthernet0/2         True     True                                             1000   1500  50:00:00:03:00:02
IOS-SW2     GigabitEthernet0/3         True     True          to_PC_1                            1000   1500  50:00:00:03:00:03
IOS-SW2     GigabitEthernet1/0         True     True                                             1000   1500  50:00:00:03:00:04
IOS-SW2     GigabitEthernet1/1         True     True                                             1000   1500  50:00:00:03:00:05
IOS-SW2     GigabitEthernet1/2         True     True                                             1000   1500  50:00:00:03:00:06
IOS-SW2     GigabitEthernet1/3         True     True                                             1000   1500  50:00:00:03:00:07
IOS-SW2     Vlan10                     True     True          Management 10.10.10.10/24          1000   1500  50:00:00:03:80:0A
IOS-R2      GigabitEthernet0/0         True     True          to_IOS-SW2 Gi0/1                   1000   1500  50:00:00:01:00:00
IOS-R2      GigabitEthernet0/0.30      True     True          to_IOS-SW2 Gi0/1 Vlan 30           1000   1500  50:00:00:01:00:00
IOS-R2      GigabitEthernet0/1         False    False                                            1000   1500  50:00:00:01:00:01
IOS-R2      GigabitEthernet0/2         True     True          to_IOSXR-R1 Gi0/0/0/2 External     1000   1500  50:00:00:01:00:02
IOS-R2      GigabitEthernet0/3         False    False                                            1000   1500  50:00:00:01:00:03
IOS-R2      Loopback0                  True     True                                             8000   1514
IOS-R3      GigabitEthernet0/0         True     True          to_IOSXR-R1 Gi0/0/0/0 External     1000   1500  50:00:00:02:00:00
IOS-R3      GigabitEthernet0/1         False    False                                            1000   1500  50:00:00:02:00:01

Ready. There we have our BGP neighbor table.

ConclusiĆ³n

We have seen with interesting examples in Cisco routers the main features of NAPALM. You do not have to worry that the devices handle different operating systems or that their commands are not the same. The NAPALM abstraction layer allows us to reuse the same code with different manufacturers. This is great, right?

It’s amazing how powerful NAPALM is. As with so few lines of code we can collect so much data and present it in this way. I know that like me you must be imagining a world of applications and uses that can help you in the networks that you are responsible for maintaining and operating. So don’t think too much about it, set up your labs and start creating.

By Michael Alvarez

2 Comments

  1. I’ve followed these sessions, very interesting as I’m a Cisco network engineer who is trying to get up to speed with using python to automate configuration tasks. I see that Napalm is a very powerful tool.

    Do you have an example where the hostnames and IP’s to be accessed are in either a CSV or JSON file? I’m trying and failing to get such to work with napalm, I’ve not long started with python.

    1. Hey, Hi Andy. Thank you for your comment.
      I use CSV files to read hostnames and IPs. Create a CSV file like this:

      hostname, ip_address
      router1, 192.168.0.1

      I use csv library to read it. I’m omitting the code to open a file, but this is an example on how to use csv to read hostnames and ip addresses:
      import csv
      routers = csv.DictReader(filelist)
      for router in routers:
      print(router[“hostname”], router[“ip_address”])

Leave a Reply

Your email address will not be published. Required fields are marked *