Skip to content
This repository was archived by the owner on Sep 17, 2019. It is now read-only.

Commit c8aef74

Browse files
authored
Release 0.22.0 (#193)
* Dbarrosop/framework test bug (#170) * Test framework failed to run tests properly against real device * eval() doesn't like objects * Replaced eval with ast.literal_eval * Fix pylama * You can now use regular expressions to validate data (#176) * Dbarrosop/validate getter args (#175) * Dbarrosop/fix signatures (#154) * Fix method signatures * Use defaults from constants * Version bump * Python3 doesnt like relative imports * Validate now support getters with args * testing mocked method is in C.ACTION_TYPE_METHODS * Adding ACTION_TYPE_METHODS to constants file * Validate reports if a method is not implemented rather than failing (#179) * Validate reports if a method is not implemented rather than failing * Adapting string comparison to regexp * Update test status to reflect NAPALM distributions that have release Python3 support (#177) * Incrementing to add napalm-eos to PY3 * Add NXOS to python3 * Updating list of PY3 supported drivers * Add second ios-xr reference * Add additional napalm packages for PY3 * Add vyos to get network driver test * Updating test requirements files adding vyos * Adding napalm-ros to supported drivers * Fixing line-length pylama issue * Fix PY2 issue with long/int type mismatch * Correct errors in fix to handle long data type * Fixing pylama issue * Added reference to '_' as bgp group name * Fixed and issue comparing lists of dicts (#181) * Fixed and issue comparing lists of dicts * Merge develop * Correctly process lists of dicts * Better output for comparing lists of dicts * Correctly process lists of dicts * Comparing lists of dicts properly * Added special keyword _name to override name in the report (#191) * Added special keyword _name to override name in the report * Fix pylama * Bump version (#192)
1 parent a198cf5 commit c8aef74

34 files changed

+691
-60
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ script:
2424
- nosetests ./test/unit/TestGetNetworkDriver.py
2525
- nosetests ./test/unit/TestHelpers.py
2626
- nosetests ./test/unit/TestNapalmTestFramework.py
27-
- py.test test/unit/validate/test_validate.py
27+
- py.test test/unit/validate
2828
- pylama .

napalm_base/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def __init__(self, hostname, username, password, timeout=60, optional_args=None)
4949
def __enter__(self):
5050
try:
5151
self.open()
52-
except:
52+
except: # noqa
5353
exc_info = sys.exc_info()
5454
self.__raise_clean_exception(exc_info[0], exc_info[1], exc_info[2])
5555
return self
@@ -471,7 +471,7 @@ def get_bgp_config(self, group='', neighbor=''):
471471
:param neighbor: Returns the configuration of a specific BGP neighbor.
472472
473473
Main dictionary keys represent the group name and the values represent a dictionary having
474-
the following keys:
474+
the keys below. Neighbors which aren't members of a group will be stored in a key named "_":
475475
* type (string)
476476
* description (string)
477477
* apply_groups (string list)

napalm_base/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
INTERFACE_NULL_SPEED = -1
99

10+
ACTION_TYPE_METHODS = ('ping', 'traceroute', )
11+
1012
BGP_NEIGHBOR_NULL_COUNTER = -1
1113

1214
SNMP_AUTHORIZATION_MODE_MAP = {

napalm_base/helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ def convert(to, who, default=u''):
177177
return default
178178
try:
179179
return to(who)
180-
except:
180+
except: # noqa
181181
return default
182182

183183

napalm_base/test/conftest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
from __future__ import print_function
55
from __future__ import unicode_literals
66

7+
import ast
8+
79
import json
810
import os
911

10-
NAPALM_TEST_MOCK = eval("{}".format(os.getenv('NAPALM_TEST_MOCK', default=True)))
12+
NAPALM_TEST_MOCK = ast.literal_eval(os.getenv('NAPALM_TEST_MOCK', default="1"))
1113
NAPALM_HOSTNAME = os.getenv('NAPALM_HOSTNAME', default='127.0.0.1')
1214
NAPALM_USERNAME = os.getenv('NAPALM_USERNAME', default='vagrant')
1315
NAPALM_PASSWORD = os.getenv('NAPALM_PASSWORD', default='vagrant')

napalm_base/test/getters.py

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
import inspect
1414
import json
15-
import os
1615

1716
import pytest
1817
from napalm_base.test import helpers
@@ -22,8 +21,7 @@
2221
# text_type is 'unicode' for py2 and 'str' for py3
2322
from napalm_base.utils.py23_compat import text_type
2423

25-
26-
NAPALM_TEST_MOCK = os.getenv('NAPALM_TEST_MOCK', default=True)
24+
from napalm_base.test import conftest
2725

2826

2927
def list_dicts_diff(prv, nxt):
@@ -63,7 +61,7 @@ def wrap_test_cases(func):
6361
func.__dict__['build_test_cases'] = True
6462

6563
@functools.wraps(func)
66-
def wrapper(cls, test_case):
64+
def mock_wrapper(cls, test_case):
6765
for patched_attr in cls.device.patched_attrs:
6866
attr = getattr(cls.device, patched_attr)
6967
attr.current_test = func.__name__
@@ -85,19 +83,18 @@ def wrapper(cls, test_case):
8583
# This is an ugly, ugly, ugly hack because some python objects don't load
8684
# as expected. For example, dicts where integers are strings
8785

88-
if NAPALM_TEST_MOCK:
89-
try:
90-
expected_result = attr.expected_result
91-
except IOError as e:
92-
raise Exception("{}. Actual result was: {}".format(e, json.dumps(result)))
93-
if isinstance(result, list):
94-
diff = list_dicts_diff(result, expected_result)
95-
else:
96-
diff = dict_diff(result, expected_result)
97-
if diff:
98-
print("Resulting JSON object was: {}".format(json.dumps(result)))
99-
raise AssertionError("Expected result varies on some keys {}".format(
100-
json.dumps(diff)))
86+
try:
87+
expected_result = attr.expected_result
88+
except IOError as e:
89+
raise Exception("{}. Actual result was: {}".format(e, json.dumps(result)))
90+
if isinstance(result, list):
91+
diff = list_dicts_diff(result, expected_result)
92+
else:
93+
diff = dict_diff(result, expected_result)
94+
if diff:
95+
print("Resulting JSON object was: {}".format(json.dumps(result)))
96+
raise AssertionError("Expected result varies on some keys {}".format(
97+
json.dumps(diff)))
10198

10299
for patched_attr in cls.device.patched_attrs:
103100
attr = getattr(cls.device, patched_attr)
@@ -106,7 +103,18 @@ def wrapper(cls, test_case):
106103

107104
return result
108105

109-
return wrapper
106+
@functools.wraps(func)
107+
def real_wrapper(cls, test_case):
108+
try:
109+
return func(cls, test_case)
110+
except NotImplementedError:
111+
pytest.skip("Method not implemented")
112+
return
113+
114+
if conftest.NAPALM_TEST_MOCK:
115+
return mock_wrapper
116+
else:
117+
return real_wrapper
110118

111119

112120
class BaseTestGetters(object):

napalm_base/test/helpers.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from __future__ import print_function
55
from __future__ import unicode_literals
66

7+
from napalm_base.utils import py23_compat
8+
79

810
def test_model(model, data):
911
"""Return if the dictionary `data` complies with the `model`."""
@@ -14,9 +16,12 @@ def test_model(model, data):
1416

1517
correct_class = True
1618
for key, instance_class in model.items():
17-
same_class = isinstance(data[key], instance_class)
18-
correct_class = correct_class and same_class
19-
if not same_class:
19+
correct_class = isinstance(data[key], instance_class)
20+
# Properly handle PY2 long
21+
if py23_compat.PY2:
22+
if isinstance(data[key], long) and isinstance(1, instance_class): # noqa
23+
correct_class = True
24+
if not correct_class:
2025
print("key: {}\nmodel_class: {}\ndata_class: {}".format(
2126
key, instance_class, data[key].__class__))
2227

napalm_base/validate.py

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
import yaml
99

1010
from napalm_base.exceptions import ValidationException
11+
from napalm_base.utils import py23_compat
12+
13+
import copy
14+
import re
1115

1216

1317
def _get_validation_file(validation_file):
@@ -36,13 +40,22 @@ def _compare_getter_list(src, dst, mode):
3640
result = {"complies": True, "present": [], "missing": [], "extra": []}
3741
for src_element in src:
3842
found = False
39-
for index, dst_element in enumerate(dst):
40-
intermediate_match = _compare_getter(src_element, dst_element)
41-
if intermediate_match:
42-
found = True
43-
result["present"].append(src_element)
44-
dst.pop(index)
43+
44+
i = 0
45+
while True:
46+
try:
47+
intermediate_match = _compare_getter(src_element, dst[i])
48+
if isinstance(intermediate_match, dict) and intermediate_match["complies"] or \
49+
not isinstance(intermediate_match, dict) and intermediate_match:
50+
found = True
51+
result["present"].append(src_element)
52+
dst.pop(i)
53+
break
54+
else:
55+
i += 1
56+
except IndexError:
4557
break
58+
4659
if not found:
4760
result["complies"] = False
4861
result["missing"].append(src_element)
@@ -56,6 +69,7 @@ def _compare_getter_list(src, dst, mode):
5669

5770
def _compare_getter_dict(src, dst, mode):
5871
result = {"complies": True, "present": {}, "missing": [], "extra": []}
72+
dst = copy.deepcopy(dst) # Otherwise we are going to modify a "live" object
5973

6074
for key, src_element in src.items():
6175
try:
@@ -93,8 +107,8 @@ def _compare_getter_dict(src, dst, mode):
93107

94108

95109
def _compare_getter(src, dst):
96-
if isinstance(src, str):
97-
src = u'{}'.format(src)
110+
if isinstance(src, py23_compat.string_types):
111+
src = py23_compat.text_type(src)
98112

99113
if isinstance(src, dict):
100114
mode = _mode(src.pop('_mode', ''))
@@ -106,7 +120,11 @@ def _compare_getter(src, dst):
106120
return _compare_getter_list(src['list'], dst, mode)
107121
return _compare_getter_dict(src, dst, mode)
108122
else:
109-
return src == dst
123+
if isinstance(src, py23_compat.string_types):
124+
m = re.search(src, py23_compat.text_type(dst))
125+
return m is not None
126+
else:
127+
return src == dst
110128

111129

112130
def compliance_report(cls, validation_file=None):
@@ -118,8 +136,16 @@ def compliance_report(cls, validation_file=None):
118136
# TBD
119137
pass
120138
else:
121-
actual_results = getattr(cls, getter)()
122-
report[getter] = _compare_getter(expected_results, actual_results)
139+
key = expected_results.pop("_name", "") or getter
123140

124-
report["complies"] = all([e["complies"] for e in report.values()])
141+
try:
142+
kwargs = expected_results.pop('_kwargs', {})
143+
actual_results = getattr(cls, getter)(**kwargs)
144+
report[key] = _compare_getter(expected_results, actual_results)
145+
except NotImplementedError:
146+
report[key] = {"skipped": True, "reason": "NotImplemented"}
147+
148+
complies = all([e.get("complies", True) for e in report.values()])
149+
report["skipped"] = [k for k, v in report.items() if v.get("skipped", False)]
150+
report["complies"] = complies
125151
return report

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
setup(
1414
name="napalm-base",
15-
version='0.21.0',
15+
version='0.22.0',
1616
packages=find_packages(),
1717
author="David Barroso",
1818
author_email="[email protected]",

test/unit/TestGetNetworkDriver.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515
class TestGetNetworkDriver(unittest.TestCase):
1616
"""Test the method get_network_driver."""
1717

18+
drivers_common = ('eos', 'ios', 'iosxr', 'IOS-XR', 'junos', 'ros', 'nxos',
19+
'pluribus', 'panos', 'vyos')
20+
drivers_py2_only = ('fortios', 'ibm')
1821
if PY2:
1922
# All drivers support python2
20-
network_drivers = ('eos', 'eos', 'fortios', 'ibm', 'ios', 'iosxr', 'IOS-XR', 'junos',
21-
'nxos', 'pluribus', 'panos')
23+
network_drivers = drivers_common + drivers_py2_only
2224
elif PY3:
2325
# Drivers that support python2 and python3
24-
network_drivers = ('ios',)
26+
network_drivers = drivers_common
2527

2628
@data(*network_drivers)
2729
def test_get_network_driver(self, driver):

0 commit comments

Comments
 (0)