mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-31 11:22:27 +00:00 
			
		
		
		
	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
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user