#!/bin/bash # enable-guest-agent-existing-vms.sh # Enable QEMU guest agent on existing VMs via Proxmox API set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" # Load environment if [ -f "${PROJECT_ROOT}/.env" ]; then set -a source "${PROJECT_ROOT}/.env" set +a fi # Try API tokens first, fall back to password PROXMOX_1_TOKEN="${PROXMOX_TOKEN_ML110_01:-}" PROXMOX_2_TOKEN="${PROXMOX_TOKEN_R630_01:-}" PROXMOX_PASS="${PROXMOX_ROOT_PASS:-L@kers2010}" PROXMOX_1_URL="https://192.168.11.10:8006" PROXMOX_2_URL="https://192.168.11.11:8006" # Colors GREEN='\033[0;32m' BLUE='\033[0;34m' RED='\033[0;31m' YELLOW='\033[1;33m' NC='\033[0m' log() { echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $*" } log_success() { echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] ✅${NC} $*" } log_error() { echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ❌${NC} $*" } log_warning() { echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] ⚠️${NC} $*" } # Get auth - try API token first, fall back to password get_auth() { local api_url=$1 local api_token=$2 local response # Try API token authentication first if [ -n "${api_token}" ]; then # Parse token format: root@pam!sankofa-instance-1-api-token=73c7e1a2-c969-409c-ae5b-68e83f012ee9 # For Proxmox API tokens, we use the full token string in Authorization header response=$(curl -k -s -X GET \ -H "Authorization: PVEAuthCookie=${api_token}" \ "${api_url}/api2/json/version" 2>/dev/null) # If token auth works (we get version info), return token for direct use if echo "${response}" | grep -q "data\|version"; then echo "${api_token}|TOKEN" return 0 fi fi # Fall back to password authentication response=$(curl -k -s -X POST \ -d "username=root@pam&password=${PROXMOX_PASS}" \ "${api_url}/api2/json/access/ticket" 2>/dev/null) if echo "${response}" | grep -q "authentication failure"; then echo "" return 1 fi local ticket csrf if command -v jq &> /dev/null; then ticket=$(echo "${response}" | jq -r '.data.ticket // empty' 2>/dev/null) csrf=$(echo "${response}" | jq -r '.data.CSRFPreventionToken // empty' 2>/dev/null) else ticket=$(echo "${response}" | grep -o '"ticket":"[^"]*' | head -1 | cut -d'"' -f4) csrf=$(echo "${response}" | grep -o '"CSRFPreventionToken":"[^"]*' | head -1 | cut -d'"' -f4) fi if [ -z "${ticket}" ] || [ -z "${csrf}" ]; then echo "" return 1 fi echo "${ticket}|${csrf}" } # List all nodes in the cluster list_nodes() { local api_url=$1 local auth_token=$2 local auth_type=$3 local response if [ "${auth_type}" = "TOKEN" ]; then response=$(curl -k -s -X GET \ -H "Authorization: PVEAuthCookie=${auth_token}" \ "${api_url}/api2/json/nodes" 2>/dev/null) else local ticket csrf IFS='|' read -r ticket csrf <<< "${auth_token}" response=$(curl -k -s -X GET \ -H "CSRFPreventionToken: ${csrf}" \ -b "PVEAuthCookie=${ticket}" \ "${api_url}/api2/json/nodes" 2>/dev/null) fi # Extract node names from response if command -v jq &> /dev/null; then echo "${response}" | jq -r '.data[]?.node // empty' 2>/dev/null | grep -v '^$' | sort else # Fallback: extract node names using grep/sed echo "${response}" | grep -o '"node":"[^"]*' | cut -d'"' -f4 | sort | uniq fi } # List all VMs on a node list_vms() { local api_url=$1 local node=$2 local auth_token=$3 local auth_type=$4 local response if [ "${auth_type}" = "TOKEN" ]; then # Use API token directly response=$(curl -k -s -X GET \ -H "Authorization: PVEAuthCookie=${auth_token}" \ "${api_url}/api2/json/nodes/${node}/qemu" 2>/dev/null) else # Use ticket and CSRF token local ticket csrf IFS='|' read -r ticket csrf <<< "${auth_token}" response=$(curl -k -s -X GET \ -H "CSRFPreventionToken: ${csrf}" \ -b "PVEAuthCookie=${ticket}" \ "${api_url}/api2/json/nodes/${node}/qemu" 2>/dev/null) fi # Extract VMIDs from response if command -v jq &> /dev/null; then echo "${response}" | jq -r '.data[]?.vmid // empty' 2>/dev/null | grep -v '^$' | sort -n else # Fallback: extract VMIDs using grep/sed echo "${response}" | grep -o '"vmid":[0-9]*' | grep -o '[0-9]*' | sort -n | uniq fi } # Check if guest agent is already enabled check_guest_agent() { local api_url=$1 local node=$2 local vmid=$3 local auth_token=$4 local auth_type=$5 local response if [ "${auth_type}" = "TOKEN" ]; then response=$(curl -k -s -X GET \ -H "Authorization: PVEAuthCookie=${auth_token}" \ "${api_url}/api2/json/nodes/${node}/qemu/${vmid}/config" 2>/dev/null) else local ticket csrf IFS='|' read -r ticket csrf <<< "${auth_token}" response=$(curl -k -s -X GET \ -H "CSRFPreventionToken: ${csrf}" \ -b "PVEAuthCookie=${ticket}" \ "${api_url}/api2/json/nodes/${node}/qemu/${vmid}/config" 2>/dev/null) fi # Check if agent is already enabled if echo "${response}" | grep -q '"agent"[[:space:]]*:[[:space:]]*"1"'; then return 0 # Already enabled fi return 1 # Not enabled } # Enable guest agent enable_guest_agent() { local api_url=$1 local node=$2 local vmid=$3 local auth_token=$4 local auth_type=$5 local response if [ "${auth_type}" = "TOKEN" ]; then # Use API token directly response=$(curl -k -s -X PUT \ -H "Authorization: PVEAuthCookie=${auth_token}" \ -d "agent=1" \ "${api_url}/api2/json/nodes/${node}/qemu/${vmid}/config" 2>/dev/null) else # Use ticket and CSRF token local ticket csrf IFS='|' read -r ticket csrf <<< "${auth_token}" response=$(curl -k -s -X PUT \ -H "CSRFPreventionToken: ${csrf}" \ -b "PVEAuthCookie=${ticket}" \ -d "agent=1" \ "${api_url}/api2/json/nodes/${node}/qemu/${vmid}/config" 2>/dev/null) fi if echo "${response}" | grep -q '"data":null'; then return 0 fi # Check if already enabled if echo "${response}" | grep -q "already"; then return 0 fi return 1 } process_node() { local api_url=$1 local node=$2 local auth_token=$3 local auth_type=$4 log "Processing node: ${node}" # Discover all VMs on this node local vmids vmids=$(list_vms "${api_url}" "${node}" "${auth_token}" "${auth_type}") if [ -z "${vmids}" ]; then log_warning " No VMs found on ${node}" return 0 fi local vm_count=0 local enabled_count=0 local skipped_count=0 local failed_count=0 while IFS= read -r vmid; do [ -z "${vmid}" ] && continue vm_count=$((vm_count + 1)) # Check if already enabled if check_guest_agent "${api_url}" "${node}" "${vmid}" "${auth_token}" "${auth_type}"; then log " VMID ${vmid}: guest agent already enabled" skipped_count=$((skipped_count + 1)) continue fi log " Enabling guest agent on VMID ${vmid}..." if enable_guest_agent "${api_url}" "${node}" "${vmid}" "${auth_token}" "${auth_type}"; then log_success " VMID ${vmid} guest agent enabled" enabled_count=$((enabled_count + 1)) else log_error " Failed to enable guest agent on VMID ${vmid}" failed_count=$((failed_count + 1)) fi sleep 0.3 done <<< "${vmids}" log " Summary for ${node}: ${vm_count} total, ${enabled_count} enabled, ${skipped_count} already enabled, ${failed_count} failed" # Return counts via global variables or echo echo "${vm_count}|${enabled_count}|${skipped_count}|${failed_count}" } process_site() { local api_url=$1 local site_name=$2 local auth_token=$3 local auth_type=$4 log "==========================================" log "Site: ${site_name}" log "==========================================" # Discover all nodes on this site local nodes nodes=$(list_nodes "${api_url}" "${auth_token}" "${auth_type}") if [ -z "${nodes}" ]; then log_error "Failed to discover nodes on ${site_name}" return 1 fi log_success "Discovered nodes: $(echo "${nodes}" | tr '\n' ' ')" log "" local site_vm_count=0 local site_enabled_count=0 local site_skipped_count=0 local site_failed_count=0 # Process each node while IFS= read -r node; do [ -z "${node}" ] && continue local result result=$(process_node "${api_url}" "${node}" "${auth_token}" "${auth_type}") if [ -n "${result}" ]; then IFS='|' read -r vm_count enabled_count skipped_count failed_count <<< "${result}" site_vm_count=$((site_vm_count + vm_count)) site_enabled_count=$((site_enabled_count + enabled_count)) site_skipped_count=$((site_skipped_count + skipped_count)) site_failed_count=$((site_failed_count + failed_count)) fi log "" done <<< "${nodes}" log "Site Summary for ${site_name}:" log " Total VMs: ${site_vm_count}" log " Enabled: ${site_enabled_count}" log " Already enabled: ${site_skipped_count}" log " Failed: ${site_failed_count}" log "" echo "${site_vm_count}|${site_enabled_count}|${site_skipped_count}|${site_failed_count}" } main() { log "==========================================" log "Enable QEMU Guest Agent on All VMs" log "==========================================" log "" log "This script will:" log "1. Discover all nodes on each Proxmox site" log "2. Discover all VMs on each node" log "3. Check if guest agent is already enabled" log "4. Enable guest agent on VMs that need it" log "" local total_vm_count=0 local total_enabled=0 local total_skipped=0 local total_failed=0 # Site 1 local auth1 auth1=$(get_auth "${PROXMOX_1_URL}" "${PROXMOX_1_TOKEN}") if [ -z "${auth1}" ]; then log_error "Failed to authenticate to Site 1" else IFS='|' read -r auth_token1 auth_type1 <<< "${auth1}" log_success "Authenticated to Site 1" log "" local result1 result1=$(process_site "${PROXMOX_1_URL}" "Site 1" "${auth_token1}" "${auth_type1}") if [ -n "${result1}" ]; then IFS='|' read -r vm_count enabled_count skipped_count failed_count <<< "${result1}" total_vm_count=$((total_vm_count + vm_count)) total_enabled=$((total_enabled + enabled_count)) total_skipped=$((total_skipped + skipped_count)) total_failed=$((total_failed + failed_count)) fi fi # Site 2 local auth2 auth2=$(get_auth "${PROXMOX_2_URL}" "${PROXMOX_2_TOKEN}") if [ -z "${auth2}" ]; then log_error "Failed to authenticate to Site 2" else IFS='|' read -r auth_token2 auth_type2 <<< "${auth2}" log_success "Authenticated to Site 2" log "" local result2 result2=$(process_site "${PROXMOX_2_URL}" "Site 2" "${auth_token2}" "${auth_type2}") if [ -n "${result2}" ]; then IFS='|' read -r vm_count enabled_count skipped_count failed_count <<< "${result2}" total_vm_count=$((total_vm_count + vm_count)) total_enabled=$((total_enabled + enabled_count)) total_skipped=$((total_skipped + skipped_count)) total_failed=$((total_failed + failed_count)) fi fi log "" log "==========================================" log "Overall Summary" log "==========================================" log "Total VMs processed: ${total_vm_count}" log_success "Guest agent enabled: ${total_enabled}" log "Already enabled: ${total_skipped}" if [ "${total_failed}" -gt 0 ]; then log_error "Failed: ${total_failed}" else log_success "Failed: ${total_failed}" fi log "" log "==========================================" log_success "Guest agent enablement complete!" log "==========================================" log "" log_warning "IMPORTANT: Guest agent must also be installed in the OS." log "" log "For existing VMs, you need to:" log "1. Wait for VMs to get IP addresses" log "2. SSH into each VM: ssh admin@" log "3. Install and enable guest agent:" log " sudo apt-get update" log " sudo apt-get install -y qemu-guest-agent" log " sudo systemctl enable qemu-guest-agent" log " sudo systemctl start qemu-guest-agent" log "" log "Note: New VMs created with the updated Crossplane provider will" log "automatically have guest agent enabled in Proxmox config." log "" } main "$@"