R-106 + R-109: the DR recipe records the resolved namespace and names the backup target (v0.118.0)

Both defects were live on both demo boxes: the recipe said namespace "root" while
storage.cfg said demo-felhom/demo-hp, and it never named which of two content=backup
dir storages holds the local archives.

R-106: the namespace came from the listed snapshot, but PBS omits `ns` per item once
the list is namespace-scoped, so it was always empty and normalised to "root". It now
resolves from the pbs STORAGE (storage.cfg's `namespace`) — the same field vzdump makes
PVE read, so the recipe cannot disagree with the backup.

R-109: backup_target resolves from the primary tier of cfg.Backup.BackupTiers(), the
function the scheduler consults, and carries the mountpoint that separates /mnt/hdd_1
from /var/lib/vz. The resolver reports the tier IN EFFECT (daemon-start config), not
agent.json on disk — a target move rewrites the file and deliberately does not restart.

Unresolvable is recorded as unresolvable: resolved|unknown plus a distinct reason,
never a default, an empty string, or a placeholder.

Needs hub v0.83.0 — AssembleDRRecipe allow-lists top-level keys, so backup_target
would otherwise be stored intact and dropped before any operator saw it.

9 tests, 4 red-proofs (each mutation asserted to have landed). Suite rc=0, 29 ok.
This commit is contained in:
2026-07-30 13:11:08 +02:00
parent 1913e12031
commit 1c8a67eece
13 changed files with 670 additions and 103 deletions
+8 -3
View File
@@ -242,9 +242,14 @@ func (o *Observer) build(s proxmox.Storage, mounts []Mount) observed {
// where it lives; it identifies nothing but itself, and it is not used for device or UUID
// resolution anywhere. MountPath stays empty when the mount is gone, which is the truth.
ConfigPath: s.Path,
ClassHint: classHint,
Role: "", // hub-owned; not derivable from a Proxmox def (slice 10)
Smart: hub.SmartSummary{Health: hub.SmartUnknown},
// R-106: the CONFIGURED PBS namespace, carried verbatim from storage.cfg. Empty for every
// non-pbs storage (Proxmox only emits it on pbs), and empty for a pbs storage in the root
// namespace — the DR recipe distinguishes those two cases by the storage's TYPE, never by
// guessing from this string.
PBSNamespace: s.Namespace,
ClassHint: classHint,
Role: "", // hub-owned; not derivable from a Proxmox def (slice 10)
Smart: hub.SmartSummary{Health: hub.SmartUnknown},
}
// Thin-pool DATA fill: surfaced prominently for lvmthin (metadata fill is Phase B/lvs).