feat(ndo): enforce service-graph device-existence + uniform site redirect (v0.24.0)

Closes F4 + F12: the sim NDO silently accepted two writes that real NDO 4.x
rejects server-side (the two documented walls behind the two-phase deploy):

- F4: binding a site-local service graph whose serviceNodes[].device.dn does
  not exist on the target APIC -> now 400 "Service graph device <dev> does
  not exist in tenant <t> in Fabric <site>" (cross-plane check against the
  per-site APIC MITStore; single-process sandbox shares live store objects).
- F12: partial per-fabric serviceGraphRelationship redirect coverage -> now
  400 "must have uniform redirect policy configured on all fabrics";
  validated on POST-request state (deepcopy->apply->validate->commit) so a
  single atomic all-fabric PATCH passes, matching real NDO. Coverage-based
  per (template, contract) — sibling templates with same-named contracts
  cannot cross-reject.

Non-service-graph PATCHes keep the in-place fast path. Fail-safe
default-allow; all-or-nothing on rejection. +33 tests (1131 passed).
Live gate: MS-TN2 two-phase chain 12/12 green (no false-reject); phase2-only
400s with golden message + zero residue; single-fabric PATCH 400s while the
atomic both-fabric control returns 200.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zdUTqU9fvCCvF3uVGZsu1
This commit is contained in:
dtzp555-max
2026-07-10 18:01:14 +10:00
co-authored by Claude Fable 5
parent fe3d13221a
commit c5f8d0f940
6 changed files with 1442 additions and 3 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "aci-sim"
version = "0.23.0"
version = "0.24.0"
description = "Faithful REST simulator of a 2-site Cisco ACI fabric (per-site APIC + ND/NDO)"
readme = "README.md"
license = "PolyForm-Noncommercial-1.0.0"