docs: scrub private repo name, self-contain design references, document F12 coverage bias

This commit is contained in:
dtzp555-max
2026-07-10 19:55:16 +10:00
parent c5f8d0f940
commit 694454d9fc
7 changed files with 47 additions and 32 deletions
+1 -1
View File
@@ -755,7 +755,7 @@ def make_ndo_app(state: NdoState, apic_states: dict[str, Any] | None = None) ->
# PATCH client-side and never sends it, but stay tolerant.
return detail
# F4 + F12 (design doc `_F4_F12_VALIDATION_DESIGN.md`): only PATCHes
# F4 + F12 (see aci_sim/ndo/service_graph_validation.py docstring): only PATCHes
# that actually touch service-graph surface (a site-local
# `serviceGraphs` add/replace, or a site-local contract's
# `serviceGraphRelationship`) take the gated copy-validate-commit
+14 -1
View File
@@ -1,6 +1,6 @@
"""F4 + F12 — NDO server-side service-graph fidelity enforcements.
See `_F4_F12_VALIDATION_DESIGN.md` (repo root) for the full design. Summary:
Summary of the two enforcements:
- **F4 — device-existence gate.** When a site-local service-graph binding op
(`/sites/{siteId}-{templateName}/serviceGraphs/...`) references an L4-L7
@@ -341,6 +341,19 @@ def _validate_uniform_site_redirect(working: dict, ops: list[dict]) -> None:
`_touched_redirect_contracts`'s docstring for the cross-template
false-reject this scoping fixes). A template with only one associated
fabric is trivially uniform (design §3.4 / §4).
Coverage-group bias (intentional fail-safe): the group below only
includes fabrics that already carry a `contracts[]` shadow entry for
*contract* — a fabric with NO shadow entry at all is excluded outright,
not counted as "not yet configured". From working state alone we can't
distinguish "this fabric hasn't received the contract shadow yet" from
"this fabric doesn't participate in this contract", so an all-but-one-
fabric-shadowless group collapses to `total <= 1` and passes rather than
risk a false 400. Live-gate testing (real NDO + cisco.mso, once
cisco.mso has built the shadow entries on every participating fabric)
confirms the real chain still correctly 400s a genuine partial redirect
write — this bias only ever widens the ambiguous states this check
deliberately stays silent on.
"""
sites = working.get("sites")
if not isinstance(sites, list):
+12 -10
View File
@@ -6,10 +6,10 @@ validation. A real APIC 400-rejects out-of-range/malformed values (e.g.
``l3extRsPathL3OutAtt encap=vlan-9999``, ``l3extRsNodeL3OutAtt
rtrId=999.888.777.241``) even when the posting client (``cisco.aci.aci_rest``
raw MO bodies, in this campaign) does zero client-side validation of its own.
This module is the table-driven rule catalog that closes that gap — see
``_F10_VALIDATION_DESIGN.md`` (design doc, authoritative) for the full rule
table, sourcing, and false-reject risk analysis. Do not add rules here that
aren't in that table without updating the design doc first.
This module is the table-driven rule catalog that closes that gap — see the
RULES table below and its section comments for the full rule set, sourcing,
and false-reject-risk analysis. Do not add rules here without a comment
documenting that row's sourcing.
Architecture (design §3.1):
- Small composable validator *factories* (``rng``, ``one_of``, ``fmt``,
@@ -117,8 +117,9 @@ _MAC_COLON_RE = re.compile(r"^[0-9A-Fa-f]{2}(:[0-9A-Fa-f]{2}){5}$")
_MAC_DOT_RE = re.compile(r"^[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}$")
#: Named L4 ports real APIC/`cisco.aci` accept in place of a numeric port,
#: sourced verbatim from `aci-ansible-dev`'s `FILTER_PORT_MAPPING`
#: (module_utils/constants.py) values, plus the literal "unspecified".
#: sourced verbatim from the vendored cisco.aci collection's
#: `FILTER_PORT_MAPPING` (module_utils/constants.py) values, plus the
#: literal "unspecified".
_NAMED_PORTS = {"https", "smtp", "http", "dns", "pop3", "rtsp", "ftpData", "unspecified"}
@@ -273,11 +274,12 @@ def all_of(*validators: Validator) -> Validator:
# ---------------------------------------------------------------------------
# RULES — (class, prop) -> Validator
#
# Every entry below is sourced from a row in _F10_VALIDATION_DESIGN.md §2A
# Every entry below falls into one of two source categories: §2A
# (Priority 1 — server-only format/range) or §2B (Priority 2 — server-only
# enums). Comments cite the design-doc row. Cross-field / cardinality rules
# (§2C, e.g. infraPortBlk fromPort<=toPort, fvnsEncapBlk from<=to) are OUT OF
# SCOPE for this batch (P1+P2 only) — deliberately not implemented here.
# enums), per the rule-source notes in this module's docstring. Cross-field /
# cardinality rules (§2C, e.g. infraPortBlk fromPort<=toPort, fvnsEncapBlk
# from<=to) are OUT OF SCOPE for this batch (P1+P2 only) — deliberately not
# implemented here.
# ---------------------------------------------------------------------------
RULES: dict[tuple[str, str], Validator] = {
+4 -4
View File
@@ -224,10 +224,10 @@ def _validate_planned(planned: list[tuple[str, dict]]) -> None:
anything malformed that predates this check).
Table-driven (F10): every ``(class, prop)`` pair checked here comes from
``aci_sim.rest_aci.validators.RULES`` — see that module + design doc
``_F10_VALIDATION_DESIGN.md`` §3.2/§3.6 for the registry and its
fail-safe default-allow policy. A ``(class, prop)`` pair with NO entry in
RULES is intentionally NOT validated (unknown ⇒ allow, never reject)."""
``aci_sim.rest_aci.validators.RULES`` — see that module's docstring and
section comments for the registry and its fail-safe default-allow
policy. A ``(class, prop)`` pair with NO entry in RULES is intentionally
NOT validated (unknown ⇒ allow, never reject)."""
for mo_cls, mo_attrs in planned:
if mo_attrs.get("status") == "deleted":
continue