Lab 01 — Rack Data Model & Inventory Engine
Phase: 01 — Rack-Scale Foundations | Difficulty: ⭐⭐☆☆☆ | Time: 3–4 hours Language: Python (stdlib only) | Hardware: none
Concept primer:
../WARMUP.mdCh. 7 (failure domains) & Ch. 10 (what to model),../HITCHHIKERS-GUIDE.md§1, §6.
Run
python3 solution.py # builds a rack, validates it, prints blast radius, self-tests
0. The mission
Build the data model that every other phase plugs into. A rack manager is, at its core, a system that keeps an accurate model of physical reality and acts on it. If the model is wrong or thin, every feature above it leaks. You will build a typed object graph of a realistic AI rack, validate it against invariants, and answer the question that begins every incident: "if this fails, what goes with it?"
Two questions you must be able to answer at the end:
- Why is inventory a graph (powers/cools/connects edges), not a flat table?
- Why must identity (serial/asset tag) be separate from location (rack + U)?
1. What the code models
| Concept (WARMUP) | In the code |
|---|---|
| Durable identity ≠ location (Ch. 10) | Identity holds asset_tag/serial/model; u_position lives on Node |
| Containment hierarchy (Ch. 10) | Rack → Node → Component; plus rack-level PDU/CDU/Switch |
| Relationship graph (Ch. 7) | edges: (relation, source, target) for powers/cools/connects/manages |
| State (Ch. 10) | Health, PowerState, Lifecycle enums |
| Invariants (Ch. 10) | validate() — power, cooling, U-collision, mgmt-reachable, dangling edges |
| Blast radius (Ch. 7) | find_blast_radius() — transitive closure over failure-propagating edges |
| Redfish shape (Ch. 8, Phase 03) | to_redfish_like() — a Chassis/Systems/Power/Thermal tree |
2. What the run shows
=== Rack rack-ai-01 (8 nodes) ===
total power draw : 14.3 kW
total heat load : 14.3 kW
...
validation : PASS (all invariants hold)
--- Blast radius examples ---
if PDU-A fails -> 4 node(s) affected ← N+1 balance: PDU-A feeds the even nodes
if CDU-1 fails -> 8 node(s) affected ← single CDU = whole-rack cooling domain
if TOR-1 fails -> 8 node(s) affected ← single ToR = whole-rack data domain
Read those three blast-radius lines carefully — they are the failure-domain lesson (WARMUP Ch. 7). A PDU failure is half the rack (because power is balanced N+1 across two feeds); the CDU and ToR are single points of failure for the whole rack. That asymmetry is exactly what drives redundancy decisions and workload placement.
3. Extension exercises (do them — this is the lab)
- Model the cooling loop properly: today
coolsis a single edge from the CDU to each node. Add aManifold/coolant loopobject between CDU and nodes so a single manifold failure has a smaller blast radius than the whole CDU. Re-run blast radius and explain the new numbers. - N+1-aware blast radius:
find_blast_radiusignores redundancy. Add afind_blast_radius_redundant()that, for power, only counts a node as "down" if it loses all its power feeds. Then give each node a secondpowersedge to the other PDU and show that a single PDU failure now affects 0 nodes — the point of N+1. - Declared vs discovered: add a
discover()that returns a slightly different rack (a missing accelerator, a firmware drift) and areconcile(declared, discovered)that reports the diff. This is the conceptual seed of the Phase 06 operator loop and Phase 10 RCA. - Identity vs location bug: write a test that moves a node to a new U position but keeps its serial, and assert that queries by serial still resolve while location-based queries see the move. Then introduce the classic bug (update location but reuse a stale asset_tag) and show your validator catches the duplicate.
4. Common pitfalls
- Flattening the graph — if you store relationships as columns on a node row, you can't traverse them; blast radius and reconciliation become impossible. Keep edges.
- Putting
u_positioninIdentity— then a part "changes identity" when it moves. Location is state of the slot, identity belongs to the part. - Forgetting rack-level objects — PDUs, CDU, switches belong to no single node; model them at the rack level or you can't compute shared-resource blast radius.
- Validating only the happy path — the value of
validate()is catching the broken configs (no power feed, U collision, dangling edge). Test those explicitly.
5. What this lab proves about you
You can design the schema a rack-management product is built on — the part that looks "boring" in an interview but is where real systems live or die. When asked "design the data model for a rack inventory system" (a near-certain system-design prompt), you don't hand-wave a table; you draw a graph with identity/location separation, invariants, and a blast-radius traversal, and you can say which objects are single points of failure and why. The Phase 13 capstone reuses this model directly.