#!/usr/bin/env perl # # © Copyright 2009 Hewlett-Packard Development Company, L.P. # EPL license http://www.eclipse.org/legal/epl-v10.html # # Revision history: # July, 2010 vallard@sumavi.com comments added. # August, 2009 blade.pm adapted to generate hpblade.pm # package xCAT_plugin::hpblade; BEGIN { $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat'; } use lib "$::XCATROOT/lib/perl"; use strict; use xCAT::Table; use xCAT::Utils; use xCAT::TableUtils; use xCAT::ServiceNodeUtils; use xCAT::Usage; use IO::Socket; use Thread 'yield'; use Storable qw(freeze thaw); use XML::Simple; use Net::SSLeay qw(die_now die_if_ssl_error); use Data::Dumper; use POSIX "WNOHANG"; use Getopt::Long; #use xCAT::hpoa; # require this dynamically below instead sub handled_commands { return { findme => 'blade', getmacs => 'nodehm:getmac,mgt', rscan => 'nodehm:mgt', rpower => 'nodehm:power,mgt', gethpbladecons => 'hpblade', getrvidparms => 'nodehm:mgt', rvitals => 'nodehm:mgt', rinv => 'nodehm:mgt', rbeacon => 'nodehm:mgt', rspreset => 'nodehm:mgt', rspconfig => 'nodehm:mgt', rbootseq => 'nodehm:mgt', reventlog => 'nodehm:mgt', switchblade => 'nodehm:mgt', }; } my $hpoa; my $activeOABay; my $slot; my ($username, $password); my %mm_comm_pids; my %macmap; #Store responses from rinv for discovery my $macmaptimestamp; #reflect freshness of cache my %oahash; my $curn; my $oa; my $getBladeStatusResponse; # Make this a global here so we can re-use the result my $status_noop = "XXXno-opXXX"; my $eventHash; my $globalDebug = 0; my $ctx; my @cfgtext; my %bootdevices = ( 0 => 'IPL_NO_OP', 1 => 'CD', 2 => 'FLOPPY', 3 => 'USB', 4 => 'HDD', 5 => 'PXE_NIC1', 6 => 'PXE_NIC2', 7 => 'PXE_NIC3', 8 => 'PXE_NIC4' ); my %bootnumbers = ( 'none' => 0, 'c' => 1, 'cd' => 1, 'dvd' => 1, 'cdrom' => 1, 'dvdrom' => 1, 'f' => 2, 'floppy' => 2, 'usb' => 3, 'usbflash' => 3, 'flash' => 3, 'h' => 4, 'hd' => 4, 'hdd' => 4, 'hd0' => 4, 'harddisk' => 4, 'eth0' => 5, 'nic1' => 5, 'net1' => 5, 'net' => 5, 'n' => 5, 'pxe_nic1' => 5, 'eth1' => 6, 'nic2' => 6, 'net2' => 6, 'pxe_nic2' => 6, 'eth2' => 7, 'nic3' => 7, 'net3' => 7, 'pxe_nic3' => 7, 'eth3' => 8, 'nic4' => 8, 'net4' => 8, 'pxe_nic4' => 8 ); my @rscan_attribs = qw(nodetype name id mtm serial mpa groups mgt); my @rscan_header = ( [ "type", "%-8s" ], [ "name", "" ], [ "id", "%-8s" ], [ "type-model", "%-12s" ], [ "serial-number", "%-15s" ], [ "address", "%s\n" ]); sub waitforack { my $sock = shift; my $select = new IO::Select; $select->add($sock); my $str; if ($select->can_read(10)) { # Continue after 10 seconds, even if not acked... if ($str = <$sock>) { } else { $select->remove($sock); #Block until parent acks data } } } # Login to the OA using credentials found in the database. sub oaLogin { my $oaName = shift; my $result = ""; my $hopa = ""; my $errHash; # we need to get the info on the OA. If the specfied OA is NOT the # ACTIVE OA then we return failure because we can't get the desired # info from a STANDBY OA. my ($username, $passwd, $encinfo); my $mpatab = xCAT::Table->new('mpa'); my $ent; if (defined($mpatab)) { ($ent) = $mpatab->getAttribs({ 'mpa' => $oaName }, 'username', 'password'); if (defined($ent->{password})) { $password = $ent->{password}; } if (defined($ent->{username})) { $username = $ent->{username}; } } $hpoa = xCAT::hpoa->new('oaAddress' => $oaName); my $loginResponse = $hpoa->userLogIn('username' => $username, 'password' => $password); if ($loginResponse->fault) { $errHash = $loginResponse->fault; print Dumper($errHash); $result = $loginResponse->oaErrorText; if ($loginResponse->fault) { return (1, "Error on login attempt"); } } my $response = $hpoa->getEnclosureInfo(); if ($response->fault) { return (1, "Error on get Enclosure Info call"); } my $numOABays = $response->result->{oaBays}; # OK We now know how many oaBays we have in this enclosure. Ask the OAs in each bay # if they are active. If they are not, then leave since we can't get what we want # from a standby OA $activeOABay = 0; for (my $oaBay = 1 ; $oaBay <= $numOABays ; $oaBay++) { $response = $hpoa->getOaInfo(bayNumber => $oaBay); if (!defined $response->result() || $response->result()->{oaRole} eq "OA_ABSENT" || $response->result->{youAreHere} eq "false") { # either there is no OA here or this is not the one I am currently # communicating with next; } elsif ($response->result->{youAreHere} eq "true") { $activeOABay = $oaBay; last; } } if (!$activeOABay) { return (1, "Cannot determine active OnBoard Administrator"); } # Last thing. Need to determine if this is the active OA. If not, then we # just tell the caller, and they can make the decision as to what they want # to do. $response = $hpoa->getOaStatus(bayNumber => $activeOABay); if ($response->result->{oaRole} ne "ACTIVE") { return (-1); } return ($hpoa); } sub oaLogout { my $hpoa = shift; my $response = $hpoa->userLogOut(); } sub convertSlot { my $origSlot = shift; if ($origSlot =~ /\D/) { my $slotNum = $origSlot; my $slotAlpha = $slotNum; $slotNum =~ s/\D//; $slotAlpha =~ s/\d//; my $side; if ($slotAlpha eq "a" or $slotAlpha eq "A") { $side = 1; } elsif ($slotAlpha eq "b" or $slotAlpha eq "B") { $side = 2; } else { return (-1); } my $returnSlot = $side * 16 + $slotNum; return ($returnSlot); } return ($origSlot); } sub gethpbladecons { my $noderange = shift; my $callback = shift; my $mpatab = xCAT::Table->new('mpa'); my $passtab = xCAT::Table->new('passwd'); my $tmp; my $user = "USERID"; if ($passtab) { ($tmp) = $passtab->getAttribs({ 'key' => 'blade' }, 'username'); if (defined($tmp)) { $user = $tmp->{username}; } } my $mptab = xCAT::Table->new('mp'); my $mptabhash = $mptab->getNodesAttribs($noderange, [ 'mpa', 'id' ]); foreach my $node (@$noderange) { my $rsp = { node => [ { name => [$node] } ] }; my $ent = $mptabhash->{$node}->[0]; #$mptab->getNodeAttribs($node,['mpa', 'id']); if (defined($ent->{mpa})) { $oa = $ent->{mpa}; $slot = convertSlot($ent->{id}); if ($slot == 0) { # want to open a console on the OA $rsp->{node}->[0]->{mm} = $oa; } else { $hpoa = oaLogin($oa); my $mpInfoResp = $hpoa->getBladeMpInfo("bayNumber" => $slot); if ($mpInfoResp->fault) { $rsp->{node}->[0]->{error} = ["Error getting MP info"]; $rsp->{node}->[0]->{errorcode} = [1]; $callback->($rsp); next; } my $ipaddress = $mpInfoResp->result->{ipAddress}; $rsp->{node}->[0]->{mm} = $ipaddress; } ($tmp) = $mpatab->getAttribs({ 'mpa' => $oa }, 'username'); $user = [ $tmp->{username} ]; $rsp->{node}->[0]->{username} = $user; } else { $rsp->{node}->[0]->{error} = ["no mpa defined"]; $rsp->{node}->[0]->{errorcode} = [1]; $callback->($rsp); next; } if (defined($ent->{id})) { $rsp->{node}->[0]->{slot} = $ent->{id}; } else { $rsp->{node}->[0]->{slot} = ""; } $callback->($rsp); } } sub preprocess_request { my $request = shift; #if ($request->{_xcatdest}) { return [$request]; } #exit if preprocessed if ((defined($request->{_xcatpreprocessed})) && ($request->{_xcatpreprocessed}->[0] == 1)) { return [$request]; } my $callback = shift; my @requests; #display usage statement if -h is present or no noderage is specified my $noderange = $request->{node}; #Should be arrayref my $command = $request->{command}->[0]; my $extrargs = $request->{arg}; my @exargs = ($request->{arg}); if (ref($extrargs)) { @exargs = @$extrargs; } my $usage_string = xCAT::Usage->parseCommand($command, @exargs); if ($usage_string) { $callback->({ data => $usage_string }); $request = {}; return; } if (!$noderange) { $usage_string = xCAT::Usage->getUsage($command); $callback->({ data => $usage_string }); $request = {}; return; } # require SOAP::Lite for hpoa.pm so we can do it dynamically my $soapsupport = eval { require SOAP::Lite; }; unless ($soapsupport) { #Still no SOAP::Lite module $callback->({ error => "SOAP::Lite perl module missing. Install perl-SOAP-Lite before running HP blade commands.", errorcode => [42] }); return []; } require xCAT::hpoa; #get the MMs for the nodes for the nodes in order to figure out which service nodes to send the requests to my $mptab = xCAT::Table->new("mp"); unless ($mptab) { $callback->({ data => ["Cannot open mp table"] }); $request = {}; return; } my %mpa_hash = (); my $mptabhash = $mptab->getNodesAttribs($noderange, [ 'mpa', 'id' ]); if ($request->{command}->[0] eq "gethpbladecons") { #Can handle it here and now gethpbladecons($noderange, $callback); return (); } foreach my $node (@$noderange) { my $ent = $mptabhash->{$node}->[0]; #$mptab->getNodeAttribs($node,['mpa', 'id']); if (defined($ent->{mpa})) { push @{ $mpa_hash{ $ent->{mpa} }{nodes} }, $node; } else { $callback->({ data => ["no mpa defined for node $node"] }); $request = {}; return; } my $tempid; if (defined($ent->{id})) { #if the ide is defined, we need to see if there is a letter embedded in it. If there is, #then we need to convert the id to the correct slot $tempid = convertSlot($ent->{id}); push @{ $mpa_hash{ $ent->{mpa} }{ids} }, $tempid; } else { push @{ $mpa_hash{ $ent->{mpa} }{ids} }, ""; } } # find service nodes for the MMs # build an individual request for each service node my $service = "xcat"; my @mms = keys(%mpa_hash); my $sn = xCAT::ServiceNodeUtils->get_ServiceNode(\@mms, $service, "MN"); # build each request for each service node foreach my $snkey (keys %$sn) { #print "snkey=$snkey\n"; my $reqcopy = {%$request}; $reqcopy->{'_xcatdest'} = $snkey; my $mms1 = $sn->{$snkey}; my @moreinfo = (); my @nodes = (); foreach (@$mms1) { push @nodes, @{ $mpa_hash{$_}{nodes} }; push @moreinfo, "\[$_\]\[" . join(',', @{ $mpa_hash{$_}{nodes} }) . "\]\[" . join(',', @{ $mpa_hash{$_}{ids} }) . "\]"; } $reqcopy->{node} = \@nodes; #print "nodes=@nodes\n"; $reqcopy->{moreinfo} = \@moreinfo; push @requests, $reqcopy; } return \@requests; } sub build_more_info { my $noderange = shift; my $callback = shift; my $mptab = xCAT::Table->new("mp"); my @moreinfo = (); unless ($mptab) { $callback->({ data => ["Cannot open mp table"] }); return @moreinfo; } my %mpa_hash = (); my $mptabhash = $mptab->getNodesAttribs($noderange, [ 'mpa', 'id' ]); foreach my $node (@$noderange) { my $ent = $mptabhash->{$node}->[0]; #$mptab->getNodeAttribs($node,['mpa', 'id']); if (defined($ent->{mpa})) { push @{ $mpa_hash{ $ent->{mpa} }{nodes} }, $node; } else { $callback->({ data => ["no mpa defined for node $node"] }); return @moreinfo; } if (defined($ent->{id})) { push @{ $mpa_hash{ $ent->{mpa} }{ids} }, $ent->{id}; } else { push @{ $mpa_hash{ $ent->{mpa} }{ids} }, ""; } } foreach (keys %mpa_hash) { push @moreinfo, "\[$_\]\[" . join(',', @{ $mpa_hash{$_}{nodes} }) . "\]\[" . join(',', @{ $mpa_hash{$_}{ids} }) . "\]"; } return \@moreinfo; } sub handle_depend { my $request = shift; my $callback = shift; my $doreq = shift; my $dp = shift; my %node = (); my $dep = @$dp[0]; my $dep_hash = @$dp[1]; # send all dependencies (along w/ those dependent on nothing) # build moreinfo for dependencies my %mpa_hash = (); my @moreinfo = (); my $reqcopy = {%$request}; my @nodes = (); foreach my $node (keys %$dep) { my $mpa = @{ $dep_hash->{$node} }[0]; push @{ $mpa_hash{$mpa}{nodes} }, $node; push @{ $mpa_hash{$mpa}{ids} }, @{ $dep_hash->{$node} }[1]; } foreach (keys %mpa_hash) { push @nodes, @{ $mpa_hash{$_}{nodes} }; push @moreinfo, "\[$_\]\[" . join(',', @{ $mpa_hash{$_}{nodes} }) . "\]\[" . join(',', @{ $mpa_hash{$_}{ids} }) . "\]"; } $reqcopy->{node} = \@nodes; $reqcopy->{moreinfo} = \@moreinfo; process_request($reqcopy, $callback, $doreq, 1); my $start = Time::HiRes::gettimeofday(); # build list of dependent nodes w/delays while (my ($name, $h) = each(%$dep)) { foreach (keys %$h) { if ($h->{$_} =~ /(^\d+$)/) { $node{$_} = $1 / 1000.0; } } } # send each dependent node as its delay expires while (%node) { my @noderange = (); my $delay = 0.1; my $elapsed = Time::HiRes::gettimeofday() - $start; # sort in ascending delay order foreach (sort { $node{$a} <=> $node{$b} } keys %node) { if ($elapsed < $node{$_}) { $delay = $node{$_} - $elapsed; last; } push @noderange, $_; delete $node{$_}; } if (@noderange) { %mpa_hash = (); foreach my $node (@noderange) { my $mpa = @{ $dep_hash->{$node} }[0]; push @{ $mpa_hash{$mpa}{nodes} }, $node; push @{ $mpa_hash{$mpa}{ids} }, @{ $dep_hash->{$node} }[1]; } @moreinfo = (); $reqcopy = {%$request}; @nodes = (); foreach (keys %mpa_hash) { push @nodes, @{ $mpa_hash{$_}{nodes} }; push @moreinfo, "\[$_\]\[" . join(',', @{ $mpa_hash{$_}{nodes} }) . "\]\[" . join(',', @{ $mpa_hash{$_}{ids} }) . "\]"; } $reqcopy->{node} = \@nodes; $reqcopy->{moreinfo} = \@moreinfo; # clear global hash variable %oahash = (); process_request($reqcopy, $callback, $doreq, 1); } # millisecond sleep Time::HiRes::sleep($delay); } return 0; } sub build_depend { my $noderange = shift; my $exargs = shift; my $depstab = xCAT::Table->new('deps'); my $mptab = xCAT::Table->new('mp'); my %dp = (); my %no_dp = (); my %mpa_hash; if (!defined($depstab)) { return ([ \%dp ]); } unless ($mptab) { return ("Cannot open mp table"); } my $depset = $depstab->getNodesAttribs($noderange, [qw(nodedep msdelay cmd)]); foreach my $node (@$noderange) { my $delay = 0; my $dep; my @ent = @{ $depset->{$node} }; #$depstab->getNodeAttribs($node,[qw(nodedep msdelay cmd)]); foreach my $h (@ent) { if (grep(/^@$exargs[0]$/, split /,/, $h->{cmd})) { if (exists($h->{nodedep})) { $dep = $h->{nodedep}; } if (exists($h->{msdelay})) { $delay = $h->{msdelay}; } last; } } if (!defined($dep)) { $no_dp{$node} = 1; } else { foreach my $n (split /,/, $dep) { if (!grep(/^$n$/, @$noderange)) { return ("Missing dependency on command-line: $node -> $n"); } elsif ($n eq $node) { next; # ignore multiple levels } $dp{$n}{$node} = $delay; } } } # if there are dependencies, add any non-dependent nodes if (scalar(%dp)) { foreach (keys %no_dp) { if (!exists($dp{$_})) { $dp{$_}{$_} = -1; } } # build hash of all nodes in preprocess_request() format my @namelist = keys %dp; my $mphash = $mptab->getNodesAttribs(\@namelist, [ 'mpa', 'id' ]); while (my ($name, $h) = each(%dp)) { my $ent = $mphash->{$name}->[0]; #$mptab->getNodeAttribs($name,['mpa', 'id']); if (!defined($ent->{mpa})) { return ("no mpa defined for node $name"); } my $id = (defined($ent->{id})) ? $ent->{id} : ""; push @{ $mpa_hash{$name} }, $ent->{mpa}; push @{ $mpa_hash{$name} }, $id; @namelist = keys %$h; my $mpsubhash = $mptab->getNodesAttribs(\@namelist, [ 'mpa', 'id' ]); foreach (keys %$h) { if ($h->{$_} =~ /(^\d+$)/) { my $ent = $mpsubhash->{$_}->[0]; #$mptab->getNodeAttribs($_,['mpa', 'id']); if (!defined($ent->{mpa})) { return ("no mpa defined for node $_"); } my $id = (defined($ent->{id})) ? $ent->{id} : ""; push @{ $mpa_hash{$_} }, $ent->{mpa}; push @{ $mpa_hash{$_} }, $id; } } } } return ([ \%dp, \%mpa_hash ]); } sub process_request { $SIG{INT} = $SIG{TERM} = sub { foreach (keys %mm_comm_pids) { kill 2, $_; } exit 0; }; my $request = shift; my $callback = shift; my $doreq = shift; my $level = shift; my $noderange = $request->{node}; my $command = $request->{command}->[0]; my @exargs; unless ($command) { return; #Empty request } # require SOAP::Lite for hpoa.pm so we can do it dynamically my $soapsupport = eval { require SOAP::Lite; }; unless ($soapsupport) { #Still no SOAP::Lite module $callback->({ error => "SOAP::Lite perl module missing. Install perl-SOAP-Lite before running HP blade commands.", errorcode => [42] }); return []; } require xCAT::hpoa; if (ref($request->{arg})) { @exargs = @{ $request->{arg} }; } else { @exargs = ($request->{arg}); } my $moreinfo; if ($request->{moreinfo}) { $moreinfo = $request->{moreinfo}; } else { $moreinfo = build_more_info($noderange, $callback); } #pdu commands will be handled in the pdu plugin if ($command eq "rpower" and grep(/^pduon|pduoff|pdustat$/, @exargs)) { return; } if ($command eq "rpower" and grep(/^on|off|boot|reset|cycle$/, @exargs)) { if (my ($index) = grep($exargs[$_] =~ /^--nodeps$/, 0 .. $#exargs)) { splice(@exargs, $index, 1); } else { # handles 1 level of dependencies only if (!defined($level)) { my $dep = build_depend($noderange, \@exargs); if (ref($dep) ne 'ARRAY') { $callback->({ data => [$dep], errorcode => 1 }); return; } if (scalar(%{ @$dep[0] })) { handle_depend($request, $callback, $doreq, $dep); return 0; } } } } # only 1 node when changing textid to something other than '*' if ($command eq "rspconfig" and grep(/^textid=[^*]/, @exargs)) { if (@$noderange > 1) { $callback->({ data => ["Single node required when changing textid"], errorcode => 1 }); return; } } my $bladeuser = 'USERID'; my $bladepass = 'PASSW0RD'; my $blademaxp = 64; #my $sitetab = xCAT::Table->new('site'); my $mpatab = xCAT::Table->new('mpa'); my $mptab = xCAT::Table->new('mp'); my $tmp; #if ($sitetab) { #($tmp)=$sitetab->getAttribs({'key'=>'blademaxp'},'value'); my @entries = xCAT::TableUtils->get_site_attribute("blademaxp"); my $t_entry = $entries[0]; if (defined($t_entry)) { $blademaxp = $t_entry; } #} my $passtab = xCAT::Table->new('passwd'); if ($passtab) { ($tmp) = $passtab->getAttribs({ 'key' => 'blade' }, 'username', 'password'); if (defined($tmp)) { $bladeuser = $tmp->{username}; $bladepass = $tmp->{password}; } } if ($request->{command}->[0] eq "findme") { if (defined($request->{discoverymethod}) and defined($request->{discoverymethod}->[0]) and ($request->{discoverymethod}->[0] ne 'undef')) { # The findme request had been processed by other module, just return return; } my $mptab = xCAT::Table->new("mp"); unless ($mptab) { return 2; } my @bladents = $mptab->getAllNodeAttribs([qw(node)]); my @blades; foreach (@bladents) { push @blades, $_->{node}; } my %invreq; $invreq{node} = \@blades; $invreq{arg} = ['mac']; $invreq{command} = ['rinv']; my $mac; my $ip = $request->{'_xcat_clientip'}; my $arptable; if (-x "/usr/sbin/arp") { $arptable = `/usr/sbin/arp -n`; } else { $arptable = `/sbin/arp -n`; } my @arpents = split /\n/, $arptable; foreach (@arpents) { if (m/^($ip)\s+\S+\s+(\S+)\s/) { $mac = $2; last; } } unless ($mac) { return } #Only refresh the the cache when the request permits and no useful answer if ($macmaptimestamp < (time() - 300)) { #after five minutes, invalidate cache %macmap = (); } unless ($request->{cacheonly}->[0] or $macmap{$mac} or $macmaptimestamp > (time() - 20)) { #do not refresh cache if requested not to, if it has an entry, or is recent %macmap = (); $macmaptimestamp = time(); foreach (@{ preprocess_request(\%invreq, \&fillresps) }) { %invreq = %$_; process_request(\%invreq, \&fillresps); } } unless ($macmap{$mac}) { xCAT::MsgUtils->message("S", "xcat.discovery.hpblade: ($request->{_xcat_clientmac}->[0]) Warning: Could not find any nodes using hpblade-based discovery"); return 1; #failure } # The discovered command will update mac table, no need to update here #my $mactab = xCAT::Table->new('mac',-create=>1); #$mactab->setNodeAttribs($macmap{$mac},{mac=>$mac}); #$mactab->close(); #my %request = ( # command => ['makedhcp'], # node => [$macmap{$mac}] # ); #$doreq->(\%request); xCAT::MsgUtils->message("S", "xcat.discovery.hpblade: ($request->{_xcat_clientmac}->[0]) Found node: $macmap{$mac}"); my $req = {%$request}; $req->{command} = ['discovered']; $req->{noderange} = [ $macmap{$mac} ]; $doreq->($req); %{$req} = (); #Clear request. it is done #undef $mactab; return 0; } my $children = 0; $SIG{CHLD} = sub { my $cpid; while ($cpid = waitpid(-1, WNOHANG) > 0) { delete $mm_comm_pids{$cpid}; $children--; } }; my $inputs = new IO::Select; foreach my $info (@$moreinfo) { $info =~ /^\[(.*)\]\[(.*)\]\[(.*)\]/; my $mpa = $1; my @nodes = split(',', $2); my @ids = split(',', $3); #print "mpa=$mpa, nodes=@nodes, ids=@ids\n"; my $user = $bladeuser; my $pass = $bladepass; my $ent; if (defined($mpatab)) { ($ent) = $mpatab->getAttribs({ 'mpa' => $mpa }, 'username', 'password'); if (defined($ent->{password})) { $pass = $ent->{password}; } if (defined($ent->{username})) { $user = $ent->{username}; } } $oahash{$mpa}->{username} = $user; $oahash{$mpa}->{password} = $pass; for (my $i = 0 ; $i < @nodes ; $i++) { my $node = $nodes[$i]; my $nodeid = $ids[$i]; $oahash{$mpa}->{nodes}->{$node} = $nodeid; } } my $sub_fds = new IO::Select; foreach $oa (sort (keys %oahash)) { while ($children > $blademaxp) { forward_data($callback, $sub_fds); } $children++; my $cfd; my $pfd; socketpair($pfd, $cfd, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!"; $cfd->autoflush(1); $pfd->autoflush(1); my $cpid = xCAT::Utils->xfork; unless (defined($cpid)) { die "Fork error"; } unless ($cpid) { close($cfd); eval { doblade($pfd, $oa, \%oahash, $command, -args => \@exargs); exit(0); }; if ($@) { die "$@"; } die "blade plugin encountered a general error while communication with $oa"; } $mm_comm_pids{$cpid} = 1; close($pfd); $sub_fds->add($cfd); } while ($sub_fds->count > 0 or $children > 0) { forward_data($callback, $sub_fds); } while (forward_data($callback, $sub_fds)) { } } my $IMPORT_SSH_KEY_HEADER = ' -----BEGIN SSH KEY----- '; my $IMPORT_SSH_KEY_FOOTER = ' -----END SSH KEY----- '; my $MOD_NETWORK_SETTINGS_HEADER = ' '; my $MOD_NETWORK_SETTINGS_FOOTER = ' '; my $GET_NETWORK_SETTINGS = ' '; Net::SSLeay::load_error_strings(); Net::SSLeay::SSLeay_add_ssl_algorithms(); Net::SSLeay::randomize(); # # opens an ssl connection to port 443 of the passed host # sub openSSLconnection($) { my $host = shift; my ($ssl, $sin, $ip, $nip); if (not $ip = inet_aton($host)) { print "$host is a DNS Name, performing lookup\n" if $globalDebug; $ip = gethostbyname($host) or die "ERROR: Host $host notfound. \n"; } $nip = inet_ntoa($ip); #print STDERR "Connecting to $nip:443\n"; $sin = sockaddr_in(443, $ip); socket(S, &AF_INET, &SOCK_STREAM, 0) or die "ERROR: socket: $!"; connect(S, $sin) or die "connect: $!"; $ctx = Net::SSLeay::CTX_new() or die_now("ERROR: Failed to create SSL_CTX $! "); Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_ALL); die_if_ssl_error("ERROR: ssl ctx set options"); $ssl = Net::SSLeay::new($ctx) or die_now("ERROR: Failed to create SSL $!"); Net::SSLeay::set_fd($ssl, fileno(S)); Net::SSLeay::connect($ssl) and die_if_ssl_error("ERROR: ssl connect"); #print STDERR 'SSL Connected '; print 'Using Cipher: ' . Net::SSLeay::get_cipher($ssl) if $globalDebug; #print STDERR "\n\n"; return $ssl; } sub closeSSLconnection($) { my $ssl = shift; Net::SSLeay::free($ssl); # Tear down connection Net::SSLeay::CTX_free($ctx); close S; } # usage: sendscript(host, script) # sends the xmlscript script to host, returns reply sub sendScript($$) { my $host = shift; my $script = shift; my ($ssl, $reply, $lastreply, $res, $n); $ssl = openSSLconnection($host); # write header $n = Net::SSLeay::ssl_write_all($ssl, '' . "\r\n"); print "Wrote $n\n" if $globalDebug; $n = Net::SSLeay::ssl_write_all($ssl, '' . "\r\n"); print "Wrote $n\n" if $globalDebug; # write script $n = Net::SSLeay::ssl_write_all($ssl, $script); print "Wrote $n\n$script\n" if $globalDebug; $reply = ""; $lastreply = ""; my $reply2return = ""; READLOOP: while (1) { $n++; $lastreply = Net::SSLeay::read($ssl); die_if_ssl_error("ERROR: ssl read"); if ($lastreply eq "") { sleep(2); # wait 2 sec for more text. $lastreply = Net::SSLeay::read($ssl); die_if_ssl_error("ERROR: ssl read"); last READLOOP if ($lastreply eq ""); } $reply .= $lastreply; print "lastreply $lastreply \b" if $globalDebug; # Check response to see if a error was returned. if ($lastreply =~ m/STATUS="(0x[0-9A-F]+)"[\s]+MESSAGE='(.*)'[\s]+\/>[\s]*(([\s]|.)*?)<\/RIBCL>/) { if ($1 eq "0x0000") { #print STDERR "$3\n" if $3; } else { $reply2return = "ERROR: STATUS: $1, MESSAGE: $2"; } } } print "READ: $lastreply\n" if $globalDebug; if ($lastreply =~ m/STATUS="(0x[0-9A-F]+)"[\s]+MESSAGE='(.*)'[\s]+\/>[\s]*(([\s]|.)*?)<\/RIBCL>/) { if ($1 eq "0x0000") { #Sprint STDERR "$3\n" if $3; } else { $reply2return = "ERROR: STATUS: $1, MESSAGE: $2"; } } closeSSLconnection($ssl); return $reply2return; } sub extractValue { my $inputString = shift; my $testString = shift; $testString = "<" . "$testString" . " VALUE="; my $start = index($inputString, $testString) + length $testString; my $end = index $inputString, "\"", ($start + 1); return (substr($inputString, ($start + 1), ($end - $start - 1))); } sub iloconfig { my $oa = shift; my $user = shift; my $pass = shift; my $node = shift; my $nodeid = shift; my $parameter; my $value; my $assignment; my $returncode = 0; my $textid = 0; @cfgtext = (); # Before we get going, lets get the info on the MP (iLO) $slot = convertSlot($nodeid); my $mpInfoResp = $hpoa->getBladeMpInfo("bayNumber" => $slot); if ($mpInfoResp->fault) { my $errorText = "Error getting MP info"; next; } my $ipaddress = $mpInfoResp->result->{ipAddress}; foreach $parameter (@_) { $assignment = 0; $value = undef; if ($parameter =~ /=/) { $assignment = 1; ($parameter, $value) = split /=/, $parameter, 2; } if ($parameter =~ /^sshcfg$/) { my $fname = "/root/.ssh/id_dsa.pub"; if (!-s $fname) { # Key file specified does not exist. Error! push @cfgtext, "rspconfig:key file does not exist"; next; } open(KEY, "$fname"); my $key = readline(KEY); close(KEY); my $script = "$IMPORT_SSH_KEY_HEADER" . "$key" . "$IMPORT_SSH_KEY_FOOTER"; $script =~ s/AdMiNnAmE/$user/; $script =~ s/PaSsWoRd/$pass/; my $reply = sendScript($ipaddress, $script); push @cfgtext, $reply; next; } if ($parameter =~ /^network$/) { if ($value) { # If value is set, then the user wans us to set these values my ($newip, $newhostname, $newgateway, $newmask) = split /,/, $value; my $script = "$MOD_NETWORK_SETTINGS_HEADER"; $script = $script . "" if ($newip); $script = $script . "" if ($newgateway); $script = $script . "" if ($newmask); $script = $script . "$MOD_NETWORK_SETTINGS_FOOTER"; $script =~ s/AdMiNnAmE/$user/; $script =~ s/PaSsWoRd/$pass/; my $reply = sendScript($ipaddress, $script); if ($newip) { push @cfgtext, "iLO IP: $newip"; } if ($newgateway) { push @cfgtext, "Gateway: $newgateway"; } if ($newmask) { push @cfgtext, "Subnet Mask: $newmask"; } push @cfgtext, $reply; } else { my $script = "$GET_NETWORK_SETTINGS"; $script =~ s/AdMiNnAmE/$user/; $script =~ s/PaSsWoRd/$pass/; my $reply = sendScript($ipaddress, $script); my $readipaddress = extractValue($reply, "IP_ADDRESS"); my $gateway = extractValue($reply, "GATEWAY_IP_ADDRESS"); my $netmask = extractValue($reply, "SUBNET_MASK"); push @cfgtext, "iLO IP: $readipaddress"; push @cfgtext, "Gateway: $gateway"; push @cfgtext, "Subnet mask: $netmask"; push @cfgtext, $reply; } } } return 0, @cfgtext; } sub getmacs { (my $code, my @macs) = inv('mac'); my $mkey; my $nic2Find; my $nrtab = xCAT::Table->new('noderes'); if ($nrtab) { my $nent = $nrtab->getNodeAttribs($curn, [ 'primarynic', 'installnic' ]); if ($nent) { if (defined $nent->{installnic}) { #Prefer the install nic $mkey = "installnic"; } elsif (defined $nent->{primarynic}) { #see if primary nic was set $mkey = "primarynic"; } $nic2Find = $nent->{$mkey}; } } # We now have the nic2Find, so we need to convert this to the NIC format # Strip away the "eth" my $interface = $nic2Find; $nic2Find =~ s/eth//; my $numberPxeNic = $nic2Find + 1; my $pxeNic = "NIC " . $numberPxeNic; if ($code == 0) { my $mac; my @allmacs; foreach my $macEntry (@macs) { if ($macEntry =~ /MAC ADDRESS $pxeNic/) { $mac = $macEntry; $mac =~ s/MAC ADDRESS $pxeNic: //; $mac = lc $mac; last; } } if (!$mac) { return 1, "Unable to retrieve MAC address for interface $pxeNic from OnBoard Administrator"; } my $mactab = xCAT::Table->new('mac', -create => 1); $mactab->setNodeAttribs($curn, { mac => $mac }, { interface => $interface }); $mactab->close; return 0, ":mac.mac set to $mac"; } else { return $code, $macs[0]; } } sub inv { my @invitems; my @output; foreach (@_) { push @invitems, split(/,/, $_); } my $item; unless (scalar(@invitems)) { @invitems = ("all"); } # Before going off to handle the items, issue a getBladeInfo, getBladeMpInfo, and getOaInfo my $getBladeInfoResult = $hpoa->getBladeInfo("bayNumber" => $slot); if ($getBladeInfoResult->fault) { return (1, "getBladeInfo on node $curn failed"); } my $getBladeMpInfoResult = $hpoa->getBladeMpInfo("bayNumber" => $slot); if ($getBladeMpInfoResult->fault) { return (1, "getBladeMpInfo on node $curn fault"); } my $getOaInfoResult = $hpoa->getOaInfo("bayNumber" => $activeOABay); if ($getOaInfoResult->fault) { my $errHash = $getOaInfoResult->fault; return (1, "getOaInfo failed"); } while (my $item = shift @invitems) { if ($item =~ /^all/) { push @invitems, (qw(mtm serial mac firm)); next; } if ($item =~ /^firm/) { push @invitems, (qw(bladerom mprom oarom)); } if ($item =~ /^bladerom/) { push @output, "BladeFW: " . $getBladeInfoResult->result->{romVersion}; } if ($item =~ /^mprom/) { push @output, "iLOFW: " . $getBladeMpInfoResult->result->{fwVersion}; } if ($item =~ /~oarom/) { push @output, "OA FW: " . $getOaInfoResult->result->{fwVersion}; } if ($item =~ /^model/ or $item =~ /^mtm/) { push @output, "Machine Type/Model: " . $getBladeInfoResult->result->{partNumber}; } if ($item =~ /^serial/) { push @output, "Serial Number: " . $getBladeInfoResult->result->{serialNumber}; } if ($item =~ /^mac/) { my $numberOfNics = $getBladeInfoResult->result->{numberOfNics}; for (my $i = 0 ; $i < $numberOfNics ; $i++) { my $mac = $getBladeInfoResult->result->{nics}->{bladeNicInfo}[$i]->{macAddress}; my $port = $getBladeInfoResult->result->{nics}->{bladeNicInfo}[$i]->{port}; push @output, "MAC ADDRESS " . $port . ": " . $mac; #push@output, "MAC Address ".($_+1).": ".$getBladeInfoResult->result->{nics}->{bladeNicInfo}[$i]->{macAddress}; } } } return (0, @output); } sub CtoF { my $Ctemp = shift; return ((($Ctemp * 9) / 5) + 32); } my %chassiswidevitals; sub vitals { my @output; my $tmp; my @vitems; if ($#_ == 0 && $_[0] eq '') { pop @_; push @_, "all" } #-- default is all if no argument given if (defined $slot and $slot > 0) { #-- blade query foreach (@_) { if ($_ eq 'all') { # push @vitems,qw(temp voltage wattage summary fan); push @vitems, qw(cpu_temp memory_temp system_temp ambient_temp summary fanspeed); push @vitems, qw(led power); } elsif ($_ =~ '^led') { push @vitems, qw(led); } else { push @vitems, split(/,/, $_); } } } else { #-- chassis query foreach (@_) { if ($_ eq 'all') { # push @vitems,qw(voltage wattage power summary); push @vitems, qw(cpu_temp memory_temp system_temp ambient_temp summary fanspeed); # push @vitems,qw(errorled beaconled infoled templed); push @vitems, qw(led power); } elsif ($_ =~ '^led') { push @vitems, qw(led); } elsif ($_ =~ '^cool') { push @vitems, qw(fanspeed); } elsif ($_ =~ '^temp') { push @vitems, qw(ambient_temp); } else { push @vitems, split(/,/, $_); } } } my @vitals; if (defined $slot and $slot > 0) { #-- querying some blade if (grep /temp/, @vitems) { my $tempResponse = $hpoa->getBladeThermalInfoArray("bayNumber" => $slot); if ($tempResponse->fault) { push @output, "Request to get Temperature info on slot $slot failed"; } elsif (!$tempResponse->result) { # If is the case then the temperature data is not yet available. push @output, "Temperature data not available."; } else { # We have data so go process it.... my @tempdata = $tempResponse->result->{bladeThermalInfo}; my $lastElement = $tempResponse->result->{bladeThermalInfo}[-1]->{sensorNumber}; if (grep /cpu_temp/, @vitems) { my $index = -1; do { $index++; if (grep /CPU/, $tempResponse->result->{bladeThermalInfo}[$index]->{description}) { my $Ctemp = $tempResponse->result->{bladeThermalInfo}[$index]->{temperatureC}; my $desc = $tempResponse->result->{bladeThermalInfo}[$index]->{description}; my $Ftemp = CtoF($Ctemp); push @output, "$desc Temperature: $Ctemp C \( $Ftemp F \)"; } } until $tempResponse->result->{bladeThermalInfo}[$index]->{sensorNumber} eq $lastElement; } if (grep /memory_temp/, @vitems) { my $index = -1; do { $index++; if (grep /Memory/, $tempResponse->result->{bladeThermalInfo}[$index]->{description}) { my $Ctemp = $tempResponse->result->{bladeThermalInfo}[$index]->{temperatureC}; my $desc = $tempResponse->result->{bladeThermalInfo}[$index]->{description}; my $Ftemp = CtoF($Ctemp); push @output, "$desc Temperature: $Ctemp C \( $Ftemp F \)"; } } until $tempResponse->result->{bladeThermalInfo}[$index]->{sensorNumber} eq $lastElement; } if (grep /system_temp/, @vitems) { my $index = -1; do { $index++; if (grep /System/, $tempResponse->result->{bladeThermalInfo}[$index]->{description}) { my $Ctemp = $tempResponse->result->{bladeThermalInfo}[$index]->{temperatureC}; my $desc = $tempResponse->result->{bladeThermalInfo}[$index]->{description}; my $Ftemp = CtoF($Ctemp); push @output, "$desc Temperature: $Ctemp C \( $Ftemp F \)"; } } until $tempResponse->result->{bladeThermalInfo}[$index]->{sensorNumber} eq $lastElement; } if (grep /ambient_temp/, @vitems) { my $index = -1; do { $index++; if (grep /Ambient/, $tempResponse->result->{bladeThermalInfo}[$index]->{description}) { my $Ctemp = $tempResponse->result->{bladeThermalInfo}[$index]->{temperatureC}; my $desc = $tempResponse->result->{bladeThermalInfo}[$index]->{description}; my $Ftemp = CtoF($Ctemp); push @output, "$desc Temperature: $Ctemp C \( $Ftemp F \)"; } } until $tempResponse->result->{bladeThermalInfo}[$index]->{sensorNumber} eq $lastElement; } } } if (grep /fanspeed/, @vitems) { my $fanInfoResponse = $hpoa->getFanInfo("bayNumber" => $slot); if ($fanInfoResponse->fault) { push @output, "Request to get Fan Info from slot $slot failed "; } elsif (!$fanInfoResponse->result) { push @output, "No Fan Information"; } else { my $fanStatus = $fanInfoResponse->result->{operationalStatus}; my $fanMax = $fanInfoResponse->result->{maxFanSpeed}; my $fanCur = $fanInfoResponse->result->{fanSpeed}; my $fanPercent = ($fanCur / $fanMax) * 100; push @output, "Fan status: $fanStatus Percent of max: $fanPercent\%"; } } if (grep /led/, @vitems) { my $currstat = $getBladeStatusResponse->result->{uid}; if ($currstat eq "UID_ON") { push @output, "Current UID Status On"; } elsif ($currstat eq "UID_OFF") { push @output, "Current UID Status Off"; } elsif ($currstat eq "UID_BLINK") { push @output, "Current UID Status Blinking"; } } if (grep /power/, @vitems) { my $currPowerStat = $getBladeStatusResponse->result->{powered}; if ($currPowerStat eq "POWER_ON") { push @output, "Current Power Status On"; } elsif ($currPowerStat eq "POWER_OFF") { push @output, "Current Power Status Off"; } } } return (0, @output); } sub buildEventHash { my $logText = shift; my $eventLogFound = 0; my $eventFound = 0; my $eventNumber = 0; my @lines = split /^/, $logText; foreach my $line (@lines) { if (!$eventLogFound) { if (!$line =~ m/EVENT_LOG/) { next; } elsif ($line =~ m/EVENT_LOG/) { $eventLogFound = 1; next; } } if (!$eventFound && $line =~ m/\/) { $eventNumber++; $eventFound = 0; next; } # We have a good line. Need to split it up and build the hash. my ($desc, $value) = split /=/, $line; for ($desc) { s/^\s+//; s/\s+$//; s/\"//g; s/\\n//; } for ($value) { s/^\s+//; s/\"//g; s/\s+$//; s/\\n//; } $eventHash->{event}->{$eventNumber}->{$desc} = $value; next; } return $eventNumber; } sub eventlog { my $subcommand = shift; my @output; $subcommand = "all" if $subcommand eq ""; if ($subcommand eq "all" or $subcommand =~ /\d+/) { my $mpEventLogResponse = $hpoa->getBladeMpEventLog("bayNumber" => $slot, "maxsize" => 640000); if ($mpEventLogResponse->fault) { return (1, "Attempt to retrieve Event Log faulted"); } my $logText = $mpEventLogResponse->result->{logContents}; my $numEvents = buildEventHash($logText); my $recCount = 0; $eventHash->{'event'} = { map { $recCount++ => $_->[1] } sort { $a->[0] <=> $b->[0] } map { (defined $_->{LAST_UPDATE} && $_->{LAST_UPDATE} ne '[NOT SET]') ? [ &extractDate($_->{LAST_UPDATE}), $_ ] : [ $_->{SELID}, $_ ] } map { $eventHash->{'event'}{$_}{SELID} = $_; $eventHash->{'event'}{$_}; } grep { defined $eventHash->{'event'}{$_} } keys %{ $eventHash->{'event'} } }; my $limitEvents = ($subcommand eq "all" ? $recCount : $subcommand); for (my $index = 0 ; $index < $limitEvents ; $index++) { --$recCount; my $class = $eventHash->{event}->{$recCount}->{CLASS}; my $severity = $eventHash->{event}->{$recCount}->{SEVERITY}; my $dateTime = $eventHash->{event}->{$recCount}->{LAST_UPDATE}; my $desc = $eventHash->{event}->{$recCount}->{DESCRIPTION}; unshift @output, "$class $severity:$dateTime $desc"; } return (0, @output); } elsif ($subcommand eq "clear") { return (1, "Command not supported"); } else { return (1, "Command '$subcommand' not supported"); } } sub rscan { my $args = shift; my @values; my $result; my %opt; @ARGV = @$args; $Getopt::Long::ignorecase = 0; Getopt::Long::Configure("bundling"); local *usage = sub { my $usage_string = xCAT::Usage->getUsage("rscan"); return (join('', ($_[0], $usage_string))); }; if (!GetOptions(\%opt, qw(V|Verbose w x z))) { return (1, usage()); } if (defined($ARGV[0])) { return (1, usage("Invalid argument: @ARGV\n")); } if (exists($opt{x}) and exists($opt{z})) { return (1, usage("-x and -z are mutually exclusive\n")); } my $encInfo = $hpoa->getEnclosureInfo(); if ($encInfo->fault) { return (1, "Attempt tp get enclosure information has failed"); } my $numBays = $encInfo->result->{bladeBays}; my $calcBladeBays = $numBays * 3; # Need to worry aboyt casmir blades my $encName = $encInfo->result->{enclosureName}; my $enctype = $encInfo->result->{name}; my $encmodel = $encInfo->result->{partNumber}; my $encserial = $encInfo->result->{serialNumber}; push @values, join(",", "hpoa", $encName, 0, "$enctype-$encmodel", $encserial, $oa); my $max = length($encName); for (my $i = 1 ; $i <= $calcBladeBays ; $i++) { my $bayInfo = $hpoa->getBladeInfo("bayNumber" => $i); if ($bayInfo->fault) { return (1, "Attempt to get blade info from bay $i has failed"); } if ($bayInfo->result->{presence} eq "ABSENT") { # no blade in the bya next; } my $name = $bayInfo->result->{serverName}; my $bayNum = $i; my $type = $bayInfo->result->{bladeType}; my $model = $bayInfo->result->{name}; my $serial = $bayInfo->result->{serialNumber}; push @values, join(",", "hpblade", $name, $bayNum, "$type-$model", $serial, ""); } my $format = sprintf "%%-%ds", ($max + 2); $rscan_header[1][1] = $format; if (exists($opt{x})) { $result = rscan_xml($oa, \@values); } elsif (exists($opt{z})) { $result = rscan_stanza($oa, \@values); } else { foreach (@rscan_header) { $result .= sprintf @$_[1], @$_[0]; } foreach (@values) { my @data = split /,/; my $i = 0; foreach (@rscan_header) { $result .= sprintf @$_[1], $data[ $i++ ]; } } } if (!exists($opt{w})) { return (0, $result); } my @tabs = qw(mp nodehm nodelist); my %db = (); foreach (@tabs) { $db{$_} = xCAT::Table->new($_, -create => 1, -autocommit => 0); if (!$db{$_}) { return (1, "Error opening '$_'"); } } foreach (@values) { my @data = split /,/; my $name = $data[1]; my ($k1, $u1); $k1->{node} = $name; $u1->{mpa} = $oa; $u1->{id} = $data[2]; $db{mp}->setAttribs($k1, $u1); $db{mp}{commit} = 1; my ($k2, $u2); $k2->{node} = $name; $u2->{mgt} = "hpblade"; $db{nodehm}->setAttribs($k2, $u2); $db{nodehm}{commit} = 1; my ($k3, $u3); $k3->{node} = $name; $u3->{groups} = "blade,all"; $db{nodelist}->setAttribs($k3, $u3); $db{nodelist}{commit} = 1; } foreach (@tabs) { if (exists($db{$_}{commit})) { $db{$_}->commit; } } return (0, $result); } sub rscan_xml { my $mpa = shift; my $values = shift; my $xml; foreach (@$values) { my @data = split /,/; my $i = 0; my $href = { Node => {} }; foreach (@rscan_attribs) { my $d = $data[ $i++ ]; my $type = $data[0]; if (/^name$/) { next; } elsif (/^nodetype$/) { $d = $type; } elsif (/^groups$/) { $d = "$type,all"; } elsif (/^mgt$/) { $d = "blade"; } elsif (/^mpa$/) { $d = $mpa; } $href->{Node}->{$_} = $d; } $xml .= XMLout($href, NoAttr => 1, KeyAttr => [], RootName => undef); } return ($xml); } sub rscan_stanza { my $mpa = shift; my $values = shift; my $result; foreach (@$values) { my @data = split /,/; my $i = 0; my $type = $data[0]; $result .= "$data[1]:\n\tobjtype=node\n"; foreach (@rscan_attribs) { my $d = $data[ $i++ ]; if (/^name$/) { next; } elsif (/^nodetype$/) { $d = $type; } elsif (/^groups$/) { $d = "$type,all"; } elsif (/^mgt$/) { $d = "blade"; } elsif (/^mpa$/) { $d = $mpa; } $result .= "\t$_=$d\n"; } } return ($result); } sub beacon { my $subcommand = shift; if ($subcommand eq "stat") { my $currstat = $getBladeStatusResponse->result->{uid}; if ($currstat eq "UID_ON") { return (0, "on"); } elsif ($currstat eq "UID_OFF") { return (0, "off"); } elsif ($currstat eq "UID_BLINK") { return (0, "blink"); } } my $response; if ($subcommand eq "on") { $response = $hpoa->setBladeUid('bayNumber' => $slot, 'uid' => "UID_CMD_ON"); if ($response->fault) { my $errHash = $response->fault; my $result = $response->oaErrorText; print "result is $result \n"; return ("1", "Uid On failed"); } else { return ("0", ""); } } elsif ($subcommand eq "off") { $response = $hpoa->setBladeUid('bayNumber' => $slot, 'uid' => "UID_CMD_OFF"); if ($response->fault) { my $errHash = $response->fault; my $result = $response->oaErrorText; print "result is $result \n"; return ("1", "Uid Off failed"); } else { return ("0", ""); } } elsif ($subcommand eq "blink") { $response = $hpoa->setBladeUid('bayNumber' => $slot, 'uid' => "UID_CMD_BLINK"); if ($response->fault) { my $errHash = $response->fault; my $result = $response->oaErrorText; print "result is $result \n"; return ("1", "Uid Blink failed"); } else { return ("0", ""); } } else { return (1, "subcommand unsupported"); } return (1, "subcommand unsupported"); } sub bootseq { my @args = @_; my $data; my @order = (); if ($args[0] eq "list" or $args[0] eq "stat") { # Before going off to handle the items, issue a getBladeInfo and getOaInfo my $getBladeBootInfoResult = $hpoa->getBladeBootInfo("bayNumber" => $slot); if ($getBladeBootInfoResult->fault) { return (1, "getBladeBootInfo on node $curn failed"); } # Go through the the IPL Array from the last call to GetBladeStatus my $numberOfIpls = $getBladeBootInfoResult->result->{numberOfIpls}; foreach (my $i = 0 ; $i < $numberOfIpls ; $i++) { foreach (my $j = 0 ; $j <= 7 ; $j++) { if ($getBladeBootInfoResult->result->{ipls}->{ipl}[$j]->{bootPriority} eq ($i + 1)) { push(@order, $getBladeBootInfoResult->result->{ipls}->{ipl}[$j]->{iplDevice}); last; } } } return (0, lc join(',', @order)); } else { foreach (@args) { my @neworder = (split /,/, $_); push @order, @neworder; } my $number = @order; if ($number > 5) { return (1, "Only five boot sequence entries allowed"); } my $nonespecified = 0; my $foundnic = 0; foreach (@order) { if (($bootnumbers{$_} > 4)) { if ($foundnic == 1) { # only one nic allowed. error out return (1, "Only one Eth/Nic device permitted."); } else { $foundnic = 1; } } unless (defined($bootnumbers{$_})) { return (1, "Unsupported device $_"); } unless ($bootnumbers{$_}) { $nonespecified = 1; } if ($nonespecified and $bootnumbers{$_}) { return (1, "Error: cannot specify 'none' before a device"); } } unless ($bootnumbers{ $order[0] }) { return (1, "Error: cannot specify 'none' as first device"); } # Build array to be sent to the blade here my @ipl; my $i = 1; foreach my $dev (@order) { push @ipl, { "bootPriority" => "$i", "iplDevice" => "$bootdevices{$bootnumbers{$order[$i - 1]}}" }; $i++; } my $setiplResponse = $hpoa->setBladeIplBootPriority("bladeIplArray" => [ 'ipl', \@ipl, "" ], "bayNumber" => $slot); if ($setiplResponse->fault) { my $errHash = $setiplResponse->fault; my $result = $setiplResponse->oaErrorText; print "result is $result \n"; return (1, "Error on slot $slot setting ipl"); } return bootseq('list'); } } sub power { my $subcommand = shift; my $command2Send; my $currPowerStat; my $returnState; $returnState = ""; $currPowerStat = $getBladeStatusResponse->result->{powered}; if ($subcommand eq "stat" || $subcommand eq "state") { if ($currPowerStat eq "POWER_ON") { return (0, "on"); } elsif ($currPowerStat eq "POWER_OFF") { return (0, "off"); } } if ($subcommand eq "on") { if ($currPowerStat eq "POWER_OFF") { $command2Send = "MOMENTARY_PRESS"; $returnState = "on"; } else { return (0, "on"); } } elsif ($subcommand eq "off") { if ($currPowerStat eq "POWER_ON") { $command2Send = "PRESS_AND_HOLD"; $returnState = "off"; } else { return (0, "off"); } } elsif ($subcommand eq "reset") { $command2Send = "RESET"; } elsif ($subcommand eq "cycle") { if ($currPowerStat eq "POWER_ON") { power("off"); } $command2Send = "MOMENTARY_PRESS"; } elsif ($subcommand eq "boot") { if ($currPowerStat eq "POWER_OFF") { $command2Send = "MOMENTARY_PRESS"; $returnState = "off on"; } else { $command2Send = "COLD_BOOT"; $returnState = "on reset"; } } elsif ($subcommand eq "softoff") { if ($currPowerStat eq "POWER_ON") { $command2Send = "MOMENTARY_PRESS"; } } #If we got here with a command to send, do it, otherwise just return if ($command2Send) { my $pwrResult = $hpoa->setBladePower('bayNumber' => $slot, 'power' => $command2Send); if ($pwrResult->fault) { return (1, "Node $curn - Power command failed"); } return (0, $returnState); } } sub bladecmd { my $oa = shift; my $node = shift; $slot = shift; my $user = shift; my $pass = shift; my $command = shift; my @args = @_; my $error; if ($slot > 0) { $getBladeStatusResponse = $hpoa->getBladeStatus('bayNumber' => $slot); if ($getBladeStatusResponse->fault) { my $errHash = $getBladeStatusResponse->fault; my $result = $getBladeStatusResponse->oaErrorText; } if ($getBladeStatusResponse->result->{presence} ne "PRESENT") { return (1, "Target bay empty"); } } if ($command eq "rbeacon") { return beacon(@args); } elsif ($command eq "rpower") { return power(@args); } elsif ($command eq "rvitals") { return vitals(@args); } elsif ($command =~ /r[ms]preset/) { return resetmp(@args); } elsif ($command eq "rspconfig") { return iloconfig($oa, $user, $pass, $node, $slot, @args); } elsif ($command eq "rbootseq") { return bootseq(@args); } elsif ($command eq "switchblade") { return switchblade(@args); } elsif ($command eq "getmacs") { return getmacs(@args); } elsif ($command eq "rinv") { return inv(@args); } elsif ($command eq "reventlog") { return eventlog(@args); } elsif ($command eq "rscan") { return rscan(\@args); } return (1, "$command not a supported command by blade method"); } sub forward_data { my $callback = shift; my $fds = shift; my @ready_fds = $fds->can_read(1); my $rfh; my $rc = @ready_fds; foreach $rfh (@ready_fds) { my $data; if ($data = <$rfh>) { while ($data !~ /ENDOFFREEZE6sK4ci/) { $data .= <$rfh>; } print $rfh "ACK\n"; my $responses = thaw($data); foreach (@$responses) { $callback->($_); } } else { $fds->remove($rfh); close($rfh); } } yield; #Try to avoid useless iterations as much as possible return $rc; } sub doblade { my $out = shift; $oa = shift; my $oahash = shift; my $command = shift; my %namedargs = @_; my @exargs = @{ $namedargs{-args} }; my $node; my $args = \@exargs; $hpoa = oaLogin($oa); # We are now logged into the OA and have a pointer to the OA session. Process # the command. #get new node status my %nodestat = (); my $check = 0; my $nsh = {}; foreach $node (sort (keys %{ $oahash->{$oa}->{nodes} })) { $curn = $node; my ($rc, @output) = bladecmd($oa, $node, $oahash->{$oa}->{nodes}->{$node}, $oahash->{$oa}->{username}, $oahash->{$oa}->{password}, $command, @$args); foreach (@output) { my %output; if ($command eq "rscan") { $output{errorcode} = $rc; $output{data} = [$_]; } else { (my $desc, my $text) = split(/:/, $_, 2); unless ($text) { $text = $desc; } else { $desc =~ s/^\s+//; $desc =~ s/\s+$//; if ($desc) { $output{node}->[0]->{data}->[0]->{desc}->[0] = $desc; } } $text =~ s/^\s+//; $text =~ s/\s+$//; $output{node}->[0]->{errorcode} = $rc; $output{node}->[0]->{name}->[0] = $node; $output{node}->[0]->{data}->[0]->{contents}->[0] = $text; } print $out freeze([ \%output ]); print $out "\nENDOFFREEZE6sK4ci\n"; yield; waitforack($out); } yield; } #update the node status to the nodelist.status table if ($check) { my %node_status = (); #foreach (keys %nodestat) { print "node=$_,status=" . $nodestat{$_} ."\n"; } #Ling:remove foreach my $node (keys %nodestat) { my $stat = $nodestat{$node}; if ($stat eq "no-op") { next; } if (exists($node_status{$stat})) { my $pa = $node_status{$stat}; push(@$pa, $node); } else { $node_status{$stat} = [$node]; } } xCAT_monitoring::monitorctrl::setNodeStatusAttributes(\%node_status, 1); } #my $msgtoparent=freeze(\@outhashes); # = XMLout(\%output,RootName => 'xcatresponse'); #print $out $msgtoparent; #$node.": $_\n"; } sub extractDate { use Time::Local; my $date = shift; return 0 unless $date =~ m/(\d{1,2})\/(\d{1,2})\/(\d{4}) (\d{1,2}):(\d{1,2})/; return timegm(0, $5, $4, $2, $1, $3); } 1;