mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-09 05:00:44 +00:00
a6212e8384
xCAT::NTP::Backend->available reported chrony as available on chronyd alone, while makentp configured chrony only where systemctl was present too. On a host with chronyd and no systemctl the selector returned chrony with no downgrade, makentp fell through to the ntpd path, and the admin saw either a silent switch or "Please make sure ntpd is installed". available now requires chronyd and systemctl for chrony, so the selector answers on the same terms makentp acts on, and makentp branches on the name alone. choose therefore downgrades to ntpd, or reports install, in the case it used to pass over. A commands argument injects the command probe, in the same shape as the existing available argument. ntp_backend_selection.t covers both commands. Six of its assertions fail without this change. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
595 lines
18 KiB
Perl
595 lines
18 KiB
Perl
#!/usr/bin/env perl
|
|
# IBM(c) 2015 EPL license http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
package xCAT_plugin::makentp;
|
|
|
|
BEGIN
|
|
{
|
|
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
|
|
}
|
|
use lib "$::XCATROOT/lib/perl";
|
|
|
|
use Getopt::Long;
|
|
use xCAT::Usage;
|
|
use xCAT::NetworkUtils;
|
|
use xCAT::TableUtils;
|
|
use xCAT::Utils;
|
|
use XML::Simple;
|
|
no strict;
|
|
use Data::Dumper;
|
|
use Socket;
|
|
|
|
my %globalopt;
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
=head1 xCAT_plugin:makentp
|
|
=head2 Package Description
|
|
Handles ntp server setup on a xCAT management node.
|
|
=cut
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 send_msg
|
|
Invokes the callback with the specified message
|
|
Arguments:
|
|
request: request structure for plguin calls
|
|
ecode: error code. 0 for succeful.
|
|
msg: messages to be displayed.
|
|
Returns:
|
|
none
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub send_msg {
|
|
|
|
my $request = shift;
|
|
my $ecode = shift;
|
|
my $msg = shift;
|
|
my %output;
|
|
|
|
# Called from child process - send to parent
|
|
if (exists($request->{pipe})) {
|
|
my $out = $request->{pipe};
|
|
|
|
$output{errorcode} = $ecode;
|
|
$output{data} = \@_;
|
|
print $out freeze([ \%output ]);
|
|
print $out "\nENDOFFREEZE6sK4ci\n";
|
|
}
|
|
|
|
# Called from parent - invoke callback directly
|
|
elsif (exists($request->{callback})) {
|
|
my $callback = $request->{callback};
|
|
$output{errorcode} = $ecode;
|
|
$output{data}->[0] = $msg;
|
|
$callback->(\%output);
|
|
}
|
|
}
|
|
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 handled_commands
|
|
It returns a list of commands handled by this plugin.
|
|
Arguments:
|
|
none
|
|
Returns:
|
|
a list of commands.
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub handled_commands {
|
|
return ({ makentp => "makentp" });
|
|
}
|
|
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 parse_args
|
|
Parse the command line options and operands.
|
|
Arguments:
|
|
request: the request structure for plugin
|
|
Returns:
|
|
Usage string or error message.
|
|
0 if no user promp needed.
|
|
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub parse_args {
|
|
|
|
my $request = shift;
|
|
my $args = $request->{arg};
|
|
my $cmd = $request->{command};
|
|
my %opt;
|
|
|
|
# Responds with usage statement
|
|
local *usage = sub {
|
|
my $usage_string = xCAT::Usage->getUsage($cmd);
|
|
send_msg($request, 0, " $usage_string");
|
|
return ([ $_[0], $usage_string ]);
|
|
};
|
|
|
|
# No command-line arguments - use defaults
|
|
if (!defined($args)) {
|
|
return (0);
|
|
}
|
|
|
|
# Checks case in GetOptions, allows opts
|
|
# to be grouped (e.g. -vx), and terminates
|
|
# at the first unrecognized option.
|
|
@ARGV = @$args;
|
|
$Getopt::Long::ignorecase = 0;
|
|
Getopt::Long::Configure("bundling");
|
|
Getopt::Long::Configure("no_pass_through");
|
|
|
|
# Process command-line flags
|
|
if (!GetOptions(\%opt,
|
|
qw(h|help V|verbose v|version a|all))) {
|
|
return (usage());
|
|
}
|
|
|
|
# Option -V for verbose output
|
|
if (exists($opt{V})) {
|
|
$globalopt{verbose} = 1;
|
|
}
|
|
|
|
if (exists($opt{a})) {
|
|
$globalopt{a} = 1;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 preprocess_request
|
|
Parse the arguments and display the usage or the version string.
|
|
|
|
=cut
|
|
|
|
#--------------------------------------------------------------------------------
|
|
sub preprocess_request {
|
|
my $req = shift;
|
|
if ($req->{_xcatpreprocessed}->[0] == 1) { return [$req]; }
|
|
my $callback = shift;
|
|
|
|
my $command = $req->{command}->[0];
|
|
my $extrargs = $req->{arg};
|
|
my @exargs = ($req->{arg});
|
|
if (ref($extrargs)) {
|
|
@exargs = @$extrargs;
|
|
}
|
|
|
|
# Build hash to pass around
|
|
my %request;
|
|
$request{arg} = $extrargs;
|
|
$request{callback} = $callback;
|
|
$request{command} = $command;
|
|
|
|
my $usage_string = xCAT::Usage->parseCommand($command, @exargs);
|
|
if ($usage_string) {
|
|
$callback->({ data => [$usage_string] });
|
|
$req = {};
|
|
return;
|
|
}
|
|
|
|
my $result = parse_args(\%request);
|
|
if (ref($result) eq 'ARRAY') {
|
|
send_msg(\%request, 1, @$result);
|
|
return (1);
|
|
}
|
|
|
|
# add current request
|
|
my @result = ();
|
|
my $reqcopy = {%$req};
|
|
$reqcopy->{_xcatpreprocessed}->[0] = 1;
|
|
if (xCAT::Utils->isMN() && exists($globalopt{a})) {
|
|
$reqcopy->{_all}->[0] = 1;
|
|
}
|
|
if (exists($globalopt{verbose})) {
|
|
$reqcopy->{_verbose}->[0] = 1;
|
|
}
|
|
push @result, $reqcopy;
|
|
|
|
return \@result;
|
|
}
|
|
|
|
#--------------------------------------------------------------------------------
|
|
|
|
=head3 process_request
|
|
Pasrse the arguments and call the correspondent functions
|
|
to do switch discovery.
|
|
|
|
=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;
|
|
my $sub_req = shift;
|
|
|
|
# Build hash to pass around
|
|
my %request;
|
|
$request{arg} = $req->{arg};
|
|
$request{callback} = $callback;
|
|
$request{command} = $req->{command}->[0];
|
|
|
|
my $verbose;
|
|
if ($req->{_verbose}->[0] == 1) {
|
|
$verbose = 1;
|
|
}
|
|
|
|
my @nodeinfo = xCAT::NetworkUtils->determinehostname();
|
|
my $nodename = pop @nodeinfo;
|
|
|
|
if (xCAT::Utils->isMN()) {
|
|
send_msg(\%request, 0, "configuring management node: $nodename.");
|
|
} else {
|
|
send_msg(\%request, 0, "configuring service node: $nodename.");
|
|
}
|
|
|
|
# get site.extntpservers for mn, for sn use mn as the server
|
|
my $ntp_servers;
|
|
my $ntp_master;
|
|
my $ntp_attrib;
|
|
if (xCAT::Utils->isMN()) {
|
|
$ntp_attrib = "extntpservers";
|
|
} else {
|
|
$ntp_attrib = "ntpservers";
|
|
}
|
|
my @entries = xCAT::TableUtils->get_site_attribute($ntp_attrib);
|
|
my $ntp_servers = $entries[0];
|
|
|
|
if (!xCAT::Utils->isMN() && ((!$ntp_servers) ||
|
|
(($ntp_servers) && ($ntp_servers =~ /<xcatmaster>/)))) {
|
|
my $retdata = xCAT::ServiceNodeUtils->readSNInfo($nodename);
|
|
$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 ($ntp_action->{name} eq 'chrony') {
|
|
send_msg(\%request, 0, "Will configure chronyd instead.");
|
|
|
|
my $cmd = setupntp_command($ntp_action->{name}, $ntp_servers);
|
|
send_msg(\%request, 0, "Calling ... " . $cmd);
|
|
|
|
my $result = xCAT::Utils->runcmd($cmd, 0);
|
|
if ($::RUNCMD_RC != 0) {
|
|
send_msg(\%request, 1, "Error from command: $cmd\n $result");
|
|
return 1;
|
|
}
|
|
|
|
send_msg(\%request, 0, "Daemon chronyd configured.");
|
|
|
|
# Cannot find a better way other than use goto statement :-/
|
|
goto HANDLE_MAKENTP_A;
|
|
}
|
|
|
|
#check if ntp is installed or not
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...checking if nptd is installed.");
|
|
}
|
|
if (!-f "/usr/sbin/ntpd") {
|
|
send_msg(\%request, 1, "Please make sure ntpd is installed on $nodename.");
|
|
return 1;
|
|
}
|
|
|
|
#configure the ntp configuration file
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...backing up the ntp configuration file /etc/ntp.conf.");
|
|
}
|
|
my $ntpcfg = "/etc/ntp.conf";
|
|
my $ntpcfgbackup = "/etc/ntp.conf.orig";
|
|
my $ntpxcatcfgbackup = "/etc/ntp.conf.xcatbackup";
|
|
if (-e $ntpcfg) {
|
|
if (!-e $ntpcfgbackup) {
|
|
|
|
# if original backup does not already exist
|
|
my $cmd = "mv $ntpcfg $ntpcfgbackup";
|
|
my $result = xCAT::Utils->runcmd($cmd, 0);
|
|
if ($::RUNCMD_RC != 0) {
|
|
send_msg(\%request, 1, "Error from command:$cmd\n $result");
|
|
return 1;
|
|
}
|
|
}
|
|
else {
|
|
# backup xcat cfg
|
|
my $cmd = "rm $ntpxcatcfgbackup;mv $ntpcfg $ntpxcatcfgbackup";
|
|
my $result = xCAT::Utils->runcmd($cmd, 0);
|
|
if ($::RUNCMD_RC != 0) {
|
|
send_msg(\%request, 1, "Error from command:$cmd\n $result.");
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...changing the ntp configuration file /etc/ntp.conf.\n ntp servers are: $ntp_servers");
|
|
}
|
|
|
|
# create ntp server config file
|
|
open(CFGFILE, ">$ntpcfg")
|
|
or xCAT::MsgUtils->message('SE',
|
|
"Cannot open $ntpcfg for NTP update. \n");
|
|
|
|
print CFGFILE "restrict default nomodify notrap nopeer noquery\n";
|
|
print CFGFILE "restrict 127.0.0.1\n";
|
|
print CFGFILE "restrict ::1\n\n";
|
|
|
|
if (defined($ntp_servers) && $ntp_servers) {
|
|
my @npt_server_array = split(',', $ntp_servers);
|
|
|
|
# add ntp servers one by one
|
|
foreach my $ntps (@npt_server_array) {
|
|
if (!$ntp_master) { $ntp_master = $ntps; }
|
|
print CFGFILE "server $ntps iburst\n";
|
|
}
|
|
}
|
|
|
|
my $os = xCAT::Utils->osver("all");
|
|
|
|
#for sles, /var/lib/ntp/drift is a dir
|
|
if (xCAT::Utils->isAIX()) {
|
|
print CFGFILE "driftfile /etc/ntp.drift\n";
|
|
print CFGFILE "tracefile /etc/ntp.trace\n";
|
|
print CFGFILE "broadcastclient\n";
|
|
} elsif ($os =~ /sles/) {
|
|
print CFGFILE "driftfile /var/lib/ntp/drift/ntp.drift\n";
|
|
} else {
|
|
print CFGFILE "driftfile /var/lib/ntp/drift\n";
|
|
}
|
|
|
|
#add xCAT mn/sn itself as a server
|
|
print CFGFILE "server 127.127.1.0\n";
|
|
print CFGFILE "fudge 127.127.1.0 stratum 10\n";
|
|
|
|
print CFGFILE "disable monitor\n";
|
|
|
|
close CFGFILE;
|
|
|
|
my $ntp_service = "ntpserver";
|
|
|
|
#stop ntpd
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...stopping $ntp_service");
|
|
}
|
|
my $rc = xCAT::Utils->stopservice($ntp_service);
|
|
if ($rc != 0) {
|
|
send_msg(\%request, 1, "Failed to stop nptd on $nodename.");
|
|
return 1;
|
|
}
|
|
|
|
#update the time now
|
|
if ($ntp_master) {
|
|
my $cmd;
|
|
if ($os =~ /sles/) {
|
|
if (-f "/usr/sbin/rcntpd") {
|
|
$cmd = "/usr/sbin/rcntpd ntptimeset";
|
|
} elsif (-f "/usr/sbin/rcntp") {
|
|
$cmd = "/usr/sbin/rcntp ntptimeset";
|
|
} else {
|
|
$cmd = "sntp -P no -r $ntp_master";
|
|
}
|
|
} else {
|
|
$cmd = "ntpdate -t5 $ntp_master";
|
|
}
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...updating the time now. $cmd");
|
|
}
|
|
my $result = xCAT::Utils->runcmd($cmd, 0);
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " $result");
|
|
}
|
|
if ($::RUNCMD_RC != 0) {
|
|
send_msg(\%request, 1, "Error from command $cmd\n $result.");
|
|
send_msg(\%request, 1, "Please check $ntp_master, make sure time is synced (can be validated by 'ntpq -p'), then rerun makentp command again ");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
#setup the hardware clock
|
|
my $hwcmd = "/sbin/hwclock --systohc --utc";
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...updating the hwclock now. $hwcmd");
|
|
}
|
|
my $hwresult = xCAT::Utils->runcmd($hwcmd, 0);
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " $hwresult");
|
|
}
|
|
if ($::RUNCMD_RC != 0) {
|
|
send_msg(\%request, 1, "Error from command $hwcmd\n $hwresult.");
|
|
return 1;
|
|
}
|
|
|
|
my $grep_cmd;
|
|
my $rc;
|
|
|
|
#setup the RTC is UTC format, which will be used by os
|
|
if ($os =~ /sles/) {
|
|
$grep_cmd = "grep -i HWCLOCK /etc/sysconfig/clock";
|
|
$rc = xCAT::Utils->runcmd($grep_cmd, 0);
|
|
if ($::RUNCMD_RC == 0) {
|
|
`sed -i 's/.*HWCLOCK.*/HWCLOCK=\"-u\"/' /etc/sysconfig/clock`;
|
|
} else {
|
|
`echo HWCLOCK=\"-u\" >> /etc/sysconfig/clock`;
|
|
}
|
|
} elsif (-f "/etc/debian_version") {
|
|
`sed -i 's/.*UTC.*/UTC=\"yes\"/' /etc/default/rcS`;
|
|
} else {
|
|
if (-f "/etc/sysconfig/clock") {
|
|
$grep_cmd = "grep -i utc /etc/sysconfig/clock";
|
|
$rc = xCAT::Utils->runcmd($grep_cmd, 0);
|
|
if ($::RUNCMD_RC == 0) {
|
|
`sed -i 's/.*UTC.*/UTC=\"yes\"/' /etc/sysconfig/clock`;
|
|
} else {
|
|
`echo UTC=\"yes\" >> /etc/sysconfig/clock`;
|
|
}
|
|
} else {
|
|
`type -P timedatectl >/dev/null 2>&1`;
|
|
`timedatectl set-local-rtc 0`;
|
|
}
|
|
}
|
|
|
|
#update the hardware clock automaticly
|
|
if (-f "/etc/sysconfig/ntpd") {
|
|
$grep_cmd = "grep -i SYNC_HWCLOCK /etc/sysconfig/ntpd";
|
|
$rc = xCAT::Utils->runcmd($grep_cmd, 0);
|
|
if ($::RUNCMD_RC == 0) {
|
|
`sed -i 's/.*SYNC_HWCLOCK.*/SYNC_HWCLOCK=\"yes\"/' /etc/sysconfig/ntpd`;
|
|
} else {
|
|
`echo SYNC_HWCLOCK=\"yes\" >> /etc/sysconfig/ntpd`;
|
|
}
|
|
} elsif (-f "/etc/sysconfig/ntp") {
|
|
`sed -i 's/.*SYNC_HWCLOCK.*/NTPD_FORCE_SYNC_HWCLOCK_ON_STARTUP=\"yes\"/' /etc/sysconfig/ntp`;
|
|
`sed -i 's/^NTPD_FORCE_SYNC_ON.*/NTPD_FORCE_SYNC_ON_STARTUP=\"yes\"/' /etc/sysconfig/ntp`;
|
|
`sed -i 's/.*RUN_CHROOTED.*/NTPD_RUN_CHROOTED=\"yes\"/' /etc/sysconfig/ntp`;
|
|
} else {
|
|
my $cron_file = "/etc/cron.daily/xcatsethwclock";
|
|
if (!-f "$cron_file") {
|
|
`echo "#!/bin/sh" > $cron_file`;
|
|
`echo "/sbin/hwclock --systohc --utc" >> $cron_file`;
|
|
`chmod a+x $cron_file`;
|
|
|
|
#service cron restart
|
|
xCAT::Utils->startservice("cron");
|
|
}
|
|
}
|
|
|
|
#start ntpd
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...starting $ntp_service");
|
|
}
|
|
my $rc = xCAT::Utils->startservice($ntp_service);
|
|
if ($rc != 0) {
|
|
send_msg(\%request, 1, "Failed to start nptd on $nodename.");
|
|
return 1;
|
|
}
|
|
|
|
#enable ntpd for node reboot
|
|
if ($verbose) {
|
|
send_msg(\%request, 0, " ...enabling $ntp_service");
|
|
}
|
|
xCAT::Utils->enableservice($ntp_service);
|
|
|
|
HANDLE_MAKENTP_A:
|
|
|
|
#now handle sn that has ntpserver=1 set in servicenode table.
|
|
# this part is called by makentp -a.
|
|
if ($req->{_all}->[0] == 1) {
|
|
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", $sn_script ],
|
|
},
|
|
$sub_req, -1, 1
|
|
);
|
|
my $retcode=$::RUNCMD_RC;
|
|
my $msg;
|
|
foreach my $line (@$ret) {
|
|
$msg .= "$line\n";
|
|
}
|
|
send_msg(\%request, $retcode, "$msg");
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
1;
|