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.md Ch. 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 437XR1138R2not 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:

  1. Why follow links instead of constructing /redfish/v1/Systems/1/...?
  2. Which resource holds power state, which holds temperature, and which holds BMC firmware?

1. What the client does (read RedfishClient)

OperationMethodPath discipline
first_system()GET ×3root → Systems collection → first member (all by link)
power_for() / thermal_for()GETSystem → Links.ChassisPower/Thermal (by link)
reset()POSTthe action's advertised target; server validates AllowableValues
subscribe()POSTEventService/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)

  1. Run it against a real Redfish service. Install the DMTF mockup server (pip install redfish-mockup-server or clone DMTF/Redfish-Mockup-Server) or OpenBMC's sushy-emulator, start it, and point RedfishClient(base) at it. Your client should work unchanged — that's the payoff of following links.
  2. Tasks: add an UpdateService SimpleUpdate 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.
  3. Session auth: add a SessionService to the mock; have the client POST credentials, capture the X-Auth-Token, send it on every request, and DELETE the session at the end. Then add HTTPS with certificate validation (and a test that rejects a bad cert).
  4. $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.
  5. Fold into the driver abstraction: make RedfishClient implement the DeviceDriver interface from WARMUP Ch. 8 so it's swappable with the IPMI/SNMP drivers from Lab 02.

4. Common pitfalls

  1. Hardcoding paths/redfish/v1/Systems/1 works on one vendor and 404s on the next (IDs differ: 1, System.Embedded.1, a UUID). Follow @odata.id.
  2. Assuming fields exist — real BMCs omit optional fields and add Oem blocks. Use .get() with defaults; validate presence.
  3. Blocking on async operations — a firmware update returns a Task, not a result. Poll it; don't wait for a synchronous body.
  4. Disabling TLS verification — the single most common Redfish security bug. Validate the certificate; pin or trust the management CA.
  5. 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.