139 lines
4.0 KiB
Bash
139 lines
4.0 KiB
Bash
#!/usr/bin/env bash
|
|
# Cluster discovery and remote update helpers for TA-ProxMenu.
|
|
|
|
TAPM_COROSYNC_CONFIG="${TAPM_COROSYNC_CONFIG:-/etc/pve/corosync.conf}"
|
|
|
|
TAPM_CLUSTER_NODES_FROM_JSON() {
|
|
python3 -c '
|
|
import json, re, sys
|
|
try:
|
|
payload = json.load(sys.stdin)
|
|
except (json.JSONDecodeError, OSError):
|
|
raise SystemExit(1)
|
|
if isinstance(payload, dict):
|
|
payload = payload.get("data", [])
|
|
if not isinstance(payload, list):
|
|
raise SystemExit(1)
|
|
valid_name = re.compile(r"^[A-Za-z0-9][A-Za-z0-9.-]{0,62}$")
|
|
rows = []
|
|
for item in payload:
|
|
if not isinstance(item, dict):
|
|
continue
|
|
name = str(item.get("node", "")).strip()
|
|
status = str(item.get("status", "unknown")).strip().lower()
|
|
if valid_name.fullmatch(name):
|
|
rows.append((name, status))
|
|
for name, status in sorted(rows):
|
|
print(f"{name}\t{status}")
|
|
'
|
|
}
|
|
|
|
TAPM_CLUSTER_NODE_ROWS() {
|
|
local node_json
|
|
|
|
node_json="$(timeout 10 pvesh get /nodes --output-format json 2>/dev/null)" ||
|
|
return 1
|
|
printf '%s' "$node_json" | TAPM_CLUSTER_NODES_FROM_JSON
|
|
}
|
|
|
|
TAPM_LOCAL_CLUSTER_NODE() {
|
|
local local_link=''
|
|
|
|
local_link="$(readlink /etc/pve/local 2>/dev/null || true)"
|
|
if [[ -n "$local_link" ]]; then
|
|
basename "$local_link"
|
|
else
|
|
hostname -s
|
|
fi
|
|
}
|
|
|
|
TAPM_UPDATE_REMOTE_NODE() {
|
|
local node="$1"
|
|
|
|
[[ "$node" =~ ^[A-Za-z0-9][A-Za-z0-9.-]{0,62}$ ]] || return 1
|
|
timeout 240 ssh \
|
|
-o BatchMode=yes \
|
|
-o ConnectTimeout=10 \
|
|
-o ServerAliveInterval=15 \
|
|
-o ServerAliveCountMax=2 \
|
|
"root@${node}" \
|
|
'/opt/idssys/ta-proxmenu/run.sh update --local-only'
|
|
}
|
|
|
|
INSTALL_CLUSTER_UPDATES() {
|
|
local index
|
|
local local_node
|
|
local node
|
|
local required_command
|
|
local status
|
|
local node_rows=''
|
|
local cluster_failed=0
|
|
local -a cluster_nodes=()
|
|
local -a cluster_statuses=()
|
|
|
|
if [[ ! -s "$TAPM_COROSYNC_CONFIG" ]]; then
|
|
INSTALL_LOCAL_UPDATES
|
|
return $?
|
|
fi
|
|
for required_command in pvesh python3 ssh timeout; do
|
|
if ! command -v "$required_command" >/dev/null 2>&1; then
|
|
echo -e "${idsCL[Red]}Cluster update requires ${required_command}; no nodes were updated.${idsCL[Default]}"
|
|
return 1
|
|
fi
|
|
done
|
|
node_rows="$(TAPM_CLUSTER_NODE_ROWS)" || {
|
|
echo -e "${idsCL[Red]}Could not retrieve the Proxmox cluster node list; no nodes were updated.${idsCL[Default]}"
|
|
return 1
|
|
}
|
|
while IFS=$'\t' read -r node status; do
|
|
[[ -n "$node" ]] || continue
|
|
cluster_nodes+=("$node")
|
|
cluster_statuses+=("$status")
|
|
done <<<"$node_rows"
|
|
if (( ${#cluster_nodes[@]} == 0 )); then
|
|
echo -e "${idsCL[Red]}The Proxmox API returned no cluster nodes; no nodes were updated.${idsCL[Default]}"
|
|
return 1
|
|
fi
|
|
|
|
local_node="$(TAPM_LOCAL_CLUSTER_NODE)"
|
|
echo -e "${idsCL[LightCyan]}Updating TA-ProxMenu across ${#cluster_nodes[@]} cluster node(s)...${idsCL[Default]}"
|
|
for index in "${!cluster_nodes[@]}"; do
|
|
node="${cluster_nodes[$index]}"
|
|
status="${cluster_statuses[$index]}"
|
|
if [[ "$node" == "$local_node" ]]; then
|
|
continue
|
|
fi
|
|
if [[ "$status" != "online" ]]; then
|
|
printf '\n'
|
|
echo -e "${idsCL[LightYellow]}Skipping ${node}: node status is ${status}.${idsCL[Default]}"
|
|
cluster_failed=1
|
|
continue
|
|
fi
|
|
printf '\n'
|
|
echo -e "${idsCL[LightCyan]}Updating remote node ${node}...${idsCL[Default]}"
|
|
if TAPM_UPDATE_REMOTE_NODE "$node"; then
|
|
echo -e "${idsCL[Green]}Remote node ${node} is updated.${idsCL[Default]}"
|
|
else
|
|
echo -e "${idsCL[Red]}Remote node ${node} failed to update.${idsCL[Default]}"
|
|
cluster_failed=1
|
|
fi
|
|
done
|
|
|
|
printf '\n'
|
|
echo -e "${idsCL[LightCyan]}Updating local node ${local_node}...${idsCL[Default]}"
|
|
if INSTALL_LOCAL_UPDATES; then
|
|
echo -e "${idsCL[Green]}Local node ${local_node} is updated.${idsCL[Default]}"
|
|
else
|
|
echo -e "${idsCL[Red]}Local node ${local_node} failed to update.${idsCL[Default]}"
|
|
cluster_failed=1
|
|
fi
|
|
|
|
printf '\n'
|
|
if (( cluster_failed == 0 )); then
|
|
echo -e "${idsCL[Green]}TA-ProxMenu is updated on all ${#cluster_nodes[@]} cluster node(s).${idsCL[Default]}"
|
|
return 0
|
|
fi
|
|
echo -e "${idsCL[LightYellow]}Cluster update completed with failures; review the node messages above.${idsCL[Default]}"
|
|
return 1
|
|
}
|