From 576955785dcefba0af89c5aeac3bd2263bde2fb6 Mon Sep 17 00:00:00 2001 From: cproudlock Date: Thu, 15 Oct 2026 21:43:33 -0400 Subject: [PATCH] Faster shopfloor-setup sync, prompt spacing, XP-era test client - Deploy shopfloor-setup with rsync instead of the copy module: the ~4 GB tree took about 4 minutes on every re-run because copy checksums each file. rsync transfers only changes; changed only when it did. - iPXE drops a trailing space, so 'Machine number:' ran into the typed digits. The space now comes from a hex 20 setting. - tests/vm: pxetest-xp is a permanent client (SeaBIOS, qemu32 32-bit CPU, rtl8139, 1 GiB, IDE disk) for the i686 Clonezilla path; start.sh xp. Only one client may run at a time across all three. --- playbook/ipxe/GetPxeScript.aspx | 5 +++- playbook/pxe_server_setup.yml | 20 +++++++++----- tests/vm/README.md | 12 +++++---- tests/vm/common.sh | 8 ++++++ tests/vm/create.sh | 47 ++++++++++++++++++++++++++++++++- tests/vm/destroy.sh | 4 +-- tests/vm/start.sh | 20 +++++++------- tests/vm/status.sh | 4 +-- tests/vm/stop.sh | 7 ++--- 9 files changed, 98 insertions(+), 29 deletions(-) diff --git a/playbook/ipxe/GetPxeScript.aspx b/playbook/ipxe/GetPxeScript.aspx index 2c38988..aab88e7 100644 --- a/playbook/ipxe/GetPxeScript.aspx +++ b/playbook/ipxe/GetPxeScript.aspx @@ -69,7 +69,10 @@ goto cz_machine # iPXE cannot check characters: pxe-cz.sh accepts letters, digits and dash # and asks again there. Empty: ask again. Esc / Ctrl-C: back to the menu. echo -echo -n Machine number: && read czmachine || goto menu +# iPXE drops a trailing space from a command line, so the space after the +# colon comes from a setting (0x20) rather than the literal text. +set sp:hex 20 +echo -n Machine number:${sp:string} && read czmachine || goto menu isset ${czmachine} || goto cz_machine # Same CPU check as the manual entry below (cpuid --ext 29 = 64-bit). cpuid --ext 29 && goto cz_auto64 || goto cz_auto32 diff --git a/playbook/pxe_server_setup.yml b/playbook/pxe_server_setup.yml index 5d9bab5..736a4ad 100644 --- a/playbook/pxe_server_setup.yml +++ b/playbook/pxe_server_setup.yml @@ -986,12 +986,20 @@ ignore_errors: yes - name: "Deploy shopfloor setup scripts to enrollment share" - copy: - src: "{{ usb_mount }}/shopfloor-setup/" - dest: /srv/samba/enrollment/shopfloor-setup/ - mode: '0755' - directory_mode: '0755' - ignore_errors: yes + # rsync, not copy: the tree is ~4 GB and the copy module checksums every + # file one by one, so a re-run spent minutes here. rsync compares size + # and mtime and transfers only what changed. Merge, no --delete: files + # a site added on the share stay. Skipped if the USB has no tree. + shell: | + set -euo pipefail + SRC="{{ usb_mount }}/shopfloor-setup/" + [ -d "$SRC" ] || { echo "SKIP: no shopfloor-setup on the USB"; exit 0; } + mkdir -p /srv/samba/enrollment/shopfloor-setup + rsync -rlt -i --chmod=D0755,F0755 "$SRC" /srv/samba/enrollment/shopfloor-setup/ + args: + executable: /bin/bash + register: shopfloor_rsync + changed_when: shopfloor_rsync.stdout | trim | length > 0 and 'SKIP:' not in shopfloor_rsync.stdout - name: "Deploy preinstall.json to pre-install/" copy: diff --git a/tests/vm/README.md b/tests/vm/README.md index 2d2c600..5f3809e 100644 --- a/tests/vm/README.md +++ b/tests/vm/README.md @@ -12,6 +12,7 @@ a laptop, without root and without touching a real network. | `pxetest-server` | 2 vCPU, 4 GiB, q35 + OVMF Secure Boot (MS keys), 60G thin qcow2 (`vda`, boot 1), the USB image as a USB disk (`sda`, boot 2). | | `pxetest-client` | 2 vCPU, 2 GiB, OVMF Secure Boot (MS keys), 1G blank disk, network boot first (UEFI IPv4 PXE, virtio-net, OVMF driver, no option ROM). Proves shim -> iPXE -> menu -> WinPE under Secure Boot; WinPE has no virtio driver, so it stops at "Waiting for network". | | `pxetest-client-nosb` | 2 vCPU, 2 GiB, OVMF without Secure Boot, e1000e NIC, 64G thin SATA disk. WinPE has in-box drivers for both, so startnet gets a network and maps the shares. | +| `pxetest-xp` | XP/Win7-era PC: 1 vCPU, 1 GiB, i440FX + SeaBIOS (legacy PXE, gets `ipxe.pxe`), `qemu32` CPU (no long mode, so the menu picks the i686 Clonezilla; `fallback='forbid'` so it never silently becomes 64-bit), rtl8139 NIC, 8G thin IDE disk. | NIC MACs (fixed): @@ -21,6 +22,7 @@ NIC MACs (fixed): | server | imaging | `52:54:00:9e:10:02` | `pxetest-imaging` | `pxe_nic_mac` | | client | - | `52:54:00:9e:10:03` | `pxetest-imaging` | - | | client-nosb | - | `52:54:00:9e:10:04` | `pxetest-imaging` | - | +| xp | - | `52:54:00:9e:10:05` | `pxetest-imaging` | - | `build-image.sh` writes `$PXETEST_CACHE/site-vars.yml` with these two MACs (`pxe_nic_mac`, `corp_nic_mac`) and passes it to `build-usb.sh @@ -48,12 +50,13 @@ IPv4 address or a non-tap bridge port. scripts/download-packages.sh --iso "$PXETEST_CACHE/$UBUNTU_ISO_NAME" "$PXETEST_PAYLOAD_ROOT/offline-packages" scripts/check-updates.py --debs "$PXETEST_PAYLOAD_ROOT/offline-packages" tests/vm/build-image.sh # build-usb.sh --image, upload as volume pxetest-usb.img - tests/vm/create.sh # network, volumes, both VMs (prints the MAC pins) + tests/vm/create.sh # network, volumes, all VMs (prints the MAC pins) tests/vm/start.sh server # autoinstall, reboot, first-boot playbook tests/vm/status.sh --shot /tmp/pxetest-shots # states, leases, screenshots tests/vm/start.sh client # once the server playbook is done (Secure Boot client) tests/vm/stop.sh client tests/vm/start.sh client2 # Secure Boot off, e1000e: WinPE maps the shares + tests/vm/start.sh xp # legacy BIOS, 32-bit: i686 Clonezilla tests/vm/stop.sh # ACPI shutdown all, keep them defined tests/vm/destroy.sh # remove every pxetest-* object @@ -120,10 +123,9 @@ the clonezilla user; `--with-boot-tools` is needed for Clonezilla itself screen, the share mounts, one disk so no disk question, image 6501-YYYY-MM-DD-HHMM (server time, Eastern) is saved, "Checking the saved image", SUCCESS banner with serial and model, power off after 60 s. -2. Same as 6501 from a 32-bit BIOS client: a legacy-BIOS VM (SeaBIOS) with - `-cpu` lacking long mode (e.g. `pentium3` - or qemu32) so `cpuid --ext 29` fails and :cz_auto32 boots - clonezilla-i686. Expect pxe-meta.txt bits=32, firmware=bios, +2. Same as 6501 from the 32-bit BIOS client (`start.sh xp`, pxetest-xp): + `cpuid --ext 29` fails on its qemu32 CPU and :cz_auto32 boots + clonezilla-i686. Give its IDE disk content first, as for step 1. Expect pxe-meta.txt bits=32, firmware=bios, clonezilla=clonezilla-live-3.2.0-5-i686. Also type `65 01` once: Clonezilla says the number is not valid and asks again. 3. Webapp (Tools > Clonezilla backups): both images under 6501, newest diff --git a/tests/vm/common.sh b/tests/vm/common.sh index 143eb45..59ba8f8 100755 --- a/tests/vm/common.sh +++ b/tests/vm/common.sh @@ -52,6 +52,14 @@ CLIENT_MAC="52:54:00:9e:10:03" # on pxetest-imaging only CLIENT2_VM="pxetest-client-nosb" CLIENT2_DISK_VOL="pxetest-client-nosb.qcow2" CLIENT2_MAC="52:54:00:9e:10:04" # on pxetest-imaging only +# Third client: a 15-20-year-old XP/Win7-era PC. Legacy BIOS (SeaBIOS), +# 32-bit only CPU (qemu32: no long mode, so the menu picks the i686 +# Clonezilla), rtl8139 NIC, IDE disk, 1 GiB. +CLIENT3_VM="pxetest-xp" +CLIENT3_DISK_VOL="pxetest-xp.qcow2" +CLIENT3_MAC="52:54:00:9e:10:05" # on pxetest-imaging only +CLIENT3_MEM_MIB="${CLIENT3_MEM_MIB:-1024}" +ALL_CLIENTS=("pxetest-client" "pxetest-client-nosb" "pxetest-xp") log() { printf '[pxetest] %s\n' "$*"; } die() { printf '[pxetest] ERROR: %s\n' "$*" >&2; exit 1; } diff --git a/tests/vm/create.sh b/tests/vm/create.sh index 357aaa7..7208aa0 100755 --- a/tests/vm/create.sh +++ b/tests/vm/create.sh @@ -12,6 +12,10 @@ # pxetest-client 2 vCPU, 2 GiB, OVMF Secure Boot with MS keys, 1G blank # disk, one NIC 52:54:00:9e:10:03 on pxetest-imaging, # network boot first (UEFI IPv4 PXE). +# pxetest-client-nosb OVMF without Secure Boot, e1000e, 64G SATA disk. +# pxetest-xp XP/Win7-era PC: SeaBIOS (legacy PXE), qemu32 CPU +# (32-bit only), 1 GiB, rtl8139 52:54:00:9e:10:05, +# 8G IDE disk. # # Usage: tests/vm/create.sh source "$(dirname "$0")/common.sh" @@ -51,6 +55,10 @@ if ! vol_exists "$CLIENT_DISK_VOL"; then virsh vol-create-as "$POOL" "$CLIENT_DISK_VOL" 1G --format qcow2 >/dev/null log "created $CLIENT_DISK_VOL (1G blank)" fi +if ! vol_exists "$CLIENT3_DISK_VOL"; then + virsh vol-create-as "$POOL" "$CLIENT3_DISK_VOL" 8G --format qcow2 >/dev/null + log "created $CLIENT3_DISK_VOL (8G thin, blank)" +fi vol_exists "$USB_VOL" || log "WARNING: no $USB_VOL volume yet; run build-image.sh before start.sh" # Disks are type='file' with the pool's path: with type='volume' sources @@ -202,8 +210,45 @@ XML log "defined $CLIENT2_VM" fi +# --- third client: XP/Win7-era PC, legacy BIOS, 32-bit CPU --- +# fallback='forbid': fail rather than quietly give the guest a 64-bit CPU, +# which would send it down the amd64 Clonezilla path. +if ! dom_exists "$CLIENT3_VM"; then + cat >"$TMP/client3.xml" < + $CLIENT3_VM + $CLIENT3_MEM_MIB + 1 + + hvm + + + qemu32 + + + + + + + + + + + + + + + + + + +XML + virsh define "$TMP/client3.xml" >/dev/null + log "defined $CLIENT3_VM" +fi + # Never attach a pxetest VM anywhere but default NAT / pxetest-imaging. -for vm in "$SERVER_VM" "$CLIENT_VM" "$CLIENT2_VM"; do +for vm in "$SERVER_VM" "${ALL_CLIENTS[@]}"; do while read -r _ type src _ _; do case "$type:$src" in network:"$GE_NET") [ "$vm" = "$SERVER_VM" ] || die "$vm is on $GE_NET" ;; diff --git a/tests/vm/destroy.sh b/tests/vm/destroy.sh index f4cffd3..0692bcd 100755 --- a/tests/vm/destroy.sh +++ b/tests/vm/destroy.sh @@ -5,13 +5,13 @@ # Usage: tests/vm/destroy.sh source "$(dirname "$0")/common.sh" -for vm in "$CLIENT_VM" "$CLIENT2_VM" "$SERVER_VM"; do +for vm in "${ALL_CLIENTS[@]}" "$SERVER_VM"; do dom_exists "$vm" || continue [ "$(dom_state "$vm")" = "shut off" ] || virsh destroy "$vm" >/dev/null virsh undefine "$vm" --nvram >/dev/null log "undefined $vm" done -for v in "$SERVER_DISK_VOL" "$USB_VOL" "$CLIENT_DISK_VOL" "$CLIENT2_DISK_VOL"; do +for v in "$SERVER_DISK_VOL" "$USB_VOL" "$CLIENT_DISK_VOL" "$CLIENT2_DISK_VOL" "$CLIENT3_DISK_VOL"; do vol_exists "$v" || continue virsh vol-delete --pool "$POOL" "$v" >/dev/null log "deleted volume $v" diff --git a/tests/vm/start.sh b/tests/vm/start.sh index c5bb65b..09dfebb 100755 --- a/tests/vm/start.sh +++ b/tests/vm/start.sh @@ -1,8 +1,9 @@ #!/bin/bash # Start pxetest VMs. Idempotent (a running VM is left alone). -# Usage: tests/vm/start.sh [server|client|client2|all] (default: server) +# Usage: tests/vm/start.sh [server|client|client2|xp|all] (default: server) # client = Secure Boot client (virtio NIC: reaches the iPXE menu and WinPE) # client2 = Secure Boot OFF client, e1000e NIC (WinPE gets a network) +# xp = XP/Win7-era PC: legacy BIOS, 32-bit CPU (i686 Clonezilla) # Start a client only once the server's playbook has finished, and only one # client at a time: server + one client = 6 GiB. source "$(dirname "$0")/common.sh" @@ -12,8 +13,9 @@ case "$which" in server) vms=("$SERVER_VM") ;; client) vms=("$CLIENT_VM") ;; client2|nosb) vms=("$CLIENT2_VM") ;; + xp|client3) vms=("$CLIENT3_VM") ;; all) vms=("$SERVER_VM" "$CLIENT_VM") ;; - *) die "usage: $0 [server|client|client2|all]" ;; + *) die "usage: $0 [server|client|client2|xp|all]" ;; esac net_exists "$IMAGING_NET" || die "run create.sh first" @@ -23,13 +25,13 @@ assert_imaging_isolated for vm in "${vms[@]}"; do dom_exists "$vm" || die "$vm not defined; run create.sh" # RAM: one client at a time. - case "$vm" in - "$CLIENT_VM") other="$CLIENT2_VM" ;; - "$CLIENT2_VM") other="$CLIENT_VM" ;; - *) other="" ;; - esac - if [ -n "$other" ] && [ "$(dom_state "$other")" = "running" ]; then - die "$other is running; stop it first (tests/vm/stop.sh) to stay within RAM" + if [ "$vm" != "$SERVER_VM" ]; then + for other in "${ALL_CLIENTS[@]}"; do + [ "$other" != "$vm" ] || continue + if [ "$(dom_state "$other")" = "running" ]; then + die "$other is running; stop it first (tests/vm/stop.sh) to stay within RAM" + fi + done fi if [ "$vm" = "$SERVER_VM" ]; then vol_exists "$USB_VOL" || die "no $USB_VOL; run build-image.sh" diff --git a/tests/vm/status.sh b/tests/vm/status.sh index 3480225..bbb63bc 100755 --- a/tests/vm/status.sh +++ b/tests/vm/status.sh @@ -12,14 +12,14 @@ echo "network $IMAGING_NET: $n" if net_active "$IMAGING_NET"; then (assert_imaging_isolated && echo " isolation: OK (no forward, no ip, no host IPv4, only vnet ports)") fi -for v in "$SERVER_DISK_VOL" "$USB_VOL" "$CLIENT_DISK_VOL" "$CLIENT2_DISK_VOL"; do +for v in "$SERVER_DISK_VOL" "$USB_VOL" "$CLIENT_DISK_VOL" "$CLIENT2_DISK_VOL" "$CLIENT3_DISK_VOL"; do if vol_exists "$v"; then echo "volume $v: $(virsh vol-info --pool "$POOL" "$v" | awk '/Capacity|Allocation/{printf "%s %s %s ", $1, $2, $3}')" else echo "volume $v: absent" fi done -for vm in "$SERVER_VM" "$CLIENT_VM" "$CLIENT2_VM"; do +for vm in "$SERVER_VM" "${ALL_CLIENTS[@]}"; do if dom_exists "$vm"; then echo "domain $vm: $(dom_state "$vm")" virsh domiflist "$vm" | tail -n +3 | grep -v '^$' | sed 's/^/ /' diff --git a/tests/vm/stop.sh b/tests/vm/stop.sh index 0ef0f8b..f1cac71 100755 --- a/tests/vm/stop.sh +++ b/tests/vm/stop.sh @@ -1,7 +1,7 @@ #!/bin/bash # Shut pxetest VMs down (ACPI, then force after a timeout). Keeps them # defined. Idempotent. -# Usage: tests/vm/stop.sh [server|client|client2|all] (default: all) +# Usage: tests/vm/stop.sh [server|client|client2|xp|all] (default: all) source "$(dirname "$0")/common.sh" which="${1:-all}" @@ -9,8 +9,9 @@ case "$which" in server) vms=("$SERVER_VM") ;; client) vms=("$CLIENT_VM") ;; client2|nosb) vms=("$CLIENT2_VM") ;; - all) vms=("$CLIENT_VM" "$CLIENT2_VM" "$SERVER_VM") ;; - *) die "usage: $0 [server|client|client2|all]" ;; + xp|client3) vms=("$CLIENT3_VM") ;; + all) vms=("${ALL_CLIENTS[@]}" "$SERVER_VM") ;; + *) die "usage: $0 [server|client|client2|xp|all]" ;; esac for vm in "${vms[@]}"; do dom_exists "$vm" || continue