Add README, update docs, fix CRLF, SSH, and playbook network detection

- Add comprehensive README.md with full project documentation
- Update SETUP.md to reflect current state (7 image types, webapp, boot tools, Samba shares)
- Enable SSH in autoinstall user-data for remote access
- Fix ansible_default_ipv4.interface error when no default gateway exists
- Fix Windows CRLF line endings on all shell scripts and YAML files
- Fix test-vm.sh: use --install kernel extraction instead of --location, don't delete source ISO on --destroy

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-02-06 17:38:55 -05:00
parent 725c8f43de
commit 851225d062
8 changed files with 1622 additions and 1295 deletions

226
README.md Normal file
View File

@@ -0,0 +1,226 @@
# GE Aerospace PXE Boot Server
Automated, air-gapped PXE boot server for deploying GE Aerospace Windows images. Built on Ubuntu 24.04 Server with zero-touch provisioning via autoinstall and Ansible.
## Overview
This project provides a complete, repeatable build process for a PXE boot server that serves Windows PE images to client machines on an isolated network. Everything runs offline after initial setup — no internet required on the target server.
### Boot Chain
```
Client PXE boot (UEFI Secure Boot)
-> iPXE (TFTP, Broadcom-signed for Secure Boot)
-> iPXE boot menu (HTTP, port 4433)
-> User selects boot option:
├── Windows PE -> wimboot -> boot.wim -> startnet.cmd -> Samba share -> Image deployment
├── Clonezilla -> vmlinuz/initrd -> Disk cloning/imaging
├── Blancco -> vmlinuz/initrd -> NIST 800-88 drive erasure
└── Memtest86+ -> Memory diagnostics
```
### Services
| Service | Port | Purpose |
|-------------|-----------|------------------------------------------|
| dnsmasq | 67/udp | DHCP (10.9.100.10-100, 12h lease) |
| dnsmasq | 69/udp | TFTP (serves ipxe.efi) |
| Apache | 80/tcp | HTTP (wimboot, WinPE boot files, proxy) |
| Apache | 4433/tcp | iPXE boot script (GetPxeScript.aspx) |
| Samba | 445/tcp | Deployment content + Clonezilla + Blancco|
| Flask Webapp| 9009/tcp | Web management interface |
### Network
- **PXE server IP:** `10.9.100.1/24`
- **DHCP range:** `10.9.100.10` - `10.9.100.100`
- **Firewall:** UFW deny-by-default, only service ports open
## Quick Start
### Prerequisites
**On your workstation (internet-connected):**
- Ubuntu 24.04 (or Linux Mint / similar) for downloading packages
- Ubuntu Server 24.04 ISO
- GE Aerospace Media Creator LITE (for WinPE images)
- USB drive >= 8 GB (32+ GB if bundling WinPE images)
**GE Access Packages (MyAccess portal):**
- EPM Rufus Exception Request
- EPM DT Functions
- DLP - Encrypted Removable (USB) Long Term Access
### Step 1: Download Offline Packages
```bash
./download-packages.sh
```
Downloads all .deb packages and Python wheels for offline installation (~140 MB of debs, ~20 MB of wheels).
### Step 2: Prepare Boot Tools (optional)
```bash
./prepare-boot-tools.sh /path/to/blancco.iso /path/to/clonezilla.zip /path/to/memtest.bin
```
Extracts and configures boot tool files (Blancco, Clonezilla, Memtest86+). Automatically patches Blancco's config.img to auto-save erasure reports to the PXE server's Samba share.
### Step 3: Build the USB
```bash
sudo ./build-usb.sh /dev/sdX /path/to/ubuntu-24.04-live-server-amd64.iso
```
Creates a bootable USB with two partitions:
- **Partition 1:** Ubuntu Server installer
- **Partition 2:** CIDATA (autoinstall config, offline .debs, pip wheels, Ansible playbook, webapp, boot tools)
### Step 4: Install on Target Server
1. Insert USB into the target machine
2. Press F12 and boot from USB
3. Ubuntu auto-installs with no interaction
4. After reboot, the first-boot script:
- Installs all offline .deb packages
- Runs the Ansible playbook (configures dnsmasq, Apache, Samba, UFW, webapp)
- Configures static IP `10.9.100.1/24`
5. Move the server's wired NIC to the isolated PXE switch
### Step 5: Access the Web Interface
Open `http://10.9.100.1:9009` from any machine on the isolated network.
## Web Management Interface
The Flask webapp (port 9009) provides a browser-based management UI:
- **Dashboard** — Service status overview, disk usage, connected DHCP clients
- **Image Import** — Import WinPE deployment images from USB drives
- **Unattend Editor** — Edit Windows unattend.xml files per image type (XML syntax highlighting)
- **startnet.cmd Editor** — Modify the startnet.cmd inside boot.wim without Windows (uses wimtools)
- **Clonezilla Backups** — Upload, download, and manage disk backup images
- **Blancco Reports** — View, download, and manage drive erasure reports (auto-collected via Samba)
- **Audit Log** — Activity history for all write operations (imports, edits, deletes)
### Image Types Supported
| Image Type | Domain | Description |
|----------------------|-----------------|-------------------------|
| gea-standard | geaerospace.com | Standard desktop |
| gea-engineer | geaerospace.com | Engineering desktop |
| gea-shopfloor | geaerospace.com | Shop floor kiosk |
| ge-standard | ge.com | Standard desktop |
| ge-engineer | ge.com | Engineering desktop |
| ge-shopfloor-lockdown| ge.com | Shop floor (locked) |
| ge-shopfloor-mce | ge.com | Shop floor (MCE) |
## Project Structure
```
pxe-server/
├── autoinstall/
│ ├── user-data # Cloud-init autoinstall config + first-boot script
│ └── meta-data # Cloud-init metadata (required, empty)
├── playbook/
│ ├── pxe_server_setup.yml # Ansible playbook: all server configuration
│ └── inventory.ini # Ansible inventory
├── webapp/
│ ├── app.py # Flask application (~900 lines)
│ ├── requirements.txt # Python deps (flask, lxml)
│ ├── static/
│ │ ├── ge-aerospace-logo.svg # GE Aerospace branding
│ │ ├── favicon.ico # Browser favicon
│ │ ├── app.js # Frontend JavaScript
│ │ ├── bootstrap.min.css # Bootstrap 5 (bundled offline)
│ │ ├── bootstrap.bundle.min.js
│ │ ├── bootstrap-icons.min.css
│ │ └── fonts/ # Icon fonts (woff/woff2)
│ └── templates/
│ ├── base.html # Layout with GE branding and sidebar nav
│ ├── dashboard.html # Service status and overview
│ ├── import.html # USB image import wizard
│ ├── unattend_editor.html # XML editor for unattend files
│ ├── startnet_editor.html # startnet.cmd WIM editor
│ ├── backups.html # Clonezilla backup management
│ ├── reports.html # Blancco erasure reports
│ └── audit.html # Activity audit log
├── unattend/
│ └── FlatUnattendW10.xml # Windows unattend.xml template
├── boot-tools/ # Extracted boot tool files (gitignored)
│ ├── blancco/ # Blancco Drive Eraser (Arch Linux-based)
│ ├── clonezilla/ # Clonezilla Live
│ └── memtest/ # Memtest86+
├── offline-packages/ # .deb files (gitignored, built by download-packages.sh)
├── pip-wheels/ # Python wheels (gitignored, built by download-packages.sh)
├── download-packages.sh # Downloads offline .debs + pip wheels
├── build-usb.sh # Builds the installer USB (2-partition)
├── prepare-boot-tools.sh # Extracts and patches boot tool files
├── test-vm.sh # KVM test environment for validation
├── SETUP.md # Detailed setup guide
└── setup-guide-original.txt # Original manual setup notes (reference)
```
## Testing with KVM
A test VM script is included for validating the full provisioning pipeline without dedicated hardware:
```bash
# Download Ubuntu Server ISO
wget -O ~/Downloads/ubuntu-24.04.3-live-server-amd64.iso \
https://releases.ubuntu.com/noble/ubuntu-24.04.3-live-server-amd64.iso
# Launch test VM (requires libvirt/KVM)
sudo ./test-vm.sh ~/Downloads/ubuntu-24.04.3-live-server-amd64.iso
# Watch install progress
sudo virsh console pxe-test
# Clean up when done
sudo ./test-vm.sh --destroy
```
The test VM creates an isolated libvirt network (10.9.100.0/24) and runs the full autoinstall + Ansible provisioning.
## Samba Shares
| Share | Path | Purpose |
|-----------------|---------------------------|--------------------------------|
| winpeapps | /srv/samba/winpeapps | WinPE deployment images |
| clonezilla | /srv/samba/clonezilla | Clonezilla disk backup images |
| blancco-reports | /srv/samba/blancco-reports| Blancco erasure reports (auto) |
All shares use guest access (no authentication) for ease of use on the isolated network.
## Blancco Drive Erasure
Blancco Drive Eraser is configured to automatically save XML erasure reports to the PXE server's Samba share (`blancco-reports`). The `prepare-boot-tools.sh` script patches Blancco's `config.img` to set:
- Network share hostname: `10.9.100.1`
- Share path: `blancco-reports`
- Auto-backup: enabled
- Erasure standard: NIST 800-88 Purge
Reports are viewable and downloadable from the web interface at `http://10.9.100.1:9009/reports`.
## Known Issues / TODO
- **wimtools** must be downloaded with `download-packages.sh` before building USB (used for startnet.cmd editing)
- **Apache VirtualHost conflict**: Two VirtualHosts on port 80 (default site and pxe-webapp proxy) — should disable default or merge
- **WinPE boot files** (wimboot, BCD, boot.sdi, bootx64.efi, boot.stl, boot.wim) must be manually placed on USB or sourced from the legacy `WestJeff` playbook folder
- **CSRF protection** not yet implemented on webapp POST forms
- Test VM requires re-download of Ubuntu ISO if `--destroy` is run (fixed in latest test-vm.sh)
## Commit History
| Commit | Description |
|---------|--------------------------------------------------------------------|
| 5791bd1 | Initial project setup: automated PXE server provisioning |
| cee4ecd | Add web management UI, offline packages, WinPE consolidation |
| f614596 | Fix unattend.xml path to match actual image structure |
| e7313c2 | Add multi-boot PXE menu, Clonezilla backups, GE Aerospace branding|
| 89b5834 | Add wimtools and startnet.cmd editor for boot.wim modification |
| 05dbb7e | Add Blancco erasure reports Samba share and webapp viewer |
| ef75839 | Auto-patch Blancco config.img for network report storage |
| 92c9b0f | Fix review findings: offline assets, security, audit logging |
| 725c8f4 | Change webapp to port 9009, add test VM script |

140
SETUP.md
View File

@@ -7,21 +7,23 @@ Automated build process for deploying an Ubuntu-based PXE boot server that hosts
``` ```
Client PXE boot Client PXE boot
-> Broadcom signed iPXE (Secure Boot) -> Broadcom signed iPXE (Secure Boot)
-> wimboot (HTTP from Apache) -> iPXE boot menu (HTTP, port 4433)
-> WinPE (boot.wim) ├── Windows PE -> wimboot -> boot.wim -> startnet.cmd -> Samba -> Image deployment
-> startnet.cmd maps Samba shares ├── Clonezilla -> vmlinuz/initrd -> Disk cloning/imaging
-> GE Aerospace image deployment ├── Blancco -> vmlinuz/initrd -> NIST 800-88 drive erasure (auto-reports)
└── Memtest86+ -> Memory diagnostics
``` ```
### Services on the PXE Server ### Services on the PXE Server
| Service | Port | Purpose | | Service | Port | Purpose |
|----------|----------|--------------------------------------| |-------------|-----------|------------------------------------------|
| dnsmasq | 67/udp | DHCP (10.9.100.10100) | | dnsmasq | 67/udp | DHCP (10.9.100.10-100) |
| dnsmasq | 69/udp | TFTP (serves ipxe.efi) | | dnsmasq | 69/udp | TFTP (serves ipxe.efi) |
| Apache | 80/tcp | HTTP (wimboot, WinPE boot files) | | Apache | 80/tcp | HTTP (wimboot, WinPE boot files, proxy) |
| Apache | 4433/tcp | iPXE boot script (GetPxeScript.aspx) | | Apache | 4433/tcp | iPXE boot script (GetPxeScript.aspx) |
| Samba | 445/tcp | Deployment content shares | | Samba | 445/tcp | Deployment content + backup + reports |
| Flask Webapp| 9009/tcp | Web management interface |
## Prerequisites ## Prerequisites
@@ -31,7 +33,7 @@ Client PXE boot
### Software (on your workstation) ### Software (on your workstation)
- Ubuntu Server 24.04 ISO — https://ubuntu.com/download/server - Ubuntu Server 24.04 ISO — https://ubuntu.com/download/server
- Docker (for downloading offline packages) - Linux workstation (Ubuntu/Mint) for running download and build scripts
- GE Aerospace Media Creator LITE (for caching WinPE images) - GE Aerospace Media Creator LITE (for caching WinPE images)
### GE Access Packages ### GE Access Packages
@@ -41,15 +43,30 @@ Client PXE boot
## Setup Process ## Setup Process
### Step 1: Download Offline Packages (one-time, requires internet + Docker) ### Step 1: Download Offline Packages (one-time, requires internet)
```bash ```bash
./download-packages.sh ./download-packages.sh
``` ```
This runs an Ubuntu 24.04 Docker container to download all .deb packages (ansible, dnsmasq, apache2, samba, etc.) into `offline-packages/`. ~102 MB total. Downloads all .deb packages (ansible, dnsmasq, apache2, samba, wimtools, etc.) into `offline-packages/` and Python wheels (flask, lxml) into `pip-wheels/`. Approximately 252 packages (~140 MB) + 8 Python wheels.
### Step 2: Build the USB **Packages included:**
- **Server:** dnsmasq, apache2, samba, ufw, cron
- **Automation:** ansible
- **Tools:** wimtools, unzip, p7zip-full
- **Python:** python3-pip, python3-venv
- **Network:** network-manager, wpasupplicant, wireless-tools, linux-firmware
### Step 2: Prepare Boot Tools (optional)
```bash
./prepare-boot-tools.sh /path/to/blancco.iso /path/to/clonezilla.zip /path/to/memtest.bin
```
Extracts boot files for Blancco, Clonezilla, and Memtest86+ into the `boot-tools/` directory. Automatically patches Blancco's `config.img` to auto-save erasure reports to the PXE server's Samba share.
### Step 3: Build the USB
```bash ```bash
# Basic — server only (import WinPE images later) # Basic — server only (import WinPE images later)
@@ -60,69 +77,121 @@ sudo ./build-usb.sh /dev/sdX /path/to/ubuntu-24.04.iso /path/to/winpe-images
``` ```
This creates a bootable USB with: This creates a bootable USB with:
- Partition 1: Ubuntu Server installer - **Partition 1:** Ubuntu Server installer
- Partition 2: CIDATA (autoinstall config, offline .debs, Ansible playbook, optional WinPE images) - **Partition 2:** CIDATA (autoinstall config, offline .debs, pip wheels, Ansible playbook, webapp, boot tools)
### Step 3: Boot the Target Machine ### Step 4: Boot the Target Machine
1. Insert the USB into the target machine 1. Insert the USB into the target machine
2. Press F12 (or vendor boot key) and select the USB 2. Press F12 (or vendor boot key) and select the USB
3. Ubuntu auto-installs — no interaction needed 3. Ubuntu auto-installs — no interaction needed
4. After reboot, the first-boot script installs all .deb packages and runs the Ansible playbook 4. After reboot, the first-boot script installs all .deb packages and runs the Ansible playbook
5. PXE services (dnsmasq, Apache, Samba) are configured automatically 5. PXE services (dnsmasq, Apache, Samba) are configured automatically
6. Flask webapp starts on port 9009
### Step 4: Connect to Isolated Network ### Step 5: Connect to Isolated Network
Move the server's wired NIC to the isolated switch for PXE clients. Move the server's wired NIC to the isolated switch for PXE clients.
### Step 5: Import WinPE Content (if not bundled in Step 2) ### Step 6: Import WinPE Content (if not bundled in Step 3)
Insert the Media Creator LITE USB and copy content to the Samba share: **Option A:** Use the web interface at `http://10.9.100.1:9009` to import from USB.
**Option B:** Manual copy:
```bash ```bash
sudo mkdir -p /mnt/usb2 sudo mkdir -p /mnt/usb2
sudo mount /dev/sdb2 /mnt/usb2 sudo mount /dev/sdb2 /mnt/usb2
sudo cp -r /mnt/usb2/. /srv/samba/winpeapps/standard sudo cp -r /mnt/usb2/. /srv/samba/winpeapps/gea-standard
sudo umount /mnt/usb2 sudo umount /mnt/usb2
``` ```
## Web Management Interface
Access at `http://10.9.100.1:9009` from any machine on the isolated network.
| Page | URL Path | Purpose |
|-------------------|-------------|-----------------------------------------------|
| Dashboard | / | Service status, disk usage, DHCP clients |
| Image Import | /import | Import WinPE images from USB drives |
| Unattend Editor | /unattend | Edit Windows unattend.xml per image type |
| startnet.cmd | /startnet | Edit startnet.cmd inside boot.wim (wimtools) |
| Clonezilla Backups| /backups | Upload/download/manage disk backup images |
| Blancco Reports | /reports | View/download drive erasure reports |
| Audit Log | /audit | Activity history for all write operations |
## Verification ## Verification
1. Connect a test workstation to the isolated switch 1. Connect a test workstation to the isolated switch
2. Set Network Boot (PXE) as first boot in BIOS/UEFI 2. Set Network Boot (PXE) as first boot in BIOS/UEFI
3. Boot — the client should pull an IP from 10.9.100.x 3. Boot — the client should pull an IP from 10.9.100.x
4. iPXE loads, fetches the boot script from port 4433 4. iPXE loads, fetches the boot script from port 4433
5. WinPE boots via wimboot + boot.wim over HTTP 5. Select an option from the boot menu:
6. WinPE maps Samba shares and begins image deployment - **Windows PE**: Boots via wimboot + boot.wim, maps Samba shares, begins deployment
- **Clonezilla**: Boots Clonezilla Live for disk imaging
- **Blancco**: Boots Drive Eraser, auto-saves reports to server
- **Memtest86+**: Runs memory diagnostics
## Testing with KVM
```bash
# Download Ubuntu ISO
wget -O ~/Downloads/ubuntu-24.04.3-live-server-amd64.iso \
https://releases.ubuntu.com/noble/ubuntu-24.04.3-live-server-amd64.iso
# Launch test VM
sudo ./test-vm.sh ~/Downloads/ubuntu-24.04.3-live-server-amd64.iso
# Watch progress (Ctrl+] to detach)
sudo virsh console pxe-test
# After install: ssh pxe@10.9.100.1 / http://10.9.100.1:9009
# Clean up
sudo ./test-vm.sh --destroy
```
## Project Structure ## Project Structure
``` ```
pxe-server/ pxe-server/
├── autoinstall/ ├── autoinstall/
│ ├── user-data # Cloud-init autoinstall (Ubuntu config, first-boot script) │ ├── user-data # Cloud-init autoinstall + first-boot script
│ └── meta-data # Cloud-init metadata (empty, required) │ └── meta-data # Cloud-init metadata (required, empty)
├── playbook/ ├── playbook/
│ ├── pxe_server_setup.yml # Ansible: dnsmasq, Apache, Samba, iPXE, firewall, netplan │ ├── pxe_server_setup.yml # Ansible: dnsmasq, Apache, Samba, iPXE, UFW, webapp
│ └── inventory.ini # Ansible inventory │ └── inventory.ini # Ansible inventory
├── webapp/
│ ├── app.py # Flask application
│ ├── requirements.txt # Python deps (flask, lxml)
│ ├── static/ # CSS, JS, fonts, logo (all bundled offline)
│ └── templates/ # Jinja2 HTML templates
├── unattend/ ├── unattend/
│ └── FlatUnattendW10.xml # Windows unattend.xml sample │ └── FlatUnattendW10.xml # Windows unattend.xml template
├── boot-tools/ # Extracted boot files (gitignored, built by prepare-boot-tools.sh)
│ ├── blancco/ # Blancco Drive Eraser
│ ├── clonezilla/ # Clonezilla Live
│ └── memtest/ # Memtest86+
├── offline-packages/ # .deb files (gitignored, built by download-packages.sh) ├── offline-packages/ # .deb files (gitignored, built by download-packages.sh)
├── build-usb.sh # Builds the installer USB ├── pip-wheels/ # Python wheels (gitignored, built by download-packages.sh)
├── download-packages.sh # Downloads offline .debs via Docker ├── download-packages.sh # Downloads all offline packages
── setup-guide-original.txt # Original manual setup doc (reference) ── build-usb.sh # Builds the 2-partition installer USB
├── prepare-boot-tools.sh # Extracts/patches boot tools from ISOs
├── test-vm.sh # KVM test environment
├── README.md # Project overview
└── setup-guide-original.txt # Original manual setup notes (reference)
``` ```
## Image Types ## Image Types
| Image Type | Domain | Description | | Image Type | Domain | Description |
|---------------|-----------------|---------------------| |----------------------|-----------------|-------------------------|
| gea-standard | geaerospace.com | Standard desktop | | gea-standard | geaerospace.com | Standard desktop |
| gea-engineer | geaerospace.com | Engineering desktop | | gea-engineer | geaerospace.com | Engineering desktop |
| gea-shopfloor | geaerospace.com | Shop floor kiosk | | gea-shopfloor | geaerospace.com | Shop floor kiosk |
| ge-standard | ge.com | Standard desktop | | ge-standard | ge.com | Standard desktop |
| ge-engineer | ge.com | Engineering desktop | | ge-engineer | ge.com | Engineering desktop |
| ge-shopfloor | ge.com | Shop floor kiosk | | ge-shopfloor-lockdown| ge.com | Shop floor (locked) |
| ge-shopfloor-mce | ge.com | Shop floor (MCE) |
## Network Configuration ## Network Configuration
@@ -130,3 +199,14 @@ pxe-server/
- DHCP range: `10.9.100.10` - `10.9.100.100` - DHCP range: `10.9.100.10` - `10.9.100.100`
- Lease time: 12 hours - Lease time: 12 hours
- DNS: `8.8.8.8` (passed to clients, not used by server) - DNS: `8.8.8.8` (passed to clients, not used by server)
- Firewall: UFW deny-by-default, allow 67/udp 69/udp 80/tcp 445/tcp 4433/tcp 9009/tcp
## Samba Shares
| Share | Path | Purpose |
|-----------------|----------------------------|-------------------------------|
| winpeapps | /srv/samba/winpeapps | WinPE deployment images |
| clonezilla | /srv/samba/clonezilla | Clonezilla disk backup images |
| blancco-reports | /srv/samba/blancco-reports | Blancco erasure reports (auto)|
All shares use guest access for the isolated network.

View File

@@ -39,6 +39,11 @@ autoinstall:
username: pxe username: pxe
password: "$6$rounds=656000$TpsuBw0N85085mpx$KtKsCwFlowg4NY41gUqx5ljef8cJ8uPFfgg43MyCPWByfXkhM5XushcdtkNps6lKeQFQZtli/QU.s52AUc7XC." password: "$6$rounds=656000$TpsuBw0N85085mpx$KtKsCwFlowg4NY41gUqx5ljef8cJ8uPFfgg43MyCPWByfXkhM5XushcdtkNps6lKeQFQZtli/QU.s52AUc7XC."
# Enable SSH
ssh:
install-server: true
allow-pw: true
# Installer-stage late commands # Installer-stage late commands
late-commands: late-commands:
# Install deb packages from CIDATA USB # Install deb packages from CIDATA USB

View File

@@ -66,11 +66,13 @@
{{ (ansible_interfaces {{ (ansible_interfaces
| select('match','^e(th|n)') | select('match','^e(th|n)')
| reject('equalto','lo') | reject('equalto','lo')
| reject('equalto', ansible_default_ipv4.interface) | reject('equalto', ansible_default_ipv4.interface | default(''))
| list | list
) )
| first | first
| default(ansible_default_ipv4.interface) }} | default(ansible_default_ipv4.interface | default(
ansible_interfaces | select('match','^e(th|n)') | first | default('eth0')
)) }}
- name: "Debug: final pxe_iface choice" - name: "Debug: final pxe_iface choice"
debug: debug:

View File

@@ -34,7 +34,8 @@ VM_DISK_SIZE=40 # GB
if [ "${1:-}" = "--destroy" ]; then if [ "${1:-}" = "--destroy" ]; then
echo "Destroying test environment..." echo "Destroying test environment..."
virsh destroy "$VM_NAME" 2>/dev/null || true virsh destroy "$VM_NAME" 2>/dev/null || true
virsh undefine "$VM_NAME" --remove-all-storage 2>/dev/null || true virsh undefine "$VM_NAME" 2>/dev/null || true
rm -f "$VM_DISK"
virsh net-destroy "$NET_NAME" 2>/dev/null || true virsh net-destroy "$NET_NAME" 2>/dev/null || true
virsh net-undefine "$NET_NAME" 2>/dev/null || true virsh net-undefine "$NET_NAME" 2>/dev/null || true
rm -f "$CIDATA_ISO" rm -f "$CIDATA_ISO"
@@ -137,21 +138,34 @@ if [ -f "$VM_DISK" ]; then
fi fi
qemu-img create -f qcow2 "$VM_DISK" "${VM_DISK_SIZE}G" qemu-img create -f qcow2 "$VM_DISK" "${VM_DISK_SIZE}G"
# --- Step 4: Launch VM --- # --- Step 4: Extract kernel/initrd from ISO ---
echo "" echo ""
echo "[4/4] Launching VM ($VM_NAME)..." echo "[4/5] Extracting kernel and initrd from ISO..."
ISO_MNT=$(mktemp -d)
mount -o loop,ro "$UBUNTU_ISO" "$ISO_MNT"
KERNEL="/tmp/${VM_NAME}-vmlinuz"
INITRD="/tmp/${VM_NAME}-initrd"
cp "$ISO_MNT/casper/vmlinuz" "$KERNEL"
cp "$ISO_MNT/casper/initrd" "$INITRD"
umount "$ISO_MNT"
rmdir "$ISO_MNT"
echo " Extracted vmlinuz and initrd from casper/"
# --- Step 5: Launch VM ---
echo ""
echo "[5/5] Launching VM ($VM_NAME)..."
virt-install \ virt-install \
--name "$VM_NAME" \ --name "$VM_NAME" \
--memory "$VM_RAM" \ --memory "$VM_RAM" \
--vcpus "$VM_CPUS" \ --vcpus "$VM_CPUS" \
--disk path="$VM_DISK",format=qcow2 \ --disk path="$VM_DISK",format=qcow2 \
--cdrom "$UBUNTU_ISO" \ --disk path="$UBUNTU_ISO",device=cdrom,readonly=on \
--disk path="$CIDATA_ISO",device=cdrom \ --disk path="$CIDATA_ISO",device=cdrom \
--network network="$NET_NAME" \ --network network="$NET_NAME" \
--os-variant ubuntu24.04 \ --os-variant ubuntu24.04 \
--graphics none \ --graphics none \
--console pty,target_type=serial \ --console pty,target_type=serial \
--extra-args "console=ttyS0,115200n8 autoinstall" \ --install kernel="$KERNEL",initrd="$INITRD",kernel_args="console=ttyS0,115200n8 autoinstall" \
--noautoconsole --noautoconsole
echo "" echo ""