restore.sh aktualisiert
parent
6fb4ab2f87
commit
9a593059f5
178
restore.sh
178
restore.sh
|
|
@ -2,7 +2,7 @@
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# /opt/windmill-restore/restore.sh
|
# /opt/windmill-restore/restore.sh
|
||||||
# Windmill Backup Restore Worker
|
# Windmill Backup Restore Worker
|
||||||
# Version: 1.0.8
|
# Version: 1.0.9
|
||||||
#
|
#
|
||||||
# WAS DIESES SCRIPT MACHT:
|
# WAS DIESES SCRIPT MACHT:
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
# non-blocking (nohup ... &). Komplett eigenständig, keine offene Verbindung.
|
# non-blocking (nohup ... &). Komplett eigenständig, keine offene Verbindung.
|
||||||
#
|
#
|
||||||
# Alle Pfade kommen aus der Datenbank via Windmill:
|
# Alle Pfade kommen aus der Datenbank via Windmill:
|
||||||
# --restore-mount Mountpoint des Restore-Volumes z.B. /mnt/4TB
|
# --restore-mount Mountpoint des Restore-Volumes z.B. /mnt/HDD_5TB.1
|
||||||
# --restore-path PVE-Storage-Name für qmrestore z.B. local-lvm
|
# --restore-path PVE-Storage-Name für qmrestore z.B. 5TB.1
|
||||||
# --rsync-target Rsync-Ziel auf Backup-Server z.B. /backup/incoming/TNP
|
# --rsync-target Rsync-Ziel auf Backup-Server z.B. /backup/incoming/TNP
|
||||||
# --pbs-storage PVE-Storage-ID des PBS-Datastores z.B. pbs-tnp-invest-gmbh
|
# --pbs-storage PVE-Storage-ID des PBS-Datastores z.B. pbs-tnp-invest-gmbh
|
||||||
#
|
#
|
||||||
|
|
@ -19,37 +19,38 @@
|
||||||
# PBS Encrypt-Key: /root/Scripte/${DATASTORE}.keyfile → für qmrestore
|
# PBS Encrypt-Key: /root/Scripte/${DATASTORE}.keyfile → für qmrestore
|
||||||
# 7z-Passwort: /root/Scripte/password_7z.txt → für 7z-Archiv
|
# 7z-Passwort: /root/Scripte/password_7z.txt → für 7z-Archiv
|
||||||
# Format: "tnp-Invest-GmbH: Passwort123"
|
# Format: "tnp-Invest-GmbH: Passwort123"
|
||||||
# Beide werden lokal gecacht – bei mehreren VMs desselben Kunden
|
#
|
||||||
# nur einmal vom PBS-Server geholt.
|
# VM_IMAGE_DIR wird dynamisch aus dem PVE-Storage-Pfad ermittelt:
|
||||||
|
# pvesh get /storage/${RESTORE_PATH} → path → /mnt/HDD_5TB.1/images/${VM_ID}
|
||||||
#
|
#
|
||||||
# ABLAUF:
|
# ABLAUF:
|
||||||
# [0] Keys holen – Keyfile + 7z-Passwort per Rsync vom PBS-Server
|
# [0] Keys holen – Keyfile + 7z-Passwort per Rsync vom PBS-Server
|
||||||
# [1] Space-Check – Freier Platz auf restore-mount prüfen
|
# [1] Space-Check – Freier Platz auf restore-mount prüfen
|
||||||
# [2] VM-ID ermitteln – Original aus Backup-Pfad, Restore-ID ab 1000
|
# [2] VM-ID ermitteln – Original aus Backup-Pfad, Restore-ID ab 1000
|
||||||
# [3] qmrestore – Direkt vom PBS-Storage mit --keyfile
|
# [3] qmrestore – Direkt vom PBS-Storage mit --keyfile
|
||||||
# [4] VM vorbereiten – unlock, cdrom/ide0 entfernen, alle Netzwerkkarten
|
# [4] VM_IMAGE_DIR – Dynamisch aus PVE-Storage-Pfad ermitteln
|
||||||
|
# [5] Images prüfen – Abbruch wenn leer/nicht vorhanden
|
||||||
|
# [6] VM vorbereiten – unlock, cdrom/ide0 entfernen, alle Netzwerkkarten
|
||||||
# löschen, Agent aktivieren
|
# löschen, Agent aktivieren
|
||||||
# [5] VM starten & – 120s auf qm-Agent warten (10s Schritte)
|
# [7] VM starten & – 120s auf qm-Agent warten (10s Schritte)
|
||||||
# Agent prüfen Kein Agent = qm_agent_ok=false, KEIN Abbruch
|
# Agent prüfen Kein Agent = qm_agent_ok=false, KEIN Abbruch
|
||||||
# [6] VM stoppen – Sauberes Shutdown, nach 30s force-stop
|
# [8] VM stoppen – Sauberes Shutdown, nach 30s force-stop
|
||||||
# [7] 7z-Archiv – VM-Images verschlüsselt zippen (7z-Passwort)
|
# [9] 7z-Archiv – VM-Images verschlüsselt zippen (7z-Passwort)
|
||||||
# [8] Rsync – ZIP zum Backup-Server (Firmen-Zielverzeichnis)
|
# [10] Rsync – ZIP zum Backup-Server (Firmen-Zielverzeichnis)
|
||||||
# 3 Versuche + Größenvergleich
|
# 3 Versuche + Größenvergleich
|
||||||
# [9] Aufräumen – VM destroy, ZIP löschen
|
# [11] Aufräumen – VM destroy, ZIP löschen
|
||||||
# [10] Webhook – JSON → Windmill schreibt DB & startet nächsten
|
# [12] Webhook – JSON → Windmill schreibt DB & startet nächsten
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# ── Konfigdatei laden (PBS-Credentials) ──────────────────────────────────────
|
# ── Konfigdatei laden (PBS-Credentials) ──────────────────────────────────────
|
||||||
# pbs.conf wird von Windmill Step C per SFTP deployt (chmod 600)
|
|
||||||
# Enthält: PBS_HOST, PBS_PORT, PBS_USER, PBS_PASSWORD, PBS_FINGERPRINT
|
|
||||||
CONF_FILE="/opt/windmill-restore/pbs.conf"
|
CONF_FILE="/opt/windmill-restore/pbs.conf"
|
||||||
[[ ! -f "$CONF_FILE" ]] && { echo "FEHLER: $CONF_FILE fehlt!" >&2; exit 1; }
|
[[ ! -f "$CONF_FILE" ]] && { echo "FEHLER: $CONF_FILE fehlt!" >&2; exit 1; }
|
||||||
# shellcheck source=/dev/null
|
# shellcheck source=/dev/null
|
||||||
source "$CONF_FILE"
|
source "$CONF_FILE"
|
||||||
|
# Enthält: PBS_HOST, PBS_PORT, PBS_USER, PBS_PASSWORD, PBS_FINGERPRINT
|
||||||
|
|
||||||
# ── Argument-Parser ───────────────────────────────────────────────────────────
|
# ── Argument-Parser ───────────────────────────────────────────────────────────
|
||||||
# Kein --encrypt-key – Keys werden per Rsync vom PBS-Server geholt
|
|
||||||
JOB_UUID=""
|
JOB_UUID=""
|
||||||
BACKUP_PATH=""
|
BACKUP_PATH=""
|
||||||
CLIENT_NAME=""
|
CLIENT_NAME=""
|
||||||
|
|
@ -75,7 +76,7 @@ while [[ $# -gt 0 ]]; do
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Pflichtparameter prüfen – kein Fallback
|
# Pflichtparameter prüfen
|
||||||
for var in JOB_UUID BACKUP_PATH CLIENT_NAME \
|
for var in JOB_UUID BACKUP_PATH CLIENT_NAME \
|
||||||
RESTORE_MOUNT RESTORE_PATH RSYNC_TARGET PBS_STORAGE WEBHOOK_URL; do
|
RESTORE_MOUNT RESTORE_PATH RSYNC_TARGET PBS_STORAGE WEBHOOK_URL; do
|
||||||
[[ -z "${!var}" ]] && { echo "FEHLER: --${var//_/-} fehlt" >&2; exit 1; }
|
[[ -z "${!var}" ]] && { echo "FEHLER: --${var//_/-} fehlt" >&2; exit 1; }
|
||||||
|
|
@ -95,9 +96,6 @@ exec > >(tee -a "$LOG_FILE") 2>&1
|
||||||
|
|
||||||
# ── Backup-Pfad zerlegen ──────────────────────────────────────────────────────
|
# ── Backup-Pfad zerlegen ──────────────────────────────────────────────────────
|
||||||
# BACKUP_PATH Format: "tnp-Invest-GmbH:vm/100/2024-01-15T02:00:00Z"
|
# BACKUP_PATH Format: "tnp-Invest-GmbH:vm/100/2024-01-15T02:00:00Z"
|
||||||
# DATASTORE = "tnp-Invest-GmbH"
|
|
||||||
# SNAPSHOT_PATH = "vm/100/2024-01-15T02:00:00Z"
|
|
||||||
# PVE_BACKUP_REF = "pbs-tnp-invest-gmbh:backup/vm/100/2024-01-15T02:00:00Z"
|
|
||||||
DATASTORE=$(echo "$BACKUP_PATH" | cut -d: -f1)
|
DATASTORE=$(echo "$BACKUP_PATH" | cut -d: -f1)
|
||||||
SNAPSHOT_PATH=$(echo "$BACKUP_PATH" | cut -d: -f2)
|
SNAPSHOT_PATH=$(echo "$BACKUP_PATH" | cut -d: -f2)
|
||||||
PVE_BACKUP_REF="${PBS_STORAGE}:backup/${SNAPSHOT_PATH}"
|
PVE_BACKUP_REF="${PBS_STORAGE}:backup/${SNAPSHOT_PATH}"
|
||||||
|
|
@ -113,6 +111,7 @@ ERROR_MSG=""
|
||||||
VM_ID_ORIGINAL=0
|
VM_ID_ORIGINAL=0
|
||||||
VM_ID_RESTORED=0
|
VM_ID_RESTORED=0
|
||||||
VM_NAME=""
|
VM_NAME=""
|
||||||
|
VM_IMAGE_DIR=""
|
||||||
ACTUAL_DISK_BYTES=0
|
ACTUAL_DISK_BYTES=0
|
||||||
ZIP_SIZE_BYTES=0
|
ZIP_SIZE_BYTES=0
|
||||||
ZIP_DURATION=0
|
ZIP_DURATION=0
|
||||||
|
|
@ -122,10 +121,9 @@ RSYNC_RETRIES=0
|
||||||
QM_AGENT_OK="false"
|
QM_AGENT_OK="false"
|
||||||
RESTORE_DURATION=0
|
RESTORE_DURATION=0
|
||||||
ZIP_PASSWORD=""
|
ZIP_PASSWORD=""
|
||||||
KEYFILE_LOCAL=""
|
|
||||||
|
|
||||||
echo "============================================================"
|
echo "============================================================"
|
||||||
echo " Windmill Restore Worker v1.0.8"
|
echo " Windmill Restore Worker v1.0.9"
|
||||||
echo " Client: $CLIENT_NAME"
|
echo " Client: $CLIENT_NAME"
|
||||||
echo " Datastore: $DATASTORE"
|
echo " Datastore: $DATASTORE"
|
||||||
echo " Backup: $BACKUP_PATH"
|
echo " Backup: $BACKUP_PATH"
|
||||||
|
|
@ -184,21 +182,16 @@ trap 'STATUS="failed"
|
||||||
send_webhook "failed" "Abgebrochen in Zeile $LINENO – $LOG_FILE"' ERR
|
send_webhook "failed" "Abgebrochen in Zeile $LINENO – $LOG_FILE"' ERR
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
# [0/10] KEYS VOM PBS-SERVER HOLEN
|
# [0/12] KEYS VOM PBS-SERVER HOLEN
|
||||||
# PBS_HOST kommt aus pbs.conf (bereits gesourct).
|
# PBS_HOST kommt aus pbs.conf. Beide Dateien werden lokal gecacht.
|
||||||
# Beide Dateien werden lokal gecacht – bei mehreren VMs desselben Datastores
|
# Bei mehreren VMs desselben Datastores nur einmal geholt.
|
||||||
# werden sie nur einmal geholt, nicht bei jeder VM erneut.
|
|
||||||
#
|
#
|
||||||
# PBS Encrypt-Keyfile: /root/Scripte/${DATASTORE}.keyfile
|
# PBS Encrypt-Keyfile: /root/Scripte/${DATASTORE}.keyfile → qmrestore --keyfile
|
||||||
# → wird an qmrestore --keyfile übergeben
|
# 7z-Passwort: /root/Scripte/password_7z.txt → 7z -p
|
||||||
# → entschlüsselt die PBS-VM-Images beim Restore
|
# Format: "tnp-Invest-GmbH: Passwort123"
|
||||||
#
|
|
||||||
# 7z-Passwort: /root/Scripte/password_7z.txt
|
|
||||||
# → Format: "tnp-Invest-GmbH: Passwort123"
|
|
||||||
# → wird an 7z -p übergeben zum Verschlüsseln des ZIP-Archivs
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
echo ""
|
echo ""
|
||||||
echo "==> [0/10] Keys vom PBS-Server holen ($PBS_HOST)..."
|
echo "==> [0/12] Keys vom PBS-Server holen ($PBS_HOST)..."
|
||||||
KEY_DIR="/opt/windmill-restore/keys"
|
KEY_DIR="/opt/windmill-restore/keys"
|
||||||
mkdir -p "$KEY_DIR"
|
mkdir -p "$KEY_DIR"
|
||||||
chmod 700 "$KEY_DIR"
|
chmod 700 "$KEY_DIR"
|
||||||
|
|
@ -236,8 +229,6 @@ else
|
||||||
echo " password_7z.txt bereits vorhanden."
|
echo " password_7z.txt bereits vorhanden."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Passwort für diesen Datastore extrahieren
|
|
||||||
# Format der Datei: "tnp-Invest-GmbH: Passwort123"
|
|
||||||
ZIP_PASSWORD=$(grep -m1 "^${DATASTORE}:" "$PW_FILE_LOCAL" \
|
ZIP_PASSWORD=$(grep -m1 "^${DATASTORE}:" "$PW_FILE_LOCAL" \
|
||||||
| awk -F': ' '{print $2}' | tr -d '[:space:]')
|
| awk -F': ' '{print $2}' | tr -d '[:space:]')
|
||||||
|
|
||||||
|
|
@ -248,24 +239,24 @@ ZIP_PASSWORD=$(grep -m1 "^${DATASTORE}:" "$PW_FILE_LOCAL" \
|
||||||
echo " 7z-Passwort geladen ✓"
|
echo " 7z-Passwort geladen ✓"
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
# [1/10] SPACE-CHECK
|
# [1/12] SPACE-CHECK
|
||||||
# Nur Warnung – kein Abbruch. PBS-Restore bricht selbst ab bei vollem Disk.
|
# Nur Warnung – kein Abbruch.
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
echo ""
|
echo ""
|
||||||
echo "==> [1/10] Prüfe freien Speicherplatz auf $RESTORE_MOUNT..."
|
echo "==> [1/12] Prüfe freien Speicherplatz auf $RESTORE_MOUNT..."
|
||||||
mkdir -p "$ZIP_DIR"
|
mkdir -p "$ZIP_DIR"
|
||||||
FREE_KB=$(df "$RESTORE_MOUNT" 2>/dev/null | awk 'NR==2{print $4}' || echo "0")
|
FREE_KB=$(df "$RESTORE_MOUNT" 2>/dev/null | awk 'NR==2{print $4}' || echo "0")
|
||||||
FREE_GB=$(( FREE_KB / 1024 / 1024 ))
|
FREE_GB=$(( FREE_KB / 1024 / 1024 ))
|
||||||
echo " Frei: ${FREE_GB} GB"
|
echo " Frei: ${FREE_GB} GB"
|
||||||
[[ $FREE_GB -lt 30 ]] && echo " WARNUNG: Weniger als 30 GB frei!"
|
[[ $FREE_GB -lt 50 ]] && echo " WARNUNG: Weniger als 50 GB frei!"
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
# [2/10] VM-ID ERMITTELN
|
# [2/12] VM-ID ERMITTELN
|
||||||
# Original aus Snapshot-Pfad (vm/100/... → 100)
|
# Original aus Snapshot-Pfad (vm/100/... → 100)
|
||||||
# Restore-ID: erste freie ab 1000, prüft QEMU + LXC
|
# Restore-ID: erste freie ab 1000, prüft QEMU + LXC
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
echo ""
|
echo ""
|
||||||
echo "==> [2/10] Ermittle VM-IDs..."
|
echo "==> [2/12] Ermittle VM-IDs..."
|
||||||
VM_ID_ORIGINAL=$(echo "$SNAPSHOT_PATH" | grep -oP '\d+' | head -1 || echo "0")
|
VM_ID_ORIGINAL=$(echo "$SNAPSHOT_PATH" | grep -oP '\d+' | head -1 || echo "0")
|
||||||
echo " Original VM-ID: $VM_ID_ORIGINAL"
|
echo " Original VM-ID: $VM_ID_ORIGINAL"
|
||||||
|
|
||||||
|
|
@ -290,14 +281,13 @@ for i in range(1000, 2000):
|
||||||
echo " Restore VM-ID: $VM_ID_RESTORED"
|
echo " Restore VM-ID: $VM_ID_RESTORED"
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
# [3/10] QMRESTORE VOM PBS-STORAGE
|
# [3/12] QMRESTORE VOM PBS-STORAGE
|
||||||
# Restore direkt vom registrierten PBS-PVE-Storage.
|
# --storage aus DB (restore_path), z.B. "5TB.1"
|
||||||
# --storage aus DB (restore_path), z.B. "local-lvm", "local"
|
|
||||||
# --keyfile lokal gecachtes Keyfile vom PBS-Server
|
# --keyfile lokal gecachtes Keyfile vom PBS-Server
|
||||||
# --unique 1 verhindert Konflikte mit bestehenden VM-Configs
|
# --unique 1 verhindert Konflikte mit bestehenden VM-Configs
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
echo ""
|
echo ""
|
||||||
echo "==> [3/10] qmrestore vom PBS-Storage..."
|
echo "==> [3/12] qmrestore vom PBS-Storage..."
|
||||||
echo " Backup-Ref: $PVE_BACKUP_REF"
|
echo " Backup-Ref: $PVE_BACKUP_REF"
|
||||||
echo " Storage: $RESTORE_PATH"
|
echo " Storage: $RESTORE_PATH"
|
||||||
echo " Keyfile: $KEYFILE_LOCAL"
|
echo " Keyfile: $KEYFILE_LOCAL"
|
||||||
|
|
@ -314,18 +304,64 @@ qmrestore "$PVE_BACKUP_REF" "$VM_ID_RESTORED" \
|
||||||
RESTORE_DURATION=$(( $(date +%s) - RESTORE_START_INNER ))
|
RESTORE_DURATION=$(( $(date +%s) - RESTORE_START_INNER ))
|
||||||
echo " Restore abgeschlossen in ${RESTORE_DURATION}s"
|
echo " Restore abgeschlossen in ${RESTORE_DURATION}s"
|
||||||
|
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
# [4/12] VM_IMAGE_DIR DYNAMISCH ERMITTELN
|
||||||
|
# PVE kennt den Basispfad des Storages.
|
||||||
|
# pvesh get /storage/5TB.1 → "path": "/mnt/HDD_5TB.1"
|
||||||
|
# VM_IMAGE_DIR = /mnt/HDD_5TB.1/images/${VM_ID_RESTORED}
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
echo ""
|
||||||
|
echo "==> [4/12] Ermittle VM-Image-Verzeichnis..."
|
||||||
|
STORAGE_BASE=$(pvesh get "/storage/${RESTORE_PATH}" --output-format json \
|
||||||
|
2>/dev/null | python3 -c "
|
||||||
|
import json, sys
|
||||||
|
cfg = json.load(sys.stdin)
|
||||||
|
print(cfg.get('path', ''))
|
||||||
|
" 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
if [[ -n "$STORAGE_BASE" ]]; then
|
||||||
|
VM_IMAGE_DIR="${STORAGE_BASE}/images/${VM_ID_RESTORED}"
|
||||||
|
else
|
||||||
|
# Fallback: direkt aus pvesm path
|
||||||
|
VM_IMAGE_DIR=$(pvesm path "${RESTORE_PATH}:vm-${VM_ID_RESTORED}-disk-0" \
|
||||||
|
2>/dev/null | xargs dirname 2>/dev/null || echo "")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$VM_IMAGE_DIR" ]]; then
|
||||||
|
# Letzter Fallback: Standard-Pfad
|
||||||
VM_IMAGE_DIR="/var/lib/vz/images/${VM_ID_RESTORED}"
|
VM_IMAGE_DIR="/var/lib/vz/images/${VM_ID_RESTORED}"
|
||||||
|
echo " WARNUNG: Storage-Pfad nicht ermittelt, nutze Fallback: $VM_IMAGE_DIR"
|
||||||
|
else
|
||||||
|
echo " VM-Image-Dir: $VM_IMAGE_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
ACTUAL_DISK_BYTES=$(du -sb "$VM_IMAGE_DIR" 2>/dev/null | awk '{print $1}' || echo "0")
|
ACTUAL_DISK_BYTES=$(du -sb "$VM_IMAGE_DIR" 2>/dev/null | awk '{print $1}' || echo "0")
|
||||||
echo " Image-Größe: $(( ACTUAL_DISK_BYTES / 1024 / 1024 / 1024 )) GB"
|
echo " Image-Größe: $(( ACTUAL_DISK_BYTES / 1024 / 1024 / 1024 )) GB"
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
# [4/10] VM VORBEREITEN
|
# [5/12] VM-IMAGES PRÜFEN
|
||||||
# unlock → stop → cdrom/ide0 entfernen → alle Netzwerkkarten (net0-net10)
|
# Wenn leer oder nicht vorhanden → failed, Webhook senden, nächstes Backup.
|
||||||
# löschen → Agent aktivieren.
|
|
||||||
# Kein Netzwerk = kein versehentlicher Zugang zum Produktivnetz beim Test.
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
echo ""
|
echo ""
|
||||||
echo "==> [4/10] VM vorbereiten..."
|
echo "==> [5/12] Prüfe VM-Images..."
|
||||||
|
if [[ ! -d "$VM_IMAGE_DIR" ]] || [[ -z "$(ls -A "$VM_IMAGE_DIR" 2>/dev/null)" ]]; then
|
||||||
|
ERROR_MSG="VM_IMAGE_DIR leer oder nicht vorhanden: $VM_IMAGE_DIR"
|
||||||
|
echo " FEHLER: $ERROR_MSG"
|
||||||
|
qm destroy "$VM_ID_RESTORED" \
|
||||||
|
--destroy-unreferenced-disks 1 --purge 1 2>/dev/null || true
|
||||||
|
trap - ERR
|
||||||
|
send_webhook "failed" "$ERROR_MSG"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo " Images vorhanden ✓"
|
||||||
|
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
# [6/12] VM VORBEREITEN
|
||||||
|
# unlock → stop → cdrom/ide0 entfernen → alle Netzwerkkarten (net0-net10)
|
||||||
|
# löschen → Agent aktivieren.
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
echo ""
|
||||||
|
echo "==> [6/12] VM vorbereiten..."
|
||||||
qm unlock "$VM_ID_RESTORED" 2>/dev/null || true
|
qm unlock "$VM_ID_RESTORED" 2>/dev/null || true
|
||||||
qm stop "$VM_ID_RESTORED" 2>/dev/null || true
|
qm stop "$VM_ID_RESTORED" 2>/dev/null || true
|
||||||
sleep 3
|
sleep 3
|
||||||
|
|
@ -341,13 +377,12 @@ qm set "$VM_ID_RESTORED" --agent "enabled=1,type=virtio" 2>/dev/null || true
|
||||||
echo " VM vorbereitet (Netzwerkkarten entfernt, Agent aktiviert)."
|
echo " VM vorbereitet (Netzwerkkarten entfernt, Agent aktiviert)."
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
# [5/10] VM STARTEN & QM-AGENT PRÜFEN
|
# [7/12] VM STARTEN & QM-AGENT PRÜFEN
|
||||||
# 120s in 10s-Schritten. Agent läuft über QEMU Guest Agent Channel –
|
# 120s in 10s-Schritten. Agent über QEMU Guest Agent Channel, kein Netzwerk.
|
||||||
# kein Netzwerk nötig. Kein Agent nach 120s = KEIN Abbruch,
|
# Kein Agent = KEIN Abbruch, qm_agent_ok=false in DB.
|
||||||
# qm_agent_ok=false wird im Webhook an Windmill gemeldet → DB-Eintrag.
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
echo ""
|
echo ""
|
||||||
echo "==> [5/10] Starte VM & warte auf qm-Agent (max. 120s)..."
|
echo "==> [7/12] Starte VM & warte auf qm-Agent (max. 120s)..."
|
||||||
qm start "$VM_ID_RESTORED" 2>/dev/null || true
|
qm start "$VM_ID_RESTORED" 2>/dev/null || true
|
||||||
|
|
||||||
AGENT_WAIT=0
|
AGENT_WAIT=0
|
||||||
|
|
@ -380,11 +415,11 @@ if [[ "$QM_AGENT_OK" == "false" ]]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
# [6/10] VM STOPPEN
|
# [8/12] VM STOPPEN
|
||||||
# Sauberes Shutdown, nach 30s force-stop falls VM noch läuft.
|
# Sauberes Shutdown, nach 30s force-stop.
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
echo ""
|
echo ""
|
||||||
echo "==> [6/10] Stoppe VM..."
|
echo "==> [8/12] Stoppe VM..."
|
||||||
qm shutdown "$VM_ID_RESTORED" 2>/dev/null || true
|
qm shutdown "$VM_ID_RESTORED" 2>/dev/null || true
|
||||||
sleep 30
|
sleep 30
|
||||||
qm stop "$VM_ID_RESTORED" --skiplock 1 2>/dev/null || true
|
qm stop "$VM_ID_RESTORED" --skiplock 1 2>/dev/null || true
|
||||||
|
|
@ -392,15 +427,12 @@ echo " VM gestoppt."
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
# [7/10] 7Z-ARCHIV ERSTELLEN
|
# [9/12] 7Z-ARCHIV ERSTELLEN
|
||||||
# VM-Images aus /var/lib/vz/images/$VM_ID_RESTORED/* zippen.
|
# VM-Images aus VM_IMAGE_DIR/* zippen.
|
||||||
# VM-Name aus qemu-server.conf lesen → ZIP bekommt echten Namen.
|
# VM-Name aus qemu-server.conf lesen → ZIP bekommt echten Namen.
|
||||||
# Passwort kommt aus password_7z.txt (Schritt [0], pro Datastore).
|
|
||||||
# -mx=1 → schnellste Kompression (Images meist schon komprimiert)
|
|
||||||
# -mhe=on → Header-Verschlüsselung (Dateinamen ohne Key unsichtbar)
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
echo ""
|
echo ""
|
||||||
echo "==> [7/10] Erstelle verschlüsseltes 7z-Archiv..."
|
echo "==> [9/12] Erstelle verschlüsseltes 7z-Archiv..."
|
||||||
|
|
||||||
VM_CONF="${VM_IMAGE_DIR}/qemu-server.conf"
|
VM_CONF="${VM_IMAGE_DIR}/qemu-server.conf"
|
||||||
VM_NAME=$(grep -m1 "^name:" "$VM_CONF" 2>/dev/null \
|
VM_NAME=$(grep -m1 "^name:" "$VM_CONF" 2>/dev/null \
|
||||||
|
|
@ -408,7 +440,7 @@ VM_NAME=$(grep -m1 "^name:" "$VM_CONF" 2>/dev/null \
|
||||||
|| echo "$SAFE_CLIENT")
|
|| echo "$SAFE_CLIENT")
|
||||||
echo " VM-Name: $VM_NAME"
|
echo " VM-Name: $VM_NAME"
|
||||||
|
|
||||||
ZIP_FILE="${ZIP_DIR}/${VM_NAME}_$(date +%Y%m%d).7z"
|
ZIP_FILE="${ZIP_DIR}/${VM_ID_ORIGINAL}_${VM_NAME}_$(date +%Y%m%d).7z"
|
||||||
ZIP_START=$(date +%s)
|
ZIP_START=$(date +%s)
|
||||||
|
|
||||||
7z a -t7z \
|
7z a -t7z \
|
||||||
|
|
@ -426,12 +458,12 @@ ZIP_SIZE_BYTES=$(stat -c%s "$ZIP_FILE" 2>/dev/null || echo "0")
|
||||||
echo " ZIP: $(( ZIP_SIZE_BYTES / 1024 / 1024 )) MB in ${ZIP_DURATION}s"
|
echo " ZIP: $(( ZIP_SIZE_BYTES / 1024 / 1024 )) MB in ${ZIP_DURATION}s"
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
# [8/10] RSYNC ZUM BACKUP-SERVER
|
# [10/12] RSYNC ZUM BACKUP-SERVER
|
||||||
# Zielverzeichnis aus DB (rsync_target) = Firmen-spezifischer Pfad.
|
# Zielverzeichnis aus DB (rsync_target) = Firmen-spezifischer Pfad.
|
||||||
# 3 Versuche mit 60s Pause + Größenvergleich lokal vs. remote.
|
# 3 Versuche mit 60s Pause + Größenvergleich lokal vs. remote.
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
echo ""
|
echo ""
|
||||||
echo "==> [8/10] Rsync → ${BACKUP_SERVER_HOST}:${RSYNC_TARGET}..."
|
echo "==> [10/12] Rsync → ${BACKUP_SERVER_HOST}:${RSYNC_TARGET}..."
|
||||||
MAX_RETRIES=3
|
MAX_RETRIES=3
|
||||||
|
|
||||||
rsync_transfer() {
|
rsync_transfer() {
|
||||||
|
|
@ -478,20 +510,19 @@ if [[ "$RSYNC_OK" == "true" ]]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
# [9/10] AUFRÄUMEN
|
# [11/12] AUFRÄUMEN
|
||||||
# VM destroy inkl. Disks (/var/lib/vz/images/$VM_ID wird durch destroy entfernt).
|
# VM destroy inkl. Disks, lokale ZIP löschen.
|
||||||
# Lokale ZIP löschen.
|
# Keys bleiben gecacht für nächste VM desselben Datastores.
|
||||||
# Keys bleiben gecacht für nächste VM desselben Datastores/Kunden.
|
|
||||||
# ═════════════════════════════════════════════════════════════════════════════
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
echo ""
|
echo ""
|
||||||
echo "==> [9/10] Aufräumen..."
|
echo "==> [11/12] Aufräumen..."
|
||||||
qm destroy "$VM_ID_RESTORED" \
|
qm destroy "$VM_ID_RESTORED" \
|
||||||
--destroy-unreferenced-disks 1 \
|
--destroy-unreferenced-disks 1 \
|
||||||
--purge 1 \
|
--purge 1 \
|
||||||
2>/dev/null || echo " VM $VM_ID_RESTORED nicht mehr vorhanden."
|
2>/dev/null || echo " VM $VM_ID_RESTORED nicht mehr vorhanden."
|
||||||
rm -f "$ZIP_FILE"
|
rm -f "$ZIP_FILE"
|
||||||
echo " VM ${VM_ID_RESTORED} entfernt, ZIP gelöscht."
|
echo " VM ${VM_ID_RESTORED} entfernt, ZIP gelöscht."
|
||||||
echo " Keys gecacht in $KEY_DIR (für nächste VM desselben Kunden)."
|
echo " Keys gecacht in $KEY_DIR"
|
||||||
|
|
||||||
# ── Zusammenfassung & Webhook ─────────────────────────────────────────────────
|
# ── Zusammenfassung & Webhook ─────────────────────────────────────────────────
|
||||||
TOTAL=$(( $(date +%s) - RESTORE_START ))
|
TOTAL=$(( $(date +%s) - RESTORE_START ))
|
||||||
|
|
@ -500,6 +531,7 @@ echo "============================================================"
|
||||||
echo " Status: $STATUS"
|
echo " Status: $STATUS"
|
||||||
echo " Gesamtdauer: ${TOTAL}s"
|
echo " Gesamtdauer: ${TOTAL}s"
|
||||||
echo " VM-Name: ${VM_NAME:-$SAFE_CLIENT}"
|
echo " VM-Name: ${VM_NAME:-$SAFE_CLIENT}"
|
||||||
|
echo " VM_IMAGE_DIR: $VM_IMAGE_DIR"
|
||||||
echo " Datastore: $DATASTORE"
|
echo " Datastore: $DATASTORE"
|
||||||
echo " qm-Agent: $QM_AGENT_OK"
|
echo " qm-Agent: $QM_AGENT_OK"
|
||||||
echo " Rsync: $RSYNC_OK (Versuche: $RSYNC_RETRIES)"
|
echo " Rsync: $RSYNC_OK (Versuche: $RSYNC_RETRIES)"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue