NAPALM Network Automation Python: Making Configurations in a Multivendor Network
Introduction
In this article we will learn about the NAPALM functions and methods to configure routers and switches in a multi-vendor network. We will also make use of two important tools used in automation, Jinja and YAML. This is a continuation of the article in which Collecting data in a multi-vendor network and 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.
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 in Cisco and Huawei devices. And it is where I basically share the code of what was seen in the video.
Jinja Template y YAML
Jinja is basically an engine used to create templates in Python. With Jinja we plan to create the configuration templates for our routers.
YAML on the other hand is a standard for serializing data in a human-readable form. With YAML we plan to define the parameters or values that we will use in the Jinja templates.
Let’s see it to make it easier to understand.
Cisco_template.yml
interfaces: GigabitEthernet0/1: description: "to_VRP-R2 Eth1/0/1 External" ipv4_addr: 172.17.3.1 ipv4_mask: 255.255.255.252 bgp: as: 300 neighbors: vrp_router: as: 200 ipv4_addr: 172.17.3.2
Huawei_template.yml
interfaces: Ethernet1/0/1: description: "to_IOS-R3 Gi0/1 External" ipv4_addr: 172.17.3.2 ipv4_mask: 255.255.255.252 bgp: as: 200 neighbors: ios_router: as: 300 ipv4_addr: 172.17.3.1
Cisco_template.j2
{% for if_name,if_data in interfaces.items() %} interface {{if_name}} description {{if_data.description}} ip address {{if_data.ipv4_addr}} {{if_data.ipv4_mask}} no shutdown {% endfor %} ! router bgp {{bgp.as}} neighbor {{bgp.neighbors.vrp_router.ipv4_addr}} remote-as {{bgp.neighbors.vrp_router.as}} ! address-family ipv4 neighbor {{bgp.neighbors.vrp_router.ipv4_addr}} activate exit-address-family
Huawei_template.j2
{% for if_name,if_data in interfaces.items() %} interface {{if_name}} description {{if_data.description}} ip address {{if_data.ipv4_addr}} {{if_data.ipv4_mask}} undo shutdown undo dcn {% endfor %} # bgp {{bgp.as}} peer {{bgp.neighbors.ios_router.ipv4_addr}} as-number {{bgp.neighbors.ios_router.as}} # ipv4-family unicast peer {{bgp.neighbors.ios_router.ipv4_addr}} enable #
In the video you will find the explanation of this code.
NAPALM Python: Config Code
What we will do now is that we will edit the script that we wrote in the previous video for the iOS and IOS-XR and Huawei VRP routers.
import napalm from tabulate import tabulate from jinja2 import Environment, FileSystemLoader import yaml def main(): driver_ios = napalm.get_network_driver("ios") driver_iosxr = napalm.get_network_driver("iosxr") driver_vrp = napalm.get_network_driver("ce") device_list = [["vrp-r2", "vrp", "router"],["ios-r3", "ios", "router"]] # device_list = [["ios-sw2","ios", "switch"],["iosxr-r1", "iosxr", "router"], # ["ios-r3", "ios", "router"],["vrp-r2", "vrp", "router"],["vrp-sw1", "vrp", "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" ) ) elif device[1] == "vrp": network_devices.append( driver_vrp( 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"] ]) #Testing Getters device.load_merge_candidate(filename=None, config=get_template_config(device_facts["vendor"])) print("\nDiff:") print(device.compare_config()) # You can commit or discard the candidate changes. try: choice = raw_input("\nWould you like to commit these changes? [yN]: ") except NameError: choice = input("\nWould you like to commit these changes? [yN]: ") if choice == "y": print("Committing ...") device.commit_config() else: print("Discarding ...") device.discard_config() device.close() print("Done.") print(tabulate(devices_table, headers="firstrow")) def get_template_config(vendor): #This loads data from YAML into Python dictionary config_data = yaml.load(open('{}_template.yml'.format(vendor)), Loader=yaml.FullLoader) #This line uses the current directory and loads the jinja2 template env = Environment(loader = FileSystemLoader('.'), trim_blocks=True, lstrip_blocks=True) template = env.get_template('{}_template.j2'.format(vendor)) #Return the template with data print(template.render(config_data)) return(template.render(config_data)) 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 vrp-r2 ...
Getting device facts
interface Ethernet1/0/1
description to_IOS-R3 Gi0/1 External
ip address 172.17.3.2 255.255.255.252
undo shutdown
undo dcn
#
bgp 200
peer 172.17.3.1 as-number 300
#
ipv4-family unicast
peer 172.17.3.1 enable
#
Diff:
description to_IOS-R3 Gi0/1 External
ip address 172.17.3.2 255.255.255.252
peer 172.17.3.1 as-number 300
peer 172.17.3.1 enable
Would you like to commit these changes? [yN]: y
Committing ...
Done.
Connecting to ios-r3 ...
Getting device facts
interface GigabitEthernet0/1
description to_VRP-R2 Eth1/0/1 External
ip address 172.17.3.1 255.255.255.252
no shutdown
!
router bgp 300
neighbor 172.17.3.2 remote-as 200
!
address-family ipv4
neighbor 172.17.3.2 activate
exit-address-family
Diff:
+interface GigabitEthernet0/1
+ description to_VRP-R2 Eth1/0/1 External
+ ip address 172.17.3.1 255.255.255.252
- no shutdown
+router bgp 300
+ neighbor 172.17.3.2 remote-as 200
+ address-family ipv4
+ neighbor 172.17.3.2 activate
Would you like to commit these changes? [yN]: y
Committing ...
Done.
hostname vendor model uptime serial_number
---------- -------- ------- -------- ---------------------
VRP-R2 Huawei NE40E 1020 []
IOS-R3 Cisco IOSv 1140 95ZAVRE4Q305F6ET85347
Conclusion
Let’s appreciate how with Network Programmability we configure interfaces and a BGP peering. For a single Peering it is probably faster to do this configuration via the command line.
The truth is that this example makes sense when we have to make massive configurations. In addition to being faster, the use of Jinja and YAML templates reduces the risk of human errors and maintains consistency in configurations.