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;