From 6482011d159fab7b7bcbbe069fed6ff7e5dc3a4f Mon Sep 17 00:00:00 2001 From: Damien Arnodo Date: Fri, 27 Feb 2026 12:21:21 +0000 Subject: [PATCH] Delete docs/netbox-data-model.md --- docs/netbox-data-model.md | 534 -------------------------------------- 1 file changed, 534 deletions(-) delete mode 100644 docs/netbox-data-model.md diff --git a/docs/netbox-data-model.md b/docs/netbox-data-model.md deleted file mode 100644 index d7a663e..0000000 --- a/docs/netbox-data-model.md +++ /dev/null @@ -1,534 +0,0 @@ -# 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](https://github.com/netbox-community/netbox-bgp) 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](https://gitea.arnodo.fr/Damien/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 - -```bash -# 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](https://gitea.arnodo.fr/Damien/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 | - -### MLAG Peer-Link Cabling - -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 - -```python -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 | - -### MLAG-Related VLANs - -| 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: - -```python -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