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

Commit a198cf5

Browse files
authored
Merge pull request #167 from napalm-automation/develop
Release 0.21.0
2 parents 80d409f + cde3dba commit a198cf5

37 files changed

+1177
-20
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,5 @@ env
6060

6161
test/unit/test_devices.py
6262

63+
report.json
64+
tags

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +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
2728
- pylama .

napalm_base/base.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525

2626
import napalm_base.constants as c
2727

28+
from napalm_base import validate
29+
2830

2931
class NetworkDriver(object):
3032

@@ -1488,3 +1490,12 @@ def get_firewall_policies(self):
14881490
}
14891491
"""
14901492
raise NotImplementedError
1493+
1494+
def compliance_report(self, validation_file='validate.yml'):
1495+
"""
1496+
Return a compliance report.
1497+
1498+
Verify that the device complies with the given validation file and writes a compliance
1499+
report file. See https://napalm.readthedocs.io/en/latest/validate.html.
1500+
"""
1501+
return validate.compliance_report(self, validation_file=validation_file)

napalm_base/clitools/cl_napalm_configure.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def run(vendor, hostname, user, password, strategy, optional_args, config_file,
5252

5353

5454
def main():
55-
args = build_help()
55+
args = build_help(configure=True)
5656
configure_logging(logger, args.debug)
5757

5858
print(run(args.vendor, args.hostname, args.user, args.password, args.strategy,

napalm_base/clitools/cl_napalm_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# stdlib
1919
import sys
2020
import logging
21-
logger = logging.getLogger(__file__)
21+
logger = logging.getLogger('cl_napalm_test.py')
2222

2323

2424
def main():
@@ -30,13 +30,13 @@ def main():
3030

3131
optional_args = parse_optional_args(args.optional_args)
3232
logger.debug('Connecting to device "{}" with user "{}" and optional_args={}'.format(
33-
device=args.hostname, user=args.user, optional_args=optional_args))
33+
args.hostname, args.user, optional_args))
3434

3535
with driver(args.hostname,
3636
args.user,
3737
args.password,
3838
optional_args=optional_args) as device:
39-
logger.debug('Successfully connected to the device: {}'.format(device))
39+
logger.debug('Successfully connected to the device: {}'.format(device.hostname))
4040
print('Successfully connected to the device')
4141
sys.exit(0)
4242

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# -*- coding: utf-8 -*-
2+
'''
3+
NAPALM CLI Tools: validate
4+
===========================
5+
6+
Validating deployments from the shell.
7+
'''
8+
9+
# Python3 support
10+
from __future__ import print_function
11+
from __future__ import unicode_literals
12+
13+
# import helpers
14+
from napalm_base import get_network_driver
15+
from napalm_base.clitools.helpers import build_help
16+
from napalm_base.clitools.helpers import configure_logging
17+
from napalm_base.clitools.helpers import parse_optional_args
18+
19+
# stdlib
20+
import sys
21+
import json
22+
import logging
23+
logger = logging.getLogger('cl_napalm_validate.py')
24+
25+
26+
def main():
27+
args = build_help(validate=True)
28+
configure_logging(logger, args.debug)
29+
30+
logger.debug('Getting driver for OS "{driver}"'.format(driver=args.vendor))
31+
driver = get_network_driver(args.vendor)
32+
33+
optional_args = parse_optional_args(args.optional_args)
34+
logger.debug('Connecting to device "{}" with user "{}" and optional_args={}'.format(
35+
args.hostname, args.user, optional_args))
36+
37+
with driver(args.hostname, args.user, args.password, optional_args=optional_args) as device:
38+
logger.debug('Generating compliance report')
39+
print(json.dumps(device.compliance_report(args.validation_file), indent=4))
40+
logger.debug('Closing session')
41+
sys.exit(0)
42+
43+
44+
if __name__ == '__main__':
45+
main()

napalm_base/clitools/helpers.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import argparse
1717

1818

19-
def build_help(connect_test=False):
19+
def build_help(connect_test=False, validate=False, configure=False):
2020
parser = argparse.ArgumentParser(
2121
description='Command line tool to handle configuration on devices using NAPALM.'
2222
'The script will print the diff on the screen',
@@ -60,7 +60,7 @@ def build_help(connect_test=False):
6060
action='store_true',
6161
help='Enables debug mode; more verbosity.'
6262
)
63-
if not connect_test:
63+
if configure:
6464
parser.add_argument(
6565
'--strategy', '-s',
6666
dest='strategy',
@@ -81,6 +81,13 @@ def build_help(connect_test=False):
8181
default=None,
8282
help='Only returns diff, it does not deploy the configuration.',
8383
)
84+
elif validate:
85+
parser.add_argument(
86+
'--validation_file', '-f',
87+
dest='validation_file',
88+
action='store',
89+
help='Validation file containing resources derised states'
90+
)
8491
args = parser.parse_args()
8592

8693
if args.password is None:

napalm_base/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,7 @@ class TemplateNotImplemented(Exception):
5555

5656
class TemplateRenderException(Exception):
5757
pass
58+
59+
60+
class ValidationException(Exception):
61+
pass

napalm_base/test/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import json
88
import os
99

10-
NAPALM_TEST_MOCK = os.getenv('NAPALM_TEST_MOCK', default=True)
10+
NAPALM_TEST_MOCK = eval("{}".format(os.getenv('NAPALM_TEST_MOCK', default=True)))
1111
NAPALM_HOSTNAME = os.getenv('NAPALM_HOSTNAME', default='127.0.0.1')
1212
NAPALM_USERNAME = os.getenv('NAPALM_USERNAME', default='vagrant')
1313
NAPALM_PASSWORD = os.getenv('NAPALM_PASSWORD', default='vagrant')

napalm_base/test/getters.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -116,27 +116,30 @@ def test_method_signatures(self):
116116
"""Test that all methods have the same signature."""
117117
errors = {}
118118
cls = self.driver
119-
attrs = [m for m in dir(cls)]
119+
# Create fictional driver instance (py3 needs bound methods)
120+
tmp_obj = cls(hostname='test', username='admin', password='pwd')
121+
attrs = [m for m, v in inspect.getmembers(tmp_obj)]
120122
for attr in attrs:
121-
func = getattr(cls, attr)
123+
func = getattr(tmp_obj, attr)
122124
if attr.startswith('_') or not inspect.ismethod(func):
123125
continue
124-
orig = getattr(NetworkDriver, attr)
125-
126-
orig_spec = inspect.getargspec(orig)
126+
try:
127+
orig = getattr(NetworkDriver, attr)
128+
orig_spec = inspect.getargspec(orig)
129+
except AttributeError:
130+
orig_spec = 'Method does not exist in napalm_base'
127131
func_spec = inspect.getargspec(func)
128132
if orig_spec != func_spec:
129133
errors[attr] = (orig_spec, func_spec)
130134

131135
EXTRA_METHODS = ['__init__', ]
132136
for method in EXTRA_METHODS:
133-
if not method.startswith('_'):
134-
orig_spec = inspect.getargspec(getattr(NetworkDriver, method))
135-
func_spec = inspect.getargspec(getattr(cls, method))
136-
if orig_spec != func_spec:
137-
errors[attr] = (orig_spec, func_spec)
137+
orig_spec = inspect.getargspec(getattr(NetworkDriver, method))
138+
func_spec = inspect.getargspec(getattr(cls, method))
139+
if orig_spec != func_spec:
140+
errors[attr] = (orig_spec, func_spec)
138141

139-
assert not errors, "Some method vary. \n{}".format(errors)
142+
assert not errors, "Some methods vary. \n{}".format(errors.keys())
140143

141144
@wrap_test_cases
142145
def test_is_alive(self, test_case):

0 commit comments

Comments
 (0)