- Add comprehensive database migrations (001-024) for schema evolution - Enhance API schema with expanded type definitions and resolvers - Add new middleware: audit logging, rate limiting, MFA enforcement, security, tenant auth - Implement new services: AI optimization, billing, blockchain, compliance, marketplace - Add adapter layer for cloud integrations (Cloudflare, Kubernetes, Proxmox, storage) - Update Crossplane provider with enhanced VM management capabilities - Add comprehensive test suite for API endpoints and services - Update frontend components with improved GraphQL subscriptions and real-time updates - Enhance security configurations and headers (CSP, CORS, etc.) - Update documentation and configuration files - Add new CI/CD workflows and validation scripts - Implement design system improvements and UI enhancements
430 lines
13 KiB
Bash
Executable File
430 lines
13 KiB
Bash
Executable File
#!/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@<vm-ip>"
|
|
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 "$@"
|
|
|