[Python-modules-commits] [napalm-base] 02/04: New upstream release.
Vincent Bernat
bernat at moszumanska.debian.org
Tue Nov 14 09:36:53 UTC 2017
This is an automated email from the git hooks/post-receive script.
bernat pushed a commit to annotated tag debian/0.25.0-1
in repository napalm-base.
commit 8c7d30ec0056b7135d4af8cb4d38d3607464c1d3
Author: Vincent Bernat <bernat at debian.org>
Date: Tue Nov 14 10:28:51 2017 +0100
New upstream release.
---
PKG-INFO | 6 +-
napalm_base.egg-info/PKG-INFO | 6 +-
napalm_base.egg-info/SOURCES.txt | 2 +
napalm_base.egg-info/entry_points.txt | 1 +
napalm_base/__init__.py | 7 +-
napalm_base/base.py | 138 ++++++++-----
napalm_base/clitools/cl_napalm.py | 289 ++++++++++++++++++++++++++++
napalm_base/clitools/cl_napalm_configure.py | 3 +
napalm_base/clitools/cl_napalm_test.py | 3 +
napalm_base/clitools/cl_napalm_validate.py | 3 +
napalm_base/clitools/helpers.py | 15 +-
napalm_base/constants.py | 4 +
napalm_base/exceptions.py | 56 ++++++
napalm_base/helpers.py | 59 +++---
napalm_base/mock.py | 199 +++++++++++++++++++
napalm_base/test/base.py | 10 +
napalm_base/test/conftest.py | 4 +-
napalm_base/test/getters.py | 44 +++--
napalm_base/test/helpers.py | 11 +-
napalm_base/utils/string_parsers.py | 47 +++--
napalm_base/validate.py | 109 +++++++++--
setup.cfg | 28 ++-
setup.py | 9 +-
23 files changed, 910 insertions(+), 143 deletions(-)
diff --git a/PKG-INFO b/PKG-INFO
index e0cb296..9f60a8f 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,10 +1,10 @@
Metadata-Version: 1.1
Name: napalm-base
-Version: 0.21.0
+Version: 0.25.0
Summary: Network Automation and Programmability Abstraction Layer with Multivendor support
Home-page: https://github.com/napalm-automation/napalm-base
-Author: David Barroso
-Author-email: dbarrosop at dravetech.com
+Author: David Barroso, Kirk Byers, Mircea Ulinic
+Author-email: dbarrosop at dravetech.com, ping at mirceaulinic.net, ktbyers at twb-tech.com
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN
diff --git a/napalm_base.egg-info/PKG-INFO b/napalm_base.egg-info/PKG-INFO
index e0cb296..9f60a8f 100644
--- a/napalm_base.egg-info/PKG-INFO
+++ b/napalm_base.egg-info/PKG-INFO
@@ -1,10 +1,10 @@
Metadata-Version: 1.1
Name: napalm-base
-Version: 0.21.0
+Version: 0.25.0
Summary: Network Automation and Programmability Abstraction Layer with Multivendor support
Home-page: https://github.com/napalm-automation/napalm-base
-Author: David Barroso
-Author-email: dbarrosop at dravetech.com
+Author: David Barroso, Kirk Byers, Mircea Ulinic
+Author-email: dbarrosop at dravetech.com, ping at mirceaulinic.net, ktbyers at twb-tech.com
License: UNKNOWN
Description: UNKNOWN
Platform: UNKNOWN
diff --git a/napalm_base.egg-info/SOURCES.txt b/napalm_base.egg-info/SOURCES.txt
index 590c02d..56aaa63 100644
--- a/napalm_base.egg-info/SOURCES.txt
+++ b/napalm_base.egg-info/SOURCES.txt
@@ -7,6 +7,7 @@ napalm_base/base.py
napalm_base/constants.py
napalm_base/exceptions.py
napalm_base/helpers.py
+napalm_base/mock.py
napalm_base/validate.py
napalm_base.egg-info/PKG-INFO
napalm_base.egg-info/SOURCES.txt
@@ -15,6 +16,7 @@ napalm_base.egg-info/entry_points.txt
napalm_base.egg-info/requires.txt
napalm_base.egg-info/top_level.txt
napalm_base/clitools/__init__.py
+napalm_base/clitools/cl_napalm.py
napalm_base/clitools/cl_napalm_configure.py
napalm_base/clitools/cl_napalm_test.py
napalm_base/clitools/cl_napalm_validate.py
diff --git a/napalm_base.egg-info/entry_points.txt b/napalm_base.egg-info/entry_points.txt
index 2a3eb91..3bcf363 100644
--- a/napalm_base.egg-info/entry_points.txt
+++ b/napalm_base.egg-info/entry_points.txt
@@ -2,4 +2,5 @@
cl_napalm_configure = napalm_base.clitools.cl_napalm_configure:main
cl_napalm_test = napalm_base.clitools.cl_napalm_test:main
cl_napalm_validate = napalm_base.clitools.cl_napalm_validate:main
+napalm = napalm_base.clitools.cl_napalm:main
diff --git a/napalm_base/__init__.py b/napalm_base/__init__.py
index 3b09d4d..bce3d11 100644
--- a/napalm_base/__init__.py
+++ b/napalm_base/__init__.py
@@ -35,6 +35,7 @@ except AttributeError:
# NAPALM base
from napalm_base.base import NetworkDriver
from napalm_base.exceptions import ModuleImportError
+from napalm_base.mock import MockDriver
from napalm_base.utils import py23_compat
try:
@@ -49,7 +50,7 @@ __all__ = [
]
-def get_network_driver(module_name):
+def get_network_driver(module_name, prepend=True):
"""
Searches for a class derived form the base NAPALM class NetworkDriver in a specific library.
The library name must repect the following pattern: napalm_[DEVICE_OS].
@@ -81,6 +82,8 @@ def get_network_driver(module_name):
napalm_base.exceptions.ModuleImportError: Cannot import "napalm_wrong". Is the library \
installed?
"""
+ if module_name == "mock":
+ return MockDriver
if not (isinstance(module_name, py23_compat.string_types) and len(module_name) > 0):
raise ModuleImportError('Please provide a valid driver name.')
@@ -91,7 +94,7 @@ def get_network_driver(module_name):
# Try to not raise error when users requests IOS-XR for e.g.
module_install_name = module_name.replace('-', '')
# Can also request using napalm_[SOMETHING]
- if 'napalm_' not in module_install_name:
+ if 'napalm_' not in module_install_name and prepend is True:
module_install_name = 'napalm_{name}'.format(name=module_install_name)
module = importlib.import_module(module_install_name)
except ImportError:
diff --git a/napalm_base/base.py b/napalm_base/base.py
index 9a512ec..1912f00 100644
--- a/napalm_base/base.py
+++ b/napalm_base/base.py
@@ -16,9 +16,6 @@
from __future__ import print_function
from __future__ import unicode_literals
-# std libs
-import sys
-
# local modules
import napalm_base.exceptions
import napalm_base.helpers
@@ -47,39 +44,31 @@ class NetworkDriver(object):
raise NotImplementedError
def __enter__(self):
- try:
- self.open()
- except:
- exc_info = sys.exc_info()
- self.__raise_clean_exception(exc_info[0], exc_info[1], exc_info[2])
+ self.open()
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
self.close()
- if exc_type is not None:
- self.__raise_clean_exception(exc_type, exc_value, exc_traceback)
-
- @staticmethod
- def __raise_clean_exception(exc_type, exc_value, exc_traceback):
- """
- This method is going to check if the exception exc_type is part of the builtins exceptions
- or part of the napalm exceptions. If it is not, it will print a message on the screen
- giving instructions to fill a bug.
-
- Finally it will raise the original exception.
-
- :param exc_type: Exception class.
- :param exc_value: Exception object.
- :param exc_traceback: Traceback.
- """
- if (exc_type.__name__ not in dir(napalm_base.exceptions) and
- exc_type.__name__ not in __builtins__.keys()):
+ if exc_type is not None and (
+ exc_type.__name__ not in dir(napalm_base.exceptions) and
+ exc_type.__name__ not in __builtins__.keys()):
epilog = ("NAPALM didn't catch this exception. Please, fill a bugfix on "
"https://github.com/napalm-automation/napalm/issues\n"
"Don't forget to include this traceback.")
print(epilog)
- # Traceback should already be attached to exception; no need to re-attach
- raise exc_value
+ return False
+
+ def __del__(self):
+ """
+ This method is used to cleanup when the program is terminated suddenly.
+ We need to make sure the connection is closed properly and the configuration DB
+ is released (unlocked).
+ """
+ try:
+ if self.is_alive()["is_alive"]:
+ self.close()
+ except Exception:
+ pass
def open(self):
"""
@@ -103,6 +92,30 @@ class NetworkDriver(object):
"""
raise NotImplementedError
+ def pre_connection_tests(self):
+ """
+ This is a helper function used by the cli tool cl_napalm_show_tech. Drivers
+ can override this method to do some tests, show information, enable debugging, etc.
+ before a connection with the device is attempted.
+ """
+ raise NotImplementedError
+
+ def connection_tests(self):
+ """
+ This is a helper function used by the cli tool cl_napalm_show_tech. Drivers
+ can override this method to do some tests, show information, enable debugging, etc.
+ before a connection with the device has been successful.
+ """
+ raise NotImplementedError
+
+ def post_connection_tests(self):
+ """
+ This is a helper function used by the cli tool cl_napalm_show_tech. Drivers
+ can override this method to do some tests, show information, enable debugging, etc.
+ after a connection with the device has been closed successfully.
+ """
+ raise NotImplementedError
+
def load_template(self, template_name, template_source=None,
template_path=None, **template_vars):
"""
@@ -231,37 +244,37 @@ class NetworkDriver(object):
{
'is_up': False,
'is_enabled': False,
- 'description': u'',
+ 'description': '',
'last_flapped': -1,
'speed': 1000,
- 'mac_address': u'dead:beef:dead',
+ 'mac_address': 'FA:16:3E:57:33:61',
},
u'Ethernet1':
{
'is_up': True,
'is_enabled': True,
- 'description': u'foo',
+ 'description': 'foo',
'last_flapped': 1429978575.1554043,
'speed': 1000,
- 'mac_address': u'beef:dead:beef',
+ 'mac_address': 'FA:16:3E:57:33:62',
},
u'Ethernet2':
{
'is_up': True,
'is_enabled': True,
- 'description': u'bla',
+ 'description': 'bla',
'last_flapped': 1429978575.1555667,
'speed': 1000,
- 'mac_address': u'beef:beef:beef',
+ 'mac_address': 'FA:16:3E:57:33:63',
},
u'Ethernet3':
{
'is_up': False,
'is_enabled': True,
- 'description': u'bar',
+ 'description': 'bar',
'last_flapped': -1,
'speed': 1000,
- 'mac_address': u'dead:dead:dead',
+ 'mac_address': 'FA:16:3E:57:33:64',
}
}
"""
@@ -333,6 +346,40 @@ class NetworkDriver(object):
* received_prefixes (int)
* accepted_prefixes (int)
* sent_prefixes (int)
+
+ Note, if is_up is False and uptime has a positive value then this indicates the
+ uptime of the last active BGP session.
+
+ Example response:
+ {
+ "global": {
+ "router_id": "10.0.1.1",
+ "peers": {
+ "10.0.0.2": {
+ "local_as": 65000,
+ "remote_as": 65000,
+ "remote_id": "10.0.1.2",
+ "is_up": True,
+ "is_enabled": True,
+ "description": "internal-2",
+ "uptime": 4838400,
+ "address_family": {
+ "ipv4": {
+ "sent_prefixes": 637213,
+ "accepted_prefixes": 3142,
+ "received_prefixes": 3142
+ },
+ "ipv6": {
+ "sent_prefixes": 36714,
+ "accepted_prefixes": 148,
+ "received_prefixes": 148
+ }
+ }
+ }
+ }
+ }
+ }
+
"""
raise NotImplementedError
@@ -471,7 +518,7 @@ class NetworkDriver(object):
:param neighbor: Returns the configuration of a specific BGP neighbor.
Main dictionary keys represent the group name and the values represent a dictionary having
- the following keys:
+ the keys below. Neighbors which aren't members of a group will be stored in a key named "_":
* type (string)
* description (string)
* apply_groups (string list)
@@ -694,13 +741,13 @@ class NetworkDriver(object):
[
{
'interface' : 'MgmtEth0/RSP0/CPU0/0',
- 'mac' : '5c:5e:ab:da:3c:f0',
+ 'mac' : '5C:5E:AB:DA:3C:F0',
'ip' : '172.17.17.1',
'age' : 1454496274.84
},
{
'interface' : 'MgmtEth0/RSP0/CPU0/0',
- 'mac' : '66:0e:94:96:e0:ff',
+ 'mac' : '5C:5E:AB:DA:3C:FF',
'ip' : '172.17.17.2',
'age' : 1435641582.49
}
@@ -859,7 +906,7 @@ class NetworkDriver(object):
[
{
- 'mac' : '00:1c:58:29:4a:71',
+ 'mac' : '00:1C:58:29:4A:71',
'interface' : 'Ethernet47',
'vlan' : 100,
'static' : False,
@@ -868,7 +915,7 @@ class NetworkDriver(object):
'last_move' : 1454417742.58
},
{
- 'mac' : '8c:60:4f:58:e1:c1',
+ 'mac' : '00:1C:58:29:4A:C1',
'interface' : 'xe-1/0/1',
'vlan' : 100,
'static' : False,
@@ -877,7 +924,7 @@ class NetworkDriver(object):
'last_move' : 1453191948.11
},
{
- 'mac' : 'f4:b5:2f:56:72:01',
+ 'mac' : '00:1C:58:29:4A:C2',
'interface' : 'ae7.900',
'vlan' : 900,
'static' : False,
@@ -1111,7 +1158,7 @@ class NetworkDriver(object):
raise NotImplementedError
def ping(self, destination, source=c.PING_SOURCE, ttl=c.PING_TTL, timeout=c.PING_TIMEOUT,
- size=c.PING_SIZE, count=c.PING_COUNT):
+ size=c.PING_SIZE, count=c.PING_COUNT, vrf=c.PING_VRF):
"""
Executes ping on the device and returns a dictionary with the result
@@ -1178,7 +1225,8 @@ class NetworkDriver(object):
destination,
source=c.TRACEROUTE_SOURCE,
ttl=c.TRACEROUTE_TTL,
- timeout=c.TRACEROUTE_TIMEOUT):
+ timeout=c.TRACEROUTE_TIMEOUT,
+ vrf=c.TRACEROUTE_VRF):
"""
Executes traceroute on the device and returns a dictionary with the result.
@@ -1496,6 +1544,6 @@ class NetworkDriver(object):
Return a compliance report.
Verify that the device complies with the given validation file and writes a compliance
- report file. See https://napalm.readthedocs.io/en/latest/validate.html.
+ report file. See https://napalm.readthedocs.io/en/latest/validate/index.html.
"""
return validate.compliance_report(self, validation_file=validation_file)
diff --git a/napalm_base/clitools/cl_napalm.py b/napalm_base/clitools/cl_napalm.py
new file mode 100755
index 0000000..9ca4682
--- /dev/null
+++ b/napalm_base/clitools/cl_napalm.py
@@ -0,0 +1,289 @@
+# Python3 support
+from __future__ import print_function
+from __future__ import unicode_literals
+
+# import helpers
+from napalm_base import get_network_driver
+from napalm_base.clitools import helpers
+
+# stdlib
+import pip
+import json
+import logging
+import argparse
+import getpass
+from functools import wraps
+
+
+def debugging(name):
+ def real_decorator(func):
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ censor_parameters = ["password"]
+ censored_kwargs = {k: v if k not in censor_parameters else "*******"
+ for k, v in kwargs.items()}
+ logger.debug("{} - Calling with args: {}, {}".format(name, args, censored_kwargs))
+ try:
+ r = func(*args, **kwargs)
+ logger.debug("{} - Successful".format(name))
+ return r
+ except NotImplementedError:
+ if name not in ["pre_connection_tests", "connection_tests",
+ "post_connection_tests"]:
+ logger.debug("{} - Not implemented".format(name))
+ except Exception as e:
+ logger.error("{} - Failed: {}".format(name, e))
+ print("\n================= Traceback =================\n")
+ raise
+ return wrapper
+ return real_decorator
+
+
+logger = logging.getLogger('napalm')
+
+
+def build_help():
+ parser = argparse.ArgumentParser(
+ description='Command line tool to handle configuration on devices using NAPALM.'
+ 'The script will print the diff on the screen',
+ epilog='Automate all the things!!!'
+ )
+ parser.add_argument(
+ dest='hostname',
+ action='store',
+ help='Host where you want to deploy the configuration.'
+ )
+ parser.add_argument(
+ '--user', '-u',
+ dest='user',
+ action='store',
+ default=getpass.getuser(),
+ help='User for authenticating to the host. Default: user running the script.'
+ )
+ parser.add_argument(
+ '--password', '-p',
+ dest='password',
+ action='store',
+ help='Password for authenticating to the host.'
+ 'If you do not provide a password in the CLI you will be prompted.',
+ )
+ parser.add_argument(
+ '--vendor', '-v',
+ dest='vendor',
+ action='store',
+ required=True,
+ help='Host Operating System.'
+ )
+ parser.add_argument(
+ '--optional_args', '-o',
+ dest='optional_args',
+ action='store',
+ help='String with comma separated key=value pairs passed via optional_args to the driver.',
+ )
+ parser.add_argument(
+ '--debug',
+ dest='debug',
+ action='store_true',
+ help='Enables debug mode; more verbosity.'
+ )
+ subparser = parser.add_subparsers(title='actions')
+
+ config = subparser.add_parser('configure', help='Perform a configuration operation')
+ config.set_defaults(which='config')
+ config.add_argument(
+ dest='config_file',
+ action='store',
+ help='File containing the configuration you want to deploy.'
+ )
+ config.add_argument(
+ '--strategy', '-s',
+ dest='strategy',
+ action='store',
+ choices=['replace', 'merge'],
+ default='replace',
+ help='Strategy to use to deploy configuration. Default: replace.'
+ )
+ config.add_argument(
+ '--dry-run', '-d',
+ dest='dry_run',
+ action='store_true',
+ default=None,
+ help='Only returns diff, it does not deploy the configuration.',
+ )
+
+ call = subparser.add_parser('call', help='Call a napalm method')
+ call.set_defaults(which='call')
+ call.add_argument(
+ dest='method',
+ action='store',
+ help='Run this method'
+ )
+ call.add_argument(
+ '--method-kwargs', '-k',
+ dest='method_kwargs',
+ action='store',
+ help='kwargs to pass to the method. For example: "destination=1.1.1.1,protocol=bgp"'
+ )
+
+ validate = subparser.add_parser('validate', help='Validate configuration/state')
+ validate.set_defaults(which='validate')
+ validate.add_argument(
+ dest='validation_file',
+ action='store',
+ help='Validation file containing resources derised states'
+ )
+ args = parser.parse_args()
+
+ if args.password is None:
+ password = getpass.getpass('Enter password: ')
+ setattr(args, 'password', password)
+
+ return args
+
+
+def check_installed_packages():
+ logger.debug("Gathering napalm packages")
+ installed_packages = pip.get_installed_distributions()
+ napalm_packages = sorted(["{}=={}".format(i.key, i.version)
+ for i in installed_packages if i.key.startswith("napalm")])
+ for n in napalm_packages:
+ logger.debug(n)
+
+
+ at debugging("get_network_driver")
+def call_get_network_driver(vendor):
+ return get_network_driver(vendor)
+
+
+ at debugging("__init__")
+def call_instantiating_object(driver, *args, **kwargs):
+ return driver(*args, **kwargs)
+
+
+ at debugging("pre_connection_tests")
+def call_pre_connection(driver):
+ driver.pre_connection_tests()
+
+
+ at debugging("connection_tests")
+def call_connection(device):
+ device.connection_tests()
+
+
+ at debugging("post_connection_tests")
+def call_post_connection(device):
+ device.post_connection_tests()
+
+
+ at debugging("get_facts")
+def call_facts(device):
+ facts = device.get_facts()
+ logger.debug("Gathered facts:\n{}".format(json.dumps(facts, indent=4)))
+ print(json.dumps(facts, indent=4))
+
+
+ at debugging("close")
+def call_close(device):
+ return device.close()
+
+
+ at debugging("open")
+def call_open_device(device):
+ return device.open()
+
+
+ at debugging("load_replace_candidate")
+def call_load_replace_candidate(device, *args, **kwargs):
+ return device.load_replace_candidate(*args, **kwargs)
+
+
+ at debugging("load_merge_candidate")
+def call_load_merge_candidate(device, *args, **kwargs):
+ return device.load_merge_candidate(*args, **kwargs)
+
+
+ at debugging("compare_config")
+def call_compare_config(device, *args, **kwargs):
+ diff = device.compare_config(*args, **kwargs)
+ logger.debug("Gathered diff:")
+ print(diff)
+ return diff
+
+
+ at debugging("commit_config")
+def call_commit_config(device, *args, **kwargs):
+ return device.commit_config(*args, **kwargs)
+
+
+def configuration_change(device, config_file, strategy, dry_run):
+ if strategy == 'replace':
+ strategy_method = call_load_replace_candidate
+ elif strategy == 'merge':
+ strategy_method = call_load_merge_candidate
+
+ strategy_method(device, filename=config_file)
+
+ diff = call_compare_config(device)
+
+ if not dry_run:
+ call_commit_config(device)
+ return diff
+
+
+ at debugging("method")
+def call_getter(device, method, **kwargs):
+ logger.debug("{} - Attempting to resolve method".format(method))
+ func = getattr(device, method)
+ logger.debug("{} - Attempting to call method with kwargs: {}".format(method, kwargs))
+ r = func(**kwargs)
+ logger.debug("{} - Response".format(method))
+ print(json.dumps(r, indent=4))
+
+
+ at debugging("compliance_report")
+def call_compliance_report(device, validation_file):
+ result = device.compliance_report(validation_file)
+ print(json.dumps(result, indent=4))
+ return result
+
+
+def run_tests(args):
+ driver = call_get_network_driver(args.vendor)
+ optional_args = helpers.parse_optional_args(args.optional_args)
+
+ device = call_instantiating_object(driver, args.hostname, args.user, password=args.password,
+ timeout=60, optional_args=optional_args)
+
+ if args.debug:
+ call_pre_connection(device)
+
+ call_open_device(device)
+
+ if args.debug:
+ call_connection(device)
+ call_facts(device)
+
+ if args.which == 'call':
+ method_kwargs = helpers.parse_optional_args(args.method_kwargs)
+ call_getter(device, args.method, **method_kwargs)
+ elif args.which == 'config':
+ configuration_change(device, args.config_file, args.strategy, args.dry_run)
+ elif args.which == 'validate':
+ call_compliance_report(device, args.validation_file)
+
+ call_close(device)
+
+ if args.debug:
+ call_post_connection(device)
+
+
+def main():
+ args = build_help()
+ helpers.configure_logging(logger, debug=args.debug)
+ logger.debug("Starting napalm's debugging tool")
+ check_installed_packages()
+ run_tests(args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/napalm_base/clitools/cl_napalm_configure.py b/napalm_base/clitools/cl_napalm_configure.py
index fce6b32..3924fc4 100644
--- a/napalm_base/clitools/cl_napalm_configure.py
+++ b/napalm_base/clitools/cl_napalm_configure.py
@@ -15,10 +15,13 @@ from napalm_base import get_network_driver
from napalm_base.clitools.helpers import build_help
from napalm_base.clitools.helpers import configure_logging
from napalm_base.clitools.helpers import parse_optional_args
+from napalm_base.clitools.helpers import warning
import sys
import logging
+
logger = logging.getLogger('cl-napalm-config.py')
+warning()
def run(vendor, hostname, user, password, strategy, optional_args, config_file, dry_run):
diff --git a/napalm_base/clitools/cl_napalm_test.py b/napalm_base/clitools/cl_napalm_test.py
index 5f934c1..7bbe682 100644
--- a/napalm_base/clitools/cl_napalm_test.py
+++ b/napalm_base/clitools/cl_napalm_test.py
@@ -14,11 +14,14 @@ from napalm_base import get_network_driver
from napalm_base.clitools.helpers import build_help
from napalm_base.clitools.helpers import configure_logging
from napalm_base.clitools.helpers import parse_optional_args
+from napalm_base.clitools.helpers import warning
# stdlib
import sys
import logging
+
logger = logging.getLogger('cl_napalm_test.py')
+warning()
def main():
diff --git a/napalm_base/clitools/cl_napalm_validate.py b/napalm_base/clitools/cl_napalm_validate.py
index 69b55a0..825d79d 100755
--- a/napalm_base/clitools/cl_napalm_validate.py
+++ b/napalm_base/clitools/cl_napalm_validate.py
@@ -15,12 +15,15 @@ from napalm_base import get_network_driver
from napalm_base.clitools.helpers import build_help
from napalm_base.clitools.helpers import configure_logging
from napalm_base.clitools.helpers import parse_optional_args
+from napalm_base.clitools.helpers import warning
# stdlib
import sys
import json
import logging
+
logger = logging.getLogger('cl_napalm_validate.py')
+warning()
def main():
diff --git a/napalm_base/clitools/helpers.py b/napalm_base/clitools/helpers.py
index 329884b..d3020e5 100644
--- a/napalm_base/clitools/helpers.py
+++ b/napalm_base/clitools/helpers.py
@@ -10,13 +10,21 @@ from __future__ import print_function
from __future__ import unicode_literals
# stdlib
+import ast
import sys
import logging
import getpass
import argparse
+import warnings
-def build_help(connect_test=False, validate=False, configure=False):
+def warning():
+ warnings.simplefilter('always', DeprecationWarning)
+ warnings.warn("This tool has been deprecated, please use `napalm` instead\n",
+ DeprecationWarning)
+
+
+def build_help(connect_test=False, validate=False, configure=False, napalm_cli=False):
parser = argparse.ArgumentParser(
description='Command line tool to handle configuration on devices using NAPALM.'
'The script will print the diff on the screen',
@@ -60,6 +68,7 @@ def build_help(connect_test=False, validate=False, configure=False):
action='store_true',
help='Enables debug mode; more verbosity.'
)
+
if configure:
parser.add_argument(
'--strategy', '-s',
@@ -111,7 +120,7 @@ def configure_logging(logger, debug):
def parse_optional_args(optional_args):
-
if optional_args is not None:
- return {x.split('=')[0]: x.split('=')[1] for x in optional_args.replace(' ', '').split(',')}
+ return {x.split('=')[0]: ast.literal_eval(x.split('=')[1])
+ for x in optional_args.split(',')}
return {}
diff --git a/napalm_base/constants.py b/napalm_base/constants.py
index c7b7823..bcfbff9 100644
--- a/napalm_base/constants.py
+++ b/napalm_base/constants.py
@@ -7,6 +7,8 @@ TIMEOUT = 60 # seconds
INTERFACE_NULL_SPEED = -1
+ACTION_TYPE_METHODS = ('ping', 'traceroute', )
+
BGP_NEIGHBOR_NULL_COUNTER = -1
SNMP_AUTHORIZATION_MODE_MAP = {
@@ -54,6 +56,7 @@ TRACEROUTE_SOURCE = ''
TRACEROUTE_TIMEOUT = 2
TRACEROUTE_NULL_HOST_NAME = '*'
TRACEROUTE_NULL_IP_ADDRESS = '*'
+TRACEROUTE_VRF = ''
OPTICS_NULL_LEVEL = '-Inf'
@@ -62,3 +65,4 @@ PING_TTL = 255
PING_TIMEOUT = 2
PING_SIZE = 100
PING_COUNT = 5
+PING_VRF = ''
diff --git a/napalm_base/exceptions.py b/napalm_base/exceptions.py
index 86dbbd6..5a14228 100644
--- a/napalm_base/exceptions.py
+++ b/napalm_base/exceptions.py
@@ -22,6 +22,40 @@ class ModuleImportError(Exception):
class ConnectionException(Exception):
+ '''
+ Unable to connect to the network device.
+ '''
+ pass
+
+
+class ConnectAuthError(ConnectionException):
+ '''
+ Unable to connect to the network device
+ due to invalid credentials.
+ '''
+ pass
+
+
+class ConnectTimeoutError(ConnectionException):
+ '''
+ Exception raised when the connection to the
+ network device takes too long.
+ This may be avoided by adjusting the `timeout`
+ argument.
+ '''
+ pass
+
+
+class ConnectionClosedException(ConnectionException):
+ '''
+ The network device closed the connection.
+ Raised whenever we try to execute a certain
+ function, but we detect that the connection
+ is not usable anymore. This can happen for
+ various reasons: the network device terminates the
+ session or it is dropped by a firewall or
+ the server.
+ '''
pass
@@ -33,6 +67,28 @@ class MergeConfigException(Exception):
pass
+class CommitError(Exception):
+ '''
+ Raised when unable to commit the candidate config
+ into the running config.
+ '''
+ pass
+
+
+class LockError(Exception):
+ '''
+ Unable to lock the candidate config.
+ '''
+ pass
+
+
+class UnlockError(Exception):
+ '''
+ Unable to unlock the candidate config.
+ '''
+ pass
+
+
class SessionLockedException(Exception):
pass
diff --git a/napalm_base/helpers.py b/napalm_base/helpers.py
index ed7d63f..8df17ab 100644
--- a/napalm_base/helpers.py
+++ b/napalm_base/helpers.py
@@ -37,29 +37,28 @@ _MACFormat.word_fmt = '%.2X'
def load_template(cls, template_name, template_source=None, template_path=None,
openconfig=False, **template_vars):
try:
+ search_path = []
if isinstance(template_source, py23_compat.string_types):
template = jinja2.Template(template_source)
else:
- current_dir = os.path.dirname(os.path.abspath(sys.modules[cls.__module__].__file__))
- if (isinstance(template_path, py23_compat.string_types) and
- os.path.isdir(template_path) and os.path.isabs(template_path)):
- current_dir = os.path.join(template_path, cls.__module__.split('.')[-1])
- # append driver name at the end of the custom path
+ if template_path is not None:
+ if (isinstance(template_path, py23_compat.string_types) and
+ os.path.isdir(template_path) and os.path.isabs(template_path)):
+ # append driver name at the end of the custom path
+ search_path.append(os.path.join(template_path, cls.__module__.split('.')[-1]))
+ else:
+ raise IOError("Template path does not exist: {}".format(template_path))
+ else:
+ # Search modules for template paths
+ search_path = [os.path.dirname(os.path.abspath(sys.modules[c.__module__].__file__))
+ for c in cls.__class__.mro() if c is not object]
if openconfig:
- template_dir_path = '{current_dir}/oc_templates'.format(current_dir=current_dir)
+ search_path = ['{}/oc_templates'.format(s) for s in search_path]
else:
- template_dir_path = '{current_dir}/templates'.format(current_dir=current_dir)
-
- if not os.path.isdir(template_dir_path):
- raise napalm_base.exceptions.DriverTemplateNotImplemented(
- '''Config template dir does not exist: {path}.
- Please create it and add driver-specific templates.'''.format(
- path=template_dir_path
- )
- )
+ search_path = ['{}/templates'.format(s) for s in search_path]
- loader = jinja2.FileSystemLoader(template_dir_path)
+ loader = jinja2.FileSystemLoader(search_path)
environment = jinja2.Environment(loader=loader)
for filter_name, filter_function in CustomJinjaFilters.filters().items():
@@ -71,9 +70,9 @@ def load_template(cls, template_name, template_source=None, template_path=None,
configuration = template.render(**template_vars)
except jinja2.exceptions.TemplateNotFound:
raise napalm_base.exceptions.TemplateNotImplemented(
- "Config template {template_name}.j2 is not defined under {path}".format(
+ "Config template {template_name}.j2 not found in search path: {sp}".format(
template_name=template_name,
- path=template_dir_path
+ sp=search_path
)
)
except (jinja2.exceptions.UndefinedError, jinja2.exceptions.TemplateSyntaxError) as jinjaerr:
@@ -177,7 +176,7 @@ def convert(to, who, default=u''):
return default
try:
return to(who)
- except:
+ except: # noqa
return default
@@ -219,9 +218,10 @@ def mac(raw):
return py23_compat.text_type(EUI(raw, dialect=_MACFormat))
-def ip(addr):
+def ip(addr, version=None):
"""
- Converts a raw string to a valid IP address.
+ Converts a raw string to a valid IP address. Optional version argument will detect that \
+ object matches specified version.
Motivation: the groups of the IP addreses may contain leading zeros. IPv6 addresses can \
contain sometimes uppercase characters. E.g.: 2001:0dB8:85a3:0000:0000:8A2e:0370:7334 has \
@@ -229,6 +229,8 @@ def ip(addr):
not the same.
:param raw: the raw string containing the value of the IP Address
+ :param version: (optional) insist on a specific IP address version.
+ :type version: int.
:return: a string containing the IP Address in a standard format (no leading zeros, \
zeros-grouping, lowercase)
@@ -239,4 +241,17 @@ def ip(addr):
>>> ip('2001:0dB8:85a3:0000:0000:8A2e:0370:7334')
u'2001:db8:85a3::8a2e:370:7334'
"""
- return py23_compat.text_type(IPAddress(addr))
+ addr_obj = IPAddress(addr)
+ if version and addr_obj.version != version:
... 677 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/napalm-base.git
More information about the Python-modules-commits
mailing list