1040 lines
23 KiB
Markdown
1040 lines
23 KiB
Markdown
# 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](#overview)
|
||
- [Core Data Models](#core-data-models)
|
||
- [Model Relationships](#model-relationships)
|
||
- [DCIM Models (Data Center)](#dcim-models-data-center)
|
||
- [IPAM Models (IP Management)](#ipam-models-ip-management)
|
||
- [Virtualization Models](#virtualization-models)
|
||
- [Matrix Cluster Example](#matrix-cluster-example)
|
||
- [Best Practices](#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 details
|
||
- `tags` - Flexible categorization
|
||
|
||
**Example:**
|
||
|
||
```python
|
||
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:**
|
||
|
||
```python
|
||
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:**
|
||
|
||
```python
|
||
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:**
|
||
|
||
```python
|
||
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:**
|
||
|
||
```python
|
||
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:**
|
||
|
||
```python
|
||
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:**
|
||
|
||
```python
|
||
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:**
|
||
|
||
```python
|
||
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:**
|
||
|
||
```python
|
||
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:**
|
||
|
||
```python
|
||
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:**
|
||
|
||
```python
|
||
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:**
|
||
|
||
```python
|
||
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
|
||
|
||
```text
|
||
Region (optional)
|
||
└── Site
|
||
├── Rack
|
||
│ └── Device
|
||
│ └── Interface
|
||
│ └── IP Address
|
||
├── Cluster
|
||
│ └── Virtual Machine
|
||
│ └── VM Interface
|
||
│ └── IP Address
|
||
└── Prefix
|
||
└── IP Address
|
||
```
|
||
|
||
### Key Relationships
|
||
|
||
**Site containment:**
|
||
|
||
```text
|
||
Site
|
||
├── has many Racks
|
||
├── has many Devices
|
||
├── has many Clusters
|
||
├── has many Prefixes
|
||
└── has many VLANs
|
||
```
|
||
|
||
**Device structure:**
|
||
|
||
```text
|
||
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:**
|
||
|
||
```text
|
||
Interface
|
||
├── belongs to Device
|
||
├── has many IP Addresses
|
||
├── connected via Cable
|
||
├── assigned to VLAN(s)
|
||
└── assigned object for IP
|
||
```
|
||
|
||
**IP Address assignment:**
|
||
|
||
```text
|
||
IP Address
|
||
├── belongs to Prefix
|
||
├── assigned to Interface (device or VM)
|
||
├── belongs to VRF (optional)
|
||
└── referenced as Primary IP by Device/VM
|
||
```
|
||
|
||
**VM structure:**
|
||
|
||
```text
|
||
Virtual Machine
|
||
├── belongs to Cluster
|
||
├── has many VM Interfaces
|
||
├── has one Primary IP4 (optional)
|
||
└── has one Primary IP6 (optional)
|
||
```
|
||
|
||
**IPAM hierarchy:**
|
||
|
||
```text
|
||
VRF (optional)
|
||
└── Prefix
|
||
├── child Prefix (nested)
|
||
└── IP Address
|
||
```
|
||
|
||
---
|
||
|
||
## DCIM Models (Data Center)
|
||
|
||
### Complete Device Example
|
||
|
||
Creating a complete device with interfaces and IPs:
|
||
|
||
```python
|
||
# 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:
|
||
|
||
```python
|
||
# 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:
|
||
|
||
```python
|
||
# 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:
|
||
|
||
```python
|
||
#!/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
|
||
|
||
```text
|
||
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
|
||
|
||
```python
|
||
tags = [
|
||
{'name': 'proxmox-node'},
|
||
{'name': 'ceph-node'},
|
||
{'name': 'production'},
|
||
{'name': 'production-dns'},
|
||
{'name': 'terraform'}
|
||
]
|
||
```
|
||
|
||
### 4. Use Prefixes as IP Pools
|
||
|
||
```python
|
||
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
|
||
|
||
```python
|
||
# After creating IPs, set primary
|
||
device.primary_ip4 = mgmt_ip.id
|
||
device.save()
|
||
```
|
||
|
||
### 6. Validate Relationships
|
||
|
||
```python
|
||
# 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
|
||
|
||
```python
|
||
device = nb.dcim.devices.create(
|
||
name='foxtrot',
|
||
description='AMD Ryzen 9 9955HX, 64GB RAM, 3× NVMe (1TB + 2× 4TB)'
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
## Related Documentation
|
||
|
||
- [NetBox API Guide](netbox-api-guide.md) - API reference
|
||
- [NetBox Best Practices](netbox-best-practices.md) - Infrastructure patterns
|
||
- [Tools: netbox_api_client.py](../tools/netbox_api_client.py) - Working examples
|
||
- [DNS Naming Conventions](../workflows/naming-conventions.md) - Naming rules
|
||
|
||
---
|
||
|
||
**Next:** [NetBox Best Practices Guide](netbox-best-practices.md)
|