2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-21 16:39:30 +00:00
Commit Graph

27987 Commits

Author SHA1 Message Date
Daniel Hilst 4c8162824d fix(xcat-core): the alien Genesis base deb keeps the rpm architecture
debuild-xcat-genesis-base maps only x86_64 to amd64. For the ppc64 rpm it
leaves every name at ppc64, so it builds xcat-genesis-base-ppc64 and
writes "Breaks: xcat-genesis-scripts-ppc64". The Debian architecture is
ppc64el, and the packages xCAT-genesis-scripts builds for it are named
ppc64el, so the Breaks names a package no repository publishes.

This change replaces the single x86_64 test with an architecture map:
x86_64 to amd64, ppc64 and ppc64le to ppc64el. The map now drives the
source tree rename, the control and changelog rewrite, and the Breaks
field. PACKAGE_ARCH keeps the rpm value, because the preinst removes the
Genesis tree under that name.

genesis_base_deb_arch.t drives the script with alien shadowed. It fails
the three ppc64el assertions without this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-04 19:51:18 -03:00
Daniel Hilst b83d3379e6 fix(xcat-core): go-xcat names a genesis package no apt repository has
On ppc64el "go-xcat install" and "go-xcat uninstall" stop with

  E: Unable to locate package xcat-genesis-scripts-ppc64

apt refuses the whole transaction, so no package of the list is installed
or removed.

The dpkg branch of GO_XCAT_INSTALL_LIST in
xCAT-server/share/xcat/tools/go-xcat names xcat-genesis-scripts-ppc64 and
xcat-genesis-base-ppc64. Those are rpm architecture names.
GO_XCAT_UNINSTALL_LIST is derived from the same array, so both actions
carry the wrong names. The Debian architecture is ppc64el.

This change renames the two entries of the dpkg branch. The rpm branch
keeps ppc64, which is the Genesis target architecture of the spec.

go_xcat_genesis_package_names.t fails the four deb assertions without
this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-04 19:51:18 -03:00
Daniel Hilst 8c87ecf8e7 test(xcat-core): capture go-xcat naming a genesis package no repository has
go-xcat keeps one package list per packaging format. Its dpkg list names
xcat-genesis-scripts-ppc64 and xcat-genesis-base-ppc64. The Debian
architecture is ppc64el, so apt cannot find either package and both
"go-xcat install" and "go-xcat uninstall" stop on ppc64el.

The test evaluates the two arrays of go-xcat, one run per branch, and
compares them with the packaging: the deb names against the Package and
Depends fields of the xCAT-genesis-scripts control files, the rpm names
against the Genesis target architectures of xCAT-genesis-base.spec.

It fails on the four deb assertions today.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-04 19:51:18 -03:00
Daniel Hilst 6798db5fb7 fix(xcat-core): the Debian genesis dependency ignores the node architecture
A ppc64el or riscv64 management node installs the amd64 Genesis. xCAT and
xCATsn declare Architecture: amd64 ppc64el riscv64 and one unrestricted
Depends: xcat-genesis-scripts-amd64, so every architecture gets it. That
package is Architecture: all, so apt reports no error. It lays down
/opt/xcat/share/xcat/netboot/genesis/x86_64 and pulls the 128 MB
xcat-genesis-base-amd64, and the node receives no Genesis for its own
architecture. xcat-genesis-scripts-ppc64, the package that would carry it, is
uninstallable: it depends on xcat-genesis-base-ppc64, and builddeb-genesis-base
names the ppc64el base deb xcat-genesis-base-ppc64el.

xCAT/debian/control and xCATsn/debian/control now restrict the dependency by
architecture, the way xCAT.spec does with
%{?genesistarch:Requires: xCAT-genesis-scripts-%{genesistarch}}. amd64 gets
xcat-genesis-scripts-amd64, ppc64el gets xcat-genesis-scripts-ppc64el, and
riscv64 gets neither, because its Genesis is the OpenEmbedded image.
xCAT-genesis-scripts/debian/control-ppc64el builds xcat-genesis-scripts-ppc64el
and depends on xcat-genesis-base-ppc64el. It conflicts with and replaces the
old name, which shares the same files.

debian_control_arch_coverage.t asserts the genesis scripts an architecture
receives are that architecture's own, and that control-<arch> builds
xcat-genesis-scripts-<arch> against xcat-genesis-base-<arch>. Eight of its
assertions fail without this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-04 19:51:18 -03:00
Daniel Hilst 76d6967ed7 test(xcat-core): capture the Debian genesis dependency ignoring the architecture
The ppc64el and riscv64 xcat debs depend on xcat-genesis-scripts-amd64, and
xcat-genesis-scripts-ppc64 depends on xcat-genesis-base-ppc64, a package no
repository publishes. Nothing reports either one: the amd64 scripts package is
Architecture: all, so it installs on any architecture, and the broken ppc64
dependency is never reached because nothing pulls that package.

Extend debian_control_arch_coverage.t. It now reads the Depends field of
xCAT/debian/control and xCATsn/debian/control, applies each architecture
restriction the way dpkg-gencontrol does, and asserts that the genesis scripts
a given architecture receives are that architecture's own. It also asserts that
xCAT-genesis-scripts/debian/control-<arch> builds xcat-genesis-scripts-<arch>
and depends on xcat-genesis-base-<arch>.

Eight of the eighteen assertions fail on this tree.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-04 19:51:18 -03:00
Daniel Hilst 1db2c7cde0 fix(xcat-core): the genesis harness passes on a nodeset that failed and a node that never booted
The nodeset_shell_incorrectmasterip case passed while "nodeset testnode shell" failed with
"/tftpboot/boot/grub2/grub2.x86_64 does not exits". The grub2 sub-case asserted nothing. Every
genesis case reported "After 30 iterations node status: powering-on" and passed anyway.

check_destiny in xCAT-test/autotest/testcase/genesis/test.sh discarded the return value of
runcmd and read the boot configuration file, which grub2.pm writes before it stops on the
missing boot loader. xCAT builds no grub2 boot loader for x86_64, so the file is absent on a
correctly built management node. wait_for_boot in genesistest.pl waited for nodelist.status
"booted"; a Genesis node reports its destiny with getdestiny and xcatd writes "shell",
"configuring" or "booting" from it. Every caller discarded the return value. The node did not
reach even those statuses, because getdestiny makes its request file with mktemp and the
dracut module never installed it.

check_destiny now returns the status of nodeset, and the grub2 check stages an empty
grub2.<arch> when the management node has none and removes it after. wait_for_node_status
takes the status the destiny implies and each caller fails when the node does not reach it;
the shell case moved into run_nodeset_shell_test so its result can be read. clearenv no longer
waits, because "rinstall <node> boot" boots a disk with no operating system and reports
nothing. The dracut modules install mktemp and verify-genesis-payload requires it.

Tests: genesis_incorrectmasterip_check.t runs the check with a failing nodeset and reads
whether the boot loader is present when nodeset runs; genesis_testcase_helpers.t drives the
status wait and the shell case; genesis_payload_verification.t reads a payload without mktemp.
Each fails on the parent commit.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>

(cherry picked from commit 8ecf0a806785023aca0ea6d1b0e41810630816b2)

The Release hunk of the original commit is dropped. buildrpms.pl writes Release from
SOURCE_DATE_EPOCH at build time, so the committed snap stamp is build debris.
2026-09-04 19:29:00 -03:00
Daniel Hilst 4dd16eb5f1 test(xcat-core): capture the genesis harness passing on a failed nodeset
The nodeset_shell_incorrectmasterip case ran "nodeset testnode shell", the command failed
with "/tftpboot/boot/grub2/grub2.x86_64 does not exits", and the case still passed.
check_destiny in xCAT-test/autotest/testcase/genesis/test.sh discards the return value of
runcmd and greps the boot configuration file, which grub2.pm writes before it stops on the
missing boot loader. The sub-case asserts nothing.

wait_for_boot in xCAT-test/autotest/testcase/genesis/genesistest.pl waits for
nodelist.status "booted". A Genesis node reports its destiny with getdestiny and xcatd
writes "shell", "configuring" or "booting" from it, never "booted". Every caller discards
the return value, so each case rests on its xdsh probes alone.

genesis_incorrectmasterip_check.t now runs the check with a nodeset that fails, and reads
whether the grub2 boot loader for the node arch is present when nodeset runs.
genesis_testcase_helpers.t drives the status wait with lsdef shadowed, and drives the shell
case with every command it runs shadowed. genesis_payload_verification.t reads a payload
without mktemp, which getdestiny needs to make its request file.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit 3597760645ddf38f110457934982e2cc9a113f84)
2026-09-04 19:28:33 -03:00
Daniel Hilst 279207cf0a fix(xcat-core): the legacy Genesis image for el10 carries no DHCP client
A compute node that boots the legacy Genesis image on el10 x86_64 never acquires an
address. Its serial console reports "/usr/bin/doxcat: line 293: dhclient: command not
found" and then "It seems to be taking a while to acquire an IPv4 address". The DHCP
server side is sound: Kea leases the address and the node never asks for it.

AlmaLinux 10 and EPEL 10 package no ISC dhcp-client. dracut_install reports a missing
binary and returns, so module-setup.sh named dhclient, the build kept going and the image
shipped without a client. doxcat then named dhclient at six call sites with no
alternative.

doxcat now chooses its client at run time. genesis_dhcp_command() returns the command
line for one interface and one address family, and genesis_start_dhcp() runs it and
reports when the image carries none. The ISC client keeps its command line where a
release packages it, so el8 and el9 are unchanged. Where it is absent, dhcpcd stands in:
AlmaLinux 10 baseos packages it at 236 KB, and it carries its own resolv.conf, hostname
and ntp hooks, so it needs no dhclient-script. dhcpcd on a single interface exits when
its 30 second timeout expires and de-configures the interface as it goes, so it is asked
for -t 0 and -p.

The dracut module installs whichever client the build root carries, together with the
hooks dhcpcd runs on every lease. The spec build-requires dhcpcd from rhel 10 on, and
verify-genesis-payload now requires usr/sbin/dhcpcd there, so an image that ships with no
client fails the build instead of reaching a node.

genesis_dhcp_client.t drives both routines with the clients shadowed by recording stubs.
It fails on the parent commit, and deleting the dhcpcd branch turns five of its
assertions red.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit b9329998c80383bb0397d16a558f66a6bb9db399)
2026-09-04 19:26:23 -03:00
Daniel Hilst afa2d85313 test(xcat-core): capture doxcat naming dhclient on a release that drops it
doxcat names dhclient at six call sites. AlmaLinux 10 and EPEL 10 package no ISC
dhcp-client, so the legacy Genesis image for el10 carries no dhclient binary. A node
that boots that image reports "dhclient: command not found" on its console and never
acquires an address.

The test lifts the client selection out of doxcat and runs it with the clients shadowed
by stubs that record their own argv. doxcat cannot be sourced, so the routines are
extracted and driven on their own. It also reads the spec and the dracut module, which
decide what client reaches the image.

It fails on the current source for nine reasons: doxcat carries no selection routine and
no runner, it still starts command lines with dhclient and still chains into dhclient
from the secondary NIC loop, the spec build-requires no client on the releases that drop
the ISC one and does not check the payload for one, and the dracut module installs
dhclient alone.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit 5d2801f400978c07f9073c7b920f327e990be25b)
2026-09-04 19:26:15 -03:00
Daniel Hilst ff20388fed fix(xcat-core): the legacy Genesis image never reaches doxcat, so no node boots a shell
A compute node fetches the legacy Genesis kernel and initramfs, the kernel starts, and then
nothing else happens: doxcat never runs, the node acquires no address, sshd refuses every
connection and the node stays at status=powering-on. The five genesis test cases in
xCAT-test/autotest/testcase/genesis have never passed. VersatusHPC/xcat-internal#78.

Three holes in the image, each fatal on its own. dracut_105/el/xcat-cmdline.sh ends in
`while :; do tmux attach-session -t doxcat || tmux new-session -s doxcat doxcat; done`, and the
image carries no locale data, so tmux exits with "need UTF-8 locale" and the loop spins
without ever reaching doxcat. module-setup.sh does not install
/usr/libexec/openssh/sshd-session, which OpenSSH 9.8 and later exec for every connection and
which EL9 now ships. xCAT-genesis-base.spec does not BuildRequire dhcp-client, so dhclient is
absent from the build chroot; dracut_install reports the missing binary and returns, and the
module install function keeps going, so the image ships without it.

xcat-cmdline.sh now resolves xcat_console_mode() once and runs doxcat directly when the
terminal multiplexer cannot start a session; the same shape replaces the screen loop on
Ubuntu. module-setup.sh installs the OpenSSH session helpers and the C.utf8 locale where they
exist. The spec BuildRequires dhcp-client on the releases that package it, and runs the new
xCAT-genesis-builder/verify-genesis-payload over the extracted payload, which fails the build
when sshd needs a helper the image lacks, when tmux has no UTF-8 locale, or when a binary the
caller named is missing.

The same runs exposed four defects in the test cases themselves. test.sh defined its synthetic
node as ppc64le whatever the management node was, so nodeset could not find a genesis kernel on
x86_64. genesistest.pl get_os() matched neither AlmaLinux nor Rocky and reported the OS as
unsupported. The -g check read $? instead of check_genesis_file()'s return value, so it could
never fail. And testxdsh() met "REMOTE HOST IDENTIFICATION HAS CHANGED" from the second boot
on, because Genesis makes new host keys every boot and nothing dropped the stale known_hosts
entry. test.sh now derives the node arch from uname and takes the tftp root from TFTPDIR,
get_os() recognises the redhat family, report_genesis_files() carries the result to an exit
status, and forget_host_keys() runs makeknownhosts -r before each probe.

Tests: genesis_console_mode.t drives xcat_console_mode() with the multiplexer shadowed;
genesis_payload_verification.t drives the verifier over payload trees carrying each hole;
genesis_testcase_helpers.t drives get_os(), check_genesis_file(), report_genesis_files() and
testxdsh(); genesis_incorrectmasterip_check.t runs test.sh against a scratch tftp root. Each
fails on the parent commit. The verifier also reports all three holes against the released
xCAT-genesis-base-x86_64-2.19.0-snap202609021858 payload.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit cb6021eb3cdb3abc75e4dd6704cb42b28074e140)
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-04 19:16:15 -03:00
Daniel Hilst ea1cb82a6d fix(xcat-core): five check lines the xcattest harness never evaluates
Three case files ship a check line the harness cannot use. pscp/cases0 asks for
"$$CN: done" with no operator, and load_case drops a check whose content does not
start with a word character, so the two lines go without a message and the case
asserts less than it reads. rscan/cases0 writes a command as a check, and
ngpfb/cases0 compares rc against a pattern, which no operator accepts, so both cases
report "Unrecognized testcase syntax" and fail on every run.

Each line is repaired to what the case around it says it means. pscp prints
"<node>: done" for each node it copied, in xCAT-client/bin/pscp, so the two lines
become "output=~$$CN: done". rscan runs its check against the definitions that
"rscan -z -w" wrote, so "check:lsdef -l $$CN" becomes a cmd and keeps the two checks
that follow it. rmhwconn is asked for output without "state=LINE UP", which is what
the lshwconn checks in the same case assert the other way round.

xCAT-test/unit/autotest_check_lines_are_understood.t reads every check line under
xCAT-test/autotest/testcase and asserts the harness reports one result for each of
them, and that none uses an operator it does not know. Without this change it names
all five: two lines from ngpfb, one from rscan, and pscp reporting 37 results for
the 39 check lines it ships.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-04 17:08:41 -03:00
Daniel Hilst f7462389b6 fix(xcat-core): xcattest reports only the check where a case stopped
A failing case named one failed check and no result at all for the checks after it,
although the commands of the case kept running. The first [Failed] line was read as
the cause of the failure three times this week, and each time the real fault was a
later check: a riscv64 cell reported a makedns check 160 lines before rpower could
not start the domain.

run_case in xCAT-test/xcattest used one variable, $failflag, for two facts: the
result of the case, and the result of the check being reported. Every branch read
$failflag to decide whether to print [Pass] or [Failed], so a check that ran after a
failed one always read as failed. The guard "last if ($failflag)" at the top of the
check loop hid that, and hid every later check with it.

The result of a check is now $checkfail, set and read inside one iteration. A
continue block carries it into $failflag, which keeps the result of the case. The
guard and the per-branch "last" statements are gone, so each check reports what it
found. The output ~~ branch no longer clears $failflag on a match, which without the
guard would have turned a failed case into a passing one.

xCAT-test/unit/xcattest_report_every_check.t runs the harness over a fixture case and
asserts on the CHECK lines it writes. Without this change it reports two of four
checks, and one of two failed checks. A case whose checks all pass logs the same text
before and after: no truncation could happen while $failflag stayed 0.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-04 16:39:33 -03:00
Daniel Hilst 506069061b fix(xcat-core): a match made elsewhere can name a KVM volume
The name of the volume of a node, and the bus of a file-backed disk, could come from a
match made by a routine on the call path. A riscv64 node breaks on it: a leaked value
that is neither scsi nor virtio gives the node an hd* volume, and the riscv64 virt
machine has no IDE controller for that disk.

createstorage and build_diskstruct in xCAT-server/lib/xcat/plugins/kvm.pm read the model
of the disk out of the vmstorage value with s/=(.*)//, then read $1. The substitution is
allowed to fail, because most vmstorage values state no model, and a failed match leaves
$1 as the last successful capture. dohyp gives every node the storage model scsi before
mkvm runs, and a captured value takes priority over it, so a leaked value can only
replace the default that keeps a riscv64 node on sd*.

The leak follows the call path, not the history of the process. Perl restores $1 when the
block that set it ends, so a match made in a routine that has returned cannot reach
createstorage; only a match still live in an enclosing block can, and a later successful
match without a group empties $1 again. A long-running xcatd is not what makes this
happen, and looking for one is a wrong turn.

Both routines now read $1 only when their own substitution matches. A vmstorage value
that states a model, and vmstoragemodel, name the volume as before.

The default itself moves into default_storagemodel, which dohyp calls, so a test can hold
it. It sat inline with a comment, and changing it to ide left every assertion passing.

kvm_createstorage_model.t runs each node twice, once with a capture left live in the
calling block, because a case that leaves $1 empty passes against the defect. Five of its
eleven assertions fail without this change, and a sixth fails if the default changes.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-04 15:20:57 -03:00
Daniel Hilst 0a858b60f4 fix(xcat-core): a pool-backed KVM disk reaches libvirt with no bus
A node whose vmstorage is a libvirt storage pool (dir://, nfs:// or lvm://) got a <disk>
element with no bus attribute. libvirt then chose the controller from the name of the
device alone, so the disk of a riscv64 node worked only while its volume was named sd*.

build_diskstruct in xCAT-server/lib/xcat/plugins/kvm.pm matched the pool entry, a hash
reference, against /^vd/, /^hd/ and /^sd/. A reference in a match is its address as a
string, so no branch ran and the bus was never set. The name of the device is in the
device field of that entry.

The three tests now read that field. The bus each one sets is the bus libvirt gives an
hd*, sd* or vd* name, so no domain changes: a riscv64 node keeps the sd* name its volume
has, and keeps the scsi controller the riscv64 virt machine provides. libvirt stores the
domain built before this change with bus="scsi" on that disk, which is what the domain
built after it states.

kvm_diskstruct_bus.t drives build_diskstruct in a scratch package, with a stub storage
pool in place of the one routine that reaches libvirt, and asserts the bus of an hd*, an
sd* and a vd* volume. It also asserts that a riscv64 node keeps the sd* name of its
volume. Four of its seven assertions fail without this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-04 15:20:47 -03:00
Daniel Hilst 1a5192d053 fix(xcat-core): build-ubunturepo is dead code that still looks buildable
builddebs.pl replaced build-ubunturepo, and both CD pipelines prefer it: the
fallback to build-ubunturepo fires only for refs that predate builddebs.pl, and
such a ref carries its own copy. Nothing on this branch runs the script, so its
presence only invites edits that never reach a build. The developer guide said
it was kept as a differential oracle until the CD pipelines moved over. They
have.

Remove the script, and record the removal in the build guide beside the
buildcore.sh, makerpm and buildlocal.sh entries.

xcat_probe_package_payload.t asserted the Debian staging by matching a `cp -f`
line in build-ubunturepo. builddebs.pl stages the helpers through
XCAT::BuildUtils::stage_probe_helpers, so the test now calls that function and
checks the files it produced. Verified by making stage_probe_helpers skip a
helper: the assertion fails.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-04 12:24:48 -03:00
Daniel Hilst 349658d3d7 fix(xcat-core): mkvm builds a riscv64 node as an x86_64 domain
A node with arch=riscv64 got an x86_64 libvirt domain from mkvm. The node took a
DHCP lease, received the riscv64 GRUB binary that nodeset staged, and could not run
it. The firmware fell through to the empty disk and stopped, so both flat
provisioning cases of the riscv64 cell failed with a node that never installed.

build_xmldesc and build_diskstruct in xCAT-server/lib/xcat/plugins/kvm.pm read the
architecture from the hypervisor cpumodel. The arch of the node was never read while
the domain XML was built, so on an x86_64 hypervisor every guest was an x86_64
guest, whatever the node said.

guest_arch_profile now takes the arch of the node as well, and returns the domain
type, the <os> arch and machine, the firmware and the device settings that follow
from them. A riscv64 node becomes a qemu domain with the virt machine type and UEFI
firmware. It drops the parts the riscv64 virt machine has no controller for, or that
libvirt refuses there: the pae, acpi and apic features, the SeaBIOS serial option,
the ich6 sound card, the USB tablet, and the ide disk and hd* optical drive. libvirt
resolves the emulator and the UEFI firmware files itself. POWER and x86_64 domains
do not change.

kvm_guest_arch.t drives build_xmldesc and build_diskstruct in a scratch package,
stubbing only the routines that reach libvirt or the xCAT database, and asserts the
domain and the disks of each architecture. Ten of its twenty assertions fail without
this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit 8d149c856302c8016fb0ead31e9859fd5a1e9dff)
2026-09-04 12:20:34 -03:00
Daniel Hilst 2bed8fe9a1 fix(xcat-core): bound the pings that assert a purged node stopped resolving
nodepurge/cases0 asserted that testnode1 and testnode2 no longer resolve by running 'ping'
with no count and expecting a non-zero exit. When the name does not resolve the ping fails
immediately, which is the passing path -- but when it DOES resolve, which is the regression
the case exists to catch, the ping never returns. The cell stops there and is killed by the
pipeline timeout, taking the whole run's JUnit with it, so the one case that finds a real
defect is also the one that hides every other result.

Bound both with -c 1 -w 2. The assertion is unchanged: a name that does not resolve, or
resolves to something that does not answer, still exits non-zero.

autotest_ping_bounded.t goes green on this commit.

Closes #59.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit 187daabe3601dfe0448656c446994d91d8f794aa)
2026-09-04 12:20:34 -03:00
Daniel Hilst 0136c51a18 fix(mknb): a Genesis copy that fails must fail mknb
The legacy branch ran both copies and kept only the second exit status, so an unreadable
Genesis root tree left mknb exiting 0 with an initramfs built from nothing -- the node then
never boots and nothing names the cause. A failing kernel copy was reported as a failure of
the root tree, because the message was chosen from a flag set before either copy ran.

Return on the first failing copy, carrying the name of the file that could not be read.

mknb_genesis_staging.t goes from 2 failures to green on this commit.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit d46ca3e09bf52827bf0b8fa9cb94279a9da2c97b)
2026-09-04 12:20:34 -03:00
Daniel Hilst 445b755d8e test(mknb): capture a Genesis copy failing without failing mknb
mknb stages the Genesis payload before building a netboot image, and those copies are the
only point at which it learns that an installed Genesis image is unusable. The legacy branch
runs two of them and keeps only the second exit status, so an unreadable root tree is
invisible: mknb exits 0 having built an initramfs from nothing, and the node never boots
with no error naming the cause. When the kernel copy is the one that fails, the message
blames the root tree instead.

Extract the staging decision as stage_genesis_payload, preserving today's behaviour exactly,
so the outcome can be driven with an injected runner instead of a real Genesis tree.

The test fails on this commit, 2 of 10: 'an unreadable root tree fails the step' and 'the
failure names the kernel, not the root tree'.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit 989deaa31eb1f3979a8db030d3ae8c404abda2fb)
2026-09-04 12:20:34 -03:00
Daniel Hilst 97f481a570 fix(build): builddebs.pl drops riscv64, so apt serves no riscv64 xCAT
builddebs.pl replaced build-ubunturepo as the Ubuntu builder, and the arch support
riscv64 has in build-ubunturepo did not come with it. The pipeline prefers builddebs.pl
whenever the ref carries it, so on this branch the switch silently stops producing riscv64
debs: no xcat_*_riscv64.deb in the pool, and a published Release that says
'Architectures: amd64 ppc64el'. apt on a riscv64 management node then reports 'Unable to
locate package xcat', which is the same failure build-ubunturepo was fixed for.

builddebs.pl reads its architectures from BuildUtils, so unlike build-ubunturepo -- which
hardcoded the pair in three places -- riscv64 goes in one: @DEB_ARCHES.

xcat-genesis-scripts is the exception and needs its own rule. Its per-arch deb Depends on
xcat-genesis-base-<arch>, and no riscv64 genesis-base deb exists, because riscv64 takes the
OpenEmbedded Genesis image from the shared xcat-dep pool. Built for riscv64 it would be
uninstallable, so deb_package_arches excludes it.

build_utils.t covers both: removing riscv64 from @DEB_ARCHES fails four assertions,
including the reprepro Architectures line, and removing the genesis-scripts exclusion fails
its own.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit 90f9156411b11b33496a470574f37fcdc2a7330f)
2026-09-04 12:20:34 -03:00
Daniel Hilst 1cffcec1cf fix(xcat-core): build the xCAT debs for riscv64
apt on a riscv64 Ubuntu management node cannot find xCAT at all:

  E: Unable to locate package xcat

xCAT/debian/control and xCATsn/debian/control list "Architecture: amd64 ppc64el",
so the build produces no riscv64 deb and the published apt repository serves only
those two architectures. Everything else riscv64 needs is already in the tree --
the rocky10/rhels10 riscv64 install templates, the grub2 boot policy, mknb, the
OpenEmbedded Genesis machine -- and the xcat-dep riscv64 repository is built,
signed and complete; only the core packages are missing.

Add riscv64 to both lists.

xCAT-test/unit/debian_control_arch_coverage.t covers this: it fails on both files
without the change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit 13492f55c32b34597b24934079d35789b4cc57c0)
2026-09-04 12:20:34 -03:00
Daniel Hilst f7d52f94ec test(xcat-core): capture the Debian control files excluding riscv64
Installing xCAT on a riscv64 Ubuntu management node fails before it starts:

  E: Unable to locate package xcat
  E: Unable to locate package xcat-test

xCAT/debian/control and xCATsn/debian/control name their architectures
explicitly, as "amd64 ppc64el". riscv64 is absent, so no riscv64 deb is ever
produced and apt has nothing to install -- while the rest of the tree already
carries riscv64 install templates, DHCP boot policy, mknb support and a Genesis
machine configuration.

The test reads both control files and asserts the explicit list covers every
Debian architecture xCAT ships. It fails on both files today.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit 059c23486d8f15e8a0a224ef7dfe05cd7fe123a4)
2026-09-04 12:20:34 -03:00
Daniel Hilst 11da82dbba fix(xcat-core): the genesis specs have no riscv64 branch, so %{tarch} stays literal
A riscv64 build of xcat-core dies at xCAT-genesis-scripts with "Cannot find/open
srpm: ...xCAT-genesis-scripts-riscv64-2.19.0-<release>.src.rpm", because the srpm
rpm produced is named xCAT-genesis-scripts-%{tarch}-2.19.0-<release>.src.rpm.

xCAT-genesis-scripts.spec and xCAT-genesis-base.spec take their package name from
%{tarch}, which an %ifarch ladder sets for x86, x86_64, ppc64 and aarch64. riscv64
is absent, so rpm leaves the macro unexpanded and builds a package whose NAME
contains it. buildrpms.pl then looks for the name it asked for and cannot find it.

Add the riscv64 branch to both specs. An arch that is still missing from the
ladder now stops the build with %{error:} instead of naming a package after a
macro.

xCAT-test/unit/genesis_spec_target_arch.t covers this: it fails on riscv64
without this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit c9de89ccceaaa618f9e91568d74812b349c513ac)
2026-09-04 12:20:34 -03:00
Daniel Hilst 3992a3c9c4 test(xcat-core): capture the genesis specs leaving %{tarch} unexpanded
A riscv64 build of xcat-core fails at xCAT-genesis-scripts:

  ERROR: Cannot find/open srpm: dist/rocky-10-riscv64-xcat/rpms/SRPMS/
  xCAT-genesis-scripts-riscv64-2.19.0-snap202609020458.src.rpm

The srpm on disk is named xCAT-genesis-scripts-%{tarch}-2.19.0-....src.rpm.
xCAT-genesis-scripts.spec and xCAT-genesis-base.spec take the package name from
%{tarch}, which an %ifarch ladder sets. That ladder has no riscv64 branch, so
%{tarch} stays literal and rpm builds a package with a macro in its name.

The test expands both specs with rpmspec for every arch xCAT supports and
asserts the Name carries that arch. It fails on riscv64 for both specs.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
(cherry picked from commit 07a0e79e42ec39eaf1ed6e9291ab6b3f6f869e97)
2026-09-04 12:20:34 -03:00
Daniel Hilst fd580b901f Merge pull request #7817 from VersatusHPC/refactor/debian-arch-map
refactor(debian): map media architectures through a shared table
2026-09-04 11:07:16 -03:00
Daniel Hilst e08fe95959 Merge pull request #7799 from VersatusHPC/fix/ipmi-ipv4-literal-guardrail
fix(ipmi): reject ambiguous IPv4 literals
2026-09-03 20:27:14 -03:00
Vinícius Ferrão ca2aec5397 test(xCAT-test): cover the debootstrap architecture genimage passes
Drive the assignment genimage makes and check the name debootstrap receives for
each architecture, including the POWER LE spelling that debootstrap rejects.
2026-09-03 19:57:11 -03:00
Vinícius Ferrão 1efeef4895 refactor(genimage): take the debootstrap architecture from the shared mapping
genimage translated one architecture for debootstrap, x86_64 to amd64, and
compared against a bareword rather than a string, which only resolves because
the script does not enable strict subs.

Read the name from xCAT::Utils, which genimage already loads. Every
architecture reaches debootstrap with the name it does today.
2026-09-03 19:44:14 -03:00
Vinícius Ferrão e982e16181 test(xCAT-test): cover the Debian architecture mapping
Pin both directions for every architecture xCAT supports on Ubuntu, the
pass-through for names Debian shares, and the round trip that copycd and
debootstrap depend on agreeing about.
2026-09-03 19:44:13 -03:00
Vinícius Ferrão bf56116732 refactor(debian): map media architectures through a shared table
copycd translated the architecture the Ubuntu media reports with its own
if/elsif chain, and genimage translates the same names back for debootstrap with
another one. Neither can be reused, so a new architecture has to be added to
both.

Put both directions in xCAT::Utils and have copycd read from there. The names
and the fallback do not change: media that xCAT has no name for still leave the
architecture as the media reported it.
2026-09-03 19:44:13 -03:00
Daniel Hilst 9f4e53e380 Merge pull request #7800 from VersatusHPC/refactor/dhcp-omapi-command-runner
refactor(dhcp): share OMAPI command runner
2026-09-03 19:34:10 -03:00
Daniel Hilst 994601c4d6 Merge pull request #7815 from VersatusHPC/refactor/ubuntu-media-kernel-probe
refactor(debian): resolve the install kernel and initrd from a table
2026-09-03 19:32:59 -03:00
Vinícius Ferrão b97c6feb2d test(xCAT-test): cover the install kernel and initrd resolution
Build each Ubuntu media layout on disk and ask the resolver for its kernel and
initrd: the netboot trees, the flat netboot layout, both casper images, and the
POWER layouts where the kernel and the initrd sit in different directories.

Pin the precedence the installer depends on, a netboot tree over a live image
and a hardware-enablement kernel over the release one, and pin the three ways
media resolve to nothing.
2026-09-03 18:49:07 -03:00
Vinícius Ferrão 4312fe8337 refactor(debian): resolve the install kernel and initrd from a table
The probe spelled out every candidate path twice inside one nested condition,
once to test it and once to assign it, so adding an architecture meant adding
another branch of the same shape. Move the candidates into a table keyed by
architecture family and walk it in order.

Same paths, same precedence, same failure behaviour: a media tree that matches
nothing leaves the caller on the "install image not found" path as before.
2026-09-03 18:49:07 -03:00
Vinícius Ferrão 95defee20b test(dhcp): exercise source-owned OMAPI behavior
Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com>
2026-09-03 18:14:00 -03:00
Vinícius Ferrão 1a2bbda692 test(dhcp): verify OMAPI writer flush ordering
Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com>
2026-09-03 18:14:00 -03:00
Vinícius Ferrão 3836ae937e test(dhcp): cover OMAPI inherited handle safety
Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com>
2026-09-03 18:13:59 -03:00
Vinícius Ferrão b2b188302a fix(dhcp): protect inherited handles in OMAPI child
Use xCAT::Utils->xfork so child-side setup or exec failures do not destroy database handles inherited from the caller.

Signed-off-by: Vinícius Ferrão <2031761+viniciusferrao@users.noreply.github.com>
2026-09-03 18:13:59 -03:00
Vinícius Ferrão 9910f4b92f test(dhcp): cover OMAPI command runner 2026-09-03 18:13:59 -03:00
Vinícius Ferrão d1bd0fe576 refactor(dhcp): share OMAPI command runner 2026-09-03 18:13:59 -03:00
Daniel Hilst 362bf5eb9f Merge pull request #7760 from VersatusHPC/fix/ubuntu-mn-ntp-daemon
fix(xcat-core): makentp fails on a stock Ubuntu MN (timesyncd cannot serve time)
2026-09-03 17:54:02 -03:00
Daniel Hilst 7003e0c0b6 Merge pull request #7761 from VersatusHPC/fix/ubuntu-subiquity-diskful-install
fix(xcat-core): the Ubuntu Subiquity diskful install never completes
2026-09-03 17:51:50 -03:00
Daniel Hilst a3e16d83f9 fix(networkutils): OnlyV4 is answered from the cache whatever family it holds
getipaddr returns a cached address before it resolves, and the bypass tests
OnlyV6 and GetAllAddresses only. A caller asking OnlyV4 therefore receives
whatever the first lookup stored. An unrestricted lookup asks for AF_UNSPEC, so
on a dual-stack management node it stores the AAAA record, and %::hostiphash is
a global in a long-lived xcatd.

debian.pm resolves the install server with OnlyV4 and writes
nfsroot=<address>:/install. Given the cached IPv6 that renders
nfsroot=2001:db8::1:/install, which does not parse, so the Subiquity installer
never mounts and the node never completes. dhcp.pm and mknb.pm hold four more
OnlyV4 callers with the same exposure.

Skip the cache when OnlyV4 is asked for and the cached address is IPv6. The
lookup that follows requests AF_INET and replaces the entry with the IPv4
address, so the next caller of either kind gets a usable one. An IPv4 entry is
still served from the cache: the bypass is about the family of the cached
answer, not about disabling the cache for OnlyV4.

networkutils_getipaddr_onlyv4.t fails without this change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 17:17:04 -03:00
Daniel Hilst 2b6337d67d test(networkutils): capture OnlyV4 being answered with a cached IPv6 address
getipaddr answers from %::hostiphash before it resolves. The bypass tests
OnlyV6 and GetAllAddresses and does not test OnlyV4, so a caller that asks for
IPv4 is handed whatever the first lookup cached.

An unrestricted lookup passes AF_UNSPEC to getaddrinfo, so on a dual-stack
management node with an AAAA record it caches the IPv6 address. xcatd is
long-lived and the hash is a global, so one earlier caller poisons every OnlyV4
caller after it.

debian.pm then writes nfsroot=2001:db8::1:/install, which is not a parseable
nfsroot, and the Subiquity install never mounts. dhcp.pm and mknb.pm hold four
more OnlyV4 callers with the same exposure.

The test also pins what the fix must not break: an IPv4 cache entry is still
served to an OnlyV4 caller, and an unrestricted caller still gets its cache hit
whatever family it holds.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 17:14:28 -03:00
Daniel Hilst efc3f53dbc Merge pull request #7759 from VersatusHPC/fix/xcatd-respawn-install-monitor
fix(xcat-core): a dead xcatd install monitor never comes back
2026-09-03 16:58:01 -03:00
Daniel Hilst a82d77fbc4 Merge pull request #7758 from VersatusHPC/fix/makedhcp-ubuntu-backend-and-query
fix(dhcp): makedhcp fails on a stock Ubuntu MN, and host-block scans match the wrong node
2026-09-03 16:57:42 -03:00
Daniel Hilst 3290358e48 Merge pull request #7726 from VersatusHPC/fix/remoteshell-sshd-config-drop-in
fix(remoteshell): stop rewriting the administrator's sshd_config
2026-09-03 14:55:51 -03:00
Daniel Hilst 39eb6ce532 Merge pull request #7767 from VersatusHPC/refactor/commandutils-executable-finder
refactor(utils): centralize executable lookup
2026-09-03 14:55:14 -03:00
Daniel Hilst eff0399a7b Merge pull request #7794 from VersatusHPC/refactor/ipmi-ipv4-command-encoding
fix(ipmi): centralize IPv4 command encoding
2026-09-03 14:53:21 -03:00
Daniel Hilst 05fc81f7b5 fix(subiquity): three values the Ubuntu install path accepts and cannot use
The boot flip in compute.subiquity.tmpl addressed port 3002. xcatd's install
monitor listens on site.xcatiport, so a cluster that moves the port loses the
flip and every node PXE-loops back into the installer. The flip now reads
site.xcatiport and keeps 3002 as the default. TABLEBLANKOKAY, because the key is
optional and a plain TABLE lookup of an absent key fails the whole template.

The flip also counted any reply as an accepted request. It now requires the
monitor's "ready" greeting before it sends "next", and "done" afterwards, so a
different service on that port is not read as a flipped node.

subiquity_nfsroot_server in debian.pm called getipaddr without a family. A
dual-stack management node answers with its IPv6 address, and casper takes
everything after the first colon in nfsroot= as the path, so the live filesystem
never mounts. It now asks for IPv4, as dhcp.pm and mknb.pm do.

The DNS setup wrote the xcatmaster name as a nameserver when getent found no
address, which is the case the step exists to prevent. It now keeps the
resolv.conf DHCP gave the live installer.

ubuntu_subiquity_boot_flip.t, debian_subiquity_boot_params.t and
ubuntu_resolvconf_ip.t fail on the parent commit and pass here.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-03 14:52:49 -03:00