Files
aci-sim/docs/MANUAL.md
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

16 KiB

aci-sim — Operation Manual / 操作手册

A stateful REST simulator of a Cisco ACI/NDO management plane (per-site APIC + Nexus Dashboard/NDO). It answers moquery/class/DN queries computed from an in-memory MIT, applies POST/DELETE mutations atomically, and models aaaLogin + NDO orchestration — enough fidelity to test Ansible playbooks, aci-py, autoACI, or any tool that speaks the APIC/NDO REST API, with no hardware, no data plane, fully deterministic and resettable.

一个有状态的 Cisco ACI/NDO 管理平面 REST 模拟器(每站点一个 APIC + Nexus Dashboard/NDO)。 它从内存中的 MIT 计算 moquery/class/DN 查询的响应、原子地应用 POST/DELETE 变更、模拟 aaaLogin 与 NDO 编排 —— 保真度足以测试 Ansible playbook、aci-py、autoACI,或任何讲 APIC/NDO REST API 的工具,无需硬件、无数据平面、完全确定且可重置。


1. Overview / 概述

  • What it does: serves the APIC REST plane (GET class/mo queries, POST/DELETE mutations, aaaLogin) and the NDO/ND REST plane (schema/template CRUD + deploy). Both are backed by one in-memory Management Information Tree (MIT) built from topology.yaml.
  • What it does NOT do: no data plane, no real NX-OS/APIC software stack, no live telemetry. It models the object model + REST behavior, not packet forwarding.

能做什么:提供 APIC REST 平面(GET class/mo 查询、POST/DELETE 变更、aaaLogin)和 NDO/ND REST 平面(schema/template 增删改 + deploy)。两者都由一棵从 topology.yaml 构建的内存 MIT 支撑。 不做什么:没有数据平面、没有真实 NX-OS/APIC 软件栈、没有实时遥测。它模拟对象模型 + REST 行为,不做报文转发。

Primary use cases / 主要场景: Ansible playbook testing · CI gate before real hardware · SDK/provider testing · AI-agent sandbox · contract/regression tests · teaching/demo.


2. Installation / 安装

Requires Python >= 3.11.

# from a checkout (or  pip install "aci-sim @ git+<repo-url>")
python3 -m venv .venv
.venv/bin/pip install -e .          # runtime deps: fastapi, uvicorn[standard], pydantic, pyyaml
.venv/bin/pip install -e '.[dev]'   # + test deps: pytest, pytest-asyncio, httpx
aci-sim --help

需要 Python >= 3.11。pip install -e . 安装运行依赖(fastapi/uvicorn/pydantic/pyyaml); .[dev] 额外装测试依赖。装完 aci-sim --help 应列出子命令,无 traceback。


3. Topology & the init wizard / 拓扑与 init 向导

Everything is driven by topology.yaml (Pydantic-validated). Author it three ways:

aci-sim init                 # interactive wizard (APIC setup-dialog style) — RECOMMENDED
aci-sim new --sites 2        # non-interactive scaffold with flags
# or hand-edit topology.yaml
aci-sim validate topology.yaml   # validate before running (CI gate)
aci-sim show topology.yaml       # print the derived inventory (IDs/IPs/ports)

The aci-sim init wizard walks: Step 0 Admin account -> Step 1 Deployment type (single fabric / multi-site) -> Step 2 per-site (name/id/asn/pod/controllers/IPs) -> Step 3 multi-site (NDO IP + ISN). Press ENTER to accept the [bracketed] default.

一切由 topology.yaml 驱动(Pydantic 校验)。三种方式创建:aci-sim init(交互式向导, 仿 APIC setup dialog,推荐)、aci-sim new(带 flag 非交互脚手架)、手改。运行前用 aci-sim validate 校验。向导顺序:Step 0 管理员账号 -> 1 部署类型 -> 2 每站点参数 -> 3 多站点 (NDO IP + ISN)。回车接受 [方括号] 里的默认值。


4. Admin account / 管理员账号

The wizard's Step 0 sets the fabric admin credentials, written to a topology.yaml auth: section:

auth:
  username: admin
  password: cisco
  # ndo_username / ndo_password only if you gave NDO a separate account
  • APIC plane ENFORCES itaaaLogin returns 401 on a username/password mismatch.
  • NDO plane is lenient by design (accepts any credential, for cisco.mso/aci-py compat); the same account drives it by default.
  • Credential precedence at aci-sim run: an explicit SIM_USERNAME/SIM_PASSWORD in the environment wins (atomic — set either and you own both) -> else the topology auth: section -> else the built-in admin/cisco default. aci-sim run prints which source is live (never the password).

向导 Step 0 设置管理员凭据,写进 topology.yamlauth: 段。APIC 平面强制校验(密码错 返回 401);NDO 平面故意宽松(接受任意凭据,为兼容 cisco.mso/aci-py),默认用同一账号。 aci-sim run 的凭据优先级:环境里显式的 SIM_USERNAME/SIM_PASSWORD 最高(原子:设了任一就 两个都归你)-> 其次 topology 的 auth: -> 再次内建默认 admin/cisco。run 会打印当前凭据来源 (不打印密码)。


5. Running — two modes / 运行的两种模式

5a. Port mode (default) / 端口模式(默认)

Binds 127.0.0.1 on distinct ports. Simplest; good for local dev + CI.

aci-sim run topology.yaml
# APIC LAB1 -> https://127.0.0.1:8443   APIC LAB2 -> :8444   NDO -> :8445

5b. Sandbox mode (real per-device IPs on :443) / sandbox 模式(真实 per-device IP,:443)

Each APIC/NDO gets its own loopback-alias IP on :443 (like real gear). Needed for tools that assume one IP per controller (e.g. autoACI multi-site discovery). Requires root (adds lo aliases + binds :443).

sudo bash scripts/sandbox-up.sh     # adds lo aliases from topology mgmt IPs, binds :443, detaches
#   APIC LAB1 -> https://10.192.0.11:443   APIC LAB2 -> https://10.192.128.11:443
#   NDO       -> https://10.192.0.10:443
sudo bash scripts/sandbox-down.sh   # stop + remove aliases

The sandbox IPs come from topology.yaml's fabric.ndo_mgmt_ip + each site.mgmt_ip. On Linux they are lo aliases -> reachable only on that host (run clients on the same box). PID in /tmp/aci-sim-sandbox.pid, log in /tmp/aci-sim-sandbox.log.

端口模式(默认):绑 127.0.0.1 不同端口,最简单,适合本地开发/CI。 sandbox 模式:每个 APIC/NDO 拿到自己的 loopback 别名 IP、都在 :443(像真机),适合假设"每个 控制器一个 IP"的工具(如 autoACI 多站点发现)。需 root(加 lo 别名 + 绑 :443)。sandbox IP 来自 topology.yamlndo_mgmt_ip 和各 site.mgmt_ip;Linux 上是 lo 别名,只在本机可达(客户端 要在同一台跑)。PID/日志见 /tmp/aci-sim-sandbox.{pid,log}


6. Connecting clients / 连接客户端

Credentials = your admin account (default admin/cisco). TLS is self-signed -> disable cert validation.

Ansible — cisco.aci (single fabric) / cisco.mso (multi-site):

# inventory: point apic_host / MSO ansible_host at the sim's IP:port
apic_host: "10.192.0.11:443"      # sandbox, or 127.0.0.1:8443 in port mode
apic_username: admin
apic_password: cisco
apic_validate_certs: no

aci-py (Python pusher): point --apic at the APIC IP; for multi-site pass an NDO connector (Ndo(host, username, password)) so tenants become NDO-managed (visible in autoACI).

autoACI: log in to the NDO IP (10.192.0.10) -> Discover -> Connect All Sites. It reads NDO, so multi-site tenants (created via cisco.mso or aci-py's NDO path) appear there.

凭据 = 你的管理员账号(默认 admin/cisco),TLS 自签 -> 关掉证书校验。Ansible 用 cisco.aci (单站点)/cisco.mso(多站点),inventory 里把 apic_host/MSO ansible_host 指向 sim 的 IP:端口。 aci-py 把 --apic 指向 APIC;多站点要传 NDO 连接器,tenant 才会被 NDO 管理(autoACI 才看得到)。 autoACI 登录 NDO IP(10.192.0.10)-> Discover -> Connect All Sites。

Multi-site invariant / 多站点不变量: a tenant is "multi-site" only if it exists in NDO. An APIC-only push (e.g. aci-py without an NDO connector) creates an APIC-local tenant that autoACI's NDO view will NOT show. 名字含多站点语义的 tenant 必须出现在 NDO 上。


7. Querying the MIT / 查询 MIT

Speaks the plain APIC REST API — works with curl, httpx, cisco.aci, or a real APIC.

# login -> cookie
curl -sk -c j.txt -X POST https://<apic>/api/aaaLogin.json \
  -d '{"aaaUser":{"attributes":{"name":"admin","pwd":"cisco"}}}'
# class query  (== moquery -c fvTenant)
curl -sk -b j.txt https://<apic>/api/class/fvTenant.json
# mo query + subtree
curl -sk -b j.txt "https://<apic>/api/mo/uni/tn-MS-TN1.json?query-target=subtree&target-subtree-class=fvBD"
# LLDP / CDP neighbors
curl -sk -b j.txt https://<apic>/api/class/lldpAdjEp.json

aci-sim lldp topology.yaml [--site N --node N --cdp --json] prints a show lldp neighbors-style table without hand-writing URLs. Query subscriptions + websocket push-on-change are supported (?subscription=yes + /socket<token>).

讲标准 APIC REST API,curl/httpx/cisco.aci/真机 APIC 都能用。class 查询等价 moquery -c; mo 查询支持 query-target=subtreeaci-sim lldp 直接打印邻居表。支持查询订阅 + websocket 变更推送(?subscription=yes)。


8. CLI reference / CLI 参考

Command Purpose / 用途
aci-sim init interactive wizard -> topology.yaml / 交互向导
aci-sim new --sites N ... non-interactive scaffold / 非交互脚手架
aci-sim validate FILE validate topology (CI gate) / 校验拓扑
aci-sim show FILE [--json] print derived inventory / 打印推导出的清单
aci-sim lldp FILE [--site --node --cdp --json] LLDP/CDP neighbor table / 邻居表
aci-sim graph FILE -o out.svg|.html render topology diagram / 渲染拓扑图
aci-sim run FILE run the supervisor (port mode) / 起 supervisor(端口模式)
scripts/sandbox-up.sh / sandbox-down.sh sandbox mode (real IPs :443) / sandbox 模式
scripts/sim-state.sh {save|restore} NAME save/restore whole-fabric state (§10) / 保存恢复整个 fabric 状态(见 §10)

9. /_sim control API / 控制 API

For AI-agent sandboxes and test isolation: snapshot / restore / reset the MIT, and seed faults — so each test starts from a known state.

面向 AI agent 沙箱和测试隔离:对 MIT 做 snapshot / restore / reset、注入 fault —— 让每个测试从 已知状态开始。


10. State persistence / 状态保存与恢复

In sandbox/port mode the sim's state is in-memory only — restarting the process wipes every tenant/schema/template on every plane. To survive a restart, save state to disk first and restore it after.

Per-plane endpoints (mirrors the /_sim control API in §9):

Plane Save Load
APIC (each site) POST /_sim/save/{name} POST /_sim/load/{name}
NDO POST /_sim/save/{name} POST /_sim/load/{name}
  • APIC's save writes {name}.{site.id}.apic.json (keyed by site id, so multiple sites in the same SIM_STATE_DIR never collide); NDO's save writes {name}.ndo.json.
  • A missing load name returns 404 — an APIC-envelope error (imdata[0].error) on the APIC plane, a plain {"detail": ...} on the NDO plane (matching each plane's existing error shape).
  • Files are plain JSON under SIM_STATE_DIR (default ~/.aci-sim/state) — human-readable, diffable, safe to check into a fixtures directory for a known-good baseline.

Wrapper script — save/restore the WHOLE fabric (all APIC sites + NDO) in one command:

scripts/sim-state.sh save mybaseline      # snapshot every plane
scripts/sim-state.sh restore mybaseline   # restore every plane (maps to /_sim/load)

It derives each plane's address the same way scripts/sandbox-up.sh does (straight from topology.yaml's fabric.ndo_mgmt_ip / each site.mgmt_ip, sandbox mode's real per-device :443 IPs), and falls back to port mode (127.0.0.1:8443/:8444/:8445) if those IPs aren't reachable — so it works unmodified in either running mode.

Restart workflow:

scripts/sim-state.sh save mybaseline
sudo bash scripts/sandbox-down.sh && sudo bash scripts/sandbox-up.sh   # or Ctrl-C + aci-sim run
scripts/sim-state.sh restore mybaseline

SIM_STATE_DIR — override the base directory for all saved state (default ~/.aci-sim/state; created automatically). Tests set this to an isolated tmp_path so nothing is ever written under the real home directory.

sandbox/端口模式下 sim 状态只在内存里——重启进程会清空所有平面上的 tenant/schema/template。 要跨重启保留状态,重启前先保存、重启后再恢复。

各平面端点(与 §9 的 /_sim 控制 API 呼应):APIC(每个站点)和 NDO 都提供 POST /_sim/save/{name} / POST /_sim/load/{name}。APIC 的保存文件名为 {name}.{site.id}.apic.json(按 site id 区分,同一个 SIM_STATE_DIR 里多个站点不会冲突); NDO 的保存文件名为 {name}.ndo.jsonload 遇到不存在的 name 返回 404 ——APIC 平面是 APIC 信封错误(imdata[0].error),NDO 平面是普通 {"detail": ...}(各自匹配平面已有的 错误格式)。文件是 SIM_STATE_DIR(默认 ~/.aci-sim/state)下的纯 JSON——可读、可 diff,也可以存进 fixtures 目录当已知良好基线用。

一键保存/恢复整个 fabric(所有 APIC 站点 + NDO):scripts/sim-state.sh save <name> / scripts/sim-state.sh restore <name>(restore 对应 /_sim/load)。地址推导方式与 scripts/sandbox-up.sh 一致(从 topology.yamlndo_mgmt_ip/各 site.mgmt_ip),这些 IP 不可达时自动退回端口模式(127.0.0.1:8443/:8444/:8445),两种运行模式下都能直接用。

重启工作流:先 sim-state.sh save → 重启 sim(sandbox-down.sh && sandbox-up.sh,或 Ctrl-C 后 aci-sim run)→ 再 sim-state.sh restore

SIM_STATE_DIR:覆盖所有保存状态的根目录(默认 ~/.aci-sim/state,自动创建)。 测试会把它设成隔离的 tmp_path,绝不会写到真实 home 目录下。


11. Multi-site vs single-fabric / 多站点 vs 单站点

  • Single-fabric: one APIC. Tenants pushed directly via cisco.aci — APIC-local, no NDO.
  • Multi-site: NDO + per-site APICs. Tenants are created as NDO schemas/templates and deployed to sites; only then do they appear on the site APICs (and in autoACI).
  • multi-pod ~= single-fabric for Ansible tenant testing — the IPN lives on external Nexus (NX-OS), not cisco.aci; the only per-pod nuance is the pod id in static binding paths, covered by site.pod.

单站点:一个 APIC,tenant 经 cisco.aci 直推,APIC 本地,无 NDO。多站点:NDO + 各站点 APIC,tenant 建成 NDO schema/template 再 deploy 到站点,之后才落到站点 APIC(和 autoACI)。对 Ansible tenant 测试,multi-pod ~= single-fabric —— IPN 在外部 Nexus 上(NX-OS),不经 cisco.aci。


12. Troubleshooting / 排错

Symptom / 现象 Cause & fix / 原因与解决
aaaLogin 401 password != the enforced account; check auth: / SIM_USERNAME+SIM_PASSWORD / the run banner.
sandbox: Needs root sudo bash scripts/sandbox-up.sh (needs lo alias + :443 bind).
sandbox: nothing on :443 after start old listener still holds the socket; the script waits for it to free — check /tmp/aci-sim-sandbox.log.
deep-root subtree query returns 0 query a DN that has a materialized MO (e.g. .../sys, not an un-materialized container).
client can't reach sandbox IP from another host sandbox IPs are lo aliases -> same-host only; use port mode + the host LAN IP for remote clients, or run the client on the sim host.
pip install CLI crashes on import ensure v0.13.1+ (earlier packaging didn't declare deps / omitted subpackages).

Generated as part of the sim playbook-E2E effort. For the REST contract details see docs/CONTRACT.md; for design rationale see docs/DESIGN.md; for the changelog see CHANGELOG.md.