Skip to content

fix(snmp-discovery): type standalone devices by their chassis part number - #665

Merged
leoparente merged 10 commits into
developfrom
fix/snmp-standalone-chassis-model
Oct 7, 2026
Merged

leoparente merged 10 commits into
developfrom
fix/snmp-standalone-chassis-model

Conversation

@leoparente

@leoparente leoparente commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Problem

SNMP discovery named a device's type in two different ways. A stack member took its chassis row's entPhysicalModelName, which for most major vendors is the orderable part number (WS-C2960X-48FPD-L). A standalone device kept the name from the bundled sysObjectID lookup, which is a MIB product name (catWsC2960x48fpdL). So the same hardware landed on two device types depending on whether it was stacked, and a standalone device never matched a curated device type such as those in the NetBox device-type library or NDX, which record the part number.

Change

  • Standalone devices of trusted product lines take the chassis model. When the sysObjectID is under one of these arcs of 1.3.6.1.4.1, the device type model comes from the single chassis row, keeping the looked-up manufacturer:

    • 9 (Cisco)
    • 11.2.3.7 (HP ProCurve and ArubaOS-Switch)
    • 25461 (Palo Alto Networks)
    • 30065 (Arista)
    • 47196 (Aruba CX)
    • 12356.101.1 (FortiGate, FortiWiFi and FortiGate Rugged), whose rows spell the model with underscores (FGT_60F). A Fortinet-only rewrite turns them into the orderable form (FG-60F, FGR-60F-3G4G). Virtual FortiGates (FGT_VM64, FGT_ARM64_…) and the 6000F, 7000E and 7000F chassis systems (.60001, .70001, .71201, where one sysObjectID covers every chassis size) keep the lookup name.
    • 171.10.70, .118, .119, .133, .137 and .141 (the D-Link DGS families recorded), which today have no lookup entry, so the raw sysObjectID was the model.
  • Why an allow-list. Across the recorded standalone walks (144 with exactly one chassis row, out of 1,882 librenms and lab recordings), those arcs reported a part number there. The FortiGate and D-Link additions came from a survey of those recordings that three adversarial reviews checked: seven distinct FortiGate models across FortiOS 5.4 to 7.2 rewrite to exact catalog part numbers, and eight distinct D-Link switches report clean models. Other vendors report FRU numbers, OS versions, serial fragments, chip or class names, which would be a worse type name than the lookup's. They keep today's name, as do HP product lines outside ProCurve, Aruba wireless (14823), Comware (25506) and Juniper, which has no recordings.

  • Guards. The chassis model is not taken when it is empty, a placeholder (N/A, Default string, chassis and similar), unprintable or longer than NetBox's 100 characters. It is taken only when the device reports exactly one chassis row, so not when other rows were refused as ambiguous or report no serial, and only when the device has a device type to carry it. A sole chassis row with no serial still names the model.

  • Operator names win.

    • A model set in defaults or override_defaults pins the whole target. That now includes a stack's master and every member, which previously took their own chassis models regardless; the old comment called this a contract bug.
    • A lookup_extensions_dir entry that changes or adds a model pins a standalone device. Stack members keep their own chassis models under it, as they always have. An unchanged copy of a bundled entry pins nothing, so seeding the directory from the bundled files does not switch the change off. Entries are compared with the bundled catalog as loaded, before any user file.
    • A user key written without the leading dot now replaces the bundled entry instead of sitting beside it unread.
  • Docs. The backend docs describe the chassis row in the model precedence, name the exact arcs and the stack rules, and advise copying only the lookup files you change.

  • A device that answers no sysObjectID. A model and manufacturer set together in defaults or override_defaults become its device type, and a stack pinned that way never takes its chassis rows' models.

  • Left out. Juniper (no usable chassis rows in recorded walks), Huawei (empty rows), Dell Force10 (internal IDs), Extreme (truncated VOSS codes), Eltex (one wrong model), Dell OS10 (one recording) and vendors with a single walk wait for real captures.

Upgrade effects

  • Existing standalone devices of those product lines change device type on their next scan, from the MIB name (or, for D-Link, the raw sysObjectID) to the part number. Where a curated type carries that model, or that part number once the Diode plugin's part-number fallback ships (feat: bind a device type by part number when its model matches nothing, without writing to it diode-netbox-plugin#217), the device lands on it. Otherwise a new type named after the part number is created. The old types are left without devices; nothing is deleted.
  • Anything keyed on the old type, such as a config context, custom field value or template, stops applying to those devices.
  • A racked device moving onto a catalog type with a larger u_height can fail NetBox's rack validation until there is room.
  • Targets that set device.model in defaults or override_defaults and are stacks now carry that model on every member.
  • To keep today's name for a product, add a lookup_extensions_dir entry for its sysObjectID, or set device.model.

Not changed

  • The stack path still takes any vendor's chassis model with only the empty-value check, as before. Applying the new guards there would re-type existing stacks, so it is left for a follow-up.
  • Unpinned stacks and every vendor outside the list behave exactly as before.

Testing

  • Unit tests cover the allow-list on whole-arc boundaries, every placeholder, length limits in runes, invalid UTF-8, refused rows, a missing device type, both pin strengths on standalone devices and stacks, and the loader's user-entry rules: copied, repeated, restored and undotted entries.
  • Runner tests drive a target end to end through the mapper for each pin source: policy defaults, a per-target override_defaults, manufacturer-only defaults, a user lookup entry, and a stack pinned both ways.
  • Unit tests also cover the FortiGate rewrite on every recorded value, virtual platforms, the excluded chassis families, the rewrite staying Fortinet-only, the D-Link families against unrecorded ones such as the DES-7200, and synthetic walks of the left-out lines (Juniper, Dell OS10, Extreme, Force10, Huawei, Eltex), which keep the lookup name.
  • Every behaviour is pinned by a mutant the tests kill. Four adversarial review rounds ran before this PR; the last came back clean.
  • go test ./... and golangci-lint run pass in orb-discovery/snmp-discovery.

🤖 Generated with Claude Code

@nbl-ai-review

nbl-ai-review Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

AI Code Review — risk tier: Lite · dimensions run: correctness, security

Advisory only. A human owns the merge decision.

Summary — Net positive. This PR adds Fortinet model rewriting and D-Link family allow-listing to the chassis-model logic for SNMP discovery; the matching logic, guards, and defaults are correct and covered by tests, with no security-relevant concerns in the new code.

No findings.

🤖 AI Code Review · run · prompts: code-review/v1 · models: claude-sonnet-5/medium · context: description · 12 comment(s) · 1 prior AI review(s) · 3/3 thread(s) resolved · force-push detected · usage: $1.72 · 3568k in (96% cached) · 11.9k out

Previous review · 2026-10-07 · 6304d2f

AI Code Review — risk tier: Full · dimensions run: correctness, security

Advisory only. A human owns the merge decision.

Summary — Net-positive change. This is a re-review after three Codex-flagged edge cases (serial-less standalone rows, pinned-stack models with absent sysObjectID, and ambiguous partial-stack chassis rows) were fixed; both correctness and security sub-reviews confirm the fixes and found no new issues.

Resolved since last review

  • orb-discovery/snmp-discovery/mapping/chassis.go:1072 — Standalone model is now read independent of whether the chassis row qualified for serial-dependent stack inventory (fixed in 575dc87).
  • orb-discovery/snmp-discovery/mapping/chassis.go:1130 — A configured defaults.device.model now pins stack devices even when sysObjectID produced no device type (fixed in 4beb604).
  • orb-discovery/snmp-discovery/mapping/chassis.go:1107 — Standalone retyping now requires exactly one eligible chassis row via soleChassisModel/typeByChassis, closing the partial-stack ambiguity (fixed in 6304d2f).

🤖 AI Code Review · run · prompts: code-review/v1 · models: claude-sonnet-5/medium · context: description · 9 comment(s) · 1 prior AI review(s) · 3/3 thread(s) resolved · force-push detected · usage: $2.32 · 4236k in (94% cached) · 20.1k out

Previous review · 2026-09-25 · 308aa6c

AI Code Review — risk tier: Full · dimensions run: correctness, security

Advisory only. A human owns the merge decision.

Summary — Net-positive change. The chassis-model allow-list, pin-precedence rules, and OID-key normalization are well covered by tests, and both dimension reviews came back clean with no findings.

🤖 AI Code Review · run · prompts: code-review/v1 · models: claude-sonnet-5/medium · context: description · 0 comment(s) · 0 prior AI review(s) · usage: $2.01 · 4207k in (96% cached) · 15k out

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Go test coverage

STATUS ELAPSED PACKAGE COVER PASS FAIL SKIP
🟢 PASS 1.05s github.com/netboxlabs/orb-agent/orb-discovery/snmp-discovery/config 86.5% 106 0 0
🟢 PASS 77.02s github.com/netboxlabs/orb-agent/orb-discovery/snmp-discovery/data 87.1% 12217 0 0
🟢 PASS 1.01s github.com/netboxlabs/orb-agent/orb-discovery/snmp-discovery/env 85.7% 15 0 0
🟢 PASS 1.29s github.com/netboxlabs/orb-agent/orb-discovery/snmp-discovery/ingest 87.7% 11 0 0
🟢 PASS 2.34s github.com/netboxlabs/orb-agent/orb-discovery/snmp-discovery/mapping 92.1% 1253 0 0
🟢 PASS 1.06s github.com/netboxlabs/orb-agent/orb-discovery/snmp-discovery/mapping/qbridge 93.2% 108 0 0
🟢 PASS 1.03s github.com/netboxlabs/orb-agent/orb-discovery/snmp-discovery/metrics 85.4% 27 0 0
🟢 PASS 36.37s github.com/netboxlabs/orb-agent/orb-discovery/snmp-discovery/policy 90.3% 381 0 33
🟢 PASS 6.13s github.com/netboxlabs/orb-agent/orb-discovery/snmp-discovery/server 86.4% 21 0 0
🟢 PASS 4.22s github.com/netboxlabs/orb-agent/orb-discovery/snmp-discovery/snmp 91.7% 60 0 0
🟢 PASS 1.04s github.com/netboxlabs/orb-agent/orb-discovery/snmp-discovery/targets 93.2% 33 0 0
🟢 PASS 1.01s github.com/netboxlabs/orb-agent/orb-discovery/snmp-discovery/version 100.0% 1 0 0

Total coverage: 91.3%

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Vulnerability Scan: Passed

Image: orb-agent:scan

No vulnerabilities found.

Commit: 1e74c1d

@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-07T00:57:47.879800Z 8a63d1d Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8482203f7e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread orb-discovery/snmp-discovery/mapping/chassis.go Outdated
@leoparente leoparente self-assigned this Oct 5, 2026
leoparente and others added 6 commits October 6, 2026 20:09
A standalone device kept the device type model from the sysObjectID lookup,
a MIB product name, while every stack member already took its chassis row's
entPhysicalModelName. The chassis row carries the part number a curated
device type records, so the same switch model landed on different types
depending on whether it was stacked, and a standalone device never matched
a catalog type.

A standalone device now takes its chassis model too, keeping the looked-up
manufacturer. An operator-set device model still wins, and an empty,
placeholder, unprintable or over-long value keeps the looked-up model.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Recorded walks show the chassis row's entPhysicalModelName is the
orderable part number for Cisco, HP, Palo Alto Networks, Arista and HPE
Aruba Networking, but a FRU number, OS version, chip or class name for
many other vendors, which would be a worse type name than the lookup's.
A standalone device now takes it only for those vendors, and not when the
row survived only because others were refused.

A model named in a lookup_extensions_dir entry now pins like one set in
defaults or override_defaults, and a pinned model applies to a stack's
master and members too, which previously took their chassis rows' models
regardless. More firmware and class placeholders are refused. The backend
docs describe the chassis row in the model precedence.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…only by defaults

The docs seed lookup_extensions_dir by copying the bundled files, which
flagged every copied entry as the operator's own, so the chassis model was
never taken and pinned stacks collapsed onto the master's MIB name. A user
entry now pins only when it changes or adds a model, and its key is
normalised to the bundled spelling so an undotted key replaces the bundled
entry instead of sitting beside it.

A lookup entry pins a standalone device only; stack members keep their
own chassis models as before. A defaults or override_defaults model pins
the whole stack, when the mapper built a device type to carry it. HP is
narrowed to the ProCurve and ArubaOS-Switch arc, the only one the recorded
walks cover. The docs name the exact arcs, the stack rules and the
precedence, and advise copying only the files you change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…talog only

A user entry was compared with whatever the map held, earlier user files
included, so the same model repeated in a second file stopped pinning,
and a later file restoring the bundled model kept pinning it. Entries are
now compared with a snapshot of the bundled catalog taken before any user
file loads. When one file spells an OID both with and without the leading
dot, the dotted entry wins deterministically and counts once.

The runner reads the sysObjectID it already extracted instead of a second
exported reader.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ey spelling

A compile-time check keeps the production device lookup answering whether
a model is the operator's, so renaming the method cannot silently drop the
pin, and a test keeps every bundled key dotted, which the user-entry
comparison relies on. The user flag's comment says what now sets it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t rules

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@leoparente
leoparente force-pushed the fix/snmp-standalone-chassis-model branch from 8482203 to ec35923 Compare October 6, 2026 23:12
…orts no serial

The inventory drops a chassis row with an empty serial, since serials
identify stack members, so a standalone device whose one chassis row
named its part but no serial kept the lookup model. With no inventory
member, the sole chassis row now names the part under the same rules:
one root row, an allow-listed vendor and no pinned model.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 575dc87a21

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread orb-discovery/snmp-discovery/mapping/chassis.go Outdated
…sysObjectID

The mapper built a device type only from the sysObjectID lookup, so a
model and manufacturer pinned in defaults went unused without one, and
the stack pin, waiting on that type, let the chassis rows name the
stack. Defaults setting both now build the type, and a defaults model
pins a stack whether or not a type could be built.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4beb604b7c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread orb-discovery/snmp-discovery/mapping/chassis.go Outdated
… when it has one

The inventory drops a chassis row with no serial without counting it
as refused, so a stack seen with one serial-less member looked
standalone and took the other member's model. Both standalone paths now
take the chassis model only when exactly one chassis row is reported.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: 6304d2fbe9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@leoparente

Copy link
Copy Markdown
Contributor Author

/ai-review

…lies by their chassis model

Recorded FortiGate, FortiWiFi and FortiGate Rugged walks report the
orderable model with underscores (FGT_60F); rewritten into Fortinet's
form (FG-60F) they match catalog part numbers, where the lookup names
fgt60F, or the 60F as fw60EI. Virtual FortiGates, which report their
platform, and the 6000F, 7000E and 7000F chassis systems, whose one
sysObjectID covers every chassis size, keep the lookup. The D-Link
DGS families recorded report the model as sold, where the lookup has
no entry and the raw sysObjectID was the model; other D-Link families,
such as the DES-7200, are left out until recorded.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@leoparente

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: 8a63d1db26

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@leoparente

Copy link
Copy Markdown
Contributor Author

/ai-review

@leoparente
leoparente marked this pull request as ready for review October 7, 2026 13:23
@leoparente
leoparente requested a review from jajeffries as a code owner October 7, 2026 13:23
@nbl-ai-review

nbl-ai-review Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

🔁 AI Code Review updated for 8a63d1db — see the review · 0 resolved · 0 open (run)

@leoparente
leoparente merged commit 8147c15 into develop Oct 7, 2026
26 checks passed
@leoparente
leoparente deleted the fix/snmp-standalone-chassis-model branch October 7, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants