#!/usr/bin/env bash # ===================================================================== # Refresh the LogShip golden template from the latest production backup # --------------------------------------------------------------------- # Runs ON prox2 (needs qm/qmrestore/pvesh). Phases 1+2 of RECLONE-RUNBOOK: # restore PBS backup of VM 105 as a NEW VM -> quarantined first boot # (NIC link_down) -> copy cleanup scripts in via guest agent -> defuse # + neutralize .env while still offline -> bring the NIC up. # Production VM 105 is never touched (source = its PBS backup). # # refresh-template.sh [--snapshot ] [--scripts ] # # NEWID free VMID (e.g. 115) IP free 192.168.12.x # VMNAME e.g. logship-template-3 # --snapshot default: newest `pvesm list pbs-logyou --vmid 105` # --scripts dir with the cleanup scripts, default /tmp/tc # (scp reference/template-cleanup/* root@prox2:/tmp/tc/ first) # # Afterwards (over SSH, see REFRESH-QUICKSTART.md): # ssh-keygen -R ; ssh root@ 'cd /root/idempiere-elastic && docker compose up -d' # /root/template-cleanup/run-cleanup.sh (then validate-template.sh, finalize) # ===================================================================== set -euo pipefail SRC_VMID=105; PBS=pbs-logyou; STORAGE=Disk1-NVMe; GW=192.168.12.1; BRIDGE=vmbr1 SCRIPTS=/tmp/tc; SNAP="" [ $# -ge 3 ] || { grep '^# ' "$0" | sed 's/^# //'; exit 1; } NEWID="$1"; IP="$2"; VMNAME="$3"; shift 3 while [ $# -gt 0 ]; do case "$1" in --snapshot) SNAP="$2"; shift 2;; --scripts) SCRIPTS="$2"; shift 2;; *) echo "unknown option $1" >&2; exit 1;; esac done # --- preflight ------------------------------------------------------------ if qm status "$NEWID" >/dev/null 2>&1; then echo "FATAL: VMID $NEWID exists" >&2; exit 1; fi if ping -c1 -W1 "$IP" >/dev/null 2>&1; then echo "FATAL: $IP answers ping" >&2; exit 1; fi if grep -rqE "ip=${IP}/" /etc/pve/qemu-server/ 2>/dev/null; then echo "FATAL: $IP configured on another VM" >&2; exit 1; fi for f in 00-defuse.sh 01-idempiere-truncate.sql 02-strapi-truncate.sql 03-neutralize-env.sh \ 04-set-instance-identity.sh run-cleanup.sh diagnose-survivors.sql validate-template.sh \ README.md TEMPLATE-README.md RECLONE-RUNBOOK.md REFRESH-QUICKSTART.md; do [ -f "$SCRIPTS/$f" ] || { echo "FATAL: $SCRIPTS/$f missing — scp reference/template-cleanup/* first" >&2; exit 1; } done [ -n "$SNAP" ] || SNAP=$(pvesm list "$PBS" --vmid "$SRC_VMID" | tail -1 | awk '{print $1}') [ -n "$SNAP" ] || { echo "FATAL: no PBS snapshot found" >&2; exit 1; } echo "== $(date '+%F %T') restoring $SNAP -> VM $NEWID ($VMNAME, $IP)" qmrestore "$SNAP" "$NEWID" --storage "$STORAGE" --unique 1 qm set "$NEWID" --name "$VMNAME" --onboot 0 --protection 0 --ipconfig0 "ip=${IP}/24,gw=${GW}" MAC=$(qm config "$NEWID" | grep "^net0:" | sed -E "s/.*virtio=([^,]+).*/\1/") qm set "$NEWID" --net0 "virtio=${MAC},bridge=${BRIDGE},link_down=1" # keep the MAC! qm start "$NEWID" echo "== $(date '+%F %T') booted quarantined (link_down), waiting for guest agent" for i in $(seq 1 40); do qm agent "$NEWID" ping >/dev/null 2>&1 && break; sleep 10 [ "$i" = 40 ] && { echo "FATAL: guest agent not up after 400 s" >&2; exit 1; }; done # --- copy scripts in (base64: pvesh file-write chokes on UTF-8) ------------- gexec() { # gexec -> prints out/err, returns exit code qm guest exec "$NEWID" --timeout "$1" -- bash -c "$2" | python3 -c ' import sys,json; d=json.load(sys.stdin) print(d.get("out-data",""), end=""); print(d.get("err-data",""), end="", file=sys.stderr) sys.exit(d.get("exitcode",1))' } gexec 30 "mkdir -p /root/template-cleanup" for f in "$SCRIPTS"/*; do b=$(basename "$f") pvesh create "/nodes/$(hostname)/qemu/$NEWID/agent/file-write" \ --file "/root/template-cleanup/$b.b64" --content "$(base64 -w0 "$f")" gexec 30 "base64 -d /root/template-cleanup/$b.b64 > /root/template-cleanup/$b && rm /root/template-cleanup/$b.b64" done echo "== scripts copied" echo "== 00-defuse.sh" gexec 900 "chmod +x /root/template-cleanup/*.sh; /root/template-cleanup/00-defuse.sh" echo "== 03-neutralize-env.sh $IP" gexec 120 "/root/template-cleanup/03-neutralize-env.sh $IP" echo "== offline checks" gexec 30 "echo host=\$(hostname); echo crontab=\$(crontab -l 2>/dev/null | grep -cE '^[^#]' || true); echo postfix=\$(systemctl is-enabled postfix 2>&1); echo hosts_logship=\$(grep -c logship.de /etc/hosts)" qm set "$NEWID" --net0 "virtio=${MAC},bridge=${BRIDGE}" echo "== $(date '+%F %T') NIC up. Next, from your machine:" echo " ssh-keygen -R $IP" echo " ssh root@$IP 'cd /root/idempiere-elastic && docker compose up -d'" echo " ssh root@$IP 'nohup /root/template-cleanup/run-cleanup.sh > /root/template-cleanup/cleanup.log 2>&1 &'"