Files
gh-basher83-lunar-claude-pl…/skills/netbox-powerdns-integration/reference/netbox-data-models.md
2025-11-29 18:00:21 +08:00

23 KiB
Raw Blame History

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

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 details
  • tags - 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 identifier
  • site - Parent site
  • u_height - Units (typically 42U)
  • desc_units - Count units top-down
  • width - 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 type
  • device_role - Purpose (server, switch, etc.)
  • site - Physical location
  • rack - Optional rack location
  • position - Rack unit position
  • status - active, offline, planned, etc.
  • primary_ip4 - Primary IPv4 address
  • primary_ip6 - Primary IPv6 address
  • tags

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 device
  • name - Interface name (e.g., "eth0", "enp1s0")
  • type - Physical type (1000base-t, 10gbase-x, etc.)
  • enabled - Administrative status
  • mtu - Maximum transmission unit
  • mac_address - MAC address
  • mode - Access or Trunk (for VLANs)
  • untagged_vlan - Native VLAN
  • tagged_vlans - Tagged VLANs
  • tags

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 B
  • type - Cable type (cat6, fiber, dac, etc.)
  • status - connected, planned, etc.
  • length - Cable length
  • length_unit - m, ft, etc.
  • color - Cable color
  • tags

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 ID
  • vrf - Virtual routing and forwarding instance
  • tenant - Tenant assignment
  • tags

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 location
  • vrf - VRF assignment
  • vlan - Associated VLAN
  • is_pool - Allow automatic IP assignment
  • description
  • tags

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 name
  • site - Site assignment
  • group - VLAN group (optional)
  • status - active, reserved, deprecated
  • role - Purpose
  • description
  • tags

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 name
  • rd - Route distinguisher (optional)
  • description
  • enforce_unique - Enforce unique IP addressing
  • tags

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 hostname
  • cluster - Virtualization cluster
  • role - VM role (optional)
  • status - active, offline, planned, etc.
  • vcpus - Virtual CPU count
  • memory - Memory in MB
  • disk - Disk in GB
  • primary_ip4 - Primary IPv4
  • primary_ip6 - Primary IPv6
  • description
  • tags

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 name
  • type - Cluster type
  • site - Physical location
  • description
  • tags

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 VM
  • name - Interface name (e.g., "eth0")
  • type - Interface type (virtual, bridge)
  • enabled - Administrative status
  • mtu - MTU
  • mac_address - MAC address
  • untagged_vlan - Native VLAN
  • tagged_vlans - Tagged VLANs
  • tags

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)'
)


Next: NetBox Best Practices Guide