Added new pasu (parallel ASU) cmd and man page
git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@14969 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
This commit is contained in:
parent
698e5b21c5
commit
001c60917c
308
xCAT-client/bin/pasu
Executable file
308
xCAT-client/bin/pasu
Executable file
@ -0,0 +1,308 @@
|
||||
#!/usr/bin/perl
|
||||
#!/usr/bin/env perl
|
||||
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
|
||||
# Run the asu64 utility out of band to multiple nodes, either sequentially or in parallel
|
||||
BEGIN
|
||||
{
|
||||
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : -d '/opt/xcat' ? '/opt/xcat' : '/usr';
|
||||
}
|
||||
use strict;
|
||||
close(STDIN);
|
||||
open(STDIN,"<","/dev/null");
|
||||
use lib "$::XCATROOT/lib/perl";
|
||||
use IO::Socket::SSL;
|
||||
use XML::Simple;
|
||||
$XML::Simple::PREFERRED_PARSER='XML::Parser';
|
||||
#use Data::Dumper;
|
||||
use IO::Handle;
|
||||
use IO::Select;
|
||||
use xCAT::Utils;
|
||||
use Getopt::Long;
|
||||
use POSIX qw(:signal_h :errno_h :sys_wait_h);
|
||||
use Thread qw(yield);
|
||||
$::asucmd = '/opt/ibm/toolscenter/asu/asu64';
|
||||
my $interface;
|
||||
my $username;
|
||||
my $passwd;
|
||||
my $fanout;
|
||||
my $batchfile;
|
||||
my $help;
|
||||
Getopt::Long::Configure("require_order");
|
||||
Getopt::Long::Configure("bundling");
|
||||
Getopt::Long::Configure("no_pass_through");
|
||||
if (!GetOptions(
|
||||
"i|interface=s" => \$interface,
|
||||
"p|passwd=s" => \$passwd,
|
||||
'l|loginname=s' => \$username,
|
||||
'f|fanout=i' => \$fanout,
|
||||
'b|batch=s' => \$batchfile,
|
||||
'r|retry' => \$::RETRY,
|
||||
'd|donotfilter' => \$::DONOTFILTER,
|
||||
"n|nonodecheck" => \$::NONODECHECK, #does not check the noderange, in this case, noderange need to be a simple list of nodes.
|
||||
"V|verbose" => \$::VERBOSE,
|
||||
'h|help' => \$help,
|
||||
) || $help || ($batchfile && scalar(@ARGV)!=1) || (!$batchfile && scalar(@ARGV)<2) ) {
|
||||
print "Usage: pasu [-V] [-d] [-n] [-i <hostname-suffix>] [-l <user>] [-p <passwd>] [-f <fanout>] <noderange> <command>\n";
|
||||
print " pasu [-V] [-d] [-n] [-i <hostname-suffix>] [-l <user>] [-p <passwd>] [-f <fanout>] -b <batchfile> <noderange>\n";
|
||||
exit;
|
||||
}
|
||||
my %nodehdl;
|
||||
my $xcathost='localhost:3001';
|
||||
my $pasumaxp = 64;
|
||||
if ($ENV{XCATHOST}) {
|
||||
$xcathost=$ENV{XCATHOST};
|
||||
}
|
||||
if ($ENV{XCATPSHFANOUT}) {
|
||||
$pasumaxp=$ENV{XCATPSHFANOUT};
|
||||
}
|
||||
if ($fanout) { # see if they overroad the fanout from the command line
|
||||
$pasumaxp=$fanout;
|
||||
}
|
||||
my $noderange = shift @ARGV;
|
||||
my @nodes=();
|
||||
|
||||
#print "fanout=$fanout, username=$username, noderange=$noderange\n";
|
||||
|
||||
if ($::NONODECHECK) {
|
||||
@nodes=split(/,/, $noderange);
|
||||
}
|
||||
else { # contact xcatd to expand noderange
|
||||
@nodes = expandnoderange();
|
||||
}
|
||||
|
||||
if (!defined($username) || !defined($passwd)) {
|
||||
my ($user, $pw) = getcredentials();
|
||||
if (!defined($username)) { $username = $user; }
|
||||
if (!defined($passwd)) { $passwd = $pw; }
|
||||
}
|
||||
|
||||
if ($::VERBOSE) { print "username=$username, passwd=$passwd\n"; }
|
||||
|
||||
my $children = 0;
|
||||
my $inputs = new IO::Select;
|
||||
my %pids; # pid => node
|
||||
my %exitcodes; # Keep a list of children with known exit codes
|
||||
my %foundcodes;
|
||||
my @retries; # the nodes that fail with connection error
|
||||
|
||||
if ($interface) {
|
||||
foreach (@nodes) {
|
||||
s/$/-$interface/;
|
||||
}
|
||||
}
|
||||
|
||||
# Fork the processes for running asu for each node
|
||||
#todo: i thought we would need retry logic because i thought asu had problems running
|
||||
# in parallel. So far (up to 24 nodes) have not had any problem, but keeping the
|
||||
# logic in just in case.
|
||||
@retries = @nodes;
|
||||
while (scalar(@retries)) {
|
||||
@nodes = @retries;
|
||||
@retries = ();
|
||||
foreach (@nodes) {
|
||||
my $node=$_;
|
||||
while ($children > $pasumaxp) { processoutput($inputs); }
|
||||
my $child;
|
||||
$children++;
|
||||
#asunode(\$child,$node,$username,$passwd,@ARGV[1 .. $#ARGV]);
|
||||
asunode(\$child,$node,$username,$passwd,$batchfile,@ARGV); # child is the fd of the child process
|
||||
$inputs->add($child);
|
||||
$nodehdl{$child} = $node;
|
||||
#sleep 3;
|
||||
}
|
||||
while ($inputs->count) {
|
||||
processoutput($inputs);
|
||||
}
|
||||
while (processoutput($inputs)) {};
|
||||
while (wait != -1) {
|
||||
yield;
|
||||
}
|
||||
}
|
||||
|
||||
my $exitcode=0;
|
||||
foreach (values %pids) {
|
||||
my $possible_codes = join ",",keys %foundcodes;
|
||||
unless (defined $exitcodes{$_}) {
|
||||
print stderr "$_: *** pasu missed exit code, probably one of the following: $possible_codes\n";
|
||||
}
|
||||
}
|
||||
foreach (keys %exitcodes) {
|
||||
if ($exitcodes{$_}) {
|
||||
print stderr "$_: *** asu exited with error code ".$exitcodes{$_}.".\n";
|
||||
$exitcode++;
|
||||
}
|
||||
}
|
||||
if ($exitcode) { #Exit code reflects number of failed nodes
|
||||
$exitcode=$exitcode%256; #keep from overflowing valid values
|
||||
unless ($exitcode) { #if number of failed nodes happened to be evenly divisible by 256, make it non-zero again
|
||||
$exitcode++;
|
||||
}
|
||||
}
|
||||
exit($exitcode);
|
||||
|
||||
# Process output on the select stmt from the forked cmds
|
||||
sub processoutput { #This way, one arbiter handles output, no interrupting
|
||||
my $inputs = shift; # the select object that contains all the file descriptors
|
||||
my @readyins = $inputs->can_read(1); # child fds with some output available
|
||||
my $rc = @readyins;
|
||||
my $readyh;
|
||||
foreach $readyh (@readyins) {
|
||||
my $cursel = new IO::Select; # need to do non-blocking reads on this fd
|
||||
$cursel->add($readyh);
|
||||
while ($cursel->can_read(0)) {
|
||||
my $line = <$readyh>;
|
||||
unless ($line) {
|
||||
$inputs->remove($readyh);
|
||||
close($readyh);
|
||||
$exitcodes{$nodehdl{$readyh}} = $? >> 8;
|
||||
$children--;
|
||||
next;
|
||||
}
|
||||
chomp($line);
|
||||
if ($::RETRY && ($line =~ /Connection link error/i) ) {
|
||||
if ($::VERBOSE) { print "Need to retry $nodehdl{$readyh}\n"; }
|
||||
push @retries, $nodehdl{$readyh};
|
||||
} elsif ($::DONOTFILTER || ($line!~/IBM Advanced Settings Utility version/i &&
|
||||
$line!~/Licensed Materials - Property of IBM/i &&
|
||||
$line!~/\(C\) Copyright IBM Corp. \d+-\d+ All Rights Reserved/i &&
|
||||
$line!~/Connected to IMM at IP address/i )) {
|
||||
print $nodehdl{$readyh}.": ".$line."\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
no strict 'subs';
|
||||
IO::Handle::flush(stdout);
|
||||
use strict 'subs';
|
||||
yield; #Explicitly give all children a chance to refill any buffers
|
||||
return $rc;
|
||||
}
|
||||
|
||||
# Fork the asu cmd for 1 node
|
||||
sub asunode {
|
||||
my $out = shift; # this is a reference to the child file descriptor
|
||||
my $node = shift;
|
||||
my $username = shift;
|
||||
my $passwd = shift;
|
||||
my $batchfile = shift;
|
||||
my $args;
|
||||
if ($batchfile) {
|
||||
$args = "batch $batchfile";
|
||||
} else {
|
||||
foreach my $a (@_) { $args .= ' ' . xCAT::Utils->quote($a); }
|
||||
}
|
||||
my $cmd = "$::asucmd $args --host $node --user $username --password $passwd 2>&1 |";
|
||||
if ($::VERBOSE) { print "forking $cmd\n"; }
|
||||
my $pid = open($$out, $cmd);
|
||||
$pids{$pid} = $node;
|
||||
}
|
||||
|
||||
# Contact xcatd to expand the noderange into a list of nodes
|
||||
sub expandnoderange {
|
||||
my @nodes;
|
||||
my @user = getpwuid($>);
|
||||
my $homedir=$user[7];
|
||||
my $client = IO::Socket::SSL->new(
|
||||
PeerAddr=>$xcathost,
|
||||
SSL_key_file=>$homedir."/.xcat/client-cred.pem",
|
||||
SSL_cert_file=>$homedir."/.xcat/client-cred.pem",
|
||||
SSL_ca_file => $homedir."/.xcat/ca.pem",
|
||||
SSL_use_cert => 1,
|
||||
#SSL_verify_mode => 1,
|
||||
);
|
||||
die "Connection failure: $!\n" unless ($client);
|
||||
#todo: get the bmc attr for each node, not the node name itself
|
||||
my %cmdref = (command => 'noderange', noderange => $noderange);
|
||||
$SIG{ALRM} = sub { die "No response getting noderange" };
|
||||
alarm(15);
|
||||
my $msg = XMLout(\%cmdref,RootName=>'xcatrequest', NoAttr=>1, KeyAttr => []);
|
||||
if ($ENV{XCATXMLTRACE}) { print $msg; }
|
||||
print $client $msg;
|
||||
alarm(15);
|
||||
my $response="";
|
||||
while (<$client>) {
|
||||
alarm(0);
|
||||
$response .= $_;
|
||||
if ($response =~ m/<\/xcatresponse>/) {
|
||||
if ($ENV{XCATXMLTRACE}) { print $response; }
|
||||
my $rsp=XMLin($response, ForceArray => ['node']);
|
||||
$response='';
|
||||
if ($rsp->{warning}) {
|
||||
printf "Warning: ".$rsp->{warning}."\n";
|
||||
}
|
||||
if ($rsp->{error}) {
|
||||
die ("ERROR: ".$rsp->{error}."\n");
|
||||
} elsif ($rsp->{node}) {
|
||||
@nodes=@{$rsp->{node}};
|
||||
}
|
||||
if ($rsp->{serverdone}) {
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
close($client);
|
||||
if ($::VERBOSE) { print 'Nodes:', join(',',@nodes), "\n"; }
|
||||
return @nodes;
|
||||
}
|
||||
|
||||
# Contact xcatd to get the default user/pw for ipmi in the xcat passwd table
|
||||
sub getcredentials {
|
||||
my @data;
|
||||
my @user = getpwuid($>);
|
||||
my $homedir=$user[7];
|
||||
my $client = IO::Socket::SSL->new(
|
||||
PeerAddr=>$xcathost,
|
||||
SSL_key_file=>$homedir."/.xcat/client-cred.pem",
|
||||
SSL_cert_file=>$homedir."/.xcat/client-cred.pem",
|
||||
SSL_ca_file => $homedir."/.xcat/ca.pem",
|
||||
SSL_use_cert => 1,
|
||||
#SSL_verify_mode => 1,
|
||||
);
|
||||
die "Connection failure: $!\n" unless ($client);
|
||||
#todo: use lissas new db api instead
|
||||
my %cmdref = (command => 'tabdump', arg => 'passwd');
|
||||
#push (@{$cmdref->{arg}}, 'passwd');
|
||||
$SIG{ALRM} = sub { die "No response getting userid and password" };
|
||||
alarm(15);
|
||||
my $msg = XMLout(\%cmdref,RootName=>'xcatrequest', NoAttr=>1, KeyAttr => []);
|
||||
if ($ENV{XCATXMLTRACE}) { print $msg; }
|
||||
print $client $msg;
|
||||
alarm(15);
|
||||
my $response="";
|
||||
while (<$client>) {
|
||||
alarm(0);
|
||||
$response .= $_;
|
||||
if ($response =~ m/<\/xcatresponse>/) {
|
||||
if ($ENV{XCATXMLTRACE}) { print $response; }
|
||||
my $rsp=XMLin($response, ForceArray => ['node']);
|
||||
$response='';
|
||||
if ($rsp->{warning}) {
|
||||
printf "Warning: ".$rsp->{warning}."\n";
|
||||
}
|
||||
if ($rsp->{error}) {
|
||||
die ("ERROR: ".$rsp->{error}."\n");
|
||||
} elsif ($rsp->{data}) {
|
||||
@data=@{$rsp->{data}};
|
||||
}
|
||||
if ($rsp->{serverdone}) {
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
close($client);
|
||||
|
||||
# go thru the data lines and find the ipmi row
|
||||
my ($user, $pw);
|
||||
foreach my $d (@data) {
|
||||
#if ($::VERBOSE) { print "$d\n"; }
|
||||
my @cols = split(',', $d);
|
||||
if ($cols[0] eq '"ipmi"') {
|
||||
($user) = $cols[1] =~ /"(.*)"/;
|
||||
($pw) = $cols[2] =~ /"(.*)"/;
|
||||
last;
|
||||
}
|
||||
}
|
||||
if (!defined($user) || !defined($pw)) { die "Did not find the ipmi username and password in the xCAT passwd table.\n"; }
|
||||
return ($user, $pw);
|
||||
}
|
||||
|
||||
# vim: set et ts=2 sts=2 sw=2 :
|
198
xCAT-client/pods/man1/pasu.1.pod
Normal file
198
xCAT-client/pods/man1/pasu.1.pod
Normal file
@ -0,0 +1,198 @@
|
||||
=head1 NAME
|
||||
|
||||
B<pasu> - run the ASU to many nodes in parallel
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<pasu> [B<-V>] [B<-d>] [B<-n>] [B<-i> I<hostname-suffix>] [B<-l> I<user>] [B<-p> I<passwd>] [B<-f> I<fanout>] I<noderange> I<command>
|
||||
|
||||
B<pasu> [B<-V>] [B<-d>] [B<-n>] [B<-i> I<hostname-suffix>] [B<-l> I<user>] [B<-p> I<passwd>] [B<-f> I<fanout>] B<-b> I<batchfile> I<noderange>
|
||||
|
||||
B<pasu> [B<-h> | B<--help>]
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The B<pasu> command runs the ASU command in out-of-band mode in parallel to multiple nodes. Out-of-band mode means
|
||||
that ASU connects from the xCAT management node to the IMM (BMC) of each node to set or query the ASU settings. To
|
||||
see all of the ASU settings available on the node, use the "show all" command. To query or set multiple values,
|
||||
use the B<-b> (batch) option. To group similar output from multiple nodes, use L<xcoll(1)|xcoll.1>.
|
||||
|
||||
Current Limitation: The pasu command currently only queries the node names from the xCAT DB, but ASU really
|
||||
needs to connect to the IMM of the node. You can make this work by defining hostnames for your IMMs that are of
|
||||
the form: <nodename>-imm . Then use the B<-i imm> flag and pasu will add "-imm" to each nodename before contacting
|
||||
it. Or you can use the B<-n> option and give the list of IMM hostnames or IP addresses explicitly.
|
||||
|
||||
Before running B<pasu>, you must install the ASU RPM from IBM. You can download it from the IBM Fix Central site.
|
||||
You also must configure the IMMs properly according to xCAT documentation. Run "B<rpower> I<noderange> B<stat>"
|
||||
to confirm that the IMMs are configured properly.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over 10
|
||||
|
||||
=item B<-i|--interface> I<suffix>
|
||||
|
||||
A suffix to add to each nodename before connecting to it. Currently use this to change the nodename into the
|
||||
IMM name.
|
||||
|
||||
=item B<-n|--nonodecheck>
|
||||
|
||||
Do not send the noderange to xcatd to expand it into a list of nodes. Use the noderange exactly as it is specified
|
||||
to pasu. In this case, the noderange must be a simple list of comma-separated node names.
|
||||
|
||||
=item B<-l|--loginname> I<username>
|
||||
|
||||
The username to use to connect to the IMMs. If not specified, the row in the xCAT B<passwd> table with key "ipmi"
|
||||
will be used to get the username. Currently B<pasu> does not use the table value ipmi.username.
|
||||
|
||||
=item B<-p|--passwd> I<passwd>
|
||||
|
||||
The password to use to connect to the IMMs. If not specified, the row in the xCAT passwd table with key "ipmi"
|
||||
will be used to get the password. Currently B<pasu> does not use the table value ipmi.password.
|
||||
|
||||
=item B<-f|--fanout>
|
||||
|
||||
How many processes to run in parallel simultaneously. The default is 64. You can also set the XCATPSHFANOUT
|
||||
environment variable.
|
||||
|
||||
=item B<-b|--batch> -I<batchfile>
|
||||
|
||||
A simple text file that contains multiple ASU commands, each on its own line.
|
||||
|
||||
=item B<-d|--donotfilter>
|
||||
|
||||
By default, pasu filters out (i.e. does not display) the standard initial output from ASU:
|
||||
|
||||
IBM Advanced Settings Utility version 9.30.79N
|
||||
Licensed Materials - Property of IBM
|
||||
(C) Copyright IBM Corp. 2007-2012 All Rights Reserved
|
||||
Connected to IMM at IP address node2-imm
|
||||
|
||||
If you want this output to be displayed, use this flag.
|
||||
|
||||
=item B<-V|--verbose>
|
||||
|
||||
Display verbose messages.
|
||||
|
||||
=item B<-h|--help>
|
||||
|
||||
Display usage message.
|
||||
|
||||
=back
|
||||
|
||||
=head1 RETURN VALUE
|
||||
|
||||
0 The command completed successfully.
|
||||
|
||||
1 An error has occurred.
|
||||
|
||||
=head1 EXAMPLES
|
||||
|
||||
=over 3
|
||||
|
||||
=item 1.
|
||||
|
||||
To display the Com1ActiveAfterBoot setting on 2 nodes:
|
||||
|
||||
pasu -n node1,node2 show DevicesandIOPorts.Com1ActiveAfterBoot
|
||||
|
||||
Output is similar to:
|
||||
|
||||
node1: DevicesandIOPorts.Com1ActiveAfterBoot=Enable
|
||||
node2: DevicesandIOPorts.Com1ActiveAfterBoot=Enable
|
||||
|
||||
=item 2.
|
||||
|
||||
To display the Com1ActiveAfterBoot setting on all compute nodes (and the IMMs have hostnames like <node>-imm):
|
||||
|
||||
pasu -i imm compute show DevicesandIOPorts.Com1ActiveAfterBoot | xcoll
|
||||
|
||||
Output is similar to:
|
||||
|
||||
====================================
|
||||
compute
|
||||
====================================
|
||||
DevicesandIOPorts.Com1ActiveAfterBoot=Enable
|
||||
|
||||
=item 3.
|
||||
|
||||
To set several settings on all compute nodes (and the IMMs have hostnames like <node>-imm), create a batch file
|
||||
called (for example) asu-settings with contents:
|
||||
|
||||
set DevicesandIOPorts.Com1ActiveAfterBoot Enable
|
||||
set DevicesandIOPorts.SerialPortSharing Enable
|
||||
set DevicesandIOPorts.SerialPortAccessMode Dedicated
|
||||
set DevicesandIOPorts.RemoteConsole Enable
|
||||
|
||||
Then run:
|
||||
|
||||
pasu -i imm -b asu-settings compute | xcoll
|
||||
|
||||
Output is similar to:
|
||||
|
||||
====================================
|
||||
compute
|
||||
====================================
|
||||
Batch mode start.
|
||||
[set DevicesandIOPorts.Com1ActiveAfterBoot Enable]
|
||||
DevicesandIOPorts.Com1ActiveAfterBoot=Enable
|
||||
|
||||
[set DevicesandIOPorts.SerialPortSharing Enable]
|
||||
DevicesandIOPorts.SerialPortSharing=Enable
|
||||
|
||||
[set DevicesandIOPorts.SerialPortAccessMode Dedicated]
|
||||
DevicesandIOPorts.SerialPortAccessMode=Dedicated
|
||||
|
||||
[set DevicesandIOPorts.RemoteConsole Enable]
|
||||
DevicesandIOPorts.RemoteConsole=Enable
|
||||
|
||||
Beginning intermediate batch update.
|
||||
Waiting for command completion status.
|
||||
Command completed successfully.
|
||||
Completed intermediate batch update.
|
||||
Batch mode competed successfully.
|
||||
|
||||
=item 4.
|
||||
|
||||
To confirm that all the settings were made on all compute nodes, create a batch file
|
||||
called (for example) asu-show with contents:
|
||||
|
||||
show DevicesandIOPorts.Com1ActiveAfterBoot
|
||||
show DevicesandIOPorts.SerialPortSharing
|
||||
show DevicesandIOPorts.SerialPortAccessMode
|
||||
show DevicesandIOPorts.RemoteConsole
|
||||
|
||||
Then run:
|
||||
|
||||
pasu -i imm -b asu-show compute | xcoll
|
||||
|
||||
Output is similar to:
|
||||
|
||||
====================================
|
||||
compute
|
||||
====================================
|
||||
Batch mode start.
|
||||
[show DevicesandIOPorts.Com1ActiveAfterBoot]
|
||||
DevicesandIOPorts.Com1ActiveAfterBoot=Enable
|
||||
|
||||
[show DevicesandIOPorts.SerialPortSharing]
|
||||
DevicesandIOPorts.SerialPortSharing=Enable
|
||||
|
||||
[show DevicesandIOPorts.SerialPortAccessMode]
|
||||
DevicesandIOPorts.SerialPortAccessMode=Dedicated
|
||||
|
||||
[show DevicesandIOPorts.RemoteConsole]
|
||||
DevicesandIOPorts.RemoteConsole=Enable
|
||||
|
||||
Batch mode competed successfully.
|
||||
|
||||
=back
|
||||
|
||||
=head1 FILES
|
||||
|
||||
/opt/xcat/bin/pasu
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<noderange(3)|noderange.3>, L<rpower(1)|rpower.1>, L<xcoll(1)|xcoll.1>
|
||||
|
Loading…
Reference in New Issue
Block a user