mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-11-03 21:02:34 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			2960 lines
		
	
	
		
			99 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			2960 lines
		
	
	
		
			99 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
#!/usr/bin/env perl -w
 | 
						|
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
 | 
						|
#####################################################
 | 
						|
#
 | 
						|
#  xCAT plugin package to handle rolling updates
 | 
						|
#
 | 
						|
#####################################################
 | 
						|
 | 
						|
package xCAT_plugin::rollupdate;
 | 
						|
 | 
						|
BEGIN
 | 
						|
{
 | 
						|
    $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
 | 
						|
}
 | 
						|
 | 
						|
require xCAT::NodeRange;
 | 
						|
require xCAT::Table;
 | 
						|
require xCAT::Utils;
 | 
						|
require xCAT::TableUtils;
 | 
						|
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;
 | 
						|
 | 
						|
#
 | 
						|
# Globals
 | 
						|
#
 | 
						|
$::LOGDIR="/var/log/xcat";
 | 
						|
$::LOGFILE="rollupdate.log";
 | 
						|
 | 
						|
#------------------------------------------------------------------------------
 | 
						|
 | 
						|
=head1    rollupdate   
 | 
						|
 | 
						|
This program module file supports the cluster rolling update functions.
 | 
						|
 | 
						|
Supported commands:
 | 
						|
   rollupdate - Create scheduler job command files and submit the jobs
 | 
						|
   runrollupdate - Reboot the updategroup in response to request from scheduler 
 | 
						|
                  job
 | 
						|
 | 
						|
If adding to this file, please take a moment to ensure that:
 | 
						|
 | 
						|
    1. Your contrib has a readable pod header describing the purpose and use of
 | 
						|
      the subroutine.
 | 
						|
 | 
						|
    2. Your contrib is under the correct heading and is in alphabetical order
 | 
						|
    under that heading.
 | 
						|
 | 
						|
    3. You have run tidypod on this file and saved the html file
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#------------------------------------------------------------------------------
 | 
						|
 | 
						|
=head2    Cluster Rolling Update
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#------------------------------------------------------------------------------
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3  handled_commands
 | 
						|
 | 
						|
        Return a list of commands handled by this plugin
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub handled_commands {
 | 
						|
    return {
 | 
						|
             rollupdate  => "rollupdate",
 | 
						|
             runrollupdate => "rollupdate"
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   preprocess_request
 | 
						|
 | 
						|
 | 
						|
        Arguments:
 | 
						|
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
 | 
						|
        Error:
 | 
						|
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub preprocess_request {
 | 
						|
 | 
						|
    #   set management node as server for all requests
 | 
						|
    #   any requests sent to service node need to get
 | 
						|
    #   get sent up to the MN
 | 
						|
 | 
						|
    my $req = shift;
 | 
						|
     #if already preprocessed, go straight to request
 | 
						|
    if (   (defined($req->{_xcatpreprocessed}))
 | 
						|
        && ($req->{_xcatpreprocessed}->[0] == 1))
 | 
						|
    {
 | 
						|
        return [$req];
 | 
						|
    }
 | 
						|
 | 
						|
    $req->{_xcatdest} = xCAT::TableUtils->get_site_Master();
 | 
						|
    return [$req];
 | 
						|
}
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   process_request
 | 
						|
 | 
						|
        Process the rolling update commands
 | 
						|
 | 
						|
        Arguments:
 | 
						|
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
 | 
						|
        Error:
 | 
						|
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub process_request {
 | 
						|
    $::request  = shift;
 | 
						|
    $::CALLBACK = shift;
 | 
						|
    $::SUBREQ   = shift;
 | 
						|
    my $ret;
 | 
						|
 | 
						|
    # globals used by all subroutines.
 | 
						|
    $::command   = $::request->{command}->[0];
 | 
						|
    $::args      = $::request->{arg};
 | 
						|
    $::stdindata = $::request->{stdin}->[0];
 | 
						|
 | 
						|
    # figure out which cmd and call the subroutine to process
 | 
						|
    if ( $::command eq "rollupdate" ) {
 | 
						|
        $ret = &rollupdate($::request);
 | 
						|
    }
 | 
						|
    elsif ( $::command eq "runrollupdate" ) {
 | 
						|
        $ret = &runrollupdate($::request);
 | 
						|
    }
 | 
						|
 | 
						|
    return $ret;
 | 
						|
}
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3  rollupdate_usage
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
        Globals:
 | 
						|
 | 
						|
        Error:
 | 
						|
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
# display the usage
 | 
						|
sub rollupdate_usage {
 | 
						|
    my $rsp;
 | 
						|
    push @{ $rsp->{data} },
 | 
						|
      "\nUsage: rollupdate - Submit cluster rolling update jobs \n";
 | 
						|
    push @{ $rsp->{data} }, "  rollupdate [-h | --help | -?] \n";
 | 
						|
    push @{ $rsp->{data} },
 | 
						|
      "  rollupdate [-V | --verbose] [-v | --version] [-t | --test] \n ";
 | 
						|
    push @{ $rsp->{data} },
 | 
						|
"      <STDIN> - stanza file, see /opt/xcat/share/xcat/rollupdate/rollupdate.input.sample";
 | 
						|
    push @{ $rsp->{data} }, "                for example \n";
 | 
						|
    xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3  runrollupdate_usage
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
        Globals:
 | 
						|
 | 
						|
        Error:
 | 
						|
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
# display the usage
 | 
						|
sub runrollupdate_usage {
 | 
						|
    my $rsp;
 | 
						|
    push @{ $rsp->{data} },
 | 
						|
      "\nUsage: runrollupdate - Run submitted cluster rolling update job \n";
 | 
						|
    push @{ $rsp->{data} }, "  runrollupdate [-h | --help | -?] \n";
 | 
						|
    push @{ $rsp->{data} },
 | 
						|
      "  runrollupdate [-V | --verbose] [-v | --version] scheduler datafile \n ";
 | 
						|
    push @{ $rsp->{data} },
 | 
						|
    xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   processArgs
 | 
						|
 | 
						|
        Process the command line and any input files provided on cmd line.
 | 
						|
 | 
						|
        Arguments:
 | 
						|
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - just print usage
 | 
						|
                2 - error
 | 
						|
        Globals:
 | 
						|
 | 
						|
        Error:
 | 
						|
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub processArgs {
 | 
						|
    my $gotattrs = 0;
 | 
						|
 | 
						|
    if ( defined ($::args) && @{$::args} ) {
 | 
						|
        @ARGV = @{$::args};
 | 
						|
    }
 | 
						|
    else {
 | 
						|
 | 
						|
        #       return 2;   # can run with no args right now
 | 
						|
    }
 | 
						|
 | 
						|
    #    if (scalar(@ARGV) <= 0) {
 | 
						|
    #        return 2;
 | 
						|
    #    }
 | 
						|
 | 
						|
    # parse the options
 | 
						|
    # options can be bundled up like -vV, flag unsupported options
 | 
						|
    Getopt::Long::Configure( "bundling", "no_ignore_case", "no_pass_through" );
 | 
						|
    Getopt::Long::GetOptions(
 | 
						|
                              'help|h|?'  => \$::opt_h,
 | 
						|
                              'test|t'    => \$::opt_t,
 | 
						|
                              'verbose|V' => \$::opt_V,
 | 
						|
                              'version|v' => \$::opt_v,
 | 
						|
    );
 | 
						|
 | 
						|
    # Option -h for Help
 | 
						|
    # if user specifies "-t" & "-h" they want a list of valid attrs
 | 
						|
    if ( defined($::opt_h) ) {
 | 
						|
        return 2;
 | 
						|
    }
 | 
						|
 | 
						|
    #  opt_t not yet supported
 | 
						|
    if ( defined($::opt_t) ) {
 | 
						|
        #$::test = 1;
 | 
						|
        $::TEST = 1;
 | 
						|
       # my $rsp;
 | 
						|
       # push @{ $rsp->{data} }, "The \'-t\' option is not yet implemented.";
 | 
						|
       # xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
       # return 2;
 | 
						|
    }
 | 
						|
 | 
						|
    # Option -v for version
 | 
						|
    if ( defined($::opt_v) ) {
 | 
						|
        my $rsp;
 | 
						|
        my $version = xCAT::Utils->Version();
 | 
						|
        push @{ $rsp->{data} }, "$::command - $version\n";
 | 
						|
        xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
        return 1;    # no usage - just exit
 | 
						|
    }
 | 
						|
 | 
						|
    # Option -V for verbose output
 | 
						|
    if ( defined($::opt_V) ) {
 | 
						|
        $::verbose = 1;
 | 
						|
        $::VERBOSE = 1;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    if ( $::command eq "rollupdate" ) {
 | 
						|
        # process @ARGV
 | 
						|
        #while (my $a = shift(@ARGV))
 | 
						|
        #{
 | 
						|
        #  no args for command yet
 | 
						|
        #}
 | 
						|
 | 
						|
        # process the <stdin> input file
 | 
						|
        if ( defined($::stdindata) ) {
 | 
						|
            my $rc = readFileInput($::stdindata);
 | 
						|
            if ($rc) {
 | 
						|
                my $rsp;
 | 
						|
                push @{ $rsp->{data} }, "Could not process file input data.\n";
 | 
						|
                xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                return 1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            # No <stdin> stanza file, print usage
 | 
						|
            return 2;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    elsif ( $::command eq "runrollupdate" ) {
 | 
						|
        # process @ARGV
 | 
						|
        $::scheduler = shift(@ARGV);
 | 
						|
        $::datafile = shift(@ARGV);
 | 
						|
        $::ll_reservation_id = shift(@ARGV);
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   readFileInput
 | 
						|
 | 
						|
        Process the command line input piped in from a stanza file.
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
        Error:
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
                        Set  %::FILEATTRS
 | 
						|
                                (i.e.- $::FILEATTRS{attr}=[val])
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub readFileInput {
 | 
						|
    my ($filedata) = @_;
 | 
						|
 | 
						|
    my @lines = split /\n/, $filedata;
 | 
						|
 | 
						|
    my $rf_rc=0;
 | 
						|
    my $rf_err= "";
 | 
						|
    my $prev_attr = "";
 | 
						|
    foreach my $l (@lines) {
 | 
						|
 | 
						|
        # skip blank and comment lines
 | 
						|
        next if ( $l =~ /^\s*$/ || $l =~ /^\s*#/ );
 | 
						|
 | 
						|
        # process a real line
 | 
						|
        if ( $l =~ /^\s*(\w+)\s*=\s*(.*)\s*/ ) {
 | 
						|
            my $attr = $1;
 | 
						|
            my $val  = $2;
 | 
						|
            my $orig_attr = $attr;
 | 
						|
            my $orig_val  = $val;
 | 
						|
            $attr =~ s/^\s*//;       # Remove any leading whitespace
 | 
						|
            $attr =~ s/\s*$//;       # Remove any trailing whitespace
 | 
						|
            $attr =~ tr/A-Z/a-z/;    # Convert to lowercase
 | 
						|
            $val  =~ s/^\s*//;
 | 
						|
            $val  =~ s/\s*$//;
 | 
						|
 | 
						|
            # Convert the following values to lowercase
 | 
						|
            if ( ($attr eq 'scheduler') ||
 | 
						|
                 ($attr eq 'updateall') ||
 | 
						|
                 ($attr eq 'update_if_down') ||
 | 
						|
                 ($attr eq 'skipshutdown') ) {
 | 
						|
                $val =~ tr/A-Z/a-z/;   
 | 
						|
                if ( ($attr eq 'scheduler') &&
 | 
						|
                     ($val ne 'loadleveler')   ) {
 | 
						|
                    $rf_err = "Stanza \'$orig_attr = $orig_val\' is not valid.  Valid values are \'loadleveler\'.";                    
 | 
						|
                    $rf_rc=1;
 | 
						|
                    my $rsp;
 | 
						|
                    push @{ $rsp->{data} },$rf_err;
 | 
						|
                    xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
                }
 | 
						|
                if ( ($attr eq 'updateall') &&
 | 
						|
                     (($val ne 'yes') &&
 | 
						|
                      ($val ne 'y')   &&
 | 
						|
                      ($val ne 'no')  &&
 | 
						|
                      ($val ne 'n')      )     ) {
 | 
						|
                    $rf_err = "Stanza \'$orig_attr = $orig_val\' is not valid.  Valid values are \'yes\' or \'no\'.";                    
 | 
						|
                    $rf_rc=1;
 | 
						|
                    my $rsp;
 | 
						|
                    push @{ $rsp->{data} },$rf_err;
 | 
						|
                    xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
                }
 | 
						|
                if ( ($attr eq 'update_if_down') &&
 | 
						|
                     (($val ne 'yes') &&
 | 
						|
                      ($val ne 'y')   &&
 | 
						|
                      ($val ne 'no')  &&
 | 
						|
                      ($val ne 'n')   &&
 | 
						|
                      ($val ne 'cancel')      )     ) {
 | 
						|
                    $rf_err = "Stanza \'$orig_attr = $orig_val\' is not valid.  Valid values are \'yes\', \'no\', or \'cancel\'.";                    
 | 
						|
                    $rf_rc=1;
 | 
						|
                    my $rsp;
 | 
						|
                    push @{ $rsp->{data} },$rf_err;
 | 
						|
                    xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
                }
 | 
						|
                if ( ($attr eq 'skipshutdown') &&
 | 
						|
                     (($val ne 'yes') &&
 | 
						|
                      ($val ne 'y')   &&
 | 
						|
                      ($val ne 'no')  &&
 | 
						|
                      ($val ne 'n')      )       ) {
 | 
						|
                    $rf_err = "Stanza \'$orig_attr = $orig_val\' is not valid.  Valid values are \'yes\' or \'no\'.";                    
 | 
						|
                    $rf_rc=1;
 | 
						|
                    my $rsp;
 | 
						|
                    push @{ $rsp->{data} },$rf_err;
 | 
						|
                    xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            # Remove surrounding quotes in the following values
 | 
						|
            if ( ($attr eq 'bringupappstatus') ) {
 | 
						|
                $val =~ s/^['"]//;   
 | 
						|
                $val =~ s/['"]$//;   
 | 
						|
            }
 | 
						|
 
 | 
						|
            # Set some required defaults if not specified
 | 
						|
            if (($prev_attr eq "prescript") && ($attr ne "prescriptnodes")) {
 | 
						|
                push ( @{ $::FILEATTRS{'prescriptnodes'} }, 'ALL_NODES_IN_UPDATEGROUP' );
 | 
						|
            }
 | 
						|
            if (($prev_attr eq "outofbandcmd") && ($attr ne "outofbandnodes")) {
 | 
						|
                push ( @{ $::FILEATTRS{'outofbandnodes'} }, 'ALL_NODES_IN_UPDATEGROUP' );
 | 
						|
            }
 | 
						|
            if (($prev_attr eq "mutex") && ($attr ne "mutex_count")) {
 | 
						|
                push ( @{ $::FILEATTRS{'mutex_count'} }, '1' );
 | 
						|
            }
 | 
						|
            if (($prev_attr eq "nodegroup_mutex") && ($attr eq "mutex_count")) {
 | 
						|
               $attr = "nodegroup_mutex_count";
 | 
						|
            }
 | 
						|
            if (($prev_attr eq "nodegroup_mutex") && ($attr ne "nodegroup_mutex_count")) {
 | 
						|
                push ( @{ $::FILEATTRS{'nodegroup_mutex_count'} }, '1' );
 | 
						|
            }
 | 
						|
 | 
						|
            # set the value in the hash for this entry
 | 
						|
            push( @{ $::FILEATTRS{$attr} }, $val );
 | 
						|
            $prev_attr = $attr;
 | 
						|
        }
 | 
						|
    }    # end while - go to next line
 | 
						|
 | 
						|
    return $rf_rc;
 | 
						|
}
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   rollupdate
 | 
						|
 | 
						|
        Support for the xCAT rollupdate command.
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
 | 
						|
        Error:
 | 
						|
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
sub rollupdate {
 | 
						|
 | 
						|
    my $rc    = 0;
 | 
						|
    my $error = 0;
 | 
						|
 | 
						|
    # process the command line
 | 
						|
    $rc = &processArgs;
 | 
						|
    if ( $rc != 0 ) {
 | 
						|
 | 
						|
        # rc: 0 - ok, 1 - return, 2 - help, 3 - error
 | 
						|
        if ( $rc != 1 ) {
 | 
						|
            &rollupdate_usage;
 | 
						|
        }
 | 
						|
        return ( $rc - 1 );
 | 
						|
    }
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        unless ( -d $::LOGDIR) {
 | 
						|
            unless ( File::Path::mkpath($::LOGDIR) ) {
 | 
						|
                my $rsp;
 | 
						|
                push @{ $rsp->{data} }, "Could not create directory $::LOGDIR, logging is disabled.";
 | 
						|
                xCAT::MsgUtils->message( "W", $rsp, $::CALLBACK );
 | 
						|
                $::VERBOSE = 0;
 | 
						|
                $::verbose = 0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        my $rsp;
 | 
						|
        push @{ $rsp->{data} }, "Running rollupdate command... ";
 | 
						|
        xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG "\n\n";
 | 
						|
        print RULOG localtime()." Running rollupdate command...\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
 | 
						|
    #
 | 
						|
    # Build updategroup nodelists
 | 
						|
    #
 | 
						|
    my %updategroup;
 | 
						|
 | 
						|
    # Check for updateall and required stanzas
 | 
						|
    $::updateall=0;
 | 
						|
    $::updateall_nodecount=1;
 | 
						|
    if ( defined($::FILEATTRS{updateall}[0])  &&
 | 
						|
     ( ($::FILEATTRS{updateall}[0] eq 'yes') ||
 | 
						|
       ($::FILEATTRS{updateall}[0] eq 'y'  ) ) ) {
 | 
						|
        $::updateall=1;
 | 
						|
        if ( defined($::FILEATTRS{updateall_nodes}[0])){
 | 
						|
            my $ugname = "UPDATEALL".time();
 | 
						|
            my $ugval = $::FILEATTRS{updateall_nodes}[0];
 | 
						|
            @{ $updategroup{$ugname} } = xCAT::NodeRange::noderange($ugval);
 | 
						|
        } else {
 | 
						|
            my $rsp;
 | 
						|
            push @{ $rsp->{data} },
 | 
						|
"Error processing stanza input:  updateall=yes but no updateall_nodes specified. ";
 | 
						|
            xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
        if ( defined($::FILEATTRS{updateall_nodecount}[0]) ) {
 | 
						|
            $::updateall_nodecount=$::FILEATTRS{updateall_nodecount}[0];
 | 
						|
        } else {
 | 
						|
            my $rsp;
 | 
						|
            push @{ $rsp->{data} },
 | 
						|
"Error processing stanza input:  updateall=yes but no updateall_nodecount specified. ";
 | 
						|
            xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
    # Standard update (NOT updateall)
 | 
						|
        foreach my $ugline ( @{ $::FILEATTRS{'updategroup'} } ) {
 | 
						|
            my ( $ugname, $ugval ) = split( /\(/, $ugline );
 | 
						|
            $ugval =~ s/\)$//;    # remove trailing ')'
 | 
						|
            @{ $updategroup{$ugname} } = xCAT::NodeRange::noderange($ugval);
 | 
						|
            if ( xCAT::NodeRange::nodesmissed() ) {
 | 
						|
                my $rsp;
 | 
						|
                push @{ $rsp->{data} }, "Error processing stanza line: ";
 | 
						|
                push @{ $rsp->{data} }, "updategroup=" . $ugline;
 | 
						|
                push @{ $rsp->{data} }, "Invalid nodes in noderange: "
 | 
						|
                  . join( ',', xCAT::NodeRange::nodesmissed() );
 | 
						|
                xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
                return 1;
 | 
						|
            }
 | 
						|
            if ($::VERBOSE) {
 | 
						|
                my $rsp;
 | 
						|
                my $prt_ugn = join(',',@{$updategroup{$ugname}});
 | 
						|
                push @{ $rsp->{data} }, "Creating update group $ugname with nodes: ";
 | 
						|
                push @{ $rsp->{data} }, $prt_ugn;
 | 
						|
                xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." Creating update group $ugname with nodes: \n";
 | 
						|
                print RULOG  "$prt_ugn\n";
 | 
						|
                close (RULOG);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        foreach my $mgline ( @{ $::FILEATTRS{'mapgroups'} } ) {
 | 
						|
            #my @ugnamelist = xCAT::NameRange::namerange( $mgline, 0 );
 | 
						|
            my @ugnamelist = xCAT::NodeRange::noderange( $mgline, 0, 1, genericrange=>1 );
 | 
						|
            foreach my $ugname (@ugnamelist) {
 | 
						|
                @{ $updategroup{$ugname} } = xCAT::NodeRange::noderange($ugname);
 | 
						|
                if ( xCAT::NodeRange::nodesmissed() ) {
 | 
						|
                    my $rsp;
 | 
						|
                    push @{ $rsp->{data} }, "Error processing stanza line: ";
 | 
						|
                    push @{ $rsp->{data} }, "mapgroups=" . $mgline;
 | 
						|
                    push @{ $rsp->{data} }, "Invalid nodes in group $ugname: "
 | 
						|
                      . join( ',', xCAT::NodeRange::nodesmissed() );
 | 
						|
                    xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
                    return 1;
 | 
						|
                }
 | 
						|
                if ($::VERBOSE) {
 | 
						|
                    my $rsp;
 | 
						|
                    my $prt_ugn = join(',',@{$updategroup{$ugname}});
 | 
						|
                    push @{ $rsp->{data} }, "Creating update group $ugname with nodes: ";
 | 
						|
                    push @{ $rsp->{data} }, $prt_ugn;
 | 
						|
                    xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                    open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                    print RULOG localtime()." Creating update group $ugname with nodes: \n";
 | 
						|
                    print RULOG  "$prt_ugn\n";
 | 
						|
                    close (RULOG);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    unless (%updategroup) {
 | 
						|
        my $rsp;
 | 
						|
        push @{ $rsp->{data} },
 | 
						|
"Error processing stanza input:  No updategroup or mapgroups entries found. ";
 | 
						|
        xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    #
 | 
						|
    # Build and submit scheduler jobs
 | 
						|
    #
 | 
						|
    my $scheduler = $::FILEATTRS{'scheduler'}[0];
 | 
						|
    if (    ( !$scheduler )
 | 
						|
         || ( $scheduler eq "loadleveler" ) )
 | 
						|
    {
 | 
						|
        $rc = ll_jobs( \%updategroup );
 | 
						|
    }
 | 
						|
    else {
 | 
						|
 | 
						|
        # TODO:  support scheduler plugins here
 | 
						|
        my $rsp;
 | 
						|
        push @{ $rsp->{data} }, "Error processing stanza line: ";
 | 
						|
        push @{ $rsp->{data} }, "scheduler=" . $::FILEATTRS{'scheduler'}[0];
 | 
						|
        push @{ $rsp->{data} }, "Scheduler not supported";
 | 
						|
        xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    return $rc;
 | 
						|
}
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   ll_jobs
 | 
						|
 | 
						|
        Build and submit LoadLeveler jobs
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
 | 
						|
        Error:
 | 
						|
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
sub ll_jobs {
 | 
						|
    my $updategroup = shift;
 | 
						|
    my $rc          = 0;
 | 
						|
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        my $rsp;
 | 
						|
        push @{ $rsp->{data} }, "Creating LL job command files ";
 | 
						|
        xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." Creating LL job command files \n";
 | 
						|
        close (RULOG);
 | 
						|
     }
 | 
						|
 | 
						|
    # Verify scheduser exists and can run xCAT runrollupdate cmd
 | 
						|
    # Get LL userid
 | 
						|
    my $lluser = $::FILEATTRS{scheduser}[0];
 | 
						|
    unless ( defined($lluser) ) {
 | 
						|
        my $rsp;
 | 
						|
        push @{ $rsp->{data} },
 | 
						|
          "Error processing stanza input:  No scheduser entries found. ";
 | 
						|
        xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    my ( $login, $pass, $uid, $gid );
 | 
						|
    ( $login, $pass, $uid, $gid ) = getpwnam($lluser);
 | 
						|
    unless ( defined($uid) ) {
 | 
						|
        my $rsp;
 | 
						|
        push @{ $rsp->{data} },
 | 
						|
"Error processing stanza input:  scheduser userid $lluser not defined in system. ";
 | 
						|
        xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    if ( &check_policy($lluser,'runrollupdate') ) {
 | 
						|
        my $rsp;
 | 
						|
        push @{ $rsp->{data} },
 | 
						|
          "Error processing stanza input:  scheduser userid $lluser not listed in xCAT policy table for runrollupdate command.  Add to policy table and ensure userid has ssh credentials for running xCAT commands. ";
 | 
						|
        xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
        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) {
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    #
 | 
						|
    # Load job command file template
 | 
						|
    #
 | 
						|
    my $tmpl_file_name = $::FILEATTRS{'jobtemplate'}[0];
 | 
						|
    unless ( defined($tmpl_file_name) ) {
 | 
						|
        my $rsp;
 | 
						|
        push @{ $rsp->{data} },
 | 
						|
          "Error processing stanza input:  No jobtemplate entries found. ";
 | 
						|
        xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    my $TMPL_FILE;
 | 
						|
    unless ( open( $TMPL_FILE, "<", $tmpl_file_name ) ) {
 | 
						|
        my $rsp;
 | 
						|
        push @{ $rsp->{data} },
 | 
						|
"Error processing stanza input:  jobtemplate file $tmpl_file_name not found. ";
 | 
						|
        xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        my $rsp;
 | 
						|
        push @{ $rsp->{data} }, "Reading LL job template file $tmpl_file_name ";
 | 
						|
        xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." Reading LL job template file $tmpl_file_name \n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
    my @lines = <$TMPL_FILE>;
 | 
						|
    close $TMPL_FILE;
 | 
						|
 | 
						|
    # Query LL for list of machines and their status
 | 
						|
    my $cmd = "llstatus -r %n %sta 2>/dev/null";
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        my $rsp;
 | 
						|
        push @{ $rsp->{data} }, "Running command: $cmd ";
 | 
						|
        xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." Running command: $cmd  \n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
    my @llstatus = xCAT::Utils->runcmd( $cmd, 0 );
 | 
						|
    if ( $::RUNCMD_RC != 0 ) {
 | 
						|
        my $rsp;
 | 
						|
        push @{ $rsp->{data} }, "Could not run llstatus command.";
 | 
						|
        push @{ $rsp->{data} }, @llstatus;
 | 
						|
        xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." Could not run llstatus command.  \n";
 | 
						|
        print RULOG @llstatus;
 | 
						|
        close (RULOG);
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    my %machines;
 | 
						|
    foreach my $machineline (@llstatus) {
 | 
						|
        my ( $mlong, $mshort, $mstatus );
 | 
						|
        ( $mlong, $mstatus ) = split( /\!/, $machineline );
 | 
						|
        ($mshort) = split( /\./, $mlong );
 | 
						|
        $machines{$mlong} = { mname => $mlong, mstatus => $mstatus };
 | 
						|
        if ( !( $mlong eq $mshort ) ) {
 | 
						|
            $machines{$mshort} = { mname => $mlong, mstatus => $mstatus };
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    #
 | 
						|
    # Generate job command file for each updategroup
 | 
						|
    #
 | 
						|
 | 
						|
    my $lljobs_dir = $::FILEATTRS{jobdir}[0];
 | 
						|
    unless ( defined($lljobs_dir) ) {
 | 
						|
        my $rsp;
 | 
						|
        push @{ $rsp->{data} },
 | 
						|
          "Error processing stanza input:  No jobdir entries found. ";
 | 
						|
        xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    unless ( -d $lljobs_dir ) {
 | 
						|
        unless ( File::Path::mkpath($lljobs_dir) ) {
 | 
						|
            my $rsp;
 | 
						|
            push @{ $rsp->{data} }, "Could not create directory $lljobs_dir";
 | 
						|
            xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
        unless ( chown( $uid, $gid, $lljobs_dir ) ) {
 | 
						|
            my $rsp;
 | 
						|
            push @{ $rsp->{data} },
 | 
						|
              "Could not change owner of directory $lljobs_dir to $lluser";
 | 
						|
            xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    my $nodestatus = $::FILEATTRS{bringupstatus}[0];
 | 
						|
    my $appstatus = $::FILEATTRS{bringupappstatus}[0];
 | 
						|
    my $df_statusline = "";
 | 
						|
    if ( defined($appstatus) ) {
 | 
						|
        $df_statusline = "bringupappstatus=$appstatus\n";
 | 
						|
    } elsif ( defined($nodestatus) ) {
 | 
						|
        $df_statusline = "bringupstatus=$nodestatus\n";
 | 
						|
    } else {
 | 
						|
        $df_statusline = "bringupstatus=booted\n";
 | 
						|
    }
 | 
						|
 | 
						|
    my $run_if_down = "cancel";
 | 
						|
    if ( defined($::FILEATTRS{update_if_down}[0]) ) {
 | 
						|
        $run_if_down = $::FILEATTRS{update_if_down}[0];
 | 
						|
        $run_if_down =~ tr/[A-Z]/[a-z]/;
 | 
						|
        if ( $run_if_down eq 'y' ) { $run_if_down = 'yes'; }
 | 
						|
        if ( $::updateall && ($run_if_down eq 'yes') ) { 
 | 
						|
            $run_if_down = 'cancel'; 
 | 
						|
            my $rsp;
 | 
						|
            push @{ $rsp->{data} }, "update_all=yes, but update_if_down is yes which is not allowed.  update_if_down=cancel will be assumed. ";
 | 
						|
            xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    my @calldirectly;
 | 
						|
  ugloop: foreach my $ugname ( keys %{$updategroup} ) {
 | 
						|
 | 
						|
        $::updateall_feature = " ";
 | 
						|
        if ($::updateall) { $::updateall_feature = "XCAT_$ugname"; }
 | 
						|
        # Build node substitution strings
 | 
						|
        my ( $nodelist, $machinelist );
 | 
						|
        my $machinecount = 0;
 | 
						|
        foreach my $node ( @{ $updategroup->{$ugname} } ) {
 | 
						|
            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{$xlated_node} ) ) {
 | 
						|
                   # llmkres -D will allow reserving down nodes as long
 | 
						|
                   # as they are present in the machine list
 | 
						|
                    $machinelist .= " $machines{$xlated_node}{'mname'}";
 | 
						|
                    $machinecount++;
 | 
						|
                }
 | 
						|
                $nodelist .= ",$node";
 | 
						|
            } elsif ( $run_if_down eq 'cancel' ) {
 | 
						|
                my $rsp;
 | 
						|
                push @{ $rsp->{data} },
 | 
						|
"Node $node is not active in LL and \"update_if_down=cancel\".  Update for updategroup $ugname is canceled.";
 | 
						|
                xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
                ++$rc;
 | 
						|
                next ugloop;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if ( ($machinecount == 0) && ($::updateall) ) {
 | 
						|
            my $rsp;
 | 
						|
            push @{ $rsp->{data} },
 | 
						|
"\"updateall=yes\" and \"update_if_down=no\", but there are no nodes specifed in the updateall noderange that are currently active in LoadLeveler.  Update for updategroup $ugname is canceled.";
 | 
						|
            xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
            ++$rc;
 | 
						|
            next ugloop;
 | 
						|
        }
 | 
						|
 | 
						|
        if ( defined($nodelist) ) { $nodelist =~ s/^\,//; }
 | 
						|
        # Build updategroup data file 
 | 
						|
        my @ugdflines;
 | 
						|
        push (@ugdflines, "# xCAT Rolling Update data file for update group $ugname \n");
 | 
						|
        push (@ugdflines, "\n");
 | 
						|
        push (@ugdflines, "updategroup=$ugname\n");
 | 
						|
        if ($::updateall){
 | 
						|
            push (@ugdflines, "updatefeature=$::updateall_feature\n");
 | 
						|
        } else { 
 | 
						|
            push (@ugdflines, "nodelist=$nodelist\n");
 | 
						|
        }
 | 
						|
        if (defined($::FILEATTRS{oldfeature}[0])){
 | 
						|
            push (@ugdflines, "oldfeature=$::FILEATTRS{oldfeature}[0]\n");
 | 
						|
        } 
 | 
						|
        if (defined($::FILEATTRS{newfeature}[0])){
 | 
						|
            push (@ugdflines, "newfeature=$::FILEATTRS{newfeature}[0]\n");
 | 
						|
        } 
 | 
						|
        if (defined($::FILEATTRS{reconfiglist}[0])){
 | 
						|
            push (@ugdflines, "reconfiglist=$::FILEATTRS{reconfiglist}[0]\n");
 | 
						|
        } 
 | 
						|
        push (@ugdflines, "\n");
 | 
						|
        push (@ugdflines, &get_prescripts($nodelist));
 | 
						|
        if (defined($::FILEATTRS{shutdowntimeout}[0])){
 | 
						|
            push (@ugdflines, "shutdowntimeout=$::FILEATTRS{shutdowntimeout}[0]\n");
 | 
						|
        } 
 | 
						|
        push (@ugdflines, &get_outofband($nodelist));
 | 
						|
        push (@ugdflines, &get_bringuporder($nodelist));
 | 
						|
        push (@ugdflines, $df_statusline);
 | 
						|
        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");
 | 
						|
        } 
 | 
						|
        my $ugdf_file = $lljobs_dir . "/rollupdate_" . $ugname . ".data";
 | 
						|
        my $UGDFFILE;
 | 
						|
        unless ( open( $UGDFFILE, ">$ugdf_file" ) ) {
 | 
						|
            my $rsp;
 | 
						|
            push @{ $rsp->{data} }, "Could not open file $ugdf_file";
 | 
						|
            xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
        if ($::VERBOSE) {
 | 
						|
            my $rsp;
 | 
						|
            push @{ $rsp->{data} }, "Writing xCAT rolling update data file $ugdf_file ";
 | 
						|
            xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
            open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
            print RULOG localtime()." Writing xCAT rolling update data file $ugdf_file \n";
 | 
						|
            close (RULOG);
 | 
						|
        }
 | 
						|
        print $UGDFFILE @ugdflines;
 | 
						|
        close($UGDFFILE);
 | 
						|
        chown( $uid, $gid, $ugdf_file );
 | 
						|
 | 
						|
        if ($machinecount > 0) {
 | 
						|
            my $llhl_file;
 | 
						|
            if ( !$::updateall ) {
 | 
						|
                # Build LL hostlist file
 | 
						|
                $machinelist =~ s/^\s+//;
 | 
						|
                my $hllines = $machinelist;
 | 
						|
                $hllines =~ s/"//g;
 | 
						|
                $hllines =~ s/\s+/\n/g;
 | 
						|
                $hllines .= "\n";
 | 
						|
                $llhl_file = $lljobs_dir . "/rollupdate_" . $ugname . ".hostlist";
 | 
						|
                my $HLFILE;
 | 
						|
                unless ( open( $HLFILE, ">$llhl_file" ) ) {
 | 
						|
                    my $rsp;
 | 
						|
                    push @{ $rsp->{data} }, "Could not open file $llhl_file";
 | 
						|
                    xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
                    return 1;
 | 
						|
                }
 | 
						|
                if ($::VERBOSE) {
 | 
						|
                    my $rsp;
 | 
						|
                    push @{ $rsp->{data} }, "Writing LL hostlist file $llhl_file ";
 | 
						|
                       xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                }
 | 
						|
                print $HLFILE $hllines;
 | 
						|
                close($HLFILE);
 | 
						|
                chown( $uid, $gid, $llhl_file );
 | 
						|
            }           
 | 
						|
 | 
						|
            # Build reservation callback script 
 | 
						|
            
 | 
						|
my @rcblines;
 | 
						|
            my $rcbcmd = $::FILEATTRS{'reservationcallback'}[0];
 | 
						|
            if (!defined($rcbcmd)){ $rcbcmd = "$::XCATROOT/bin/runrollupdate"; }
 | 
						|
            push (@rcblines, "#!/bin/sh \n");
 | 
						|
            push (@rcblines, "# LL Reservation Callback script for xCAT Rolling Update group $ugname \n");
 | 
						|
            push (@rcblines, "\n");
 | 
						|
            push (@rcblines, "if [ \"\$2\"  ==  \"RESERVATION_ACTIVE\"  ] ; then\n");
 | 
						|
            my $send_verbose = "";
 | 
						|
            if ($::VERBOSE) {$send_verbose="--verbose";}
 | 
						|
            push (@rcblines, "    $rcbcmd $send_verbose loadleveler $ugdf_file \$1 &\n");
 | 
						|
            push (@rcblines, "fi \n");
 | 
						|
            push (@rcblines, "\n");
 | 
						|
            my $llrcb_file = $lljobs_dir . "/rollupdate_" . $ugname . ".rsvcb";
 | 
						|
            my $RCBFILE;
 | 
						|
            unless ( open( $RCBFILE, ">$llrcb_file" ) ) {
 | 
						|
                my $rsp;
 | 
						|
                push @{ $rsp->{data} }, "Could not open file $llrcb_file";
 | 
						|
                xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
                return 1;
 | 
						|
            }
 | 
						|
            if ($::VERBOSE) {
 | 
						|
                my $rsp;
 | 
						|
                push @{ $rsp->{data} }, "Writing LL reservation callback script $llrcb_file ";
 | 
						|
                xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." Writing LL reservation callback script $llrcb_file \n";
 | 
						|
                close (RULOG);
 | 
						|
            }
 | 
						|
            print $RCBFILE @rcblines;
 | 
						|
            close($RCBFILE);
 | 
						|
            chown( $uid, $gid, $llrcb_file );
 | 
						|
            chmod( 0700, $llrcb_file );
 | 
						|
 | 
						|
            # Build output file
 | 
						|
            my $mutex_string = &get_mutex($ugname);
 | 
						|
            my $lastcount = 0;
 | 
						|
            my $llcount = $machinecount;
 | 
						|
            if (($::updateall) &&
 | 
						|
                ($machinecount gt $::updateall_nodecount)) {
 | 
						|
                $lastcount = $machinecount % $::updateall_nodecount;
 | 
						|
                $llcount = $::updateall_nodecount;
 | 
						|
            }
 | 
						|
            my @jclines;
 | 
						|
            my @jclines2;
 | 
						|
            foreach my $line (@lines) {
 | 
						|
                my $jcline = $line;
 | 
						|
                my $jcline2;
 | 
						|
                $jcline =~ s/\[\[NODESET\]\]/$ugname/;
 | 
						|
                $jcline =~ s/\[\[JOBDIR\]\]/$lljobs_dir/;
 | 
						|
                if (defined($nodelist)){
 | 
						|
                    $jcline =~ s/\[\[XNODELIST\]\]/$nodelist/;
 | 
						|
                } else {
 | 
						|
                    $jcline =~ s/\[\[XNODELIST\]\]//;
 | 
						|
                }
 | 
						|
                if (defined($llhl_file)){
 | 
						|
                    $jcline =~ s/\[\[LLHOSTFILE\]\]/$llhl_file/;
 | 
						|
                } else {
 | 
						|
                    $jcline =~ s/\[\[LLHOSTFILE\]\]//;
 | 
						|
                }
 | 
						|
                if (defined($::FILEATTRS{oldfeature}[0])){
 | 
						|
                    $jcline =~ s/\[\[OLDFEATURE\]\]/$::FILEATTRS{oldfeature}[0]/;
 | 
						|
                } else {
 | 
						|
                    $jcline =~ s/\[\[OLDFEATURE\]\]//;
 | 
						|
                }
 | 
						|
                $jcline =~ s/\[\[UPDATEALLFEATURE\]\]/$::updateall_feature/;
 | 
						|
                $jcline =~ s/\[\[MUTEXRESOURCES\]\]/$mutex_string/;
 | 
						|
                # LL is VERY picky about extra blanks in Feature string
 | 
						|
                if ( $jcline =~ /Feature/ ) {
 | 
						|
                    $jcline =~ s/\"\s+/\"/g;
 | 
						|
                    $jcline =~ s/\s+\"/\"/g;
 | 
						|
                    $jcline =~ s/\=\"/\= \"/g;
 | 
						|
                }
 | 
						|
                if ($lastcount) {
 | 
						|
                    $jcline2 = $jcline;
 | 
						|
                    $jcline2 =~ s/\[\[LLCOUNT\]\]/$lastcount/;
 | 
						|
                    push( @jclines2, $jcline2 );
 | 
						|
                }
 | 
						|
                if ( $jcline =~ /\[\[LLCOUNT\]\]/ ) {
 | 
						|
                   $jcline =~ s/\[\[LLCOUNT\]\]/$llcount/;
 | 
						|
                }
 | 
						|
                push( @jclines, $jcline );
 | 
						|
            }
 | 
						|
            my $lljob_file = $lljobs_dir . "/rollupdate_" . $ugname . ".cmd";
 | 
						|
            my $JOBFILE;
 | 
						|
            unless ( open( $JOBFILE, ">$lljob_file" ) ) {
 | 
						|
                my $rsp;
 | 
						|
                push @{ $rsp->{data} }, "Could not open file $lljob_file";
 | 
						|
                xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
                return 1;
 | 
						|
            }
 | 
						|
            if ($::VERBOSE) {
 | 
						|
                my $rsp;
 | 
						|
                push @{ $rsp->{data} }, "Writing LL job command file $lljob_file ";
 | 
						|
                xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." Writing LL job command file $lljob_file \n";
 | 
						|
                close (RULOG);
 | 
						|
            }
 | 
						|
            print $JOBFILE @jclines;
 | 
						|
            close($JOBFILE);
 | 
						|
            chown( $uid, $gid, $lljob_file );
 | 
						|
            my $lljob_file2 = $lljobs_dir . "/rollupdate_LAST_" . $ugname . ".cmd";
 | 
						|
            if ($lastcount) {
 | 
						|
                my $JOBFILE2;
 | 
						|
                unless ( open( $JOBFILE2, ">$lljob_file2" ) ) {
 | 
						|
                    my $rsp;
 | 
						|
                    push @{ $rsp->{data} }, "Could not open file $lljob_file2";
 | 
						|
                    xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
                    return 1;
 | 
						|
                }
 | 
						|
                if ($::VERBOSE) {
 | 
						|
                    my $rsp;
 | 
						|
                    push @{ $rsp->{data} }, "Writing LL job command file $lljob_file2 ";
 | 
						|
                    xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                    open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                    print RULOG localtime()." Writing LL job command file $lljob_file2 \n";
 | 
						|
                    close (RULOG);
 | 
						|
                }
 | 
						|
                print $JOBFILE2 @jclines2;
 | 
						|
                close($JOBFILE2);
 | 
						|
                chown( $uid, $gid, $lljob_file2 );
 | 
						|
            }
 | 
						|
 | 
						|
            if ($::updateall) {
 | 
						|
                &set_LL_feature($machinelist,$::updateall_feature);
 | 
						|
 | 
						|
            }
 | 
						|
 | 
						|
            # Need to change status before actually submitting LL jobs
 | 
						|
            # If LL jobs happen to run right away, the update code checking
 | 
						|
            # for the status may run before we've had a chance to actually update it
 | 
						|
            my $nltab = xCAT::Table->new('nodelist');
 | 
						|
            my @nodes = split( /\,/, $nodelist );
 | 
						|
            xCAT::TableUtils->setAppStatus(\@nodes,"RollingUpdate","update_job_submitted");
 | 
						|
            # Submit LL reservation
 | 
						|
            my $downnodes = "-D";
 | 
						|
            if ($::updateall){$downnodes = " ";}
 | 
						|
            my $cmd = qq~su - $lluser "-c llmkres -x -d $::FILEATTRS{'reservationduration'}[0] -f $lljob_file -p $llrcb_file $downnodes "~;
 | 
						|
            my $cmd2 = qq~su - $lluser "-c llmkres -x -d $::FILEATTRS{'reservationduration'}[0] -f $lljob_file2 -p $llrcb_file $downnodes "~;
 | 
						|
            if ($::VERBOSE) {
 | 
						|
                my $rsp;
 | 
						|
                push @{ $rsp->{data} }, "Running command: $cmd ";
 | 
						|
                xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." Running command: $cmd  \n";
 | 
						|
                close (RULOG);
 | 
						|
            }
 | 
						|
            my @llsubmit; 
 | 
						|
            if ($::TEST) {
 | 
						|
                my $rsp;
 | 
						|
                push @{ $rsp->{data} }, "In TEST mode.  Will NOT run command: $cmd ";
 | 
						|
                   xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                $::RUNCMD_RC = 0;
 | 
						|
            } else {
 | 
						|
                my $submit_count = 1;
 | 
						|
                if ($::updateall){ 
 | 
						|
                   $submit_count = int($machinecount / $::updateall_nodecount);
 | 
						|
                   if ($submit_count == 0) {$submit_count = 1;}
 | 
						|
                }
 | 
						|
                for (1..$submit_count) {
 | 
						|
                    @llsubmit = xCAT::Utils->runcmd( "$cmd", 0 );
 | 
						|
                    if ( $::RUNCMD_RC != 0 ) {
 | 
						|
                        my $rsp;
 | 
						|
                        push @{ $rsp->{data} }, "Could not run llmkres command.";
 | 
						|
                        push @{ $rsp->{data} }, @llsubmit;
 | 
						|
                        xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
                        return 1;
 | 
						|
                    }
 | 
						|
                    if ( $::VERBOSE ) {
 | 
						|
                        my $rsp;
 | 
						|
                        push @{ $rsp->{data} }, @llsubmit;
 | 
						|
                        xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                        print RULOG @llsubmit;
 | 
						|
                        close (RULOG);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                if ($lastcount) {
 | 
						|
                    if ($::VERBOSE) {
 | 
						|
                        my $rsp;
 | 
						|
                        push @{ $rsp->{data} }, "Running command: $cmd2 ";
 | 
						|
                        xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                        print RULOG localtime()." Running command: $cmd2  \n";
 | 
						|
                        close (RULOG);
 | 
						|
                    }
 | 
						|
                    @llsubmit = xCAT::Utils->runcmd( "$cmd2", 0 );
 | 
						|
                    if ( $::RUNCMD_RC != 0 ) {
 | 
						|
                        my $rsp;
 | 
						|
                        push @{ $rsp->{data} }, "Could not run llmkres command.";
 | 
						|
                        push @{ $rsp->{data} }, @llsubmit;
 | 
						|
                        xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
                        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                        print RULOG @llsubmit;
 | 
						|
                        close (RULOG);
 | 
						|
                        return 1;
 | 
						|
                    }
 | 
						|
                    if ( $::VERBOSE ) {
 | 
						|
                        my $rsp;
 | 
						|
                        push @{ $rsp->{data} }, @llsubmit;
 | 
						|
                        xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                        print RULOG @llsubmit;
 | 
						|
                        close (RULOG);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            } 
 | 
						|
        }
 | 
						|
        elsif ( defined($nodelist) ) {
 | 
						|
 | 
						|
            # No nodes in LL to submit job to -- not able to schedule.
 | 
						|
            # Call xCAT directly for all other nodes.
 | 
						|
            # TODO - this will serialize updating the updategroups
 | 
						|
            #         is this okay, or do we want forked child processes?
 | 
						|
            push @calldirectly, $ugname;
 | 
						|
        }
 | 
						|
    } # end ugloop
 | 
						|
 | 
						|
    if ( scalar(@calldirectly) > 0 ) {
 | 
						|
        my @children;
 | 
						|
        foreach my $ugname (@calldirectly) {
 | 
						|
            my $nodelist = join( ',', @{ $updategroup->{$ugname} } );
 | 
						|
            my $ugdf = $lljobs_dir . "/rollupdate_" . $ugname . ".data";
 | 
						|
            my $rsp;
 | 
						|
            push @{ $rsp->{data} },
 | 
						|
              "No active LL nodes in update group $ugname";
 | 
						|
            push @{ $rsp->{data} },
 | 
						|
"These nodes will be updated now.  This will take a few minutes...";
 | 
						|
            xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
            if ($::VERBOSE) {
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." No active LL nodes in update group $ugname.  These nodes will be updated now.\n";
 | 
						|
                close (RULOG);
 | 
						|
            }
 | 
						|
            my $nltab = xCAT::Table->new('nodelist');
 | 
						|
            my @nodes = split( /\,/, $nodelist );
 | 
						|
            xCAT::TableUtils->setAppStatus(\@nodes,"RollingUpdate","update_job_submitted");
 | 
						|
            my $childpid = runrollupdate( { command => ['runrollupdate'],
 | 
						|
                                                arg => [ 'internal','loadleveler', $ugdf ]
 | 
						|
                                           });
 | 
						|
            if (defined($childpid) && ($childpid != 0)) {
 | 
						|
                push (@children, $childpid);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        # wait until all the children are finished before returning
 | 
						|
        foreach my $child (@children) {
 | 
						|
            if ($::VERBOSE) {
 | 
						|
                my $rsp;
 | 
						|
                push @{ $rsp->{data} }, "Waiting for child PID $child to complete ";
 | 
						|
                xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." Waiting for child PID $child to complete \n";
 | 
						|
                close (RULOG);
 | 
						|
            }
 | 
						|
            waitpid($child,0);  
 | 
						|
        }    
 | 
						|
    }
 | 
						|
 | 
						|
    return $rc;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   check_policy
 | 
						|
 | 
						|
        Check the policy table to see if userid is authorized to run command
 | 
						|
 | 
						|
        Arguments:  userid, command
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
        Error:
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub check_policy {
 | 
						|
  my $userid = shift;
 | 
						|
  my $xcatcmd = shift;
 | 
						|
 | 
						|
  my $policytable = xCAT::Table->new('policy');
 | 
						|
  unless ($policytable) {
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  my $policies = $policytable->getAllEntries;
 | 
						|
  $policytable->close;
 | 
						|
  foreach my $rule (@$policies) {
 | 
						|
    if ( $rule->{name} && 
 | 
						|
        (($rule->{name} eq "*")  || ($rule->{name} eq $userid)) ) {
 | 
						|
      if ( $rule->{commands} ) {
 | 
						|
          if (($rule->{commands} eq "") || ($rule->{commands} eq "*") || ($rule->{commands} =~ /$xcatcmd/) ){
 | 
						|
            return 0;  # match found
 | 
						|
          }
 | 
						|
      } else {
 | 
						|
          return 0;  # default match if commands is unset
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return 1;  # no match found
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=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
 | 
						|
 | 
						|
        Sets the specified feature for the list of LL machines
 | 
						|
 | 
						|
        Arguments:  $machinelist - blank delimited list of LL machines
 | 
						|
                    $feature - feature value to set
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
        Error:
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub set_LL_feature {
 | 
						|
    my $machinelist = shift;
 | 
						|
    my $feature = shift;
 | 
						|
 | 
						|
    # Query current feature
 | 
						|
    my $cmd = "llconfig -h $machinelist -d FEATURE";
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." Running command \'$cmd\'\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
    my @llcfgoutput = xCAT::Utils->runcmd( $cmd, 0 );
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." Return code:  $::RUNCMD_RC\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
 | 
						|
    my %set_features;
 | 
						|
    my %had_cfg;
 | 
						|
    foreach my $llcfgout (@llcfgoutput) {
 | 
						|
        my @llfeatures;
 | 
						|
        my $haveit = 0;
 | 
						|
        if ( $llcfgout =~ /:FEATURE =/ ) {
 | 
						|
            my ($stuff,$curfeature_string) = split(/=/,$llcfgout);
 | 
						|
            my $newfeature_string = "";
 | 
						|
            my ($machine,$morestuff) = split(/:/,$stuff);
 | 
						|
            @llfeatures = split(/\s+/,$curfeature_string);
 | 
						|
            foreach my $f (@llfeatures) {
 | 
						|
                if ($f =~ /XCAT_UPDATEALL\d*/) {
 | 
						|
                    $f = "";
 | 
						|
                } else {
 | 
						|
                    $newfeature_string .= " $f";
 | 
						|
                }
 | 
						|
                if ($f eq $feature){
 | 
						|
                   $haveit = 1;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if ( !$haveit) {
 | 
						|
                $newfeature_string .= " $feature";
 | 
						|
            }
 | 
						|
            $set_features{$newfeature_string} .= " $machine";
 | 
						|
            $had_cfg{$machine} = 1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    foreach my $m (split(/\s+/,$machinelist)){
 | 
						|
        if (! $had_cfg{$m} ){
 | 
						|
            $set_features{$feature} .= " $m";
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    # Change in LL database
 | 
						|
    foreach my $sf (keys %set_features) {
 | 
						|
        if ($set_features{$sf} !~ /^\s*$/){
 | 
						|
            $cmd = "llconfig -N -h $set_features{$sf}  -c FEATURE=\"$sf\"";
 | 
						|
            if ($::VERBOSE) {
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." Running command \'$cmd\'\n";
 | 
						|
                close (RULOG);
 | 
						|
            }
 | 
						|
            if ($::TEST) {
 | 
						|
                my $rsp;
 | 
						|
                push @{ $rsp->{data} }, "In TEST mode.  Will NOT run command: $cmd ";
 | 
						|
                xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                $::RUNCMD_RC = 0;
 | 
						|
            } else {
 | 
						|
                xCAT::Utils->runcmd( $cmd, 0 );
 | 
						|
                if ($::VERBOSE) {
 | 
						|
                    open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                    print RULOG localtime()." Return code:  $::RUNCMD_RC\n";
 | 
						|
                    close (RULOG);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    # Send LL reconfig to all central mgrs and resource mgrs
 | 
						|
    llreconfig();
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   get_prescripts
 | 
						|
 | 
						|
        Generate the prescripts for this nodelist
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
        Error:
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub get_prescripts {
 | 
						|
    my $nodelist = shift;
 | 
						|
    my @nl = split(/,/, $nodelist);
 | 
						|
 | 
						|
    my @datalines;
 | 
						|
    my $psindex=0;
 | 
						|
    foreach my $psl ( @{ $::FILEATTRS{'prescript'} } ) {
 | 
						|
        my $psline = $psl;   
 | 
						|
        if ($::updateall) {
 | 
						|
            push (@datalines, "prescript=".$psline."\n");
 | 
						|
            $psindex++;
 | 
						|
            next;
 | 
						|
        }
 | 
						|
        my $psnoderange = $::FILEATTRS{'prescriptnodes'}[$psindex];
 | 
						|
        my $executenodelist = "";
 | 
						|
        if ($psnoderange eq "ALL_NODES_IN_UPDATEGROUP") {
 | 
						|
           $executenodelist = $nodelist;
 | 
						|
        } else {
 | 
						|
            my @psns = xCAT::NodeRange::noderange($psnoderange);
 | 
						|
            unless (@psns) { $psindex++; next; }
 | 
						|
            my @executenodes;
 | 
						|
            foreach my $node (@nl) {
 | 
						|
                push (@executenodes, grep (/^$node$/,@psns));                 
 | 
						|
            }
 | 
						|
            $executenodelist = join(',',@executenodes);
 | 
						|
        }
 | 
						|
        if ($executenodelist ne "") {
 | 
						|
            $psline =~ s/\$NODELIST/$executenodelist/g;
 | 
						|
            push (@datalines, "prescript=".$psline."\n");
 | 
						|
        } 
 | 
						|
        $psindex++;
 | 
						|
    }
 | 
						|
    return @datalines;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   get_outofband
 | 
						|
 | 
						|
        Generate the out of band scripts for this nodelist
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
        Error:
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub get_outofband {
 | 
						|
    my $nodelist = shift;
 | 
						|
    my @nl = split(/,/, $nodelist);
 | 
						|
 | 
						|
    my @datalines;
 | 
						|
    my $obindex=0;
 | 
						|
    foreach my $obl ( @{ $::FILEATTRS{'outofbandcmd'} } ) {
 | 
						|
        my $obline = $obl;
 | 
						|
        if ($::updateall) {
 | 
						|
            push (@datalines, "outofbandcmd=".$obline."\n");
 | 
						|
            $obindex++;
 | 
						|
            next;
 | 
						|
        }
 | 
						|
        my $obnoderange = $::FILEATTRS{'outofbandnodes'}[$obindex];
 | 
						|
        my $executenodelist = "";
 | 
						|
        if ($obnoderange eq "ALL_NODES_IN_UPDATEGROUP") {
 | 
						|
           $executenodelist = $nodelist;
 | 
						|
        } else {
 | 
						|
            my @obns = xCAT::NodeRange::noderange($obnoderange);
 | 
						|
            unless (@obns) { $obindex++; next; }
 | 
						|
            my @executenodes;
 | 
						|
            foreach my $node (@nl) {
 | 
						|
                push (@executenodes, grep (/^$node$/,@obns));                 
 | 
						|
            }
 | 
						|
            $executenodelist = join(',',@executenodes);
 | 
						|
        }
 | 
						|
        if ($executenodelist ne "") {
 | 
						|
            $obline =~ s/\$NODELIST/$executenodelist/g;
 | 
						|
            push (@datalines, "outofbandcmd=".$obline."\n");
 | 
						|
        } 
 | 
						|
        $obindex++;
 | 
						|
    }
 | 
						|
    return @datalines;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   get_bringuporder
 | 
						|
 | 
						|
        Generate the bringup order for this nodelist
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
        Error:
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub get_bringuporder {
 | 
						|
    my $nodelist = shift;
 | 
						|
    my @nl = split(/,/, $nodelist);
 | 
						|
 | 
						|
    my @datalines;
 | 
						|
    if ($::updateall) {
 | 
						|
        $datalines[0]="\n";
 | 
						|
        return @datalines;
 | 
						|
    }
 | 
						|
    foreach my $buline ( @{ $::FILEATTRS{'bringuporder'} } ) {
 | 
						|
        my @buns = xCAT::NodeRange::noderange($buline);
 | 
						|
        unless (@buns) { next; }
 | 
						|
        my @bringupnodes;
 | 
						|
        foreach my $node (@nl) {
 | 
						|
            if (defined($node)) {
 | 
						|
                (my $found) =  grep (/^$node$/,@buns);
 | 
						|
                if ($found) {
 | 
						|
                    push (@bringupnodes, $found);
 | 
						|
                    undef $node;  # don't try to use this node again
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        my $bringupnodelist = join(',',@bringupnodes);
 | 
						|
        if ($bringupnodelist ne "") {
 | 
						|
            push (@datalines, "bringuporder=".$bringupnodelist."\n");
 | 
						|
        } 
 | 
						|
    }
 | 
						|
    return @datalines;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   get_mutex
 | 
						|
 | 
						|
        Generate the list of LL mutual exclusion resources for this 
 | 
						|
        update group
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
        Error:
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub get_mutex {
 | 
						|
    my $ugname = shift;
 | 
						|
 | 
						|
    my $mutex_string = "";
 | 
						|
 | 
						|
    my $max_updates = $::FILEATTRS{'maxupdates'}[0];
 | 
						|
    if ( defined($max_updates)  && ($max_updates ne 'all') ) {
 | 
						|
         $mutex_string .= "XCATROLLINGUPDATE_MAXUPDATES(1) ";
 | 
						|
    }
 | 
						|
 | 
						|
    if ($::updateall){
 | 
						|
        return $mutex_string;
 | 
						|
    }
 | 
						|
 | 
						|
    my $num_mutexes = scalar @::MUTEX;
 | 
						|
    if ( $num_mutexes > 0 ) {
 | 
						|
        foreach my $row (0..($num_mutexes-1)) {
 | 
						|
            foreach my $ugi (0..(@{$::MUTEX[$row]} - 1)) {
 | 
						|
                if ( defined($::MUTEX[$row][$ugi]) && ($ugname eq $::MUTEX[$row][$ugi]) ) {
 | 
						|
                    $mutex_string .= "XCATROLLINGUPDATE_MUTEX".$row."(1) ";
 | 
						|
                    last;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return $mutex_string;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   create_LL_mutex_resources
 | 
						|
 | 
						|
        Create all required LL mutex resources
 | 
						|
 | 
						|
        Arguments:
 | 
						|
                   updategroup
 | 
						|
                   maxupdates_only:
 | 
						|
                        1 - only create MAXUPDATES resources (for updateall)
 | 
						|
			0 - create MAXUPDATES and all MUTEX resources
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
        Error:
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments: Reads the mutex entries in $::FILEATTRS and
 | 
						|
                  sets the global array $::MUTEX
 | 
						|
                  which is a multi-dimensional array:
 | 
						|
                  Each row is an array of mutually exclusive update
 | 
						|
                   group names
 | 
						|
                 A LL Floating Resource will be created for each
 | 
						|
                 row of the array.
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub create_LL_mutex_resources {
 | 
						|
 | 
						|
    my $updategroup=shift;
 | 
						|
    my $maxupdates_only=shift;
 | 
						|
 | 
						|
    $::LL_MUTEX_RESOURCES_CREATED = 0;
 | 
						|
    my $mxindex=0;
 | 
						|
    my $fileattrs_index=0;
 | 
						|
    if (!$maxupdates_only) {
 | 
						|
        foreach my $mxline ( @{ $::FILEATTRS{'mutex'} } ) {
 | 
						|
            my $mx_count = $::FILEATTRS{'mutex_count'}[$fileattrs_index];
 | 
						|
            my @mxparts = split(/,/,$mxline);
 | 
						|
            if ( scalar @mxparts < 2 ) {
 | 
						|
                my $rsp;
 | 
						|
                push @{ $rsp->{data} }, "Error processing stanza line: ";
 | 
						|
                push @{ $rsp->{data} }, "mutex=" . $mxline;
 | 
						|
                push @{ $rsp->{data} }, "Value must contain at least 2 update groups";
 | 
						|
                xCAT::MsgUtils->message( "E", $rsp, $::CALLBACK );
 | 
						|
                return 1; 
 | 
						|
            }
 | 
						|
 | 
						|
            my $mxpi = 0;
 | 
						|
            my $mxindexmax = $mxindex;
 | 
						|
            my @ugnames;
 | 
						|
            foreach my $mxpart ( @mxparts ) {
 | 
						|
                my $mxindex2 = $mxindex;
 | 
						|
                #my @ugnamelist = xCAT::NameRange::namerange( $mxpart, 0 );
 | 
						|
                my @ugnamelist = xCAT::NodeRange::noderange( $mxpart, 0, 1, genericrange=>1 );
 | 
						|
                foreach my $ugname (@ugnamelist) {
 | 
						|
                    $::MUTEX[$mxindex2][$mxpi] = $ugname;
 | 
						|
                    $mxindex2++;
 | 
						|
                }
 | 
						|
                $mxindexmax = ($mxindex2 > $mxindexmax) ? $mxindex2 : $mxindexmax;
 | 
						|
                $mxpi++;
 | 
						|
            }
 | 
						|
            my $mxc;
 | 
						|
            for ($mxc=$mxindex; $mxc < $mxindexmax; $mxc++) {
 | 
						|
                $::MUTEX_COUNT[$mxc] = $mx_count;
 | 
						|
            }
 | 
						|
            $mxindex = $mxindexmax;
 | 
						|
            $fileattrs_index++;
 | 
						|
         }
 | 
						|
 | 
						|
    # If nodegroup_mutex entries are specified, we need to use the 
 | 
						|
    # list of all the nodes in each updategroup for this entire run.
 | 
						|
    # Then we need to get a list of all the nodes in the specified
 | 
						|
    # nodegroup and look for any intersections to create mutexes.
 | 
						|
        $fileattrs_index=0;
 | 
						|
        foreach my $mxnodegrp_range ( @{ $::FILEATTRS{'nodegroup_mutex'} } ) {
 | 
						|
            my $mx_count = $::FILEATTRS{'nodegroup_mutex_count'}[$fileattrs_index];
 | 
						|
 | 
						|
            #foreach my $mxnodegroup ( xCAT::NameRange::namerange( $mxnodegrp_range, 0 ) ) {
 | 
						|
            foreach my $mxnodegroup ( xCAT::NodeRange::noderange( $mxnodegrp_range, 0, 1, genericrange=>1 ) ) {
 | 
						|
              my $mxpi = 0;
 | 
						|
mxnode_loop:  foreach my $mxnode ( xCAT::NodeRange::noderange($mxnodegroup) ) {
 | 
						|
                foreach my $ugname ( keys %{$updategroup} ) {
 | 
						|
                  foreach my $node ( @{ $updategroup->{$ugname} } ) {
 | 
						|
                     if ($mxnode eq $node) {
 | 
						|
                     # found a match, add updategroup to this mutex if we
 | 
						|
                     # don't already have it listed
 | 
						|
                        my $chk = 0;
 | 
						|
                        while ( $chk < $mxpi ){
 | 
						|
                            if ($::MUTEX[$mxindex][$chk] eq $ugname) {
 | 
						|
                                # already have this one, skip to next
 | 
						|
                                next mxnode_loop;
 | 
						|
                            }
 | 
						|
                            $chk++;
 | 
						|
                        }
 | 
						|
                        $::MUTEX[$mxindex][$mxpi] = $ugname;
 | 
						|
                        $mxpi++;
 | 
						|
                        next mxnode_loop;
 | 
						|
                     } # end if found match 
 | 
						|
                  }  
 | 
						|
                }
 | 
						|
              } # end mxnode_loop
 | 
						|
              if ($mxpi == 1) {
 | 
						|
                 # only one updategroup in this mutex, not valid -- ignore it
 | 
						|
                 delete $::MUTEX[$mxindex];
 | 
						|
              } elsif ( $mxpi > 1 ) {
 | 
						|
                 $::MUTEX_COUNT[$mxindex] = $mx_count;
 | 
						|
                 $mxindex++;
 | 
						|
              }
 | 
						|
            }
 | 
						|
            $fileattrs_index++;
 | 
						|
        }
 | 
						|
     }
 | 
						|
 | 
						|
 | 
						|
     # Build the actual FLOATING_RESOURCES and SCHEDULE_BY_RESOURCES
 | 
						|
     # strings to write into the LL database 
 | 
						|
     my $resource_string = "";
 | 
						|
     my $max_updates = $::FILEATTRS{'maxupdates'}[0];
 | 
						|
     if ( ! defined($max_updates)  || ($max_updates eq 'all') ) {
 | 
						|
         $max_updates = 0;
 | 
						|
     } else {
 | 
						|
         $resource_string .= "XCATROLLINGUPDATE_MAXUPDATES($max_updates) ";
 | 
						|
     }
 | 
						|
 | 
						|
     if (!$maxupdates_only) {
 | 
						|
         my $num_mutexes = scalar @::MUTEX;
 | 
						|
         if ( $num_mutexes > 0 ) {
 | 
						|
            foreach my $row (0..($num_mutexes-1)) {
 | 
						|
                $resource_string .= "XCATROLLINGUPDATE_MUTEX".$row."($::MUTEX_COUNT[$row]) ";
 | 
						|
            }
 | 
						|
         }
 | 
						|
     }
 | 
						|
 | 
						|
     if ( $resource_string ) {
 | 
						|
        my $cfg_change = 0;
 | 
						|
        my $cmd = "llconfig -d FLOATING_RESOURCES SCHEDULE_BY_RESOURCES CENTRAL_MANAGER_LIST RESOURCE_MGR_LIST";
 | 
						|
        my @llcfg_d = xCAT::Utils->runcmd( $cmd, 0 );
 | 
						|
        my $curSCHED = "";
 | 
						|
        my $curFLOAT = "";
 | 
						|
        foreach my $cfgo (@llcfg_d) {
 | 
						|
            chomp $cfgo;
 | 
						|
            my($llattr,$llval) = split (/ = /,$cfgo);
 | 
						|
            if ( $llattr =~ /SCHEDULE_BY_RESOURCES/ ) {
 | 
						|
                $curSCHED = $llval; }
 | 
						|
            if ( $llattr =~ /FLOATING_RESOURCES/ ) {
 | 
						|
                $curFLOAT = $llval; }
 | 
						|
        }
 | 
						|
        my $origSCHED = $curSCHED;
 | 
						|
        my $origFLOAT = $curFLOAT;
 | 
						|
        $cmd = "llconfig -N -c ";
 | 
						|
        $curFLOAT =~ s/XCATROLLINGUPDATE_MUTEX(\d)*\((\d)*\)//g;
 | 
						|
        $curFLOAT =~ s/XCATROLLINGUPDATE_MAXUPDATES(\d)*\((\d)*\)//g;
 | 
						|
        $curFLOAT .= " $resource_string";
 | 
						|
        $curFLOAT =~ s/\s+/ /g; $curFLOAT =~ s/^\s//g; $curFLOAT =~ s/\s$//g;
 | 
						|
        $origFLOAT =~ s/\s+/ /g; $origFLOAT =~ s/^\s//g; $origFLOAT =~ s/\s$//g;
 | 
						|
        if ( $curFLOAT ne $origFLOAT ) {
 | 
						|
            $cmd .= "FLOATING_RESOURCES=\"$curFLOAT\" ";
 | 
						|
            $cfg_change = 1;
 | 
						|
        }
 | 
						|
 | 
						|
        $resource_string =~ s/\((\d)*\)//g;
 | 
						|
        $curSCHED =~ s/XCATROLLINGUPDATE_MUTEX(\d)*//g;
 | 
						|
        $curSCHED =~ s/XCATROLLINGUPDATE_MAXUPDATES(\d)*//g;
 | 
						|
        $curSCHED .= " $resource_string";
 | 
						|
        $curSCHED =~ s/\s+/ /g; $curSCHED =~ s/^\s//g; $curSCHED =~ s/\s$//g;
 | 
						|
        $origSCHED =~ s/\s+/ /g; $origSCHED =~ s/^\s//g; $origSCHED =~ s/\s$//g;
 | 
						|
        if ( $curSCHED ne $origSCHED ) {
 | 
						|
            $cmd .= "SCHEDULE_BY_RESOURCES=\"$curSCHED\" ";
 | 
						|
            $cfg_change = 1;
 | 
						|
        }
 | 
						|
        if ($cfg_change) {
 | 
						|
            my @llcfg_c;
 | 
						|
            if ($::TEST) {
 | 
						|
                my $rsp;
 | 
						|
                push @{ $rsp->{data} }, "In TEST mode.  Will NOT run command: $cmd ";
 | 
						|
                   xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
                $::RUNCMD_RC = 0;
 | 
						|
            } else {
 | 
						|
                @llcfg_c = xCAT::Utils->runcmd( $cmd, 0 );
 | 
						|
            }
 | 
						|
 | 
						|
            # Send LL reconfig to all central mgrs and resource mgrs
 | 
						|
            llreconfig();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 
 | 
						|
    $::LL_MUTEX_RESOURCES_CREATED = 1;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   runrollupdate
 | 
						|
 | 
						|
        Reboot updategroup in response to request from scheduler job
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
 | 
						|
        Error:
 | 
						|
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
            Note that since this command only gets called from the daemon
 | 
						|
            through a port request from a node, there is no active callback
 | 
						|
            to return messages to.  Log only critical errors to the system log.
 | 
						|
 | 
						|
            This subroutine will fork a child process to do the actual
 | 
						|
            work of shutting down and rebooting nodes.  The parent will return
 | 
						|
            immediately to the caller so that other reboot requests can be
 | 
						|
            handled in parallel.  It is the caller's responsibility to ensure
 | 
						|
            the parent process does not go away and take these children down
 | 
						|
            with it.  (Not a problem when called from xcatd, since that is
 | 
						|
            "long-running".)
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
sub runrollupdate {
 | 
						|
 | 
						|
    my $reboot_request = shift;
 | 
						|
    if ( ! $reboot_request->{arg} ) {
 | 
						|
       &runrollupdate_usage;
 | 
						|
       return;
 | 
						|
    }
 | 
						|
 | 
						|
    my @reboot_args = @{$reboot_request->{arg}};
 | 
						|
    my $internal = 0;
 | 
						|
    if ( $reboot_args[0] eq "internal" ) { $internal = 1; }
 | 
						|
 | 
						|
    my $rc    = 0;
 | 
						|
    my $error = 0;
 | 
						|
 | 
						|
    # process the command line
 | 
						|
    if ( $internal ) {
 | 
						|
        $::scheduler = $reboot_args[1];
 | 
						|
        $::datafile = $reboot_args[2];
 | 
						|
        $::ll_reservation_id = "";
 | 
						|
    } else {
 | 
						|
        $rc = &processArgs;
 | 
						|
        if ( $rc != 0 ) {
 | 
						|
 | 
						|
            # rc: 0 - ok, 1 - return, 2 - help, 3 - error
 | 
						|
            if ( $rc != 1 ) {
 | 
						|
                &runrollupdate_usage;
 | 
						|
            }
 | 
						|
            return ( $rc - 1 );
 | 
						|
        }
 | 
						|
    }
 | 
						|
    $::scheduler =~ tr/[A-Z]/[a-z]/;
 | 
						|
 | 
						|
 | 
						|
    if ($::VERBOSE) { 
 | 
						|
        unless (-d $::LOGDIR) { File::Path::mkpath($::LOGDIR); } 
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()."runrollupdate request for $::scheduler $::datafile $::ll_reservation_id \n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
 | 
						|
    my $childpid = xCAT::Utils->xfork();
 | 
						|
    unless (defined $childpid)  { die "Fork failed" };
 | 
						|
    if ($childpid != 0) {
 | 
						|
        # This is the parent process, just return and let the child do all
 | 
						|
        # the work.
 | 
						|
        return $childpid;
 | 
						|
    }
 | 
						|
 | 
						|
    # This is now the child process
 | 
						|
    
 | 
						|
    # Load the datafile 
 | 
						|
    &readDataFile($::datafile);
 | 
						|
    # set some defaults
 | 
						|
    $::ug_name =  $::DATAATTRS{updategroup}[0];
 | 
						|
    my ($statusattr,$statusval,$statustimeout);
 | 
						|
    if (defined($::DATAATTRS{bringupappstatus}[0])) {
 | 
						|
        $statusattr = "appstatus";
 | 
						|
        $statusval = $::DATAATTRS{bringupappstatus}[0];
 | 
						|
    } elsif (defined($::DATAATTRS{bringupstatus}[0])) {
 | 
						|
        $statusattr="status";
 | 
						|
        $statusval = $::DATAATTRS{bringupstatus}[0];
 | 
						|
    } else {
 | 
						|
        $statusattr="status";
 | 
						|
        $statusval = "booted";
 | 
						|
    }
 | 
						|
    if (defined($::DATAATTRS{bringuptimeout}[0])) {
 | 
						|
        $statustimeout = $::DATAATTRS{bringuptimeout}[0];
 | 
						|
    } else {
 | 
						|
        $statustimeout = 10;
 | 
						|
    }
 | 
						|
    my $skipshutdown = 0;
 | 
						|
    if ((defined($::DATAATTRS{skipshutdown}[0])) && 
 | 
						|
               ( ($::DATAATTRS{skipshutdown}[0] eq "yes") ||
 | 
						|
                 ($::DATAATTRS{skipshutdown}[0] eq "y")   ||
 | 
						|
                 ($::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();
 | 
						|
    if (! $hostlist ) {
 | 
						|
        if ($::VERBOSE) { 
 | 
						|
            open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
            print RULOG localtime()."$::ug_name:  Cannot determine nodelist for this request.  \n"; 
 | 
						|
            close (RULOG);
 | 
						|
        }
 | 
						|
        my $rsp;
 | 
						|
        xCAT::MsgUtils->message( "S",
 | 
						|
"rollupdate failure:  $::ug_name: Cannot determine nodelist for this request.  ");
 | 
						|
        exit(1);
 | 
						|
    }
 | 
						|
 | 
						|
    my $nltab = xCAT::Table->new('nodelist');
 | 
						|
    my @nodes = split( /\,/, $hostlist );    
 | 
						|
    my $appstatus=xCAT::TableUtils->getAppStatus(\@nodes,"RollingUpdate");
 | 
						|
    foreach my $node (@nodes) {
 | 
						|
        unless ( defined($appstatus->{$node})
 | 
						|
                 && ( $appstatus->{$node} eq "update_job_submitted" ) )
 | 
						|
        {
 | 
						|
            if ($::VERBOSE) { 
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." $::ug_name:  Node $node appstatus not in valid state for rolling update\n";
 | 
						|
                print RULOG "The following nodelist will not be processed:\n $hostlist \n";
 | 
						|
                close (RULOG);
 | 
						|
            }
 | 
						|
            my $rsp;
 | 
						|
            xCAT::MsgUtils->message(
 | 
						|
                "S",
 | 
						|
"ROLLUPDATE failure: $::ug_name:  Node $node appstatus not in valid state for rolling update "
 | 
						|
            );
 | 
						|
            if ($::ll_reservation_id){
 | 
						|
               my @remove_res;
 | 
						|
               $remove_res[0]='CANCEL_DUE_TO_ERROR';
 | 
						|
               &remove_LL_reservations(\@remove_res);
 | 
						|
            }
 | 
						|
            exit(1);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    # Run prescripts for this update group
 | 
						|
    xCAT::TableUtils->setAppStatus(\@nodes,"RollingUpdate","running_prescripts");
 | 
						|
    foreach my $psline ( @{ $::DATAATTRS{'prescript'} } ) {
 | 
						|
        $psline =~ s/\$NODELIST/$hostlist/g;
 | 
						|
        # Run the command
 | 
						|
        if ($::VERBOSE) {
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." $::ug_name:  Running prescript \'$psline\'\n";
 | 
						|
                close (RULOG);
 | 
						|
        }
 | 
						|
        my @psoutput;
 | 
						|
        if ($::TEST) {
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." $::ug_name:  In TEST mode.  Will NOT run prescript \'$psline\'\n";
 | 
						|
                close (RULOG);
 | 
						|
        } else {
 | 
						|
           @psoutput = xCAT::Utils->runcmd( $psline, 0 );
 | 
						|
        }
 | 
						|
        if ($::VERBOSE) {
 | 
						|
            open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
            print RULOG localtime()." Prescript output:\n";
 | 
						|
            foreach my $psoline (@psoutput) {
 | 
						|
                print RULOG $psoline."\n";
 | 
						|
            }
 | 
						|
            close (RULOG);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
 | 
						|
    # Shutdown the nodes
 | 
						|
    if ( ! $skipshutdown ) {
 | 
						|
        xCAT::TableUtils->setAppStatus(\@nodes,"RollingUpdate","shutting_down");
 | 
						|
        my $shutdown_cmd;
 | 
						|
        if (xCAT::Utils->isAIX()) { $shutdown_cmd = "shutdown -F &"; }
 | 
						|
                             else { $shutdown_cmd = "shutdown -h now &"; }
 | 
						|
 
 | 
						|
 
 | 
						|
        if ($::VERBOSE) { 
 | 
						|
            open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
            print RULOG localtime()." $::ug_name:   Running command \'xdsh $hostlist -v $shutdown_cmd\' \n";
 | 
						|
            close (RULOG);
 | 
						|
        }
 | 
						|
        if ($::TEST) { 
 | 
						|
            open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
            print RULOG localtime()." $::ug_name:  In TEST mode.  Will NOT run command \'xdsh $hostlist -v $shutdown_cmd\' \n";
 | 
						|
            close (RULOG);
 | 
						|
        } else {
 | 
						|
            xCAT::Utils->runxcmd( { command => ['xdsh'],
 | 
						|
                                    node    => \@nodes,
 | 
						|
                                    arg     => [ "-v", $shutdown_cmd ]
 | 
						|
                              }, $::SUBREQ, -1);
 | 
						|
        }
 | 
						|
        my $slept    = 0;
 | 
						|
        my $alldown  = 1;
 | 
						|
        my $nodelist = join( ',', @nodes );
 | 
						|
        if (! $::TEST) { 
 | 
						|
        do {
 | 
						|
            $alldown = 1;
 | 
						|
            my $shutdownmax = 5;
 | 
						|
            if ( defined($::DATAATTRS{shutdowntimeout}[0] ) ) {
 | 
						|
                $shutdownmax = $::DATAATTRS{shutdowntimeout}[0];
 | 
						|
            }
 | 
						|
            my $pwrstat_cmd = "rpower $nodelist stat";
 | 
						|
            if ($::VERBOSE) { 
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." $::ug_name:  Running command \'$pwrstat_cmd\' \n";
 | 
						|
                close (RULOG);
 | 
						|
            }
 | 
						|
            my $pwrstat = xCAT::Utils->runxcmd( $pwrstat_cmd, $::SUBREQ, -1, 1 );
 | 
						|
            foreach my $pline (@{$pwrstat}) {
 | 
						|
                my ( $pnode, $pstat, $rest ) = split( /\s+/, $pline );
 | 
						|
                if (    ( ! $pstat )
 | 
						|
                     || ( $pstat eq "Running" )
 | 
						|
                     || ( $pstat eq "Shutting" )
 | 
						|
                     || ( $pstat eq "on" )
 | 
						|
                     || ( $pstat eq "Error:" ) )
 | 
						|
                {
 | 
						|
 | 
						|
                    $alldown = 0;
 | 
						|
                    # give up on shutdown after requested wait time and force the
 | 
						|
                    # node off
 | 
						|
                    if ( $slept >= ($shutdownmax * 60) ) {
 | 
						|
                        $pnode =~ s/://g;
 | 
						|
                        my $pwroff_cmd = "rpower $pnode off";
 | 
						|
                        if ($::VERBOSE) { 
 | 
						|
                            open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                            print RULOG localtime()." $::ug_name:  shutdowntimeout exceeded for $pnode \n";
 | 
						|
                            print RULOG localtime()." $::ug_name:  Running command \'$pwroff_cmd\' \n";
 | 
						|
                            close (RULOG);
 | 
						|
                        }
 | 
						|
                        xCAT::TableUtils->setAppStatus([$pnode],"RollingUpdate","shutdowntimeout_exceeded__forcing_rpower_off");
 | 
						|
                        xCAT::Utils->runxcmd( $pwroff_cmd, $::SUBREQ, -1 );
 | 
						|
                    } else {
 | 
						|
                        last;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
    
 | 
						|
            # If all nodes are not down yet, wait some more
 | 
						|
            unless ($alldown) {
 | 
						|
                sleep(20);
 | 
						|
                $slept += 20;
 | 
						|
            }
 | 
						|
        } until ($alldown);
 | 
						|
        } # end not TEST
 | 
						|
 | 
						|
        # Run out-of-band commands for this update group
 | 
						|
        xCAT::TableUtils->setAppStatus(\@nodes,"RollingUpdate","running_outofbandcmds");
 | 
						|
        foreach my $obline ( @{ $::DATAATTRS{'outofbandcmd'} } ) {
 | 
						|
            $obline =~ s/\$NODELIST/$hostlist/g;
 | 
						|
            # Run the command
 | 
						|
            if ($::VERBOSE) {
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." $::ug_name:  Running out-of-band command  \'$obline\'\n";
 | 
						|
                close (RULOG);
 | 
						|
            }
 | 
						|
            my @oboutput;
 | 
						|
            if ($::TEST) {
 | 
						|
                    open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                    print RULOG localtime()." $::ug_name:  In TEST mode.  Will NOT run out-of-band command  \'$obline\'\n";
 | 
						|
                    close (RULOG);
 | 
						|
            } else {
 | 
						|
                @oboutput = xCAT::Utils->runcmd( $obline, 0 );
 | 
						|
            }
 | 
						|
            if ($::VERBOSE) {
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." $::ug_name:  Out-of-band command output:\n";
 | 
						|
                foreach my $oboline (@oboutput) {
 | 
						|
                    print RULOG $oboline."\n";
 | 
						|
                }
 | 
						|
                close (RULOG);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } # end !$skipshutdown
 | 
						|
    
 | 
						|
 | 
						|
 | 
						|
    # reboot the nodes
 | 
						|
    #   Use bringuporder defined in datafile
 | 
						|
    
 | 
						|
    my $numboots = 0;
 | 
						|
    if ( defined($::DATAATTRS{bringuporder}[0]) ) {
 | 
						|
        $numboots = scalar( @{$::DATAATTRS{bringuporder}} );
 | 
						|
    }
 | 
						|
    if ( $skipshutdown ) {
 | 
						|
        $numboots = 0;
 | 
						|
    }
 | 
						|
    my @remaining_nodes = @nodes;
 | 
						|
    foreach my $bootindex (0..$numboots){
 | 
						|
        my @bootnodes;
 | 
						|
        if ((!$skipshutdown) &&
 | 
						|
            (defined($::DATAATTRS{bringuporder}[$bootindex]))) {
 | 
						|
            @bootnodes = split(/,/,$::DATAATTRS{bringuporder}[$bootindex]);
 | 
						|
            foreach my $rn (@remaining_nodes) {
 | 
						|
                if ((defined($rn)) && (grep(/^$rn$/,@bootnodes))) {
 | 
						|
                    undef($rn);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            foreach my $rn (@remaining_nodes) {
 | 
						|
                if (defined($rn)) {
 | 
						|
                    push (@bootnodes,$rn);
 | 
						|
                    undef($rn);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if (!scalar (@bootnodes)) { next; } 
 | 
						|
 | 
						|
        # reboot command determined by nodehm power/mgt attributes
 | 
						|
        if (!$skipshutdown) {
 | 
						|
            my $hmtab = xCAT::Table->new('nodehm');
 | 
						|
            my @rpower_nodes;
 | 
						|
            my @rnetboot_nodes;
 | 
						|
            my $hmtab_entries =
 | 
						|
              $hmtab->getNodesAttribs( \@bootnodes, [ 'node', 'mgt', 'power' ] );
 | 
						|
            foreach my $node (@bootnodes) {
 | 
						|
                my $pwr = $hmtab_entries->{$node}->[0]->{power};
 | 
						|
                unless ( defined($pwr) ) { $pwr = $hmtab_entries->{$node}->[0]->{mgt}; }
 | 
						|
                if ( $pwr eq 'hmc' ) {
 | 
						|
                    push( @rnetboot_nodes, $node );
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    push( @rpower_nodes, $node );
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            xCAT::TableUtils->setAppStatus(\@bootnodes,"RollingUpdate","rebooting");
 | 
						|
            if ($bootindex < $numboots) {
 | 
						|
                xCAT::TableUtils->setAppStatus(\@remaining_nodes,"RollingUpdate","waiting_on_bringuporder");
 | 
						|
            }
 | 
						|
            if ( scalar(@rnetboot_nodes) > 0 ) {
 | 
						|
                my $rnb_nodelist = join( ',', @rnetboot_nodes );
 | 
						|
                # my $cmd = "rnetboot $rnb_nodelist -f";
 | 
						|
####  TODO:  DO WE STILL NEED 2 LISTS?
 | 
						|
####         RUNNING rpower FOR ALL BOOTS NOW!  MUCH FASTER!!!
 | 
						|
                my $cmd = "rpower $rnb_nodelist on";
 | 
						|
                if ($::VERBOSE) { 
 | 
						|
                    open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                    print RULOG localtime()." $::ug_name:  Running command \'$cmd\' \n";
 | 
						|
                    close (RULOG);
 | 
						|
                }
 | 
						|
                if ($::TEST) { 
 | 
						|
                    open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                    print RULOG localtime()." $::ug_name:  In TEST mode.  Will NOT run command \'$cmd\' \n";
 | 
						|
                    close (RULOG);
 | 
						|
                } else {
 | 
						|
                    xCAT::Utils->runxcmd( $cmd, $::SUBREQ, 0 );
 | 
						|
                }
 | 
						|
            } elsif ( scalar(@rpower_nodes) > 0 ) {
 | 
						|
                my $rp_nodelist = join( ',', @rpower_nodes );
 | 
						|
                my $cmd = "rpower $rp_nodelist boot";
 | 
						|
                if ($::VERBOSE) { 
 | 
						|
                    open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                    print RULOG localtime()." $::ug_name:  Running command \'$cmd\' \n";
 | 
						|
                    close (RULOG);
 | 
						|
                }
 | 
						|
                if ($::TEST) { 
 | 
						|
                    open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                    print RULOG localtime()." $::ug_name:  In TEST mode.  Will NOT run command \'$cmd\' \n";
 | 
						|
                    close (RULOG);
 | 
						|
                } else {
 | 
						|
                    xCAT::Utils->runxcmd( $cmd, $::SUBREQ, 0 );
 | 
						|
                }
 | 
						|
            }
 | 
						|
        } # end !$skipshutdown
 | 
						|
 | 
						|
         # wait for bringupstatus to be set
 | 
						|
        my $not_done = 1;
 | 
						|
        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(",",@query_bootnodes)." xCAT database $statusattr for value $statusval \n";
 | 
						|
                 close (RULOG);
 | 
						|
            }
 | 
						|
            my $nltab_stats =
 | 
						|
                $nltab->getNodesAttribs( \@query_bootnodes, [ 'node', $statusattr ] );
 | 
						|
#           %ll_res = ();
 | 
						|
            $not_done = 0;
 | 
						|
            foreach my $bn (@query_bootnodes) {
 | 
						|
                if ( $nltab_stats->{$bn}->[0]->{$statusattr} !~ /$statusval/ ) {
 | 
						|
                  $ll_res{$bn}{not_done}=1;
 | 
						|
                  $not_done = 1;
 | 
						|
                } else {
 | 
						|
                  $ll_res{$bn}{remove}=1;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if (($::scheduler eq "loadleveler") &&
 | 
						|
                ($::ll_reservation_id)){ 
 | 
						|
                my @remove_res_LL;
 | 
						|
                my @remove_res_xCAT;
 | 
						|
                foreach my $bn (keys %ll_res) {
 | 
						|
                    if (($ll_res{$bn}{remove}) && (! $ll_res{$bn}{removed}) ){
 | 
						|
                        if ( defined($::XLATED{$bn}) ) {
 | 
						|
                            push (@remove_res_LL,$::XLATED{$bn});
 | 
						|
                            push (@remove_res_xCAT,$bn);
 | 
						|
                        } else {
 | 
						|
                            push (@remove_res_LL,$bn);
 | 
						|
                            push (@remove_res_xCAT,$bn);
 | 
						|
                        }
 | 
						|
                        $ll_res{$bn}{removed} = 1;
 | 
						|
                        $ll_res{$bn}{not_done} = 0;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                if (@remove_res_LL) {
 | 
						|
                    &remove_LL_reservations(\@remove_res_LL);
 | 
						|
                    xCAT::TableUtils->setAppStatus(\@remove_res_xCAT,"RollingUpdate","update_complete");
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if ($not_done) {
 | 
						|
                if ($::TEST) { 
 | 
						|
                    $not_done = 0; 
 | 
						|
                } else {
 | 
						|
                    sleep(20);
 | 
						|
                    $totalwait += 20;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if ($not_done) { 
 | 
						|
            if (($::scheduler eq "loadleveler") &&
 | 
						|
                ($::ll_reservation_id)){ 
 | 
						|
                 open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                 print RULOG "\n";
 | 
						|
                 print RULOG localtime()." ERROR:  Update group $::ug_name:  Reached bringuptimeout before all nodes completed bringup.  Some nodes may not have been updated. \n";
 | 
						|
                print RULOG "Cancelling LL reservation $::ll_reservation_id \n";
 | 
						|
                print RULOG "\n";
 | 
						|
                close (RULOG);
 | 
						|
 | 
						|
                my @remove_res;
 | 
						|
                $remove_res[0]='CANCEL_DUE_TO_ERROR';
 | 
						|
                &remove_LL_reservations(\@remove_res);
 | 
						|
                my @error_nodes;
 | 
						|
                foreach my $bn (keys %ll_res) {
 | 
						|
                    if ($ll_res{$bn}{not_done}) {
 | 
						|
                        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::TableUtils->setAppStatus(\@error_nodes,"RollingUpdate","ERROR_bringuptimeout_exceeded");
 | 
						|
                if ( @remaining_nodes ) {
 | 
						|
                  my @leftover_nodes;
 | 
						|
                  foreach my $rn (@remaining_nodes) {
 | 
						|
                    if (defined($rn)) {
 | 
						|
                        push (@leftover_nodes,$rn);
 | 
						|
                    }
 | 
						|
                  }
 | 
						|
                  if ( @leftover_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(",",@leftover_nodes);
 | 
						|
                    print RULOG "\n";
 | 
						|
                    xCAT::TableUtils->setAppStatus(\@leftover_nodes,"RollingUpdate","ERROR_bringuptimeout_exceeded_for_previous_node");
 | 
						|
                  }
 | 
						|
                }
 | 
						|
                close (RULOG);
 | 
						|
            }
 | 
						|
            last;
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
    if ($::VERBOSE) { 
 | 
						|
          open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
          print RULOG localtime()." $::ug_name:  Rolling update complete.\n\n";
 | 
						|
          close (RULOG);
 | 
						|
    }
 | 
						|
 | 
						|
    exit(0);
 | 
						|
}
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   readDataFile
 | 
						|
 | 
						|
        Process the command line input piped in from a stanza file.
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
        Error:
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
                        Set  %::DATAATTRS
 | 
						|
                                (i.e.- $::DATAATTRS{attr}=[val])
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub readDataFile {
 | 
						|
    my $filedata = shift;
 | 
						|
 | 
						|
    my $DF;
 | 
						|
    unless ( open( $DF, "<", $filedata ) ) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." runrollupdate cannot open datafile $filedata\n";
 | 
						|
        close (RULOG);
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." runrollupdate reading datafile $filedata\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
    my @lines = <$DF>;
 | 
						|
    close $DF;
 | 
						|
 | 
						|
    foreach my $l (@lines) {
 | 
						|
 | 
						|
        # skip blank and comment lines
 | 
						|
        next if ( $l =~ /^\s*$/ || $l =~ /^\s*#/ );
 | 
						|
 | 
						|
        # process a real line
 | 
						|
        if ( $l =~ /^\s*(\w+)\s*=\s*(.*)\s*/ ) {
 | 
						|
            my $attr = $1;
 | 
						|
            my $val  = $2;
 | 
						|
            $attr =~ s/^\s*//;       # Remove any leading whitespace
 | 
						|
            $attr =~ s/\s*$//;       # Remove any trailing whitespace
 | 
						|
            $attr =~ tr/A-Z/a-z/;    # Convert to lowercase
 | 
						|
            $val  =~ s/^\s*//;
 | 
						|
            $val  =~ s/\s*$//;
 | 
						|
 | 
						|
            # set the value in the hash for this entry
 | 
						|
            push( @{ $::DATAATTRS{$attr} }, $val );
 | 
						|
        }
 | 
						|
    }    # end while - go to next line
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   get_hostlist
 | 
						|
 | 
						|
        Returns a comma-delimited hostlist string for this updategroup
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
        Error:
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub get_hostlist {
 | 
						|
    if ( defined($::DATAATTRS{nodelist}[0]) ){
 | 
						|
       return $::DATAATTRS{nodelist}[0];
 | 
						|
    }
 | 
						|
    my $cmd;
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        $cmd = "llqres -r -s -R $::ll_reservation_id 2>>$::LOGDIR/$::LOGFILE";
 | 
						|
    } else {
 | 
						|
        $cmd = "llqres -r -s -R $::ll_reservation_id";
 | 
						|
    }
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." $::ug_name:  Running command \'$cmd\'\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
    my ($llstatus) = xCAT::Utils->runcmd( $cmd, 0 );
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." Return code:  $::RUNCMD_RC\n";
 | 
						|
        print RULOG localtime()." $llstatus \n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
    if ($::RUNCMD_RC) {
 | 
						|
        if ($::VERBOSE) {
 | 
						|
            open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
            print RULOG localtime()." ERROR:  FAILED calling: \n     $cmd \n";
 | 
						|
            close (RULOG);
 | 
						|
        }
 | 
						|
      return 0;
 | 
						|
    } else {
 | 
						|
        my @status_fields = split(/!/,$llstatus);
 | 
						|
        if ($::VERBOSE) {
 | 
						|
            open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
            print RULOG localtime()." Hostlist:  $status_fields[22] \n";
 | 
						|
            close (RULOG);
 | 
						|
        }
 | 
						|
        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;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   remove_LL_reservations
 | 
						|
 | 
						|
        Changes the LL feature for nodes from oldfeature to newfeature
 | 
						|
        Remove nodes from LL reservation
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
        Error:
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub remove_LL_reservations {
 | 
						|
    my $input_nodes = shift;
 | 
						|
    my $nodes = $input_nodes;
 | 
						|
    my $CANCEL_DUE_TO_ERROR = 0;
 | 
						|
    if ( $input_nodes->[0] eq 'CANCEL_DUE_TO_ERROR') {
 | 
						|
       $CANCEL_DUE_TO_ERROR = 1;
 | 
						|
    }
 | 
						|
 | 
						|
    my $cmd;
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        $cmd = "llqres -r -R $::ll_reservation_id 2>>$::LOGDIR/$::LOGFILE";
 | 
						|
    } else {
 | 
						|
        $cmd = "llqres -r -R $::ll_reservation_id";
 | 
						|
    }
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        my $nl = join(",",@{$nodes});
 | 
						|
        print RULOG localtime()." $::ug_name:  remove_LL_reservations for $nl \n";
 | 
						|
        print RULOG localtime()." $::ug_name:  Running command \'$cmd\'\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
    my ($llstatus) = xCAT::Utils->runcmd( $cmd, 0 );
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." Return code:  $::RUNCMD_RC\n";
 | 
						|
        print RULOG localtime()." $llstatus \n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
    if ( $::RUNCMD_RC ) {
 | 
						|
        if ($::VERBOSE) {
 | 
						|
            open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
            print RULOG localtime()." ERROR:  FAILED calling:\n   $cmd  \n";
 | 
						|
            close (RULOG);
 | 
						|
        }
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    my @status_fields = split(/!/,$llstatus);
 | 
						|
    my $llnodelist = $status_fields[22];
 | 
						|
    my @llnodes = split(/,/,$llnodelist);
 | 
						|
    my $llnode_count = scalar (@llnodes);
 | 
						|
    my $remove_count = 0;
 | 
						|
    my $remove_reservation = 0;
 | 
						|
    my $remove_cmd = "llchres -R $::ll_reservation_id -h -";
 | 
						|
 | 
						|
    if ($CANCEL_DUE_TO_ERROR) {
 | 
						|
        $nodes = \@llnodes;
 | 
						|
    }
 | 
						|
    my @llnodes_removed;
 | 
						|
    foreach my $n (@{$nodes}) {
 | 
						|
        # change features for this node
 | 
						|
        if ($CANCEL_DUE_TO_ERROR) {
 | 
						|
            &remove_LL_updatefeature_only($n);
 | 
						|
        } else {
 | 
						|
            &change_LL_feature($n);
 | 
						|
        }
 | 
						|
        my @lln;
 | 
						|
        if ( (@lln=grep(/^$n$/,@llnodes)) | (@lln=grep(/^$n\./,@llnodes)) ) {
 | 
						|
            $remove_count++;
 | 
						|
            push (@llnodes_removed,$lln[0]);
 | 
						|
            if ( $remove_count < $llnode_count ) {
 | 
						|
              $remove_cmd .= " $lln[0]";
 | 
						|
            } else {
 | 
						|
              $remove_reservation = 1;
 | 
						|
              last;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    # Send LL reconfig to all central mgrs and resource mgrs
 | 
						|
    llreconfig();
 | 
						|
   #  Verify that the config change has been registered and that updatefeature 
 | 
						|
   #  has been removed according to what the LL daemons report 
 | 
						|
     if (defined($::DATAATTRS{updatefeature}[0])) {
 | 
						|
         my $machinelist = join(" ",@llnodes_removed);
 | 
						|
         my $llset;
 | 
						|
         my $statrun = 0;
 | 
						|
         do {
 | 
						|
            sleep 1;
 | 
						|
            my $llstatus = "llstatus -Lmachine -h $machinelist -l | grep -i feature | grep -i \" $::DATAATTRS{updatefeature}[0] \"";
 | 
						|
            if ($::VERBOSE) {
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." Running command \'$llstatus\'\n";
 | 
						|
                close (RULOG);
 | 
						|
            }
 | 
						|
            my @stat_out = xCAT::Utils->runcmd( $llstatus, 0 );
 | 
						|
            if ($::VERBOSE) {
 | 
						|
                open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
                print RULOG localtime()." Return code:  $::RUNCMD_RC\n";
 | 
						|
                close (RULOG);
 | 
						|
            }
 | 
						|
            # Are there any nodes with this feature still set?
 | 
						|
            $llset = $stat_out[0];
 | 
						|
### DEBUG - print output to see what's going on
 | 
						|
#if ($llset) {
 | 
						|
#   open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
#   print RULOG localtime()." llset:  $llset\n";
 | 
						|
#   close (RULOG);
 | 
						|
#}
 | 
						|
### end DEBUG
 | 
						|
            $statrun ++;
 | 
						|
         } until ((! $llset) || ($statrun > 10) );
 | 
						|
    }
 | 
						|
 | 
						|
    if ($remove_reservation) {
 | 
						|
        $cmd = "llrmres -R $::ll_reservation_id";
 | 
						|
    } elsif ( $remove_count > 0 ) {
 | 
						|
        $cmd = $remove_cmd;
 | 
						|
    } else {
 | 
						|
        return 0;  # no LL nodes to remove
 | 
						|
    }
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." $::ug_name:  Running command \'$cmd\'\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
#    if ($::TEST) { 
 | 
						|
#        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
#        print RULOG localtime()." $::ug_name:  In TEST mode.  Will NOT run command \'$cmd\' \n";
 | 
						|
#        close (RULOG);
 | 
						|
#    } else {
 | 
						|
        xCAT::Utils->runcmd( $cmd, 0 );
 | 
						|
        if ( $::RUNCMD_RC != 0 ) {
 | 
						|
            open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
            print RULOG localtime()." $::ug_name:  Error running command \'$cmd\'\n";
 | 
						|
            print RULOG localtime()." Return code:  $::RUNCMD_RC\n";
 | 
						|
            close (RULOG);
 | 
						|
            return 1;
 | 
						|
        }
 | 
						|
#    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   change_LL_feature
 | 
						|
 | 
						|
        Changes the LL feature for the node from oldfeature to newfeature
 | 
						|
           and removes updatefeature
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
        Error:
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub change_LL_feature {
 | 
						|
    my $node = shift;
 | 
						|
 | 
						|
    if (!defined($::DATAATTRS{updatefeature}[0]) && 
 | 
						|
        !defined($::DATAATTRS{oldfeature}[0]) && 
 | 
						|
        !defined($::DATAATTRS{newfeature}[0]) ) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    # Query current feature
 | 
						|
    my $cmd = "llconfig -h $node -d FEATURE";
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." $::ug_name:  Running command \'$cmd\'\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
    my ($llcfgout) = xCAT::Utils->runcmd( $cmd, 0 );
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." Return code:  $::RUNCMD_RC\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
 | 
						|
    # Remove old feature 
 | 
						|
    my $newfeature_string = "";
 | 
						|
    my @llfeatures;
 | 
						|
    if ( $llcfgout =~ /:FEATURE =/ ) {
 | 
						|
        my ($stuff,$curfeature_string) = split(/=/,$llcfgout);
 | 
						|
        @llfeatures = split(/\s+/,$curfeature_string);
 | 
						|
        my $oldfeature = " ";
 | 
						|
        my $updateallfeature = " ";
 | 
						|
        if (defined($::DATAATTRS{oldfeature}[0])) {
 | 
						|
           $oldfeature = $::DATAATTRS{oldfeature}[0];
 | 
						|
        }
 | 
						|
        if (defined($::DATAATTRS{updatefeature}[0])) {
 | 
						|
           $updateallfeature = $::DATAATTRS{updatefeature}[0];
 | 
						|
        }
 | 
						|
        foreach my $f (@llfeatures) {
 | 
						|
            if (($f eq $oldfeature) || ($f eq $updateallfeature)) {
 | 
						|
               $f = " "; 
 | 
						|
            }
 | 
						|
        }
 | 
						|
        $newfeature_string = join(" ",@llfeatures);
 | 
						|
    }
 | 
						|
 | 
						|
    # Add new feature
 | 
						|
    if ( defined($::DATAATTRS{newfeature}[0]) ) {
 | 
						|
        my $haveit = 0;
 | 
						|
        foreach my $f (@llfeatures) {
 | 
						|
            if ($f eq $::DATAATTRS{newfeature}[0]){
 | 
						|
               $haveit = 1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if ( !$haveit) {
 | 
						|
            $newfeature_string .= " $::DATAATTRS{newfeature}[0]";
 | 
						|
        }
 | 
						|
    }
 | 
						|
    # Change in LL database
 | 
						|
    $cmd = "llconfig -N -h $node -c FEATURE=\"$newfeature_string\"";
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." $::ug_name:  Running command \'$cmd\'\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
    xCAT::Utils->runcmd( $cmd, 0 );
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." Return code:  $::RUNCMD_RC\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
 | 
						|
    # Send LL reconfig to all central mgrs and resource mgrs
 | 
						|
  #  llreconfig();
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   remove_LL_updatefeature_only
 | 
						|
 | 
						|
        Changes the LL feature for the node to remove only the updatefeature 
 | 
						|
        Will NOT remove oldfeature or set newfeature
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
        Error:
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub remove_LL_updatefeature_only {
 | 
						|
    my $node = shift;
 | 
						|
 | 
						|
    if (!defined($::DATAATTRS{updatefeature}[0]) ) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    # Query current feature
 | 
						|
    my $cmd = "llconfig -h $node -d FEATURE";
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." $::ug_name:  Running command \'$cmd\'\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
    my ($llcfgout) = xCAT::Utils->runcmd( $cmd, 0 );
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." Return code:  $::RUNCMD_RC\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
 | 
						|
    # Remove old feature 
 | 
						|
    my $newfeature_string = "";
 | 
						|
    my @llfeatures;
 | 
						|
    if ( $llcfgout =~ /:FEATURE =/ ) {
 | 
						|
        my ($stuff,$curfeature_string) = split(/=/,$llcfgout);
 | 
						|
        @llfeatures = split(/\s+/,$curfeature_string);
 | 
						|
        my $updateallfeature = " ";
 | 
						|
        if (defined($::DATAATTRS{updatefeature}[0])) {
 | 
						|
           $updateallfeature = $::DATAATTRS{updatefeature}[0];
 | 
						|
        }
 | 
						|
        foreach my $f (@llfeatures) {
 | 
						|
            if ($f eq $updateallfeature) {
 | 
						|
               $f = " "; 
 | 
						|
            }
 | 
						|
        }
 | 
						|
        $newfeature_string = join(" ",@llfeatures);
 | 
						|
    }
 | 
						|
 | 
						|
    # Change in LL database
 | 
						|
    $cmd = "llconfig -N -h $node -c FEATURE=\"$newfeature_string\"";
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." $::ug_name:  Running command \'$cmd\'\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
    xCAT::Utils->runcmd( $cmd, 0 );
 | 
						|
    if ($::VERBOSE) {
 | 
						|
        open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
        print RULOG localtime()." Return code:  $::RUNCMD_RC\n";
 | 
						|
        close (RULOG);
 | 
						|
    }
 | 
						|
 | 
						|
    # Send LL reconfig to all central mgrs and resource mgrs
 | 
						|
 #   llreconfig();
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------------
 | 
						|
 | 
						|
=head3   llreconfig
 | 
						|
 | 
						|
        Queries LoadLeveler for the list of central mgrs and resource mgrs
 | 
						|
           and sends a llrctl reconfig to those nodes
 | 
						|
 | 
						|
        Arguments:
 | 
						|
        Returns:
 | 
						|
                0 - OK
 | 
						|
                1 - error
 | 
						|
        Globals:
 | 
						|
        Error:
 | 
						|
        Example:
 | 
						|
 | 
						|
        Comments:
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
sub llreconfig {
 | 
						|
 | 
						|
    # Send LL reconfig to all central mgrs and resource mgrs
 | 
						|
    my $cmd = "llconfig -d CENTRAL_MANAGER_LIST RESOURCE_MGR_LIST";
 | 
						|
    my @llcfg_d = xCAT::Utils->runcmd( $cmd, 0 );
 | 
						|
    my $llcms = "";
 | 
						|
    my $llrms = "";
 | 
						|
    foreach my $cfgo (@llcfg_d) {
 | 
						|
        chomp $cfgo;
 | 
						|
        my($llattr,$llval) = split (/ = /,$cfgo);
 | 
						|
        if ( $llattr =~ /CENTRAL_MANAGER_LIST/ ) {
 | 
						|
            $llcms = $llval; }
 | 
						|
        if ( $llattr =~ /RESOURCE_MGR_LIST/ ) {
 | 
						|
            $llrms = $llval; }
 | 
						|
    }
 | 
						|
    $cmd = "llrctl reconfig";
 | 
						|
    my @llms = split(/\s+/,$llcms." ".$llrms);
 | 
						|
    my %have = ();
 | 
						|
    my @llnodes;
 | 
						|
    my $runlocal=1;   # need to always run reconfig at least on local MN
 | 
						|
    foreach my $m (@llms) {
 | 
						|
       my ($sm,$rest) = split(/\./,$m);
 | 
						|
       my $xlated_sm = $sm;
 | 
						|
       if ( defined ($::XLATED{$sm}) ) { $xlated_sm = $::XLATED{$sm}; }
 | 
						|
       if (xCAT::NetworkUtils->thishostisnot($m)) {
 | 
						|
           push(@llnodes, $xlated_sm) unless $have{$sm}++;
 | 
						|
       } else {
 | 
						|
           $runlocal=1;
 | 
						|
       }
 | 
						|
    }
 | 
						|
    if ( defined($::FILEATTRS{reconfiglist}[0]) ) { 
 | 
						|
        push (@llnodes, split( /,/,$::FILEATTRS{reconfiglist}[0]) ); 
 | 
						|
    } elsif ( defined($::DATAATTRS{reconfiglist}[0]) ) {
 | 
						|
        push (@llnodes, split( /,/,$::DATAATTRS{reconfiglist}[0]) ); 
 | 
						|
    }
 | 
						|
    if ($runlocal) {
 | 
						|
        if ($::VERBOSE) {
 | 
						|
            open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
            print RULOG localtime()." Running local command \'$cmd\'\n";
 | 
						|
            close (RULOG);
 | 
						|
        }
 | 
						|
        if ($::TEST) {
 | 
						|
            my $rsp;
 | 
						|
            push @{ $rsp->{data} }, "In TEST mode.  Will NOT run command:  $cmd ";
 | 
						|
               xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
            $::RUNCMD_RC = 0;
 | 
						|
        } else {
 | 
						|
            xCAT::Utils->runcmd( $cmd, 0 );
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if ( scalar(@llnodes) > 0 ) {
 | 
						|
        if ($::VERBOSE) {
 | 
						|
            open (RULOG, ">>$::LOGDIR/$::LOGFILE");
 | 
						|
            print RULOG localtime()." Running command \'xdsh ".join(',',@llnodes)." $cmd\'\n";
 | 
						|
            close (RULOG);
 | 
						|
        }
 | 
						|
        if ($::TEST) {
 | 
						|
            my $rsp;
 | 
						|
            push @{ $rsp->{data} }, "In TEST mode.  Will NOT run command: xdsh <llcm,llrm> $cmd ";
 | 
						|
               xCAT::MsgUtils->message( "I", $rsp, $::CALLBACK );
 | 
						|
            $::RUNCMD_RC = 0;
 | 
						|
        } else {
 | 
						|
            xCAT::Utils->runxcmd(
 | 
						|
                  { command => ['xdsh'],
 | 
						|
                     node    => \@llnodes,
 | 
						|
                     arg     => [ "-v", $cmd ]
 | 
						|
                  },
 | 
						|
                  $::SUBREQ, -1);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
1;
 |