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
494 lines
18 KiB
Bash
Executable File
494 lines
18 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# cluster-startup.sh — repornirea clusterului Proxmox `romfast` după o oprire
|
|
# planificată făcută cu cluster-shutdown.sh.
|
|
#
|
|
# Așteaptă nodurile, așteaptă quorumul, pornește guest-urile în ordinea corectă
|
|
# de dependențe (Oracle primul), restaurează cron-urile de monitorizare.
|
|
#
|
|
# Pornește DOAR guest-urile care rulau înainte de oprire, citite din fișierul de
|
|
# stare scris de cluster-shutdown.sh. Fără fișier de stare, folosește lista
|
|
# implicită de mai jos.
|
|
#
|
|
# RULEAZĂ DE PE STAȚIA DE ADMIN, nu de pe un nod.
|
|
# Excepție: cu --allow-on-node poate rula chiar pe pvemini (lansat din
|
|
# dashboard/Discord, detașat prin systemd-run) — nssh către pvemini devine
|
|
# execuție locală.
|
|
#
|
|
# NODURILE TREBUIE PORNITE FIZIC ÎNTÂI (sau prin WoL). Scriptul le așteaptă.
|
|
# Pornește-le pe TOATE TREI aproximativ simultan: un singur nod pornit nu are
|
|
# quorum, /etc/pve rămâne read-only și nu se poate porni niciun guest.
|
|
#
|
|
# Utilizare:
|
|
# ./cluster-startup.sh # interactiv
|
|
# ./cluster-startup.sh --yes # fără confirmări
|
|
# ./cluster-startup.sh --dry-run # arată ce ar face
|
|
# ./cluster-startup.sh --all # ignoră fișierul de stare, pornește lista implicită
|
|
# ./cluster-startup.sh --allow-on-node # permite rularea chiar pe pvemini
|
|
#
|
|
set -euo pipefail
|
|
|
|
# ---------------------------------------------------------------- configurare
|
|
|
|
declare -A NODE_IP=(
|
|
[pve1]=10.0.20.200
|
|
[pvemini]=10.0.20.201
|
|
[pveelite]=10.0.20.202
|
|
)
|
|
|
|
# Ordinea de PORNIRE a guest-urilor: baza de date întâi, consumatorii după.
|
|
# Inversul ordinii de oprire din cluster-shutdown.sh.
|
|
GUEST_START_ORDER=(
|
|
108 # central-oracle — PRIMUL, restul depind de el
|
|
201 # roacentral — IIS reverse proxy, consumator Oracle
|
|
104 # flowise — consumator Oracle
|
|
100 # portainer
|
|
102 # docker.romfast.ro
|
|
103 # dokploy
|
|
106 # gitea
|
|
171 # claude-agent
|
|
101 # minecraft
|
|
110 # moltbot
|
|
303 # Win11-Adina
|
|
304 # Win11-Marius
|
|
302 # oracle-test-302
|
|
109 # oracle-dr-windows — DR, în mod normal rămâne oprit
|
|
301 # docker-portainer-template
|
|
310 # Win11-Template
|
|
)
|
|
|
|
# Guest-uri care NU se pornesc automat când LIPSEȘTE fișierul de stare.
|
|
# Dacă fișierul de stare există, el decide — dacă erau pornite, se repornesc.
|
|
# 109 — VM DR. Pornirea lui nedorită a declanșat bucla OOM din incidentul
|
|
# 2026-04-20; în mod normal stă oprit și e pornit doar de testul DR.
|
|
# 301, 310 — template-uri, nu servicii.
|
|
NEVER_AUTOSTART=(109 301 310)
|
|
|
|
# Rețeaua dedicată de cluster („insula"), existentă din 2026-08-29. De ea atârnă
|
|
# replicarea ZFS (migration: network=10.10.10.0/24 în datacenter.cfg) și storage-ul
|
|
# NFS backup-pvemini. Pe pve1 și pveelite IP-ul stă pe dongle-ul USB, adus la boot
|
|
# de un „auto enx..." din /etc/network/interfaces: dacă nucleul enumerează USB-ul
|
|
# DUPĂ ce networking.service a terminat, interfața rămâne fără IP, iar nodul pare
|
|
# perfect sănătos. Detalii: ../docs/switch-2.5g-dedicat-noduri.md
|
|
declare -A ISLAND_IP=(
|
|
[pve1]=10.10.10.200
|
|
[pvemini]=10.10.10.201
|
|
[pveelite]=10.10.10.202
|
|
)
|
|
ISLAND_NFS_MOUNT=/mnt/pve/backup-pvemini
|
|
ISLAND_NFS_SERVER=10.10.10.201
|
|
ISLAND_NFS_NODES=(pve1 pveelite)
|
|
|
|
ORACLE_CT=108
|
|
ORACLE_DOCKER=oracle-xe
|
|
# Măsurat pe 2026-08-29, la pornire de la rece după oprirea completă a clusterului:
|
|
# instanța a avut nevoie de ~10 minute ca să ajungă OPEN. 600s erau prea strâmți.
|
|
ORACLE_WAIT=1200 # secunde așteptate până baza răspunde la interogări
|
|
|
|
NODE_WAIT=1800 # secunde așteptate până apar toate nodurile
|
|
QUORUM_WAIT=300
|
|
GUEST_WAIT=300
|
|
CRON_BACKUP=/root/crontab.backup-mentenanta.txt
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
LOCAL_NODE=$(hostname)
|
|
|
|
ASSUME_YES=0
|
|
DRY_RUN=0
|
|
USE_ALL=0
|
|
ALLOW_ON_NODE=0
|
|
|
|
# ------------------------------------------------------------------- utilitare
|
|
|
|
c_red=$'\033[31m'; c_grn=$'\033[32m'; c_yel=$'\033[33m'; c_dim=$'\033[2m'; c_off=$'\033[0m'
|
|
|
|
log() { printf '%s[%s]%s %s\n' "$c_dim" "$(date +%H:%M:%S)" "$c_off" "$*"; }
|
|
ok() { printf '%s ✓%s %s\n' "$c_grn" "$c_off" "$*"; }
|
|
warn() { printf '%s !%s %s\n' "$c_yel" "$c_off" "$*"; }
|
|
die() { printf '%s ✗ %s%s\n' "$c_red" "$*" "$c_off" >&2; exit 1; }
|
|
step() { printf '\n%s══ %s%s\n' "$c_grn" "$*" "$c_off"; }
|
|
|
|
ping_host() {
|
|
if [[ "${OSTYPE:-}" == msys* || "${OSTYPE:-}" == cygwin* || "${OS:-}" == Windows_NT ]]; then
|
|
ping -n 1 -w 2000 "$1" >/dev/null 2>&1
|
|
else
|
|
ping -c 1 -W 2 "$1" >/dev/null 2>&1
|
|
fi
|
|
}
|
|
|
|
nssh() {
|
|
local node=$1; shift
|
|
if (( ALLOW_ON_NODE )) && [[ "$node" == "$LOCAL_NODE" ]]; then
|
|
bash -c "$*"
|
|
return
|
|
fi
|
|
ssh -o BatchMode=yes -o ConnectTimeout=8 -o StrictHostKeyChecking=accept-new \
|
|
"root@${NODE_IP[$node]}" "$@"
|
|
}
|
|
|
|
run() {
|
|
local node=$1; shift
|
|
if (( DRY_RUN )); then
|
|
printf '%s [dry-run] %s: %s%s\n' "$c_dim" "$node" "$*" "$c_off"
|
|
return 0
|
|
fi
|
|
nssh "$node" "$@"
|
|
}
|
|
|
|
confirm() {
|
|
(( ASSUME_YES || DRY_RUN )) && return 0
|
|
local reply
|
|
printf '\n%s%s%s\n' "$c_yel" "$1" "$c_off"
|
|
read -r -p "Scrie DA ca să continui: " reply
|
|
[[ "$reply" == "DA" ]] || die "Anulat de utilizator."
|
|
}
|
|
|
|
while (( $# )); do
|
|
case "$1" in
|
|
--yes|-y) ASSUME_YES=1 ;;
|
|
--dry-run) DRY_RUN=1 ;;
|
|
--all) USE_ALL=1 ;;
|
|
--allow-on-node) ALLOW_ON_NODE=1 ;;
|
|
-h|--help) sed -n '2,28p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
|
*) die "Argument necunoscut: $1" ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
STATE_FILE="${CLUSTER_STATE_FILE:-}"
|
|
if [[ -z "$STATE_FILE" ]]; then
|
|
if (( ALLOW_ON_NODE )); then
|
|
STATE_FILE=/opt/scripts/.cluster-state.txt
|
|
else
|
|
STATE_FILE="$SCRIPT_DIR/.cluster-state.txt"
|
|
fi
|
|
fi
|
|
|
|
if command -v pvecm >/dev/null 2>&1 && (( ! ALLOW_ON_NODE )); then
|
|
die "Rulezi pe un nod Proxmox. Scriptul trebuie rulat de pe stația de admin (sau cu --allow-on-node)."
|
|
fi
|
|
|
|
# ------------------------------------------------- pas 1: așteaptă nodurile
|
|
|
|
step "Aștept nodurile"
|
|
|
|
warn "Nodurile trebuie pornite FIZIC. Pornește-le pe toate trei aproximativ simultan."
|
|
|
|
waited=0
|
|
while (( waited < NODE_WAIT )); do
|
|
missing=()
|
|
for node in "${!NODE_IP[@]}"; do
|
|
ping_host "${NODE_IP[$node]}" || missing+=("$node")
|
|
done
|
|
(( ${#missing[@]} == 0 )) && break
|
|
printf '\r%s aștept: %s (%ds) %s' "$c_dim" "${missing[*]}" "$waited" "$c_off"
|
|
sleep 10; waited=$(( waited + 10 ))
|
|
done
|
|
printf '\r%*s\r' 70 ''
|
|
(( waited < NODE_WAIT )) || die "Nodurile ${missing[*]} nu au apărut în ${NODE_WAIT}s."
|
|
|
|
for node in "${!NODE_IP[@]}"; do
|
|
w=0
|
|
until nssh "$node" true 2>/dev/null; do
|
|
(( w += 10 )); (( w > 300 )) && die "$node răspunde la ping dar nu la SSH."
|
|
sleep 10
|
|
done
|
|
ok "$node — up, SSH OK ($(nssh "$node" 'uptime -p'))"
|
|
done
|
|
|
|
# --------------------------------------------------- pas 2: așteaptă quorum
|
|
|
|
step "Aștept quorumul"
|
|
|
|
waited=0
|
|
until nssh pvemini "pvecm status 2>/dev/null | grep -q 'Quorate: *Yes'"; do
|
|
(( waited >= QUORUM_WAIT )) && die "Clusterul nu a atins quorumul în ${QUORUM_WAIT}s."
|
|
printf '\r%s aștept quorum (%ds)...%s' "$c_dim" "$waited" "$c_off"
|
|
sleep 10; waited=$(( waited + 10 ))
|
|
done
|
|
printf '\r%*s\r' 40 ''
|
|
|
|
VOTES=$(nssh pvemini "pvecm status | awk '/Total votes/ {print \$3}'")
|
|
[[ "$VOTES" == "3" ]] || warn "Quorum atins, dar doar $VOTES/3 voturi. Un nod lipsește din cluster."
|
|
ok "quorum OK, $VOTES/3 voturi"
|
|
|
|
nssh pvemini "ha-manager status | head -4"
|
|
|
|
# ----------------------------------- pas 3: rețeaua dedicată de cluster (insula)
|
|
|
|
# Se verifică ÎNAINTE de pornirea guest-urilor: dacă insula e jos, replicarea și
|
|
# backup-ul NFS tac fără nicio alertă, iar simptomul apare abia peste ore.
|
|
# Nu blochează pornirea — la revenirea curentului, Oracle sus contează mai mult
|
|
# decât replicarea — dar nu te lasă să treci pe lângă problemă fără să o vezi.
|
|
|
|
step "Verific rețeaua de cluster (10.10.10.0/24)"
|
|
|
|
if (( DRY_RUN )); then
|
|
warn "dry-run — sar peste verificarea insulei"
|
|
else
|
|
ISLAND_BAD=()
|
|
|
|
for node in "${!NODE_IP[@]}"; do
|
|
want="${ISLAND_IP[$node]}"
|
|
have=$(nssh "$node" "ip -4 -o addr show | grep -c ' $want/' || true")
|
|
|
|
if [[ "$have" == "0" ]]; then
|
|
# Cazul așteptat de eșec: dongle-ul USB enumerat târziu la boot.
|
|
# ifreload -a aplică doar diferențele față de /etc/network/interfaces,
|
|
# deci nu atinge interfețele tap/veth ale guest-urilor.
|
|
warn "$node — lipsește $want, încerc 'ifreload -a'"
|
|
nssh "$node" "PATH=/usr/sbin:/sbin:/usr/bin:/bin ifreload -a" || true
|
|
sleep 8
|
|
have=$(nssh "$node" "ip -4 -o addr show | grep -c ' $want/' || true")
|
|
fi
|
|
|
|
if [[ "$have" == "0" ]]; then
|
|
ISLAND_BAD+=("$node")
|
|
warn "$node — TOT fără $want după ifreload"
|
|
else
|
|
ok "$node — $want prezent"
|
|
fi
|
|
done
|
|
|
|
# Un IP configurat local nu garantează și cablul/switch-ul. pvemini e sursa
|
|
# replicării și serverul NFS, deci se verifică din el spre celelalte două.
|
|
for node in pve1 pveelite; do
|
|
ip="${ISLAND_IP[$node]}"
|
|
if nssh pvemini "ping -c1 -W2 $ip >/dev/null 2>&1"; then
|
|
ok "pvemini → $node ($ip) — ping OK"
|
|
else
|
|
printf '%s\n' "${ISLAND_BAD[@]:-}" | grep -qx "$node" || ISLAND_BAD+=("$node")
|
|
warn "pvemini → $node ($ip) — NU răspunde (cablu? switch?)"
|
|
fi
|
|
done
|
|
|
|
NFS_BAD=()
|
|
for node in "${ISLAND_NFS_NODES[@]}"; do
|
|
src=$(nssh "$node" "findmnt -no SOURCE $ISLAND_NFS_MOUNT 2>/dev/null || true")
|
|
if [[ "$src" == *"$ISLAND_NFS_SERVER"* ]]; then
|
|
ok "$node — NFS $ISLAND_NFS_MOUNT montat de pe $ISLAND_NFS_SERVER"
|
|
elif [[ -n "${src// /}" ]]; then
|
|
# Prins pe 2026-08-29 la primul boot la rece: o linie rămasă în /etc/fstab
|
|
# monta storage-ul de pe IP-ul de producție ÎNAINTE ca pvestatd să apuce,
|
|
# deci backup-ul mergea tăcut pe rețeaua greșită, cu storage.cfg corect.
|
|
NFS_BAD+=("$node")
|
|
warn "$node — NFS montat de pe ALT server: $src (așteptat $ISLAND_NFS_SERVER)"
|
|
warn " verifică: ssh root@${NODE_IP[$node]} 'grep nfs /etc/fstab'"
|
|
else
|
|
# pvestatd remontează singur în câteva zeci de secunde dacă rețeaua e sus.
|
|
warn "$node — NFS $ISLAND_NFS_MOUNT NEMONTAT (pvestatd îl reia dacă insula urcă)"
|
|
fi
|
|
done
|
|
|
|
if (( ${#NFS_BAD[@]} )); then
|
|
warn "Backup-ul NFS trece pe altă rețea decât cea proiectată pe: ${NFS_BAD[*]}"
|
|
fi
|
|
|
|
if (( ${#ISLAND_BAD[@]} )); then
|
|
warn ""
|
|
warn "INSULA E JOS pe: ${ISLAND_BAD[*]}"
|
|
warn "Consecință: replicarea ZFS și storage-ul NFS backup-pvemini NU vor funcționa."
|
|
warn "Guest-urile pornesc și merg normal — problema e tăcută, se vede abia în pvesr status."
|
|
warn "Verifică: ssh root@<nod> \"ip -br a; dmesg | tail -20\" (dongle USB enumerat târziu?)"
|
|
confirm "Continui cu pornirea guest-urilor, cu replicarea jos?"
|
|
elif (( ${#NFS_BAD[@]} == 0 )); then
|
|
ok "insulă completă — replicarea și NFS-ul au calea liberă"
|
|
else
|
|
warn "insula e sus, dar NFS-ul nu o folosește — vezi mai sus"
|
|
fi
|
|
fi
|
|
|
|
# ------------------------------------------------- pas 4: ce trebuie pornit
|
|
|
|
step "Ce se pornește"
|
|
|
|
TO_START=""
|
|
if (( ! USE_ALL )) && [[ -f "$STATE_FILE" ]]; then
|
|
TO_START=$(grep -v '^#' "$STATE_FILE" | grep -v '^[[:space:]]*$' || true)
|
|
ok "citit din $STATE_FILE ($(grep -c . <<<"$TO_START") guest-uri)"
|
|
else
|
|
(( USE_ALL )) || warn "Nu există $STATE_FILE — folosesc lista implicită."
|
|
HA_SIDS=$(nssh pvemini "ha-manager config | grep -E '^(ct|vm):' | tr -d ' '" || true)
|
|
for node in "${!NODE_IP[@]}"; do
|
|
while read -r id _; do
|
|
[[ -n "$id" ]] || continue
|
|
h=noha; grep -qx "ct:$id" <<<"$HA_SIDS" && h=ha
|
|
TO_START+="ct $node $id stopped $h"$'\n'
|
|
done < <(nssh "$node" "pct list | awk 'NR>1 {print \$1, \$2}'")
|
|
while read -r id _; do
|
|
[[ -n "$id" ]] || continue
|
|
h=noha; grep -qx "vm:$id" <<<"$HA_SIDS" && h=ha
|
|
TO_START+="vm $node $id stopped $h"$'\n'
|
|
done < <(nssh "$node" "qm list | awk 'NR>1 {print \$1, \$3}'")
|
|
done
|
|
# Fără fișier de stare pornim doar ce e explicit în lista de ordine ȘI nu e
|
|
# în NEVER_AUTOSTART. Altfel am porni DR-ul și template-urile, care erau
|
|
# oprite intenționat.
|
|
FILTERED=""
|
|
for id in "${GUEST_START_ORDER[@]}"; do
|
|
if printf '%s\n' "${NEVER_AUTOSTART[@]}" | grep -qx "$id"; then
|
|
warn "$id sărit (NEVER_AUTOSTART) — pornește-l manual dacă chiar îl vrei"
|
|
continue
|
|
fi
|
|
line=$(awk -v id="$id" '$3==id' <<<"$TO_START") || true
|
|
[[ -n "$line" ]] && FILTERED+="$line"$'\n'
|
|
done
|
|
TO_START="$FILTERED"
|
|
fi
|
|
|
|
[[ -n "$TO_START" ]] || die "Nimic de pornit."
|
|
|
|
while read -r type node id status ha; do
|
|
[[ -n "${id:-}" ]] && printf ' %-3s %-9s %-4s %s\n' "$type" "$node" "$id" "$ha"
|
|
done <<<"$TO_START"
|
|
|
|
confirm "Se pornesc guest-urile de mai sus, în ordinea: ${GUEST_START_ORDER[*]}"
|
|
|
|
# ------------------------------------------------- pas 5: pornire guest-uri
|
|
|
|
step "Pornire guest-uri"
|
|
|
|
guest_status() {
|
|
local node=$1 type=$2 id=$3
|
|
if [[ "$type" == ct ]]; then
|
|
nssh "$node" "pct status $id 2>/dev/null | awk '{print \$2}'" || echo unknown
|
|
else
|
|
nssh "$node" "qm status $id 2>/dev/null | awk '{print \$2}'" || echo unknown
|
|
fi
|
|
}
|
|
|
|
start_guest() {
|
|
local type=$1 node=$2 id=$3 ha=$4
|
|
local sid="$type:$id" cmd
|
|
[[ "$type" == ct ]] && cmd=pct || cmd=qm
|
|
|
|
if [[ "$(guest_status "$node" "$type" "$id")" == "running" ]]; then
|
|
ok "$sid deja pornit"; return 0
|
|
fi
|
|
|
|
if [[ "$ha" == ha ]]; then
|
|
# Pentru resursele HA, `--state started` repune resursa sub controlul CRM.
|
|
run "$node" "ha-manager set $sid --state started"
|
|
else
|
|
run "$node" "$cmd start $id"
|
|
fi
|
|
|
|
(( DRY_RUN )) && return 0
|
|
|
|
local waited=0
|
|
while (( waited < GUEST_WAIT )); do
|
|
[[ "$(guest_status "$node" "$type" "$id")" == "running" ]] && { ok "$sid pornit (${waited}s)"; return 0; }
|
|
sleep 5; waited=$(( waited + 5 ))
|
|
done
|
|
warn "$sid nu a pornit în ${GUEST_WAIT}s — verifică manual"
|
|
return 1
|
|
}
|
|
|
|
wait_oracle() {
|
|
local node=$1
|
|
log "aștept ca instanța Oracle să accepte interogări..."
|
|
(( DRY_RUN )) && return 0
|
|
local waited=0
|
|
while (( waited < ORACLE_WAIT )); do
|
|
# `bash -lc`, NU `bash -c`: fără shell de login nu există sqlplus în PATH, iar
|
|
# așteptarea ar expira după $ORACLE_WAIT secunde chiar cu baza perfect deschisă.
|
|
#
|
|
# `timeout 30` e OBLIGATORIU, nu o precauție. Pe 2026-08-29, la pornirea de la
|
|
# rece, sqlplus lansat către o instanță încă în startup s-a blocat definitiv:
|
|
# docker exec a moștenit blocajul, ssh a așteptat, iar bucla n-a apucat să
|
|
# facă nici măcar o iterație. Scriptul a stat agățat 14 minute fără niciun
|
|
# mesaj, cu propriul prag de așteptare nefolosit. Sonda trebuie să răspundă
|
|
# mereu, fie și cu eșec.
|
|
if nssh "$node" "timeout 30 pct exec $ORACLE_CT -- docker exec $ORACLE_DOCKER bash -lc \"printf 'set pagesize 0 feedback off\nselect status from v\\\$instance;\nexit\n' | sqlplus -s / as sysdba\" 2>/dev/null | grep -q OPEN"; then
|
|
ok "Oracle deschis și interogabil (${waited}s)"
|
|
return 0
|
|
fi
|
|
printf '\r%s Oracle încă nu răspunde (%ds)...%s' "$c_dim" "$waited" "$c_off"
|
|
sleep 15; waited=$(( waited + 15 ))
|
|
done
|
|
printf '\r%*s\r' 50 ''
|
|
warn "Oracle nu a răspuns în ${ORACLE_WAIT}s. Continui, dar VERIFICĂ MANUAL."
|
|
warn " ssh root@${NODE_IP[$node]} \"pct exec $ORACLE_CT -- docker logs --tail 50 $ORACLE_DOCKER\""
|
|
return 1
|
|
}
|
|
|
|
FAILED=()
|
|
for id in "${GUEST_START_ORDER[@]}"; do
|
|
line=$(awk -v id="$id" '$3==id' <<<"$TO_START") || true
|
|
[[ -n "$line" ]] || continue
|
|
read -r type node vmid status ha <<<"$line"
|
|
|
|
log "pornesc $type:$vmid pe $node ($ha)"
|
|
start_guest "$type" "$node" "$vmid" "$ha" || FAILED+=("$type:$vmid")
|
|
|
|
# Oracle e blocant: nimic care depinde de el nu pornește până nu e deschis.
|
|
if [[ "$vmid" == "$ORACLE_CT" ]]; then
|
|
wait_oracle "$node" || true
|
|
fi
|
|
done
|
|
|
|
# orice a mai rămas în lista de pornit și nu era în ordinea definită
|
|
while read -r type node id status ha; do
|
|
[[ -n "${id:-}" ]] || continue
|
|
printf '%s\n' "${GUEST_START_ORDER[@]}" | grep -qx "$id" && continue
|
|
warn "guest neplanificat: $type:$id pe $node — îl pornesc"
|
|
start_guest "$type" "$node" "$id" "$ha" || FAILED+=("$type:$id")
|
|
done <<<"$TO_START"
|
|
|
|
# ------------------------------------------------ pas 6: restaurare cron-uri
|
|
|
|
step "Restaurare cron-uri de monitorizare"
|
|
|
|
for node in "${!NODE_IP[@]}"; do
|
|
if ! nssh "$node" "test -f $CRON_BACKUP"; then
|
|
warn "$node — nu există $CRON_BACKUP, sar peste"
|
|
continue
|
|
fi
|
|
run "$node" "crontab $CRON_BACKUP"
|
|
if (( ! DRY_RUN )); then
|
|
left=$(nssh "$node" "crontab -l | grep -c '^#MENTENANTA' || true")
|
|
if [[ "$left" == "0" ]]; then
|
|
ok "$node — cron-uri restaurate"
|
|
else
|
|
warn "$node — au rămas $left linii #MENTENANTA, verifică manual"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# ------------------------------------------------------- pas 7: verificare
|
|
|
|
step "Verificare finală"
|
|
|
|
if (( DRY_RUN )); then
|
|
warn "dry-run — sar peste verificare"
|
|
else
|
|
nssh pvemini "ha-manager status"
|
|
echo
|
|
nssh pvemini "pvesr status"
|
|
echo
|
|
|
|
BADREP=$(nssh pvemini "pvesr status | awk 'NR>1 && \$NF!=\"OK\" {print}'" || true)
|
|
[[ -z "$BADREP" ]] && ok "replicare: toate job-urile OK" \
|
|
|| { warn "job-uri de replicare cu probleme:"; printf '%s\n' "$BADREP"; }
|
|
fi
|
|
|
|
step "Cluster pornit"
|
|
|
|
if (( ${#FAILED[@]} )); then
|
|
warn "Guest-uri care NU au pornit: ${FAILED[*]}"
|
|
warn "Verifică-le manual înainte de a considera repornirea terminată."
|
|
else
|
|
ok "toate guest-urile au pornit"
|
|
fi
|
|
|
|
cat <<EOF
|
|
|
|
De verificat manual:
|
|
- datacenter.cfg are încă AMBELE linii: 'ha: shutdown_policy=freeze'
|
|
și 'migration: network=10.10.10.0/24,type=insecure'
|
|
- dacă lipsesc date recente, compară cu ultima replicare din pvesr status
|
|
- dacă a fost prima repornire după 2026-08-29: notează în
|
|
docs/switch-2.5g-dedicat-noduri.md dacă insula a urcat singură la boot
|
|
|
|
EOF
|