7 months, 2 weeks

NAPALM





NAPALM (Network Automation and Programmability Abstraction Layer with Multivendor support) is a Python library that implements a set of functions to interact with different router vendor devices using a unified API.
f you wanted to access different network devices with different operating systems through Python, we would probably have to use libraries developed for each operating system. It would be a tedious task to apply Network Programmability in these circumstances. To facilitate programming, NAPALM adds an abstraction layer. This layer allows us to use the same function to perform the same action on different operating systems.

Please also install the napalm package that is, python3 -m pip install napalm. 

We will use the following steps to establish a connection to our network device using NAPALM:
1. Import the napalm module:
import napalm
2. From the napalm module, retrieve the appropriate driver for your device's vendor. 
driver = napalm.get_network_driver("<insert driver name>")
3. Specify the connection details of your device. Note that you can pass a non-standard port in the optional arguments. If your device connects on the standard port, you can omit this:
conn_details = {
 "hostname": "<insert hostname>",
 "username": "<insert username>",
 "password": "<insert password>",
 "optional_args": {
 "port": <insert port as integer>
 }
}
4. With the driver and connection details specified, we can create a new device:
device = driver(**conn_details)
5. Now we can establish a connection to the device, print out a success message, and then close the connection again:
device.open()
print("Succesfully connected to the device")
device.close()

NAPALM uses the concept of drivers. These drivers abstract away the vendor-specific behavior of the device and, therefore, allow for a unified interface to be presented to the user. 
The drivers for the different vendors are as follows:
• eos for EOS
• junos for Juniper
• iosxr and ios for the two different iOS variants from Cisco 
• nxos and nxos_ssh for the Nexus switches from Cisco

While NAPALM tries to support all the functionality on all the different drivers, and therefore different vendors, some features are only available with certain drivers/devices. When designing your automation script, you can consult the support matrix provided by NAPALM. This matrix is available in the official documentation. Currently, it can be found at https://napalm.readthedocs.io/en/latest/support/index.html.

Issuing commands to a device using NAPALM
1. Import the napalm module:
import napalm
2. From the napalm module, retrieve the appropriate driver for your device's vendor. 
driver = napalm.get_network_driver("<insert driver name>")
3. Specify the connection details of your device. 
conn_details = {
 "hostname": "<insert hostname>",
 "username": "<insert username>",
 "password": "<insert password>",
 "optional_args": {
 "port": <insert port as integer>
 }
}
4. With the driver and connection details specified, we can create a new device:
device = driver(**conn_details)
5. Specify the commands that we want to run on our device. 
commands = [
 "show running-configuration",
 "show version"
]
6. Open a connection to the device:
device.open()
7. With our device connected, we will issue the commands and store the results in a variable called out:
out = device.cli(commands)
8. Finally, print out the command's output and close the connection to the device:
print(out)
device.close()

Testing network reachability using ping and NAPALM
We will use the following steps to establish a connection with our network device. Then, we will use the ping() method to test connectivity:
1. Import the napalm module:
import napalm
2. From the napalm module, retrieve the appropriate driver for your device's vendor. 
driver = napalm.get_network_driver("<insert driver name>")
3. Specify the connection details of your device.
conn_details = {
 "hostname": "<insert hostname>",
 "username": "<insert username>",
 "password": "<insert password>",
 "optional_args": {
"port": <insert port as integer>
 }
}
4. With the driver and connection details specified, we can create a new device:
device = driver(**conn_details)
5. Open a connection to the device:
device.open()
6. With our device connected, we can specify the hosts that we want to ping. 
to_ping = [
 "athaenas.com",
 "10.0.0.1"
]
7. Next, we loop over each of the hosts and ping them using the device's ping()method. Then, we close the device connection once all hosts have been pinged:
for host in to_ping:
    out = device.ping(host)
    print(f"Results for {host}")
    print(out)
device.close()

Backing up your device configuration using NAPALM
The following steps demonstrate how to connect to a device, read the different types of configurations, and then write them to a file:
1. Import the napalm module:
import napalm
2. From the napalm module, retrieve the appropriate driver for your device's vendor. 
driver = napalm.get_network_driver("<insert driver name>")
3. Specify the connection details of your device. 
conn_details = {
 "hostname": "<insert hostname>",
 "username": "<insert username>",
 "password": "<insert password>",
 "optional_args": {
 "port": <insert port as integer>
 }
}
4. With the driver and connection details specified, we can create a new device:
device = driver(**conn_details)
5. Open a connection to the device:
device.open()
6. With our device connected, we can use the get_config() method to retrieve the different configurations:
config = device.get_config()
7. Retrieve the hostname from the connection details and save it to a variable. This is so that we can later use it as part of the filename for our backup:
host = conn_details['hostname']
8. The returned config is not a plain string but a dictionary where the keys specify the different configuration types; here, they are the starting, running, and candidate configurations. We can loop over all the keys that are present and create a backup of our config that includes the hostname:
for conf_type in config.keys():
    with open(f"{host}-{conf_type}.conf.bak", "w") as f:
    f.writelines(config[conf_type])
9. Close the device:
device.close()

Gathering facts about your network device using NAPALM
The following steps demonstrate how to connect to a device and retrieve some facts about it using the getters that NAPALM provides:
1. Import the napalm module:
import napalm
2. From the napalm module, retrieve the appropriate driver for your device's vendor. 
driver = napalm.get_network_driver("<insert driver name>")
3. Specify the connection details of your device.
conn_details = {
 "hostname": "<insert hostname>",
 "username": "<insert username>",
 "password": "<insert password>",
 "optional_args": {
 "port": <insert port as integer>
 }
}
4. With the driver and connection details specified, we can create a new device:
device = driver(**conn_details)
5. Open a connection to the device:
device.open()
6. With our device connected, we can now use the getter to retrieve some information. First, let's use the get_facts() function to retrieve general facts, such as the software version and the uptime:
facts = device.get_facts()
7. Next, we also want to retrieve the interfaces that are configured on this device:
interfaces = device.get_interfaces()
8. With our facts retrieved, we can now print them back to the user. NAPALM returns the dictionaries again. For the facts, we'll start by printing out the key-value pairs:
print("Key facts about your device:")
for fact, value in facts.items():
     print(f"-> {fact}: {value}")
9. For our interfaces, the get_interfaces() function returns a dictionary where the key is the name of the interface, and the value is another dictionary that contains facts such as the Mac address or description. We can use two nested for loops to print out this information to the user:
print("Facts about your devices interfaces")
for intf_name, details in interfaces.items():
    print(f"{intf_name}")
    for fact, value in details.items():
       print(f"=> {fact}: {value}")
10. Finally, close the connection to the device:
device.close()

 The simple facts include, among other things, the following:
• uptime
• vendor
• os_version
• serial number
• hostname
Then, we also loop over all of our configured interfaces. The interfaces come as a nested dictionary where each interface's name is associated with a dictionary that contains, among other things, the details for that specific interface. These can include the following:
• ip address
• enabled status
• active status
• mac address
• mtu
• description

Creating and applying a configuration template with jinja2 and NAPALM
The following steps demonstrate how to render a configuration template and apply it to your device:
1. Open the acl.conf.tpl file. This is the template that is being rendered. First, we are going to specify the interface for our ACL and then loop over the two lists we have provided as an argument to create the necessary commands to allow or deny the host from the network:
interface {{ intf }}
ip access-group 1 in
{% for host in disallowed %}
access-list 1 deny host {{ host }}
{% endfor %}
{% for host in allowed %}
access-list 1 permit host {{ host }}
{% endfor %}
2. Continuing in our apply_template.py file, import the napalm module and the jinja2 module:
import napalm
from jinja2 import Environment, FileSystemLoader
3. Next, we need to set up our jinaj2 environment to render the template:
loader = FileSystemLoader("templates")
environment = Environment(loader=loader)
tpl = environment.get_template("acl.conf.tpl")

4. Now that we have our template, we need to define two lists that hold the IPs we want to deny and those we want to allow. Additionally, we are going to specify the interface that we want this ACL to be configured on in the following manner:
allowed = [
 "10.10.0.10",
 "10.10.0.11",
 "10.10.0.12"
]
disallowed = [
"10.10.0.50",
 "10.10.0.62"
]
intf = "ethernet0"
5. With the information that is needed to render our template now in place, we can create a configuration from our template and store it in a variable:
out = tpl.render(allowed=allowed,  disallowed=disallowed,  intf=intf)
6. With our configuration rendered, we are ready to connect to our device. 
driver = napalm.get_network_driver("<insert driver name>")
7. Specify the connection details of your device.
conn_details = {
 "hostname": "<insert hostname>",
 "username": "<insert username>",
 "password": "<insert password>",
 "optional_args": {
 "port": <insert port as integer>
 }
}
8. With the driver and connection details specified, we can create a new device:
device = driver(**conn_details)
9. Open a connection to the device:
device.open()
10. With our device connected, we can now use the load_merge_candidate()function to load the rendered template as a candidate for merging:
device.load_merge_candidate(config=out)
11. The previous step has only loaded the config for merging. In order to apply it, we have to commit the configuration using the commit_config() function:
device.commit_config()
12. Finally, close the device connection:
device.close()

Rolling back configuration changes using NAPALM
The following steps demonstrate how to connect to a device, view the differences between the new configuration and the old configuration, and then lets you choose whether you want to apply the configuration or not:
1. Import the napalm module:
import napalm
2. From the napalm module, retrieve the appropriate driver for your device's vendor. 
driver = napalm.get_network_driver("<insert driver name>")
3. Specify the connection details of your device.
conn_details = {
 "hostname": "<insert hostname>",
 "username": "<insert username>",
 "password": "<insert password>",
 "optional_args": {
 "port": <insert port as integer>
 }
}
4. With the driver and connection details specified, we can create a new device:
device = driver(**conn_details)
5. Open a connection to the device:
device.open()
6. With our device connected, first, we create a new configuration.
config_change = [
 "hostname test"
]
7. Since NAPALM requires a string as the replacement config, we join all the entries of our config_change list with newlines:
new_config "\n".join(config_change)
device.load_merge_candidate(config=new_config)

8. Then, we compare the differences between the currently running configuration and the configuration loaded for merging and display this information:
print(device.compare_config())
9. With the difference represented visually, we can ask the user to specify whether to either apply the configuration candidate or disregard it:
user_in = input("Continue? [y/n]")
if user_in == "y":
   device.commit_config()
   print("Applied config to device")
else:
   device.rollback()
   print("Rolled back to previous config")
10. Finally, close the connection to the device:
device.close()

Validating deployments using NAPALM
To verify the compliance of a deployment based on a .yaml file describing the desired state, follow these steps:
1. Let's start by defining the desired state in our compliance.yaml file:
---
- get_facts:
 os_version: 4.17
- get_interfaces_ip:
 GigabitEthernet1:
 ipv4:
    10.10.10.1
2. Going back to our validate_deployment.py, we can now import the napalm module:
import napalm
3. From the napalm module, retrieve the appropriate driver for your device's vendor.
driver = napalm.get_network_driver("<insert driver name>")
4. Specify the connection details of your device. 
conn_details = {
 "hostname": "<insert hostname>",
 "username": "<insert username>",
 "password": "<insert password>",
 "optional_args": {
 "port": <insert port as integer>
 }
}
5. With the driver and connection details specified, we can create a new device:
device = driver(**conn_details)
6. Open a connection to the device:
device.open()
7. With our device connected, we can run our compliance report using the compliance_report() function:
out = device.compliance_report("compliance.yaml")
print(out)
8. Finally, close the connection to the device:
device.close()


 


Responses(0)