2779f579e1
git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@2976 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
6381 lines
153 KiB
Perl
6381 lines
153 KiB
Perl
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
|
|
#egan@us.ibm.com
|
|
#modified by jbjohnso@us.ibm.com
|
|
#(C)IBM Corp
|
|
|
|
package xCAT_plugin::ipmi;
|
|
BEGIN
|
|
{
|
|
$::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
|
|
}
|
|
use lib "$::XCATROOT/lib/perl";
|
|
use strict;
|
|
use warnings "all";
|
|
use xCAT::GlobalDef;
|
|
use xCAT_monitoring::monitorctrl;
|
|
use xCAT::SPD qw/decode_spd/;
|
|
|
|
use POSIX qw(ceil floor);
|
|
use Storable qw(store_fd retrieve_fd thaw freeze);
|
|
use xCAT::Utils;
|
|
use xCAT::SvrUtils;
|
|
use xCAT::Usage;
|
|
use Thread qw(yield);
|
|
use LWP 5.64;
|
|
use HTTP::Request::Common;
|
|
my $tfactor = 0;
|
|
my $vpdhash;
|
|
my %bmc_comm_pids;
|
|
|
|
require Exporter;
|
|
our @ISA = qw(Exporter);
|
|
our @EXPORT = qw(
|
|
ipmiinit
|
|
ipmicmd
|
|
);
|
|
|
|
sub handled_commands {
|
|
return {
|
|
rpower => 'nodehm:power,mgt',
|
|
getipmicons => 'ipmi',
|
|
rspconfig => 'nodehm:mgt',
|
|
rvitals => 'nodehm:mgt',
|
|
rinv => 'nodehm:mgt',
|
|
rsetboot => 'nodehm:mgt',
|
|
rbeacon => 'nodehm:mgt',
|
|
reventlog => 'nodehm:mgt',
|
|
rfrurewrite => 'nodehm:mgt',
|
|
getrvidparms => 'nodehm:mgt'
|
|
}
|
|
}
|
|
|
|
|
|
use Data::Dumper;
|
|
use POSIX "WNOHANG";
|
|
use IO::Handle;
|
|
use IO::Socket;
|
|
use IO::Select;
|
|
use Class::Struct;
|
|
use Digest::MD5 qw(md5);
|
|
use POSIX qw(WNOHANG mkfifo strftime);
|
|
use Fcntl qw(:flock);
|
|
|
|
|
|
#local to module
|
|
my @rmcp = (0x06,0x00,0xff,0x07);
|
|
my $auth;
|
|
my $rssa = 0x20;
|
|
my $rqsa = 0x81;
|
|
my $seqlun = 0x00;
|
|
my @session_id = (0,0,0,0);
|
|
my @challenge = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
|
my @seqnum = (0,0,0,0);
|
|
my $outfd; #File descriptor for children to send messages to parent
|
|
my $currnode; #string to describe current node, presumably nodename
|
|
my $globrc=0;
|
|
my $userid;
|
|
my $passwd;
|
|
my $ipmi_bmcipaddr;
|
|
my $timeout;
|
|
my $port;
|
|
my $debug;
|
|
my $ndebug = 0;
|
|
my @cmdargv;
|
|
my $sock;
|
|
my @user;
|
|
my @pass;
|
|
my $channel_number;
|
|
my %sdr_hash;
|
|
my %fru_hash;
|
|
my $ipmiv2=0;
|
|
my $authoffset=0;
|
|
my $enable_cache="yes";
|
|
my $cache_dir = "/var/cache/xcat";
|
|
#my $ibmledtab = $ENV{XCATROOT}."/lib/GUMI/ibmleds.tab";
|
|
use xCAT::data::ibmleds;
|
|
use xCAT::data::ipmigenericevents;
|
|
use xCAT::data::ipmisensorevents;
|
|
my $cache_version = 2;
|
|
my $frudex; #iterator for initfru to use
|
|
|
|
my $status_noop="XXXno-opXXX";
|
|
|
|
my %idpxthermprofiles = (
|
|
'0z' => [0x37,0x41,0,0,0,0,5,0xa,0x3c,0xa,0xa,0x1e],
|
|
'1a' => [0x30,0x3c,0,0,0,0,5,0xa,0x3c,0xa,0xa,0x1e],
|
|
'2b' => [0x30,0x3c,0,0,0,0,5,0xa,0x3c,0xa,0xa,0x1e],
|
|
'3c' => [0x30,0x3c,0,0,0,0,5,0xa,0x3c,0xa,0xa,0x1e],
|
|
'4d' => [0x37,0x44,0,0,0,0,5,0xa,0x3c,0xa,0xa,0x1e],
|
|
'5e' => [0x37,0x44,0,0,0,0,5,0xa,0x3c,0xa,0xa,0x1e],
|
|
'6f' => [0x35,0x44,0,0,0,0,5,0xa,0x3c,0xa,0xa,0x1e],
|
|
);
|
|
my %codes = (
|
|
0x00 => "Command Completed Normal",
|
|
0xC0 => "Node busy, command could not be processed",
|
|
0xC1 => "Invalid or unsupported command",
|
|
0xC2 => "Command invalid for given LUN",
|
|
0xC3 => "Timeout while processing command, response unavailable",
|
|
0xC4 => "Out of space, could not execute command",
|
|
0xC5 => "Reservation canceled or invalid reservation ID",
|
|
0xC6 => "Request data truncated",
|
|
0xC7 => "Request data length invalid",
|
|
0xC8 => "Request data field length limit exceeded",
|
|
0xC9 => "Parameter out of range",
|
|
0xCA => "Cannot return number of requested data bytes",
|
|
0xCB => "Requested Sensor, data, or record not present",
|
|
0xCB => "Not present",
|
|
0xCC => "Invalid data field in Request",
|
|
0xCD => "Command illegal for specified sensor or record type",
|
|
0xCE => "Command response could not be provided",
|
|
0xCF => "Cannot execute duplicated request",
|
|
0xD0 => "Command reqponse could not be provided. SDR Repository in update mode",
|
|
0xD1 => "Command response could not be provided. Device in firmware update mode",
|
|
0xD2 => "Command response could not be provided. BMC initialization or initialization agent in progress",
|
|
0xD3 => "Destination unavailable",
|
|
0xD4 => "Insufficient privilege level",
|
|
0xD5 => "Command or request parameter(s) not supported in present state",
|
|
0xFF => "Unspecified error",
|
|
);
|
|
|
|
my %units = (
|
|
0 => "", #"unspecified",
|
|
1 => "C",
|
|
2 => "F",
|
|
3 => "K",
|
|
4 => "Volts",
|
|
5 => "Amps",
|
|
6 => "Watts",
|
|
7 => "Joules",
|
|
8 => "Coulombs",
|
|
9 => "VA",
|
|
10 => "Nits",
|
|
11 => "lumen",
|
|
12 => "lux",
|
|
13 => "Candela",
|
|
14 => "kPa",
|
|
15 => "PSI",
|
|
16 => "Newton",
|
|
17 => "CFM",
|
|
18 => "RPM",
|
|
19 => "Hz",
|
|
20 => "microsecond",
|
|
21 => "millisecond",
|
|
22 => "second",
|
|
23 => "minute",
|
|
24 => "hour",
|
|
25 => "day",
|
|
26 => "week",
|
|
27 => "mil",
|
|
28 => "inches",
|
|
29 => "feet",
|
|
30 => "cu in",
|
|
31 => "cu feet",
|
|
32 => "mm",
|
|
33 => "cm",
|
|
34 => "m",
|
|
35 => "cu cm",
|
|
36 => "cu m",
|
|
37 => "liters",
|
|
38 => "fluid ounce",
|
|
39 => "radians",
|
|
40 => "steradians",
|
|
41 => "revolutions",
|
|
42 => "cycles",
|
|
43 => "gravities",
|
|
44 => "ounce",
|
|
45 => "pound",
|
|
46 => "ft-lb",
|
|
47 => "oz-in",
|
|
48 => "gauss",
|
|
49 => "gilberts",
|
|
50 => "henry",
|
|
51 => "millihenry",
|
|
52 => "farad",
|
|
53 => "microfarad",
|
|
54 => "ohms",
|
|
55 => "siemens",
|
|
56 => "mole",
|
|
57 => "becquerel",
|
|
58 => "PPM",
|
|
59 => "reserved",
|
|
60 => "Decibels",
|
|
61 => "DbA",
|
|
62 => "DbC",
|
|
63 => "gray",
|
|
64 => "sievert",
|
|
65 => "color temp deg K",
|
|
66 => "bit",
|
|
67 => "kilobit",
|
|
68 => "megabit",
|
|
69 => "gigabit",
|
|
70 => "byte",
|
|
71 => "kilobyte",
|
|
72 => "megabyte",
|
|
73 => "gigabyte",
|
|
74 => "word",
|
|
75 => "dword",
|
|
76 => "qword",
|
|
77 => "line",
|
|
78 => "hit",
|
|
79 => "miss",
|
|
80 => "retry",
|
|
81 => "reset",
|
|
82 => "overflow",
|
|
83 => "underrun",
|
|
84 => "collision",
|
|
85 => "packets",
|
|
86 => "messages",
|
|
87 => "characters",
|
|
88 => "error",
|
|
89 => "correctable error",
|
|
90 => "uncorrectable error",
|
|
);
|
|
|
|
my %chassis_types = (
|
|
0 => "Unspecified",
|
|
1 => "Other",
|
|
2 => "Unknown",
|
|
3 => "Desktop",
|
|
4 => "Low Profile Desktop",
|
|
5 => "Pizza Box",
|
|
6 => "Mini Tower",
|
|
7 => "Tower",
|
|
8 => "Portable",
|
|
9 => "LapTop",
|
|
10 => "Notebook",
|
|
11 => "Hand Held",
|
|
12 => "Docking Station",
|
|
13 => "All in One",
|
|
14 => "Sub Notebook",
|
|
15 => "Space-saving",
|
|
16 => "Lunch Box",
|
|
17 => "Main Server Chassis",
|
|
18 => "Expansion Chassis",
|
|
19 => "SubChassis",
|
|
20 => "Bus Expansion Chassis",
|
|
21 => "Peripheral Chassis",
|
|
22 => "RAID Chassis",
|
|
23 => "Rack Mount Chassis",
|
|
);
|
|
|
|
my %MFG_ID = (
|
|
2 => "IBM",
|
|
343 => "Intel",
|
|
);
|
|
|
|
my %PROD_ID = (
|
|
"2:34869" => "e325",
|
|
"2:3" => "x346",
|
|
"2:4" => "x336",
|
|
"343:258" => "Tiger 2",
|
|
"343:256" => "Tiger 4",
|
|
);
|
|
|
|
my $localtrys = 3;
|
|
my $localdebug = 0;
|
|
|
|
struct SDR_rep_info => {
|
|
version => '$',
|
|
rec_count => '$',
|
|
resv_sdr => '$',
|
|
};
|
|
|
|
struct SDR => {
|
|
rec_type => '$',
|
|
sensor_owner_id => '$',
|
|
sensor_owner_lun => '$',
|
|
sensor_number => '$',
|
|
entity_id => '$',
|
|
entity_instance => '$',
|
|
sensor_init => '$',
|
|
sensor_cap => '$',
|
|
sensor_type => '$',
|
|
event_type_code => '$',
|
|
ass_event_mask => '@',
|
|
deass_event_mask => '@',
|
|
dis_read_mask => '@',
|
|
sensor_units_1 => '$',
|
|
sensor_units_2 => '$',
|
|
sensor_units_3 => '$',
|
|
linearization => '$',
|
|
M => '$',
|
|
tolerance => '$',
|
|
B => '$',
|
|
accuracy => '$',
|
|
accuracy_exp => '$',
|
|
R_exp => '$',
|
|
B_exp => '$',
|
|
analog_char_flag => '$',
|
|
nominal_reading => '$',
|
|
normal_max => '$',
|
|
normal_min => '$',
|
|
sensor_max_read => '$',
|
|
sensor_min_read => '$',
|
|
upper_nr_threshold => '$',
|
|
upper_crit_thres => '$',
|
|
upper_ncrit_thres => '$',
|
|
lower_nr_threshold => '$',
|
|
lower_crit_thres => '$',
|
|
lower_ncrit_thres => '$',
|
|
pos_threshold => '$',
|
|
neg_threshold => '$',
|
|
id_string_type => '$',
|
|
id_string => '$',
|
|
#LED id
|
|
led_id => '$',
|
|
fru_type => '$',
|
|
fru_subtype => '$',
|
|
};
|
|
|
|
struct FRU => {
|
|
rec_type => '$',
|
|
desc => '$',
|
|
value => '$',
|
|
};
|
|
|
|
sub decode_fru_locator { #Handle fru locator records
|
|
my @locator = @_;
|
|
my $sdr = SDR->new();
|
|
$sdr->rec_type(0x11);
|
|
$sdr->sensor_owner_id("FRU");
|
|
$sdr->sensor_owner_lun("FRU");
|
|
$sdr->sensor_number($locator[7]);
|
|
unless ($locator[8] & 0x80 and ($locator[8] & 0x1f) == 0 and $locator[9] == 0) {
|
|
#only logical devices at lun 0 supported for now
|
|
return undef;
|
|
}
|
|
unless (($locator[16] & 0xc0) == 0xc0) { #Only unpacked ASCII for now, no unicode or BCD plus yet
|
|
return undef;
|
|
}
|
|
my $idlen = $locator[16] & 0x3f;
|
|
unless ($idlen > 1) { return undef; }
|
|
$sdr->id_string(pack("C*",@locator[17..17+$idlen-1]));
|
|
$sdr->fru_type($locator[11]);
|
|
$sdr->fru_subtype($locator[12]);
|
|
|
|
return $sdr;
|
|
}
|
|
|
|
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 translate_sensor {
|
|
my $reading = shift;
|
|
my $sdr = shift;
|
|
my $unitdesc;
|
|
my $value;
|
|
my $lformat;
|
|
my $per;
|
|
$unitdesc = $units{$sdr->sensor_units_2};
|
|
if ($sdr->rec_type == 1) {
|
|
$value = (($sdr->M * $reading) + ($sdr->B * (10**$sdr->B_exp))) * (10**$sdr->R_exp);
|
|
} else {
|
|
$value = $reading;
|
|
}
|
|
if($sdr->rec_type !=1 or $sdr->linearization == 0) {
|
|
$reading = $value;
|
|
if($value == int($value)) {
|
|
$lformat = "%-30s%8d%-20s";
|
|
} else {
|
|
$lformat = "%-30s%8.3f%-20s";
|
|
}
|
|
} elsif($sdr->linearization == 7) {
|
|
if($value > 0) {
|
|
$reading = 1/$value;
|
|
} else {
|
|
$reading = 0;
|
|
}
|
|
$lformat = "%-30s%8d %-20s";
|
|
} else {
|
|
$reading = "RAW($sdr->linearization) $reading";
|
|
}
|
|
if($sdr->sensor_units_1 & 1) {
|
|
$per = "% ";
|
|
} else {
|
|
$per = " ";
|
|
}
|
|
my $numformat = ($sdr->sensor_units_1 & 0b11000000) >> 6;
|
|
if ($numformat) {
|
|
if ($numformat eq 0b11) {
|
|
#Not sure what to do.. leave it alone for now
|
|
} else {
|
|
if ($reading & 0b10000000) {
|
|
if ($numformat eq 0b01) {
|
|
$reading = 0-((~($reading&0b01111111))&0b1111111);
|
|
} elsif ($numformat eq 0b10) {
|
|
$reading = 0-(((~($reading&0b01111111))&0b1111111)+1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if($unitdesc eq "Watts") {
|
|
my $f = ($reading * 3.413);
|
|
$unitdesc = "Watts (" . int($f + .5) . " BTUs/hr)";
|
|
#$f = ($reading * 0.00134);
|
|
#$unitdesc .= " $f horsepower)";
|
|
}
|
|
if($unitdesc eq "C") {
|
|
my $f = ($reading * 9/5) + 32;
|
|
$unitdesc = "C (" . int($f + .5) . " F)";
|
|
}
|
|
if($unitdesc eq "F") {
|
|
my $c = ($reading - 32) * 5/9;
|
|
$unitdesc = "F (" . int($c + .5) . " C)";
|
|
}
|
|
return "$reading $unitdesc";
|
|
}
|
|
|
|
|
|
sub ipmiinit {
|
|
my $ipmimaxp = 80;
|
|
my $ipmitimeout = 3;
|
|
my $ipmitrys = 3;
|
|
my $ipmiuser = 'USERID';
|
|
my $ipmipass = 'PASSW0RD';
|
|
my $tmp;
|
|
|
|
|
|
my $table = xCAT::Table->new('site');
|
|
if ($table) {
|
|
($tmp)=$table->getAttribs({'key'=>'ipmimaxp'},'value');
|
|
if (defined($tmp)) { $ipmimaxp=$tmp->{value}; }
|
|
($tmp)=$table->getAttribs({'key'=>'ipmitimeout'},'value');
|
|
if (defined($tmp)) { $ipmitimeout=$tmp->{value}; }
|
|
($tmp)=$table->getAttribs({'key'=>'ipmiretries'},'value');
|
|
if (defined($tmp)) { $ipmitrys=$tmp->{value}; }
|
|
($tmp)=$table->getAttribs({'key'=>'ipmisdrcache'},'value');
|
|
}
|
|
$table = xCAT::Table->new('passwd');
|
|
if ($table) {
|
|
($tmp)=$table->getAttribs({'key'=>'ipmi'},'username','password');
|
|
if (defined($tmp)) {
|
|
$ipmiuser = $tmp->{username};
|
|
$ipmipass = $tmp->{password};
|
|
}
|
|
}
|
|
return($ipmiuser,$ipmipass,$ipmimaxp,$ipmitimeout,$ipmitrys);
|
|
}
|
|
|
|
sub ipmicmd {
|
|
my $node = shift;
|
|
$port = shift;
|
|
$userid = shift;
|
|
$passwd = shift;
|
|
$timeout = shift;
|
|
$localtrys = shift;
|
|
$debug = shift;
|
|
$localdebug = $debug;
|
|
|
|
if($userid eq "(null)") {
|
|
$userid = "";
|
|
}
|
|
if($passwd eq "(null)") {
|
|
$passwd = "";
|
|
}
|
|
|
|
@user = dopad16($userid);
|
|
@pass = dopad16($passwd);
|
|
|
|
$seqlun = 0x00;
|
|
@session_id = (0,0,0,0);
|
|
@challenge = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
|
|
@seqnum = (0,0,0,0);
|
|
$authoffset=0;
|
|
|
|
my $command = shift;
|
|
@cmdargv = @_;
|
|
my $subcommand = shift;
|
|
|
|
|
|
my $rc=0;
|
|
my $text="";
|
|
my $error="";
|
|
my @output;
|
|
my $noclose=0;
|
|
|
|
my $packed_ip = gethostbyname($node);
|
|
if(!defined($packed_ip)) {
|
|
$text = "failed to get IP for $node";
|
|
return(2,$text);
|
|
}
|
|
$ipmi_bmcipaddr=inet_ntoa($packed_ip);
|
|
|
|
$sock = IO::Socket::INET->new(
|
|
Proto => 'udp',
|
|
PeerHost => $ipmi_bmcipaddr,
|
|
PeerPort => $port,
|
|
);
|
|
if(!defined($sock)) {
|
|
$text = "failed to get socket: $@\n";
|
|
return(2,$text);
|
|
}
|
|
|
|
$error = getchanauthcap();
|
|
if($error) {
|
|
return(1,$error);
|
|
}
|
|
if($debug) {
|
|
print "$node: gotchanauthcap\n";
|
|
}
|
|
|
|
if($command eq "ping") {
|
|
return(0,"ping");
|
|
}
|
|
|
|
$error = getsessionchallenge();
|
|
if($error) {
|
|
return(1,$error);
|
|
}
|
|
if($debug) {
|
|
print "$node: gotsessionchallenge\n";
|
|
}
|
|
|
|
$error = activatesession();
|
|
if($error) {
|
|
return(1,$error);
|
|
}
|
|
if($debug) {
|
|
print "$node: active session\n";
|
|
}
|
|
|
|
$error = setprivlevel();
|
|
if($error) {
|
|
return(1,$error);
|
|
}
|
|
if($debug) {
|
|
print "$node: priv level set\n";
|
|
}
|
|
|
|
if($command eq "rpower") {
|
|
if($subcommand eq "stat" || $subcommand eq "state" || $subcommand eq "status") {
|
|
($rc,$text) = power("stat");
|
|
}
|
|
elsif($subcommand eq "on") {
|
|
my ($oldrc,$oldtext) = power("stat");
|
|
($rc,$text) = power("on");
|
|
if(($rc == 0) && ($text eq "on") && ($oldtext eq "on")) { $text .= " $status_noop"; }
|
|
}
|
|
elsif($subcommand eq "nmi") {
|
|
($rc,$text) = power("nmi");
|
|
}
|
|
elsif($subcommand eq "off" or $subcommand eq "softoff") {
|
|
my ($oldrc,$oldtext) = power("stat");
|
|
($rc,$text) = power($subcommand);
|
|
if(($rc == 0) && ($text eq "off") && ($oldtext eq "off")) { $text .= " $status_noop"; }
|
|
|
|
# if($text0 ne "") {
|
|
# $text = $text0 . " " . $text;
|
|
# }
|
|
}
|
|
elsif($subcommand eq "reset") {
|
|
my ($oldrc,$oldtext) = power("stat");
|
|
($rc,$text) = power("reset");
|
|
$noclose = 0;
|
|
if(($rc == 0) && ($text eq "off") && ($oldtext eq "off")) { $text .= " $status_noop"; }
|
|
}
|
|
elsif($subcommand eq "cycle") {
|
|
my $text2;
|
|
|
|
($rc,$text) = power("stat");
|
|
|
|
if($rc == 0 && $text eq "on") {
|
|
($rc,$text) = power("off");
|
|
if($rc == 0) {
|
|
sleep(5);
|
|
}
|
|
}
|
|
|
|
if($rc == 0 && $text eq "off") {
|
|
($rc,$text2) = power("on");
|
|
}
|
|
|
|
if($rc == 0) {
|
|
$text = $text . " " . $text2
|
|
}
|
|
}
|
|
elsif($subcommand eq "boot") {
|
|
my $text2;
|
|
|
|
($rc,$text) = power("stat");
|
|
|
|
if($rc == 0) {
|
|
if($text eq "on") {
|
|
($rc,$text2) = power("reset");
|
|
$noclose = 0;
|
|
}
|
|
elsif($text eq "off") {
|
|
($rc,$text2) = power("on");
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
}
|
|
|
|
$text = $text . " " . $text2
|
|
}
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = "unsupported command $command $subcommand";
|
|
}
|
|
}
|
|
elsif($command eq "rbeacon") {
|
|
($rc,$text) = beacon($subcommand);
|
|
}
|
|
elsif($command eq "getrvidparms") {
|
|
($rc,@output) = getrvidparms($subcommand);
|
|
}
|
|
# elsif($command eq "info") {
|
|
# if($subcommand eq "sensorname") {
|
|
# ($rc,$text) = initsdr();
|
|
# if($rc == 0) {
|
|
# my $key;
|
|
# $text="";
|
|
# foreach $key (keys %sdr_hash) {
|
|
# my $sdr = $sdr_hash{$key};
|
|
# if($sdr->sensor_number == @_) {
|
|
# $text = $sdr_hash{$key}->id_string;
|
|
# last;
|
|
# }
|
|
# }
|
|
## if(defined $sdr_hash{@_}) {
|
|
## $text = $sdr_hash{@_}->id_string;
|
|
## }
|
|
# }
|
|
# }
|
|
# }
|
|
elsif($command eq "rvitals") {
|
|
($rc,@output) = vitals($subcommand);
|
|
}
|
|
elsif($command eq "rspreset") {
|
|
($rc,@output) = resetbmc();
|
|
$noclose=1;
|
|
}
|
|
elsif($command eq "reventlog") {
|
|
if($subcommand eq "decodealert") {
|
|
($rc,$text) = decodealert(@_);
|
|
}
|
|
else {
|
|
($rc,@output) = eventlog($subcommand);
|
|
}
|
|
}
|
|
elsif($command eq "rinv") {
|
|
($rc,@output) = inv($subcommand);
|
|
}
|
|
elsif($command eq "fru") {
|
|
($rc,@output) = fru($subcommand);
|
|
}
|
|
elsif($command eq "sol.command") {
|
|
my $dc=0;
|
|
|
|
$@ = "";
|
|
eval {
|
|
my $cc=0;
|
|
my $kid;
|
|
my $pid=$$;
|
|
|
|
$SIG{USR1} = sub {$cc=0;};
|
|
$SIG{USR2} = sub {$dc++;};
|
|
$SIG{CHLD} = sub {while(waitpid(-1,WNOHANG) > 0) { sleep(1); }};
|
|
|
|
mkfifo("/tmp/.sol.$pid",0666);
|
|
|
|
my $child = xCAT::Utils->xfork();
|
|
if(!defined $child) {
|
|
die;
|
|
}
|
|
|
|
if($child > 0) {
|
|
$cc=1;
|
|
}
|
|
else {
|
|
system("$subcommand /tmp/.sol.$pid");
|
|
|
|
if($?/256 == 1) {
|
|
kill(12,$pid);
|
|
}
|
|
if($?/256 == 2) {
|
|
kill(12,$pid);
|
|
sleep(1);
|
|
kill(12,$pid);
|
|
}
|
|
|
|
kill(10,$pid);
|
|
exit(0);
|
|
}
|
|
|
|
open(FH,"< /tmp/.sol.$pid");
|
|
my $kpid = <FH>;
|
|
close(FH);
|
|
unlink("/tmp/.sol.$pid");
|
|
|
|
while($cc == 1) {
|
|
sleep(5);
|
|
($rc,$text) = power("stat");
|
|
$text="";
|
|
if($rc != 0) {
|
|
kill(15,$kpid);
|
|
$cc=0;
|
|
}
|
|
}
|
|
|
|
do {
|
|
$kid = waitpid(-1,WNOHANG);
|
|
sleep(1);
|
|
} until($kid == -1);
|
|
};
|
|
if($@) {
|
|
@output = $@;
|
|
}
|
|
|
|
$rc = $dc;
|
|
if($rc == 1) {
|
|
$noclose = 1;
|
|
}
|
|
}
|
|
elsif($command eq "rgetnetinfo") {
|
|
my @subcommands = ($subcommand);
|
|
if($subcommand eq "all") {
|
|
@subcommands = (
|
|
"ip",
|
|
"netmask",
|
|
"gateway",
|
|
"backupgateway",
|
|
"snmpdest1",
|
|
"snmpdest2",
|
|
"snmpdest3",
|
|
"snmpdest4",
|
|
"community",
|
|
);
|
|
|
|
my @coutput;
|
|
|
|
foreach(@subcommands) {
|
|
$subcommand = $_;
|
|
($rc,@output) = getnetinfo($subcommand);
|
|
push(@coutput,@output);
|
|
}
|
|
|
|
@output = @coutput;
|
|
}
|
|
else {
|
|
($rc,@output) = getnetinfo($subcommand);
|
|
}
|
|
}
|
|
elsif($command eq "rspconfig") {
|
|
foreach ($subcommand,@_) {
|
|
my @coutput;
|
|
($rc,@coutput) = setnetinfo($_);
|
|
if($rc == 0) {
|
|
($rc,@coutput) = getnetinfo($_);
|
|
}
|
|
push(@output,@coutput);
|
|
}
|
|
}
|
|
elsif($command eq "sete325cli") {
|
|
($rc,@output) = sete325cli($subcommand);
|
|
}
|
|
elsif($command eq "sete326cli") {
|
|
($rc,@output) = sete325cli($subcommand);
|
|
}
|
|
elsif($command eq "generic") {
|
|
($rc,@output) = generic($subcommand);
|
|
}
|
|
elsif($command eq "rfrurewrite") {
|
|
($rc,@output) = writefru($subcommand,shift);
|
|
}
|
|
elsif($command eq "fru") {
|
|
($rc,@output) = fru($subcommand);
|
|
}
|
|
elsif($command eq "rsetboot") {
|
|
($rc,@output) = setboot($subcommand);
|
|
}
|
|
|
|
else {
|
|
$rc = 1;
|
|
$text = "unsupported command $command $subcommand";
|
|
}
|
|
if($debug) {
|
|
print "$node: command completed\n";
|
|
}
|
|
|
|
if($noclose == 0) {
|
|
$error = closesession();
|
|
if($error) {
|
|
return(1,"$text, session close: $error");
|
|
}
|
|
if($debug) {
|
|
print "$node: session closed.\n";
|
|
}
|
|
}
|
|
|
|
if($text) {
|
|
push(@output,$text);
|
|
}
|
|
|
|
$sock->close();
|
|
return($rc,@output);
|
|
}
|
|
|
|
sub resetbmc {
|
|
my $netfun = 0x18;
|
|
my @cmd = (0x02);
|
|
my @returnd = ();
|
|
my $rc = 0;
|
|
my $text;
|
|
my $error;
|
|
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
if ($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
} else {
|
|
if (0 == $returnd[36]) {
|
|
$text = "BMC reset";
|
|
} else {
|
|
if ($codes{$returnd[36]}) {
|
|
$text = $codes{$returnd[36]};
|
|
} else {
|
|
$text = sprintf("BMC Responded with code %d",$returnd[36]);
|
|
}
|
|
}
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
|
|
sub setnetinfo {
|
|
my $subcommand = shift;
|
|
my $argument;
|
|
($subcommand,$argument) = split(/=/,$subcommand);
|
|
my @input = @_;
|
|
|
|
my $netfun = 0x30;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my $code;
|
|
my $match;
|
|
|
|
if($subcommand eq "snmpdest") {
|
|
$subcommand = "snmpdest1";
|
|
}
|
|
|
|
|
|
unless(defined($argument)) {
|
|
return 0;
|
|
}
|
|
if ($subcommand eq "thermprofile") {
|
|
return idpxthermprofile($argument);
|
|
}
|
|
if ($subcommand eq "alert" and $argument eq "on" or $argument =~ /^en/ or $argument =~ /^enable/) {
|
|
$netfun = 0x10;
|
|
@cmd = (0x12,0x9,0x1,0x18,0x11,0x00);
|
|
} elsif ($subcommand eq "alert" and $argument eq "off" or $argument =~ /^dis/ or $argument =~ /^disable/) {
|
|
$netfun = 0x10;
|
|
@cmd = (0x12,0x9,0x1,0x10,0x11,0x00);
|
|
}
|
|
elsif($subcommand eq "garp") {
|
|
my $halfsec = $argument * 2; #pop(@input) * 2;
|
|
|
|
if($halfsec > 255) {
|
|
$halfsec = 255;
|
|
}
|
|
if($halfsec < 4) {
|
|
$halfsec = 4;
|
|
}
|
|
|
|
@cmd = (0x01,$channel_number,0x0b,$halfsec);
|
|
}
|
|
elsif($subcommand =~ m/community/ ) {
|
|
my $cindex = 0;
|
|
my @clist;
|
|
foreach (0..17) {
|
|
push @clist,0;
|
|
}
|
|
foreach (split //,$argument) {
|
|
$clist[$cindex++]=ord($_);
|
|
}
|
|
@cmd = (1,$channel_number,0x10,@clist);
|
|
}
|
|
elsif($subcommand =~ m/snmpdest(\d+)/ ) {
|
|
my $dstip = $argument; #pop(@input);
|
|
my @dip = split /\./, $dstip;
|
|
@cmd = (0x01,$channel_number,0x13,$1,0x00,0x00,$dip[0],$dip[1],$dip[2],$dip[3],0,0,0,0,0,0);
|
|
}
|
|
#elsif($subcommand eq "alert" ) {
|
|
# my $action=pop(@input);
|
|
#print "action=$action\n";
|
|
# $netfun=0x28; #TODO: not right
|
|
|
|
# mapping alert action to number
|
|
# my $act_number=8;
|
|
# if ($action eq "on") {$act_number=8;}
|
|
# elsif ($action eq "off") { $act_number=0;}
|
|
# else { return(1,"unsupported alert action $action");}
|
|
# @cmd = (0x12, $channel_number,0x09, 0x01, $act_number+16, 0x11,0x00);
|
|
#}
|
|
else {
|
|
return(1,"configuration of $subcommand is not implemented currently");
|
|
}
|
|
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
}
|
|
else {
|
|
if($subcommand eq "garp" or $subcommand =~ m/snmpdest\d+/ or $subcommand eq "alert" or $subcommand =~ /community/) {
|
|
$code = $returnd[36];
|
|
|
|
if($code == 0x00) {
|
|
$text = "ok";
|
|
}
|
|
}
|
|
|
|
if(!$text) {
|
|
$rc = 1;
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
}
|
|
|
|
return($rc,$text);
|
|
}
|
|
|
|
sub getnetinfo {
|
|
my $subcommand = shift;
|
|
$subcommand =~ s/=.*//;
|
|
if ($subcommand eq "thermprofile") {
|
|
my $code;
|
|
my @returnd;
|
|
my $thermdata;
|
|
my $netfun=0x2e<<2; #currently combined netfun & lun, to be simplified later
|
|
my @cmd = (0x41,0x4d,0x4f,0x00,0x6f,0xff,0x61,0x00);
|
|
my @bytes;
|
|
my $error = docmd($netfun,\@cmd,\@bytes);
|
|
@bytes=splice @bytes,36-$authoffset;
|
|
@bytes=splice @bytes,16;
|
|
my $validprofiles="";
|
|
foreach (keys %idpxthermprofiles) {
|
|
if (sprintf("%02x %02x %02x %02x %02x %02x %02x",@bytes) eq sprintf("%02x %02x %02x %02x %02x %02x %02x",@{$idpxthermprofiles{$_}})) {
|
|
$validprofiles.="$_,";
|
|
}
|
|
}
|
|
if ($validprofiles) {
|
|
chop($validprofiles);
|
|
return (0,"The following thermal profiles are in effect: ".$validprofiles);
|
|
}
|
|
return (1,sprintf("Unable to identify current thermal profile: \"%02x %02x %02x %02x %02x %02x %02x\"",@bytes));
|
|
}
|
|
|
|
my $netfun = 0x30;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my $code;
|
|
my $format = "%-25s";
|
|
|
|
if ($subcommand eq "snmpdest") {
|
|
$subcommand = "snmpdest1";
|
|
}
|
|
|
|
if ($subcommand eq "alert") {
|
|
$netfun = 0x10;
|
|
@cmd = (0x13,9,1,0);
|
|
}
|
|
elsif($subcommand eq "garp") {
|
|
@cmd = (0x02,$channel_number,0x0b,0x00,0x00);
|
|
}
|
|
elsif ($subcommand =~ m/^snmpdest(\d+)/ ) {
|
|
@cmd = (0x02,$channel_number,0x13,$1,0x00);
|
|
}
|
|
elsif ($subcommand eq "ip") {
|
|
@cmd = (0x02,$channel_number,0x03,0x00,0x00);
|
|
}
|
|
elsif ($subcommand eq "netmask") {
|
|
@cmd = (0x02,$channel_number,0x06,0x00,0x00);
|
|
}
|
|
elsif ($subcommand eq "gateway") {
|
|
@cmd = (0x02,$channel_number,0x0C,0x00,0x00);
|
|
}
|
|
elsif ($subcommand eq "backupgateway") {
|
|
@cmd = (0x02,$channel_number,0x0E,0x00,0x00);
|
|
}
|
|
elsif ($subcommand eq "community") {
|
|
@cmd = (0x02,$channel_number,0x10,0x00,0x00);
|
|
}
|
|
else {
|
|
return(1,"unsupported command getnetinfo $subcommand");
|
|
}
|
|
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
}
|
|
else {
|
|
# response format:
|
|
# 4 bytes (RMCP header)
|
|
# 1 byte (auth type)
|
|
# 4 bytes (session sequence)
|
|
# 4 bytes (session id)
|
|
# 16 bytes (message auth code, not present if auth type is 0, $authoffset=16)
|
|
# 1 byte (ipmi message length)
|
|
# 1 byte (requester's address
|
|
# 1 byte (netfun, req lun)
|
|
# 1 byte (checksum)
|
|
# 1 byte (Responder's slave address)
|
|
# 1 byte (Sequence number, generated by the requester)
|
|
# 1 byte (command)
|
|
# 1 byte (return code)
|
|
# 1 byte (param revision)
|
|
# N bytes (data)
|
|
# 1 byte (checksum)
|
|
if($subcommand eq "garp") {
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
$code = $returnd[38-$authoffset] / 2;
|
|
$text = sprintf("$format %d","Gratuitous ARP seconds:",$code);
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
}
|
|
elsif($subcommand eq "alert") {
|
|
if ($returnd[39-$authoffset] & 0x8) {
|
|
$text = "SP Alerting: enabled";
|
|
} else {
|
|
$text = "SP Alerting: disabled";
|
|
}
|
|
}
|
|
elsif($subcommand =~ m/^snmpdest(\d+)/ ) {
|
|
$text = sprintf("$format %d.%d.%d.%d",
|
|
"SP SNMP Destination $1:",
|
|
$returnd[41-$authoffset],
|
|
$returnd[42-$authoffset],
|
|
$returnd[43-$authoffset],
|
|
$returnd[44-$authoffset]);
|
|
}
|
|
elsif($subcommand eq "ip") {
|
|
$text = sprintf("$format %d.%d.%d.%d",
|
|
"BMC IP:",
|
|
$returnd[38-$authoffset],
|
|
$returnd[39-$authoffset],
|
|
$returnd[40-$authoffset],
|
|
$returnd[41-$authoffset]);
|
|
}
|
|
elsif($subcommand eq "netmask") {
|
|
$text = sprintf("$format %d.%d.%d.%d",
|
|
"BMC Netmask:",
|
|
$returnd[38-$authoffset],
|
|
$returnd[39-$authoffset],
|
|
$returnd[40-$authoffset],
|
|
$returnd[41-$authoffset]);
|
|
}
|
|
elsif($subcommand eq "gateway") {
|
|
$text = sprintf("$format %d.%d.%d.%d",
|
|
"BMC Gateway:",
|
|
$returnd[38-$authoffset],
|
|
$returnd[39-$authoffset],
|
|
$returnd[40-$authoffset],
|
|
$returnd[41-$authoffset]);
|
|
}
|
|
elsif($subcommand eq "backupgateway") {
|
|
$text = sprintf("$format %d.%d.%d.%d",
|
|
"BMC Backup Gateway:",
|
|
$returnd[38-$authoffset],
|
|
$returnd[39-$authoffset],
|
|
$returnd[40-$authoffset],
|
|
$returnd[41-$authoffset]);
|
|
}
|
|
elsif ($subcommand eq "community") {
|
|
$text = sprintf("$format ","SP SNMP Community:");
|
|
my $l = 38-$authoffset;
|
|
while ($returnd[$l] ne 0) {
|
|
$l = $l + 1;
|
|
}
|
|
my $i=38-$authoffset;
|
|
while ($i<$l) {
|
|
$text = $text . sprintf("%c",$returnd[$i]);
|
|
$i = $i + 1;
|
|
}
|
|
}
|
|
|
|
if(!$text) {
|
|
$rc = 1;
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
}
|
|
|
|
return($rc,$text);
|
|
}
|
|
|
|
sub sete325cli {
|
|
my $subcommand = shift;
|
|
|
|
my $netfun = 0xc8;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my $code;
|
|
|
|
if($subcommand eq "disable") {
|
|
@cmd = (0x00);
|
|
}
|
|
elsif($subcommand eq "cli") {
|
|
@cmd = (0x02);
|
|
}
|
|
else {
|
|
return(1,"unsupported command sete325cli $subcommand");
|
|
}
|
|
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
}
|
|
else {
|
|
if($code == 0x00) {
|
|
$rc = 0;
|
|
$text = "$subcommand";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
|
|
if(!$text) {
|
|
$rc = 1;
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
}
|
|
|
|
return($rc,$text);
|
|
}
|
|
|
|
sub setboot {
|
|
my $subcommand=shift;
|
|
my $netfun = 0x00;
|
|
my @cmd = (0x08,0x3,0x8);
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text = "";
|
|
my $code;
|
|
my $skipset = 0;
|
|
my %bootchoices = (
|
|
0 => 'BIOS default',
|
|
1 => 'Network',
|
|
2 => 'Hard Drive',
|
|
5 => 'CD/DVD',
|
|
6 => 'BIOS Setup',
|
|
15 => 'Floppy'
|
|
);
|
|
|
|
#This disables the 60 second timer
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
if ($subcommand eq "net") {
|
|
@cmd=(0x08,0x5,0x80,0x4,0x0,0x0,0x0);
|
|
}
|
|
elsif ($subcommand eq "hd" ) {
|
|
@cmd=(0x08,0x5,0x80,0x8,0x0,0x0,0x0);
|
|
}
|
|
elsif ($subcommand eq "cd" ) {
|
|
@cmd=(0x08,0x5,0x80,0x14,0x0,0x0,0x0);
|
|
}
|
|
elsif ($subcommand eq "floppy" ) {
|
|
@cmd=(0x08,0x5,0x80,0x3c,0x0,0x0,0x0);
|
|
}
|
|
elsif ($subcommand =~ m/^def/) {
|
|
@cmd=(0x08,0x5,0x0,0x0,0x0,0x0,0x0);
|
|
}
|
|
elsif ($subcommand eq "setup" ) { #Not supported by BMCs I've checked so far..
|
|
@cmd=(0x08,0x5,0x18,0x0,0x0,0x0,0x0);
|
|
}
|
|
elsif ($subcommand =~ m/^stat/) {
|
|
$skipset=1;
|
|
}
|
|
else {
|
|
return(1,"unsupported command setboot $subcommand");
|
|
}
|
|
|
|
|
|
unless ($skipset) {
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
if($error) {
|
|
return(1,$error);
|
|
}
|
|
$code = $returnd[36-$authoffset];
|
|
unless ($code == 0x00) {
|
|
return(1,$codes{$code});
|
|
}
|
|
}
|
|
@cmd=(0x09,0x5,0x0,0x0);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
if($error) {
|
|
return(1,$error);
|
|
}
|
|
$code = $returnd[36-$authoffset];
|
|
unless ($code == 0x00) {
|
|
return(1,$codes{$code});
|
|
}
|
|
unless ($returnd[39-$authoffset] & 0x80) {
|
|
$text = "boot override inactive";
|
|
return($rc,$text);
|
|
}
|
|
my $boot=($returnd[40-$authoffset] & 0x3C) >> 2;
|
|
$text = $bootchoices{$boot};
|
|
return($rc,$text);
|
|
}
|
|
|
|
sub idpxthermprofile {
|
|
#iDataplex thermal profiles as of 6/10/2008
|
|
my $subcommand = lc(shift);
|
|
my @returnd;
|
|
my $netfun = 0xb8;
|
|
my @cmd = (0x41,0x4d,0x4f,0x00,0x6f,0xfe,0x60,0,0,0,0,0,0,0,0xff);
|
|
if ($idpxthermprofiles{$subcommand}) {
|
|
push @cmd,@{$idpxthermprofiles{$subcommand}};
|
|
} else {
|
|
return (1,"Not an understood thermal profile, expected a 2 hex digit value corresponding to chassis label on iDataplex server");
|
|
}
|
|
docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
return (0,"OK");
|
|
}
|
|
|
|
|
|
sub getrvidparms {
|
|
my $netfun = 0x3a;
|
|
my @mcinfo=getdevid();
|
|
unless ($mcinfo[2] == 2) { #Only implemented for IBM servers
|
|
return(1,"Remote video is not supported on this system");
|
|
}
|
|
#TODO: use get bmc capabilities to see if rvid is actually supported before bothering the client java app
|
|
my @build_id;
|
|
my $localerror = docmd(
|
|
0xe8,
|
|
[0x50],
|
|
\@build_id
|
|
);
|
|
if ($localerror) {
|
|
return(1,$localerror);
|
|
}
|
|
@build_id=splice @build_id,36-$authoffset;
|
|
unless ($build_id[1]==0x59 and $build_id[2]==0x55 and $build_id[3]==0x4f and $build_id[4]==0x4f) { #Only know how to cope with yuoo builds
|
|
return(1,"Remote video is not supported on this system");
|
|
}
|
|
#wvid should be a possiblity, time to do the http...
|
|
my $browser = LWP::UserAgent->new();
|
|
my $message = "$userid,$passwd";
|
|
$browser->cookie_jar({});
|
|
my $baseurl = "http://".$ipmi_bmcipaddr."/";
|
|
my $response = $browser->request(POST $baseurl."/session/create",'Content-Type'=>"text/xml",Content=>$message);
|
|
unless ($response->content eq "ok") {
|
|
return (1,"Server returned unexpected data");
|
|
}
|
|
|
|
$response = $browser->request(GET $baseurl."/kvm/kvm/jnlp");
|
|
my $jnlp = $response->content;
|
|
if ($jnlp =~ /This advanced option requires the purchase and installation/) {
|
|
return (1,"Node does not have feature key for remote video");
|
|
}
|
|
$jnlp =~ s!argument>title=.*Video Viewer</argument>!argument>title=$currnode wvid</argument>!;
|
|
my @return=("method:imm","jnlp:$jnlp");
|
|
if (grep /-m/,@cmdargv) {
|
|
$response = $browser->request(GET $baseurl."/kvm/vm/jnlp");
|
|
push @return,"mediajnlp:".$response->content;
|
|
}
|
|
return (0,@return);
|
|
}
|
|
|
|
|
|
sub power {
|
|
my $subcommand = shift;
|
|
|
|
my $netfun = 0x00;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my $code;
|
|
|
|
if($subcommand eq "stat") {
|
|
@cmd = (0x01);
|
|
}
|
|
elsif($subcommand eq "on") {
|
|
@cmd = (0x02,0x01);
|
|
}
|
|
elsif($subcommand eq "softoff") {
|
|
@cmd = (0x02,0x05);
|
|
}
|
|
elsif($subcommand eq "off") {
|
|
@cmd = (0x02,0x00);
|
|
}
|
|
elsif($subcommand eq "reset") {
|
|
@cmd = (0x02,0x03);
|
|
}
|
|
elsif($subcommand eq "nmi") {
|
|
@cmd = (0x02,0x04);
|
|
}
|
|
else {
|
|
return(1,"unsupported command power $subcommand");
|
|
}
|
|
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
}
|
|
else {
|
|
if($subcommand eq "stat") {
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
$code = $returnd[37-$authoffset];
|
|
|
|
if($code & 0b00000001) {
|
|
$text = "on";
|
|
}
|
|
else {
|
|
$text = "off";
|
|
}
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
}
|
|
if($subcommand eq "nmi") {
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
$text="nmi";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
}
|
|
if($subcommand eq "on") {
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
$text="on";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
}
|
|
if($subcommand eq "softoff") {
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
$text="softoff";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
}
|
|
if($subcommand eq "off") {
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
$text="off";
|
|
}
|
|
elsif($code == 0xd5) {
|
|
$text="off";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
}
|
|
if($subcommand eq "reset") {
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
$text="reset";
|
|
}
|
|
elsif($code == 0xd5) {
|
|
$text="off";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
}
|
|
|
|
if(!$text) {
|
|
$rc = 1;
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
}
|
|
|
|
return($rc,$text);
|
|
}
|
|
|
|
sub generic {
|
|
my $subcommand = shift;
|
|
my $netfun;
|
|
my @args;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my $code;
|
|
|
|
($netfun,@args) = split(/-/,$subcommand);
|
|
|
|
$netfun=oct($netfun);
|
|
printf("netfun: 0x%02x\n",$netfun);
|
|
|
|
print "command: ";
|
|
foreach(@args) {
|
|
push(@cmd,oct($_));
|
|
printf("0x%02x ",oct($_));
|
|
}
|
|
print "\n\n";
|
|
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
|
|
printf("return code: 0x%02x\n\n",$code);
|
|
|
|
print "return data:\n";
|
|
my @rdata = @returnd[37-$authoffset..@returnd-2];
|
|
hexadump(\@rdata);
|
|
print "\n";
|
|
|
|
print "full output:\n";
|
|
hexadump(\@returnd);
|
|
print "\n";
|
|
|
|
# if(!$text) {
|
|
# $rc = 1;
|
|
# $text = sprintf("unknown response %02x",$code);
|
|
# }
|
|
|
|
return($rc,$text);
|
|
}
|
|
|
|
sub beacon {
|
|
my $subcommand = shift;
|
|
|
|
my $netfun = 0x00;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my $code;
|
|
|
|
if($subcommand eq "on") {
|
|
if ($ipmiv2) {
|
|
@cmd = (0x04,0x0,0x01);
|
|
} else {
|
|
@cmd = (0x04,0xFF);
|
|
}
|
|
}
|
|
elsif($subcommand eq "off") {
|
|
if ($ipmiv2) {
|
|
@cmd = (0x04,0x0,0x00);
|
|
} else {
|
|
@cmd = (0x04,0x00);
|
|
}
|
|
}
|
|
else {
|
|
return(1,"unsupported command beacon $subcommand");
|
|
}
|
|
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
}
|
|
else {
|
|
if($subcommand eq "on") {
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
$text="on";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
}
|
|
if($subcommand eq "off") {
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
$text="off";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
}
|
|
|
|
if(!$text) {
|
|
$rc = 1;
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
}
|
|
|
|
return($rc,$text);
|
|
}
|
|
|
|
sub inv {
|
|
my $subcommand = shift;
|
|
|
|
my $rc = 0;
|
|
my $text;
|
|
my @output;
|
|
my @types;
|
|
my $format = "%-20s %s";
|
|
|
|
($rc,$text) = initsdr(); #Look for those precious locator reconds
|
|
if($rc != 0) {
|
|
return($rc,$text);
|
|
}
|
|
($rc,$text) = initfru();
|
|
if($rc != 0) {
|
|
return($rc,$text);
|
|
}
|
|
|
|
unless ($subcommand) {
|
|
$subcommand = "all";
|
|
}
|
|
if($subcommand eq "all") {
|
|
@types = qw(model serial deviceid mprom guid misc hw asset);
|
|
}
|
|
elsif($subcommand eq "asset") {
|
|
@types = qw(asset);
|
|
}
|
|
elsif($subcommand eq "model") {
|
|
@types = qw(model);
|
|
}
|
|
elsif($subcommand eq "serial") {
|
|
@types = qw(serial);
|
|
}
|
|
elsif($subcommand eq "vpd") {
|
|
@types = qw(model serial deviceid mprom);
|
|
}
|
|
elsif($subcommand eq "mprom") {
|
|
@types = qw(mprom);
|
|
}
|
|
elsif($subcommand eq "misc") {
|
|
@types = qw(misc);
|
|
}
|
|
elsif($subcommand eq "deviceid") {
|
|
@types = qw(deviceid);
|
|
}
|
|
elsif($subcommand eq "guid") {
|
|
@types = qw(guid);
|
|
}
|
|
elsif($subcommand eq "uuid") {
|
|
@types = qw(guid);
|
|
}
|
|
else {
|
|
@types = ($subcommand);
|
|
#return(1,"unsupported BMC inv argument $subcommand");
|
|
}
|
|
|
|
my $otext;
|
|
my $key;
|
|
|
|
foreach $key (sort keys %fru_hash) {
|
|
my $fru = $fru_hash{$key};
|
|
my $type;
|
|
foreach $type (split /,/,$fru->rec_type) {
|
|
if(grep {$_ eq $type} @types) {
|
|
$otext = sprintf($format,$fru_hash{$key}->desc . ":",$fru_hash{$key}->value);
|
|
#print $otext;
|
|
push(@output,$otext);
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
|
|
return($rc,@output);
|
|
}
|
|
|
|
sub initoemfru {
|
|
my $mfg_id = shift;
|
|
my $prod_id = shift;
|
|
my $device_id = shift;
|
|
|
|
my $netfun;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my @output;
|
|
my $code;
|
|
|
|
if($mfg_id == 2 && ($prod_id == 34869 or $prod_id == 31081 or $prod_id==34888)) {
|
|
$netfun = 0xc8;
|
|
|
|
@cmd=(0x05);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
|
|
if($rc != 0) {
|
|
if(!$text) {
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
|
|
my @oem_fru_data = @returnd[37-$authoffset..@returnd-2];
|
|
my $model_type = getascii(@oem_fru_data[0..3]);
|
|
my $model_number = getascii(@oem_fru_data[4..6]);
|
|
my $serial = getascii(@oem_fru_data[7..13]);
|
|
my $model = "$model_type-$model_number";
|
|
|
|
my $fru = FRU->new();
|
|
$fru->rec_type("serial");
|
|
$fru->desc("Serial Number");
|
|
$fru->value($serial);
|
|
$fru_hash{1} = $fru;
|
|
|
|
$fru = FRU->new();
|
|
$fru->rec_type("model");
|
|
$fru->desc("Model Number");
|
|
$fru->value($model);
|
|
$fru_hash{2} = $fru;
|
|
|
|
return(2,"");
|
|
}
|
|
if($mfg_id == 2 && $prod_id == 4 && 0) {
|
|
$netfun = 0x3a;
|
|
|
|
@cmd=(0x0b,0x0,0x0,0x0,0x1,0x8);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
|
|
if($rc != 0) {
|
|
if(!$text) {
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
|
|
hexadump(\@returnd);
|
|
return(2,"");
|
|
|
|
my @oem_fru_data = @returnd[37-$authoffset..@returnd-2];
|
|
my $model_type = getascii(@oem_fru_data[0..3]);
|
|
my $model_number = getascii(@oem_fru_data[4..6]);
|
|
my $serial = getascii(@oem_fru_data[7..13]);
|
|
my $model = "$model_type-$model_number";
|
|
|
|
my $fru = FRU->new();
|
|
$fru->rec_type("serial");
|
|
$fru->desc("Serial Number");
|
|
$fru->value($serial);
|
|
$fru_hash{1} = $fru;
|
|
|
|
$fru = FRU->new();
|
|
$fru->rec_type("model");
|
|
$fru->desc("Model Number");
|
|
$fru->value($model);
|
|
$fru_hash{2} = $fru;
|
|
|
|
return(2,"");
|
|
}
|
|
if($mfg_id == 2 && $prod_id == 20) {
|
|
my $serial = "unknown";
|
|
my $model = "x3655";
|
|
|
|
my $fru = FRU->new();
|
|
$fru->rec_type("serial");
|
|
$fru->desc("Serial Number");
|
|
$fru->value($serial);
|
|
$fru_hash{1} = $fru;
|
|
|
|
$fru = FRU->new();
|
|
$fru->rec_type("model");
|
|
$fru->desc("Model Number");
|
|
$fru->value($model);
|
|
$fru_hash{2} = $fru;
|
|
|
|
return(2,"");
|
|
}
|
|
if($mfg_id == 2 && $prod_id == 3) {
|
|
my $serial = "unknown";
|
|
my $model = "x346";
|
|
|
|
my $fru = FRU->new();
|
|
$fru->rec_type("serial");
|
|
$fru->desc("Serial Number");
|
|
$fru->value($serial);
|
|
$fru_hash{1} = $fru;
|
|
|
|
$fru = FRU->new();
|
|
$fru->rec_type("model");
|
|
$fru->desc("Model Number");
|
|
$fru->value($model);
|
|
$fru_hash{2} = $fru;
|
|
|
|
return(2,"");
|
|
}
|
|
if($mfg_id == 2 && $prod_id == 4) {
|
|
my $serial = "unknown";
|
|
my $model = "x336";
|
|
|
|
my $fru = FRU->new();
|
|
$fru->rec_type("serial");
|
|
$fru->desc("Serial Number");
|
|
$fru->value($serial);
|
|
$fru_hash{1} = $fru;
|
|
|
|
$fru = FRU->new();
|
|
$fru->rec_type("model");
|
|
$fru->desc("Model Number");
|
|
$fru->value($model);
|
|
$fru_hash{2} = $fru;
|
|
|
|
return(2,"");
|
|
}
|
|
my $serial = "unkown";
|
|
my $model = "unkown";
|
|
|
|
my $fru = FRU->new();
|
|
$fru->rec_type("serial");
|
|
$fru->desc("Serial Number");
|
|
$fru->value($serial);
|
|
$fru_hash{1} = $fru;
|
|
|
|
$fru = FRU->new();
|
|
$fru->rec_type("model");
|
|
$fru->desc("Model Number");
|
|
$fru->value($model);
|
|
$fru_hash{2} = $fru;
|
|
|
|
return(2,"");
|
|
|
|
|
|
return(1,"No OEM FRU Support");
|
|
}
|
|
|
|
sub add_textual_fru {
|
|
my $parsedfru = shift;
|
|
my $description = shift;
|
|
my $category = shift;
|
|
my $subcategory = shift;
|
|
my $types = shift;
|
|
|
|
if ($parsedfru->{$category} and $parsedfru->{$category}->{$subcategory}) {
|
|
my $fru;
|
|
my @subfrus;
|
|
|
|
if (ref $parsedfru->{$category}->{$subcategory} eq 'ARRAY') {
|
|
@subfrus = @{$parsedfru->{$category}->{$subcategory}};
|
|
} else {
|
|
@subfrus = ($parsedfru->{$category}->{$subcategory})
|
|
}
|
|
foreach (@subfrus) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type($types);
|
|
$fru->desc($description);
|
|
if (not ref $_) {
|
|
$fru->value($_);
|
|
} else {
|
|
if ($_->{encoding} == 3) {
|
|
$fru->value($_->{value});
|
|
} else {
|
|
$fru->value(phex($_->{value}));
|
|
}
|
|
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
}
|
|
}
|
|
sub add_textual_frus {
|
|
my $parsedfru = shift;
|
|
my $desc = shift;
|
|
my $categorydesc = shift;
|
|
my $category = shift;
|
|
my $type = shift;
|
|
unless ($type) { $type = 'hw'; }
|
|
add_textual_fru($parsedfru,$desc." ".$categorydesc."Part Number",$category,"partnumber","hw");
|
|
add_textual_fru($parsedfru,$desc." ".$categorydesc."Manufacturer",$category,"manufacturer","hw");
|
|
add_textual_fru($parsedfru,$desc." ".$categorydesc."Serial Number",$category,"serialnumber","hw");
|
|
add_textual_fru($parsedfru,$desc." ".$categorydesc."",$category,"name","hw");
|
|
if ($parsedfru->{$category}->{builddate}) {
|
|
add_textual_fru($parsedfru,$desc." ".$categorydesc."Manufacture Date",$category,"builddate","hw");
|
|
}
|
|
if ($parsedfru->{$category}->{buildlocation}) {
|
|
add_textual_fru($parsedfru,$desc." ".$categorydesc."Manufacture Location",$category,"buildlocation","hw");
|
|
}
|
|
if ($parsedfru->{$category}->{model}) {
|
|
add_textual_fru($parsedfru,$desc." ".$categorydesc."Model",$category,"model","hw");
|
|
}
|
|
add_textual_fru($parsedfru,$desc." ".$categorydesc."Additional Info",$category,"extra","hw");
|
|
}
|
|
|
|
sub initfru {
|
|
my $netfun = 0x28;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my @output;
|
|
my $code;
|
|
|
|
my $mfg_id;
|
|
my $prod_id;
|
|
my $device_id;
|
|
my $dev_rev;
|
|
my $fw_rev1;
|
|
my $fw_rev2;
|
|
my $mprom;
|
|
my $fru;
|
|
my $guid;
|
|
my @guidcmd;
|
|
|
|
($rc,$text,$mfg_id,$prod_id,$device_id,$dev_rev,$fw_rev1,$fw_rev2) = getdevid();
|
|
if($rc != 0) {
|
|
return($rc,$text);
|
|
}
|
|
|
|
@guidcmd = (0x18,0x37);
|
|
if($mfg_id == 2 && $prod_id == 34869) {
|
|
@guidcmd = (0x18,0x08);
|
|
}
|
|
if($mfg_id == 2 && $prod_id == 4) {
|
|
@guidcmd = (0x18,0x08);
|
|
}
|
|
if($mfg_id == 2 && $prod_id == 3) {
|
|
@guidcmd = (0x18,0x08);
|
|
}
|
|
|
|
($rc,$text,$guid) = getguid(\@guidcmd);
|
|
if($rc != 0) {
|
|
return($rc,$text);
|
|
}
|
|
|
|
if($mfg_id == 2 && $prod_id == 34869) {
|
|
$mprom = sprintf("%x.%x",$fw_rev1,$fw_rev2);
|
|
}
|
|
elsif ($mfg_id == 2) {
|
|
my @lcmd = (0x50);
|
|
my @lreturnd = ();
|
|
my $lerror = docmd(
|
|
0xe8,
|
|
\@lcmd,
|
|
\@lreturnd
|
|
);
|
|
if ($lerror eq "" && $lreturnd[36-$authoffset] == 0) {
|
|
my @a = ($fw_rev2);
|
|
my @b= @lreturnd[37-$authoffset .. $#lreturnd-1];
|
|
$mprom = sprintf("%d.%s (%s)",$fw_rev1,decodebcd(\@a),getascii(@b));
|
|
} else {
|
|
my @a = ($fw_rev2);
|
|
$mprom = sprintf("%d.%s",$fw_rev1,decodebcd(\@a));
|
|
}
|
|
} else {
|
|
my @a = ($fw_rev2);
|
|
$mprom = sprintf("%d.%s",$fw_rev1,decodebcd(\@a));
|
|
}
|
|
|
|
$fru = FRU->new();
|
|
$fru->rec_type("mprom");
|
|
$fru->desc("BMC Firmware");
|
|
$fru->value($mprom);
|
|
$fru_hash{mprom} = $fru;
|
|
|
|
$fru = FRU->new();
|
|
$fru->rec_type("guid");
|
|
$fru->desc("GUID");
|
|
$fru->value($guid);
|
|
$fru_hash{guid} = $fru;
|
|
|
|
$fru = FRU->new();
|
|
$fru->rec_type("deviceid");
|
|
$fru->desc("Manufacturer ID");
|
|
my $value = $mfg_id;
|
|
if($MFG_ID{$mfg_id}) {
|
|
$value = "$MFG_ID{$mfg_id} ($mfg_id)";
|
|
}
|
|
$fru->value($value);
|
|
$fru_hash{mfg_id} = $fru;
|
|
|
|
$fru = FRU->new();
|
|
$fru->rec_type("deviceid");
|
|
$fru->desc("Product ID");
|
|
$value = $prod_id;
|
|
my $tmp = "$mfg_id:$prod_id";
|
|
if($PROD_ID{$tmp}) {
|
|
$value = "$PROD_ID{$tmp} ($prod_id)";
|
|
}
|
|
$fru->value($value);
|
|
$fru_hash{prod_id} = $fru;
|
|
|
|
$fru = FRU->new();
|
|
$fru->rec_type("deviceid");
|
|
$fru->desc("Device ID");
|
|
$fru->value($device_id);
|
|
$fru_hash{device_id} = $fru;
|
|
|
|
# ($rc,$text)=initoemfru($mfg_id,$prod_id,$device_id);
|
|
# if($rc == 1) {
|
|
# return($rc,$text);
|
|
# }
|
|
# if($rc == 2) {
|
|
# return(0,"");
|
|
# }
|
|
$netfun = 0x28; # Storage (0x0A << 2)
|
|
my @bytes;
|
|
@cmd=(0x10,0x00);
|
|
$error = docmd($netfun,\@cmd,\@bytes);
|
|
if ($error) { return (1,$error); }
|
|
@bytes=splice @bytes,36-$authoffset;
|
|
pop @bytes;
|
|
unless (defined $bytes[0] and $bytes[0] == 0) {
|
|
if ($codes{$bytes[0]}) {
|
|
return (1,"FRU device 0 inaccessible".$codes{$bytes[0]});
|
|
} else {
|
|
return (1,"FRU device 0 inaccessible");
|
|
}
|
|
}
|
|
my $frusize=($bytes[2]<<8)+$bytes[1];
|
|
|
|
($rc,@bytes) = frudump(0,$frusize,16);
|
|
if($rc != 0) {
|
|
return($rc,@bytes);
|
|
}
|
|
my $fruhash;
|
|
($error,$fruhash) = parsefru(\@bytes);
|
|
if ($error) {
|
|
($rc,$text)=initoemfru($mfg_id,$prod_id,$device_id);
|
|
if($rc == 1) {
|
|
$text = "FRU format unknown";
|
|
return($rc,$text);
|
|
}
|
|
if($rc == 2) {
|
|
return(0,"");
|
|
}
|
|
}
|
|
$frudex=0;
|
|
if (defined $fruhash->{product}->{manufacturer}->{value}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("misc");
|
|
$fru->desc("System Manufacturer");
|
|
if ($fruhash->{product}->{product}->{encoding}==3) {
|
|
$fru->value($fruhash->{product}->{manufacturer}->{value});
|
|
} else {
|
|
$fru->value(phex($fruhash->{product}->{manufacturer}->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
if (defined $fruhash->{product}->{product}->{value}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("model");
|
|
$fru->desc("System Description");
|
|
if ($fruhash->{product}->{product}->{encoding}==3) {
|
|
$fru->value($fruhash->{product}->{product}->{value});
|
|
} else {
|
|
$fru->value(phex($fruhash->{product}->{product}->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
if (defined $fruhash->{product}->{model}->{value}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("model");
|
|
$fru->desc("System Model/MTM");
|
|
if ($fruhash->{product}->{model}->{encoding}==3) {
|
|
$fru->value($fruhash->{product}->{model}->{value});
|
|
} else {
|
|
$fru->value(phex($fruhash->{product}->{model}->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
if (defined $fruhash->{product}->{version}->{value}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("misc");
|
|
$fru->desc("System Revision");
|
|
if ($fruhash->{product}->{version}->{encoding}==3) {
|
|
$fru->value($fruhash->{product}->{version}->{value});
|
|
} else {
|
|
$fru->value(phex($fruhash->{product}->{version}->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
if (defined $fruhash->{product}->{serialnumber}->{value}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("serial");
|
|
$fru->desc("System Serial Number");
|
|
if ($fruhash->{product}->{serialnumber}->{encoding}==3) {
|
|
$fru->value($fruhash->{product}->{serialnumber}->{value});
|
|
} else {
|
|
$fru->value(phex($fruhash->{product}->{serialnumber}->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
if (defined $fruhash->{product}->{asset}->{value}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("asset");
|
|
$fru->desc("System Asset Number");
|
|
if ($fruhash->{product}->{asset}->{encoding}==3) {
|
|
$fru->value($fruhash->{product}->{asset}->{value});
|
|
} else {
|
|
$fru->value(phex($fruhash->{product}->{asset}->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
foreach (@{$fruhash->{product}->{extra}}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("misc");
|
|
$fru->desc("Product Extra data");
|
|
if ($_->{encoding} == 3) {
|
|
$fru->value($_->{value});
|
|
} else {
|
|
#print Dumper($_);
|
|
#print $_->{encoding};
|
|
next;
|
|
$fru->value(phex($_->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
|
|
|
|
if ($fruhash->{chassis}->{serialnumber}->{value}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("serial");
|
|
$fru->desc("Chassis Serial Number");
|
|
if ($fruhash->{chassis}->{serialnumber}->{encoding}==3) {
|
|
$fru->value($fruhash->{chassis}->{serialnumber}->{value});
|
|
} else {
|
|
$fru->value(phex($fruhash->{chassis}->{serialnumber}->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
|
|
if ($fruhash->{chassis}->{partnumber}->{value}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("model");
|
|
$fru->desc("Chassis Part Number");
|
|
if ($fruhash->{chassis}->{partnumber}->{encoding}==3) {
|
|
$fru->value($fruhash->{chassis}->{partnumber}->{value});
|
|
} else {
|
|
$fru->value(phex($fruhash->{chassis}->{partnumber}->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
|
|
|
|
foreach (@{$fruhash->{chassis}->{extra}}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("misc");
|
|
$fru->desc("Chassis Extra data");
|
|
if ($_->{encoding} == 3) {
|
|
$fru->value($_->{value});
|
|
} else {
|
|
next;
|
|
#print Dumper($_);
|
|
#print $_->{encoding};
|
|
$fru->value(phex($_->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
|
|
if ($fruhash->{board}->{builddate}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("misc");
|
|
$fru->desc("Board manufacture date");
|
|
$fru->value($fruhash->{board}->{builddate});
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
|
|
if ($fruhash->{board}->{manufacturer}->{value}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("misc");
|
|
$fru->desc("Board manufacturer");
|
|
if ($fruhash->{board}->{manufacturer}->{encoding}==3) {
|
|
$fru->value($fruhash->{board}->{manufacturer}->{value});
|
|
} else {
|
|
$fru->value(phex($fruhash->{board}->{manufacturer}->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
if ($fruhash->{board}->{name}->{value}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("misc");
|
|
$fru->desc("Board Description");
|
|
if ($fruhash->{board}->{name}->{encoding}==3) {
|
|
$fru->value($fruhash->{board}->{name}->{value});
|
|
} else {
|
|
$fru->value(phex($fruhash->{board}->{name}->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
if ($fruhash->{board}->{serialnumber}->{value}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("misc");
|
|
$fru->desc("Board Serial Number");
|
|
if ($fruhash->{board}->{serialnumber}->{encoding}==3) {
|
|
$fru->value($fruhash->{board}->{serialnumber}->{value});
|
|
} else {
|
|
$fru->value(phex($fruhash->{board}->{serialnumber}->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
if ($fruhash->{board}->{partnumber}->{value}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("misc");
|
|
$fru->desc("Board Model Number");
|
|
if ($fruhash->{board}->{partnumber}->{encoding}==3) {
|
|
$fru->value($fruhash->{board}->{partnumber}->{value});
|
|
} else {
|
|
$fru->value(phex($fruhash->{board}->{partnumber}->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
foreach (@{$fruhash->{board}->{extra}}) {
|
|
$fru = FRU->new();
|
|
$fru->rec_type("misc");
|
|
$fru->desc("Board Extra data");
|
|
if ($_->{encoding} == 3) {
|
|
$fru->value($_->{value});
|
|
} else {
|
|
next;
|
|
#print Dumper($_);
|
|
#print $_->{encoding};
|
|
$fru->value(phex($_->{value}));
|
|
}
|
|
$fru_hash{$frudex++} = $fru;
|
|
}
|
|
#Ok, done with fru 0, on to the other fru devices from SDR
|
|
my $key;
|
|
my $subrc;
|
|
foreach $key (sort {$sdr_hash{$a}->id_string cmp $sdr_hash{$b}->id_string} keys %sdr_hash) {
|
|
my $sdr = $sdr_hash{$key};
|
|
unless ($sdr->rec_type == 0x11 and $sdr->fru_type == 0x10) { #skip non fru sdr stuff and frus I don't understand
|
|
next;
|
|
}
|
|
|
|
if ($sdr->fru_type == 0x10) { #supported
|
|
if ($sdr->fru_subtype == 0x1) { #DIMM
|
|
$fru = FRU->new();
|
|
$fru->rec_type("hw,dimm");
|
|
$fru->desc($sdr->id_string);
|
|
($subrc,@bytes) = frudump(0,get_frusize($sdr->sensor_number),16,$sdr->sensor_number);
|
|
if ($subrc) {
|
|
print $sdr->id_string.":".$bytes[0]."\n";
|
|
$fru->value($bytes[0]);
|
|
$fru_hash{$frudex++} = $fru;
|
|
next;
|
|
}
|
|
my $parsedfru = decode_spd(@bytes);
|
|
add_textual_frus($parsedfru,$sdr->id_string,"",'product','dimm,hw');
|
|
} elsif ($sdr->fru_subtype == 0 or $sdr->fru_subtype == 2) {
|
|
($subrc,@bytes) = frudump(0,get_frusize($sdr->sensor_number),16,$sdr->sensor_number);
|
|
if ($subrc) {
|
|
$fru = FRU->new();
|
|
$fru->value($bytes[0]);
|
|
$fru->rec_type("hw");
|
|
$fru->desc($sdr->id_string);
|
|
$fru_hash{$frudex++} = $fru;
|
|
next;
|
|
}
|
|
my $parsedfru=parsefru(\@bytes);
|
|
add_textual_frus($parsedfru,$sdr->id_string,"Board ",'board');
|
|
add_textual_frus($parsedfru,$sdr->id_string,"Product ",'product');
|
|
add_textual_frus($parsedfru,$sdr->id_string,"Chassis ",'chassis');
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return($rc,$text);
|
|
}
|
|
sub get_frusize {
|
|
my $fruid=shift;
|
|
my $netfun = 0x28; # Storage (0x0A << 2)
|
|
my @cmd=(0x10,$fruid);
|
|
my @bytes;
|
|
my $error = docmd($netfun,\@cmd,\@bytes);
|
|
@bytes=splice @bytes,36-$authoffset;
|
|
pop @bytes;
|
|
unless (defined $bytes[0] and $bytes[0] == 0) {
|
|
if ($codes{$bytes[0]}) {
|
|
return (0,$codes{$bytes[0]});
|
|
}
|
|
return (0,"FRU device $fruid inaccessible");
|
|
}
|
|
return ($bytes[2]<<8)+$bytes[1];
|
|
}
|
|
|
|
sub formfru {
|
|
my $fruhash = shift;
|
|
my $frusize = shift;
|
|
$frusize-=8; #consume 8 bytes for mandatory header
|
|
my $availindex=1;
|
|
my @bytes=(1,0,0,0,0,0,0,0); #
|
|
if ($fruhash->{internal}) { #Allocate the space at header time
|
|
$bytes[1]=$availindex;
|
|
$availindex+=ceil((scalar @{$fruhash->{internal}})/8);
|
|
$frusize-=(scalar @{$fruhash->{internal}}); #consume internal bytes
|
|
push @bytes,@{$fruhash->{internal}};
|
|
}
|
|
if ($fruhash->{chassis}) {
|
|
$bytes[2]=$availindex;
|
|
push @bytes,@{$fruhash->{chassis}->{raw}};
|
|
$availindex+=ceil((scalar @{$fruhash->{chassis}->{raw}})/8);
|
|
$frusize -= ceil((scalar @{$fruhash->{chassis}->{raw}})/8)*8;
|
|
}
|
|
if ($fruhash->{board}) {
|
|
$bytes[3]=$availindex;
|
|
push @bytes,@{$fruhash->{board}->{raw}};
|
|
$availindex+=ceil((scalar @{$fruhash->{board}->{raw}})/8);
|
|
$frusize -= ceil((scalar @{$fruhash->{board}->{raw}})/8)*8;
|
|
}
|
|
#xCAT will always have a product FRU in this process
|
|
$bytes[4]=$availindex;
|
|
unless (defined $fruhash->{product}) { #Make sure there is a data structure
|
|
#to latch onto..
|
|
$fruhash->{product}={};
|
|
}
|
|
my @prodbytes = buildprodfru($fruhash->{product});
|
|
push @bytes,@prodbytes;
|
|
$availindex+=ceil((scalar @prodbytes)/8);
|
|
$frusize -= ceil((scalar @prodbytes)/8)*8;;
|
|
#End of product fru setup
|
|
if ($fruhash->{extra}) {
|
|
$bytes[5]=$availindex;
|
|
push @bytes,@{$fruhash->{extra}};
|
|
$frusize -= ceil((scalar @{$fruhash->{extra}})/8)*8;
|
|
#Don't need to track availindex anymore
|
|
}
|
|
$bytes[7] = dochksum([@bytes[0..6]]);
|
|
if ($frusize<0) {
|
|
return undef;
|
|
} else {
|
|
return \@bytes;
|
|
}
|
|
}
|
|
|
|
sub transfieldtobytes {
|
|
my $hashref=shift;
|
|
unless (defined $hashref) {
|
|
return (0xC0);
|
|
}
|
|
my @data;
|
|
my $size;
|
|
if ($hashref->{encoding} ==3) {
|
|
@data=unpack("C*",$hashref->{value});
|
|
} else {
|
|
@data=@{$hashref->{value}};
|
|
}
|
|
$size=scalar(@data);
|
|
if ($size > 64) {
|
|
die "Field too large for IPMI FRU specification";
|
|
}
|
|
unshift(@data,$size|($hashref->{encoding}<<6));
|
|
return @data;
|
|
}
|
|
sub mergefru {
|
|
my $phash = shift; #Product hash
|
|
if ($vpdhash->{$currnode}->[0]->{mtm}) {
|
|
$phash->{model}->{encoding}=3;
|
|
$phash->{model}->{value}=$vpdhash->{$currnode}->[0]->{mtm};
|
|
}
|
|
if ($vpdhash->{$currnode}->[0]->{serial}) {
|
|
$phash->{serialnumber}->{encoding}=3;
|
|
$phash->{serialnumber}->{value}=$vpdhash->{$currnode}->[0]->{serial};
|
|
}
|
|
if ($vpdhash->{$currnode}->[0]->{asset}) {
|
|
$phash->{asset}->{encoding}=3;
|
|
$phash->{asset}->{value}=$vpdhash->{$currnode}->[0]->{asset};
|
|
}
|
|
}
|
|
|
|
sub buildprodfru {
|
|
my $prod=shift;
|
|
mergefru($prod);
|
|
my @bytes=(1,0,0);
|
|
my @data;
|
|
my $padsize;
|
|
push @bytes,transfieldtobytes($prod->{manufacturer});
|
|
push @bytes,transfieldtobytes($prod->{product});
|
|
push @bytes,transfieldtobytes($prod->{model});
|
|
push @bytes,transfieldtobytes($prod->{version});
|
|
push @bytes,transfieldtobytes($prod->{serialnumber});
|
|
push @bytes,transfieldtobytes($prod->{asset});
|
|
push @bytes,transfieldtobytes($prod->{fruid});
|
|
push @bytes,transfieldtobytes($prod->{fruid});
|
|
foreach (@{$prod->{extra}}) {
|
|
my $sig=getascii(transfieldtobytes($_));
|
|
unless ($sig and $sig =~ /FRU by xCAT/) {
|
|
push @bytes,transfieldtobytes($_);
|
|
}
|
|
}
|
|
push @bytes,transfieldtobytes({encoding=>3,value=>"$currnode FRU by xCAT ".xCAT::Utils::Version('short')});
|
|
push @bytes,(0xc1);
|
|
$bytes[1]=ceil((scalar(@bytes)+1)/8);
|
|
$padsize=(ceil((scalar(@bytes)+1)/8)*8)-scalar(@bytes)-1;
|
|
while ($padsize--) {
|
|
push @bytes,(0x00);
|
|
}
|
|
$padsize=dochksum(\@bytes);#reuse padsize for a second to store checksum
|
|
push @bytes,$padsize;
|
|
|
|
return @bytes;
|
|
}
|
|
|
|
sub fru {
|
|
my $subcommand = shift;
|
|
my $netfun = 0x28;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my @output;
|
|
my $code;
|
|
|
|
@cmd=(0x10,0x00);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
|
|
if($rc != 0) {
|
|
if(!$text) {
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
|
|
my $fru_size_ls = $returnd[37-$authoffset];
|
|
my $fru_size_ms = $returnd[38-$authoffset];
|
|
my $fru_size = $fru_size_ms*256 + $fru_size_ls;
|
|
|
|
if($subcommand eq "dump") {
|
|
print "FRU Size: $fru_size\n";
|
|
my ($rc,@output) = frudump(0,$fru_size,8);
|
|
if($rc) {
|
|
return($rc,@output);
|
|
}
|
|
hexadump(\@output);
|
|
return(0,"");
|
|
}
|
|
if($subcommand eq "wipe") {
|
|
my @bytes = ();
|
|
|
|
for(my $i = 0;$i < $fru_size;$i++) {
|
|
push(@bytes,0xff);
|
|
}
|
|
my ($rc,$text) = fruwrite(0,\@bytes,8);
|
|
if($rc) {
|
|
return($rc,$text);
|
|
}
|
|
return(0,"FRU $fru_size bytes wiped");
|
|
}
|
|
|
|
return(0,"");
|
|
}
|
|
|
|
sub frudump {
|
|
my $offset = shift;
|
|
my $length = shift;
|
|
my $chunk = shift;
|
|
my $fruid = shift;
|
|
unless (defined $fruid) { $fruid = 0; }
|
|
unless ($length) { return (1,$chunk); } #chunk happens to get the error text
|
|
|
|
my $netfun = 0x28;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my @output;
|
|
my $code;
|
|
my @fru_data=();
|
|
|
|
for(my $c=$offset;$c < $length+$offset;$c += $chunk) {
|
|
my $ms = int($c / 0x100);
|
|
my $ls = $c - $ms * 0x100;
|
|
my $reqsize = $chunk;
|
|
if ($c+$chunk > $length+$offset) {
|
|
$reqsize = ($length+$offset-$c);
|
|
}
|
|
|
|
@cmd=(0x11,$fruid,$ls,$ms,$reqsize);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
|
|
if($rc != 0) {
|
|
if(!$text) {
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
|
|
my $count = $returnd[37-$authoffset];
|
|
if($count != $reqsize) {
|
|
$rc = 1;
|
|
$text = "FRU read error (bytes requested: $reqsize, got: $count)";
|
|
return($rc,$text);
|
|
}
|
|
|
|
my @data = @returnd[38-$authoffset..@returnd-2];
|
|
@fru_data = (@fru_data,@data);
|
|
}
|
|
|
|
return(0,@fru_data);
|
|
}
|
|
|
|
sub parsefru {
|
|
my $bytes = shift;
|
|
my $fruhash;
|
|
my $curridx; #store indexes as needed for convenience
|
|
my $currsize; #store current size
|
|
my $subidx;
|
|
my @currarea;
|
|
unless ($bytes->[0]==1) {
|
|
if ($bytes->[0]==0 or $bytes->[0]==0xff) { #not in spec, but probably unitialized, xCAT probably will rewrite fresh
|
|
return "clear",undef;
|
|
} else { #some meaning suggested, but not parsable, xCAT shouldn't meddle
|
|
return "unknown",undef;
|
|
}
|
|
}
|
|
if ($bytes->[1]) { #The FRU spec, unfortunately, gave no easy way to tell the size of internal area
|
|
#consequently, will find the next defined field and preserve the addressing and size of current FRU
|
|
#area until then
|
|
my $internal_size;
|
|
if ($bytes->[2]) {
|
|
$internal_size=$bytes->[2]*8-($bytes->[1]*8);
|
|
} elsif ($bytes->[3]) {
|
|
$internal_size=$bytes->[3]*8-($bytes->[1]*8);
|
|
} elsif ($bytes->[4]) {
|
|
$internal_size=$bytes->[4]*8-($bytes->[1]*8);
|
|
} elsif ($bytes->[5]) {
|
|
$internal_size=$bytes->[5]*8-($bytes->[1]*8);
|
|
} else { #The FRU area is intact enough to signify xCAT can't safely manipulate contents
|
|
return "unknown-winternal",undef;
|
|
}
|
|
#capture slice of bytes
|
|
$fruhash->{internal}=[@{$bytes}[($bytes->[1]*8)..($bytes->[1]*8+$internal_size-1)]]; #,$bytes->[1]*8,$internal_size];
|
|
}
|
|
if ($bytes->[2]) { #Chassis info area, xCAT will preserve fields, not manipulate them
|
|
$curridx=$bytes->[2]*8;
|
|
unless ($bytes->[$curridx]==1) { #definitely unparsable, but the section is preservable
|
|
return "unknown-COULDGUESS",undef; #be lazy for now, TODO revisit this and add guessing if it ever matters
|
|
}
|
|
$currsize=($bytes->[$curridx+1])*8;
|
|
@currarea=@{$bytes}[$curridx..($curridx+$currsize-1)]; #splice @$bytes,$curridx,$currsize;
|
|
$fruhash->{chassis} = parsechassis(@currarea);
|
|
}
|
|
if ($bytes->[3]) { #Board info area, to be preserved
|
|
$curridx=$bytes->[3]*8;
|
|
unless ($bytes->[$curridx]==1) {
|
|
return "unknown-COULDGUESS",undef;
|
|
}
|
|
$currsize=($bytes->[$curridx+1])*8;
|
|
@currarea=@{$bytes}[$curridx..($curridx+$currsize-1)];
|
|
$fruhash->{board} = parseboard(@currarea);
|
|
}
|
|
if ($bytes->[4]) { #Product info area present, will probably be thoroughly modified
|
|
$curridx=$bytes->[4]*8;
|
|
unless ($bytes->[$curridx]==1) {
|
|
return "unknown-COULDGUESS",undef;
|
|
}
|
|
$currsize=($bytes->[$curridx+1])*8;
|
|
@currarea=@{$bytes}[$curridx..($curridx+$currsize-1)];
|
|
$fruhash->{product} = parseprod(@currarea);
|
|
}
|
|
if ($bytes->[5]) { #Generic multirecord present..
|
|
$fruhash->{extra}=[];
|
|
my $last=0;
|
|
$curridx=$bytes->[5]*8;
|
|
my $currsize;
|
|
while (not $last) {
|
|
if ($bytes->[$curridx+1] & 128) {
|
|
$last=1;
|
|
}
|
|
$currsize=$bytes->[$curridx+2];
|
|
push @{$fruhash->{extra}},$bytes->[$curridx..$curridx+4+$currsize-1];
|
|
}
|
|
}
|
|
return 0,$fruhash;
|
|
}
|
|
|
|
sub parseprod {
|
|
my @area = @_;
|
|
my %info;
|
|
my $language=$area[2];
|
|
my $idx=3;
|
|
my $currsize;
|
|
my $currdata;
|
|
my $encode;
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
unless ($currsize) {
|
|
return \%info;
|
|
}
|
|
$idx+=$currsize;
|
|
if ($currsize>1) {
|
|
$info{manufacturer}->{encoding}=$encode;
|
|
$info{manufacturer}->{value}=$currdata;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
unless ($currsize) {
|
|
return \%info;
|
|
}
|
|
$idx+=$currsize;
|
|
if ($currsize>1) {
|
|
$info{product}->{encoding}=$encode;
|
|
$info{product}->{value}=$currdata;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
unless ($currsize) {
|
|
return \%info;
|
|
}
|
|
$idx+=$currsize;
|
|
if ($currsize>1) {
|
|
$info{model}->{encoding}=$encode;
|
|
$info{model}->{value}=$currdata;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
unless ($currsize) {
|
|
return \%info;
|
|
}
|
|
$idx+=$currsize;
|
|
if ($currsize>1) {
|
|
$info{version}->{encoding}=$encode;
|
|
$info{version}->{value}=$currdata;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
unless ($currsize) {
|
|
return \%info;
|
|
}
|
|
$idx+=$currsize;
|
|
if ($currsize>1) {
|
|
$info{serialnumber}->{encoding}=$encode;
|
|
$info{serialnumber}->{value}=$currdata;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
unless ($currsize) {
|
|
return \%info;
|
|
}
|
|
$idx+=$currsize;
|
|
if ($currsize>1) {
|
|
$info{asset}->{encoding}=$encode;
|
|
$info{asset}->{value}=$currdata;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
unless ($currsize) {
|
|
return \%info;
|
|
}
|
|
$idx+=$currsize;
|
|
if ($currsize>1) {
|
|
$info{fruid}->{encoding}=$encode;
|
|
$info{fruid}->{value}=$currdata;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
if ($currsize) {
|
|
$info{extra}=[];
|
|
}
|
|
while ($currsize>0) {
|
|
if ($currsize>1) {
|
|
push @{$info{extra}},{value=>$currdata,encoding=>$encode};
|
|
}
|
|
$idx+=$currsize;
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
}
|
|
return \%info;
|
|
|
|
}
|
|
sub parseboard {
|
|
my @area = @_;
|
|
my %boardinf;
|
|
my $idx=6;
|
|
my $language=$area[2];
|
|
my $tstamp = ($area[3]+($area[4]<<8)+($area[5]<<16))*60+820472400; #820472400 is meant to be 1/1/1996
|
|
$boardinf{raw}=[@area]; #store for verbatim replacement
|
|
unless ($tstamp == 820472400) {
|
|
$boardinf{builddate}=scalar localtime($tstamp);
|
|
}
|
|
my $encode;
|
|
my $currsize;
|
|
my $currdata;
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
unless ($currsize) {
|
|
return \%boardinf;
|
|
}
|
|
$idx+=$currsize;
|
|
if ($currsize>1) {
|
|
$boardinf{manufacturer}->{encoding}=$encode;
|
|
$boardinf{manufacturer}->{value}=$currdata;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
unless ($currsize) {
|
|
return \%boardinf;
|
|
}
|
|
$idx+=$currsize;
|
|
if ($currsize>1) {
|
|
$boardinf{name}->{encoding}=$encode;
|
|
$boardinf{name}->{value}=$currdata;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
unless ($currsize) {
|
|
return \%boardinf;
|
|
}
|
|
$idx+=$currsize;
|
|
if ($currsize>1) {
|
|
$boardinf{serialnumber}->{encoding}=$encode;
|
|
$boardinf{serialnumber}->{value}=$currdata;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
unless ($currsize) {
|
|
return \%boardinf;
|
|
}
|
|
$idx+=$currsize;
|
|
if ($currsize>1) {
|
|
$boardinf{partnumber}->{encoding}=$encode;
|
|
$boardinf{partnumber}->{value}=$currdata;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
unless ($currsize) {
|
|
return \%boardinf;
|
|
}
|
|
$idx+=$currsize;
|
|
if ($currsize>1) {
|
|
$boardinf{fruid}->{encoding}=$encode;
|
|
$boardinf{fruid}->{value}=$currdata;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
if ($currsize) {
|
|
$boardinf{extra}=[];
|
|
}
|
|
while ($currsize>0) {
|
|
if ($currsize>1) {
|
|
push @{$boardinf{extra}},{value=>$currdata,encoding=>$encode};
|
|
}
|
|
$idx+=$currsize;
|
|
($currsize,$currdata,$encode)=extractfield(\@area,$idx);
|
|
}
|
|
return \%boardinf;
|
|
}
|
|
sub parsechassis {
|
|
my @chassarea=@_;
|
|
my %chassisinf;
|
|
my $currsize;
|
|
my $currdata;
|
|
my $idx=3;
|
|
my $encode;
|
|
$chassisinf{raw}=[@chassarea]; #store for verbatim replacement
|
|
$chassisinf{type}="unknown";
|
|
if ($chassis_types{$chassarea[2]}) {
|
|
$chassisinf{type}=$chassis_types{$chassarea[2]};
|
|
}
|
|
if ($chassarea[$idx] == 0xc1) {
|
|
return \%chassisinf;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@chassarea,$idx);
|
|
unless ($currsize) {
|
|
return \%chassisinf;
|
|
}
|
|
$idx+=$currsize;
|
|
if ($currsize>1) {
|
|
$chassisinf{partnumber}->{encoding}=$encode;
|
|
$chassisinf{partnumber}->{value}=$currdata;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@chassarea,$idx);
|
|
unless ($currsize) {
|
|
return \%chassisinf;
|
|
}
|
|
$idx+=$currsize;
|
|
if ($currsize>1) {
|
|
$chassisinf{serialnumber}->{encoding}=$encode;
|
|
$chassisinf{serialnumber}->{value}=$currdata;
|
|
}
|
|
($currsize,$currdata,$encode)=extractfield(\@chassarea,$idx);
|
|
if ($currsize) {
|
|
$chassisinf{extra}=[];
|
|
}
|
|
while ($currsize>0) {
|
|
if ($currsize>1) {
|
|
push @{$chassisinf{extra}},{value=>$currdata,encoding=>$encode};
|
|
}
|
|
$idx+=$currsize;
|
|
($currsize,$currdata,$encode)=extractfield(\@chassarea,$idx);
|
|
}
|
|
return \%chassisinf;
|
|
}
|
|
|
|
sub extractfield { #idx is location of the type/length byte, returns something appropriate
|
|
my $area = shift;
|
|
my $idx = shift;
|
|
my $language=shift;
|
|
my $data;
|
|
my $size = $area->[$idx] & 0b00111111;
|
|
my $encoding = ($area->[$idx] & 0b11000000)>>6;
|
|
unless ($size) {
|
|
return 1,undef,undef;
|
|
}
|
|
if ($size==1 && $encoding==3) {
|
|
return 0,'','';
|
|
}
|
|
if ($encoding==3) {
|
|
$data=getascii(@$area[$idx+1..$size+$idx]);
|
|
} else {
|
|
$data = [@$area[$idx+1..$size+$idx]];
|
|
}
|
|
return $size+1,$data,$encoding;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sub writefru {
|
|
my $netfun = 0x28; # Storage (0x0A << 2)
|
|
my @cmd=(0x10,0);
|
|
my @bytes;
|
|
my $error = docmd($netfun,\@cmd,\@bytes);
|
|
@bytes=splice @bytes,36-$authoffset;
|
|
pop @bytes;
|
|
unless (defined $bytes[0] and $bytes[0] == 0) {
|
|
return (1,"FRU device 0 inaccessible");
|
|
}
|
|
my $frusize=($bytes[2]<<8)+$bytes[1];
|
|
($error,@bytes) = frudump(0,$frusize,16);
|
|
if ($error) {
|
|
return (1,"Error retrieving FRU: ".$error);
|
|
}
|
|
my $fruhash;
|
|
($error,$fruhash) = parsefru(\@bytes);
|
|
my $newfru=formfru($fruhash,$frusize);
|
|
unless ($newfru) {
|
|
return (1,"FRU data will not fit in BMC FRU space, fields too long");
|
|
}
|
|
my $rc=1;
|
|
my $writeattempts=0;
|
|
my $text;
|
|
while ($rc and $writeattempts<15) {
|
|
if ($writeattempts) {
|
|
sleep 1;
|
|
}
|
|
($rc,$text) = fruwrite(0,$newfru,8);
|
|
if ($text =~ /rotected/) {
|
|
last;
|
|
}
|
|
$writeattempts++;
|
|
}
|
|
if($rc) {
|
|
return($rc,$text);
|
|
}
|
|
return(0,"FRU Updated");
|
|
}
|
|
|
|
sub fruwrite {
|
|
my $offset = shift;
|
|
my $bytes = shift;
|
|
my $chunk = shift;
|
|
my $length = @$bytes;
|
|
|
|
my $netfun = 0x28;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my @output;
|
|
my $code;
|
|
my @fru_data=();
|
|
|
|
for(my $c=$offset;$c < $length+$offset;$c += $chunk) {
|
|
my $ms = int($c / 0x100);
|
|
my $ls = $c - $ms * 0x100;
|
|
|
|
@cmd=(0x12,0x00,$ls,$ms,@$bytes[$c-$offset..$c-$offset+$chunk-1]);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
|
|
if($rc != 0) {
|
|
if ($code == 0x80) {
|
|
$text = "Write protected FRU";
|
|
}
|
|
if(!$text) {
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
|
|
my $count = $returnd[37-$authoffset];
|
|
if($count != $chunk) {
|
|
$rc = 1;
|
|
$text = "FRU write error (bytes requested: $chunk, wrote: $count)";
|
|
return($rc,$text);
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
sub decodealert {
|
|
my $trap = shift;
|
|
my $skip_sdrinit=0;
|
|
if ($trap =~ /xCAT_plugin::ipmi/) {
|
|
$trap=shift;
|
|
$skip_sdrinit=1;
|
|
}
|
|
my $node = shift;
|
|
my @pet = @_;
|
|
my $rc;
|
|
my $text;
|
|
|
|
if (!$skip_sdrinit) {
|
|
($rc,$text) = initsdr();
|
|
if($rc != 0) {
|
|
return($rc,$text);
|
|
}
|
|
}
|
|
|
|
my $type;
|
|
my $desc;
|
|
#my $ipmisensoreventtab = "$ENV{XCATROOT}/lib/GUMI/ipmisensorevent.tab";
|
|
#my $ipmigenericeventtab = "$ENV{XCATROOT}/lib/GUMI/ipmigenericevent.tab";
|
|
|
|
my $offsetmask = 0b00000000000000000000000000001111;
|
|
my $offsetrmask = 0b00000000000000000000000001110000;
|
|
my $assertionmask = 0b00000000000000000000000010000000;
|
|
my $eventtypemask = 0b00000000000000001111111100000000;
|
|
my $sensortypemask = 0b00000000111111110000000000000000;
|
|
my $reservedmask = 0b11111111000000000000000000000000;
|
|
|
|
my $offset = $trap & $offsetmask;
|
|
my $offsetr = $trap & $offsetrmask;
|
|
my $event_dir = $trap & $assertionmask;
|
|
my $event_type = ($trap & $eventtypemask) >> 8;
|
|
my $sensor_type = ($trap & $sensortypemask) >> 16;
|
|
my $reserved = ($trap & $reservedmask) >> 24;
|
|
|
|
if($debug >= 2) {
|
|
printf("offset: %02xh\n",$offset);
|
|
printf("offsetr: %02xh\n",$offsetr);
|
|
printf("assertion: %02xh\n",$event_dir);
|
|
printf("eventtype: %02xh\n",$event_type);
|
|
printf("sensortype: %02xh\n",$sensor_type);
|
|
printf("reserved: %02xh\n",$reserved);
|
|
}
|
|
|
|
my @hex = (0,@pet);
|
|
my $pad = $hex[0];
|
|
my @uuid = @hex[1..16];
|
|
my @seqnum = @hex[17,18];
|
|
my @timestamp = @hex[19,20,21,22];
|
|
my @utcoffset = @hex[23,24];
|
|
my $trap_source_type = $hex[25];
|
|
my $event_source_type = $hex[26];
|
|
my $sev = $hex[27];
|
|
my $sensor_device = $hex[28];
|
|
my $sensor_num = $hex[29];
|
|
my $entity_id = $hex[30];
|
|
my $entity_instance = $hex[31];
|
|
my $event_data_1 = $hex[32];
|
|
my $event_data_2 = $hex[33];
|
|
my $event_data_3 = $hex[34];
|
|
my @event_data = @hex[35..39];
|
|
my $langcode = $hex[40];
|
|
my $mfg_id = $hex[41] + $hex[42] * 0x100 + $hex[43] * 0x10000 + $hex[44] * 0x1000000;
|
|
my $prod_id = $hex[45] + $hex[46] * 0x100;
|
|
my @oem = $hex[47..@hex-1];
|
|
|
|
if($sev == 0x00) {
|
|
$sev = "LOG";
|
|
}
|
|
elsif($sev == 0x01) {
|
|
$sev = "MONITOR";
|
|
}
|
|
elsif($sev == 0x02) {
|
|
$sev = "INFORMATION";
|
|
}
|
|
elsif($sev == 0x04) {
|
|
$sev = "OK";
|
|
}
|
|
elsif($sev == 0x08) {
|
|
$sev = "WARNING";
|
|
}
|
|
elsif($sev == 0x10) {
|
|
$sev = "CRITICAL";
|
|
}
|
|
elsif($sev == 0x20) {
|
|
$sev = "NON-RECOVERABLE";
|
|
}
|
|
else {
|
|
$sev = "UNKNOWN-SEVERITY:$sev";
|
|
}
|
|
$text = "$sev:";
|
|
|
|
($rc,$type,$desc) = getsensorevent($sensor_type,$offset,"ipmisensorevents");
|
|
if($rc == 1) {
|
|
$type = "Unknown Type $sensor_type";
|
|
$desc = "Unknown Event $offset";
|
|
$rc = 0;
|
|
}
|
|
|
|
if($event_type <= 0x0c) {
|
|
my $gtype;
|
|
my $gdesc;
|
|
($rc,$gtype,$gdesc) = getsensorevent($event_type,$offset,"ipmigenericevents");
|
|
if($rc == 1) {
|
|
$gtype = "Unknown Type $gtype";
|
|
$gdesc = "Unknown Event $offset";
|
|
$rc = 0;
|
|
}
|
|
|
|
$desc = $gdesc;
|
|
}
|
|
|
|
if($type eq "" || $type eq "-") {
|
|
$type = "OEM Sensor Type $sensor_type"
|
|
}
|
|
if($desc eq "" || $desc eq "-") {
|
|
$desc = "OEM Sensor Event $offset"
|
|
}
|
|
|
|
if($type eq $desc) {
|
|
$desc = "";
|
|
}
|
|
|
|
my $extra_info = getaddsensorevent($sensor_type,$offset,$event_data_1,$event_data_2,$event_data_3);
|
|
if($extra_info) {
|
|
if($desc) {
|
|
$desc = "$desc $extra_info";
|
|
}
|
|
else {
|
|
$desc = "$extra_info";
|
|
}
|
|
}
|
|
|
|
$text = "$text $type,";
|
|
$text = "$text $desc";
|
|
|
|
my $key;
|
|
my $sensor_desc = sprintf("Sensor 0x%02x",$sensor_num);
|
|
foreach $key (keys %sdr_hash) {
|
|
my $sdr = $sdr_hash{$key};
|
|
if($sdr->sensor_number == $sensor_num) {
|
|
$sensor_desc = $sdr_hash{$key}->id_string;
|
|
if($sdr->rec_type == 0x01) {
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
|
|
$text = "$text ($sensor_desc)";
|
|
|
|
if($event_dir) {
|
|
$text = "$text - Recovered";
|
|
}
|
|
|
|
return(0,$text);
|
|
}
|
|
|
|
sub readauxentry {
|
|
my $netfn=0x2e<<2;
|
|
my $entrynum = shift;
|
|
my $entryls = ($entrynum&0xff);
|
|
my $entryms = ($entrynum>>8);
|
|
my @cmd = (0x93,0x4d,0x4f,0x00,$entryls,$entryms,0,0,0xff,0x5); #Get log size andup to 1275 bytes of data, keeping it under 1500 to accomodate mixed-mtu circumstances
|
|
my @data;
|
|
my $error = docmd(
|
|
$netfn,
|
|
\@cmd,
|
|
\@data
|
|
);
|
|
if ($error) { return $error; }
|
|
@data=splice @data,36-$authoffset;
|
|
if ($data[0]) { return $data[0]; }
|
|
my $text;
|
|
unless ($data[1] == 0x4d and $data[2] == 0x4f and $data[3] == 0) { return "Unrecognized response format" }
|
|
$entrynum=$data[6]+($data[7]<<8);
|
|
if (($data[10]&1) == 1) {
|
|
$text="POSSIBLY INCOMPLETE DATA FOLLOWS:\n";
|
|
}
|
|
my $addtext="";
|
|
if ($data[5] > 5) {
|
|
$addtext="\nTODO:SUPPORT MORE DATA THAT WAS SEEN HERE";
|
|
}
|
|
@data = splice @data,11;
|
|
pop @data;
|
|
while(scalar(@data)) {
|
|
my @subdata = splice @data,0,30;
|
|
my $numbytes = scalar(@subdata);
|
|
my $formatstring="%02x"x$numbytes;
|
|
$formatstring =~ s/%02x%02x/%02x%02x /g;
|
|
$text.=sprintf($formatstring."\n",@subdata);
|
|
}
|
|
$text.=$addtext;
|
|
return (0,$entrynum,$text);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
sub eventlog {
|
|
my $subcommand = shift;
|
|
|
|
my $netfun = 0x28;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my $code;
|
|
my @output;
|
|
my $num;
|
|
my $entry;
|
|
my $skiptail=0;
|
|
my @sel;
|
|
#my $ipmisensoreventtab = "$ENV{XCATROOT}/lib/GUMI/ipmisensorevent.tab";
|
|
#my $ipmigenericeventtab = "$ENV{XCATROOT}/lib/GUMI/ipmigenericevent.tab";
|
|
my $mfg_id;
|
|
my $prod_id;
|
|
my $device_id;
|
|
|
|
($rc,$text,$mfg_id,$prod_id,$device_id) = getdevid();
|
|
$rc=0;
|
|
unless (defined($subcommand)) {
|
|
$subcommand = 'all';
|
|
}
|
|
if($subcommand eq "all") {
|
|
$skiptail=1;
|
|
|
|
$num = 0x100 * 0x100;
|
|
}
|
|
elsif($subcommand eq "clear") {
|
|
}
|
|
elsif($subcommand =~ /^\d+$/) {
|
|
$num = $subcommand;
|
|
}
|
|
else {
|
|
return(1,"unsupported command eventlog $subcommand");
|
|
}
|
|
|
|
#Here we set tfactor based on the delta between the BMC reported time and our
|
|
#time. The IPMI spec says the BMC should return seconds since 1970 in local
|
|
#time, but the reality is the firmware pushing to the BMC has no context
|
|
#to know, so here we guess and adjust all timestamps based on delta between
|
|
#our now and the BMC's now
|
|
$error = docmd(
|
|
$netfun,
|
|
[0x48],
|
|
\@returnd
|
|
);
|
|
$tfactor = $returnd[40]<<24 | $returnd[39]<<16 | $returnd[38]<<8 | $returnd[37];
|
|
if ($tfactor > 0x20000000) {
|
|
$tfactor -= time();
|
|
} else {
|
|
$tfactor = 0;
|
|
}
|
|
|
|
@cmd=(0x40);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
}
|
|
elsif($code == 0x81) {
|
|
$rc = 1;
|
|
$text = "cannot execute command, SEL erase in progress";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
|
|
if($rc != 0) {
|
|
if(!$text) {
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
|
|
my $sel_version = $returnd[37-$authoffset];
|
|
if($sel_version != 0x51) {
|
|
$rc = 1;
|
|
$text = sprintf("SEL version 51h support only, version reported: %x",$sel_version);
|
|
return($rc,$text);
|
|
}
|
|
|
|
my $num_entries = $returnd[39-$authoffset]*256 + $returnd[38-$authoffset];
|
|
if($num_entries <= 0) {
|
|
$rc = 1;
|
|
$text = "no SEL entries";
|
|
return($rc,$text);
|
|
}
|
|
|
|
my $canres = $returnd[50-$authoffset] & 0b00000010;
|
|
if(!$canres) {
|
|
$rc = 1;
|
|
$text = "SEL reservation not supported";
|
|
return($rc,$text);
|
|
}
|
|
|
|
my $res_id_ls=0;
|
|
my $res_id_ms=0;
|
|
my %auxloginfo;
|
|
if ($subcommand =~ /clear/) { #Don't bother with a reservation unless a clear is involved
|
|
#atomic SEL retrieval need not require it, so an event during retrieval will not kill reventlog effort off
|
|
@cmd=(0x42);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
}
|
|
elsif($code == 0x81) {
|
|
$rc = 1;
|
|
$text = "cannot execute command, SEL erase in progress";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
|
|
if($rc != 0) {
|
|
if(!$text) {
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
|
|
$res_id_ls = $returnd[37-$authoffset];
|
|
$res_id_ms = $returnd[38-$authoffset];
|
|
} elsif ($mfg_id == 2) {
|
|
#For requests other than clear, we check for IBM extended auxillary log data
|
|
my @auxdata;
|
|
my $netfn = 0xa << 2;
|
|
my @auxlogcmd = (0x5a,1);
|
|
$error = docmd(
|
|
$netfn,
|
|
\@auxlogcmd,
|
|
\@auxdata);
|
|
@auxdata=splice @auxdata,36-$authoffset;
|
|
print Dumper(\@auxdata);
|
|
unless ($error or $auxdata[0] or $auxdata[5] != 0x4d or $auxdata[6] != 0x4f or $auxdata[7] !=0x0 ) { #Don't bother if support cannot be confirmed by service processor
|
|
$netfn=0x2e<<2; #switch netfunctions to read
|
|
my $numauxlogs = $auxdata[8]+($auxdata[9]<<8);
|
|
my $auxidx=1;
|
|
my $rc;
|
|
my $entry;
|
|
my $extdata;
|
|
while ($auxidx<=$numauxlogs) {
|
|
($rc,$entry,$extdata) = readauxentry($auxidx++);
|
|
unless ($rc) {
|
|
if ($auxloginfo{$entry}) {
|
|
$auxloginfo{$entry}.="!".$extdata;
|
|
} else {
|
|
$auxloginfo{$entry}=$extdata;
|
|
}
|
|
}
|
|
}
|
|
if ($auxloginfo{0}) {
|
|
if ($skiptail) {
|
|
foreach (split /!/,$auxloginfo{0}) {
|
|
sendoutput(0,":Unassociated auxillary data detected:");
|
|
foreach (split /\n/,$_) {
|
|
sendoutput(0,$_);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
print Dumper(\%auxloginfo);
|
|
}
|
|
}
|
|
|
|
|
|
if($subcommand eq "clear") {
|
|
@cmd=(0x47,$res_id_ls,$res_id_ms,0x43,0x4c,0x52,0xaa);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
|
|
if($rc != 0) {
|
|
if(!$text) {
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
|
|
my $erase_status = $returnd[37-$authoffset] & 0b00000001;
|
|
|
|
#skip test for now, need to get new res id for some machines
|
|
while($erase_status == 0 && 0) {
|
|
sleep(1);
|
|
@cmd=(0x47,$res_id_ls,$res_id_ms,0x43,0x4c,0x52,0x00);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
|
|
if($rc != 0) {
|
|
if(!$text) {
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
|
|
$erase_status = $returnd[37-$authoffset] & 0b00000001;
|
|
}
|
|
|
|
$text = "SEL cleared";
|
|
return($rc,$text);
|
|
}
|
|
|
|
($rc,$text) = initsdr();
|
|
if($rc != 0) {
|
|
return($rc,$text);
|
|
}
|
|
|
|
@cmd=(0x43,$res_id_ls,$res_id_ms,0x00,0x00,0x00,0xFF);
|
|
while(1) {
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
if ($skiptail) {
|
|
sendoutput($rc,$text);
|
|
return;
|
|
}
|
|
push(@output,$text);
|
|
return($rc,@output);
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
}
|
|
elsif($code == 0x81) {
|
|
$rc = 1;
|
|
$text = "cannot execute command, SEL erase in progress";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
}
|
|
|
|
if($rc != 0) {
|
|
if(!$text) {
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
if ($skiptail) {
|
|
sendoutput($rc,$text);
|
|
return;
|
|
}
|
|
push(@output,$text);
|
|
return($rc,@output);
|
|
}
|
|
|
|
my $next_rec_ls = $returnd[37-$authoffset];
|
|
my $next_rec_ms = $returnd[38-$authoffset];
|
|
my @sel_data = @returnd[39-$authoffset..39-$authoffset+16];
|
|
@cmd=(0x43,$res_id_ls,$res_id_ms,$next_rec_ls,$next_rec_ms,0x00,0xFF);
|
|
|
|
$entry++;
|
|
if ($debug) {
|
|
print "$entry: ";
|
|
hexdump(\@sel_data);
|
|
}
|
|
|
|
my $record_id = $sel_data[0] + $sel_data[1]*256;
|
|
my $record_type = $sel_data[2];
|
|
|
|
if($record_type == 0x02) {
|
|
}
|
|
else {
|
|
$text=getoemevent($record_type,$mfg_id,\@sel_data);
|
|
if ($auxloginfo{$entry}) {
|
|
$text.=" With additional data:\n".$auxloginfo{$entry};
|
|
}
|
|
if ($skiptail) {
|
|
sendoutput($rc,$text);
|
|
} else {
|
|
push(@output,$text);
|
|
}
|
|
if($next_rec_ms == 0xFF && $next_rec_ls == 0xFF) {
|
|
last;
|
|
}
|
|
next;
|
|
}
|
|
|
|
my $timestamp = ($sel_data[3] | $sel_data[4]<<8 | $sel_data[5]<<16 | $sel_data[6]<<24);
|
|
unless ($timestamp < 0x20000000) { #IPMI Spec says below this is effectively BMC uptime, not correctable
|
|
$timestamp -= $tfactor; #apply correction factor based on how off the current BMC clock is from management server
|
|
}
|
|
my ($seldate,$seltime) = timestamp2datetime($timestamp);
|
|
# $text = "$entry: $seldate $seltime";
|
|
$text = ":$seldate $seltime";
|
|
|
|
# my $gen_id_slave_addr = ($sel_data[7] & 0b11111110) >> 1;
|
|
# my $gen_id_slave_addr_hs = ($sel_data[7] & 0b00000001);
|
|
# my $gen_id_ch_num = ($sel_data[8] & 0b11110000) >> 4;
|
|
# my $gen_id_ipmb = ($sel_data[8] & 0b00000011);
|
|
|
|
my $sensor_owner_id = $sel_data[7];
|
|
my $sensor_owner_lun = $sel_data[8];
|
|
|
|
my $sensor_type = $sel_data[10];
|
|
my $sensor_num = $sel_data[11];
|
|
my $event_dir = $sel_data[12] & 0b10000000;
|
|
my $event_type = $sel_data[12] & 0b01111111;
|
|
my $offset = $sel_data[13] & 0b00001111;
|
|
my $event_data_1 = $sel_data[13];
|
|
my $event_data_2 = $sel_data[14];
|
|
my $event_data_3 = $sel_data[15];
|
|
my $sev = 0;
|
|
$sev = ($sel_data[14] & 0b11110000) >> 4;
|
|
# if($event_type != 1) {
|
|
# $sev = ($sel_data[14] & 0b11110000) >> 4;
|
|
# }
|
|
# $text = "$text $sev:";
|
|
|
|
my $type;
|
|
my $desc;
|
|
($rc,$type,$desc) = getsensorevent($sensor_type,$offset,"ipmisensorevents");
|
|
if($rc == 1) {
|
|
$type = "Unknown Type $sensor_type";
|
|
$desc = "Unknown Event $offset";
|
|
$rc = 0;
|
|
}
|
|
|
|
if($event_type <= 0x0c) {
|
|
my $gtype;
|
|
my $gdesc;
|
|
($rc,$gtype,$gdesc) = getsensorevent($event_type,$offset,"ipmigenericevents");
|
|
if($rc == 1) {
|
|
$gtype = "Unknown Type $gtype";
|
|
$gdesc = "Unknown Event $offset";
|
|
$rc = 0;
|
|
}
|
|
|
|
$desc = $gdesc;
|
|
}
|
|
|
|
if($type eq "" || $type eq "-") {
|
|
$type = "OEM Sensor Type $sensor_type"
|
|
}
|
|
if($desc eq "" || $desc eq "-") {
|
|
$desc = "OEM Sensor Event $offset"
|
|
}
|
|
|
|
if($type eq $desc) {
|
|
$desc = "";
|
|
}
|
|
|
|
my $extra_info = getaddsensorevent($sensor_type,$offset,$event_data_1,$event_data_2,$event_data_3);
|
|
if($extra_info) {
|
|
if($desc) {
|
|
$desc = "$desc $extra_info";
|
|
}
|
|
else {
|
|
$desc = "$extra_info";
|
|
}
|
|
}
|
|
|
|
$text = "$text $type,";
|
|
$text = "$text $desc";
|
|
|
|
# my $key;
|
|
my $key = $sensor_owner_id . "." . $sensor_owner_lun . "." . $sensor_num;
|
|
my $sensor_desc = sprintf("Sensor 0x%02x",$sensor_num);
|
|
# foreach $key (keys %sdr_hash) {
|
|
# my $sdr = $sdr_hash{$key};
|
|
# if($sdr->sensor_number == $sensor_num) {
|
|
# $sensor_desc = $sdr_hash{$key}->id_string;
|
|
# last;
|
|
# }
|
|
# }
|
|
if(defined $sdr_hash{$key}) {
|
|
$sensor_desc = $sdr_hash{$key}->id_string;
|
|
if ($sdr_hash{$key}->event_type_code == 1) {
|
|
if (($event_data_1 & 0b11000000) == 0b01000000) {
|
|
$sensor_desc .= " reading ".translate_sensor($event_data_2,$sdr_hash{$key});
|
|
if (($event_data_1 & 0b00110000) == 0b00010000) {
|
|
$sensor_desc .= " with threshold " . translate_sensor($event_data_3,$sdr_hash{$key});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$text = "$text ($sensor_desc)";
|
|
|
|
if($event_dir) {
|
|
$text = "$text - Recovered";
|
|
}
|
|
|
|
if ($auxloginfo{$entry}) {
|
|
$text.=" with additional data:";
|
|
if ($skiptail) {
|
|
sendoutput($rc,$text);
|
|
foreach (split /\n/,$auxloginfo{$entry}) {
|
|
sendoutput(0,$_);
|
|
}
|
|
} else {
|
|
push(@output,$text);
|
|
push @output,split /\n/,$auxloginfo{$entry};
|
|
}
|
|
|
|
} else {
|
|
if ($skiptail) {
|
|
sendoutput($rc,$text);
|
|
} else {
|
|
push(@output,$text);
|
|
}
|
|
}
|
|
|
|
if($next_rec_ms == 0xFF && $next_rec_ls == 0xFF) {
|
|
last;
|
|
}
|
|
}
|
|
|
|
my @routput = reverse(@output);
|
|
my @noutput;
|
|
my $c;
|
|
foreach(@routput) {
|
|
$c++;
|
|
if($c > $num) {
|
|
last;
|
|
}
|
|
push(@noutput,$_);
|
|
}
|
|
@output = reverse(@noutput);
|
|
|
|
return($rc,@output);
|
|
}
|
|
sub getoemevent {
|
|
my $record_type = shift;
|
|
my $mfg_id = shift;
|
|
my $sel_data = shift;
|
|
my $text=":";
|
|
if ($record_type < 0xE0 && $record_type > 0x2F) { #Should be timestampped, whatever it is
|
|
my $timestamp = (@$sel_data[3] | @$sel_data[4]<<8 | @$sel_data[5]<<16 | @$sel_data[6]<<24);
|
|
unless ($timestamp < 0x20000000) {
|
|
$timestamp -= $tfactor;
|
|
}
|
|
my ($seldate,$seltime) = timestamp2datetime($timestamp);
|
|
my @rest = @$sel_data[7..15];
|
|
if ($mfg_id==2) {
|
|
$text.="$seldate $seltime IBM OEM Event-";
|
|
if ($rest[3]==0 && $rest[4]==0 && $rest[7]==0) {
|
|
$text=$text."PCI Event/Error, details in next event"
|
|
} elsif ($rest[3]==1 && $rest[4]==0 && $rest[7]==0) {
|
|
$text=$text."Processor Event/Error occurred, details in next event"
|
|
} elsif ($rest[3]==2 && $rest[4]==0 && $rest[7]==0) {
|
|
$text=$text."Memory Event/Error occurred, details in next event"
|
|
} elsif ($rest[3]==3 && $rest[4]==0 && $rest[7]==0) {
|
|
$text=$text."Scalability Event/Error occurred, details in next event"
|
|
} elsif ($rest[3]==4 && $rest[4]==0 && $rest[7]==0) {
|
|
$text=$text."PCI bus Event/Error occurred, details in next event"
|
|
} elsif ($rest[3]==5 && $rest[4]==0 && $rest[7]==0) {
|
|
$text=$text."Chipset Event/Error occurred, details in next event"
|
|
} elsif ($rest[3]==6 && $rest[4]==1 && $rest[7]==0) {
|
|
$text=$text."BIOS/BMC Power Executive mismatch (BIOS $rest[5], BMC $rest[6])"
|
|
} elsif ($rest[3]==6 && $rest[4]==2 && $rest[7]==0) {
|
|
$text=$text."Boot denied due to power limitations"
|
|
} else {
|
|
$text=$text."Unknown event ". phex(\@rest);
|
|
}
|
|
} else {
|
|
$text .= "$seldate $seltime " . sprintf("Unknown OEM SEL Type %02x:",$record_type) . phex(\@rest);
|
|
}
|
|
} else { #Non-timestamped
|
|
my %memerrors = (
|
|
0x00 => "DIMM enabled",
|
|
0x01 => "DIMM disabled, failed ECC test",
|
|
0x02 => "POST/BIOS memory test failed, DIMM disabled",
|
|
0x03 => "DIMM disabled, non-supported memory device",
|
|
0x04 => "DIMM disabled, non-matching or missing DIMM(s)",
|
|
);
|
|
my %pcierrors = (
|
|
0x00 => "Device OK",
|
|
0x01 => "Required ROM space not available",
|
|
0x02 => "Required I/O Space not available",
|
|
0x03 => "Required memory not available",
|
|
0x04 => "Required memory below 1MB not available",
|
|
0x05 => "ROM checksum failed",
|
|
0x06 => "BIST failed",
|
|
0x07 => "Planar device missing or disabled by user",
|
|
0x08 => "PCI device has an invalid PCI configuration space header",
|
|
0x09 => "FRU information for added PCI device",
|
|
0x0a => "FRU information for removed PCI device",
|
|
0x0b => "A PCI device was added, PCI FRU information is stored in next log entry",
|
|
0x0c => "A PCI device was removed, PCI FRU information is stored in next log entry",
|
|
0x0d => "Requested resources not available",
|
|
0x0e => "Required I/O Space Not Available",
|
|
0x0f => "Required I/O Space Not Available",
|
|
0x10 => "Required I/O Space Not Available",
|
|
0x11 => "Required I/O Space Not Available",
|
|
0x12 => "Required I/O Space Not Available",
|
|
0x13 => "Planar video disabled due to add in video card",
|
|
0x14 => "FRU information for PCI device partially disabled ",
|
|
0x15 => "A PCI device was partially disabled, PCI FRU information is stored in next log entry",
|
|
0x16 => "A 33Mhz device is installed on a 66Mhz bus, PCI device information is stored in next log entry",
|
|
0x17 => "FRU information, 33Mhz device installed on 66Mhz bus",
|
|
0x18 => "Merge cable missing",
|
|
0x19 => "Node 1 to Node 2 cable missing",
|
|
0x1a => "Node 1 to Node 3 cable missing",
|
|
0x1b => "Node 2 to Node 3 cable missing",
|
|
0x1c => "Nodes could not merge",
|
|
0x1d => "No 8 way SMP cable",
|
|
0x1e => "Primary North Bridge to PCI Host Bridge IB Link has failed",
|
|
0x1f => "Redundant PCI Host Bridge IB Link has failed",
|
|
);
|
|
my %procerrors = (
|
|
0x00 => "Processor has failed BIST",
|
|
0x01 => "Unable to apply processor microcode update",
|
|
0x02 => "POST does not support current stepping level of processor",
|
|
0x03 => "CPU mismatch detected",
|
|
);
|
|
my @rest = @$sel_data[3..15];
|
|
if ($record_type == 0xE0 && $rest[0]==2 && $mfg_id==2 && $rest[1]==0 && $rest[12]==1) { #Rev 1 POST memory event
|
|
$text="IBM Memory POST Event-";
|
|
my $msuffix=sprintf(", chassis %d, card %d, dimm %d",$rest[3],$rest[4],$rest[5]);
|
|
#the next bit is a basic lookup table, should implement as a table ala ibmleds.tab, or a hash... yeah, a hash...
|
|
$text=$text.$memerrors{$rest[2]}.$msuffix;
|
|
} elsif ($record_type == 0xE0 && $rest[0]==1 && $mfg_id==2 && $rest[12]==0) { #A processor error or event, rev 0 only known in the spec I looked at
|
|
$text=$text.$procerrors{$rest[1]};
|
|
} elsif ($record_type == 0xE0 && $rest[0]==0 && $mfg_id==2) { #A PCI error or event, rev 1 or 2, the revs differe in endianness
|
|
my $msuffix;
|
|
if ($rest[12]==0) {
|
|
$msuffix=sprintf("chassis %d, slot %d, bus %s, device %02x%02x:%02x%02x",$rest[2],$rest[3],$rest[4],$rest[5],$rest[6],$rest[7],$rest[8]);
|
|
} elsif ($rest[12]==1) {
|
|
$msuffix=sprintf("chassis %d, slot %d, bus %s, device %02x%02x:%02x%02x",$rest[2],$rest[3],$rest[4],$rest[5],$rest[6],$rest[7],$rest[8]);
|
|
} else {
|
|
return ("Unknown IBM PCI event/error format");
|
|
}
|
|
$text=$text.$pcierrors{$rest[1]}.$msuffix;
|
|
} else {
|
|
#Some event we can't define that is OEM or some otherwise unknown event
|
|
$text = sprintf("SEL Type %02x:",$record_type) . phex(\@rest);
|
|
}
|
|
} #End timestampped intepretation
|
|
return ($text);
|
|
}
|
|
|
|
sub getsensorevent
|
|
{
|
|
my $sensortype = sprintf("%02Xh",shift);
|
|
my $sensoroffset = sprintf("%02Xh",shift);
|
|
my $file = shift;
|
|
|
|
my @line;
|
|
my $type;
|
|
my $code;
|
|
my $desc;
|
|
my $offset;
|
|
my $rc = 1;
|
|
|
|
if ($file eq "ipmigenericevents") {
|
|
if ($xCAT::data::ipmigenericevents::ipmigenericevents{"$sensortype,$sensoroffset"}) {
|
|
($type,$desc) = split (/,/,$xCAT::data::ipmigenericevents::ipmigenericevents{"$sensortype,$sensoroffset"},2);
|
|
return(0,$type,$desc);
|
|
}
|
|
if ($xCAT::data::ipmigenericevents::ipmigenericevents{"$sensortype,-"}) {
|
|
($type,$desc) = split (/,/,$xCAT::data::ipmigenericevents::ipmigenericevents{"$sensortype,-"},2);
|
|
return(0,$type,$desc);
|
|
}
|
|
}
|
|
if ($file eq "ipmisensorevents") {
|
|
if ($xCAT::data::ipmisensorevents::ipmisensorevents{"$sensortype,$sensoroffset"}) {
|
|
($type,$desc) = split (/,/,$xCAT::data::ipmisensorevents::ipmisensorevents{"$sensortype,$sensoroffset"},2);
|
|
return(0,$type,$desc);
|
|
}
|
|
if ($xCAT::data::ipmisensorevents::ipmisensorevents{"$sensortype,-"}) {
|
|
($type,$desc) = split (/,/,$xCAT::data::ipmisensorevents::ipmisensorevents{"$sensortype,-"},2);
|
|
return(0,$type,$desc);
|
|
}
|
|
}
|
|
return (0,"No Mappings found ($sensortype)","No Mappings found ($sensoroffset)");
|
|
}
|
|
|
|
sub getaddsensorevent {
|
|
my $sensor_type = shift;
|
|
my $offset = shift;
|
|
my $event_data_1 = shift;
|
|
my $event_data_2 = shift;
|
|
my $event_data_3 = shift;
|
|
my $text = "";
|
|
|
|
if ($sensor_type == 0x08 && $offset == 6) {
|
|
my %extra = (
|
|
0x0 => "Vendor mismatch",
|
|
0x1 => "Revision mismatch",
|
|
0x2 => "Processor missing",
|
|
);
|
|
if ($extra{$event_data_3}) {
|
|
$text = $extra{$event_data_3};
|
|
}
|
|
}
|
|
if ($sensor_type == 0x0C) {
|
|
$text = sprintf ("Memory module %d",$event_data_3);
|
|
}
|
|
|
|
if($sensor_type == 0x0f) {
|
|
if($offset == 0x00) {
|
|
my %extra = (
|
|
0x00 => "Unspecified",
|
|
0x01 => "No system memory installed",
|
|
0x02 => "No usable system memory",
|
|
0x03 => "Unrecoverable hard disk failure",
|
|
0x04 => "Unrecoverable system board failure",
|
|
0x05 => "Unrecoverable diskette failure",
|
|
0x06 => "Unrecoverable hard disk controller failure",
|
|
0x07 => "Unrecoverable keyboard failure",
|
|
0x08 => "Removable boot media not found",
|
|
0x09 => "Unrecoverable video controller failure",
|
|
0x0a => "No video device detected",
|
|
0x0b => "Firmware (BIOS) ROM corruption detected",
|
|
0x0c => "CPU voltage mismatch",
|
|
0x0d => "CPU speed matching failure",
|
|
);
|
|
$text = $extra{$event_data_2};
|
|
}
|
|
if($offset == 0x02) {
|
|
my %extra = (
|
|
0x00 => "Unspecified",
|
|
0x01 => "Memory initialization",
|
|
0x02 => "Hard-disk initialization",
|
|
0x03 => "Secondary processor(s) initialization",
|
|
0x04 => "User authentication",
|
|
0x05 => "User-initiated system setup",
|
|
0x06 => "USB resource configuration",
|
|
0x07 => "PCI resource configuration",
|
|
0x08 => "Option ROM initialization",
|
|
0x09 => "Video initialization",
|
|
0x0a => "Cache initialization",
|
|
0x0b => "SM Bus initialization",
|
|
0x0c => "Keyboard controller initialization",
|
|
0x0d => "Embedded controller/management controller initialization",
|
|
0x0e => "Docking station attachement",
|
|
0x0f => "Enabling docking station",
|
|
0x10 => "Docking staion ejection",
|
|
0x11 => "Disable docking station",
|
|
0x12 => "Calling operation system wake-up vector",
|
|
0x13 => "Starting operation system boot process, call init 19h",
|
|
0x14 => "Baseboard or motherboard initialization",
|
|
0x16 => "Floppy initialization",
|
|
0x17 => "Keyboard test",
|
|
0x18 => "Pointing device test",
|
|
0x19 => "Primary processor initialization",
|
|
);
|
|
$text = $extra{$event_data_2};
|
|
}
|
|
}
|
|
if ($sensor_type == 0x10) {
|
|
if ($offset == 0x0) {
|
|
$text = sprintf("Memory module %d",$event_data_2);
|
|
} elsif ($offset == 0x01) {
|
|
$text = "Disabled for ";
|
|
unless ($event_data_3 & 0x20) {
|
|
if ($event_data_3 & 0x10) {
|
|
$text .= "assertions of";
|
|
} else {
|
|
$text .= "deassertions of";
|
|
}
|
|
}
|
|
$text .= sprintf ("type %02xh/offset %02xh",$event_data_2,$event_data_3&0x0F);
|
|
} elsif ($offset == 0x05) {
|
|
$text = "$event_data_3% full";
|
|
}
|
|
}
|
|
|
|
if($sensor_type == 0x12) {
|
|
if($offset == 0x03) {
|
|
}
|
|
if($offset == 0x04) {
|
|
if($event_data_2 & 0b00100000) {
|
|
$text = "$text, NMI";
|
|
}
|
|
if($event_data_2 & 0b00010000) {
|
|
$text = "$text, OEM action";
|
|
}
|
|
if($event_data_2 & 0b00001000) {
|
|
$text = "$text, power cycle";
|
|
}
|
|
if($event_data_2 & 0b00000100) {
|
|
$text = "$text, reset";
|
|
}
|
|
if($event_data_2 & 0b00000010) {
|
|
$text = "$text, power off";
|
|
}
|
|
if($event_data_2 & 0b00000001) {
|
|
$text = "$text, Alert";
|
|
}
|
|
$text =~ s/^, //;
|
|
}
|
|
}
|
|
if ($sensor_type == 0x1d && $offset == 0x07) {
|
|
my %causes = (
|
|
0 => "Unknown",
|
|
1 => "Chassis reset via User command to BMC",
|
|
2 => "Reset button",
|
|
3 => "Power button",
|
|
4 => "Watchdog action",
|
|
5 => "OEM",
|
|
6 => "AC Power apply force on",
|
|
7 => "Restore previous power state on AC",
|
|
8 => "PEF initiated reset",
|
|
9 => "PEF initiated power cycle",
|
|
10 => "Soft reboot",
|
|
11 => "RTC Wake",
|
|
);
|
|
if ($causes{$event_data_2 & 0xf}) {
|
|
$text = $causes{$event_data_2};
|
|
} else {
|
|
$text = "Unrecognized cause ".$event_data_2 & 0xf;
|
|
}
|
|
$text .= "via channel $event_data_3";
|
|
}
|
|
if ($sensor_type == 0x21) {
|
|
my %extra = (
|
|
0 => "PCI slot",
|
|
1 => "Drive array",
|
|
2 => "External connector",
|
|
3 => "Docking port",
|
|
4 => "Other slot",
|
|
5 => "Sensor ID",
|
|
6 => "AdvncedTCA",
|
|
7 => "Memory slot",
|
|
8 => "FAN",
|
|
9 => "PCIe",
|
|
10 => "SCSI",
|
|
11 => "SATA/SAS",
|
|
);
|
|
|
|
$text=$extra{$event_data_2 & 127};
|
|
unless ($text) {
|
|
$text = "Unknown slot/conn type ".$event_data_2&127;
|
|
}
|
|
$text .= " $event_data_3";
|
|
}
|
|
if ($sensor_type == 0x23) {
|
|
my %extra = (
|
|
0x10 => "SMI",
|
|
0x20 => "NMI",
|
|
0x30 => "Messaging Interrupt",
|
|
0xF0 => "Unspecified",
|
|
0x01 => "BIOS FRB2",
|
|
0x02 => "BIOS/POST",
|
|
0x03 => "OS Load",
|
|
0x04 => "SMS/OS",
|
|
0x05 => "OEM",
|
|
0x0F => "Unspecified"
|
|
);
|
|
if ($extra{$event_data_2 & 0xF0}) {
|
|
$text = $extra{$event_data_2 & 0xF0};
|
|
}
|
|
if ($extra{$event_data_2 & 0x0F}) {
|
|
$text .= ", ".$extra{$event_data_2 & 0x0F};
|
|
}
|
|
$text =~ s/^, //;
|
|
}
|
|
if ($sensor_type == 0x28) {
|
|
if ($offset == 0x4) {
|
|
$text = "Sensor $event_data_2";
|
|
} elsif ($offset == 0x5) {
|
|
$text = "";
|
|
my $logicalfru=0;
|
|
if ($event_data_2 & 128) {
|
|
$logicalfru=1;
|
|
}
|
|
my $intelligent=1;
|
|
if ($event_data_2 & 24) {
|
|
$text .= "LUN ".($event_data_2&24)>>3;
|
|
} else {
|
|
$intelligent=0;
|
|
}
|
|
if ($event_data_2 & 7) {
|
|
$text .= "Bus ID ".($event_data_2&7);
|
|
}
|
|
if ($logicalfru) {
|
|
$text .= "FRU ID ".$event_data_3;
|
|
} elsif (not $intelligent) {
|
|
$text .= "I2C addr ".$event_data_3>>1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($sensor_type == 0x2a) {
|
|
$text = sprintf("Channel %d, User %d",$event_data_3&0x0f,$event_data_2&0x3f);
|
|
if ($offset == 1) {
|
|
if (($event_data_3 & 207) == 1) {
|
|
$text .= " at user request";
|
|
} elsif (($event_data_3 & 207) == 2) {
|
|
$text .= " timed out";
|
|
} elsif (($event_data_3 & 207) == 3) {
|
|
$text .= " configuration change";
|
|
}
|
|
}
|
|
}
|
|
if ($sensor_type == 0x2b) {
|
|
my %extra = (
|
|
0x0 => "Unspecified",
|
|
0x1 => "BMC device ID",
|
|
0x2 => "BMC Firmware",
|
|
0x3 => "BMC Hardware",
|
|
0x4 => "BMC manufacturer",
|
|
0x5 => "IPMI Version",
|
|
0x6 => "BMC aux firmware ID",
|
|
0x7 => "BMC boot block",
|
|
0x8 => "Other BMC Firmware",
|
|
0x09 => "BIOS/EFI change",
|
|
0x0a => "SMBIOS change",
|
|
0x0b => "OS change",
|
|
0x0c => "OS Loader change",
|
|
0x0d => "Diagnostics change",
|
|
0x0e => "Management agent change",
|
|
0x0f => "Management software change",
|
|
0x10 => "Management middleware change",
|
|
0x11 => "FPGA/CPLD/PSoC change",
|
|
0x12 => "FRU change",
|
|
0x13 => "device addition/removal",
|
|
0x14 => "Equivalent replacement",
|
|
0x15 => "Newer replacement",
|
|
0x16 => "Older replacement",
|
|
0x17 => "DIP/Jumper change",
|
|
);
|
|
if ($extra{$event_data_2}) {
|
|
$text = $extra{$event_data_2};
|
|
} else {
|
|
$text = "Unknown version change type $event_data_2";
|
|
}
|
|
}
|
|
if ($sensor_type == 0x2c) {
|
|
my %extra = (
|
|
0 => "",
|
|
1 => "Software dictated",
|
|
2 => "Latch operated",
|
|
3 => "Hotswap buton pressed",
|
|
4 => "automatic operation",
|
|
5 => "Communication lost",
|
|
6 => "Communication lost locally",
|
|
7 => "Unexpected removal",
|
|
8 => "Operator intervention",
|
|
9 => "Unknwon IPMB address",
|
|
10 => "Unexpected deactivation",
|
|
0xf => "unknown",
|
|
);
|
|
if ($extra{$event_data_2>>4}) {
|
|
$text = $extra{$event_data_2>>4};
|
|
} else {
|
|
$text = "Unrecognized cause ".$event_data_2>>4;
|
|
}
|
|
my $prev_state=$event_data_2 & 0xf;
|
|
unless ($prev_state == $offset) {
|
|
my %oldstates = (
|
|
0 => "Not Installed",
|
|
1 => "Inactive",
|
|
2 => "Activation requested",
|
|
3 => "Activating",
|
|
4 => "Active",
|
|
5 => "Deactivation requested",
|
|
6 => "Deactivating",
|
|
7 => "Communication lost",
|
|
);
|
|
if ($oldstates{$prev_state}) {
|
|
$text .= "(was ".$oldstates{$prev_state}.")";
|
|
} else {
|
|
$text .= "(was in unrecognized state $prev_state)";
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return($text);
|
|
}
|
|
|
|
sub checkleds {
|
|
my $netfun = 0xe8; #really 0x3a
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $led_id_ms;
|
|
my $led_id_ls;
|
|
my $rc = 0;
|
|
my @output =();
|
|
my $text="";
|
|
my $key;
|
|
my $mfg_id;
|
|
my $prod_id;
|
|
($rc,$text,$mfg_id,$prod_id) = getdevid();
|
|
if ($mfg_id != 2) {
|
|
return (0,"LED status not supported on this system");
|
|
}
|
|
|
|
($rc,$text) = initsdr();
|
|
if($rc != 0) {
|
|
return($rc,$text);
|
|
}
|
|
foreach $key (sort {$sdr_hash{$a}->id_string cmp $sdr_hash{$b}->id_string} keys %sdr_hash) {
|
|
my $sdr = $sdr_hash{$key};
|
|
if($sdr->rec_type == 0xC0 && $sdr->sensor_type == 0xED) {
|
|
#this stuff is to help me build the file from spec paste
|
|
#my $tehstr=sprintf("grep 0x%04X /opt/xcat/lib/x3755led.tab",$sdr->led_id);
|
|
#my $tehstr=`$tehstr`;
|
|
#$tehstr =~ s/^0x....//;
|
|
|
|
#printf("%X.%X.0x%04x",$mfg_id,$prod_id,$sdr->led_id);
|
|
#print $tehstr;
|
|
|
|
#We are inconsistant in our spec, first try a best guess
|
|
#at endianness, assume the smaller value is MSB
|
|
if (($sdr->led_id&0xff) > ($sdr->led_id>>8)) {
|
|
$led_id_ls=$sdr->led_id&0xff;
|
|
$led_id_ms=$sdr->led_id>>8;
|
|
} else {
|
|
$led_id_ls=$sdr->led_id>>8;
|
|
$led_id_ms=$sdr->led_id&0xff;
|
|
}
|
|
|
|
@cmd=(0xc0,$led_id_ms,$led_id_ls);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
if ($returnd[36-$authoffset] == 0xc9) {
|
|
my $tmp;
|
|
#we probably guessed endianness wrong.
|
|
$tmp=$led_id_ls;
|
|
$led_id_ls=$led_id_ms;
|
|
$led_id_ms=$tmp;
|
|
@cmd=(0xc0,$led_id_ms,$led_id_ls);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
}
|
|
|
|
if ($returnd[38-$authoffset]) { # != 0) {
|
|
#It's on...
|
|
if ($returnd[42-$authoffset] == 4) {
|
|
push(@output,sprintf("BIOS or admininstrator has %s lit",getsensorname($mfg_id,$prod_id,$sdr->led_id,"ibmleds")));
|
|
}
|
|
elsif ($returnd[42-$authoffset] == 3) {
|
|
push(@output,sprintf("A user has manually requested LED 0x%04x (%s) be active",$sdr->led_id,getsensorname($mfg_id,$prod_id,$sdr->led_id,"ibmleds")));
|
|
}
|
|
elsif ($returnd[42-$authoffset] == 1 && $sdr->led_id !=0) {
|
|
push(@output,sprintf("LED 0x%02x%02x (%s) active to indicate LED 0x%02x%02x (%s) is active",$led_id_ms,$led_id_ls,getsensorname($mfg_id,$prod_id,$sdr->led_id,"ibmleds"),$returnd[40-$authoffset],$returnd[41-$authoffset],getsensorname($mfg_id,$prod_id,($returnd[40-$authoffset]<<8)+$returnd[41-$authoffset],"ibmleds")));
|
|
}
|
|
elsif ($sdr->led_id ==0) {
|
|
push(@output,sprintf("LED 0x0000 (%s) active to indicate system error condition.",getsensorname($mfg_id,$prod_id,$sdr->led_id,"ibmleds")));
|
|
}
|
|
elsif ($returnd[42-$authoffset] == 2) {
|
|
my $sensor_desc;
|
|
#Ok, LED is tied to a sensor..
|
|
my $sensor_num=$returnd[41-$authoffset];
|
|
foreach $key (keys %sdr_hash) {
|
|
my $osdr = $sdr_hash{$key};
|
|
if($osdr->sensor_number == $sensor_num) {
|
|
$sensor_desc = $sdr_hash{$key}->id_string;
|
|
if($osdr->rec_type == 0x01) {
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
$rc=0;
|
|
#push(@output,sprintf("Sensor 0x%02x (%s) has activated LED 0x%04x",$sensor_num,$sensor_desc,$sdr->led_id));
|
|
push(@output,sprintf("LED 0x%02x%02x active to indicate Sensor 0x%02x (%s) error.",$led_id_ms,$led_id_ls,$sensor_num,$sensor_desc));
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
if ($#output==-1) {
|
|
push(@output,"No active error LEDs detected");
|
|
}
|
|
return($rc,@output);
|
|
}
|
|
|
|
sub vitals {
|
|
my $subcommand = shift;
|
|
|
|
my $rc = 0;
|
|
my $text;
|
|
my $key;
|
|
my @sensor_filters=(0x00);
|
|
my @output;
|
|
my $reading;
|
|
my $unitdesc;
|
|
my $value;
|
|
my $extext;
|
|
my $format = "%-30s%8s %-20s";
|
|
my $per = " ";
|
|
my $doall;
|
|
$doall=0;
|
|
$rc=0;
|
|
|
|
if($subcommand eq "all") {
|
|
@sensor_filters=(0x01); #,0x02,0x03,0x04);
|
|
$doall=1;
|
|
}
|
|
elsif($subcommand =~ /temp/) {
|
|
@sensor_filters=(0x01);
|
|
}
|
|
elsif($subcommand eq "voltage") {
|
|
@sensor_filters=(0x02);
|
|
}
|
|
elsif($subcommand =~ /watt/) {
|
|
@sensor_filters=(0x03);
|
|
}
|
|
elsif($subcommand eq "fanspeed") {
|
|
@sensor_filters=(0x04);
|
|
}
|
|
elsif($subcommand eq "power") {
|
|
($rc,$text) = power("stat");
|
|
$text = sprintf($format,"Power Status:",$text);
|
|
return($rc,$text);
|
|
}
|
|
elsif($subcommand eq "leds") {
|
|
my @cleds;
|
|
($rc,@cleds) = checkleds();
|
|
foreach $text (@cleds) {
|
|
push(@output,$text);
|
|
}
|
|
}
|
|
else {
|
|
return(1,"unsupported command vitals $subcommand");
|
|
}
|
|
|
|
($rc,$text) = initsdr();
|
|
if($rc != 0) {
|
|
return($rc,$text);
|
|
}
|
|
|
|
foreach(@sensor_filters) {
|
|
my $filter = $_;
|
|
|
|
foreach $key (sort {$sdr_hash{$a}->id_string cmp $sdr_hash{$b}->id_string} keys %sdr_hash) {
|
|
my $sdr = $sdr_hash{$key};
|
|
if(($doall and not $sdr->rec_type == 0x11 and not $sdr->sensor_type==0xed) or ($sdr->rec_type == 0x01 and $sdr->sensor_type == $filter)) {
|
|
my $lformat = $format;
|
|
|
|
($rc,$reading,$extext) = readsensor($sdr);
|
|
$unitdesc = "";
|
|
if($rc == 0) {
|
|
$unitdesc = $units{$sdr->sensor_units_2};
|
|
|
|
$value = $reading;
|
|
if ($sdr->rec_type==1) {
|
|
$value = (($sdr->M * $reading) + ($sdr->B * (10**$sdr->B_exp))) * (10**$sdr->R_exp);
|
|
}
|
|
if($sdr->rec_type != 1 or $sdr->linearization == 0) {
|
|
$reading = $value;
|
|
if($value == int($value)) {
|
|
$lformat = "%-30s%8d%s";
|
|
}
|
|
else {
|
|
$lformat = "%-30s%8.3f%s";
|
|
}
|
|
}
|
|
elsif($sdr->linearization == 7) {
|
|
if($value > 0) {
|
|
$reading = 1/$value;
|
|
}
|
|
else {
|
|
$reading = 0;
|
|
}
|
|
$lformat = "%-30s%8d %s";
|
|
}
|
|
else {
|
|
$reading = "RAW(".$sdr->linearization.") $reading";
|
|
}
|
|
|
|
if($sdr->sensor_units_1 & 1) {
|
|
$per = "% ";
|
|
} else {
|
|
$per = " ";
|
|
}
|
|
my $numformat = ($sdr->sensor_units_1 & 0b11000000) >> 6;
|
|
if ($numformat) {
|
|
if ($numformat eq 0b11) {
|
|
#Not sure what to do here..
|
|
} else {
|
|
if ($reading & 0b10000000) {
|
|
if ($numformat eq 0b01) {
|
|
$reading = 0-((~($reading&0b01111111))&0b1111111);
|
|
} elsif ($numformat eq 0b10) {
|
|
$reading = 0-(((~($reading&0b01111111))&0b1111111)+1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if($unitdesc eq "Watts") {
|
|
my $f = ($reading * 3.413);
|
|
$unitdesc = "Watts (".int($f+.5)." BTUs/hr)";
|
|
}
|
|
if($unitdesc eq "C") {
|
|
my $f = ($reading * 9/5) + 32;
|
|
$unitdesc = "C (" . int($f + .5) . " F)";
|
|
}
|
|
if($unitdesc eq "F") {
|
|
my $c = ($reading - 32) * 5/9;
|
|
$unitdesc = "F (" . int($c + .5) . " C)";
|
|
}
|
|
}
|
|
#$unitdesc.= sprintf(" %x",$sdr->sensor_type);
|
|
$text = sprintf($lformat,$sdr->id_string . ":",$reading,$per.$unitdesc);
|
|
if ($extext) {
|
|
$text="$text ($extext)";
|
|
}
|
|
push(@output,$text);
|
|
}
|
|
# else {
|
|
# printf("%x %s %d\n",$sdr->sensor_number,$sdr->id_string,$sdr->sensor_type);
|
|
# }
|
|
}
|
|
}
|
|
|
|
if($subcommand eq "all") {
|
|
my @cleds;
|
|
($rc,$text) = power("stat");
|
|
$text = sprintf($format,"Power Status:",$text,"");
|
|
push(@output,$text);
|
|
($rc,@cleds) = checkleds();
|
|
foreach $text (@cleds) {
|
|
push(@output,$text);
|
|
}
|
|
}
|
|
|
|
return($rc,@output);
|
|
}
|
|
|
|
sub readsensor {
|
|
my $sdr = shift;
|
|
my $sensor = $sdr->sensor_number;
|
|
my $netfun = 0x10;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my $code;
|
|
|
|
@cmd = (0x2d,$sensor);
|
|
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
if($code != 0x00) {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
|
|
if(!$text) {
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
chomp $text;
|
|
|
|
return($rc,$text);
|
|
}
|
|
|
|
if ($returnd[38-$authoffset] & 0x20) {
|
|
$rc = 1;
|
|
$text = "N/A";
|
|
return($rc,$text);
|
|
}
|
|
$text = $returnd[37-$authoffset];
|
|
my $exdata1 = $returnd[39-$authoffset];
|
|
my $exdata2 = $returnd[39-$authoffset];
|
|
my $extext;
|
|
my @exparts;
|
|
if ($sdr->event_type_code == 0x1) {
|
|
if ($exdata1 & 1<<5) {
|
|
$extext = "At or above upper non-recoverable threshold";
|
|
} elsif ($exdata1 & 1<<4) {
|
|
$extext = "At or above upper critical threshold";
|
|
} elsif ($exdata1 & 1<<3) {
|
|
$extext = "At or above upper non-critical threshold";
|
|
}
|
|
if ($exdata1 & 1<<2) {
|
|
$extext = "At or below lower non-critical threshold";
|
|
} elsif ($exdata1 & 1<<1) {
|
|
$extext = "At or below lower critical threshold";
|
|
} elsif ($exdata1 & 1) {
|
|
$extext = "At or below lower non-recoverable threshold";
|
|
}
|
|
} elsif ($sdr->event_type_code == 0x6f) {
|
|
if ($sdr->sensor_type == 0x10) {
|
|
@exparts=();
|
|
if ($exdata1 & 1<<4) {
|
|
push @exparts,"SEL full";
|
|
} elsif ($exdata1 & 1<<5) {
|
|
push @exparts,"SEL almost full";
|
|
}
|
|
if ($exdata1 & 1) {
|
|
push @exparts,"Correctable Memory Error Logging Disabled";
|
|
}
|
|
if ($exdata1 & 1<<3) {
|
|
push @exparts,"All logging disabled";
|
|
} elsif ($exdata1 & 1<<1) {
|
|
push @exparts,"Some logging disabled";
|
|
}
|
|
if (@exparts) {
|
|
$extext = join(",",@exparts);
|
|
}
|
|
} elsif ($sdr->sensor_type == 0x7) {
|
|
@exparts=();
|
|
if ($exdata1 & 1) {
|
|
push @exparts,"IERR";
|
|
}
|
|
if ($exdata1 & 1<<1) {
|
|
push @exparts,"Thermal trip";
|
|
}
|
|
if ($exdata1 & 1<<2) {
|
|
push @exparts,"FRB1/BIST failure";
|
|
}
|
|
if ($exdata1 & 1<<3) {
|
|
push @exparts,"FRB2/Hang in POST due to processor";
|
|
}
|
|
if ($exdata1 & 1<<4) {
|
|
push @exparts,"FRB3/Processor Initialization failure";
|
|
}
|
|
if ($exdata1 & 1<<5) {
|
|
push @exparts,"Configuration error";
|
|
}
|
|
if ($exdata1 & 1<<6) {
|
|
push @exparts,"Uncorrectable CPU-complex error";
|
|
}
|
|
if ($exdata1 & 1<<7) {
|
|
push @exparts,"Present";
|
|
}
|
|
if ($exdata1 & 1<<8) {
|
|
push @exparts,"Processor disabled";
|
|
}
|
|
if ($exdata1 & 1<<9) {
|
|
push @exparts,"Terminator present";
|
|
}
|
|
if ($exdata1 & 1<<10) {
|
|
push @exparts,"Hardware throttled";
|
|
}
|
|
} elsif ($sdr->sensor_type == 0x8) {
|
|
@exparts=();
|
|
if ($exdata1 & 1) {
|
|
push @exparts,"Present";
|
|
}
|
|
if ($exdata1 & 1<<1) {
|
|
push @exparts,"Failed";
|
|
}
|
|
if ($exdata1 & 1<<2) {
|
|
push @exparts,"Failure predicted";
|
|
}
|
|
if ($exdata1 & 1<<3) {
|
|
push @exparts,"AC Lost";
|
|
}
|
|
if ($exdata1 & 1<<4) {
|
|
push @exparts,"AC input lost or out of range";
|
|
}
|
|
if ($exdata1 & 1<<5) {
|
|
push @exparts,"AC input out of range";
|
|
}
|
|
if ($exdata1 & 1<<6) {
|
|
push @exparts,"Configuration error";
|
|
}
|
|
if (@exparts) {
|
|
$extext = join(",",@exparts);
|
|
}
|
|
} elsif ($sdr->sensor_type == 0x13) {
|
|
@exparts=();
|
|
if ($exdata1 & 1) {
|
|
push @exparts,"Front panel NMI/Diagnostic";
|
|
}
|
|
if ($exdata1 & 1<<1) {
|
|
push @exparts,"Bus timeout";
|
|
}
|
|
if ($exdata1 & 1<<2) {
|
|
push @exparts,"I/O channel check NMI";
|
|
}
|
|
if ($exdata1 & 1<<3) {
|
|
push @exparts,"Software NMI";
|
|
}
|
|
if ($exdata1 & 1<<4) {
|
|
push @exparts,"PCI PERR";
|
|
}
|
|
if ($exdata1 & 1<<5) {
|
|
push @exparts,"PCI SERR";
|
|
}
|
|
if ($exdata1 & 1<<6) {
|
|
push @exparts,"EISA failsafe timeout";
|
|
}
|
|
if ($exdata1 & 1<<7) {
|
|
push @exparts,"Bus correctable .rror";
|
|
}
|
|
if ($exdata1 & 1<<8) {
|
|
push @exparts,"Bus uncorrectable error";
|
|
}
|
|
if ($exdata1 & 1<<9) {
|
|
push @exparts,"Fatal NMI";
|
|
}
|
|
if ($exdata1 & 1<<10) {
|
|
push @exparts,"Bus fatal error";
|
|
}
|
|
if (@exparts) {
|
|
$extext = join(",",@exparts);
|
|
}
|
|
} elsif ($sdr->sensor_type == 0xc) {
|
|
@exparts=();
|
|
if ($exdata1 & 1) {
|
|
push @exparts,"Correctable error(s)";
|
|
}
|
|
if ($exdata1 & 1<<1) {
|
|
push @exparts,"Uncorrectable error(s)";
|
|
}
|
|
if ($exdata1 & 1<<2) {
|
|
push @exparts,"Parity";
|
|
}
|
|
if ($exdata1 & 1<<3) {
|
|
push @exparts,"Memory scrub failure";
|
|
}
|
|
if ($exdata1 & 1<<4) {
|
|
push @exparts,"DIMM disabled";
|
|
}
|
|
if ($exdata1 & 1<<5) {
|
|
push @exparts,"Correctable error limit reached";
|
|
}
|
|
if ($exdata1 & 1<<6) {
|
|
push @exparts,"Present";
|
|
}
|
|
if ($exdata1 & 1<<7) {
|
|
push @exparts,"Configuration error";
|
|
}
|
|
if ($exdata1 & 1<<8) {
|
|
push @exparts,"Spare";
|
|
}
|
|
if (@exparts) {
|
|
$extext = join(",",@exparts);
|
|
}
|
|
} elsif ($sdr->sensor_type == 0x21) {
|
|
@exparts=();
|
|
if ($exdata1 & 1) {
|
|
push @exparts,"Fault";
|
|
}
|
|
if ($exdata1 & 1<<1) {
|
|
push @exparts,"Identify";
|
|
}
|
|
if ($exdata1 & 1<<2) {
|
|
push @exparts,"Installed/attached";
|
|
}
|
|
if ($exdata1 & 1<<3) {
|
|
push @exparts,"Ready for install";
|
|
}
|
|
if ($exdata1 & 1<<4) {
|
|
push @exparts,"Ready for removal";
|
|
}
|
|
if ($exdata1 & 1<<5) {
|
|
push @exparts,"Powered off";
|
|
}
|
|
if ($exdata1 & 1<<6) {
|
|
push @exparts,"Removal requested";
|
|
}
|
|
if ($exdata1 & 1<<7) {
|
|
push @exparts,"Interlocked";
|
|
}
|
|
if ($exdata1 & 1<<8) {
|
|
push @exparts,"Disabled";
|
|
}
|
|
if ($exdata1 & 1<<9) {
|
|
push @exparts,"Spare";
|
|
}
|
|
} elsif ($sdr->sensor_type == 0xf) {
|
|
@exparts=();
|
|
if ($exdata1 & 1) {
|
|
push @exparts,"POST error";
|
|
}
|
|
if ($exdata1 & 1<<1) {
|
|
push @exparts,"Firmware hang";
|
|
}
|
|
if ($exdata1 & 1<<2) {
|
|
push @exparts,"Firmware progress";
|
|
}
|
|
if (@exparts) {
|
|
$extext = join(",",@exparts);
|
|
}
|
|
} elsif ($sdr->sensor_type == 0x9) {
|
|
@exparts=();
|
|
if ($exdata1 & 1) {
|
|
push @exparts,"Power off";
|
|
}
|
|
if ($exdata1 & 1<<1) {
|
|
push @exparts,"Power off";
|
|
}
|
|
if ($exdata1 & 1<<2) {
|
|
push @exparts,"240VA Power Down";
|
|
}
|
|
if ($exdata1 & 1<<3) {
|
|
push @exparts,"Interlock Power Down";
|
|
}
|
|
if ($exdata1 & 1<<4) {
|
|
push @exparts,"AC lost";
|
|
}
|
|
if ($exdata1 & 1<<5) {
|
|
push @exparts,"Soft power control failure";
|
|
}
|
|
if ($exdata1 & 1<<6) {
|
|
push @exparts,"Power unit failure";
|
|
}
|
|
if ($exdata1 & 1<<7) {
|
|
push @exparts,"Power unit failure predicted";
|
|
}
|
|
if (@exparts) {
|
|
$extext = join(",",@exparts);
|
|
}
|
|
} else {
|
|
$extext = "xCAT needs to add support for ".$sdr->sensor_type;
|
|
}
|
|
}
|
|
|
|
return($rc,$text,$extext);
|
|
}
|
|
|
|
sub initsdr {
|
|
my $netfun;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my $code;
|
|
|
|
my $sdr_rep_info = SDR_rep_info->new();
|
|
|
|
my $resv_id_ls;
|
|
my $resv_id_ms;
|
|
my $nrid_ls = 0;
|
|
my $nrid_ms = 0;
|
|
my $rid_ls = 0;
|
|
my $rid_ms = 0;
|
|
my $sdr_ver;
|
|
my $sdr_type;
|
|
my $sdr_offset;
|
|
my $sdr_len;
|
|
my @sdr_data = ();
|
|
my $offset;
|
|
my $len;
|
|
my $i;
|
|
# my $numbytes = 27;
|
|
my $numbytes = 22;
|
|
my $override_string;
|
|
my $ipmisensortab = "$ENV{XCATROOT}/lib/GUMI/ipmisensor.tab";
|
|
my $byte_format;
|
|
my $cache_file;
|
|
|
|
my $mfg_id;
|
|
my $prod_id;
|
|
my $device_id;
|
|
my $dev_rev;
|
|
my $fw_rev1;
|
|
my $fw_rev2;
|
|
|
|
($rc,$text,$mfg_id,$prod_id,$device_id,$dev_rev,$fw_rev1,$fw_rev2) = getdevid();
|
|
if($rc != 0) {
|
|
return($rc,$text);
|
|
}
|
|
|
|
$cache_file = "$cache_dir/sdr_$mfg_id.$prod_id.$device_id.$dev_rev.$fw_rev1.$fw_rev2.$cache_version";
|
|
if($enable_cache eq "yes") {
|
|
$rc = loadsdrcache($cache_file);
|
|
if($rc == 0) {
|
|
return($rc);
|
|
}
|
|
$rc = 0;
|
|
}
|
|
|
|
($rc,$text) = get_sdr_rep_info($sdr_rep_info);
|
|
if($rc != 0) {
|
|
return($rc,$text);
|
|
}
|
|
|
|
if($sdr_rep_info->version != 0x51) {
|
|
$rc = 1;
|
|
$text = "SDR version 51h support only.";
|
|
return($rc,$text);
|
|
}
|
|
|
|
if($sdr_rep_info->resv_sdr != 1) {
|
|
$rc = 1;
|
|
$text = "SDR reservation unsupported.";
|
|
return($rc,$text);
|
|
}
|
|
|
|
($rc,$text,$resv_id_ls,$resv_id_ms) = resv_sdr_repo();
|
|
if($rc != 0) {
|
|
return($rc,$text);
|
|
}
|
|
|
|
if($debug) {
|
|
print "mfg,prod,dev: $mfg_id, $prod_id, $device_id\n";
|
|
printf("SDR info: %02x %d %d\n",$sdr_rep_info->version,$sdr_rep_info->rec_count,$sdr_rep_info->resv_sdr);
|
|
print "resv_id: $resv_id_ls $resv_id_ms\n";
|
|
}
|
|
|
|
foreach(1..$sdr_rep_info->rec_count) {
|
|
$netfun = 0x28;
|
|
@cmd = (0x23,$resv_id_ls,$resv_id_ms,$nrid_ls,$nrid_ms,0,5);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
if($code != 0x00) {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
if(!$text) {
|
|
$rc = 1;
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
###x336 hack
|
|
$rid_ls = $nrid_ls;
|
|
$rid_ms = $nrid_ms;
|
|
###
|
|
$nrid_ls = $returnd[37-$authoffset];
|
|
$nrid_ms = $returnd[38-$authoffset];
|
|
### correct IPMI code
|
|
# $rid_ls = $returnd[39-$authoffset];
|
|
# $rid_ms = $returnd[40-$authoffset];
|
|
###
|
|
$sdr_ver = $returnd[41-$authoffset];
|
|
$sdr_type = $returnd[42-$authoffset];
|
|
$sdr_len = $returnd[43-$authoffset] + 5;
|
|
|
|
if($sdr_type == 0x01) {
|
|
$sdr_offset = 0;
|
|
}
|
|
elsif($sdr_type == 0x02) {
|
|
$sdr_offset = 16;
|
|
}
|
|
elsif($sdr_type == 0xC0) {
|
|
#LED descriptor, maybe
|
|
}
|
|
elsif($sdr_type == 0x11) { #FRU locator
|
|
}
|
|
elsif($sdr_type == 0x12) {
|
|
next;
|
|
}
|
|
else {
|
|
next;
|
|
}
|
|
|
|
@sdr_data = (0,0,0,$sdr_ver,$sdr_type,$sdr_len);
|
|
$offset = 5;
|
|
for($i=5;$i<$sdr_len;$i+=$numbytes) {
|
|
$len = $numbytes;
|
|
if($offset+$len > $sdr_len) {
|
|
$len = $sdr_len - $offset;
|
|
}
|
|
|
|
@cmd = (0x23,$resv_id_ls,$resv_id_ms,$rid_ls,$rid_ms,$offset,$len);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
if($code != 0x00) {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
if(!$text) {
|
|
$rc = 1;
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
|
|
@sdr_data = (@sdr_data,@returnd[39-$authoffset..@returnd-2]);
|
|
|
|
$offset += $len;
|
|
}
|
|
if($sdr_type == 0x11) { #FRU locator
|
|
my $sdr = decode_fru_locator(@sdr_data);
|
|
if ($sdr) {
|
|
$sdr_hash{$sdr->sensor_owner_id . "." . $sdr->sensor_owner_lun . "." . $sdr->sensor_number} = $sdr;
|
|
}
|
|
next;
|
|
}
|
|
|
|
if($debug) {
|
|
hexadump(\@sdr_data);
|
|
}
|
|
if($sdr_type == 0x12) {
|
|
hexadump(\@sdr_data);
|
|
next;
|
|
}
|
|
|
|
my $sdr = SDR->new();
|
|
|
|
if ($mfg_id == 2 && $sdr_type==0xC0 && $sdr_data[9] == 0xED) {
|
|
#printf("%02x%02x\n",$sdr_data[13],$sdr_data[12]);
|
|
$sdr->rec_type($sdr_type);
|
|
$sdr->sensor_type($sdr_data[9]);
|
|
#Using an impossible sensor number to not conflict with decodealert
|
|
$sdr->sensor_owner_id(260);
|
|
$sdr->sensor_owner_lun(260);
|
|
$sdr->id_string("LED");
|
|
if ($sdr_data[12] > $sdr_data[13]) {
|
|
$sdr->led_id(($sdr_data[13]<<8)+$sdr_data[12]);
|
|
} else {
|
|
$sdr->led_id(($sdr_data[12]<<8)+$sdr_data[13]);
|
|
}
|
|
#$sdr->led_id_ms($sdr_data[13]);
|
|
#$sdr->led_id_ls($sdr_data[12]);
|
|
$sdr->sensor_number(sprintf("%04x",$sdr->led_id));
|
|
#printf("%02x,%02x,%04x\n",$mfg_id,$prod_id,$sdr->led_id);
|
|
#Was going to have a human readable name, but specs
|
|
#seem to not to match reality...
|
|
#$override_string = getsensorname($mfg_id,$prod_id,$sdr->sensor_number,$ipmiledtab);
|
|
#I'm hacking in owner and lun of 260 for LEDs....
|
|
$sdr_hash{"260.260.".$sdr->led_id} = $sdr;
|
|
next;
|
|
}
|
|
|
|
|
|
$sdr->rec_type($sdr_type);
|
|
$sdr->sensor_owner_id($sdr_data[6]);
|
|
$sdr->sensor_owner_lun($sdr_data[7]);
|
|
$sdr->sensor_number($sdr_data[8]);
|
|
$sdr->entity_id($sdr_data[9]);
|
|
$sdr->entity_instance($sdr_data[10]);
|
|
$sdr->sensor_type($sdr_data[13]);
|
|
$sdr->event_type_code($sdr_data[14]);
|
|
$sdr->sensor_units_2($sdr_data[22]);
|
|
$sdr->sensor_units_3($sdr_data[23]);
|
|
|
|
if($sdr_type == 0x01) {
|
|
$sdr->sensor_units_1($sdr_data[21]);
|
|
$sdr->linearization($sdr_data[24] & 0b01111111);
|
|
$sdr->M(comp2int(10,(($sdr_data[26] & 0b11000000) << 2) + $sdr_data[25]));
|
|
$sdr->B(comp2int(10,(($sdr_data[28] & 0b11000000) << 2) + $sdr_data[27]));
|
|
$sdr->R_exp(comp2int(4,($sdr_data[30] & 0b11110000) >> 4));
|
|
$sdr->B_exp(comp2int(4,$sdr_data[30] & 0b00001111));
|
|
} elsif ($sdr_type == 0x02) {
|
|
$sdr->sensor_units_1($sdr_data[21]);
|
|
}
|
|
|
|
$sdr->id_string_type($sdr_data[48-$sdr_offset]);
|
|
|
|
$override_string = getsensorname($mfg_id,$prod_id,$sdr->sensor_number,$ipmisensortab);
|
|
|
|
if($override_string ne "") {
|
|
$sdr->id_string($override_string);
|
|
}
|
|
else {
|
|
unless (defined $sdr->id_string_type) { next; }
|
|
$byte_format = ($sdr->id_string_type & 0b11000000) >> 6;
|
|
if($byte_format == 0b11) {
|
|
my $len = ($sdr->id_string_type & 0b00011111) - 1;
|
|
if($len > 1) {
|
|
$sdr->id_string(pack("C*",@sdr_data[49-$sdr_offset..49-$sdr_offset+$len]));
|
|
}
|
|
else {
|
|
$sdr->id_string("no description");
|
|
}
|
|
}
|
|
elsif($byte_format == 0b10) {
|
|
$sdr->id_string("ASCII packed unsupported");
|
|
}
|
|
elsif($byte_format == 0b01) {
|
|
$sdr->id_string("BCD unsupported");
|
|
}
|
|
elsif($byte_format == 0b00) {
|
|
my $len = ($sdr->id_string_type & 0b00011111) - 1;
|
|
if ($len > 1) { #It should be something, but need sample to code
|
|
$sdr->id_string("unicode unsupported");
|
|
} else {
|
|
next;
|
|
}
|
|
}
|
|
}
|
|
|
|
$sdr_hash{$sdr->sensor_owner_id . "." . $sdr->sensor_owner_lun . "." . $sdr->sensor_number} = $sdr;
|
|
}
|
|
|
|
if($debug) {
|
|
my $key;
|
|
# foreach $key (sort {$sdr_hash{$a}->sensor_number <=> $sdr_hash{$b}->sensor_number} keys %sdr_hash) {
|
|
foreach $key (sort {$sdr_hash{$a}->id_string cmp $sdr_hash{$b}->id_string} keys %sdr_hash) {
|
|
my $sdr = $sdr_hash{$key};
|
|
# printf("%d %x %s\n",$sdr->rec_type,$sdr->sensor_number,$sdr->id_string);
|
|
# printf("%x %x %x %s\n",$sdr->sensor_owner_id,$sdr->sensor_owner_lun,$sdr->sensor_number,$sdr->id_string);
|
|
printf("%x %x %x %s %d\n",$sdr->sensor_owner_id,$sdr->sensor_owner_lun,$sdr->sensor_number,$sdr->id_string,$sdr->linearization);
|
|
}
|
|
# printf("\n%x %s\n",$sdr_hash{0x70}->sensor_number,$sdr_hash{0x70}->id_string);
|
|
}
|
|
|
|
if($enable_cache eq "yes") {
|
|
storsdrcache($cache_file);
|
|
}
|
|
|
|
return($rc,$text);
|
|
}
|
|
|
|
sub getsensorname
|
|
{
|
|
my $mfgid = shift;
|
|
my $prodid = shift;
|
|
my $sensor = shift;
|
|
my $file = shift;
|
|
|
|
my $mfg;
|
|
my $prod;
|
|
my $type;
|
|
my $num;
|
|
my $desc;
|
|
my $name="";
|
|
|
|
if ($file eq "ibmleds") {
|
|
if ($xCAT::data::ibmleds::leds{"$mfgid,$prodid"}->{$sensor}) {
|
|
return $xCAT::data::ibmleds::leds{"$mfgid,$prodid"}->{$sensor}. " LED";
|
|
} elsif ($ndebug) {
|
|
return "Unknown $sensor/$mfgid/$prodid";
|
|
} else {
|
|
return sprintf ("LED 0x%x",$sensor);
|
|
}
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
sub getchassiscap {
|
|
my $netfun = 0x00;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my $code;
|
|
|
|
@cmd = (0x00);
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
|
|
$code = $returnd[36-$authoffset];
|
|
if($code == 0x00) {
|
|
$text = "";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
if(!$text) {
|
|
$rc = 1;
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
|
|
return($rc,@returnd[37-$authoffset..@returnd-2]);
|
|
}
|
|
|
|
sub getdevid {
|
|
my $netfun = 0x18;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my $code;
|
|
|
|
@cmd = (0x01);
|
|
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
else {
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
$text = "";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
if(!$text) {
|
|
$rc = 1;
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
}
|
|
|
|
my $device_id = $returnd[37-$authoffset];
|
|
my $device_rev = $returnd[38-$authoffset] & 0b00001111;
|
|
my $firmware_rev1 = $returnd[39-$authoffset] & 0b01111111;
|
|
my $firmware_rev2 = $returnd[40-$authoffset];
|
|
my $ipmi_ver = $returnd[41-$authoffset];
|
|
my $dev_support = $returnd[42-$authoffset];
|
|
my $sensor_device = 0;
|
|
my $SDR = 0;
|
|
my $SEL = 0;
|
|
my $FRU = 0;
|
|
my $IPMB_ER = 0;
|
|
my $IPMB_EG = 0;
|
|
my $BD = 0;
|
|
my $CD = 0;
|
|
if($dev_support & 0b00000001) {
|
|
$sensor_device = 1;
|
|
}
|
|
if($dev_support & 0b00000010) {
|
|
$SDR = 1;
|
|
}
|
|
if($dev_support & 0b00000100) {
|
|
$SEL = 1;
|
|
}
|
|
if($dev_support & 0b00001000) {
|
|
$FRU = 1;
|
|
}
|
|
if($dev_support & 0b00010000) {
|
|
$IPMB_ER = 1;
|
|
}
|
|
if($dev_support & 0b00100000) {
|
|
$IPMB_EG = 1;
|
|
}
|
|
if($dev_support & 0b01000000) {
|
|
$BD = 1;
|
|
}
|
|
if($dev_support & 0b10000000) {
|
|
$CD = 1;
|
|
}
|
|
my $mfg_id = $returnd[43-$authoffset] + $returnd[44-$authoffset]*0x100 + $returnd[45-$authoffset]*0x10000;
|
|
my $prod_id = $returnd[46-$authoffset] + $returnd[47-$authoffset]*0x100;
|
|
my @data = @returnd[48-$authoffset..@returnd-2];
|
|
|
|
return($rc,$text,$mfg_id,$prod_id,$device_id,$device_rev,$firmware_rev1,$firmware_rev2);
|
|
}
|
|
|
|
sub getguid {
|
|
my $guidcmd = shift;
|
|
my $netfun = @$guidcmd[0] || 0x18;
|
|
my @cmd = @$guidcmd[1] || 0x37;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my $code;
|
|
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
else {
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
$text = "";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
if(!$text) {
|
|
$rc = 1;
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
}
|
|
|
|
my @guid = @returnd[37-$authoffset..52-$authoffset];
|
|
my $guidtext = sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",@guid);
|
|
$guidtext =~ tr/[a-z]/[A-Z]/;
|
|
|
|
return($rc,$text,$guidtext);
|
|
}
|
|
|
|
sub get_sdr_rep_info {
|
|
my $sdr_rep_info = shift;
|
|
|
|
my $netfun = 0x28;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my $code;
|
|
|
|
@cmd = (0x20);
|
|
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
else {
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
$text = "";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
if(!$text) {
|
|
$rc = 1;
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
}
|
|
|
|
$sdr_rep_info->version($returnd[37-$authoffset]);
|
|
$sdr_rep_info->rec_count($returnd[38-$authoffset] + $returnd[39-$authoffset]*0x100);
|
|
$sdr_rep_info->resv_sdr(($returnd[50-$authoffset] & 0b00000010) ? 1 : 0);
|
|
|
|
return($rc,$text);
|
|
}
|
|
|
|
sub resv_sdr_repo {
|
|
my $netfun = 0x28;
|
|
my @cmd;
|
|
my @returnd = ();
|
|
my $error;
|
|
my $rc = 0;
|
|
my $text;
|
|
my $code;
|
|
|
|
@cmd = (0x22);
|
|
|
|
$error = docmd(
|
|
$netfun,
|
|
\@cmd,
|
|
\@returnd
|
|
);
|
|
|
|
if($error) {
|
|
$rc = 1;
|
|
$text = $error;
|
|
return($rc,$text);
|
|
}
|
|
else {
|
|
$code = $returnd[36-$authoffset];
|
|
|
|
if($code == 0x00) {
|
|
$text = "";
|
|
}
|
|
else {
|
|
$rc = 1;
|
|
$text = $codes{$code};
|
|
if(!$text) {
|
|
$rc = 1;
|
|
$text = sprintf("unknown response %02x",$code);
|
|
}
|
|
return($rc,$text);
|
|
}
|
|
}
|
|
|
|
my $resv_id_ls = $returnd[37-$authoffset];
|
|
my $resv_id_ms = $returnd[38-$authoffset];
|
|
|
|
return($rc,$text,$resv_id_ls,$resv_id_ms);
|
|
}
|
|
|
|
sub docmd {
|
|
my $netfun = shift;
|
|
my $cmd = shift;
|
|
my $response = shift;
|
|
|
|
my @rn;
|
|
my $length;
|
|
|
|
my @msg;
|
|
my @message;
|
|
my $error = "";
|
|
my @response;
|
|
my @data;
|
|
|
|
incseqlun();
|
|
|
|
@data = ($rqsa,$seqlun,@$cmd);
|
|
@rn = ($rssa,$netfun);
|
|
$length = (scalar @data)+4;
|
|
@message = ($rssa,$netfun,dochksum(\@rn),@data,dochksum(\@data));
|
|
|
|
incseqnum();
|
|
|
|
@msg = (
|
|
@rmcp,
|
|
$auth,
|
|
@seqnum,
|
|
@session_id,
|
|
authcode(2,\@message),
|
|
$length,
|
|
@message
|
|
);
|
|
|
|
($error,@$response) = domsg($sock,\@msg,$timeout,1);
|
|
|
|
return($error);
|
|
}
|
|
|
|
sub getchanauthcap {
|
|
$auth = 0x00;
|
|
my $netfun = 0x18;
|
|
my @data;
|
|
my @rn;
|
|
my $length;
|
|
|
|
my @msg;
|
|
my $error = "";
|
|
my @response;
|
|
my $code;
|
|
|
|
@data = ($rqsa,$seqlun,0x38,0x8e,0x04);
|
|
@rn = ($rssa,$netfun);
|
|
$length = (scalar @data)+4;
|
|
|
|
@msg = (
|
|
@rmcp,
|
|
$auth,
|
|
@seqnum,
|
|
@session_id,
|
|
$length,
|
|
$rssa,
|
|
$netfun,
|
|
dochksum(\@rn),
|
|
@data,
|
|
dochksum(\@data)
|
|
);
|
|
|
|
($error,@response) = domsg($sock,\@msg,$timeout,0);
|
|
|
|
if($error) {
|
|
return($error);
|
|
}
|
|
|
|
$code = $response[20];
|
|
if ($code == 0xcc) {
|
|
#Despite the fact that the IPMI 1.5 spec declared the high bits to be
|
|
#reserved, some 1.5 BMCs checked the value anyway (erroneously)
|
|
#This retries with the IPMI 2.0 bit cleared
|
|
@data = ($rqsa,$seqlun,0x38,0x0e,0x04);
|
|
@rn = ($rssa,$netfun);
|
|
$length = (scalar @data)+4;
|
|
|
|
@msg = (
|
|
@rmcp,
|
|
$auth,
|
|
@seqnum,
|
|
@session_id,
|
|
$length,
|
|
$rssa,
|
|
$netfun,
|
|
dochksum(\@rn),
|
|
@data,
|
|
dochksum(\@data)
|
|
);
|
|
|
|
($error,@response) = domsg($sock,\@msg,$timeout,0);
|
|
|
|
if($error) {
|
|
return($error);
|
|
}
|
|
$code = $response[20];
|
|
}
|
|
if($code != 0x00) {
|
|
$error = $codes{$code};
|
|
if(!$error) {
|
|
$error = "Unknown get channel authentication capabilities error $code"
|
|
}
|
|
return($error);
|
|
}
|
|
|
|
$channel_number=$response[21];
|
|
|
|
if($response[22] & 0b10000000 and $response[24] & 0b00000010) {
|
|
$ipmiv2=1;
|
|
}
|
|
if($response[22] & 0b00000100) {
|
|
$auth=0x02;
|
|
}
|
|
elsif($response[22] & 0b00010000) {
|
|
$auth=0x04;
|
|
}
|
|
else {
|
|
$error = "unsupported Authentication Type Support";
|
|
}
|
|
|
|
return($error);
|
|
}
|
|
|
|
sub getsessionchallenge {
|
|
my $tauth = 0x00;
|
|
my $netfun = 0x18;
|
|
my @data;
|
|
my @rn;
|
|
my $length;
|
|
|
|
my @msg;
|
|
my $error = "";
|
|
my @response;
|
|
my $code;
|
|
|
|
incseqlun();
|
|
|
|
@data = ($rqsa,$seqlun,0x39,$auth,@user);
|
|
@rn = ($rssa,$netfun);
|
|
$length = (scalar @data)+4;
|
|
|
|
@msg = (
|
|
@rmcp,
|
|
$tauth,
|
|
@seqnum,
|
|
@session_id,
|
|
$length,
|
|
$rssa,
|
|
$netfun,
|
|
dochksum(\@rn),
|
|
@data,
|
|
dochksum(\@data)
|
|
);
|
|
|
|
($error,@response) = domsg($sock,\@msg,$timeout,0);
|
|
|
|
if(!$error) {
|
|
$code = $response[20];
|
|
if($code != 0x00) {
|
|
$error = $codes{$code};
|
|
if(!$error) {
|
|
$error = "Unknown get session challenge error $code"
|
|
}
|
|
}
|
|
|
|
if($code == 0x81) {
|
|
$error = "Invalid user name";
|
|
}
|
|
elsif($code == 0x82) {
|
|
$error = "null user name not enabled";
|
|
}
|
|
|
|
@session_id = @response[21,22,23,24];
|
|
|
|
for (my $i=0;$i<16;$i++){
|
|
$challenge[$i] = $response[25+$i];
|
|
}
|
|
}
|
|
|
|
return($error);
|
|
}
|
|
|
|
sub activatesession {
|
|
my $netfun = 0x18;
|
|
my @data;
|
|
my @rn;
|
|
my $length;
|
|
|
|
my @msg;
|
|
my @message;
|
|
my $error = "";
|
|
my @response;
|
|
my $code;
|
|
|
|
incseqlun();
|
|
|
|
@data = ($rqsa,$seqlun,0x3A,$auth,0x04,@challenge,0x01,0x00,0x00,0x00);
|
|
@rn = ($rssa,$netfun);
|
|
$length = (scalar @data)+4;
|
|
@message = ($rssa,$netfun,dochksum(\@rn),@data,dochksum(\@data));
|
|
|
|
@msg = (
|
|
@rmcp,
|
|
$auth,
|
|
@seqnum,
|
|
@session_id,
|
|
authcode(2,\@message),
|
|
$length,
|
|
@message
|
|
);
|
|
|
|
($error,@response) = domsg($sock,\@msg,$timeout,0);
|
|
|
|
if(!$error) {
|
|
$code = $response[36];
|
|
if($code != 0x00) {
|
|
$error = $codes{$code};
|
|
if(!$error) {
|
|
$error = "Unknown activate session error $code"
|
|
}
|
|
}
|
|
|
|
if($code == 0x81) {
|
|
$error = "No session slot available";
|
|
}
|
|
elsif($code == 0x82) {
|
|
$error = "No slot available for given user";
|
|
}
|
|
elsif($code == 0x83) {
|
|
$error = "No slot available to support user due to maximum privilege capability";
|
|
}
|
|
elsif($code == 0x84) {
|
|
$error = "Session sequence number out-of-range";
|
|
}
|
|
elsif($code == 0x85) {
|
|
$error = "Invalid session ID in request";
|
|
}
|
|
elsif($code == 0x86) {
|
|
$error = "Requested maximum privilege level exceeds user and/of channel privilege limit";
|
|
}
|
|
|
|
unless ($error) {
|
|
$auth = $response[37];
|
|
if($auth == 0x00) {
|
|
$authoffset=16;
|
|
}
|
|
elsif($auth == 0x02) {
|
|
}
|
|
elsif($auth == 0x04) {
|
|
}
|
|
else {
|
|
$error = "activate session requested unsupported Authentication Type Support";
|
|
}
|
|
}
|
|
|
|
###check
|
|
@session_id = @response[38,39,40,41];
|
|
@seqnum = @response[42,43,44,45];
|
|
}
|
|
|
|
return($error);
|
|
}
|
|
|
|
sub setprivlevel()
|
|
{
|
|
my $netfun = 0x18;
|
|
my @data;
|
|
my @rn;
|
|
my $length;
|
|
|
|
my @msg;
|
|
my @message;
|
|
my $error = "";
|
|
my @response;
|
|
my $code;
|
|
|
|
incseqlun();
|
|
|
|
@data = ($rqsa,$seqlun,0x3B,0x04);
|
|
@rn = ($rssa,$netfun);
|
|
$length = (scalar @data)+4;
|
|
@message = ($rssa,$netfun,dochksum(\@rn),@data,dochksum(\@data));
|
|
|
|
@msg = (
|
|
@rmcp,
|
|
$auth,
|
|
@seqnum,
|
|
@session_id,
|
|
authcode(2,\@message),
|
|
$length,
|
|
@message
|
|
);
|
|
|
|
($error,@response) = domsg($sock,\@msg,$timeout,1);
|
|
|
|
if(!$error) {
|
|
$code = $response[36-$authoffset];
|
|
if($code != 0x00) {
|
|
$error = $codes{$code};
|
|
if(!$error) {
|
|
$error = "Unknown set session privilege level error $code"
|
|
}
|
|
}
|
|
|
|
if($code == 0x80) {
|
|
$error = "Requested level not available for this user";
|
|
}
|
|
elsif($code == 0x81) {
|
|
$error = "Requested level exceeds channel and/or user privilege limit";
|
|
}
|
|
elsif($code == 0x82) {
|
|
$error = "Cannot disable user level authentication";
|
|
}
|
|
}
|
|
|
|
return($error);
|
|
}
|
|
|
|
sub closesession()
|
|
{
|
|
incseqnum();
|
|
|
|
my $netfun = 0x18;
|
|
my @data;
|
|
my @rn;
|
|
my $length;
|
|
|
|
my @msg;
|
|
my @message;
|
|
my $error = "";
|
|
my @response;
|
|
my $code;
|
|
|
|
incseqlun();
|
|
|
|
@data = ($rqsa,$seqlun,0x3C,@session_id);
|
|
@rn = ($rssa,$netfun);
|
|
$length = (scalar @data)+4;
|
|
@message = ($rssa,$netfun,dochksum(\@rn),@data,dochksum(\@data));
|
|
|
|
@msg = (
|
|
@rmcp,
|
|
$auth,
|
|
@seqnum,
|
|
@session_id,
|
|
authcode(2,\@message),
|
|
$length,
|
|
@message
|
|
);
|
|
|
|
($error,@response) = domsg($sock,\@msg,$timeout,1);
|
|
|
|
if(!$error) {
|
|
$code = $response[36-$authoffset];
|
|
if($code != 0x00) {
|
|
$error = $codes{$code};
|
|
if(!$error) {
|
|
$error = "Unknown close session error $code"
|
|
}
|
|
}
|
|
|
|
if($code == 0x87) {
|
|
$error = "Invalid session ID in request";
|
|
}
|
|
}
|
|
|
|
return($error);
|
|
}
|
|
|
|
sub domsg {
|
|
my $sock = shift;
|
|
my $msg = shift;
|
|
my $timeout = shift;
|
|
my $seq = shift || 0;
|
|
my $debug = $localdebug;
|
|
my $trys = $localtrys;
|
|
my $send;
|
|
my $quit = 0;
|
|
my $error="";
|
|
my $recv;
|
|
my @response;
|
|
my $timedout;
|
|
my @foo;
|
|
my @message;
|
|
|
|
$send = pack('C*',@$msg);
|
|
|
|
while($trys > 0) {
|
|
$trys--;
|
|
$error = "";
|
|
$timedout = 0;
|
|
|
|
if($debug) {
|
|
print "try: $trys, timeout: $timeout\n";
|
|
}
|
|
|
|
if(!$sock->send($send)) {
|
|
$error = $!;
|
|
sleep(1);
|
|
next;
|
|
}
|
|
my $s = IO::Select->new($sock);
|
|
#local $SIG{ALRM} = sub { $timedout = 1 and die };
|
|
#alarm($timeout);
|
|
my $received = $s->can_read($timeout);
|
|
if($received and $received > 0) {
|
|
if ($sock->recv($recv,1300)) {
|
|
if($recv) {
|
|
@response = unpack("C*",$recv);
|
|
last;
|
|
}
|
|
} else {
|
|
$error = $!;
|
|
}
|
|
}
|
|
else {
|
|
$error = "timeout";
|
|
}
|
|
|
|
###ugly updated hack to support md5.
|
|
if($seq) {
|
|
incseqnum();
|
|
|
|
@$msg[5..8] = @seqnum[0..3];
|
|
@message = @$msg[30..@$msg-1];
|
|
if($auth != 0x00) {
|
|
@$msg[13..28] = authcode(2,\@message);
|
|
}
|
|
|
|
$send = pack('C*',@$msg);
|
|
}
|
|
}
|
|
|
|
if($timedout == 1) {
|
|
if($error) {
|
|
$error = "timeout $error"
|
|
}
|
|
else {
|
|
$error = "timeout"
|
|
}
|
|
}
|
|
|
|
return($error,@response);
|
|
}
|
|
|
|
sub dochksum()
|
|
{
|
|
my $data = shift;
|
|
my $sum = 0;
|
|
|
|
foreach(@$data) {
|
|
$sum += $_;
|
|
}
|
|
|
|
$sum = ~$sum + 1;
|
|
return($sum & 0xFF);
|
|
}
|
|
|
|
sub dopad16 {
|
|
my @pad16 = unpack("C*",shift);
|
|
|
|
for(my $i=@pad16;$i<16;$i++) {
|
|
$pad16[$i] = 0;
|
|
}
|
|
|
|
return(@pad16);
|
|
}
|
|
|
|
sub hexdump {
|
|
my $data = shift;
|
|
|
|
foreach(@$data) {
|
|
printf("%02x ",$_);
|
|
}
|
|
print "\n";
|
|
}
|
|
|
|
sub getascii {
|
|
my @alpha;
|
|
my $text ="";
|
|
my $c = 0;
|
|
|
|
foreach(@_) {
|
|
if (defined $_ and $_ < 128 and $_ > 0x20) {
|
|
$alpha[$c] = sprintf("%c",$_);
|
|
} else {
|
|
$alpha[$c]=" ";
|
|
}
|
|
if($alpha[$c] !~ /[\/\w\-:\[\.\]]/) {
|
|
if ($alpha[($c-1)] !~ /\s/) {
|
|
$alpha[$c] = " ";
|
|
} else {
|
|
$c--;
|
|
}
|
|
}
|
|
$c++;
|
|
}
|
|
foreach(@alpha) {
|
|
$text=$text.$_;
|
|
}
|
|
$text =~ s/^\s+|\s+$//;
|
|
return $text;
|
|
}
|
|
sub phex {
|
|
my $data = shift;
|
|
my @alpha;
|
|
my $text ="";
|
|
my $c = 0;
|
|
|
|
foreach(@$data) {
|
|
$text = $text . sprintf("%02x ",$_);
|
|
$alpha[$c] = sprintf("%c",$_);
|
|
if($alpha[$c] !~ /\w/) {
|
|
$alpha[$c] = " ";
|
|
}
|
|
$c++;
|
|
}
|
|
$text = $text . "(";
|
|
foreach(@alpha) {
|
|
$text=$text.$_;
|
|
}
|
|
$text = $text . ")";
|
|
return $text;
|
|
}
|
|
|
|
sub hexadump {
|
|
my $data = shift;
|
|
my @alpha;
|
|
my $c = 0;
|
|
|
|
foreach(@$data) {
|
|
printf("%02x ",$_);
|
|
$alpha[$c] = sprintf("%c",$_);
|
|
if($alpha[$c] !~ /\w/) {
|
|
$alpha[$c] = ".";
|
|
}
|
|
$c++;
|
|
if($c == 16) {
|
|
print " ";
|
|
foreach(@alpha) {
|
|
print $_;
|
|
}
|
|
print "\n";
|
|
@alpha=();
|
|
$c=0;
|
|
}
|
|
}
|
|
foreach($c..16) {
|
|
print " ";
|
|
}
|
|
foreach(@alpha) {
|
|
print $_;
|
|
}
|
|
print "\n";
|
|
}
|
|
|
|
sub incseqnum {
|
|
my $i;
|
|
|
|
for($i = 0;$i < 4;$i++) {
|
|
if($seqnum[$i] < 0xFF) {
|
|
$seqnum[$i]++;
|
|
last;
|
|
}
|
|
$seqnum[$i] = 0;
|
|
}
|
|
|
|
if($seqnum[3] > 0xFF) {
|
|
@seqnum = (0,0,0,0);
|
|
}
|
|
}
|
|
|
|
sub incseqlun {
|
|
$seqlun += 4;
|
|
|
|
if($seqlun > 0xFF) {
|
|
$seqlun = 0;
|
|
}
|
|
}
|
|
|
|
sub authcode {
|
|
my $type = shift;
|
|
my $message = shift;
|
|
my @authcode;
|
|
|
|
if($auth == 0x02) {
|
|
if($type == 1) {
|
|
@authcode = unpack("C*",md5(pack("C*",@pass,@session_id,@challenge,@pass)));
|
|
}
|
|
elsif($type == 2) {
|
|
@authcode = unpack("C*",md5(pack("C*",@pass,@session_id,@$message,@seqnum,@pass)));
|
|
}
|
|
}
|
|
elsif($auth == 0x04) {
|
|
@authcode = @pass;
|
|
}
|
|
elsif($auth == 0x00) {
|
|
@authcode = ();
|
|
}
|
|
|
|
return(@authcode);
|
|
}
|
|
|
|
sub comp2int {
|
|
my $length = shift;
|
|
my $bits = shift;
|
|
my $neg = 0;
|
|
|
|
if($bits & 2**($length - 1)) {
|
|
$neg = 1;
|
|
}
|
|
|
|
$bits &= (2**($length - 1) - 1);
|
|
|
|
if($neg) {
|
|
$bits -= 2**($length - 1);
|
|
}
|
|
|
|
return($bits);
|
|
}
|
|
|
|
sub timestamp2datetime {
|
|
my $ts = shift;
|
|
if ($ts < 0x20000000) {
|
|
return "BMC Uptime",sprintf("%6d s",$ts);
|
|
}
|
|
my @t = localtime($ts);
|
|
my $time = strftime("%H:%M:%S",@t);
|
|
my $date = strftime("%m/%d/%Y",@t);
|
|
|
|
return($date,$time);
|
|
}
|
|
|
|
sub decodebcd {
|
|
my $numbers = shift;
|
|
my @bcd;
|
|
my $text;
|
|
my $ms;
|
|
my $ls;
|
|
|
|
foreach(@$numbers) {
|
|
$ms = ($_ & 0b11110000) >> 4;
|
|
$ls = ($_ & 0b00001111);
|
|
push(@bcd,$ms);
|
|
push(@bcd,$ls);
|
|
}
|
|
|
|
foreach(@bcd) {
|
|
if($_ < 0x0a) {
|
|
$text .= $_;
|
|
}
|
|
elsif($_ == 0x0a) {
|
|
$text .= " ";
|
|
}
|
|
elsif($_ == 0x0b) {
|
|
$text .= "-";
|
|
}
|
|
elsif($_ == 0x0c) {
|
|
$text .= ".";
|
|
}
|
|
}
|
|
|
|
return($text);
|
|
}
|
|
|
|
sub storsdrcache {
|
|
my $file = shift;
|
|
my $key;
|
|
my $fh;
|
|
|
|
system("mkdir -p $cache_dir");
|
|
if(!open($fh,">$file")) {
|
|
return(1);
|
|
}
|
|
|
|
flock($fh,LOCK_EX) || return(1);
|
|
|
|
foreach $key (keys %sdr_hash) {
|
|
my $r = $sdr_hash{$key};
|
|
store_fd($r,$fh);
|
|
}
|
|
|
|
close($fh);
|
|
|
|
return(0);
|
|
}
|
|
|
|
sub loadsdrcache {
|
|
my $file = shift;
|
|
my $r;
|
|
my $c=0;
|
|
my $fh;
|
|
|
|
if(!open($fh,"<$file")) {
|
|
return(1);
|
|
}
|
|
|
|
flock($fh,LOCK_SH) || return(1);
|
|
|
|
while() {
|
|
eval {
|
|
$r = retrieve_fd($fh);
|
|
} || last;
|
|
|
|
$sdr_hash{$r->sensor_owner_id . "." . $r->sensor_owner_lun . "." . $r->sensor_number} = $r;
|
|
}
|
|
|
|
close($fh);
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
sub preprocess_request {
|
|
$SIG{INT} = $SIG{TERM} = sub {
|
|
foreach (keys %bmc_comm_pids) {
|
|
kill 2, $_;
|
|
}
|
|
exit 0;
|
|
};
|
|
my $request = shift;
|
|
if ($request->{_xcatdest}) { return [$request]; } #exit if preprocessed
|
|
my $callback=shift;
|
|
my @requests;
|
|
|
|
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 ($command eq "rpower") {
|
|
my $subcmd=$exargs[0];
|
|
if ( ($subcmd ne 'stat') && ($subcmd ne 'state') && ($subcmd ne 'status') && ($subcmd ne 'on') && ($subcmd ne 'off') && ($subcmd ne 'softoff') && ($subcmd ne 'nmi')&& ($subcmd ne 'cycle') && ($subcmd ne 'reset') && ($subcmd ne 'boot')) {
|
|
$callback->({data=>["Unsupported command: $command $subcmd", $usage_string]});
|
|
$request = {};
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
if (!$noderange) {
|
|
$usage_string=xCAT::Usage->getUsage($command);
|
|
$callback->({data=>$usage_string});
|
|
$request = {};
|
|
return;
|
|
}
|
|
|
|
#print "noderange=@$noderange\n";
|
|
|
|
# find service nodes for requested nodes
|
|
# build an individual request for each service node
|
|
my $service = "xcat";
|
|
my $sn = xCAT::Utils->get_ServiceNode($noderange, $service, "MN");
|
|
|
|
# build each request for each service node
|
|
|
|
foreach my $snkey (keys %$sn)
|
|
{
|
|
#print "snkey=$snkey\n";
|
|
my $reqcopy = {%$request};
|
|
$reqcopy->{node} = $sn->{$snkey};
|
|
$reqcopy->{'_xcatdest'} = $snkey;
|
|
push @requests, $reqcopy;
|
|
}
|
|
return \@requests;
|
|
}
|
|
|
|
|
|
sub getipmicons {
|
|
my $argr=shift;
|
|
#$argr is [$node,$nodeip,$nodeuser,$nodepass];
|
|
my $cb = shift;
|
|
my $ipmicons={node=>[{name=>[$argr->[0]]}]};
|
|
$ipmicons->{node}->[0]->{bmcaddr}->[0]=$argr->[1];
|
|
$ipmicons->{node}->[0]->{bmcuser}->[0]=$argr->[2];
|
|
$ipmicons->{node}->[0]->{bmcpass}->[0]=$argr->[3];
|
|
$cb->($ipmicons);
|
|
}
|
|
|
|
|
|
|
|
|
|
sub process_request {
|
|
my $request = shift;
|
|
my $callback = shift;
|
|
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 $ipmiuser = 'USERID';
|
|
my $ipmipass = 'PASSW0RD';
|
|
my $ipmitrys = 3;
|
|
my $ipmitimeout = 2;
|
|
my $ipmimaxp = 64;
|
|
my $sitetab = xCAT::Table->new('site');
|
|
my $ipmitab = xCAT::Table->new('ipmi');
|
|
my $tmp;
|
|
if ($sitetab) {
|
|
($tmp)=$sitetab->getAttribs({'key'=>'ipmimaxp'},'value');
|
|
if (defined($tmp)) { $ipmimaxp=$tmp->{value}; }
|
|
($tmp)=$sitetab->getAttribs({'key'=>'ipmitimeout'},'value');
|
|
if (defined($tmp)) { $ipmitimeout=$tmp->{value}; }
|
|
($tmp)=$sitetab->getAttribs({'key'=>'ipmiretries'},'value');
|
|
if (defined($tmp)) { $ipmitrys=$tmp->{value}; }
|
|
($tmp)=$sitetab->getAttribs({'key'=>'ipmisdrcache'},'value');
|
|
if (defined($tmp)) { $enable_cache=$tmp->{value}; }
|
|
}
|
|
my $passtab = xCAT::Table->new('passwd');
|
|
if ($passtab) {
|
|
($tmp)=$passtab->getAttribs({'key'=>'ipmi'},'username','password');
|
|
if (defined($tmp)) {
|
|
$ipmiuser = $tmp->{username};
|
|
$ipmipass = $tmp->{password};
|
|
}
|
|
}
|
|
|
|
#my @threads;
|
|
my @donargs=();
|
|
if ($request->{command}->[0] =~ /fru/) {
|
|
my $vpdtab = xCAT::Table->new('vpd');
|
|
$vpdhash = $vpdtab->getNodesAttribs($noderange,[qw(serial mtm asset)]);
|
|
}
|
|
my $ipmihash = $ipmitab->getNodesAttribs($noderange,['bmc','username','password']) ;
|
|
foreach(@$noderange) {
|
|
my $node=$_;
|
|
my $nodeuser=$ipmiuser;
|
|
my $nodepass=$ipmipass;
|
|
my $nodeip = $node;
|
|
my $ent;
|
|
if (defined($ipmitab)) {
|
|
$ent=$ipmihash->{$node}->[0];
|
|
if (ref($ent) and defined $ent->{bmc}) { $nodeip = $ent->{bmc}; }
|
|
if (ref($ent) and defined $ent->{username}) { $nodeuser = $ent->{username}; }
|
|
if (ref($ent) and defined $ent->{password}) { $nodepass = $ent->{password}; }
|
|
}
|
|
push @donargs,[$node,$nodeip,$nodeuser,$nodepass];
|
|
}
|
|
if ($request->{command}->[0] eq "getipmicons") {
|
|
foreach (@donargs) {
|
|
getipmicons($_,$callback);
|
|
}
|
|
return;
|
|
}
|
|
|
|
#get new node status
|
|
my %oldnodestatus=(); #saves the old node status
|
|
my @allerrornodes=();
|
|
my $check=0;
|
|
my $global_check=1;
|
|
if ($sitetab) {
|
|
(my $ref) = $sitetab->getAttribs({key => 'nodestatus'}, 'value');
|
|
if ($ref) {
|
|
if ($ref->{value} =~ /0|n|N/) { $global_check=0; }
|
|
}
|
|
}
|
|
|
|
|
|
if ($command eq 'rpower') {
|
|
if (($global_check) && ($extrargs->[0] ne 'stat') && ($extrargs->[0] ne 'status') && ($extrargs->[0] ne 'state')) {
|
|
$check=1;
|
|
my @allnodes=();
|
|
foreach (@donargs) { push(@allnodes, $_->[0]); }
|
|
|
|
#save the old status
|
|
my $nodelisttab = xCAT::Table->new('nodelist');
|
|
if ($nodelisttab) {
|
|
my $tabdata = $nodelisttab->getNodesAttribs(\@allnodes, ['node', 'status']);
|
|
foreach my $node (@allnodes)
|
|
{
|
|
my $tmp1 = $tabdata->{$node}->[0];
|
|
if ($tmp1) {
|
|
if ($tmp1->{status}) { $oldnodestatus{$node}=$tmp1->{status}; }
|
|
else { $oldnodestatus{$node}=""; }
|
|
}
|
|
}
|
|
}
|
|
#print "oldstatus:" . Dumper(\%oldnodestatus);
|
|
|
|
#set the new status to the nodelist.status
|
|
my %newnodestatus=();
|
|
my $newstat;
|
|
if (($extrargs->[0] eq 'off') || ($extrargs->[0] eq 'softoff')) {
|
|
my $newstat=$::STATUS_POWERING_OFF;
|
|
$newnodestatus{$newstat}=\@allnodes;
|
|
} else {
|
|
#get the current nodeset stat
|
|
if (@allnodes>0) {
|
|
my $nsh={};
|
|
my ($ret, $msg)=xCAT::SvrUtils->getNodesetStates(\@allnodes, $nsh);
|
|
if (!$ret) {
|
|
foreach (keys %$nsh) {
|
|
my $newstat=xCAT_monitoring::monitorctrl->getNodeStatusFromNodesetState($_, "rpower");
|
|
$newnodestatus{$newstat}=$nsh->{$_};
|
|
}
|
|
} else {
|
|
$callback->({data=>$msg});
|
|
}
|
|
}
|
|
}
|
|
#print "newstatus" . Dumper(\%newnodestatus);
|
|
xCAT_monitoring::monitorctrl::setNodeStatusAttributes(\%newnodestatus, 1);
|
|
}
|
|
}
|
|
|
|
my $children = 0;
|
|
$SIG{CHLD} = sub {my $kpid; do { $kpid = waitpid(-1, WNOHANG); if ($kpid > 0) { delete $bmc_comm_pids{$kpid}; $children--; } } while $kpid > 0; };
|
|
my $sub_fds = new IO::Select;
|
|
foreach (@donargs) {
|
|
while ($children > $ipmimaxp) {
|
|
my $handlednodes={};
|
|
forward_data($callback,$sub_fds,$handlednodes);
|
|
#update the node status to the nodelist.status table
|
|
if ($check) {
|
|
updateNodeStatus($handlednodes, \@allerrornodes);
|
|
}
|
|
}
|
|
$children++;
|
|
my $cfd;
|
|
my $pfd;
|
|
socketpair($pfd, $cfd,AF_UNIX,SOCK_STREAM,PF_UNSPEC) or die "socketpair: $!";
|
|
$cfd->autoflush(1);
|
|
$pfd->autoflush(1);
|
|
my $child = xCAT::Utils->xfork();
|
|
unless (defined $child) { die "Fork failed" };
|
|
if ($child == 0) {
|
|
close($cfd);
|
|
my $rrc=donode($pfd,$_->[0],$_->[1],$_->[2],$_->[3],$ipmitimeout,$ipmitrys,$command,-args=>\@exargs);
|
|
close($pfd);
|
|
exit(0);
|
|
}
|
|
$bmc_comm_pids{$child}=1;
|
|
close ($pfd);
|
|
$sub_fds->add($cfd)
|
|
}
|
|
while ($sub_fds->count > 0 and $children > 0) {
|
|
my $handlednodes={};
|
|
forward_data($callback,$sub_fds,$handlednodes);
|
|
#update the node status to the nodelist.status table
|
|
if ($check) {
|
|
updateNodeStatus($handlednodes, \@allerrornodes);
|
|
}
|
|
}
|
|
|
|
#Make sure they get drained, this probably is overkill but shouldn't hurt
|
|
my $rc=1;
|
|
while ( $rc>0 ) {
|
|
my $handlednodes={};
|
|
$rc=forward_data($callback,$sub_fds,$handlednodes);
|
|
#update the node status to the nodelist.status table
|
|
if ($check) {
|
|
updateNodeStatus($handlednodes, \@allerrornodes);
|
|
}
|
|
}
|
|
|
|
if ($check) {
|
|
#print "allerrornodes=@allerrornodes\n";
|
|
#revert the status back for there is no-op for the nodes
|
|
my %old=();
|
|
foreach my $node (@allerrornodes) {
|
|
my $stat=$oldnodestatus{$node};
|
|
if (exists($old{$stat})) {
|
|
my $pa=$old{$stat};
|
|
push(@$pa, $node);
|
|
}
|
|
else {
|
|
$old{$stat}=[$node];
|
|
}
|
|
}
|
|
xCAT_monitoring::monitorctrl::setNodeStatusAttributes(\%old, 1);
|
|
}
|
|
}
|
|
|
|
sub updateNodeStatus {
|
|
my $handlednodes=shift;
|
|
my $allerrornodes=shift;
|
|
foreach my $node (keys(%$handlednodes)) {
|
|
if ($handlednodes->{$node} == -1) { push(@$allerrornodes, $node); }
|
|
}
|
|
}
|
|
|
|
|
|
|
|
sub forward_data { #unserialize data from pipe, chunk at a time, use magic to determine end of data structure
|
|
my $callback = shift;
|
|
my $fds = shift;
|
|
my $errornodes=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) {
|
|
#save the nodes that has errors and the ones that has no-op for use by the node status monitoring
|
|
my $no_op=0;
|
|
if (exists($_->{node}->[0]->{errorcode})) { $no_op=1; }
|
|
else {
|
|
my $text=$_->{node}->[0]->{data}->[0]->{contents}->[0];
|
|
#print "data:$text\n";
|
|
if (($text) && ($text =~ /$status_noop/)) {
|
|
$no_op=1;
|
|
#remove the symbols that meant for use by node status
|
|
$_->{node}->[0]->{data}->[0]->{contents}->[0] =~ s/ $status_noop//;
|
|
}
|
|
}
|
|
#print "data:". $_->{node}->[0]->{data}->[0]->{contents}->[0] . "\n";
|
|
if ($no_op) {
|
|
if ($errornodes) { $errornodes->{$_->{node}->[0]->{name}->[0]}=-1; }
|
|
} else {
|
|
if ($errornodes) { $errornodes->{$_->{node}->[0]->{name}->[0]}=1; }
|
|
}
|
|
$callback->($_);
|
|
}
|
|
} else {
|
|
$fds->remove($rfh);
|
|
close($rfh);
|
|
}
|
|
}
|
|
yield; #Avoid useless loop iterations by giving children a chance to fill pipes
|
|
return $rc;
|
|
}
|
|
|
|
sub donode {
|
|
$outfd = shift;
|
|
my $node = shift;
|
|
$currnode=$node;
|
|
my $bmcip = shift;
|
|
my $user = shift;
|
|
my $pass = shift;
|
|
my $timeout = shift;
|
|
my $retries = shift;
|
|
my $command = shift;
|
|
my %namedargs=@_;
|
|
my $transid = $namedargs{-transid};
|
|
my $extra=$namedargs{-args};
|
|
my @exargs=@$extra;
|
|
my ($rc,@output) = ipmicmd($bmcip,623,$user,$pass,$timeout,$retries,0,$command,@exargs);
|
|
my @outhashes;
|
|
sendoutput($rc,@output);
|
|
yield;
|
|
#my $msgtoparent=freeze(\@outhashes);
|
|
# print $outfd $msgtoparent;
|
|
return $rc;
|
|
}
|
|
|
|
sub sendoutput {
|
|
my $rc=shift;
|
|
foreach (@_) {
|
|
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]->{name}->[0]=$currnode;
|
|
if ($rc) {
|
|
$output{node}->[0]->{errorcode}=[$rc];
|
|
$output{node}->[0]->{error}->[0]=$text;
|
|
} else {
|
|
$output{node}->[0]->{data}->[0]->{contents}->[0]=$text;
|
|
}
|
|
#push @outhashes,\%output; #Save everything for the end, don't know how to be slicker with Storable and a pipe
|
|
print $outfd freeze([\%output]);
|
|
print $outfd "\nENDOFFREEZE6sK4ci\n";
|
|
yield;
|
|
waitforack($outfd);
|
|
}
|
|
}
|
|
|
|
1;
|