Last active
June 14, 2025 09:23
-
-
Save QNimbus/f6f3253e66b9747034be12d0e79d8afe to your computer and use it in GitHub Desktop.
Proxmox k3s destroy script #proxmox #shell #scripts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# To download: `curl -O https://gist.githubusercontent.com/QNimbus/f6f3253e66b9747034be12d0e79d8afe/raw/destroy_vms.sh` | |
# To run: `curl -s https://gist.githubusercontent.com/QNimbus/f6f3253e66b9747034be12d0e79d8afe/raw/destroy_vms.sh > destroy_vms.sh && chmod +x destroy_vms.sh && ./destroy_vms.sh` | |
# ==================================================== | |
# K3S VM Destruction Script | |
# ==================================================== | |
# | |
# This script automates the destruction of K3s VMs on Proxmox VE cluster. | |
# It performs the following operations: | |
# 1. Stops running VMs safely | |
# 2. Destroys server, worker, and storage VMs | |
# 3. Provides confirmation prompts for safety | |
# | |
# Requirements: | |
# - Proxmox VE with qm and pvesh commands available | |
# - Appropriate permissions to destroy VMs | |
# | |
# Usage: ./destroy_vms.sh [--vmid <id1,id2,...>] [--force] | |
# | |
# Output: Creates a log file (vm_destruction_output.log) with detailed operation logs | |
# ==================================================== | |
# Function to display usage information | |
usage() { | |
echo "Usage: $0 [--vmid <id1,id2,...>] [--force] [--destroy | --reset-disk]" | |
echo "" | |
echo " --vmid Comma-separated list of VM IDs to process (optional, defaults to all K3s VMs)" | |
echo " --force Skip confirmation prompt (use with caution!)" | |
echo " --destroy Destroy VMs completely (REQUIRED for destruction)" | |
echo " --reset-disk Reset/format/erase the primary hard disk of VMs" | |
echo "" | |
echo "Note: You must specify either --destroy or --reset-disk to perform any action." | |
echo "" | |
echo "Examples:" | |
echo " $0 # Display usage (no action without explicit flag)" | |
echo " $0 --destroy # Destroy all K3s VMs (with confirmation)" | |
echo " $0 --vmid 1211,1221,1231 --destroy # Destroy only specific VMs (with confirmation)" | |
echo " $0 --destroy --force # Destroy all K3s VMs without confirmation" | |
echo " $0 --reset-disk # Reset primary disk of all K3s VMs (with confirmation)" | |
echo " $0 --vmid 1211,1221 --reset-disk # Reset primary disk of specific VMs" | |
exit 1 | |
} | |
# Parse and validate command-line arguments | |
VM_IDS="" | |
FORCE_MODE=false | |
RESET_DISK_MODE=false | |
DESTROY_MODE=false | |
while [[ $# -gt 0 ]]; do | |
case $1 in | |
--vmid) | |
if [[ -z "$2" ]]; then | |
echo "Error: --vmid flag requires a comma-separated list of VM IDs" >&2 | |
exit 1 | |
fi | |
VM_IDS="$2" | |
shift 2 | |
;; | |
--force) | |
FORCE_MODE=true | |
shift | |
;; | |
--reset-disk) | |
RESET_DISK_MODE=true | |
shift | |
;; | |
--destroy) | |
DESTROY_MODE=true | |
shift | |
;; | |
-h|--help) | |
usage | |
;; | |
*) | |
echo "Unknown option: $1" >&2 | |
usage | |
;; | |
esac | |
done | |
# Validate that either --destroy or --reset-disk is specified | |
if [[ "$DESTROY_MODE" != "true" && "$RESET_DISK_MODE" != "true" ]]; then | |
echo "Error: You must specify either --destroy or --reset-disk to perform any action." >&2 | |
echo "" | |
usage | |
fi | |
# Validate that both modes are not specified simultaneously | |
if [[ "$DESTROY_MODE" == "true" && "$RESET_DISK_MODE" == "true" ]]; then | |
echo "Error: Cannot specify both --destroy and --reset-disk. Choose one operation." >&2 | |
exit 1 | |
fi | |
# Exit immediately if a command exits with a non-zero status | |
set -euo pipefail | |
echo "=== K3S VM Management Script ===" | |
if [ "$RESET_DISK_MODE" = true ]; then | |
echo "This script will RESET/FORMAT the primary disks of K3s VMs on Proxmox nodes" | |
elif [ "$DESTROY_MODE" = true ]; then | |
echo "This script will DESTROY K3s VMs on Proxmox nodes" | |
fi | |
echo "Script will exit on any error" | |
echo "===============================" | |
# Check if running in a non-interactive shell without force mode | |
if [[ ! -t 0 && "$FORCE_MODE" != "true" ]]; then | |
echo "ERROR: This script requires an interactive terminal for confirmation." | |
echo "You appear to be running this script via pipe (e.g., curl | bash) which doesn't support interactive prompts." | |
echo "" | |
echo "Instead, please use one of these methods:" | |
echo "" | |
echo "1. Download and run the script directly:" | |
echo " curl -s https://gist.githubusercontent.com/QNimbus/f6f3253e66b9747034be12d0e79d8afe/raw/destroy_vms.sh > destroy_vms.sh" | |
echo " chmod +x destroy_vms.sh" | |
echo " ./destroy_vms.sh" | |
echo "" | |
echo "2. Use the --force flag to bypass confirmation (USE WITH CAUTION):" | |
echo " curl -s https://gist.githubusercontent.com/QNimbus/f6f3253e66b9747034be12d0e79d8afe/raw/destroy_vms.sh | bash -s -- --force" | |
echo "" | |
exit 1 | |
fi | |
# Function for preflight checks | |
# | |
# Verifies that required Proxmox tools (qm and pvesh) are available | |
# before proceeding with VM destruction. | |
# | |
# Arguments: None | |
# Returns: Exits with status 1 if required tools are not found | |
preflight_checks() { | |
echo "Performing preflight checks..." | |
if ! command -v qm &> /dev/null; then | |
echo "Error: 'qm' command not found. Please ensure Proxmox VE CLI tools are installed." >&2 | |
exit 1 | |
fi | |
if ! command -v pvesh &> /dev/null; then | |
echo "Error: 'pvesh' command not found. Please ensure Proxmox VE API client is installed." >&2 | |
exit 1 | |
fi | |
if ! command -v jq &> /dev/null; then | |
echo "Error: 'jq' command not found. Please install jq for JSON parsing." >&2 | |
exit 1 | |
fi | |
echo "✓ Required tools are available" | |
} | |
# Function to restore cursor visibility | |
# | |
# Ensures the terminal cursor is visible, typically called | |
# after operations that might hide the cursor. | |
# | |
# Arguments: None | |
# Returns: None | |
restore_cursor() { | |
printf "\033[?25h" # Show cursor | |
} | |
# Ensure cursor is restored on script exit | |
trap restore_cursor EXIT | |
# Function to display spinner while command executes | |
# | |
# Creates a visual spinner animation to indicate a long-running | |
# process is executing in the background. | |
# | |
# Arguments: | |
# $1 - PID of the background process to monitor | |
# | |
# Returns: None | |
spinner() { | |
local pid=$1 | |
local delay=0.1 | |
local spinstr='|/-\' | |
printf "\033[?25l" # Hide cursor | |
while kill -0 $pid 2>/dev/null; do | |
local temp=${spinstr#?} | |
printf " [%c] " "$spinstr" | |
local spinstr=$temp${spinstr%"$temp"} | |
sleep $delay | |
printf "\b\b\b\b\b\b" | |
done | |
printf " \b\b\b\b" | |
printf "\033[?25h" # Show cursor | |
} | |
# Function to run command with spinner and log output | |
# | |
# Executes a command with a visual spinner, logs the command and its output, | |
# and provides status feedback when complete. | |
# | |
# Arguments: | |
# $1 - Description of the command being run | |
# $2 - Command to execute | |
# | |
# Returns: Exits with status 1 if command fails | |
run_with_spinner() { | |
local desc=$1 | |
local cmd=$2 | |
printf "%-50s" "$desc" | |
echo "$(date): $desc - COMMAND: $cmd" >> "$LOG_FILE" | |
bash -c "$cmd" >> "$LOG_FILE" 2>&1 & | |
local pid=$! | |
spinner $pid | |
wait $pid | |
local status=$? | |
if [ $status -eq 0 ]; then | |
echo "[DONE]" | |
else | |
echo "[FAILED] - Check $LOG_FILE for details" | |
exit 1 | |
fi | |
} | |
# Function to get the node where a VM is located | |
# | |
# Queries the Proxmox cluster to determine which node hosts a specific VM. | |
# | |
# Arguments: | |
# $1 - VM ID to locate | |
# | |
# Returns: Outputs the node name, or empty string if VM not found | |
get_vm_node() { | |
local vm_id=$1 | |
local node_info=$(pvesh get /cluster/resources --type vm --output-format=json 2>/dev/null | jq -r ".[] | select(.vmid == $vm_id) | .node" 2>/dev/null || echo "") | |
echo "$node_info" | |
} | |
# Function to get all K3s VM IDs from Proxmox cluster | |
# | |
# Scans the entire Proxmox cluster for VMs with K3s tags (k3s-server, k3s-agent, k3s-storage) | |
# and returns their VM IDs. This replaces the hardcoded VM ID list with dynamic discovery. | |
# | |
# Arguments: None | |
# Returns: Outputs space-separated list of all K3s VM IDs found in the cluster | |
get_all_k3s_vm_ids() { | |
echo "Scanning Proxmox cluster for K3s VMs..." >&2 | |
# Get all VMs from the cluster and filter by K3s tags | |
local k3s_vm_ids=$(pvesh get /cluster/resources --type vm --output-format=json 2>/dev/null | \ | |
jq -r '.[] | select(.tags // "" | test("k3s-(server|agent|storage)")) | .vmid' 2>/dev/null | \ | |
sort -n | tr '\n' ' ' || echo "") | |
if [[ -z "$k3s_vm_ids" ]]; then | |
echo "No K3s VMs found in the cluster (no VMs with k3s-server, k3s-agent, or k3s-storage tags)" >&2 | |
return 0 | |
fi | |
# Remove trailing space and output | |
k3s_vm_ids=$(echo "$k3s_vm_ids" | sed 's/[[:space:]]*$//') | |
echo "Found K3s VMs: $k3s_vm_ids" >&2 | |
echo "$k3s_vm_ids" | |
} | |
# Function to get VM name and role from Proxmox | |
# | |
# Queries Proxmox to get the actual VM name and determines role from tags. | |
# This provides more accurate information than the hardcoded naming convention. | |
# | |
# Arguments: | |
# $1 - VM ID | |
# | |
# Returns: Outputs the VM name and role (e.g., "k3s-server-001 (server)") | |
get_vm_info_from_proxmox() { | |
local vm_id=$1 | |
# Get VM info from Proxmox | |
local vm_info=$(pvesh get /cluster/resources --type vm --output-format=json 2>/dev/null | \ | |
jq -r ".[] | select(.vmid == $vm_id) | {name: .name, tags: .tags}" 2>/dev/null) | |
if [[ -z "$vm_info" ]]; then | |
echo "k3s-vm-$vm_id (unknown)" | |
return | |
fi | |
local vm_name=$(echo "$vm_info" | jq -r '.name // empty' 2>/dev/null) | |
local vm_tags=$(echo "$vm_info" | jq -r '.tags // empty' 2>/dev/null) | |
# Determine role from tags | |
local role="unknown" | |
if [[ "$vm_tags" =~ k3s-server ]]; then | |
role="server" | |
elif [[ "$vm_tags" =~ k3s-agent ]]; then | |
role="agent" | |
elif [[ "$vm_tags" =~ k3s-storage ]]; then | |
role="storage" | |
fi | |
# Use VM name if available, otherwise generate from ID | |
if [[ -n "$vm_name" ]]; then | |
echo "$vm_name ($role)" | |
else | |
echo "k3s-vm-$vm_id ($role)" | |
fi | |
} | |
# Function to check SSH connectivity to a node | |
# | |
# Verifies that SSH connection to a Proxmox node is working | |
# before attempting to execute remote commands. | |
# | |
# Arguments: | |
# $1 - Name of the Proxmox node to check | |
# | |
# Returns: Exits with status 1 if SSH connection fails | |
check_ssh_connectivity() { | |
local node=$1 | |
if ! ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 "$node" "echo 'SSH connection successful'" >/dev/null 2>&1; then | |
echo "Error: Cannot establish SSH connection to node $node" >&2 | |
exit 1 | |
fi | |
} | |
# Function to safely stop and destroy a VM with checks | |
# | |
# Stops a running VM (if running) and then destroys it, with safety checks | |
# to handle VMs that don't exist or are already stopped. Now handles | |
# distributed VMs by connecting to the appropriate node via SSH. | |
# | |
# Arguments: | |
# $1 - VM ID to destroy | |
# $2 - VM name for logging purposes | |
# | |
# Returns: 0 if successful or VM doesn't exist | |
# Exits with status 1 if destruction fails | |
destroy_vm_with_check() { | |
local vm_id=$1 | |
local vm_name=$2 | |
local desc="Destroying ${vm_name} (ID: ${vm_id})..." | |
printf "%-50s" "$desc" | |
echo "$(date): $desc - Locating VM $vm_id" >> "$LOG_FILE" | |
# Find which node the VM is on | |
local vm_node=$(get_vm_node $vm_id) | |
if [ -z "$vm_node" ]; then | |
echo "[SKIPPED] VM $vm_id does not exist" | |
echo "$(date): $desc - VM $vm_id does not exist. Skipping." >> "$LOG_FILE" | |
return 0 | |
fi | |
echo "$(date): $desc - VM $vm_id found on node $vm_node" >> "$LOG_FILE" | |
# Check SSH connectivity to the node | |
check_ssh_connectivity "$vm_node" | |
# Check VM status and stop if running via SSH | |
local vm_status=$(ssh -o StrictHostKeyChecking=no "$vm_node" "qm status $vm_id" 2>/dev/null | awk '{print $2}' || echo "unknown") | |
if [ "$vm_status" = "running" ]; then | |
echo "$(date): $desc - VM $vm_id is running on $vm_node, stopping first" >> "$LOG_FILE" | |
ssh -o StrictHostKeyChecking=no "$vm_node" "qm stop $vm_id" >> "$LOG_FILE" 2>&1 | |
sleep 3 # Give VM time to stop gracefully | |
fi | |
local cmd="ssh -o StrictHostKeyChecking=no $vm_node 'qm destroy $vm_id --purge'" | |
echo "$(date): $desc - COMMAND: $cmd" >> "$LOG_FILE" | |
bash -c "$cmd" >> "$LOG_FILE" 2>&1 & | |
local pid=$! | |
spinner $pid | |
wait $pid | |
local status=$? | |
if [ $status -eq 0 ]; then | |
echo "[DONE]" | |
else | |
echo "[FAILED] - Check $LOG_FILE for details" | |
exit 1 | |
fi | |
} | |
# Function to wait for VM to be completely stopped | |
# | |
# Waits for a VM to reach 'stopped' status with timeout to ensure | |
# it's safe to perform disk operations. | |
# | |
# Arguments: | |
# $1 - VM ID | |
# $2 - Node where VM is located | |
# $3 - Timeout in seconds (optional, defaults to 30) | |
# | |
# Returns: 0 if VM is stopped, 1 if timeout or error | |
wait_for_vm_stopped() { | |
local vm_id=$1 | |
local vm_node=$2 | |
local timeout=${3:-30} | |
local elapsed=0 | |
echo "$(date): Waiting for VM $vm_id to stop completely (timeout: ${timeout}s)..." >> "$LOG_FILE" | |
while [ $elapsed -lt $timeout ]; do | |
local vm_status=$(ssh -o StrictHostKeyChecking=no "$vm_node" "qm status $vm_id" 2>/dev/null | awk '{print $2}' || echo "unknown") | |
if [ "$vm_status" = "stopped" ]; then | |
echo "$(date): VM $vm_id is now stopped" >> "$LOG_FILE" | |
return 0 | |
fi | |
sleep 2 | |
elapsed=$((elapsed + 2)) | |
done | |
echo "$(date): Timeout waiting for VM $vm_id to stop" >> "$LOG_FILE" | |
return 1 | |
} | |
# Function to get VM disk configuration from Proxmox | |
# | |
# Queries Proxmox to get the disk configuration for a VM, | |
# identifying the primary boot disk (usually ide0, scsi0, or virtio0). | |
# | |
# Arguments: | |
# $1 - VM ID | |
# $2 - Node where VM is located | |
# | |
# Returns: Outputs the primary disk identifier (e.g., "scsi0", "virtio0") | |
get_vm_primary_disk() { | |
local vm_id=$1 | |
local vm_node=$2 | |
# Get VM configuration and find the primary disk | |
local disk_info=$(ssh -o StrictHostKeyChecking=no "$vm_node" "qm config $vm_id" 2>/dev/null | grep -E '^(ide0|scsi0|virtio0|sata0):' | head -1 | cut -d: -f1) | |
if [[ -z "$disk_info" ]]; then | |
echo "No primary disk found for VM $vm_id" >&2 | |
return 1 | |
fi | |
echo "$disk_info" | |
} | |
# Function to parse disk configuration and extract storage and size | |
# | |
# Parses the disk configuration line to extract storage pool and disk size. | |
# Handles various Proxmox disk formats. | |
# | |
# Arguments: | |
# $1 - Full disk configuration line (e.g., "scsi0: vmdata:vm-1211-disk-0,size=10G") | |
# | |
# Returns: Outputs "storage:size" format (e.g., "vmdata:10G") | |
parse_disk_config() { | |
local disk_config="$1" | |
# Extract the part after the colon and first comma | |
# Format examples: | |
# scsi0: vmdata:vm-1211-disk-0,size=10G | |
# scsi0: local-lvm:vm-1211-disk-0,size=32G | |
local config_part=$(echo "$disk_config" | cut -d: -f2- | sed 's/^[[:space:]]*//') | |
# Extract storage (part before first colon in config_part) | |
local storage=$(echo "$config_part" | cut -d: -f1) | |
# Extract size (look for size= parameter) | |
local size=$(echo "$config_part" | sed -n 's/.*size=\([^,]*\).*/\1/p') | |
if [[ -z "$storage" || -z "$size" ]]; then | |
echo "Failed to parse storage ($storage) or size ($size) from: $disk_config" >&2 | |
return 1 | |
fi | |
echo "$storage:$size" | |
} | |
# Function to reset/format VM primary disk | |
# | |
# Stops the VM if running, waits for complete shutdown, removes the primary disk, | |
# and recreates it with the same size but completely empty/formatted. | |
# | |
# Arguments: | |
# $1 - VM ID to reset disk | |
# $2 - VM name for logging purposes | |
# | |
# Returns: 0 if successful, exits with status 1 if reset fails | |
reset_vm_disk() { | |
local vm_id=$1 | |
local vm_name=$2 | |
local desc="Resetting disk for ${vm_name} (ID: ${vm_id})..." | |
printf "%-50s" "$desc" | |
echo "$(date): $desc - Locating VM $vm_id" >> "$LOG_FILE" | |
# Find which node the VM is on | |
local vm_node=$(get_vm_node $vm_id) | |
if [ -z "$vm_node" ]; then | |
echo "[SKIPPED] VM $vm_id does not exist" | |
echo "$(date): $desc - VM $vm_id does not exist. Skipping." >> "$LOG_FILE" | |
return 0 | |
fi | |
echo "$(date): $desc - VM $vm_id found on node $vm_node" >> "$LOG_FILE" | |
# Check SSH connectivity to the node | |
check_ssh_connectivity "$vm_node" | |
# Check VM status and stop if running | |
local vm_status=$(ssh -o StrictHostKeyChecking=no "$vm_node" "qm status $vm_id" 2>/dev/null | awk '{print $2}' || echo "unknown") | |
if [ "$vm_status" = "running" ]; then | |
echo "$(date): $desc - VM $vm_id is running on $vm_node, stopping first" >> "$LOG_FILE" | |
ssh -o StrictHostKeyChecking=no "$vm_node" "qm stop $vm_id" >> "$LOG_FILE" 2>&1 | |
# Wait for VM to be completely stopped | |
if ! wait_for_vm_stopped "$vm_id" "$vm_node" 30; then | |
echo "[FAILED] - VM did not stop within timeout" | |
echo "$(date): $desc - VM $vm_id did not stop within timeout" >> "$LOG_FILE" | |
exit 1 | |
fi | |
elif [ "$vm_status" != "stopped" ]; then | |
echo "[FAILED] - VM status is '$vm_status', expected 'stopped' or 'running'" | |
echo "$(date): $desc - VM $vm_id has unexpected status: $vm_status" >> "$LOG_FILE" | |
exit 1 | |
fi | |
# Get primary disk information | |
local primary_disk=$(get_vm_primary_disk "$vm_id" "$vm_node") | |
if [[ -z "$primary_disk" ]]; then | |
echo "[FAILED] - Cannot identify primary disk" | |
echo "$(date): $desc - Failed to identify primary disk for VM $vm_id" >> "$LOG_FILE" | |
exit 1 | |
fi | |
echo "$(date): $desc - Primary disk identified as $primary_disk" >> "$LOG_FILE" | |
# Get current disk configuration to preserve settings | |
local disk_config=$(ssh -o StrictHostKeyChecking=no "$vm_node" "qm config $vm_id | grep '^$primary_disk:'" 2>/dev/null) | |
if [[ -z "$disk_config" ]]; then | |
echo "[FAILED] - Cannot retrieve disk configuration" | |
echo "$(date): $desc - Failed to retrieve disk configuration for $primary_disk" >> "$LOG_FILE" | |
exit 1 | |
fi | |
echo "$(date): $desc - Current disk config: $disk_config" >> "$LOG_FILE" | |
# Parse storage and size from disk configuration | |
local storage_size=$(parse_disk_config "$disk_config") | |
if [[ $? -ne 0 || -z "$storage_size" ]]; then | |
echo "[FAILED] - Cannot parse disk configuration" | |
echo "$(date): $desc - Failed to parse disk configuration: $disk_config" >> "$LOG_FILE" | |
exit 1 | |
fi | |
echo "$(date): $desc - Parsed storage:size as: $storage_size" >> "$LOG_FILE" | |
# Create command to reset disk (remove and recreate) | |
local cmd="ssh -o StrictHostKeyChecking=no $vm_node 'qm set $vm_id --delete $primary_disk && qm set $vm_id --$primary_disk $storage_size'" | |
echo "$(date): $desc - COMMAND: $cmd" >> "$LOG_FILE" | |
bash -c "$cmd" >> "$LOG_FILE" 2>&1 & | |
local pid=$! | |
spinner $pid | |
wait $pid | |
local status=$? | |
if [ $status -eq 0 ]; then | |
echo "[DONE]" | |
echo "$(date): $desc - Successfully reset disk for VM $vm_id" >> "$LOG_FILE" | |
else | |
echo "[FAILED] - Check $LOG_FILE for details" | |
exit 1 | |
fi | |
} | |
# Function to filter VM IDs based on provided list | |
# | |
# Filters the complete list of K3s VM IDs to only include those | |
# specified in the --vmid parameter. | |
# | |
# Arguments: | |
# $1 - Space-separated string of all available VM IDs | |
# $2 - Comma-separated string of requested VM IDs | |
# | |
# Returns: Outputs space-separated string of valid VM IDs | |
filter_vm_ids() { | |
local all_ids_str=$1 | |
local requested_ids_str=$2 | |
# Convert comma-separated to array | |
IFS=',' read -ra requested_ids <<< "$requested_ids_str" | |
# Convert space-separated to array | |
read -ra all_ids <<< "$all_ids_str" | |
local filtered_ids=() | |
for requested_id in "${requested_ids[@]}"; do | |
# Remove any whitespace | |
requested_id=$(echo "$requested_id" | xargs) | |
# Check if this ID exists in all_ids | |
for all_id in "${all_ids[@]}"; do | |
if [[ "$all_id" == "$requested_id" ]]; then | |
filtered_ids+=("$requested_id") | |
break | |
fi | |
done | |
done | |
# Output filtered IDs as space-separated string | |
echo "${filtered_ids[*]}" | |
} | |
# Function to get confirmation from user | |
# | |
# Prompts the user for confirmation before proceeding with VM destruction or disk reset. | |
# Provides a safety mechanism to prevent accidental operations. | |
# Dynamically shows only the VMs that will actually be affected. | |
# | |
# Arguments: | |
# $1 - Space-separated list of VM IDs that will be affected | |
# | |
# Returns: Exits with status 1 if user doesn't confirm | |
get_confirmation() { | |
local vm_ids_to_process=($1) | |
# Skip confirmation if in force mode | |
if [ "$FORCE_MODE" = true ]; then | |
if [ "$RESET_DISK_MODE" = true ]; then | |
echo "Force mode enabled - skipping confirmation for disk reset" | |
else | |
echo "Force mode enabled - skipping confirmation" | |
fi | |
return 0 | |
fi | |
echo "" | |
if [ "$RESET_DISK_MODE" = true ]; then | |
echo "WARNING: This will RESET/FORMAT the primary disks of the following VMs:" | |
echo "All data on the primary disk will be PERMANENTLY LOST!" | |
else | |
echo "WARNING: This will PERMANENTLY DESTROY the following VMs:" | |
fi | |
# Group VMs by type for better display | |
local servers=() | |
local agents=() | |
local storage=() | |
local others=() | |
for vm_id in "${vm_ids_to_process[@]}"; do | |
local vm_info=$(get_vm_info_from_proxmox "$vm_id") | |
if [[ "$vm_info" =~ \(server\) ]]; then | |
servers+=("$vm_info") | |
elif [[ "$vm_info" =~ \(agent\) ]]; then | |
agents+=("$vm_info") | |
elif [[ "$vm_info" =~ \(storage\) ]]; then | |
storage+=("$vm_info") | |
else | |
others+=("$vm_info") | |
fi | |
done | |
# Display grouped VMs | |
if [[ ${#servers[@]} -gt 0 ]]; then | |
echo " Server VMs: ${servers[*]}" | |
fi | |
if [[ ${#agents[@]} -gt 0 ]]; then | |
echo " Agent VMs: ${agents[*]}" | |
fi | |
if [[ ${#storage[@]} -gt 0 ]]; then | |
echo " Storage VMs: ${storage[*]}" | |
fi | |
if [[ ${#others[@]} -gt 0 ]]; then | |
echo " Other VMs: ${others[*]}" | |
fi | |
echo "" | |
if [ "$RESET_DISK_MODE" = true ]; then | |
echo "This will erase all data on the primary disk of these VMs!" | |
echo "The VMs will remain but their disks will be completely empty!" | |
else | |
echo "This action CANNOT be undone!" | |
fi | |
echo "" | |
if [ "$RESET_DISK_MODE" = true ]; then | |
read -p "Are you absolutely sure you want to reset these VM disks? (type 'RESET' to confirm): " confirmation | |
if [ "$confirmation" != "RESET" ]; then | |
echo "Disk reset cancelled by user." | |
exit 1 | |
fi | |
echo "" | |
echo "Proceeding with VM disk reset..." | |
else | |
read -p "Are you absolutely sure you want to proceed? (type 'DESTROY' to confirm): " confirmation | |
if [ "$confirmation" != "DESTROY" ]; then | |
echo "Destruction cancelled by user." | |
exit 1 | |
fi | |
echo "" | |
echo "Proceeding with VM destruction..." | |
fi | |
} | |
# Execute preflight checks | |
preflight_checks | |
# Get all K3s VM IDs from Proxmox cluster | |
all_vm_ids_str=$(get_all_k3s_vm_ids) | |
all_vm_ids=($(echo "$all_vm_ids_str")) | |
# Check if any K3s VMs were found | |
if [[ ${#all_vm_ids[@]} -eq 0 || ( ${#all_vm_ids[@]} -eq 1 && -z "${all_vm_ids[0]}" ) ]]; then | |
echo "No K3s VMs found in the Proxmox cluster." | |
echo "VMs must be tagged with 'k3s-server', 'k3s-agent', or 'k3s-storage' to be managed by this script." | |
exit 0 | |
fi | |
# Filter VM IDs if --vmid is provided | |
if [[ -n "$VM_IDS" ]]; then | |
filtered_vm_ids_str=$(filter_vm_ids "$all_vm_ids_str" "$VM_IDS") | |
filtered_vm_ids=($(echo "$filtered_vm_ids_str")) | |
# Check if any valid VM IDs were found | |
if [[ ${#filtered_vm_ids[@]} -eq 0 || ( ${#filtered_vm_ids[@]} -eq 1 && -z "${filtered_vm_ids[0]}" ) ]]; then | |
echo "Error: No valid VM IDs found in the provided list: $VM_IDS" >&2 | |
echo "Valid K3s VM IDs are: ${all_vm_ids[*]}" >&2 | |
exit 1 | |
fi | |
if [ "$DESTROY_MODE" = true ]; then | |
echo "Filtering VMs to destroy: ${filtered_vm_ids[*]} (from list of ${#all_vm_ids[@]} total VMs)" | |
else | |
echo "Filtering VMs to reset disks: ${filtered_vm_ids[*]} (from list of ${#all_vm_ids[@]} total VMs)" | |
fi | |
all_vm_ids=("${filtered_vm_ids[@]}") | |
else | |
if [ "$DESTROY_MODE" = true ]; then | |
echo "Found ${#all_vm_ids[@]} VMs to destroy: ${all_vm_ids[*]}" | |
else | |
echo "Found ${#all_vm_ids[@]} VMs to reset disks: ${all_vm_ids[*]}" | |
fi | |
fi | |
# Get user confirmation with the actual VMs to be destroyed | |
get_confirmation "${all_vm_ids[*]}" | |
# Create output log file | |
LOG_FILE="vm_destruction_output.log" | |
if ! touch "$LOG_FILE" 2>/dev/null; then | |
echo "Error: Cannot write to log file $LOG_FILE. Check permissions or disk space." >&2 | |
exit 1 | |
fi | |
if [ "$RESET_DISK_MODE" = true ]; then | |
echo "$(date): Starting VM disk reset" > "$LOG_FILE" | |
else | |
echo "$(date): Starting VM destruction" > "$LOG_FILE" | |
fi | |
# Process VMs based on mode (destroy or reset disk) | |
if [ "$RESET_DISK_MODE" = true ]; then | |
echo "Resetting disks for ${#all_vm_ids[@]} VMs..." | |
for vm_id in "${all_vm_ids[@]}"; do | |
if [[ -n "$vm_id" ]]; then | |
vm_info=$(get_vm_info_from_proxmox "$vm_id") | |
vm_name=$(echo "$vm_info" | sed 's/ (.*)$//') # Extract just the name part | |
reset_vm_disk "$vm_id" "$vm_name" | |
fi | |
done | |
echo "===== VM Disk Reset Complete =====" | |
echo "All specified K3s VM disks have been reset successfully." | |
echo "$(date): VM Disk Reset Complete - All specified K3s VM primary disks have been reset successfully." >> "$LOG_FILE" | |
elif [ "$DESTROY_MODE" = true ]; then | |
# Destroy VMs dynamically based on the filtered list | |
echo "Destroying ${#all_vm_ids[@]} VMs..." | |
for vm_id in "${all_vm_ids[@]}"; do | |
if [[ -n "$vm_id" ]]; then | |
vm_info=$(get_vm_info_from_proxmox "$vm_id") | |
vm_name=$(echo "$vm_info" | sed 's/ (.*)$//') # Extract just the name part | |
destroy_vm_with_check "$vm_id" "$vm_name" | |
fi | |
done | |
echo "===== VM Destruction Complete =====" | |
echo "All specified K3s VMs have been destroyed successfully." | |
echo "$(date): VM Destruction Complete - All specified K3s VMs have been destroyed successfully." >> "$LOG_FILE" | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment