Eliminate USB requirement for WinPE PXE boot, add image upload script

- Add startnet.cmd: FlatSetupLoader.exe + Boot.tag/Media.tag eliminates
  physical USB requirement for WinPE PXE deployment
- Add Upload-Image.ps1: PowerShell script to robocopy MCL cached images
  to PXE server via SMB (Deploy, Tools, Sources)
- Add gea-shopfloor-mce image type across playbook, webapp, startnet
- Change webapp import to move (not copy) for upload sources to save disk
- Add Samba symlink following config for shared image directories
- Add Media.tag creation task in playbook for drive detection
- Update prepare-boot-tools.sh with Blancco config/initramfs patching
- Add grub-efi-amd64-bin to download-packages.sh

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-02-12 16:40:27 -05:00
parent f4c158a5ac
commit 1a5c4f7124
7 changed files with 696 additions and 38 deletions

View File

@@ -105,7 +105,10 @@ if [ -n "$BLANCCO_ISO" ] && [ -f "$BLANCCO_ISO" ]; then
cp "$TMPDIR/arch/x86_64/airootfs.sfs" "$OUT_DIR/blancco/arch/x86_64/"
echo " Extracted Blancco boot files."
# Patch config.img to auto-save reports to PXE server Samba share
# --- Patch config.img (size-preserving) ---
# config.img is a CPIO archive containing preferences.xml (padded to 32768 bytes).
# The CPIO itself must remain exactly 194560 bytes (380 x 512-byte blocks).
# We use Python for byte-level replacement to preserve exact sizes.
if [ -f "$OUT_DIR/blancco/config.img" ]; then
echo " Patching config.img for network report storage..."
CFGTMP=$(mktemp -d)
@@ -113,19 +116,149 @@ if [ -n "$BLANCCO_ISO" ] && [ -f "$BLANCCO_ISO" ]; then
cpio -id < "$OUT_DIR/blancco/config.img" 2>/dev/null
if [ -f "$CFGTMP/preferences.xml" ]; then
# Set network share to PXE server's blancco-reports Samba share
sed -i 's|<hostname></hostname>|<hostname>10.9.100.1</hostname>|' "$CFGTMP/preferences.xml"
sed -i 's|<path></path>|<path>blancco-reports</path>|' "$CFGTMP/preferences.xml"
# Enable auto-backup of reports to the network share
sed -i 's|<auto_backup>false</auto_backup>|<auto_backup>true</auto_backup>|' "$CFGTMP/preferences.xml"
ORIG_SIZE=$(stat -c%s "$CFGTMP/preferences.xml")
# Repack config.img
ls -1 | cpio -o -H newc > "$OUT_DIR/blancco/config.img" 2>/dev/null
echo " Reports will auto-save to \\\\10.9.100.1\\blancco-reports"
python3 << 'PYEOF'
import sys
with open("preferences.xml", "rb") as f:
data = f.read()
orig_size = len(data)
# Set SMB share credentials and path
data = data.replace(
b'<username encrypted="false"></username>',
b'<username encrypted="false">blancco</username>'
)
data = data.replace(
b'<password encrypted="false"></password>',
b'<password encrypted="false">blancco</password>'
)
data = data.replace(
b'<hostname></hostname>',
b'<hostname>10.9.100.1</hostname>'
)
data = data.replace(
b'<path></path>',
b'<path>blancco-reports</path>'
)
# Enable auto-backup
data = data.replace(
b'<auto_backup>false</auto_backup>',
b'<auto_backup>true</auto_backup>'
)
# Enable bootable report
data = data.replace(
b'<bootable_report>\n <enabled>false</enabled>\n </bootable_report>',
b'<bootable_report>\n <enabled>true</enabled>\n </bootable_report>'
)
# Maintain exact file size by trimming trailing padding/whitespace
diff = len(data) - orig_size
if diff > 0:
# The file has trailing whitespace/padding before the final XML closing tags
# Trim from the padding area (spaces before closing comment or end of file)
end_pos = data.rfind(b'<!-- ')
if end_pos > 0:
comment_end = data.find(b' -->', end_pos)
if comment_end > 0:
data = data[:comment_end - diff] + data[comment_end:]
if len(data) > orig_size:
# Fallback: trim trailing whitespace
data = data.rstrip()
data = data + b'\n' * (orig_size - len(data))
elif diff < 0:
# Pad with spaces to maintain size
data = data[:-1] + b' ' * (-diff) + data[-1:]
if len(data) != orig_size:
print(f" WARNING: Size mismatch ({len(data)} vs {orig_size}), padding to match")
if len(data) > orig_size:
data = data[:orig_size]
else:
data = data + b'\x00' * (orig_size - len(data))
with open("preferences.xml", "wb") as f:
f.write(data)
print(f" preferences.xml: {orig_size} bytes (preserved)")
PYEOF
# Repack CPIO with exact 512-byte block alignment (194560 bytes)
ls -1 "$CFGTMP" | (cd "$CFGTMP" && cpio -o -H newc 2>/dev/null) | \
dd bs=512 conv=sync 2>/dev/null > "$OUT_DIR/blancco/config.img"
echo " Reports: SMB blancco@10.9.100.1/blancco-reports, bootable report enabled"
fi
cd "$SCRIPT_DIR"
rm -rf "$CFGTMP"
fi
# --- Patch initramfs to keep network interfaces up after copytoram ---
# Blancco uses copytoram=y which triggers archiso_pxe_common latehook to
# flush all network interfaces. Binary-patch the check from "y" to "N" so
# the condition never matches. IMPORTANT: full extract/repack BREAKS booting.
echo " Patching initramfs to preserve network after copytoram..."
python3 << PYEOF
import lzma, sys, os
initramfs = "$OUT_DIR/blancco/initramfs-bde-linux.img"
with open(initramfs, "rb") as f:
compressed = f.read()
# Decompress XZ stream
try:
raw = lzma.decompress(compressed)
except lzma.LZMAError:
print(" WARNING: Could not decompress initramfs (not XZ?), skipping patch")
sys.exit(0)
# Binary patch: change the copytoram check from "y" to "N"
old = b'"y" ]; then\n for curif in /sys/class/net'
new = b'"N" ]; then\n for curif in /sys/class/net'
if old not in raw:
# Try alternate pattern with different whitespace
old = b'"\${copytoram}" = "y"'
new = b'"\${copytoram}" = "N"'
if old in raw:
raw = raw.replace(old, new, 1)
# Recompress with same XZ settings as archiso
recompressed = lzma.compress(raw, format=lzma.FORMAT_XZ,
check=lzma.CHECK_CRC32,
preset=6)
with open(initramfs, "wb") as f:
f.write(recompressed)
print(" initramfs patched: copytoram network flush disabled")
else:
print(" WARNING: copytoram pattern not found in initramfs, skipping patch")
PYEOF
# --- Build GRUB EFI binary for Blancco chainload ---
# Broadcom iPXE can't pass initrd to Linux kernels in UEFI mode.
# Solution: iPXE chains to grubx64.efi, which loads kernel+initrd via TFTP.
GRUB_CFG="$SCRIPT_DIR/boot-tools/blancco/grub-blancco.cfg"
if [ -f "$GRUB_CFG" ]; then
if command -v grub-mkstandalone &>/dev/null; then
echo " Building grubx64.efi (GRUB chainload for Blancco)..."
grub-mkstandalone \
--format=x86_64-efi \
--output="$OUT_DIR/blancco/grubx64.efi" \
--modules="linux normal echo net efinet tftp chain sleep" \
"boot/grub/grub.cfg=$GRUB_CFG" 2>/dev/null
echo " Built grubx64.efi ($(du -h "$OUT_DIR/blancco/grubx64.efi" | cut -f1))"
else
echo " WARNING: grub-mkstandalone not found. Install grub-efi-amd64-bin:"
echo " sudo apt install grub-efi-amd64-bin grub-common"
echo " Then re-run this script to build grubx64.efi"
fi
else
echo " WARNING: grub-blancco.cfg not found at $GRUB_CFG"
fi
else
echo " Could not extract boot files from ISO."
fi