agent v0.87.0: SystemDisks device-mapper walk — legacy-boot hosts get a working drive wizard (IA finding 2, MEDIUM)
Operator ruling 2026-07-13: walk the root's backing device through /sys/block/<dev>/slaves recursively down to physical disks (dm AND md; topology, never VG names); those + any mounted-ESP holder are system; the all-system fail-safe returns to being the WALK-FAILURE error case only. SAFETY DIRECTION: a root-backing disk classified candidate is made impossible — per-branch conservatism (any unresolvable slave fails the WHOLE walk -> ok=false -> the unchanged all-system path). - physicalDisksOf/walkSlaves in role.go (symlink canon -> wholeDiskOf fast path -> recursive slaves walk; cycle/depth guard; non-/dev sources unwalkable) - HostReader.BlockSlaves(name) — the ONE new seam method; ProcHostReader reads /sys/block/<name>/slaves; all four test fakes mirror it - role_walk_test.go: signature table (root-backing disk ALWAYS system across legacy-LVM / md-raid / EFI+raw / EFI+LVM / nested dm-on-md — NEVER weaken) + dead-wizard-lives + dangling-slave fail-safe (real sysKnown=false path) + cycle + empty-slaves; red-proofs A/B/D run->fail->revert (recorded in REPORT) - §3 spike transcripts (drill legacy: dm-1->sda3->sda; felhom-pve: ESP+walk agree on sda -> byte-identical regression); caller audit: none relied on all-system as a feature - format/mkfs paths, data-bearing guards, wizard UI untouched
This commit is contained in:
@@ -90,11 +90,12 @@ func (f fakeGuestList) ListLXC(context.Context) ([]proxmox.Guest, error) { retur
|
||||
// a different whole-disk (e.g. /dev/sdb1) classifies as user-data without touching the real host.
|
||||
type fakeHostReader struct{ mounts []storage.Mount }
|
||||
|
||||
func (f fakeHostReader) Mounts() ([]storage.Mount, error) { return f.mounts, nil }
|
||||
func (f fakeHostReader) ResolveUUID(string) (string, bool) { return "", false }
|
||||
func (f fakeHostReader) DeviceExists(string) bool { return true }
|
||||
func (f fakeHostReader) Rotational(string) (bool, bool) { return false, false }
|
||||
func (f fakeHostReader) Removable(string) (bool, bool) { return false, false }
|
||||
func (f fakeHostReader) Mounts() ([]storage.Mount, error) { return f.mounts, nil }
|
||||
func (f fakeHostReader) ResolveUUID(string) (string, bool) { return "", false }
|
||||
func (f fakeHostReader) DeviceExists(string) bool { return true }
|
||||
func (f fakeHostReader) Rotational(string) (bool, bool) { return false, false }
|
||||
func (f fakeHostReader) Removable(string) (bool, bool) { return false, false }
|
||||
func (f fakeHostReader) BlockSlaves(string) ([]string, bool) { return nil, false }
|
||||
|
||||
// sysOnSDA is the default system-disk fixture (root on /dev/sda) used by the disk-server test helpers.
|
||||
func sysOnSDA() fakeHostReader {
|
||||
|
||||
@@ -18,9 +18,10 @@ func (u uuidHostReader) ResolveUUID(dev string) (string, bool) {
|
||||
v, ok := u.uuids[dev]
|
||||
return v, ok
|
||||
}
|
||||
func (u uuidHostReader) DeviceExists(string) bool { return true }
|
||||
func (u uuidHostReader) Rotational(string) (bool, bool) { return false, false }
|
||||
func (u uuidHostReader) Removable(string) (bool, bool) { return false, false }
|
||||
func (u uuidHostReader) DeviceExists(string) bool { return true }
|
||||
func (u uuidHostReader) Rotational(string) (bool, bool) { return false, false }
|
||||
func (u uuidHostReader) Removable(string) (bool, bool) { return false, false }
|
||||
func (u uuidHostReader) BlockSlaves(string) ([]string, bool) { return nil, false }
|
||||
|
||||
// Impl-2b: a RAW enrolled drive is not a PVE storage (absent from Observe), so its durable-id must be
|
||||
// resolved from the mount table's device fs-UUID. RED-PROOF: with the Observe-only resolution (before
|
||||
|
||||
@@ -33,6 +33,7 @@ func (f *f2HostReader) ResolveUUID(dev string) (string, bool) { u, ok := f.uuids
|
||||
func (f *f2HostReader) DeviceExists(string) bool { return true }
|
||||
func (f *f2HostReader) Rotational(string) (bool, bool) { return false, false }
|
||||
func (f *f2HostReader) Removable(string) (bool, bool) { return false, false }
|
||||
func (f *f2HostReader) BlockSlaves(string) ([]string, bool) { return nil, false }
|
||||
func (f *f2HostReader) mountsCalls() int { f.mu.Lock(); defer f.mu.Unlock(); return f.calls }
|
||||
|
||||
// errStorage is a StorageView whose Observe always fails — for the Observe-error fail-safe (edge C3).
|
||||
@@ -67,8 +68,8 @@ func TestRoleForMountPath_F2_BindMountedUserData_Ejectable(t *testing.T) {
|
||||
}}
|
||||
host := &f2HostReader{
|
||||
mounts: []storage.Mount{
|
||||
{Device: "/dev/sda1", MountPoint: "/"}, // system disk
|
||||
{Device: "/dev/sdb", MountPoint: "/mnt/teszt_enroll"}, // the raw enrolled user-data drive
|
||||
{Device: "/dev/sda1", MountPoint: "/"}, // system disk
|
||||
{Device: "/dev/sdb", MountPoint: "/mnt/teszt_enroll"}, // the raw enrolled user-data drive
|
||||
},
|
||||
uuids: map[string]string{"/dev/sdb": "f2236136-ced7"},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user