Lab BONUS 01.2 - Using NAPALM with Cisco IOS¶
This lab uses multi-vendor NAPALM with Cisco IOS in particular.
- Lab BONUS 01.2 - Using NAPALM with Cisco IOS
- Task 1 - Basic Configuration Merge with NAPALM
- Task 2 - NAPALM Getters
Task 1 - Basic Configuration Merge with NAPALM¶
In this task, you will explore working with the NAPALM Python library to perform a configuration merge on a Cisco router.
Step 1-1¶
Connect to csr1 and configure three SNMP community strings.
ntc@ntc-training:~$ ssh csr1
Warning: Permanently added 'csr1,172.18.0.3' (RSA) to the list of known hosts.
Password:
csr1#conf t
Enter configuration commands, one per line. End with CNTL/Z.
csr1(config)#snmp-server community NTC ro
csr1(config)#snmp-server community networktocode ro
csr1(config)#snmp-server community public ro
csr1(config)#end
Step 1-2¶
Create a new file called snmp.conf in your /home/ntc/labs/python/ directory and open the file in the editor of your choice.
Step 1-3¶
Take the config snippet below and save it in the file just created (snmp.conf). These commands will be used to directly configure the routers.
no snmp-server community NTC ro
snmp-server community networktocode ro
snmp-server community public ro
snmp-server community private rw
snmp-server community supersecret rw
snmp-server location SYDNEY
snmp-server contact JOHN_SMITH
Step 1-4¶
Enter the Python shell from the same directory.
ntc@ntc-training:python$ python
Python 3.8.12 (default, Oct 13 2021, 09:22:51)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
Step 1-5¶
Load the correct NAPALM driver. Since we're using the Cisco router in this lab, load the ios driver.
Step 1-6¶
Create an ios device object for csr1 using the previously loaded driver. Use the variable name device.
Step 1-7¶
Use help on device. You will be able to see all of supported properties and methods of this object.
Just like you've seen with built-in data types, you can use help() and dir() on 3rd party object types too.
There are two primary methods of the device class you'd work with to apply configurations. They are load_merge_candidate and load_replace_candidate.
These are used to load a partial configuration (and merge with the existing configuration) and load a full configuration that will replace the full running configuration, respectively. You'll see these if you look at the full help documentation (the above is just a snippet from the help output).
For this lab, we are using load_merge_candidate.
Step 1-8¶
Open a connection to the device. This is done using the open() method.
Step 1-9¶
Load the configuration you created in Step 3 onto the device.
This is done by using the load_merge_candiate method of the device object.
As soon as you hit enter in this step, NAPALM is loading this configuration onto the device, but NOT committing it to the running configuration. How this happens is different for every OS.
Step 1-10¶
Use compare_config() to show candidate configuration diffs.
>>> diffs = device.compare_config()
>>>
>>> print(diffs)
-no snmp-server community NTC ro
+snmp-server community private rw
+snmp-server community supersecret rw
+snmp-server location SYDNEY
+snmp-server contact JOHN_SMITH
As stated before, these changes are not applied to the running configuration yet. Instead, a merge_config.txt is created on the device. Feel free to connect to csr1 and verify that none of those configuration lines has been applied yet.
Step 1-11¶
Commit the config to the device while from the Python shell.
This is when the configuration will be activated and committed to the running configuration.
If you wanted to discard this change rather than commit, alternatively you could have run the following:
Step 1-12¶
To rollback, you can use the rollback method.
Feel free to view the config on the CLI of the device before and after you issue the next command.
Step 1-13¶
Load the new SNMP configuration on the three other Cisco routers.
Use a for loop to build the proper NAPALM device object as well as load and commit the configuration for each IOS-XE router.
Task 2 - NAPALM Getters¶
In this task you will now practice using NAPALM getters on several platforms at the same time. Since NAPALM abstracts and normalizes data, you will see how easy it is to work with devices from other vendors using the same language constructs.
Step 2-1¶
Enter into a new Python shell from your working directory.
ntc@ntc-training:python$ python
Python 3.8.12 (default, Oct 13 2021, 09:22:51)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
Step 2-2¶
Import get_network_driver from napalm and load the drivers for IOS, NXOS, Junos and EOS.
>>> from napalm import get_network_driver
>>>
>>> ios_driver = get_network_driver('ios')
>>> nxos_driver = get_network_driver('nxos')
>>> eos_driver = get_network_driver('eos')
>>>
Step 2-3¶
Create 3 devices, one for each NAPALM driver you have loaded.
>>> ios_device = ios_driver('csr1', 'ntc', 'ntc123')
>>> nxos_device = nxos_driver('nxos-spine1', 'ntc', 'ntc123')
>>> eos_device = eos_driver('eos-spine1', 'ntc', 'ntc123')
>>>
Step 2-4¶
Open a NAPALM session for all the devices you have just created.
Note: if you get an SSL exception for NXOS here, please connect to the device via ssh and ensure the following command is present:
nxapi ssl protocols TLSv1 TLSv1.1 TLSv1.2
Step 2-5¶
Import json library and pretty print facts for all devices.
>>> import json
>>>
>>> print(json.dumps(ios_device.get_facts(), indent=4))
{
"uptime": 11280,
"vendor": "Cisco",
"os_version": "Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 17.1.1, RELEASE SOFTWARE (fc3)",
"serial_number": "9SAGBHTUEE9",
"model": "CSR1000V",
"hostname": "csr1",
"fqdn": "csr1.ntc.com",
"interface_list": [
"GigabitEthernet1",
"GigabitEthernet2",
"GigabitEthernet3",
"GigabitEthernet4",
"GigabitEthernet5",
"GigabitEthernet6",
"GigabitEthernet7",
"GigabitEthernet8",
"GigabitEthernet9"
]
}
>>>
>>> print(json.dumps(nxos_device.get_facts(), indent=4))
{
"vendor": "Cisco",
"serial_number": "9X487AEJUEE",
"model": "Nexus9000 C9300v Chassis",
"hostname": "nxos-spine1",
"os_version": "9.3(3)",
"uptime": 11187,
"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",
"Vlan1"
],
"fqdn": "nxos-spine1.ntc.com"
}
>>>
>>> print(json.dumps(eos_device.get_facts(), indent=4))
{
"hostname": "eos-spine1",
"fqdn": "eos-spine1.ntc.com",
"vendor": "Arista",
"model": "vEOS",
"serial_number": "",
"os_version": "4.22.4M-15583082.4224M",
"uptime": 10761,
"interface_list": [
"Ethernet1",
"Ethernet2",
"Ethernet3",
"Ethernet4",
"Ethernet5",
"Ethernet6",
"Ethernet7",
"Ethernet8",
"Ethernet9",
"Ethernet10",
"Ethernet11",
"Ethernet12",
"Ethernet13",
"Ethernet14",
"Ethernet15",
"Ethernet16",
"Ethernet17",
"Ethernet18",
"Ethernet19",
"Management1"
]
}
>>>
Step 2-6¶
Print all interfaces for all devices. Output is not shown below due to its length.
>>> print(json.dumps(ios_device.get_interfaces(), indent=4))
>>> print(json.dumps(nxos_device.get_interfaces(), indent=4))
>>> print(json.dumps(eos_device.get_interfaces(), indent=4))
>>>
Pay special attention to the output structure, noting it is the same regardless of the device platform.
Step 2-7¶
To further drive home the normalization aspect, print out the up/down state of each interface across all three devices using a function.
First, copy/paste the function definition below into the Python interpreter, then use it for each device on their facts dictionary.
def print_up_down_state(device_interfaces):
for interface in device_interfaces:
state = "up" if device_interfaces[interface]['is_up'] else "down"
print(f"Interface {interface} is {state}")
>>> def print_up_down_state(device_interfaces):
... for interface in device_interfaces:
... state = "up" if device_interfaces[interface]['is_up'] else "down"
... print(f"Interface {interface} is {state}")
...
>>> print_up_down_state(ios_device.get_interfaces())
Interface GigabitEthernet1 is up
Interface GigabitEthernet2 is up
Interface GigabitEthernet3 is up
Interface GigabitEthernet4 is up
Interface GigabitEthernet5 is up
Interface GigabitEthernet6 is down
Interface GigabitEthernet7 is down
Interface GigabitEthernet8 is down
Interface GigabitEthernet9 is down
>>> print_up_down_state(nxos_device.get_interfaces())
Interface Ethernet1/1 is up
Interface Ethernet1/2 is up
Interface Ethernet1/3 is up
Interface Ethernet1/4 is up
Interface Ethernet1/5 is up
... OUTPUT SNIPPED ...
>>> print_up_down_state(eos_device.get_interfaces())
Interface Management1 is up
Interface Ethernet8 is up
Interface Ethernet9 is up
Interface Ethernet2 is up
... OUTPUT SNIPPED ...
Step 2-8¶
Finally, try printing just the "running" configuration of each device using the get_config() method:
What object type is it?
Does it store more than the running configuration? Try to print only the "running" configuration now.