Intro

Napalm is a network automation library written in python that abstracts the differences between libraries such as Juniper's pyez and Arista's pyeapi bringing a common interface across many API's.

Napalm is well supported in the network community, originally started by David Barroso and Elisa Jasinska Napalm now has a long list of contributors. Napalm supports a large number of device types and configuration methods. Be sure the check the documentation to ensure your devices are supported.

Lab Environment

In this lab I will setup a Centos minimal server to install Napalm then connect to and extract data from a Cisco Nexus 9K, Juniper SRX and Arista EOS images. We will also cover applying device configuration with Napalm.

Topology

blog/napalm-getting-started/napalm-basic.svg

Code Versions

  • Centos/7 - 1702.01
  • Nexusv - 7.0.3.I6.1
  • vSRX - 12.1X47-D15.4
  • vEOS - 4.18.1F
  • Napalm - 1.2.0
  • Python - 2.7.13

Installation

I ran into issues with SSL certificates when using the napalm-eos module. The default python install on centos 7 is python 2.7.5 which is quite old and was the source of the problems. See the following link for a workaround if you insist on using python 2.7.5 . You can follow this post to upgrade the python install to a modern version on Centos 7 minimal.

Install required YUM packages

cmd
sudo yum install -y epel-release
sudo yum group install -y "Development tools"
sudo yum install -y gcc libffi-devel libxml2-devel libxslt-devel openssl openssl-devel sshpass python-virtualenv

# if did not install python from suggested blog post

sudo yum install -y gcc libffi-devel libxml2-devel libxslt-devel python2-pip python-devel openssl openssl-devel sshpass python-setuptools python-virtualenv

Dependancy installation instructions for yum and apt based systems can be found in the official docs.

Upgrade pip and setuptools

cmd
sudo pip2.7 install -U pip setuptools

Create a virtual environment and install Napalm

cmd
# as normal user

mkdir envs
virtualenv -p python2.7 envs/napalm-test
source envs/napalm-test/bin/activate

# inside virtual environment

pip install -U pip setuptools
pip install napalm

Usage

To connect to devices, access to the API's needs to be enabled.

cmd
# junos

system {
    services {
        netconf {
            ssh;
        }
    }
}

# eos

config
management api http-commands
  no shutdown
  protocol https
!

# nexus

conf t
feature scp-server
feature nxapi
interface mgmt 0
 vrf context managment
!

You will also need to configure the interfaces with IP addresses, I will leave this as an exercise for the reader.

Connecting To Devices

Connecting to a device requires you to know a few things about the device. IE the device type and the login credentials. Each supported device type has a driver that is used to setup the connection and perform actions on the devices.

python
# in python IDLE session

from napalm_base import get_network_driver

nxos_driver = get_network_driver('nxos')
nxos = nxos_driver('169.254.1.11', 'vagrant', 'vagrant')

junos_driver = get_network_driver('junos')
junos = junos_driver('169.254.1.12', 'root', 'Juniper')

eos_driver = get_network_driver('eos')
eos = eos_driver('169.254.1.13', 'vagrant', 'vagrant')

Now that the connection properties are setup we can connect to the devices.

python
nxos.open()

junos.open()

eos.open()

Getting Data

Each driver has a number of methods for retrieving data from and configuring devices. View the list of supported methods for each device type in the docs.

Now that we are connected its time to use some of the available methods.

python
nxos.get_facts()

# output

{'fqdn': 'nxosv',
 u'hostname': 'nxosv',
 u'interface_list': ['mgmt0',
  'Ethernet1/1',
  'Ethernet1/2',
  'Ethernet1/3',
  'Ethernet1/4',
  'Ethernet1/5',
  'Ethernet1/6',
  'Ethernet1/7',
  'Ethernet1/8',
  'Ethernet1/9',
  'Ethernet1/10',
  'Ethernet1/11',
  'Ethernet1/12',
  'Ethernet1/13',
  'Ethernet1/14',
  'Ethernet1/15',
  'Ethernet1/16',
  'Ethernet1/17',
  'Ethernet1/18',
  'Ethernet1/19',
  'Ethernet1/20',
  'Ethernet1/21',
  'Ethernet1/22',
  'Ethernet1/23',
  'Ethernet1/24',
  'Ethernet1/25',
  'Ethernet1/26',
  'Ethernet1/27',
  'Ethernet1/28',
  'Ethernet1/29',
  'Ethernet1/30',
  'Ethernet1/31',
  'Ethernet1/32',
  'Ethernet1/33',
  'Ethernet1/34',
  'Ethernet1/35',
  'Ethernet1/36',
  'Ethernet1/37',
  'Ethernet1/38',
  'Ethernet1/39',
  'Ethernet1/40',
  'Ethernet1/41',
  'Ethernet1/42',
  'Ethernet1/43',
  'Ethernet1/44',
  'Ethernet1/45',
  'Ethernet1/46',
  'Ethernet1/47',
  'Ethernet1/48',
  'Ethernet1/49',
  'Ethernet1/50',
  'Ethernet1/51',
  'Ethernet1/52',
  'Ethernet1/53',
  'Ethernet1/54',
  'Ethernet1/55',
  'Ethernet1/56',
  'Ethernet1/57',
  'Ethernet1/58',
  'Ethernet1/59',
  'Ethernet1/60',
  'Ethernet1/61',
  'Ethernet1/62',
  'Ethernet1/63',
  'Ethernet1/64'],
 u'model': 'NX-OSv Chassis',
 u'os_version': '7.0(3)I6(1)',
 u'serial_number': '9VV6Z8H5H4H',
 'uptime': 17801,
 u'vendor': u'Cisco'}


junos.get_interfaces()

# output

{'.local.': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'Unspecified',
  u'speed': -1},
 'dsc': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'Unspecified',
  u'speed': -1},
 'ge-0/0/0': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': 31272.0,
  u'mac_address': u'08:00:27:AE:F4:51',
  u'speed': 1000},
 'ge-0/0/1': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': 31272.0,
  u'mac_address': u'08:00:27:82:DC:49',
  u'speed': 1000},
 'gr-0/0/0': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'None',
  u'speed': 800},
 'gre': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'None',
  u'speed': -1},
 'ip-0/0/0': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'None',
  u'speed': 800},
 'ipip': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'None',
  u'speed': -1},
 'irb': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'4C:96:14:10:01:30',
  u'speed': -1},
 'lo0': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'Unspecified',
  u'speed': -1},
 'lsi': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'Unspecified',
  u'speed': -1},
 'lsq-0/0/0': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': 31273.0,
  u'mac_address': u'None',
  u'speed': -1},
 'lt-0/0/0': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'02:96:14:10:01:33',
  u'speed': 800},
 'mt-0/0/0': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'None',
  u'speed': 800},
 'mtun': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'None',
  u'speed': -1},
 'pimd': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'None',
  u'speed': -1},
 'pime': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'None',
  u'speed': -1},
 'pp0': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'Unspecified',
  u'speed': -1},
 'ppd0': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'None',
  u'speed': 800},
 'ppe0': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'None',
  u'speed': 800},
 'sp-0/0/0': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': 31273.0,
  u'mac_address': u'Unspecified',
  u'speed': 800},
 'st0': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'None',
  u'speed': -1},
 'tap': {u'description': u'',
  u'is_enabled': True,
  u'is_up': True,
  u'last_flapped': -1.0,
  u'mac_address': u'Unspecified',
  u'speed': -1},
 'vlan': {u'description': u'',
  u'is_enabled': True,
  u'is_up': False,
  u'last_flapped': 31282.0,
  u'mac_address': u'00:00:00:00:00:00',
  u'speed': 1000}}


eos.get_environment()

# output

{u'cpu': {0: {u'%usage': 5.3}},
 u'fans': {},
 u'memory': {u'available_ram': 66772, u'used_ram': 1830768},
 u'power': {},
 u'temperature': {}}

The great thing about Napalm is that it abstracts all the differences between the various API's and returns data in the same structure no matter the device type. Consider the get_arp_table() method. This method returns the arp table from nxos , junos and eos all in the same format making it much eaiser to reconcile the data in our code.

python
nxos.get_arp_table()

# output

[{u'age': 1015.0,
  u'interface': u'Ethernet1/1',
  u'ip': u'169.254.1.10',
  u'mac': u'08:00:27:FE:41:B6'},
 {u'age': 917.0,
  u'interface': u'Ethernet1/1',
  u'ip': u'169.254.1.12',
  u'mac': u'08:00:27:82:DC:49'},
 {u'age': 558.0,
  u'interface': u'Ethernet1/1',
  u'ip': u'169.254.1.13',
  u'mac': u'08:00:27:EB:40:24'}]


junos.get_arp_table()

# output

[{'age': 491.0,
  'interface': u'ge-0/0/0.0',
  'ip': u'10.0.2.2',
  'mac': u'52:54:00:12:35:02'},
 {'age': 1104.0,
  'interface': u'ge-0/0/1.0',
  'ip': u'169.254.1.10',
  'mac': u'08:00:27:FE:41:B6'},
 {'age': 851.0,
  'interface': u'ge-0/0/1.0',
  'ip': u'169.254.1.11',
  'mac': u'08:00:27:6C:EE:14'},
 {'age': 832.0,
  'interface': u'ge-0/0/1.0',
  'ip': u'169.254.1.13',
  'mac': u'08:00:27:EB:40:24'}]


eos.get_arp_table()

# output

[{u'age': 0.0,
  u'interface': u'Ethernet1',
  u'ip': u'169.254.1.10',
  u'mac': u'08:00:27:FE:41:B6'},
 {u'age': 0.0,
  u'interface': u'Ethernet1',
  u'ip': u'169.254.1.11',
  u'mac': u'08:00:27:6C:EE:14'},
 {u'age': 0.0,
  u'interface': u'Ethernet1',
  u'ip': u'169.254.1.12',
  u'mac': u'08:00:27:82:DC:49'},
 {u'age': 0.0,
  u'interface': u'Management1',
  u'ip': u'10.0.2.2',
  u'mac': u'52:54:00:12:35:02'},
 {u'age': 0.0,
  u'interface': u'Management1',
  u'ip': u'10.0.2.3',
  u'mac': u'52:54:00:12:35:03'}]

Configuring Devices

When configuring devices there are a number of ways to do it with Napalm. The general workflow is to load a candidate configuration, compare the configuration diff, if the changes are acceptable commit the changes.

Be sure the check the configuration support matrix for the methods supported by your devices.

Firstly we need to define a configuration. Configuration can either be defined as a variable or loaded from a file. In this case I will create a configuration variable.

python
nxos_ntp = 'ntp server 1.1.1.1 use-vrf default'

Once the configuration is defined we will load a candidate configuration. It is possible to either replace the whole configuration or merge with the existing configuration. In this instance I will merge the configuration with the current configuration.

python
nxos.load_merge_candidate(config=nxos_ntp)

Once the candidate configuration is loaded, we can compare it to the running configuration to be sure that it is what we intended.

python
nxos.compare_config()

# output

'ntp server 1.1.1.1 use-vrf default'

Once we are happy with the changes it is time to commit the configuration.

python
nxos.commit_config()

To confirm the changes were successful lets check the running configuration.

python
config = nxos.get_config()

print(config['running'])

# output

!Command: show running-config
. <snip>
.
.
ntp server 1.1.1.1 use-vrf default
.
.
. <snip>
line vty

As you can see the NTP server is now apart of the running config.

Outro

Napalm is a fantastic library with a broad range of supported devices across multiple vendors. The library is well maintained and the developers are very helpful. I suggest reading over the official documentation and having a play around with the library to see how it can add value to your organisation. If you have questions the best place to ask is over at the network.toCode() slack channel.