#!/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 xCAT::Usage; use IO::Socket; use SNMP; use strict; my %mm_comm_pids; use XML::Simple; if ($^O =~ /^linux/i) { $XML::Simple::PREFERRED_PARSER='XML::Parser'; } use Data::Dumper; use POSIX "WNOHANG"; use Storable qw(freeze thaw); use IO::Select; use IO::Handle; use Time::HiRes qw(gettimeofday sleep); use Net::Telnet; use xCAT::DBobjUtils; use Getopt::Long; sub handled_commands { return { findme => 'blade', getmacs => 'nodehm:getmac,mgt', rscan => 'nodehm:mgt', rpower => 'nodehm:power,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 %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]; $mac = uc($mac); #Make sure it is uppercase, the MM people seem to change their mind on this.. $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 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 } } } 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; unless ($cmd) { $cmd='all'; } 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 setoid { my $oid = shift; my $offset = shift; my $value = shift; my $type = shift; unless ($type) { $type = 'INTEGER'; } my $varbind = new SNMP::Varbind([$oid,$offset,$value,$type]); my $data = $session->set($varbind); if ($session->{ErrorStr}) { return (1,$session->{ErrorStr}); } return 0,$varbind; } sub enabledefaultalerts { #Customizers: most oids are listed, and some commented out. uncomment if you want to get them #deprecated options are in, but commented, will elect to use what the MM official strategy suggests my @enabledalerts = ( #Deprecated '1.3.6.1.4.1.2.3.51.2.4.2.1.1', #critical temperature #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.1.2', #critical voltage #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.1.4', #critical blower '1.3.6.1.4.1.2.3.51.2.4.2.1.5', #critical power #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.1.6', #critical Hard drive #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.1.7', #critical VRM #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.1.8', #critical switch module #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.1.9', #critical config '1.3.6.1.4.1.2.3.51.2.4.2.1.10', #critical blade '1.3.6.1.4.1.2.3.51.2.4.2.1.11', #critical IO '1.3.6.1.4.1.2.3.51.2.4.2.1.12', #critical storage '1.3.6.1.4.1.2.3.51.2.4.2.1.13', #critical chassis '1.3.6.1.4.1.2.3.51.2.4.2.1.14', #critical fan #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.2.2', #warn single blower #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.2.3', #warn temp #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.2.4', #warn volt #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.2.6', #warn backup MM #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.2.7', #warn tray/KVM switch prob '1.3.6.1.4.1.2.3.51.2.4.2.2.10', #warn log full '1.3.6.1.4.1.2.3.51.2.4.2.2.15', #warn blade warning '1.3.6.1.4.1.2.3.51.2.4.2.2.16', #warn io warning '1.3.6.1.4.1.2.3.51.2.4.2.2.17', #warn storage warning '1.3.6.1.4.1.2.3.51.2.4.2.2.18', #warn power module '1.3.6.1.4.1.2.3.51.2.4.2.2.19', #warn chassis '1.3.6.1.4.1.2.3.51.2.4.2.2.20', #warn cooling #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.3.4', #info power off #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.3.5', #info power on #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.3.8', #info PFA '1.3.6.1.4.1.2.3.51.2.4.2.3.10', #info inventory (insert/remove) '1.3.6.1.4.1.2.3.51.2.4.2.3.11', #info 75% events '1.3.6.1.4.1.2.3.51.2.4.2.3.12', #info net reconfig #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.3.13', #info throttling #deprecated '1.3.6.1.4.1.2.3.51.2.4.2.3.14', #info power management #annoying '1.3.6.1.4.1.2.3.51.2.4.2.3.15', #info login events '1.3.6.1.4.1.2.3.51.2.4.2.3.16', #info blade events '1.3.6.1.4.1.2.3.51.2.4.2.3.17', #info IO events '1.3.6.1.4.1.2.3.51.2.4.2.3.18', #info storage events '1.3.6.1.4.1.2.3.51.2.4.2.3.19', #info power module events '1.3.6.1.4.1.2.3.51.2.4.2.3.20', #info chassis events '1.3.6.1.4.1.2.3.51.2.4.2.3.21', #info blower event '1.3.6.1.4.1.2.3.51.2.4.2.3.22', #info power on/off ); setoid('1.3.6.1.4.1.2.3.51.2.4.2.4',0,1); foreach (@enabledalerts) { setoid($_,0,1); } } my @cfgtext; sub mpaconfig { #OIDs of interest: #1.3.6.1.4.1.2.3.51.2.4.9.3.1.4.1.1.4 snmpCommunityEntryCommunityIpAddress2 #snmpCommunityEntryCommunityName 1.3.6.1.4.1.2.3.51.2.4.9.3.1.4.1.1.2 #remoteAlerts 1.3.6.1.4.1.2.3.51.2.4.2 #remoteAlertIdEntryTextDescription 1.3.6.1.4.1.2.3.51.2.4.1.3.1.1.4 #remoteAlertIdEntryStatus 1.3.6.1.4.1.2.3.51.2.4.1.3.1.1.2 (0 invalid, 2 enable) my $mpa=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; if ($didchassis) { return 0, @cfgtext } #"Chassis already configured for this command" } @cfgtext=(); my $result = telnetcmds($mpa,$user,$pass,$nodeid,@_); $returncode |= @$result[0]; my $args = @$result[1]; foreach $parameter (@$args) { $assignment = 0; $value = undef; if ($parameter =~ /=/) { $assignment = 1; ($parameter,$value) = split /=/,$parameter,2; } if ($parameter =~ /^ntp$/) { my $result = ntp($value); $returncode |= shift(@$result); push @cfgtext,@$result; next; } if ($parameter =~ /^network$/) { my $data = $session->get(['1.3.6.1.4.1.2.3.51.2.4.9.1.1.4',0]); push @cfgtext,"MM IP: $data"; my $data = $session->get(['1.3.6.1.4.1.2.3.51.2.4.9.1.1.3',0]); push @cfgtext,"MM Hostname: $data"; my $data = $session->get(['1.3.6.1.4.1.2.3.51.2.4.9.1.1.9',0]); push @cfgtext,"Gateway: $data"; my $data = $session->get(['1.3.6.1.4.1.2.3.51.2.4.9.1.1.14',0]); push @cfgtext,"Subnet Mask: $data"; next; } if ($parameter =~ /^build$/) { my $data = $session->get(['1.3.6.1.4.1.2.3.51.2.2.21.3.1.1.3',1]); push @cfgtext,"Build ID: $data"; next; } if ($parameter eq "textid") { if ($assignment) { my $txtid = ($value =~ /^\*/) ? $node : $value; setoid("1.3.6.1.4.1.2.3.51.2.22.1.7.1.1.5",$nodeid,$txtid,'OCTET'); } my $data; if ($slot > 0) { $data = $session->get([$bladeoname,$nodeid]); } else { $data = $session->get([$mmoname,0]); } $textid = 1; push @cfgtext,"textid: $data"; } if ($parameter =~ /^snmpcfg$/i) { my $data = $session->get(['1.3.6.1.4.1.2.3.51.2.4.9.3.1.6',0]); if ($data) { push @cfgtext,"SNMP: enabled"; } else { push @cfgtext,"SNMP: disabled"; } next; } if ($parameter =~ /^sshcfg$/i) { my $data = $session->get(['1.3.6.1.4.1.2.3.51.2.4.9.3.4.10',0]); if ($data =~ /NOSUCHOBJECT/) { push @cfgtext,"SSH: Not supported"; } elsif ($data) { push @cfgtext,"SSH: enabled"; } else { push @cfgtext,"SSH: disabled"; } } if ($parameter eq "snmpdest") { $parameter = "snmpdest1"; } if ($parameter =~ /snmpdest(\d+)/) { if ($1 > 3) { $returncode |= 1; push(@cfgtext,"Only up to three snmp destinations may be defined"); next; } my $dstindex = $1; if ($assignment) { setoid("1.3.6.1.4.1.2.3.51.2.4.9.3.1.4.1.1.".(2+$dstindex).".1",1,$value,'OCTET'); setoid("1.3.6.1.4.1.2.3.51.2.4.9.3.1.4.1.1.6.1",1,1,'INTEGER'); #access type: read-traps, don't give full write access to the community } my $data = $session->get(["1.3.6.1.4.1.2.3.51.2.4.9.3.1.4.1.1.".(2+$dstindex).".1.1"]); push @cfgtext,"SP SNMP Destination $1: $data"; next; } if ($parameter =~ /^community/i) { if ($assignment) { setoid("1.3.6.1.4.1.2.3.51.2.4.9.3.1.4.1.1.2.1.1",0,$value,'OCTET'); } my $data = $session->get(["1.3.6.1.4.1.2.3.51.2.4.9.3.1.4.1.1.2.1.1"]); push @cfgtext,"SP SNMP Community: $data"; next; } if ($parameter =~ /^alert/i) { if ($assignment) { if ($value =~ /^enable/i or $value =~ /^en/i or $value =~ /^on$/i) { setoid('1.3.6.1.4.1.2.3.51.2.4.1.3.1.1.4',12,'xCAT configured SNMP','OCTET'); #Set a description so the MM doesn't flip out setoid('1.3.6.1.4.1.2.3.51.2.4.1.3.1.1.5',12,4); #Set Dest12 to SNMP setoid('1.3.6.1.4.1.2.3.51.2.4.1.3.1.1.2',12,2); #enable dest12 setoid('1.3.6.1.4.1.2.3.51.2.4.9.3.1.3',0,0); #Enable SNMP traps enabledefaultalerts(); } elsif ($value =~ /^disable/i or $value =~ /^dis/i or $value =~ /^off$/i) { setoid('1.3.6.1.4.1.2.3.51.2.4.1.3.1.1.2',12,0); #Disable alert dest 12 setoid('1.3.6.1.4.1.2.3.51.2.4.9.3.1.3',0,1); #Disable SNMP traps period } } my $data = $session->get(['1.3.6.1.4.1.2.3.51.2.4.1.3.1.1.2.12']); if ($data == 2) { push @cfgtext,"SP Alerting: enabled"; next; } else { push @cfgtext,"SP Alerting: disabled"; next; } } } unless ($textid) { $didchassis=1; } return $returncode,@cfgtext; } sub switchblade { #OIDS of interest: #1.3.6.1.4.1.2.3.51.2.22.1.1 media tray ownership #1.3.6.1.4.1.2.3.51.2.22.1.2 kvm ownership my @args=@_; my $data; my @rettext; my $domt=0; my $dokvm=0; my $targnum=$slot; if ($args[1] =~ /^\d+$/) { $targnum = $args[1]; } if ($args[0] eq "list" or $args[0] eq "stat") { $data = $session->get(["1.3.6.1.4.1.2.3.51.2.22.1.1.0"]); push @rettext,"Media Tray slot: $data"; $data = $session->get(["1.3.6.1.4.1.2.3.51.2.22.1.2.0"]); push @rettext,"KVM slot: $data"; } elsif ($args[0] eq "both") { $domt=1; $dokvm=1; } elsif ($args[0] eq "mt" or $args[0] eq "media") { $domt=1; } elsif ($args[0] eq "kvm" or $args[0] eq "video") { $dokvm=1; } if ($domt) { setoid("1.3.6.1.4.1.2.3.51.2.22.1.1",0,$targnum); $data = $session->get(["1.3.6.1.4.1.2.3.51.2.22.1.1.0"]); push @rettext,"Media Tray slot: $data"; } if ($dokvm) { setoid("1.3.6.1.4.1.2.3.51.2.22.1.2",0,$targnum); $data = $session->get(["1.3.6.1.4.1.2.3.51.2.22.1.2.0"]); push @rettext,"KVM slot: $data"; } return 0,@rettext; } 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 $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 $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,"Unable to retrieve MAC address from Management Module"; } } else { return $code,$macs; } } sub inv { my @invitems; my $data; my @output; foreach (@_) { push @invitems,split( /,/,$_); } my $item; unless (scalar(@invitems)) { @invitems = ("all"); } 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 = "on 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; my $node = 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 "rspconfig") { return mpaconfig($mpa,$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 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 %mpahash = (); 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"); } 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) || !grep(/^@$exargs[0]$/, split /,/, $cmd )) { $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 ) { return( "Node dependent on itself: $n -> $node" ); } $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 while(my ($name,$h) = each(%dp) ) { my $ent=$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; foreach ( keys %$h ) { if ( $h->{$_} =~ /(^\d+$)/ ) { my $ent=$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 preprocess_request { my $request = shift; if ($request->{_xcatdest}) { return [$request]; } #exit if preprocessed 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; } #print "noderange=@$noderange\n"; #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=(); foreach my $node (@$noderange) { my $ent=$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; } if (defined($ent->{id})) { push @{$mpa_hash{$ent->{mpa}}{ids}}, $ent->{id};} 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::Utils->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=(); foreach my $node (@$noderange) { my $ent=$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 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 } 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);} 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'); 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(); foreach (@{preprocess_request(\%invreq,\&fillresps)}) { %invreq = %$_; 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 { 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}; } } $mpahash{$mpa}->{username} = $user; $mpahash{$mpa}->{password} = $pass; for (my $i=0; $i<@nodes; $i++) { my $node=$nodes[$i];; my $nodeid=$ids[$i]; $mpahash{$mpa}->{nodes}->{$node}=$nodeid; } } my $sub_fds = new IO::Select; foreach $mpa (sort (keys %mpahash)) { while ($children > $blademaxp) { sleep (0.1); } $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); dompa($pfd,$mpa,\%mpahash,$command,-args=>\@exargs); exit(0); } $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)) {} } sub telnetcmds { my $mpa=shift; my $user=shift; my $pass=shift; my $nodeid=shift; my $value; my @unhandled; my %handled = (); my $result; my @tcmds = qw(snmpcfg sshcfg network swnet pd1 pd2 textid); # most of these commands should be able to be done # through SNMP, but they produce various errors. foreach my $cmd (@_) { if ($cmd =~ /^swnet|pd1|pd2|=/) { if (($cmd =~ /^textid/) and ($nodeid > 0)) { push @unhandled,$cmd; next; } my ($command,$value) = split /=/,$cmd; if (grep(/^$command$/,@tcmds)) { $handled{$command} = $value; next; } } push @unhandled,$cmd; } if (!defined(%handled)) { return([0,\@unhandled]); } my $t = new Net::Telnet( Timeout=>15, Errmode=>'return', Prompt=>'/system> $/' ); my $Rc = $t->open($mpa); if ($Rc) { $Rc = $t->login($user,$pass); } if (!$Rc) { push @cfgtext,$t->errmsg; return([1,\@unhandled]); } $Rc = 0; foreach (keys %handled) { if (/^snmpcfg/) { $result = snmpcfg($t,$handled{$_},$user,$pass); } elsif (/^sshcfg$/) { $result = sshcfg($t,$handled{$_},$user); } elsif (/^network$/) { $result = network($t,$handled{$_},$mpa); } elsif (/^swnet$/) { $result = swnet($t,$handled{$_}); } elsif (/^pd1|pd2$/) { $result = pd($t,$_,$handled{$_}); } elsif (/^textid$/) { $result = mmtextid($t,$mpa,$handled{$_}); } $Rc |= shift(@$result); push @cfgtext,@$result; } $t->close; return([$Rc,\@unhandled]); } sub mmtextid { my $t = shift; my $mpa = shift; my $value = shift; $value = ($value =~ /^\*/) ? $mpa : $value; my @data = $t->cmd("config -name $value -T system:mm[1]"); if (!grep(/OK/i,@data)) { return([1,@data]); } return([0,"textid: $value"]); } sub pd { my $t = shift; my $pd = shift; my $value = shift; my @result; if ($value) { if ($value !~ /^nonred|redwoperf|redwperf$/) { return([1,"Invalid power management (redwoperf|redwperf|nonred)"]); } my @data = $t->cmd("fuelg $pd -os $value"); if (!grep(/OK/i,@data)) { return([1,@data]); } return([0,"$pd: $value"]); } my @data = $t->cmd("fuelg"); my @pds = split /--------------/,join('',@data); $pd =~ /pd(\d)/; $pds[$1] =~ /Power Management Policy:\s+(.*)\n/; return([0,$1]); } sub network { my $t = shift; my $value = shift; my $mpa = shift; my $cmd = "ifconfig -eth0 -c static -r auto -d auto -m 1500 -T system:mm[1]"; my ($ip,$host,$gateway,$mask); if ($value) { if ($value !~ /\*/) { ($ip,$host,$gateway,$mask) = split /,/,$value; if (!$ip and !$host and !$gateway and !$mask) { return([1,"No changes specified"]); } } else { if ( $value !~ /^\*$/) { return([1,"Invalid format: 'network=*'"]); } my %nethash = xCAT::DBobjUtils->getNetwkInfo([$mpa]); my $gate = $nethash{$mpa}{gateway}; my $result; if ($gate) { $result = xCAT::Utils::toIP($gate); if (@$result[0] == 0) { $gateway = @$result[1]; } } $mask = $nethash{$mpa}{mask}; $host = $mpa; my $hosttab = xCAT::Table->new( 'hosts' ); if ($hosttab) { my ($ent) = $hosttab->getAttribs({node=>$mpa},'ip'); if (defined($ent)) { $ip = $ent->{ip}; } $hosttab->close(); } } } if ($ip) { $cmd.=" -i $ip"; } if ($host) { $cmd.=" -n $host"; } if ($gateway){ $cmd.=" -g $gateway"; } if ($mask) { $cmd.=" -s $mask"; } my @data = $t->cmd($cmd); my @result = grep(/These configuration changes will become active/,@data); if (!@result) { return([1,@data]); } if ($ip) { push @result,"MM IP: $ip"; } if ($host) { push @result,"MM Hostname: $host"; } if ($gateway){ push @result,"Gateway: $gateway"; } if ($mask) { push @result,"Subnet Mask: $mask"; } return([0,@result]); } sub swnet { my $t = shift; my $value = shift; my @result; my ($ip,$gateway,$mask); if (!$value) { my @data = $t->cmd("ifconfig -T system::switch[1]"); my $s = join('',@data); if ($s =~ /-i\s+(\S+)/) { $ip = $1; } if ($s =~ /-g\s+(\S+)/) { $gateway = $1; } if ($s =~ /-s\s+(\S+)/) { $mask = $1; } } else { my $cmd = "ifconfig -em disabled -ep enabled -pip enabled -T system:switch[1]"; ($ip,$gateway,$mask) = split /,/,$value; if (!$ip and !$gateway and !$mask) { return([1,"No changes specified"]); } if ($ip) { $cmd.=" -i $ip"; } if ($gateway){ $cmd.=" -g $gateway"; } if ($mask) { $cmd.=" -s $mask"; } my @data = $t->cmd($cmd); @result = grep(/OK/i,@data); if (!@result) { return([1,@data]); } } if ($ip) { push @result,"Switch IP: $ip"; } if ($gateway){ push @result,"Gateway: $gateway"; } if ($mask) { push @result,"Subnet Mask: $mask"; } return([0,@result]); } sub snmpcfg { my $t = shift; my $value = shift; my $uid = shift; my $pass = shift; if ($value !~ /^enable|disable$/i) { return([1,"Invalid argument '$value' (enable|disable)"]); } # Query users on MM my @data = $t->cmd("users -T system:mm[1]"); my ($user) = grep(/\d+\.\s+$uid/, @data); if (!$user) { return([1,"Cannot find user: '$uid' on MM"]); } $user =~ /^(\d+)./; my $id = $1; my $pp = ($value =~ /^enable$/i) ? "des" : "none"; my $cmd= "users -$id -ap sha -at write -ppw $pass -pp $pp -T system:mm[1]"; my @data = $t->cmd($cmd); if (grep(/OK/i,@data)) { return([0,"SNMP $value: OK"]); } return([1,@data]); } sub sshcfg { my $t = shift; my $value = shift; my $uid = shift; my $fname = ((xCAT::Utils::isAIX()) ? "/.ssh/":"/root/.ssh/")."id_rsa.pub"; if ($value !~ /^enable|disable$/i) { return([1,"Invalid argument '$value' (enable|disable)"]); } # Does MM support SSH my @data = $t->cmd("sshcfg -hk rsa -T system:mm[1]"); if (grep(/Error: Command not recognized/,@data)) { return([1,"SSH supported on AMM with minimum firmware BPET32"]); } # Get firmware version on MM @data = $t->cmd("update -a -T system:mm[1]"); my ($line) = grep(/Build ID:\s+\S+/, @data); # Minumum firmware version BPET32 required for SSH $line =~ /(\d+)/; if ($1 < 32) { return([1,"SSH supported on AMM with minimum firmware BPET32"]); } if ($value =~ /^disable$/i) { @data = $t->cmd("ports -sshe off -T system:mm[1]"); return([0,"OK"]); } # Get SSH key on Management Node unless (open(RSAKEY,"<$fname")) { return([1,"Error opening '$fname'"]); } my ($sshkey)=; close(RSAKEY); if ($sshkey !~ /\s+(\S+\@\S+$)/) { return([1,"Cannot find userid\@host in '$fname'"]); } my $login = $1; # Query users on MM @data = $t->cmd("users -T system:mm[1]"); my ($user) = grep(/\d+\.\s+$uid/, @data); if (!$user) { return([1,"Cannot find user: '$uid' on MM"]); } $user =~ /^(\d+)./; my $id = $1; # Determine is key already exists on MM @data = $t->cmd("users -$id -pk all -T system:mm[1]"); # Remove existing keys for this login foreach (split(/Key\s+/,join('',@data))) { if (/-cm\s+$login/) { /^(\d+)/; my $key = $1; @data = $t->cmd("users -$id -pk -$key -remove -T system:mm[1]"); } } # Make sure SSH key is generated on MM @data = $t->cmd("sshcfg -hk rsa -T system:mm[1]"); if (!grep(/ssh-rsa/,@data)) { @data = $t->cmd("sshcfg -hk gen -T system:mm[1]"); if (!grep(/^OK$/i, @data)) { return([1,@data]); } # Wait for SSH key generation to complete my $timeout = time+240; while (1) { if (time >= $timeout) { return([1,"SSH key generation timeout"]); } sleep(15); @data = $t->cmd("sshcfg -hk rsa -T system:mm[1]"); if (grep(/ssh-rsa/,@data)) { last; } } } # Transfer SSH key from Management Node to MM $sshkey =~ s/@/\@/; @data = $t->cmd("users -$id -pk -T system:mm[1] -add $sshkey"); if ($data[0]=~/Error/i) { return([1,$data[0]]); } # Enable ssh on MM @data = $t->cmd("ports -sshe on -T system:mm[1]"); return([0,"SSH $value: OK"]); } sub ntp { my $value = shift; my @result; my $data = $session->get(['1.3.6.1.4.1.2.3.51.2.4.9.3.8.1',0]); if ($data =~ /NOSUCHOBJECT/) { return([1,"NTP Not supported"]); } if ($value) { my ($ntp,$ip,$f,$v3) = split /,/,$value; if ($ntp) { if ($ntp !~ /^enable|disable$/i) { return([1,"Invalid argument '$ntp' (enable|disable)"]); } } if ($v3) { if ($v3 !~ /^enable|disable$/i) { return([1,"Invalid argument '$v3' (enable|disable)"]); } } if (!$ntp and !$ip and !$f and !$v3) { return([1,"No changes specified"]); } if ($ntp) { my $d = ($ntp =~ /^enable$/i) ? 1 : 0; setoid('1.3.6.1.4.1.2.3.51.2.4.9.3.8.1',0,$d,'INTEGER'); push @result,"NTP: $ntp"; } if ($ip) { setoid('1.3.6.1.4.1.2.3.51.2.4.9.3.8.2',0,$ip,'OCTET'); push @result,"NTP Server: $ip"; } if ($f) { setoid('1.3.6.1.4.1.2.3.51.2.4.9.3.8.3',0,$f,'INTEGER'); push @result,"NTP Frequency: $f"; } if ($v3) { my $d = ($v3 =~ /^enable$/i) ? 1 : 0; setoid('1.3.6.1.4.1.2.3.51.2.4.9.3.8.7',0,$d,'INTEGER'); push @result,"NTP v3: $v3"; } return([0,@result]); } my $d = (!$data) ? "disabled" : "enabled"; push @result,"NTP: $d"; $data = $session->get(['1.3.6.1.4.1.2.3.51.2.4.9.3.8.2',0]); push @result,"NTP Server: $data"; $data = $session->get(['1.3.6.1.4.1.2.3.51.2.4.9.3.8.3',0]); push @result,"NTP Frequency: $data (minutes)"; $data = $session->get(['1.3.6.1.4.1.2.3.51.2.4.9.3.8.7',0]); $d = (!$data) ? "disabled" : "enabled"; push @result,"NTP v3: $d"; return([0,@result]); } 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 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 => 4, # Give up sooner to make commands go smoother Timeout=>1500000, #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"; yield; waitforack($out); 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,$node,$mpahash->{$mpa}->{nodes}->{$node},$mpahash->{$mpa}->{username},$mpahash->{$mpa}->{password},$command,@exargs); my @output_hashes; 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; } #my $msgtoparent=freeze(\@outhashes); # = XMLout(\%output,RootName => 'xcatresponse'); #print $out $msgtoparent; #$node.": $_\n"; } 1;