diff options
Diffstat (limited to 'test-env')
| -rw-r--r-- | test-env/README.md | 110 | ||||
| -rwxr-xr-x | test-env/net-sim/netsim.sh | 489 | ||||
| -rwxr-xr-x | test-env/provision-vm.sh | 118 |
3 files changed, 717 insertions, 0 deletions
diff --git a/test-env/README.md b/test-env/README.md new file mode 100644 index 0000000..ef2bb0a --- /dev/null +++ b/test-env/README.md @@ -0,0 +1,110 @@ +# Technical Specification: Automated KVM Provisioning for Hoods Gate Testing + +This document outlines the architecture and implementation of a shell-based +automation utility for deploying Kernel-based Virtual Machines (KVM) on Ubuntu +Server using virt-install. This approach is designed for rapid iteration in +kernel development and network stack testing where total environment isolation +is required. + +## Overview + +The goal of this implementation is to provide a reproducible, command-line +driven interface for deploying headless virtual machines. By utilizing a Linux +Bridge and Serial Console redirection, developers can perform destructive +kernel operations while maintaining persistent access to the debug output, +bypassing the overhead of a graphical user interface. + +## System Requirements & Prerequisites + +The host system must support hardware virtualization (Intel VT-x or AMD-V) and +have the following packages installed: + +- qemu-kvm: Backend emulator +- libvirt-daemon-system: Management system +- virinst: CLI Utility for provisioning +- bridge-utils: Necessary for L2 network bridging + +### Storage Persistence + +Virtual disk images created by this script are stored in +/var/lib/libvirt/images/. During kernel testing, if the filesystem becomes +corrupted due to experimental kernel modules, the storage must be manually +purged or overwritten. + +### Network Configuration + +The host must have a bridge interface (e.g., br0) configured via Netplan. +This allows the Guest VM to obtain its own IP address and provides a +transparent environment for testing custom networking protocols or firewall +rules. + +## Implementation + +The script, [provision-lab.sh](./provision-vm.sh), automates the +virt-install process for isolated kernel and network testing. It utilizes local +ISO media and libosinfo detection to ensure hardware-optimized environments +while configuring the serial console for direct TTY access. + +## Operational Procedures + +### VM Creation + +Execute the script with optional parameters for resource allocation: + +```shell +sudo ./provision-vm.sh <name> <vcpus> <ram_in_mb> +``` + +### Interacting with the Guest Kernel + +Since the VM is configured without a graphics card, the standard virsh console +command is used to attach to the guest's serial port. This is essential for +capturing Kernel Panics or Early Printk output that would otherwise be lost. + +Attach to Console: + +```shell +virsh console <vm-name> +``` + +Detach from Console (Press): + +``` +CTRL + ] +``` + +### Modifying Kernel Parameters + +To test specific kernel options (e.g., disabling KASLR or isolating CPUs), +edit the VM configuration directly via the XML descriptor: + +```shell +virsh edit <vm-name> +``` + +Locate the <cmdline> tag within the <os> section to append your required flags. + +### Network Analysis + +Use tcpdump -i br0 on the host to monitor raw packet traffic moving through the +VM's virtual interface. + +```shell +tcpdump -i br0 +``` + +## Lifecycle Management + +Operation|Command|Description +Start|virsh start <name>|Powers on the virtual machine +Stop|virsh shutdown <name>|Sends an ACPI power signal for a graceful exit +Destroy|virsh destroy <name>|Equivalent to pulling the power plug (immediate stop) +Undefine|virsh undefine <name> --remove-all-storage|Completely removes the VM and its disk images + +## Technical Considerations + +When performing kernel modifications, the following behaviors should be expected: + +- _Storage Persistence_: Changes made to the filesystem persist unless the --remove-all-storage flag is used during deletion. +- _Network Isolation_: By using a bridge, the VM possesses its own MAC address. Ensure the host firewall (ufw/iptables) is configured to allow traffic across the bridge. +- _Instruction Set Passthrough_: If the kernel testing requires specific CPU instructions (e.g., AES-NI), append --cpu host-passthrough to the virt-install command. diff --git a/test-env/net-sim/netsim.sh b/test-env/net-sim/netsim.sh new file mode 100755 index 0000000..8b0f68e --- /dev/null +++ b/test-env/net-sim/netsim.sh @@ -0,0 +1,489 @@ +#!/bin/bash +# +# Network Simulator for Hoods Gate Testing +# Isolated network environment with fake internet responses +# + +# Configuration +STATE_DIR="/tmp/netsim" +STATE_FILE="${STATE_DIR}/state" +NETNS_CLIENT="netsim_client" +NETNS_EXIT="netsim_exit" +NETNS_INTERNET="netsim_internet" + +# Network Configuration +BRIDGE_CLIENT="br-client" +BRIDGE_EXIT="br-exit" +BRIDGE_WAN="br-wan" # Bridge for WAN side connection +VETH_CLIENT_HOST="veth-c-host" +VETH_CLIENT_NS="veth-c-ns" +VETH_EXIT_HOST="veth-e-host" +VETH_EXIT_NS="veth-e-ns" +VETH_INET_EXIT="veth-i-exit" +VETH_INET_NS="veth-i-ns" +EXIT_WAN_IFACE="wan0" # Interface name inside exit KVM + +# IP Configuration +IP_CLIENT_BRIDGE="10.10.1.1/24" +IP_CLIENT_VM="10.10.1.10/24" +IP_EXIT_BRIDGE="10.10.2.1/24" +IP_EXIT_VM="10.10.2.10/24" +IP_EXIT_WAN="203.0.113.10/24" # WAN IP for exit node +IP_INET_GATEWAY="203.0.113.1/24" # Gateway in internet namespace + +RED=$'\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC=$'\033[0m' + +check_root() { + if [ "$EUID" -ne 0 ]; then + echo -e "${RED}Error: This script must be run as root${NC}" + exit 1 + fi +} + +fail() { + echo -e "${RED}$2${NC}" >&2 + exit $1 +} + +usage() { + cat << EOF + ▄▄▄ ▄▄ ██ + ███ ██ ██ ▀▀ + ██▀█ ██ ▄████▄ ███████ ▄▄█████▄ ████ ████▄██▄ + ██ ██ ██ ██▄▄▄▄██ ██ ██▄▄▄▄ ▀ ██ ██ ██ ██ + ██ █▄██ ██▀▀▀▀▀▀ ██ ▀▀▀▀██▄ ██ ██ ██ ██ + ██ ███ ▀██▄▄▄▄█ ██▄▄▄ █▄▄▄▄▄██ ▄▄▄██▄▄▄ ██ ██ ██ + ▀▀ ▀▀▀ ▀▀▀▀▀ ▀▀▀▀ ▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀ ▀▀ ▀▀ ▀▀ +*Network Simulator for Hoods Gate Testing${NC} + +Usage: + $0 setup [bandwidth] [latency] + Create network topology for Hoods Gate testing + bandwidth: in mbit (default: 100) + latency: in ms (default: 10) + Example: $0 setup 50 20 + + $0 status + Show current network configuration and statistics + + $0 logs + Show traffic logs from fake internet + + $0 adjust <bandwidth> <latency> + Adjust network conditions on the fly + Example: $0 adjust 10 100 + + $0 teardown + Remove all network configuration + + $0 test + Run connectivity tests + +Network Topology Created: + [Client KVM] ---> [Bridge] ---> [Exit KVM] ---> [Fake Internet] + 10.10.1.10 10.10.1.1 10.10.2.10 192.168.100.2 + +Instructions for KVMs: + Client KVM: Attach to bridge '${BRIDGE_CLIENT}', use IP 10.10.1.10/24, gateway 10.10.1.1 + Exit KVM: Attach to bridge '${BRIDGE_EXIT}', use IP 10.10.2.10/24, gateway 10.10.2.1 + SDN should route traffic to 192.168.100.1 + +EOF + exit 0 +} + +init_state() { + mkdir -p "${STATE_DIR}" + touch "${STATE_FILE}" +} + +# Setup network topology +setup_network() { + local bandwidth=${1:-100} + local latency=${2:-10} + + echo -e "${GREEN}Setting up network simulation...${NC}" + + # Clean up any existing setup + teardown_network 2>/dev/null + + echo -e "${BLUE}[1/7]${NC} Creating network namespaces..." + ip netns add ${NETNS_EXIT} 2>/dev/null + ip netns add ${NETNS_INTERNET} 2>/dev/null + + echo -e "${BLUE}[2/7]${NC} Creating bridges for KVM attachment..." + ip link add ${BRIDGE_CLIENT} type bridge + ip link add ${BRIDGE_EXIT} type bridge + ip link add ${BRIDGE_WAN} type bridge + ip addr add ${IP_CLIENT_BRIDGE} dev ${BRIDGE_CLIENT} + ip addr add ${IP_EXIT_BRIDGE} dev ${BRIDGE_EXIT} + ip link set ${BRIDGE_CLIENT} up + ip link set ${BRIDGE_EXIT} up + ip link set ${BRIDGE_WAN} up + + echo -e "${BLUE}[3/7]${NC} Creating veth pairs..." + # Exit namespace LAN side connection + ip link add ${VETH_EXIT_HOST} type veth peer name ${VETH_EXIT_NS} + ip link set ${VETH_EXIT_NS} netns ${NETNS_EXIT} + ip link set ${VETH_EXIT_HOST} master ${BRIDGE_EXIT} + ip link set ${VETH_EXIT_HOST} up + + # WAN bridge to internet namespace connection + ip link add veth-wan-br type veth peer name veth-wan-inet + ip link set veth-wan-br master ${BRIDGE_WAN} + ip link set veth-wan-inet netns ${NETNS_INTERNET} + ip link set veth-wan-br up + + # Internet namespace connection + ip link add ${VETH_INET_EXIT} type veth peer name ${VETH_INET_NS} + ip link set ${VETH_INET_EXIT} master ${BRIDGE_WAN} + ip link set ${VETH_INET_NS} netns ${NETNS_INTERNET} + ip link set ${VETH_INET_EXIT} up + + echo -e "${BLUE}[4/7]${NC} Configuring IP addresses..." + # Exit namespace LAN IP only (no WAN IP - that's on the KVM side) + ip netns exec ${NETNS_EXIT} ip addr add ${IP_EXIT_BRIDGE} dev ${VETH_EXIT_NS} + ip netns exec ${NETNS_EXIT} ip link set ${VETH_EXIT_NS} up + ip netns exec ${NETNS_EXIT} ip link set lo up + + # Internet namespace IPs (acts as the gateway) + ip netns exec ${NETNS_INTERNET} ip addr add ${IP_INET_GATEWAY} dev veth-wan-inet + ip netns exec ${NETNS_INTERNET} ip addr add 8.8.8.8/32 dev lo # Fake DNS server + ip netns exec ${NETNS_INTERNET} ip addr add 1.1.1.1/32 dev lo # Fake DNS server + ip netns exec ${NETNS_INTERNET} ip link set veth-wan-inet up + ip netns exec ${NETNS_INTERNET} ip link set ${VETH_INET_NS} up + ip netns exec ${NETNS_INTERNET} ip link set lo up + + echo -e "${BLUE}[5/7]${NC} Configuring routing..." + # Host routing for client bridge - NAT to exit bridge + iptables -t nat -A POSTROUTING -s 10.10.1.0/24 -o ${BRIDGE_EXIT} -j MASQUERADE + echo 1 > /proc/sys/net/ipv4/ip_forward + + # Internet namespace routing (responds to everything, blackholes it) + ip netns exec ${NETNS_INTERNET} sysctl -w net.ipv4.ip_forward=1 >/dev/null + ip netns exec ${NETNS_INTERNET} ip route add default dev veth-wan-inet + + # Note: Exit KVM will handle routing from LAN to WAN interface + + echo -e "${BLUE}[6/7]${NC} Applying traffic control (${bandwidth}mbit, ${latency}ms)..." + apply_tc ${bandwidth} ${latency} + + echo -e "${BLUE}[7/7]${NC} Starting fake internet services..." + start_fake_internet + + # Save configuration + echo "bandwidth=${bandwidth}" > "${STATE_FILE}" + echo "latency=${latency}" >> "${STATE_FILE}" + echo "timestamp=$(date +%s)" >> "${STATE_FILE}" + + echo -e "${GREEN}✓ Network simulation setup complete!${NC}" + echo "" + echo -e "${YELLOW}Next steps:${NC}" + echo " 1. Attach Client KVM to bridge: ${BRIDGE_CLIENT}" + echo " Configure with IP: 10.10.1.10/24, Gateway: 10.10.1.1, DNS: 10.10.1.1" + echo "" + echo " 2. Attach Exit KVM with TWO network interfaces:" + echo " - LAN interface to bridge: ${BRIDGE_EXIT}" + echo " Configure with IP: 10.10.2.10/24" + echo " - WAN interface to bridge: ${BRIDGE_WAN}" + echo " This will appear as '${EXIT_WAN_IFACE}' inside the VM" + echo " Configure with IP: 203.0.113.10/24, Gateway: 203.0.113.1" + echo "" + echo " 3. SDN should route traffic OUT of the '${EXIT_WAN_IFACE}' interface" + echo " (Just like routing out eth0 on a normal machine)" + echo "" + echo " 4. Run '$0 test' to verify connectivity" +} + +# Apply traffic control +apply_tc() { + local bandwidth=$1 + local latency=$2 + + # Apply to client->exit connection + tc qdisc del dev ${VETH_EXIT_HOST} root 2>/dev/null + tc qdisc add dev ${VETH_EXIT_HOST} root handle 1: htb default 10 + tc class add dev ${VETH_EXIT_HOST} parent 1: classid 1:10 htb rate ${bandwidth}mbit + tc qdisc add dev ${VETH_EXIT_HOST} parent 1:10 handle 10: netem delay ${latency}ms + + # Apply to WAN bridge (exit->internet) + tc qdisc del dev ${VETH_INET_EXIT} root 2>/dev/null + tc qdisc add dev ${VETH_INET_EXIT} root handle 1: htb default 10 + tc class add dev ${VETH_INET_EXIT} parent 1: classid 1:10 htb rate ${bandwidth}mbit + tc qdisc add dev ${VETH_INET_EXIT} parent 1:10 handle 10: netem delay ${latency}ms +} + +# Start fake internet services +start_fake_internet() { + local log_file="${STATE_DIR}/traffic.log" + > "${log_file}" + + # DNS Server (responds with fake IPs) + ip netns exec ${NETNS_INTERNET} dnsmasq \ + --no-daemon \ + --listen-address=${IP_INET_GATEWAY%/*} \ + --address=/#/93.184.216.34 \ + --log-queries \ + --log-facility=${STATE_DIR}/dns.log \ + --no-resolv \ + --no-poll \ + >/dev/null 2>&1 & + echo $! > "${STATE_DIR}/dns.pid" + + # HTTP/HTTPS responder + ip netns exec ${NETNS_INTERNET} python3 -c " +import socket +import threading +import datetime + +def log_request(proto, ip, port, data): + with open('${log_file}', 'a') as f: + f.write(f'{datetime.datetime.now()} | {proto} | {ip}:{port} | {len(data)} bytes\\n') + +def http_server(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind(('0.0.0.0', 80)) + sock.listen(5) + while True: + try: + conn, addr = sock.accept() + data = conn.recv(4096) + log_request('HTTP', addr[0], addr[1], data) + response = b'HTTP/1.1 200 OK\\r\\nContent-Type: text/html\\r\\n\\r\\n<html><body><h1>Fake Internet Response</h1></body></html>' + conn.sendall(response) + conn.close() + except: pass + +def https_server(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind(('0.0.0.0', 443)) + sock.listen(5) + while True: + try: + conn, addr = sock.accept() + data = conn.recv(4096) + log_request('HTTPS', addr[0], addr[1], data) + conn.close() + except: pass + +def generic_tcp(): + for port in [22, 21, 25, 3389, 3306, 5432, 6379]: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + try: + sock.bind(('0.0.0.0', port)) + sock.listen(1) + threading.Thread(target=lambda s=sock, p=port: tcp_handler(s, p), daemon=True).start() + except: pass + +def tcp_handler(sock, port): + while True: + try: + conn, addr = sock.accept() + data = conn.recv(4096) + log_request(f'TCP/{port}', addr[0], addr[1], data) + conn.close() + except: pass + +threading.Thread(target=http_server, daemon=True).start() +threading.Thread(target=https_server, daemon=True).start() +threading.Thread(target=generic_tcp, daemon=True).start() + +import time +while True: time.sleep(1) +" >/dev/null 2>&1 & + echo $! > "${STATE_DIR}/http.pid" + + # ICMP responder (respond to pings) + ip netns exec ${NETNS_INTERNET} bash -c " + while true; do + ping -c 1 8.8.8.8 >/dev/null 2>&1 || true + sleep 1 + done + " >/dev/null 2>&1 & + echo $! > "${STATE_DIR}/icmp.pid" +} + +# Show status +show_status() { + if [ ! -f "${STATE_FILE}" ]; then + echo -e "${YELLOW}No simulation running${NC}" + return + fi + + source "${STATE_FILE}" + + echo -e "${GREEN}Network Simulation Status${NC}" + echo "================================" + echo -e "Bandwidth: ${bandwidth}mbit" + echo -e "Latency: ${latency}ms" + echo -e "Running since: $(date -d @${timestamp})" + echo "" + + echo -e "${GREEN}Network Namespaces:${NC}" + ip netns list | grep netsim && echo -e "${GREEN}✓${NC} Namespaces active" || echo -e "${RED}✗${NC} Namespaces missing" + echo "" + + echo -e "${GREEN}Bridges (for KVM attachment):${NC}" + ip link show ${BRIDGE_CLIENT} >/dev/null 2>&1 && echo -e "${GREEN}✓${NC} ${BRIDGE_CLIENT} - Client KVM (LAN side)" || echo -e "${RED}✗${NC} ${BRIDGE_CLIENT} missing" + ip link show ${BRIDGE_EXIT} >/dev/null 2>&1 && echo -e "${GREEN}✓${NC} ${BRIDGE_EXIT} - Exit KVM (LAN side)" || echo -e "${RED}✗${NC} ${BRIDGE_EXIT} missing" + ip link show ${BRIDGE_WAN} >/dev/null 2>&1 && echo -e "${GREEN}✓${NC} ${BRIDGE_WAN} - Exit KVM (WAN side)" || echo -e "${RED}✗${NC} ${BRIDGE_WAN} missing" + echo "" + + echo -e "${GREEN}Fake Internet Services:${NC}" + [ -f "${STATE_DIR}/dns.pid" ] && kill -0 $(cat "${STATE_DIR}/dns.pid") 2>/dev/null && echo -e "${GREEN}✓${NC} DNS (port 53)" || echo -e "${RED}✗${NC} DNS not running" + [ -f "${STATE_DIR}/http.pid" ] && kill -0 $(cat "${STATE_DIR}/http.pid") 2>/dev/null && echo -e "${GREEN}✓${NC} HTTP/HTTPS (ports 80, 443)" || echo -e "${RED}✗${NC} HTTP not running" + echo "" + + echo -e "${GREEN}Traffic Statistics:${NC}" + if [ -f "${STATE_DIR}/traffic.log" ]; then + local total=$(wc -l < "${STATE_DIR}/traffic.log") + echo " Total requests: ${total}" + if [ ${total} -gt 0 ]; then + echo " Last 5 requests:" + tail -5 "${STATE_DIR}/traffic.log" | sed 's/^/ /' + fi + else + echo " No traffic logged yet" + fi +} + +# Show logs +show_logs() { + echo -e "${GREEN}Traffic Logs${NC}" + echo "================================" + + if [ -f "${STATE_DIR}/traffic.log" ]; then + cat "${STATE_DIR}/traffic.log" + else + echo "No traffic logged" + fi + + echo "" + echo -e "${GREEN}DNS Queries${NC}" + echo "================================" + + if [ -f "${STATE_DIR}/dns.log" ]; then + tail -20 "${STATE_DIR}/dns.log" + else + echo "No DNS queries logged" + fi +} + +# Adjust network conditions +adjust_network() { + local bandwidth=$1 + local latency=$2 + + if [ -z "$bandwidth" ] || [ -z "$latency" ]; then + fail 1 "Usage: $0 adjust <bandwidth> <latency>" + fi + + echo -e "${GREEN}Adjusting network conditions...${NC}" + echo " Bandwidth: ${bandwidth}mbit" + echo " Latency: ${latency}ms" + + apply_tc ${bandwidth} ${latency} + + # Update state file + sed -i "s/^bandwidth=.*/bandwidth=${bandwidth}/" "${STATE_FILE}" + sed -i "s/^latency=.*/latency=${latency}/" "${STATE_FILE}" + + echo -e "${GREEN}✓ Network conditions updated${NC}" +} + +# Run connectivity tests +run_tests() { + echo -e "${GREEN}Running connectivity tests...${NC}" + echo "" + + echo -e "${BLUE}Test 1: Ping fake internet gateway${NC}" + ping -c 3 ${IP_INET_GATEWAY%/*} 2>/dev/null && \ + echo -e "${GREEN}✓ PASS${NC}" || echo -e "${RED}✗ FAIL${NC}" + echo "" + + echo -e "${BLUE}Test 2: HTTP request to fake internet${NC}" + ip netns exec ${NETNS_INTERNET} timeout 5 python3 -m http.server 8080 >/dev/null 2>&1 & + local pid=$! + sleep 1 + curl -s --max-time 5 http://${IP_INET_GATEWAY%/*}:8080 >/dev/null && \ + echo -e "${GREEN}✓ PASS${NC}" || echo -e "${RED}✗ FAIL${NC}" + kill $pid 2>/dev/null + echo "" + + echo -e "${BLUE}Test 3: DNS resolution${NC}" + dig @${IP_INET_GATEWAY%/*} google.com +short +time=2 >/dev/null && \ + echo -e "${GREEN}✓ PASS${NC}" || echo -e "${RED}✗ FAIL${NC}" + echo "" + + echo -e "${YELLOW}Note: For full testing, attach your KVMs and test from there${NC}" +} + +# Teardown network +teardown_network() { + echo -e "${GREEN}Tearing down network simulation...${NC}" + + # Kill services + [ -f "${STATE_DIR}/dns.pid" ] && kill $(cat "${STATE_DIR}/dns.pid") 2>/dev/null + [ -f "${STATE_DIR}/http.pid" ] && kill $(cat "${STATE_DIR}/http.pid") 2>/dev/null + [ -f "${STATE_DIR}/icmp.pid" ] && kill $(cat "${STATE_DIR}/icmp.pid") 2>/dev/null + + # Remove traffic control + tc qdisc del dev ${VETH_EXIT_HOST} root 2>/dev/null || true + + # Delete bridges + ip link set ${BRIDGE_CLIENT} down 2>/dev/null || true + ip link set ${BRIDGE_EXIT} down 2>/dev/null || true + ip link set ${BRIDGE_WAN} down 2>/dev/null || true + ip link del ${BRIDGE_CLIENT} 2>/dev/null || true + ip link del ${BRIDGE_EXIT} 2>/dev/null || true + ip link del ${BRIDGE_WAN} 2>/dev/null || true + + # Delete namespaces (this removes veth pairs automatically) + ip netns del ${NETNS_EXIT} 2>/dev/null || true + ip netns del ${NETNS_INTERNET} 2>/dev/null || true + + # Clean iptables + iptables -t nat -D POSTROUTING -s 10.10.1.0/24 -o ${BRIDGE_EXIT} -j MASQUERADE 2>/dev/null || true + + # Clean state + rm -rf "${STATE_DIR}" + + echo -e "${GREEN}✓ Teardown complete${NC}" +} + +# Main +check_root +init_state + +case "${1:-}" in + setup) + setup_network "${2:-100}" "${3:-10}" + ;; + status) + show_status + ;; + logs) + show_logs + ;; + adjust) + adjust_network "$2" "$3" + ;; + test) + run_tests + ;; + teardown) + teardown_network + ;; + *) + usage + ;; +esac + +exit 0 diff --git a/test-env/provision-vm.sh b/test-env/provision-vm.sh new file mode 100755 index 0000000..97bfb32 --- /dev/null +++ b/test-env/provision-vm.sh @@ -0,0 +1,118 @@ +#!/bin/bash + +# Default Configuration +VM_NAME="kernel-test-lab" +VCPUS=2 +RAM=4096 +DISK_SIZE=20 +BRIDGE_INT="virbr0" +ISO_DIR="/var/lib/libvirt/images" +ISO_PATH="" + +#OS_VARIANT=$(osinfo-query os | grep -i "$(basename "$ISO_PATH" | cut -d'-' -f1)" | head -n1 | awk '{print $1}') +# more than likely this will be set to none or "" see: https://linux.die.net/man/1/virt-install +OS_VARIANT="" + +usage() { + cat << EOF +┌──────────────────────────────────────────────────────────┐ +│░█░█░█░█░█▄█░░░█▀█░█▀▄░█▀█░█░█░▀█▀░█▀▀░▀█▀░█▀█░█▀█░█▀▀░█▀▄│ +│░█▀▄░▀▄▀░█░█░░░█▀▀░█▀▄░█░█░▀▄▀░░█░░▀▀█░░█░░█░█░█░█░█▀▀░█▀▄│ +│░▀░▀░░▀░░▀░▀░░░▀░░░▀░▀░▀▀▀░░▀░░▀▀▀░▀▀▀░▀▀▀░▀▀▀░▀░▀░▀▀▀░▀░▀│ +└──────────────────────────────────────────────────────────┘ +*KVM Provisioning Utility for Isolated Kernel and Network Testing. + +Usage: sudo $0 [OPTIONS] + +Options: + -n NAME Name of the Virtual Machine (Default: kernel-test-lab) + -c CPUS Number of vCPUs to allocate (Default: 2) + -m RAM Memory allocation in MB (Default: 4096) + -d DISK Disk size in GB (Default: 20) + -b BRIDGE Host bridge interface (Default: virbr0) + -i ISO Absolute path to ISO (Bypasses interactive selection) + -ov OSVARIANT Optimize the guest configuration for a specific os variant (Bypasses Interactive selection) + -h Display this technical manual + +Example: + sudo ./provision-lab.sh -n kernel-dev-01 -c 4 -m 8192 -d 40 +EOF + exit 0 +} + +if [[ $EUID -ne 0 ]]; then + echo "Error: Execution requires root privileges." >&2 + exit 1 +fi + +while getopts "n:c:m:d:b:i:ov:h" opt; do + case ${opt} in + n) VM_NAME=$OPTARG ;; + c) VCPUS=$OPTARG ;; + m) RAM=$OPTARG ;; + d) DISK_SIZE=$OPTARG ;; + b) BRIDGE_INT=$OPTARG ;; + i) ISO_PATH=$OPTARG ;; + ov) OS_VARIANT=$OPTARG ;; + h) usage ;; + *) usage ;; + esac +done + +if [[ -z "$ISO_PATH" ]]; then + echo "--- KVM Lab Provisioner ---" + echo "Scanning $ISO_DIR for installation media..." + + mapfile -t ISO_LIST < <(ls "$ISO_DIR"/*.iso 2>/dev/null) + + if [ ${#ISO_LIST[@]} -eq 0 ]; then + echo "Error: No ISO files detected in $ISO_DIR" >&2 + exit 1 + fi + + select opt in "${ISO_LIST[@]}" "Cancel"; do + if [[ "$opt" == "Cancel" ]]; then exit 0; fi + if [[ -f "$opt" ]]; then + ISO_PATH="$opt" + break + else + echo "Invalid selection." + fi + done +fi + +if [[ -z "$OS_VARIANT" ]]; then + echo "Need a OS Variant..." + read OS_VARIANT +fi + +echo "Provisioning Details:" +echo " Name: $VM_NAME" +echo " vCPUs: $VCPUS" +echo " RAM: $RAM MB" +echo " Storage: $DISK_SIZE GB" +echo " Bridge: $BRIDGE_INT" +echo " ISO: $ISO_PATH" +echo " Profile: $OS_VARIANT" + +# error was: ERROR Kernel arguments are only supported with location or kernel installs +# changed from location to cdrom; can't pass kernel args unless you use --location +# --extra-args "console=ttyS0,115200n8 serial" \ +virt-install \ + --name "$VM_NAME" \ + --vcpus "$VCPUS" \ + --memory "$RAM" \ + --disk "size=$DISK_SIZE,format=qcow2" \ + --os-variant "$OS_VARIANT" \ + --location "$ISO_PATH" \ + --extra-args "console=ttyS0,115200n8 serial" \ + --network "bridge=$BRIDGE_INT" \ + --graphics none \ + --console pty,target_type=serial \ + --check all=off \ + --hvm \ + --virt-type kvm \ + --noreboot + +echo "--- Provisioning Sequence Complete ---" +echo "To begin testing: virsh start $VM_NAME --console" |
