def compare(src, dst): if isinstance(src, py23_compat.string_types): src = py23_compat.text_type(src) if isinstance(src, dict): mode = _mode(src.pop('_mode', '')) if 'list' in src.keys(): if not isinstance(dst, list): # This can happen with nested lists return False return _compare_getter_list(src['list'], dst, mode) return _compare_getter_dict(src, dst, mode) elif isinstance(src, py23_compat.string_types): if src.startswith('<') or src.startswith('>'): cmp_result = _compare_numeric(src, dst) return cmp_result else: m = re.search(src, py23_compat.text_type(dst)) if m: return bool(m) else: return src == dst elif(type(src) == type(dst) == list): pairs = zip(src, dst) diff_lists = [[(k, x[k], y[k]) for k in x if not re.search(x[k], y[k])] for x, y in pairs if x != y] return empty_tree(diff_lists) else: return src == dst
def 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 test_context_manager(self): with pytest.raises(napalm.base.exceptions.ConnectionException) as e, \ driver("blah", "bleh", "blih", optional_args=fail_args) as d: pass assert "You told me to do this" in py23_compat.text_type(e.value) with pytest.raises(AttributeError) as e, \ driver("blah", "bleh", "blih", optional_args=optional_args) as d: assert d.is_alive() == {u'is_alive': True} d.__fake_call() assert d.is_alive() == {u'is_alive': False} assert "object has no attribute" in py23_compat.text_type(e.value)
def get_config(self, retrieve='all'): config = { 'startup': '', 'running': '', 'candidate': '' } # default values if retrieve.lower() in ('running', 'all'): command = 'show running-config' config['running'] = py23_compat.text_type(self._send_command(command, raw_text=True)) if retrieve.lower() in ('startup', 'all'): command = 'show startup-config' config['startup'] = py23_compat.text_type(self._send_command(command, raw_text=True)) return config
def get_facts(self): """Return a set of facts from the devices.""" # default values. vendor = "Mellanox" uptime = -1 os_version, hostname, model = ("", ) * 3 # obtain output from device show_ver = self._send_command("show version") show_hosts = self._send_command("show hosts") show_int_status = self._send_command("show interface status") # uptime/serial_number/IOS version for line in show_ver.splitlines(): if "Uptime:" in line: _, uptime_str = line.split("Uptime:") uptime = self.parse_uptime(uptime_str) if "Product release:" in line: line = line.strip() os_version = line.split()[2] os_version = os_version.strip() if "Product model:" in line: line = line.strip() model = line.split()[2] for line in show_hosts.splitlines(): if "Hostname: " in line: _, hostname = line.split("Hostname: ") break interface_list = [] for line in show_int_status.splitlines(): if line == '': continue elif line.startswith('E') or line.startswith('m'): interface = line.split()[0] # Return canonical interface name interface_list.append( helpers.canonical_interface_name(interface)) return { "uptime": int(uptime), "vendor": vendor, "os_version": py23_compat.text_type(os_version), "model": py23_compat.text_type(model), "hostname": py23_compat.text_type(hostname), "interface_list": interface_list, }
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 test_not_mocking_getters(self): d = driver("blah", "bleh", "blih", optional_args=optional_args) d.open() with pytest.raises(NotImplementedError) as excinfo: d.get_route_to() expected = "You can provide mocked data in {}/get_route_to.1".format(optional_args["path"]) assert expected in py23_compat.text_type(excinfo.value) with pytest.raises(NotImplementedError) as excinfo: d.get_route_to() expected = "You can provide mocked data in {}/get_route_to.2".format(optional_args["path"]) assert expected in py23_compat.text_type(excinfo.value) d.close()
def get_network_instances(self, name=''): """ get_network_instances implementation for NX-OS """ # command 'show vrf detail' returns all VRFs with detailed information # format: list of dictionaries with keys such as 'vrf_name' and 'rd' command = u'show vrf detail' vrf_table_raw = self._get_command_table(command, u'TABLE_vrf', u'ROW_vrf') # command 'show vrf interface' returns all interfaces including their assigned VRF # format: list of dictionaries with keys 'if_name', 'vrf_name', 'vrf_id' and 'soo' command = u'show vrf interface' intf_table_raw = self._get_command_table(command, u'TABLE_if', u'ROW_if') # create a dictionary with key = 'vrf_name' and value = list of interfaces vrf_intfs = defaultdict(list) for intf in intf_table_raw: vrf_intfs[intf[u'vrf_name']].append(py23_compat.text_type(intf['if_name'])) vrfs = {} for vrf in vrf_table_raw: vrf_name = py23_compat.text_type(vrf.get('vrf_name')) vrfs[vrf_name] = {} vrfs[vrf_name][u'name'] = vrf_name # differentiate between VRF type 'DEFAULT_INSTANCE' and 'L3VRF' if vrf_name == u'default': vrfs[vrf_name][u'type'] = u'DEFAULT_INSTANCE' else: vrfs[vrf_name][u'type'] = u'L3VRF' vrfs[vrf_name][u'state'] = {u'route_distinguisher': py23_compat.text_type(vrf.get('rd'))} # convert list of interfaces (vrf_intfs[vrf_name]) to expected format # format = dict with key = interface name and empty values vrfs[vrf_name][u'interfaces'] = {} vrfs[vrf_name][u'interfaces'][u'interface'] = dict.fromkeys(vrf_intfs[vrf_name], {}) # if name of a specific VRF was passed as an argument # only return results for this particular VRF if name: if name in vrfs.keys(): return {py23_compat.text_type(name): vrfs[name]} else: return {} # else return results for all VRFs else: return vrfs
def get_lldp_neighbors(self): """ProCurve implementation of get_lldp_neighbors.""" lldp = {} command = 'show lldp info remote-device' output = self._send_command(command) # Check if system supports the command if 'Invalid input:' in output: return {} # Process the output to obtain just the LLDP entries try: split_output = re.split(r'^.*--------.*$', output, flags=re.M)[1] except IndexError: return {} split_output = split_output.strip() for lldp_entry in split_output.splitlines(): # Example, 1 | 00 25 90 3d c3 1f eth0 eth0 (none).(none) # Example Arubat, A4 | 000ce6-38d120 00 0c e6 38 d1 20 eth0 blnap013 local_port = lldp_entry.strip().split(' ', 1)[0].strip() if '...' in lldp_entry: # ... means something got truncated, we need to look at # the details to get the full output remote_port, device_id = self._get_lldp_neighbors_detail( local_port) else: try: (local_port, delim, chassis_id, r_01, r_02, r_03, r_04, r_05, r_06, remote_port_desc, device_id) = lldp_entry.split() port_id = '{}:{}:{}:{}:{}:{}'.format( r_01, r_02, r_03, r_04, r_05, r_06) except ValueError: remote_port, device_id = self._get_lldp_neighbors_detail( local_port) entry = { 'port': py23_compat.text_type(remote_port_desc), 'hostname': py23_compat.text_type(device_id) } lldp.setdefault(local_port, []) lldp[local_port].append(entry) return lldp
def ip(addr, version=None): """ Converts a raw string to a valid IP address. Optional version argument will detect that \ object matches specified version. Motivation: the groups of the IP addreses may contain leading zeros. IPv6 addresses can \ contain sometimes uppercase characters. E.g.: 2001:0dB8:85a3:0000:0000:8A2e:0370:7334 has \ the same logical value as 2001:db8:85a3::8a2e:370:7334. However, their values as strings are \ not the same. :param raw: the raw string containing the value of the IP Address :param version: (optional) insist on a specific IP address version. :type version: int. :return: a string containing the IP Address in a standard format (no leading zeros, \ zeros-grouping, lowercase) Example: .. code-block:: python >>> ip('2001:0dB8:85a3:0000:0000:8A2e:0370:7334') u'2001:db8:85a3::8a2e:370:7334' """ addr_obj = IPAddress(addr) if version and addr_obj.version != version: raise ValueError("{} is not an ipv{} address".format(addr, version)) return py23_compat.text_type(addr_obj)
def canonical_interface_name(interface, addl_name_map=None): """Function to return an interface's canonical name (fully expanded name). Use of explicit matches used to indicate a clear understanding on any potential match. Regex and other looser matching methods were not implmented to avoid false positive matches. As an example, it would make sense to do "[P|p][O|o]" which would incorrectly match PO = POS and Po = Port-channel, leading to a false positive, not easily troubleshot, found, or known. :param interface: The interface you are attempting to expand. :param addl_name_map (optional): A dict containing key/value pairs that updates the base mapping. Used if an OS has specific differences. e.g. {"Po": "PortChannel"} vs {"Po": "Port-Channel"} """ name_map = {} name_map.update(base_interfaces) interface_type, interface_number = split_interface(interface) if isinstance(addl_name_map, dict): name_map.update(addl_name_map) # check in dict for mapping if name_map.get(interface_type): long_int = name_map.get(interface_type) return long_int + py23_compat.text_type(interface_number) # if nothing matched, return the original name else: return interface
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 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 mac(raw): """ Converts a raw string to a standardised MAC Address EUI Format. :param raw: the raw string containing the value of the MAC Address :return: a string with the MAC Address in EUI format Example: .. code-block:: python >>> mac('0123.4567.89ab') u'01:23:45:67:89:AB' Some vendors like Cisco return MAC addresses like a9:c5:2e:7b:6: which is not entirely valid (with respect to EUI48 or EUI64 standards). Therefore we need to stuff with trailing zeros Example >>> mac('a9:c5:2e:7b:6:') u'A9:C5:2E:7B:60:00' If Cisco or other obscure vendors use their own standards, will throw an error and we can fix later, however, still works with weird formats like: >>> mac('123.4567.89ab') u'01:23:45:67:89:AB' >>> mac('23.4567.89ab') u'00:23:45:67:89:AB' """ if raw.endswith(':'): flat_raw = raw.replace(':', '') raw = '{flat_raw}{zeros_stuffed}'.format(flat_raw=flat_raw, zeros_stuffed='0' * (12 - len(flat_raw))) return py23_compat.text_type(EUI(raw, dialect=_MACFormat))
def 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 _create_tmp_file(config): tmp_dir = tempfile.gettempdir() rand_fname = py23_compat.text_type(uuid.uuid4()) filename = os.path.join(tmp_dir, rand_fname) with open(filename, 'wt') as fobj: fobj.write(config) return filename
def get_ntp_servers(self): """Returns the NTP servers configuration as dictionary.""" ntp_servers = {} command = 'show sntp' output = self._send_command(command) if 'Invalid input' in output: raise ValueError("Command not supported by network device") try: split_sntp = re.split(r'^ -----.*$', output, flags=re.M)[1].strip() except IndexError: return {} if 'Priority' in output: server_idx = 1 else: server_idx = 0 for line in split_sntp.splitlines(): split_line = line.split() ntp_servers[py23_compat.text_type(split_line[server_idx])] = {} return ntp_servers
def _read_wrapper(data): """Ensure unicode always returned on read.""" # Paramiko (strangely) in PY3 returns an int here. if isinstance(data, int): data = chr(data) # Ensure unicode return py23_compat.text_type(data)
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 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_bgp_neighbors_detail(self, neighbor_address=u''): """FTOS implementation of get_bgp_neighbors_detail.""" cmd = ["show ip bgp neighbors"] if len(neighbor_address.strip()) > 0: cmd.append(neighbor_address) command = ' '.join(cmd) neighbors = self._send_command(command) neighbors = textfsm_extractor(self, 'show_ip_bgp_neighbors', neighbors) table = {u'global': {}} for idx, entry in enumerate(neighbors): # TODO: couldn't detect VRF from output vrf = u'global' neighbor = { "up": (entry['connection_state'] == 'ESTABLISHED'), "local_as": -1, # unimplemented "router_id": ip(entry['router_id']), "local_address": py23_compat.text_type(entry['local_address']), "routing_table": u'', # unimplemented "local_address_configured": False, # unimplemented "local_port": entry['local_port'], "remote_address": ip(entry['remote_address']), "multihop": False, # unimplemented "multipath": False, # unimplemented "remove_private_as": False, # unimplemented "import_policy": u'', # unimplemented "export_policy": u'', # unimplemented "connection_state": entry['connection_state'], "previous_connection_state": u'', # unimplemented "last_event": u'', # unimplemented "suppress_4byte_as": False, # unimplemented "local_as_prepend": False, # unimplemented "configured_holdtime": -1, # unimplemented "configured_keepalive": -1, # unimplemented "active_prefix_count": -1, # unimplemented "received_prefix_count": -1, # unimplemented "suppressed_prefix_count": -1, # unimplemented } # cast some integers for k in [ 'remote_as', 'local_port', 'remote_port', 'input_messages', 'output_messages', 'input_updates', 'output_updates', 'messages_queued_out', 'holdtime', 'keepalive', 'accepted_prefix_count', 'advertised_prefix_count', 'flap_count' ]: try: neighbor[k] = int(entry[k]) except ValueError: neighbor[k] = -1 if entry['remote_as'] not in table[vrf]: table[vrf][int(entry['remote_as'])] = [] table[vrf][int(entry['remote_as'])].append(neighbor) return table
def get_lldp_neighbors(self): """ Return LLDP neighbors brief info. Sample input: <device-vrp>dis lldp neighbor brief Local Intf Neighbor Dev Neighbor Intf Exptime(s) XGE0/0/1 huawei-S5720-01 XGE0/0/1 96 XGE0/0/3 huawei-S5720-POE XGE0/0/1 119 XGE0/0/46 Aruba-7210-M GE0/0/2 95 Sample output: { 'XGE0/0/1': [ { 'hostname': 'huawei-S5720-01', 'port': 'XGE0/0/1' }, 'XGE0/0/3': [ { 'hostname': 'huawei-S5720-POE', 'port': 'XGE0/0/1' }, 'XGE0/0/46': [ { 'hostname': 'Aruba-7210-M', 'port': 'GE0/0/2' }, ] } """ results = {} command = 'display lldp neighbor brief' output = self.device.send_command(command) re_lldp = r"(?P<local>\S+)\s+(?P<hostname>\S+)\s+(?P<port>\S+)\s+\d+\s+" match = re.findall(re_lldp, output, re.M) for neighbor in match: local_intf = neighbor[0] if local_intf not in results: results[local_intf] = [] neighbor_dict = dict() neighbor_dict['hostname'] = py23_compat.text_type(neighbor[1]) neighbor_dict['port'] = py23_compat.text_type(neighbor[2]) results[local_intf].append(neighbor_dict) return results
def traceroute(self, destination, source=u'', ttl=255, timeout=2, vrf=u''): """FTOS implementation of traceroute.""" # source, ttl and timeout are not implemented and therefore ignored cmd = ["traceroute"] if len(vrf.strip()) > 0: cmd.append("vrf %s" % vrf) cmd.append(destination) command = ' '.join(cmd) result = self._send_command(command) # check if output holds an error m = re.search(r'% Error: (.+)', result) if m: return { 'error': m.group(1) } # process results of succesful traceroute result = textfsm_extractor(self, 'traceroute', result) trace = {} ttl = None for idx, entry in enumerate(result): if len(entry['ttl'].strip()) > 0 and ttl != int(entry['ttl']): ttl = int(entry['ttl']) trace[ttl] = {'probes': {}} ctr = 1 # rewrite probes for easier splitting probes = re.sub(r'\s+', ' ', entry['probes'].replace('ms', '').strip()) if len(probes) == 0: probes = [] else: probes = probes.split(' ') for probe in probes: trace[ttl]['probes'][ctr] = { 'rtt': float(probe), 'ip_address': ip(py23_compat.text_type(entry['hop'])), 'host_name': py23_compat.text_type(entry['hop']), } ctr += 1 return { 'success': trace, }
def as_number(as_number_val): """Convert AS Number to standardized asplain notation as an integer.""" as_number_str = py23_compat.text_type(as_number_val) if '.' in as_number_str: big, little = as_number_str.split('.') return (int(big) << 16) + int(little) else: return int(as_number_str)
def send_command_expect(self, command, **kwargs): """Fake execute a command in the device by just returning the content of a file.""" # cmd = re.sub(r'[\[\]\*\^\+\s\|/]', '_', command) cmd = '{}'.format(BaseTestDouble.sanitize_text(command)) file_path = 'dellos10/mock_data/{}.txt'.format(cmd) print("file_path :: " + file_path) output = self.read_txt_file(file_path) return py23_compat.text_type(output)
def test_mock_error(self): d = driver("blah", "bleh", "blih", optional_args=optional_args) d.open() with pytest.raises(KeyError) as excinfo: d.get_bgp_neighbors() assert "Something" in py23_compat.text_type(excinfo.value) with pytest.raises(napalm.base.exceptions.ConnectionClosedException) as excinfo: d.get_bgp_neighbors() assert "Something" in py23_compat.text_type(excinfo.value) with pytest.raises(TypeError) as excinfo: d.get_bgp_neighbors() assert "Couldn't resolve exception NoIdeaException" in py23_compat.text_type( excinfo.value) d.close()
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._send_command(command, raw_text=True) cli_output[py23_compat.text_type(command)] = command_output return cli_output
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: output = self.device.send_command(command) cli_output[py23_compat.text_type(command)] = output return cli_output
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 ping(self, destination, source=c.PING_SOURCE, ttl=c.PING_TTL, timeout=c.PING_TIMEOUT, size=c.PING_SIZE, count=c.PING_COUNT, vrf=c.PING_VRF): """Execute ping on the device.""" ping_dict = {} command = 'ping' # Timeout in milliseconds to wait for each reply, the default is 2000 command += ' -t {}'.format(timeout*1000) # Specify the number of data bytes to be sent command += ' -s {}'.format(size) # Specify the number of echo requests to be sent command += ' -c {}'.format(count) if source != '': command += ' -a {}'.format(source) command += ' {}'.format(destination) output = self.device.send_command(command) if 'Error' in output: ping_dict['error'] = output elif 'PING' in output: ping_dict['success'] = { 'probes_sent': 0, 'packet_loss': 0, 'rtt_min': 0.0, 'rtt_max': 0.0, 'rtt_avg': 0.0, 'rtt_stddev': 0.0, 'results': [] } match_sent = re.search(r"(\d+).+transmitted", output, re.M) match_received = re.search(r"(\d+).+received", output, re.M) try: probes_sent = int(match_sent.group(1)) probes_received = int(match_received.group(1)) ping_dict['success']['probes_sent'] = probes_sent ping_dict['success']['packet_loss'] = probes_sent - probes_received except Exception: msg = "Unexpected output data:\n{}".format(output) raise ValueError(msg) match = re.search(r"min/avg/max = (\d+)/(\d+)/(\d+)", output, re.M) if match: ping_dict['success'].update({ 'rtt_min': float(match.group(1)), 'rtt_avg': float(match.group(2)), 'rtt_max': float(match.group(3)), }) results_array = [] match = re.findall(r"Reply from.+time=(\d+)", output, re.M) for i in match: results_array.append({'ip_address': py23_compat.text_type(destination), 'rtt': float(i)}) ping_dict['success'].update({'results': results_array}) return ping_dict
def get_config(self, retrieve='all'): """ Get config from device. """ config = { 'startup': '', 'running': '', 'candidate': '' } if retrieve.lower() in ('running', 'all'): command = 'show config current_config' config['running'] = py23_compat.text_type(self.device.send_command(command)) # Some D-link switch need run other command if "Configuration" not in config['running']: command = 'show config active' config['running'] = py23_compat.text_type(self.device.send_command(command)) if retrieve.lower() in ('candidate', 'all'): command = 'show config config_in_nvram' config['candidate'] = py23_compat.text_type(self.device.send_command(command)) return config
def get_arp_table(self, vrf=""): # 'age' is not implemented yet """ 'show arp' output example: Address HWtype HWaddress Flags Mask Iface 10.129.2.254 ether 00:50:56:97:af:b1 C eth0 192.168.1.134 (incomplete) eth1 192.168.1.1 ether 00:50:56:ba:26:7f C eth1 10.129.2.97 ether 00:50:56:9f:64:09 C eth0 192.168.1.3 ether 00:50:56:86:7b:06 C eth1 """ if vrf: raise NotImplementedError( "VRF support has not been added for this getter on this platform." ) output = self.device.send_command("show arp") output = output.split("\n") # Skip the header line output = output[1:-1] arp_table = list() for line in output: line = line.split() # 'line' example: # ["10.129.2.254", "ether", "00:50:56:97:af:b1", "C", "eth0"] # [u'10.0.12.33', u'(incomplete)', u'eth1'] if "incomplete" in line[1]: macaddr = py23_compat.text_type("00:00:00:00:00:00") else: macaddr = py23_compat.text_type(line[2]) arp_table.append({ 'interface': py23_compat.text_type(line[-1]), 'mac': macaddr, 'ip': py23_compat.text_type(line[0]), 'age': 0.0 }) return arp_table
def get_lldp_neighbors(self): """ Return LLDP neighbors brief info. Sample input: <XG.DC06.F058-AS-S5560-101>display lldp neighbor-information list Chassis ID : * -- -- Nearest nontpmr bridge neighbor # -- -- Nearest customer bridge neighbor Default -- -- Nearest bridge neighbor System Name Local Interface Chassis ID Port ID XG.DC06.F060-CS-S6800-100 XGE1/0/51 d461-feab-b3ab Ten-GigabitEthernet1/2/1 XG.DC06.F060-CS-S6800-100 XGE1/0/52 d461-feab-b3ab Ten-GigabitEthernet2/2/1 Sample output: { 'XGE1/0/51': [ { 'hostname': 'XG.DC06.F060-CS-S6800-100', 'port': 'Ten-GigabitEthernet1/2/1' }, 'XGE1/0/52': [ { 'hostname': 'XG.DC06.F060-CS-S6800-100', 'port': 'Ten-GigabitEthernet2/2/1' }, ] } """ results = {} command = 'display lldp neighbor-information list' output = self.device.send_command(command) re_lldp = r"(?P<hostname>\S+)\s+(?P<local>\S+)\s+\S+\s+(?P<port>[G,T]\S+)\s+" match = re.findall(re_lldp, output, re.M) for neighbor in match: local_intf = neighbor[1] if local_intf not in results: results[local_intf] = [] neighbor_dict = dict() neighbor_dict['hostname'] = py23_compat.text_type(neighbor[0]) neighbor_dict['port'] = py23_compat.text_type(neighbor[2]) results[local_intf].append(neighbor_dict) return results
def get_ntp_stats(self): """ 'ntpq -np' output example remote refid st t when poll reach delay offset jitter ============================================================================== 116.91.118.97 133.243.238.244 2 u 51 64 377 5.436 987971. 1694.82 219.117.210.137 .GPS. 1 u 17 64 377 17.586 988068. 1652.00 133.130.120.204 133.243.238.164 2 u 46 64 377 7.717 987996. 1669.77 """ output = self.device.send_command("ntpq -np") output = output.split("\n")[2:] ntp_stats = list() for ntp_info in output: if len(ntp_info) > 0: remote, refid, st, t, when, hostpoll, reachability, delay, offset, \ jitter = ntp_info.split() # 'remote' contains '*' if the machine synchronized with NTP server synchronized = "*" in remote match = re.search("(\d+\.\d+\.\d+\.\d+)", remote) ip = match.group(1) when = when if when != '-' else 0 ntp_stats.append({ "remote": py23_compat.text_type(ip), "referenceid": py23_compat.text_type(refid), "synchronized": bool(synchronized), "stratum": int(st), "type": py23_compat.text_type(t), "when": py23_compat.text_type(when), "hostpoll": int(hostpoll), "reachability": int(reachability), "delay": float(delay), "offset": float(offset), "jitter": float(jitter) }) return ntp_stats
def test_arguments(self): d = driver("blah", "bleh", "blih", optional_args=optional_args) d.open() with pytest.raises(TypeError) as excinfo: d.get_route_to(1, 2, 3) assert ("get_route_to: expected at most 3 arguments, got 4" in py23_compat.text_type(excinfo.value)) with pytest.raises(TypeError) as excinfo: d.get_route_to(1, 1, protocol=2) assert ("get_route_to: expected at most 3 arguments, got 3" in py23_compat.text_type(excinfo.value)) with pytest.raises(TypeError) as excinfo: d.get_route_to(proto=2) assert ("get_route_to got an unexpected keyword argument 'proto'" in py23_compat.text_type(excinfo.value)) d.close()
def test_basic(self): d = driver("blah", "bleh", "blih", optional_args=optional_args) assert d.is_alive() == {u'is_alive': False} d.open() assert d.is_alive() == {u'is_alive': True} d.close() assert d.is_alive() == {u'is_alive': False} with pytest.raises(napalm.base.exceptions.ConnectionClosedException) as excinfo: d.get_facts() assert "connection closed" in py23_compat.text_type(excinfo.value)
def get_ntp_peers(self): output = self.device.send_command("ntpq -np") output_peers = output.split("\n")[2:] ntp_peers = dict() for line in output_peers: if len(line) > 0: match = re.search("(\d+\.\d+\.\d+\.\d+)\s+", line) ntp_peers.update({py23_compat.text_type(match.group(1)): {}}) return ntp_peers
def test_arguments(self): d = driver("blah", "bleh", "blih", optional_args=optional_args) d.open() with pytest.raises(TypeError) as excinfo: d.get_route_to(1, 2, 3) assert "get_route_to: expected at most 3 arguments, got 4" in py23_compat.text_type( excinfo.value) with pytest.raises(TypeError) as excinfo: d.get_route_to(1, 1, protocol=2) assert "get_route_to: expected at most 3 arguments, got 3" in py23_compat.text_type( excinfo.value) with pytest.raises(TypeError) as excinfo: d.get_route_to(proto=2) assert "get_route_to got an unexpected keyword argument 'proto'" in py23_compat.text_type( excinfo.value) d.close()
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_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_interfaces(self): LOOPBACK_SUBIF_DEFAULTS = { 'is_up': True, 'is_enabled': True, 'speed': 0, 'last_flapped': -1.0, 'mac_address': '', 'description': 'N/A' } interface_dict = {} interface_list = self._extract_interface_list() for intf in interface_list: interface = {} cmd = "<show><interface>{0}</interface></show>".format(intf) try: self.device.op(cmd=cmd) interface_info_xml = xmltodict.parse(self.device.xml_root()) interface_info_json = json.dumps(interface_info_xml['response']['result']['hw']) interface_info = json.loads(interface_info_json) except KeyError as err: if 'loopback.' in intf and 'hw' in str(err): # loopback sub-ifs don't return a 'hw' key interface_dict[intf] = LOOPBACK_SUBIF_DEFAULTS continue raise interface['is_up'] = interface_info.get('state') == 'up' conf_state = interface_info.get('state_c') if conf_state == 'down': interface['is_enabled'] = False elif conf_state in ('up', 'auto'): interface['is_enabled'] = True else: msg = 'Unknown configured state {} for interface {}'.format(conf_state, intf) raise RuntimeError(msg) interface['last_flapped'] = -1.0 interface['speed'] = interface_info.get('speed') # Loopback and down interfaces if interface['speed'] in ('[n/a]', 'unknown'): interface['speed'] = 0 else: interface['speed'] = int(interface['speed']) interface['mac_address'] = standardize_mac(interface_info.get('mac')) interface['description'] = py23_compat.text_type('N/A') interface_dict[intf] = interface return interface_dict
def textfsm_extractor(cls, template_name, raw_text): """ Applies a TextFSM template over a raw text and return the matching table. Main usage of this method will be to extract data form a non-structured output from a network device and return the values in a table format. :param cls: Instance of the driver class :param template_name: Specifies the name of the template to be used :param raw_text: Text output as the devices prompts on the CLI :return: table-like list of entries """ textfsm_data = list() cls.__class__.__name__.replace('Driver', '') current_dir = os.path.dirname(os.path.abspath(sys.modules[cls.__module__].__file__)) template_dir_path = '{current_dir}/utils/textfsm_templates'.format( current_dir=current_dir ) template_path = '{template_dir_path}/{template_name}.tpl'.format( template_dir_path=template_dir_path, template_name=template_name ) try: fsm_handler = textfsm.TextFSM(open(template_path)) except IOError: raise napalm.base.exceptions.TemplateNotImplemented( "TextFSM template {template_name}.tpl is not defined under {path}".format( template_name=template_name, path=template_dir_path ) ) except textfsm.TextFSMTemplateError as tfte: raise napalm.base.exceptions.TemplateRenderException( "Wrong format of TextFSM template {template_name}: {error}".format( template_name=template_name, error=py23_compat.text_type(tfte) ) ) objects = fsm_handler.ParseText(raw_text) for obj in objects: index = 0 entry = {} for entry_value in obj: entry[fsm_handler.header[index].lower()] = entry_value index += 1 textfsm_data.append(entry) return textfsm_data
def get_facts(self): facts = {} try: self.device.op(cmd='<show><system><info></info></system></show>') system_info_xml = xmltodict.parse(self.device.xml_root()) system_info_json = json.dumps(system_info_xml['response']['result']['system']) system_info = json.loads(system_info_json) except AttributeError: system_info = {} if system_info: facts['hostname'] = system_info['hostname'] facts['vendor'] = py23_compat.text_type('Palo Alto Networks') facts['uptime'] = int(convert_uptime_string_seconds(system_info['uptime'])) facts['os_version'] = system_info['sw-version'] facts['serial_number'] = system_info['serial'] facts['model'] = system_info['model'] facts['fqdn'] = py23_compat.text_type('N/A') facts['interface_list'] = self._extract_interface_list() facts['interface_list'].sort() return facts
def get_config(self, retrieve='all'): configs = {} running = py23_compat.text_type('') candidate = py23_compat.text_type('') startup = py23_compat.text_type('') if retrieve == 'all': running = py23_compat.text_type(self._get_running()) candidate = py23_compat.text_type(self._get_candidate()) elif retrieve == 'running': running = py23_compat.text_type(self._get_running()) elif retrieve == 'candidate': candidate = py23_compat.text_type(self._get_candidate()) configs['running'] = running configs['candidate'] = candidate configs['startup'] = startup return configs
def find_txt(xml_tree, path, default=''): """ Extracts the text value from an XML tree, using XPath. In case of error, will return a default value. :param xml_tree: the XML Tree object. Assumed is <type 'lxml.etree._Element'>. :param path: XPath to be applied, in order to extract the desired data. :param default: Value to be returned in case of error. :return: a str value. """ value = '' try: xpath_applied = xml_tree.xpath(path) # will consider the first match only if len(xpath_applied) and xpath_applied[0] is not None: xpath_result = xpath_applied[0] if isinstance(xpath_result, type(xml_tree)): value = xpath_result.text.strip() else: value = xpath_result except Exception: # in case of any exception, returns default value = default return py23_compat.text_type(value)
def abbreviated_interface_name(interface, addl_name_map=None, addl_reverse_map=None): """Function to return an abbreviated representation of the interface name. :param interface: The interface you are attempting to abbreviate. :param addl_name_map (optional): A dict containing key/value pairs that updates the base mapping. Used if an OS has specific differences. e.g. {"Po": "PortChannel"} vs {"Po": "Port-Channel"} :param addl_reverse_map (optional): A dict containing key/value pairs that updates the reverse mapping. Used if an OS has specific differences. e.g. {"PortChannel": "Po"} vs {"PortChannel": "po"} """ name_map = {} name_map.update(base_interfaces) interface_type, interface_number = split_interface(interface) if isinstance(addl_name_map, dict): name_map.update(addl_name_map) rev_name_map = {} rev_name_map.update(reverse_mapping) if isinstance(addl_reverse_map, dict): rev_name_map.update(addl_reverse_map) # Try to ensure canonical type. if name_map.get(interface_type): canonical_type = name_map.get(interface_type) else: canonical_type = interface_type try: abbreviated_name = rev_name_map[canonical_type] + py23_compat.text_type(interface_number) return abbreviated_name except KeyError: pass # If abbreviated name lookup fails, return original name return interface
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_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 mac(raw): """ Converts a raw string to a standardised MAC Address EUI Format. :param raw: the raw string containing the value of the MAC Address :return: a string with the MAC Address in EUI format Example: .. code-block:: python >>> mac('0123.4567.89ab') u'01:23:45:67:89:AB' Some vendors like Cisco return MAC addresses like a9:c5:2e:7b:6: which is not entirely valid (with respect to EUI48 or EUI64 standards). Therefore we need to stuff with trailing zeros Example >>> mac('a9:c5:2e:7b:6:') u'A9:C5:2E:7B:60:00' If Cisco or other obscure vendors use their own standards, will throw an error and we can fix later, however, still works with weird formats like: >>> mac('123.4567.89ab') u'01:23:45:67:89:AB' >>> mac('23.4567.89ab') u'00:23:45:67:89:AB' """ if raw.endswith(':'): flat_raw = raw.replace(':', '') raw = '{flat_raw}{zeros_stuffed}'.format( flat_raw=flat_raw, zeros_stuffed='0'*(12-len(flat_raw)) ) return py23_compat.text_type(EUI(raw, dialect=_MACFormat))
def 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 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 send_command_expect(self, command, **kwargs): """Fake execute a command in the device by just returning the content of a file.""" cmd = re.sub(r'[\[\]\*\^\+\s\|]', '_', command) output = self.read_txt_file('ios/mock_data/{}.txt'.format(cmd)) return py23_compat.text_type(output)
def send_command(self, command, **kwargs): filename = '{}.txt'.format(self.sanitize_text(command)) full_path = self.find_file(filename) result = self.read_txt_file(full_path) return py23_compat.text_type(result)
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