diff --git a/xCAT-server/lib/xcat/plugins/makentp.pm b/xCAT-server/lib/xcat/plugins/makentp.pm index 620a45e85..1dd8e0d8c 100644 --- a/xCAT-server/lib/xcat/plugins/makentp.pm +++ b/xCAT-server/lib/xcat/plugins/makentp.pm @@ -261,13 +261,22 @@ sub process_request { send_msg(\%request, 1, $ntp_backend->{error}); return 1; } + if ($ntp_backend->{downgraded}) { + send_msg(\%request, 0, + "NTP backend $ntp_backend->{downgraded} is not installed; using $ntp_backend->{name} instead."); + } + if ($ntp_backend->{install}) { + send_msg(\%request, 1, + "Neither chrony nor ntp is installed on $nodename. Install $ntp_backend->{name}, or set site.ntpbackend to the daemon you have."); + return 1; + } my $have_systemctl = (-x "/usr/bin/systemctl" || -x "/bin/systemctl"); # Handle chronyd here, if ($ntp_backend->{name} eq 'chrony' && $have_systemctl) { send_msg(\%request, 0, "Will configure chronyd instead."); - my $cmd = "/install/postscripts/setupntp " . + my $cmd = "/install/postscripts/setupntp --backend $ntp_backend->{name} " . join(' ', split(',', $ntp_servers)); send_msg(\%request, 0, "Calling ... " . $cmd); @@ -497,12 +506,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 ); diff --git a/xCAT-test/unit/makentp_ntp_deps.t b/xCAT-test/unit/makentp_ntp_deps.t index f4fbe4e29..a849fe311 100644 --- a/xCAT-test/unit/makentp_ntp_deps.t +++ b/xCAT-test/unit/makentp_ntp_deps.t @@ -25,9 +25,13 @@ my $source = join '', @lines; # 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) = @_; @@ -123,4 +127,27 @@ SKIP: { '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. +foreach my $case ( + # argv chronyd present? 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' ], +) { + my ($argv, $missing, $want, $name) = @$case; + my $root = File::Temp::tempdir(CLEANUP => 1); + my $prelude = "logger() { printf '%s\\n' \"\$*\" >>\"$root/log\"; return 0; }\n" + . "check_executes() { for c in \"\$@\"; do [ \"\$c\" = \"$missing\" ] && return 1; 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(); diff --git a/xCAT/postscripts/setupntp b/xCAT/postscripts/setupntp index 2cc903ff3..e1d3cf9de 100755 --- a/xCAT/postscripts/setupntp +++ b/xCAT/postscripts/setupntp @@ -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,20 @@ 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 chronyd +# still has to be configured, so fall back to ntpd and say so rather than failing. +if [ "${NTP_BACKEND}" = "ntpd" ] +then + USE_NTPD="yes" +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 if [ -n "${USE_NTPD}" ] then