#!/usr/bin/env bash # Read-only: compare expected VMIDs from config/proxmox-operational-template.json # to live Proxmox inventory (pct/qm list) over SSH. No cluster changes. # # Usage (from repo root): # bash scripts/verify/audit-proxmox-operational-template.sh # SSH_USER=root SSH_OPTS="-o BatchMode=yes" bash scripts/verify/audit-proxmox-operational-template.sh # # Env: # PROXMOX_HOSTS Space-separated IPs (default: sources config/ip-addresses.conf — ML110, R630-01, R630-02) # SSH_USER default root # SSH_OPTS extra ssh options (e.g. -i /path/key) # # Exit: 0 always (report-only). Prints [MISSING_ON_CLUSTER] / [EXTRA_ON_CLUSTER] when SSH works. set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" TEMPLATE_JSON="$PROJECT_ROOT/config/proxmox-operational-template.json" SSH_USER="${SSH_USER:-root}" SSH_OPTS="${SSH_OPTS:--o ConnectTimeout=6 -o StrictHostKeyChecking=accept-new}" cd "$PROJECT_ROOT" if ! command -v jq &>/dev/null; then echo "[WARN] jq not installed; install jq to compare VMIDs." exit 0 fi if [[ ! -f "$TEMPLATE_JSON" ]]; then echo "[ERROR] Missing $TEMPLATE_JSON" exit 1 fi # shellcheck source=/dev/null source "$PROJECT_ROOT/config/ip-addresses.conf" 2>/dev/null || true PROXMOX_HOSTS="${PROXMOX_HOSTS:-${PROXMOX_HOST_ML110:-192.168.11.10} ${PROXMOX_HOST_R630_01:-192.168.11.11} ${PROXMOX_HOST_R630_02:-192.168.11.12}}" EXPECTED_VMIDS=$(jq -r '.services[] | select(.vmid != null) | .vmid' "$TEMPLATE_JSON" | sort -n | uniq) echo "=== Proxmox template audit (read-only) ===" echo "Template: $TEMPLATE_JSON" echo "Expected VMIDs (non-null): $(echo "$EXPECTED_VMIDS" | wc -l) rows" echo "" ALL_LIVE="" for h in $PROXMOX_HOSTS; do out=$(ssh $SSH_OPTS "${SSH_USER}@${h}" "pct list 2>/dev/null | awk 'NR>1 {print \$1}'; qm list 2>/dev/null | awk 'NR>1 {print \$1}'" 2>/dev/null || true) if [[ -z "$out" ]]; then echo "[SKIP] No inventory from $h (SSH failed or empty)" continue fi echo "--- Live inventory: $h ---" while IFS= read -r vid; do [[ -z "${vid:-}" ]] && continue echo " VMID $vid" ALL_LIVE+="$vid"$'\n' done <<< "$out" done LIVE_SORTED=$(echo "$ALL_LIVE" | sed '/^$/d' | sort -n | uniq) if [[ -z "$LIVE_SORTED" ]]; then echo "" echo "[INFO] No live VMIDs collected (no SSH to cluster). Run from LAN with keys to Proxmox nodes." exit 0 fi echo "" echo "=== Diff (template expected vs union of live VMIDs) ===" MISSING=0 while IFS= read -r ev; do [[ -z "${ev:-}" ]] && continue if ! echo "$LIVE_SORTED" | grep -qx "$ev"; then echo "[MISSING_ON_CLUSTER] VMID $ev (in template, not seen on scanned nodes)" MISSING=$((MISSING + 1)) fi done <<< "$EXPECTED_VMIDS" EXTRA=0 while IFS= read -r lv; do [[ -z "${lv:-}" ]] && continue if ! echo "$EXPECTED_VMIDS" | grep -qx "$lv"; then echo "[EXTRA_ON_CLUSTER] VMID $lv (on cluster, not in template services[])" EXTRA=$((EXTRA + 1)) fi done <<< "$LIVE_SORTED" echo "" echo "Summary: missing_on_template_scan=$MISSING extra_vs_template=$EXTRA" echo "Note: VMIDs on nodes not scanned (other hosts) appear as MISSING. Expand PROXMOX_HOSTS if needed."