Time Machine
Snapshot APIC query results over time, compare any two points, and detect configuration drift automatically — without adding a write-path burden on your fabric.
Time Machine turns a saved query into a timeline. Every time the query runs — manually, on schedule, or as part of a pipeline — Fabrik can capture the result as a snapshot. Later, you can pick any two snapshots and see exactly what changed: which objects were added, which were deleted, which attributes mutated.
It's the difference between knowing the fabric's current state and knowing how it got there.
What problems it solves
A few scenarios show up constantly in ACI operations:
- "Who changed this?" — a tenant's config was different yesterday. The audit log tells you someone modified it; Time Machine tells you what the old value was and what the new one is, at the attribute level.
- "Did anything drift overnight?" — schedule a daily snapshot of critical state. Fabrik flags snapshots that differ from the previous one. One dashboard, one glance, done.
- "What did this look like before the change window?" — snapshot before a maintenance, snapshot after, compare. Zero ambiguity on what the work touched.
- "Prove we didn't break anything." — compliance and audit teams care about before/after evidence. Snapshots are hash-stamped, immutable, and retained per policy.
Everything Time Machine does is read-only. It captures query results — it doesn't push anything back to APIC. If it sees drift, that's information, not an intervention.
The mental model
One concept, three moving parts:
| Part | What it is |
|---|---|
| Snapshot | One captured run of a query: raw result JSON, hash, timestamp, who ran it, which APIC, which saved query version. |
| Settings | Retention policy (by days / by count / unlimited), size limits, duplicate handling. Global default plus per-user overrides. |
| Diff | Structured comparison of two snapshots — added objects, deleted objects, modified objects with per-attribute changes. |
The same saved query produces a stream of snapshots over time. Each snapshot is a frozen read of APIC state at one moment.
How capture actually works
When a snapshot is requested (manual button click, scheduled task completion, pipeline tail):
- The query runs against APIC and returns the raw result.
- Fabrik serializes it and computes a SHA-256 hash.
- The hash is compared to the previous snapshot's hash for the same
(query, APIC). - If they match and
store_duplicatesis off, the snapshot is skipped — no row written, no disk wasted. - Otherwise, the snapshot is persisted with
has_changes = true/falsebased on the hash comparison.
The hash-based dedup is the key optimization. Stable networks produce identical results day after day; without dedup you'd fill the database with duplicates of the same state. With dedup, a month of daily snapshots for an unchanging tenant takes exactly one row.
Error response rejection
APIC occasionally returns an empty imdata with a messages array flagging an error — expired token, 403, query timeout. Saving that as a snapshot would make the next comparison report every object as deleted (the error result has nothing to diff against). Fabrik detects these responses and rejects them outright; nothing is stored.
Size limits
Snapshots are JSON blobs stored in PostgreSQL jsonb. Big tenants and large endpoint tables can produce tens of megabytes per snapshot. A per-user max_snapshot_size_mb limit (default 10 MB) prevents runaway growth. Over the limit → snapshot refused, warning logged.
How diff actually works
Comparing two snapshots is DN-based, not positional:
- Pull both snapshots' raw imdata arrays.
- For each item, extract the
dnattribute — that's the stable identity. - Build a
{dn → object}map for each side. - Deleted = DNs in the old but not the new.
- Added = DNs in the new but not the old.
- Modified = DNs in both, but attributes differ — drill down to show exactly which attributes changed.
The DN-based approach means items that reordered don't spuriously appear as "changed" — order is ignored, identity is what matters. Items without a DN attribute are silently skipped rather than triggering an O(n²) positional fallback.
The diff walks all attribute keys (in both snapshots) and records any value that differs. The output is a sorted list of {key, old, new} entries per modified object — deterministic ordering so the UI always renders the same diff the same way.
Version awareness
Saved queries have a semantic version: major.minor (see Query Builder → Saving and sharing). Each snapshot captures the query version at execution time.
Why this matters: if you changed the query structure (added a filter, removed a post-processor), the before/after results are no longer directly comparable. Time Machine records the version hash alongside each snapshot so the UI can warn you when comparing across structurally different versions.
The UI will still let you compare — sometimes that's exactly what you want — but the mismatch is flagged so you don't mistake a query change for a fabric change.
Retention — three strategies
Snapshots don't live forever by default. The retention policy decides what gets deleted:
- By days (default 30) — anything older than N days is purged.
- By count — keep the newest N snapshots per saved query, purge the rest.
- Unlimited — never purge. Used for compliance-sensitive queries.
Cleanup runs daily at 03:30 server time via a Celery Beat task. The delete is a single set-based SQL statement (not Django's row-by-row .delete()) so it stays fast even against hundreds of thousands of expired rows.
Per-user settings override the global default, so an admin can set "30 days for everyone" and a compliance user can set "unlimited for my own queries" without conflict.
Where snapshots come from
Three capture paths:
- Manual — on the Query Results panel, the Save Snapshot button captures the current result. Useful for one-off "before/after" pairs around a change window.
- Scheduled — when a scheduled task runs a saved query, it can also capture the result. This is the normal drift-detection pattern.
- Ad-hoc queries — snapshots exist for unsaved queries too, but they don't appear in the main Time Machine list view (there's no stable identity to group them under). The
query_structurefield on the snapshot preserves the flow so the original query is reconstructable.
Continue reading
Capturing snapshots
Manual capture from Query Results, scheduled capture via tasks, annotations and labels, and when a capture gets rejected.
Comparing and drift
Pick any two snapshots, read the DN-based diff, drill into per-attribute changes, and use the heatmap to spot noisy days.
Track DN
Follow a single object across snapshots — created/deleted markers, side-by-side attribute diff, and date-range scoping for incident response.
Retention and settings
Global vs per-user retention, size limits, duplicate handling, cleanup timing, and how to keep compliance-sensitive history forever.
Time Machine is small in code but high-leverage in practice. Start with capture, wire up a daily scheduled task for your critical state, and the drift questions start answering themselves.
Executions
Live AWX job monitoring — streaming playbook output, per-host outcomes, relaunch and cancel, and how the backend keeps every event in sync with the browser.
Capturing snapshots
How to take a snapshot — manually from the results panel, automatically via a scheduled task, or programmatically — and what happens when a capture is rejected.