2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2025-05-29 17:23:08 +00:00

Merge pull request #3456 from robin2008/nodeset-disjoint

Fix #3088, Using 'disjointdhcps' site attribute to make nodeset request will only be handled on service node for nodes it manages
This commit is contained in:
zet809 2017-08-02 15:10:27 +08:00 committed by GitHub
commit 8fbf24d781
10 changed files with 674 additions and 289 deletions

View File

@ -110,8 +110,9 @@ site Attributes:
dhcplease: The lease time for the dhcp client. The default value is 43200.
disjointdhcps: If set to '1', the .leases file on a service node only contains
the nodes it manages. The default value is '0'.
'0' value means include all the nodes in the subnet.
the nodes it manages. And when 'sharedtftp' is disabled, nodeset handles
boot loader configuration on a service node only for the nodes it manages.
The default value is '0'. It means include all the nodes in the subnet.
pruneservices: Whether to enable service pruning when noderm is run (i.e.
removing DHCP entries when noderm is executed)

View File

@ -19,7 +19,7 @@ Name
****************
\ **nodeset**\ \ *noderange*\ [\ **boot**\ | \ **stat**\ | \ **offline**\ | \ **runcmd=bmcsetup**\ | \ **osimage**\ [=\ *imagename*\ ] | \ **shell**\ | \ **shutdown**\ ]
\ **nodeset**\ \ *noderange*\ [\ **boot**\ | \ **stat**\ [\ **-a**\ ]| \ **offline**\ | \ **runcmd=bmcsetup**\ | \ **osimage**\ [=\ *imagename*\ ] | \ **shell**\ | \ **shutdown**\ ]
\ **nodeset**\ \ *noderange*\ \ **osimage**\ [=\ *imagename*\ ] [\ **-**\ **-noupdateinitrd**\ ] [\ **-**\ **-ignorekernelchk**\ ]
@ -101,7 +101,7 @@ A user can supply their own scripts to be run on the mn or on the service node (
\ **stat**\
Display the current boot loader config file description for the nodes requested
Display the current boot loader config file description for the nodes requested. When \ **disjointdhcps**\ is set, using \ **-a**\ to display them on all available service nodes.

View File

@ -1645,10 +1645,14 @@ sub getNodeIPaddress
{
require xCAT::Table;
my $nodetocheck = shift;
my $port = shift;
my $nodeip;
if ($nodetocheck eq 'xCAT::NetworkUtils') { #was called with -> syntax
$nodetocheck = shift;
}
$nodeip = xCAT::NetworkUtils->getipaddr($nodetocheck);
# Quick return if pass in an IP
return $nodetocheck if (xCAT::NetworkUtils->isIpaddr($nodetocheck));
my $nodeip = xCAT::NetworkUtils->getipaddr($nodetocheck);
if (!$nodeip)
{
my $hoststab = xCAT::Table->new('hosts');
@ -1666,6 +1670,51 @@ sub getNodeIPaddress
}
#-------------------------------------------------------------------------------
=head3 checkNodeIPaddress
Arguments:
Node name only one at a time
Returns: a hash object contains IP or Error
Globals:
none
Example: my $ipresult = xCAT::NetworkUtils::checkNodeIPaddress($nodetocheck);
=cut
#-------------------------------------------------------------------------------
sub checkNodeIPaddress
{
require xCAT::Table;
my $nodetocheck = shift;
if ($nodetocheck eq 'xCAT::NetworkUtils') { #was called with -> syntax
$nodetocheck = shift;
}
my $ret;
my $nodeip;
my $hoststab = xCAT::Table->new('hosts');
my $ent = $hoststab->getNodeAttribs($nodetocheck, ['ip']);
if ($ent->{'ip'}) {
$nodeip = $ent->{'ip'};
}
# Get the IP from DNS
my $dnsip = xCAT::NetworkUtils->getipaddr($nodetocheck);
if (!$dnsip)
{
$ret->{'error'} = "The $nodetocheck can not be resolved.";
$ret->{'ip'} = $nodeip if ($nodeip);
} elsif (!$nodeip) {
$ret->{'ip'} = $dnsip;
} else {
$ret->{'ip'} = $nodeip;
$ret->{'error'} = "Defined IP address of $nodetocheck is inconsistent with DNS." if ($nodeip ne $dnsip);
}
return $ret;
}
#-------------------------------------------------------------------------------

View File

@ -1029,8 +1029,9 @@ passed as argument rather than by table value',
" dhcpsetup: If set to 'n', it will skip the dhcp setup process in the nodeset cmd.\n\n" .
" dhcplease: The lease time for the dhcp client. The default value is 43200.\n\n" .
" disjointdhcps: If set to '1', the .leases file on a service node only contains\n" .
" the nodes it manages. The default value is '0'.\n" .
" '0' value means include all the nodes in the subnet.\n\n" .
" the nodes it manages. And when 'sharedtftp' is disabled, nodeset handles\n" .
" boot loader configuration on a service node only for the nodes it manages.\n" .
" The default value is '0'. It means include all the nodes in the subnet.\n\n" .
" pruneservices: Whether to enable service pruning when noderm is run (i.e.\n" .
" removing DHCP entries when noderm is executed)\n\n" .
" managedaddressmode: The mode of networking configuration during node provision.\n" .

View File

@ -2,6 +2,7 @@ package xCAT::Scope;
use xCAT::Utils;
use xCAT::Table;
use xCAT::TableUtils;
use xCAT::ServiceNodeUtils qw(getSNList);
@ -125,16 +126,17 @@ sub get_parallel_scope {
=head3 get_broadcast_scope_with_parallel
Convert a request object to an array of multiple requests according to the
splitted node range.
splitted node range. Also it replicates the requests to all required service
nodes or management node.
Arguments:
Reference of request
Callback: TODO, Optional, the Callback will be used to filter the nodes
SN list: Array of target service nodes
Returns: An array of requests
Error:
none
Example:
my $reqs = xCAT::Scope->get_broadcast_scope($request);
my $reqs = xCAT::Scope->get_broadcast_scope_with_parallel($request, \@snlist);
=cut
#-----------------------------------------------------------------------------
@ -145,25 +147,118 @@ sub get_broadcast_scope_with_parallel {
}
#Exit if the packet has been preprocessed in its history
if ($req->{_xcatpreprocessed}->[0] == 1) { return [$req]; }
$req->{_xcatpreprocessed}->[0] = 1;
#Handle the one for current management/service node
my $reqs = get_parallel_scope($req);
my @requests = @$reqs;
my $snlist = shift;
#Broadcast the request to other management/service nodes
foreach (xCAT::ServiceNodeUtils->getSNList()) {
if (xCAT::NetworkUtils->thishostisnot($_)) {
my $xcatdest = $_;
my $reqcopy = {%$req};
$reqcopy->{'_xcatdest'} = $_;
$reqcopy->{_xcatpreprocessed}->[0] = 1;
#Apply callback to filter the node range in future.
$reqs = get_parallel_scope($reqcopy);
foreach (@$reqs) {
push @requests, {%$_};
}
my $reqs = get_parallel_scope($req);
my @requests = (); # The request array will be return.
push @requests, @$reqs;
# when this method is called on service node, it is required to broadcast to MN too.
# get site.master from DB in order to dispatch to MN ( MN will not be added in servicenode table)
if ( xCAT::Utils->isServiceNode() ) {
my @entries = xCAT::TableUtils->get_site_attribute("master");
my $master = $entries[0];
foreach (@$reqs) {
my $reqcopy = {%$_};
$reqcopy->{'_xcatdest'} = $master;
push @requests, $reqcopy;
}
}
#Broadcast the request to all service nodes
foreach (@$snlist) {
my $xcatdest = $_;
next unless (xCAT::NetworkUtils->thishostisnot($xcatdest));
foreach (@$reqs) {
my $reqcopy = {%$_};
$reqcopy->{'_xcatdest'} = $xcatdest;
push @requests, $reqcopy;
}
}
return \@requests;
}
#-----------------------------------------------------------------------------
=head3 get_broadcast_disjoint_scope_with_parallel
Convert a request object to an array of multiple requests according to the
splitted node range. Also it replicates the requests to all required service
nodes or management node, but the request to a service node will only contains
the node range it manages.
Arguments:
Reference of request
SN hash: Hash of target service nodes => Managed CNs
Special servers: Array of servers, those servers are required to handle whole noderange.
Returns: An array of requests
Error:
none
Example:
my $reqs = xCAT::Scope->get_broadcast_disjoint_scope_with_parallel($request, \@snhash);
=cut
#-----------------------------------------------------------------------------
sub get_broadcast_disjoint_scope_with_parallel {
my $req = shift;
if ($req =~ /xCAT::Scope/) {
$req = shift;
}
#Exit if the packet has been preprocessed in its history
if ($req->{_xcatpreprocessed}->[0] == 1) { return [$req]; }
$req->{_xcatpreprocessed}->[0] = 1;
my $sn_hash = shift;
my $extras = shift;
my @requests = (); # The request array will be return.
my $reqs = get_parallel_scope($req);
my $handled4me = 0; # indicate myself is already handled.
my %prehandledhash = ();# the servers which is already handled.
foreach (@$extras) {
my $xcatdest = $_;
if (xCAT::NetworkUtils->thishostisnot($xcatdest)) {
# TODO, To avoid sending request to a multi-home server many times.
foreach (@$reqs) {
my $reqcopy = {%$_};
$reqcopy->{'_xcatdest'} = $xcatdest;
push @requests, $reqcopy;
}
$prehandledhash{$xcatdest} = 1;
} elsif ($handled4me == 0) {
push @requests, @$reqs;
$handled4me = 1;
}
}
#Broadcast the request to all available service nodes
foreach (keys %$sn_hash) {
my $xcatdest = $_;
# to check if the SN already handled
next if (exists($prehandledhash{$xcatdest}));
if (xCAT::NetworkUtils->thishostisnot($xcatdest)) {
my $reqcopy = {%$req};
$reqcopy->{'_xcatdest'} = $xcatdest;
$reqcopy->{'node'} = $sn_hash->{$xcatdest};
$reqs = get_parallel_scope($reqcopy);
push @requests, @$reqs;
} elsif ($handled4me == 0) {
my $reqcopy = {%$req};
$reqcopy->{'node'} = $sn_hash->{$xcatdest};
$reqs = get_parallel_scope($reqcopy);
push @requests, @$reqs;
$handled4me = 1;
}
}
return \@requests;
}

View File

@ -484,7 +484,7 @@ Options:
"Usage:
Common:
nodeset [-h|--help|-v|--version]
nodeset <noderange> [shell|boot|runcmd=bmcsetup|osimage[=<imagename>]|offline|shutdown|stat]",
nodeset <noderange> [shell|boot|runcmd=bmcsetup|osimage[=<imagename>]|offline|shutdown|stat [-a]]",
"rmflexnode" =>
"Usage:
rmflexnode [-h|--help|-v|--version]

View File

@ -4,7 +4,7 @@ B<nodeset> - set the boot state for a noderange
=head1 B<Synopsis>
B<nodeset> I<noderange> [B<boot> | B<stat> | B<offline> | B<runcmd=bmcsetup> | B<osimage>[=I<imagename>] | B<shell> | B<shutdown>]
B<nodeset> I<noderange> [B<boot> | B<stat> [B<-a>]| B<offline> | B<runcmd=bmcsetup> | B<osimage>[=I<imagename>] | B<shell> | B<shutdown>]
B<nodeset> I<noderange> B<osimage>[=I<imagename>] [B<--noupdateinitrd>] [B<--ignorekernelchk>]
@ -68,7 +68,7 @@ If you would like to run a task after deployment, you can define that task with
=item B<stat>
Display the current boot loader config file description for the nodes requested
Display the current boot loader config file description for the nodes requested. When B<disjointdhcps> is set, using B<-a> to display them on all available service nodes.
=item B<runcmd=bmcsetup>

View File

@ -1,6 +1,6 @@
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
package xCAT_plugin::grub2;
use Data::Dumper;
use Sys::Syslog;
use xCAT::Scope;
use xCAT::Utils;
@ -123,12 +123,10 @@ sub setstate {
# We are in the service node pools, print error if no facing ip.
if (xCAT::InstUtils->is_me($sn)) {
my @myself = xCAT::NetworkUtils->determinehostname();
my $myname = $myself[ (scalar @myself) - 1 ];
$::callback->(
{
error => [
"$myname: $ipfnd[1] on service node $sn"
"$::myxcatname: $ipfnd[1] on service node $sn"
],
errorcode => [1]
}
@ -140,7 +138,7 @@ sub setstate {
$::callback->(
{
error => [
"$myname: $ipfnd[1]"
"$::myxcatname: $ipfnd[1]"
],
errorcode => [1]
}
@ -176,9 +174,9 @@ sub setstate {
$kern->{kcmdline} .= " " . $cmdhashref->{volatile};
}
my $pcfg;
unless (-d "$tftpdir/boot/grub2") {
mkpath("$tftpdir/boot/grub2");
my $bootloader_root = "$tftpdir/boot/grub2";
unless (-d "$bootloader_root") {
mkpath("$bootloader_root");
}
my $nodemac;
my %client_nethash = xCAT::DBobjUtils->getNetwkInfo([$node]);
@ -186,9 +184,10 @@ sub setstate {
my $cref = $chainhash{$node}->[0]; #$chaintab->getNodeAttribs($node,['currstate']);
# remove the old boot configuration files and create a new one, but only if not offline directive
system("find $tftpdir/boot/grub2/ -inum \$(stat --printf \%i $tftpdir/boot/grub2/$node 2>/dev/null) -exec rm -f {} \\; 2>/dev/null");
system("find $bootloader_root/ -inum \$(stat --printf \%i $bootloader_root/$node 2>/dev/null) -exec rm -f {} \\; 2>/dev/null");
my $pcfg;
if ($cref and $cref->{currstate} ne "offline") {
open($pcfg, '>', $tftpdir . "/boot/grub2/" . $node);
open($pcfg, '>', "$bootloader_root/" . $node);
print $pcfg "#" . $cref->{currstate} . "\n";
if (($::XCATSITEVALS{xcatdebugmode} eq "1") or ($::XCATSITEVALS{xcatdebugmode} eq "2")) {
@ -294,11 +293,6 @@ sub setstate {
print $pcfg "}";
close($pcfg);
}
my $inetn = xCAT::NetworkUtils->getipaddr($node);
unless ($inetn) {
syslog("local1|err", "xCAT unable to resolve IP for $node in grub2 plugin");
return;
}
} else {
close($pcfg);
}
@ -334,9 +328,9 @@ sub setstate {
my $pname = "grub.cfg-" . sprintf("%02X%02X%02X%02X", @ipa);
# remove the old boot configuration file and copy (link) a new one, but only if not offline directive
unlink($tftpdir . "/boot/grub2/" . $pname);
unlink("$bootloader_root/" . $pname);
if ($cref and $cref->{currstate} ne "offline") {
link($tftpdir . "/boot/grub2/" . $node, $tftpdir . "/boot/grub2/" . $pname);
link("$bootloader_root/" . $node, "$bootloader_root/" . $pname);
}
}
@ -359,9 +353,9 @@ sub setstate {
my $pname = "grub.cfg-01-" . $tmp;
# remove the old boot configuration file and copy (link) a new one, but only if not offline directive
unlink($tftpdir . "/boot/grub2/" . $pname);
unlink("$bootloader_root/" . $pname);
if ($cref and $cref->{currstate} ne "offline") {
link($tftpdir . "/boot/grub2/" . $node, $tftpdir . "/boot/grub2/" . $pname);
link("$bootloader_root/" . $node, "$bootloader_root/" . $pname);
}
}
return;
@ -402,12 +396,14 @@ sub preprocess_request {
#use Getopt::Long;
my $HELP;
my $ALLFLAG;
my $VERSION;
my $VERBOSE;
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("pass_through");
if (!GetOptions('h|?|help' => \$HELP,
'v|version' => \$VERSION,
'a' =>\$ALLFLAG,
'V' => \$VERBOSE #>>>>>>>used for trace log>>>>>>>
)) {
if ($usage{$command}) {
@ -469,12 +465,12 @@ sub preprocess_request {
xCAT::ServiceNodeUtils->getSNandCPnodes(\@$nodes, \@SN, \@CN);
unless (($args[0] eq 'stat') or ($args[0] eq 'enact')) {
if ((@SN > 0) && (@CN > 0)) { # there are both SN and CN
my $rsp;
$rsp->{data}->[0] =
my %rsp;
$rsp{errorcode}->[0] = 1;
$rsp{error}->[0] =
"Nodeset was run with a noderange containing both service nodes and compute nodes. This is not valid. You must submit with either compute nodes in the noderange or service nodes. \n";
xCAT::MsgUtils->message("E", $rsp, $callback1);
$callback1->(\%rsp);
return;
}
}
@ -482,10 +478,40 @@ sub preprocess_request {
if ($req->{inittime}->[0]) {
return [$req];
}
if (@CN > 0) { # if compute nodes broadcast to all servicenodes
return xCAT::Scope->get_broadcast_scope_with_parallel($req);
if (@CN > 0) { # if compute nodes only, then broadcast to servic enodes
my @sn = xCAT::ServiceNodeUtils->getSNList();
unless ( @sn > 0 ) {
return xCAT::Scope->get_parallel_scope($req)
}
my $mynodeonly = 0;
my @entries = xCAT::TableUtils->get_site_attribute("disjointdhcps");
my $t_entry = $entries[0];
if (defined($t_entry)) {
$mynodeonly = $t_entry;
}
$req->{'_disjointmode'} = [$mynodeonly];
xCAT::MsgUtils->trace(0, "d", "grub2: disjointdhcps=$mynodeonly");
if ($mynodeonly == 0 || $ALLFLAG) { # broadcast to all service nodes
return xCAT::Scope->get_broadcast_scope_with_parallel($req, \@sn);
}
my $sn_hash = xCAT::ServiceNodeUtils->getSNformattedhash(\@CN, "xcat", "MN");
my @dhcpsvrs = ();
my $ntab = xCAT::Table->new('networks');
if ($ntab) {
foreach (@{ $ntab->getAllEntries() }) {
next unless ($_->{dynamicrange});
# if dynamicrange specified but dhcpserver was not - issue error message
push @dhcpsvrs, $_->{dhcpserver} if ($_->{dhcpserver})
}
}
return xCAT::Scope->get_broadcast_disjoint_scope_with_parallel($req, $sn_hash, \@dhcpsvrs);
}
}
# Do not dispatch to service nodes if non-sharedtftp or the node range contains only SNs.
return xCAT::Scope->get_parallel_scope($req);
}
@ -496,19 +522,16 @@ sub process_request {
$sub_req = shift;
my $command = $request->{command}->[0];
%breaknetbootnodes = ();
%normalnodes = ();
%normalnodes = (); # It will be fill-up by method: setstate.
my @args;
my @nodes;
my @rnodes;
#>>>>>>>used for trace log start>>>>>>>
my %opt;
my $verbose_on_off = 0;
if (ref($::XNBA_request->{arg})) {
@args = @{ $::XNBA_request->{arg} };
if (ref($request->{arg})) {
@args = @{ $request->{arg} };
} else {
@args = ($::XNBA_request->{arg});
@args = ($request->{arg});
}
@ARGV = @args;
GetOptions('V' => \$opt{V});
@ -516,6 +539,10 @@ sub process_request {
#>>>>>>>used for trace log end>>>>>>>
my @hostinfo = xCAT::NetworkUtils->determinehostname();
$::myxcatname = $hostinfo[-1];
xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: running on $::myxcatname");
my @rnodes;
if (ref($request->{node})) {
@rnodes = @{ $request->{node} };
} else {
@ -528,61 +555,101 @@ sub process_request {
return;
}
#if not shared tftpdir, then filter, otherwise, set up everything
if ($request->{'_disparatetftp'}->[0]) { #reading hint from preprocess_command
@nodes = ();
my @hostinfo = xCAT::NetworkUtils->determinehostname();
my $cur_xmaster = pop @hostinfo;
xCAT::MsgUtils->trace(0, "d", "grub2: running on $cur_xmaster");
# Get current server managed node list
my $sn_hash = xCAT::ServiceNodeUtils->getSNformattedhash(\@rnodes, "xcat", "MN");
my %managed = {};
foreach (@{ $sn_hash->{$cur_xmaster} }) { $managed{$_} = 1; }
foreach (@rnodes) {
if (xCAT::NetworkUtils->nodeonmynet($_)) {
push @nodes, $_;
if ($args[0] eq 'stat') {
my $noderestab = xCAT::Table->new('noderes'); #in order to detect per-node tftp directories
my %nrhash = %{ $noderestab->getNodesAttribs(\@rnodes, [qw(tftpdir)]) };
foreach my $node (@rnodes) {
my %response;
my $tftpdir;
if ($nrhash{$node}->[0] and $nrhash{$node}->[0]->{tftpdir}) {
$tftpdir = $nrhash{$node}->[0]->{tftpdir};
} else {
my $msg = "grub2 configuration file was not created for node [$_] because sharedtftp attribute is not set and the node is not on same network as this xcatmaster";
if ( $cur_xmaster ) {
$msg .= ": $cur_xmaster";
}
if ( exists( $managed{$_} ) ) {
# report error when it is under my control but I cannot handle it.
my $rsp;
$rsp->{data}->[0] = $msg;
xCAT::MsgUtils->message("E", $rsp, $callback);
} else {
xCAT::MsgUtils->message("S", $msg);
}
$tftpdir = $globaltftpdir;
}
$response{node}->[0]->{name}->[0] = $node;
$response{node}->[0]->{data}->[0] = getstate($node, $tftpdir);
$callback->(\%response);
}
} else {
@nodes = @rnodes;
}
#>>>>>>>used for trace log>>>>>>>
my $str_node = join(" ", @nodes);
xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: nodes are $str_node");
# return directly if no nodes in the same network
unless (@nodes) {
xCAT::MsgUtils->message("S", "xCAT: grub2 netboot: no valid nodes. Stop the operation on this server.");
return;
}
if (ref($request->{arg})) {
@args = @{ $request->{arg} };
my @nodes = ();
# Filter those nodes which have bad DNS: not resolvable or inconsistent IP
my %failurenodes = ();
my %preparednodes = ();
foreach (@rnodes) {
my $ipret = xCAT::NetworkUtils->checkNodeIPaddress($_);
my $errormsg = $ipret->{'error'};
my $nodeip = $ipret->{'ip'};
if ($errormsg) {# Add the node to failure set
xCAT::MsgUtils->trace(0, "E", "grub2: Defined IP address of $_ is $nodeip. $errormsg");
unless ($nodeip) {
$failurenodes{$_} = 1;
}
}
if ($nodeip) {
$preparednodes{$_} = $nodeip;
}
}
#if not shared tftpdir, then filter, otherwise, set up everything
if ($request->{'_disparatetftp'}->[0]) { #reading hint from preprocess_command
# Filter those nodes not in the same subnet, and print error message in log file.
foreach (keys %preparednodes) {
# Only handle its boot configuration files if the node in same subnet
if (xCAT::NetworkUtils->nodeonmynet($preparednodes{$_})) {
push @nodes, $_;
} else {
xCAT::MsgUtils->trace(0, "W", "grub2: configuration file was not created for [$_] because the node is not on the same network as this server");
delete $preparednodes{$_};
}
}
} else {
@args = ($request->{arg});
@nodes = keys %preparednodes;
}
my $str_node = join(" ", @nodes);
xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: nodes are $str_node") if ($str_node);
# Return directly if no nodes in the same network, need to report error on console if its managed nodes are not handled.
unless (@nodes) {
xCAT::MsgUtils->message("S", "xCAT: grub2 netboot: no valid nodes. Stop the operation on this server.");
# If non-shared tftproot and non disjoint mode, need to figure out if no nodes here is a normal case.
if ($request->{'_disparatetftp'}->[0] && $request->{'_disjointmode'}->[0] != 1) {
# Find out which nodes are really mine only when not sharedtftp and not disjoint mode.
my %iphash = ();
# flag the IPs or names in iphash
foreach (@hostinfo) { $iphash{$_} = 1; }
# Get managed node list under current server
# The node will be under under 'site.master' if no 'noderes.servicenode' is defined
my $sn_hash = xCAT::ServiceNodeUtils->getSNformattedhash(\@rnodes, "xcat", "MN");
my $req2manage = 0;
foreach (keys %$sn_hash) {
if (exists($iphash{$_})) {
$req2manage = 1;
last;
}
}
# Okay, now report error as no nodes are handled.
if ($req2manage == 0) {
xCAT::MsgUtils->trace(0, "d", "grub2: No nodes are required to be managed on this server");
return;
}
}
my $rsp;
$rsp->{errorcode}->[0] = 1;
$rsp->{error}->[0] = "Failed to generate grub2 configurations for some node(s) on $::myxcatname. Check xCAT log file for more details.";
$callback->($rsp);
return;
}
#now run the begin part of the prescripts
unless ($args[0] eq 'stat') { # or $args[0] eq 'enact') {
unless ($args[0] eq '') { # or $args[0] eq 'enact') {
$errored = 0;
if ($request->{'_disparatetftp'}->[0]) { #the call is distrubuted to the service node already, so only need to handles my own children
xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: the call is distrubuted to the service node already, so only need to handles my own children");
if ($request->{'_disparatetftp'}->[0]) { #the call is distrubuted to the service node already, so only need to handle my own children
xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: the call is distrubuted to the service node already, so only need to handle my own children");
xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: issue runbeginpre request");
$sub_req->({ command => ['runbeginpre'],
node => \@nodes,
@ -609,7 +676,7 @@ sub process_request {
if (!$inittime) { $inittime = 0; }
my %bphash;
unless ($args[0] eq 'stat') { # or $args[0] eq 'enact') {
unless ($args[0] eq '') { # or $args[0] eq 'enact') {
$errored = 0;
xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: issue setdestiny request");
$sub_req->({ command => ['setdestiny'],
@ -619,11 +686,12 @@ sub process_request {
bootparams => \%bphash
}, \&pass_along);
if ($errored) {
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: Failed in processing setdestiny. Processing will not continue.");
xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: Failed in processing setdestiny. Processing will not continue.");
return;
}
}
xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: starting to handle configuration...");
my $chaintab = xCAT::Table->new('chain', -create => 1);
my $chainhash = $chaintab->getNodesAttribs(\@nodes, ['currstate']);
my $noderestab = xCAT::Table->new('noderes', -create => 1);
@ -649,10 +717,7 @@ sub process_request {
$tftpdir = $globaltftpdir;
}
$response{node}->[0]->{name}->[0] = $_;
if ($args[0] eq 'stat') {
$response{node}->[0]->{data}->[0] = getstate($_, $tftpdir);
$callback->(\%response);
} elsif ($args[0]) { #If anything else, send it on to the destiny plugin, then setstate
if ($args[0]) { # Send it on to the destiny plugin, then setstate
my $ent = $typehash->{$_}->[0];
my $osimgname = $ent->{'provmethod'};
my $linuximghash = undef;
@ -668,11 +733,11 @@ sub process_request {
}
}
} # end of foreach node
xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: Finish to handle configurations");
my @normalnodeset = keys %normalnodes;
my @breaknetboot = keys %breaknetbootnodes;
#print "grub2 :inittime=$inittime; normalnodeset=@normalnodeset; breaknetboot=@breaknetboot\n";
my %osimagenodehash;
for my $nn (@normalnodeset) {
@ -693,7 +758,7 @@ sub process_request {
}
#Don't bother to try dhcp binding changes if sub_req not passed, i.e. service node build time
unless (($args[0] eq 'stat') || ($inittime) || ($args[0] eq 'offline')) {
unless (($inittime) || ($args[0] eq 'offline')) {
foreach my $osimage (keys %osimagenodehash) {
#TOTO check the existence of grub2 executable files for corresponding arch
@ -793,7 +858,7 @@ sub process_request {
}
#now run the end part of the prescripts
unless ($args[0] eq 'stat') { # or $args[0] eq 'enact')
unless ($args[0] eq '') { # or $args[0] eq 'enact')
$errored = 0;
if ($request->{'_disparatetftp'}->[0]) { #the call is distrubuted to the service node already, so only need to handles my own children
xCAT::MsgUtils->trace($verbose_on_off, "d", "grub2: issue runendpre request");
@ -814,6 +879,16 @@ sub process_request {
return;
}
}
# Return error codes if there are failed nodes
if (%failurenodes) {
my $rsp;
$rsp->{errorcode}->[0] = 1;
$rsp->{error}->[0] = "Failed to generate grub2 configurations for some node(s) on $::myxcatname. Check xCAT log file for more details.";
$callback->($rsp);
return;
}
}
#----------------------------------------------------------------------------

View File

@ -78,7 +78,7 @@ sub setstate {
=pod
This function will manipulate the yaboot structure to match what the noderes/chain tables indicate the node should be booting.
This function will manipulate the petitboot structure to match what the noderes/chain tables indicate the node should be booting.
=cut
@ -125,12 +125,10 @@ sub setstate {
# We are in the service node pools, print error if no facing ip.
if (xCAT::InstUtils->is_me($sn)) {
my @myself = xCAT::NetworkUtils->determinehostname();
my $myname = $myself[ (scalar @myself) - 1 ];
$::callback->(
{
error => [
"$myname: $ipfnd[1] on service node $sn"
"$::myxcatname: $ipfnd[1] on service node $sn"
],
errorcode => [1]
}
@ -142,7 +140,7 @@ sub setstate {
$::callback->(
{
error => [
"$myname: $ipfnd[1]"
"$::myxcatname: $ipfnd[1]"
],
errorcode => [1]
}
@ -170,18 +168,19 @@ sub setstate {
}
}
my $pcfg;
unless (-d "$tftpdir/petitboot") {
mkpath("$tftpdir/petitboot");
my $bootloader_root = "$tftpdir/petitboot";
unless (-d "$bootloader_root") {
mkpath("$bootloader_root");
}
my $nodemac;
my $cref = $chainhash{$node}->[0]; #$chaintab->getNodeAttribs($node,['currstate']);
my $pcfg;
# remove the old boot configuration file and create a new one, but only if not offline directive
unlink($tftpdir . "/petitboot/" . $node);
unlink("$bootloader_root/" . $node);
if ($cref and $cref->{currstate} ne "offline") {
open($pcfg, '>', $tftpdir . "/petitboot/" . $node);
open($pcfg, '>', "$bootloader_root/" . $node);
print $pcfg "#" . $cref->{currstate} . "\n";
}
$normalnodes{$node} = 1; #Assume a normal netboot (well, normal dhcp,
@ -217,18 +216,13 @@ sub setstate {
print $pcfg "\tappend \"" . $kern->{kcmdline} . "\"\n";
}
close($pcfg);
my $inetn = xCAT::NetworkUtils->getipaddr($node);
unless ($inetn) {
syslog("local1|err", "xCAT unable to resolve IP for $node in petitboot plugin");
return;
}
} else { #TODO: actually, should possibly default to xCAT image?
#print $pcfg "bye\n";
close($pcfg);
}
my $ip = xCAT::NetworkUtils->getipaddr($node);
unless ($ip) {
syslog("local1|err", "xCAT unable to resolve IP in petitboot plugin");
syslog("local1|err", "xCAT unable to resolve IP for $node in petitboot plugin");
return;
}
@ -237,9 +231,9 @@ sub setstate {
$pname = uc($pname);
# remove the old boot configuration file and copy (link) a new one, but only if not offline directive
unlink($tftpdir . "/" . $pname);
unlink("$tftpdir/" . $pname);
if ($cref and $cref->{currstate} ne "offline") {
link($tftpdir . "/petitboot/" . $node, $tftpdir . "/" . $pname);
link("$bootloader_root/" . $node, "$tftpdir/" . $pname);
}
return;
}
@ -283,12 +277,14 @@ sub preprocess_request {
#use Getopt::Long;
my $HELP;
my $ALLFLAG;
my $VERSION;
my $VERBOSE;
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("pass_through");
if (!GetOptions('h|?|help' => \$HELP,
'v|version' => \$VERSION,
'a' =>\$ALLFLAG,
'V' => \$VERBOSE #>>>>>>>used for trace log>>>>>>>
)) {
if ($usage{$command}) {
@ -334,6 +330,13 @@ sub preprocess_request {
return;
}
if ($ARGV[0] ne "stat" && $ALLFLAG) {
my %rsp;
$rsp{error}->[0] = "'-a' could only be used with 'stat' subcommand.";
$rsp{errorcode}->[0] = 1;
$callback1->(\%rsp);
return;
}
#Assume shared tftp directory for boring people, but for cool people, help sync up tftpdirectory contents when
#if they specify no sharedtftp in site table
@ -346,23 +349,55 @@ sub preprocess_request {
my @SN;
my @CN;
xCAT::ServiceNodeUtils->getSNandCPnodes(\@$nodes, \@SN, \@CN);
if ((@SN > 0) && (@CN > 0)) { # there are both SN and CN
my $rsp;
$rsp->{data}->[0] =
"Nodeset was run with a noderange containing both service nodes and compute nodes. This is not valid. You must submit with either compute nodes in the noderange or service nodes. \n";
xCAT::MsgUtils->message("E", $rsp, $callback1);
return;
unless (($args[0] eq 'stat') or ($args[0] eq 'enact')) {
if ((@SN > 0) && (@CN > 0)) { # there are both SN and CN
my %rsp;
$rsp{errorcode}->[0] = 1;
$rsp{error}->[0] =
"Nodeset was run with a noderange containing both service nodes and compute nodes. This is not valid. You must submit with either compute nodes in the noderange or service nodes. \n";
$callback1->(\%rsp);
return;
}
}
$req->{'_disparatetftp'} = [1];
if ($req->{inittime}->[0]) {
return [$req];
}
if (@CN > 0) { # if compute nodes broadcast to all servicenodes
return xCAT::Scope->get_broadcast_scope_with_parallel($req);
if (@CN > 0) { # if compute nodes only, then broadcast to servic enodes
my @sn = xCAT::ServiceNodeUtils->getSNList();
unless ( @sn > 0 ) {
return xCAT::Scope->get_parallel_scope($req)
}
my $mynodeonly = 0;
my @entries = xCAT::TableUtils->get_site_attribute("disjointdhcps");
my $t_entry = $entries[0];
if (defined($t_entry)) {
$mynodeonly = $t_entry;
}
$req->{'_disjointmode'} = [$mynodeonly];
xCAT::MsgUtils->trace(0, "d", "petitboot: disjointdhcps=$mynodeonly");
if ($mynodeonly == 0 || $ALLFLAG) { # broadcast to all service nodes
return xCAT::Scope->get_broadcast_scope_with_parallel($req, \@sn);
}
my $sn_hash = xCAT::ServiceNodeUtils->getSNformattedhash(\@CN, "xcat", "MN");
my @dhcpsvrs = ();
my $ntab = xCAT::Table->new('networks');
if ($ntab) {
foreach (@{ $ntab->getAllEntries() }) {
next unless ($_->{dynamicrange});
# if dynamicrange specified but dhcpserver was not - issue error message
push @dhcpsvrs, $_->{dhcpserver} if ($_->{dhcpserver})
}
}
return xCAT::Scope->get_broadcast_disjoint_scope_with_parallel($req, $sn_hash, \@dhcpsvrs);
}
}
# Do not dispatch to service nodes if non-sharedtftp or the node range contains only SNs.
return xCAT::Scope->get_parallel_scope($req);
}
@ -374,16 +409,16 @@ sub process_request {
$sub_req = shift;
my $command = $request->{command}->[0];
%breaknetbootnodes = ();
%normalnodes = ();
%normalnodes = (); # It will be fill-up by method: setstate.
#>>>>>>>used for trace log start>>>>>>>
my @args = ();
my %opt;
my $verbose_on_off = 0;
if (ref($::request->{arg})) {
@args = @{ $::request->{arg} };
if (ref($request->{arg})) {
@args = @{ $request->{arg} };
} else {
@args = ($::request->{arg});
@args = ($request->{arg});
}
@ARGV = @args;
GetOptions('V' => \$opt{V});
@ -394,7 +429,9 @@ sub process_request {
if ($::XCATSITEVALS{"httpmethod"}) { $httpmethod = $::XCATSITEVALS{"httpmethod"}; }
if ($::XCATSITEVALS{"httpport"}) { $httpport = $::XCATSITEVALS{"httpport"}; }
my @nodes;
my @hostinfo = xCAT::NetworkUtils->determinehostname();
$::myxcatname = $hostinfo[-1];
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: running on $::myxcatname");
my @rnodes;
if (ref($request->{node})) {
@rnodes = @{ $request->{node} };
@ -408,61 +445,101 @@ sub process_request {
return;
}
#if not shared tftpdir, then filter, otherwise, set up everything
if ($request->{'_disparatetftp'}->[0]) { #reading hint from preprocess_command
@nodes = ();
my @hostinfo = xCAT::NetworkUtils->determinehostname();
my $cur_xmaster = pop @hostinfo;
xCAT::MsgUtils->trace(0, "d", "petitboot: running on $cur_xmaster");
# Get current server managed node list
my $sn_hash = xCAT::ServiceNodeUtils->getSNformattedhash(\@rnodes, "xcat", "MN");
my %managed = {};
foreach (@{ $sn_hash->{$cur_xmaster} }) { $managed{$_} = 1; }
foreach (@rnodes) {
if (xCAT::NetworkUtils->nodeonmynet($_)) {
push @nodes, $_;
if ($args[0] eq 'stat') {
my $noderestab = xCAT::Table->new('noderes'); #in order to detect per-node tftp directories
my %nrhash = %{ $noderestab->getNodesAttribs(\@rnodes, [qw(tftpdir)]) };
foreach my $node (@rnodes) {
my %response;
my $tftpdir;
if ($nrhash{$node}->[0] and $nrhash{$node}->[0]->{tftpdir}) {
$tftpdir = $nrhash{$node}->[0]->{tftpdir};
} else {
my $msg = "petitboot configuration file was not created for node [$_] because sharedtftp attribute is not set and the node is not on same network as this xcatmaster";
if ( $cur_xmaster ) {
$msg .= ": $cur_xmaster";
}
if ( exists( $managed{$_} ) ) {
# report error when it is under my control but I cannot handle it.
my $rsp;
$rsp->{data}->[0] = $msg;
xCAT::MsgUtils->message("E", $rsp, $callback);
} else {
xCAT::MsgUtils->message("S", $msg);
}
$tftpdir = $globaltftpdir;
}
$response{node}->[0]->{name}->[0] = $node;
$response{node}->[0]->{data}->[0] = getstate($node, $tftpdir);
$callback->(\%response);
}
} else {
@nodes = @rnodes;
}
#>>>>>>>used for trace log>>>>>>>
my $str_node = join(" ", @nodes);
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: nodes are $str_node");
# return directly if no nodes in the same network
unless (@nodes) {
xCAT::MsgUtils->message("S", "xCAT: petitboot netboot: no valid nodes. Stop the operation on this server.");
return;
}
if (ref($request->{arg})) {
@args = @{ $request->{arg} };
my @nodes = ();
# Filter those nodes which have bad DNS: not resolvable or inconsistent IP
my %failurenodes = ();
my %preparednodes = ();
foreach (@rnodes) {
my $ipret = xCAT::NetworkUtils->checkNodeIPaddress($_);
my $errormsg = $ipret->{'error'};
my $nodeip = $ipret->{'ip'};
if ($errormsg) {# Add the node to failure set
xCAT::MsgUtils->trace(0, "E", "petitboot: Defined IP address of $_ is $nodeip. $errormsg");
unless ($nodeip) {
$failurenodes{$_} = 1;
}
}
if ($nodeip) {
$preparednodes{$_} = $nodeip;
}
}
#if not shared tftpdir, then filter, otherwise, set up everything
if ($request->{'_disparatetftp'}->[0]) { #reading hint from preprocess_command
# Filter those nodes not in the same subnet, and print error message in log file.
foreach (keys %preparednodes) {
# Only handle its boot configuration files if the node in same subnet
if (xCAT::NetworkUtils->nodeonmynet($preparednodes{$_})) {
push @nodes, $_;
} else {
xCAT::MsgUtils->trace(0, "W", "petitboot: configuration file was not created for [$_] because the node is not on the same network as this server");
delete $preparednodes{$_};
}
}
} else {
@args = ($request->{arg});
@nodes = keys %preparednodes;
}
my $str_node = join(" ", @nodes);
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: nodes are $str_node") if ($str_node);
# Return directly if no nodes in the same network, need to report error on console if its managed nodes are not handled.
unless (@nodes) {
xCAT::MsgUtils->message("S", "xCAT: petitboot netboot: no valid nodes. Stop the operation on this server.");
# If non-shared tftproot and non disjoint mode, need to figure out if no nodes here is a normal case.
if ($request->{'_disparatetftp'}->[0] && $request->{'_disjointmode'}->[0] != 1) {
# Find out which nodes are really mine only when not sharedtftp and not disjoint mode.
my %iphash = ();
# flag the IPs or names in iphash
foreach (@hostinfo) { $iphash{$_} = 1; }
# Get managed node list under current server
# The node will be under under 'site.master' if no 'noderes.servicenode' is defined
my $sn_hash = xCAT::ServiceNodeUtils->getSNformattedhash(\@rnodes, "xcat", "MN");
my $req2manage = 0;
foreach (keys %$sn_hash) {
if (exists($iphash{$_})) {
$req2manage = 1;
last;
}
}
if ($req2manage == 0) {
#No nodes are required to be handled, quit without error.
return;
}
}
# Okay, now report error as no nodes are handled.
my $rsp;
$rsp->{errorcode}->[0] = 1;
$rsp->{error}->[0] = "Failed to generate petitboot configurations for some node(s) on $::myxcatname. Check xCAT log file for more details.";
$callback->($rsp);
return;
}
#now run the begin part of the prescripts
unless ($args[0] eq 'stat') { # or $args[0] eq 'enact') {
unless ($args[0] eq '') { # or $args[0] eq 'enact') {
$errored = 0;
if ($request->{'_disparatetftp'}->[0]) { #the call is distrubuted to the service node already, so only need to handles my own children
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: the call is distrubuted to the service node already, so only need to handles my own children");
if ($request->{'_disparatetftp'}->[0]) { #the call is distrubuted to the service node already, so only need to handle my own children
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: the call is distrubuted to the service node already, so only need to handle my own children");
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: issue runbeginpre request");
$sub_req->({ command => ['runbeginpre'],
node => \@nodes,
@ -471,7 +548,7 @@ sub process_request {
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: nodeset did not distribute to the service node");
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: issue runbeginpre request");
$sub_req->({ command => ['runbeginpre'],
node => \@rnodes,
node => \@nodes,
arg => [ $args[0] ] }, \&pass_along);
}
if ($errored) {
@ -489,7 +566,7 @@ sub process_request {
if (!$inittime) { $inittime = 0; }
my %bphash;
unless ($args[0] eq 'stat') { # or $args[0] eq 'enact') {
unless ($args[0] eq '') { # or $args[0] eq 'enact') {
$errored = 0;
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: issue setdestiny request");
$sub_req->({ command => ['setdestiny'],
@ -515,6 +592,8 @@ sub process_request {
});
xCAT::MsgUtils->message("S", "xCAT: petitboot netboot: clear node(s): @nodes boot device setting.");
}
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: starting to handle configuration...");
my $chaintab = xCAT::Table->new('chain', -create => 1);
my $chainhash = $chaintab->getNodesAttribs(\@nodes, ['currstate']);
my $noderestab = xCAT::Table->new('noderes', -create => 1);
@ -538,13 +617,10 @@ sub process_request {
$tftpdir = $globaltftpdir;
}
$response{node}->[0]->{name}->[0] = $_;
if ($args[0] eq 'stat') {
$response{node}->[0]->{data}->[0] = getstate($_, $tftpdir);
$callback->(\%response);
} elsif ($args[0]) { #If anything else, send it on to the destiny plugin, then setstate
if ($args[0]) { # send it on to the destiny plugin, then setstate
my $ent = $typehash->{$_}->[0];
my $osimgname = $ent->{'provmethod'};
my $linuximghash = $linuximghash = $linuximgtab->getAttribs({ imagename => $osimgname }, 'boottarget', 'addkcmdline');
my $linuximghash = $linuximgtab->getAttribs({ imagename => $osimgname }, 'boottarget', 'addkcmdline');
($rc, $errstr) = setstate($_, \%bphash, $chainhash, $machash, $tftpdir, $nodereshash, $linuximghash);
@ -555,11 +631,10 @@ sub process_request {
}
}
} # end of foreach node
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: Finish to handle configurations");
my @normalnodeset = keys %normalnodes;
my @breaknetboot = keys %breaknetbootnodes;
#print "yaboot:inittime=$inittime; normalnodeset=@normalnodeset; breaknetboot=@breaknetboot\n";
my %osimagenodehash;
for my $nn (@normalnodeset) {
@ -570,7 +645,7 @@ sub process_request {
}
#Don't bother to try dhcp binding changes if sub_req not passed, i.e. service node build time
unless (($args[0] eq 'stat') || ($inittime) || ($args[0] eq 'offline')) {
unless (($inittime) || ($args[0] eq 'offline')) {
#dhcp stuff
my $do_dhcpsetup = 1;
@ -580,16 +655,17 @@ sub process_request {
if ($t_entry =~ /0|n|N/) { $do_dhcpsetup = 0; }
}
if ($do_dhcpsetup) {
if ($::request->{'_disparatetftp'}->[0]) { #reading hint from preprocess_command
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: issue makedhcp request");
$sub_req->({ command => ['makedhcp'], arg => ['-l'],
node => \@normalnodeset }, $callback);
} else {
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: issue makedhcp request");
$sub_req->({ command => ['makedhcp'],
node => \@normalnodeset }, $callback);
}
my @parameter;
push @parameter, '-l' if ($::request->{'_disparatetftp'}->[0]);
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: issue makedhcp request");
$sub_req->({ command => ['makedhcp'],
arg => \@parameter,
node => \@normalnodeset }, $callback);
} else {
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: dhcpsetup=$do_dhcpsetup");
}
}
if ($args[0] eq 'offline') {
@ -599,7 +675,7 @@ sub process_request {
}
#now run the end part of the prescripts
unless ($args[0] eq 'stat') { # or $args[0] eq 'enact')
unless ($args[0] eq '') { # or $args[0] eq 'enact')
$errored = 0;
if ($request->{'_disparatetftp'}->[0]) { #the call is distrubuted to the service node already, so only need to handles my own children
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: issue runendpre request");
@ -609,7 +685,7 @@ sub process_request {
} else { #nodeset did not distribute to the service node, here we need to let runednpre to distribute the nodes to their masters
xCAT::MsgUtils->trace($verbose_on_off, "d", "petitboot: issue runendpre request");
$sub_req->({ command => ['runendpre'],
node => \@rnodes,
node => \@nodes,
arg => [ $args[0] ] }, \&pass_along);
}
if ($errored) {
@ -620,6 +696,15 @@ sub process_request {
return;
}
}
# Return error codes if there are failed nodes
if (%failurenodes) {
my $rsp;
$rsp->{errorcode}->[0] = 1;
$rsp->{error}->[0] = "Failed to generate petitboot configurations for some node(s) on $::myxcatname. Check xCAT log file for more details.";
$callback->($rsp);
return;
}
}
#----------------------------------------------------------------------------

View File

@ -331,10 +331,12 @@ sub preprocess_request {
Getopt::Long::Configure("bundling");
Getopt::Long::Configure("pass_through");
my $HELP;
my $ALLFLAG;
my $VERSION;
my $VERBOSE;
if (!GetOptions('h|?|help' => \$HELP,
'v|version' => \$VERSION,
'a' =>\$ALLFLAG,
'V' => \$VERBOSE #>>>>>>>used for trace log>>>>>>>
)) {
if ($usage{$command}) {
@ -380,6 +382,13 @@ sub preprocess_request {
return;
}
if ($ARGV[0] ne "stat" && $ALLFLAG) {
my %rsp;
$rsp{error}->[0] = "'-a' could only be used with 'stat' subcommand.";
$rsp{errorcode}->[0] = 1;
$callback1->(\%rsp);
return;
}
#Assume shared tftp directory for boring people, but for cool people, help sync up tftpdirectory contents when
#they specify no sharedtftp in site table
@ -394,12 +403,12 @@ sub preprocess_request {
xCAT::ServiceNodeUtils->getSNandCPnodes(\@$nodes, \@SN, \@CN);
unless (($args[0] eq 'stat') or ($args[0] eq 'enact')) { # mix is ok for these options
if ((@SN > 0) && (@CN > 0)) { # there are both SN and CN
my $rsp;
$rsp->{data}->[0] =
my %rsp;
$rsp{errorcode}->[0] = 1;
$rsp{error}->[0] =
"Nodeset was run with a noderange containing both service nodes and compute nodes. This is not valid. You must submit with either compute nodes in the noderange or service nodes. \n";
xCAT::MsgUtils->message("E", $rsp, $callback1);
$callback1->(\%rsp);
return;
}
}
@ -408,9 +417,39 @@ sub preprocess_request {
return [$req];
}
if (@CN > 0) { # if compute nodes broadcast to all servicenodes
return xCAT::Scope->get_broadcast_scope_with_parallel($req);
my @sn = xCAT::ServiceNodeUtils->getSNList();
unless ( @sn > 0 ) {
return xCAT::Scope->get_parallel_scope($req)
}
my $mynodeonly = 0;
my @entries = xCAT::TableUtils->get_site_attribute("disjointdhcps");
my $t_entry = $entries[0];
if (defined($t_entry)) {
$mynodeonly = $t_entry;
}
$req->{'_disjointmode'} = [$mynodeonly];
xCAT::MsgUtils->trace(0, "d", "xnba: disjointdhcps=$mynodeonly");
if ($mynodeonly == 0 || $ALLFLAG) { # broadcast to all service nodes
return xCAT::Scope->get_broadcast_scope_with_parallel($req, \@sn);
}
my $sn_hash = xCAT::ServiceNodeUtils->getSNformattedhash(\@CN, "xcat", "MN");
my @dhcpsvrs = ();
my $ntab = xCAT::Table->new('networks');
if ($ntab) {
foreach (@{ $ntab->getAllEntries() }) {
next unless ($_->{dynamicrange});
# if dynamicrange specified but dhcpserver was not - issue error message
push @dhcpsvrs, $_->{dhcpserver} if ($_->{dhcpserver})
}
}
return xCAT::Scope->get_broadcast_disjoint_scope_with_parallel($req, $sn_hash, \@dhcpsvrs);
}
}
# Do not dispatch to service nodes if non-sharedtftp or the node range contains only SNs.
return xCAT::Scope->get_parallel_scope($req);
}
@ -437,12 +476,14 @@ sub process_request {
#>>>>>>>used for trace log end>>>>>>>
my @hostinfo = xCAT::NetworkUtils->determinehostname();
$::myxcatname = $hostinfo[-1];
xCAT::MsgUtils->trace(0, "d", "xnba: running on $::myxcatname");
if (ref($::XNBA_request->{node})) {
@rnodes = @{ $::XNBA_request->{node} };
} else {
if ($::XNBA_request->{node}) { @rnodes = ($::XNBA_request->{node}); }
}
unless (@rnodes) {
if ($usage{ $::XNBA_request->{command}->[0] }) {
$::XNBA_callback->({ data => $usage{ $::XNBA_request->{command}->[0] } });
@ -450,60 +491,98 @@ sub process_request {
return;
}
#if not shared, then help sync up
if ($::XNBA_request->{'_disparatetftp'}->[0]) { #reading hint from preprocess_command
@nodes = ();
my @hostinfo = xCAT::NetworkUtils->determinehostname();
my $cur_xmaster = pop @hostinfo;
xCAT::MsgUtils->trace(0, "d", "xnba: running on $cur_xmaster");
# Get current server managed node list
my $sn_hash = xCAT::ServiceNodeUtils->getSNformattedhash(\@rnodes, "xcat", "MN");
my %managed = {};
foreach (@{ $sn_hash->{$cur_xmaster} }) { $managed{$_} = 1; }
# Whatever the node managed by this xcatmaster explicitly, if the node in same subnet, we need to handle its boot configuration files
foreach (@rnodes) {
if (xCAT::NetworkUtils->nodeonmynet($_)) {
push @nodes, $_;
if ($args[0] eq 'stat') {
my $noderestab = xCAT::Table->new('noderes'); #in order to detect per-node tftp directories
my %nrhash = %{ $noderestab->getNodesAttribs(\@rnodes, [qw(tftpdir)]) };
foreach my $node (@rnodes) {
my %response;
my $tftpdir;
if ($nrhash{$node}->[0] and $nrhash{$node}->[0]->{tftpdir}) {
$tftpdir = $nrhash{$node}->[0]->{tftpdir};
} else {
my $msg = "xnba configuration file was not created for node [$_] because sharedtftp attribute is not set and the node is not on same network as this xcatmaster";
if ( $cur_xmaster ) {
$msg .= ": $cur_xmaster";
}
if ( exists( $managed{$_} ) ) {
# report error when it is under my control but I cannot handle it.
my $rsp;
$rsp->{data}->[0] = $msg;
xCAT::MsgUtils->message("E", $rsp, $::XNBA_callback);
} else {
xCAT::MsgUtils->message("S", $msg);
}
$tftpdir = $globaltftpdir;
}
$response{node}->[0]->{name}->[0] = $node;
$response{node}->[0]->{data}->[0] = getstate($node, $tftpdir);
$::XNBA_callback->(\%response);
}
} else {
@nodes = @rnodes;
}
#>>>>>>>used for trace log>>>>>>>
my $str_node = join(" ", @nodes);
xCAT::MsgUtils->trace(0, "d", "xnba: nodes are $str_node");
# return directly if no nodes in the same network
unless (@nodes) {
xCAT::MsgUtils->message("S", "xCAT: xnba netboot: no valid nodes. Stop the operation on this server.");
return;
}
if (ref($::XNBA_request->{arg})) {
@args = @{ $::XNBA_request->{arg} };
my @nodes = ();
# Filter those nodes which have bad DNS: not resolvable or inconsistent IP
my %failurenodes = ();
my %preparednodes = ();
foreach (@rnodes) {
my $ipret = xCAT::NetworkUtils->checkNodeIPaddress($_);
my $errormsg = $ipret->{'error'};
my $nodeip = $ipret->{'ip'};
if ($errormsg) {# Add the node to failure set
xCAT::MsgUtils->trace(0, "E", "xnba: Defined IP address of $_ is $nodeip. $errormsg");
unless ($nodeip) {
$failurenodes{$_} = 1;
}
}
if ($nodeip) {
$preparednodes{$_} = $nodeip;
}
}
#if not shared tftpdir, then filter, otherwise, set up everything
if ($::XNBA_request->{'_disparatetftp'}->[0]) { #reading hint from preprocess_command
# Filter those nodes not in the same subnet, and print error message in log file.
foreach (keys %preparednodes) {
# Only handle its boot configuration files if the node in same subnet
if (xCAT::NetworkUtils->nodeonmynet($preparednodes{$_})) {
push @nodes, $_;
} else {
xCAT::MsgUtils->trace(0, "W", "xnba: configuration file was not created for [$_] because the node is not on the same network as this server");
delete $preparednodes{$_};
}
}
} else {
@args = ($::XNBA_request->{arg});
@nodes = keys %preparednodes;
}
my $str_node = join(" ", @nodes);
xCAT::MsgUtils->trace(0, "d", "xnba: nodes are $str_node") if ($str_node);
# Return directly if no nodes in the same network, need to report error on console if its managed nodes are not handled.
unless (@nodes) {
xCAT::MsgUtils->message("S", "xCAT: xnba netboot: no valid nodes. Stop the operation on this server.");
# If non-shared tftproot and non disjoint mode, need to figure out if no nodes here is a normal case.
if ($::XNBA_request->{'_disparatetftp'}->[0] && $::XNBA_request->{'_disjointmode'}->[0] != 1) {
# Find out which nodes are really mine only when not sharedtftp and not disjoint mode.
my %iphash = ();
# flag the IPs or names in iphash
foreach (@hostinfo) { $iphash{$_} = 1; }
# Get managed node list under current server
# The node will be under under 'site.master' if no 'noderes.servicenode' is defined
my $sn_hash = xCAT::ServiceNodeUtils->getSNformattedhash(\@rnodes, "xcat", "MN");
my $req2manage = 0;
foreach (keys %$sn_hash) {
if (exists($iphash{$_})) {
$req2manage = 1;
last;
}
}
if ($req2manage == 0) {
#No nodes are required to be handled, quit without error.
return;
}
}
# Okay, now report error as no nodes are handled.
my $rsp;
$rsp->{errorcode}->[0] = 1;
$rsp->{error}->[0] = "Failed to generate xnba configurations for some node(s) on $::myxcatname. Check xCAT log file for more details.";
$::XNBA_callback->($rsp);
return;
}
#now run the begin part of the prescripts
unless ($args[0] eq 'stat') { # or $args[0] eq 'enact') {
unless ($args[0] eq '') { # or $args[0] eq 'enact') {
$errored = 0;
if ($::XNBA_request->{'_disparatetftp'}->[0]) { #the call is distrubuted to the service node already, so only need to handles my own children
xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: the call is distrubuted to the service node already, so only need to handles my own children");
@ -515,7 +594,7 @@ sub process_request {
xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: nodeset did not distribute to the service node");
xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: issue runbeginpre request");
$sub_req->({ command => ['runbeginpre'],
node => \@rnodes,
node => \@nodes,
arg => [ $args[0] ] }, \&pass_along);
}
if ($errored) {
@ -547,7 +626,7 @@ sub process_request {
if (!$inittime) { $inittime = 0; }
my %bphash;
unless ($args[0] eq 'stat') { # or $args[0] eq 'enact') {
unless ($args[0] eq '') { # or $args[0] eq 'enact') {
$errored = 0;
xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: issue setdestiny request");
$sub_req->({ command => ['setdestiny'],
@ -562,6 +641,7 @@ sub process_request {
}
}
xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: starting to handle configuration...");
#Time to actually configure the nodes, first extract database data with the scalable calls
my $chaintab = xCAT::Table->new('chain');
my $noderestab = xCAT::Table->new('noderes'); #in order to detect per-node tftp directories
@ -589,10 +669,7 @@ sub process_request {
mkpath($tftpdir . "/xcat/xnba/nodes/");
my %response;
$response{node}->[0]->{name}->[0] = $_;
if ($args[0] eq 'stat') {
$response{node}->[0]->{data}->[0] = getstate($_, $tftpdir);
$::XNBA_callback->(\%response);
} elsif ($args[0]) { #If anything else, send it on to the destiny plugin, then setstate
if ($args[0]) { # Send it on to the destiny plugin, then setstate
my $rc;
my $errstr;
my $ent = $typehash->{$_}->[0];
@ -617,45 +694,38 @@ sub process_request {
}
}
}
xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: Finish to handle configurations");
# for offline operation, remove the dhcp entries
if ($args[0] eq 'offline') {
$sub_req->({ command => ['makedhcp'], arg => ['-d'], node => \@nodes }, $::XNBA_callback);
}
#dhcp stuff -- inittime is set when xcatd on sn is started
unless (($args[0] eq 'stat') || ($inittime) || ($args[0] eq 'offline')) {
unless (($inittime) || ($args[0] eq 'offline')) {
my $do_dhcpsetup = 1;
#my $sitetab = xCAT::Table->new('site');
#if ($sitetab) {
#(my $ref) = $sitetab->getAttribs({key => 'dhcpsetup'}, 'value');
my @entries = xCAT::TableUtils->get_site_attribute("dhcpsetup");
my $t_entry = $entries[0];
if (defined($t_entry)) {
if ($t_entry =~ /0|n|N/) { $do_dhcpsetup = 0; }
}
#}
if ($do_dhcpsetup) {
if ($::XNBA_request->{'_disparatetftp'}->[0]) { #reading hint from preprocess_command
xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: issue makedhcp request");
$sub_req->({ command => ['makedhcp'], arg => ['-l'],
node => \@nodes }, $::XNBA_callback);
} else {
xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: issue makedhcp request");
$sub_req->({ command => ['makedhcp'],
node => \@nodes }, $::XNBA_callback);
}
my @parameter;
push @parameter, '-l' if ($::request->{'_disparatetftp'}->[0]);
xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: issue makedhcp request");
$sub_req->({ command => ['makedhcp'],
arg => \@parameter,
node => \@nodes }, $::XNBA_callback);
} else {
xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: dhcpsetup=$do_dhcpsetup");
}
}
#now run the end part of the prescripts
unless ($args[0] eq 'stat') { # or $args[0] eq 'enact')
unless ($args[0] eq '') { # or $args[0] eq 'enact')
$errored = 0;
if ($::XNBA_request->{'_disparatetftp'}->[0]) { #the call is distrubuted to the service node already, so only need to handles my own children
if ($::XNBA_request->{'_disparatetftp'}->[0]) { #the call is distrubuted to the service node already, so only need to handle my own children
xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: issue runendpre request");
$sub_req->({ command => ['runendpre'],
node => \@nodes,
@ -663,7 +733,7 @@ sub process_request {
} else { #nodeset did not distribute to the service node, here we need to let runednpre to distribute the nodes to their masters
xCAT::MsgUtils->trace($verbose_on_off, "d", "xnba: issue runendpre request");
$sub_req->({ command => ['runendpre'],
node => \@rnodes,
node => \@nodes,
arg => [ $args[0] ] }, \&pass_along);
}
if ($errored) {
@ -674,6 +744,15 @@ sub process_request {
}
}
# Return error codes if there are failed nodes
if (%failurenodes) {
my $rsp;
$rsp->{errorcode}->[0] = 1;
$rsp->{error}->[0] = "Failed to generate xnba configurations for some node(s) on $::myxcatname. Check xCAT log file for more details.";
$::XNBA_callback->($rsp);
return;
}
}