diff --git a/docs/netbox-data-model.md b/docs/netbox-data-model.md index 20c08b5..d7a663e 100644 --- a/docs/netbox-data-model.md +++ b/docs/netbox-data-model.md @@ -11,6 +11,14 @@ The fabric-orchestrator uses NetBox as the **source of truth** for EVPN-VXLAN fa - 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 @@ -22,6 +30,7 @@ The fabric-orchestrator uses NetBox as the **source of truth** for EVPN-VXLAN fa | **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/` | @@ -152,6 +161,138 @@ Based on the reference topology: --- +## 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 @@ -258,7 +399,7 @@ MLAG configuration uses Custom Fields since NetBox doesn't have native MLAG supp ### 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 @@ -313,48 +454,48 @@ 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 │ │ -│ │ (65001) │ │ -│ └────────────┘ │ -│ │ +┌──────────────────────────────────────────────────────────────────────────────┐ +│ 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 │ -│ ▼ │ +│ │ +│ ┌────────────┐ ┌───────────────────────┐ ┌────────────┐ │ +│ │ VLAN │─────│ L2VPN Termination │─────│ L2VPN │ │ +│ │ (40) │ │ │ │ (EVPN) │ │ +│ └────────────┘ └───────────────────────┘ └────────────┘ │ +│ │ │ +│ │ identifier │ +│ ▼ │ │ ┌────────────┐ │ │ │ 100040 │ │ │ │ (VNI) │ │ │ └────────────┘ │ -│ │ +│ │ │ ┌────────────┐ ┌───────────────────┐ │ │ │ VRF │─────│ Route Target │ │ │ │ (gold) │ │ (1:100001) │ │ │ │ cf:l3vni │ └───────────────────┘ │ -│ └────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────┘ +│ └────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────────────────────────┘ ``` --- @@ -367,6 +508,7 @@ l3vni = vrf.custom_fields.get('l3vni') |----------|--------|---------| | `/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` | @@ -386,6 +528,7 @@ l3vni = vrf.custom_fields.get('l3vni') ## Next Steps 1. **Custom Fields Setup** - Create all custom fields listed above in NetBox -2. **Data Population** - Enter fabric topology data into NetBox -3. **NetBox Client** - Update `src/netbox/client.py` to use custom fields -4. **Validation** - Verify data retrieval matches expected fabric config +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