#!/usr/bin/env perl # IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html package xCAT_plugin::blade; #use Net::SNMP qw(:snmp INTEGER); use xCAT::Table; use Thread qw(yield); use xCAT::Utils; use IO::Socket; use SNMP; use strict; use XML::Simple; use Data::Dumper; use POSIX "WNOHANG"; use Storable qw(freeze thaw); use IO::Select; use IO::Handle; use Time::HiRes qw(gettimeofday); sub handled_commands { return { findme => 'blade', getmacs => 'nodehm:getmacs,mgt', rscan => 'nodehm:mgt', rpower => 'nodehm:power,mgt', rvitals => 'nodehm:vitals,mgt', rinv => 'nodehm:inv,mgt', rbeacon => 'nodehm:beacon,mgt', rspreset => 'nodehm:mgt', rbootseq => 'nodehm:bootseq,mgt', reventlog => 'nodehm:eventlog,mgt', }; } my %usage = ( "rpower" => "Usage: rpower [-d][on|off|reset|stat|boot]", "rbeacon" => "Usage: rbeacon [on|off|stat]", "rvitals" => "Usage: rvitals [all|temp|voltage|fanspeed|power|leds]", "reventlog" => "Usage: reventlog [all|clear|]", "rinv" => "Usage: rinv [all|model|serial|vpd|mprom|deviceid|uuid]", "rbootseq" => "Usage: rbootseq [hd0|hd1|hd2|hd3|net|iscsi|usbflash|floppy|none],...", "rscan" => "Usage: rscan [-w][-x|-z]" ); my %macmap; #Store responses from rinv for discovery my $macmaptimestamp; #reflect freshness of cache my $mmprimoid = '1.3.6.1.4.1.2.3.51.2.22.5.1.1.4';#mmPrimary my $beaconoid = '1.3.6.1.4.1.2.3.51.2.2.8.2.1.1.11'; #ledBladeIdentity my $powerstatoid = '1.3.6.1.4.1.2.3.51.2.22.1.5.1.1.4';#bladePowerState my $powerchangeoid = '1.3.6.1.4.1.2.3.51.2.22.1.6.1.1.7';#powerOnOffBlade my $powerresetoid = '1.3.6.1.4.1.2.3.51.2.22.1.6.1.1.8';#restartBlade my $mpresetoid = '1.3.6.1.4.1.2.3.51.2.22.1.6.1.1.9'; #restartBladeSMP my $bladexistsoid = '1.3.6.1.4.1.2.3.51.2.22.1.5.1.1.3'; #bladeExists my $bladeserialoid = '1.3.6.1.4.1.2.3.51.2.2.21.4.1.1.6'; #bladeHardwareVpdSerialNumber my $blademtmoid = '1.3.6.1.4.1.2.3.51.2.2.21.4.1.1.7'; #bladeHardwareVpdMachineType my $bladempveroid = '1.3.6.1.4.1.2.3.51.2.2.21.5.3.1.7'; #bladeSysMgmtProcVpdRevision my $bladempaveroid = '1.3.6.1.4.1.2.3.51.2.2.21.3.1.1.4';#mmMainApplVpdRevisonNumber my $bladempabuildidoid = '1.3.6.1.4.1.2.3.51.2.2.21.3.1.1.3';#mmMainApplVpdBuildId my $bladempadateoid = '1.3.6.1.4.1.2.3.51.2.2.21.3.1.1.6';#mmMainApplVpdBuildDate my $bladempbuildidoid = '1.3.6.1.4.1.2.3.51.2.2.21.5.3.1.6'; #bladeSysMgmtProcVpdBuildId my $bladebiosveroid = '1.3.6.1.4.1.2.3.51.2.2.21.5.1.1.7'; #bladeBiosVpdRevision my $bladebiosbuildidoid = '1.3.6.1.4.1.2.3.51.2.2.21.5.1.1.6'; #bladeBiosVpdBuildId my $bladebiosdateoid = '1.3.6.1.4.1.2.3.51.2.2.21.5.1.1.8'; #bladeBiosVpdDate my $bladediagveroid = '1.3.6.1.4.1.2.3.51.2.2.21.5.2.1.7'; #bladeDiagsVpdRevision my $bladediagbuildidoid = '1.3.6.1.4.1.2.3.51.2.2.21.5.2.1.6'; #bladeDiagsVpdBuildId my $bladediagdateoid = '1.3.6.1.4.1.2.3.51.2.2.21.5.2.1.8';#bladeDiagsVpdDate my $eventlogoid = '1.3.6.1.4.1.2.3.51.2.3.4.2.1.2';#readEventLogString my $clearlogoid = '.1.3.6.1.4.1.2.3.51.2.3.4.3';#clearEventLog my $blower1speedoid = '.1.3.6.1.4.1.2.3.51.2.2.3.1';#blower2speed my $blower2speedoid = '.1.3.6.1.4.1.2.3.51.2.2.3.2';#blower2speed my $blower3speedoid = '.1.3.6.1.4.1.2.3.51.2.2.3.3';#blower2speed my $blower4speedoid = '.1.3.6.1.4.1.2.3.51.2.2.3.4';#blower2speed my $blower1stateoid = '.1.3.6.1.4.1.2.3.51.2.2.3.10';#blower1State my $blower2stateoid = '.1.3.6.1.4.1.2.3.51.2.2.3.11';#blower2State my $blower3stateoid = '.1.3.6.1.4.1.2.3.51.2.2.3.12';#blower2State my $blower4stateoid = '.1.3.6.1.4.1.2.3.51.2.2.3.13';#blower2State my $mmoname = '1.3.6.1.4.1.2.3.51.2.22.4.3';#chassisName my $mmotype = '1.3.6.1.4.1.2.3.51.2.2.21.1.1.1';#bladeCenterVpdMachineType my $mmomodel = '1.3.6.1.4.1.2.3.51.2.2.21.1.1.2';#bladeCenterVpdMachineModel my $mmoserial = '1.3.6.1.4.1.2.3.51.2.2.21.1.1.3';#bladeCenterSerialNumber my $bladeoname = '1.3.6.1.4.1.2.3.51.2.22.1.5.1.1.6';#bladeName my $bladeomodel = '1.3.6.1.4.1.2.3.51.2.2.21.4.1.1.12';#bladeModel my @macoids = ( '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.2', #bladeMACAddress1Vpd '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.3', #bladeMACAddress2Vpd '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.4', #bladeMACAddress3Vpd '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.5', #bladeMACAddress4Vpd ); my @dcmacoids = ( '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.10', #bladeDaughterCard1MACAddress1Vpd '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.11', #bladeDaughterCard1MACAddress2Vpd '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.12', #bladeDaughterCard1MACAddress3Vpd '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.13', #bladeDaughterCard1MACAddress4Vpd ); my @hsdcmacoids = ( '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.100', #bladeHSDaughterCard1MACAddress1Vpd '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.101', #bladeHSDaughterCard1MACAddress2Vpd '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.102', #bladeHSDaughterCard1MACAddress3Vpd '1.3.6.1.4.1.2.3.51.2.2.21.4.2.1.103', #bladeHSDaughterCard1MACAddress4Vpd ); my @bootseqoids = ( '1.3.6.1.4.1.2.3.51.2.22.1.3.1.7', #bootSequence1 '1.3.6.1.4.1.2.3.51.2.22.1.3.1.8', #bootSequence2 '1.3.6.1.4.1.2.3.51.2.22.1.3.1.9', #bootSequence3 '1.3.6.1.4.1.2.3.51.2.22.1.3.1.10', #bootSequence4 ); my %bootdevices = ( 0 => 'none', 1 => 'floppy', 2 => 'cdrom', 3 => 'hd0', 4 => 'hd1', 5 => 'hd2', 6 => 'hd3', 7 => 'net', 8 => 'iscsi', 9 => 'iscsicrit', 10 => 'hd4', 11 => 'usbflash' ); my %bootnumbers = ( 'none' => 0, 'f' => 1, 'floppy' => 1, 'c' => 2, 'cd' => 2, 'dvd' => 2, 'cdrom' => 2, 'dvdrom' => 2, 'h' => 3, #in absence of an index, presuming hd0 intended 'hd' => 3, 'hardisk' => 3, 'hd0' => 3, 'harddisk0' => 3, 'hd1' => 4, 'harddisk1' => 4, 'hd2' => 5, 'harddisk2' => 5, 'hd3' => 6, 'harddisk3' => 6, 'n' => 7, 'network' => 7, 'net' => 7, 'iscsi' => 8, 'iscsicrit' => 9, 'hd4' => 10, 'harddisk4' => 10, 'usbflash' => 11, 'flash' => 11, 'usb' => 11 ); my @rscan_header = ( ["type", "%-8s" ], ["name", "" ], ["id", "%-8s" ], ["type-model", "%-12s" ], ["serial-number", "%-15s" ], ["address", "%s\n" ]); my $session; my $slot; my $didchassis = 0; my @eventlog_array = (); my $activemm; my %mpahash; my $mpa; my $allinchassis=0; my $curn; sub fillresps { my $response = shift; my $mac = $response->{node}->[0]->{data}->[0]->{contents}->[0]; my $node = $response->{node}->[0]->{name}->[0]; $macmap{$mac} = $node; #$macmap{$response->{node}->[0]->{data}->{contents}->[0]}=$response->{node}->[0]->{name}; } sub isallchassis { my $bladesinchassis = 0; if ($allinchassis) { return 1; } foreach (1..14) { my $tmp = $session->get([$bladexistsoid.".$_"]); if ($tmp eq 1) { $bladesinchassis++ } } my $count = keys %{$mpahash{$mpa}->{nodes}}; if ($count >= $bladesinchassis) { $allinchassis++; return 1 }; #commands that affect entire are okayed, i.e eventlog clear return 0; } sub resetmp { my $data; my $stat; my $rc; #$data = $session->set($mpresetoid.".$slot", 1); $data = $session->set(new SNMP::Varbind([".".$mpresetoid,$slot,1,'INTEGER'])); unless ($data) { return (1,$session->{ErrorStr}); } return (0,"mpreset"); #if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } #if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } #if ($data->{$mpresetoid.".$slot"} == 1) { # return (0, "mpreset"); #} else { # return (1,"error"); #} } sub walkelog { my $session = shift; my $oid = shift; unless ($oid =~ /^\./) { $oid = '.'.$oid; } my $retmap = undef; my $current = 1; my @bindlist; my $varbind; do { foreach ($current..$current+31) { #Attempt to retrive 32 ents at a time, seems to be working... push @bindlist,[$oid,$_]; } $current+=32; $varbind = new SNMP::VarList( @bindlist ); $session->get($varbind); foreach(@$varbind) { unless (${_}->[2]) {last;} if( ${_}->[2] =~ /NOSUCHINSTANCE/) {last;} $retmap->{$_->[1]}=$_->[2]; } } while ($varbind->[31] and $varbind->[31]->[2] != 'NOSUCHINSTANCE' and ($current < 600)); return $retmap; print "Count was $current\n"; #print Dumper($varbind->[60]->[2]); print "\n\n"; return undef; my $count=0; while ($varbind->[0] =~ /^$oid\.?(.*)/) { $count++; if ($1) { $retmap->{$1.".".$varbind->[1]}=$varbind->[2]; #If $1 is set, means key should } else { $retmap->{$varbind->[1]}=$varbind->[2]; #If $1 is set, means key should } $session->getnext($varbind); } return $retmap; } sub eventlog { #Tried various optimizations, but MM seems not to do bulk-request #TODO: retrieval of non blade events, what should be syntax? #TODO: try retrieving 5 at a time, then 1 at a time when that stops working my $cmd=shift; my $data; my @output; my $oid = $eventlogoid; if ($cmd eq 'all') { $cmd=65535; #no MM has this many logs possible, should be a good number } if ($cmd =~ /^(\d+)$/) { my $requestednumber=$1; unless (@eventlog_array) { #my $varbind=new SNMP::Varbind([$oid,0]); #while ($data=$session->getnext($varbind)) { # print Dumper($data); # if ($session->{ErrorStr}) { printf $session->{ErrorStr}."\n"; } # foreach (keys %$data) { # $oid=$_; # } # unless (oid_base_match($eventlogoid,$oid)) { # last; # } my $logents = walkelog($session,$oid); foreach (sort {$a <=> $b} (keys %$logents)) { push @eventlog_array,$logents->{$_}."\n"; } #push @eventlog_array,$data->{$oid}; #TODO: filter against slot number, check for $allchassis for non-blade #} } my $numentries=0; #my $allchassis = isallchassis; foreach (@eventlog_array) { m/Severity:(\S+)\s+Source:(\S+)\s+Name:\S*\s+Date:(\S+)\s+Time:(\S+)\s+Text:(.+)/; my $sev=$1; my $source=$2; my $date=$3; my $time=$4; my $text=$5; my $matchstring; if ($slot > 0) { $matchstring=sprintf("BLADE_%02d",$slot); } else { $matchstring="^(?!BLADE).*"; } if ($source =~ m/$matchstring$/i) { #MM guys changed their minds on capitalization $numentries++; unshift @output,"$sev:$date $time $text"; #unshift to get it in a sane order } if ($numentries >= $requestednumber) { last; } } return (0,@output); } my $data; if ($cmd eq "clear") { unless (isallchassis) { return (1,"Cannot clear eventlogs except for entire chassis"); } if ($didchassis) { return 0, "eventlog cleared" } my $varbind = new SNMP::Varbind([$clearlogoid,0,1,'INTEGER']); $data = $session->set($varbind); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } $didchassis=1; if ($varbind->[2] eq 1) { return 0, "eventlog cleared"; } } } sub bootseq { my @args=@_; my $data; my @order=(); if ($args[0] eq "list" or $args[0] eq "stat") { foreach my $oid (@bootseqoids) { $data=$session->get([$oid,$slot]); if ($session->{ErrorStr}) { return (1, $session->{ErrorStr}); } push @order,$bootdevices{$data}; } return (0,join(',',@order)); } else { foreach (@args) { my @neworder=(split /,/,$_); push @order,@neworder; } my $number=@order; if ($number > 4) { return (1,"Only four boot sequence entries allowed"); } my $nonespecified=0; foreach (@order) { 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"); } foreach (3,2,1,0) { my $param = $bootnumbers{$order[$_]}; unless ($param) { $param = 0; my $varbind = new SNMP::Varbind([$bootseqoids[$_],$slot,$param,'INTEGER']); $data = $session->set($varbind); #$session->set($bootseqoids[$_].".$slot",$param); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } } } foreach (0,1,2,3) { my $param = $bootnumbers{$order[$_]}; if ($param) { my $varbind = new SNMP::Varbind([$bootseqoids[$_],$slot,$param,'INTEGER']); $data = $session->set($varbind); #$session->set($bootseqoids[$_].".$slot",$param); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } } } return bootseq('list'); } } sub vitals { my @output; my $tmp; my @vitems; foreach (@_) { if ($_ eq 'all') { push @vitems,qw(temp,wattage,voltage,fan,summary); } else { push @vitems,split( /,/,$_); } } my $tmp; if (grep /watt/,@vitems) { if ($slot < 8) { $tmp = $session->get(["1.3.6.1.4.1.2.3.51.2.2.10.2.1.1.7.".($slot+16)]); } else { $tmp = $session->get(["1.3.6.1.4.1.2.3.51.2.2.10.3.1.1.7.".($slot+9)]); } unless ($tmp =~ /Not Readable/) { if ($tmp =~ /(\d+)W/) { $tmp = "$1 Watts (". int($tmp * 3.413+0.5)." BTUs/hr)"; } $tmp =~ s/^/Power Usage:/; push @output,"$tmp"; } } if (grep /fan/,@vitems or grep /blower/,@vitems) { $tmp=$session->get(['1.3.6.1.4.1.2.3.51.2.2.3.1.0']); push @output,"Blower 1: $tmp"; $tmp=$session->get(['1.3.6.1.4.1.2.3.51.2.2.3.2.0']); push @output,"Blower 2: $tmp"; $tmp=$session->get(['1.3.6.1.4.1.2.3.51.2.2.3.3.0']); if ($tmp and $tmp !~ /NOSUCHINSTANCE/) { push @output,"Blower 3: $tmp"; } $tmp=$session->get(['1.3.6.1.4.1.2.3.51.2.2.3.4.0']); if ($tmp and $tmp !~ /NOSUCHINSTANCE/) { push @output,"Blower 4: $tmp"; } $tmp=$session->get(['1.3.6.1.4.1.2.3.51.2.2.6.1.1.5.1']); push @output,"Fan Pack 1: $tmp"; $tmp=$session->get(['1.3.6.1.4.1.2.3.51.2.2.6.1.1.5.2']); push @output,"Fan Pack 2: $tmp"; $tmp=$session->get(['1.3.6.1.4.1.2.3.51.2.2.6.1.1.5.3']); push @output,"Fan Pack 3: $tmp"; $tmp=$session->get(['1.3.6.1.4.1.2.3.51.2.2.6.1.1.5.4']); push @output,"Fan Pack 4: $tmp"; } if (grep /volt/,@vitems) { for my $idx (15..40) { $tmp=$session->get([".1.3.6.1.4.1.2.3.51.2.22.1.5.5.1.$idx.$slot"]); unless ((not $tmp) or $tmp =~ /Not Readable/) { $tmp =~ s/ = /:/; push @output,"$tmp"; } } } if (grep /temp/,@vitems) { $tmp=$session->get(["1.3.6.1.4.1.2.3.51.2.2.1.5.1.0"]); push (@output,"Ambient: $tmp"); for my $idx (6..20) { if ($idx eq 11) { next; } $tmp=$session->get([".1.3.6.1.4.1.2.3.51.2.22.1.5.3.1.$idx.$slot"]); unless ($tmp =~ /Not Readable/) { $tmp =~ s/ = /:/; push @output,"$tmp"; } } } if (grep /summary/,@vitems) { $tmp="Status: ".$session->get(['1.3.6.1.4.1.2.3.51.2.22.1.5.2.1.3.'.$slot]); $tmp.=", ".$session->get(['1.3.6.1.4.1.2.3.51.2.22.1.5.2.1.4.'.$slot]); push @output,"$tmp"; } return(0,@output); } sub rscan { my $subcommand = shift; my @values; my $result; my %opt; @ARGV = $subcommand; use Getopt::Long; $Getopt::Long::ignorecase = 0; Getopt::Long::Configure( "bundling" ); if ( !GetOptions( \%opt, qw(h|help V|Verbose v|version w x z) )){ return( usage() ); } if ( exists($opt{x}) and exists($opt{z}) ) { return (1,"-x and -z are mutually exclusive" ); } my $mmname = $session->get([$mmoname,0]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } my $mmtype = $session->get([$mmotype,0]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } my $mmmodel = $session->get([$mmomodel,0]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } my $mmserial = $session->get([$mmoserial,0]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } push @values, join( ",", "mm", $mmname, 0, "$mmtype-$mmmodel", $mmserial, $mpa); my $max = length( $mmname ); foreach (1..14) { my $tmp = $session->get([$bladexistsoid.".$_"]); if ( $tmp eq 1 ) { my $type = $session->get([$blademtmoid,$_]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } $type =~ s/Not available/null/; my $model = $session->get([$bladeomodel,$_]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } $model =~ s/Not available/null/; my $serial = $session->get([$bladeserialoid,$_]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } $serial =~ s/Not available/null/; my $name = $session->get([$bladeoname,$_]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } push @values, join( ",", "blade", $name, $_, "$type-$model", $serial, ""); my $length = length( $name ); $max = ($length > $max) ? $length : $max; } } my $format = sprintf "%%-%ds", ($max + 2 ); $rscan_header[1][1] = $format; if ( exists( $opt{x} )) { $result = rscan_xml( \@values ); } elsif ( exists( $opt{z} )) { $result = rscan_stanza( \@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} = $mpa; $u1->{id} = $data[2]; $db{mp}->setAttribs( $k1, $u1 ); $db{mp}{commit} = 1; my ($k2,$u2); $k2->{node} = $name; $u2->{mgt} = "blade"; $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 $values = shift; my $xml; foreach ( @$values ) { my @data = split /,/; my $i = 0; my $href = { Node => { } }; foreach ( @rscan_header ) { $href->{Node}->{@$_[0]} = $data[$i++]; } $xml.= XMLout($href, NoAttr => 1, KeyAttr => [], RootName => undef ); } return( $xml ); } sub rscan_stanza { my $values = shift; my $result; foreach ( @$values ) { my @data = split /,/; my $i = 0; $result .= "$data[1]:\n\tobjtype=node\n"; foreach ( @rscan_header ) { if ( @$_[0] ne "name" ) { $result .= "\t@$_[0]=$data[$i++]\n"; } $i++; } } return( $result ); } sub getmacs { (my $code,my $macs)=inv('mac'); if ($code==0) { my @macs = split /\n/,$macs; (my $macd,my $mac) = split (/:/,$macs[0],2); $mac =~ s/\s+//g; if ($macd =~ /mac address 1/i) { my $mactab = xCAT::Table->new('mac',-create=>1); $mactab->setNodeAttribs($curn,{mac=>$mac}); $mactab->close; return 0,":mac.mac set to $mac"; } else { return 1,"confusing situation"; } } else { return $code,$macs; } } sub inv { my @invitems; my $data; my @output; foreach (@_) { push @invitems,split( /,/,$_); } my $item; while (my $item = shift @invitems) { if ($item =~ /^all/) { push @invitems,(qw(mtm serial mac firm)); next; } if ($item =~ /^firm/) { push @invitems,(qw(bios diag mprom mparom)); next; } if ($item =~ /^bios/) { my $biosver; my $biosbuild; my $biosdate; $biosver=$session->get([$bladebiosveroid.".$slot"]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } $biosbuild=$session->get([$bladebiosbuildidoid.".$slot"]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } $biosdate=$session->get([$bladebiosdateoid.".$slot"]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } push @output,"BIOS: $biosver ($biosbuild $biosdate)"; } if ($item =~ /^diag/) { my $diagver; my $diagdate; my $diagbuild; $data=$session->get([$bladediagveroid,$slot]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } $diagver = $data; $data=$session->get([$bladediagbuildidoid,$slot]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } $diagbuild = $data; $data=$session->get([$bladediagdateoid,$slot]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } $diagdate = $data; push @output,"Diagnostics: $diagver ($diagbuild $diagdate)"; } if ($item =~ /^[sm]prom/) { my $spver; my $spbuild; $data=$session->get([$bladempveroid,$slot]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } $spver=$data; $data=$session->get([$bladempbuildidoid,$slot]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } $spbuild=$data; push @output,"BMC/Mgt processor: $spver ($spbuild)"; } if ($item =~ /^mparom/) { my $mpabuild; my $mpaver; my $mpadate; $data=$session->get([$bladempaveroid,$activemm]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } $mpaver=$data; $data=$session->get([$bladempabuildidoid,$activemm]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } $mpabuild=$data; $data=$session->get([$bladempadateoid,$activemm]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } $mpadate=$data; push @output,"Management Module firmware: $mpaver ($mpabuild $mpadate)"; } if ($item =~ /^model/ or $item =~ /^mtm/) { $data=$session->get([$blademtmoid,$slot]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } push @output,"Machine Type/Model: ".$data; } if ($item =~ /^serial/) { $data=$session->get([$bladeserialoid,$slot]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } push @output,"Serial Number: ".$data; } if ($item =~ /^mac/) { foreach (0..3) { $data=$session->get([$macoids[$_],$slot]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } if ($data =~ /:/) { push @output,"MAC Address ".($_+1).": ".$data; } } foreach (0..3) { my $oid=$hsdcmacoids[$_].".$slot"; $data=$session->get([$hsdcmacoids[$_],$slot]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } if ($data =~ /:/) { push @output,"HS Daughter card MAC Address ".($_+1).": ".$data; } } foreach (0..3) { $data=$session->get([$dcmacoids[$_],$slot]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } if ($data =~ /:/) { push @output,"Daughter card 1 MAC Address ".($_+1).": ".$data; } } } } return (0,@output); } sub power { my $subcommand = shift; my $data; my $stat; unless ($slot > 0) { if ($subcommand eq "reset" or $subcommand eq "boot") { $data = $session->set(new SNMP::Varbind([".1.3.6.1.4.1.2.3.51.2.7.4",0,1,'INTEGER'])); unless ($data) { return (1,$session->{ErrorStr}); } return (0,"reset"); } else { return (1,"$subcommand unsupported on the management module"); } } if ($subcommand eq "stat" or $subcommand eq "boot") { $data = $session->get([$powerstatoid.".".$slot]); if ($data == 1) { $stat = "on"; } elsif ( $data == 0) { $stat = "off"; } else { $stat= "error"; } } elsif ($subcommand eq "off") { $data = $session->set(new SNMP::Varbind([".".$powerchangeoid,$slot,0,'INTEGER'])); unless ($data) { return (1,$session->{ErrorStr}); } $stat = "off"; } if ($subcommand eq "on" or ($subcommand eq "boot" and $stat eq "off")) { $data = $session->set(new SNMP::Varbind([".".$powerchangeoid,$slot,1,'INTEGER'])); unless ($data) { return (1,$session->{ErrorStr}); } $stat = ($data ? "on" : "off"); } elsif ($subcommand eq "reset" or ($subcommand eq "boot" and $stat eq "on")) { $data = $session->set(new SNMP::Varbind([".".$powerresetoid,$slot ,1,'INTEGER'])); unless ($data) { return (1,$session->{ErrorStr}); } $stat = "reset"; } if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } if ($stat) { return (0,$stat); } } sub beacon { my $subcommand = shift; my $data; if ($subcommand eq "stat") { } elsif ($subcommand eq "on") { $data = $session->set(new SNMP::Varbind([$beaconoid,$slot , 1,'INTEGER'])); } elsif ($subcommand eq "off") { $data = $session->set(new SNMP::Varbind([$beaconoid,$slot , 0,'INTEGER'])); } elsif ($subcommand eq "blink") { $data = $session->set(new SNMP::Varbind([$beaconoid,$slot , 2,'INTEGER'])); } else { return (1,"$subcommand unsupported"); } my $stat = $session->get([$beaconoid.".".$slot]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } if ($stat==0) { return (0,"off"); } elsif ($stat==1) { return (0,"on"); } elsif ($stat==2) { return (0,"blink"); } elsif ($stat==3) { return (0,"unsupported"); } } sub bladecmd { $mpa = shift; $slot = shift; #my $user = shift; #my $pass = shift; my $command = shift; my @args = @_; my $error; if ($slot > 0) { my $tmp = $session->get([$bladexistsoid.".$slot"]); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } unless ($tmp eq 1) { 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 "rbootseq") { return bootseq(@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 handle_depend { my $request = shift; my $callback = shift; my $doreq = shift; my $dep = shift; my %node = (); # send all dependencies (along w/ those dependent on nothing) $request->{node} = [keys %$dep]; process_request($request,$callback,$doreq); 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) { %mpahash = (); $request->{node} = \@noderange; process_request($request,$callback,$doreq); } # millisecond sleep Time::HiRes::sleep($delay); } return 0; } sub build_depend { my $noderange = shift; my $exargs = shift; my $depstab = xCAT::Table->new('deps'); my %dp = (); my %no_dp = (); if (!defined($depstab)) { return(\%dp); } foreach my $node (@$noderange) { my $delay = 0; my $dep; my $cmd; my $ent=$depstab->getNodeAttribs($node,[qw(nodedep msdelay cmd)]); if (defined($ent->{nodedep})) { $dep=$ent->{nodedep}; } if (defined($ent->{cmd})) { $cmd=$ent->{cmd}; } if (defined($ent->{msdelay})) { $delay=$ent->{msdelay}; } if (!defined($dep)) { $no_dp{$node} = 1; } elsif ( grep(/^@$exargs[0]$/, split /,/, $cmd )) { foreach (split /,/,$dep ) { $dp{$_}{$node} = $delay; } } # if there are dependencies, add any non-dependent nodes if (scalar(%dp)) { foreach (keys %no_dp) { if (!exists( $dp{$_} )) { $dp{$_}{$_} = -1; } } } } return( \%dp ); } sub process_request { my $request = shift; my $callback = shift; my $doreq = shift; my $noderange = $request->{node}; my $command = $request->{command}->[0]; my @exargs; unless ($noderange or $command eq "findme") { if ($usage{$command}) { $callback->({data=>$usage{$command}}); $request = {}; } return; } if (ref($request->{arg})) { @exargs = @{$request->{arg}}; } else { @exargs = ($request->{arg}); } if ($command eq "rpower" and grep(/^on|off|boot|reset$/, @exargs)) { if (grep /^-d$/, @exargs) { # handles 1 level of dependencies only @{$request->{arg}} = grep(!/^-d$/, @{$request->{arg}}); @exargs = @{$request->{arg}}; my $dep = build_depend($noderange,\@exargs); if (scalar(%$dep)) { handle_depend( $request, $callback, $doreq, $dep ); return 0; } } } 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'); if (defined($tmp)) { $blademaxp=$tmp->{value}; } } 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") { 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 = `/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(); process_request(\%invreq,\&fillresps); } unless ($macmap{$mac}) { return 1; #failure } 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); $request->{command}=['discovered']; $request->{noderange} = [$macmap{$mac}]; $doreq->($request); %{$request}=(); #Clear request. it is done undef $mactab; return 0; } my $children = 0; $SIG{CHLD} = sub { while (waitpid(-1, WNOHANG) > 0) { $children--; } }; my $inputs = new IO::Select;; foreach (@$noderange) { my $node=$_; my $user=$bladeuser; my $pass=$bladepass; my $nodeid; my $mpa; my $ent; if (defined($mptab)) { $ent=$mptab->getNodeAttribs($node,['mpa','id']); if (defined($ent->{mpa})) { $mpa=$ent->{mpa}; } if (defined($ent->{id})) { $nodeid = $ent->{id}; } } if (defined($mpatab)) { ($ent)=$mpatab->getAttribs({'mpa'=>$mpa},'username','password'); if (defined($ent->{password})) { $pass = $ent->{password}; } if (defined($ent->{username})) { $user = $ent->{username}; } } $mpahash{$mpa}->{nodes}->{$node}=$nodeid; $mpahash{$mpa}->{username} = $user; $mpahash{$mpa}->{password} = $pass; } my $sub_fds = new IO::Select; foreach $mpa (sort (keys %mpahash)) { while ($children > $blademaxp) { sleep (0.1); } $children++; my $cfd; my $pfd; pipe $cfd, $pfd; $cfd->autoflush(1); $pfd->autoflush(1); my $cpid = xCAT::Utils->xfork; unless (defined($cpid)) { die "Fork error"; } unless ($cpid) { close($cfd); dompa($pfd,$mpa,\%mpahash,$command,-args=>@exargs); exit(0); } close ($pfd); $sub_fds->add($cfd); } while ($children > 0) { forward_data($callback,$sub_fds); } while (forward_data($callback,$sub_fds)) {} } 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>; } 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 dompa { my $out = shift; $mpa = shift; my $mpahash = shift; my $command=shift; my %namedargs=@_; my @exargs=$namedargs{-args}; my $node; $session = new SNMP::Session( DestHost => $mpa, Version => '3', SecName => $mpahash->{$mpa}->{username}, AuthProto => 'SHA', AuthPass => $mpahash->{$mpa}->{password}, PrivProto => 'DES', SecLevel => 'authPriv', UseNumeric => 1, Retries => 2, # Give up sooner to make commands go smoother Timeout=>1300000, #Beacon, for one, takes a bit over a second to return PrivPass => $mpahash->{$mpa}->{password}); if ($session->{ErrorStr}) { return 1,$session->{ErrorStr}; } unless ($session and keys %$session) { my %err=(node=>[]); foreach (keys %{$mpahash{$mpa}->{nodes}}) { push (@{$err{node}},{name=>[$_],error=>["Cannot communicate with $mpa"],errorcode=>[1]}); } print $out freeze([\%err]); print $out "\nENDOFFREEZE6sK4ci\n"; return 1,"General error establishing SNMP communication"; } my $tmp = $session->get([$mmprimoid.".1"]); if ($session->{ErrorStr}) { print $session->{ErrorStr}; } $activemm = ($tmp ? 1 : 2); my @outhashes; if ($command eq "reventlog" and isallchassis) { #Add a dummy node for eventlog to get non-blade events $mpahash{$mpa}->{nodes}->{$mpa}=-1; } foreach $node (sort (keys %{$mpahash->{$mpa}->{nodes}})) { $curn = $node; my ($rc,@output) = bladecmd($mpa,$mpahash->{$mpa}->{nodes}->{$node},$command,@exargs); my @output_hashes; foreach(@output) { my %output; if ( $command eq "rscan" ) { $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]->{name}->[0]=$node; $output{node}->[0]->{data}->[0]->{contents}->[0]=$text; } print $out freeze([\%output]); print $out "\nENDOFFREEZE6sK4ci\n"; } } #my $msgtoparent=freeze(\@outhashes); # = XMLout(\%output,RootName => 'xcatresponse'); #print $out $msgtoparent; #$node.": $_\n"; } 1;