feat(cluster): cluster-shutdown/startup --allow-on-node pentru rulare pe pvemini

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
This commit is contained in:
Claude Agent
2026-09-13 10:18:12 +00:00
parent 0ac1cdd45f
commit 0104c255a5
4 changed files with 126 additions and 23 deletions

View File

@@ -11,6 +11,9 @@
# 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
@@ -21,6 +24,7 @@
# ./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
@@ -87,11 +91,12 @@ 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}"
LOCAL_NODE=$(hostname)
ASSUME_YES=0
DRY_RUN=0
USE_ALL=0
ALLOW_ON_NODE=0
# ------------------------------------------------------------------- utilitare
@@ -113,6 +118,10 @@ ping_host() {
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]}" "$@"
}
@@ -136,17 +145,27 @@ confirm() {
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" ;;
--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
if command -v pvecm >/dev/null 2>&1; then
die "Rulezi pe un nod Proxmox. Scriptul trebuie rulat de pe stația de admin."
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