napalm-base-0.25.0/0000755000175000017500000000000013150265743014626 5ustar travistravis00000000000000napalm-base-0.25.0/napalm_base/0000755000175000017500000000000013150265743017070 5ustar travistravis00000000000000napalm-base-0.25.0/napalm_base/clitools/0000755000175000017500000000000013150265743020720 5ustar travistravis00000000000000napalm-base-0.25.0/napalm_base/clitools/__init__.py0000644000175000017500000000000013150265625023016 0ustar travistravis00000000000000napalm-base-0.25.0/napalm_base/clitools/cl_napalm.py0000755000175000017500000002047713150265625023234 0ustar travistravis00000000000000# 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) @debugging("get_network_driver") def call_get_network_driver(vendor): return get_network_driver(vendor) @debugging("__init__") def call_instantiating_object(driver, *args, **kwargs): return driver(*args, **kwargs) @debugging("pre_connection_tests") def call_pre_connection(driver): driver.pre_connection_tests() @debugging("connection_tests") def call_connection(device): device.connection_tests() @debugging("post_connection_tests") def call_post_connection(device): device.post_connection_tests() @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)) @debugging("close") def call_close(device): return device.close() @debugging("open") def call_open_device(device): return device.open() @debugging("load_replace_candidate") def call_load_replace_candidate(device, *args, **kwargs): return device.load_replace_candidate(*args, **kwargs) @debugging("load_merge_candidate") def call_load_merge_candidate(device, *args, **kwargs): return device.load_merge_candidate(*args, **kwargs) @debugging("compare_config") def call_compare_config(device, *args, **kwargs): diff = device.compare_config(*args, **kwargs) logger.debug("Gathered diff:") print(diff) return diff @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 @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)) @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() napalm-base-0.25.0/napalm_base/clitools/cl_napalm_configure.py0000644000175000017500000000412713150265625025264 0ustar travistravis00000000000000# -*- coding: utf-8 -*- ''' NAPALM CLI Tools: configure =========================== Deploy device config from the shell. ''' # 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.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): logger.debug('Getting driver for OS "{driver}"'.format(driver=vendor)) driver = get_network_driver(vendor) optional_args = parse_optional_args(optional_args) logger.debug('Connecting to device "{}" with user "{}" and optional_args={}'.format( hostname, user, optional_args)) with driver(hostname, user, password, optional_args=optional_args) as device: logger.debug('Strategy for loading configuration is "{strategy}"'.format(strategy=strategy)) if strategy == 'replace': strategy_method = device.load_replace_candidate elif strategy == 'merge': strategy_method = device.load_merge_candidate logger.debug('Loading configuration file "{config}"'.format(config=config_file)) strategy_method(filename=config_file) logger.debug('Comparing configuration') diff = device.compare_config() if dry_run: logger.debug('Dry-run. Discarding configuration.') else: logger.debug('Committing configuration') device.commit_config() logger.debug('Closing session') return diff def main(): args = build_help(configure=True) configure_logging(logger, args.debug) print(run(args.vendor, args.hostname, args.user, args.password, args.strategy, args.optional_args, args.config_file, args.dry_run)) sys.exit(0) if __name__ == '__main__': main() napalm-base-0.25.0/napalm_base/clitools/cl_napalm_test.py0000644000175000017500000000264413150265625024264 0ustar travistravis00000000000000# -*- coding: utf-8 -*- ''' NAPALM CLI Tools: test connectivity =================================== Module to test connectivity with the network device through NAPALM. ''' from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals # import helpers 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(): args = build_help(connect_test=True) configure_logging(logger, args.debug) logger.debug('Getting driver for OS "{driver}"'.format(driver=args.vendor)) driver = get_network_driver(args.vendor) optional_args = parse_optional_args(args.optional_args) logger.debug('Connecting to device "{}" with user "{}" and optional_args={}'.format( args.hostname, args.user, optional_args)) with driver(args.hostname, args.user, args.password, optional_args=optional_args) as device: logger.debug('Successfully connected to the device: {}'.format(device.hostname)) print('Successfully connected to the device') sys.exit(0) if __name__ == '__main__': main() napalm-base-0.25.0/napalm_base/clitools/cl_napalm_validate.py0000755000175000017500000000254013150265625025074 0ustar travistravis00000000000000# -*- coding: utf-8 -*- ''' NAPALM CLI Tools: validate =========================== Validating deployments from the shell. ''' # 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.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(): args = build_help(validate=True) configure_logging(logger, args.debug) logger.debug('Getting driver for OS "{driver}"'.format(driver=args.vendor)) driver = get_network_driver(args.vendor) optional_args = parse_optional_args(args.optional_args) logger.debug('Connecting to device "{}" with user "{}" and optional_args={}'.format( args.hostname, args.user, optional_args)) with driver(args.hostname, args.user, args.password, optional_args=optional_args) as device: logger.debug('Generating compliance report') print(json.dumps(device.compliance_report(args.validation_file), indent=4)) logger.debug('Closing session') sys.exit(0) if __name__ == '__main__': main() napalm-base-0.25.0/napalm_base/clitools/helpers.py0000644000175000017500000000713413150265625022740 0ustar travistravis00000000000000# -*- coding: utf-8 -*- ''' NAPALM CLI Tools: helpers ========================= Defines helpers for the CLI tools. ''' from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals # stdlib import ast import sys import logging import getpass import argparse import warnings 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', 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.' ) if configure: parser.add_argument( '--strategy', '-s', dest='strategy', action='store', choices=['replace', 'merge'], default='replace', help='Strategy to use to deploy configuration. Default: replace.' ) parser.add_argument( dest='config_file', action='store', help='File containing the configuration you want to deploy.' ) parser.add_argument( '--dry-run', '-d', dest='dry_run', action='store_true', default=None, help='Only returns diff, it does not deploy the configuration.', ) elif validate: parser.add_argument( '--validation_file', '-f', 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 configure_logging(logger, debug): if debug: logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.INFO) ch = logging.StreamHandler(sys.stdout) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) logger.addHandler(ch) return logger def parse_optional_args(optional_args): if optional_args is not None: return {x.split('=')[0]: ast.literal_eval(x.split('=')[1]) for x in optional_args.split(',')} return {} napalm-base-0.25.0/napalm_base/test/0000755000175000017500000000000013150265743020047 5ustar travistravis00000000000000napalm-base-0.25.0/napalm_base/test/__init__.py0000644000175000017500000000000013150265625022145 0ustar travistravis00000000000000napalm-base-0.25.0/napalm_base/test/base.py0000644000175000017500000005117213150265625021340 0ustar travistravis00000000000000# Copyright 2015 Spotify AB. All rights reserved. # # The contents of this file are licensed under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with the # License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under # the License. # Python3 support from __future__ import print_function from __future__ import unicode_literals import difflib from napalm_base import exceptions from napalm_base.test import models from unittest import SkipTest # text_type is 'unicode' for py2 and 'str' for py3 from napalm_base.utils.py23_compat import text_type class TestConfigNetworkDriver(object): @classmethod def setup_class(cls): """Added for py.test/nosetests compatibility""" cls.setUpClass() @classmethod def teardown_class(cls): """Added for py.test/nosetests compatibility""" cls.tearDownClass() @classmethod def tearDownClass(cls): cls.device.load_replace_candidate(filename='%s/initial.conf' % cls.vendor) cls.device.commit_config() cls.device.close() @staticmethod def read_file(filename): with open(filename, 'r') as f: return f.read().strip() @staticmethod def print_diff_strings(orig, new): for line in difflib.context_diff(orig.splitlines(), new.splitlines()): print(line) def test_replacing_and_committing_config(self): try: self.device.load_replace_candidate(filename='%s/new_good.conf' % self.vendor) self.device.commit_config() except NotImplementedError: raise SkipTest() # The diff should be empty as the configuration has been committed already diff = self.device.compare_config() # Reverting changes self.device.load_replace_candidate(filename='%s/initial.conf' % self.vendor) self.device.commit_config() self.assertEqual(len(diff), 0) def test_replacing_config_with_typo(self): result = False try: self.device.load_replace_candidate(filename='%s/new_typo.conf' % self.vendor) self.device.commit_config() except NotImplementedError: raise SkipTest() except exceptions.ReplaceConfigException: self.device.load_replace_candidate(filename='%s/initial.conf' % self.vendor) diff = self.device.compare_config() self.device.discard_config() result = True and len(diff) == 0 self.assertTrue(result) def test_replacing_config_and_diff_and_discard(self): intended_diff = self.read_file('%s/new_good.diff' % self.vendor) self.device.load_replace_candidate(filename='%s/new_good.conf' % self.vendor) commit_diff = self.device.compare_config() print(commit_diff) self.device.discard_config() discard_diff = self.device.compare_config() self.device.discard_config() result = (commit_diff == intended_diff) and (discard_diff == '') self.assertTrue(result) def test_replacing_config_and_rollback(self): self.device.load_replace_candidate(filename='%s/new_good.conf' % self.vendor) orig_diff = self.device.compare_config() self.device.commit_config() # Now we rollback changes replace_config_diff = self.device.compare_config() self.device.rollback() # We try to load the config again. If the rollback was successful new diff should be # like the first one self.device.load_replace_candidate(filename='%s/new_good.conf' % self.vendor) last_diff = self.device.compare_config() self.device.discard_config() result = (orig_diff == last_diff) and (len(replace_config_diff) == 0) self.assertTrue(result) def test_merge_configuration(self): intended_diff = self.read_file('%s/merge_good.diff' % self.vendor) self.device.load_merge_candidate(filename='%s/merge_good.conf' % self.vendor) self.device.commit_config() # Reverting changes self.device.load_replace_candidate(filename='%s/initial.conf' % self.vendor) diff = self.device.compare_config() print(diff) self.device.commit_config() self.assertEqual(diff, intended_diff) def test_merge_configuration_typo_and_rollback(self): result = False try: self.device.load_merge_candidate(filename='%s/merge_typo.conf' % self.vendor) self.device.compare_config() self.device.commit_config() raise Exception("We shouldn't be here") except exceptions.MergeConfigException: # We load the original config as candidate. If the commit failed cleanly the # compare_config should be empty self.device.load_replace_candidate(filename='%s/initial.conf' % self.vendor) result = self.device.compare_config() == '' self.device.discard_config() self.assertTrue(result) def test_load_template(self): """Test load_template method.""" self.device.load_template('set_hostname', hostname='my-hostname') diff = self.device.compare_config() self.device.discard_config() self.assertTrue(diff is not '') class TestGettersNetworkDriver(object): @staticmethod def _test_model(model, data): same_keys = set(model.keys()) == set(data.keys()) if not same_keys: print("model_keys: {}\ndata_keys: {}".format(sorted(model.keys()), sorted(data.keys()))) correct_class = True for key, instance_class in model.items(): same_class = isinstance(data[key], instance_class) correct_class = correct_class and same_class if not same_class: print("key: {}\nmodel_class: {}\ndata_class: {}".format( key, instance_class, data[key].__class__)) return correct_class and same_keys def test_get_firewall_policies(self): try: policies = self.device.get_firewall_policies() except NotImplementedError: raise SkipTest() result = len(policies) > 0 for policy_name, policy_details in policies.items(): for policy_term in policy_details: result = result and self._test_model(models.firewall_policies, policy_term) self.assertTrue(result) def test_is_alive(self): try: alive = self.device.is_alive() except NotImplementedError: raise SkipTest() result = self._test_model(models.alive, alive) self.assertTrue(result) def test_get_facts(self): try: facts = self.device.get_facts() except NotImplementedError: raise SkipTest() result = self._test_model(models.facts, facts) self.assertTrue(result) def test_get_interfaces(self): try: get_interfaces = self.device.get_interfaces() except NotImplementedError: raise SkipTest() result = len(get_interfaces) > 0 for interface, interface_data in get_interfaces.items(): result = result and self._test_model(models.interface, interface_data) self.assertTrue(result) def test_get_lldp_neighbors(self): try: get_lldp_neighbors = self.device.get_lldp_neighbors() except NotImplementedError: raise SkipTest() result = len(get_lldp_neighbors) > 0 for interface, neighbor_list in get_lldp_neighbors.items(): for neighbor in neighbor_list: result = result and self._test_model(models.lldp_neighbors, neighbor) self.assertTrue(result) def test_get_interfaces_counters(self): try: get_interfaces_counters = self.device.get_interfaces_counters() except NotImplementedError: raise SkipTest() result = len(self.device.get_interfaces_counters()) > 0 for interface, interface_data in get_interfaces_counters.items(): result = result and self._test_model(models.interface_counters, interface_data) self.assertTrue(result) def test_get_environment(self): try: environment = self.device.get_environment() except NotImplementedError: raise SkipTest() result = len(environment) > 0 for fan, fan_data in environment['fans'].items(): result = result and self._test_model(models.fan, fan_data) for power, power_data in environment['power'].items(): result = result and self._test_model(models.power, power_data) for temperature, temperature_data in environment['temperature'].items(): result = result and self._test_model(models.temperature, temperature_data) for cpu, cpu_data in environment['cpu'].items(): result = result and self._test_model(models.cpu, cpu_data) result = result and self._test_model(models.memory, environment['memory']) self.assertTrue(result) def test_get_bgp_neighbors(self): try: get_bgp_neighbors = self.device.get_bgp_neighbors() except NotImplementedError: raise SkipTest() result = 'global' in get_bgp_neighbors.keys() if not result: print('global is not part of the returned vrfs') else: for vrf, vrf_data in get_bgp_neighbors.items(): result = result and isinstance(vrf_data['router_id'], text_type) if not result: print('router_id is not {}'.format(text_type)) for peer, peer_data in vrf_data['peers'].items(): result = result and self._test_model(models.peer, peer_data) for af, af_data in peer_data['address_family'].items(): result = result and self._test_model(models.af, af_data) self.assertTrue(result) def test_get_lldp_neighbors_detail(self): try: get_lldp_neighbors_detail = self.device.get_lldp_neighbors_detail() except NotImplementedError: raise SkipTest() result = len(get_lldp_neighbors_detail) > 0 for interface, neighbor_list in get_lldp_neighbors_detail.items(): for neighbor in neighbor_list: result = result and self._test_model(models.lldp_neighbors_detail, neighbor) self.assertTrue(result) def test_get_bgp_config(self): try: get_bgp_config = self.device.get_bgp_config() except NotImplementedError: raise SkipTest() result = len(get_bgp_config) > 0 for bgp_group in get_bgp_config.values(): result = result and self._test_model(models.bgp_config_group, bgp_group) for bgp_neighbor in bgp_group.get('neighbors', {}).values(): result = result and self._test_model(models.bgp_config_neighbor, bgp_neighbor) self.assertTrue(result) def test_get_bgp_neighbors_detail(self): try: get_bgp_neighbors_detail = self.device.get_bgp_neighbors_detail() except NotImplementedError: raise SkipTest() result = len(get_bgp_neighbors_detail) > 0 for vrf, vrf_ases in get_bgp_neighbors_detail.items(): result = result and isinstance(vrf, text_type) for remote_as, neighbor_list in vrf_ases.items(): result = result and isinstance(remote_as, int) for neighbor in neighbor_list: result = result and self._test_model(models.peer_details, neighbor) self.assertTrue(result) def test_get_arp_table(self): try: get_arp_table = self.device.get_arp_table() except NotImplementedError: raise SkipTest() result = len(get_arp_table) > 0 for arp_entry in get_arp_table: result = result and self._test_model(models.arp_table, arp_entry) self.assertTrue(result) def test_get_ntp_peers(self): try: get_ntp_peers = self.device.get_ntp_peers() except NotImplementedError: raise SkipTest() result = len(get_ntp_peers) > 0 for peer, peer_details in get_ntp_peers.items(): result = result and isinstance(peer, text_type) result = result and self._test_model(models.ntp_peer, peer_details) self.assertTrue(result) def test_get_ntp_servers(self): try: get_ntp_servers = self.device.get_ntp_servers() except NotImplementedError: raise SkipTest() result = len(get_ntp_servers) > 0 for server, server_details in get_ntp_servers.items(): result = result and isinstance(server, text_type) result = result and self._test_model(models.ntp_server, server_details) self.assertTrue(result) def test_get_ntp_stats(self): try: get_ntp_stats = self.device.get_ntp_stats() except NotImplementedError: raise SkipTest() result = len(get_ntp_stats) > 0 for ntp_peer_details in get_ntp_stats: result = result and self._test_model(models.ntp_stats, ntp_peer_details) self.assertTrue(result) def test_get_interfaces_ip(self): try: get_interfaces_ip = self.device.get_interfaces_ip() except NotImplementedError: raise SkipTest() result = len(get_interfaces_ip) > 0 for interface, interface_details in get_interfaces_ip.items(): ipv4 = interface_details.get('ipv4', {}) ipv6 = interface_details.get('ipv6', {}) for ip, ip_details in ipv4.items(): result = result and self._test_model(models.interfaces_ip, ip_details) for ip, ip_details in ipv6.items(): result = result and self._test_model(models.interfaces_ip, ip_details) self.assertTrue(result) def test_get_mac_address_table(self): try: get_mac_address_table = self.device.get_mac_address_table() except NotImplementedError: raise SkipTest() result = len(get_mac_address_table) > 0 for mac_table_entry in get_mac_address_table: result = result and self._test_model(models.mac_address_table, mac_table_entry) self.assertTrue(result) def test_get_route_to(self): destination = '1.0.4.0/24' protocol = 'bgp' try: get_route_to = self.device.get_route_to(destination=destination, protocol=protocol) except NotImplementedError: raise SkipTest() result = len(get_route_to) > 0 for prefix, routes in get_route_to.items(): for route in routes: result = result and self._test_model(models.route, route) self.assertTrue(result) def test_get_snmp_information(self): try: get_snmp_information = self.device.get_snmp_information() except NotImplementedError: raise SkipTest() result = len(get_snmp_information) > 0 for snmp_entry in get_snmp_information: result = result and self._test_model(models.snmp, get_snmp_information) for community, community_data in get_snmp_information['community'].items(): result = result and self._test_model(models.snmp_community, community_data) self.assertTrue(result) def test_get_probes_config(self): try: get_probes_config = self.device.get_probes_config() except NotImplementedError: raise SkipTest() result = len(get_probes_config) > 0 for probe_name, probe_tests in get_probes_config.items(): for test_name, test_config in probe_tests.items(): result = result and self._test_model(models.probe_test, test_config) self.assertTrue(result) def test_get_probes_results(self): try: get_probes_results = self.device.get_probes_results() except NotImplementedError: raise SkipTest() result = len(get_probes_results) > 0 for probe_name, probe_tests in get_probes_results.items(): for test_name, test_results in probe_tests.items(): result = result and self._test_model(models.probe_test_results, test_results) self.assertTrue(result) def test_ping(self): destination = '8.8.8.8' try: get_ping = self.device.ping(destination) except NotImplementedError: raise SkipTest() result = isinstance(get_ping.get('success'), dict) ping_results = get_ping.get('success', {}) result = result and self._test_model(models.ping, ping_results) for ping_result in ping_results.get('results', []): result = result and self._test_model(models.ping_result, ping_result) self.assertTrue(result) def test_traceroute(self): destination = '8.8.8.8' try: get_traceroute = self.device.traceroute(destination) except NotImplementedError: raise SkipTest() result = isinstance(get_traceroute.get('success'), dict) traceroute_results = get_traceroute.get('success', {}) for hope_id, hop_result in traceroute_results.items(): for probe_id, probe_result in hop_result.get('probes', {}).items(): result = result and self._test_model(models.traceroute, probe_result) self.assertTrue(result) def test_get_users(self): try: get_users = self.device.get_users() except NotImplementedError: raise SkipTest() result = len(get_users) for user, user_details in get_users.items(): result = result and self._test_model(models.users, user_details) result = result and (0 <= user_details.get('level') <= 15) self.assertTrue(result) def test_get_optics(self): try: get_optics = self.device.get_optics() except NotImplementedError: raise SkipTest() assert isinstance(get_optics, dict) for iface, iface_data in get_optics.items(): assert isinstance(iface, text_type) for channel in iface_data['physical_channels']['channel']: assert len(channel) == 2 assert isinstance(channel['index'], int) for field in ['input_power', 'output_power', 'laser_bias_current']: assert len(channel['state'][field]) == 4 assert isinstance(channel['state'][field]['instant'], float) assert isinstance(channel['state'][field]['avg'], float) assert isinstance(channel['state'][field]['min'], float) assert isinstance(channel['state'][field]['max'], float) def test_get_config(self): """Test get_config method.""" try: get_config = self.device.get_config() except NotImplementedError: raise SkipTest() assert isinstance(get_config, dict) assert self._test_model(models.config, get_config) def test_get_config_filtered(self): """Test get_config method.""" for config in ['running', 'startup', 'candidate']: try: get_config = self.device.get_config(retrieve=config) except NotImplementedError: raise SkipTest() assert get_config['candidate'] == "" if config != "candidate" else True assert get_config['startup'] == "" if config != "startup" else True assert get_config['running'] == "" if config != "running" else True def test_get_network_instances(self): """Test get_network_instances method.""" try: get_network_instances = self.device.get_network_instances() except NotImplementedError: raise SkipTest() result = isinstance(get_network_instances, dict) for network_instance_name, network_instance in get_network_instances.items(): result = result and self._test_model(models.network_instance, network_instance) result = result and \ self._test_model(models.network_instance_state, network_instance['state']) result = result and \ self._test_model(models.network_instance_interfaces, network_instance['interfaces']) self.assertTrue(result) napalm-base-0.25.0/napalm_base/test/conftest.py0000644000175000017500000000322113150265625022243 0ustar travistravis00000000000000"""Test fixtures.""" # Python3 support from __future__ import print_function from __future__ import unicode_literals import ast import json import os NAPALM_TEST_MOCK = ast.literal_eval(os.getenv('NAPALM_TEST_MOCK', default="1")) NAPALM_HOSTNAME = os.getenv('NAPALM_HOSTNAME', default='127.0.0.1') NAPALM_USERNAME = os.getenv('NAPALM_USERNAME', default='vagrant') NAPALM_PASSWORD = os.getenv('NAPALM_PASSWORD', default='vagrant') NAPALM_OPTIONAL_ARGS = json.loads(os.getenv('NAPALM_OPTIONAL_ARGS', default='{"port": 12443}')) def set_device_parameters(request): """Set up the class.""" if NAPALM_TEST_MOCK: driver = request.cls.patched_driver else: driver = request.cls.driver request.cls.device = driver(NAPALM_HOSTNAME, NAPALM_USERNAME, NAPALM_PASSWORD, timeout=60, optional_args=NAPALM_OPTIONAL_ARGS) request.cls.device.open() def pytest_generate_tests(metafunc, basefile): """Generate test cases dynamically.""" if metafunc.function.__dict__.get('build_test_cases', False): path = os.path.join(os.path.dirname(basefile), 'mocked_data', metafunc.function.__name__) if os.path.exists(path): sub_folders = os.listdir(path) else: sub_folders = [] test_cases = [] for test_case in sub_folders: if os.path.isdir(os.path.join(path, test_case)): test_cases.append(test_case) if not test_cases: test_cases.append("no_test_case_found") metafunc.parametrize("test_case", test_cases) napalm-base-0.25.0/napalm_base/test/double.py0000644000175000017500000000463113150265625021676 0ustar travistravis00000000000000"""Base class for Test doubles.""" # Python3 support from __future__ import print_function from __future__ import unicode_literals import json import re import os import sys class BaseTestDouble(object): """Base class for test doubles.""" def __init__(self, *args, **kwargs): """Initiate object.""" self.current_test = '' self.current_test_case = '' def find_file(self, filename): """Find the necessary file for the given test case.""" # Find base_dir of submodule module_dir = os.path.dirname(sys.modules[self.__module__].__file__) full_path = os.path.join(module_dir, 'mocked_data', self.current_test, self.current_test_case, filename) if os.path.exists(full_path): return full_path else: raise IOError("Couldn't find file with mocked data: {}".format(full_path)) @staticmethod def sanitize_text(text): """Remove some weird characters from text, useful for building filenames from commands.""" regexp = '[^a-zA-Z0-9]' return re.sub(regexp, '_', text)[0:150] @staticmethod def read_json_file(filename): """Parse a json file and return its content.""" with open(filename) as data_file: return json.load(data_file) @staticmethod def read_txt_file(filename): """Return the content of a file.""" with open(filename) as data_file: return data_file.read() @property def expected_result(self): """Return the expected result for the current test case.""" filename = self.find_file('expected_result.json') with open(filename, mode='r') as f: try: return json.loads(f.read()) except ValueError: raise ValueError("No JSON object could be decoded on filename: {}".format(filename)) def _string_key_to_int(param): """For a given dictionary, convert all strings that represent a number into an int.""" new_dict = {} if isinstance(param, list): return [_string_key_to_int(element) for element in param] elif isinstance(param, dict): for key, value in param.items(): try: new_dict[int(key)] = _string_key_to_int(value) except ValueError: new_dict[key] = _string_key_to_int(value) return new_dict else: return param napalm-base-0.25.0/napalm_base/test/getters.py0000644000175000017500000004504713150265625022107 0ustar travistravis00000000000000"""Testing framework.""" from __future__ import print_function from __future__ import unicode_literals import functools # python2/3 support try: from itertools import izip_longest as zip_longest except ImportError: from itertools import zip_longest import inspect import json import pytest from napalm_base.test import helpers from napalm_base.test import models from napalm_base import NetworkDriver # text_type is 'unicode' for py2 and 'str' for py3 from napalm_base.utils.py23_compat import text_type from napalm_base.test import conftest def list_dicts_diff(prv, nxt): """Compare two lists of dicts.""" result = [] for prv_element, nxt_element in zip_longest(prv, nxt, fillvalue={}): intermediate_result = dict_diff(prv_element, nxt_element) if intermediate_result: result.append(intermediate_result) return result def dict_diff(prv, nxt): """Return a dict of keys that differ with another config object.""" keys = set(list(prv.keys()) + list(nxt.keys())) result = {} for k in keys: if isinstance(prv.get(k), dict): if isinstance(nxt.get(k), dict): "If both are dicts we do a recursive call." diff = dict_diff(prv.get(k), nxt.get(k)) if diff: result[k] = diff else: "If only one is a dict they are clearly different" result[k] = {'result': prv.get(k), 'expected': nxt.get(k)} else: "Ellipsis is a wildcard.""" if prv.get(k) != nxt.get(k) and nxt.get(k) != "...": result[k] = {'result': prv.get(k), 'expected': nxt.get(k)} return result def wrap_test_cases(func): """Wrap test cases.""" func.__dict__['build_test_cases'] = True @functools.wraps(func) def mock_wrapper(cls, test_case): for patched_attr in cls.device.patched_attrs: attr = getattr(cls.device, patched_attr) attr.current_test = func.__name__ attr.current_test_case = test_case try: # This is an ugly, ugly, ugly hack because some python objects don't load # as expected. For example, dicts where integers are strings result = json.loads(json.dumps(func(cls, test_case))) except IOError: if test_case == "no_test_case_found": pytest.fail("No test case for '{}' found".format(func.__name__)) else: raise except NotImplementedError: pytest.skip("Method not implemented") return # This is an ugly, ugly, ugly hack because some python objects don't load # as expected. For example, dicts where integers are strings try: expected_result = attr.expected_result except IOError as e: raise Exception("{}. Actual result was: {}".format(e, json.dumps(result))) if isinstance(result, list): diff = list_dicts_diff(result, expected_result) else: diff = dict_diff(result, expected_result) if diff: print("Resulting JSON object was: {}".format(json.dumps(result))) raise AssertionError("Expected result varies on some keys {}".format( json.dumps(diff))) for patched_attr in cls.device.patched_attrs: attr = getattr(cls.device, patched_attr) attr.current_test = '' # Empty them to avoid side effects attr.current_test_case = '' # Empty them to avoid side effects return result @functools.wraps(func) def real_wrapper(cls, test_case): try: return func(cls, test_case) except NotImplementedError: pytest.skip("Method not implemented") return if conftest.NAPALM_TEST_MOCK: return mock_wrapper else: return real_wrapper class BaseTestGetters(object): """Base class for testing drivers.""" def test_method_signatures(self): """Test that all methods have the same signature.""" errors = {} cls = self.driver # Create fictional driver instance (py3 needs bound methods) tmp_obj = cls(hostname='test', username='admin', password='pwd') attrs = [m for m, v in inspect.getmembers(tmp_obj)] for attr in attrs: func = getattr(tmp_obj, attr) if attr.startswith('_') or not inspect.ismethod(func): continue try: orig = getattr(NetworkDriver, attr) orig_spec = inspect.getargspec(orig) except AttributeError: orig_spec = 'Method does not exist in napalm_base' func_spec = inspect.getargspec(func) if orig_spec != func_spec: errors[attr] = (orig_spec, func_spec) EXTRA_METHODS = ['__init__', ] for method in EXTRA_METHODS: orig_spec = inspect.getargspec(getattr(NetworkDriver, method)) func_spec = inspect.getargspec(getattr(cls, method)) if orig_spec != func_spec: errors[attr] = (orig_spec, func_spec) assert not errors, "Some methods vary. \n{}".format(errors.keys()) @wrap_test_cases def test_is_alive(self, test_case): """Test is_alive method.""" alive = self.device.is_alive() assert helpers.test_model(models.alive, alive) return alive @wrap_test_cases def test_get_facts(self, test_case): """Test get_facts method.""" facts = self.device.get_facts() assert helpers.test_model(models.facts, facts) return facts @wrap_test_cases def test_get_interfaces(self, test_case): """Test get_interfaces.""" get_interfaces = self.device.get_interfaces() assert len(get_interfaces) > 0 for interface, interface_data in get_interfaces.items(): assert helpers.test_model(models.interface, interface_data) return get_interfaces @wrap_test_cases def test_get_lldp_neighbors(self, test_case): """Test get_lldp_neighbors.""" get_lldp_neighbors = self.device.get_lldp_neighbors() assert len(get_lldp_neighbors) > 0 for interface, neighbor_list in get_lldp_neighbors.items(): for neighbor in neighbor_list: assert helpers.test_model(models.lldp_neighbors, neighbor) return get_lldp_neighbors @wrap_test_cases def test_get_interfaces_counters(self, test_case): """Test get_interfaces_counters.""" get_interfaces_counters = self.device.get_interfaces_counters() assert len(self.device.get_interfaces_counters()) > 0 for interface, interface_data in get_interfaces_counters.items(): assert helpers.test_model(models.interface_counters, interface_data) return get_interfaces_counters @wrap_test_cases def test_get_environment(self, test_case): """Test get_environment.""" environment = self.device.get_environment() assert len(environment) > 0 for fan, fan_data in environment['fans'].items(): assert helpers.test_model(models.fan, fan_data) for power, power_data in environment['power'].items(): assert helpers.test_model(models.power, power_data) for temperature, temperature_data in environment['temperature'].items(): assert helpers.test_model(models.temperature, temperature_data) for cpu, cpu_data in environment['cpu'].items(): assert helpers.test_model(models.cpu, cpu_data) assert helpers.test_model(models.memory, environment['memory']) return environment @wrap_test_cases def test_get_bgp_neighbors(self, test_case): """Test get_bgp_neighbors.""" get_bgp_neighbors = self.device.get_bgp_neighbors() assert 'global' in get_bgp_neighbors.keys() for vrf, vrf_data in get_bgp_neighbors.items(): assert isinstance(vrf_data['router_id'], text_type) for peer, peer_data in vrf_data['peers'].items(): assert helpers.test_model(models.peer, peer_data) for af, af_data in peer_data['address_family'].items(): assert helpers.test_model(models.af, af_data) return get_bgp_neighbors @wrap_test_cases def test_get_lldp_neighbors_detail(self, test_case): """Test get_lldp_neighbors_detail.""" get_lldp_neighbors_detail = self.device.get_lldp_neighbors_detail() assert len(get_lldp_neighbors_detail) > 0 for interface, neighbor_list in get_lldp_neighbors_detail.items(): for neighbor in neighbor_list: assert helpers.test_model(models.lldp_neighbors_detail, neighbor) return get_lldp_neighbors_detail @wrap_test_cases def test_get_bgp_config(self, test_case): """Test get_bgp_config.""" get_bgp_config = self.device.get_bgp_config() assert len(get_bgp_config) > 0 for bgp_group in get_bgp_config.values(): assert helpers.test_model(models.bgp_config_group, bgp_group) for bgp_neighbor in bgp_group.get('neighbors', {}).values(): assert helpers.test_model(models.bgp_config_neighbor, bgp_neighbor) return get_bgp_config @wrap_test_cases def test_get_bgp_neighbors_detail(self, test_case): """Test get_bgp_neighbors_detail.""" get_bgp_neighbors_detail = self.device.get_bgp_neighbors_detail() assert len(get_bgp_neighbors_detail) > 0 for vrf, vrf_ases in get_bgp_neighbors_detail.items(): assert isinstance(vrf, text_type) for remote_as, neighbor_list in vrf_ases.items(): assert isinstance(remote_as, int) for neighbor in neighbor_list: assert helpers.test_model(models.peer_details, neighbor) return get_bgp_neighbors_detail @wrap_test_cases def test_get_arp_table(self, test_case): """Test get_arp_table.""" get_arp_table = self.device.get_arp_table() assert len(get_arp_table) > 0 for arp_entry in get_arp_table: assert helpers.test_model(models.arp_table, arp_entry) return get_arp_table @wrap_test_cases def test_get_ntp_peers(self, test_case): """Test get_ntp_peers.""" get_ntp_peers = self.device.get_ntp_peers() assert len(get_ntp_peers) > 0 for peer, peer_details in get_ntp_peers.items(): assert isinstance(peer, text_type) assert helpers.test_model(models.ntp_peer, peer_details) return get_ntp_peers @wrap_test_cases def test_get_ntp_servers(self, test_case): """Test get_ntp_servers.""" get_ntp_servers = self.device.get_ntp_servers() assert len(get_ntp_servers) > 0 for server, server_details in get_ntp_servers.items(): assert isinstance(server, text_type) assert helpers.test_model(models.ntp_server, server_details) return get_ntp_servers @wrap_test_cases def test_get_ntp_stats(self, test_case): """Test get_ntp_stats.""" get_ntp_stats = self.device.get_ntp_stats() assert len(get_ntp_stats) > 0 for ntp_peer_details in get_ntp_stats: assert helpers.test_model(models.ntp_stats, ntp_peer_details) return get_ntp_stats @wrap_test_cases def test_get_interfaces_ip(self, test_case): """Test get_interfaces_ip.""" get_interfaces_ip = self.device.get_interfaces_ip() assert len(get_interfaces_ip) > 0 for interface, interface_details in get_interfaces_ip.items(): ipv4 = interface_details.get('ipv4', {}) ipv6 = interface_details.get('ipv6', {}) for ip, ip_details in ipv4.items(): assert helpers.test_model(models.interfaces_ip, ip_details) for ip, ip_details in ipv6.items(): assert helpers.test_model(models.interfaces_ip, ip_details) return get_interfaces_ip @wrap_test_cases def test_get_mac_address_table(self, test_case): """Test get_mac_address_table.""" get_mac_address_table = self.device.get_mac_address_table() assert len(get_mac_address_table) > 0 for mac_table_entry in get_mac_address_table: assert helpers.test_model(models.mac_address_table, mac_table_entry) return get_mac_address_table @wrap_test_cases def test_get_route_to(self, test_case): """Test get_route_to.""" destination = '1.0.4.0/24' protocol = 'bgp' get_route_to = self.device.get_route_to(destination=destination, protocol=protocol) assert len(get_route_to) > 0 for prefix, routes in get_route_to.items(): for route in routes: assert helpers.test_model(models.route, route) return get_route_to @wrap_test_cases def test_get_snmp_information(self, test_case): """Test get_snmp_information.""" get_snmp_information = self.device.get_snmp_information() assert len(get_snmp_information) > 0 for snmp_entry in get_snmp_information: assert helpers.test_model(models.snmp, get_snmp_information) for community, community_data in get_snmp_information['community'].items(): assert helpers.test_model(models.snmp_community, community_data) return get_snmp_information @wrap_test_cases def test_get_probes_config(self, test_case): """Test get_probes_config.""" get_probes_config = self.device.get_probes_config() assert len(get_probes_config) > 0 for probe_name, probe_tests in get_probes_config.items(): for test_name, test_config in probe_tests.items(): assert helpers.test_model(models.probe_test, test_config) return get_probes_config @wrap_test_cases def test_get_probes_results(self, test_case): """Test get_probes_results.""" get_probes_results = self.device.get_probes_results() assert len(get_probes_results) > 0 for probe_name, probe_tests in get_probes_results.items(): for test_name, test_results in probe_tests.items(): assert helpers.test_model(models.probe_test_results, test_results) return get_probes_results @wrap_test_cases def test_ping(self, test_case): """Test ping.""" destination = '8.8.8.8' get_ping = self.device.ping(destination) assert isinstance(get_ping.get('success'), dict) ping_results = get_ping.get('success', {}) assert helpers.test_model(models.ping, ping_results) for ping_result in ping_results.get('results', []): assert helpers.test_model(models.ping_result, ping_result) return get_ping @wrap_test_cases def test_traceroute(self, test_case): """Test traceroute.""" destination = '8.8.8.8' get_traceroute = self.device.traceroute(destination) assert isinstance(get_traceroute.get('success'), dict) traceroute_results = get_traceroute.get('success', {}) for hope_id, hop_result in traceroute_results.items(): for probe_id, probe_result in hop_result.get('probes', {}).items(): assert helpers.test_model(models.traceroute, probe_result) return get_traceroute @wrap_test_cases def test_get_users(self, test_case): """Test get_users.""" get_users = self.device.get_users() assert len(get_users) for user, user_details in get_users.items(): assert helpers.test_model(models.users, user_details) assert (0 <= user_details.get('level') <= 15) return get_users @wrap_test_cases def test_get_optics(self, test_case): """Test get_optics.""" get_optics = self.device.get_optics() assert isinstance(get_optics, dict) for iface, iface_data in get_optics.items(): assert isinstance(iface, text_type) for channel in iface_data['physical_channels']['channel']: assert len(channel) == 2 assert isinstance(channel['index'], int) for field in ['input_power', 'output_power', 'laser_bias_current']: assert len(channel['state'][field]) == 4 assert isinstance(channel['state'][field]['instant'], float) assert isinstance(channel['state'][field]['avg'], float) assert isinstance(channel['state'][field]['min'], float) assert isinstance(channel['state'][field]['max'], float) return get_optics @wrap_test_cases def test_get_config(self, test_case): """Test get_config method.""" get_config = self.device.get_config() assert isinstance(get_config, dict) assert helpers.test_model(models.config, get_config) return get_config @wrap_test_cases def test_get_config_filtered(self, test_case): """Test get_config method.""" for config in ['running', 'startup', 'candidate']: get_config = self.device.get_config(retrieve=config) assert get_config['candidate'] == "" if config != "candidate" else True assert get_config['startup'] == "" if config != "startup" else True assert get_config['running'] == "" if config != "running" else True return get_config @wrap_test_cases def test_get_network_instances(self, test_case): """Test get_network_instances method.""" get_network_instances = self.device.get_network_instances() assert isinstance(get_network_instances, dict) for network_instance_name, network_instance in get_network_instances.items(): assert helpers.test_model(models.network_instance, network_instance) assert helpers.test_model(models.network_instance_state, network_instance['state']) assert helpers.test_model(models.network_instance_interfaces, network_instance['interfaces']) return get_network_instances @wrap_test_cases def test_get_firewall_policies(self, test_case): """Test get_firewall_policies method.""" get_firewall_policies = self.device.get_firewall_policies() assert len(get_firewall_policies) > 0 for policy_name, policy_details in get_firewall_policies.items(): for policy_term in policy_details: assert helpers.test_model(models.firewall_policies, policy_term) return get_firewall_policies napalm-base-0.25.0/napalm_base/test/helpers.py0000644000175000017500000000203213150265625022057 0ustar travistravis00000000000000"""Several methods to help with the tests.""" # Python3 support from __future__ import print_function from __future__ import unicode_literals from napalm_base.utils import py23_compat def test_model(model, data): """Return if the dictionary `data` complies with the `model`.""" same_keys = set(model.keys()) == set(data.keys()) if not same_keys: print("model_keys: {}\ndata_keys: {}".format(sorted(model.keys()), sorted(data.keys()))) correct_class = True for key, instance_class in model.items(): correct_class = isinstance(data[key], instance_class) and correct_class # Properly handle PY2 long if py23_compat.PY2: if isinstance(data[key], long) and isinstance(1, instance_class): # noqa correct_class = True and correct_class if not correct_class: print("key: {}\nmodel_class: {}\ndata_class: {}".format( key, instance_class, data[key].__class__)) return correct_class and same_keys napalm-base-0.25.0/napalm_base/test/models.py0000644000175000017500000001507513150265625021713 0ustar travistravis00000000000000 # text_type is 'unicode' for py2 and 'str' for py3 from napalm_base.utils.py23_compat import text_type alive = { 'is_alive': bool } facts = { 'os_version': text_type, 'uptime': int, 'interface_list': list, 'vendor': text_type, 'serial_number': text_type, 'model': text_type, 'hostname': text_type, 'fqdn': text_type } interface = { 'is_up': bool, 'is_enabled': bool, 'description': text_type, 'last_flapped': float, 'speed': int, 'mac_address': text_type, } lldp_neighbors = { 'hostname': text_type, 'port': text_type, } interface_counters = { 'tx_errors': int, 'rx_errors': int, 'tx_discards': int, 'rx_discards': int, 'tx_octets': int, 'rx_octets': int, 'tx_unicast_packets': int, 'rx_unicast_packets': int, 'tx_multicast_packets': int, 'rx_multicast_packets': int, 'tx_broadcast_packets': int, 'rx_broadcast_packets': int, } temperature = { 'is_alert': bool, 'is_critical': bool, 'temperature': float, } power = { 'status': bool, 'output': float, 'capacity': float } memory = { 'used_ram': int, 'available_ram': int, } fan = { 'status': bool, } cpu = { '%usage': float, } peer = { 'is_enabled': bool, 'uptime': int, 'remote_as': int, 'description': text_type, 'remote_id': text_type, 'local_as': int, 'is_up': bool, 'address_family': dict, } af = { 'sent_prefixes': int, 'accepted_prefixes': int, 'received_prefixes': int } lldp_neighbors_detail = { 'parent_interface': text_type, 'remote_port': text_type, 'remote_chassis_id': text_type, 'remote_port': text_type, 'remote_port_description': text_type, 'remote_system_name': text_type, 'remote_system_description': text_type, 'remote_system_capab': text_type, 'remote_system_enable_capab': text_type } bgp_config_group = { 'type': text_type, 'description': text_type, 'apply_groups': list, 'multihop_ttl': int, 'multipath': bool, 'local_address': text_type, 'local_as': int, 'remote_as': int, 'import_policy': text_type, 'export_policy': text_type, 'remove_private_as': bool, 'prefix_limit': dict, 'neighbors': dict } bgp_config_neighbor = { 'description': text_type, 'import_policy': text_type, 'export_policy': text_type, 'local_address': text_type, 'authentication_key': text_type, 'nhs': bool, 'route_reflector_client': bool, 'local_as': int, 'remote_as': int, 'prefix_limit': dict } peer_details = { 'up': bool, 'local_as': int, 'remote_as': int, 'router_id': text_type, 'local_address': text_type, 'routing_table': text_type, 'local_address_configured': bool, 'local_port': int, 'remote_address': text_type, 'remote_port': int, 'multihop': bool, 'multipath': bool, 'remove_private_as': bool, 'import_policy': text_type, 'export_policy': text_type, 'input_messages': int, 'output_messages': int, 'input_updates': int, 'output_updates': int, 'messages_queued_out': int, 'connection_state': text_type, 'previous_connection_state': text_type, 'last_event': text_type, 'suppress_4byte_as': bool, 'local_as_prepend': bool, 'holdtime': int, 'configured_holdtime': int, 'keepalive': int, 'configured_keepalive': int, 'active_prefix_count': int, 'received_prefix_count': int, 'accepted_prefix_count': int, 'suppressed_prefix_count': int, 'advertised_prefix_count': int, 'flap_count': int } arp_table = { 'interface': text_type, 'mac': text_type, 'ip': text_type, 'age': float } ntp_peer = { # will populate it in the future wit potential keys } ntp_server = { # will populate it in the future wit potential keys } ntp_stats = { 'remote': text_type, 'referenceid': text_type, 'synchronized': bool, 'stratum': int, 'type': text_type, 'when': text_type, 'hostpoll': int, 'reachability': int, 'delay': float, 'offset': float, 'jitter': float } interfaces_ip = { 'prefix_length': int } mac_address_table = { 'mac': text_type, 'interface': text_type, 'vlan': int, 'static': bool, 'active': bool, 'moves': int, 'last_move': float } route = { 'protocol': text_type, 'current_active': bool, 'last_active': bool, 'age': int, 'next_hop': text_type, 'outgoing_interface': text_type, 'selected_next_hop': bool, 'preference': int, 'inactive_reason': text_type, 'routing_table': text_type, 'protocol_attributes': dict } snmp = { 'chassis_id': text_type, 'community': dict, 'contact': text_type, 'location': text_type } snmp_community = { 'acl': text_type, 'mode': text_type, } probe_test = { 'probe_type': text_type, 'target': text_type, 'source': text_type, 'probe_count': int, 'test_interval': int } probe_test_results = { 'target': text_type, 'source': text_type, 'probe_type': text_type, 'probe_count': int, 'rtt': float, 'round_trip_jitter': float, 'last_test_loss': int, 'current_test_min_delay': float, 'current_test_max_delay': float, 'current_test_avg_delay': float, 'last_test_min_delay': float, 'last_test_max_delay': float, 'last_test_avg_delay': float, 'global_test_min_delay': float, 'global_test_max_delay': float, 'global_test_avg_delay': float } ping = { 'probes_sent': int, 'packet_loss': int, 'rtt_min': float, 'rtt_max': float, 'rtt_avg': float, 'rtt_stddev': float, 'results': list } ping_result = { 'ip_address': text_type, 'rtt': float } traceroute = { 'rtt': float, 'ip_address': text_type, 'host_name': text_type } users = { 'level': int, 'password': text_type, 'sshkeys': list } optics_state = { 'instant': float, 'avg': float, 'min': float, 'max': float } config = { 'running': text_type, 'startup': text_type, 'candidate': text_type, } network_instance = { 'name': text_type, 'type': text_type, 'state': dict, 'interfaces': dict, } network_instance_state = { 'route_distinguisher': text_type, } network_instance_interfaces = { 'interface': dict, } firewall_policies = { 'position': int, 'packet_hits': int, 'byte_hits': int, 'id': text_type, 'enabled': bool, 'schedule': text_type, 'log': text_type, 'l3_src': text_type, 'l3_dst': text_type, 'service': text_type, 'src_zone': text_type, 'dst_zone': text_type, 'action': text_type } napalm-base-0.25.0/napalm_base/utils/0000755000175000017500000000000013150265743020230 5ustar travistravis00000000000000napalm-base-0.25.0/napalm_base/utils/__init__.py0000644000175000017500000000000013150265625022326 0ustar travistravis00000000000000napalm-base-0.25.0/napalm_base/utils/jinja_filters.py0000644000175000017500000000272613150265625023433 0ustar travistravis00000000000000"""Some common jinja filters.""" from __future__ import print_function from __future__ import unicode_literals class CustomJinjaFilters(object): """Utility filters for jinja2.""" @classmethod def filters(cls): """Return jinja2 filters that this module provide.""" return { 'oc_attr_isdefault': oc_attr_isdefault, 'openconfig_to_cisco_af': openconfig_to_cisco_af, 'openconfig_to_eos_af': openconfig_to_eos_af, } def oc_attr_isdefault(o): """Return wether an OC attribute has been defined or not.""" if not o._changed() and not o.default(): return True if o == o.default(): return True return False def openconfig_to_cisco_af(value): """Translate openconfig AF name to Cisco AFI name.""" if ":" in value: value = value.split(":")[1] mapd = { 'IPV4_UNICAST': "ipv4 unicast", 'IPV6_UNICAST': "ipv6 unicast", 'IPV4_LABELED_UNICAST': "ipv4 unicast", 'IPV6_LABELED_UNICAST': "ipv6 unicast", 'L3VPN_IPV4_UNICAST': "vpnv4", 'L3VPN_IPV6_UNICAST': "vpnv6", } return mapd[value] def openconfig_to_eos_af(value): """Translate openconfig AF name to EOS AFI name.""" if ":" in value: value = value.split(":")[1] mapd = { 'IPV4_UNICAST': "ipv4", 'IPV6_UNICAST': "ipv6", } return mapd[value] napalm-base-0.25.0/napalm_base/utils/py23_compat.py0000644000175000017500000000062613150265625022745 0ustar travistravis00000000000000"""Simplify Python3 compatibility. Modeled after six behavior for small set of things""" from __future__ import print_function from __future__ import unicode_literals import sys PY2 = sys.version_info.major == 2 PY3 = sys.version_info.major == 3 if sys.version_info.major == 3: string_types = (str,) text_type = str else: string_types = (basestring,) # noqa text_type = unicode # noqa napalm-base-0.25.0/napalm_base/utils/string_parsers.py0000644000175000017500000000770313150265625023655 0ustar travistravis00000000000000from __future__ import print_function from __future__ import unicode_literals import re def convert(text): if text.isdigit(): return int(text) else: return text def alphanum_key(key): return [convert(c) for c in re.split('([0-9]+)', key)] def sorted_nicely(l): """ Sort the given iterable in the way that humans expect.""" return sorted(l, key=alphanum_key) def colon_separated_string_to_dict(string, separator=':'): ''' Converts a string in the format: Name: Et3 Switchport: Enabled Administrative Mode: trunk Operational Mode: trunk MAC Address Learning: enabled Access Mode VLAN: 3 (VLAN0003) Trunking Native Mode VLAN: 1 (default) Administrative Native VLAN tagging: disabled Administrative private VLAN mapping: ALL Trunking VLANs Enabled: 2-3,5-7,20-21,23,100-200 Trunk Groups: into a dictionary ''' dictionary = dict() for line in string.splitlines(): line_data = line.split(separator) if len(line_data) > 1: dictionary[line_data[0].strip()] = ''.join(line_data[1:]).strip() elif len(line_data) == 1: dictionary[line_data[0].strip()] = None else: raise Exception('Something went wrong parsing the colo separated string {}' .format(line)) return dictionary def hyphen_range(string): ''' Expands a string of numbers separated by commas and hyphens into a list of integers. For example: 2-3,5-7,20-21,23,100-200 ''' list_numbers = list() temporary_list = string.split(',') for element in temporary_list: sub_element = element.split('-') if len(sub_element) == 1: list_numbers.append(int(sub_element[0])) elif len(sub_element) == 2: for x in range(int(sub_element[0]), int(sub_element[1])+1): list_numbers.append(x) else: raise Exception('Something went wrong expanding the range'.format(string)) return list_numbers def convert_uptime_string_seconds(uptime): '''Convert uptime strings to seconds. The string can be formatted various ways.''' regex_list = [ # n years, n weeks, n days, n hours, n minutes where each of the fields except minutes # is optional. Additionally, can be either singular or plural (r"((?P\d+) year(s)?,\s+)?((?P\d+) week(s)?,\s+)?" r"((?P\d+) day(s)?,\s+)?((?P\d+) " r"hour(s)?,\s+)?((?P\d+) minute(s)?)"), # n days, HH:MM:SS where each field is required (except for days) (r"((?P\d+) day(s)?,\s+)?" r"((?P\d+)):((?P\d+)):((?P\d+))"), # 7w6d5h4m3s where each field is optional (r"((?P\d+)w)?((?P\d+)d)?((?P\d+)h)?" r"((?P\d+)m)?((?P\d+)s)?"), ] regex_list = [re.compile(x) for x in regex_list] uptime_dict = {} for regex in regex_list: match = regex.search(uptime) if match: uptime_dict = match.groupdict() break uptime_seconds = 0 for unit, value in uptime_dict.items(): if value is not None: if unit == 'years': uptime_seconds += int(value) * 31536000 elif unit == 'weeks': uptime_seconds += int(value) * 604800 elif unit == 'days': uptime_seconds += int(value) * 86400 elif unit == 'hours': uptime_seconds += int(value) * 3600 elif unit == 'minutes': uptime_seconds += int(value) * 60 elif unit == 'seconds': uptime_seconds += int(value) else: raise Exception('Unrecognized unit "{}" in uptime:{}'.format(unit, uptime)) if not uptime_dict: raise Exception('Unrecognized uptime string:{}'.format(uptime)) return uptime_seconds napalm-base-0.25.0/napalm_base/__init__.py0000644000175000017500000001037513150265625021206 0ustar travistravis00000000000000# Copyright 2015 Spotify AB. All rights reserved. # # The contents of this file are licensed under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with the # License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under # the License. """napalm_base package.""" # Python3 support from __future__ import print_function from __future__ import unicode_literals # Python std lib import sys import inspect import importlib import pkg_resources # Verify Python Version that is running try: if not(sys.version_info.major == 2 and sys.version_info.minor == 7) and \ not(sys.version_info.major == 3): raise RuntimeError('NAPALM requires Python 2.7 or Python3') except AttributeError: raise RuntimeError('NAPALM requires Python 2.7 or Python3') # 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: __version__ = pkg_resources.get_distribution('napalm-base').version except pkg_resources.DistributionNotFound: __version__ = "Not installed" __all__ = [ 'get_network_driver', # export the function 'NetworkDriver' # also export the base class ] 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]. NAPALM community supports a list of devices and provides the corresponding libraries; for full reference please refer to the `Supported Network Operation Systems`_ paragraph on `Read the Docs`_. .. _`Supported Network Operation Systems`: \ http://napalm.readthedocs.io/en/latest/#supported-network-operating-systems .. _`Read the Docs`: \ http://napalm.readthedocs.io/ :param module_name: the name of the device operating system or the name of the library. :return: the first class derived from NetworkDriver, found in the library. :raise ModuleImportError: when the library is not installed or a derived class from \ NetworkDriver was not found. Example:: .. code-block:: python >>> get_network_driver('junos') >>> get_network_driver('IOS-XR') >>> get_network_driver('napalm_eos') >>> get_network_driver('wrong') 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.') try: # Only lowercase allowed module_name = module_name.lower() # 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 and prepend is True: module_install_name = 'napalm_{name}'.format(name=module_install_name) module = importlib.import_module(module_install_name) except ImportError: raise ModuleImportError( 'Cannot import "{install_name}". Is the library installed?'.format( install_name=module_install_name ) ) for name, obj in inspect.getmembers(module): if inspect.isclass(obj) and issubclass(obj, NetworkDriver): return obj # looks like you don't have any Driver class in your module... raise ModuleImportError( 'No class inheriting "napalm_base.base.NetworkDriver" found in "{install_name}".' .format(install_name=module_install_name)) napalm-base-0.25.0/napalm_base/base.py0000644000175000017500000016243113150265625020362 0ustar travistravis00000000000000# Copyright 2015 Spotify AB. All rights reserved. # # The contents of this file are licensed under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with the # License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under # the License. # Python3 support from __future__ import print_function from __future__ import unicode_literals # local modules import napalm_base.exceptions import napalm_base.helpers import napalm_base.constants as c from napalm_base import validate class NetworkDriver(object): def __init__(self, hostname, username, password, timeout=60, optional_args=None): """ This is the base class you have to inherit from when writing your own Network Driver to manage any device. You will, in addition, have to override all the methods specified on this class. Make sure you follow the guidelines for every method and that you return the correct data. :param hostname: (str) IP or FQDN of the device you want to connect to. :param username: (str) Username you want to use :param password: (str) Password :param timeout: (int) Time in seconds to wait for the device to respond. :param optional_args: (dict) Pass additional arguments to underlying driver :return: """ raise NotImplementedError def __enter__(self): self.open() return self def __exit__(self, exc_type, exc_value, exc_traceback): self.close() 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) 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): """ Opens a connection to the device. """ raise NotImplementedError def close(self): """ Closes the connection to the device. """ raise NotImplementedError def is_alive(self): """ Returns a flag with the connection state. Depends on the nature of API used by each driver. The state does not reflect only on the connection status (when SSH), it must also take into consideration other parameters, e.g.: NETCONF session might not be usable, althought the underlying SSH session is still open etc. """ 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): """ Will load a templated configuration on the device. :param cls: Instance of the driver class. :param template_name: Identifies the template name. :param template_source (optional): Custom config template rendered and loaded on device :param template_path (optional): Absolute path to directory for the configuration templates :param template_vars: Dictionary with arguments to be used when the template is rendered. :raise DriverTemplateNotImplemented: No template defined for the device type. :raise TemplateNotImplemented: The template specified in template_name does not exist in \ the default path or in the custom path if any specified using parameter `template_path`. :raise TemplateRenderException: The template could not be rendered. Either the template \ source does not have the right format, either the arguments in `template_vars` are not \ properly specified. """ return napalm_base.helpers.load_template(self, template_name, template_source=template_source, template_path=template_path, **template_vars) def load_replace_candidate(self, filename=None, config=None): """ Populates the candidate configuration. You can populate it from a file or from a string. If you send both a filename and a string containing the configuration, the file takes precedence. If you use this method the existing configuration will be replaced entirely by the candidate configuration once you commit the changes. This method will not change the configuration by itself. :param filename: Path to the file containing the desired configuration. By default is None. :param config: String containing the desired configuration. :raise ReplaceConfigException: If there is an error on the configuration sent. """ raise NotImplementedError def load_merge_candidate(self, filename=None, config=None): """ Populates the candidate configuration. You can populate it from a file or from a string. If you send both a filename and a string containing the configuration, the file takes precedence. If you use this method the existing configuration will be merged with the candidate configuration once you commit the changes. This method will not change the configuration by itself. :param filename: Path to the file containing the desired configuration. By default is None. :param config: String containing the desired configuration. :raise MergeConfigException: If there is an error on the configuration sent. """ raise NotImplementedError def compare_config(self): """ :return: A string showing the difference between the running configuration and the \ candidate configuration. The running_config is loaded automatically just before doing the \ comparison so there is no need for you to do it. """ raise NotImplementedError def commit_config(self): """ Commits the changes requested by the method load_replace_candidate or load_merge_candidate. """ raise NotImplementedError def discard_config(self): """ Discards the configuration loaded into the candidate. """ raise NotImplementedError def rollback(self): """ If changes were made, revert changes to the original state. """ raise NotImplementedError def get_facts(self): """ Returns a dictionary containing the following information: * uptime - Uptime of the device in seconds. * vendor - Manufacturer of the device. * model - Device model. * hostname - Hostname of the device * fqdn - Fqdn of the device * os_version - String with the OS version running on the device. * serial_number - Serial number of the device * interface_list - List of the interfaces of the device Example:: { 'uptime': 151005.57332897186, 'vendor': u'Arista', 'os_version': u'4.14.3-2329074.gaatlantarel', 'serial_number': u'SN0123A34AS', 'model': u'vEOS', 'hostname': u'eos-router', 'fqdn': u'eos-router', 'interface_list': [u'Ethernet2', u'Management1', u'Ethernet1', u'Ethernet3'] } """ raise NotImplementedError def get_interfaces(self): """ Returns a dictionary of dictionaries. The keys for the first dictionary will be the \ interfaces in the devices. The inner dictionary will containing the following data for \ each interface: * is_up (True/False) * is_enabled (True/False) * description (string) * last_flapped (int in seconds) * speed (int in Mbit) * mac_address (string) Example:: { u'Management1': { 'is_up': False, 'is_enabled': False, 'description': '', 'last_flapped': -1, 'speed': 1000, 'mac_address': 'FA:16:3E:57:33:61', }, u'Ethernet1': { 'is_up': True, 'is_enabled': True, 'description': 'foo', 'last_flapped': 1429978575.1554043, 'speed': 1000, 'mac_address': 'FA:16:3E:57:33:62', }, u'Ethernet2': { 'is_up': True, 'is_enabled': True, 'description': 'bla', 'last_flapped': 1429978575.1555667, 'speed': 1000, 'mac_address': 'FA:16:3E:57:33:63', }, u'Ethernet3': { 'is_up': False, 'is_enabled': True, 'description': 'bar', 'last_flapped': -1, 'speed': 1000, 'mac_address': 'FA:16:3E:57:33:64', } } """ raise NotImplementedError def get_lldp_neighbors(self): """ Returns a dictionary where the keys are local ports and the value is a list of \ dictionaries with the following information: * hostname * port Example:: { u'Ethernet2': [ { 'hostname': u'junos-unittest', 'port': u'520', } ], u'Ethernet3': [ { 'hostname': u'junos-unittest', 'port': u'522', } ], u'Ethernet1': [ { 'hostname': u'junos-unittest', 'port': u'519', }, { 'hostname': u'ios-xrv-unittest', 'port': u'Gi0/0/0/0', } ], u'Management1': [ { 'hostname': u'junos-unittest', 'port': u'508', } ] } """ raise NotImplementedError def get_bgp_neighbors(self): """ Returns a dictionary of dictionaries. The keys for the first dictionary will be the vrf (global if no vrf). The inner dictionary will contain the following data for each vrf: * router_id * peers - another dictionary of dictionaries. Outer keys are the IPs of the neighbors. \ The inner keys are: * local_as (int) * remote_as (int) * remote_id - peer router id * is_up (True/False) * is_enabled (True/False) * description (string) * uptime (int in seconds) * address_family (dictionary) - A dictionary of address families available for the \ neighbor. So far it can be 'ipv4' or 'ipv6' * 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 def get_environment(self): """ Returns a dictionary where: * fans is a dictionary of dictionaries where the key is the location and the values: * status (True/False) - True if it's ok, false if it's broken * temperature is a dict of dictionaries where the key is the location and the values: * temperature (float) - Temperature in celsius the sensor is reporting. * is_alert (True/False) - True if the temperature is above the alert threshold * is_critical (True/False) - True if the temp is above the critical threshold * power is a dictionary of dictionaries where the key is the PSU id and the values: * status (True/False) - True if it's ok, false if it's broken * capacity (float) - Capacity in W that the power supply can support * output (float) - Watts drawn by the system * cpu is a dictionary of dictionaries where the key is the ID and the values * %usage * memory is a dictionary with: * available_ram (int) - Total amount of RAM installed in the device * used_ram (int) - RAM in use in the device """ raise NotImplementedError def get_interfaces_counters(self): """ Returns a dictionary of dictionaries where the first key is an interface name and the inner dictionary contains the following keys: * tx_errors (int) * rx_errors (int) * tx_discards (int) * rx_discards (int) * tx_octets (int) * rx_octets (int) * tx_unicast_packets (int) * rx_unicast_packets (int) * tx_multicast_packets (int) * rx_multicast_packets (int) * tx_broadcast_packets (int) * rx_broadcast_packets (int) Example:: { u'Ethernet2': { 'tx_multicast_packets': 699, 'tx_discards': 0, 'tx_octets': 88577, 'tx_errors': 0, 'rx_octets': 0, 'tx_unicast_packets': 0, 'rx_errors': 0, 'tx_broadcast_packets': 0, 'rx_multicast_packets': 0, 'rx_broadcast_packets': 0, 'rx_discards': 0, 'rx_unicast_packets': 0 }, u'Management1': { 'tx_multicast_packets': 0, 'tx_discards': 0, 'tx_octets': 159159, 'tx_errors': 0, 'rx_octets': 167644, 'tx_unicast_packets': 1241, 'rx_errors': 0, 'tx_broadcast_packets': 0, 'rx_multicast_packets': 0, 'rx_broadcast_packets': 80, 'rx_discards': 0, 'rx_unicast_packets': 0 }, u'Ethernet1': { 'tx_multicast_packets': 293, 'tx_discards': 0, 'tx_octets': 38639, 'tx_errors': 0, 'rx_octets': 0, 'tx_unicast_packets': 0, 'rx_errors': 0, 'tx_broadcast_packets': 0, 'rx_multicast_packets': 0, 'rx_broadcast_packets': 0, 'rx_discards': 0, 'rx_unicast_packets': 0 } } """ raise NotImplementedError def get_lldp_neighbors_detail(self, interface=''): """ Returns a detailed view of the LLDP neighbors as a dictionary containing lists of dictionaries for each interface. Inner dictionaries contain fields: * parent_interface (string) * remote_port (string) * remote_port_description (string) * remote_chassis_id (string) * remote_system_name (string) * remote_system_description (string) * remote_system_capab (string) * remote_system_enabled_capab (string) Example:: { 'TenGigE0/0/0/8': [ { 'parent_interface': u'Bundle-Ether8', 'remote_chassis_id': u'8c60.4f69.e96c', 'remote_system_name': u'switch', 'remote_port': u'Eth2/2/1', 'remote_port_description': u'Ethernet2/2/1', 'remote_system_description': u'''Cisco Nexus Operating System (NX-OS) Software 7.1(0)N1(1a) TAC support: http://www.cisco.com/tac Copyright (c) 2002-2015, Cisco Systems, Inc. All rights reserved.''', 'remote_system_capab': u'B, R', 'remote_system_enable_capab': u'B' } ] } """ raise NotImplementedError def get_bgp_config(self, group='', neighbor=''): """ Returns a dictionary containing the BGP configuration. Can return either the whole config, either the config only for a group or neighbor. :param group: Returns the configuration of a specific BGP group. :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 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) * multihop_ttl (int) * multipath (True/False) * local_address (string) * local_as (int) * remote_as (int) * import_policy (string) * export_policy (string) * remove_private_as (True/False) * prefix_limit (dictionary) * neighbors (dictionary) Neighbors is a dictionary of dictionaries with the following keys: * description (string) * import_policy (string) * export_policy (string) * local_address (string) * local_as (int) * remote_as (int) * authentication_key (string) * prefix_limit (dictionary) * route_reflector_client (True/False) * nhs (True/False) The inner dictionary prefix_limit has the same structure for both layers:: { [FAMILY_NAME]: { [FAMILY_TYPE]: { 'limit': [LIMIT], ... other options } } } Example:: { 'PEERS-GROUP-NAME':{ 'type' : u'external', 'description' : u'Here we should have a nice description', 'apply_groups' : [u'BGP-PREFIX-LIMIT'], 'import_policy' : u'PUBLIC-PEER-IN', 'export_policy' : u'PUBLIC-PEER-OUT', 'remove_private_as' : True, 'multipath' : True, 'multihop_ttl' : 30, 'neighbors' : { '192.168.0.1': { 'description' : 'Facebook [CDN]', 'prefix_limit' : { 'inet': { 'unicast': { 'limit': 100, 'teardown': { 'threshold' : 95, 'timeout' : 5 } } } } 'remote_as' : 32934, 'route_reflector_client': False, 'nhs' : True }, '172.17.17.1': { 'description' : 'Twitter [CDN]', 'prefix_limit' : { 'inet': { 'unicast': { 'limit': 500, 'no-validate': 'IMPORT-FLOW-ROUTES' } } } 'remote_as' : 13414 'route_reflector_client': False, 'nhs' : False } } } } """ raise NotImplementedError def cli(self, commands): """ Will execute a list of commands and return the output in a dictionary format. Example:: { u'show version and haiku': u'''Hostname: re0.edge01.arn01 Model: mx480 Junos: 13.3R6.5 Help me, Obi-Wan I just saw Episode Two You're my only hope ''', u'show chassis fan' : u''' Item Status RPM Measurement Top Rear Fan OK 3840 Spinning at intermediate-speed Bottom Rear Fan OK 3840 Spinning at intermediate-speed Top Middle Fan OK 3900 Spinning at intermediate-speed Bottom Middle Fan OK 3840 Spinning at intermediate-speed Top Front Fan OK 3810 Spinning at intermediate-speed Bottom Front Fan OK 3840 Spinning at intermediate-speed''' } """ raise NotImplementedError def get_bgp_neighbors_detail(self, neighbor_address=''): """ Returns a detailed view of the BGP neighbors as a dictionary of lists. :param neighbor_address: Retuns the statistics for a spcific BGP neighbor. Returns a dictionary of dictionaries. The keys for the first dictionary will be the vrf (global if no vrf). The keys of the inner dictionary represent the AS number of the neighbors. Leaf dictionaries contain the following fields: * up (True/False) * local_as (int) * remote_as (int) * router_id (string) * local_address (string) * routing_table (string) * local_address_configured (True/False) * local_port (int) * remote_address (string) * remote_port (int) * multihop (True/False) * multipath (True/False) * remove_private_as (True/False) * import_policy (string) * export_policy (string) * input_messages (int) * output_messages (int) * input_updates (int) * output_updates (int) * messages_queued_out (int) * connection_state (string) * previous_connection_state (string) * last_event (string) * suppress_4byte_as (True/False) * local_as_prepend (True/False) * holdtime (int) * configured_holdtime (int) * keepalive (int) * configured_keepalive (int) * active_prefix_count (int) * received_prefix_count (int) * accepted_prefix_count (int) * suppressed_prefix_count (int) * advertised_prefix_count (int) * flap_count (int) Example:: { 'global': { 8121: [ { 'up' : True, 'local_as' : 13335, 'remote_as' : 8121, 'local_address' : u'172.101.76.1', 'local_address_configured' : True, 'local_port' : 179, 'routing_table' : u'inet.0', 'remote_address' : u'192.247.78.0', 'remote_port' : 58380, 'multihop' : False, 'multipath' : True, 'remove_private_as' : True, 'import_policy' : u'4-NTT-TRANSIT-IN', 'export_policy' : u'4-NTT-TRANSIT-OUT', 'input_messages' : 123, 'output_messages' : 13, 'input_updates' : 123, 'output_updates' : 5, 'messages_queued_out' : 23, 'connection_state' : u'Established', 'previous_connection_state' : u'EstabSync', 'last_event' : u'RecvKeepAlive', 'suppress_4byte_as' : False, 'local_as_prepend' : False, 'holdtime' : 90, 'configured_holdtime' : 90, 'keepalive' : 30, 'configured_keepalive' : 30, 'active_prefix_count' : 132808, 'received_prefix_count' : 566739, 'accepted_prefix_count' : 566479, 'suppressed_prefix_count' : 0, 'advertised_prefix_count' : 0, 'flap_count' : 27 } ] } } """ raise NotImplementedError def get_arp_table(self): """ Returns a list of dictionaries having the following set of keys: * interface (string) * mac (string) * ip (string) * age (float) Example:: [ { 'interface' : 'MgmtEth0/RSP0/CPU0/0', 'mac' : '5C:5E:AB:DA:3C:F0', 'ip' : '172.17.17.1', 'age' : 1454496274.84 }, { 'interface' : 'MgmtEth0/RSP0/CPU0/0', 'mac' : '5C:5E:AB:DA:3C:FF', 'ip' : '172.17.17.2', 'age' : 1435641582.49 } ] """ raise NotImplementedError def get_ntp_peers(self): """ Returns the NTP peers configuration as dictionary. The keys of the dictionary represent the IP Addresses of the peers. Inner dictionaries do not have yet any available keys. Example:: { '192.168.0.1': {}, '17.72.148.53': {}, '37.187.56.220': {}, '162.158.20.18': {} } """ raise NotImplementedError def get_ntp_servers(self): """ Returns the NTP servers configuration as dictionary. The keys of the dictionary represent the IP Addresses of the servers. Inner dictionaries do not have yet any available keys. Example:: { '192.168.0.1': {}, '17.72.148.53': {}, '37.187.56.220': {}, '162.158.20.18': {} } """ raise NotImplementedError def get_ntp_stats(self): """ Returns a list of NTP synchronization statistics. * remote (string) * referenceid (string) * synchronized (True/False) * stratum (int) * type (string) * when (string) * hostpoll (int) * reachability (int) * delay (float) * offset (float) * jitter (float) Example:: [ { 'remote' : u'188.114.101.4', 'referenceid' : u'188.114.100.1', 'synchronized' : True, 'stratum' : 4, 'type' : u'-', 'when' : u'107', 'hostpoll' : 256, 'reachability' : 377, 'delay' : 164.228, 'offset' : -13.866, 'jitter' : 2.695 } ] """ raise NotImplementedError def get_interfaces_ip(self): """ Returns all configured IP addresses on all interfaces as a dictionary of dictionaries. Keys of the main dictionary represent the name of the interface. Values of the main dictionary represent are dictionaries that may consist of two keys 'ipv4' and 'ipv6' (one, both or none) which are themselvs dictionaries witht the IP addresses as keys. Each IP Address dictionary has the following keys: * prefix_length (int) Example:: { u'FastEthernet8': { u'ipv4': { u'10.66.43.169': { 'prefix_length': 22 } } }, u'Loopback555': { u'ipv4': { u'192.168.1.1': { 'prefix_length': 24 } }, u'ipv6': { u'1::1': { 'prefix_length': 64 }, u'2001:DB8:1::1': { 'prefix_length': 64 }, u'2::': { 'prefix_length': 64 }, u'FE80::3': { 'prefix_length': u'N/A' } } }, u'Tunnel0': { u'ipv4': { u'10.63.100.9': { 'prefix_length': 24 } } } } """ raise NotImplementedError def get_mac_address_table(self): """ Returns a lists of dictionaries. Each dictionary represents an entry in the MAC Address Table, having the following keys: * mac (string) * interface (string) * vlan (int) * active (boolean) * static (boolean) * moves (int) * last_move (float) However, please note that not all vendors provide all these details. E.g.: field last_move is not available on JUNOS devices etc. Example:: [ { 'mac' : '00:1C:58:29:4A:71', 'interface' : 'Ethernet47', 'vlan' : 100, 'static' : False, 'active' : True, 'moves' : 1, 'last_move' : 1454417742.58 }, { 'mac' : '00:1C:58:29:4A:C1', 'interface' : 'xe-1/0/1', 'vlan' : 100, 'static' : False, 'active' : True, 'moves' : 2, 'last_move' : 1453191948.11 }, { 'mac' : '00:1C:58:29:4A:C2', 'interface' : 'ae7.900', 'vlan' : 900, 'static' : False, 'active' : True, 'moves' : None, 'last_move' : None } ] """ raise NotImplementedError def get_route_to(self, destination='', protocol=''): """ Returns a dictionary of dictionaries containing details of all available routes to a destination. :param destination: The destination prefix to be used when filtering the routes. :param protocol (optional): Retrieve the routes only for a specific protocol. Each inner dictionary contains the following fields: * protocol (string) * current_active (True/False) * last_active (True/False) * age (int) * next_hop (string) * outgoing_interface (string) * selected_next_hop (True/False) * preference (int) * inactive_reason (string) * routing_table (string) * protocol_attributes (dictionary) protocol_attributes is a dictionary with protocol-specific information, as follows: - BGP * local_as (int) * remote_as (int) * peer_id (string) * as_path (string) * communities (list) * local_preference (int) * preference2 (int) * metric (int) * metric2 (int) - ISIS: * level (int) Example:: { "1.0.0.0/24": [ { "protocol" : u"BGP", "inactive_reason" : u"Local Preference", "last_active" : False, "age" : 105219, "next_hop" : u"172.17.17.17", "selected_next_hop" : True, "preference" : 170, "current_active" : False, "outgoing_interface": u"ae9.0", "routing_table" : "inet.0", "protocol_attributes": { "local_as" : 13335, "as_path" : u"2914 8403 54113 I", "communities" : [ u"2914:1234", u"2914:5678", u"8403:1717", u"54113:9999" ], "preference2" : -101, "remote_as" : 2914, "local_preference" : 100 } } ] } """ raise NotImplementedError def get_snmp_information(self): """ Returns a dict of dicts containing SNMP configuration. Each inner dictionary contains these fields * chassis_id (string) * community (dictionary) * contact (string) * location (string) 'community' is a dictionary with community string specific information, as follows: * acl (string) # acl number or name * mode (string) # read-write (rw), read-only (ro) Example:: { 'chassis_id': u'Asset Tag 54670', 'community': { u'private': { 'acl': u'12', 'mode': u'rw' }, u'public': { 'acl': u'11', 'mode': u'ro' }, u'public_named_acl': { 'acl': u'ALLOW-SNMP-ACL', 'mode': u'ro' }, u'public_no_acl': { 'acl': u'N/A', 'mode': u'ro' } }, 'contact' : u'Joe Smith', 'location': u'123 Anytown USA Rack 404' } """ raise NotImplementedError def get_probes_config(self): """ Returns a dictionary with the probes configured on the device. Probes can be either RPM on JunOS devices, either SLA on IOS-XR. Other vendors do not support probes. The keys of the main dictionary represent the name of the probes. Each probe consists on multiple tests, each test name being a key in the probe dictionary. A test has the following keys: * probe_type (str) * target (str) * source (str) * probe_count (int) * test_interval (int) Example:: { 'probe1':{ 'test1': { 'probe_type' : 'icmp-ping', 'target' : '192.168.0.1', 'source' : '192.168.0.2', 'probe_count' : 13, 'test_interval': 3 }, 'test2': { 'probe_type' : 'http-ping', 'target' : '172.17.17.1', 'source' : '192.17.17.2', 'probe_count' : 5, 'test_interval': 60 } } } """ raise NotImplementedError def get_probes_results(self): """ Returns a dictionary with the results of the probes. The keys of the main dictionary represent the name of the probes. Each probe consists on multiple tests, each test name being a key in the probe dictionary. A test has the following keys: * target (str) * source (str) * probe_type (str) * probe_count (int) * rtt (float) * round_trip_jitter (float) * current_test_loss (float) * current_test_min_delay (float) * current_test_max_delay (float) * current_test_avg_delay (float) * last_test_min_delay (float) * last_test_max_delay (float) * last_test_avg_delay (float) * global_test_min_delay (float) * global_test_max_delay (float) * global_test_avg_delay (float) Example:: { 'probe1': { 'test1': { 'last_test_min_delay' : 63.120, 'global_test_min_delay' : 62.912, 'current_test_avg_delay': 63.190, 'global_test_max_delay' : 177.349, 'current_test_max_delay': 63.302, 'global_test_avg_delay' : 63.802, 'last_test_avg_delay' : 63.438, 'last_test_max_delay' : 65.356, 'probe_type' : 'icmp-ping', 'rtt' : 63.138, 'current_test_loss' : 0, 'round_trip_jitter' : -59.0, 'target' : '192.168.0.1', 'source' : '192.168.0.2' 'probe_count' : 15, 'current_test_min_delay': 63.138 }, 'test2': { 'last_test_min_delay' : 176.384, 'global_test_min_delay' : 169.226, 'current_test_avg_delay': 177.098, 'global_test_max_delay' : 292.628, 'current_test_max_delay': 180.055, 'global_test_avg_delay' : 177.959, 'last_test_avg_delay' : 177.178, 'last_test_max_delay' : 184.671, 'probe_type' : 'icmp-ping', 'rtt' : 176.449, 'current_test_loss' : 0, 'round_trip_jitter' : -34.0, 'target' : '172.17.17.1', 'source' : '172.17.17.2' 'probe_count' : 15, 'current_test_min_delay': 176.402 } } } """ 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, vrf=c.PING_VRF): """ Executes ping on the device and returns a dictionary with the result :param destination: Host or IP Address of the destination :param source (optional): Source address of echo request :param ttl (optional): Maximum number of hops :param timeout (optional): Maximum seconds to wait after sending final packet :param size (optional): Size of request (bytes) :param count (optional): Number of ping request to send Output dictionary has one of following keys: * success * error In case of success, inner dictionary will have the followin keys: * probes_sent (int) * packet_loss (int) * rtt_min (float) * rtt_max (float) * rtt_avg (float) * rtt_stddev (float) * results (list) 'results' is a list of dictionaries with the following keys: * ip_address (str) * rtt (float) Example:: { 'success': { 'probes_sent': 5, 'packet_loss': 0, 'rtt_min': 72.158, 'rtt_max': 72.433, 'rtt_avg': 72.268, 'rtt_stddev': 0.094, 'results': [ { 'ip_address': u'1.1.1.1', 'rtt': 72.248 }, { 'ip_address': '2.2.2.2', 'rtt': 72.299 } ] } } OR { 'error': 'unknown host 8.8.8.8.8' } """ raise NotImplementedError def traceroute(self, destination, source=c.TRACEROUTE_SOURCE, ttl=c.TRACEROUTE_TTL, timeout=c.TRACEROUTE_TIMEOUT, vrf=c.TRACEROUTE_VRF): """ Executes traceroute on the device and returns a dictionary with the result. :param destination: Host or IP Address of the destination :param source (optional): Use a specific IP Address to execute the traceroute :param ttl (optional): Maimum number of hops :param timeout (optional): Number of seconds to wait for response Output dictionary has one of the following keys: * success * error In case of success, the keys of the dictionary represent the hop ID, while values are dictionaries containing the probes results: * rtt (float) * ip_address (str) * host_name (str) Example:: { 'success': { 1: { 'probes': { 1: { 'rtt': 1.123, 'ip_address': u'206.223.116.21', 'host_name': u'eqixsj-google-gige.google.com' }, 2: { 'rtt': 1.9100000000000001, 'ip_address': u'206.223.116.21', 'host_name': u'eqixsj-google-gige.google.com' }, 3: { 'rtt': 3.347, 'ip_address': u'198.32.176.31', 'host_name': u'core2-1-1-0.pao.net.google.com'} } }, 2: { 'probes': { 1: { 'rtt': 1.586, 'ip_address': u'209.85.241.171', 'host_name': u'209.85.241.171' }, 2: { 'rtt': 1.6300000000000001, 'ip_address': u'209.85.241.171', 'host_name': u'209.85.241.171' }, 3: { 'rtt': 1.6480000000000001, 'ip_address': u'209.85.241.171', 'host_name': u'209.85.241.171'} } }, 3: { 'probes': { 1: { 'rtt': 2.529, 'ip_address': u'216.239.49.123', 'host_name': u'216.239.49.123'}, 2: { 'rtt': 2.474, 'ip_address': u'209.85.255.255', 'host_name': u'209.85.255.255' }, 3: { 'rtt': 7.813, 'ip_address': u'216.239.58.193', 'host_name': u'216.239.58.193'} } }, 4: { 'probes': { 1: { 'rtt': 1.361, 'ip_address': u'8.8.8.8', 'host_name': u'google-public-dns-a.google.com' }, 2: { 'rtt': 1.605, 'ip_address': u'8.8.8.8', 'host_name': u'google-public-dns-a.google.com' }, 3: { 'rtt': 0.989, 'ip_address': u'8.8.8.8', 'host_name': u'google-public-dns-a.google.com'} } } } } OR { 'error': 'unknown host 8.8.8.8.8' } """ raise NotImplementedError def get_users(self): """ Returns a dictionary with the configured users. The keys of the main dictionary represents the username. The values represent the details of the user, represented by the following keys: * level (int) * password (str) * sshkeys (list) The level is an integer between 0 and 15, where 0 is the lowest access and 15 represents full access to the device. Example:: { 'mircea': { 'level': 15, 'password': '$1$0P70xKPa$z46fewjo/10cBTckk6I/w/', 'sshkeys': [ 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4pFn+shPwTb2yELO4L7NtQrKOJXNeCl1je\ l9STXVaGnRAnuc2PXl35vnWmcUq6YbUEcgUTRzzXfmelJKuVJTJIlMXii7h2xkbQp0YZIEs4P\ 8ipwnRBAxFfk/ZcDsN3mjep4/yjN56eorF5xs7zP9HbqbJ1dsqk1p3A/9LIL7l6YewLBCwJj6\ D+fWSJ0/YW+7oH17Fk2HH+tw0L5PcWLHkwA4t60iXn16qDbIk/ze6jv2hDGdCdz7oYQeCE55C\ CHOHMJWYfN3jcL4s0qv8/u6Ka1FVkV7iMmro7ChThoV/5snI4Ljf2wKqgHH7TfNaCfpU0WvHA\ nTs8zhOrGScSrtb mircea@master-roshi' ] } } """ raise NotImplementedError def get_optics(self): """Fetches the power usage on the various transceivers installed on the switch (in dbm), and returns a view that conforms with the openconfig model openconfig-platform-transceiver.yang Returns a dictionary where the keys are as listed below: * intf_name (unicode) * physical_channels * channels (list of dicts) * index (int) * state * input_power * instant (float) * avg (float) * min (float) * max (float) * output_power * instant (float) * avg (float) * min (float) * max (float) * laser_bias_current * instant (float) * avg (float) * min (float) * max (float) Example: { 'et1': { 'physical_channels': { 'channel': [ { 'index': 0, 'state': { 'input_power': { 'instant': 0.0, 'avg': 0.0, 'min': 0.0, 'max': 0.0, }, 'output_power': { 'instant': 0.0, 'avg': 0.0, 'min': 0.0, 'max': 0.0, }, 'laser_bias_current': { 'instant': 0.0, 'avg': 0.0, 'min': 0.0, 'max': 0.0, }, } } ] } } } """ raise NotImplementedError def get_config(self, retrieve='all'): """ Return the configuration of a device. Args: retrieve(string): Which configuration type you want to populate, default is all of them. The rest will be set to "". Returns: The object returned is a dictionary with the following keys: - running(string) - Representation of the native running configuration - candidate(string) - Representation of the native candidate configuration. If the device doesnt differentiate between running and startup configuration this will an empty string - startup(string) - Representation of the native startup configuration. If the device doesnt differentiate between running and startup configuration this will an empty string """ raise NotImplementedError def get_network_instances(self, name=''): """ Return a dictionary of network instances (VRFs) configured, including default/global Args: name(string) - Name of the network instance to return, default is all. Returns: A dictionary of network instances in OC format: * name (dict) * name (unicode) * type (unicode) * state (dict) * route_distinguisher (unicode) * interfaces (dict) * interface (dict) * interface name: (dict) Example: { u'MGMT': { u'name': u'MGMT', u'type': u'L3VRF', u'state': { u'route_distinguisher': u'123:456', }, u'interfaces': { u'interface': { u'Management1': {} } } } u'default': { u'name': u'default', u'type': u'DEFAULT_INSTANCE', u'state': { u'route_distinguisher': None, }, u'interfaces: { u'interface': { u'Ethernet1': {} u'Ethernet2': {} u'Ethernet3': {} u'Ethernet4': {} } } } } """ raise NotImplementedError def get_firewall_policies(self): """ Returns a dictionary of lists of dictionaries where the first key is an unique policy name and the inner dictionary contains the following keys: * position (int) * packet_hits (int) * byte_hits (int) * id (text_type) * enabled (bool) * schedule (text_type) * log (text_type) * l3_src (text_type) * l3_dst (text_type) * service (text_type) * src_zone (text_type) * dst_zone (text_type) * action (text_type) Example:: { 'policy_name': [{ 'position': 1, 'packet_hits': 200, 'byte_hits': 83883, 'id': '230', 'enabled': True, 'schedule': 'Always', 'log': 'all', 'l3_src': 'any', 'l3_dst': 'any', 'service': 'HTTP', 'src_zone': 'port2', 'dst_zone': 'port3', 'action': 'Permit' }] } """ raise NotImplementedError def compliance_report(self, validation_file='validate.yml'): """ 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/index.html. """ return validate.compliance_report(self, validation_file=validation_file) napalm-base-0.25.0/napalm_base/constants.py0000644000175000017500000000247113150265625021461 0ustar travistravis00000000000000"""Constants to be used across NAPALM drivers.""" from __future__ import unicode_literals CONFIG_LOCK = True # must be changed soon! TIMEOUT = 60 # seconds INTERFACE_NULL_SPEED = -1 ACTION_TYPE_METHODS = ('ping', 'traceroute', ) BGP_NEIGHBOR_NULL_COUNTER = -1 SNMP_AUTHORIZATION_MODE_MAP = { 'read-only': 'ro', 'read-write': 'rw' } ROUTE_COMMON_PROTOCOL_FIELDS = [ 'destination', 'prefix_length', 'protocol', 'current_active', 'last_active', 'age', 'next_hop', 'outgoing_interface', 'selected_next_hop', 'preference', 'inactive_reason', 'routing_table' ] # identifies the list of fileds common for all protocols ROUTE_PROTOCOL_SPECIFIC_FIELDS = { 'bgp': [ 'local_as', 'remote_as', 'as_path', 'communities', 'local_preference', 'preference2', 'remote_address', 'metric', 'metric2' ], 'isis': [ 'level', 'metric', 'local_as' ], 'static': [ # nothing specific to static routes ] } TRACEROUTE_TTL = 255 TRACEROUTE_SOURCE = '' TRACEROUTE_TIMEOUT = 2 TRACEROUTE_NULL_HOST_NAME = '*' TRACEROUTE_NULL_IP_ADDRESS = '*' TRACEROUTE_VRF = '' OPTICS_NULL_LEVEL = '-Inf' PING_SOURCE = '' PING_TTL = 255 PING_TIMEOUT = 2 PING_SIZE = 100 PING_COUNT = 5 PING_VRF = '' napalm-base-0.25.0/napalm_base/exceptions.py0000644000175000017500000000461013150265625021623 0ustar travistravis00000000000000# Copyright 2015 Spotify AB. All rights reserved. # # The contents of this file are licensed under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with the # License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under # the License. # Python3 support from __future__ import print_function from __future__ import unicode_literals class ModuleImportError(Exception): pass 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 class ReplaceConfigException(Exception): pass 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 class CommandTimeoutException(Exception): pass class CommandErrorException(Exception): pass class DriverTemplateNotImplemented(Exception): pass class TemplateNotImplemented(Exception): pass class TemplateRenderException(Exception): pass class ValidationException(Exception): pass napalm-base-0.25.0/napalm_base/helpers.py0000644000175000017500000002220013150265625021077 0ustar travistravis00000000000000"""Helper functions for the NAPALM base.""" # Python3 support from __future__ import print_function from __future__ import unicode_literals # std libs import os import sys # third party libs import jinja2 import jtextfsm as textfsm from netaddr import EUI from netaddr import mac_unix from netaddr import IPAddress # local modules import napalm_base.exceptions from napalm_base.utils.jinja_filters import CustomJinjaFilters from napalm_base.utils import py23_compat # ---------------------------------------------------------------------------------------------------------------------- # helper classes -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- class _MACFormat(mac_unix): pass _MACFormat.word_fmt = '%.2X' # ---------------------------------------------------------------------------------------------------------------------- # callable helpers # ---------------------------------------------------------------------------------------------------------------------- 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: 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: search_path = ['{}/oc_templates'.format(s) for s in search_path] else: search_path = ['{}/templates'.format(s) for s in search_path] loader = jinja2.FileSystemLoader(search_path) environment = jinja2.Environment(loader=loader) for filter_name, filter_function in CustomJinjaFilters.filters().items(): environment.filters[filter_name] = filter_function template = environment.get_template('{template_name}.j2'.format( template_name=template_name )) configuration = template.render(**template_vars) except jinja2.exceptions.TemplateNotFound: raise napalm_base.exceptions.TemplateNotImplemented( "Config template {template_name}.j2 not found in search path: {sp}".format( template_name=template_name, sp=search_path ) ) except (jinja2.exceptions.UndefinedError, jinja2.exceptions.TemplateSyntaxError) as jinjaerr: raise napalm_base.exceptions.TemplateRenderException( "Unable to render the Jinja config template {template_name}: {error}".format( template_name=template_name, error=jinjaerr.message ) ) return cls.load_merge_candidate(config=configuration) def textfsm_extractor(cls, template_name, raw_text): """ Applies a TextFSM template over a raw text and return the matching table. Main usage of this method will be to extract data form a non-structured output from a network device and return the values in a table format. :param cls: Instance of the driver class :param template_name: Specifies the name of the template to be used :param raw_text: Text output as the devices prompts on the CLI :return: table-like list of entries """ textfsm_data = list() cls.__class__.__name__.replace('Driver', '') current_dir = os.path.dirname(os.path.abspath(sys.modules[cls.__module__].__file__)) template_dir_path = '{current_dir}/utils/textfsm_templates'.format( current_dir=current_dir ) template_path = '{template_dir_path}/{template_name}.tpl'.format( template_dir_path=template_dir_path, template_name=template_name ) try: fsm_handler = textfsm.TextFSM(open(template_path)) except IOError: raise napalm_base.exceptions.TemplateNotImplemented( "TextFSM template {template_name}.tpl is not defined under {path}".format( template_name=template_name, path=template_dir_path ) ) except textfsm.TextFSMTemplateError as tfte: raise napalm_base.exceptions.TemplateRenderException( "Wrong format of TextFSM template {template_name}: {error}".format( template_name=template_name, error=py23_compat.text_type(tfte) ) ) objects = fsm_handler.ParseText(raw_text) for obj in objects: index = 0 entry = {} for entry_value in obj: entry[fsm_handler.header[index].lower()] = entry_value index += 1 textfsm_data.append(entry) return textfsm_data def find_txt(xml_tree, path, default=''): """ Extracts the text value from an XML tree, using XPath. In case of error, will return a default value. :param xml_tree: the XML Tree object. Assumed is . :param path: XPath to be applied, in order to extract the desired data. :param default: Value to be returned in case of error. :return: a str value. """ value = '' try: xpath_applied = xml_tree.xpath(path) # will consider the first match only if len(xpath_applied) and xpath_applied[0] is not None: xpath_result = xpath_applied[0] if isinstance(xpath_result, type(xml_tree)): value = xpath_result.text.strip() else: value = xpath_result except Exception: # in case of any exception, returns default value = default return py23_compat.text_type(value) def convert(to, who, default=u''): """ Converts data to a specific datatype. In case of error, will return a default value. :param to: datatype to be casted to. :param who: value to cast. :param default: value to return in case of error. :return: a str value. """ if who is None: return default try: return to(who) except: # noqa return default def mac(raw): """ Converts a raw string to a standardised MAC Address EUI Format. :param raw: the raw string containing the value of the MAC Address :return: a string with the MAC Address in EUI format Example: .. code-block:: python >>> mac('0123.4567.89ab') u'01:23:45:67:89:AB' Some vendors like Cisco return MAC addresses like a9:c5:2e:7b:6: which is not entirely valid (with respect to EUI48 or EUI64 standards). Therefore we need to stuff with trailing zeros Example >>> mac('a9:c5:2e:7b:6:') u'A9:C5:2E:7B:60:00' If Cisco or other obscure vendors use their own standards, will throw an error and we can fix later, however, still works with weird formats like: >>> mac('123.4567.89ab') u'01:23:45:67:89:AB' >>> mac('23.4567.89ab') u'00:23:45:67:89:AB' """ if raw.endswith(':'): flat_raw = raw.replace(':', '') raw = '{flat_raw}{zeros_stuffed}'.format( flat_raw=flat_raw, zeros_stuffed='0'*(12-len(flat_raw)) ) return py23_compat.text_type(EUI(raw, dialect=_MACFormat)) def ip(addr, version=None): """ 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 \ the same logical value as 2001:db8:85a3::8a2e:370:7334. However, their values as strings are \ 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) Example: .. code-block:: python >>> ip('2001:0dB8:85a3:0000:0000:8A2e:0370:7334') u'2001:db8:85a3::8a2e:370:7334' """ addr_obj = IPAddress(addr) if version and addr_obj.version != version: raise ValueError("{} is not an ipv{} address".format(addr, version)) return py23_compat.text_type(addr_obj) def as_number(as_number_val): """Convert AS Number to standardized asplain notation as an integer.""" as_number_str = py23_compat.text_type(as_number_val) if '.' in as_number_str: big, little = as_number_str.split('.') return (int(big) << 16) + int(little) else: return int(as_number_str) napalm-base-0.25.0/napalm_base/mock.py0000644000175000017500000001465013150265625020400 0ustar travistravis00000000000000# Copyright 2017 Dravetech AB. All rights reserved. # # The contents of this file are licensed under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with the # License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under # the License. # Python3 support from __future__ import print_function from __future__ import unicode_literals from napalm_base.base import NetworkDriver import napalm_base.exceptions import inspect import json import os import re from pydoc import locate def raise_exception(result): exc = locate(result["exception"]) if exc: raise exc(*result.get("args", []), **result.get("kwargs", {})) else: raise TypeError("Couldn't resolve exception {}", result["exception"]) def is_mocked_method(method): mocked_methods = [] if method.startswith("get_") or method in mocked_methods: return True return False def mocked_method(path, name, count): parent_method = getattr(NetworkDriver, name) parent_method_args = inspect.getargspec(parent_method) modifier = 0 if 'self' not in parent_method_args.args else 1 def _mocked_method(*args, **kwargs): # Check len(args) if len(args) + len(kwargs) + modifier > len(parent_method_args.args): raise TypeError( "{}: expected at most {} arguments, got {}".format( name, len(parent_method_args.args), len(args) + modifier)) # Check kwargs unexpected = [x for x in kwargs if x not in parent_method_args.args] if unexpected: raise TypeError("{} got an unexpected keyword argument '{}'".format(name, unexpected[0])) return mocked_data(path, name, count) return _mocked_method def mocked_data(path, name, count): filename = "{}.{}".format(os.path.join(path, name), count) try: with open(filename) as f: result = json.loads(f.read()) except IOError: raise NotImplementedError("You can provide mocked data in {}".format(filename)) if "exception" in result: raise_exception(result) else: return result class MockDevice(object): def __init__(self, parent, profile): self.parent = parent self.profile = profile def run_commands(self, commands): """Only useful for EOS""" if "eos" in self.profile: return list(self.parent.cli(commands).values())[0] else: raise AttributeError("MockedDriver instance has not attribute '_rpc'") class MockDriver(NetworkDriver): def __init__(self, hostname, username, password, timeout=60, optional_args=None): """ Supported optional_args: * path(str) - path to where the mocked files are located * profile(list) - List of profiles to assign """ self.hostname = hostname self.username = username self.password = password self.path = optional_args["path"] self.profile = optional_args.get("profile", []) self.fail_on_open = optional_args.get("fail_on_open", False) self.opened = False self.calls = {} self.device = MockDevice(self, self.profile) # None no action, True load_merge, False load_replace self.merge = None self.filename = None self.config = None def _count_calls(self, name): current_count = self.calls.get(name, 0) self.calls[name] = current_count + 1 return self.calls[name] def _raise_if_closed(self): if not self.opened: raise napalm_base.exceptions.ConnectionClosedException("connection closed") def open(self): if self.fail_on_open: raise napalm_base.exceptions.ConnectionException("You told me to do this") self.opened = True def close(self): self.opened = False def is_alive(self): return {"is_alive": self.opened} def cli(self, commands): count = self._count_calls("cli") result = {} regexp = re.compile('[^a-zA-Z0-9]+') for i, c in enumerate(commands): sanitized = re.sub(regexp, '_', c) name = "cli.{}.{}".format(count, sanitized) filename = "{}.{}".format(os.path.join(self.path, name), i) with open(filename, 'r') as f: result[c] = f.read() return result def load_merge_candidate(self, filename=None, config=None): count = self._count_calls("load_merge_candidate") self._raise_if_closed() self.merge = True self.filename = filename self.config = config mocked_data(self.path, "load_merge_candidate", count) def load_replace_candidate(self, filename=None, config=None): count = self._count_calls("load_replace_candidate") self._raise_if_closed() self.merge = False self.filename = filename self.config = config mocked_data(self.path, "load_replace_candidate", count) def compare_config(self, filename=None, config=None): count = self._count_calls("compare_config") self._raise_if_closed() return mocked_data(self.path, "compare_config", count)["diff"] def commit_config(self): count = self._count_calls("commit_config") self._raise_if_closed() self.merge = None self.filename = None self.config = None mocked_data(self.path, "commit_config", count) def discard_config(self): count = self._count_calls("commit_config") self._raise_if_closed() self.merge = None self.filename = None self.config = None mocked_data(self.path, "discard_config", count) def _rpc(self, get): """This one is only useful for junos.""" return list(self.cli([get]).values())[0] def __getattribute__(self, name): if is_mocked_method(name): self._raise_if_closed() count = self._count_calls(name) return mocked_method(self.path, name, count) else: return object.__getattribute__(self, name) napalm-base-0.25.0/napalm_base/validate.py0000644000175000017500000001415613150265625021241 0ustar travistravis00000000000000""" Validation methods for the NAPALM base. See: https://napalm.readthedocs.io/en/latest/validate.html """ from __future__ import unicode_literals import yaml from napalm_base.exceptions import ValidationException from napalm_base.utils import py23_compat import copy import re # We put it here to compile it only once numeric_compare_regex = re.compile("^(<|>|<=|>=|==|!=)(\d+(\.\d+){0,1})$") def _get_validation_file(validation_file): try: with open(validation_file, 'r') as stream: try: validation_source = yaml.load(stream) except yaml.YAMLError as exc: raise ValidationException(exc) except IOError: raise ValidationException("File {0} not found.".format(validation_file)) return validation_source def _mode(mode_string): mode = {'strict': False} for m in mode_string.split(): if m not in mode.keys(): raise ValidationException("mode '{}' not recognized".format(m)) mode[m] = True return mode def _compare_getter_list(src, dst, mode): result = {"complies": True, "present": [], "missing": [], "extra": []} for src_element in src: found = False i = 0 while True: try: intermediate_match = _compare_getter(src_element, dst[i]) if isinstance(intermediate_match, dict) and intermediate_match["complies"] or \ not isinstance(intermediate_match, dict) and intermediate_match: found = True result["present"].append(src_element) dst.pop(i) break else: i += 1 except IndexError: break if not found: result["complies"] = False result["missing"].append(src_element) if mode["strict"] and dst: result["extra"] = dst result["complies"] = False return result def _compare_getter_dict(src, dst, mode): result = {"complies": True, "present": {}, "missing": [], "extra": []} dst = copy.deepcopy(dst) # Otherwise we are going to modify a "live" object for key, src_element in src.items(): try: dst_element = dst.pop(key) result["present"][key] = {} intermediate_result = _compare_getter(src_element, dst_element) if isinstance(intermediate_result, dict): nested = True complies = intermediate_result["complies"] if not complies: result["present"][key]['diff'] = intermediate_result else: complies = intermediate_result nested = False if not complies: result["present"][key]["expected_value"] = src_element result["present"][key]["actual_value"] = dst_element if not complies: result["complies"] = False result["present"][key]["complies"] = complies result["present"][key]["nested"] = nested except KeyError: result["missing"].append(key) result["complies"] = False if mode["strict"] and dst: result["extra"] = list(dst.keys()) result["complies"] = False return result def _compare_getter(src, dst): if isinstance(src, py23_compat.string_types): src = py23_compat.text_type(src) if isinstance(src, dict): mode = _mode(src.pop('_mode', '')) if 'list' in src.keys(): if not isinstance(dst, list): # This can happen with nested lists return False return _compare_getter_list(src['list'], dst, mode) return _compare_getter_dict(src, dst, mode) elif isinstance(src, py23_compat.string_types): if src.startswith('<') or src.startswith('>'): cmp_result = compare_numeric(src, dst) return cmp_result else: m = re.search(src, py23_compat.text_type(dst)) if m: return bool(m) else: return src == dst elif(type(src) == type(dst) == list): pairs = zip(src, dst) diff_lists = [[(k, x[k], y[k]) for k in x if not re.search(x[k], y[k])] for x, y in pairs if x != y] return empty_tree(diff_lists) else: return src == dst def compare_numeric(src_num, dst_num): """Compare numerical values. You can use '<%d','>%d'.""" dst_num = float(dst_num) match = numeric_compare_regex.match(src_num) if not match: error = "Failed numeric comparison. Collected: {}. Expected: {}".format(dst_num, src_num) raise ValueError(error) operand = { "<": "__lt__", ">": "__gt__", ">=": "__ge__", "<=": "__le__", "==": "__eq__", "!=": "__ne__", } return getattr(dst_num, operand[match.group(1)])(float(match.group(2))) def empty_tree(input_list): """Recursively iterate through values in nested lists.""" for item in input_list: if not isinstance(item, list) or not empty_tree(item): return False return True def compliance_report(cls, validation_file=None): report = {} validation_source = _get_validation_file(validation_file) for validation_check in validation_source: for getter, expected_results in validation_check.items(): if getter == "get_config": # TBD pass else: key = expected_results.pop("_name", "") or getter try: kwargs = expected_results.pop('_kwargs', {}) actual_results = getattr(cls, getter)(**kwargs) report[key] = _compare_getter(expected_results, actual_results) except NotImplementedError: report[key] = {"skipped": True, "reason": "NotImplemented"} complies = all([e.get("complies", True) for e in report.values()]) report["skipped"] = [k for k, v in report.items() if v.get("skipped", False)] report["complies"] = complies return report napalm-base-0.25.0/napalm_base.egg-info/0000755000175000017500000000000013150265743020562 5ustar travistravis00000000000000napalm-base-0.25.0/napalm_base.egg-info/PKG-INFO0000644000175000017500000000143513150265742021661 0ustar travistravis00000000000000Metadata-Version: 1.1 Name: napalm-base 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, Kirk Byers, Mircea Ulinic Author-email: dbarrosop@dravetech.com, ping@mirceaulinic.net, ktbyers@twb-tech.com License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN Classifier: Topic :: Utilities Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Operating System :: POSIX :: Linux Classifier: Operating System :: POSIX :: Linux Classifier: Operating System :: MacOS napalm-base-0.25.0/napalm_base.egg-info/SOURCES.txt0000644000175000017500000000173613150265743022455 0ustar travistravis00000000000000MANIFEST.in requirements.txt setup.cfg setup.py napalm_base/__init__.py 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 napalm_base.egg-info/dependency_links.txt 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 napalm_base/clitools/helpers.py napalm_base/test/__init__.py napalm_base/test/base.py napalm_base/test/conftest.py napalm_base/test/double.py napalm_base/test/getters.py napalm_base/test/helpers.py napalm_base/test/models.py napalm_base/utils/__init__.py napalm_base/utils/jinja_filters.py napalm_base/utils/py23_compat.py napalm_base/utils/string_parsers.py test/__init__.pynapalm-base-0.25.0/napalm_base.egg-info/dependency_links.txt0000644000175000017500000000000113150265742024627 0ustar travistravis00000000000000 napalm-base-0.25.0/napalm_base.egg-info/entry_points.txt0000644000175000017500000000040013150265742024051 0ustar travistravis00000000000000[console_scripts] 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 napalm-base-0.25.0/napalm_base.egg-info/requires.txt0000644000175000017500000000003713150265742023161 0ustar travistravis00000000000000jtextfsm jinja2 netaddr pyYAML napalm-base-0.25.0/napalm_base.egg-info/top_level.txt0000644000175000017500000000002113150265742023304 0ustar travistravis00000000000000napalm_base test napalm-base-0.25.0/test/0000755000175000017500000000000013150265743015605 5ustar travistravis00000000000000napalm-base-0.25.0/test/__init__.py0000644000175000017500000000000013150265625017703 0ustar travistravis00000000000000napalm-base-0.25.0/MANIFEST.in0000644000175000017500000000003113150265625016355 0ustar travistravis00000000000000include requirements.txt napalm-base-0.25.0/requirements.txt0000644000175000017500000000003713150265625020111 0ustar travistravis00000000000000jtextfsm jinja2 netaddr pyYAML napalm-base-0.25.0/setup.py0000644000175000017500000000275413150265625016347 0ustar travistravis00000000000000"""setup.py file.""" import uuid from setuptools import setup, find_packages from pip.req import parse_requirements __author__ = 'David Barroso ' install_reqs = parse_requirements('requirements.txt', session=uuid.uuid1()) reqs = [str(ir.req) for ir in install_reqs] setup( name="napalm-base", version='0.25.0', packages=find_packages(), author="David Barroso, Kirk Byers, Mircea Ulinic", author_email="dbarrosop@dravetech.com, ping@mirceaulinic.net, ktbyers@twb-tech.com", description="Network Automation and Programmability Abstraction Layer with Multivendor support", classifiers=[ 'Topic :: Utilities', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Operating System :: POSIX :: Linux', 'Operating System :: POSIX :: Linux', 'Operating System :: MacOS', ], url="https://github.com/napalm-automation/napalm-base", include_package_data=True, install_requires=reqs, entry_points={ 'console_scripts': [ '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', ], } ) napalm-base-0.25.0/PKG-INFO0000644000175000017500000000143513150265743015726 0ustar travistravis00000000000000Metadata-Version: 1.1 Name: napalm-base 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, Kirk Byers, Mircea Ulinic Author-email: dbarrosop@dravetech.com, ping@mirceaulinic.net, ktbyers@twb-tech.com License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN Classifier: Topic :: Utilities Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Operating System :: POSIX :: Linux Classifier: Operating System :: POSIX :: Linux Classifier: Operating System :: MacOS napalm-base-0.25.0/setup.cfg0000644000175000017500000000101613150265743016445 0ustar travistravis00000000000000[pylama] linters = mccabe,pep8,pyflakes ignore = D203,C901 skip = .tox/* [pylama:pep8] max_line_length = 100 [tool:pytest] norecursedirs = .git .tox .env dist build south_migraitons migrations napalm_base python_files = test_*.py *_test.py tests.py addopts = --cov=napalm_base --cov-report term-missing -vs --pylama json_report = report.json jsonapi = true [coverage:run] include = napalm_base/* [coverage:report] omit = napalm_base/test/* [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0