diff --git a/.infrahub.yml b/.infrahub.yml new file mode 100644 index 0000000..60b2cb5 --- /dev/null +++ b/.infrahub.yml @@ -0,0 +1,7 @@ +# yaml-language-server: $schema=https://schema.infrahub.app/python-sdk/repository-config/latest.json +--- +schemas: + - schemas/ + +menus: + - menus/fabric-menu.yml diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4c28566 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,82 @@ +# Claude Code Instructions — fabric-orchestrator + +## Project Context + +Fabric-orchestrator manages Arista EVPN-VXLAN fabrics using Infrahub as the source of truth. +Gitea: https://gitea.arnodo.fr/Damien/fabric-orchestrator +Branch: `feature/41-infrahub-schema` + +## Reference Topology (overlaid.net) + +- 2 spines (AS 65000), 8 leafs in 4 MLAG pairs (AS 65001–65004) +- Loopback0: BGP router-id per device, Loopback1: shared VTEP IP per MLAG pair +- VLANs 4090 (MLAG peer) / 4091 (MLAG iBGP) with trunk groups +- Underlay: eBGP point-to-point (spine↔leaf) + iBGP (leaf↔leaf peer) +- Overlay: EVPN eBGP multihop on loopbacks, spines as route-reflectors with next-hop-unchanged +- L2VXLAN (EVPN Type-2): VLAN→VNI mapping, redistribute learned +- L3VXLAN (EVPN Type-5): VRF→VNI mapping, redistribute connected +- Border peering: BGP sessions inside VRF (leaf7/8 → AS 64999 in VRF gold) + +## Schema Files (Infrahub YAML format, in `schemas/`) + +| File | Content | +| ---------------- | ----------------------------------------------------------------------------- | +| `base.yml` | Device, Interfaces (Ethernet, Loopback, Vlan, Lag, Vxlan), IPAddress | +| `bgp.yml` | AutonomousSystem, BGPRouterConfig, BGPPeerGroup, BGPSession, BGPAddressFamily | +| `vlan_vxlan.yml` | VLAN, VNI, VTEP, VlanVniMapping, EVPNInstance | +| `vrf.yml` | VRFConfig, RouteTarget, VRFDeviceAssignment | +| `mlag.yml` | MlagDomain, MlagPeerConfig, MlagInterface | +| `extensions.yml` | UnderlayLink, HostConnection, FabricSettings | + +## Infrahub Schema Rules + +1. `human_friendly_id` fields MUST have `unique: true` — globally unique across all instances +2. To scope human_friendly_id per-device, traverse Parent relationships: + `parent_rel__parent_attr__value` (e.g. `device__name__value`) +3. Parent relationships (`kind: Parent`) need `optional: false` explicitly +4. Self-referencing relationships need `direction: outbound` +5. Multiple relationships to the same peer type need distinct `identifier` values +6. Don't model the same concept twice (e.g. VTEP source_address vs InterfaceVxlan source_interface) +7. `kind: Attribute` for association, `kind: Component` for ownership (lifecycle-coupled) +8. `kind: Parent` implies the child cannot exist without the parent + +## Validation + +```bash +infrahubctl schema check schemas/ +``` + +Always run before committing. Fix any errors before pushing. + +## Commit Convention + +``` +fix(schema): short description — refs # +feat(schema): short description — refs # +``` + +- One logical change per commit +- Reference the Gitea issue number +- Comment on the issue after implementation with changes summary + +## Open Issues (prioritized) + +Check https://gitea.arnodo.fr/Damien/fabric-orchestrator/issues for current backlog. +Critical issues (blocking data load) take priority over functional improvements. +Dependency chain: resolve critical → significant → minor sequentially. + +## Workflow + +1. Read the issue description fully before starting +2. Read relevant schema files to understand current state +3. Make minimal, targeted changes +4. Validate with `infrahubctl schema check schemas/` +5. Commit with proper message referencing the issue +6. Comment on the Gitea issue with what was changed + +## Don'ts + +- Don't add fields not needed by the reference topology +- Don't change `human_friendly_id` without checking uniqueness implications +- Don't remove existing relationships without checking reverse dependencies +- Don't create new schema files without discussing first diff --git a/README.md b/README.md index ba59d4a..2015aa5 100644 --- a/README.md +++ b/README.md @@ -24,14 +24,14 @@ Think `terraform plan` and `terraform apply`, but for your network fabric — po We chose [InfraHub](https://github.com/opsmill/infrahub) over NetBox as Source of Truth for several reasons: -| Feature | NetBox | InfraHub | -|---------|--------|----------| -| **Schema** | Fixed DCIM/IPAM model | Fully customizable YAML schema | -| **Git Integration** | External sync needed | Native - branches = data branches | -| **Versioning** | Changelog only | True Git-like versioning with merges | -| **Test/Redeploy** | Dump/restore | `git clone` = complete environment | -| **Transforms** | Limited | Built-in Jinja2 + Python transforms | -| **GraphQL** | Yes | Yes (auto-generated from schema) | +| Feature | NetBox | InfraHub | +| ------------------- | --------------------- | ------------------------------------ | +| **Schema** | Fixed DCIM/IPAM model | Fully customizable YAML schema | +| **Git Integration** | External sync needed | Native - branches = data branches | +| **Versioning** | Changelog only | True Git-like versioning with merges | +| **Test/Redeploy** | Dump/restore | `git clone` = complete environment | +| **Transforms** | Limited | Built-in Jinja2 + Python transforms | +| **GraphQL** | Yes | Yes (auto-generated from schema) | **Key benefits for this project:** @@ -98,16 +98,16 @@ git push ## 🎛 Why Prefect? -| Feature | Benefit | -|---------|---------| -| **Python-native workflows** | Use `@flow` and `@task` decorators — no YAML, just Python | -| **Free secrets management** | Native `Secret` blocks for credentials (free in OSS) | -| **Built-in UI** | Dashboard, logs, metrics, execution history via `prefect server start` | -| **No containerization required** | Run flows directly with `.serve()` — no Docker needed | -| **Event-driven triggers** | Schedule, webhooks (via FastAPI), flow triggers out of the box | -| **Task dependencies** | Automatic dependency ordering via task result passing or `wait_for` | -| **Retry & error handling** | Built-in retry policies with `@task(retries=3)` | -| **Human-in-the-loop** | Native `pause_flow_run()` for approval workflows | +| Feature | Benefit | +| -------------------------------- | ---------------------------------------------------------------------- | +| **Python-native workflows** | Use `@flow` and `@task` decorators — no YAML, just Python | +| **Free secrets management** | Native `Secret` blocks for credentials (free in OSS) | +| **Built-in UI** | Dashboard, logs, metrics, execution history via `prefect server start` | +| **No containerization required** | Run flows directly with `.serve()` — no Docker needed | +| **Event-driven triggers** | Schedule, webhooks (via FastAPI), flow triggers out of the box | +| **Task dependencies** | Automatic dependency ordering via task result passing or `wait_for` | +| **Retry & error handling** | Built-in retry policies with `@task(retries=3)` | +| **Human-in-the-loop** | Native `pause_flow_run()` for approval workflows | ## 🎯 Target Fabric @@ -124,12 +124,12 @@ Reference: [arista-evpn-vxlan-clab](https://gitea.arnodo.fr/Damien/arista-evpn-v Progress is tracked via issues. See [all issues](https://gitea.arnodo.fr/Damien/fabric-orchestrator/issues) or filter by phase: -| Phase | Description | Status | -|-------|-------------|--------| -| **Phase 1** | YANG Path Discovery - Map EOS 4.35.0F YANG models, validate gNMI | ✅ Complete | +| Phase | Description | Status | +| ----------- | -------------------------------------------------------------------- | ------------- | +| **Phase 1** | YANG Path Discovery - Map EOS 4.35.0F YANG models, validate gNMI | ✅ Complete | | **Phase 2** | InfraHub Setup & Core Reconciler - Schema, diff engine, YANG mappers | 🔄 In Progress | -| **Phase 3** | Full Fabric Coverage - BGP, MLAG, VRFs mappers | 📋 Planned | -| **Phase 4** | Prefect Integration - Flows, webhooks, drift detection | 📋 Planned | +| **Phase 3** | Full Fabric Coverage - BGP, MLAG, VRFs mappers | 📋 Planned | +| **Phase 4** | Prefect Integration - Flows, webhooks, drift detection | 📋 Planned | ## 📁 Project Structure @@ -197,17 +197,17 @@ fabric-orchestrator/ ## 🛠️ Technology Stack -| Component | Technology | Purpose | -|-----------|------------|---------| -| Source of Truth | **InfraHub** | Intent definition via custom schema | -| Data Storage | **This Git repo** | Schema + data versioned together | -| Orchestrator | **Prefect** | Python-native workflow orchestration | -| Transport | gNMI | Configuration and telemetry | -| Data Models | YANG (OpenConfig + Arista) | Structured configuration | -| Python Library | pygnmi + infrahub-sdk | gNMI/InfraHub interactions | -| CLI | Click + Rich | YANG discovery tools | -| Validation | Pydantic v2 | Intent data validation | -| Lab | ContainerLab + cEOS | Development environment | +| Component | Technology | Purpose | +| --------------- | -------------------------- | ------------------------------------ | +| Source of Truth | **InfraHub** | Intent definition via custom schema | +| Data Storage | **This Git repo** | Schema + data versioned together | +| Orchestrator | **Prefect** | Python-native workflow orchestration | +| Transport | gNMI | Configuration and telemetry | +| Data Models | YANG (OpenConfig + Arista) | Structured configuration | +| Python Library | pygnmi + infrahub-sdk | gNMI/InfraHub interactions | +| CLI | Click + Rich | YANG discovery tools | +| Validation | Pydantic v2 | Intent data validation | +| Lab | ContainerLab + cEOS | Development environment | ## 🔗 Related Projects diff --git a/menus/fabric-menu.yml b/menus/fabric-menu.yml new file mode 100644 index 0000000..49b5bc6 --- /dev/null +++ b/menus/fabric-menu.yml @@ -0,0 +1,247 @@ +# yaml-language-server: $schema=https://schema.infrahub.app/infrahub/menu/latest.json +# Custom menu for EVPN-VXLAN Fabric Orchestrator +# Organizes schema nodes into logical topology-aligned categories +--- +apiversion: infrahub.app/v1 +kind: Menu +spec: + data: + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + # Fabric Topology + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + - namespace: Topology + name: Mainmenu + label: Fabric Topology + icon: "mdi:lan" + children: + data: + - namespace: Topology + name: Fabric + label: Fabrics + kind: InfraFabric + icon: "mdi:vector-polygon" + + - namespace: Topology + name: Site + label: Sites + kind: LocationSite + icon: "mingcute:building-4-line" + + - namespace: Topology + name: Device + label: Devices + kind: InfraDevice + icon: "mdi:server-network" + + - namespace: Topology + name: Platform + label: Platforms + kind: InfraPlatform + icon: "mdi:chip" + + - namespace: Topology + name: UnderlayLink + label: Underlay Links + kind: InfraUnderlayLink + icon: "mdi:cable-data" + + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + # Interfaces + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + - namespace: Interfaces + name: Mainmenu + label: Interfaces + icon: "mdi:ethernet" + children: + data: + - namespace: Interfaces + name: Ethernet + label: Ethernet + kind: InfraInterfaceEthernet + icon: "mdi:ethernet" + + - namespace: Interfaces + name: Loopback + label: Loopback + kind: InfraInterfaceLoopback + icon: "mdi:reload" + + - namespace: Interfaces + name: VlanSvi + label: VLAN SVI + kind: InfraInterfaceVlan + icon: "mdi:lan" + + - namespace: Interfaces + name: Lag + label: LAG / Port-Channel + kind: InfraInterfaceLag + icon: "mdi:link-variant" + + - namespace: Interfaces + name: Vxlan + label: VXLAN Tunnel + kind: InfraInterfaceVxlan + icon: "mdi:tunnel" + + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + # IP Addressing + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + - namespace: Addressing + name: Mainmenu + label: IP Addressing + icon: "mdi:ip-network" + children: + data: + - namespace: Addressing + name: IPAddress + label: IP Addresses + kind: InfraIPAddress + icon: "mdi:ip-network" + + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + # Layer 2 / VXLAN + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + - namespace: Layer2 + name: Mainmenu + label: Layer 2 / VXLAN + icon: "mdi:switch" + children: + data: + - namespace: Layer2 + name: Vlan + label: VLANs + kind: InfraVLAN + icon: "mdi:lan-connect" + + - namespace: Layer2 + name: Vni + label: VNIs + kind: InfraVNI + icon: "mdi:tunnel-outline" + + - namespace: Layer2 + name: Vtep + label: VTEPs + kind: InfraVTEP + icon: "mdi:server-network-outline" + + - namespace: Layer2 + name: VlanVniMapping + label: VLAN-VNI Mappings + kind: InfraVlanVniMapping + icon: "mdi:swap-horizontal" + + - namespace: Layer2 + name: EvpnInstance + label: EVPN Instances + kind: InfraEVPNInstance + icon: "mdi:cloud-sync" + + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + # Routing / BGP + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + - namespace: Routing + name: Mainmenu + label: Routing / BGP + icon: "mdi:routes" + children: + data: + - namespace: Routing + name: AutonomousSystem + label: Autonomous Systems + kind: InfraAutonomousSystem + icon: "mdi:cloud-outline" + + - namespace: Routing + name: BGPRouterConfig + label: BGP Router Config + kind: InfraBGPRouterConfig + icon: "mdi:router-wireless" + + - namespace: Routing + name: BGPPeerGroup + label: BGP Peer Groups + kind: InfraBGPPeerGroup + icon: "mdi:account-group" + + - namespace: Routing + name: BGPSession + label: BGP Sessions + kind: InfraBGPSession + icon: "mdi:connection" + + - namespace: Routing + name: BGPAddressFamily + label: BGP Address Families + kind: InfraBGPAddressFamily + icon: "mdi:format-list-bulleted" + + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + # VRF + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + - namespace: Vrf + name: Mainmenu + label: VRF + icon: "mdi:router" + children: + data: + - namespace: Vrf + name: Vrf + label: VRFs + kind: InfraVRF + icon: "mdi:router" + + - namespace: Vrf + name: RouteTarget + label: Route Targets + kind: InfraRouteTarget + icon: "mdi:target" + + - namespace: Vrf + name: VrfAssignment + label: VRF Assignments + kind: InfraVRFDeviceAssignment + icon: "mdi:router-network" + + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + # MLAG + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + - namespace: Mlag + name: Mainmenu + label: MLAG + icon: "mdi:link-variant" + children: + data: + - namespace: Mlag + name: MlagDomain + label: MLAG Domains + kind: InfraMlagDomain + icon: "mdi:link-variant" + + - namespace: Mlag + name: MlagPeerConfig + label: MLAG Peer Config + kind: InfraMlagPeerConfig + icon: "mdi:server-network" + + - namespace: Mlag + name: MlagInterface + label: MLAG Interfaces + kind: InfraMlagInterface + icon: "mdi:ethernet-cable" + + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + # Host Connectivity + # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + - namespace: Connectivity + name: Mainmenu + label: Host Connectivity + icon: "mdi:desktop-tower" + children: + data: + - namespace: Connectivity + name: HostConnection + label: Host Connections + kind: InfraHostConnection + icon: "mdi:desktop-tower" diff --git a/schemas/README.md b/schemas/README.md new file mode 100644 index 0000000..ce54fc2 --- /dev/null +++ b/schemas/README.md @@ -0,0 +1,217 @@ +# Infrahub Schema for EVPN-VXLAN Fabric + +This directory contains the Infrahub schema definitions for modeling an EVPN-VXLAN fabric. The schema is designed to represent the [overlaid.net reference topology](https://overlaid.net/2019/01/27/arista-bgp-evpn-configuration-example/) (2 spines, 8 leafs in 4 MLAG pairs). + +## Schema Files + +| File | Nodes | Description | +|------|-------|-------------| +| `base.yml` | Device, InterfaceEthernet, InterfaceLoopback, InterfaceVlan, InterfaceLag, IPAddress, Site, Platform | Core infrastructure and generic Interface | +| `bgp.yml` | AutonomousSystem, BGPRouterConfig, BGPPeerGroup, BGPSession, BGPAddressFamily | BGP routing configuration | +| `vlan_vxlan.yml` | VLAN, VNI, VTEP, VlanVniMapping, EVPNInstance | Layer 2 overlay and VXLAN tunneling | +| `vrf.yml` | VRF, RouteTarget, VRFDeviceAssignment | VRF and L3VNI configuration | +| `mlag.yml` | MlagDomain, MlagPeerConfig, MlagInterface | MLAG domain and peer configuration | +| `extensions.yml` | Fabric, UnderlayLink, HostConnection | Fabric topology and connectivity | + +## Entity Relationship Diagram + +``` + ┌──────────────┐ + │ InfraFabric │ + └──────┬───────┘ + ┌───────────┼───────────┐ + ▼ ▼ ▼ + ┌────────────┐ ┌──────────┐ ┌──────────────────┐ + │LocationSite│ │InfraAS │ │InfraUnderlayLink │ + └────────────┘ └────┬─────┘ │ local/remote: │ + │ │ Device,Interface,│ + │ │ IPAddress │ + ┌────────────┘ └──────────────────┘ + ▼ + ┌──────────────┐ ┌────────────────┐ + │ InfraDevice │◄────────│ InfraPlatform │ + └──────┬───────┘ └────────────────┘ + │ + ┌─────────────┼──────────────┬────────────────┐ + ▼ ▼ ▼ ▼ +┌─────────────┐ ┌──────────┐ ┌───────────┐ ┌──────────────┐ +│InfraInterface│ │InfraBGP- │ │InfraVTEP │ │InfraMlagDomain│ +│ (generic) │ │RouterCfg │ │ │ │ (2 devices) │ +└──────┬──────┘ └────┬─────┘ └─────┬─────┘ └──────┬───────┘ + │ │ │ │ + ▼ ▼ ▼ ▼ + Subtypes: ┌──────────┐ ┌──────────┐ ┌───────────────┐ + - Ethernet │BGPPeer- │ │VlanVni- │ │MlagPeerConfig │ + - Loopback │ Group │ │ Mapping │ │ (per device) │ + - Vlan └──────────┘ └──────────┘ └───────────────┘ + - Lag ┌──────────┐ ┌───────────────┐ + │ │BGPSession│ │MlagInterface │ + ▼ └────┬─────┘ └───────────────┘ +┌──────────────┐ │ +│InfraIPAddress│ │ optional vrf +└──────────────┘ ▼ + ┌─────────┐ ┌──────────────┐ + │InfraVRF │◄────│InfraRoute- │ + └────┬────┘ │ Target │ + │ └──────────────┘ + ▼ + ┌────────────────┐ + │VRFDevice- │ + │ Assignment │ + │ (per device RD)│ + └────────────────┘ + +Layer 2 / EVPN: +┌──────────┐ ┌──────────┐ ┌──────────────┐ +│InfraVLAN │◄──►│ InfraVNI │ │EVPNInstance │ +│ │ │(L2/L3) │ │(per device │ +│ │ └──────────┘ │ RD/RT) │ +└──────────┘ └──────────────┘ +``` + +### Relationship Legend + +| Symbol | Meaning | +|--------|---------| +| `Parent` → child | Child lifecycle depends on parent (e.g., Device → Interface) | +| `Component` → child | Owned collection (e.g., VTEP → VlanVniMapping) | +| `Attribute` | Association without ownership (e.g., BGPSession → VRF) | +| `Generic` | Polymorphic (e.g., IPAddress → any Interface subtype) | + +### All Relationships + +| Source | Relationship | Target | Kind | Cardinality | +|--------|-------------|--------|------|-------------| +| **base.yml** | | | | | +| InfraInterface | `device` | InfraDevice | Parent | one | +| InfraInterface | `ip_addresses` | InfraIPAddress | Generic | many | +| InfraDevice | `site` | LocationSite | Attribute | one (opt) | +| InfraDevice | `platform` | InfraPlatform | Attribute | one (opt) | +| InfraDevice | `asn` | InfraAutonomousSystem | Attribute | one (opt) | +| InfraDevice | `interfaces` | InfraInterface | Component | many | +| InfraDevice | `mlag_domain` | InfraMlagDomain | Attribute | one (opt) | +| InterfaceEthernet | `lag` | InterfaceLag | Attribute | one (opt) | +| InterfaceEthernet | `connected_interface` | InterfaceEthernet | outbound | one (opt) | +| InterfaceVlan | `vlan` | InfraVLAN | Attribute | one (opt) | +| InterfaceLag | `members` | InterfaceEthernet | Component | many | +| InfraIPAddress | `interface` | InfraInterface | Attribute | one (opt) | +| **bgp.yml** | | | | | +| BGPRouterConfig | `device` | InfraDevice | Parent | one | +| BGPRouterConfig | `local_asn` | InfraAutonomousSystem | Attribute | one | +| BGPRouterConfig | `peer_groups` | BGPPeerGroup | Component | many | +| BGPRouterConfig | `sessions` | BGPSession | Component | many | +| BGPPeerGroup | `bgp_config` | BGPRouterConfig | Parent | one | +| BGPPeerGroup | `remote_asn` | InfraAutonomousSystem | Attribute | one (opt) | +| BGPSession | `bgp_config` | BGPRouterConfig | Parent | one | +| BGPSession | `peer_group` | BGPPeerGroup | Attribute | one (opt) | +| BGPSession | `remote_asn` | InfraAutonomousSystem | Attribute | one (opt) | +| BGPSession | `peer_device` | InfraDevice | Attribute | one (opt) | +| BGPSession | `vrf` | InfraVRF | Attribute | one (opt) | +| BGPAddressFamily | `bgp_config` | BGPRouterConfig | Parent | one | +| BGPAddressFamily | `active_peer_groups` | BGPPeerGroup | Attribute | many | +| BGPAddressFamily | `networks` | InfraIPAddress | Attribute | many (opt) | +| **vlan_vxlan.yml** | | | | | +| InfraVLAN | `vni` | InfraVNI | Attribute | one (opt) | +| InfraVLAN | `site` | LocationSite | Attribute | one (opt) | +| InfraVNI | `vlan` | InfraVLAN | Attribute | one (opt) | +| InfraVNI | `vrf` | InfraVRF | Attribute | one (opt) | +| InfraVTEP | `device` | InfraDevice | Parent | one | +| InfraVTEP | `source_interface` | InterfaceLoopback | Attribute | one | +| InfraVTEP | `vlan_vni_mappings` | VlanVniMapping | Component | many | +| VlanVniMapping | `vtep` | InfraVTEP | Parent | one | +| VlanVniMapping | `vlan` | InfraVLAN | Attribute | one | +| VlanVniMapping | `vni` | InfraVNI | Attribute | one | +| EVPNInstance | `device` | InfraDevice | Parent | one | +| EVPNInstance | `vlan` | InfraVLAN | Attribute | one | +| **vrf.yml** | | | | | +| InfraVRF | `l3vni` | InfraVNI | Attribute | one (opt) | +| InfraVRF | `import_targets` | InfraRouteTarget | outbound | many (opt) | +| InfraVRF | `export_targets` | InfraRouteTarget | outbound | many (opt) | +| InfraVRF | `interfaces` | InfraInterface | Attribute | many (opt) | +| VRFDeviceAssignment | `vrf` | InfraVRF | Attribute | one | +| VRFDeviceAssignment | `device` | InfraDevice | Parent | one | +| VRFDeviceAssignment | `import_targets` | InfraRouteTarget | outbound | many (opt) | +| VRFDeviceAssignment | `export_targets` | InfraRouteTarget | outbound | many (opt) | +| **mlag.yml** | | | | | +| MlagDomain | `devices` | InfraDevice | Attribute | many (2) | +| MlagDomain | `peer_vlan` | InfraVLAN | outbound | one | +| MlagDomain | `ibgp_vlan` | InfraVLAN | outbound | one (opt) | +| MlagPeerConfig | `device` | InfraDevice | Parent | one | +| MlagPeerConfig | `mlag_domain` | MlagDomain | Attribute | one | +| MlagPeerConfig | `local_interface` | InterfaceVlan | Attribute | one | +| MlagPeerConfig | `peer_link` | InterfaceLag | Attribute | one | +| MlagInterface | `mlag_domain` | MlagDomain | Attribute | one | +| MlagInterface | `lag_interfaces` | InterfaceLag | Attribute | many (1-2) | +| **extensions.yml** | | | | | +| InfraFabric | `spine_asn` | InfraAutonomousSystem | Attribute | one (opt) | +| InfraFabric | `sites` | LocationSite | Attribute | many (opt) | +| UnderlayLink | `fabric` | InfraFabric | Parent | one | +| UnderlayLink | `local_device` | InfraDevice | outbound | one | +| UnderlayLink | `local_interface` | InterfaceEthernet | outbound | one | +| UnderlayLink | `local_ip_address` | InfraIPAddress | outbound | one | +| UnderlayLink | `remote_device` | InfraDevice | outbound | one | +| UnderlayLink | `remote_interface` | InterfaceEthernet | outbound | one | +| UnderlayLink | `remote_ip_address` | InfraIPAddress | outbound | one | +| HostConnection | `vlans` | InfraVLAN | Attribute | many | +| HostConnection | `mlag_interface` | MlagInterface | Attribute | one (opt) | +| HostConnection | `lag_interface` | InterfaceLag | Attribute | one (opt) | + +## Reference Topology Mapping + +| Physical | Infrahub Model | +|----------|----------------| +| spine1, spine2 | InfraDevice (role: spine) | +| leaf1-8 | InfraDevice (role: leaf) | +| AS 65000 | InfraAutonomousSystem (spines) | +| AS 65001-65004 | InfraAutonomousSystem (leaf pairs) | +| AS 64999 | InfraAutonomousSystem (border router) | +| VLAN 40, 34, 78, 900 | InfraVLAN + InfraVNI | +| VLAN 4090, 4091 | InfraVLAN (vlan_type: mlag_peer/mlag_ibgp, trunk_groups, stp_enabled: false) | +| VRF gold | InfraVRF + VRFDeviceAssignment (per-device RD) | +| Route targets 1:100001 | InfraRouteTarget | +| leaf1+leaf2 pair | InfraMlagDomain | +| Port-Channel999 | InfraInterfaceLag (peer-link) | +| Port-Channel1 | InfraMlagInterface (host-facing) | +| Ethernet1-12 | InfraInterfaceEthernet | +| Loopback0 | InfraInterfaceLoopback (BGP router-id) | +| Loopback1 | InfraInterfaceLoopback (shared VTEP IP per MLAG pair) | +| Vxlan1 | InfraVTEP | +| Vlan34/78 SVIs | InfraInterfaceVlan (virtual_router_address for anycast gateway) | +| peer groups (underlay, evpn, underlay_ibgp) | InfraBGPPeerGroup | +| BGP sessions (global) | InfraBGPSession (vrf: null) | +| BGP sessions in VRF gold (leaf7/8 → AS 64999) | InfraBGPSession (vrf: gold) | +| spine1/2 p2p links | InfraUnderlayLink (IPAddress relations, not attributes) | +| distance bgp 20 200 200 | BGPRouterConfig (ebgp_distance, ibgp_distance, local_distance) | + +## Usage + +### Loading the Schema + +```bash +infrahubctl schema load schemas/ +``` + +### Validation + +```bash +infrahubctl schema check schemas/ +``` + +## Key Design Decisions + +1. **Generic Interface**: All interface types inherit from `InfraInterface` generic for polymorphic queries +2. **MLAG as Domain**: MLAG is modeled as a domain containing exactly 2 devices, with per-device config via MlagPeerConfig +3. **BGP Hierarchy**: BGPRouterConfig → PeerGroups → Sessions allows template-based configuration +4. **BGP VRF Sessions**: BGPSession has an optional `vrf` relation (kind: Attribute) to support peering inside a VRF context (#50) +5. **VTEP as single model**: VTEP is the unique VXLAN model (InterfaceVxlan was removed to avoid duplication — #44) +6. **EVPN Instance per VLAN**: Allows device-specific RD/RT while referencing common VLAN/VNI +7. **Per-device scoped IDs**: BGPPeerGroup and BGPSession use `bgp_config__router_id__value` prefix in human_friendly_id for global uniqueness (#43) +8. **UnderlayLink IPs as relations**: local/remote IPs reference InfraIPAddress objects instead of inline attributes to avoid dual source of truth (#47) +9. **VRFDeviceAssignment**: Separates VRF definition (global) from per-device assignment (with device-specific RD/RT overrides) +10. **Anycast gateway**: InterfaceVlan has `virtual_router_address` for `ip virtual-router address` and `autostate` for MLAG SVIs (#46) + +## Related Issues + +- Parent issue: [#41 — Define Infrahub Schema for EVPN-VXLAN Fabric](https://gitea.arnodo.fr/Damien/fabric-orchestrator/issues/41) +- Schema fixes: #43 (unique IDs), #44 (remove duplicate VTEP), #45 (VLAN unique), #46 (anycast gateway), #47 (underlay IPs), #48 (BGP distance), #49 (trunk groups), #50 (VRF BGP sessions) +- Depends on: Schema being loaded before transforms (#30, #31, #32, #33) diff --git a/schemas/base.yml b/schemas/base.yml new file mode 100644 index 0000000..7e33dc4 --- /dev/null +++ b/schemas/base.yml @@ -0,0 +1,347 @@ +# Base Infrastructure Schema for EVPN-VXLAN Fabric +# This schema defines core infrastructure objects required for fabric orchestration +--- +version: "1.0" +generics: + - name: Interface + namespace: Infra + description: Generic interface - parent for all interface types + label: Interface + include_in_menu: false + hierarchical: false + display_label: "{{ name__value }}" + attributes: + - name: name + kind: Text + description: Interface name (e.g., Ethernet1, Loopback0) + - name: description + kind: Text + optional: true + - name: enabled + kind: Boolean + default_value: true + - name: mtu + kind: Number + optional: true + description: Maximum Transmission Unit + relationships: + - name: device + peer: InfraDevice + cardinality: one + kind: Parent + optional: false + - name: ip_addresses + peer: InfraIPAddress + identifier: interface__ip_addresses + cardinality: many + kind: Generic + +nodes: + # ================================================================ + # Location + # ================================================================ + - name: Site + namespace: Location + description: Physical site or data center + label: Site + icon: mingcute--building-4-line + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: "{{ name__value }}" + attributes: + - name: name + kind: Text + unique: true + - name: description + kind: Text + optional: true + - name: facility + kind: Text + optional: true + description: Facility identifier or code + + # ================================================================ + # Platform + # ================================================================ + - name: Platform + namespace: Infra + description: Device platform/OS (e.g., Arista EOS, Cisco NX-OS) + label: Platform + icon: mdi--chip + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: "{{ name__value }}" + attributes: + - name: name + kind: Text + unique: true + description: Platform name (e.g., arista_eos, cisco_nxos) + - name: description + kind: Text + optional: true + - name: napalm_driver + kind: Text + optional: true + description: NAPALM driver name + - name: netmiko_device_type + kind: Text + optional: true + description: Netmiko device type + + # ================================================================ + # Device + # ================================================================ + - name: Device + namespace: Infra + description: Network device (spine, leaf, etc.) + label: Device + icon: mdi--server-network + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: "{{ name__value }}" + attributes: + - name: name + kind: Text + unique: true + description: Device hostname + - name: description + kind: Text + optional: true + - name: role + kind: Dropdown + choices: + - name: spine + label: Spine + color: "#3b82f6" + - name: leaf + label: Leaf + color: "#22c55e" + - name: border_leaf + label: Border Leaf + color: "#f59e0b" + description: Fabric role + - name: status + kind: Dropdown + default_value: active + choices: + - name: active + label: Active + color: "#22c55e" + - name: planned + label: Planned + color: "#3b82f6" + - name: maintenance + label: Maintenance + color: "#f59e0b" + - name: decommissioned + label: Decommissioned + color: "#ef4444" + relationships: + - name: site + peer: LocationSite + cardinality: one + optional: true + - name: platform + peer: InfraPlatform + cardinality: one + optional: true + - name: asn + peer: InfraAutonomousSystem + cardinality: one + optional: true + description: BGP Autonomous System + - name: interfaces + peer: InfraInterface + cardinality: many + kind: Component + - name: mlag_domain + peer: InfraMlagDomain + cardinality: one + optional: true + + # ================================================================ + # Interface Types (inherit from InfraInterface generic) + # ================================================================ + - name: InterfaceEthernet + namespace: Infra + description: Physical Ethernet interface + label: Ethernet Interface + icon: mdi--ethernet + include_in_menu: false + inherit_from: + - InfraInterface + uniqueness_constraints: + - ["device", "name__value"] + human_friendly_id: + - device__name__value + - name__value + display_label: "{{ name__value }}" + attributes: + - name: speed + kind: Dropdown + optional: true + choices: + - name: "1000" + label: 1 Gbps + - name: "10000" + label: 10 Gbps + - name: "25000" + label: 25 Gbps + - name: "40000" + label: 40 Gbps + - name: "100000" + label: 100 Gbps + - name: mode + kind: Dropdown + optional: true + choices: + - name: access + label: Access + - name: trunk + label: Trunk + - name: routed + label: Routed (L3) + description: Switchport mode + relationships: + - name: lag + peer: InfraInterfaceLag + cardinality: one + optional: true + description: Parent LAG interface + - name: connected_interface + peer: InfraInterfaceEthernet + identifier: ethernet_connected_to + direction: outbound + cardinality: one + optional: true + description: Connected peer interface + + - name: InterfaceLoopback + namespace: Infra + description: Loopback interface + label: Loopback Interface + icon: mdi--reload + include_in_menu: false + inherit_from: + - InfraInterface + uniqueness_constraints: + - ["device", "name__value"] + human_friendly_id: + - device__name__value + - name__value + display_label: "{{ name__value }}" + + - name: InterfaceVlan + namespace: Infra + description: VLAN SVI interface + label: VLAN Interface + icon: mdi--lan + include_in_menu: false + inherit_from: + - InfraInterface + uniqueness_constraints: + - ["device", "name__value"] + human_friendly_id: + - device__name__value + - name__value + display_label: "{{ name__value }}" + attributes: + - name: virtual_router_address + kind: IPHost + optional: true + description: Anycast gateway IP (ip virtual-router address) + - name: autostate + kind: Boolean + default_value: true + description: "Enable autostate (set false for MLAG peer SVIs)" + relationships: + - name: vlan + peer: InfraVLAN + cardinality: one + optional: true + + - name: InterfaceLag + namespace: Infra + description: Link Aggregation (Port-Channel) interface + label: LAG Interface + icon: mdi--link-variant + include_in_menu: false + inherit_from: + - InfraInterface + uniqueness_constraints: + - ["device", "name__value"] + human_friendly_id: + - device__name__value + - name__value + display_label: "{{ name__value }}" + attributes: + - name: lacp_mode + kind: Dropdown + optional: true + choices: + - name: active + label: Active + - name: passive + label: Passive + - name: static + label: Static (No LACP) + - name: mlag_id + kind: Number + optional: true + description: MLAG interface ID + relationships: + - name: members + peer: InfraInterfaceEthernet + cardinality: many + kind: Component + + # ================================================================ + # IP Address + # ================================================================ + - name: IPAddress + namespace: Infra + description: IP Address assignment + label: IP Address + icon: mdi--ip-network + include_in_menu: false + human_friendly_id: + - address__value + order_by: + - address__value + display_label: "{{ address__value }}" + attributes: + - name: address + kind: IPNetwork + description: IP address with prefix (e.g., 10.0.1.1/31) + - name: description + kind: Text + optional: true + - name: status + kind: Dropdown + default_value: active + choices: + - name: active + label: Active + color: "#22c55e" + - name: reserved + label: Reserved + color: "#3b82f6" + - name: deprecated + label: Deprecated + color: "#ef4444" + relationships: + - name: interface + peer: InfraInterface + identifier: interface__ip_addresses + cardinality: one + optional: true + kind: Attribute diff --git a/schemas/bgp.yml b/schemas/bgp.yml new file mode 100644 index 0000000..2b48d01 --- /dev/null +++ b/schemas/bgp.yml @@ -0,0 +1,271 @@ +# BGP Schema for EVPN-VXLAN Fabric +# Defines Autonomous System, Peer Groups, and BGP Sessions +--- +version: "1.0" +nodes: + # ================================================================ + # Autonomous System + # ================================================================ + - name: AutonomousSystem + namespace: Infra + description: BGP Autonomous System + label: Autonomous System + icon: mdi--cloud-outline + include_in_menu: false + human_friendly_id: + - asn__value + order_by: + - asn__value + display_label: "{{ asn__value }}" + attributes: + - name: asn + kind: Number + unique: true + description: AS Number (e.g., 65000) + - name: description + kind: Text + optional: true + - name: as_type + kind: Dropdown + default_value: private + choices: + - name: private + label: Private + - name: public + label: Public + + # ================================================================ + # BGP Router Configuration (per device) + # ================================================================ + - name: BGPRouterConfig + namespace: Infra + description: BGP router configuration on a device + label: BGP Router Config + icon: mdi--router-wireless + include_in_menu: false + human_friendly_id: + - device__name__value + display_label: "{{ router_id__value }}" + attributes: + - name: router_id + kind: IPHost + unique: true + description: BGP Router ID + - name: default_ipv4_unicast + kind: Boolean + default_value: false + description: Enable default IPv4 unicast + - name: log_neighbor_changes + kind: Boolean + default_value: true + - name: ecmp_max_paths + kind: Number + default_value: 4 + description: Maximum ECMP paths + - name: ecmp_max_ecmp + kind: Number + default_value: 64 + description: Maximum ECMP routes + - name: ebgp_distance + kind: Number + default_value: 20 + description: eBGP administrative distance + - name: ibgp_distance + kind: Number + default_value: 200 + description: iBGP administrative distance + - name: local_distance + kind: Number + default_value: 200 + description: Local route administrative distance + relationships: + - name: device + peer: InfraDevice + cardinality: one + kind: Parent + optional: false + - name: local_asn + peer: InfraAutonomousSystem + cardinality: one + - name: peer_groups + peer: InfraBGPPeerGroup + cardinality: many + kind: Component + - name: sessions + peer: InfraBGPSession + cardinality: many + kind: Component + + # ================================================================ + # BGP Peer Group + # ================================================================ + - name: BGPPeerGroup + namespace: Infra + description: BGP peer group template + label: BGP Peer Group + icon: mdi--account-group + include_in_menu: false + uniqueness_constraints: + - ["bgp_config", "name__value"] + human_friendly_id: + - bgp_config__router_id__value + - name__value + display_label: "{{ name__value }}" + attributes: + - name: name + kind: Text + description: Peer group name (e.g., underlay, evpn) + - name: description + kind: Text + optional: true + - name: update_source + kind: Text + optional: true + description: Update source interface (e.g., Loopback0) + - name: ebgp_multihop + kind: Number + optional: true + description: eBGP multihop TTL + - name: send_community + kind: Dropdown + default_value: none + choices: + - name: none + label: None + - name: standard + label: Standard + - name: extended + label: Extended + - name: both + label: Both + - name: next_hop_self + kind: Boolean + default_value: false + - name: next_hop_unchanged + kind: Boolean + default_value: false + description: Keep next-hop unchanged (for route reflector) + - name: maximum_routes + kind: Number + optional: true + - name: maximum_routes_warning_only + kind: Boolean + default_value: true + - name: peer_group_type + kind: Dropdown + default_value: underlay + choices: + - name: underlay + label: Underlay IPv4 + - name: underlay_ibgp + label: Underlay iBGP + - name: evpn + label: EVPN Overlay + relationships: + - name: bgp_config + peer: InfraBGPRouterConfig + cardinality: one + kind: Parent + optional: false + - name: remote_asn + peer: InfraAutonomousSystem + cardinality: one + optional: true + + # ================================================================ + # BGP Session (Neighbor) + # ================================================================ + - name: BGPSession + namespace: Infra + description: BGP neighbor session + label: BGP Session + icon: mdi--connection + include_in_menu: false + uniqueness_constraints: + - ["bgp_config", "peer_address__value"] + human_friendly_id: + - bgp_config__router_id__value + - peer_address__value + display_label: "{{ peer_address__value }}" + attributes: + - name: peer_address + kind: IPHost + description: Neighbor IP address + - name: description + kind: Text + optional: true + - name: enabled + kind: Boolean + default_value: true + relationships: + - name: bgp_config + peer: InfraBGPRouterConfig + cardinality: one + kind: Parent + optional: false + - name: peer_group + peer: InfraBGPPeerGroup + cardinality: one + optional: true + - name: remote_asn + peer: InfraAutonomousSystem + cardinality: one + optional: true + description: Override peer group remote-as + - name: peer_device + peer: InfraDevice + cardinality: one + optional: true + description: Remote peer device (for documentation) + - name: vrf + peer: InfraVRF + cardinality: one + kind: Attribute + optional: true + description: VRF context for this session (null = global BGP process) + + # ================================================================ + # BGP Address Family Configuration + # ================================================================ + - name: BGPAddressFamily + namespace: Infra + description: BGP address family configuration + label: BGP Address Family + icon: mdi--format-list-bulleted + include_in_menu: false + display_label: "{{ afi__value }}" + attributes: + - name: afi + kind: Dropdown + choices: + - name: ipv4 + label: IPv4 + - name: ipv6 + label: IPv6 + - name: evpn + label: EVPN + description: Address Family Identifier + - name: safi + kind: Dropdown + default_value: unicast + choices: + - name: unicast + label: Unicast + - name: multicast + label: Multicast + description: Sub Address Family Identifier + relationships: + - name: bgp_config + peer: InfraBGPRouterConfig + cardinality: one + kind: Parent + optional: false + - name: active_peer_groups + peer: InfraBGPPeerGroup + cardinality: many + description: Peer groups activated in this AF + - name: networks + peer: InfraIPAddress + cardinality: many + optional: true + description: Networks to advertise diff --git a/schemas/extensions.yml b/schemas/extensions.yml new file mode 100644 index 0000000..d2d8c9e --- /dev/null +++ b/schemas/extensions.yml @@ -0,0 +1,164 @@ +# Extensions Schema for EVPN-VXLAN Fabric +# Custom attributes and fabric-specific configurations +--- +version: "1.0" +nodes: + # ================================================================ + # Fabric (Top-level container for all fabric objects) + # ================================================================ + - name: Fabric + namespace: Infra + description: EVPN-VXLAN Fabric definition + label: Fabric + icon: mdi--vector-polygon + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: "{{ name__value }}" + attributes: + - name: name + kind: Text + unique: true + description: Fabric name (e.g., arista-evpn-fabric) + - name: description + kind: Text + optional: true + - name: underlay_protocol + kind: Dropdown + default_value: ebgp + choices: + - name: ebgp + label: eBGP + - name: ospf + label: OSPF + - name: isis + label: IS-IS + - name: overlay_protocol + kind: Dropdown + default_value: evpn + choices: + - name: evpn + label: EVPN + - name: ingress_replication + label: Ingress Replication + - name: anycast_gateway_mac + kind: Text + optional: true + description: "Shared MAC for anycast gateway (format: xxxx.xxxx.xxxx)" + relationships: + - name: spine_asn + peer: InfraAutonomousSystem + cardinality: one + optional: true + description: AS used by spine layer + - name: sites + peer: LocationSite + cardinality: many + optional: true + + # ================================================================ + # Underlay P2P Link + # ================================================================ + - name: UnderlayLink + namespace: Infra + description: Point-to-point underlay link between devices + label: Underlay Link + icon: mdi--cable-data + include_in_menu: false + display_label: "{{ description__value }}" + attributes: + - name: description + kind: Text + description: Link description (e.g., spine1:eth1 <-> leaf1:eth11) + - name: mtu + kind: Number + default_value: 9214 + relationships: + - name: fabric + peer: InfraFabric + cardinality: one + kind: Parent + optional: false + - name: local_device + peer: InfraDevice + identifier: underlay_link_local_device + cardinality: one + direction: outbound + - name: local_interface + peer: InfraInterfaceEthernet + identifier: underlay_link_local_interface + cardinality: one + direction: outbound + - name: local_ip_address + peer: InfraIPAddress + identifier: underlay_link_local_ip + cardinality: one + direction: outbound + - name: remote_device + peer: InfraDevice + identifier: underlay_link_remote_device + cardinality: one + direction: outbound + - name: remote_interface + peer: InfraInterfaceEthernet + identifier: underlay_link_remote_interface + cardinality: one + direction: outbound + - name: remote_ip_address + peer: InfraIPAddress + identifier: underlay_link_remote_ip + cardinality: one + direction: outbound + + # ================================================================ + # Host Connection + # ================================================================ + - name: HostConnection + namespace: Infra + description: Host connection to fabric (single or dual-homed) + label: Host Connection + icon: mdi--desktop-tower + include_in_menu: false + human_friendly_id: + - hostname__value + display_label: "{{ hostname__value }}" + attributes: + - name: hostname + kind: Text + description: Connected host name + - name: description + kind: Text + optional: true + - name: connection_type + kind: Dropdown + default_value: dual_homed + choices: + - name: single_homed + label: Single-Homed + - name: dual_homed + label: Dual-Homed (MLAG) + - name: lacp_mode + kind: Dropdown + default_value: active + choices: + - name: active + label: Active + - name: passive + label: Passive + relationships: + - name: vlans + peer: InfraVLAN + cardinality: many + description: VLANs allowed on this connection + - name: mlag_interface + peer: InfraMlagInterface + cardinality: one + optional: true + description: MLAG interface for dual-homed + - name: lag_interface + peer: InfraInterfaceLag + cardinality: one + optional: true + description: LAG for single-homed diff --git a/schemas/mlag.yml b/schemas/mlag.yml new file mode 100644 index 0000000..7a6c220 --- /dev/null +++ b/schemas/mlag.yml @@ -0,0 +1,141 @@ +# MLAG Schema for EVPN-VXLAN Fabric +# Defines MLAG domain and peer configuration +--- +version: "1.0" +nodes: + # ================================================================ + # MLAG Domain + # ================================================================ + - name: MlagDomain + namespace: Infra + description: MLAG domain configuration for leaf pair + label: MLAG Domain + icon: mdi--link-variant + include_in_menu: false + human_friendly_id: + - domain_id__value + display_label: "{{ domain_id__value }}" + attributes: + - name: domain_id + kind: Text + description: MLAG domain identifier (e.g., leafs) + - name: description + kind: Text + optional: true + - name: virtual_mac + kind: Text + description: "Shared virtual MAC (format: xxxx.xxxx.xxxx)" + - name: heartbeat_vrf + kind: Text + default_value: mgmt + description: VRF for heartbeat (typically mgmt) + - name: dual_primary_detection + kind: Boolean + default_value: true + - name: dual_primary_delay + kind: Number + default_value: 10 + description: Delay in seconds before dual-primary action + - name: dual_primary_action + kind: Dropdown + default_value: errdisable + choices: + - name: errdisable + label: Error Disable Interfaces + - name: none + label: No Action + relationships: + - name: devices + peer: InfraDevice + cardinality: many + min_count: 2 + max_count: 2 + description: MLAG peer devices + - name: peer_vlan + peer: InfraVLAN + identifier: mlag_domain_peer_vlan + cardinality: one + direction: outbound + description: VLAN for MLAG peer-link control traffic + - name: ibgp_vlan + peer: InfraVLAN + identifier: mlag_domain_ibgp_vlan + cardinality: one + direction: outbound + optional: true + description: VLAN for iBGP peering between MLAG peers + + # ================================================================ + # MLAG Peer Configuration (per device) + # ================================================================ + - name: MlagPeerConfig + namespace: Infra + description: MLAG configuration on a specific device + label: MLAG Peer Config + icon: mdi--server-network + include_in_menu: false + human_friendly_id: + - device__name__value + display_label: "{{ local_interface_ip__value }}" + attributes: + - name: local_interface_ip + kind: IPNetwork + description: IP on MLAG peer VLAN SVI + - name: peer_address + kind: IPHost + description: Peer's MLAG SVI IP address + - name: heartbeat_peer_ip + kind: IPHost + description: Peer's management IP for heartbeat + relationships: + - name: device + peer: InfraDevice + cardinality: one + kind: Parent + optional: false + - name: mlag_domain + peer: InfraMlagDomain + cardinality: one + - name: local_interface + peer: InfraInterfaceVlan + cardinality: one + description: Local MLAG SVI + - name: peer_link + peer: InfraInterfaceLag + cardinality: one + description: Peer-link port-channel + + # ================================================================ + # MLAG Interface (MLAG-enabled LAG) + # ================================================================ + - name: MlagInterface + namespace: Infra + description: MLAG interface configuration + label: MLAG Interface + icon: mdi--ethernet-cable + include_in_menu: false + display_label: "{{ mlag_id__value }}" + attributes: + - name: mlag_id + kind: Number + description: MLAG interface ID + - name: description + kind: Text + optional: true + - name: lacp_fallback_timeout + kind: Number + default_value: 5 + description: LACP fallback timeout in seconds + - name: lacp_fallback_individual + kind: Boolean + default_value: true + relationships: + - name: mlag_domain + peer: InfraMlagDomain + cardinality: one + - name: lag_interfaces + peer: InfraInterfaceLag + cardinality: many + min_count: 1 + max_count: 2 + description: LAG interfaces on each MLAG peer diff --git a/schemas/vlan_vxlan.yml b/schemas/vlan_vxlan.yml new file mode 100644 index 0000000..b17cf82 --- /dev/null +++ b/schemas/vlan_vxlan.yml @@ -0,0 +1,221 @@ +# VLAN and VXLAN Schema for EVPN-VXLAN Fabric +# Defines VLAN, VNI mappings, and VTEP configuration +--- +version: "1.0" +nodes: + # ================================================================ + # VLAN + # ================================================================ + - name: VLAN + namespace: Infra + description: Virtual LAN configuration + label: VLAN + icon: mdi--lan-connect + include_in_menu: false + human_friendly_id: + - vlan_id__value + order_by: + - vlan_id__value + display_label: "{{ vlan_id__value }} ({{ name__value }})" + attributes: + - name: vlan_id + kind: Number + unique: true + description: VLAN ID (1-4094) + - name: name + kind: Text + description: VLAN name + - name: description + kind: Text + optional: true + - name: status + kind: Dropdown + default_value: active + choices: + - name: active + label: Active + color: "#22c55e" + - name: reserved + label: Reserved + color: "#3b82f6" + - name: deprecated + label: Deprecated + color: "#ef4444" + - name: vlan_type + kind: Dropdown + default_value: standard + choices: + - name: standard + label: Standard + - name: mlag_peer + label: MLAG Peer + - name: mlag_ibgp + label: MLAG iBGP + description: VLAN purpose + - name: trunk_groups + kind: List + optional: true + description: "Trunk groups restricting VLAN propagation (e.g., mlag-peer)" + - name: stp_enabled + kind: Boolean + default_value: true + description: "Enable spanning-tree on this VLAN (set false for MLAG peer VLANs)" + relationships: + - name: vni + peer: InfraVNI + cardinality: one + optional: true + description: Associated L2VNI for VXLAN + - name: site + peer: LocationSite + cardinality: one + optional: true + + # ================================================================ + # VNI (VXLAN Network Identifier) + # ================================================================ + - name: VNI + namespace: Infra + description: VXLAN Network Identifier + label: VNI + icon: mdi--tunnel-outline + include_in_menu: false + human_friendly_id: + - vni__value + order_by: + - vni__value + display_label: "{{ vni__value }}" + attributes: + - name: vni + kind: Number + unique: true + description: VNI value (1-16777215) + - name: description + kind: Text + optional: true + - name: vni_type + kind: Dropdown + default_value: l2vni + choices: + - name: l2vni + label: L2 VNI + color: "#3b82f6" + - name: l3vni + label: L3 VNI + color: "#22c55e" + description: VNI type for EVPN + relationships: + - name: vlan + peer: InfraVLAN + cardinality: one + optional: true + description: Associated VLAN for L2VNI + - name: vrf + peer: InfraVRF + cardinality: one + optional: true + description: Associated VRF for L3VNI + + # ================================================================ + # VTEP (VXLAN Tunnel Endpoint) + # ================================================================ + - name: VTEP + namespace: Infra + description: VXLAN Tunnel Endpoint configuration + label: VTEP + icon: mdi--server-network-outline + include_in_menu: false + human_friendly_id: + - device__name__value + display_label: "{{ source_address__value }}" + attributes: + - name: source_address + kind: IPHost + description: VTEP source IP (typically from Loopback1) + - name: udp_port + kind: Number + default_value: 4789 + - name: learn_restrict + kind: Dropdown + default_value: any + choices: + - name: any + label: Any + - name: flood + label: Flood + description: MAC learning restriction mode + relationships: + - name: device + peer: InfraDevice + cardinality: one + kind: Parent + optional: false + - name: source_interface + peer: InfraInterfaceLoopback + cardinality: one + description: Source interface for VTEP + - name: vlan_vni_mappings + peer: InfraVlanVniMapping + cardinality: many + kind: Component + + # ================================================================ + # VLAN to VNI Mapping (per-device) + # ================================================================ + - name: VlanVniMapping + namespace: Infra + description: VLAN to VNI mapping on a VTEP + label: VLAN-VNI Mapping + icon: mdi--swap-horizontal + include_in_menu: false + display_label: "{{ description__value }}" + attributes: + - name: description + kind: Text + optional: true + description: "Mapping description (e.g., VLAN 40 <-> VNI 10040)" + relationships: + - name: vtep + peer: InfraVTEP + cardinality: one + kind: Parent + optional: false + - name: vlan + peer: InfraVLAN + cardinality: one + - name: vni + peer: InfraVNI + cardinality: one + + # ================================================================ + # EVPN Instance (per VLAN) + # ================================================================ + - name: EVPNInstance + namespace: Infra + description: EVPN instance configuration (route targets for L2 extension) + label: EVPN Instance + icon: mdi--cloud-sync + include_in_menu: false + display_label: "{{ route_distinguisher__value }}" + attributes: + - name: route_distinguisher + kind: Text + description: "Route Distinguisher (format: ASN:VNI or IP:VNI)" + - name: route_target_import + kind: Text + description: "Import Route Target (format: VNI:VNI)" + - name: route_target_export + kind: Text + description: "Export Route Target (format: VNI:VNI)" + - name: redistribute_learned + kind: Boolean + default_value: true + relationships: + - name: vlan + peer: InfraVLAN + cardinality: one + - name: device + peer: InfraDevice + cardinality: one + kind: Parent + optional: false diff --git a/schemas/vrf.yml b/schemas/vrf.yml new file mode 100644 index 0000000..68e1569 --- /dev/null +++ b/schemas/vrf.yml @@ -0,0 +1,124 @@ +# VRF Schema for EVPN-VXLAN Fabric +# Defines VRF and Route Target configuration for L3 VPN +--- +version: "1.0" +nodes: + # ================================================================ + # VRF (Virtual Routing and Forwarding) + # ================================================================ + - name: VRF + namespace: Infra + description: Virtual Routing and Forwarding instance + label: VRF + icon: mdi--router + include_in_menu: false + human_friendly_id: + - name__value + order_by: + - name__value + display_label: "{{ name__value }}" + attributes: + - name: name + kind: Text + unique: true + description: VRF name + - name: description + kind: Text + optional: true + - name: route_distinguisher + kind: Text + optional: true + description: "Route Distinguisher (format: ASN:NN or IP:NN)" + - name: vrf_id + kind: Number + optional: true + description: VRF table ID + relationships: + - name: l3vni + peer: InfraVNI + cardinality: one + optional: true + description: L3 VNI for symmetric IRB + - name: import_targets + peer: InfraRouteTarget + identifier: vrf_import_targets + cardinality: many + direction: outbound + optional: true + - name: export_targets + peer: InfraRouteTarget + identifier: vrf_export_targets + cardinality: many + direction: outbound + optional: true + - name: interfaces + peer: InfraInterface + cardinality: many + optional: true + description: Interfaces assigned to this VRF + + # ================================================================ + # Route Target + # ================================================================ + - name: RouteTarget + namespace: Infra + description: BGP Route Target for VPN import/export + label: Route Target + icon: mdi--target + include_in_menu: false + human_friendly_id: + - target__value + order_by: + - target__value + display_label: "{{ target__value }}" + attributes: + - name: target + kind: Text + unique: true + description: "Route Target value (format: ASN:NN or IP:NN)" + - name: description + kind: Text + optional: true + + # ================================================================ + # VRF Device Assignment + # ================================================================ + - name: VRFDeviceAssignment + namespace: Infra + description: VRF assignment to a specific device + label: VRF Assignment + icon: mdi--router-network + include_in_menu: false + human_friendly_id: + - device__name__value + - vrf__name__value + display_label: "{{ device__name__value }} - {{ vrf__name__value }}" + attributes: + - name: route_distinguisher + kind: Text + optional: true + description: "Device-specific RD (overrides VRF default)" + relationships: + - name: vrf + peer: InfraVRF + cardinality: one + optional: false + - name: device + peer: InfraDevice + cardinality: one + kind: Parent + optional: false + - name: import_targets + peer: InfraRouteTarget + identifier: vrf_assignment_import_targets + cardinality: many + direction: outbound + optional: true + description: Device-specific import RTs + - name: export_targets + peer: InfraRouteTarget + identifier: vrf_assignment_export_targets + cardinality: many + direction: outbound + optional: true + description: Device-specific export RTs