Apache reverse proxy for webapp, UI improvements
- Move Flask to localhost:9010, Apache serves port 9009 with static file handling and reverse proxy to fix intermittent asset loading on remote clients - Add "PXE Manager" branding beneath logo in sidebar - Increase code editor size (startnet.cmd and unattend XML) to 70vh - Add test-lab.sh for full lab VM testing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -331,6 +331,7 @@
|
|||||||
port: "{{ item }}"
|
port: "{{ item }}"
|
||||||
proto: "{{ 'udp' if item in ['67','69'] else 'tcp' }}"
|
proto: "{{ 'udp' if item in ['67','69'] else 'tcp' }}"
|
||||||
loop:
|
loop:
|
||||||
|
- "22"
|
||||||
- "67"
|
- "67"
|
||||||
- "69"
|
- "69"
|
||||||
- "80"
|
- "80"
|
||||||
@@ -430,6 +431,7 @@
|
|||||||
copy:
|
copy:
|
||||||
dest: /etc/apache2/sites-available/pxe-server.conf
|
dest: /etc/apache2/sites-available/pxe-server.conf
|
||||||
content: |
|
content: |
|
||||||
|
Listen 9009
|
||||||
<VirtualHost *:80>
|
<VirtualHost *:80>
|
||||||
DocumentRoot {{ web_root }}
|
DocumentRoot {{ web_root }}
|
||||||
<Directory "{{ web_root }}">
|
<Directory "{{ web_root }}">
|
||||||
@@ -438,8 +440,19 @@
|
|||||||
Require all granted
|
Require all granted
|
||||||
</Directory>
|
</Directory>
|
||||||
ProxyPreserveHost On
|
ProxyPreserveHost On
|
||||||
ProxyPass /manage http://127.0.0.1:9009/
|
ProxyPass /manage http://127.0.0.1:9010/
|
||||||
ProxyPassReverse /manage http://127.0.0.1:9009/
|
ProxyPassReverse /manage http://127.0.0.1:9010/
|
||||||
|
</VirtualHost>
|
||||||
|
<VirtualHost *:9009>
|
||||||
|
Alias /static /opt/pxe-webapp/static
|
||||||
|
<Directory "/opt/pxe-webapp/static">
|
||||||
|
Require all granted
|
||||||
|
Options -Indexes
|
||||||
|
</Directory>
|
||||||
|
ProxyPreserveHost On
|
||||||
|
ProxyPass /static !
|
||||||
|
ProxyPass / http://127.0.0.1:9010/
|
||||||
|
ProxyPassReverse / http://127.0.0.1:9010/
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
|
|
||||||
- name: "Enable Apache proxy modules"
|
- name: "Enable Apache proxy modules"
|
||||||
|
|||||||
332
test-lab.sh
Executable file
332
test-lab.sh
Executable file
@@ -0,0 +1,332 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# test-lab.sh — Full PXE lab: server + client VMs on an isolated network
|
||||||
|
#
|
||||||
|
# Creates an isolated libvirt network, boots the PXE server from the
|
||||||
|
# Proxmox installer ISO, then launches a UEFI PXE client to test the
|
||||||
|
# full boot chain (DHCP -> TFTP -> iPXE -> boot menu).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./test-lab.sh /path/to/ubuntu-24.04.iso # Launch server
|
||||||
|
# ./test-lab.sh --client # Launch PXE client
|
||||||
|
# ./test-lab.sh --status # Check if server is ready
|
||||||
|
# ./test-lab.sh --destroy # Remove everything
|
||||||
|
#
|
||||||
|
# Workflow:
|
||||||
|
# 1. Run the script with the Ubuntu ISO — server VM starts installing
|
||||||
|
# 2. Wait ~15 minutes (monitor with: sudo virsh console pxe-lab-server)
|
||||||
|
# 3. Run --status to check if PXE services are up
|
||||||
|
# 4. Run --client to launch a PXE client VM
|
||||||
|
# 5. Open virt-viewer to watch the client PXE boot:
|
||||||
|
# virt-viewer pxe-lab-client
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
NET_NAME="pxe-lab"
|
||||||
|
SERVER_NAME="pxe-lab-server"
|
||||||
|
CLIENT_NAME="pxe-lab-client"
|
||||||
|
SERVER_DISK="/var/lib/libvirt/images/${SERVER_NAME}.qcow2"
|
||||||
|
PROXMOX_ISO="$SCRIPT_DIR/pxe-server-proxmox.iso"
|
||||||
|
VM_RAM=4096
|
||||||
|
VM_CPUS=2
|
||||||
|
VM_DISK_SIZE=40 # GB
|
||||||
|
|
||||||
|
# --- Helper: check if we can run virsh ---
|
||||||
|
check_virsh() {
|
||||||
|
if ! virsh net-list &>/dev/null; then
|
||||||
|
echo "ERROR: Cannot connect to libvirt. Are you in the 'libvirt' group?"
|
||||||
|
echo " sudo usermod -aG libvirt $USER && newgrp libvirt"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Helper: ensure network exists ---
|
||||||
|
ensure_network() {
|
||||||
|
if ! virsh net-info "$NET_NAME" &>/dev/null; then
|
||||||
|
echo "Creating isolated network ($NET_NAME)..."
|
||||||
|
NET_XML=$(mktemp)
|
||||||
|
cat > "$NET_XML" << EOF
|
||||||
|
<network>
|
||||||
|
<name>$NET_NAME</name>
|
||||||
|
<bridge name="virbr-pxe" stp="on" delay="0"/>
|
||||||
|
</network>
|
||||||
|
EOF
|
||||||
|
virsh net-define "$NET_XML" >/dev/null
|
||||||
|
rm "$NET_XML"
|
||||||
|
fi
|
||||||
|
if ! virsh net-info "$NET_NAME" 2>/dev/null | grep -q "Active:.*yes"; then
|
||||||
|
virsh net-start "$NET_NAME" >/dev/null
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# =====================================================================
|
||||||
|
# --destroy: Remove everything
|
||||||
|
# =====================================================================
|
||||||
|
if [ "${1:-}" = "--destroy" ]; then
|
||||||
|
check_virsh
|
||||||
|
echo "Destroying PXE lab environment..."
|
||||||
|
virsh destroy "$CLIENT_NAME" 2>/dev/null || true
|
||||||
|
virsh undefine "$CLIENT_NAME" --nvram 2>/dev/null || true
|
||||||
|
virsh vol-delete "${CLIENT_NAME}.qcow2" --pool default 2>/dev/null || true
|
||||||
|
virsh destroy "$SERVER_NAME" 2>/dev/null || true
|
||||||
|
virsh undefine "$SERVER_NAME" 2>/dev/null || true
|
||||||
|
virsh vol-delete "${SERVER_NAME}.qcow2" --pool default 2>/dev/null || true
|
||||||
|
rm -f "/tmp/${SERVER_NAME}-vmlinuz" "/tmp/${SERVER_NAME}-initrd"
|
||||||
|
virsh net-destroy "$NET_NAME" 2>/dev/null || true
|
||||||
|
virsh net-undefine "$NET_NAME" 2>/dev/null || true
|
||||||
|
echo "Done."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =====================================================================
|
||||||
|
# --status: Check if PXE server is ready
|
||||||
|
# =====================================================================
|
||||||
|
if [ "${1:-}" = "--status" ]; then
|
||||||
|
check_virsh
|
||||||
|
echo "PXE Lab Status"
|
||||||
|
echo "============================================"
|
||||||
|
|
||||||
|
# Network
|
||||||
|
if virsh net-info "$NET_NAME" &>/dev/null; then
|
||||||
|
echo " Network ($NET_NAME): $(virsh net-info "$NET_NAME" 2>/dev/null | grep Active | awk '{print $2}')"
|
||||||
|
else
|
||||||
|
echo " Network ($NET_NAME): not defined"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Server VM
|
||||||
|
if virsh dominfo "$SERVER_NAME" &>/dev/null; then
|
||||||
|
STATE=$(virsh domstate "$SERVER_NAME" 2>/dev/null)
|
||||||
|
echo " Server ($SERVER_NAME): $STATE"
|
||||||
|
else
|
||||||
|
echo " Server ($SERVER_NAME): not defined"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Client VM
|
||||||
|
if virsh dominfo "$CLIENT_NAME" &>/dev/null; then
|
||||||
|
STATE=$(virsh domstate "$CLIENT_NAME" 2>/dev/null)
|
||||||
|
echo " Client ($CLIENT_NAME): $STATE"
|
||||||
|
else
|
||||||
|
echo " Client ($CLIENT_NAME): not defined"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try to check PXE services on the server
|
||||||
|
# Add a temporary IP to the bridge so we can reach the server
|
||||||
|
echo ""
|
||||||
|
echo "Checking PXE server services..."
|
||||||
|
BRIDGE_HAS_IP=false
|
||||||
|
ADDED_IP=false
|
||||||
|
if ip addr show virbr-pxe 2>/dev/null | grep -q "10.9.100.254"; then
|
||||||
|
BRIDGE_HAS_IP=true
|
||||||
|
else
|
||||||
|
# Need sudo for IP manipulation — try it, skip if unavailable
|
||||||
|
if sudo -n ip addr add 10.9.100.254/24 dev virbr-pxe 2>/dev/null; then
|
||||||
|
BRIDGE_HAS_IP=true
|
||||||
|
ADDED_IP=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$BRIDGE_HAS_IP" = true ]; then
|
||||||
|
# Check each service with a short timeout
|
||||||
|
for check in \
|
||||||
|
"DHCP/TFTP (dnsmasq):10.9.100.1:69:udp" \
|
||||||
|
"HTTP (Apache):10.9.100.1:80:tcp" \
|
||||||
|
"iPXE boot script:10.9.100.1:4433:tcp" \
|
||||||
|
"Samba:10.9.100.1:445:tcp" \
|
||||||
|
"Webapp:10.9.100.1:9009:tcp"; do
|
||||||
|
LABEL="${check%%:*}"
|
||||||
|
REST="${check#*:}"
|
||||||
|
HOST="${REST%%:*}"
|
||||||
|
REST="${REST#*:}"
|
||||||
|
PORT="${REST%%:*}"
|
||||||
|
PROTO="${REST#*:}"
|
||||||
|
|
||||||
|
if [ "$PROTO" = "tcp" ]; then
|
||||||
|
if timeout 2 bash -c "echo >/dev/tcp/$HOST/$PORT" 2>/dev/null; then
|
||||||
|
echo " [UP] $LABEL (port $PORT)"
|
||||||
|
else
|
||||||
|
echo " [DOWN] $LABEL (port $PORT)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# UDP — just check if host is reachable
|
||||||
|
if ping -c1 -W1 "$HOST" &>/dev/null; then
|
||||||
|
echo " [PING] $LABEL (host reachable)"
|
||||||
|
else
|
||||||
|
echo " [DOWN] $LABEL (host unreachable)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Clean up the temporary IP (only if we added it)
|
||||||
|
if [ "$ADDED_IP" = true ]; then
|
||||||
|
sudo -n ip addr del 10.9.100.254/24 dev virbr-pxe 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " (Cannot reach server — bridge not available)"
|
||||||
|
echo " Use 'sudo virsh console $SERVER_NAME' to check manually"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Commands:"
|
||||||
|
echo " sudo virsh console $SERVER_NAME # Server serial console"
|
||||||
|
echo " virt-viewer $CLIENT_NAME # Client VNC display"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =====================================================================
|
||||||
|
# --client: Launch PXE client VM
|
||||||
|
# =====================================================================
|
||||||
|
if [ "${1:-}" = "--client" ]; then
|
||||||
|
check_virsh
|
||||||
|
ensure_network
|
||||||
|
|
||||||
|
# Check if server VM exists and is running
|
||||||
|
if ! virsh domstate "$SERVER_NAME" 2>/dev/null | grep -q "running"; then
|
||||||
|
echo "WARNING: Server VM ($SERVER_NAME) is not running."
|
||||||
|
echo " The PXE client needs the server for DHCP and boot files."
|
||||||
|
read -rp " Continue anyway? (y/N): " PROCEED
|
||||||
|
if [[ ! "$PROCEED" =~ ^[Yy]$ ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove existing client if present
|
||||||
|
if virsh dominfo "$CLIENT_NAME" &>/dev/null; then
|
||||||
|
echo "Removing existing client VM..."
|
||||||
|
virsh destroy "$CLIENT_NAME" 2>/dev/null || true
|
||||||
|
virsh undefine "$CLIENT_NAME" --nvram 2>/dev/null || true
|
||||||
|
virsh vol-delete "${CLIENT_NAME}.qcow2" --pool default 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Launching PXE client ($CLIENT_NAME)..."
|
||||||
|
echo " UEFI PXE boot on network: $NET_NAME"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
virsh vol-create-as default "${CLIENT_NAME}.qcow2" "${VM_DISK_SIZE}G" --format qcow2 >/dev/null
|
||||||
|
|
||||||
|
virt-install \
|
||||||
|
--name "$CLIENT_NAME" \
|
||||||
|
--memory "$VM_RAM" \
|
||||||
|
--vcpus "$VM_CPUS" \
|
||||||
|
--disk "vol=default/${CLIENT_NAME}.qcow2" \
|
||||||
|
--network network="$NET_NAME",model=virtio \
|
||||||
|
--os-variant ubuntu24.04 \
|
||||||
|
--boot uefi,network \
|
||||||
|
--graphics vnc,listen=0.0.0.0 \
|
||||||
|
--noautoconsole
|
||||||
|
|
||||||
|
# Get VNC port
|
||||||
|
VNC_PORT=$(virsh vncdisplay "$CLIENT_NAME" 2>/dev/null | sed 's/://' || echo "?")
|
||||||
|
VNC_PORT=$((5900 + VNC_PORT))
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo "PXE client launched!"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "Watch the PXE boot:"
|
||||||
|
echo " virt-viewer $CLIENT_NAME"
|
||||||
|
echo " (or VNC to localhost:$VNC_PORT)"
|
||||||
|
echo ""
|
||||||
|
echo "Expected boot sequence:"
|
||||||
|
echo " 1. UEFI firmware -> PXE boot"
|
||||||
|
echo " 2. DHCP from server (10.9.100.x)"
|
||||||
|
echo " 3. TFTP download ipxe.efi"
|
||||||
|
echo " 4. iPXE loads boot menu from port 4433"
|
||||||
|
echo " 5. GE Aerospace PXE Boot Menu appears"
|
||||||
|
echo ""
|
||||||
|
echo "Manage:"
|
||||||
|
echo " sudo virsh reboot $CLIENT_NAME # Retry PXE boot"
|
||||||
|
echo " sudo virsh destroy $CLIENT_NAME # Stop client"
|
||||||
|
echo " $0 --destroy # Remove everything"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =====================================================================
|
||||||
|
# Default: Launch PXE server VM
|
||||||
|
# =====================================================================
|
||||||
|
check_virsh
|
||||||
|
|
||||||
|
UBUNTU_ISO="${1:-}"
|
||||||
|
if [ -z "$UBUNTU_ISO" ] || [ ! -f "$UBUNTU_ISO" ]; then
|
||||||
|
echo "Usage: sudo $0 /path/to/ubuntu-24.04-live-server-amd64.iso"
|
||||||
|
echo ""
|
||||||
|
echo "Commands:"
|
||||||
|
echo " $0 /path/to/ubuntu.iso Launch PXE server VM"
|
||||||
|
echo " $0 --client Launch PXE client VM"
|
||||||
|
echo " $0 --status Check server readiness"
|
||||||
|
echo " $0 --destroy Remove everything"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if Proxmox ISO exists, build if not
|
||||||
|
if [ ! -f "$PROXMOX_ISO" ]; then
|
||||||
|
echo "Proxmox ISO not found. Building it first..."
|
||||||
|
echo ""
|
||||||
|
"$SCRIPT_DIR/build-proxmox-iso.sh" "$UBUNTU_ISO"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check server doesn't already exist
|
||||||
|
if virsh dominfo "$SERVER_NAME" &>/dev/null; then
|
||||||
|
echo "ERROR: Server VM already exists. Destroy first with: sudo $0 --destroy"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "============================================"
|
||||||
|
echo "PXE Lab Environment Setup"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# --- Step 1: Create isolated network ---
|
||||||
|
echo "[1/3] Setting up isolated network ($NET_NAME)..."
|
||||||
|
ensure_network
|
||||||
|
echo " Bridge: virbr-pxe (isolated, no host DHCP)"
|
||||||
|
|
||||||
|
# --- Step 2: Extract kernel/initrd for direct boot ---
|
||||||
|
echo "[2/3] Extracting kernel and initrd from ISO..."
|
||||||
|
KERNEL="/tmp/${SERVER_NAME}-vmlinuz"
|
||||||
|
INITRD="/tmp/${SERVER_NAME}-initrd"
|
||||||
|
7z e -o/tmp -y "$PROXMOX_ISO" casper/vmlinuz casper/initrd >/dev/null 2>&1
|
||||||
|
mv /tmp/vmlinuz "$KERNEL"
|
||||||
|
mv /tmp/initrd "$INITRD"
|
||||||
|
echo " Extracted vmlinuz and initrd"
|
||||||
|
|
||||||
|
# --- Step 3: Launch server VM ---
|
||||||
|
echo "[3/3] Launching PXE server ($SERVER_NAME)..."
|
||||||
|
|
||||||
|
virsh vol-create-as default "${SERVER_NAME}.qcow2" "${VM_DISK_SIZE}G" --format qcow2 >/dev/null
|
||||||
|
|
||||||
|
virt-install \
|
||||||
|
--name "$SERVER_NAME" \
|
||||||
|
--memory "$VM_RAM" \
|
||||||
|
--vcpus "$VM_CPUS" \
|
||||||
|
--disk "vol=default/${SERVER_NAME}.qcow2" \
|
||||||
|
--disk path="$PROXMOX_ISO",device=cdrom,readonly=on \
|
||||||
|
--network network="$NET_NAME" \
|
||||||
|
--os-variant ubuntu24.04 \
|
||||||
|
--graphics none \
|
||||||
|
--console pty,target_type=serial \
|
||||||
|
--install kernel="$KERNEL",initrd="$INITRD",kernel_args="console=ttyS0,115200n8 autoinstall ds=nocloud\;s=/cdrom/server/" \
|
||||||
|
--noautoconsole
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo "PXE server VM launched!"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "The autoinstall + first-boot will take ~15 minutes."
|
||||||
|
echo ""
|
||||||
|
echo "Step 1 — Monitor the server install:"
|
||||||
|
echo " virsh console $SERVER_NAME"
|
||||||
|
echo " (Ctrl+] to detach)"
|
||||||
|
echo ""
|
||||||
|
echo "Step 2 — Check when services are ready:"
|
||||||
|
echo " $0 --status"
|
||||||
|
echo ""
|
||||||
|
echo "Step 3 — Launch a PXE client to test booting:"
|
||||||
|
echo " $0 --client"
|
||||||
|
echo ""
|
||||||
|
echo "Cleanup:"
|
||||||
|
echo " $0 --destroy"
|
||||||
|
echo ""
|
||||||
@@ -958,4 +958,4 @@ def inject_globals():
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run(host="0.0.0.0", port=9009, debug=False)
|
app.run(host="127.0.0.1", port=9010, debug=False, threaded=True)
|
||||||
|
|||||||
@@ -55,8 +55,9 @@
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
border-bottom: 1px solid rgba(255,255,255,0.08);
|
border-bottom: 1px solid rgba(255,255,255,0.08);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.3rem;
|
||||||
}
|
}
|
||||||
.sidebar .brand .bi {
|
.sidebar .brand .bi {
|
||||||
font-size: 1.3rem;
|
font-size: 1.3rem;
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
.cmd-editor {
|
.cmd-editor {
|
||||||
font-family: 'Consolas', 'Courier New', monospace;
|
font-family: 'Consolas', 'Courier New', monospace;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
min-height: 400px;
|
min-height: 600px;
|
||||||
|
height: 70vh;
|
||||||
|
resize: vertical;
|
||||||
background-color: #1e1e1e;
|
background-color: #1e1e1e;
|
||||||
color: #d4d4d4;
|
color: #d4d4d4;
|
||||||
border: 1px solid #333;
|
border: 1px solid #333;
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
.raw-xml-editor {
|
.raw-xml-editor {
|
||||||
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
min-height: 500px;
|
min-height: 600px;
|
||||||
|
height: 70vh;
|
||||||
tab-size: 2;
|
tab-size: 2;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
|
|||||||
Reference in New Issue
Block a user