mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-06-11 11:06:24 +00:00
127 lines
4.7 KiB
Bash
127 lines
4.7 KiB
Bash
#!/usr/bin/env bash
|
|
# ==========================================================
|
|
# ProxMenux backup manifest orchestrator
|
|
# ==========================================================
|
|
# Composes the six collectors into one manifest.json that
|
|
# validates against schema/manifest.schema.json. Designed to
|
|
# be called by backup_host.sh during a backup run. Read-only
|
|
# (no side effects on the host).
|
|
#
|
|
# Usage:
|
|
# build_manifest.sh [--paths-archived <path1> <path2> ...]
|
|
# build_manifest.sh --validate (re-runs the JSON Schema validation)
|
|
#
|
|
# Stdout: pretty-printed manifest JSON.
|
|
# Stderr: progress + warnings.
|
|
# ==========================================================
|
|
set -euo pipefail
|
|
|
|
COLLECTORS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
SCHEMA_FILE="$COLLECTORS_DIR/../schema/manifest.schema.json"
|
|
|
|
# Parse flags
|
|
paths_archived='null'
|
|
do_validate=0
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--paths-archived)
|
|
shift
|
|
tmp='[]'
|
|
while [[ $# -gt 0 && "$1" != --* ]]; do
|
|
tmp="$(jq --argjson a "$tmp" --arg p "$1" -n '$a + [$p]')"
|
|
shift
|
|
done
|
|
paths_archived="$tmp"
|
|
;;
|
|
--validate)
|
|
do_validate=1; shift ;;
|
|
-h|--help)
|
|
sed -nE '/^# Usage:/,/^# Stderr:/p' "$0" | sed -E 's/^# ?//' >&2
|
|
exit 0
|
|
;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
|
|
# Run each collector. If a collector fails we fall back to a safe default
|
|
# (empty array / null object) and warn — the manifest is still useful even
|
|
# if one section is incomplete.
|
|
run_collector() {
|
|
local name="$1" fallback="$2"
|
|
local out
|
|
if out="$(bash "$COLLECTORS_DIR/$name" 2>>/tmp/proxmenux-manifest-stderr.log)"; then
|
|
printf '%s' "$out"
|
|
else
|
|
printf 'warning: collector %s failed; using fallback\n' "$name" >&2
|
|
printf '%s' "$fallback"
|
|
fi
|
|
}
|
|
|
|
# Empty error log first so we can attribute failures to this run.
|
|
: >/tmp/proxmenux-manifest-stderr.log
|
|
|
|
source_host="$(run_collector collect_source_host.sh '{}')"
|
|
hardware_inventory="$(run_collector collect_hardware.sh '{"gpu":[],"tpu":[],"nic":[],"wireless":[]}')"
|
|
storage_inventory="$(run_collector collect_storage.sh '{"zfs_pools":[],"lvm":{"vgs":[]},"physical_disks":[],"pve_storage_cfg":[],"mounts":[]}')"
|
|
installed_components="$(run_collector collect_proxmenux_state.sh '[]')"
|
|
kernel_params="$(run_collector collect_kernel.sh '{"cmdline_extra":[],"modules_loaded_at_boot":[],"modprobe_d_files":[]}')"
|
|
guests="$(run_collector collect_guests.sh '{"vms":[],"lxcs":[]}')"
|
|
|
|
created_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
|
|
# Compose the final manifest. The wrapper key matches the schema:
|
|
# the top level is a single "proxmenux_backup_manifest" object.
|
|
manifest="$(jq -n \
|
|
--arg created_at "$created_at" \
|
|
--arg created_by "proxmenux-host-backup/1.3.0" \
|
|
--argjson source_host "$source_host" \
|
|
--argjson hardware "$hardware_inventory" \
|
|
--argjson storage "$storage_inventory" \
|
|
--argjson components "$installed_components" \
|
|
--argjson kernel "$kernel_params" \
|
|
--argjson guests "$guests" \
|
|
--argjson paths_archived "$paths_archived" \
|
|
'{
|
|
proxmenux_backup_manifest: {
|
|
schema_version: 1,
|
|
created_at: $created_at,
|
|
created_by: $created_by,
|
|
source_host: $source_host,
|
|
hardware_inventory: $hardware,
|
|
storage_inventory: $storage,
|
|
proxmenux_installed_components: $components,
|
|
kernel_params: $kernel,
|
|
vms_lxcs_at_backup: $guests,
|
|
backup_metadata: {
|
|
encrypted: false,
|
|
encryption_format: null,
|
|
compression: "zstd",
|
|
paths_archived: $paths_archived,
|
|
sha256_archive: null,
|
|
size_bytes: null
|
|
}
|
|
}
|
|
}')"
|
|
|
|
# Optional validation step. If python3 + jsonschema are available, run
|
|
# them; otherwise silently skip (validation is mostly a developer aid).
|
|
if [[ "$do_validate" == 1 ]]; then
|
|
if command -v python3 >/dev/null 2>&1 && python3 -c 'import jsonschema' 2>/dev/null; then
|
|
printf '%s' "$manifest" | python3 -c "
|
|
import json, sys, jsonschema
|
|
schema = json.load(open('$SCHEMA_FILE'))
|
|
inst = json.load(sys.stdin)
|
|
try:
|
|
jsonschema.validate(instance=inst, schema=schema)
|
|
print('manifest: validates against schema', file=sys.stderr)
|
|
except jsonschema.exceptions.ValidationError as e:
|
|
print(f'manifest: SCHEMA VIOLATION at {list(e.absolute_path)}: {e.message}', file=sys.stderr)
|
|
sys.exit(1)
|
|
"
|
|
else
|
|
printf 'manifest: jsonschema python module not present; skipping validation\n' >&2
|
|
fi
|
|
fi
|
|
|
|
printf '%s\n' "$manifest"
|