Hoisting the systemd-timesyncd stop/disable above the ntpd hand-off moved it
inside the window makentp_ntp_deps.t extracts as $select, and that harness
stubbed only logger and check_executes. So the extracted region called the real
systemctl -- and the unit suite runs as root in CI. Probed with a recording
systemctl first on PATH, `prove makentp_ntp_deps.t` made 12 real invocations,
stop and disable for each of the six selection cases; on the Ubuntu review MN a
stand-in timesyncd unit went from enabled/active to disabled/inactive while the
suite reported PASS.
That is the "never let a test escape its scratch tree" rule, and it failed
silently: the assertions passed either way, so nothing said the host had been
changed.
Shadow every command the region can reach, not only the ones it reached when
this was written -- the region moves. Bash resolves functions ahead of $PATH,
so these win without touching PATH.
The stubs use echo rather than printf '...': the harness runs `bash -c '...'`,
so a single quote inside the prelude closes that string early. The existing
logger stub gets away with it; three more did not, and the ntpd selection cases
failed until they were rewritten.
Verified after: zero real systemctl invocations from the whole unit suite under
the same probe, and 18/18 still pass.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
The stop/disable sat below the `exec setupntp.traditional` that the ntpd path
takes, and setupntp.traditional never mentions timesyncd, so on that path it
was never reached: ntpd and timesyncd both ended up disciplining the clock.
Hoist it above the hand-off so both backends get it, guarded on systemctl
existing -- the ntpd path is taken precisely when systemctl may be absent.
The comment justifying it was also wrong, and is corrected: chrony and ntpsec
both Conflicts: time-daemon, so installing either already displaces timesyncd.
That only helps when an install actually happens; on a re-run, or where the
daemon was already present, timesyncd is still enabled, which is what this
covers.
The four timesyncd assertions in makentp_ntp_deps.t are dropped rather than
repaired: they ran over the section from `check_exec_or_exit cp cat logger grep`
onwards, which only the chrony path reaches, so the code they covered is no
longer in their window. setupntp_timesyncd_both_backends.t drives both paths
instead.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
setupntp stops and disables systemd-timesyncd so it does not discipline the
clock against the daemon being configured. That block sits ~24 lines below the
`exec setupntp.traditional` the ntpd path takes, and setupntp.traditional never
mentions timesyncd -- so on the ntpd path the disable is never reached and both
end up stepping the clock.
setupntp resets PATH at the top, so PATH stubs cannot shadow anything in it;
the region is extracted and driven with systemctl shadowed by a shell function,
which bash resolves ahead of PATH. `exec` is a builtin and cannot be shadowed,
so the harness puts a recording stand-in where the script execs -- the run
ending there is the behaviour under test.
Five of the eight assertions pass already, pinning the chrony path and the
hand-off itself. The three that fail are the ntpd path.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
The NTP backend selector was well covered and nothing connected it to makentp:
copying the base makentp.pm over the head one left the whole unit suite
byte-identical, so the branches that consume choose()'s answer -- abort on a
selector error, warn on a downgrade, abort when neither daemon is installed --
and the --backend argument handed to setupntp were covered by nothing.
They were unreachable from a test because they sat inside process_request,
which needs a management node. Move the decisions into ntp_backend_action() and
setupntp_command(), which take their inputs and return an answer; the caller
keeps send_msg and runcmd. No behaviour changes -- the same messages are sent
on the same conditions, and the same command is built.
Verified by mutation rather than by reading: dropping the install abort reds 3
of 15, the downgrade note 1, the --backend argument 2, the server-list split 1,
and the selector-error abort 2.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
ntp_backend_selection.t covers xCAT::NTP::Backend thoroughly, but copying the
base makentp.pm over the head one leaves the entire unit suite byte-identical.
Zero assertions cover the file the fix is named after: the call site that
consumes the selector, the --backend pass-through to setupntp, and the abort
branches for "selector errored" and "neither daemon installed" all run in no
test. A helper can be perfectly covered while nothing calls it.
This drives the decisions the call site makes -- abort vs configure, the
downgrade note, and the command handed to setupntp -- rather than
process_request, which needs a management node. It fails at the extraction
guard until those decisions are routines 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>
makentp picks the NTP daemon through xCAT::NTP::Backend, but then runs setupntp -- on the
management node and, through updatenode -P, on every service node -- and setupntp decided for
itself with `check_executes chronyd || USE_NTPD=yes`. A cluster with site.ntpbackend=ntpd and
chronyd present therefore configured ntpd on the MN and chrony everywhere else. The selector was
one code path only on the side that does not write the config.
setupntp now takes --backend chrony|ntpd, and makentp passes what it chose on both call sites.
The service-node dispatch passes the cluster's intent rather than this host's availability: a
service node may have a different daemon installed, and the requested backend is a preference --
a node without chronyd still falls back to ntpd and logs that it did, rather than failing.
--use-ntpd keeps working.
Two results of choose() were computed and never read. A downgrade is now reported, so an admin
who asked for one daemon and got the other is told. install=1 -- neither daemon present -- is an
error naming the daemon that is missing, instead of falling through to the ntpd branch and
reporting "Please make sure ntpd is installed" even when chrony was the preferred choice.
Six cases cover the selection: the backend honoured in both directions, the probe still used when
none is given, and the fallback when the requested daemon is absent. Removing the --backend case
fails one; ignoring the preference fails two.
Also worth stating plainly, since the PR reads as a management-node fix: setupntp stops and
disables systemd-timesyncd wherever it runs, nodes included. It has to -- timesyncd disciplines
the clock against the daemon being configured -- but a node that was relying on it loses it.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
setupntp explained systemd-timesyncd in four lines where two carry the reason nothing displaces
it, and repeated the hwclock rationale that the guard below already implies. NTP::Backend's
header narrated that the module exists to be unit-tested.
The test header restated all of it a third time, and said "the first three are behaviours of the
script" about a list of three items whose third is the packaging check. Corrected and shortened;
the note on why setupntp cannot simply be executed is kept, since that is not obvious from
reading it.
Interface documentation is left alone: choose()'s parameter list and default_backend()'s
per-family table are what a caller needs.
18 comment lines removed, no behaviour change.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
The test added with this fix matched regexes against the text of setupntp and makentp.pm. A
source match cannot tell whether the code it found ever runs, and it describes the fix rather
than the behaviour: unlike(qr/check_exec_or_exit[^\n]*hwclock/) says "this line does not mention
hwclock", where what matters is that a management node without hwclock still gets its clock set.
Run the script instead.
setupntp cannot simply be executed: it forces its own PATH, so its commands cannot be stubbed
from outside, and it exits unless UID is 0, so it cannot run as an ordinary user. The test takes
the script's own helper functions and the section that configures the daemon and drives them with
shell functions, which bash resolves ahead of PATH and which both `type` and `command -v` report
as present -- the two probes the script uses. A node without hwclock is simulated by hiding it
from both, rather than by asserting on the shape of the check.
Every assertion now fails when the behaviour it describes is removed:
hwclock required by check_exec_or_exit again 5 assertions fail
the hwclock guard removed 2 assertions fail
systemd-timesyncd left running 2 assertions fail
systemd-timesyncd left enabled 2 assertions fail
The debian/control and xCAT.spec checks are kept as they were. Those are manifest contents, not
behaviour -- there is nothing to execute, and the assertion is on a package name that survives
reformatting.
The match on makentp.pm for xCAT::NTP::Backend->choose is dropped. It asserted that a call site
exists; ntp_backend_selection.t already drives the selector itself across 33 assertions, which is
the decision that matters.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
Add xCAT::NTP::Backend, a selector for the NTP daemon xCAT configures, in the same
spirit as xCAT::DHCP::Backend. It honours site.ntpbackend, defaults per distro family
(EL7+/SLES15+ chrony, older ntpd, Ubuntu/Debian chrony), and downgrades to whichever
of chrony/ntpd is actually installed rather than installing a second daemon. makentp
selects through it instead of probing /usr/sbin/chronyd directly, so the choice is one
unit-tested code path.
Guarantee the daemon at install time: the xcat metapackage now Depends on
"chrony | ntp" and the xCAT rpm Requires "(chrony or ntp)". Both sit beside the
existing service dependencies the metapackage already declares -- isc-dhcp-server|kea,
apache2, nfs-kernel-server -- because an MN that cannot serve time cannot serve its
compute nodes, and Ubuntu's default systemd-timesyncd is a client only.
Stop requiring hwclock in setupntp. It only persists the already-stepped system clock
to the RTC, and Ubuntu 24.04 moved it to util-linux-extra, absent on minimal images --
so a fatal check_exec_or_exit aborted the entire NTP setup, including the clock step
that does not use it. Use it when present, log and continue when not, and pull
util-linux-extra through Recommends and the diskless pkglist so it usually is.
Disable systemd-timesyncd there too. It ships as part of systemd rather than a
time-daemon package, so nothing displaces it, and it keeps disciplining the clock
against the daemon being configured.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
An xCAT management node must serve time to its compute nodes (they point at
ntpservers=<xcatmaster>). Ubuntu ships only systemd-timesyncd, an SNTP client that
disciplines the local clock but cannot serve time, and makentp supports chronyd and
ntpd only. On a stock Ubuntu MN makentp therefore fails outright with "Please make
sure ntpd is installed", reddening reg_linux_diskfull_installation_flat.
Two further defects compound it. setupntp hard-requires hwclock through
check_exec_or_exit, but Ubuntu 24.04 moved hwclock into util-linux-extra, which is
absent from minimal images -- so the whole NTP setup, including the clock step that
does not need hwclock at all, aborts. And systemd-timesyncd is part of systemd rather
than a time-daemon package, so it coexists at the package level and keeps disciplining
the clock against whichever daemon xCAT just configured.
Assert a shared, unit-tested daemon selector in the spirit of xCAT::DHCP::Backend, the
package dependencies that guarantee a server-capable daemon and hwclock, and the
setupntp changes. All fail today.
Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
Cover the order that names the install device: installnic, then primarynic,
then mac.mac. Either attribute may name an interface or carry an address, and
the keyword mac returns to mac.mac.
Cover the device the kickstart names for each of those inputs. A node that
sets neither attribute keeps the address it has today. Cover the defect the
change closes, where a mac.mac entry that holds several untagged addresses
resolves to the last of them.
Cover that Ubuntu keeps its own pair of a name and an address over the same
resolution, and that the unique local address still comes from the hardware
address.
noderes.installnic names the adapter that deploys the operating system. The
kickstart network line ignored it and named the adapter from mac.mac alone, so
a node that sets installnic got a kernel command line that obeys installnic and
a kickstart that configures a different adapter. On a node with more than one
adapter the installer then brings up the wrong one and cannot reach the
repository.
Name the device from the shared resolution, which gives the interface name when
installnic or primarynic names one, and the address otherwise. A node that sets
neither attribute keeps the address it has today.
The unique local address still comes from the hardware address, because
autoulaaddress builds the address from it.
The order that names the install device is noderes.installnic, then
noderes.primarynic, then mac.mac. gen_net_boot_params owns that order for the
netboot kernel parameters. Only the Ubuntu template reused it. Every other
install template reads mac.mac on its own.
Move the reuse into install_device_params so that any install template can
share it. subiquity_install_netcfg keeps its own name and its own return
value, because netplan needs the pair of a name and an address. Behaviour
does not change.
Five comments in the discovery path explained a consequence chain or
justified the code to a reviewer rather than stating what is not visible
locally: the payload protocol the code below already shows, a rationale
for the missing boot loader message, the two Genesis compression
variants, the HTTP and TFTP entry pair, and the :noboot divergence from
the PXELINUX files. Say the non-obvious part once.
Comments only; no executable line changes.
Firmware configured for UEFI HTTP boot sends client architecture 28
(0x001c) and only accepts an offer whose boot file is a URL and whose
reply carries the HTTPClient vendor class; it ignores the PXE offer that
answers architecture 27. riscv64 nodes therefore could not boot at all
from firmware set up that way.
Answer them with the same grub2 image over HTTP. The URL has to name the
management node address of the network the request came in on, so the
class belongs to the subnet, like the POWER OPAL and xNBA network classes
next to it, and it is only offered while the boot loader is actually
published. The ISC backend renders the matching subnet branch.
This covers every client without a reservation, which is the discovery
case. A node that nodeset has configured keeps its per-node boot loader
over TFTP, as on the other architectures.
The option flags of a client class -- always-send and its siblings -- were
passed through as whatever the caller set, so a plain Perl 1 reached the
configuration as the number 1 and Kea refuses to parse that. The class
flag next to it is already normalised; do the same for the option data, so
callers can stay free of JSON.
The grub2 discovery configuration loaded the Genesis kernel and initramfs
over TFTP, a lockstep protocol that acknowledges every block and runs one
server process per client. Fetching the same 79 MiB Genesis image from a
node on the lab network took 61.5 s over TFTP and 1.2 s over HTTP, and a
whole cluster discovering at once queues on the TFTP server.
Write two entries instead. The default one sets root to the HTTP server
of the management node on that network and loads the same files from
below the TFTP root, the way nodeset does for netboot=grub2-http; the
second keeps the TFTP paths for a management node that does not serve the
TFTP root over HTTP, and "set fallback=1" moves to it when GRUB cannot
fetch the payload over HTTP. site.httpport is honoured.
riscv64 nodes boot through UEFI and grub2 only, and xCAT builds no boot
loader: /tftpboot/boot/grub2/grub2.riscv64 has to come from the xcat-dep
grub2-xcat package or be copied by hand, which is a step an admin only
finds out about when a node times out in firmware.
The EL riscv64 media carry exactly that image as EFI/BOOT/grubriscv64.efi,
so copycd publishes it after a successful media copy, and says so. An
image the management node already has is never replaced, and the media of
every other architecture is untouched.
Explain why the riscv64 templates disable the installer's kdump add-on,
how to reserve memory for crash dumps on an installed node, and what
diskless images reserve by default.
EL10 defines no default crash kernel reservation for riscv64, so the
installer's kdump add-on falls back to writing the literal
"crashkernel=auto" into the boot loader arguments of the installed
system. EL10 kernels dropped support for that value: nothing is
reserved, but the string is on the command line, so kdump.service passes
its condition and then fails on every installed riscv64 node.
Turn the add-on off in the riscv64 templates. Nodes come up with kdump
inactive instead of failed, and a node that should take crash dumps gets
a real reservation the usual way, through linuximage.addkcmdline or
bootparams.addkcmdline.
The kdump branch of the diskless kernel command line has a default
reservation for ppc64 and x86 only. On any other architecture an image
with linuximage.dump set but no linuximage.crashkernelsize got dump= and
no crashkernel= at all, so the kernel reserved nothing and kdump could
never run. EL has no default reservation for riscv64 either
(kdumpctl get-default-crashkernel is empty there), so nothing else fills
the gap.
Give riscv64 the same treatment as the architectures around it and
default to 256M. An explicit crashkernelsize still wins, and images
without dump are unchanged.
Document the riscv64 kickstart templates that work around the EL10
anaconda's missing RISC-V EFI platform, where grub2.riscv64 and the
riscv64 Genesis packages come from, which dependencies a riscv64
management node takes from the riscv64 dependency repository, and why
xCAT-server only recommends perl-DB_File.