feat(dcim): support NetBox device-type and module-type imports - #76
Draft
petercrocker wants to merge 1 commit into
Draft
feat(dcim): support NetBox device-type and module-type imports#76petercrocker wants to merge 1 commit into
petercrocker wants to merge 1 commit into
Conversation
Deploying schema-library with
|
| Latest commit: |
b8df83d
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://24559ea3.schema-library.pages.dev |
| Branch Preview URL: | https://feat-netbox-import-module-ba.schema-library.pages.dev |
petercrocker
force-pushed
the
feat-netbox-import-module-bays
branch
3 times, most recently
from
August 4, 2026 10:23
301918a to
0e14203
Compare
Importing the NetBox devicetype-library into Infrahub loses most of what
a modular chassis and a module type describe, and a batch of module types
fails to load outright. Verified end to end against a live instance with
three device types and five module types, including a modular chassis and
its supervisor, line card, fabric module, PSU and a module with bays.
base/dcim.yml
Enable generate_template on DcimDevice. Template* kinds only exist
where a node declares it, so object templates could not be used at all
without editing the library. Additive for anyone who never creates a
template.
extensions/module_bay (new)
Module bays are the physical slots of a chassis, as distinct from the
modules installed into them. extensions/modules already models the
installed module, keyed by a serial number a device type cannot
supply; nothing modelled the slot. Without a bay node a modular
chassis converts to an empty object template - a DCS-7508N has 24 bays
and zero interfaces of its own, so every port is lost.
position is Text, not Number: bay positions are free-form, and the
DCS-7508N alone uses 'F1'..'F6' and 'PSU-1'..'PSU-8' alongside
'1'..'10'.
The NetBox label lands in bay_label, NOT label. Infrahub auto-populates
an attribute literally named label from name when unset, and title-cases
it, so an unlabelled bay comes back labelled with its own name and
'no label' becomes indistinguishable from 'label equals name'. On a
DCS-7508N only 10 of 24 bays carry one.
The Parent side peers DcimGenericDevice because human_friendly_id
traverses device__name__value and name is not declared on
DcimPhysicalDevice. The Component side sits on DcimPhysicalDevice so
DcimVirtualDevice does not inherit slots it cannot have.
extensions/module_port (new)
The ports a module provides, as declared by its module type - what
NetBox lists under interfaces, console-ports and power-ports.
These deliberately are not DcimInterface. DcimInterface.device is a
mandatory Parent, and Infrahub requires relationships used in a
uniqueness constraint to be mandatory, so relaxing it fails with
"cannot use device relationship, relationship must be mandatory" and
would break the device__name__value human_friendly_id too. A
DeviceModulePort is a declaration parented by the module, carrying
name, category, the NetBox type slug, mgmt_only and maximum_draw.
port_type is Text, not Dropdown: NetBox uses well over a hundred type
slugs across the three lists, and a Dropdown fails the load on every
slug not enumerated.
Port names keep NetBox's {module} token verbatim - a template is not
bound to a bay, so it cannot be resolved at import time. Substituting
it and creating the real device interfaces is a generator step once the
module is installed; the traversal it needs is documented in the file.
extensions/modules/modules.yml
- Drop unique from DeviceGenericModuleType.part_number. Many published
module types carry no part number, and Infrahub counts repeated
nulls as a uniqueness violation, so the second part-number-less
module type in a batch fails with "Violates uniqueness constraint
'part_number'". Three of five module types in the test set hit this.
Identity is still guaranteed by the name+manufacturer constraint.
- Add weight_grams. Infrahub has no float attribute kind, so a weight
is a whole number or nothing, and modules are exactly the light
hardware that integer kilograms destroy: a transceiver or supervisor
rounds to 0 kg, which reads as data rather than as a missing value.
experimental/modules_linecards/linecard.yml
- Enable generate_template on DeviceLinecard, so a module type can be
imported as a reusable blueprint. The unique serial_number inherited
from DeviceGenericModule is not carried onto the generated template
at all - Infrahub keys it on template_name instead.
- DeviceLinecard.slot becomes optional. A NetBox module type describes
a model rather than an installed card and carries no slot, so a
mandatory slot made every imported module type unloadable.
Nothing is removed. DcimDeviceType.weight is left as Number: importers
round to whole kilograms. Docs are not regenerated here, matching
d5ae01b and a7ccf78.
petercrocker
force-pushed
the
feat-netbox-import-module-bays
branch
from
August 6, 2026 11:34
0e14203 to
b8df83d
Compare
Contributor
|
@petercrocker I completely revisited the device module extension here https://github.com/opsmill/schema-library/pull/75/changes#diff-3388ec71d8a9c2c95e93b3f50658e4fb0de13acc38cdc98e3908738c435e3b0d |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Importing the NetBox devicetype-library into Infrahub loses most of what a modular chassis and a module type describe, and a batch of module types fails to load outright.
Changes
All additive or relaxing. Nothing is removed.
base/dcim.ymlEnable
generate_templateonDcimDevice.Template*kinds only exist where a node declares it, so object templates could not be used without editing the library. Additive for anyone who never creates a template.DcimDeviceType.weightis deliberately left asNumber— see the notes below.extensions/module_bay(new)Module bays are the physical slots of a chassis, as distinct from the modules installed into them.
extensions/modulesalready models the installed module, keyed by aserial_numberthat a device type cannot supply — nothing modelled the slot. Without a bay node a modular chassis converts to an empty object template: a DCS-7508N has 24 bays and zero interfaces of its own, so every port is lost.bay_label, notlabel. Infrahub auto-populates an attribute literally namedlabelfromnamewhen it is unset — and title-cases it. An unlabelled bay comes back labelled with its own name (Fabric Module Slot 1), andLabelTest BaybecomesLabeltest Bay, so "NetBox supplied no label" becomes indistinguishable from "label equals name". On a DCS-7508N only 10 of 24 bays carry one (Supervisor,Line Card). Verified: withbay_labelthe unlabelled bays correctly read backNone.positionisText, notNumber. Bay positions are free-form; the DCS-7508N alone usesF1–F6andPSU-1–PSU-8alongside1–10. ANumberattribute rejects 14 of its 24 bays.Parentside peersDcimGenericDevicebecausehuman_friendly_idtraversesdevice__name__value, andnameis not declared onDcimPhysicalDevice(peering there fails validation outright). TheComponentside sits onDcimPhysicalDevicesoDcimVirtualDevicedoes not inherit slots it cannot have. Both anchorings validate; only this one gives the right inherited set.extensions/module_port(new)The ports a module provides, as declared by its module type — what NetBox lists under
interfaces,console-portsandpower-ports. Previously these had nowhere to go at all.These are deliberately not
DcimInterface. I tested that first:DcimInterface.deviceis a mandatoryParent, and Infrahub requires relationships used in a uniqueness constraint to be mandatory, so relaxing it so an interface could hang off a module fails outright:Every interface kind is keyed
[device, name__value]with adevice__name__valueHFID, so that route means dismantling interface identity schema-wide.A
DeviceModulePortis instead a declaration parented by the module:name,category(interface/console/power/front/rear),port_type,mgmt_only,maximum_draw. One typed collection onDeviceGenericModule.portsrather than five parallel relationships.port_typeisText, notDropdown. NetBox uses well over a hundred type slugs across the three lists; aDropdownfails the load on every slug not enumerated.{module}token verbatim. A template is not bound to a bay, so the token cannot be resolved at import time. Substituting it and creating the real device interfaces is a generator step once the module is installed — the traversal a generator needs is documented in the file, and is verified to exist end to end (DcimDevice.modules→DeviceLinecard.module_bay.position+.ports→DcimDevice.interfaces).extensions/modules/modules.ymlDrop
uniquefromDeviceGenericModuleType.part_number. A hard blocker on bulk import. Many published module types carry no part number, and Infrahub counts repeated nulls as a uniqueness violation, so the second part-number-less module type in any batch fails:Three of five module types in the test set hit this. Identity is still guaranteed by the existing
name__value+manufacturerconstraint.Add
weight_grams(Number). Infrahub has no float attribute kind, so a weight is a whole number or nothing — and modules are exactly the light hardware that integer kilograms destroy. A transceiver or supervisor rounds to0kg, which reads as data rather than as a missing value.experimental/modules_linecards/linecard.ymlgenerate_templateonDeviceLinecard, so a module type can be imported as a reusable blueprint. The uniqueserial_numberinherited fromDeviceGenericModuleis not carried onto the generated template at all — Infrahub keys it ontemplate_nameinstead. Verified:serial_numberis not even queryable onTemplateDeviceLinecard.DeviceLinecard.slotbecomes optional. A NetBox module type describes a model rather than an installed card and carries no slot, so a mandatory slot made every imported module type unloadable.Verification
Schema loaded onto a branch of a live Infrahub instance (1.10.6), then three device types and five module types converted and loaded — all five generated object files, zero errors:
The module set is deliberately varied: a supervisor, a 36-port line card, a fabric module with no components, a PSU using NetBox 4.3
profile/attribute_data, and a module that itself has bays. All 24 bay positions round-trip intact including the non-numeric ones.yamllint,markdownlintandfind_cycles.pyclean.Notes for reviewers
uniquedoes not take effect on an already-loaded branch. Re-loading the corrected schema onto a branch that already hadunique: trueleft the constraint in place; only a branch created fresh picked it up. Not a problem for this PR (the schema ships correct), but worth knowing for anyone migrating an existing instance.DcimDeviceType.weightis untouched, but it has a known sharp edge. With no float kind, importers round to whole kilograms — which rounds roughly 300 published device types (sub-500 g transceivers and access points) to0, and a zero looks like data. Fixing it properly means aweight_gramsattribute onDcimDeviceTypetoo, mirroring what this adds for modules. Left out to avoid two competing weight attributes in the core schema without a maintainer decision; happy to add it.invoke docs.generateonmainalso surfaces unrelated pre-existing drift (sfp,security,lag,mlag, …) which felt wrong to fold in.