incus-contrib/incusos/helpers/proxmox-screenshot

173 lines
4.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# proxmox-screenshot -- Take a console screenshot of a Proxmox VM
#
# Usage: proxmox-screenshot VMID [OUTPUT.png]
#
# Requires: sshpass, python3-pil (for PNG conversion)
# Config: proxmox.yaml (host), env file (PROXMOX_ROOT_PASSWORD)
#
# Outputs the path to the PNG file on stdout.
set -euo pipefail
# ---------------------------------------------------------------------------
# Usage
# ---------------------------------------------------------------------------
usage() {
cat <<'EOF'
Usage: proxmox-screenshot VMID [OUTPUT.png]
Take a console screenshot of a Proxmox VM via QEMU monitor screendump.
Arguments:
VMID VM ID (must be in allowed range: 400-499, 800-939)
OUTPUT.png Output file path (default: /tmp/vm-VMID-screen.png)
Environment:
PROXMOX_ROOT_PASSWORD SSH root password (from env file)
Config:
proxmox.yaml Proxmox host (auto-discovered)
Examples:
proxmox-screenshot 900
proxmox-screenshot 900 /tmp/my-screenshot.png
EOF
exit 0
}
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
usage
fi
# ---------------------------------------------------------------------------
# Validate arguments
# ---------------------------------------------------------------------------
if [[ $# -lt 1 ]]; then
echo "Error: VMID required. Usage: proxmox-screenshot VMID [OUTPUT.png]" >&2
exit 1
fi
VMID="$1"
OUTPUT="${2:-/tmp/vm-${VMID}-screen.png}"
# VMID safety check: only allowed ranges
if ! [[ "$VMID" =~ ^[0-9]+$ ]]; then
echo "Error: VMID must be a number" >&2
exit 1
fi
if ! (( (VMID >= 400 && VMID <= 499) || (VMID >= 800 && VMID <= 939) )); then
echo "Error: VMID $VMID not in allowed range (400-499, 800-939)" >&2
exit 1
fi
# ---------------------------------------------------------------------------
# Load environment
# ---------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
load_env() {
local dir="$SCRIPT_DIR"
while [[ "$dir" != "/" ]]; do
if [[ -f "${dir}/env" ]]; then
# shellcheck disable=SC1091
source "${dir}/env"
return
fi
dir="$(dirname "$dir")"
done
}
load_env
if [[ -z "${PROXMOX_ROOT_PASSWORD:-}" ]]; then
echo "Error: PROXMOX_ROOT_PASSWORD not set. Add it to the env file." >&2
exit 1
fi
# ---------------------------------------------------------------------------
# Load proxmox.yaml for host
# ---------------------------------------------------------------------------
yaml_get() {
local file="$1" key="$2" default="${3:-}"
local val
val=$(awk -F': ' -v k="$key" '$1 == k {print $2; exit}' "$file" 2>/dev/null) || val=""
val="${val#\"}" ; val="${val%\"}"
val="${val#\'}" ; val="${val%\'}"
echo "${val:-$default}"
}
find_proxmox_yaml() {
# Search: script's grandparent (repo/incusos -> repo), script parent, cwd
local search_dirs=(
"${SCRIPT_DIR}/.."
"${SCRIPT_DIR}/../.."
"$(pwd)"
)
for dir in "${search_dirs[@]}"; do
if [[ -f "${dir}/proxmox.yaml" ]]; then
echo "${dir}/proxmox.yaml"
return
fi
if [[ -f "${dir}/incusos/proxmox.yaml" ]]; then
echo "${dir}/incusos/proxmox.yaml"
return
fi
done
echo ""
}
PROXMOX_YAML=$(find_proxmox_yaml)
if [[ -z "$PROXMOX_YAML" ]]; then
echo "Error: proxmox.yaml not found" >&2
exit 1
fi
PVE_HOST=$(yaml_get "$PROXMOX_YAML" "host")
if [[ -z "$PVE_HOST" ]]; then
echo "Error: proxmox.yaml: 'host' is required" >&2
exit 1
fi
# ---------------------------------------------------------------------------
# Take screenshot
# ---------------------------------------------------------------------------
SSH_OPTS=(-o StrictHostKeyChecking=no -o ConnectTimeout=5 -o LogLevel=ERROR)
REMOTE_PPM="/tmp/vm-${VMID}-screen.ppm"
LOCAL_PPM="/tmp/vm-${VMID}-screen.ppm"
# Take screenshot via QEMU monitor
if ! sshpass -p "$PROXMOX_ROOT_PASSWORD" ssh "${SSH_OPTS[@]}" \
"root@${PVE_HOST}" \
"echo 'screendump ${REMOTE_PPM}' | qm monitor ${VMID}" &>/dev/null; then
echo "Error: screendump failed (VM $VMID not running or SSH error)" >&2
exit 1
fi
# Retrieve PPM file
if ! sshpass -p "$PROXMOX_ROOT_PASSWORD" scp "${SSH_OPTS[@]}" \
"root@${PVE_HOST}:${REMOTE_PPM}" "$LOCAL_PPM" &>/dev/null; then
echo "Error: failed to retrieve screenshot from Proxmox host" >&2
exit 1
fi
# Cleanup remote file
sshpass -p "$PROXMOX_ROOT_PASSWORD" ssh "${SSH_OPTS[@]}" \
"root@${PVE_HOST}" "rm -f ${REMOTE_PPM}" &>/dev/null || true
# Convert PPM -> PNG
if python3 -c "from PIL import Image; Image.open('${LOCAL_PPM}').save('${OUTPUT}')" 2>/dev/null; then
rm -f "$LOCAL_PPM"
else
# PIL not available, keep as PPM
OUTPUT="${OUTPUT%.png}.ppm"
mv "$LOCAL_PPM" "$OUTPUT" 2>/dev/null || true
fi
echo "$OUTPUT"