From 475bdce7e43a377816129c683cc772f458d55fe1 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Sun, 4 Oct 2026 08:52:35 +0200 Subject: [PATCH] DR bring-up refuses beside a live original (R-834): source guest present, drives bind, or unreadable config The DR route keeps onboot 1, binds the real drives and starts the guest: right on a replaced host, a second box on the same drives beside a live original. The restore-test's no-host-bind half is now pinned too (measured safe live on demo-hp 2026-10-04). Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS --- internal/reconcile/bringup.go | 57 +++++++++ internal/reconcile/bringup_beside_test.go | 138 ++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 internal/reconcile/bringup_beside_test.go diff --git a/internal/reconcile/bringup.go b/internal/reconcile/bringup.go index adeac4e..30af2de 100644 --- a/internal/reconcile/bringup.go +++ b/internal/reconcile/bringup.go @@ -302,6 +302,19 @@ func (e *Engine) runBringUp(ctx context.Context, spec BringUpSpec, res *BringUpR } } + // R-834: DR keeps the archive's `onboot: 1`, binds the host's REAL drives (4d) and STARTS the guest + // — right on a replaced host, where the original is gone. Beside a LIVE original it would be a + // second controller for the same household on the same drives. So DR refuses when this host + // still carries the original (the archive's source VMID) or any guest that binds the drives. + // A copy beside the original is the restore-test's job (onboot=0, throwaway stand-ins, torn + // down) or the runbook's beside-restore. Pinned by TestRunBringUp_DRRefusesBesideALiveOriginal. + if spec.Mode == ModeDRGuestLoss { + if why := e.liveOriginalBeside(ctx, lxc, spec.Archive); why != "" { + res.Err = fmt.Errorf("reconcile: dr bring-up refused: %s — a DR restore beside a live original would run two boxes on the same drives (R-834)", why) + return + } + } + base := JournalEntry{OpID: e.bringUpOpID(spec.VMID), VMID: spec.VMID, Kind: bringUpKind, Rollback: true} // OWN the rollback BEFORE any mutation. From here a crash leaves an in-flight Rollback @@ -734,3 +747,47 @@ func net0MAC(cfg proxmox.GuestConfig) string { } return "" } + +// archiveSourceVMID reads the source guest's VMID from a backup volid: a vzdump file +// (`…/vzdump-lxc--.tar.zst`) or a PBS snapshot (`…:backup/ct//