#!/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[@]}"