- 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.
263 lines
8.9 KiB
Bash
Executable File
263 lines
8.9 KiB
Bash
Executable File
#!/bin/bash
|
|
# Define the pxetest network, volumes and VMs. Idempotent: existing
|
|
# pxetest-* objects are kept (run destroy.sh first to rebuild them).
|
|
#
|
|
# pxetest-imaging isolated libvirt network: no <forward>, no <ip>, so the
|
|
# host has no address on it and libvirt runs no DHCP.
|
|
# Bridge virbr-pxetest holds only the VMs' tap devices.
|
|
# pxetest-server 2 vCPU, 4 GiB, OVMF (Secure Boot, MS keys), 60G thin
|
|
# qcow2 + the USB image as a USB disk.
|
|
# NIC1 52:54:00:9e:10:01 on 'default' NAT ("GE/corp")
|
|
# NIC2 52:54:00:9e:10:02 on pxetest-imaging ("imaging")
|
|
# 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"
|
|
|
|
TMP=$(mktemp -d)
|
|
trap 'rm -rf "$TMP"' EXIT
|
|
|
|
# --- isolated imaging network ---
|
|
if ! net_exists "$IMAGING_NET"; then
|
|
cat >"$TMP/net.xml" <<XML
|
|
<network>
|
|
<name>$IMAGING_NET</name>
|
|
<bridge name='$IMAGING_BRIDGE' stp='on' delay='0'/>
|
|
</network>
|
|
XML
|
|
virsh net-define "$TMP/net.xml" >/dev/null
|
|
log "defined network $IMAGING_NET (isolated, no forward, no ip)"
|
|
fi
|
|
virsh net-autostart --disable "$IMAGING_NET" >/dev/null 2>&1 || true
|
|
if ! net_active "$IMAGING_NET"; then
|
|
virsh net-start "$IMAGING_NET" >/dev/null
|
|
fi
|
|
assert_imaging_isolated
|
|
net_exists "$GE_NET" || die "libvirt network '$GE_NET' (NAT) does not exist"
|
|
net_active "$GE_NET" || virsh net-start "$GE_NET" >/dev/null
|
|
|
|
# --- volumes ---
|
|
if ! vol_exists "$SERVER_DISK_VOL"; then
|
|
virsh vol-create-as "$POOL" "$SERVER_DISK_VOL" "$SERVER_DISK_SIZE" --format qcow2 >/dev/null
|
|
log "created $SERVER_DISK_VOL ($SERVER_DISK_SIZE thin qcow2)"
|
|
fi
|
|
if ! vol_exists "$CLIENT2_DISK_VOL"; then
|
|
virsh vol-create-as "$POOL" "$CLIENT2_DISK_VOL" 64G --format qcow2 >/dev/null
|
|
log "created $CLIENT2_DISK_VOL (64G thin, blank)"
|
|
fi
|
|
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
|
|
# qemu got "Permission denied" opening them (AppArmor profile).
|
|
POOL_DIR=$(virsh pool-dumpxml "$POOL" | sed -n 's|.*<path>\(.*\)</path>.*|\1|p' | head -1)
|
|
[ -n "$POOL_DIR" ] || die "cannot read the path of pool $POOL"
|
|
|
|
# Secure Boot with Microsoft keys enrolled: libvirt picks the edk2
|
|
# descriptor (40-edk2-x86_64-secure-enrolled.json) and copies its
|
|
# OVMF_VARS_4M.ms.fd template into a per-VM nvram.
|
|
efi_os() {
|
|
cat <<XML
|
|
<os firmware='efi'>
|
|
<type arch='x86_64' machine='q35'>hvm</type>
|
|
<firmware>
|
|
<feature enabled='yes' name='secure-boot'/>
|
|
<feature enabled='yes' name='enrolled-keys'/>
|
|
</firmware>
|
|
</os>
|
|
<features><acpi/><apic/><smm state='on'/></features>
|
|
XML
|
|
}
|
|
|
|
# --- server VM ---
|
|
# Boot order: the installed disk first, the USB second. A blank disk falls
|
|
# through to the USB; after the install the disk boots, so autoinstall does
|
|
# not loop. CIDATA stays attached for the first-boot playbook.
|
|
if ! dom_exists "$SERVER_VM"; then
|
|
cat >"$TMP/server.xml" <<XML
|
|
<domain type='kvm'>
|
|
<name>$SERVER_VM</name>
|
|
<memory unit='MiB'>$SERVER_MEM_MIB</memory>
|
|
<vcpu>2</vcpu>
|
|
$(efi_os)
|
|
<cpu mode='host-passthrough'/>
|
|
<clock offset='utc'/>
|
|
<on_reboot>restart</on_reboot>
|
|
<devices>
|
|
<disk type='file' device='disk'>
|
|
<driver name='qemu' type='qcow2' discard='unmap'/>
|
|
<source file='$POOL_DIR/$SERVER_DISK_VOL'/>
|
|
<target dev='vda' bus='virtio'/>
|
|
<boot order='1'/>
|
|
</disk>
|
|
<disk type='file' device='disk'>
|
|
<driver name='qemu' type='raw'/>
|
|
<source file='$POOL_DIR/$USB_VOL'/>
|
|
<target dev='sda' bus='usb' removable='on'/>
|
|
<boot order='2'/>
|
|
</disk>
|
|
<controller type='usb' model='qemu-xhci'/>
|
|
<interface type='network'>
|
|
<source network='$GE_NET'/>
|
|
<mac address='$SERVER_GE_MAC'/>
|
|
<model type='virtio'/>
|
|
</interface>
|
|
<interface type='network'>
|
|
<source network='$IMAGING_NET'/>
|
|
<mac address='$SERVER_IMAGING_MAC'/>
|
|
<model type='virtio'/>
|
|
</interface>
|
|
<serial type='pty'/>
|
|
<console type='pty'/>
|
|
<graphics type='vnc' listen='127.0.0.1' autoport='yes'/>
|
|
<video><model type='virtio'/></video>
|
|
<memballoon model='virtio'/>
|
|
<rng model='virtio'><backend model='random'>/dev/urandom</backend></rng>
|
|
</devices>
|
|
</domain>
|
|
XML
|
|
virsh define "$TMP/server.xml" >/dev/null
|
|
log "defined $SERVER_VM"
|
|
fi
|
|
|
|
# --- client VM ---
|
|
if ! dom_exists "$CLIENT_VM"; then
|
|
cat >"$TMP/client.xml" <<XML
|
|
<domain type='kvm'>
|
|
<name>$CLIENT_VM</name>
|
|
<memory unit='MiB'>$CLIENT_MEM_MIB</memory>
|
|
<vcpu>2</vcpu>
|
|
$(efi_os)
|
|
<cpu mode='host-passthrough'/>
|
|
<clock offset='utc'/>
|
|
<devices>
|
|
<interface type='network'>
|
|
<source network='$IMAGING_NET'/>
|
|
<mac address='$CLIENT_MAC'/>
|
|
<model type='virtio'/>
|
|
<rom enabled='no'/>
|
|
<boot order='1'/>
|
|
</interface>
|
|
<disk type='file' device='disk'>
|
|
<driver name='qemu' type='qcow2'/>
|
|
<source file='$POOL_DIR/$CLIENT_DISK_VOL'/>
|
|
<target dev='vda' bus='virtio'/>
|
|
<boot order='2'/>
|
|
</disk>
|
|
<serial type='pty'/>
|
|
<console type='pty'/>
|
|
<graphics type='vnc' listen='127.0.0.1' autoport='yes'/>
|
|
<video><model type='virtio'/></video>
|
|
<memballoon model='virtio'/>
|
|
</devices>
|
|
</domain>
|
|
XML
|
|
virsh define "$TMP/client.xml" >/dev/null
|
|
log "defined $CLIENT_VM"
|
|
fi
|
|
|
|
# --- second client: Secure Boot off, e1000e + SATA (WinPE in-box drivers) ---
|
|
if ! dom_exists "$CLIENT2_VM"; then
|
|
cat >"$TMP/client2.xml" <<XML
|
|
<domain type='kvm'>
|
|
<name>$CLIENT2_VM</name>
|
|
<memory unit='MiB'>$CLIENT_MEM_MIB</memory>
|
|
<vcpu>2</vcpu>
|
|
<os firmware='efi'>
|
|
<type arch='x86_64' machine='q35'>hvm</type>
|
|
<firmware>
|
|
<feature enabled='no' name='secure-boot'/>
|
|
</firmware>
|
|
</os>
|
|
<features><acpi/><apic/></features>
|
|
<cpu mode='host-passthrough'/>
|
|
<clock offset='localtime'/>
|
|
<devices>
|
|
<interface type='network'>
|
|
<source network='$IMAGING_NET'/>
|
|
<mac address='$CLIENT2_MAC'/>
|
|
<model type='e1000e'/>
|
|
<boot order='1'/>
|
|
</interface>
|
|
<disk type='file' device='disk'>
|
|
<driver name='qemu' type='qcow2'/>
|
|
<source file='$POOL_DIR/$CLIENT2_DISK_VOL'/>
|
|
<target dev='sda' bus='sata'/>
|
|
<boot order='2'/>
|
|
</disk>
|
|
<serial type='pty'/>
|
|
<console type='pty'/>
|
|
<graphics type='vnc' listen='127.0.0.1' autoport='yes'/>
|
|
<video><model type='vga'/></video>
|
|
<memballoon model='virtio'/>
|
|
</devices>
|
|
</domain>
|
|
XML
|
|
virsh define "$TMP/client2.xml" >/dev/null
|
|
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" <<XML
|
|
<domain type='kvm'>
|
|
<name>$CLIENT3_VM</name>
|
|
<memory unit='MiB'>$CLIENT3_MEM_MIB</memory>
|
|
<vcpu>1</vcpu>
|
|
<os>
|
|
<type arch='i686' machine='pc'>hvm</type>
|
|
</os>
|
|
<features><acpi/></features>
|
|
<cpu mode='custom' match='exact'><model fallback='forbid'>qemu32</model></cpu>
|
|
<clock offset='localtime'/>
|
|
<devices>
|
|
<interface type='network'>
|
|
<source network='$IMAGING_NET'/>
|
|
<mac address='$CLIENT3_MAC'/>
|
|
<model type='rtl8139'/>
|
|
<boot order='1'/>
|
|
</interface>
|
|
<disk type='file' device='disk'>
|
|
<driver name='qemu' type='qcow2'/>
|
|
<source file='$POOL_DIR/$CLIENT3_DISK_VOL'/>
|
|
<target dev='hda' bus='ide'/>
|
|
<boot order='2'/>
|
|
</disk>
|
|
<graphics type='vnc' listen='127.0.0.1' autoport='yes'/>
|
|
<video><model type='vga'/></video>
|
|
</devices>
|
|
</domain>
|
|
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" "${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" ;;
|
|
network:"$IMAGING_NET") ;;
|
|
*) die "$vm has an interface on $type:$src (only $GE_NET / $IMAGING_NET allowed)" ;;
|
|
esac
|
|
done < <(virsh domiflist "$vm" | tail -n +3 | grep -v '^$')
|
|
done
|
|
log "ready. Playbook NIC pins for this site:"
|
|
log " pxe_nic_mac: \"$SERVER_IMAGING_MAC\""
|
|
log " corp_nic_mac: \"$SERVER_GE_MAC\""
|