diff --git a/setup.py b/setup.py index a4e75a2..58605b3 100644 --- a/setup.py +++ b/setup.py @@ -32,6 +32,9 @@ "Topic :: Utilities", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ] diff --git a/test/test_misc_netmiko_templates_2.py b/test/test_misc_netmiko_templates_2.py new file mode 100644 index 0000000..c41cee6 --- /dev/null +++ b/test/test_misc_netmiko_templates_2.py @@ -0,0 +1,700 @@ +import sys +import pprint + +#sys.path.insert(0, "..") + +from netmiko import ConnectHandler +from ttp_templates import get_template +from ttp_templates import ttp_vars + + +def mock_output_cisco_ios(command_string, *args, **kwargs): + outputs = { + "show ip arp": """ +Protocol Address Age (min) Hardware Addr Type Interface +Internet 172.29.50.1 8 84b8.0276.ac0e ARPA +Internet 172.29.50.2 221 0019.0725.344a ARPA Vlan20 +Internet 172.29.50.3 - 0024.f7dd.7741 ARPA Vlan21 + """, + "show running-configuration | section interface": """ +router-1# show running-config | section interface +interface Vlan1234 + description Some description + ip address 10.1.251.170 255.255.255.0 + ip address 10.1.251.171 255.255.255.0 secondary + standby 1234 ip 172.20.128.3 + ipv6 address AAAA::1/64 + ipv6 address BBBB::1/64 +! +interface GigabitEthernet1 + description Workstations subnet 1 + vrf forwarding OFFICE + ip address 10.2.251.170 255.255.255.0 + vrrp 123 ip 10.2.251.1 +! + """, + "show running-configuration | section bgp": """ +router bgp 65001 + ! + bgp router-id 10.5.1.1 + bgp log-neighbor-changes + neighbor 2001:db8::1 remote-as 65003 + neighbor 2001:db8::1 description Peer1-Global + neighbor 10.11.0.81 remote-as 65002 + neighbor 10.11.0.81 description Peer2-Global + neighbor 10.11.0.81 shutdown + neighbor RR-CLIENTS peer-group + neighbor RR-CLIENTS remote-as 65001 + neighbor RR-CLIENTS description [ibgp - rr clients] + neighbor RR-CLIENTS update-source GigabitEthernet1 + neighbor 10.0.0.3 peer-group RR-CLIENTS + neighbor 10.0.0.5 peer-group RR-CLIENTS + ! + address-family ipv4 + network 10.255.10.0 mask 255.255.248.0 + network 10.255.10.0 mask 255.255.255.0 + redistribute connected route-map PORTABLE-v4 + neighbor 10.11.0.81 activate + neighbor 10.11.0.81 description Peer2-IPv4 + neighbor RR-CLIENTS route-reflector-client + neighbor RR-CLIENTS route-map PASS-IN in + neighbor RR-CLIENTS route-map PASS-OUT out + neighbor RR-CLIENTS maximum-prefix 1000 80 restart 15 + neighbor 10.0.0.3 activate + neighbor 10.0.0.5 activate + exit-address-family + ! + address-family ipv6 + redistribute connected + network 2001:db8::/48 + neighbor 2001:db8::1 activate + neighbor 2001:db8::1 description Peer1-IPv6 + exit-address-family + ! + address-family ipv4 multicast + exit-address-family + ! + address-family vpnv4 unicast + neighbor 3.3.3.3 activate + exit-address-family + ! + address-family ipv4 vrf VoIP + network 10.255.10.0 mask 255.255.248.0 + bgp router-id 10.2.1.193 + redistribute connected route-map tospokes + neighbor 10.2.1.65 remote-as 65001 + neighbor 10.2.1.65 description voip peer 1 + neighbor 10.2.1.65 activate + neighbor 10.2.1.78 remote-as 65001 + neighbor 10.2.1.78 description voip peer 2 + neighbor 10.2.1.78 shutdown + neighbor 10.2.1.78 activate + neighbor 10.2.1.78 next-hop-self + neighbor 10.2.1.78 prefix-list VoIP-prefixes out + exit-address-family +! + address-family ipv4 vrf CUST-2 + bgp router-id 1.1.1.1 + redistribute connected + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 description peer 12 + neighbor 2.2.2.2 activate + exit-address-family +! + """ + } + return outputs[command_string] + + +def mock_output_cisco_ios_xr(command_string, *args, **kwargs): + outputs = { + "show arp vrf all": """ +------------------------------------------------------------------------------- +0/0/CPU0 +------------------------------------------------------------------------------- +Address Age Hardware Addr State Type Interface +10.1.2.3 - 1234.7698.d416 Interface ARPA GigabitEthernet0/0/0/22 +10.1.2.4 00:35:34 1234.c686.c9c0 Dynamic ARPA GigabitEthernet0/0/0/22 + +------------------------------------------------------------------------------- +0/RP0/CPU0 +------------------------------------------------------------------------------- +Address Age Hardware Addr State Type Interface +10.2.3.33 00:10:50 1234.dd60.1f88 Dynamic ARPA MgmtEth0/RP0/CPU0/0 +10.5.3.34 - 7cad.4fe6.a49a Interface ARPA MgmtEth0/RP0/CPU0/0 + """, + "show running-config interface": """ +RP/0/RP0/CPU0:r1#show running-config interface +interface Bundle-Ether1 + description Description of interface + ipv4 address 10.1.2.54 255.255.255.252 + ipv6 address fd00:1:2::31/126 +! +interface Loopback123 + description VRF 123 + vrf VRF-1123 + ipv4 address 10.1.0.10 255.255.255.255 +! + """, + "show running-config router vrrp": """ +RP/0/RP0/CPU0:r1#show running-config router vrrp +router vrrp + interface Bundle-Ether1 + address-family ipv4 + vrrp 1 + address 1.1.1.1 + ! + ! + address-family ipv6 + vrrp 1 + address global fd::1 +! + """, + "show running-config router hsrp": """ +RP/0/RP0/CPU0:r1#show running-config router hsrp +router hsrp + interface Bundle-Ether1 + address-family ipv4 + hsrp 1 + address 3.3.3.3 + ! + ! + address-family ipv6 + hsrp 1 + address global fd::3 +! + """ + } + return outputs[command_string] + + +def mock_output_huawei_vrp(command_string, *args, **kwargs): + outputs = { + "display current-configuration interface": """ +display current-configuration interface +interface Eth-Trunk1.100 + description Link description here + ip address 10.1.130.2 255.255.255.252 + ip address 10.1.130.3 255.255.255.252 sub + ip binding vpn-instance VRF2 +# +interface vlanif10 + description Link description here 2 + ip binding vpn-instance VRF2 + ip address 10.2.130.2 255.255.255.252 + vrrp vrid 1 virtual-ip 10.1.1.2 + ipv6 address fc00:1::2/64 + vrrp6 vrid 1 virtual-ip 2001:DB8::100 +# + """ + } + return outputs[command_string] + + +lab_cisco_ios = { + "device_type": "cisco_ios", + "host": "1.2.3.4", + "username": "cisco", + "password": "cisco", + "auto_connect": False, # stop Netmiko trying connect to device +} +connection_cisco_ios = ConnectHandler(**lab_cisco_ios) +# override send command method to return mock data +setattr(connection_cisco_ios, "send_command", mock_output_cisco_ios) + +lab_cisco_ios_xr = { + "device_type": "cisco_xr", + "host": "1.2.3.4", + "username": "cisco", + "password": "cisco", + "auto_connect": False, # stop Netmiko trying connect to device +} +connection_cisco_ios_xr = ConnectHandler(**lab_cisco_ios_xr) +setattr(connection_cisco_ios_xr, "send_command", mock_output_cisco_ios_xr) + + +lab_huawei_vrp = { + "device_type": "huawei", + "host": "1.2.3.4", + "username": "huawei", + "password": "huawei", + "auto_connect": False, # stop Netmiko trying connect to device +} +connection_huawei_vrp = ConnectHandler(**lab_huawei_vrp) +setattr(connection_huawei_vrp, "send_command", mock_output_huawei_vrp) + + +def test_cisco_ios_arp_original_intf_names(): + res = connection_cisco_ios.run_ttp( + "ttp://misc/netmiko/cisco.ios.arp.txt", res_kwargs={"structure": "flat_list"} + ) + # pprint.pprint(res) + assert res == [ + { + "age": 8, + "interface": "Uncknown", + "ip": "172.29.50.1", + "mac": "84:b8:02:76:ac:0e", + "protocol": "Internet", + "type": "ARPA", + }, + { + "age": 221, + "interface": "Vlan20", + "ip": "172.29.50.2", + "mac": "00:19:07:25:34:4a", + "protocol": "Internet", + "type": "ARPA", + }, + { + "age": -1, + "interface": "Vlan21", + "ip": "172.29.50.3", + "mac": "00:24:f7:dd:77:41", + "protocol": "Internet", + "type": "ARPA", + }, + ] + + +# test_cisco_ios_arp_original_intf_names() + + +def test_cisco_ios_cfg_ip_original_intf_names(): + res = connection_cisco_ios.run_ttp( + "ttp://misc/netmiko/cisco.ios.cfg.ip.txt", res_kwargs={"structure": "flat_list"} + ) + # pprint.pprint(res) + assert res == [ + { + "description": "Some description", + "hostname": "router-1", + "interface": "Vlan1234", + "ipv4": "10.1.251.170", + "mask": "255.255.255.0", + 'vrf': 'default', + }, + { + "description": "Some description", + "hostname": "router-1", + "interface": "Vlan1234", + "ipv4": "10.1.251.171", + "mask": "255.255.255.0", + "secondary": True, + 'vrf': 'default', + }, + { + "description": "Some description", + "hostname": "router-1", + "interface": "Vlan1234", + "ipv4": "172.20.128.3", + "vip": True, + "vip_type": "HSRP", + 'vrf': 'default', + }, + { + "mask": "64", + "description": "Some description", + "hostname": "router-1", + "interface": "Vlan1234", + "ipv6": "AAAA::1", + 'vrf': 'default', + }, + { + "mask": "64", + "description": "Some description", + "hostname": "router-1", + "interface": "Vlan1234", + "ipv6": "BBBB::1", + 'vrf': 'default', + }, + { + "description": "Workstations subnet 1", + "hostname": "router-1", + "interface": "GigabitEthernet1", + "ipv4": "10.2.251.170", + "mask": "255.255.255.0", + "vrf": "OFFICE", + }, + { + "description": "Workstations subnet 1", + "hostname": "router-1", + "interface": "GigabitEthernet1", + "ipv4": "10.2.251.1", + "vip": True, + "vip_type": "VRRP", + "vrf": "OFFICE", + }, + ] + + +# test_cisco_ios_cfg_ip_original_intf_names() + + +def test_cisco_ios_cfg_ip_short_intf_names(): + res = connection_cisco_ios.run_ttp( + "ttp://misc/netmiko/cisco.ios.cfg.ip.txt", + res_kwargs={"structure": "flat_list"}, + vars=ttp_vars.all_vars, + ) + # pprint.pprint(res) + assert res == [ + { + "description": "Some description", + "hostname": "router-1", + "interface": "VLAN1234", + "ipv4": "10.1.251.170", + "mask": "255.255.255.0", + 'vrf': 'default', + }, + { + "description": "Some description", + "hostname": "router-1", + "interface": "VLAN1234", + "ipv4": "10.1.251.171", + "mask": "255.255.255.0", + "secondary": True, + 'vrf': 'default', + }, + { + "description": "Some description", + "hostname": "router-1", + "interface": "VLAN1234", + "ipv4": "172.20.128.3", + "vip": True, + "vip_type": "HSRP", + 'vrf': 'default', + }, + { + "mask": "64", + "description": "Some description", + "hostname": "router-1", + "interface": "VLAN1234", + "ipv6": "AAAA::1", + 'vrf': 'default', + }, + { + "mask": "64", + "description": "Some description", + "hostname": "router-1", + "interface": "VLAN1234", + "ipv6": "BBBB::1", + 'vrf': 'default', + }, + { + "description": "Workstations subnet 1", + "hostname": "router-1", + "interface": "GE1", + "ipv4": "10.2.251.170", + "mask": "255.255.255.0", + "vrf": "OFFICE", + }, + { + "description": "Workstations subnet 1", + "hostname": "router-1", + "interface": "GE1", + "ipv4": "10.2.251.1", + "vip": True, + "vip_type": "VRRP", + "vrf": "OFFICE", + }, + ] + + +# test_cisco_ios_cfg_ip_short_intf_names() + + +def test_cisco_ios_xr_arp_short_intf_names(): + res = connection_cisco_ios_xr.run_ttp( + "ttp://misc/netmiko/cisco.iosxr.arp.txt", + res_kwargs={"structure": "flat_list"}, + vars={"short_interface_names": ttp_vars.short_interface_names}, + ) + # pprint.pprint(res) + assert res == [ + { + "age": -1, + "interface": "GE0/0/0/22", + "ip": "10.1.2.3", + "mac": "12:34:76:98:d4:16", + "state": "interface", + "type": "ARPA", + }, + { + "age": "00:35:34", + "interface": "GE0/0/0/22", + "ip": "10.1.2.4", + "mac": "12:34:c6:86:c9:c0", + "state": "dynamic", + "type": "ARPA", + }, + { + "age": "00:10:50", + "interface": "MGMT0/RP0/CPU0/0", + "ip": "10.2.3.33", + "mac": "12:34:dd:60:1f:88", + "state": "dynamic", + "type": "ARPA", + }, + { + "age": -1, + "interface": "MGMT0/RP0/CPU0/0", + "ip": "10.5.3.34", + "mac": "7c:ad:4f:e6:a4:9a", + "state": "interface", + "type": "ARPA", + }, + ] + + +# test_cisco_ios_xr_arp_short_intf_names() + + +def test_cisco_ios_xr_arp_original_intf_names(): + res = connection_cisco_ios_xr.run_ttp( + "ttp://misc/netmiko/cisco.iosxr.arp.txt", res_kwargs={"structure": "flat_list"} + ) + # pprint.pprint(res) + assert res == [ + { + "age": -1, + "interface": "GigabitEthernet0/0/0/22", + "ip": "10.1.2.3", + "mac": "12:34:76:98:d4:16", + "state": "interface", + "type": "ARPA", + }, + { + "age": "00:35:34", + "interface": "GigabitEthernet0/0/0/22", + "ip": "10.1.2.4", + "mac": "12:34:c6:86:c9:c0", + "state": "dynamic", + "type": "ARPA", + }, + { + "age": "00:10:50", + "interface": "MgmtEth0/RP0/CPU0/0", + "ip": "10.2.3.33", + "mac": "12:34:dd:60:1f:88", + "state": "dynamic", + "type": "ARPA", + }, + { + "age": -1, + "interface": "MgmtEth0/RP0/CPU0/0", + "ip": "10.5.3.34", + "mac": "7c:ad:4f:e6:a4:9a", + "state": "interface", + "type": "ARPA", + }, + ] + + +# test_cisco_ios_xr_arp_original_intf_names() + + +def test_huawei_vrp_cfg_ip_original_intf_names(): + res = connection_huawei_vrp.run_ttp( + "ttp://misc/netmiko/huawei.vrp.cfg.ip.txt", res_kwargs={"structure": "flat_list"} + ) + # pprint.pprint(res) + assert res == [{'description': 'Link description here', + 'hostname': 'router_23', + 'interface': 'Eth-Trunk1.100', + 'ipv4': '10.1.130.2', + 'mask': '255.255.255.252', + 'vrf': 'default'}, + {'description': 'Link description here', + 'hostname': 'router_23', + 'interface': 'Eth-Trunk1.100', + 'ipv4': '10.1.130.3', + 'mask': '255.255.255.252', + 'secondary': True, + 'vrf': 'default'}, + {'description': 'Link description here 2', + 'hostname': 'router_23', + 'interface': 'vlanif10', + 'ipv4': '10.2.130.2', + 'mask': '255.255.255.252', + 'vrf': 'VRF2'}, + {'description': 'Link description here 2', + 'hostname': 'router_23', + 'interface': 'vlanif10', + 'ipv4': '10.1.1.2', + 'vip': True, + 'vip_type': 'VRRP', + 'vrf': 'VRF2'}, + {'description': 'Link description here 2', + 'hostname': 'router_23', + 'interface': 'vlanif10', + 'ipv6': 'fc00:1::2', + 'mask': '64', + 'vrf': 'VRF2'}, + {'description': 'Link description here 2', + 'hostname': 'router_23', + 'interface': 'vlanif10', + 'ipv6': '2001:DB8::100', + 'vip': True, + 'vip_type': 'VRRP', + 'vrf': 'VRF2'}] + +# test_huawei_vrp_cfg_ip_original_intf_names() + + +def test_huawei_vrp_cfg_ip_short_intf_names(): + res = connection_huawei_vrp.run_ttp( + "ttp://misc/netmiko/huawei.vrp.cfg.ip.txt", + res_kwargs={"structure": "flat_list"}, + vars=ttp_vars.all_vars + ) + # pprint.pprint(res) + assert res == [{'description': 'Link description here', + 'hostname': 'router_23', + 'interface': 'LAG1.100', + 'ipv4': '10.1.130.2', + 'mask': '255.255.255.252', + 'vrf': 'default'}, + {'description': 'Link description here', + 'hostname': 'router_23', + 'interface': 'LAG1.100', + 'ipv4': '10.1.130.3', + 'mask': '255.255.255.252', + 'secondary': True, + 'vrf': 'default'}, + {'description': 'Link description here 2', + 'hostname': 'router_23', + 'interface': 'VLAN10', + 'ipv4': '10.2.130.2', + 'mask': '255.255.255.252', + 'vrf': 'VRF2'}, + {'description': 'Link description here 2', + 'hostname': 'router_23', + 'interface': 'VLAN10', + 'ipv4': '10.1.1.2', + 'vip': True, + 'vip_type': 'VRRP', + 'vrf': 'VRF2'}, + {'description': 'Link description here 2', + 'hostname': 'router_23', + 'interface': 'VLAN10', + 'ipv6': 'fc00:1::2', + 'mask': '64', + 'vrf': 'VRF2'}, + {'description': 'Link description here 2', + 'hostname': 'router_23', + 'interface': 'VLAN10', + 'ipv6': '2001:DB8::100', + 'vip': True, + 'vip_type': 'VRRP', + 'vrf': 'VRF2'}] + +# test_huawei_vrp_cfg_ip_short_intf_names() + + +def test_cisco_iosxr_cfg_ip_original_intf_names(): + res = connection_cisco_ios_xr.run_ttp( + "ttp://misc/netmiko/cisco.iosxr.cfg.ip.txt", + res_kwargs={"structure": "flat_list"} + ) + # pprint.pprint(res) + assert res == [{'description': 'Description of interface', + 'hostname': 'r1', + 'interface': 'Bundle-Ether1', + 'ipv4': '10.1.2.54', + 'mask4': '255.255.255.252', + 'vrf': 'default'}, + {'description': 'Description of interface', + 'hostname': 'r1', + 'interface': 'Bundle-Ether1', + 'ipv6': 'fd00:1:2::31', + 'mask6': '126', + 'vrf': 'default'}, + {'description': 'VRF 123', + 'hostname': 'r1', + 'interface': 'Loopback123', + 'ipv4': '10.1.0.10', + 'mask4': '255.255.255.255', + 'vrf': 'VRF-1123'}, + {'description': 'Description of interface', + 'hostname': 'r1', + 'interface': 'Bundle-Ether1', + 'ipv4': '1.1.1.1', + 'vip': True, + 'vip_type': 'VRRP', + 'vrf': 'default'}, + {'description': 'Description of interface', + 'hostname': 'r1', + 'interface': 'Bundle-Ether1', + 'ipv6': 'fd::1', + 'vip': True, + 'vip_type': 'VRRP', + 'vrf': 'default'}, + {'description': 'Description of interface', + 'hostname': 'r1', + 'interface': 'Bundle-Ether1', + 'ipv4': '3.3.3.3', + 'vip': True, + 'vip_type': 'HSRP', + 'vrf': 'default'}, + {'description': 'Description of interface', + 'hostname': 'r1', + 'interface': 'Bundle-Ether1', + 'ipv6': 'fd::3', + 'vip': True, + 'vip_type': 'HSRP', + 'vrf': 'default'}] + +# test_cisco_iosxr_cfg_ip_original_intf_names() + + +def test_cisco_ios_cfg_bgp(): + res = connection_cisco_ios.run_ttp( + "ttp://misc/netmiko/cisco.ios.cfg.bgp.txt", + # res_kwargs={"structure": "flat_list"} + ) + # pprint.pprint(res, width=150) + assert res == [[{'bgp': {'afis': {'ipv4_multicast': {}, + 'ipv4_unicast': {'config': {'networks': [{'mask': '255.255.248.0', 'network': '10.255.10.0'}, + {'mask': '255.255.255.0', 'network': '10.255.10.0'}], + 'redistribute_connected': True, + 'redistribute_connected_rpl': 'PORTABLE-v4'}, + 'neighbors': {'10.0.0.3': {'activate': True}, + '10.0.0.5': {'activate': True}, + '10.11.0.81': {'activate': True}, + 'RR-CLIENTS': {'max_prefix_action': 'restart', + 'max_prefix_limit': '1000', + 'max_prefix_restart_interval': '15', + 'max_prefix_threshold': '80', + 'rpl_out': 'PASS-OUT', + 'rr_client': True}}}, + 'ipv6_unicast': {'config': {'networks': [{'mask': '48', 'network': '2001:db8::'}], 'redistribute_connected': True}, + 'neighbors': {'2001:db8::1': {'activate': True}}}, + 'vpnv4_unicast': {'neighbors': {'3.3.3.3': {'activate': True}}}}, + 'asn': '65001', + 'config': {'bgp_rid': '10.5.1.1', 'log_neighbor_changes': True}, + 'neighbors': {'10.0.0.3': {'peer_group': 'RR-CLIENTS'}, + '10.0.0.5': {'peer_group': 'RR-CLIENTS'}, + '10.11.0.81': {'asn': '65002', 'description': 'Peer2-Global', 'disabled': True}, + '2001:db8::1': {'asn': '65003', 'description': 'Peer1-Global'}, + 'RR-CLIENTS': {'asn': '65001', + 'description': '[ibgp - rr clients]', + 'is_peer_group': True, + 'update_source': 'GigabitEthernet1'}}, + 'vrfs': {'CUST-2': {'afi': 'ipv4', + 'config': {'bgp_rid': '1.1.1.1', 'redistribute_connected': True}, + 'neighbors': {'2.2.2.2': {'activate': True, 'asn': '65002', 'description': 'peer 12'}}}, + 'VoIP': {'afi': 'ipv4', + 'config': {'bgp_rid': '10.2.1.193', + 'networks': [{'mask': '255.255.248.0', 'network': '10.255.10.0'}], + 'redistribute_connected': True, + 'redistribute_connected_rpl': 'tospokes'}, + 'neighbors': {'10.2.1.65': {'activate': True, 'asn': '65001', 'description': 'voip peer 1'}, + '10.2.1.78': {'activate': True, + 'asn': '65001', + 'description': 'voip peer 2', + 'disabled': True, + 'next_hop_self': True, + 'pfl_out': 'VoIP-prefixes'}}}}}}]] + +# test_cisco_ios_cfg_bgp() \ No newline at end of file diff --git a/ttp_templates/ttp_templates.py b/ttp_templates/ttp_templates.py index a210863..0337e01 100644 --- a/ttp_templates/ttp_templates.py +++ b/ttp_templates/ttp_templates.py @@ -56,9 +56,11 @@ def get_template( print("!!!!!!!!!!") print("path: ", path) print("template_dir: ", template_dir) + print("__file__: ", __file__) print("os.path.isdir(template_dir): ", os.path.isdir(template_dir)) print('template_dir.count("ttp_templates"): ', template_dir.count("ttp_templates")) - + print("os.path.exists(os.path.join(template_dir, path)): ", os.path.exists(os.path.join(template_dir, path))) + # Ubuntu returns path that looks like this when calling run_ttp for Netmiko: # '/home/runner/work/ttp_templates/ttp_templates/ttp_templates' # below code is to ensure we go two levels up