c67a44d85f
git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@4852 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
1683 lines
54 KiB
Perl
1683 lines
54 KiB
Perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
package xCAT::PPC;
|
|
use strict;
|
|
use lib "/opt/xcat/lib/perl";
|
|
use xCAT::Table;
|
|
use xCAT::Utils;
|
|
use xCAT::SvrUtils;
|
|
use xCAT::Usage;
|
|
use POSIX "WNOHANG";
|
|
use Storable qw(freeze thaw);
|
|
use Time::HiRes qw(gettimeofday sleep);
|
|
use IO::Select;
|
|
use Socket;
|
|
use xCAT::PPCcli;
|
|
use xCAT::GlobalDef;
|
|
use xCAT::DBobjUtils;
|
|
use xCAT_monitoring::monitorctrl;
|
|
use Thread qw(yield);
|
|
|
|
|
|
##########################################
|
|
# Globals
|
|
##########################################
|
|
my %modules = (
|
|
rinv => { hmc => "xCAT::PPCinv",
|
|
fsp => "xCAT::FSPinv",
|
|
bpa => "xCAT::FSPinv",
|
|
},
|
|
rpower => { hmc => "xCAT::PPCpower",
|
|
fsp => "xCAT::FSPpower",
|
|
bpa => "xCAT::FSPpower",
|
|
},
|
|
rvitals => { hmc => "xCAT::PPCvitals",
|
|
},
|
|
rscan => { hmc => "xCAT::PPCscan",
|
|
},
|
|
mkvm => { hmc => "xCAT::PPCvm",
|
|
},
|
|
rmvm => { hmc => "xCAT::PPCvm",
|
|
},
|
|
lsvm => { hmc => "xCAT::PPCvm",
|
|
},
|
|
chvm => { hmc => "xCAT::PPCvm",
|
|
},
|
|
rnetboot => { hmc => "xCAT::PPCboot",
|
|
},
|
|
getmacs => { hmc => "xCAT::PPCmac",
|
|
},
|
|
reventlog => { hmc => "xCAT::PPClog",
|
|
},
|
|
rspconfig => { hmc => "xCAT::PPCcfg",
|
|
fsp => "xCAT::PPCcfg",
|
|
bpa => "xCAT::PPCcfg",
|
|
},
|
|
rflash => { hmc => "xCAT::PPCrflash",
|
|
fsp => "xCAT::FSPflash",
|
|
bpa => "xCAT::FSPflash",
|
|
},
|
|
mkhwconn => { hmc => "xCAT::PPCconn",
|
|
fsp => "xCAT::PPCconn",
|
|
bpa => "xCAT::PPCconn",
|
|
},
|
|
rmhwconn => { hmc => "xCAT::PPCconn",
|
|
fsp => "xCAT::PPCconn",
|
|
bpa => "xCAT::PPCconn",
|
|
},
|
|
lshwconn => { hmc => "xCAT::PPCconn",
|
|
fsp => "xCAT::PPCconn",
|
|
bpa => "xCAT::PPCconn",
|
|
},
|
|
renergy => { hmc => "xCAT::PPCenergy",
|
|
},
|
|
);
|
|
|
|
|
|
##########################################
|
|
# Database errors
|
|
##########################################
|
|
my %errmsg = (
|
|
NODE_UNDEF =>"Node not defined in '%s' database",
|
|
NO_ATTR =>"'%s' not defined in '%s' database",
|
|
NO_UNDEF =>"'%s' not defined in '%s' database for '%s'",
|
|
DB_UNDEF =>"'%s' database not defined"
|
|
);
|
|
|
|
##########################################################################
|
|
# Invokes the callback with the specified message
|
|
##########################################################################
|
|
sub send_msg {
|
|
|
|
my $request = shift;
|
|
my $ecode = shift;
|
|
my %output;
|
|
|
|
#################################################
|
|
# Called from child process - send to parent
|
|
#################################################
|
|
if ( exists( $request->{pipe} )) {
|
|
my $out = $request->{pipe};
|
|
|
|
$output{errorcode} = $ecode;
|
|
$output{data} = \@_;
|
|
print $out freeze( [\%output] );
|
|
print $out "\nENDOFFREEZE6sK4ci\n";
|
|
}
|
|
#################################################
|
|
# Called from parent - invoke callback directly
|
|
#################################################
|
|
elsif ( exists( $request->{callback} )) {
|
|
my $callback = $request->{callback};
|
|
|
|
$output{errorcode} = $ecode;
|
|
$output{data} = \@_;
|
|
$callback->( \%output );
|
|
}
|
|
}
|
|
|
|
|
|
##########################################################################
|
|
# Fork child to execute remote commands
|
|
##########################################################################
|
|
sub process_command {
|
|
|
|
my $request = shift;
|
|
my %nodes = ();
|
|
my $callback = $request->{callback};
|
|
my $sitetab = xCAT::Table->new( 'site' );
|
|
my @site = qw(ppcmaxp ppctimeout maxssh ppcretry fsptimeout);
|
|
my $start;
|
|
|
|
#######################################
|
|
# Default site table attributes
|
|
#######################################
|
|
$request->{ppcmaxp} = 64;
|
|
$request->{ppctimeout} = 0;
|
|
$request->{fsptimeout} = 0;
|
|
$request->{ppcretry} = 3;
|
|
$request->{maxssh} = 8;
|
|
|
|
#######################################
|
|
# Get site table attributes
|
|
#######################################
|
|
if ( defined( $sitetab )) {
|
|
foreach ( @site ) {
|
|
my ($ent) = $sitetab->getAttribs({ key=>$_},'value');
|
|
if ( defined($ent) ) {
|
|
$request->{$_} = $ent->{value};
|
|
}
|
|
}
|
|
}
|
|
if ( exists( $request->{verbose} )) {
|
|
$start = Time::HiRes::gettimeofday();
|
|
}
|
|
|
|
#xCAT doesn't support FSPpower,FSPinv and FSPrflash by default
|
|
$request->{fsp_api} = 0;
|
|
#######################################
|
|
# FSPpower, FSPinv and FSPrflash handler
|
|
#########################################
|
|
my $hwtype = $request->{hwtype};
|
|
if ( $hwtype eq "fsp" or $hwtype eq "bpa") {
|
|
my $fsp_api = check_fsp_api($request);
|
|
if($fsp_api == 0 &&
|
|
($request->{command} =~ /^(rpower)$/ || $request->{command} =~ /^rinv$/ || $request->{command} =~ /^rflash$/)) {
|
|
#support FSPpower, FSPinv and FSPrflash
|
|
$request->{fsp_api} = 1;
|
|
}
|
|
}
|
|
|
|
#######################################
|
|
# Group nodes based on command
|
|
#######################################
|
|
my $nodes = preprocess_nodes( $request );
|
|
if ( !defined( $nodes )) {
|
|
return(1);
|
|
}
|
|
|
|
#get new node status
|
|
my %oldnodestatus=(); #saves the old node status
|
|
my @allerrornodes=();
|
|
my $check=0;
|
|
my $global_check=1;
|
|
if ($sitetab) {
|
|
(my $ref) = $sitetab->getAttribs({key => 'nodestatus'}, 'value');
|
|
if ($ref) {
|
|
if ($ref->{value} =~ /0|n|N/) { $global_check=0; }
|
|
}
|
|
}
|
|
|
|
my $command=$request->{command};
|
|
if (($command eq 'rpower') || ($command eq 'rnetboot')) {
|
|
my $subcommand="temp";
|
|
if ($command eq 'rpower') { $subcommand=$request->{op}; }
|
|
if (($global_check) && ($subcommand ne 'stat') && ($subcommand ne 'status') && ($subcommand ne 'state')) {
|
|
$check=1;
|
|
my $noderange = $request->{node};
|
|
my @allnodes=@$noderange;
|
|
|
|
#save the old status
|
|
my $nodelisttab = xCAT::Table->new('nodelist');
|
|
if ($nodelisttab) {
|
|
my $tabdata = $nodelisttab->getNodesAttribs(\@allnodes, ['node', 'status']);
|
|
foreach my $node (@allnodes)
|
|
{
|
|
my $tmp1 = $tabdata->{$node}->[0];
|
|
if ($tmp1) {
|
|
if ($tmp1->{status}) { $oldnodestatus{$node}=$tmp1->{status}; }
|
|
else { $oldnodestatus{$node}=""; }
|
|
}
|
|
}
|
|
}
|
|
#print "oldstatus:" . Dumper(\%oldnodestatus);
|
|
|
|
#set the new status to the nodelist.status
|
|
my %newnodestatus=();
|
|
my $newstat;
|
|
if (($subcommand eq 'off') || ($subcommand eq 'softoff')) {
|
|
my $newstat=$::STATUS_POWERING_OFF;
|
|
$newnodestatus{$newstat}=\@allnodes;
|
|
} else {
|
|
#get the current nodeset stat
|
|
if (@allnodes>0) {
|
|
my $nsh={};
|
|
my ($ret, $msg)=xCAT::SvrUtils->getNodesetStates(\@allnodes, $nsh);
|
|
if (!$ret) {
|
|
foreach (keys %$nsh) {
|
|
my $newstat=xCAT_monitoring::monitorctrl->getNodeStatusFromNodesetState($_, $command);
|
|
$newnodestatus{$newstat}=$nsh->{$_};
|
|
}
|
|
} else {
|
|
trace( $request, $msg );
|
|
}
|
|
}
|
|
}
|
|
#print "newstatus" . Dumper(\%newnodestatus);
|
|
xCAT_monitoring::monitorctrl::setNodeStatusAttributes(\%newnodestatus, 1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#######################################
|
|
# Fork process
|
|
#######################################
|
|
my $children = 0;
|
|
my $fds = new IO::Select;
|
|
|
|
# For the commands getmacs and rnetboot, each time
|
|
# to fork process, pick out the HMC that has the
|
|
# least process number created to connect to the HMC.
|
|
# After the process by preprocess_nodes, the $nodes
|
|
# variable has following structure:
|
|
# $nodes
|
|
# |hcp
|
|
# |[[hcp,node1_attr], [hcp,node2_attr] ...]
|
|
# |count //node number managed by the hcp
|
|
# |runprocess //the process number connect to the hcp
|
|
# |index //the index of node will be forked of the hcp
|
|
if ( ($request->{command} =~ /^(getmacs)$/ && exists( $request->{opt}->{D} )) || ($request->{command} =~ /^(rnetboot)$/) ) {
|
|
my %pid_owner = ();
|
|
|
|
$request->{maxssh} = int($request->{maxssh}/2);
|
|
# Use the CHID signal to control the
|
|
#connection number of certain hcp
|
|
$SIG{CHLD} = sub { my $pid = 0; while (($pid = waitpid(-1, WNOHANG)) > 0)
|
|
{ $nodes->{$pid_owner{$pid}}{'runprocess'}--; delete $pid_owner{$pid}; $children--; } };
|
|
|
|
$SIG{INT} = $SIG{TERM} = sub { #prepare to process job termination and propogate it down
|
|
foreach (keys %pid_owner) {
|
|
kill 9, $_;
|
|
}
|
|
exit 0;
|
|
};
|
|
|
|
my $hasnode = 1;
|
|
while ($hasnode) {
|
|
while ( $children >= $request->{ppcmaxp} ) {
|
|
my $handlednodes={};
|
|
child_response( $callback, $fds, $handlednodes);
|
|
|
|
#update the node status to the nodelist.status table
|
|
if ($check) {
|
|
updateNodeStatus($handlednodes, \@allerrornodes);
|
|
}
|
|
|
|
Time::HiRes::sleep(0.1);
|
|
}
|
|
# Pick out the hcp which has least processes
|
|
my $least_processes = $request->{maxssh};
|
|
my $least_hcp;
|
|
my $got_one = 0;
|
|
while (!$got_one) {
|
|
$hasnode = 0;
|
|
foreach my $hcp (keys %$nodes) {
|
|
if ($nodes->{$hcp}{'index'} < $nodes->{$hcp}{'count'}) {
|
|
$hasnode = 1;
|
|
if ($nodes->{$hcp}{'runprocess'} < $least_processes) {
|
|
$least_processes = $nodes->{$hcp}{'runprocess'};
|
|
$least_hcp = $hcp;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$hasnode) {
|
|
# There are no node in the $nodes
|
|
goto ENDOFFORK;
|
|
}
|
|
|
|
if ($least_processes < $request->{maxssh}) {
|
|
$got_one = 1;
|
|
} else {
|
|
my $handlednodes={};
|
|
child_response( $callback, $fds, $handlednodes);
|
|
|
|
#update the node status to the nodelist.status table
|
|
if ($check) {
|
|
updateNodeStatus($handlednodes, \@allerrornodes);
|
|
}
|
|
Time::HiRes::sleep(0.1);
|
|
}
|
|
}
|
|
|
|
Time::HiRes::sleep(0.1);
|
|
my ($pipe, $pid) = fork_cmd( $nodes->{$least_hcp}{'nodegroup'}->[$nodes->{$least_hcp}{'index'}]->[0],
|
|
$nodes->{$least_hcp}{'nodegroup'}->[$nodes->{$least_hcp}{'index'}]->[1], $request );
|
|
|
|
if ($pid) {
|
|
$pid_owner{$pid} = $least_hcp;
|
|
$nodes->{$least_hcp}{'index'}++;
|
|
$nodes->{$least_hcp}{'runprocess'}++;
|
|
}
|
|
|
|
if ( $pipe ) {
|
|
$fds->add( $pipe );
|
|
$children++;
|
|
}
|
|
}
|
|
} elsif ( $request->{command} =~ /^(getmacs)$/ && exists( $request->{opt}->{arp} ) ) {
|
|
my $node;
|
|
my $data;
|
|
my $unreachable_nodes;
|
|
my $noderange = join (',', @$nodes);
|
|
my @output = xCAT::Utils->runcmd("/opt/xcat/bin/pping $noderange", -1);
|
|
|
|
foreach my $line (@output) {
|
|
my ($hostname, $result) = split ':', $line;
|
|
my ($token, $status) = split ' ', $result;
|
|
chomp($token);
|
|
if ($token eq 'ping') {
|
|
$node->{$hostname}->{reachable} = 1;
|
|
}
|
|
}
|
|
|
|
foreach my $n ( @$nodes ) {
|
|
if ( $node->{$n}->{reachable} ) {
|
|
my $output;
|
|
my $IP = xCAT::Utils::toIP( $n );
|
|
if ( xCAT::Utils->isAIX() ) {
|
|
$output = `/usr/sbin/arp -a`;
|
|
} else {
|
|
$output = `/sbin/arp -n`;
|
|
}
|
|
|
|
my ($ip, $mac);
|
|
my @lines = split /\n/, $output;
|
|
foreach my $line ( @lines ) {
|
|
if ( xCAT::Utils->isAIX() && $line =~ /\((\S+)\)\s+at\s+(\S+)/ ) {
|
|
($ip, $mac) = ($1,$2);
|
|
######################################################
|
|
# Change mac format to be same as linux. For example:
|
|
# '0:d:60:f4:f8:22' to '00:0d:60:f4:f8:22'
|
|
######################################################
|
|
if ( $mac)
|
|
{
|
|
my @mac_sections = split /:/, $mac;
|
|
for my $m (@mac_sections)
|
|
{
|
|
$m = "0$m" if ( length($m) == 1);
|
|
}
|
|
$mac = join ':', @mac_sections;
|
|
}
|
|
} elsif ( $line =~ /^(\S+)+\s+\S+\s+(\S+)\s/ ) {
|
|
($ip, $mac) = ($1,$2);
|
|
} else {
|
|
($ip, $mac) = (undef,undef);
|
|
}
|
|
if ( @$IP[1] !~ $ip ) {
|
|
($ip, $mac) = (undef,undef);
|
|
} else {
|
|
last;
|
|
}
|
|
}
|
|
if ( $ip && $mac ) {
|
|
if ( !exists( $request->{opt}->{d} ) ) {
|
|
#####################################
|
|
# Write adapter mac to database
|
|
#####################################
|
|
my $mactab = xCAT::Table->new( "mac", -create=>1, -autocommit=>1 );
|
|
$mactab->setNodeAttribs( $n,{mac=>$mac} );
|
|
$mactab->close();
|
|
}
|
|
|
|
$callback->({node=>[{name=>[$n],data=>["\n#IP MAC\n$ip $mac\n"]}]});
|
|
}
|
|
} else {
|
|
$unreachable_nodes = join (",", $n, $unreachable_nodes);
|
|
}
|
|
}
|
|
$callback->({data=>["Unreachable Nodes:"]});
|
|
$callback->({data=>["$unreachable_nodes\n"]});
|
|
} elsif ( $request->{command} =~ /^rpower$/ ) {
|
|
my $hw;
|
|
my $sessions;
|
|
my $pid_owner;
|
|
my $remain_node = $nodes;
|
|
|
|
while ( scalar($remain_node) ) {
|
|
$remain_node = ();
|
|
foreach my $hash ( @$nodes ) {
|
|
$SIG{CHLD} = sub { my $pid = 0; while (($pid = waitpid(-1, WNOHANG)) > 0) { $hw->{$pid_owner->{$pid}}--; $children--; } };
|
|
|
|
while ( $children >= $request->{ppcmaxp} ) {
|
|
my $handlednodes={};
|
|
child_response( $callback, $fds, $handlednodes);
|
|
|
|
#update the node status to the nodelist.status table
|
|
if ($check) {
|
|
updateNodeStatus($handlednodes, \@allerrornodes);
|
|
}
|
|
|
|
Time::HiRes::sleep(0.1);
|
|
}
|
|
if ( $hw->{@$hash[0]} >= $request->{maxssh} ) {
|
|
my $handlednodes={};
|
|
child_response( $callback, $fds, $handlednodes);
|
|
|
|
#update the node status to the nodelist.status table
|
|
if ($check) {
|
|
updateNodeStatus($handlednodes, \@allerrornodes);
|
|
}
|
|
|
|
Time::HiRes::sleep(0.1);
|
|
push( @$remain_node, [@$hash[0], @$hash[1]] );
|
|
next;
|
|
}
|
|
|
|
my ($pipe,$pid) = fork_cmd( @$hash[0], @$hash[1], $request );
|
|
|
|
if ($pid) {
|
|
$pid_owner->{$pid} = @$hash[0];
|
|
$hw->{@$hash[0]}++;
|
|
}
|
|
|
|
if ( $pipe ) {
|
|
$fds->add( $pipe );
|
|
$children++;
|
|
}
|
|
}
|
|
|
|
$nodes = $remain_node;
|
|
}
|
|
} else {
|
|
$SIG{CHLD} = sub { while (waitpid(-1, WNOHANG) > 0) { $children--; } };
|
|
my $hw;
|
|
my $sessions;
|
|
|
|
foreach ( @$nodes ) {
|
|
while ( $children >= $request->{ppcmaxp} ) {
|
|
my $handlednodes={};
|
|
child_response( $callback, $fds, $handlednodes);
|
|
|
|
#update the node status to the nodelist.status table
|
|
if ($check) {
|
|
updateNodeStatus($handlednodes, \@allerrornodes);
|
|
}
|
|
|
|
Time::HiRes::sleep(0.1);
|
|
}
|
|
###################################
|
|
# sleep between connects to same
|
|
# HMC/IVM so as not to overwelm it
|
|
###################################
|
|
if ( $hw ne @$_[0] ) {
|
|
$sessions = 1;
|
|
} elsif ( $sessions++ >= $request->{maxssh} ) {
|
|
sleep(1);
|
|
$sessions = 1;
|
|
}
|
|
$hw = @$_[0];
|
|
|
|
my ($pipe) = fork_cmd( @$_[0], @$_[1], $request );
|
|
if ( $pipe ) {
|
|
$fds->add( $pipe );
|
|
$children++;
|
|
}
|
|
}
|
|
}
|
|
|
|
ENDOFFORK:
|
|
#######################################
|
|
# Process responses from children
|
|
#######################################
|
|
while ( $fds->count > 0 or $children > 0 ) {
|
|
my $handlednodes={};
|
|
child_response( $callback, $fds, $handlednodes);
|
|
|
|
#update the node status to the nodelist.status table
|
|
if ($check) {
|
|
updateNodeStatus($handlednodes, \@allerrornodes);
|
|
}
|
|
|
|
Time::HiRes::sleep(0.1);
|
|
}
|
|
|
|
#drain one more time
|
|
my $rc=1;
|
|
while ( $rc>0 ) {
|
|
my $handlednodes={};
|
|
$rc=child_response( $callback, $fds, $handlednodes);
|
|
#update the node status to the nodelist.status table
|
|
if ($check) {
|
|
updateNodeStatus($handlednodes, \@allerrornodes);
|
|
}
|
|
}
|
|
|
|
if ( exists( $request->{verbose} )) {
|
|
my $elapsed = Time::HiRes::gettimeofday() - $start;
|
|
my $msg = sprintf( "Total Elapsed Time: %.3f sec\n", $elapsed );
|
|
trace( $request, $msg );
|
|
}
|
|
|
|
if ($check) {
|
|
#print "allerrornodes=@allerrornodes\n";
|
|
#revert the status back for there is no-op for the nodes
|
|
my %old=();
|
|
foreach my $node (@allerrornodes) {
|
|
my $stat=$oldnodestatus{$node};
|
|
if (exists($old{$stat})) {
|
|
my $pa=$old{$stat};
|
|
push(@$pa, $node);
|
|
}
|
|
else {
|
|
$old{$stat}=[$node];
|
|
}
|
|
}
|
|
xCAT_monitoring::monitorctrl::setNodeStatusAttributes(\%old, 1);
|
|
}
|
|
|
|
|
|
return(0);
|
|
}
|
|
|
|
##########################################################################
|
|
# updateNodeStatus
|
|
##########################################################################
|
|
sub updateNodeStatus {
|
|
my $handlednodes=shift;
|
|
my $allerrornodes=shift;
|
|
foreach my $node (keys(%$handlednodes)) {
|
|
if ($handlednodes->{$node} == -1) { push(@$allerrornodes, $node); }
|
|
}
|
|
}
|
|
|
|
##########################################################################
|
|
# Verbose mode (-V)
|
|
##########################################################################
|
|
sub trace {
|
|
|
|
my $request = shift;
|
|
my $msg = shift;
|
|
|
|
my ($sec,$min,$hour,$mday,$mon,$yr,$wday,$yday,$dst) = localtime(time);
|
|
my $formatted = sprintf "%02d:%02d:%02d %5d %s", $hour,$min,$sec,$$,$msg;
|
|
|
|
my $callback = $request->{callback};
|
|
$callback->( {data=>[$formatted]} );
|
|
}
|
|
|
|
|
|
##########################################################################
|
|
# Send response from child process back to xCAT client
|
|
##########################################################################
|
|
sub child_response {
|
|
|
|
my $callback = shift;
|
|
my $fds = shift;
|
|
my $errornodes=shift;
|
|
my @ready_fds = $fds->can_read(1);
|
|
my $rc = @ready_fds;
|
|
|
|
foreach my $rfh (@ready_fds) {
|
|
my $data = <$rfh>;
|
|
|
|
#################################
|
|
# Read from child process
|
|
#################################
|
|
if ( defined( $data )) {
|
|
while ($data !~ /ENDOFFREEZE6sK4ci/) {
|
|
$data .= <$rfh>;
|
|
}
|
|
my $responses = thaw($data);
|
|
foreach ( @$responses ) {
|
|
#save the nodes that has errors for node status monitoring
|
|
if ((exists($_->{errorcode})) && ($_->{errorcode} != 0)) {
|
|
if ($errornodes) { $errornodes->{$_->{node}->[0]->{name}->[0]}=-1; }
|
|
} else {
|
|
if ($errornodes) { $errornodes->{$_->{node}->[0]->{name}->[0]}=1; }
|
|
}
|
|
$callback->( $_ );
|
|
}
|
|
next;
|
|
}
|
|
#################################
|
|
# Done - close handle
|
|
#################################
|
|
$fds->remove($rfh);
|
|
close($rfh);
|
|
}
|
|
yield; #Try to avoid useless iterations as much as possible
|
|
return $rc;
|
|
}
|
|
|
|
|
|
##########################################################################
|
|
# Finds attributes for given node is various databases
|
|
##########################################################################
|
|
sub resolve_hcp {
|
|
my $request = shift;
|
|
my $noderange = shift;
|
|
my @nodegroup = ();
|
|
my $tab = ($request->{hwtype} eq "fsp" or $request->{hwtype} eq "bpa") ? "ppcdirect" : "ppchcp";
|
|
my $db = xCAT::Table->new( $tab );
|
|
|
|
####################################
|
|
# Database not defined
|
|
####################################
|
|
if ( !defined( $db )) {
|
|
send_msg( $request, 1, sprintf( $errmsg{DB_UNDEF}, $tab ));
|
|
return undef;
|
|
}
|
|
####################################
|
|
# Process each node
|
|
####################################
|
|
foreach my $hcp ( @$noderange ) {
|
|
# my ($ent) = $db->getAttribs( {hcp=>$hcp},"hcp" );
|
|
# my ($ent) = $db->getNodeAttribs( $hcp, ["hcp"]);
|
|
|
|
# if ( !defined( $ent )) {
|
|
# my $msg = sprintf( "$hcp: $errmsg{NODE_UNDEF}", $tab );
|
|
# send_msg( $request, 1, $msg );
|
|
# next;
|
|
# }
|
|
################################
|
|
# Get userid and password
|
|
################################
|
|
my @cred = xCAT::PPCdb::credentials( $hcp, $request->{hwtype} );
|
|
$request->{$hcp}{cred} = \@cred;
|
|
|
|
################################
|
|
# Save values
|
|
################################
|
|
push @nodegroup,[$hcp];
|
|
}
|
|
return( \@nodegroup );
|
|
|
|
}
|
|
|
|
|
|
##########################################################################
|
|
# Group nodes depending on command
|
|
##########################################################################
|
|
sub preprocess_nodes {
|
|
|
|
my $request = shift;
|
|
my $noderange = $request->{node};
|
|
my $method = $request->{method};
|
|
my %nodehash = ();
|
|
my @nodegroup = ();
|
|
my %hcpgroup = ();
|
|
my %tabs = ();
|
|
my $netwk;
|
|
|
|
########################################
|
|
# Special cases
|
|
# rscan - Nodes are hardware control pts
|
|
# FSPpower, FSPinv and FSPrflash
|
|
########################################
|
|
if (( !$request->{hcp} && ($request->{hcp} ne "hmc" ))
|
|
and ($request->{command} !~ /^renergy$/)
|
|
and (( $request->{command} =~ /^(rscan|rspconfig)$/ )
|
|
or ($request->{hwtype} eq "fsp" or $request->{hwtype} eq "bpa" )
|
|
or ($request->{command} eq 'lshwconn' and $request->{nodetype} eq 'hmc')) and ($request->{fsp_api} != 1)
|
|
) {
|
|
my $result = resolve_hcp( $request, $noderange );
|
|
return( $result );
|
|
}
|
|
##########################################
|
|
# Special processing - rnetboot
|
|
##########################################
|
|
if ( $request->{command} eq "rnetboot" ) {
|
|
$netwk = resolve_netwk( $request, $noderange );
|
|
if ( !defined( %$netwk )) {
|
|
return undef;
|
|
}
|
|
}
|
|
##########################################
|
|
# Open databases needed
|
|
##########################################
|
|
foreach ( qw(ppc vpd nodetype) ) {
|
|
$tabs{$_} = xCAT::Table->new($_);
|
|
|
|
if ( !exists( $tabs{$_} )) {
|
|
send_msg( $request, 1, sprintf( $errmsg{DB_UNDEF}, $_ ));
|
|
return undef;
|
|
}
|
|
}
|
|
|
|
##########################################
|
|
# Group nodes
|
|
##########################################
|
|
foreach my $node ( @$noderange ) {
|
|
my $d = resolve( $request, $node, \%tabs );
|
|
|
|
######################################
|
|
# Error locating node attributes
|
|
######################################
|
|
if ( ref($d) ne 'ARRAY' ) {
|
|
send_msg( $request, 1, "$node: $d");
|
|
next;
|
|
}
|
|
######################################
|
|
# Get data values
|
|
######################################
|
|
my $hcp = @$d[3];
|
|
my $mtms = @$d[2];
|
|
|
|
######################################
|
|
# Special case for mkhwconn
|
|
######################################
|
|
if ( $request->{command} eq "mkhwconn" and
|
|
exists $request->{opt}->{p})
|
|
{
|
|
$nodehash{ $request->{opt}->{p}}{$mtms}{$node} = $d;
|
|
}
|
|
######################################
|
|
#The common case
|
|
######################################
|
|
else
|
|
{
|
|
$nodehash{$hcp}{$mtms}{$node} = $d;
|
|
}
|
|
}
|
|
|
|
##########################################
|
|
# Get userid and password
|
|
##########################################
|
|
while (my ($hcp,$hash) = each(%nodehash) ) {
|
|
my @cred;
|
|
if ($request->{hcp} && ($request->{hcp} eq "hmc" )) {
|
|
@cred = xCAT::PPCdb::credentials( $hcp, $request->{hcp} );
|
|
} else {
|
|
@cred = xCAT::PPCdb::credentials( $hcp, $request->{hwtype} );
|
|
}
|
|
$request->{$hcp}{cred} = \@cred;
|
|
}
|
|
##########################################
|
|
# Group the nodes - we will fork one
|
|
# process per nodegroup array element.
|
|
##########################################
|
|
|
|
##########################################
|
|
# These commands are grouped on an
|
|
# LPAR-by-LPAR basis - fork one process
|
|
# per LPAR.
|
|
##########################################
|
|
if ( ($method =~ /^(getmacs)$/ && exists( $request->{opt}->{D} )) || ($method =~ /^(rnetboot)$/) ) {
|
|
while (my ($hcp,$hash) = each(%nodehash) ) {
|
|
@nodegroup = ();
|
|
while (my ($mtms,$h) = each(%$hash) ) {
|
|
while (my ($lpar,$d) = each(%$h)) {
|
|
push @$d, $lpar;
|
|
|
|
##########################
|
|
# Save network info
|
|
##########################
|
|
if ( $method =~ /^rnetboot$/ ) {
|
|
push @$d, $netwk->{$lpar};
|
|
}
|
|
push @nodegroup,[$hcp,$d];
|
|
}
|
|
}
|
|
$hcpgroup{$hcp}{'nodegroup'} = [@nodegroup];
|
|
$hcpgroup{$hcp}{'count'} = $#nodegroup + 1;
|
|
$hcpgroup{$hcp}{'runprocess'} = 0;
|
|
$hcpgroup{$hcp}{'index'} = 0;
|
|
}
|
|
return( \%hcpgroup );
|
|
}
|
|
|
|
elsif ( $method =~ /^(getmacs)$/ && exists( $request->{opt}->{arp} ) ) {
|
|
return( $noderange );
|
|
}
|
|
|
|
##########################################
|
|
# Power control commands are grouped
|
|
# by CEC which is the smallest entity
|
|
# that commands can be sent to in parallel.
|
|
# If commands are sent in parallel to a
|
|
# single CEC, the CEC itself will serialize
|
|
# them - fork one process per CEC.
|
|
##########################################
|
|
elsif ( $method =~ /^powercmd/ || $method =~ /^renergy/ ) {
|
|
while (my ($hcp,$hash) = each(%nodehash) ) {
|
|
while (my ($mtms,$h) = each(%$hash) ) {
|
|
push @nodegroup,[$hcp,$h];
|
|
}
|
|
}
|
|
return( \@nodegroup );
|
|
}
|
|
##########################################
|
|
# All other commands are grouped by
|
|
# hardware control point - fork one
|
|
# process per hardware control point.
|
|
##########################################
|
|
while (my ($hcp,$hash) = each(%nodehash) ) {
|
|
push @nodegroup,[$hcp,$hash];
|
|
}
|
|
return( \@nodegroup );
|
|
}
|
|
|
|
|
|
##########################################################################
|
|
# Finds attributes for given node is various databases
|
|
##########################################################################
|
|
sub resolve_netwk {
|
|
|
|
my $request = shift;
|
|
my $noderange = shift;
|
|
my %nethash = xCAT::DBobjUtils->getNetwkInfo( $noderange );
|
|
my $tab = xCAT::Table->new( 'mac' );
|
|
my %result = ();
|
|
my $ip;
|
|
|
|
#####################################
|
|
# Network attributes undefined
|
|
#####################################
|
|
if ( !%nethash ) {
|
|
send_msg( $request,1,sprintf( $errmsg{NODE_UNDEF}, "networks" ));
|
|
return undef;
|
|
}
|
|
#####################################
|
|
# mac database undefined
|
|
#####################################
|
|
if ( !defined( $tab )) {
|
|
send_msg( $request, 1, sprintf( $errmsg{DB_UNDEF}, "mac" ));
|
|
return undef;
|
|
}
|
|
|
|
foreach ( @$noderange ) {
|
|
#################################
|
|
# Get gateway (-G)
|
|
#################################
|
|
if ( !exists( $nethash{$_} )) {
|
|
my $msg = sprintf( "$_: $errmsg{NODE_UNDEF}", "networks");
|
|
send_msg( $request, 1, $msg );
|
|
next;
|
|
}
|
|
my $gateway = $nethash{$_}{gateway};
|
|
if ( !defined( $gateway )) {
|
|
my $msg = sprintf("$_: $errmsg{NO_ATTR}","gateway","networks");
|
|
send_msg( $request, 1, $msg );
|
|
next;
|
|
}
|
|
$ip = xCAT::Utils::toIP( $gateway );
|
|
if ( @$ip[0] != 0 ) {
|
|
send_msg( $request, 1, "$_: Cannot resolve '$gateway'" );
|
|
next;
|
|
}
|
|
my $gateway_ip = @$ip[1];
|
|
|
|
#################################
|
|
# Get server (-S)
|
|
#################################
|
|
my $server = xCAT::Utils->GetMasterNodeName( $_ );
|
|
if ( $server == 1 ) {
|
|
send_msg( $request, 1, "$_: Unable to identify master" );
|
|
next;
|
|
}
|
|
$ip = xCAT::Utils::toIP( $server );
|
|
if ( @$ip[0] != 0 ) {
|
|
send_msg( $request, 1, "$_: Cannot resolve '$server'" );
|
|
next;
|
|
}
|
|
my $server_ip = @$ip[1];
|
|
|
|
#################################
|
|
# Get client (-C)
|
|
#################################
|
|
$ip = xCAT::Utils::toIP( $_ );
|
|
if ( @$ip[0] != 0 ) {
|
|
send_msg( $request, 1, "$_: Cannot resolve '$_'" );
|
|
next;
|
|
}
|
|
my $client_ip = @$ip[1];
|
|
|
|
#################################
|
|
# Get mac-address (-m)
|
|
#################################
|
|
my ($ent) = $tab->getNodeAttribs( $_, ['mac'] );
|
|
if ( !defined($ent) ) {
|
|
my $msg = sprintf( "$_: $errmsg{NO_ATTR}","mac","mac");
|
|
send_msg( $request, 1, $msg );
|
|
next;
|
|
}
|
|
#################################
|
|
# Save results
|
|
#################################
|
|
$result{$_}{gateway} = $gateway_ip;
|
|
$result{$_}{server} = $server_ip;
|
|
$result{$_}{client} = $client_ip;
|
|
$result{$_}{mac} = $ent->{mac};
|
|
}
|
|
return( \%result );
|
|
}
|
|
|
|
|
|
##########################################################################
|
|
# Finds attributes for given node is various databases
|
|
##########################################################################
|
|
sub resolve {
|
|
|
|
my $request = shift;
|
|
my $node = shift;
|
|
my $tabs = shift;
|
|
my @attribs = qw(id pprofile parent hcp);
|
|
my @values = ();
|
|
|
|
#################################
|
|
# Get node type
|
|
#################################
|
|
my $ent = $tabs->{nodetype}->getNodeAttribs($node,[qw(nodetype node)]);
|
|
if ( !defined( $ent )) {
|
|
return( sprintf( $errmsg{NODE_UNDEF}, "nodetype" ));
|
|
}
|
|
#################################
|
|
# Check for type
|
|
#################################
|
|
if ( !exists( $ent->{nodetype} )) {
|
|
return( sprintf( $errmsg{NO_ATTR}, "nodetype","nodetype" ));
|
|
}
|
|
#################################
|
|
# Check for valid "type"
|
|
#################################
|
|
my ($type) = grep(
|
|
/^$::NODETYPE_LPAR|$::NODETYPE_OSI|$::NODETYPE_BPA|$::NODETYPE_FSP$/,
|
|
split /,/, $ent->{nodetype} );
|
|
|
|
if ( !defined( $type )) {
|
|
return( "Invalid node type: $ent->{nodetype}" );
|
|
}
|
|
#################################
|
|
# Get attributes
|
|
#################################
|
|
my ($att) = $tabs->{ppc}->getNodeAttribs( $node, \@attribs );
|
|
|
|
if ( !defined( $att )) {
|
|
return( sprintf( $errmsg{NODE_UNDEF}, "ppc" ));
|
|
}
|
|
#################################
|
|
# Special lpar processing
|
|
#################################
|
|
if ( $type =~ /^$::NODETYPE_OSI|$::NODETYPE_LPAR$/ ) {
|
|
$att->{bpa} = 0;
|
|
$att->{type} = "lpar";
|
|
$att->{node} = $att->{parent};
|
|
|
|
if ( !exists( $att->{parent} )) {
|
|
return( sprintf( $errmsg{NO_ATTR}, "parent", "ppc" ));
|
|
}
|
|
#############################
|
|
# Get BPA (if any)
|
|
#############################
|
|
if (( $request->{command} eq "rvitals" ) &&
|
|
( $request->{method} =~ /^all|temp$/ )) {
|
|
my ($ent) = $tabs->{ppc}->getNodeAttribs( $att->{parent},['parent']);
|
|
|
|
#############################
|
|
# Find MTMS in vpd database
|
|
#############################
|
|
if (( defined( $ent )) && exists( $ent->{parent} )) {
|
|
my @attrs = qw(mtm serial);
|
|
my ($vpd) = $tabs->{vpd}->getNodeAttribs($ent->{parent},\@attrs);
|
|
|
|
########################
|
|
# Verify attributes
|
|
########################
|
|
foreach ( @attrs ) {
|
|
if ( !defined( $vpd ) || !exists( $vpd->{$_} )) {
|
|
return( sprintf( $errmsg{NO_UNDEF}, $_, "vpd", $ent->{parent} ));
|
|
}
|
|
}
|
|
$att->{bpa} = "$vpd->{mtm}*$vpd->{serial}";
|
|
}
|
|
}
|
|
}
|
|
#################################
|
|
# Optional and N/A fields
|
|
#################################
|
|
elsif ( $type =~ /^$::NODETYPE_FSP$/ ) {
|
|
$att->{pprofile} = 0;
|
|
$att->{id} = 0;
|
|
$att->{fsp} = 0;
|
|
$att->{node} = $node;
|
|
$att->{type} = $type;
|
|
$att->{parent} = exists($att->{parent}) ? $att->{parent} : 0;
|
|
$att->{bpa} = $att->{parent};
|
|
}
|
|
elsif ( $type =~ /^$::NODETYPE_BPA$/ ) {
|
|
$att->{pprofile} = 0;
|
|
$att->{id} = 0;
|
|
$att->{bpa} = 0;
|
|
$att->{parent} = 0;
|
|
$att->{fsp} = 0;
|
|
$att->{node} = $node;
|
|
$att->{type} = $type;
|
|
}
|
|
#################################
|
|
# Find MTMS in vpd database
|
|
#################################
|
|
my @attrs = qw(mtm serial);
|
|
my ($vpd) = $tabs->{vpd}->getNodeAttribs($att->{node}, \@attrs );
|
|
|
|
if ( !defined( $vpd )) {
|
|
return( sprintf( $errmsg{NODE_UNDEF}, "vpd: ($att->{node})" ));
|
|
}
|
|
################################
|
|
# Verify both vpd attributes
|
|
################################
|
|
foreach ( @attrs ) {
|
|
if ( !exists( $vpd->{$_} )) {
|
|
return( sprintf( $errmsg{NO_ATTR}, $_, "vpd: ($att->{node})" ));
|
|
}
|
|
}
|
|
$att->{fsp} = "$vpd->{mtm}*$vpd->{serial}";
|
|
|
|
#################################
|
|
# Verify required attributes
|
|
#################################
|
|
foreach my $at ( @attribs ) {
|
|
if ( !exists( $att->{$at} )) {
|
|
return( sprintf( $errmsg{NO_ATTR}, $at, "ppc" ));
|
|
}
|
|
}
|
|
#################################
|
|
# Build array of data
|
|
#################################
|
|
foreach ( qw(id pprofile fsp hcp type bpa) ) {
|
|
push @values, $att->{$_};
|
|
}
|
|
return( \@values );
|
|
}
|
|
|
|
|
|
|
|
##########################################################################
|
|
# Forks a process to run the ssh command
|
|
##########################################################################
|
|
sub fork_cmd {
|
|
|
|
my $host = shift;
|
|
my $nodes = shift;
|
|
my $request = shift;
|
|
|
|
#######################################
|
|
# Pipe childs output back to parent
|
|
#######################################
|
|
my $parent;
|
|
my $child;
|
|
pipe $parent, $child;
|
|
my $pid = xCAT::Utils->xfork;
|
|
|
|
if ( !defined($pid) ) {
|
|
###################################
|
|
# Fork error
|
|
###################################
|
|
send_msg( $request, 1, "Fork error: $!" );
|
|
return undef;
|
|
}
|
|
elsif ( $pid == 0 ) {
|
|
###################################
|
|
# Child process
|
|
###################################
|
|
close( $parent );
|
|
$request->{pipe} = $child;
|
|
|
|
invoke_cmd( $host, $nodes, $request );
|
|
exit(0);
|
|
}
|
|
else {
|
|
###################################
|
|
# Parent process
|
|
###################################
|
|
close( $child );
|
|
return( $parent, $pid );
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
##########################################################################
|
|
# Run the command, process the response, and send to parent
|
|
##########################################################################
|
|
sub invoke_cmd {
|
|
|
|
my $host = shift;
|
|
my $nodes = shift;
|
|
my $request = shift;
|
|
my $hwtype = $request->{hwtype};
|
|
my $verbose = $request->{verbose};
|
|
my $cmd = $request->{command};
|
|
my $power = $request->{hcp};
|
|
my @exp;
|
|
my $verbose_log;
|
|
my @outhash;
|
|
|
|
########################################
|
|
# If the request command is renergy, just
|
|
# uses the xCAT CIM Client to handle it
|
|
########################################
|
|
if ( $request->{command} eq "renergy" ) {
|
|
my $result = &runcmd($request, $host, $nodes);
|
|
|
|
########################################
|
|
# Format and send back to parent
|
|
########################################
|
|
foreach my $line ( @$result ) {
|
|
my %output;
|
|
$output{node}->[0]->{name}->[0] = @$line[0];
|
|
$output{node}->[0]->{data}->[0]->{contents}->[0] = @$line[1];
|
|
$output{errorcode} = @$line[2];
|
|
push @outhash, \%output;
|
|
}
|
|
my $out = $request->{pipe};
|
|
print $out freeze( [@outhash] );
|
|
print $out "\nENDOFFREEZE6sK4ci\n";
|
|
|
|
return;
|
|
}
|
|
|
|
########################################
|
|
# Direct-attached FSP handler
|
|
########################################
|
|
if ( ($power ne "hmc") && ( $hwtype eq "fsp" or $hwtype eq "bpa") && $request->{fsp_api} == 0) {
|
|
|
|
####################################
|
|
# Dynamically load FSP module
|
|
####################################
|
|
eval { require xCAT::PPCfsp };
|
|
if ( $@ ) {
|
|
send_msg( $request, 1, $@ );
|
|
return;
|
|
}
|
|
my @exp = xCAT::PPCfsp::connect( $request, $host );
|
|
|
|
####################################
|
|
# Error connecting
|
|
####################################
|
|
if ( ref($exp[0]) ne "LWP::UserAgent" ) {
|
|
send_msg( $request, 1, $exp[0] );
|
|
return;
|
|
}
|
|
my $result = xCAT::PPCfsp::handler( $host, $request, \@exp );
|
|
|
|
####################################
|
|
# Output verbose Perl::LWP
|
|
####################################
|
|
if ( $verbose ) {
|
|
$verbose_log = $exp[3];
|
|
|
|
my %output;
|
|
$output{data} = [$$verbose_log];
|
|
unshift @$result, \%output;
|
|
}
|
|
my $out = $request->{pipe};
|
|
print $out freeze( $result );
|
|
print $out "\nENDOFFREEZE6sK4ci\n";
|
|
return;
|
|
}
|
|
|
|
########################################
|
|
# HMC and IVM-managed handler
|
|
# Connect to list of remote servers
|
|
########################################
|
|
foreach ( split /,/, $host ) {
|
|
if ( $power ne "hmc" ) {
|
|
@exp = xCAT::PPCcli::connect( $request, $hwtype, $_ );
|
|
} else {
|
|
@exp = xCAT::PPCcli::connect( $request, $power, $_);
|
|
}
|
|
####################################
|
|
# Successfully connected
|
|
####################################
|
|
if ( ref($exp[0]) eq "Expect" ) {
|
|
last;
|
|
}
|
|
}
|
|
########################################
|
|
# Error connecting
|
|
########################################
|
|
if ( ref($exp[0]) ne "Expect" ) {
|
|
send_msg( $request, 1, $exp[0] );
|
|
return;
|
|
}
|
|
########################################
|
|
# Process specific command
|
|
########################################
|
|
my $result = runcmd( $request, $nodes, \@exp );
|
|
|
|
########################################
|
|
# Close connection to remote server
|
|
########################################
|
|
xCAT::PPCcli::disconnect( \@exp );
|
|
|
|
########################################
|
|
# Get verbose Expect output
|
|
########################################
|
|
if ( $verbose ) {
|
|
$verbose_log = $exp[6];
|
|
}
|
|
########################################
|
|
# Return error
|
|
########################################
|
|
if ( ref($result) ne 'ARRAY' ) {
|
|
send_msg( $request, 1, $$verbose_log.$result );
|
|
return;
|
|
}
|
|
########################################
|
|
# Prepend verbose output
|
|
########################################
|
|
if ( defined( $verbose_log )) {
|
|
my %output;
|
|
$output{data} = [$$verbose_log];
|
|
push @outhash, \%output;
|
|
}
|
|
########################################
|
|
# Send result back to parent process
|
|
########################################
|
|
if ( @$result[0] eq "FORMATDATA6sK4ci" ) {
|
|
my $out = $request->{pipe};
|
|
|
|
push @outhash, @$result[1];
|
|
print $out freeze( [@outhash] );
|
|
print $out "\nENDOFFREEZE6sK4ci\n";
|
|
return;
|
|
}
|
|
########################################
|
|
# Format and send back to parent
|
|
########################################
|
|
foreach ( @$result ) {
|
|
my %output;
|
|
$output{node}->[0]->{name}->[0] = @$_[0];
|
|
$output{node}->[0]->{data}->[0]->{contents}->[0] = @$_[1];
|
|
$output{errorcode} = @$_[2];
|
|
push @outhash, \%output;
|
|
}
|
|
my $out = $request->{pipe};
|
|
print $out freeze( [@outhash] );
|
|
print $out "\nENDOFFREEZE6sK4ci\n";
|
|
}
|
|
|
|
|
|
##########################################################################
|
|
# Run the command method specified
|
|
##########################################################################
|
|
sub runcmd {
|
|
|
|
my $request = shift;
|
|
my $cmd = $request->{command};
|
|
my $method = $request->{method};
|
|
my $hwtype = $request->{hwtype};
|
|
#my $modname = $modules{$cmd};
|
|
my $modname = $modules{$cmd}{$hwtype};
|
|
|
|
######################################
|
|
# Command not supported
|
|
######################################
|
|
if ( !defined( $modname )) {
|
|
return( ["$cmd not a supported command by $hwtype method"] );
|
|
}
|
|
######################################
|
|
# Load specific module
|
|
######################################
|
|
eval "require $modname";
|
|
if ( $@ ) {
|
|
return( [$@] );
|
|
}
|
|
######################################
|
|
# Invoke method
|
|
######################################
|
|
no strict 'refs';
|
|
my $result = ${$modname."::"}{$method}->($request,@_);
|
|
use strict;
|
|
|
|
return( $result );
|
|
|
|
}
|
|
|
|
##########################################################################
|
|
# Pre-process request from xCat daemon. Send the request to the the service
|
|
# nodes of the HCPs.
|
|
##########################################################################
|
|
sub preprocess_request {
|
|
|
|
my $package = shift;
|
|
my $req = shift;
|
|
#if ($req->{_xcatdest}) { return [$req]; } #exit if preprocessed
|
|
if ($req->{_xcatpreprocessed}->[0] == 1 ) { return [$req]; }
|
|
my $callback = shift;
|
|
my @requests;
|
|
|
|
#####################################
|
|
# Parse arguments
|
|
#####################################
|
|
my $opt = parse_args($package, $req, $callback);
|
|
if ( ref($opt) eq 'ARRAY' )
|
|
{
|
|
send_msg( $req, 1, @$opt );
|
|
return(1);
|
|
}
|
|
|
|
$req->{opt} = $opt;
|
|
|
|
####################################
|
|
# Get hwtype
|
|
####################################
|
|
$package =~ s/xCAT_plugin:://;
|
|
|
|
####################################
|
|
# Prompt for usage if needed and on MN
|
|
####################################
|
|
my $noderange = $req->{node}; #Should be arrayref
|
|
my $command = $req->{command}->[0];
|
|
my $extrargs = $req->{arg};
|
|
my @exargs=($req->{arg});
|
|
if (ref($extrargs)) {
|
|
@exargs=@$extrargs;
|
|
}
|
|
if ($ENV{'XCATBYPASS'}){
|
|
my $usage_string=xCAT::Usage->parseCommand($command, @exargs);
|
|
if ($usage_string) {
|
|
$callback->({data=>[$usage_string]});
|
|
$req = {};
|
|
return ;
|
|
}
|
|
if (!$noderange) {
|
|
$usage_string="Missing noderange";
|
|
$callback->({data=>[$usage_string]});
|
|
$req = {};
|
|
return ;
|
|
}
|
|
}
|
|
|
|
|
|
##################################################################
|
|
# get the HCPs for the LPARs in order to figure out which service
|
|
# nodes to send the requests to
|
|
###################################################################
|
|
my $hcptab_name = ($package eq "fsp" or $package eq "bpa") ? "ppcdirect" : "ppchcp";
|
|
my $hcptab = xCAT::Table->new( $hcptab_name );
|
|
unless ($hcptab ) {
|
|
$callback->({data=>["Cannot open $hcptab_name table"]});
|
|
$req = {};
|
|
return;
|
|
}
|
|
# Check if each node is hcp
|
|
my %hcp_hash=();
|
|
my @missednodes=();
|
|
foreach ( @$noderange ) {
|
|
my ($ent) = $hcptab->getAttribs( {hcp=>$_},"hcp" );
|
|
if ( !defined( $ent )) {
|
|
push @missednodes, $_;
|
|
next;
|
|
}
|
|
push @{$hcp_hash{$_}{nodes}}, $_;
|
|
}
|
|
|
|
#check if the left-over nodes are lpars
|
|
if (@missednodes > 0) {
|
|
my $ppctab = xCAT::Table->new("ppc");
|
|
unless ($ppctab) {
|
|
$callback->({data=>["Cannot open ppc table"]});
|
|
$req = {};
|
|
return;
|
|
}
|
|
foreach my $node (@missednodes) {
|
|
my $ent=$ppctab->getNodeAttribs($node,['hcp']);
|
|
if (defined($ent->{hcp})) { push @{$hcp_hash{$ent->{hcp}}{nodes}}, $node;}
|
|
else {
|
|
$callback->({data=>["The node $node is neither a hcp nor an lpar"]});
|
|
$req = {};
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
# find service nodes for the HCPs
|
|
# build an individual request for each service node
|
|
my $service = "xcat";
|
|
my @hcps=keys(%hcp_hash);
|
|
my $sn = xCAT::Utils->get_ServiceNode(\@hcps, $service, "MN");
|
|
|
|
# build each request for each service node
|
|
foreach my $snkey (keys %$sn)
|
|
{
|
|
#$callback->({data=>["The service node $snkey "]});
|
|
my $reqcopy = {%$req};
|
|
$reqcopy->{'_xcatdest'} = $snkey;
|
|
$reqcopy->{_xcatpreprocessed}->[0] = 1;
|
|
my $hcps1=$sn->{$snkey};
|
|
my @nodes=();
|
|
foreach (@$hcps1) {
|
|
push @nodes, @{$hcp_hash{$_}{nodes}};
|
|
}
|
|
$reqcopy->{node} = \@nodes;
|
|
#print "nodes=@nodes\n";
|
|
push @requests, $reqcopy;
|
|
}
|
|
return \@requests;
|
|
}
|
|
####################################
|
|
# Parse arguments
|
|
####################################
|
|
sub parse_args
|
|
{
|
|
my $package = shift;
|
|
my $req = shift;
|
|
my $callback= shift;
|
|
$package =~ s/xCAT_plugin:://;
|
|
# if ( exists $req->{opt})
|
|
# {
|
|
# return $req->{opt};
|
|
# }
|
|
|
|
#################################
|
|
# To match the old logic
|
|
##################################
|
|
my $command = $req->{command}->[0];
|
|
my $stdin = $req->{stdin}->[0];
|
|
$req->{command} = $command;
|
|
$req->{stdin} = $stdin;
|
|
$req->{hwtype} = $package;
|
|
$req->{callback} = $callback;
|
|
$req->{method} = "parse_args";
|
|
|
|
my $opt = runcmd( $req);
|
|
|
|
$req->{command} = [ $command];
|
|
$req->{stdin} = [ $stdin];
|
|
return $opt;
|
|
}
|
|
|
|
|
|
##########################################################################
|
|
# Process request from xCat daemon
|
|
##########################################################################
|
|
sub process_request {
|
|
|
|
my $package = shift;
|
|
my $req = shift;
|
|
my $callback = shift;
|
|
my $subreq = shift;
|
|
|
|
####################################
|
|
# Get hwtype
|
|
####################################
|
|
$package =~ s/xCAT_plugin:://;
|
|
|
|
####################################
|
|
# Build hash to pass around
|
|
####################################
|
|
my $request = {%$req};
|
|
$request->{command} = $req->{command}->[0];
|
|
$request->{stdin} = $req->{stdin}->[0];
|
|
# $request->{hwtype} = $package;
|
|
$request->{callback}= $callback;
|
|
$request->{subreq} = $subreq;
|
|
#########################
|
|
#This is a special case for rspconfig and mkhwconn,
|
|
#we shouldn't set hwtype as$package. and reserved for other commands.
|
|
#probably for all HW ctrl commands it still true?
|
|
#########################
|
|
if($request->{command} ne "rspconfig" and
|
|
$request->{command} ne "mkhwconn") {
|
|
$request->{hwtype} = $package;
|
|
}
|
|
|
|
####################################
|
|
# Option -V for verbose output
|
|
####################################
|
|
if ( exists( $request->{opt}->{V} )) {
|
|
$request->{verbose} = 1;
|
|
}
|
|
####################################
|
|
# Process remote command
|
|
####################################
|
|
process_command( $request );
|
|
}
|
|
|
|
##########################################################################
|
|
# connect hmc via ssh and execute remote command
|
|
##########################################################################
|
|
sub sshcmds_on_hmc
|
|
{
|
|
my $ip = shift;
|
|
my $user = shift;
|
|
my $password = shift;
|
|
my @cmds = @_;
|
|
|
|
my %handled;
|
|
my @data;
|
|
my @exp;
|
|
for my $cmd (@cmds)
|
|
{
|
|
if ( $cmd =~ /(.+?)=(.*)/)
|
|
{
|
|
my ($command,$value) = ($1,$2);
|
|
$handled{$command} = $value;
|
|
}
|
|
}
|
|
my %request = (
|
|
ppcretry => 1,
|
|
verbose => 0,
|
|
ppcmaxp => 64,
|
|
ppctimeout => 0,
|
|
fsptimeout => 0,
|
|
ppcretry => 3,
|
|
maxssh => 8
|
|
|
|
);
|
|
|
|
my $valid_ip;
|
|
foreach my $individual_ip ( split /,/, $ip ) {
|
|
################################
|
|
# Get userid and password
|
|
################################
|
|
my @cred = xCAT::PPCdb::credentials( $individual_ip, "hmc" );
|
|
$request{$individual_ip}{cred} = \@cred;
|
|
|
|
@exp = xCAT::PPCcli::connect( \%request, 'hmc', $individual_ip);
|
|
####################################
|
|
# Successfully connected
|
|
####################################
|
|
if ( ref($exp[0]) eq "Expect" ) {
|
|
$valid_ip = $individual_ip;
|
|
last;
|
|
}
|
|
}
|
|
|
|
########################################
|
|
# Error connecting
|
|
########################################
|
|
if ( ref($exp[0]) ne "Expect" ) {
|
|
return ([1,@cmds]);
|
|
}
|
|
########################################
|
|
# Process specific command
|
|
########################################
|
|
for my $cmd ( keys %handled)
|
|
{
|
|
my $result;
|
|
if ($cmd eq 'network_reset')
|
|
{
|
|
$result = xCAT::PPCcli::network_reset( \@exp, $valid_ip, $handled{$cmd});
|
|
my $RC = shift( @$result);
|
|
}
|
|
push @data, @$result[0];
|
|
}
|
|
########################################
|
|
# Close connection to remote server
|
|
########################################
|
|
xCAT::PPCcli::disconnect( \@exp );
|
|
|
|
return ([0, undef, \@data]);
|
|
}
|
|
|
|
##########################################################################
|
|
# logon asm and update configuration
|
|
##########################################################################
|
|
sub updconf_in_asm
|
|
{
|
|
my $ip = shift;
|
|
my $target_dev = shift;
|
|
my @cmds = @_;
|
|
|
|
eval { require xCAT::PPCfsp };
|
|
if ( $@ ) {
|
|
return ([1,@cmds]);
|
|
}
|
|
|
|
my %handled;
|
|
for my $cmd (@cmds)
|
|
{
|
|
if ( $cmd =~ /(.+?)=(.*)/)
|
|
{
|
|
my ($command,$value) = ($1,$2);
|
|
$handled{$command} = $value;
|
|
}
|
|
}
|
|
|
|
my %request = (
|
|
ppcretry => 1,
|
|
verbose => 0,
|
|
ppcmaxp => 64,
|
|
ppctimeout => 0,
|
|
fsptimeout => 0,
|
|
ppcretry => 3,
|
|
maxssh => 8,
|
|
arg => \@cmds,
|
|
method => \%handled,
|
|
command => 'rspconfig',
|
|
hwtype => lc($target_dev->{'type'}),
|
|
);
|
|
|
|
my $valid_ip;
|
|
my @exp;
|
|
foreach my $individual_ip ( split /,/, $ip ) {
|
|
################################
|
|
# Get userid and password
|
|
################################
|
|
my @cred = xCAT::PPCdb::credentials( $individual_ip, lc($target_dev->{'type'}));
|
|
$request{$individual_ip}{cred} = \@cred;
|
|
$request{node} = [$individual_ip];
|
|
|
|
@exp = xCAT::PPCfsp::connect(\%request, $individual_ip);
|
|
####################################
|
|
# Successfully connected
|
|
####################################
|
|
if ( ref($exp[0]) eq "LWP::UserAgent" ) {
|
|
$valid_ip = $individual_ip;
|
|
last;
|
|
}
|
|
}
|
|
|
|
####################################
|
|
# Error connecting
|
|
####################################
|
|
if ( ref($exp[0]) ne "LWP::UserAgent" ) {
|
|
return ([1,@cmds]);
|
|
}
|
|
my $result = xCAT::PPCfsp::handler( $valid_ip, \%request, \@exp );
|
|
my $RC = shift( @$result);
|
|
my @data;
|
|
push @data, @$result[0];
|
|
|
|
return ([0, undef, \@data]);
|
|
}
|
|
|
|
sub check_fsp_api
|
|
{
|
|
my $request = shift;
|
|
|
|
# my $fsp_api = "/opt/xcat/sbin/fsp-api";
|
|
my $fsp_api = ($::XCATROOT) ? "$::XCATROOT/sbin/fsp-api" : "/opt/xcat/sbin/fsp-api";
|
|
# my $libfsp = "/usr/lib/libfsp.a";
|
|
my $libfsp = "/opt/xcat/lib/libfsp.a";
|
|
# my $libfsp = ($::XCATROOT) ? "$::XCATROOT/lib/libfsp.a" : "/opt/xcat/lib/libfsp.a";
|
|
# my $hw_svr = "/opt/csm/csmbin/hdwr_svr";
|
|
|
|
my $msg = ();
|
|
# if((-e $fsp_api) && (-x $fsp_api)&& (-e $libfsp) && (-e $hw_svr)) {
|
|
if((-e $fsp_api) && (-x $fsp_api)&& (-e $libfsp)) {
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
1;
|
|
|