From 68880771973daa153cfae8ffb689b329d0e142c8 Mon Sep 17 00:00:00 2001 From: ogenstad Date: Mon, 19 Dec 2016 08:52:46 +0100 Subject: [PATCH 01/20] Move to tox --- .travis.yml | 8 ++++---- setup.cfg | 6 +++++- tox.ini | 9 +++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 tox.ini diff --git a/.travis.yml b/.travis.yml index c774c32..5bda2a7 100755 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,8 @@ python: - 3.4 - 3.5 install: -- pip install -r requirements-dev.txt -- pip install . +- pip install tox-travis +- pip install coveralls deploy: provider: pypi user: dbarroso @@ -15,8 +15,8 @@ deploy: tags: true branch: master script: -- py.test --cov-report= --cov=napalm_ios test/ -- pylama . +- tox + after_success: - coveralls - if [ $TRAVIS_TAG ]; then curl -X POST https://readthedocs.org/build/napalm; fi diff --git a/setup.cfg b/setup.cfg index 7e73a45..f37ce56 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,6 +7,10 @@ skip = build/*,.tox/* max_line_length = 100 [tool:pytest] -addopts = --cov=./ -vs +addopts = --cov=napalm_ios --cov-report term-missing -vs --pylama json_report = report.json jsonapi = true + +[coverage:run] +include = + napalm_ios/* diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..5ae5d81 --- /dev/null +++ b/tox.ini @@ -0,0 +1,9 @@ +[tox] +envlist = py27,py34,py35 + +[testenv] +deps = + -rrequirements-dev.txt + +commands= + py.test From 9a3a98be948402cf24630573ba2a7ba15e3c0af8 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 4 Jan 2017 10:35:44 -0800 Subject: [PATCH 02/20] Fix issue with new version of coverage --- setup.cfg | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index f37ce56..7283207 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,5 +12,4 @@ json_report = report.json jsonapi = true [coverage:run] -include = - napalm_ios/* +source = napalm_ios From ab6af0a3a38e67df9d2a2ba895a93e0fd1ac6d3a Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 4 Jan 2017 10:37:17 -0800 Subject: [PATCH 03/20] Get mac address table improvements --- napalm_ios/ios.py | 4 +++- .../alt_format1/expected_result.json | 1 + .../alt_format1/show_mac_address_table.txt | 14 ++++++++++++++ .../alt_format2/expected_result.json | 1 + .../alt_format2/show_mac_address_table.txt | 18 ++++++++++++++++++ .../alt_format3/expected_result.json | 1 + .../alt_format3/show_mac_address_table.txt | 18 ++++++++++++++++++ .../expected_result.json | 0 .../show_mac_address_table.txt | 18 ++++++++++++++++++ 9 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 test/unit/mocked_data/test_get_mac_address_table/alt_format1/expected_result.json create mode 100644 test/unit/mocked_data/test_get_mac_address_table/alt_format1/show_mac_address_table.txt create mode 100644 test/unit/mocked_data/test_get_mac_address_table/alt_format2/expected_result.json create mode 100644 test/unit/mocked_data/test_get_mac_address_table/alt_format2/show_mac_address_table.txt create mode 100644 test/unit/mocked_data/test_get_mac_address_table/alt_format3/expected_result.json create mode 100644 test/unit/mocked_data/test_get_mac_address_table/alt_format3/show_mac_address_table.txt create mode 100644 test/unit/mocked_data/test_get_mac_address_table/expected_result.json create mode 100644 test/unit/mocked_data/test_get_mac_address_table/show_mac_address_table.txt diff --git a/napalm_ios/ios.py b/napalm_ios/ios.py index e3048d6..c78f72c 100755 --- a/napalm_ios/ios.py +++ b/napalm_ios/ios.py @@ -1369,7 +1369,7 @@ def process_mac_fields(vlan, mac, mac_type, interface): static = True if vlan.lower() == 'all': vlan = 0 - if interface.lower() == 'cpu': + if interface.lower() == 'cpu' or interface.lower() == 'router': interface = '' else: static = False @@ -1394,6 +1394,7 @@ 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() + output = re.split(r'Multicast Entries.*', output)[0] for line in output.splitlines(): line = line.strip() if line == '': @@ -1427,6 +1428,7 @@ def process_mac_fields(vlan, mac, mac_type, interface): mac_address_table.append(process_mac_fields(vlan, mac, mac_type, interface)) else: raise ValueError("Unexpected output from: {}".format(repr(line))) + return mac_address_table def get_snmp_information(self): diff --git a/test/unit/mocked_data/test_get_mac_address_table/alt_format1/expected_result.json b/test/unit/mocked_data/test_get_mac_address_table/alt_format1/expected_result.json new file mode 100644 index 0000000..1d383e9 --- /dev/null +++ b/test/unit/mocked_data/test_get_mac_address_table/alt_format1/expected_result.json @@ -0,0 +1 @@ +[{"interface": "Te1/30", "vlan": 666, "moves": -1, "static": false, "active": true, "mac": "30a3.30a3.a1c3", "last_move": -1.0}, {"interface": "Po3", "vlan": 666, "moves": -1, "static": false, "active": true, "mac": "30a3.30a3.5ab8", "last_move": -1.0}, {"interface": "Te1/21", "vlan": 60, "moves": -1, "static": false, "active": true, "mac": "30a3.30a3.4d54", "last_move": -1.0}, {"interface": "", "vlan": 777, "moves": -1, "static": true, "active": false, "mac": "0000.30a3.0167", "last_move": -1.0}, {"interface": "Po6", "vlan": 664, "moves": -1, "static": false, "active": true, "mac": "30a3.30a3.58b5", "last_move": -1.0}, {"interface": "Te3/20", "vlan": 667, "moves": -1, "static": false, "active": true, "mac": "30a3.30a3.daf5", "last_move": -1.0}, {"interface": "Po6", "vlan": 668, "moves": -1, "static": false, "active": true, "mac": "30a3.30a3.e401", "last_move": -1.0}, {"interface": "Te3/20", "vlan": 669, "moves": -1, "static": false, "active": true, "mac": "30a3.30a3.5a22", "last_move": -1.0}] diff --git a/test/unit/mocked_data/test_get_mac_address_table/alt_format1/show_mac_address_table.txt b/test/unit/mocked_data/test_get_mac_address_table/alt_format1/show_mac_address_table.txt new file mode 100644 index 0000000..d2d3498 --- /dev/null +++ b/test/unit/mocked_data/test_get_mac_address_table/alt_format1/show_mac_address_table.txt @@ -0,0 +1,14 @@ +Legend: * - primary entry + age - seconds since last seen + n/a - not available + + vlan mac address type learn age ports +------+----------------+--------+-----+----------+-------------------------- + 666 30a3.30a3.a1c3 dynamic Yes 1200 Te1/30 + 666 30a3.30a3.5ab8 dynamic Yes 0 Po3 + 60 30a3.30a3.4d54 dynamic Yes 3600 Te1/21 +* 777 0000.30a3.0167 static No - Router + 664 30a3.30a3.58b5 dynamic Yes 180 Po6 + 667 30a3.30a3.daf5 dynamic Yes 0 Te3/20 + 668 30a3.30a3.e401 dynamic Yes 600 Po6 + 669 30a3.30a3.5a22 dynamic Yes 300 Te3/20 diff --git a/test/unit/mocked_data/test_get_mac_address_table/alt_format2/expected_result.json b/test/unit/mocked_data/test_get_mac_address_table/alt_format2/expected_result.json new file mode 100644 index 0000000..9dcc078 --- /dev/null +++ b/test/unit/mocked_data/test_get_mac_address_table/alt_format2/expected_result.json @@ -0,0 +1 @@ +[{"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 1, "mac": "30a3.30a3.a1c3", "static": false}, {"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 99, "mac": "30a3.30a3.a1c4", "static": false}, {"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 99, "mac": "30a3.30a3.a1c5", "static": false}, {"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 99, "mac": "30a3.30a3.a1c6", "static": false}, {"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 99, "mac": "30a3.30a3.a1c7", "static": false}, {"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 99, "mac": "30a3.30a3.a1c8", "static": false}, {"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 99, "mac": "30a3.30a3.a1c9", "static": false}, {"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 99, "mac": "30a3.30a3.a1ca", "static": false}] diff --git a/test/unit/mocked_data/test_get_mac_address_table/alt_format2/show_mac_address_table.txt b/test/unit/mocked_data/test_get_mac_address_table/alt_format2/show_mac_address_table.txt new file mode 100644 index 0000000..de245ec --- /dev/null +++ b/test/unit/mocked_data/test_get_mac_address_table/alt_format2/show_mac_address_table.txt @@ -0,0 +1,18 @@ +Unicast Entries + vlan mac address type protocols port +---------+---------------+--------+---------------------+------------------------- + 1 30a3.30a3.a1c3 dynamic ip,ipx,assigned,other Port-channel1 + 99 30a3.30a3.a1c4 dynamic ip,ipx,assigned,other Port-channel1 + 99 30a3.30a3.a1c5 dynamic ip,ipx,assigned,other Port-channel1 + 99 30a3.30a3.a1c6 dynamic ip,ipx,assigned,other Port-channel1 + 99 30a3.30a3.a1c7 dynamic ip,ipx,assigned,other Port-channel1 + 99 30a3.30a3.a1c8 dynamic ip,ipx,assigned,other Port-channel1 + 99 30a3.30a3.a1c9 dynamic ip,ipx,assigned,other Port-channel1 + 99 30a3.30a3.a1ca dynamic ip,ipx,assigned,other Port-channel1 + +Multicast Entries +vlan mac address type ports +---------+---------------+-------+-------------------------------------------- +1 0100.0ccc.ccce system Po1 +1 ffff.ffff.ffff system Po1 +39 ffff.ffff.ffff system Gi10/31,Gi10/32,Switch,Po1 diff --git a/test/unit/mocked_data/test_get_mac_address_table/alt_format3/expected_result.json b/test/unit/mocked_data/test_get_mac_address_table/alt_format3/expected_result.json new file mode 100644 index 0000000..bef304d --- /dev/null +++ b/test/unit/mocked_data/test_get_mac_address_table/alt_format3/expected_result.json @@ -0,0 +1 @@ +[{"interface": "", "vlan": 0, "mac": "0100.0ccc.cccc", "active": false, "moves": -1, "static": true, "last_move": -1.0}, {"interface": "", "vlan": 0, "mac": "0100.0ccc.cccd", "active": false, "moves": -1, "static": true, "last_move": -1.0}, {"interface": "", "vlan": 0, "mac": "0100.0ccc.ccce", "active": false, "moves": -1, "static": true, "last_move": -1.0}, {"interface": "", "vlan": 0, "mac": "0180.c200.0000", "active": false, "moves": -1, "static": true, "last_move": -1.0}, {"interface": "", "vlan": 0, "mac": "0180.c200.0001", "active": false, "moves": -1, "static": true, "last_move": -1.0}, {"interface": "Po1", "vlan": 1, "mac": "30a3.30a3.a1c3", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Po1", "vlan": 1, "mac": "30a3.30a3.a1c4", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Po1", "vlan": 1, "mac": "30a3.30a3.a1c5", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Po1", "vlan": 1, "mac": "30a3.30a3.a1c6", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Te1/1/3", "vlan": 1, "mac": "30a3.30a3.a1c7", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Te1/1/3", "vlan": 99, "mac": "30a3.30a3.a1c8", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Te1/1/3", "vlan": 99, "mac": "30a3.30a3.a1c9", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Te1/1/3", "vlan": 99, "mac": "30a3.30a3.a1ca", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Te1/1/3", "vlan": 99, "mac": "30a3.30a3.a1cb", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Vl99", "vlan": 99, "mac": "30a3.30a3.a1cc", "active": false, "moves": -1, "static": true, "last_move": -1.0}, {"interface": "Po6", "vlan": 99, "mac": "30a3.30a3.a1cd", "active": true, "moves": -1, "static": false, "last_move": -1.0}] diff --git a/test/unit/mocked_data/test_get_mac_address_table/alt_format3/show_mac_address_table.txt b/test/unit/mocked_data/test_get_mac_address_table/alt_format3/show_mac_address_table.txt new file mode 100644 index 0000000..4dc926e --- /dev/null +++ b/test/unit/mocked_data/test_get_mac_address_table/alt_format3/show_mac_address_table.txt @@ -0,0 +1,18 @@ +Vlan Mac Address Type Ports +---- ----------- -------- ----- + All 0100.0ccc.cccc STATIC CPU + All 0100.0ccc.cccd STATIC CPU + All 0100.0ccc.ccce STATIC CPU + All 0180.c200.0000 STATIC CPU + All 0180.c200.0001 STATIC CPU + 1 30a3.30a3.a1c3 DYNAMIC Po1 + 1 30a3.30a3.a1c4 DYNAMIC Po1 + 1 30a3.30a3.a1c5 DYNAMIC Po1 + 1 30a3.30a3.a1c6 DYNAMIC Po1 + 1 30a3.30a3.a1c7 DYNAMIC Te1/1/3 + 99 30a3.30a3.a1c8 DYNAMIC Te1/1/3 + 99 30a3.30a3.a1c9 DYNAMIC Te1/1/3 + 99 30a3.30a3.a1ca DYNAMIC Te1/1/3 + 99 30a3.30a3.a1cb DYNAMIC Te1/1/3 + 99 30a3.30a3.a1cc STATIC Vl99 + 99 30a3.30a3.a1cd DYNAMIC Po6 diff --git a/test/unit/mocked_data/test_get_mac_address_table/expected_result.json b/test/unit/mocked_data/test_get_mac_address_table/expected_result.json new file mode 100644 index 0000000..e69de29 diff --git a/test/unit/mocked_data/test_get_mac_address_table/show_mac_address_table.txt b/test/unit/mocked_data/test_get_mac_address_table/show_mac_address_table.txt new file mode 100644 index 0000000..4dc926e --- /dev/null +++ b/test/unit/mocked_data/test_get_mac_address_table/show_mac_address_table.txt @@ -0,0 +1,18 @@ +Vlan Mac Address Type Ports +---- ----------- -------- ----- + All 0100.0ccc.cccc STATIC CPU + All 0100.0ccc.cccd STATIC CPU + All 0100.0ccc.ccce STATIC CPU + All 0180.c200.0000 STATIC CPU + All 0180.c200.0001 STATIC CPU + 1 30a3.30a3.a1c3 DYNAMIC Po1 + 1 30a3.30a3.a1c4 DYNAMIC Po1 + 1 30a3.30a3.a1c5 DYNAMIC Po1 + 1 30a3.30a3.a1c6 DYNAMIC Po1 + 1 30a3.30a3.a1c7 DYNAMIC Te1/1/3 + 99 30a3.30a3.a1c8 DYNAMIC Te1/1/3 + 99 30a3.30a3.a1c9 DYNAMIC Te1/1/3 + 99 30a3.30a3.a1ca DYNAMIC Te1/1/3 + 99 30a3.30a3.a1cb DYNAMIC Te1/1/3 + 99 30a3.30a3.a1cc STATIC Vl99 + 99 30a3.30a3.a1cd DYNAMIC Po6 From 20e958f0e4aebd44146f45263e4470f453bd8361 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 4 Jan 2017 11:34:15 -0800 Subject: [PATCH 04/20] get mac address table improvements --- .gitignore | 1 + napalm_ios/ios.py | 22 ++++++--- .../3560_format/expected_result.json | 1 + .../3560_format/show_mac_address_table.txt | 48 +++++++++++++++++++ .../4500_format/expected_result.json | 1 + .../show_mac_address_table.txt | 0 .../alt_format2/expected_result.json | 1 - .../alt_format3/expected_result.json | 1 - .../alt_format3/show_mac_address_table.txt | 18 ------- .../expected_result.json | 0 .../show_mac_address_table.txt | 18 ------- 11 files changed, 66 insertions(+), 45 deletions(-) create mode 100644 test/unit/mocked_data/test_get_mac_address_table/3560_format/expected_result.json create mode 100644 test/unit/mocked_data/test_get_mac_address_table/3560_format/show_mac_address_table.txt create mode 100644 test/unit/mocked_data/test_get_mac_address_table/4500_format/expected_result.json rename test/unit/mocked_data/test_get_mac_address_table/{alt_format2 => 4500_format}/show_mac_address_table.txt (100%) delete mode 100644 test/unit/mocked_data/test_get_mac_address_table/alt_format2/expected_result.json delete mode 100644 test/unit/mocked_data/test_get_mac_address_table/alt_format3/expected_result.json delete mode 100644 test/unit/mocked_data/test_get_mac_address_table/alt_format3/show_mac_address_table.txt delete mode 100644 test/unit/mocked_data/test_get_mac_address_table/expected_result.json delete mode 100644 test/unit/mocked_data/test_get_mac_address_table/show_mac_address_table.txt diff --git a/.gitignore b/.gitignore index 7b231ee..d3d0498 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,7 @@ env test/unit/test_devices.py test/unit/TestIOSDriverKB.py +test/unit/TestIOSDriverKB.py_safe #test/unit/ios/*.conf #test/unit/ios/*.diff test/unit/ios/cleanup.sh diff --git a/napalm_ios/ios.py b/napalm_ios/ios.py index c78f72c..787150b 100755 --- a/napalm_ios/ios.py +++ b/napalm_ios/ios.py @@ -1361,15 +1361,16 @@ def get_mac_address_table(self): RE_MACTABLE_6500_2 = r"^{}\s+{}\s+".format(VLAN_REGEX, MAC_REGEX) # 6 fields RE_MACTABLE_4500 = r"^{}\s+{}\s+".format(VLAN_REGEX, MAC_REGEX) # 5 fields RE_MACTABLE_2960_1 = r"^All\s+{}".format(MAC_REGEX) - RE_MACTABLE_2960_2 = r"^{}\s+{}\s+".format(VLAN_REGEX, MAC_REGEX) # 4 fields + RE_MACTABLE_GEN_1 = r"^{}\s+{}\s+".format(VLAN_REGEX, MAC_REGEX) # 4 fields (2960/4500) def process_mac_fields(vlan, mac, mac_type, interface): """Return proper data for mac address fields.""" - if mac_type.lower() in ['self', 'static']: + if mac_type.lower() in ['self', 'static', 'system']: static = True if vlan.lower() == 'all': vlan = 0 - if interface.lower() == 'cpu' or interface.lower() == 'router': + if interface.lower() == 'cpu' or re.search(r'router', interface.lower()) or \ + re.search(r'switch', interface.lower()): interface = '' else: static = False @@ -1394,7 +1395,6 @@ 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() - output = re.split(r'Multicast Entries.*', output)[0] for line in output.splitlines(): line = line.strip() if line == '': @@ -1414,21 +1414,29 @@ def process_mac_fields(vlan, mac, mac_type, interface): elif len(line.split()) == 6: vlan, mac, mac_type, _, _, interface = line.split() mac_address_table.append(process_mac_fields(vlan, mac, mac_type, interface)) - # Cat4948 format + # Cat4500 format elif re.search(RE_MACTABLE_4500, line) and len(line.split()) == 5: vlan, mac, mac_type, _, interface = line.split() mac_address_table.append(process_mac_fields(vlan, mac, mac_type, interface)) # Cat2960 format - ignore extra header line elif re.search(r"^Vlan\s+Mac Address\s+", line): continue - # Cat2960 format - elif (re.search(RE_MACTABLE_2960_1, line) or re.search(RE_MACTABLE_2960_2, line)) and \ + # Cat2960 format (Cat4500 format multicast entries) + elif (re.search(RE_MACTABLE_2960_1, line) or re.search(RE_MACTABLE_GEN_1, line)) and \ len(line.split()) == 4: vlan, mac, mac_type, interface = line.split() mac_address_table.append(process_mac_fields(vlan, mac, mac_type, interface)) + elif re.search(r"Total Mac Addresses", line): + continue + elif re.search(r"Multicast Entries", line): + continue + elif re.search(r"vlan.*mac.*address.*type.*", line): + continue else: raise ValueError("Unexpected output from: {}".format(repr(line))) + from pprint import pprint as pp + pp(mac_address_table) return mac_address_table def get_snmp_information(self): diff --git a/test/unit/mocked_data/test_get_mac_address_table/3560_format/expected_result.json b/test/unit/mocked_data/test_get_mac_address_table/3560_format/expected_result.json new file mode 100644 index 0000000..87cd0a1 --- /dev/null +++ b/test/unit/mocked_data/test_get_mac_address_table/3560_format/expected_result.json @@ -0,0 +1 @@ +[{"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0100.0ccc.cccc", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0100.0ccc.cccd", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.0000", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.0001", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.0002", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.0003", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.0004", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.0005", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.0006", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.0007", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.0008", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.0009", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.000a", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.000b", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.000c", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.000d", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.000e", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.000f", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "0180.c200.0010", "moves": -1}, {"vlan": 0, "static": true, "interface": "", "last_move": -1.0, "active": false, "mac": "ffff.ffff.ffff", "moves": -1}, {"vlan": 1, "static": false, "interface": "Fa0/16", "last_move": -1.0, "active": true, "mac": "000a.b82d.10e0", "moves": -1}, {"vlan": 1, "static": false, "interface": "Fa0/3", "last_move": -1.0, "active": true, "mac": "0012.80b6.4cd8", "moves": -1}, {"vlan": 1, "static": false, "interface": "Fa0/16", "last_move": -1.0, "active": true, "mac": "0012.80b6.4cd9", "moves": -1}, {"vlan": 1, "static": false, "interface": "Fa0/16", "last_move": -1.0, "active": true, "mac": "0014.6915.4100", "moves": -1}, {"vlan": 1, "static": false, "interface": "Fa0/16", "last_move": -1.0, "active": true, "mac": "0018.b921.9200", "moves": -1}, {"vlan": 1, "static": false, "interface": "Fa0/1", "last_move": -1.0, "active": true, "mac": "0018.b921.9278", "moves": -1}, {"vlan": 1, "static": false, "interface": "Fa0/16", "last_move": -1.0, "active": true, "mac": "0018.b974.528f", "moves": -1}, {"vlan": 1, "static": false, "interface": "Fa0/13", "last_move": -1.0, "active": true, "mac": "0019.0617.660f", "moves": -1}, {"vlan": 1, "static": false, "interface": "Fa0/14", "last_move": -1.0, "active": true, "mac": "0019.0617.6610", "moves": -1}, {"vlan": 1, "static": false, "interface": "Fa0/15", "last_move": -1.0, "active": true, "mac": "0019.0617.6611", "moves": -1}, {"vlan": 1, "static": false, "interface": "Fa0/19", "last_move": -1.0, "active": true, "mac": "001b.d450.970f", "moves": -1}, {"vlan": 1, "static": false, "interface": "Fa0/20", "last_move": -1.0, "active": true, "mac": "001b.d450.9710", "moves": -1}, {"vlan": 1, "static": false, "interface": "Fa0/21", "last_move": -1.0, "active": true, "mac": "001b.d450.9711", "moves": -1}, {"vlan": 4, "static": false, "interface": "Fa0/16", "last_move": -1.0, "active": true, "mac": "0018.b974.528f", "moves": -1}, {"vlan": 45, "static": false, "interface": "Fa0/19", "last_move": -1.0, "active": true, "mac": "0018.b945.d5a9", "moves": -1}, {"vlan": 45, "static": false, "interface": "Fa0/5", "last_move": -1.0, "active": true, "mac": "0018.b945.f780", "moves": -1}, {"vlan": 45, "static": false, "interface": "Fa0/16", "last_move": -1.0, "active": true, "mac": "0018.b974.528f", "moves": -1}, {"vlan": 56, "static": false, "interface": "Fa0/16", "last_move": -1.0, "active": true, "mac": "0018.b945.f781", "moves": -1}, {"vlan": 56, "static": false, "interface": "Fa0/16", "last_move": -1.0, "active": true, "mac": "0018.b974.528f", "moves": -1}, {"vlan": 56, "static": false, "interface": "Fa0/19", "last_move": -1.0, "active": true, "mac": "0019.069c.80e1", "moves": -1}, {"vlan": 6, "static": false, "interface": "Fa0/16", "last_move": -1.0, "active": true, "mac": "0018.b974.528f", "moves": -1}, {"vlan": 6, "static": false, "interface": "Fa0/13", "last_move": -1.0, "active": true, "mac": "0019.069c.80e0", "moves": -1}] diff --git a/test/unit/mocked_data/test_get_mac_address_table/3560_format/show_mac_address_table.txt b/test/unit/mocked_data/test_get_mac_address_table/3560_format/show_mac_address_table.txt new file mode 100644 index 0000000..b6af798 --- /dev/null +++ b/test/unit/mocked_data/test_get_mac_address_table/3560_format/show_mac_address_table.txt @@ -0,0 +1,48 @@ + Mac Address Table +------------------------------------------- + +Vlan Mac Address Type Ports +---- ----------- -------- ----- +All 0100.0ccc.cccc STATIC CPU +All 0100.0ccc.cccd STATIC CPU +All 0180.c200.0000 STATIC CPU +All 0180.c200.0001 STATIC CPU +All 0180.c200.0002 STATIC CPU +All 0180.c200.0003 STATIC CPU +All 0180.c200.0004 STATIC CPU +All 0180.c200.0005 STATIC CPU +All 0180.c200.0006 STATIC CPU +All 0180.c200.0007 STATIC CPU +All 0180.c200.0008 STATIC CPU +All 0180.c200.0009 STATIC CPU +All 0180.c200.000a STATIC CPU +All 0180.c200.000b STATIC CPU +All 0180.c200.000c STATIC CPU +All 0180.c200.000d STATIC CPU +All 0180.c200.000e STATIC CPU +All 0180.c200.000f STATIC CPU +All 0180.c200.0010 STATIC CPU +All ffff.ffff.ffff STATIC CPU + 1 000a.b82d.10e0 DYNAMIC Fa0/16 + 1 0012.80b6.4cd8 DYNAMIC Fa0/3 + 1 0012.80b6.4cd9 DYNAMIC Fa0/16 + 1 0014.6915.4100 DYNAMIC Fa0/16 + 1 0018.b921.9200 DYNAMIC Fa0/16 + 1 0018.b921.9278 DYNAMIC Fa0/1 + 1 0018.b974.528f DYNAMIC Fa0/16 + 1 0019.0617.660f DYNAMIC Fa0/13 + 1 0019.0617.6610 DYNAMIC Fa0/14 + 1 0019.0617.6611 DYNAMIC Fa0/15 + 1 001b.d450.970f DYNAMIC Fa0/19 + 1 001b.d450.9710 DYNAMIC Fa0/20 + 1 001b.d450.9711 DYNAMIC Fa0/21 + 4 0018.b974.528f DYNAMIC Fa0/16 + 45 0018.b945.d5a9 DYNAMIC Fa0/19 + 45 0018.b945.f780 DYNAMIC Fa0/5 + 45 0018.b974.528f DYNAMIC Fa0/16 + 56 0018.b945.f781 DYNAMIC Fa0/16 + 56 0018.b974.528f DYNAMIC Fa0/16 + 56 0019.069c.80e1 DYNAMIC Fa0/19 + 6 0018.b974.528f DYNAMIC Fa0/16 + 6 0019.069c.80e0 DYNAMIC Fa0/13 +Total Mac Addresses for this criterion: 42 diff --git a/test/unit/mocked_data/test_get_mac_address_table/4500_format/expected_result.json b/test/unit/mocked_data/test_get_mac_address_table/4500_format/expected_result.json new file mode 100644 index 0000000..a08408d --- /dev/null +++ b/test/unit/mocked_data/test_get_mac_address_table/4500_format/expected_result.json @@ -0,0 +1 @@ +[{"moves": -1, "interface": "Port-channel1", "vlan": 1, "static": false, "mac": "30a3.30a3.a1c3", "active": true, "last_move": -1.0}, {"moves": -1, "interface": "Port-channel1", "vlan": 99, "static": false, "mac": "30a3.30a3.a1c4", "active": true, "last_move": -1.0}, {"moves": -1, "interface": "Port-channel1", "vlan": 99, "static": false, "mac": "30a3.30a3.a1c5", "active": true, "last_move": -1.0}, {"moves": -1, "interface": "Port-channel1", "vlan": 99, "static": false, "mac": "30a3.30a3.a1c6", "active": true, "last_move": -1.0}, {"moves": -1, "interface": "Port-channel1", "vlan": 99, "static": false, "mac": "30a3.30a3.a1c7", "active": true, "last_move": -1.0}, {"moves": -1, "interface": "Port-channel1", "vlan": 99, "static": false, "mac": "30a3.30a3.a1c8", "active": true, "last_move": -1.0}, {"moves": -1, "interface": "Port-channel1", "vlan": 99, "static": false, "mac": "30a3.30a3.a1c9", "active": true, "last_move": -1.0}, {"moves": -1, "interface": "Port-channel1", "vlan": 99, "static": false, "mac": "30a3.30a3.a1ca", "active": true, "last_move": -1.0}, {"moves": -1, "interface": "Po1", "vlan": 1, "static": true, "mac": "0100.0ccc.ccce", "active": false, "last_move": -1.0}, {"moves": -1, "interface": "Po1", "vlan": 1, "static": true, "mac": "ffff.ffff.ffff", "active": false, "last_move": -1.0}, {"moves": -1, "interface": "", "vlan": 39, "static": true, "mac": "ffff.ffff.ffff", "active": false, "last_move": -1.0}] diff --git a/test/unit/mocked_data/test_get_mac_address_table/alt_format2/show_mac_address_table.txt b/test/unit/mocked_data/test_get_mac_address_table/4500_format/show_mac_address_table.txt similarity index 100% rename from test/unit/mocked_data/test_get_mac_address_table/alt_format2/show_mac_address_table.txt rename to test/unit/mocked_data/test_get_mac_address_table/4500_format/show_mac_address_table.txt diff --git a/test/unit/mocked_data/test_get_mac_address_table/alt_format2/expected_result.json b/test/unit/mocked_data/test_get_mac_address_table/alt_format2/expected_result.json deleted file mode 100644 index 9dcc078..0000000 --- a/test/unit/mocked_data/test_get_mac_address_table/alt_format2/expected_result.json +++ /dev/null @@ -1 +0,0 @@ -[{"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 1, "mac": "30a3.30a3.a1c3", "static": false}, {"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 99, "mac": "30a3.30a3.a1c4", "static": false}, {"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 99, "mac": "30a3.30a3.a1c5", "static": false}, {"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 99, "mac": "30a3.30a3.a1c6", "static": false}, {"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 99, "mac": "30a3.30a3.a1c7", "static": false}, {"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 99, "mac": "30a3.30a3.a1c8", "static": false}, {"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 99, "mac": "30a3.30a3.a1c9", "static": false}, {"active": true, "moves": -1, "interface": "Port-channel1", "last_move": -1.0, "vlan": 99, "mac": "30a3.30a3.a1ca", "static": false}] diff --git a/test/unit/mocked_data/test_get_mac_address_table/alt_format3/expected_result.json b/test/unit/mocked_data/test_get_mac_address_table/alt_format3/expected_result.json deleted file mode 100644 index bef304d..0000000 --- a/test/unit/mocked_data/test_get_mac_address_table/alt_format3/expected_result.json +++ /dev/null @@ -1 +0,0 @@ -[{"interface": "", "vlan": 0, "mac": "0100.0ccc.cccc", "active": false, "moves": -1, "static": true, "last_move": -1.0}, {"interface": "", "vlan": 0, "mac": "0100.0ccc.cccd", "active": false, "moves": -1, "static": true, "last_move": -1.0}, {"interface": "", "vlan": 0, "mac": "0100.0ccc.ccce", "active": false, "moves": -1, "static": true, "last_move": -1.0}, {"interface": "", "vlan": 0, "mac": "0180.c200.0000", "active": false, "moves": -1, "static": true, "last_move": -1.0}, {"interface": "", "vlan": 0, "mac": "0180.c200.0001", "active": false, "moves": -1, "static": true, "last_move": -1.0}, {"interface": "Po1", "vlan": 1, "mac": "30a3.30a3.a1c3", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Po1", "vlan": 1, "mac": "30a3.30a3.a1c4", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Po1", "vlan": 1, "mac": "30a3.30a3.a1c5", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Po1", "vlan": 1, "mac": "30a3.30a3.a1c6", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Te1/1/3", "vlan": 1, "mac": "30a3.30a3.a1c7", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Te1/1/3", "vlan": 99, "mac": "30a3.30a3.a1c8", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Te1/1/3", "vlan": 99, "mac": "30a3.30a3.a1c9", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Te1/1/3", "vlan": 99, "mac": "30a3.30a3.a1ca", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Te1/1/3", "vlan": 99, "mac": "30a3.30a3.a1cb", "active": true, "moves": -1, "static": false, "last_move": -1.0}, {"interface": "Vl99", "vlan": 99, "mac": "30a3.30a3.a1cc", "active": false, "moves": -1, "static": true, "last_move": -1.0}, {"interface": "Po6", "vlan": 99, "mac": "30a3.30a3.a1cd", "active": true, "moves": -1, "static": false, "last_move": -1.0}] diff --git a/test/unit/mocked_data/test_get_mac_address_table/alt_format3/show_mac_address_table.txt b/test/unit/mocked_data/test_get_mac_address_table/alt_format3/show_mac_address_table.txt deleted file mode 100644 index 4dc926e..0000000 --- a/test/unit/mocked_data/test_get_mac_address_table/alt_format3/show_mac_address_table.txt +++ /dev/null @@ -1,18 +0,0 @@ -Vlan Mac Address Type Ports ----- ----------- -------- ----- - All 0100.0ccc.cccc STATIC CPU - All 0100.0ccc.cccd STATIC CPU - All 0100.0ccc.ccce STATIC CPU - All 0180.c200.0000 STATIC CPU - All 0180.c200.0001 STATIC CPU - 1 30a3.30a3.a1c3 DYNAMIC Po1 - 1 30a3.30a3.a1c4 DYNAMIC Po1 - 1 30a3.30a3.a1c5 DYNAMIC Po1 - 1 30a3.30a3.a1c6 DYNAMIC Po1 - 1 30a3.30a3.a1c7 DYNAMIC Te1/1/3 - 99 30a3.30a3.a1c8 DYNAMIC Te1/1/3 - 99 30a3.30a3.a1c9 DYNAMIC Te1/1/3 - 99 30a3.30a3.a1ca DYNAMIC Te1/1/3 - 99 30a3.30a3.a1cb DYNAMIC Te1/1/3 - 99 30a3.30a3.a1cc STATIC Vl99 - 99 30a3.30a3.a1cd DYNAMIC Po6 diff --git a/test/unit/mocked_data/test_get_mac_address_table/expected_result.json b/test/unit/mocked_data/test_get_mac_address_table/expected_result.json deleted file mode 100644 index e69de29..0000000 diff --git a/test/unit/mocked_data/test_get_mac_address_table/show_mac_address_table.txt b/test/unit/mocked_data/test_get_mac_address_table/show_mac_address_table.txt deleted file mode 100644 index 4dc926e..0000000 --- a/test/unit/mocked_data/test_get_mac_address_table/show_mac_address_table.txt +++ /dev/null @@ -1,18 +0,0 @@ -Vlan Mac Address Type Ports ----- ----------- -------- ----- - All 0100.0ccc.cccc STATIC CPU - All 0100.0ccc.cccd STATIC CPU - All 0100.0ccc.ccce STATIC CPU - All 0180.c200.0000 STATIC CPU - All 0180.c200.0001 STATIC CPU - 1 30a3.30a3.a1c3 DYNAMIC Po1 - 1 30a3.30a3.a1c4 DYNAMIC Po1 - 1 30a3.30a3.a1c5 DYNAMIC Po1 - 1 30a3.30a3.a1c6 DYNAMIC Po1 - 1 30a3.30a3.a1c7 DYNAMIC Te1/1/3 - 99 30a3.30a3.a1c8 DYNAMIC Te1/1/3 - 99 30a3.30a3.a1c9 DYNAMIC Te1/1/3 - 99 30a3.30a3.a1ca DYNAMIC Te1/1/3 - 99 30a3.30a3.a1cb DYNAMIC Te1/1/3 - 99 30a3.30a3.a1cc STATIC Vl99 - 99 30a3.30a3.a1cd DYNAMIC Po6 From bf5558fd8263734e93e7ea67cfe5e9a420d617ed Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 4 Jan 2017 21:08:14 -0800 Subject: [PATCH 05/20] Improving get_mac_address_table --- napalm_ios/ios.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/napalm_ios/ios.py b/napalm_ios/ios.py index 787150b..e84f2ba 100755 --- a/napalm_ios/ios.py +++ b/napalm_ios/ios.py @@ -1395,12 +1395,18 @@ 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) for line in output.splitlines(): line = line.strip() if line == '': continue + if re.search(r"^---", line): + # Convert any '---' to VLAN 0 + line = re.sub(r"^---", "0", line, flags=re.M) + # Format1 - elif re.search(RE_MACTABLE_DEFAULT, line): + if re.search(RE_MACTABLE_DEFAULT, line): if len(line.split()) == 4: mac, mac_type, vlan, interface = line.split() mac_address_table.append(process_mac_fields(vlan, mac, mac_type, interface)) @@ -1435,8 +1441,6 @@ def process_mac_fields(vlan, mac, mac_type, interface): else: raise ValueError("Unexpected output from: {}".format(repr(line))) - from pprint import pprint as pp - pp(mac_address_table) return mac_address_table def get_snmp_information(self): From 80ceec7862dc3f5aa77a1beefc515188007c092b Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 4 Jan 2017 21:09:00 -0800 Subject: [PATCH 06/20] Improving get_mac_address_table tests --- .../test_get_mac_address_table/6500_format2/expected_result.json | 1 + .../{alt_format1 => 6500_format2}/show_mac_address_table.txt | 1 + .../test_get_mac_address_table/alt_format1/expected_result.json | 1 - 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 test/unit/mocked_data/test_get_mac_address_table/6500_format2/expected_result.json rename test/unit/mocked_data/test_get_mac_address_table/{alt_format1 => 6500_format2}/show_mac_address_table.txt (92%) delete mode 100644 test/unit/mocked_data/test_get_mac_address_table/alt_format1/expected_result.json diff --git a/test/unit/mocked_data/test_get_mac_address_table/6500_format2/expected_result.json b/test/unit/mocked_data/test_get_mac_address_table/6500_format2/expected_result.json new file mode 100644 index 0000000..9be13e7 --- /dev/null +++ b/test/unit/mocked_data/test_get_mac_address_table/6500_format2/expected_result.json @@ -0,0 +1 @@ +[{"last_move": -1.0, "vlan": 666, "moves": -1, "mac": "30a3.30a3.a1c3", "static": false, "interface": "Te1/30", "active": true}, {"last_move": -1.0, "vlan": 666, "moves": -1, "mac": "30a3.30a3.5ab8", "static": false, "interface": "Po3", "active": true}, {"last_move": -1.0, "vlan": 60, "moves": -1, "mac": "30a3.30a3.4d54", "static": false, "interface": "Te1/21", "active": true}, {"last_move": -1.0, "vlan": 777, "moves": -1, "mac": "0000.30a3.0167", "static": true, "interface": "", "active": false}, {"last_move": -1.0, "vlan": 664, "moves": -1, "mac": "30a3.30a3.58b5", "static": false, "interface": "Po6", "active": true}, {"last_move": -1.0, "vlan": 667, "moves": -1, "mac": "30a3.30a3.daf5", "static": false, "interface": "Te3/20", "active": true}, {"last_move": -1.0, "vlan": 668, "moves": -1, "mac": "30a3.30a3.e401", "static": false, "interface": "Po6", "active": true}, {"last_move": -1.0, "vlan": 669, "moves": -1, "mac": "30a3.30a3.5a22", "static": false, "interface": "Te3/20", "active": true}, {"last_move": -1.0, "vlan": 0, "moves": -1, "mac": "0000.0000.0000", "static": true, "interface": "", "active": false}] diff --git a/test/unit/mocked_data/test_get_mac_address_table/alt_format1/show_mac_address_table.txt b/test/unit/mocked_data/test_get_mac_address_table/6500_format2/show_mac_address_table.txt similarity index 92% rename from test/unit/mocked_data/test_get_mac_address_table/alt_format1/show_mac_address_table.txt rename to test/unit/mocked_data/test_get_mac_address_table/6500_format2/show_mac_address_table.txt index d2d3498..e86a9a7 100644 --- a/test/unit/mocked_data/test_get_mac_address_table/alt_format1/show_mac_address_table.txt +++ b/test/unit/mocked_data/test_get_mac_address_table/6500_format2/show_mac_address_table.txt @@ -12,3 +12,4 @@ Legend: * - primary entry 667 30a3.30a3.daf5 dynamic Yes 0 Te3/20 668 30a3.30a3.e401 dynamic Yes 600 Po6 669 30a3.30a3.5a22 dynamic Yes 300 Te3/20 +* --- 0000.0000.0000 static No - Router diff --git a/test/unit/mocked_data/test_get_mac_address_table/alt_format1/expected_result.json b/test/unit/mocked_data/test_get_mac_address_table/alt_format1/expected_result.json deleted file mode 100644 index 1d383e9..0000000 --- a/test/unit/mocked_data/test_get_mac_address_table/alt_format1/expected_result.json +++ /dev/null @@ -1 +0,0 @@ -[{"interface": "Te1/30", "vlan": 666, "moves": -1, "static": false, "active": true, "mac": "30a3.30a3.a1c3", "last_move": -1.0}, {"interface": "Po3", "vlan": 666, "moves": -1, "static": false, "active": true, "mac": "30a3.30a3.5ab8", "last_move": -1.0}, {"interface": "Te1/21", "vlan": 60, "moves": -1, "static": false, "active": true, "mac": "30a3.30a3.4d54", "last_move": -1.0}, {"interface": "", "vlan": 777, "moves": -1, "static": true, "active": false, "mac": "0000.30a3.0167", "last_move": -1.0}, {"interface": "Po6", "vlan": 664, "moves": -1, "static": false, "active": true, "mac": "30a3.30a3.58b5", "last_move": -1.0}, {"interface": "Te3/20", "vlan": 667, "moves": -1, "static": false, "active": true, "mac": "30a3.30a3.daf5", "last_move": -1.0}, {"interface": "Po6", "vlan": 668, "moves": -1, "static": false, "active": true, "mac": "30a3.30a3.e401", "last_move": -1.0}, {"interface": "Te3/20", "vlan": 669, "moves": -1, "static": false, "active": true, "mac": "30a3.30a3.5a22", "last_move": -1.0}] From 764bc78c45513da1f6b8aebade26dad436789473 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Thu, 5 Jan 2017 15:36:11 -0800 Subject: [PATCH 07/20] Improvments to merge operation --- napalm_ios/ios.py | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/napalm_ios/ios.py b/napalm_ios/ios.py index e84f2ba..5f44a0b 100755 --- a/napalm_ios/ios.py +++ b/napalm_ios/ios.py @@ -195,17 +195,41 @@ def _normalize_compare_config(diff): @staticmethod def _normalize_merge_diff(diff): - """Make compare_config() for merge look similar to replace config diff.""" + """Make compare_config() for merge look similar to replace config diff. + + Cisco IOS incremental-diff output + + No changes: + !List of Commands: + end + !No changes were found + """ new_diff = [] + changes_found = False for line in diff.splitlines(): + if re.search(r'order-dependent line.*re-ordered', line): + changes_found = True + elif 'No changes were found' in line: + # IOS in the re-order case still claims "No changes were found" + if not changes_found: + return '' + else: + continue + + if line.strip() == 'end': + continue + elif 'List of Commands' in line: + continue # Filter blank lines and prepend +sign - if line.strip(): - new_diff.append('+' + line) - if new_diff: - new_diff.insert(0, '! Cisco IOS does not support true compare_config() for merge: ' - 'echo merge file.') - else: - new_diff.append('! No changes specified in merge file.') + elif line.strip(): + if re.search(r"^no\s+", line.strip()): + new_diff.append('-' + line) + else: + new_diff.append('+' + line) + #if new_diff: + # new_diff.insert(0, '! incremental-diff failed; falling back to showing merge file') + #else: + # new_diff.append('! No changes specified in merge file.') return "\n".join(new_diff) def compare_config(self): @@ -231,8 +255,10 @@ def compare_config(self): diff = self.device.send_command_expect(cmd) diff = self._normalize_compare_config(diff) else: - cmd = 'more {}'.format(new_file_full) + cmd = 'show archive config incremental-diffs {} ignorecase'.format(new_file_full) diff = self.device.send_command_expect(cmd) + if '% Invalid' in diff: + cmd = 'more {}'.format(new_file_full) diff = self._normalize_merge_diff(diff) return diff.strip() From 1c0d8baa5078c90647faf827d9f45dce7113bc15 Mon Sep 17 00:00:00 2001 From: Ken Celenza Date: Fri, 6 Jan 2017 10:37:30 -0500 Subject: [PATCH 08/20] Update lldp detail regex Port description can have ' -' as delimiter instead of ':' The findall between IP and other should happen in one check, otherwise it doesn't return both at the same time, when you have a mix. Test Data ``` csr1#show lldp neighbors GigabitEthernet 1 detail ------------------------------------------------ Local Intf: Gi1 Chassis id: 2cc2.603e.363b Port id: Management1 Port Description - not advertised System Name: eos-spine1.ntc.com System Description: Arista Networks EOS version 4.15.2F running on an Arista Networks vEOS Time remaining: 95 seconds System Capabilities: B,R Enabled Capabilities: B,R Management Addresses: Other: 2C FF 60 3E 36 3B 00 Auto Negotiation - not supported Physical media capabilities - not advertised Media Attachment Unit type - not advertised Vlan ID: - not advertised ------------------------------------------------ Local Intf: Gi1 Chassis id: 0005.8671.58c0 Port id: fxp0 Port Description: fxp0 System Name: vmx1 System Description: Juniper Networks, Inc. vmx internet router, kernel JUNOS 15.1F4.15, Build date: 2015-12-23 19:22:55 UTC Copyright (c) 1996-2015 Juniper Networks, Inc. Time remaining: 116 seconds System Capabilities: B,R Enabled Capabilities: B,R Management Addresses: IP: 10.0.0.31 OID: 0.1.3.6.1.2.1.31.1.1.1.1.1. Auto Negotiation - supported, disabled Physical media capabilities: 1000baseT(FD) 1000baseX(FD) 1000baseX(HD) Symm, Asym Pause(FD) 100base-TX(FD) 100base-TX(HD) 10base-T(FD) 10base-T(HD) Media Attachment Unit type - not advertised Vlan ID: - not advertised Total entries displayed: 2 csr1# ``` --- napalm_ios/ios.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/napalm_ios/ios.py b/napalm_ios/ios.py index e84f2ba..2b0c1eb 100755 --- a/napalm_ios/ios.py +++ b/napalm_ios/ios.py @@ -486,15 +486,13 @@ def get_lldp_neighbors_detail(self, interface=''): local_port = interface port_id = re.findall(r"Port id: (.+)", output) - port_description = re.findall(r"Port Description: (.+)", output) + port_description = re.findall(r"Port Description(?:\s\-|:) (.+)", output) chassis_id = re.findall(r"Chassis id: (.+)", output) system_name = re.findall(r"System Name: (.+)", output) system_description = re.findall(r"System Description: \n(.+)", output) system_capabilities = re.findall(r"System Capabilities: (.+)", output) enabled_capabilities = re.findall(r"Enabled Capabilities: (.+)", output) - remote_address = re.findall(r"Management Addresses:\n IP: (.+)", output) - if not remote_address: - remote_address = re.findall(r"Management Addresses:\n Other: (.+)", output) + remote_address = re.findall(r"Management Addresses:\n (?:IP:|Other:) (.+)", output) number_entries = len(port_id) lldp_fields = [port_id, port_description, chassis_id, system_name, system_description, system_capabilities, enabled_capabilities, remote_address] From 1b6852992dfc39c8e32d9eef9b3f56af3f2580f6 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Sat, 14 Jan 2017 10:48:18 -0800 Subject: [PATCH 09/20] IOS incremental diff support for merge --- napalm_ios/ios.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/napalm_ios/ios.py b/napalm_ios/ios.py index 5f44a0b..c19b33c 100755 --- a/napalm_ios/ios.py +++ b/napalm_ios/ios.py @@ -194,8 +194,8 @@ def _normalize_compare_config(diff): return "\n".join(new_list) @staticmethod - def _normalize_merge_diff(diff): - """Make compare_config() for merge look similar to replace config diff. + def _normalize_merge_diff_incr(diff): + """Make the compare config output look better. Cisco IOS incremental-diff output @@ -205,6 +205,7 @@ def _normalize_merge_diff(diff): !No changes were found """ new_diff = [] + changes_found = False for line in diff.splitlines(): if re.search(r'order-dependent line.*re-ordered', line): @@ -226,10 +227,20 @@ def _normalize_merge_diff(diff): new_diff.append('-' + line) else: new_diff.append('+' + line) - #if new_diff: - # new_diff.insert(0, '! incremental-diff failed; falling back to showing merge file') - #else: - # new_diff.append('! No changes specified in merge file.') + return "\n".join(new_diff) + + @staticmethod + def _normalize_merge_diff(diff): + """Make compare_config() for merge look similar to replace config diff.""" + new_diff = [] + for line in diff.splitlines(): + # Filter blank lines and prepend +sign + if line.strip(): + new_diff.append('+' + line) + if new_diff: + new_diff.insert(0, '! incremental-diff failed; falling back to echo of merge file') + else: + new_diff.append('! No changes specified in merge file.') return "\n".join(new_diff) def compare_config(self): @@ -255,11 +266,16 @@ def compare_config(self): diff = self.device.send_command_expect(cmd) diff = self._normalize_compare_config(diff) else: + # merge cmd = 'show archive config incremental-diffs {} ignorecase'.format(new_file_full) diff = self.device.send_command_expect(cmd) - if '% Invalid' in diff: + if '% Invalid' not in diff: + diff = self._normalize_merge_diff_incr(diff) + else: cmd = 'more {}'.format(new_file_full) - diff = self._normalize_merge_diff(diff) + diff = self.device.send_command_expect(cmd) + diff = self._normalize_merge_diff(diff) + return diff.strip() def _commit_hostname_handler(self, cmd): @@ -1467,6 +1483,7 @@ def process_mac_fields(vlan, mac, mac_type, interface): else: raise ValueError("Unexpected output from: {}".format(repr(line))) + print(mac_address_table) return mac_address_table def get_snmp_information(self): From 22d90dda566591e1e3d450a527628013ee3548ad Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Sat, 14 Jan 2017 10:48:43 -0800 Subject: [PATCH 10/20] Require new version of Netmiko and napalm-base --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 393ca39..baf5e8b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -napalm_base>=0.20.4 -netmiko>=1.0.0 +napalm_base>=0.21.0 +netmiko>=1.2.5 From 4dfefec0d5a7da796eaab9f1d096eb1637270f53 Mon Sep 17 00:00:00 2001 From: itdependsnetworks Date: Sun, 15 Jan 2017 23:47:56 -0500 Subject: [PATCH 11/20] Add testing and \s+ --- napalm_ios/ios.py | 17 +++--- .../alternate/expected_result.json | 24 +++++++++ .../alternate/show_int_Gi1.txt | 28 ++++++++++ .../alternate/show_lldp_neighbors.txt | 10 ++++ ...lldp_neighbors_GigabitEthernet1_detail.txt | 53 +++++++++++++++++++ 5 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/expected_result.json create mode 100644 test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/show_int_Gi1.txt create mode 100644 test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/show_lldp_neighbors.txt create mode 100644 test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/show_lldp_neighbors_GigabitEthernet1_detail.txt diff --git a/napalm_ios/ios.py b/napalm_ios/ios.py index 2b0c1eb..d30afa7 100755 --- a/napalm_ios/ios.py +++ b/napalm_ios/ios.py @@ -485,14 +485,15 @@ def get_lldp_neighbors_detail(self, interface=''): return {} local_port = interface - port_id = re.findall(r"Port id: (.+)", output) - port_description = re.findall(r"Port Description(?:\s\-|:) (.+)", output) - chassis_id = re.findall(r"Chassis id: (.+)", output) - system_name = re.findall(r"System Name: (.+)", output) - system_description = re.findall(r"System Description: \n(.+)", output) - system_capabilities = re.findall(r"System Capabilities: (.+)", output) - enabled_capabilities = re.findall(r"Enabled Capabilities: (.+)", output) - remote_address = re.findall(r"Management Addresses:\n (?:IP:|Other:) (.+)", output) + port_id = re.findall(r"Port id:\s+(.+)", output) + port_description = re.findall(r"Port Description(?:\s\-|:)\s+(.+)", output) + chassis_id = re.findall(r"Chassis id:\s+(.+)", output) + system_name = re.findall(r"System Name:\s+(.+)", output) + system_description = re.findall(r"System Description:\s*\n(.+)", output) + system_capabilities = re.findall(r"System Capabilities:\s+(.+)", output) + enabled_capabilities = re.findall(r"Enabled Capabilities:\s+(.+)", output) + remote_address = re.findall(r"Management Addresses:\n\s+(?:IP|Other)(?::\s+?)(.+)", + output) number_entries = len(port_id) lldp_fields = [port_id, port_description, chassis_id, system_name, system_description, system_capabilities, enabled_capabilities, remote_address] diff --git a/test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/expected_result.json b/test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/expected_result.json new file mode 100644 index 0000000..11b7563 --- /dev/null +++ b/test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/expected_result.json @@ -0,0 +1,24 @@ +{ + "GigabitEthernet1": [ + { + "parent_interface": "N/A", + "remote_chassis_id": "2cc2.603e.363b", + "remote_port": "Management1", + "remote_port_description": "not advertised", + "remote_system_capab": "B,R", + "remote_system_description": "Arista Networks EOS version 4.15.2F running on an Arista Networks vEOS", + "remote_system_enable_capab": "B,R", + "remote_system_name": "eos-spine1.ntc.com" + }, + { + "parent_interface": "N/A", + "remote_chassis_id": "0005.8671.58c0", + "remote_port": "fxp0", + "remote_port_description": "fxp0", + "remote_system_capab": "B,R", + "remote_system_description": "Juniper Networks, Inc. vmx internet router, kernel JUNOS 15.1F4.15, Build date: 2015-12-23 19:22:55 UTC Copyright (c) 1996-2015 Juniper Networks, Inc.", + "remote_system_enable_capab": "B,R", + "remote_system_name": "vmx1" + } + ] +} diff --git a/test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/show_int_Gi1.txt b/test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/show_int_Gi1.txt new file mode 100644 index 0000000..4d4e9c2 --- /dev/null +++ b/test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/show_int_Gi1.txt @@ -0,0 +1,28 @@ +GigabitEthernet1 is up, line protocol is up + Hardware is CSR vNIC, address is 2cc2.603f.437a (bia 2cc2.603f.437a) + Internet address is 10.0.0.51/24 + MTU 1500 bytes, BW 1000000 Kbit/sec, DLY 10 usec, + reliability 198/255, txload 1/255, rxload 1/255 + Encapsulation ARPA, loopback not set + Keepalive set (10 sec) + Full Duplex, 1000Mbps, link type is auto, media type is RJ45 + output flow-control is unsupported, input flow-control is unsupported + ARP type: ARPA, ARP Timeout 04:00:00 + Last input 00:00:07, output 00:00:18, output hang never + Last clearing of "show interface" counters never + Input queue: 0/375/0/0 (size/max/drops/flushes); Total output drops: 0 + Queueing strategy: fifo + Output queue: 0/40 (size/max) + 5 minute input rate 1000 bits/sec, 1 packets/sec + 5 minute output rate 0 bits/sec, 0 packets/sec + 3815699 packets input, 275467675 bytes, 0 no buffer + Received 0 broadcasts (0 IP multicasts) + 0 runts, 0 giants, 0 throttles + 2610907 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored + 0 watchdog, 0 multicast, 0 pause input + 5249863 packets output, 384472234 bytes, 0 underruns + 0 output errors, 0 collisions, 0 interface resets + 11192 unknown protocol drops + 0 babbles, 0 late collision, 0 deferred + 0 lost carrier, 0 no carrier, 0 pause output + 0 output buffer failures, 0 output buffers swapped out diff --git a/test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/show_lldp_neighbors.txt b/test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/show_lldp_neighbors.txt new file mode 100644 index 0000000..1ad0449 --- /dev/null +++ b/test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/show_lldp_neighbors.txt @@ -0,0 +1,10 @@ +Capability codes: + (R) Router, (B) Bridge, (T) Telephone, (C) DOCSIS Cable Device + (W) WLAN Access Point, (P) Repeater, (S) Station, (O) Other + +Device ID Local Intf Hold-time Capability Port ID +eos-spine1.ntc.com Gi1 120 B,R Management1 +vmx1 Gi1 120 B,R fxp0 + +Total entries displayed: 2 + diff --git a/test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/show_lldp_neighbors_GigabitEthernet1_detail.txt b/test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/show_lldp_neighbors_GigabitEthernet1_detail.txt new file mode 100644 index 0000000..daa224d --- /dev/null +++ b/test/unit/mocked_data/test_get_lldp_neighbors_detail/alternate/show_lldp_neighbors_GigabitEthernet1_detail.txt @@ -0,0 +1,53 @@ +------------------------------------------------ +Local Intf: Gi1 +Chassis id: 2cc2.603e.363b +Port id: Management1 +Port Description - not advertised +System Name: eos-spine1.ntc.com + +System Description: +Arista Networks EOS version 4.15.2F running on an Arista Networks vEOS + +Time remaining: 95 seconds +System Capabilities: B,R +Enabled Capabilities: B,R +Management Addresses: + Other: 2C FF 60 3E 36 3B 00 +Auto Negotiation - not supported +Physical media capabilities - not advertised +Media Attachment Unit type - not advertised +Vlan ID: - not advertised + +------------------------------------------------ +Local Intf: Gi1 +Chassis id: 0005.8671.58c0 +Port id: fxp0 +Port Description: fxp0 +System Name: vmx1 + +System Description: +Juniper Networks, Inc. vmx internet router, kernel JUNOS 15.1F4.15, Build date: 2015-12-23 19:22:55 UTC Copyright (c) 1996-2015 Juniper Networks, Inc. + +Time remaining: 116 seconds +System Capabilities: B,R +Enabled Capabilities: B,R +Management Addresses: + IP: 10.0.0.31 + OID: + 0.1.3.6.1.2.1.31.1.1.1.1.1. +Auto Negotiation - supported, disabled +Physical media capabilities: + 1000baseT(FD) + 1000baseX(FD) + 1000baseX(HD) + Symm, Asym Pause(FD) + 100base-TX(FD) + 100base-TX(HD) + 10base-T(FD) + 10base-T(HD) +Media Attachment Unit type - not advertised +Vlan ID: - not advertised + + +Total entries displayed: 2 + From da86d5390a15ba94fc60d866efbdf77f7183a995 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Mon, 16 Jan 2017 11:02:37 -0800 Subject: [PATCH 12/20] Rewrite merge and replace operations to support Netmiko InLineTransfer and to support config argument --- napalm_ios/ios.py | 136 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 104 insertions(+), 32 deletions(-) diff --git a/napalm_ios/ios.py b/napalm_ios/ios.py index c19b33c..6ac375b 100755 --- a/napalm_ios/ios.py +++ b/napalm_ios/ios.py @@ -17,8 +17,10 @@ from __future__ import unicode_literals import re +import os +import uuid -from netmiko import ConnectHandler, FileTransfer +from netmiko import ConnectHandler, FileTransfer, InLineTransfer from netmiko import __version__ as netmiko_version from napalm_base.base import NetworkDriver from napalm_base.exceptions import ReplaceConfigException, MergeConfigException @@ -60,6 +62,7 @@ def __init__(self, hostname, username, password, timeout=60, optional_args=None) self.candidate_cfg = optional_args.get('candidate_cfg', 'candidate_config.txt') self.merge_cfg = optional_args.get('merge_cfg', 'merge_config.txt') self.rollback_cfg = optional_args.get('rollback_cfg', 'rollback_config.txt') + self.inline_transfer = optionals_args.get('inline_transfer', False) # None will cause autodetection of dest_file_system self.dest_file_system = optional_args.get('dest_file_system', None) @@ -142,6 +145,45 @@ def is_alive(self): 'is_alive': self.device.remote_conn.transport.is_active() } + def _scp_tmp_file(self, config): + """Write temp file and for use with inline config and SCP.""" + tmp_dir = '/tmp/' + rand_fname = py23_compat.text_type(uuid.uuid4()) + filename = os.path.join(tmp_dir, rand_fname) + with open(filename, 'wt') as fobj: + fobj.write(config) + (return_status, msg) = self._scp_file(source_file=filename, + dest_file=self.merge_cfg, + file_system=self.dest_file_system) + # removing the temp file + os.remove(filename) + return (return_status, msg) + + def _load_candidate_wrapper(self, source_file=None, source_config=None, dest_file=None, + file_system=None): + """ + Transfer file to remote device for either merge or replace operations + + Returns (return_status, msg) + """ + return_status = False + msg = '' + if source_file and source_config: + raise ValueError("Cannot simultaneously set source_file and source_config") + if config: + if self.inline_transfer: + (return_status, msg) = self._inline_tcl_xfer(source_config=source_config, dest_file=dest_file, + file_system=file_system) + else: + (return_status, msg) = self._scp_tmp_file(self, config) + if filename: + (return_status, msg) = self._scp_file(source_file=filename, dest_file=self.candidate_cfg, + file_system=self.dest_file_system) + if not return_status: + if msg == '': + msg = "Transfer to remote device failed" + return (return_status, msg) + def load_replace_candidate(self, filename=None, config=None): """ SCP file to device filesystem, defaults to candidate_config. @@ -149,16 +191,12 @@ def load_replace_candidate(self, filename=None, config=None): Return None or raise exception """ self.config_replace = True - if config: - raise NotImplementedError - if filename: - (return_status, msg) = self._scp_file(source_file=filename, - dest_file=self.candidate_cfg, - file_system=self.dest_file_system) - if not return_status: - if msg == '': - msg = "SCP transfer to remote device failed" - raise ReplaceConfigException(msg) + return_status, msg = self._load_candidate_wrapper(source_file=filename, + source_config=config + dest_file=self.candidate_cfg, + file_system=self.dest_file_system) + if not return_status: + raise ReplaceConfigException(msg) def load_merge_candidate(self, filename=None, config=None): """ @@ -167,16 +205,12 @@ def load_merge_candidate(self, filename=None, config=None): Merge configuration in: copy running-config """ self.config_replace = False - if config: - raise NotImplementedError - if filename: - (return_status, msg) = self._scp_file(source_file=filename, - dest_file=self.merge_cfg, - file_system=self.dest_file_system) - if not return_status: - if msg == '': - msg = "SCP transfer to remote device failed" - raise MergeConfigException(msg) + return_status, msg = self._load_candidate_wrapper(source_file=filename, + source_config=config + dest_file=self.merge_cfg, + file_system=self.dest_file_system) + if not return_status: + raise MergeConfigException(msg) @staticmethod def _normalize_compare_config(diff): @@ -352,6 +386,23 @@ def rollback(self): cmd = 'configure replace {} force'.format(cfg_file) self.device.send_command_expect(cmd) + def _inline_tcl_xfer(self, source_file=None, source_config=None, dest_file=None, + file_system=None): + """ + Use Netmiko InlineFileTransfer (TCL) to transfer file or config to remote device. + + Return (status, msg) + status = boolean + msg = details on what happened + """ + if source_file: + return self._xfer_file(source_file=source_file, dest_file=dest_file, + file_system=file_system, TransferClass=InLineTransfer) + if source_config: + return self._xfer_file(source_config=source_config, dest_file=dest_file, + file_system=file_system, TransferClass=InLineTransfer) + raise ValueError("File source not specified for transfer.") + def _scp_file(self, source_file, dest_file, file_system): """ SCP file to remote device. @@ -360,30 +411,51 @@ def _scp_file(self, source_file, dest_file, file_system): status = boolean msg = details on what happened """ - # Will automaticall enable SCP on remote device - enable_scp = True + return self._xfer_file(source_file=source_file, dest_file=dest_file, + file_system=file_system, TransferClass=FileTransfer) - with FileTransfer(self.device, - source_file=source_file, - dest_file=dest_file, - file_system=file_system) as scp_transfer: + def _xfer_file(self, source_file=None, source_config=None, dest_file=None, file_system=None, + TransferClass=FileTransfer): + """Transfer file to remote device. + + By default, this will use Secure Copy if self.inline_transfer is set, then will use + Netmiko InlineTransfer method to transfer inline using either SSH or telnet (plus TCL + onbox). + + Return (status, msg) + status = boolean + msg = details on what happened + """ + if not source_file and not source_config: + raise ValueError("File source not specified for transfer.") + if not dest_file or not file_system: + raise ValueError("Destination file or file system not specified.") + + enable_scp = True + if not self.inline_transfer: + enable_scp = False + with TransferClass(self.device, + source_file=source_file, + dest_file=dest_file, + direction='put', + file_system=file_system) as transfer: # Check if file already exists and has correct MD5 - if scp_transfer.check_file_exists() and scp_transfer.compare_md5(): + if transfer.check_file_exists() and transfer.compare_md5(): msg = "File already exists and has correct MD5: no SCP needed" return (True, msg) - if not scp_transfer.verify_space_available(): + if not transfer.verify_space_available(): msg = "Insufficient space available on remote device" return (False, msg) if enable_scp: - scp_transfer.enable_scp() + transfer.enable_scp() # Transfer file - scp_transfer.transfer_file() + transfer.transfer_file() # Compares MD5 between local-remote files - if scp_transfer.verify_file(): + if transfer.verify_file(): msg = "File successfully transferred to remote device" return (True, msg) else: From 4a00f3cd4b86c42715e6b262dbabb8091a2476ea Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Mon, 16 Jan 2017 11:13:57 -0800 Subject: [PATCH 13/20] Fixing bugs with merge and replace operations --- napalm_ios/ios.py | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/napalm_ios/ios.py b/napalm_ios/ios.py index 6ac375b..3ccb8fb 100755 --- a/napalm_ios/ios.py +++ b/napalm_ios/ios.py @@ -62,7 +62,7 @@ def __init__(self, hostname, username, password, timeout=60, optional_args=None) self.candidate_cfg = optional_args.get('candidate_cfg', 'candidate_config.txt') self.merge_cfg = optional_args.get('merge_cfg', 'merge_config.txt') self.rollback_cfg = optional_args.get('rollback_cfg', 'rollback_config.txt') - self.inline_transfer = optionals_args.get('inline_transfer', False) + self.inline_transfer = optional_args.get('inline_transfer', False) # None will cause autodetection of dest_file_system self.dest_file_system = optional_args.get('dest_file_system', None) @@ -145,19 +145,15 @@ def is_alive(self): 'is_alive': self.device.remote_conn.transport.is_active() } - def _scp_tmp_file(self, config): + @staticmethod + def _create_tmp_file(config): """Write temp file and for use with inline config and SCP.""" tmp_dir = '/tmp/' rand_fname = py23_compat.text_type(uuid.uuid4()) filename = os.path.join(tmp_dir, rand_fname) with open(filename, 'wt') as fobj: fobj.write(config) - (return_status, msg) = self._scp_file(source_file=filename, - dest_file=self.merge_cfg, - file_system=self.dest_file_system) - # removing the temp file - os.remove(filename) - return (return_status, msg) + return filename def _load_candidate_wrapper(self, source_file=None, source_config=None, dest_file=None, file_system=None): @@ -170,15 +166,22 @@ def _load_candidate_wrapper(self, source_file=None, source_config=None, dest_fil msg = '' if source_file and source_config: raise ValueError("Cannot simultaneously set source_file and source_config") - if config: + if source_config: if self.inline_transfer: - (return_status, msg) = self._inline_tcl_xfer(source_config=source_config, dest_file=dest_file, + (return_status, msg) = self._inline_tcl_xfer(source_config=source_config, + dest_file=dest_file, file_system=file_system) else: - (return_status, msg) = self._scp_tmp_file(self, config) - if filename: - (return_status, msg) = self._scp_file(source_file=filename, dest_file=self.candidate_cfg, - file_system=self.dest_file_system) + tmp_file = self._create_tmp_file(source_config) + (return_status, msg) = self._scp_file(source_file=tmp_file, dest_file=dest_file, + file_system=file_system) + if os.path.isfile(tmp_file): + pass + # os.remove(tmp_file) + + if source_file: + (return_status, msg) = self._scp_file(source_file=source_file, dest_file=dest_file, + file_system=file_system) if not return_status: if msg == '': msg = "Transfer to remote device failed" @@ -192,7 +195,7 @@ def load_replace_candidate(self, filename=None, config=None): """ self.config_replace = True return_status, msg = self._load_candidate_wrapper(source_file=filename, - source_config=config + source_config=config, dest_file=self.candidate_cfg, file_system=self.dest_file_system) if not return_status: @@ -206,7 +209,7 @@ def load_merge_candidate(self, filename=None, config=None): """ self.config_replace = False return_status, msg = self._load_candidate_wrapper(source_file=filename, - source_config=config + source_config=config, dest_file=self.merge_cfg, file_system=self.dest_file_system) if not return_status: @@ -398,7 +401,7 @@ def _inline_tcl_xfer(self, source_file=None, source_config=None, dest_file=None, if source_file: return self._xfer_file(source_file=source_file, dest_file=dest_file, file_system=file_system, TransferClass=InLineTransfer) - if source_config: + if source_config: return self._xfer_file(source_config=source_config, dest_file=dest_file, file_system=file_system, TransferClass=InLineTransfer) raise ValueError("File source not specified for transfer.") From 5b7e05283631706894df9590068863da0fa4f5d8 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Tue, 17 Jan 2017 11:55:10 -0800 Subject: [PATCH 14/20] Working on merge operations --- napalm_ios/ios.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/napalm_ios/ios.py b/napalm_ios/ios.py index 3ccb8fb..4a2c18a 100755 --- a/napalm_ios/ios.py +++ b/napalm_ios/ios.py @@ -148,7 +148,7 @@ def is_alive(self): @staticmethod def _create_tmp_file(config): """Write temp file and for use with inline config and SCP.""" - tmp_dir = '/tmp/' + tmp_dir = os.getcwd() rand_fname = py23_compat.text_type(uuid.uuid4()) filename = os.path.join(tmp_dir, rand_fname) with open(filename, 'wt') as fobj: @@ -167,21 +167,26 @@ def _load_candidate_wrapper(self, source_file=None, source_config=None, dest_fil if source_file and source_config: raise ValueError("Cannot simultaneously set source_file and source_config") if source_config: + # Currently always have to create a temp file. + tmp_file = self._create_tmp_file(source_config) if self.inline_transfer: - (return_status, msg) = self._inline_tcl_xfer(source_config=source_config, + (return_status, msg) = self._inline_tcl_xfer(source_file=tmp_file, dest_file=dest_file, file_system=file_system) else: - tmp_file = self._create_tmp_file(source_config) (return_status, msg) = self._scp_file(source_file=tmp_file, dest_file=dest_file, file_system=file_system) - if os.path.isfile(tmp_file): - pass - # os.remove(tmp_file) + if os.path.isfile(tmp_file): + os.remove(tmp_file) if source_file: - (return_status, msg) = self._scp_file(source_file=source_file, dest_file=dest_file, - file_system=file_system) + if self.inline_transfer: + (return_status, msg) = self._inline_tcl_xfer(source_file=source_file, + dest_file=dest_file, + file_system=file_system) + else: + (return_status, msg) = self._scp_file(source_file=source_file, dest_file=dest_file, + file_system=file_system) if not return_status: if msg == '': msg = "Transfer to remote device failed" @@ -435,7 +440,7 @@ def _xfer_file(self, source_file=None, source_config=None, dest_file=None, file_ raise ValueError("Destination file or file system not specified.") enable_scp = True - if not self.inline_transfer: + if self.inline_transfer: enable_scp = False with TransferClass(self.device, source_file=source_file, From b1614d6ef6061dc56e3afad4f1c0d87991c7dcc3 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Tue, 17 Jan 2017 12:04:27 -0800 Subject: [PATCH 15/20] Eliminate some redundant code --- napalm_ios/ios.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/napalm_ios/ios.py b/napalm_ios/ios.py index 4a2c18a..c6c1968 100755 --- a/napalm_ios/ios.py +++ b/napalm_ios/ios.py @@ -166,19 +166,11 @@ def _load_candidate_wrapper(self, source_file=None, source_config=None, dest_fil msg = '' if source_file and source_config: raise ValueError("Cannot simultaneously set source_file and source_config") + tmp_file = "" if source_config: # Currently always have to create a temp file. tmp_file = self._create_tmp_file(source_config) - if self.inline_transfer: - (return_status, msg) = self._inline_tcl_xfer(source_file=tmp_file, - dest_file=dest_file, - file_system=file_system) - else: - (return_status, msg) = self._scp_file(source_file=tmp_file, dest_file=dest_file, - file_system=file_system) - if os.path.isfile(tmp_file): - os.remove(tmp_file) - + source_file = tmp_file if source_file: if self.inline_transfer: (return_status, msg) = self._inline_tcl_xfer(source_file=source_file, @@ -187,6 +179,8 @@ def _load_candidate_wrapper(self, source_file=None, source_config=None, dest_fil else: (return_status, msg) = self._scp_file(source_file=source_file, dest_file=dest_file, file_system=file_system) + if tmp_file and os.path.isfile(tmp_file): + os.remove(tmp_file) if not return_status: if msg == '': msg = "Transfer to remote device failed" From 70b04bed5c68f52f52efd73c76dd7fca5f909ba0 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 18 Jan 2017 10:29:53 -0800 Subject: [PATCH 16/20] Merge/replace improvements --- napalm_ios/ios.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/napalm_ios/ios.py b/napalm_ios/ios.py index c6c1968..ff9fe1a 100755 --- a/napalm_ios/ios.py +++ b/napalm_ios/ios.py @@ -166,11 +166,19 @@ def _load_candidate_wrapper(self, source_file=None, source_config=None, dest_fil msg = '' if source_file and source_config: raise ValueError("Cannot simultaneously set source_file and source_config") - tmp_file = "" + if source_config: - # Currently always have to create a temp file. - tmp_file = self._create_tmp_file(source_config) - source_file = tmp_file + if self.inline_transfer: + (return_status, msg) = self._inline_tcl_xfer(source_config=source_config, + dest_file=dest_file, + file_system=file_system) + else: + # Use SCP + tmp_file = self._create_tmp_file(source_config) + (return_status, msg) = self._scp_file(source_file=tmp_file, dest_file=dest_file, + file_system=file_system) + if tmp_file and os.path.isfile(tmp_file): + os.remove(tmp_file) if source_file: if self.inline_transfer: (return_status, msg) = self._inline_tcl_xfer(source_file=source_file, @@ -179,8 +187,6 @@ def _load_candidate_wrapper(self, source_file=None, source_config=None, dest_fil else: (return_status, msg) = self._scp_file(source_file=source_file, dest_file=dest_file, file_system=file_system) - if tmp_file and os.path.isfile(tmp_file): - os.remove(tmp_file) if not return_status: if msg == '': msg = "Transfer to remote device failed" @@ -433,14 +439,16 @@ def _xfer_file(self, source_file=None, source_config=None, dest_file=None, file_ if not dest_file or not file_system: raise ValueError("Destination file or file system not specified.") + if source_file: + kwargs = dict(ssh_conn=self.device, source_file=source_file, dest_file=dest_file, + direction='put', file_system=file_system) + elif source_config: + kwargs = dict(ssh_conn=self.device, source_config=source_config, dest_file=dest_file, + direction='put', file_system=file_system) enable_scp = True if self.inline_transfer: enable_scp = False - with TransferClass(self.device, - source_file=source_file, - dest_file=dest_file, - direction='put', - file_system=file_system) as transfer: + with TransferClass(**kwargs) as transfer: # Check if file already exists and has correct MD5 if transfer.check_file_exists() and transfer.compare_md5(): @@ -1557,7 +1565,6 @@ def process_mac_fields(vlan, mac, mac_type, interface): else: raise ValueError("Unexpected output from: {}".format(repr(line))) - print(mac_address_table) return mac_address_table def get_snmp_information(self): From a3ad36dd718fad8f859c2892b0e893dfcd03fffc Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 18 Jan 2017 10:40:25 -0800 Subject: [PATCH 17/20] Increment Netmiko to 1.2.7 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index baf5e8b..a2c2728 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ napalm_base>=0.21.0 -netmiko>=1.2.5 +netmiko>=1.2.7 From 7217f19b1226b8696534dc9d36b1fc92a0061496 Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Wed, 18 Jan 2017 14:00:36 -0800 Subject: [PATCH 18/20] Change tmp directory location --- napalm_ios/ios.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/napalm_ios/ios.py b/napalm_ios/ios.py index ff9fe1a..b5c586d 100755 --- a/napalm_ios/ios.py +++ b/napalm_ios/ios.py @@ -19,6 +19,7 @@ import re import os import uuid +import tempfile from netmiko import ConnectHandler, FileTransfer, InLineTransfer from netmiko import __version__ as netmiko_version @@ -148,7 +149,7 @@ def is_alive(self): @staticmethod def _create_tmp_file(config): """Write temp file and for use with inline config and SCP.""" - tmp_dir = os.getcwd() + tmp_dir = tempfile.gettempdir() rand_fname = py23_compat.text_type(uuid.uuid4()) filename = os.path.join(tmp_dir, rand_fname) with open(filename, 'wt') as fobj: From c406542c9277ad51573fd834646558ac122a60bf Mon Sep 17 00:00:00 2001 From: Kirk Byers Date: Sat, 28 Jan 2017 13:51:44 -0800 Subject: [PATCH 19/20] Force Travis to re-test --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index 7283207..ac248c6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,3 +13,4 @@ jsonapi = true [coverage:run] source = napalm_ios + From 97bcc017246a5428a5a321b04af2c9f07c39a127 Mon Sep 17 00:00:00 2001 From: Mircea Ulinic Date: Mon, 30 Jan 2017 17:28:50 +0000 Subject: [PATCH 20/20] Version 0.6.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 87d93f2..c00f850 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name="napalm-ios", - version="0.5.1", + version="0.6.0", packages=find_packages(), author="Kirk Byers", author_email="ktbyers@twb-tech.com",