agent v0.49.0: reboot-during-backup stale-lock recovery (F2-b) + shared-parent script redeploy fix (F2-a)

F2-b: at startup, recover a guest left with a stale vzdump lock by a
reboot-during-backup — pct unlock -> delete dangling vzdump snapshot ->
start iff onboot, guarded by a no-vzdump-running invariant (fail-safe).
New internal/localapi/stalelock.go; proxmox GuestConfig.Lock()/OnBoot(),
ListSnapshots, ListRunningTasks, Snapshot type. New narrow sudoers grant
FELHOM_STALELOCK (pct unlock) + Critical capability stalelock-unlock.

F2-a: EnsureSharedParent only redeployed the boot script when the UNIT
differed, so the v0.36.6 make-private fix never reached hosts whose unit
was current -> /mnt/felhom-drives stayed in root's shared:1 and doubled
every drive bind. New sharedParentInstallStale compares BOTH script and
unit. Boot-time-only; never churns the live mount.

Both root causes confirmed live on felhom-pve before fixing. Green gate
(build/vet/test) all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162BnMpUXscPsUB1cU8Tr6K
This commit is contained in:
2026-06-30 09:00:32 +02:00
parent 81954c3d2a
commit 6e38e2f921
12 changed files with 743 additions and 32 deletions
+18
View File
@@ -54,6 +54,24 @@ func (c *Client) GuestConfig(ctx context.Context, vmid int) (GuestConfig, error)
return cfg, c.get(ctx, path, &cfg)
}
// ListSnapshots returns GET /nodes/{node}/lxc/{vmid}/snapshot (the guest's snapshots, including the
// synthetic "current"). The startup stale-lock recovery uses it to find a dangling "vzdump" snapshot
// left by an interrupted snapshot-mode backup.
func (c *Client) ListSnapshots(ctx context.Context, vmid int) ([]Snapshot, error) {
var ss []Snapshot
path := fmt.Sprintf("/nodes/%s/lxc/%d/snapshot", c.node, vmid)
return ss, c.get(ctx, path, &ss)
}
// ListRunningTasks returns the node's currently-running tasks (GET /nodes/{node}/tasks?running=1).
// The startup stale-lock recovery uses it as the load-bearing safety guard: a backup lock is cleared
// ONLY when no vzdump task is genuinely in-flight for the guest (an agent restart while a real backup
// runs must never clear the live lock).
func (c *Client) ListRunningTasks(ctx context.Context) ([]TaskStatus, error) {
var ts []TaskStatus
return ts, c.get(ctx, "/nodes/"+c.node+"/tasks?running=1", &ts)
}
// ListStorage returns GET /storage (cluster-wide storage definitions).
func (c *Client) ListStorage(ctx context.Context) ([]Storage, error) {
var ss []Storage