Skip to content

firmware-update: reports success on a no-op flash (start POST not verified) #94

Closed

Description

@DustinTrap

Summary

GLKVMDriver.apply_firmware_update reports a flash succeeded whenever POST /api/upgrade/start returns without an HTTP error — it never verifies a flash actually started. This produced a misleading "Firmware flash started" on the first-ever real-hardware run, where nothing was flashed.

Follow-up finding against #92 (gated remote-flash capability).

Evidence (live, 2026-07-03, homelab2 / GL RM1PE @ 10.0.1.20, firmware V1.5.1 release2)

kvm-pilot firmware-update --profile homelab2 --execute --i-have-physical-access --yes printed:

INFO  kvm_pilot.safety: Executing destructive op 'firmware.flash' ...
WARNING kvm_pilot.glkvm: Starting firmware flash on 10.0.1.20 ...
Firmware flash started. The device will reboot and may be unreachable for several minutes ...

Observed reality:

  • Device never rebooted — healthcheck polled every 20s for ~5 min, firmware=V1.5.1 release2 on every sample, API reachable throughout.
  • Operator confirmed device LEDs solid (no flash activity).
  • Fresh firmware-check afterward: still installed V1.5.1 release2.

So POST /api/upgrade/start returned a non-error response and the CLI declared success, but no flash occurred.

Root cause

src/kvm_pilot/drivers/pikvm.py:249-250:

result = self._http.post("/api/upgrade/start", long_timeout=1800, retry=False)
return {"sent": True, "dry_run": False, "plan": plan, "result": result}

sent: True is returned on any non-raising POST. A 2xx (or any body the transport doesn't treat as an error) is taken as "flashing," with no confirmation that the device actually entered an upgrade state. Compounded by the fact that the /api/upgrade/* request bodies are provisional/undocumented (see companion issue) — the call can be accepted and no-op.

Proposed fix

After issuing start, verify the flash actually began before returning success. Options, cheapest first:

  • Poll GET /api/upgrade/status for a state transition into an in-progress/flashing state within a short window; if it never transitions, return sent: False with a clear "device did not enter upgrade state" error.
  • Fall back to detecting the expected control-channel drop / reboot (the driver already documents that the channel drops mid-flash) as corroboration.
  • Surface the actual start response body in the result so operators can see what the device returned.

Tie into the harness work (epic under #1): "firmware-update passes" must require an observed effect (version actually changed), not merely a returned call.

Acceptance

  • A start that does not put the device into an upgrade state returns sent: False and a descriptive error, not "flash started."
  • Unit test with a mocked transport where start 200s but status never flips → apply_firmware_update reports failure.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions