From d29f467806e52ed4823fe10c7e4d24f693bb974c Mon Sep 17 00:00:00 2001
From: mellor <mellor@8638fb3e-16cb-4fca-ae20-7b5d299a9bcd>
Date: Tue, 5 Jul 2011 17:55:18 +0000
Subject: [PATCH] rollupdate - add support for translatenames

git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@10012 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
---
 xCAT-server/lib/xcat/plugins/rollupdate.pm    | 222 ++++++++++++++++--
 .../xcat/rollupdate/rollupdate.input.sample   |  17 ++
 .../rollupdate/rollupdate_all.input.sample    |  18 ++
 .../rollupdate_stateful.input.sample          |  17 ++
 4 files changed, 254 insertions(+), 20 deletions(-)

diff --git a/xCAT-server/lib/xcat/plugins/rollupdate.pm b/xCAT-server/lib/xcat/plugins/rollupdate.pm
index 58662431b..38bcb4025 100644
--- a/xCAT-server/lib/xcat/plugins/rollupdate.pm
+++ b/xCAT-server/lib/xcat/plugins/rollupdate.pm
@@ -20,6 +20,10 @@ require Data::Dumper;
 require Getopt::Long;
 require xCAT::MsgUtils;
 require File::Path;
+use Text::Balanced qw(extract_bracketed);
+use Safe;
+my $evalcpt = new Safe;
+
 use strict;
 use warnings;
 
@@ -559,6 +563,7 @@ sub rollupdate {
         return 1;
     }
 
+
     #
     # Build and submit scheduler jobs
     #
@@ -643,6 +648,15 @@ sub ll_jobs {
         return 1;
     }
 
+    # Translate xCAT names to LL names
+    $::XLATED = {};
+    if (defined($::FILEATTRS{translatenames}[0])) {
+       foreach my $xlate_stanza( @{ $::FILEATTRS{'translatenames'} } ) {
+          translate_names($xlate_stanza);
+       }
+    }
+    
+
     # Create LL floating resources for mutual exclusion support
     #   and max_updates
     if (&create_LL_mutex_resources($updategroup,$::updateall) > 0) {
@@ -774,16 +788,22 @@ sub ll_jobs {
         my ( $nodelist, $machinelist );
         my $machinecount = 0;
         foreach my $node ( @{ $updategroup->{$ugname} } ) {
-            if ( defined( $machines{$node} )
-                 && ( $machines{$node}{'mstatus'} eq "1" ) ) {
-                $machinelist .= " $machines{$node}{'mname'}";
+            my $xlated_node;
+            if ( defined ($::XLATED{$node}) ){
+               $xlated_node = $::XLATED{$node};
+            } else {
+               $xlated_node = $node;
+            }
+            if ( defined( $machines{$xlated_node} )
+                 && ( $machines{$xlated_node}{'mstatus'} eq "1" ) ) {
+                $machinelist .= " $machines{$xlated_node}{'mname'}";
                 $machinecount++;
                 $nodelist .= ",$node";
             } elsif ( $run_if_down eq 'yes' ) {
-                if ( defined( $machines{$node} ) ) {
+                if ( defined( $machines{$xlated_node} ) ) {
                    # llmkres -D will allow reserving down nodes as long
                    # as they are present in the machine list
-                    $machinelist .= " $machines{$node}{'mname'}";
+                    $machinelist .= " $machines{$xlated_node}{'mname'}";
                     $machinecount++;
                 }
                 $nodelist .= ",$node";
@@ -833,6 +853,11 @@ sub ll_jobs {
         if (defined($::FILEATTRS{bringuptimeout}[0])){
             push (@ugdflines, "bringuptimeout=$::FILEATTRS{bringuptimeout}[0]\n");
         } 
+        if (defined($::FILEATTRS{translatenames}[0])){
+            foreach my $xlate_stanza( @{ $::FILEATTRS{'translatenames'} } ) {
+                push (@ugdflines, "translatenames=$xlate_stanza\n");
+            }
+        } 
         if (defined($::FILEATTRS{skipshutdown}[0])){
             push (@ugdflines, "skipshutdown=$::FILEATTRS{skipshutdown}[0]\n");
         } 
@@ -884,7 +909,8 @@ sub ll_jobs {
             }           
 
             # Build reservation callback script 
-            my @rcblines;
+            
+my @rcblines;
             my $rcbcmd = $::FILEATTRS{'reservationcallback'}[0];
             if (!defined($rcbcmd)){ $rcbcmd = "$::XCATROOT/bin/runrollupdate"; }
             push (@rcblines, "#!/bin/sh \n");
@@ -953,6 +979,7 @@ sub ll_jobs {
                 if ( $jcline =~ /Feature/ ) {
                     $jcline =~ s/\"\s+/\"/g;
                     $jcline =~ s/\s+\"/\"/g;
+                    $jcline =~ s/\=\"/\= \"/g;
                 }
                 if ($lastcount) {
                     $jcline2 = $jcline;
@@ -1191,6 +1218,117 @@ sub check_policy {
 
 
 
+#----------------------------------------------------------------------------
+
+=head3   translate_names
+
+        Translate xCAT node names to scheduler names as requested by the user
+
+        Arguments:  $instructions - translation instructions of the form:
+                      <xcat_noderange>:/<pattern>/<replacement>/
+                    OR
+                      <xcat_noderange>:|<pattern>|<replacement>|
+        Returns: 
+        Globals:
+                hash:  $::XLATED{$node}=$xlated_name                
+                AND    $::XLATED{$xlated_name}=$node
+                to allow easy lookup in either direction
+        Error:
+        Example:
+
+        Comments:
+
+=cut
+
+#-----------------------------------------------------------------------------
+# This is a utility function to create a number out of a string, useful for things like round robin algorithms on unnumbered nodes
+sub mknum {
+    my $string = shift;
+    my $number=0;
+    foreach (unpack("C*",$string)) { #do big endian, then it would make 'fred' and 'free' be one number apart
+        $number += $_;
+    }
+    return $number;
+}
+$evalcpt->share('&mknum');
+$evalcpt->permit('require');
+
+sub translate_names{
+  my $instructions = shift;
+
+  my ($nr,$regexps) = split( /\:/, $instructions );
+  my @xCATnodes = xCAT::NodeRange::noderange($nr);
+
+  foreach my $xCATnode (@xCATnodes) {
+        my $xlated_node = $xCATnode;
+        my $datum = $regexps;
+  # The following is based on code copied from Table::getNodeAttribs
+        if ($datum =~ /^\/[^\/]*\/[^\/]*\/$/)
+        {
+            my $exp = substr($datum, 1);
+            chop $exp;
+            my @parts = split('/', $exp, 2);
+            $xlated_node =~ s/$parts[0]/$parts[1]/;
+            $datum = $xlated_node;
+        }
+        elsif ($datum =~ /^\|.*\|.*\|$/)
+       {
+            #Perform arithmetic and only arithmetic operations in bracketed issues on the right.
+            #Tricky part:  don't allow potentially dangerous code, only eval if
+            #to-be-evaled expression is only made up of ()\d+-/%$
+            #Futher paranoia?  use Safe module to make sure I'm good
+            my $exp = substr($datum, 1);
+            chop $exp;
+            my @parts = split('\|', $exp, 2);
+            my $curr;
+            my $next;
+            my $prev;
+            my $retval = $parts[1];
+            ($curr, $next, $prev) =
+              extract_bracketed($retval, '()', qr/[^()]*/);
+
+            unless($curr) { #If there were no paramaters to save, treat this one like a plain regex
+               undef $@; #extract_bracketed would have set $@ if it didn't return, undef $@
+               $retval = $xlated_node;
+               $retval =~ s/$parts[0]/$parts[1]/;
+               $datum = $retval;
+               unless ($datum =~ /^$/) { # ignore blank translations
+                 $xlated_node=$datum;
+               }
+               next; #skip the redundancy that follows otherwise
+            }
+            while ($curr)
+            {
+
+                #my $next = $comps[0];
+                my $value = $xlated_node;
+                $value =~ s/$parts[0]/$curr/;
+#                $value = $evalcpt->reval('use integer;'.$value);
+                $value = $evalcpt->reval($value);
+                $retval = $prev . $value . $next;
+                #use text::balanced extract_bracketed to parse each atom, make sure nothing but arith operators, parens, and numbers are in it to guard against code execution
+                ($curr, $next, $prev) =
+                  extract_bracketed($retval, '()', qr/[^()]*/);
+            }
+            undef $@;
+            #At this point, $retval is the expression after being arithmetically contemplated, a generated regex, and therefore
+            #must be applied in total
+            my $answval = $xlated_node;
+            $answval =~ s/$parts[0]/$retval/;
+            $datum = $answval; #$retval;
+        }
+        unless ($datum =~ /^$/) {
+            $::XLATED{$xCATnode}=$datum;
+            $::XLATED{$datum}=$xCATnode;
+        }
+
+  }
+#  print Dumper($::XLATED);
+  return ;
+}
+
+
+
 #----------------------------------------------------------------------------
 
 =head3   set_LL_feature
@@ -1788,9 +1926,15 @@ sub runrollupdate {
                  ($::DATAATTRS{skipshutdown}[0] eq "1") ) ) {
         $skipshutdown = 1;
     } 
+    $::XLATED = {};
+    if (defined($::DATAATTRS{translatenames}[0])) {
+       foreach my $xlate_stanza( @{ $::DATAATTRS{'translatenames'} } ) {
+          translate_names($xlate_stanza);
+       }
+    }
 
     # make sure nodes are in correct state
-    my $hostlist = &get_hostlist;
+    my $hostlist = &get_hostlist();
     if (! $hostlist ) {
         if ($::VERBOSE) { 
             open (RULOG, ">>$::LOGDIR/$::LOGFILE");
@@ -1804,7 +1948,7 @@ sub runrollupdate {
     }
 
     my $nltab = xCAT::Table->new('nodelist');
-    my @nodes = split( /\,/, $hostlist );
+    my @nodes = split( /\,/, $hostlist );    
     my $appstatus=xCAT::Utils->getAppStatus(\@nodes,"RollingUpdate");
     foreach my $node (@nodes) {
         unless ( defined($appstatus->{$node})
@@ -2054,16 +2198,25 @@ sub runrollupdate {
         my $totalwait = 0;
         my %ll_res;
         while ($not_done && $totalwait < ($statustimeout * 60)) {
+            my @query_bootnodes;
+            foreach my $bn (keys %ll_res) {
+                if ( ! ($ll_res{$bn}{removed}) ) {
+                    push (@query_bootnodes,$bn);
+                }
+            }
+            if ( ! @query_bootnodes ) {
+                @query_bootnodes = @bootnodes;
+            }
             if ($::VERBOSE) { 
                  open (RULOG, ">>$::LOGDIR/$::LOGFILE");
-                 print RULOG localtime()." $::ug_name:  Checking ".join(",",@bootnodes)." xCAT database $statusattr for value $statusval \n";
+                 print RULOG localtime()." $::ug_name:  Checking ".join(",",@query_bootnodes)." xCAT database $statusattr for value $statusval \n";
                  close (RULOG);
             }
             my $nltab_stats =
-                $nltab->getNodesAttribs( \@bootnodes, [ 'node', $statusattr ] );
-            %ll_res = ();
+                $nltab->getNodesAttribs( \@query_bootnodes, [ 'node', $statusattr ] );
+#           %ll_res = ();
             $not_done = 0;
-            foreach my $bn (@bootnodes) {
+            foreach my $bn (@query_bootnodes) {
                 if ( $nltab_stats->{$bn}->[0]->{$statusattr} !~ /$statusval/ ) {
                   $ll_res{$bn}{not_done}=1;
                   $not_done = 1;
@@ -2075,9 +2228,14 @@ sub runrollupdate {
                 ($::ll_reservation_id)){ 
                 my @remove_res;
                 foreach my $bn (keys %ll_res) {
-                    if ($ll_res{$bn}{remove} && ! $ll_res{$bn}{removed} ){
-                        push (@remove_res,$bn);
+                    if (($ll_res{$bn}{remove}) && (! $ll_res{$bn}{removed}) ){
+                        if ( defined($::XLATED{$bn}) ) {
+                            push (@remove_res,$::XLATED{$bn});
+                        } else {
+                            push (@remove_res,$bn);
+                        }
                         $ll_res{$bn}{removed} = 1;
+                        $ll_res{$bn}{not_done} = 0;
                     }
                 }
                 if (@remove_res) {
@@ -2086,8 +2244,12 @@ sub runrollupdate {
                 }
             }
             if ($not_done) {
-                sleep(20);
-                $totalwait += 20;
+                if ($::TEST) { 
+                    $not_done = 0; 
+                } else {
+                    sleep(20);
+                    $totalwait += 20;
+                }
             }
         }
         if ($not_done) { 
@@ -2109,10 +2271,19 @@ sub runrollupdate {
                         push (@error_nodes,$bn);
                     }
                 }
+                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
+                print RULOG "\n";
+                print RULOG localtime()." ERROR:  bringuptimeout exceeded for the following nodes: \n";
+                print RULOG join(",",@error_nodes);
+                print RULOG "\n";
                 xCAT::Utils->setAppStatus(\@error_nodes,"RollingUpdate","ERROR_bringuptimeout_exceeded");
-                if ( defined($remaining_nodes[0]) ) {
+                if ( @remaining_nodes ) {
+                  print RULOG localtime()." ERROR:  bringuptimeout exceeded for some nodes in a preceding bringuporder.  The following nodes will not be powered on: \n";
+                  print RULOG join(",",@remaining_nodes);
+                  print RULOG "\n";
                   xCAT::Utils->setAppStatus(\@remaining_nodes,"RollingUpdate","ERROR_bringuptimeout_exceeded_for_previous_node");
                 }
+                close (RULOG);
             }
             last;
         }
@@ -2237,7 +2408,16 @@ sub get_hostlist {
         print RULOG localtime()." Hostlist:  $status_fields[22] \n";
         close (RULOG);
     }
-    return $status_fields[22];
+    my $return_list;
+    foreach my $machine (split( /\,/, $status_fields[22])) {
+       if ( defined($::XLATED{$machine}) ) {
+           $return_list = $return_list.','.$::XLATED{$machine};
+       } else {
+           $return_list = $return_list.','.$machine;
+       }
+    }
+    $return_list =~ s/^,//;
+    return $return_list;
 }
 
 
@@ -2609,8 +2789,10 @@ sub llreconfig {
     my $runlocal=0;
     foreach my $m (@llms) {
        my ($sm,$rest) = split(/\./,$m);
+       my $xlated_sm = $sm;
+       if ( defined ($::XLATED{$sm}) ) { $xlated_sm = $::XLATED{$sm}; }
        if (xCAT::Utils->thishostisnot($m)) {
-           push(@llnodes, $sm) unless $have{$sm}++;
+           push(@llnodes, $xlated_sm) unless $have{$sm}++;
        } else {
            $runlocal=1;
        }
@@ -2634,7 +2816,7 @@ sub llreconfig {
     if ( scalar(@llnodes) > 0 ) {
         if ($::VERBOSE) {
             open (RULOG, ">>$::LOGDIR/$::LOGFILE");
-            print RULOG localtime()." Running command \'xdsh $llcms $llrms $cmd\'\n";
+            print RULOG localtime()." Running command \'xdsh ".join(',',@llnodes)." $cmd\'\n";
             close (RULOG);
         }
         if ($::TEST) {
diff --git a/xCAT-server/share/xcat/rollupdate/rollupdate.input.sample b/xCAT-server/share/xcat/rollupdate/rollupdate.input.sample
index a81937343..f1ffcff44 100644
--- a/xCAT-server/share/xcat/rollupdate/rollupdate.input.sample
+++ b/xCAT-server/share/xcat/rollupdate/rollupdate.input.sample
@@ -122,6 +122,23 @@ mutex_count=2
 #      nodegroup_mutex=block2IO
 #      nodegroup_mutex=block3IO
 
+# translatenames:
+# If your scheduler will be using names for nodes that are different from
+# xCAT node names (e.g. the scheduler is using a different administrative
+# network), you will need to tell xCAT how to translate from xCAT node names
+# to the node names registered with your scheduler.
+#
+# Syntax:
+#  translatenames=<xCAT_noderange>:/<pattern>/<replacement>/
+# where <pattern> and <replacement> are perl regular expressions to
+# be performed on the node names in <xCAT_noderange>.
+# See the xcatdb man page for more details on using regular expressions.
+# Multiple translatenames statements are allowed.  If an xCAT nodename
+# exists in more than one xCAT_noderange, the last translated value
+# will be used.
+#translatenames=service:|bb(\d+)s(\d+)|bb($1)sn($2)|
+#translatenames=compute:/\z/-hf2/
+
 
 
 
diff --git a/xCAT-server/share/xcat/rollupdate/rollupdate_all.input.sample b/xCAT-server/share/xcat/rollupdate/rollupdate_all.input.sample
index 05d047121..c3e684a94 100644
--- a/xCAT-server/share/xcat/rollupdate/rollupdate_all.input.sample
+++ b/xCAT-server/share/xcat/rollupdate/rollupdate_all.input.sample
@@ -68,6 +68,24 @@ updateall_nodes=compute
 updateall_nodecount=3
 
 
+# translatenames:
+# If your scheduler will be using names for nodes that are different from
+# xCAT node names (e.g. the scheduler is using a different administrative
+# network), you will need to tell xCAT how to translate from xCAT node names
+# to the node names registered with your scheduler.
+#
+# Syntax:
+#  translatenames=<xCAT_noderange>:/<pattern>/<replacement>/
+# where <pattern> and <replacement> are perl regular expressions to
+# be performed on the node names in <xCAT_noderange>.
+# See the xcatdb man page for more details on using regular expressions.
+# Multiple translatenames statements are allowed.  If an xCAT nodename
+# exists in more than one xCAT_noderange, the last translated value
+# will be used.
+#translatenames=service:|bb(\d+)s(\d+)|bb($1)sn($2)|
+#translatenames=compute:/\z/-hf2/
+
+
 
 # Scheduler Feature values 
 #    Node feature values that will be changed in the scheduler during the
diff --git a/xCAT-server/share/xcat/rollupdate/rollupdate_stateful.input.sample b/xCAT-server/share/xcat/rollupdate/rollupdate_stateful.input.sample
index ab5e9399b..23df5266e 100644
--- a/xCAT-server/share/xcat/rollupdate/rollupdate_stateful.input.sample
+++ b/xCAT-server/share/xcat/rollupdate/rollupdate_stateful.input.sample
@@ -91,6 +91,23 @@ updategroup=ns11(c4lpar211-c4lpar214)
 #mutex=block2a,block2b,block2c
 #mutex=block3a,block3b,block3c
 
+# translatenames:
+# If your scheduler will be using names for nodes that are different from
+# xCAT node names (e.g. the scheduler is using a different administrative
+# network), you will need to tell xCAT how to translate from xCAT node names
+# to the node names registered with your scheduler.
+#
+# Syntax:
+#  translatenames=<xCAT_noderange>:/<pattern>/<replacement>/
+# where <pattern> and <replacement> are perl regular expressions to
+# be performed on the node names in <xCAT_noderange>.
+# See the xcatdb man page for more details on using regular expressions.
+# Multiple translatenames statements are allowed.  If an xCAT nodename
+# exists in more than one xCAT_noderange, the last translated value
+# will be used.
+#translatenames=service:|bb(\d+)s(\d+)|bb($1)sn($2)|
+#translatenames=compute:/\z/-hf2/
+
 
 
 # maxupdates:  Maximum number of updategroups that can be updated at one time