feat(lldp): report neighbours from the LLDP Remote Device Summary page
get_lldp_neighbors() was never implemented, so the switch reported no
neighbours and NetOrk's network map had nothing to draw for the site it
serves — on the reference installation two APs, a firewall and a Proxmox host
behind a 1820 8G PoE+ (J9982A) all stood unconnected.
The web UI does carry the data. /htdocs/pages/switching/lldp_remote.lsp is
served with the rows embedded in the page's aDataSet, so the existing _rows()
helper reads it with no new plumbing:
['7', '3', '00:...:44', '00:...:46', 'enp3s0', 'host.example.com',
'bridge, WLAN access point, router, station only', 'bridge', '192.0.2.10']
Column 3 is the neighbour's port ID, which on many peers is a MAC, while
column 4 is its port description — a name a person can read. The description
wins, with the ID used only when it is missing. The chassis ID is passed
through as `mac` when it looks like one, since netOrk resolves a neighbour by
MAC before falling back to its name.
Fixture captured from the live switch and anonymised in the style of the
existing ones.
Closes #1
This commit is contained in:
@@ -30,6 +30,7 @@ from napalm_hpe_officeconnect.client import OfficeConnectClient
|
||||
|
||||
logger = logging.getLogger("napalm_hpe_officeconnect")
|
||||
|
||||
_MAC_RE = re.compile(r"(?:[0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}")
|
||||
_SPEED_RE = re.compile(r"(\d+)\s*Mbps", re.IGNORECASE)
|
||||
|
||||
# Participation values that make an interface a member of a VLAN.
|
||||
@@ -76,6 +77,7 @@ class OfficeConnectDriver(SwitchDriver):
|
||||
PORT_STATS = "/htdocs/pages/base/port_summary_stats.lsp"
|
||||
MAC_TABLE = "/htdocs/pages/base/mac_address_table.lsp"
|
||||
VLAN_STATUS = "/htdocs/pages/switching/vlan_status.lsp"
|
||||
LLDP_REMOTE = "/htdocs/pages/switching/lldp_remote.lsp"
|
||||
VLAN_PER_PORT = "/htdocs/pages/switching/vlan_per_port.lsp?vlan={vlan}"
|
||||
|
||||
def __init__(
|
||||
@@ -222,6 +224,43 @@ class OfficeConnectDriver(SwitchDriver):
|
||||
"""Like :meth:`get_vlans` but keeping tagged and untagged apart."""
|
||||
return self._vlan_participation()
|
||||
|
||||
def get_lldp_neighbors(self) -> dict[str, list[dict[str, str]]]:
|
||||
"""Return LLDP neighbours keyed by local port.
|
||||
|
||||
Rows of the "LLDP Remote Device Summary" page, verbatim from a
|
||||
1820 8G PoE+ (J9982A)::
|
||||
|
||||
['7', '3', '00:...:44', '00:...:46', 'enp3s0', 'host.example.com',
|
||||
'bridge, WLAN access point, router, station only', 'bridge',
|
||||
'192.0.2.10']
|
||||
|
||||
Column 3 is the neighbour's port *ID*, which on many peers is a MAC;
|
||||
column 4 is its port *description*, which is a name a person can read.
|
||||
The description is preferred and the ID only used when it is absent.
|
||||
|
||||
Without this the switch reports no neighbours at all, and every device
|
||||
behind it — on the reference installation two APs and a firewall —
|
||||
stands unconnected on the network map.
|
||||
"""
|
||||
neighbors: dict[str, list[dict[str, str]]] = {}
|
||||
for row in self._rows(self.LLDP_REMOTE):
|
||||
if len(row) < 6:
|
||||
continue
|
||||
local_port, chassis_id, port_id, port_desc, sysname = (
|
||||
row[0],
|
||||
row[2],
|
||||
row[3],
|
||||
row[4],
|
||||
row[5],
|
||||
)
|
||||
if not local_port:
|
||||
continue
|
||||
entry = {"hostname": sysname, "port": port_desc or port_id}
|
||||
if _MAC_RE.fullmatch(chassis_id or ""):
|
||||
entry["mac"] = chassis_id.lower()
|
||||
neighbors.setdefault(local_port, []).append(entry)
|
||||
return neighbors
|
||||
|
||||
def get_mac_address_table(self) -> list[dict[str, Any]]:
|
||||
"""Columns: [VLAN ID, MAC Address, Interface, Interface Index, Status]."""
|
||||
table = []
|
||||
|
||||
Reference in New Issue
Block a user