Add scripts/it-ops export pipeline (collect_inventory_remote, compute_ipam_drift) and proxmox_guest_lan_ips parser for ipconfig* and all net* interfaces. Reconcile ALL_VMIDS, ip-addresses.conf, and operational template with live VMID/IP data; Order portal env vars; DBIS node matrix; inventory helpers. Track latest reports/status/live_inventory.json and drift.json (137 guests, no duplicate LAN IPs). Document export in AGENTS.md. Co-authored-by: Cursor <cursoragent@cursor.com>
73 lines
2.6 KiB
Bash
Executable File
73 lines
2.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Live Proxmox guest inventory + drift vs config/ip-addresses.conf.
|
|
# Usage: bash scripts/it-ops/export-live-inventory-and-drift.sh
|
|
# Requires: SSH key root@SEED, python3 locally and on PVE.
|
|
set -euo pipefail
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
# shellcheck source=/dev/null
|
|
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
|
|
SEED="${SEED_HOST:-${PROXMOX_HOST_R630_01:-192.168.11.11}}"
|
|
OUT_DIR="${OUT_DIR:-${PROJECT_ROOT}/reports/status}"
|
|
TS="$(date +%Y%m%d_%H%M%S)"
|
|
TMP="${TMPDIR:-/tmp}/live_inv_${TS}.json"
|
|
PY="${SCRIPT_DIR}/lib/collect_inventory_remote.py"
|
|
LAN_IPS_PY="${PROJECT_ROOT}/scripts/lib/proxmox_guest_lan_ips.py"
|
|
|
|
mkdir -p "$OUT_DIR"
|
|
|
|
stub_unreachable() {
|
|
python3 - <<'PY'
|
|
import json
|
|
from datetime import datetime, timezone
|
|
print(json.dumps({
|
|
"collected_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
"error": "seed_unreachable",
|
|
"guests": [],
|
|
}, indent=2))
|
|
PY
|
|
}
|
|
|
|
if ! ping -c1 -W2 "$SEED" >/dev/null 2>&1; then
|
|
stub_unreachable >"$TMP"
|
|
else
|
|
REMOTE_DIR="$(ssh -o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=no \
|
|
"root@${SEED}" 'mktemp -d /tmp/pve-inv-collect.XXXXXX')"
|
|
cleanup_remote() {
|
|
ssh -o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=no \
|
|
"root@${SEED}" "rm -rf '${REMOTE_DIR}'" 2>/dev/null || true
|
|
}
|
|
trap cleanup_remote EXIT
|
|
scp -o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=no \
|
|
"$LAN_IPS_PY" "$PY" "root@${SEED}:${REMOTE_DIR}/" >/dev/null
|
|
REMOTE_ENV=()
|
|
case "${IT_COLLECT_IP_NEIGH:-}" in
|
|
1|yes|true|TRUE|Yes) REMOTE_ENV+=(IT_COLLECT_IP_NEIGH=1) ;;
|
|
esac
|
|
if ! ssh -o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=no \
|
|
"root@${SEED}" \
|
|
"PYTHONPATH='${REMOTE_DIR}' ${REMOTE_ENV[*]} python3 '${REMOTE_DIR}/collect_inventory_remote.py'" \
|
|
>"$TMP" 2>/dev/null; then
|
|
stub_unreachable >"$TMP"
|
|
fi
|
|
trap - EXIT
|
|
cleanup_remote
|
|
fi
|
|
|
|
set +e
|
|
python3 "${SCRIPT_DIR}/compute_ipam_drift.py" --live "$TMP" \
|
|
--ip-conf "${PROJECT_ROOT}/config/ip-addresses.conf" \
|
|
--all-vmids-md "${PROJECT_ROOT}/docs/04-configuration/ALL_VMIDS_ENDPOINTS.md" \
|
|
--out-dir "$OUT_DIR"
|
|
DRIFT_RC=$?
|
|
set -e
|
|
|
|
cp -f "$OUT_DIR/live_inventory.json" "${OUT_DIR}/live_inventory_${TS}.json" 2>/dev/null || true
|
|
cp -f "$OUT_DIR/drift.json" "${OUT_DIR}/drift_${TS}.json" 2>/dev/null || true
|
|
rm -f "$TMP"
|
|
if [[ -n "${IT_BFF_SNAPSHOT_DB:-}" ]]; then
|
|
python3 "${SCRIPT_DIR}/persist-it-snapshot-sqlite.py" "$IT_BFF_SNAPSHOT_DB" "$OUT_DIR" "${DRIFT_RC}" 2>/dev/null || true
|
|
fi
|
|
echo "Latest: ${OUT_DIR}/live_inventory.json , ${OUT_DIR}/drift.json"
|
|
exit "${DRIFT_RC}"
|