Skip to content

Audit LLDP adjacencies

  • “We replaced a fiber pair on the sw-edge-7 ↔ sw-core-1 trunk. Is LLDP bidirectional now?”
  • “sw-core-1 isn’t showing up in any traceroute. Is the LLDP collection actually working both ways?”
  • “We’re running gNMI primary + SNMP backup. Are they seeing the same fabric?”

In the TUI: press Ctrl+U from any screen (or click the AUDIT tile on home). One table with color-coded status per adjacency. Press Ctrl+R to re-run the audit; the as-of header re-fires it on audit-time change.

From the shell:

Terminal window
make audit-adjacencies

Or, with filters:

Terminal window
docker compose run --rm reconciler l2trace audit-adjacencies --source snmp
docker compose run --rm reconciler l2trace audit-adjacencies --show-healthy

A green check means every open adjacency has a confirmed reverse observation. Red, yellow, or any non-empty table means investigate.

For each open adjacency row from A→B, the audit checks whether B→A exists. Three outcomes:

SymbolMeaningWhat it usually indicates
both directions seen by the same sourcehealthy
bidirectional, but via DIFFERENT sourcestelemetry asymmetry — link is fine, one source is lagging
NO reverse observation from any sourceone-way cable / LLDP disabled on peer / port shutdown

Both has_reverse_same_source and has_reverse_any_source are returned per row so operators can distinguish “actual fabric problem” from “gNMI hasn’t polled yet”.

adjacency audit
┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━┳━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━┓
┃ status ┃ local ┃ port ┃ → ┃ peer ┃ peer port ┃ source ┃
┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━╇━━━╇━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━┩
│ ✗ no reverse │ sw-edge-7 │ Eth8 │ → │ sw-core-1 │ Eth1 │ snmp │
│ △ source asym │ sw-core-1 │ Eth2 │ → │ sw-edge-8 │ Eth8 │ gnmi │
└───────────────┴───────────┴──────┴───┴───────────┴───────────┴────────┘
1 adjacency rows with NO reverse observation
1 adjacency rows with cross-source asymmetry
  • The first row is a real problem: sw-edge-7 sees sw-core-1, but sw-core-1’s adjacency table has no entry pointing back at sw-edge-7 via SNMP (or any other source). Possible causes:

    • One-way cable damage (TX from edge works, RX from core broken)
    • LLDP disabled on sw-core-1’s Eth1
    • sw-core-1’s SNMP poll hasn’t run since the link came up
    • sw-core-1 isn’t registered in l2trace at all
  • The second row is fine for connectivity but worth knowing: sw-core-1’s gNMI sees sw-edge-8, and sw-edge-8 sees sw-core-1 — but sw-edge-8’s gNMI hasn’t observed sw-core-1 yet (its SNMP collector did). The cable works; gNMI is behind on one end.

audit_adjacencies in db/queries.py:

WITH open_adj AS (
SELECT entry_id, local_device_id, local_port_id,
remote_chassis_id, remote_device_id, source, ...
FROM adjacency
WHERE upper_inf(valid_during)
AND upper_inf(recorded_during)
AND remote_device_id IS NOT NULL -- skip unresolved
)
SELECT *,
EXISTS (SELECT 1 FROM open_adj r
WHERE r.local_device_id = o.remote_device_id
AND r.remote_device_id = o.local_device_id
AND r.source = o.source) AS has_reverse_same_source,
EXISTS (SELECT 1 FROM adjacency r ...) AS has_reverse_any_source
FROM open_adj o;

Two important constraints:

  1. Only rows with remote_device_id IS NOT NULL are audited. A row where the peer hasn’t been identified yet is pending, not broken — see How peer resolution works for why this distinction matters. Pending rows get retroactively resolved by the backfill path the next time the peer’s chassis_id is learned.

  2. The same-source check matches r.source = o.source exactly. The any-source check doesn’t. The difference is what separates “telemetry hasn’t caught up” from “the link is actually broken.”

Run the audit:

  • Manually after any cable change to verify the new run.
  • On a cron schedule — once an hour is plenty. Pipe failures into your alerting:
    17 * * * * cd ~/l2trace && make audit-adjacencies | grep -E 'no reverse|asym' | mail -s 'l2trace audit' netops@example
  • As part of post-deploy validation when new switches come online.