Some test targets in "Makefile.am", such as "run-virt-p2v-in-a-vm"
and
"run-virt-p2v-in-an-nvme-vm", use both the PHYSICAL_MACHINE disk (as the
conversion subject) and "virt-p2v.img" (as boot media).
Unless the user overrides the PHYSICAL_MACHINE macro, we generate it with
virt-builder, using a distro that we open-code (currently: Fedora 35).
"virt-p2v.img" is also generated with virt-builder, but the distro
installed in it follows the user's host OS (therefore we have less control
over the distro choice in "virt-p2v.img").
The user's host OS may happen to match the OS we hardcode for
PHYSICAL_MACHINE (currently: Fedora 35), in which case we end up using the
same virt-builder template for both images. This is a problem: the
filesystems between both images will have identical UUIDs, which may cause
the kernel loaded from "virt-p2v.img" to mount the root filesystem from
PHYSICAL_MACHINE.
Prevent this by regenerating the filesystem UUIDs in PHYSICAL_MACHINE
(whose distro we closely control, unless the user sets PHYSICAL_MACHINE
themselves). Instead of the virt-sysprep operation "fs-uuids", which
currently cannot update the filesystem UUID references in the guest's
config files (<
https://libguestfs.org/virt-sysprep.1.html#fs-uuids>,
<
https://bugzilla.redhat.com/show_bug.cgi?id=991641>), implement a
somewhat crude guest-side shell command for the same purpose. Such a
command would likely not be flexible / robust enough for "fs-uuids" in
virt-sysprep, but -- due to our tight control over the PHYSICAL_MACHINE
operating system in virt-p2v -- it should suffice for virt-p2v testing.
Signed-off-by: Laszlo Ersek <lersek(a)redhat.com>
---
make-physical-machine.sh | 82 +++++++++++++++++++-
1 file changed, 81 insertions(+), 1 deletion(-)
diff --git a/make-physical-machine.sh b/make-physical-machine.sh
index ff6db4940b51..946d59f0c30b 100755
--- a/make-physical-machine.sh
+++ b/make-physical-machine.sh
@@ -16,11 +16,16 @@
set -e -u -C
+GUESTFISH_PID=
disk=
cleanup()
{
set +e
+ if test -n "$GUESTFISH_PID"; then
+ guestfish --remote -- exit >/dev/null 2>&1
+ GUESTFISH_PID=
+ fi
if test -n "$disk"; then
rm -f -- "$disk"
disk=
@@ -32,6 +37,81 @@ trap cleanup EXIT
output=$1
outdir=$(dirname -- "$output")
disk=$(mktemp -p "$outdir" physical-machine.tmp.XXXXXXXXXX)
-virt-builder --format raw -o "$disk" --root-password password:p2v-phys
fedora-35
+# Delay the SELinux relabeling.
+virt-builder --format raw -o "$disk" --root-password password:p2v-phys \
+ --no-selinux-relabel fedora-35
+
+# Start a guestfish server on the disk image, so that each of the several
+# UUID-manipulation commands below not need a separate guestfish launch.
+guestfish_set_pid=$(guestfish --listen --format=raw --add "$disk")
+eval "$guestfish_set_pid"
+guestfish --remote -- run
+
+# The array below open-codes the partition:filesystem layout of the virt-builder
+# disk template used above. Whenever you bump the Fedora version, update the
+# array below as needed.
+#
+# We cannot use inspection either before or after the filesystem UUID changes:
+#
+# - the UUID of a mounted filesystem cannot be changed (at least with XFS);
+#
+# - right after the UUID changes, inspection does not work correctly, as the new
+# fs UUIDs are out of sync with the UUID references in those config files that
+# inspection relies upon.
+#
+# Note that the order of entries is important too: the mount points must be
+# listed in dependency order (put the dependees first, the dependants last).
+fsdevs=(/dev/sda3:/ /dev/sda2:/boot)
+
+# For each filesystem:
+#
+# - regenerate the UUID,
+#
+# - produce a sed command (scriptlet) that performs the same UUID replacement in
+# a text file,
+#
+# - mount the filesystem.
+#
+# Note that later we're going to rely on the fact that the generated sed
+# commands *require no quoting* on the shell command line.
+declare -a sed_script
+sed_idx=0
+for fsdev in "${fsdevs[@]}"; do
+ device=${fsdev%:*}
+ mountpoint=${fsdev#*:}
+
+ old_uuid=$(guestfish --remote -- get-uuid "$device")
+ guestfish --remote -- set-uuid-random "$device"
+ new_uuid=$(guestfish --remote -- get-uuid "$device")
+
+ sed_script[sed_idx++]=-e
+ sed_script[sed_idx++]=s/$old_uuid/$new_uuid/ig
+
+ guestfish --remote -- mount "$device" "$mountpoint"
+done
+
+# Prepare the UUID replacement shell command for the appliance.
+refresh_uuid_refs=(find /boot /etc -type f -print0 '|'
+ xargs -0 -r -- sed -i "${sed_script[@]}" --)
+
+# Passing the shell command to the appliance is where we rely on the fact that
+# the sed commands for replacing UUIDs require no quoting.
+guestfish --remote -- sh "${refresh_uuid_refs[*]}"
+
+# Tear down the guestfish server before we use virt-customize.
+waitpid=$GUESTFISH_PID
+guestfish --remote -- exit
+GUESTFISH_PID=
+while kill -s 0 -- "$waitpid" 2>/dev/null; do
+ sleep 1
+done
+
+# Reapply the SELinux labels now. Use virt-customize for this, rather than
+# guestfish's "selinux-relabel", as virt-customize contains some heavy
logic
+# related to "specfile". Inspection does work here, because the config files
are
+# in sync again with the filesystem UUIDs.
+virt-customize --format raw --add "$disk" --selinux-relabel
+
+# We're done; rename the temporary disk image to the expected output file.
mv -- "$disk" "$output"
disk=