preferred_video_model returned vga whenever no preferred model was
found, also when the hypervisor did not offer vga, did not support video,
or the capability query failed. Under native aarch64 KVM, vga and the
other PCI framebuffer models do not display correctly.
An aarch64 guest now takes the first offered model of virtio and ramfb
under KVM, or of virtio, vga and ramfb under TCG. When video is
unsupported or no wanted model is offered, build_xmldesc writes no
<video> and no <graphics>, and the guest uses its serial console. When
the capabilities are unavailable, build_xmldesc returns an error that
names the query error and vm.vidmodel.
vm.vidmodel still overrides the choice. Other architectures do not
query the capabilities and keep their fixed model.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
tabrestore stripped the comma after each field, so a comma after the
field of the last column left nothing to report. A row with one more
empty field was restored as if it fit the columns. A last extra field
of 0 also passed, because the check tested the rest of the line for
truth.
tabrestore now refuses a row whose last column field ends with a comma,
or that has any text left, and rolls back the restore.
The kit commands refuse RHEL 8 and later, but compared the release as a
string. "10.1" sorts before "8", so the kit commands ran on RHEL 10.
They now compare the major release as a number.
imgexport records an extra file with its destination directory. When
that directory did not exist, imgimport copied the file to the path of
the directory, so the file took the name of the directory. When a file
was at that path, imgimport replaced it.
imgimport now creates the destination directory and its missing
parents, as it does for an extra directory. A file at that path fails
the import and stays as it was. The copy runs without a shell, so a
file name with glob characters cannot match another file. A failed
backup of the previous file now fails the import.
kit.pm tested the bareword locked instead of $locked. The test was
always false, so a kit command never took the kit lock, and it ran also
while another process held the lock.
A kit command now takes the lock in one step, and fails while another
process holds it. The is_locked probe is gone, because its release
removed the lock file that another kit command could then hold. A kit
command that another kit command runs goes on under the lock of that
command.
imgimport looked up the litefile entries for all images with "image" =
'ALL'. MySQL takes a double-quoted name for a string, so the lookup
found nothing, and imgimport added entries that an ALL entry already
covered. The lookup now quotes the column the way the database needs.
The removal of abandoned stages, the removal of the other archive
formats after a publication, and rmimage found their files with glob.
glob splits a pattern at a space, so for an image directory such as
"/install/custom images/node" the first word named another path.
rmtree then removed that directory, with its images. glob also takes a
wildcard character in the path of the image directory as a pattern.
image_dir_entries now lists the image directory itself and matches the
names of its entries, and these removals use it.
A failed publication put back only the content of the previous checksum
file. A checksum symlink became a regular file, which a later switch of
the symlinks would leave stale.
publish_rootimg now keeps the previous checksum entry, and a rollback
renames it back. When that rename fails, the previous content goes
back, and the error reports it.
When a failed publication could not put back the previous archive or
metainfo, it moved them to a .rootimg-previous-* directory. When that
move failed too, they stayed in the staging directory, which packimage,
liteimg and imgimport then removed. The only copy of the previous files
was lost.
Staging that keeps a previous file now stays, and the error names it.
The removal of abandoned stages leaves it, and rmimage removes it with
the image. A publication removes its previous files from staging
otherwise, so only a failed one leaves them.
When a publication failed, the rollback put back the previous metainfo
and checksum without checking either step. A metainfo that could not go
back stayed in staging, which cleanup then removed, and the rollback
reported nothing. The rollback now checks each step. It moves a previous
metainfo that cannot go back next to the image, as it does with a
previous archive, and reports where. It also reports a checksum that it
cannot put back.
When no archive was published before, as for the first pack or a change
of format, and the metainfo rename failed after the archive rename, the
rollback looked for a previous archive that did not exist. It reported
that archive as kept in an empty directory, and left the new archive and
its checksum published. The rollback now removes the new archive and its
checksum.
rmimage removed rootimg.* from the image directory. That missed rootimg-
statelite.gz with its checksum and metainfo, and the .rootimgcheck
marker of the initial ramdisk. A temporary directory that an interrupted
packimage, liteimg or imgimport left on this host also stayed, with a
partial archive in it.
rmimage now removes these files, the temporary directories that writers
on this host abandoned, and the .rootimg-previous-* directories that
failed publications kept. rootimg_stage and rmimage share this cleanup
through remove_abandoned_rootimg_stages.
A SLES statelite ramdisk image built with dracut boots
initrd-stateless.gz, but nodeset copied only initrd-statelite.gz to the
TFTP directory and checked only for that file. Nodes requested an initial
ramdisk that nodeset never copied, or one that an older nodeset left.
nodeset now chooses the initial ramdisk once, then copies, checks and
boots that file, as it does on Red Hat.
The statelite branch of debian.pm tested rootfstype without its $, so it
compared the string rootfstype with ramdisk and always took the NFS root
branch. Ubuntu statelite ramdisk nodes got an NFS root and no imgurl, so
they never downloaded rootimg-statelite.gz.
nodeset now tests $rootfstype, and Ubuntu statelite ramdisk nodes get
the imgurl of rootimg-statelite.gz, as on Red Hat and SUSE.
The root image check is on by default, so an upgrade from a release
without it would turn it on for every existing stateless and statelite
image. Their nodes would then need a checksum and an initial ramdisk
that can check.
On an upgrade, while site.rootimgchecksum is not set, xcatconfig sets
the rootimgchecksum attribute to no for each of these images that does
not set it, and then sets site.rootimgchecksum to yes. The existing
images keep booting unchecked, and new images get the check. A later
upgrade finds the site attribute set and changes nothing. A new
installation sets site.rootimgchecksum to yes.
Nodes could not know whether an image turned the root image checksum on.
An initial ramdisk built before the check booted the image unchecked,
and nothing reported it. An archive packed before the check had no
checksum for a new initial ramdisk to find.
When the image or the site turns the checksum on, nodeset adds
rootimgchecksum=yes to the kernel command line of the node. This applies
to stateless and statelite ramdisk nodes. genimage writes
<initrd>.rootimgcheck next to an initial ramdisk that can check. The
marker holds the SHA-256 checksum of the initial ramdisk, so a replaced
ramdisk does not match it. imgexport and imgimport carry the marker with
the ramdisk.
When the checksum is on, nodeset warns once for each image whose initial
ramdisk has no matching marker. Its nodes may boot the image unchecked.
nodeset judges the initial ramdisk in TFTP after it copies the boot
files, because nodes boot that copy, also with --noupdateinitrd. A copy
that fails is now an error for the node, instead of a boot entry for the
previous boot files.
genimage and nodeset write a checksum for each archive of the image
directory that has none, also when the check is off. Nodes with older
boot entries still check. genimage does it before it copies a new
initial ramdisk to TFTP, and nodeset before it copies the boot files of
a node. Both hold the publication lock while they write. When they
cannot write the checksum, they keep the boot files that nodes use now
and report how to recover. With the check off, nodeset reports it as a
warning instead, and still sets up the node. nodeset marks the boot
files of an image as copied only after this step succeeds. When the
first node of an image fails it, the next node copies them.
The change covers the Red Hat, SUSE and Ubuntu nodeset plugins, which
share one helper in xCAT::SvrUtils.
liteimg deleted rootimg-statelite.gz before it packed a statelite
ramdisk image, and wrote the new archive in place. It ignored a failed
pack, so nodes could get no image or a partial one. It took no image
lock. --tracker took no value, so the metainfo named the tracker 1, and
it described rootimg.gz instead of the new archive.
liteimg now takes the image lock and packs into the temporary directory
that packimage uses. It fails when a command of the pack fails. It
writes the SHA-256 checksum, and publishes it with the archive through
publish_rootimg. --tracker takes the tracker URL, as it does for
packimage, and the metainfo describes the new archive. Nodes keep
downloading the previous archive until the new one replaces it.
imgimport moved the published root image away, then moved the copy from
the bundle in. When the bundle was on another file system, that move was
a copy. Nodes could get no image or a partial one. The checksum that
packimage had published for the previous image also stayed.
imgimport now takes the image lock that genimage, packimage and rmimage
use. It stages the imported image and its checksum in the image
directory. It publishes both with the packimage helper, under the same
publication lock. It writes the checksum whatever the rootimgchecksum
attribute of the imported image, as packimage does. The previous image
stays as .ORIG through a hard link, and its metainfo is removed.
imgimport also places the root image before the kernel and ramdisk now.
When it cannot publish the root image, it stops, so the kernel and
ramdisk keep matching the published image. It takes the image lock
before it imports the image definition, so an import that another image
command blocks changes nothing.
A node could not tell a damaged root image download from a good one,
because packimage published nothing to check it against.
packimage now writes rootimg.<suffix>.sha256 in sha256sum format next to
the archive. It builds the checksum in the temporary directory with the
archive, and publish_rootimg publishes both under the publication lock.
A pack that fails before publication keeps the previous archive and
checksum. When packimage removes the other formats, it keeps their
checksums, so a node that already downloads one can still check it.
Until the archive is replaced, the checksum file lists the new and the
previous archive. publish_rootimg computes the line of a previous
archive that has none. The published archive then matches a line of it
at each step, also when the publisher is killed. The previous metainfo
leaves first, so it never describes the new archive. publish_rootimg
keeps a link to the previous archive, or a copy with its mode without
hard links. When a step fails, the previous archive, checksum and
metainfo stay or come back. When it can keep the previous archive in
neither way, it publishes nothing. When it cannot put the previous
archive back, it keeps it in a .rootimg-previous-* directory and names
that directory in the error.
The new site.rootimgchecksum attribute is yes by default, and the
rootimgchecksum attribute of an osimage overrides it. They tell nodeset
whether nodes check the archive. packimage publishes the checksum
whatever their value, because nodes with older boot entries can still
check.
imgimport must replace a root image the way packimage does. Move the
host-scoped temporary directory and the locked publication from
packimage to xCAT::SvrUtils, so both commands can share them. The
publication still covers the archive and its metainfo. The behavior of
packimage does not change.
build_xmldesc, build_diskstruct and createstorage made their domain
decisions inline, so the unit tests could reach them only by loading the
whole plugin. kvm.pm now calls xCAT::KVMUtils for each decision and keeps
the libvirt calls, the table updates and the globals. $updatetable,
$confdata, $hypconn and $node are lexicals again.
The domain XML, the disk list and the volume names are unchanged.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
A local imgexport wrote the bundle to the current working directory and
kept only the file name of the destination, so a destination such as
/backup/images/compute.tgz went to ./compute.tgz. A destination in a
missing directory failed only after the image was copied.
imgexport now writes the bundle to the destination, and takes a
relative path from the current working directory. It checks the
directory before it copies the image. tar and scp take the path as an
argument without a shell, so a path with a space works.
packimage passes the image directory to shell commands without quotes,
such as the rm -rf of the xcatpost directory. It finds its abandoned
stages with glob. For an image directory such as
"/install/custom images/node", the shell and glob split the path at the
space, so packimage removed /install/custom with the images in it.
packimage now refuses an image directory whose path has a space or a
shell character, before it runs any command.
kvm_guest_arch.t cut build_xmldesc and five other routines out of
kvm.pm with a regex and ran them with eval in a scratch package. The
test passed when kvm.pm changed in a way the regex did not see, and
it failed when kvm.pm was reformatted.
The test now loads kvm.pm with require and calls
xCAT_plugin::kvm::build_xmldesc and build_diskstruct. Sys::Virt and
the monitoring plugin are stubbed in %INC. kvm.pm declares
$updatetable, $confdata, $hypconn and $node with our instead of my,
so that the test can set them with local. The test sets XCATROOT to
the checkout and asserts that no module loads from /opt/xcat.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
packimage deleted every rootimg.* file before it packed, and wrote the new
cpio or tar archive under its final name with mode 0600. Until the pack
finished, a node that booted got HTTP 403 for its image. When the pack
failed, the image was gone or left truncated, and no node could boot until
a later pack succeeded.
Build every archive in a temporary directory inside the image directory,
and rename the archive over the final name only after the pack succeeds,
as the openEuler path already did with a temporary file. With --tracker,
ctorrent builds the metainfo in the same directory, from the new archive
under its final name. The previous metainfo is removed before the archive
is renamed, and the new metainfo is renamed after it, so no failure
leaves a metainfo that describes another archive. The other rootimg.*
files are removed after the rename. The next pack on the same host
removes a temporary directory that an interrupted pack left. Image locks
are per host, so temporary directories of other hosts that share the
directory stay.
A pack now also fails when find cannot list the image or cpio cannot read
a file, as on the openEuler path. Before, only the compressor status
counted, and such a pack published an incomplete archive.
Every failure after packimage switches a StateLite tree to stateless files
now switches it back. That includes a failure to create the temporary
directory or the squashfs staging directory, which File::Temp reports
with die. packimage records each file that it moves and checks each move. When
a file cannot be moved back, the original stays in .statebackup,
packimage reports it, and the next pack refuses to run until the files
are restored. The new archive replaces the previous one only after the
StateLite files are back. Before, a failed pack left the tree switched,
and a retry overwrote the saved StateLite files.
The image lock is per host, and service nodes mount a shared image
directory without NFS locks. So a pack also holds the directory
.packimage-publish while it replaces the archive and removes the other
formats. mkdir is atomic for every host, also on NFS without locks. When
the lock is older than 60 seconds, or stays held for 60 seconds, the pack
fails, names the holder, and keeps the previous archive. packimage does
not take the lock over, because its holder can still be running.
A ctorrent failure now fails the pack, and the previous archive and
metainfo stay. Before, a failed ctorrent left a partial metainfo, and
nodeset pointed nodes at it.
The aarch64 profile named one video model for every hypervisor. A model is not
a property of the architecture: the emulator build decides it. On a host whose
aarch64 emulator carries no virtio-gpu device, libvirt answers "domain
configuration does not support video model 'virtio'" and mkvm cannot define the
node, while the riscv64 emulator of the same host offers virtio.
build_xmldesc() now reads the domain capabilities of the hypervisor and
preferred_video_model() returns the first model the profile wants that the
hypervisor offers. An aarch64 guest wants virtio and falls back to vga, which
is what an emulator with no virtio-gpu device offers. vm.vidmodel still
overrides both, and an architecture with no preference list makes no libvirt
call.
Measured on a hypervisor whose aarch64 emulator has no virtio-gpu device: the
builder picks vga and virsh define accepts the domain. The same builder asking
for virtio is refused there.
Three assertions in kvm_guest_arch.t fail without this change.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
guest_arch_profile() gave every aarch64 guest domain type qemu and the
cortex-a57 CPU model, so an aarch64 hypervisor ran its own guests under
emulation. The profile now reads both architectures: an aarch64 guest on an
aarch64 hypervisor is a kvm domain with CPU mode host-passthrough, which is the
only CPU KVM runs on ARM, and every other hypervisor keeps the emulated case
with the custom cortex-a57 model.
build_xmldesc() read the hypervisor cpumodel alone for the CPU model, the CPU
topology and the qemu-system-ppc64 emulator, so an aarch64 guest on a POWER
hypervisor received a POWER CPU model, a pseries topology and a ppc64 emulator.
The profile now marks a pseries guest and those three settings follow the mark.
libvirt resolves the emulator of an ARM guest from its own capabilities.
A cpumode from vm.othersettings reached a domain of either type. Both
host-passthrough and host-model read the host CPU, which an emulated guest does
not run, and libvirt reports both as unsupported for an emulated aarch64
domain. An explicit mode now reaches a kvm domain only.
The video model is virtio, as it is for a riscv64 virt guest. The aarch64 virt
machine supports virtio video; the earlier refusal came from an emulator build
with no virtio-gpu device.
kvm_guest_arch.t covers the four host and guest pairs and the explicit CPU
modes. Ten of its assertions fail without this change.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
guest_arch_profile() in the kvm plugin has an arm for riscv64 and an arm for
POWER, and none for aarch64. A node whose nodetype.arch is aarch64 therefore
keeps the x86 defaults, and mkvm defines a domain with os/type/@arch x86_64 and
machine pc-i440fx. The compute node gets its lease, loads the x86 iPXE ROM,
fetches the grub2.aarch64 image that nodeset staged, and answers "Could not
boot image: Exec format error". It then falls through to the empty disk.
guest_arch_profile() now returns an aarch64 profile: domain type qemu, os arch
aarch64, machine virt, UEFI firmware, no x86 features and no SeaBIOS serial
option, no USB tablet, and scsi disks with the sd prefix. machine is the
unversioned alias, so libvirt resolves the version the hypervisor has. The
video model stays vga, unlike riscv64: the aarch64 virt machine offers no
virtio video model, and libvirt refuses the domain that asks for one.
The profile also names a CPU model, which riscv64 did not need. With no <cpu>
element libvirt passes -cpu max, which under TCG turns on SVE, and the guest
stays in its firmware. build_xmldesc writes the model only when nothing else
built a cpu element, so vm.cpumode and a pseries guest keep theirs.
kvm_guest_arch.t covers the new arm. Eleven of its assertions fail without this
change.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
The comments added with these fixes carried the bug history, the failing case
names and the reproduction detail. A reader of the code cannot act on any of
it, and the commit message already holds it.
Each comment is reduced to the one fact the code does not show: the platform
difference, the invariant, or the reason the simpler form is wrong.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
makedhcp <node> answers "Unable to find a Kea subnet for <node> (<ip>), skipping DHCP
reservation" for a node on a network whose mgtifname is "!remote!<nic>". The node gets no
reservation and never boots. The same cluster works with the ISC backend.
kea_build_dhcp4_intent skips a relayed network unless %activenics carries a "!remote!" key.
Nothing sets that key: %activenics comes from site.dhcpinterfaces or from the local routing
table, and a relay agent answers the network, so no local NIC carries it. The subnet4 is
absent and subnet_id_for_ip finds nothing for the node. The ISC path has no such condition.
Its @nrn loop writes the shared-network and the subnet from the networks table alone.
Require nothing for a relayed network, as ISC does. kea_subnet4_intent already leaves the
interface out of a relayed subnet, so Kea answers it through the relay.
The case that covers this is makedhcp_remote_network.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit 62af5506dad06c7017c0d57a69d7ccc47a471dd5)
Add the linuximage gpgcheck attribute. When it is set to 1 or yes,
genimage passes --gpgcheck to the shared RPM image builder, which
exports the builder's trusted RPM keys exactly as it does for openEuler
and writes gpgcheck=1 with that gpgkey for the pkgdir, kerneldir and
otherpkgdir repositories. Unset keeps the existing unverified
configuration, openEuler stays always verified, and builders without
the option reject the attribute instead of ignoring it.
Collect the otherpkgs repositories in imgutils so that an otherpkgdir
holding only URLs no longer adds a file:/// repository that fails to
download metadata on EL.
Signed-off-by: Timothy Middelkoop <tmiddelkoop@internet2.edu>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>