Files
aci-sim/aci_sim/build/orchestrator.py
T
taodengandClaude Fable 5 7e9a175ce6 Initial public release — aci-sim v0.16.0
aci-sim is a stateful REST simulator of a 2-site Cisco ACI fabric
(per-site APIC + ND/NDO management planes) — for testing ACI automation
(Ansible cisco.aci / cisco.mso, aci-py, custom REST clients) and CI gates
without real hardware.

Highlights: APIC + NDO REST surface served from one in-memory MIT built
from a declarative topology.yaml; port mode + sandbox (real per-device IPs
on :443) run modes; LLDP/CDP neighbor visibility; NDO->APIC deploy mirror
(multi-site templates materialize onto the target sites' APIC stores);
real-APIC fvBD default attributes; file-backed state save/restore; an
`aci-sim` CLI (validate/show/graph/run/new/init/lldp); and an 888-test suite.

History squashed for the public release.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 20:06:36 +10:00

90 lines
3.2 KiB
Python

"""Orchestrator — assemble a full per-site MITStore from the topology.
Every sub-builder is a pure function of (topology, site) that only ADDS MOs and
never reads another builder's output, so the order below is for readability only.
"""
from __future__ import annotations
from aci_sim.build import (
access,
cabling,
endpoints,
fabric,
health_faults,
interfaces,
l3out,
mgmt,
overlay,
routing,
tenants,
underlay,
userprofile,
zoning,
)
from aci_sim.mit.store import MITStore
from aci_sim.topology.schema import Site, Topology
# fabric first (nodes), then wiring/underlay/overlay, then tenant config,
# then L3Out (needs border leaves), endpoints, and finally health/faults.
#
# Batch-1 note: `endpoints` now runs BEFORE `tenants` (moved from after
# `l3out`) so tenants.py's fvRsPathAtt (static binding) can read back the
# real fvCEp/fabricPathDn each EPG's endpoints already got from the store,
# instead of re-deriving port assignment logic independently (which risked
# silently drifting out of sync with endpoints.py). Every other builder
# remains a pure "only ADDS MOs, never reads another builder's output"
# function per the orchestrator's original contract — endpoints.py itself
# still only adds and doesn't read from tenants/access/l3out, so moving it
# earlier is safe (its only cross-module dependency, _HOST_PORTS, comes from
# `interfaces`, which still runs first).
#
# Batch-2 note: `zoning` runs AFTER both `tenants` (needs fvAEPg.pcTag/
# fvCtx.scope, and reuses tenants.py's pctag_for/scope_id_for helpers) and
# `endpoints` (needs fvCEp to know which leaves host each EPG's local
# endpoints) — same read-back pattern as tenants.py's fvRsPathAtt, so zoning
# is placed right after tenants in the list (endpoints already ran earlier).
# `overlay.build_vpn_routes` (bgpVpnRoute/bgpPath) has the same fvRsBd/fvCEp
# read-back dependency, so it also runs post-tenants/endpoints — as a SECOND
# call into the `overlay` module, alongside (not instead of) its early
# `overlay.build` call which only needs fabric/topology data and stays in
# its original early slot for BGP peer setup.
#
# OOB-gateway PR: `mgmt` builds the mgmt-tenant OOB scaffolding
# (fvTenant(mgmt)/mgmtMgmtP/mgmtOoB/mgmtRsOoBStNode) that carries
# site.oob_gateway on a real MO. It only needs topology data (site.all_nodes(),
# oob_ip(), site.controller_oob_ips()) — no read-back from another builder's
# store output — so, like every other builder here, it is placed for
# readability only; it is grouped right after `fabric` since both build
# node-identity/OOB data.
_BUILDERS = [
fabric,
mgmt,
cabling,
underlay,
overlay,
interfaces,
endpoints,
tenants,
zoning,
access,
l3out,
routing,
health_faults,
userprofile,
]
def build_site(topo: Topology, site: Site) -> MITStore:
"""Build and return a fully-populated MITStore for one site."""
store = MITStore()
for mod in _BUILDERS:
mod.build(topo, site, store)
overlay.build_vpn_routes(topo, site, store)
return store
def build_all(topo: Topology) -> dict[str, MITStore]:
"""Build every site → ``{site_name: MITStore}``."""
return {site.name: build_site(topo, site) for site in topo.sites}