Compare commits
2 Commits
63f4f77366
...
3dbd0d0aea
Author | SHA1 | Date | |
---|---|---|---|
3dbd0d0aea | |||
d336f36929 |
61
tools/extract_kernel.sh
Executable file
61
tools/extract_kernel.sh
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Extract kernel and initrd from Ubuntu cloud image
|
||||
# This script ensures kernel boot works automatically
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
log() {
|
||||
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
BASE_SUBVOL="/var/lib/vms/base"
|
||||
BASE_IMAGE_PATH="$BASE_SUBVOL/ubuntu-24.04-server-cloudimg-amd64.raw"
|
||||
KERNEL_PATH="$BASE_SUBVOL/vmlinuz"
|
||||
INITRD_PATH="$BASE_SUBVOL/initrd.img"
|
||||
|
||||
# Check if kernel and initrd already exist
|
||||
if [ -f "$KERNEL_PATH" ] && [ -f "$INITRD_PATH" ]; then
|
||||
log "Kernel and initrd already extracted"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if base image exists
|
||||
if [ ! -f "$BASE_IMAGE_PATH" ]; then
|
||||
error "Base image not found at $BASE_IMAGE_PATH"
|
||||
fi
|
||||
|
||||
log "Extracting kernel and initrd from base image..."
|
||||
|
||||
# Create temporary mount point
|
||||
TEMP_MOUNT=$(mktemp -d)
|
||||
trap "umount '$TEMP_MOUNT' 2>/dev/null || true; losetup -d /dev/loop1 2>/dev/null || true; rmdir '$TEMP_MOUNT'" EXIT
|
||||
|
||||
# Mount the image
|
||||
losetup -P /dev/loop1 "$BASE_IMAGE_PATH"
|
||||
|
||||
# Mount the boot partition (partition 16 contains kernel/initrd)
|
||||
mount /dev/loop1p16 "$TEMP_MOUNT"
|
||||
|
||||
# Copy kernel and initrd
|
||||
if [ -f "$TEMP_MOUNT/vmlinuz-6.8.0-60-generic" ] && [ -f "$TEMP_MOUNT/initrd.img-6.8.0-60-generic" ]; then
|
||||
cp "$TEMP_MOUNT/vmlinuz-6.8.0-60-generic" "$KERNEL_PATH"
|
||||
cp "$TEMP_MOUNT/initrd.img-6.8.0-60-generic" "$INITRD_PATH"
|
||||
log "✓ Kernel and initrd extracted successfully"
|
||||
else
|
||||
error "Kernel or initrd not found in boot partition"
|
||||
fi
|
||||
|
||||
# Cleanup is handled by trap
|
||||
log "Kernel extraction completed"
|
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Ubuntu VM Delete Script with Comprehensive Cleanup
|
||||
# Usage: ubuntu_vm_delete.sh <vm_name|all>
|
||||
# Usage: ubuntu_vm_delete.sh <vm_number|all>
|
||||
# Use 'all' to delete all VMs
|
||||
|
||||
set -e
|
||||
@@ -39,15 +39,18 @@ info() {
|
||||
show_usage() {
|
||||
echo "Ubuntu VM Delete Script"
|
||||
echo ""
|
||||
echo "Usage: $0 <vm_name|all>"
|
||||
echo "Usage: $0 <vm_number|all|list>"
|
||||
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"
|
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Ubuntu VM Management Script
|
||||
# Usage: ubuntu_vm_manage.sh <command> [vm_name]
|
||||
# Usage: ubuntu_vm_manage.sh <command> [vm_number]
|
||||
|
||||
set -e
|
||||
|
||||
@@ -36,65 +36,73 @@ info() {
|
||||
show_usage() {
|
||||
echo "Ubuntu VM Management Script"
|
||||
echo ""
|
||||
echo "Usage: $0 <command> [vm_name]"
|
||||
echo "Usage: $0 <command> [vm_number]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " list - List all VMs and their status"
|
||||
echo " status <vm_name> - Show detailed status of a specific VM"
|
||||
echo " console <vm_name> - Connect to VM console (serial)"
|
||||
echo " ssh <vm_name> - SSH to VM (requires network setup)"
|
||||
echo " stop <vm_name> - Stop a running VM"
|
||||
echo " start <vm_name> - Start a stopped VM"
|
||||
echo " delete <vm_name> - Delete a VM completely"
|
||||
echo " ip <vm_name> - Show VM IP address"
|
||||
echo " logs <vm_name> - Show VM logs"
|
||||
echo " status <vm_number> - Show detailed status of a specific VM"
|
||||
echo " console <vm_number> - Connect to VM console (serial)"
|
||||
echo " ssh <vm_number> - SSH to VM (requires network setup)"
|
||||
echo " stop <vm_number> - Stop a running VM"
|
||||
echo " start <vm_number> - Start a stopped VM"
|
||||
echo " delete <vm_number> - Delete a VM completely"
|
||||
echo " ip <vm_number> - Show VM IP address"
|
||||
echo " logs <vm_number> - Show VM logs"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 list"
|
||||
echo " $0 status test-vm"
|
||||
echo " $0 console test-vm"
|
||||
echo " $0 ssh test-vm"
|
||||
echo " $0 status 1"
|
||||
echo " $0 console 1"
|
||||
echo " $0 ssh 1"
|
||||
}
|
||||
|
||||
list_vms() {
|
||||
log "Listing all VMs..."
|
||||
echo ""
|
||||
printf "%-15s %-10s %-8s %-15s %-20s\n" "VM NAME" "STATUS" "PID" "MEMORY" "STARTED"
|
||||
printf "%-15s %-10s %-8s %-15s %-20s\n" "-------" "------" "---" "------" "-------"
|
||||
printf "%-8s %-15s %-15s %-10s %-8s %-15s %-20s\n" "VM #" "IP ADDRESS" "VM NAME" "STATUS" "PID" "MEMORY" "STARTED"
|
||||
printf "%-8s %-15s %-15s %-10s %-8s %-15s %-20s\n" "----" "----------" "-------" "------" "---" "------" "-------"
|
||||
|
||||
if [ ! -d "$VMS_SUBVOL" ]; then
|
||||
warn "No VMs directory found at $VMS_SUBVOL"
|
||||
return
|
||||
fi
|
||||
|
||||
for vm_dir in "$VMS_SUBVOL"/*; do
|
||||
for vm_dir in "$VMS_SUBVOL"/vm*; do
|
||||
if [ -d "$vm_dir" ]; then
|
||||
vm_name=$(basename "$vm_dir")
|
||||
vm_info_file="$vm_dir/vm-info.txt"
|
||||
|
||||
if [ -f "$vm_info_file" ]; then
|
||||
# Safely source the file with error handling
|
||||
if source "$vm_info_file" 2>/dev/null; then
|
||||
# Check if VM is running
|
||||
if [ -n "$VM_PID" ] && kill -0 "$VM_PID" 2>/dev/null; then
|
||||
status="${GREEN}RUNNING${NC}"
|
||||
pid="$VM_PID"
|
||||
vm_dirname=$(basename "$vm_dir")
|
||||
# Extract number from vm directory name (vm1, vm2, etc.)
|
||||
vm_number=${vm_dirname#vm}
|
||||
if [[ "$vm_number" =~ ^[0-9]+$ ]]; then
|
||||
vm_ip="192.168.100.$vm_number"
|
||||
vm_info_file="$vm_dir/vm-info.txt"
|
||||
|
||||
if [ -f "$vm_info_file" ]; then
|
||||
# Safely source the file with error handling
|
||||
if source "$vm_info_file" 2>/dev/null; then
|
||||
# Check if VM is running
|
||||
if [ -n "$VM_PID" ] && kill -0 "$VM_PID" 2>/dev/null; then
|
||||
status="${GREEN}RUNNING${NC}"
|
||||
pid="$VM_PID"
|
||||
else
|
||||
status="${RED}STOPPED${NC}"
|
||||
pid="N/A"
|
||||
fi
|
||||
|
||||
# Handle missing or malformed STARTED field
|
||||
if [ -z "$STARTED" ]; then
|
||||
STARTED="Unknown"
|
||||
fi
|
||||
|
||||
# Use VM_NAME from config, fallback to vm number
|
||||
display_name="${VM_NAME:-vm$vm_number}"
|
||||
|
||||
printf "%-8s %-15s %-15s %-18s %-8s %-15s %-20s\n" "$vm_number" "$vm_ip" "$display_name" "$(printf "%b" "$status")" "$pid" "${MEMORY_MB}MB" "$STARTED"
|
||||
else
|
||||
status="${RED}STOPPED${NC}"
|
||||
pid="N/A"
|
||||
printf "%-8s %-15s %-15s %-18s %-8s %-15s %-20s\n" "$vm_number" "$vm_ip" "vm$vm_number" "$(printf "%b" "${RED}ERROR${NC}")" "N/A" "N/A" "Config Error"
|
||||
fi
|
||||
|
||||
# Handle missing or malformed STARTED field
|
||||
if [ -z "$STARTED" ]; then
|
||||
STARTED="Unknown"
|
||||
fi
|
||||
|
||||
printf "%-15s %-18s %-8s %-15s %-20s\n" "$vm_name" "$(printf "%b" "$status")" "$pid" "${MEMORY_MB}MB" "$STARTED"
|
||||
else
|
||||
printf "%-15s %-18s %-8s %-15s %-20s\n" "$vm_name" "$(printf "%b" "${RED}ERROR${NC}")" "N/A" "N/A" "Config Error"
|
||||
printf "%-8s %-15s %-15s %-18s %-8s %-15s %-20s\n" "$vm_number" "$vm_ip" "vm$vm_number" "$(printf "%b" "${YELLOW}UNKNOWN${NC}")" "N/A" "N/A" "N/A"
|
||||
fi
|
||||
else
|
||||
printf "%-15s %-18s %-8s %-15s %-20s\n" "$vm_name" "$(printf "%b" "${YELLOW}UNKNOWN${NC}")" "N/A" "N/A" "N/A"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -102,23 +110,30 @@ list_vms() {
|
||||
}
|
||||
|
||||
show_vm_status() {
|
||||
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"
|
||||
|
||||
# Validate VM number
|
||||
if ! [[ "$vm_number" =~ ^[0-9]+$ ]]; then
|
||||
error "VM number must be a number"
|
||||
fi
|
||||
|
||||
if [ ! -d "$vm_dir" ]; then
|
||||
error "VM '$vm_name' not found"
|
||||
error "VM number '$vm_number' not found"
|
||||
fi
|
||||
|
||||
if [ ! -f "$vm_info_file" ]; then
|
||||
error "VM info file not found for '$vm_name'"
|
||||
error "VM info file not found for VM number '$vm_number'"
|
||||
fi
|
||||
|
||||
source "$vm_info_file"
|
||||
|
||||
log "VM Status for: $vm_name"
|
||||
log "VM Status for VM number: $vm_number"
|
||||
echo ""
|
||||
echo "VM Name: $VM_NAME"
|
||||
echo "VM Number: $vm_number"
|
||||
echo "VM Name: ${VM_NAME:-vm$vm_number}"
|
||||
echo "Static IP: 192.168.100.$vm_number"
|
||||
echo "Memory: ${MEMORY_MB}MB"
|
||||
echo "CPU Cores: $CPU_CORES"
|
||||
echo "Started: $STARTED"
|
||||
@@ -152,21 +167,26 @@ show_vm_status() {
|
||||
}
|
||||
|
||||
connect_console() {
|
||||
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"
|
||||
|
||||
# Validate VM number
|
||||
if ! [[ "$vm_number" =~ ^[0-9]+$ ]]; then
|
||||
error "VM number must be a number"
|
||||
fi
|
||||
|
||||
if [ ! -f "$vm_info_file" ]; then
|
||||
error "VM '$vm_name' not found"
|
||||
error "VM number '$vm_number' not found"
|
||||
fi
|
||||
|
||||
source "$vm_info_file"
|
||||
|
||||
if [ -z "$VM_PID" ] || ! kill -0 "$VM_PID" 2>/dev/null; then
|
||||
error "VM '$vm_name' is not running"
|
||||
error "VM number '$vm_number' is not running"
|
||||
fi
|
||||
|
||||
info "Connecting to console for VM '$vm_name'"
|
||||
info "Connecting to console for VM number '$vm_number'"
|
||||
info "Press Ctrl+A then X to exit console"
|
||||
echo ""
|
||||
|
||||
@@ -186,39 +206,50 @@ connect_console() {
|
||||
}
|
||||
|
||||
ssh_to_vm() {
|
||||
local vm_name="$1"
|
||||
local vm_number="$1"
|
||||
local vm_ip="192.168.100.$vm_number"
|
||||
|
||||
warn "SSH connection requires:"
|
||||
warn "1. VM to be fully booted"
|
||||
warn "2. Network bridge configured with IP"
|
||||
warn "3. VM to have received IP via DHCP"
|
||||
echo ""
|
||||
info "To set up networking:"
|
||||
info "1. Configure bridge IP: ip addr add 192.168.100.1/24 dev br0"
|
||||
info "2. Enable IP forwarding: echo 1 > /proc/sys/net/ipv4/ip_forward"
|
||||
info "3. Set up NAT: iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -j MASQUERADE"
|
||||
echo ""
|
||||
# Validate VM number
|
||||
if ! [[ "$vm_number" =~ ^[0-9]+$ ]]; then
|
||||
error "VM number must be a number"
|
||||
fi
|
||||
|
||||
info "Attempting SSH connection to VM number $vm_number at $vm_ip"
|
||||
info "Default login: ubuntu / ubuntu"
|
||||
info "SSH command: ssh ubuntu@<vm-ip>"
|
||||
echo ""
|
||||
|
||||
# Try to SSH directly
|
||||
if command -v sshpass &>/dev/null; then
|
||||
info "Using sshpass for automatic login..."
|
||||
sshpass -p 'ubuntu' ssh -o StrictHostKeyChecking=no ubuntu@"$vm_ip"
|
||||
else
|
||||
info "Manual SSH connection (enter password 'ubuntu'):"
|
||||
ssh -o StrictHostKeyChecking=no ubuntu@"$vm_ip"
|
||||
fi
|
||||
}
|
||||
|
||||
stop_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"
|
||||
|
||||
# Validate VM number
|
||||
if ! [[ "$vm_number" =~ ^[0-9]+$ ]]; then
|
||||
error "VM number must be a number"
|
||||
fi
|
||||
|
||||
if [ ! -f "$vm_info_file" ]; then
|
||||
error "VM '$vm_name' not found"
|
||||
error "VM number '$vm_number' not found"
|
||||
fi
|
||||
|
||||
source "$vm_info_file"
|
||||
|
||||
if [ -z "$VM_PID" ] || ! kill -0 "$VM_PID" 2>/dev/null; then
|
||||
warn "VM '$vm_name' is not running"
|
||||
warn "VM number '$vm_number' is not running"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Stopping VM '$vm_name' (PID: $VM_PID)..."
|
||||
log "Stopping VM number '$vm_number' (PID: $VM_PID)..."
|
||||
|
||||
# Graceful shutdown first
|
||||
kill -TERM "$VM_PID" 2>/dev/null || true
|
||||
@@ -242,15 +273,20 @@ stop_vm() {
|
||||
rm -f "$VM_SOCKET"
|
||||
fi
|
||||
|
||||
log "VM '$vm_name' stopped"
|
||||
log "VM number '$vm_number' stopped"
|
||||
}
|
||||
|
||||
delete_vm() {
|
||||
local vm_name="$1"
|
||||
local vm_dir="$VMS_SUBVOL/$vm_name"
|
||||
local vm_number="$1"
|
||||
local vm_dir="$VMS_SUBVOL/vm$vm_number"
|
||||
|
||||
# Validate VM number
|
||||
if ! [[ "$vm_number" =~ ^[0-9]+$ ]]; then
|
||||
error "VM number must be a number"
|
||||
fi
|
||||
|
||||
if [ ! -d "$vm_dir" ]; then
|
||||
error "VM '$vm_name' not found"
|
||||
error "VM number '$vm_number' not found"
|
||||
fi
|
||||
|
||||
# Stop VM first if running
|
||||
@@ -258,45 +294,50 @@ delete_vm() {
|
||||
source "$vm_dir/vm-info.txt"
|
||||
if [ -n "$VM_PID" ] && kill -0 "$VM_PID" 2>/dev/null; then
|
||||
log "Stopping VM before deletion..."
|
||||
stop_vm "$vm_name"
|
||||
stop_vm "$vm_number"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Confirm deletion
|
||||
echo -e "${RED}WARNING: This will permanently delete VM '$vm_name' and all its data!${NC}"
|
||||
echo -e "${RED}WARNING: This will permanently delete VM number '$vm_number' and all its data!${NC}"
|
||||
read -p "Are you sure? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" = "yes" ]; then
|
||||
log "Deleting VM '$vm_name'..."
|
||||
log "Deleting VM number '$vm_number'..."
|
||||
btrfs subvolume delete "$vm_dir"
|
||||
log "VM '$vm_name' deleted successfully"
|
||||
log "VM number '$vm_number' deleted successfully"
|
||||
else
|
||||
info "Deletion cancelled"
|
||||
fi
|
||||
}
|
||||
|
||||
start_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"
|
||||
|
||||
# Validate VM number
|
||||
if ! [[ "$vm_number" =~ ^[0-9]+$ ]]; then
|
||||
error "VM number must be a number"
|
||||
fi
|
||||
|
||||
if [ ! -d "$vm_dir" ]; then
|
||||
error "VM '$vm_name' not found"
|
||||
error "VM number '$vm_number' not found"
|
||||
fi
|
||||
|
||||
if [ ! -f "$vm_info_file" ]; then
|
||||
error "VM info file not found for '$vm_name'"
|
||||
error "VM info file not found for VM number '$vm_number'"
|
||||
fi
|
||||
|
||||
source "$vm_info_file"
|
||||
|
||||
# Check if VM is already running
|
||||
if [ -n "$VM_PID" ] && kill -0 "$VM_PID" 2>/dev/null; then
|
||||
warn "VM '$vm_name' is already running (PID: $VM_PID)"
|
||||
warn "VM number '$vm_number' is already running (PID: $VM_PID)"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Starting VM '$vm_name'..."
|
||||
log "Starting VM number '$vm_number'..."
|
||||
|
||||
# Create TAP interface
|
||||
if ! ip link show "$TAP_NAME" &>/dev/null; then
|
||||
@@ -309,12 +350,17 @@ start_vm() {
|
||||
# Remove existing socket if it exists
|
||||
rm -f "$VM_SOCKET"
|
||||
|
||||
# Start Cloud Hypervisor in background
|
||||
# Start Cloud Hypervisor in background with kernel boot
|
||||
KERNEL_PATH="/var/lib/vms/base/vmlinuz"
|
||||
INITRD_PATH="/var/lib/vms/base/initrd.img"
|
||||
|
||||
cloud-hypervisor \
|
||||
--api-socket "$VM_SOCKET" \
|
||||
--memory "size=${MEMORY_MB}M" \
|
||||
--cpus "boot=$CPU_CORES" \
|
||||
--kernel "/var/lib/vms/base/hypervisor-fw" \
|
||||
--kernel "$KERNEL_PATH" \
|
||||
--initramfs "$INITRD_PATH" \
|
||||
--cmdline "root=LABEL=cloudimg-rootfs ro console=tty1 console=ttyS0" \
|
||||
--disk "path=$VM_IMAGE_PATH" "path=$CLOUD_INIT_PATH,readonly=on" \
|
||||
--net "tap=$TAP_NAME,mac=52:54:00:$(printf '%02x:%02x:%02x' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)))" \
|
||||
--serial tty \
|
||||
@@ -326,10 +372,10 @@ start_vm() {
|
||||
# Update VM info file with new PID
|
||||
sed -i "s/VM_PID=.*/VM_PID=$NEW_VM_PID/" "$vm_info_file"
|
||||
|
||||
log "VM '$vm_name' started with PID $NEW_VM_PID"
|
||||
log "VM number '$vm_number' started with PID $NEW_VM_PID"
|
||||
log "VM socket: $VM_SOCKET"
|
||||
log "TAP interface: $TAP_NAME"
|
||||
log "To connect to console: ./ubuntu_vm_manage.sh console $vm_name"
|
||||
log "To connect to console: ./ubuntu_vm_manage.sh console $vm_number"
|
||||
}
|
||||
|
||||
# Main script logic
|
||||
@@ -339,50 +385,65 @@ if [ $# -eq 0 ]; then
|
||||
fi
|
||||
|
||||
COMMAND="$1"
|
||||
VM_NAME="$2"
|
||||
VM_NUMBER="$2"
|
||||
|
||||
case "$COMMAND" in
|
||||
"list")
|
||||
list_vms
|
||||
;;
|
||||
"status")
|
||||
if [ -z "$VM_NAME" ]; then
|
||||
error "VM name required for status command"
|
||||
if [ -z "$VM_NUMBER" ]; then
|
||||
error "VM number required for status command"
|
||||
fi
|
||||
show_vm_status "$VM_NAME"
|
||||
show_vm_status "$VM_NUMBER"
|
||||
;;
|
||||
"console")
|
||||
if [ -z "$VM_NAME" ]; then
|
||||
error "VM name required for console command"
|
||||
if [ -z "$VM_NUMBER" ]; then
|
||||
error "VM number required for console command"
|
||||
fi
|
||||
connect_console "$VM_NAME"
|
||||
connect_console "$VM_NUMBER"
|
||||
;;
|
||||
"ssh")
|
||||
if [ -z "$VM_NAME" ]; then
|
||||
error "VM name required for ssh command"
|
||||
if [ -z "$VM_NUMBER" ]; then
|
||||
error "VM number required for ssh command"
|
||||
fi
|
||||
ssh_to_vm "$VM_NAME"
|
||||
ssh_to_vm "$VM_NUMBER"
|
||||
;;
|
||||
"stop")
|
||||
if [ -z "$VM_NAME" ]; then
|
||||
error "VM name required for stop command"
|
||||
if [ -z "$VM_NUMBER" ]; then
|
||||
error "VM number required for stop command"
|
||||
fi
|
||||
stop_vm "$VM_NAME"
|
||||
stop_vm "$VM_NUMBER"
|
||||
;;
|
||||
"start")
|
||||
if [ -z "$VM_NAME" ]; then
|
||||
error "VM name required for start command"
|
||||
if [ -z "$VM_NUMBER" ]; then
|
||||
error "VM number required for start command"
|
||||
fi
|
||||
start_vm "$VM_NAME"
|
||||
start_vm "$VM_NUMBER"
|
||||
;;
|
||||
"delete")
|
||||
if [ -z "$VM_NAME" ]; then
|
||||
error "VM name required for delete command"
|
||||
if [ -z "$VM_NUMBER" ]; then
|
||||
error "VM number required for delete command"
|
||||
fi
|
||||
delete_vm "$VM_NAME"
|
||||
delete_vm "$VM_NUMBER"
|
||||
;;
|
||||
"ip"|"logs")
|
||||
warn "Command '$COMMAND' not yet implemented"
|
||||
"ip")
|
||||
if [ -z "$VM_NUMBER" ]; then
|
||||
error "VM number required for ip command"
|
||||
fi
|
||||
echo "VM $VM_NUMBER IP address: 192.168.100.$VM_NUMBER"
|
||||
;;
|
||||
"logs")
|
||||
if [ -z "$VM_NUMBER" ]; then
|
||||
error "VM number required for logs command"
|
||||
fi
|
||||
log_file="/tmp/cloud-hypervisor-vm$VM_NUMBER.log"
|
||||
if [ -f "$log_file" ]; then
|
||||
log "Showing logs for VM number $VM_NUMBER:"
|
||||
tail -f "$log_file"
|
||||
else
|
||||
warn "Log file not found: $log_file"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
error "Unknown command: $COMMAND"
|
||||
|
@@ -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 <vm_name> <memory_mb> <cpu_cores>"
|
||||
if [ $# -ne 4 ]; then
|
||||
error "Usage: $0 <vm_number> <vm_name> <memory_mb> <cpu_cores>"
|
||||
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
|
||||
@@ -325,10 +383,36 @@ if [ ! -f "$FIRMWARE_PATH" ]; then
|
||||
chmod +x "$FIRMWARE_PATH"
|
||||
test_step "Firmware executable check" "[ -x '$FIRMWARE_PATH' ]"
|
||||
log "✓ Firmware downloaded to $FIRMWARE_PATH"
|
||||
else
|
||||
log "✓ Firmware already exists at $FIRMWARE_PATH"
|
||||
test_file_exists "$FIRMWARE_PATH" "Firmware verification"
|
||||
fi
|
||||
else
|
||||
log "✓ Firmware already exists at $FIRMWARE_PATH"
|
||||
test_file_exists "$FIRMWARE_PATH" "Firmware verification"
|
||||
fi
|
||||
|
||||
# Extract kernel and initrd from base image
|
||||
log "Extracting kernel and initrd for kernel boot..."
|
||||
EXTRACT_SCRIPT="$(dirname "$0")/extract_kernel.sh"
|
||||
if [ -f "$EXTRACT_SCRIPT" ]; then
|
||||
"$EXTRACT_SCRIPT"
|
||||
else
|
||||
warn "Kernel extraction script not found, attempting manual extraction..."
|
||||
# Fallback manual extraction
|
||||
KERNEL_PATH="$BASE_SUBVOL/vmlinuz"
|
||||
INITRD_PATH="$BASE_SUBVOL/initrd.img"
|
||||
if [ ! -f "$KERNEL_PATH" ] || [ ! -f "$INITRD_PATH" ]; then
|
||||
log "Extracting kernel and initrd manually..."
|
||||
TEMP_MOUNT=$(mktemp -d)
|
||||
losetup -P /dev/loop1 "$BASE_IMAGE_PATH"
|
||||
mount /dev/loop1p16 "$TEMP_MOUNT"
|
||||
cp "$TEMP_MOUNT/vmlinuz-6.8.0-60-generic" "$KERNEL_PATH" 2>/dev/null || true
|
||||
cp "$TEMP_MOUNT/initrd.img-6.8.0-60-generic" "$INITRD_PATH" 2>/dev/null || true
|
||||
umount "$TEMP_MOUNT"
|
||||
losetup -d /dev/loop1
|
||||
rmdir "$TEMP_MOUNT"
|
||||
fi
|
||||
fi
|
||||
|
||||
test_file_exists "$BASE_SUBVOL/vmlinuz" "Kernel extraction"
|
||||
test_file_exists "$BASE_SUBVOL/initrd.img" "Initrd extraction"
|
||||
|
||||
# Create VM subvolume by cloning from base
|
||||
log "Setting up VM-specific storage..."
|
||||
@@ -388,13 +472,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 +582,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 +637,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"
|
||||
@@ -560,14 +651,19 @@ log "Generated MAC address for VM: $VM_MAC"
|
||||
log "Launching Cloud Hypervisor..."
|
||||
|
||||
# Try to start Cloud Hypervisor and capture any error output
|
||||
log "Starting Cloud Hypervisor with command:"
|
||||
log "cloud-hypervisor --api-socket $VM_SOCKET --memory size=${MEMORY_MB}M --cpus boot=$CPU_CORES --firmware $FIRMWARE_PATH --disk path=$VM_IMAGE_PATH path=$CLOUD_INIT_PATH,readonly=on --net tap=$TAP_NAME,mac=$VM_MAC --serial file=$VM_LOG_FILE --console off --event-monitor path=${VM_LOG_FILE}.events"
|
||||
log "Starting Cloud Hypervisor with kernel boot:"
|
||||
log "cloud-hypervisor --memory size=${MEMORY_MB}M --cpus boot=$CPU_CORES --kernel $KERNEL_PATH --initramfs $INITRD_PATH --cmdline 'root=LABEL=cloudimg-rootfs ro console=tty1 console=ttyS0' --disk path=$VM_IMAGE_PATH path=$CLOUD_INIT_PATH,readonly=on --net tap=$TAP_NAME,mac=$VM_MAC --serial file=$VM_LOG_FILE --console off --event-monitor path=${VM_LOG_FILE}.events"
|
||||
|
||||
# Use kernel boot instead of firmware boot to properly pass root device
|
||||
KERNEL_PATH="$BASE_SUBVOL/vmlinuz"
|
||||
INITRD_PATH="$BASE_SUBVOL/initrd.img"
|
||||
|
||||
# Try with API socket first, fall back without it if needed
|
||||
cloud-hypervisor \
|
||||
--memory "size=${MEMORY_MB}M" \
|
||||
--cpus "boot=$CPU_CORES" \
|
||||
--firmware "$FIRMWARE_PATH" \
|
||||
--kernel "$KERNEL_PATH" \
|
||||
--initramfs "$INITRD_PATH" \
|
||||
--cmdline "root=LABEL=cloudimg-rootfs ro console=tty1 console=ttyS0" \
|
||||
--disk "path=$VM_IMAGE_PATH" "path=$CLOUD_INIT_PATH,readonly=on" \
|
||||
--net "tap=$TAP_NAME,mac=$VM_MAC" \
|
||||
--serial "file=$VM_LOG_FILE" \
|
||||
@@ -617,6 +713,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 +725,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 +743,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 +776,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."
|
||||
|
Reference in New Issue
Block a user