initial commit
This commit is contained in:
269
proxmox-to-sentri.sh
Executable file
269
proxmox-to-sentri.sh
Executable file
@@ -0,0 +1,269 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Proxmox VM Inventory Exporter
|
||||
#
|
||||
# Exports one JSON object per VM with:
|
||||
# - CPU / RAM
|
||||
# - Network IPs
|
||||
# - Snapshots
|
||||
# - Backup detection
|
||||
# - VM notes
|
||||
# - Disk information
|
||||
#
|
||||
# Requirements:
|
||||
# - curl
|
||||
# - jq
|
||||
# - for ipv4 and ipv6 collection install the proxmox agent on the VM
|
||||
#
|
||||
# Files:
|
||||
# .env
|
||||
#
|
||||
# Example .env:
|
||||
# PM_HOST="https://proxmox.example.com:8006"
|
||||
# PM_USER="root@pam"
|
||||
# PM_PASS="password"
|
||||
#
|
||||
# Output:
|
||||
# ./output/<vmid>.json
|
||||
# -----------------------------------------------------------------------------
|
||||
SCRIPT_PATH="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
|
||||
|
||||
OUTPUT_DIR="${SCRIPT_DIR}/output"
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
ENV_FILE="${SCRIPT_DIR}/.env"
|
||||
|
||||
# Load environment variables
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
set -a
|
||||
source "$ENV_FILE"
|
||||
set +a
|
||||
else
|
||||
echo ".env file missing: $ENV_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate vars
|
||||
: "${PM_HOST:?Missing PM_HOST}"
|
||||
: "${PM_USER:?Missing PM_USER}"
|
||||
: "${PM_PASS:?Missing PM_PASS}"
|
||||
|
||||
# Authenticate
|
||||
AUTH_RESPONSE=$(curl -sk \
|
||||
--data-urlencode "username=${PM_USER}" \
|
||||
--data-urlencode "password=${PM_PASS}" \
|
||||
"${PM_HOST}/api2/json/access/ticket")
|
||||
|
||||
TICKET=$(echo "$AUTH_RESPONSE" | jq -r '.data.ticket // empty')
|
||||
CSRF=$(echo "$AUTH_RESPONSE" | jq -r '.data.CSRFPreventionToken // empty')
|
||||
|
||||
if [[ "$TICKET" == "null" || -z "$TICKET" ]]; then
|
||||
echo "Authentication failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
api_get() {
|
||||
local endpoint="$1"
|
||||
|
||||
curl -sk \
|
||||
-H "Cookie: PVEAuthCookie=${TICKET}" \
|
||||
-H "CSRFPreventionToken: ${CSRF}" \
|
||||
"${PM_HOST}${endpoint}"
|
||||
}
|
||||
|
||||
echo "Collecting VMs..."
|
||||
|
||||
VMS=$(api_get "/api2/json/cluster/resources?type=vm")
|
||||
|
||||
echo "$VMS" | jq -c '.data[]' | while read -r VM; do
|
||||
|
||||
VMID=$(echo "$VM" | jq -r '.vmid')
|
||||
NODE=$(echo "$VM" | jq -r '.node')
|
||||
NAME=$(echo "$VM" | jq -r '.name')
|
||||
STATUS=$(echo "$VM" | jq -r '.status')
|
||||
CPU=$(echo "$VM" | jq -r '.maxcpu')
|
||||
MEMORY_MB=$(echo "$VM" | jq -r '.maxmem / 1024 / 1024 | floor')
|
||||
|
||||
echo "Processing VM ${VMID} (${NAME})"
|
||||
|
||||
# VM Config
|
||||
CONFIG=$(api_get "/api2/json/nodes/${NODE}/qemu/${VMID}/config")
|
||||
|
||||
DESCRIPTION=$(echo "$CONFIG" | jq -r '.data.description // empty')
|
||||
|
||||
# Try to detect OS
|
||||
OS_INFO=$(api_get "/api2/json/nodes/${NODE}/qemu/${VMID}/agent/get-osinfo" || true)
|
||||
|
||||
OS=$(echo "$OS_INFO" | jq -r '
|
||||
.data["pretty-name"] // empty
|
||||
')
|
||||
|
||||
# Snapshot count
|
||||
SNAPSHOTS=$(api_get "/api2/json/nodes/${NODE}/qemu/${VMID}/snapshot")
|
||||
|
||||
SNAPSHOT_COUNT=$(echo "$SNAPSHOTS" | jq '.data | length - 1')
|
||||
|
||||
# Backup detection
|
||||
BACKUP_ENABLED="no"
|
||||
|
||||
STORAGES=$(api_get "/api2/json/storage" | jq -r '.data[].storage')
|
||||
|
||||
for STORAGE in $STORAGES; do
|
||||
|
||||
CONTENT=$(api_get "/api2/json/nodes/${NODE}/storage/${STORAGE}/content" || true)
|
||||
|
||||
if echo "$CONTENT" | jq -e --arg VMID "$VMID" '
|
||||
.data[]?
|
||||
| select(
|
||||
.volid
|
||||
| test("vzdump-qemu-" + $VMID + "-")
|
||||
)
|
||||
' >/dev/null; then
|
||||
|
||||
BACKUP_ENABLED="yes"
|
||||
break
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
# Network interfaces
|
||||
IPV4=()
|
||||
IPV6=()
|
||||
|
||||
AGENT=$(api_get "/api2/json/nodes/${NODE}/qemu/${VMID}/agent/network-get-interfaces" || true)
|
||||
|
||||
if echo "$AGENT" | jq -e '.data.result' >/dev/null 2>&1; then
|
||||
|
||||
while read -r IP; do
|
||||
IPV4+=("$IP")
|
||||
done < <(
|
||||
echo "$AGENT" | jq -r '
|
||||
.data.result[]."ip-addresses"[]?
|
||||
| select(."ip-address-type" == "ipv4")
|
||||
| ."ip-address"
|
||||
' \
|
||||
| grep -v '^127\.' \
|
||||
| grep -v '^172\.17\.' \
|
||||
| grep -v '^172\.18\.' \
|
||||
| grep -v '^172\.19\.' \
|
||||
| grep -v '^172\.20\.' \
|
||||
| grep -v '^172\.21\.' \
|
||||
| grep -v '^172\.22\.' \
|
||||
| grep -v '^172\.30\.'
|
||||
)
|
||||
|
||||
while read -r IP; do
|
||||
IPV6+=("$IP")
|
||||
done < <(
|
||||
echo "$AGENT" | jq -r '
|
||||
.data.result[]."ip-addresses"[]?
|
||||
| select(."ip-address-type" == "ipv6")
|
||||
| ."ip-address"
|
||||
' \
|
||||
| grep -v '^::1' \
|
||||
| grep -v '^fe80:'
|
||||
)
|
||||
fi
|
||||
|
||||
# MAC Address
|
||||
MAC=$(echo "$CONFIG" | jq -r '
|
||||
.data.net0 // ""
|
||||
' | sed -n 's/.*virtio=\([^,]*\).*/\1/p' \
|
||||
| tr '[:upper:]' '[:lower:]' \
|
||||
| tr -d ':')
|
||||
|
||||
# Disks
|
||||
DISKS_JSON="[]"
|
||||
|
||||
while read -r DISKKEY; do
|
||||
|
||||
DISKCONF=$(echo "$CONFIG" | jq -r ".data.${DISKKEY}")
|
||||
|
||||
STORAGE=$(echo "$DISKCONF" | cut -d':' -f1)
|
||||
|
||||
DISK_LOCATION=$(echo "$DISKCONF" | cut -d',' -f1)
|
||||
|
||||
SIZE=$(echo "$DISKCONF" | sed -n 's/.*size=\([^,]*\).*/\1/p')
|
||||
|
||||
DISKS_JSON=$(echo "$DISKS_JSON" | jq \
|
||||
--arg name "$DISKKEY" \
|
||||
--arg size "$SIZE" \
|
||||
--arg used "$SIZE" \
|
||||
--arg location "$DISK_LOCATION" \
|
||||
'. += [{
|
||||
"disk_name": $name,
|
||||
"disk_space": $size,
|
||||
"disk_used": $used,
|
||||
"disk_location": $location
|
||||
}]'
|
||||
)
|
||||
|
||||
done < <(
|
||||
echo "$CONFIG" | jq -r '
|
||||
.data
|
||||
| keys[]
|
||||
| select(test("^(virtio|scsi|sata|ide)[0-9]+$"))
|
||||
'
|
||||
)
|
||||
|
||||
# Build JSON
|
||||
jq -n \
|
||||
--arg vmid "$MAC" \
|
||||
--arg vmhost "$NAME" \
|
||||
--arg power "$STATUS" \
|
||||
--arg hostname "$NAME" \
|
||||
--arg os "$OS" \
|
||||
--argjson cpu "$CPU" \
|
||||
--argjson memory "$MEMORY_MB" \
|
||||
--argjson snapshot "$SNAPSHOT_COUNT" \
|
||||
--arg description "$DESCRIPTION" \
|
||||
--argjson disks "$DISKS_JSON" \
|
||||
--argjson ipv4 "$(printf '%s\n' "${IPV4[@]}" | jq -R . | jq -s .)" \
|
||||
--argjson ipv6 "$(printf '%s\n' "${IPV6[@]}" | jq -R . | jq -s .)" \
|
||||
--arg backup "$BACKUP_ENABLED" '
|
||||
{
|
||||
"server_vm_id": $vmid,
|
||||
"server_vm_host_name": $vmhost,
|
||||
"server_power_state": $power,
|
||||
"server_state": "new",
|
||||
"server_hostname": $hostname
|
||||
}
|
||||
+
|
||||
(
|
||||
if $os != "" then
|
||||
{ "server_os": $os }
|
||||
else
|
||||
{}
|
||||
end
|
||||
)
|
||||
+
|
||||
{
|
||||
"server_cpu": $cpu,
|
||||
"server_memory": $memory,
|
||||
"server_memory_demand": $memory,
|
||||
"server_disks": $disks,
|
||||
"server_ipv4": $ipv4,
|
||||
"server_ipv6": $ipv6,
|
||||
"server_vm_snapshot": $snapshot,
|
||||
"server_backup":
|
||||
(
|
||||
if $backup == "yes" then
|
||||
[{"+proxmox":"yes"}]
|
||||
else
|
||||
[]
|
||||
end
|
||||
),
|
||||
"server_description": $description
|
||||
}
|
||||
' > "${OUTPUT_DIR}/${VMID}.json"
|
||||
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Done."
|
||||
echo "JSON files written to: ${OUTPUT_DIR}/"
|
||||
Reference in New Issue
Block a user