def main(): password = getpass() nxos1 = { 'host': 'nxos1.twb-tech.com', 'username': '******', 'password': password, 'transport': 'https', 'port': 8443, } nxos2 = { # noqa 'host': 'nxos2.twb-tech.com', 'username': '******', 'password': password, 'transport': 'https', 'port': 8443, } print() for device in (nxos1, ): nxapi_conn = Device(**device) print('-' * 40) route_table = nxapi_conn.show('show ip route vrf management') route_table = process_route_table(route_table) for route_entry in route_table: if route_entry['ipprefix'] == '0.0.0.0/0': next_hop = extract_next_hop(route_entry) print("Default Gateway: {}".format(next_hop)) break print('-' * 40) print()
def main(): requests.packages.urllib3.disable_warnings(InsecureRequestWarning) password = getpass() for host in ['nxos1.twb-tech.com', 'nxos2.twb-tech.com']: device = Device(host=host, username='******', password=password, transport='https', port=8443) result = device.show('show ip route vrf management') # ipnexthop = result['TABLE_vrf']['ROW_vrf']['TABLE_addrf']['ROW_addrf']['TABLE_prefix']['ROW_prefix'][0]['TABLE_path']['ROW_path']['ipnexthop'] route_table = result['TABLE_vrf']['ROW_vrf']['TABLE_addrf'][ 'ROW_addrf']['TABLE_prefix']['ROW_prefix'] print() print(host) print('-' * 40) print(route_table) print('-' * 40) for route_entry in route_table: if route_entry['ipprefix'] == '0.0.0.0/0': next_hop = route_entry['TABLE_path']['ROW_path']['ipnexthop'] print("Default Gateway: {}".format(next_hop)) break print('-' * 40) print()
def main(): requests.packages.urllib3.disable_warnings(InsecureRequestWarning) for host in ['nxos1.twb-tech.com', 'nxos2.twb-tech.com']: device = Device(host=host, username='******', password=getpass(), transport='https', port=8443) print(device.show('show hostname'))
def main(): requests.packages.urllib3.disable_warnings(InsecureRequestWarning) hosts = ['nxos1.twb-tech.com', 'nxos2.twb-tech.com'] password = getpass() command_list = ['interface Loopback66', 'ip address 172.31.255.66/32'] for host in hosts: device = Device(host=host, username='******', password=password, transport='https', port=8443) device.config_list(command_list) result = device.show('show run interface loopback66', raw_text=True) print(result)
def main(): requests.packages.urllib3.disable_warnings(InsecureRequestWarning) for host in ['nxos1.twb-tech.com', 'nxos2.twb-tech.com']: device = Device(host=host, username='******', password=getpass(), transport='https', port=8443) result = device.show('show ip route vrf management') ipnexthop = result['TABLE_vrf']['ROW_vrf']['TABLE_addrf']['ROW_addrf'][ 'TABLE_prefix']['ROW_prefix'][0]['TABLE_path']['ROW_path'][ 'ipnexthop'] print() print("The next hop IP address for the default route is {}.".format( ipnexthop)) print()
def open(self): try: self.device = NXOSDevice(self.hostname, self.username, self.password, timeout=self.timeout, port=self.port, transport=self.transport) self.device.show('show hostname') self.up = True except (CLIError, ValueError): # unable to open connection raise ConnectionException('Cannot connect to {}'.format(self.hostname))
def open(self): try: self.device = NXOSDevice(self.hostname, self.username, self.password, timeout=self.timeout, port=self.port, transport=self.transport) self.device.show('show hostname') self.up = True except (CLIError, ValueError): # unable to open connection raise ConnectionException('Cannot connect to {}'.format( self.hostname))
def main(): password = getpass() nxos1 = { 'host': 'nxos1.twb-tech.com', 'username': '******', 'password': password, 'transport': 'https', 'port': 8443, } nxos2 = { # noqa 'host': 'nxos2.twb-tech.com', 'username': '******', 'password': password, 'transport': 'https', 'port': 8443, } config_commands = ['interface Loopback99', 'ip address 172.31.254.99/32'] for device in (nxos1, ): nxapi_conn = Device(**device) nxapi_conn.config_list(config_commands) output = nxapi_conn.show('show run interface loopback99', raw_text=True) print(output)
def __init__(self, host, username, password, transport="http", timeout=30, port=None, **kwargs): super().__init__(host, username, password, device_type="cisco_nxos_nxapi") self.transport = transport self.timeout = timeout self.native = NXOSNative(host, username, password, transport=transport, timeout=timeout, port=port)
def __init__(self, host, username, password, transport='http', timeout=30, port=None, **kwargs): super(NXOSDevice, self).__init__(host, username, password, vendor='cisco', device_type='cisco_nxos_nxapi') self.transport = transport self.timeout = timeout self.native = NXOSNative(host, username, password, transport=transport, timeout=timeout, port=port)
def main(): """Configure BGP using NX-API.""" # Nexus switches switch_ip = raw_input("Enter switch IP: ") username = '******' password = getpass() eth_intf = "Ethernet 2/1" intf_ip = '10.1.4.1/30' peer_ip = '10.1.4.2' as_number = '10' config_eth = ['interface ' + eth_intf, 'ip address ' + intf_ip] config_bgp = [ 'license grace-period', 'feature bgp', 'router bgp ' + as_number, 'neighbor {} remote-as {}'.format(peer_ip, as_number), 'address-family ipv4 unicast', ] nxs = Device(host=switch_ip, username=username, password=password, transport='https', port=8443) print "\nCurrent Ethernet config: " cmd = 'show run int {}'.format(eth_intf) print nxs.show(cmd, raw_text=True) print "\nConfig Eth Interface:" results = nxs.config_list(config_eth) if check_nxapi_errors(results): print "...interface configured successfully." print "\nCurrent Ethernet config: " cmd = 'show run int {}'.format(eth_intf) print nxs.show(cmd, raw_text=True) print print "-" * 50 print "BGP Config" print "-" * 50 cmd = 'show run bgp' print nxs.show(cmd, raw_text=True) print "\nConfigure BGP" results = nxs.config_list(config_bgp) if check_nxapi_errors(results): print "...BGP configured successfully." cmd = 'show run bgp' print nxs.show(cmd, raw_text=True) print "\nVerify BGP (requires both peers)." print "Sleeping..." time.sleep(15) cmd = 'show bgp session' bgp_out = nxs.show(cmd) pprint(bgp_out) print "\nSaving config..." print nxs.save()
def setUp(self, mock_rpc): self.device = Device("host", "user", "pass") self.rpc = mock_rpc self.send_request = mock_rpc.return_value.send_request self.send_request.side_effect = send_request
class NXOSDevice(BaseDevice): def __init__(self, host, username, password, transport='http', timeout=30, port=None, **kwargs): super(NXOSDevice, self).__init__(host, username, password, vendor='cisco', device_type=NXOS_API_DEVICE_TYPE) self.transport = transport self.timeout = timeout self.native = NXOSNative(host, username, password, transport=transport, timeout=timeout, port=port) def open(self): pass def close(self): pass def set_timeout(self, timeout): self.native.timeout = timeout def config(self, command): try: self.native.config(command) except CLIError as e: raise CommandError(command, str(e)) def config_list(self, commands): try: self.native.config_list(commands) except CLIError as e: raise CommandListError(commands, e.command, str(e)) def show(self, command, raw_text=False): try: return strip_unicode(self.native.show(command, raw_text=raw_text)) except CLIError as e: raise CommandError(command, str(e)) def show_list(self, commands, raw_text=False): try: return strip_unicode(self.native.show_list(commands, raw_text=raw_text)) except CLIError as e: raise CommandListError(commands, e.command, str(e)) def save(self, filename='startup-config'): return self.native.save(filename=filename) def file_copy_remote_exists(self, src, dest): return self.native.file_copy_remote_exists(src, dest) def file_copy(self, src, dest): try: return self.native.file_copy(src, dest) except NXOSFileTransferError: raise FileTransferError def reboot(self, timer=0, confirm=False): self.native.reboot(confirm=confirm) def get_boot_options(self): return self.native.get_boot_options() def set_boot_options(self, image_name, kickstart=None, **vendor_specifics): return self.native.set_boot_options(image_name, kickstart=kickstart) def checkpoint(self, filename): return self.native.checkpoint(filename) def rollback(self, filename): try: self.native.rollback(filename) except CLIError: raise RollbackError('Rollback unsuccessful, %s may not exist.' % filename) def backup_running_config(self, filename): self.native.backup_running_config(filename) @property def facts(self): if hasattr(self, '_facts'): return self._facts facts = strip_unicode(self.native.facts) facts['vendor'] = self.vendor self._facts = facts return self._facts @property def running_config(self): return self.native.running_config @property def startup_config(self): return self.show('show startup-config', raw_text=True)
class NXOSDriver(NetworkDriver): def __init__(self, hostname, username, password, timeout=60, optional_args=None): if optional_args is None: optional_args = {} self.hostname = hostname self.username = username self.password = password self.timeout = timeout self.up = False self.replace = True self.loaded = False self.fc = None self.changed = False self.replace_file = None self.merge_candidate = '' if optional_args is None: optional_args = {} self.transport = optional_args.get('nxos_protocol', 'http') if self.transport == 'https': self.port = optional_args.get('port', 443) elif self.transport == 'http': self.port = optional_args.get('port', 80) def open(self): try: self.device = NXOSDevice(self.hostname, self.username, self.password, timeout=self.timeout, port=self.port, transport=self.transport) self.device.show('show hostname') self.up = True except (CLIError, ValueError): # unable to open connection raise ConnectionException('Cannot connect to {}'.format( self.hostname)) def close(self): if self.changed: self._delete_file(self.backup_file) self.device = None @staticmethod def _compute_timestamp(stupid_cisco_output): """ Some fields such `uptime` are returned as: 23week(s) 3day(s) This method will determine the epoch of the event. e.g.: 23week(s) 3day(s) -> 1462248287 """ if not stupid_cisco_output or stupid_cisco_output == 'never': return -1.0 if '(s)' in stupid_cisco_output: pass elif ':' in stupid_cisco_output: stupid_cisco_output = stupid_cisco_output.replace( ':', 'hour(s) ', 1) stupid_cisco_output = stupid_cisco_output.replace( ':', 'minute(s) ', 1) stupid_cisco_output += 'second(s)' else: stupid_cisco_output = stupid_cisco_output.replace('d', 'day(s) ') stupid_cisco_output = stupid_cisco_output.replace('h', 'hour(s)') things = { 'second(s)': { 'weight': 1 }, 'minute(s)': { 'weight': 60 }, 'hour(s)': { 'weight': 3600 }, 'day(s)': { 'weight': 24 * 3600 }, 'week(s)': { 'weight': 7 * 24 * 3600 }, 'year(s)': { 'weight': 365.25 * 24 * 3600 } } things_keys = things.keys() for part in stupid_cisco_output.split(): for key in things_keys: if key in part: things[key]['count'] = napalm_base.helpers.convert( int, part.replace(key, ''), 0) delta = sum([ det.get('count', 0) * det.get('weight') for det in things.values() ]) return time.time() - delta @staticmethod def _get_reply_body(result): # useful for debugging ret = result.get('ins_api', {}).get('outputs', {}).get('output', {}).get('body', {}) # Original 'body' entry may have been an empty string, don't return that. if not isinstance(ret, dict): return {} return ret @staticmethod def _get_table_rows(parent_table, table_name, row_name): # because if an inconsistent piece of shit. # {'TABLE_intf': [{'ROW_intf': { # vs # {'TABLE_mac_address': {'ROW_mac_address': [{ # vs # {'TABLE_vrf': {'ROW_vrf': {'TABLE_adj': {'ROW_adj': { _table = parent_table.get(table_name) _table_rows = [] if isinstance(_table, list): _table_rows = [_table_row.get(row_name) for _table_row in _table] elif isinstance(_table, dict): _table_rows = _table.get(row_name) if not isinstance(_table_rows, list): _table_rows = [_table_rows] return _table_rows @staticmethod def fix_checkpoint_string(string, filename): # used to generate checkpoint-like files pattern = '''!Command: Checkpoint cmd vdc 1''' if '!Command' in string: return re.sub('!Command.*', pattern.format(filename), string) else: return "{0}\n{1}".format(pattern.format(filename), string) def _get_reply_table(self, result, table_name, row_name): return self._get_table_rows(result, table_name, row_name) def _get_command_table(self, command, table_name, row_name): json_output = self.device.show(command) return self._get_reply_table(json_output, table_name, row_name) def is_alive(self): if self.device: return {'is_alive': True} else: return {'is_alive': False} def load_replace_candidate(self, filename=None, config=None): self.replace = True self.loaded = True if not filename and not config: raise ReplaceConfigException( 'filename or config param must be provided.') if filename is None: temp_file = tempfile.NamedTemporaryFile() temp_file.write(config) temp_file.flush() cfg_filename = temp_file.name else: cfg_filename = filename self.replace_file = cfg_filename with open(self.replace_file, 'r') as f: file_content = f.read() file_content = self.fix_checkpoint_string(file_content, self.replace_file) temp_file = tempfile.NamedTemporaryFile() temp_file.write(file_content) temp_file.flush() self.replace_file = cfg_filename self._send_file(temp_file.name, cfg_filename) def load_merge_candidate(self, filename=None, config=None): self.replace = False self.loaded = True if not filename and not config: raise MergeConfigException( 'filename or config param must be provided.') self.merge_candidate += '\n' # insert one extra line if filename is not None: with open(filename, "r") as f: self.merge_candidate += f.read() else: self.merge_candidate += config def _send_file(self, filename, dest): self.fc = FileCopy(self.device, filename, dst=dest.split('/')[-1]) try: if not self.fc.remote_file_exists(): self.fc.send() elif not self.fc.file_already_exists(): commands = [ 'terminal dont-ask', 'delete {0}'.format(self.fc.dst) ] self.device.config_list(commands) self.fc.send() except NXOSFileTransferError as fte: raise ReplaceConfigException(fte.message) def _create_sot_file(self): """Create Source of Truth file to compare.""" commands = ['terminal dont-ask', 'checkpoint file sot_file'] self.device.config_list(commands) def _get_diff(self, cp_file): """Get a diff between running config and a proposed file.""" diff = [] self._create_sot_file() diff_out = self.device.show( 'show diff rollback-patch file {0} file {1}'.format( 'sot_file', self.replace_file.split('/')[-1]), raw_text=True) try: diff_out = diff_out.split('#Generating Rollback Patch')[1].replace( 'Rollback Patch is Empty', '').strip() for line in diff_out.splitlines(): if line: if line[0].strip() != '!': diff.append(line.rstrip(' ')) except (AttributeError, KeyError): raise ReplaceConfigException( 'Could not calculate diff. It\'s possible the given file doesn\'t exist.' ) return '\n'.join(diff) def _get_merge_diff(self): diff = [] running_config = self.get_config(retrieve='running')['running'] running_lines = running_config.splitlines() for line in self.merge_candidate.splitlines(): if line not in running_lines and line: if line[0].strip() != '!': diff.append(line) return '\n'.join(diff) # the merge diff is not necessarily what needs to be loaded # for example under NTP, as the `ntp commit` command might be # alread configured, it is mandatory to be sent # otherwise it won't take the new configuration - see #59 # https://github.com/napalm-automation/napalm-nxos/issues/59 # therefore this method will return the real diff # but the merge_candidate will remain unchanged # previously: self.merge_candidate = '\n'.join(diff) def compare_config(self): if self.loaded: if not self.replace: return self._get_merge_diff() # return self.merge_candidate diff = self._get_diff(self.fc.dst) return diff return '' def _commit_merge(self): commands = [ command for command in self.merge_candidate.splitlines() if command ] self.device.config_list(commands) if not self.device.save(): raise CommandErrorException('Unable to commit config!') def _save_config(self, filename): """Save the current running config to the given file.""" self.device.show('checkpoint file {}'.format(filename), raw_text=True) def _disable_confirmation(self): self.device.config('terminal dont-ask') def _load_config(self): cmd = 'rollback running file {0}'.format( self.replace_file.split('/')[-1]) self._disable_confirmation() try: rollback_result = self.device.config(cmd) except ConnectionError: # requests will raise an error with verbose warning output return True except Exception: return False if 'Rollback failed.' in rollback_result['msg']: raise ReplaceConfigException(rollback_result['msg']) return True def commit_config(self): if self.loaded: self.backup_file = 'config_' + str(datetime.now()).replace( ' ', '_') self._save_config(self.backup_file) if self.replace: if self._load_config() is False: raise ReplaceConfigException else: try: self._commit_merge() self.merge_candidate = '' # clear the merge buffer except Exception as e: raise MergeConfigException(str(e)) self.changed = True self.loaded = False else: raise ReplaceConfigException('No config loaded.') def _delete_file(self, filename): commands = [ 'terminal dont-ask', 'delete {}'.format(filename), 'no terminal dont-ask' ] self.device.show_list(commands, raw_text=True) def discard_config(self): if self.loaded: self.merge_candidate = '' # clear the buffer if self.loaded and self.replace: try: self._delete_file(self.fc.dst) except CLIError: pass self.loaded = False def rollback(self): if self.changed: self.device.rollback(self.backup_file) self.device.save() self.changed = False def get_facts(self): pynxos_facts = self.device.facts final_facts = { key: value for key, value in pynxos_facts.items() if key not in ['interfaces', 'uptime_string', 'vlans'] } if pynxos_facts['interfaces']: final_facts['interface_list'] = pynxos_facts['interfaces'] else: final_facts['interface_list'] = self.get_interfaces().keys() final_facts['vendor'] = 'Cisco' hostname_cmd = 'show hostname' hostname = self.device.show(hostname_cmd).get('hostname') if hostname: final_facts['fqdn'] = hostname return final_facts def get_interfaces(self): interfaces = {} iface_cmd = 'show interface' interfaces_out = self.device.show(iface_cmd) interfaces_body = interfaces_out['TABLE_interface']['ROW_interface'] for interface_details in interfaces_body: interface_name = interface_details.get('interface') # Earlier version of Nexus returned a list for 'eth_bw' (observed on 7.1(0)N1(1a)) interface_speed = interface_details.get('eth_bw', 0) if isinstance(interface_speed, list): interface_speed = interface_speed[0] interface_speed = int(interface_speed / 1000) if 'admin_state' in interface_details: is_up = interface_details.get('admin_state', '') == 'up' else: is_up = interface_details.get('state', '') == 'up' interfaces[interface_name] = { 'is_up': is_up, 'is_enabled': (interface_details.get('state') == 'up'), 'description': py23_compat.text_type( interface_details.get('desc', '').strip('"')), 'last_flapped': self._compute_timestamp( interface_details.get('eth_link_flapped', '')), 'speed': interface_speed, 'mac_address': napalm_base.helpers.convert( napalm_base.helpers.mac, interface_details.get('eth_hw_addr')), } return interfaces def get_lldp_neighbors(self): results = {} try: command = 'show lldp neighbors' lldp_raw_output = self.cli([command]).get(command, '') lldp_neighbors = napalm_base.helpers.textfsm_extractor( self, 'lldp_neighbors', lldp_raw_output) except CLIError: lldp_neighbors = [] for neighbor in lldp_neighbors: local_iface = neighbor.get('local_interface') if neighbor.get(local_iface) is None: if local_iface not in results: results[local_iface] = [] neighbor_dict = {} neighbor_dict['hostname'] = py23_compat.text_type( neighbor.get('neighbor')) neighbor_dict['port'] = py23_compat.text_type( neighbor.get('neighbor_interface')) results[local_iface].append(neighbor_dict) return results def get_bgp_neighbors(self): results = {} try: cmd = 'show bgp sessions vrf all' vrf_list = self._get_command_table(cmd, 'TABLE_vrf', 'ROW_vrf') except CLIError: vrf_list = [] for vrf_dict in vrf_list: result_vrf_dict = {} result_vrf_dict['router_id'] = py23_compat.text_type( vrf_dict['router-id']) result_vrf_dict['peers'] = {} neighbors_list = vrf_dict.get('TABLE_neighbor', {}).get('ROW_neighbor', []) if isinstance(neighbors_list, dict): neighbors_list = [neighbors_list] for neighbor_dict in neighbors_list: neighborid = napalm_base.helpers.ip( neighbor_dict['neighbor-id']) remoteas = napalm_base.helpers.as_number( neighbor_dict['remoteas']) result_peer_dict = { 'local_as': int(vrf_dict['local-as']), 'remote_as': remoteas, 'remote_id': neighborid, 'is_enabled': True, 'uptime': -1, 'description': py23_compat.text_type(''), 'is_up': True } result_peer_dict['address_family'] = { 'ipv4': { 'sent_prefixes': -1, 'accepted_prefixes': -1, 'received_prefixes': -1 } } result_vrf_dict['peers'][neighborid] = result_peer_dict vrf_name = vrf_dict['vrf-name-out'] if vrf_name == 'default': vrf_name = 'global' results[vrf_name] = result_vrf_dict return results def _set_checkpoint(self, filename): commands = [ 'terminal dont-ask', 'checkpoint file {0}'.format(filename) ] self.device.config_list(commands) def _get_checkpoint_file(self): filename = 'temp_cp_file_from_napalm' self._set_checkpoint(filename) cp_out = self.device.show('show file {0}'.format(filename), raw_text=True) self._delete_file(filename) return cp_out def get_lldp_neighbors_detail(self, interface=''): lldp_neighbors = {} filter = '' if interface: filter = 'interface {name} '.format(name=interface) command = 'show lldp neighbors {filter}detail'.format(filter=filter) # seems that some old devices may not return JSON output... try: lldp_neighbors_table_str = self.cli([command]).get(command) # thus we need to take the raw text output lldp_neighbors_list = lldp_neighbors_table_str.splitlines() except CLIError: lldp_neighbors_list = [] if not lldp_neighbors_list: return lldp_neighbors # empty dict CHASSIS_REGEX = '^(Chassis id:)\s+([a-z0-9\.]+)$' PORT_REGEX = '^(Port id:)\s+([0-9]+)$' LOCAL_PORT_ID_REGEX = '^(Local Port id:)\s+(.*)$' PORT_DESCR_REGEX = '^(Port Description:)\s+(.*)$' SYSTEM_NAME_REGEX = '^(System Name:)\s+(.*)$' SYSTEM_DESCR_REGEX = '^(System Description:)\s+(.*)$' SYST_CAPAB_REEGX = '^(System Capabilities:)\s+(.*)$' ENABL_CAPAB_REGEX = '^(Enabled Capabilities:)\s+(.*)$' VLAN_ID_REGEX = '^(Vlan ID:)\s+(.*)$' lldp_neighbor = {} interface_name = None for line in lldp_neighbors_list: chassis_rgx = re.search(CHASSIS_REGEX, line, re.I) if chassis_rgx: lldp_neighbor = { 'remote_chassis_id': napalm_base.helpers.mac(chassis_rgx.groups()[1]) } continue lldp_neighbor['parent_interface'] = '' port_rgx = re.search(PORT_REGEX, line, re.I) if port_rgx: lldp_neighbor['parent_interface'] = py23_compat.text_type( port_rgx.groups()[1]) continue local_port_rgx = re.search(LOCAL_PORT_ID_REGEX, line, re.I) if local_port_rgx: interface_name = local_port_rgx.groups()[1] continue port_descr_rgx = re.search(PORT_DESCR_REGEX, line, re.I) if port_descr_rgx: lldp_neighbor['remote_port'] = py23_compat.text_type( port_descr_rgx.groups()[1]) lldp_neighbor[ 'remote_port_description'] = py23_compat.text_type( port_descr_rgx.groups()[1]) continue syst_name_rgx = re.search(SYSTEM_NAME_REGEX, line, re.I) if syst_name_rgx: lldp_neighbor['remote_system_name'] = py23_compat.text_type( syst_name_rgx.groups()[1]) continue syst_descr_rgx = re.search(SYSTEM_DESCR_REGEX, line, re.I) if syst_descr_rgx: lldp_neighbor[ 'remote_system_description'] = py23_compat.text_type( syst_descr_rgx.groups()[1]) continue syst_capab_rgx = re.search(SYST_CAPAB_REEGX, line, re.I) if syst_capab_rgx: lldp_neighbor['remote_system_capab'] = py23_compat.text_type( syst_capab_rgx.groups()[1]) continue syst_enabled_rgx = re.search(ENABL_CAPAB_REGEX, line, re.I) if syst_enabled_rgx: lldp_neighbor[ 'remote_system_enable_capab'] = py23_compat.text_type( syst_enabled_rgx.groups()[1]) continue vlan_rgx = re.search(VLAN_ID_REGEX, line, re.I) if vlan_rgx: # at the end of the loop if interface_name not in lldp_neighbors.keys(): lldp_neighbors[interface_name] = [] lldp_neighbors[interface_name].append(lldp_neighbor) return lldp_neighbors def cli(self, commands): cli_output = {} if type(commands) is not list: raise TypeError('Please enter a valid list of commands!') for command in commands: command_output = self.device.show(command, raw_text=True) cli_output[py23_compat.text_type(command)] = command_output return cli_output def get_arp_table(self): arp_table = [] command = 'show ip arp' arp_table_vrf = self._get_command_table(command, 'TABLE_vrf', 'ROW_vrf') arp_table_raw = self._get_table_rows(arp_table_vrf[0], 'TABLE_adj', 'ROW_adj') for arp_table_entry in arp_table_raw: raw_ip = arp_table_entry.get('ip-addr-out') raw_mac = arp_table_entry.get('mac') age = arp_table_entry.get('time-stamp') if age == '-': age_sec = float(-1) else: age_time = ''.join(age.split(':')) age_sec = float(3600 * int(age_time[:2]) + 60 * int(age_time[2:4]) + int(age_time[4:])) interface = py23_compat.text_type(arp_table_entry.get('intf-out')) arp_table.append({ 'interface': interface, 'mac': napalm_base.helpers.convert(napalm_base.helpers.mac, raw_mac, raw_mac), 'ip': napalm_base.helpers.ip(raw_ip), 'age': age_sec }) return arp_table def _get_ntp_entity(self, peer_type): ntp_entities = {} command = 'show ntp peers' ntp_peers_table = self._get_command_table(command, 'TABLE_peers', 'ROW_peers') for ntp_peer in ntp_peers_table: if ntp_peer.get('serv_peer', '').strip() != peer_type: continue peer_addr = napalm_base.helpers.ip( ntp_peer.get('PeerIPAddress').strip()) ntp_entities[peer_addr] = {} return ntp_entities def get_ntp_peers(self): return self._get_ntp_entity('Peer') def get_ntp_servers(self): return self._get_ntp_entity('Server') def get_ntp_stats(self): ntp_stats = [] command = 'show ntp peer-status' ntp_stats_table = self._get_command_table(command, 'TABLE_peersstatus', 'ROW_peersstatus') for ntp_peer in ntp_stats_table: peer_address = napalm_base.helpers.ip( ntp_peer.get('remote').strip()) syncmode = ntp_peer.get('syncmode') stratum = int(ntp_peer.get('st')) hostpoll = int(ntp_peer.get('poll')) reachability = int(ntp_peer.get('reach')) delay = float(ntp_peer.get('delay')) ntp_stats.append({ 'remote': peer_address, 'synchronized': (syncmode == '*'), 'referenceid': peer_address, 'stratum': stratum, 'type': '', 'when': '', 'hostpoll': hostpoll, 'reachability': reachability, 'delay': delay, 'offset': 0.0, 'jitter': 0.0 }) return ntp_stats def get_interfaces_ip(self): interfaces_ip = {} ipv4_command = 'show ip interface' ipv4_interf_table_vrf = self._get_command_table( ipv4_command, 'TABLE_intf', 'ROW_intf') for interface in ipv4_interf_table_vrf: interface_name = py23_compat.text_type( interface.get('intf-name', '')) address = napalm_base.helpers.ip(interface.get('prefix')) prefix = int(interface.get('masklen', '')) if interface_name not in interfaces_ip.keys(): interfaces_ip[interface_name] = {} if 'ipv4' not in interfaces_ip[interface_name].keys(): interfaces_ip[interface_name]['ipv4'] = {} if address not in interfaces_ip[interface_name].get('ipv4'): interfaces_ip[interface_name]['ipv4'][address] = {} interfaces_ip[interface_name]['ipv4'][address].update( {'prefix_length': prefix}) secondary_addresses = interface.get('TABLE_secondary_address', {})\ .get('ROW_secondary_address', []) if type(secondary_addresses) is dict: secondary_addresses = [secondary_addresses] for secondary_address in secondary_addresses: secondary_address_ip = napalm_base.helpers.ip( secondary_address.get('prefix1')) secondary_address_prefix = int( secondary_address.get('masklen1', '')) if 'ipv4' not in interfaces_ip[interface_name].keys(): interfaces_ip[interface_name]['ipv4'] = {} if secondary_address_ip not in interfaces_ip[ interface_name].get('ipv4'): interfaces_ip[interface_name]['ipv4'][ secondary_address_ip] = {} interfaces_ip[interface_name]['ipv4'][ secondary_address_ip].update( {'prefix_length': secondary_address_prefix}) ipv6_command = 'show ipv6 interface' ipv6_interf_table_vrf = self._get_command_table( ipv6_command, 'TABLE_intf', 'ROW_intf') for interface in ipv6_interf_table_vrf: interface_name = py23_compat.text_type( interface.get('intf-name', '')) address = napalm_base.helpers.ip( interface.get('addr', '').split('/')[0]) prefix = interface.get('prefix', '').split('/')[-1] if prefix: prefix = int(interface.get('prefix', '').split('/')[-1]) else: prefix = 128 if interface_name not in interfaces_ip.keys(): interfaces_ip[interface_name] = {} if 'ipv6' not in interfaces_ip[interface_name].keys(): interfaces_ip[interface_name]['ipv6'] = {} if address not in interfaces_ip[interface_name].get('ipv6'): interfaces_ip[interface_name]['ipv6'][address] = {} interfaces_ip[interface_name]['ipv6'][address].update( {'prefix_length': prefix}) secondary_addresses = interface.get('TABLE_sec_addr', {}).get('ROW_sec_addr', []) if type(secondary_addresses) is dict: secondary_addresses = [secondary_addresses] for secondary_address in secondary_addresses: sec_prefix = secondary_address.get('sec-prefix', '').split('/') secondary_address_ip = napalm_base.helpers.ip(sec_prefix[0]) secondary_address_prefix = int(sec_prefix[-1]) if 'ipv6' not in interfaces_ip[interface_name].keys(): interfaces_ip[interface_name]['ipv6'] = {} if secondary_address_ip not in interfaces_ip[ interface_name].get('ipv6'): interfaces_ip[interface_name]['ipv6'][ secondary_address_ip] = {} interfaces_ip[interface_name]['ipv6'][ secondary_address_ip].update( {'prefix_length': secondary_address_prefix}) return interfaces_ip def get_mac_address_table(self): mac_table = [] command = 'show mac address-table' mac_table_raw = self._get_command_table(command, 'TABLE_mac_address', 'ROW_mac_address') for mac_entry in mac_table_raw: raw_mac = mac_entry.get('disp_mac_addr') interface = py23_compat.text_type(mac_entry.get('disp_port')) vlan = int(mac_entry.get('disp_vlan')) active = True static = (mac_entry.get('disp_is_static') != '0') moves = 0 last_move = 0.0 mac_table.append({ 'mac': napalm_base.helpers.mac(raw_mac), 'interface': interface, 'vlan': vlan, 'active': active, 'static': static, 'moves': moves, 'last_move': last_move }) return mac_table def get_snmp_information(self): snmp_information = {} snmp_command = 'show running-config' snmp_raw_output = self.cli([snmp_command]).get(snmp_command, '') snmp_config = napalm_base.helpers.textfsm_extractor( self, 'snmp_config', snmp_raw_output) if not snmp_config: return snmp_information snmp_information = { 'contact': py23_compat.text_type(''), 'location': py23_compat.text_type(''), 'community': {}, 'chassis_id': py23_compat.text_type('') } for snmp_entry in snmp_config: contact = py23_compat.text_type(snmp_entry.get('contact', '')) if contact: snmp_information['contact'] = contact location = py23_compat.text_type(snmp_entry.get('location', '')) if location: snmp_information['location'] = location community_name = py23_compat.text_type( snmp_entry.get('community', '')) if not community_name: continue if community_name not in snmp_information['community'].keys(): snmp_information['community'][community_name] = { 'acl': py23_compat.text_type(snmp_entry.get('acl', '')), 'mode': py23_compat.text_type(snmp_entry.get('mode', '').lower()) } else: acl = py23_compat.text_type(snmp_entry.get('acl', '')) if acl: snmp_information['community'][community_name]['acl'] = acl mode = py23_compat.text_type( snmp_entry.get('mode', '').lower()) if mode: snmp_information['community'][community_name][ 'mode'] = mode return snmp_information def get_users(self): _CISCO_TO_CISCO_MAP = {'network-admin': 15, 'network-operator': 5} _DEFAULT_USER_DICT = {'password': '', 'level': 0, 'sshkeys': []} users = {} command = 'show running-config' section_username_raw_output = self.cli([command]).get(command, '') section_username_tabled_output = napalm_base.helpers.textfsm_extractor( self, 'users', section_username_raw_output) for user in section_username_tabled_output: username = user.get('username', '') if not username: continue if username not in users: users[username] = _DEFAULT_USER_DICT.copy() password = user.get('password', '') if password: users[username]['password'] = py23_compat.text_type( password.strip()) level = 0 role = user.get('role', '') if role.startswith('priv'): level = int(role.split('-')[-1]) else: level = _CISCO_TO_CISCO_MAP.get(role, 0) if level > users.get(username).get('level'): # unfortunately on Cisco you can set different priv levels for the same user # Good news though: the device will consider the highest level users[username]['level'] = level sshkeytype = user.get('sshkeytype', '') sshkeyvalue = user.get('sshkeyvalue', '') if sshkeytype and sshkeyvalue: if sshkeytype not in ['ssh-rsa', 'ssh-dsa']: continue users[username]['sshkeys'].append( py23_compat.text_type(sshkeyvalue)) return users def traceroute(self, destination, source=c.TRACEROUTE_SOURCE, ttl=c.TRACEROUTE_TTL, timeout=c.TRACEROUTE_TIMEOUT, vrf=c.TRACEROUTE_VRF): _HOP_ENTRY_PROBE = [ '\s+', '(', # beginning of host_name (ip_address) RTT group '(', # beginning of host_name (ip_address) group only '([a-zA-Z0-9\.:-]*)', # hostname '\s+', '\(?([a-fA-F0-9\.:][^\)]*)\)?' # IP Address between brackets ')?', # end of host_name (ip_address) group only # also hostname/ip are optional -- they can or cannot be specified # if not specified, means the current probe followed the same path as the previous '\s+', '(\d+\.\d+)\s+ms', # RTT '|\*', # OR *, when non responsive hop ')' # end of host_name (ip_address) RTT group ] _HOP_ENTRY = [ '\s?', # space before hop index? '(\d+)', # hop index ] traceroute_result = {} timeout = 5 # seconds probes = 3 # 3 probes/jop and this cannot be changed on NXOS! version = '' try: version = '6' if IPAddress(destination).version == 6 else '' except AddrFormatError: return { 'error': 'Destination doest not look like a valid IP Address: {}'. format(destination) } source_opt = '' if source: source_opt = 'source {source}'.format(source=source) command = 'traceroute{version} {destination} {source_opt}'.format( version=version, destination=destination, source_opt=source_opt) try: traceroute_raw_output = self.cli([command]).get(command) except CommandErrorException: return { 'error': 'Cannot execute traceroute on the device: {}'.format(command) } hop_regex = ''.join(_HOP_ENTRY + _HOP_ENTRY_PROBE * probes) traceroute_result['success'] = {} if traceroute_raw_output: for line in traceroute_raw_output.splitlines(): hop_search = re.search(hop_regex, line) if not hop_search: continue hop_details = hop_search.groups() hop_index = int(hop_details[0]) previous_probe_host_name = '*' previous_probe_ip_address = '*' traceroute_result['success'][hop_index] = {'probes': {}} for probe_index in range(probes): host_name = hop_details[3 + probe_index * 5] ip_address_raw = hop_details[4 + probe_index * 5] ip_address = napalm_base.helpers.convert( napalm_base.helpers.ip, ip_address_raw, ip_address_raw) rtt = hop_details[5 + probe_index * 5] if rtt: rtt = float(rtt) else: rtt = timeout * 1000.0 if not host_name: host_name = previous_probe_host_name if not ip_address: ip_address = previous_probe_ip_address if hop_details[1 + probe_index * 5] == '*': host_name = '*' ip_address = '*' traceroute_result['success'][hop_index]['probes'][ probe_index + 1] = { 'host_name': py23_compat.text_type(host_name), 'ip_address': py23_compat.text_type(ip_address), 'rtt': rtt } previous_probe_host_name = host_name previous_probe_ip_address = ip_address return traceroute_result def get_config(self, retrieve='all'): config = { 'startup': '', 'running': '', 'candidate': '' } # default values if retrieve.lower() in ('running', 'all'): _cmd = 'show running-config' config['running'] = py23_compat.text_type( self.cli([_cmd]).get(_cmd)) if retrieve.lower() in ('startup', 'all'): _cmd = 'show startup-config' config['startup'] = py23_compat.text_type( self.cli([_cmd]).get(_cmd)) return config
def main(): switch_ip = "31.220.71.66" username = '******' password = getpass() eth_inf = "Ethernet 2/2" int_ip = "10.1.4.6/30" peer_ip = "10.1.4.5" AS = "10" configure_int = [ "interface " + eth_inf, "ip address " + int_ip, ] configure_bgp = [ "license grace-period", "feature bgp", "router bgp " + AS, "neighbor {} remote-as {}".format(peer_ip, AS), "address-family ipv4 unicast", ] nxs = Device(host=switch_ip, username=username, password=password, transport="https", port=8443) print "current config of Ethernet interface :" cmd = "show run int {}".format(eth_inf) print nxs.show(cmd, raw_text=True) print "\nConfiguring Ethernet Interface:" nxs.config_list(configure_int) print "new config of Ethernet interface :" cmd = "show run int {}".format(eth_inf) print nxs.show(cmd, raw_text=True) print "current BGP config :" cmd = 'show run bgp' print nxs.show(cmd, raw_text=True) print "\nConfiguring BGP" nxs.config_list(configure_bgp) print "new BGP config :" cmd = 'show run bgp' print nxs.show(cmd, raw_text=True) print "\nVerifying BGP peers" cmd = 'show bgp session' print nxs.show(cmd, raw_text=True) print "\nSaving config..." print nxs.save()
#!/usr/bin/env python import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning from pprint import pprint from getpass import getpass from pynxos.device import Device requests.packages.urllib3.disable_warnings(InsecureRequestWarning) #nexus_ip = "34.195.147.241" nexus_ip = "nxos1.twb-tech.com" nxs_test = Device(host=nexus_ip, username="******", password=getpass(), transport='https', port=8443) #### FACTS print "\nFacts" print '-' * 50 pprint(nxs_test.facts) raw_input("\n\nHit a key to continue: ") ###### SHOW print "\nShow hostname" print '-' * 50 print nxs_test.show("show hostname") print '-' * 50 raw_input("\n\nHit a key to continue: ") ###### SHOW RAW TEXT
#!/usr/bin/env python from __future__ import unicode_literals, print_function from pynxos.device import Device from getpass import getpass from pprint import pprint as pp import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) nxos1 = Device(host='nxos1.twb-tech.com', username='******', password=getpass(), transport='https', port='8443') nxos2 = Device(host='nxos2.twb-tech.com', username='******', password=getpass(), transport='https', port='8443') command = "show ip route vrf management" output = nxos1.show(command) routes = output['TABLE_vrf']['ROW_vrf']['TABLE_addrf']['ROW_addrf'][ 'TABLE_prefix']['ROW_prefix'] for route in routes: if route['ipprefix'] == '0.0.0.0/0': print(route['TABLE_path']['ROW_path']['ipnexthop'])
from pynxos.device import Device from getpass import getpass import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) password = '******' nxos1 = { 'host': 'nxos1.twb-tech.com', 'username': '******', 'password': password, 'transport': 'https', 'port': 8443, } nxos2 = { 'host': 'nxos2.twb-tech.com', 'username': '******', 'password': password, 'transport': 'https', 'port': 8443, } print() for device in (nxos1, nxos2): nxapi_conn = Device(**device) print('-' * 40) print(nxapi_conn.show('show hostname')) print('-' * 40) print()
from __future__ import print_function, unicode_literals from pynxos.device import Device from getpass import getpass from pprint import pprint import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) device = Device(host='nxos2.twb-tech.com', username='******', password=getpass(), transport='https', port=8443) print(device.show('show hostname')) # Show command command = 'show version' output = device.show(command) pprint(output) # Show comand with raw_text i.e. unstructured data command = 'show version' output = device.show(command, raw_text=True) print(output) # Config command commands = [ 'logging history size 300',
class NXOSDevice(BaseDevice): def __init__(self, host, username, password, transport='http', timeout=30, port=None, **kwargs): super(NXOSDevice, self).__init__(host, username, password, vendor='cisco', device_type='cisco_nxos_nxapi') self.transport = transport self.timeout = timeout self.native = NXOSNative(host, username, password, transport=transport, timeout=timeout, port=port) def _image_booted(self, image_name, **vendor_specifics): version_data = self.show("show version", raw_text=True) if re.search(image_name, version_data): return True return False def _wait_for_device_reboot(self, timeout=3600): start = time.time() while time.time() - start < timeout: try: self.show("show hostname") return except: pass raise RebootTimeoutError(hostname=self.facts["hostname"], wait_time=timeout) def backup_running_config(self, filename): self.native.backup_running_config(filename) def checkpoint(self, filename): return self.native.checkpoint(filename) def close(self): pass def config(self, command): try: self.native.config(command) except CLIError as e: raise CommandError(command, str(e)) def config_list(self, commands): try: self.native.config_list(commands) except CLIError as e: raise CommandListError(commands, e.command, str(e)) @property def facts(self): if hasattr(self, '_facts'): return self._facts facts = strip_unicode(self.native.facts) facts['vendor'] = self.vendor self._facts = facts return self._facts def file_copy(self, src, dest=None, file_system='bootflash:'): if not self.file_copy_remote_exists(src, dest, file_system): dest = dest or os.path.basename(src) try: file_copy = self.native.file_copy(src, dest, file_system=file_system) if not self.file_copy_remote_exists(src, dest, file_system): raise FileTransferError( message="Attempted file copy, " "but could not validate file existed after transfer") return file_copy except NXOSFileTransferError as e: print(str(e)) raise FileTransferError # TODO: Make this an internal method since exposing file_copy should be sufficient def file_copy_remote_exists(self, src, dest=None, file_system='bootflash:'): dest = dest or os.path.basename(src) return self.native.file_copy_remote_exists(src, dest, file_system=file_system) def get_boot_options(self): return self.native.get_boot_options() def open(self): pass def reboot(self, confirm=False, timer=0): if timer != 0: raise RebootTimerError(self.device_type) self.native.reboot(confirm=confirm) def rollback(self, filename): try: self.native.rollback(filename) except CLIError: raise RollbackError('Rollback unsuccessful, %s may not exist.' % filename) @property def running_config(self): return self.native.running_config def save(self, filename='startup-config'): return self.native.save(filename=filename) def set_boot_options(self, image_name, kickstart=None, **vendor_specifics): file_system = vendor_specifics.get("file_system") if file_system is None: file_system = "bootflash:" file_system_files = self.show("dir {0}".format(file_system), raw_text=True) if re.search(image_name, file_system_files) is None: raise NTCFileNotFoundError(hostname=self.facts.get("hostname"), file=image_name, dir=file_system) if kickstart is not None: if re.search(kickstart, file_system_files) is None: raise NTCFileNotFoundError(hostname=self.facts.get("hostname"), file=image_name, dir=file_system) kickstart = file_system + kickstart image_name = file_system + image_name return self.native.set_boot_options(image_name, kickstart=kickstart) def set_timeout(self, timeout): self.native.timeout = timeout def show(self, command, raw_text=False): try: return strip_unicode(self.native.show(command, raw_text=raw_text)) except CLIError as e: raise CommandError(command, str(e)) def show_list(self, commands, raw_text=False): try: return strip_unicode( self.native.show_list(commands, raw_text=raw_text)) except CLIError as e: raise CommandListError(commands, e.command, str(e)) @property def startup_config(self): return self.show('show startup-config', raw_text=True)
class TestDevice(unittest.TestCase): @mock.patch("pynxos.device.RPCClient") def setUp(self, mock_rpc): self.device = Device("host", "user", "pass") self.rpc = mock_rpc self.send_request = mock_rpc.return_value.send_request self.send_request.side_effect = send_request def test_init(self): self.assertEqual(self.device.host, "host") self.assertEqual(self.device.username, "user") self.assertEqual(self.device.password, "pass") self.assertEqual(self.device.transport, "http") self.assertEqual(self.device.timeout, 30) self.rpc.assert_called_with("host", "user", "pass", transport="http", port=None) def test_show(self): result = self.device.show("sh clock") expected = {"simple_time": "18:06:31.021 UTC Tue Mar 22 2016\n"} self.assertEqual(result, expected) self.send_request.assert_called_with(["sh clock"], method=u"cli", timeout=30) def test_show_empty_response(self): self.send_request.return_value = [] self.send_request.side_effect = None result = self.device.show("sh clock") expected = {} self.assertEqual(result, expected) self.send_request.assert_called_with(["sh clock"], method=u"cli", timeout=30) def test_show_raw_text(self): result = self.device.show("sh clock", raw_text=True) expected = "18:29:19.583 UTC Tue Mar 22 2016\n" self.assertEqual(result, expected) self.send_request.assert_called_with(["sh clock"], method=u"cli_ascii", timeout=30) def test_show_list(self): result = self.device.show_list(["sh clock", "sh hostname"]) expected = [{"simple_time": "18:52:45.084 UTC Tue Mar 22 2016\n"}, {"hostname": "N9K2.ntc.com"}] self.assertEqual(result, expected) self.send_request.assert_called_with(["sh clock", "sh hostname"], method=u"cli", timeout=30) def test_show_list_raw_text(self): result = self.device.show_list(["sh clock", "sh hostname"], raw_text=True) expected = ["18:55:38.720 UTC Tue Mar 22 2016\n", "N9K2.ntc.com \n"] self.assertEqual(result, expected) self.send_request.assert_called_with(["sh clock", "sh hostname"], method=u"cli_ascii", timeout=30) def test_config(self): result = self.device.config("int ethernet 1/1") expected = None self.assertEqual(result, expected) self.send_request.assert_called_with(["int ethernet 1/1"], method=u"cli", timeout=30) def test_config_list(self): result = self.device.config_list(["int ethernet 1/1", "no shutdown"]) expected = [None, None] self.assertEqual(result, expected) self.send_request.assert_called_with(["int ethernet 1/1", "no shutdown"], method=u"cli", timeout=30) def test_save(self): result = self.device.save() expected = True self.assertEqual(result, expected) self.send_request.assert_called_with([u"copy run startup-config"], method=u"cli_ascii", timeout=30) def test_save_error(self): result = self.device.save(filename="abc") expected = False self.assertEqual(result, expected) self.send_request.assert_called_with([u"copy run abc"], method=u"cli_ascii", timeout=30) @mock.patch("pynxos.device.FileCopy") def test_file_copy_remote_exists(self, mock_fc): mock_fc.return_value.remote_file_exists.return_value = True result = self.device.file_copy_remote_exists("source", dest="dest") self.assertEqual(result, True) mock_fc.assert_called_with(self.device, "source", dst="dest", file_system="bootflash:") @mock.patch("pynxos.device.FileCopy") def test_file_copy_remote_doesnt_exist(self, mock_fc): mock_fc.return_value.remote_file_exists.return_value = False result = self.device.file_copy_remote_exists("source", dest="dest") self.assertEqual(result, False) mock_fc.assert_called_with(self.device, "source", dst="dest", file_system="bootflash:") @mock.patch("pynxos.device.FileCopy") def test_file_copy(self, mock_fc): result = self.device.file_copy("source", dest="dest") mock_fc.assert_called_with(self.device, "source", dst="dest", file_system="bootflash:") mock_fc.return_value.send.assert_called_with() @mock.patch.object(Device, "show") def test_reboot(self, mock_show): self.device.reboot(confirm=True) mock_show.assert_any_call("terminal dont-ask") mock_show.assert_any_call("reload") @mock.patch.object(Device, "show") def test_set_boot_options(self, mock_show): self.device.set_boot_options("boot.sys") mock_show.assert_called_with("install all nxos boot.sys", raw_text=True) @mock.patch.object(Device, "show") def test_set_boot_options_kickstart(self, mock_show): self.device.set_boot_options("boot.sys", kickstart="boot.kick") mock_show.assert_called_with("install all system boot.sys kickstart boot.kick", raw_text=True) def test_get_boot_options(self): result = self.device.get_boot_options() expected = { "sys": "nxos.7.0.3.I2.1.bin", "status": 'This is the log of last installation.\nVerifying image bootflash:/nxos.7.0.3.I2.1.bin for boot variable "nxos".\n -- SUCCESS\nVerifying image type.\n -- SUCCESS\nPreparing "nxos" version info using image bootflash:/nxos.7.0.3.I2.1.bin.\n -- SUCCESS\nPreparing "bios" version info using image bootflash:/nxos.7.0.3.I2.1.bin.\n -- SUCCESS\nPerforming module support checks.\n -- SUCCESS\nNotifying services about system upgrade.\n -- SUCCESS\nCompatibility check is done:\nModule bootable Impact Install-type Reason\n------ -------- -------------- ------------ ------\n 1 yes disruptive reset Reset due to single supervisor\nImages will be upgraded according to following table:\nModule Image Running-Version(pri:alt) New-Version Upg-Required\n------ ---------- ---------------------------------------- -------------------- ------------\n 1 nxos 6.1(2)I3(1) 7.0(3)I2(1) yes\n 1 bios v07.15(06/29/2014):v07.06(03/02/2014) v07.34(08/11/2015) yes\nSwitch will be reloaded for disruptive upgrade.\nInstall is in progress, please wait.\nPerforming runtime checks.\n -- SUCCESS\nSetting boot variables.\n -- SUCCESS\nPerforming configuration copy.\n -- SUCCESS\nModule 1: Refreshing compact flash and upgrading bios/loader/bootrom.\nWarning: please do not remove or power off the module at this time.\n -- SUCCESS\nFinishing the upgrade, switch will reboot in 10 seconds.\n', } self.assertEqual(result, expected) self.send_request.assert_called_with(["show install all status"], method=u"cli_ascii", timeout=30) def test_get_boot_options_kickstart(self): def special_send_request(commands, method="cli", timeout=30.0): if commands == ["show boot"]: return json.load(open(os.path.join(CURRNENT_DIR, "mocks", "send_request_raw", "show_boot_kick.json"))) elif commands == ["show install all status"]: return json.load( open(os.path.join(CURRNENT_DIR, "mocks", "send_request_raw", "show_install_all_status_kick.json")) ) self.send_request.side_effect = special_send_request result = self.device.get_boot_options() expected = { "sys": "n5000-uk9.7.2.1.N1.1.bin", "status": "This is the log of last installation.\nContinuing with installation process, please wait.\nThe login will be disabled until the installation is completed.\nPerforming supervisor state verification. \nSUCCESS\nSupervisor non-disruptive upgrade successful.\nInstall has been successful.\n", "kick": "n5000-uk9-kickstart.7.2.1.N1.1.bin", } self.assertEqual(result, expected) self.send_request.assert_called_with(["show install all status"], method=u"cli_ascii", timeout=30) @mock.patch.object(Device, "show") def test_rollback(self, mock_show): self.device.rollback("rb_file") mock_show.assert_called_with("rollback running-config file rb_file", raw_text=True) @mock.patch.object(Device, "show_list") def test_checkpoint(self, mock_show_list): self.device.checkpoint("cp_file") mock_show_list.assert_called_with(["terminal dont-ask", "checkpoint file cp_file"], raw_text=True) def test_running_config(self): result = self.device.running_config expected = '!Command: show running-config\n!Time: Tue Mar 22 21:23:11 2016\nversion 7.0(3)I2(1)\nhostname N9K2\nvdc N9K2 id 1\n limit-resource vlan minimum 16 maximum 4094\n limit-resource vrf minimum 2 maximum 4096\n limit-resource port-channel minimum 0 maximum 511\n limit-resource u4route-mem minimum 248 maximum 248\n limit-resource u6route-mem minimum 96 maximum 96\n limit-resource m4route-mem minimum 58 maximum 58\n limit-resource m6route-mem minimum 8 maximum 8\nfeature telnet\nfeature nxapi\nfeature bash-shell\nfeature scp-server\nfeature vrrp\nfeature tacacs+\ncfs eth distribute\nfeature pim\nfeature udld\nfeature interface-vlan\nfeature hsrp\nfeature lacp\nfeature dhcp\nfeature vpc\nfeature lldp\nfeature vtp\nonep\n session key-required enabled\nno password strength-check\nusername admin password 5 $1$6Anve29g$aKsAE8iRKAQzY7sW1qKZh0 role network-admin\nusername cisco password 5 $1$nGd5VWnS$LJ/a9ztNEt6xruMCG2Erl/ role network-admin\nusername jay password 5 $1$K6cIEEfy$vkYaWr5tEdgr55C86b74u/ role network-operator\nusername ntc password 5 $1$0WWXa9uW$EnQSp3nRPD.nIZTqAE//11 role network-admin\nusername netauto password 5 $1$ITxT/Gi0$QbHUtgzTCFt39i4FYSuzl1 role network-admin\nnxapi http port 80\nnxapi https port 443\nbanner motd *\nDISCONNECT FROM DEVICE IMMEDIATELY.\nIF YOU CONTINUE, YOU WILL BE PROSECUTED TO THE FULLEST\nEXTENT OF THE LAW!!!!\n*\nssh login-attempts 10\nip domain-lookup\nip domain-name ntc.com\nip name-server 208.67.222.222\nip host puppet 176.126.88.189\ntacacs-server timeout 10\ntacacs-server deadtime 30\ntacacs-server host 5.6.7.8 \ntacacs-server host 1.2.3.4 key 7 "\\"hello\\"" \nradius-server host 1.2.3.4 authentication accounting \nobject-group ip address OBJECTGROUP-IP\n 10 1.1.1.1/24 \n 20 2.2.2.2/24 \nip access-list INBOUND_MGMT\n statistics per-entry\n 20 permit tcp 63.118.185.0/24 10.1.100.21/32 eq 22 \n 30 permit icmp any 10.1.100.21/32 \n 40 permit tcp any 10.1.100.21/32 eq 443 \n 50 permit tcp any 10.1.100.21/32 eq www \n 60 permit ip 10.1.100.0/24 10.1.100.21/32 \n 80 permit tcp 89.101.133.0/24 10.1.100.21/32 eq 22 \n 90 permit udp any 10.1.100.21/32 eq snmp \n 100 permit udp any 10.1.100.20/32 eq snmp \n 110 permit tcp 79.52.99.64/32 10.1.100.21/32 eq 22 \n 120 permit tcp 176.126.88.189/32 10.1.100.21/32 eq 22 \nip access-list MYACL\n 10 permit tcp 1.1.1.1/32 eq www any established log \n 20 deny udp 2.1.1.1/20 neq 80 5.5.5.0/24 eq 443 \n 40 remark COMMENT REMARK BY ANSIBLE\n 100 permit ip 10.1.1.1/32 100.1.1.1/24 log \nip access-list ONE\n 15 deny eigrp 1.1.1.1/32 2.2.2.2/32 \n 30 permit tcp any gt smtp any lt 33 urg ack psh rst syn fin established dscp cs7 log \n 40 permit eigrp any any precedence flash fragments time-range RANGE log \n 50 permit udp any range 10 20 any dscp af11 \n 55 permit eigrp any any precedence flash fragments time-range RANGE log \n 65 permit tcp any any precedence routine \n 70 permit tcp any any precedence routine \nip access-list POLICY\n 10 permit 23 any any \nip access-list TWO\n 2 remark this is a test string\n 4 permit eigrp any any \n 10 permit tcp 1.1.1.1/32 eq www any established log \n 20 permit tcp 1.1.1.1/32 any \ntime-range RANGE\ntime-range TEIMER\nvtp domain ntc\nsnmp-server user jay network-operator auth md5 0xe3b9e394dff8a08e8dbfef2c3f9a6564 priv 0xe3b9e394dff8a08e8dbfef2c3f9a6564 localizedkey\nsnmp-server user ntc network-admin auth md5 0x779969ac744909382f0c4bf39275a2c3 priv 0x779969ac744909382f0c4bf39275a2c3 localizedkey\nsnmp-server user netauto network-admin auth md5 0xd85b615bbd22469d476b571844afe9e6 priv 0xd85b615bbd22469d476b571844afe9e6 localizedkey\nrmon event 1 log trap public description FATAL(1) owner PMON@FATAL\nrmon event 2 log trap public description CRITICAL(2) owner PMON@CRITICAL\nrmon event 3 log trap public description ERROR(3) owner PMON@ERROR\nrmon event 4 log trap public description WARNING(4) owner PMON@WARNING\nrmon event 5 log trap public description INFORMATION(5) owner PMON@INFO\nno snmp-server enable traps entity entity_mib_change\nno snmp-server enable traps entity entity_module_status_change\nno snmp-server enable traps entity entity_power_status_change\nno snmp-server enable traps entity entity_module_inserted\nno snmp-server enable traps entity entity_module_removed\nno snmp-server enable traps entity entity_unrecognised_module\nno snmp-server enable traps entity entity_fan_status_change\nno snmp-server enable traps entity entity_power_out_change\nno snmp-server enable traps link linkDown\nno snmp-server enable traps link linkUp\nno snmp-server enable traps link extended-linkDown\nno snmp-server enable traps link extended-linkUp\nno snmp-server enable traps link cieLinkDown\nno snmp-server enable traps link cieLinkUp\nno snmp-server enable traps link delayed-link-state-change\nno snmp-server enable traps rf redundancy_framework\nno snmp-server enable traps license notify-license-expiry\nno snmp-server enable traps license notify-no-license-for-feature\nno snmp-server enable traps license notify-licensefile-missing\nno snmp-server enable traps license notify-license-expiry-warning\nno snmp-server enable traps upgrade UpgradeOpNotifyOnCompletion\nno snmp-server enable traps upgrade UpgradeJobStatusNotify\nno snmp-server enable traps rmon risingAlarm\nno snmp-server enable traps rmon fallingAlarm\nno snmp-server enable traps rmon hcRisingAlarm\nno snmp-server enable traps rmon hcFallingAlarm\nno snmp-server enable traps entity entity_sensor\nno snmp-server enable traps entity cefcMIBEnableStatusNotification\nsnmp-server community networktocode group network-operator\nntp server 33.33.33.33 prefer key 32\nntp server 192.0.2.10 use-vrf ntc\nntp peer 2001:db8::4101\nntp authentication-key 42 md5 qpg 7\nntp trusted-key 42\nntp logging\nntp master 8\naaa authentication login console none \nip route 1.1.1.0/24 2.2.2.2 tag 90 80\nip route 1.1.1.1/32 10.1.10.2\nip route 1.1.1.1/32 10.10.10.1\nip route 1.1.1.1/32 10.10.20.1\nip pim ssm range 232.0.0.0/8\nno ip igmp snooping\nvlan 1-20,30,33,40,100-105,333,400-401\nvlan 2\n name native\nvlan 10\n name test_segment\nvlan 20\n name peer_keepalive\nvlan 30\n name Puppet\nvlan 33\n name PuppetAnsible\nvlan 333\n name webvlan\nvlan 400\n name db_vlan\nvlan 401\n name dba_vlan\nservice dhcp\nip dhcp relay\nipv6 dhcp relay\nvrf context TESTING\nvrf context TestVRF\n shutdown\nvrf context keepalive\nvrf context management\n ip domain-name ntc.com\n ip name-server 208.67.222.222\n ip route 0.0.0.0/0 10.1.100.1\nvrf context test\ninterface Vlan1\n mtu 1600\ninterface Vlan10\n no shutdown\n mtu 1600\n vrf member ntc\n hsrp version 2\ninterface Vlan20\n no shutdown\n mtu 1600\n vrf member keepalive\n ip address 10.1.20.3/24\ninterface Vlan100\n mtu 1600\n no ip redirects\n ip address 20.20.20.2/24\n ip address 100.100.100.2/24 secondary\ninterface Vlan233\n mtu 1600\ninterface port-channel11\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\ninterface port-channel12\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n spanning-tree port type network\ninterface port-channel100\n mtu 9216\n lacp min-links 2\ninterface Ethernet1/1\ninterface Ethernet1/2\ninterface Ethernet1/3\ninterface Ethernet1/4\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\ninterface Ethernet1/5\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\ninterface Ethernet1/6\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 11 mode active\ninterface Ethernet1/7\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 11 mode active\ninterface Ethernet1/8\ninterface Ethernet1/9\ninterface Ethernet1/10\ninterface Ethernet1/11\ninterface Ethernet1/12\ninterface Ethernet1/13\ninterface Ethernet1/14\ninterface Ethernet1/15\ninterface Ethernet1/16\ninterface Ethernet1/17\ninterface Ethernet1/18\ninterface Ethernet1/19\ninterface Ethernet1/20\ninterface Ethernet1/21\ninterface Ethernet1/22\ninterface Ethernet1/23\ninterface Ethernet1/24\ninterface Ethernet1/25\ninterface Ethernet1/26\ninterface Ethernet1/27\ninterface Ethernet1/28\n mtu 9216\n channel-group 100 mode active\ninterface Ethernet1/29\n mtu 9216\n channel-group 100 mode active\ninterface Ethernet1/30\n ip access-group ONE in\ninterface Ethernet1/31\n ip access-group POLICY out\n no switchport\n mtu 1700\ninterface Ethernet1/32\n no switchport\n mtu 1600\n ip pim sparse-mode\n ip igmp version 2\n ip igmp startup-query-interval 31\n ip igmp startup-query-count 2\n ip igmp static-oif route-map ANOTHER_TEST\n no shutdown\ninterface Ethernet1/33\n ip access-group ONE in\n no switchport\n mtu 1600\n ip pim sparse-mode\n ip igmp static-oif 236.0.0.0\n ip igmp static-oif 237.0.0.0\n ip igmp static-oif 238.0.0.0\n ip igmp static-oif 239.0.0.0 source 1.1.1.1\n no shutdown\ninterface Ethernet1/34\ninterface Ethernet1/35\ninterface Ethernet1/36\ninterface Ethernet1/37\ninterface Ethernet1/38\ninterface Ethernet1/39\ninterface Ethernet1/40\ninterface Ethernet1/41\ninterface Ethernet1/42\ninterface Ethernet1/43\ninterface Ethernet1/44\ninterface Ethernet1/45\ninterface Ethernet1/46\ninterface Ethernet1/47\ninterface Ethernet1/48\ninterface Ethernet2/1\n no switchport\n vrf member ntc\n ip address 10.1.100.13/24\n no shutdown\ninterface Ethernet2/2\n no switchport\n mtu 1600\n ip address 10.10.60.1/24\n no shutdown\ninterface Ethernet2/3\n no switchport\n mtu 1600\n ip address 10.10.70.1/24\n no shutdown\ninterface Ethernet2/4\n no switchport\n mtu 1600\n ip address 10.10.80.1/24\n no shutdown\ninterface Ethernet2/5\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 12 mode active\ninterface Ethernet2/6\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 12 mode active\ninterface Ethernet2/7\ninterface Ethernet2/8\ninterface Ethernet2/9\ninterface Ethernet2/10\ninterface Ethernet2/11\n shutdown\ninterface Ethernet2/12\n shutdown\ninterface mgmt0\n description out of band mgmt interface\n ip access-group INBOUND_MGMT in\n vrf member management\n ip address 10.1.100.21/24\ninterface loopback10\n vrf member ntc\ninterface loopback11\n vrf member ntc\n ip address 11.11.11.11/24\ninterface loopback13\n ip address 13.13.13.13/24\ninterface loopback15\n vrf member test\ninterface loopback16\ncli alias name puppetoper show puppet agent oper\ncli alias name puppetshow show puppet agent last-exec-log\ncli alias name puppetdump show puppet config\ncli alias name puppetfacter show puppet facter\ncli alias name puppetrun execute puppet agent-oneshot\ncli alias name puppetconfig show run | sec puppet\ncli alias name puppetexecute execute puppet agent-oneshot\nline console\nline vty\n session-limit 16\n exec-timeout 0\nboot nxos bootflash:/nxos.7.0.3.I2.1.bin \n' self.assertEqual(result, expected) def test_backup_running_config(self): temp_file = NamedTemporaryFile() self.device.backup_running_config(temp_file.name) expected = '!Command: show running-config\n!Time: Tue Mar 22 21:23:11 2016\nversion 7.0(3)I2(1)\nhostname N9K2\nvdc N9K2 id 1\n limit-resource vlan minimum 16 maximum 4094\n limit-resource vrf minimum 2 maximum 4096\n limit-resource port-channel minimum 0 maximum 511\n limit-resource u4route-mem minimum 248 maximum 248\n limit-resource u6route-mem minimum 96 maximum 96\n limit-resource m4route-mem minimum 58 maximum 58\n limit-resource m6route-mem minimum 8 maximum 8\nfeature telnet\nfeature nxapi\nfeature bash-shell\nfeature scp-server\nfeature vrrp\nfeature tacacs+\ncfs eth distribute\nfeature pim\nfeature udld\nfeature interface-vlan\nfeature hsrp\nfeature lacp\nfeature dhcp\nfeature vpc\nfeature lldp\nfeature vtp\nonep\n session key-required enabled\nno password strength-check\nusername admin password 5 $1$6Anve29g$aKsAE8iRKAQzY7sW1qKZh0 role network-admin\nusername cisco password 5 $1$nGd5VWnS$LJ/a9ztNEt6xruMCG2Erl/ role network-admin\nusername jay password 5 $1$K6cIEEfy$vkYaWr5tEdgr55C86b74u/ role network-operator\nusername ntc password 5 $1$0WWXa9uW$EnQSp3nRPD.nIZTqAE//11 role network-admin\nusername netauto password 5 $1$ITxT/Gi0$QbHUtgzTCFt39i4FYSuzl1 role network-admin\nnxapi http port 80\nnxapi https port 443\nbanner motd *\nDISCONNECT FROM DEVICE IMMEDIATELY.\nIF YOU CONTINUE, YOU WILL BE PROSECUTED TO THE FULLEST\nEXTENT OF THE LAW!!!!\n*\nssh login-attempts 10\nip domain-lookup\nip domain-name ntc.com\nip name-server 208.67.222.222\nip host puppet 176.126.88.189\ntacacs-server timeout 10\ntacacs-server deadtime 30\ntacacs-server host 5.6.7.8 \ntacacs-server host 1.2.3.4 key 7 "\\"hello\\"" \nradius-server host 1.2.3.4 authentication accounting \nobject-group ip address OBJECTGROUP-IP\n 10 1.1.1.1/24 \n 20 2.2.2.2/24 \nip access-list INBOUND_MGMT\n statistics per-entry\n 20 permit tcp 63.118.185.0/24 10.1.100.21/32 eq 22 \n 30 permit icmp any 10.1.100.21/32 \n 40 permit tcp any 10.1.100.21/32 eq 443 \n 50 permit tcp any 10.1.100.21/32 eq www \n 60 permit ip 10.1.100.0/24 10.1.100.21/32 \n 80 permit tcp 89.101.133.0/24 10.1.100.21/32 eq 22 \n 90 permit udp any 10.1.100.21/32 eq snmp \n 100 permit udp any 10.1.100.20/32 eq snmp \n 110 permit tcp 79.52.99.64/32 10.1.100.21/32 eq 22 \n 120 permit tcp 176.126.88.189/32 10.1.100.21/32 eq 22 \nip access-list MYACL\n 10 permit tcp 1.1.1.1/32 eq www any established log \n 20 deny udp 2.1.1.1/20 neq 80 5.5.5.0/24 eq 443 \n 40 remark COMMENT REMARK BY ANSIBLE\n 100 permit ip 10.1.1.1/32 100.1.1.1/24 log \nip access-list ONE\n 15 deny eigrp 1.1.1.1/32 2.2.2.2/32 \n 30 permit tcp any gt smtp any lt 33 urg ack psh rst syn fin established dscp cs7 log \n 40 permit eigrp any any precedence flash fragments time-range RANGE log \n 50 permit udp any range 10 20 any dscp af11 \n 55 permit eigrp any any precedence flash fragments time-range RANGE log \n 65 permit tcp any any precedence routine \n 70 permit tcp any any precedence routine \nip access-list POLICY\n 10 permit 23 any any \nip access-list TWO\n 2 remark this is a test string\n 4 permit eigrp any any \n 10 permit tcp 1.1.1.1/32 eq www any established log \n 20 permit tcp 1.1.1.1/32 any \ntime-range RANGE\ntime-range TEIMER\nvtp domain ntc\nsnmp-server user jay network-operator auth md5 0xe3b9e394dff8a08e8dbfef2c3f9a6564 priv 0xe3b9e394dff8a08e8dbfef2c3f9a6564 localizedkey\nsnmp-server user ntc network-admin auth md5 0x779969ac744909382f0c4bf39275a2c3 priv 0x779969ac744909382f0c4bf39275a2c3 localizedkey\nsnmp-server user netauto network-admin auth md5 0xd85b615bbd22469d476b571844afe9e6 priv 0xd85b615bbd22469d476b571844afe9e6 localizedkey\nrmon event 1 log trap public description FATAL(1) owner PMON@FATAL\nrmon event 2 log trap public description CRITICAL(2) owner PMON@CRITICAL\nrmon event 3 log trap public description ERROR(3) owner PMON@ERROR\nrmon event 4 log trap public description WARNING(4) owner PMON@WARNING\nrmon event 5 log trap public description INFORMATION(5) owner PMON@INFO\nno snmp-server enable traps entity entity_mib_change\nno snmp-server enable traps entity entity_module_status_change\nno snmp-server enable traps entity entity_power_status_change\nno snmp-server enable traps entity entity_module_inserted\nno snmp-server enable traps entity entity_module_removed\nno snmp-server enable traps entity entity_unrecognised_module\nno snmp-server enable traps entity entity_fan_status_change\nno snmp-server enable traps entity entity_power_out_change\nno snmp-server enable traps link linkDown\nno snmp-server enable traps link linkUp\nno snmp-server enable traps link extended-linkDown\nno snmp-server enable traps link extended-linkUp\nno snmp-server enable traps link cieLinkDown\nno snmp-server enable traps link cieLinkUp\nno snmp-server enable traps link delayed-link-state-change\nno snmp-server enable traps rf redundancy_framework\nno snmp-server enable traps license notify-license-expiry\nno snmp-server enable traps license notify-no-license-for-feature\nno snmp-server enable traps license notify-licensefile-missing\nno snmp-server enable traps license notify-license-expiry-warning\nno snmp-server enable traps upgrade UpgradeOpNotifyOnCompletion\nno snmp-server enable traps upgrade UpgradeJobStatusNotify\nno snmp-server enable traps rmon risingAlarm\nno snmp-server enable traps rmon fallingAlarm\nno snmp-server enable traps rmon hcRisingAlarm\nno snmp-server enable traps rmon hcFallingAlarm\nno snmp-server enable traps entity entity_sensor\nno snmp-server enable traps entity cefcMIBEnableStatusNotification\nsnmp-server community networktocode group network-operator\nntp server 33.33.33.33 prefer key 32\nntp server 192.0.2.10 use-vrf ntc\nntp peer 2001:db8::4101\nntp authentication-key 42 md5 qpg 7\nntp trusted-key 42\nntp logging\nntp master 8\naaa authentication login console none \nip route 1.1.1.0/24 2.2.2.2 tag 90 80\nip route 1.1.1.1/32 10.1.10.2\nip route 1.1.1.1/32 10.10.10.1\nip route 1.1.1.1/32 10.10.20.1\nip pim ssm range 232.0.0.0/8\nno ip igmp snooping\nvlan 1-20,30,33,40,100-105,333,400-401\nvlan 2\n name native\nvlan 10\n name test_segment\nvlan 20\n name peer_keepalive\nvlan 30\n name Puppet\nvlan 33\n name PuppetAnsible\nvlan 333\n name webvlan\nvlan 400\n name db_vlan\nvlan 401\n name dba_vlan\nservice dhcp\nip dhcp relay\nipv6 dhcp relay\nvrf context TESTING\nvrf context TestVRF\n shutdown\nvrf context keepalive\nvrf context management\n ip domain-name ntc.com\n ip name-server 208.67.222.222\n ip route 0.0.0.0/0 10.1.100.1\nvrf context test\ninterface Vlan1\n mtu 1600\ninterface Vlan10\n no shutdown\n mtu 1600\n vrf member ntc\n hsrp version 2\ninterface Vlan20\n no shutdown\n mtu 1600\n vrf member keepalive\n ip address 10.1.20.3/24\ninterface Vlan100\n mtu 1600\n no ip redirects\n ip address 20.20.20.2/24\n ip address 100.100.100.2/24 secondary\ninterface Vlan233\n mtu 1600\ninterface port-channel11\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\ninterface port-channel12\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n spanning-tree port type network\ninterface port-channel100\n mtu 9216\n lacp min-links 2\ninterface Ethernet1/1\ninterface Ethernet1/2\ninterface Ethernet1/3\ninterface Ethernet1/4\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\ninterface Ethernet1/5\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\ninterface Ethernet1/6\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 11 mode active\ninterface Ethernet1/7\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 11 mode active\ninterface Ethernet1/8\ninterface Ethernet1/9\ninterface Ethernet1/10\ninterface Ethernet1/11\ninterface Ethernet1/12\ninterface Ethernet1/13\ninterface Ethernet1/14\ninterface Ethernet1/15\ninterface Ethernet1/16\ninterface Ethernet1/17\ninterface Ethernet1/18\ninterface Ethernet1/19\ninterface Ethernet1/20\ninterface Ethernet1/21\ninterface Ethernet1/22\ninterface Ethernet1/23\ninterface Ethernet1/24\ninterface Ethernet1/25\ninterface Ethernet1/26\ninterface Ethernet1/27\ninterface Ethernet1/28\n mtu 9216\n channel-group 100 mode active\ninterface Ethernet1/29\n mtu 9216\n channel-group 100 mode active\ninterface Ethernet1/30\n ip access-group ONE in\ninterface Ethernet1/31\n ip access-group POLICY out\n no switchport\n mtu 1700\ninterface Ethernet1/32\n no switchport\n mtu 1600\n ip pim sparse-mode\n ip igmp version 2\n ip igmp startup-query-interval 31\n ip igmp startup-query-count 2\n ip igmp static-oif route-map ANOTHER_TEST\n no shutdown\ninterface Ethernet1/33\n ip access-group ONE in\n no switchport\n mtu 1600\n ip pim sparse-mode\n ip igmp static-oif 236.0.0.0\n ip igmp static-oif 237.0.0.0\n ip igmp static-oif 238.0.0.0\n ip igmp static-oif 239.0.0.0 source 1.1.1.1\n no shutdown\ninterface Ethernet1/34\ninterface Ethernet1/35\ninterface Ethernet1/36\ninterface Ethernet1/37\ninterface Ethernet1/38\ninterface Ethernet1/39\ninterface Ethernet1/40\ninterface Ethernet1/41\ninterface Ethernet1/42\ninterface Ethernet1/43\ninterface Ethernet1/44\ninterface Ethernet1/45\ninterface Ethernet1/46\ninterface Ethernet1/47\ninterface Ethernet1/48\ninterface Ethernet2/1\n no switchport\n vrf member ntc\n ip address 10.1.100.13/24\n no shutdown\ninterface Ethernet2/2\n no switchport\n mtu 1600\n ip address 10.10.60.1/24\n no shutdown\ninterface Ethernet2/3\n no switchport\n mtu 1600\n ip address 10.10.70.1/24\n no shutdown\ninterface Ethernet2/4\n no switchport\n mtu 1600\n ip address 10.10.80.1/24\n no shutdown\ninterface Ethernet2/5\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 12 mode active\ninterface Ethernet2/6\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 12 mode active\ninterface Ethernet2/7\ninterface Ethernet2/8\ninterface Ethernet2/9\ninterface Ethernet2/10\ninterface Ethernet2/11\n shutdown\ninterface Ethernet2/12\n shutdown\ninterface mgmt0\n description out of band mgmt interface\n ip access-group INBOUND_MGMT in\n vrf member management\n ip address 10.1.100.21/24\ninterface loopback10\n vrf member ntc\ninterface loopback11\n vrf member ntc\n ip address 11.11.11.11/24\ninterface loopback13\n ip address 13.13.13.13/24\ninterface loopback15\n vrf member test\ninterface loopback16\ncli alias name puppetoper show puppet agent oper\ncli alias name puppetshow show puppet agent last-exec-log\ncli alias name puppetdump show puppet config\ncli alias name puppetfacter show puppet facter\ncli alias name puppetrun execute puppet agent-oneshot\ncli alias name puppetconfig show run | sec puppet\ncli alias name puppetexecute execute puppet agent-oneshot\nline console\nline vty\n session-limit 16\n exec-timeout 0\nboot nxos bootflash:/nxos.7.0.3.I2.1.bin \n' contents = temp_file.read() self.assertEqual(contents, expected) def test_facts(self): self.assertEqual(hasattr(self.device, "_facts"), False) expected = { "uptime_string": "07:05:47:10", "uptime": 625630, "vlans": [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "30", "33", "40", "100", "101", "102", "103", "104", "105", "333", "400", "401", ], u"os_version": "7.0(3)I2(1)", u"serial_number": "SAL1819S6BE", u"model": "Nexus9000 C9396PX Chassis", u"hostname": "N9K2", "interfaces": [ "mgmt0", "Ethernet1/1", "Ethernet1/2", "Ethernet1/3", "Ethernet1/4", "Ethernet1/5", "Ethernet1/6", "Ethernet1/7", "Ethernet1/8", "Ethernet1/9", "Ethernet1/10", "Ethernet1/11", "Ethernet1/12", "Ethernet1/13", "Ethernet1/14", "Ethernet1/15", "Ethernet1/16", "Ethernet1/17", "Ethernet1/18", "Ethernet1/19", "Ethernet1/20", "Ethernet1/21", "Ethernet1/22", "Ethernet1/23", "Ethernet1/24", "Ethernet1/25", "Ethernet1/26", "Ethernet1/27", "Ethernet1/28", "Ethernet1/29", "Ethernet1/30", "Ethernet1/31", "Ethernet1/32", "Ethernet1/33", "Ethernet1/34", "Ethernet1/35", "Ethernet1/36", "Ethernet1/37", "Ethernet1/38", "Ethernet1/39", "Ethernet1/40", "Ethernet1/41", "Ethernet1/42", "Ethernet1/43", "Ethernet1/44", "Ethernet1/45", "Ethernet1/46", "Ethernet1/47", "Ethernet1/48", "Ethernet2/1", "Ethernet2/2", "Ethernet2/3", "Ethernet2/4", "Ethernet2/5", "Ethernet2/6", "Ethernet2/7", "Ethernet2/8", "Ethernet2/9", "Ethernet2/10", "Ethernet2/11", "Ethernet2/12", "port-channel11", "port-channel12", "port-channel100", "loopback10", "loopback11", "loopback13", "loopback15", "loopback16", "Vlan1", "Vlan10", "Vlan20", "Vlan100", "Vlan233", ], "fqdn": "N/A", } self.assertEqual(self.device.facts, expected) self.assertEqual(hasattr(self.device, "_facts"), True) # caching test self.assertEqual(self.device.facts, expected) # caching test
def check_nxapi_errors(results, verbose=False): """Return True if all commands are None""" for entry in results: if entry is not None: if verbose: print entry return False return True nexus_ip = "nxos3.twb-tech.com" nxs = Device(host=nexus_ip, username="******", password="******", transport='https', port=8443) #nxs_facts = nxs.facts #pprint (nxs_facts) def main(): """Configure BGP using NX-API.""" # Nexus switches # switch_ip = raw_input("Enter switch IP: ") # username = '******' # password = getpass() eth_intf = "Ethernet 2/2" intf_ip = '10.1.4.5/30' peer_ip = '10.1.4.6' as_number = '10'
#!/usr/bin/env python from pprint import pprint from getpass import getpass from pynxos.device import Device nexus_ip = "nxos1.twb-tech.com" nxs_test = Device(host=nexus_ip, username="******", password=getpass(), transport='https', port=8443) my_facts = nxs_test.facts pprint(nxs_test.facts)
class NXOSDevice(BaseDevice): """Cisco NXOS Device Implementation.""" vendor = "cisco" def __init__(self, host, username, password, transport="http", timeout=30, port=None, **kwargs): super().__init__(host, username, password, device_type="cisco_nxos_nxapi") self.transport = transport self.timeout = timeout self.native = NXOSNative(host, username, password, transport=transport, timeout=timeout, port=port) def _image_booted(self, image_name, **vendor_specifics): version_data = self.show("show version", raw_text=True) if re.search(image_name, version_data): return True return False def _wait_for_device_reboot(self, timeout=600): start = time.time() while time.time() - start < timeout: try: self.refresh_facts() if self.uptime < 180: return except: # noqa E722 # nosec pass raise RebootTimeoutError(hostname=self.hostname, wait_time=timeout) def backup_running_config(self, filename): self.native.backup_running_config(filename) @property def boot_options(self): return self.native.get_boot_options() def checkpoint(self, filename): return self.native.checkpoint(filename) def close(self): pass def config(self, command): try: self.native.config(command) except CLIError as e: raise CommandError(command, str(e)) def config_list(self, commands): try: self.native.config_list(commands) except CLIError as e: raise CommandListError(commands, e.command, str(e)) @property def uptime(self): if self._uptime is None: self._uptime = self.native.facts.get("uptime") return self._uptime @property def hostname(self): if self._hostname is None: self._hostname = self.native.facts.get("hostname") return self._hostname @property def interfaces(self): if self._interfaces is None: self._interfaces = self.native.facts.get("interfaces") return self._interfaces @property def vlans(self): if self._vlans is None: self._vlans = self.native.facts.get("vlans") return self._vlans @property def fqdn(self): if self._fqdn is None: self._fqdn = self.native.facts.get("fqdn") return self._fqdn @property def model(self): if self._model is None: self._model = self.native.facts.get("model") return self._model @property def os_version(self): if self._os_version is None: self._os_version = self.native.facts.get("os_version") return self._os_version @property def serial_number(self): if self._serial_number is None: self._serial_number = self.native.facts.get("serial_number") return self._serial_number def file_copy(self, src, dest=None, file_system="bootflash:"): if not self.file_copy_remote_exists(src, dest, file_system): dest = dest or os.path.basename(src) try: file_copy = self.native.file_copy(src, dest, file_system=file_system) if not self.file_copy_remote_exists(src, dest, file_system): raise FileTransferError( message= "Attempted file copy, but could not validate file existed after transfer" ) return file_copy except NXOSFileTransferError as e: print(str(e)) raise FileTransferError # TODO: Make this an internal method since exposing file_copy should be sufficient def file_copy_remote_exists(self, src, dest=None, file_system="bootflash:"): dest = dest or os.path.basename(src) return self.native.file_copy_remote_exists(src, dest, file_system=file_system) def install_os(self, image_name, **vendor_specifics): timeout = vendor_specifics.get("timeout", 3600) if not self._image_booted(image_name): self.set_boot_options(image_name, **vendor_specifics) self._wait_for_device_reboot(timeout=timeout) if not self._image_booted(image_name): raise OSInstallError(hostname=self.facts.get("hostname"), desired_boot=image_name) self.save() return True return False def open(self): pass def reboot(self, timer=0, **kwargs): """ Reload the controller or controller pair. Args: timer (int, optional): The time to wait before reloading. Defaults to 0. Raises: RebootTimerError: When the device is still unreachable after the timeout period. Example: >>> device = NXOSDevice(**connection_args) >>> device.reboot() >> """ if kwargs.get("confirm"): warnings.warn("Passing 'confirm' to reboot method is deprecated.", DeprecationWarning) if timer != 0: raise RebootTimerError(self.device_type) self.native.reboot(confirm=True) def rollback(self, filename): try: self.native.rollback(filename) except CLIError: raise RollbackError("Rollback unsuccessful, %s may not exist." % filename) @property def running_config(self): return self.native.running_config def save(self, filename="startup-config"): return self.native.save(filename=filename) def set_boot_options(self, image_name, kickstart=None, **vendor_specifics): file_system = vendor_specifics.get("file_system") if file_system is None: file_system = "bootflash:" file_system_files = self.show("dir {0}".format(file_system), raw_text=True) if re.search(image_name, file_system_files) is None: raise NTCFileNotFoundError(hostname=self.hostname, file=image_name, dir=file_system) if kickstart is not None: if re.search(kickstart, file_system_files) is None: raise NTCFileNotFoundError(hostname=self.hostname, file=kickstart, dir=file_system) kickstart = file_system + kickstart image_name = file_system + image_name self.native.timeout = 300 upgrade_result = self.native.set_boot_options(image_name, kickstart=kickstart) self.native.timeout = 30 return upgrade_result def set_timeout(self, timeout): self.native.timeout = timeout def show(self, command, raw_text=False): try: return self.native.show(command, raw_text=raw_text) except CLIError as e: raise CommandError(command, str(e)) def show_list(self, commands, raw_text=False): try: return self.native.show_list(commands, raw_text=raw_text) except CLIError as e: raise CommandListError(commands, e.command, str(e)) @property def startup_config(self): return self.show("show startup-config", raw_text=True)
import socket # Library that will allow for socket timeouts to be coupled with try: except: import sys # Library of variables that have strong interaction with the interpreter import requests # Main Python library for HTTP and HTTPS interactions # import pynxos import time banner = ('-' * 80) # Create a banner for use as a section separator # Turn off SSL warnings import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) nxos1 = Device(host='nxos1.twb-tech.com', username='******', password='******', transport='https', port=8443) nxos2 = Device(host='nxos1.twb-tech.com', username='******', password='******', transport='https', port=8443) raw_route_table = nxos1.show('show ip route vrf management') pprint(raw_route_table) print() print(banner) print()
#!/usr/bin/env python from pprint import pprint from getpass import getpass from pynxos.device import Device import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) nexus_ip = "nxos4.twb-tech.com" nxs_test = Device(host=nexus_ip, username="******", password= '******', transport='https', port=8443) print 'hello' my_facts = nxs_test.facts pprint(nxs_test.facts) print "show version text:" output = nxs_test.show("show version") print output print 'proc_board_id ' + output['proc_board_id']
class NXOSDevice(BaseDevice): def __init__(self, host, username, password, transport='http', timeout=30, port=None, **kwargs): super(NXOSDevice, self).__init__(host, username, password, vendor='cisco', device_type=NXOS_API_DEVICE_TYPE) self.transport = transport self.timeout = timeout self.native = NXOSNative(host, username, password, transport=transport, timeout=timeout, port=port) def open(self): pass def close(self): pass def set_timeout(self, timeout): self.native.timeout = timeout def config(self, command): try: self.native.config(command) except CLIError as e: raise CommandError(command, str(e)) def config_list(self, commands): try: self.native.config_list(commands) except CLIError as e: raise CommandListError(commands, e.command, str(e)) def show(self, command, raw_text=False): try: return strip_unicode(self.native.show(command, raw_text=raw_text)) except CLIError as e: raise CommandError(command, str(e)) def show_list(self, commands, raw_text=False): try: return strip_unicode( self.native.show_list(commands, raw_text=raw_text)) except CLIError as e: raise CommandListError(commands, e.command, str(e)) def save(self, filename='startup-config'): return self.native.save(filename=filename) def file_copy_remote_exists(self, src, dest=None, file_system='bootflash:'): dest = dest or os.path.basename(src) return self.native.file_copy_remote_exists(src, dest, file_system=file_system) def file_copy(self, src, dest=None, file_system='bootflash:'): dest = dest or os.path.basename(src) try: return self.native.file_copy(src, dest, file_system=file_system) except NXOSFileTransferError as e: print str(e) raise FileTransferError def reboot(self, confirm=False, timer=0): if timer != 0: raise RebootTimerError(self.device_type) self.native.reboot(confirm=confirm) def get_boot_options(self): return self.native.get_boot_options() def set_boot_options(self, image_name, kickstart=None, **vendor_specifics): return self.native.set_boot_options(image_name, kickstart=kickstart) def checkpoint(self, filename): return self.native.checkpoint(filename) def rollback(self, filename): try: self.native.rollback(filename) except CLIError: raise RollbackError('Rollback unsuccessful, %s may not exist.' % filename) def backup_running_config(self, filename): self.native.backup_running_config(filename) @property def facts(self): if hasattr(self, '_facts'): return self._facts facts = strip_unicode(self.native.facts) facts['vendor'] = self.vendor self._facts = facts return self._facts @property def running_config(self): return self.native.running_config @property def startup_config(self): return self.show('show startup-config', raw_text=True)
class TestDevice(unittest.TestCase): @mock.patch('pynxos.device.RPCClient') def setUp(self, mock_rpc): self.device = Device('host', 'user', 'pass') self.rpc = mock_rpc self.send_request = mock_rpc.return_value.send_request self.send_request.side_effect = send_request def test_init(self): self.assertEqual(self.device.host, 'host') self.assertEqual(self.device.username, 'user') self.assertEqual(self.device.password, 'pass') self.assertEqual(self.device.transport, 'http') self.assertEqual(self.device.timeout, 30) self.rpc.assert_called_with('host', 'user', 'pass', transport='http', port=None) def test_show(self): result = self.device.show('sh clock') expected = {'simple_time': '18:06:31.021 UTC Tue Mar 22 2016\n'} self.assertEqual(result, expected) self.send_request.assert_called_with(['sh clock'], method=u'cli', timeout=30) def test_show_empty_response(self): self.send_request.return_value = [] self.send_request.side_effect = None result = self.device.show('sh clock') expected = {} self.assertEqual(result, expected) self.send_request.assert_called_with(['sh clock'], method=u'cli', timeout=30) def test_show_raw_text(self): result = self.device.show('sh clock', raw_text=True) expected = '18:29:19.583 UTC Tue Mar 22 2016\n' self.assertEqual(result, expected) self.send_request.assert_called_with(['sh clock'], method=u'cli_ascii', timeout=30) def test_show_list(self): result = self.device.show_list(['sh clock', 'sh hostname']) expected = [{ 'simple_time': '18:52:45.084 UTC Tue Mar 22 2016\n' }, { 'hostname': 'N9K2.ntc.com' }] self.assertEqual(result, expected) self.send_request.assert_called_with(['sh clock', 'sh hostname'], method=u'cli', timeout=30) def test_show_list_raw_text(self): result = self.device.show_list(['sh clock', 'sh hostname'], raw_text=True) expected = ['18:55:38.720 UTC Tue Mar 22 2016\n', 'N9K2.ntc.com \n'] self.assertEqual(result, expected) self.send_request.assert_called_with(['sh clock', 'sh hostname'], method=u'cli_ascii', timeout=30) def test_config(self): result = self.device.config('int ethernet 1/1') expected = None self.assertEqual(result, expected) self.send_request.assert_called_with(['int ethernet 1/1'], method=u'cli', timeout=30) def test_config_list(self): result = self.device.config_list(['int ethernet 1/1', 'no shutdown']) expected = [None, None] self.assertEqual(result, expected) self.send_request.assert_called_with( ['int ethernet 1/1', 'no shutdown'], method=u'cli', timeout=30) def test_save(self): result = self.device.save() expected = True self.assertEqual(result, expected) self.send_request.assert_called_with([u'copy run startup-config'], method=u'cli_ascii', timeout=30) def test_save_error(self): result = self.device.save(filename='abc') expected = False self.assertEqual(result, expected) self.send_request.assert_called_with([u'copy run abc'], method=u'cli_ascii', timeout=30) @mock.patch('pynxos.device.FileCopy') def test_file_copy_remote_exists(self, mock_fc): mock_fc.return_value.remote_file_exists.return_value = True result = self.device.file_copy_remote_exists('source', dest='dest') self.assertEqual(result, True) mock_fc.assert_called_with(self.device, 'source', dst='dest', file_system='bootflash:') @mock.patch('pynxos.device.FileCopy') def test_file_copy_remote_doesnt_exist(self, mock_fc): mock_fc.return_value.remote_file_exists.return_value = False result = self.device.file_copy_remote_exists('source', dest='dest') self.assertEqual(result, False) mock_fc.assert_called_with(self.device, 'source', dst='dest', file_system='bootflash:') @mock.patch('pynxos.device.FileCopy') def test_file_copy(self, mock_fc): result = self.device.file_copy('source', dest='dest') mock_fc.assert_called_with(self.device, 'source', dst='dest', file_system='bootflash:') mock_fc.return_value.send.assert_called_with() @mock.patch.object(Device, 'show') def test_reboot(self, mock_show): self.device.reboot(confirm=True) mock_show.assert_any_call('terminal dont-ask') mock_show.assert_any_call('reload') @mock.patch.object(Device, 'show') def test_set_boot_options(self, mock_show): self.device.set_boot_options('boot.sys') mock_show.assert_called_with('install all nxos boot.sys', raw_text=True) @mock.patch.object(Device, 'show') def test_set_boot_options_kickstart(self, mock_show): self.device.set_boot_options('boot.sys', kickstart='boot.kick') mock_show.assert_called_with( 'install all system boot.sys kickstart boot.kick', raw_text=True) def test_get_boot_options(self): result = self.device.get_boot_options() expected = { 'sys': 'nxos.7.0.3.I2.1.bin', 'status': 'This is the log of last installation.\nVerifying image bootflash:/nxos.7.0.3.I2.1.bin for boot variable "nxos".\n -- SUCCESS\nVerifying image type.\n -- SUCCESS\nPreparing "nxos" version info using image bootflash:/nxos.7.0.3.I2.1.bin.\n -- SUCCESS\nPreparing "bios" version info using image bootflash:/nxos.7.0.3.I2.1.bin.\n -- SUCCESS\nPerforming module support checks.\n -- SUCCESS\nNotifying services about system upgrade.\n -- SUCCESS\nCompatibility check is done:\nModule bootable Impact Install-type Reason\n------ -------- -------------- ------------ ------\n 1 yes disruptive reset Reset due to single supervisor\nImages will be upgraded according to following table:\nModule Image Running-Version(pri:alt) New-Version Upg-Required\n------ ---------- ---------------------------------------- -------------------- ------------\n 1 nxos 6.1(2)I3(1) 7.0(3)I2(1) yes\n 1 bios v07.15(06/29/2014):v07.06(03/02/2014) v07.34(08/11/2015) yes\nSwitch will be reloaded for disruptive upgrade.\nInstall is in progress, please wait.\nPerforming runtime checks.\n -- SUCCESS\nSetting boot variables.\n -- SUCCESS\nPerforming configuration copy.\n -- SUCCESS\nModule 1: Refreshing compact flash and upgrading bios/loader/bootrom.\nWarning: please do not remove or power off the module at this time.\n -- SUCCESS\nFinishing the upgrade, switch will reboot in 10 seconds.\n' } self.assertEqual(result, expected) self.send_request.assert_called_with(['show install all status'], method=u'cli_ascii', timeout=30) def test_get_boot_options_kickstart(self): def special_send_request(commands, method='cli', timeout=30.0): if commands == ['show boot']: return json.load( open( os.path.join(CURRNENT_DIR, 'mocks', 'send_request_raw', 'show_boot_kick.json'))) elif commands == ['show install all status']: return json.load( open( os.path.join(CURRNENT_DIR, 'mocks', 'send_request_raw', 'show_install_all_status_kick.json'))) self.send_request.side_effect = special_send_request result = self.device.get_boot_options() expected = { 'sys': 'n5000-uk9.7.2.1.N1.1.bin', 'status': 'This is the log of last installation.\nContinuing with installation process, please wait.\nThe login will be disabled until the installation is completed.\nPerforming supervisor state verification. \nSUCCESS\nSupervisor non-disruptive upgrade successful.\nInstall has been successful.\n', 'kick': 'n5000-uk9-kickstart.7.2.1.N1.1.bin' } self.assertEqual(result, expected) self.send_request.assert_called_with(['show install all status'], method=u'cli_ascii', timeout=30) @mock.patch.object(Device, 'show') def test_rollback(self, mock_show): self.device.rollback('rb_file') mock_show.assert_called_with('rollback running-config file rb_file', raw_text=True) @mock.patch.object(Device, 'show_list') def test_checkpoint(self, mock_show_list): self.device.checkpoint('cp_file') mock_show_list.assert_called_with( ['terminal dont-ask', 'checkpoint file cp_file'], raw_text=True) def test_running_config(self): result = self.device.running_config expected = '!Command: show running-config\n!Time: Tue Mar 22 21:23:11 2016\nversion 7.0(3)I2(1)\nhostname N9K2\nvdc N9K2 id 1\n limit-resource vlan minimum 16 maximum 4094\n limit-resource vrf minimum 2 maximum 4096\n limit-resource port-channel minimum 0 maximum 511\n limit-resource u4route-mem minimum 248 maximum 248\n limit-resource u6route-mem minimum 96 maximum 96\n limit-resource m4route-mem minimum 58 maximum 58\n limit-resource m6route-mem minimum 8 maximum 8\nfeature telnet\nfeature nxapi\nfeature bash-shell\nfeature scp-server\nfeature vrrp\nfeature tacacs+\ncfs eth distribute\nfeature pim\nfeature udld\nfeature interface-vlan\nfeature hsrp\nfeature lacp\nfeature dhcp\nfeature vpc\nfeature lldp\nfeature vtp\nonep\n session key-required enabled\nno password strength-check\nusername admin password 5 $1$6Anve29g$aKsAE8iRKAQzY7sW1qKZh0 role network-admin\nusername cisco password 5 $1$nGd5VWnS$LJ/a9ztNEt6xruMCG2Erl/ role network-admin\nusername jay password 5 $1$K6cIEEfy$vkYaWr5tEdgr55C86b74u/ role network-operator\nusername ntc password 5 $1$0WWXa9uW$EnQSp3nRPD.nIZTqAE//11 role network-admin\nusername netauto password 5 $1$ITxT/Gi0$QbHUtgzTCFt39i4FYSuzl1 role network-admin\nnxapi http port 80\nnxapi https port 443\nbanner motd *\nDISCONNECT FROM DEVICE IMMEDIATELY.\nIF YOU CONTINUE, YOU WILL BE PROSECUTED TO THE FULLEST\nEXTENT OF THE LAW!!!!\n*\nssh login-attempts 10\nip domain-lookup\nip domain-name ntc.com\nip name-server 208.67.222.222\nip host puppet 176.126.88.189\ntacacs-server timeout 10\ntacacs-server deadtime 30\ntacacs-server host 5.6.7.8 \ntacacs-server host 1.2.3.4 key 7 "\\"hello\\"" \nradius-server host 1.2.3.4 authentication accounting \nobject-group ip address OBJECTGROUP-IP\n 10 1.1.1.1/24 \n 20 2.2.2.2/24 \nip access-list INBOUND_MGMT\n statistics per-entry\n 20 permit tcp 63.118.185.0/24 10.1.100.21/32 eq 22 \n 30 permit icmp any 10.1.100.21/32 \n 40 permit tcp any 10.1.100.21/32 eq 443 \n 50 permit tcp any 10.1.100.21/32 eq www \n 60 permit ip 10.1.100.0/24 10.1.100.21/32 \n 80 permit tcp 89.101.133.0/24 10.1.100.21/32 eq 22 \n 90 permit udp any 10.1.100.21/32 eq snmp \n 100 permit udp any 10.1.100.20/32 eq snmp \n 110 permit tcp 79.52.99.64/32 10.1.100.21/32 eq 22 \n 120 permit tcp 176.126.88.189/32 10.1.100.21/32 eq 22 \nip access-list MYACL\n 10 permit tcp 1.1.1.1/32 eq www any established log \n 20 deny udp 2.1.1.1/20 neq 80 5.5.5.0/24 eq 443 \n 40 remark COMMENT REMARK BY ANSIBLE\n 100 permit ip 10.1.1.1/32 100.1.1.1/24 log \nip access-list ONE\n 15 deny eigrp 1.1.1.1/32 2.2.2.2/32 \n 30 permit tcp any gt smtp any lt 33 urg ack psh rst syn fin established dscp cs7 log \n 40 permit eigrp any any precedence flash fragments time-range RANGE log \n 50 permit udp any range 10 20 any dscp af11 \n 55 permit eigrp any any precedence flash fragments time-range RANGE log \n 65 permit tcp any any precedence routine \n 70 permit tcp any any precedence routine \nip access-list POLICY\n 10 permit 23 any any \nip access-list TWO\n 2 remark this is a test string\n 4 permit eigrp any any \n 10 permit tcp 1.1.1.1/32 eq www any established log \n 20 permit tcp 1.1.1.1/32 any \ntime-range RANGE\ntime-range TEIMER\nvtp domain ntc\nsnmp-server user jay network-operator auth md5 0xe3b9e394dff8a08e8dbfef2c3f9a6564 priv 0xe3b9e394dff8a08e8dbfef2c3f9a6564 localizedkey\nsnmp-server user ntc network-admin auth md5 0x779969ac744909382f0c4bf39275a2c3 priv 0x779969ac744909382f0c4bf39275a2c3 localizedkey\nsnmp-server user netauto network-admin auth md5 0xd85b615bbd22469d476b571844afe9e6 priv 0xd85b615bbd22469d476b571844afe9e6 localizedkey\nrmon event 1 log trap public description FATAL(1) owner PMON@FATAL\nrmon event 2 log trap public description CRITICAL(2) owner PMON@CRITICAL\nrmon event 3 log trap public description ERROR(3) owner PMON@ERROR\nrmon event 4 log trap public description WARNING(4) owner PMON@WARNING\nrmon event 5 log trap public description INFORMATION(5) owner PMON@INFO\nno snmp-server enable traps entity entity_mib_change\nno snmp-server enable traps entity entity_module_status_change\nno snmp-server enable traps entity entity_power_status_change\nno snmp-server enable traps entity entity_module_inserted\nno snmp-server enable traps entity entity_module_removed\nno snmp-server enable traps entity entity_unrecognised_module\nno snmp-server enable traps entity entity_fan_status_change\nno snmp-server enable traps entity entity_power_out_change\nno snmp-server enable traps link linkDown\nno snmp-server enable traps link linkUp\nno snmp-server enable traps link extended-linkDown\nno snmp-server enable traps link extended-linkUp\nno snmp-server enable traps link cieLinkDown\nno snmp-server enable traps link cieLinkUp\nno snmp-server enable traps link delayed-link-state-change\nno snmp-server enable traps rf redundancy_framework\nno snmp-server enable traps license notify-license-expiry\nno snmp-server enable traps license notify-no-license-for-feature\nno snmp-server enable traps license notify-licensefile-missing\nno snmp-server enable traps license notify-license-expiry-warning\nno snmp-server enable traps upgrade UpgradeOpNotifyOnCompletion\nno snmp-server enable traps upgrade UpgradeJobStatusNotify\nno snmp-server enable traps rmon risingAlarm\nno snmp-server enable traps rmon fallingAlarm\nno snmp-server enable traps rmon hcRisingAlarm\nno snmp-server enable traps rmon hcFallingAlarm\nno snmp-server enable traps entity entity_sensor\nno snmp-server enable traps entity cefcMIBEnableStatusNotification\nsnmp-server community networktocode group network-operator\nntp server 33.33.33.33 prefer key 32\nntp server 192.0.2.10 use-vrf ntc\nntp peer 2001:db8::4101\nntp authentication-key 42 md5 qpg 7\nntp trusted-key 42\nntp logging\nntp master 8\naaa authentication login console none \nip route 1.1.1.0/24 2.2.2.2 tag 90 80\nip route 1.1.1.1/32 10.1.10.2\nip route 1.1.1.1/32 10.10.10.1\nip route 1.1.1.1/32 10.10.20.1\nip pim ssm range 232.0.0.0/8\nno ip igmp snooping\nvlan 1-20,30,33,40,100-105,333,400-401\nvlan 2\n name native\nvlan 10\n name test_segment\nvlan 20\n name peer_keepalive\nvlan 30\n name Puppet\nvlan 33\n name PuppetAnsible\nvlan 333\n name webvlan\nvlan 400\n name db_vlan\nvlan 401\n name dba_vlan\nservice dhcp\nip dhcp relay\nipv6 dhcp relay\nvrf context TESTING\nvrf context TestVRF\n shutdown\nvrf context keepalive\nvrf context management\n ip domain-name ntc.com\n ip name-server 208.67.222.222\n ip route 0.0.0.0/0 10.1.100.1\nvrf context test\ninterface Vlan1\n mtu 1600\ninterface Vlan10\n no shutdown\n mtu 1600\n vrf member ntc\n hsrp version 2\ninterface Vlan20\n no shutdown\n mtu 1600\n vrf member keepalive\n ip address 10.1.20.3/24\ninterface Vlan100\n mtu 1600\n no ip redirects\n ip address 20.20.20.2/24\n ip address 100.100.100.2/24 secondary\ninterface Vlan233\n mtu 1600\ninterface port-channel11\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\ninterface port-channel12\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n spanning-tree port type network\ninterface port-channel100\n mtu 9216\n lacp min-links 2\ninterface Ethernet1/1\ninterface Ethernet1/2\ninterface Ethernet1/3\ninterface Ethernet1/4\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\ninterface Ethernet1/5\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\ninterface Ethernet1/6\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 11 mode active\ninterface Ethernet1/7\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 11 mode active\ninterface Ethernet1/8\ninterface Ethernet1/9\ninterface Ethernet1/10\ninterface Ethernet1/11\ninterface Ethernet1/12\ninterface Ethernet1/13\ninterface Ethernet1/14\ninterface Ethernet1/15\ninterface Ethernet1/16\ninterface Ethernet1/17\ninterface Ethernet1/18\ninterface Ethernet1/19\ninterface Ethernet1/20\ninterface Ethernet1/21\ninterface Ethernet1/22\ninterface Ethernet1/23\ninterface Ethernet1/24\ninterface Ethernet1/25\ninterface Ethernet1/26\ninterface Ethernet1/27\ninterface Ethernet1/28\n mtu 9216\n channel-group 100 mode active\ninterface Ethernet1/29\n mtu 9216\n channel-group 100 mode active\ninterface Ethernet1/30\n ip access-group ONE in\ninterface Ethernet1/31\n ip access-group POLICY out\n no switchport\n mtu 1700\ninterface Ethernet1/32\n no switchport\n mtu 1600\n ip pim sparse-mode\n ip igmp version 2\n ip igmp startup-query-interval 31\n ip igmp startup-query-count 2\n ip igmp static-oif route-map ANOTHER_TEST\n no shutdown\ninterface Ethernet1/33\n ip access-group ONE in\n no switchport\n mtu 1600\n ip pim sparse-mode\n ip igmp static-oif 236.0.0.0\n ip igmp static-oif 237.0.0.0\n ip igmp static-oif 238.0.0.0\n ip igmp static-oif 239.0.0.0 source 1.1.1.1\n no shutdown\ninterface Ethernet1/34\ninterface Ethernet1/35\ninterface Ethernet1/36\ninterface Ethernet1/37\ninterface Ethernet1/38\ninterface Ethernet1/39\ninterface Ethernet1/40\ninterface Ethernet1/41\ninterface Ethernet1/42\ninterface Ethernet1/43\ninterface Ethernet1/44\ninterface Ethernet1/45\ninterface Ethernet1/46\ninterface Ethernet1/47\ninterface Ethernet1/48\ninterface Ethernet2/1\n no switchport\n vrf member ntc\n ip address 10.1.100.13/24\n no shutdown\ninterface Ethernet2/2\n no switchport\n mtu 1600\n ip address 10.10.60.1/24\n no shutdown\ninterface Ethernet2/3\n no switchport\n mtu 1600\n ip address 10.10.70.1/24\n no shutdown\ninterface Ethernet2/4\n no switchport\n mtu 1600\n ip address 10.10.80.1/24\n no shutdown\ninterface Ethernet2/5\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 12 mode active\ninterface Ethernet2/6\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 12 mode active\ninterface Ethernet2/7\ninterface Ethernet2/8\ninterface Ethernet2/9\ninterface Ethernet2/10\ninterface Ethernet2/11\n shutdown\ninterface Ethernet2/12\n shutdown\ninterface mgmt0\n description out of band mgmt interface\n ip access-group INBOUND_MGMT in\n vrf member management\n ip address 10.1.100.21/24\ninterface loopback10\n vrf member ntc\ninterface loopback11\n vrf member ntc\n ip address 11.11.11.11/24\ninterface loopback13\n ip address 13.13.13.13/24\ninterface loopback15\n vrf member test\ninterface loopback16\ncli alias name puppetoper show puppet agent oper\ncli alias name puppetshow show puppet agent last-exec-log\ncli alias name puppetdump show puppet config\ncli alias name puppetfacter show puppet facter\ncli alias name puppetrun execute puppet agent-oneshot\ncli alias name puppetconfig show run | sec puppet\ncli alias name puppetexecute execute puppet agent-oneshot\nline console\nline vty\n session-limit 16\n exec-timeout 0\nboot nxos bootflash:/nxos.7.0.3.I2.1.bin \n' self.assertEqual(result, expected) def test_backup_running_config(self): temp_file = NamedTemporaryFile() self.device.backup_running_config(temp_file.name) expected = '!Command: show running-config\n!Time: Tue Mar 22 21:23:11 2016\nversion 7.0(3)I2(1)\nhostname N9K2\nvdc N9K2 id 1\n limit-resource vlan minimum 16 maximum 4094\n limit-resource vrf minimum 2 maximum 4096\n limit-resource port-channel minimum 0 maximum 511\n limit-resource u4route-mem minimum 248 maximum 248\n limit-resource u6route-mem minimum 96 maximum 96\n limit-resource m4route-mem minimum 58 maximum 58\n limit-resource m6route-mem minimum 8 maximum 8\nfeature telnet\nfeature nxapi\nfeature bash-shell\nfeature scp-server\nfeature vrrp\nfeature tacacs+\ncfs eth distribute\nfeature pim\nfeature udld\nfeature interface-vlan\nfeature hsrp\nfeature lacp\nfeature dhcp\nfeature vpc\nfeature lldp\nfeature vtp\nonep\n session key-required enabled\nno password strength-check\nusername admin password 5 $1$6Anve29g$aKsAE8iRKAQzY7sW1qKZh0 role network-admin\nusername cisco password 5 $1$nGd5VWnS$LJ/a9ztNEt6xruMCG2Erl/ role network-admin\nusername jay password 5 $1$K6cIEEfy$vkYaWr5tEdgr55C86b74u/ role network-operator\nusername ntc password 5 $1$0WWXa9uW$EnQSp3nRPD.nIZTqAE//11 role network-admin\nusername netauto password 5 $1$ITxT/Gi0$QbHUtgzTCFt39i4FYSuzl1 role network-admin\nnxapi http port 80\nnxapi https port 443\nbanner motd *\nDISCONNECT FROM DEVICE IMMEDIATELY.\nIF YOU CONTINUE, YOU WILL BE PROSECUTED TO THE FULLEST\nEXTENT OF THE LAW!!!!\n*\nssh login-attempts 10\nip domain-lookup\nip domain-name ntc.com\nip name-server 208.67.222.222\nip host puppet 176.126.88.189\ntacacs-server timeout 10\ntacacs-server deadtime 30\ntacacs-server host 5.6.7.8 \ntacacs-server host 1.2.3.4 key 7 "\\"hello\\"" \nradius-server host 1.2.3.4 authentication accounting \nobject-group ip address OBJECTGROUP-IP\n 10 1.1.1.1/24 \n 20 2.2.2.2/24 \nip access-list INBOUND_MGMT\n statistics per-entry\n 20 permit tcp 63.118.185.0/24 10.1.100.21/32 eq 22 \n 30 permit icmp any 10.1.100.21/32 \n 40 permit tcp any 10.1.100.21/32 eq 443 \n 50 permit tcp any 10.1.100.21/32 eq www \n 60 permit ip 10.1.100.0/24 10.1.100.21/32 \n 80 permit tcp 89.101.133.0/24 10.1.100.21/32 eq 22 \n 90 permit udp any 10.1.100.21/32 eq snmp \n 100 permit udp any 10.1.100.20/32 eq snmp \n 110 permit tcp 79.52.99.64/32 10.1.100.21/32 eq 22 \n 120 permit tcp 176.126.88.189/32 10.1.100.21/32 eq 22 \nip access-list MYACL\n 10 permit tcp 1.1.1.1/32 eq www any established log \n 20 deny udp 2.1.1.1/20 neq 80 5.5.5.0/24 eq 443 \n 40 remark COMMENT REMARK BY ANSIBLE\n 100 permit ip 10.1.1.1/32 100.1.1.1/24 log \nip access-list ONE\n 15 deny eigrp 1.1.1.1/32 2.2.2.2/32 \n 30 permit tcp any gt smtp any lt 33 urg ack psh rst syn fin established dscp cs7 log \n 40 permit eigrp any any precedence flash fragments time-range RANGE log \n 50 permit udp any range 10 20 any dscp af11 \n 55 permit eigrp any any precedence flash fragments time-range RANGE log \n 65 permit tcp any any precedence routine \n 70 permit tcp any any precedence routine \nip access-list POLICY\n 10 permit 23 any any \nip access-list TWO\n 2 remark this is a test string\n 4 permit eigrp any any \n 10 permit tcp 1.1.1.1/32 eq www any established log \n 20 permit tcp 1.1.1.1/32 any \ntime-range RANGE\ntime-range TEIMER\nvtp domain ntc\nsnmp-server user jay network-operator auth md5 0xe3b9e394dff8a08e8dbfef2c3f9a6564 priv 0xe3b9e394dff8a08e8dbfef2c3f9a6564 localizedkey\nsnmp-server user ntc network-admin auth md5 0x779969ac744909382f0c4bf39275a2c3 priv 0x779969ac744909382f0c4bf39275a2c3 localizedkey\nsnmp-server user netauto network-admin auth md5 0xd85b615bbd22469d476b571844afe9e6 priv 0xd85b615bbd22469d476b571844afe9e6 localizedkey\nrmon event 1 log trap public description FATAL(1) owner PMON@FATAL\nrmon event 2 log trap public description CRITICAL(2) owner PMON@CRITICAL\nrmon event 3 log trap public description ERROR(3) owner PMON@ERROR\nrmon event 4 log trap public description WARNING(4) owner PMON@WARNING\nrmon event 5 log trap public description INFORMATION(5) owner PMON@INFO\nno snmp-server enable traps entity entity_mib_change\nno snmp-server enable traps entity entity_module_status_change\nno snmp-server enable traps entity entity_power_status_change\nno snmp-server enable traps entity entity_module_inserted\nno snmp-server enable traps entity entity_module_removed\nno snmp-server enable traps entity entity_unrecognised_module\nno snmp-server enable traps entity entity_fan_status_change\nno snmp-server enable traps entity entity_power_out_change\nno snmp-server enable traps link linkDown\nno snmp-server enable traps link linkUp\nno snmp-server enable traps link extended-linkDown\nno snmp-server enable traps link extended-linkUp\nno snmp-server enable traps link cieLinkDown\nno snmp-server enable traps link cieLinkUp\nno snmp-server enable traps link delayed-link-state-change\nno snmp-server enable traps rf redundancy_framework\nno snmp-server enable traps license notify-license-expiry\nno snmp-server enable traps license notify-no-license-for-feature\nno snmp-server enable traps license notify-licensefile-missing\nno snmp-server enable traps license notify-license-expiry-warning\nno snmp-server enable traps upgrade UpgradeOpNotifyOnCompletion\nno snmp-server enable traps upgrade UpgradeJobStatusNotify\nno snmp-server enable traps rmon risingAlarm\nno snmp-server enable traps rmon fallingAlarm\nno snmp-server enable traps rmon hcRisingAlarm\nno snmp-server enable traps rmon hcFallingAlarm\nno snmp-server enable traps entity entity_sensor\nno snmp-server enable traps entity cefcMIBEnableStatusNotification\nsnmp-server community networktocode group network-operator\nntp server 33.33.33.33 prefer key 32\nntp server 192.0.2.10 use-vrf ntc\nntp peer 2001:db8::4101\nntp authentication-key 42 md5 qpg 7\nntp trusted-key 42\nntp logging\nntp master 8\naaa authentication login console none \nip route 1.1.1.0/24 2.2.2.2 tag 90 80\nip route 1.1.1.1/32 10.1.10.2\nip route 1.1.1.1/32 10.10.10.1\nip route 1.1.1.1/32 10.10.20.1\nip pim ssm range 232.0.0.0/8\nno ip igmp snooping\nvlan 1-20,30,33,40,100-105,333,400-401\nvlan 2\n name native\nvlan 10\n name test_segment\nvlan 20\n name peer_keepalive\nvlan 30\n name Puppet\nvlan 33\n name PuppetAnsible\nvlan 333\n name webvlan\nvlan 400\n name db_vlan\nvlan 401\n name dba_vlan\nservice dhcp\nip dhcp relay\nipv6 dhcp relay\nvrf context TESTING\nvrf context TestVRF\n shutdown\nvrf context keepalive\nvrf context management\n ip domain-name ntc.com\n ip name-server 208.67.222.222\n ip route 0.0.0.0/0 10.1.100.1\nvrf context test\ninterface Vlan1\n mtu 1600\ninterface Vlan10\n no shutdown\n mtu 1600\n vrf member ntc\n hsrp version 2\ninterface Vlan20\n no shutdown\n mtu 1600\n vrf member keepalive\n ip address 10.1.20.3/24\ninterface Vlan100\n mtu 1600\n no ip redirects\n ip address 20.20.20.2/24\n ip address 100.100.100.2/24 secondary\ninterface Vlan233\n mtu 1600\ninterface port-channel11\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\ninterface port-channel12\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n spanning-tree port type network\ninterface port-channel100\n mtu 9216\n lacp min-links 2\ninterface Ethernet1/1\ninterface Ethernet1/2\ninterface Ethernet1/3\ninterface Ethernet1/4\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\ninterface Ethernet1/5\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\ninterface Ethernet1/6\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 11 mode active\ninterface Ethernet1/7\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 11 mode active\ninterface Ethernet1/8\ninterface Ethernet1/9\ninterface Ethernet1/10\ninterface Ethernet1/11\ninterface Ethernet1/12\ninterface Ethernet1/13\ninterface Ethernet1/14\ninterface Ethernet1/15\ninterface Ethernet1/16\ninterface Ethernet1/17\ninterface Ethernet1/18\ninterface Ethernet1/19\ninterface Ethernet1/20\ninterface Ethernet1/21\ninterface Ethernet1/22\ninterface Ethernet1/23\ninterface Ethernet1/24\ninterface Ethernet1/25\ninterface Ethernet1/26\ninterface Ethernet1/27\ninterface Ethernet1/28\n mtu 9216\n channel-group 100 mode active\ninterface Ethernet1/29\n mtu 9216\n channel-group 100 mode active\ninterface Ethernet1/30\n ip access-group ONE in\ninterface Ethernet1/31\n ip access-group POLICY out\n no switchport\n mtu 1700\ninterface Ethernet1/32\n no switchport\n mtu 1600\n ip pim sparse-mode\n ip igmp version 2\n ip igmp startup-query-interval 31\n ip igmp startup-query-count 2\n ip igmp static-oif route-map ANOTHER_TEST\n no shutdown\ninterface Ethernet1/33\n ip access-group ONE in\n no switchport\n mtu 1600\n ip pim sparse-mode\n ip igmp static-oif 236.0.0.0\n ip igmp static-oif 237.0.0.0\n ip igmp static-oif 238.0.0.0\n ip igmp static-oif 239.0.0.0 source 1.1.1.1\n no shutdown\ninterface Ethernet1/34\ninterface Ethernet1/35\ninterface Ethernet1/36\ninterface Ethernet1/37\ninterface Ethernet1/38\ninterface Ethernet1/39\ninterface Ethernet1/40\ninterface Ethernet1/41\ninterface Ethernet1/42\ninterface Ethernet1/43\ninterface Ethernet1/44\ninterface Ethernet1/45\ninterface Ethernet1/46\ninterface Ethernet1/47\ninterface Ethernet1/48\ninterface Ethernet2/1\n no switchport\n vrf member ntc\n ip address 10.1.100.13/24\n no shutdown\ninterface Ethernet2/2\n no switchport\n mtu 1600\n ip address 10.10.60.1/24\n no shutdown\ninterface Ethernet2/3\n no switchport\n mtu 1600\n ip address 10.10.70.1/24\n no shutdown\ninterface Ethernet2/4\n no switchport\n mtu 1600\n ip address 10.10.80.1/24\n no shutdown\ninterface Ethernet2/5\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 12 mode active\ninterface Ethernet2/6\n switchport mode trunk\n switchport trunk native vlan 2\n switchport trunk allowed vlan 2-20\n channel-group 12 mode active\ninterface Ethernet2/7\ninterface Ethernet2/8\ninterface Ethernet2/9\ninterface Ethernet2/10\ninterface Ethernet2/11\n shutdown\ninterface Ethernet2/12\n shutdown\ninterface mgmt0\n description out of band mgmt interface\n ip access-group INBOUND_MGMT in\n vrf member management\n ip address 10.1.100.21/24\ninterface loopback10\n vrf member ntc\ninterface loopback11\n vrf member ntc\n ip address 11.11.11.11/24\ninterface loopback13\n ip address 13.13.13.13/24\ninterface loopback15\n vrf member test\ninterface loopback16\ncli alias name puppetoper show puppet agent oper\ncli alias name puppetshow show puppet agent last-exec-log\ncli alias name puppetdump show puppet config\ncli alias name puppetfacter show puppet facter\ncli alias name puppetrun execute puppet agent-oneshot\ncli alias name puppetconfig show run | sec puppet\ncli alias name puppetexecute execute puppet agent-oneshot\nline console\nline vty\n session-limit 16\n exec-timeout 0\nboot nxos bootflash:/nxos.7.0.3.I2.1.bin \n' contents = temp_file.read() self.assertEqual(contents, expected) def test_facts(self): self.assertEqual(hasattr(self.device, '_facts'), False) expected = { 'uptime_string': '07:05:47:10', 'uptime': 625630, 'vlans': [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '30', '33', '40', '100', '101', '102', '103', '104', '105', '333', '400', '401' ], u'os_version': '7.0(3)I2(1)', u'serial_number': 'SAL1819S6BE', u'model': 'Nexus9000 C9396PX Chassis', u'hostname': 'N9K2', 'interfaces': [ 'mgmt0', 'Ethernet1/1', 'Ethernet1/2', 'Ethernet1/3', 'Ethernet1/4', 'Ethernet1/5', 'Ethernet1/6', 'Ethernet1/7', 'Ethernet1/8', 'Ethernet1/9', 'Ethernet1/10', 'Ethernet1/11', 'Ethernet1/12', 'Ethernet1/13', 'Ethernet1/14', 'Ethernet1/15', 'Ethernet1/16', 'Ethernet1/17', 'Ethernet1/18', 'Ethernet1/19', 'Ethernet1/20', 'Ethernet1/21', 'Ethernet1/22', 'Ethernet1/23', 'Ethernet1/24', 'Ethernet1/25', 'Ethernet1/26', 'Ethernet1/27', 'Ethernet1/28', 'Ethernet1/29', 'Ethernet1/30', 'Ethernet1/31', 'Ethernet1/32', 'Ethernet1/33', 'Ethernet1/34', 'Ethernet1/35', 'Ethernet1/36', 'Ethernet1/37', 'Ethernet1/38', 'Ethernet1/39', 'Ethernet1/40', 'Ethernet1/41', 'Ethernet1/42', 'Ethernet1/43', 'Ethernet1/44', 'Ethernet1/45', 'Ethernet1/46', 'Ethernet1/47', 'Ethernet1/48', 'Ethernet2/1', 'Ethernet2/2', 'Ethernet2/3', 'Ethernet2/4', 'Ethernet2/5', 'Ethernet2/6', 'Ethernet2/7', 'Ethernet2/8', 'Ethernet2/9', 'Ethernet2/10', 'Ethernet2/11', 'Ethernet2/12', 'port-channel11', 'port-channel12', 'port-channel100', 'loopback10', 'loopback11', 'loopback13', 'loopback15', 'loopback16', 'Vlan1', 'Vlan10', 'Vlan20', 'Vlan100', 'Vlan233' ], 'fqdn': 'N/A' } self.assertEqual(self.device.facts, expected) self.assertEqual(hasattr(self.device, '_facts'), True) # caching test self.assertEqual(self.device.facts, expected) # caching test
interface = 'Eth 2/4' ip_address2 = ['192.168.5.1/24', '192.168.5.2/24'] nexus3_dict = { 'hostname': nexus3_ip, 'ip': '192.168.5.1/24', 'peer_ip': '192.168.5.2' } nexus4_dict = { 'hostname': nexus4_ip, 'ip': '192.168.5.2/24', 'peer_ip': '192.168.5.1' } i = 0 for switch in [nexus3_dict, nexus4_dict]: nxs_switch = Device(host=switch['hostname'], username="******", password='******', transport='https', port=8443) config_int_txt = 'interface ' + interface config_ip_txt = 'ip address ' + switch['ip'] config_bgp_txt = [ 'router bgp 10', 'neighbor {} remote-as 10'.format(switch['peer_ip']), 'address-family ipv4 unicast' ] config_list = [config_int_txt, config_ip_txt] + config_bgp_txt config_interface = nxs_switch.config_list(config_list)
def setUp(self, mock_rpc): self.device = Device('host', 'user', 'pass') self.rpc = mock_rpc self.send_request = mock_rpc.return_value.send_request self.send_request.side_effect = send_request
import socket # Library that will allow for socket timeouts to be coupled with try: except: import sys # Library of variables that have strong interaction with the interpreter import requests # Main Python library for HTTP and HTTPS interactions # import pynxos import time banner = ('-' * 80) # Create a banner for use as a section separator # Turn off SSL warnings import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) nxos1 = Device(host='nxos1.twb-tech.com', username='******', password='******', transport='https', port=8443) nxos2 = Device(host='nxos1.twb-tech.com', username='******', password='******', transport='https', port=8443) device_list = [nxos1, nxos2] for host in device_list: print() print(host.show('show hostname'))
#!/usr/bin/env python from __future__ import unicode_literals, print_function from pynxos.device import Device from getpass import getpass from pprint import pprint as pp import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) nxos1 = Device(host='nxos1.twb-tech.com', username='******', password=getpass(), transport='https', port='8443') nxos2 = Device(host='nxos2.twb-tech.com', username='******', password=getpass(), transport='https', port='8443') print(nxos1.show('show hostname')) print(nxos2.show('show hostname'))
#!/usr/bin/env python from __future__ import unicode_literals, print_function from pynxos.device import Device from getpass import getpass from pprint import pprint as pp import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) nxos1 = Device(host='nxos1.twb-tech.com', username='******', password=getpass(), transport='https', port='8443') #nxos2 = Device(host='nxos2.twb-tech.com', # username='******', # password=getpass(), # transport='https', # port='8443') print(nxos1.show('show hostname')) commands = [ 'interface Loopback68', 'ip address 172.16.68.68 255.255.255.255' ] nxos1.config_list(commands)
from getpass import getpass import requests from pynxos.device import Device from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) nexus_ip = "153.92.39.248" nxs_test = Device(host=nexus_ip, username="******", password=getpass(), transport='https', port=8443) print nxs_test.show("show version") print(nxs_test.facts)
def __init__(self, host, username, password, transport='http', timeout=30, port=None, **kwargs): super(NXOSDevice, self).__init__(host, username, password, vendor='cisco', device_type=NXOS_API_DEVICE_TYPE) self.transport = transport self.timeout = timeout self.native = NXOSNative(host, username, password, transport=transport, timeout=timeout, port=port)
class NXOSDriver(NetworkDriver): def __init__(self, hostname, username, password, timeout=60, optional_args=None): if optional_args is None: optional_args = {} self.hostname = hostname self.username = username self.password = password self.timeout = timeout self.up = False self.replace = True self.loaded = False self.fc = None self.changed = False self.replace_file = None self.merge_candidate = '' if optional_args is None: optional_args = {} # nxos_protocol is there for backwards compatibility, transport is the preferred method self.transport = optional_args.get('transport', optional_args.get('nxos_protocol', 'https')) if self.transport == 'https': self.port = optional_args.get('port', 443) elif self.transport == 'http': self.port = optional_args.get('port', 80) def open(self): try: self.device = NXOSDevice(self.hostname, self.username, self.password, timeout=self.timeout, port=self.port, transport=self.transport) self.device.show('show hostname') self.up = True except (CLIError, ValueError): # unable to open connection raise ConnectionException('Cannot connect to {}'.format(self.hostname)) def close(self): if self.changed: self._delete_file(self.backup_file) self.device = None @staticmethod def _compute_timestamp(stupid_cisco_output): """ Some fields such `uptime` are returned as: 23week(s) 3day(s) This method will determine the epoch of the event. e.g.: 23week(s) 3day(s) -> 1462248287 """ if not stupid_cisco_output or stupid_cisco_output == 'never': return -1.0 if '(s)' in stupid_cisco_output: pass elif ':' in stupid_cisco_output: stupid_cisco_output = stupid_cisco_output.replace(':', 'hour(s) ', 1) stupid_cisco_output = stupid_cisco_output.replace(':', 'minute(s) ', 1) stupid_cisco_output += 'second(s)' else: stupid_cisco_output = stupid_cisco_output.replace('d', 'day(s) ') stupid_cisco_output = stupid_cisco_output.replace('h', 'hour(s)') things = { 'second(s)': { 'weight': 1 }, 'minute(s)': { 'weight': 60 }, 'hour(s)': { 'weight': 3600 }, 'day(s)': { 'weight': 24 * 3600 }, 'week(s)': { 'weight': 7 * 24 * 3600 }, 'year(s)': { 'weight': 365.25 * 24 * 3600 } } things_keys = things.keys() for part in stupid_cisco_output.split(): for key in things_keys: if key in part: things[key]['count'] = napalm.base.helpers.convert( int, part.replace(key, ''), 0) delta = sum([det.get('count', 0) * det.get('weight') for det in things.values()]) return time.time() - delta @staticmethod def _get_reply_body(result): # useful for debugging ret = result.get('ins_api', {}).get('outputs', {}).get('output', {}).get('body', {}) # Original 'body' entry may have been an empty string, don't return that. if not isinstance(ret, dict): return {} return ret @staticmethod def _get_table_rows(parent_table, table_name, row_name): # because if an inconsistent piece of shit. # {'TABLE_intf': [{'ROW_intf': { # vs # {'TABLE_mac_address': {'ROW_mac_address': [{ # vs # {'TABLE_vrf': {'ROW_vrf': {'TABLE_adj': {'ROW_adj': { _table = parent_table.get(table_name) _table_rows = [] if isinstance(_table, list): _table_rows = [_table_row.get(row_name) for _table_row in _table] elif isinstance(_table, dict): _table_rows = _table.get(row_name) if not isinstance(_table_rows, list): _table_rows = [_table_rows] return _table_rows @staticmethod def fix_checkpoint_string(string, filename): # used to generate checkpoint-like files pattern = '''!Command: Checkpoint cmd vdc 1''' if '!Command' in string: return re.sub('!Command.*', pattern.format(filename), string) else: return "{0}\n{1}".format(pattern.format(filename), string) def _get_reply_table(self, result, table_name, row_name): return self._get_table_rows(result, table_name, row_name) def _get_command_table(self, command, table_name, row_name): json_output = self.device.show(command) return self._get_reply_table(json_output, table_name, row_name) def is_alive(self): if self.device: return {'is_alive': True} else: return {'is_alive': False} def load_replace_candidate(self, filename=None, config=None): self.replace = True self.loaded = True if not filename and not config: raise ReplaceConfigException('filename or config param must be provided.') if filename is None: temp_file = tempfile.NamedTemporaryFile() temp_file.write(config) temp_file.flush() cfg_filename = temp_file.name else: cfg_filename = filename self.replace_file = cfg_filename with open(self.replace_file, 'r') as f: file_content = f.read() file_content = self.fix_checkpoint_string(file_content, self.replace_file) temp_file = tempfile.NamedTemporaryFile() temp_file.write(file_content.encode()) temp_file.flush() self.replace_file = cfg_filename self._send_file(temp_file.name, cfg_filename) def load_merge_candidate(self, filename=None, config=None): self.replace = False self.loaded = True if not filename and not config: raise MergeConfigException('filename or config param must be provided.') self.merge_candidate += '\n' # insert one extra line if filename is not None: with open(filename, "r") as f: self.merge_candidate += f.read() else: self.merge_candidate += config def _send_file(self, filename, dest): self.fc = FileCopy(self.device, filename, dst=dest.split('/')[-1]) try: if not self.fc.remote_file_exists(): self.fc.send() elif not self.fc.file_already_exists(): commands = ['terminal dont-ask', 'delete {0}'.format(self.fc.dst)] self.device.config_list(commands) self.fc.send() except NXOSFileTransferError as fte: raise ReplaceConfigException(fte.message) def _create_sot_file(self): """Create Source of Truth file to compare.""" commands = ['terminal dont-ask', 'checkpoint file sot_file'] self.device.config_list(commands) def _get_diff(self, cp_file): """Get a diff between running config and a proposed file.""" diff = [] self._create_sot_file() diff_out = self.device.show( 'show diff rollback-patch file {0} file {1}'.format( 'sot_file', self.replace_file.split('/')[-1]), raw_text=True) try: diff_out = diff_out.split( '#Generating Rollback Patch')[1].replace( 'Rollback Patch is Empty', '').strip() for line in diff_out.splitlines(): if line: if line[0].strip() != '!': diff.append(line.rstrip(' ')) except (AttributeError, KeyError): raise ReplaceConfigException( 'Could not calculate diff. It\'s possible the given file doesn\'t exist.') return '\n'.join(diff) def _get_merge_diff(self): diff = [] running_config = self.get_config(retrieve='running')['running'] running_lines = running_config.splitlines() for line in self.merge_candidate.splitlines(): if line not in running_lines and line: if line[0].strip() != '!': diff.append(line) return '\n'.join(diff) # the merge diff is not necessarily what needs to be loaded # for example under NTP, as the `ntp commit` command might be # alread configured, it is mandatory to be sent # otherwise it won't take the new configuration - see #59 # https://github.com/napalm-automation/napalm-nxos/issues/59 # therefore this method will return the real diff # but the merge_candidate will remain unchanged # previously: self.merge_candidate = '\n'.join(diff) def compare_config(self): if self.loaded: if not self.replace: return self._get_merge_diff() # return self.merge_candidate diff = self._get_diff(self.fc.dst) return diff return '' def _commit_merge(self): commands = [command for command in self.merge_candidate.splitlines() if command] self.device.config_list(commands) if not self.device.save(): raise CommandErrorException('Unable to commit config!') def _save_config(self, filename): """Save the current running config to the given file.""" self.device.show('checkpoint file {}'.format(filename), raw_text=True) def _disable_confirmation(self): self.device.config('terminal dont-ask') def _load_config(self): cmd = 'rollback running file {0}'.format(self.replace_file.split('/')[-1]) self._disable_confirmation() try: rollback_result = self.device.config(cmd) except ConnectionError: # requests will raise an error with verbose warning output return True except Exception: return False if 'Rollback failed.' in rollback_result['msg'] or 'ERROR' in rollback_result: raise ReplaceConfigException(rollback_result['msg']) return True def commit_config(self): if self.loaded: self.backup_file = 'config_' + str(datetime.now()).replace(' ', '_') self._save_config(self.backup_file) if self.replace: if self._load_config() is False: raise ReplaceConfigException else: try: self._commit_merge() self.merge_candidate = '' # clear the merge buffer except Exception as e: raise MergeConfigException(str(e)) self.changed = True self.loaded = False else: raise ReplaceConfigException('No config loaded.') def _delete_file(self, filename): commands = ['terminal dont-ask', 'delete {}'.format(filename), 'no terminal dont-ask'] self.device.show_list(commands, raw_text=True) def discard_config(self): if self.loaded: self.merge_candidate = '' # clear the buffer if self.loaded and self.replace: try: self._delete_file(self.fc.dst) except CLIError: pass self.loaded = False def rollback(self): if self.changed: self.device.rollback(self.backup_file) self.device.save() self.changed = False def get_facts(self): pynxos_facts = self.device.facts final_facts = {key: value for key, value in pynxos_facts.items() if key not in ['interfaces', 'uptime_string', 'vlans']} if pynxos_facts['interfaces']: final_facts['interface_list'] = pynxos_facts['interfaces'] else: final_facts['interface_list'] = self.get_interfaces().keys() final_facts['vendor'] = 'Cisco' hostname_cmd = 'show hostname' hostname = self.device.show(hostname_cmd).get('hostname') if hostname: final_facts['fqdn'] = hostname return final_facts def get_interfaces(self): interfaces = {} iface_cmd = 'show interface' interfaces_out = self.device.show(iface_cmd) interfaces_body = interfaces_out['TABLE_interface']['ROW_interface'] for interface_details in interfaces_body: interface_name = interface_details.get('interface') # Earlier version of Nexus returned a list for 'eth_bw' (observed on 7.1(0)N1(1a)) interface_speed = interface_details.get('eth_bw', 0) if isinstance(interface_speed, list): interface_speed = interface_speed[0] interface_speed = int(interface_speed / 1000) if 'admin_state' in interface_details: is_up = interface_details.get('admin_state', '') == 'up' else: is_up = interface_details.get('state', '') == 'up' interfaces[interface_name] = { 'is_up': is_up, 'is_enabled': (interface_details.get('state') == 'up'), 'description': py23_compat.text_type(interface_details.get('desc', '').strip('"')), 'last_flapped': self._compute_timestamp( interface_details.get('eth_link_flapped', '')), 'speed': interface_speed, 'mac_address': napalm.base.helpers.convert( napalm.base.helpers.mac, interface_details.get('eth_hw_addr')), } return interfaces def get_lldp_neighbors(self): results = {} try: command = 'show lldp neighbors' lldp_raw_output = self.cli([command]).get(command, '') lldp_neighbors = napalm.base.helpers.textfsm_extractor( self, 'lldp_neighbors', lldp_raw_output) except CLIError: lldp_neighbors = [] for neighbor in lldp_neighbors: local_iface = neighbor.get('local_interface') if neighbor.get(local_iface) is None: if local_iface not in results: results[local_iface] = [] neighbor_dict = {} neighbor_dict['hostname'] = py23_compat.text_type(neighbor.get('neighbor')) neighbor_dict['port'] = py23_compat.text_type(neighbor.get('neighbor_interface')) results[local_iface].append(neighbor_dict) return results def get_bgp_neighbors(self): results = {} bgp_state_dict = { 'Idle': {'is_up': False, 'is_enabled': True}, 'Active': {'is_up': False, 'is_enabled': True}, 'Open': {'is_up': False, 'is_enabled': True}, 'Established': {'is_up': True, 'is_enabled': True}, 'Closing': {'is_up': True, 'is_enabled': True}, 'Shutdown': {'is_up': False, 'is_enabled': False}, } try: cmd = 'show bgp sessions vrf all' vrf_list = self._get_command_table(cmd, 'TABLE_vrf', 'ROW_vrf') except CLIError: vrf_list = [] for vrf_dict in vrf_list: result_vrf_dict = {} result_vrf_dict['router_id'] = py23_compat.text_type(vrf_dict['router-id']) result_vrf_dict['peers'] = {} neighbors_list = vrf_dict.get('TABLE_neighbor', {}).get('ROW_neighbor', []) if isinstance(neighbors_list, dict): neighbors_list = [neighbors_list] for neighbor_dict in neighbors_list: neighborid = napalm.base.helpers.ip(neighbor_dict['neighbor-id']) remoteas = napalm.base.helpers.as_number(neighbor_dict['remoteas']) state = py23_compat.text_type(neighbor_dict['state']) bgp_state = bgp_state_dict[state] result_peer_dict = { 'local_as': int(vrf_dict['local-as']), 'remote_as': remoteas, 'remote_id': neighborid, 'is_enabled': bgp_state['is_enabled'], 'uptime': -1, 'description': '', 'is_up': bgp_state['is_up'], } result_peer_dict['address_family'] = { 'ipv4': { 'sent_prefixes': -1, 'accepted_prefixes': -1, 'received_prefixes': -1 } } result_vrf_dict['peers'][neighborid] = result_peer_dict vrf_name = vrf_dict['vrf-name-out'] if vrf_name == 'default': vrf_name = 'global' results[vrf_name] = result_vrf_dict return results def _set_checkpoint(self, filename): commands = ['terminal dont-ask', 'checkpoint file {0}'.format(filename)] self.device.config_list(commands) def _get_checkpoint_file(self): filename = 'temp_cp_file_from_napalm' self._set_checkpoint(filename) cp_out = self.device.show('show file {0}'.format(filename), raw_text=True) self._delete_file(filename) return cp_out def get_lldp_neighbors_detail(self, interface=''): lldp_neighbors = {} filter = '' if interface: filter = 'interface {name} '.format(name=interface) command = 'show lldp neighbors {filter}detail'.format(filter=filter) # seems that some old devices may not return JSON output... try: lldp_neighbors_table_str = self.cli([command]).get(command) # thus we need to take the raw text output lldp_neighbors_list = lldp_neighbors_table_str.splitlines() except CLIError: lldp_neighbors_list = [] if not lldp_neighbors_list: return lldp_neighbors # empty dict CHASSIS_REGEX = r'^(Chassis id:)\s+([a-z0-9\.]+)$' PORT_REGEX = r'^(Port id:)\s+([0-9]+)$' LOCAL_PORT_ID_REGEX = r'^(Local Port id:)\s+(.*)$' PORT_DESCR_REGEX = r'^(Port Description:)\s+(.*)$' SYSTEM_NAME_REGEX = r'^(System Name:)\s+(.*)$' SYSTEM_DESCR_REGEX = r'^(System Description:)\s+(.*)$' SYST_CAPAB_REEGX = r'^(System Capabilities:)\s+(.*)$' ENABL_CAPAB_REGEX = r'^(Enabled Capabilities:)\s+(.*)$' VLAN_ID_REGEX = r'^(Vlan ID:)\s+(.*)$' lldp_neighbor = {} interface_name = None for line in lldp_neighbors_list: chassis_rgx = re.search(CHASSIS_REGEX, line, re.I) if chassis_rgx: lldp_neighbor = { 'remote_chassis_id': napalm.base.helpers.mac(chassis_rgx.groups()[1]) } continue lldp_neighbor['parent_interface'] = '' port_rgx = re.search(PORT_REGEX, line, re.I) if port_rgx: lldp_neighbor['parent_interface'] = py23_compat.text_type(port_rgx.groups()[1]) continue local_port_rgx = re.search(LOCAL_PORT_ID_REGEX, line, re.I) if local_port_rgx: interface_name = local_port_rgx.groups()[1] continue port_descr_rgx = re.search(PORT_DESCR_REGEX, line, re.I) if port_descr_rgx: lldp_neighbor['remote_port'] = py23_compat.text_type(port_descr_rgx.groups()[1]) lldp_neighbor['remote_port_description'] = py23_compat.text_type( port_descr_rgx.groups()[1]) continue syst_name_rgx = re.search(SYSTEM_NAME_REGEX, line, re.I) if syst_name_rgx: lldp_neighbor['remote_system_name'] = py23_compat.text_type( syst_name_rgx.groups()[1]) continue syst_descr_rgx = re.search(SYSTEM_DESCR_REGEX, line, re.I) if syst_descr_rgx: lldp_neighbor['remote_system_description'] = py23_compat.text_type( syst_descr_rgx.groups()[1]) continue syst_capab_rgx = re.search(SYST_CAPAB_REEGX, line, re.I) if syst_capab_rgx: lldp_neighbor['remote_system_capab'] = py23_compat.text_type( syst_capab_rgx.groups()[1]) continue syst_enabled_rgx = re.search(ENABL_CAPAB_REGEX, line, re.I) if syst_enabled_rgx: lldp_neighbor['remote_system_enable_capab'] = py23_compat.text_type( syst_enabled_rgx.groups()[1]) continue vlan_rgx = re.search(VLAN_ID_REGEX, line, re.I) if vlan_rgx: # at the end of the loop if interface_name not in lldp_neighbors.keys(): lldp_neighbors[interface_name] = [] lldp_neighbors[interface_name].append(lldp_neighbor) return lldp_neighbors def cli(self, commands): cli_output = {} if type(commands) is not list: raise TypeError('Please enter a valid list of commands!') for command in commands: command_output = self.device.show(command, raw_text=True) cli_output[py23_compat.text_type(command)] = command_output return cli_output def get_arp_table(self): arp_table = [] command = 'show ip arp' arp_table_vrf = self._get_command_table(command, 'TABLE_vrf', 'ROW_vrf') arp_table_raw = self._get_table_rows(arp_table_vrf[0], 'TABLE_adj', 'ROW_adj') for arp_table_entry in arp_table_raw: raw_ip = arp_table_entry.get('ip-addr-out') raw_mac = arp_table_entry.get('mac') age = arp_table_entry.get('time-stamp') if age == '-': age_sec = -1.0 elif ':' not in age: # Cisco sometimes returns a sub second arp time 0.411797 try: age_sec = float(age) except ValueError: age_sec = -1.0 else: fields = age.split(':') if len(fields) == 3: try: fields = [float(x) for x in fields] hours, minutes, seconds = fields age_sec = 3600 * hours + 60 * minutes + seconds except ValueError: age_sec = -1.0 age_sec = round(age_sec, 1) interface = py23_compat.text_type(arp_table_entry.get('intf-out')) arp_table.append({ 'interface': interface, 'mac': napalm.base.helpers.convert( napalm.base.helpers.mac, raw_mac, raw_mac), 'ip': napalm.base.helpers.ip(raw_ip), 'age': age_sec }) return arp_table def _get_ntp_entity(self, peer_type): ntp_entities = {} command = 'show ntp peers' ntp_peers_table = self._get_command_table(command, 'TABLE_peers', 'ROW_peers') for ntp_peer in ntp_peers_table: if ntp_peer.get('serv_peer', '').strip() != peer_type: continue peer_addr = napalm.base.helpers.ip(ntp_peer.get('PeerIPAddress').strip()) ntp_entities[peer_addr] = {} return ntp_entities def get_ntp_peers(self): return self._get_ntp_entity('Peer') def get_ntp_servers(self): return self._get_ntp_entity('Server') def get_ntp_stats(self): ntp_stats = [] command = 'show ntp peer-status' ntp_stats_table = self._get_command_table(command, 'TABLE_peersstatus', 'ROW_peersstatus') for ntp_peer in ntp_stats_table: peer_address = napalm.base.helpers.ip(ntp_peer.get('remote').strip()) syncmode = ntp_peer.get('syncmode') stratum = int(ntp_peer.get('st')) hostpoll = int(ntp_peer.get('poll')) reachability = int(ntp_peer.get('reach')) delay = float(ntp_peer.get('delay')) ntp_stats.append({ 'remote': peer_address, 'synchronized': (syncmode == '*'), 'referenceid': peer_address, 'stratum': stratum, 'type': '', 'when': '', 'hostpoll': hostpoll, 'reachability': reachability, 'delay': delay, 'offset': 0.0, 'jitter': 0.0 }) return ntp_stats def get_interfaces_ip(self): interfaces_ip = {} ipv4_command = 'show ip interface' ipv4_interf_table_vrf = self._get_command_table(ipv4_command, 'TABLE_intf', 'ROW_intf') for interface in ipv4_interf_table_vrf: interface_name = py23_compat.text_type(interface.get('intf-name', '')) address = napalm.base.helpers.ip(interface.get('prefix')) prefix = int(interface.get('masklen', '')) if interface_name not in interfaces_ip.keys(): interfaces_ip[interface_name] = {} if 'ipv4' not in interfaces_ip[interface_name].keys(): interfaces_ip[interface_name]['ipv4'] = {} if address not in interfaces_ip[interface_name].get('ipv4'): interfaces_ip[interface_name]['ipv4'][address] = {} interfaces_ip[interface_name]['ipv4'][address].update({ 'prefix_length': prefix }) secondary_addresses = interface.get('TABLE_secondary_address', {})\ .get('ROW_secondary_address', []) if type(secondary_addresses) is dict: secondary_addresses = [secondary_addresses] for secondary_address in secondary_addresses: secondary_address_ip = napalm.base.helpers.ip(secondary_address.get('prefix1')) secondary_address_prefix = int(secondary_address.get('masklen1', '')) if 'ipv4' not in interfaces_ip[interface_name].keys(): interfaces_ip[interface_name]['ipv4'] = {} if secondary_address_ip not in interfaces_ip[interface_name].get('ipv4'): interfaces_ip[interface_name]['ipv4'][secondary_address_ip] = {} interfaces_ip[interface_name]['ipv4'][secondary_address_ip].update({ 'prefix_length': secondary_address_prefix }) ipv6_command = 'show ipv6 interface' ipv6_interf_table_vrf = self._get_command_table(ipv6_command, 'TABLE_intf', 'ROW_intf') for interface in ipv6_interf_table_vrf: interface_name = py23_compat.text_type(interface.get('intf-name', '')) address = napalm.base.helpers.ip(interface.get('addr', '').split('/')[0]) prefix = interface.get('prefix', '').split('/')[-1] if prefix: prefix = int(interface.get('prefix', '').split('/')[-1]) else: prefix = 128 if interface_name not in interfaces_ip.keys(): interfaces_ip[interface_name] = {} if 'ipv6' not in interfaces_ip[interface_name].keys(): interfaces_ip[interface_name]['ipv6'] = {} if address not in interfaces_ip[interface_name].get('ipv6'): interfaces_ip[interface_name]['ipv6'][address] = {} interfaces_ip[interface_name]['ipv6'][address].update({ 'prefix_length': prefix }) secondary_addresses = interface.get('TABLE_sec_addr', {}).get('ROW_sec_addr', []) if type(secondary_addresses) is dict: secondary_addresses = [secondary_addresses] for secondary_address in secondary_addresses: sec_prefix = secondary_address.get('sec-prefix', '').split('/') secondary_address_ip = napalm.base.helpers.ip(sec_prefix[0]) secondary_address_prefix = int(sec_prefix[-1]) if 'ipv6' not in interfaces_ip[interface_name].keys(): interfaces_ip[interface_name]['ipv6'] = {} if secondary_address_ip not in interfaces_ip[interface_name].get('ipv6'): interfaces_ip[interface_name]['ipv6'][secondary_address_ip] = {} interfaces_ip[interface_name]['ipv6'][secondary_address_ip].update({ 'prefix_length': secondary_address_prefix }) return interfaces_ip def get_mac_address_table(self): mac_table = [] command = 'show mac address-table' mac_table_raw = self._get_command_table(command, 'TABLE_mac_address', 'ROW_mac_address') for mac_entry in mac_table_raw: raw_mac = mac_entry.get('disp_mac_addr') interface = py23_compat.text_type(mac_entry.get('disp_port')) vlan = int(mac_entry.get('disp_vlan')) active = True static = (mac_entry.get('disp_is_static') != '0') moves = 0 last_move = 0.0 mac_table.append({ 'mac': napalm.base.helpers.mac(raw_mac), 'interface': interface, 'vlan': vlan, 'active': active, 'static': static, 'moves': moves, 'last_move': last_move }) return mac_table def get_snmp_information(self): snmp_information = {} snmp_command = 'show running-config' snmp_raw_output = self.cli([snmp_command]).get(snmp_command, '') snmp_config = napalm.base.helpers.textfsm_extractor(self, 'snmp_config', snmp_raw_output) if not snmp_config: return snmp_information snmp_information = { 'contact': py23_compat.text_type(''), 'location': py23_compat.text_type(''), 'community': {}, 'chassis_id': py23_compat.text_type('') } for snmp_entry in snmp_config: contact = py23_compat.text_type(snmp_entry.get('contact', '')) if contact: snmp_information['contact'] = contact location = py23_compat.text_type(snmp_entry.get('location', '')) if location: snmp_information['location'] = location community_name = py23_compat.text_type(snmp_entry.get('community', '')) if not community_name: continue if community_name not in snmp_information['community'].keys(): snmp_information['community'][community_name] = { 'acl': py23_compat.text_type(snmp_entry.get('acl', '')), 'mode': py23_compat.text_type(snmp_entry.get('mode', '').lower()) } else: acl = py23_compat.text_type(snmp_entry.get('acl', '')) if acl: snmp_information['community'][community_name]['acl'] = acl mode = py23_compat.text_type(snmp_entry.get('mode', '').lower()) if mode: snmp_information['community'][community_name]['mode'] = mode return snmp_information def get_users(self): _CISCO_TO_CISCO_MAP = { 'network-admin': 15, 'network-operator': 5 } _DEFAULT_USER_DICT = { 'password': '', 'level': 0, 'sshkeys': [] } users = {} command = 'show running-config' section_username_raw_output = self.cli([command]).get(command, '') section_username_tabled_output = napalm.base.helpers.textfsm_extractor( self, 'users', section_username_raw_output) for user in section_username_tabled_output: username = user.get('username', '') if not username: continue if username not in users: users[username] = _DEFAULT_USER_DICT.copy() password = user.get('password', '') if password: users[username]['password'] = py23_compat.text_type(password.strip()) level = 0 role = user.get('role', '') if role.startswith('priv'): level = int(role.split('-')[-1]) else: level = _CISCO_TO_CISCO_MAP.get(role, 0) if level > users.get(username).get('level'): # unfortunately on Cisco you can set different priv levels for the same user # Good news though: the device will consider the highest level users[username]['level'] = level sshkeytype = user.get('sshkeytype', '') sshkeyvalue = user.get('sshkeyvalue', '') if sshkeytype and sshkeyvalue: if sshkeytype not in ['ssh-rsa', 'ssh-dsa']: continue users[username]['sshkeys'].append(py23_compat.text_type(sshkeyvalue)) return users def traceroute(self, destination, source=c.TRACEROUTE_SOURCE, ttl=c.TRACEROUTE_TTL, timeout=c.TRACEROUTE_TIMEOUT, vrf=c.TRACEROUTE_VRF): _HOP_ENTRY_PROBE = [ r'\s+', r'(', # beginning of host_name (ip_address) RTT group r'(', # beginning of host_name (ip_address) group only r'([a-zA-Z0-9\.:-]*)', # hostname r'\s+', r'\(?([a-fA-F0-9\.:][^\)]*)\)?' # IP Address between brackets r')?', # end of host_name (ip_address) group only # also hostname/ip are optional -- they can or cannot be specified # if not specified, means the current probe followed the same path as the previous r'\s+', r'(\d+\.\d+)\s+ms', # RTT r'|\*', # OR *, when non responsive hop r')' # end of host_name (ip_address) RTT group ] _HOP_ENTRY = [ r'\s?', # space before hop index? r'(\d+)', # hop index ] traceroute_result = {} timeout = 5 # seconds probes = 3 # 3 probes/jop and this cannot be changed on NXOS! version = '' try: version = '6' if IPAddress(destination).version == 6 else '' except AddrFormatError: return {'error': 'Destination doest not look like a valid IP Address: {}'.format( destination)} source_opt = '' if source: source_opt = 'source {source}'.format(source=source) command = 'traceroute{version} {destination} {source_opt}'.format( version=version, destination=destination, source_opt=source_opt ) try: traceroute_raw_output = self.cli([command]).get(command) except CommandErrorException: return {'error': 'Cannot execute traceroute on the device: {}'.format(command)} hop_regex = ''.join(_HOP_ENTRY + _HOP_ENTRY_PROBE * probes) traceroute_result['success'] = {} if traceroute_raw_output: for line in traceroute_raw_output.splitlines(): hop_search = re.search(hop_regex, line) if not hop_search: continue hop_details = hop_search.groups() hop_index = int(hop_details[0]) previous_probe_host_name = '*' previous_probe_ip_address = '*' traceroute_result['success'][hop_index] = {'probes': {}} for probe_index in range(probes): host_name = hop_details[3+probe_index*5] ip_address_raw = hop_details[4+probe_index*5] ip_address = napalm.base.helpers.convert( napalm.base.helpers.ip, ip_address_raw, ip_address_raw) rtt = hop_details[5+probe_index*5] if rtt: rtt = float(rtt) else: rtt = timeout * 1000.0 if not host_name: host_name = previous_probe_host_name if not ip_address: ip_address = previous_probe_ip_address if hop_details[1+probe_index*5] == '*': host_name = '*' ip_address = '*' traceroute_result['success'][hop_index]['probes'][probe_index+1] = { 'host_name': py23_compat.text_type(host_name), 'ip_address': py23_compat.text_type(ip_address), 'rtt': rtt } previous_probe_host_name = host_name previous_probe_ip_address = ip_address return traceroute_result def get_config(self, retrieve='all'): config = { 'startup': '', 'running': '', 'candidate': '' } # default values if retrieve.lower() in ('running', 'all'): _cmd = 'show running-config' config['running'] = py23_compat.text_type(self.cli([_cmd]).get(_cmd)) if retrieve.lower() in ('startup', 'all'): _cmd = 'show startup-config' config['startup'] = py23_compat.text_type(self.cli([_cmd]).get(_cmd)) return config