mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-04 20:17:55 +00:00
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)
This commit is contained in:
@@ -32,6 +32,8 @@ By default, it sets up the NTP server for xCAT management node. If \ **-a**\ fl
|
||||
|
||||
\ *site.ntpservers*\ -- the NTP servers for the service node and compute node to sync with. The keyword <xcatmaster> means that the node's NTP server is the node that is managing it (either its service node or the management node).
|
||||
|
||||
\ *site.ntpbackend*\ -- the NTP daemon to configure. Valid values are auto, chrony, and ntpd. The default is auto, which selects chrony on distributions that ship it and ntpd on older ones such as EL6 and SLES 12. If the selected daemon is not installed, \ **makentp**\ uses the other one and reports the change. The same value reaches the service nodes and the compute nodes, which the \ *setupntp*\ postscript also accepts as \ **-**\ **-backend chrony|ntpd**\ .
|
||||
|
||||
To setup NTP on the compute node, add \ *setupntp*\ postscript to the \ *postscripts*\ table and run \ **updatenode node -P setupntp**\ command.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
package xCAT::NTP::Backend;
|
||||
|
||||
# Selector for the NTP daemon xCAT configures (chrony vs. ntpd), in the same spirit as
|
||||
# xCAT::DHCP::Backend. There is no timesyncd path: it is an SNTP client and cannot serve time to
|
||||
# compute nodes, so an MN always needs chrony or ntpd.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my %valid_backend = map { $_ => 1 } qw(auto chrony ntpd);
|
||||
|
||||
# Accept common aliases so site.ntpbackend / callers can say chronyd or ntp.
|
||||
my %alias = (
|
||||
chronyd => 'chrony',
|
||||
ntp => 'ntpd',
|
||||
ntpsec => 'ntpd',
|
||||
);
|
||||
|
||||
sub normalize {
|
||||
my ( $class, $backend ) = @_;
|
||||
|
||||
$backend = 'auto' unless defined($backend) && $backend ne '';
|
||||
$backend =~ s/^\s+|\s+$//g;
|
||||
$backend = lc($backend);
|
||||
$backend = $alias{$backend} if exists $alias{$backend};
|
||||
|
||||
return $backend if $valid_backend{$backend};
|
||||
return;
|
||||
}
|
||||
|
||||
# choose: resolve the effective backend for this node.
|
||||
# requested -- override (default: site.ntpbackend, else 'auto')
|
||||
# os_name/version -- OS identity (default: detected)
|
||||
# available -- optional { chrony => 0/1, ntpd => 0/1 } to bypass command detection (tests)
|
||||
# commands -- optional { <command> => 0/1 } to bypass the PATH probe for one command
|
||||
# check_available -- when true, downgrade to whichever daemon is installed, and flag install=1
|
||||
# when neither is.
|
||||
sub choose {
|
||||
my ( $class, %args ) = @_;
|
||||
|
||||
my $requested = exists $args{requested} ? $args{requested} : $class->_site_backend();
|
||||
my $normalized = $class->normalize($requested);
|
||||
unless ($normalized) {
|
||||
return { error => "Invalid site.ntpbackend value '$requested'. Valid values are auto, chrony, and ntpd." };
|
||||
}
|
||||
|
||||
my $selected = $normalized eq 'auto' ? $class->default_backend(%args) : $normalized;
|
||||
my $result = { requested => $normalized, name => $selected, install => 0 };
|
||||
|
||||
return $result unless $args{check_available};
|
||||
|
||||
my $other = $selected eq 'chrony' ? 'ntpd' : 'chrony';
|
||||
if ( $class->available( $selected, %args ) ) {
|
||||
return $result;
|
||||
} elsif ( $class->available( $other, %args ) ) {
|
||||
# respect what is actually installed rather than installing a second daemon
|
||||
$result->{name} = $other;
|
||||
$result->{downgraded} = $selected;
|
||||
return $result;
|
||||
}
|
||||
|
||||
# neither present: keep the preferred choice and tell the caller to install it
|
||||
$result->{install} = 1;
|
||||
return $result;
|
||||
}
|
||||
|
||||
# default_backend: table-driven per distro family.
|
||||
# EL/RHEL & clones: >= 7 -> chrony, 6 -> ntpd
|
||||
# SLES/SUSE: >= 15 -> chrony, 12 -> ntpd
|
||||
# Ubuntu/Debian: chrony (timesyncd is the OOB client but cannot serve; chrony from 18.04+)
|
||||
sub default_backend {
|
||||
my ( $class, %args ) = @_;
|
||||
|
||||
my $os_name = exists $args{os_name} ? $args{os_name} : $class->_osver('os');
|
||||
my $version = exists $args{version} ? $args{version} : ( split /,/, $class->_osver('all'), 2 )[1];
|
||||
my ($major) = ( defined($version) ? $version : '' ) =~ /^(\d+)/;
|
||||
|
||||
if ( defined($os_name) && $os_name =~ /^(?:rhel|rhels|rocky|alma|centos|ol|fedora)$/i ) {
|
||||
return 'ntpd' if defined($major) && $major <= 6;
|
||||
return 'chrony';
|
||||
}
|
||||
if ( defined($os_name) && $os_name =~ /^(?:sles|sled|suse|opensuse|leap)$/i ) {
|
||||
return 'ntpd' if defined($major) && $major <= 12;
|
||||
return 'chrony';
|
||||
}
|
||||
if ( defined($os_name) && $os_name =~ /^(?:ubuntu|debian)$/i ) {
|
||||
return 'chrony';
|
||||
}
|
||||
|
||||
return 'chrony';
|
||||
}
|
||||
|
||||
sub available {
|
||||
my ( $class, $backend, %args ) = @_;
|
||||
|
||||
if ( exists $args{available} && ref( $args{available} ) eq 'HASH' && exists $args{available}{$backend} ) {
|
||||
return $args{available}{$backend} ? 1 : 0;
|
||||
}
|
||||
|
||||
# xCAT drives chrony through systemd: makentp configures it only where systemctl is present,
|
||||
# and setupntp hands over to ntpd without it. chronyd alone is not a usable chrony backend.
|
||||
if ( $backend eq 'chrony' ) {
|
||||
return ( _has_command( 'chronyd', %args ) && _has_command( 'systemctl', %args ) ) ? 1 : 0;
|
||||
}
|
||||
return _has_command( 'ntpd', %args ) if $backend eq 'ntpd';
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub _has_command {
|
||||
my ( $command, %args ) = @_;
|
||||
|
||||
if ( ref( $args{commands} ) eq 'HASH' && exists $args{commands}{$command} ) {
|
||||
return $args{commands}{$command} ? 1 : 0;
|
||||
}
|
||||
|
||||
return _command_exists($command);
|
||||
}
|
||||
|
||||
sub _site_backend {
|
||||
my $backend = eval {
|
||||
require xCAT::TableUtils;
|
||||
return xCAT::TableUtils->get_site_attribute( 'ntpbackend', 'auto' );
|
||||
};
|
||||
|
||||
return $backend || 'auto';
|
||||
}
|
||||
|
||||
sub _osver {
|
||||
my ( $class, $type ) = @_;
|
||||
|
||||
my $osver = eval {
|
||||
require xCAT::Utils;
|
||||
return defined($type) ? xCAT::Utils->osver($type) : xCAT::Utils->osver();
|
||||
};
|
||||
|
||||
return $osver || 'unknown';
|
||||
}
|
||||
|
||||
sub _command_exists {
|
||||
my ($command) = @_;
|
||||
|
||||
foreach my $dir ( split /:/, $ENV{PATH} || '' ) {
|
||||
next unless $dir;
|
||||
return 1 if -x "$dir/$command";
|
||||
}
|
||||
foreach my $path ( "/usr/sbin/$command", "/usr/bin/$command", "/sbin/$command", "/bin/$command" ) {
|
||||
return 1 if -x $path;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1283,6 +1283,11 @@ passed as argument rather than by table value',
|
||||
" give stable output. You can increase the timeout value by specifying \n" .
|
||||
" '--min-rtt-timeout 1s'. xCAT will append the options defined here to \n" .
|
||||
" the nmap command.\n\n" .
|
||||
" ntpbackend: The NTP daemon used by makentp and the setupntp postscript. Valid\n" .
|
||||
" values are auto, chrony, and ntpd. The default is auto. In auto\n" .
|
||||
" mode, xCAT uses chrony on distributions that ship it and ntpd on\n" .
|
||||
" older ones such as EL6 and SLES 12. If the selected daemon is not\n" .
|
||||
" installed, xCAT uses the other one and reports the change.\n\n" .
|
||||
" ntpservers: A comma delimited list of NTP servers for the service node and\n" .
|
||||
" the compute node to sync with. The keyword <xcatmaster> means that\n" .
|
||||
" the node's NTP server is the node that is managing it\n" .
|
||||
|
||||
@@ -26,6 +26,8 @@ I<site.extntpservers> -- the NTP servers for the management node to sync with. I
|
||||
|
||||
I<site.ntpservers> -- the NTP servers for the service node and compute node to sync with. The keyword <xcatmaster> means that the node's NTP server is the node that is managing it (either its service node or the management node).
|
||||
|
||||
I<site.ntpbackend> -- the NTP daemon to configure. Valid values are auto, chrony, and ntpd. The default is auto, which selects chrony on distributions that ship it and ntpd on older ones such as EL6 and SLES 12. If the selected daemon is not installed, B<makentp> uses the other one and reports the change. The same value reaches the service nodes and the compute nodes, which the I<setupntp> postscript also accepts as B<--backend chrony|ntpd>.
|
||||
|
||||
=back
|
||||
|
||||
To setup NTP on the compute node, add I<setupntp> postscript to the I<postscripts> table and run B<updatenode node -P setupntp> command.
|
||||
|
||||
@@ -209,6 +209,70 @@ sub preprocess_request {
|
||||
=cut
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
=head3 ntp_backend_action
|
||||
|
||||
Decide what makentp should do with the answer xCAT::NTP::Backend->choose gave it.
|
||||
|
||||
Kept separate from process_request so the decision can be driven directly: the caller
|
||||
keeps the side effects (send_msg, runcmd) and this returns only what to do.
|
||||
|
||||
Arguments:
|
||||
$backend the hashref from xCAT::NTP::Backend->choose
|
||||
$nodename the host makentp is configuring, for the error text
|
||||
Returns:
|
||||
a hashref: action => 'abort'|'configure', error => the message to report when
|
||||
aborting, name => the daemon to configure, notes => messages to report either way
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
sub ntp_backend_action {
|
||||
my ($backend, $nodename) = @_;
|
||||
|
||||
$backend ||= {};
|
||||
return { action => 'abort', error => $backend->{error}, notes => [] }
|
||||
if $backend->{error};
|
||||
|
||||
my @notes;
|
||||
push @notes,
|
||||
"NTP backend $backend->{downgraded} is not installed; using $backend->{name} instead."
|
||||
if $backend->{downgraded};
|
||||
|
||||
return {
|
||||
action => 'abort',
|
||||
error => "Neither chrony nor ntp is installed on $nodename. "
|
||||
. "Install $backend->{name}, or set site.ntpbackend to the daemon you have.",
|
||||
notes => \@notes,
|
||||
} if $backend->{install};
|
||||
|
||||
return { action => 'configure', name => $backend->{name}, notes => \@notes };
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
=head3 setupntp_command
|
||||
|
||||
Build the setupntp invocation. The server list arrives comma separated from the site
|
||||
table and setupntp takes them as separate arguments.
|
||||
|
||||
Arguments:
|
||||
$backend_name the daemon setupntp should configure
|
||||
$ntp_servers the comma separated server list
|
||||
Returns:
|
||||
the command line
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
sub setupntp_command {
|
||||
my ($backend_name, $ntp_servers) = @_;
|
||||
|
||||
return "/install/postscripts/setupntp --backend $backend_name "
|
||||
. join(' ', split(',', $ntp_servers));
|
||||
}
|
||||
|
||||
sub process_request {
|
||||
my $req = shift;
|
||||
my $callback = shift;
|
||||
@@ -252,13 +316,23 @@ sub process_request {
|
||||
$ntp_servers = $retdata->{'master'};
|
||||
}
|
||||
|
||||
# Pick the NTP daemon (chrony vs ntpd) via the shared, unit-tested selector -- the same spirit
|
||||
# as xCAT::DHCP::Backend (ISC vs Kea). It honors site.ntpbackend, defaults per distro, and
|
||||
# downgrades chrony->ntpd (or vice versa) to whichever is actually installed.
|
||||
require xCAT::NTP::Backend;
|
||||
my $ntp_backend = xCAT::NTP::Backend->choose(check_available => 1);
|
||||
my $ntp_action = ntp_backend_action($ntp_backend, $nodename);
|
||||
send_msg(\%request, 0, $_) for @{ $ntp_action->{notes} };
|
||||
if ($ntp_action->{action} eq 'abort') {
|
||||
send_msg(\%request, 1, $ntp_action->{error});
|
||||
return 1;
|
||||
}
|
||||
|
||||
# Handle chronyd here,
|
||||
if (-x "/usr/sbin/chronyd" &&
|
||||
(-x "/usr/bin/systemctl" || -x "/bin/systemctl")) {
|
||||
if ($ntp_action->{name} eq 'chrony') {
|
||||
send_msg(\%request, 0, "Will configure chronyd instead.");
|
||||
|
||||
my $cmd = "/install/postscripts/setupntp " .
|
||||
join(' ', split(',', $ntp_servers));
|
||||
my $cmd = setupntp_command($ntp_action->{name}, $ntp_servers);
|
||||
send_msg(\%request, 0, "Calling ... " . $cmd);
|
||||
|
||||
my $result = xCAT::Utils->runcmd($cmd, 0);
|
||||
@@ -487,12 +561,21 @@ HANDLE_MAKENTP_A:
|
||||
my @servicenodes = xCAT::ServiceNodeUtils->getSNList('ntpserver');
|
||||
if (@servicenodes > 0) {
|
||||
send_msg(\%request, 0, "configuring service nodes: @servicenodes");
|
||||
|
||||
# Pass the cluster's intent, not this host's availability: a service node may have a
|
||||
# different daemon installed, and setupntp falls back locally when it does.
|
||||
require xCAT::NTP::Backend;
|
||||
my $sn_backend = xCAT::NTP::Backend->choose();
|
||||
my $sn_script = $sn_backend->{name}
|
||||
? "setupntp --backend $sn_backend->{name}"
|
||||
: "setupntp";
|
||||
|
||||
my $ret =
|
||||
xCAT::Utils->runxcmd(
|
||||
{
|
||||
command => ['updatenode'],
|
||||
node => \@servicenodes,
|
||||
arg => [ "-P", "setupntp" ],
|
||||
arg => [ "-P", $sn_script ],
|
||||
},
|
||||
$sub_req, -1, 1
|
||||
);
|
||||
|
||||
@@ -16,6 +16,7 @@ gzip
|
||||
xz-utils
|
||||
cpio
|
||||
chrony
|
||||
util-linux-extra
|
||||
iproute2
|
||||
dracut
|
||||
dracut-network
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Spec;
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# Regression: the NTP backend selector was well covered and nothing connected it to makentp.
|
||||
#
|
||||
# ntp_backend_selection.t exercises xCAT::NTP::Backend thoroughly, but copying the BASE
|
||||
# makentp.pm over the head one left the whole unit suite byte-identical -- so the call site
|
||||
# that consumes the selector, the --backend pass-through and the abort branches ran in no test
|
||||
# at all. A helper can be perfectly covered while nothing calls it.
|
||||
#
|
||||
# process_request needs a management node, so the decisions it makes about the selector's
|
||||
# answer live in two small routines that take their inputs and return an answer, and those are
|
||||
# what this drives. The side effects (send_msg, runcmd, updatenode) stay in the caller.
|
||||
|
||||
my $repo_root = File::Spec->rel2abs(
|
||||
File::Spec->catdir( $FindBin::Bin, '..', '..' )
|
||||
);
|
||||
my $plugin = File::Spec->catfile(
|
||||
$repo_root, 'xCAT-server', 'lib', 'xcat', 'plugins', 'makentp.pm'
|
||||
);
|
||||
plan skip_all => "makentp.pm not found" unless -f $plugin;
|
||||
|
||||
my $src = do { local $/; open my $fh, '<', $plugin or die $!; <$fh> };
|
||||
|
||||
# BAIL_OUT rather than skip, so a rename fails loudly instead of silently covering nothing.
|
||||
my @wanted = qw(ntp_backend_action setupntp_command);
|
||||
my $body = '';
|
||||
for my $name (@wanted) {
|
||||
my ($sub) = $src =~ /\n(sub \Q$name\E \{.*?\n\})\n/s;
|
||||
BAIL_OUT("could not extract $name from makentp.pm") unless defined $sub;
|
||||
$body .= "$sub\n";
|
||||
}
|
||||
|
||||
{
|
||||
package T;
|
||||
eval "$body; 1" or main::BAIL_OUT("could not eval the makentp helpers: $@");
|
||||
}
|
||||
|
||||
# --- the selector said no backend is usable at all -------------------------
|
||||
{
|
||||
my $r = T::ntp_backend_action( { error => 'site.ntpbackend is nonsense' }, 'mn1' );
|
||||
is( $r->{action}, 'abort', 'a selector error aborts makentp' );
|
||||
is( $r->{error}, 'site.ntpbackend is nonsense',
|
||||
'and the selector error is what the caller reports' );
|
||||
}
|
||||
|
||||
# --- neither daemon installed ----------------------------------------------
|
||||
{
|
||||
my $r = T::ntp_backend_action( { name => 'chrony', install => 1 }, 'mn1' );
|
||||
is( $r->{action}, 'abort', 'nothing installed aborts rather than configuring' );
|
||||
like( $r->{error}, qr/Neither chrony nor ntp is installed on mn1/,
|
||||
'the abort names the node' );
|
||||
like( $r->{error}, qr/set site\.ntpbackend/,
|
||||
'and tells the admin how to override the choice' );
|
||||
}
|
||||
|
||||
# --- the requested backend is absent, so the selector downgraded ------------
|
||||
{
|
||||
my $r = T::ntp_backend_action(
|
||||
{ name => 'chrony', downgraded => 'ntpd' }, 'mn1' );
|
||||
is( $r->{action}, 'configure', 'a downgrade still configures' );
|
||||
is( $r->{name}, 'chrony', 'using the daemon that is actually installed' );
|
||||
like( join( '|', @{ $r->{notes} } ),
|
||||
qr/ntpd is not installed; using chrony instead/,
|
||||
'and says so, rather than silently using something else' );
|
||||
}
|
||||
|
||||
# --- the ordinary case ------------------------------------------------------
|
||||
{
|
||||
my $r = T::ntp_backend_action( { name => 'chrony' }, 'mn1' );
|
||||
is( $r->{action}, 'configure', 'an installed backend configures' );
|
||||
is( $r->{name}, 'chrony', 'with the name the selector chose' );
|
||||
is_deeply( $r->{notes}, [], 'and says nothing extra' );
|
||||
}
|
||||
|
||||
{
|
||||
my $r = T::ntp_backend_action( { name => 'ntpd' }, 'mn1' );
|
||||
is( $r->{name}, 'ntpd', 'ntpd is carried through as chosen' );
|
||||
}
|
||||
|
||||
# --- the --backend pass-through, which is what setupntp reads ---------------
|
||||
{
|
||||
is( T::setupntp_command( 'chrony', '10.0.0.1,10.0.0.2' ),
|
||||
'/install/postscripts/setupntp --backend chrony 10.0.0.1 10.0.0.2',
|
||||
'the chosen backend reaches setupntp and the server list is space separated' );
|
||||
|
||||
is( T::setupntp_command( 'ntpd', '10.0.0.1' ),
|
||||
'/install/postscripts/setupntp --backend ntpd 10.0.0.1',
|
||||
'a different backend produces a different command' );
|
||||
|
||||
unlike( T::setupntp_command( 'chrony', '10.0.0.1' ), qr/,/,
|
||||
'no comma survives into the argument list' );
|
||||
}
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,161 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use File::Temp qw(tempdir);
|
||||
use Test::More;
|
||||
|
||||
# A stock Ubuntu MN broke three ways: a missing hwclock aborted the whole NTP setup,
|
||||
# systemd-timesyncd kept fighting the daemon being configured, and the xcat package pulled in no
|
||||
# daemon that can serve time. The first two are behaviours of the script, so run it; the third is
|
||||
# a property of the packaging manifests, so read those.
|
||||
|
||||
my $repo = "$FindBin::Bin/../..";
|
||||
my $setupntp_path = "$repo/xCAT/postscripts/setupntp";
|
||||
plan skip_all => 'setupntp not found' unless -r $setupntp_path;
|
||||
plan skip_all => 'setupntp targets Linux management nodes' unless $^O eq 'linux';
|
||||
|
||||
open(my $fh, '<', $setupntp_path) or die "open $setupntp_path: $!";
|
||||
my @lines = <$fh>;
|
||||
close $fh;
|
||||
my $source = join '', @lines;
|
||||
|
||||
# setupntp forces its own PATH and exits unless UID is 0, so it cannot be stubbed from outside
|
||||
# or run unprivileged. Drive its own helper functions with shell functions instead: bash resolves
|
||||
# those ahead of PATH, and both `type` and `command -v` report them as present.
|
||||
my ($helpers) = $source =~ /\A(.*?)^\[ "\$\{UID\}" -eq "0" \]/ms;
|
||||
my ($args) = $source =~ /^(# Handle command line arguments\n.*?)^if \[ "\$\{#NTP_SERVERS\[@\]\}"/ms;
|
||||
my ($select) = $source =~ /^(# The requested backend is a preference.*?)^if \[ -n "\$\{USE_NTPD\}"/ms;
|
||||
my ($body) = $source =~ /^(check_exec_or_exit cp cat logger grep\n.*?)^CHRONY_CONF=/ms;
|
||||
BAIL_OUT('could not take the helper functions from setupntp') unless $helpers;
|
||||
BAIL_OUT('could not take the daemon setup section from setupntp') unless $body;
|
||||
BAIL_OUT('could not take the argument parsing from setupntp') unless $args;
|
||||
BAIL_OUT('could not take the backend selection from setupntp') unless $select;
|
||||
|
||||
sub run_setupntp {
|
||||
my (%opt) = @_;
|
||||
my $root = tempdir(CLEANUP => 1);
|
||||
|
||||
# Record every call the section makes, and answer as the case requires.
|
||||
my $doubles = <<"BASH";
|
||||
log() { printf '%s\\n' "\$*" >>"$root/calls"; }
|
||||
systemctl() { log "systemctl \$*"; return 0; }
|
||||
timedatectl() { log "timedatectl \$*"; return 0; }
|
||||
chronyd() { log "chronyd \$*"; return 0; }
|
||||
logger() { log "logger \$*"; return 0; }
|
||||
rm() { log "rm \$*"; return 0; }
|
||||
BASH
|
||||
$doubles .= $opt{hwclock}
|
||||
? qq{hwclock() { log "hwclock \$*"; return 0; }\n}
|
||||
# hwclock genuinely absent: hide it from both probes the script can use
|
||||
: qq{command() { if [ "\$1" = "-v" ] && [ "\$2" = "hwclock" ]; then return 1; fi; builtin command "\$@"; }\n}
|
||||
. qq{type() { if [ "\$1" = "hwclock" ]; then return 1; fi; builtin type "\$@"; }\n};
|
||||
|
||||
$doubles .= "declare -a NTP_SERVERS=(" . ($opt{server} ? qq{"$opt{server}"} : '') . ")\n";
|
||||
$doubles .= "log_label=xcat\n";
|
||||
|
||||
my $rc = system('bash', '-c', $doubles . $helpers . $body);
|
||||
|
||||
my $calls = '';
|
||||
if (open my $ch, '<', "$root/calls") { local $/; $calls = <$ch>; close $ch }
|
||||
return { rc => $rc >> 8, calls => $calls };
|
||||
}
|
||||
|
||||
# --- a management node with no hwclock: the bug this fix exists for --------
|
||||
{
|
||||
my $r = run_setupntp(hwclock => 0);
|
||||
|
||||
is($r->{rc}, 0, 'setupntp completes on a node with no hwclock instead of aborting');
|
||||
like($r->{calls}, qr/^chronyd .*-q/m,
|
||||
'the system clock is still stepped, which is the part that never needed hwclock');
|
||||
unlike($r->{calls}, qr/^hwclock/m, 'and no attempt is made to use the missing hwclock');
|
||||
like($r->{calls}, qr/hwclock not present/,
|
||||
'the skipped RTC persist is reported rather than passing silently');
|
||||
}
|
||||
|
||||
# --- a management node that has hwclock ------------------------------------
|
||||
{
|
||||
my $r = run_setupntp(hwclock => 1);
|
||||
|
||||
is($r->{rc}, 0, 'setupntp completes on a node that has hwclock');
|
||||
like($r->{calls}, qr/^hwclock --systohc --utc$/m,
|
||||
'the stepped system clock is persisted to the RTC when hwclock is available');
|
||||
like($r->{calls}, qr/^chronyd .*-q/m, 'the clock is stepped in this case too');
|
||||
}
|
||||
|
||||
# systemd-timesyncd used to be checked here, over $body -- the section from
|
||||
# `check_exec_or_exit cp cat logger grep` onwards, which only the chrony path reaches. The
|
||||
# stop/disable has moved above the ntpd hand-off so it runs on both paths, which puts it outside
|
||||
# this window; setupntp_timesyncd_both_backends.t drives both paths and asserts it there.
|
||||
|
||||
# --- the configured NTP server reaches the clock step ----------------------
|
||||
{
|
||||
my $r = run_setupntp(hwclock => 1, server => 'ntp.example.com');
|
||||
like($r->{calls}, qr/^chronyd .*server ntp\.example\.com iburst/m,
|
||||
'the clock is stepped against the NTP server the node was given');
|
||||
}
|
||||
{
|
||||
my $r = run_setupntp(hwclock => 1);
|
||||
like($r->{calls}, qr/^chronyd .*pool pool\.ntp\.org iburst/m,
|
||||
'a node given no NTP server falls back to the public pool');
|
||||
}
|
||||
|
||||
# --- the packaging must supply a daemon that can serve time ----------------
|
||||
# These are manifest contents, not behaviour: there is nothing to execute.
|
||||
sub slurp { my $p = shift; open my $h, '<', "$repo/$p" or return undef; local $/; <$h> }
|
||||
|
||||
my $ctrl = slurp('xCAT/debian/control');
|
||||
SKIP: {
|
||||
skip 'debian/control not found', 2 unless defined $ctrl;
|
||||
like($ctrl, qr/^Depends:.*\bchrony \| ntp\b/m,
|
||||
'the xcat debian package depends on a server-capable NTP daemon');
|
||||
like($ctrl, qr/^Recommends:.*\butil-linux-extra\b/m,
|
||||
'and recommends the package that carries hwclock on noble and later');
|
||||
}
|
||||
|
||||
my $spec = slurp('xCAT/xCAT.spec');
|
||||
SKIP: {
|
||||
skip 'xCAT.spec not found', 1 unless defined $spec;
|
||||
like($spec, qr/^Requires:\s*\(chrony or ntp\)/m,
|
||||
'the xCAT rpm requires a server-capable NTP daemon');
|
||||
}
|
||||
|
||||
# --- the backend the management node chose must reach the node ------------
|
||||
# makentp selects the daemon from site.ntpbackend through xCAT::NTP::Backend and passes it here,
|
||||
# so a cluster told to use ntpd does not get chrony on every node that happens to have it.
|
||||
# The backend is a preference on both sides: a node that does not have the requested daemon is
|
||||
# configured with the other one, rather than handed a daemon that is not there.
|
||||
foreach my $case (
|
||||
# argv absent commands expected daemon
|
||||
[ '--backend ntpd pool.ntp.org', 'nothing', 'ntpd', 'ntpd is honoured even where chronyd is installed' ],
|
||||
[ '--backend chrony pool.ntp.org','nothing', 'chrony', 'chrony is honoured' ],
|
||||
[ 'pool.ntp.org', 'nothing', 'chrony', 'with no backend given the probe still picks chrony' ],
|
||||
[ 'pool.ntp.org', 'chronyd', 'ntpd', 'with no backend given and no chronyd it falls back' ],
|
||||
[ '--backend chrony pool.ntp.org','chronyd', 'ntpd', 'chrony requested but absent falls back rather than failing' ],
|
||||
[ '--use-ntpd pool.ntp.org', 'nothing', 'ntpd', 'the legacy --use-ntpd flag still forces ntpd' ],
|
||||
[ '--backend ntpd pool.ntp.org', 'ntpd', 'chrony', 'ntpd requested but absent falls back to chrony' ],
|
||||
[ '--use-ntpd pool.ntp.org', 'ntpd', 'chrony', 'the legacy flag falls back the same way' ],
|
||||
[ '--backend ntpd pool.ntp.org', 'ntpd chronyd', 'ntpd', 'with neither daemon present the request is kept' ],
|
||||
[ '--backend ntpd pool.ntp.org', 'ntpd systemctl', 'ntpd', 'chrony without systemctl is no fallback' ],
|
||||
) {
|
||||
my ($argv, $missing, $want, $name) = @$case;
|
||||
my $root = File::Temp::tempdir(CLEANUP => 1);
|
||||
# Every command the extracted region can reach has to be shadowed, not just the ones it
|
||||
# reached when this was written: the region moves. systemctl is here because the
|
||||
# systemd-timesyncd stop/disable was hoisted above the ntpd hand-off and landed inside this
|
||||
# window -- unstubbed, and the suite runs as root in CI, it really disabled timesyncd on the
|
||||
# host running the tests. Bash resolves functions ahead of $PATH, so these win.
|
||||
my $prelude = "logger() { printf '%s\\n' \"\$*\" >>\"$root/log\"; return 0; }\n"
|
||||
. "systemctl() { echo \"systemctl \$*\" >>\"$root/calls\"; return 0; }\n"
|
||||
. "timedatectl() { echo \"timedatectl \$*\" >>\"$root/calls\"; return 0; }\n"
|
||||
. "hwclock() { echo \"hwclock \$*\" >>\"$root/calls\"; return 0; }\n"
|
||||
. "check_executes() { for c in \"\$@\"; do case \" $missing \" in *\" \$c \"*) return 1;; esac; done; return 0; }\n"
|
||||
. "log_label=xcat\nset -- $argv\n";
|
||||
my $out = `bash -c 'exec 2>/dev/null; $prelude$args$select
|
||||
printf "USE_NTPD=%s\\n" "\${USE_NTPD:-}"' 2>/dev/null`;
|
||||
my $got = ($out =~ /USE_NTPD=yes/) ? 'ntpd' : 'chrony';
|
||||
is($got, $want, $name);
|
||||
}
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,121 @@
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../../perl-xCAT";
|
||||
|
||||
use Test::More;
|
||||
|
||||
use xCAT::NTP::Backend;
|
||||
|
||||
# --- normalize: aliases, trimming, case, validation --------------------------------------------
|
||||
is( xCAT::NTP::Backend->normalize(undef), 'auto', 'undefined backend defaults to auto' );
|
||||
is( xCAT::NTP::Backend->normalize(''), 'auto', 'empty backend defaults to auto' );
|
||||
is( xCAT::NTP::Backend->normalize(' Chrony '), 'chrony', 'values are trimmed and lowercased' );
|
||||
is( xCAT::NTP::Backend->normalize('chronyd'), 'chrony', 'chronyd aliases to chrony' );
|
||||
is( xCAT::NTP::Backend->normalize('ntp'), 'ntpd', 'ntp aliases to ntpd' );
|
||||
is( xCAT::NTP::Backend->normalize('ntpsec'), 'ntpd', 'ntpsec aliases to ntpd' );
|
||||
is( xCAT::NTP::Backend->normalize('ntpd'), 'ntpd', 'ntpd is valid' );
|
||||
is( xCAT::NTP::Backend->normalize('bogus'), undef, 'invalid backend is rejected' );
|
||||
|
||||
# --- default_backend: per-distro table ---------------------------------------------------------
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'rhel', version => 6 ), 'ntpd', 'EL6 defaults to ntpd' );
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'rhel', version => 7 ), 'chrony', 'EL7 defaults to chrony' );
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'rhels', version => 8 ), 'chrony', 'EL8 defaults to chrony' );
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'rocky', version => 9 ), 'chrony', 'EL9 clones default to chrony' );
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'alma', version => 10 ), 'chrony', 'EL10 clones default to chrony' );
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'sles', version => 12 ), 'ntpd', 'SLES12 defaults to ntpd' );
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'sles', version => 15 ), 'chrony', 'SLES15 defaults to chrony' );
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'sles', version => 16 ), 'chrony', 'SLES16 defaults to chrony' );
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'leap', version => '15.6' ), 'chrony', 'Leap 15.x defaults to chrony' );
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'ubuntu', version => '18.04' ), 'chrony', 'Ubuntu 18.04 defaults to chrony (timesyncd cannot serve)' );
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'ubuntu', version => '20.04' ), 'chrony', 'Ubuntu 20.04 defaults to chrony' );
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'ubuntu', version => '22.04' ), 'chrony', 'Ubuntu 22.04 defaults to chrony' );
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'ubuntu', version => '24.04' ), 'chrony', 'Ubuntu 24.04 defaults to chrony' );
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'ubuntu', version => '26.04' ), 'chrony', 'Ubuntu 26.04 defaults to chrony' );
|
||||
is( xCAT::NTP::Backend->default_backend( os_name => 'debian', version => 12 ), 'chrony', 'Debian defaults to chrony' );
|
||||
|
||||
# --- choose: explicit override, auto, availability downgrade, install flag ----------------------
|
||||
my $r;
|
||||
|
||||
$r = xCAT::NTP::Backend->choose( requested => 'ntpd', os_name => 'ubuntu', version => '24.04' );
|
||||
is( $r->{name}, 'ntpd', 'explicit ntpd override is honored on Ubuntu' );
|
||||
|
||||
$r = xCAT::NTP::Backend->choose( requested => 'auto', os_name => 'ubuntu', version => '24.04' );
|
||||
is( $r->{name}, 'chrony', 'auto resolves to the Ubuntu default (chrony)' );
|
||||
|
||||
$r = xCAT::NTP::Backend->choose( requested => 'bogus', os_name => 'ubuntu', version => '24.04' );
|
||||
like( $r->{error}, qr/Invalid site\.ntpbackend/, 'invalid override returns an error' );
|
||||
|
||||
# chrony chosen but absent, ntpd present -> downgrade to ntpd (do not install a 2nd daemon)
|
||||
$r = xCAT::NTP::Backend->choose(
|
||||
requested => 'auto', os_name => 'ubuntu', version => '24.04',
|
||||
check_available => 1, available => { chrony => 0, ntpd => 1 } );
|
||||
is( $r->{name}, 'ntpd', 'chrony absent + ntpd present downgrades to ntpd' );
|
||||
is( $r->{downgraded}, 'chrony', 'downgrade records the preferred backend' );
|
||||
is( $r->{install}, 0, 'no install when a supported daemon is already present' );
|
||||
|
||||
# chrony chosen and present -> keep it, no install
|
||||
$r = xCAT::NTP::Backend->choose(
|
||||
requested => 'auto', os_name => 'ubuntu', version => '24.04',
|
||||
check_available => 1, available => { chrony => 1, ntpd => 0 } );
|
||||
is( $r->{name}, 'chrony', 'chrony present keeps chrony' );
|
||||
is( $r->{install}, 0, 'chrony present needs no install' );
|
||||
|
||||
# neither present -> keep the preferred choice and flag install (stock Ubuntu MN)
|
||||
$r = xCAT::NTP::Backend->choose(
|
||||
requested => 'auto', os_name => 'ubuntu', version => '24.04',
|
||||
check_available => 1, available => { chrony => 0, ntpd => 0 } );
|
||||
is( $r->{name}, 'chrony', 'neither present keeps the preferred chrony' );
|
||||
is( $r->{install}, 1, 'neither present flags install of the preferred daemon' );
|
||||
|
||||
# --- available: chrony needs systemd, not just chronyd -----------------------------------------
|
||||
# makentp configures chrony only where systemctl is present, and setupntp hands over to ntpd
|
||||
# without it. The selector has to call chrony usable on the same terms, or makentp silently takes
|
||||
# the ntpd path for a backend the selector reported as available.
|
||||
is( xCAT::NTP::Backend->available( 'chrony', commands => { chronyd => 1, systemctl => 1 } ), 1,
|
||||
'chrony is available where chronyd and systemctl are both present' );
|
||||
is( xCAT::NTP::Backend->available( 'chrony', commands => { chronyd => 1, systemctl => 0 } ), 0,
|
||||
'chronyd without systemctl is not a usable chrony backend' );
|
||||
is( xCAT::NTP::Backend->available( 'chrony', commands => { chronyd => 0, systemctl => 1 } ), 0,
|
||||
'systemctl without chronyd is not a usable chrony backend either' );
|
||||
is( xCAT::NTP::Backend->available( 'ntpd', commands => { ntpd => 1 } ), 1,
|
||||
'ntpd needs only ntpd' );
|
||||
|
||||
$r = xCAT::NTP::Backend->choose(
|
||||
requested => 'chrony', check_available => 1,
|
||||
commands => { chronyd => 1, systemctl => 0, ntpd => 1 } );
|
||||
is( $r->{name}, 'ntpd', 'chronyd without systemctl downgrades to the daemon that can be used' );
|
||||
is( $r->{downgraded}, 'chrony', 'and the downgrade is reported rather than silent' );
|
||||
|
||||
$r = xCAT::NTP::Backend->choose(
|
||||
requested => 'chrony', check_available => 1,
|
||||
commands => { chronyd => 1, systemctl => 0, ntpd => 0 } );
|
||||
is( $r->{install}, 1, 'chronyd without systemctl and no ntpd asks for an install' );
|
||||
|
||||
# --- site.ntpbackend is user facing, so the site table help has to carry it --------------------
|
||||
# The help text is what lsdef -t site -h and tabdump -d print. site.dhcpbackend is documented
|
||||
# there; ntpbackend selects the NTP daemon the same way and was not.
|
||||
require xCAT::Schema;
|
||||
no warnings 'once';
|
||||
my $site_help = $xCAT::Schema::tabspec{site}{descriptions}{key};
|
||||
like( $site_help, qr/^\s*ntpbackend:/m,
|
||||
'the site table help documents ntpbackend' );
|
||||
like( $site_help, qr/ntpbackend:.{0,400}auto/s,
|
||||
'and names auto' );
|
||||
like( $site_help, qr/ntpbackend:.{0,400}chrony/s,
|
||||
'and chrony' );
|
||||
like( $site_help, qr/ntpbackend:.{0,400}ntpd/s,
|
||||
'and ntpd, the values the selector accepts' );
|
||||
|
||||
# The makentp man page lists the site attributes the command honors.
|
||||
my $pod = do {
|
||||
local $/;
|
||||
open my $fh, '<', "$FindBin::Bin/../../xCAT-client/pods/man1/makentp.1.pod"
|
||||
or die "open makentp.1.pod: $!";
|
||||
<$fh>;
|
||||
};
|
||||
like( $pod, qr/site\.ntpbackend/,
|
||||
'the makentp man page lists site.ntpbackend beside ntpservers and extntpservers' );
|
||||
|
||||
done_testing();
|
||||
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Spec;
|
||||
use File::Temp qw(tempdir);
|
||||
use FindBin;
|
||||
use Test::More;
|
||||
|
||||
# Regression: systemd-timesyncd is only stopped on the chrony path.
|
||||
#
|
||||
# setupntp disciplines the clock with whichever daemon it configures, and timesyncd is an SNTP
|
||||
# client that disciplines the same clock. Leaving it running means two things stepping the clock.
|
||||
# The stop/disable sits ~24 lines BELOW the `exec setupntp.traditional` that the ntpd path takes,
|
||||
# and setupntp.traditional never touches timesyncd -- so on the ntpd path it is never reached.
|
||||
#
|
||||
# setupntp resets PATH at the top, so PATH stubs cannot shadow anything in it. The region that
|
||||
# decides this is extracted instead and driven with systemctl and friends shadowed by shell
|
||||
# functions, 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, and the exec ends the run --
|
||||
# which is exactly the behaviour under test.
|
||||
|
||||
my $repo_root = File::Spec->rel2abs(
|
||||
File::Spec->catdir( $FindBin::Bin, '..', '..' )
|
||||
);
|
||||
my $setupntp = File::Spec->catfile( $repo_root, 'xCAT', 'postscripts', 'setupntp' );
|
||||
plan skip_all => "setupntp not found" unless -f $setupntp;
|
||||
|
||||
my $src = do { local $/; open my $fh, '<', $setupntp or die $!; <$fh> };
|
||||
|
||||
# Take the span that contains BOTH the ntpd dispatch and the timesyncd handling, whichever
|
||||
# order they appear in -- the ordering is asserted at runtime below, from what the run actually
|
||||
# recorded, not from where the text sits. Anchored on the dispatch and on the `unset` block that
|
||||
# follows it, so a reordering does not silently shrink the region to cover only one of them.
|
||||
# BAIL_OUT rather than skip, so a rename fails loudly instead of silently covering nothing.
|
||||
my $dispatch_at = index( $src, 'if [ -n "${USE_NTPD}" ]' . "\nthen" );
|
||||
my $timesyncd_at = index( $src, 'systemctl stop systemd-timesyncd.service' );
|
||||
my $end_at = index( $src, '# Unset xCAT passed environment variables' );
|
||||
BAIL_OUT('could not locate the ntpd dispatch in setupntp') if $dispatch_at < 0;
|
||||
BAIL_OUT('could not locate the timesyncd handling in setupntp') if $timesyncd_at < 0;
|
||||
BAIL_OUT('could not locate the end of the dispatch region') if $end_at < 0;
|
||||
|
||||
BAIL_OUT('the timesyncd handling is outside the extracted region')
|
||||
if $timesyncd_at > $end_at;
|
||||
|
||||
# Start at the top of the paragraph the earlier landmark sits in, so an enclosing `if ... then`
|
||||
# comes with its `fi`. Slicing at the landmark itself orphaned the guard and the region would
|
||||
# not parse.
|
||||
my $start_at = $dispatch_at < $timesyncd_at ? $dispatch_at : $timesyncd_at;
|
||||
my $para = rindex( $src, "\n\n", $start_at );
|
||||
$start_at = $para + 2 if $para >= 0;
|
||||
|
||||
my $region = substr( $src, $start_at, $end_at - $start_at );
|
||||
BAIL_OUT('the extracted region does not parse as shell')
|
||||
if system( '/bin/bash', '-n', '-c', "f(){ :; }\n$region" ) != 0;
|
||||
|
||||
my $dir = tempdir( CLEANUP => 1 );
|
||||
my $run = 0;
|
||||
|
||||
sub drive {
|
||||
my ($use_ntpd) = @_;
|
||||
$run++;
|
||||
my $root = File::Spec->catdir( $dir, "run$run" );
|
||||
mkdir $root;
|
||||
my $calls = File::Spec->catfile( $root, 'calls' );
|
||||
|
||||
# The script execs "${0%/*}/setupntp.traditional"; $0 is the harness, so this is where it
|
||||
# lands. Recording rather than executing anything real.
|
||||
my $trad = File::Spec->catfile( $root, 'setupntp.traditional' );
|
||||
open my $t, '>', $trad or die $!;
|
||||
print $t "#!/bin/bash\necho 'EXEC setupntp.traditional' >> '$calls'\nexit 0\n";
|
||||
close $t;
|
||||
chmod 0755, $trad;
|
||||
|
||||
my $harness = File::Spec->catfile( $root, 'harness.sh' );
|
||||
open my $fh, '>', $harness or die $!;
|
||||
print $fh <<"PRE";
|
||||
#!/bin/bash
|
||||
USE_NTPD='$use_ntpd'
|
||||
NTP_SERVERS=(pool.ntp.org)
|
||||
log_label=xcat
|
||||
logger(){ :; }
|
||||
systemctl(){ echo "systemctl \$*" >> '$calls'; return 0; }
|
||||
timedatectl(){ echo "timedatectl \$*" >> '$calls'; return 0; }
|
||||
check_exec_or_exit(){ :; }
|
||||
check_executes(){ return 0; }
|
||||
PRE
|
||||
print $fh $region, "\n";
|
||||
close $fh;
|
||||
|
||||
system( '/bin/bash', $harness );
|
||||
return '' unless -f $calls;
|
||||
return do { local $/; open my $c, '<', $calls or die $!; <$c> };
|
||||
}
|
||||
|
||||
# The chrony path: this already worked, and pins the behaviour we are extending.
|
||||
my $chrony = drive('');
|
||||
like( $chrony, qr/^systemctl stop systemd-timesyncd\.service$/m,
|
||||
'chrony path: timesyncd is stopped' );
|
||||
like( $chrony, qr/^systemctl disable systemd-timesyncd\.service$/m,
|
||||
'chrony path: timesyncd is disabled' );
|
||||
unlike( $chrony, qr/^EXEC setupntp\.traditional$/m,
|
||||
'chrony path: does not hand off to setupntp.traditional' );
|
||||
|
||||
# The ntpd path: it hands off, so anything that must happen has to happen first.
|
||||
my $ntpd = drive('yes');
|
||||
like( $ntpd, qr/^EXEC setupntp\.traditional$/m,
|
||||
'ntpd path: hands off to setupntp.traditional' );
|
||||
like( $ntpd, qr/^systemctl stop systemd-timesyncd\.service$/m,
|
||||
'ntpd path: timesyncd is stopped too' );
|
||||
like( $ntpd, qr/^systemctl disable systemd-timesyncd\.service$/m,
|
||||
'ntpd path: timesyncd is disabled too' );
|
||||
|
||||
my ($before_exec) = $ntpd =~ /\A(.*?)^EXEC setupntp\.traditional/ms;
|
||||
ok( defined $before_exec && $before_exec =~ /systemctl disable systemd-timesyncd/,
|
||||
'ntpd path: the disable happens before the hand-off, not after it' );
|
||||
|
||||
# setupntp.traditional does not do it either, which is why the ordering above matters.
|
||||
my $trad_src = File::Spec->catfile(
|
||||
$repo_root, 'xCAT', 'postscripts', 'setupntp.traditional' );
|
||||
SKIP: {
|
||||
skip 'setupntp.traditional not found', 1 unless -f $trad_src;
|
||||
my $t = do { local $/; open my $h, '<', $trad_src or die $!; <$h> };
|
||||
unlike( $t, qr/timesyncd/,
|
||||
'setupntp.traditional does not handle timesyncd itself' );
|
||||
}
|
||||
|
||||
done_testing();
|
||||
+2
-2
@@ -9,8 +9,8 @@ Homepage: https://xcat.org/
|
||||
|
||||
Package: xcat
|
||||
Architecture: amd64 ppc64el
|
||||
Depends: ${perl:Depends}, goconserver(>= 0.3.3-snap000000000000), xcat-server (>= 2.13-snap000000000000), xcat-client (>= 2.13-snap000000000000), libdbd-sqlite3-perl, isc-dhcp-server | kea, bind9, apache2, nfs-kernel-server, libxml-parser-perl, rsync, tftpd-hpa, libnet-telnet-perl, xcat-genesis-scripts-amd64 (>= 2.13-snap000000000000)
|
||||
Recommends: net-tools, nmap, kea, tftp-hpa, ipmitool-xcat (>= 1.8.17-1), syslinux[any-amd64], libsys-virt-perl, syslinux-xcat, xnba-undi, elilo-xcat, xcat-buildkit (>= 2.13-snap000000000000), xcat-probe (>= 2.13-snap000000000000), xcat-genesis-openembedded-x86-64, xcat-genesis-openembedded-ppc64le, xcat-genesis-openembedded-riscv64
|
||||
Depends: ${perl:Depends}, goconserver(>= 0.3.3-snap000000000000), xcat-server (>= 2.13-snap000000000000), xcat-client (>= 2.13-snap000000000000), libdbd-sqlite3-perl, isc-dhcp-server | kea, bind9, apache2, nfs-kernel-server, libxml-parser-perl, rsync, tftpd-hpa, libnet-telnet-perl, chrony | ntp, xcat-genesis-scripts-amd64 (>= 2.13-snap000000000000)
|
||||
Recommends: net-tools, nmap, kea, tftp-hpa, ipmitool-xcat (>= 1.8.17-1), syslinux[any-amd64], libsys-virt-perl, syslinux-xcat, xnba-undi, elilo-xcat, util-linux-extra, xcat-buildkit (>= 2.13-snap000000000000), xcat-probe (>= 2.13-snap000000000000), xcat-genesis-openembedded-x86-64, xcat-genesis-openembedded-ppc64le, xcat-genesis-openembedded-riscv64
|
||||
Suggests: yaboot-xcat
|
||||
Description: Metapackage for a common, default xCAT setup
|
||||
xCAT is Extreme Cluster/Cloud Administration Toolkit. xCAT offers complete
|
||||
|
||||
@@ -103,7 +103,17 @@ do
|
||||
case "$1" in
|
||||
"--use-ntpd")
|
||||
# Use traditional ntpd
|
||||
USE_NTPD="yes"
|
||||
NTP_BACKEND="ntpd"
|
||||
;;
|
||||
"--backend")
|
||||
# The daemon xCAT::NTP::Backend picked for this cluster, passed by makentp so
|
||||
# site.ntpbackend reaches the nodes and not just the management node.
|
||||
shift
|
||||
case "$1" in
|
||||
"chrony"|"chronyd") NTP_BACKEND="chrony" ;;
|
||||
"ntp"|"ntpd") NTP_BACKEND="ntpd" ;;
|
||||
*) logger -t $log_label -p local4.warning "setupntp: ignoring unknown --backend '$1'" ;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
NTP_SERVERS+=($1)
|
||||
@@ -129,8 +139,39 @@ then
|
||||
esac
|
||||
fi
|
||||
|
||||
check_executes chronyd >/dev/null 2>&1 || USE_NTPD="yes"
|
||||
check_executes systemctl >/dev/null 2>&1 || USE_NTPD="yes"
|
||||
# The requested backend is a preference, not a guarantee: a node that does not have the
|
||||
# requested daemon still has to be configured, so use the other one and say so.
|
||||
if [ "${NTP_BACKEND}" = "ntpd" ]
|
||||
then
|
||||
USE_NTPD="yes"
|
||||
if ! check_executes ntpd >/dev/null 2>&1 &&
|
||||
check_executes chronyd >/dev/null 2>&1 &&
|
||||
check_executes systemctl >/dev/null 2>&1
|
||||
then
|
||||
USE_NTPD=""
|
||||
logger -t $log_label -p local4.warning \
|
||||
"setupntp: ntpd requested but ntpd is absent; using chrony"
|
||||
fi
|
||||
else
|
||||
check_executes chronyd >/dev/null 2>&1 || USE_NTPD="yes"
|
||||
check_executes systemctl >/dev/null 2>&1 || USE_NTPD="yes"
|
||||
if [ -n "${USE_NTPD}" ] && [ "${NTP_BACKEND}" = "chrony" ]
|
||||
then
|
||||
logger -t $log_label -p local4.warning \
|
||||
"setupntp: chrony requested but chronyd/systemctl are absent; using ntpd"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Both backends discipline the clock, and so does systemd-timesyncd, so it has to go either
|
||||
# way. Installing chrony or ntpsec usually displaces it already -- both Conflicts: time-daemon --
|
||||
# but that only helps when an install actually happens; on a re-run, or where the daemon was
|
||||
# already present, timesyncd is still enabled. This must stay ABOVE the ntpd hand-off below:
|
||||
# that path execs setupntp.traditional, which never returns and never touches timesyncd.
|
||||
if check_executes systemctl >/dev/null 2>&1
|
||||
then
|
||||
systemctl stop systemd-timesyncd.service 2>/dev/null
|
||||
systemctl disable systemd-timesyncd.service 2>/dev/null
|
||||
fi
|
||||
|
||||
if [ -n "${USE_NTPD}" ]
|
||||
then
|
||||
@@ -146,7 +187,10 @@ unset MASTER
|
||||
unset NTPSERVERS
|
||||
|
||||
check_exec_or_exit cp cat logger grep
|
||||
check_exec_or_exit systemctl timedatectl hwclock
|
||||
# hwclock is NOT required: it only persists the already-stepped clock to the RTC below, and on
|
||||
# Ubuntu 24.04+ it moved to util-linux-extra, absent on minimal images. Requiring it here aborted
|
||||
# the whole NTP setup, including the clock step that does not use it.
|
||||
check_exec_or_exit systemctl timedatectl
|
||||
|
||||
systemctl stop ntp.service 2>/dev/null
|
||||
systemctl disable ntp.service 2>/dev/null
|
||||
@@ -177,10 +221,14 @@ chronyd -f /dev/null -q "$(
|
||||
fi
|
||||
)"
|
||||
|
||||
rm -f /etc/adjtime
|
||||
# Set the hardware clock from the system clock
|
||||
hwclock --systohc --utc
|
||||
warn_if_bad "$?" "Failed to set the hardware clock"
|
||||
# Set the hardware clock from the system clock, when hwclock is available (optional; see above).
|
||||
if command -v hwclock >/dev/null 2>&1; then
|
||||
rm -f /etc/adjtime
|
||||
hwclock --systohc --utc
|
||||
warn_if_bad "$?" "Failed to set the hardware clock"
|
||||
else
|
||||
logger -t $log_label -p local4.info "setupntp: hwclock not present; skipping RTC persist (system clock already set via chronyd)"
|
||||
fi
|
||||
|
||||
## On RHEL 8
|
||||
#CHRONY_USER="chrony"
|
||||
|
||||
@@ -73,6 +73,9 @@ Requires: httpd nfs-utils nmap bind perl(CGI)
|
||||
# on RHEL7, need to specify it explicitly
|
||||
Requires: net-tools
|
||||
Requires: /usr/bin/killall
|
||||
# makentp/setupntp configure the MN as an NTP server for its compute nodes and support chronyd/ntpd
|
||||
# only. chrony is the default on EL7+/SLES15+ (and the only option on EL8+); ntp covers the rest.
|
||||
Requires: (chrony or ntp)
|
||||
# DHCP backend resolved at INSTALL time (not build time) via an RPM rich
|
||||
# dependency, so a single flat xcat-core build is correct on every EL: el10+
|
||||
# dropped ISC dhcp from its distro and uses Kea; el8/el9 use ISC dhcpd. SLES
|
||||
|
||||
Reference in New Issue
Block a user