Rulare detasata din dashboard/Discord: nssh local fara ssh la sine, CT 171 ramane pornit si moare odata cu pvemini (HA freeze il reporneste), email cu instructiunile de pornire, pauza si pentru testul DR si fereastra de patch. Garda CT 171 nu mai depinde de /proc/1/environ (era sarita fara root). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KddsXCqEbKMhdFJDYbAsx8
140 lines
4.7 KiB
Bash
Executable File
140 lines
4.7 KiB
Bash
Executable File
#!/bin/bash
|
|
# Revenire VM 201 pe pvemini după failover manual
|
|
# Deployed la /opt/scripts/recover-vm201-to-pvemini.sh pe pveelite
|
|
# Rulează DUPĂ ce pvemini e stabil din nou
|
|
set -euo pipefail
|
|
|
|
VMID=201
|
|
PRIMARY=pvemini
|
|
PRIMARY_IP=10.0.20.201
|
|
SECONDARY=$(hostname)
|
|
LOG=/var/log/recover-vm201.log
|
|
MAIL_TO=mmarius28@gmail.com
|
|
MIN_UPTIME_MIN=30 # pvemini trebuie să fie UP min. 30 min
|
|
|
|
log() { echo "[$(date '+%F %T')] $*" | tee -a "$LOG"; }
|
|
die() { log "ABORT: $*"; exit 1; }
|
|
|
|
confirm() {
|
|
local msg="$1"
|
|
if [[ "${FORCE:-}" == "--yes" ]]; then
|
|
log "$msg → auto-confirmat (--yes)"
|
|
return 0
|
|
fi
|
|
echo
|
|
read -p "$msg (tastează 'DA'): " ANS
|
|
[[ "$ANS" == "DA" ]] || die "Anulat la pasul: $msg"
|
|
}
|
|
|
|
[[ "$SECONDARY" == "pveelite" ]] || die "Rulează pe pveelite, nu pe $SECONDARY"
|
|
[[ $EUID -eq 0 ]] || die "Trebuie root"
|
|
|
|
FORCE=${1:-}
|
|
|
|
log "=== Recovery VM $VMID → $PRIMARY pornit ==="
|
|
|
|
# === CHECK-URI PRE-RECOVERY ===
|
|
|
|
# Check 1: VM rulează pe pveelite
|
|
if ! qm status "$VMID" 2>/dev/null | grep -q running; then
|
|
die "VM $VMID NU rulează pe $SECONDARY. Nu e nimic de recuperat."
|
|
fi
|
|
log "VM $VMID running pe $SECONDARY ✓"
|
|
|
|
# Check 2: pvemini reachable + ssh OK
|
|
if ! ping -c 3 -W 2 "$PRIMARY_IP" &>/dev/null; then
|
|
die "$PRIMARY nu răspunde la ping. Abort."
|
|
fi
|
|
if ! ssh -o ConnectTimeout=5 -o BatchMode=yes "root@$PRIMARY_IP" 'pvecm status' &>/dev/null; then
|
|
die "$PRIMARY nu răspunde la ssh sau nu e în cluster. Abort."
|
|
fi
|
|
log "$PRIMARY reachable ✓"
|
|
|
|
# Check 3: uptime pvemini > threshold
|
|
UPTIME_SEC=$(ssh "root@$PRIMARY_IP" "cat /proc/uptime | awk '{print int(\$1)}'")
|
|
UPTIME_MIN=$((UPTIME_SEC / 60))
|
|
log "$PRIMARY uptime: ${UPTIME_MIN} min"
|
|
if [[ "$UPTIME_MIN" -lt "$MIN_UPTIME_MIN" ]]; then
|
|
die "$PRIMARY uptime doar ${UPTIME_MIN} min (< ${MIN_UPTIME_MIN} min). Așteaptă stabilizare."
|
|
fi
|
|
|
|
# Check 4: errors recente pe pvemini
|
|
ERR_COUNT=$(ssh "root@$PRIMARY_IP" "journalctl -p err -b --since '30 min ago' 2>/dev/null | grep -v '^-- ' | wc -l" || echo 0)
|
|
log "$PRIMARY erori în ultimele 30 min: $ERR_COUNT"
|
|
if [[ "$ERR_COUNT" -gt 50 ]]; then
|
|
log "ATENȚIE: $ERR_COUNT erori pe $PRIMARY"
|
|
confirm "Continui oricum?"
|
|
fi
|
|
|
|
# Check 5: VM 201 NU există ca running pe pvemini
|
|
if ssh "root@$PRIMARY_IP" "qm status $VMID 2>/dev/null | grep -q running"; then
|
|
die "VM $VMID rulează deja pe $PRIMARY (split brain!). Oprește manual întâi."
|
|
fi
|
|
|
|
# === EXECUȚIE ===
|
|
|
|
echo
|
|
echo "========================================"
|
|
echo "Plan recovery VM $VMID către $PRIMARY:"
|
|
echo " 1. Shutdown VM $VMID pe $SECONDARY"
|
|
echo " 2. Replicare inversă $SECONDARY → $PRIMARY"
|
|
echo " 3. Migrare offline $VMID către $PRIMARY"
|
|
echo " 4. Restore replicări normale"
|
|
echo " 5. Start VM $VMID pe $PRIMARY"
|
|
echo "========================================"
|
|
confirm "Pornesc recovery?"
|
|
|
|
# Pas 1: Shutdown VM 201 pe pveelite
|
|
log "Pas 1/5: Shutdown VM $VMID pe $SECONDARY..."
|
|
qm shutdown "$VMID" --timeout 120 || die "Shutdown VM eșuat"
|
|
sleep 5
|
|
qm status "$VMID" | grep -q stopped || die "VM $VMID nu s-a oprit"
|
|
log "VM $VMID stopped ✓"
|
|
|
|
# Pas 2: Job replicare inversă pveelite → pvemini
|
|
log "Pas 2/5: Creez replicare inversă $SECONDARY → $PRIMARY..."
|
|
if pvesr status | grep -q "^${VMID}-2 "; then
|
|
log "Job ${VMID}-2 deja există, îl șterg"
|
|
pvesr delete "${VMID}-2" --force 1 || true
|
|
sleep 2
|
|
fi
|
|
pvesr create-local-job "${VMID}-2" "$PRIMARY" --schedule '*/5' --source "$SECONDARY" \
|
|
--comment "recovery reverse"
|
|
log "Rulez replicare inversă acum (poate dura, depinde de delta)..."
|
|
pvesr run --id "${VMID}-2" || die "Replicare inversă eșuată"
|
|
log "Replicare inversă OK ✓"
|
|
|
|
# Pas 3: Migrare offline VM către pvemini
|
|
log "Pas 3/5: Migrare offline VM $VMID → $PRIMARY..."
|
|
qm migrate "$VMID" "$PRIMARY" || die "Migrare eșuată"
|
|
log "Migrare completă ✓"
|
|
|
|
# Pas 4: Șterge job reverse + verifică replicările normale
|
|
log "Pas 4/5: Curăț jobul de replicare inversă..."
|
|
pvesr delete "${VMID}-2" --force 1 || true
|
|
sleep 2
|
|
log "Replicări active:"
|
|
pvesr status | grep "^${VMID}-" | tee -a "$LOG"
|
|
|
|
# Pas 5: Start VM pe pvemini
|
|
log "Pas 5/5: Start VM $VMID pe $PRIMARY..."
|
|
ssh "root@$PRIMARY_IP" "qm start $VMID" || die "Start pe $PRIMARY eșuat"
|
|
sleep 10
|
|
PRIMARY_STATUS=$(ssh "root@$PRIMARY_IP" "qm status $VMID" | awk '{print $2}')
|
|
log "VM $VMID pe $PRIMARY: $PRIMARY_STATUS"
|
|
|
|
# Mail confirmare
|
|
{
|
|
echo "Recovery VM $VMID complet la $(date)"
|
|
echo
|
|
echo "$SECONDARY → $PRIMARY migrare reușită."
|
|
echo "Status VM pe $PRIMARY: $PRIMARY_STATUS"
|
|
echo
|
|
echo "Replicări active:"
|
|
pvesr status | grep "^${VMID}-"
|
|
echo
|
|
echo "Verifică aplicația Windows pe 10.0.20.201 (IIS etc)."
|
|
} | mail -r 'ups@romfast.ro' -s "[OK] Recovery VM $VMID pe $PRIMARY" "$MAIL_TO"
|
|
|
|
log "=== Recovery complet ==="
|