mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-31 03:12:30 +00:00 
			
		
		
		
	git-svn-id: https://svn.code.sf.net/p/xcat/code/xcat-core/trunk@5508 8638fb3e-16cb-4fca-ae20-7b5d299a9bcd
		
			
				
	
	
		
			5532 lines
		
	
	
		
			164 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			5532 lines
		
	
	
		
			164 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| #!/usr/bin/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 xCAT::IPMI;
 | |
| 
 | |
| 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 $iem_support;
 | |
| my $vpdhash;
 | |
| my %allerrornodes=();
 | |
| 
 | |
| eval {
 | |
|     require IBM::EnergyManager;
 | |
|     $iem_support=1;
 | |
| };
 | |
| 
 | |
| require Exporter;
 | |
| our @ISA = qw(Exporter);
 | |
| our @EXPORT = qw(
 | |
| 	ipmicmd
 | |
| );
 | |
| 
 | |
| sub handled_commands {
 | |
|   return {
 | |
|     rpower => 'nodehm:power,mgt', #done
 | |
|     renergy => 'nodehm:power,mgt',
 | |
|     getipmicons => 'ipmi', #done
 | |
|     rspconfig => 'nodehm:mgt', #done
 | |
|     rspreset => 'nodehm:mgt', #done
 | |
|     rvitals => 'nodehm:mgt', #done
 | |
|     rinv => 'nodehm:mgt', #done
 | |
|     rsetboot => 'nodehm:mgt', #done
 | |
|     rbeacon => 'nodehm:mgt', #done
 | |
|     reventlog => 'nodehm:mgt',
 | |
| #    rfrurewrite => 'nodehm:mgt', #deferred, doesn't even work on several models, no one asks about it, keeping it commented for future requests
 | |
|     getrvidparms => 'nodehm:mgt' #done
 | |
|   }
 | |
| }
 | |
| 
 | |
|     
 | |
| #use Data::Dumper;
 | |
| use POSIX "WNOHANG";
 | |
| use IO::Handle;
 | |
| use IO::Socket;
 | |
| use IO::Select;
 | |
| use Class::Struct;
 | |
| use Digest::MD5 qw(md5);
 | |
| use Digest::SHA1 qw(sha1);
 | |
| use POSIX qw(WNOHANG mkfifo strftime);
 | |
| use Fcntl qw(:flock);
 | |
| 
 | |
| 
 | |
| #local to module
 | |
| my $callback;
 | |
| my $ipmi_bmcipaddr;
 | |
| my $timeout;
 | |
| my $port;
 | |
| my $debug;
 | |
| my $ndebug = 0;
 | |
| my @cmdargv;
 | |
| my $sock;
 | |
| my $noclose;
 | |
| my %sessiondata; #hold per session variables, in preparation for single-process strategy
 | |
| my %pendingtransactions; #list of peers with callbacks, callback arguments, and timer expiry data
 | |
| 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 = 3;
 | |
| my %sdr_caches; #store sdr cachecs in memory indexed such that identical nodes do not hit the disk multiple times
 | |
| 
 | |
| #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",
 | |
| );
 | |
| 
 | |
| #Payload types:
 | |
| #  0 => IPMI  (format 1 0)
 | |
| #  1 => SOL 1 0
 | |
| #  0x10 => rmcp+ open req 1 0
 | |
| #  0x11 => rmcp+ response 1 0
 | |
| #  0x12 => rakp1 (all 1 0)
 | |
| #  0x13 => rakp2
 | |
| #  0x14 => rakp3
 | |
| #  0x15 => rakp4
 | |
|   
 | |
| 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 => {
 | |
| 	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(60)) { # Continue after 60 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 ipmicmd {
 | |
|     my $sessdata = shift;
 | |
| 
 | |
| 
 | |
| 	my $rc=0;
 | |
| 	my $text="";
 | |
| 	my $error="";
 | |
| 	my @output;
 | |
| 	my $noclose=0;
 | |
| 
 | |
|     $sessdata->{ipmisession}->login(callback=>\&on_bmc_connect,callback_args=>$sessdata);
 | |
| }
 | |
| 
 | |
| sub on_bmc_connect {
 | |
|     my $status = shift;
 | |
|     my $sessdata = shift;
 | |
| 	my $command = $sessdata->{command};
 | |
|     @cmdargv = @{$sessdata->{extraargs}};
 | |
|     if ($status =~ /ERROR:/) {
 | |
|         sendmsg([1,$status],$sessdata->{node});
 | |
|         return;
 | |
|     }
 | |
|     #ok, detect some common prereqs here, notably:
 | |
|     #getdevid
 | |
|     if ($command eq "getrvidparms") {
 | |
|         unless (defined $sessdata->{device_id}) {
 | |
|             $sessdata->{ipmisession}->subcmd(netfn=>6,command=>1,data=>[],callback=>\&gotdevid,callback_args=>$sessdata);
 | |
| 	    return;
 | |
|         }
 | |
|         getrvidparms($sessdata);
 | |
|     }
 | |
|     #initsdr
 | |
|     if ($command eq "rinv" or $command eq "reventlog" or $command eq "rvitals") {
 | |
|         unless (defined $sessdata->{device_id}) {
 | |
|             $sessdata->{ipmisession}->subcmd(netfn=>6,command=>1,data=>[],callback=>\&gotdevid,callback_args=>$sessdata);
 | |
|             return;
 | |
|         }
 | |
|         unless ($sessdata->{sdr_hash}) {
 | |
|             initsdr($sessdata);
 | |
| 	    return;
 | |
|         }
 | |
|     }
 | |
| 	if($command eq "ping") {
 | |
| 		sendmsg("ping",$sessdata->{node});
 | |
| 		return;
 | |
| 	}
 | |
| 	if ($command eq "rpower") {
 | |
| 		return power($sessdata);
 | |
| 	} elsif ($command eq "rspreset") {
 | |
|         return resetbmc($sessdata);
 | |
|     } elsif($command eq "rbeacon") {
 | |
| 		return beacon($sessdata);
 | |
| 	} elsif($command eq "rsetboot") {
 | |
| 		return setboot($sessdata);
 | |
| 	} elsif($command eq "rspconfig") {
 | |
|        shift @{$sessdata->{extraargs}};
 | |
|        if ($sessdata->{subcommand} =~ /=/) {
 | |
|            setnetinfo($sessdata);
 | |
|        } else {
 | |
|            getnetinfo($sessdata);
 | |
|        }
 | |
| 	} elsif($command eq "rvitals") {
 | |
|         vitals($sessdata);
 | |
| 	} elsif($command eq "rinv") {
 | |
|         inv($sessdata);
 | |
|     } elsif($command eq "reventlog") {
 | |
|         eventlog($sessdata);
 | |
|     } elsif($command eq "renergy") {
 | |
|         renergy($sessdata);
 | |
|     }
 | |
|     return;
 | |
|     my @output;
 | |
| 
 | |
|     my $rc; #in for testing, evaluated as a TODO
 | |
|     my $text;
 | |
|     my $error;
 | |
|     my $node;
 | |
| 	my $subcommand = "";
 | |
| 	if($command eq "rvitals") {
 | |
| 		($rc,@output) = vitals($subcommand);
 | |
| 	}
 | |
| 	elsif($command eq "renergy") {
 | |
| 		($rc,@output) = renergy($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 "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 "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($text) {
 | |
| 		push(@output,$text);
 | |
| 	}
 | |
| 
 | |
| 	return($rc,@output);
 | |
| }
 | |
| 
 | |
| sub resetbmc {
 | |
|     my $sessdata = shift;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>6,command=>2,data=>[],callback=>\&resetedbmc,callback_args=>$sessdata);
 | |
| }
 | |
| sub resetedbmc {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
| 	if ($rsp->{error}) {
 | |
|         sendmsg([1,$rsp->{error}],$sessdata->{node});
 | |
| 	} else {
 | |
|         if ($rsp->{code}) {
 | |
|             if ($codes{$rsp->{code}}) {
 | |
|                 sendmsg([1,$codes{$rsp->{code}}],$sessdata->{node});
 | |
|             } else {
 | |
|                 sendmsg([1,sprintf("Unknown error %02xh",$rsp->{code})],$sessdata->{node});
 | |
|             }
 | |
|             return;
 | |
|         } 
 | |
|         sendmsg("BMC reset",$sessdata->{node});
 | |
|         $sessdata->{ipmisession} = undef; #throw away now unusable session
 | |
| 	}
 | |
| }
 | |
| 
 | |
| sub setnetinfo {
 | |
|     my $sessdata = shift;
 | |
| 	my $subcommand = $sessdata->{subcommand};
 | |
|    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;
 | |
|     my $channel_number = $sessdata->{ipmisession}->{currentchannel};
 | |
| 
 | |
| 	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);
 | |
|         $dstip = inet_ntoa(inet_aton($dstip));
 | |
| 		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");
 | |
| 	}
 | |
|     my $command = shift @cmd;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0x0c,command=>$command,data=>\@cmd,callback=>\&netinfo_set,callback_args=>$sessdata);
 | |
| }
 | |
| sub netinfo_set {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     if ($rsp->{error}) { 
 | |
|         sendmsg([1,$rsp->{error}],$sessdata->{node});
 | |
|         return;
 | |
|     }
 | |
|     if ($rsp->{code}) {
 | |
|         if ($codes{$rsp->{code}}) {
 | |
|             sendmsg([1,$codes{$rsp->{code}}],$sessdata->{node});
 | |
|         } else {
 | |
|             sendmsg([1,sprintf("Unknown ipmi error %02xh",$rsp->{code})],$sessdata->{node});
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     getnetinfo($sessdata);
 | |
|     return;
 | |
| }
 | |
| 
 | |
| sub getnetinfo {
 | |
|     my $sessdata = shift;
 | |
| 	my $subcommand = $sessdata->{subcommand};
 | |
|     my $channel_number = $sessdata->{ipmisession}->{currentchannel};
 | |
|    $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,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 @cmd;
 | |
| 	my @returnd = ();
 | |
| 	my $error;
 | |
| 	my $rc = 0;
 | |
| 	my $text;
 | |
| 	my $code;
 | |
| 
 | |
| 	if ($subcommand eq "snmpdest") {
 | |
| 		$subcommand = "snmpdest1";
 | |
| 	}
 | |
| 
 | |
|     my $netfun = 0x0c;
 | |
|    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");
 | |
| 	}
 | |
| 
 | |
|     my $command = shift @cmd;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>$netfun,command=>$command,data=>\@cmd,callback=>\&getnetinfo_response,callback_args=>$sessdata);
 | |
| }
 | |
| sub getnetinfo_response {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my $subcommand = $sessdata->{subcommand};
 | |
|     $sessdata->{subcommand} = shift @{$sessdata->{extraargs}};
 | |
|     if ($rsp->{error}) { 
 | |
|         sendmsg([1,$rsp->{error}],$sessdata->{node});
 | |
|         return;
 | |
|     }
 | |
|     if ($rsp->{code}) {
 | |
|         if ($codes{$rsp->{code}}) {
 | |
|             sendmsg([1,$codes{$rsp->{code}}],$sessdata->{node});
 | |
|         } else {
 | |
|             sendmsg([1,sprintf("Unknown ipmi error %02xh",$rsp->{code})],$sessdata->{node});
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     if ($subcommand eq "snmpdest") {
 | |
|         $subcommand = "snmpdest1";
 | |
|     }
 | |
|     my @returnd = (0,@{$rsp->{data}});
 | |
| 	my $format = "%-25s";
 | |
| 	if($subcommand eq "garp") {
 | |
| 			my $code = $returnd[2] / 2;
 | |
| 				sendmsg(sprintf("$format %d","Gratuitous ARP seconds:",$code),$sessdata->{node});
 | |
| 	}
 | |
|     elsif($subcommand eq "alert") {
 | |
|         if ($returnd[3] & 0x8) { 
 | |
|            sendmsg("SP Alerting: enabled",$sessdata->{node});
 | |
|         } else {
 | |
|            sendmsg("SP Alerting: disabled",$sessdata->{node});
 | |
|         }
 | |
|      }
 | |
| 	elsif($subcommand =~ m/^snmpdest(\d+)/ ) {
 | |
| 			sendmsg(sprintf("$format %d.%d.%d.%d",
 | |
| 				"SP SNMP Destination $1:",
 | |
| 				$returnd[5],
 | |
| 				$returnd[6],
 | |
| 				$returnd[7],
 | |
| 				$returnd[8]),$sessdata->{node});
 | |
| 	} elsif($subcommand eq "ip") {
 | |
| 			sendmsg(sprintf("$format %d.%d.%d.%d",
 | |
| 				"BMC IP:",
 | |
| 				$returnd[2],
 | |
| 				$returnd[3],
 | |
| 				$returnd[4],
 | |
| 				$returnd[5]),$sessdata->{node});
 | |
| 	} elsif($subcommand eq "netmask") {
 | |
| 			sendmsg(sprintf("$format %d.%d.%d.%d",
 | |
| 				"BMC Netmask:",
 | |
| 				$returnd[2],
 | |
| 				$returnd[3],
 | |
| 				$returnd[4],
 | |
| 				$returnd[5]),$sessdata->{node});
 | |
| 	} elsif($subcommand eq "gateway") {
 | |
| 			sendmsg(sprintf("$format %d.%d.%d.%d",
 | |
| 				"BMC Gateway:",
 | |
| 				$returnd[2],
 | |
| 				$returnd[3],
 | |
| 				$returnd[4],
 | |
| 				$returnd[5]),$sessdata->{node});
 | |
| 	} elsif($subcommand eq "backupgateway") {
 | |
| 			sendmsg(sprintf("$format %d.%d.%d.%d",
 | |
| 				"BMC Backup Gateway:",
 | |
| 				$returnd[2],
 | |
| 				$returnd[3],
 | |
| 				$returnd[4],
 | |
| 				$returnd[5]),$sessdata->{node});
 | |
| 	} elsif ($subcommand eq "community") {
 | |
| 			my $text = sprintf("$format ","SP SNMP Community:");
 | |
| 			my $l = 2;
 | |
| 			while ($returnd[$l] ne 0) {
 | |
| 				$l = $l + 1;
 | |
| 			}
 | |
| 			my $i=2;
 | |
| 			while ($i<$l) {
 | |
| 				$text = $text . sprintf("%c",$returnd[$i]);
 | |
| 				$i = $i + 1;
 | |
| 			}
 | |
|             sendmsg($text,$sessdata->{node});
 | |
| 	}
 | |
|     if ($sessdata->{subcommand}) {
 | |
|         if ($sessdata->{subcommand} =~ /=/) {
 | |
|             setnetinfo($sessdata);
 | |
|         } else {
 | |
|             getnetinfo($sessdata);
 | |
|         }
 | |
|     }
 | |
|     return;
 | |
| }
 | |
| 
 | |
| sub setboot {
 | |
|     my $sessdata = shift;
 | |
|     #This disables the 60 second timer
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0,command=>8,data=>[3,8],callback=>\&setboot_timerdisabled,callback_args=>$sessdata);
 | |
| }
 | |
| sub setboot_timerdisabled {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my $subcommand=$sessdata->{subcommand};
 | |
|     if ($rsp->{error}) { 
 | |
|         sendmsg([1,$rsp->{error}],$sessdata->{node});
 | |
|         return;
 | |
|     }
 | |
|     if ($rsp->{code}) {
 | |
|         if ($codes{$rsp->{code}}) {
 | |
|             sendmsg([1,$codes{$rsp->{code}}],$sessdata->{node});
 | |
|         } else {
 | |
|             sendmsg([1,sprintf("Unknown ipmi error %02xh",$rsp->{code})],$sessdata->{node});
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     my $error;
 | |
| 
 | |
|     my @cmd;
 | |
|     if ($subcommand eq "net") {
 | |
|         @cmd=(0x5,0x80,0x4,0x0,0x0,0x0);
 | |
|     }
 | |
|     elsif ($subcommand eq "hd" ) {
 | |
|         @cmd=(0x5,0x80,0x8,0x0,0x0,0x0);
 | |
|     }
 | |
|     elsif ($subcommand eq "cd" ) {
 | |
|         @cmd=(0x5,0x80,0x14,0x0,0x0,0x0);
 | |
|     }
 | |
|     elsif ($subcommand eq "floppy" ) {
 | |
|         @cmd=(0x5,0x80,0x3c,0x0,0x0,0x0);
 | |
|     }
 | |
|     elsif ($subcommand =~ m/^def/) {
 | |
|         @cmd=(0x5,0x0,0x0,0x0,0x0,0x0);
 | |
|     }
 | |
|     elsif ($subcommand eq "setup" ) { #Not supported by BMCs I've checked so far..
 | |
|         @cmd=(0x5,0x18,0x0,0x0,0x0,0x0);
 | |
|     }
 | |
|     elsif ($subcommand =~ m/^stat/) {
 | |
|         setboot_stat("NOQUERY",$sessdata);
 | |
|         return;
 | |
|     }
 | |
|     else {
 | |
|         sendmsg([1,"unsupported command setboot $subcommand"],$sessdata->{node});
 | |
|     }
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0,command=>8,data=>\@cmd,callback=>\&setboot_stat,callback_args=>$sessdata);
 | |
| }
 | |
| sub setboot_stat {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     if (ref $rsp) {
 | |
|         if ($rsp->{error}) { sendmsg([1,$rsp->{error}],$sessdata->{node}); }
 | |
|         elsif ($rsp->{code}) {
 | |
|         if ($codes{$rsp->{code}}) {
 | |
|             sendmsg([1,$codes{$rsp->{code}}],$sessdata->{node});
 | |
|         } else {
 | |
|             sendmsg([1,sprintf("Unknown ipmi error %02xh",$rsp->{code})],$sessdata->{node});
 | |
|         }
 | |
|         return;
 | |
|         }
 | |
|     }
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0,command=>9,data=>[5,0,0],callback=>\&setboot_gotstat,callback_args=>$sessdata);
 | |
| }
 | |
| sub setboot_gotstat {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     if ($rsp->{error}) { sendmsg([1,$rsp->{error}],$sessdata->{node}); }
 | |
|     elsif ($rsp->{code}) {
 | |
|     if ($codes{$rsp->{code}}) {
 | |
|         sendmsg([1,$codes{$rsp->{code}}],$sessdata->{node});
 | |
|     } else {
 | |
|         sendmsg([1,sprintf("Unknown ipmi error %02xh",$rsp->{code})],$sessdata->{node});
 | |
|     }
 | |
|     return;
 | |
|     }
 | |
|     my %bootchoices = (
 | |
|         0 => 'BIOS default',
 | |
|         1 => 'Network',
 | |
|         2 => 'Hard Drive',
 | |
|         5 => 'CD/DVD',
 | |
|         6 => 'BIOS Setup',
 | |
|         15 => 'Floppy'
 | |
|     );
 | |
|     my @returnd = ($rsp->{code},@{$rsp->{data}});
 | |
|     unless ($returnd[3] & 0x80) {
 | |
|         sendmsg("boot override inactive",$sessdata->{node});
 | |
|         return;
 | |
|     }
 | |
|     my $boot=($returnd[4] & 0x3C) >> 2;
 | |
|     sendmsg($bootchoices{$boot},$sessdata->{node});
 | |
|     return;
 | |
| }
 | |
| 
 | |
| 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 $sessdata = shift;
 | |
|     unless ($sessdata) { die "not fixed yet" }
 | |
| #check devide id
 | |
|     unless ($sessdata->{mfg_id} == 2) { #Only implemented for IBM servers
 | |
|         sendmsg([1,"Remote video is not supported on this system"],$sessdata->{node});
 | |
|         return;
 | |
|     }
 | |
|     #TODO: use get bmc capabilities to see if rvid is actually supported before bothering the client java app
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0x50,data=>[],callback=>\&getrvidparms_with_buildid,callback_args=>$sessdata);
 | |
| }
 | |
| sub check_rsp_errors { #TODO: pass in command-specfic error code translation table
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
| 	if($rsp->{error}) { #non ipmi error
 | |
|         sendmsg([1,$rsp->{error}],$sessdata->{node});
 | |
|         return 1;
 | |
| 	}
 | |
|     if ($rsp->{code}) { #ipmi error
 | |
|         if ($codes{$rsp->{code}}) {
 | |
|             sendmsg([1,$codes{$rsp->{code}}]);
 | |
|         } else {
 | |
|              sendmsg([1,sprintf("Unknown error code %02xh",$rsp->{code})],$sessdata->{node});
 | |
|         }
 | |
|         return 1;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| sub getrvidparms_with_buildid {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @build_id = (0,@{$rsp->{data}});
 | |
|     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
 | |
|         sendmsg([1,"Remote video is not supported on this system"],$sessdata->{node});
 | |
|         return;
 | |
|     }
 | |
|     #wvid should be a possiblity, time to do the http...
 | |
|     my $browser = LWP::UserAgent->new();
 | |
|     my $message = $sessdata->{ipmisession}->{userid}.",".$sessdata->{ipmisession}->{password};
 | |
|     $browser->cookie_jar({});
 | |
|     my $baseurl = "http://".$sessdata->{ipmisession}->{bmc}."/";
 | |
|     my $response = $browser->request(POST $baseurl."/session/create",'Content-Type'=>"text/xml",Content=>$message);
 | |
|     unless ($response->content eq "ok") {
 | |
|         sendmsg ([1,"Server returned unexpected data"],$sessdata->{node});
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     $response = $browser->request(GET $baseurl."/page/session.html"); #we don't care, but some firmware is confused if we don't
 | |
|     $response = $browser->request(GET $baseurl."/kvm/kvm/jnlp");
 | |
|     my $jnlp = $response->content;
 | |
|     if ($jnlp =~ /This advanced option requires the purchase and installation/) {
 | |
|         sendmsg ([1,"Node does not have feature key for remote video"],$sessdata->{node});
 | |
|     }
 | |
|     my $currnode = $sessdata->{node};
 | |
|     $jnlp =~ s!argument>title=.*Video Viewer</argument>!argument>title=$currnode wvid</argument>!;
 | |
|     sendmsg("method:imm",$sessdata->{node});
 | |
|     sendmsg("jnlp:$jnlp",$sessdata->{node});
 | |
|     my @cmdargv = @{$sessdata->{extraargs}};
 | |
|     if (grep /-m/,@cmdargv) {
 | |
|         $response = $browser->request(GET $baseurl."/kvm/vm/jnlp");
 | |
|         sendmsg("mediajnlp:".$response->content,$sessdata->{node});;
 | |
|     }
 | |
|     return;
 | |
| }
 | |
| 
 | |
| 
 | |
| sub power {
 | |
| 	my $sessdata = shift;
 | |
| 
 | |
| 	my $netfun = 0x00;
 | |
| 	my @cmd;
 | |
| 	my @returnd = ();
 | |
| 	my $error;
 | |
| 	my $rc = 0;
 | |
| 	my $text;
 | |
| 	my $code;
 | |
| 	$sessdata->{ipmisession}->subcmd(netfn=>0,command=>1,data=>[],callback=>\&power_with_context,callback_args=>$sessdata);
 | |
| }
 | |
| sub power_with_context {
 | |
| 	my $rsp = shift;
 | |
| 	my $sessdata = shift;
 | |
| 	my $text="";
 | |
| 	if ($rsp->{error}) {
 | |
| 		sendmsg([1,$rsp->{error}],$sessdata->{node});
 | |
| 		return;
 | |
| 	}
 | |
| 	if ($rsp->{code} != 0) {
 | |
| 		$text = $codes{$rsp->{code}};
 | |
| 		unless ($text) { $text = sprintf("Unknown error code %02xh",$rsp->{code}); }
 | |
| 		sendmsg([1,$text],$sessdata->{node});
 | |
| 		return;
 | |
| 	}
 | |
| 	$sessdata->{powerstatus} = ($rsp->{data}->[0] & 1 ? "on" : "off");
 | |
| 	if ($sessdata->{subcommand} eq "stat" or $sessdata->{subcommand} eq "state" or $sessdata->{subcommand} eq "status") { 
 | |
|         if ($sessdata->{powerstatprefix}) {
 | |
| 		    sendmsg($sessdata->{powerstatprefix}.$sessdata->{powerstatus},$sessdata->{node});
 | |
|         } else {
 | |
| 		    sendmsg($sessdata->{powerstatus},$sessdata->{node});
 | |
|         }
 | |
|         if ($sessdata->{sensorstoread} and scalar @{$sessdata->{sensorstoread}}) { #if we are in an rvitals path, hook back into good graces
 | |
|             $sessdata->{currsdr} = shift @{$sessdata->{sensorstoread}};
 | |
|             readsensor($sessdata); #next
 | |
|         }
 | |
| 		return;
 | |
| 	}
 | |
| 	my $subcommand = $sessdata->{subcommand};
 | |
| 	if ($sessdata->{subcommand} eq "boot") {
 | |
| 		$text = $sessdata->{powerstatus}. " ";
 | |
| 		$subcommand = ($sessdata->{powerstatus} eq "on" ? "reset" : "on");
 | |
| 		$sessdata->{subcommand}=$subcommand; #lazy typing..
 | |
| 	}
 | |
| 	my %argmap = ( #english to ipmi dictionary
 | |
| 		"on" => 1,
 | |
| 		"off" => 0,
 | |
| 		"softoff" => 5,
 | |
| 		"reset" => 3,
 | |
| 		"nmi" => 4
 | |
| 		);
 | |
| 	if($subcommand eq "on") {
 | |
| 		if ($sessdata->{powerstatus} eq "on") {
 | |
| 			sendmsg("on",$sessdata->{node});
 | |
|             $allerrornodes{$sessdata->{node}}=1;
 | |
| 			return; # don't bother sending command
 | |
| 		}
 | |
| 	} elsif ($subcommand eq "softoff" or $subcommand eq "off" or $subcommand eq "reset") {
 | |
| 		if ($sessdata->{powerstatus} eq "off") {
 | |
| 			sendmsg("off",$sessdata->{node});
 | |
|             $allerrornodes{$sessdata->{node}}=1;
 | |
| 			return;
 | |
| 		}
 | |
| 	} elsif (not $argmap{$subcommand}) {
 | |
| 		sendmsg([1,"unsupported command power $subcommand"]);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	$sessdata->{ipmisession}->subcmd(netfn=>0,command=>2,data=>[$argmap{$subcommand}],callback=>\&power_response,callback_args=>$sessdata);
 | |
| }
 | |
| sub power_response { 
 | |
| 	my $rsp = shift;
 | |
| 	my $sessdata = shift;
 | |
| 	my @returnd = ($rsp->{code},@{$rsp->{data}});
 | |
| 	if($rsp->{error}) {
 | |
| 		sendmsg([1,$rsp->{error}],$sessdata->{node});
 | |
| 		return;
 | |
| 	}
 | |
| 	if ($rsp->{code}) {
 | |
| 		my $text = $codes{$rsp->{code}};
 | |
| 		unless ($text) { $text = sprintf("Unknown response %02xh",$rsp->{code}); }
 | |
| 		sendmsg([1,$text],$sessdata->{node});
 | |
| 	}
 | |
| 	sendmsg($sessdata->{subcommand},$sessdata->{node});
 | |
| }
 | |
| 
 | |
| 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[0];
 | |
| 
 | |
| 	if($code == 0x00) {
 | |
| 	}
 | |
| 	else {
 | |
| 		$rc = 1;
 | |
| 		$text = $codes{$code};
 | |
| 	}
 | |
| 
 | |
| 	printf("return code: 0x%02x\n\n",$code);
 | |
| 
 | |
| 	print "return data:\n";
 | |
| 	my @rdata = @returnd[1..@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 $sessdata = shift;
 | |
| 	my $subcommand = $sessdata->{subcommand};
 | |
|     my $ipmiv2=0;
 | |
|     if ($sessdata->{ipmisession}->{ipmiversion} eq '2.0') {
 | |
|         $ipmiv2 = 1;
 | |
|     }
 | |
| 	if($subcommand ne "on" and $subcommand ne "off"){
 | |
|                 sendmsg([1,"please specify on or off for ipmi nodes (stat impossible)"],$sessdata->{node});
 | |
|      }
 | |
| 
 | |
|     #if stuck with 1.5, say light for 255 seconds.  In 2.0, specify to turn it on forever
 | |
| 	if($subcommand eq "on") {
 | |
|         if ($ipmiv2) {
 | |
|             $sessdata->{ipmisession}->subcmd(netfn=>0,command=>4,data=>[0,1],callback=>\&beacon_answer,callback_args=>$sessdata);
 | |
|         } else {
 | |
|             $sessdata->{ipmisession}->subcmd(netfn=>0,command=>4,data=>[0xff],callback=>\&beacon_answer,callback_args=>$sessdata);
 | |
|         }
 | |
| 	} 
 | |
| 	elsif($subcommand eq "off") {
 | |
|         if ($ipmiv2) {
 | |
|             $sessdata->{ipmisession}->subcmd(netfn=>0,command=>4,data=>[0,0],callback=>\&beacon_answer,callback_args=>$sessdata);
 | |
|         } else {
 | |
|             $sessdata->{ipmisession}->subcmd(netfn=>0,command=>4,data=>[0x0],callback=>\&beacon_answer,callback_args=>$sessdata);
 | |
|         }
 | |
| 	}
 | |
| 	else {
 | |
|         return;
 | |
| 	}
 | |
| }
 | |
| sub beacon_answer {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
| 
 | |
| 	if($rsp->{error}) { #non ipmi error
 | |
|         sendmsg([1,$rsp->{error}],$sessdata->{node});
 | |
|         return;
 | |
| 	}
 | |
|     if ($rsp->{code}) { #ipmi error
 | |
|         if ($codes{$rsp->{code}}) {
 | |
|             sendmsg([1,$codes{$rsp->{code}}]);
 | |
|         } else {
 | |
|              sendmsg([1,sprintf("Unknown error code %02xh",$rsp->{code})],$sessdata->{node});
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     sendmsg($sessdata->{subcommand},$sessdata->{node});
 | |
| }
 | |
| 
 | |
| sub inv {
 | |
|     my $sessdata = shift;
 | |
| 	my $subcommand = $sessdata->{subcommand};
 | |
| 
 | |
| 	my $rc = 0;
 | |
| 	my $text;
 | |
| 	my @output;
 | |
| 	my @types;
 | |
| 
 | |
| 
 | |
|     unless ($subcommand) {
 | |
|         $subcommand = "all";
 | |
|     }
 | |
| 	if($subcommand eq "all") {
 | |
| 		@types = qw(model serial deviceid mprom guid misc hw asset firmware);
 | |
| 	}
 | |
| 	elsif($subcommand eq "asset") {
 | |
|         $sessdata->{skipotherfru}=1;
 | |
| 		@types = qw(asset);
 | |
| 	}
 | |
| 	elsif($subcommand eq "model") {
 | |
|         $sessdata->{skipotherfru}=1;
 | |
| 		@types = qw(model);
 | |
| 	}
 | |
| 	elsif($subcommand eq "serial") {
 | |
|         $sessdata->{skipotherfru}=1;
 | |
| 		@types = qw(serial);
 | |
| 	}
 | |
| 	elsif($subcommand eq "vpd") {
 | |
|         $sessdata->{skipotherfru}=1;
 | |
| 		@types = qw(model serial deviceid mprom);
 | |
| 	}
 | |
| 	elsif($subcommand eq "mprom") {
 | |
|         $sessdata->{skipfru}=1; #full fru read is expensive, skip it
 | |
| 		@types = qw(mprom);
 | |
| 	}
 | |
| 	elsif($subcommand eq "misc") {
 | |
|         $sessdata->{skipotherfru}=1;
 | |
| 		@types = qw(misc);
 | |
| 	}
 | |
| 	elsif($subcommand eq "deviceid") {
 | |
|         $sessdata->{skipfru}=1; #full fru read is expensive, skip it
 | |
| 		@types = qw(deviceid);
 | |
| 	}
 | |
| 	elsif($subcommand eq "guid") {
 | |
|         $sessdata->{skipfru}=1; #full fru read is expensive, skip it
 | |
| 		@types = qw(guid);
 | |
| 	}
 | |
| 	elsif($subcommand eq "uuid") {
 | |
|         $sessdata->{skipfru}=1; #full fru read is expensive, skip it
 | |
| 		@types = qw(guid);
 | |
| 	}
 | |
| 	else {
 | |
|         @types = ($subcommand);
 | |
| 		#return(1,"unsupported BMC inv argument $subcommand");
 | |
| 	}
 | |
|     $sessdata->{invtypes} = \@types;
 | |
| 	initfru($sessdata);
 | |
| }
 | |
| sub fru_initted {
 | |
|     my $sessdata = shift;
 | |
| 	my $key;
 | |
|     my @types = @{$sessdata->{invtypes}};
 | |
| 	my $format = "%-20s %s";
 | |
| 
 | |
| 	foreach $key (sort keys %{$sessdata->{fru_hash}}) {
 | |
| 		my $fru = $sessdata->{fru_hash}->{$key};
 | |
|         my $type;
 | |
|         foreach $type (split /,/,$fru->rec_type) {
 | |
|     		if(grep {$_ eq $type} @types) {
 | |
|     			sendmsg(sprintf($format,$sessdata->{fru_hash}->{$key}->desc . ":",$sessdata->{fru_hash}->{$key}->value),$sessdata->{node});
 | |
|                 last;
 | |
|             }
 | |
|         }
 | |
| 	}
 | |
| }
 | |
| 
 | |
| sub add_textual_fru {
 | |
|     my $parsedfru = shift;
 | |
|     my $description = shift;
 | |
|     my $category = shift;
 | |
|     my $subcategory = shift;
 | |
|     my $types = shift;
 | |
|     my $sessdata = 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}));
 | |
|                 }
 | |
|                     
 | |
|             }
 | |
|             $sessdata->{fru_hash}->{$sessdata->{frudex}} = $fru;
 | |
|             $sessdata->{frudex} += 1;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| sub add_textual_frus {
 | |
|     my $parsedfru = shift;
 | |
|     my $desc = shift;
 | |
|     my $categorydesc = shift;
 | |
|     my $category = shift;
 | |
|     my $type = shift;
 | |
|     my $sessdata = shift;
 | |
|     unless ($type) { $type = 'hw'; }
 | |
|     add_textual_fru($parsedfru,$desc." ".$categorydesc."Part Number",$category,"partnumber",$type,$sessdata);
 | |
|     add_textual_fru($parsedfru,$desc." ".$categorydesc."Manufacturer",$category,"manufacturer",$type,$sessdata);
 | |
|     add_textual_fru($parsedfru,$desc." ".$categorydesc."Serial Number",$category,"serialnumber",$type,$sessdata);
 | |
|     add_textual_fru($parsedfru,$desc." ".$categorydesc."",$category,"name",$type,$sessdata);
 | |
|     if ($parsedfru->{$category}->{builddate}) {
 | |
|         add_textual_fru($parsedfru,$desc." ".$categorydesc."Manufacture Date",$category,"builddate",$type,$sessdata);
 | |
|     }
 | |
|     if ($parsedfru->{$category}->{buildlocation}) {
 | |
|         add_textual_fru($parsedfru,$desc." ".$categorydesc."Manufacture Location",$category,"buildlocation",$type,$sessdata);
 | |
|     }
 | |
|     if ($parsedfru->{$category}->{model})  {
 | |
|         add_textual_fru($parsedfru,$desc." ".$categorydesc."Model",$category,"model",$type,$sessdata);
 | |
|     }
 | |
|     add_textual_fru($parsedfru,$desc." ".$categorydesc."Additional Info",$category,"extra",$type,$sessdata);
 | |
| }
 | |
| 
 | |
| sub initfru {
 | |
| 	my $netfun = 0x28;
 | |
|     my $sessdata = shift;
 | |
|     $sessdata->{fru_hash} = {};
 | |
| 
 | |
|     my $mfg_id = $sessdata->{mfg_id};
 | |
|     my $prod_id = $sessdata->{prod_id};
 | |
| 	my $device_id=$sessdata->{device_id};
 | |
| 
 | |
| 	my $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);
 | |
| 	$sessdata->{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);
 | |
| 	$sessdata->{fru_hash}->{prod_id} = $fru;
 | |
| 
 | |
| 	$fru = FRU->new();
 | |
| 	$fru->rec_type("deviceid");
 | |
| 	$fru->desc("Device ID");
 | |
| 	$fru->value($device_id);
 | |
| 	$sessdata->{fru_hash}->{device_id} = $fru;
 | |
| 
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0x6,command=>0x37,data=>[],callback=>\&gotguid,callback_args=>$sessdata);
 | |
| }
 | |
| sub got_bmc_fw_info {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
| 	my $fw_rev1=$sessdata->{firmware_rev1};
 | |
|     my $fw_rev2=$sessdata->{firmware_rev2};
 | |
|     my $mprom;
 | |
|     my $isanimm=0;
 | |
|     if (ref $rsp and not $rsp->{error} and not $rsp->{code}) { #I am a callback and the command worked
 | |
|         my @returnd = (@{$rsp->{data}});
 | |
| 			my @a = ($fw_rev2);
 | |
|             my $prefix = pack("C*",@returnd[0..3]);
 | |
|             if ($prefix =~ /yuoo/i) { #we have an imm
 | |
|                 $isanimm=1;
 | |
|             }
 | |
| 			$mprom = sprintf("%d.%s (%s)",$fw_rev1,decodebcd(\@a),getascii(@returnd));
 | |
| 	} else { #either not a callback or IBM call failed
 | |
| 		my @a = ($fw_rev2);
 | |
| 		$mprom = sprintf("%d.%s",$fw_rev1,decodebcd(\@a));
 | |
| 	}
 | |
|     my $fru = FRU->new();
 | |
|    	$fru->rec_type("mprom,firmware,bmc,imm");
 | |
|    	$fru->desc("BMC Firmware");
 | |
|    	$fru->value($mprom);
 | |
|    	$sessdata->{fru_hash}->{mprom} = $fru;
 | |
|     if ($isanimm) {
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xf0,data=>[0,0,0,0],callback=>\&get_uefi_version_with_fmapi,callback_args=>$sessdata);
 | |
|     } else {
 | |
|         initfru_with_mprom($sessdata);
 | |
|     }
 | |
| }
 | |
| sub get_uefi_version_with_fmapi {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @data = @{$rsp->{data}};
 | |
|     unless ($data[2] == 0 and $data[3] = 3 and $data[4]==0x12 and $data[5]==2) { #we support this major version only 
 | |
|         initfru_with_mprom($sessdata);
 | |
|         return;
 | |
|     }
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xf0,data=>[1,0,0,5,0x84,0x62,0x69,0x6f,0x73],callback=>\&get_uefi_version_with_xid,callback_args=>$sessdata);
 | |
| }
 | |
| 
 | |
| sub get_uefi_version_with_xid {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @data = @{$rsp->{data}};
 | |
|     if ($data[2] != 0 or $data[3] != 5 or $data[4] != 0x44) {
 | |
|         sendmsg([1,"Error1 retrieving UEFI build version"],$sessdata->{node});
 | |
|         return;
 | |
|     }
 | |
|     splice @data,0,5;
 | |
|     $sessdata->{fmapixid} = \@data;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xf0,data=>[3,0,0,0xa,0x44,@{$sessdata->{fmapixid}},0x84,0x62,0x69,0x6f,0x73],callback=>\&waitfor_openxid,callback_args=>$sessdata);
 | |
| }
 | |
| 
 | |
| sub waitfor_openxid {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @data = @{$rsp->{data}};
 | |
|     if ($data[2] != 0) {
 | |
|         sendmsg([1,"Error2 retrieving UEFI build version"],$sessdata->{node});
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xf0,data=>[0x4,0,0,0x05,0x44,@{$sessdata->{fmapixid}}],callback=>\&fmapi_xid_closed,callback_args=>$sessdata);
 | |
|         return;
 | |
|     }
 | |
|     if ((scalar @data) > 4 and $data[5] == 0x40) { #ready to proceed
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xf0,data=>[0xa,0,0,0xf,0x44,@{$sessdata->{fmapixid}},0x87,0x62,0x75,0x69,0x6C,0x64,0x69,0x64,0x11,0x52],callback=>\&got_uefi_buildid,callback_args=>$sessdata);
 | |
|     } else {
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xf0,data=>[0x9,0,0,0x5,0x44,@{$sessdata->{fmapixid}}],callback=>\&waitfor_openxid,callback_args=>$sessdata);
 | |
|     }
 | |
| }
 | |
| sub got_uefi_buildid {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @data = @{$rsp->{data}};
 | |
|     if ($data[2] != 0) {
 | |
|         sendmsg([1,"Error3 retrieving UEFI build version"],$sessdata->{node});
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xf0,data=>[0x4,0,0,0x05,0x44,@{$sessdata->{fmapixid}}],callback=>\&fmapi_xid_closed,callback_args=>$sessdata);
 | |
|         return;
 | |
|     }
 | |
|     my $buildsize = $data[4]&0x7f;
 | |
|     my @buildid = splice @data,5,$buildsize;
 | |
|     $sessdata->{biosbuildid} = pack("C*",@buildid);
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xf0,data=>[0xa,0,0,0x14,0x44,@{$sessdata->{fmapixid}},0x8c,0x62,0x75,0x69,0x6C,0x64,0x76,0x65,0x72,0x73,0x69,0x6F,0x6E,0x11,0x52],callback=>\&got_uefi_buildversion,callback_args=>$sessdata);
 | |
| }
 | |
| sub got_uefi_buildversion {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @data = @{$rsp->{data}};
 | |
|     if ($data[2] != 0) {
 | |
|         sendmsg([1,"Error4 retrieving UEFI build version"],$sessdata->{node});
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xf0,data=>[0x4,0,0,0x05,0x44,@{$sessdata->{fmapixid}}],callback=>\&fmapi_xid_closed,callback_args=>$sessdata);
 | |
|         return;
 | |
|     }
 | |
|     my $buildsize = $data[4]&0x7f;
 | |
|     my @buildid = splice @data,5,$buildsize;
 | |
|     $sessdata->{biosbuildversion} = pack("C*",@buildid);
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xf0,data=>[0xa,0,0,0x11,0x44,@{$sessdata->{fmapixid}},0x89,0x62,0x75,0x69,0x6C,0x64,0x64,0x61,0x74,0x65,0x11,0x52],callback=>\&got_uefi_builddate,callback_args=>$sessdata);
 | |
| }
 | |
| sub got_uefi_builddate {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @data = @{$rsp->{data}};
 | |
|     if ($data[2] != 0) {
 | |
|         sendmsg([1,"Error5 retrieving UEFI build version"],$sessdata->{node});
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xf0,data=>[0x4,0,0,0x05,0x44,@{$sessdata->{fmapixid}}],callback=>\&fmapi_xid_closed,callback_args=>$sessdata);
 | |
|         return;
 | |
|     }
 | |
|     my $buildsize = $data[4]&0x7f;
 | |
|     my @buildid = splice @data,5,$buildsize;
 | |
|     $sessdata->{biosbuilddate} = pack("C*",@buildid);
 | |
|     my $bver = $sessdata->{biosbuildversion}." (".$sessdata->{biosbuildid}." ".$sessdata->{biosbuilddate}.")";
 | |
|     my $fru = FRU->new();
 | |
|    	$fru->rec_type("bios,uefi,firmware");
 | |
|    	$fru->desc("UEFI Version");
 | |
|    	$fru->value($bver);
 | |
|     $sessdata->{fru_hash}->{uefi} = $fru;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xf0,data=>[0x4,0,0,0x05,0x44,@{$sessdata->{fmapixid}}],callback=>\&fmapi_xid_closed,callback_args=>$sessdata);
 | |
| }
 | |
| sub fmapi_xid_closed {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata=shift;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xf0,data=>[0x2,0,0,0x05,0x44,@{$sessdata->{fmapixid}}],callback=>\&fmapi_xid_destroyed,callback_args=>$sessdata);
 | |
| }
 | |
| sub fmapi_xid_destroyed {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     initfru_with_mprom($sessdata);
 | |
| }
 | |
| 
 | |
| sub initfru_withguid {
 | |
|     my $sessdata = shift;
 | |
|     my $mfg_id = $sessdata->{mfg_id};
 | |
|     my $prod_id = $sessdata->{prod_id};
 | |
| 	my $mprom;
 | |
| 
 | |
| 	if($mfg_id == 2 && $prod_id != 34869) {
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0x50,data=>[],callback=>\&got_bmc_fw_info,callback_args=>$sessdata);
 | |
| 	} else {
 | |
|         got_bmc_fw_info(0,$sessdata);
 | |
|     }
 | |
| }
 | |
| sub initfru_with_mprom {
 | |
|     my $sessdata = shift;
 | |
|     if ($sessdata->{skipfru}) {
 | |
|         fru_initted($sessdata);
 | |
|         return;
 | |
|     }
 | |
|     $sessdata->{currfruid}=0;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0xa,command=>0x10,data=>[0],callback=>\&process_currfruid,callback_args=>$sessdata);
 | |
| }
 | |
| sub process_currfruid {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     if ($rsp->{code} == 0xcb) {
 | |
|             $sessdata->{currfrudata}="Not Present";
 | |
|             $sessdata->{currfrudone}=1;
 | |
|             add_fruhash($sessdata);
 | |
|             return;
 | |
|     }
 | |
|     if (check_rsp_errors($rsp,$sessdata)) {
 | |
|         return;
 | |
|     }
 | |
|     my @bytes =@{$rsp->{data}};
 | |
|     $sessdata->{currfrusize} = ($bytes[1]<<8)+$bytes[0];
 | |
|     readcurrfrudevice(0,$sessdata);
 | |
| }
 | |
| sub initfru_zero {
 | |
|     my $sessdata = shift;
 | |
|     my $fruhash = shift;
 | |
|     my $frudex=0;
 | |
|     my $fru;
 | |
|     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}));
 | |
|         }
 | |
|     	$sessdata->{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}));
 | |
|         }
 | |
|     	$sessdata->{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}));
 | |
|         }
 | |
|     	$sessdata->{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}));
 | |
|         }
 | |
|     	$sessdata->{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}));
 | |
|         }
 | |
|     	$sessdata->{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}));
 | |
|         }
 | |
|     	$sessdata->{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}));
 | |
|         }
 | |
|         $sessdata->{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}));
 | |
|         }
 | |
|     	$sessdata->{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}));
 | |
|         }
 | |
|     	$sessdata->{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}));
 | |
|         }
 | |
|         $sessdata->{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});
 | |
|         $sessdata->{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}));
 | |
|         }
 | |
|     	$sessdata->{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}));
 | |
|         }
 | |
|     	$sessdata->{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}));
 | |
|         }
 | |
|     	$sessdata->{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}));
 | |
|         }
 | |
|     	$sessdata->{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}));
 | |
|         }
 | |
|         $sessdata->{fru_hash}->{$frudex++} = $fru;
 | |
|     }
 | |
|     #Ok, done with fru 0, on to the other fru devices from SDR
 | |
|     $sessdata->{frudex} = $frudex;
 | |
|     if ($sessdata->{skipotherfru}) { #skip non-primary fru devices
 | |
|         fru_initted($sessdata);
 | |
|         return;
 | |
|     }
 | |
|     my $key;
 | |
|     my $subrc;
 | |
|     my %sdr_hash = %{$sessdata->{sdr_hash}};
 | |
|     $sessdata->{dimmfru} = [];
 | |
|     $sessdata->{genhwfru} = [];
 | |
|     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
 | |
|                 push @{$sessdata->{dimmfru}},$sdr;
 | |
|             } elsif ($sdr->fru_subtype == 0 or $sdr->fru_subtype == 2) {
 | |
|                 push @{$sessdata->{genhwfru}},$sdr;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|    if (scalar @{$sessdata->{dimmfru}}) {
 | |
|         $sessdata->{currfrusdr} = shift  @{$sessdata->{dimmfru}};
 | |
|         $sessdata->{currfruid} = $sessdata->{currfrusdr}->sensor_number;
 | |
|         $sessdata->{currfrutype}="dimm";
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0xa,command=>0x10,data=>[$sessdata->{currfruid}],callback=>\&process_currfruid,callback_args=>$sessdata);
 | |
|   } elsif (scalar @{$sessdata->{genhwfru}}) {
 | |
|         $sessdata->{currfrusdr} = shift  @{$sessdata->{genhwfru}};
 | |
|         $sessdata->{currfruid} = $sessdata->{currfrusdr}->sensor_number;
 | |
|         $sessdata->{currfrutype}="genhw";
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0xa,command=>0x10,data=>[$sessdata->{currfruid}],callback=>\&process_currfruid,callback_args=>$sessdata);
 | |
|   } else {
 | |
|       fru_initted($sessdata);
 | |
|   }
 | |
| }
 | |
| sub get_frusize {
 | |
|     my $fruid=shift;
 | |
|     my $netfun = 0x28; # Storage (0x0A << 2)
 | |
|     my @cmd=(0x10,$fruid);
 | |
| 	my @bytes;
 | |
|     my $error = docmd($netfun,\@cmd,\@bytes);
 | |
|     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 $sessdata = shift;
 | |
|     my $phash = shift; #Product hash
 | |
|     unless ($phash) { die "here" }
 | |
|     my $currnode = $sessdata->{node};
 | |
|     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 $sessdata = shift;
 | |
|     my $prod=shift;
 | |
|     mergefru($sessdata,$prod);
 | |
|     my $currnode = $sessdata->{node};
 | |
|     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[0];
 | |
| 
 | |
| 	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[1];
 | |
| 	my $fru_size_ms = $returnd[2];
 | |
| 	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 add_fruhash {
 | |
|     my $sessdata = shift;
 | |
|     my $fruhash;
 | |
|     if ($sessdata->{currfruid} !=0 and not ref $sessdata->{currfrudata}) {
 | |
|         my $fru = FRU->new();
 | |
|         if ($sessdata->{currfrutype} and $sessdata->{currfrutype} eq 'dimm') {
 | |
|             $fru->rec_type("dimm,hw");
 | |
|         } else {
 | |
|              $fru->rec_type("hw");
 | |
|         }
 | |
|         $fru->value($sessdata->{currfrudata});
 | |
|         $fru->desc($sessdata->{currfrusdr}->id_string);
 | |
|         $sessdata->{fru_hash}->{$sessdata->{frudex}} = $fru;
 | |
|         $sessdata->{frudex} += 1;
 | |
|     } elsif ($sessdata->{currfrutype} and $sessdata->{currfrutype} eq 'dimm') {
 | |
|         $fruhash = decode_spd(@{$sessdata->{currfrudata}});
 | |
|     } else {
 | |
|             my $err;
 | |
|             ($err,$fruhash) = parsefru($sessdata->{currfrudata});
 | |
|             if ($err) {
 | |
|                 sendmsg([1,"Error reading fru area $err".$sessdata->{currfruid}]);
 | |
|                 return;
 | |
|             }
 | |
|     }
 | |
|     if ($sessdata->{currfruid} == 0) {
 | |
|         initfru_zero($sessdata,$fruhash);
 | |
|         return;
 | |
|     } elsif (ref $sessdata->{currfrudata}) {
 | |
|         if ($sessdata->{currfrutype} and $sessdata->{currfrutype} eq 'dimm') {
 | |
|             add_textual_frus($fruhash,$sessdata->{currfrusdr}->id_string,"","product","dimm,hw",$sessdata);
 | |
|         } else {
 | |
|             add_textual_frus($fruhash,$sessdata->{currfrusdr}->id_string,"Board","board",undef,$sessdata);
 | |
|             add_textual_frus($fruhash,$sessdata->{currfrusdr}->id_string,"Product","product",undef,$sessdata);
 | |
|             add_textual_frus($fruhash,$sessdata->{currfrusdr}->id_string,"Chassis","chassis",undef,$sessdata);
 | |
|         }
 | |
|     }
 | |
|     if (scalar @{$sessdata->{dimmfru}}) {
 | |
|         $sessdata->{currfrusdr} = shift  @{$sessdata->{dimmfru}};
 | |
|         $sessdata->{currfruid} = $sessdata->{currfrusdr}->sensor_number;
 | |
|         $sessdata->{currfrutype}="dimm";
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0xa,command=>0x10,data=>[$sessdata->{currfruid}],callback=>\&process_currfruid,callback_args=>$sessdata);
 | |
|     } elsif (scalar @{$sessdata->{genhwfru}}) {
 | |
|         $sessdata->{currfrusdr} = shift  @{$sessdata->{genhwfru}};
 | |
|         $sessdata->{currfruid} = $sessdata->{currfrusdr}->sensor_number;
 | |
|         $sessdata->{currfrutype}="genhw";
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0xa,command=>0x10,data=>[$sessdata->{currfruid}],callback=>\&process_currfruid,callback_args=>$sessdata);
 | |
|     } else {
 | |
|         fru_initted($sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub readcurrfrudevice {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my $chunk=16; #we have no idea how much will be supported to grab at a time, stick to 16 as a magic number for the moment
 | |
|     if (not ref $rsp) {
 | |
|         $sessdata->{currfruoffset}=0;
 | |
|         $sessdata->{currfrudata}=[];
 | |
|         $sessdata->{currfrudone}=0;
 | |
|         $sessdata->{currfruchunk}=16;
 | |
|     } else {
 | |
|         if ($rsp->{code} != 0xcb and check_rsp_errors($rsp,$sessdata)) {
 | |
|             return;
 | |
|         } elsif ($rsp->{code} == 0xcb) {
 | |
|             $sessdata->{currfrudata}="Not Present";
 | |
|             $sessdata->{currfrudone}=1;
 | |
|             add_fruhash($sessdata);
 | |
|             return;
 | |
|         }
 | |
|         my @data = @{$rsp->{data}};
 | |
|         if ($data[0] != $sessdata->{currfruchunk}) {
 | |
|             sendmsg([1,"Received incorrect data from BMC"],$sessdata->{node});
 | |
|             return;
 | |
|         }
 | |
|         shift @data;
 | |
|         push @{$sessdata->{currfrudata}},@data;
 | |
|         if ($sessdata->{currfrudone}) {
 | |
|             add_fruhash($sessdata);
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     my $ms=$sessdata->{currfruoffset}>>8;
 | |
|     my $ls=$sessdata->{currfruoffset}&0xff;
 | |
|     if ($sessdata->{currfruoffset}+16  >= $sessdata->{currfrusize}) {
 | |
|         $chunk =  $sessdata->{currfrusize}-$sessdata->{currfruoffset}; # shrink chunk to only get the remainder data
 | |
|         $sessdata->{currfrudone}=1;
 | |
|     } else {
 | |
|         $sessdata->{currfruoffset}+=$chunk;
 | |
|     }
 | |
|     $sessdata->{currfruchunk}=$chunk;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0xa,command=>0x11,data=>[$sessdata->{currfruid},$ls,$ms,$chunk],callback=>\&readcurrfrudevice,callback_args=>$sessdata);
 | |
| }
 | |
| 
 | |
| 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;
 | |
|     if ($idx >= scalar @$area)  {
 | |
|         sendmsg([1,"Error parsing FRU data from BMC"]);
 | |
|         return 1,undef,undef;
 | |
|     }
 | |
|     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);
 | |
|     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[0];
 | |
| 
 | |
| 		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[1];
 | |
| 		if($count != $chunk) {
 | |
| 			$rc = 1;
 | |
| 			$text = "FRU write error (bytes requested: $chunk, wrote: $count)";
 | |
| 			return($rc,$text);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| sub decodealert {
 | |
|     my $sessdata = shift;
 | |
|   my $skip_sdrinit=0;
 | |
|     unless (ref $sessdata) { #called from xcat traphandler
 | |
|         $sessdata = { sdr_hash => {} };
 | |
|         $skip_sdrinit=1; #TODO sdr_init, cache only to avoid high trap handling overhead
 | |
|     }
 | |
|   my $trap = shift;
 | |
|   if ($trap =~ /xCAT_plugin::ipmi/) {
 | |
|     $trap=shift;
 | |
|     $skip_sdrinit=1;
 | |
|   }
 | |
| 	my $node = shift;
 | |
| 	my @pet = @_;
 | |
| 	my $rc;
 | |
| 	my $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);
 | |
|     my %sdr_hash = %{$sessdata->{sdr_hash}};
 | |
| 	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; }
 | |
|     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 $sessdata = shift;
 | |
| 	my $subcommand = $sessdata->{subcommand};
 | |
|     unless ($sessdata) { die "not fixed yet" }
 | |
| 
 | |
| 	my $netfun = 0x0a;
 | |
| 	my @cmd;
 | |
| 	my @returnd = ();
 | |
| 	my $error;
 | |
| 	my $rc = 0;
 | |
| 	my $text;
 | |
| 	my $code;
 | |
| 	my @output;
 | |
| 	my $entry;
 | |
|     $sessdata->{fullsel}=0;
 | |
| 	my @sel;
 | |
| 	my $mfg_id;
 | |
| 	my $prod_id;
 | |
| 	my $device_id;
 | |
| 
 | |
| #device id needed here
 | |
| 	$rc=0;
 | |
|    unless (defined($subcommand)) {
 | |
|       $subcommand = 'all';
 | |
|    }
 | |
| 	if($subcommand eq "all") {
 | |
|          $sessdata->{fullsel}=1;
 | |
| 	}
 | |
| 	elsif($subcommand eq "clear") {
 | |
| 	}
 | |
| 	elsif($subcommand =~ /^\d+$/) {
 | |
|         $sessdata->{numevents} = $subcommand;
 | |
| 	}
 | |
| 	else {
 | |
| 		return(1,"unsupported command eventlog $subcommand");
 | |
| 	}
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0xa,command=>0x48,data=>[],callback=>\&eventlog_with_time,callback_args=>$sessdata);
 | |
| }
 | |
| sub eventlog_with_time {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @returnd = (0,@{$rsp->{data}});
 | |
| 
 | |
|    #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
 | |
|    $sessdata->{tfactor} = $returnd[4]<<24 | $returnd[3]<<16 | $returnd[2]<<8 | $returnd[1];
 | |
|    if ($sessdata->{tfactor} > 0x20000000) {
 | |
|       $sessdata->{tfactor} -= time(); 
 | |
|    } else {
 | |
|       $sessdata->{tfactor} = 0;
 | |
|    }
 | |
|    $sessdata->{ipmisession}->subcmd(netfn=>0x0a,command=>0x40,data=>[],callback=>\&eventlog_with_selinfo,callback_args=>$sessdata);
 | |
| }
 | |
| sub eventlog_with_selinfo {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
| 	my $code = $rsp->{code}; 
 | |
|     my @returnd = (0,@{$rsp->{data}});
 | |
| 
 | |
| 	#sif($code == 0x81) { 
 | |
| 	#	$rc = 1;
 | |
| 	#	$text = "cannot execute command, SEL erase in progress";
 | |
| 	#}
 | |
| 
 | |
| 	my $sel_version = $returnd[1];
 | |
| 	if($sel_version != 0x51) {
 | |
| 		sendmsg(sprintf("SEL version 51h support only, version reported: %x",$sel_version),$sessdata->{node});
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
|     hexdump(\@returnd);
 | |
| 	my $num_entries = ($returnd[3]<<8) + $returnd[2];
 | |
| 	if($num_entries <= 0) {
 | |
| 		sendmsg("no SEL entries",$sessdata->{node});
 | |
|         return;
 | |
| 	}
 | |
| 
 | |
| 	my $canres = $returnd[14] & 0b00000010;
 | |
| 	if(!$canres) {
 | |
|         sendmsg([1,"SEL reservation not supported"],$sessdata->{node});
 | |
|         return;
 | |
| 	}
 | |
| 
 | |
|     my $subcommand = $sessdata->{subcommand};
 | |
|     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
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0xa,command=>0x42,data=>[],callback=>\&clear_sel_with_reservation,callback_args=>$sessdata);
 | |
|         return;
 | |
|     } elsif ($sessdata->{mfg_id} == 2) {
 | |
|         #read_ibm_auxlog($sessdata); #TODO JBJ fix this back in
 | |
|         #return;
 | |
|         #For requests other than clear, we check for IBM extended auxillary log data
 | |
|     }
 | |
|     $sessdata->{selentries} = [];
 | |
|     $sessdata->{selentry}=0;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0xa,command=>0x43,data=>[0,0,0x00,0x00,0x00,0xFF],callback=>\&got_sel,callback_args=>$sessdata);
 | |
| }
 | |
| sub got_sel {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @returnd = (0,@{$rsp->{data}});
 | |
| 		#elsif($code == 0x81) {
 | |
| 		#	$rc = 1;
 | |
| 		#	$text = "cannot execute command, SEL erase in progress";
 | |
| 		#}
 | |
| 
 | |
| 
 | |
| 	my $next_rec_ls = $returnd[1];
 | |
| 	my $next_rec_ms = $returnd[2];
 | |
| 	my @sel_data = @returnd[3..19];
 | |
| 
 | |
| 		$sessdata->{selentry}+=1;
 | |
|         if ($debug) {
 | |
| 			print $sessdata->{selentry}.": ";
 | |
| 			hexdump(\@sel_data);
 | |
|         }
 | |
| 
 | |
| 		my $record_id = $sel_data[0] + $sel_data[1]*256;
 | |
| 		my $record_type = $sel_data[2];
 | |
| 
 | |
| 		if($record_type == 0x02) {
 | |
| 		}
 | |
| 		else {
 | |
| 			my $text=getoemevent($record_type,$sessdata->{mfg_id},\@sel_data);
 | |
|             my $entry =  $sessdata->{selentry};
 | |
|             if ($sessdata->{auxloginfo} and $sessdata->{auxloginfo}->{$entry}) { 
 | |
|                 $text.=" With additional data:\n".$sessdata->{auxloginfo}->{$entry};
 | |
|             }
 | |
|             if ($sessdata->{fullsel}) {
 | |
|                sendmsg($text,$sessdata->{node});
 | |
|             } else {
 | |
| 			    push(@{$sessdata->{selentries}},$text);
 | |
|             }
 | |
| 			if($next_rec_ms == 0xFF && $next_rec_ls == 0xFF) {
 | |
| 				sendsel($sessdata);
 | |
|                 return;
 | |
| 			}
 | |
|             $sessdata->{ipmisession}->subcmd(netfn=>0xa,command=>0x43,data=>[0,0,$next_rec_ls,$next_rec_ms,0x00,0xFF],callback=>\&got_sel,callback_args=>$sessdata);
 | |
|             return;
 | |
| 		}
 | |
| 
 | |
| 		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 -= $sessdata->{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";
 | |
| 		my $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;
 | |
|         my $rc;
 | |
| 		($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;
 | |
| #			}
 | |
| #		}
 | |
|         my %sdr_hash = %{$sessdata->{sdr_hash}};
 | |
| 		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";
 | |
| 		}
 | |
|         my $entry = $sessdata->{selentry};
 | |
| 
 | |
|         if ($sessdata->{auxloginfo} and $sessdata->{auxloginfo}->{$entry}) {
 | |
|              $text.=" with additional data:";
 | |
|              if ($sessdata->{fullsel}) {
 | |
|                 sendmsg($text,$sessdata->{node});
 | |
|                 foreach (split /\n/,$sessdata->{auxloginfo}->{$entry}) {
 | |
|                     sendmsg(0,$_,$sessdata->{node});
 | |
|                 }
 | |
|              } else {
 | |
|         		push(@{$sessdata->{selentries}},$text);
 | |
|                 push @{$sessdata->{selentries}},split /\n/,$sessdata->{auxloginfo}->{$entry};
 | |
|              } 
 | |
| 
 | |
|         } else {
 | |
|             if ($sessdata->{fullsel}) {
 | |
|                 sendmsg($text,$sessdata->{node});
 | |
|             } else {
 | |
|         		push(@{$sessdata->{selentries}},$text);
 | |
|             }
 | |
|         }
 | |
| 
 | |
| 		if($next_rec_ms == 0xFF && $next_rec_ls == 0xFF) {
 | |
| 				sendsel($sessdata);
 | |
|                 return;
 | |
| 		}
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0xa,command=>0x43,data=>[0,0,$next_rec_ls,$next_rec_ms,0x00,0xFF],callback=>\&got_sel,callback_args=>$sessdata);
 | |
| }
 | |
| 
 | |
| sub sendsel {
 | |
|     my $sessdata = shift;
 | |
| ####my @routput = reverse(@output);
 | |
| ####my @noutput;
 | |
| ####my $c;
 | |
| ####foreach(@routput) {
 | |
| ####	$c++;
 | |
| ####	if($c > $num) {
 | |
| ####		last;
 | |
| ####	}
 | |
| ####	push(@noutput,$_);
 | |
| ####}
 | |
| ####@output = reverse(@noutput);
 | |
| 
 | |
| ####return($rc,@output);
 | |
| }
 | |
| sub clear_sel_with_reservation {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @returnd = (0,@{$rsp->{data}});
 | |
|     	#elsif($code == 0x81) {
 | |
|     	#	$rc = 1;
 | |
|     	#	$text = "cannot execute command, SEL erase in progress";
 | |
|     	#}
 | |
|         $sessdata->{res_id_ls} = $returnd[1];
 | |
|         $sessdata->{res_id_ms} = $returnd[2];
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0xa,command=>0x47,data=>[$sessdata->{res_id_ls},$sessdata->{res_id_ms},0x43,0x4c,0x52,0xaa],callback=>\&wait_for_selerase,callback_args=>$sessdata);
 | |
| }
 | |
| sub wait_for_selerase {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @returnd = (0,@{$rsp->{data}});
 | |
| 	my $erase_status = $returnd[1] & 0b00000001;
 | |
|     sendmsg("SEL cleared",$sessdata->{node});
 | |
| }
 | |
| 
 | |
| #commenting out usless 'while 0' loop.
 | |
| #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[0];
 | |
| 
 | |
| #   		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[1] & 0b00000001;
 | |
| #   	}
 | |
| 
 | |
| sub read_ibm_auxlog {
 | |
|     my $sessdata = shift;
 | |
|     my $entry = $sessdata->{selentry};
 | |
|         my @auxdata;
 | |
|         my $netfn = 0xa << 2;
 | |
|         my @auxlogcmd = (0x5a,1);
 | |
|         my $error = docmd(
 | |
|             $netfn,
 | |
|             \@auxlogcmd,
 | |
|             \@auxdata);
 | |
|         #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 ($sessdata->{auxloginfo}->{$entry}) {
 | |
|                         $sessdata->{auxloginfo}->{$entry}.="!".$extdata;
 | |
|                     } else {
 | |
|                         $sessdata->{auxloginfo}->{$entry}=$extdata;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             if ($sessdata->{auxloginfo}->{0}) {
 | |
|                 if ($sessdata->{fullsel}) {
 | |
|                     foreach (split /!/,$sessdata->{auxloginfo}->{0}) {
 | |
|                         sendoutput(0,":Unassociated auxillary data detected:");
 | |
|                         foreach (split /\n/,$_) {
 | |
|                             sendoutput(0,$_);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             #print Dumper(\%auxloginfo);
 | |
|         }
 | |
| }
 | |
| 
 | |
| sub getoemevent {
 | |
| 	my $record_type = shift;
 | |
| 	my $mfg_id = shift;
 | |
| 	my $sel_data = shift;
 | |
|     my $sessdata;
 | |
| 	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 -= $sessdata->{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 initiem {
 | |
|     my $sessdata = shift;
 | |
|     $sessdata->{iem} =  IBM::EnergyManager->new();
 | |
|     my @payload = $sessdata->{iem}->get_next_payload();
 | |
|     my $netfun = shift @payload;
 | |
|     my $command = shift @payload;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>$netfun,command=>$command,data=>\@payload,callback=>\&ieminitted,callback_args=>$sessdata);
 | |
| }
 | |
| sub ieminitted {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @returnd = ($rsp->{code},@{$rsp->{data}});
 | |
|     $sessdata->{iem}->handle_next_payload(@returnd);
 | |
|     $sessdata->{iemcallback}->($sessdata);
 | |
| }
 | |
| 
 | |
| sub readenergy {
 | |
|     my $sessdata = shift;
 | |
|     unless ($iem_support) { 
 | |
|         sendmsg([1,"IBM::EnergyManager package required for this value"],$sessdata->{node});
 | |
|         return;
 | |
|     }
 | |
|     my @entries;
 | |
|     $sessdata->{iemcallback}=\&readenergy_withiem;
 | |
|     initiem($sessdata);
 | |
| }
 | |
| sub readenergy_withiem {
 | |
|     my $sessdata = shift;
 | |
|     $sessdata->{iem}->prep_get_ac_energy();
 | |
|     $sessdata->{iemcallback} = \&got_ac_energy;
 | |
|     process_data_from_iem($sessdata);
 | |
| }
 | |
| sub got_ac_energy {
 | |
|     my $sessdata = shift;
 | |
|     $sessdata->{iem}->prep_get_precision();
 | |
|     $sessdata->{iemcallback} = \&got_ac_energy_with_precision;
 | |
|     execute_iem_commands($sessdata); #this gets all precision data initialized
 | |
| }
 | |
| sub got_ac_energy_with_precision {
 | |
|     my $sessdata=shift;
 | |
|     $sessdata->{iemtextdata} .= sprintf(" +/-%.1f%%",$sessdata->{iem}->energy_ac_precision()*0.1); #note while \x{B1} would be cool, it's non-trivial to support
 | |
|     sendmsg($sessdata->{iemtextdata},$sessdata->{node});
 | |
|     $sessdata->{iem}->prep_get_dc_energy();
 | |
|     $sessdata->{iemcallback} = \&got_dc_energy;
 | |
|     process_data_from_iem($sessdata);
 | |
| }
 | |
| sub got_dc_energy {
 | |
|     my $sessdata = shift;
 | |
|     $sessdata->{iemtextdata} .= sprintf(" +/-%.1f%%",$sessdata->{iem}->energy_dc_precision()*0.1);
 | |
|     sendmsg($sessdata->{iemtextdata},$sessdata->{node});
 | |
|     if (scalar @{$sessdata->{sensorstoread}}) {
 | |
|         $sessdata->{currsdr} = shift @{$sessdata->{sensorstoread}};
 | |
|         readsensor($sessdata); #next sensor
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| sub execute_iem_commands {
 | |
|     my $sessdata = shift;
 | |
|     my @payload = $sessdata->{iem}->get_next_payload();
 | |
|     if (scalar @payload) {
 | |
|         my $netfun = shift @payload;
 | |
|         my $command = shift @payload;
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>$netfun,command=>$command,data=>\@payload,callback=>\&executed_iem_command,callback_args=>$sessdata);
 | |
|     } else { #complete, return to callback
 | |
|         $sessdata->{iemcallback}->($sessdata);
 | |
|     }
 | |
| }
 | |
| sub executed_iem_command {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @returnd = ($rsp->{code},@{$rsp->{data}});
 | |
|     $sessdata->{iem}->handle_next_payload(@returnd);
 | |
|     execute_iem_commands($sessdata);
 | |
| }
 | |
| 
 | |
| sub process_data_from_iem {
 | |
|     my $sessdata = shift;
 | |
|     
 | |
|     my @returnd;
 | |
|     $sessdata->{iemdatacallback} = $sessdata->{iemcallback};
 | |
|     $sessdata->{iemcallback} = \&got_data_to_process_from_iem;
 | |
|     execute_iem_commands($sessdata);
 | |
| }
 | |
| sub got_data_to_process_from_iem {
 | |
|     my $sessdata = shift;
 | |
|     my @iemdata = $sessdata->{iem}->extract_data;
 | |
|     my $label = shift @iemdata;
 | |
|     my $units = shift @iemdata;
 | |
|     my $value=0;
 | |
|     my $shift=0;
 | |
|     while (scalar @iemdata) { #stuff the 64-bits of data into an int, would break in 32 bit
 | |
|         $value+=pop(@iemdata)<<$shift;
 | |
|         #$value.=sprintf("%02x ",shift @iemdata);
 | |
|         $shift+=8;
 | |
|     }
 | |
|     if ($units eq "mJ") {
 | |
|         $units = "kWh";
 | |
|         $value = $value / 3600000000;
 | |
|         $sessdata->{iemtextdata} = sprintf("$label: %.4f $units",$value);
 | |
|     } elsif ($units eq "mW") {
 | |
|         $units = "W";
 | |
|         $value = $value / 1000.0;
 | |
|         $sessdata->{iemtextdata} = sprintf("$label: %.1f $units",$value);
 | |
|     }
 | |
|     $sessdata->{iemdatacallback}->($sessdata);
 | |
| }
 | |
| 
 | |
| sub checkleds {
 | |
|     my $sessdata = shift;
 | |
| 	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=$sessdata->{mfg_id};
 | |
| #TODO device id
 | |
| 	if ($mfg_id != 2) {
 | |
| 		sendmsg("LED status not supported on this system",$sessdata->{node});
 | |
|         return;
 | |
| 	}
 | |
| 	
 | |
|     my %sdr_hash = %{$sessdata->{sdr_hash}};
 | |
|     $sessdata->{doleds} = [];
 | |
| 	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;
 | |
| 			}
 | |
| 				
 | |
|             push @{$sessdata->{doleds}},[$led_id_ms,$led_id_ls,$sdr];
 | |
|         }
 | |
|     }
 | |
|     $sessdata->{doled} = shift @{$sessdata->{doleds}};
 | |
|     if ($sessdata->{doled}) {
 | |
|         $sessdata->{current_led_sdr} = pop @{$sessdata->{doled}};
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xc0,data=>$sessdata->{doled},callback=>\&did_led,callback_args=>$sessdata);
 | |
|     } else {
 | |
|         sendmsg("No supported LEDs found in system",$sessdata->{node});
 | |
|     }
 | |
| #	if ($#output==-1) {
 | |
| #		push(@output,"No active error LEDs detected");
 | |
| #	}
 | |
| }
 | |
| 
 | |
| sub did_led {
 | |
|     my $rsp = $_[0];
 | |
|     my $sessdata = $_[1];
 | |
|     my $mfg_id = $sessdata->{mfg_id};
 | |
|     my $prod_id = $sessdata->{prod_id};
 | |
|     my $sdr = $sessdata->{current_led_sdr};
 | |
|     if (not $sessdata->{ledswappedendian} and $_[0]->{code} == 0xc9) { #missed an endian guess probably
 | |
|         $sessdata->{ledswappedendian}=1;
 | |
|         my @doled;
 | |
|         $doled[0]=$sessdata->{doled}->[1];
 | |
|         $doled[1]=$sessdata->{doled}->[0];
 | |
|         $sessdata->{doled} = \@doled;
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xc0,data=>$sessdata->{doled},callback=>\&did_led,callback_args=>$sessdata);
 | |
|         return;
 | |
|     } elsif ( $_[0]->{code} == 0xc9) {
 | |
|         $_[0]->{code} = 0; #TODO: some system actually gives an led locator record that doesn't exist....
 | |
|                     print "DEBUG: unfindable LED record\n";
 | |
|     }
 | |
|     $sessdata->{ledswappedendian}=0; #reset ledswappedendian flag to allow future swaps
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $led_id_ls = $sessdata->{doled}->[1];
 | |
|     my $led_id_ms = $sessdata->{doled}->[0];
 | |
|     my @returnd = (0,@{$rsp->{data}});
 | |
| 		if ($returnd[2]) { # != 0) {
 | |
| 			#It's on...
 | |
| 			if ($returnd[6] == 4) {
 | |
| 				sendmsg(sprintf("BIOS or admininstrator has %s lit",getsensorname($mfg_id,$prod_id,$sdr->led_id,"ibmleds")),$sessdata->{node});
 | |
|             $sessdata->{activeleds}=1;
 | |
| 			}
 | |
| 			elsif ($returnd[6] == 3) {
 | |
| 				sendmsg(sprintf("A user has manually requested LED 0x%04x (%s) be active",$sdr->led_id,getsensorname($mfg_id,$prod_id,$sdr->led_id,"ibmleds")),$sessdata->{node});
 | |
|             $sessdata->{activeleds}=1;
 | |
| 			}
 | |
| 			elsif ($returnd[6] == 1 && $sdr->led_id !=0) {
 | |
| 				sendmsg(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[4],$returnd[5],getsensorname($mfg_id,$prod_id,($returnd[4]<<8)+$returnd[5],"ibmleds")),$sessdata->{node});
 | |
|             $sessdata->{activeleds}=1;
 | |
| 			}
 | |
| 			elsif ($sdr->led_id ==0) {
 | |
| 				sendmsg(sprintf("LED 0x0000 (%s) active to indicate system error condition.",getsensorname($mfg_id,$prod_id,$sdr->led_id,"ibmleds")),$sessdata->{node});
 | |
|             $sessdata->{activeleds}=1;
 | |
| 			}
 | |
| 			elsif ($returnd[6] == 2) {
 | |
| 				my $sensor_desc;
 | |
| 				#Ok, LED is tied to a sensor..
 | |
| 				my $sensor_num=$returnd[5];
 | |
|                 my %sdr_hash = %{$sessdata->{sdr_hash}};
 | |
| 			        foreach my $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;
 | |
| 						}
 | |
| 		                        }
 | |
| 		                }
 | |
| 				#push(@output,sprintf("Sensor 0x%02x (%s) has activated LED 0x%04x",$sensor_num,$sensor_desc,$sdr->led_id));
 | |
| 				sendmsg(sprintf("LED 0x%02x%02x active to indicate Sensor 0x%02x (%s) error.",$led_id_ms,$led_id_ls,$sensor_num,$sensor_desc),$sessdata->{node});
 | |
|             $sessdata->{activeleds}=1;
 | |
| 		        } else { #an LED is on for some other reason
 | |
|                     print "DEBUG: unknown LED reason code ".$returnd[6]."\n";
 | |
|                     #TODO: discern meaning of more 'reason' codes, 5 and ff have come up
 | |
|                 }
 | |
|                     
 | |
| 		} 
 | |
|     $sessdata->{doled} = shift @{$sessdata->{doleds}};
 | |
|     if ($sessdata->{doled}) {
 | |
|         $sessdata->{current_led_sdr} = pop @{$sessdata->{doled}};
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0x3a,command=>0xc0,data=>$sessdata->{doled},callback=>\&did_led,callback_args=>$sessdata);
 | |
|     } elsif (not $sessdata->{activeleds}) {
 | |
|         sendmsg("No active error LEDs detected",$sessdata->{node});
 | |
|     }
 | |
|     if (scalar @{$sessdata->{sensorstoread}}) {
 | |
|         $sessdata->{currsdr} = shift @{$sessdata->{sensorstoread}};
 | |
|         readsensor($sessdata); #next sensor
 | |
|     }
 | |
| }
 | |
| 	
 | |
| sub renergy {
 | |
|     my $sessdata = shift;
 | |
|     my @subcommands = @{$sessdata->{extraargs}};
 | |
|     unless ($iem_support) {
 | |
|         return (1,"Command unsupported without IBM::EnergyManager installed");
 | |
|     }
 | |
|     my @directives=();
 | |
|     foreach (@subcommands) {
 | |
|         if ($_ eq 'cappingmaxmin') {
 | |
|             push @directives,'cappingmax','cappingmin';
 | |
|         }
 | |
|         push @directives,split /,/,$_;
 | |
|     }
 | |
|     $sessdata->{directives} = \@directives;
 | |
|     $sessdata->{iemcallback}=\&renergy_withiem;
 | |
|     initiem($sessdata);
 | |
| }
 | |
| sub renergy_withiem {
 | |
|     my $sessdata = shift;
 | |
|     my @settable_keys = qw/savingstatus cappingstatus cappingwatt cappingvalue/;
 | |
|     my $directive = shift (@{$sessdata->{directives}});
 | |
|     if ($sessdata->{iemtextdata}) {
 | |
|         sendmsg($sessdata->{iemtextdata},$sessdata->{node});
 | |
|         $sessdata->{iemtextdata}="";
 | |
|     }
 | |
|     if ($sessdata->{gotcapstatus}) {
 | |
|         $sessdata->{gotcapstatus}=0;
 | |
|         my $capenabled = $sessdata->{iem}->capping_enabled();
 | |
|         sendmsg("cappingstatus: ".($capenabled ? "on" : "off"),$sessdata->{node});
 | |
|     }
 | |
|     if ($sessdata->{gothistogram}) {
 | |
|         $sessdata->{gothistogram}=0;
 | |
|         my @histdata = $sessdata->{iem}->extract_relative_histogram;
 | |
|         foreach (sort { $a <=> $b } keys %{$histdata[0]}) {
 | |
|             sendmsg("$_: ".$histdata[0]->{$_},$sessdata->{node});
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     unless ($directive) { 
 | |
|         return;
 | |
|     }
 | |
|     my $value=undef;
 | |
|     my $key=undef;
 | |
|     $sessdata->{iemcallback} = \&renergy_withiem;
 | |
|     if ($directive =~ /(.*)=(.*)\z/) {
 | |
|         $key = $1;
 | |
|         $value = $2;
 | |
|         unless (grep /$key/,@settable_keys and $value) {
 | |
|             return (1,"Malformed argument $directive");
 | |
|         }
 | |
|         if ($key eq "cappingwatt" or $key eq "cappingvalue") {
 | |
|             $value = $value*1000; #convert to milliwatts
 | |
|             $sessdata->{iem}->prep_set_cap($value);
 | |
|             execute_iem_commands($sessdata); #this gets all precision data initialized
 | |
|         }
 | |
|         if ($key eq "cappingstatus") {
 | |
|             if (grep /$value/,qw/enable on 1/) {
 | |
|                 $value = 1;
 | |
|             } else {
 | |
|                 $value = 0;
 | |
|             }
 | |
|             $sessdata->{iem}->prep_set_capenable($value);
 | |
|             execute_iem_commands($sessdata); #this gets all precision data initialized
 | |
|         }
 | |
| 
 | |
|     }
 | |
|     if ($directive =~ /cappingmin/) {
 | |
|         $sessdata->{iem}->prep_get_mincap();
 | |
|         process_data_from_iem($sessdata);
 | |
|     } elsif ($directive =~ /cappingmax$/) {
 | |
|         $sessdata->{iem}->prep_get_maxcap();
 | |
|         process_data_from_iem($sessdata);
 | |
|     }
 | |
|     if ($directive =~ /cappingvalue/) {
 | |
|         $sessdata->{iem}->prep_get_cap();
 | |
|         process_data_from_iem($sessdata);
 | |
|     }
 | |
|     if ($directive =~ /cappingstatus/) {
 | |
|         $sessdata->{iem}->prep_get_powerstatus();
 | |
|         $sessdata->{gotcapstatus}=1;
 | |
|         execute_iem_commands($sessdata);
 | |
|     }
 | |
|     if ($directive =~ /relhistogram/) {
 | |
|         $sessdata->{gothistogram}=1;
 | |
|         $sessdata->{iem}->prep_retrieve_histogram();
 | |
|         execute_iem_commands($sessdata);
 | |
|     }
 | |
|     return;
 | |
| }
 | |
| sub vitals {
 | |
|     my $sessdata = shift;
 | |
|     my %sdr_hash = %{$sessdata->{sdr_hash}};
 | |
|     my @textfilters;
 | |
|     foreach (@{$sessdata->{extraargs}}) {
 | |
|         push @textfilters,(split /,/,$_);
 | |
|     }
 | |
|     unless (scalar @textfilters) { @textfilters = ("all"); }
 | |
| 
 | |
| 	my $rc = 0;
 | |
| 	my $text;
 | |
| 	my $key;
 | |
| 	my %sensor_filters=();
 | |
| 	my @output;
 | |
| 	my $reading;
 | |
| 	my $unitdesc;
 | |
| 	my $value;
 | |
| 	my $extext;
 | |
| 	my $format = "%-30s%8s %-20s";
 | |
|     my $doall;
 | |
|     $doall=0;
 | |
| 	$rc=0;
 | |
|     #filters: defined in sensor type codes and data table
 | |
|     # 1 == temp, 2 == voltage 3== current (we lump in wattage here for lack of a better spot), 4 == fan
 | |
| 
 | |
| 	if(grep { $_ eq "all"} @textfilters) {
 | |
| 	  $sensor_filters{1}=1; #,0x02,0x03,0x04); rather than filtering, unfiltered results
 | |
|       $sensor_filters{energy}=1;
 | |
|       $doall=1;
 | |
| 	}
 | |
| 	if(grep /temp/,@textfilters) {
 | |
| 		$sensor_filters{0x01}=1;
 | |
| 	}
 | |
| 	if(grep /volt/,@textfilters) {
 | |
| 		$sensor_filters{0x02}=1;
 | |
| 	}
 | |
|     if(grep /watt/,@textfilters) {
 | |
|         $sensor_filters{0x03}=1;
 | |
|     }
 | |
| 	if(grep /fan/,@textfilters) {
 | |
| 		$sensor_filters{0x04}=1;
 | |
| 	}
 | |
| 	if(grep /power/,@textfilters) {  #power does not really include energy, but most people use 'power' to mean both
 | |
|         $sensor_filters{0x03}=1;
 | |
|         $sensor_filters{powerstate}=1;
 | |
|         $sensor_filters{energy}=1;
 | |
| 	}
 | |
| 	if(grep /energy/,@textfilters) { 
 | |
|         $sensor_filters{energy}=1;
 | |
| 	}
 | |
| 	if(grep /led/,@textfilters) {
 | |
|         $sensor_filters{leds}=1;
 | |
| 	}
 | |
| 	unless (keys %sensor_filters) {
 | |
|         sendmsg([1,"Unrecognized rvitals arguments ".join(" ",@{$sessdata->{extraargs}})],$sessdata->{node});;
 | |
| 	}
 | |
| 
 | |
|     $sessdata->{sensorstoread} = [];
 | |
| 	foreach(keys %sensor_filters) {
 | |
| 		my $filter = $_;
 | |
|         if ($filter eq "energy" or $filter eq "leds") { next; }
 | |
| 
 | |
| 		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;
 | |
|                 push @{$sessdata->{sensorstoread}},$sdr;
 | |
|             }
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if($sensor_filters{leds}) {
 | |
|         push @{$sessdata->{sensorstoread}},"leds";
 | |
| 		#my @cleds;
 | |
| 		#($rc,@cleds) = checkleds();
 | |
|         #push @output,@cleds;
 | |
|     }
 | |
|     if ($sensor_filters{powerstate}) {
 | |
|         push @{$sessdata->{sensorstoread}},"powerstat";
 | |
| 		#($rc,$text) = power("stat");
 | |
| 		#$text = sprintf($format,"Power Status:",$text,"");
 | |
| 		#push(@output,$text);
 | |
|     }
 | |
|     if ($sensor_filters{energy}) {
 | |
|         if ($iem_support) {
 | |
|             push @{$sessdata->{sensorstoread}},"energy";
 | |
|         } elsif (not $doall) {
 | |
|             sendmsg([1,"Energy data requires additional IBM::EnergyManager plugin in conjunction with IMM managed IBM equipment"],$sessdata->{node});
 | |
|         }
 | |
|         #my @energies;
 | |
|         #($rc,@energies)=readenergy();
 | |
|         #push @output,@energies;
 | |
| 	}
 | |
|     if (scalar @{$sessdata->{sensorstoread}}) {
 | |
|         $sessdata->{currsdr} = shift @{$sessdata->{sensorstoread}};
 | |
|         readsensor($sessdata); #and we are off
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| sub sensorformat {
 | |
|     my $sessdata = shift;
 | |
|     my $sdr = $sessdata->{currsdr};
 | |
|     my $rc = shift;
 | |
|     my $reading = shift;
 | |
|     my $extext = shift;
 | |
| 	my $unitdesc = "";
 | |
|     my $value;
 | |
| 	my $lformat = "%-30s %-20s";
 | |
| 	my $per = " ";
 | |
|     my $data;
 | |
| 	if($rc == 0) {
 | |
|         $data = translate_sensor($reading,$sdr);
 | |
| 	} else {
 | |
|         $data = "N/A";
 | |
|     }
 | |
| #$unitdesc.= sprintf(" %x",$sdr->sensor_type);
 | |
|     use Data::Dumper;
 | |
|     print Dumper($lformat,$sdr->id_string,$data);
 | |
| 	my $text = sprintf($lformat,$sdr->id_string . ":",$data);
 | |
| 	if ($extext) {
 | |
| 		$text="$text ($extext)";
 | |
| 	}
 | |
|     sendmsg($text,$sessdata->{node});
 | |
|     if (scalar @{$sessdata->{sensorstoread}}) {
 | |
|         $sessdata->{currsdr} = shift @{$sessdata->{sensorstoread}};
 | |
|         readsensor($sessdata); #next
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub readsensor {
 | |
|     my $sessdata = shift;
 | |
|     if (not ref $sessdata->{currsdr}) {
 | |
|         if ($sessdata->{currsdr} eq "leds") {
 | |
|             checkleds($sessdata);
 | |
|             return;
 | |
|         } elsif ($sessdata->{currsdr} eq "powerstat") {
 | |
|             $sessdata->{powerstatprefix}="Power Status: ";
 | |
|             $sessdata->{subcommand}="stat";
 | |
|             power($sessdata);
 | |
|             return;
 | |
|         } elsif ($sessdata->{currsdr} eq "energy") {
 | |
|             readenergy($sessdata);
 | |
|             return;
 | |
|         } else {
 | |
|         sendmsg([1,"TODO: make ".$sessdata->{currsdr}." work again"],$sessdata->{node});
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
| 	my $sensor = $sessdata->{currsdr}->sensor_number;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0x4,command=>0x2d,data=>[$sensor],callback=>\&sensor_was_read,callback_args=>$sessdata);
 | |
| }
 | |
| 
 | |
| sub sensor_was_read {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     if ($rsp->{error}) {
 | |
|         sendmsg([1,$rsp->{error}],$sessdata->{node});
 | |
|     }
 | |
|     if ($rsp->{code}) {
 | |
|         my $text = $codes{$rsp->{code}};
 | |
|         unless ($text) { $text = sprintf("Unknown error %02xh",$rsp->{code}) };
 | |
|         return sensorformat($sessdata,1,$text);
 | |
|     }
 | |
| 
 | |
|     my @returnd = (0,@{$rsp->{data}});
 | |
| 	
 | |
| 	if ($returnd[2] & 0x20) {
 | |
| 		return sensorformat($sessdata,1,"N/A");
 | |
| 	}
 | |
| 	my $text = $returnd[1];
 | |
|     my $exdata1 = $returnd[3];
 | |
|     my $exdata2 = $returnd[3];
 | |
|     my $extext;
 | |
|     my @exparts;
 | |
|     my $sdr = $sessdata->{currsdr};
 | |
|     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 sensorformat($sessdata,0,$text,$extext);
 | |
| }
 | |
| 
 | |
| sub initsdr {
 | |
|     my $sessdata=shift;
 | |
| 	my $netfun;
 | |
| 	my @cmd;
 | |
| 	my @returnd = ();
 | |
| 	my $error;
 | |
| 	my $rc = 0;
 | |
| 	my $text;
 | |
| 	my $code;
 | |
| 
 | |
| 	my $resv_id_ls;
 | |
| 	my $resv_id_ms;
 | |
| 	my $sdr_type;
 | |
| 	my $sdr_offset;
 | |
| 	my $sdr_len;
 | |
| 	my @sdr_data = ();
 | |
| 	my $offset;
 | |
| 	my $len;
 | |
| 	my $i;
 | |
| #	my $numbytes = 27;
 | |
| 	my $ipmisensortab = "$ENV{XCATROOT}/lib/GUMI/ipmisensor.tab";
 | |
| 	my $byte_format;
 | |
| 	my $cache_file;
 | |
| 
 | |
|      #device id data TODO
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0x0a,command=>0x20,data=>[],callback=>\&got_sdr_rep_info,callback_args=>$sessdata);
 | |
| }
 | |
| 
 | |
| sub  initsdr_withrepinfo {
 | |
|     my $sessdata = shift;
 | |
| 	my $mfg_id=$sessdata->{mfg_id};
 | |
| 	my $prod_id=$sessdata->{prod_id};
 | |
| 	my $device_id=$sessdata->{device_id};
 | |
| 	my $dev_rev=$sessdata->{device_rev};
 | |
| 	my $fw_rev1=$sessdata->{firmware_rev1};
 | |
| 	my $fw_rev2=$sessdata->{firmware_rev2};
 | |
|     #TODO: beware of dynamic SDR contents
 | |
| 
 | |
| 	my $cache_file = "$cache_dir/sdr_$mfg_id.$prod_id.$device_id.$dev_rev.$fw_rev1.$fw_rev2.$cache_version";
 | |
|     $sessdata->{sdrcache_file} = $cache_file;
 | |
| 	if($enable_cache eq "yes") {
 | |
|         if ($sdr_caches{"$mfg_id.$prod_id.$device_id.$dev_rev.$fw_rev1.$fw_rev2.$cache_version"}) {
 | |
|             $sessdata->{sdr_hash} = $sdr_caches{"$mfg_id.$prod_id.$device_id.$dev_rev.$fw_rev1.$fw_rev2.$cache_version"};
 | |
|             on_bmc_connect("SUCCESS",$sessdata); #retry bmc_connect since sdr_cache is validated
 | |
|             return; #don't proceed to slow load
 | |
|         } else {
 | |
|     		my $rc = loadsdrcache($sessdata,$cache_file);
 | |
|     		if($rc == 0) {
 | |
|                 $sdr_caches{"$mfg_id.$prod_id.$device_id.$dev_rev.$fw_rev1.$fw_rev2.$cache_version"} = $sessdata->{sdr_hash};
 | |
|                 on_bmc_connect("SUCCESS",$sessdata); #retry bmc_connect since sdr_cache is validated
 | |
|                 return; #don't proceed to slow load
 | |
|     		}
 | |
|         }
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	if($sessdata->{sdr_info}->{version} != 0x51) {
 | |
|         sendoutput(1,"SDR version unsupported.");
 | |
| 		return(1); #bail, do not try to continue
 | |
| 	}
 | |
| 
 | |
| 	if($sessdata->{sdr_info}->{resv_sdr} != 1) {
 | |
|         sendoutput(1,"SDR reservation unsupported.");
 | |
|         return 1;
 | |
| 	}
 | |
| 
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0x0a,command=>0x22,data=>[],callback=>\&reserved_sdr_repo,callback_args=>$sessdata);
 | |
| }
 | |
| sub initsdr_withreservation {
 | |
|     my $sessdata = shift;
 | |
| 	my $rid_ls = 0;
 | |
| 	my $rid_ms = 0;
 | |
| 	if ($sessdata->{sdr_nrid_ls}) { $rid_ls = $sessdata->{sdr_nrid_ls}; }
 | |
| 	if ($sessdata->{sdr_nrid_ms}) { $rid_ms = $sessdata->{sdr_nrid_ms}; }
 | |
| 
 | |
| ####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";
 | |
| ####}
 | |
|     my $resv_id_ls = $sessdata->{resv_id_ls};
 | |
|     my $resv_id_ms = $sessdata->{resv_id_ms};
 | |
|     if ( $rid_ls == 0xff and $rid_ms == 0xff) {
 | |
| 	    if($enable_cache eq "yes") { #cache SDR repository for future use
 | |
| 		    storsdrcache($sessdata->{sdrcache_file},$sessdata);
 | |
| 	    }
 | |
|         on_bmc_connect("SUCCESS",$sessdata); #go back armed with a capable reserviction
 | |
|         return; #Have reached the end
 | |
|     }
 | |
|     $sessdata->{sdr_fetch_args} = [$resv_id_ls,$resv_id_ms,$rid_ls,$rid_ms,0,5];
 | |
|     $sessdata->{ipmisession}->subcmd(netfn=>0xa,command=>0x23,data=>$sessdata->{sdr_fetch_args},callback=>\&start_sdr_record,callback_args=>$sessdata);
 | |
|     return;
 | |
| }
 | |
| 
 | |
| sub start_sdr_record {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
| 	if($rsp->{error}) {
 | |
|         sendoutput(1,$rsp->{error});
 | |
| 		return;
 | |
| 	}
 | |
|     my $resv_id_ls = shift @{$sessdata->{sdr_fetch_args}};
 | |
|     my $resv_id_ms = shift @{$sessdata->{sdr_fetch_args}};
 | |
|     my $rid_ls = shift @{$sessdata->{sdr_fetch_args}};
 | |
|     my $rid_ms = shift @{$sessdata->{sdr_fetch_args}};
 | |
|     my @returnd = ($rsp->{code},@{$rsp->{data}});
 | |
| 	my $code = $returnd[0];
 | |
| 	if($code != 0x00) {
 | |
| 		my $text = $codes{$code};
 | |
| 		if(!$text) {
 | |
| 			$text = sprintf("unknown response %02x",$code);
 | |
| 		}
 | |
|         sendoutput(1,$text);
 | |
| 		return;
 | |
| 	}
 | |
| 	$sessdata->{sdr_nrid_ls} = $returnd[1];
 | |
| 	$sessdata->{sdr_nrid_ms} = $returnd[2];
 | |
| 	my $sdr_ver = $returnd[5];
 | |
| 	my $sdr_type = $returnd[6];
 | |
|    	$sessdata->{curr_sdr_type} = $sdr_type;
 | |
| 	$sessdata->{curr_sdr_len} = $returnd[7] + 5;
 | |
| 
 | |
| 	if($sdr_type == 0x01) {
 | |
| 		$sessdata->{total_sdr_offset} = 0;
 | |
| 	}
 | |
| 	elsif($sdr_type == 0x02) {
 | |
| 		$sessdata->{total_sdr_offset} = 16; #TODO: understand this..
 | |
| 	}
 | |
| 	elsif($sdr_type == 0xC0) {
 | |
| 		#LED descriptor, maybe
 | |
| 	}
 | |
| 	elsif($sdr_type == 0x11) { #FRU locator
 | |
| 	}
 | |
| 	elsif($sdr_type == 0x12) {
 | |
|         initsdr_withreservation($sessdata); #next, skip this unsupported record type
 | |
|         return;
 | |
| 	}
 | |
| 	else {
 | |
|         initsdr_withreservation($sessdata); #next
 | |
|         return;
 | |
| 	}
 | |
| 
 | |
| 	$sessdata->{sdr_data} = [0,0,0,$sdr_ver,$sdr_type,$sessdata->{curr_sdr_len}];
 | |
| 	$sessdata->{sdr_offset} = 5;
 | |
|     my $offset=5; #why duplicate? to make for shorter typing
 | |
| 	my $numbytes = 22;
 | |
|     if (5<$sessdata->{curr_sdr_len}) { #can't imagine this not bing the case,but keep logic in case
 | |
|         if($offset+$numbytes > $sessdata->{curr_sdr_len}) { #scale back request for remainder
 | |
|             $numbytes = $sessdata->{curr_sdr_len} - $offset;
 | |
|         }
 | |
|         $sessdata->{sdr_fetch_args} = [$resv_id_ls,$resv_id_ms,$rid_ls,$rid_ms,$offset,$numbytes];
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0x0a,command=>0x23,data=>$sessdata->{sdr_fetch_args},callback=>\&add_sdr_data,callback_args=>$sessdata);
 | |
|         return;
 | |
|     } else {
 | |
|         initsdr_withreservation($sessdata); #next
 | |
|         return;
 | |
|     }
 | |
| }
 | |
| sub add_sdr_data {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
| 	my $numbytes = $sessdata->{sdr_fetch_args}->[5];
 | |
|     my $offset = $sessdata->{sdr_offset}; #shorten typing a little
 | |
|     if ($rsp->{error}) {
 | |
|         sendoutput([1,$rsp->{error}]);
 | |
|         return; #give up
 | |
|     }
 | |
|     my @returnd = ($rsp->{code},@{$rsp->{data}});
 | |
| 	my $code = $returnd[0];
 | |
| 	if($code != 0x00) {
 | |
| 		my $text = $codes{$code};
 | |
| 		if(!$text) {
 | |
| 			$text = sprintf("unknown response %02x",$code);
 | |
| 		}
 | |
|         sendoutput([1,$text]);
 | |
|         return; #abort the whole mess
 | |
| 	}
 | |
|     push @{$sessdata->{sdr_data}},@returnd[3..@returnd-1];
 | |
| 	$sessdata->{sdr_offset} += $numbytes;
 | |
|     if($sessdata->{sdr_offset}+$numbytes > $sessdata->{curr_sdr_len}) { #scale back request for remainder
 | |
|        $numbytes = $sessdata->{curr_sdr_len} - $sessdata->{sdr_offset};
 | |
|     }
 | |
|     $sessdata->{sdr_fetch_args}->[4] = $sessdata->{sdr_offset};
 | |
|     $sessdata->{sdr_fetch_args}->[5] = $numbytes;
 | |
|     if ($sessdata->{sdr_offset}<$sessdata->{curr_sdr_len}) {
 | |
|         $sessdata->{ipmisession}->subcmd(netfn=>0x0a,command=>0x23,data=>$sessdata->{sdr_fetch_args},callback=>\&add_sdr_data,callback_args=>$sessdata);
 | |
|         return;
 | |
|     } else { #in this case, time to parse the accumulated data
 | |
|         parse_sdr($sessdata);
 | |
|     }
 | |
| }
 | |
| sub parse_sdr { #parse sdr data, then cann initsdr_withreserveation to advance to next record
 | |
|     my $sessdata = shift;
 | |
|     my @sdr_data =  @{$sessdata->{sdr_data}};
 | |
|     #not bothering trying to keep a packet pending concurrent with operation, harder to code that
 | |
| 	my $mfg_id=$sessdata->{mfg_id};
 | |
| 	my $prod_id=$sessdata->{prod_id};
 | |
| 	my $device_id=$sessdata->{device_id};
 | |
| 	my $dev_rev=$sessdata->{device_rev};
 | |
| 	my $fw_rev1=$sessdata->{firmware_rev1};
 | |
| 	my $fw_rev2=$sessdata->{firmware_rev2};
 | |
|     my $sdr_type = $sessdata->{curr_sdr_type};
 | |
| 	if($sdr_type == 0x11) { #FRU locator
 | |
|         my $sdr = decode_fru_locator(@sdr_data);
 | |
|         if ($sdr) {
 | |
| 	        $sessdata->{sdr_hash}->{$sdr->sensor_owner_id . "." . $sdr->sensor_owner_lun . "." . $sdr->sensor_number} = $sdr;
 | |
|         }
 | |
|         initsdr_withreservation($sessdata); #advance to next record
 | |
|         return;
 | |
|     }
 | |
| 
 | |
| ####if($debug) {
 | |
| ####	hexadump(\@sdr_data);
 | |
| ####}
 | |
| 
 | |
| 
 | |
|     if($sdr_type == 0x12) { #if required, TODO support type 0x12
 | |
| 	   hexadump(\@sdr_data);
 | |
|        initsdr_withreservation($sessdata); #next record
 | |
|        return;
 | |
| 	}
 | |
| 
 | |
| 	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....
 | |
| 			$sessdata->{sdr_hash}->{"260.260.".$sdr->led_id} = $sdr;
 | |
|             initsdr_withreservation($sessdata); #next record
 | |
|             return;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		$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-$sessdata->{total_sdr_offset}]);
 | |
| 
 | |
| 		my $override_string = getsensorname($mfg_id,$prod_id,$sdr->sensor_number);
 | |
| 
 | |
| 		if($override_string ne "") {
 | |
| 			$sdr->id_string($override_string);
 | |
| 		}
 | |
| 		else {
 | |
|             unless (defined $sdr->id_string_type) { initsdr_withreservation($sessdata); return; }
 | |
| 			my $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-$sessdata->{total_sdr_offset}..49-$sessdata->{total_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 {
 | |
|                     initsdr_withreservation($sessdata); return;
 | |
|                 }
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		$sessdata->{sdr_hash}->{$sdr->sensor_owner_id . "." . $sdr->sensor_owner_lun . "." . $sdr->sensor_number} = $sdr;
 | |
|         initsdr_withreservation($sessdata); return;
 | |
| }
 | |
| 
 | |
| sub getsensorname
 | |
| {
 | |
| 	my $mfgid = shift;
 | |
| 	my $prodid = shift;
 | |
| 	my $sensor = shift;
 | |
| 	my $file = shift;
 | |
| 
 | |
| 	my $mfg;
 | |
| 	my $prod;
 | |
| 	my $type;
 | |
| 	my $desc;
 | |
| 	my $name="";
 | |
| 
 | |
|     if ($file and $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[0];
 | |
| 	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[1..@returnd-2]);
 | |
| }
 | |
| 
 | |
| sub gotdevid {
 | |
| 	#($rc,$text,$mfg_id,$prod_id,$device_id,$dev_rev,$fw_rev1,$fw_rev2) = getdevid();
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
|     my $text;
 | |
| 
 | |
| 	if($rsp->{error}) {
 | |
|         sendoutput([1,$rsp->{error}]);
 | |
|         return;
 | |
| 	}
 | |
| 	else {
 | |
| 		my $code = $rsp->{code};
 | |
| 
 | |
| 		if($code != 0x00) {
 | |
| 			my $text = $codes{$code};
 | |
| 			if(!$text) {
 | |
| 				$text = sprintf("unknown response %02x",$code);
 | |
| 			}
 | |
|             sendoutput([1,$text]);
 | |
|             return;
 | |
| 		}
 | |
| 	}
 | |
|     my @returnd = ($rsp->{code},@{$rsp->{data}});
 | |
| 
 | |
| 	$sessdata->{device_id} = $returnd[1];
 | |
| 	$sessdata->{device_rev} = $returnd[2] & 0b00001111;
 | |
| 	$sessdata->{firmware_rev1} = $returnd[3] & 0b01111111;
 | |
| 	$sessdata->{firmware_rev2} = $returnd[4];
 | |
| 	$sessdata->{ipmi_ver} = $returnd[5];
 | |
| 	$sessdata->{dev_support} = $returnd[6];
 | |
| ####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;
 | |
| ####}
 | |
| 	$sessdata->{mfg_id} = $returnd[7] + $returnd[8]*0x100 +  $returnd[9]*0x10000;
 | |
| 	$sessdata->{prod_id} = $returnd[10] + $returnd[11]*0x100;
 | |
|     on_bmc_connect("SUCCESS",$sessdata);
 | |
| #	my @data = @returnd[12..@returnd-2];
 | |
| 
 | |
| #	return($rc,$text,$mfg_id,$prod_id,$device_id,$device_rev,$firmware_rev1,$firmware_rev2);
 | |
| }
 | |
| 
 | |
| sub gotguid {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
| 	#my @guidcmd = (0x18,0x37);
 | |
| 	#if($mfg_id == 2 && $prod_id == 34869) { TODO: if GUID is inaccurate on the products mentioned, this code may be uncommented
 | |
| 	#	@guidcmd = (0x18,0x08);
 | |
| 	#}
 | |
| 	#if($mfg_id == 2 && $prod_id == 4) {
 | |
| 	#	@guidcmd = (0x18,0x08);
 | |
| 	#}
 | |
| 	#if($mfg_id == 2 && $prod_id == 3) {
 | |
| 	#	@guidcmd = (0x18,0x08);
 | |
| 	#}
 | |
| 	my $fru = FRU->new();
 | |
| 	$fru->rec_type("guid");
 | |
| 	$fru->desc("GUID");
 | |
| 	$fru->value(sprintf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",@{$rsp->{data}}));
 | |
| 	$sessdata->{fru_hash}->{guid} = $fru;
 | |
|     initfru_withguid($sessdata);
 | |
| }
 | |
| 
 | |
| sub got_sdr_rep_info {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
| 
 | |
| 	if($rsp->{error}) {
 | |
|         sendoutput([1,$rsp->{error}]);
 | |
|         return;
 | |
| 	}
 | |
| 	else {
 | |
| 		my $code = $rsp->{code};
 | |
| 
 | |
| 		if($code != 0x00) {
 | |
| 			my $text = $codes{$code};
 | |
| 			if(!$text) {
 | |
| 				$text = sprintf("unknown response %02x",$code);
 | |
| 			}
 | |
|             sendoutput(1,$text);
 | |
|             return;
 | |
| 		}
 | |
| 	}
 | |
|     my @returnd = @{$rsp->{data}};
 | |
|     $sessdata->{sdr_info}->{version} = $returnd[0];
 | |
|     $sessdata->{sdr_info}->{rec_count} = $returnd[1] + $returnd[2]<<8;
 | |
|     $sessdata->{sdr_info}->{resv_sdr} = ($returnd[13] & 0b00000010)>>1;
 | |
|     initsdr_withrepinfo($sessdata);
 | |
| }
 | |
| 
 | |
| sub reserved_sdr_repo {
 | |
|     my $rsp = shift;
 | |
|     my $sessdata = shift;
 | |
| 	if($rsp->{error}) {
 | |
|         sendoutput([1,$rsp->{error}]);;
 | |
|         return;
 | |
| 	}
 | |
| 	else {
 | |
| 		my $code = $rsp->{code};
 | |
| 
 | |
| 		if($code != 0x00) {
 | |
| 			my $text = $codes{$code};
 | |
| 			if(!$text) {
 | |
| 				$text = sprintf("unknown response %02x",$code);
 | |
| 			}
 | |
|             sendoutput([1,$text]);
 | |
| 		}
 | |
| 	}
 | |
|     my @returnd = @{$rsp->{data}};
 | |
| 
 | |
|     $sessdata->{resv_id_ls} =  $returnd[0];
 | |
|     $sessdata->{resv_id_ms} =  $returnd[1];
 | |
|     initsdr_withreservation($sessdata);
 | |
| }
 | |
| 
 | |
| sub dochksum()
 | |
| {
 | |
| 	my $data = shift;
 | |
| 	my $sum = 0;
 | |
| 
 | |
| 	foreach(@$data) {
 | |
| 		$sum += $_;
 | |
| 	}
 | |
| 
 | |
| 	$sum = ~$sum + 1;
 | |
| 	return($sum & 0xFF);
 | |
| }
 | |
| 
 | |
| 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 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 $sessdata = shift;
 | |
|     unless ($sessdata) { die "need to fix this one too" }
 | |
| 	my $key;
 | |
| 	my $fh;
 | |
| 
 | |
| 	system("mkdir -p $cache_dir");
 | |
| 	if(!open($fh,">$file")) {
 | |
| 		return(1);
 | |
| 	}
 | |
| 
 | |
| 	flock($fh,LOCK_EX) || return(1);
 | |
| 
 | |
| 	foreach $key (keys %{$sessdata->{sdr_hash}}) {
 | |
| 		my $r = $sessdata->{sdr_hash}->{$key};
 | |
| 		store_fd($r,$fh);
 | |
| 	}
 | |
| 
 | |
| 	close($fh);
 | |
| 
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| sub loadsdrcache {
 | |
|     my $sessdata = shift;
 | |
| 	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;
 | |
| 
 | |
| 		$sessdata->{sdr_hash}->{$r->sensor_owner_id . "." . $r->sensor_owner_lun . "." . $r->sensor_number} = $r;
 | |
| 	}
 | |
| 
 | |
| 	close($fh);
 | |
| 
 | |
| 	return(0);
 | |
| }
 | |
| 
 | |
| 
 | |
| sub preprocess_request { 
 | |
|   my $request = shift;
 | |
|   if (defined $request->{_xcatpreprocessed}->[0] and $request->{_xcatpreprocessed}->[0] == 1) { 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 eq ''){
 | |
| 	  		$callback->({data=>["Please enter an action (eg: boot,off,on, etc)",  $usage_string]});
 | |
| 	  		$request = {};
 | |
| 				return 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;
 | |
|     $reqcopy->{_xcatpreprocessed}->[0] = 1;
 | |
|     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;
 | |
|   $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 $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;
 | |
|     my $sub_fds = new IO::Select;
 | |
|     foreach (@donargs) {
 | |
|       donode($_->[0],$_->[1],$_->[2],$_->[3],$ipmitimeout,$ipmitrys,$command,-args=>\@exargs);
 | |
| 	}
 | |
|     while (xCAT::IPMI->waitforrsp()) { yield };
 | |
|     my $node;
 | |
|     foreach $node (keys %sessiondata) {
 | |
|         if ($sessiondata{$node}->{ipmisession}) {
 | |
|             $sessiondata{$node}->{ipmisession}->logout();
 | |
|         }
 | |
|     }
 | |
|     while (xCAT::IPMI->waitforrsp()) { yield };
 | |
| ####return;
 | |
| ####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 (keys %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>;
 | |
| #     }
 | |
| #     eval { print $rfh "ACK\n"; };  # Ignore ack loss to child that has given up and exited
 | |
| #     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 {
 | |
|   my $node = shift;
 | |
|   my $bmcip = shift;
 | |
|   my $user = shift;
 | |
|   my $pass = shift;
 | |
|   my $timeout = shift;
 | |
|   my $retries = shift;
 | |
|   my $command = shift;
 | |
|   my %namedargs=@_;
 | |
|   my $extra=$namedargs{-args};
 | |
|   my @exargs=@$extra;
 | |
|   $sessiondata{$node} = {
 | |
|       node => $node, #this seems redundant, but some code will not be privy to what the key was
 | |
|       ipmisession => xCAT::IPMI->new(bmc=>$bmcip,userid=>$user,password=>$pass),
 | |
|       command => $command,
 | |
|       extraargs => \@exargs,
 | |
|       subcommand => $exargs[0],
 | |
|   };
 | |
|   if ($sessiondata{$node}->{ipmisession}->{error}) {
 | |
|       sendmsg([1,$sessiondata{$node}->{ipmisession}->{error}],$node);
 | |
|   } else {
 | |
|     my ($rc,@output) = ipmicmd($sessiondata{$node});
 | |
|     sendoutput($rc,@output);
 | |
|     yield;
 | |
|     return $rc;
 | |
|   }
 | |
|   #my $msgtoparent=freeze(\@outhashes);
 | |
|  # print $outfd $msgtoparent;
 | |
| }
 | |
| 
 | |
| sub sendmsg {
 | |
| #    my $callback = $output_handler;
 | |
|     my $text = shift;
 | |
|     my $node = shift;
 | |
|     my $descr;
 | |
|     my $rc;
 | |
|     if (ref $text eq 'HASH') {
 | |
|         die "not right now";
 | |
|     } elsif (ref $text eq 'ARRAY') {
 | |
|         $rc = $text->[0];
 | |
|         $text = $text->[1];
 | |
|     }
 | |
|     if ($text =~ /:/) {
 | |
|         ($descr,$text) = split /:/,$text,2;
 | |
|     }
 | |
|     $text =~ s/^ *//;
 | |
|     $text =~ s/ *$//;
 | |
|     my $msg;
 | |
|     my $curptr;
 | |
|     if ($node) {
 | |
|         $msg->{node}=[{name => [$node]}];
 | |
|         $curptr=$msg->{node}->[0];
 | |
|     } else {
 | |
|         $msg = {};
 | |
|         $curptr = $msg;
 | |
|     }
 | |
|     if ($rc) {
 | |
|         $curptr->{errorcode}=[$rc];
 | |
|         $curptr->{error}=[$text];
 | |
|         $curptr=$curptr->{error}->[0];
 | |
|         if (defined $node) {
 | |
|             $allerrornodes{$node}=1;
 | |
|         }
 | |
|     } else {
 | |
|         $curptr->{data}=[{contents=>[$text]}];
 | |
|         $curptr=$curptr->{data}->[0];
 | |
|         if ($descr) { $curptr->{desc}=[$descr]; }
 | |
|     }
 | |
| #        print $outfd freeze([$msg]);
 | |
| #        print $outfd "\nENDOFFREEZE6sK4ci\n";
 | |
| #        yield;
 | |
| #        waitforack($outfd);
 | |
|     $callback->($msg);
 | |
| }
 | |
| 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]="BADCODE";
 | |
|         if ($rc) {
 | |
|           $output{node}->[0]->{errorcode}=[$rc];
 | |
|             $output{node}->[0]->{error}->[0]=$text;
 | |
|         } else {
 | |
|             $output{node}->[0]->{data}->[0]->{contents}->[0]=$text;
 | |
|         }
 | |
|         $callback->(\%output);
 | |
|         #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;
 |