271 lines
8.4 KiB
Bash
271 lines
8.4 KiB
Bash
#!/bin/bash
|
|
|
|
# ======================================================
|
|
# 🚀 Automated VirtualBox VM Management Script (Updated)
|
|
# ======================================================
|
|
|
|
# --- Configuration ---
|
|
BASE_VM_NAME="DB_Image" # Name of the template/base VM to clone from
|
|
VM_USER="asf_user" # User inside the VM (for IP detection and final output)
|
|
VM_PASSWORD="ASF" # Password for the VM user (for IP detection)
|
|
|
|
# Ports Configuration
|
|
BASE_SSH_PORT=6002 # Starting SSH NAT port
|
|
BASE_VNC_PORT=5002 # Starting VNC (VRDE) port
|
|
PORT_DB_FILE="$HOME/.vms_ports_db.json" # JSON file for port persistence
|
|
|
|
# VM Resources (50 GB is 51200 MB)
|
|
VM_MEMORY=51200
|
|
VM_VRAM=128
|
|
VM_CPUS=2
|
|
|
|
# --- Utility Functions (All functions remain the same) ---
|
|
|
|
usage() {
|
|
echo "Usage:"
|
|
echo " $0 create <new_vm_name>"
|
|
echo " $0 status <vm_name>"
|
|
echo " $0 activate <vm_name>"
|
|
echo " $0 delete <vm_name>"
|
|
exit 1
|
|
}
|
|
|
|
# Function to remove VM port entry from JSON
|
|
remove_port_entry() {
|
|
local vm_name="$1"
|
|
init_port_db # Ensure DB exists
|
|
|
|
if [[ -f "$PORT_DB_FILE" ]]; then
|
|
if grep -q "\"$vm_name\"" "$PORT_DB_FILE"; then
|
|
# Use grep and sed to remove the entry, handling list structure complexities
|
|
grep -v "\"$VM_NAME\"" "$PORT_DB_FILE" | sed 's/}, /}/' | sed 's/, }/}/' > "${PORT_DB_FILE}.tmp"
|
|
|
|
if grep -q "^{.*}$" "${PORT_DB_FILE}.tmp"; then
|
|
mv "${PORT_DB_FILE}.tmp" "$PORT_DB_FILE"
|
|
else
|
|
echo "{}" > "$PORT_DB_FILE"
|
|
fi
|
|
# NOTE: Removed the success echo to keep the main flow cleaner
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# Function to safely create/initialize the JSON port database
|
|
init_port_db() {
|
|
if [[ ! -f "$PORT_DB_FILE" ]]; then
|
|
echo "{}" > "$PORT_DB_FILE"
|
|
fi
|
|
}
|
|
|
|
# Function to get VM status
|
|
get_vm_status() {
|
|
local vm_name="$1"
|
|
local status
|
|
status=$(VBoxManage showvminfo "$vm_name" --machinereadable 2>/dev/null | grep -i "^VMState=" | cut -d'"' -f2)
|
|
|
|
if [[ "$status" == "running" ]]; then
|
|
echo "active"
|
|
else
|
|
echo "not active"
|
|
fi
|
|
}
|
|
|
|
# Function to start VM if not running
|
|
activate_vm() {
|
|
local vm_name="$1"
|
|
local status
|
|
status=$(get_vm_status "$vm_name")
|
|
if [[ "$status" == "active" ]]; then
|
|
echo "✅ VM '$vm_name' is already active."
|
|
return 0
|
|
fi
|
|
|
|
echo "🚀 Starting VM '$vm_name'..."
|
|
VBoxManage startvm "$vm_name" --type headless
|
|
if [[ $? -eq 0 ]]; then
|
|
echo "✅ VM '$vm_name' started successfully."
|
|
else
|
|
echo "❌ Failed to start VM '$vm_name'."
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Function to read ports from JSON
|
|
get_vm_ports() {
|
|
local vm_name="$1"
|
|
local ports
|
|
|
|
# Check if the VM entry exists in the JSON file
|
|
ports=$(cat "$PORT_DB_FILE" | grep "\"$vm_name\"" | awk -F': ' '{print $2}' | tr -d '",}{')
|
|
|
|
if [[ -z "$ports" ]]; then
|
|
echo "" # Return empty if not found
|
|
else
|
|
# Assuming the format in the JSON is {"vm_name": "SSH_PORT VNC_PORT"}
|
|
echo "$ports"
|
|
fi
|
|
}
|
|
|
|
# Function to assign unique ports for SSH/VNC per VM using JSON
|
|
assign_ports() {
|
|
local vm_name="$1"
|
|
init_port_db
|
|
|
|
# 1. Check if ports already assigned in JSON
|
|
local assigned_ports
|
|
assigned_ports=$(get_vm_ports "$vm_name")
|
|
|
|
if [[ -n "$assigned_ports" ]]; then
|
|
echo "$assigned_ports"
|
|
return 0
|
|
fi
|
|
|
|
# 2. Collect ALL currently used ports from the JSON file
|
|
local used_ports
|
|
used_ports=$(grep -oE "[0-9]+" "$PORT_DB_FILE" 2>/dev/null)
|
|
|
|
local ssh_port=$BASE_SSH_PORT
|
|
local vnc_port=$BASE_VNC_PORT
|
|
|
|
# Find unique SSH port
|
|
while grep -qw "$ssh_port" <<<"$used_ports" || netstat -tln 2>/dev/null | grep -q ":$ssh_port"; do
|
|
ssh_port=$((ssh_port + 1))
|
|
done
|
|
|
|
# Find unique VNC port
|
|
while grep -qw "$vnc_port" <<<"$used_ports" || netstat -tln 2>/dev/null | grep -q ":$vnc_port"; do
|
|
vnc_port=$((vnc_port + 1))
|
|
done
|
|
|
|
# 3. Save new ports to JSON using a simple tool for JSON manipulation
|
|
local new_entry="\"$vm_name\": \"$ssh_port $vnc_port\""
|
|
|
|
local current_content
|
|
current_content=$(cat "$PORT_DB_FILE")
|
|
|
|
if [[ "$current_content" == "{}" ]]; then
|
|
echo "{$new_entry}" > "$PORT_DB_FILE"
|
|
else
|
|
sed '$s/}$/, '"$new_entry"'}' "$PORT_DB_FILE" > "${PORT_DB_FILE}.tmp" && mv "${PORT_DB_FILE}.tmp" "$PORT_DB_FILE"
|
|
fi
|
|
|
|
echo "$ssh_port $vnc_port"
|
|
}
|
|
|
|
# --- Main Logic ---
|
|
|
|
if [[ $# -lt 2 && "$1" != "create" ]]; then
|
|
usage
|
|
fi
|
|
|
|
COMMAND="$1"
|
|
ARG="$2"
|
|
|
|
case "$COMMAND" in
|
|
status)
|
|
get_vm_status "$ARG"
|
|
;;
|
|
|
|
activate)
|
|
activate_vm "$ARG"
|
|
;;
|
|
|
|
create)
|
|
NEW_VM_NAME="$ARG"
|
|
init_port_db # Ensure DB exists
|
|
|
|
if VBoxManage showvminfo "$NEW_VM_NAME" &>/dev/null; then
|
|
echo "VM '$NEW_VM_NAME' already exists. Please delete it or choose another name."
|
|
exit 1
|
|
fi
|
|
|
|
# Assign ports and store them in the JSON
|
|
read -r SSH_PORT VNC_PORT < <(assign_ports "$NEW_VM_NAME")
|
|
|
|
echo "Cloning VM '$BASE_VM_NAME' to '$NEW_VM_NAME' (SSH:$SSH_PORT, VNC:$VNC_PORT)..."
|
|
VBoxManage clonevm "$BASE_VM_NAME" --name "$NEW_VM_NAME" --register --mode all
|
|
|
|
if [[ $? -ne 0 ]]; then
|
|
echo "❌ Failed to clone VM. Aborting."
|
|
remove_port_entry "$NEW_VM_NAME"
|
|
exit 1
|
|
fi
|
|
|
|
# Configure VM resources
|
|
echo "Configuring VM resources (Memory: $VM_MEMORY MB, CPUs: $VM_CPUS)..."
|
|
VBoxManage modifyvm "$NEW_VM_NAME" --memory "$VM_MEMORY" --vram "$VM_VRAM" --cpus "$VM_CPUS"
|
|
VBoxManage modifyvm "$NEW_VM_NAME" --nic1 nat
|
|
VBoxManage modifyvm "$NEW_VM_NAME" --cableconnected1 on
|
|
|
|
# --- PHASE 1: Initial Boot and Shutdown ---
|
|
|
|
echo "Starting VM '$NEW_VM_NAME' for initial boot (1 minute)..."
|
|
VBoxManage startvm "$NEW_VM_NAME" --type headless
|
|
|
|
# Wait for 1 minute (60 seconds) for first boot/OS initialization
|
|
sleep 60
|
|
echo "Initial boot complete. Shutting down VM."
|
|
|
|
VBoxManage controlvm "$NEW_VM_NAME" poweroff
|
|
|
|
# --- PHASE 2: Setting Network Ports ---
|
|
echo "Setting up network port forwarding (SSH:$SSH_PORT, VNC:$VNC_PORT)..."
|
|
|
|
# Remove old (potentially conflicting) rules and add the new one
|
|
VBoxManage modifyvm "$NEW_VM_NAME" --natpf1 delete ssh 2>/dev/null || true
|
|
|
|
# Add the new NAT port forwarding rules
|
|
VBoxManage modifyvm "$NEW_VM_NAME" --natpf1 "ssh,tcp,,${SSH_PORT},,22"
|
|
VBoxManage modifyvm "$NEW_VM_NAME" --vrde on --vrdeport "$VNC_PORT"
|
|
|
|
# --- PHASE 3: Final Start and Wait ---
|
|
echo "Starting VM '$NEW_VM_NAME' headless for final usage (Waiting 5 minutes)..."
|
|
VBoxManage startvm "$NEW_VM_NAME" --type headless
|
|
|
|
# Wait for 5 minutes (300 seconds) for the system and services to fully initialize
|
|
sleep 300
|
|
echo "Initialization complete. VM should be ready."
|
|
|
|
# --- PHASE 4: Final Output ---
|
|
|
|
echo "--- ✅ VM $NEW_VM_NAME Created and Ready ---"
|
|
echo "Host: 127.0.0.1 (use your external DDNS for remote access)"
|
|
echo "User: $VM_USER"
|
|
echo "SSH Port: $SSH_PORT"
|
|
echo "VNC Port: $VNC_PORT"
|
|
|
|
;;
|
|
|
|
delete)
|
|
VM_NAME="$ARG"
|
|
init_port_db # Ensure DB exists
|
|
|
|
echo "🛑 Powering off VM '$VM_NAME'..."
|
|
VBoxManage controlvm "$VM_NAME" poweroff 2>/dev/null || true
|
|
|
|
echo "🗑️ Unregistering and deleting VM '$VM_NAME'..."
|
|
VBoxManage unregistervm "$VM_NAME" --delete
|
|
|
|
# Remove entry from port DB
|
|
if [[ -f "$PORT_DB_FILE" ]]; then
|
|
|
|
if grep -q "\"$VM_NAME\"" "$PORT_DB_FILE"; then
|
|
# Remove the entire line containing the VM entry
|
|
grep -v "\"$VM_NAME\"" "$PORT_DB_FILE" | sed 's/}, /}/' | sed 's/, }/}/' > "${PORT_DB_FILE}.tmp"
|
|
|
|
# Check if the file is now just "{}" and fix it
|
|
if grep -q "^{.*}$" "${PORT_DB_FILE}.tmp"; then
|
|
mv "${PORT_DB_FILE}.tmp" "$PORT_DB_FILE"
|
|
else
|
|
echo "{}" > "$PORT_DB_FILE"
|
|
fi
|
|
|
|
echo "✅ Removed '$VM_NAME' entry from $PORT_DB_FILE"
|
|
fi
|
|
fi
|
|
;;
|
|
|
|
*)
|
|
usage
|
|
;;
|
|
esac |