# shellcheck shell=bash disable=SC2034 # Shared settings for the pxetest VM harness. Sourced, not run. # # Everything this harness creates is named pxetest-*: the two domains, the # isolated network, and the volumes in the libvirt 'default' pool. Nothing # else is touched. set -euo pipefail VM_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$VM_DIR/../.." && pwd)" export LIBVIRT_DEFAULT_URI="${LIBVIRT_DEFAULT_URI:-qemu:///system}" # Download / build cache (ISO, USB image, extracted helper tools). PXETEST_CACHE="${PXETEST_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/pxetest}" # Where the gitignored payload dirs (offline-packages/, pip-wheels/, # boot-files/, boot-tools/, enrollment/, ...) live. Defaults to this repo; # point it at the main checkout when running from a worktree. PXETEST_PAYLOAD_ROOT="${PXETEST_PAYLOAD_ROOT:-$REPO_ROOT}" # Ubuntu ISO UBUNTU_SERIES="${UBUNTU_SERIES:-24.04}" UBUNTU_ISO_NAME="${UBUNTU_ISO_NAME:-ubuntu-24.04.5-live-server-amd64.iso}" UBUNTU_RELEASE_URL="${UBUNTU_RELEASE_URL:-https://releases.ubuntu.com/$UBUNTU_SERIES}" UBUNTU_ISO="$PXETEST_CACHE/$UBUNTU_ISO_NAME" USB_IMAGE="$PXETEST_CACHE/pxetest-usb.img" # libvirt objects POOL="default" SERVER_VM="pxetest-server" CLIENT_VM="pxetest-client" IMAGING_NET="pxetest-imaging" IMAGING_BRIDGE="virbr-pxetest" GE_NET="default" # libvirt NAT, 192.168.122.0/24 = "GE/corp" SERVER_DISK_VOL="pxetest-server.qcow2" SERVER_DISK_SIZE="${SERVER_DISK_SIZE:-60G}" USB_VOL="pxetest-usb.img" CLIENT_DISK_VOL="pxetest-client.qcow2" SERVER_MEM_MIB="${SERVER_MEM_MIB:-4096}" CLIENT_MEM_MIB="${CLIENT_MEM_MIB:-2048}" # Fixed MACs so the playbook's NIC pins can be written once for this site. SERVER_GE_MAC="52:54:00:9e:10:01" # on 'default' NAT (GE/corp side) SERVER_IMAGING_MAC="52:54:00:9e:10:02" # on pxetest-imaging (imaging side) CLIENT_MAC="52:54:00:9e:10:03" # on pxetest-imaging only # Second client: Secure Boot OFF, e1000e NIC and SATA disk (in-box WinPE # drivers), so WinPE gets a network and startnet can map the shares. CLIENT2_VM="pxetest-client-nosb" CLIENT2_DISK_VOL="pxetest-client-nosb.qcow2" CLIENT2_MAC="52:54:00:9e:10:04" # on pxetest-imaging only log() { printf '[pxetest] %s\n' "$*"; } die() { printf '[pxetest] ERROR: %s\n' "$*" >&2; exit 1; } dom_exists() { virsh dominfo "$1" >/dev/null 2>&1; } dom_state() { virsh domstate "$1" 2>/dev/null | head -1; } net_exists() { virsh net-info "$1" >/dev/null 2>&1; } # No "| grep -q" under pipefail: grep exits early, the writer gets SIGPIPE # and the pipeline reads as failed even on a match. net_active() { [ "$(virsh net-info "$1" 2>/dev/null | awk '/^Active:/{print $2}')" = "yes" ]; } vol_exists() { virsh vol-info --pool "$POOL" "$1" >/dev/null 2>&1; } # sfdisk and mtools are needed for image mode. If the host lacks them, # extract the Ubuntu debs into the cache (no root, no install). ensure_image_tools() { local troot="$PXETEST_CACHE/tools/root" if [ -d "$troot" ]; then export PATH="$troot/usr/sbin:$troot/usr/bin:$PATH" export LD_LIBRARY_PATH="$troot/usr/lib/x86_64-linux-gnu${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" fi if command -v sfdisk >/dev/null 2>&1 && command -v mcopy >/dev/null 2>&1 \ && sfdisk --version >/dev/null 2>&1; then return 0 fi log "sfdisk/mtools missing: extracting fdisk, libfdisk1, mtools debs into $troot" mkdir -p "$PXETEST_CACHE/tools/debs" "$troot" (cd "$PXETEST_CACHE/tools/debs" && apt-get download fdisk libfdisk1 mtools) \ || die "apt-get download fdisk libfdisk1 mtools failed; install fdisk + mtools" local d for d in "$PXETEST_CACHE/tools/debs"/*.deb; do dpkg-deb -x "$d" "$troot" done export PATH="$troot/usr/sbin:$troot/usr/bin:$PATH" export LD_LIBRARY_PATH="$troot/usr/lib/x86_64-linux-gnu${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" if ! sfdisk --version >/dev/null || ! command -v mcopy >/dev/null; then die "extracted sfdisk/mcopy do not run" fi } # The imaging network must be fully isolated: no , no # (so the host has no address on it and libvirt runs no DHCP there). assert_imaging_isolated() { local xml xml=$(virsh net-dumpxml "$IMAGING_NET") if grep -q ' or element: NOT isolated. Run destroy.sh and create.sh." fi local v4 v4=$(ip -4 -o addr show dev "$IMAGING_BRIDGE" 2>/dev/null || true) if [ -n "$v4" ]; then die "host has an IPv4 address on $IMAGING_BRIDGE: NOT isolated" fi # The bridge must hold only libvirt tap devices (vnetN), never a # physical port. local port for port in /sys/class/net/"$IMAGING_BRIDGE"/brif/*; do [ -e "$port" ] || continue port=$(basename "$port") case "$port" in vnet*) ;; *) die "$IMAGING_BRIDGE has non-tap port $port: NOT isolated" ;; esac done }