Files
felhom.eu/scripts/felhom-restore-beside.sh
T
2026-10-04 08:56:56 +02:00

61 lines
3.1 KiB
Bash
Executable File

#!/bin/bash
# felhom-restore-beside.sh — restore a whole-guest archive BESIDE a live original, safely (R-834).
#
# Run as root on a Proxmox host. The restored guest is for READING (pct mount, a file copy, a check):
# - it is created with onboot 0, so a host reboot never starts it;
# - every mpN that binds a HOST path (mp8 = the household's real drives, mp9 = the original guest's
# bootstrap) is removed before anything can start it;
# - every NIC is set link_down=1 (the archive keeps the original's MAC and island address);
# - it is NEVER started by this script.
# The script then reads the config back and fails loudly if any of that is not true.
#
# NOT for a replaced host where the original is gone — there the binds are right; use the agent's DR
# bring-up or RUNBOOK-manual-guest-restore.md §3.
#
# Usage: felhom-restore-beside.sh <scratch-vmid> <volid> <storage>
# e.g. felhom-restore-beside.sh 9299 tmp-dooplex-copy:backup/ct/9201/2026-10-03T19:00:00Z nvme-scratch
#
# Test: scripts/test_felhom_restore_beside.py (a fake pct on PATH).
set -euo pipefail
die() { echo "restore-beside: REFUSED: $*" >&2; exit 1; }
log() { echo "restore-beside: $*"; }
[ $# -eq 3 ] || die "usage: $0 <scratch-vmid> <volid> <storage>"
vmid=$1 volid=$2 storage=$3
[[ "$vmid" =~ ^[0-9]+$ ]] || die "vmid '$vmid' is not a number"
# The restore-test band and the standing scratch are the agent's; a customer guest is never a target.
if [ "$vmid" -ge 990000 ] && [ "$vmid" -le 990009 ]; then die "vmid $vmid is the agent's restore-test band"; fi
[ "$vmid" != 9999 ] || die "vmid 9999 is the agent's standing scratch"
if pct status "$vmid" >/dev/null 2>&1; then die "vmid $vmid already exists — this script never restores over a guest"; fi
log "restoring $volid -> $vmid on $storage with onboot 0 (never started)"
pct restore "$vmid" "$volid" --storage "$storage" --unprivileged 1 --onboot 0
# Strip host-path binds and take the NICs down, BEFORE anything else can touch the guest.
conf=$(pct config "$vmid" --current)
binds=$(printf '%s\n' "$conf" | sed -n -E 's/^(mp[0-9]+): \/.*/\1/p' | paste -sd, -)
if [ -n "$binds" ]; then
log "removing host-path binds: $binds"
pct set "$vmid" --delete "$binds"
fi
printf '%s\n' "$conf" | sed -n -E 's/^(net[0-9]+): (.*)$/\1 \2/p' | while read -r key val; do
case ",$val," in *",link_down=1,"*) continue ;; esac
log "link down: $key"
pct set "$vmid" "--$key" "$val,link_down=1"
done
pct set "$vmid" --onboot 0
# Read back: the CONSEQUENCE, not the commands.
conf=$(pct config "$vmid" --current)
bad=""
printf '%s\n' "$conf" | grep -qx 'onboot: 0' || bad="$bad onboot-not-0"
printf '%s\n' "$conf" | grep -qE '^mp[0-9]+: /' && bad="$bad host-bind-left"
printf '%s\n' "$conf" | grep -E '^net[0-9]+: ' | grep -qv 'link_down=1' && bad="$bad nic-up"
if [ -n "$bad" ]; then
echo "restore-beside: FAILED read-back on $vmid:$bad — do NOT start it; destroy it with: pct destroy $vmid" >&2
exit 2
fi
log "OK: $vmid has onboot 0, no host-path binds, every NIC link_down; it was not started"
log "read it with: pct mount $vmid (then pct unmount $vmid; pct destroy $vmid --purge when done)"