Ring1 aplicat si verificat pe cluster live: config_version 17, ring1_addr pe
10.10.10.20x la fiecare nod, interface{linknumber:1}. Validat cu `corosync -t`
inainte de instalare. Testat prin oprirea reala a lui ring0 pe pveelite: link0
disconnected, link1 connected, cvorum 3/3 neatins - exact scenariul care pe
27 august a lasat nodul mort 16 ore.
Test de repornire completa a clusterului, cu Wake-on-LAN (trezire in ~15s).
Insula a urcat singura pe toate trei nodurile; ipoteza enumerarii tarzii a
USB-ului nu s-a materializat. Unealta noua: wake-cluster.ps1.
Testul a scos la iveala o linie ramasa in /etc/fstab pe pve1 si pveelite, care
monta storage-ul NFS de pe IP-ul de productie inaintea lui pvestatd. Backup-ul
trecea tacut pe reteaua gresita, cu storage.cfg corect. cluster-startup verifica
acum asta automat, impreuna cu IP-urile de insula si conectivitatea reala.
Patru bug-uri gasite prin rulare pe cluster live:
- sonda Oracle nu avea timeout: un sqlplus agatat pe o instanta in pornire a
blocat cluster-startup 14 minute, fara mesaj, cu propriul prag de 600s
nefolosit, fiindca bucla n-a apucat o iteratie;
- `bash -c` in loc de `bash -lc`: sqlplus lipsea din PATH, deci baza nu se
oprea si containerul s-ar fi inchis peste ea;
- PowerShell 5.1 pierde ghilimelele duble catre exe-uri native, deci comanda
ajungea rupta pe nod - trecut pe trimitere codificata base64;
- backup-ul de crontab si fisierul de stare se rescriau la o a doua rulare,
lasand monitorizarea oprita permanent si lista de repornit goala.
Documentatia de oprire planificata pornea de la o afirmatie devenita falsa
("nu exista datacenter.cfg") si de la o comanda care ar fi adaugat o a doua
linie `ha:`. Actualizata, impreuna cu inventarul de guest-uri (VM 304 lipsea
din toate listele de ordine si cadea in maturarea de dupa Oracle).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RJFQZcA7uerXRaL1NrsXpS
475 lines
18 KiB
Bash
Executable File
475 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.
|
|
#
|
|
# 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ă
|
|
#
|
|
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)"
|
|
STATE_FILE="${CLUSTER_STATE_FILE:-$SCRIPT_DIR/.cluster-state.txt}"
|
|
|
|
ASSUME_YES=0
|
|
DRY_RUN=0
|
|
USE_ALL=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
|
|
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 ;;
|
|
-h|--help) sed -n '2,22p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
|
*) die "Argument necunoscut: $1" ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if command -v pvecm >/dev/null 2>&1; then
|
|
die "Rulezi pe un nod Proxmox. Scriptul trebuie rulat de pe stația de admin."
|
|
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
|