Skip to content

Extend report lldp into a cabling check against NetBox - #2881

Draft
berendt wants to merge 4 commits into
implement/issue-2876-report-transceiverfrom
implement/issue-2877-report-lldp-cabling-check
Draft

berendt wants to merge 4 commits into
implement/issue-2876-report-transceiverfrom
implement/issue-2877-report-lldp-cabling-check

Conversation

@berendt

@berendt berendt commented Nov 11, 2026 •

Copy link
Copy Markdown
Member

This pull request turns osism report lldp into a cabling check. The command collects the LLDP neighbours of nodes and SONiC switches live over SSH and compares them with the cabling documented in NetBox. Every port gets a result, so a missing cable, a cable in the wrong port and swapped cables are visible at a glance.

It is the third of a stack of three and builds on #2880. The base branch of this pull request is the branch of #2880.

Commits

  1. Collect LLDP neighbours on SONiC switches: the SONiC collector reads LLDP_ENTRY_TABLE from APPL_DB and falls back to lldpctl in the lldp container. Both are normalised into the neighbour records already used for nodes.
  2. Add NetBox device lookups and the vendor alias index: lookups for the documented peers and for neighbours that announce a name NetBox knows, and the translation of vendor port aliases.
  3. Add the comparison of LLDP neighbours with NetBox cabling: osism/utils/hardware/cabling.py, a pure function over plain data. It resolves LLDP identities to NetBox devices and interfaces, gives every port a result, and finds one-sided links and swapped cables across the whole selection.
  4. Extend report lldp into a cabling check: the reworked Lldp command.

Behavior

  • Results are ok, missing, no-lldp, wrong-port, wrong-device, swapped, unexpected, one-sided, unmapped and stale, each with a hint.
  • Two exchanged cables are reported as swapped on both ports with the partner named, within one device and across devices. Longer rotations are found as well.
  • The expected peer is the far end of the cable path, so a cable through a patch panel is compared with the device behind it.
  • The management port of a switch is checked by default. The BMC port of a node has no counterpart in the operating system and produces no finding on the node. --no-mgmt leaves all mgmt_only interfaces out.
  • Only selected devices are logged in to. Documented peers outside the selection are read from NetBox.
  • --problems, --result and --interface filter the rows. --strict exits with 1 when a device failed or a port has a result other than ok, whatever the filters hide.
  • --no-compare, and a setup without NetBox, give the neighbour listing with the columns the command had before, now also for switches. no-lldp and one-sided stay active there.

Changes for existing users

  • The default output is the comparison table when NetBox is configured. --no-compare gives the previous listing.
  • The command exits with 1 when nothing was selected. It returned success for an empty inventory before.
  • A node without a running lldpd is reported as a failed device, not as a host without neighbours.
  • The failure summary reads Failed to query N device(s): name (reason) instead of N host(s).

Decisions taken while implementing

  • A port that NetBox does not know and that has no neighbour is not listed without --long, also when its link is up. The USB NIC of a BMC is such a port and would show up in every run.
  • A port with link, without a neighbour and without a cable in NetBox is reported as unexpected. The issue lists such ports but names no result for them.
  • A documented interface with a cable and without a live port is reported as unmapped.
  • Nodes are not matched to NetBox interfaces by their LLDP neighbour in this command. A port identified only by what it is plugged into could never be found in the wrong place.
  • A queried peer that sees this device on a port it cannot identify does not make a link one-sided.

Verification

The unit tests of this revision run in CI; they were not run locally. Every commit of the stack was checked with black --check and flake8, and all test modules import at every commit. mypy --ignore-missing-imports reports nothing in the new modules.

The parsers were run against the complete captures before the fixtures were derived from them, and the expectations of the new tests were taken from those runs.

Checked against captures

The captures of #2884 hold both ends of several cables: nodes and the two leaf switches they are cabled to, the link between the leaf switches, and the management ports towards a RouterOS switch. The points the issue lists to confirm on real hardware:

  • Port ID of a SONiC switch configured by OSISM: the alias, with the subtype local, also for breakout ports (Eth1/1/1). The port description is the SONiC name. The management port announces eth0.
  • Port ID of lldpd on the nodes: the MAC address of the port, with the name of the port as description. The chassis ID is the MAC address of some interface of the node.
  • System name: short on nodes and switches.
  • LLDP_ENTRY_TABLE: the field names are the ones the parser reads, and the neighbour of eth0 is listed. The key is the port name on Enterprise SONiC 4.4.1 and the port name followed by the source MAC address of the neighbour on 4.5.2. Both are read. Before this, every neighbour of a 4.5.2 switch was reported a second time, under a port name that does not exist.

What the captures showed beyond that, and what changed because of it:

  • lldp_rem_time_mark is not the age of a neighbour on Enterprise SONiC, but the uptime of lldpd at which it was learned. Neighbours read from the table have no age.
  • A node does not always announce its device name. One node has another host name, one still has the default name of its image, and on some ports the LLDP agent of the NIC answers in place of the host, with the name of the NIC. Such a neighbour is found by its port ID, the MAC address of the port, when NetBox documents it.
  • The chassis ID and the management addresses are not unique. Several nodes announce the MAC address of the USB host interface of their BMC as chassis ID, which is the same on every node of a model, and the address of docker0 as management address. A MAC address that NetBox documents on several devices is not used to identify a device.
  • RouterOS announces a bridge port as bridge/ether9. It is compared by the name of the port.

A test compares the captures of a node and of both leaf switches against a matching NetBox description: every cable is ok from both ends, including the management ports.

Not verified

  • None of the captured nodes has a bond, so what lldpd announces for bond members is still open.
  • The fallback to lldpctl in the lldp container was not needed on the captured switches. The capture of the 4.5.2 switch contains its output, which the parser reads.
  • Community SONiC was not captured.
  • The command was not run against NetBox.

Closes #2877

Assisted-by: Claude:claude-fable-5-2

@berendt
berendt added this pull request to stack #2882 November 11, 2026 21:46
@berendt berendt moved this from New to In progress in Human Board Nov 11, 2026
@berendt berendt self-assigned this Nov 11, 2026
@berendt
berendt force-pushed the implement/issue-2877-report-lldp-cabling-check branch from 448fd30 to cf6b4e8 Compare November 13, 2026 13:08
The cabling check needs the neighbours of the switches as well as
those of the nodes.

The SONiC collector gains the section lldp. It dumps LLDP_ENTRY_TABLE
from APPL_DB. On Enterprise SONiC 4.4 and 4.5 the table lists the
management port next to the front-panel ports. lldpctl inside the
lldp container is only run when the table does not list eth0: it is
the fallback for a switch without the table, or with one that only
has front-panel ports.

The key of an entry is the port name on Enterprise SONiC 4.4 and the
port name followed by the source MAC address of the neighbour on 4.5,
which keeps several neighbours per port. Both are read, in the
collector and in the parser.

parse_lldp turns the table into the same neighbour records as lldpctl
JSON, translating the numeric LLDP-MIB subtypes into the names
lldpctl uses. The records have no age: lldp_rem_time_mark is the
uptime of lldpd at which the neighbour was learned on Enterprise
SONiC, not the time since then. Ports the table does not list are
filled in from lldpctl. A switch where neither source can be read
raises an error with the reason, so that it is reported as a failed
device and not as a switch without neighbours.

The switch fixture gains its LLDP data, and a capture of the second
leaf of the pair, running Enterprise SONiC 4.4.1, is added.

Tests: the collector section with its fallbacks, and the parser for
both key formats, the merge with lldpctl, the failure messages, and
both ends of a captured cable.

Part of #2877.

Assisted-by: Claude:claude-fable-5-2
Signed-off-by: Christian Berendt <berendt@osism.tech>
Resolving an LLDP neighbour to NetBox needs devices the report did
not select: the documented peers of the selected interfaces, and
devices a neighbour announces by name.

fetch_devices() loads devices by ID in chunks. find_devices() looks
them up by name, ignoring case, and also tries the name without its
domain, because a device can announce itself fully qualified while
NetBox holds the short name. sonic_alias_index() maps the vendor
aliases of a HWSKU port config to SONiC port names, for switches that
advertise twentyFiveGigE1 instead of the NetBox name.

Tests: chunking, the name variants, and the alias index against the
port config files of the repository.

Part of #2877.

Assisted-by: Claude:claude-fable-5-2
Signed-off-by: Christian Berendt <berendt@osism.tech>
NetBox holds the intended cabling and the devices know their
neighbours. cabling brings the two together, as a pure function over
plain data without SSH or pynetbox.

Directory resolves what a neighbour announces. The device is found by
system name, case-insensitively and without the domain. A node does
not always announce its device name: its host name can differ, it
can still have the default name of its image, and the LLDP agent of
a NIC can answer in place of the host, with the name of the NIC. The
fallbacks are therefore the port ID when it is a MAC address, which
is what lldpd sends and which names one interface, then the
management addresses, then the chassis ID. A MAC address that NetBox
documents on several devices identifies none of them: the USB host
interface of a BMC has the same address on every node of a model,
and lldpd uses it as chassis ID on some.

The port is found by interface name or label, through the HWSKU port
config for a SONiC port name or vendor alias, by MAC address, or
through the live interfaces of a device that was queried as well.
RouterOS announces a bridge port as bridge/ether9; it is compared by
the name of the port.

compare() then gives every port a result: ok, missing, no-lldp,
wrong-port, wrong-device, unexpected, unmapped or stale. The expected
peer is the far end of the cable path, so a cable through a patch
panel is compared with the device behind it. A management interface
without a live counterpart is the BMC of a node and is skipped; its
cable is checked from the switch it is plugged into.

Two passes look across devices. A port that sees its documented peer
becomes one-sided when the peer was queried and sees something else.
Mismatches are searched for exchanged cables: when the port that owns
the cable seen here in turn sees a cable owned by another mismatch,
and the chain leads back, all its members are swapped and name their
partners. That covers two ports crossed on one node, the cables of
two nodes crossed on a switch, and longer rotations.

neighbour_listing() is the variant without NetBox. It keeps the two
checks that need no source of truth, no-lldp and one-sided.

Tests: the result matrix for single ports, swaps within one device,
across devices and as a rotation, both passes with and without a
queried peer, management interfaces, and every resolution rule. The
captures of a node and of the two switches it is cabled to are
compared from both ends.

Part of #2877.

Assisted-by: Claude:claude-fable-5-2
Signed-off-by: Christian Berendt <berendt@osism.tech>
Cabling errors are the most common fault after racking and after
hardware replacement, and they are slow to find: two swapped cables
still give link on both ports. osism report lldp printed the raw
neighbours of the nodes, to be read next to NetBox by hand.

The command now collects the neighbours of nodes and SONiC switches
in parallel and compares them with the cabling in NetBox. Every port
with a cable in NetBox or a live neighbour gets a result and a hint,
so a missing cable, a cable in the wrong port and swapped cables are
visible at a glance. Devices are selected with the options of report
transceiver, and only selected devices are logged in to; documented
peers outside the selection are read from NetBox. The management
port of a switch is part of the check unless --no-mgmt is given.

--problems, --result and --interface filter the rows, and the output
is a table, CSV or JSON. --strict exits with 1 when a device failed
or a port has a result other than ok, whatever the filters hide.

--no-compare, and a setup without NetBox, give the neighbour listing
with the columns the command had before, now also for switches.

The command exits with 1 when nothing was selected, where it used to
return success for an empty inventory. A node without a running
lldpd is reported as a failed device instead of as a host without
neighbours.

Tests: the existing lldp tests run against the new collection path
and keep their assertions; the failure summary names devices instead
of hosts. New tests cover the comparison, the swap across nodes, the
management port, the filters and formats, --strict, failed devices,
the lookup of unknown neighbours and the listing without NetBox.

Closes #2877

Assisted-by: Claude:claude-fable-5-2
Signed-off-by: Christian Berendt <berendt@osism.tech>
@berendt
berendt force-pushed the implement/issue-2877-report-lldp-cabling-check branch from cf6b4e8 to b358370 Compare November 13, 2026 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

Extend report lldp into a cabling check: compare live LLDP neighbours of nodes and SONiC switches with NetBox

1 participant