F20-BUG2 + F9-reporting: /disks surfaces wipe_durable_id (gate scheme) + guest_attached

F20-BUG2: the /disks list only carried DurableID in the uuid: scheme (for /disks/assign),
but the wipe gate resolves devices in the byid:/byuuid: scheme — so a customer confirming a
wipe with the advertised id was refused (binding_mismatch). Added a shared s.deviceDurableID
seam used by BOTH handleDisks (new DiskInfo.WipeDurableID) and the format gate, so the id the
customer copies from the list is exactly the id the gate accepts. DurableID (uuid:) is unchanged
(still feeds assign).

F9 (reporting half): added DiskInfo.GuestAttached — whether the drive's namespace is actually
bound into THIS guest's config (guestBoundPaths), distinct from mere host presence (State). This
is the signal whose absence made the HDD look available when it wasn't attached, and resolves the
F2 hdd_configured-vs-/disks disagreement.

Tests: wipe_durable_id is the gate scheme + distinct from uuid:; the list's wipe id matches the
gate's device-id binding (no mismatch); guest_attached true iff bound into the guest.
This commit is contained in:
2026-06-14 15:00:56 +02:00
parent b566fbe57f
commit a2a76e7624
4 changed files with 177 additions and 6 deletions
+3
View File
@@ -118,6 +118,9 @@ func newDiskServer(t *testing.T, d *fakeDiskOps, g *fakeGate, sv StorageView, gl
// device the format tests use; antiRetargetResolve itself is covered directly
// in wipe_reresolve_test.go.
srv.reresolveWipe = func(_ context.Context, _ string) (string, error) { return "/dev/sdb", nil }
// F20-BUG2: the wipe id derivation hits /dev/disk/by-* in production; stub it deterministically so
// both the /disks list and the gate (which share this seam) resolve the same id in tests.
srv.deviceDurableID = func(device string) (string, error) { return "byid:wwn-" + strings.TrimPrefix(device, "/dev/"), nil }
return srv.Handler()
}