From d336f36929e325ae0c9d2d931045390e664f020a Mon Sep 17 00:00:00 2001 From: kristof Date: Sun, 15 Jun 2025 19:31:47 +0200 Subject: [PATCH] ... --- tools/ubuntu_vm_delete.sh | 147 +++++++++++++++++++++++++---------- tools/ubuntu_vm_start.sh | 156 ++++++++++++++++++++++++++++---------- 2 files changed, 225 insertions(+), 78 deletions(-) diff --git a/tools/ubuntu_vm_delete.sh b/tools/ubuntu_vm_delete.sh index 5a28404..fce8889 100755 --- a/tools/ubuntu_vm_delete.sh +++ b/tools/ubuntu_vm_delete.sh @@ -1,7 +1,7 @@ #!/bin/bash # Ubuntu VM Delete Script with Comprehensive Cleanup -# Usage: ubuntu_vm_delete.sh +# Usage: ubuntu_vm_delete.sh # Use 'all' to delete all VMs set -e @@ -39,15 +39,18 @@ info() { show_usage() { echo "Ubuntu VM Delete Script" echo "" - echo "Usage: $0 " + echo "Usage: $0 " echo "" echo "Arguments:" - echo " vm_name - Name of the VM to delete" + echo " vm_number - Number of the VM to delete (1-200)" echo " all - Delete ALL VMs (use with extreme caution)" + echo " list - List all existing VMs" echo "" echo "Examples:" - echo " $0 test-vm # Delete specific VM" + echo " $0 1 # Delete VM number 1" + echo " $0 42 # Delete VM number 42" echo " $0 all # Delete all VMs" + echo " $0 list # List all VMs" echo "" echo "This script will:" echo " - Stop running VM processes" @@ -70,9 +73,15 @@ fi VM_TARGET="$1" -# Validate VM name (unless it's 'all') -if [ "$VM_TARGET" != "all" ] && [[ "$VM_TARGET" =~ [^a-zA-Z0-9_-] ]]; then - error "VM name can only contain alphanumeric characters, hyphens, and underscores" +# Validate VM number (unless it's 'all' or 'list') +if [ "$VM_TARGET" != "all" ] && [ "$VM_TARGET" != "list" ]; then + if ! [[ "$VM_TARGET" =~ ^[0-9]+$ ]]; then + error "VM number must be a number" + fi + + if [ "$VM_TARGET" -lt 1 ] || [ "$VM_TARGET" -gt 200 ]; then + error "VM number must be between 1 and 200" + fi fi # Check if VMs directory exists @@ -159,7 +168,7 @@ cleanup_network() { # Function to clean up VM files and sockets cleanup_vm_files() { local vm_socket="$1" - local vm_name="$2" + local vm_number="$2" # Remove VM socket if [ -n "$vm_socket" ] && [ -e "$vm_socket" ]; then @@ -168,28 +177,28 @@ cleanup_vm_files() { fi # Remove log files - local log_file="/tmp/cloud-hypervisor-$vm_name.log" + local log_file="/tmp/cloud-hypervisor-vm$vm_number.log" if [ -f "$log_file" ]; then log "Removing VM log file '$log_file'" rm -f "$log_file" || warn "Failed to remove log file '$log_file'" fi # Remove any other temporary files - rm -f "/tmp/cloud-hypervisor-$vm_name"* 2>/dev/null || true + rm -f "/tmp/cloud-hypervisor-vm$vm_number"* 2>/dev/null || true } # Function to delete a single VM delete_single_vm() { - local vm_name="$1" - local vm_dir="$VMS_SUBVOL/$vm_name" + local vm_number="$1" + local vm_dir="$VMS_SUBVOL/vm$vm_number" local vm_info_file="$vm_dir/vm-info.txt" if [ ! -d "$vm_dir" ]; then - warn "VM '$vm_name' not found at '$vm_dir'" + warn "VM number '$vm_number' not found at '$vm_dir'" return 1 fi - log "Deleting VM: $vm_name" + log "Deleting VM number: $vm_number" # Initialize variables with defaults local VM_PID="" @@ -210,33 +219,33 @@ delete_single_vm() { else warn "VM info file not found at '$vm_info_file', proceeding with best-effort cleanup" # Try to guess some values - TAP_NAME="tap-$vm_name" + TAP_NAME="tap-vm$vm_number" BRIDGE_NAME="br0" - VM_SOCKET="/tmp/cloud-hypervisor-$vm_name.sock" + VM_SOCKET="/tmp/cloud-hypervisor-vm$vm_number.sock" fi # Stop VM process if [ -n "$VM_PID" ]; then - stop_vm_process "$VM_PID" "$vm_name" + stop_vm_process "$VM_PID" "vm$vm_number" else # Try to find the process by name - local found_pids=$(pgrep -f "cloud-hypervisor.*$vm_name" 2>/dev/null || echo "") + local found_pids=$(pgrep -f "cloud-hypervisor.*vm$vm_number" 2>/dev/null || echo "") if [ -n "$found_pids" ]; then warn "Found VM process(es) by name: $found_pids" # Process each PID separately echo "$found_pids" | while read -r pid; do if [ -n "$pid" ]; then - stop_vm_process "$pid" "$vm_name" + stop_vm_process "$pid" "vm$vm_number" fi done fi fi # Clean up network interfaces - cleanup_network "$TAP_NAME" "$BRIDGE_NAME" "$vm_name" + cleanup_network "$TAP_NAME" "$BRIDGE_NAME" "vm$vm_number" # Clean up VM files and sockets - cleanup_vm_files "$VM_SOCKET" "$vm_name" + cleanup_vm_files "$VM_SOCKET" "$vm_number" # Verify the directory is a btrfs subvolume before attempting deletion if btrfs subvolume show "$vm_dir" &>/dev/null; then @@ -253,7 +262,7 @@ delete_single_vm() { log "Directory '$vm_dir' removed successfully" fi - log "VM '$vm_name' deleted successfully" + log "VM number '$vm_number' deleted successfully" return 0 } @@ -265,33 +274,93 @@ list_all_vms() { return 0 fi - for vm_dir in "$VMS_SUBVOL"/*; do + for vm_dir in "$VMS_SUBVOL"/vm*; do if [ -d "$vm_dir" ]; then local vm_name=$(basename "$vm_dir") - vm_list+=("$vm_name") + # Extract number from vm directory name (vm1, vm2, etc.) + local vm_number=${vm_name#vm} + if [[ "$vm_number" =~ ^[0-9]+$ ]]; then + vm_list+=("$vm_number") + fi fi done - printf '%s\n' "${vm_list[@]}" + # Sort numerically + if [ ${#vm_list[@]} -gt 0 ]; then + printf '%s\n' "${vm_list[@]}" | sort -n + fi } -# Main deletion logic -if [ "$VM_TARGET" = "all" ]; then +# Function to show detailed VM list +show_vm_list() { + local vm_numbers=($(list_all_vms)) + + if [ ${#vm_numbers[@]} -eq 0 ]; then + info "No VMs found" + return 0 + fi + + echo "" + echo "Existing VMs:" + echo "=============" + + for vm_number in "${vm_numbers[@]}"; do + local vm_dir="$VMS_SUBVOL/vm$vm_number" + local vm_info_file="$vm_dir/vm-info.txt" + local vm_ip="192.168.100.$vm_number" + + echo -n "VM $vm_number: " + + # Check if VM is running + if [ -f "$vm_info_file" ]; then + local VM_PID="" + local VM_NAME="" + local STARTED="" + source "$vm_info_file" 2>/dev/null || true + + if [ -n "$VM_PID" ] && kill -0 "$VM_PID" 2>/dev/null; then + echo -e "${GREEN}RUNNING${NC} (PID: $VM_PID, IP: $vm_ip)" + if [ -n "$VM_NAME" ]; then + echo " Name: $VM_NAME" + fi + if [ -n "$STARTED" ]; then + echo " Started: $STARTED" + fi + else + echo -e "${YELLOW}STOPPED${NC} (IP: $vm_ip)" + if [ -n "$VM_NAME" ]; then + echo " Name: $VM_NAME" + fi + fi + else + echo -e "${RED}UNKNOWN${NC} (no info file)" + fi + done + echo "" +} + +# Main logic +if [ "$VM_TARGET" = "list" ]; then + # List all VMs + show_vm_list + exit 0 + +elif [ "$VM_TARGET" = "all" ]; then # Delete all VMs warn "You are about to delete ALL VMs!" echo "" # List all VMs - vm_list=($(list_all_vms)) + vm_numbers=($(list_all_vms)) - if [ ${#vm_list[@]} -eq 0 ]; then + if [ ${#vm_numbers[@]} -eq 0 ]; then info "No VMs found to delete" exit 0 fi - echo "VMs to be deleted:" - for vm in "${vm_list[@]}"; do - echo " - $vm" + echo "VM numbers to be deleted:" + for vm_number in "${vm_numbers[@]}"; do + echo " - VM $vm_number (IP: 192.168.100.$vm_number)" done echo "" @@ -301,8 +370,8 @@ if [ "$VM_TARGET" = "all" ]; then success_count=0 failure_count=0 - for vm_name in "${vm_list[@]}"; do - if delete_single_vm "$vm_name"; then + for vm_number in "${vm_numbers[@]}"; do + if delete_single_vm "$vm_number"; then success_count=$((success_count + 1)) else failure_count=$((failure_count + 1)) @@ -338,14 +407,14 @@ if [ "$VM_TARGET" = "all" ]; then else # Delete single VM - vm_name="$VM_TARGET" + vm_number="$VM_TARGET" - if [ ! -d "$VMS_SUBVOL/$vm_name" ]; then - error "VM '$vm_name' not found" + if [ ! -d "$VMS_SUBVOL/vm$vm_number" ]; then + error "VM number '$vm_number' not found" fi - log "Deleting VM '$vm_name' without confirmation..." - delete_single_vm "$vm_name" + log "Deleting VM number '$vm_number' without confirmation..." + delete_single_vm "$vm_number" fi log "VM deletion script completed successfully" \ No newline at end of file diff --git a/tools/ubuntu_vm_start.sh b/tools/ubuntu_vm_start.sh index 30783fe..6098cb4 100755 --- a/tools/ubuntu_vm_start.sh +++ b/tools/ubuntu_vm_start.sh @@ -1,7 +1,7 @@ #!/bin/bash # Ubuntu VM Start Script with Cloud Hypervisor and Btrfs Thin Provisioning -# Usage: ubuntu_vm_start.sh $name $mem $cores +# Usage: ubuntu_vm_start.sh $vm_number $name $mem $cores set -e @@ -37,9 +37,9 @@ warn() { error() { echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}" - # If VM_NAME is set and we're in VM creation phase, clean up - if [ -n "$VM_NAME" ] && [ -n "$VM_PID" ]; then - cleanup_failed_vm "$VM_NAME" + # If VM_NUMBER is set and we're in VM creation phase, clean up + if [ -n "$VM_NUMBER" ] && [ -n "$VM_PID" ]; then + cleanup_failed_vm "$VM_NUMBER" fi exit 1 @@ -96,14 +96,14 @@ test_process_running() { # Cleanup function for failed VM creation cleanup_failed_vm() { - local vm_name="$1" + local vm_number="$1" warn "VM creation failed, cleaning up..." # Call the delete script to clean up local delete_script="$(dirname "$0")/ubuntu_vm_delete.sh" if [ -f "$delete_script" ]; then log "Running cleanup script: $delete_script" - "$delete_script" "$vm_name" || warn "Cleanup script failed, manual cleanup may be required" + "$delete_script" "$vm_number" || warn "Cleanup script failed, manual cleanup may be required" else warn "Delete script not found at $delete_script, manual cleanup required" fi @@ -115,32 +115,77 @@ generate_password_hash() { python3 -c "import crypt; print(crypt.crypt('ubuntu', crypt.mksalt(crypt.METHOD_SHA512)))" } -# Wait for VM to boot and get IP +# Wait for VM to boot and verify static IP wait_for_vm_boot() { local vm_name="$1" - local max_wait=120 # 2 minutes + local expected_ip="$2" + local max_wait=120 # 3 minutes local count=0 - log "Waiting for VM '$vm_name' to boot and get IP address..." + log "Waiting for VM '$vm_name' to boot with static IP $expected_ip..." while [ $count -lt $max_wait ]; do - # Check if VM got an IP from DHCP - local vm_ip=$(arp -a | grep "192.168.100" | grep -v "192.168.100.1" | head -1 | sed 's/.*(\([^)]*\)).*/\1/') + # Check if VM process is still running + if ! kill -0 "$VM_PID" 2>/dev/null; then + error "VM process died while waiting for boot. Check log: $VM_LOG_FILE" + fi - if [ -n "$vm_ip" ] && [ "$vm_ip" != "192.168.100.1" ]; then - log "VM got IP address: $vm_ip" - echo "$vm_ip" + # Try to ping the expected static IP + if ping -c 1 -W 2 "$expected_ip" >/dev/null 2>&1; then + log "VM is responding at static IP address: $expected_ip" + echo "$expected_ip" return 0 fi - sleep 2 - count=$((count + 2)) - if [ $((count % 10)) -eq 0 ]; then + # Also check ARP table for our MAC address + local vm_ip=$(arp -a | grep "$VM_MAC" | sed 's/.*(\([^)]*\)).*/\1/' | head -1) + if [ -n "$vm_ip" ] && [ "$vm_ip" = "$expected_ip" ]; then + log "VM MAC address found in ARP table with expected IP: $expected_ip" + echo "$expected_ip" + return 0 + fi + + sleep 3 + count=$((count + 3)) + if [ $((count % 15)) -eq 0 ]; then info "Still waiting for VM to boot... ($count/$max_wait seconds)" + info "VM process PID $VM_PID is still running" + info "Expected static IP: $expected_ip" + # Show recent log entries + if [ -f "$VM_LOG_FILE" ]; then + info "Recent VM log entries:" + tail -3 "$VM_LOG_FILE" 2>/dev/null || true + fi fi done - warn "VM did not get an IP address within $max_wait seconds" + warn "VM did not respond at expected IP $expected_ip within $max_wait seconds" + warn "VM may still be booting - check manually with: ping $expected_ip" + return 1 +} + +# Test IP connectivity +test_ip_connectivity() { + local vm_ip="$1" + local max_attempts=10 + local attempt=1 + + log "Testing IP connectivity to $vm_ip..." + + while [ $attempt -le $max_attempts ]; do + info "Ping attempt $attempt/$max_attempts to $vm_ip" + + # Test ping connectivity with timeout + if ping -c 3 -W 2 "$vm_ip" >/dev/null 2>&1; then + log "✓ IP connectivity successful to $vm_ip" + return 0 + fi + + sleep 3 + attempt=$((attempt + 1)) + done + + error "✗ IP connectivity failed after $max_attempts attempts to $vm_ip" return 1 } @@ -175,15 +220,24 @@ if [ "$EUID" -ne 0 ]; then fi # Parse arguments -if [ $# -ne 3 ]; then - error "Usage: $0 " +if [ $# -ne 4 ]; then + error "Usage: $0 " fi -VM_NAME="$1" -MEMORY_MB="$2" -CPU_CORES="$3" +VM_NUMBER="$1" +VM_NAME="$2" +MEMORY_MB="$3" +CPU_CORES="$4" # Validate arguments +if ! [[ "$VM_NUMBER" =~ ^[0-9]+$ ]]; then + error "VM number must be a number" +fi + +if [ "$VM_NUMBER" -lt 1 ] || [ "$VM_NUMBER" -gt 200 ]; then + error "VM number must be between 1 and 200" +fi + if ! [[ "$MEMORY_MB" =~ ^[0-9]+$ ]]; then error "Memory must be a number (in MB)" fi @@ -196,7 +250,11 @@ if [[ "$VM_NAME" =~ [^a-zA-Z0-9_-] ]]; then error "VM name can only contain alphanumeric characters, hyphens, and underscores" fi -log "Starting VM: $VM_NAME with ${MEMORY_MB}MB RAM and $CPU_CORES CPU cores" +# Calculate static IP address based on VM number +VM_STATIC_IP="192.168.100.$VM_NUMBER" + +log "Starting VM: $VM_NAME (number $VM_NUMBER) with ${MEMORY_MB}MB RAM and $CPU_CORES CPU cores" +log "VM will be assigned static IP: $VM_STATIC_IP" # Comprehensive prerequisite checks log "Performing prerequisite checks..." @@ -278,8 +336,8 @@ test_step "VMs subvolume verification" "btrfs subvolume show '$VMS_SUBVOL' &>/de # Define paths BASE_IMAGE_PATH="$BASE_SUBVOL/${BASE_IMAGE_NAME}.raw" FIRMWARE_PATH="$BASE_SUBVOL/hypervisor-fw" -VM_SUBVOL_PATH="$VMS_SUBVOL/$VM_NAME" -VM_IMAGE_PATH="$VM_SUBVOL_PATH/${VM_NAME}.raw" +VM_SUBVOL_PATH="$VMS_SUBVOL/vm$VM_NUMBER" +VM_IMAGE_PATH="$VM_SUBVOL_PATH/vm$VM_NUMBER.raw" CLOUD_INIT_PATH="$VM_SUBVOL_PATH/cloud-init.img" # Download and prepare base image if it doesn't exist @@ -388,13 +446,19 @@ chpasswd: # SSH configuration ssh_authorized_keys: [] -# Network configuration to ensure DHCP works +# Network configuration with static IP network: version: 2 ethernets: eth0: - dhcp4: true - dhcp-identifier: mac + dhcp4: false + addresses: + - $VM_STATIC_IP/24 + gateway4: 192.168.100.1 + nameservers: + addresses: + - 8.8.8.8 + - 8.8.4.4 # Package updates and installs package_update: true @@ -492,6 +556,7 @@ else fi # Create TAP interface for the VM +TAP_NAME="tap-vm$VM_NUMBER" log "Creating TAP interface $TAP_NAME..." # Remove existing TAP interface if it exists @@ -546,8 +611,8 @@ fi # Start the VM with Cloud Hypervisor log "Starting VM $VM_NAME..." -VM_SOCKET="/tmp/cloud-hypervisor-$VM_NAME.sock" -VM_LOG_FILE="/tmp/cloud-hypervisor-$VM_NAME.log" +VM_SOCKET="/tmp/cloud-hypervisor-vm$VM_NUMBER.sock" +VM_LOG_FILE="/tmp/cloud-hypervisor-vm$VM_NUMBER.log" # Remove existing socket and log file if they exist rm -f "$VM_SOCKET" "$VM_LOG_FILE" @@ -617,6 +682,7 @@ log "✓ VM initialization completed" log "Saving VM configuration..." VM_INFO_FILE="$VM_SUBVOL_PATH/vm-info.txt" cat > "$VM_INFO_FILE" << EOF +VM_NUMBER=$VM_NUMBER VM_NAME=$VM_NAME VM_PID=$VM_PID VM_SOCKET=$VM_SOCKET @@ -628,6 +694,7 @@ VM_IMAGE_PATH=$VM_IMAGE_PATH CLOUD_INIT_PATH=$CLOUD_INIT_PATH VM_MAC=$VM_MAC VM_LOG_FILE=$VM_LOG_FILE +VM_STATIC_IP=$VM_STATIC_IP STARTED="$(date '+%Y-%m-%d %H:%M:%S')" EOF @@ -645,16 +712,27 @@ cleanup_on_exit() { rm -f "$VM_SOCKET" } -# Test VM boot and SSH connectivity -log "Testing VM boot and SSH connectivity..." +# Test VM boot and connectivity +log "Testing VM boot and connectivity..." -# Wait for VM to boot and get IP -VM_IP=$(wait_for_vm_boot "$VM_NAME") +# Show the static IP that will be used +log "VM $VM_NAME will use static IP address: $VM_STATIC_IP" + +# Wait for VM to boot and verify static IP +VM_IP=$(wait_for_vm_boot "$VM_NAME" "$VM_STATIC_IP") if [ $? -ne 0 ] || [ -z "$VM_IP" ]; then - error "VM failed to boot or get IP address. Check log: $VM_LOG_FILE" + error "VM failed to boot or respond at static IP $VM_STATIC_IP. Check log: $VM_LOG_FILE" fi -log "✓ VM booted successfully and got IP: $VM_IP" +log "✓ VM booted successfully and is using IP: $VM_IP" + +# Test IP connectivity first +log "Testing IP connectivity before SSH..." +if test_ip_connectivity "$VM_IP"; then + log "✓ IP connectivity test passed for $VM_IP" +else + error "IP connectivity test failed for $VM_IP" +fi # Test SSH connectivity if test_ssh_connection "$VM_IP"; then @@ -667,12 +745,12 @@ if test_ssh_connection "$VM_IP"; then info "VM $VM_NAME is ready for use!" info "Connect via SSH: ssh ubuntu@$VM_IP" info "Default password: ubuntu (please change after first login)" - info "To stop the VM: sudo $(dirname "$0")/ubuntu_vm_delete.sh $VM_NAME" + info "To stop the VM: sudo $(dirname "$0")/ubuntu_vm_delete.sh $VM_NUMBER" echo "" # Don't set trap for successful VMs - let them run log "VM $VM_NAME will continue running in the background." - log "Use 'sudo $(dirname "$0")/ubuntu_vm_delete.sh $VM_NAME' to stop and delete it." + log "Use 'sudo $(dirname "$0")/ubuntu_vm_delete.sh $VM_NUMBER' to stop and delete it." else error "SSH connectivity test failed. VM will be deleted for retry."