def get_arp_table(self, vrf=u''): """FTOS implementation of get_arp_table.""" if vrf: msg = "VRF support has not been added for this getter on this platform." raise NotImplementedError(msg) command = "show arp" arp_entries = self._send_command(command) arp_entries = textfsm_extractor(self, 'show_arp', arp_entries) table = [] for idx, arp in enumerate(arp_entries): entry = { 'interface': arp['interface'], 'ip': ip(arp['ip']), 'mac': mac(arp['mac']), } try: # age is given in minutes entry['age'] = float(arp['age']) * 60 except ValueError: entry['age'] = -1.0 table.append(entry) return table
def process_mac_fields(vlan, mac, mac_type, interface): """Return proper data for mac address fields.""" if mac_type.lower() in ["self", "static", "system"]: static = True if vlan.lower() == "all": vlan = 0 elif vlan == "-": vlan = 0 if ( interface.lower() == "cpu" or re.search(r"router", interface.lower()) or re.search(r"switch", interface.lower()) ): interface = "" else: static = False if mac_type.lower() in ["dynamic"]: active = True else: active = False return { "mac": helpers.mac(mac), "interface": helpers.canonical_interface_name(interface), "vlan": int(vlan), "static": static, "active": active, "moves": -1, "last_move": -1.0, }
def get_interfaces(self): """FTOS implementation of get_interfaces.""" iface_entries = self._get_interfaces_detail() interfaces = {} for i, entry in enumerate(iface_entries): if len(entry['iface_name']) == 0: continue if len(entry['mac_address']) == 0: continue # init interface entry with default values iface = { 'is_enabled': False, 'is_up': False, 'description': str(entry['description']), 'mac_address': u'', 'last_flapped': 0.0, # in seconds 'speed': 0, # in megabits 'mtu': 0, } # not all interface have MAC addresses specified in `show interfaces' # so if converting it to a MAC address won't work, leave it like that try: iface['mac_address'] = mac(entry['mac_address']) except AddrFormatError: pass # set statuses if entry['admin_status'] == 'up': iface['is_enabled'] = True if entry['oper_status'] == 'up': iface['is_up'] = True # parse line_speed if re.search(r'bit$', entry['line_speed']): speed = entry['line_speed'].split(' ') if speed[1] == 'Mbit': iface['speed'] = int(speed[0]) # not sure if this ever occurs elif speed[1] == 'Gbit': iface['speed'] = int(speed[0] * 1000) # parse mtu iface['mtu'] = int(entry['mtu']) # parse last_flapped iface['last_flapped'] = float( parse_uptime(entry['last_flapped'], True)) # add interface data to dict local_intf = canonical_interface_name(entry['iface_name']) interfaces[local_intf] = iface return interfaces
def get_facts(self): """ Return a set of facts from the devices. """ # default values. serial_number, fqdn, os_version, hostname, domain_name = ( "Unknown", ) * 5 output = self._send_command("show switch") show_switch = textfsm_extractor(self, "show_switch", output)[0] uptime = self.parse_uptime(show_switch["uptime"]) vendor = "Dlink" os_version = show_switch["os_version"] serial_number = show_switch["serial_number"] model = show_switch["model"] # In Dlink device can't change hostname. Add system_name. hostname = fqdn = show_switch["system_name"] # Get interface list show_ports = self._send_command("show ports") interface_list = re.findall(r'^\d+', show_ports, re.MULTILINE) return { "uptime": uptime, "vendor": vendor, "os_version": str(os_version), "serial_number": str(serial_number), "model": str(model), "hostname": str(hostname), "fqdn": fqdn, "interface_list": interface_list, # Additional data "mac_addr": mac(show_switch["mac_addr"]), "ip_addr": show_switch["ip_addr"], "vlan_name": show_switch["vlan_name"], "subnet_mask": show_switch["subnet_mask"], "gateway": show_switch["gateway"], "boot_version": show_switch["boot_version"], "protocol_version": show_switch["protocol_version"], "hardware_version": show_switch["hardware_version"], "system_time": show_switch["system_time"], "location": show_switch["location"], "contact": show_switch["contact"], "stp": show_switch["stp"], "gvrp": show_switch["gvrp"], "igmp_snooping": show_switch["igmp_snooping"], "radius": show_switch["radius"], "telnet": show_switch["telnet"], "web": show_switch["web"], "rmon": show_switch["rmon"], "ssh": show_switch["ssh"], "vlan_trunk": show_switch["vlan_trunk"], "syslog": show_switch["syslog"], "cli_paging": show_switch["cli_paging"], "password_encryption": show_switch["password_encryption"], }
def get_lldp_neighbors_detail(self, interface=''): """FTOS implementation of get_lldp_neighbors_detail.""" if interface: command = "show lldp neighbors interface {} detail".format( interface) else: command = "show lldp neighbors detail" lldp_entries = self._send_command(command) lldp_entries = textfsm_extractor(self, 'show_lldp_neighbors_detail', lldp_entries) lldp = {} for idx, lldp_entry in enumerate(lldp_entries): # TODO: the current textfsm template keeps adding an empty entry at # the end of each interface and I couldn't fix it so at some point # it was just easier to get rid of these empty entries in code nonZero = False for key in lldp_entry.keys(): # local_interface is set to Filldown so that is always filled if key == 'local_interface': continue if len(lldp_entry[key].strip()) > 0: nonZero = True break if not nonZero: continue # get pretty interface name local_intf = canonical_interface_name( lldp_entry.pop('local_interface')) # cast some mac addresses for k in ['remote_port', 'remote_chassis_id']: if len(lldp_entry[k].strip()) > 0: try: lldp_entry[k] = mac(lldp_entry[k]) except AddrFormatError: pass # transform capabilities for k in ['remote_system_capab', 'remote_system_enable_capab']: lldp_entry[k] = transform_lldp_capab(lldp_entry[k]) # not implemented lldp_entry['parent_interface'] = u'' lldp.setdefault(local_intf, []) lldp[local_intf].append(lldp_entry) return lldp
def get_mac_address_table(self): """ Returns a lists of dictionaries. Each dictionary represents an entry in the MAC Address Table, having the following keys * mac (string) * interface (string) * vlan (int) * active (boolean) * static (boolean) * moves (int) * last_move (float) Format: VID VLAN Name MAC Address Port Type ---- -------------------------------- ----------------- ---- --------------- 903 ACswmgmt C0-A0-BB-DB-7D-C5 CPU Self 903 ACswmgmt 28-8A-1C-A8-1A-96 25 Dynamic 903 ACswmgmt 70-62-B8-A8-94-13 25 Dynamic """ mac_address_table = [] output = self._send_command("show fdb") show_fdb = textfsm_extractor(self, "show_fdb", output) for line in show_fdb: mac_addr = mac(line["mac"]) interface = line["interface"] vlan = int(line["vlan"]) static = False if line["mac_type"].lower() in ["self", "static", "system"]: static = True if (line["interface"].lower() == "cpu" or re.search(r"router", line["mac_type"].lower()) or re.search(r"switch", line["mac_type"].lower())): interface = "" mac_address_table.append({ "mac": mac_addr, "interface": interface, "vlan": vlan, "static": static, "active": True, "moves": -1, "last_move": -1.0, }) return mac_address_table
def get_mac_address_table(self): """FTOS implementation of get_mac_address_table.""" mac_entries = self._send_command("show mac-address-table") mac_entries = textfsm_extractor(self, 'show_mac-address-table', mac_entries) mac_table = [] for idx, entry in enumerate(mac_entries): entry['mac'] = mac(entry['mac']) entry['interface'] = canonical_interface_name(entry['interface']) entry['vlan'] = int(entry['vlan']) entry['static'] = (entry['static'] == 'Static') entry['active'] = (entry['active'] == 'Active') entry['moves'] = -1 # not implemented entry['last_move'] = -1.0 # not implemented mac_table.append(entry) return mac_table
def get_interfaces(self): """FTOS implementation of get_interfaces.""" iface_entries = self._get_interfaces_detail() interfaces = {} for i, entry in enumerate(iface_entries): if len(entry['iface_name']) == 0: continue if len(entry['mac_address']) == 0: continue # init interface entry with default values iface = { 'is_enabled': False, 'is_up': False, 'description': entry['description'], 'mac_address': mac(entry['mac_address']), 'last_flapped': 0.0, # in seconds 'speed': 0, # in megabits } # set statuses if entry['admin_status'] == 'up': iface['is_enabled'] = True if entry['oper_status'] == 'up': iface['is_up'] = True # parse line_speed if re.search(r'bit$', entry['line_speed']): speed = entry['line_speed'].split(' ') if speed[1] == 'Mbit': iface['speed'] = int(speed[0]) # not sure if this ever occurs elif speed[1] == 'Gbit': iface['speed'] = int(speed[0]*1000) # parse last_flapped iface['last_flapped'] = float(parse_uptime(entry['last_flapped'], True)) # add interface data to dict local_intf = canonical_interface_name(entry['iface_name']) interfaces[local_intf] = iface return interfaces
def get_arp_table(self, vrf=u''): if vrf: msg = "VRF support has not been added for this getter on this platform." raise NotImplementedError(msg) command = "arp -van" arp_entries = self._send_command(command) arp_entries = textfsm_extractor(self, 'show_arp', arp_entries) table = [] for idx, arp in enumerate(arp_entries): entry = { 'interface': arp['interface'], 'ip': ip(arp['ip']), 'mac': mac(arp['mac']), } table.append(entry) return table
def get_arp_table(self, vrf=""): """ Device does not support VRF. Using age as is configured on device, not real aging time. Get arp table information. Return a list of dictionaries having the following set of keys: * interface (string) * mac (string) * ip (string) * age (float) For example:: [ { 'interface' : 'MgmtEth0/RSP0/CPU0/0', 'mac' : '5c:5e:ab:da:3c:f0', 'ip' : '172.17.17.1', 'age' : 1454496274.84 }, { 'interface': 'MgmtEth0/RSP0/CPU0/0', 'mac' : '66:0e:94:96:e0:ff', 'ip' : '172.17.17.2', 'age' : 1435641582.49 } ] """ arp_table = [] output = self._send_command("show arpentry") show_arpentry = textfsm_extractor(self, "show_arpentry", output) for line in show_arpentry: line["mac"] = mac(line["mac"]) line["age"] = int(line["age"]) * 60 arp_table.append(line) return arp_table
def get_arp_table(self, vrf=""): """ Get arp table information. Return a list of dictionaries having the following set of keys: * interface (string) * mac (string) * ip (string) * age (float) For example:: [ { 'interface' : 'MgmtEth0/RSP0/CPU0/0', 'mac' : '5c:5e:ab:da:3c:f0', 'ip' : '172.17.17.1', 'age' : 12.0 }, { 'interface': 'MgmtEth0/RSP0/CPU0/0', 'mac' : '66:0e:94:96:e0:ff', 'ip' : '172.17.17.2', 'age' : 14.0 } ] """ arp_table = [] command = "show ip arp vrf {} | exc INCOMPLETE".format(vrf or "all") output = self._send_command(command) separator = r"^Address\s+Age.*Interface.*$" arp_list = re.split(separator, output, flags=re.M) if len(arp_list) != 2: raise ValueError("Error processing arp table output:\n\n{}".format(output)) arp_entries = arp_list[1].strip() for line in arp_entries.splitlines(): if len(line.split()) >= 4: # Search for extra characters to strip, currently strip '*', '+', '#', 'D' line = re.sub(r"\s+[\*\+\#D]{1,4}\s*$", "", line, flags=re.M) address, age, mac, interface = line.split() else: raise ValueError("Unexpected output from: {}".format(line.split())) if age == "-": age = -1.0 elif ":" not in age: # Cisco sometimes returns a sub second arp time 0.411797 try: age = float(age) except ValueError: age = -1.0 else: age = convert_hhmmss(age) age = float(age) age = round(age, 1) # Validate we matched correctly if not re.search(RE_IPADDR, address): raise ValueError("Invalid IP Address detected: {}".format(address)) if not re.search(RE_MAC, mac): raise ValueError("Invalid MAC Address detected: {}".format(mac)) entry = { "interface": interface, "mac": helpers.mac(mac), "ip": address, "age": age, } arp_table.append(entry) return arp_table
def parse_intf_section(interface): """Parse a single entry from show interfaces output. Different cases: mgmt0 is up admin state is up Ethernet2/1 is up admin state is up, Dedicated Interface Vlan1 is down (Administratively down), line protocol is down, autostate enabled Ethernet154/1/48 is up (with no 'admin state') """ interface = interface.strip() re_protocol = ( r"^(?P<intf_name>\S+?)\s+is\s+(?P<status>.+?)" r",\s+line\s+protocol\s+is\s+(?P<protocol>\S+).*$" ) re_intf_name_state = r"^(?P<intf_name>\S+) is (?P<intf_state>\S+).*" re_is_enabled_1 = r"^admin state is (?P<is_enabled>\S+)$" re_is_enabled_2 = r"^admin state is (?P<is_enabled>\S+), " re_is_enabled_3 = r"^.* is down.*Administratively down.*$" re_mac = r"^\s+Hardware:\s+(?P<hardware>.*),\s+address:\s+(?P<mac_address>\S+) " re_speed = r"\s+MTU .*?,\s+BW\s+(?P<speed>\S+)\s+(?P<speed_unit>\S+).*$" re_description_1 = r"^\s+Description:\s+(?P<description>.*) (?:MTU|Internet)" re_description_2 = r"^\s+Description:\s+(?P<description>.*)$" re_hardware = r"^.* Hardware: (?P<hardware>\S+)$" # Check for 'protocol is ' lines match = re.search(re_protocol, interface, flags=re.M) if match: intf_name = match.group("intf_name") status = match.group("status") protocol = match.group("protocol") if "admin" in status.lower(): is_enabled = False else: is_enabled = True is_up = bool("up" in protocol) else: # More standard is up, next line admin state is lines match = re.search(re_intf_name_state, interface) intf_name = match.group("intf_name") intf_state = match.group("intf_state").strip() is_up = True if intf_state == "up" else False admin_state_present = re.search("admin state is", interface) if admin_state_present: # Parse cases where 'admin state' string exists for x_pattern in [re_is_enabled_1, re_is_enabled_2]: match = re.search(x_pattern, interface, flags=re.M) if match: is_enabled = match.group("is_enabled").strip() is_enabled = True if re.search("up", is_enabled) else False break else: msg = "Error parsing intf, 'admin state' never detected:\n\n{}".format( interface ) raise ValueError(msg) else: # No 'admin state' should be 'is up' or 'is down' strings # If interface is up; it is enabled is_enabled = True if not is_up: match = re.search(re_is_enabled_3, interface, flags=re.M) if match: is_enabled = False match = re.search(re_mac, interface, flags=re.M) if match: mac_address = match.group("mac_address") mac_address = helpers.mac(mac_address) else: mac_address = "" match = re.search(re_hardware, interface, flags=re.M) speed_exist = True if match: if match.group("hardware") == "NVE": speed_exist = False if speed_exist: match = re.search(re_speed, interface, flags=re.M) speed = int(match.group("speed")) speed_unit = match.group("speed_unit") speed_unit = speed_unit.rstrip(",") # This was alway in Kbit (in the data I saw) if speed_unit != "Kbit": msg = "Unexpected speed unit in show interfaces parsing:\n\n{}".format( interface ) raise ValueError(msg) speed = int(round(speed / 1000.0)) else: speed = -1 description = "" for x_pattern in [re_description_1, re_description_2]: match = re.search(x_pattern, interface, flags=re.M) if match: description = match.group("description") break return { intf_name: { "description": description, "is_enabled": is_enabled, "is_up": is_up, "last_flapped": -1.0, "mac_address": mac_address, "speed": speed, } }