Skip to main content

Architecture overview

Developer Guide

Assumes familiarity with Infrahub and Python. If you only want to use the system, start with Quick Start.

The solution is a repository of schemas, generators, and transforms loaded on top of the Infrahub platform. The sections below cover its components, data model, and the generator and transform pipelines.

System components​

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Infrahub Platform β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Neo4j Graph β”‚ β”‚ PostgreSQL β”‚ β”‚ Object Store β”‚ β”‚
β”‚ β”‚ Database β”‚ β”‚ (Metadata) β”‚ β”‚ (Hostvars, Configs) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Redis Cache β”‚ β”‚ RabbitMQ β”‚ β”‚ Infrahub Server β”‚ β”‚
β”‚ β”‚ β”‚ β”‚ (Queue) β”‚ β”‚ (API + Git Backend) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Repository Solution β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Schemas β”‚ β”‚ Generators β”‚ β”‚ Transforms β”‚ β”‚
β”‚ β”‚ (YAML DSL) β”‚ β”‚ (Python) β”‚ β”‚ (Python + Jinja2) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Object Data β”‚ β”‚ Queries β”‚ β”‚ Core Library β”‚ β”‚
β”‚ β”‚ (Seed YAML) β”‚ β”‚ (GraphQL) β”‚ β”‚ (src/solution_arista_avd) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Data model hierarchy​

The system models a 3-tier datacenter network fabric:

NetworkFabric (e.g., "Fabric-L3LS-MultiPod-A")
β”œβ”€β”€ NetworkPod (e.g., "Pod-A1", "Pod-A2")
β”‚ β”œβ”€β”€ LocationRack (e.g., "Rack-A1-01", "Rack-A1-02")
β”‚ β”‚ └── DcimDevice [leaf] (e.g., "leaf-A1-01-1")
β”‚ β”‚ └── InterfacePhysical (e.g., "Ethernet1")
β”‚ β”‚ β”œβ”€β”€ NetworkLink β†’ remote interface
β”‚ β”‚ └── IpamIPAddress
β”‚ └── DcimDevice [spine] (e.g., "spine-A1-1")
└── DcimDevice [super_spine] (e.g., "ss-A-1")

IP address management​

Fabric-level pool allocation:

NetworkFabric
β”œβ”€β”€ loopback_pool: CoreIPPrefixPool
β”‚ └── Internal CoreIPAddressPool wrapper: Loopback0 addresses
β”œβ”€β”€ vtep_pool: CoreIPPrefixPool
β”‚ └── Internal CoreIPAddressPool wrapper: VTEP loopback addresses
β”œβ”€β”€ uplink_pool: CoreIPPrefixPool
β”‚ └── Prefix allocations for point-to-point links
β”œβ”€β”€ mgmt_pool: CoreIPAddressPool
β”‚ └── OOB management addresses
β”œβ”€β”€ CoreNumberPool: ASN Pool (65000-65999)
β”‚ └── Tier-aware eBGP ASN allocation: shared super-spine ASN per fabric, shared spine ASN per pod, leaf ASNs per device or MLAG domain
└── CoreNumberPool: Node ID Pool (1-65535)
└── Per-device unique identifier

Generator pipeline​

Generators run in sequence to build infrastructure:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 1. FabricGenerator β”‚ Triggered on: NetworkFabric
β”‚ - Resolve fabric pools β”‚ Creates: Super-spine devices
β”‚ - Create super-spines β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 2. PodGenerator β”‚ Triggered on: NetworkPod
β”‚ - Create spine devices β”‚ Creates: Spine switches
β”‚ - Link to super-spines β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 3. RackGenerator β”‚ Triggered on: LocationRack
β”‚ - Create leaf devices β”‚ Creates: Leaf switches
β”‚ - Link to spines β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 4. AVD Generators β”‚ Triggered on: NetworkFabric/Device
β”‚ - Build hostvars β”‚ Creates: AVD configs
β”‚ - Generate struct cfg β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Two further generators sit outside this chain:

  • ServerCablingGenerator (on ComputePhysicalServer) cables a server to the leaves in its rack, then reconciles its LAGs and VLANs and re-triggers hostvar generation for those leaves.
  • BackfillStructuredConfigGenerator (on AvdStructuredConfigFile) runs in the opposite direction, reading AVD's structured-config output back into IPAM, interface, BGP, and routing objects.

See Generators.

Transform pipeline​

Transforms convert data to artifacts:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ GraphQL Query β”‚ ──▢ β”‚ Transform Logic β”‚ ──▢ β”‚ Output Artifactβ”‚
β”‚ (Data Fetch) β”‚ β”‚ (Python/Jinja2) β”‚ β”‚ (Config/Doc) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Examples:
- DcimInterface β†’ ComputedInterfaceDescription β†’ "β†’ device:interface"
- NetworkFabric β†’ CablingPlan β†’ CSV cabling matrix
- DcimDevice β†’ AvdEosConfig β†’ EOS CLI configuration
- NetworkFabric β†’ AvdFabricDoc β†’ Markdown documentation
- DcimDevice β†’ AvdAntaCatalog β†’ ANTA test catalog (YAML)
- NetworkFabric β†’ ContainerLabTopology β†’ ContainerLab topology (YAML)

Validation pipeline​

Alongside transforms, proposed-change validation runs checks β€” Python routines that report pass, information, or error rather than producing an artifact. The repository ships one, cv-config-validation, which deploys the rendered EOS configs into a CloudVision workspace and blocks the proposed change on a failed build. See Checks.

Checksum-based change detection​

Generators use checksums to avoid redundant regeneration:

class GeneratorMixin:
def calculate_checksum(self, related_node_ids: list[str]) -> str:
"""Create deterministic hash from related node IDs"""
return hashlib.sha256("".join(sorted(related_node_ids))).hexdigest()

# Usage in generator:
new_checksum = self.calculate_checksum([pod.id, device.id, ...])
if new_checksum != target.checksum:
# Regenerate
target.checksum = new_checksum

Configuration files​

FileRole
.infrahub.ymlRegister queries, generators, transforms, artifacts
repository.ymlDefine repository as CoreRepository in Infrahub
docker-compose.ymlOrchestrate Infrahub services
pyproject.tomlPython dependencies and tool configuration

Docker service stack​

services:
infrahub-server: # Main API server
infrahub-git: # Git backend for repository
infrahub-worker: # Async task execution
neo4j: # Graph database
postgres: # Relational metadata
redis: # Cache layer
rabbitmq: # Message queue

Development workflow​

1. Edit schema (schemas/*.yml)
↓
2. Load schema: inv load-schema
↓
3. Edit generator/transform code
↓
4. Test: pytest tests/
↓
5. Lint: inv lint
↓
6. Reload: inv load
↓
7. Run generators via UI

Pool role resolution​

Fabric and pod IP pool intent is role-driven. NetworkFabric.fabric_ip_pools is the authoritative fabric collection for Management, Loopback, Loopback VTEP, Fabric Point-to-Point, DCI, and Fabric Supernet roles. NetworkPod.pod_ip_pools is the authoritative pod collection for pod-scoped Loopback, Loopback VTEP, Fabric Point-to-Point, MLAG, and MLAG Peering roles.

Validation resolves a pool's purpose from the roles on its backing IpamPrefix resources. The proposed-change check rejects duplicate authoritative roles, mixed-role pools, non-IP pool members, pod management pools, and pod prefixes that are not contained by the matching parent fabric pool.

During migration, legacy fabric and pod pool relationships are still present and object data is dual-populated. Generators prefer collection relationships and fall back to legacy relationships only when needed.

Source​

  • Infrahub configuration: .infrahub.yml β€” the queries, generators, transforms, and artifact definitions registered with Infrahub.
  • Schemas: schemas/ β€” the data model.
  • Generators: generators/ β€” Python generator classes.
  • Transforms: transforms/ β€” Python transform classes and templates.
  • Checks: checks/ β€” proposed-change validation, currently CloudVision.
  • Playbooks: ansible/ β€” the tree Semaphore runs for EOS config deployment and ContainerLab staging.
  • Core library: src/solution_arista_avd/ β€” shared protocols, AVD utilities, sorting, addressing.
  • Service portal: service_catalog/ β€” Streamlit UI that orchestrates the portal workflows.