From 0bfbc6fa73262299da2e5e80bde0988ecd0b7472 Mon Sep 17 00:00:00 2001 From: Dmitry Luhtionov Date: Wed, 18 Aug 2021 14:51:55 +0300 Subject: [PATCH] AAA --- .../DLink/DxS_Industrial_CLI/get_arp.py | 2 + .../DLink/DxS_Industrial_CLI/get_config.py | 14 ++- .../DxS_Industrial_CLI/get_interfaces.py | 118 ++++++++++++++---- .../DxS_Industrial_CLI/get_lldp_neighbors.py | 99 ++++++++++----- .../DLink/DxS_Industrial_CLI/profile.py | 22 +++- 5 files changed, 192 insertions(+), 63 deletions(-) diff --git a/sa/profiles/DLink/DxS_Industrial_CLI/get_arp.py b/sa/profiles/DLink/DxS_Industrial_CLI/get_arp.py index 27f92f0503..6eaa673d9c 100644 --- a/sa/profiles/DLink/DxS_Industrial_CLI/get_arp.py +++ b/sa/profiles/DLink/DxS_Industrial_CLI/get_arp.py @@ -23,5 +23,7 @@ class Script(BaseScript): r = [] t = parse_table(v) for i in t: + if i[0].startswith("Total Entries"): + break r += [{"ip": i[0], "mac": i[1], "interface": i[2]}] return r diff --git a/sa/profiles/DLink/DxS_Industrial_CLI/get_config.py b/sa/profiles/DLink/DxS_Industrial_CLI/get_config.py index ae677e64ba..d1ca8e7fc9 100644 --- a/sa/profiles/DLink/DxS_Industrial_CLI/get_config.py +++ b/sa/profiles/DLink/DxS_Industrial_CLI/get_config.py @@ -1,7 +1,7 @@ # --------------------------------------------------------------------- # DLink.DxS_Industrial_CLI.get_config # --------------------------------------------------------------------- -# Copyright (C) 2007-2019 The NOC Project +# Copyright (C) 2007-2021 The NOC Project # See LICENSE for details # --------------------------------------------------------------------- @@ -15,9 +15,13 @@ class Script(BaseScript): name = "DLink.DxS_Industrial_CLI.get_config" interface = IGetConfig - def execute_cli(self, **kwargs): - config = self.cli("show running-config") - if "System locked by other session" in config: - raise CLIOperationError("System locked by other session!") + def execute_cli(self, policy="r"): + assert policy in ("r", "s") + if policy == "s": + config = self.cli("show startup-config") + else: + config = self.cli("show running-config") + if "System locked by other session" in config: + raise CLIOperationError("System locked by other session!") config = self.strip_first_lines(config, 3) return self.cleaned_config(config) diff --git a/sa/profiles/DLink/DxS_Industrial_CLI/get_interfaces.py b/sa/profiles/DLink/DxS_Industrial_CLI/get_interfaces.py index 31761f8bcb..10c3077411 100644 --- a/sa/profiles/DLink/DxS_Industrial_CLI/get_interfaces.py +++ b/sa/profiles/DLink/DxS_Industrial_CLI/get_interfaces.py @@ -1,7 +1,7 @@ # --------------------------------------------------------------------- # DLink.DxS_Industrial_CLI.get_interfaces # --------------------------------------------------------------------- -# Copyright (C) 2007-2018 The NOC Project +# Copyright (C) 2007-2021 The NOC Project # See LICENSE for details # --------------------------------------------------------------------- @@ -12,6 +12,7 @@ import re # NOC modules from noc.core.script.base import BaseScript from noc.sa.interfaces.igetinterfaces import IGetInterfaces +from noc.core.ip import IPv4 class Script(BaseScript): @@ -19,10 +20,10 @@ class Script(BaseScript): interface = IGetInterfaces rx_iface = re.compile( - r"^\s*(?P(?:Eth|vlan)\S+) is (?P\S+), [Ll]ink status is (?P\S+)\s*\n" + r"^\s*(?P(?:Eth|vlan|mgmt_ipif |Port-channel)\S+) is (?P\S+),? [Ll]ink status is (?P\S+)\s*\n" r"^\s*Interface type: \S+\s*\n" - r"^\s*Interface description:(?P.+)\n" - r"^\s*MAC [Aa]ddress: (?P\S+)\s*\n", + r"^\s*Interface description:(?P.*)\n" + r"(^\s*MAC [Aa]ddress: (?P\S+)\s*\n)?", re.MULTILINE, ) rx_mtu = re.compile(r"^\s*Maximum transmit unit: (?P\d+) bytes", re.MULTILINE) @@ -33,28 +34,35 @@ class Script(BaseScript): r"^\s*Untagged Member Ports\s*:(?P.*)\n", re.MULTILINE, ) + rx_portgroup_port = re.compile(r"^\s*(?P[Ee]th\d+\S+)\s+", re.MULTILINE) rx_ip_iface = re.compile( - r"^\s*Interface (?Pvlan\d+) is (?P\S+), [Ll]ink status is (?P\S+)\s*\n" - r"^\s*IP Address is (?P\S+)", + r"^\s*(Interface )?(?P(?:vlan|mgmt_ipif |loopback)\d+) is (?P\S+),(?: [Ll]ink status is (?P\S+))?\s*\n" + r"(^\s*IP [Aa]ddress is (?P\S+).*)?\n?" + r"(^\s*IP [Aa]ddress is (?P\S+) \(Manual\) Secondary)?", re.MULTILINE, ) + rx_ip_mtu = re.compile(r"^\s*IP MTU is (?P\d+) bytes\s*\n", re.MULTILINE) + rx_ip_with_prefix = re.compile(r"\d+\.\d+\.\d+.\d+/\d+\.\d+\.\d+.\d+") def execute_cli(self): interfaces = [] v = self.cli("show interfaces") + has_if_range = False for line in v.split("\n\n"): match = self.rx_iface.search(line) if not match: continue + ifname = match.group("ifname") i = { - "name": match.group("ifname"), - "type": "physical", + "name": ifname, + "type": self.profile.get_interface_type(ifname), "admin_status": match.group("admin_status") == "enabled", "oper_status": match.group("oper_status") == "up", "mac": match.group("mac"), + "enabled_protocols": [], } sub = { - "name": match.group("ifname"), + "name": ifname, "admin_status": match.group("admin_status") == "enabled", "oper_status": match.group("oper_status") == "up", "mac": match.group("mac"), @@ -64,11 +72,35 @@ class Script(BaseScript): if descr: i["description"] = descr sub["description"] = descr - v1 = self.cli("show lldp interface %s | include Admin Status" % match.group("ifname")) - if "TX and RX" in v1: - i["enabled_protocols"] = ["LLDP"] + if i["type"] == "physical": + if_range = "%s-%s" % (ifname[3:], ifname.split("/")[2]) + if not has_if_range: + try: + v1 = self.cli("show lldp interface %s | include Admin Status" % ifname) + except self.CLISyntaxError: + v1 = self.cli( + "show lldp interface ethernet %s | include Admin Status" % if_range + ) + has_if_range = True + else: + v1 = self.cli( + "show lldp interface ethernet %s | include Admin Status" % if_range + ) + if "TX and RX" in v1: + i["enabled_protocols"] += ["LLDP"] match = self.rx_mtu.search(line) sub["mtu"] = match.group("mtu") + if i["type"] == "aggregated" and ifname.startswith("Port-channel"): + portgroup = ifname[12:] + v1 = self.cli("show channel-group channel %s detail" % portgroup) + for match1 in self.rx_portgroup_port.finditer(v1): + port = self.profile.convert_interface_name(match1.group("port")) + for iface in interfaces: + if iface["name"] == port: + iface["aggregated_interface"] = ifname + break + if "Protocol: LACP" in v1: + i["enabled_protocols"] += ["LACP"] i["subinterfaces"] = [sub] interfaces += [i] @@ -88,33 +120,71 @@ class Script(BaseScript): sub["untagged_vlan"] = vlan_id v = self.cli("show ip interface") + has_space = False for line in v.split("\n\n"): match = self.rx_ip_iface.search(line) if not match: continue + ifname = match.group("ifname").replace(" ", "") + if match.group("oper_status"): + oper_status = match.group("oper_status") == "up" + else: + oper_status = True i = { - "name": match.group("ifname"), - "type": "SVI", + "name": ifname, + "type": self.profile.get_interface_type(ifname), "admin_status": match.group("admin_status") == "enabled", - "oper_status": match.group("oper_status") == "up", + "oper_status": oper_status, } sub = { - "name": match.group("ifname"), + "name": ifname, "admin_status": match.group("admin_status") == "enabled", - "oper_status": match.group("oper_status") == "up", - "enabled_afi": ["IPv4"], - "ipv4_addresses": [match.group("ip")], - "vlan_ids": [match.group("ifname")[4:]], + "oper_status": oper_status, } - - v1 = self.cli("show interface %s" % match.group("ifname")) + ip = match.group("ip") + if ip: + match1 = self.rx_ip_with_prefix.search(ip) + if match1: + ip_address, ip_subnet = ip.split("/") + ip = "%s/%s" % (ip_address, IPv4.netmask_to_len(ip_subnet)) + sub["enabled_afi"] = ["IPv4"] + sub["ipv4_addresses"] = [ip] + ip = match.group("ip2") + if ip: + match1 = self.rx_ip_with_prefix.search(ip) + if match1: + ip_address, ip_subnet = ip.split("/") + ip = "%s/%s" % (ip_address, IPv4.netmask_to_len(ip_subnet)) + sub["ipv4_addresses"] += [ip] + if i["type"] == "SVI": + sub["vlan_ids"] = (ifname[4:],) + match1 = self.rx_ip_mtu.search(line) + if match1: + i["mtu"] = match1.group("mtu") + if ifname.startswith("vlan"): + ifname_with_space = "vlan %s" % ifname[4:] + elif ifname.startswith("mgmt_ipif"): + ifname_with_space = "mgmt %s" % ifname[9:] + elif ifname.startswith("lopback"): + ifname_with_space = "loopback %s" % ifname[8:] + elif ifname.startswith("Null"): + ifname_with_space = "Null %s" % ifname[4:] + if not has_space: + try: + v1 = self.cli("show interface %s" % ifname) + except self.CLISyntaxError: + v1 = self.cli("show interface %s" % ifname_with_space) + has_space = True + else: + v1 = self.cli("show interface %s" % ifname_with_space) match1 = self.rx_iface.search(v1) descr = match1.group("descr").strip() if descr: i["description"] = descr sub["description"] = descr - i["mac"] = match1.group("mac") - sub["mac"] = match1.group("mac") + if match1.group("mac"): + i["mac"] = match1.group("mac") + sub["mac"] = match1.group("mac") i["subinterfaces"] = [sub] interfaces += [i] diff --git a/sa/profiles/DLink/DxS_Industrial_CLI/get_lldp_neighbors.py b/sa/profiles/DLink/DxS_Industrial_CLI/get_lldp_neighbors.py index 9ef546c919..95c00a56a6 100644 --- a/sa/profiles/DLink/DxS_Industrial_CLI/get_lldp_neighbors.py +++ b/sa/profiles/DLink/DxS_Industrial_CLI/get_lldp_neighbors.py @@ -1,7 +1,7 @@ # --------------------------------------------------------------------- # DLink.DxS_Industrial_CLI.get_lldp_neighbors # --------------------------------------------------------------------- -# Copyright (C) 2007-2019 The NOC Project +# Copyright (C) 2007-2021 The NOC Project # See LICENSE for details # --------------------------------------------------------------------- @@ -14,6 +14,31 @@ from noc.sa.interfaces.igetlldpneighbors import IGetLLDPNeighbors from noc.sa.interfaces.base import MACAddressParameter from noc.sa.interfaces.base import IPv4Parameter from noc.core.text import parse_table +from noc.core.lldp import ( + LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT, + LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS, + LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT, + LLDP_CHASSIS_SUBTYPE_MAC, + LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS, + LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME, + LLDP_CHASSIS_SUBTYPE_LOCAL, + LLDP_PORT_SUBTYPE_ALIAS, + LLDP_PORT_SUBTYPE_COMPONENT, + LLDP_PORT_SUBTYPE_MAC, + LLDP_PORT_SUBTYPE_NETWORK_ADDRESS, + LLDP_PORT_SUBTYPE_NAME, + LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID, + LLDP_PORT_SUBTYPE_LOCAL, + LLDP_CAP_OTHER, + LLDP_CAP_REPEATER, + LLDP_CAP_BRIDGE, + LLDP_CAP_WLAN_ACCESS_POINT, + LLDP_CAP_ROUTER, + LLDP_CAP_TELEPHONE, + LLDP_CAP_DOCSIS_CABLE_DEVICE, + LLDP_CAP_STATION_ONLY, + lldp_caps_to_bits, +) class Script(BaseScript): @@ -32,35 +57,45 @@ class Script(BaseScript): re.MULTILINE, ) - def execute(self): + def execute_cli(self): r = [] + has_if_range = False v = self.cli("show interfaces status") t = parse_table(v) for i in t: - iface = {"local_interface": i[0], "neighbors": []} - v = self.cli("show lldp neighbors interface %s" % i[0]) + ifname = i[0] + iface = {"local_interface": ifname, "neighbors": []} + if_range = "%s-%s" % (ifname[3:], ifname.split("/")[2]) + if not has_if_range: + try: + v = self.cli("show lldp neighbors interface %s" % ifname) + except self.CLISyntaxError: + v = self.cli("show lldp neighbors interface ethernet %s" % if_range) + has_if_range = True + else: + v = self.cli("show lldp neighbors interface ethernet %s" % if_range) for m in self.rx_entity.finditer(v): n = {} n["remote_chassis_id_subtype"] = { - "chassis component": 1, - "interface alias": 2, - "port component": 3, - "mac address": 4, - "network address": 5, - "interface name": 6, - "local": 7, + "chassis component": LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT, + "interface alias": LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS, + "port component": LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT, + "mac address": LLDP_CHASSIS_SUBTYPE_MAC, + "network address": LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS, + "interface name": LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME, + "local": LLDP_CHASSIS_SUBTYPE_LOCAL, }[m.group("chassis_id_type").strip().lower()] n["remote_chassis_id"] = m.group("chassis_id").strip() remote_port_subtype = m.group("port_id_type") remote_port_subtype.replace("_", " ") n["remote_port_subtype"] = { - "interface alias": 1, - "port component": 2, - "mac address": 3, - "network address": 4, - "interface name": 5, - "agent circuit id": 6, - "local": 7, + "interface alias": LLDP_PORT_SUBTYPE_ALIAS, + "port component": LLDP_PORT_SUBTYPE_COMPONENT, + "mac address": LLDP_PORT_SUBTYPE_MAC, + "network address": LLDP_PORT_SUBTYPE_NETWORK_ADDRESS, + "interface name": LLDP_PORT_SUBTYPE_NAME, + "agent circuit id": LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID, + "local": LLDP_PORT_SUBTYPE_LOCAL, }[remote_port_subtype.strip().lower()] n["remote_port"] = m.group("port_id").strip() if n["remote_port_subtype"] == 3: @@ -78,21 +113,19 @@ class Script(BaseScript): n["remote_system_description"] = re.sub( r"\n\s*", "", m.group("system_description").strip() ) - caps = 0 - for c in m.group("system_capabilities").split(","): - c = c.strip() - if not c: - break - caps |= { - "Other": 1, - "Repeater": 2, - "Bridge": 4, - "WLAN Access Point": 8, - "Router": 16, - "Telephone": 32, - "DOCSIS Cable Device": 64, - "Station Only": 128, - }[c] + caps = lldp_caps_to_bits( + m.group("system_capabilities").split(","), + { + "other": LLDP_CAP_OTHER, + "repeater": LLDP_CAP_REPEATER, + "bridge": LLDP_CAP_BRIDGE, + "wlan access point": LLDP_CAP_WLAN_ACCESS_POINT, + "router": LLDP_CAP_ROUTER, + "telephone": LLDP_CAP_TELEPHONE, + "docsis cable device": LLDP_CAP_DOCSIS_CABLE_DEVICE, + "station only": LLDP_CAP_STATION_ONLY, + }, + ) n["remote_capabilities"] = caps iface["neighbors"] += [n] if iface["neighbors"]: diff --git a/sa/profiles/DLink/DxS_Industrial_CLI/profile.py b/sa/profiles/DLink/DxS_Industrial_CLI/profile.py index 3ead21f68e..ec9a2ac303 100644 --- a/sa/profiles/DLink/DxS_Industrial_CLI/profile.py +++ b/sa/profiles/DLink/DxS_Industrial_CLI/profile.py @@ -2,7 +2,7 @@ # Vendor: DLink # OS: DxS_Industrial_CLI # --------------------------------------------------------------------- -# Copyright (C) 2007-2019 The NOC Project +# Copyright (C) 2007-2021 The NOC Project # See LICENSE for details # --------------------------------------------------------------------- @@ -30,10 +30,30 @@ class Profile(BaseProfile): 'Eth1/0/1' >>> Profile().convert_interface_name("1/1") 'Eth1/0/1' + >>> Profile().convert_interface_name("port-channel 1") + 'Port-channel1' + >>> Profile().convert_interface_name("mgmt_ipif 0") + 'mgmt_ipif0' """ + s = s.replace(" ", "") if s.startswith("eth"): return "E%s" % s[1:] elif s.startswith("1/"): return "Eth1/0/%s" % s[2:] + elif s.startswith("port-channel"): + return "P%s" % s[1:] else: return s + + INTERFACE_TYPES = { + "eth": "physical", + "vla": "SVI", # vlan + "mgm": "management", # mgmt_ipif + "por": "aggregated", # Port-channel + "nul": "null", # null + "loo": "loopback", # loopback + } + + @classmethod + def get_interface_type(cls, name): + return cls.INTERFACE_TYPES.get((name[:3]).lower()) -- GitLab