Files
fabric-orchestrator/docs/netbox-data-model.md
Damien Arnodo cfb9c8730e docs: add cabling section for fabric topology
- Document NetBox Cable model usage
- Add complete spine-leaf cabling matrix
- Add MLAG peer-link cabling
- Add host dual-homing connections
- Include pynetbox examples for cable retrieval
- Reference arista-evpn-vxlan-clab topology

Relates to #5
2026-01-10 14:57:55 +00:00

22 KiB

NetBox Data Model for Fabric Orchestrator

This document describes how fabric intent is represented in NetBox for the fabric-orchestrator to consume.

Overview

The fabric-orchestrator uses NetBox as the source of truth for EVPN-VXLAN fabric configuration. Instead of using ConfigContexts, we leverage NetBox's native data models plus the NetBox BGP Plugin for comprehensive fabric representation.

Requirements

  • NetBox 4.4.x
  • NetBox BGP Plugin v0.17.x

Reference Topology

This data model represents the fabric defined in arista-evpn-vxlan-clab:

  • 2 Spine switches (spine1, spine2)
  • 8 Leaf switches in 4 MLAG pairs (leaf1-2, leaf3-4, leaf5-6, leaf7-8)
  • 4 Hosts dual-homed to MLAG pairs

Data Model Summary

Native NetBox Models

Feature NetBox Model API Endpoint
Devices dcim.Device /api/dcim/devices/
Interfaces dcim.Interface /api/dcim/interfaces/
LAGs/Port-Channels dcim.Interface (type=LAG) /api/dcim/interfaces/
Cables dcim.Cable /api/dcim/cables/
VLANs ipam.VLAN /api/ipam/vlans/
VLAN Groups ipam.VLANGroup /api/ipam/vlan-groups/
VRFs ipam.VRF /api/ipam/vrfs/
Route Targets ipam.RouteTarget /api/ipam/route-targets/
IP Addresses ipam.IPAddress /api/ipam/ip-addresses/
Prefixes ipam.Prefix /api/ipam/prefixes/
ASNs ipam.ASN /api/ipam/asns/
L2VPN (EVPN) vpn.L2VPN /api/vpn/l2vpns/
L2VPN Terminations vpn.L2VPNTermination /api/vpn/l2vpn-terminations/

NetBox BGP Plugin Models

Feature Plugin Model API Endpoint
BGP Sessions netbox_bgp.BGPSession /api/plugins/bgp/sessions/
BGP Peer Groups netbox_bgp.PeerGroup /api/plugins/bgp/peer-groups/
BGP Communities netbox_bgp.Community /api/plugins/bgp/communities/
Routing Policies netbox_bgp.RoutingPolicy /api/plugins/bgp/routing-policies/
Prefix Lists netbox_bgp.PrefixList /api/plugins/bgp/prefix-lists/
AS Path Lists netbox_bgp.ASPathList /api/plugins/bgp/as-path-lists/

Custom Fields Reference

NetBox doesn't natively support all fabric-specific configurations. The following custom fields must be created to support MLAG, ASN assignment, and VRF extensions.

Complete Custom Fields List

Device Custom Fields

Field Name Label Type Required Description
asn ASN Integer No BGP Autonomous System Number assigned to this device
mlag_domain_id MLAG Domain ID Text No MLAG domain identifier (e.g., "leafs")
mlag_peer_address MLAG Peer Address Text No MLAG peer IP address
mlag_local_address MLAG Local Address Text No MLAG local IP address
mlag_virtual_mac MLAG Virtual MAC Text No Shared virtual-router MAC (e.g., "c001.cafe.babe")

Interface Custom Fields

Field Name Label Type Required Description
mlag_peer_link MLAG Peer Link Boolean No Marks interface as MLAG peer-link
mlag_id MLAG ID Integer No MLAG port-channel ID for host-facing LAGs

VRF Custom Fields

Field Name Label Type Required Description
l3vni L3 VNI Integer No Layer 3 VNI for EVPN symmetric IRB
vrf_vlan VRF VLAN Integer No VLAN ID used for L3 VNI SVI

IP Address Custom Fields

Field Name Label Type Required Description
virtual_ip Virtual IP Boolean No Marks IP as anycast/virtual IP (shared across MLAG pair)

Custom Field Creation via API

# Example: Create ASN custom field on Device
curl -X POST "https://netbox.example.com/api/extras/custom-fields/" \
  -H "Authorization: Token $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content_types": ["dcim.device"],
    "name": "asn",
    "label": "ASN",
    "type": "integer",
    "required": false,
    "description": "BGP Autonomous System Number assigned to this device"
  }'

# Example: Create MLAG Peer Link custom field on Interface
curl -X POST "https://netbox.example.com/api/extras/custom-fields/" \
  -H "Authorization: Token $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content_types": ["dcim.interface"],
    "name": "mlag_peer_link",
    "label": "MLAG Peer Link",
    "type": "boolean",
    "required": false,
    "description": "Marks interface as MLAG peer-link"
  }'

Fabric Component Mapping

Devices and Roles

Fabric Role NetBox Device Role Description
Spine spine Spine switches (AS 65000)
Leaf leaf Leaf switches (AS 65001-65004)

Example Device Setup:

  • spine1, spine2 - Device Role: spine
  • leaf1 through leaf8 - Device Role: leaf

Interfaces

Interface Type NetBox Interface Type Usage
Physical uplinks 1000base-t / 10gbase-x-sfpp Spine-Leaf connections
Loopback0 virtual BGP Router-ID
Loopback1 virtual VTEP source (shared by MLAG pair)
Port-Channel lag MLAG peer-link, host bonds
Vxlan1 virtual VXLAN tunnel interface
SVI virtual VLAN interfaces (Vlan40, etc.)

IP Addressing Scheme

Based on the reference topology:

Purpose Prefix Example
Spine-Leaf P2P (Spine1) 10.0.1.0/24 10.0.1.0/31, 10.0.1.2/31, ...
Spine-Leaf P2P (Spine2) 10.0.2.0/24 10.0.2.0/31, 10.0.2.2/31, ...
MLAG iBGP Peer 10.0.3.0/24 10.0.3.0/31 per MLAG pair
MLAG Peer VLAN 4090 10.0.199.0/24 /31 per MLAG pair
Loopback0 (Router-ID) 10.0.250.0/24 Spine: .1-.2, Leaf: .11-.18
Loopback1 (VTEP) 10.0.255.0/24 .11-.14 (shared per MLAG pair)

Cabling

NetBox's native dcim.Cable model represents physical connections between devices. This enables topology visualization (via plugins like netbox-topology-views) and validation of the fabric infrastructure.

Cable Model

Field Description
a_terminations Source interface(s)
b_terminations Destination interface(s)
type Cable type (e.g., cat6a, mmf-om4, dac-passive)
status connected, planned, decommissioning
label Optional identifier
length Cable length (with unit)

Spine-Leaf Cabling Matrix

Based on the arista-evpn-vxlan-clab topology:

Spine1 to Leafs

Spine1 Interface Leaf Leaf Interface Description
Ethernet1 leaf1 Ethernet11 Underlay uplink
Ethernet2 leaf2 Ethernet11 Underlay uplink
Ethernet3 leaf3 Ethernet11 Underlay uplink
Ethernet4 leaf4 Ethernet11 Underlay uplink
Ethernet5 leaf5 Ethernet11 Underlay uplink
Ethernet6 leaf6 Ethernet11 Underlay uplink
Ethernet7 leaf7 Ethernet11 Underlay uplink
Ethernet8 leaf8 Ethernet11 Underlay uplink

Spine2 to Leafs

Spine2 Interface Leaf Leaf Interface Description
Ethernet1 leaf1 Ethernet12 Underlay uplink
Ethernet2 leaf2 Ethernet12 Underlay uplink
Ethernet3 leaf3 Ethernet12 Underlay uplink
Ethernet4 leaf4 Ethernet12 Underlay uplink
Ethernet5 leaf5 Ethernet12 Underlay uplink
Ethernet6 leaf6 Ethernet12 Underlay uplink
Ethernet7 leaf7 Ethernet12 Underlay uplink
Ethernet8 leaf8 Ethernet12 Underlay uplink

Each MLAG pair has a dedicated peer-link connection:

MLAG Pair Device A Interface A Device B Interface B Description
VTEP1 leaf1 Ethernet10 leaf2 Ethernet10 MLAG peer-link
VTEP2 leaf3 Ethernet10 leaf4 Ethernet10 MLAG peer-link
VTEP3 leaf5 Ethernet10 leaf6 Ethernet10 MLAG peer-link
VTEP4 leaf7 Ethernet10 leaf8 Ethernet10 MLAG peer-link

Note: In production, peer-links typically use multiple interfaces in a Port-Channel for redundancy (e.g., Ethernet10 + Ethernet13 → Port-Channel10).

Host Dual-Homing Cabling

Hosts are dual-homed to MLAG pairs using LACP bonding:

Host Leaf A Leaf A Interface Leaf B Leaf B Interface MLAG ID
host1 leaf1 Ethernet1 leaf2 Ethernet1 1
host2 leaf3 Ethernet1 leaf4 Ethernet1 1
host3 leaf5 Ethernet1 leaf6 Ethernet1 1
host4 leaf7 Ethernet1 leaf8 Ethernet1 1

Cabling Conventions

Interface Range Purpose
Ethernet1-9 Host-facing (downlinks)
Ethernet10 MLAG peer-link
Ethernet11-12 Spine uplinks

Cable Retrieval via API

import pynetbox

nb = pynetbox.api('http://netbox.example.com', token='your-token')

# Get all cables for a device
cables = nb.dcim.cables.filter(device='leaf1')
for cable in cables:
    a_term = cable.a_terminations[0]
    b_term = cable.b_terminations[0]
    print(f"{a_term.device.name}:{a_term.name} <-> {b_term.device.name}:{b_term.name}")

# Get spine-leaf cables only
spine_cables = nb.dcim.cables.filter(device='spine1')

# Get peer-link cables (filter by interface name pattern)
leaf1_interfaces = nb.dcim.interfaces.filter(device='leaf1', name='Ethernet10')
for iface in leaf1_interfaces:
    if iface.cable:
        cable = nb.dcim.cables.get(iface.cable.id)
        print(f"Peer-link: {cable}")

# Validate expected connections
def validate_spine_leaf_cabling(nb, spine_name, expected_leafs):
    """Validate that spine has cables to all expected leafs."""
    cables = nb.dcim.cables.filter(device=spine_name)
    connected_devices = set()
    for cable in cables:
        for term in cable.b_terminations:
            connected_devices.add(term.device.name)
    
    missing = set(expected_leafs) - connected_devices
    if missing:
        print(f"WARNING: {spine_name} missing connections to: {missing}")
    return len(missing) == 0

# Example validation
validate_spine_leaf_cabling(nb, 'spine1', 
    ['leaf1', 'leaf2', 'leaf3', 'leaf4', 'leaf5', 'leaf6', 'leaf7', 'leaf8'])

Topology Visualization

With cables properly defined, the netbox-topology-views plugin can automatically generate fabric diagrams. The plugin uses cable data to draw connections between devices.

Key benefits:

  • Auto-generated diagrams: No manual drawing required
  • Real-time updates: Topology reflects current NetBox data
  • Drill-down: Click devices/cables for details
  • Export: SVG/PNG export for documentation

BGP Configuration

ASN Assignments

Device(s) ASN Type Custom Field
spine1, spine2 65000 eBGP asn: 65000
leaf1, leaf2 65001 iBGP pair asn: 65001
leaf3, leaf4 65002 iBGP pair asn: 65002
leaf5, leaf6 65003 iBGP pair asn: 65003
leaf7, leaf8 65004 iBGP pair asn: 65004

BGP Sessions (Plugin)

Underlay eBGP Sessions

Each leaf has eBGP sessions to both spines over point-to-point interfaces:

Local Device Local IP Remote Device Remote IP Remote ASN
leaf1 10.0.1.1 spine1 10.0.1.0 65000
leaf1 10.0.2.1 spine2 10.0.2.0 65000

Underlay iBGP Sessions

Each MLAG pair has an iBGP session for redundancy:

Local Device Local IP Remote Device Remote IP Notes
leaf1 10.0.3.0 leaf2 10.0.3.1 next-hop-self

Overlay EVPN Sessions

EVPN sessions use loopback addresses with ebgp-multihop 3:

Local Device Local IP (Lo0) Remote Device Remote IP (Lo0) AFI/SAFI
leaf1 10.0.250.11 spine1 10.0.250.1 L2VPN EVPN
leaf1 10.0.250.11 spine2 10.0.250.2 L2VPN EVPN

BGP Peer Groups (Plugin)

Peer Group Purpose Key Settings
underlay eBGP to spines remote-as 65000, maximum-routes 12000
underlay_ibgp iBGP to MLAG peer next-hop-self
evpn EVPN overlay update-source Loopback0, ebgp-multihop 3, send-community extended

VXLAN / EVPN Configuration

L2VPN for EVPN VNI Mappings

NetBox's L2VPN model represents EVPN instances:

L2VPN Name Type Identifier (VNI) Description
VLAN40-L2VNI EVPN 100040 L2 VXLAN for VLAN 40
VRF-gold EVPN 100001 L3 VNI for VRF gold

L2VPN Terminations

Link VLANs to L2VPNs on specific devices:

L2VPN Device Interface/VLAN
VLAN40-L2VNI leaf1 VLAN 40
VLAN40-L2VNI leaf2 VLAN 40
VLAN40-L2VNI leaf5 VLAN 40
VLAN40-L2VNI leaf6 VLAN 40

Route Targets

VRF/VLAN Import RT Export RT
VLAN 40 40:100040 40:100040
VRF gold 1:100001 1:100001

MLAG Configuration

MLAG configuration uses Custom Fields since NetBox doesn't have native MLAG support.

MLAG Pair Configuration

Device Domain ID Local IP Peer IP Virtual MAC
leaf1 leafs 10.0.199.254 10.0.199.255 c001.cafe.babe
leaf2 leafs 10.0.199.255 10.0.199.254 c001.cafe.babe
VLAN Name Purpose Trunk Group
4090 mlag-peer MLAG peer communication mlag-peer
4091 mlag-ibgp iBGP between MLAG peers mlag-peer

VRF Configuration

VRF Definition

VRF Name RD Import RT Export RT L3 VNI (custom field)
gold 10.0.250.X:1 1:100001 1:100001 100001

Note: RD uses device's Loopback0 IP for uniqueness

VRF Interface Assignment

SVIs are assigned to VRFs in NetBox. VRF membership is tracked via IP addresses assigned to interfaces:

Interface VRF IP Address Virtual IP (custom field)
Vlan34 (leaf3) gold 10.34.34.2/24 No
Vlan34 (leaf4) gold 10.34.34.3/24 No
Vlan34 (leaf3) gold 10.34.34.1/24 Yes (anycast)
Vlan78 (leaf7) gold 10.78.78.2/24 No
Vlan78 (leaf8) gold 10.78.78.3/24 No
Vlan78 (leaf7) gold 10.78.78.1/24 Yes (anycast)

Data Retrieval Strategy

The fabric-orchestrator retrieves intent from NetBox using pynetbox:

import pynetbox

nb = pynetbox.api('http://netbox.example.com', token='your-token')

# Get all leaf devices
leaves = nb.dcim.devices.filter(role='leaf')

# Get device ASN from custom field
device = nb.dcim.devices.get(name='leaf1')
asn = device.custom_fields.get('asn')

# Get BGP sessions for a device
bgp_sessions = nb.plugins.bgp.sessions.filter(device='leaf1')

# Get L2VPNs (EVPN)
l2vpns = nb.vpn.l2vpns.filter(type='evpn')

# Get VLAN-to-VNI mappings via L2VPN terminations
terminations = nb.vpn.l2vpn_terminations.filter(l2vpn='VLAN40-L2VNI')

# Get VRF with L3 VNI
vrf = nb.ipam.vrfs.get(name='gold')
l3vni = vrf.custom_fields.get('l3vni')

Relationship Diagram

┌──────────────────────────────────────────────────────────────────────────────┐
│                                NetBox                                         │
├──────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌────────────┐     ┌────────────────┐     ┌───────────────┐                │
│  │  Device    │─────│  Interface     │─────│ IP Address    │                │
│  │ (leaf1)    │     │ (Loopback0)    │     │(10.0.250.11)  │                │
│  │ cf:asn     │     │ cf:mlag_peer_  │     │ cf:virtual_ip │                │
│  └────────────┘     │    link        │     └───────────────┘                │
│       │             └────────────────┘                                       │
│       │                    │                                                 │
│       │ custom_fields      │                                                 │
│       ▼                    ▼                                                 │
│  ┌────────────┐     ┌────────────────┐                                      │
│  │  cf:asn    │     │    Cable       │                                      │
│  │ (65001)    │     │ (connections)  │                                      │
│  └────────────┘     └────────────────┘                                      │
│                                                                              │
│  ┌──────────────────┐     ┌───────────────────┐                             │
│  │ BGP Session      │─────│   Peer Group      │                             │
│  │ (plugin)         │     │   (plugin)        │                             │
│  └──────────────────┘     └───────────────────┘                             │
│                                                                              │
│  ┌────────────┐     ┌───────────────────────┐     ┌────────────┐            │
│  │   VLAN     │─────│ L2VPN Termination     │─────│  L2VPN     │            │
│  │  (40)      │     │                       │     │ (EVPN)     │            │
│  └────────────┘     └───────────────────────┘     └────────────┘            │
│                                                        │                     │
│                                                        │ identifier          │
│                                                        ▼                     │
│                                                   ┌────────────┐            │
│                                                   │  100040    │            │
│                                                   │  (VNI)     │            │
│                                                   └────────────┘            │
│                                                                              │
│  ┌────────────┐     ┌───────────────────┐                                   │
│  │   VRF      │─────│  Route Target     │                                   │
│  │ (gold)     │     │ (1:100001)        │                                   │
│  │ cf:l3vni   │     └───────────────────┘                                   │
│  └────────────┘                                                              │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

API Filter Compatibility Notes

Working Filters (NetBox 4.4)

Endpoint Filter Example
/api/dcim/devices/ role, name, site ?role=leaf
/api/dcim/interfaces/ device, type, name ?device=leaf1&type=virtual
/api/dcim/cables/ device, site, rack ?device=leaf1
/api/ipam/ip-addresses/ device, interface, vrf ?device=leaf1&vrf=gold
/api/ipam/vlans/ vid, site, group ?vid=40
/api/vpn/l2vpns/ type, name ?type=evpn
/api/vpn/l2vpn-terminations/ l2vpn, vlan_id ?l2vpn=VLAN40-L2VNI
/api/plugins/bgp/sessions/ device, peer_group ?device=leaf1

Non-Working Filters (Require Workarounds)

Endpoint Invalid Filter Workaround
/api/ipam/asns/ device Use custom field asn on Device
/api/vpn/l2vpn-terminations/ device Filter by vlan_id then correlate
/api/dcim/interfaces/ vrf Query IPs with VRF filter, get interface IDs

Next Steps

  1. Custom Fields Setup - Create all custom fields listed above in NetBox
  2. Cabling Setup - Create cables for spine-leaf, peer-links, and host connections
  3. Data Population - Enter fabric topology data into NetBox
  4. NetBox Client - Update src/netbox/client.py to use custom fields and cables
  5. Validation - Verify data retrieval matches expected fabric config