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>
CI gate example — fail a PR before it reaches real hardware
Pattern: run your network automation (Ansible / Terraform / Python / aci-py)
against a running aci-sim, then assert the MIT reached the expected
end state. A failed assertion fails the build. No hardware, deterministic,
resettable.
Files
assert_mit.py— the gate. Logs into the sim (or a real APIC) and checks that the objects your change was supposed to create/modify are present. Exit code = number of failed checks, so CI can gate on it.github-actions-aci-gate.yml— a copy-pasteable GitHub Actions workflow wiring the three steps (start sim → run automation → assert).
assert_mit.py check syntax
Each --expect (repeatable):
| Check | Meaning |
|---|---|
class:<cls>[>=N] |
the class query returns ≥ N objects (default N=1) |
mo:<dn> |
GET /api/mo/<dn>.json returns the object (exists) |
absent:<dn> |
the MO does NOT exist (e.g. after a state=absent / delete) |
attr:<dn>:<key>=<val> |
the MO at <dn> has attribute <key> == <val> |
Try it locally
# 1. start the sim (ships MS-TN1 / SF-TN1-* tenants from topology.yaml)
aci-sim run &
# 2. assert the seeded end state
python examples/ci/assert_mit.py --host 127.0.0.1:8443 \
--expect "class:fvTenant>=1" \
--expect "mo:uni/tn-MS-TN1" \
--expect "attr:uni/tn-MS-TN1:name=MS-TN1" \
--expect "absent:uni/tn-DoesNotExist"
# → 4/4 checks passed, exit 0
Because assert_mit.py speaks the plain APIC REST API, you can point it at a
real APIC too (--host <apic-ip> --user ... --password ...) to diff
sim-vs-real, or to validate the same expectations against staging gear.
Real-world shape
In practice step 2 is your existing playbook (see ../ansible/smoke.yml for a
full cisco.aci create→idempotency example), and step 3's --expect list is the
objects that playbook is contracted to produce. When someone edits the playbook
in a way that stops creating those objects, the PR goes red — before anything
touches production.