diff --git a/docs/source/guides/admin-guides/references/man5/site.5.rst b/docs/source/guides/admin-guides/references/man5/site.5.rst index 9e48d38c2..7fb2bf6cc 100644 --- a/docs/source/guides/admin-guides/references/man5/site.5.rst +++ b/docs/source/guides/admin-guides/references/man5/site.5.rst @@ -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) diff --git a/docs/source/guides/admin-guides/references/man8/nodeset.8.rst b/docs/source/guides/admin-guides/references/man8/nodeset.8.rst index 35edfcd45..50d12e87c 100644 --- a/docs/source/guides/admin-guides/references/man8/nodeset.8.rst +++ b/docs/source/guides/admin-guides/references/man8/nodeset.8.rst @@ -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. diff --git a/perl-xCAT/xCAT/NetworkUtils.pm b/perl-xCAT/xCAT/NetworkUtils.pm index d54bf253b..9b65054a2 100755 --- a/perl-xCAT/xCAT/NetworkUtils.pm +++ b/perl-xCAT/xCAT/NetworkUtils.pm @@ -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; +} + #------------------------------------------------------------------------------- diff --git a/perl-xCAT/xCAT/Schema.pm b/perl-xCAT/xCAT/Schema.pm index 1314d5249..2dfdb7076 100755 --- a/perl-xCAT/xCAT/Schema.pm +++ b/perl-xCAT/xCAT/Schema.pm @@ -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" . diff --git a/perl-xCAT/xCAT/Scope.pm b/perl-xCAT/xCAT/Scope.pm index a7aaf2588..39bb3da44 100644 --- a/perl-xCAT/xCAT/Scope.pm +++ b/perl-xCAT/xCAT/Scope.pm @@ -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; } diff --git a/perl-xCAT/xCAT/Usage.pm b/perl-xCAT/xCAT/Usage.pm index 82ac2eff8..94b1c94af 100755 --- a/perl-xCAT/xCAT/Usage.pm +++ b/perl-xCAT/xCAT/Usage.pm @@ -484,7 +484,7 @@ Options: "Usage: Common: nodeset [-h|--help|-v|--version] - nodeset [shell|boot|runcmd=bmcsetup|osimage[=]|offline|shutdown|stat]", + nodeset [shell|boot|runcmd=bmcsetup|osimage[=]|offline|shutdown|stat [-a]]", "rmflexnode" => "Usage: rmflexnode [-h|--help|-v|--version] diff --git a/xCAT-client/pods/man8/nodeset.8.pod b/xCAT-client/pods/man8/nodeset.8.pod index 15618d46d..2fd8bc957 100644 --- a/xCAT-client/pods/man8/nodeset.8.pod +++ b/xCAT-client/pods/man8/nodeset.8.pod @@ -4,7 +4,7 @@ B - set the boot state for a noderange =head1 B -B I [B | B | B | B | B[=I] | B | B] +B I [B | B [B<-a>]| B | B | B[=I] | B | B] B I B[=I] [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 -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 is set, using B<-a> to display them on all available service nodes. =item B diff --git a/xCAT-server/lib/xcat/plugins/grub2.pm b/xCAT-server/lib/xcat/plugins/grub2.pm index f6600d22c..03482eeb4 100644 --- a/xCAT-server/lib/xcat/plugins/grub2.pm +++ b/xCAT-server/lib/xcat/plugins/grub2.pm @@ -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; + } + } #---------------------------------------------------------------------------- diff --git a/xCAT-server/lib/xcat/plugins/petitboot.pm b/xCAT-server/lib/xcat/plugins/petitboot.pm index 04f7bcdf7..af3320ae2 100644 --- a/xCAT-server/lib/xcat/plugins/petitboot.pm +++ b/xCAT-server/lib/xcat/plugins/petitboot.pm @@ -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; + } } #---------------------------------------------------------------------------- diff --git a/xCAT-server/lib/xcat/plugins/xnba.pm b/xCAT-server/lib/xcat/plugins/xnba.pm index dd8fe9d1c..7250f1b6c 100644 --- a/xCAT-server/lib/xcat/plugins/xnba.pm +++ b/xCAT-server/lib/xcat/plugins/xnba.pm @@ -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; + } + }