From a7e56eb7f7ff9f0c654c12d626ab54f694441025 Mon Sep 17 00:00:00 2001 From: ligc <ligc@8638fb3e-16cb-4fca-ae20-7b5d299a9bcd> Date: Mon, 2 Aug 2010 03:29:15 +0000 Subject: [PATCH] code drop for coordinated cluster bringup and shutdown git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@6933 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd --- xCAT-server/lib/perl/xCAT/PPC.pm | 266 ++++++++++++++++++-------- xCAT-server/lib/perl/xCAT/SvrUtils.pm | 219 +++++++++++++++++++++ 2 files changed, 404 insertions(+), 81 deletions(-) diff --git a/xCAT-server/lib/perl/xCAT/PPC.pm b/xCAT-server/lib/perl/xCAT/PPC.pm index 1072325d3..e0ef7964c 100644 --- a/xCAT-server/lib/perl/xCAT/PPC.pm +++ b/xCAT-server/lib/perl/xCAT/PPC.pm @@ -1295,6 +1295,7 @@ sub preprocess_request { #if ($req->{_xcatdest}) { return [$req]; } #exit if preprocessed if ($req->{_xcatpreprocessed}->[0] == 1 ) { return [$req]; } my $callback = shift; + my $subreq = shift; my @requests; ##################################### @@ -1319,98 +1320,201 @@ sub preprocess_request { #################################### $package =~ s/xCAT_plugin:://; - #################################### - # Prompt for usage if needed and on MN - #################################### - my $noderange = $req->{node}; #Should be arrayref + my $deps; + my $nodeseq; + if (($req->{command}->[0] eq 'rpower') && (!grep(/^--nodeps$/, @{$req->{arg}})) + && (($req->{op}->[0] eq 'on') || ($req->{op}->[0] eq 'off') + || ($req->{op}->[0] eq 'softoff') || ($req->{op}->[0] eq 'reset'))) { + + $deps = xCAT::SvrUtils->build_deps($req->{node}, $req->{op}->[0]); + + # no dependencies at all + if (!defined($deps)) { + foreach my $node (@{$req->{node}}) { + $nodeseq->[0]->{$node} = 1; + } + } else { + $nodeseq = xCAT::SvrUtils->handle_deps($deps, $req->{node}, $callback); + } + } + + if ($nodeseq == 1) { + return undef; + } + # no dependency defined in deps table, + # generate the $nodeseq hash + if (!$nodeseq) { + foreach my $node (@{$req->{node}}) { + $nodeseq->[0]->{$node} = 1; + } + } + + my $i = 0; + for ($i = 0; $i < scalar(@{$nodeseq}); $i++) { + #reset the @requests for this loop + @requests = (); + #################################### + # Prompt for usage if needed and on MN + #################################### + my @dnodes = keys(%{$nodeseq->[$i]}); + + if (scalar(@dnodes) == 0) { + next; + } + if (scalar(@{$nodeseq}) > 1) { + my %output; + my $cnodes = join(',', @dnodes); + $output{data} = ["Performing action against the following nodes: $cnodes"]; + $callback->( \%output ); + } + my $noderange = \@dnodes; + $req->{node} = \@dnodes; #Should be arrayref + #$req->{noderange} = \@dnodes; #Should be arrayref my $command = $req->{command}->[0]; - my $extrargs = $req->{arg}; - my @exargs=($req->{arg}); - if (ref($extrargs)) { - @exargs=@$extrargs; - } - if ($ENV{'XCATBYPASS'}){ - my $usage_string=xCAT::Usage->parseCommand($command, @exargs); - if ($usage_string) { - $callback->({data=>[$usage_string]}); - $req = {}; - return ; + my $extrargs = $req->{arg}; + my @exargs=($req->{arg}); + if (ref($extrargs)) { + @exargs=@$extrargs; } - if (!$noderange) { - $usage_string="Missing noderange"; - $callback->({data=>[$usage_string]}); - $req = {}; - return ; + if ($ENV{'XCATBYPASS'}){ + my $usage_string=xCAT::Usage->parseCommand($command, @exargs); + if ($usage_string) { + $callback->({data=>[$usage_string]}); + $req = {}; + return ; + } + if (!$noderange) { + $usage_string="Missing noderange"; + $callback->({data=>[$usage_string]}); + $req = {}; + return ; + } } - } - - - ################################################################## - # get the HCPs for the LPARs in order to figure out which service - # nodes to send the requests to - ################################################################### - my $hcptab_name = ($package eq "fsp" or $package eq "bpa") ? "ppcdirect" : "ppchcp"; - my $hcptab = xCAT::Table->new( $hcptab_name ); - unless ($hcptab ) { - $callback->({data=>["Cannot open $hcptab_name table"]}); - $req = {}; - return; - } - # Check if each node is hcp - my %hcp_hash=(); - my @missednodes=(); - foreach ( @$noderange ) { - my ($ent) = $hcptab->getAttribs( {hcp=>$_},"hcp" ); - if ( !defined( $ent )) { - push @missednodes, $_; - next; - } - push @{$hcp_hash{$_}{nodes}}, $_; - } - - #check if the left-over nodes are lpars - if (@missednodes > 0) { - my $ppctab = xCAT::Table->new("ppc"); - unless ($ppctab) { - $callback->({data=>["Cannot open ppc table"]}); + + + ################################################################## + # get the HCPs for the LPARs in order to figure out which service + # nodes to send the requests to + ################################################################### + my $hcptab_name = ($package eq "fsp" or $package eq "bpa") ? "ppcdirect" : "ppchcp"; + my $hcptab = xCAT::Table->new( $hcptab_name ); + unless ($hcptab ) { + $callback->({data=>["Cannot open $hcptab_name table"]}); $req = {}; return; } - foreach my $node (@missednodes) { - my $ent=$ppctab->getNodeAttribs($node,['hcp']); - if (defined($ent->{hcp})) { push @{$hcp_hash{$ent->{hcp}}{nodes}}, $node;} - else { - $callback->({data=>["The node $node is neither a hcp nor an lpar"]}); + # Check if each node is hcp + my %hcp_hash=(); + my @missednodes=(); + foreach ( @$noderange ) { + my ($ent) = $hcptab->getAttribs( {hcp=>$_},"hcp" ); + if ( !defined( $ent )) { + push @missednodes, $_; + next; + } + push @{$hcp_hash{$_}{nodes}}, $_; + } + + #check if the left-over nodes are lpars + if (@missednodes > 0) { + my $ppctab = xCAT::Table->new("ppc"); + unless ($ppctab) { + $callback->({data=>["Cannot open ppc table"]}); $req = {}; return; } + foreach my $node (@missednodes) { + my $ent=$ppctab->getNodeAttribs($node,['hcp']); + if (defined($ent->{hcp})) { push @{$hcp_hash{$ent->{hcp}}{nodes}}, $node;} + else { + $callback->({data=>["The node $node is neither a hcp nor an lpar"]}); + $req = {}; + return; + } + } + } + + # find service nodes for the HCPs + # build an individual request for each service node + my $service = "xcat"; + my @hcps=keys(%hcp_hash); + my $sn = xCAT::Utils->get_ServiceNode(\@hcps, $service, "MN"); + + # build each request for each service node + foreach my $snkey (keys %$sn) + { + #$callback->({data=>["The service node $snkey "]}); + my $reqcopy = {%$req}; + $reqcopy->{'_xcatdest'} = $snkey; + $reqcopy->{_xcatpreprocessed}->[0] = 1; + my $hcps1=$sn->{$snkey}; + my @nodes=(); + foreach (@$hcps1) { + push @nodes, @{$hcp_hash{$_}{nodes}}; + } + @nodes = sort @nodes; + $reqcopy->{node} = \@nodes; + #print "nodes=@nodes\n"; + push @requests, $reqcopy; + } + + # No dependency, use the original logic + if (scalar(@{$nodeseq}) == 1) { + return \@requests; + } + + # do all the new request entries in this loop + my $j = 0; + for ($j = 0; $j < scalar(@requests); $j++) { + $subreq->(\%{$requests[$j]}, $callback); + } + + # We can not afford waiting 'msdelay' for each node, + # for performance considerations, + # use the maximum msdelay for all nodes + my $delay = 0; + # do not need to calculate msdelay for the last loop + if ($i < scalar(@{$nodeseq})) { + foreach my $reqnode (@{$req->{node}}) { + foreach my $depnode (keys %{$deps}) { + foreach my $depent (@{$deps->{$depnode}}) { + # search if the 'nodedep' includes the $reqnode + # do not use grep, performance problem! + foreach my $depentnode (split(/,/, $depent->{'nodedep'})) { + if ($depentnode eq $reqnode) { + if ($depent->{'msdelay'} > $delay) { + $delay = $depent->{'msdelay'}; + } + } + } + } + } + } + } + + if ($ENV{'XCATDEBUG'}) { + my %output; + $output{data} = ["delay = $delay"]; + $callback->( \%output ); + } + #convert from millisecond to second + $delay /= 1000.0; + if ($delay & ($i < scalar(@{$nodeseq}))) { + my %output; + $output{data} = ["Waiting $delay seconds for node dependencies\n"]; + $callback->( \%output ); + if ($ENV{'XCATDEBUG'}) { + $output{data} = ["Before sleep $delay seconds"]; + $callback->( \%output ); + } + Time::HiRes::sleep($delay); + if ($ENV{'XCATDEBUG'}) { + $output{data} = ["Wake up!"]; + $callback->( \%output ); + } } } - - # find service nodes for the HCPs - # build an individual request for each service node - my $service = "xcat"; - my @hcps=keys(%hcp_hash); - my $sn = xCAT::Utils->get_ServiceNode(\@hcps, $service, "MN"); - - # build each request for each service node - foreach my $snkey (keys %$sn) - { - #$callback->({data=>["The service node $snkey "]}); - my $reqcopy = {%$req}; - $reqcopy->{'_xcatdest'} = $snkey; - $reqcopy->{_xcatpreprocessed}->[0] = 1; - my $hcps1=$sn->{$snkey}; - my @nodes=(); - foreach (@$hcps1) { - push @nodes, @{$hcp_hash{$_}{nodes}}; - } - @nodes = sort @nodes; - $reqcopy->{node} = \@nodes; - #print "nodes=@nodes\n"; - push @requests, $reqcopy; - } - return \@requests; + return undef; } #################################### # Parse arguments diff --git a/xCAT-server/lib/perl/xCAT/SvrUtils.pm b/xCAT-server/lib/perl/xCAT/SvrUtils.pm index 930b36e46..e66b64d3b 100644 --- a/xCAT-server/lib/perl/xCAT/SvrUtils.pm +++ b/xCAT-server/lib/perl/xCAT/SvrUtils.pm @@ -1116,5 +1116,224 @@ sub setupStatemnt { } +#------------------------------------------------------------------------------- + +=head3 build_deps + Look up the "deps" table to generate the dependencies for the nodes + Arguments: + nodes: The nodes list in an array reference + Returns: + depset: dependencies hash reference + Globals: + none + Error: + none + Example: + my $deps = xCAT::SvrUtils->build_deps($req->{node}); + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub build_deps() +{ + my ($class, $nodes, $cmd) = @_; + my %depshash = (); + + my $depstab = xCAT::Table->new('deps'); + if (!defined($depstab)) { + return undef; + } + + my $depset = $depstab->getNodesAttribs($nodes,[qw(nodedep msdelay cmd)]); + if (!defined($depset)) + { + return undef; + } + foreach my $node (@$nodes) { + # Delete the nodes without dependencies from the hash + if (!defined($depset->{$node}[0])) { + delete($depset->{$node}); + } + } + + # the deps hash does not check the 'cmd', + # use the realdeps to reflect the 'cmd' also + my $realdep; + foreach my $node (@$nodes) { + foreach my $depent (@{$depset->{$node}}){ + my @depcmd = split(/,/, $depent->{'cmd'}); + #dependency match + if (grep(/^$cmd$/, @depcmd)) { + #expand the noderange + my @nodedep = xCAT::NodeRange::noderange($depent->{'nodedep'},1); + my $depsnode = join(',', @nodedep); + if ($depsnode) { + $depent->{'nodedep'} = $depsnode; + push @{$realdep->{$node}}, $depent; + } + } + } + } + return $realdep; +} + + +#------------------------------------------------------------------------------- + +=head3 handle_deps + Group the nodes according to the deps hash returned from build_deps + Arguments: + deps: the dependencies hash reference + nodes: The nodes list in an array reference + $callback: sub request callback + Returns: + nodeseq: the nodes categorized based on dependencies + returns 1 if runs into problem + Globals: + none + Error: + none + Example: + my $deps = xCAT::SvrUtils->handle_deps($deps, $req->{node}); + Comments: + none +=cut + +#------------------------------------------------------------------------------- +sub handle_deps() +{ + my ($class, $dephash, $nodes, $callback) = @_; + + # a small subroutine to remove some specific node from a comma separated list + sub remove_node_from_list() + { + my ($string, $nodetoremove) = @_; + my @arr = split(',', $string); + my @newarr = (); + foreach my $tmp (@arr) { + if ($tmp ne $nodetoremove) { + push @newarr, $tmp; + } + } + return join(',', @newarr); + } + + # This is an example of the deps hash ref + # DB<3> x $deps + #0 HASH(0x239db47c) + # 'aixcn1' => ARRAY(0x23a26be0) + # 0 HASH(0x23a21968) + # 'cmd' => 'off' + # 'msdelay' => 10000 + # 'node' => 'aixcn1' + # 'nodedep' => 'aixmn2' + # 'aixsn1' => ARRAY(0x23a2219c) + # 0 HASH(0x23a21728) + # 'cmd' => 'off' + # 'msdelay' => 10000 + # 'node' => 'aixsn1' + # 'nodedep' => 'aixcn1' + + #copy the dephash, do not manipulate the subroutine argument $dephash + my $deps; + foreach my $node (keys %{$dephash}) { + my $i = 0; + for ($i = 0; $i < scalar(@{$dephash->{$node}}); $i++) { + foreach my $attr (keys %{$dephash->{$node}->[$i]}) { + $deps->{$node}->[$i]->{$attr} = $dephash->{$node}->[$i]->{$attr}; + } + } + } + + #needs to search the nodes list a lot of times + #using hash will be more effective + my %nodelist; + foreach my $node (@{$nodes}) { + $nodelist{$node} = 1; + } + + + # check if any depnode is not in the nodelist, + # print warning message + my $depsnotinargs; + foreach my $node (keys %{$deps}){ + my $keepnode = 0; + foreach my $depent (@{$deps->{$node}}){ + # an autonomy dependency group? + foreach my $dep (split(/,/, $depent->{'nodedep'})) { + if (!defined($nodelist{$dep})) { + $depsnotinargs->{$dep} = 1; + $depent->{'nodedep'} = &remove_node_from_list($depent->{'nodedep'}, $dep); + } + } + if ($depent->{'nodedep'}) { + $keepnode = 1; + } + } + if (!$keepnode) { + delete($deps->{$node}); + } + } + if (scalar(keys %{$depsnotinargs}) > 0) { + my $n = join(',', keys %{$depsnotinargs}); + + my %output; + $output{data} = ["The following nodes are dependencies for some nodes passed in through arguments, but not in the command arguments: $n, make sure these nodes are in correct state"]; + $callback->( \%output ); + } + + + + my $arrayindex = 0; + my $nodeseq; + #handle all the nodes + while (keys %nodelist) { + + my @curnodes; + foreach my $node (keys %nodelist) { + #no dependency + if (!defined($deps->{$node})) { + $nodeseq->[$arrayindex]->{$node} = 1; + delete($nodelist{$node}); + push @curnodes, $node; + } + } + + if (scalar(@curnodes) == 0) { + # no nodes in this loop at all, + # means infinite loop??? + my %output; + my $nodesinlist = join(',', keys %nodelist); + $output{errorcode}=1; + $output{data} = ["Loop dependency, check your deps table, may be related to the following nodes: $nodesinlist"]; + $callback->( \%output ); + return 1; + } + + # update deps for the next loop + # remove the node from the 'nodedep' attribute + my $keepnode = 0; + foreach my $nodeindeps (keys %{$deps}) { + my $keepnode = 0; + foreach my $depent (@{$deps->{$nodeindeps}}){ + #remove the curnodes from the 'nodedep' + foreach my $nodetoremove (@curnodes) { + $depent->{'nodedep'} = &remove_node_from_list($depent->{'nodedep'}, $nodetoremove); + } + if ($depent->{'nodedep'}) { + $keepnode = 1; + } + } + if (!$keepnode) { + delete($deps->{$nodeindeps}); + } + } + + # the round is over, jump to the next arrary entry + $arrayindex++; + } + return $nodeseq; +} 1;