Files
ProxMenux/scripts/backup_restore/collectors/collect_guests.sh
2026-06-09 00:13:24 +02:00

98 lines
3.6 KiB
Bash

#!/usr/bin/env bash
# ==========================================================
# ProxMenux backup manifest collector — vms_lxcs_at_backup
# ==========================================================
# Enumerates VMs (qm list) and LXCs (pct list) on this PVE node.
# Read-only; emits the metadata only — actual VM/LXC data is
# the responsibility of vzdump / PBS, not this manifest.
# Schema: scripts/backup_restore/schema/manifest.schema.json
# ==========================================================
set -euo pipefail
vms='[]'
lxcs='[]'
# ── VMs (qm list) ──
# Output:
# VMID NAME STATUS MEM(MB) BOOTDISK(GB) PID
# 100 Alpine-Linux-3-21 stopped 4096 0.00 0
# Header line starts with VMID; we skip it.
if command -v qm >/dev/null 2>&1; then
while IFS= read -r line; do
[[ -z "$line" ]] && continue
# Skip the header
[[ "$line" =~ ^[[:space:]]*VMID[[:space:]] ]] && continue
# Parse positionally. NAME can contain spaces, but `qm list` pads/columns
# them, so we use fixed positions: VMID at col 1, STATUS as the 3rd
# whitespace-delimited token from the END (mem, bootdisk, pid are after).
vmid="$(printf '%s' "$line" | awk '{print $1}')"
[[ "$vmid" =~ ^[0-9]+$ ]] || continue
# Strip trailing PID + BOOTDISK + MEM(MB) + STATUS to extract the NAME.
# rev → cut → rev technique:
trailing="$(printf '%s' "$line" | awk '{printf "%s %s %s %s", $(NF-3), $(NF-2), $(NF-1), $NF}')"
status="$(printf '%s' "$trailing" | awk '{print $1}')"
memory_mb="$(printf '%s' "$trailing" | awk '{print $2}')"
bootdisk_gb="$(printf '%s' "$trailing" | awk '{print $3}')"
# Name: drop first column (vmid) and last 4 columns
name="$(printf '%s' "$line" | awk '{$1=""; for(i=NF-3;i<=NF;i++) $i=""; sub(/^[[:space:]]+/,""); sub(/[[:space:]]+$/,""); print}')"
case "$status" in
running|stopped|paused) ;;
*) status="stopped" ;;
esac
vms="$(jq --argjson acc "$vms" \
--argjson vmid "$vmid" \
--arg name "$name" \
--argjson memory_mb "${memory_mb:-0}" \
--argjson bootdisk_gb "${bootdisk_gb:-0}" \
--arg status "$status" \
-n '
$acc + [{
vmid: $vmid,
name: $name,
memory_mb: $memory_mb,
bootdisk_gb: $bootdisk_gb,
status: $status,
config_file: ("configs/qemu-server/" + ($vmid|tostring) + ".conf")
}]
')"
done < <(qm list 2>/dev/null || true)
fi
# ── LXCs (pct list) ──
# Output:
# VMID Status Lock Name
# 101 running alpine
# Header line starts with VMID; we skip it.
if command -v pct >/dev/null 2>&1; then
while IFS= read -r line; do
[[ -z "$line" ]] && continue
[[ "$line" =~ ^[[:space:]]*VMID[[:space:]] ]] && continue
vmid="$(printf '%s' "$line" | awk '{print $1}')"
[[ "$vmid" =~ ^[0-9]+$ ]] || continue
status="$(printf '%s' "$line" | awk '{print $2}')"
# Lock column is sparse; name is always last positional non-empty token
name="$(printf '%s' "$line" | awk '{print $NF}')"
case "$status" in
running|stopped) ;;
*) status="stopped" ;;
esac
lxcs="$(jq --argjson acc "$lxcs" \
--argjson vmid "$vmid" \
--arg name "$name" \
--arg status "$status" \
-n '
$acc + [{
vmid: $vmid,
name: $name,
status: $status,
config_file: ("configs/lxc/" + ($vmid|tostring) + ".conf")
}]
')"
done < <(pct list 2>/dev/null || true)
fi
jq -n --argjson vms "$vms" --argjson lxcs "$lxcs" \
'{ vms: $vms, lxcs: $lxcs }'