Snapshot — deterministic configuration captures¶
Deterministic, git-diffable snapshots of a fabric’s configuration.
A snapshot is a fact: every instance of every exportable class under a
scope DN, read through the sharded, paginated bulk reader, normalised by the
catalogue’s own metadata, and serialised so that the same fabric state always
produces the same bytes. Two snapshots of an unchanged fabric git diff
empty; a config change diffs as exactly that change.
Three catalogue-driven decisions, none of them a hand-maintained list:
Which objects: classes the schema marks
isExportable— Cisco’s own definition of what a configuration export contains (measured on 6.0(9c): exportable is a strict subset of configurable; the difference is runtime state such as login sessions, which a backup must not carry). The curated DSL vocabulary plays no role here: an object the DSL cannot express is still configuration, and ignoring it would make the backup lie.Which properties: those the schema marks
isConfigurable— the operational halo (timestamps, status, computed backpointers) falls away data-driven, never through a denylist.What never ships: the curated secret policy. Values the schema flags
securenever read back from an APIC in the first place; the curated positions the flag misses are redacted to"<redacted>"; and an object whose DN carries a secret (an SNMP community string names its own object) cannot be redacted in place — it is reported inSnapshot.warningsso the caller decides, instead of publishing a secret to git silently.
The serialised form is wire-format (APIC class and property names), so a snapshot survives any renaming of the SDK’s readable surface unchanged.
Constants¶
- niwaki.snapshot.REDACTED = '<redacted>'¶
The stable sentinel written in place of a curated secret value.
Functions¶
- niwaki.snapshot.take(aci, scope='uni')[source]¶
Capture the configuration under scope into a
Snapshot.Reads every exportable class under the scope through the sharded, paginated bulk reader (one request in the common case, several when the class list or the result set demands it), then normalises:
non-configurable properties are dropped (operational halo);
curated secret values are replaced by
niwaki.snapshot.REDACTED;objects whose DN carries a secret are collected into
Snapshot.warnings.
- Parameters:
aci (Any) – A connected
niwaki.Niwakiclient (observation only — a snapshot never writes).scope (str) – The subtree to capture.
"uni"for the whole configuration,"uni/tn-<name>"for one tenant, any config DN for narrower.
- Returns:
The
Snapshot.treeisNonewhen nothing exists at scope.- Return type:
Example:
snap = snapshot.take(aci, "uni/tn-prod") Path("tn-prod.json").write_text(snap.to_json())
- niwaki.snapshot.diff(a, b)[source]¶
Compare two snapshots structurally — the drift detector.
Works on any two snapshots of the same scope: two moments of one fabric (config drift), or the same scope on two fabrics (divergence between a reference fabric and a replica).
- Parameters:
- Returns:
A
SnapshotDiff. Empty (has_changesfalse) when the two captures describe identical configuration.- Raises:
ValueError – The snapshots cover different scopes — comparing a tenant to a whole fabric is a mistake, not a diff.
- Return type:
Example:
before = snapshot.take(aci, "uni/tn-prod") ... after = snapshot.take(aci, "uni/tn-prod") delta = snapshot.diff(before, after) for dn in delta.added: print("new object:", dn)
Result types¶
- class niwaki.snapshot.Snapshot(scope, tree, coverage=<factory>, warnings=())[source]¶
Bases:
objectOne deterministic capture of a scope’s configuration.
- tree¶
The captured configuration as nested plain dicts, wire-format:
{"class": "fvTenant", "rn": "tn-prod", "attributes": {"name": "prod", ...}, "children": [...]}
Children are sorted by
(class, rn)and attributes by name — the determinism lives in the data, not in the serialiser.
- coverage¶
Instance count per class — the interpretation beside the fact: what the capture actually contains, at a glance.
- warnings¶
Human-readable notices the caller must not ignore, one per object whose DN itself carries a secret (an SNMP community profile is named by its community string).