NAPALM Network Automation Python: Collect Data from Multiple Vendors
Introduction
In this article we will learn about the functions and methods of NAPALM to collect data on routers and switches from multiple vendors. We will write multiple Python scripts to collect data from this network, which you can use to create reports or investigate a problem. This is a continuation of the article “Working with Huawei VRP” on and it 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 here where I basically share the code of what was seen in the video.
NAPALM Python: Get Facts, Get Interfaces and Get MAC Address table
We are going to modify the script that we did in the video Working with Huawei VRP so that it prints information in a table such as the hostname, manufacturer, model, time they have in operation and the serial number, a table with the interfaces and another table with the MAC address table of the Huawei and Cisco devices. Here is the modified code:
import napalm from tabulate import tabulate 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 = [["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"]] devices_table_int = [["hostname","interface","is_up", "is_enabled", "description", "speed", "mtu", "mac_address"]] devices_mac_table = [["hostname","mac", "interface", "vlan", "static"]] 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"] ]) print("Getting device interfaces") device_interfaces = device.get_interfaces() for interface in device_interfaces: if device_interfaces[interface]['is_up']: devices_table_int.append([device_facts["hostname"], interface, device_interfaces[interface]['is_up'], device_interfaces[interface]['is_enabled'], device_interfaces[interface]['description'], device_interfaces[interface]['speed'], device_interfaces[interface]['mtu'], device_interfaces[interface]['mac_address'] ]) if "SW" in device_facts["hostname"]: print("Getting Mac Address Table from Switch") device_mac_info = device.get_mac_address_table() for mac_entry in device_mac_info: devices_mac_table.append([device_facts["hostname"], mac_entry["mac"], mac_entry["interface"], mac_entry["vlan"], mac_entry["static"], ]) device.close() print("Done.") print(tabulate(devices_table, headers="firstrow")) print() print(tabulate(devices_table_int, headers="firstrow")) print() print(tabulate(devices_mac_table, headers="firstrow")) print() if __name__ == '__main__': main()
In the video you will find the explanation of this code.
Let’s see the output of the Script:
Connecting to ios-sw2 ...
Getting device facts
Getting device interfaces
Getting Mac Address Table from Switch
Done.
Connecting to iosxr-r1 ...
Getting device facts
Getting device interfaces
Done.
Connecting to ios-r3 ...
Getting device facts
Getting device interfaces
Done.
Connecting to vrp-r2 ...
Getting device facts
Getting device interfaces
Done.
Connecting to vrp-sw1 ...
Getting device facts
Getting device interfaces
Getting Mac Address Table from Switch
Done.
hostname vendor model uptime serial_number
---------- -------- ------- -------- ---------------------
IOS-SW2 Cisco IOSv 1380 9QEN7MU6YX0
IOSXR-R1 Cisco 1394
IOS-R3 Cisco IOSv 1380 95ZAVRE4Q305F6ET85347
VRP-R2 Huawei NE40E 1320 []
VRP-SW1 Huawei CE6800 1320 []
hostname interface is_up is_enabled description speed mtu mac_address
---------- ------------------------- ------- ------------ -------------------------------- ------- ----- -----------------
IOS-SW2 GigabitEthernet0/0 True True 1000 1500 50:00:00:0A:00:00
IOS-SW2 GigabitEthernet0/1 True True Connected to IOSXR-R1 L2-Trunk 1000 1500 50:00:00:0A:00:01
IOS-SW2 GigabitEthernet0/2 True True 1000 1500 50:00:00:0A:00:02
IOS-SW2 GigabitEthernet0/3 True True to_PC_1 1000 1500 50:00:00:0A:00:03
IOS-SW2 GigabitEthernet1/0 True True 1000 1500 50:00:00:0A:00:04
IOS-SW2 GigabitEthernet1/1 True True 1000 1500 50:00:00:0A:00:05
IOS-SW2 GigabitEthernet1/2 True True 1000 1500 50:00:00:0A:00:06
IOS-SW2 GigabitEthernet1/3 True True 1000 1500 50:00:00:0A:00:07
IOS-SW2 Vlan10 True True Management 10.10.10.10/24 1000 1500 50:00:00:0A:80:0A
IOSXR-R1 GigabitEthernet0/0/0/0 True True to_IOS-R3 Gi0/2 External 1000 1514 50:00:00:09:00:01
IOSXR-R1 GigabitEthernet0/0/0/1 True True to_IOS-SW2 Gi0/1 1000 1514 50:00:00:09:00:02
IOSXR-R1 GigabitEthernet0/0/0/1.1 True True to_IOS-SW2 Gi0/1 Vlan 20 1000 1518 50:00:00:09:00:02
IOSXR-R1 GigabitEthernet0/0/0/1.10 True True to_IOS-SW2 Gi0/1 Vlan 10 1000 1518 50:00:00:09:00:02
IOSXR-R1 GigabitEthernet0/0/0/2 True True to_VRP-R2 Eth1/0/2 External 1000 1514 50:00:00:09:00:03
IOSXR-R1 Loopback0 True True Management IP 0 1500
IOSXR-R1 Null0 True True 0 1500
IOS-R3 GigabitEthernet0/0 True True to_IOSXR-R1 Gi0/0/0/0 External 1000 1500 50:00:00:08:00:00
IOS-R3 GigabitEthernet0/1 True True to_VRP-R2 Eth1/0/1 External 1000 1500 50:00:00:08:00:01
IOS-R3 Loopback0 True True 8000 1514
IOS-R3 Loopback1 True True 8000 1514
IOS-R3 Loopback100 True True ***CISCO LOOPBACK 100**** 8000 1514
VRP-R2 Ethernet1/0/0.30 True True to_VRP-SW1 GE 1/0/0 - VLAN 30 -1 1500 38:49:80:01:01:00
VRP-R2 Ethernet1/0/1 True True to_IOS-R3 Gi0/1 - External -1 1500 38:49:80:01:01:01
VRP-R2 Ethernet1/0/2 True True to_IOSXR-R1 Gi0/0/0/2 - External -1 1500 38:49:80:01:01:02
VRP-R2 LoopBack0 True True to_VRP-SW2 GE 1/0/1 - VLAN 1 -1 1500
VRP-R2 LoopBack100 True True test -1 1500
VRP-R2 LoopBack200 True True ***HUAWEI LOOPBACK 200**** -1 1500
VRP-R2 NULL0 True True -1 1500
VRP-SW1 GE1/0/0 True True to_VRP-R2 Eth 1/0/0 - LAN -1 70:7B:E8:CD:0D:7B
VRP-SW1 GE1/0/3 True True to_PC1 - LAN -1 70:7B:E8:CD:0D:7B
VRP-SW1 NULL0 True True -1 1500
VRP-SW1 Vlanif30 True True -1 1500 70:7B:E8:CD:0D:7B
hostname mac interface vlan static
---------- ----------------- ----------- ------ --------
IOS-SW2 0A:00:27:00:00:19 Gi0/0 1 False
IOS-SW2 50:00:00:09:00:02 Gi0/1 1 False
IOS-SW2 00:50:00:00:0B:00 Gi0/3 10 False
IOS-SW2 50:00:00:09:00:02 Gi0/1 10 False
VRP-SW1 38:49:80:01:01:00 GE1/0/0 30 False
NAPALM Python: Get Interfaces IP
We are going to modify the code above to add other functionality. This functionality will allow you to print a table with information on the layer 3 interfaces of the routers, that is, the IP addresses and their network masks. In addition to this information, we are going to include some of the counters of these interfaces to display the Errors or Discards of packets.
import napalm from tabulate import tabulate 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 = [["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"]] devices_table_int = [["hostname","interface","is_up", "is_enabled", "description", "speed", "mtu", "mac_address"]] devices_mac_table = [["hostname","mac", "interface", "vlan", "static"]] devices_int_ip_table = [["hostname","interface","is_up","ipv4/mask", "tx_errors","rx_errors","tx_discards","rx_discards"]] 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"] ]) print("Getting device interfaces") device_interfaces = device.get_interfaces() for interface in device_interfaces: if device_interfaces[interface]['is_up']: devices_table_int.append([device_facts["hostname"], interface, device_interfaces[interface]['is_up'], device_interfaces[interface]['is_enabled'], device_interfaces[interface]['description'], device_interfaces[interface]['speed'], device_interfaces[interface]['mtu'], device_interfaces[interface]['mac_address'] ]) if "SW" in device_facts["hostname"]: print("Getting Mac Address Table from Switch") device_mac_info = device.get_mac_address_table() for mac_entry in device_mac_info: devices_mac_table.append([device_facts["hostname"], mac_entry["mac"], mac_entry["interface"], mac_entry["vlan"], mac_entry["static"], ]) print("Getting physical interfaces ip and counters") device_interfaces_ip = device.get_interfaces_ip() device_interfaces_counters = device.get_interfaces_counters() for interface in device_interfaces_ip: if device_interfaces[interface]['is_up'] and interface in device_interfaces_counters: ip_address = list(device_interfaces_ip[interface]['ipv4'].keys())[0] devices_int_ip_table.append([device_facts["hostname"], interface, device_interfaces[interface]['is_up'], "{}/{}".format(ip_address, device_interfaces_ip[interface]['ipv4'][ip_address]['prefix_length']), device_interfaces_counters[interface]['tx_errors'], device_interfaces_counters[interface]['rx_errors'], device_interfaces_counters[interface]['tx_discards'], device_interfaces_counters[interface]['rx_discards'] ]) device.close() print("Done.") print(tabulate(devices_table, headers="firstrow")) print() print(tabulate(devices_table_int, headers="firstrow")) print() print(tabulate(devices_mac_table, headers="firstrow")) print() print(tabulate(devices_int_ip_table, headers="firstrow")) if __name__ == '__main__': main()
In the video you will find the explanation of this code.
Let’s see the output of the Script:
Connecting to ios-sw2 ...
Getting device facts
Getting device interfaces
Getting Mac Address Table from Switch
Getting physical interfaces ip and counters
Done.
Connecting to iosxr-r1 ...
Getting device facts
Getting device interfaces
Getting physical interfaces ip and counters
Done.
Connecting to ios-r3 ...
Getting device facts
Getting device interfaces
Getting physical interfaces ip and counters
Done.
Connecting to vrp-r2 ...
Getting device facts
Getting device interfaces
Getting physical interfaces ip and counters
Done.
Connecting to vrp-sw1 ...
Getting device facts
Getting device interfaces
Getting Mac Address Table from Switch
Getting physical interfaces ip and counters
Done.
hostname vendor model uptime serial_number
---------- -------- ------- -------- ---------------------
IOS-SW2 Cisco IOSv 1440 9QEN7MU6YX0
IOSXR-R1 Cisco 1478
IOS-R3 Cisco IOSv 1440 95ZAVRE4Q305F6ET85347
VRP-R2 Huawei NE40E 1380 []
VRP-SW1 Huawei CE6800 1440 []
hostname interface is_up is_enabled description speed mtu mac_address
---------- ------------------------- ------- ------------ -------------------------------- ------- ----- -----------------
IOS-SW2 GigabitEthernet0/0 True True 1000 1500 50:00:00:0A:00:00
IOS-SW2 GigabitEthernet0/1 True True Connected to IOSXR-R1 L2-Trunk 1000 1500 50:00:00:0A:00:01
IOS-SW2 GigabitEthernet0/2 True True 1000 1500 50:00:00:0A:00:02
IOS-SW2 GigabitEthernet0/3 True True to_PC_1 1000 1500 50:00:00:0A:00:03
IOS-SW2 GigabitEthernet1/0 True True 1000 1500 50:00:00:0A:00:04
IOS-SW2 GigabitEthernet1/1 True True 1000 1500 50:00:00:0A:00:05
IOS-SW2 GigabitEthernet1/2 True True 1000 1500 50:00:00:0A:00:06
IOS-SW2 GigabitEthernet1/3 True True 1000 1500 50:00:00:0A:00:07
IOS-SW2 Vlan10 True True Management 10.10.10.10/24 1000 1500 50:00:00:0A:80:0A
IOSXR-R1 GigabitEthernet0/0/0/0 True True to_IOS-R3 Gi0/2 External 1000 1514 50:00:00:09:00:01
IOSXR-R1 GigabitEthernet0/0/0/1 True True to_IOS-SW2 Gi0/1 1000 1514 50:00:00:09:00:02
IOSXR-R1 GigabitEthernet0/0/0/1.1 True True to_IOS-SW2 Gi0/1 Vlan 20 1000 1518 50:00:00:09:00:02
IOSXR-R1 GigabitEthernet0/0/0/1.10 True True to_IOS-SW2 Gi0/1 Vlan 10 1000 1518 50:00:00:09:00:02
IOSXR-R1 GigabitEthernet0/0/0/2 True True to_VRP-R2 Eth1/0/2 External 1000 1514 50:00:00:09:00:03
IOSXR-R1 Loopback0 True True Management IP 0 1500
IOSXR-R1 Null0 True True 0 1500
IOS-R3 GigabitEthernet0/0 True True to_IOSXR-R1 Gi0/0/0/0 External 1000 1500 50:00:00:08:00:00
IOS-R3 GigabitEthernet0/1 True True to_VRP-R2 Eth1/0/1 External 1000 1500 50:00:00:08:00:01
IOS-R3 Loopback0 True True 8000 1514
IOS-R3 Loopback1 True True 8000 1514
IOS-R3 Loopback100 True True ***CISCO LOOPBACK 100**** 8000 1514
VRP-R2 Ethernet1/0/0.30 True True to_VRP-SW1 GE 1/0/0 - VLAN 30 -1 1500 38:49:80:01:01:00
VRP-R2 Ethernet1/0/1 True True to_IOS-R3 Gi0/1 - External -1 1500 38:49:80:01:01:01
VRP-R2 Ethernet1/0/2 True True to_IOSXR-R1 Gi0/0/0/2 - External -1 1500 38:49:80:01:01:02
VRP-R2 LoopBack0 True True to_VRP-SW2 GE 1/0/1 - VLAN 1 -1 1500
VRP-R2 LoopBack100 True True test -1 1500
VRP-R2 LoopBack200 True True ***HUAWEI LOOPBACK 200**** -1 1500
VRP-R2 NULL0 True True -1 1500
VRP-SW1 GE1/0/0 True True to_VRP-R2 Eth 1/0/0 - LAN -1 70:7B:E8:CD:0D:7B
VRP-SW1 GE1/0/3 True True to_PC1 - LAN -1 70:7B:E8:CD:0D:7B
VRP-SW1 NULL0 True True -1 1500
VRP-SW1 Vlanif30 True True -1 1500 70:7B:E8:CD:0D:7B
hostname mac interface vlan static
---------- ----------------- ----------- ------ --------
IOS-SW2 0A:00:27:00:00:19 Gi0/0 1 False
IOS-SW2 50:00:00:09:00:02 Gi0/1 1 False
IOS-SW2 00:50:00:00:0B:00 Gi0/3 10 False
IOS-SW2 50:00:00:09:00:02 Gi0/1 10 False
VRP-SW1 38:49:80:01:01:00 GE1/0/0 30 False
hostname interface is_up ipv4/mask tx_errors rx_errors tx_discards rx_discards
---------- ------------------------- ------- ----------------- ----------- ----------- ------------- -------------
IOS-SW2 Vlan10 True 10.10.10.10/24 0 0 0 0
IOSXR-R1 GigabitEthernet0/0/0/0 True 172.17.2.1/30 0 0 0 0
IOSXR-R1 GigabitEthernet0/0/0/1.1 True 192.168.56.101/24 0 0 0 480
IOSXR-R1 GigabitEthernet0/0/0/1.10 True 10.10.10.1/24 0 0 0 0
IOSXR-R1 GigabitEthernet0/0/0/2 True 172.17.1.1/30 0 0 0 0
IOS-R3 GigabitEthernet0/0 True 172.17.2.2/30 0 0 0 0
IOS-R3 GigabitEthernet0/1 True 172.17.3.1/30 0 0 0 0
IOS-R3 Loopback0 True 192.168.21.103/32 0 0 0 0
IOS-R3 Loopback1 True 10.20.20.1/24 0 0 0 0
IOS-R3 Loopback100 True 4.4.4.100/32 0 0 0 0
VRP-R2 Ethernet1/0/0.30 True 10.30.30.1/24 0 0 0 0
VRP-R2 Ethernet1/0/1 True 172.17.3.2/30 0 0 0 0
VRP-R2 Ethernet1/0/2 True 172.17.1.2/30 0 0 0 0
VRP-R2 LoopBack0 True 192.168.21.202/32 0 0 0 0
VRP-R2 LoopBack200 True 4.4.4.200/32 0 0 0 0
VRP-SW1 Vlanif30 True 10.30.30.31/24 0 0 0 0
NAPALM Python: BGP Neighbors
Here we have our get bgp neighbors method that was developed to be able to have a function just like in Cisco that allows me to get the bgp neighbors, the remote autonomous system, the prefixes sent and the prefixes received.
def get_bgp_neighbors(self): """ { "global": { "router_id": "192.168.21.102", "peers": { "172.17.1.1": { "local_as": 200, "remote_as": 100, "remote_id": "192.168.21.101", "is_up": true, "is_enabled": true, "description": "", "uptime": 142, "address_family": { "ipv4 unicast": { "received_prefixes": 5, "accepted_prefixes": 5, "sent_prefixes": 2 } } } } } } """ afi_supported = { "Ipv6 Unicast" : "ipv6 unicast", "Ipv4 Unicast" : "ipv4 unicast", "Vpnv4 All" : "vpnv4 unicast", "Vpnv6 All" : "vpnv6 unicast" } bgp_neighbors = {} command_bgp = "display bgp all summary" output = self.device.send_command(command_bgp) if output == "": return bgp_neighbors #Regular Expressions re_separator = r"\n\s*(?=Address Family:\s*\w+)" re_vpn_instance_separator = r"\n\s*(?=VPN-Instance\s+[-_a-zA-Z0-9]+)" re_address_family = r"Address Family:(?P<address_family>\w+\s\w+)" re_global_router_id = r"BGP local router ID :\s+(?P<glob_router_id>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" re_global_local_as = r"Local AS number :\s+(?P<local_as>{})".format(ASN_REGEX) re_vrf_router_id = r"VPN-Instance\s+(?P<vrf>[-_a-zA-Z0-9]+), [rR]outer ID\s+" \ r"(?P<vrf_router_id>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" re_peers = r"(?P<peer_ip>({})|({}))\s+" \ r"(?P<as>{})\s+\d+\s+\d+\s+\d+\s+(?P<updown_time>[a-zA-Z0-9:]+)\s+" \ r"(?P<state>[a-zA-Z0-9\(\)]+)\s+(?P<received_prefixes>\d+)\s+(?P<adv_prefixes>\d+)".format( IPV4_ADDR_REGEX, IPV6_ADDR_REGEX, ASN_REGEX) re_remote_rid = r"Remote router ID\s+(?P<remote_rid>{})".format(IPV4_ADDR_REGEX) re_peer_description = r"Peer's description:\s+\"(?P<peer_description>.*)\"" #Separation of AFIs afi_list = re.split(re_separator, output, flags=re.M) #return afi_list bgp_global_router_id = "" bgp_global_local_as = "" for afi in afi_list: match_afi = re.search(re_global_router_id,afi, flags=re.M) match_local_as = re.search(re_global_local_as,afi, flags=re.M) if match_afi is not None: bgp_global_router_id = match_afi.group('glob_router_id') bgp_global_local_as = match_local_as.group('local_as') match_afi = re.search(re_address_family, afi, flags=re.M) if match_afi is not None and any((s in match_afi.group('address_family') for s in ['Ipv4 Unicast','Ipv6 Unicast'])): bgp_neighbors.update({"global": {"router_id": bgp_global_router_id, "peers" : {}}}) for peer in afi.splitlines(): match_peer = re.search(re_peers, peer, flags=re.M) if match_peer: peer_bgp_command = "" if "Ipv6" in match_afi.group('address_family'): peer_bgp_command = "display bgp ipv6 peer {} verbose".format(match_peer.group('peer_ip')) else: peer_bgp_command = "display bgp peer {} verbose".format(match_peer.group('peer_ip')) peer_detail = self.device.send_command(peer_bgp_command) match_remote_rid = re.search(re_remote_rid, peer_detail, flags=re.M) match_peer_description = re.search(re_peer_description, peer_detail, flags=re.M) bgp_neighbors["global"]["peers"].update( { match_peer.group('peer_ip'): { "local_as": int(bgp_global_local_as), "remote_as": int(match_peer.group('as')), "remote_id": "" if match_remote_rid is None else match_remote_rid.group('remote_rid'), "is_up": True if "Established" in match_peer.group('state') else False, "is_enabled": False if "Admin" in match_peer.group('state') else True, "description": "" if match_peer_description is None else match_peer_description.group('peer_description'), "uptime": int(self.bgp_time_conversion(match_peer.group('updown_time'))), "address_family": { afi_supported[match_afi.group('address_family')]: { "received_prefixes": int(match_peer.group('received_prefixes')), "accepted_prefixes": "Unknown", "sent_prefixes": int(match_peer.group('adv_prefixes')) } } } }) elif match_afi is not None and any((s in match_afi.group('address_family') for s in ['Vpnv4 All','Vpnv6 All'])): if bgp_neighbors['global'] is False: bgp_neighbors.update({"global": {"router_id": bgp_global_router_id, "peers" : {}}}) #Separation of VPNs vpn_instance_list = re.split(re_vpn_instance_separator, afi, flags=re.M) for vpn_peers in vpn_instance_list: if "VPN-Instance " not in vpn_peers: for peer in vpn_peers.splitlines(): match_peer = re.search(re_peers, peer, flags=re.M) if match_peer: if bgp_neighbors["global"]["peers"][match_peer.group('peer_ip')]: bgp_neighbors["global"]["peers"][match_peer.group('peer_ip')]["address_family"].update( { afi_supported[match_afi.group('address_family')]: { "received_prefixes": int(match_peer.group('received_prefixes')), "accepted_prefixes": "Unknown", "sent_prefixes": int(match_peer.group('adv_prefixes')) } } ) else: peer_bgp_command = "" if "Ipv6" in match_afi.group('address_family'): peer_bgp_command = "display bgp ipv6 peer {} verbose".format(match_peer.group('peer_ip')) else: peer_bgp_command = "display bgp peer {} verbose".format(match_peer.group('peer_ip')) peer_detail = self.device.send_command(peer_bgp_command) match_remote_rid = re.search(re_remote_rid, peer_detail, flags=re.M) match_peer_description = re.search(re_peer_description, peer_detail, flags=re.M) bgp_neighbors["global"]["peers"].update( { match_peer.group('peer_ip'): { "local_as": int(bgp_global_local_as), "remote_as": int(match_peer.group('as')), "remote_id": "" if match_remote_rid is None else match_remote_rid.group('remote_rid'), "is_up": True if "Established" in match_peer.group('state') else False, "is_enabled": False if "Admin" in match_peer.group('state') else True, "description": "" if match_peer_description is None else match_peer_description.group('peer_description'), "uptime": int(self.bgp_time_conversion(match_peer.group('updown_time'))), "address_family": { afi_supported[match_afi.group('address_family')]: { "received_prefixes": int(match_peer.group('received_prefixes')), "accepted_prefixes": "Unknown", "sent_prefixes": int(match_peer.group('adv_prefixes')) } } } }) else: match_vrf_router_id = re.search(re_vrf_router_id, vpn_peers, flags=re.M) if match_vrf_router_id is None: msg = "No Match Found" raise ValueError(msg) peer_vpn_instance = match_vrf_router_id.group('vrf') peer_router_id = match_vrf_router_id.group('vrf_router_id') bgp_neighbors.update({peer_vpn_instance: { "router_id": peer_router_id, "peers" : {}}}) for peer in vpn_peers.splitlines(): match_peer = re.search(re_peers, peer, flags=re.M) if match_peer: peer_bgp_command = "" afi_vrf = "" if "Ipv6" in match_afi.group('address_family'): peer_bgp_command = "display bgp ipv6 peer {} verbose".format(match_peer.group('peer_ip')) afi_vrf = "ipv6 unicast" else: peer_bgp_command = "display bgp peer {} verbose".format(match_peer.group('peer_ip')) afi_vrf = "ipv4 unicast" peer_detail = self.device.send_command(peer_bgp_command) match_remote_rid = re.search(re_remote_rid, peer_detail, flags=re.M) match_peer_description = re.search(re_peer_description, peer_detail, flags=re.M) bgp_neighbors[peer_vpn_instance]["peers"].update( { match_peer.group('peer_ip'): { "local_as": int(bgp_global_local_as), "remote_as": int(match_peer.group('as')), "remote_id": "" if match_remote_rid is None else match_remote_rid.group('remote_rid'), "is_up": True if "Established" in match_peer.group('state') else False, "is_enabled": False if "Admin" in match_peer.group('state') else True, "description": "" if match_peer_description is None else match_peer_description.group('peer_description'), "uptime": int(self.bgp_time_conversion(match_peer.group('updown_time'))), "address_family": { afi_vrf: { "received_prefixes": int(match_peer.group('received_prefixes')), "accepted_prefixes": "Unknown", "sent_prefixes": int(match_peer.group('adv_prefixes')) } } } }) return bgp_neighbors
Here is the code that we have been working on including the development we had done previously to print the BGP neighbors table.
import napalm from tabulate import tabulate 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 = [["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"]] devices_table_int = [["hostname","interface","is_up", "is_enabled", "description", "speed", "mtu", "mac_address"]] devices_mac_table = [["hostname","mac", "interface", "vlan", "static"]] devices_int_ip_table = [["hostname","interface","is_up","ipv4/mask", "tx_errors","rx_errors","tx_discards","rx_discards"]] devices_table_bgp = [["hostname", "neighbor", "remote-as", "status", "sent prefixes", "received prefixes"]] 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"] ]) print("Getting device interfaces") device_interfaces = device.get_interfaces() for interface in device_interfaces: if device_interfaces[interface]['is_up']: devices_table_int.append([device_facts["hostname"], interface, device_interfaces[interface]['is_up'], device_interfaces[interface]['is_enabled'], device_interfaces[interface]['description'], device_interfaces[interface]['speed'], device_interfaces[interface]['mtu'], device_interfaces[interface]['mac_address'] ]) if "SW" in device_facts["hostname"]: print("Getting Mac Address Table from Switch") device_mac_info = device.get_mac_address_table() for mac_entry in device_mac_info: devices_mac_table.append([device_facts["hostname"], mac_entry["mac"], mac_entry["interface"], mac_entry["vlan"], mac_entry["static"], ]) print("Getting physical interfaces ip and counters") device_interfaces_ip = device.get_interfaces_ip() device_interfaces_counters = device.get_interfaces_counters() for interface in device_interfaces_ip: if device_interfaces[interface]['is_up'] and interface in device_interfaces_counters: ip_address = list(device_interfaces_ip[interface]['ipv4'].keys())[0] devices_int_ip_table.append([device_facts["hostname"], interface, device_interfaces[interface]['is_up'], "{}/{}".format(ip_address, device_interfaces_ip[interface]['ipv4'][ip_address]['prefix_length']), device_interfaces_counters[interface]['tx_errors'], device_interfaces_counters[interface]['rx_errors'], device_interfaces_counters[interface]['tx_discards'], device_interfaces_counters[interface]['rx_discards'] ]) if not "SW" in device_facts["hostname"]: print("Getting device BGP Neighbors") device_bgp_peers = device.get_bgp_neighbors() address_fam = "ipv4 unicast" if "IOSXR" in device_facts["hostname"]: address_fam = "ipv4" for bgp_neighbor in device_bgp_peers['global']['peers']: devices_table_bgp.append([device_facts["hostname"], bgp_neighbor, device_bgp_peers['global']['peers'][bgp_neighbor]['remote_as'], device_bgp_peers['global']['peers'][bgp_neighbor]['is_up'], device_bgp_peers['global']['peers'][bgp_neighbor]['address_family'][address_fam]['sent_prefixes'], device_bgp_peers['global']['peers'][bgp_neighbor]['address_family'][address_fam]['received_prefixes'] ]) device.close() print("Done.") print(tabulate(devices_table, headers="firstrow")) print() print(tabulate(devices_table_int, headers="firstrow")) print() print(tabulate(devices_mac_table, headers="firstrow")) print() print(tabulate(devices_int_ip_table, headers="firstrow")) print() print(tabulate(devices_table_bgp, headers="firstrow")) if __name__ == '__main__': main()
Let’s see the output of the Script:
Connecting to ios-sw2 ...
Getting device facts
Getting device interfaces
Getting Mac Address Table from Switch
Getting physical interfaces ip and counters
Done.
Connecting to iosxr-r1 ...
Getting device facts
Getting device interfaces
Getting physical interfaces ip and counters
Getting device BGP Neighbors
Done.
Connecting to ios-r3 ...
Getting device facts
Getting device interfaces
Getting physical interfaces ip and counters
Getting device BGP Neighbors
Done.
Connecting to vrp-r2 ...
Getting device facts
Getting device interfaces
Getting physical interfaces ip and counters
Getting device BGP Neighbors
Done.
Connecting to vrp-sw1 ...
Getting device facts
Getting device interfaces
Getting Mac Address Table from Switch
Getting physical interfaces ip and counters
Done.
hostname vendor model uptime serial_number
---------- -------- ------- -------- ---------------------
IOS-SW2 Cisco IOSv 1620 9QEN7MU6YX0
IOSXR-R1 Cisco 1678
IOS-R3 Cisco IOSv 1680 95ZAVRE4Q305F6ET85347
VRP-R2 Huawei NE40E 1620 []
VRP-SW1 Huawei CE6800 1620 []
hostname interface is_up is_enabled description speed mtu mac_address
---------- ------------------------- ------- ------------ -------------------------------- ------- ----- -----------------
IOS-SW2 GigabitEthernet0/0 True True 1000 1500 50:00:00:0A:00:00
IOS-SW2 GigabitEthernet0/1 True True Connected to IOSXR-R1 L2-Trunk 1000 1500 50:00:00:0A:00:01
IOS-SW2 GigabitEthernet0/2 True True 1000 1500 50:00:00:0A:00:02
IOS-SW2 GigabitEthernet0/3 True True to_PC_1 1000 1500 50:00:00:0A:00:03
IOS-SW2 GigabitEthernet1/0 True True 1000 1500 50:00:00:0A:00:04
IOS-SW2 GigabitEthernet1/1 True True 1000 1500 50:00:00:0A:00:05
IOS-SW2 GigabitEthernet1/2 True True 1000 1500 50:00:00:0A:00:06
IOS-SW2 GigabitEthernet1/3 True True 1000 1500 50:00:00:0A:00:07
IOS-SW2 Vlan10 True True Management 10.10.10.10/24 1000 1500 50:00:00:0A:80:0A
IOSXR-R1 GigabitEthernet0/0/0/0 True True to_IOS-R3 Gi0/2 External 1000 1514 50:00:00:09:00:01
IOSXR-R1 GigabitEthernet0/0/0/1 True True to_IOS-SW2 Gi0/1 1000 1514 50:00:00:09:00:02
IOSXR-R1 GigabitEthernet0/0/0/1.1 True True to_IOS-SW2 Gi0/1 Vlan 20 1000 1518 50:00:00:09:00:02
IOSXR-R1 GigabitEthernet0/0/0/1.10 True True to_IOS-SW2 Gi0/1 Vlan 10 1000 1518 50:00:00:09:00:02
IOSXR-R1 GigabitEthernet0/0/0/2 True True to_VRP-R2 Eth1/0/2 External 1000 1514 50:00:00:09:00:03
IOSXR-R1 Loopback0 True True Management IP 0 1500
IOSXR-R1 Null0 True True 0 1500
IOS-R3 GigabitEthernet0/0 True True to_IOSXR-R1 Gi0/0/0/0 External 1000 1500 50:00:00:08:00:00
IOS-R3 GigabitEthernet0/1 True True to_VRP-R2 Eth1/0/1 External 1000 1500 50:00:00:08:00:01
IOS-R3 Loopback0 True True 8000 1514
IOS-R3 Loopback1 True True 8000 1514
IOS-R3 Loopback100 True True ***CISCO LOOPBACK 100**** 8000 1514
VRP-R2 Ethernet1/0/0.30 True True to_VRP-SW1 GE 1/0/0 - VLAN 30 -1 1500 38:49:80:01:01:00
VRP-R2 Ethernet1/0/1 True True to_IOS-R3 Gi0/1 - External -1 1500 38:49:80:01:01:01
VRP-R2 Ethernet1/0/2 True True to_IOSXR-R1 Gi0/0/0/2 - External -1 1500 38:49:80:01:01:02
VRP-R2 LoopBack0 True True to_VRP-SW2 GE 1/0/1 - VLAN 1 -1 1500
VRP-R2 LoopBack100 True True test -1 1500
VRP-R2 LoopBack200 True True ***HUAWEI LOOPBACK 200**** -1 1500
VRP-R2 NULL0 True True -1 1500
VRP-SW1 GE1/0/0 True True to_VRP-R2 Eth 1/0/0 - LAN -1 70:7B:E8:CD:0D:7B
VRP-SW1 GE1/0/3 True True to_PC1 - LAN -1 70:7B:E8:CD:0D:7B
VRP-SW1 NULL0 True True -1 1500
VRP-SW1 Vlanif30 True True -1 1500 70:7B:E8:CD:0D:7B
hostname mac interface vlan static
---------- ----------------- ----------- ------ --------
IOS-SW2 0A:00:27:00:00:19 Gi0/0 1 False
IOS-SW2 50:00:00:09:00:02 Gi0/1 1 False
IOS-SW2 00:50:00:00:0B:00 Gi0/3 10 False
IOS-SW2 50:00:00:09:00:02 Gi0/1 10 False
VRP-SW1 38:49:80:01:01:00 GE1/0/0 30 False
hostname interface is_up ipv4/mask tx_errors rx_errors tx_discards rx_discards
---------- ------------------------- ------- ----------------- ----------- ----------- ------------- -------------
IOS-SW2 Vlan10 True 10.10.10.10/24 0 0 0 0
IOSXR-R1 GigabitEthernet0/0/0/0 True 172.17.2.1/30 0 0 0 0
IOSXR-R1 GigabitEthernet0/0/0/1.1 True 192.168.56.101/24 0 0 0 552
IOSXR-R1 GigabitEthernet0/0/0/1.10 True 10.10.10.1/24 0 0 0 0
IOSXR-R1 GigabitEthernet0/0/0/2 True 172.17.1.1/30 0 0 0 0
IOS-R3 GigabitEthernet0/0 True 172.17.2.2/30 0 0 0 0
IOS-R3 GigabitEthernet0/1 True 172.17.3.1/30 0 0 0 0
IOS-R3 Loopback0 True 192.168.21.103/32 0 0 0 0
IOS-R3 Loopback1 True 10.20.20.1/24 0 0 0 0
IOS-R3 Loopback100 True 4.4.4.100/32 0 0 0 0
VRP-R2 Ethernet1/0/0.30 True 10.30.30.1/24 0 0 0 0
VRP-R2 Ethernet1/0/1 True 172.17.3.2/30 0 0 0 0
VRP-R2 Ethernet1/0/2 True 172.17.1.2/30 0 0 0 0
VRP-R2 LoopBack0 True 192.168.21.202/32 0 0 0 0
VRP-R2 LoopBack200 True 4.4.4.200/32 0 0 0 0
VRP-SW1 Vlanif30 True 10.30.30.31/24 0 0 0 0
hostname neighbor remote-as status sent prefixes received prefixes
---------- ---------- ----------- -------- --------------- -------------------
IOSXR-R1 172.17.1.2 200 True 7 4
IOSXR-R1 172.17.2.2 300 True 7 4
IOS-R3 172.17.2.1 100 True 7 5
IOS-R3 172.17.3.2 200 True 7 5
VRP-R2 172.17.1.1 100 True 7 5
VRP-R2 172.17.3.1 300 True 7 5
Conclusion
Again I am surprised when I see that in 135 lines of code, I have a script that connects my multi-vendor network and in 1 minute it gives me a summary of:
- Data of the interfaces.
- Data from the Mac Address table of the Switches.
- Layer 3 interface data with packet discard and error statistics.
- And data from the BGP protocol and its neighbors.
Surprising no?
Excellent job Michael!
Huawei VRP is a pain in the butt when it comes to automating things. I like how you implemented BGP neighbor function with so much effort. Maybe you can consider contributing to the current huawei-ce or huawei-vrp repo on github.
Also, your code is missing some stuff(IPV4_ADDR_REGEX, IPV6_ADDR_REGEX, ASN_REGEX and bgp_time_conversion function) in order to run straight out of box.
Do you have a github account that I can follow?
Thanks Kevin. I have considered to contribute on current huawei-ce, i just haven’t dedicate the time to push the code i have developed. But you motivated to do it. I just update my github account, you can find me on: codingnetworksb.