From 3088232126b1452fdefdc5f8435c5914533271d8 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Mon, 9 Oct 2017 13:28:26 -0700 Subject: [PATCH 01/12] Fix issue with sub second arp data --- napalm_nxos/nxos.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/napalm_nxos/nxos.py b/napalm_nxos/nxos.py index 50658bd..4dffcd3 100644 --- a/napalm_nxos/nxos.py +++ b/napalm_nxos/nxos.py @@ -606,12 +606,24 @@ def get_arp_table(self): raw_mac = arp_table_entry.get('mac') age = arp_table_entry.get('time-stamp') if age == '-': - age_sec = float(-1) + age_sec = -1.0 + elif ':' not in age: + # Cisco sometimes returns a sub second arp time 0.411797 + try: + age_sec = float(age) + except ValueError: + age_sec = -1.0 else: - age_time = ''.join(age.split(':')) - age_sec = float( - 3600 * int(age_time[:2]) + 60 * int(age_time[2:4]) + int(age_time[4:]) - ) + fields = age.split(':') + if len(fields) == 3: + try: + fields = [float(x) for x in fields] + hours, minutes, seconds = fields + age_sec = 3600 * hours + 60 * minutes + seconds + except ValueError: + age_sec = -1.0 + age_sec = round(age_sec, 1) + interface = py23_compat.text_type(arp_table_entry.get('intf-out')) arp_table.append({ 'interface': interface, From 5879ad35bcafc99f5559bd05760f4766b1bebc26 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Mon, 9 Oct 2017 13:28:53 -0700 Subject: [PATCH 02/12] Fix issue with sub second arp data --- .../alt_test1/expected_result.json | 8 ++++++++ .../alt_test1/show_ip_arp.json | 18 ++++++++++++++++++ .../test_sub_second_data/expected_result.json | 8 ++++++++ .../test_sub_second_data/show_ip_arp.json | 18 ++++++++++++++++++ 4 files changed, 52 insertions(+) create mode 100644 test/unit/mocked_data/test_get_arp_table/alt_test1/expected_result.json create mode 100644 test/unit/mocked_data/test_get_arp_table/alt_test1/show_ip_arp.json create mode 100644 test/unit/mocked_data/test_get_arp_table/test_sub_second_data/expected_result.json create mode 100644 test/unit/mocked_data/test_get_arp_table/test_sub_second_data/show_ip_arp.json diff --git a/test/unit/mocked_data/test_get_arp_table/alt_test1/expected_result.json b/test/unit/mocked_data/test_get_arp_table/alt_test1/expected_result.json new file mode 100644 index 0000000..803aeb7 --- /dev/null +++ b/test/unit/mocked_data/test_get_arp_table/alt_test1/expected_result.json @@ -0,0 +1,8 @@ +[ + { + "interface": "Vlan100", + "ip": "10.122.100.3", + "mac": "18:8B:9D:0B:B3:3F", + "age": 583.0 + } +] diff --git a/test/unit/mocked_data/test_get_arp_table/alt_test1/show_ip_arp.json b/test/unit/mocked_data/test_get_arp_table/alt_test1/show_ip_arp.json new file mode 100644 index 0000000..f85020a --- /dev/null +++ b/test/unit/mocked_data/test_get_arp_table/alt_test1/show_ip_arp.json @@ -0,0 +1,18 @@ +{ + "TABLE_vrf": { + "ROW_vrf": { + "vrf-name-out": "default", + "TABLE_adj": { + "ROW_adj": [ + { + "ip-addr-out": "10.122.100.3", + "mac": "188b.9d0b.b33f", + "time-stamp": "00:09:43", + "intf-out": "Vlan100" + } + ] + }, + "cnt-total": "2438" + } + } +} diff --git a/test/unit/mocked_data/test_get_arp_table/test_sub_second_data/expected_result.json b/test/unit/mocked_data/test_get_arp_table/test_sub_second_data/expected_result.json new file mode 100644 index 0000000..68f277b --- /dev/null +++ b/test/unit/mocked_data/test_get_arp_table/test_sub_second_data/expected_result.json @@ -0,0 +1,8 @@ +[ + { + "interface": "Vlan100", + "ip": "10.122.100.3", + "mac": "18:8B:9D:0B:B3:3F", + "age": 0.4 + } +] diff --git a/test/unit/mocked_data/test_get_arp_table/test_sub_second_data/show_ip_arp.json b/test/unit/mocked_data/test_get_arp_table/test_sub_second_data/show_ip_arp.json new file mode 100644 index 0000000..b84255e --- /dev/null +++ b/test/unit/mocked_data/test_get_arp_table/test_sub_second_data/show_ip_arp.json @@ -0,0 +1,18 @@ +{ + "TABLE_vrf": { + "ROW_vrf": { + "vrf-name-out": "default", + "TABLE_adj": { + "ROW_adj": [ + { + "ip-addr-out": "10.122.100.3", + "mac": "188b.9d0b.b33f", + "time-stamp": "0.411797", + "intf-out": "Vlan100" + } + ] + }, + "cnt-total": "2438" + } + } +} From 8cba371e7e3170c6d8a58bffa48897ffc01e2204 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Mon, 9 Oct 2017 14:18:41 -0700 Subject: [PATCH 03/12] Fix subsecond entries for nxos_ssh --- napalm_nxos_ssh/nxos_ssh.py | 16 ++++++---- .../sub_second_entry/expected_result.json | 32 +++++++++++++++++++ ...ow_ip_arp_vrf_default___exc_INCOMPLETE.txt | 14 ++++++++ 3 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 test/unit_ssh/mocked_data/test_get_arp_table/sub_second_entry/expected_result.json create mode 100644 test/unit_ssh/mocked_data/test_get_arp_table/sub_second_entry/show_ip_arp_vrf_default___exc_INCOMPLETE.txt diff --git a/napalm_nxos_ssh/nxos_ssh.py b/napalm_nxos_ssh/nxos_ssh.py index 245d144..5c25956 100644 --- a/napalm_nxos_ssh/nxos_ssh.py +++ b/napalm_nxos_ssh/nxos_ssh.py @@ -1098,14 +1098,18 @@ def get_arp_table(self): else: raise ValueError("Unexpected output from: {}".format(line.split())) - try: - if age == '-': - age = 0 + if age == '-': + age = -1.0 + elif ':' not in age: + # Cisco sometimes returns a sub second arp time 0.411797 + try: + age = float(age) + except ValueError: + age = -1.0 + else: age = convert_hhmmss(age) age = float(age) - age = round(age, 1) - except ValueError: - raise ValueError("Unable to convert age value to float: {}".format(age)) + age = round(age, 1) # Validate we matched correctly if not re.search(RE_IPADDR, address): diff --git a/test/unit_ssh/mocked_data/test_get_arp_table/sub_second_entry/expected_result.json b/test/unit_ssh/mocked_data/test_get_arp_table/sub_second_entry/expected_result.json new file mode 100644 index 0000000..a288df6 --- /dev/null +++ b/test/unit_ssh/mocked_data/test_get_arp_table/sub_second_entry/expected_result.json @@ -0,0 +1,32 @@ +[ + { + "interface": "mgmt0", + "ip": "10.0.0.2", + "mac": "2C:C2:60:FF:00:21", + "age": 4.0 + }, + { + "interface": "mgmt0", + "ip": "10.0.0.72", + "mac": "2C:C2:60:36:32:21", + "age": 140.0 + }, + { + "interface": "Vlan357", + "ip": "10.5.159.59", + "mac": "00:50:56:14:44:BE", + "age": 590.0 + }, + { + "interface": "Vlan357", + "ip": "10.5.159.78", + "mac": "00:50:56:14:2F:2A", + "age": 0.3 + }, + { + "interface": "Vlan357", + "ip": "10.5.159.79", + "mac": "00:50:56:14:2F:2A", + "age": 851.0 + } +] diff --git a/test/unit_ssh/mocked_data/test_get_arp_table/sub_second_entry/show_ip_arp_vrf_default___exc_INCOMPLETE.txt b/test/unit_ssh/mocked_data/test_get_arp_table/sub_second_entry/show_ip_arp_vrf_default___exc_INCOMPLETE.txt new file mode 100644 index 0000000..ad85520 --- /dev/null +++ b/test/unit_ssh/mocked_data/test_get_arp_table/sub_second_entry/show_ip_arp_vrf_default___exc_INCOMPLETE.txt @@ -0,0 +1,14 @@ + +Flags: * - Adjacencies learnt on non-active FHRP router + + - Adjacencies synced via CFSoE + # - Adjacencies Throttled for Glean + D - Static Adjacencies attached to down interface + +IP ARP Table for context default +Total number of entries: 2 +Address Age MAC Address Interface +10.0.0.2 00:00:04 2cc2.60ff.0021 mgmt0 +10.0.0.72 00:02:20 2cc2.6036.3221 mgmt0 +10.5.159.59 00:09:50 0050.5614.44be Vlan357 +10.5.159.78 0.282854 0050.5614.2f2a Vlan357 +10.5.159.79 00:14:11 0050.5614.2f2a Vlan357 From 120c548788ff32a6f4b71ed19d39b5d3727a1534 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Mon, 9 Oct 2017 16:32:31 -0700 Subject: [PATCH 04/12] Fixing bug with gateway_mac --- napalm_nxos_ssh/nxos_ssh.py | 26 ++++++++++--------- .../gateway_mac/expected_result.json | 11 ++++++++ .../gateway_mac/show_mac_address_table.txt | 10 +++++++ 3 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 test/unit_ssh/mocked_data/test_get_mac_address_table/gateway_mac/expected_result.json create mode 100644 test/unit_ssh/mocked_data/test_get_mac_address_table/gateway_mac/show_mac_address_table.txt diff --git a/napalm_nxos_ssh/nxos_ssh.py b/napalm_nxos_ssh/nxos_ssh.py index 245d144..e7eecd7 100644 --- a/napalm_nxos_ssh/nxos_ssh.py +++ b/napalm_nxos_ssh/nxos_ssh.py @@ -1263,9 +1263,10 @@ def get_mac_address_table(self): """ # The '*' is stripped out later - RE_MACTABLE_FORMAT1 = r"^\s+{}\s+{}\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+".format( - VLAN_REGEX, - MAC_REGEX) # 7 fields + RE_MACTABLE_FORMAT1 = r"^\s+{}\s+{}\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+".format(VLAN_REGEX, + MAC_REGEX) + RE_MACTABLE_FORMAT2 = r"^\s+{}\s+{}\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+".format('-', + MAC_REGEX) mac_address_table = [] command = 'show mac address-table' @@ -1280,6 +1281,8 @@ def process_mac_fields(vlan, mac, mac_type, interface): static = True if vlan.lower() == 'all': vlan = 0 + elif vlan == '-': + vlan = 0 if interface.lower() == 'cpu' or re.search(r'router', interface.lower()) or \ re.search(r'switch', interface.lower()): interface = '' @@ -1302,8 +1305,8 @@ def process_mac_fields(vlan, mac, mac_type, interface): # Skip the header lines output = re.split(r'^----.*', output, flags=re.M)[1:] output = "\n".join(output).strip() - # Strip any leading astericks - output = re.sub(r"^\*", "", output, flags=re.M) + # Strip any leading astericks or G character + output = re.sub(r"^[\*G]", "", output, flags=re.M) for line in output.splitlines(): @@ -1321,14 +1324,13 @@ def process_mac_fields(vlan, mac, mac_type, interface): continue elif re.search('^\s*$', line): continue - # Format1 - elif re.search(RE_MACTABLE_FORMAT1, line): - if len(line.split()) == 7: - vlan, mac, mac_type, _, _, _, interface = line.split() - mac_address_table.append(process_mac_fields(vlan, mac, mac_type, interface)) - else: - raise ValueError("Unexpected output from: {}".format(line.split())) + for pattern in [RE_MACTABLE_FORMAT1, RE_MACTABLE_FORMAT2]: + if re.search(pattern, line): + if len(line.split()) == 7: + vlan, mac, mac_type, _, _, _, interface = line.split() + mac_address_table.append(process_mac_fields(vlan, mac, mac_type, interface)) + break else: raise ValueError("Unexpected output from: {}".format(repr(line))) diff --git a/test/unit_ssh/mocked_data/test_get_mac_address_table/gateway_mac/expected_result.json b/test/unit_ssh/mocked_data/test_get_mac_address_table/gateway_mac/expected_result.json new file mode 100644 index 0000000..89812d9 --- /dev/null +++ b/test/unit_ssh/mocked_data/test_get_mac_address_table/gateway_mac/expected_result.json @@ -0,0 +1,11 @@ +[ + { + "interface": "sup-eth1(R)", + "moves": -1, + "static": true, + "last_move": -1.0, + "vlan": 0, + "active": false, + "mac": "2C:C2:60:5E:5D:E8" + } +] diff --git a/test/unit_ssh/mocked_data/test_get_mac_address_table/gateway_mac/show_mac_address_table.txt b/test/unit_ssh/mocked_data/test_get_mac_address_table/gateway_mac/show_mac_address_table.txt new file mode 100644 index 0000000..17de17b --- /dev/null +++ b/test/unit_ssh/mocked_data/test_get_mac_address_table/gateway_mac/show_mac_address_table.txt @@ -0,0 +1,10 @@ + Note: MAC table entries displayed are getting read from software. + Use the 'hardware-age' keyword to get information related to 'Age' + + Legend: + * - primary entry, G - Gateway MAC, (R) - Routed MAC, O - Overlay MAC + age - seconds since last seen,+ - primary entry using vPC Peer-Link, E - EVPN entry + (T) - True, (F) - False , ~~~ - use 'hardware-age' keyword to retrieve age info + VLAN/BD MAC Address Type age Secure NTFY Ports/SWID.SSID.LID +---------+-----------------+--------+---------+------+----+------------------ +G - 2cc2.605e.5de8 static - F F sup-eth1(R) From bea29be3cc11c43c8550e6e31ffd43b1c50f9822 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Tue, 10 Oct 2017 09:02:12 -0700 Subject: [PATCH 05/12] Fixing missing domain name --- napalm_nxos_ssh/nxos_ssh.py | 11 +- .../missing_domain/expected_result.json | 10 ++ .../missing_domain/show_hostname.txt | 1 + .../missing_domain/show_hosts.txt | 6 + .../missing_domain/show_interface_status.txt | 150 ++++++++++++++++++ .../missing_domain/show_version.txt | 37 +++++ 6 files changed, 209 insertions(+), 6 deletions(-) create mode 100644 test/unit_ssh/mocked_data/test_get_facts/missing_domain/expected_result.json create mode 100644 test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_hostname.txt create mode 100644 test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_hosts.txt create mode 100644 test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_interface_status.txt create mode 100644 test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_version.txt diff --git a/napalm_nxos_ssh/nxos_ssh.py b/napalm_nxos_ssh/nxos_ssh.py index 245d144..5418cc5 100644 --- a/napalm_nxos_ssh/nxos_ssh.py +++ b/napalm_nxos_ssh/nxos_ssh.py @@ -758,7 +758,7 @@ def get_facts(self): # default values. vendor = u'Cisco' uptime = -1 - serial_number, fqdn, os_version, hostname = (u'Unknown', u'Unknown', u'Unknown', u'Unknown') + serial_number, fqdn, os_version, hostname, domain_name = ('Unknown',) * 5 # obtain output from device show_ver = self.device.send_command('show version') @@ -793,11 +793,10 @@ def get_facts(self): _, domain_name = re.split(r".*Default domain.*is ", line) domain_name = domain_name.strip() break - if domain_name != 'Unknown' and hostname != 'Unknown': - if hostname.count(".") >= 2: - fqdn = hostname - else: - fqdn = '{}.{}'.format(hostname, domain_name) + if hostname.count(".") >= 2: + fqdn = hostname + elif domain_name != 'Unknown': + fqdn = '{}.{}'.format(hostname, domain_name) # interface_list filter interface_list = [] diff --git a/test/unit_ssh/mocked_data/test_get_facts/missing_domain/expected_result.json b/test/unit_ssh/mocked_data/test_get_facts/missing_domain/expected_result.json new file mode 100644 index 0000000..2582d55 --- /dev/null +++ b/test/unit_ssh/mocked_data/test_get_facts/missing_domain/expected_result.json @@ -0,0 +1,10 @@ +{ + "hostname": "nxos1", + "fqdn": "Unknown", + "model": "NX-OSv", + "uptime": 2030983, + "vendor": "Cisco", + "os_version": "7.3(1)D1(1)", + "serial_number": "TM6012EC74B", + "interface_list": ["mgmt0", "Eth2/1", "Eth2/2", "Eth2/3", "Eth2/4", "Eth2/5", "Eth2/6", "Eth2/7", "Eth2/8", "Eth2/9", "Eth2/10", "Eth2/11", "Eth2/12", "Eth2/13", "Eth2/14", "Eth2/15", "Eth2/16", "Eth2/17", "Eth2/18", "Eth2/19", "Eth2/20", "Eth2/21", "Eth2/22", "Eth2/23", "Eth2/24", "Eth2/25", "Eth2/26", "Eth2/27", "Eth2/28", "Eth2/29", "Eth2/30", "Eth2/31", "Eth2/32", "Eth2/33", "Eth2/34", "Eth2/35", "Eth2/36", "Eth2/37", "Eth2/38", "Eth2/39", "Eth2/40", "Eth2/41", "Eth2/42", "Eth2/43", "Eth2/44", "Eth2/45", "Eth2/46", "Eth2/47", "Eth2/48", "Eth3/1", "Eth3/2", "Eth3/3", "Eth3/4", "Eth3/5", "Eth3/6", "Eth3/7", "Eth3/8", "Eth3/9", "Eth3/10", "Eth3/11", "Eth3/12", "Eth3/13", "Eth3/14", "Eth3/15", "Eth3/16", "Eth3/17", "Eth3/18", "Eth3/19", "Eth3/20", "Eth3/21", "Eth3/22", "Eth3/23", "Eth3/24", "Eth3/25", "Eth3/26", "Eth3/27", "Eth3/28", "Eth3/29", "Eth3/30", "Eth3/31", "Eth3/32", "Eth3/33", "Eth3/34", "Eth3/35", "Eth3/36", "Eth3/37", "Eth3/38", "Eth3/39", "Eth3/40", "Eth3/41", "Eth3/42", "Eth3/43", "Eth3/44", "Eth3/45", "Eth3/46", "Eth3/47", "Eth3/48", "Eth4/1", "Eth4/2", "Eth4/3", "Eth4/4", "Eth4/5", "Eth4/6", "Eth4/7", "Eth4/8", "Eth4/9", "Eth4/10", "Eth4/11", "Eth4/12", "Eth4/13", "Eth4/14", "Eth4/15", "Eth4/16", "Eth4/17", "Eth4/18", "Eth4/19", "Eth4/20", "Eth4/21", "Eth4/22", "Eth4/23", "Eth4/24", "Eth4/25", "Eth4/26", "Eth4/27", "Eth4/28", "Eth4/29", "Eth4/30", "Eth4/31", "Eth4/32", "Eth4/33", "Eth4/34", "Eth4/35", "Eth4/36", "Eth4/37", "Eth4/38", "Eth4/39", "Eth4/40", "Eth4/41", "Eth4/42", "Eth4/43", "Eth4/44", "Eth4/45", "Eth4/46", "Eth4/47", "Eth4/48", "Lo55", "Vlan1"] +} diff --git a/test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_hostname.txt b/test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_hostname.txt new file mode 100644 index 0000000..3a3959c --- /dev/null +++ b/test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_hostname.txt @@ -0,0 +1 @@ +nxos1 diff --git a/test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_hosts.txt b/test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_hosts.txt new file mode 100644 index 0000000..10f8c5c --- /dev/null +++ b/test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_hosts.txt @@ -0,0 +1,6 @@ +DNS lookup enabled +Name/address lookup uses domain service +Name servers are 255.255.255.255 + + +Host Address diff --git a/test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_interface_status.txt b/test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_interface_status.txt new file mode 100644 index 0000000..51b581d --- /dev/null +++ b/test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_interface_status.txt @@ -0,0 +1,150 @@ +-------------------------------------------------------------------------------- +Port Name Status Vlan Duplex Speed Type +-------------------------------------------------------------------------------- +mgmt0 -- connected routed full a-1000 -- +Eth2/1 -- connected routed full 1000 +Eth2/2 -- connected routed full 1000 +Eth2/3 -- connected routed full 1000 +Eth2/4 -- connected routed full 1000 +Eth2/5 -- connected routed full 1000 +Eth2/6 -- connected routed full 1000 +Eth2/7 -- connected routed full 1000 +Eth2/8 -- disabled routed auto auto +Eth2/9 -- disabled routed auto auto +Eth2/10 -- disabled routed auto auto +Eth2/11 -- disabled routed auto auto +Eth2/12 -- disabled routed auto auto +Eth2/13 -- disabled routed auto auto +Eth2/14 -- disabled routed auto auto +Eth2/15 -- disabled routed auto auto +Eth2/16 -- disabled routed auto auto +Eth2/17 -- disabled routed auto auto +Eth2/18 -- disabled routed auto auto +Eth2/19 -- disabled routed auto auto +Eth2/20 -- disabled routed auto auto +Eth2/21 -- disabled routed auto auto +Eth2/22 -- disabled routed auto auto +Eth2/23 -- disabled routed auto auto +Eth2/24 -- disabled routed auto auto +Eth2/25 -- disabled routed auto auto +Eth2/26 -- disabled routed auto auto +Eth2/27 -- disabled routed auto auto +Eth2/28 -- disabled routed auto auto +Eth2/29 -- disabled routed auto auto +Eth2/30 -- disabled routed auto auto +Eth2/31 -- disabled routed auto auto +Eth2/32 -- disabled routed auto auto +Eth2/33 -- disabled routed auto auto +Eth2/34 -- disabled routed auto auto +Eth2/35 -- disabled routed auto auto +Eth2/36 -- disabled routed auto auto +Eth2/37 -- disabled routed auto auto +Eth2/38 -- disabled routed auto auto +Eth2/39 -- disabled routed auto auto +Eth2/40 -- disabled routed auto auto +Eth2/41 -- disabled routed auto auto +Eth2/42 -- disabled routed auto auto +Eth2/43 -- disabled routed auto auto +Eth2/44 -- disabled routed auto auto +Eth2/45 -- disabled routed auto auto +Eth2/46 -- disabled routed auto auto +Eth2/47 -- disabled routed auto auto +Eth2/48 -- disabled routed auto auto +Eth3/1 -- disabled routed auto auto +Eth3/2 -- disabled routed auto auto +Eth3/3 -- disabled routed auto auto +Eth3/4 -- disabled routed auto auto +Eth3/5 -- disabled routed auto auto +Eth3/6 -- disabled routed auto auto +Eth3/7 -- disabled routed auto auto +Eth3/8 -- disabled routed auto auto +Eth3/9 -- disabled routed auto auto +Eth3/10 -- disabled routed auto auto +Eth3/11 -- disabled routed auto auto +Eth3/12 -- disabled routed auto auto +Eth3/13 -- disabled routed auto auto +Eth3/14 -- disabled routed auto auto +Eth3/15 -- disabled routed auto auto +Eth3/16 -- disabled routed auto auto +Eth3/17 -- disabled routed auto auto +Eth3/18 -- disabled routed auto auto +Eth3/19 -- disabled routed auto auto +Eth3/20 -- disabled routed auto auto +Eth3/21 -- disabled routed auto auto +Eth3/22 -- disabled routed auto auto +Eth3/23 -- disabled routed auto auto +Eth3/24 -- disabled routed auto auto +Eth3/25 -- disabled routed auto auto +Eth3/26 -- disabled routed auto auto +Eth3/27 -- disabled routed auto auto +Eth3/28 -- disabled routed auto auto +Eth3/29 -- disabled routed auto auto +Eth3/30 -- disabled routed auto auto +Eth3/31 -- disabled routed auto auto +Eth3/32 -- disabled routed auto auto +Eth3/33 -- disabled routed auto auto +Eth3/34 -- disabled routed auto auto +Eth3/35 -- disabled routed auto auto +Eth3/36 -- disabled routed auto auto +Eth3/37 -- disabled routed auto auto +Eth3/38 -- disabled routed auto auto +Eth3/39 -- disabled routed auto auto +Eth3/40 -- disabled routed auto auto +Eth3/41 -- disabled routed auto auto +Eth3/42 -- disabled routed auto auto +Eth3/43 -- disabled routed auto auto +Eth3/44 -- disabled routed auto auto +Eth3/45 -- disabled routed auto auto +Eth3/46 -- disabled routed auto auto +Eth3/47 -- disabled routed auto auto +Eth3/48 -- disabled routed auto auto +Eth4/1 -- disabled routed auto auto +Eth4/2 -- disabled routed auto auto +Eth4/3 -- disabled routed auto auto +Eth4/4 -- disabled routed auto auto +Eth4/5 -- disabled routed auto auto +Eth4/6 -- disabled routed auto auto +Eth4/7 -- disabled routed auto auto +Eth4/8 -- disabled routed auto auto +Eth4/9 -- disabled routed auto auto +Eth4/10 -- disabled routed auto auto +Eth4/11 -- disabled routed auto auto +Eth4/12 -- disabled routed auto auto +Eth4/13 -- disabled routed auto auto +Eth4/14 -- disabled routed auto auto +Eth4/15 -- disabled routed auto auto +Eth4/16 -- disabled routed auto auto +Eth4/17 -- disabled routed auto auto +Eth4/18 -- disabled routed auto auto +Eth4/19 -- disabled routed auto auto +Eth4/20 -- disabled routed auto auto +Eth4/21 -- disabled routed auto auto +Eth4/22 -- disabled routed auto auto +Eth4/23 -- disabled routed auto auto +Eth4/24 -- disabled routed auto auto +Eth4/25 -- disabled routed auto auto +Eth4/26 -- disabled routed auto auto +Eth4/27 -- disabled routed auto auto +Eth4/28 -- disabled routed auto auto +Eth4/29 -- disabled routed auto auto +Eth4/30 -- disabled routed auto auto +Eth4/31 -- disabled routed auto auto +Eth4/32 -- disabled routed auto auto +Eth4/33 -- disabled routed auto auto +Eth4/34 -- disabled routed auto auto +Eth4/35 -- disabled routed auto auto +Eth4/36 -- disabled routed auto auto +Eth4/37 -- disabled routed auto auto +Eth4/38 -- disabled routed auto auto +Eth4/39 -- disabled routed auto auto +Eth4/40 -- disabled routed auto auto +Eth4/41 -- disabled routed auto auto +Eth4/42 -- disabled routed auto auto +Eth4/43 -- disabled routed auto auto +Eth4/44 -- disabled routed auto auto +Eth4/45 -- disabled routed auto auto +Eth4/46 -- disabled routed auto auto +Eth4/47 -- disabled routed auto auto +Eth4/48 -- disabled routed auto auto +Lo55 -- connected routed auto auto -- +Vlan1 -- down routed auto auto -- diff --git a/test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_version.txt b/test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_version.txt new file mode 100644 index 0000000..ed3931c --- /dev/null +++ b/test/unit_ssh/mocked_data/test_get_facts/missing_domain/show_version.txt @@ -0,0 +1,37 @@ +Cisco Nexus Operating System (NX-OS) Software +TAC support: http://www.cisco.com/tac +Documents: http://www.cisco.com/en/US/products/ps9372/tsd_products_support_series_home.html +Copyright (c) 2002-2016, Cisco Systems, Inc. All rights reserved. +The copyrights to certain works contained herein are owned by +other third parties and are used and distributed under license. +Some parts of this software are covered under the GNU Public +License. A copy of the license is available at +http://www.gnu.org/licenses/gpl.html. + +NX-OSv is a demo version of the Nexus Operating System + +Software + loader: version N/A + kickstart: version 7.3(1)D1(1) [build 7.3(1)D1(0.10)] + system: version 7.3(1)D1(1) [build 7.3(1)D1(0.10)] + kickstart image file is: bootflash:///titanium-d1-kickstart.7.3.1.D1.0.10.bin + kickstart compile time: 1/11/2016 16:00:00 [02/22/2016 23:39:33] + system image file is: bootflash:///titanium-d1.7.3.1.D1.0.10.bin + system compile time: 1/11/2016 16:00:00 [02/23/2016 01:43:36] + + +Hardware + cisco NX-OSv Chassis ("NX-OSv Supervisor Module") + Intel(R) Xeon(R) CPU E5-2670 with 4002312 kB of memory. + Processor Board ID TM6012EC74B + + Device name: nxos1 + bootflash: 1582402 kB + +Kernel uptime is 23 day(s), 12 hour(s), 9 minute(s), 43 second(s) + + +plugin + Core Plugin, Ethernet Plugin + +Active Package(s) From 03018cce5365c5941059036ddac408957eda9909 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Tue, 10 Oct 2017 09:34:50 -0700 Subject: [PATCH 06/12] Don't use Unknown for unspecified values --- napalm_nxos_ssh/nxos_ssh.py | 4 ++-- .../test_get_facts/missing_domain/expected_result.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/napalm_nxos_ssh/nxos_ssh.py b/napalm_nxos_ssh/nxos_ssh.py index 5418cc5..c07aaa8 100644 --- a/napalm_nxos_ssh/nxos_ssh.py +++ b/napalm_nxos_ssh/nxos_ssh.py @@ -758,7 +758,7 @@ def get_facts(self): # default values. vendor = u'Cisco' uptime = -1 - serial_number, fqdn, os_version, hostname, domain_name = ('Unknown',) * 5 + serial_number, fqdn, os_version, hostname, domain_name = ('',) * 5 # obtain output from device show_ver = self.device.send_command('show version') @@ -795,7 +795,7 @@ def get_facts(self): break if hostname.count(".") >= 2: fqdn = hostname - elif domain_name != 'Unknown': + elif domain_name: fqdn = '{}.{}'.format(hostname, domain_name) # interface_list filter diff --git a/test/unit_ssh/mocked_data/test_get_facts/missing_domain/expected_result.json b/test/unit_ssh/mocked_data/test_get_facts/missing_domain/expected_result.json index 2582d55..b8702b4 100644 --- a/test/unit_ssh/mocked_data/test_get_facts/missing_domain/expected_result.json +++ b/test/unit_ssh/mocked_data/test_get_facts/missing_domain/expected_result.json @@ -1,6 +1,6 @@ { "hostname": "nxos1", - "fqdn": "Unknown", + "fqdn": "", "model": "NX-OSv", "uptime": 2030983, "vendor": "Cisco", From ad5723a000be7d1c190b86aed3a5ae5d862e0961 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 11 Oct 2017 10:54:45 -0700 Subject: [PATCH 07/12] Fixing rollback issue; cleaning up some config operations --- napalm_nxos_ssh/nxos_ssh.py | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/napalm_nxos_ssh/nxos_ssh.py b/napalm_nxos_ssh/nxos_ssh.py index 1df18f8..b1fb461 100644 --- a/napalm_nxos_ssh/nxos_ssh.py +++ b/napalm_nxos_ssh/nxos_ssh.py @@ -668,9 +668,9 @@ def _commit_merge(self): if 'Invalid command' in output: raise MergeConfigException('Error while applying config!') if not self._save(): - raise CommandErrorException('Unable to commit config!') + raise CommandErrorException('Unable to save running-config to startup!') - def _save_config(self, filename): + def _save_to_checkpoint(self, filename): """Save the current running config to the given file.""" command = 'checkpoint file {}'.format(filename) self.device.send_command(command) @@ -678,13 +678,10 @@ def _save_config(self, filename): def _disable_confirmation(self): self._send_config_commands(['terminal dont-ask']) - def _load_config(self): + def _load_cfg_from_checkpoint(self): command = 'rollback running file {0}'.format(self.replace_file.split('/')[-1]) self._disable_confirmation() - try: - rollback_result = self.device.send_command(command) - except Exception: - return False + rollback_result = self.device.send_command(command) if 'Rollback failed.' in rollback_result['msg'] or 'ERROR' in rollback_result: raise ReplaceConfigException(rollback_result['msg']) elif rollback_result == []: @@ -694,9 +691,11 @@ def _load_config(self): def commit_config(self): if self.loaded: self.backup_file = 'config_' + str(datetime.now()).replace(' ', '_') - self._save_config(self.backup_file) + # Create Checkpoint from current running-config + self._save_to_checkpoint(self.backup_file) if self.replace: - if self._load_config() is False: + cfg_replace_status = self._load_cfg_from_checkpoint() + if not cfg_replace_status: raise ReplaceConfigException else: try: @@ -704,7 +703,6 @@ def commit_config(self): self.merge_candidate = '' # clear the merge buffer except Exception as e: raise MergeConfigException(str(e)) - self.changed = True self.loaded = False else: @@ -726,16 +724,14 @@ def discard_config(self): self._delete_file(self.replace_file) self.loaded = False - def _rollback_ssh(self, backup_file): - command = 'rollback running-config file %s' % backup_file - result = self.device.send_command(command) - if 'completed' not in result.lower(): - raise ReplaceConfigException(result) - self._save_ssh() - def rollback(self): if self.changed: - self._rollback_ssh(self.backup_file) + command = 'rollback running-config file {}'.format(self.backup_file) + result = self.device.send_command(command) + if 'completed' not in result.lower(): + raise ReplaceConfigException(result) + if not self._save(): + raise CommandErrorException('Unable to save running-config to startup!') self.changed = False def _apply_key_map(self, key_map, table): From ff15056380052daea12fc31ec8c748d075e62155 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 11 Oct 2017 12:00:55 -0700 Subject: [PATCH 08/12] Fix bug with rollback and 'msg' key --- napalm_nxos_ssh/nxos_ssh.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/napalm_nxos_ssh/nxos_ssh.py b/napalm_nxos_ssh/nxos_ssh.py index b1fb461..a97be84 100644 --- a/napalm_nxos_ssh/nxos_ssh.py +++ b/napalm_nxos_ssh/nxos_ssh.py @@ -682,8 +682,8 @@ def _load_cfg_from_checkpoint(self): command = 'rollback running file {0}'.format(self.replace_file.split('/')[-1]) self._disable_confirmation() rollback_result = self.device.send_command(command) - if 'Rollback failed.' in rollback_result['msg'] or 'ERROR' in rollback_result: - raise ReplaceConfigException(rollback_result['msg']) + if 'Rollback failed.' in rollback_result or 'ERROR' in rollback_result: + raise ReplaceConfigException(rollback_result) elif rollback_result == []: return False return True From a4468526f1fd1f90af3b6f61e6f7fc75d3e4fe47 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Mon, 23 Oct 2017 12:05:12 -0700 Subject: [PATCH 09/12] Fix github templates --- .github/ISSUE_TEMPLATE | 33 ++++----------------------------- .github/PULL_REQUEST_TEMPLATE | 8 +++++++- 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE index c4809be..56a3192 100644 --- a/.github/ISSUE_TEMPLATE +++ b/.github/ISSUE_TEMPLATE @@ -1,32 +1,7 @@ -### Description of Issue/Question +### The NAPALM Project has reunified! +Please submit all NAPALM issues to: +https://github.com/napalm-automation/napalm/issues -### Did you follow the steps from https://github.com/napalm-automation/napalm#faq -[ ] Yes -[ ] No - -### Setup - -### napalm-nxos version -(Paste verbatim output from `pip freeze | grep napalm-nxos` between quotes below) - -``` - -``` - -### NX-OS version -(Paste verbatim output from `show version | json` between quotes below) - -``` - -``` - -### Steps to Reproduce the Issue - -### Error Traceback -(Paste the complete traceback of the exception between quotes below) - -``` - -``` +### DO NOT submit any issues to this repository. diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index 8767eea..55fb53c 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -1 +1,7 @@ - +### The NAPALM Project has reunified! + +Please submit all NAPALM pull requests to: +https://github.com/napalm-automation/napalm/pulls + + +### DO NOT submit any pull requests to this repository. From d1900c01d0e94585804ec84439ee8b25ac8875a2 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 25 Oct 2017 14:16:15 -0700 Subject: [PATCH 10/12] Fix bug with adding content to replace file --- napalm_nxos_ssh/nxos_ssh.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/napalm_nxos_ssh/nxos_ssh.py b/napalm_nxos_ssh/nxos_ssh.py index 1fc4ae3..5936e1a 100644 --- a/napalm_nxos_ssh/nxos_ssh.py +++ b/napalm_nxos_ssh/nxos_ssh.py @@ -456,16 +456,6 @@ def parse_uptime(uptime_str): (hours * 3600) + (minutes * 60) + seconds return uptime_sec - @staticmethod - def fix_checkpoint_string(string, filename): - # used to generate checkpoint-like files - pattern = '''!Command: Checkpoint cmd vdc 1''' - - if '!Command' in string: - return re.sub('!Command.*', pattern.format(filename), string) - else: - return "{0}\n{1}".format(pattern.format(filename), string) - def is_alive(self): """Returns a flag with the state of the SSH connection.""" null = chr(0) @@ -512,23 +502,17 @@ def _verify_remote_file_exists(self, dst, file_system='bootflash:'): def _replace_candidate(self, filename, config): if not filename: - file_content = self.fix_checkpoint_string(config, self.replace_file) filename = self._create_tmp_file(config) else: if not os.path.isfile(filename): raise ReplaceConfigException("File {} not found".format(filename)) self.replace_file = filename - with open(self.replace_file, 'r+') as f: - file_content = f.read() - file_content = self.fix_checkpoint_string(file_content, self.replace_file) - f.write(file_content) - if not self._enough_space(self.replace_file): msg = 'Could not transfer file. Not enough space on device.' raise ReplaceConfigException(msg) - self._check_file_exists(self.replace_file) + self._check_file_exists(self.replace_file): dest = os.path.basename(self.replace_file) full_remote_path = 'bootflash:{}'.format(dest) with paramiko.SSHClient() as ssh: From 4d9838fa93bc3e2882e557b32d059829736856a9 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 25 Oct 2017 15:16:06 -0700 Subject: [PATCH 11/12] Remove extra colon --- napalm_nxos_ssh/nxos_ssh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/napalm_nxos_ssh/nxos_ssh.py b/napalm_nxos_ssh/nxos_ssh.py index 5936e1a..363ce62 100644 --- a/napalm_nxos_ssh/nxos_ssh.py +++ b/napalm_nxos_ssh/nxos_ssh.py @@ -512,7 +512,7 @@ def _replace_candidate(self, filename, config): msg = 'Could not transfer file. Not enough space on device.' raise ReplaceConfigException(msg) - self._check_file_exists(self.replace_file): + self._check_file_exists(self.replace_file) dest = os.path.basename(self.replace_file) full_remote_path = 'bootflash:{}'.format(dest) with paramiko.SSHClient() as ssh: From 739c721ec03915915e1d429eda8de0f86c5d231a Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Thu, 26 Oct 2017 09:27:49 -0700 Subject: [PATCH 12/12] Roll version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d1a9fdc..5d6bafc 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="napalm-nxos", - version="0.7.0", + version="0.7.1", packages=find_packages(exclude=["test", "test.*"]), author="David Barroso", author_email="dbarrosop@dravetech.com",