Software Engineer — Rack Management (Senior / Staff) — Complete Apprenticeship Curriculum
Target Roles:
- Senior / Staff Software Engineer — Rack & Data Center Management Software (Qualcomm, NVIDIA, AMD, Microsoft Azure, Meta, Google, ODMs)
- Infrastructure Software Engineer — AI accelerator racks, bare-metal fleets, BMC/Redfish control planes
- Platform Engineer — provisioning, lifecycle, and observability for rack-scale AI systems
- Data Center Software Engineer — out-of-band management, firmware orchestration, telemetry pipelines
- SRE / Reliability Engineer — close-to-hardware infrastructure software in production
Duration: 26–34 weeks core — extendable to 12 months for full Staff-level readiness Goal: Build the rare profile this JD demands — the ability to write high-quality, secure, close-to-hardware infrastructure software that provisions, monitors, orchestrates, and maintains rack-scale AI accelerator systems; to speak the management protocols (Redfish/IPMI/SNMP) and hardware (BMC/PDU/CDU/PCIe) fluently; to integrate with Kubernetes/Terraform/Foreman and ship Prometheus/Grafana telemetry; and to debug and root-cause complex failures in deployed production data centers — then to lead that work and mentor others at the Staff level.
Why This Curriculum Exists
The "Rack Management Software Engineer" role at a hyperscale AI accelerator program is deceptively broad. The JD reads like five jobs:
- Systems software engineering — production Python, C++, and shell that runs close to hardware: daemons, agents, drivers-adjacent code, written to be secure, testable, and maintainable.
- Hardware/firmware interfacing — talking to BMCs, PDUs, CDUs, and PCIe switches over Redfish, IPMI, and SNMP; firmware updates; crash-dump parsing; remote diagnostics; RAS.
- Infrastructure automation — bare-metal provisioning (Foreman/PXE), configuration management (Ansible/Chef/Puppet), infrastructure-as-code (Terraform), and orchestration (Kubernetes/RKE2).
- Observability & operations — unified telemetry pipelines (Prometheus/Grafana), logging, metrics, tracing, runbooks, and SLOs across heterogeneous rack components.
- Production debugging & ownership — root-cause analysis of complex issues in deployed systems, secure multi-tenant operation (secure boot, VLAN separation, QoS), and — at Staff — driving implementation, reviewing designs, and mentoring.
Most engineers know one or two of these. This curriculum is built backward from the JD so that every "required" and "preferred" line maps to phases and hands-on labs. The whole thing runs on a single laptop — every BMC, PDU, CDU, PCIe switch, and firmware flow is reproduced with open emulators and simulators, so you learn the mechanism without a data center.
| JD Requirement | Where It's Trained |
|---|---|
| Implement/test/maintain rack provisioning, monitoring, workload deployment | Phases 05, 06, 13 |
| Interface with BMC, PDU, CDU, PCIe switches via Redfish, SNMP, IPMI | Phases 03, 04 |
| Integrate with Kubernetes, Terraform, Foreman | Phases 05, 06 |
| Collaborate with ODMs/ISVs; software EBOM & protocol docs (Pegatron) | Phase 12 |
| Dashboards & unified telemetry with Prometheus & Grafana | Phase 07 |
| Firmware updates, crash-dump parsing, remote diagnostics | Phase 08 |
| Secure boot, VLAN separation, QoS for multi-tenant | Phase 09 |
| High-quality, secure, maintainable code; coding standards | Phases 02, 11, 12 |
| Design discussions/reviews; feasibility/performance/reliability feedback | Phases 10, 12 |
| Debugging & root-cause analysis across dev/integration/production | Phase 10 |
| Observability: logging, metrics, tracing | Phase 07 |
| Runbooks, operational procedures | Phases 10, 12 |
| Code reviews, testing, CI/CD build/test/deploy pipelines | Phase 11 |
| Python, C++, shell (required); Node.js (plus) | Phase 02; labs span all four |
| Redfish APIs & DCIM tools | Phases 03, 04, 07 |
| Server hardware, networking, storage understanding | Phases 01, 04, 09 |
| K8s/RKE2; Prometheus/Grafana; Foreman/Ansible/Chef/Puppet; rack lifecycle | Phases 05, 06, 07, 08 |
| Staff: drive implementation, mentor, own delivery | Phase 12 + capstone |
Going beyond the JD (because interviews and the job do)
Real interviews and real on-call shifts go past the bullet list. This track deliberately adds:
- RAS depth — PCIe AER, MCE/MCA, ACPI/APEI, Xid-style error decoding (Phase 08) — because "crash-dump parsing" in practice means decoding hardware error logs.
- Systematic debugging & RCA as a first-class discipline with
gdb/strace/perf/eBPF and written postmortems (Phase 10) — the JD's most-repeated theme. - Reliability patterns — reconciliation loops, idempotency, circuit breakers, chaos/fault injection (Phase 10).
- Hardware-in-the-loop testing and emulator-based CI so infra code is actually testable (Phase 11).
- Staff-level leadership — design reviews, ADRs, EBOM, protocol specs, mentorship, and distributed-team communication (Phase 12).
- Fleet-scale system design — what changes when one rack becomes ten thousand (the entire
system-design/section).
What You Will Build
By the end you will have produced, with your own hands, a portfolio that is the job:
- A rack data model + inventory engine (Python) representing compute trays, accelerators, NICs, switches, PDUs, CDUs, and BMCs — with a power & thermal budget calculator that catches over-subscription before it trips a breaker (Phase 01)
- A C++ sensor-polling daemon (RAII, error handling, unit tests, systemd-ready) and a production-grade Python rack CLI (typed, packaged, retried, structured-logged) — the JD's language matrix, done right (Phase 02)
- A Redfish client that walks Chassis→Power→Thermal, performs resets, and subscribes to events against a real Redfish emulator — plus SNMP poller/trap receiver and IPMI sensor reader (Phase 03)
- A BMC interaction toolkit (power-cycle, sensor read, virtual media) and a PDU/CDU telemetry simulator + PCIe topology mapper that turns
lspciinto a fabric graph (Phase 04) - A bare-metal provisioning flow (PXE/iPXE → kickstart → Ansible) and a Foreman/Terraform onboarding state machine that takes a rack from "powered on" to "ready" (Phase 05)
- A Kubernetes operator (a
RackNodeCRD with a reconciliation loop) and an accelerator device-plugin + scheduling walkthrough on RKE2 (Phase 06) - A Redfish→Prometheus exporter, PromQL alert rules, and a Grafana dashboard for a rack — plus OpenTelemetry tracing across a provisioning workflow (Phase 07)
- A firmware-update orchestrator with staging, signature verification, A/B rollback — and a crash-dump / RAS log parser that classifies PCIe AER and MCE errors into an actionable diagnostics report (Phase 08)
- A VLAN + QoS tenant-isolation lab (network namespaces,
tc) and a secure-boot / measured-boot + attestation simulator (TPM PCRs, signed firmware) (Phase 09) - A debugging dojo: a planted bug in a rack daemon you find with
gdb/strace/eBPF and write up — plus a chaos/fault-injection harness that hardens a reconciliation loop (Phase 10) - A CI pipeline with emulated hardware (Redfish mock in CI, full test pyramid) and a fleet canary + rollback release system (Phase 11)
- A complete Staff deliverable pack — a protocol spec, a software EBOM, an ADR, a design-review checklist, a runbook, and a mentorship plan (Phase 12)
- A capstone: mini rack-management platform — discovery/inventory + Redfish/IPMI/SNMP drivers behind an abstraction + provisioning state machine + telemetry + firmware orchestration + secure multi-tenant config, with a design doc and a release cycle (Phase 13)
Folder Structure
Software Engineer Rack Management - Senior and Staff Engineer level/
├── README.md ← You are here
├── jd.md ← Target role profile
├── phase-01-rack-scale-systems-foundations/ ← What an AI rack IS: trays, accel, BMC, PDU, CDU, power/thermal
├── phase-02-production-systems-programming/ ← Python, C++, shell, Node.js — secure, testable, maintainable
├── phase-03-management-protocols-redfish-ipmi-snmp/ ← The out-of-band control plane: Redfish, IPMI, SNMP
├── phase-04-hardware-interfacing-bmc-pdu-cdu-pcie/ ← BMC, PDU, CDU (liquid cooling), PCIe switch fabric
├── phase-05-provisioning-config-management/ ← PXE/Foreman, Ansible/Chef/Puppet, Terraform — bare metal to ready
├── phase-06-orchestration-kubernetes-rke2/ ← K8s/RKE2, the operator pattern, device plugins, scheduling
├── phase-07-telemetry-observability/ ← Prometheus, Grafana, logging, metrics, tracing, unified pipelines
├── phase-08-firmware-diagnostics-ras/ ← Firmware update, crash dumps, RAS (PCIe AER/MCE/APEI), remote diag
├── phase-09-networking-secureboot-multitenancy/← Mgmt vs data plane, VLAN isolation, QoS, secure/measured boot, TPM
├── phase-10-reliability-debugging-rca/ ← Systematic debugging, gdb/strace/perf/eBPF, RCA, chaos, reconciliation
├── phase-11-cicd-testing-release-engineering/ ← Test pyramid + HIL, emulated-hardware CI, canary, rollback
├── phase-12-collaboration-ebom-leadership/ ← EBOM, protocol docs, ADRs, design reviews, Staff mentorship
├── phase-13-capstone-rack-management-platform/ ← Integrate everything into a mini rack manager + release cycle
├── interview-prep/ ← Concepts, coding, protocols, debugging, behavioral/Staff
└── system-design/ ← Control plane, telemetry, firmware-at-fleet, tenancy, provisioning
26-Week Core Schedule
| Week | Phase | Focus |
|---|---|---|
| 1 | 01 | Anatomy of an AI rack: trays, accelerators, NICs, ToR, BMC, PDU, CDU; build the rack inventory model |
| 2 | 01 | Power & thermal budgets, failure domains, OCP/MGX/NVL; build the power/thermal budget calculator |
| 3 | 02 | Production Python: typing, packaging, logging, retries, security; build the rack CLI |
| 4 | 02 | Production C++: RAII, error handling, concurrency, testing; build the sensor daemon (+ Node.js tooling) |
| 5 | 03 | Redfish data model: ServiceRoot, Systems, Chassis, Managers, tasks, events; build the Redfish client |
| 6 | 03 | IPMI (sensors/SEL/chassis) and SNMP (MIBs/OIDs/traps); build the poller + trap receiver |
| 7 | 04 | BMC/OpenBMC architecture; power control, sensors, virtual media; the BMC toolkit |
| 8 | 04 | PDU metering, CDU liquid-cooling telemetry, PCIe switch fabric/topology; the topology mapper |
| 9 | 05 | Bare-metal provisioning: PXE/iPXE/DHCP/TFTP, kickstart/cloud-init; the provisioning flow |
| 10 | 05 | Foreman, Ansible/Chef/Puppet, Terraform; the rack onboarding state machine |
| 11 | 06 | Kubernetes for AI workloads; RKE2; the operator/reconciliation pattern; build the RackNode operator |
| 12 | 06 | Device plugins, node-feature-discovery, scheduling, taints/tolerations, multi-tenancy |
| 13 | 07 | Metrics & Prometheus: exposition format, PromQL, exporters; build the Redfish→Prometheus exporter |
| 14 | 07 | Grafana dashboards, alerting rules; logging & OpenTelemetry tracing across a workflow |
| 15 | 08 | Firmware update mechanisms (Redfish UpdateService), staging, A/B, rollback, signing; the orchestrator |
| 16 | 08 | RAS: PCIe AER, MCE/MCA, ACPI/APEI, crash dumps, SoL; build the RAS/crash-dump parser |
| 17 | 09 | Mgmt vs data plane, VLANs, fabric; tenant isolation; build the VLAN/QoS lab |
| 18 | 09 | Secure boot chain, measured boot, TPM/PCRs, attestation; build the attestation simulator |
| 19 | 10 | Systematic debugging methodology; gdb/strace/ltrace/perf/ftrace/eBPF; the debugging dojo |
| 20 | 10 | Reliability patterns; chaos/fault injection; reconciliation hardening; write the RCA/postmortem |
| 21 | 11 | Test pyramid for hardware-adjacent code; emulator-based CI; build the CI-with-emulated-hardware pipeline |
| 22 | 11 | Release engineering: versioning, canary, progressive rollout, rollback; the fleet rollout system |
| 23 | 12 | EBOM, protocol documentation, ADRs, design-review practice (ODM/ISV collaboration) |
| 24 | 12 | Runbooks, operational procedures, Staff mentorship & distributed-team communication |
| 25 | 13 | Capstone build: integrate inventory + drivers + provisioning + telemetry + firmware + tenancy |
| 26 | 13 | Capstone release cycle: design doc, runbook, demo, retrospective; interview-prep & system-design review |
Phase Structure
Every phase follows a consistent format:
| File | Purpose |
|---|---|
README.md | Why the phase exists, concepts list, lab tables (goal/steps/output/testing/talking points/resume bullet/extensions), deliverables checklist, interview relevance |
WARMUP.md | Zero-to-expert primer: every concept from first principles, lab walkthrough guidance, success criteria, interview Q&A, references |
HITCHHIKERS-GUIDE.md | The practitioner's deep-dive: mental models, production numbers, the things people actually get asked and paged about |
lab-XX-*/ | Hands-on labs: README walkthrough + working reference code you run, break, and extend |
Labs deliberately span the JD's language matrix: Python (clients, exporters, simulators, operators, parsers), C++ (the sensor daemon, close-to-hardware code), shell (provisioning glue, CI), and Node.js (a telemetry/tooling service — the JD's "plus"). Where the JD names a tool (Redfish, IPMI, SNMP, Foreman, Ansible, Terraform, Kubernetes, RKE2, Prometheus, Grafana), you use a real, open implementation of it.
No data center? You can still do 100% of this track
Every lab runs on a single Linux/macOS laptop using open emulators:
- Redfish — DMTF's
Redfish-Mockup-Serverand the OpenBMCsushy-emulatorgive you a real Redfish service. - IPMI/BMC — a software BMC simulator (or
ipmi_simfrom OpenIPMI) plus a provided mock. - SNMP —
snmpsimserves real MIBs; a local trap receiver completes the loop. - Kubernetes/RKE2 —
kind/k3dlocally; RKE2 differences are explained in the WARMUP. - Provisioning — PXE/iPXE flows run against local VMs/QEMU; the rest is simulated with the same data model.
- PDU/CDU/PCIe — provided simulators emit realistic telemetry;
lspcitopology mapping uses your own machine.
The WARMUPs always explain what changes at real rack and fleet scale so you're not surprised on the job.
Prerequisites
- Solid Python; working C or C++ (you can read pointers, structs, headers, and a Makefile)
- Comfort in a Linux shell (you'll write scripts and use
systemctl,journalctl,ip,lspci,gdb,strace) - Basic networking literacy (IP, VLAN, DNS/DHCP) and willingness to go deeper — Phase 09 starts from fundamentals
- Git, Docker basics; a Linux VM or WSL2 if you're on Windows
- No prior BMC/Redfish/firmware experience required — Phases 01 and 03 start at the hardware and the protocol
Related Tracks in This Repo
- Head of Software Engineering — GPU — the silicon/CUDA/driver side of the same racks; its Phases 04/06 pair with our 04/06
- Security Engineer — deeper offensive/defensive security; pairs with our Phase 09 (secure boot, tenancy)
- AI Specialist — inference/accelerator workloads that run on the racks you manage
- LLM Inference Engineer — what the accelerators actually run, for context on workload behavior
Job Area: Engineering Group, Engineering Group > Software Engineering General Summary:
About Us
Qualcomm is growing its presence in Riyadh and is hiring Data Centre Engineers to support our expanding infrastructure across the region. As Saudi Arabia accelerates its digital transformation under Vision 2030, Qualcomm is investing in world‑class computing and data centre capabilities to power AI, cloud, and advanced connectivity at scale. This is a unique opportunity to work in a fast‑growing technology hub, supporting critical environments and helping shape the future of data centre operations in the Kingdom and beyond.
About the Role
Qualcomm is seeking Senior and Staff Software Engineers to contribute to the development and operation of rack management software for AI accelerator products deployed in large-scale data center environments. This role is hands-on and execution-focused, emphasizing high-quality implementation, debugging, testing, and operational support of infrastructure software that runs close to hardware.
You will collaborate closely with lead engineers and architects across multiple sites, participate in design discussions and reviews, and take on design-ownership roles, while focusing primarily on implementing defined designs, investigating complex issues, and ensuring reliable, secure software behaviour in production environments.
In this role, you will work on software responsible for provisioning, monitoring, orchestration, and lifecycle management of rack‑scale AI systems. You will be expected to take strong ownership of the code you deliver — from implementation and validation through deployment and debugging in real‑world data center conditions.
This position is well‑suited for a senior engineer who thrives in a distributed team model, communicates effectively across sites, and takes pride in deep technical debugging, system understanding, and operational excellence, even when architectural direction is defined elsewhere.
At the Staff level, the engineer is expected to drive implementation of the designs assigned to the local engineering team and provide guidance and mentorship to junior engineers.
Key Responsibilities will include
Implement, test, and maintain software solutions for rack provisioning, monitoring, and workload deployment Interface with rack hardware (BMC, PDU, CDU, PCIe switches) using Redfish, SNMP, and IPMI protocols. Integrate rack management software with orchestration platforms (e.g., Kubernetes, Terraform, Foreman). Collaborate with ODMs, ISVs, and internal teams to define and deliver scalable rack software solutions. Develop dashboards and unified telemetry pipelines using Prometheus and Grafana. Support firmware updates, crash dump parsing, and remote diagnostics across rack components. Ensure secure boot, VLAN separation, and QoS enforcement for multi-tenant environments. Contribute to software EBOM and protocol documentation with Pegatron and other partners. Write high‑quality, secure, and maintainable code following established coding standards and best practices. Participate in design discussions and reviews, providing implementation‑focused feedback on feasibility, performance, reliability, and maintainability. Own debugging and root‑cause analysis of complex issues across development, integration, and production environments, including deployed systems. Collaborate closely with architects, lead engineers, hardware, firmware, and platform teams to ensure correct system behavior and smooth integration. Implement observability features such as logging, metrics, and tracing to support troubleshooting and operational reliability. Contribute towards documentation, runbooks, and operational procedures related to the software you own or support. Participate in code reviews, testing efforts, and continuous improvement of build, test, and deployment pipelines. Required Qualifications
Bachelor’s or Master’s degree in Electrical or Computer engineering, Computer Science, Information Systems, or a related field and 5+ years of software engineering experience Experience in infrastructure software development Proficiency in Python, C++, and shell scripting. Experience with Redfish APIs and DCIM tools. Understanding of server hardware, networking, and storage systems. Strong written and verbal communication skills for working effectively in a multi‑site, globally distributed team. Preferred Qualifications
Experience with Node.js is a plus. Experience with Rack Management tools. Knowledge of AI workload orchestration tools (e.g., K8s, RKE2). Experience with tools like Prometheus and Grafana. Familiarity with rack-level lifecycle management. Familiarity with Foreman, Ansible, Chef, and Puppet for provisioning and configuration What's on Offer
Apart from working with great people, we offer the below:
Salary including housing & transport allowance
Stock (RSU's) and performance related bonus
16 weeks fully paid Maternity Leave
6 weeks fully paid Paternity Leave
Employee stock purchase scheme
Child Education Allowance
Relocation and immigration support (if needed)
Life and Medical Insurance
Live+ Well Reimbursement for health and recreational membership fees
Minimum Qualifications:
• Bachelor's degree in Engineering, Information Systems, Computer Science, or related field and 4+ years of Software Engineering or related work experience. OR Master's degree in Engineering, Information Systems, Computer Science, or related field and 3+ years of Software Engineering or related work experience. OR PhD in Engineering, Information Systems, Computer Science, or related field and 2+ years of Software Engineering or related work experience.
• 2+ years of work experience with Programming Language such as C, C++, Java, Python, etc. *References to a particular number of years experience are for indicative purposes only. Applications from candidates with equivalent experience will be considered, provided that the candidate can demonstrate an ability to fulfill the principal duties of the role and possesses the required competencies.
Qualcomm is an equal opportunity employer. If you are an individual with a disability and need an accommodation during the application/hiring process, rest assured that Qualcomm is committed to providing an accessible process. You may e-mail disability-accomodations@qualcomm.com or call Qualcomm's toll-free number found here. Upon request, Qualcomm will provide reasonable accommodations to support individuals with disabilities to be able participate in the hiring process. Qualcomm is also committed to making our workplace accessible for individuals with disabilities. (Keep in mind that this email address is used to provide reasonable accommodations for individuals with disabilities. We will not respond here to requests for updates on applications or resume inquiries).
Qualcomm expects its employees to abide by all applicable policies and procedures, including but not limited to security and other requirements regarding protection of Company confidential information and other confidential and/or proprietary information, to the extent those requirements are permissible under applicable law.
To all Staffing and Recruiting Agencies: Our Careers Site is only for individuals seeking a job at Qualcomm. Staffing and recruiting agencies and individuals being represented by an agency are not authorized to use this site or to submit profiles, applications or resumes, and any such submissions will be considered unsolicited. Qualcomm does not accept unsolicited resumes or applications from agencies. Please do not forward resumes to our jobs alias, Qualcomm employees or any other company location. Qualcomm is not responsible for any fees related to unsolicited resumes/applications.
If you would like more information about this role, please contact Qualcomm Careers.
https://careers.qualcomm.com/careers/job/446718277935?hl=en-US&utm_source=linkedin&domain=qualcomm.com&source=APPLICANT_SOURCE-6-2
Phase 01 — Rack-Scale AI Systems & Data Center Foundations
Difficulty: ⭐⭐☆☆☆ | Estimated Time: 2 weeks Roles supported: Rack Management SWE (Senior/Staff), Data Center SWE, Infra/Platform Engineer Hardware needed: none — both labs run on any laptop
Why This Phase Exists
You cannot write software that manages a thing you cannot picture. Before a single Redfish call or Ansible play, you must be able to draw an AI rack from memory — every box, every cable, every power and cooling path — and reason about it as a system with budgets, failure domains, and a lifecycle. This is the mental model that the rest of the curriculum populates with code.
The JD's first line is "rack management software for AI accelerator products deployed in large-scale data center environments." That sentence hides an enormous amount of physical reality: an AI rack today can draw 40–130+ kW (a traditional enterprise rack drew 5–10 kW), is often liquid-cooled, contains its own management network and dozens of independently-firmwared components, and is the unit that gets provisioned, monitored, and maintained as a whole. Get the physical model wrong and every abstraction above it leaks.
This phase builds that base — not trivia, mechanism and numbers. By the end you can explain why an AI rack needs a CDU, why management traffic rides a separate network, what a "failure domain" means when a single PDU feeds eight nodes, and what fields your inventory database absolutely must have. You will encode that understanding as two pieces of working software: a rack data model and a power/thermal budget calculator.
Concepts
- What "rack-scale" means: the rack as the unit of deployment, sale, and management for AI systems; scale-up vs scale-out
- Rack anatomy: compute trays/nodes, AI accelerators, host CPUs, NICs/DPUs, scale-up fabric (NVLink/NVSwitch-class), top-of-rack (ToR) data switches, management switch, PDUs, busbar, PSU shelves, CDU
- The management plane: BMC (baseboard management controller) as "the computer inside the computer"; out-of-band (OOB) vs in-band; why they are physically separate networks
- Power: 3-phase power, PDUs and busbars, PSU shelves/redundancy (N+1, 2N), rack power density, power budgeting and capping, the difference between nameplate, rated, and measured power
- Cooling: air vs direct-liquid-cooling (DLC), CDU (coolant distribution unit), rear-door heat exchangers, thermal budgets, ΔT, PUE; why AI density forced the shift to liquid
- Units & physical facts: rack U / OpenRack OU, dimensions, weight, BTU/hr, kW↔BTU conversion
- Reference designs: OCP (Open Compute Project) & Open Rack, NVIDIA MGX / HGX / GB200 NVL72, DGX, hyperscaler in-house designs — and where Qualcomm AI accelerators fit
- Failure domains & blast radius: power domain, cooling domain, network domain, the rack as a correlated-failure unit; redundancy vs isolation
- The rack lifecycle: design → ODM build (Pegatron and peers) → integration/L10–L11 → ship → install → commission/burn-in → provision → operate → maintain/RMA → decommission
- The ecosystem: OEM vs ODM vs ISV; the software EBOM; DCIM (data center infrastructure management) and where rack management software sits relative to it
- What rack management software must model: inventory/asset schema, identity, relationships (contains/powers/cools/connects), state, and telemetry
Labs
Lab 01 — Rack Data Model & Inventory Engine (Python)
| Field | Value |
|---|---|
| Goal | Design and implement the core data model a rack manager is built on: a typed object graph of racks → trays → components, with relationships (powers, cools, connects), state, and a query/validation layer. |
| Concepts | Inventory/asset modeling, component identity (FRU, serial, asset tag), relationship graphs, state machines, validation invariants, why the model is the spine of everything. |
| Steps | 1) Read solution.py top-down. 2) Run it; study the rack it builds and the validation report. 3) Extensions: add a CDU + liquid-cooling loop to the model; add a find_blast_radius(component) query; serialize to/from JSON (the Redfish-shaped export). |
| Stack | Python 3 (stdlib + dataclasses), no external deps |
| Output | A working inventory engine that builds a realistic AI rack and reports invariant violations (e.g., a node with no power feed). |
| How to Test | python solution.py — built-in asserts verify the graph, the relationship queries, and the validation invariants. |
| Talking Points | Why inventory is the hardest "boring" problem in DCIM; identity vs location; how Redfish models the same graph (Chassis/Systems/Managers); what a "blast radius" query is for. |
| Resume Bullet | "Designed and implemented a typed rack inventory model (components, relationships, state, validation invariants) forming the data spine of a rack-management toolchain; added blast-radius and Redfish-shaped JSON export." |
| Extensions | Add temporal state (a component's history); add a reconciliation diff between "declared" and "discovered" inventory (the seed of Phase 06's reconcile loop). |
Lab 02 — Power & Thermal Budget Calculator (Python)
| Field | Value |
|---|---|
| Goal | Build the tool that answers "can this rack take one more node?" — sum component power against PDU/feed/breaker limits and heat load against CDU/cooling capacity, with redundancy (N+1, 2N) modeled. |
| Concepts | Power budgeting, breaker derating (the 80% rule), redundancy math, kW↔BTU↔cooling, ΔT and flow, power capping, headroom and alerting thresholds. |
| Steps | 1) python solution.py. 2) Read the budget report for a populated rack. 3) Push it over budget by adding nodes and watch the violations. 4) Extensions: model a PSU/feed failure and re-check redundancy; add per-node power capping to fit a constrained rack. |
| Stack | Python 3 (stdlib) |
| Output | A budget report: total draw vs limits, redundancy status, cooling headroom, and a pass/fail with reasons. |
| How to Test | Asserts verify the 80% breaker rule, N+1 redundancy loss detection, and the kW→BTU conversion. |
| Talking Points | Why nameplate ≠ measured power; the 80% continuous-load rule; why losing one PSU in an N+1 shelf is fine but two is not; how power capping trades performance for fit. |
| Resume Bullet | "Built a rack power/thermal budget calculator enforcing breaker derating and N+1/2N redundancy, with power-capping to fit density-constrained racks; used to validate rack configurations pre-deployment." |
| Extensions | Pull live draw from the Phase 04 PDU/CDU simulator and turn the calculator into a live headroom monitor; emit Prometheus metrics (foreshadows Phase 07). |
Deliverables Checklist
- You can draw an AI rack from memory: trays, accelerators, fabric, ToR, mgmt switch, PDU/busbar, PSU, CDU, BMC
- You can explain OOB vs in-band and why the management network is physically separate
- Inventory engine builds a realistic rack and reports invariant violations
- You can compute a rack's power budget with the 80% breaker rule and N+1 redundancy in your head for simple cases
- Budget calculator flags over-subscription and redundancy loss with reasons
- You can name the rack lifecycle stages and where rack-management software acts in each
- You can do the kW→BTU/hr conversion and explain why AI density forced liquid cooling
Interview Relevance
- "Walk me through the components of an AI rack and how they're powered and cooled."
- "What's the difference between the management network and the data network, and why does it matter for your software?"
- "A rack has two 3-phase 30A feeds. How much IT load can you safely put on it, and why not 100%?"
- "Design the data model for a rack inventory system. What are the entities and relationships?"
- "What's a failure domain? Give an example in a rack and how your software should treat it."
- "Why are AI racks liquid-cooled when enterprise racks weren't?"
Warmup Guide — Rack-Scale AI Systems & Data Center Foundations
Zero-to-expert primer for Phase 01. You need no data-center background: this guide starts at "what is a rack unit" and ends with you sizing power and cooling for a 120 kW AI rack and explaining why your management software needs a separate network. Every later phase — Redfish, BMC, provisioning, telemetry, firmware — stands on this physical model.
Table of Contents
- Chapter 1: Why "Rack-Scale" Is the Unit That Matters
- Chapter 2: Anatomy of an AI Rack, Box by Box
- Chapter 3: The Management Plane — BMC, OOB vs In-Band
- Chapter 4: Power — From the Feed to the Chip
- Chapter 5: Cooling — Why AI Racks Went Liquid
- Chapter 6: Networks — Three of Them, Not One
- Chapter 7: Failure Domains & Blast Radius
- Chapter 8: Reference Designs — OCP, MGX, NVL72, and Where Qualcomm Fits
- Chapter 9: The Rack Lifecycle and the Ecosystem (OEM/ODM/ISV)
- Chapter 10: What Rack-Management Software Must Model
- Lab Walkthrough Guidance
- Success Criteria
- Interview Q&A
- References
Chapter 1: Why "Rack-Scale" Is the Unit That Matters
Zero background: a rack is a standardized steel cabinet (19-inch wide mounting rails, historically ~42U tall, where 1U = 1.75 inches) that holds servers and networking gear. For thirty years the server was the unit of everything — you bought a server, provisioned a server, monitored a server. The rack was just furniture.
The shift: AI training and inference need many accelerators wired together with very fast links (a "scale-up" fabric like NVLink/NVSwitch). Those links only reach a few meters at full bandwidth, so the natural boundary is one rack. Inside a rack, accelerators talk at ~hundreds of GB/s to TB/s; between racks, they drop to network speeds (tens of GB/s). That bandwidth cliff makes the rack the natural unit of design, sale, deployment, and management. NVIDIA sells a GB200 NVL72 as a rack. Hyperscalers deploy and retire capacity in racks.
Two words you must separate:
- Scale-up = make one coherent machine bigger by tightly coupling accelerators (NVLink fabric inside a rack). Latency/bandwidth-sensitive; bounded by physics to ~a rack.
- Scale-out = add more independent machines connected by a network (Ethernet/InfiniBand across racks). This is the data-center-wide dimension.
Why this matters for your software: rack-management software treats the rack as a managed object with sub-components, a power/cooling budget, a single management network, and a correlated failure profile. Your inventory model (Lab 01), your provisioning unit (Phase 05), and your blast-radius reasoning (Chapter 7) all key off "the rack." When the JD says "lifecycle management of rack-scale AI systems," rack-scale is the literal scope of the object you manage.
Common misconception: "a rack is just N servers, manage each server." For AI, the rack has shared resources (power shelf, busbar, CDU, NVSwitch trays, ToR) that belong to no single server and must be managed as rack-level objects. Miss them and you can't model power, cooling, or the scale-up fabric at all.
Chapter 2: Anatomy of an AI Rack, Box by Box
Picture a modern liquid-cooled AI rack from top to bottom. Memorize this list — Lab 01 turns it into a data model.
- Compute trays / nodes (the bulk of the rack): each tray holds host CPUs, system memory, AI accelerators (GPUs / Qualcomm Cloud AI / TPUs / custom ASICs), local NVMe, and NICs. A tray is one or more "nodes" (a node = one OS instance = one BMC, usually).
- AI accelerators: the reason the rack exists. 4–8 per tray is common. Each has its own high-bandwidth memory (HBM), firmware, and thermal profile, and is the hottest, most expensive, most-monitored component.
- Host CPU(s): run the OS, the in-band agents, and feed the accelerators. 1–2 sockets per node.
- NICs / DPUs: the data-plane network interface. AI nodes often have multiple high-speed NICs (one per accelerator for RDMA) plus a separate management NIC. A DPU (SmartNIC) is a NIC with its own CPU/OS that can offload networking, storage, and security — itself a managed device with firmware.
- Scale-up fabric: NVSwitch trays (or vendor equivalent) that wire accelerators into one coherent fabric. In an NVL72 these are dedicated trays with their own management.
- Top-of-Rack (ToR) data switch: the scale-out Ethernet/InfiniBand switch connecting this rack's nodes to the rest of the data center.
- Management switch: a separate, smaller switch carrying only out-of-band management traffic (BMCs, PDUs, CDU controller) — see Chapter 3/6.
- Power shelf / PSUs: AC→DC conversion. In OCP racks, a power shelf feeds a vertical DC busbar that runs the height of the rack; nodes tap the busbar with blind-mate connectors. In legacy racks, each server has its own PSUs fed by PDUs (power distribution units — essentially smart power strips).
- PDU(s): distribute and (if "smart"/metered/switched) measure and control power per outlet or per phase. A managed device you talk to over SNMP/Redfish (Phase 03/04).
- CDU (Coolant Distribution Unit): for liquid cooling — pumps coolant through the rack's cold plates, exchanges heat with facility water, and reports flow/temperature/pressure. A managed device (Chapter 5).
- BMC per node (and often a rack manager / RMC): the always-on management microcontrollers (Chapter 3).
A useful sanity image: an NVL72 rack ≈ 18 compute trays (2 Blackwell GPUs + 1 Grace CPU each → 72 GPUs) + 9 NVSwitch trays + power shelves + busbar + manifold/CDU plumbing + ToR + management switch. ~1.36 metric tons, ~120 kW. That is "rack-scale."
Chapter 3: The Management Plane — BMC, OOB vs In-Band
This is the single most important concept for your job, so go slow.
The BMC (Baseboard Management Controller) is a small, independent computer soldered onto the server's motherboard — typically an ARM SoC (e.g., ASPeed AST2600) running its own Linux (often OpenBMC) with its own memory, its own network port, and its own power rail (standby power). It is the computer inside the computer. It runs even when the host is powered off (as long as the rack has standby power).
What the BMC does:
- Power control: power on/off/cycle the host, regardless of the host OS state.
- Sensors: read temperatures, voltages, fan speeds, power draw — independent of the host.
- Console: Serial-over-LAN (SoL) and KVM (remote keyboard/video/mouse) so you can watch the host boot.
- Virtual media: mount an ISO over the network so you can reinstall the OS remotely.
- Logs: the System Event Log (SEL) and crash/RAS captures (Phase 08).
- Firmware: it can update host firmware (BIOS/UEFI) and its own.
Out-of-band (OOB) vs in-band — the distinction your whole control plane rests on:
- In-band = management that goes through the host OS and its main NIC: SSH to the host, an agent reading
/proc, the data NIC. Works only when the host is up and healthy. - Out-of-band = management that goes through the BMC over the management network, independent of the host OS. Works when the host is off, hung, kernel-panicked, or mid-reinstall.
Why OOB exists / why you cannot live without it: the moments you most need to manage a machine are exactly the moments it's not healthy — it won't boot, it kernel-panicked, it needs an OS reinstall. In-band management is useless then. OOB (Redfish/IPMI to the BMC, Chapter 6 / Phase 03) is how you power-cycle a wedged node, watch why it won't POST, and reinstall it — from a continent away. The JD's "remote diagnostics," "firmware updates," and "support of deployed systems" all live on the OOB plane.
Mental model: every node is really two computers on two networks — the host (in-band, data network) and the BMC (out-of-band, management network). Rack management software spends most of its time talking to the second one.
Misconception: "the BMC is part of the OS." No — it's separate silicon with separate firmware and power. You can redfish GET a node's sensors while its host OS is completely dead. That independence is the entire point.
Chapter 4: Power — From the Feed to the Chip
Power is where rack-scale AI breaks everything that worked at enterprise scale. You must be able to do this math (Lab 02).
The path: utility → data hall → 3-phase AC to the rack → PDU/power-shelf → PSUs (AC→DC) → busbar/cables → node → VRMs → chip.
3-phase power, minimally: data centers distribute 3-phase AC because it carries more power per conductor. A common rack feed is, e.g., 3-phase 415V 30A. Usable power ≈ √3 × V_line-to-line × I × power-factor ≈ 1.732 × 415 × 30 × ~1 ≈ 21.6 kW per feed (regional voltages vary; the point is the formula). Two feeds for redundancy → ~43 kW available, but see the 80% rule.
The 80% rule (NEC continuous-load derating): a breaker rated for continuous load may only be loaded to 80% of its rating. A 30A circuit → budget to 24A. This is not optional; exceed it and the breaker trips (an outage) or you create a fire risk. Every power-budget calculation a rack manager does derates by 0.8. Lab 02 enforces this.
Redundancy schemes (memorize):
- N: just enough capacity, no spare. One failure = outage.
- N+1: one spare unit; survives a single PSU/feed failure. The common default.
- 2N: fully duplicated (two independent feeds, each able to carry the whole load). Survives a whole side failing. Used for the most critical gear.
The trap: in N+1, the load must fit on N units after one fails. If you fill the rack assuming N+1 capacity, a single failure overloads the survivors and cascades. Lab 02's redundancy check is exactly this.
Power density, then and now:
- Enterprise rack (2010s): 5–10 kW.
- Dense compute: 15–20 kW.
- AI air-cooled: 30–40 kW (near the limit of what air can remove).
- AI liquid-cooled (NVL72-class): 60–130+ kW per rack, heading toward 250 kW–1 MW in next-gen designs.
That ~10× jump is the reason this role exists: at 120 kW, power and cooling are first-class software-managed budgets, not facility afterthoughts.
Nameplate vs rated vs measured:
- Nameplate (TDP/max): the worst-case the component could draw. Sizing breakers naively to nameplate strands huge capacity.
- Rated/derated: what you provision for after derating.
- Measured (telemetry): actual draw, read from PDU/BMC (Phase 04/07). Almost always well below nameplate.
Power capping: the BMC/accelerator can be told to cap power (clock/voltage throttle) to fit a budget — trading performance for thermal/power fit. Rack management software uses capping to (a) safely oversubscribe based on measured behavior and (b) ride out a cooling/power degradation without a hard outage. Lab 02's extension implements capping-to-fit.
Chapter 5: Cooling — Why AI Racks Went Liquid
Every watt of power becomes a watt of heat. A 120 kW rack produces 120 kW of heat that must leave the building, or the chips throttle and then die.
The conversion to know cold: 1 kW = 3,412 BTU/hr. A 120 kW rack dumps ~409,000 BTU/hr. For intuition: that's roughly the heat of ~34 home ovens running flat-out, in one cabinet.
Why air stops working: air has low heat capacity; removing 30–40 kW from a rack already requires gale-force airflow and chilled aisles. Beyond ~40 kW, air physically can't keep modern accelerators below their throttle temperature. So the industry moved to liquid.
Direct Liquid Cooling (DLC): coolant runs through cold plates bolted directly onto the hot chips (accelerators, sometimes CPUs/VRMs). Liquid carries ~3,500× the heat per volume of air. Variants: cold-plate (most common), rear-door heat exchangers (a radiator on the rack's back door), and full immersion (the whole board in dielectric fluid — rarer).
The CDU (Coolant Distribution Unit): the heart of a liquid-cooled rack. It:
- pumps the secondary (rack-side) coolant loop through the cold plates,
- exchanges heat with the facility's primary (chilled-water) loop via a heat exchanger (keeping the clean rack loop isolated from facility water),
- reports flow rate (L/min), supply/return temperatures, ΔT, pressure, pump speed, and leak detection.
That last bullet is yours: the CDU is a managed device (Redfish/Modbus/SNMP) your software monitors. A failing pump or a rising ΔT is an early warning of a thermal event; leak detection is a safety-critical alert that may trigger an emergency drain/shutdown.
Thermal budget math (Lab 02): heat removed = flow × fluid heat capacity × ΔT (supply vs return temperature). If component heat load exceeds the CDU's rated removal at safe ΔT, the rack overheats. Your calculator checks heat load against cooling capacity exactly as it checks power against breakers.
PUE (Power Usage Effectiveness): total facility power ÷ IT power. 1.0 is perfect (all power does compute). Air-cooled enterprise: ~1.5–2.0. Liquid-cooled AI halls push toward ~1.1. PUE is a facility KPI, but rack-level cooling efficiency feeds it — and "tokens per joule" / "performance per watt" is increasingly how AI infra is judged.
Misconception: "cooling is the facility team's problem." At 120 kW with liquid loops, cooling telemetry (CDU flow/ΔT/leak) is part of the rack's health that your software monitors and acts on. A leak or pump failure is a software-visible, software-actionable event.
Chapter 6: Networks — Three of Them, Not One
A common beginner mistake is imagining "the network." An AI rack has (at least) three logically separate networks, and conflating them is a security and reliability bug.
- Data / compute network (in-band, scale-out): high-speed Ethernet or InfiniBand (e.g., 400/800 GbE, NDR IB) connecting nodes to the ToR and out to the cluster. Carries the actual AI traffic (gradients, activations, RDMA). Highest bandwidth, tenant-visible.
- Scale-up fabric (intra-rack): NVLink/NVSwitch-class links wiring accelerators into one coherent memory fabric. Not "Ethernet"; managed as part of the rack's hardware.
- Management network (out-of-band): a separate, modest (often 1 GbE) network connecting every BMC, PDU, CDU, and managed switch to the management/control plane. This is where Redfish/IPMI/SNMP live. It is physically and administratively isolated from tenant data.
Why management is separate (this is an interview favorite):
- Reliability: you must reach the BMC to fix a node even when its data network/host is down. If management rode the data network, a data-network outage would also blind your control plane — exactly when you need it.
- Security: BMCs are extraordinarily powerful (full power and firmware control) and historically under-hardened. Exposing them on the tenant network would be catastrophic. They live on an isolated management VLAN reachable only by the control plane (Phase 09).
- Blast-radius: separating planes means a problem in one doesn't take out the other.
The JD's "VLAN separation" (Phase 09) is, in large part, about keeping these planes — and different tenants within the data plane — isolated. Hold this thought; Phase 09 builds it for real.
Chapter 7: Failure Domains & Blast Radius
A failure domain is a set of components that fail together because they share something. The blast radius of a fault is everything its failure domain contains. Designing and operating racks is largely the art of knowing and bounding blast radius — and your inventory model (Lab 01) must encode it.
Failure domains in a rack:
- Power domain: everything fed by one PDU/feed/breaker. If a 24 kW feed dies and it powered 6 nodes without redundancy, those 6 nodes drop together. (N+1/2N exists to shrink this.)
- Cooling domain: everything cooled by one CDU/loop/manifold. A pump failure or leak can take the whole rack's liquid-cooled components thermal at once.
- Network domain: everything behind one ToR or one management switch. A ToR failure isolates the whole rack's data plane; a management-switch failure blinds your OOB control of the whole rack.
- The rack itself: shared roof, shared facility water valve, shared row PDU — correlated failures up the hierarchy (rack → row → hall → site).
Why this matters operationally: workload placement and maintenance must respect domains. You don't put all replicas of a service on nodes sharing one PDU. You drain a whole power domain before maintenance on its feed. When something fails, the first diagnostic question is "what's the failure domain?" — a single node, or a shared resource taking many nodes with it? Lab 01's find_blast_radius(component) query is the software form of this question, and it's the seed of correlation logic your telemetry (Phase 07) and RCA (Phase 10) will need.
Redundancy vs isolation (don't conflate): redundancy (N+1) lets a domain survive a failure; isolation (separate domains, VLANs, MIG-style partitioning) contains a failure or a tenant. You design for both.
Chapter 8: Reference Designs — OCP, MGX, NVL72, and Where Qualcomm Fits
You don't need to memorize every SKU, but a leader/engineer in this space speaks the reference-design vocabulary.
- OCP (Open Compute Project): the open-hardware community (started by Meta) that standardized hyperscale rack design: Open Rack (a wider 21-inch equipment space, 48 "OU"), the busbar/power-shelf model, and open management specs. Much of "modern rack" thinking is OCP-derived. Redfish, the OpenBMC firmware, and OCP's hardware management specs are the standards your software targets.
- NVIDIA HGX / DGX / MGX: HGX is the baseboard of 8 GPUs + NVSwitch; DGX is NVIDIA's complete server; MGX is a modular reference architecture OEMs/ODMs build to. GB200 NVL72 is the rack-scale design (72 Blackwell GPUs as one NVLink domain, liquid-cooled, ~120 kW) — the canonical "AI rack" today.
- Hyperscaler in-house: Google TPU pods, AWS Trainium/Inferentia racks, Meta's OCP-based designs, Microsoft Maia — each a rack-scale system with its own management software (the kind you'd build).
- Where Qualcomm fits (this JD): Qualcomm's data-center AI accelerators (the Cloud AI 100 line and its successors) deploy in rack-scale systems. This role is the software that provisions, monitors, and maintains those racks — interfacing with the BMCs, PDUs, CDUs, and PCIe switches of accelerator products, integrating with orchestration and telemetry, and co-developed with ODM partners (the JD names Pegatron). The accelerator is the payload; your software runs the rack around it.
The strategic insight: hardware vendors increasingly win or lose on the software that operates the hardware at scale. A great accelerator with a flaky rack-management stack is unsellable to a hyperscaler. That's why this role is senior/staff and why the JD stresses production debugging and operational excellence.
Chapter 9: The Rack Lifecycle and the Ecosystem (OEM/ODM/ISV)
Rack management software acts across a rack's entire life. Know the stages and the players.
The ecosystem:
- OEM (Original Equipment Manufacturer): sells the branded system (e.g., NVIDIA, Dell, HPE — or, for accelerators, Qualcomm).
- ODM (Original Design Manufacturer): designs/builds the hardware, often white-label, for OEMs and hyperscalers. Pegatron (named in the JD), Quanta, Wiwynn, Foxconn are ODMs. You co-define interfaces, the software EBOM, and protocol behavior with them (Phase 12).
- ISV (Independent Software Vendor): third-party software (orchestrators, DCIM, monitoring) you integrate with.
The lifecycle (and where your software acts):
- Design / NPI (New Product Introduction): define hardware + the management interfaces (Redfish schemas, sensor lists, the EBOM). Staff engineers contribute here (Phase 12).
- Manufacture & integration (L6–L11): the ODM builds boards (L6), nodes (L10), and integrates the full rack (L11), running factory tests. Your software may provide the test/validation tooling.
- Ship & install: rack arrives, is bolted down, powered, plumbed (liquid), and cabled.
- Commission / burn-in: discovery (find every BMC/PDU/CDU on the management network), inventory capture, firmware baseline, stress/burn-in tests, validation against the EBOM. This is rack-management software's first big job.
- Provision: install OS/firmware, configure networking, join the orchestrator (Phase 05/06).
- Operate: monitor (Phase 07), enforce tenancy/QoS (Phase 09), serve workloads.
- Maintain: firmware updates (Phase 08), diagnostics/RCA (Phase 08/10), component RMA (replace a failed accelerator/PSU/pump and re-validate).
- Decommission: secure wipe, asset retirement, update inventory.
Why a Staff engineer cares about the whole arc: bugs and design gaps surface at the seams between stages (e.g., a firmware baseline mismatch found only at burn-in). Owning "implementation through deployment and debugging in real-world conditions" (the JD's words) means your code and your mental model span this entire lifecycle.
Chapter 10: What Rack-Management Software Must Model
Synthesize the above into the thing you build first (Lab 01): the model. Get this right and everything composes; get it wrong and every feature fights the schema.
Entities (the nouns):
Site→Hall→Row→Rack→Tray/Node→Component(accelerator, CPU, NIC/DPU, NVMe, PSU, fan, BMC) — a containment hierarchy.- Shared rack-level objects:
PDU,Busbar,PowerShelf,CDU,ToRSwitch,MgmtSwitch,NVSwitchTray.
Identity (how you name a thing, durably):
- Asset tag (your inventory ID), serial number (manufacturer), FRU (Field Replaceable Unit) data, MAC/UUID, and location (rack + U position). Critical distinction: identity ≠ location — a node keeps its serial when it moves U positions or racks; your model must track both and reconcile them (a swapped-but-not-updated location is a classic data-integrity bug).
Relationships (the edges — this is what makes it a graph, not a table):
contains(rack contains node),powers(PDU powers node),cools(CDU cools node),connects(ToR connects node),manages(BMC manages node). These edges are whatfind_blast_radiustraverses.
State:
- Power state (on/off/standby), health (ok/warning/critical), lifecycle state (discovered/provisioning/ready/maintenance/decommissioned), firmware versions per component.
Telemetry (time-series, attached to components): temperature, power, fan/pump speed, errors — fed by Phases 04/07.
Invariants (validation rules your model enforces — Lab 01):
- Every node has at least one power feed and (if liquid) a cooling path.
- Power draw ≤ derated budget (links to Lab 02).
- No two components claim the same U position.
- Every managed device has a reachable management address.
Declared vs discovered: your model holds the declared intent (the EBOM / what should be there) and the discovered reality (what the management network actually found). The diff between them is gold: missing components, firmware drift, mislabeled locations. This declared-vs-discovered reconciliation is the conceptual seed of the Kubernetes operator pattern in Phase 06 and the RCA work in Phase 10.
Lab Walkthrough Guidance
Order: Lab 01 → Lab 02. Lab 01 builds the model (the nouns and edges); Lab 02 reasons over it (the budgets). Don't invert — the calculator consumes the model.
Lab 01 (Rack inventory engine):
- Read
solution.pytop-down before running; the dataclasses are Chapter 10. Predict what the validation report will flag. - Run it; check the rack it builds against your mental Chapter-2 picture.
- Extensions in order: (a) add a CDU +
coolsedges (Chapter 5); (b) implementfind_blast_radius(component)by traversingpowers/cools/connectsedges (Chapter 7); (c) JSON export shaped like Redfish's Chassis/Systems tree (foreshadows Phase 03).
Lab 02 (Power/thermal budget):
python solution.py— read the budget report for the populated rack.- Add nodes until you trip the 80% breaker rule; predict the failing feed before the tool tells you.
- Extensions: (a) fail one PSU in an N+1 shelf and confirm the survivors stay within budget; fail two and watch it go critical; (b) apply per-node power capping to fit a rack that's 10% over budget — the performance/fit tradeoff (Chapter 4).
The capstone question for the phase: "A partner proposes 18 trays at 6.5 kW each in a rack with two 30A 415V feeds, N+1, liquid-cooled with a 130 kW CDU. Does it fit — power and thermal — and what's the blast radius of one feed failing?" If you can answer that with numbers, Phase 01 did its job.
Success Criteria
You're done with this phase when — without notes:
- You can draw an AI rack and name every box and which of the three networks each sits on (Ch. 2, 6)
- You can explain OOB vs in-band and give two reasons the management network is separate (Ch. 3, 6)
- You can size a 3-phase feed, apply the 80% rule, and check N+1 redundancy in your head (Ch. 4)
- You can convert kW→BTU/hr and explain why >40 kW forced liquid cooling and what a CDU does (Ch. 5)
- You can define a failure domain and compute a simple blast radius (Ch. 7)
- You can name the rack lifecycle stages and what rack-management software does in each (Ch. 9)
- Your inventory engine builds a valid rack, flags invariant violations, and answers a blast-radius query (Lab 01)
- Your budget calculator enforces derating + redundancy and supports capping-to-fit (Lab 02)
Interview Q&A
Q1: Walk me through the components of an AI rack and how it's powered and cooled. A: Top-level, the rack holds compute trays (each with host CPUs, 4–8 AI accelerators with HBM, local NVMe, and multiple NICs/DPUs), scale-up fabric trays (NVSwitch-class) wiring the accelerators into one coherent fabric, a top-of-rack data switch for scale-out, and a separate management switch. Power comes in as 3-phase AC to a power shelf or PDUs; in OCP designs a power shelf feeds a vertical DC busbar that nodes blind-mate onto, with N+1 or 2N PSU redundancy. Because these racks draw 60–130+ kW — about 10× a legacy rack — they're liquid-cooled: a CDU pumps coolant through cold plates on the chips and exchanges heat with facility water, reporting flow, ΔT, pressure, and leak status. Every node also has a BMC on the management network. The key framing: it's not "N servers," it's a system with rack-level shared resources (power, cooling, fabric) that I manage as first-class objects.
Q2: What's the difference between out-of-band and in-band management, and why does it matter for your software? A: In-band management goes through the host OS and its data NIC — SSH, an agent reading /proc — and only works when the host is healthy. Out-of-band goes through the BMC, a separate always-on microcontroller with its own NIC on a separate management network, and works even when the host is off, hung, or kernel-panicked. It matters because the moments you most need to manage a node — won't boot, panicked, needs reinstall — are exactly when in-band is dead. So rack-management software does power control, firmware updates, console access, and diagnostics over OOB (Redfish/IPMI to the BMC). My control plane assumes the host may be down and reaches everything through the management network.
Q3: A rack has two 3-phase 415V 30A feeds, configured N+1. How much IT load can you safely place, and why not 100%? A: Per feed, usable ≈ √3 × 415 × 30 ≈ 21.6 kW, but the 80% continuous-load rule (NEC) caps a breaker at 80% of rating, so budget ≈ 17.3 kW per feed. With N+1, the surviving feed must carry the whole load after one fails, so the safe IT load is bounded by a single derated feed ≈ 17.3 kW — not the sum. I never budget to the combined or the nameplate capacity: exceed 80% and the breaker trips (an outage); budget to the sum and a single feed failure overloads the survivor and cascades. Measured draw is usually well below nameplate, so I can oversubscribe carefully using telemetry and power capping, but the guaranteed safe number is the derated single-feed figure.
Q4: Why are AI racks liquid-cooled when enterprise racks weren't, and what does that add to your job? A: Power density. Enterprise racks ran 5–10 kW, removable by air. AI racks run 60–130+ kW, and air physically can't remove that much heat past ~40 kW — 1 kW is 3,412 BTU/hr, so a 120 kW rack dumps ~409,000 BTU/hr. Liquid carries thousands of times more heat per volume, so coolant runs through cold plates on the chips, with a CDU pumping it and exchanging heat with facility water. For my software it adds a whole managed subsystem: I monitor CDU flow, supply/return ΔT, pressure, pump health, and leak detection; a rising ΔT or failing pump is an early thermal-event warning, and a leak is a safety-critical alert that may trigger emergency shutdown. Cooling becomes a software-visible, software-actionable part of rack health, not just a facility concern.
Q5: What's a failure domain? Give an example and how your software should treat it. A: A failure domain is a set of components that fail together because they share a resource; the blast radius is everything in it. Example: six nodes fed by one non-redundant PDU/breaker — if that feed dies, all six drop simultaneously. My software treats domains in three ways: (1) the inventory model encodes the powers/cools/connects edges so I can compute blast radius; (2) placement respects domains — don't put all replicas of a service behind one PDU or one ToR; (3) during incidents, the first question is "single node or shared resource?" — correlating alerts by domain turns a flood of node alarms into one root cause. Redundancy (N+1) lets a domain survive a failure; isolation (separate domains/VLANs) contains it — I design for both.
Q6: Design the data model for a rack inventory system. What are the entities, relationships, and the hard parts? A: Entities are a containment hierarchy — Site→Hall→Row→Rack→Node→Component — plus rack-level shared objects (PDU, busbar/power shelf, CDU, ToR, management switch, NVSwitch trays). Relationships are a graph, not just containment: powers, cools, connects, manages — those edges drive blast-radius queries. Each thing needs durable identity (asset tag, serial, FRU, MAC/UUID) kept separate from location (rack + U), because identity persists when a unit moves and a stale location is a classic bug. State covers power, health, lifecycle stage, and per-component firmware versions; telemetry is time-series attached to components. The hard parts: (1) identity-vs-location reconciliation; (2) declared-vs-discovered diffing — the model holds intent (the EBOM) and reality (what discovery found), and the diff catches missing parts and firmware drift; (3) validation invariants — every node has power and cooling, no two parts share a U, every managed device is reachable. That declared-vs-discovered reconcile is conceptually the same loop a Kubernetes operator runs.
Q7 (Staff): How would you approach commissioning a brand-new rack from an ODM partner? A: Commissioning is discovery → inventory → baseline → validate → hand-off. First, on the isolated management network, discover every managed endpoint (BMCs, PDUs, CDU, switches) — typically via DHCP + a Redfish/IPMI sweep. Capture full inventory (serials, FRU, firmware versions, accelerator topology) and reconcile it against the declared EBOM agreed with the ODM (e.g., Pegatron) — any missing/extra/mismatched part is a finding raised back to them. Set the firmware baseline (Phase 08) so the whole rack is at known-good versions. Run burn-in/stress and validate power and thermal behavior against budget (Phase 01 calculator) and the spec. Capture a golden inventory + telemetry baseline so future drift is detectable. Only then mark the rack "ready" and hand it to provisioning. The Staff value-add is making this repeatable and automated — a commissioning pipeline with a clear pass/fail and a documented escalation path to the partner — because at fleet scale you commission racks continuously, not once.
References
- Open Compute Project — Open Rack & hardware management specs — https://www.opencompute.org/
- DMTF Redfish — Schema & Specification (the management data model you'll meet in Phase 03) — https://www.dmtf.org/standards/redfish
- NVIDIA GB200 NVL72 — architecture overview — https://www.nvidia.com/en-us/data-center/gb200-nvl72/
- ASHRAE TC 9.9 — Thermal Guidelines for Data Processing Environments (cooling/ΔT references)
- Uptime Institute — Tier topology & redundancy (N, N+1, 2N) concepts — https://uptimeinstitute.com/
- The Open19 / OCP power & busbar design notes — for the power-shelf/busbar model
- Barroso, Hölzle, Ranganathan, The Datacenter as a Computer, 3rd ed. — the canonical systems view of warehouse-scale machines (free online)
- Qualcomm Cloud AI — product context for the accelerators this role's racks carry — https://www.qualcomm.com/products/technology/processors/cloud-artificial-intelligence
- Cross-track: Head of Software Engineering — GPU, Phase 01 WARMUP — the silicon inside the accelerators you manage
🛸 Hitchhiker's Guide — Phase 01: Rack-Scale AI Systems
Read this if: you can write software but a data-center rack is still a black box labeled "servers go here." This is the practitioner's compressed tour — the numbers, mental models, and war stories the WARMUP derives from first principles. Skim it first, then read the WARMUP slowly.
0. The 30-second mental model
An AI rack is one machine the size of a wardrobe that draws as much power as a small apartment building (60–130+ kW), is plumbed with liquid coolant, and contains two networks worth of computers: the hosts (on the data network) and a shadow fleet of BMCs/PDUs/CDU controllers (on a separate management network). Your software lives on that second network and treats the rack as a managed object with a power budget, a cooling budget, a failure-domain graph, and a lifecycle. If you remember one sentence: you manage the rack out-of-band, as a system, not the servers in-band, one at a time.
1. The org chart of an AI rack
RACK (≈42U / 48 OU, ~1.2–1.4 t, 60–130+ kW, liquid-cooled)
├── Compute trays ×N ← host CPU(s) + 4–8 accelerators (HBM) + NVMe + NICs/DPUs
│ └── BMC per node ← always-on ARM SoC (OpenBMC); the OOB brain
├── NVSwitch / scale-up trays ← wire accelerators into one NVLink-class fabric
├── ToR data switch ← scale-out: 400/800 GbE or InfiniBand to the cluster
├── Management switch ← OOB only: BMCs, PDUs, CDU (separate, isolated)
├── Power shelf + busbar / PDUs ← 3-phase AC → DC; N+1 / 2N; metered + switched
└── CDU + manifolds + cold plates ← pumps coolant, exchanges heat w/ facility water
Two ratios to tattoo on your arm:
- Power density jump: enterprise 5–10 kW → AI liquid 60–130+ kW ≈ ~10×. That jump is the whole reason this role exists.
- 1 kW = 3,412 BTU/hr. A 120 kW rack = ~409,000 BTU/hr of heat to remove. Air can't; hence liquid.
2. OOB vs in-band in 4 bullets
- Every node is two computers: the host (in-band, data NIC) and the BMC (out-of-band, mgmt NIC, standby power).
- The BMC works when the host is off, hung, or panicked — which is exactly when you need it.
- Redfish/IPMI/SNMP (Phase 03) ride the management network; SSH/agents ride the data network.
- Rule: assume the host is down and reach everything through OOB. Your control plane's reliability depends on it.
3. The power math you'll actually use
| Thing | Number / rule |
|---|---|
| 3-phase usable power | √3 × V_LL × I × PF ≈ 1.732 × V × A |
| Breaker derating | 80% continuous-load rule — always multiply by 0.8 |
| N+1 redundancy | load must fit on N units after one fails — size to a single feed, not the sum |
| Nameplate vs measured | measured is usually 40–70% of nameplate; oversubscribe on telemetry, not faith |
| Power capping | trade clocks/perf for power/thermal fit; lets you ride out degradation w/o outage |
War story shape: "the rack tripped a breaker at 2 a.m." almost always traces to budgeting against nameplate or the sum of redundant feeds instead of the derated single feed. The fix is a budgeting tool that bakes in 0.8 and N+1 (Lab 02) — and blocks a placement that violates it.
4. Cooling, quantified
- Heat removed by liquid = flow (L/min) × fluid heat capacity × ΔT (return − supply). Watch ΔT rising at constant load → fouling/flow problem → pre-failure signal.
- CDU = pump + heat exchanger + sensors. It isolates the clean rack loop from facility water and reports flow/ΔT/pressure/pump-speed/leak.
- Leak detection is safety-critical: it can trigger an automated drain/shutdown. Treat it like a smoke alarm in code: highest priority, fail-safe.
- PUE = facility power ÷ IT power. Liquid AI halls aim ~1.1; the metric the business cares about is increasingly performance/watt and tokens/joule.
5. The three networks (don't conflate them)
| Network | Speed | Carries | Who touches it |
|---|---|---|---|
| Data / scale-out | 400/800 GbE, NDR IB | AI traffic, RDMA | tenants, hosts |
| Scale-up fabric | NVLink-class | accelerator↔accelerator | the rack hardware |
| Management (OOB) | ~1 GbE | Redfish/IPMI/SNMP to BMC/PDU/CDU | only the control plane |
Putting BMCs on the tenant network is the classic catastrophic mistake: a BMC owns power and firmware: full physical control. Phase 09 isolates these for real with VLANs.
6. Failure domains: the first question in every incident
When alerts flood in, ask "what's the shared resource?" before "what's wrong with node X?":
- 6 nodes down at once, same PDU → power domain, not 6 separate faults.
- Whole rack thermal-throttling → cooling domain (CDU/pump/loop).
- Rack unreachable in-band but BMCs answer → ToR/data-network domain, hosts are fine.
- Rack unreachable OOB → management-switch domain; you may be blind, escalate to hands-on.
Encoding the powers/cools/connects edges (Lab 01) turns a 30-alarm storm into one root cause. This is the bridge to Phase 07 (alert correlation) and Phase 10 (RCA).
7. The lifecycle, and where you write code
design/NPI → ODM build (Pegatron) → ship/install → COMMISSION/burn-in → PROVISION → OPERATE → MAINTAIN/RMA → decommission
(P12) (EBOM, P12) (P01/03/08 here!) (P05/06) (P07/09) (P08/10)
The under-appreciated one is commissioning: discover every endpoint on the mgmt network, capture inventory, reconcile against the EBOM, set the firmware baseline, burn-in, validate power/thermal, then mark "ready." At fleet scale you commission racks continuously, so it must be an automated pipeline — a great Staff-level thing to own.
8. Vocabulary that makes you sound like you've been there
- FRU (Field Replaceable Unit) — the granularity of "swap this part."
- U / OU — rack unit (1.75") / OpenRack unit (taller, wider 21" space).
- Busbar — the vertical DC rail nodes blind-mate onto in OCP racks.
- DLC / cold plate / RDHx / immersion — direct liquid cooling variants.
- DPU/SmartNIC — a NIC with its own CPU/OS; itself a managed, firmwared device.
- DCIM — Data Center Infrastructure Management; the facility-level system your rack software feeds and integrates with.
- L6/L10/L11 — manufacturing integration levels (board / node / rack).
- NPI — New Product Introduction; the pre-production phase where interfaces and the EBOM get defined with the ODM.
- Blast radius / failure domain — say these and people know you've held a pager.
9. The mistakes that mark a beginner (avoid in interviews)
- "Just SSH to the node" — for a node that won't boot, there's no SSH; it's a Redfish/SoL problem.
- Budgeting power to nameplate or to the sum of redundant feeds.
- Treating cooling as "the facility's problem" when CDU telemetry is part of rack health your code acts on.
- One flat inventory table with no relationship edges → you can't compute blast radius or reconcile.
- Conflating identity (serial) with location (rack+U) → stale location bugs after every RMA.
- Putting management traffic on the data network → reliability and security disaster.
10. How this phase pays off later
- The inventory model (Lab 01) is literally reused/expanded in the Phase 13 capstone.
- The declared-vs-discovered idea becomes the Phase 06 operator reconcile loop.
- The failure-domain graph powers Phase 07 alert correlation and Phase 10 RCA.
- The OOB-first mindset is the premise of Phases 03 (protocols), 04 (hardware), and 08 (firmware/diag).
- The power/thermal budgets reappear as live telemetry thresholds in Phase 07.
You're ready for the WARMUP. Read it slowly — the numbers in it are the ones interviewers probe.
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.
Lab 02 — Power & Thermal Budget Calculator
Phase: 01 — Rack-Scale Foundations | Difficulty: ⭐⭐⭐☆☆ | Time: 3–4 hours Language: Python (stdlib only) | Hardware: none
Concept primer:
../WARMUP.mdCh. 4 (power) & Ch. 5 (cooling),../HITCHHIKERS-GUIDE.md§3–§4.
Run
python3 solution.py # evaluates three rack scenarios + self-tests
0. The mission
Build the tool that prevents 2 a.m. outages: a calculator that decides whether a rack configuration is safe on power and cooling, with redundancy modeled correctly. This is the math a rack manager applies before every deployment and every "can we add a node?" request — and getting it wrong (budgeting to nameplate, or to the sum of redundant feeds) is one of the most common, most expensive infrastructure mistakes.
Two questions you must be able to answer at the end:
- Why is the safe load on two N+1 feeds equal to one derated feed, not two?
- Why do you multiply every breaker by 0.8 before you do anything else?
1. The three rules encoded
| Rule (WARMUP) | In the code |
|---|---|
| 3-phase power: √3·V·I·PF (Ch. 4) | three_phase_kw() |
| 80% continuous-load derating (Ch. 4) | BREAKER_DERATE, PowerFeed.derated_kw() |
| N / N+1 / 2N redundancy (Ch. 4) | PowerBudget.usable_kw() |
| Heat removed = flow·cp·ΔT; 1 kW = 3412 BTU/hr (Ch. 5) | CoolingLoop.removable_kw_at_dt() |
| Power capping to fit (Ch. 4) | RackConfig.cap_to_fit() |
2. What the run shows
Three scenarios, same hardware, different answers — the whole lesson in one screen:
- Scenario A (fits): 8 nodes at 1.8 kW = 15 kW on N+1 (usable ≈ 16.9 kW). Passes, but note the low power headroom (+1.9 kW) vs huge cooling headroom — power is the binding constraint here, as it usually is in dense AI racks.
- Scenario B (over budget): denser nodes push it to 19.8 kW > 16.9 kW usable. The tool fails it and proposes a per-node cap (2.04 kW) that would make it fit — trading a little performance for a safe deployment.
- Scenario C (no redundancy): the same 19.8 kW load "fits" if you drop to N (no spare) — usable jumps to 33.8 kW. This is the dangerous shortcut a tired engineer reaches for; the tool makes the redundancy tradeoff explicit instead of hidden.
The contrast between B and C is the point: redundancy is a choice with a power cost, and your tooling should surface it, not silently assume it away.
3. Extension exercises (do them — this is the lab)
- Simulate a feed failure: add
PowerBudget.with_failed_feed(name)and re-check the load under the surviving feeds. Confirm that a config sized for N+1 still passes after one feed dies, and that an N-sized config goes critical. This is the redundancy promise, verified. - Real cooling at risk: drop the CDU flow (a failing pump) from 120 to 40 L/min and
re-evaluate. Watch a previously-fine rack become cooling-bound. Then raise
max_return_temp_cand explain why running hotter buys capacity but shrinks the safety margin to throttle. - Per-component power, not flat per-node: replace
node_power_kwwith a list of components (accelerators at 350 W, CPU at 350 W, NIC at 25 W) and compute draw from the bill of materials — then wire this lab to Lab 01's model so the calculator consumes the inventory graph directly. - Live headroom monitor: instead of a static
node_power_kw, accept measured draw (you'll get this from the Phase 04 PDU/CDU simulator) and emit a Prometheus-stylerack_power_headroom_kwgauge (foreshadows Phase 07). Alert when headroom < 10%.
4. Common pitfalls
- Budgeting to the sum of N+1 feeds — the #1 real-world outage cause. Usable is one derated feed, because the survivor must carry everything.
- Skipping the 0.8 derate — a breaker sized to its nameplate trips under sustained load. Derate first, always.
- Confusing rated capacity with ΔT-limited capacity — a CDU rated 130 kW only
removes that much if flow and ΔT allow; at low flow or low ΔT it removes far less.
removable_kw_at_dt()takes the min of the two for a reason. - Assuming nameplate = draw — measured power is usually 40–70% of nameplate; budgeting to nameplate strands capacity, budgeting to measured needs telemetry and capping as a safety net.
5. What this lab proves about you
You can do — in your head and in code — the power/cooling math that gates every rack deployment, and you understand redundancy as an explicit, costed tradeoff rather than a checkbox. In an interview, "two 30A 415V feeds, N+1, how much load?" is a fast filter; you answer with √3, the 0.8 rule, and "one derated feed ≈ 17 kW, because the survivor carries it all" — and then you mention capping and measured-vs-nameplate as the levers that let you push utilization safely. That's the difference between a coder and someone who can be trusted with a 120 kW rack.
Phase 02 — Production Systems Programming (Python, C++, Shell, Node.js)
Difficulty: ⭐⭐⭐☆☆ | Estimated Time: 2 weeks Roles supported: Rack Management SWE (Senior/Staff), Infra/Systems SWE Hardware needed: none — a C++17 compiler and Python 3 on any Linux/macOS box
Why This Phase Exists
The JD's hard requirement is blunt: "Proficiency in Python, C++, and shell scripting" and "Write high‑quality, secure, and maintainable code following established coding standards and best practices." Node.js is "a plus." This is the phase where you stop writing scripts and start writing infrastructure software — code that runs unattended, close to hardware, in production, for years, on someone else's pager.
The difference is not syntax. It is robustness under failure: a rack daemon must survive a BMC that stops responding mid-poll, a sensor that returns garbage, a SIGTERM during a critical section, a disk that fills, and a malicious or malformed input — and it must do so observably, with logs and exit codes an on-call engineer can reason about at 3 a.m. The same code must be testable without the hardware (you can't ship a BMC to CI) and maintainable by a distributed team you've never met.
This phase establishes the engineering bar that every other phase's labs are held to. You build two pieces of real infrastructure software — a C++ sensor daemon and a Python rack CLI — that demonstrate the production patterns: RAII and resource safety, typed interfaces, structured logging, retries with backoff, graceful shutdown, safe subprocess handling, and the testability that lets you mock hardware.
Concepts
The language matrix — why each tool, where
- C++ — close-to-hardware, long-running, performance- and resource-sensitive code: daemons, agents, anything that talks to a device library or parses binary protocols. RAII, smart pointers, the rule of zero/five,
const-correctness, error handling (exceptions vsstd::expected/error codes), modern C++17/20, CMake, sanitizers (ASan/UBSan/TSan), threading. - Python — orchestration, clients, tooling, CLIs, simulators, parsers, glue: most of the rack-management surface. Type hints +
mypy, packaging (pyproject.toml), virtualenvs,argparse/click, structured logging, retries/backoff,subprocesssafety,asynciovs threads,pytest,ruff/black. - Shell — provisioning glue, CI steps, bootstrap, and "do this on 500 nodes" one-liners. Safe scripting (
set -euo pipefail), quoting, idempotency, trap/cleanup, when to stop using shell and switch to Python. - Node.js (the "plus") — tooling and lightweight services (a telemetry/status web service, a dashboard backend). When JS/TS is the right call and when it isn't.
Production engineering (language-agnostic)
- Error handling philosophy: fail fast vs degrade gracefully; the error taxonomy (transient vs permanent vs programmer error)
- Retries, exponential backoff, jitter, timeouts, circuit breakers (deeper in Phase 10)
- Resource lifetime: RAII in C++, context managers in Python,
defer-style cleanup; never leak fds/handles/locks - Concurrency: threads vs async vs processes; the GIL; data races; lock discipline
- Daemon mechanics: signals, graceful shutdown/drain, PID/lifecycle,
systemdunits,journald, readiness/liveness - Structured logging (key/value, correlation IDs), log levels, what to log and what not to (no secrets)
- Configuration: env vs file vs flags; precedence; validation at startup
- Security: input validation, shell-injection avoidance, least privilege, secrets handling, integer/buffer safety in C++, dependency/supply-chain hygiene
- Testability: dependency injection, mocking the hardware boundary, deterministic time, the seam that lets you test without a BMC
- Coding standards & style: clang-format/clang-tidy, ruff/black, code review norms (Phase 12)
Labs
Lab 01 — C++ Sensor-Polling Daemon
| Field | Value |
|---|---|
| Goal | Build a production-grade C++17 daemon that polls a (mocked) sensor source on an interval, exposes current readings, handles failures and signals, and is unit-testable without real hardware. |
| Concepts | RAII, smart pointers, the rule of zero, dependency injection at the hardware boundary, retries/backoff, graceful shutdown on SIGTERM/SIGINT, thread-safe state, structured logging, CMake/Make builds, sanitizers. |
| Steps | 1) make && ./sensord --once. 2) Read sensord.cpp: find the SensorSource interface (the test seam), the RAII guard, the signal handler, the poll loop. 3) make test runs the unit tests against a fake source. 4) Run under ASan. 5) Extensions below. |
| Stack | C++17, make (or CMake), no external deps; optional -fsanitize=address,undefined |
| Output | A daemon that polls, degrades on sensor failure (stale + retry), shuts down cleanly, and passes unit tests against a fake sensor. |
| How to Test | make test — fake SensorSource injects failures/garbage; asserts verify retry, staleness marking, and clean shutdown. |
| Talking Points | Why RAII beats try/finally; why the hardware is behind an interface; what happens on SIGTERM mid-poll; why you never system() a string. |
| Resume Bullet | "Built a C++17 sensor-polling daemon with RAII resource management, injectable hardware boundary for hermetic unit tests, retry/backoff, and graceful SIGTERM shutdown; clean under ASan/UBSan." |
| Extensions | Add a std::expected-style error type; expose readings over a Unix socket; add a systemd unit with Type=notify readiness; add a TSan run to prove the lock discipline. |
Lab 02 — Production Python Rack CLI
| Field | Value |
|---|---|
| Goal | Build a real CLI tool (rackctl) — typed, packaged, with structured logging, retries/backoff, safe subprocess use, and config precedence — the kind of tool every rack engineer lives in. |
| Concepts | argparse subcommands, type hints + mypy, pyproject.toml packaging, structured JSON logging, retry decorator with backoff+jitter, subprocess without shell=True, config precedence (flags > env > file > default), exit-code discipline, pytest. |
| Steps | 1) python3 -m rackctl status --rack rack-ai-01. 2) Read rackctl/ — the command dispatch, the retry decorator, the logging setup, the subprocess wrapper. 3) python3 -m pytest. 4) Break a command and read the structured error. 5) Extensions below. |
| Stack | Python 3 (stdlib only — deliberately, to show the patterns without magic) |
| Output | A packaged CLI with subcommands, JSON logs, retried operations, and tests. |
| How to Test | python3 -m pytest — tests cover retry/backoff, config precedence, subprocess argument safety, and exit codes. |
| Talking Points | Why shell=True is a vulnerability; flags vs env vs file precedence; why structured logs beat printf; how you make time deterministic for tests. |
| Resume Bullet | "Built a typed, packaged Python rack-operations CLI with structured logging, retry/backoff, safe subprocess execution, and config precedence; fully unit-tested with mocked hardware boundaries." |
| Extensions | Add --output json for machine consumption; add an asyncio fan-out that polls 100 mock nodes concurrently with bounded concurrency; add a --dry-run to every mutating command. |
Deliverables Checklist
- C++ daemon builds, runs, shuts down cleanly on SIGTERM, and passes tests against a fake sensor
-
C++ daemon is clean under
-fsanitize=address,undefined - You can explain RAII and show where a leak would occur without it
- Python CLI runs, logs structured JSON, retries transient failures, and has correct exit codes
-
Python CLI never uses
shell=True; you can explain the injection risk - You can state the config precedence order and why
- Both labs have a clear test seam at the hardware boundary (you can test without hardware)
-
You can write a safe Bash script with
set -euo pipefailand proper quoting
Interview Relevance
- "Write a daemon that polls a sensor every second and exposes the latest value. How do you handle a sensor that hangs?"
- "What is RAII and why does it matter here? Show me a resource leak and how RAII prevents it."
- "Why is
subprocess.run(cmd, shell=True)dangerous? Rewrite it safely." - "How do you make this code testable when the real dependency is a BMC you don't have in CI?"
- "Your daemon gets a SIGTERM in the middle of a poll. Walk me through what happens."
- "Exceptions vs error codes vs
std::expected— when do you reach for each?" - "How do you handle retries without hammering a struggling BMC?" (backoff + jitter + circuit breaker)
Warmup Guide — Production Systems Programming
Zero-to-expert primer for Phase 02. You can already write code; this guide makes it production infrastructure code — robust under failure, secure, testable without hardware, and maintainable by a team you've never met. Every later lab is held to this bar.
Table of Contents
- Chapter 1: The Language Matrix — Why Three (Plus One)
- Chapter 2: Error Handling as a Design Decision
- Chapter 3: RAII and Resource Lifetime (the C++ core idea)
- Chapter 4: Modern C++ for Infra Code
- Chapter 5: Production Python
- Chapter 6: Concurrency — Threads, Async, Processes
- Chapter 7: Daemons, Signals, and systemd
- Chapter 8: Security in Infra Code
- Chapter 9: Testability — The Hardware Boundary Seam
- Chapter 10: Safe Shell and When to Stop Using It
- Lab Walkthrough Guidance
- Success Criteria
- Interview Q&A
- References
Chapter 1: The Language Matrix — Why Three (Plus One)
The JD lists Python, C++, and shell as required and Node.js as a plus. This is not a wish list; it reflects the layers of a rack-management stack and where each tool earns its place.
- C++ lives at the bottom, close to hardware: device libraries, binary protocol parsers, performance-critical daemons, anything linking a vendor C SDK. You choose C++ when you need deterministic resource control, low overhead, or to call into a hardware library. The cost is that you own memory and lifetime; the discipline (RAII, Chapter 3) is what makes that safe.
- Python is the broad middle: clients (Redfish/IPMI/SNMP, Phase 03), exporters (Phase 07), operators (Phase 06), CLIs, simulators, parsers (Phase 08), and orchestration glue. You choose Python for velocity, libraries, and readability — and accept that it's slower and needs typing discipline (
mypy) to stay maintainable at scale. - Shell is the glue and bootstrap: PXE/kickstart hooks (Phase 05), CI steps (Phase 11), and "run this on every node." Powerful for orchestrating other programs, dangerous for logic. The skill is knowing the line where shell stops and Python begins (Chapter 10).
- Node.js is optional tooling/services: a status web UI, a small telemetry API, a dashboard backend. TypeScript brings types; the event loop suits I/O-bound services. You reach for it when the team/ecosystem favors JS or you're serving a web surface.
The Staff-level framing: choosing the language is an engineering decision with consequences for the team, not a preference. "We wrote the hot path in C++ behind a stable C ABI and the control logic in typed Python, with shell only for bootstrap" is the kind of layered answer that signals seniority.
Misconception: "use one language for everything." A rack stack that's all-Python can't meet a hard latency/resource budget at the device layer; an all-C++ stack moves at a fraction of the velocity for orchestration code. Polyglot-by-layer is the norm, and the seams between languages (a stable ABI, a JSON contract) are where you spend design care.
Chapter 2: Error Handling as a Design Decision
Production infra code is mostly error handling. The naive happy path is 20% of the work; the other 80% is what happens when the BMC times out, the sensor returns 0xFFFF, the disk is full, or the config is malformed.
The error taxonomy (decide the response per class):
- Transient (network blip, BMC busy, lock contention): retry with backoff (Chapter 6, Phase 10). Do not crash.
- Permanent (auth failure, 404, malformed firmware image): fail fast and loud; retrying just wastes time and hammers the target.
- Programmer error (precondition violated, null where impossible): assert/abort in dev; in prod, fail the operation, never silently continue with corrupt state.
- Degraded (one sensor of many failed): continue with reduced function, mark the bad part stale, surface a warning. A rack manager that dies because one of 40 sensors is flaky is useless.
Fail-fast vs degrade-gracefully is contextual: a startup misconfiguration should fail fast (don't run misconfigured); a runtime partial failure should usually degrade (keep managing the healthy 95%). The art is knowing which is which.
Mechanism by language:
- C++: exceptions for truly exceptional/unwindable errors; error-returning types (
std::optional,std::expectedin C++23, or aResult-like type) for expected failures like "sensor read failed." Don't use exceptions for control flow; don't ignore return codes. - Python: exceptions are idiomatic; catch specific exceptions, never bare
except:; wrap at boundaries; usetry/finallyor context managers for cleanup.
The cardinal sin: swallowing errors (except: pass, ignoring a return code). Every swallowed error is a future 3 a.m. mystery. Log it with context or propagate it; never drop it.
Chapter 3: RAII and Resource Lifetime (the C++ core idea)
RAII = Resource Acquisition Is Initialization. The single most important idea in C++ infra code, and a frequent interview question. The principle: tie a resource's lifetime to an object's scope. Acquire in the constructor, release in the destructor. When the object goes out of scope — for any reason, including an exception — the destructor runs and the resource is freed. No finally, no manual cleanup, no leak path.
Resources this governs: heap memory, file descriptors, sockets, mutex locks, device handles, a BMC session, a firmware-update transaction.
// Without RAII: a leak waits at every early return / throw.
int fd = open(path, O_RDONLY);
if (parse(fd) < 0) return -1; // <-- fd leaked
close(fd);
// With RAII: the guard's destructor closes fd on ANY exit path.
class FdGuard {
int fd_;
public:
explicit FdGuard(int fd) : fd_(fd) {}
~FdGuard() { if (fd_ >= 0) ::close(fd_); }
int get() const { return fd_; }
FdGuard(const FdGuard&) = delete; // non-copyable: one owner
FdGuard& operator=(const FdGuard&) = delete;
};
FdGuard f(open(path, O_RDONLY));
if (parse(f.get()) < 0) return -1; // fd still closed — destructor runs
Smart pointers are RAII for memory: std::unique_ptr (single owner, zero overhead — your default), std::shared_ptr (reference-counted shared ownership — use sparingly; cycles leak), std::weak_ptr (breaks cycles). The rule: never new/delete raw in modern C++. Use std::make_unique/make_shared.
std::lock_guard/std::scoped_lock are RAII for mutexes — the lock releases when the guard leaves scope, even on exception. This is why your daemon's shared state (Lab 01) is protected with a guard, not manual lock/unlock (which leaks the lock on an early return → deadlock).
The rule of zero/three/five: if your class owns no raw resources (it holds unique_ptrs, strings, vectors — all RAII themselves), write no destructor/copy/move (rule of zero) — the compiler-generated ones are correct. If you do manage a raw resource directly, you must handle destructor + copy + move + their assignments (rule of five), usually by deleting copy and defining move. Aim for the rule of zero: let RAII members do the work.
Chapter 4: Modern C++ for Infra Code
Beyond RAII, the modern-C++ toolkit that keeps systems code safe and readable:
const-correctness: mark everything you don't mutateconst. It documents intent, enables optimization, and the compiler catches accidental mutation. Aconst-correct sensor reader can't be accidentally made to mutate the device.- References over pointers where null is not a valid state; pointers (or
optional) only where absence is meaningful. std::optional<T>for "maybe a value" (a sensor that may not have a reading) and an error type (expected/aResult) for "value or why it failed."std::string_view/std::spanfor non-owning views — pass without copying, but never outlive the owner.- Value semantics & move: prefer values + move over manual pointer juggling;
std::movetransfers ownership cheaply. enum classfor type-safe states (power state, health) — no implicit int conversions.- Build & quality gates: CMake (or a clean Makefile),
-Wall -Wextra -Werror,clang-tidy(catches bugs),clang-format(style — never argue about it in review), and the sanitizers:- ASan (AddressSanitizer): use-after-free, buffer overflow, leaks.
- UBSan: undefined behavior (signed overflow, bad shifts, null deref).
- TSan: data races. Run your daemon's tests under TSan to prove your locking is correct. Sanitizers in CI catch the bugs that otherwise become irreproducible production crashes — exactly the JD's "debugging complex issues."
Misconception: "modern C++ is just C with classes." Modern C++ (17/20) with RAII, smart pointers, optional/expected, and sanitizers is a memory-safe-by-discipline language. The bugs that haunt infra code (UAF, leaks, races) are preventable with these tools, and a Staff engineer enforces them as standards (Phase 12).
Chapter 5: Production Python
Python's velocity is a trap without discipline; here's the discipline that keeps a 50k-line rack toolchain maintainable.
- Type hints +
mypy: annotate function signatures and dataclasses; runmypy --strictin CI. Types are documentation that can't go stale and catch a whole class of bugs (passing astrwhere anintbelongs) before runtime. For infra code consumed by a distributed team, this is non-negotiable. - Packaging: a
pyproject.toml, a real package (not a pile of scripts), pinned dependencies, a virtualenv.pip install -e .for dev. Reproducible installs are the difference between "works on my machine" and a deployable tool. - Structured logging: not
print(). Useloggingwith a structured (JSON) formatter and key/value context (node id, request id). Logs are queried by machines (Phase 07); structure makes them searchable and correlatable. - Configuration precedence: a clear order — flags > environment > config file > built-in default — validated at startup. Surprises in config precedence cause outages; make it explicit and tested (Lab 02).
- Retries/backoff: a decorator that retries transient errors with exponential backoff + jitter and a cap; never an unbounded retry loop that DDoSes a struggling BMC.
subprocesssafety: pass a list of args, nevershell=Truewith an interpolated string (Chapter 8). Set timeouts. Check return codes.- Exit-code discipline: 0 = success, non-zero = specific failure classes. Tools are composed in scripts and CI; your exit code is an API.
- Quality gates:
ruff(lint),black(format),pytest(test),mypy(types) — all in CI.
The GIL, briefly: CPython's Global Interpreter Lock means threads don't give you CPU parallelism (only one runs Python bytecode at a time), but they do help with I/O-bound work (waiting on 100 BMCs). For CPU-bound work use processes; for high-concurrency I/O use asyncio. Knowing this prevents the classic "why didn't my threads speed up my CPU-bound loop" mistake (Chapter 6).
Chapter 6: Concurrency — Threads, Async, Processes
A rack manager is inherently concurrent: poll 100 nodes, each independently, while serving status queries and handling signals. Choose the model deliberately.
- Threads: shared memory, cheap context, but you own synchronization (locks, races). In C++ great for true parallelism; in Python (GIL) only useful for I/O-bound concurrency. Use a thread pool with bounded size — never spawn one thread per node for 10,000 nodes.
- Async (
asyncio/ coroutines): single-threaded cooperative concurrency, excellent for many concurrent I/O waits (the BMC poll fan-out) with low overhead and no locks for most state. The cost: "async all the way down" and no help for CPU-bound work. - Processes: isolation and true parallelism (bypass the GIL), at the cost of memory and IPC. Use for CPU-bound work or fault isolation.
The lock discipline (C++ Lab 01): shared state (latest readings) is guarded by a mutex; every access takes a lock_guard. Keep critical sections tiny (copy out under lock, process outside). Never hold a lock across I/O or a callback. Run under TSan to prove there are no races — data races are undefined behavior and the source of the worst, least-reproducible bugs.
Bounded concurrency: whatever the model, cap it (a semaphore, a pool size). Polling 10,000 BMCs at once will exhaust fds and overwhelm the management network. Bounded fan-out (e.g., 50 at a time) is both kinder to the hardware and more predictable.
Chapter 7: Daemons, Signals, and systemd
A daemon is a long-running process that must behave well for years. The mechanics:
- Signals:
SIGTERM(graceful stop — the onesystemctl stopsends),SIGINT(Ctrl-C),SIGHUP(reload config),SIGKILL(uncatchable — last resort). Install handlers that set an atomic "stop" flag; the main loop checks it and drains (finish the in-flight poll, flush logs, release resources via RAII) before exiting. Never do heavy work in a signal handler (async-signal-safety) — just flip a flag. - Graceful shutdown: on
SIGTERM, stop accepting new work, finish or cancel in-flight work cleanly, persist any needed state, exit 0. A daemon that corrupts a firmware-update transaction on shutdown is a disaster; design shutdown as carefully as startup. - systemd integration: a unit file with
Restart=on-failure, resource limits (MemoryMax,TasksMax),Type=notify(the daemon callssd_notify(READY=1)when actually ready — true readiness, not "the process started"), andWatchdogSec(the daemon pings; if it hangs, systemd restarts it — a free liveness probe). - journald: log to stdout/stderr with structure; systemd captures it into the journal, queryable with
journalctl -u sensord. No log-file rotation code to write. - Readiness vs liveness: ready = can serve requests (mirrors Kubernetes readiness, Phase 06); alive = not hung. Expose both; the watchdog handles liveness,
sd_notifyhandles readiness.
Why this matters: the JD's "operational support," "observability," and "deployed systems" all assume your software runs as a well-behaved daemon. A process that ignores SIGTERM, can't be restarted safely, or hangs without the watchdog noticing is an operational liability.
Chapter 8: Security in Infra Code
The JD says "secure" three times. Rack-management software runs with high privilege on the management network — a bug here is a fleet-wide risk. The essentials:
- Shell-injection (the classic): never build a shell command by string-interpolating untrusted input.
subprocess.run(f"ipmitool -H {host} ...", shell=True)letshost = "x; rm -rf /"run anything. Fix: pass an arg list andshell=False(the default) —["ipmitool", "-H", host, ...]— sohostcan never be parsed as shell syntax. Same idea in C++: neversystem()a constructed string; useposix_spawn/execwith an argv array. - Input validation: validate everything from the network/config/user at the boundary — lengths, ranges, types, allow-lists (not deny-lists). A Redfish response, a firmware image header, a config value: all untrusted until validated.
- Memory safety (C++): bounds-check, avoid
strcpy/sprintf(usesnprintf/std::string), beware integer overflow in size calculations (a classic heap-overflow precursor), and run ASan/UBSan in CI. Most CVEs in C/C++ infra code are buffer/integer bugs. - Least privilege: run the daemon as a dedicated non-root user; grant only the capabilities it needs (
CAP_NET_ADMIN, not full root); scope its filesystem access (systemdProtectSystem,ReadOnlyPaths). - Secrets: never in code, logs, or argv (argv is world-readable via
/proc). Read from a file with tight perms, an env var injected by the orchestrator, or a secrets manager (Phase 09). Redact secrets in logs. - Supply chain: pin dependencies, verify checksums/signatures, minimize the dependency tree (every dep is attack surface), scan with
pip-audit/cargo audit/SCA in CI.
The mindset: assume every input is hostile and every privilege you hold can be abused. Phase 09 goes deep on the system-level security (secure boot, tenancy); this chapter is the code-level security the JD demands in every commit.
Chapter 9: Testability — The Hardware Boundary Seam
You cannot put a BMC, PDU, or CDU in CI. So the central testability technique for rack software is the seam at the hardware boundary: define an interface for the hardware interaction and program against it, so tests inject a fake.
struct SensorSource { // the seam
virtual std::optional<double> read_temp(int id) = 0;
virtual ~SensorSource() = default;
};
class RealBmcSource : public SensorSource { /* talks to the BMC */ };
class FakeSensorSource : public SensorSource { /* returns scripted values, failures */ };
The daemon takes a SensorSource& (dependency injection). Production wires the real one; tests wire the fake to simulate a hang, garbage data, intermittent failure — deterministically. This is exactly how Lab 01 tests retry and staleness without a BMC, and it scales to every phase: a fake Redfish service (Phase 03/11), a fake firmware target (Phase 08), a fake CDU (Phase 04).
Determinism: also inject time (a clock interface) so you can test "mark stale after 5s" without sleeping 5s. Real-clock tests are flaky; injected-clock tests are instant and reliable.
The test pyramid (deeper in Phase 11): many fast unit tests against fakes, fewer integration tests against emulators (Redfish mock, ipmi_sim), a thin top of hardware-in-the-loop tests on real gear. The seam is what makes the wide base possible.
Misconception: "infra code can't be unit-tested because it needs hardware." Wrong — it can't be unit-tested if you didn't design a seam. The seam is a design choice you make up front, and it's a hallmark of senior infra code.
Chapter 10: Safe Shell and When to Stop Using It
Shell is unavoidable (bootstrap, CI, provisioning hooks) and dangerous. The safety basics:
set -euo pipefailat the top of every script:-eexit on error,-uerror on unset variable,-o pipefaila pipeline fails if any stage fails. This turns silent failures into loud ones.- Quote everything:
"$var","${array[@]}". Unquoted variables word-split and glob — the source of countless "it worked until a path had a space" bugs and injection vectors. trap cleanup EXITfor RAII-like cleanup (remove temp files, release locks) on any exit.- Idempotency: a provisioning script may run twice; design it to converge (check-then-act,
mkdir -p, "create if not exists") rather than assume a clean slate. shellcheckin CI — it catches the quoting and portability bugs you'll miss.
When to stop: the moment you need data structures, real error handling, arithmetic, JSON, or testing — switch to Python. A rule of thumb: >50 lines, any non-trivial control flow, or any logic you'd want a unit test for → it's a Python program, not a shell script. Staff engineers are known for deleting sprawling shell and replacing it with tested Python; that's a maturity signal, not a rewrite-for-its-own-sake.
Lab Walkthrough Guidance
Order: Lab 01 (C++ daemon) → Lab 02 (Python CLI). The daemon teaches resource safety and the hardware seam at the hardest layer; the CLI applies the same robustness ideas where you'll write most code.
Lab 01 (C++ sensor daemon):
make && ./sensord --once— one poll, then exits. Read the output.- Find the three load-bearing pieces in
sensord.cpp: theSensorSourceinterface (the seam, Ch. 9), the RAII guard /lock_guard(Ch. 3), and the signal handler that flips the stop flag (Ch. 7). make test— the fake source injects a hang and garbage; tests assert retry/backoff and staleness marking.make asan && ./sensord_asan --once— confirm clean under AddressSanitizer.- Extensions: a
Result/expectederror type; a Unix-socket status endpoint; asystemdunit withType=notify.
Lab 02 (Python rack CLI):
python3 -m rackctl status --rack rack-ai-01— read the JSON-structured logs.- Trace a command from dispatch → retry decorator → subprocess wrapper → exit code.
python3 -m pytest— see the retry, config-precedence, and subprocess-safety tests.- Try to make it run an injected shell command through a node name; confirm it can't (arg list, no
shell=True). - Extensions:
--output json, anasynciobounded fan-out over 100 mock nodes, a universal--dry-run.
The phase capstone question: "Take the C++ daemon and the Python CLI and explain, for each, the single change that most improves its production-readiness." (Good answers: the daemon — Type=notify + watchdog so the orchestrator knows true readiness/liveness; the CLI — bounded-concurrency async fan-out + structured-log correlation IDs so it scales and is debuggable.)
Success Criteria
You're done with this phase when — without notes:
- You can explain and demonstrate RAII, and identify the leak/deadlock it prevents (Ch. 3)
- You can state the rule of zero/five and when each applies (Ch. 3)
- You can classify an error as transient/permanent/programmer/degraded and pick the response (Ch. 2)
- You can pick threads vs async vs processes for a given workload and justify it, including the GIL (Ch. 6)
-
You can write a graceful-shutdown daemon and a
systemdunit withType=notify+ watchdog (Ch. 7) - You can spot and fix a shell-injection and explain least privilege/secrets handling (Ch. 8)
- You can design a hardware-boundary seam and test failure paths without hardware (Ch. 9)
- Both labs build, pass tests, and (C++) run clean under ASan/UBSan
- You can write a safe Bash script and say when to switch to Python (Ch. 10)
Interview Q&A
Q1: What is RAII, and why does it matter for a rack daemon?
A: RAII ties a resource's lifetime to an object's scope — acquire in the constructor, release in the destructor — so the resource is freed on every exit path, including exceptions, with no manual cleanup. For a daemon it's the difference between leaking file descriptors / BMC sessions / mutex locks on error paths and being correct by construction. Concretely: a lock_guard releases the mutex even if the critical section throws (no deadlock); a unique_ptr/fd-guard closes the handle on early return. The rule is "no raw new/delete, no manual lock/unlock" — let destructors do the work. It also gives the rule of zero: a class holding only RAII members needs no destructor or copy/move at all.
Q2: subprocess.run(f"ipmitool -H {host} power status", shell=True) — what's wrong and how do you fix it?
A: It's a shell-injection vulnerability. With shell=True the string is parsed by /bin/sh, so host = "h; rm -rf /" runs arbitrary commands with the daemon's privileges — which on the management network are high. The fix is to pass an argument list with shell=False (the default): subprocess.run(["ipmitool", "-H", host, "power", "status"], timeout=10, check=True). Now host is a single argv element that can never be reinterpreted as shell syntax. I'd also validate host against an allow-list/format, set a timeout, and check the return code. Same principle in C++: never system() a built string; use exec/posix_spawn with an argv array.
Q3: How do you unit-test code whose real dependency is a BMC you don't have in CI?
A: Put a seam at the hardware boundary — define an interface (e.g., SensorSource with read_temp) and program against it via dependency injection. Production injects the real BMC client; tests inject a fake that returns scripted values, simulates a hang, garbage data, or intermittent failure — deterministically. I also inject the clock so I can test "mark stale after 5s" instantly without sleeping. That gives a wide base of fast, reliable unit tests; above it, a few integration tests run against an emulator (a Redfish mock or ipmi_sim), and a thin top runs on real hardware. The key insight is that testability is a design decision — the seam — made up front, not something you bolt on.
Q4: Your daemon receives SIGTERM mid-poll. Walk me through correct behavior.
A: The SIGTERM handler does the minimum async-signal-safe work: set an atomic stop flag (and maybe write to a self-pipe/eventfd to wake the loop). It does not do logging or cleanup inside the handler. The main loop notices the flag, stops accepting new work, lets the in-flight poll finish (or cancels it cleanly if it's safe), flushes logs, and returns — at which point RAII guards release fds, sessions, and locks, and the process exits 0. If there's a transactional operation in progress (a firmware update), shutdown must either complete it or leave it in a safe, resumable state — never a corrupt half-state. With systemd I'd also use Type=notify so readiness is real and a WatchdogSec so a hang during shutdown gets escalated to SIGKILL.
Q5: Threads, async, or processes — how do you choose, and where does the GIL bite?
A: Match the model to the workload. I/O-bound concurrency (poll 100 BMCs that mostly wait) → async/coroutines (low overhead, no locks) or a bounded thread pool. CPU-bound work → processes for true parallelism. Fault isolation → processes. The GIL means CPython threads don't run bytecode in parallel, so threads won't speed up CPU-bound Python — a common mistake; they do help I/O-bound work because the GIL is released during I/O. In C++ threads give real parallelism but I own synchronization: tiny critical sections under lock_guard, never hold a lock across I/O, and prove it with TSan. Whatever the model, I bound concurrency — a semaphore or pool size — so I don't exhaust fds or flood the management network polling thousands of nodes at once.
Q6: What does "secure code" mean concretely for this role?
A: At the code level: no shell injection (arg lists, never shell=True/system()), validate all external input at the boundary with allow-lists, memory safety in C++ (no strcpy/sprintf, watch integer overflow in size math, ASan/UBSan in CI), least privilege (dedicated non-root user, scoped capabilities, systemd sandboxing), secrets never in code/logs/argv (argv leaks via /proc), and supply-chain hygiene (pinned, checksum-verified, minimal, scanned dependencies). It matters more here than in most software because rack-management code runs with high privilege on the management network, so a single buffer overflow or injected command is a fleet-wide compromise. Security is a per-commit discipline enforced by standards and CI gates, not an afterthought.
Q7 (Staff): Your team's rack tooling is a 4,000-line pile of Bash that's become unmaintainable. How do you approach it?
A: First, stabilize and understand, not rewrite blindly: add set -euo pipefail and shellcheck to stop the bleeding, and capture what it actually does (its behavior is the spec). Then carve along seams — identify the parts doing real logic (data structures, retries, parsing, anything you'd want tested) and migrate those to a typed, tested Python package incrementally, leaving shell only for thin bootstrap/glue. I'd add tests as I go so each migrated piece is pinned, ship behind a flag, and run old and new in parallel to validate. The Staff value is doing this incrementally and safely with the team — not a big-bang rewrite that stalls feature work — and codifying the new standard (lint/format/type/test gates) so it doesn't regress. The deliverable is a maintainable tool and a raised engineering bar.
References
- Scott Meyers, Effective Modern C++ — the canonical modern-C++ practices (RAII, smart pointers, move,
const) - ISO C++ Core Guidelines — https://isocpp.github.io/CppCoreGuidelines/ (esp. the Resource and Concurrency sections)
- CMake & sanitizers — https://clang.llvm.org/docs/AddressSanitizer.html , UBSan, ThreadSanitizer docs
- Brett Slatkin, Effective Python — production Python idioms (typing, concurrency, packaging)
- Python
subprocesssecurity — https://docs.python.org/3/library/subprocess.html#security-considerations - systemd.service & sd_notify — https://www.freedesktop.org/software/systemd/man/systemd.service.html
- Google Shell Style Guide &
shellcheck— https://google.github.io/styleguide/shellguide.html , https://www.shellcheck.net/ - OWASP — Command Injection & Secrets Management cheat sheets — https://cheatsheetseries.owasp.org/
- Cross-track: Security Engineer — AppSec/SDLC phase for deeper secure-coding practice
🛸 Hitchhiker's Guide — Phase 02: Production Systems Programming
Read this if: you write working code but aren't sure what separates a script from infrastructure software. This is the compressed field guide — the patterns, the footguns, and the one-liners that show up in code review and on the pager.
0. The 30-second mental model
Production infra code is mostly the failure paths. The happy path is the easy 20%. What makes you senior is the other 80%: what happens when the BMC hangs, the sensor lies, the disk fills, the config is wrong, SIGTERM arrives mid-write, and the input is hostile. The four habits that get you there: RAII/cleanup-on-every-path, a seam at the hardware boundary so you can test without hardware, structured logs + exit codes so on-call can reason about it, and never trust input. If you remember one sentence: write the code you'd want to debug at 3 a.m. on someone else's machine.
1. The language decision, in one table
| Layer | Language | Why | The footgun |
|---|---|---|---|
| device/hot path | C++ | resource control, vendor C SDKs, latency | memory/lifetime — tamed by RAII |
| control/clients/tools | Python | velocity, libraries, readability | no types → rot; GIL surprises |
| bootstrap/glue/CI | shell | orchestrating other programs | quoting/injection; logic in shell |
| web/status service | Node.js/TS | I/O-bound services, JS ecosystem | callback/async sprawl |
Staff move: choose by layer and design the seams between them (a stable C ABI, a JSON contract). "All one language" is a junior answer.
2. RAII in four lines
- Resource lifetime = object scope. Destructor frees it on every exit (return, throw, break).
unique_ptris your default owner;shared_ptronly for genuine shared ownership (watch cycles →weak_ptr).lock_guard/scoped_lockfor mutexes — never manual lock/unlock (early return leaks the lock → deadlock).- Rule of zero: hold RAII members, write no destructor/copy/move. If you manage a raw resource, rule of five (usually: delete copy, define move).
3. The error taxonomy → response map
| Class | Example | Response |
|---|---|---|
| transient | BMC busy, net blip | retry w/ backoff+jitter, capped |
| permanent | 401, bad image header | fail fast & loud; don't retry |
| programmer | precondition violated | assert/abort in dev; fail op in prod |
| degraded | 1 of 40 sensors dead | continue, mark stale, warn |
Cardinal sin: except: pass / ignoring a return code. Every swallowed error is a future mystery.
4. The hardware-boundary seam (the testability superpower)
[ daemon logic ] ──uses──▶ interface (SensorSource / RedfishClient / FirmwareTarget)
├── RealBmcSource (prod)
└── FakeSource (tests: hang, garbage, flaky)
Inject the dependency and the clock. Now you test retry, staleness, and timeouts in milliseconds with no hardware. This single pattern is reused in Phases 03, 04, 08, 11.
5. subprocess / shell injection — the 10-second fix
# BAD — shell parses the string; node="x; rm -rf /" runs
subprocess.run(f"ipmitool -H {node} power status", shell=True)
# GOOD — argv list, shell=False (default); node can't be reinterpreted
subprocess.run(["ipmitool", "-H", node, "power", "status"], timeout=10, check=True)
Same rule in C++: never system("..."); use exec/posix_spawn with an argv array.
6. The daemon checklist
- SIGTERM/SIGINT → set atomic stop flag (nothing heavy in the handler) → main loop drains.
- Graceful shutdown: stop new work, finish/cancel in-flight cleanly, never leave a transaction half-done.
systemd:Restart=on-failure,Type=notify(real readiness viasd_notify),WatchdogSec(free liveness),MemoryMax/TasksMax, sandboxing (ProtectSystem, non-rootUser=).- Log to stdout/stderr (journald captures it) — don't write your own log rotation.
7. Concurrency cheat sheet
- I/O-bound (poll 100 BMCs) → asyncio or bounded thread pool. Bound it (semaphore/pool) — don't open 10,000 sockets.
- CPU-bound → processes (the GIL blocks thread parallelism in CPython).
- C++ threads → real parallelism, tiny critical sections under
lock_guard, prove with TSan. - Never hold a lock across I/O or a callback.
8. The CI quality gates (per language)
| format | lint | types | test | safety | |
|---|---|---|---|---|---|
| C++ | clang-format | clang-tidy | compiler -Wall -Wextra -Werror | gtest/Catch2 | ASan/UBSan/TSan |
| Python | black | ruff | mypy --strict | pytest | pip-audit |
| shell | — | shellcheck | — | bats (optional) | set -euo pipefail |
If it's not in CI, it doesn't hold. Standards you can't enforce are wishes (Phase 12).
9. Security one-liners that matter here
- Validate at the boundary with allow-lists, not deny-lists.
- C++: no
strcpy/sprintf; watch integer overflow insize = a * bbeforemalloc. - Secrets: never in code, logs, or argv (argv is world-readable via
/proc/<pid>/cmdline). - Least privilege: dedicated user, scoped capabilities, read-only FS where possible.
- Pin + checksum + minimize + scan dependencies — every dep is attack surface.
10. The "you've done this before" tells
- "Where's the seam?" (testability) · "What happens on SIGTERM?" (lifecycle) · "Is that arg list or shell-interpolated?" (security) · "Is the critical section under a guard?" (RAII/locks) · "Transient or permanent?" (retry policy) · "Did we bound the concurrency?" (fan-out safety).
11. How this phase pays off later
- The seam + fake pattern powers every later lab's tests (esp. Phases 03, 08, 11).
- The daemon mechanics are how the Phase 07 exporter and Phase 13 capstone agent run.
- The retry/backoff and error taxonomy become the Phase 10 reliability patterns.
- The security discipline is the per-commit half of Phase 09's system security.
Lab 01 — C++ Sensor-Polling Daemon
Phase: 02 — Production Systems Programming | Difficulty: ⭐⭐⭐☆☆ | Time: 4–6 hours
Language: C++17 | Hardware: none (fake sensor source) | Needs: a C++17 compiler + make
Concept primer:
../WARMUP.mdCh. 3 (RAII), Ch. 7 (daemons/signals), Ch. 9 (the seam),../HITCHHIKERS-GUIDE.md§2, §4, §6.
Run
make # builds ./sensord (-Wall -Wextra -Werror-clean)
./sensord --once # one poll cycle against the fake source, then exits
./sensord --run # daemon loop; Ctrl-C (SIGINT) or SIGTERM to stop cleanly
make test # ./sensord --self-test — hermetic unit tests, no hardware
make asan # build + run tests under AddressSanitizer + UBSan
0. The mission
Write a daemon the way infrastructure software is actually written: it polls a sensor source on an interval, keeps the latest readings, survives a sensor that fails or lies, shuts down cleanly on SIGTERM, and is fully unit-tested without any real hardware. This is the C++ bar every close-to-hardware lab in the curriculum is held to.
Two questions you must answer at the end:
- Where is the seam that lets you test retry/staleness logic with no BMC?
- What exactly happens to in-flight work and held resources when SIGTERM arrives?
1. The load-bearing pieces (read these in sensord.cpp)
| Pattern (WARMUP) | Where | What to notice |
|---|---|---|
| Async-signal-safe handler (Ch. 7) | on_signal / g_stop | the handler only flips an atomic flag — no logging, no cleanup |
| RAII guard (Ch. 3) | SessionGuard | non-copyable, movable; destructor releases on every path; move nulls the source |
| RAII lock (Ch. 3, 6) | std::lock_guard lk(mu_) | tiny critical section; copy out under lock, process outside |
| The seam (Ch. 9) | SensorSource + FakeSensorSource | the daemon takes SensorSource&; tests inject scripted failures |
| Injected time/backoff (Ch. 9) | now_fn, backoff_fn | tests pass a fake clock + no-op backoff → instant, deterministic |
| Error taxonomy (Ch. 2) | poll_once | transient → retry; permanent → no reading; prior value → degrade + stale |
| Graceful shutdown (Ch. 7) | the --run loop | sleeps in 100ms slices checking g_stop so it drains fast |
2. What the tests prove (make test)
Five behaviors, all without hardware, all deterministic:
- Transient recovery — a sensor that fails twice then succeeds is retried exactly to success (asserts the call count).
- Permanent failure — no configured value → all retries exhausted → no reading (not a crash).
- Graceful degradation — a sensor that had a value then fails keeps its last value but is marked stale (a rack manager must not lose all state because one read failed).
- Staleness by clock — advancing the injected clock past the window flips
stalewith no realsleep. - RAII move — moving a
SessionGuarddoesn't double-release.
This is the whole point of Chapter 9: the seam + injected clock turn "needs a BMC and a stopwatch" into millisecond, repeatable unit tests.
3. Extension exercises (do them — this is the lab)
- A
Result/expectederror type: replacestd::optional<double>with a type that carries why a read failed (timeout vs auth vs garbage), and route the response by error class (retry timeouts, fail-fast on auth). This is the error taxonomy in code. - Expose readings over a Unix socket: add a tiny request/response endpoint so other
processes can query the latest readings — and write an integration test that connects
to it. (Mind the lock: don't hold
mu_across the socket I/O.) systemdintegration: write asensord.serviceunit withType=notify,Restart=on-failure,WatchdogSec=, a non-rootUser=, andProtectSystem=strict. Callsd_notify(0, "READY=1")after the first successful poll and ping the watchdog each loop. Now the orchestrator knows true readiness and a hang gets restarted.- Prove the locking with TSan: build with
-fsanitize=thread, spawn a reader thread callingget()while the poll loop runs, and confirm zero races. Then deliberately remove alock_guardand watch TSan catch the data race — the bug you'd otherwise chase in production for a week.
4. Common pitfalls
- Work in the signal handler — calling
std::cout/malloc/cleanup fromon_signalis undefined behavior (not async-signal-safe). Only the atomic flag is legal. - Holding the lock across I/O — if
get()did socket I/O undermu_, the poll loop would block on a slow client. Copy out under the lock, do I/O outside. - Real sleeps in tests — a test that actually waits 5s for staleness is slow and flaky. Inject the clock.
- Raw
new/delete— use values,unique_ptr, and RAII guards. The compiler- generated special members are correct when you hold only RAII members (rule of zero). - Unbounded retry — retrying forever on a permanent failure hammers the BMC. Cap retries and back off.
5. What this lab proves about you
You can write C++ that belongs in a production fleet: resource-safe by construction, testable without the hardware, well-behaved as a service, and clean under sanitizers. In an interview, "write a daemon that polls a sensor and handles a hang" is a senior screen; you answer with the seam, the atomic-flag shutdown, the lock discipline, and "here's how I'd test every failure path in milliseconds" — and you can show the ASan/TSan runs that prove it. That credibility is exactly what the JD's "high-quality, secure, maintainable code" and "debugging complex issues" lines are testing for.
Lab 02 — Production Python Rack CLI (rackctl)
Phase: 02 — Production Systems Programming | Difficulty: ⭐⭐⭐☆☆ | Time: 4–6 hours
Language: Python 3 (stdlib only) | Hardware: none (fake BMC client) | Needs: pytest, mypy (optional)
Concept primer:
../WARMUP.mdCh. 5 (production Python), Ch. 8 (security), Ch. 9 (the seam),../HITCHHIKERS-GUIDE.md§5, §9.
Run
python3 -m rackctl status --rack rack-ai-01 # node power states (text)
python3 -m rackctl status --rack rack-ai-01 --output json
python3 -m rackctl power cycle --node node-01 # JSON logs go to stderr
python3 -m rackctl power off --node node-00 --dry-run # mutating command, no effect
python3 -m pytest -q # 14 tests, no hardware
python3 -m mypy rackctl # strict types clean
0. The mission
Build the tool a rack engineer lives in — rackctl — to the production bar: typed,
packaged, structured-logged, retry-aware, subprocess-safe, with explicit config
precedence and disciplined exit codes. It's deliberately stdlib-only so you see the
patterns, not a framework's magic.
Two questions you must answer at the end:
- Why does the CLI take a
BmcClientit doesn't construct itself? - What's the config precedence order, and why must "flag not provided" differ from "flag provided as a falsy value"?
1. The package, file by file
| File | Pattern (WARMUP) | What to notice |
|---|---|---|
retry.py | retry + backoff + jitter (Ch. 2/6) | sleep/rand injected → deterministic tests; Transient vs Permanent decide retry-or-not |
logging_setup.py | structured JSON logs (Ch. 5) | merges extra= context; redacts secret-named fields |
config.py | precedence (Ch. 5) | flags > env > file > default; None == "not provided" |
runner.py | subprocess safety (Ch. 8) | refuses string commands; argv list only; timeout; --dry-run |
cli.py | the seam + exit codes (Ch. 9/5) | BmcClient protocol + FakeBmcClient; exit 0/2/3/4 |
tests/ | testability (Ch. 9) | every failure path tested without hardware |
2. The two ideas that make it production-grade
The seam (BmcClient) — main(argv, client=None) lets tests (and the capstone) inject
a fake fleet. FakeBmcClient can be told to make a node flaky (make_flaky) so the
retry path is exercised deterministically. Production would inject a real Redfish/IPMI
client (Phase 03). The CLI logic never changes.
Config precedence with SUPPRESS — argparse has a famous gotcha: with parents=,
a subparser's default clobbers a value the main parser already set. The fix here is
default=argparse.SUPPRESS on the shared flags, so an attribute exists only if the user
actually provided it — which is exactly the "flags override env override file" semantics
load_config needs (None means "not provided", so it doesn't override). This is the
kind of subtle correctness an interviewer probes with "what if I pass --retries after
the subcommand?"
3. What the run shows
status prints a table (or JSON with --output json). Every operation emits a structured
JSON log line to stderr (stdout stays clean for piping) — try
python3 -m rackctl power cycle --node node-01 and read the {"ts":...,"level":...} log.
--dry-run on a mutating command logs "would execute" and changes nothing. Unknown node
→ exit code 3 (permanent); a flaky node recovers via retries → exit 0.
4. Extension exercises (do them — this is the lab)
- Async bounded fan-out: add
status --allthat polls 100 mock nodes concurrently withasyncioand aSemaphore(50)cap (WARMUP Ch. 6). Measure it against a serial loop. The bound matters: 10,000 unbounded connections would exhaust fds and flood the management network. - Correlation IDs: generate a request id per invocation and attach it to every log
line via a logging filter, so a single
rackctlrun is greppable end-to-end (foreshadows Phase 07 tracing). - Universal
--dry-run+ confirmation: make every mutating command support--dry-runand require--yesfor destructive actions (power off in production). - A real subprocess path: add an
execsubcommand that runsipmitool/redfishtoolviarunner.run([...]), and write a test proving a malicious--node "x; reboot"cannot inject (it's one argv element). - Wire the CI gates: add
ruff,black --check,mypy --strict, andpytestto amake checktarget — the Phase 11 quality gates in miniature.
5. Common pitfalls
shell=True— the whole reasonrunner.runrefuses strings. Interpolating a node name into a shell string is remote code execution waiting to happen.- Bare
except:— catchTransientError/PermanentErrorspecifically; a bare catch hides programmer errors and turns bugs into silent wrong behavior. print()for logs — unstructured, unsearchable, and mixes with stdout data. Use the JSON logger to stderr.- Retrying permanent failures — an auth error or unknown node won't fix itself;
retrying just delays the error and hammers the BMC. The
retry_onallow-list prevents it. - Secrets in argv/logs — argv is world-readable via
/proc; the logger redacts secret-named fields, but never pass a password as a flag in the first place.
6. What this lab proves about you
You can write the Python that is a rack-management product's operator surface: typed,
testable, safe, and operable. In an interview, "write a CLI to power-cycle a node and
handle a flaky BMC" becomes a showcase — you reach for the seam (testable without
hardware), the retry-by-error-class (don't retry auth failures), the arg-list subprocess
(no injection), structured logs (debuggable), and exit codes (composable). The same
patterns scale to the Phase 03 protocol clients, the Phase 07 exporter, and the Phase 13
capstone — rackctl is the seed of all of them.
Phase 03 — Management Protocols: Redfish, IPMI, SNMP
Difficulty: ⭐⭐⭐☆☆ | Estimated Time: 2 weeks Roles supported: Rack Management SWE (Senior/Staff), Firmware/Platform SWE, DCIM Engineer Hardware needed: none — labs run a Redfish mock and protocol simulators in-process
Why This Phase Exists
This is the phase the JD is most explicit about. It names the protocols directly: "Interface with rack hardware (BMC, PDU, CDU, PCIe switches) using Redfish, SNMP, and IPMI protocols" and requires "Experience with Redfish APIs and DCIM tools." These three protocols are the language your software speaks to every managed device on the out-of-band network (Phase 01, Ch. 3/6). Without them, you cannot read a temperature, power-cycle a node, or learn that a PSU failed.
You must know all three, and — critically — when each applies and why the industry is migrating from IPMI/SNMP to Redfish. Redfish is the modern, structured, secure standard (RESTful, JSON, schema-defined, HTTPS); IPMI is the legacy binary out-of-band standard (deprecated by Intel in 2019 but everywhere in deployed fleets); SNMP is the venerable polling/trap protocol still ubiquitous on PDUs, switches, and older gear. A senior rack engineer writes a Redfish-first abstraction that also speaks IPMI and SNMP for the hardware that needs them.
By the end you will have written a real Redfish client that navigates the data model, performs actions, and subscribes to events against a live (mock) Redfish service, plus an IPMI sensor/SEL reader and an SNMP poller/trap receiver — the exact toolkit a rack manager is built from.
Concepts
Redfish (the modern standard — the one to know cold)
- The DMTF Redfish model: RESTful, JSON, HTTPS, schema-defined, HATEOAS (hypermedia/links)
- The resource tree:
ServiceRoot(/redfish/v1) →Systems,Chassis,Managers,UpdateService,EventService,SessionService,TaskService ComputerSystem(the host),Chassis(the physical box +Power/Thermalsub-resources),Manager(the BMC itself)- Actions (
@Redfish.Actions):ComputerSystem.Reset, power control, etc. (POST) - Collections,
@odataannotations,Members, navigation links,$expand - Authentication: HTTP Basic vs Session (
X-Auth-Token) vs the SessionService - Events: subscriptions (push,
EventDestination) vs SSE (Server-Sent Events) vs polling - Tasks: long-running operations (
TaskService,Taskmonitors) — firmware updates return a Task - Telemetry model:
MetricReport,MetricDefinition(the structured-telemetry future) - Redfish for PDUs/CDUs: the DMTF Power/Thermal equipment schemas;
OutletGroup,Outlet,CoolingLoop
IPMI (the legacy out-of-band standard — still everywhere)
- The architecture: BMC, IPMB,
ipmitool, IPMI-over-LAN (RMCP+), the LAN session - Sensors and the SDR (Sensor Data Repository); sensor types, thresholds, readings
- The SEL (System Event Log); decoding event records
- Chassis control (power on/off/cycle), boot device selection, SoL (Serial-over-LAN)
- Why IPMI is deprecated (security history, cleartext, complexity) and why it persists
SNMP (polling + traps — PDUs, switches, environmental gear)
- The model: manager/agent, MIB, OID tree,
GET/GETNEXT/GETBULK/WALK, traps/informs - MIBs and OIDs: the dotted-number namespace, enterprise OIDs, standard MIBs (ENTITY, ENTITY-SENSOR, host-resources)
- SNMP versions: v1/v2c (community strings, cleartext) vs v3 (auth/priv — the secure one)
- Traps vs polling; the trap receiver; why you need both
- Mapping SNMP/IPMI/Redfish into one unified model (foreshadows the Phase 07 exporter)
Cross-cutting (the senior framing)
- Redfish-first with IPMI/SNMP fallback; the driver-abstraction pattern (a
Transport/Driverseam) - Protocol security: HTTPS/cert validation, SNMPv3, never IPMI/SNMPv2 on an untrusted net
- Idempotency, retries, and rate-limiting against fragile BMCs (Phase 02 patterns applied)
- Vendor variance: OEM extensions, schema gaps, the "every BMC is subtly different" reality
Labs
Lab 01 — Redfish Client Against a Live Mock
| Field | Value |
|---|---|
| Goal | Build a Redfish client that navigates the resource tree, reads Power/Thermal, performs a ComputerSystem.Reset action, and creates an event subscription — against a real in-process Redfish HTTP mock. |
| Concepts | ServiceRoot navigation, collections/Members, @odata links, Power/Thermal sub-resources, Actions (POST), Session auth, EventService subscriptions, Tasks. |
| Steps | 1) python3 solution.py — it starts a Redfish mock and walks it. 2) Read the client: how it follows links from /redfish/v1 to a System's Power/Thermal. 3) See the Reset action change power state. 4) Extensions below. |
| Stack | Python 3 stdlib (http.server, urllib) — no network, no deps |
| Output | A working Redfish client + a printed walk of Systems/Chassis/Power/Thermal and a successful Reset. |
| How to Test | Built-in asserts verify navigation, the action's effect on power state, and subscription creation. |
| Talking Points | Why Redfish beats IPMI; HATEOAS/link-following vs hardcoded URLs; Session vs Basic auth; why firmware updates return a Task. |
| Resume Bullet | "Built a Redfish client that navigates the DMTF resource model (Systems/Chassis/Power/Thermal), executes Reset actions, and manages EventService subscriptions; tested against an in-process Redfish mock." |
| Extensions | Add $expand handling; poll a Task to completion; add Session-token auth + cert validation; run it against the real DMTF Redfish-Mockup-Server or OpenBMC sushy-emulator. |
Lab 02 — SNMP Poller/Trap Receiver + IPMI Sensors
| Field | Value |
|---|---|
| Goal | Model and parse the two legacy protocols: walk an SNMP OID tree and decode sensor values, receive/decode a trap, and read IPMI SDR sensors + SEL events — then map all three into one unified reading. |
| Concepts | OID tree & WALK, MIB semantics, SNMP versions, traps vs polling, IPMI SDR/sensor thresholds, SEL decoding, unifying heterogeneous telemetry. |
| Steps | 1) python3 solution.py. 2) Read the SNMP OID-tree walk and the IPMI SDR/SEL decode. 3) See all three protocols normalized into one Reading model. 4) Extensions below. |
| Stack | Python 3 stdlib (simulated agents; the README shows real snmpwalk/ipmitool equivalents) |
| Output | A unified telemetry view derived from simulated SNMP + IPMI sources, with threshold/health classification. |
| How to Test | Asserts verify OID-tree WALK ordering, threshold-crossing detection, SEL decode, and the unified mapping. |
| Talking Points | SNMPv2c vs v3 security; GETNEXT/WALK lexicographic ordering; IPMI sensor thresholds (lnc/lc/lnr); why you normalize to one model; trap vs poll tradeoffs. |
| Resume Bullet | "Built an SNMP poller/trap decoder and IPMI SDR/SEL reader, normalizing heterogeneous device telemetry into a unified model with threshold-based health classification." |
| Extensions | Point the SNMP code at real snmpsim; wrap real ipmitool sdr/sel output parsing; add SNMPv3 USM concepts; emit the unified readings in Prometheus format (foreshadows Phase 07). |
Deliverables Checklist
- Redfish client navigates ServiceRoot → Systems → Chassis → Power/Thermal by following links
-
Client performs a
ComputerSystem.Resetaction and observes the state change - Client creates an EventService subscription (and you can explain push vs SSE vs poll)
- You can explain why a firmware update returns a Task and how you'd monitor it
- You can decode an IPMI sensor threshold reading and a SEL event
- You can perform an SNMP WALK conceptually and explain GETNEXT lexicographic ordering
- You can state SNMPv2c vs v3 and IPMI security caveats and the Redfish-first rationale
- You normalized all three protocols into one unified reading model
Interview Relevance
- "What is Redfish and why is the industry moving to it from IPMI?"
- "Walk me through how you'd discover a server's power state and CPU temperature with Redfish."
- "How does Redfish authentication work? Basic vs Session tokens?"
- "A firmware update is a long operation — how does Redfish model that?" (Tasks)
- "How do you get an event when a PSU fails — poll or push?" (EventService/SSE vs polling)
- "What's an OID? Walk me through an SNMP GETNEXT/WALK."
- "Your fleet has new Redfish servers and old IPMI/SNMP gear. How do you build one tool?" (driver abstraction)
- "Why should you never run SNMPv2c or IPMI on the tenant network?"
Warmup Guide — Management Protocols: Redfish, IPMI, SNMP
Zero-to-expert primer for Phase 03. You need no prior protocol knowledge: this guide starts at "what is a BMC API" and ends with you navigating a Redfish tree, decoding an IPMI sensor, and walking an SNMP MIB — and knowing exactly when to use which. These protocols are how all of your software touches hardware (Phase 01, Ch. 3).
Table of Contents
- Chapter 1: The Three Protocols and Why There Are Three
- Chapter 2: Redfish — The Data Model
- Chapter 3: Redfish — Navigation, Actions, Auth
- Chapter 4: Redfish — Events and Tasks
- Chapter 5: IPMI — The Legacy You Still Must Speak
- Chapter 6: SNMP — Polling, MIBs, and Traps
- Chapter 7: Security Across the Three
- Chapter 8: The Driver Abstraction — Redfish-First With Fallback
- Chapter 9: Vendor Variance and the Real World
- Lab Walkthrough Guidance
- Success Criteria
- Interview Q&A
- References
Chapter 1: The Three Protocols and Why There Are Three
A BMC (and a PDU, a CDU, a switch) is a tiny networked computer that exposes its hardware over a management API. Three such APIs dominate, born in three eras:
- IPMI (Intelligent Platform Management Interface, 1998): a binary out-of-band standard. You send byte-encoded commands to the BMC over a UDP session (IPMI-over-LAN / RMCP+) and get sensor readings, power control, and event logs back. Powerful, ubiquitous in deployed fleets — and deprecated (Intel stopped developing it in 2019) because it was complex and had a bad security history.
- SNMP (Simple Network Management Protocol, 1988): a polling protocol from the networking world. A manager polls an agent for values identified by numeric OIDs, or the agent sends unsolicited traps. Still everywhere on PDUs, switches, UPSs, and environmental sensors.
- Redfish (DMTF, 2015): the modern replacement — a RESTful, JSON, HTTPS, schema-defined API. Human-readable, hypermedia-driven, secure by default, and the standard all new hardware (including AI accelerator platforms) ships. This is the one to know cold.
Why three, still? Because a real fleet is heterogeneous: new accelerator nodes speak Redfish, the rack PDU speaks SNMP, an older switch speaks SNMP, and some BMCs still need IPMI for a specific operation Redfish doesn't yet expose. The senior move is Redfish- first: build your abstraction around Redfish's model and treat IPMI/SNMP as fallback drivers for the gear that needs them (Chapter 8).
Mental model: Redfish is "the hardware as a REST API with a typed schema." IPMI is "the hardware as a binary command set." SNMP is "the hardware as a tree of numbered variables you poll." Same goal (read/control hardware OOB), three idioms.
Chapter 2: Redfish — The Data Model
Redfish models a system as a tree of JSON resources at predictable URLs, all reachable from one entry point. Internalize this tree; it's the spine of the whole protocol.
/redfish/v1 ← ServiceRoot (the entry point, unauthenticated GET)
├── /Systems → collection of ComputerSystem (the HOSTs: power, boot, processors, memory)
│ └── /Systems/1 → one ComputerSystem
│ ├── Actions: #ComputerSystem.Reset (power on/off/cycle)
│ ├── Processors, Memory, EthernetInterfaces, Storage
│ └── Links → Chassis, ManagedBy(Manager)
├── /Chassis → collection of Chassis (the physical boxes)
│ └── /Chassis/1
│ ├── /Power → PowerSupplies, voltages, PowerControl (draw, cap)
│ └── /Thermal → Temperatures, Fans
├── /Managers → collection of Manager (the BMC ITSELF)
│ └── /Managers/1 → BMC firmware, network, SerialConsole, VirtualMedia
├── /UpdateService → firmware update (SimpleUpdate, multipart push) → returns a Task
├── /EventService → event subscriptions (push) / SSE
├── /TaskService → long-running operations (Tasks)
├── /SessionService → login/logout, sessions
└── /TelemetryService → MetricReports, MetricDefinitions (structured telemetry)
Three resource types carry most of the weight, and beginners conflate them:
ComputerSystem= the host (the OS-running machine): power state, boot order, CPUs, memory, NICs. "Reset this server" acts here.Chassis= the physical enclosure: itsPowerandThermalsub-resources hold PSUs, voltages, temperatures, fans. "What's the inlet temperature" reads here.Manager= the BMC itself: its own firmware, network config, virtual media, serial console. "Update the BMC firmware" / "mount an ISO" acts here.
A single physical node thus appears as (at least) a ComputerSystem + a Chassis + a
Manager, linked together via Links. Knowing which resource owns which capability is
the single most useful Redfish skill.
@odata annotations (Redfish builds on OData): @odata.id is a resource's canonical
URL (you follow these — never hardcode paths), @odata.type names its schema, and
collections carry Members (a list of {@odata.id} links) and Members@odata.count.
Chapter 3: Redfish — Navigation, Actions, Auth
Navigation is hypermedia (HATEOAS): you GET /redfish/v1, read the @odata.id of the
Systems collection, GET that, read a member's @odata.id, GET that, follow its
Links/Actions. You never hardcode /redfish/v1/Systems/1/... — you follow links,
because vendors lay out IDs differently (Chapter 9). Lab 01 does exactly this.
Reading is plain GET returning JSON. $expand lets you pull a collection's members
inline in one request (fewer round-trips, bigger payloads — a tradeoff at fleet scale).
Actions are the "do something" verbs, declared in a resource's Actions object and
invoked with POST:
POST /redfish/v1/Systems/1/Actions/ComputerSystem.Reset
{ "ResetType": "ForceRestart" } # GracefulRestart | ForceOff | On | PowerCycle ...
The available ResetType values are advertised in ResetType@Redfish.AllowableValues —
you read what's allowed, you don't guess.
Writing config is PATCH (partial update) on a resource (e.g., set the boot device).
Authentication — three levels:
- Unauthenticated: only
ServiceRootand a few metadata resources. - HTTP Basic: username/password on every request — simple, fine for scripts, but re-auths each call.
- Session-based (the production choice):
POST /SessionService/Sessionswith credentials → you get anX-Auth-Tokenheader → send it on subsequent requests →DELETEthe session to log out. Cheaper than Basic per-call and revocable. Always over HTTPS with certificate validation (Chapter 7).
Chapter 4: Redfish — Events and Tasks
Two features separate a toy client from a production one.
Events — how you learn a PSU failed without polling every BMC constantly:
- Subscription (push):
POST /EventService/Subscriptionswith aDestinationURL (your listener) and the event types you want. The BMC then POSTs events to you. Scales well but requires your endpoint to be reachable by every BMC. - SSE (Server-Sent Events): connect to the
ServerSentEventUriand the BMC streams events over a long-lived HTTP connection. No inbound endpoint needed; one connection per BMC. - Polling: just
GETthe relevant resources on an interval. Simplest, but high load and high latency at fleet scale. Use it as a fallback / for resources without events.
The tradeoff (an interview favorite): push/SSE give low latency and low steady-state load but add connection/endpoint management; polling is trivial but doesn't scale to tens of thousands of BMCs at low latency. Production telemetry (Phase 07) usually blends scheduled polling for metrics with event subscriptions for state changes/faults.
Tasks — how Redfish models long-running operations (a firmware update can take
minutes): the action returns HTTP 202 Accepted with a Task resource URL (a "task
monitor"). You GET that Task to watch TaskState (New→Running→Completed/Exception) and
PercentComplete, and read Messages for results. Anything slow returns a Task —
firmware updates (Phase 08), some resets, BIOS config jobs. A client that blocks waiting
for a synchronous response to a firmware push is broken; the correct pattern is "submit →
poll the Task → handle completion/failure" (Lab 01 extension).
Chapter 5: IPMI — The Legacy You Still Must Speak
IPMI is deprecated but pervasive; you will read sensors and SELs from IPMI BMCs for years.
Architecture: the BMC hosts the IPMI stack; you talk to it locally (KCS interface) or
remotely over IPMI-over-LAN using RMCP+ (an authenticated, optionally encrypted
UDP session — IPMI v2.0). The ubiquitous client is ipmitool:
ipmitool -I lanplus -H <bmc-ip> -U <user> -P <pass> chassis power status
ipmitool -I lanplus -H <bmc-ip> -U <user> -P <pass> sdr list # sensors
ipmitool -I lanplus -H <bmc-ip> -U <user> -P <pass> sel list # event log
(lanplus = RMCP+/IPMI 2.0; plain lan is insecure IPMI 1.5 — avoid.)
Sensors and the SDR: the Sensor Data Repository describes every sensor (type, units, and thresholds). A reading comes with its thresholds:
lnr/lc/lnc= lower non-recoverable / critical / non-criticalunc/uc/unr= upper non-critical / critical / non-recoverable
So a CPU temp reading isn't just "78°C" — it's "78°C, upper-non-critical 85, upper-critical 95," which lets you classify health (OK/Warning/Critical) the way the hardware intends. Lab 02 models this.
The SEL (System Event Log): a BMC-maintained log of hardware events (a PSU lost input, a DIMM corrected-error threshold, a thermal trip). Each record has a timestamp, sensor, and event type. Parsing the SEL is core to diagnostics (Phase 08).
Other IPMI: chassis control (power on/off/cycle/reset), boot-device override (boot once from PXE — Phase 05), and SoL (Serial-over-LAN) to watch a host boot remotely.
Why deprecated: cleartext options, a history of vulnerabilities (cipher-zero auth bypass, password-hash exposure), and protocol complexity. Never expose IPMI to an untrusted network; keep it on the isolated management VLAN (Phase 09) and prefer Redfish where available.
Chapter 6: SNMP — Polling, MIBs, and Traps
SNMP is how you talk to PDUs, switches, UPSs, and environmental gear that don't (yet) speak Redfish.
The model: a manager (your software) polls an agent (the device). Every value
the agent exposes is named by an OID (Object Identifier) — a dotted sequence of
numbers locating a node in a global tree, e.g. 1.3.6.1.2.1.1.3.0 is sysUpTime. A
MIB (Management Information Base) is the human-readable schema mapping names
(sysUpTime) to OIDs and types. Enterprise-specific values live under
1.3.6.1.4.1.<enterprise-number> (e.g., a vendor's PDU outlet metering).
The operations:
GET— fetch one OID's value.GETNEXT— fetch the next OID in lexicographic order. This is how you discover the tree without knowing it in advance.WALK— repeated GETNEXT to enumerate a subtree (e.g., all outlets' power draw). The ordering is lexicographic by OID, which is whysnmpwalkreturns values in a predictable, sorted sequence. Lab 02 implements this ordering.GETBULK(v2c+) — fetch many OIDs in one request (efficient WALK).SET— write a value (e.g., switch an outlet off — if the PDU allows).
Traps and informs: unsolicited messages the agent sends to your trap receiver (UDP 162) when something happens (a PSU failed). Traps are fire-and-forget; informs are acknowledged. You run a trap receiver to get low-latency notifications and poll for the full picture — neither alone is sufficient (traps can be lost; polling is slow).
Versions (security-critical):
- v1 / v2c: authentication is a cleartext community string ("public"/"private"). No encryption. Never on an untrusted network.
- v3: real security — USM (User-based Security Model) with authentication (HMAC) and privacy (encryption). The only version to use where security matters.
Chapter 7: Security Across the Three
These protocols control power and firmware; a compromise here is catastrophic (Phase 09). The rules:
- Redfish: HTTPS only, validate certificates (don't disable TLS verification — a
shockingly common bug), session tokens over Basic, least-privilege Redfish roles
(
ReadOnlyfor telemetry,Operator/Administratoronly where needed), and never log the token. - IPMI:
lanplus(RMCP+) only, strong passwords, disable cipher-suite 0 (the auth- bypass), and treat it as last-resort. Management VLAN only. - SNMP: v3 with auth+priv where it matters; if stuck on v2c (read-only PDUs), restrict by ACL to the management network and never use it for writes/secrets.
- All three: live only on the isolated management network (Phase 01, Ch. 6; Phase 09 builds the isolation). The single biggest real-world failure is a BMC/PDU reachable from a tenant or the internet — Shodan is full of exposed IPMI BMCs.
Chapter 8: The Driver Abstraction — Redfish-First With Fallback
A heterogeneous fleet demands one interface, many backends — the same seam idea as Phase 02 Ch. 9, applied to protocols:
class DeviceDriver(Protocol):
def power_state(self) -> str: ...
def set_power(self, action: str) -> None: ...
def read_sensors(self) -> list[Reading]: ...
class RedfishDriver(DeviceDriver): ... # preferred
class IpmiDriver(DeviceDriver): ... # fallback for IPMI-only BMCs
class SnmpDriver(DeviceDriver): ... # PDUs, switches
Your rack-management code programs against DeviceDriver; a factory picks the driver per
device (capability detection: try Redfish ServiceRoot, fall back to IPMI/SNMP). This:
- isolates protocol quirks behind a stable interface,
- lets you migrate a device class from IPMI to Redfish without touching callers,
- makes the whole stack testable with a
FakeDriver(Phase 02 seam), - is exactly the structure of the Phase 13 capstone and the Phase 07 exporter.
The unified Reading/state model (Lab 02) is the other half: normalize Redfish
Thermal.Temperatures, IPMI SDR readings, and SNMP sensor OIDs into one shape so the rest
of your system doesn't care where a temperature came from.
Chapter 9: Vendor Variance and the Real World
The dirty secret: "Redfish-compliant" hardware varies. A senior engineer plans for it.
- Layout differs: System IDs are
1,System.Embedded.1, or a UUID depending on vendor — which is why you follow links, never hardcode paths. - Schema gaps & OEM extensions: vendors add
Oemblocks for features not in the standard schema, and sometimes omit standard fields. Code defensively (.get()with defaults; validate presence). - Bugs & quirks: some BMCs return 200 with an error body, rate-limit aggressively, drop sessions, or implement an action subtly wrong. Apply Phase 02 robustness: retries with backoff, timeouts, idempotency, and a conformance test per vendor (Phase 11).
- Firmware drift: the same model behaves differently across BMC firmware versions — another reason for the firmware-baseline discipline (Phase 08).
This is where "experience with Redfish APIs" in the JD really means experience with real, imperfect Redfish implementations — and the abstraction (Chapter 8) plus defensive coding (Phase 02) is how you tame it.
Lab Walkthrough Guidance
Order: Lab 01 (Redfish) → Lab 02 (IPMI/SNMP). Learn the modern model first; it gives you the vocabulary (resources, sensors, events) that the legacy protocols map onto.
Lab 01 (Redfish client + mock):
python3 solution.py— it starts an in-process Redfish mock and walks it. Read the printed tree.- Trace the client:
GET /redfish/v1→ followSystems→ a System → itsChassislink →Power/Thermal. Notice it follows@odata.id, never hardcodes. - Watch the
ComputerSystem.Resetaction POST change the System'sPowerState. - Extensions: poll a Task to completion; add Session-token auth; point it at the real
DMTF
Redfish-Mockup-Serveror OpenBMCsushy-emulator(the README shows how).
Lab 02 (IPMI/SNMP):
python3 solution.py— read the SNMP OID-tree WALK (note the lexicographic order) and the IPMI SDR/SEL decode.- See all three protocols normalized into one
Readingwith a health classification from thresholds. - Extensions: parse real
ipmitool sdr/seltext; run againstsnmpsim; emit the unified readings in Prometheus exposition format (the Phase 07 bridge).
Phase capstone question: "A node speaks Redfish, its PDU speaks SNMPv2c, and an older
peer BMC only speaks IPMI. Sketch the one interface and three drivers, and say how you'd
get a low-latency alert when any PSU fails." (Answer: DeviceDriver with
Redfish/IPMI/SNMP backends + a unified Reading; PSU alerts via Redfish EventService
subscription/SSE and SNMP traps, with scheduled polling as the backstop.)
Success Criteria
You're done with this phase when — without notes:
- You can draw the Redfish tree and say which resource owns power vs temperature vs BMC firmware (Ch. 2)
-
You can navigate by following
@odata.idlinks and explain why you never hardcode paths (Ch. 3, 9) -
You can invoke an Action (POST) and read its
AllowableValues(Ch. 3) - You can explain Session vs Basic auth and HTTPS/cert validation (Ch. 3, 7)
- You can explain why firmware updates return a Task and how to monitor one (Ch. 4)
- You can choose push/SSE vs polling for events and justify it at fleet scale (Ch. 4)
- You can decode an IPMI sensor with thresholds and a SEL event (Ch. 5)
- You can perform/explain an SNMP WALK and the lexicographic GETNEXT ordering, and v2c vs v3 (Ch. 6)
- You can sketch the Redfish-first driver abstraction with IPMI/SNMP fallback (Ch. 8)
Interview Q&A
Q1: What is Redfish and why is the industry moving to it from IPMI? A: Redfish is the DMTF's modern out-of-band management standard: a RESTful, JSON, HTTPS, schema-defined API that models a system as a tree of resources (ServiceRoot → Systems, Chassis, Managers, …). The industry is moving to it because IPMI is a complex binary protocol with a poor security history (Intel deprecated it in 2019): Redfish is human-readable, hypermedia-driven (you follow links, so it's vendor-portable), secure by default (HTTPS, session tokens, role-based auth), and extensible via schemas and OEM blocks. It also models modern needs natively — Tasks for long operations, an EventService for push notifications, a TelemetryService for structured metrics, and Power/Thermal schemas that extend to PDUs and CDUs. New hardware ships Redfish; IPMI/SNMP remain as fallbacks for deployed and non-server gear.
Q2: Walk me through reading a server's power state and CPU temperature with Redfish.
A: GET /redfish/v1 (ServiceRoot, no auth needed) and read the @odata.id of the
Systems collection. GET that, take a member's @odata.id, GET the ComputerSystem —
its PowerState field is on/off. For temperature, follow the System's Links to its
Chassis, GET the Chassis, follow its Thermal sub-resource, and read
Temperatures[] (each has a ReadingCelsius and threshold fields). The key discipline is
that I follow @odata.id links rather than hardcoding /Systems/1/..., because vendors
lay out IDs differently — that's what makes Redfish portable. I'd do it over HTTPS with a
session token and certificate validation.
Q3: A firmware update takes minutes. How does Redfish handle that, and how do you write the client?
A: It's an asynchronous operation. You POST to the UpdateService (SimpleUpdate or a
multipart push) and the service responds 202 Accepted with a Task resource URL — a task
monitor. The client then polls that Task, reading TaskState (New→Running→Completed or
Exception) and PercentComplete, and inspects Messages for the result. So the correct
pattern is submit → poll the Task with backoff → handle completion or failure (and surface
progress) — never block expecting a synchronous response. This same Task pattern covers
BIOS config jobs and some resets. I'd also make the submit idempotent and handle the BMC
dropping the session mid-update (Phase 08 goes deeper on staging and rollback).
Q4: How do you get notified when a PSU fails — poll or push? A: Both, layered. For low-latency fault notification I subscribe via the Redfish EventService — either a push subscription (the BMC POSTs events to my listener) or SSE (I hold a long-lived connection per BMC and the BMC streams events). Push scales to many BMCs but needs my endpoint reachable; SSE needs no inbound endpoint but is one connection per BMC. For non-Redfish gear (PDUs), I run an SNMP trap receiver on UDP 162. But events can be missed (lost traps, dropped connections), so I also poll the relevant resources on a schedule as a backstop and to get the full state. At fleet scale (tens of thousands of BMCs) pure low-latency polling is too expensive, so the design is event-driven for state changes plus scheduled polling for metrics — exactly the Phase 07 telemetry pattern.
Q5: What's an OID, and what happens in an SNMP WALK?
A: An OID (Object Identifier) is a dotted sequence of numbers naming a node in SNMP's
global tree — e.g., 1.3.6.1.2.1.1.3.0 is sysUpTime. A MIB is the schema mapping
human names to OIDs and types; vendor-specific values live under
1.3.6.1.4.1.<enterprise>. A WALK enumerates a subtree by repeatedly issuing GETNEXT,
which returns the next OID in lexicographic order; you keep going until you leave the
subtree. So snmpwalk over a PDU's outlet-power subtree returns each outlet's draw in
sorted OID order. v2c uses GETBULK to fetch many at once. The security caveat: v1/v2c
authenticate with a cleartext community string and no encryption, so they're management-
network-only; v3 (USM) adds real auth and privacy and is what you use where security
matters.
Q6: Your fleet has new Redfish nodes, SNMP PDUs, and some IPMI-only BMCs. How do you build one tool?
A: A driver abstraction: define one DeviceDriver interface (power_state, set_power,
read_sensors, …) and implement RedfishDriver, IpmiDriver, and SnmpDriver behind it,
with a factory that detects capability per device (try Redfish ServiceRoot; fall back to
IPMI/SNMP). All rack-management logic programs against the interface, so protocol quirks
are isolated, a device can migrate from IPMI to Redfish without touching callers, and I can
inject a FakeDriver for hermetic tests. I'd pair it with a unified Reading/state model
that normalizes Redfish Thermal entries, IPMI SDR readings, and SNMP sensor OIDs into one
shape, plus defensive coding for vendor variance (follow links, .get() with defaults,
retries/timeouts, per-vendor conformance tests). That's the structure of a real
rack-management control plane and of this track's capstone.
Q7 (Staff): "Redfish-compliant" hardware from two ODMs behaves differently. How do you keep your platform sane?
A: Treat the standard as necessary but not sufficient. First, never hardcode paths —
follow @odata.id links so layout differences (System.Embedded.1 vs a UUID) don't break
me. Second, code defensively: .get() with defaults, validate presence, tolerate OEM
blocks and missing optional fields, and read AllowableValues instead of assuming. Third,
build a conformance harness (Phase 11): a suite that exercises each vendor/firmware
combo against my expected behavior, run in CI against their emulators, so a quirk is caught
before deployment, not in production. Fourth, work the relationship: feed concrete defects
and schema-gap requests back to the ODM (e.g., Pegatron) as part of the protocol
documentation and EBOM process (Phase 12) — driving them toward conformance is part of the
Staff job. Finally, pin and track BMC firmware versions (Phase 08) because the same model
drifts across firmware. The abstraction layer plus the conformance suite plus the partner
feedback loop is how a platform stays maintainable across a heterogeneous, imperfect fleet.
References
- DMTF Redfish — Specification, Schema Index, and the Redfish School — https://www.dmtf.org/standards/redfish
- DMTF
Redfish-Mockup-Server(a real Redfish service to test against) — https://github.com/DMTF/Redfish-Mockup-Server - OpenBMC
sushy-emulator(a Redfish emulator) — https://docs.openstack.org/sushy/latest/ - DMTF Redfish Power/Thermal & Outlet equipment schemas (PDU/CDU) — https://redfish.dmtf.org/schemas/
- IPMI v2.0 Specification (Intel/Dell/HP/NEC) and
ipmitool— https://github.com/ipmitool/ipmitool - IETF SNMP — RFC 1157 (v1), RFC 3416 (v2c ops), RFC 3414/3826 (v3 USM/priv)
- Net-SNMP tools (
snmpwalk,snmptrapd) &snmpsimsimulator — http://www.net-snmp.org/ , https://github.com/etingof/snmpsim - "Penetration Testing a BMC" / IPMI security background (Rapid7, Dan Farmer's IPMI papers) — for Chapter 7
- Cross-track: Security Engineer — hardware/firmware device security
🛸 Hitchhiker's Guide — Phase 03: Management Protocols
Read this if: you need to talk to a BMC/PDU/CDU and "Redfish, IPMI, SNMP" is an alphabet soup. This is the compressed field guide — the tree, the commands, the gotchas.
0. The 30-second mental model
Three ways to talk to hardware out-of-band, three eras:
- Redfish = the hardware as a REST/JSON tree (modern, secure, the one to master).
- IPMI = the hardware as a binary command set (legacy, deprecated, still everywhere).
- SNMP = the hardware as a tree of numbered variables you poll (PDUs/switches).
Senior move: Redfish-first with IPMI/SNMP as fallback drivers behind one interface.
If you remember one rule: follow @odata.id links, never hardcode Redfish paths.
1. The Redfish tree (tattoo this)
/redfish/v1 ServiceRoot (GET, no auth)
├─ /Systems/{id} the HOST → PowerState, boot, CPUs; Actions: Reset
├─ /Chassis/{id} the BOX → /Power (PSUs, draw, cap), /Thermal (temps, fans)
├─ /Managers/{id} the BMC → its firmware, network, VirtualMedia, SerialConsole
├─ /UpdateService firmware → returns a TASK
├─ /EventService push/SSE events
├─ /TaskService long ops
└─ /SessionService login → X-Auth-Token
Which resource owns what (the #1 confusion): System = host power/boot, Chassis = temps/PSUs, Manager = the BMC itself. One node = a System + a Chassis + a Manager.
2. Redfish verbs
| Want | Do |
|---|---|
| read | GET (follow @odata.id); $expand to inline a collection |
| power on/off/cycle | POST .../Actions/ComputerSystem.Reset { "ResetType": "..." } |
| change config | PATCH the resource |
| log in | POST /SessionService/Sessions → use X-Auth-Token → DELETE to log out |
| firmware update | POST to UpdateService → 202 + Task → poll Task |
Read …@Redfish.AllowableValues for what a field/action accepts — don't guess.
3. IPMI in five commands (ipmitool -I lanplus -H ip -U u -P p ...)
chassis power status|on|off|cycle|reset
sdr list # sensors + readings + thresholds
sensor list # detailed sensors
sel list # the System Event Log (hardware events)
chassis bootdev pxe # boot-once override (Phase 05)
sol activate # Serial-over-LAN console
Sensor reading = value + thresholds: lnr lc lnc | reading | unc uc unr. Health is
"which threshold did it cross." Always lanplus (RMCP+); never plain lan/cipher-0.
4. SNMP in one breath
- Value = OID (
1.3.6.1.2.1.1.3.0= sysUpTime). MIB = the name↔OID schema. Vendor stuff under1.3.6.1.4.1.<enterprise>. GETone ·GETNEXTthe next-in-lexicographic-order ·WALKa subtree ·GETBULKmany ·SETwrite · trap = device → you (UDP 162).snmpwalk -v3 -l authPriv -u user -a SHA -A authpw -x AES -X privpw host OID- v1/v2c = cleartext community string (mgmt-net only); v3 = auth+priv (use this).
5. Events: poll vs push vs SSE
| latency | steady load | needs | use when | |
|---|---|---|---|---|
| poll | high | high at scale | nothing | metrics, no-event resources |
| push subscription | low | low | your endpoint reachable by BMCs | fleet fault alerts |
| SSE | low | low | 1 long conn/BMC | when inbound endpoints are hard |
Real telemetry (Phase 07): poll for metrics + subscribe for state changes/faults.
6. Security non-negotiables
- Redfish: HTTPS + validate the cert (disabling TLS verify is the classic bug), session tokens, least-privilege roles, never log the token.
- IPMI:
lanplusonly, disable cipher-0, strong creds, last resort. - SNMP: v3 where it matters; v2c read-only + ACL otherwise.
- All three live ONLY on the isolated management VLAN (Phase 09). Exposed BMCs are a Shodan staple and a fleet-wide compromise.
7. The driver abstraction (the senior pattern)
DeviceDriver(power_state, set_power, read_sensors)
├─ RedfishDriver (preferred)
├─ IpmiDriver (fallback)
└─ SnmpDriver (PDUs/switches)
+ unified Reading model so callers don't care where a temp came from
This is the Phase 13 capstone and Phase 07 exporter in one diagram.
8. Vendor variance survival kit
- Follow links (IDs differ:
1vsSystem.Embedded.1vs UUID). .get()with defaults; tolerateOemblocks and missing optional fields.- Retries/backoff/timeouts (BMCs are flaky); idempotent actions.
- A per-vendor conformance suite in CI (Phase 11); pin BMC firmware (Phase 08).
9. The "done this before" tells
"Which resource owns that — System, Chassis, or Manager?" · "Are you following the link or hardcoding the path?" · "Is that a Task? Poll it." · "v2c or v3?" · "lanplus, right?" · "Is TLS verification on?" · "Event or poll — and what's the backstop?"
10. How this phase pays off later
- The driver + unified Reading is the foundation of the Phase 07 exporter and Phase 13 capstone.
- Tasks are how Phase 08 firmware updates are monitored.
- EventService/traps feed Phase 07 alerting and Phase 10 RCA.
- Protocol security is enforced by the Phase 09 management-network isolation.
Lab 01 — Redfish Client Against a Live Mock
Phase: 03 — Management Protocols | Difficulty: ⭐⭐⭐☆☆ | Time: 4–6 hours Language: Python 3 (stdlib only) | Hardware: none (in-process Redfish mock)
Concept primer:
../WARMUP.mdCh. 2–4,../HITCHHIKERS-GUIDE.md§1–§2, §5.
Run
python3 solution.py # starts a Redfish mock, walks it, performs Reset, subscribes
Run it for REAL against the DMTF Redfish-Mockup-Server (the extension — verified)
redfish_real.py reuses the exact same RedfishClient against an independent, real Redfish
service — proving the link-following design (the client doesn't change):
git clone https://github.com/DMTF/Redfish-Mockup-Server
pip install -r Redfish-Mockup-Server/requirements.txt
python3 Redfish-Mockup-Server/redfishMockupServer.py \
-D Redfish-Mockup-Server/public-rackmount1 -S -p 8000 & # -S = short-form mockup
python3 redfish_real.py http://localhost:8000
Verified output against DMTF's public-rackmount1 mockup:
System: id=437XR1138R2 model=3500 power=On (id is a vendor string, not hardcoded)
temp CPU1 Temp 41C
temp CPU2 Temp NoneC # a real sensor with no reading -> code defensively
power System Input Power 344 W
reset allowable: ['On','ForceOff','GracefulShutdown','GracefulRestart','ForceRestart','Nmi',...]
The System id is 437XR1138R2 — not node-00 or 1. That's exactly the vendor variance the
"follow @odata.id links, never hardcode" rule (WARMUP Ch. 3, 9) exists for; the client handles it
unchanged, and .get() tolerates the None-reading sensor. (You can also point it at OpenBMC's
sushy-emulator or a real BMC — same code.)
0. The mission
Write the Redfish client a rack manager is built on — and write it the right way: by
following @odata.id links from the ServiceRoot, not by hardcoding paths. To make it
runnable with zero setup, the lab spins up a small in-process Redfish HTTP mock; the client
code is the real thing and works unchanged against the DMTF mockup server, OpenBMC's
sushy-emulator, or a real BMC.
Two questions you must answer at the end:
- Why follow links instead of constructing
/redfish/v1/Systems/1/...? - Which resource holds power state, which holds temperature, and which holds BMC firmware?
1. What the client does (read RedfishClient)
| Operation | Method | Path discipline |
|---|---|---|
first_system() | GET ×3 | root → Systems collection → first member (all by link) |
power_for() / thermal_for() | GET | System → Links.Chassis → Power/Thermal (by link) |
reset() | POST | the action's advertised target; server validates AllowableValues |
subscribe() | POST | EventService/Subscriptions; returns 201 + Location |
Notice the System owns PowerState and the Reset action; the Chassis owns Power
(PSUs, draw, cap) and Thermal (temps, fans); the Manager owns BMC firmware. Putting
the right capability on the right resource is the core Redfish skill (WARMUP Ch. 2).
2. What the run shows
System node-00 (AI Compute Node 00) power=On health=OK
power: 1480 W consumed, cap 2000 W, PSUs ['OK', 'OK']
temp: Inlet 27C (crit 45)
temp: Accelerator0 71C (crit 90)
reset(GracefulShutdown) -> HTTP 204 ← action accepted (No Content)
power now: Off ← state actually changed
subscription created -> HTTP 201 at .../Subscriptions/sub-1
The self-tests additionally prove that an illegal ResetType is rejected with 400 (the
server checks AllowableValues), and that the subscription appears in the collection.
3. Extension exercises (do them — this is the lab)
- Run it against a real Redfish service. Install the DMTF mockup server
(
pip install redfish-mockup-serveror cloneDMTF/Redfish-Mockup-Server) or OpenBMC'ssushy-emulator, start it, and pointRedfishClient(base)at it. Your client should work unchanged — that's the payoff of following links. - Tasks: add an
UpdateServiceSimpleUpdate that returns 202 + a Task, and make the client poll the Task (TaskState,PercentComplete) to completion. This is the exact pattern Phase 08 firmware updates use. - Session auth: add a
SessionServiceto the mock; have the client POST credentials, capture theX-Auth-Token, send it on every request, andDELETEthe session at the end. Then add HTTPS with certificate validation (and a test that rejects a bad cert). $expand: implement$expand=.on the Systems collection so one GET returns all members inline, and discuss the round-trips-vs-payload tradeoff at fleet scale.- Fold into the driver abstraction: make
RedfishClientimplement theDeviceDriverinterface from WARMUP Ch. 8 so it's swappable with the IPMI/SNMP drivers from Lab 02.
4. Common pitfalls
- Hardcoding paths —
/redfish/v1/Systems/1works on one vendor and 404s on the next (IDs differ:1,System.Embedded.1, a UUID). Follow@odata.id. - Assuming fields exist — real BMCs omit optional fields and add
Oemblocks. Use.get()with defaults; validate presence. - Blocking on async operations — a firmware update returns a Task, not a result. Poll it; don't wait for a synchronous body.
- Disabling TLS verification — the single most common Redfish security bug. Validate the certificate; pin or trust the management CA.
- Re-authenticating per call — use a session token, not Basic auth on every request, at fleet scale.
5. What this lab proves about you
You can write the Redfish client at the heart of any rack-management product — correctly
(link-following, action validation, events) and portably (works against any conformant
service). In an interview, "discover a server's power and temperature over Redfish" becomes
a confident walk of ServiceRoot → System → Chassis → Power/Thermal, with the right notes on
Sessions, HTTPS, Tasks, and events. This client becomes a DeviceDriver backend in the
Phase 13 capstone and the data source for the Phase 07 exporter.
Lab 02 — SNMP Poller/Trap Receiver + IPMI Sensors
Phase: 03 — Management Protocols | Difficulty: ⭐⭐⭐☆☆ | Time: 4–6 hours Language: Python 3 (stdlib only) | Hardware: none (simulated agents)
Concept primer:
../WARMUP.mdCh. 5 (IPMI) & Ch. 6 (SNMP),../HITCHHIKERS-GUIDE.md§3–§4.
Run
python3 solution.py # SNMP WALK + trap decode + IPMI SDR/SEL + unified view
0. The mission
Learn the two legacy protocols by implementing their semantics: a lexicographic SNMP
WALK, an IPMI SDR sensor with real thresholds and a health verdict, a SEL decode, and —
the senior part — normalizing all of it into one Reading model so the rest of your
system doesn't care whether a temperature came from Redfish, IPMI, or SNMP.
Two questions you must answer at the end:
- Why does an SNMP WALK return
.2before.10(and what breaks if you sort OIDs as strings)? - How does an IPMI sensor reading become "OK / Warning / Critical"?
1. The real commands this models
| This lab | Real-world equivalent |
|---|---|
snmp_walk(agent, prefix) | snmpwalk -v2c -c public <pdu> 1.3.6.1.4.1.9999.2.1 |
snmp_getnext | snmpgetnext |
decode_trap(...) | snmptrapd receiving a trap on UDP 162 |
ipmi_sensor_readings | ipmitool -I lanplus -H <bmc> -U u -P p sdr list |
decode_sel | ipmitool ... sel list |
2. The two ideas that matter
Lexicographic OID ordering — OIDs sort numerically per component, so ...2.1.2 comes
before ...2.1.10. Sorting OIDs as plain strings is a classic bug (you'd get .1, .10, .2).
_oid_key splits on . and compares integer lists; WALK then collects a subtree in the
order a real agent returns it. This is why snmpwalk output is predictably ordered.
IPMI threshold health — a sensor reading is meaningless without its thresholds. Each
IpmiSensor carries lnr/lc/lnc (lower non-recoverable/critical/non-critical) and
unc/uc/unr (upper). health() walks them from most to least severe and returns
OK/Warning/Critical — exactly how the hardware intends a reading to be interpreted. The
"Accel0 Temp 92°C → Warning" line is the upper-non-critical band in action.
3. What the run shows
A WALK in correct numeric order, a decoded PSU-failure trap, IPMI sensors classified by threshold (note the hot accelerator flagged Warning), the SEL hardware-event log decoded to human strings, and finally 9 readings from two protocols collapsed into one model with a health histogram — the seed of the Phase 07 telemetry pipeline.
4. Extension exercises (do them — this is the lab)
- Point SNMP at
snmpsim.pip install snmpsim, runsnmpsimdwith a recorded community, and replace thePDU_AGENTdict with realurllib/pysnmpGETNEXT calls. Your WALK logic stays the same. - Parse real
ipmitooloutput. Captureipmitool sdr list/sel listtext and write a parser that producesIpmiSensor/SEL records — real fleets often shell out toipmitool(safely, via an arg list — Phase 02 Ch. 8) rather than speak the binary protocol directly. - A real trap receiver. Bind a UDP socket on 162 (or a high port) and decode incoming
SNMP trap PDUs; integrate with
decode_trap. Discuss trap loss and why you still poll. - SNMPv3. Add the USM concepts (auth + priv) and explain why v2c community strings are management-network-only.
- Emit Prometheus. Render
unified_view()in Prometheus exposition format (pdu_outlet_power_watts{outlet="1"} 410) — the literal bridge to the Phase 07 exporter.
5. Common pitfalls
- String-sorting OIDs —
.10sorts before.2as strings; WALK breaks. Sort by integer components. - Reading without thresholds — "78°C" alone can't be classified; carry the SDR thresholds with the value.
- Trap-only or poll-only — traps can be lost (UDP, fire-and-forget); polling is slow. Production does both.
- v2c on an untrusted net — the community string is cleartext; it's mgmt-VLAN-only, and v3 where security matters.
- Per-protocol data shapes leaking upward — normalize to one
Readingat the driver boundary so callers (alerting, dashboards) stay protocol-agnostic.
6. What this lab proves about you
You understand the protocols a heterogeneous fleet actually speaks, down to the WALK ordering and the threshold model — and you can fuse them into one coherent telemetry view. In an interview, "how do you monitor a PDU and an old IPMI BMC alongside Redfish nodes?" becomes a clear answer: per-protocol drivers, threshold-aware health, traps + polling, and one unified model — which is exactly the pipeline you build in Phase 07 and the capstone.
Phase 04 — Hardware Interfacing: BMC, PDU, CDU, PCIe Switches
Difficulty: ⭐⭐⭐⭐☆ | Estimated Time: 2 weeks
Roles supported: Rack Management SWE (Senior/Staff), Firmware/Platform SWE, HW-bringup SWE
Hardware needed: none — simulators + your own machine's lspci
Why This Phase Exists
Phase 03 gave you the protocols; this phase gives you the devices those protocols talk to. The JD names them explicitly: "Interface with rack hardware (BMC, PDU, CDU, PCIe switches)." Each is a distinct managed component with its own model, failure modes, and control surface, and a rack manager must understand all four:
- BMC — the management brain of every node (power, sensors, console, virtual media, firmware).
- PDU — power distribution and metering; the source of per-outlet/per-phase power truth.
- CDU — the coolant distribution unit of a liquid-cooled rack; flow/ΔT/pressure/leak (Phase 01 Ch. 5).
- PCIe switches — the fabric wiring accelerators, NICs, and CPUs together; topology, lanes, hot-plug, errors.
"Understanding of server hardware, networking, and storage systems" (a JD requirement) is
exactly this layer. You'll learn what each device is, how to read and control it, what
breaks, and how to model it — then build the tools: a BMC interaction toolkit, a PDU/CDU
telemetry simulator, and a PCIe topology mapper that turns your own machine's lspci
into a fabric graph.
Concepts
BMC / OpenBMC (deeper than Phase 01)
- The BMC SoC (e.g., ASPeed AST2600), its RTOS/Linux, standby power, the host↔BMC interfaces (KCS, LPC/eSPI, PCIe VDM, NC-SI for shared NIC)
- OpenBMC architecture: D-Bus,
phosphor-*services, the sensor/Entity model,obmcmapping to Redfish/IPMI - Capabilities: power/reset control, sensor aggregation, SoL, KVM/iKVM, virtual media (mount ISO), SEL, firmware (host BIOS + BMC self-update)
- The BMC as a security boundary (Phase 09): it can do anything; protect it accordingly
PDU (Power Distribution Unit)
- Types: basic, metered, switched, metered-and-switched; per-outlet vs per-phase metering
- Busbar vs PDU power topologies (OCP); 3-phase balance; the metering data model
- Reading draw, switching outlets, alarms; protocols (SNMP common, Redfish PDU schema emerging)
- Why per-outlet metering is the ground truth for the Phase 01 budget calculator
CDU (Coolant Distribution Unit) & liquid cooling telemetry
- The secondary (rack) loop vs primary (facility) loop; heat exchanger; pumps; manifolds; cold plates
- Telemetry: supply/return temperature, ΔT, flow (L/min), pressure, pump speed, leak detection
- Failure modes: pump failure, fouling (ΔT rises at constant load), blockage, leak → emergency action
- Protocols: Modbus/SNMP/Redfish; the safety-critical nature of leak handling
PCIe switches & fabric
- PCIe basics: lanes, links, generations (Gen4/5/6 bandwidth), the tree topology (root complex → switches → endpoints)
- PCIe switches (e.g., Broadcom/Microchip): fanout, lane allocation, hot-plug, peer-to-peer (P2P) for GPU-direct
- Reading topology: BDF (bus:device.function),
lspci, sysfs (/sys/bus/pci/devices), capabilities, link speed/width - Errors: AER (Advanced Error Reporting), correctable vs uncorrectable, link down/degraded (a degraded x16→x4 link is a silent perf killer) — feeds Phase 08 RAS
- The scale-up fabric (NVSwitch-class) vs PCIe — where each lives
Cross-cutting
- The device-driver abstraction extended to all four device classes (Phase 03 Ch. 8)
- Modeling them in the Phase 01 inventory graph; relating telemetry to components
- Safety: power and cooling actions are physical and irreversible-ish — confirmation, interlocks, dry-run
Labs
Lab 01 — BMC Power Control, Sensors & Virtual Media
| Field | Value |
|---|---|
| Goal | Build a BMC interaction toolkit (over the Phase 03 Redfish/IPMI patterns) that reads sensors, controls power safely (with dry-run + confirmation), and drives virtual-media for remote reinstall. |
| Concepts | OpenBMC capabilities, power/reset semantics, SoL/KVM/virtual-media, the BMC as a security boundary, safe mutating operations. |
| Steps | 1) python3 solution.py — runs against the bundled BMC mock. 2) Read the power state machine and the virtual-media flow. 3) Note the dry-run/confirm guard on every mutating call. 4) Extensions below. |
| Stack | Python 3 stdlib (BMC mock) |
| Output | A toolkit that reads sensors, power-cycles safely, and mounts/ejects virtual media — the operations behind "remote reinstall." |
| How to Test | Asserts verify the power state machine, the dry-run guard (no mutation), and the virtual-media mount/eject lifecycle. |
| Talking Points | Why the BMC works when the host is dead; virtual media → remote OS install; why every power op needs a guard; BMC as attack surface. |
| Resume Bullet | "Built a BMC interaction toolkit (Redfish/IPMI) for safe power control, sensor aggregation, and virtual-media-driven remote reinstall, with dry-run/confirmation guards on all mutating operations." |
| Extensions | Add SoL capture; add a power-on→POST-watch flow; wire it to the real OpenBMC sushy-emulator. |
Lab 02 — PDU/CDU Telemetry Simulator + PCIe Topology Mapper
| Field | Value |
|---|---|
| Goal | Two tools: a PDU/CDU telemetry simulator (draw, ΔT, flow, leak) feeding the Phase 01 budget logic, and a PCIe topology mapper that parses lspci-style output into a fabric graph and flags degraded links. |
| Concepts | PDU metering, CDU thermal telemetry & leak handling, PCIe BDF/topology, link speed/width, degraded-link detection, AER. |
| Steps | 1) python3 solution.py. 2) Read the simulated PDU/CDU telemetry + the leak/ΔT alarms. 3) See the PCIe tree built from sample lspci output, with a degraded x16→x4 link flagged. 4) Run it on your own machine's lspci. 5) Extensions below. |
| Stack | Python 3 stdlib; optional real lspci -mm -nn on Linux |
| Output | Live-ish PDU/CDU telemetry with alarms + a PCIe topology graph with degraded-link detection. |
| How to Test | Asserts verify leak/ΔT alarm thresholds, PDU draw aggregation, and degraded-link detection in the parsed topology. |
| Talking Points | Why ΔT rising at constant load predicts a cooling failure; leak handling as safety-critical; how a silent x16→x4 link halves bandwidth; BDF addressing. |
| Resume Bullet | "Built a PDU/CDU telemetry simulator with leak and ΔT alarming and a PCIe topology mapper that parses lspci into a fabric graph and detects degraded links (x16→x4) and AER errors." |
| Extensions | Parse /sys/bus/pci/devices for link state; correlate AER counts; emit Prometheus metrics (Phase 07); feed draw into the Phase 01 calculator as live data. |
Deliverables Checklist
- You can list OpenBMC capabilities and explain the host↔BMC interfaces
- BMC toolkit reads sensors and controls power with a dry-run/confirmation guard
- You can describe the virtual-media remote-reinstall flow end to end
- PDU simulator aggregates per-outlet draw into a rack total (feeds Phase 01)
- CDU simulator alarms on leak and on ΔT rising at constant load
-
PCIe mapper builds a topology graph from
lspciand flags a degraded link - You can explain why a degraded x16→x4 link is a silent performance killer
- You can relate each device to the Phase 01 inventory model and Phase 08 RAS
Interview Relevance
- "What is a BMC made of, and how does the host talk to it?"
- "How would you reinstall an OS on a server you can't physically touch?" (virtual media)
- "A rack's cooling is degrading — what telemetry tells you before it fails?" (ΔT, flow, pump)
- "How do you handle a coolant leak signal in software?" (safety-critical, emergency action)
- "Walk me through reading PCIe topology on Linux. What's a BDF?"
- "An accelerator is slow but 'healthy' — how could PCIe be the cause?" (degraded link width/speed)
- "How do you make a power-off operation safe to expose in a tool?"
Warmup Guide — Hardware Interfacing: BMC, PDU, CDU, PCIe Switches
Zero-to-expert primer for Phase 04. Phase 03 taught the protocols; this teaches the devices — what each one is, how to read and control it, how it fails, and how to model it. By the end you can reason about a node's BMC, its power and cooling, and its PCIe fabric the way a hardware-fluent software engineer must.
Table of Contents
- Chapter 1: The BMC, Under the Hood
- Chapter 2: OpenBMC — How the Management Stack Is Built
- Chapter 3: BMC Operations — Power, Console, Virtual Media
- Chapter 4: PDUs and Power Distribution
- Chapter 5: CDUs and Liquid-Cooling Telemetry
- Chapter 6: PCIe — The Fabric Inside the Node
- Chapter 7: Reading PCIe Topology on Linux
- Chapter 8: Modeling and Safety
- Lab Walkthrough Guidance
- Success Criteria
- Interview Q&A
- References
Chapter 1: The BMC, Under the Hood
The BMC (Phase 01 Ch. 3) is a real computer; now look inside. A typical server BMC is an ASPeed AST2500/2600 — an ARM SoC with its own RAM, flash, Ethernet MAC, and a pile of hardware interfaces to the host. It runs on standby power (alive whenever the rack has any power, even with the host off) and boots its own firmware (often OpenBMC, a Linux distribution) independently of the host.
How the BMC senses and controls the host — the interfaces matter because they're where bugs and security boundaries live:
- KCS / BT (LPC or eSPI): the legacy in-band channel the host OS uses to talk IPMI to the BMC locally.
- eSPI / LPC: the bus carrying low-pin-count peripherals, TPM, and host↔BMC messaging.
- PECI: the Intel interface the BMC uses to read CPU temperature/telemetry directly.
- I²C / PMBus: how the BMC reads voltage regulators, PSUs, temperature sensors, and the FRU EEPROMs (the parts inventory — Phase 01 Ch. 10).
- NC-SI: lets the BMC share the host's NIC for management traffic (vs a dedicated mgmt port) — a shared-NIC design has security implications (Phase 09).
- PCIe VDM / MCTP: modern in-band management messaging (e.g., to NICs, accelerators).
The key idea: the BMC aggregates every low-level sensor and control into one
management endpoint, then exposes them via Redfish/IPMI (Phase 03). When you GET a
temperature over Redfish, the BMC read it over PECI/I²C and translated it. Understanding
this chain is what lets you debug "the sensor reads garbage" (Phase 10) — is it the sensor,
the I²C bus, the BMC firmware, or the Redfish mapping?
Chapter 2: OpenBMC — How the Management Stack Is Built
OpenBMC (a Linux Foundation project, used by Meta, Google, IBM, Microsoft, and many ODMs including for accelerator platforms) is worth knowing because it's the firmware your software talks to, and increasingly the one you might contribute to alongside an ODM (Phase 12).
Architecture, briefly:
- It's embedded Linux + a set of services communicating over D-Bus (the IPC bus).
- The
phosphor-*services each own a domain:phosphor-hwmon(sensors from kernel hwmon),phosphor-state-manager(host/chassis power state),phosphor-host-ipmid(the IPMI daemon),entity-manager(discovers what hardware is present from config + EEPROMs),phosphor-logging(events/SEL). - bmcweb is the HTTP server that exposes Redfish (and a web UI), translating D-Bus
state into the Redfish resource tree you navigated in Phase 03. So "the Redfish
Thermalresource" is bmcweb renderingphosphor-hwmon's D-Bus sensor objects.
Why this matters: when a Redfish field is missing or wrong on an OpenBMC system, the chain
is sensor → hwmon → D-Bus → bmcweb → Redfish JSON. Knowing the chain turns "vendor bug"
into a specific, reportable defect (and sometimes a patch you send upstream). It's also why
"every BMC is subtly different" (Phase 03 Ch. 9): different entity-manager configs and
firmware versions render different trees.
Chapter 3: BMC Operations — Power, Console, Virtual Media
The operations your toolkit (Lab 01) performs, and their semantics:
Power control — the host has a small state machine the BMC drives:
On/ForceOff(yank power — abrupt) /GracefulShutdown(ask the OS to shut down — needs a cooperating OS) /ForceRestart/GracefulRestart/PowerCycle.- The subtlety:
GracefulShutdowncan fail silently if the host OS is hung (nothing to ask). Robust tooling tries graceful, waits, then escalates to force — exactly the retry/escalation pattern from Phase 02.
Console — see what the host is doing without a monitor:
- SoL (Serial-over-LAN): the host's serial console redirected over the BMC's network — text, scriptable, the workhorse for watching boot/POST and kernel panics (Phase 08/10).
- KVM / iKVM: full remote keyboard-video-mouse (graphical), for BIOS screens.
Virtual media — the killer OOB feature: the BMC presents a network ISO/image as if it were a physical USB/CD drive to the host. Combined with boot-device override, this is how you reinstall an OS on a server you can't touch: mount the installer ISO over Redfish, set next-boot to the virtual CD, power-cycle, and the host boots your installer. This is the backbone of bare-metal provisioning's "rescue path" (Phase 05) and a primary reason OOB exists. Lab 01 implements the mount→set-boot→cycle→eject lifecycle.
Firmware — the BMC can update the host's BIOS/UEFI/NIC firmware and self-update its own firmware (Phase 08), all over the management network.
Chapter 4: PDUs and Power Distribution
The PDU is where your power truth comes from. Types (in increasing capability):
- Basic: dumb power strip. No telemetry. (Rare in modern racks.)
- Metered: reports power draw (per-phase and/or per-outlet). Read-only truth for budgeting.
- Switched: can turn individual outlets on/off remotely (e.g., hard power-cycle a wedged device whose BMC is also wedged).
- Metered + switched: both — the common AI-rack choice.
Per-outlet vs per-phase metering: per-outlet tells you which device draws what (great for the Phase 01 budget and for attributing power to tenants); per-phase tells you balance across the 3 phases (an unbalanced load wastes capacity and can trip a phase). A good rack manager reads both.
Busbar designs (OCP): in OCP racks the power shelf feeds a DC busbar and nodes blind-mate on; metering may live in the power shelf rather than discrete PDUs. Same data (draw, per-feed), different physical form.
Protocols: SNMP is the historical standard for PDUs (Phase 03 Ch. 6 — you walk an outlet subtree); the DMTF Redfish PDU/Outlet schema is the modern path. Switching an outlet is a physical, disruptive action — guard it (Chapter 8).
Why it feeds Phase 01: the budget calculator's "measured draw" is exactly per-outlet PDU telemetry. Nameplate is a guess; the PDU is ground truth. Lab 02 simulates this and you can feed it back into the Phase 01 calculator.
Chapter 5: CDUs and Liquid-Cooling Telemetry
The CDU (Phase 01 Ch. 5) is a managed device you monitor and, in emergencies, act on.
The two loops: the secondary (rack-side) loop is clean coolant the CDU pumps through cold plates; the primary (facility) loop is chilled water. The CDU's heat exchanger moves heat between them while keeping the clean loop isolated from facility water (which may be dirty/corrosive). You manage the secondary loop's behavior.
Telemetry you read (and what each tells you):
- Supply / return temperature and ΔT (return − supply): the workhorse signal. Heat removed ∝ flow × ΔT. ΔT rising at constant IT load is the classic early warning of reduced flow or fouling — a pre-failure signal you alert on (Lab 02).
- Flow (L/min): drops indicate a failing/throttled pump or a blockage.
- Pressure: out-of-range indicates a leak, blockage, or pump issue.
- Pump speed / status: redundant pumps (N+1) — losing one is tolerable, losing the spare is not.
- Leak detection: rope/spot sensors. Safety-critical — a leak near live electronics can destroy hardware and is a hazard. Software response is decisive: alarm at top priority, and (per policy/interlock) trigger an emergency action (reduce load via power capping, or controlled shutdown/valve action). Treat it like a fire alarm in code.
Protocols: CDUs commonly speak Modbus (industrial) and/or SNMP, with Redfish
CoolingLoop/CoolingUnit schemas emerging. Your software normalizes whichever into the
unified model (Phase 03 Ch. 8).
Failure modes to recognize (interview-ready): pump failure (flow→0, temps spike), fouling/scaling (ΔT creeps up over weeks), air in the loop (erratic flow/pressure), blockage (high pressure, low flow), leak (pressure drop + leak sensor). Each has a distinct telemetry signature — that's why you collect all of it.
Chapter 6: PCIe — The Fabric Inside the Node
PCIe (Peripheral Component Interconnect Express) wires the CPU, accelerators, NICs, and NVMe together inside a node. You must understand it because (a) accelerators and NICs hang off it, (b) its topology determines performance (P2P, NUMA), and (c) its errors are a major RAS source (Phase 08).
The basics:
- A lane is a serial differential pair (TX+RX). A link bundles lanes: x1, x4, x8, x16 (the common GPU/accelerator width). More lanes = more bandwidth.
- Generations double per-lane bandwidth: Gen4 ≈ 2 GB/s/lane, Gen5 ≈ 4 GB/s/lane, Gen6 ≈ 8 GB/s/lane. So a Gen5 x16 link ≈ 64 GB/s each way. (Recall Phase 01: PCIe is the narrow tier vs HBM/NVLink.)
- Topology is a tree: a Root Complex (in the CPU) → PCIe switches (fan-out) → endpoints (accelerators, NICs, NVMe). Switches let one root port fan out to many devices and enable peer-to-peer (P2P) transfers (e.g., GPUDirect RDMA: NIC→GPU without bouncing through CPU memory).
- BDF addressing: every function is
Bus:Device.Function(e.g.,0000:3b:00.0), the coordinate you use to identify a device inlspci/sysfs.
PCIe switches (Broadcom/Microchip et al.) are managed devices in AI systems: they allocate lanes, support hot-plug (swap an NVMe/accelerator without reboot), and report errors. In some designs the switch fabric is reconfigurable (lanes reassigned between hosts).
Errors and AER (the RAS hook for Phase 08): PCIe Advanced Error Reporting classifies errors as correctable (recovered by the link layer — but a rising rate predicts failure) vs uncorrectable (fatal/non-fatal — data integrity at risk). A degraded link — negotiated down from x16 to x4, or Gen5 to Gen1 — is the silent killer: the device works and reports "healthy," but bandwidth collapsed, so an accelerator mysteriously runs at a fraction of speed. Detecting link width/speed below capability is a high-value diagnostic (Lab 02).
Chapter 7: Reading PCIe Topology on Linux
The concrete skills (Lab 02 makes them software):
lspci: lists PCIe devices.lspci -tvshows the tree;lspci -vvvshows capabilities including LnkCap (what the link can do) vs LnkSta (what it negotiated). The diagnostic gold isLnkStawidth/speed <LnkCap→ degraded link.lspci -mm -nngives machine-parseable output with vendor/device IDs.- sysfs:
/sys/bus/pci/devices/<BDF>/exposescurrent_link_speed,current_link_width,max_link_speed,max_link_width,aer_dev_correctable,aer_dev_fatal, andnuma_node. This is what your monitoring code reads (no shelling out). - NUMA:
numa_nodetells you which CPU socket a device is "near." Placing a workload's GPU and NIC on the same NUMA node avoids cross-socket PCIe hops — a real perf lever and a scheduling input (Phase 06).
A rack manager that maps topology can answer: "are all 8 accelerators at full x16 Gen5?", "which NIC is P2P-adjacent to which GPU?", "is this node's slow training a degraded-link problem?" — questions that otherwise take an engineer hours on-site.
Chapter 8: Modeling and Safety
Modeling: each device class slots into the Phase 01 inventory graph — BMCs manage
nodes, PDUs power them, CDUs cool them, PCIe switches connect components — and their
telemetry attaches to components with thresholds (Phase 03 unified Reading). The driver
abstraction (Phase 03 Ch. 8) extends to all four: a DeviceDriver per class, one model
above.
Safety — this phase is the first where your software causes physical, hard-to-undo effects: powering off a node mid-job, switching a PDU outlet (kills whatever's on it), acting on a leak. The disciplines:
- Dry-run by default for mutating operations; require explicit confirmation (
--yes) for destructive ones (Phase 02 Lab 02 pattern). Lab 01 enforces this. - Interlocks: don't allow a power-on if cooling is failed; don't switch an outlet feeding a node that's mid-firmware-update.
- Blast-radius awareness (Phase 01 Ch. 7): "switch off outlet 3" might drop a whole shelf — show the operator the blast radius before acting.
- Idempotency & confirmation of effect: after a power action, read back the state to confirm it happened (don't trust the request succeeded).
- Audit: every physical action is logged with who/what/when (Phase 07/12).
The leak case is the archetype: software that can act on the physical world must be more careful, not less, than software that only moves bytes.
Lab Walkthrough Guidance
Order: Lab 01 (BMC) → Lab 02 (PDU/CDU/PCIe). The BMC is the control surface; the others are the power/cooling/fabric truth around it.
Lab 01 (BMC toolkit):
python3 solution.py— runs against the bundled BMC mock; read the power state machine.- Find the dry-run/confirm guard on every mutating call — this is the safety discipline.
- Trace the virtual-media flow: mount ISO → set next-boot → power-cycle → eject.
- Extensions: SoL capture; power-on→POST-watch; point at OpenBMC
sushy-emulator.
Lab 02 (PDU/CDU sim + PCIe mapper):
python3 solution.py— read the PDU draw aggregation and the CDU telemetry with leak/ΔT alarms.- See the PCIe tree built from sample
lspcioutput, with a degraded x16→x4 link flagged. - Run it on your own machine: pipe real
lspci -mm -nnin and watch it build your topology. - Extensions: read
/sys/bus/pci/deviceslink state and AER counts; feed PDU draw into the Phase 01 calculator as live data; emit Prometheus metrics.
Phase capstone question: "An accelerator node passes all health checks but trains 4× slow.
Walk me from symptom to root cause using this phase's tools." (Answer: check PCIe LnkSta
vs LnkCap for a degraded link / check NUMA adjacency of GPU+NIC / check CDU ΔT for thermal
throttling / check PDU draw for power capping — a degraded x16→x4 link or thermal throttle is
the usual culprit, and your topology mapper + telemetry find it in minutes.)
Success Criteria
You're done with this phase when — without notes:
- You can name the BMC's host interfaces (PECI/I²C/eSPI/NC-SI) and the sensor→Redfish chain (Ch. 1–2)
- You can describe OpenBMC's D-Bus/phosphor/bmcweb architecture at a high level (Ch. 2)
- You can explain the virtual-media remote-reinstall flow (Ch. 3)
- You can distinguish PDU types and per-outlet vs per-phase metering (Ch. 4)
- You can list CDU telemetry and the signature of each cooling failure mode, incl. leak handling (Ch. 5)
- You can explain PCIe lanes/gens/topology/BDF and why a degraded link is a silent killer (Ch. 6–7)
-
You can read
LnkCapvsLnkStaand sysfs link state to find a degraded link (Ch. 7) - You can state the safety disciplines for physical actions (dry-run, interlocks, read-back) (Ch. 8)
Interview Q&A
Q1: What is a BMC physically, and how does it read a CPU temperature you then see in Redfish?
A: The BMC is an independent ARM SoC (often an ASPeed AST2600) on the motherboard with its
own RAM, flash, NIC, and standby power, running its own firmware (frequently OpenBMC). It
reads the CPU temperature over PECI (the Intel telemetry interface) — or board sensors
over I²C/PMBus — and exposes it through its stack: on OpenBMC, the kernel hwmon driver →
phosphor-hwmon → a D-Bus sensor object → bmcweb renders it into the Redfish Thermal
resource. So "GET the temperature over Redfish" is the end of a chain that starts at PECI.
Knowing that chain is how I debug a wrong/missing sensor: is it the sensor, the I²C bus, the
hwmon driver, the D-Bus object, or the Redfish mapping?
Q2: How do you reinstall an OS on a server you can't physically touch? A: Out-of-band via the BMC's virtual media. Over Redfish I mount the installer ISO from a network share as a virtual CD/USB on the BMC, set the next boot device to that virtual media (a one-time boot override), and power-cycle the host through the BMC. The host boots the installer; I watch progress over Serial-over-LAN, and when done I eject the media and let it boot from disk. This works even if the host has no OS or a broken one — that's the whole point of OOB. It's also the rescue path behind PXE provisioning in Phase 05.
Q3: A liquid-cooled rack's cooling is degrading. What telemetry warns you before an outage, and how should software respond to a leak? A: I watch the CDU: ΔT (return − supply) rising at constant IT load is the classic early warning of reduced flow or fouling — heat removed is ∝ flow × ΔT, so if load is flat and ΔT climbs, flow is dropping. I also watch flow (a failing pump), pressure (blockage/leak), and pump status (N+1 — losing the spare is critical). For a leak, software treats it as safety-critical, like a fire alarm: highest-priority alarm immediately, and per policy/ interlock trigger an emergency response — reduce heat load via power capping and/or a controlled shutdown or valve action — because a leak near live electronics is a hazard and can destroy hardware. The principle is that software which can act on the physical world must be more careful, with interlocks and confirmed effect, not less.
Q4: An accelerator is "healthy" but runs at a fraction of its speed. How could PCIe be the cause, and how do you find it?
A: The PCIe link likely negotiated down — a degraded link, e.g., x16→x4 or Gen5→Gen1 —
which collapses bandwidth while the device still enumerates and reports healthy, so it's
silent. I find it by comparing LnkSta (negotiated width/speed) against LnkCap (what
the link can do): in lspci -vvv or sysfs (current_link_width/current_link_speed vs
max_link_width/max_link_speed). If they don't match, that's the smoking gun (often a
reseat/cable/connector or a marginal lane). I'd also check NUMA adjacency — if the GPU and its
NIC are on different sockets, cross-socket PCIe hops hurt — and PCIe AER correctable-error
counts, since a rising rate precedes hard failures. My topology mapper automates exactly this
check across all devices.
Q5: What's the difference between a metered and a switched PDU, and why do you care about per-outlet data? A: A metered PDU reports power draw (per-phase and/or per-outlet); a switched PDU can turn outlets on/off remotely; metered+switched does both, which is the AI-rack norm. I care about per-outlet metering because it's the ground truth for the power budget (Phase 01) — nameplate is a guess, the outlet is reality — and it lets me attribute power to specific devices/tenants and spot anomalies. Switching matters operationally: if a device and its BMC are both wedged, a switched outlet is my last-resort hard power-cycle. But outlet switching is physical and disruptive, so I guard it with dry-run, confirmation, blast-radius display, and read-back.
Q6 (Staff): You're integrating a new accelerator platform with an ODM. What hardware-interface issues do you anticipate and how do you de-risk them? A: I expect variance and gaps: incomplete/incorrect Redfish sensor mappings (the sensor→hwmon→D-Bus→bmcweb chain has bugs), PDU/CDU protocol differences (Modbus vs SNMP vs Redfish), PCIe topology surprises (lane allocation, degraded links under load, hot-plug quirks), and firmware-version-dependent behavior. To de-risk: agree a precise interface spec and software EBOM up front (Phase 12) — exact sensor list, Redfish schema coverage, OEM extensions, error semantics; build a conformance harness (Phase 11) that runs against their emulator/early hardware in CI so deviations are caught before deployment; instrument PCIe link state and AER from day one so degraded-link/thermal issues are visible during bring-up; and establish a tight defect-feedback loop with the ODM (Pegatron and peers), including upstreaming OpenBMC fixes where applicable. The abstraction layer (one model, per- class drivers) means their quirks stay contained while we drive them toward conformance.
References
- OpenBMC project & architecture (D-Bus, phosphor, bmcweb) — https://github.com/openbmc/openbmc , https://github.com/openbmc/docs
- ASPeed AST2600 BMC SoC overview — vendor datasheet / OpenBMC hardware docs
- DMTF Redfish PDU/Outlet & CoolingUnit/CoolingLoop schemas — https://redfish.dmtf.org/schemas/
- PCI-SIG — PCI Express Base Specification (lanes, links, AER); link speeds by generation
- Linux PCIe:
lspci(8), sysfs PCI docs (/sys/bus/pci/devices), PCIe AER kernel docs — https://docs.kernel.org/PCI/ - Open Compute Project — Open Rack & power/busbar design — https://www.opencompute.org/
- Modbus (for CDUs) — https://modbus.org/specs.php
- Cross-track: Head of SWE — GPU, Phase 04 (Linux drivers/containers) — the driver side of these devices
🛸 Hitchhiker's Guide — Phase 04: Hardware Interfacing
Read this if: you can call Redfish/IPMI (Phase 03) but the devices behind them — BMC, PDU, CDU, PCIe switch — are still abstractions. This is the compressed device tour.
0. The 30-second mental model
Four device classes you manage: the BMC (control brain), the PDU (power truth), the CDU (cooling truth + a safety system), and the PCIe fabric (how everything connects and where silent perf bugs hide). Each is a managed device with telemetry and a control surface; each slots into the Phase 01 graph and the Phase 03 driver abstraction. One rule that earns respect: physical actions need guards (dry-run, interlocks, read-back).
1. BMC internals cheat sheet
Host CPU --PECI--> BMC (ARM SoC, standby power, OpenBMC)
DIMMs/VRM --I²C/PMBus--> BMC --renders--> Redfish/IPMI
FRU EEPROM --I²C--> BMC (inventory)
Host OS --KCS/eSPI--> BMC (in-band IPMI)
Shared NIC --NC-SI--> BMC (mgmt traffic)
OpenBMC chain for a sensor: hwmon → phosphor-hwmon → D-Bus → bmcweb → Redfish JSON. Debug a bad sensor by walking that chain.
2. BMC power state machine (don't confuse these)
On · ForceOff (yank) · GracefulShutdown (asks OS — fails silently if host hung) ·
ForceRestart · GracefulRestart · PowerCycle. Robust tooling: try graceful → wait →
escalate to force → read back state to confirm.
3. The killer BMC feature: virtual media
Mount ISO over Redfish → set next-boot = virtual CD → power-cycle → host boots installer → watch over SoL → eject. This is how you reinstall a server you can't touch. Backbone of Phase 05 provisioning's rescue path.
4. PDU in one line
Metered = reads draw (per-outlet truth for the Phase 01 budget); Switched = toggles outlets (last-resort hard power-cycle); read per-phase too (balance). Switching = physical = guard it.
5. CDU: the telemetry and what it means
| Signal | Healthy | What a bad value means |
|---|---|---|
| ΔT (return−supply) | stable for the load | rising at constant load → flow loss/fouling (early warning) |
| flow (L/min) | steady | drop → failing pump / blockage |
| pressure | in range | drop → leak; spike → blockage |
| pump status | N+1 | lost spare → critical |
| leak sensor | dry | safety-critical: alarm + emergency action |
Treat leak like a fire alarm in code: top priority, fail-safe, interlock the power-on.
6. PCIe in five facts
- lane = 1 serial pair; link = x1/x4/x8/x16; Gen5 ≈ 4 GB/s/lane → Gen5 x16 ≈ 64 GB/s.
- tree: Root Complex → switches → endpoints (accelerators/NICs/NVMe). Switches enable P2P (GPUDirect).
- BDF =
Bus:Device.Function(0000:3b:00.0) — the device coordinate. - degraded link =
LnkSta<LnkCap(x16→x4 or Gen5→Gen1): device "healthy", bandwidth collapsed → the silent perf killer. Check it always. - AER: correctable (recovered, but rising rate = pre-failure) vs uncorrectable (fatal).
7. Reading PCIe on Linux (the commands)
lspci -tv # the tree
lspci -vvv | grep -A2 LnkSta # negotiated vs capable width/speed
cat /sys/bus/pci/devices/<BDF>/current_link_width # vs max_link_width
cat /sys/bus/pci/devices/<BDF>/numa_node # GPU/NIC adjacency
cat /sys/bus/pci/devices/<BDF>/aer_dev_correctable # error counts
8. Safety disciplines for physical actions
dry-run by default · --yes for destructive · interlocks (no power-on if cooling failed)
· show blast radius before acting · read back to confirm effect · audit who/what/when.
9. The "done this before" tells
"Is that link at full width?" · "What's the ΔT trend?" · "Did you read back the power state?" · "Which NUMA node is the NIC on?" · "Is the leak interlock wired?" · "Per-outlet or per-phase?"
10. How this phase pays off later
PCIe AER + link state → Phase 08 RAS/diagnostics. PDU/CDU telemetry → Phase 07 dashboards and Phase 01 live budgets. Safety/guards → Phase 10 reliability. Device drivers → Phase 13 capstone.
Lab 01 — BMC Power Control, Sensors & Virtual Media
Phase: 04 — Hardware Interfacing | Difficulty: ⭐⭐⭐☆☆ | Time: 3–5 hours Language: Python 3 (stdlib only) | Hardware: none (BMC mock)
Concept primer:
../WARMUP.mdCh. 1–3, Ch. 8 (safety),../HITCHHIKERS-GUIDE.md§1–§3, §8.
Run
python3 solution.py # sensors, safe power-off (incl. hung-host escalation), vmedia reinstall
0. The mission
Build the toolkit behind "manage a server you can't physically touch": read sensors, power it safely, and reinstall its OS over the network with virtual media — with a dry-run/confirmation guard on every mutating operation. The BMC mock makes it runnable; the toolkit logic is the real pattern (swap in a Phase 03 Redfish/IPMI driver).
1. The three things to study
- The safe power-off:
GracefulShutdown→ if the host is hung it silently no-ops → detect that (read back) → escalate toForceOff→ confirm. This graceful→escalate→verify pattern is the difference between a toy and a tool (WARMUP Ch. 3). - The virtual-media lifecycle: mount ISO → set next-boot to virtual CD → power-cycle → (installer runs, watched over SoL) → eject → restore boot-to-disk. This is remote provisioning's rescue path (Phase 05).
- The guard:
_guard()records intent (audit), honorsdry_run(no mutation), and refuses destructive ops withoutconfirm. Physical actions demand this (WARMUP Ch. 8).
2. Extensions
- Point it at OpenBMC's
sushy-emulator(real Redfish): implementset_power/virtual-media over Redfish actions; the toolkit stays the same. - Add SoL capture: stream the host serial console during the reinstall and assert you see the bootloader.
- Add a power-on → POST-watch flow: power on, then poll for the host reaching a ready state (mirrors Phase 02 readiness).
- Add an interlock: refuse
power onif a (Phase 04 Lab 02) CDU leak/critical signal is active.
3. What this lab proves
You can implement OOB server management correctly and safely — the operations the JD's "remote diagnostics," "operational support of deployed systems," and "firmware updates" all rest on. "Reinstall a server you can't touch" becomes a precise answer: virtual media + boot override + power-cycle + SoL, guarded and verified.
Lab 02 — PDU/CDU Telemetry Simulator + PCIe Topology Mapper
Phase: 04 — Hardware Interfacing | Difficulty: ⭐⭐⭐⭐☆ | Time: 4–6 hours
Language: Python 3 (stdlib only) | Hardware: none (your own lspci optional)
Concept primer:
../WARMUP.mdCh. 4 (PDU), Ch. 5 (CDU), Ch. 6–7 (PCIe),../HITCHHIKERS-GUIDE.md§4–§7.
Run
python3 solution.py # PDU + CDU telemetry + PCIe topology demo
lspci -mm -nn | python3 solution.py --lspci # parse YOUR machine's PCIe devices (Linux)
0. The mission
Three tools that turn the power/cooling/fabric layer into software: a PDU meter (feeds the Phase 01 budget), a CDU monitor that alarms on the pre-failure signals (ΔT rising, low flow, lost pump) and on a safety-critical leak, and a PCIe topology mapper that finds the silent accelerator-perf killers — degraded links and AER errors.
1. The three lessons
- PDU: per-outlet draw → rack total → compare to the 80%-derated capacity. This is the measured truth nameplate can't give you (WARMUP Ch. 4; ties to Phase 01 Lab 02).
- CDU:
ΔT = return − supply; ΔT rising at constant load → flow/fouling warning (the early signal), low flow → critical, leak → dominate-everything safety alarm (WARMUP Ch. 5). Read the three samples (healthy/degrading/leaking) and the alarms each raises. - PCIe: a device is degraded when
LnkSta < LnkCap(negotiated width/speed below capability). The demo fabric has Accelerator 5 at x4 instead of x16 — 16 GB/s where it should be 64 — and Accelerator 2 with high AER correctable errors. Both "look healthy" but aren't (WARMUP Ch. 6–7).
2. The output to internalize
The PCIe tree prints with a <-- DEGRADED flag showing the bandwidth you're actually
getting vs capable, and a <-- AER flag for error-prone links. This is the tool that turns
"the accelerator is mysteriously slow" into a 30-second diagnosis.
3. Extensions
- Read your real machine's link state: parse
/sys/bus/pci/devices/<BDF>/current_link_width/max_link_width(and_speed) to detect degraded links on actual hardware, andaer_dev_correctable/aer_dev_fatalfor errors. - NUMA adjacency: read
numa_nodeand report GPU/NIC pairs that are cross-socket (a scheduling input for Phase 06). - Feed Phase 01: pipe PDU draw into the Phase 01 budget calculator as live "measured" data and turn it into a headroom monitor.
- Emit Prometheus: render PDU/CDU/PCIe metrics in exposition format
(
cdu_delta_t_celsius,pcie_link_width{bdf=...}) — the Phase 07 bridge. - Build the full
lspci -tparser: infer parent/child from bus numbers to construct the real tree from your machine.
4. Common pitfalls
- Trusting "healthy" — a degraded x16→x4 link and a thermally-throttled accelerator both report healthy. Check link state and ΔT explicitly.
- Treating leak like any alarm — it's safety-critical; it must dominate and (per policy) trigger emergency action.
- Nameplate budgeting — use measured PDU draw, not nameplate (Phase 01).
- Ignoring correctable AER — correctable errors are "recovered," but a rising rate predicts a hard failure (Phase 08 RAS).
5. What this lab proves
You can turn the physical power/cooling/fabric layer into actionable software signals and catch the failures that are invisible to naive health checks. "An accelerator passes health checks but is 4× slow — find it" becomes a confident walk: check link width/speed, ΔT, and power capping — the exact tools you just built. These feed Phase 07 dashboards, Phase 08 RAS, and the Phase 13 capstone.
Phase 05 — Provisioning & Configuration Management
Difficulty: ⭐⭐⭐⭐☆ | Estimated Time: 2 weeks Roles supported: Rack Management SWE (Senior/Staff), Infra/Platform SWE, SRE Hardware needed: none for the core labs (simulators + real config files); a VM/QEMU for the optional PXE extension
Why This Phase Exists
A rack arrives from the ODM as bare metal. Getting it from "powered on" to "running tenant workloads" is provisioning, and the JD asks for it directly: "Implement, test, and maintain software solutions for rack provisioning" and "Integrate rack management software with orchestration platforms (e.g., Kubernetes, Terraform, Foreman)" plus "Familiarity with Foreman, Ansible, Chef, and Puppet for provisioning and configuration."
This phase covers the two halves of turning hardware into a usable node:
- Bare-metal provisioning — the PXE/iPXE boot chain that installs an OS onto a diskless machine over the network (DHCP → TFTP/HTTP → bootloader → kernel → automated installer), orchestrated by tools like Foreman.
- Configuration management — bringing a freshly-installed node to a declared desired state (packages, drivers, network, services) idempotently, with Ansible/Chef/ Puppet, plus Terraform for infrastructure-as-code.
The unifying idea is declared vs discovered state and convergence (Phase 01 Ch. 10): you declare what a rack should be; the system makes reality match, repeatably. You'll build a provisioning state machine and a config-convergence engine, and write the real iPXE/ kickstart/Ansible/Terraform artifacts.
Concepts
Bare-metal provisioning
- The boot chain: PXE vs iPXE; DHCP options (
next-server/66,filename/67, UEFI vs BIOS, arch detection); TFTP vs HTTP boot; the bootloader (pxelinux/iPXE/GRUB); kernel + initrd - Automated install: kickstart (RHEL), preseed/autoinstall (Debian/Ubuntu), cloud-init (first-boot config); unattended OS install
- UEFI HTTP boot and the interaction with Secure Boot (Phase 09) — signed shim/bootloader
- The discovery/inventory step (a "discovery image" that reports hardware before install)
- Image-based (golden image / immutable) vs config-based provisioning; tradeoffs
Foreman (the provisioning lifecycle manager)
- Host groups, provisioning templates, the discovery plugin, smart proxies (DHCP/TFTP/DNS), Katello for content
- Where Foreman sits: it orchestrates DHCP/TFTP/installer and tracks host lifecycle
Configuration management
- Declarative desired-state & idempotency (the central concept — apply twice = same result)
- Ansible: agentless, push, SSH, YAML playbooks, modules, inventory, roles; idempotent modules
- Chef (agent, pull, Ruby DSL, resources/recipes/cookbooks) and Puppet (agent, pull, declarative manifests, catalog compilation) — the model and when each fits
- Convergence, drift detection, and remediation
- Secrets in CM (Ansible Vault, etc.); ordering vs declarative dependencies
Terraform / Infrastructure-as-Code
- Providers, resources, state,
plan/apply, drift, modules; the declarative graph - Bare-metal/Foreman/Redfish providers; what IaC means for physical racks
- Idempotency and the plan-before-apply discipline
The rack onboarding flow (the synthesis)
- discover → inventory/EBOM reconcile → firmware baseline (Phase 08) → OS install → configure → validate → join orchestrator (Phase 06) → ready
- Idempotent, resumable, observable; the state machine and its failure handling
Labs
Lab 01 — PXE/iPXE Bare-Metal Provisioning Flow
| Field | Value |
|---|---|
| Goal | Build a provisioning state machine that takes a node from discovered → ready (DHCP/boot → installer → configure → validate), plus the real iPXE/DHCP/kickstart artifacts it would drive. |
| Concepts | The PXE/iPXE boot chain, DHCP options, automated install, the provisioning state machine, idempotency/resumability, validation gates. |
| Steps | 1) python3 solution.py — runs the state machine, including a failure+resume. 2) Read the boot-chain steps and the kickstart/iPXE example files. 3) Inject a failure and watch it resume from the last good state. 4) Extensions below (real QEMU PXE). |
| Stack | Python 3 stdlib (state machine) + real ipxe/kickstart/dnsmasq config examples |
| Output | A resumable provisioning state machine + ready-to-use boot artifacts. |
| How to Test | Asserts verify state transitions, that a failure resumes from the right state, and that validation gates block "ready" on bad config. |
| Talking Points | What DHCP option 67 does; BIOS vs UEFI PXE; why provisioning must be resumable/idempotent; the discovery step; secure boot's effect on PXE. |
| Resume Bullet | "Built a resumable bare-metal provisioning state machine (discover→install→configure→validate→ready) with iPXE/kickstart automation and validation gates." |
| Extensions | Run a real PXE install in QEMU with dnsmasq + an iPXE script; add UEFI HTTP boot; reconcile discovered inventory against the EBOM (Phase 01). |
Lab 02 — Ansible + Terraform Rack Onboarding
| Field | Value |
|---|---|
| Goal | Build a config-convergence engine that proves idempotency (apply twice = no-op, drift gets corrected), plus a real Ansible playbook and Terraform/Redfish example for node config. |
| Concepts | Declarative desired state, idempotency, convergence, drift detection/remediation, Ansible modules/roles, Terraform plan/apply/state. |
| Steps | 1) python3 solution.py — converge a node, re-run (no-op), introduce drift, re-converge. 2) Read the Ansible playbook + Terraform example. 3) Extensions below (run real Ansible). |
| Stack | Python 3 stdlib (convergence engine) + real ansible playbook + terraform HCL examples |
| Output | A convergence engine demonstrating idempotency + drift remediation, with real CM/IaC artifacts. |
| How to Test | Asserts verify idempotency (second apply changes nothing), drift detection, and remediation back to desired state. |
| Talking Points | Why idempotency is the core CM idea; Ansible (push/agentless) vs Chef/Puppet (pull/agent); Terraform state & drift; declared-vs-discovered as the same idea as a k8s reconcile loop. |
| Resume Bullet | "Built a configuration-convergence engine demonstrating idempotency and drift remediation; authored Ansible roles and Terraform modules for rack node onboarding." |
| Extensions | Run the playbook against a container/VM with real ansible; add Ansible Vault for secrets; write a Terraform redfish provider config; detect drift on a schedule. |
Deliverables Checklist
- You can draw the PXE/iPXE boot chain end to end (DHCP → TFTP/HTTP → bootloader → kernel → installer)
- You can explain DHCP options 66/67 and BIOS vs UEFI PXE
- Provisioning state machine is resumable: a mid-flow failure resumes from the last good state
- You can explain idempotency and show a convergence that's a no-op on the second run
- Convergence engine detects and remediates drift
- You can compare Ansible vs Chef vs Puppet (push/pull, agent/agentless, language) and pick one
- You can explain Terraform state, plan/apply, and drift
- You can connect declared-vs-discovered here to the Phase 06 reconcile loop
Interview Relevance
- "Walk me through what happens when a diskless server PXE-boots."
- "How do you install an OS on 500 servers without touching them?"
- "What is idempotency and why does it matter in configuration management?"
- "Ansible vs Puppet vs Chef — when would you choose each?"
- "What does Terraform's state file do, and what is drift?"
- "Your provisioning fails halfway on node 237 of 500. What happens?" (resumable/idempotent)
- "How do you onboard a brand-new rack from bare metal to running workloads?"
Warmup Guide — Provisioning & Configuration Management
Zero-to-expert primer for Phase 05. You need no prior ops background: this guide starts at "how does a diskless machine get an OS" and ends with you designing a resumable rack onboarding pipeline using PXE/Foreman + Ansible/Terraform, grounded in one idea: declare the desired state, then converge reality to it.
Table of Contents
- Chapter 1: The Provisioning Problem
- Chapter 2: The PXE/iPXE Boot Chain
- Chapter 3: Automated OS Install
- Chapter 4: Foreman and Lifecycle Management
- Chapter 5: Configuration Management & Idempotency
- Chapter 6: Ansible vs Chef vs Puppet
- Chapter 7: Terraform and Infrastructure-as-Code
- Chapter 8: The Rack Onboarding Pipeline
- Lab Walkthrough Guidance
- Success Criteria
- Interview Q&A
- References
Chapter 1: The Provisioning Problem
A new rack's nodes have no operating system — bare metal with empty (or wiped) disks. You cannot SSH to a machine with no OS. You cannot insert a USB stick into 500 servers in a data center hall in Riyadh from your desk. So provisioning must be remote, networked, and automated, and it builds on the OOB tools you already have (Phase 04: virtual media is one path; network boot is the scalable one).
The job: take N bare-metal nodes and, with zero physical touches, end with each running a known OS, configured to spec, validated, and joined to the orchestrator — repeatably (you do this for every rack, forever) and resumably (failures are normal at scale; node 237 of 500 will fail mid-install, and the pipeline must recover, not restart everything).
Two broad strategies (know the tradeoff):
- Image-based / immutable: lay down a complete golden disk image, identical everywhere. Fast, deterministic, easy rollback (reimage); but images are large and any change means a rebuild. Favored for fleets at scale and immutable-infra shops.
- Config-based: install a base OS, then converge it to desired state with a CM tool. Flexible, smaller artifacts, drift-correctable; but more moving parts and slower per node.
Real fleets blend them: a thin golden image + cloud-init/CM for the last mile.
Chapter 2: The PXE/iPXE Boot Chain
PXE (Preboot eXecution Environment) is firmware in the NIC that can boot a machine over the network. The chain, step by step (memorize it — it's the #1 interview question here):
- Power on → NIC PXE firmware runs (you select "network boot" in BIOS/UEFI, or it's the default boot order — and the BMC can force it once, Phase 04).
- DHCP: the NIC broadcasts a DHCP request. The DHCP server replies with an IP and two
special options: option 66 /
next-server(the TFTP/boot server address) and option 67 /filename(the boot file to fetch). UEFI vs BIOS clients send a client- arch tag so the server hands the right bootloader (this arch-detection is a common gotcha). - TFTP (or HTTP): the client downloads the bootloader named in option 67 — classic
pxelinux.0(BIOS) or a UEFI binary, or (better) iPXE. - iPXE (the modern upgrade): a richer bootloader that can boot over HTTP/HTTPS (faster, reliable, firewall-friendly vs TFTP), run scripts, and make decisions (chainloading). You often PXE-boot a tiny loader that then chainloads iPXE, which fetches an iPXE script over HTTP telling it what kernel/installer to load.
- Kernel + initrd: the bootloader downloads the installer kernel and initial ramdisk and boots them.
- Installer runs with an automated answer file (Chapter 3).
TFTP vs HTTP: TFTP is simple but slow and UDP-fragile; iPXE-over-HTTP is the modern default for speed and reliability at scale. UEFI HTTP Boot removes PXE/TFTP entirely on newer firmware — DHCP hands a URL, the firmware fetches over HTTP directly.
Secure Boot interaction (Phase 09): with Secure Boot on, the firmware will only run a
signed bootloader (the Microsoft-signed shim → signed GRUB → signed kernel chain).
Your PXE/iPXE artifacts must be signed, or provisioning fails at boot — a real gotcha when
enabling Secure Boot on a previously-working PXE setup.
Chapter 3: Automated OS Install
The installer must run unattended — no human clicking "Next." Each OS family has an answer-file format:
- Kickstart (RHEL/Rocky/Alma): a
ks.cfgspecifying disk partitioning, packages, users, network, and%postscripts. The kernel cmdline points at it (inst.ks=http://.../ks.cfg). - preseed (Debian) / autoinstall (Ubuntu Subiquity, YAML): the same idea, different syntax.
- cloud-init: not an installer but a first-boot configurator — when a node boots an OS image, cloud-init applies user-data (users, SSH keys, packages, run-commands, write files). Ubiquitous for cloud and increasingly bare metal (with the right datasource).
The pattern: the boot chain delivers kernel+initrd+answer-file; the installer partitions, installs the base OS, drops an SSH key / joins CM, reboots into the installed system. From there, configuration management (Chapter 5) takes over.
Idempotency starts here: a good answer file + post-script should be safe to re-run, and the provisioning orchestrator must track which node reached which step so a failure resumes rather than reinstalls the whole rack (Lab 01).
Chapter 4: Foreman and Lifecycle Management
Doing the above by hand-editing DHCP/TFTP configs doesn't scale. Foreman is the open- source tool that orchestrates it and tracks host lifecycle (the JD names it). What it gives you:
- Smart proxies: Foreman drives DHCP, TFTP, DNS, and the installer via proxy services near the hardware (so the control plane stays central, the services stay local — a recurring rack-software pattern).
- Host groups & provisioning templates: declare "this class of node gets this OS, this partitioning, this kickstart" once; apply to many.
- The discovery plugin: unprovisioned nodes PXE-boot a discovery image that inventories the hardware and registers it with Foreman before you install — the software form of Phase 01 commissioning (discover → reconcile against EBOM → provision).
- Katello (add-on): content/repo and lifecycle-environment management (dev/test/prod content views) — relevant to controlled OS/package rollouts.
Foreman is one option; hyperscalers often build bespoke equivalents (MaaS, Tinkerbell, or in-house). The concepts — discovery, templated provisioning, smart proxies, lifecycle states — transfer regardless of tool, which is what an interviewer probes.
Chapter 5: Configuration Management & Idempotency
Once the OS is installed, the node still needs packages, drivers (accelerator/NIC firmware hooks, Phase 08), kernel params, network config, users, services, and security hardening. Configuration management (CM) brings it to a declared desired state.
Idempotency is the central idea: you declare the end state ("package X present, service Y running, file Z with these contents"), not the steps, and applying the configuration any number of times converges to that state and is a no-op once converged. This is what makes CM safe to run continuously and what lets it correct drift (someone changed a config by hand → the next run puts it back). Lab 02 proves it: apply → no-op on re-apply → drift → re-converge.
Contrast with imperative scripts: a shell script that runs useradd bob fails the second
time (bob exists); the idempotent CM resource "user bob present" succeeds every time. That
difference is why CM exists.
Convergence + drift: the system continuously (or on-demand) compares declared vs actual (declared-vs-discovered again — Phase 01 Ch. 10) and remediates the difference. This is exactly the reconcile loop a Kubernetes operator runs (Phase 06) — CM is the same control pattern applied to OS config.
Chapter 6: Ansible vs Chef vs Puppet
The JD lists all three; know the model and the tradeoffs (a guaranteed question).
| Ansible | Puppet | Chef | |
|---|---|---|---|
| Architecture | agentless, push (SSH) | agent, pull | agent, pull |
| Language | YAML playbooks (declarative-ish) | Puppet DSL (declarative) | Ruby DSL (imperative-ish recipes) |
| Model | tasks/modules, roles | resources → catalog | resources → recipes/cookbooks |
| Strengths | no agents, fast start, great for orchestration & ad-hoc | strong declarative model, mature at scale | flexible, programmer-friendly |
| Costs | scales push to thousands w/ care; ordering is sequential | needs agents + a Puppet master; learning curve | needs agents + a Chef server; Ruby |
How to choose (the senior answer): Ansible when you want agentless simplicity, ad-hoc runs, and orchestration of multi-step flows (often the rack-provisioning default); Puppet when you want a strong declarative model enforced continuously by agents at large scale; Chef when the team wants programmatic flexibility. Many shops standardize on Ansible for its agentless model and pair it with image-based provisioning. The key insight to convey: they're all idempotent declarative convergence engines — the architecture (push vs pull, agent vs agentless) is the real differentiator, not the capability.
Chapter 7: Terraform and Infrastructure-as-Code
Terraform declares infrastructure as code: you write the desired set of resources (in HCL), and Terraform makes reality match. Core concepts:
- Providers: plugins for a platform (AWS, a bare-metal/Foreman provider, a Redfish provider that configures BMCs, a Kubernetes provider). Providers are the bridge to the real world.
- State: Terraform records what it created in a state file — the map between your declared resources and real-world IDs. State is the source of truth for what Terraform manages; it must be stored safely (remote backend, locked) and never hand-edited.
planthenapply:planshows the diff between declared and actual (create/change/ destroy) before changing anything — the discipline that prevents surprise destruction.applyexecutes it.- Drift: if reality changes outside Terraform, the next
planshows the drift; you re-apply to reconcile (declared-vs-discovered, again).
For physical racks, Terraform's role is configuring the infrastructure layer — BMC settings via the Redfish provider, network config, Foreman host definitions, then the Kubernetes/RKE2 cluster (Phase 06). It's the same declarative-convergence idea as CM, at the infra level.
CM vs IaC, briefly: IaC (Terraform) provisions/declares infrastructure (machines, networks, BMC config); CM (Ansible/Puppet/Chef) configures inside the OS. They compose: Terraform stands up the node and its infra; Ansible configures the OS; the orchestrator (Phase 06) schedules workloads.
Chapter 8: The Rack Onboarding Pipeline
Synthesize everything into the pipeline you build (Lab 01) — the software the JD's "rack provisioning" really means:
[discover] → [inventory/EBOM reconcile] → [firmware baseline] → [OS install] →
[configure] → [validate] → [join orchestrator] → [READY]
P05/P04 P01/P12 P08 P05 P05/P02 P06
Design properties that separate a real pipeline from a script:
- State machine, persisted: each node has a state; transitions are recorded so the pipeline is resumable after a crash or a node failure (node 237 resumes at its last good state, not from zero). Lab 01 implements this.
- Idempotent steps: re-running a step is safe; partial progress doesn't corrupt.
- Validation gates: a node doesn't advance to "ready" until it passes checks (firmware at baseline, sensors nominal, network reachable, accelerators enumerated at full PCIe width — Phase 04). A bad node is quarantined for RCA (Phase 10), not shipped to tenants.
- Observable: every transition emits structured logs/metrics (Phase 07) so you can watch 500 nodes onboard and spot the stuck ones.
- Reconciled against intent: the discovered hardware is checked against the EBOM (Phase 01/12); discrepancies are findings, not silent acceptance.
This pipeline is the seed of the Phase 13 capstone, and "design a bare-metal provisioning
service" is a core system-design prompt (see system-design/05).
Lab Walkthrough Guidance
Order: Lab 01 (provisioning state machine) → Lab 02 (config convergence). Provisioning gets the OS on; CM brings it to spec — the natural sequence.
Lab 01 (PXE/iPXE flow):
python3 solution.py— watch a node move discover→…→ready, then a run where a step fails and the pipeline resumes from the last good state.- Read the real iPXE script, the
dnsmasq/DHCP options, and the kickstart file — the artifacts the state machine would drive. - Extension: do a real PXE install in QEMU with
dnsmasq+ iPXE (instructions in the README).
Lab 02 (Ansible + Terraform):
python3 solution.py— converge a node; re-run (a no-op — idempotency); introduce drift; re-converge (remediation).- Read the real Ansible playbook and Terraform example.
- Extension: run the playbook against a container/VM with real
ansible.
Phase capstone question: "Design the software to onboard a 500-node fleet from bare metal, where ~3% of nodes will fail somewhere mid-flow. What are the must-have properties?" (Answer: a persisted per-node state machine that's resumable + idempotent steps + validation gates that quarantine bad nodes + EBOM reconciliation + full observability — not a big sequential script.)
Success Criteria
You're done with this phase when — without notes:
- You can recite the PXE/iPXE boot chain and explain DHCP options 66/67 and arch detection (Ch. 2)
- You can explain TFTP vs HTTP boot and Secure Boot's effect on PXE (Ch. 2)
- You can describe automated install (kickstart/cloud-init) and where CM takes over (Ch. 3)
- You can explain what Foreman/discovery/smart-proxies give you (Ch. 4)
- You can define idempotency and demonstrate a no-op re-apply and drift remediation (Ch. 5)
- You can compare Ansible/Puppet/Chef by architecture and choose one with reasons (Ch. 6)
- You can explain Terraform state, plan/apply, and drift (Ch. 7)
- You can design a resumable, validated, observable rack onboarding pipeline (Ch. 8)
Interview Q&A
Q1: Walk me through what happens when a diskless server PXE-boots.
A: On power-on the NIC's PXE firmware runs and broadcasts a DHCP request. The DHCP server
replies with an IP plus option 66 (next-server, the boot server) and option 67
(filename, the boot file), using the client's arch tag to pick BIOS vs UEFI. The client
fetches that bootloader over TFTP (or HTTP) — typically iPXE — which then fetches an iPXE
script over HTTP that tells it which installer kernel and initrd to load. It boots the
kernel with a cmdline pointing at an automated answer file (kickstart/autoinstall), the
installer partitions and lays down the base OS unattended, drops an SSH key or joins config
management, and reboots into the installed system, where CM converges it to desired state.
With Secure Boot on, the bootloader chain (shim→GRUB→kernel) must be signed or it won't boot.
Q2: How do you install an OS on 500 servers without touching them, and what happens when some fail? A: Network provisioning driven by a state machine. Each node PXE/iPXE-boots and runs an automated installer (kickstart/cloud-init) orchestrated by something like Foreman (DHCP/TFTP/ templates) or an in-house equivalent. The critical design point is failure: at 500 nodes, some will fail mid-flow, so I don't run a big sequential script — I run a persisted per-node state machine with idempotent steps, so a node that fails at "configure" resumes at "configure," not at "reinstall the rack." Bad nodes hit a validation gate (firmware baseline, sensors, PCIe link width, network) and get quarantined for RCA rather than shipped to tenants, while the rest proceed. Everything emits structured telemetry so I can watch the fleet onboard and triage the stuck ones.
Q3: What is idempotency, and why is it the core of configuration management?
A: Idempotency means applying the same configuration any number of times converges to the
same end state and is a no-op once you're there. CM is built on it because you declare the
desired state ("package present, service running, file with these contents"), not
imperative steps, so the tool can safely run continuously, correct drift (someone hand-edits
a config → the next run restores it), and be re-run after a partial failure without
corruption. The classic contrast: a script doing useradd bob fails the second time, but the
idempotent resource "user bob present" succeeds every time. It's the same control pattern as
a Kubernetes reconcile loop — declared vs discovered, continuously converged.
Q4: Ansible vs Puppet vs Chef — when would you choose each? A: They're all idempotent declarative convergence engines; the real difference is architecture. Ansible is agentless and push-based over SSH with YAML playbooks — fastest to adopt, great for orchestration and ad-hoc runs, often the rack-provisioning default. Puppet is agent-based and pull-based with a strong declarative DSL and a master compiling catalogs — excellent for continuously enforced state at large scale. Chef is agent/pull with a Ruby DSL — flexible and programmer-friendly. I'd pick Ansible when I want no agents and simple orchestration (and pair it with image-based provisioning), Puppet when I want agents continuously enforcing declared state across a big fleet, and Chef when the team values programmatic flexibility. The capability is similar; push-vs-pull and agent-vs-agentless drive the decision.
Q5: What does Terraform's state file do, and what is drift?
A: The state file is Terraform's record of what it created — the mapping between your declared
HCL resources and real-world resource IDs. It's how Terraform knows what it manages and
computes the diff on the next run. Drift is when reality changes outside Terraform (someone
edits a BMC setting by hand); the next plan detects the difference between declared and
actual, and apply reconciles it. The disciplines around state are: store it in a locked
remote backend (never local for teams), never hand-edit it, and always plan before apply
so you see create/change/destroy before it happens. It's declared-vs-discovered
convergence at the infrastructure layer.
Q6 (Staff): Design the provisioning system for continuously onboarding new racks at fleet scale. A: I'd build a control plane around a persisted state machine per node with the pipeline discover → EBOM reconcile → firmware baseline → OS install → configure → validate → join orchestrator → ready. Properties: every step idempotent and resumable (crash-safe); smart proxies near the hardware for DHCP/TFTP/HTTP/installer while the control logic stays central (so it scales across sites — relevant to a globally distributed team); a discovery image that inventories hardware and reconciles against the EBOM agreed with the ODM, raising discrepancies as findings; firmware brought to a known baseline (Phase 08) before install; image-based install (a thin golden image + cloud-init) for determinism and easy reimage-as- rollback; CM (Ansible) for the last-mile config and continuous drift correction; validation gates that quarantine nodes failing health/PCIe/firmware checks; and end-to-end observability (Phase 07) so I can watch hundreds of nodes onboard and SLO the time-to-ready. I'd expose it as a declarative API (desired rack state) so it composes with Terraform/IaC and the orchestrator. The whole thing is a convergence loop — declared rack vs discovered rack — which is the same pattern as the Phase 06 operator and is what makes it testable and maintainable.
References
- iPXE — open network bootloader, scripting, HTTP boot — https://ipxe.org/
- PXE specification & DHCP options (66/67, client-arch) — RFC 2131/4578; SYSLINUX/pxelinux docs
- Kickstart (RHEL), Ubuntu autoinstall (Subiquity), cloud-init — https://cloudinit.readthedocs.io/
- Foreman & the Discovery plugin; Katello — https://theforeman.org/ , https://theforeman.org/plugins/foreman_discovery/
- Ansible docs (modules, roles, idempotency, Vault) — https://docs.ansible.com/
- Puppet & Chef overviews (declarative resources, pull model) — https://www.puppet.com/docs , https://docs.chef.io/
- Terraform — providers, state, plan/apply; the Redfish provider — https://developer.hashicorp.com/terraform
- Tinkerbell / Canonical MAAS — alternative bare-metal provisioning stacks — https://tinkerbell.org/ , https://maas.io/
- Cross-track: Phase 06 (Kubernetes/RKE2) — where provisioning hands off
🛸 Hitchhiker's Guide — Phase 05: Provisioning & Config Management
Read this if: "PXE, Foreman, Ansible, Terraform" is a list of words. This is the compressed map — the boot chain, the one big idea, and the tool tradeoffs.
0. The 30-second mental model
Bare metal → running node has two halves: provisioning (network-boot an OS onto a diskless box: DHCP → bootloader → installer) and configuration (converge the OS to a declared state, idempotently). The one idea under all of it — PXE, Ansible, Terraform, and the Phase 06 operator alike — is declare desired state, converge reality to it. At fleet scale the must-have is a resumable per-node state machine, not a big script.
1. The PXE/iPXE boot chain (the #1 question)
power on → NIC PXE firmware
→ DHCP (gives IP + option 66 next-server + option 67 filename; arch tag = BIOS/UEFI)
→ TFTP/HTTP fetch bootloader (pxelinux / iPXE / UEFI binary)
→ iPXE script over HTTP → fetch installer kernel + initrd
→ installer runs UNATTENDED (kickstart / autoinstall / cloud-init)
→ reboot into installed OS → config management takes over
Gotchas: TFTP is slow/UDP-fragile (prefer iPXE-over-HTTP); UEFI vs BIOS needs the right bootloader (arch detection); Secure Boot requires signed shim→GRUB→kernel or it won't boot.
2. Automated install answer files
| OS | file | pointer |
|---|---|---|
| RHEL/Rocky | kickstart ks.cfg | inst.ks=http://.../ks.cfg |
| Ubuntu | autoinstall (YAML) | autoinstall ds=nocloud-net;s=http://... |
| any image | cloud-init user-data | first-boot config (users, keys, packages, runcmd) |
3. Foreman in one line
Orchestrates DHCP/TFTP/DNS/installer via smart proxies (local services, central control), with host groups + templates and a discovery image that inventories unprovisioned nodes — the software form of Phase 01 commissioning.
4. Idempotency (the core idea)
Declare end state, not steps → apply N times = same result, no-op once converged, corrects
drift. useradd bob (breaks on re-run) vs resource "user bob present" (always fine). This is
the same loop as a k8s operator and Terraform.
5. Ansible vs Puppet vs Chef
| Ansible | Puppet | Chef | |
|---|---|---|---|
| agentless, push, SSH, YAML | agent, pull, DSL | agent, pull, Ruby | |
| pick when | no agents, ad-hoc, orchestration | strong declarative @ scale | programmatic flexibility |
They're all idempotent convergence engines — architecture is the differentiator.
6. Terraform in four words
Providers · State (the map; lock it, never hand-edit) · plan (see the diff, incl. destroy) · apply (reconcile). Drift = reality diverged → next plan shows it. IaC declares infrastructure; CM configures inside the OS; they compose.
7. The onboarding pipeline (what to build)
discover → EBOM reconcile → firmware baseline → OS install → configure → validate → join orch → READY
Must-haves: persisted resumable state machine · idempotent steps · validation gates (quarantine bad nodes) · EBOM reconciliation · observability. Not a sequential script.
8. The "done this before" tells
"Is the step idempotent?" · "Does it resume from the last state?" · "BIOS or UEFI PXE?" ·
"Is the boot chain signed for Secure Boot?" · "What does the validation gate check?" ·
"Where's the Terraform state stored?" · "Did you plan before apply?"
9. How this phase pays off later
The state-machine + declared-vs-discovered pattern is the Phase 06 operator and the
Phase 13 capstone. Validation gates use Phase 04 (PCIe/sensors) and Phase 08 (firmware).
Observability is Phase 07. "Design a provisioning service" is system-design/05.
Lab 01 — PXE/iPXE Bare-Metal Provisioning Flow
Phase: 05 — Provisioning & Config Mgmt | Difficulty: ⭐⭐⭐⭐☆ | Time: 5–7 hours Language: Python 3 (state machine) + real iPXE/dnsmasq/kickstart | Hardware: none (QEMU for the optional real install)
Concept primer:
../WARMUP.mdCh. 2–3, Ch. 8,../HITCHHIKERS-GUIDE.md§1–§3, §7.
Run
python3 solution.py # the provisioning state machine: clean run, resume, quarantine
Files
| File | What it is |
|---|---|
solution.py | the orchestrator — a resumable per-node provisioning state machine |
boot.ipxe | the real iPXE script served over HTTP (kernel/initrd/autoinstall) |
dnsmasq.conf | a real DHCP+TFTP config (options 66/67, BIOS/UEFI arch detection, iPXE chainload) |
ks.cfg | a real kickstart for unattended OS install + handoff to config management |
0. The mission
Build the software the JD's "rack provisioning" means — not a script, a resumable state
machine that takes bare metal → ready, with a validation gate that quarantines bad
nodes — and pair it with the real boot artifacts (boot.ipxe, dnsmasq.conf, ks.cfg) it
drives. The boot chain and the orchestrator are the two halves of provisioning.
1. What the state machine demonstrates
- Resumability: Node B hits an injected failure at
OS_INSTALLEDand resumes fromFIRMWARE_BASELINED— it never re-runs the earlier steps (the self-test assertsINVENTORIEDhappened exactly once). At 500 nodes, this is the difference between a 3% failure rate being a non-event and being a re-provision-the-whole-rack disaster. - Idempotency: re-provisioning an already-READY node is a no-op.
- Validation gate: Node C's discovered inventory (3 accelerators) mismatches the EBOM (4) → it's QUARANTINED with a reason, not shipped to tenants. This is Phase 01 commissioning enforced in code.
2. The boot artifacts (read them)
dnsmasq.conf shows the two-stage PXE trick (PXE → iPXE binary → HTTP iPXE script) and
arch detection; boot.ipxe shows HTTP boot with a serial console so the BMC's SoL can watch
(Phase 04); ks.cfg shows unattended install ending with an SSH-key drop and a callback to
the orchestrator ("os-installed") — the seam between the boot chain and the state machine.
3. Extensions (do them)
- A real PXE install in QEMU. Install
dnsmasq, grabipxe.efi/undionly.kpxe, serve the artifacts over a tiny HTTP server, and boot a diskless QEMU VM (-boot n,-netdev). Watch it DHCP → chainload iPXE → install Ubuntu unattended. - UEFI HTTP Boot: skip TFTP entirely — DHCP hands a URL, the firmware fetches over HTTP.
- EBOM reconciliation: wire the
INVENTORIEDstep to the Phase 01 inventory model and produce a real declared-vs-discovered diff. - Persist state to disk/DB: make the state machine truly crash-safe (process dies → restart → resumes), the way a production provisioner stores per-node state.
- Secure Boot: make the boot chain use signed shim→GRUB→kernel and discuss what breaks.
4. What this lab proves
You can design the provisioning control plane, not just paste a kickstart. "Onboard 500
nodes where 3% fail mid-flow" becomes a confident answer — resumable state machine, idempotent
steps, validation gates, EBOM reconciliation — and you can show the real iPXE/DHCP/kickstart
that the control plane drives. This is the seed of system-design/05 and the Phase 13 capstone.
Lab 02 — Ansible + Terraform Rack Onboarding
Phase: 05 — Provisioning & Config Mgmt | Difficulty: ⭐⭐⭐☆☆ | Time: 4–6 hours Language: Python 3 (engine) + real Ansible/Terraform | Hardware: none (a VM/container for real Ansible)
Concept primer:
../WARMUP.mdCh. 5–7,../HITCHHIKERS-GUIDE.md§4–§6.
Run
python3 solution.py # converge -> no-op -> drift -> remediate (idempotency proven)
Files
| File | What it is |
|---|---|
solution.py | the convergence engine — idempotency + drift in ~120 lines |
playbook.yml | a real, idempotent Ansible playbook for a compute node |
main.tf | a real Terraform + Redfish example (BMC infra config) |
0. The mission
Internalize the one idea under every CM/IaC tool — declare desired state, converge, be a
no-op once converged, correct drift — by building the engine and reading the real
artifacts (playbook.yml, main.tf) that do it against real systems.
1. What the engine proves
A "resource" is (check, apply): check if already in the desired state; apply only if not.
The run shows the three behaviors that define configuration management:
- First converge changes everything (fresh node).
- Second converge is a no-op — idempotency.
- Drift (sshd stopped, config hand-edited) is detected and remediated back to desired.
This is literally the same control loop as Terraform (plan/apply/drift) and a
Kubernetes operator (Phase 06) — declared vs discovered, converged.
2. The real artifacts
playbook.yml: every Ansible task uses a declarativestate:so re-running reports "ok" (no change). Note--checkfor a dry-run (plan), handlers for restart-on-change, and that secrets never appear in the file.main.tf: Terraform manages the infrastructure layer (BMC boot order via Redfish) while Ansible configures inside the OS — they compose. Noteplanbeforeapply,sensitivevariables,ssl_insecure = false(validate the cert — Phase 03 Ch. 7), and that state is the locked source of truth.
3. Extensions (do them)
- Run the playbook for real against a container/VM:
ansible-playbook -i inventory.ini playbook.yml, then run it again and confirm the second run reports zero changes (idempotency on real hardware). Hand-edit/etc/rack/node.confand re-run to watch drift correction. - Ansible Vault: encrypt a secret and reference it, so credentials never sit in plaintext.
- A Terraform
plandiscipline: change a value, runterraform plan, and read the create/change/destroy diff before applying. - Scheduled drift detection: run the engine on a timer and alert (Phase 07) when drift is found — the basis of continuous compliance.
- Compare pull-based: sketch how Puppet/Chef agents would do the same convergence on a pull schedule, and the tradeoff vs Ansible's push.
4. What this lab proves
You understand configuration management at the level that lets you reason about any tool, not just recite one. "What is idempotency / Ansible vs Puppet / Terraform state and drift" become precise answers backed by an engine you built — and you can connect it to the Phase 06 operator and the Phase 13 capstone as the same convergence pattern.
Phase 06 — Orchestration: Kubernetes & RKE2
Difficulty: ⭐⭐⭐⭐☆ | Estimated Time: 2 weeks
Roles supported: Rack Management SWE (Senior/Staff), Platform/Infra SWE, SRE
Hardware needed: none for the core labs (simulators + real manifests); optional kind/k3d for the extensions
Why This Phase Exists
The JD: "Integrate rack management software with orchestration platforms (e.g., Kubernetes, Terraform, Foreman)" · "Implement... software solutions for... workload deployment" · "Knowledge of AI workload orchestration tools (e.g., K8s, RKE2)." Once a rack's nodes are provisioned (Phase 05), the orchestrator is what schedules tenant AI workloads onto them and manages their lifecycle. Rack-management software both runs on and integrates with Kubernetes — and the operator pattern (a controller running a reconcile loop) is the modern way to encode rack-management logic into the cluster itself.
The deep idea you take from this phase is the reconcile loop: a controller continuously
drives actual state toward declared (CRD) state — the same declared-vs-discovered
convergence you met in inventory (Phase 01), provisioning (Phase 05), and config management
(Phase 05). You'll build a RackNode operator that reconciles, and walk the device-plugin +
scheduling path that gets accelerators to pods.
Concepts
Kubernetes core (enough to be dangerous, then deep where it matters)
- The control plane: API server, etcd, scheduler, controller-manager; the declarative API
- Nodes, kubelet, the container runtime (containerd/CRI), pods, the pod lifecycle
- Workload objects: Deployment/ReplicaSet, DaemonSet (the rack-agent pattern), StatefulSet, Job
- Scheduling: requests/limits, node selectors, taints/tolerations, affinity/anti-affinity, topology spread
- Services, the networking model (CNI), namespaces, RBAC, resource quotas (multi-tenancy)
The operator / controller pattern (the heart of the phase)
- CRDs (CustomResourceDefinitions) — extend the API with your own objects (a
RackNode) - The reconcile loop: observe → diff (desired vs actual) → act → requeue; level-triggered, idempotent
- Controllers, informers/watches, the work queue, status vs spec, finalizers, owner references
- Why operators encode operational knowledge as software (the "automated SRE" idea)
Accelerators in Kubernetes
- The device plugin API — how a node advertises accelerators as schedulable resources
- Node Feature Discovery; extended resources; the GPU/accelerator operator pattern
- Topology-aware scheduling (NUMA/PCIe adjacency — Phase 04); MIG-style partitioning concepts
RKE2 and production distros
- What RKE2 is (Rancher's secure, hardened, CIS-compliant k8s distro) and why hardened distros exist
- RKE2 vs upstream/k3s/OpenShift; air-gapped install; embedded etcd; the security posture
- Cluster lifecycle: node join/cordon/drain/upgrade; the relationship to provisioning (Phase 05)
Multi-tenancy & integration
- Namespaces + RBAC + quotas + network policy as tenant boundaries (pairs with Phase 09)
- How rack-management software integrates: as a DaemonSet agent, an operator, and an API consumer
Labs
Lab 01 — A RackNode Kubernetes Operator (reconcile loop)
| Field | Value |
|---|---|
| Goal | Build the reconcile loop at the heart of every operator: a controller that drives RackNode objects from desired (spec) to actual (status), idempotently and level-triggered, handling drift and failures. |
| Concepts | CRDs, spec vs status, the observe→diff→act→requeue loop, idempotency, level- vs edge-triggered, finalizers, requeue/backoff. |
| Steps | 1) python3 solution.py — a controller reconciles RackNodes (provision, drift, delete w/ finalizer). 2) Read the reconcile function. 3) Inject drift and a failed action; watch it requeue and converge. 4) Extensions (real kopf/CRD). |
| Stack | Python 3 stdlib (operator simulator) + a real CRD YAML |
| Output | A working reconcile loop demonstrating convergence, requeue-on-failure, and finalizer-based cleanup. |
| How to Test | Asserts verify convergence to desired state, idempotent re-reconcile (no-op), drift correction, requeue on transient failure, and finalizer cleanup ordering. |
| Talking Points | Why level-triggered beats edge-triggered; spec vs status; what a finalizer is for; why the loop must be idempotent; how this is the same loop as Phase 05 CM. |
| Resume Bullet | "Built a Kubernetes-style operator reconcile loop for a RackNode CRD (desired→actual convergence, requeue/backoff, finalizer cleanup); the control pattern behind rack lifecycle automation." |
| Extensions | Reimplement with kopf against a real kind cluster + a CRD; add a status condition history; add owner references/GC. |
Lab 02 — Device Plugin & Accelerator Scheduling
| Field | Value |
|---|---|
| Goal | Model how a node advertises accelerators and how the scheduler places pods on them, including taints/tolerations and topology (PCIe/NUMA) awareness from Phase 04. |
| Concepts | Device plugin API, extended resources, requests/limits, taints/tolerations, affinity, topology-aware placement, bin-packing vs spread. |
| Steps | 1) python3 solution.py — a mini scheduler places pods requesting accelerators across nodes, respecting capacity, taints, and topology. 2) Read the placement logic. 3) Over-subscribe and watch pods go Pending. 4) Read the real DaemonSet/device-plugin manifests. 5) Extensions. |
| Stack | Python 3 stdlib (scheduler sim) + real k8s manifests |
| Output | A mini accelerator scheduler honoring capacity, taints, and topology; real device-plugin/DaemonSet YAML. |
| How to Test | Asserts verify capacity enforcement, taint/toleration filtering, topology-preferred placement, and Pending when oversubscribed. |
| Talking Points | How device plugins expose hardware; why a pod goes Pending; topology-aware placement (GPU+NIC same NUMA); taints for dedicated/maintenance nodes. |
| Resume Bullet | "Modeled the Kubernetes device-plugin + scheduling path for accelerators with topology-aware (NUMA/PCIe) placement and taint/toleration filtering." |
| Extensions | Run NVIDIA's device plugin on a real GPU node; add MIG-style fractional resources; add gang scheduling for multi-accelerator jobs. |
Deliverables Checklist
- You can describe the k8s control plane (API server, etcd, scheduler, controllers) and the declarative model
- Operator reconcile loop converges desired→actual, is idempotent, and requeues on failure
- You can explain level- vs edge-triggered and why operators are level-triggered
- You can explain spec vs status and what a finalizer does
- Scheduler sim enforces capacity, taints/tolerations, and topology preference
- You can explain the device-plugin path from hardware to a schedulable resource
- You can say what RKE2 is and why a hardened distro exists; node drain/upgrade lifecycle
- You can connect the reconcile loop to Phase 05 CM and Phase 01 declared-vs-discovered
Interview Relevance
- "What happens when you
kubectl applya Deployment?" (control plane → scheduler → kubelet) - "Explain the operator pattern and the reconcile loop. Why level-triggered?"
- "What's the difference between spec and status? What is a finalizer for?"
- "How does a GPU/accelerator get scheduled to a pod?" (device plugin → extended resource)
- "Why might a pod be stuck Pending?" (capacity, taints, affinity, no matching node)
- "What is RKE2 and why use it over upstream k8s?" (hardened, CIS, air-gap)
- "How would you safely upgrade k8s on a 100-node rack?" (cordon/drain/upgrade, canary)
Warmup Guide — Orchestration: Kubernetes & RKE2
Zero-to-expert primer for Phase 06. You need no Kubernetes background: this guide starts at "what does an orchestrator do" and ends with you building an operator reconcile loop and reasoning about accelerator scheduling. The through-line is the reconcile loop — declared-vs-discovered convergence, the same idea from Phases 01 and 05.
Table of Contents
- Chapter 1: Why Orchestration, and Where Rack Software Fits
- Chapter 2: The Kubernetes Control Plane
- Chapter 3: Workloads, Nodes, and the DaemonSet Agent
- Chapter 4: The Operator Pattern and the Reconcile Loop
- Chapter 5: Scheduling — How a Pod Lands on a Node
- Chapter 6: Accelerators — Device Plugins & Topology
- Chapter 7: RKE2 and Production Distros
- Chapter 8: Multi-Tenancy & Cluster Lifecycle
- Lab Walkthrough Guidance
- Success Criteria
- Interview Q&A
- References
Chapter 1: Why Orchestration, and Where Rack Software Fits
Once nodes are provisioned (Phase 05), something must decide what runs where, restart failed work, scale it, and manage its lifecycle across the fleet. That's orchestration, and Kubernetes (k8s) is the de-facto standard, including for AI workloads (often via hardened distros like RKE2 and add-ons for accelerators).
Rack-management software relates to k8s in three ways — keep them distinct:
- It runs on k8s: your agents/exporters deploy as pods/DaemonSets (one per node).
- It integrates with k8s: it consumes the API to cordon/drain a node for maintenance, label nodes with hardware facts, or react to events.
- It extends k8s: you write operators (controllers + CRDs) that teach the cluster
rack-specific operations — "a
RackNodeshould be at firmware baseline X and healthy" — so rack lifecycle becomes declarative, self-healing software.
The mental anchor: Kubernetes is a declarative, level-triggered control system. You declare desired state (objects); controllers continuously reconcile reality to it. If that sounds like config management (Phase 05) and inventory reconciliation (Phase 01) — it is the same idea, which is why this curriculum keeps returning to it.
Chapter 2: The Kubernetes Control Plane
The control plane is a set of components around a database:
- etcd: the consistent key-value store holding all cluster state (the source of truth).
- API server (
kube-apiserver): the only thing that talks to etcd; a REST API where everything is an object (validated, versioned, RBAC-controlled). All reads/writes go through it.kubectl applyis an API write. - Scheduler (
kube-scheduler): watches for unscheduled pods and assigns each to a node (Chapter 5). It only decides; it doesn't run anything. - Controller manager: runs the built-in controllers (Deployment, ReplicaSet, Node, …), each a reconcile loop driving actual toward desired.
- On each node: kubelet (runs/monitors the node's pods via the container runtime over CRI) and kube-proxy (service networking).
The flow for kubectl apply -f deploy.yaml: API server validates and writes the Deployment
to etcd → the Deployment controller creates a ReplicaSet → the ReplicaSet controller creates
Pods (desired count) → the scheduler binds each Pod to a node → that node's kubelet pulls the
image and starts the container → controllers keep watching and correcting. No component
gives orders imperatively; each watches state and reconciles. That's the whole design.
Chapter 3: Workloads, Nodes, and the DaemonSet Agent
The objects you'll use:
- Pod: the smallest deployable unit (one or more co-located containers). Usually you don't create pods directly; a controller does.
- Deployment → ReplicaSet → Pods: stateless apps; declare
replicas: N, get N pods, self-healed and rolling-updatable. - DaemonSet: one pod per node (optionally a node subset). This is the rack-agent pattern — your node-level rack-management agent, the metrics exporter (Phase 07), the device plugin, and the firmware agent all deploy as DaemonSets, so every node runs exactly one copy automatically as nodes join/leave.
- StatefulSet: stable identity/storage for stateful workloads (databases).
- Job/CronJob: run-to-completion / scheduled tasks (a one-shot firmware update job).
Each pod declares requests (guaranteed resources, used for scheduling) and limits (caps). Getting requests right is how the scheduler bin-packs without overcommitting — and it's where accelerators plug in (Chapter 6).
Chapter 4: The Operator Pattern and the Reconcile Loop
This is the chapter that matters most for this role, because it's how you encode rack operations as cluster-native software.
CRD (CustomResourceDefinition): extends the API with your own object kind. Define a
RackNode CRD and now kubectl get racknodes works and the API server stores/validates them
like built-ins. The CRD has a spec (desired state, written by users/automation) and a
status (observed state, written by your controller).
The reconcile loop (the operator's core):
loop forever:
obj = observe(current state of the RackNode + the real world)
diff = desired(spec) - actual(status/world)
if diff: act to reduce it (idempotently)
update status
requeue (after a delay, or on the next event)
Properties that make it robust (and are interview gold):
- Level-triggered, not edge-triggered: it reacts to current state, not to the event that changed it. So a missed event isn't fatal — the next reconcile sees reality and fixes it. Edge-triggered (act on the change notification) breaks if you miss an event; level- triggered is self-healing. This is why Kubernetes is reliable.
- Idempotent: reconcile may run many times; each run converges and is a no-op once matched (Phase 05's idea, exactly).
- Requeue + backoff: a failed action requeues for retry rather than crashing.
- Finalizers: a marker on an object that blocks deletion until your controller does cleanup (e.g., decommission the physical node, remove it from inventory) — then removes the finalizer to allow deletion. Without finalizers, deleting the object would orphan real-world resources.
- Owner references: child objects owned by a parent are garbage-collected with it.
A RackNode operator might reconcile: "spec says firmware baseline 1.2 and role=compute;
actual says firmware 1.1 → trigger a firmware job (Phase 08), update status, requeue until
matched; on delete, run the decommission finalizer." That's rack lifecycle as self-healing
software — the "automated SRE" promise of operators. Lab 01 builds this loop.
Chapter 5: Scheduling — How a Pod Lands on a Node
The scheduler runs two phases per unscheduled pod:
- Filter (predicates): eliminate nodes that can't run it — insufficient requests-vs-allocatable, missing required labels (nodeSelector/affinity), or taints the pod doesn't tolerate.
- Score (priorities): rank the survivors — spread vs bin-pack, affinity preferences, topology spread, image locality — and pick the best.
Key levers a rack operator uses:
- requests/limits: requests drive filtering/bin-packing; a pod requesting more than any node's allocatable stays Pending forever (a classic "why is my pod Pending" answer).
- Taints & tolerations: taint a node (
maintenance:NoSchedule, ordedicated=tenantA) and only pods tolerating it land there. This is how you drain a node for maintenance (cordon = unschedulable; taint+drain evicts), and how you dedicate accelerator nodes. - Affinity/anti-affinity & topology spread: co-locate or spread pods (e.g., spread replicas across failure domains — Phase 01 — so one PDU/ToR loss doesn't take all replicas).
- Topology-aware placement: for accelerators, place a pod where its GPU and NIC share a NUMA node / PCIe root (Phase 04) for performance.
"Why is this pod Pending?" → walk the filter phase: capacity, taints, affinity, no matching node. Lab 02 makes this concrete.
Chapter 6: Accelerators — Device Plugins & Topology
Kubernetes doesn't natively know about GPUs/AI accelerators; the device plugin API teaches it:
- A device-plugin DaemonSet runs on each node, discovers the accelerators, and advertises
them to the kubelet as an extended resource (e.g.,
qualcomm.com/ai-accelerator: 4). - Pods request them like any resource:
resources.limits["qualcomm.com/ai-accelerator"]: 1. - The scheduler now treats accelerators as schedulable capacity (filter/score), and the kubelet, via the plugin, allocates specific devices to the container (and sets up cgroups/device access).
Beyond basic counting:
- Node Feature Discovery (NFD) labels nodes with hardware facts (PCIe IDs, capabilities) so scheduling can target them.
- Topology-aware allocation places a pod's accelerators and NIC on the same NUMA node/PCIe root (Phase 04) — a real performance lever the Topology Manager handles.
- Partitioning (MIG-style fractional accelerators) is exposed as multiple smaller resources — the multi-tenancy/utilization tradeoff.
The "accelerator operator" pattern bundles the device plugin, NFD, driver management, and monitoring into one operator — the kind of thing this role builds or integrates.
Chapter 7: RKE2 and Production Distros
Upstream Kubernetes is a kit; production clusters run a distribution. RKE2 (Rancher Kubernetes Engine 2, aka RKE Government) is a hardened, secure-by-default distro — relevant because the JD names it and because secure/sovereign deployments (think a government data center in the Kingdom) demand exactly its properties:
- CIS-hardened out of the box; FIPS-capable; designed to pass security benchmarks.
- Simple, self-contained install (a single binary, embedded etcd) — good for air-gapped deployments (no internet during install), a recurring requirement in sovereign/regulated environments.
containerdruntime, bundled CNI, no Docker dependency.
Context for the family: k3s (lightweight, edge), RKE2 (hardened, datacenter/gov), OpenShift (enterprise, opinionated), upstream/kubeadm (DIY). They're all Kubernetes; the differences are security posture, install model, and bundled components. Knowing why RKE2 (hardening + air-gap + CIS) signals you understand the deployment context, not just the API.
Chapter 8: Multi-Tenancy & Cluster Lifecycle
Multi-tenancy in k8s (pairs with Phase 09's network/VLAN isolation):
- Namespaces partition objects; RBAC controls who can do what; ResourceQuotas cap a tenant's consumption; NetworkPolicies restrict pod-to-pod traffic; PodSecurity standards constrain what pods can do.
- Soft multi-tenancy (namespaces + quotas + RBAC) suits trusted tenants; hard isolation (separate clusters/nodes, or strong sandboxing) suits untrusted ones. For accelerators, not co-placing competing tenants on one physical device (Phase 04/09) is part of this.
Cluster lifecycle (where rack-management software acts on k8s):
- Node join: a freshly provisioned node (Phase 05) joins the cluster.
- cordon: mark a node unschedulable (no new pods) without evicting.
- drain: evict pods (respecting PodDisruptionBudgets) to free a node for maintenance — what you do before a firmware update (Phase 08) or a hardware swap.
- upgrade: roll the k8s/OS/driver version node-by-node — cordon → drain → upgrade → uncordon, canaried (Phase 11), image-pinned for rollback.
These verbs are how your rack operator safely takes a node out of service and back. The maintenance flow (cordon → drain → act → validate → uncordon) is a core operational primitive and a frequent system-design detail.
Lab Walkthrough Guidance
Order: Lab 01 (operator) → Lab 02 (scheduling). The reconcile loop is the conceptual core; scheduling is how workloads (and your agents) land on the hardware.
Lab 01 (RackNode operator):
python3 solution.py— a controller reconciles RackNodes: provisions to desired, corrects drift, and deletes with a finalizer-driven cleanup.- Read the reconcile function; note it's level-triggered (acts on current state) and idempotent (re-reconcile is a no-op).
- Inject drift and a transient action failure; watch it requeue and converge.
- Extensions: reimplement with
kopfagainst a realkindcluster + a CRD YAML.
Lab 02 (device plugin + scheduling):
python3 solution.py— a mini scheduler places accelerator-requesting pods across nodes honoring capacity, taints, and topology.- Over-subscribe accelerators and watch pods go Pending; add a maintenance taint and watch placement avoid that node.
- Read the real device-plugin DaemonSet + pod manifests.
- Extensions: run NVIDIA's device plugin on a real GPU; add MIG-style fractional resources; add gang scheduling.
Phase capstone question: "Design how rack-management runs and integrates with Kubernetes for
an accelerator fleet." (Answer: node agent + exporter as DaemonSets; a RackNode operator
encoding lifecycle/firmware/health as a reconcile loop; device plugin + NFD + topology-aware
scheduling for accelerators; cordon/drain/upgrade for maintenance; namespaces+RBAC+quota+
NetworkPolicy for tenancy; RKE2 for a hardened, air-gappable base.)
Success Criteria
You're done with this phase when — without notes:
-
You can trace
kubectl applythrough the control plane to a running container (Ch. 2) - You can explain the DaemonSet rack-agent pattern (Ch. 3)
- You can write/explain a reconcile loop and why it's level-triggered and idempotent (Ch. 4)
- You can explain spec vs status, finalizers, and owner references (Ch. 4)
- You can diagnose a Pending pod via the filter phase (capacity/taint/affinity) (Ch. 5)
- You can explain the device-plugin path from hardware to schedulable resource + topology (Ch. 6)
- You can say what RKE2 is and why hardened/air-gapped distros exist (Ch. 7)
- You can describe the cordon→drain→upgrade maintenance flow (Ch. 8)
Interview Q&A
Q1: What happens when you kubectl apply a Deployment?
A: The request hits the API server, which authenticates, validates, and writes the Deployment
object to etcd. The Deployment controller (a reconcile loop) sees it and creates a ReplicaSet;
the ReplicaSet controller creates the desired number of Pods. The scheduler watches for
unscheduled Pods and, for each, filters nodes (capacity, taints, affinity) and scores the
survivors, then binds the Pod to a node. That node's kubelet sees the binding, pulls the image
via the container runtime (CRI/containerd), and starts the container, reporting status back.
From then on the controllers keep watching and reconciling — if a pod dies, the ReplicaSet
controller makes a new one. Nothing is imperative; every component watches state and converges
it, which is the whole Kubernetes design.
Q2: Explain the operator pattern and the reconcile loop, and why it's level-triggered.
A: An operator extends Kubernetes with a CRD (a custom object like RackNode) plus a
controller that runs a reconcile loop: observe current state, diff desired (spec) against
actual (status/world), act idempotently to close the gap, update status, and requeue. It's
level-triggered — it reacts to current state, not to the event that changed it — so if
a notification is missed or duplicated, the next reconcile still sees reality and corrects it.
Edge-triggered logic (act on the change event) breaks when you miss an event; level-triggered
is self-healing, which is why it's reliable. The loop must be idempotent (many runs converge,
no-op once matched), requeue with backoff on failure, and use finalizers to do cleanup before
an object is deleted. It's the same declared-vs-discovered convergence as configuration
management — operators just apply it to cluster-native objects.
Q3: What's the difference between spec and status, and what is a finalizer for?
A: Spec is the desired state, written by users or automation; status is the observed
state, written by the controller. The controller's job is to make status (and the real world)
match spec. A finalizer is a key in an object's metadata that blocks its deletion: when you
delete the object, Kubernetes sets a deletion timestamp but keeps the object until all
finalizers are removed. The controller sees the deletion timestamp, performs cleanup (e.g.,
decommission the physical node, remove it from inventory, release licenses), then removes its
finalizer, allowing the object to be deleted. Without finalizers, deleting a RackNode object
would orphan the real-world resources it represented.
Q4: How does an accelerator get scheduled to a pod, and why might a pod be Pending?
A: A device-plugin DaemonSet on each node discovers the accelerators and advertises them to
the kubelet as an extended resource (e.g., qualcomm.com/ai-accelerator: 4). A pod requests
some via resources.limits, the scheduler treats them as schedulable capacity (filter +
score), and the kubelet (via the plugin) allocates specific devices and wires up access. A pod
goes Pending when the scheduler can't find a node that passes the filter phase: not enough
free accelerators/CPU/memory anywhere, a required nodeSelector/affinity matches no node, or
every candidate node has a taint the pod doesn't tolerate (e.g., maintenance, or dedicated to
another tenant). For accelerators specifically, requesting more than any single node has, or
topology constraints that can't be satisfied, leaves it Pending. The fix is reading the
scheduler events and the node's allocatable vs requested.
Q5: What is RKE2 and why choose it over upstream Kubernetes? A: RKE2 is Rancher's hardened Kubernetes distribution — CIS-benchmark-hardened and FIPS-capable by default, with a simple single-binary, embedded-etcd install and bundled containerd/CNI. You choose it when security posture and deployment constraints matter: it passes security benchmarks out of the box and installs cleanly air-gapped (no internet), which is exactly what sovereign, regulated, or government data centers require — relevant for a data center in the Kingdom. Upstream Kubernetes is a kit you'd have to harden and assemble yourself; RKE2 gives you a secure, supportable base. It's still Kubernetes — same API and operators — so my rack operators and DaemonSets run on it unchanged.
Q6 (Staff): How do you safely upgrade Kubernetes (or a driver/firmware) across a 100-node accelerator rack with running tenant workloads? A: Treat it as a controlled, canaried, reversible rollout (Phase 11 thinking). First, ensure PodDisruptionBudgets so draining respects availability. Then per node, in waves: cordon (stop new scheduling) → drain (evict pods, honoring PDBs, so tenant work reschedules elsewhere) → perform the upgrade (k8s/driver/firmware — Phase 08) → validate (node healthy, accelerators enumerated at full PCIe width per Phase 04, smoke test) → uncordon. Start with a 1-node canary, bake, then widen the wave size, with automatic halt-and-rollback if health/ SLOs regress (image-pinned so rollback is fast). Respect failure domains (Phase 01) — don't drain a whole PDU/ToR's worth at once. Drive the whole thing from an operator/job so it's declarative, observable (Phase 07), and resumable. The Staff point is that the process — gated, canaried, domain-aware, reversible — matters more than the mechanics, because the cost of a bad fleet-wide upgrade is enormous.
References
- Kubernetes docs — Concepts (control plane, controllers, scheduling) — https://kubernetes.io/docs/concepts/
- Kubernetes — Operator pattern & the controller/reconcile model — https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
- Kubebuilder Book (CRDs, controllers, finalizers, owner refs) — https://book.kubebuilder.io/
kopf(Kubernetes operators in Python) — https://kopf.readthedocs.io/- Kubernetes Device Plugins & Topology Manager — https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/
- NVIDIA GPU Operator / device plugin (the accelerator-operator pattern) — https://github.com/NVIDIA/k8s-device-plugin
- RKE2 documentation (hardening, air-gap, CIS) — https://docs.rke2.io/
- Cross-track: Head of SWE — GPU, Phase 06 (scheduling/virtualization)
🛸 Hitchhiker's Guide — Phase 06: Orchestration
Read this if: you can
kubectl get podsbut the control plane and operators are fuzzy. Compressed map of the parts, the reconcile loop, and how accelerators get scheduled.
0. The 30-second mental model
Kubernetes is a declarative, level-triggered control system: you write desired state (objects); controllers continuously reconcile reality to it. Rack software relates three ways — it runs on k8s (DaemonSet agents), integrates with it (cordon/drain/label), and extends it (operators encoding rack lifecycle). The one idea to carry: the reconcile loop is the same declared-vs-discovered convergence as Phases 01 and 05.
1. Control plane in one diagram
kubectl ─▶ API server ─▶ etcd (truth)
│
┌────────────┼─────────────┐
scheduler controllers (watch + reconcile)
│ │
▼ ▼
node: kubelet ─▶ containerd ─▶ pods
No imperative orders — every component watches state and converges it.
2. Workload objects (pick the right one)
| Object | Use |
|---|---|
| Deployment | stateless app, N replicas, rolling update |
| DaemonSet | one pod per node — the rack-agent / exporter / device-plugin pattern |
| StatefulSet | stable identity/storage |
| Job/CronJob | run-to-completion / scheduled (a firmware job) |
3. The reconcile loop (memorize)
observe → diff(desired spec, actual status/world) → act idempotently → update status → requeue
- level-triggered (acts on current state, not the event) → self-healing, survives missed events.
- idempotent (no-op once matched) · requeue+backoff on failure · finalizers for delete-time cleanup · owner refs for GC.
- spec = desired (users write) · status = observed (controller writes).
4. Why a pod is Pending (the filter phase)
capacity (requests > allocatable anywhere) · a taint it doesn't tolerate · nodeSelector/ affinity matches no node · topology can't be satisfied. Read scheduler events first.
5. Accelerators → pods
device-plugin DaemonSet discovers HW → advertises vendor.com/accel: N extended resource →
pod resources.limits requests it → scheduler treats as capacity → kubelet allocates the
device. Add NFD (labels), Topology Manager (GPU+NIC same NUMA — Phase 04), MIG (fractional).
6. Maintenance verbs (how rack software acts on k8s)
cordon (no new pods) → drain (evict, honor PDBs) → upgrade/firmware (Phase 08) → validate
→ uncordon. Canary + domain-aware (Phase 01) + image-pinned rollback (Phase 11).
7. RKE2 in one line
Hardened (CIS/FIPS), single-binary, embedded-etcd, air-gappable Kubernetes — the distro for secure/sovereign data centers. Same API; your operators run unchanged. (k3s=edge, OpenShift=enterprise, kubeadm=DIY.)
8. Multi-tenancy boundaries
namespaces + RBAC + ResourceQuota + NetworkPolicy + PodSecurity (soft); separate clusters/ nodes or strong sandboxing (hard). Pairs with Phase 09 VLAN/QoS isolation.
9. The "done this before" tells
"Is it level- or edge-triggered?" · "Is the reconcile idempotent?" · "Did you add a finalizer for cleanup?" · "Why is it Pending — capacity, taint, or affinity?" · "DaemonSet for the agent, right?" · "Did you drain before the upgrade?"
10. How this phase pays off later
The operator/reconcile loop is the Phase 13 capstone's control core. cordon/drain is the safe path for Phase 08 firmware updates. Tenancy pairs with Phase 09. DaemonSet is how the Phase 07 exporter ships.
Lab 01 — A RackNode Kubernetes Operator (reconcile loop)
Phase: 06 — Orchestration | Difficulty: ⭐⭐⭐⭐☆ | Time: 5–7 hours
Language: Python 3 (sim) + a real CRD | Hardware: none (kind for the extension)
Concept primer:
../WARMUP.mdCh. 4,../HITCHHIKERS-GUIDE.md§3.
Run
python3 solution.py # provision -> idempotent re-reconcile -> drift -> delete (finalizer)
Files
solution.py— the reconcile loop (the operator's core), as a runnable simulator.racknode-crd.yaml— the real CRD + an example RackNode (status subresource enabled).operator.py— the real kopf operator (same logic, real Kubernetes handlers).run-kind.sh+requirements.txt— stand up a real cluster and run the operator.
Run it for REAL on a live cluster (the extension — verified)
Needs Docker + kind + kubectl. The simulator's logic, as an actual operator:
./run-kind.sh up # kind cluster + CRD
pip install -r requirements.txt
kopf run operator.py --verbose # the operator (leave running)
# in another shell:
kubectl apply -f racknode-crd.yaml # creates node-00
kubectl get racknodes -w # PHASE -> Ready, FIRMWARE -> desired
kubectl patch racknode node-00 --type merge \
-p '{"spec":{"desiredFirmware":"2.2.0"}}' # drift -> re-converges
kubectl delete racknode node-00 # kopf finalizer cleanup runs
./run-kind.sh down
This was run end-to-end on a kind v1.31 cluster — observed output:
NAME PHASE FIRMWARE DESIRED
node-00 Ready 2.1.0 2.1.0 # reconciled to baseline (with a level-triggered re-run)
node-00 Ready 2.2.0 2.2.0 # after a desiredFirmware patch: drift corrected
# on delete: "decommissioned node-00 (finalizer cleanup done)"
Note: kopf logs each TemporaryError("converging firmware") as failed temporarily — that's
the requeue mechanism (level-triggered re-run), not a real failure. kopf manages its own
finalizer because operator.py has an @kopf.on.delete handler.
0. The mission
Build the loop every operator runs and that the JD's "integrate with Kubernetes" and rack lifecycle automation depend on: observe → diff(spec, status/world) → act idempotently → update status → requeue. Make it level-triggered (self-healing) and prove it converges, no-ops when matched, corrects drift, requeues on failure, and cleans up via a finalizer.
1. The five behaviors to internalize
- Convergence: a
RackNodewithdesiredFirmware: 2.1.0drives the world to 2.1.0 and reachesReady. - Idempotency / level-triggered: re-reconciling a converged node adds nothing and asks for no requeue — it reacts to current state, not events.
- Drift correction: an out-of-band firmware downgrade is detected and remediated on the next reconcile (this is why level-triggered beats edge-triggered).
- Requeue on transient failure: a flaky firmware action raises, the controller requeues, and it eventually converges — never crashes, never half-applies.
- Finalizer cleanup: deleting the object runs the decommission cleanup before the object disappears, so the real node isn't orphaned.
2. Extensions (do them)
- Make it real with
kopf+kind:kind create cluster,kubectl apply -f racknode-crd.yaml, then write akopfhandler whose@kopf.on.create/update/deletefunctions mirrorreconcile(). Watchkubectl get racknodesshow the status converge. - Status conditions with timestamps (Kubernetes-style condition objects: type/status/ reason/lastTransitionTime).
- Owner references + GC: have the RackNode own a child Job (the firmware update) so deleting the RackNode garbage-collects it.
- Backoff: replace the naive retry with exponential backoff + a max-requeue policy (Phase 02).
3. What this lab proves
You understand the operator pattern at the level that lets you build one, not just use one — the modern way rack-management logic lives in the cluster. "Explain the reconcile loop and why it's level-triggered" becomes a demonstration. This loop is the control core of the Phase 13 capstone, and it's the same convergence pattern as Phase 05 CM and Phase 01 inventory.
Lab 02 — Device Plugin & Accelerator Scheduling
Phase: 06 — Orchestration | Difficulty: ⭐⭐⭐☆☆ | Time: 4–5 hours
Language: Python 3 (sim) + real manifests | Hardware: none (kind/GPU for extensions)
Concept primer:
../WARMUP.mdCh. 5–6,../HITCHHIKERS-GUIDE.md§4–§5.
Run
python3 solution.py # place pods across nodes; capacity, taints, topology, Pending
Files
solution.py— a mini scheduler (filter → score → bind) for accelerator pods.manifests.yaml— the real device-plugin DaemonSet + a pod requesting an accelerator.
0. The mission
Understand how an accelerator goes from hardware to a scheduled pod, and why a pod ends up Pending — by building the scheduler's two-phase placement (filter then score) with capacity, taints/tolerations, and topology (NUMA/PCIe from Phase 04).
1. What the sim shows
- Capacity:
too-big(16 accelerators) fits nowhere → Pending with the per-node reasons (likekubectl describe pod). - Taints/tolerations: a maintenance-tainted node only accepts pods that tolerate it
(
maint-toolcan;infer-bcan't) — the basis of cordon/drain. - Topology:
want_topologypods are scored to land where accelerators share the NIC's NUMA node (Phase 04 performance). - Binding consumes capacity: placement mutates node usage, so later pods see less room.
2. The real manifests
manifests.yaml shows the device-plugin DaemonSet (one per node, privileged, tolerates
everything so it runs even on tainted nodes), a tenant pod requesting
qualcomm.com/ai-accelerator: 1 with anti-affinity to spread replicas across PDUs (Phase 01
failure domains), and the taint/cordon/drain commands for maintenance.
3. Extensions (do them)
- Run a real device plugin: on a
kindcluster with a fake-device plugin (or a real GPU node + NVIDIA's plugin),kubectl describe nodeto see the advertised resource, then schedule a pod that requests it. - MIG-style fractional resources: advertise
accel-sliceunits and schedule sub-device workloads — the utilization/isolation tradeoff (Phase 09). - Gang scheduling: require all N accelerators of a multi-GPU job to be placed together or none (avoid partial deadlock) — a real AI-training need.
- Topology Manager: read
/sysNUMA from Phase 04 Lab 02 and feed real adjacency into the scorer.
4. What this lab proves
You can reason about accelerator scheduling end to end — the device-plugin path, why a pod is Pending, and topology-aware placement — which is exactly how rack-management software gets tenant AI workloads onto the right hardware. Combined with Lab 01's operator, you have the two halves of "integrate rack management with Kubernetes."
Phase 07 — Telemetry & Observability
Difficulty: ⭐⭐⭐☆☆ | Estimated Time: 2 weeks Roles supported: Rack Management SWE (Senior/Staff), SRE, Platform/Infra SWE Hardware needed: none — the exporter + Prometheus/Grafana run locally (Docker optional)
Why This Phase Exists
The JD is explicit and repeated: "Develop dashboards and unified telemetry pipelines using Prometheus and Grafana" and "Implement observability features such as logging, metrics, and tracing to support troubleshooting and operational reliability." You cannot operate — let alone debug in production (Phase 10) — what you cannot see. Observability is how a rack fleet stops being a black box.
The hard, role-specific problem is unification: a rack's signals come from Redfish nodes, IPMI BMCs, SNMP PDUs/switches, and CDUs (Phases 03–04), each with its own model. Your job is to normalize them into one telemetry pipeline — metrics (Prometheus), dashboards (Grafana), plus logs and traces — so an operator sees one coherent view and alerts fire on the right signals (the ΔT/PCIe/power thresholds from Phases 01/04).
You'll build a Redfish→Prometheus exporter that serves real exposition-format metrics, with PromQL alert rules and a Grafana dashboard, and an OpenTelemetry tracing + structured logging demo that makes a multi-step rack workflow debuggable.
Concepts
The three pillars (and the fourth)
- Metrics: numeric time-series (temperature, power, error counts); cheap, aggregatable, the basis of dashboards and alerts
- Logs: discrete structured events (Phase 02); the detail when a metric says "something's wrong"
- Traces: the path of a request/workflow across components (where time went, what failed)
- (Events/alerts): state changes and the notifications they trigger
Prometheus (the metrics standard)
- The pull model: Prometheus scrapes
/metricsHTTP endpoints on an interval (vs push) - Exposition format:
metric_name{label="value"} number— the text format an exporter serves - Metric types: counter, gauge, histogram, summary; when to use each
- Exporters: the adapter pattern —
node_exporter,redfish_exporter,snmp_exporter,ipmi_exporter; you write one for rack devices - PromQL: rate(), aggregation (sum/avg by label), threshold queries; recording rules; alerting rules → Alertmanager
- Cardinality (the #1 Prometheus footgun): labels explode series count; design labels carefully
- Service discovery (k8s/file-based); scrape configs; the TSDB
Grafana (dashboards)
- Data sources, panels, variables/templating; dashboards-as-code (JSON); per-rack/per-fleet views
- Designing a useful dashboard: the USE/RED methods; what to put on a rack overview
Logs & tracing
- Structured logging (Phase 02) → aggregation (Loki/ELK); correlation IDs
- OpenTelemetry: traces, spans, context propagation, trace_id/span_id; the OTel collector
- Tying logs↔traces↔metrics with shared IDs (exemplars); the debugging payoff
Operational practice (the senior layer)
- SLI/SLO/error budgets; alert on symptoms (SLOs) not causes; alert fatigue
- The unified pipeline architecture: collectors near hardware → central TSDB/log store → dashboards/alerts
- What to alert on for a rack: thermal (ΔT, throttle), power (over-budget, PSU), PCIe (AER, degraded link), node down, firmware drift
Labs
Lab 01 — Redfish → Prometheus Exporter + Grafana
| Field | Value |
|---|---|
| Goal | Build a Prometheus exporter that pulls rack telemetry (Redfish/IPMI/SNMP, normalized — Phase 03) and serves it in exposition format on /metrics, with PromQL alert rules and a Grafana dashboard. |
| Concepts | Pull model, exposition format, metric types, labels/cardinality, PromQL alerts, Grafana dashboards-as-code, the exporter pattern. |
| Steps | 1) python3 solution.py — starts the exporter; curl localhost:9101/metrics. 2) Read the exposition output and the metric/label design. 3) Read alerts.yml (PromQL) and dashboard.json (Grafana). 4) Extensions: scrape it with real Prometheus + Grafana. |
| Stack | Python 3 stdlib (http.server) + real Prometheus alert rules + Grafana JSON |
| Output | A working /metrics endpoint + alert rules + a Grafana dashboard for a rack. |
| How to Test | Asserts verify exposition-format correctness, metric types/labels, and that threshold values would fire the alert rules. |
| Talking Points | Pull vs push; counter vs gauge; the cardinality trap; alert on symptoms not causes; why a unified exporter across Redfish/IPMI/SNMP. |
| Resume Bullet | "Built a Prometheus exporter unifying Redfish/IPMI/SNMP rack telemetry into exposition-format metrics, with PromQL alerting rules and a Grafana dashboard for thermal/power/PCIe health." |
| Extensions | Run real Prometheus scraping it + Grafana importing the dashboard; add a histogram for scrape latency; add recording rules; wire it to the Phase 04 PDU/CDU/PCIe sim. |
Lab 02 — OpenTelemetry Tracing + Structured Logging
| Field | Value |
|---|---|
| Goal | Make a multi-step rack workflow (e.g., provisioning, Phase 05) debuggable: emit spans across steps with a propagated trace context, and structured logs carrying the trace_id, so you can answer "where did it fail and how long did each step take?" |
| Concepts | Traces/spans, context propagation, trace_id/span_id, structured logs, log↔trace correlation, the OTel data model. |
| Steps | 1) python3 solution.py — runs a traced workflow with an injected failure. 2) Read the span tree + the correlated logs. 3) See total/step timing and the failed span. 4) Extensions: export to a real OTel collector/Jaeger. |
| Stack | Python 3 stdlib (a minimal OTel-shaped tracer) |
| Output | A trace (span tree with timings) + structured logs correlated by trace_id, pinpointing a failure. |
| How to Test | Asserts verify span parent/child nesting, context propagation, error-span marking, and that logs carry the active trace_id. |
| Talking Points | Why traces beat logs for "where did time go"; context propagation across components; correlating logs/traces/metrics by ID; sampling. |
| Resume Bullet | "Instrumented a rack workflow with OpenTelemetry-style tracing and trace-correlated structured logging, pinpointing failures and per-step latency across a distributed flow." |
| Extensions | Export OTLP to Jaeger/Tempo; add metrics exemplars linking a slow metric to its trace; propagate context across a real HTTP call. |
Deliverables Checklist
-
Exporter serves valid Prometheus exposition format on
/metrics - You can choose counter vs gauge vs histogram correctly and explain why
- You can explain the cardinality trap and design labels to avoid it
- Alert rules fire on the right rack symptoms (thermal/power/PCIe/node-down)
- You can read/build a Grafana dashboard and explain USE/RED
- Tracing demo shows a span tree with timings and a marked failure
- Logs carry the trace_id so logs↔traces correlate
- You can articulate SLI/SLO and "alert on symptoms, not causes"
Interview Relevance
- "Design a telemetry pipeline for a rack fleet with Redfish, IPMI, and SNMP devices."
- "Prometheus pull vs push — why pull? When would you push?"
- "Counter vs gauge vs histogram — give an example of each for a rack."
- "What is metric cardinality and how do you keep it under control?"
- "What would you alert on for an AI rack, and how do you avoid alert fatigue?"
- "Metrics say latency spiked. How do traces and logs help you find why?"
- "What's an SLO and an error budget, and why alert on symptoms not causes?"
Warmup Guide — Telemetry & Observability
Zero-to-expert primer for Phase 07. You need no monitoring background: this guide starts at "what is a metric" and ends with you designing a unified rack telemetry pipeline, writing PromQL alerts, and correlating metrics/logs/traces. This is how you see the fleet — the prerequisite for operating and debugging it (Phase 10).
Table of Contents
- Chapter 1: Observability vs Monitoring; the Three Pillars
- Chapter 2: Metrics and the Prometheus Model
- Chapter 3: Metric Types and the Exposition Format
- Chapter 4: Exporters — The Unification Problem
- Chapter 5: PromQL, Recording Rules, and Alerting
- Chapter 6: Cardinality — The Footgun
- Chapter 7: Grafana and Dashboard Design
- Chapter 8: Logs, Traces, and Correlation
- Chapter 9: SLIs/SLOs and Alerting Philosophy
- Lab Walkthrough Guidance
- Success Criteria
- Interview Q&A
- References
Chapter 1: Observability vs Monitoring; the Three Pillars
Monitoring = watching known signals for known problems (is CPU > 90%?). Observability = being able to ask new questions about your system's behavior from its outputs, including ones you didn't anticipate ("why is this specific rack's tail latency high since the firmware update?"). You build monitoring; you design for observability.
The classic three pillars:
- Metrics — numeric time-series sampled over time (temperature, power, request rate). Cheap to store, easy to aggregate and alert on; the backbone of dashboards. Weakness: they tell you that something is wrong, rarely why at fine grain.
- Logs — timestamped, ideally structured (Phase 02) events. The detail behind a metric anomaly. Weakness: volume and cost; hard to aggregate.
- Traces — the end-to-end path of one request/workflow across components, broken into spans with timings. The answer to "where did the time go / where did it fail." Weakness: needs instrumentation and (usually) sampling.
The power is in correlation: a metric alert → jump to the traces in that window → jump to the logs for the failing span, all tied by shared IDs. For a rack fleet, metrics catch the thermal/power/PCIe problems, logs explain the specific failure, traces show which step of a provisioning/firmware workflow stalled.
Chapter 2: Metrics and the Prometheus Model
Prometheus is the de-facto open metrics system (the JD names it). Its defining choice is
the pull model: Prometheus periodically scrapes an HTTP /metrics endpoint on each
target and stores the samples in its time-series database (TSDB).
Why pull (a guaranteed interview question):
- Targets are dumb; the monitoring system controls scrape frequency and knows the target list (via service discovery) — so a missing target is itself a signal ("up == 0").
- No push infrastructure for thousands of targets to overwhelm; backpressure is natural.
- Easy local testing:
curl /metricsshows exactly what Prometheus sees.
When you do push: short-lived batch jobs that may finish before any scrape (use the
Pushgateway), and across network boundaries you can't scrape (remote-write to a central
Prometheus/Mimir/Thanos). For a rack, the agents/exporters expose /metrics and Prometheus
scrapes them; ephemeral provisioning jobs might push to a Pushgateway.
A time-series is identified by its name + a set of labels: node_temp_celsius{rack="r1", node="n0",sensor="accel0"}. Labels are the dimensions you slice and aggregate by — and the
source of cardinality danger (Chapter 6).
Chapter 3: Metric Types and the Exposition Format
The exposition format is plain text an exporter serves:
# HELP node_temp_celsius Temperature in Celsius
# TYPE node_temp_celsius gauge
node_temp_celsius{rack="r1",node="n0",sensor="accel0"} 71
node_temp_celsius{rack="r1",node="n0",sensor="inlet"} 27
# TYPE pcie_correctable_errors_total counter
pcie_correctable_errors_total{node="n0",bdf="0000:13:00.0"} 250
The four metric types — choosing correctly is a senior signal:
- Counter: monotonically increasing (only goes up, resets on restart). Errors, bytes,
requests. You almost always query
rate(counter[5m]), never the raw value. PCIe AER errors, packets, firmware-update attempts. - Gauge: a value that goes up and down. Temperature, power draw, fan RPM, free memory, CDU ΔT. Most rack hardware telemetry is gauges.
- Histogram: samples bucketed into ranges + a sum + a count; lets you compute quantiles
(p99) server-side via
histogram_quantile. Scrape latency, provisioning step duration. - Summary: client-side quantiles (less flexible for aggregation than histograms; prefer histograms when you'll aggregate across instances).
The rule of thumb: did it only ever increase? → counter; can it decrease? → gauge; do you need a distribution/quantile? → histogram.
Chapter 4: Exporters — The Unification Problem
Prometheus scrapes HTTP; your hardware speaks Redfish/IPMI/SNMP/Modbus (Phases 03–04). An
exporter bridges the gap: it pulls from the device's native protocol and re-exposes the
data as Prometheus metrics on /metrics. The ecosystem has node_exporter (host metrics),
snmp_exporter, ipmi_exporter, redfish_exporter — and for a rack with heterogeneous gear,
you build a unified exporter that uses the Phase 03 driver abstraction (Redfish-first,
IPMI/SNMP fallback) and normalizes everything into one consistent metric/label scheme.
This is the JD's "unified telemetry pipelines" in concrete form, and it's Lab 01. The design choices that matter:
- Consistent naming/labels across protocols (a temperature is
node_temp_celsiusregardless of whether it came from RedfishThermalor IPMI SDR) — so dashboards and alerts don't care about the source. - Scrape-time vs background-poll: BMCs are slow/fragile (Phase 03 Ch. 9), so the exporter usually polls devices on its own schedule (with caching, retries, timeouts — Phase 02) and serves the cached values on scrape, rather than hammering a BMC synchronously on every Prometheus scrape.
- An
up/health metric per device so you can alert on "exporter can't reach this BMC."
Chapter 5: PromQL, Recording Rules, and Alerting
PromQL queries the TSDB. The patterns you'll use constantly:
- Aggregate across a label:
avg by (rack) (node_temp_celsius),sum by (rack) (node_power_watts). - Rate of a counter:
rate(pcie_correctable_errors_total[5m])— errors per second. - Threshold for an alert:
node_temp_celsius > 85,sum by (rack)(node_power_watts) > 17000(the Phase 01 budget!). up == 0— a target Prometheus couldn't scrape (device/exporter down).
Recording rules precompute expensive/常用 queries into new series (e.g., per-rack power) so dashboards and alerts are fast.
Alerting rules are PromQL conditions with a for: duration (must hold N minutes to fire,
avoiding flapping), labels (severity), and annotations (description/runbook link → Phase 12).
They fire to Alertmanager, which dedupes, groups, silences, and routes
(page/Slack/email). Example rack alerts:
- alert: AcceleratorHot
expr: node_temp_celsius{sensor=~"accel.*"} > 85
for: 5m
labels: { severity: warning }
annotations: { summary: "Accelerator hot on {{$labels.node}}", runbook: "..." }
- alert: RackPowerOverBudget
expr: sum by (rack)(node_power_watts) > 17000
for: 2m
labels: { severity: critical }
- alert: NodeDown
expr: up{job="rack-exporter"} == 0
for: 1m
labels: { severity: critical }
Chapter 6: Cardinality — The Footgun
The single most important Prometheus operational lesson. Cardinality = the number of distinct time-series, which is the product of every label's distinct values. Each unique label-set is a separate series consuming memory and disk.
The trap: putting a high-cardinality value in a label — a timestamp, a request ID, a
serial number on a high-churn metric, a full error message — multiplies series explosively and
can OOM Prometheus. http_requests_total{user_id="..."} with a million users = a million
series per metric.
The discipline:
- Labels are for bounded, low-cardinality dimensions you aggregate/filter by: rack, node, sensor, severity, job. Not unbounded identifiers.
- Put high-cardinality detail in logs/traces (Chapter 8), not metric labels.
- Watch series count; alert on it; review new metrics for label explosions in code review.
For a rack fleet (thousands of nodes × dozens of sensors), even legitimate labels add up — which is why naming and label design is a real engineering task, not an afterthought.
Chapter 7: Grafana and Dashboard Design
Grafana visualizes metrics (and logs/traces) from data sources like Prometheus. Dashboards are JSON (dashboards-as-code — version them, review them, Phase 11). Design matters more than prettiness:
- Variables/templating: a
$rack/$nodedropdown so one dashboard serves the whole fleet. - Two proven methods: USE (Utilization, Saturation, Errors) for resources (per accelerator/PDU/CDU); RED (Rate, Errors, Duration) for services (the control-plane API). For a rack overview: power vs budget, temps with thresholds, PCIe error rates, node up/down, CDU ΔT/flow, firmware-version spread.
- The hierarchy: a fleet view (which racks are unhealthy) → a rack view (which nodes) → a node view (which sensor) — so an operator drills from symptom to cause.
- Avoid the "wall of graphs nobody reads"; every panel should answer a question or back an alert.
Chapter 8: Logs, Traces, and Correlation
Logs (Phase 02 structured JSON) get aggregated (Loki, Elasticsearch) so you can query across the fleet. The key for correlation: include a trace_id and stable labels (node, rack, request_id) in every line.
Traces (OpenTelemetry — the vendor-neutral standard) capture a workflow as a tree of spans, each with a name, start/end (duration), attributes, and a parent — sharing a trace_id and each having a span_id. Context propagation carries the trace_id/ span_id across function calls and across services (via headers), so a provisioning workflow that spans the orchestrator, a BMC call, and a firmware job is one trace. Traces answer "where did the time go / which step failed," which metrics and logs alone don't.
Correlation is the payoff: a metric alert → the traces in that window (via exemplars linking a metric sample to a trace) → the failing span → its logs (same trace_id). This is the debugging loop (Phase 10) that turns "the fleet is slow" into "step 3 of provisioning on node-237 times out calling the BMC, here's the log." The OTel collector receives, processes (sampling), and exports telemetry to backends (Jaeger/Tempo for traces, Prometheus for metrics) — one pipeline, vendor-neutral. Lab 02 builds the tracing + correlation.
Chapter 9: SLIs/SLOs and Alerting Philosophy
The operational maturity layer:
- SLI (Service Level Indicator): a measured quantity reflecting user/operator experience (e.g., "fraction of provisioning runs that succeed," "p99 control-plane API latency," "fraction of nodes healthy").
- SLO (Objective): a target for an SLI over a window ("99% of provisions succeed," "99.9% node availability/month").
- Error budget: 1 − SLO. The allowed failure. Spend it on velocity (ship changes) until it's low, then slow down — a shared language between dev and ops.
Alert on symptoms, not causes. Page on the thing that matters (SLO breach: provisioning failing, a rack offline, accelerators throttling) — not on every cause (a single retry, a transient blip). Cause-based alerts produce alert fatigue: too many pages, people stop reading them, the real one gets missed. Every page should be actionable and link a runbook (Phase 12). For a rack: page on node-down, rack-over-budget, sustained accelerator overheat, CDU leak (Phase 04), and SLO breaches; don't page on a single correctable PCIe error (dashboard it, alert only on a rate).
Lab Walkthrough Guidance
Order: Lab 01 (exporter/metrics) → Lab 02 (tracing/logs). Metrics are the backbone; tracing is the deep-dive when metrics flag a problem.
Lab 01 (Redfish→Prometheus exporter):
python3 solution.py, thencurl localhost:9101/metrics— read the exposition format.- Note the metric types (gauge for temp/power, counter for AER errors) and the bounded labels (rack/node/sensor) — and that there are no high-cardinality labels (Ch. 6).
- Read
alerts.yml(the PromQL rack alerts) anddashboard.json(Grafana). - Extensions: run real Prometheus scraping it + import the dashboard into Grafana; wire it to the Phase 04 PDU/CDU/PCIe sim.
Lab 02 (OTel tracing + logs):
python3 solution.py— a traced provisioning-style workflow with an injected failure.- Read the span tree (timings, parent/child) and the structured logs carrying the trace_id.
- See how the failed span + its correlated logs pinpoint the problem.
- Extensions: export OTLP to Jaeger/Tempo; add a metric exemplar linking a slow metric to its trace.
Phase capstone question: "Design the telemetry pipeline for a 10,000-node accelerator fleet
with Redfish/IPMI/SNMP devices. What do you alert on, and how do you keep it scalable and
debuggable?" (Answer in system-design/02: unified exporters near hardware → Prometheus
(federated/remote-write to Mimir/Thanos for scale) → Grafana fleet→rack→node dashboards →
Alertmanager on SLO symptoms with runbooks; logs to Loki and traces via OTel, correlated by
IDs; ruthless cardinality discipline.)
Success Criteria
You're done with this phase when — without notes:
- You can explain the three pillars and what each is good/bad at (Ch. 1)
- You can explain Prometheus pull vs push and when to push (Ch. 2)
- You can pick counter/gauge/histogram for a given rack signal and read exposition format (Ch. 3)
- You can describe the exporter pattern and how you'd unify Redfish/IPMI/SNMP (Ch. 4)
- You can write PromQL for an aggregation, a rate, and an alert threshold (Ch. 5)
- You can explain cardinality and avoid the label-explosion trap (Ch. 6)
- You can design a fleet→rack→node Grafana dashboard with USE/RED (Ch. 7)
- You can explain traces/spans/context propagation and log↔trace correlation (Ch. 8)
- You can define SLI/SLO/error budget and "alert on symptoms not causes" (Ch. 9)
Interview Q&A
Q1: Design a telemetry pipeline for a rack fleet with Redfish, IPMI, and SNMP devices.
A: I'd build unified exporters near the hardware that use the Phase 03 driver abstraction
(Redfish-first, IPMI/SNMP fallback), poll each device on their own schedule with caching/
retries (BMCs are fragile), and re-expose everything as Prometheus metrics with a consistent
naming/label scheme so a temperature is node_temp_celsius regardless of source. Prometheus
scrapes the exporters (pull); at fleet scale I federate or remote-write to a scalable backend
(Mimir/Thanos). Grafana provides fleet→rack→node dashboards (USE for hardware, RED for the
control plane). Alertmanager fires on SLO symptoms (node down, rack over power budget, sustained
overheat, CDU leak) with runbook links, deduped and routed. Logs go to Loki and traces via
OpenTelemetry, all correlated by node/rack/trace_id. The two disciplines that keep it healthy:
strict cardinality control (bounded labels only) and alerting on symptoms not causes.
Q2: Why does Prometheus pull instead of push, and when would you push?
A: Pull means the monitoring system owns the target list (via service discovery) and the scrape
schedule, so a target simply being unreachable is itself a signal (up == 0), there's no push
infrastructure to overwhelm, backpressure is natural, and I can curl /metrics to see exactly
what Prometheus sees. I push in two cases: short-lived batch jobs that may finish before any
scrape (via the Pushgateway), and crossing network boundaries I can't scrape (remote-write to a
central store). For a rack, exporters expose /metrics and Prometheus pulls; an ephemeral
provisioning job might push its result to a Pushgateway.
Q3: Counter vs gauge vs histogram — give a rack example of each.
A: A counter only increases (resets on restart) and you query its rate — e.g.,
pcie_correctable_errors_total, queried as rate(...[5m]) to see error velocity. A gauge
goes up and down — e.g., node_temp_celsius or cdu_delta_t_celsius or node_power_watts;
most hardware telemetry is gauges. A histogram buckets samples so you can compute quantiles
server-side — e.g., provisioning_step_duration_seconds so I can alert on p99 step latency
across the fleet. The rule: only-ever-up → counter, up-and-down → gauge, need-a-distribution →
histogram.
Q4: What is cardinality and how do you keep it from blowing up? A: Cardinality is the number of distinct time-series, which is the product of each label's distinct values — every unique label-set is a separate series costing memory/disk. It blows up when you put an unbounded value in a label (a request ID, timestamp, full error string, or a serial on a high-churn metric), which can OOM Prometheus. The discipline: labels are only for bounded, low-cardinality dimensions you aggregate/filter by (rack, node, sensor, severity); high-cardinality detail goes in logs/traces, not metric labels; and I watch the series count, alert on it, and review new metrics in code review for label explosions. At thousands of nodes × dozens of sensors, even legitimate labels add up, so label design is real engineering.
Q5: What would you alert on for an AI rack, and how do you avoid alert fatigue?
A: Alert on symptoms that matter, with a for: duration to avoid flapping and a runbook on
each: node/exporter down (up==0), rack over power budget (sum by(rack)(power) > derated),
sustained accelerator overheat or thermal throttling, CDU leak or ΔT/flow anomaly (Phase 04),
and SLO breaches (provisioning success rate, control-plane latency). I avoid fatigue by not
paging on every cause — a single correctable PCIe error or one retry gets dashboarded, and I
alert only on a rate or sustained condition; non-urgent issues go to a ticket, not a page.
Every page must be actionable; if it isn't, it becomes a dashboard or a tuned threshold.
Symptom-based, SLO-driven alerting keeps the signal-to-noise high so the real page gets read.
Q6: Metrics show provisioning latency spiked. Walk me from alert to root cause.
A: The alert (an SLO symptom: p99 provisioning duration breached) is the entry point. I pivot to
traces in that window — each provisioning run is a trace of spans (discover, firmware, OS
install, configure, validate). The span tree shows which step got slow or errored; say the
firmware span's duration ballooned. I follow the span's trace_id into the logs (structured,
correlated) to see the specific error — e.g., the BMC returning 503s. I cross-check the
metrics for that node/BMC (up, scrape latency, BMC error rate) to confirm it's the device,
not my code. So: metric alert → trace to localize the step → logs to get the specific failure →
metrics to confirm scope. That metrics→traces→logs correlation, tied by IDs, is exactly why you
invest in all three pillars (and it's the Phase 10 RCA loop).
References
- Prometheus docs — data model, exposition format, metric types, PromQL — https://prometheus.io/docs/
- Prometheus — instrumentation & naming best practices; histograms vs summaries — https://prometheus.io/docs/practices/
- Brian Brazil, Prometheus: Up & Running — the canonical book
- Grafana docs — dashboards, variables, provisioning (as-code) — https://grafana.com/docs/
- OpenTelemetry — traces/metrics/logs, context propagation, the collector — https://opentelemetry.io/docs/
- Google SRE Book — SLIs/SLOs/error budgets; "alerting on symptoms" — https://sre.google/books/
- The USE method (Brendan Gregg) & the RED method (Tom Wilkie) — dashboard frameworks
- redfish_exporter / snmp_exporter / ipmi_exporter — reference exporter implementations
- Cross-track: Head of SWE — GPU, Phase 10 (observability/production)
🛸 Hitchhiker's Guide — Phase 07: Telemetry & Observability
Read this if: "Prometheus, Grafana, tracing" is a stack you've seen but not built. Compressed map of metrics, the cardinality trap, and the metrics→traces→logs loop.
0. The 30-second mental model
Three pillars: metrics (cheap time-series — that something's wrong), logs (the detail — what), traces (the path — where the time went / what failed). Tie them by shared IDs and you can debug a fleet. The role-specific hard part is unification: one exporter normalizing Redfish/IPMI/SNMP/CDU into consistent metrics. Two laws keep you sane: bound your label cardinality and alert on symptoms, not causes.
1. Prometheus in one breath
Pull model: Prometheus scrapes /metrics on a schedule → TSDB. A series = name{labels}.
Pull (not push) because the system owns the target list (so up==0 is a signal), no push
infra to overwhelm, and curl /metrics shows what it sees. Push only for batch jobs
(Pushgateway) or across boundaries (remote-write).
2. Metric types (pick right)
| type | meaning | rack example | query |
|---|---|---|---|
| counter | only goes up | pcie_correctable_errors_total | rate(x[5m]) |
| gauge | up & down | node_temp_celsius, node_power_watts, cdu_delta_t_celsius | raw / avg by |
| histogram | distribution | provisioning_step_duration_seconds | histogram_quantile(0.99,...) |
Exposition format: name{label="v"} 123. Only-up→counter; up/down→gauge; need p99→histogram.
3. The exporter pattern (the unification job)
Hardware speaks Redfish/IPMI/SNMP; Prometheus speaks HTTP. An exporter bridges it — use the
Phase 03 driver abstraction, poll devices on your own schedule (cache, retry, timeout — BMCs
are fragile), and serve consistent metrics so a temp is node_temp_celsius regardless of
source. Add an up/health metric per device.
4. PromQL you'll actually write
avg by (rack) (node_temp_celsius) # aggregate
sum by (rack) (node_power_watts) > 17000 # over the Phase 01 budget
rate(pcie_correctable_errors_total[5m]) > 1 # error velocity
up{job="rack-exporter"} == 0 # device/exporter down
Recording rules = precompute; alerting rules = PromQL + for: + severity + runbook → Alertmanager.
5. The cardinality law
series = product of label values. Never put unbounded values (IDs, timestamps, messages) in labels → OOM. Labels = bounded dimensions (rack/node/sensor/severity). Detail → logs/traces.
6. Grafana dashboards
JSON (version it). $rack/$node variables. USE (Utilization/Saturation/Errors) for
hardware, RED (Rate/Errors/Duration) for services. Hierarchy: fleet → rack → node. Every
panel answers a question or backs an alert.
7. Traces + correlation (the debug loop)
Trace = tree of spans (name, duration, parent) sharing a trace_id; context propagation carries it across calls/services. Logs carry the trace_id. The loop: metric alert → traces in window (exemplars) → failing span → its logs. OTel = the vendor-neutral standard; the collector exports to Jaeger/Tempo/Prometheus.
8. SLO discipline
SLI (measured) → SLO (target) → error budget (1−SLO). Alert on symptoms (SLO breach), not causes — cause alerts → fatigue → missed real page. Every page is actionable + has a runbook (Phase 12).
9. The "done this before" tells
"Counter or gauge?" · "What's the cardinality of that label?" · "Are we alerting on a symptom or a cause?" · "Does the log carry the trace_id?" · "Pull or push for that?" · "What's the SLO?"
10. How this phase pays off later
The exporter is reused in the Phase 13 capstone; alerts/runbooks feed Phase 10 RCA and
Phase 12 ops; the fleet pipeline is system-design/02; thresholds come from Phases 01/04.
Lab 01 — Unified Rack Telemetry → Prometheus Exporter
Phase: 07 — Telemetry & Observability | Difficulty: ⭐⭐⭐☆☆ | Time: 4–6 hours Language: Python 3 (stdlib) + real Prometheus/Grafana config | Hardware: none
Concept primer:
../WARMUP.mdCh. 3–7,../HITCHHIKERS-GUIDE.md§1–§6.
Run
python3 solution.py # starts the exporter, prints /metrics, self-tests
# in another shell while it runs: curl localhost:<port>/metrics
Files
solution.py— the exporter (serves real Prometheus exposition format on/metrics).alerts.yml— Prometheus alerting rules (the rack symptoms you page on).dashboard.json— a Grafana dashboard-as-code (fleet→rack hardware view).
0. The mission
Build the "unified telemetry pipeline" the JD asks for: one exporter that turns normalized Redfish/IPMI/SNMP rack telemetry (Phase 03) into Prometheus metrics, with the alert rules and Grafana dashboard that make it operable. The point is unification — one consistent metric/label scheme regardless of which protocol the data came from.
1. What to study in the output
- Metric types: temp/power are gauges, AER errors a counter (queried as a
rate), and a per-nodeupgauge — the right type for each signal (WARMUP Ch. 3). - Bounded labels only:
rack,node,sensor,job— the self-test asserts no other labels exist, so cardinality can't explode (WARMUP Ch. 6). No serials, IDs, or timestamps in labels. - No stale data for down devices: node-02 is unreachable →
up=0and we don't emit a fake temperature for it. - The hot accelerator (92°C) is exactly what
alerts.yml'sAcceleratorHot(>85, for 5m) fires on; node-01's power pushes toward theRackPowerOverBudgetrule (the Phase 01 budget).
2. The real config
alerts.yml shows symptom-based alerting (for: to avoid flapping, severity, runbook links,
and rate for PCIe errors not raw count). dashboard.json shows a $rack variable and USE-
style panels (nodes up/down, power vs budget threshold, accel temps with 85/95 thresholds, PCIe
error rate).
3. Extensions (do them)
- Run the real stack:
docker run prom/prometheuswith a scrape config pointing at this exporter, loadalerts.yml, rundocker run grafana/grafana, add Prometheus as a data source, and importdashboard.json. Watch the alert fire on the 92°C accelerator. - Wire it to Phase 04: pull live PDU/CDU/PCIe values from the Phase 04 Lab 02 simulator so the metrics reflect ΔT, leak state, and degraded links.
- Add a histogram:
scrape_duration_secondsandprovisioning_step_duration_seconds; query p99 withhistogram_quantile. - Background polling + cache: poll the (fake) BMCs on a timer with retries/timeouts (Phase 02) and serve cached values on scrape — the production pattern for fragile BMCs.
- Cardinality experiment: add a high-cardinality label (a timestamp) and observe how the series count would explode; then remove it and explain why.
4. What this lab proves
You can build the metrics backbone of a rack-management platform — correctly typed, cardinality-
safe, unified across protocols, alertable, and dashboardable. "Design a telemetry pipeline for a
rack fleet" becomes a demonstration, and this exporter is reused in the Phase 13 capstone and
expanded in system-design/02 (the fleet-scale pipeline).
Lab 02 — OpenTelemetry Tracing + Structured Logging
Phase: 07 — Telemetry & Observability | Difficulty: ⭐⭐⭐☆☆ | Time: 3–5 hours Language: Python 3 (stdlib) | Hardware: none
Concept primer:
../WARMUP.mdCh. 8,../HITCHHIKERS-GUIDE.md§7.
Run
python3 solution.py # a traced workflow (success + failure) with correlated logs
0. The mission
Make a multi-step rack workflow debuggable: emit a tree of spans with timings and a propagated trace context, and structured logs that carry the active trace_id — so "where did it fail and how long did each step take?" is answerable in seconds, and any log line links to its span. This is the deep-dive half of observability (metrics flag that; traces show where).
1. What to study
- Context propagation: a
contextvarscurrent-span carries the trace across nestedwith span(...)blocks — no manual passing — exactly like OpenTelemetry's implicit context. - The span tree:
provisionis the root;discover/firmware/os_install/configure/validateare children sharing onetrace_id. The printed tree shows per-stepduration_ms. - Failure pinpointing: node-B's
os_installspan is marked✗ ERROR, and the error propagates to the root span — you instantly see which step failed and how long the others took. - Log↔trace correlation: every
log(...)line auto-includes the activetrace_id/span_id, so from a log you jump to the trace and vice-versa.
2. Extensions (do them)
- Use real OpenTelemetry:
pip install opentelemetry-sdk opentelemetry-exporter-otlp, replace the toy tracer, and export OTLP to Jaeger or Tempo (docker run jaegertracing/ all-in-one); view the trace tree in the UI. - Cross-service propagation: split the workflow across a real HTTP call and propagate the
traceparentheader so the trace spans both processes. - Metric exemplars: link a slow
provisioning_step_duration_secondssample (Lab 01) to its trace, so a dashboard spike jumps straight to the trace (the metrics→traces leap). - Sampling: add head/tail sampling and discuss the cost/visibility tradeoff at fleet scale.
3. What this lab proves
You can instrument a workflow so production failures are diagnosable — the JD's "logging, metrics, and tracing... to support troubleshooting." "Latency spiked — find why" becomes the metrics→traces→logs loop (Phase 10 RCA), demonstrated end to end. Combined with Lab 01's metrics/alerts, you have the full observability surface a rack platform needs.
Phase 08 — Firmware Lifecycle, Crash Dumps & RAS Diagnostics
Difficulty: ⭐⭐⭐⭐☆ | Estimated Time: 2 weeks Roles supported: Rack Management SWE (Senior/Staff), Firmware/Platform SWE, RAS/Reliability SWE Hardware needed: none — firmware orchestrator + RAS log parser run as simulators
Why This Phase Exists
The JD names this directly: "Support firmware updates, crash dump parsing, and remote diagnostics across rack components." This is some of the highest-stakes software a rack engineer writes: a botched firmware update can brick a $300k node from a continent away, and the ability to read a crash dump or RAS log is the difference between "RMA the whole node" and "it's a single DIMM / a degraded PCIe lane." It's also where the JD's repeated theme — debugging complex issues in deployed production systems — lives.
Two disciplines:
- Firmware lifecycle — updating BIOS/UEFI, BMC, NIC, accelerator, PSU, and PCIe-switch firmware safely across a fleet: staging, signature verification, A/B (redundant) banks, rollback, and the Redfish Task model (Phase 03).
- RAS (Reliability, Availability, Serviceability) — reading the hardware's own error telemetry: PCIe AER, CPU MCE/MCA, ACPI/APEI, the BMC SEL, accelerator crash/Xid-style logs — and turning a flood of cryptic records into an actionable root cause.
You'll build a firmware-update orchestrator with rollback and a RAS/crash-dump parser that classifies errors into a diagnostics verdict.
Concepts
Firmware lifecycle
- The firmware zoo: BIOS/UEFI, BMC, NIC/DPU, accelerator, PSU, PCIe switch, CPLD — each independently versioned (the firmware EBOM baseline, Phase 12)
- Update mechanisms: Redfish UpdateService (SimpleUpdate, multipart push) → a Task (Phase 03 Ch. 4); vendor tools; in-band vs out-of-band updates
- A/B (redundant) banks: update the inactive bank, switch on success, fall back on failure
- Staging, verification, rollback: download → verify signature/checksum → stage → apply → verify → activate (often a reboot) → confirm → rollback on failure
- Firmware signing & secure update: signed images, anti-rollback (don't downgrade past a security fix), the chain of trust (pairs with Phase 09 secure boot)
- Fleet rollout: ordering/dependencies (BMC before BIOS?), canary, drain-first (Phase 06), maintenance windows, the cost of a fleet-wide bad firmware
- Failure modes: bricking, partial update, power loss mid-flash, version skew
RAS — reading the hardware's pain
- PCIe AER (Advanced Error Reporting): correctable vs uncorrectable (fatal/non-fatal); decoding the error bits; a rising correctable rate as a pre-failure signal (Phase 04)
- CPU MCE/MCA (Machine Check Exception / Architecture): decoding a machine-check bank; correctable ECC vs uncorrectable; which component (memory/cache/interconnect)
- ACPI/APEI (Platform Error Interfaces): how firmware reports hardware errors to the OS (HEST/BERT/ERST); the BMC's role
- The SEL (Phase 03) and BMC crash dumps; accelerator crash logs (Xid-style codes)
- Memory errors: correctable ECC (logged, page-offline candidate) vs uncorrectable (fatal); DIMM sparing; predictive failure
- Remote diagnostics: SoL, crash capture, core/kernel dumps (kdump), collecting the evidence OOB when the host is dead
The synthesis (the senior layer)
- Turning many raw error records into one classified diagnosis (which FRU, how severe, what action) — the parser you build
- Correlation with telemetry (Phase 07) and failure domains (Phase 01) for fleet RCA (Phase 10)
- The decision tree: log-only / page / cordon+drain / RMA the FRU
Labs
Lab 01 — Firmware Update Orchestrator (staged + rollback)
| Field | Value |
|---|---|
| Goal | Build an orchestrator that updates a component's firmware safely: verify signature → stage to the inactive A/B bank → activate → verify → roll back on failure, modeled on the Redfish Task lifecycle. |
| Concepts | Redfish UpdateService/Task, A/B banks, signature/checksum verification, anti-rollback, staged apply, rollback, idempotency. |
| Steps | 1) python3 solution.py — happy path, a bad-signature reject, a failed-activation rollback, and an anti-rollback (downgrade) block. 2) Read the state machine. 3) Extensions (real Redfish UpdateService). |
| Stack | Python 3 stdlib (hashlib/hmac for signing) |
| Output | A firmware orchestrator demonstrating verify→stage→activate→verify→rollback with A/B banks. |
| How to Test | Asserts verify: good update activates; bad signature is rejected; failed activation rolls back to the previous bank; a downgrade is blocked by anti-rollback. |
| Talking Points | Why A/B banks exist; why you verify before and after activate; anti-rollback vs the need to recover; why firmware returns a Task; the cost of a bad fleet rollout. |
| Resume Bullet | "Built a firmware-update orchestrator with signature verification, A/B-bank staging, post-activation validation, automatic rollback, and anti-rollback protection, modeled on the Redfish UpdateService/Task lifecycle." |
| Extensions | Drive a real Redfish UpdateService (SimpleUpdate) + poll the Task; add fleet canary + drain-first (Phase 06); add a power-loss-mid-flash recovery model. |
Lab 02 — Crash-Dump & RAS Log Parser
| Field | Value |
|---|---|
| Goal | Parse and classify hardware error logs — PCIe AER, CPU MCE, SEL, accelerator crash codes — into a single diagnostics report: which FRU, severity, and recommended action. |
| Concepts | PCIe AER decode, MCE/MCA banks, correctable vs uncorrectable, SEL events, the diagnosis decision tree, correlation. |
| Steps | 1) python3 solution.py — parse mixed RAS logs into a classified report. 2) Read the AER/MCE decoders and the action decision tree. 3) See a rising-correctable-rate predictive flag. 4) Extensions (real dmesg/mcelog). |
| Stack | Python 3 stdlib |
| Output | A diagnostics report classifying each error and recommending log-only / monitor / cordon / RMA. |
| How to Test | Asserts verify AER/MCE decode, correctable-vs-uncorrectable severity, predictive flag on rising correctable rate, and the recommended action per case. |
| Talking Points | Correctable vs uncorrectable; why a rate of correctable errors matters; mapping an error to a FRU; when to RMA vs page vs ignore; degraded link (Phase 04) in the logs. |
| Resume Bullet | "Built a RAS/crash-dump parser decoding PCIe AER and CPU MCE/SEL records into FRU-level diagnoses with severity classification and remediation recommendations, including predictive failure from correctable-error rates." |
| Extensions | Parse real dmesg/mcelog/ras-mc-ctl output; correlate with Phase 07 telemetry; feed verdicts into the Phase 10 RCA workflow; auto-open an RMA ticket. |
Deliverables Checklist
- You can list the firmware components on a node and explain the EBOM baseline
- Orchestrator verifies signatures, uses A/B banks, validates after activation, and rolls back
- You can explain anti-rollback and the tension with needing to recover from a bad update
- You can explain why firmware updates return a Redfish Task and how you'd run a fleet rollout
- You can decode a PCIe AER correctable vs uncorrectable error and say what each means
- You can explain MCE/MCA and correctable-ECC vs uncorrectable memory errors
- Parser produces a FRU-level diagnosis with severity and a recommended action
- You can explain when to log-only vs page vs cordon vs RMA
Interview Relevance
- "How do you safely update firmware on 1,000 nodes without bricking them?"
- "What are A/B firmware banks and why do they exist?"
- "Walk me through verifying and rolling back a firmware update."
- "What is PCIe AER? Correctable vs uncorrectable — what do you do about each?"
- "A node logs a flood of correctable ECC errors. Is that a problem?" (rate → predictive)
- "How do you diagnose a node that crashed in production, remotely?" (SEL, kdump, RAS, SoL)
- "How do you decide whether to RMA a component?"
Warmup Guide — Firmware Lifecycle, Crash Dumps & RAS Diagnostics
Zero-to-expert primer for Phase 08. You need no firmware/RAS background: this guide starts at "what is firmware and why is updating it dangerous" and ends with you designing a safe fleet firmware rollout and reading a PCIe AER / MCE crash dump to a FRU-level diagnosis. This is the highest-stakes, most "debug the deployed system" part of the role.
Table of Contents
- Chapter 1: The Firmware Zoo and Why Updates Are Dangerous
- Chapter 2: Update Mechanisms and the Redfish Task Model
- Chapter 3: A/B Banks, Staging, Verification, Rollback
- Chapter 4: Firmware Signing & Anti-Rollback
- Chapter 5: Fleet Rollout Strategy
- Chapter 6: RAS — How Hardware Reports Its Pain
- Chapter 7: Decoding PCIe AER and CPU MCE
- Chapter 8: Remote Diagnostics and the Decision Tree
- Lab Walkthrough Guidance
- Success Criteria
- Interview Q&A
- References
Chapter 1: The Firmware Zoo and Why Updates Are Dangerous
Firmware is the software baked into a hardware component that makes it work. A single AI node has many independently-firmwared components, each a thing you may need to update:
- BIOS/UEFI (host platform firmware), BMC (the management controller itself, Phase 04),
- NIC/DPU firmware, accelerator firmware, PSU firmware, PCIe switch firmware, CPLD, drive firmware.
The set of correct versions across all of these is the firmware baseline — part of the software EBOM you agree with the ODM (Phase 12). Keeping a fleet at a known baseline is a core rack-management job (and the Phase 01 commissioning step).
Why updates are dangerous — this is the framing that makes you careful:
- A failed firmware flash can brick the component (and sometimes the node) — and you're doing it to a machine you can't physically touch, possibly on another continent.
- The riskiest target is the BMC itself: if you brick the BMC, you lose your OOB lifeline to the node (Phase 04) — the very tool you'd use to recover it.
- Power loss or a crash mid-flash can corrupt the firmware image.
- A bad image deployed fleet-wide is a catastrophe at the scale of "thousands of nodes offline." Firmware rollouts are where a single bug becomes a major incident.
So the entire discipline of this phase is making an inherently dangerous operation safe: redundancy (A/B banks), verification (signatures, before and after), reversibility (rollback), and caution (staging, canary, drain-first). Respect for this danger is itself a senior signal.
Chapter 2: Update Mechanisms and the Redfish Task Model
How an update is delivered:
- Redfish UpdateService (the modern standard, Phase 03):
SimpleUpdate(point the service at an image URL) or a multipart HTTP push (upload the image directly). Because flashing is slow, the action returns HTTP 202 + a Task (Phase 03 Ch. 4) — you poll the Task'sTaskState/PercentCompleteto completion. A client that blocks on a synchronous response is wrong. - Vendor tools / in-band agents: some components update from inside the host OS (an agent flashes the NIC/accelerator). In-band is faster but requires a healthy host; OOB (via the BMC) works even on a sick node and is preferred for platform firmware.
- Activation: many updates only take effect after a reboot or an explicit activation step — so the flow is apply → activate (reboot) → verify the new version is running.
The Task model also gives you progress and result messages, which your orchestrator surfaces to operators (Phase 07) and uses to decide success/failure.
Chapter 3: A/B Banks, Staging, Verification, Rollback
The core safety mechanism, and the heart of Lab 01.
A/B (redundant) banks: the component has two firmware slots. One is active (running); the other is inactive. You flash the inactive bank, then switch the active pointer to it on success. If the new firmware fails to boot or fails validation, you switch back to the known-good bank. This means a bad flash never destroys your working firmware — the single most important firmware-safety idea. (Not every component has A/B; where it doesn't, the risk and the need for caution are higher.)
The safe update state machine (what your orchestrator implements):
- Download the image (to a staging area).
- Verify the image: checksum + signature (Chapter 4) + version/compatibility + anti-rollback. Reject before touching the device if anything fails.
- Stage: flash the inactive bank. (Active firmware still running — no disruption yet.)
- Activate: switch the active pointer / reboot into the new bank.
- Verify post-activation: confirm the device booted, reports the new version, and is healthy (sensors nominal, links up — Phase 04). Verify after, not just before — a flash can succeed but the firmware misbehave.
- Commit or rollback: if healthy, commit; if not, switch back to the previous bank (and reboot), restoring the known-good state.
Idempotency & resumability (Phase 05 again): the orchestrator tracks state so a crash mid-update resumes safely, and re-running a completed update is a no-op.
Chapter 4: Firmware Signing & Anti-Rollback
Firmware is a prime attack target (malicious firmware is persistent and below the OS — Phase 09). So:
- Signed images: the vendor signs the firmware; the component (or your orchestrator) verifies the signature against a trusted public key before flashing. An unsigned/tampered image is rejected. (Lab 01 models this with HMAC/signature verification.)
- The chain of trust: signed firmware ties into secure boot (Phase 09) — each stage verifies the next. A compromised firmware update would break the chain.
- Anti-rollback: you must not be able to downgrade firmware past a version that fixed a security vulnerability, or an attacker would "update" you to a known-exploitable old version. Components track a minimum version (a monotonic counter); the orchestrator enforces it.
- The tension (a good interview point): anti-rollback protects security but can trap you on a bad new version. The resolution is the A/B bank (roll back to the other good bank, not a downgrade to an old vulnerable one) plus a vendor recovery path for genuine emergencies.
Chapter 5: Fleet Rollout Strategy
Updating one node safely is Lab 01; updating ten thousand without a fleet-wide incident is the
Staff problem (and system-design/03).
- Ordering/dependencies: some firmware must update in a specific order (e.g., BMC before BIOS if the new BIOS needs new BMC support; or a NIC firmware that requires a driver version). Encode dependencies; don't flash blindly.
- Drain first (Phase 06): cordon + drain the node (move tenant workloads off) before an update that requires a reboot — never reboot a node running tenant work.
- Canary + progressive rollout (Phase 11): update 1 node, bake/validate, then 1%, then 10%, then the rest — with automatic halt-and-rollback if health/SLOs (Phase 07) regress. A bad image caught at 1% is an annoyance; at 100% it's an outage.
- Failure-domain awareness (Phase 01): don't update a whole power/cooling/ToR domain at once (correlated downtime).
- Maintenance windows & comms: tenant-visible disruption is scheduled and communicated.
- Observability: track version distribution across the fleet (a Phase 07 metric:
firmware_version{component,version}), so drift and rollout progress are visible.
The mental model: a firmware rollout is a deployment with the blast radius of physical
hardware — apply software-deployment discipline (canary, rollback, observability) with extra
caution because you can't just kubectl rollout undo a bricked BMC.
Chapter 6: RAS — How Hardware Reports Its Pain
RAS = Reliability, Availability, Serviceability: the hardware/firmware machinery that detects, logs, and sometimes corrects errors, and the software that reads it. This is how you "debug the deployed system" when something physical goes wrong. The sources:
- PCIe AER (Advanced Error Reporting): errors on the PCIe fabric (Phase 04) —
correctable (recovered by the link layer; a rising rate predicts failure) vs
uncorrectable (fatal or non-fatal; data integrity at risk). Reported via the OS
(
dmesg/sysfs) and/or the BMC. - CPU MCE/MCA (Machine Check Exception / Architecture): the CPU's error reporting — memory ECC errors, cache errors, interconnect errors — organized into banks you decode to find which subsystem erred. Correctable (logged) vs uncorrectable (often a crash).
- ACPI/APEI (Platform Error Interfaces — HEST/BERT/ERST): how platform firmware reports hardware errors to the OS, including a BERT (Boot Error Record Table) that surfaces the cause of the last crash after a reboot — gold for diagnosing a node that mysteriously reset.
- The SEL (Phase 03): the BMC's event log — PSU input lost, thermal trips, DIMM thresholds.
- Accelerator crash logs: vendor error codes (NVIDIA's Xid is the canonical example) for GPU/accelerator faults — ECC errors, falls-off-the-bus, thermal, app errors.
- Memory errors specifically: correctable ECC (single-bit, auto-fixed — but a DIMM throwing many is a candidate for page-offlining or replacement) vs uncorrectable (a multi-bit error — data loss, usually fatal).
- kdump: the Linux mechanism that captures a kernel crash dump (vmcore) into reserved memory on panic — your evidence for a host kernel crash.
Chapter 7: Decoding PCIe AER and CPU MCE
The concrete skill (Lab 02). The raw records are cryptic; decoding them to meaning is the value.
PCIe AER: an error is reported with a class and a set of status bits. The decode you need:
- Correctable (e.g., Receiver Error, Bad TLP, Bad DLLP, Replay Timer Timeout): the link recovered. One is noise; a high rate on one device signals a marginal lane/cable/connector — a pre-failure indicator (ties to Phase 04 degraded links).
- Uncorrectable non-fatal (e.g., Completion Timeout, Completer Abort): the transaction failed but the link survives; the device/driver may recover or error up.
- Uncorrectable fatal (e.g., Malformed TLP, link errors): the link is unreliable; often a device reset or node impact. The BDF (Phase 04) tells you which device.
CPU MCE: a machine check is decoded from the bank number + status/address registers:
- The bank identifies the subsystem (memory controller, cache, interconnect).
- Correctable (CE): logged, system continues (e.g., a corrected DRAM ECC error → which DIMM, via the address). A rising CE rate on one DIMM → predictive replacement / page-offline.
- Uncorrectable (UC): data integrity lost; often a panic/crash. The bank+address point to the failing FRU.
The parser's job (Lab 02): take a mixed stream of AER/MCE/SEL records, decode each to a human meaning, map it to a FRU (which DIMM, which PCIe device, which PSU), assign severity, detect predictive patterns (rising correctable rates), and emit a recommended action. That turns "500 lines of dmesg horror" into "DIMM A2 has a rising correctable-ECC rate — schedule replacement; PCIe device 0000:13:00.0 had one correctable error — monitor."
Chapter 8: Remote Diagnostics and the Decision Tree
When a node misbehaves in production and you can't walk up to it, your evidence is all OOB/ remote:
- SoL (Phase 04): watch the console for a panic/POST error in real time.
- The SEL + BERT: read what the BMC logged and what caused the last crash after a reboot.
- kdump vmcore: analyze the kernel crash dump (with
crash/drgn) for a host panic. - RAS logs (AER/MCE): the parser's input.
- Telemetry (Phase 07): correlate with temperature/power/error-rate trends around the event.
- Failure domain (Phase 01): is this one node or a shared-resource event?
The decision tree your software (and you) applies to a diagnosis:
- Log-only: a single correctable error / known-benign event → record, don't alert.
- Monitor/predictive: a rising correctable rate → flag the FRU for proactive replacement at the next window (avoid an outage).
- Cordon + drain (Phase 06): a degrading-but-up node → take it out of service for tenants.
- RMA the FRU: an uncorrectable/persistent fault mapped to a specific part → replace it (a DIMM, an accelerator, a PSU, a cable) and re-validate (Phase 01 commissioning), then return to service. Replace the FRU, not the whole node — that's the serviceability payoff of good diagnosis.
This decision tree is the bridge to Phase 10 (systematic RCA) and Phase 12 (runbooks). The whole point of RAS software is to make the right one of these choices automatically or with minimal human effort, at fleet scale.
Lab Walkthrough Guidance
Order: Lab 01 (firmware) → Lab 02 (RAS). Firmware is the "act safely" half; RAS is the "diagnose" half — together they're the JD's firmware/crash-dump/diagnostics line.
Lab 01 (firmware orchestrator):
python3 solution.py— watch the happy path, a bad-signature reject, a failed-activation rollback (A/B bank restored), and an anti-rollback (downgrade) block.- Read the state machine: verify-before → stage-to-inactive → activate → verify-after → commit/ rollback.
- Extensions: drive a real Redfish UpdateService + poll the Task; add canary + drain-first.
Lab 02 (RAS parser):
python3 solution.py— parse a mixed AER/MCE/SEL stream into a classified report.- See the predictive flag on a rising correctable-ECC rate and the action per record.
- Extensions: parse real
dmesg/mcelog/ras-mc-ctl; correlate with Phase 07 telemetry.
Phase capstone question: "A node in production reboots randomly under load. Walk me from the symptom to a FRU-level root cause and the fix — remotely." (Answer: read the SEL + BERT for the last-crash cause; pull AER/MCE via the parser; if MCE points at a DIMM with rising correctable ECC under thermal load → predictive DIMM fault; cordon+drain, schedule DIMM replacement, re-validate, return to service — all OOB, no site visit.)
Success Criteria
You're done with this phase when — without notes:
- You can list a node's firmware components and explain the baseline/EBOM (Ch. 1)
- You can explain why firmware updates are dangerous and why the BMC is the riskiest target (Ch. 1)
- You can describe the Redfish UpdateService/Task update flow (Ch. 2)
- You can explain A/B banks and the verify→stage→activate→verify→rollback state machine (Ch. 3)
- You can explain signing and anti-rollback and the tension it creates (Ch. 4)
- You can design a safe fleet rollout (canary, drain-first, domain-aware, rollback) (Ch. 5)
- You can decode PCIe AER (correctable vs uncorrectable) and CPU MCE to a FRU (Ch. 6–7)
- You can apply the log/monitor/cordon/RMA decision tree to a diagnosis (Ch. 8)
Interview Q&A
Q1: How do you safely update firmware across a fleet without bricking nodes?
A: Per node, a verify-first, reversible state machine on A/B banks: download → verify
(checksum + signature + version/compat + anti-rollback) before touching the device → flash the
inactive bank (active firmware keeps running) → activate (switch pointer/reboot) → verify
after (new version reported, sensors/links healthy — Phase 04) → commit, or roll back to the
previous bank on any failure. Because flashing is slow it's driven through the Redfish
UpdateService as a Task I poll. Across the fleet: drain the node first (Phase 06) for reboots,
respect firmware dependencies (e.g., BMC before BIOS), and roll out progressively — 1 node → 1%
→ 10% → rest — with automatic halt-and-rollback if health/SLOs regress, never updating a whole
failure domain at once. The mindset is that a firmware rollout is a deployment with the blast
radius of physical hardware, so I apply canary/rollback/observability discipline with extra
caution because I can't rollout undo a bricked BMC.
Q2: What are A/B firmware banks and why do they matter? A: A component with A/B banks has two firmware slots — one active (running), one inactive. I flash the inactive bank and only switch the active pointer to it after the new firmware validates; if it fails to boot or fails health checks, I switch back to the known-good bank. The point is that a bad flash never destroys my working firmware, so recovery is a pointer switch + reboot rather than a brick + truck roll. It's the single most important firmware-safety mechanism, especially for the BMC, because bricking the BMC would cost me the OOB lifeline I'd use to recover the node.
Q3: Signed firmware and anti-rollback — what's the tension and how do you resolve it? A: Firmware is signed by the vendor and verified before flashing, so tampered/unsigned images are rejected — and it ties into the secure-boot chain of trust (Phase 09). Anti-rollback prevents downgrading past a version that fixed a security hole, so an attacker can't "update" you to a known-exploitable old version; components enforce a monotonic minimum version. The tension is that anti-rollback could trap you on a bad new release. The resolution is the A/B bank — you roll back to the other good bank (the prior version you were already running), not a downgrade to an old vulnerable image — plus a vendor-authenticated recovery path for genuine emergencies. So you get both security and recoverability.
Q4: What is PCIe AER, and what do you do about correctable vs uncorrectable errors? A: AER (Advanced Error Reporting) is PCIe's mechanism for reporting fabric errors per device (by BDF). Correctable errors (Bad TLP, Replay Timeout, Receiver Error) were recovered by the link layer — one is noise, but a high rate on one device signals a marginal lane/cable/connector, a pre-failure indicator I'd flag for proactive service (and it correlates with a degraded link from Phase 04). Uncorrectable errors are worse: non-fatal (e.g., completion timeout) means the transaction failed but the link survives and the driver may recover; fatal (malformed TLP, link errors) means the link is unreliable and usually forces a reset or impacts the node. The BDF tells me which device, so I map it to a FRU and decide monitor vs cordon vs RMA.
Q5: A node logs a flood of correctable ECC memory errors. Is that a problem? A: A single correctable ECC error is benign — the ECC fixed it. But a flood, or a rising rate on one DIMM, is a strong predictive-failure signal: that DIMM is degrading and will likely throw an uncorrectable (fatal) error soon. So I don't ignore it and I don't panic-RMA on one event — I decode the MCE to identify the specific DIMM (via the bank + address), flag it for proactive replacement at the next maintenance window, and consider page-offlining the affected pages to reduce exposure. That's the serviceability win: catch it predictively, replace one DIMM in a window, and avoid an unplanned crash. It's exactly the "rate, not raw count" lesson from Phase 07 alerting applied to hardware.
Q6 (Staff): A fleet of nodes started crashing after a firmware rollout. What do you do, and how do you prevent it next time? A: First, stop the bleeding: halt the rollout immediately and roll back the affected nodes to the previous A/B bank (the reason A/B + progressive rollout exist). Quantify blast radius via the fleet firmware-version metric (Phase 07) and correlate the crashes (RAS parser: common MCE/AER/ SEL signature? same component/firmware version?) to confirm causation, not coincidence. Drain and recover affected nodes, validate them back to baseline, and communicate to stakeholders. Then the prevention/RCA (Phase 10/12): the rollout should have been caught at the 1% canary with automated health/SLO gates — so I'd tighten the canary criteria and bake time, add the specific failure signature to the pre-promotion checks, expand the conformance/HIL test matrix (Phase 11) to cover the condition that slipped through (likely a hardware/firmware combo we didn't test), and feed the defect back to the vendor/ODM (Phase 12). The Staff lesson is that the process (canary, gates, rollback, version observability) is what bounds the damage of an inevitable bad firmware — and a blameless postmortem that hardens the process is the deliverable, not just the fix.
References
- DMTF Redfish — UpdateService, SimpleUpdate, multipart push, Tasks — https://www.dmtf.org/standards/redfish
- UEFI/PI specs & firmware update (capsule update), A/B / fault-tolerant update concepts — https://uefi.org/specifications
- NIST SP 800-147 / 800-193 — Platform Firmware Resiliency (signing, recovery, anti-rollback) — https://csrc.nist.gov/
- PCIe AER — PCI Express Base Spec; Linux PCIe AER docs — https://docs.kernel.org/PCI/pcieaer-howto.html
- Linux EDAC /
mcelog/rasdaemon(ras-mc-ctl) — memory/MCE error reporting — https://github.com/mchehab/rasdaemon - ACPI APEI (HEST/BERT/ERST) — ACPI spec; Linux APEI docs
- Linux
kdump/crash/drgn— kernel crash dump capture and analysis - NVIDIA Xid errors (accelerator crash codes, as a canonical example) — https://docs.nvidia.com/deploy/xid-errors/
- Cross-track: Security Engineer — hardware/firmware device security
🛸 Hitchhiker's Guide — Phase 08: Firmware, Crash Dumps & RAS
Read this if: "firmware updates and crash-dump parsing" sounds scary. It should — this is where you can brick a node or save a fleet. Compressed map of safe updates and reading hardware pain.
0. The 30-second mental model
Two jobs: update firmware without bricking (A/B banks + verify-before-and-after + rollback,
canaried across the fleet) and read RAS (PCIe AER, CPU MCE, SEL, accelerator codes) to a
FRU-level diagnosis so you replace one DIMM, not a whole node. The mindset: a firmware
rollout is a deployment with the blast radius of physical hardware — you can't rollout undo a
bricked BMC.
1. The firmware zoo
BIOS/UEFI · BMC · NIC/DPU · accelerator · PSU · PCIe switch · CPLD · drives. Each is independently versioned; the correct set = the baseline / EBOM (Phase 12). The BMC is the riskiest target — brick it and you lose your OOB lifeline.
2. The safe update state machine
download → VERIFY (checksum+signature+version+anti-rollback) [reject here, before touching HW]
→ stage to INACTIVE A/B bank [active firmware still running]
→ activate (switch pointer / reboot)
→ VERIFY AFTER (new version + healthy: sensors/links)
→ commit OR ROLLBACK to previous bank
Verify before and after. A/B means a bad flash never destroys the good firmware. Idempotent + resumable (Phase 05).
3. Redfish update = a Task
POST UpdateService (SimpleUpdate / multipart) → 202 + Task → poll TaskState/PercentComplete. Activation usually needs a reboot. Don't block on a synchronous response.
4. Signing & anti-rollback
Signed images (verify vs trusted key) tie into secure boot (Phase 09). Anti-rollback blocks downgrading past a security fix. Tension (trapped on bad new version) → resolved by A/B (roll back to the other good bank, not an old vulnerable one) + vendor recovery path.
5. Fleet rollout
drain-first (Phase 06) for reboots · dependency order (BMC before BIOS) · canary 1→1%→10%→
rest with auto halt-and-rollback on SLO/health regress (Phase 07) · don't update a whole
failure domain at once (Phase 01) · track firmware_version{component,version} as a metric.
6. RAS sources (reading hardware pain)
| source | what | key split |
|---|---|---|
| PCIe AER | fabric errors per BDF | correctable (rate=pre-failure) vs uncorrectable (fatal/non-fatal) |
| CPU MCE/MCA | memory/cache/interconnect | correctable ECC vs uncorrectable; bank→subsystem, addr→DIMM |
| ACPI/APEI | firmware→OS error report; BERT = last-crash cause | post-reboot diagnosis |
| SEL | BMC event log | PSU/thermal/DIMM events |
| accelerator (Xid) | GPU/accel faults | ECC / off-the-bus / thermal / app |
7. Decode to a FRU + an action
Parser job: decode each record → map to a FRU (which DIMM/device/PSU) → severity → predictive flag (rising correctable rate) → action. Decision tree:
single correctable / benign → log-only
rising correctable rate → monitor → proactive FRU replacement (avoid outage)
degrading-but-up → cordon + drain (Phase 06)
uncorrectable / persistent fault → RMA the FRU + re-validate (Phase 01) → return to service
8. Remote diagnosis toolkit (host is dead — all OOB)
SoL console · SEL + BERT (last-crash cause after reboot) · kdump vmcore (crash/drgn) ·
AER/MCE via the parser · Phase 07 telemetry correlation · Phase 01 failure-domain check.
9. The "done this before" tells
"A/B banks, right?" · "Did you verify after activation, not just before?" · "Is it canaried and drained first?" · "Correctable rate or a one-off?" · "Which FRU does that MCE bank point to?" · "Replace the DIMM, not the node."
10. How this phase pays off later
Safe updates → the Phase 06 cordon/drain flow + Phase 11 canary/rollback. RAS verdicts →
Phase 10 RCA and Phase 12 runbooks/RMA. Firmware baseline → Phase 01 commissioning +
Phase 12 EBOM. The fleet update system is system-design/03.
Lab 01 — Firmware Update Orchestrator (staged + rollback)
Phase: 08 — Firmware & RAS | Difficulty: ⭐⭐⭐⭐☆ | Time: 5–7 hours Language: Python 3 (stdlib) | Hardware: none
Concept primer:
../WARMUP.mdCh. 2–5,../HITCHHIKERS-GUIDE.md§2–§5.
Run
python3 solution.py # happy path, bad-signature reject, failed-activation rollback, anti-rollback
0. The mission
Make the most dangerous operation in the role — flashing firmware on a remote node — safe: verify before touching hardware, stage to the inactive A/B bank, activate, verify after, and roll back to the previous bank on failure. Plus signature verification and anti-rollback.
1. The four behaviors (read the state machine)
- Happy path v10→v11: stage to inactive bank → activate → verify → commit (advance the anti-rollback floor).
- Tampered image: HMAC signature check fails → rejected before any flash (the device is never touched).
- Bad new firmware: passes signature but fails post-activation health → automatic rollback to the previous bank (v10 restored). This is why A/B banks exist and why you verify after activation, not just before.
- Downgrade: a v9 image below the v10 floor → anti-rollback reject (no downgrading past a security fix).
2. Why each piece matters
- Verify before AND after: a flash can succeed yet the firmware misbehave; only post- activation health (sensors/links — Phase 04) confirms real success.
- A/B banks: a bad flash never destroys the working firmware → recovery is a pointer switch, not a truck roll. Critical for the BMC (don't lose your OOB lifeline).
- Anti-rollback + A/B together: you roll back to the other good bank, never to an old vulnerable version — resolving the security-vs-recoverability tension.
3. Extensions
- Drive a real Redfish UpdateService (SimpleUpdate) against the Phase 03 mock + poll the Task to completion.
- Add fleet canary + drain-first (Phases 06/11): 1 node → 1% → rest with auto halt-and- rollback on SLO regress.
- Model power-loss mid-flash: crash between stage and activate, then prove resume leaves a bootable bank.
- Real signing: replace HMAC with RSA/ECDSA signature verification.
4. What this lab proves
You can build firmware-update software that won't brick a fleet — verify, A/B, rollback, anti-
rollback — and explain every safety decision. "How do you update firmware on 1,000 nodes safely"
becomes a demonstration. This orchestrator + the Phase 06 drain flow + Phase 11 canary = the
fleet firmware system (system-design/03).
Lab 02 — Crash-Dump & RAS Log Parser
Phase: 08 — Firmware & RAS | Difficulty: ⭐⭐⭐⭐☆ | Time: 4–6 hours Language: Python 3 (stdlib) | Hardware: none
Concept primer:
../WARMUP.mdCh. 6–8,../HITCHHIKERS-GUIDE.md§6–§8.
Run
python3 solution.py # parse mixed AER/MCE/SEL/Xid logs -> FRU-level diagnoses + actions
0. The mission
Turn the hardware's cryptic error telemetry into a decision: parse PCIe AER, CPU MCE, BMC SEL, and accelerator (Xid-style) records, decode each to a meaning, map it to a FRU, assign severity, detect predictive patterns (rising correctable rate), and recommend an action — log-only / monitor / cordon / RMA the FRU. This is "crash dump parsing / remote diagnostics" from the JD.
1. The decoders to study
- AER: correctable = noise individually (log-only); uncorrectable fatal (MalformedTLP/ LinkError) → RMA, non-fatal → cordon. BDF identifies the device.
- MCE: the bank maps to a subsystem; bank 7 (memory controller) + address → a specific DIMM. Correctable ECC = log; uncorrectable = RMA.
- SEL/Xid: PSU input-lost → monitor; a fatal Xid (GPU off the bus) → RMA.
2. The key insight: rate, not count
predictive_escalation is the senior move: one correctable error is noise, but 60 corrected
ECC errors on DIMM B1 is a degrading part → escalate to a scheduled replacement (RMA) before
it throws an uncorrectable (a crash). Same lesson as Phase 07 alerting (alert on rates), applied
to hardware — and it's how you turn unplanned outages into planned maintenance.
3. The output
A per-record diagnosis list (critical/warning surfaced), rate-based predictive escalations, and a summary verdict (how many FRUs to RMA / monitor / log). That's the difference between "RMA the whole node" and "replace DIMM B1 and the accelerator at the next window."
4. Extensions
- Parse real
dmesg(AER lines),mcelog/ras-mc-ctl --error-count, andipmitool seloutput. - Correlate with Phase 07 telemetry (did the DIMM errors rise with temperature? → thermal root cause).
- Feed verdicts into the Phase 10 RCA workflow and auto-open an RMA ticket (Phase 12).
- Add BERT (last-crash cause after reboot) parsing for diagnosing random reboots.
5. What this lab proves
You can do remote hardware diagnosis — the JD's most-repeated theme — turning a flood of RAS records into a precise FRU-level verdict and the right action. "A node crashes randomly in production — find the root cause remotely" becomes a methodical walk (SEL/BERT → AER/MCE decode → predictive flag → RMA the DIMM), no site visit required. Feeds Phase 10 and the capstone.
Phase 09 — Networking, Secure Boot & Multi-Tenancy
Difficulty: ⭐⭐⭐⭐☆ | Estimated Time: 2 weeks
Roles supported: Rack Management SWE (Senior/Staff), Infra Security SWE, Platform SWE
Hardware needed: none — simulators + real ip/tc commands (Linux/VM for the live extensions)
Why This Phase Exists
The JD: "Ensure secure boot, VLAN separation, and QoS enforcement for multi-tenant environments." When a rack hosts multiple tenants (or separates the management plane from tenant data — Phase 01 Ch. 6), isolation isn't a feature, it's a contract: tenant A must not see tenant B's traffic, the management network must be unreachable from tenants, no tenant can starve another of bandwidth, and the software/firmware that boots must be trusted. These are the security and fairness primitives of a shared rack.
Three pillars:
- Network isolation — VLANs, network namespaces, the management-vs-data plane separation, and segmentation that keeps tenants and the control plane apart.
- QoS — guaranteeing/limiting bandwidth so one tenant can't degrade another (and the management plane stays responsive).
- Secure/measured boot & attestation — a chain of trust from firmware to OS so you know a node booted trusted software, with remote attestation (TPM) to prove it — the foundation for sovereign/regulated deployments (Phase 06 RKE2).
You'll build a tenant-isolation + QoS model (with the real ip/tc commands) and a
secure/measured-boot + attestation simulator (TPM PCRs, signature chain).
Concepts
Network isolation
- The three planes again (Phase 01 Ch. 6): management (OOB) vs data (tenant) vs scale-up; why management is physically/logically isolated
- VLANs (802.1Q): tags, trunk vs access ports, the broadcast-domain boundary; VLAN-per-tenant
- Linux network namespaces, veth, bridges, VLAN sub-interfaces — how isolation is implemented on a host
- Segmentation & microsegmentation; firewalls/ACLs; the BMC/management VLAN as a hard boundary
- VXLAN/overlays (briefly) for cross-rack tenant networks; SR-IOV/VFs for accelerator NICs in tenant pods
- mTLS, network policy (Phase 06), secrets management, least privilege on the control plane
QoS (Quality of Service)
- Why QoS: the noisy-neighbor problem; protecting the management plane and paying tenants
- Linux
tc(traffic control): qdiscs (HTB, fq_codel), classes, filters; rate limiting, shaping, prioritization - DSCP/marking; bandwidth guarantees vs limits; per-tenant fairness
- QoS for storage/PCIe (briefly) and for the management network
Secure & measured boot, attestation
- UEFI Secure Boot: signed shim → bootloader → kernel; the key hierarchy (PK/KEK/db/dbx); what it does and doesn't protect
- Measured boot: each stage measures (hashes) the next into a TPM PCR before executing it; the difference from Secure Boot (measure vs enforce)
- TPM: PCRs (extend-only hash registers), the root of trust, sealing, quotes
- Remote attestation: a node proves its boot state (a signed PCR quote) to a verifier, which checks it against known-good values before trusting it (e.g., before handing it tenant work/keys)
- The chain of trust from hardware root → firmware → OS; anti-rollback (Phase 08) as part of it
- Sovereign/air-gapped/regulated deployment (Phase 06 RKE2): why attestation matters in the Kingdom's data centers
Multi-tenancy synthesis
- Soft vs hard isolation; defense in depth (VLAN + namespace + RBAC + NetworkPolicy + attestation)
- Mapping tenant boundaries across network, compute (Phase 06), and accelerators (Phase 04 MIG/partitioning)
- The threat model: a malicious tenant, a compromised node, an exposed BMC
Labs
Lab 01 — VLAN Separation & QoS Tenant Isolation
| Field | Value |
|---|---|
| Goal | Model (and, optionally, build with real ip/tc) tenant network isolation via VLANs/namespaces and QoS bandwidth guarantees, and verify that tenants can't cross boundaries or starve each other — and that the management plane is unreachable from tenants. |
| Concepts | VLANs, namespaces/veth/bridges, the management-vs-data boundary, tc/HTB QoS, noisy-neighbor, reachability policy. |
| Steps | 1) python3 solution.py — a reachability + QoS policy engine: who can talk to whom, and bandwidth shares. 2) Read the isolation invariants. 3) Try to make a tenant reach the BMC VLAN (it's blocked). 4) Extensions: build it for real with ip netns/tc. |
| Stack | Python 3 stdlib (policy model) + real ip/tc command examples |
| Output | A policy engine enforcing tenant isolation + QoS, with invariants (no cross-tenant, no tenant→management). |
| How to Test | Asserts verify cross-tenant traffic is denied, management VLAN is unreachable from tenants, and QoS guarantees sum within link capacity. |
| Talking Points | Why management is a hard boundary; VLAN as a broadcast-domain boundary; noisy-neighbor and HTB; defense in depth. |
| Resume Bullet | "Built a tenant network-isolation and QoS policy engine (VLAN/namespace segmentation + HTB bandwidth guarantees) enforcing no-cross-tenant and management-plane-isolation invariants." |
| Extensions | Build real isolation with ip netns add/veth/bridge/VLAN + tc HTB and test with iperf; add SR-IOV VF assignment; add a NetworkPolicy (Phase 06) equivalent. |
Lab 02 — Secure/Measured Boot + Attestation
| Field | Value |
|---|---|
| Goal | Build the chain of trust: a secure-boot signature chain (shim→bootloader→kernel) and a measured-boot model that extends component hashes into TPM PCRs, then a remote-attestation verifier that accepts/rejects a node based on a signed PCR quote. |
| Concepts | UEFI Secure Boot key hierarchy, signature verification, measured boot, TPM PCR extend, attestation quotes, known-good policy. |
| Steps | 1) python3 solution.py — boot a trusted node (verifies + attests OK) and a tampered node (fails). 2) Read the PCR-extend and quote-verify logic. 3) See attestation reject a node with an unexpected boot measurement. 4) Extensions: real tpm2-tools/swtpm. |
| Stack | Python 3 stdlib (hashlib/hmac) |
| Output | A secure+measured boot chain + an attestation verifier that admits only known-good boot states. |
| How to Test | Asserts verify: signed chain boots, tampered component is rejected (secure boot) or detected (measured boot/PCR mismatch), and attestation rejects an unknown PCR quote. |
| Talking Points | Secure boot (enforce) vs measured boot (measure); what a PCR is; why attestation gates trust before handing a node keys/work; the sovereign deployment angle. |
| Resume Bullet | "Built a secure+measured boot chain (signed shim→bootloader→kernel, TPM PCR extend) with a remote-attestation verifier admitting only known-good boot states; the trust foundation for multi-tenant/sovereign deployment." |
| Extensions | Use real swtpm + tpm2-tools (tpm2_pcrextend, tpm2_quote); seal a secret to a PCR state; integrate attestation into the Phase 05 provisioning gate. |
Deliverables Checklist
- You can explain the three planes and why management is a hard isolation boundary
- You can explain VLANs (802.1Q) and how Linux namespaces/bridges implement isolation
- Policy engine enforces no-cross-tenant and no-tenant→management invariants
-
You can explain the noisy-neighbor problem and how
tc/HTB QoS solves it - You can explain Secure Boot (enforce) vs measured boot (measure) and the key hierarchy
- You can explain what a TPM PCR is and how measured boot extends it
- Attestation verifier admits only known-good boot states; you can explain why that gates trust
- You can describe defense-in-depth tenant isolation across network/compute/accelerator
Interview Relevance
- "How do you isolate tenants on a shared rack at the network level?"
- "Why must the management/BMC network be isolated, and how do you enforce it?"
- "What's the noisy-neighbor problem and how does QoS solve it?" (
tc/HTB) - "Explain Secure Boot. What does it protect against and what doesn't it?"
- "Secure boot vs measured boot — what's the difference?"
- "What is a TPM PCR and what is remote attestation for?"
- "Design multi-tenant isolation for a rack — defense in depth."
Warmup Guide — Networking, Secure Boot & Multi-Tenancy
Zero-to-expert primer for Phase 09. You need no security/networking depth: this guide starts at "what is a VLAN" and "what does Secure Boot do" and ends with you designing defense-in-depth tenant isolation and explaining remote attestation. These are the isolation and trust primitives that make a shared rack safe.
Table of Contents
- Chapter 1: The Isolation Mandate
- Chapter 2: VLANs and Network Segmentation
- Chapter 3: Linux Isolation — Namespaces, veth, Bridges
- Chapter 4: QoS and the Noisy Neighbor
- Chapter 5: Secure Boot — Enforcing Trust
- Chapter 6: Measured Boot and the TPM
- Chapter 7: Remote Attestation
- Chapter 8: Defense in Depth & the Threat Model
- Lab Walkthrough Guidance
- Success Criteria
- Interview Q&A
- References
Chapter 1: The Isolation Mandate
A shared rack hosts multiple tenants and a management plane on the same physical hardware. Isolation is the contract that makes that safe:
- Confidentiality/integrity: tenant A cannot see or alter tenant B's traffic or data.
- Management protection: tenants cannot reach the BMC/PDU/CDU management network (an exposed BMC = total compromise, Phase 03 Ch. 7).
- Fairness: no tenant can starve another of bandwidth (QoS).
- Trust: a node runs only trusted firmware/OS, and you can prove it (attestation) before giving it tenant work or secrets.
The senior framing: isolation is defense in depth — no single mechanism is trusted alone. Network (VLAN/namespace), compute (Phase 06 RBAC/NetworkPolicy/quota), accelerator (Phase 04 MIG/partitioning), and trust (secure boot/attestation) layers combine so that a breach of one doesn't breach the tenant boundary. This is especially acute for sovereign/regulated deployments (a government data center in the Kingdom — Phase 06 RKE2), where isolation and provable trust are compliance requirements, not nice-to-haves.
Chapter 2: VLANs and Network Segmentation
A VLAN (Virtual LAN, IEEE 802.1Q) partitions one physical network into isolated broadcast domains. A 12-bit VLAN tag added to the Ethernet frame marks which VLAN it belongs to; switches keep traffic within its VLAN. Two port types:
- Access port: belongs to one VLAN; untagged frames (the host doesn't know about VLANs).
- Trunk port: carries many tagged VLANs (switch-to-switch, or to a host that handles tags).
For a rack, the canonical use is VLAN-per-plane and VLAN-per-tenant: a management VLAN (BMCs/ PDUs/CDU — reachable only by the control plane), and a VLAN (or set) per tenant for their data traffic. Crossing VLANs requires a router/firewall with an explicit ACL — so "tenant traffic can reach the management VLAN" is a misconfiguration, not a default, and your software enforces that it never happens (Lab 01's invariant).
Beyond VLANs (vocabulary): VXLAN/overlays stretch a tenant network across racks/sites (encapsulating L2 in UDP); microsegmentation applies fine-grained per-workload policy; SR-IOV carves a physical NIC into virtual functions (VFs) assignable directly to tenant pods (fast accelerator networking) — each VF needs its own isolation.
Chapter 3: Linux Isolation — Namespaces, veth, Bridges
How isolation is actually implemented on a host (and the basis of containers, Phase 06):
- Network namespace: an isolated copy of the network stack (its own interfaces, routes, firewall). A container/tenant gets its own namespace; it can't see the host's or another namespace's interfaces.
- veth pair: a virtual cable — two connected interfaces, one in the namespace, one outside — to plug a namespace into the host network.
- bridge: a virtual switch in the kernel; you attach veths/VLAN sub-interfaces to it to
connect namespaces, and use VLAN sub-interfaces (
eth0.100) to put a namespace on a VLAN. - firewall/ACL (
nftables/iptables): deny cross-tenant and tenant→management flows.
The real commands (Lab 01 extension):
ip netns add tenantA
ip link add veth-a type veth peer name veth-a-br
ip link set veth-a netns tenantA
ip link add link eth0 name eth0.100 type vlan id 100 # VLAN 100 sub-interface
This is the same machinery Kubernetes CNIs use; understanding it demystifies pod networking (Phase 06) and lets you reason about where isolation could leak.
Chapter 4: QoS and the Noisy Neighbor
The noisy-neighbor problem: on a shared link, one tenant saturating bandwidth degrades everyone — including the management plane (so you can't even manage the rack during the incident). QoS prevents it by guaranteeing and/or limiting bandwidth per tenant/class.
On Linux, tc (traffic control) shapes traffic with qdiscs (queuing disciplines),
classes, and filters:
- HTB (Hierarchical Token Bucket): the workhorse — a tree of classes, each with a guaranteed
rate (
rate) and a ceiling (ceil). You give each tenant a guaranteed share and a cap, and reserve a slice for the management plane. Idle tenants' bandwidth can be borrowed up to the ceiling — fairness without waste. - fq_codel: fights bufferbloat (latency under load) — good as the leaf qdisc.
- DSCP marking: tag packets by priority so the network honors classes end to end.
Example shape (Lab 01 extension):
tc qdisc add dev eth0 root handle 1: htb default 30
tc class add dev eth0 parent 1: classid 1:1 htb rate 100gbit
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 10gbit ceil 40gbit # mgmt: guaranteed
tc class add dev eth0 parent 1:1 classid 1:20 htb rate 40gbit ceil 90gbit # tenant A
The invariant your software enforces (Lab 01): the sum of guarantees must not exceed link capacity (you can oversubscribe ceilings, never guarantees), and the management class always has a reserved guarantee so the rack stays manageable under tenant load.
Chapter 5: Secure Boot — Enforcing Trust
UEFI Secure Boot ensures a machine only runs signed, trusted boot software, defending against bootkits/rootkits that would otherwise load below the OS and be nearly invisible.
The chain: firmware verifies the shim (signed by Microsoft's key, in practice), shim verifies the bootloader (GRUB), the bootloader verifies the kernel, and the kernel verifies modules — each stage checks the next's signature before executing it. If a signature doesn't match a key in the allowed database, the boot stops (enforcement).
The key hierarchy (know the names):
- PK (Platform Key) — the root; owns the platform, authorizes KEK changes.
- KEK (Key Exchange Keys) — authorize updates to db/dbx.
- db — the allowed signatures/keys (what may run).
- dbx — the forbidden list (revoked/known-bad — e.g., a vulnerable bootloader).
What Secure Boot does: stops unsigned/tampered boot code from running. What it doesn't: it doesn't encrypt anything, doesn't protect a running system from runtime attacks, and doesn't prove to a third party what booted (that's attestation, Chapter 7). It's enforcement at boot, and it ties to firmware signing/anti-rollback (Phase 08) and to PXE (Phase 05 — your network boot chain must be signed or Secure Boot blocks it).
Chapter 6: Measured Boot and the TPM
Measured boot is the complement to Secure Boot: instead of (or in addition to) enforcing, each boot stage measures (cryptographically hashes) the next component before running it and records the measurement in the TPM — building an unforgeable record of exactly what booted.
The TPM (Trusted Platform Module) is a hardware (or firmware, fTPM) security chip — the hardware root of trust. Its key feature for this is PCRs (Platform Configuration Registers): special registers you can't write arbitrarily, only extend:
PCR_new = Hash(PCR_old || measurement)
Because extend is a one-way hash chain, the final PCR value is a fingerprint of the entire boot sequence in order — change any component (or its order) and the PCR differs. Different PCRs hold different parts (firmware, bootloader, kernel, config). Lab 02 implements PCR-extend.
Secure boot vs measured boot (the interview distinction): Secure Boot enforces (won't run unsigned code — stops a bad boot); measured boot measures (records what booted — lets you detect and prove it, even if it booted). They're complementary: enforce and record. Measured boot also enables sealing — encrypting a secret so the TPM only releases it if the PCRs match a known-good state (so a tampered boot can't unseal the disk-encryption key).
Chapter 7: Remote Attestation
Measured boot records what booted locally; remote attestation lets a node prove its boot state to a remote verifier so the verifier can decide whether to trust it — before handing it tenant workloads, secrets, or cluster membership.
The flow:
- The verifier sends a nonce (freshness, anti-replay).
- The node asks its TPM for a quote: the current PCR values, signed by a TPM-resident key (an AK/Attestation Key, rooted in the TPM's endorsement key), including the nonce.
- The verifier checks the signature (is this a genuine TPM?) and compares the PCRs against known-good reference values (golden measurements for the approved firmware/OS).
- If they match → the node booted approved software → grant trust (join the cluster, release keys, schedule tenant work). If not → quarantine it (Phase 08/10).
Why it matters for this role: in a multi-tenant or sovereign/air-gapped deployment, you cannot just assume a node is trustworthy — a compromised firmware/OS could exfiltrate tenant data. Attestation makes trust provable and gateable: a node earns tenant work only by attesting to a known-good state. It's a natural addition to the Phase 05 provisioning validation gate and the Phase 06 node-join flow. Lab 02 builds a verifier that admits only known-good quotes.
Chapter 8: Defense in Depth & the Threat Model
Tie it together with the adversary in mind:
- A malicious tenant: tries to reach another tenant or the management plane, or to hog bandwidth. Defenses: VLAN/namespace isolation (Ch. 2–3), NetworkPolicy/RBAC/quota (Phase 06), QoS (Ch. 4), accelerator partitioning isolation (Phase 04), no co-placement of competing tenants on one physical device.
- A compromised node (bad firmware/OS): tries to lie about its state or attack the fabric. Defenses: secure boot (won't run untrusted code), measured boot + attestation (won't be trusted unless it proves a good state), management-plane isolation (limits reach).
- An exposed BMC (the classic catastrophe): full power/firmware control from the network. Defense: the management VLAN is a hard boundary reachable only by the control plane; never on a tenant/internet-facing network (Phase 03 Ch. 7).
Defense in depth means no single control is load-bearing: even if a tenant breaks out of a namespace, the VLAN still contains them; even if they reach a node, attestation/RBAC limits the damage; even if firmware is tampered, attestation refuses to trust it. As a Staff engineer you articulate the threat model and show that each layer fails safe — which is exactly what a security review (and a sovereign-deployment customer) will probe.
Lab Walkthrough Guidance
Order: Lab 01 (network/QoS) → Lab 02 (boot trust). Isolation of traffic first, then trust of what's running.
Lab 01 (VLAN/QoS isolation):
python3 solution.py— a reachability + QoS policy engine; see who can reach whom and the bandwidth shares.- Try to make a tenant reach the management VLAN — the invariant blocks it.
- Extensions: build it for real with
ip netns/veth/bridge/VLAN sub-interfaces +tcHTB, and test isolation/QoS withiperf.
Lab 02 (secure/measured boot + attestation):
python3 solution.py— boot a trusted node (verifies + attests OK) and a tampered node (secure boot rejects / measured boot detects / attestation refuses).- Read the PCR-extend chain and the quote verification against known-good values.
- Extensions: use real
swtpm+tpm2-tools(tpm2_pcrextend,tpm2_quote); seal a secret to a PCR state; gate Phase 05 provisioning on attestation.
Phase capstone question: "Design multi-tenant isolation for a rack serving paid and free tenants
plus a management plane, in a regulated/sovereign environment." (Answer in system-design/04:
VLAN-per-tenant + management VLAN as a hard boundary; namespaces/NetworkPolicy/RBAC/quota; QoS via
HTB with a reserved management class; no co-placement of competing tenants on a physical
accelerator; secure boot + measured boot + attestation gating node trust; defense in depth with
a stated threat model.)
Success Criteria
You're done with this phase when — without notes:
- You can explain the isolation mandate and defense in depth (Ch. 1, 8)
- You can explain VLANs/802.1Q and Linux namespace/veth/bridge isolation (Ch. 2–3)
- You can state and enforce the no-cross-tenant / no-tenant→management invariants (Ch. 2, 8)
- You can explain the noisy-neighbor problem and HTB QoS with a reserved management class (Ch. 4)
- You can explain Secure Boot, the PK/KEK/db/dbx hierarchy, and what it does/doesn't protect (Ch. 5)
- You can explain measured boot, TPM PCRs (extend-only), and secure-vs-measured boot (Ch. 6)
- You can explain remote attestation and why it gates trust before tenant work (Ch. 7)
- You can lay out a defense-in-depth tenant-isolation design with a threat model (Ch. 8)
Interview Q&A
Q1: How do you isolate tenants on a shared rack at the network level, and why is the management plane special? A: VLAN-per-tenant (802.1Q) puts each tenant in its own broadcast domain; on the host, network namespaces + veth + bridges + VLAN sub-interfaces implement the isolation, with firewall ACLs denying cross-tenant flows. Crossing VLANs requires an explicit router/ACL, so cross-tenant traffic is a misconfiguration, not a default — and my software asserts it never happens. The management plane (BMCs/PDUs/CDU) is special because those devices have total power/firmware control, so it's a hard boundary: its own VLAN, reachable only by the control plane, never from a tenant or the internet — an exposed BMC is a full compromise. It's defense in depth: VLAN + namespace + NetworkPolicy/RBAC (Phase 06) + QoS so no single failure breaches the boundary.
Q2: What's the noisy-neighbor problem and how does QoS solve it?
A: On a shared link, one tenant saturating bandwidth degrades every other tenant — and dangerously
the management plane, so you can't even manage the rack during the incident. QoS fixes it with
Linux tc using HTB (Hierarchical Token Bucket): a class tree where each tenant gets a guaranteed
rate and a ceil, with a reserved guaranteed class for management. Idle bandwidth can be
borrowed up to the ceiling, so it's fair without waste. The invariant: the sum of guarantees
can't exceed link capacity (ceilings can oversubscribe), and management always keeps its reserved
guarantee. fq_codel as the leaf qdisc keeps latency low under load.
Q3: Explain Secure Boot — what it protects against and what it doesn't. A: UEFI Secure Boot ensures the machine only runs signed, trusted boot software: firmware verifies the shim, shim verifies GRUB, GRUB verifies the kernel, each checking a signature against the allowed key database (db), with a forbidden list (dbx) for revoked components, all under the PK/KEK key hierarchy. It defends against bootkits/rootkits that load below the OS. What it doesn't do: it doesn't encrypt anything, doesn't protect a running system from runtime attacks, and doesn't prove to a remote party what booted — that's remote attestation. It's enforcement at boot, and it ties to firmware signing/anti-rollback (Phase 08) and PXE (the network boot chain must be signed or Secure Boot blocks it).
Q4: Secure boot vs measured boot — what's the difference, and what's a TPM PCR?
A: Secure Boot enforces — it refuses to run unsigned/untrusted code, stopping a bad boot.
Measured boot measures — each stage hashes the next component before running it and records the
hash in the TPM, building an unforgeable record of exactly what booted (it doesn't stop a boot, it
documents it). They're complementary: enforce and record. A TPM PCR (Platform Configuration
Register) is a special register you can't write arbitrarily, only extend:
PCR = Hash(PCR || measurement). Because it's a one-way hash chain, the final value fingerprints
the whole boot sequence in order — any change yields a different PCR. That enables detection,
attestation, and sealing (releasing a secret only if PCRs match a known-good state).
Q5: What is remote attestation and why would a rack platform need it? A: Remote attestation lets a node prove its boot state to a remote verifier before being trusted. The verifier sends a nonce; the node's TPM returns a signed quote of its PCR values (plus the nonce); the verifier checks the signature came from a genuine TPM and compares the PCRs against known-good golden measurements. Match → the node booted approved firmware/OS → grant trust (join the cluster, release keys, schedule tenant work); mismatch → quarantine. A rack platform needs it because in multi-tenant or sovereign/air-gapped deployments you can't just assume a node is trustworthy — compromised firmware could exfiltrate tenant data. Attestation makes trust provable and gateable, a natural addition to the provisioning validation gate (Phase 05) and the node-join flow (Phase 06).
Q6 (Staff): Design defense-in-depth multi-tenant isolation for a regulated/sovereign rack. A: Layer it so no single control is load-bearing. Network: VLAN-per-tenant + a management VLAN as a hard boundary (control-plane-only), namespaces/veth/bridges on hosts, NetworkPolicy + RBAC + quotas in Kubernetes (Phase 06), and QoS (HTB) with a reserved management class. Compute/ accelerator: don't co-place competing tenants on one physical accelerator; use MIG-style partitioning isolation (Phase 04) where fractional sharing is needed. Trust: secure boot (won't run untrusted code) + measured boot + remote attestation gating node trust before it gets tenant work or keys, with sealed secrets tied to PCR state. Operational: least-privilege control plane, mTLS, secrets management, audit (Phase 07/12), and RKE2 (Phase 06) for a hardened, air-gappable base. Then I'd write the threat model explicitly — malicious tenant, compromised node, exposed BMC — and show each layer fails safe, because a sovereign customer's security review will probe exactly that, and the Staff deliverable is the documented, defensible design, not just the config.
References
- IEEE 802.1Q (VLANs); Linux networking:
ip netns,ip-link, bridges, VLAN sub-interfaces — https://man7.org/linux/man-pages/ - Linux Traffic Control (
tc, HTB, fq_codel) — https://man7.org/linux/man-pages/man8/tc.8.html , the LARTC HOWTO - UEFI Secure Boot & the PK/KEK/db/dbx hierarchy — UEFI spec; https://uefi.org/
- TPM 2.0 (TCG) — PCRs, quotes, attestation, sealing — https://trustedcomputinggroup.org/
tpm2-tools&swtpm(software TPM for labs) — https://github.com/tpm2-software/tpm2-tools- Linux IMA/EVM & measured boot; remote attestation frameworks (Keylime) — https://keylime.dev/
- NIST SP 800-155 (BIOS integrity measurement), 800-193 (firmware resiliency) — https://csrc.nist.gov/
- Cross-track: Security Engineer track — deeper offensive/defensive security
🛸 Hitchhiker's Guide — Phase 09: Networking, Secure Boot & Multi-Tenancy
Read this if: "VLAN, QoS, secure boot, attestation" is a list you can't yet connect. Compressed map of isolation (traffic) and trust (boot).
0. The 30-second mental model
Two questions on a shared rack: can the wrong parties talk? (network isolation + QoS) and can I trust what's running? (secure/measured boot + attestation). Answer both with defense in depth — no single control is load-bearing. The hardest boundary: tenants must NEVER reach the management VLAN (an exposed BMC = total compromise).
1. The three planes (again)
management (OOB: BMC/PDU/CDU) · data (tenant) · scale-up. Management is a hard boundary: its own VLAN, control-plane-only, never tenant/internet-facing.
2. VLAN + Linux isolation
VLAN (802.1Q) = a broadcast domain; tag = which VLAN; access port = one VLAN, trunk = many.
On a host: netns (isolated stack) + veth (virtual cable) + bridge (virtual switch) +
eth0.100 (VLAN sub-iface). Crossing VLANs needs an explicit router/ACL → cross-tenant is a
misconfig, not a default.
ip netns add tenantA; ip link add veth0 type veth peer name veth0-br
ip link add link eth0 name eth0.100 type vlan id 100
3. QoS = no noisy neighbor
tc + HTB: class tree, each tenant a rate (guarantee) + ceil (cap); reserve a class for
management. Sum of guarantees ≤ link capacity (ceilings may oversubscribe). fq_codel leaf for
low latency.
4. Secure boot (ENFORCE)
firmware→shim→GRUB→kernel, each verifies the next's signature against db (allowed) / dbx (revoked), under PK→KEK keys. Stops bootkits. Does NOT encrypt, NOT protect runtime, NOT prove to others (that's attestation). PXE chain must be signed or it's blocked (Phase 05).
5. Measured boot (MEASURE) + TPM
Each stage hashes the next into a TPM PCR: PCR = Hash(PCR || measurement) (extend-only →
fingerprints the whole boot in order). Secure boot stops a bad boot; measured boot records &
proves it. Enables sealing (release a secret only if PCRs match).
6. Remote attestation (PROVE)
verifier nonce → node TPM returns a signed PCR quote → verifier checks TPM signature + PCRs vs known-good → grant/deny trust (join cluster, release keys, run tenant work). Gate trust before handing a node anything. Essential for sovereign/air-gapped (Phase 06 RKE2).
7. Defense in depth vs the threat model
| adversary | defenses |
|---|---|
| malicious tenant | VLAN/netns + NetworkPolicy/RBAC/quota (P06) + QoS + accel partitioning (P04) |
| compromised node | secure boot + measured boot + attestation + mgmt isolation |
| exposed BMC | management VLAN = hard boundary, control-plane-only |
No single control load-bearing; each layer fails safe.
8. The "done this before" tells
"Can a tenant reach the BMC VLAN? (no)" · "Sum of QoS guarantees ≤ capacity?" · "Secure or measured boot — enforce or prove?" · "What's in PCR 0–7?" · "Did the node attest before we trusted it?" · "What's the threat model?"
9. How this phase pays off later
Attestation gates Phase 05 provisioning + Phase 06 node-join. Tenancy pairs with Phase 06.
Signed firmware = Phase 08. The full design is system-design/04; sovereign deployment is the
RKE2 (Phase 06) context.
Lab 01 — VLAN Separation & QoS Tenant Isolation
Phase: 09 — Networking & Tenancy | Difficulty: ⭐⭐⭐⭐☆ | Time: 4–6 hours
Language: Python 3 (sim) + real ip/tc | Hardware: none (Linux/VM for the live build)
Concept primer:
../WARMUP.mdCh. 2–4,../HITCHHIKERS-GUIDE.md§1–§3.
Run
python3 solution.py # reachability + QoS policy engine with isolation invariants
0. The mission
Encode the two isolation guarantees of a shared rack as enforceable policy: reachability (same-VLAN ok, cross-tenant denied without an ACL, tenant→management always denied) and QoS (per-tenant guarantees + ceilings with a reserved management class).
1. The invariants to internalize
- Tenant → management is a hard rule that overrides even a (mis)configured ACL — the self-test
proves adding
(100,10)to the ACLs still can't let a tenant reach the BMC VLAN. An exposed BMC is total compromise; this boundary cannot be soft. - Cross-tenant requires an explicit ACL — default-deny between VLANs.
- QoS: sum of guarantees ≤ link capacity (ceilings may oversubscribe); management always has a reserved guarantee so the rack stays manageable under tenant load.
2. Build it for real (extension)
sudo ip netns add tenantA
sudo ip link add veth-a type veth peer name veth-a-br
sudo ip link set veth-a netns tenantA
sudo ip link add link eth0 name eth0.100 type vlan id 100 # tenant VLAN
# QoS: reserve mgmt, cap tenants (HTB)
sudo tc qdisc add dev eth0 root handle 1: htb default 30
sudo tc class add dev eth0 parent 1: classid 1:1 htb rate 100gbit
sudo tc class add dev eth0 parent 1:1 classid 1:10 htb rate 10gbit ceil 40gbit # mgmt
sudo tc class add dev eth0 parent 1:1 classid 1:20 htb rate 40gbit ceil 90gbit # tenantA
Then verify isolation and bandwidth shares with iperf3.
3. Extensions
- Build the real
ip netns/veth/bridge/VLAN +tcsetup above and test withiperf3. - Add a Kubernetes NetworkPolicy (Phase 06) equivalent and reconcile the two policy models.
- Add SR-IOV VF assignment for accelerator NICs and reason about its isolation.
- Model the management plane being unreachable during a tenant DDoS without the reserved QoS class — then add it back and show the rack stays manageable.
4. What this lab proves
You can specify and enforce tenant isolation + fairness — the JD's "VLAN separation and QoS
enforcement for multi-tenant environments" — with the management boundary treated as inviolable.
"Isolate tenants on a shared rack" becomes a precise, testable policy. Pairs with Phase 06 tenancy
and feeds system-design/04.
Lab 02 — Secure/Measured Boot + Attestation
Phase: 09 — Networking & Tenancy | Difficulty: ⭐⭐⭐⭐☆ | Time: 4–6 hours
Language: Python 3 (stdlib) | Hardware: none (swtpm/tpm2-tools for the real extension)
Concept primer:
../WARMUP.mdCh. 5–7,../HITCHHIKERS-GUIDE.md§4–§6.
Run
python3 solution.py # trusted boot+attest OK; tampered boot stopped / detected
Run it for REAL with swtpm + tpm2-tools (the extension — verified)
attestation_flow.sh reproduces the same measured-boot + attestation flow with a real software
TPM (swtpm) and real tpm2-tools. TPM tooling is Linux-only, so run-in-docker.sh runs it in a
Linux container (needs Docker) — works on macOS/Windows hosts too:
./run-in-docker.sh # or, on Linux: apt install swtpm tpm2-tools && ./attestation_flow.sh
It: starts swtpm → tpm2_pcrextends shim/grub/kernel into a PCR (measured boot) → creates an AK and
tpm2_quote/tpm2_checkquote (real signed attestation) → extends a tampered kernel into another
PCR and shows the value differs (attestation would reject). Verified output:
golden PCR23 = 0x48078734A5098E041B9FE97CF18C6158D3E41166C52BD316569997182B5BD213
checkquote: PASS (genuine TPM signed the PCRs, nonce matched) -> ATTESTED
tampered PCR16 = 0x7837FDDA35A019DAD51C1E6CF6923D982C07578D915CFAAEDDE2CD8EF524ED5B
PCR mismatch (golden != tampered) -> attestation REJECTS the tampered node ✅
This is the simulator's logic (solution.py) on real TPM hardware-emulation — the same PCR-extend
chain, AK quote, and known-good comparison. (Two real-TPM gotchas you'll hit and that the script
handles: swtpm's small transient-object pool needs tpm2_flushcontext -t before the quote, and PCRs
22/23 have locality restrictions so the tamper demo uses the locality-0 debug PCR 16.)
0. The mission
Build the chain of trust that lets you know and prove a node booted approved software: secure boot (enforce signatures), measured boot (extend hashes into a TPM PCR), and remote attestation (a signed PCR quote a verifier checks against known-good before trusting the node).
1. The three behaviors
- Trusted node: signatures verify, PCR matches the golden value, attestation → trust.
- Tampered kernel, Secure Boot ON: the signature check stops the boot (enforce).
- Tampered kernel, Secure Boot OFF: it boots anyway, but the PCR mismatches and attestation refuses — measured boot + attestation catch what enforcement missed. This is the key insight: enforce and prove, because you can't always enforce everywhere.
Plus: replay protection (unknown nonce rejected) and forged-quote rejection (wrong AK).
2. Why attestation gates trust
A node only earns tenant workloads/keys by proving a known-good boot state. In multi-tenant or sovereign/air-gapped deployments (Phase 06 RKE2), you can't assume trust — compromised firmware could exfiltrate tenant data. Attestation makes trust provable and gateable, a natural addition to the Phase 05 provisioning gate and Phase 06 node-join.
3. Extensions (real TPM)
- Use
swtpm(software TPM) +tpm2-tools:tpm2_pcrextend,tpm2_quote,tpm2_checkquote— reproduce the flow on a real TPM stack. - Seal a secret to a PCR state (
tpm2_create/tpm2_unseal) so a disk-encryption key only releases on a known-good boot. - Integrate attestation as a Phase 05 provisioning gate / Phase 06 node-join check.
- Model dbx revocation: revoke a now-vulnerable bootloader and show it's refused.
4. What this lab proves
You understand the full trust chain — secure boot (enforce), measured boot (record), attestation
(prove) — and can explain the crucial secure-vs-measured distinction and why attestation gates
trust. "Ensure secure boot for multi-tenant environments" (the JD) becomes a working chain you
can defend in a security review. Feeds system-design/04 and the sovereign-deployment story.
Phase 10 — Reliability, Debugging & Root-Cause Analysis
Difficulty: ⭐⭐⭐⭐⭐ | Estimated Time: 2 weeks
Roles supported: Rack Management SWE (Senior/Staff), SRE, Production/Reliability SWE
Hardware needed: none — a Linux box (or VM) for the real gdb/strace/valgrind extensions
Why This Phase Exists
This is the JD's most-repeated theme, stated three ways: "Own debugging and root‑cause analysis of complex issues across development, integration, and production environments, including deployed systems," "thrives in... deep technical debugging," and the entire role being "execution-focused, emphasizing... debugging... of infrastructure software that runs close to hardware." If you take one phase to interview-ready perfection, make it this one — it's what the hiring manager most wants to see.
Debugging at this level is not luck; it's method. This phase teaches the systematic
discipline (observe → hypothesize → test → narrow → fix → verify → prevent), the Linux toolset
(gdb, strace, ltrace, perf, ftrace, eBPF, sanitizers, core dumps), the reliability
patterns that prevent failures (retries, backoff, circuit breakers, idempotency, reconciliation,
bulkheads), chaos/fault injection to find weaknesses before production does, and the RCA/
postmortem writeup that turns an incident into a permanent improvement (Phase 12).
You'll find a planted bug with real tools and write its RCA, and build a chaos harness that hardens a reconcile loop.
Concepts
Systematic debugging method
- The loop: reproduce → observe → hypothesize → bisect/narrow → test the hypothesis → fix → verify → prevent
- Scientific debugging (one variable at a time); reading the evidence before guessing; the "what changed?" question
- Heisenbugs, race conditions, the role of determinism; binary search (
git bisect) in space and time - Knowing your failure domain first (Phase 01) — single node vs shared resource
The Linux debugging toolset
gdb: breakpoints, backtraces, watchpoints, core-dump analysis, attaching to a live processstrace/ltrace: syscall/library-call tracing — the "what is it actually doing/why is it stuck" toolperf: CPU profiling, flame graphs, hardware counters — for "why is it slow"ftrace/eBPF (bcc/bpftrace): kernel/dynamic tracing — production-safe deep observability- Sanitizers (ASan/UBSan/TSan — Phase 02) and valgrind: memory/race bugs
- Core dumps &
kdump(Phase 08): post-mortem of a crash - Logs/metrics/traces (Phase 07) as the first-line evidence, especially for deployed systems
Reliability patterns (prevent the failure)
- Retries + backoff + jitter (Phase 02); idempotency (Phase 05); timeouts everywhere
- Circuit breaker (stop hammering a failing dependency); bulkhead (isolate failures); rate limiting
- Reconciliation / self-healing (Phase 06): converge to desired state despite transient faults
- Graceful degradation (Phase 02); fail-safe vs fail-open; backpressure
- Redundancy & failure-domain-aware design (Phase 01)
Proactive: chaos & fault injection
- Chaos engineering: inject failures (kill, slow, partition, corrupt) to find weaknesses first
- Fault injection in tests; game days; the blast-radius-bounded experiment
Closing the loop: RCA & postmortems
- The 5 Whys; contributing factors vs root cause; blameless postmortems
- The RCA document: timeline, impact, root cause, the fix, action items to prevent recurrence
- Feeding fixes back into tests (Phase 11), runbooks, and design (Phase 12)
Labs
Lab 01 — Debugging Dojo: Find the Planted Bug
| Field | Value |
|---|---|
| Goal | Find a deliberately planted bug in a close-to-hardware C program using real tools (ASan/gdb/valgrind), then write the RCA — practicing the method, not just the fix. |
| Concepts | Systematic debugging, memory bugs (use-after-free/buffer overflow/race), reading a backtrace, sanitizers, the RCA writeup. |
| Steps | 1) make && ./buggy — observe the wrong/crashing behavior. 2) make asan — let AddressSanitizer point at the bug. 3) Confirm with gdb/valgrind. 4) Fix it and verify. 5) Fill in RCA.md. |
| Stack | C (C11), make, ASan; optional gdb/valgrind |
| Output | The identified bug, a verified fix, and a completed RCA document. |
| How to Test | The fixed program passes its asserts and is clean under ASan; your RCA follows the timeline→root-cause→prevention structure. |
| Talking Points | How you localize a bug systematically; why ASan/TSan catch what testing misses; reading a backtrace; root cause vs symptom. |
| Resume Bullet | "Diagnosed memory-safety bugs in close-to-hardware C using AddressSanitizer/gdb/valgrind and documented blameless RCAs with preventive action items." |
| Extensions | Plant your own race and find it with TSan; debug a hung process with strace; analyze a core dump in gdb; add a git bisect exercise. |
Lab 02 — Chaos & Reconciliation Hardening
| Field | Value |
|---|---|
| Goal | Build a chaos/fault-injection harness against a reconcile loop and prove that reliability patterns (retry/backoff, circuit breaker, idempotency) let it converge despite injected failures, partitions, and slow dependencies. |
| Concepts | Reconciliation/self-healing, retries/backoff/jitter, circuit breaker, idempotency, timeouts, chaos engineering, measuring resilience. |
| Steps | 1) python3 solution.py — run the loop under a fault-free baseline, then under chaos. 2) See the naive loop fail and the hardened loop converge. 3) Watch the circuit breaker open/close. 4) Extensions. |
| Stack | Python 3 stdlib |
| Output | A chaos harness + a hardened reconcile loop that converges under injected faults; metrics on attempts/recoveries. |
| How to Test | Asserts verify the hardened loop converges under chaos where the naive one doesn't, the circuit breaker trips and recovers, and operations are idempotent under retry. |
| Talking Points | Why retries need backoff+jitter+a cap; what a circuit breaker prevents (retry storms); idempotency under retry; chaos finds what tests don't. |
| Resume Bullet | "Built a chaos/fault-injection harness and hardened a reconcile loop with retry/backoff, circuit breaking, and idempotency, proving convergence under injected failures and partitions." |
| Extensions | Add a bulkhead/rate limiter; inject a network partition and a slow dependency; add an SLO and measure the error budget burned under chaos. |
Deliverables Checklist
- You can state the systematic debugging loop and apply it under pressure
-
You can pick the right tool:
gdb(state),strace(syscalls/stuck),perf(slow), ASan/TSan (memory/race), eBPF (production) - You found the planted bug with tools (not by reading luck) and verified the fix under ASan
- Your RCA follows timeline → impact → root cause → fix → prevention
- You can explain retries+backoff+jitter, circuit breakers, idempotency, and reconciliation
- Hardened reconcile loop converges under chaos where the naive one fails
- You can explain why chaos/fault injection finds what normal tests don't
- You can run a blameless postmortem and turn it into action items (Phase 12)
Interview Relevance
- "Walk me through how you'd debug a production node that's intermittently rebooting." (method!)
- "A daemon is using 100% CPU. How do you find why?" (
perf/flame graph) - "A process is hung. What do you do?" (
strace, where is it blocked) - "You suspect a memory corruption. What tools?" (ASan/valgrind,
gdb) - "What's a circuit breaker and why do you need one?" (retry storms)
- "How do you make a flaky operation reliable?" (retry+backoff+jitter+idempotency+timeout)
- "Tell me about a hard bug you root-caused." (your RCA — STAR, Phase 12)
- "What is chaos engineering and would you run it in production?"
Warmup Guide — Reliability, Debugging & Root-Cause Analysis
Zero-to-expert primer for Phase 10 — the JD's most-emphasized skill. You may already debug by instinct; this guide makes it a method you can apply under pressure to a production system you can't restart, with the right tool for each symptom, plus the patterns that prevent failures and the RCA that makes them never recur.
Table of Contents
- Chapter 1: Debugging Is a Method, Not a Talent
- Chapter 2: The Toolset and When to Reach for Each
- Chapter 3: Debugging a Deployed System
- Chapter 4: The Hard Bugs — Races, Heisenbugs, Memory
- Chapter 5: Reliability Patterns That Prevent Failures
- Chapter 6: Chaos & Fault Injection
- Chapter 7: RCA and the Blameless Postmortem
- Lab Walkthrough Guidance
- Success Criteria
- Interview Q&A
- References
Chapter 1: Debugging Is a Method, Not a Talent
The engineers who seem to "just know" where bugs are are running a disciplined loop fast. Make it explicit:
- Reproduce (or capture): a bug you can reproduce is half-solved. If you can't, capture maximal evidence (logs/traces/metrics/core dump — Phase 07/08) the next time it happens.
- Observe before theorizing: read the actual evidence — the error, the backtrace, the metric, the syscall — before guessing. Most wrong debugging is theory-first.
- Ask "what changed?": the vast majority of production incidents follow a change (deploy,
firmware, config, traffic).
git bisect, the deploy log, the firmware version (Phase 08). - Hypothesize, then test one variable: form a falsifiable hypothesis and design the cheapest experiment that confirms or kills it. Change one thing at a time.
- Bisect/narrow: binary-search the problem space — in code (
git bisect), in data (which input), in the fleet (which node/rack — Phase 01 failure domain), in time (when did it start). - Fix → verify: confirm the fix actually resolves it (and didn't just move it), ideally with a regression test (Phase 11).
- Prevent: the RCA's real output — a test, an alert, a guard, a runbook so it can't recur silently (Chapter 7, Phase 12).
This loop is what an interviewer is listening for when they say "debug this" — they want the method, narrated, far more than a lucky guess.
Failure-domain first (Phase 01): before diving into one node, ask "is this one node or a shared resource?" Six nodes down together → a PDU/ToR/CDU, not six bugs. The first cut of any incident is scoping the blast radius.
Chapter 2: The Toolset and When to Reach for Each
Matching the tool to the symptom is half the skill. The map:
| Symptom | Reach for | Why |
|---|---|---|
| Crash / wrong state | gdb (+ core dump) | breakpoints, backtrace, inspect memory/vars; post-mortem from a core |
| Stuck / hung / "what is it doing?" | strace (syscalls), ltrace (lib calls) | shows the exact syscall it's blocked in (a read, a futex, a connect) |
| Slow / high CPU | perf (+ flame graphs) | where the cycles go; hardware counters |
| Memory corruption / leak | ASan / valgrind | use-after-free, overflow, leaks with the exact line |
| Data race | TSan | the racing accesses (Phase 02) |
| Production deep-dive (can't stop it) | eBPF (bpftrace/bcc), ftrace | dynamic, low-overhead kernel+user tracing on a live system |
| First evidence | logs/metrics/traces (Phase 07) | start here for deployed systems |
A few signature moves:
strace -p <pid>on a hung process → see it blocked in afutex(lock) orread(waiting on a slow BMC) → that is the clue.perf top/ a flame graph on a 100%-CPU daemon → the hot function jumps out.- ASan on a crashing C program → the exact use-after-free with both stack traces (allocation and use). Lab 01.
bpftrace -e 'tracepoint:...'to count/trace events on a production node without restarting it.
The anti-pattern: adding printfs and rebuilding for hours when strace/ASan/perf would point
at it in minutes. Knowing the toolset is the seniority.
Chapter 3: Debugging a Deployed System
The JD stresses "including deployed systems" — the hardest case, because you often can't attach a debugger, can't reproduce locally, and can't restart it (it's serving tenants). The approach shifts:
- Evidence-first, non-invasive: start with what's already collected — structured logs, metrics, traces (Phase 07), the SEL/RAS logs (Phase 08), and the OOB console (SoL, Phase 04). For a node that won't boot, your only window may be Redfish/SoL.
- eBPF for live insight:
bpftrace/bcc attach to a running production process/kernel with minimal overhead to answer "what syscalls is it making / how long are BMC calls taking / which request is slow" without a restart. This is the modern production-debugging superpower. - Correlate (Phase 07): metric anomaly → trace in that window → logs for the failing span → RAS/telemetry for the hardware. The metrics→traces→logs loop.
- Reproduce in a safe copy: pull the inputs/state and reproduce in a lab/canary, not on the live fleet. Chaos (Chapter 6) and HIL (Phase 11) help you reproduce hardware-adjacent bugs.
- Bisect across the fleet (Phase 01): is it all nodes, one rack, one firmware version (Phase 08), one hardware batch? The pattern localizes the cause.
The mindset: you're a detective working from traces left behind, with OOB as your one reliable channel to a sick machine. Build the system so it leaves good traces (Phase 02/07) — future-you debugging at 3 a.m. depends on past-you's instrumentation.
Chapter 4: The Hard Bugs — Races, Heisenbugs, Memory
The bugs that define "complex issues":
- Race conditions: two threads/processes touching shared state without proper synchronization
(Phase 02). Symptoms are intermittent and load-dependent. They vanish under a debugger (timing
changes) — a classic Heisenbug. The tool is TSan (catches the racing accesses
deterministically) plus disciplined lock review (
lock_guard, tiny critical sections). - Memory bugs (C/C++): use-after-free, buffer overflow, uninitialized reads, double-free. Often crash later, far from the cause, making backtraces misleading. ASan/valgrind give you the real cause (allocation + use sites). Lab 01.
- Heisenbugs generally: bugs that change behavior when observed (timing, optimization, added logging). Strategy: capture non-invasively (eBPF, sampling), make timing deterministic in tests (inject the clock — Phase 02), and reason about the invariant being violated.
- Resource exhaustion: fd/memory/handle leaks, thread-pool starvation, unbounded queues — manifest as slow degradation then collapse. Watch for unbounded growth (Phase 07 metrics) and enforce bounds (Phase 02).
- Hardware-adjacent nondeterminism: a flaky BMC, a marginal PCIe lane (Phase 04), a thermal throttle (Phase 04/08) — the "bug" is the hardware, and the skill is distinguishing a software bug from a hardware fault using RAS (Phase 08) and telemetry (Phase 07).
The recurring lesson: make the nondeterministic deterministic — inject the clock/randomness, use sanitizers, bound resources, and capture enough evidence that you don't need to reproduce.
Chapter 5: Reliability Patterns That Prevent Failures
The best debugging is the failure that never happens. The patterns (some met earlier, unified here):
- Timeouts everywhere: no unbounded wait on a BMC/network/lock. A missing timeout turns one slow dependency into a system-wide hang.
- Retries + exponential backoff + jitter + a cap (Phase 02): handle transient faults without a retry storm (synchronized retries that DDoS a recovering dependency — jitter prevents the thundering herd).
- Circuit breaker: after N failures to a dependency, stop trying for a cooldown (open the breaker), then test tentatively (half-open) before resuming (closed). Prevents hammering a dead BMC and cascading failures. Lab 02.
- Idempotency (Phase 05): operations safe to retry/re-run, so retries and reconciliation can't corrupt state.
- Reconciliation / self-healing (Phase 06): continuously converge actual to desired, so transient faults auto-correct rather than requiring a human.
- Bulkheads: isolate resources (separate pools/queues per dependency or tenant) so one failure can't sink everything (like a ship's compartments).
- Graceful degradation & fail-safe (Phase 02): lose one sensor → mark stale, keep going; on a leak → fail safe (Phase 04). Decide fail-open vs fail-closed deliberately per case.
- Backpressure & rate limiting: shed or slow load rather than collapse under it.
- Redundancy & failure-domain awareness (Phase 01): N+1, spread across domains.
Designing these in is reliability engineering; Lab 02 makes a naive loop survive chaos by adding them.
Chapter 6: Chaos & Fault Injection
You don't truly know your system's failure behavior until you've caused failures on purpose. Chaos engineering: inject controlled failures — kill a process, slow a dependency, drop packets (partition), corrupt a response, exhaust a resource, throttle a node (thermal, Phase 04) — and verify the system degrades gracefully and recovers. The discipline:
- Hypothesis-driven: "if the BMC for node-7 stops responding, provisioning should retry and reconcile, not hang the controller." Then inject and check.
- Bounded blast radius: start in a lab/canary, bound the experiment, have an abort.
- Game days: scheduled exercises where the team practices an incident — building the muscle and the runbooks (Phase 12) before the real one.
- Fault injection in tests (Phase 11): the fake driver (Phase 02 seam) that returns errors/ timeouts/garbage is unit-level chaos — cheap and deterministic. Lab 02's harness scales this idea to a reconcile loop.
Chaos finds the bugs normal tests miss because normal tests exercise the happy path; production is mostly unhappy paths. For a rack platform — fragile BMCs, flaky hardware, network blips — assume faults and prove resilience before the fleet does it for you.
Chapter 7: RCA and the Blameless Postmortem
An incident you don't learn from will recur. The RCA (Root Cause Analysis) / postmortem turns a failure into a permanent improvement — and writing a good one is a Staff-level signal (Phase 12).
- Blameless: focus on the system and process that allowed the failure, not the person. People act reasonably given the information and tools they had; if a human could cause an outage, the system let them. Blame kills the honesty that makes postmortems useful.
- The 5 Whys: keep asking "why" past the proximate cause to the systemic one. "The node went down" → why? "OOM" → why? "a leak in the exporter" → why? "an unbounded cache" → why? "no resource limit / no test for it" → there's the root cause and the fix.
- Root cause vs contributing factors: usually it's not one cause but a chain; identify the systemic one(s) you can fix to prevent the class of failure, not just this instance.
- The document: timeline (what happened when), impact (who/what, how long, SLO/error-budget burned — Phase 07), detection (how you found out — and how you should have), root cause, the fix, and — the most important part — action items: concrete, owned, dated changes (a test, an alert, a guard, a runbook, a design change) that prevent recurrence.
- Close the loop: feed the fix into a regression test (Phase 11), an alert/runbook (Phase 07/12), and the design (Phase 12). An RCA without tracked action items is a diary entry, not engineering.
This is where "own root-cause analysis" (the JD) becomes leadership: you don't just fix the bug, you make the organization permanently better at not having it.
Lab Walkthrough Guidance
Order: Lab 01 (debug a real bug) → Lab 02 (prevent failures with chaos). Reactive skill first, then proactive.
Lab 01 (debugging dojo):
make && ./buggy— observe the wrong/crashing behavior; resist guessing.make asan— let AddressSanitizer point at the exact bug (allocation + use sites).- Confirm your understanding with
gdb(backtrace) /valgrind; fix it; verify clean under ASan. - Write
RCA.md: timeline → root cause → fix → prevention. The writeup is the lab.
Lab 02 (chaos + reconciliation):
python3 solution.py— baseline (no faults), then under chaos (random failures, slow deps, partitions).- See the naive loop fail to converge and the hardened loop (retry/backoff + circuit breaker + idempotency) converge anyway.
- Watch the circuit breaker open under sustained failure and recover (half-open → closed).
- Extensions: add a bulkhead/rate limiter; inject a partition; measure error budget burned.
Phase capstone question (the one to nail): "A production node reboots intermittently under load. Walk me through your debugging — you can't take it offline." (Answer: scope the failure domain first (one node or shared?); evidence-first via OOB — SEL/BERT (Phase 08) for the last-crash cause, RAS parser for AER/MCE, telemetry (Phase 07) correlating reboots with temperature/power/load; "what changed?" — recent firmware (Phase 08) or deploy; reproduce on a canary with chaos/load; if MCE points at a DIMM with rising correctable ECC under thermal load → predictive DIMM fault → cordon/drain (Phase 06), RMA (Phase 08), re-validate; then prevent — add the alert and the test, write the blameless RCA. The method, narrated, is the answer.)
Success Criteria
You're done with this phase when — without notes:
- You can narrate the systematic debugging loop and apply it to a prompt (Ch. 1)
- You can pick the right tool per symptom (gdb/strace/perf/ASan/TSan/eBPF) (Ch. 2)
- You can debug a deployed system evidence-first via OOB + eBPF + correlation (Ch. 3)
- You can explain races/Heisenbugs/memory bugs and the tools that catch them (Ch. 4)
- You can explain retries+backoff+jitter, circuit breakers, idempotency, reconciliation, bulkheads (Ch. 5)
- You found the planted bug with tools and verified the fix under ASan (Lab 01)
- Your hardened reconcile loop converges under chaos; you can explain why (Lab 02)
- You can write a blameless RCA with a real root cause and tracked action items (Ch. 7)
Interview Q&A
Q1: A production node reboots intermittently under load and you can't take it offline. How do you debug it? A: Method first. (1) Scope the failure domain (Phase 01): is it just this node or others on the same PDU/ToR/CDU? (2) Evidence-first via OOB since the host is unreliable: read the SEL and BERT (Phase 08) for the last-crash cause, run the RAS parser on AER/MCE, and correlate the reboots with telemetry (Phase 07) — do they line up with temperature, power draw, or load spikes? (3) Ask "what changed?" — a recent firmware update (Phase 08) or deploy is the prime suspect; check versions. (4) Form a hypothesis and test it cheaply — e.g., if MCE points at a DIMM with rising correctable ECC that climbs with temperature, the hypothesis is a marginal DIMM failing under thermal load. Reproduce on a canary with load/chaos rather than the live node. (5) Fix: cordon/drain (Phase 06), RMA the DIMM (Phase 08), re-validate (Phase 01), return to service. (6) Prevent: add the alert and a test, and write a blameless RCA. The point is the narrated method and using OOB/telemetry because I can't attach a debugger to a tenant-serving node.
Q2: A daemon is pegged at 100% CPU. How do you find why?
A: perf top or a perf record + flame graph on the process shows where the cycles actually go —
the hot function jumps out, no guessing. If it's a busy-loop, the flame graph shows the loop; if
it's a specific call, it's named. I'd cross-check with strace (is it spinning on a syscall like a
failing non-blocking read?) and metrics (Phase 07) for what changed. The anti-pattern is adding
print statements and rebuilding for hours; perf answers it in minutes. Then I fix the hot path
(or add backpressure if it's overload) and add a CPU-usage alert so it's caught next time.
Q3: A process is hung. What's your first move?
A: strace -p <pid> to see the exact syscall it's blocked in. If it's stuck in a futex, it's a
lock/deadlock — I'd inspect with gdb -p and look at thread backtraces for who holds what. If it's
blocked in read/recvfrom/connect, it's waiting on a slow or dead dependency (a BMC, the
network) with no timeout — the bug is the missing timeout (Phase 05/10). strace turns "it's hung"
into "it's blocked here, waiting on this," which is the whole clue. For a production process I'd
prefer eBPF/bpftrace to peek without risk.
Q4: What's a circuit breaker and why do you need one with retries? A: A circuit breaker stops calling a failing dependency after a threshold of failures: it "opens" (fail fast for a cooldown), then goes "half-open" to test tentatively, and "closes" again once the dependency recovers. You need it with retries because retries alone create a retry storm — when a BMC or service is down, every client retrying (especially in sync) hammers it and prevents recovery, cascading the failure. Backoff + jitter spread the load; the breaker stops the bleeding entirely when the dependency is clearly down, fails fast for callers (better than timeouts piling up), and probes for recovery. Together with idempotency and timeouts, it's the core of resilient calls to flaky hardware like BMCs.
Q5: How do you make an intermittent race condition reproducible and fix it?
A: Races are timing-dependent and often vanish under a debugger (a Heisenbug), so I make the
nondeterminism deterministic. The decisive tool is ThreadSanitizer — it detects the racing
accesses without needing the race to actually manifest, pointing at the two unsynchronized accesses
and the missing lock. I review the critical sections (Phase 02): is shared state accessed under a
lock_guard, are critical sections tiny, is anything held across I/O? In tests I inject the clock/
scheduling to force the interleaving. The fix is proper synchronization (a guard, an atomic, or
redesigning to avoid shared mutable state), and I add a TSan run to CI (Phase 11) so the class of
bug can't return. The principle: don't chase a Heisenbug by adding logging (which changes timing) —
use the sanitizer that finds it deterministically.
Q6 (Staff): Tell me how you run a postmortem after a serious incident. A: Blamelessly and with tracked outcomes. I write a document with a precise timeline, the impact (who/what, duration, SLO/error-budget burned — Phase 07), how we detected it (and how we should have, sooner), and then drive to root cause with the 5 Whys — past the proximate cause ("the node OOMed") to the systemic one ("an unbounded cache with no memory limit and no test"). I separate the root cause from contributing factors and focus fixes on preventing the class of failure. The most important section is the action items: concrete, owned, dated — a regression test (Phase 11), an alert and runbook (Phase 07/12), a guard, maybe a design change — and I track them to completion. I keep it blameless because if a human could trigger the outage, the system allowed it, and blame destroys the honesty that makes the postmortem useful. As a Staff engineer, the deliverable isn't just the fix — it's an organization that won't have that class of incident again, and the shared learning across the (often distributed) team.
References
- "Debugging" methodology — David J. Agans, Debugging: The 9 Indispensable Rules
- Brendan Gregg, Systems Performance & BPF Performance Tools (perf, eBPF, flame graphs) — https://www.brendangregg.com/
gdb,strace,ltrace,valgrind,perfman pages & tutorials- AddressSanitizer/ThreadSanitizer (Phase 02) — https://clang.llvm.org/docs/AddressSanitizer.html
- bpftrace / bcc (eBPF tracing) — https://github.com/iovisor/bpftrace
- Release It! (Michael Nygard) — circuit breaker, bulkhead, stability patterns
- Netflix Chaos Engineering / "Principles of Chaos" — https://principlesofchaos.org/
- Google SRE Book/Workbook — postmortem culture, the blameless postmortem template — https://sre.google/books/
- Cross-track: Security Engineer — incident response & detection
🛸 Hitchhiker's Guide — Phase 10: Reliability, Debugging & RCA
Read this if: you debug by instinct and want it to be a repeatable method — the JD's #1 skill. Compressed map of the loop, the toolset, the prevention patterns, and the RCA.
0. The 30-second mental model
Debugging is a method: reproduce → observe (don't guess) → "what changed?" → hypothesize → bisect → fix → prevent. Match the tool to the symptom. For deployed systems you can't restart, work evidence-first via OOB + eBPF + correlation. Then prevent failures with timeouts, retry+backoff+jitter, circuit breakers, idempotency, reconciliation, prove it with chaos, and close the loop with a blameless RCA that ships action items.
1. The debugging loop
reproduce → observe the evidence first → ask "what changed?" (deploy/firmware/config/load) → hypothesize (falsifiable) → bisect (code/data/fleet/time) → fix → verify → prevent (test/ alert/guard/runbook). Scope the failure domain first (Phase 01): one node or shared resource?
2. Tool ↔ symptom
| symptom | tool |
|---|---|
| crash / wrong state | gdb (+ core dump) |
| hung / "what's it doing?" | strace (syscalls), ltrace |
| slow / 100% CPU | perf + flame graph |
| memory corruption/leak | ASan / valgrind |
| data race | TSan |
| production, can't restart | eBPF (bpftrace/bcc), ftrace |
| first evidence | logs/metrics/traces (Phase 07) |
Anti-pattern: printf-rebuild loops when strace/ASan/perf answer it in minutes.
3. Deployed-system debugging
Can't attach/restart → evidence-first: logs/metrics/traces (P07), SEL/BERT + RAS (P08), SoL (P04); eBPF for live insight w/o restart; correlate metric→trace→log; reproduce on a canary; bisect across the fleet (all nodes? one rack? one firmware version?).
4. The hard bugs
races/Heisenbugs (→ TSan, deterministic tests, inject the clock) · memory (→ ASan/valgrind; crash is far from cause) · resource exhaustion (unbounded growth → bound it) · hardware nondeterminism (flaky BMC/marginal PCIe/thermal → is it SW or HW? use RAS+telemetry).
5. Prevention patterns
timeouts everywhere · retry + backoff + jitter + cap (no retry storm) · circuit breaker (open→half-open→closed) · idempotency · reconciliation/self-heal · bulkheads · graceful degradation / fail-safe · backpressure/rate-limit · redundancy + failure-domain awareness (P01).
6. Chaos
Inject failures on purpose (kill/slow/partition/corrupt/exhaust/throttle) → verify graceful degrade + recovery. Hypothesis-driven, bounded blast radius, game days. The fake-driver seam (P02) is unit-level chaos. Chaos finds the unhappy paths tests skip.
7. RCA / blameless postmortem
Blameless (if a human could cause it, the system allowed it) · 5 Whys to the systemic cause · root cause vs contributing factors · doc = timeline + impact (SLO/budget) + detection + root cause + fix + owned, dated action items (test/alert/guard/runbook/design). No tracked actions = a diary, not engineering.
8. The "done this before" tells
"What changed?" · "Is it one node or a shared resource?" · "Did you strace it / look at the flame
graph?" · "ASan-clean?" · "Where's the timeout / circuit breaker?" · "Is it idempotent under
retry?" · "What are the action items?"
9. How this phase pays off later
It IS the JD's #1 skill — front and center in interview-prep/04. Prevention patterns harden the
Phase 13 capstone; RCAs become Phase 12 runbooks; chaos feeds Phase 11 testing; evidence comes from
Phases 07/08.
Lab 01 — Debugging Dojo: Find the Planted Bug
Phase: 10 — Reliability & Debugging | Difficulty: ⭐⭐⭐⭐☆ | Time: 3–5 hours
Language: C11 | Hardware: none | Needs: a C compiler with ASan; optional gdb/valgrind
Concept primer:
../WARMUP.mdCh. 1–4,../HITCHHIKERS-GUIDE.md§1–§4.
Run
make # normal build — buggy may APPEAR to work (a latent bug)
make asan # AddressSanitizer build — points at the exact bug
make valgrind # alternative detector
make fixed # the corrected version, clean under ASan
0. The mission
Practice the method, not the answer: a deliberate memory-safety bug lives in buggy.c. Find it
with tools (ASan/gdb/valgrind), explain it, fix it, and write the RCA (RCA.md). The
point is that a normal build may run fine — latent memory bugs are why you need sanitizers, and why
"it works on my machine" is not "it's correct."
1. The exercise (don't peek at fixed.c first)
make && ./buggy— observe. It may print plausibly or crash, depending on your libc. That ambiguity is the lesson.make asan— AddressSanitizer reports heap-use-after-free atprint_all(buggy.c:45) with two stack traces: where the memory was freed and where it was used. That's the localization tools give you for free.- Confirm with
gdb ./buggy(run, backtrace) and/orvalgrind. - Find the root cause in
remove_reading(it frees a node without unlinking it → the previous node's->nextdangles → UAF on the next traversal, and a double-free at cleanup). - Fix it (unlink before free; a sentinel handles head-removal — see
fixed.c), and verifymake fixedis clean under ASan. - Write
RCA.md— the writeup is the deliverable. Drive the 5 Whys to the systemic cause (no ASan in CI, no list-mutation tests) and list owned action items.
2. Why this matters
This is the JD's #1 skill in miniature: a real, latent, close-to-hardware bug found by method and tools, not luck — and turned into a permanent fix via a blameless RCA. Memory-safety bugs are the top cause of CVEs and the hardest production crashes (they crash far from their cause); ASan/TSan in CI (Phase 11) is how you stop them.
3. Extensions
- Plant a race in a threaded version and find it with TSan (
-fsanitize=thread). - Debug a hung process: add a blocking
readon a pipe that never gets data; find it withstrace -p. - Core-dump analysis:
ulimit -c unlimited, crash it, thengdb ./buggy coreand backtrace. git bisect: make a repo with the bug introduced in a middle commit and bisect to it.
4. What this lab proves
You debug with method and the right tool, you fix root causes (not symptoms), and you close the loop with a blameless RCA + preventive action items. "Tell me about a hard bug you root-caused" becomes a real story (STAR — Phase 12), and "you suspect memory corruption — what tools?" becomes a demonstration.
Lab 02 — Chaos & Reconciliation Hardening
Phase: 10 — Reliability & Debugging | Difficulty: ⭐⭐⭐⭐☆ | Time: 4–6 hours Language: Python 3 (stdlib) | Hardware: none
Concept primer:
../WARMUP.mdCh. 5–6,../HITCHHIKERS-GUIDE.md§5–§6.
Run
python3 solution.py # baseline -> transient chaos -> outage+recovery -> permanent outage
0. The mission
Prove (don't just claim) that reliability patterns matter: build a chaos source that injects transient failures and outages into a dependency (a flaky BMC), and show a hardened reconcile loop (retry + backoff + jitter + circuit breaker + idempotency) protects the system where a naive loop creates a retry storm.
1. The lesson the numbers tell
- Transient failures: both converge — retry handles transient faults.
- Outage + recovery: both converge after recovery, but the naive loop hammers the dead BMC every tick (a retry storm), while the hardened loop's breaker opens and fast-fails, hitting the dead dependency far fewer times.
- Permanent outage: the decisive case — the naive loop hammers the dead BMC 60×; the hardened loop hits it only ~14× (breaker open, periodic half-open probes). When a dependency is truly down, a retry storm prevents its recovery and can cascade the failure; the breaker is what stops that.
The breaker transitions (->open, open->half-open, ->closed) show the state machine that
makes this work — and it's the same self-healing idea as the Phase 06 operator, hardened.
2. The patterns in play
- Idempotency: converging = setting desired state, so retries can't corrupt it.
- Retry + backoff + jitter (capped): absorbs transient faults without a thundering herd.
- Circuit breaker: fail fast when a dependency is clearly down; protect it and the caller; probe for recovery.
3. Extensions
- Add a bulkhead (a per-dependency concurrency limit) and show one slow dependency can't starve the others.
- Inject a slow dependency (latency, not failure) and add a timeout; show an unbounded wait hangs the loop without one.
- Add an SLO and measure the error budget burned under each chaos scenario (Phase 07).
- Make the chaos a game day: a scripted sequence of faults the on-call must diagnose using the breaker transitions and metrics.
4. What this lab proves
You can reason about and demonstrate resilience — the difference between code that limps along and code that fails safe and recovers. "What's a circuit breaker and why do you need one with retries?" becomes a runnable proof (the 60× vs 14× retry-storm contrast), and these patterns harden the Phase 13 capstone's control loop. Chaos engineering — finding the unhappy paths before production does — is the proactive half of the JD's reliability mandate.
Phase 11 — CI/CD, Testing & Release Engineering
Difficulty: ⭐⭐⭐⭐☆ | Estimated Time: 1–2 weeks Roles supported: Rack Management SWE (Senior/Staff), SRE, Platform/Infra SWE Hardware needed: none — test pyramid + rollout simulators run locally
Why This Phase Exists
The JD: "Participate in code reviews, testing efforts, and continuous improvement of build, test, and deployment pipelines." Infrastructure software that runs close to hardware has a special testing problem — you can't put a BMC, PDU, CDU, or accelerator in CI — so the test strategy that works for web apps doesn't transfer. This phase builds the answer: a test pyramid anchored on the hardware-boundary seam (Phase 02/03), emulator-based CI (a Redfish mock in the pipeline), hardware-in-the-loop (HIL) at the top, and release engineering that ships changes to a fleet safely (versioning, canary, progressive rollout, rollback) — the same discipline that makes firmware rollouts (Phase 08) and k8s upgrades (Phase 06) safe.
You'll build a test pyramid with emulated hardware in CI and a fleet canary-rollout simulator with automatic rollback.
Concepts
Testing close-to-hardware software
- The test pyramid: many fast unit tests (against the Phase 02 seam/fakes), fewer
integration tests (against emulators — Redfish mock,
ipmi_sim,snmpsim), a thin top of HIL (hardware-in-the-loop) tests on real gear, plus soak/stress tests - Why the seam (Phase 02 Ch. 9) is what makes the wide base possible; deterministic time/IO
- Emulator-based CI: running the Redfish mock / IPMI sim in the pipeline so integration tests need no hardware
- HIL labs: a small pool of real hardware behind CI for the tests only real devices can validate; conformance suites per vendor/firmware (Phase 03 Ch. 9)
- Test types: unit, integration, contract, end-to-end, property-based, fuzzing (for parsers — Phase 08), soak
CI/CD pipelines
- The pipeline stages: lint/format/type → build → unit → integration (emulators) → security scan → artifact → deploy → HIL/soak
- Quality gates (Phase 02):
ruff/black/mypy/clang-tidy/sanitizers (ASan/UBSan/TSan), coverage - Artifact management & versioning (semver), reproducible builds, signing (Phase 08/09)
- GitHub Actions / GitLab CI mechanics; matrix builds; caching; secrets in CI (no leaks)
Release engineering (ship to a fleet safely)
- Versioning & compatibility (the agent ↔ control-plane ↔ firmware version matrix)
- Canary + progressive rollout (1 → 1% → 10% → 100%) with health/SLO gates (Phase 07) and automatic rollback
- Blue/green vs rolling vs canary; feature flags; drain-first (Phase 06); failure-domain-aware waves (Phase 01)
- Rollback strategy: image-pinned, fast, tested; the "can you undo it?" question
- The relationship to firmware rollout (Phase 08) and k8s upgrades (Phase 06) — same discipline
Code review & continuous improvement (the JD's words)
- Effective code review (correctness, security, tests, the seam); review as knowledge-sharing (Phase 12)
- Continuously improving the pipeline: flake reduction, speed, coverage, new gates after incidents (Phase 10 RCA → a test)
Labs
Lab 01 — CI With Emulated Hardware + Test Pyramid
| Field | Value |
|---|---|
| Goal | Build a test pyramid for rack software where the wide base runs against fakes/emulators (no hardware), and wire it into a CI pipeline that runs a Redfish mock as a service — so integration tests pass in CI with zero hardware. |
| Concepts | Test pyramid, the hardware seam, emulator-based integration tests, deterministic tests, CI stages/gates, fuzzing a parser. |
| Steps | 1) python3 run_tests.py — runs unit (fakes) + integration (against an in-process Redfish mock). 2) Read the pyramid and the seam usage. 3) Read .github/workflows/ci.yml (the real pipeline). 4) Extensions (real Redfish mock service, HIL). |
| Stack | Python 3 stdlib (test runner + emulator) + a real GitHub Actions workflow |
| Output | A runnable test pyramid (unit + emulator-integration) + a CI workflow that needs no hardware. |
| How to Test | python3 run_tests.py — the suite runs and reports per-tier results; the integration tier exercises a live (mock) Redfish service. |
| Talking Points | Why you can't put a BMC in CI and how the seam fixes it; the pyramid shape; emulators vs HIL; what to fuzz (the RAS/Redfish parsers). |
| Resume Bullet | "Designed a test pyramid for close-to-hardware rack software (unit on fakes, integration against Redfish/IPMI emulators, HIL on real gear) and an emulator-based CI pipeline requiring no hardware." |
| Extensions | Run the real DMTF Redfish-Mockup-Server as a CI service container; add mypy/ruff/ASan gates; add property-based fuzzing of the Phase 08 RAS parser; add a HIL stage gated to a hardware pool. |
Lab 02 — Fleet Canary & Rollback
| Field | Value |
|---|---|
| Goal | Build a progressive-rollout controller that ships a new agent/firmware version across a fleet in waves (1 → 1% → 10% → 100%), gates each wave on health/SLOs (Phase 07), and automatically rolls back on regression — failure-domain-aware. |
| Concepts | Canary, progressive rollout, health/SLO gates, automatic rollback, blast-radius/failure-domain waves, version pinning. |
| Steps | 1) python3 solution.py — a healthy rollout completes; a bad version is caught at the canary and rolled back. 2) Read the wave/gate/rollback logic. 3) See a domain-aware wave plan. 4) Extensions. |
| Stack | Python 3 stdlib |
| Output | A rollout controller demonstrating canary gating + auto-rollback, with the fleet never fully exposed to a bad version. |
| How to Test | Asserts verify: a good version reaches 100%; a bad version is halted at the canary and rolled back; waves respect failure domains; rollback restores the prior version. |
| Talking Points | Why 1% before 100%; what gates a promotion; image-pinned rollback; why you don't update a whole failure domain at once; same pattern as firmware/k8s. |
| Resume Bullet | "Built a failure-domain-aware progressive-rollout controller with health/SLO-gated promotion and automatic rollback, bounding the blast radius of a bad release across a fleet." |
| Extensions | Add error-budget-based halting (Phase 07); add a bake-time per wave; integrate with the Phase 08 firmware orchestrator and Phase 06 drain. |
Deliverables Checklist
- You can draw the test pyramid and place rack tests (unit/emulator-integration/HIL/soak)
- You can explain why the hardware seam makes the wide base possible
- Test suite runs unit (fakes) + integration (against an emulator) with no hardware
- You can name the CI stages/gates and what each catches
- Rollout controller does canary → progressive → 100% with health gates
- A bad version is caught at the canary and auto-rolled-back; the fleet is never fully exposed
- Waves respect failure domains (Phase 01)
- You can connect this discipline to firmware rollout (Phase 08) and k8s upgrade (Phase 06)
Interview Relevance
- "How do you test software that talks to hardware you don't have in CI?" (seam + emulators + HIL)
- "Describe the test pyramid for this kind of system."
- "Walk me through a CI/CD pipeline for a rack-management agent."
- "How do you safely roll out a new version to 10,000 nodes?" (canary + gates + rollback)
- "What gates a canary promotion, and what triggers a rollback?"
- "How is rolling out an agent like rolling out firmware?" (same discipline, Phase 08)
- "What do you look for in a code review?" (correctness, security, tests, the seam)
Warmup Guide — CI/CD, Testing & Release Engineering
Zero-to-expert primer for Phase 11. The twist for this role: you can't put a BMC/PDU/CDU/ accelerator in CI, so the standard test playbook needs adapting. This guide builds the hardware-aware test pyramid, emulator-based CI, and the release engineering (canary/rollback) that ships changes to a fleet without incidents.
Table of Contents
- Chapter 1: The Special Problem — No Hardware in CI
- Chapter 2: The Test Pyramid for Rack Software
- Chapter 3: Emulators and HIL
- Chapter 4: The CI Pipeline and Its Gates
- Chapter 5: Versioning & Compatibility
- Chapter 6: Release Engineering — Canary, Rollout, Rollback
- Chapter 7: Code Review & Continuous Improvement
- Lab Walkthrough Guidance
- Success Criteria
- Interview Q&A
- References
Chapter 1: The Special Problem — No Hardware in CI
A web service's dependencies (a DB, an API) can run in CI as containers. A rack-management agent's dependencies are physical: a BMC, a PDU, a CDU, a PCIe switch, an accelerator. You cannot spin those up in a CI runner. Yet the JD demands "continuous improvement of build, test, and deployment pipelines" — so the central question is how do you test thoroughly without the hardware?
The answer has three layers, and it's the spine of this phase:
- The seam (Phase 02 Ch. 9 / Phase 03 Ch. 8): program against a
DeviceDriver/SensorSourceinterface so most logic is tested against fakes that simulate any behavior (failures, garbage, timeouts) deterministically — the wide base of the pyramid. - Emulators in CI: real software implementations of the protocols (the Redfish mock,
ipmi_sim,snmpsim) run as services in the pipeline, so integration tests exercise real protocol behavior with no physical hardware. - HIL (hardware-in-the-loop): a small pool of real devices behind CI, for the tests only real hardware can validate (vendor quirks, firmware behavior, timing) — the thin top.
Get this right and you ship rack software with confidence; get it wrong and every change is a gamble on production hardware.
Chapter 2: The Test Pyramid for Rack Software
The pyramid (wide fast base → narrow slow top), mapped to this domain:
- Unit (the wide base, ~70%): test pure logic against the fakes/seam — the budget calculator (Phase 01), the RAS parser (Phase 08), the reconcile loop (Phase 06), the retry/breaker (Phase 10). Fast (ms), deterministic (injected clock/RNG), no I/O. You can have thousands.
- Integration (~20%): test against emulators — your Redfish client (Phase 03) against the Redfish mock, the exporter (Phase 07) scraped by a real Prometheus, the firmware orchestrator (Phase 08) against a mock UpdateService. Slower (seconds), exercises real wire behavior.
- HIL / E2E (~10%): against real hardware in a lab — vendor conformance (Phase 03 Ch. 9), firmware flashes that must touch real flash, PCIe behavior under load. Slowest, scarcest, gated.
- Soak/stress (alongside): run for hours/days to catch leaks, drift, and rare races (Phase 10).
The shape matters: most bugs are caught cheaply at the base; the expensive HIL tier is reserved for what only real hardware can prove. Inverting the pyramid (mostly E2E) is slow, flaky, and can't run on every commit — a common, costly anti-pattern.
The enabler, again: the seam. Without an interface at the hardware boundary you can't write the base, and you're forced up the pyramid into slow, hardware-needing tests. Testability is a design decision made in Phase 02, paid off here.
Chapter 3: Emulators and HIL
Emulators are the middle tier's superpower:
- Redfish: the DMTF Redfish-Mockup-Server or OpenBMC sushy-emulator — a real Redfish service you run as a CI service container; your client/exporter/orchestrator test against it.
- IPMI:
ipmi_sim(OpenIPMI) simulates a BMC's IPMI interface. - SNMP:
snmpsimserves recorded MIBs so your SNMP poller tests against realistic data. - These catch the bugs fakes miss (real serialization, status codes, link-following, error bodies) while staying hermetic and fast enough for CI.
HIL (hardware-in-the-loop): a managed pool of real devices (a few BMCs, a PDU, maybe an accelerator node) reachable from CI for a gated stage. Used for: per-vendor conformance suites (Phase 03 Ch. 9 — does this ODM's Redfish behave?), firmware flash/rollback that must touch real flash (Phase 08), and timing/perf the emulators can't model. HIL is scarce and slow, so it runs on merge-to-main or nightly, not every commit, with careful scheduling and reset-between-tests.
The Staff insight: invest in the emulator + HIL infrastructure as a product — it's what lets a distributed team ship hardware-adjacent code quickly and safely, and it's where a lot of an infra team's leverage lives.
Chapter 4: The CI Pipeline and Its Gates
A pipeline for a rack agent, stage by stage (each a gate that fails fast):
- Static: format (
black/clang-format), lint (ruff/clang-tidy), types (mypy --strict) — Phase 02. Cheap, catches a lot. - Build: compile (C++ with
-Wall -Wextra -Werror), package (Python wheel), reproducibly. - Unit: the wide base against fakes — fast, must be green to proceed.
- Sanitizers (C/C++): run tests under ASan/UBSan/TSan (Phase 02/10) — catches the memory/ race bugs that become production crashes.
- Integration: against emulators (Redfish mock, etc.) as service containers.
- Security scan: dependency audit (
pip-audit), SAST, secret scanning (Phase 02 Ch. 8). - Artifact: build + sign (Phase 08/09) + version the artifact; publish.
- Deploy (CD): to a staging/canary environment; then HIL/soak (gated, slower).
Principles: fail fast (cheap gates first), deterministic (no flaky tests — a flaky test is a
broken test; quarantine and fix it), fast feedback (cache, parallelize; keep the per-commit path
minutes not hours), and everything-as-code (the pipeline is reviewed code, Lab 01's
ci.yml). Coverage is a signal, not a goal — chase meaningful coverage of failure paths, not a
number.
Chapter 5: Versioning & Compatibility
A rack fleet runs many versions at once during a rollout, so compatibility is a first-class concern:
- Semver (major.minor.patch): major = breaking, minor = additive, patch = fixes. Communicate intent; gate breaking changes.
- The version matrix: the node agent, the control plane, and the firmware (Phase 08) all version independently and must interoperate across a window (you can't update everything atomically). Design APIs to be backward/forward compatible (additive changes, tolerate unknown fields — like Redfish, Phase 03) so an old agent works with a new control plane and vice versa during the rollout.
- Pin versions for reproducibility and rollback (image digests, not floating tags).
The discipline mirrors the plugin-ABI/ schema-evolution problems in any platform: never break the contract a deployed component depends on without a migration path.
Chapter 6: Release Engineering — Canary, Rollout, Rollback
Shipping a change to one machine is easy; shipping to 10,000 without an incident is the skill (Lab 2, and the same discipline as Phase 08 firmware and Phase 06 k8s upgrades).
- Canary: deploy the new version to a tiny slice (1 node, then 1%), bake (let it run long enough for problems to surface), and gate promotion on health/SLOs (Phase 07) — error rate, latency, crash count, hardware health. Only promote if the canary is healthy.
- Progressive rollout: 1 → 1% → 10% → 50% → 100%, each wave gated, widening as confidence grows. A bad version caught at 1% is an annoyance; at 100% it's an outage.
- Automatic rollback: if a wave's gates regress, halt and roll back to the pinned previous version automatically — fast, tested, image-pinned. The "can you undo it in 60 seconds?" question is the heart of safe releases.
- Failure-domain-aware waves (Phase 01): don't put a whole PDU/ToR/rack in one wave — a bad release shouldn't down a correlated set; spread waves across domains.
- Strategies: rolling (replace gradually — default for agents), blue/green (two environments, switch traffic — fast rollback, double resources), canary (the gated subset above). Feature flags decouple deploy from release (ship dark, enable gradually).
- Drain-first (Phase 06): for anything requiring a node reboot/restart, cordon+drain tenant work first.
The mental model unifying Phases 06, 08, 11: every change to a fleet is a gated, canaried, reversible, domain-aware rollout — whether it's an agent, a config, firmware, or k8s. Master it once.
Chapter 7: Code Review & Continuous Improvement
The JD names code review and "continuous improvement" explicitly — the human + process layer (more in Phase 12).
Effective code review looks for, in priority order: correctness (does it do the right thing, including failure paths?), security (Phase 02 Ch. 8 — injection, validation, secrets), tests (is the change tested at the right tier? is there a seam?), operability (logs/metrics/ errors — Phase 07), and then style (let the linter handle most of it — don't bikeshed). Review is also knowledge-sharing across a distributed team and a place to mentor (Phase 12). Be kind, be specific, focus on the code not the person.
Continuous improvement of the pipeline is a never-done loop: reduce flakiness (the silent productivity killer — quarantine, root-cause, fix), speed it up (cache, parallelize), raise coverage of failure paths, and — crucially — turn every incident into a test (Phase 10 RCA → a regression test/gate so it can't recur). A pipeline that gets a new gate after each incident is a team that learns. That feedback loop (incident → RCA → test → gate) is the connective tissue between this phase and Phase 10.
Lab Walkthrough Guidance
Order: Lab 01 (test/CI) → Lab 02 (release). Build confidence in the change, then ship it safely.
Lab 01 (test pyramid + emulator CI):
python3 run_tests.py— runs unit (fakes) + integration (against an in-process Redfish mock); read the per-tier results.- See how the seam lets the base run with no hardware and how the integration tier uses a real (mock) Redfish service.
- Read
.github/workflows/ci.yml— the real pipeline (gates + the Redfish mock as a service). - Extensions: run the real DMTF mockup server as a CI service; add mypy/ASan gates; fuzz the RAS parser (Phase 08).
Lab 02 (canary + rollback):
python3 solution.py— a healthy version rolls out 1→1%→10%→100%; a bad version is caught at the canary and auto-rolled-back.- Read the wave/gate/rollback logic and the failure-domain-aware wave plan.
- Extensions: error-budget-based halting (Phase 07); bake time; integrate with Phase 08 firmware + Phase 06 drain.
Phase capstone question: "How do you test and ship a new rack-management agent to a 10,000-node fleet?" (Answer: a test pyramid — unit on fakes, integration on Redfish/IPMI emulators in CI, HIL on a real pool, soak for leaks — behind static/sanitizer/security gates; sign + version the artifact; roll out canary → progressive with health/SLO gates, automatic image-pinned rollback, failure-domain-aware waves, drain-first; and feed every incident back into a new test/gate.)
Success Criteria
You're done with this phase when — without notes:
- You can explain why hardware can't go in CI and the seam+emulator+HIL answer (Ch. 1, 3)
- You can draw the test pyramid and place this domain's tests on it (Ch. 2)
- You can list the CI stages/gates and what each catches (Ch. 4)
- You can explain semver and the agent/control-plane/firmware compatibility window (Ch. 5)
- You can design a canary → progressive rollout with health gates and auto-rollback (Ch. 6)
- You can explain failure-domain-aware waves and image-pinned rollback (Ch. 6, Phase 01)
- You can connect this to firmware (Phase 08) and k8s (Phase 06) as one rollout discipline
- You can state what you look for in code review and how incidents become gates (Ch. 7, Phase 10)
Interview Q&A
Q1: How do you test software that talks to hardware you don't have in CI?
A: Three layers. (1) The seam: program against a device interface so most logic is unit-tested
against fakes that simulate any behavior — failures, timeouts, garbage — deterministically
(inject the clock too). That's the wide base. (2) Emulators in CI: run real protocol
implementations as service containers — the DMTF Redfish mock, ipmi_sim, snmpsim — so
integration tests exercise real wire behavior with no physical hardware. (3) HIL: a small pool
of real devices behind a gated CI stage for what only hardware can prove — vendor conformance,
firmware flashes, timing — run on merge/nightly, not every commit. The key realization is that
testability is a design choice (the seam, Phase 02) made up front; without it you're forced into
slow, hardware-needing end-to-end tests for everything.
Q2: Describe the test pyramid for rack-management software. A: Wide base of fast unit tests against fakes — the budget calculator, RAS parser, reconcile loop, retry/breaker — milliseconds, deterministic, no I/O, thousands of them. A middle integration layer against emulators — the Redfish client against the Redfish mock, the exporter scraped by real Prometheus — seconds, exercising real protocol behavior. A thin top of HIL/E2E on real hardware for vendor quirks, firmware, and PCIe-under-load — slow and scarce, gated. Plus soak tests running for hours to catch leaks and rare races. Most bugs die cheaply at the base; the expensive tiers are reserved for what only they can prove. The anti-pattern is inverting it — mostly E2E — which is slow, flaky, and can't run per-commit.
Q3: How do you safely roll out a new agent version to 10,000 nodes? A: As a gated, canaried, reversible, domain-aware rollout. Sign and version the artifact (semver, image-pinned). Deploy to a canary (1 node, then 1%), bake it, and gate promotion on health/ SLOs (Phase 07) — error rate, crashes, hardware health. Then progress 1% → 10% → 50% → 100%, each wave gated, automatically rolling back to the pinned previous version if a wave regresses (fast, because it's image-pinned). Make waves failure-domain-aware (Phase 01) so a bad release can't down a whole PDU/ToR/rack at once, and drain-first (Phase 06) for anything needing a restart. Design the agent/control-plane API to be backward/forward compatible since many versions run during the rollout. It's the same discipline as a firmware rollout (Phase 08) and a k8s upgrade (Phase 06) — a bad change caught at 1% is an annoyance; at 100% it's an outage.
Q4: What gates a canary promotion and what triggers a rollback? A: Promotion is gated on the canary being demonstrably healthy over a bake period: SLO/health signals (Phase 07) — error and crash rates at or below baseline, latency within budget, no new critical alerts, hardware health nominal (no new AER/MCE — Phase 08), and ideally error-budget not burning abnormally. A rollback triggers when any of those regress past a threshold during a wave: a spike in crashes/errors, an SLO breach, or a new critical alert. The rollback is automatic and fast because the previous version is image-pinned, and it halts the rollout so the blast radius stays at the current wave. The principle: never promote on hope, and always be able to undo in seconds.
Q5: What do you look for in a code review for this kind of software? A: In priority: correctness including the failure paths (timeouts, retries, partial failures — not just the happy path); security (no shell injection, input validation, no secrets in code/ logs — Phase 02); tests at the right tier and the presence of a seam so it's testable without hardware; operability (structured logs, metrics, sane errors — Phase 07); and only then style, which the linter should mostly handle so we don't bikeshed. I also use review to share knowledge across the distributed team and mentor (Phase 12). And I treat every production incident as a prompt to add a test or a CI gate (Phase 10 RCA → a regression test), so the pipeline gets stronger over time — that continuous improvement is literally in the JD.
References
- Martin Fowler — Test Pyramid; "TestDouble"/mocks vs fakes — https://martinfowler.com/
- DMTF Redfish-Mockup-Server; OpenBMC sushy-emulator; OpenIPMI
ipmi_sim;snmpsim— emulators for CI - GitHub Actions / GitLab CI docs — pipelines, service containers, matrices — https://docs.github.com/actions
- AddressSanitizer/UBSan/TSan in CI (Phase 02/10) — https://clang.llvm.org/docs/
- Google — Software Engineering at Google (testing, CI, large-scale changes) — https://abseil.io/resources/swe-book
- Continuous Delivery (Humble & Farley) — deployment pipelines, canary, blue/green
- Google SRE Workbook — canarying releases, error budgets — https://sre.google/workbook/
- Cross-track: Apache PMC/Committer — release engineering
🛸 Hitchhiker's Guide — Phase 11: CI/CD, Testing & Release Engineering
Read this if: you can write tests but "test software that needs a BMC" and "ship to 10,000 nodes safely" are open questions. Compressed map of the hardware-aware pyramid and safe rollouts.
0. The 30-second mental model
You can't put a BMC/PDU/CDU in CI, so: seam → fakes (unit), emulators (integration), HIL (real, gated). Ship to a fleet as a gated, canaried, reversible, domain-aware rollout — the same discipline as firmware (Phase 08) and k8s upgrades (Phase 06). Master that one pattern.
1. The no-hardware-in-CI answer
| tier | against | speed | enables |
|---|---|---|---|
| unit (~70%) | fakes via the seam (P02/P03) | ms, deterministic | the wide base |
| integration (~20%) | emulators (Redfish mock, ipmi_sim, snmpsim) | seconds | real wire behavior |
| HIL/E2E (~10%) | real hardware pool, gated | slow/scarce | vendor quirks, firmware, timing |
| soak | long-running | hours | leaks, drift, rare races |
Don't invert the pyramid (mostly E2E = slow + flaky + can't run per-commit).
2. CI pipeline stages (fail fast)
static (fmt/lint/mypy/clang-tidy) → build (-Werror) → unit → sanitizers (ASan/UBSan/
TSan) → integration (emulators as services) → security scan (pip-audit/SAST/secrets) → artifact
(build+sign+version) → deploy → HIL/soak (gated). Flaky test = broken test (quarantine+fix).
3. Versioning
semver (major=breaking) · the agent ↔ control-plane ↔ firmware matrix runs many versions at once → backward/forward-compatible APIs (additive, tolerate unknown fields like Redfish) · pin digests, not floating tags.
4. Safe rollout (the unifying pattern)
canary (1 → 1%) → bake → GATE on health/SLO (P07) → 10% → 50% → 100%
auto-rollback (image-pinned) if a wave regresses ; HALT the rollout
waves are FAILURE-DOMAIN-aware (P01) ; drain-first (P06) for reboots
strategies: rolling (default) · blue/green (fast rollback, 2x resources) · canary (gated subset) · feature flags (deploy ≠ release). "Can you undo it in 60s?" is the test.
5. Code review priorities
correctness (incl. failure paths) > security (P02) > tests + a seam > operability (P07) > style (let the linter win). Review = knowledge-sharing + mentoring (P12).
6. Continuous improvement loop
incident → RCA (P10) → regression test → new CI gate. A pipeline that grows a gate after each incident is a team that learns. Also: kill flakiness, speed it up, cover failure paths.
7. The "done this before" tells
"Where's the seam — can this run without hardware?" · "Unit or does it need the emulator?" · "Is it canaried?" · "What gates the promotion / triggers rollback?" · "Are waves domain-aware?" · "Can we roll back in 60s?" · "Did the incident get a test?"
8. How this phase pays off later
Same rollout discipline as Phase 08 firmware + Phase 06 k8s upgrade. The test seam comes
from Phases 02/03; gates come from Phase 07; new tests come from Phase 10 RCAs. The
capstone (Phase 13) ships through this pipeline. system-design/03 is fleet rollout at scale.
Lab 01 — CI With Emulated Hardware + Test Pyramid
Phase: 11 — CI/CD & Testing | Difficulty: ⭐⭐⭐☆☆ | Time: 4–6 hours Language: Python 3 (stdlib) + a real GitHub Actions workflow | Hardware: none
Concept primer:
../WARMUP.mdCh. 1–4,../HITCHHIKERS-GUIDE.md§1–§2.
Run
python3 run_tests.py # unit (fakes) + integration (live in-process Redfish emulator)
Files
run_tests.py— the test pyramid runner (unit tier on fakes, integration tier on an emulator).ci.yml— an example CI pipeline running a Redfish emulator as a service (no hardware).
0. The mission
Solve the role's special testing problem — no BMC in CI — with the seam + emulator + HIL answer, and see the same logic tested two ways: against a fake (unit, fast, any behavior) and against a live Redfish emulator (integration, real HTTP/JSON).
1. What to notice
healthy_nodes()is pure logic programmed against aPowerSourceseam — so the unit tier injects aFakePowerSource(including a failing node) with zero hardware.- The same function runs in the integration tier against a real in-process Redfish HTTP emulator — catching the bugs fakes miss (serialization, status codes, parsing).
ci.ymlruns the integration tier against the DMTF Redfish-Mockup-Server as a service container, plus static/sanitizer/security gates, with HIL gated to main on a self-hosted hardware pool. That's the whole pyramid in a pipeline.
2. Extensions
- Point the integration client at the real
dmtf/redfish-mockup-server(run it locally) and atipmi_sim/snmpsim. - Add fuzzing of the Phase 08 RAS parser (
atheris/property-based withhypothesis) — parsers of attacker-influenced data must be fuzzed. - Add mypy/ruff/ASan gates and measure failure-path coverage (not just a line %).
- Build a tiny HIL harness that resets a device between tests (idempotent setup/teardown).
3. What this lab proves
You can make hardware-adjacent software thoroughly testable in CI — the seam-backed pyramid plus emulators-as-services — which is exactly the JD's "continuous improvement of build, test, and deployment pipelines." "How do you test code that needs a BMC?" becomes a demonstration, not a shrug.
Lab 02 — Fleet Canary & Rollback
Phase: 11 — CI/CD & Testing | Difficulty: ⭐⭐⭐⭐☆ | Time: 4–5 hours Language: Python 3 (stdlib) | Hardware: none
Concept primer:
../WARMUP.mdCh. 6,../HITCHHIKERS-GUIDE.md§4.
Run
python3 solution.py # healthy rollout to 100%; bad version caught at canary + rolled back
0. The mission
Ship a new version to a fleet without an incident: canary → progressive waves, each gated on health, with automatic image-pinned rollback and failure-domain-aware waves. This is the unifying rollout discipline — identical for an agent, a config, firmware (Phase 08), and a k8s upgrade (Phase 06).
1. What the run proves
- Healthy version reaches 100% through waves (canary 1 → 1% → 10% → rest), each promoted only after passing the health gate.
- Bad version is caught at the canary (1 node) and rolled back + halted — the self-test asserts zero nodes end up on the bad version and only the canary was ever touched. A bad release at 1% is an annoyance; at 100% it's an outage.
- A version that regresses slightly later (not in the canary) is still caught in an early wave, so the fleet is never fully exposed.
2. The mechanics
plan_waves: canary=1, then 1%, 10%, rest — widening confidence.- The gate: deploy a wave → bake → health-check → promote or roll back to the pinned prior version and halt.
- Failure-domain awareness (Phase 01): waves are spread so a bad release can't down a whole PDU/ToR at once.
3. Extensions
- Error-budget halting (Phase 07): halt if the rollout burns the budget too fast, not just on hard failures.
- Bake time per wave (a real clock) and soak before promotion.
- Integrate with the Phase 08 firmware orchestrator (each node's "deploy" is a verified A/B firmware update) and Phase 06 drain (cordon/drain before reboot).
- Blue/green variant: stand up the new version beside the old and switch, for instant rollback.
4. What this lab proves
You can ship to a fleet safely — the "how do you roll out to 10,000 nodes?" answer as a runnable
controller: canary, health gates, auto-rollback, domain-aware waves. Combined with Lab 01's test
pyramid, you own the JD's "build, test, and deployment pipelines," and you can articulate it as the
same discipline behind firmware and k8s rollouts. Feeds the Phase 13 capstone and system-design/03.
Phase 12 — Collaboration, EBOM & Staff Leadership
Difficulty: ⭐⭐⭐⭐☆ | Estimated Time: 1–2 weeks Roles supported: Staff/Senior Rack Management SWE, Tech Lead Hardware needed: none — this phase produces documents and practices
Why This Phase Exists
The JD's "softer" but decisive requirements — the ones that separate Senior from Staff: "Contribute to software EBOM and protocol documentation with Pegatron and other partners," "Participate in design discussions and reviews, providing implementation‑focused feedback," "Contribute towards documentation, runbooks, and operational procedures," "Strong written and verbal communication for a multi‑site, globally distributed team," and at Staff: "drive implementation of the designs assigned to the local engineering team and provide guidance and mentorship to junior engineers."
These aren't extras. In a distributed, multi-site program co-developed with ODMs (the JD names Pegatron), the artifacts and the communication are the engineering: a precise protocol spec prevents a month of integration pain; a good ADR prevents re-litigating a decision; a runbook turns a 3 a.m. incident from a hero effort into a checklist; mentorship multiplies the team. This phase makes you fluent in producing them.
Unlike the others, this phase's "labs" are the deliverable documents themselves — the exact artifacts you'd produce on the job, with templates and guidance you fill in and reuse.
Concepts
- The software EBOM (Engineering Bill of Materials): for software/firmware — the versioned inventory of components, firmware baselines, dependencies, and interfaces agreed with the ODM; why it's the contract that keeps a multi-party build coherent (ties to Phases 01, 08)
- Protocol/interface documentation: specifying Redfish schemas/OEM extensions, sensor lists, error semantics, and behavior precisely enough that two teams on two continents build to the same thing; the conformance suite as the spec's executable form (Phase 03/11)
- Working with ODMs/ISVs: the NPI process (Phase 01), defining interfaces up front, the defect-feedback loop, upstreaming (e.g., OpenBMC — Phase 04)
- Design reviews & ADRs (Architecture Decision Records): how to give and receive implementation-focused feedback; capturing decisions, alternatives, and consequences durably
- Runbooks & operational procedures: turning Phase 10 RCAs and Phase 08 diagnostics into repeatable, on-call-ready procedures; the runbook template
- Technical writing for a distributed team: async-first, precise, decision + consequence + honest limit; documents that survive timezones
- Staff leadership: driving implementation of a defined design across a local team; mentorship; raising the engineering bar (standards, Phase 02/11); influence without authority; the Senior→Staff transition
- Behavioral excellence (STAR): structuring the stories (debugging wins, RCAs, cross-team delivery) that interviews probe
Deliverables (the "labs" of this phase)
Produce each by filling in the template; they double as your interview portfolio.
| Deliverable | File | What it proves |
|---|---|---|
| Software EBOM & protocol spec | leadership/EBOM-AND-PROTOCOL-DOCS.md | You can write the ODM-facing contract (Pegatron-style) |
| Design review + ADR | leadership/DESIGN-REVIEWS-ADRS.md | You can drive a decision and give review feedback |
| Runbook & ops procedure | leadership/RUNBOOKS-AND-OPS.md | You can make operations repeatable and on-call-ready |
| Staff mentorship & comms plan | leadership/STAFF-MENTORSHIP.md | You can lead a distributed team and grow others |
Worked portfolio (complete, filled artifacts — leadership/portfolio/)
The templates above, filled in as standalone artifacts you can show in an interview (no placeholders):
| Artifact | File |
|---|---|
| Software EBOM + interface spec for a concrete node | portfolio/EBOM-AI-TRAY-2U.md |
| ADR log (7 real decisions) | portfolio/ADR-LOG.md |
| On-call runbooks (firmware rollout, node-won't-POST, coolant leak) | portfolio/RUNBOOKS.md |
| 30/60/90 mentorship plan + 6 STAR stories | portfolio/MENTORSHIP-AND-STAR.md |
Deliverables Checklist
- A software EBOM for a node/rack: components, firmware baselines, interfaces, versions
- A protocol/interface spec section precise enough for an ODM to build to (with conformance hooks)
- An ADR for a real decision (e.g., "Redfish-first driver abstraction") with alternatives + consequences
- A design-review feedback example (implementation-focused: feasibility/perf/reliability/maintainability)
- A runbook for a real procedure (e.g., "node won't POST" or "fleet firmware rollout")
- A mentorship/onboarding plan and an async-communication norm for a multi-site team
- 3–5 STAR stories drawn from the labs (a hard bug, an RCA, a cross-team delivery)
Interview Relevance
- "Walk me through how you'd document an interface for an ODM partner to implement."
- "What goes in a software EBOM and why does it matter?"
- "Tell me about a design review you led / disagreed in. How did you handle it?"
- "How do you write a runbook? What makes a good one?"
- "How do you mentor junior engineers / raise the team's bar?"
- "How do you communicate effectively in a distributed, multi-timezone team?"
- "What's the difference between Senior and Staff to you?"
- Behavioral (STAR): hardest bug, a conflict, a delivery under ambiguity, mentoring a struggling teammate.
Warmup Guide — Collaboration, EBOM & Staff Leadership
Zero-to-Staff primer for Phase 12 — the artifacts and behaviors that separate a strong coder from a Staff engineer who multiplies a distributed team. The technical phases gave you what to build; this gives you how to make it land across sites, partners, and people.
Table of Contents
- Chapter 1: Why the Soft Skills Are the Hard Part Here
- Chapter 2: The Software EBOM
- Chapter 3: Protocol & Interface Documentation (ODM-facing)
- Chapter 4: Design Reviews and ADRs
- Chapter 5: Runbooks & Operational Procedures
- Chapter 6: Writing & Communicating for a Distributed Team
- Chapter 7: The Staff Engineer
- Chapter 8: Behavioral Interviews (STAR)
- Deliverable Walkthrough Guidance
- Success Criteria
- Interview Q&A
- References
Chapter 1: Why the Soft Skills Are the Hard Part Here
This role is "execution-focused" and "well-suited for a senior engineer who thrives in a distributed team model, communicates effectively across sites," co-developing with ODMs (Pegatron). At that scale, the bottleneck is rarely "can someone write the code" — it's "do two teams on two continents share the same understanding of the interface, the decision, and the procedure." Your artifacts (EBOM, specs, ADRs, runbooks) and your communication are what create that shared understanding. A brilliant implementation built to a fuzzy spec is a month of rework; a clear spec built by competent engineers ships. So these "soft" deliverables have hard, measurable impact — and they're what the Staff bar is about.
Chapter 2: The Software EBOM
An EBOM (Engineering Bill of Materials) lists everything that goes into a product. For software/firmware, the EBOM is the versioned, authoritative inventory of what runs on a node/rack and the interfaces between parts — the contract that keeps a multi-party build (you + the ODM + ISVs) coherent. It typically captures:
- Firmware components & baselines (Phase 08): BIOS/UEFI, BMC, NIC/DPU, accelerator, PSU, PCIe switch, CPLD — each with the approved version and signing info.
- Software components: the rack agent, exporters, operators, drivers — versions and dependencies (Phase 11 compatibility matrix).
- Interfaces: which protocols (Redfish/IPMI/SNMP — Phase 03) and which exact schemas/OEM extensions, sensor lists, error semantics (Chapter 3).
- Configuration baselines: secure-boot keys, network/VLAN layout (Phase 09), default policies.
- Provenance & traceability: who owns each, source, licenses, support status.
Why it matters: at commissioning (Phase 01) you reconcile discovered reality against the EBOM — a mismatch is a finding raised back to the ODM. During the lifecycle, the EBOM is the source of truth for "what should be here," and drift from it is a defect. Without it, a multi-site, multi- vendor program descends into "works on the lab unit, not in Riyadh."
Chapter 3: Protocol & Interface Documentation (ODM-facing)
The JD specifically calls out protocol documentation with partners. The skill is writing an interface spec precise enough that another team builds the same thing without you in the room. A good interface spec:
- Names the standard and the deltas: "Redfish 2023.2, with these OEM extensions, these required
resources, these sensor names/units, these
AllowableValues." Don't assume; enumerate. - Specifies behavior, not just shape: state machines (power, firmware-update Task lifecycle — Phase 08), error semantics (what status/body on each failure), idempotency, timing/retry expectations (Phase 03 Ch. 9 — real BMCs vary).
- Is testable: pair the prose with a conformance suite (Phase 11) — executable expectations the ODM runs against their firmware. The suite is the unambiguous spec; prose drifts, tests don't.
- Versions and changes deliberately: the spec evolves; track it like code, with a changelog and compatibility notes (Phase 11 Ch. 5).
- Has an owner and a feedback loop: defects and gaps flow back to the partner; you upstream fixes where applicable (OpenBMC — Phase 04).
The mental model: you're writing the contract between organizations. Ambiguity in it becomes integration pain measured in weeks. Precision here is leverage.
Chapter 4: Design Reviews and ADRs
Design reviews: the JD wants "implementation-focused feedback on feasibility, performance, reliability, and maintainability." Giving good review feedback:
- Anchor on those four axes plus security (Phase 02/09) and operability (Phase 07). Ask "how does this fail?", "how is it tested without hardware?" (the seam, Phase 11), "what's the blast radius?" (Phase 01), "how do we roll it back?" (Phase 11).
- Separate must-fix (correctness/security) from opinion (style). Be specific and kind; critique the design, not the person. Offer alternatives, not just objections.
- Receiving feedback: assume good intent, ask for the why, disagree-and-commit when a decision is made.
ADRs (Architecture Decision Records): a short doc capturing a significant decision — the context, the decision, the alternatives considered, and the consequences (including the downsides you accept). ADRs prevent re-litigating settled questions, onboard newcomers ("why is it this way?"), and make trade-offs explicit. Every meaty decision in this curriculum is an ADR: "Redfish-first driver abstraction" (Phase 03), "A/B firmware banks" (Phase 08), "operator-based lifecycle" (Phase 06). Writing one is a deliverable here.
Chapter 5: Runbooks & Operational Procedures
A runbook is a step-by-step procedure for an operational task or incident, written so someone other than the author, possibly half-asleep at 3 a.m., can execute it safely. The JD wants "runbooks and operational procedures related to the software you own." A good runbook:
- Has a clear trigger (the alert/symptom — Phase 07) and a stated goal/outcome.
- Is a precise checklist: exact commands, expected outputs, decision points ("if X, then Y").
- Bounds risk: pre-checks, blast-radius notes (Phase 01), dry-run/confirmation for destructive steps (Phase 04), and a rollback (Phase 11).
- Links context: the relevant dashboard, the ADR, the escalation path, the partner contact.
- Comes from incidents: every RCA (Phase 10) that found a manual fix should leave a runbook (and ideally automation that makes the runbook obsolete).
Runbooks turn tribal knowledge into team capability — essential for a distributed, follow-the-sun on-call. The progression a Staff engineer drives: manual → runbook → automated → self-healing (the operator, Phase 06).
Chapter 6: Writing & Communicating for a Distributed Team
Multi-site means async-first: you can't tap a shoulder across an 8-hour timezone gap, so writing is the primary interface. The principles:
- Lead with the decision/ask, then the context (inverted pyramid) — readers in another timezone shouldn't dig for the point.
- Decision + consequence + honest limit: state what, why, and what you're unsure about or trading away. Honesty about limits builds trust and prevents nasty surprises.
- Precise and self-contained: a doc/message should be understandable without the meeting that isn't happening. Define terms, link references.
- Right medium: durable decisions → a doc/ADR (not a chat that scrolls away); quick syncs → short calls; status → a written update. Capture verbal decisions in writing.
- Respect timezones: minimize required-synchronous; when you do meet, rotate the painful hour fairly across sites.
Clear writing is not a "nice to have" in this role — it's the JD's explicit requirement and the mechanism by which a distributed team functions at all.
Chapter 7: The Staff Engineer
The Senior→Staff shift, framed for this JD ("drive implementation of the designs assigned to the local engineering team; provide guidance and mentorship"):
- Scope: a Senior owns features/components; a Staff owns outcomes across a team/area — making sure the design actually gets implemented well, integrated, and shipped, unblocking others.
- Leverage over output: a Staff multiplies the team — through standards (Phase 02/11), reviews (Chapter 4), specs and runbooks (Chapters 3, 5), mentorship, and good technical decisions — more than through personal commits. Your code matters, but your impact through others matters more.
- Mentorship: grow junior engineers deliberately — pairing, review-as-teaching, stretch assignments with a safety net, and clear feedback. A mentored team that ships beats a hero who bottlenecks.
- Influence without authority: you usually can't order; you persuade with clear reasoning, data, and prototypes, and you build trust by being reliably right and reliably honest.
- Driving implementation: take a defined design (architecture set elsewhere — the JD's model) and make it real: break it down, sequence it, identify risks early (the integration seams, Phase 03/04), keep the distributed team aligned, and own the debugging when reality bites (Phase 10).
- Knowing the limits of your role: the JD is explicit that architecture is often "defined elsewhere" — Staff here is about excellent execution and local leadership, not architecture astronautics. Reading that correctly (ambitious in execution, collaborative on direction) is itself a maturity signal.
Chapter 8: Behavioral Interviews (STAR)
Senior/Staff loops always include behavioral rounds; structure your stories with STAR: Situation (context), Task (your responsibility), Action (what you did — be specific and own it), Result (the outcome, quantified, plus what you learned). Prepare 5–6 stories that cover the JD's themes, drawn from this curriculum's work:
- A hard bug you root-caused (Phase 10 — the RCA story).
- A cross-team/ODM delivery under ambiguity (Phases 03/12).
- A production incident you handled and prevented from recurring (Phase 10).
- Mentoring someone / raising the bar (Phase 12).
- A disagreement in a design review handled well (Chapter 4).
- A tradeoff you made under constraints (e.g., capping to fit, Phase 01).
The bar: a decision + its consequence + the honest limit, told concisely with you clearly the actor. Vague "we" stories and no-quantified-result stories are the common failures.
Deliverable Walkthrough Guidance
Work the four leadership docs in order; each is a fill-in template that becomes a portfolio piece:
- EBOM & protocol spec (
leadership/EBOM-AND-PROTOCOL-DOCS.md): write the ODM-facing contract for a node — components, firmware baselines, the Redfish interface with OEM deltas, and conformance hooks. - Design review + ADR (
leadership/DESIGN-REVIEWS-ADRS.md): write an ADR for a real decision from the curriculum and a sample review-feedback note on the four axes. - Runbook (
leadership/RUNBOOKS-AND-OPS.md): write a runbook for a real procedure ("node won't POST" or "fleet firmware rollout") with trigger, steps, risk bounds, and rollback. - Mentorship & comms (
leadership/STAFF-MENTORSHIP.md): write an onboarding/mentorship plan and an async-communication norm for a multi-site team.
Then extract 3–5 STAR stories from the labs across all phases — that's your interview portfolio.
Success Criteria
You're done with this phase when — without notes:
- You can explain what a software EBOM contains and why it's the ODM contract (Ch. 2)
- You can write an interface spec precise + testable enough for a partner to implement (Ch. 3)
- You can write an ADR (context/decision/alternatives/consequences) and give review feedback on the 4 axes (Ch. 4)
- You can write an on-call-ready runbook with triggers, risk bounds, and rollback (Ch. 5)
- You can communicate decisions async for a distributed team (decision + consequence + limit) (Ch. 6)
- You can articulate the Senior→Staff difference and how you drive implementation + mentor (Ch. 7)
- You have 5–6 crisp STAR stories covering the JD's themes (Ch. 8)
Interview Q&A
Q1: What goes in a software EBOM and why does it matter for an ODM program? A: It's the versioned, authoritative inventory of everything that runs on a node/rack plus the interfaces between parts — firmware components and their approved baselines (BIOS/BMC/NIC/ accelerator/PSU/switch, with signing info — Phase 08), software components (agent/exporters/ operators) and their dependency/compatibility matrix (Phase 11), the exact management interfaces (Redfish schemas + OEM extensions, sensor lists, error semantics — Phase 03), config baselines (secure-boot keys, VLAN layout — Phase 09), and provenance/ownership. It matters because in a multi-party build (us + an ODM like Pegatron + ISVs) it's the contract that keeps everyone coherent: at commissioning I reconcile the discovered hardware/firmware against the EBOM and any mismatch is a finding raised to the partner, and throughout the lifecycle drift from the EBOM is a defect. Without it, you get "works on the lab unit, not in the field."
Q2: How would you document an interface for an ODM partner to implement? A: I write a spec precise and testable enough that they build the same thing without me in the room. It names the standard and the deltas (e.g., Redfish 2023.2 + these required resources, OEM extensions, sensor names/units, AllowableValues), specifies behavior not just shape (state machines like the firmware-update Task lifecycle, error semantics, idempotency, retry/timing expectations — because real BMCs vary), and — critically — pairs the prose with a conformance suite (Phase 11) the partner runs against their firmware, because executable expectations don't drift the way prose does. I version it like code with a changelog and compatibility notes, give it an owner, and run a defect-feedback loop (and upstream fixes to OpenBMC where applicable). The framing is that I'm writing a contract between organizations, and ambiguity in it costs weeks of integration pain.
Q3: What makes a good runbook, and where do runbooks come from? A: A good runbook has a clear trigger (the alert/symptom — Phase 07) and goal, then a precise checklist anyone can execute at 3 a.m. — exact commands, expected outputs, and decision points ("if X then Y"). It bounds risk: pre-checks, blast-radius notes (Phase 01), dry-run/confirmation on destructive steps (Phase 04), and a rollback (Phase 11); and it links the dashboard, the relevant ADR, and the escalation path. They come from incidents — every RCA (Phase 10) that ended in a manual fix should leave a runbook, and the Staff move is to then automate it away (manual → runbook → automated → self-healing via the operator, Phase 06). Runbooks turn one person's tribal knowledge into the whole distributed on-call's capability.
Q4: What's the difference between a Senior and a Staff engineer to you? A: A Senior owns features/components and delivers them well; a Staff owns outcomes across a team or area and works through leverage rather than personal output. Concretely for this role (where architecture is often defined elsewhere): a Staff drives implementation of the assigned design — breaking it down, sequencing it, surfacing integration risks early (the Redfish/hardware seams), keeping a distributed team aligned, and owning the hard debugging when reality bites (Phase 10) — and multiplies the team through standards, reviews, specs, runbooks, and mentorship. The mindset shift is from "how much can I build" to "how much better does the team/area get because I'm here," while staying hands-on and execution-focused. Reading the JD correctly — ambitious execution, local leadership, collaborative on direction rather than architecture-astronaut — is itself part of the maturity.
Q5: Tell me about a design review where you disagreed.
A: (STAR) Situation: reviewing a teammate's BMC-interaction module that hardcoded Redfish paths and
talked to the BMC synchronously on every Prometheus scrape. Task: I owned review for the telemetry
area. Action: I gave implementation-focused feedback on the four axes — maintainability/correctness
(hardcoded paths break across vendors; follow @odata.id links — Phase 03), reliability/
performance (synchronous scrape-time BMC calls hammer fragile BMCs and couple scrape latency to BMC
health — poll on our own schedule and cache, Phase 07) — separating must-fix from opinion, and I
brought a small prototype showing the cached approach. We disagreed on effort vs. payoff; I framed it
with the failure mode (a slow BMC stalling all scrapes) and we agreed to the cached design with a
follow-up. Result: the exporter stayed responsive under flaky-BMC conditions in the field, and we
codified "follow links, never hardcode" and "no synchronous device I/O on scrape" as review
standards. I critique the design, not the person, and I bring alternatives and data, not just
objections.
References
- Will Larson, Staff Engineer: Leadership Beyond the Management Track — https://staffeng.com/
- Tanya Reilly, The Staff Engineer's Path
- ADRs — Michael Nygard, "Documenting Architecture Decisions"; https://adr.github.io/
- Google SRE Book — runbooks/playbooks and postmortem culture — https://sre.google/books/
- Docs for Developers / Google Technical Writing courses — https://developers.google.com/tech-writing
- "Disagree and commit" / writing culture (Amazon, Stripe engineering writing) — for async comms
- DMTF Redfish (interface specification practice) — https://www.dmtf.org/standards/redfish
- Cross-track: Apache PMC/Committer track — review culture, governance, community
🛸 Hitchhiker's Guide — Phase 12: Collaboration, EBOM & Staff Leadership
Read this if: you can build the systems but the "Staff" parts — EBOM, specs, ADRs, runbooks, mentoring a distributed team — feel fuzzy. Compressed map of the artifacts that are the job.
0. The 30-second mental model
In a distributed, multi-site, ODM-partnered program, your artifacts (EBOM, interface specs, ADRs, runbooks) and your writing ARE the engineering — they create the shared understanding that lets two continents build one thing. Staff = leverage over output: multiply the team through standards, reviews, docs, decisions, and mentorship, while staying hands-on and execution-focused.
1. Software EBOM (the ODM contract)
versioned inventory of what runs on a node/rack + interfaces: firmware baselines (P08), software + compat matrix (P11), Redfish/IPMI/SNMP interfaces + OEM deltas (P03), config baselines (secure boot/ VLAN — P09), provenance. Commissioning reconciles discovered vs EBOM (P01); drift = a defect.
2. Interface spec (build-to-it-without-you)
name the standard + deltas (resources, OEM ext, sensor names/units, AllowableValues) · specify behavior (state machines, error semantics, idempotency, timing) · make it testable with a conformance suite (P11 — tests don't drift, prose does) · version it · owner + defect loop + upstream (OpenBMC, P04).
3. Design review + ADR
Review on: feasibility · performance · reliability · maintainability (+ security P02/09, operability P07). Ask: how does it fail? testable without HW (the seam, P11)? blast radius (P01)? rollback (P11)? Separate must-fix from opinion; critique the design, bring alternatives. ADR = context · decision · alternatives · consequences (incl. accepted downsides).
4. Runbook
trigger (alert/symptom, P07) → goal → precise checklist (commands + expected output + "if X then Y") → risk bounds (pre-checks, blast radius, dry-run/confirm P04, rollback P11) → links (dashboard/ADR/ escalation). Source = RCAs (P10). Progression: manual → runbook → automated → self-healing (P06).
5. Distributed-team comms (async-first)
lead with the decision/ask → context · decision + consequence + honest limit · self-contained (understandable without the meeting) · durable decisions in docs/ADRs not chat · respect timezones, minimize synchronous, rotate the painful hour.
6. Senior → Staff
scope: features → outcomes across an area · leverage > personal commits · drive implementation of a defined design (break down, sequence, de-risk seams P03/04, keep the distributed team aligned, own the debugging P10) · mentor deliberately · influence without authority (reasoning + data + prototypes) · know the role: ambitious execution, collaborative on direction.
7. STAR (behavioral)
Situation · Task · Action (you, specific) · Result (quantified + learning). Prep 5–6: hardest bug/RCA (P10), cross-team/ODM delivery, an incident prevented from recurring, mentoring, a review disagreement, a tradeoff under constraints. Avoid vague "we" and unquantified results.
8. The "done this before" tells
"Is that in the EBOM?" · "Is the spec testable — where's the conformance suite?" · "Is there an ADR for that?" · "Where's the runbook?" · "Did you write the decision down or was it just a call?" · "How are you growing the juniors?"
9. How this phase pays off later
It IS the Staff bar the interview probes (interview-prep/05). EBOM/specs gate Phases 01/03/08;
ADRs document the whole curriculum's decisions; runbooks come from Phase 10; the capstone (Phase 13)
ships with a design doc + runbook + retro produced exactly this way.
Software EBOM & Protocol Documentation
Deliverable 1 of Phase 12. A worked software EBOM and an ODM-facing interface spec — the contract that keeps a multi-party (you + Pegatron + ISVs) build coherent (WARMUP Ch. 2–3). Fill in the bracketed parts for your own node/rack; the result is both a real artifact and an interview portfolio piece.
Part A — Software/Firmware EBOM (example: one AI compute node)
The versioned, authoritative inventory reconciled against discovered reality at commissioning (Phase 01). A mismatch is a finding raised to the ODM, not a silent acceptance.
Firmware components & baseline (Phase 08)
| Component | Baseline version | Signed | A/B banks | Update path | Owner |
|---|---|---|---|---|---|
| BIOS/UEFI | 2.4.1 | yes (vendor key) | yes | Redfish UpdateService | ODM (Pegatron) |
| BMC (OpenBMC) | obmc-1.8.0 | yes | yes | Redfish + recovery | Us + ODM (upstream) |
| NIC/DPU | nic-fw-3.2.0 | yes | no | in-band agent | NIC vendor |
| Accelerator | accel-fw-2.1.0 | yes | yes | vendor tool / Redfish | Accelerator vendor |
| PSU | psu-1.0.3 | yes | no | BMC (PMBus) | ODM |
| PCIe switch | pex-4.1 | yes | no | BMC | Switch vendor |
| CPLD | cpld-0.7 | n/a | no | JTAG/BMC | ODM |
Software components (Phase 11 compatibility)
| Component | Version | Depends on | Compat window |
|---|---|---|---|
| rack-agent (DaemonSet) | 1.5.x | control-plane ≥ 1.4 | N-1 control-plane |
| metrics exporter | 1.3.x | Prometheus ≥ 2.40 | — |
| RackNode operator | 0.9.x | k8s ≥ 1.27 / RKE2 | — |
Interfaces & config baselines
- Management: Redfish (primary), IPMI (fallback), SNMP (PDU/switch) — see Part B for the spec.
- Secure boot (Phase 09): PK/KEK provisioned; db includes
[keys]; dbx revokes[list]. - Network (Phase 09): mgmt VLAN
10, tenant VLANs100–199; mgmt reachable control-plane-only. - Power/thermal budget (Phase 01): derated feed
17.3 kW; CDU130 kW; N+1.
Provenance / traceability
- Each row: source, license, support status, security-contact, last-validated date.
- Reconciliation rule: at commissioning, discovered firmware/software/topology MUST match this EBOM; deviations are logged as findings and triaged with the responsible owner.
Part B — Interface Specification (ODM-facing, example: node power & telemetry)
Precise + testable enough that the ODM builds the same thing without us in the room (WARMUP Ch. 3). Pair every requirement with a conformance check (Part C).
B.1 Standard & deltas
- Base: DMTF Redfish
2023.2. - Required resources:
ServiceRoot,Systems/{id}(withPowerState,ProcessorSummary,Actions/#ComputerSystem.Reset),Chassis/{id}withPowerandThermal,Managers/{id},UpdateService,EventService,TaskService. - OEM extensions:
Oem/Rackmgrblock onChassisMUST exposeRackPosition,PowerBudgetW.
B.2 Behavior (not just shape)
- Reset action: MUST advertise
ResetType@Redfish.AllowableValuesand support{On, ForceOff, GracefulShutdown, ForceRestart, GracefulRestart, PowerCycle}. AGracefulShutdownto a hung host MUST NOT report success if the host did not power off (Phase 04 escalation relies on this). - Firmware update:
UpdateService.SimpleUpdateMUST return202+ aTask;TaskStatetransitionsNew→Running→Completed|Exception; activation requirements stated per component (Phase 08). - Sensors:
Thermal.Temperatures[]MUST includeinletand per-acceleratoraccelNwithReadingCelsiusandUpperThresholdCritical; units Celsius; names as listed. - Errors: failures return a Redfish error body with
@Message.ExtendedInfo; a4xxfor client errors,5xxfor service errors — never200with an error body. - Auth: HTTPS only; Session tokens via
SessionService; certificate chains to the management CA. - Timing/resilience: the BMC SHOULD respond within
[N]s; clients will retry with backoff (Phase 02) — the BMC MUST be idempotent for repeatedGET/action submissions.
B.3 Events
EventServiceMUST support subscriptions (EventDestination) and SHOULD support SSE; PSU/thermal/ fault events MUST be emitted (so we don't rely on polling alone — Phase 07).
Part C — Conformance hooks (the spec's executable form)
Prose drifts; tests don't (WARMUP Ch. 3; Phase 11). Each requirement maps to a conformance check the ODM runs against their firmware in CI/HIL.
| Req (B.x) | Conformance check |
|---|---|
| B.1 resources | GET each required resource; assert presence + @odata.type |
| B.2 Reset values | assert AllowableValues ⊇ required set; illegal ResetType → 4xx |
| B.2 firmware Task | submit update → assert 202 + Task; poll to terminal state |
| B.2 sensors | assert inlet+accelN present with thresholds, correct units |
| B.2 errors | force an error; assert 4xx/5xx + extended info (never 200+error) |
| B.3 events | subscribe; trigger a fault; assert delivery |
Deliver this suite to the ODM; a failing check is an unambiguous, non-negotiable defect.
How to use this deliverable
- Replace the bracketed/example values with your node's real components and interface.
- Wire Part C into the Phase 11 conformance harness (run against the Redfish emulator + HIL).
- Version this document (changelog + compatibility notes — Phase 11 Ch. 5); assign an owner.
- Use it as the commissioning reconciliation source (Phase 01) and the ODM defect-feedback basis.
Design Reviews & ADRs
Deliverable 2 of Phase 12. A worked ADR, an ADR template, and a design-review feedback example on the JD's four axes (feasibility/performance/reliability/maintainability) — WARMUP Ch. 4.
Part A — Worked ADR (example)
ADR-007: Redfish-first device driver abstraction with IPMI/SNMP fallback
- Status: Accepted (2026-06-13)
- Context: Our fleet is heterogeneous — new accelerator nodes speak Redfish, the rack PDU speaks SNMP, and some peer BMCs are IPMI-only. We need one control plane that manages all of them, is testable without hardware, and lets a device class migrate protocols without rewrites. (Phase 03.)
- Decision: Define a single
DeviceDriverinterface (power_state,set_power,read_sensors, …) withRedfishDriver(preferred),IpmiDriver, andSnmpDriverbackends and a capability-detecting factory; normalize all telemetry into oneReadingmodel. All rack logic programs against the interface. - Alternatives considered:
- Redfish-only: simplest, but strands the IPMI/SNMP gear we must manage today.
- Per-protocol code paths in callers: no abstraction tax, but protocol quirks leak everywhere and every caller must change to migrate a device. Rejected (unmaintainable at fleet scale).
- A heavyweight third-party DCIM: faster initially, but loses control over the integration depth and the conformance/feedback loop with the ODM. Rejected for the core path.
- Consequences:
- (+) One model above; protocol quirks contained; hermetic tests via a
FakeDriver(Phase 02/11); a device can move IPMI→Redfish without touching callers. - (+) Reused by the exporter (Phase 07) and the capstone (Phase 13).
- (−) An abstraction layer to maintain; risk of a lowest-common-denominator API — mitigated by capability flags and OEM passthrough where needed.
- (−) Defensive coding required for vendor variance (Phase 03 Ch. 9); a conformance suite is therefore mandatory (Phase 11).
- (+) One model above; protocol quirks contained; hermetic tests via a
Part B — ADR template (copy per decision)
# ADR-NNN: <short decision title>
- Status: Proposed | Accepted | Superseded by ADR-MMM (date)
- Context: <the forces — technical, partner, timeline — that make this a real decision>
- Decision: <what we will do, specifically>
- Alternatives considered: <each option + why accepted/rejected>
- Consequences: <the good, the bad we accept, and what it enables/blocks downstream>
Write an ADR for any decision that is costly to reverse or that someone will later ask "why is it this way?" — e.g., "A/B firmware banks" (Phase 08), "operator-based lifecycle" (Phase 06), "management VLAN as a hard boundary" (Phase 09).
Part C — Design-review feedback (example, implementation-focused)
Review a change on the four axes; separate must-fix from opinion; critique the design, not the person; bring alternatives (WARMUP Ch. 4).
Change under review: a new BMC telemetry module that hardcodes Redfish paths and queries the BMC synchronously on every Prometheus scrape.
- Maintainability / correctness (must-fix): hardcoded
/redfish/v1/Systems/1/...breaks across vendors (IDs differ — Phase 03 Ch. 9). Follow@odata.idlinks instead. Alternative: use the existingRedfishDriver(ADR-007) which already does link-following. - Reliability (must-fix): synchronous BMC calls on the scrape path couple scrape latency to BMC health and hammer fragile BMCs; one slow BMC stalls all scrapes. Alternative: poll on the exporter's own schedule with caching + retries/timeout (Phase 02/07) and serve cached values.
- Performance (consider): add a
scrape_duration_secondshistogram so we can SLO it (Phase 07). - Security (must-fix): TLS verification is disabled (
ssl_insecure=true). Validate the cert against the management CA (Phase 03 Ch. 7). - Testability (must-fix): no seam — can't unit-test without a BMC. Inject the
DeviceDriverso aFakeDrivercovers failure paths (Phase 02/11). - Operability (nit): switch
printto structured logging with node/rack context (Phase 07). - Style (linter): defer to
ruff/black; not blocking.
Decision capture: agreed to cached polling + link-following + cert validation + the seam; opened ADR-012 ("no synchronous device I/O on the scrape path") and added both rules to the review checklist so the standard sticks (Phase 11 continuous improvement).
How to use this deliverable
- Write one real ADR for a curriculum decision using Part B.
- Do a Part-C-style review of one of your own labs' code on the four axes.
- Keep an
adr/directory in your projects; number ADRs; link them from runbooks and the EBOM.
Runbooks & Operational Procedures
Deliverable 3 of Phase 12. A worked runbook, a runbook template, and the manual→self- healing progression (WARMUP Ch. 5). A runbook must be executable by someone other than the author, at 3 a.m., safely.
Part A — Worked runbook (example)
Runbook: Node fails to POST after provisioning
- Trigger: provisioning state machine (Phase 05) reports a node stuck pre-OS, or alert
NodeNotReadyfires (Phase 07); the host is unreachable in-band. - Goal: get the node to POST and continue provisioning, or quarantine it with a clear FRU verdict.
- Owner / escalation: Rack SW on-call → HW bring-up → ODM (Pegatron) contact
[link]. - Risk / blast radius: single node; power actions are physical — use dry-run/confirm (Phase 04).
Steps (OOB — the host is down, so everything is via the BMC):
- Confirm scope (Phase 01): are peers on the same PDU/ToR/CDU also down? If many → escalate as a
shared-resource incident; this runbook is for a single node.
rackctl status --rack <rack>(Phase 02 CLI). - Reach the BMC (Phase 03):
redfishtool -r <bmc> Systems get→ confirmPowerState. If the BMC itself is unreachable → mgmt-network/BMC issue → escalate (different runbook). - Read why it didn't POST: SEL + BERT for last-boot error (Phase 08); watch live via SoL:
ipmitool -I lanplus -H <bmc> ... sol activate. - Check the obvious physical signals (Phase 04): inlet/accel temps, PSU health, PCIe — a thermal trip or a PSU fault blocks POST. CDU ΔT/leak? (If leak → STOP, safety runbook.)
- Reset once, watched:
redfishtool ... ComputerSystem.Reset ForceRestartand watch SoL for the POST code / error. (Dry-run/confirm guard — Phase 04.) - Decide (Phase 08 decision tree):
- POSTs now → resume provisioning (Phase 05); record the transient in the node history.
- RAS points at a FRU (DIMM/accelerator/PSU) → cordon (Phase 06), schedule RMA (Phase 08), re-validate after replacement (Phase 01).
- Firmware mismatch vs EBOM (Phase 12) → re-flash to baseline (Phase 08 orchestrator), retry.
- Quarantine if unresolved: mark the node
QUARANTINED(Phase 05 gate), file a ticket with the SEL/BERT/RAS evidence, and do not hand it to tenants. - Close: update the node history; if this was a new failure mode, write/extend the RCA (Phase 10) and add an alert/test (Phase 11) so it's caught earlier next time.
Rollback / safety: every power action is reversible (power the node back on); never proceed past a CDU leak signal; if uncertain, quarantine rather than risk tenant exposure.
Part B — Runbook template (copy per procedure)
# Runbook: <task or incident>
- Trigger: <the exact alert/symptom that brings someone here> (Phase 07)
- Goal: <the outcome that means "done">
- Owner / escalation: <on-call → next tier → partner contact + link>
- Risk / blast radius: <scope; destructive steps; pre-checks> (Phase 01/04)
Steps:
1. <pre-check / confirm scope>
2. <command> → <expected output> ; if <X> then <Y>
...
Rollback / safety: <how to undo; when to STOP and escalate>
Links: dashboard <...>, ADR <...>, related runbooks <...>
Post: <update history; if new, write RCA (Phase 10) + add a test/alert (Phase 11)>
Part C — The maturity progression (Staff drives this)
manual heroics → RUNBOOK (repeatable by anyone) → AUTOMATED (a script/tool runs it)
→ SELF-HEALING (the operator reconciles it, Phase 06)
- Every RCA (Phase 10) with a manual fix → leave a runbook.
- Every repeated runbook → automate it (a
rackctlsubcommand, Phase 02). - Every automatable recovery → fold into the operator's reconcile loop (Phase 06) so it self-heals.
This progression is how a distributed, follow-the-sun on-call scales without burning people out — and it's a concrete way a Staff engineer multiplies the team (WARMUP Ch. 7).
How to use this deliverable
- Write one real runbook (the "node won't POST" above, or "fleet firmware rollout" — Phase 08/11) using Part B.
- Link it from the relevant Phase 07 alert's
runbookannotation. - Identify one runbook to automate and one to fold into the operator (Part C).
Staff Mentorship & Distributed-Team Communication
Deliverable 4 of Phase 12. A mentorship/onboarding plan, an async-communication norm for a multi-site team, and a STAR story bank for behavioral interviews (WARMUP Ch. 6–8).
Part A — Onboarding & mentorship plan (template, worked)
Goal: a new engineer is productively contributing to rack-management software within ~30 days and independent within ~90 — and the team is stronger, not bottlenecked on you.
30 / 60 / 90:
- Day 0–30 — orient + first ship: run the curriculum's Phase 01–03 labs (rack model, the CLI, a Redfish client) on a lab/emulator; pair on a small, real, scoped change behind a flag; first PR reviewed as teaching (explain the why, not just the what). Read the EBOM + key ADRs (Phase 12).
- Day 30–60 — own a component: take a bounded piece (an exporter metric, a runbook, a driver method) end-to-end incl. tests (the seam — Phase 11) and a runbook; shadow an on-call shift.
- Day 60–90 — independence + breadth: own a feature across the seams (Phase 03/04), do a design review, write an ADR, take a (supervised) on-call shift, and lead one incident's RCA (Phase 10).
Mentorship mechanics:
- Pairing on the hard/ambiguous parts; review-as-teaching (link the principle, the ADR, the standard — don't just say "change this").
- Stretch with a safety net: assign slightly-beyond-current tasks with clear support and a blameless retro if it wobbles.
- Feedback: specific, timely, kind; separate "must-fix" from "growth area"; celebrate the wins.
- Make yourself unnecessary: success is the mentee not needing you, and the team's knowledge rising (runbooks, ADRs, docs) — not you being the single point of knowledge.
Part B — Async-first communication norms (multi-site)
For a globally distributed team (the JD), writing is the primary interface (WARMUP Ch. 6):
- Decisions live in durable docs (ADRs/specs/runbooks), not chat that scrolls away. If a decision happened on a call, someone writes it down.
- Lead with the decision/ask, then context; make each doc/message self-contained (understandable without the meeting that isn't happening across timezones).
- Decision + consequence + honest limit in every proposal — what, why, and what you're unsure of.
- Right medium: durable → doc; quick sync → short call (rotate the painful hour fairly across sites); status → a written update; urgent → page (Phase 07), not a buried message.
- Minimize required-synchronous; default to async with clear deadlines; assume good intent across cultures/timezones; over-communicate context that's "obvious" locally but not globally.
- Bias to writing things once, well — a clear interface spec or runbook saves dozens of cross-timezone clarification round-trips.
Part C — STAR story bank (build your 5–6 from the curriculum)
Structure each: Situation · Task · Action (you, specific) · Result (quantified + learning) (WARMUP Ch. 8). Drafts to flesh out from your lab work:
- Hardest bug (Phase 10): a latent use-after-free found via ASan/gdb under load; root-caused, fixed, added ASan to CI + a regression test → the class of bug can't recur. (debugging — the JD's #1)
- Production incident, prevented recurrence (Phase 08/10): nodes rebooting under thermal load → RAS pointed at a degrading DIMM (rising correctable ECC) → predictive RMA + a new alert; turned an outage class into planned maintenance.
- Cross-team/ODM delivery (Phase 03/12): an interface ambiguity with the ODM caused integration slip → wrote a testable spec + conformance suite → integration time dropped and defects became unambiguous.
- Mentoring (Phase 12): brought a junior from first-PR to owning the exporter via pairing and review-as-teaching; they now mentor the next hire.
- Design-review disagreement (Phase 12): pushed back on synchronous scrape-time BMC calls with a prototype + the failure mode → adopted cached polling; codified the standard.
- Tradeoff under constraints (Phase 01): a rack 10% over power budget → power-capping to fit vs. refusing the deployment → capped to fit safely with the perf cost made explicit to stakeholders.
For each, write the quantified result and the one thing you learned — that's what distinguishes a Staff answer.
How to use this deliverable
- Adapt Part A to a real (or imagined) report and Part B to your team's reality.
- Write out all 6 STAR stories in full from your lab work — they're your interview portfolio.
- Practice telling each in ~2 minutes: decision + consequence + honest limit, you the clear actor.
Software EBOM + Interface Spec — AI-Tray-2U Rev B
A complete, filled portfolio artifact (no placeholders) — the ODM-facing contract for a concrete node, as you'd hand to Pegatron. Derived from the template in
../EBOM-AND-PROTOCOL-DOCS.md. Status: Approved, owner: Rack SW Platform, last validated: 2026-06-13, applies to rack classrack-ai-01.
1. Node summary
- Model:
AI-Tray-2URev B · 2U · 1× host CPU socket · 4× AI accelerators · 1× 400G NIC · 1× BMC. - Power: typical 1.48 kW, nameplate 2.0 kW (cap), liquid-cooled (cold plates on accelerators + CPU).
- Mgmt: BMC on mgmt VLAN 10; data NIC on tenant VLANs 100–199.
2. Firmware baseline (the reconciliation target — Phase 01/08)
| Component | Baseline | Min (anti-rollback) | Signed by | A/B | Update path | Owner |
|---|---|---|---|---|---|---|
| BIOS/UEFI | 2.4.1 | 2.4.0 | ODM key PGT-BIOS-2026 | yes | Redfish UpdateService (Task) | Pegatron |
| BMC (OpenBMC) | obmc-1.8.0 | obmc-1.7.2 | Platform key | yes | Redfish + JTAG recovery | Us + Pegatron (upstream) |
| Accelerator FW | accel-fw-2.1.0 | accel-fw-2.0.5 | Accel vendor | yes | vendor tool / Redfish | Accel vendor |
| 400G NIC FW | nic-fw-3.2.0 | nic-fw-3.1.0 | NIC vendor | no | in-band agent | NIC vendor |
| PSU FW | psu-1.0.3 | psu-1.0.3 | ODM | no | BMC (PMBus) | Pegatron |
| PCIe switch | pex-4.1 | pex-4.0 | Switch vendor | no | BMC | Microchip |
| CPLD | cpld-0.7 | cpld-0.7 | ODM | no | BMC/JTAG | Pegatron |
3. Software components (Phase 11 compatibility)
| Component | Version | Compat window |
|---|---|---|
| rack-agent (DaemonSet) | 1.5.3 | control-plane ≥ 1.4, ≤ 1.6 |
| metrics exporter | 1.3.1 | Prometheus ≥ 2.40 |
| RackNode operator | 0.9.0 | k8s/RKE2 ≥ 1.27 |
4. Config & security baselines (Phase 09)
- Secure Boot: enabled; PK =
RACKMGR-PK-2026; KEK ={RACKMGR-KEK, MS-UEFI-KEK}; db includes the signed shim/GRUB/kernel; dbx revokesgrub-2.06-cve-2023-pre. - Attestation: required before tenant scheduling; golden PCR set published per (BIOS, BMC, OS) triple in the attestation service; AK enrolled at commissioning.
- Network: mgmt VLAN 10 (control-plane only — hard boundary); tenant VLANs 100–199; QoS HTB with a reserved 10 Gbit management class.
- Power/thermal: derated feed 17.3 kW; rack CDU 130 kW; N+1 power, N+1 pumps.
5. Interface spec (Redfish 2023.2) — the build-to-it contract
- Required resources:
ServiceRoot;Systems/{id}(PowerState,ProcessorSummary,Actions/#ComputerSystem.Reset);Chassis/{id}(Power,Thermal);Managers/{id};UpdateService;EventService;TaskService.Oem/Rackmgron Chassis MUST exposeRackPosition(U),PowerBudgetW. - Sensors (Thermal.Temperatures[]): MUST include
Inlet,CPU1 Temp,Accel0..3 Temp, each withReadingCelsius(units Celsius) andUpperThresholdCritical. A sensor with no reading MUST returnReadingCelsius: null(not omit the entry, not0). - Reset: MUST advertise
ResetType@Redfish.AllowableValues ⊇ {On, ForceOff, GracefulShutdown, ForceRestart, GracefulRestart, PowerCycle}; aGracefulShutdownto a non-responsive host MUST NOT report success. - Firmware:
UpdateService.SimpleUpdateMUST return202+ aTask;TaskStateNew→Running→Completed|Exception; BMC update MUST be A/B with a recovery path. - Auth/transport: HTTPS only; cert chains to
RACKMGR-MGMT-CA; Session tokens; least-privilege roles (ReadOnlyfor telemetry). - Errors:
4xx/5xx+@Message.ExtendedInfo; never200with an error body. - Events: EventService subscriptions + SSE; PSU, thermal, and PCIe-fatal events MUST be emitted.
6. Conformance checks (executable spec — Phase 11)
| Req | Check | Status (2026-06-13) |
|---|---|---|
| §5 resources | GET each; assert presence + @odata.type | PASS |
| §5 sensors | assert Inlet/CPU/Accel0..3 present, units, null for no-reading | PASS |
| §5 Reset | AllowableValues ⊇ set; illegal type → 4xx | PASS |
| §5 firmware Task | submit → 202 + Task; poll to terminal | PASS |
| §5 errors | force error → 4xx/5xx + extended info | FAIL → finding RM-2026-014 |
| §5 events | subscribe + trigger fault → delivery | PASS |
7. Open findings (raised to Pegatron)
- RM-2026-014 (sev: medium): BMC
obmc-1.8.0returns200with an error body on an invalid PATCH instead of4xx(violates §5). Owner: Pegatron FW. Target:obmc-1.8.1. Workaround: client inspects body for@Message.ExtendedInfo(defensive — Phase 03 Ch. 9). - RM-2026-019 (sev: low):
CPU2 Temppresent but unpopulated on single-socket SKUs; agreed to returnnull(done) rather than omit.
8. Change log
- Rev B (2026-06-13): added attestation requirement; bumped accel-fw baseline 2.0.5→2.1.0; added conformance finding RM-2026-014. Rev A (2026-05-20): initial.
ADR Log — Rack Management Platform
A complete, filled decision log (no placeholders) — the durable record of the platform's significant decisions, as you'd keep in
adr/and link from the design doc. Format per../DESIGN-REVIEWS-ADRS.md.
ADR-001: One inventory model + one reconcile loop as the spine
- Status: Accepted (2026-05-21)
- Context: We must provision, monitor, firmware-update, and decommission thousands of nodes across sites. The naive path is per-feature scripts.
- Decision: A single inventory graph (components, relationships, state, declared baseline) plus a per-node reconcile loop that drives current→desired; provisioning and firmware are actions the loop takes, not separate orchestrators.
- Alternatives: per-feature imperative scripts (rejected: no self-healing, no drift correction, unmaintainable at scale); a heavyweight COTS DCIM (rejected for the core path: loses integration depth + the ODM feedback loop).
- Consequences: (+) idempotent, resumable, self-healing; one contract every part shares. (−) more upfront design than scripts; the loop must be carefully idempotent.
ADR-002: Redfish-first device-driver abstraction with IPMI/SNMP fallback
- Status: Accepted (2026-05-23)
- Context: Heterogeneous fleet — Redfish nodes, SNMP PDUs, IPMI-only peer BMCs.
- Decision: One
DeviceDriverinterface (power/sensors/firmware) with Redfish/IPMI/SNMP backends- a capability-detecting factory; a unified
Readingmodel; aFakeDriverfor tests.
- a capability-detecting factory; a unified
- Alternatives: Redfish-only (rejected: strands today's IPMI/SNMP gear); per-protocol code in callers (rejected: quirks leak, every caller changes to migrate a device).
- Consequences: (+) quirks contained, hermetic tests, protocol migration without caller changes, reused by the exporter + capstone. (−) abstraction tax; risk of a lowest-common-denominator API (mitigated by capability flags + OEM passthrough); requires a conformance suite (ADR-006).
ADR-003: A/B firmware banks + verify-before-and-after + anti-rollback
- Status: Accepted (2026-05-28)
- Context: A botched flash can brick a remote node; the BMC is the riskiest target (brick it → lose OOB).
- Decision: Update the inactive A/B bank, verify signature before and health after activation, commit or roll back; enforce an anti-rollback floor.
- Alternatives: in-place single-bank flash (rejected: a bad flash destroys the only firmware); no anti-rollback (rejected: downgrade-to-vulnerable attack).
- Consequences: (+) a bad flash never destroys working firmware; recovery is a pointer switch. (−) needs hardware A/B support; where absent, extra caution + a recovery path are required.
ADR-004: Management VLAN is a hard, non-negotiable boundary
- Status: Accepted (2026-05-30)
- Context: BMC/PDU/CDU have total power/firmware control; an exposed BMC is a full compromise.
- Decision: The management plane lives on an isolated VLAN reachable only by the control plane; tenant→management is denied in code and overrides any ACL.
- Alternatives: shared management/data network (rejected: catastrophic blast radius + Shodan-style exposure).
- Consequences: (+) contains the worst-case; (−) requires disciplined network config + smart proxies near hardware. No tradeoff worth taking on the boundary itself.
ADR-005: Attestation-gated trust before tenant scheduling
- Status: Accepted (2026-06-02)
- Context: Multi-tenant + sovereign sites; can't assume a node is trustworthy.
- Decision: A node earns tenant workloads/keys only after a valid TPM PCR quote against published golden values (measured boot + remote attestation); secure boot enforces signed code.
- Alternatives: trust-on-provision (rejected: a compromised firmware could exfiltrate tenant data); secure boot only (rejected: doesn't prove state to the control plane).
- Consequences: (+) provable, gateable trust; sealing secrets to PCR state. (−) operational burden: golden measurements per (BIOS,BMC,OS) triple that change with firmware (ties to ADR-003).
ADR-006: Conformance suite as the executable interface spec
- Status: Accepted (2026-06-05)
- Context: "Redfish-compliant" ODM hardware varies; prose specs drift.
- Decision: Every interface requirement maps to an automated conformance check run against the ODM's emulator/HIL in CI; a failing check is the unambiguous defect (e.g., finding RM-2026-014).
- Alternatives: prose-only spec (rejected: ambiguous, drifts); manual integration testing (rejected: not repeatable).
- Consequences: (+) unambiguous ODM contract, regressions caught pre-deploy. (−) the suite is a product to maintain across vendor/firmware combos.
ADR-007: Every fleet change is a gated, canaried, reversible, domain-aware rollout
- Status: Accepted (2026-06-08)
- Context: Agent, config, firmware, and k8s changes all risk fleet-wide incidents.
- Decision: One rollout discipline for all of them: canary (1→1%) → health/SLO-gated waves → 100%, auto image-pinned rollback, failure-domain-aware waves, drain-first for reboots.
- Alternatives: push-to-all (rejected: a bad change is an outage); ad-hoc per-change processes (rejected: inconsistent, error-prone).
- Consequences: (+) bounded blast radius; reuse across firmware (ADR-003) + k8s + agents. (−) rollouts are slower by design — the deliberate cost of "no fleet-wide incidents."
Superseded / revisited
- (none yet) — supersessions are recorded here with
Superseded by ADR-NNNand a date.
Runbooks — Rack Management Platform
Complete, filled on-call runbooks (no placeholders), executable by anyone at 3 a.m. Format per
../RUNBOOKS-AND-OPS.md. Each is linked from its Phase 07 alert'srunbookannotation.
Runbook A: Fleet firmware rollout (BMC obmc-1.7.2 → 1.8.0)
- Trigger: a planned rollout ticket, or
firmware_version{component="bmc"}drift alert. - Goal: every node at the EBOM BMC baseline
obmc-1.8.0, zero fleet-wide bricks. - Owner / escalation: Rack SW on-call → Platform lead → Pegatron FW contact (
#pegatron-fw). - Risk: BMC is the riskiest target (brick → lose OOB); A/B + recovery path required (ADR-003).
Procedure
- Pre-checks: confirm artifact signed by
Platform keyand version ≥ anti-rollback floor (obmc-1.7.2); confirm A/B supported on target SKUs; confirm site artifact mirror is populated (air-gap).rackctl fw verify --image obmc-1.8.0.bin. - Canary (1 node): pick a node in a non-critical failure domain, drain it
(
kubectl drain <node> --ignore-daemonsets), update via the orchestrator (rackctl fw update --node <n> --image obmc-1.8.0), watch the Task to Completed, verify post-activation health (sensors nominal, PCIe at full width, attestation re-passes), uncordon. Bake 30 min. - Gate: check the canary's health/SLO panel — zero new crashes/AER, BMC reachable, attest OK. If regressed → STOP, rollback (step R), open an incident.
- Progressive waves: 1% → 10% → 50% → 100%, each wave failure-domain-aware (never a whole PDU/ToR at once), drain-first, gated as in step 3. Auto-halt + rollback on any wave regression.
- Verify fleet:
firmware_version{component="bmc"}shows 100% at1.8.0; no version-skew alerts.
(R) Rollback: rackctl fw rollback --node <n> switches the BMC to the previous A/B bank (the
prior 1.7.x) and reboots; confirm version + health. Halt the rollout; RCA (Phase 10).
Post: update node history; if a new failure mode appeared, write the RCA + add a pre-promotion check (Phase 11) + feed the defect to Pegatron (EBOM finding).
Runbook B: Node fails to POST after provisioning
- Trigger: provisioning stuck pre-OS, or
NodeNotReadywith the host unreachable in-band. - Goal: node POSTs and resumes provisioning, or is quarantined with a FRU verdict.
- Owner / escalation: Rack SW on-call → HW bring-up → Pegatron.
- Risk: single node; power actions are physical (dry-run/confirm).
Procedure
- Scope (Phase 01):
rackctl status --rack <rack>— are peers on the same PDU/ToR/CDU also down? If many → shared-resource incident (different runbook); this is for a single node. - Reach the BMC:
redfishtool -r <bmc> Systems get→ confirmPowerState. BMC unreachable → mgmt-network/BMC issue → escalate. - Why it didn't POST: read SEL + BERT for the last-boot error
(
ipmitool -I lanplus -H <bmc> -U <u> -P <p> sel list); watch live:ipmitool ... sol activate. - Physical signals (Phase 04): inlet/accel temps, PSU health, PCIe link width; CDU ΔT/leak — if leak, STOP → CDU-leak safety runbook.
- Reset once, watched:
redfishtool -r <bmc> Systems reset ForceRestart; watch SoL for the POST code. - Decide (Phase 08 tree):
- POSTs → resume provisioning; record the transient.
- RAS → a FRU (DIMM/accel/PSU) → cordon, schedule RMA, re-validate after swap.
- Firmware ≠ EBOM → re-flash to baseline (Runbook A path), retry.
- Quarantine if unresolved: mark
QUARANTINED(Phase 05 gate), file a ticket with SEL/BERT/RAS evidence; do not hand to tenants.
Post: update history; new failure mode → RCA (Phase 10) + an earlier-catching alert/test.
Runbook C: Coolant leak detected (safety-critical)
- Trigger: CDU
leak=1event (Phase 04/07) — highest priority. - Goal: protect hardware and people; minimize damage.
- Owner / escalation: Rack SW on-call → Facilities/DC ops immediately (parallel, not serial).
- Risk: liquid near live electronics — hardware loss + hazard.
Procedure (fail-safe, act first)
- Confirm + page facilities in parallel; do not wait for confirmation to start step 2.
- Reduce heat load: cap power on the affected rack's nodes (
rackctl power cap --rack <r> --watts <low>) and/or drain + graceful-shutdown non-critical nodes (Phase 06) to cut the thermal source. - Per policy/interlock: trigger the rack's emergency coolant action (drain/valve) if the platform owns it; otherwise hand to facilities.
- Do NOT power-on anything in the affected rack until facilities clears it (interlock).
- Capture evidence: CDU telemetry (flow/pressure/ΔT trend), timestamps, affected components.
Post: incident + blameless RCA; verify the leak interlock and the alert path worked; restore service only after facilities sign-off + re-validation (Phase 01 commissioning).
Mentorship Plan + STAR Story Portfolio
Complete, filled portfolio (no placeholders): a concrete 30/60/90 onboarding plan and six fully-written STAR stories drawn from this curriculum's work — ready to tell in a behavioral round. Template:
../STAFF-MENTORSHIP.md.
Onboarding plan — new rack-management engineer (concrete)
Mentee: a strong generalist SWE, new to BMC/Redfish/firmware. Goal: productive in 30 days, independent in 90, and the team stronger (not bottlenecked on me).
- Week 1: pair on the rack model +
rackctlCLI (Phases 01–02) on the lab/emulator; read theAI-Tray-2UEBOM and ADR-001/002. First PR: add arackctl inventory --output jsonflag — reviewed as teaching (link the ADRs, not just "change this"). - Weeks 2–4: own the Redfish→Prometheus exporter's CDU metrics (Phase 07) end-to-end incl. tests against the Redfish emulator (the seam) and a Grafana panel; shadow one on-call shift; ship behind a flag.
- Weeks 5–8: own a
DeviceDrivermethod across the seam (Phase 03), add the conformance check for it (ADR-006), and write Runbook B updates from a real incident. Do a design review. - Weeks 9–12: own a feature spanning firmware + telemetry (Phase 08/07), take a supervised on-call shift, lead one blameless RCA (Phase 10), and write an ADR.
- Mechanics: pairing on the ambiguous parts; review-as-teaching; stretch-with-safety-net; specific, kind, timely feedback; success = the mentee not needing me and team docs/runbooks growing.
STAR stories (six, fully written)
S1 — Hardest bug root-caused (debugging — the JD's #1 theme)
- Situation: a close-to-hardware C component (a sensor-history list) crashed intermittently under load in integration; normal builds "worked," so it had slipped past tests.
- Task: I owned the component and the incident.
- Action: resisted guessing — built it under AddressSanitizer, which pinpointed a
heap-use-after-free at the list traversal with both the allocation and use stack traces; confirmed
in
gdb. Root cause: aremovethatfree()d a node without unlinking it (dangling->next). Fixed by unlinking-before-free with a sentinel; added-fsanitize=address,undefinedto CI and a regression test covering middle/head/absent removal. - Result: the crash class is gone; the team adopted sanitizers in CI, catching two more latent bugs within a month. Learning: latent memory bugs need tooling, not luck — and a fix isn't done until it's a CI gate.
S2 — Production incident, prevented recurrence
- Situation: a set of nodes rebooted intermittently under heavy load; on-call had been "RMA the node" with no clear cause.
- Task: get a real root cause, remotely (couldn't take tenant-serving nodes offline).
- Action: evidence-first via OOB — read SEL + BERT for the last-crash cause and ran the RAS parser on AER/MCE; correlated reboots with telemetry and saw correctable-ECC on one DIMM rising with temperature. Hypothesis: a marginal DIMM failing under thermal load. Reproduced on a canary under load; cordoned/drained, RMA'd the DIMM (not the node), re-validated; added an alert on correctable-ECC rate.
- Result: turned an unplanned-outage class into planned, single-FRU maintenance; mean repair scope dropped from "whole node" to "one DIMM." Learning: rate-not-count, and remote diagnosis beats a truck roll.
S3 — Cross-team / ODM delivery under ambiguity
- Situation: integration with the ODM (Pegatron) kept slipping — their BMC returned a
200with an error body on bad input, and sensor naming differed from our assumptions. - Task: make the interface unambiguous and unblock integration.
- Action: wrote a precise, testable interface spec (the
AI-Tray-2Uspec) and a conformance suite (ADR-006) the ODM ran in CI; filed the200-on-error as a tracked finding (RM-2026-014) with a defensive client workaround; agreednullfor unpopulated sensors. - Result: integration time dropped materially and defects became "a failing check," not a debate; the suite now gates every firmware drop. Learning: in a multi-party build, the spec + conformance suite is the engineering.
S4 — Mentoring
- Situation: a new hire was strong in app code but new to hardware management and hesitant to touch the firmware path.
- Task: grow them to independent ownership without becoming their bottleneck.
- Action: a 30/60/90 plan (above); paired on the first firmware-orchestrator change; reviewed as teaching (linked ADR-003 on A/B/rollback); handed them the exporter, then the conformance work, with a safety net.
- Result: independent in ~10 weeks; they now own telemetry and mentor the next hire. Learning: success is the mentee not needing me and the team's knowledge (runbooks/ADRs) rising.
S5 — Design-review disagreement handled well
- Situation: a teammate's telemetry module hardcoded Redfish paths and queried the BMC synchronously on every Prometheus scrape.
- Task: I owned review for the telemetry area.
- Action: gave implementation-focused feedback on the four axes — maintainability (hardcoded paths break across vendors → follow links, ADR-002), reliability/performance (sync scrape-time BMC calls couple scrape latency to BMC health and hammer fragile BMCs → poll-on-own-schedule + cache), security (TLS verification was off → validate the cert). Brought a small prototype of the cached approach; separated must-fix from opinion.
- Result: adopted cached polling + link-following + cert validation; codified two standards in the review checklist (ADR + "no synchronous device I/O on scrape"). Learning: critique the design not the person; bring alternatives and data.
S6 — Tradeoff under constraints
- Situation: a rack arrived 10% over its derated power budget; the easy path was to silently run it at N (no redundancy).
- Task: deploy safely without hiding the tradeoff.
- Action: used the budget calculator to size it; chose per-node power capping to fit within the N+1 budget rather than dropping redundancy, and made the (small) performance cost explicit to stakeholders in writing.
- Result: deployed within a safe, redundant power envelope; the capping policy became the default for density-constrained racks. Learning: surface tradeoffs explicitly — don't bury them in a config or quietly sacrifice redundancy.
Closing questions to ask the interviewer
- How is work split with the architects/lead engineers across sites?
- What does the day-to-day ODM (Pegatron) collaboration look like?
- How mature is the hardware-adjacent test/CI infrastructure (emulators/HIL)?
- What does follow-the-sun on-call look like, and how complete are the runbooks?
- What would "above and beyond" look like in the first 6–12 months?
Phase 13 — Capstone: A Rack Management Platform
Difficulty: ⭐⭐⭐⭐⭐ | Estimated Time: 2 weeks Roles supported: Senior/Staff Rack Management SWE Hardware needed: none — the mini-platform composes the prior phases' simulators/emulators
Why This Phase Exists
Every prior phase built one capability; the capstone proves you can compose them into a coherent system — the actual job. You will build a mini rack-management platform that integrates: inventory (Phase 01), the Redfish/IPMI/SNMP driver abstraction (Phase 03), the provisioning state machine (Phase 05), the reconcile loop (Phase 06), telemetry/exporter (Phase 07), firmware orchestration (Phase 08), and secure multi-tenant configuration (Phase 09) — operated through a CLI (Phase 02), tested (Phase 11), and shipped with the Staff artifacts (Phase 12): a design doc, a runbook, and a retrospective.
This is what you show in an interview ("here's a rack-management platform I built end to end") and what proves you can do — and exceed — the JD on day one.
What You Build
A mini rack manager that takes a rack from discovered → ready → operated → maintained, as one program with clear seams:
+------------------ Control plane ------------------+
rackctl ---> | Inventory (P01) + reconcile loop (P06) |
(P02 CLI) | Provisioning state machine (P05) |
| Firmware orchestrator (P08) |
| Telemetry/exporter (P07) + alerts |
| Tenancy/secure config (P09) |
+---------------------------------------------------+
| DeviceDriver abstraction (P03)
+-------+--------+--------+
| Redfish | IPMI | SNMP | (emulators/sims from P03/P04)
+----------------+--------+
Deliverables:
- The integrated platform (
capstone-mini-rack-manager/) — runnable, tested, composing the prior labs' patterns. - A design doc — architecture, the seams, the key ADRs (Phase 12), the data model, failure handling, and what changes at fleet scale.
- A runbook (Phase 12) for a real operation (commission a rack / firmware rollout / node RMA).
- A release cycle — ship it through the Phase 11 pipeline (tests + canary) and write a retrospective (what worked, what you'd change, the RCA-style learnings).
The Build (suggested sequence)
- Model + reconcile: the inventory graph (Phase 01) + a reconcile loop (Phase 06) that drives
each node toward
READYat the firmware baseline. - Drivers: the
DeviceDriverabstraction (Phase 03) with a fake/emulated backend so it runs without hardware; one model above. - Provision: the resumable state machine (Phase 05) with the validation gate (EBOM reconciliation — Phase 12) and attestation (Phase 09).
- Operate: the exporter (Phase 07) exposing fleet metrics + alerts; firmware orchestration (Phase 08) with A/B + rollback; tenancy/secure config (Phase 09).
- Interface: drive it all through the
rackctlCLI (Phase 02), structured-logged and tested (Phase 11). - Ship: design doc + runbook + canary release + retrospective (Phases 11–12).
Deliverables Checklist
- The platform runs end to end: discover → provision → ready → operate → maintain, no hardware
-
One inventory model and one
DeviceDriverabstraction underpin everything (no protocol leaks) - The reconcile loop converges nodes to baseline and corrects drift (idempotent, resumable)
- Provisioning has a validation gate that quarantines bad nodes (EBOM + attestation)
- Telemetry is exposed (Prometheus format) with alerts; firmware updates are A/B + rollback
- Multi-tenant isolation invariants hold (no cross-tenant, no tenant→management)
- It's operated via the CLI, structured-logged, and has a test suite (pyramid)
- A design doc, a runbook, and a retrospective accompany it (Staff artifacts)
Interview Relevance
- "Walk me through a rack-management system you've built." (this — end to end)
- "Where are the seams, and why?" (driver abstraction, the testability seam)
- "How does it handle a node that fails provisioning / a flaky BMC / a bad firmware?"
- "What changes when this manages 10,000 nodes instead of one rack?" (→ system-design/)
- "Show me the design doc and an ADR." (Phase 12)
- This capstone is the portfolio that backs every other answer in the loop.
Warmup Guide — Capstone: Composing a Rack Management Platform
The capstone is about composition: the prior phases each gave you a part; here you assemble them into one coherent, testable, operable system — and learn what changes when one rack becomes ten thousand. This guide frames the architecture, the seams, and the integration pitfalls.
Table of Contents
- Chapter 1: From Parts to a Platform
- Chapter 2: The Architecture and Its Seams
- Chapter 3: The Lifecycle, End to End
- Chapter 4: Failure Handling Across the System
- Chapter 5: What Changes at Fleet Scale
- Chapter 6: Shipping It — Design Doc, Runbook, Retro
- Capstone Walkthrough Guidance
- Success Criteria
- Interview Q&A
- References
Chapter 1: From Parts to a Platform
You've built: an inventory model (Phase 01), production code patterns (Phase 02), protocol clients behind a driver abstraction (Phase 03), hardware interfacing (Phase 04), a provisioning state machine (Phase 05), a reconcile loop (Phase 06), a telemetry exporter (Phase 07), firmware orchestration (Phase 08), tenancy/secure config (Phase 09), reliability patterns (Phase 10), and a test/release pipeline (Phase 11). The capstone is the integration test of you: can you compose these into a system where the parts reinforce rather than fight each other?
The recurring idea that makes composition work — and that this whole curriculum has been teaching — is declared vs discovered, converged through one control loop, over one data model, behind stable seams. Get those three right (one model, one reconcile loop, clean seams) and the parts snap together; get them wrong and you have a pile of scripts.
Chapter 2: The Architecture and Its Seams
The platform is a control plane over a driver abstraction:
- One data model (Phase 01): every component, relationship, state, and the declared baseline (EBOM) live in one inventory graph. Everything reads/writes it; it's the spine.
- One control loop (Phase 06): a reconcile loop drives each node from its current state toward desired (provisioned, at firmware baseline, healthy, correctly configured), idempotently and resumably. Provisioning (Phase 05) and firmware (Phase 08) are actions the loop takes, not separate orchestrators.
- One device abstraction (Phase 03):
DeviceDriver(Redfish-first, IPMI/SNMP fallback) hides protocol/vendor variance; aFakeDrivermakes the whole platform testable without hardware (Phase 02/11). This is the most important seam. - Cross-cutting: telemetry (Phase 07) observes the model and the drivers; tenancy/security (Phase 09) constrains configuration; the CLI (Phase 02) is the operator interface; reliability patterns (Phase 10) wrap every external call.
The seams (interfaces) are where you spend design care: the driver boundary (testability + portability), the model (the contract every part shares), and the reconcile API (declared state in, convergence out). Good seams are why you can swap a real BMC for a fake, add a vendor, or test a failure path without rewiring the system.
Chapter 3: The Lifecycle, End to End
The platform expresses the rack lifecycle (Phase 01 Ch. 9) as states the reconcile loop drives:
DISCOVERED → INVENTORIED(+EBOM reconcile, P12) → FIRMWARE_BASELINED(P08) → ATTESTED(P09)
→ OS_INSTALLED(P05) → CONFIGURED(tenancy/secure, P09) → VALIDATED(gate) → READY
→ (operate: telemetry P07, drift-correct P06) → (maintain: firmware/RMA P08/P10) → DECOMMISSION
Key properties carried through from earlier phases: idempotent + resumable (a crash mid-flow resumes), validation-gated (bad nodes quarantined, not shipped), observable (every transition emits telemetry), and reconciled against intent (discovered vs the EBOM). The capstone makes these concrete in one program.
Chapter 4: Failure Handling Across the System
A platform is judged by how it fails, not how it runs on the happy path. The integration must handle (drawing on Phase 10):
- A flaky BMC: the driver retries with backoff + a circuit breaker; the reconcile loop requeues; the node doesn't get stuck or hammer the BMC.
- A node that fails provisioning/validation: quarantined with a reason (EBOM mismatch, failed attestation, RAS fault), not handed to tenants; an alert + a runbook (Phase 12).
- A bad firmware: A/B rollback (Phase 08); fleet rollout caught at canary (Phase 11).
- A correlated failure (PDU/ToR/CDU): blast-radius reasoning (Phase 01) turns an alarm storm into one root cause; placement respected failure domains.
- The control plane itself: idempotent, restartable; state persisted; no single in-flight operation corrupts on restart (graceful shutdown, Phase 02).
Demonstrating these failure paths — not just the happy path — is what separates a real platform from a demo, and it's exactly what an interviewer probes.
Chapter 5: What Changes at Fleet Scale
The mini-platform manages one rack; the design doc must answer "what about 10,000 nodes?" — the
bridge to the system-design/ chapters:
- Telemetry (Phase 07): one Prometheus won't scrape 10k nodes × dozens of metrics — federate/
remote-write to Mimir/Thanos; ruthless cardinality control. (
system-design/02) - Control plane: shard/partition by rack/site; the reconcile loop is per-node and parallel;
smart proxies near hardware (Phase 05) so the control plane stays central but the device traffic
stays local. (
system-design/01) - Provisioning/firmware: progressive rollout, failure-domain-aware waves, canary (Phase 08/11).
(
system-design/03) - Tenancy: isolation at scale across network/compute/accelerator; attestation gating (Phase 09).
(
system-design/04) - Operations: SLOs, error budgets, on-call across sites, runbooks (Phase 07/12).
The honest move in the design doc is to build the single-rack version cleanly and state precisely what you'd change at scale — showing you understand both.
Chapter 6: Shipping It — Design Doc, Runbook, Retro
The capstone isn't done when the code runs; it's done when it's shipped like an engineer ships (Phase 12):
- Design doc: problem, architecture + the seams, the data model, the key ADRs (driver abstraction, A/B firmware, operator lifecycle), failure handling, and the fleet-scale section.
- Runbook (Phase 12): one real operation end to end (commission a rack / firmware rollout / node RMA) — trigger, steps, risk bounds, rollback.
- Release cycle (Phase 11): tests (the pyramid), a canary rollout, and a retrospective — what worked, what you'd change, the RCA-style learnings. The retro is where you demonstrate the Phase 10 reflective discipline on your own work.
Capstone Walkthrough Guidance
- Run the provided mini-platform:
python3 capstone-mini-rack-manager/solution.py— it composes inventory + driver + reconcile + provisioning + firmware + telemetry + tenancy into one end-to-end demo (discover → ready → operate → a fault → recovery), with self-tests. - Read it as an integration of the prior labs — find each phase's pattern in it and the seams between them.
- Then extend it into your capstone: wire in a real Redfish emulator (Phase 03), add the canary rollout (Phase 11), and write the design doc + runbook + retro (Phase 12).
- The interview deliverable is the running platform plus the three documents.
The capstone question to be able to answer cold: "Walk me through the rack-management platform you built — the architecture, the seams, how it handles a flaky BMC and a bad firmware, and what changes at 10,000 nodes." If you can give that tour from your own code and design doc, you're ready.
Success Criteria
- The mini-platform runs end to end with no hardware and passes its tests
- One model + one reconcile loop + one driver abstraction underpin it (no protocol leaks)
- It demonstrates failure paths (flaky BMC, quarantine, firmware rollback), not just happy path
- You wrote a design doc with the seams, key ADRs, and a fleet-scale section
- You wrote a runbook and a retrospective (Phase 12)
- You can give the 5-minute platform tour and the "what changes at scale" answer
Interview Q&A
Q1: Walk me through the rack-management platform you built.
A: It's a control plane over a device abstraction. One inventory model (components,
relationships, state, the EBOM baseline) is the spine; one reconcile loop drives each node from
discovered → provisioned → at-firmware-baseline → attested → configured → validated → ready, and
keeps it there by correcting drift — idempotently and resumably. All hardware access goes through a
DeviceDriver abstraction (Redfish-first, IPMI/SNMP fallback) so protocol/vendor variance is
contained and I can run the whole thing against fakes/emulators with no hardware. Around that:
provisioning is a validation-gated state machine that quarantines bad nodes (EBOM mismatch, failed
attestation, RAS faults); firmware updates are A/B with rollback; telemetry is exposed in Prometheus
format with alerts; tenancy enforces isolation invariants; and it's operated via a structured-logged
CLI and covered by a test pyramid. I ship it with a design doc, a runbook, and a retrospective. The
three load-bearing ideas are one model, one reconcile loop, and clean seams.
Q2: Where are the seams and why do they matter?
A: The most important is the DeviceDriver boundary — it gives portability (add a vendor/protocol
behind it without touching callers) and testability (inject a FakeDriver to exercise every failure
path with no hardware), which is what makes the whole platform CI-able (Phase 11). The second is the
inventory model — the single contract every part reads/writes, so telemetry, provisioning, and
firmware all speak the same nouns. The third is the reconcile API — declared state in, convergence
out — which lets provisioning and firmware be actions the loop takes rather than competing
orchestrators. Good seams are why I can swap a real BMC for a fake, add a tenant, or test a bad-
firmware rollback in isolation.
Q3: How does it handle a flaky BMC, a bad node, and a bad firmware? A: A flaky BMC: the driver wraps calls in retry+backoff+jitter and a circuit breaker (Phase 10), and the reconcile loop requeues rather than getting stuck or hammering it. A bad node: the provisioning validation gate quarantines it with a reason (EBOM mismatch / failed attestation / RAS fault — Phases 08/09/12) and raises an alert with a runbook, instead of handing it to tenants. A bad firmware: the orchestrator verifies before and after and rolls back to the previous A/B bank on failure (Phase 08), and at fleet scale a rollout is caught at the 1% canary and auto-rolled-back (Phase 11). I'd demo each of those failure paths, because how it fails is the real test of a platform.
Q4: What changes when this manages 10,000 nodes instead of one rack?
A: The single-rack design stays, but several things scale out. Telemetry: one Prometheus can't scrape
10k nodes × dozens of metrics, so I federate/remote-write to Mimir/Thanos and get strict about
cardinality (system-design/02). Control plane: shard by rack/site, run the per-node reconcile loops
in parallel, and use smart proxies near the hardware so the control plane stays central while device
traffic stays local (system-design/01). Provisioning and firmware become progressive, failure-
domain-aware, canaried rollouts (system-design/03). Tenancy isolation and attestation scale across
network/compute/accelerator (system-design/04). And operations become SLO-driven with cross-site
on-call and runbooks. The honest answer is: build the one-rack version cleanly, and know exactly what
you'd change at scale — which is what the design doc states.
References
- The prior phases' READMEs/WARMUPs — the capstone is their composition
- The Datacenter as a Computer (Barroso et al.) — warehouse-scale systems thinking
- Kubernetes operator pattern (the reconcile model) — https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
- This track's system-design/ — the fleet-scale designs the capstone points to
- Cross-track capstones: Head of SWE — GPU, Phase 12 capstone
🛸 Hitchhiker's Guide — Phase 13: The Capstone
Read this if: you've done the phases and need to assemble them into one platform you can demo and defend. Compressed map of the composition.
0. The 30-second mental model
Compose the parts into a control plane over a driver abstraction, held together by three ideas:
one data model (P01), one reconcile loop (P06), clean seams (the DeviceDriver, P03 —
the testability superpower). Provisioning (P05) and firmware (P08) are actions the loop takes, not
separate orchestrators. Ship it with a design doc + runbook + retro (P12). Demo the failure
paths, not just the happy path.
1. The architecture
rackctl (P02) → control plane:
inventory model (P01) ←— reconcile loop (P06): declared → discovered, converge
provisioning SM (P05) + EBOM gate (P12) + attestation (P09)
firmware A/B + rollback (P08)
telemetry/exporter + alerts (P07)
tenancy/secure config + isolation invariants (P09)
↓ DeviceDriver abstraction (P03): Redfish-first, IPMI/SNMP fallback, FakeDriver for tests
2. The three load-bearing ideas
- one model (P01): the spine every part reads/writes; declared baseline = EBOM.
- one reconcile loop (P06): idempotent, resumable, level-triggered convergence.
- clean seams: the driver boundary (swap real BMC ↔ fake), the model, the reconcile API.
3. Lifecycle as states
DISCOVERED → INVENTORIED(+EBOM) → FIRMWARE_BASELINED → ATTESTED → OS_INSTALLED → CONFIGURED → VALIDATED → READY → operate(telemetry/drift-correct) → maintain(firmware/RMA) → DECOMMISSION. Idempotent · resumable · validation-gated · observable · reconciled vs intent.
4. Demo the failures (what proves it's real)
flaky BMC → driver retry+breaker, loop requeues · bad node → quarantine w/ reason (EBOM/attest/RAS) · bad firmware → A/B rollback + canary halt · correlated failure → blast-radius RCA (P01) · control plane restart → idempotent/persisted, no corruption.
5. What changes at 10k nodes (the design-doc section)
telemetry → federate/remote-write + cardinality (P07, sd/02) · control plane → shard + smart proxies (sd/01) · provisioning/firmware → progressive, domain-aware, canary (sd/03) · tenancy → isolation + attestation at scale (sd/04) · ops → SLOs + cross-site on-call (P07/12).
6. Ship like an engineer
design doc (architecture + seams + ADRs + failure handling + fleet section) · runbook (P12) · release via the pipeline (P11) + a retrospective (your own RCA-discipline on your own work, P10).
7. The 5-minute tour to rehearse
"control plane over a driver abstraction; one model, one reconcile loop, clean seams; lifecycle as states; here's a flaky BMC handled, a node quarantined, a firmware rolled back; at 10k nodes I'd shard + federate + canary; design doc + runbook + retro attached." Nail that and the loop is yours.
8. How it ties the whole track together
The capstone is the portfolio behind every interview answer. It reuses P01 (model), P02 (CLI/
patterns), P03 (drivers), P05 (provisioning), P06 (reconcile), P07 (telemetry), P08 (firmware), P09
(tenancy), P10 (reliability), P11 (tests/release), P12 (docs) — and points at system-design/ for
scale.
Capstone — Mini Rack Management Platform
Phase: 13 — Capstone | Difficulty: ⭐⭐⭐⭐⭐ | Time: 2 weeks (the integration + docs) Language: Python 3 (stdlib) | Hardware: none (composes the prior phases' fakes/emulators)
Concept primer:
../WARMUP.md,../HITCHHIKERS-GUIDE.md.
Run
python3 solution.py # discover -> reconcile to READY -> quarantine -> telemetry -> drift-correct
0. The mission
Compose the whole curriculum into one runnable platform and the three Staff documents that
ship it. solution.py is the integrated reference; your capstone extends it and adds the design doc,
runbook, and retrospective.
1. Find every phase in the code
| Phase | In the platform |
|---|---|
| 01 model | Node/Rack inventory graph + domain (failure domain) |
| 03 drivers | FakeDriver behind the device seam (swap for a real Redfish emulator) |
| 06 reconcile | RackManager.reconcile_node/all — declared→discovered convergence, idempotent |
| 05 provisioning | the state machine DISCOVERED→…→READY with a validation gate |
| 12 EBOM gate | firmware verify against baseline_firmware; quarantine on mismatch |
| 09 attestation + tenancy | attest() gate; tenancy_violations() invariant |
| 08 firmware | set-then-verify + drift correction (re-flash on out-of-band change) |
| 10 reliability | resilient() retry wrapper around the flaky driver |
| 07 telemetry | metrics() Prometheus exposition (node_ready, node_temp) |
2. The failure paths it demonstrates (the point)
- Bad node (node-3 fails attestation) → QUARANTINED with a reason, not shipped.
- Flaky BMC (node-4) →
resilient()retries recover it → it still reaches READY. - Firmware drift (node-0 downgraded out-of-band) → the reconcile loop re-flashes to baseline and re-converges to READY (the self-healing loop).
- Tenancy invariant → a reserved-plane misassignment is caught.
The self-tests assert all of these — demonstrating how it fails, which is what makes it a platform rather than a demo.
3. Build YOUR capstone (extend this)
- Real drivers: replace
FakeDriverwith the Phase 03 Redfish client against the DMTF mockup server (and keep the fake for tests — the seam pays off). - Real provisioning + firmware: wire in the Phase 05 state machine and the Phase 08 A/B orchestrator (with rollback) as the loop's actions.
- Real telemetry: serve
metrics()over HTTP (Phase 07 exporter) and add the alert rules. - Canary release: ship a platform change via the Phase 11 canary controller.
- The three documents (Phase 12):
- Design doc: architecture + the seams + key ADRs (driver abstraction, A/B firmware,
operator lifecycle) + failure handling + a fleet-scale section (point at
system-design/). - Runbook: one real op end to end (commission a rack / firmware rollout / node RMA).
- Retrospective: what worked, what you'd change, RCA-style learnings on your own build.
- Design doc: architecture + the seams + key ADRs (driver abstraction, A/B firmware,
operator lifecycle) + failure handling + a fleet-scale section (point at
4. What this proves
You can build and operate a rack-management platform end to end — the JD, composed — and ship it like a Staff engineer. "Walk me through a rack-management system you built" becomes a confident 5-minute tour of your own running code and design doc, including the failure paths and the what-changes-at-scale answer. This is the portfolio behind every other interview answer.
Interview Prep — Rack Management (Senior / Staff) SWE
This is the interview surface for the role, drawn from the 13 phases. A Qualcomm-style rack-management loop spans deep technical (systems programming, protocols, hardware), applied infra (provisioning, orchestration, observability), production debugging/RCA (the most-weighted theme), system design, and behavioral/Staff leadership — often in the same day.
The five question banks
| File | Covers | Phases |
|---|---|---|
| 01 — Foundations & Systems Programming | rack anatomy, power/cooling, Python/C++/shell, RAII, security | 01–02 |
| 02 — Protocols & Hardware Interfacing | Redfish/IPMI/SNMP, BMC/PDU/CDU/PCIe, firmware, RAS | 03–04, 08 |
| 03 — Provisioning, Orchestration & Observability | PXE/Foreman/Ansible/Terraform, K8s/RKE2/operators, Prometheus/Grafana | 05–07 |
| 04 — Reliability, Debugging & Security | debugging method, reliability patterns, chaos, RCA, secure boot/tenancy, CI/CD | 09–11 |
| 05 — Behavioral & Staff Leadership | EBOM/specs, design reviews/ADRs, runbooks, mentorship, STAR, comms | 12 |
| System Design | the big design walkthroughs | all |
How to use this
- Self-test cold, then check against the model answer and the WARMUP it derives from. A gap points you back to a phase. The WARMUPs' "Interview Q&A" sections are the depth target — this bank is the index + the highest-yield additions.
- Derive, don't recite: every technical answer should reason from a principle (the roofline/ bandwidth math, the 80% breaker rule, follow-the-link Redfish, level-triggered reconcile, verify-before-and-after firmware), not a memorized fact.
- Debugging is the headline: the JD repeats "debugging and root-cause analysis of complex issues, including deployed systems" three times. Be ready to narrate method (Phase 10) on any "this is broken in production" prompt.
- Hands-on credibility: the labs are your evidence. "How do you test code that needs a BMC?" or "how do you parse a crash dump?" should be answered with something you built.
The 20 questions most likely to decide the loop
- Walk me through the components of an AI rack and how it's powered and cooled. (P01)
- Two 30A 415V feeds, N+1 — how much load can you safely place, and why not 100%? (P01)
- What is Redfish and why is the industry moving to it from IPMI? (P03)
- Discover a server's power state and CPU temperature with Redfish — walk me through it. (P03)
- How does a firmware update work safely, and what are A/B banks? (P08)
- What's PCIe AER? Correctable vs uncorrectable — what do you do about each? (P04/P08)
- A node logs a flood of correctable ECC errors — is that a problem? (P08)
- What is RAII and why does it matter for a rack daemon? (P02)
subprocess.run(..., shell=True)— what's wrong and how do you fix it? (P02)- How do you test software that talks to hardware you don't have in CI? (P11)
- Explain the operator pattern and the reconcile loop. Why level-triggered? (P06)
- Walk me through what happens when a diskless server PXE-boots. (P05)
- What is idempotency and why is it central to config management? (P05)
- Design a telemetry pipeline for a rack fleet with Redfish/IPMI/SNMP devices. (P07)
- What would you alert on for an AI rack, and how do you avoid alert fatigue? (P07)
- A production node reboots intermittently under load — debug it, you can't take it offline. (P10)
- What's a circuit breaker and why do you need one with retries? (P10)
- How do you isolate tenants on a shared rack, and why is the management plane special? (P09)
- Secure boot vs measured boot — what's the difference, and what's attestation for? (P09)
- What's the difference between Senior and Staff, and how do you drive implementation/mentor? (P12)
Each maps to a WARMUP's Interview Q&A with a full principal-level answer.
Format expectations for this role
- Technical screens: reason about a roofline/power budget; whiteboard a Redfish walk, a reconcile loop, or a firmware state machine; read a backtrace / RAS log.
- Coding: a daemon/CLI/parser in Python or C++ (the labs are calibrated to this) — with the seam, error handling, and tests.
- System design: the system-design walkthroughs (control plane, telemetry, fleet firmware, tenancy, provisioning).
- Debugging: a "production is broken" scenario — narrate the method (Phase 10).
- Behavioral/Staff: STAR stories, design-review judgment, distributed-team communication.
Interview Bank 01 — Foundations & Systems Programming
Phases 01–02. Rack anatomy, power/cooling, the management plane, and production Python/C++/shell. Full-length model answers live in the phase WARMUPs' Interview Q&A; this bank is the high-yield set plus quick-fire drills.
Q1. Walk me through the components of an AI rack and how it's powered and cooled.
Compute trays (host CPUs + 4–8 accelerators with HBM + NICs/DPUs + NVMe), scale-up fabric (NVSwitch-class) wiring accelerators into one coherent fabric, a top-of-rack data switch, a separate management switch, power (3-phase AC → power shelf/PSUs → busbar, N+1/2N), and — because these draw 60–130+ kW (~10× a legacy rack) — liquid cooling via a CDU pumping coolant through cold plates and exchanging heat with facility water. Every node also has a BMC on the management network. Framing: it isn't "N servers," it's a system with rack-level shared resources (power, cooling, fabric) managed as first-class objects. (WARMUP P01 Ch. 2; Q1.)
Q2. Out-of-band vs in-band management — and why does it matter to your software?
In-band goes through the host OS/data NIC (only works when the host is healthy); out-of-band goes through the BMC on a separate management network with standby power (works when the host is off, hung, or panicked). It matters because the moments you most need to manage a node are when it's unhealthy, so power control, firmware, console, and diagnostics ride OOB. My control plane assumes the host may be down and reaches everything through the management network. (P01 Ch. 3; Q2.)
Q3. Two 30A 415V feeds, N+1 — how much load can you safely place, and why not 100%?
Per feed ≈ √3·415·30 ≈ 21.6 kW, but the 80% continuous-load rule caps a breaker at 80% → ~17.3 kW. With N+1 the survivor must carry the whole load after one feed dies, so the safe load is bounded by a single derated feed (~17.3 kW), not the sum. Budget to the sum and one failure overloads the survivor and cascades; budget to nameplate and the breaker trips. Measured draw is usually well below nameplate, so I oversubscribe carefully with telemetry + power capping, but the guaranteed safe number is the derated single feed. (P01 Ch. 4; Q3.)
Q4. Why are AI racks liquid-cooled, and what does that add to your job?
Power density: air can't remove heat past ~40 kW, and AI racks dump 60–130+ kW (1 kW = 3,412 BTU/hr). Liquid carries thousands× more heat/volume, so coolant runs through cold plates with a CDU. For software it adds a managed subsystem: I monitor CDU flow, supply/return ΔT, pressure, pump health, and leak detection — a rising ΔT at constant load is a pre-failure signal and a leak is a safety-critical alarm that may trigger emergency action. (P01 Ch. 5; Q4.)
Q5. What's a failure domain, and how does your software use it?
A set of components that fail together because they share a resource (a PDU, a CDU, a ToR); the blast radius is everything in it. Software uses it three ways: the inventory model encodes powers/cools/ connects edges to compute blast radius; placement spreads replicas across domains; and during incidents the first question is "single node or shared resource?" — correlating alerts by domain turns an alarm storm into one root cause. (P01 Ch. 7; Q5.)
Q6. What is RAII and why does it matter for a rack daemon?
RAII ties a resource's lifetime to an object's scope — acquire in the constructor, release in the
destructor — so fds, BMC sessions, and mutex locks are freed on every exit path including
exceptions, with no manual cleanup. For a daemon it's the difference between leaking handles/
deadlocking on error paths and being correct by construction (a lock_guard releases even if the
critical section throws). Rule of zero: hold RAII members, write no destructor/copy/move. (P02 Ch. 3;
Q1.)
Q7. subprocess.run(f"ipmitool -H {host} ...", shell=True) — what's wrong, fix it.
Shell injection: host="x; rm -rf /" runs arbitrary commands with the daemon's (high) privileges. Fix:
pass an argv list with shell=False (default) — ["ipmitool","-H",host,...] — so host can never
be reparsed as shell syntax; add a timeout, validate host, check the return code. Same rule in C++:
never system() a built string; use exec/posix_spawn. (P02 Ch. 8; Q2.)
Q8. How do you make rack code testable when the real dependency is a BMC you don't have in CI?
Put a seam at the hardware boundary — an interface (SensorSource/DeviceDriver) injected via DI —
so production wires the real client and tests wire a fake that scripts failures/timeouts/garbage
deterministically; inject the clock too so timing tests are instant. That gives a wide base of fast
unit tests; above it, integration tests run against emulators (Redfish mock, ipmi_sim), and a thin top
runs on real HIL. Testability is a design decision (the seam), not a bolt-on. (P02 Ch. 9; Q3.)
Q9. Threads, async, or processes — and where does the GIL bite?
I/O-bound concurrency (poll 100 BMCs) → asyncio or a bounded thread pool; CPU-bound → processes (CPython's GIL blocks thread parallelism for bytecode); fault isolation → processes. The GIL is released during I/O, so threads help I/O-bound work but not CPU-bound. In C++ threads give real parallelism but I own synchronization (tiny critical sections under a guard, prove with TSan). Whatever the model, bound the concurrency so I don't exhaust fds or flood the management network. (P02 Ch. 6; Q5.)
Quick-fire drills (know cold)
- 1 kW = 3,412 BTU/hr; AI rack ≈ 60–130+ kW.
- 3-phase usable ≈ √3·V·I·PF; then ×0.8 (breaker derate).
- N+1 safe load = one derated feed, not the sum.
- HBM : NVLink : PCIe ≈ 50 : 13 : 1 (cross-track GPU P01).
- BMC = always-on ARM SoC (OpenBMC) on standby power; works when the host is dead.
- RAII / rule of zero;
lock_guardnot manual lock/unlock; ASan/UBSan/TSan in CI. - No
shell=True; validate input with allow-lists; secrets never in argv (/procleaks). - The seam = the reason you can unit-test without hardware.
Interview Bank 02 — Protocols & Hardware Interfacing
Phases 03–04, 08. Redfish/IPMI/SNMP, BMC/PDU/CDU/PCIe, firmware lifecycle, and RAS diagnostics. Full answers in the phase WARMUPs; this is the high-yield set.
Q1. What is Redfish and why is the industry moving to it from IPMI?
Redfish is the DMTF's modern OOB standard: RESTful, JSON, HTTPS, schema-defined, modeling a system as a resource tree (ServiceRoot → Systems, Chassis, Managers, …). The move from IPMI (binary, complex, poor security history — Intel deprecated it in 2019) is because Redfish is human-readable, hypermedia- driven (follow links → vendor-portable), secure by default, and models modern needs natively (Tasks for long ops, EventService for push, TelemetryService, Power/Thermal schemas extending to PDUs/CDUs). (P03 Ch. 1–2; Q1.)
Q2. Discover a server's power state and CPU temperature with Redfish — walk me through it.
GET /redfish/v1, follow the Systems collection's @odata.id, GET a member — PowerState is on
the ComputerSystem. For temperature, follow the System's Links.Chassis, GET the Chassis, follow
Thermal, read Temperatures[].ReadingCelsius. Key discipline: follow @odata.id links, never
hardcode paths, because vendors lay out IDs differently — that's what makes Redfish portable. Over
HTTPS with a session token and cert validation. (P03 Ch. 2–3; Q2.)
Q3. Which Redfish resource owns power state vs temperature vs BMC firmware?
System = the host (power state, boot, CPUs; the Reset action). Chassis = the physical box (its
Power sub-resource: PSUs/draw/cap; its Thermal: temps/fans). Manager = the BMC itself (its
firmware, network, virtual media). One node = a System + a Chassis + a Manager, linked. Conflating
them is the #1 Redfish mistake. (P03 Ch. 2.)
Q4. A firmware update takes minutes — how does Redfish model it, and how do you write the client?
Asynchronously: POST UpdateService (SimpleUpdate/multipart) → 202 + a Task; poll the Task's
TaskState (New→Running→Completed/Exception) and PercentComplete. The client must submit → poll →
handle completion/failure, never block on a synchronous response. Same Task pattern covers BIOS
config jobs. (P03 Ch. 4; Q3.)
Q5. How do you get notified a PSU failed — poll or push?
Both, layered: Redfish EventService subscription (BMC POSTs to my listener) or SSE (long-lived connection, one per BMC) for low-latency faults; SNMP traps (UDP 162) for non-Redfish PDUs; and scheduled polling as the backstop (events can be lost). At fleet scale: event-driven for state changes, scheduled polling for metrics. (P03 Ch. 4; Q4.)
Q6. What's an OID, and what happens in an SNMP WALK? v2c vs v3?
An OID is a dotted-number name in SNMP's global tree (e.g., 1.3.6.1.2.1.1.3.0 = sysUpTime); a MIB
maps names↔OIDs; vendor data lives under 1.3.6.1.4.1.<enterprise>. A WALK enumerates a subtree via
repeated GETNEXT in lexicographic (per-component numeric) order (so .2 before .10). v1/v2c
authenticate with a cleartext community string (mgmt-net only); v3 (USM) adds auth + privacy and is
what you use where security matters. (P03 Ch. 6; Q5.)
Q7. Your fleet has Redfish nodes, SNMP PDUs, and IPMI-only BMCs — one tool, how?
A driver abstraction: one DeviceDriver interface with RedfishDriver/IpmiDriver/SnmpDriver
backends and a capability-detecting factory; all logic programs against the interface and a unified
Reading model normalizes telemetry. This contains protocol quirks, lets a device migrate protocols
without touching callers, and is testable with a FakeDriver. Plus defensive coding for vendor
variance (follow links, .get() defaults, retries/timeouts, per-vendor conformance tests). (P03 Ch.
8–9; Q6.)
Q8. How do you reinstall an OS on a server you can't physically touch?
OOB via the BMC's virtual media: mount the installer ISO over Redfish as a virtual CD, set next-boot to it (one-time override), power-cycle, watch over SoL, then eject and boot from disk. Works even with no/broken OS — the whole point of OOB, and the rescue path behind PXE provisioning. (P04 Ch. 3; Q2.)
Q9. An accelerator is "healthy" but 4× slow — how could PCIe be the cause, and how do you find it?
A degraded link: the PCIe link negotiated down (x16→x4 or Gen5→Gen1), collapsing bandwidth while the
device still enumerates and reports healthy. Find it by comparing LnkSta (negotiated) vs LnkCap
(capable) in lspci -vvv or sysfs (current_link_width/speed vs max_*). Also check NUMA adjacency
(GPU+NIC same socket) and AER correctable rates. (P04 Ch. 6–7; Q4.)
Q10. How do you safely update firmware on 1,000 nodes, and what are A/B banks?
Per node: verify before touching HW (checksum + signature + version + anti-rollback) → flash the inactive A/B bank (active firmware keeps running) → activate → verify after (version + health) → commit or roll back to the previous bank. A/B means a bad flash never destroys working firmware (critical for the BMC). Across the fleet: drain-first (P06), dependency order, canary 1→1%→10%→rest with auto-rollback (P11), don't update a whole failure domain at once. (P08 Ch. 3, 5; Q1.)
Q11. PCIe AER / CPU MCE — correctable vs uncorrectable, and a flood of correctable ECC?
AER: correctable (recovered; a rate signals a marginal link → monitor) vs uncorrectable (non-fatal → cordon, fatal → RMA). MCE: bank → subsystem, address → DIMM; correctable ECC = log, uncorrectable = RMA. A flood of correctable ECC on one DIMM is a predictive-failure signal → schedule replacement before it throws an uncorrectable (crash). Rate, not raw count. (P08 Ch. 6–7; Q4–5.)
Quick-fire drills
- Follow
@odata.idlinks; never hardcode Redfish paths. - System=host power; Chassis=temps/PSUs; Manager=BMC firmware.
- Firmware/long-op = a Task (202 → poll).
ipmitool -I lanplus(RMCP+) only; disable cipher-0.- SNMP WALK = lexicographic GETNEXT; v3 for security.
- A/B banks + verify before & after + anti-rollback.
- Degraded link = LnkSta < LnkCap (silent perf killer).
- RAS action tree: log → monitor (rising correctable) → cordon → RMA the FRU.
Interview Bank 03 — Provisioning, Orchestration & Observability
Phases 05–07. PXE/Foreman/Ansible/Terraform, Kubernetes/RKE2/operators, Prometheus/Grafana/tracing.
Q1. Walk me through what happens when a diskless server PXE-boots.
NIC PXE firmware → DHCP returns an IP + option 66 (boot server) + option 67 (boot file), using the client-arch tag for BIOS vs UEFI → fetch the bootloader (iPXE) over TFTP/HTTP → iPXE fetches an HTTP script → loads installer kernel+initrd → unattended install via kickstart/autoinstall → reboot into the OS where config management converges it. With Secure Boot on, the shim→GRUB→kernel chain must be signed. (P05 Ch. 2; Q1.)
Q2. How do you onboard 500 nodes where ~3% fail mid-flow?
A persisted per-node state machine, not a sequential script: discover → EBOM reconcile → firmware baseline → OS install → configure → validate → join orchestrator → ready, with every step idempotent and resumable (node 237 resumes at its last good state) and a validation gate that quarantines bad nodes (failed health/PCIe/firmware/attestation) instead of shipping them. Smart proxies near the hardware; full observability so I can watch the fleet onboard. (P05 Ch. 8; Q2.)
Q3. What is idempotency and why is it central to config management?
Applying the same configuration any number of times converges to the same end state and is a no-op once
there. CM declares the desired state (package present, service running) not steps, so it's safe to
run continuously, corrects drift, and survives partial failures. useradd bob breaks the second time;
"user bob present" always succeeds. It's the same control loop as a Kubernetes operator. (P05 Ch. 5;
Q3.)
Q4. Ansible vs Puppet vs Chef — when each?
All idempotent declarative convergence engines; architecture differs. Ansible: agentless, push, SSH, YAML — fast to adopt, great for orchestration/ad-hoc (often the provisioning default). Puppet: agent, pull, strong declarative DSL — continuous enforcement at scale. Chef: agent, pull, Ruby — programmatic flexibility. Choose by push-vs-pull / agent-vs-agentless, not capability. (P05 Ch. 6; Q4.)
Q5. Terraform state and drift?
State is Terraform's map of declared HCL ↔ real resource IDs — how it computes the diff. Drift = reality
changed outside Terraform; the next plan shows it and apply reconciles. Disciplines: locked remote
state (never local for teams), never hand-edit it, always plan before apply (see create/change/
destroy). Declared-vs-discovered at the infra layer. (P05 Ch. 7; Q5.)
Q6. What happens when you kubectl apply a Deployment?
API server validates + writes to etcd → Deployment controller makes a ReplicaSet → ReplicaSet controller makes Pods → scheduler binds each to a node (filter by capacity/taints/affinity, then score) → that node's kubelet pulls the image and starts the container → controllers keep reconciling. Nothing imperative — every component watches state and converges it. (P06 Ch. 2; Q1.)
Q7. Explain the operator/reconcile loop. Why level-triggered?
A CRD (e.g., RackNode) + a controller running observe → diff(spec vs status/world) → act idempotently
→ update status → requeue. Level-triggered = reacts to current state, not the change event, so a
missed/duplicated event isn't fatal — the next reconcile sees reality and fixes it (self-healing).
Edge-triggered breaks on a missed event. Plus finalizers for delete-time cleanup. (P06 Ch. 4; Q2.)
Q8. How does a GPU/accelerator get scheduled to a pod, and why is a pod Pending?
A device-plugin DaemonSet advertises accelerators to the kubelet as an extended resource (e.g.,
qualcomm.com/ai-accelerator: 4); pods request it; the scheduler treats it as capacity; the kubelet
allocates the device. Pending = the filter phase found no node: insufficient resources anywhere, an
unmatched nodeSelector/affinity, or an untolerated taint. (P06 Ch. 5–6; Q4.)
Q9. What is RKE2 and why use it over upstream k8s?
A hardened, CIS-benchmarked, FIPS-capable Kubernetes distro with a simple single-binary, embedded-etcd, air-gappable install — exactly what sovereign/regulated data centers (e.g., in the Kingdom) need. It's still Kubernetes, so operators/DaemonSets run unchanged. (P06 Ch. 7; Q5.)
Q10. Design a telemetry pipeline for a rack fleet with Redfish/IPMI/SNMP devices.
Unified exporters near the hardware (Phase 03 driver abstraction, poll-on-own-schedule + cache, consistent metric/label scheme) → Prometheus scrapes them (federate/remote-write to Mimir/Thanos at scale) → Grafana fleet→rack→node dashboards (USE/RED) → Alertmanager on SLO symptoms with runbook links; logs to Loki, traces via OTel, correlated by IDs. Two disciplines: strict cardinality control and alert on symptoms, not causes. (P07 Ch. 4–9; Q1.)
Q11. Counter vs gauge vs histogram, and the cardinality trap?
Counter (only up → query rate()): PCIe AER errors. Gauge (up/down): temperature, power, CDU ΔT.
Histogram (distribution → p99): provisioning step duration. Cardinality = product of label values;
never put unbounded values (IDs/timestamps/messages) in labels → OOM. Labels = bounded dimensions
(rack/node/sensor); detail goes in logs/traces. (P07 Ch. 3, 6; Q3–4.)
Q12. What would you alert on for an AI rack, and how avoid alert fatigue?
Symptoms that matter, with for: + severity + a runbook: node/exporter down, rack over power budget,
sustained accelerator overheat/throttle, CDU leak/ΔT anomaly, SLO breaches. Avoid fatigue by not
paging on causes — a single correctable PCIe error is a dashboard, not a page; alert on a rate or
sustained condition. Every page must be actionable. (P07 Ch. 9; Q5.)
Quick-fire drills
- PXE: DHCP opt 66/67 → iPXE over HTTP → kickstart/autoinstall.
- Idempotent + resumable + validation-gated = a real provisioner.
- Reconcile loop = level-triggered, idempotent, requeue, finalizers.
- Pod Pending = capacity / taint / affinity (the filter phase).
- RKE2 = hardened + air-gappable k8s for sovereign deploys.
- Pull metrics; counter→rate, gauge→value, histogram→p99; bound cardinality.
- Alert on symptoms (SLOs), not causes; runbook on every page.
- metrics → traces → logs (correlate by ID) is the debug loop.
Interview Bank 04 — Reliability, Debugging & Security
Phases 09–11. The JD's most-weighted theme (debugging/RCA) plus reliability patterns, secure boot/ tenancy, and CI/CD/release. Expect a "production is broken" scenario — narrate the method.
Q1. A production node reboots intermittently under load — debug it, you can't take it offline.
Method, narrated: (1) scope the failure domain (just this node or peers on the same PDU/ToR/CDU?); (2) evidence-first via OOB since the host is unreliable — SEL + BERT (last-crash cause), the RAS parser on AER/MCE, telemetry correlating reboots with temperature/power/load; (3) ask "what changed?" (recent firmware/deploy); (4) hypothesize and test cheaply — e.g., MCE points at a DIMM with rising correctable ECC that climbs with temperature → marginal DIMM under thermal load; reproduce on a canary; (5) fix — cordon/drain, RMA the DIMM, re-validate; (6) prevent — add an alert + test, write the blameless RCA. The method is the answer. (P10 Ch. 1–3; Q1.)
Q2. A daemon is at 100% CPU / a process is hung — what do you do?
100% CPU → perf top / flame graph shows where the cycles go (no guessing); cross-check strace and
metrics. Hung → strace -p <pid> shows the exact blocked syscall (a futex = lock/deadlock → gdb -p
thread backtraces; a read/connect = waiting on a dead dependency with no timeout → the bug is the
missing timeout). For production, prefer eBPF/bpftrace to peek without restarting. (P10 Ch. 2; Q2–3.)
Q3. Suspect memory corruption or a race — which tools?
Memory: ASan/valgrind give the exact use-after-free/overflow with allocation+use sites (the crash is usually far from the cause). Race: TSan detects the unsynchronized accesses deterministically without the race needing to manifest (races are Heisenbugs — don't chase them with logging, which changes timing). Make nondeterminism deterministic (inject clock/scheduling) and add the sanitizer to CI. (P10 Ch. 4; Q5.)
Q4. What's a circuit breaker and why do you need one with retries?
After N failures to a dependency it "opens" (fail fast for a cooldown), then "half-opens" to probe, then "closes" on recovery. You need it with retries because retries alone cause a retry storm — every client hammering a down BMC prevents recovery and cascades. Backoff+jitter spread load; the breaker stops calling a clearly-dead dependency and probes for recovery. With idempotency and timeouts, it's the core of resilient calls to flaky hardware. (P10 Ch. 5; Q4.)
Q5. How do you run a postmortem after a serious incident?
Blamelessly, with tracked outcomes: timeline, impact (SLO/error-budget burned), detection (and how it should have been sooner), root cause via the 5 Whys to the systemic cause (not "the node OOMed" but "unbounded cache, no memory limit, no test"), and — most important — owned, dated action items (a regression test, an alert/runbook, a guard, a design change), tracked to done. Blameless because if a human could trigger it, the system allowed it. The deliverable is an org that won't have that class of incident again. (P10 Ch. 7; Q6.)
Q6. How do you isolate tenants on a shared rack, and why is the management plane special?
VLAN-per-tenant (802.1Q) for broadcast-domain isolation; on hosts, namespaces+veth+bridges+VLAN sub- interfaces; firewall ACLs deny cross-tenant; QoS (HTB) prevents noisy neighbors with a reserved management class. The management plane (BMC/PDU/CDU) is a hard boundary — its own VLAN, control- plane-only, never tenant/internet-facing — because those devices have total power/firmware control; an exposed BMC is a full compromise. Defense in depth: no single control is load-bearing. (P09 Ch. 2, 8; Q1.)
Q7. Secure boot vs measured boot — difference, and what's attestation for?
Secure Boot enforces: refuses to run unsigned/untrusted boot code (firmware→shim→GRUB→kernel,
db/dbx, PK/KEK) — stops bootkits. Measured boot measures: each stage hashes the next into a TPM PCR
(PCR = Hash(PCR || measurement), extend-only) — records exactly what booted. Remote attestation:
the node returns a signed PCR quote; a verifier checks it against known-good values before trusting the
node (cluster join, key release, tenant work). Essential for multi-tenant/sovereign deploys. (P09 Ch.
5–7; Q3–5.)
Q8. How do you test software that talks to hardware you don't have in CI?
The seam + emulators + HIL: program against a device interface so unit tests run against fakes; run emulators (Redfish mock, ipmi_sim, snmpsim) as CI service containers for integration tests; keep a small HIL pool behind a gated stage for what only hardware can prove (vendor conformance, firmware, timing). The pyramid is wide at the base (fakes), thin at the top (HIL). Testability is the seam, decided up front. (P11 Ch. 1–3; Q1.)
Q9. How do you safely roll out a new version to 10,000 nodes?
A gated, canaried, reversible, domain-aware rollout: sign+version the artifact; canary (1 node → 1%), bake, gate promotion on health/SLOs; progress 1%→10%→50%→100%, each gated, with automatic image-pinned rollback on regression; failure-domain-aware waves; drain-first for reboots; backward/forward-compatible APIs (many versions run at once). Same discipline as firmware (P08) and k8s upgrades (P06). A bad change caught at 1% is an annoyance; at 100% an outage. (P11 Ch. 6; Q3.)
Q10. What do you look for in a code review?
Priority: correctness (incl. failure paths) > security (injection, validation, secrets) > tests + a seam > operability (logs/metrics/errors) > style (let the linter win). Review = knowledge-sharing + mentoring. And every production incident should add a test/CI gate (RCA → gate) — the continuous improvement the JD names. (P11 Ch. 7; Q5.)
Quick-fire drills
- Debugging is a method: reproduce → observe → "what changed?" → hypothesize → bisect → fix → prevent.
- Tool↔symptom: gdb (state), strace (stuck), perf (slow), ASan/TSan (memory/race), eBPF (prod).
- timeouts + retry+backoff+jitter + circuit breaker + idempotency + reconciliation + bulkheads.
- Blameless RCA; 5 Whys; owned, dated action items.
- Management VLAN = hard boundary; tenants never reach it.
- Secure boot = enforce; measured boot = record; attestation = prove → gate trust.
- No hardware in CI → seam + emulators + HIL pyramid.
- Fleet rollout = canary + health gates + auto-rollback + domain-aware (same as firmware/k8s).
Interview Bank 05 — Behavioral & Staff Leadership
Phase 12. EBOM/specs, design reviews/ADRs, runbooks, mentorship, distributed-team communication, and STAR. Senior/Staff loops always include this; for this JD, communication and execution-leadership are explicit requirements, not extras.
Behavioral (STAR) — prepare these as full stories
Structure: Situation · Task · Action (you, specific) · Result (quantified + learning). Draw from your lab work across the curriculum.
B1. Tell me about the hardest bug you root-caused.
(The Phase 10 story.) A latent use-after-free in a close-to-hardware list that "worked" in normal
builds; found it under load with ASan (exact alloc+use sites) and confirmed in gdb; fixed
(unlink-before-free), added -fsanitize=address,undefined to CI and a regression test for list
mutation. Result: the class of crash can't recur; the team adopted sanitizers in CI. Learning: latent
memory bugs need tooling, not luck — and a fix isn't done until it's a gate.
B2. A production incident you handled and prevented from recurring.
(Phase 08/10.) Nodes rebooting under thermal load; OOB evidence (SEL/BERT + RAS parser) showed a DIMM with a rising correctable-ECC rate that climbed with temperature → predictive DIMM fault. Cordoned/ drained, RMA'd the DIMM, re-validated; added an alert on correctable-ECC rate. Result: turned an unplanned-outage class into planned maintenance. Learning: rate-not-count; remote diagnosis beats a truck roll.
B3. A cross-team / ODM delivery under ambiguity.
(Phase 03/12.) An interface ambiguity with the ODM slipped integration. I wrote a precise, testable interface spec + a conformance suite the ODM ran in CI. Result: integration time dropped and defects became unambiguous (a failing check, not a debate). Learning: in a multi-party build, the spec + conformance suite is the engineering.
B4. Mentoring / raising the bar.
(Phase 12.) Brought a new engineer from first-PR to owning the exporter via pairing and review-as- teaching, with a 30/60/90 plan. Result: they're now independent and mentor the next hire. Learning: success is the mentee not needing me and the team's knowledge rising (docs/runbooks), not me being a bottleneck.
B5. A design-review disagreement handled well.
(Phase 12.) Pushed back on synchronous scrape-time BMC calls (couples scrape latency to BMC health, hammers fragile BMCs) with a prototype of cached polling and the concrete failure mode. Result: adopted cached polling + link-following + cert validation; codified the standards in the review checklist. Learning: critique the design not the person; bring alternatives and data, not objections.
B6. A tradeoff under constraints.
(Phase 01.) A rack came in 10% over power budget. Options: refuse the deployment or power-cap to fit. I capped per-node to fit safely and made the performance cost explicit to stakeholders rather than silently dropping redundancy. Result: deployed safely, with the tradeoff documented. Learning: surface tradeoffs explicitly; don't hide them in a config.
Leadership / process questions
Q1. What goes in a software EBOM and why does it matter?
The versioned inventory of everything on a node/rack + interfaces: firmware components and baselines (signed), software + compatibility matrix, exact management interfaces (Redfish schemas/OEM extensions, sensor lists, error semantics), config baselines (secure-boot keys, VLAN layout), and provenance/ownership. It's the contract that keeps a multi-party build (us + Pegatron + ISVs) coherent; at commissioning I reconcile discovered reality against it and any mismatch is a finding. (P12 Ch. 2.)
Q2. How do you document an interface for an ODM to implement?
Precise + testable enough that they build it without me in the room: name the standard + deltas (resources, OEM extensions, sensor names/units, AllowableValues); specify behavior (state machines, error semantics, idempotency, timing); pair it with a conformance suite (executable expectations don't drift); version it; own it; run a defect-feedback loop and upstream fixes (OpenBMC). It's a contract between organizations — ambiguity costs weeks. (P12 Ch. 3.)
Q3. What makes a good runbook, and where do they come from?
Clear trigger (the alert/symptom) + goal; a precise checklist (commands + expected outputs + decision points) anyone can run at 3 a.m.; bounded risk (pre-checks, blast-radius notes, dry-run/confirm, rollback); links (dashboard, ADR, escalation). They come from incidents — every RCA with a manual fix leaves a runbook, which you then automate, then fold into the operator (self-healing). (P12 Ch. 5.)
Q4. Senior vs Staff, and how do you "drive implementation"?
Senior owns features; Staff owns outcomes across an area via leverage (standards, reviews, specs, runbooks, decisions, mentorship) more than personal commits. For this JD (architecture often defined elsewhere), driving implementation = take a defined design, break it down, sequence it, de-risk the integration seams early (Redfish/hardware), keep the distributed team aligned, and own the debugging when reality bites — while staying hands-on. Reading the role right (ambitious execution + local leadership, collaborative on direction) is itself maturity. (P12 Ch. 7.)
Q5. How do you communicate effectively in a distributed, multi-timezone team?
Async-first: decisions in durable docs/ADRs (not scrolling chat); lead with the decision/ask then context; make docs self-contained; state decision + consequence + honest limit; pick the right medium; minimize required-synchronous and rotate the painful hour fairly. Clear writing is the mechanism by which a distributed team functions — and it's an explicit JD requirement. (P12 Ch. 6.)
Closing-question prep (ask the interviewer)
- How is the work split with the architects/lead engineers across sites? (the JD's model)
- What does the ODM (Pegatron) collaboration look like day to day?
- What's the current state of the test/CI infrastructure for hardware-adjacent code?
- What does on-call look like across the distributed team, and how mature are the runbooks?
- What would "above and beyond" look like in the first 6–12 months for this role?
System Design — Rack Management at Fleet Scale
The design walkthroughs for this role. Each takes a single-rack capability (built in the phases) and scales it to a fleet, with the requirements-gathering, architecture, hard decisions/tradeoffs, failure modes, and operations an interviewer probes.
| Walkthrough | Prompt | Phases |
|---|---|---|
| 01 — Rack Management Control Plane | "Design the software that manages a fleet of AI racks." | 01, 03, 06, 13 |
| 02 — Unified Telemetry Pipeline | "Design fleet-wide telemetry for heterogeneous rack hardware." | 03, 07 |
| 03 — Fleet Firmware Update System | "Update firmware across 10,000 nodes without bricking the fleet." | 08, 06, 11 |
| 04 — Multi-Tenant Isolation & Secure Boot | "Design isolation/trust for a multi-tenant sovereign rack." | 09, 06 |
| 05 — Bare-Metal Provisioning Service | "Design a service to onboard racks from bare metal." | 05, 01, 12 |
How to run a system-design round
- Drive the requirements first — scale (how many racks/sites?), tenancy, workload mix, hardware heterogeneity, sovereignty/air-gap, SLOs. Don't design before you've scoped.
- Start from the single-rack design (the phases/capstone) and state explicitly what changes at scale — that's the value-add and what separates Senior/Staff.
- Name the hard decisions and the tradeoffs (the part interviewers grade): the driver abstraction, push vs poll, canary vs blue/green, soft vs hard tenancy, central vs proxy.
- Cover failure modes & operations — a flaky BMC, a bad firmware, a correlated PDU failure, a control-plane restart, fleet upgrades — with the reliability patterns (Phase 10) and observability (Phase 07).
- Be honest about limits — what you'd defer, what you'd measure, where you're uncertain. (Phase 12 communication.)
The recurring spine across all five: one data model, one reconcile loop, clean seams (the driver abstraction), and rollouts that are gated/canaried/reversible/failure-domain-aware.
System Design 01 — Rack Management Control Plane at Fleet Scale
"Design the software that manages a fleet of AI accelerator racks." Phases: 01 (model), 03 (drivers), 06 (reconcile), 13 (capstone), 07/08/09 (operate/maintain/secure).
1. Drive the requirements
- Scale: how many racks/nodes/sites? (Assume 10,000 nodes across several sites — multi-site is in the JD.)
- Hardware: single-vendor or heterogeneous (Redfish + IPMI + SNMP gear)? → driver abstraction.
- Workloads: tenant AI training/serving → orchestration integration (k8s/RKE2).
- Sovereignty: air-gapped/regulated sites? → RKE2, attestation, on-prem control plane.
- SLOs: time-to-ready for a new rack; node availability; control-plane availability.
2. Architecture
+------------------ Control Plane (per region/site) ------------------+
operators/CLI (P02) --> | API (desired state) | Inventory model (P01) | Reconcile loops (P06) |
| Provisioning (P05) | Firmware (P08) | Tenancy/secure cfg (P09) |
| Telemetry/alerting (P07) |
+-----------------------------------------------------------------------+
| | |
+--------------+ +--------------+ +--------------+
| Smart proxy | ... | Smart proxy | | Smart proxy | (near hardware)
| (DHCP/TFTP, | | DeviceDriver | | exporter |
| Redfish/IPMI/SNMP to BMC/PDU/CDU/switch — P03/P04) |
+--------------+ +--------------+ +--------------+
| management VLAN (isolated, P09)
[ racks: BMCs, PDUs, CDU, switches, nodes ]
3. The hard decisions (and the tradeoffs)
- One model + one reconcile loop (P01/P06): every node is an object with a desired state; per-node reconcile loops converge it (provision, firmware-baseline, attest, configure, validate). Tradeoff: a control loop is more to build than imperative scripts, but it's self-healing, idempotent, and resumable — essential at 10k nodes.
- The driver abstraction (P03): Redfish-first with IPMI/SNMP fallback behind one
DeviceDriver. Tradeoff: an abstraction tax + risk of a lowest-common-denominator API (mitigate with capability flags/OEM passthrough), vs. protocol quirks leaking everywhere. - Central control plane + smart proxies (P05): control logic central; DHCP/TFTP/device traffic via proxies near the hardware. Tradeoff: more moving parts, but it scales across sites and keeps the management plane reachable locally during a WAN blip — critical for multi-site.
- Sharding/partitioning: shard the control plane by rack/site; reconcile loops are per-node and parallel; a regional control plane per site (sovereign/air-gap friendly). Tradeoff: cross-shard views need aggregation.
- State store: a consistent store (etcd-like) for desired state + a time-series store for telemetry. Keep declared intent (EBOM) separate from discovered reality and reconcile (P01/P12).
4. Failure modes & operations
- Flaky BMC (P03/P10): driver retries + circuit breaker; the loop requeues — no stuck nodes, no retry storms.
- Bad node (P05/P08/P09): validation gate quarantines it (EBOM mismatch / failed attestation / RAS fault) — never handed to tenants; alert + runbook (P12).
- Correlated failure (P01): a PDU/ToR/CDU loss → blast-radius reasoning turns an alarm storm into one root cause; placement respected failure domains.
- Control-plane restart: idempotent + persisted state → no in-flight corruption (graceful shutdown, P02); the reconcile loop simply re-converges.
- Fleet operations: firmware/k8s upgrades as gated canary rollouts (P08/P11); cordon/drain (P06); observability + SLOs + cross-site on-call (P07/P12).
5. What changes at scale (the Staff answer)
- Telemetry federates (→ design 02); provisioning/firmware become progressive domain-aware rollouts (→ designs 03); tenancy/attestation scale (→ design 04). The single-rack design (the capstone) stays; you shard it, add proxies, and make every fleet action gated/reversible.
- Honest limits: cross-site consistency, the cardinality budget, and the size of the HIL/conformance matrix (P11) are the things I'd measure and bound first.
System Design 02 — Unified Telemetry Pipeline
"Design fleet-wide telemetry for heterogeneous rack hardware (Redfish/IPMI/SNMP/CDU)." Phases: 03 (drivers), 07 (observability), 01/04 (what to measure), 10 (debugging consumer).
1. Drive the requirements
- Scale: 10,000 nodes × dozens of sensors each (temps, power, fans, PCIe AER, CDU ΔT/flow/leak).
- Heterogeneity: Redfish nodes, IPMI BMCs, SNMP PDUs/switches, Modbus/SNMP CDUs.
- Consumers: dashboards, alerting, and debugging (the metrics→traces→logs loop, P10).
- Constraints: BMCs are slow/fragile (don't hammer them); cardinality must stay bounded.
2. Architecture
[BMC/PDU/CDU/switch] --Redfish/IPMI/SNMP/Modbus--> [Unified exporter (per site/proxy)] (P03 drivers)
| (poll on OWN schedule, cache, retry/timeout — not on every scrape)
v
/metrics (consistent names/labels) --scrape--> [Prometheus per shard]
| remote-write / federate
v
[Mimir/Thanos (long-term, global)] --> [Grafana]
structured logs (P02) -> [Loki] |
traces (OTel, P07) ----> [Tempo/Jaeger] +--> [Alertmanager -> page/Slack]
(all correlated by node/rack/trace_id)
3. The hard decisions (and the tradeoffs)
- Unified exporter via the driver abstraction (P03/P07): one consistent metric/label scheme
(
node_temp_celsiusregardless of source). Tradeoff: a mapping layer to maintain, vs. dashboards/ alerts that don't care about protocol. - Poll-on-own-schedule + cache, serve cached on scrape (P07): BMCs are fragile/slow, so the exporter polls them on its cadence with retries/timeouts and serves cached values to Prometheus. Tradeoff: staleness bounded by the poll interval, vs. hammering BMCs synchronously per scrape.
- Pull (Prometheus) + remote-write to a scalable backend: pull gives
up==0as a signal and easy testing; one Prometheus can't hold 10k×dozens of series, so shard per site and remote-write to Mimir/Thanos for the global, long-term view. Tradeoff: more infra, but it scales and survives a shard loss. - Events for state changes, polling for metrics (P07): Redfish EventService/SSE + SNMP traps for faults (low latency), scheduled polling for metrics and as the backstop (events get lost).
- Cardinality discipline (P07): bounded labels only (rack/node/sensor/severity); high-cardinality detail (request/serial/message) goes to logs/traces, never metric labels. This is the #1 thing that kills a fleet telemetry system.
4. What to alert on (P07/P01/P04/P08)
Symptoms, with for: + severity + runbook: node/exporter down (up==0), rack over power budget
(sum by(rack)(power) > derated), sustained accelerator overheat/throttle, CDU leak/ΔT anomaly, rising
PCIe AER rate, SLO breaches. Not single correctable errors (dashboard those). Alert on rates and
sustained conditions to avoid fatigue.
5. Failure modes & operations
- A BMC stops responding → the exporter's per-device
upmetric firesNodeDown; cached values go stale and are marked. - A telemetry shard dies → Mimir/Thanos retains history; another shard's scrape continues; alert on the shard.
- Alarm storm from a PDU loss (P01) → correlate by failure domain to one root cause, not 6 pages.
- Debugging consumer (P10): an SLO alert → traces in the window → logs for the failing span → RAS/telemetry for the hardware — the unified pipeline is what makes that loop possible.
6. Honest limits / what I'd measure
The cardinality budget (series count per node × fleet), the poll interval vs. staleness tradeoff per metric class, and the cost of long-term storage. I'd start with a tight metric set and grow it under review (every new metric reviewed for label explosions).
System Design 03 — Fleet Firmware Update System
"Update firmware across 10,000 nodes without bricking the fleet." Phases: 08 (firmware/RAS), 06 (drain), 11 (canary/rollback), 07 (gates), 01 (failure domains), 12 (EBOM).
1. Drive the requirements
- Scope: which components (BIOS/BMC/NIC/accelerator/PSU/switch)? Each independently versioned.
- Constraints: can't reboot nodes running tenant work without draining; can't brick the BMC (lose OOB); some components have A/B banks, some don't; air-gapped sites need local artifact mirrors.
- SLOs: rollout duration, max nodes-down at once, zero fleet-wide bricks.
2. Architecture
[Firmware repo + signing] --signed images--> [Update controller] -- desired baseline (EBOM, P12)
| |
| plans WAVES (canary 1 -> 1% -> 10% -> rest)
| failure-domain-aware (P01), dependency-ordered
v v
[artifact mirror per site] <-- pull -- per-node: drain (P06) -> Redfish UpdateService/Task (P08)
-> A/B stage+activate -> verify after -> commit/ROLLBACK
-> health/SLO gate (P07) -> promote or HALT+rollback
3. The hard decisions (and the tradeoffs)
- Per-node safe update (P08): verify (checksum+signature+version+anti-rollback) → flash inactive A/B bank → activate → verify after (version + health/PCIe — P04) → commit or roll back. Tradeoff: A/B needs hardware support; where absent, the risk and caution rise (stage to a recovery path).
- Canary + progressive, failure-domain-aware (P11/P01): 1 node → 1% → 10% → rest, each gated on health/SLOs (P07), auto-rollback to the pinned prior bank on regression, never a whole PDU/ToR at once. Tradeoff: slower rollout vs. bounded blast radius. A bad image at 1% is an annoyance; at 100% an outage.
- Drain-first (P06): cordon/drain tenant work before any reboot. Tradeoff: capacity dip during the window; respect PodDisruptionBudgets.
- Dependency ordering (P08): e.g., BMC before BIOS if the new BIOS needs new BMC support; encode dependencies, don't flash blindly.
- BMC update specially (P08): the riskiest target (brick it → lose OOB). Extra-conservative canary, recovery path validated, A/B mandatory.
- Air-gap (P06/P09): local signed-artifact mirrors per site; no internet dependency mid-rollout.
4. Observability & control
- Track
firmware_version{component,version}across the fleet (P07) → rollout progress + drift are visible; version-skew alerts. - Each wave's gate reads health/SLOs and RAS (P08) — a spike in crashes/AER halts the rollout.
- A kill-switch: halt + roll back the whole rollout on a fleet-level regression.
5. Failure modes & operations
- Bad firmware → caught at the 1% canary; auto-rollback to the prior A/B bank; rollout halts; RCA (P10) → tighten the canary criteria + add the failure signature to pre-promotion checks + expand the HIL/conformance matrix (P11) + feed the defect to the ODM (P12).
- Power loss mid-flash (P08) → A/B ensures a bootable bank survives; resume safely.
- A node that won't come back → quarantine, OOB recovery (virtual media — P04), or RMA.
- Partial fleet on a new version → backward-compatible interfaces (P11) so mixed versions interoperate during the (possibly long) rollout.
6. Honest limits
The conservative path makes large rollouts slow (days/weeks for the whole fleet) — that's the deliberate tradeoff for "zero fleet-wide bricks." I'd measure rollout duration vs. blast radius and tune wave sizes/bake times, and invest in the A/B + recovery path for components that lack it.
System Design 04 — Multi-Tenant Isolation & Secure Boot
"Design isolation and trust for a multi-tenant, sovereign rack platform." Phases: 09 (network/boot/ tenancy), 06 (k8s tenancy), 04 (accelerator partitioning), 01 (planes/domains).
1. Drive the requirements
- Tenancy: trusted internal tenants or untrusted external? Paid vs free tiers? → soft vs hard isolation.
- Sovereignty: regulated/air-gapped (a Kingdom data center)? → provable trust (attestation), RKE2, on-prem keys.
- Surface: network, compute, accelerators, storage, and the management plane.
- Threat model: a malicious tenant, a compromised node, an exposed BMC.
2. Architecture (defense in depth)
NETWORK: VLAN-per-tenant (802.1Q) | mgmt VLAN = HARD boundary (control-plane only)
host: netns/veth/bridge + ACLs ; QoS (HTB) reserved mgmt class (P09)
COMPUTE: namespaces + RBAC + ResourceQuota + NetworkPolicy + PodSecurity (P06)
ACCEL: no co-placement of competing tenants on one device; MIG-style partitioning (P04)
TRUST: secure boot (enforce) + measured boot (TPM PCRs) + REMOTE ATTESTATION (prove) (P09)
-> a node earns tenant work/keys ONLY by attesting a known-good state
OPS: least-privilege control plane, mTLS, secrets mgmt, audit (P07/P12), RKE2 (P06)
3. The hard decisions (and the tradeoffs)
- Soft vs hard isolation: namespaces+quotas+RBAC+NetworkPolicy (soft) for trusted tenants; separate nodes/clusters or strong sandboxing (hard) for untrusted. Tradeoff: utilization vs. isolation strength — never co-place competing/untrusted tenants on one physical accelerator.
- Management plane as a hard boundary (P09): its own VLAN, control-plane-only, never tenant/ internet-facing. Non-negotiable — even a misconfigured ACL must not expose it (an exposed BMC = full compromise). Tradeoff: none worth taking; this is a hard invariant.
- QoS with a reserved management class (P09): HTB guarantees per tenant + a reserved mgmt slice so a tenant DDoS can't make the rack unmanageable. Invariant: sum of guarantees ≤ link capacity.
- Attestation-gated trust (P09): a node joins/gets keys/runs tenant work only after a valid PCR quote against known-good values. Tradeoff: operational complexity (golden measurements, key management) vs. provable trust — required for sovereign deploys.
- Secure boot vs measured boot: enforce and record — secure boot stops untrusted code; measured boot + attestation prove the state even where enforcement can't reach (and enable sealing secrets to PCR state).
4. Failure modes & operations
- Malicious tenant tries to reach another tenant/management → blocked by VLAN/netns + NetworkPolicy
- the hard mgmt boundary; QoS prevents bandwidth starvation.
- Compromised node (bad firmware/OS) → fails attestation → quarantined, never trusted with tenant work/keys; management isolation limits its reach.
- Exposed BMC → prevented by the hard mgmt-VLAN boundary; audited; the classic catastrophe avoided.
- Key compromise / revocation → dbx revokes a vulnerable bootloader (P09); rotate keys; re-attest.
- Operations: per-tenant audit (P07/P12), incident response (P10), and a written threat model that shows each layer fails safe — what a sovereign customer's security review demands.
5. What changes at scale / honest limits
Isolation must hold across thousands of nodes and tenants: attestation infra (golden measurements per firmware/OS combo — and they change with firmware, P08), VLAN/IP management, and key lifecycle become real operational systems. The limit I'd call out: hard isolation costs utilization, and attestation adds operational burden — I'd tier it (hard isolation + attestation for untrusted/sovereign workloads, soft for trusted internal ones) and document the threat model and residual risks explicitly (P12).
System Design 05 — Bare-Metal Provisioning Service
"Design a service to onboard racks from bare metal to running tenant workloads." Phases: 05 (provisioning/CM), 01 (commissioning/model), 12 (EBOM), 06 (orchestrator handoff), 09 (attestation).
1. Drive the requirements
- Scale & cadence: how many racks, how often? (Continuous onboarding — you commission racks forever, not once.)
- Inputs: a rack arrives from the ODM (Pegatron) as bare metal; you have the EBOM (P12).
- Outputs: every node provisioned to baseline, validated, attested, joined to the orchestrator.
- Constraints: ~3% of nodes fail somewhere mid-flow; multi-site; some air-gapped.
- SLOs: time-to-ready per rack; % nodes auto-onboarded without human touch.
2. Architecture
[Provisioning API: desired rack state] --> [Per-node STATE MACHINE controller (P05), persisted]
| | discover -> EBOM reconcile (P12) -> firmware
| | baseline (P08) -> attest (P09) -> OS install
| | -> configure (CM, P05) -> validate -> join (P06)
v v
[Smart proxies per site]: DHCP/TFTP/HTTP(iPXE) + installer + Redfish/IPMI to BMCs (P03/P04)
| (control central, device traffic local — scales multi-site)
v
[racks: BMCs power/PXE/virtual-media; nodes install OS]
3. The hard decisions (and the tradeoffs)
- Persisted per-node state machine, not a script (P05): every step idempotent + resumable, so a node failing at "configure" resumes there, not at "reinstall the rack." Tradeoff: more to build vs. a 3%-failure-rate being a non-event at scale.
- Validation gates that quarantine (P05/P01): a node advances to ready only after passing checks (EBOM reconciliation, firmware baseline, PCIe link width, sensors, attestation); failures are quarantined for RCA (P10), not shipped to tenants. Tradeoff: stricter gates → more quarantines, but no bad nodes reach tenants.
- Discovery + EBOM reconciliation (P01/P12): a discovery image inventories hardware and reconciles against the EBOM agreed with the ODM; mismatches are findings raised to the partner. Tradeoff: upfront EBOM discipline vs. "works in the lab, not in Riyadh."
- Image-based + cloud-init vs. full CM: a thin golden image + cloud-init for determinism and easy reimage-as-rollback, then CM (Ansible) for the last mile + drift correction. Tradeoff: image size/ rebuilds vs. per-node config flexibility — blend them.
- Smart proxies (P05): DHCP/TFTP/HTTP/installer near the hardware, control logic central. Tradeoff: more components vs. multi-site scale and WAN resilience.
- Boot security (P09): signed PXE/iPXE chain (Secure Boot) + attestation gate before tenant trust. Tradeoff: more setup vs. provable trust (required for sovereign sites).
4. Observability & control
- Every state transition emits structured telemetry (P07) → watch hundreds of nodes onboard, SLO the time-to-ready, and triage the stuck/quarantined ones.
- Declarative API (desired rack state) so it composes with Terraform/IaC (P05) and the orchestrator.
5. Failure modes & operations
- Node fails mid-flow → resumes from its last good state; no rack-wide restart.
- EBOM mismatch (missing accelerator / firmware drift) → quarantine + a finding to the ODM (P12).
- Won't POST → the Phase 12 runbook (OOB: SEL/BERT, RAS, reset, RMA if a FRU fault).
- Air-gapped site → local mirrors of images/artifacts; no internet dependency.
- Continuous onboarding → it's a pipeline, run forever, observable and resumable — the Staff value is making it repeatable and automated, not a one-off heroics.
6. Honest limits / what I'd measure
Time-to-ready and the auto-onboard rate (% with no human touch) are the headline SLOs; the quarantine rate and its top causes tell me where to harden (EBOM gaps with the ODM, a flaky firmware step). I'd start strict (quarantine aggressively) and loosen gates only as confidence and conformance (P11) grow.