Skip to main content

Architecture

This page summarises the data flow and the role of each directory.

Data flow​

Schema definition → Bootstrap data → Generators → Transforms → Artifacts
↓
Checks

Directory map​

PathPurpose
schemas/base/Core node definitions copied from schema-library
schemas/extensions/VRF, routing, BGP, topology extensions from schema-library
schemas/sp/SP-demo-specific schemas (MPLS, L3VPN service, PE role)
objects/Pre-loaded bootstrap data (PEs, backbone, pools, tenants)
generators/L3VpnGenerator materialises VRF + interfaces + IPs
transforms/One Python+Jinja transform per vendor + the clab transform
templates/Jinja2 templates (one per vendor + clab + macros)
checks/Five checks gating the proposed-change pipeline (incl. Batfish)
service_catalog/Streamlit sidecar (Dashboard + Create L3VPN)
queries/GraphQL queries used by generators, transforms, checks
menus/Sidebar menu configuration
lab/Runtime-only; not committed

Backbone​

The MPLS backbone is static demo data and is dataset-specific — each dataset ships its own backbone. The default financial dataset has 8 PEs, all Arista cEOS (pe-01…pe-08), in a partial mesh (an 8-node ring plus four cross-chords = 12 p2p links, every PE at degree 3) over a full iBGP mesh. The isp dataset has 4 PEs, one per vendor, in a full mesh (6 p2p links). Both run ISIS L2 for the underlay and LDP for label distribution. This keeps the demo focused on the L3VPN-as-a-service story. To add or move PEs, edit objects/datasets/<dataset>/60_backbone.yml (and 70_topology.yml for the p2p links) by hand and rerun invoke bootstrap.

L3VPN service flow​

When an operator creates an L3VPN through the Streamlit catalog:

  1. The catalog allocates a vpn_id from the vpn_id_pool number pool.
  2. It opens a feature branch and creates ServiceL3Vpn + one or more ServiceL3VpnSite objects.
  3. It adds the ServiceL3Vpn to the l3vpns group. A group-membership trigger (objects/events/00_triggers.yml) fires L3VpnGenerator on the branch, which materialises the VRF, route targets, PE-CE interfaces, IP addresses, and an eBGP session if the routing protocol is set to ebgp. The generator runs event-driven rather than in the proposed-change pipeline (execute_in_proposed_change: false) so its data lands before artifacts render — otherwise the pipeline races and the configuration diff comes out empty.
  4. Once the generator finishes, per-PE configuration artifacts are rendered by the transform layer against the now-complete branch data.
  5. The catalog opens a CoreProposedChange targeting main.
  6. The proposed-change checks run in the pipeline — any failure blocks the merge.
  7. The operator reviews the diff in the Infrahub UI and merges.

See services/l3vpn for the full service reference.

Transform layer​

Each vendor has a dedicated Python transform module (in transforms/) backed by a Jinja2 template (in templates/). The transform fetches the full PE state via GraphQL and renders a single, complete device configuration fragment. The mapping is:

VendorTransformTemplate
Arista EOStransforms/pe_arista_eos.pytemplates/pe_arista_eos.j2
Cisco IOS-XRtransforms/pe_cisco_iosxr.pytemplates/pe_cisco_iosxr.j2
Juniper Junostransforms/pe_juniper_junos.pytemplates/pe_juniper_junos.j2
Nokia SR OStransforms/pe_nokia_sros.pytemplates/pe_nokia_sros.j2
Containerlabtransforms/clab_topology.pytemplates/clab_topology.j2

All four per-vendor PE transforms are still in play: the isp dataset has one PE per vendor and exercises all of them. The default financial backbone is all-Arista, so it only renders through pe_arista_eos.

Checks​

Five checks gate the proposed-change pipeline:

CheckWhat it enforces
l3vpn_overlapNo duplicate VPN IDs across active L3VPNs
l3vpn_site_subnetCustomer subnet is reachable / not already allocated in the same VRF
pe_interface_allocThe nominated PE interface is free (status = free)
backbone_session_countEvery PE has its full-mesh complement of Nāˆ’1 iBGP sessions (N = PE count: 7 each for the 8-PE financial backbone, 3 each for the 4-PE isp backbone)
batfish_backboneBatfish-driven static validation of rendered backbone configs (parse status, undefined references, BGP session compatibility, IS-IS adjacency mesh). See Batfish validation.

Per-vendor interface name macros​

The schema uses an abstract interface name (Ethernet1, Ethernet2, …); each vendor template translates that to the platform-native form via a macro in templates/_macros.j2:

VendorMacroEthernet1 becomes
Arista EOS(no translation)Ethernet1
Cisco IOS-XRiosxr_ifaceGigabitEthernet0/0/0/0
Juniper Junosjunos_ifacege-0/0/0
SR Linux (lab substitute)srl_ifaceethernet-1/1

Without these macros the rendered IOS-XR / Junos configs would carry Arista-style Ethernet<N> interface names — invalid on the target platform and reported as parse errors by BatfishBackboneCheck.

Schema layering​

Schemas are loaded in three passes so each layer can reference the previous:

invoke bootstrap
└── infrahubctl schema load schemas/base/
└── infrahubctl schema load schemas/extensions/
└── infrahubctl schema load schemas/sp/

The SP layer adds ServiceL3Vpn, ServiceL3VpnSite, TopologyMplsBackbone, MplsIsisProcess, MplsLdpProcess, and MplsBgpProcess. See schema-reference for field-level details.