Files
2026-07-07 16:57:39 +00:00

177 lines
6.8 KiB
Bash

#!/usr/bin/env bash
# ───────────────────────────────────────────────────────────
# run-qemu.sh — Launch the Ansible Control Node VM
#
# Boots the minimal Alpine Linux VM with:
# • Machine: pc-q35-10.0
# • TTY: ttyS0, xterm-256color (serial console)
# • Keyboard: PS/2 (atkbd via QEMU default)
# • Network: user-mode NAT with port forwards:
# localhost:2222 → VM:22 (SSH)
# localhost:8080 → VM:8080 (custom services)
# • Disk: virtio-blk, read from output/
# • Init: OpenRC (no systemd)
#
# Usage:
# ./scripts/run-qemu.sh # serial console (default)
# ./scripts/run-qemu.sh --gui # graphical (GTK) window
# ./scripts/run-qemu.sh --vnc :0 # VNC on display :0
# ./scripts/run-qemu.sh --debug # verbose kernel boot
#
# Environment:
# QEMU_MEMORY — RAM size (default: 1024M)
# QEMU_SMP — CPU count (default: 2)
# SSH_PORT — host port for SSH forward (default: 2222)
#
# Access the VM:
# ssh -p 2222 ansible@localhost # password: ansible
# ssh -p 2222 root@localhost # password: ansible
#
# Stop the VM:
# Press Ctrl-A then X (in -nographic mode)
# Or: sudo shutdown -h now (inside VM)
# ───────────────────────────────────────────────────────────
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
OUTPUT_DIR="${OUTPUT_DIR:-$SCRIPT_DIR/../output}"
# ── Configuration (env-overridable) ─────────────────────────
QEMU_BIN="${QEMU_BIN:-qemu-system-x86_64}"
QEMU_MACHINE="${QEMU_MACHINE:-pc-q35-10.0}"
QEMU_MEMORY="${QEMU_MEMORY:-1024M}"
QEMU_SMP="${QEMU_SMP:-2}"
SSH_PORT="${SSH_PORT:-2222}"
EXTRA_PORT="${EXTRA_PORT:-8090}"
# ── Colour helpers ──────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; BOLD='\033[1m'; NC='\033[0m'
info() { echo -e "${BLUE}[*]${NC} $*"; }
ok() { echo -e "${GREEN}[✓]${NC} $*"; }
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
# ── Find build artifacts ────────────────────────────────────
# Prefer qcow2 over raw
if [ -f "$OUTPUT_DIR/ansible-node.qcow2" ]; then
DISK="$OUTPUT_DIR/ansible-node.qcow2"
DISK_FMT="qcow2"
elif [ -f "$OUTPUT_DIR/ansible-node.raw" ]; then
DISK="$OUTPUT_DIR/ansible-node.raw"
DISK_FMT="raw"
else
echo -e "${RED}[✗]${NC} No disk image found in ${OUTPUT_DIR}"
echo " Run ./scripts/build-qemu.sh first."
exit 1
fi
KERNEL="$OUTPUT_DIR/vmlinuz-virt"
INITRD="$OUTPUT_DIR/initramfs-virt"
for f in "$DISK" "$KERNEL" "$INITRD"; do
if [ ! -f "$f" ]; then
echo -e "${RED}[✗]${NC} Missing: $f"
exit 1
fi
done
# ── Parse arguments ─────────────────────────────────────────
DISPLAY_MODE="nographic"
KERNEL_APPEND="root=/dev/vda console=ttyS0 TERM=xterm-256color quiet modules=virtio_blk,ext4 rootflags=rw"
EXTRA_QEMU_ARGS=()
while [[ $# -gt 0 ]]; do
case "$1" in
--gui|-g)
DISPLAY_MODE="gtk"
;;
--vnc)
DISPLAY_MODE="vnc"
VNC_DISPLAY="${2:-:0}"
shift
;;
--debug)
# Remove 'quiet', add verbose/delay for debugging
KERNEL_APPEND="${KERNEL_APPEND// quiet/} debug_init rootdelay=3"
;;
--help|-h)
echo "Usage: $0 [--gui|--vnc :N|--debug] [extra qemu args...]"
echo ""
echo "Modes:"
echo " (default) Serial console (-nographic), Ctrl-A X to exit"
echo " --gui Graphical GTK window with keyboard support"
echo " --vnc :N VNC server on display N"
echo " --debug Verbose kernel boot messages"
echo ""
echo "Environment:"
echo " QEMU_MEMORY=1024M RAM size"
echo " QEMU_SMP=2 CPU count"
echo " SSH_PORT=2222 Host SSH port"
exit 0
;;
*)
EXTRA_QEMU_ARGS+=("$1")
;;
esac
shift
done
# ── Display mode flags ──────────────────────────────────────
case "$DISPLAY_MODE" in
nographic)
DISPLAY_FLAG="-nographic"
;;
gtk)
DISPLAY_FLAG="-display gtk"
# QEMU's GTK display includes keyboard support via PS/2 emulation
# which covers the CONFIG_KEYBOARD_ATKBD kernel requirement
;;
vnc)
DISPLAY_FLAG="-vnc ${VNC_DISPLAY} -vga virtio"
KERNEL_APPEND="$KERNEL_APPEND video=1024x768"
;;
esac
# ── Launch ──────────────────────────────────────────────────
echo ""
echo -e "${BOLD}══════════════════════════════════════════════${NC}"
echo -e "${BOLD} Ansible Control Node${NC}"
echo -e "${BOLD}══════════════════════════════════════════════${NC}"
echo ""
echo -e " Machine: ${GREEN}${QEMU_MACHINE}${NC}"
echo -e " Memory: ${GREEN}${QEMU_MEMORY}${NC}"
echo -e " CPUs: ${GREEN}${QEMU_SMP}${NC}"
echo -e " Disk: ${GREEN}${DISK_FMT}:${DISK}${NC}"
echo -e " Display: ${GREEN}${DISPLAY_MODE}${NC}"
echo -e " Kernel: ${KERNEL_APPEND}"
echo ""
echo -e " SSH: ${YELLOW}ssh -p ${SSH_PORT} ansible@localhost${NC}"
echo -e " Password: ${YELLOW}ansible${NC}"
echo ""
echo -e " ${BOLD}Ctrl-A X${NC} to quit (serial mode)"
echo -e " ${BOLD}Ctrl-C${NC} to force-quit (any mode)"
echo ""
# Build netdev string, skipping extra port if already in use
NETDEV_FORWARDS="hostfwd=tcp::${SSH_PORT}-:22"
if ! ss -tlnp 2>/dev/null | grep -q ":${EXTRA_PORT} "; then
NETDEV_FORWARDS="${NETDEV_FORWARDS},hostfwd=tcp::${EXTRA_PORT}-:8080"
else
warn "Port ${EXTRA_PORT} already in use — skipping extra forward"
fi
# shellcheck disable=SC2086
exec "$QEMU_BIN" \
-machine "$QEMU_MACHINE" \
-m "$QEMU_MEMORY" \
-smp "$QEMU_SMP" \
-enable-kvm \
-kernel "$KERNEL" \
-initrd "$INITRD" \
-append "$KERNEL_APPEND" \
-drive "file=$DISK,if=virtio,format=$DISK_FMT" \
-netdev "user,id=net0,${NETDEV_FORWARDS}" \
-device virtio-net,netdev=net0 \
$DISPLAY_FLAG \
"${EXTRA_QEMU_ARGS[@]}"