[Libguestfs] [p2v PATCH 3/4] make-physical-machine.sh: regenerate filesystem UUIDs

Laszlo Ersek lersek at redhat.com
Fri Aug 26 11:39:07 UTC 2022


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 at 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=



More information about the Libguestfs mailing list