NAPALM Introduction - Part 1

Introduction

NAPALM stands for ‘Network Automation and Programmability Abstraction Layer with Multivendor support (NAPALM)’ and is a Python library that can be used to automate and interact with networking devices and OS’es using a unified API. So if you have multiple vendors in your network and don’t want to write a dedicated automation script per vendor device, then NAPALM is exactly what you are looking for.

Disclaimer: the code in this post is not production-grade code. One should never store the username and password in the clear, not in the source code itself. The examples in the post are merely conceptual and for informational purposes.

NAPALM primarily focuses on the following:

  • configuration management: configurations can be replaced or merged with existing configurations
  • Gather operational state: NAPALM allows you to retrieve operational data using a consistent interface and data model (e.g. NAPALM getters)
  • Validate operational state: NAPALM compares an intended operational state against an actual state so we can validate a network before and after a change

Installing the library

Installing NAPALM is very straightforward and is described very well in the online documentation.

WAUTERW-M-65P7:Napalm_intro wauterw$ python3 -m venv venv
WAUTERW-M-65P7:Napalm_Introduction wauterw$ source venv/bin/activate
(venv) WAUTERW-M-65P7:Napalm_Introduction wauterw$ pip3 install napalm
Collecting napalm
**Truncated**
Successfully installed ciscoconfparse-1.5.1 colorama-0.4.3 dnspython-1.16.0 junos-eznc-2.2.1 napalm-2.5.0 netaddr-0.7.19 netmiko-2.4.2 nxapi-plumbing-0.5.2 passlib-1.7.2 pyIOSXR-0.53 pyYAML-5.3.1 pyeapi-0.8.3

NAPALM CLI

Installing NAPALM will also provide a CLI command.

(venv) WAUTERW-M-65P7:Napalm_Introduction wauterw$ 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

Getting to know NAPALM

In this first use case, we will give some examples for you to build an understanding on how NAPALM works. First of course, we will load the NAPALM library, more in particular the get_network_driver method. This immediately triggers the question what network devices (or drivers) are supported. This can be found back in the documentation, see here the support matrix.

I’ll be testing here with an IOS XR device from Cisco DevNet. We load the correct driver using the get_network_driver method. Here you essentially tell NAPALM that it needs to connect to an ‘IOSXR’ device. As you can see in the support matrix, under the hood NAPALM used the pyIOSXR Python library.

After making a connection to the device using the driver() method (the result from the get_network_driver method), we just open the connection using the open() connection.

As we want to show what methods are supported by this connection, we are using the dir() function to print these.

from napalm import get_network_driver
import json

driver_xr = get_network_driver("iosxr")

device = {
    "device_type": "cisco_xr",
    "ip": "sbx-iosxr-mgmt.cisco.com",
    "username": "***",
    "password": "***",
    "port": "8181",
}

device_xr = driver_xr(hostname=device['ip'], username=device['username'], password=device['password'], optional_args={'port':device['port']})
device_xr.open()
get_method = dir(device_xr)
print(json.dumps(get_method, sort_keys=True, indent=4))

This results in the following:

(venv) WAUTERW-M-65P7:Napalm_Introduction wauterw$ python3 start.py 
[
    "__class__",
    "__del__",
     ***Truncated***
    "compare_config",
    "compliance_report",
    "connection_tests",
    "device",
    "hostname",
    ***Truncated***
    "traceroute",
    "username"
]

The output represents a list of methods that can be called on this connection. Let’s do this by adding following lines to our script:

get_hostname = device_xr.hostname
print(f"Hostname is {get_hostname}")

This will result in:

venv) WAUTERW-M-65P7:Napalm_Introduction wauterw$ python3 start.py 
Hostname is sbx-iosxr-mgmt.cisco.com

Another method (refer to the output before), is the get_facts method. Let’s call this by adding the following lines to our script:

(venv) WAUTERW-M-65P7:Napalm_Introduction wauterw$ python3 start.py 
{
    "fqdn": "iosxr1",
    "hostname": "iosxr1",
    "interface_list": [
        "GigabitEthernet0/0/0/0",
        "GigabitEthernet0/0/0/0.144",
        "GigabitEthernet0/0/0/1",
        "GigabitEthernet0/0/0/2",
        "GigabitEthernet0/0/0/3",
        "GigabitEthernet0/0/0/4",
        "GigabitEthernet0/0/0/5",
        "GigabitEthernet0/0/0/6",
        "Loopback100",
        "Loopback200",
        "MgmtEth0/RP0/CPU0/0",
        "Null0"
    ],
    "model": "R-IOSXRV9000-CC",
    "os_version": "6.5.3",
    "serial_number": "05EFB4E4D3D",
    "uptime": 78892,
    "vendor": "Cisco"
}

As you can witness, this will give an overview of some important information, e.g hostname, interface overview, serial number and more.

Let’s pretend we want to know more about a given interface, let’s say the IP configuration or the interface counters. Let’s do this next.

get_interfaces_counters = device_xr.get_interfaces_counters()
print(json.dumps(get_interfaces_counters, sort_keys=True, indent=4))

get_interfaces_ip = device_xr.get_interfaces_ip()
print(json.dumps(get_interfaces_ip, sort_keys=True, indent=4))

The output of above print statements is the provided in the below snippet. Note that I truncated the output quite a bit but I’m sure you get the picture.

(venv) WAUTERW-M-65P7:Napalm_Introduction wauterw$ python3 start.py 
{
    "GigabitEthernet0/0/0/0": {
        "rx_broadcast_packets": 0,
        "rx_discards": 0,
        "rx_errors": 0,
        "rx_multicast_packets": 0,
        "rx_octets": 0,
        "rx_unicast_packets": 0,
        "tx_broadcast_packets": 0,
        "tx_discards": 0,
        "tx_errors": 0,
        "tx_multicast_packets": 0,
        "tx_octets": 0,
        "tx_unicast_packets": 0
    },
    ***Truncated***
    "MgmtEth0/RP0/CPU0/0": {
        "ipv4": {
            "10.10.20.175": {
                "prefix_length": 24
            }
        }
    }
}

The code we have been using in this blog post can be found here. The start.py file covers exactly what we discussed in this post.