agent v0.37.0: host-reboot remount re-resolves enrolled drives by fs-UUID
TASK A — close out the reboot story (agent half). Root cause (pinned live on felhom-pve): an enrolled .mount unit left `disabled` by a prior detach never auto-mounts at boot, and kernel re-enumeration can move a drive's node (/dev/sdb->sdc). Fix re-asserts every enrolled mount by filesystem UUID at startup + on the periodic tick. - ResolveStorageDevice: resolve uuid:<fs-uuid> -> current /dev node via /dev/disk/by-uuid (never a cached node); errors if absent. - parseFelhomMountUnit: pure inverse of renderMountUnit (marker-gated). - (*SudoHostOps).ReassertEnrolledMounts: re-run EnsureMount (enable --now) for any enrolled unit not in /proc/mounts; idempotent, skips mounted/absent. - main.go: runs before ReassertGuestBinds at startup + on the 20s tick. - tests (Linux, seam=device resolution): letter-move tolerated (sdb->sdc) + red-proof companion, absent/scheme rejection, render->parse round-trip. TASK A2 verdict: enrolling a NEW drive does NOT need an LXC restart — the path lands on the live AttachDrive (shared parent, named live slots), not RebootGuest. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -73,6 +73,24 @@ func ResolveDurableDevice(durableID string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// ResolveStorageDevice resolves an enrolled STORAGE durable-id (the `uuid:<fs-uuid>` scheme that
|
||||
// deriveDurableID emits for usb/local-dir drives) to its CURRENT backing /dev path by re-scanning
|
||||
// /dev/disk/by-uuid. This is the load-bearing host-reboot fix: kernel re-enumeration can move a drive
|
||||
// from /dev/sdb to /dev/sdc, so a remount MUST re-resolve by UUID (never trust a remembered node) — the
|
||||
// reshuffle is then a no-op. Errors if the UUID no longer resolves (the drive is genuinely absent), so a
|
||||
// caller can skip re-mounting a gone drive instead of failing on a stale node.
|
||||
func ResolveStorageDevice(durableID string) (string, error) {
|
||||
const scheme = "uuid:"
|
||||
if !strings.HasPrefix(durableID, scheme) {
|
||||
return "", fmt.Errorf("storage: durable id %q is not the uuid: scheme — cannot resolve by filesystem UUID", durableID)
|
||||
}
|
||||
uuid := strings.TrimPrefix(durableID, scheme)
|
||||
if !safeLinkName(uuid) {
|
||||
return "", fmt.Errorf("storage: unsafe durable id %q", durableID)
|
||||
}
|
||||
return filepath.EvalSymlinks(filepath.Join(devDiskRoot, "by-uuid", uuid))
|
||||
}
|
||||
|
||||
// bestByIDLink returns the highest-priority /dev/disk/by-id link name whose target is `device`
|
||||
// (already symlink-resolved), or "" if none.
|
||||
func bestByIDLink(device string) string {
|
||||
|
||||
Reference in New Issue
Block a user