xcat-core/xCAT-server/lib/xcat/plugins/blade.pm
sikorsky 571c1bae2d Allows diskless nodes to boot via other interfaces aside of primary boot interface, ie. provides ethernet failover.
1) In initrd, bring up aside from main interface (genimage -i option) also other interfaces (-r option).
2) /etc/resolv.conf creation needed to be rewritten, othewise it gets confused by several dhcpcd records.
3) Fill database with MAC addresses for all interfaces. Since it't not possible to have several stanzas of the same name in dhcpd.conf, we need to define unique "alias" for every interface. For example, for host "host1" we will add '00:1A:64:5D:1B:84!host1e0|00:1A:64:5D:1B:86!host1e1' to macs table. Of course, this aliases have to be defined in DNS, otherwise makedhcp command will not use this aliases:
/etc/hosts:
10.217.249.232  host1   host1e0 host1e1
Since this are aliases (need not be nessesary), both interfaces get the same IP address during initrd, but this doesn't break anything.
  a) blades - changed getmacs function to gather all MAC addresses. Which interfaces we are interested in are defined in noderes.installnic or noderes.primarynic as "eth0|eth1".
  b) all other - TODO. We have only 8 non-blade nodes, so we fill database manually.
Backwards compatible:
      - if there is only one interface in noderes.installnic, getmacs function gathers only this one MAC address
      - if we run genimage without -r option, only one interface is brought up


git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@2124 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
2008-09-10 15:04:32 +00:00

2313 lines
68 KiB
Perl

#!/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;
#use warnings;
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',
getbladecons => 'blade',
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_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" ]);
my $session;
my $slot;
my $didchassis = 0;
my @eventlog_array = ();
my $activemm;
my %mpahash;
my $mpa;
my $allinchassis=0;
my $curn;
my @cfgtext;
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..
if ($mac =~ /->/) { #The new and 'improved' syntax for pBlades
$mac =~ /(\w+):(\w+):(\w+):(\w+):(\w+):(\w+)\s*->\s*(\w+):(\w+):(\w+):(\w+):(\w+):(\w+)/;
my $fmac=hex($3.$4.$5.$6);
my $lmac=hex($9.$10.$11.$12);
my $pfx = $1.$2;
foreach ($fmac..$lmac) {
my $key = $pfx.sprintf("%08x",$_);
$key =~ s/(\w{2})/$1:/g;
chop($key);
$key = uc($key);
$macmap{$key} = $node;
}
} else {
$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];
}
@bindlist=();
} while ($varbind->[31] and $varbind->[31]->[2] ne 'NOSUCHINSTANCE' and ($current < 2000));
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);
}
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);
}
}
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=();
foreach $parameter (@_) {
$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";
$data = $session->get(['1.3.6.1.4.1.2.3.51.2.4.9.1.1.3',0]);
push @cfgtext,"MM Hostname: $data";
$data = $session->get(['1.3.6.1.4.1.2.3.51.2.4.9.1.1.9',0]);
push @cfgtext,"Gateway: $data";
$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 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 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( /,/,$_);
}
}
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($mpa,\@values);
}
elsif ( exists( $opt{z} )) {
$result = rscan_stanza($mpa,\@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 $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 getmacs {
(my $code,my @macs)=inv('mac');
my $midx=0;
my @midxary;
my $nrtab = xCAT::Table->new('noderes');
if ($nrtab) {
my $nent = $nrtab->getNodeAttribs($curn,['primarynic','installnic']);
if ($nent) {
my $mkey;
if (defined $nent->{installnic}) { #Prefer the install nic
$mkey="installnic";
} elsif (defined $nent->{primarynic}) { #see if primary nic was set
$mkey="primarynic";
}
if ($mkey) {
while ( $nent->{$mkey} =~ /(\d+)/g ) {
push @midxary,$1;
}
}
}
}
if ($code==0) {
#my @macs = split /\n/,$macs;
my @allmacs;
foreach my $midx ( @midxary) {
(my $macd,my $mac) = split (/:/,$macs[$midx],2);
$mac =~ s/\s+//g;
if ($macd =~ /mac address \d/i) {
$mac =~ s/\s*->.*$//;
} else {
return 1,"Unable to retrieve MAC address for interface $midx from Management Module";
}
if ( $#midxary == 0 ) { #-- backward compatibility mode - do not add host name to mac.mac if only one iface is used
push @allmacs,$mac;
} else {
push @allmacs,$mac."!".$curn."e".$midx;
}
}
my $macstring = join("|",@allmacs);
my $mactab = xCAT::Table->new('mac',-create=>1);
$mactab->setNodeAttribs($curn,{mac=>$macstring});
$mactab->close;
return 0,":mac.mac set to $macstring";
} else {
return $code,$macs[0];
}
}
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;
my $validsub=0;
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") {
$validsub=1;
$data = $session->get([$powerstatoid.".".$slot]);
if ($data == 1) {
$stat = "on";
} elsif ( $data == 0) {
$stat = "off";
} else {
$stat= "error";
}
} elsif ($subcommand eq "off") {
$validsub=1;
$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}); }
if ($subcommand eq "boot") { $stat = "on reset"; } else { $stat = "reset"; }
} elsif (not $validsub) {
return 1,"Unknown/Unsupported power command $subcommand";
}
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");
}
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 getbladecons {
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 %mpausers;
my %checkedmpas=();
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})) {
$rsp->{node}->[0]->{mm}->[0]=$ent->{mpa};
if (defined($checkedmpas{$ent->{$mpa}}) or not defined $mpatab) {
if (defined($mpausers{$ent->{mpa}})) {
$rsp->{node}->[0]->{username}=[$mpausers{$ent->{mpa}}];
} else {
$rsp->{node}->[0]->{username}=[$user];
}
} else {
$checkedmpas{$ent->{$mpa}}=1;
($tmp)=$mpatab->getAttribs({'mpa'=>$mpa},'username');
if (defined($tmp) and defined $tmp->{username}) {
$mpausers{$ent->{mpa}}=$tmp->{username};
$rsp->{node}->[0]->{username}=[$tmp->{username}];
} else {
$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
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=();
my $mptabhash = $mptab->getNodesAttribs($noderange,['mpa','id']);
if ($request->{command}->[0] eq "getbladecons") { #Can handle it here and now
getbladecons($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;
}
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=();
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 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) { 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);
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 network_reset);
# 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|sshcfg|=/) {
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;
}
unless (%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,$t->errmsg]);
}
$Rc = 0;
my @data;
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{$_}); }
elsif (/^network_reset$/) { $result = network($t,$handled{$_},$mpa,1); }
push @data, "$_: @$result";
$Rc |= shift(@$result);
push @cfgtext,@$result;
}
$t->close;
return([$Rc,\@unhandled,\@data]);
}
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 $reset = 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"; }
if (defined($reset)) {
$t->cmd("reset -T system:mm[1]");
}
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]";
@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) {
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"]);
}
# Get SSH key on Management Node
unless (open(RSAKEY,"<$fname")) {
return([1,"Error opening '$fname'"]);
}
my ($sshkey)=<RSAKEY>;
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]");
# Query if enabled/disabled
if (!$value) {
my @ddata = $t->cmd("sshcfg -T system:mm[1]");
if (my ($d) = grep(/^-cstatus\s+(\S+)$/,@ddata)) {
if ($d=~ /\s(\S+)$/) {
if ($1=~ /^disabled/i) {
return([0,"SSH: disabled"]);
}
}
}
# Find login
foreach (split(/Key\s+/,join('',@data))) {
if (/-cm\s+$login/) {
return([0,"SSH: enabled"]);
}
}
return([0,"SSH: disabled"]);
}
# 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]");
}
}
if ($value =~ /^disable$/i) {
if (!grep(/^OK$/i, @data)) {
return([1,"SSH Key not found on MM"]);
}
return([0,"disabled"]);
}
# 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/@/\@/;
$t->cmd("users -$id -at set -T system:mm[1]");
@data = $t->cmd("users -$id -pk -T system:mm[1] -add $sshkey");
if ($data[0]=~/Error/i) {
if ($data[0]=~/Error writing data for option -add/i) {
return([1,"Maximum number of SSH keys reached for this chassis"]);
}
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;
my $args = \@exargs;
# Handle telnet commands before SNMP
if ($command eq "rspconfig") {
foreach $node (sort (keys %{$mpahash->{$mpa}->{nodes}})) {
@cfgtext=();
my $slot = $mpahash->{$mpa}->{nodes}->{$node};
my $user = $mpahash->{$mpa}->{username};
my $pass = $mpahash->{$mpa}->{password};
my $result = telnetcmds($mpa,$user,$pass,$slot,@exargs);
my $rc |= @$result[0];
$args = @$result[1];
foreach(@cfgtext) {
my %output;
(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);
}
}
}
# Only telnet commands
unless ( @$args ) {
return;
}
$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,@$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;
}
#my $msgtoparent=freeze(\@outhashes); # = XMLout(\%output,RootName => 'xcatresponse');
#print $out $msgtoparent; #$node.": $_\n";
}
1;