2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-09-05 04:27:55 +00:00
Commit Graph

222 Commits

Author SHA1 Message Date
Daniel Hilst a07b1f8ae4 test(postage): nothing covers makescript's syncfiles deferral call site
defer_syncfiles_to_postboot has tests, but deleting the whole block out of
makescript -- the provmethod override and the call -- left the entire unit
suite green. That is precisely how the wrong deletion shipped two commits ago:
the override was removed on the mistaken grounds that %image_hash never carries
a provmethod, and nothing noticed.

makescript needs a management node and a database, so the block is lifted out
and eval'd into a scratch package, driven with the hash makescript actually
builds. What it pins is the resolution -- an osimage NAME becoming the
osimage's real provmethod -- and that the resolved value is what reaches the
helper.

Verified by mutation rather than by shape: an override that is present but
never fires reddens 1-3, and passing $provmethod instead of
$effective_provmethod reddens 2-3. Deleting the block, or the override, trips
the extraction guard instead, which is a loud failure rather than a silent
pass.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-01 23:02:17 -03:00
Daniel Hilst abc45b1f74 fix(postage): restore the provmethod override, and cover mkinstall's call site
The previous commit deleted the $effective_provmethod override on the grounds
that %image_hash never carries a provmethod. That was wrong, and the review
caught it: makescript builds %image_hash, calls getImage() on it, and then
hands the SAME hashref to getScripts(), which fills provmethod for every
osimage from the osimage table. getDisklessNet() already reads that key the
same way. The override was live, not dead.

Restore it and say what is actually true in the comment. nodetype.provmethod is
frequently an osimage name rather than 'install', and resolving it is the point
of the lookup.

Also close the gap that made the wrong deletion so easy to ship: reverting
mkinstall's subiquity branch to its pre-fix body left the whole unit suite
green. debian_mkinstall_subiquity_branch.t lifts that branch out and drives it
inside a real loop, so the `next` it performs is the one under test, with
report_node_error and the getipaddr seam stood in for. It calls
subiquity_boot_params with no injected resolver, exactly as production does.

The branch is selected out of debian.pm by what it contains rather than by
where it sits -- there are four `if (using_subiquity(...))` in that file, and an
earlier draft of this test silently matched the wrong one and ran past its
block.

Now observable, each verified by mutation: swapping $pkgdir and $httpport at
the call site reddens the nfsroot assertion; reverting the branch wholesale
fails the extraction guard rather than passing.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-01 22:28:44 -03:00
Daniel Hilst 73ebe96f72 fix(postage): the provmethod override in makescript can never fire
The syncfiles deferral resolved the node's provmethod through
$image_hash{$osimgname}{provmethod} when the node names an osimage. makescript
fills %image_hash from getImage(), which stores pkglist, pkgdir, otherpkglist,
otherpkgdir and environvar -- and no provmethod. getScripts() has a separate
hash that does store one, which is where the pattern was copied from. So the
lookup was always undef, the override never fired, and the code claimed a
behaviour it did not have.

Pass $provmethod directly and say in the comment why there is nothing to
resolve it with. No behaviour changes -- the branch was inert -- so there is no
red to show first; what the deletion needs is coverage that the path it was
supposed to serve still works.

That is what the two new assertions do: an osimage-named provmethod with
nodesetstate 'install' still defers, and the same name with no nodesetstate is
not mistaken for a diskful install. nodesetstate is what carries the install
signal here, which is why the override was never load-bearing. Making the
deferral ignore nodesetstate and require provmethod eq 'install' reddens both.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-01 21:48:54 -03:00
Daniel Hilst b784aa782c fix(debian): connect the subiquity helpers to mkinstall, and close the sandbox guard
Two things the review found, both in code added by this branch.

Reverting mkinstall's call site -- putting xCAT::NetworkUtils->getipaddr back
in place of subiquity_nfsroot_server, the exact regression the fix removes --
left the entire unit suite green. The helpers were covered; nothing linked them
to production. Compose the two steps in subiquity_boot_params(), which takes its
inputs and returns either a command line or the reason there isn't one, so the
composition can be driven; mkinstall keeps report_node_error and the loop's
`next`. That same revert now reddens 6 of 9 assertions.

The test stubs xCAT::NetworkUtils::getipaddr deliberately. Without it a call
site that bypassed the injected resolver died on a missing module -- a red, but
for the wrong reason. With it, bypassing the resolver returns the wrong answer,
which is what the assertions are there to catch.

The resolv.conf sandbox guard matched `/etc/` with a trailing slash, so the one
respelling its own comment names -- `etcdir=/etc; rm -f "$etcdir/resolv.conf"`
-- walked straight past it and the fragment would rm the runner's real
resolv.conf, as root in CI. `/etc\b` catches it: applying that respelling now
BAIL_OUTs instead of running.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-01 21:09:31 -03:00
Daniel Hilst da0bfdbd22 test(debian): nothing connects the subiquity helpers to mkinstall
subiquity_nfsroot_server and subiquity_kcmdline each have tests, but reverting
the call site in mkinstall -- putting xCAT::NetworkUtils->getipaddr back in
place of subiquity_nfsroot_server, which is precisely the regression the fix
removes -- leaves the whole unit suite green. A helper can be perfectly covered
while nothing links it to production, and that is the shape the review found.

mkinstall needs a management node, so this drives the composition it performs:
resolve the install server, then build the command line, or explain why not.
It fails at the extraction guard until that composition is a routine that can
be called, so the behavioural proof is the mutation on top of the fix, not this
red alone.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-01 21:05:20 -03:00
Daniel Hilst 1cebb4d9e8 test(subiquity): the resolv.conf sandbox fails open, as root
ubuntu_resolvconf_ip.t sandboxes the fragment by rewriting /etc/resolv.conf to
a path inside a tempdir. The fragment contains `rm -f /etc/resolv.conf` and the
unit suite runs as root in CI, so if that substitution ever stops matching the
test deletes the runner's resolver configuration instead of failing.

It matches today. It is one respelling away from not: writing the path in the
template as `etcdir=/etc; rm -f "$etcdir/resolv.conf"` slips straight past it,
and the existing BAIL_OUT does not catch that -- it guards only the fragment
extraction, not the rewrite.

Check the rewritten script for any /etc path outside the scratch tree and
BAIL_OUT rather than execute it. AGENTS.md asks that a test never escape its
scratch tree and notes that rewriting paths in the source under test is the
fragile way to arrange it; this makes the fragile part fail closed.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-01 18:51:05 -03:00
Daniel Hilst 7082fabe0b test(subiquity): the boot-flip test skips wherever the suite runs
ubuntu_subiquity_boot_flip.t bound 3002 on the loopback to check it was free
and skip_all'd when it was not. 3002 is the install-monitor port, so on any
management node xcatd is already listening there -- which is precisely where
the suite runs. The CI log for #7761 reads

    ubuntu_subiquity_boot_flip.t ... skipped: port 3002 is not available on
    the loopback interface

so all 139 lines and 12 assertions never executed once, and never would have.
Coverage that reports as a skip is worse than none, because the gap is
invisible.

Take an ephemeral port from the kernel and rewrite the extracted command to use
it, in both the /dev/tcp target and the log message. The port number is not what
is under test -- the retry-and-log behaviour is.

The template's own port is read out of the command rather than hard-coded, and
asserted to be 3002, so moving the install-monitor still leaves the file
covering something instead of silently testing a port nothing uses.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-01 18:50:31 -03:00
Daniel Hilst 2558e84736 test(debian): nothing catches !myipfn! being treated as a hostname
A node whose noderes.xcatmaster is unset gets $instserver = '!myipfn!'. That is
a placeholder, not a name: pxe.pm:176 and grub2.pm:129 substitute it with
my_ip_facing($node) -- an address -- when they write the boot config. The
subiquity path resolves $instserver with getipaddr, which returns undef for the
placeholder, so it reports "Could not resolve the install server" and `next`s
past the node. Before this branch it produced exactly the numeric nfsroot the
change is trying to guarantee.

anaconda.pm and sles.pm both guard the same placeholder with
`unless ($instserver eq '!myipfn!')`. noderes.5.rst:125 documents an unset
xcatmaster as supported.

No pipeline can catch it: reg_linux_diskfull_installation_flat chdefs
xcatmaster=$$MN and no ci/conf/pipelines/*.conf leaves it unset, so CI always
takes the resolvable branch.

This drives the decision with an injected resolver, so it also checks the
resolver is never ASKED about the placeholder rather than only checking the
return value. It fails at the extraction guard until the routine exists, so the
behavioural proof is the mutation on top of the fix, not this red alone.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-01 18:47:10 -03:00
Daniel Hilst bca12ca29d fix(xcat-core): bound the boot-flip exchange, and fail when the install server will not resolve
Two ways the Subiquity install could fail without saying anything useful.

The boot flip read from the install monitor with no timeout. A monitor that accepts the
connection and then never answers -- which #7759 shows is a state it gets into -- blocked the read
forever, and with it the Subiquity late-command and the install. The retry loop could not help:
it never reached the retry. Both reads now take -t 10, so five attempts are bounded at roughly two
minutes and end in the failure that is already logged. The regression test stands up a listener
that accepts and holds the connection; removing the timeouts fails it.

mkinstall resolved the install server for nfsroot and fell back to the name when that failed:
"getipaddr($instserver) || $instserver". The name is the original defect -- klibc's nfsmount
cannot resolve one -- so the node panicked "can't parse IP address" at boot, on the node, with
nothing reported on the management node. The management node knows at template time, so it says
so there and skips the node, as the other unrecoverable per-node conditions in this routine do.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-08-28 10:51:55 -03:00
Daniel Hilst 0f12faa0fc fix(xcat-core): do not add a second syncfiles when the node already defers it
A node may list syncfiles in both postscripts and postbootscripts -- once for the install and
once for the booted node. The deferral prepended its own copy regardless, so the booted node ran
syncfiles twice in a row inside a marker block that made it look like xCAT had generated the
duplicate.

Keep the entry the node already has and still remove the install-time one, which is the copy
that cannot work in the in-target chroot.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-08-28 09:45:56 -03:00
Daniel Hilst a0c8f7eae7 fix(xcat-core): trim the commentary around the Subiquity diskful fix
Three passes of the same reasoning had accumulated: in the code, in the POD of the routines the
previous commit extracted, and again in the test headers. Say each once, where the reader needs it.

The POD blocks were the worst of it -- extracting subiquity_kcmdline() and
defer_syncfiles_to_postboot() moved the essays out of the routines but did not shorten them. The
toram paragraph ran eleven lines for one kernel argument; the exact systemd-shutdown message and
the size of the 24.04 layers are colour, not reasoning.

The template's two comments and the apt-sources pair are trimmed the same way: the sources.list
explanation lives in Template.pm, and the test says which case it is checking and points there.

That the tests still pass after rewriting the template is the point of the previous commit -- a
test matching its text would have broken here.

47 comment and POD lines removed, no behaviour change.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-08-27 16:00:57 -03:00
Daniel Hilst 00ef09add2 test(xcat-core): assert the Subiquity install behaviour instead of the source that implements it
The tests added with this fix matched regexes against the text of debian.pm, Postage.pm and
compute.subiquity.tmpl. A source match cannot tell whether the code it found ever runs: moving
the Subiquity command line into its own routine leaves every one of those assertions passing,
and reformatting a line fails them while the behaviour is untouched. One of them pinned Perl
syntax outright, qr/\$kcmdline\s*\.=\s*" ---";/, and another matched the text of a substitution.

Run the code instead.

Two decisions were lifted out of the routines that had grown around them, so a test can call
them: subiquity_kcmdline() in debian.pm builds the installer command line from its inputs, and
defer_syncfiles_to_postboot() in Postage.pm returns the adjusted postscript lists. Both are pure
and carry the reasoning that used to sit inline. The callers keep their behaviour exactly.

The template's two shell fragments are extracted and executed: the boot flip runs against a
stand-in for xcatd on the install-monitor port, and the resolv.conf step runs with a getent that
answers as the case requires.

Every assertion now fails when the behaviour it describes is removed, which is what the source
matches only appeared to do:

  boot=casper dropped from the command line     1 assertion fails
  toram dropped                                 1 assertion fails
  nfsroot given the host name instead of the IP 2 assertions fail
  the deferral made a no-op                     7 assertions fail
  the wrong token sent to xcatd                 1 assertion fails
  the failed-flip log line removed              3 assertions fail
  the retry loop reduced to one attempt         2 assertions fail
  resolv.conf given the host name               3 assertions fail
  the resolution fallback removed               2 assertions fail

ubuntu_subiquity_bootparams.t is removed. Its three matches are covered by execution in
debian_subiquity_netboot.t, and it demonstrated the problem: it still passed after the command
line moved into another routine.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-08-26 17:31:46 -03:00
Daniel Hilst d8a01bcf15 test(xcat-core): capture the Ubuntu Subiquity diskful install never completing
The gating reg_linux_diskfull_installation_flat case cannot pass on Ubuntu: the
compute node never reaches a booted, installed OS, and the case only ever reports
"ssh: connect ... port 22: Connection refused". It is not one defect but a chain, each
reachable only once the one before it is fixed.

The installer never boots. Without boot=casper, casper never processes netboot=nfs --
it scans the local disks, finds no live media and panics "Unable to find a medium
containing a live file system". nfsroot must also be a literal IP: casper mounts the
live filesystem with klibc's nfsmount, which cannot resolve a hostname. And with the
NFS root still mounted at end of install, systemd-shutdown blocks forever on an
lvm/pvscan wedged in uninterruptible I/O on it, so the node never power-cycles into
the disk it just installed.

The installer has no usable DNS. A nameserver line in /etc/resolv.conf must hold an IP
-- glibc's resolver discards a hostname written there -- so writing the xcatmaster name
leaves the installer, and the in-target apt that inherits the file, hanging on
archive.ubuntu.com.

In-target apt cannot find its packages on classic-sources releases, because Subiquity
renders the target sources.list from the install media alone.

The node never leaves the installer. The boot flip to local disk goes through
updateflag.awk, which needs gawk's |& /inet coprocess, but Ubuntu's /usr/bin/awk is
mawk -- so the flip fails silently and the node reinstalls forever.

And syncfiles runs inside the in-target chroot, asking the MN to scp files into a node
that has no sshd yet, so it times out and the node reports failed on a good install.

Cover each stage. All fail today.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-08-24 15:41:32 -03:00
Daniel Hilst f2f96b67fc Merge pull request #7728 from VersatusHPC/fix/xml-external-entity
fix(xcatd): block XML external entities on the legacy parser path
2026-08-24 14:23:48 -03:00
Daniel Hilst 5ca148889c Merge pull request #7749 from VersatusHPC/fix/nodestat-usefping-option
fix(nodestat): accept the fping option that the usage message gives
2026-08-24 12:42:21 -03:00
Daniel Hilst bcf6f9059a Merge pull request #7750 from VersatusHPC/fix/dbobjutils-exact-only-if-values
fix(dbobjutils): match exact only-if values
2026-08-24 12:39:34 -03:00
Daniel Hilst 14feebce2f Merge pull request #7753 from VersatusHPC/fix/genesis-lzma-via-xz
fix(mknb): compress the genesis image with xz when lzma is absent
2026-08-24 12:36:26 -03:00
Daniel Hilst ca5d1cfa86 Merge pull request #7751 from VersatusHPC/refactor/dbobjutils-remove-legacy-group-matcher
refactor(dbobjutils): remove redundant group matcher
2026-08-24 12:29:02 -03:00
Daniel Hilst 8c3aaa4471 Merge pull request #7727 from VersatusHPC/refactor/kea-shared-service-mapping
refactor(kea): reuse shared service mapping
2026-08-24 12:27:38 -03:00
Daniel Hilst a7f4c770b5 Merge pull request #7631 from VersatusHPC/refactor/ipmi-rmcp-response-identity
refactor(ipmi): centralize RMCP response identity check
2026-08-24 12:27:17 -03:00
Vinícius Ferrão eaa1e94a32 test(nodestat): pin the fping option and the usemon abbreviations
Add a unit test for the option that selects fping instead of nmap.

The test takes the specification out of the plugin source and gives it to
Getopt::Long with the settings that the daemon uses, so it drives the
specification that the plugin ships.

It shows that -f, --usefping and the older --useping each select fping, that
--use and --us still select usemon and do not select fping, that the bundles
-mf and -fm select both options, and that both places parse through the one
specification.
2026-08-23 22:38:41 -03:00
Vinícius Ferrão 73b145d7ff test(mknb): pin which program compresses the genesis image
Add a unit test for the routine that chooses the compression program. The
test lifts the routine out of the plugin source, because the plugin needs a
management node to load.

The test shows that lzma is used when it is there, that xz stands in when it
is not, and that xz is asked for the lzma container rather than its own. It
also shows that the caller takes the command from the routine, that the file
keeps its name and its suffix, and that the gzip fallback and the rename into
place both remain.
2026-08-23 22:38:41 -03:00
Vinícius Ferrão d3d0f86abf test(network): cover shared netmask behavior 2026-08-23 13:51:39 -03:00
Vinícius Ferrão 7afa152f8d test(dhcp): prepare shared network helper stubs 2026-08-23 13:49:15 -03:00
Vinícius Ferrão 1e8917ebe0 test(dbobjutils): guard only-if routing invariants 2026-08-23 13:13:16 -03:00
Vinícius Ferrão 21755f8f93 test(dbobjutils): cover exact only-if value matching 2026-08-23 11:38:55 -03:00
Vinícius Ferrão 5d39fc30d4 test(utils): cover comma-list membership 2026-08-23 11:08:59 -03:00
Daniel Hilst 7733d16f8d Merge pull request #7721 from VersatusHPC/feature/genesis-openembedded
feat(genesis): build images with OpenEmbedded
2026-08-21 20:36:20 -03:00
Vinícius Ferrão 9da2d71b90 test(genesis): cover legacy destiny retries 2026-08-21 20:11:31 -03:00
Vinícius Ferrão c6c11d70a1 test(genesis): cover the action boot gate 2026-08-21 02:13:19 -03:00
Vinícius Ferrão 4c50b4da50 test(genesis): cover disappearing devices 2026-08-21 02:12:56 -03:00
Vinícius Ferrão 53c8538e4a test(genesis): cover logging failures 2026-08-21 02:12:22 -03:00
Vinícius Ferrão a47c7f8e7c test(genesis): cover long kernel command lines 2026-08-21 02:10:23 -03:00
Vinícius Ferrão 492f9171a2 test(getinstdisk): cover the driver group against the identifier
Cover a RAID volume that reports a WWN against a direct attached disk
that reports none, in both scan orders, which the previous readback
decided by identifier. Keep the identifier rules of one group under
test as well: the disk that reports a WWN wins, the lower WWN wins
between two, and a path wins over no identifier at all.
2026-08-21 01:13:16 -03:00
Vinícius Ferrão bbd1a7e9d3 test(getinstdisk): cover the Xen disk scan
Cover a guest whose only disk is a Xen disk, which the scan has to
select rather than leave to the fallback, and a guest with two Xen
disks, where the driver group decides. Against the previous filter both
cases fail.
2026-08-21 01:13:15 -03:00
Vinícius Ferrão 7e8994e1b9 test(getinstdisk): pin the single script layout
Assert that the RHEL 10 copy is gone, that the RHEL 10 installer
includes the common script, and that the common script keeps the VROC
fallback, the Xen fallback and the guarded failure log.
2026-08-21 01:13:15 -03:00
Vinícius Ferrão 046c97ba2a test(getinstdisk): target the common script only
The RHEL 10 copy of the script is about to go away, so stop naming it
here first. The cases keep running against the common script, so the
coverage does not change.
2026-08-21 01:13:15 -03:00
Vinícius Ferrão 59219181c9 test(genesis): use Yocto extension filenames 2026-08-20 23:23:19 -03:00
Vinícius Ferrão baa68945cc test(genesis): use Yocto release filenames 2026-08-20 23:21:39 -03:00
Vinícius Ferrão cba24cc4c0 test(genesis): reject unsafe network state 2026-08-20 23:07:31 -03:00
Vinícius Ferrão cef35ba5e1 test(genesis): cover signed extension bundles 2026-08-20 23:03:53 -03:00
Vinícius Ferrão 56040cb4f0 test(genesis): cover release compliance output 2026-08-20 23:00:15 -03:00
Vinícius Ferrão 321595fb62 test(getinstdisk): cover the install disk selection order
Run the real scripts in a sandbox. A stub udevadm serves the device
properties from fixture files, and the partition list and the output
paths move into the sandbox. Every case runs against the common script
and against the copy the RHEL 10 installer includes.

Cover the direct attached disk against a RAID volume, a RAID only
server, the host adapter against a direct attached disk and against an
unknown driver, an NVMe device from the last group, and the default
fallback. Against the previous scripts the RAID cases fail, so they
discriminate.
2026-08-20 22:59:11 -03:00
Vinícius Ferrão a903a12463 test(genesis): cover sequential BMC setup 2026-08-20 22:56:07 -03:00
Vinícius Ferrão 43ca1c6363 test(genesis): cover fatal action service policy 2026-08-20 22:52:44 -03:00
Vinícius Ferrão c0af2535f8 test(genesis): cover network refresh rollback 2026-08-20 22:48:13 -03:00
Vinícius Ferrão 63f2326b6e test(genesis): cover BMCs without SOL 2026-08-20 22:43:13 -03:00
Vinícius Ferrão 0da0828913 test(genesis): cover narrow console headers 2026-08-20 22:41:53 -03:00
Vinícius Ferrão 8442dba2bf test(genesis): cover destiny client termination 2026-08-20 22:40:37 -03:00
Vinícius Ferrão 6562a4e9d7 test(genesis): cover export manifest 2026-08-20 22:29:57 -03:00