23 KiB
NetBox Data Models and Relationships
NetBox Version: 4.3.0
Comprehensive guide to NetBox's data models, their relationships, and how they map to the Matrix cluster infrastructure in Virgo-Core.
Table of Contents
- Overview
- Core Data Models
- Model Relationships
- DCIM Models (Data Center)
- IPAM Models (IP Management)
- Virtualization Models
- Matrix Cluster Example
- Best Practices
Overview
NetBox organizes infrastructure data into logical models across several applications:
| Application | Purpose | Key Models |
|---|---|---|
| DCIM | Data Center Infrastructure | Site, Rack, Device, Interface, Cable |
| IPAM | IP Address Management | IP Address, Prefix, VLAN, VRF |
| Virtualization | Virtual Machines | Virtual Machine, Cluster, VM Interface |
| Circuits | WAN/Circuit Management | Circuit, Provider, Circuit Termination |
| Tenancy | Multi-tenant Support | Tenant, Tenant Group, Contact |
| Extras | Extensions | Tag, Custom Field, Webhook |
Core Data Models
Site
Represents a physical location containing infrastructure.
Fields:
name- Display name (e.g., "Matrix Cluster")slug- URL-friendly identifier (e.g., "matrix")status- active, planned, retired, etc.region- Geographic region (optional)description- Purpose and detailstags- Flexible categorization
Example:
site = nb.dcim.sites.create(
name="Matrix Cluster",
slug="matrix",
status="active",
description="3-node Proxmox VE cluster (foxtrot, golf, hotel)",
tags=[{"name": "proxmox"}, {"name": "homelab"}]
)
Relationships:
- Has many: Racks, Devices, Prefixes
- Belongs to: Region (optional)
Rack
Physical rack within a site.
Fields:
name- Rack identifiersite- Parent siteu_height- Units (typically 42U)desc_units- Count units top-downwidth- 19" or 23"tags
Example:
rack = nb.dcim.racks.create(
name="Rack-01",
site=site.id,
u_height=42,
width=19
)
Relationships:
- Belongs to: Site
- Has many: Devices (mounted in rack)
Device
Physical piece of equipment.
Fields:
name- Device hostname (e.g., "foxtrot")device_type- Reference to device typedevice_role- Purpose (server, switch, etc.)site- Physical locationrack- Optional rack locationposition- Rack unit positionstatus- active, offline, planned, etc.primary_ip4- Primary IPv4 addressprimary_ip6- Primary IPv6 addresstags
Example:
device = nb.dcim.devices.create(
name="foxtrot",
device_type=device_type.id,
device_role=role.id,
site=site.id,
status="active",
tags=[{"name": "proxmox-node"}]
)
Relationships:
- Belongs to: Site, Rack, Device Type, Device Role
- Has many: Interfaces, Console Ports, Power Ports
- Has one: Primary IP4, Primary IP6
Interface
Network interface on a device.
Fields:
device- Parent devicename- Interface name (e.g., "eth0", "enp1s0")type- Physical type (1000base-t, 10gbase-x, etc.)enabled- Administrative statusmtu- Maximum transmission unitmac_address- MAC addressmode- Access or Trunk (for VLANs)untagged_vlan- Native VLANtagged_vlans- Tagged VLANstags
Example:
interface = nb.dcim.interfaces.create(
device=device.id,
name="enp1s0",
type="10gbase-x-sfpp",
enabled=True,
mtu=9000, # Jumbo frames for CEPH
tags=[{"name": "ceph-public"}]
)
Relationships:
- Belongs to: Device
- Has many: IP Addresses (assigned to interface)
- Connected to: Cable (physical connection)
Cable
Physical cable connection between interfaces.
Fields:
a_terminations- End A (interface, console port, etc.)b_terminations- End Btype- Cable type (cat6, fiber, dac, etc.)status- connected, planned, etc.length- Cable lengthlength_unit- m, ft, etc.color- Cable colortags
Example:
cable = nb.dcim.cables.create(
a_terminations=[{"object_type": "dcim.interface", "object_id": iface1.id}],
b_terminations=[{"object_type": "dcim.interface", "object_id": iface2.id}],
type="dac-active",
status="connected",
length=3,
length_unit="m"
)
Relationships:
- Connects: Two termination objects (interfaces, ports, etc.)
IP Address
IPv4 or IPv6 address.
Fields:
address- IP with CIDR (e.g., "192.168.3.5/24")dns_name- FQDN (e.g., "foxtrot.spaceships.work")status- active, reserved, deprecated, etc.role- loopback, secondary, anycast, etc.assigned_object_type- Interface type (dcim.interface or virtualization.vminterface)assigned_object_id- Interface IDvrf- Virtual routing and forwarding instancetenant- Tenant assignmenttags
Example:
ip = nb.ipam.ip_addresses.create(
address="192.168.3.5/24",
dns_name="foxtrot.spaceships.work",
status="active",
assigned_object_type="dcim.interface",
assigned_object_id=interface.id,
tags=[{"name": "production-dns"}]
)
Relationships:
- Belongs to: Prefix, VRF (optional)
- Assigned to: Interface (device or VM)
- Referenced by: Device (as primary IP)
Prefix
IP network or subnet.
Fields:
prefix- Network in CIDR (e.g., "192.168.3.0/24")status- active, reserved, deprecated, etc.role- Purpose (e.g., "management", "ceph-public")site- Physical locationvrf- VRF assignmentvlan- Associated VLANis_pool- Allow automatic IP assignmentdescriptiontags
Example:
prefix = nb.ipam.prefixes.create(
prefix="192.168.3.0/24",
status="active",
role=nb.ipam.roles.get(slug='management').id,
site=site.id,
is_pool=True,
description="Management network for Matrix cluster",
tags=[{"name": "proxmox-mgmt"}]
)
Relationships:
- Belongs to: Site, VRF, VLAN (optional)
- Contains: IP Addresses
- Hierarchical: Can contain child prefixes
VLAN
Virtual LAN.
Fields:
vid- VLAN ID (1-4094)name- VLAN namesite- Site assignmentgroup- VLAN group (optional)status- active, reserved, deprecatedrole- Purposedescriptiontags
Example:
vlan = nb.ipam.vlans.create(
vid=9,
name="Corosync",
site=site.id,
status="active",
description="Proxmox corosync cluster communication",
tags=[{"name": "proxmox-cluster"}]
)
Relationships:
- Belongs to: Site, VLAN Group
- Assigned to: Prefixes, Interfaces
VRF
Virtual Routing and Forwarding instance.
Fields:
name- VRF namerd- Route distinguisher (optional)descriptionenforce_unique- Enforce unique IP addressingtags
Example:
vrf = nb.ipam.vrfs.create(
name="management",
enforce_unique=True,
description="Management VRF"
)
Relationships:
- Has many: Prefixes, IP Addresses
Virtual Machine
VM in a virtualization cluster.
Fields:
name- VM hostnamecluster- Virtualization clusterrole- VM role (optional)status- active, offline, planned, etc.vcpus- Virtual CPU countmemory- Memory in MBdisk- Disk in GBprimary_ip4- Primary IPv4primary_ip6- Primary IPv6descriptiontags
Example:
vm = nb.virtualization.virtual_machines.create(
name="docker-01",
cluster=cluster.id,
status="active",
vcpus=4,
memory=8192, # 8 GB
disk=100, # 100 GB
tags=[{"name": "docker"}, {"name": "production"}]
)
Relationships:
- Belongs to: Cluster, Role (optional)
- Has many: VM Interfaces
- Has one: Primary IP4, Primary IP6
Cluster
Virtualization cluster (e.g., Proxmox, VMware).
Fields:
name- Cluster nametype- Cluster typesite- Physical locationdescriptiontags
Example:
cluster_type = nb.virtualization.cluster_types.get(slug='proxmox')
cluster = nb.virtualization.clusters.create(
name="Matrix",
type=cluster_type.id,
site=site.id,
description="3-node Proxmox VE 9.x cluster",
tags=[{"name": "production"}]
)
Relationships:
- Belongs to: Site, Cluster Type
- Has many: Virtual Machines
VM Interface
Network interface on a virtual machine.
Fields:
virtual_machine- Parent VMname- Interface name (e.g., "eth0")type- Interface type (virtual, bridge)enabled- Administrative statusmtu- MTUmac_address- MAC addressuntagged_vlan- Native VLANtagged_vlans- Tagged VLANstags
Example:
vm_interface = nb.virtualization.interfaces.create(
virtual_machine=vm.id,
name="eth0",
type="virtual",
enabled=True,
mtu=1500
)
Relationships:
- Belongs to: Virtual Machine
- Has many: IP Addresses
Model Relationships
Hierarchical Relationships
Region (optional)
└── Site
├── Rack
│ └── Device
│ └── Interface
│ └── IP Address
├── Cluster
│ └── Virtual Machine
│ └── VM Interface
│ └── IP Address
└── Prefix
└── IP Address
Key Relationships
Site containment:
Site
├── has many Racks
├── has many Devices
├── has many Clusters
├── has many Prefixes
└── has many VLANs
Device structure:
Device
├── belongs to Site
├── belongs to Rack (optional)
├── belongs to Device Type
├── belongs to Device Role
├── has many Interfaces
├── has one Primary IP4 (optional)
└── has one Primary IP6 (optional)
Interface connectivity:
Interface
├── belongs to Device
├── has many IP Addresses
├── connected via Cable
├── assigned to VLAN(s)
└── assigned object for IP
IP Address assignment:
IP Address
├── belongs to Prefix
├── assigned to Interface (device or VM)
├── belongs to VRF (optional)
└── referenced as Primary IP by Device/VM
VM structure:
Virtual Machine
├── belongs to Cluster
├── has many VM Interfaces
├── has one Primary IP4 (optional)
└── has one Primary IP6 (optional)
IPAM hierarchy:
VRF (optional)
└── Prefix
├── child Prefix (nested)
└── IP Address
DCIM Models (Data Center)
Complete Device Example
Creating a complete device with interfaces and IPs:
# 1. Create device type (if not exists)
manufacturer = nb.dcim.manufacturers.get(slug='minisforum')
if not manufacturer:
manufacturer = nb.dcim.manufacturers.create(name='MINISFORUM', slug='minisforum')
device_type = nb.dcim.device_types.get(slug='ms-a2')
if not device_type:
device_type = nb.dcim.device_types.create(
manufacturer=manufacturer.id,
model='MS-A2',
slug='ms-a2'
)
# 2. Create device role
role = nb.dcim.device_roles.get(slug='proxmox-node')
if not role:
role = nb.dcim.device_roles.create(
name='Proxmox Node',
slug='proxmox-node',
color='2196f3'
)
# 3. Create device
device = nb.dcim.devices.create(
name='foxtrot',
device_type=device_type.id,
device_role=role.id,
site=site.id,
status='active',
tags=[{'name': 'proxmox-node'}, {'name': 'ceph-node'}]
)
# 4. Create management interface
mgmt_iface = nb.dcim.interfaces.create(
device=device.id,
name='enp2s0',
type='2.5gbase-t',
enabled=True,
mtu=1500,
description='Management interface'
)
# 5. Assign management IP
mgmt_ip = nb.ipam.ip_addresses.create(
address='192.168.3.5/24',
dns_name='foxtrot.spaceships.work',
status='active',
assigned_object_type='dcim.interface',
assigned_object_id=mgmt_iface.id,
tags=[{'name': 'production-dns'}]
)
# 6. Set as primary IP
device.primary_ip4 = mgmt_ip.id
device.save()
# 7. Create CEPH public interface
ceph_pub_iface = nb.dcim.interfaces.create(
device=device.id,
name='enp1s0f0',
type='10gbase-x-sfpp',
enabled=True,
mtu=9000,
description='CEPH public network'
)
# 8. Assign CEPH public IP
ceph_pub_ip = nb.ipam.ip_addresses.create(
address='192.168.5.5/24',
dns_name='foxtrot-ceph-pub.spaceships.work',
status='active',
assigned_object_type='dcim.interface',
assigned_object_id=ceph_pub_iface.id
)
# 9. Create CEPH private interface
ceph_priv_iface = nb.dcim.interfaces.create(
device=device.id,
name='enp1s0f1',
type='10gbase-x-sfpp',
enabled=True,
mtu=9000,
description='CEPH private network'
)
# 10. Assign CEPH private IP
ceph_priv_ip = nb.ipam.ip_addresses.create(
address='192.168.7.5/24',
status='active',
assigned_object_type='dcim.interface',
assigned_object_id=ceph_priv_iface.id
)
IPAM Models (IP Management)
Complete IPAM Example
Setting up IPAM for Matrix cluster:
# 1. Create VRF (optional but recommended)
vrf_mgmt = nb.ipam.vrfs.create(
name='management',
enforce_unique=True,
description='Management VRF'
)
# 2. Create prefix role
role_mgmt = nb.ipam.roles.get(slug='management')
if not role_mgmt:
role_mgmt = nb.ipam.roles.create(
name='Management',
slug='management'
)
# 3. Create management prefix
prefix_mgmt = nb.ipam.prefixes.create(
prefix='192.168.3.0/24',
status='active',
role=role_mgmt.id,
site=site.id,
vrf=vrf_mgmt.id,
is_pool=True,
description='Management network for Matrix cluster'
)
# 4. Create CEPH public prefix
role_storage = nb.ipam.roles.create(name='Storage', slug='storage')
prefix_ceph_pub = nb.ipam.prefixes.create(
prefix='192.168.5.0/24',
status='active',
role=role_storage.id,
site=site.id,
is_pool=True,
description='CEPH public network (MTU 9000)'
)
# 5. Create CEPH private prefix
prefix_ceph_priv = nb.ipam.prefixes.create(
prefix='192.168.7.0/24',
status='active',
role=role_storage.id,
site=site.id,
is_pool=True,
description='CEPH private network (MTU 9000)'
)
# 6. Create Corosync VLAN
vlan_corosync = nb.ipam.vlans.create(
vid=9,
name='Corosync',
site=site.id,
status='active',
description='Proxmox cluster communication'
)
# 7. Create Corosync prefix
prefix_corosync = nb.ipam.prefixes.create(
prefix='192.168.8.0/24',
status='active',
site=site.id,
vlan=vlan_corosync.id,
description='Corosync cluster network (VLAN 9)'
)
# 8. Get available IPs from prefix
available_ips = prefix_mgmt.available_ips.list()
print(f"Available IPs in management network: {len(available_ips)}")
# 9. Reserve gateway
gateway = nb.ipam.ip_addresses.create(
address='192.168.3.1/24',
status='active',
role='anycast',
description='Management network gateway'
)
Virtualization Models
Complete VM Example
Creating a VM with network configuration:
# 1. Create cluster type (if not exists)
cluster_type = nb.virtualization.cluster_types.get(slug='proxmox')
if not cluster_type:
cluster_type = nb.virtualization.cluster_types.create(
name='Proxmox VE',
slug='proxmox'
)
# 2. Create cluster
cluster = nb.virtualization.clusters.create(
name='Matrix',
type=cluster_type.id,
site=site.id,
description='3-node Proxmox VE 9.x cluster'
)
# 3. Create VM role
vm_role = nb.dcim.device_roles.get(slug='docker-host')
if not vm_role:
vm_role = nb.dcim.device_roles.create(
name='Docker Host',
slug='docker-host',
vm_role=True, # Mark as VM role
color='4caf50'
)
# 4. Create VM
vm = nb.virtualization.virtual_machines.create(
name='docker-01',
cluster=cluster.id,
role=vm_role.id,
status='active',
vcpus=4,
memory=8192,
disk=100,
description='Docker host for Nexus registry',
tags=[{'name': 'docker'}, {'name': 'production'}]
)
# 5. Create VM interface
vm_iface = nb.virtualization.interfaces.create(
virtual_machine=vm.id,
name='eth0',
type='virtual',
enabled=True,
mtu=1500
)
# 6. Get next available IP from prefix
prefix = nb.ipam.prefixes.get(prefix='192.168.3.0/24')
vm_ip = prefix.available_ips.create(
dns_name='docker-01-nexus.spaceships.work',
status='active',
assigned_object_type='virtualization.vminterface',
assigned_object_id=vm_iface.id,
tags=[{'name': 'production-dns'}, {'name': 'terraform'}]
)
# 7. Set as primary IP
vm.primary_ip4 = vm_ip.id
vm.save()
# 8. Query VM with interfaces
vm = nb.virtualization.virtual_machines.get(name='docker-01')
print(f"VM: {vm.name}")
print(f"Cluster: {vm.cluster.name}")
print(f"Primary IP: {vm.primary_ip4.address}")
for iface in vm.interfaces:
print(f" Interface: {iface.name}")
for ip in nb.ipam.ip_addresses.filter(vminterface_id=iface.id):
print(f" IP: {ip.address} ({ip.dns_name})")
Matrix Cluster Example
Complete NetBox representation of the Matrix cluster:
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = ["pynetbox>=7.0.0", "infisical-python>=2.3.3"]
# ///
import pynetbox
from infisical import InfisicalClient
# Get token
client = InfisicalClient()
token = client.get_secret(
secret_name="NETBOX_API_TOKEN",
project_id="7b832220-24c0-45bc-a5f1-ce9794a31259",
environment="prod",
path="/matrix"
).secret_value
nb = pynetbox.api('https://netbox.spaceships.work', token=token)
# Create site
site = nb.dcim.sites.create(
name="Matrix Cluster",
slug="matrix",
status="active",
description="3-node Proxmox VE 9.x cluster with CEPH storage"
)
# Create cluster
cluster = nb.virtualization.clusters.create(
name="Matrix",
type=nb.virtualization.cluster_types.get(slug='proxmox').id,
site=site.id
)
# Create prefixes
prefixes = {
'mgmt': nb.ipam.prefixes.create(
prefix='192.168.3.0/24',
site=site.id,
description='Management network',
is_pool=True
),
'ceph_pub': nb.ipam.prefixes.create(
prefix='192.168.5.0/24',
site=site.id,
description='CEPH public (MTU 9000)',
is_pool=True
),
'ceph_priv': nb.ipam.prefixes.create(
prefix='192.168.7.0/24',
site=site.id,
description='CEPH private (MTU 9000)',
is_pool=True
),
'corosync': nb.ipam.prefixes.create(
prefix='192.168.8.0/24',
site=site.id,
description='Corosync (VLAN 9)',
is_pool=True
)
}
# Matrix nodes
nodes = [
{'name': 'foxtrot', 'mgmt_ip': '192.168.3.5', 'ceph_pub': '192.168.5.5',
'ceph_priv': '192.168.7.5', 'corosync': '192.168.8.5'},
{'name': 'golf', 'mgmt_ip': '192.168.3.6', 'ceph_pub': '192.168.5.6',
'ceph_priv': '192.168.7.6', 'corosync': '192.168.8.6'},
{'name': 'hotel', 'mgmt_ip': '192.168.3.7', 'ceph_pub': '192.168.5.7',
'ceph_priv': '192.168.7.7', 'corosync': '192.168.8.7'}
]
for node_data in nodes:
# Create device
device = nb.dcim.devices.create(
name=node_data['name'],
device_type=nb.dcim.device_types.get(slug='ms-a2').id,
device_role=nb.dcim.device_roles.get(slug='proxmox-node').id,
site=site.id,
status='active'
)
# Create interfaces and IPs
# Management
mgmt_iface = nb.dcim.interfaces.create(
device=device.id, name='enp2s0', type='2.5gbase-t', mtu=1500
)
mgmt_ip = nb.ipam.ip_addresses.create(
address=f"{node_data['mgmt_ip']}/24",
dns_name=f"{node_data['name']}.spaceships.work",
assigned_object_type='dcim.interface',
assigned_object_id=mgmt_iface.id,
tags=[{'name': 'production-dns'}]
)
device.primary_ip4 = mgmt_ip.id
device.save()
# CEPH public
ceph_pub_iface = nb.dcim.interfaces.create(
device=device.id, name='enp1s0f0', type='10gbase-x-sfpp', mtu=9000
)
nb.ipam.ip_addresses.create(
address=f"{node_data['ceph_pub']}/24",
assigned_object_type='dcim.interface',
assigned_object_id=ceph_pub_iface.id
)
# CEPH private
ceph_priv_iface = nb.dcim.interfaces.create(
device=device.id, name='enp1s0f1', type='10gbase-x-sfpp', mtu=9000
)
nb.ipam.ip_addresses.create(
address=f"{node_data['ceph_priv']}/24",
assigned_object_type='dcim.interface',
assigned_object_id=ceph_priv_iface.id
)
# Corosync
corosync_iface = nb.dcim.interfaces.create(
device=device.id, name='enp2s0.9', type='virtual', mtu=1500
)
nb.ipam.ip_addresses.create(
address=f"{node_data['corosync']}/24",
assigned_object_type='dcim.interface',
assigned_object_id=corosync_iface.id
)
print("Matrix cluster created in NetBox!")
Best Practices
1. Plan Hierarchy First
1. Create Site
2. Create Prefixes (IPAM)
3. Create VLANs
4. Create Device Types/Roles
5. Create Devices
6. Create Interfaces
7. Assign IPs
8. Set Primary IPs
2. Use Consistent Naming
- Sites: Descriptive names (e.g., "Matrix Cluster")
- Devices: Hostname only (e.g., "foxtrot")
- Interfaces: Match OS names (e.g., "enp1s0f0")
- DNS names: Follow convention (e.g., "foxtrot.spaceships.work")
3. Tag Everything
tags = [
{'name': 'proxmox-node'},
{'name': 'ceph-node'},
{'name': 'production'},
{'name': 'production-dns'},
{'name': 'terraform'}
]
4. Use Prefixes as IP Pools
prefix = nb.ipam.prefixes.create(
prefix='192.168.3.0/24',
is_pool=True # Enable automatic IP assignment
)
# Get next available IP
ip = prefix.available_ips.create(dns_name='host.domain')
5. Always Set Primary IPs
# After creating IPs, set primary
device.primary_ip4 = mgmt_ip.id
device.save()
6. Validate Relationships
# Check if IP is assigned
ip = nb.ipam.ip_addresses.get(address='192.168.3.5/24')
if ip.assigned_object:
print(f"Assigned to: {ip.assigned_object.name}")
else:
print("IP not assigned to any interface")
7. Use Descriptions
device = nb.dcim.devices.create(
name='foxtrot',
description='AMD Ryzen 9 9955HX, 64GB RAM, 3× NVMe (1TB + 2× 4TB)'
)
Related Documentation
- NetBox API Guide - API reference
- NetBox Best Practices - Infrastructure patterns
- Tools: netbox_api_client.py - Working examples
- DNS Naming Conventions - Naming rules