Introduction to NetBox/Nautobot Refactoring
NetBox and its fork Nautobot are open‑source IPAM/DCIM platforms built on Django. They provide a relational model for network objects such as devices, device types, sites, racks, interfaces, IP addresses, VLANs, VRFs, and circuits. Core features include:
- RESTful API (GraphQL in Nautobot) for CRUD operations
- Web UI for visual validation and manual editing
- Change‑logging and object‑level permissions
- Extensibility via custom fields, relationships, and validation plugins
- Built‑in synchronization with external sources (Ansible, Terraform, SNMP)
Both platforms store data in PostgreSQL, enforce referential integrity, and expose a consistent object model that can be version‑controlled via migrations.
Benefits of Refactoring Network Inventory
Moving from per‑device YAML files with freeform variables to a centralized NetBox/Nautobot model yields:
- Single source of truth – eliminates drift between disparate YAML files.
- Queryability – complex filters become simple API calls or UI filters.
- Relationship integrity – inter‑device links, power feeds, and circuit terminations are enforced as first‑class relationships.
- Change management – every create/update/delete is logged; diff views show revisions.
- Automation friendliness – LLMs, operators, and CI pipelines can invoke the same API used by humans.
- Scalability – indexed tables support tens of thousands of objects with sub‑second lookups.
- Validation – built‑in validators catch errors at ingest time (e.g., IP overlap, VLAN ID range).
Understanding Current Network Inventory
YAML Device Configuration
Each device is described by a standalone YAML file named after its hostname (e.g., leaf01.yaml). A typical file:
hostname: leaf01
role: leaf
site: dc1
rack: R12
position: 20
vendor: arista
model: vEOS
os_version: 4.27.2F
interfaces:
- name: Ethernet1
description: uplink-to-spine1
enabled: true
speed: 10000
mode: routed
ip_addresses:
- 10.0.0.1/31
- name: Ethernet2
description: server‑face
enabled: true
speed: 1000
mode: access
vlans:
- 10
Freeform variables (e.g., bgp_asn, loopback_ip) are often added at the bottom without a schema, relying on naming conventions (*_loopback, *_mgmt) for interpretation.
Freeform Variables and Naming Conventions
Operators embed semantics in variable names:
loopback_ip→ /32 address used for router‑ID.mgmt_ip→ out‑of‑band management address.uplink_*→ interfaces facing spine or core.vrf_*→ VRF name associated with a set of interfaces.
These conventions live only in tribal knowledge or scattered README files, making onboarding error‑prone.
Limitations of Current Approach
- No referential integrity – VLAN IDs cannot be automatically cross‑checked.
- Duplication – site, rack, device type repeated across files → inconsistency on rename.
- Limited querying – finding all devices with a VLAN requires grepping hundreds of files.
- Fragile change propagation – updating a device type model needs manual edits in every referencing file.
- No built‑in audit – reliance on external Git history, which may miss automated updates.
- Scaling pain – parsing 10 000 YAML files on each refresh can take minutes and consume significant CPU.
NetBox/Nautobot Object Modeling
Devices and Device Types
- Device Type – specific hardware/virtual model; fields:
model,vendor,u_height,is_full_depth, optionalsubdevice_role. Custom fields can capture ASIC type, firmware version, etc. - Device – instance of a Device Type tied to a site, rack, and position. Key fields:
name,device_type,site,rack,position,face,status,primary_ip4,primary_ip6,tenant,custom_field_data.
Example API calls
# Create device type for Arista vEOS
curl -s -X POST "$NETBOX_URL/api/dcim/device-types/" \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "vEOS",
"vendor": {"name": "Arista"},
"u_height": 1,
"is_full_depth": false
}' | jq .
# Obtain the newly created device type ID
DT_ID=$(curl -s -H "Authorization: Token $TOKEN" "$NETBOX_URL/api/dcim/device-types/?model=vEOS" | jq -r '.results[0].id')
# Create the device
curl -s -X POST "$NETBOX_URL/api/dcim/devices/" \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"leaf01\",
\"device_type\": $DT_ID,
\"site\": {\"name\": \"dc1\"},
\"rack\": {\"name\": \"R12\"},
\"position\": 20,
\"status\": \"active\"
}" | jq .
Sites and Locations
- Site – top‑level container (e.g., data‑center campus). Fields:
name,slug,region,tenant,status,custom_field_data. - Location – hierarchical subdivision under a Site (building → floor → room). Supports nested locations via
parent_location.
Example: rack inside a location
# Assume site dc1 already exists (ID=1)
LOC_ID=$(curl -s -H "Authorization: Token $TOKEN" "$NETBOX_URL/api/dcim/locations/?name=BuildingA&site=dc1" | jq -r '.results[0].id')
RACK_ID=$(curl -s -H "Authorization: Token $TOKEN" "$NETBOX_URL/api/dcim/racks/?name=R12&site=dc1&location=$LOC_ID" | jq -r '.results[0].id')
VLANs and VRFs
- VLAN – stores
vid,name,site(ortenant),status, optionalrole. Can be scoped globally or per‑site. - VRF – Layer‑3 routing context with fields
name,rd(route distinguisher),tenant,enforced,export_targets,import_targets.
Example: create VLAN 10 for site dc1
SITE_ID=$(curl -s -H "Authorization: Token $TOKEN" "$NETBOX_URL/api/dcim/sites/?name=dc1" | jq -r '.results[0].id')
curl -s -X POST "$NETBOX_URL/api/ipam/vlans/" \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"vid\": 10,
\"name\": \"users\",
\"site\": $SITE_ID,
\"status\": \"active\"
}" | jq .
IPAM and Prefixes
- Prefix – IPv4/IPv6 network (
network,prefix_length). Scopable to site, VRF, or tenant. - IPAddress – individual address (
address,status,assigned_object_type,assigned_object_id).
Example: allocate a /30 prefix and assign first address to an interface
# Create prefix 10.0.0.0/30 in VRF default (VRF ID 1)
PREFIX_ID=$(curl -s -H "Authorization: Token $TOKEN" "$NETBOX_URL/api/ipam/prefixes/?network=10.0.0.0/30" | jq -r '.results[0].id')
# Assign first usable address to interface Ethernet1 of leaf01 (assume interface ID 42)
curl -s -X POST "$NETBOX_URL/api/ipam/ip-addresses/" \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"address\": \"10.0.0.1/31\",
\"status\": \"active\",
\"assigned_object_type\": \"dcim.interface\",
\"assigned_object_id\": 42
}" | jq .
Relationships and Derived Intent
Device Interconnections and Interfaces
- Interface – belongs to a Device; fields:
name,type,enabled,mgmt_only,mode(access/trunk/routed),speed,duplex,mtu. - Cable – physical/virtual link between two interfaces, terminations, or power feeds. Fields:
type,status,label,length,length_unit,termination_a_type,termination_a_id,termination_b_type,termination_b_id.
Example: cable between leaf01:Ethernet1 and spine1:Ethernet5
# Get interface IDs
LEAF_IF_ID=$(curl -s -H "Authorization: Token $TOKEN" "$NETBOX_URL/api/dcim/interfaces/?device=leaf01&name=Ethernet1" | jq -r '.results[0].id')
SPINE_IF_ID=$(curl -s -H "Authorization: Token $TOKEN" "$NETBOX_URL/api/dcim/interfaces/?device=spine1&name=Ethernet5" | jq -r '.results[0].id')
# Create cable
curl -s -X POST "$NETBOX_URL/api/dcim/cables/" \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"termination_a_type\": \"dcim.interface\",
\"termination_a_id\": $LEAF_IF_ID,
\"termination_b_type\": \"dcim.interface\",
\"termination_b_id\": $SPINE_IF_ID,
\"type\": \"cat6a\",
\"status\": \"connected\",
\"length\": 5,
\"length_unit\": \"m\"
}" | jq .
Power and Network Feeds
- PowerFeed – models a power circuit feeding a device or rack. Fields:
name,supply(AC/DC),voltage,amperage,max_utilization,type(primary/redundant),status. - PowerOutlet / PowerPort – consumable power sockets on devices; linked via
PowerCableto a PowerFeed.
Example: assign a redundant power feed to a rack
# Assume power feed PF-R12-A already exists (ID=7)
curl -s -X POST "$NETBOX_URL/api/dcim/power-outlets/" \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"Outlet1\",
\"device\": {\"name\": \"leaf01\"},
\"type\": \"iec-60320-c13\",
\"feed\": 7
}" | jq .
Intent-Based Networking and Validation
NetBox/Nautobot can store derived intent as computed properties or via Custom Fields and Validation Rules.
- Intent example: “All leaf interfaces facing servers must be in access mode and belong to VLAN 10‑20.”
- Implementation: create a Custom Field
intent_server_face(boolean) on Interface, then run a validation job/webhook on interface update:
# Pseudocode for a validation job (Nautobot)
def validate_interface(intent):
if intent.custom_field_data.get('intent_server_face'):
if intent.mode != 'access':
raise ValidationError("Server‑facing intent requires access mode")
if not any(v.vid in range(10,21) for v in intent.tagged_vlans.all()):
raise ValidationError("Server‑facing intent must have VLAN 10‑20 tagged")
The job runs in the background, flags violations in the UI, and can block status changes via Object Change hooks.
First‑Class Modeling vs Generated Facts
Identifying Key Network Attributes
Attributes that benefit from first‑class modeling
- Identity: hostname, device type, site, rack position.
- Logical grouping: VLAN, VRF, tenant, role.
- Physical termination: interface, cable, power feed, power outlet.
- Addressing: IP address, prefix, MAC address (via
Interface.mac_address). - Operational status: device status, interface enabled, cable status.
Attributes that can remain generated (derived on‑the‑fly)
- Port‑channel members (derived from LACP config; optionally stored as a Custom Field).
- BGP peer relationships (derived from interface IP + configured ASN; store as Custom Fields only if needed for reporting).
- Interface description templates (generated from naming convention, e.g.,
uplink-to-<peer-hostname>). - Utilization metrics (pulled from monitoring systems, not stored in NetBox).
Device and Interface Attributes
| Attribute | First‑Class? | Reason |
|---|---|---|
device.name | Yes | Primary key for lookup |
device.device_type | Yes | Enforces model constraints (e.g., console port count) |
device.site | Yes | Enables site‑scoped reports |
device.rack & position | Yes | Physical placement, power/cooling calculations |
device.custom_field_data (e.g., bgp_asn) | Conditional | Promote to CF if frequently queried; otherwise generate |
interface.name | Yes | Unique per device |
interface.type | Yes | Determines speed, duplex, supported modules |
interface.enabled | Yes | Operational state |
interface.mode (access/trunk/routed) | Yes | Drives VLAN handling |
interface.mac_address | Yes | Needed for L2/L3 forwarding, ACLs, and change‑of‑authentication checks |
End of document.