Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7fd7c62
test(snmp-discovery): pin current optic-row handling with capture-fai…
leoparente Aug 11, 2026
8356aa7
fix(snmp-discovery): drop vendor brand from optic fixture identifiers
leoparente Aug 11, 2026
8667900
fix(snmp-discovery): stop emitting optic rows as lanes or empty bays
leoparente Aug 11, 2026
25f3b01
fix(snmp-discovery): discover optics published as container or port rows
leoparente Aug 11, 2026
ca6a9b0
fix(snmp-discovery): name fixed-port optic bays for the interface served
leoparente Aug 12, 2026
e64c66c
fix(snmp-discovery): keep a serial-free optic's cage harvestable
leoparente Aug 12, 2026
4909f94
fix(snmp-discovery): keep fixed-port optics out of linecards mode
leoparente Aug 12, 2026
5203b53
docs(snmp-discovery): describe optic discovery and its limits
leoparente Aug 12, 2026
190581d
refactor(snmp-discovery): stop recomputing the effective optic PID
leoparente Aug 12, 2026
66bd9e6
docs(snmp-discovery): correct stale class=9-only scan comments
leoparente Aug 12, 2026
532d280
fix(snmp-discovery): guard against duplicate transceiver bay names
leoparente Aug 12, 2026
2c553cc
test(snmp-discovery): assert the empty-bay harvest directly
leoparente Aug 12, 2026
2cbd555
test(snmp-discovery): pin the class-10 serial-free cage's no-bay outcome
leoparente Aug 12, 2026
521ded7
fix(snmp-discovery): share the duplicate-bay-name guard with submodul…
leoparente Aug 12, 2026
1c0e095
fix(snmp-discovery): reject a name token equal to the optic's own PID
leoparente Aug 12, 2026
f6e066c
fix(snmp-discovery): resolve device before the duplicate-bay-name guard
leoparente Aug 12, 2026
1095bd7
fix(snmp-discovery): scope the missing-serial gate to container/port …
leoparente Aug 13, 2026
5aa4886
fix(snmp-discovery): count new optic drop paths in modules_dropped
leoparente Aug 13, 2026
879bd7c
docs(snmp-discovery): scope the no-serial optic rule to container/port
leoparente Aug 13, 2026
b7c8707
fix(snmp-discovery): name a cageless linecard optic for its interface
leoparente Aug 18, 2026
6dff26b
fix(snmp-discovery): recognise the optic designators device-discovery…
leoparente Aug 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/backends/snmp_discovery/supported_platforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,19 @@ Any other vendor that populates `entPhysicalTable` per RFC 6933 will be discover

**PID classifier.** Module rows (`entPhysicalClass = module(9)`) are split into `supervisor` / `linecard` / `transceiver` / `psu` / `fan` types by matching `entPhysicalModelName` (the vendor product ID) against a small set of prefix rules: `SUP*` / `SUPV*` / `SUP\d` → `supervisor`; optic prefixes `SFP-` / `QSFP-` / `X2-` / `GLC-` / `CFP-` / `XENPAK-` / `XFP-` → `transceiver`; `PSU-` / `PWR-` or `-PWR-` infixes → `psu`; `FAN` / `-FAN-` → `fan`; everything else inside a chassis slot defaults to `linecard`. The classifier is shared across all vendors — no Cisco-only / Arista-only branch. PSU and fan modules are recognised so they label correctly in OTLP metrics, but **never** emitted as `Module` entities (counted in `modules_dropped` instead) — the inventory surface in NetBox stays scoped to line cards, supervisors, and transceivers.

### Fixed-port optics

Not every transceiver arrives as a `module(9)` row under a linecard. On fixed-port hardware — and in some per-port cages on modular platforms — the optic itself is published as a `container(5)` row or a `port(10)` row, with no `module(9)` level in between. The module scan widens to those two classes, but only for rows that carry a recognised optic PID (the same prefix list the PID classifier above uses); a bare cage or a port row without one is left alone, so ordinary container and port rows are never mistaken for modules. `entPhysicalClass = module(9)` remains the only class scanned unconditionally.

**Bay naming.** A fixed-port optic's bay is named for the interface it serves rather than the cage's bare position number, which by itself does not identify the port. The name is taken from an anchored `Xcvr for <iface>` `entPhysicalDescr` — anchored because some platforms publish a "Lane N for Xcvr for `<iface>`" row beneath the same optic, and an unanchored match would emit one bay per lane — or, failing that, from an interface-shaped `entPhysicalName`. Either candidate must contain a digit: one platform names every optic row with the literal token `port`, which would otherwise give every bay on the chassis the same name.

**Optics without a serial.** A module bay requires a serial on the module installed in it, so a `container(5)` or `port(10)` optic reporting none is discovered but never emitted — its would-be bay and model are dropped along with it. This rule is scoped to those two classes, not `module(9)`: a `module(9)` optic under a linecard with no serial was already discoverable before the `container(5)`/`port(10)` widening above — nested under its linecard and emitted with its serial left unset — and applying the same gate to it would have removed working behaviour rather than tightened new behaviour, so it does not. Skipped `container(5)`/`port(10)` optics are aggregated into a single warning per device rather than one line per optic, so a platform publishing dozens of serial-less rows doesn't flood the log every poll.

**Interface-association limitation.** A fixed-port transceiver — one with no `module(9)` parent — is still emitted as its own `ModuleBay` and `Module`, with model, serial, and the interface-named bay all present. Its owning interface does not carry a `module=` reference, though: the `entAliasMappingTable`-based routing that attaches `Interface.module` only walks transceivers nested under another module, and a fixed-port optic never is one, so it never participates — this holds even on a device that populates `entAliasMappingTable`. The visible shape is the same as the documented `iosxr` limitation on the device-discovery side (a transceiver Module emitted without its interface backref), though the cause there is a location-string/interface-name mismatch rather than this routing gap.

**Known false negatives.** The PID prefix list is not exhaustive. Real transceivers observed in captures whose PID it does not match, and are therefore not recognised as transceivers: `CAB-SFP-SFP-1M`, `SFPP-PC005`, `ABCU-5710RZ-CS5`, `FN-TRAN-SFP+GC`, `CVR-QSFP-SFP10G`, `10GE SR 300m SFP+`, `10GE LR 10km SFP+`. Broadening the list is deliberately deferred: those prefixes now reach `container(5)` and `port(10)` rows as well as `module(9)` ones, so loosening them enough to catch these would risk matching a non-optic row too and manufacturing a bay that doesn't exist on the device.

**Empty-bay harvest and nested containers.** A `container(5)` row with no `module(9)` or `port(10)` child underneath is harvested as an empty bay in `full` mode (see [Empty bays](./README.md#modules--modulebays) in the SNMP discovery README). That harvest used to mark only a bay's *nearest* `container(5)` ancestor as populated, so a container whose own children are themselves containers — never a module or port leaf directly — was reported as an empty bay even when everything beneath it was fully populated. On two Arista EOS captures this misreported three containers per device — the transceiver, fan-tray, and power-supply slot containers — as empty even though every cage beneath them held a populated module. The rule is now: a container is an empty bay only if nothing beneath it was emitted, implemented by walking up from every populated bay and marking each `container(5)` ancestor in turn. A genuinely empty slot is unaffected: it has no module beneath it at any depth, so it is still correctly reported as an empty bay.

**Test fixtures.** With one exception, the unit fixtures behind these rules are transcribed from real SNMP simulator recordings rather than hand-authored: row class, parentage, `entPhysicalParentRelPos`, and which fields are populated all mirror the capture, because those are exactly the values the logic above reads. Two mirror separate Arista EOS fixed-port captures — one where every optic has a lane child, one where almost none do, which is the shape that would otherwise reach the empty-bay harvest. One mirrors a Cisco Catalyst 9404R capture with a `port(10)` optic nested inside a linecard's own cage. One mirrors a two-member stack whose optics are named with the literal token `port`. One mirrors a fixed-port device reporting no serial on any optic. The single exception is synthetic rather than transcribed: no captured device has been seen publishing a serial-less optic in a `container(5)` cage of its own, so that shape is built by hand to pin the invariant that such a cage still gets harvested as an empty bay once the serial-less optic above it is dropped.

Loading
Loading