Newer ansible-core only takes a stdout callback name and no longer
templates untrusted YAML. Attach the results collector directly, load
plays trusted through the DataLoader, and report a crashed play
process as a failed task.
post.sh wrote installedargs into /etc/default/grub but only ran
update-grub when it had also added a console, so grub.cfg kept the old
command line until some later grub update.
Capture copies an initramfs-tools hook and conf.d/confluent.conf into the
source to build the deployment initramfs, but only ever removed the EL
dracut module afterwards. With MODULES=list left in place, the source's
next initramfs update was limited to the hook's driver list, and a source
captured again carried both files into the image, where rebuilding the
initramfs on install to disk shrank the clones' initramfs the same way.
Remove them after capture and leave them out of the image.
post.sh took the whole installedargs line from profile.yaml, key
included, so every Ubuntu install from these profiles got a literal
'installedargs:' in GRUB_CMDLINE_LINUX.
The installed system kept the initramfs of the image. Ubuntu 26.04
builds it with dracut in host-only mode, so a captured image's initramfs
waits for the source's volume group and filesystem UUIDs and the
installed node ends in emergency mode. Rebuild it in post.sh, as EL does
through kernel-install.
Dropping every NetworkManager profile and ifcfg file also removed
settings that confignet does not recreate from confluent attributes,
such as bonds, DHCP profiles, MTU or the InfiniBand transport mode. Only
drop the profiles that carry static addresses of the source.
dhcpcd, present on Ubuntu 24.04 and later and on EL10, keeps its leases,
DUID and secret in /var/lib/dhcpcd, and NetworkManager keeps a secret in
secret_key. Copied into an image, every clone would use the same DHCPv6
DUID and derive the same stable privacy IPv6 addresses. Both regenerate
them when missing.
dhclient keeps its leases in /var/lib/dhcp there, and on EL also as
*.leases next to the *.lease files already excluded. Keep dhcpd.leases
of a DHCP server, which lives in the same directory.
The image kept /etc/hostname of the source host, so a diskless node ran
under that name until onboot set its own, and early journal and syslog
entries carried the wrong name. With an empty /etc/hostname systemd
keeps the node name the deployment initramfs already set. Install to
disk writes /etc/hostname itself.
Captured images kept the NetworkManager connection profiles and ifcfg
files of the source host. A diskless boot kept them too, and confignet
only rewrites the profiles named like the interfaces it configures, so
every other interface came up with the source host's settings, such as
its static addresses. Install to disk already removes them, and capture
already masks netplan files for the same reason. Drop them from the
image, except ifcfg-lo.
lvm is optional in the capture initramfs now, so installimage printed
'lvm: command not found' on every install of a capture taken from a host
without lvm2. Only run it when it is there.
mksquashfs matches excluded paths by inode, so excluding a hard linked
file also dropped its other links elsewhere in the filesystem. Keep such
files in the image. squashfs-tools 4.3 also splits exclude file lines on
whitespace, so pass paths with whitespace or backslashes with -e instead.
The guess for volume groups named by the OS installer (<os>_<node>)
matched any device mapper name with an underscore followed later by a
hyphen. A localstorage volume for a mount point with a hyphen, such as
localstorage-_srv_my--data, was taken for one and got a broken volume
group name. Recognize volumes of the target volume group before trying
that guess.
The plain partitions were addressed by their number on the source system,
while parted numbers them in the order they are created. A source with a
gap, such as swap between filesystems (it is not captured), made
image2disk format the wrong partition and look for swap on one that does
not exist. Count the partitions as they are created instead.
Also look up filesystem UUIDs through one helper.
image2disk.py formatted the target filesystems, copied the image and
ran fixup only in the branch that sets up LVM. For an image whose
filesystems are all plain partitions it created the partitions and
returned success without writing anything, and installimage then failed
on the missing target.
Run that part for both layouts. Plain partitions also need their swap
in fstab by UUID instead of the localstorage-swap volume, and on EL9
and later root= and resume= refer to the new root and swap by UUID as
well, since partition names are not stable with several disks.
image2disk.py puts the volumes of a node it installs into the volume
group localstorage. Capturing such a node and installing the profile to
disk again failed with 'No mapping defined to create volume group
localstorage': volumes named localstorage-root were taken for volumes of
an extra volume group that needs volumegroupmap.yml.
Treat volumes of a volume group named like the target one as main volume
group volumes, and split device mapper names into volume group and
logical volume names correctly, including hyphens that device mapper
doubles inside either name.
When the largest filesystem of a captured image is a plain partition,
it was sized as the disk minus the other filesystems. That ignored the
2048 sector gap before the first partition and the rounding of every
partition up to a 2048 sector boundary, so the last partition always
ran past the end of the disk. The clamp then set its end to the sector
count, one past the last sector and inside the backup GPT, and parted
refused it with 'The location ... is outside of the device'. Images
with the largest filesystem in LVM were not affected.
Reserve that overhead plus 1MiB at the end of the disk for the backup
GPT, and clamp to that last sector instead. Also round the swap
partition by its own size rather than by the previous partition's.
Capture copied everything on the local filesystems, including user
files and sockets in /tmp and /var/tmp, crash dumps, logs and the
persistent journal, sssd cached credentials, package caches, the
random seed, DHCP leases and root's shell history. All of it ended up
on every node deployed from the profile.
Drop the contents of scratch, crash, journal, mail spool, sssd cache
and package cache directories, keep only the directory tree under
/var/log and /var/cache/apt since daemons expect their subdirectories
there, and drop a short list of individual files. The directories
themselves are kept, so their mode, ownership and labels survive.
mksquashfs gets the paths as an exclude file, so this applies the same
way whether or not a directory is its own mount.
Hosts without lvm2 installed made dracut report a FAILED dracut-install
during imgutil capture. The initramfs was still built, just without lvm.
lvm is only used there to deactivate stale volume groups before the
install. Any LVM layout is created from inside the image, which carries
its own lvm2 when the source used LVM.
Mark lvm optional, the same way unsquashfs already is.
mksquashfs packs the image and the dracut module needs openssl. Without
them capture failed silently and only the later rsync of private.key
reported an error. Also stop when capturing a filesystem fails, unmount
the capture tmpfs on the target, and have run_constrained print the
exception it used to swallow. squashfs-tools is now required, so drop
the optional prerequisite reporting.
Captured images keep the policy of the source, which lacks the rules
images built with --selinux get. Install them on the source for the
capture and remove them again afterwards, unless the source already
had them.
Their scripts run onboot.d and the others, but the profiles did not
have them like el8 and el9. el10 also lacked the syncfiles template,
now linked to el9's like its other shared files, and Ubuntu lacked the
firstboot.custom that its firstboot.sh runs.
Mark urlmount as an initramfs root storage daemon before entering FUSE so systemd-shutdown does not wait for or kill the process serving the root image.
imgutil build left the downloaded packages and repository metadata in
the image: 1.3 GB of .deb archives plus 145 MB of package lists for
Ubuntu 26.04, about 100 to 160 MB of dnf metadata for EL and 80 MB of
zypp metadata for Leap 16. Untethered nodes copy all of it into memory.
Remove them after building. Installing more packages into the image
then needs a repository refresh first, as usual for images.
setfiles without -F only corrects the type of a file and keeps its user,
role and range. Files of a new build tree get the SELinux user of the
process that created them, so about 1800 of them stayed unconfined_u
instead of system_u. Label the new tree with -F. Pack keeps relabeling
without it, so contexts set on purpose in a captured or edited tree stay.
imageboot.sh wrote /etc/netplan/10-<iface>-confluentcfg.yaml with the
default mode 0644, and netplan warns that its configuration should not
be accessible by others. confignet already writes its files 0600, but
it leaves an identical file in place, so the boot-time one stayed 0644.
urlmount is started by the initramfs before the policy is loaded, so it
keeps running as kernel_t. The EL10 targeted policy denies kernel_t
connecting to http_port_t, so after systemd loads the policy every read
of a tethered root image that needs a new connection fails and the node
freezes. imageboot.sh also runs dd from the initramfs later, which is
denied entrypoint. Install a small module with exactly these two rules
in images built with --selinux.