...
This commit is contained in:
@@ -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
|
||||
@@ -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."
|
||||
|
||||
Reference in New Issue
Block a user