mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-31 03:12:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			9093 lines
		
	
	
		
			324 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			9093 lines
		
	
	
		
			324 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
 | |
| # VIM: set tabstop=8 softtabstop=0 expandtab shiftwidth=4 smarttab
 | |
| #
 | |
| 
 | |
| 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 xCAT::PasswordUtils;
 | |
| use File::Basename;
 | |
| my %needbladeinv;
 | |
| 
 | |
| use POSIX qw(ceil floor);
 | |
| use Storable qw(nstore_fd retrieve_fd thaw freeze);
 | |
| use Scalar::Util qw(looks_like_number);
 | |
| use xCAT::Utils;
 | |
| use xCAT::TableUtils;
 | |
| use xCAT::IMMUtils;
 | |
| use xCAT::ServiceNodeUtils;
 | |
| use xCAT::SvrUtils;
 | |
| use xCAT::NetworkUtils;
 | |
| use xCAT::Usage;
 | |
| use File::Path;
 | |
| use File::Spec;
 | |
| 
 | |
| use Thread qw(yield);
 | |
| use LWP 5.64;
 | |
| use HTTP::Request::Common;
 | |
| use Time::HiRes qw/time/;
 | |
| my $iem_support;
 | |
| my $vpdhash;
 | |
| my %allerrornodes = ();
 | |
| my %newnodestatus = ();
 | |
| my $global_sessdata;
 | |
| my %child_pids;
 | |
| my $xcatdebugmode = 0;
 | |
| 
 | |
| my $IPMIXCAT  = "/opt/xcat/bin/ipmitool-xcat";
 | |
| my $NON_BLOCK = 1;
 | |
| use constant RFLASH_LOG_DIR => "/var/log/xcat/rflash";
 | |
| if (-d RFLASH_LOG_DIR) {
 | |
|     chmod 0700, RFLASH_LOG_DIR;
 | |
| } else {
 | |
|     mkpath(RFLASH_LOG_DIR, 0, 0700);
 | |
| }
 | |
| 
 | |
| require xCAT::data::ibmhwtypes;
 | |
| 
 | |
| 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:mgt',          #support renergy for OperPOWER servers
 | |
|         getipmicons => 'ipmi',            #done
 | |
|         rspconfig   => 'nodehm:mgt',      #done
 | |
|         rspreset    => 'nodehm:mgt',      #done
 | |
|         rvitals     => 'nodehm:mgt',      #done
 | |
|         rinv        => 'nodehm:mgt',      #done
 | |
|         rflash      => 'nodehm:mgt',      #done
 | |
|         rsetboot    => 'nodehm:mgt',      #done
 | |
|         rbeacon     => 'nodehm:mgt',      #done
 | |
|         reventlog   => 'nodehm:mgt',
 | |
|         ripmi       => 'ipmi',
 | |
| 
 | |
|         #    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
 | |
|         rscan => 'nodehm:mgt', # used to scan the mic cards installed on the target node
 | |
|       }
 | |
| }
 | |
| 
 | |
| use POSIX "WNOHANG";
 | |
| use IO::Handle;
 | |
| use IO::Socket;
 | |
| use IO::Select;
 | |
| use Class::Struct;
 | |
| use Digest::MD5 qw(md5);
 | |
| use POSIX qw(WNOHANG mkfifo strftime);
 | |
| use Fcntl qw(:flock);
 | |
| 
 | |
| #local to module
 | |
| my $callback;
 | |
| my $ipmi_bmcipaddr;
 | |
| my $timeout;
 | |
| my $port;
 | |
| my $debug;
 | |
| my $ndebug = 0;
 | |
| 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 = 4;
 | |
| 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";
 | |
| 
 | |
| # Firmware data hash
 | |
| 
 | |
| my %hpm_data_hash     = ();
 | |
| 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",
 | |
|     20301 => "IBM",
 | |
|     19046 => "Lenovo",
 | |
| );
 | |
| 
 | |
| 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 => '$',
 | |
|     fru_oem     => '$',
 | |
| };
 | |
| 
 | |
| 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]);
 | |
|     $sdr->fru_oem($locator[15]);
 | |
| 
 | |
|     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};
 | |
|     if ($status =~ /ERROR:/) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $status ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     #ok, detect some common prereqs here, notably:
 | |
|     #getdevid
 | |
|     if ($command eq "getrvidparms" or $command eq "rflash") {
 | |
|         unless (defined $sessdata->{device_id}) {
 | |
|             $sessdata->{ipmisession}->subcmd(netfn => 6, command => 1, data => [], callback => \&gotdevid, callback_args => $sessdata);
 | |
|             return;
 | |
|         }
 | |
|         if ($command eq "getrvidparms") {
 | |
|             getrvidparms($sessdata);
 | |
|         } else {
 | |
|             rflash($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") {
 | |
|         xCAT::SvrUtils::sendmsg("ping", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     if ($command eq "rpower") {
 | |
|         unless ($sessdata->{subcommand} eq "reseat" or defined $sessdata->{device_id}) { #need get device id data initted for S3 support
 | |
|             $sessdata->{ipmisession}->subcmd(netfn => 6, command => 1, data => [], callback => \&gotdevid, callback_args => $sessdata);
 | |
|             return;
 | |
|         }
 | |
|         return power($sessdata);
 | |
|     } elsif ($command eq "ripmi") {
 | |
|         return ripmi($sessdata);
 | |
|     } elsif ($command eq "rspreset") {
 | |
|         return resetbmc($sessdata);
 | |
|     } elsif ($command eq "rbeacon") {
 | |
|         unless (defined $sessdata->{device_id}) { #need get device id data initted for SD350 workaround
 | |
|             $sessdata->{ipmisession}->subcmd(netfn => 6, command => 1, data => [], callback => \&gotdevid, callback_args => $sessdata);
 | |
|             return;
 | |
|         }
 | |
|         return beacon($sessdata);
 | |
|     } elsif ($command eq "rsetboot") {
 | |
|         return setboot($sessdata);
 | |
|     } elsif ($command eq "rspconfig") {
 | |
|         shift @{ $sessdata->{extraargs} };
 | |
| 
 | |
|         # set username or/and password for specified userid #
 | |
|         if ($sessdata->{subcommand} =~ /userid|username|password/) {
 | |
|             setpassword($sessdata);
 | |
|         } else {
 | |
|             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") {
 | |
|         unless (defined $sessdata->{device_id}) {
 | |
|             $sessdata->{ipmisession}->subcmd(netfn => 6, command => 1, data => [], callback => \&gotdevid, callback_args => $sessdata);
 | |
|             return;
 | |
|         }
 | |
|         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}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|     } else {
 | |
|         if ($rsp->{code}) {
 | |
|             if ($codes{ $rsp->{code} }) {
 | |
|                 xCAT::SvrUtils::sendmsg([ 1, $codes{ $rsp->{code} } ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             } else {
 | |
|                 xCAT::SvrUtils::sendmsg([ 1, sprintf("Unknown error %02xh", $rsp->{code}) ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             }
 | |
|             return;
 | |
|         }
 | |
|         xCAT::SvrUtils::sendmsg("BMC reset", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         $sessdata->{ipmisession} = undef;    #throw away now unusable session
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| sub setpassword {
 | |
|     my $sessdata = shift;
 | |
|     my $subcmd   = $sessdata->{subcommand};
 | |
|     my $netfun   = 0x06;
 | |
|     my $command  = undef;
 | |
|     my ($subcommand, $argument) = split(/=/, $subcmd);
 | |
|     if ($subcommand eq "userid") {
 | |
|         if ($argument !~ /^\d+$/) {
 | |
|             return (1, "The value for $subcommand is invalid");
 | |
|         }
 | |
|         $sessdata->{onuserid}   = $argument;
 | |
|         $sessdata->{subcommand} = shift @{ $sessdata->{extraargs} };
 | |
|         if ($sessdata->{subcommand} and $sessdata->{subcommand} ne '') {
 | |
|             setpassword($sessdata);
 | |
|         }
 | |
|     } elsif ($subcommand eq "username") {
 | |
|         unless ($sessdata->{onuserid}) {
 | |
|             my @cmdargv = @{ $sessdata->{extraargs} };
 | |
|             if (grep /userid/, @cmdargv) {
 | |
|                 foreach my $opt (@cmdargv) {
 | |
|                     if ($opt =~ m/userid=\s*(\d+)/) {
 | |
|                         $sessdata->{onuserid} = $1;
 | |
|                         last;
 | |
|                     } else {
 | |
|                         return (1, "The value for $subcommand is invalid");
 | |
|                     }
 | |
|                 }
 | |
|             } else {
 | |
|                 $sessdata->{onuserid} = '2'; # The default userid will be 2 if no userid specified
 | |
|             }
 | |
|         }
 | |
|         my @data = ();
 | |
|         $command = 0x45;
 | |
|         @data = unpack("C*", $argument);
 | |
|         if ($#data > 15 or $#data < 0) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, "The username is invalid" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             return (1, "The username is invalid.");
 | |
|         }
 | |
|         $sessdata->{newusername} = $argument;
 | |
|         my $index = $#data;
 | |
|         while ($index < 15) { # Username must be padded with 0 to 16 bytes for IPMI 2.0
 | |
|             push @data, 0;
 | |
|             $index += 1;
 | |
|         }
 | |
|         unshift @data, $sessdata->{onuserid} - '0'; # byte 1, userID, User ID 1 is permanently associated with User 1, the null user name
 | |
|         unless (exists($sessdata->{setuseraccess})) {
 | |
|             $sessdata->{setuseraccess} = 1;
 | |
|         }
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => $netfun, command => $command, data => \@data, callback => \&password_set, callback_args => $sessdata);
 | |
|     } elsif ($subcommand eq "password") {
 | |
|         unless ($sessdata->{onuserid}) {
 | |
|             my @cmdargv = @{ $sessdata->{extraargs} };
 | |
|             if (grep /userid/, @cmdargv) {
 | |
|                 foreach my $opt (@cmdargv) {
 | |
|                     if ($opt =~ m/userid=\s*(\d+)/) {
 | |
|                         $sessdata->{onuserid} = $1;
 | |
|                         last;
 | |
|                     } else {
 | |
|                         return (1, "The value for $subcommand is invalid");
 | |
|                     }
 | |
|                 }
 | |
|             } else {
 | |
| 
 | |
|                 # If have username specified, if has been dealt will be store in newusername, else need to check the args array. And the default userid must be 2.
 | |
|                 if (exists($sessdata->{newusername}) or (grep /username/, @{ $sessdata->{extraargs} })) {
 | |
|                     $sessdata->{onuserid} = '2';
 | |
|                 } else {
 | |
| 
 | |
|                     # If No username specified, the default userid will be 1.
 | |
|                     $sessdata->{onuserid} = '1';
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         $command = 0x47;
 | |
|         my @data = ();
 | |
|         @data = unpack("C*", $argument);
 | |
|         if ($#data > 15 or $#data < 0) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, "The new password is invalid" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             return (1, "The new password is invalid.");
 | |
|         }
 | |
|         $sessdata->{newpassword} = $argument;
 | |
|         my $index = $#data;
 | |
|         while ($index < 15) { # Password must be padded with 0 to 20 bytes for IPMI 2.0
 | |
|             push @data, 0;
 | |
|             $index += 1;
 | |
|         }
 | |
|         unless (exists($sessdata->{enableuserdone})) {
 | |
|             unshift @data, 0x01; # byte 2, operation: 0x00 disable user, 0x01 enable user, 0x02 set password, 0x03 test password
 | |
|             unshift @data, ($sessdata->{onuserid} - '0'); # byte 1, userID, User ID 1 is permanently associated with User 1, the null user name
 | |
|             $sessdata->{enableuserdone} = 1;
 | |
|             $sessdata->{ipmisession}->subcmd(netfn => $netfun, command => $command, data => \@data, callback => \&password_set, callback_args => $sessdata);
 | |
|         } else {
 | |
|             unshift @data, 0x02; # byte 2, operation: 0x00 disable user, 0x01 enable user, 0x02 set password, 0x03 test password
 | |
|             unshift @data, ($sessdata->{onuserid} - '0'); # byte 1, userID, User ID 1 is permanently associated with User 1, the null user name
 | |
|             unless (exists($sessdata->{setuseraccess})) {
 | |
|                 $sessdata->{setuseraccess} = 1;
 | |
|             }
 | |
|             $sessdata->{ipmisession}->subcmd(netfn => $netfun, command => $command, data => \@data, callback => \&password_set, callback_args => $sessdata);
 | |
|         }
 | |
|     }
 | |
|     return;
 | |
| }
 | |
| 
 | |
| sub password_set {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     if ($rsp->{error}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     if ($rsp->{code}) {
 | |
|         if ($codes{ $rsp->{code} }) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, $codes{ $rsp->{code} } ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         } else {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, sprintf("Unknown ipmi error %02xh", $rsp->{code}) ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     if ($sessdata->{setuseraccess} and $sessdata->{onuserid} ne '1' and $sessdata->{onuserid} ne '2') {
 | |
|         setuseraccess($sessdata);
 | |
|         return;
 | |
|     }
 | |
|     if ($sessdata->{enableuserdone}) {
 | |
|         $sessdata->{enableuserdone} = 0;
 | |
|         setpassword($sessdata);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # Won't update ipmi table, since option username=<> and password=<> can be run Respectively
 | |
|     #my $ipmitab = xCAT::Table->new('ipmi');
 | |
|     #if (!$ipmitab)  {
 | |
|     #    xCAT::SvrUtils::sendmsg([1, "Failed to update ipmi table."],$callback,$sessdata->{node},%allerrornodes);
 | |
|     #} else {
 | |
|     #    if ($sessdata->{subcommand} =~ m/username/) {
 | |
|     #        $ipmitab->setNodeAttribs($sessdata->{node}, {'username'=>$sessdata->{newusername}});
 | |
|     #        xCAT::SvrUtils::sendmsg("set username Done",$callback,$sessdata->{node},%allerrornodes);
 | |
|     #    } elsif ($sessdata->{subcommand} =~  m/password/) {
 | |
|     #        $ipmitab->setNodeAttribs($sessdata->{node}, {'password'=>$sessdata->{newpassword}});
 | |
|     #        xCAT::SvrUtils::sendmsg("set password Done",$callback,$sessdata->{node},%allerrornodes);
 | |
|     #    }
 | |
|     #}
 | |
| 
 | |
|     # Give out the return complete MSG
 | |
|     if ($sessdata->{subcommand} =~ m/username/) {
 | |
|         xCAT::SvrUtils::sendmsg("set username Done", $callback, $sessdata->{node}, %allerrornodes);
 | |
|     } elsif ($sessdata->{subcommand} =~ m/password/) {
 | |
|         xCAT::SvrUtils::sendmsg("set password Done", $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
| 
 | |
|     $sessdata->{subcommand} = shift @{ $sessdata->{extraargs} };
 | |
|     if ($sessdata->{subcommand} and $sessdata->{subcommand} ne '') {
 | |
|         setpassword($sessdata);
 | |
|     }
 | |
|     return;
 | |
| }
 | |
| 
 | |
| sub setuseraccess {
 | |
|     my $sessdata       = shift;
 | |
|     my $channel_number = $sessdata->{ipmisession}->{currentchannel};
 | |
|     unless ($sessdata->{onuserid}) {
 | |
|         return;
 | |
|     }
 | |
|     $sessdata->{setuseraccess} = 0;
 | |
|     my @data = ();
 | |
|     push @data, (0xd0 | $channel_number);
 | |
|     push @data, ($sessdata->{onuserid} - '0');
 | |
|     push @data, 0x04;
 | |
|     push @data, 0;
 | |
| 
 | |
|     $sessdata->{ipmisession}->subcmd(netfn => 0x06, command => 0x43, data => \@data, callback => \&password_set, callback_args => $sessdata);
 | |
|     return;
 | |
| }
 | |
| 
 | |
| sub next_setnetinfo {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     &setnetinfo($sessdata);
 | |
| }
 | |
| 
 | |
| sub setnetinfo {
 | |
|     my $sessdata   = shift;
 | |
|     my $subcommand = $sessdata->{subcommand};
 | |
|     my $argument;
 | |
|     ($subcommand, $argument) = split(/=/, $subcommand);
 | |
|     my @input = @_;
 | |
| 
 | |
|     my $netfun = 0x0c;
 | |
|     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 = 0x4;
 | |
|         @cmd = (0x12, 0x9, 0x1, 0x18, 0x11, 0x00);
 | |
|     } elsif ($subcommand eq "alert" and $argument eq "off" or $argument =~ /^dis/ or $argument =~ /^disable/) {
 | |
|         $netfun = 0x4;
 | |
|         @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 =~ m/netmask/) {
 | |
|         if ($argument =~ /\./) {
 | |
|             my @mask = split /\./, $argument;
 | |
|             foreach (0 .. 3) {
 | |
|                 $mask[$_] = $mask[$_] + 0;
 | |
|             }
 | |
|             @cmd = (0x01, $channel_number, 0x6, @mask);
 | |
|         }
 | |
|     } elsif ($subcommand =~ m/gateway/) {
 | |
|         my $gw = inet_ntoa(inet_aton($argument));
 | |
|         my @mask = split /\./, $gw;
 | |
|         foreach (0 .. 3) {
 | |
|             $mask[$_] = $mask[$_] + 0;
 | |
|         }
 | |
|         @cmd = (0x01, $channel_number, 12, @mask);
 | |
|     } elsif ($subcommand =~ m/vlan/) {
 | |
|         unless ( int($argument) == $argument ) {
 | |
|             $callback->({ errorcode => [1], error => ["The input $argument is invalid, please input an integer"] });
 | |
|             return;
 | |
|         }
 | |
|         unless (($argument>=1) && ($argument<=4096)) {
 | |
|             $callback->({ errorcode => [1], error => ["The input $argument is invalid, valid value [1-4096]"] });
 | |
|             return;
 | |
|         }
 | |
|         my @vlanparam = ();
 | |
|         $vlanparam[0] = ($argument & 0xff);
 | |
|         $vlanparam[1] = 0x80 | (($argument & 0xf00) >> 8);
 | |
|         @cmd = (0x01, $channel_number, 0x14, @vlanparam);
 | |
|     } elsif ($subcommand =~ m/ip/ and $argument =~ m/dhcp/) {
 | |
|         @cmd = (0x01, $channel_number, 0x4, 0x2);
 | |
|     } elsif ($subcommand =~ m/ip/) {
 | |
| 
 | |
|         # Need to set ipsrc to static for IBM POWER S822LC and S812LC
 | |
|         if (exists($sessdata->{netinfo_setinprogress})) {
 | |
|             unless ($sessdata->{set_ipsrc_static}) {
 | |
|                 $sessdata->{set_ipsrc_static} = 1;
 | |
|                 $sessdata->{ipmisession}->subcmd(netfn => $netfun, command => 0x01, data => [ $channel_number, 0x04, 0x1 ], callback => \&next_setnetinfo, callback_args => $sessdata);
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|         my $mip = inet_ntoa(inet_aton($argument));
 | |
|         my @mask = split /\./, $mip;
 | |
|         foreach (0 .. 3) {
 | |
|             $mask[$_] = $mask[$_] + 0;
 | |
|         }
 | |
|         @cmd = (0x01, $channel_number, 0x3, @mask);
 | |
|     }
 | |
| 
 | |
|     #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");
 | |
|     }
 | |
|     unless ($sessdata->{netinfo_setinprogress}) {
 | |
|         $sessdata->{netinfo_setinprogress} = '1';
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => $netfun, command => 0x01, data => [ $channel_number, 0x0, 0x1 ], callback => \&next_setnetinfo, callback_args => $sessdata);
 | |
|         return;
 | |
|     }
 | |
|     my $command = shift @cmd;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn => $netfun, command => $command, data => \@cmd, callback => \&netinfo_set, callback_args => $sessdata);
 | |
| }
 | |
| 
 | |
| 
 | |
| sub netinfo_set {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     if ($sessdata->{netinfo_setinprogress}) {
 | |
|         my $channel_number = $sessdata->{ipmisession}->{currentchannel};
 | |
|         delete $sessdata->{netinfo_setinprogress};
 | |
|         $sessdata->{rsp}->{error} = $rsp->{error};
 | |
|         $sessdata->{rsp}->{code}  = $rsp->{code};
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0x0c, command => 0x01, data => [ $channel_number, 0x0, 0x0 ], callback => \&netinfo_set, callback_args => $sessdata);
 | |
|     } else {
 | |
|         $rsp = $sessdata->{rsp};
 | |
|     }
 | |
|     if ($rsp->{error}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     if ($rsp->{code}) {
 | |
|         if ($codes{ $rsp->{code} }) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, $codes{ $rsp->{code} } ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         } else {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, sprintf("Unknown ipmi error %02xh", $rsp->{code}) ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     if ($sessdata->{subcommand} =~ m/^ip=dhcp/) {
 | |
|         $sessdata->{subcommand} = shift @{ $sessdata->{extraargs} };
 | |
|         if (!defined($sessdata->{subcommand}) or $sessdata->{subcommand} eq '') {
 | |
|             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 = 0x4;
 | |
|         @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 "vlan") {
 | |
|         @cmd = (0x02, $channel_number, 0x14, 0x00, 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};
 | |
|     $subcommand =~ s/=.*//;
 | |
|     $sessdata->{subcommand} = shift @{ $sessdata->{extraargs} };
 | |
|     if ($rsp->{error}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     if ($rsp->{code}) {
 | |
|         if ($codes{ $rsp->{code} }) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, $codes{ $rsp->{code} } ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         } else {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, sprintf("Unknown ipmi error %02xh", $rsp->{code}) ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     if ($subcommand eq "snmpdest") {
 | |
|         $subcommand = "snmpdest1";
 | |
|     }
 | |
|     my $bmcifo = "";
 | |
|     if ($sessdata->{bmcnum} != 1) { $bmcifo .= " on BMC " . $sessdata->{bmcnum}; }
 | |
|     my @returnd = (0, @{ $rsp->{data} });
 | |
|     my $format = "%-25s";
 | |
|     if ($subcommand eq "garp") {
 | |
|         my $code = $returnd[2] / 2;
 | |
|         xCAT::SvrUtils::sendmsg(sprintf("$format %d", "Gratuitous ARP seconds:", $code), $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
|     elsif ($subcommand eq "alert") {
 | |
|         if ($returnd[3] & 0x8) {
 | |
|             xCAT::SvrUtils::sendmsg("SP Alerting: enabled" . $bmcifo, $callback, $sessdata->{node}, %allerrornodes);
 | |
|         } else {
 | |
|             xCAT::SvrUtils::sendmsg("SP Alerting: disabled" . $bmcifo, $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|     }
 | |
|     elsif ($subcommand =~ m/^snmpdest(\d+)/) {
 | |
|         xCAT::SvrUtils::sendmsg(sprintf("$format %d.%d.%d.%d" . $bmcifo,
 | |
|                 "SP SNMP Destination $1:",
 | |
|                 $returnd[5],
 | |
|                 $returnd[6],
 | |
|                 $returnd[7],
 | |
|                 $returnd[8]), $callback, $sessdata->{node}, %allerrornodes);
 | |
|     } elsif ($subcommand eq "vlan") {
 | |
|         if (($returnd[3] >> 7) & 0x01) {
 | |
|             xCAT::SvrUtils::sendmsg(sprintf("$format %d" . $bmcifo,
 | |
|                 "BMC VLAN ID enabled:",
 | |
|                 (($returnd[3] & 0x0f) << 8) | $returnd[2]), $callback, $sessdata->{node}, %allerrornodes);
 | |
|         } else {
 | |
|             xCAT::SvrUtils::sendmsg("BMC VLAN disabled" . $bmcifo, $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|     } elsif ($subcommand eq "ip") {
 | |
|         xCAT::SvrUtils::sendmsg(sprintf("$format %d.%d.%d.%d" . $bmcifo,
 | |
|                 "BMC IP:",
 | |
|                 $returnd[2],
 | |
|                 $returnd[3],
 | |
|                 $returnd[4],
 | |
|                 $returnd[5]), $callback, $sessdata->{node}, %allerrornodes);
 | |
|     } elsif ($subcommand eq "netmask") {
 | |
|         xCAT::SvrUtils::sendmsg(sprintf("$format %d.%d.%d.%d" . $bmcifo,
 | |
|                 "BMC Netmask:",
 | |
|                 $returnd[2],
 | |
|                 $returnd[3],
 | |
|                 $returnd[4],
 | |
|                 $returnd[5]), $callback, $sessdata->{node}, %allerrornodes);
 | |
|     } elsif ($subcommand eq "gateway") {
 | |
|         xCAT::SvrUtils::sendmsg(sprintf("$format %d.%d.%d.%d" . $bmcifo,
 | |
|                 "BMC Gateway:",
 | |
|                 $returnd[2],
 | |
|                 $returnd[3],
 | |
|                 $returnd[4],
 | |
|                 $returnd[5]), $callback, $sessdata->{node}, %allerrornodes);
 | |
|     } elsif ($subcommand eq "backupgateway") {
 | |
|         xCAT::SvrUtils::sendmsg(sprintf("$format %d.%d.%d.%d" . $bmcifo,
 | |
|                 "BMC Backup Gateway:",
 | |
|                 $returnd[2],
 | |
|                 $returnd[3],
 | |
|                 $returnd[4],
 | |
|                 $returnd[5]), $callback, $sessdata->{node}, %allerrornodes);
 | |
|     } 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;
 | |
|         }
 | |
|         $text .= $bmcifo;
 | |
|         xCAT::SvrUtils::sendmsg($text, $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
|     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;
 | |
|     if ($rsp->{error}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     if ($rsp->{code}) {
 | |
|         if ($codes{ $rsp->{code} }) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, $codes{ $rsp->{code} } ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             return;
 | |
|         } elsif ($rsp->{code} == 0x80) {
 | |
| 
 | |
|             #xCAT::SvrUtils::sendmsg("Unable to disable countdown timer, boot device may revert in 60 seconds",$callback,$sessdata->{node},%allerrornodes);
 | |
|         } else {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, sprintf("Unknown ipmi error %02xh", $rsp->{code}) ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     my $subcommand = undef;
 | |
|     if (@{ $sessdata->{extraargs} }) {
 | |
|         $subcommand = @{ $sessdata->{extraargs} }[0];
 | |
|     }
 | |
|     if (!defined($subcommand) or $subcommand =~ m/^stat/) {
 | |
|         setboot_stat("NOQUERY", $sessdata);
 | |
|         return;
 | |
|     } elsif ($subcommand !~ /net|hd|cd|floppy|def|setup/) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "unsupported command setboot $subcommand" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|     } else {
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0, command => 8, data => [ 0, 1 ], callback => \&setboot_setstart, callback_args => $sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub setboot_setstart {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     if ($rsp->{code}) {
 | |
|         if ($codes{ $rsp->{code} }) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, $codes{ $rsp->{code} } ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             return;
 | |
|         } elsif ($rsp->{code} == 0x80) {
 | |
|             xCAT::SvrUtils::sendmsg("Unable to set boot param", $callback, $sessdata->{node}, %allerrornodes);
 | |
|             return;
 | |
|         } elsif ($rsp->{code} == 0x81) {
 | |
|             xCAT::SvrUtils::sendmsg("Attemp to set the 'set in process' value", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     my $error;
 | |
|     @ARGV = @{ $sessdata->{extraargs} };
 | |
|     my $persistent = 0;
 | |
|     my $uefi       = 0;
 | |
|     use Getopt::Long;
 | |
|     unless (GetOptions(
 | |
|             'p' => \$persistent,
 | |
|             'u' => \$uefi,
 | |
|         )) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Error parsing arguments" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     my $subcommand = shift @ARGV;
 | |
| 
 | |
|     my @cmd;
 | |
|     my $overbootflags = 0x80 | $persistent << 6 | $uefi << 5;
 | |
|     if ($subcommand eq "net") {
 | |
|         @cmd = (0x5, $overbootflags, 0x4, 0x0, 0x0, 0x0);
 | |
|     }
 | |
|     elsif ($subcommand eq "hd") {
 | |
|         @cmd = (0x5, $overbootflags, 0x8, 0x0, 0x0, 0x0);
 | |
|     }
 | |
|     elsif ($subcommand eq "cd") {
 | |
|         @cmd = (0x5, $overbootflags, 0x14, 0x0, 0x0, 0x0);
 | |
|     }
 | |
|     elsif ($subcommand eq "floppy") {
 | |
|         @cmd = (0x5, $overbootflags, 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, $overbootflags, 0x18, 0x0, 0x0, 0x0);
 | |
|     }
 | |
|     elsif ($subcommand =~ m/^stat/) {
 | |
|         setboot_stat("NOQUERY", $sessdata);
 | |
|         return;
 | |
|     }
 | |
|     else {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "unsupported command setboot $subcommand" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
|     $sessdata->{ipmisession}->subcmd(netfn => 0, command => 8, data => \@cmd, callback => \&setboot_done, callback_args => $sessdata);
 | |
| }
 | |
| 
 | |
| sub setboot_done {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     if (ref $rsp) {
 | |
|         if ($rsp->{error}) { xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes); }
 | |
|         elsif ($rsp->{code}) {
 | |
|             if ($codes{ $rsp->{code} }) {
 | |
|                 xCAT::SvrUtils::sendmsg([ 1, $codes{ $rsp->{code} } ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             } else {
 | |
|                 xCAT::SvrUtils::sendmsg([ 1, sprintf("setboot_stat Unknown ipmi error %02xh", $rsp->{code}) ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             }
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     $sessdata->{ipmisession}->subcmd(netfn => 0, command => 8, data => [ 0, 0 ], callback => \&setboot_stat, callback_args => $sessdata);
 | |
| 
 | |
| }
 | |
| 
 | |
| sub setboot_stat {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     if (ref $rsp) {
 | |
|         if ($rsp->{error}) { xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes); }
 | |
|         elsif ($rsp->{code}) {
 | |
|             if ($codes{ $rsp->{code} }) {
 | |
|                 xCAT::SvrUtils::sendmsg([ 1, $codes{ $rsp->{code} } ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             } else {
 | |
|                 xCAT::SvrUtils::sendmsg([ 1, sprintf("Unknown ipmi error %02xh", $rsp->{code}) ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             }
 | |
|             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}) { xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes); }
 | |
|     elsif ($rsp->{code}) {
 | |
|         if ($codes{ $rsp->{code} }) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, $codes{ $rsp->{code} } ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         } else {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, sprintf("Unknown ipmi error %02xh", $rsp->{code}) ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|         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) {
 | |
|         xCAT::SvrUtils::sendmsg("boot override inactive", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     my $boot = ($returnd[4] & 0x3C) >> 2;
 | |
|     xCAT::SvrUtils::sendmsg($bootchoices{$boot}, $callback, $sessdata->{node}, %allerrornodes);
 | |
|     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 is_systemx {
 | |
|     my $sessdata = shift;
 | |
|     return ($sessdata->{mfg_id} == 20301 or $sessdata->{mfg_id} == 2 or $sessdata->{mfg_id} == 19046);
 | |
| }
 | |
| 
 | |
| sub getrvidparms {
 | |
|     my $sessdata = shift;
 | |
|     unless ($sessdata) { die "not fixed yet" }
 | |
| 
 | |
|     #check devide id
 | |
|     if ($sessdata->{mfg_id} == 20301 and $sessdata->{prod_id} == 220) {
 | |
|         my $browser = LWP::UserAgent->new();
 | |
|         my $message = "WEBVAR_USERNAME=" . $sessdata->{ipmisession}->{userid} . "&WEBVAR_PASSWORD=" . $sessdata->{ipmisession}->{password};
 | |
|         $browser->cookie_jar({});
 | |
|         my $baseurl = "https://" . $sessdata->{ipmisession}->{bmc} . "/";
 | |
|         my $response = $browser->request(GET $baseurl. "rpc/WEBSES/validate.asp");
 | |
|         $response = $browser->request(POST $baseurl. "rpc/WEBSES/create.asp", 'Content-Type' => "application/x-www-form-urlencoded", Content => $message);
 | |
|         $response = $response->content;
 | |
|         if ($response and $response =~ /SESSION_COOKIE' : '([^']*)'/) {
 | |
| 
 | |
|             foreach (keys %{ $browser->cookie_jar->{COOKIES} }) {
 | |
|                 $browser->cookie_jar()->set_cookie(1, "SessionCookie", $1, "/", $_);
 | |
|             }
 | |
|         }
 | |
|         $response = $browser->request(GET $baseurl. "/Java/jviewer.jnlp?ext_ip=" . $sessdata->{ipmisession}->{bmc});
 | |
|         $response = $response->content;
 | |
|         xCAT::SvrUtils::sendmsg("method:imm", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         xCAT::SvrUtils::sendmsg("jnlp:$response", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     unless ($sessdata->{mfg_id} == 2 or $sessdata->{mfg_id} == 01) { #Only implemented for IBM servers
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Remote video is not supported on this system" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         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
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return 1;
 | |
|     }
 | |
|     if ($rsp->{code}) {     #ipmi error
 | |
|         if ($codes{ $rsp->{code} }) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, $codes{ $rsp->{code} } ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         } else {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, sprintf("Unknown error code %02xh", $rsp->{code}) ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|         return 1;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| sub getrvidparms_imm2 {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
| 
 | |
|     #wvid should be a possiblity, time to do the http...
 | |
|     $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0; #TODO: for standalone IMMs, automate CSR retrieval and granting at setup itme
 | |
|       #for flex, grab the CA from each CMM and store in a way accessible to this command
 | |
|       #for now, accept the MITM risk no worse than http, the intent being feature parity
 | |
|       #with http: for envs with https only, like Flex
 | |
|     my $browser = LWP::UserAgent->new();
 | |
|     if ($sessdata->{ipmisession}->{bmc} =~ /^fe80/ and $sessdata->{ipmisession}->{bmc} =~ /%/) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "wvid not supported with IPv6 LLA addressing mode" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     my $host = $sessdata->{ipmisession}->{bmc};
 | |
|     my $hostname;
 | |
|     ($hostname, $host) = xCAT::NetworkUtils->gethostnameandip($host);
 | |
|     my $ip6mode = 0;
 | |
|     if ($host =~ /:/) { $ip6mode = 1; $host = "[" . $host . "]"; }
 | |
|     my $message = "user=" . $sessdata->{ipmisession}->{userid} . "&password=" . $sessdata->{ipmisession}->{password} . "&SessionTimeout=1200";
 | |
|     $browser->cookie_jar({});
 | |
|     my $httpport = 443;
 | |
|     my $baseurl  = "https://$host/";
 | |
|     my $response = $browser->request(POST $baseurl. "data/login", Referer => "https://$host/designs/imm/index.php", 'Content-Type' => "application/x-www-form-urlencoded", Content => $message);
 | |
| 
 | |
|     if ($response->code == 500) {
 | |
|         $httpport = 80;
 | |
|         $baseurl  = "http://$host/";
 | |
|         $response = $browser->request(POST $baseurl. "data/login", Referer => "http://$host/designs/imm/index.php", 'Content-Type' => "application/x-www-form-urlencoded", Content => $message);
 | |
|     }
 | |
|     my $sessionid;
 | |
|     unless ($response->content =~ /\"ok\"?(.*)/ and $response->content =~ /\"authResult\":\"0\"/) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Server returned unexpected data" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     $response = $browser->request(GET $baseurl. "/designs/imm/remote-control.php");
 | |
|     if ($response->content =~ /isRPInstalled\s*=\s*'0'/) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Node does not have feature key for remote video" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         $response = $browser->request(GET $baseurl. "data/logout");
 | |
|         return;
 | |
|     }
 | |
|     $response = $browser->request(GET $baseurl. "designs/imm/viewer(" . $host . '@' . $ip6mode . '@' . time() . '@1@0@1@jnlp)');
 | |
| 
 | |
|     #arguments are host, then ipv6 or not, then timestamp, then whether to encrypte or not, singleusermode, finally 'notwin32'
 | |
|     my $jnlp = $response->content;
 | |
|     unless ($jnlp) {    #ok, might be the newer syntax...
 | |
|         $response = $browser->request(GET $baseurl. "designs/imm/viewer(" . $host . '@' . $httpport . '@' . $ip6mode . '@' . time() . '@1@0@1@jnlp' . '@USERID@0@0@0@0' . ')');
 | |
| 
 | |
|         #arguments are host, then ipv6 or not, then timestamp, then whether to encrypte or not, singleusermode, finally 'notwin32'
 | |
|         $jnlp = $response->content;
 | |
|         if ($jnlp =~ /Failed to parse ip format for request/) {
 | |
|             $response = $browser->request(GET $baseurl. "designs/imm/viewer(" . $host . '@' . $httpport . '@' . $ip6mode . '@' . time() . '@1@0@1@jnlp' . '@USERID@0@0@0@0@0' . ')');
 | |
| 
 | |
|             #arguments are host, then ipv6 or not, then timestamp, then whether to encrypte or not, singleusermode,  'notwin32', and one more (unknown)
 | |
|             $jnlp = $response->content;
 | |
|         }
 | |
| 
 | |
|     }
 | |
|     $response = $browser->request(GET $baseurl. "data/logout");
 | |
|     my $currnode = $sessdata->{node};
 | |
|     $jnlp =~ s!argument>title=.*Video Viewer</argument>!argument>title=$currnode wvid</argument>!;
 | |
|     xCAT::SvrUtils::sendmsg("method:imm", $callback, $sessdata->{node}, %allerrornodes);
 | |
|     xCAT::SvrUtils::sendmsg("jnlp:$jnlp", $callback, $sessdata->{node}, %allerrornodes);
 | |
| }
 | |
| 
 | |
| sub getrvidparms_with_buildid {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @build_id = (0, @{ $rsp->{data} });
 | |
|     if ($build_id[1] == 0x54 and $build_id[2] == 0x43 and $build_id[3] == 0x4f and $build_id[4] == 0x4f) { ##Lenovo IMM2
 | |
|         return getrvidparms_imm2($rsp, $sessdata);
 | |
|     }
 | |
|     if ($build_id[1] == 0x31 and $build_id[2] == 0x41 and $build_id[3] == 0x4f and $build_id[4] == 0x4f) { #Only know how to cope with yuoo builds
 | |
|         return getrvidparms_imm2($rsp, $sessdata);
 | |
|     }
 | |
| 
 | |
|     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
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Remote video is not supported on this system" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         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);
 | |
|     my $sessionid;
 | |
|     if ($response->content =~ /^ok:?(.*)/) {
 | |
|         $sessionid = $1;
 | |
|     } else {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Server returned unexpected data" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     $response = $browser->request(GET $baseurl. "/page/session.html"); #we don't care, but some firmware is confused if we don't
 | |
|     if ($sessionid) {
 | |
|         $response = $browser->request(GET $baseurl. "/kvm/kvm/jnlp?session_id=$sessionid");
 | |
|     } else {
 | |
|         $response = $browser->request(GET $baseurl. "/kvm/kvm/jnlp");
 | |
|     }
 | |
|     my $jnlp = $response->content;
 | |
|     if ($jnlp =~ /This advanced option requires the purchase and installation/) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Node does not have feature key for remote video" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     my $currnode = $sessdata->{node};
 | |
|     $jnlp =~ s!argument>title=.*Video Viewer</argument>!argument>title=$currnode wvid</argument>!;
 | |
|     xCAT::SvrUtils::sendmsg("method:imm", $callback, $sessdata->{node}, %allerrornodes);
 | |
|     xCAT::SvrUtils::sendmsg("jnlp:$jnlp", $callback, $sessdata->{node}, %allerrornodes);
 | |
|     my @cmdargv = @{ $sessdata->{extraargs} };
 | |
|     if (grep /-m/, @cmdargv) {
 | |
|         if ($sessionid) {
 | |
|             $response = $browser->request(GET $baseurl. "/kvm/vm/jnlp?session_id=$sessionid");
 | |
|         } else {
 | |
|             $response = $browser->request(GET $baseurl. "/kvm/vm/jnlp");
 | |
|         }
 | |
|         xCAT::SvrUtils::sendmsg("mediajnlp:" . $response->content, $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
|     return;
 | |
| }
 | |
| 
 | |
| 
 | |
| sub ripmi {    #implement generic raw ipmi commands
 | |
|     my $sessdata = shift;
 | |
|     my $netfun   = hex(shift @{ $sessdata->{extraargs} });
 | |
|     my $command  = hex(shift @{ $sessdata->{extraargs} });
 | |
|     my @data;
 | |
|     foreach (@{ $sessdata->{extraargs} }) {
 | |
|         push @data, hex($_);
 | |
|     }
 | |
|     $sessdata->{ipmisession}->subcmd(netfn => $netfun, command => $command, data => \@data, callback => \&ripmi_callback, callback_args => $sessdata);
 | |
| }
 | |
| 
 | |
| sub ripmi_callback {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     if ($rsp->{error}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     my $output = sprintf("%02X " x (scalar(@{ $rsp->{data} })), @{ $rsp->{data} });
 | |
|     xCAT::SvrUtils::sendmsg($output, $callback, $sessdata->{node}, %allerrornodes);
 | |
| }
 | |
| 
 | |
| sub isfpc {
 | |
|     my $sessdata = shift;
 | |
|     return 1
 | |
| }
 | |
| 
 | |
| sub isopenpower {
 | |
|     my $sessdata = shift;
 | |
|     if ($sessdata->{prod_id} == 43707 and $sessdata->{mfg_id} == 0) {
 | |
|         # mft_id 0 and prod_id 43707 is for Firestone,Minsky
 | |
|         return 1;
 | |
|     } elsif (($sessdata->{prod_id} =~ /0|2355|2437/) and $sessdata->{mfg_id} == 10876) {
 | |
|         # mfg_id 10876 is for IBM Power S822LC for Big Data (Supermicro), prod_id 2355 for B&S, and 0 or 2437 for Boston
 | |
|         return 1;
 | |
|     } else {
 | |
|         return 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub check_firmware_version {
 | |
| 
 | |
|     sub _on_receive_ugp {
 | |
|         my $rsp     = shift;
 | |
|         my $sessdta = shift;
 | |
|         shift @{ $rsp->{data} };
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     sub _on_receive_version {
 | |
|         my $rsp      = shift;
 | |
|         my $sessdata = shift;
 | |
|         shift @{ $rsp->{data} };
 | |
|         my $c_id = $sessdata->{c_id};
 | |
|         $sessdata->{$c_id} = $rsp->{data};
 | |
|     }
 | |
| 
 | |
|     sub _on_receive_component_name {
 | |
|         my $rsp      = shift;
 | |
|         my $sessdata = shift;
 | |
|         shift @{ $rsp->{data} };
 | |
|         my $c_id = $sessdata->{c_id};
 | |
| 
 | |
|         # Convert ASCII to char
 | |
|         my $format_string = "";
 | |
|         my $ascii_data    = $rsp->{data};
 | |
|         for my $i (@${ascii_data}) {
 | |
|             if ($i != 0) {
 | |
|                 $format_string = $format_string . chr($i);
 | |
|             }
 | |
|         }
 | |
|         $sessdata->{$c_id} = $format_string;
 | |
|     }
 | |
| 
 | |
|     my $sessdata         = shift;
 | |
|     my $firmware_version = shift;
 | |
|     my $component_string = shift;
 | |
| 
 | |
|     # GET TARGET UPGRADE CAPABILITIES
 | |
|     $sessdata->{ipmisession}->subcmd(netfn => 0x2c, command => 0x2e,
 | |
|             data => [ 0 ],
 | |
|             callback      => \&_on_receive_ugp,
 | |
|             callback_args => $sessdata);
 | |
|         while (xCAT::IPMI->waitforrsp()) { yield }
 | |
| 
 | |
| 
 | |
|     foreach my $c_id (@{ $sessdata->{component_ids} }) {
 | |
|         $sessdata->{c_id} = $c_id;
 | |
| 
 | |
|         # Get component property 1 - Current firmware version
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0x2c, command => 0x2f,
 | |
|             data => [ 0, $c_id / 2, 1 ],
 | |
|             callback      => \&_on_receive_version,
 | |
|             callback_args => $sessdata);
 | |
|         while (xCAT::IPMI->waitforrsp()) { yield }
 | |
|         $firmware_version->{$c_id} = $sessdata->{$c_id};
 | |
| 
 | |
|         # Get component property 2 - Component description string
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0x2c, command => 0x2f,
 | |
|             data => [ 0, $c_id / 2, 2 ],
 | |
|             callback      => \&_on_receive_component_name,
 | |
|             callback_args => $sessdata);
 | |
|         while (xCAT::IPMI->waitforrsp()) { yield }
 | |
|         $component_string->{$c_id} = $sessdata->{$c_id};
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub get_ipmitool_version {
 | |
|     my $version_ptr = shift;
 | |
|     my $cmd         = "$IPMIXCAT -V";
 | |
|     my $output      = xCAT::Utils->runcmd($cmd, -1);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         $callback->({ error => "Running ipmitool command failed. Error Code: $::RUNCMD_RC",
 | |
|                 errorcode => 1 });
 | |
|         return -1;
 | |
|     }
 | |
|     $$version_ptr = (split(/ /, $output))[2];
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------#
 | |
| # Calculate IPMItool Version format:
 | |
| # Example : 1.8.15 --> 1* 1 0000 0000 + 8 * 0000 0000 + 15
 | |
| #----------------------------------------------------------------#
 | |
| sub calc_ipmitool_version {
 | |
|     my $ver_str = shift;
 | |
|     my @vers = split(/\./, $ver_str);
 | |
|     return ord($vers[2]) + ord($vers[1]) * 10000 + ord($vers[0]) * 100000000;
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------#
 | |
| # Check bmc status:
 | |
| #  Arguments:
 | |
| #        pre_cmd:  A string prep cmd like ipmitool-xcat -H
 | |
| #                  <bmc_addr> -I lan -U <bmc_userid> -P
 | |
| #                  <bmc_password>
 | |
| #        inteval:  inteval time to check
 | |
| #        retry:    max retry time
 | |
| #        zz_retry: minimum number of 00 to receive before exiting
 | |
| #        sessdata: session data for display
 | |
| #        verbose:  verbose output
 | |
| #        use_rc:   Some machines, like IBM Power S822LC for Big Data (Supermicro), do not return
 | |
| #                  a "00" ready string from calling ipmi raw command. Instead they return RC=0. This
 | |
| #                  flag if 1, tells the check_bmc_status_with_ipmitool() to check the RC 
 | |
| #                  instead of "00" string. If set to 0, the "00" ready string is checked.
 | |
| #    Returns:
 | |
| #        1 when bmc is up
 | |
| #        0 when no response from bmc
 | |
| #----------------------------------------------------------------#
 | |
| sub check_bmc_status_with_ipmitool {
 | |
|     my $pre_cmd      = shift;
 | |
|     my $interval     = shift;
 | |
|     my $retry        = shift;
 | |
|     my $zz_retry     = shift;
 | |
|     my $sessdata     = shift;
 | |
|     my $verbose      = shift;
 | |
|     my $use_rc       = shift;
 | |
|     my $count        = 0;
 | |
|     my $zz_count     = 0;
 | |
|     my $bmc_response = 0;
 | |
|     my $cmd          = $pre_cmd . " raw 0x3a 0x0a";
 | |
|     my $bmc_ready_code = "00";
 | |
|     my $code_type    = "ready";
 | |
| 
 | |
|     if ($use_rc) {
 | |
|         $cmd            = $cmd . " >> /dev/null 2>&1 ; echo \$?";
 | |
|         $bmc_ready_code = "0";
 | |
|         $code_type      = "return";
 | |
|     }
 | |
|     # BMC response of " c0" means BMC still running IPL
 | |
|     # BMC response of " 00" means ready to flash
 | |
|     #
 | |
|     # Under certain conditions is it necessary to make sure BMC ready code 00 gets
 | |
|     # returned for several seconds. zz_retry argument is used to control how many iterations
 | |
|     # in the row 00 code is returned. This counter is reset if some other, non 00 code
 | |
|     # interrupts it.
 | |
|     while ($count < $retry) {
 | |
|         $bmc_response = xCAT::Utils->runcmd($cmd, -1);
 | |
|         if ($bmc_response =~ /$bmc_ready_code/) {
 | |
|             if ($zz_count > $zz_retry) {
 | |
|                 # zero-zero ready code was received for $zz_count iterations - good to exit
 | |
|                 if ($verbose) {
 | |
|                     xCAT::SvrUtils::sendmsg("Received BMC $code_type code $bmc_ready_code for $zz_count iterations - BMC is ready.", $callback, $sessdata->{node}, %allerrornodes);
 | |
|                 }
 | |
|                 return 1;
 | |
|             }
 | |
|             else {
 | |
|                 # check to make sure zero-zero is received again
 | |
|                 if ($verbose) {
 | |
|                     xCAT::SvrUtils::sendmsg("($zz_count) BMC $code_type code - $bmc_ready_code", $callback, $sessdata->{node}, %allerrornodes);
 | |
|                 }
 | |
|                 $zz_count++;
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             if ($zz_count > 0) {
 | |
|                 # zero-zero was received before, but now we get something else.
 | |
|                 # reset the zero-zero counter to make sure we get $zz_count iterations of zero-zero
 | |
|                 if ($verbose) {
 | |
|                     xCAT::SvrUtils::sendmsg("Resetting counter because BMC $code_type code - $bmc_response", $callback, $sessdata->{node}, %allerrornodes);
 | |
|                 }
 | |
|                 $zz_count = 0;
 | |
|             }
 | |
|         }
 | |
|         sleep($interval);
 | |
|         $count++;
 | |
|     }
 | |
|     if ($verbose) {
 | |
|         xCAT::SvrUtils::sendmsg("Never received $code_type code $bmc_ready_code after $count retries - BMC not ready.", $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------#
 | |
| # Wait for OS to reboot by checking "nodestat" to be "sshd" :
 | |
| #  Arguments:
 | |
| #        initial_sleep: seconds to sleep before checking loop start
 | |
| #        inteval:  inteval time to check
 | |
| #        retry:    max retry time
 | |
| #        sessdata: session data for display
 | |
| #        verbose:  verbose output
 | |
| #    Returns:
 | |
| #        1 when OS is up
 | |
| #        0 when no response of "sshd" from OS
 | |
| #----------------------------------------------------------------#
 | |
| sub wait_for_os_to_reboot {
 | |
|     my $initial_sleep = shift;
 | |
|     my $interval      = shift;
 | |
|     my $retry         = shift;
 | |
|     my $sessdata     = shift;
 | |
|     my $verbose      = shift;
 | |
|     my $cmd;
 | |
|     my $output;
 | |
|         if ($verbose) {
 | |
|             xCAT::SvrUtils::sendmsg("Sleeping for a few min waiting for node to power on before attempting to continue", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|         sleep($initial_sleep); # sleep initially for $initial_sleep seconds for node to reboot
 | |
|         # Start testing every $interval sec for node to be booted. Give up after $retry times.
 | |
|         foreach (1..$retry) {
 | |
|             # Test node is booted in to OS
 | |
|             $cmd = "nodestat $sessdata->{node} | /usr/bin/grep sshd";
 | |
|             $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|             if ($::RUNCMD_RC == 0) {
 | |
|                 # Node is ready to retry an upgrage
 | |
|                 if ($verbose) {
 | |
|                     xCAT::SvrUtils::sendmsg("Detected node booted. Will retry upgrade", $callback, $sessdata->{node}, %allerrornodes);
 | |
|                 }
 | |
|                 return 1; #Node booted
 | |
|             }
 | |
|             else {
 | |
|                 # Still not booted, wait for $interval sec and try again
 | |
|                 if ($verbose) {
 | |
|                     $cmd = "nodestat $sessdata->{node}";
 | |
|                     $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|                     my ($nodename, $state) = split(/:/, $output);
 | |
|                     xCAT::SvrUtils::sendmsg("($_) Node still not ready. Current state - $state, test again in $interval sec.", $callback, $sessdata->{node}, %allerrornodes);
 | |
|                 }
 | |
|                 sleep($interval);
 | |
|             }
 | |
|         }
 | |
|     return 0; #Node did not boot after requested delay
 | |
| }
 | |
| 
 | |
| sub do_firmware_update {
 | |
|     my $sessdata = shift;
 | |
|     my $ret;
 | |
|     my $ipmitool_ver;
 | |
|     my $verbose = 0;
 | |
|     my $retry = 2;
 | |
|     my $verbose_opt;
 | |
|     my $is_firestone = 0;
 | |
|     my $firestone_update_version;
 | |
|     my $htm_update_version;
 | |
|     my $pUpdate_directory;
 | |
|     $ret = get_ipmitool_version(\$ipmitool_ver);
 | |
|     exit $ret if $ret < 0;
 | |
| 
 | |
|     my $exit_with_error_func = sub {
 | |
|         my ($node, $callback, $message) = @_;
 | |
|         my $status = "failed to update firmware";
 | |
|         my $nodelist_table = xCAT::Table->new('nodelist');
 | |
|         if (!$nodelist_table) {
 | |
|             xCAT::MsgUtils->message("S", "Unable to open nodelist table, denying");
 | |
|         } else {
 | |
|             $nodelist_table->setNodeAttribs($node, { status => $status });
 | |
|             $nodelist_table->close();
 | |
|         }
 | |
|         xCAT::MsgUtils->message("S", $node.": ".$message);
 | |
|         $callback->({ error => "$node: $message", errorcode => 1 });
 | |
|         exit -1;
 | |
|     };
 | |
| 
 | |
|     my $exit_with_success_func = sub {
 | |
|         my ($node, $callback, $message) = @_;
 | |
|         my $status = "success updating firmware";
 | |
|         my $nodelist_table = xCAT::Table->new('nodelist');
 | |
|         if (!$nodelist_table) {
 | |
|             xCAT::MsgUtils->message("S", "Unable to open nodelist table, denying");
 | |
|         } else {
 | |
|             $nodelist_table->setNodeAttribs($node, { status => $status });
 | |
|             $nodelist_table->close();
 | |
|         }
 | |
|         xCAT::MsgUtils->message("S", $node.": ".$message);
 | |
|         $callback->({ data => "$node: $message" });
 | |
|         exit 0;
 | |
|     };
 | |
| 
 | |
| 
 | |
|     # only 1.8.15 or above support hpm update for firestone machines.
 | |
|     if (calc_ipmitool_version($ipmitool_ver) < calc_ipmitool_version("1.8.15")) {
 | |
|         $callback->({ error => "IPMITool $ipmitool_ver do not support firmware update for " .
 | |
|                   "firestone machines, please setup IPMITool 1.8.15 or above.",
 | |
|                 errorcode => 1 });
 | |
|         exit -1;
 | |
|     }
 | |
| 
 | |
|     my $output;
 | |
|     my $bmc_addr     = $sessdata->{ipmisession}->{bmc};
 | |
|     my $bmc_userid   = $sessdata->{ipmisession}->{userid};
 | |
|     my $bmc_password = undef;
 | |
|     if (defined($sessdata->{ipmisession}->{password})) {
 | |
|         $bmc_password = $sessdata->{ipmisession}->{password};
 | |
|     }
 | |
|     my $hpm_file = $sessdata->{extraargs}->[0];
 | |
|     if ($hpm_file !~ /^\//) {
 | |
|         $hpm_file = xCAT::Utils->full_path($hpm_file, $::cwd);
 | |
|     }
 | |
| 
 | |
|     my $rflash_log_file = xCAT::Utils->full_path($sessdata->{node}.".log", RFLASH_LOG_DIR);
 | |
| 
 | |
|     # NOTE (chenglch) lanplus should be used for the task of hpm update
 | |
|     # which indicate the bmc support ipmi protocol version 2.0.
 | |
|     my $pre_cmd = "$IPMIXCAT -H $bmc_addr -I lanplus -U $bmc_userid";
 | |
|     if ($bmc_password) {
 | |
|         $pre_cmd = $pre_cmd . " -P $bmc_password";
 | |
|     }
 | |
|     
 | |
|     # check for 8335-GTB Model Type to adjust buffer size
 | |
|     my $buffer_size = "30000";
 | |
|     my $cmd = $pre_cmd . " fru print 3";
 | |
|     $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|             "Running ipmitool command $cmd failed: $output");
 | |
|     }
 | |
|     if ($output =~ /8335-GTB/) {
 | |
|         $buffer_size = "15000";
 | |
|     }
 | |
| 
 | |
|     my $directory_name;
 | |
|     if (@{ $sessdata->{extraargs} } > 1) {
 | |
|         @ARGV = @{ $sessdata->{extraargs} };
 | |
|         use Getopt::Long;
 | |
|         GetOptions('d:s' => \$directory_name);
 | |
|     }
 | |
|     
 | |
|     # check verbose, buffersize, and retry options
 | |
|     for my $opt (@{$sessdata->{'extraargs'}}) {
 | |
|         if ($opt =~ /-V{1,4}/) {
 | |
|             $verbose_opt = lc($opt);
 | |
|             $verbose = 1;
 | |
|         }
 | |
|         if ($opt =~ /buffersize=/) {
 | |
|             my ($attribute, $buffer_value) = split(/=/, $opt);
 | |
|             if ($buffer_value) {
 | |
|                 # buffersize option was passed in, reset the default if valid
 | |
|                 if (looks_like_number($buffer_value) and $buffer_value > 0) {
 | |
|                     $buffer_size = $buffer_value;
 | |
|                 }
 | |
|                 else {
 | |
|                     $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                         "Invalid buffer size value $buffer_value");
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         if ($opt =~ /retry=/) {
 | |
|             my ($attribute, $retry_value) = split(/=/, $opt);
 | |
|             if (defined $retry_value) {
 | |
|                 # retry option was passed in, reset the default if valid
 | |
|                 if (looks_like_number($retry_value) and $retry_value >= 0) {
 | |
|                     $retry = $retry_value;
 | |
|                 }
 | |
|                 else {
 | |
|                     $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                         "Invalid retry value $retry_value");
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # For IBM Power S822LC for Big Data (Supermicro) machines such as 
 | |
|     # P9 Boston (9006-22C, 9006-12C, 5104-22C) or P8 Briggs (8001-22C) 
 | |
|     # firmware update is done using pUpdate utility expected to be in the 
 | |
|     # specified data directory along with the update files .bin for BMC or .pnor for Host
 | |
|     if ($output =~ /8001-22C|9006-22C|5104-22C|9006-12C/) {
 | |
|         # Verify valid data directory was specified
 | |
|         if (defined $directory_name) {
 | |
|             unless (File::Spec->file_name_is_absolute($directory_name)) {
 | |
|                 # Directory name was passed in as relative path, prepend current working dir
 | |
|                 $directory_name = xCAT::Utils->full_path($directory_name, $::cwd);
 | |
|             }
 | |
|             # directory was passed in, verify it is valid
 | |
|             if (-d $directory_name) {
 | |
|                 # Passed in directory name exists
 | |
|                 $pUpdate_directory = $directory_name;
 | |
|             }
 | |
|             else {
 | |
|                 $exit_with_error_func->($sessdata->{node}, $callback, "Can not access data directory $directory_name");
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback, "Directory name is required to update IBM Power S822LC for Big Data machines.");
 | |
|         }
 | |
|         # Verify specified directory contains pUpdate utility
 | |
|         unless (-e "$pUpdate_directory/pUpdate") {
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Can not find pUpdate utility in data directory $pUpdate_directory.");
 | |
|         }
 | |
|         # Verify specified directory contains executable pUpdate utility
 | |
|         unless (-x "$pUpdate_directory/pUpdate") {
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Execute permission is not set for pUpdate utility in data directory $pUpdate_directory.");
 | |
|         }
 | |
| 
 | |
|         # Verify there is at least one of update files inside data directory - .bin or .pnor
 | |
| 
 | |
|         my @pnor_files = <"$pUpdate_directory/*.pnor">; # Get a list of all .pnor files
 | |
|         my @bmc_files  = <"$pUpdate_directory/*.bin">;  # Get a list of all .bin files
 | |
| 
 | |
|         if (scalar(@pnor_files) > 1) {
 | |
|             # Error if more than one .pnor file in data directory
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Multiple PNOR update files detected in data directory $pUpdate_directory.");
 | |
|         }
 | |
| 
 | |
|         if (scalar(@bmc_files) > 1) {
 | |
|             # Error if more than one .bin file in data directory
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Multiple BMC update files detected in data directory $pUpdate_directory.");
 | |
|         }
 | |
| 
 | |
|         my $pnor_file;
 | |
|         if (scalar(@pnor_files) > 0) {
 | |
|             $pnor_file = $pnor_files[0];
 | |
|         }
 | |
| 
 | |
|         my $bmc_file;
 | |
|         if (scalar(@bmc_files) > 0) {
 | |
|             $bmc_file = $bmc_files[0];
 | |
|         }
 | |
| 
 | |
|         # At least one update file is needed, .bmc or .pnor
 | |
|         unless ($pnor_file || $bmc_file) {
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "At least one update file (.bin or .pnor) needs to be in data directory $pUpdate_directory.");
 | |
|         }
 | |
|         # All checks are done, run pUpdate utility on each of the update files found in 
 | |
|         # the specified data directory
 | |
|         xCAT::SvrUtils::sendmsg("rflash started, Please wait...", $callback, $sessdata->{node});
 | |
| 
 | |
|         # step 1 power off
 | |
|         $cmd = $pre_cmd . " chassis power off";
 | |
|         if ($verbose) {
 | |
|             xCAT::SvrUtils::sendmsg("Preparing to upgrade firmware, powering chassis off...", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|         $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Running ipmitool command $cmd failed: $output");
 | |
|         }
 | |
|         unless (check_bmc_status_with_ipmitool($pre_cmd, 5, 10, 2, $sessdata, $verbose, 1)) {
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|             "Timeout to check the bmc status");
 | |
|         }
 | |
| 
 | |
|         # step 2 update BMC file or PNOR file, or both
 | |
|         if ($bmc_file) {
 | |
|             # BMC file was found in data directory, run update with it
 | |
|             my $pUpdate_bmc_cmd = "$pUpdate_directory/pUpdate -f $bmc_file -i lan -h $bmc_addr -u $bmc_userid -p $bmc_password >".$rflash_log_file." 2>&1";
 | |
|             if ($verbose) {
 | |
|                 xCAT::SvrUtils::sendmsg([ 0,
 | |
|                     "rflashing BMC, see the detail progress :\"tail -f $rflash_log_file\"" ],
 | |
|                     $callback, $sessdata->{node});
 | |
|             }
 | |
|             my $output = xCAT::Utils->runcmd($pUpdate_bmc_cmd, -1);
 | |
|             if ($::RUNCMD_RC != 0) {
 | |
|                 $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                     "Error running command $pUpdate_bmc_cmd");
 | |
|             }
 | |
|             # Wait for BMC to reboot before continuing to next step.
 | |
|             # Since this is a IBM Power S822LC for Big Data (Supermicro) machine, use RC to check if ready 
 | |
|             unless (check_bmc_status_with_ipmitool($pre_cmd, 5, 60, 10, $sessdata, $verbose, 1)) {
 | |
|                 $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Timeout to check the bmc status");
 | |
|             }
 | |
|         }
 | |
| 
 | |
| 
 | |
|         if ($pnor_file) {
 | |
|             # PNOR file was found in data directory, run update with it
 | |
|             my $pUpdate_pnor_cmd = "$pUpdate_directory/pUpdate -pnor $pnor_file -i lan -h $bmc_addr -u $bmc_userid -p $bmc_password >>".$rflash_log_file." 2>&1";
 | |
|             if ($verbose) {
 | |
|                 xCAT::SvrUtils::sendmsg([ 0,
 | |
|                     "rflashing PNOR, see the detail progress :\"tail -f $rflash_log_file\"" ],
 | |
|                     $callback, $sessdata->{node});
 | |
|             }
 | |
|             my $output = xCAT::Utils->runcmd($pUpdate_pnor_cmd, -1);
 | |
|             if ($::RUNCMD_RC != 0) {
 | |
|                 $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                     "Error running command $pUpdate_pnor_cmd");
 | |
|             }
 | |
|         }
 | |
|         # step 3 power on
 | |
|         $cmd = $pre_cmd . " chassis power on";
 | |
|         $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Running ipmitool command $cmd failed: $output");
 | |
|         }
 | |
| 
 | |
|         $exit_with_success_func->($sessdata->{node}, $callback, "Firmware updated, powering chassis on to populate FRU information...");
 | |
|     }
 | |
| 
 | |
|     if (($hpm_data_hash{deviceID} ne $sessdata->{device_id}) ||
 | |
|         ($hpm_data_hash{productID} ne $sessdata->{prod_id}) ||
 | |
|         ($hpm_data_hash{manufactureID} ne $sessdata->{mfg_id})) {
 | |
|         $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|             "The image file doesn't match this machine");
 | |
|     }
 | |
| 
 | |
|     # check for 8335-GTB Firmware above 1610A release.  If below, exit
 | |
|     if ($output =~ /8335-GTB/) {
 | |
|         $cmd = $pre_cmd . " fru print 47";
 | |
|         $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Running ipmitool command $cmd failed: $output");
 | |
|         }
 | |
|         my $grs_version = $output =~ /OP8_.*v(\d*\.\d*_\d*\.\d*)/;
 | |
|         if ($grs_version =~ /\d\.(\d+)_(\d+\.\d+)/) {
 | |
|             my $prim_grs_version = $1;
 | |
|             my $sec_grs_version = $2;
 | |
|             if ($prim_grs_version <= 7 && $sec_grs_version < 2.55) {
 | |
|                 $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                     "Error: Current firmware level OP8v_$grs_version requires one-time manual update to at least version OP8v_1.7_2.55");
 | |
|             }
 | |
|         }
 | |
| 
 | |
|     }
 | |
|     # For Firestone the update from 810 to 820 or from 820 to 810 needs to be done in 3 steps
 | |
|     # instead of usual one.
 | |
|     if ($output =~ /8335-GCA|8335-GTA/) {
 | |
|         $is_firestone = 1;
 | |
|         $cmd = $pre_cmd . " fru print 47";
 | |
|         $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Running ipmitool command $cmd failed: $output");
 | |
|         }
 | |
|         # Check what firmware version is currently running on the machine
 | |
|         if ($output =~ /OP8_.*v\d\.\d+_(\d+)\.\d+/) {
 | |
|             my $frs_version = $1;
 | |
|             if ($frs_version == 1) {
 | |
|                 $firestone_update_version = "810";
 | |
|             }
 | |
|             if ($frs_version == 2) {
 | |
|                 $firestone_update_version = "820";
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Unable to determine firmware version currently installed. Verify that \"$cmd | grep OP8_v\" command returns a version.");
 | |
|         }
 | |
| 
 | |
|         # Check what firmware version is specified in htm file
 | |
|         $cmd = "/usr/bin/grep -a FW_DESC $hpm_file";
 | |
|         $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Running ipmitool command $cmd failed: $output");
 | |
|         }
 | |
|         # Parse out build date from the description string
 | |
|         if ($output =~ /FW_DESC=8335 \w+ \w+ \w+ (\w+)/) {
 | |
|             my $htm_date= $1;
 | |
|             # Parse out the year from "mmddyyyy" (skip 4 digits, grab last 4)
 | |
|             if ($htm_date =~ /\d{4}(\d+)/) {
 | |
|                 my $htm_year = $1;
 | |
|                 if ($htm_year == 2016) {
 | |
|                     $htm_update_version = "810";
 | |
|                 }
 | |
|                 if ($htm_year == 2017) {
 | |
|                     $htm_update_version = "820";
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Unable to determine firmware version of $hpm_file.");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if ($is_firestone and $firestone_update_version and
 | |
|         (($firestone_update_version eq "820" and $htm_update_version eq "810") or 
 | |
|          ($firestone_update_version eq "810" and $htm_update_version eq "820")) 
 | |
|        ) {
 | |
|         xCAT::SvrUtils::sendmsg("rflash started, Please wait...", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         $retry = 0; # No retry support for 3 step update process
 | |
|     }
 | |
|     else {
 | |
|         xCAT::SvrUtils::sendmsg("rflash started, upgrade failure will be retried up to $retry times. Please wait...", $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
| 
 | |
| RETRY_UPGRADE:
 | |
|     my $failed_upgrade = 0;
 | |
| 
 | |
|     # step 1 power off
 | |
|     $cmd = $pre_cmd . " chassis power off";
 | |
|     if ($verbose) {
 | |
|         xCAT::SvrUtils::sendmsg("Preparing to upgrade firmware, powering chassis off...", $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
|     $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|             "Running ipmitool command $cmd failed: $output");
 | |
|     }
 | |
| 
 | |
|     # step 2 reset cold
 | |
|     $cmd = $pre_cmd . " mc reset cold";
 | |
|     $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|             "Running ipmitool command $cmd failed: $output");
 | |
|     }
 | |
| 
 | |
|     # check reset status
 | |
|     unless (check_bmc_status_with_ipmitool($pre_cmd, 5, 60, 2, $sessdata, $verbose, 0)) {
 | |
|         $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|             "Timeout to check the bmc status");
 | |
|     }
 | |
| 
 | |
|     # step 3 protect network
 | |
|     $cmd = $pre_cmd . " raw 0x32 0xba 0x18 0x00";
 | |
|     $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|             "Running ipmitool command $cmd failed: $output");
 | |
|     }
 | |
| 
 | |
|     # step 4 upgrade firmware
 | |
|     # For firestone machines if updating from 810 to 820 version or from 820 to 810,
 | |
|     # extra steps are needed. Hanled in "if" block, "else" block is normal update in a single step.
 | |
|     if ($is_firestone and 
 | |
|         (($firestone_update_version eq "820" and $htm_update_version eq "810") or 
 | |
|          ($firestone_update_version eq "810" and $htm_update_version eq "820")) 
 | |
|        ) {
 | |
| 
 | |
|         # Step 4.1
 | |
|         $cmd = $pre_cmd . " -z " . $buffer_size . " hpm upgrade $hpm_file component 0 force ";
 | |
|         $cmd .= $verbose_opt;
 | |
| 
 | |
|         $cmd .= " >".$rflash_log_file." 2>&1";
 | |
|         if ($verbose) {
 | |
|             xCAT::SvrUtils::sendmsg([ 0,
 | |
|                 "rflashing component 0, see the detail progress :\"tail -f $rflash_log_file\"" ],
 | |
|                 $callback, $sessdata->{node});
 | |
|         }
 | |
|         $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             # Since "hpm update" command in step 4.1 above redirects standard out and error to a log file,
 | |
|             # nothing gets returned from execution of the command. Here if RC is not zero, we
 | |
|             # extract all lines containing "Error" from that log file and display them in the sendmsg below
 | |
|             my $get_error_cmd = "/usr/bin/grep Error $rflash_log_file";
 | |
|             $output = xCAT::Utils->runcmd($get_error_cmd, 0);
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Running ipmitool command $cmd failed with rc=$::RUNCMD_RC and the following error messages:\n$output\nSee the $rflash_log_file for details.");
 | |
|         }
 | |
| 
 | |
|         sleep(1); # Sleep for a second before next step
 | |
| 
 | |
|         # Step 4.2
 | |
|         $cmd = $pre_cmd . " -z " . $buffer_size . " hpm upgrade $hpm_file component 1 force ";
 | |
|         $cmd .= $verbose_opt;
 | |
| 
 | |
|         $cmd .= " >>".$rflash_log_file." 2>&1";
 | |
|         if ($verbose) {
 | |
|             xCAT::SvrUtils::sendmsg([ 0,
 | |
|                 "rflashing component 1, see the detail progress :\"tail -f $rflash_log_file\"" ],
 | |
|                 $callback, $sessdata->{node});
 | |
|         }
 | |
|         $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             # Since "hpm update" command in step 4.2 above redirects standard out and error to a log file,
 | |
|             # nothing gets returned from execution of the command. Here if RC is not zero, we
 | |
|             # extract all lines containing "Error" from that log file and display them in the sendmsg below
 | |
|             my $get_error_cmd = "/usr/bin/grep Error $rflash_log_file";
 | |
|             $output = xCAT::Utils->runcmd($get_error_cmd, 0);
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Running ipmitool command $cmd failed with rc=$::RUNCMD_RC and the following error messages:\n$output\nSee the $rflash_log_file for details.");
 | |
|         }
 | |
| 
 | |
|         # Wait for BMC to reboot before continuing to next step
 | |
|         unless (check_bmc_status_with_ipmitool($pre_cmd, 5, 60, 10, $sessdata, $verbose, 0)) {
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Timeout waiting for the bmc ready status. Firmware update suspended");
 | |
|         }
 | |
| 
 | |
|         # Step 4.3
 | |
|         $cmd = $pre_cmd . " -z " . $buffer_size . " hpm upgrade $hpm_file component 2 force ";
 | |
|         $cmd .= $verbose_opt;
 | |
| 
 | |
|         $cmd .= " >>".$rflash_log_file." 2>&1";
 | |
|         if ($verbose) {
 | |
|             xCAT::SvrUtils::sendmsg([ 0,
 | |
|                 "rflashing component 2, see the detail progress :\"tail -f $rflash_log_file\"" ],
 | |
|                 $callback, $sessdata->{node});
 | |
|         }
 | |
|         $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             # Since "hpm update" command in step 4.3 above redirects standard out and error to a log file,
 | |
|             # nothing gets returned from execution of the command. Here if RC is not zero, we
 | |
|             # extract all lines containing "Error" from that log file and display them in the sendmsg below
 | |
|             my $get_error_cmd = "/usr/bin/grep Error $rflash_log_file";
 | |
|             $output = xCAT::Utils->runcmd($get_error_cmd, 0);
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Running ipmitool command $cmd failed with rc=$::RUNCMD_RC and the following error messages:\n$output\nSee the $rflash_log_file for details.");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     else {
 | |
|         $cmd = $pre_cmd . " -z " . $buffer_size . " hpm upgrade $hpm_file force ";
 | |
|         $cmd .= $verbose_opt;
 | |
| 
 | |
|         $cmd .= " >".$rflash_log_file." 2>&1";
 | |
|         if ($verbose) {
 | |
|             xCAT::SvrUtils::sendmsg([ 0,
 | |
|                 "rflashing ... See the detail progress :\"tail -f $rflash_log_file\"" ],
 | |
|                 $callback, $sessdata->{node});
 | |
|         }
 | |
| 
 | |
|         $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|         # if upgrade command failed and we exausted number of retries 
 | |
|         # report an error, exit to the caller and leave node in powered off state
 | |
|         # otherwise report an error, power on the node  and try upgrade again
 | |
|         if ($::RUNCMD_RC != 0) {
 | |
|             # Since "hpm update" command in step 4 above redirects standard out and error to a log file,
 | |
|             # nothing gets returned from execution of the command. Here if RC is not zero, we
 | |
|             # extract all lines containing "Error" from that log file and display them in the sendmsg below
 | |
|             my $get_error_cmd = "/usr/bin/grep Error $rflash_log_file";
 | |
|             $output = xCAT::Utils->runcmd($get_error_cmd, 0);
 | |
|             if ($retry == 0) {
 | |
|                 # No more retries left, report an error and exit
 | |
|                 $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                     "Running ipmitool command $cmd failed with rc=$::RUNCMD_RC and the following error messages:\n$output\nSee the $rflash_log_file for details.");
 | |
|             }
 | |
|             else {
 | |
|                 # Error upgrading, set a flag to attempt a retry
 | |
|                 xCAT::SvrUtils::sendmsg("Running attempt $retry of ipmitool command $cmd failed with rc=$::RUNCMD_RC and the following error messages:\n$output\nSee the $rflash_log_file for details.", $callback, $sessdata->{node}, %allerrornodes);
 | |
|                 $failed_upgrade = 1;
 | |
|             
 | |
|             } 
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # step 5 power on
 | |
|     # check reset status
 | |
|     unless (check_bmc_status_with_ipmitool($pre_cmd, 5, 60, 10, $sessdata, $verbose, 0)) {
 | |
|         $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|             "Timeout to check the bmc status");
 | |
|     }
 | |
| 
 | |
|     if ($failed_upgrade) {
 | |
|         xCAT::SvrUtils::sendmsg("Firmware update failed, powering chassis on for a retry. This can take several minutes. $retry retries left ...", $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
|     else {
 | |
|         if ($verbose) {
 | |
|             xCAT::SvrUtils::sendmsg("Firmware updated, powering chassis on to populate FRU information...", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     $cmd = $pre_cmd . " chassis power on";
 | |
|     $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|     if ($::RUNCMD_RC != 0) {
 | |
|         $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|             "Running ipmitool command $cmd failed: $output");
 | |
|     }
 | |
| 
 | |
|     if ($failed_upgrade) {
 | |
|         # Update has failed in step 4. Wait for node to reboot and try again
 | |
|         my $node_ready_for_retry = wait_for_os_to_reboot(300,10,30,$sessdata,$verbose);
 | |
|         if ($node_ready_for_retry) {
 | |
|             $retry--;   # decrement number of retries left
 | |
|             # Yes, it is a goto statement here. Ugly, but removes the need to restructure
 | |
|             # the above block of code.
 | |
|             goto RETRY_UPGRADE; 
 | |
|         }
 | |
|         else {
 | |
|             # After 10 min of waiting node has not rebooted. Give up retrying.
 | |
|             $exit_with_error_func->($sessdata->{node}, $callback,
 | |
|                 "Giving up waiting for the node to reboot. No further retries will be attempted.");
 | |
|         }
 | |
|         
 | |
|     }
 | |
|     else {
 | |
|         $exit_with_success_func->($sessdata->{node}, $callback,
 | |
|             "Success updating firmware.");
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub rflash {
 | |
|     my $sessdata = shift;
 | |
|     my $directory_flag = 0;
 | |
|     if (isopenpower($sessdata)) {
 | |
| 
 | |
|         # Do firmware update for firestone here.
 | |
|         @{ $sessdata->{component_ids} } = qw/1 2 4/;
 | |
|         foreach my $opt (@{ $sessdata->{extraargs} }) {
 | |
|             if ($opt =~ /^(-c|--check)$/i) {
 | |
|                 $sessdata->{subcommand} = "check";
 | |
|                 # support verbose options for ipmitool command
 | |
|             } elsif ($opt =~ /^-d$/) {
 | |
|                 # special handling if -d option which can be followed by a directory name
 | |
|                 $directory_flag = 1; # set a flag that directory option was given
 | |
|             } elsif ($opt !~ /.*\.hpm$/i && $opt !~ /^-V{1,4}$|^--buffersize=|^--retry=/) {
 | |
|                 # An unexpected flag was passed, but it could be a directory name. Display error only if not -d option
 | |
|                 unless ($directory_flag) {
 | |
|                     $callback->({ error => "The option $opt is not supported or invalid update file specified",
 | |
|                         errorcode => 1 });
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # Send firmware version information to clent.
 | |
|         if ($sessdata->{subcommand} eq 'check') {
 | |
|             my %firmware_version;
 | |
|             my %comp_string;
 | |
|             check_firmware_version($sessdata, \%firmware_version, \%comp_string);
 | |
|             my $msg = "";
 | |
|             my $i;
 | |
|             for ($i = 0 ; $i < scalar(@{ $sessdata->{component_ids} }) ; $i++) {
 | |
|                 my $c_id          = ${ $sessdata->{component_ids} }[$i];
 | |
|                 my $version       = $firmware_version{$c_id};
 | |
|                 my $format_string = $comp_string{$c_id};
 | |
|                 my $format_ver    = sprintf("%3d.%02x.%d",
 | |
|                     $version->[0], $version->[1],
 | |
|                     $version->[5]*0x1000000 +$version->[4]*0x10000+ $version->[3]*0x100+$version->[2]);
 | |
|                 $msg = $msg . $sessdata->{node} . ": " .
 | |
| "Node firmware version for $format_string component: $format_ver";
 | |
|                 if ($i != scalar(@{ $sessdata->{component_ids} }) - 1) {
 | |
|                     $msg = $msg . "\n";
 | |
|                 }
 | |
|             }
 | |
|             $callback->({ data => $msg });
 | |
|             return;
 | |
|         }
 | |
|         return do_firmware_update($sessdata);
 | |
|     } elsif (isfpc($sessdata)) {
 | |
| 
 | |
|         #first, start a fpc firmware transaction
 | |
|         $sessdata->{firmpath} = $sessdata->{subcommand};
 | |
|         $sessdata->{firmctx}  = "init";
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0x8, command => 0x17,
 | |
|             data => [ 0, 0, 1, 0, 0, 0, 0 ],
 | |
|             callback      => \&fpc_firmup_config,
 | |
|             callback_args => $sessdata);
 | |
|     } else {
 | |
|         die "Unimplemented";
 | |
|     }
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------#
 | |
| # Running rflash procedure in a child process
 | |
| # Note (chenglch) If the parent process abort unexpectedly, the
 | |
| # child process can not be terminated by xcat.
 | |
| #----------------------------------------------------------------#
 | |
| sub do_rflash_process {
 | |
|     my $node = shift;
 | |
|     my $pid  = xCAT::Utils->xfork;
 | |
|     if (!defined($pid)) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Fork rflash process Error." ],
 | |
|             $callback, $node, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # child
 | |
|     elsif ($pid == 0) {
 | |
|         unless (setpgrp()) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, "Faild to run setgprp for $$ process" ],
 | |
|                 $callback, $node);
 | |
|             exit(1);
 | |
|         }
 | |
|         my $extra = $_[8];
 | |
|         my @exargs = @$extra;
 | |
|         my $programe = \$0;
 | |
|         $$programe = "$node: rflash child process";
 | |
|         if (grep(/^(-c|--check)$/i, @exargs)) {
 | |
|             $SIG{INT} = $SIG{TERM} = $SIG{HUP} = "DEFAULT";
 | |
|         }
 | |
|         # NOTE (chenglch): Actually if multiple client or rest api works on the same node,
 | |
|         # the bmc of the node may not be protected while rflash is running. As xcat may not
 | |
|         # support lock on node level, just require a lock for rflash command for specific node.
 | |
|         my $lock = xCAT::Utils->acquire_lock("rflash_$node", $NON_BLOCK);
 | |
|         if (!$lock) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, "rflash is running on $node, please retry after a while" ],
 | |
|                 $callback, $node, %allerrornodes);
 | |
|             exit(1);
 | |
|         }
 | |
|         my $recover_image;
 | |
|         if (grep(/^(--recover)$/, @{ $extra })) {
 | |
|             if (@{ $extra } != 2) {
 | |
|                 xCAT::SvrUtils::sendmsg([ 1, "The command format for recovery is invalid. Only support 'rflash <noderange> --recover <bmc_file_path>'" ],
 | |
|                     $callback, $node);
 | |
|                 exit(1);
 | |
|             }
 | |
|             my @argv = @{ $extra };
 | |
|             if ($argv[0] eq "--recover") {
 | |
|                 $recover_image = $argv[1];
 | |
|             } elsif ($argv[1] eq "--recover") {
 | |
|                 $recover_image = $argv[0];
 | |
|             }
 | |
|         }
 | |
|         if (defined($recover_image)) {
 | |
|             if ($recover_image !~ /^\//) {
 | |
|                 $recover_image = xCAT::Utils->full_path($recover_image, $::cwd);
 | |
|             }
 | |
|             unless(-x "/usr/bin/tftp") {
 | |
|                 $callback->({ error => "Could not find executable file /usr/bin/tftp, please setup tftp client.",
 | |
|                         errorcode => 1 });
 | |
|                 exit(1);
 | |
|             }
 | |
|             my $bmcip     = $_[0];
 | |
|             my $cmd = "/usr/bin/tftp $bmcip -m binary -c put $recover_image ".basename($recover_image);
 | |
|             my $output = xCAT::Utils->runcmd($cmd, -1);
 | |
|             if ($::RUNCMD_RC != 0) {
 | |
|                 $callback->({ error => "Running tftp command \'$cmd\' failed. Error Code: $::RUNCMD_RC. Output: $output.",
 | |
|                         errorcode => 1 });
 | |
|                 exit(1);
 | |
|             }
 | |
|             # Sometimes tftp command retrun error message but without nonzero error code
 | |
|             if($output) {
 | |
|                 $callback->({ error => "Running tftp command \'$cmd\' failed. Output: $output",
 | |
|                         errorcode => 1 });
 | |
|                 exit(1);
 | |
|             }
 | |
|             $callback->({ data => "$node: Successfully updated recovery image. BMC is restarting and will not be reachable for 5-10 minutes."});
 | |
|             exit(0);
 | |
|         }
 | |
|         donode($node, @_);
 | |
|         while (xCAT::IPMI->waitforrsp()) { yield }
 | |
|         xCAT::Utils->release_lock($lock, $NON_BLOCK);
 | |
|         exit(0);
 | |
|     }
 | |
| 
 | |
|     # parent
 | |
|     else {
 | |
|         $child_pids{$pid} = $node;
 | |
|     }
 | |
|     return $pid;
 | |
| }
 | |
| 
 | |
| sub start_rflash_processes {
 | |
|     my $donargs_ptr = shift;
 | |
|     my @donargs     = @{$donargs_ptr};
 | |
|     my $ipmitimeout = shift;
 | |
|     my $ipmitrys    = shift;
 | |
|     my $command     = shift;
 | |
|     my %namedargs   = @_;
 | |
|     my $extra       = $namedargs{-args};
 | |
|     my @exargs      = @$extra;
 | |
|     # rflash processes can not be terminated from client
 | |
|     if (!grep(/^(-c|--check)$/i, @exargs)) {
 | |
|         $SIG{INT} = $SIG{TERM} = $SIG{HUP}="IGNORE";
 | |
|     } else {
 | |
|         $SIG{INT} = $SIG{TERM} = $SIG{HUP} = sub {
 | |
|             foreach (keys %child_pids) {
 | |
|                 kill 2, $_;
 | |
|             }
 | |
|             exit 0;
 | |
|         };
 | |
|     }
 | |
|     my $rflash_status;
 | |
|     foreach (@donargs) {
 | |
|         do_rflash_process($_->[0], $_->[1], $_->[2], $_->[3], $_->[4],
 | |
|             $ipmitimeout, $ipmitrys, $command, -args => \@exargs);
 | |
|         $rflash_status->{$_->[0]}->{status} = "updating firmware";
 | |
|     }
 | |
|     if (!grep(/^(-c|--check)$/i, @exargs)) {
 | |
|         my $nodelist_table = xCAT::Table->new('nodelist');
 | |
|         if (!$nodelist_table) {
 | |
|             xCAT::MsgUtils->message("S", "Unable to open nodelist table, denying");
 | |
|         } else {
 | |
|             $nodelist_table->setNodesAttribs($rflash_status);
 | |
|             $nodelist_table->close();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # Wait for all processes to end
 | |
|     while (keys %child_pids) {
 | |
|         my $cpid;
 | |
|         if (($cpid = wait()) > 0) {
 | |
|             delete $child_pids{$cpid};
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub fpc_firmup_config {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         abort_fpc_update($_[1]);
 | |
|         return;
 | |
|     }
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     unless ($sessdata->{firmupxid}) {
 | |
|         $sessdata->{firmupxid} = $rsp->{data}->[0];
 | |
|     }
 | |
|     my $data;
 | |
|     if ($sessdata->{firmctx} eq 'init') {
 | |
|         $data = [ 0, $sessdata->{firmupxid}, 1, 0, 1, 0, 0, 0,
 | |
|             length($sessdata->{firmpath}),
 | |
|             unpack("C*", $sessdata->{firmpath}) ];
 | |
|         $sessdata->{firmctx} = 'p1';
 | |
|     } elsif ($sessdata->{firmctx} eq 'p1') {
 | |
|         $data = [ 0, $sessdata->{firmupxid}, 3, 0, 5 ];
 | |
|         $sessdata->{firmctx} = 'p2';
 | |
|     } elsif ($sessdata->{firmctx} eq 'p2') {
 | |
|         $data = [ 0, $sessdata->{firmupxid}, 4, 0, 0xa ];
 | |
|         $sessdata->{firmctx} = 'p3';
 | |
|     } elsif ($sessdata->{firmctx} eq 'p3') {
 | |
|         $data = [ 0, $sessdata->{firmupxid}, 5, 0, 3 ];
 | |
|         $sessdata->{firmctx} = 'p4';
 | |
|     } elsif ($sessdata->{firmctx} eq 'p4') {
 | |
|         $data = [ 0, $sessdata->{firmupxid}, 6, 0, 1 ];
 | |
|         $sessdata->{firmctx} = 'xfer';
 | |
|         xCAT::SvrUtils::sendmsg("Transferring firmware", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0x8, command => 0x19,
 | |
|             data => [ 0, $sessdata->{firmupxid} ],
 | |
|             callback      => \&fpc_firmxfer_watch,
 | |
|             callback_args => $sessdata);
 | |
|         return;
 | |
| 
 | |
|     }
 | |
|     $sessdata->{ipmisession}->subcmd(netfn => 0x8, command => 0x18,
 | |
|         data          => $data,
 | |
|         callback      => \&fpc_firmup_config,
 | |
|         callback_args => $sessdata);
 | |
| }
 | |
| 
 | |
| sub abort_fpc_update {
 | |
|     my $sessdata = shift;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn => 0x8, command => 0x15, data => [], callback => \&fpc_update_aborted, callback_args => $sessdata);
 | |
| }
 | |
| 
 | |
| sub fpc_update_aborted {
 | |
|     check_rsp_errors(@_);
 | |
|     return;
 | |
| }
 | |
| 
 | |
| sub fpc_firmxfer_watch {
 | |
|     my $abort = 0;
 | |
|     if ($_[0]->{code} == 0x89) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Transfer failed (wrong url?)" ], $callback, $_[1]->{node}, %allerrornodes);
 | |
|         $abort = 1;
 | |
|     } elsif ($_[0]->{code} == 0x91) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Invalid URL format given" ], $callback, $_[1]->{node}, %allerrornodes);
 | |
|         $abort = 1;
 | |
|     }
 | |
|     if ($abort) {
 | |
|         abort_fpc_update($_[1]);
 | |
|         return;
 | |
|     }
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         abort_fpc_update($_[1]);
 | |
|         return;
 | |
|     }
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     my $delay    = 1;
 | |
|     my $watch    = 2;
 | |
|     if ($sessdata->{firmctx} eq 'apply') { $delay = 15; $watch = 1; }
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $percent = 0;
 | |
|     if ($rsp->{data} and (scalar(@{ $rsp->{data} }) > 0)) {
 | |
|         $percent = $rsp->{data}->[0];
 | |
|     }
 | |
| 
 | |
|     #$callback->({sinfo=>"$percent%"});
 | |
|     if ($percent == 100) {
 | |
|         if ($sessdata->{firmctx} eq 'xfer') {
 | |
|             xCAT::SvrUtils::sendmsg("Applying firmware", $callback, $sessdata->{node}, %allerrornodes);
 | |
|             $sessdata->{firmctx} = "apply";
 | |
|             $sessdata->{ipmisession}->subcmd(netfn => 0x8, command => 0x20,
 | |
|                 data => [ 0, $sessdata->{firmupxid} ],
 | |
|                 callback      => \&fpc_firmxfer_watch,
 | |
|                 callback_args => $sessdata);
 | |
|             return;
 | |
|         } else {
 | |
|             xCAT::SvrUtils::sendmsg("Resetting FPC", $callback, $sessdata->{node}, %allerrornodes);
 | |
|             resetbmc($sessdata);
 | |
|         }
 | |
|     } else {
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0x8, command => 0x12,
 | |
|             data          => [$watch],
 | |
|             delayxmit     => $delay,
 | |
|             callback      => \&fpc_firmxfer_watch,
 | |
|             callback_args => $sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| my %fpcsessions;
 | |
| 
 | |
| sub reseat_node {
 | |
|     my $sessdata = shift;
 | |
|     if (1) {    # TODO: FPC path checked for
 | |
|         my $mptab = xCAT::Table->new('mp', -create => 0);
 | |
|         unless ($mptab) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, "mp table must be configured for reseat" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             return;
 | |
|         }
 | |
|         my $mpent = $mptab->getNodeAttribs($sessdata->{node}, [qw/mpa id/]);
 | |
|         unless ($mpent and $mpent->{mpa} and $mpent->{id}) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, "mp table must be configured for reseat" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             return;
 | |
|         }
 | |
|         my $fpc     = $mpent->{mpa};
 | |
|         my $ipmitab = xCAT::Table->new("ipmi");
 | |
|         my $ipmihash = $ipmitab->getNodesAttribs([$fpc], [ 'bmc', 'username', 'password' ]);
 | |
|         my $authdata = xCAT::PasswordUtils::getIPMIAuth(noderange => [$fpc], ipmihash => $ipmihash);
 | |
|         my $nodeuser = $authdata->{$fpc}->{username};
 | |
|         my $nodepass = $authdata->{$fpc}->{password};
 | |
|         $sessdata->{slotnumber} = $mpent->{id};
 | |
| 	if (exists $fpcsessions{$mpent->{mpa}}) {
 | |
|             $sessdata->{fpcipmisession} = $fpcsessions{$mpent->{mpa}}; 
 | |
| 	    until ($sessdata->{fpcipmisession}->{logged}) {
 | |
|                 $sessdata->{fpcipmisession}->waitforrsp(timeout=>1);
 | |
|             }
 | |
| 	    $sessdata->{fpcipmisession}->subcmd(netfn => 0x32, command => 0xa4,
 | |
| 		    data => [ $sessdata->{slotnumber}, 2 ],
 | |
| 		    callback => \&fpc_node_reseat_complete, callback_args => $sessdata);
 | |
| 	} else {
 | |
|             $sessdata->{fpcipmisession} = xCAT::IPMI->new(bmc => $mpent->{mpa}, userid => $nodeuser, password => $nodepass);
 | |
| 	    $fpcsessions{$mpent->{mpa}} = $sessdata->{fpcipmisession};
 | |
|             $sessdata->{fpcipmisession}->login(callback => \&fpc_node_reseat, callback_args => $sessdata);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub fpc_node_reseat {
 | |
|     my $status   = shift;
 | |
|     my $sessdata = shift;
 | |
|     if ($status =~ /ERROR:/) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $status ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     $sessdata->{fpcipmisession}->subcmd(netfn => 0x32, command => 0xa4,
 | |
|         data => [ $sessdata->{slotnumber}, 2 ],
 | |
|         callback => \&fpc_node_reseat_complete, callback_args => $sessdata);
 | |
| }
 | |
| 
 | |
| sub fpc_node_reseat_complete {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     if ($rsp->{error}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     if ($rsp->{code} == 0) {
 | |
|         xCAT::SvrUtils::sendmsg("reseat", $callback, $sessdata->{node}, %allerrornodes);
 | |
|     } elsif ($rsp->{code} == 0xd5) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "No node in slot" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|     } else {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Unknown error code " . $rsp->{code} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub power {
 | |
|     my $sessdata = shift;
 | |
| 
 | |
|     my $netfun = 0x00;
 | |
|     my @cmd;
 | |
|     my @returnd = ();
 | |
|     my $error;
 | |
|     my $rc = 0;
 | |
|     my $text;
 | |
|     my $code;
 | |
| 
 | |
|     if (($sessdata->{subcommand} !~ /^on$|^off$|^reset$|^boot$|^stat$|^state$|^status$/) and isopenpower($sessdata)) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "unsupported command rpower $sessdata->{subcommand} for OpenPOWER" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if ($sessdata->{subcommand} eq "reseat") {
 | |
|         reseat_node($sessdata);
 | |
|     } elsif (not $sessdata->{acpistate} and is_systemx($sessdata)) { #Only implemented for IBM servers
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0x3a, command => 0x1d, data => [1], callback => \&power_with_acpi, callback_args => $sessdata);
 | |
|     } else {
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0, command => 1, data => [], callback => \&power_with_context, callback_args => $sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub power_with_acpi {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     if ($rsp->{error}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     if ($rsp->{code} == 0) {
 | |
|         if ($rsp->{data}->[0] == 3) {
 | |
|             $sessdata->{acpistate} = "suspend";
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #unless ($text) { $text = sprintf("Unknown error code %02xh",$rsp->{code}); }
 | |
|     #xCAT::SvrUtils::sendmsg([1,$text],$callback,$sessdata->{node},%allerrornodes);
 | |
|     #}
 | |
|     $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}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     if ($rsp->{code} != 0) {
 | |
|         $text = $codes{ $rsp->{code} };
 | |
|         unless ($text) { $text = sprintf("Unknown error code %02xh", $rsp->{code}); }
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $text ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     $sessdata->{powerstatus} = ($rsp->{data}->[0] & 1 ? "on" : "off");
 | |
|     my $reportstate;
 | |
|     if ($sessdata->{acpistate}) {
 | |
|         $reportstate = $sessdata->{acpistate};
 | |
|     } else {
 | |
|         $reportstate = $sessdata->{powerstatus};
 | |
|     }
 | |
| 
 | |
|     if ($sessdata->{subcommand} eq "stat" or $sessdata->{subcommand} eq "state" or $sessdata->{subcommand} eq "status") {
 | |
|         if ($sessdata->{powerstatprefix}) {
 | |
|             xCAT::SvrUtils::sendmsg($sessdata->{powerstatprefix} . $reportstate, $callback, $sessdata->{node}, %allerrornodes);
 | |
|         } else {
 | |
|             xCAT::SvrUtils::sendmsg($reportstate, $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|         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,
 | |
|         "cycle"   => 2,
 | |
|     );
 | |
|     if ($subcommand eq "on") {
 | |
|         if ($sessdata->{powerstatus} eq "on") {
 | |
|             if ($sessdata->{acpistate} and $sessdata->{acpistate} eq "suspend") { #ok, make this a wake
 | |
|                 $sessdata->{subcommand} = "wake";
 | |
|                 $sessdata->{ipmisession}->subcmd(netfn => 0x3a, command => 0x1d, data => [ 0, 0 ], callback => \&power_response, callback_args => $sessdata);
 | |
|                 return;
 | |
|             }
 | |
|             xCAT::SvrUtils::sendmsg("on", $callback, $sessdata->{node}, %allerrornodes);
 | |
|             $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") {
 | |
|             xCAT::SvrUtils::sendmsg("off", $callback, $sessdata->{node}, %allerrornodes);
 | |
|             $allerrornodes{ $sessdata->{node} } = 1;
 | |
|             return;
 | |
|         }
 | |
|     } elsif ($subcommand eq "suspend") {
 | |
|         my $waitforsuspend;
 | |
|         my $failtopowerdown;
 | |
|         my $failtoreset;
 | |
|         if ($sessdata->{powerstatus} eq "off") {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, "System is off, cannot be suspended" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (@{ $sessdata->{extraargs} } > 1) {
 | |
|             @ARGV = @{ $sessdata->{extraargs} };
 | |
|             use Getopt::Long;
 | |
|             unless (GetOptions(
 | |
|                     'w:i' => \$waitforsuspend,
 | |
|                     'o'   => \$failtopowerdown,
 | |
|                     'r'   => \$failtoreset,
 | |
|                 )) {
 | |
|                 xCAT::SvrUtils::sendmsg([ 1, "Error parsing arguments" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|         if (defined $waitforsuspend) {
 | |
|             if ($waitforsuspend == 0) { $waitforsuspend = 30; }
 | |
|             $sessdata->{waitforsuspend} = time() + $waitforsuspend;
 | |
|         }
 | |
|         $sessdata->{failtopowerdown} = $failtopowerdown;
 | |
|         $sessdata->{failtoreset}     = $failtoreset;
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0x3a, command => 0x1d, data => [ 0, 3 ], callback => \&power_response, callback_args => $sessdata);
 | |
|         return;
 | |
|     } elsif ($subcommand eq "wake") {
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0x3a, command => 0x1d, data => [ 0, 0 ], callback => \&power_response, callback_args => $sessdata);
 | |
|         return;
 | |
|     } elsif (not $argmap{$subcommand}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "unsupported command power $subcommand" ], $callback);
 | |
|         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 $newstat;
 | |
| 
 | |
|     if ($rsp->{error}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     my @returnd = ($rsp->{code}, @{ $rsp->{data} });
 | |
|     if ($rsp->{code}) {
 | |
|         my $text = $codes{ $rsp->{code} };
 | |
|         unless ($text) { $text = sprintf("Unknown response %02xh", $rsp->{code}); }
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $text ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     } else {
 | |
|         my $command = $sessdata->{subcommand};
 | |
|         my $status  = $sessdata->{powerstatus};
 | |
|         if ($command eq "on" or $command eq "boot") { $newstat = $::STATUS_POWERING_ON; }
 | |
|         if ($command eq "off" or $command eq "softoff") { $newstat = $::STATUS_POWERING_OFF; }
 | |
|         if ($command eq "reset") {
 | |
|             if ($status eq "off") {
 | |
|                 $newstat = $::STATUS_POWERING_OFF;
 | |
|             } else {
 | |
|                 $newstat = $::STATUS_POWERING_ON;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if ($newstat) {
 | |
|             push @{ $newnodestatus{$newstat} }, $sessdata->{node};
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if ($sessdata->{waitforsuspend}) { #have to repeatedly power stat until happy or timeout exceeded
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0x3a, command => 0x1d, data => [1], callback => \&power_wait_for_suspend, callback_args => $sessdata);
 | |
|         return;
 | |
|     }
 | |
|     xCAT::SvrUtils::sendmsg($sessdata->{subcommand}, $callback, $sessdata->{node}, %allerrornodes);
 | |
| }
 | |
| 
 | |
| sub power_wait_for_suspend {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     if ($rsp->{error}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     if ($rsp->{code} == 0) {
 | |
|         if ($rsp->{data}->[0] == 3) {
 | |
|             $sessdata->{acpistate} = "suspend";
 | |
|         }
 | |
|     }
 | |
|     if ($sessdata->{acpistate} eq "suspend") {
 | |
|         xCAT::SvrUtils::sendmsg("suspend", $callback, $sessdata->{node}, %allerrornodes);
 | |
|     } elsif ($sessdata->{waitforsuspend} <= time()) {
 | |
|         delete $sessdata->{waitforsuspend};
 | |
|         if ($sessdata->{failtopowerdown}) {
 | |
|             $sessdata->{subcommand} = 'off',
 | |
|               xCAT::SvrUtils::sendmsg([ 1, "Failed to enter suspend state, forcing off" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             power($sessdata);
 | |
|         } elsif ($sessdata->{failtoreset}) {
 | |
|             $sessdata->{subcommand} = 'reset',
 | |
|               xCAT::SvrUtils::sendmsg([ 1, "Failed to enter suspend state, forcing reset" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             power($sessdata);
 | |
|         } else {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, "Failed to enter suspend state" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|     } else {
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0x3a, command => 0x1d, delayxmit => 5, data => [1], callback => \&power_wait_for_suspend, callback_args => $sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 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") {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Only 'on' or 'off' is supported for IPMI managed nodes."], $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
| 
 | |
|     #if stuck with 1.5, say light for 255 seconds.  In 2.0, specify to turn it on forever
 | |
|     if ($subcommand eq "on") {
 | |
|         if ($sessdata->{mfg_id} == 19046 and $sessdata->{prod_id} == 13616) { # Lenovo SD350
 | |
|             $sessdata->{ipmisession}->subcmd(netfn => 0x3a, command => 6, data => [ 1, 1 ], callback => \&beacon_answer, callback_args => $sessdata);
 | |
| 	} elsif ($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
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     if ($rsp->{code}) {     #ipmi error
 | |
|         if ($codes{ $rsp->{code} }) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, $codes{ $rsp->{code} } ], $callback);
 | |
|         } else {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, sprintf("Unknown error code %02xh", $rsp->{code}) ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     xCAT::SvrUtils::sendmsg($sessdata->{subcommand}, $callback, $sessdata->{node}, %allerrornodes);
 | |
| }
 | |
| 
 | |
| sub inv {
 | |
|     my $sessdata   = shift;
 | |
|     my $command = $sessdata->{command};
 | |
|     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 mac wwn);
 | |
|     }
 | |
|     elsif ($subcommand eq "asset") {
 | |
|         $sessdata->{skipotherfru} = 1;
 | |
|         @types = qw(asset);
 | |
|     }
 | |
|     elsif ($subcommand eq "firm" || $subcommand eq "firmware") {
 | |
|         $sessdata->{skipotherfru} = 1;
 | |
|         @types = qw(firmware);
 | |
|     }
 | |
|     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);
 | |
|     }
 | |
|     elsif ($subcommand eq "hw" or $subcommand eq "dimm" or $subcommand eq "misc") {
 | |
|         @types = ($subcommand);
 | |
|     }
 | |
|     else {
 | |
|         my $usage_string = xCAT::Usage->getUsage($command);
 | |
|         $callback->({ error => ["$usage_string"], errorcode => [1] });
 | |
|         return 1;
 | |
|     }
 | |
|     $sessdata->{invtypes} = \@types;
 | |
|     initfru($sessdata);
 | |
| }
 | |
| 
 | |
| sub fru_initted {
 | |
|     my $sessdata = shift;
 | |
|     my $key;
 | |
|     my @args     = @{ $sessdata->{extraargs} };
 | |
|     my $up_group = undef;
 | |
|     if (grep /-t/, @args) {
 | |
|         $up_group = '1';
 | |
|     }
 | |
|     my @types  = @{ $sessdata->{invtypes} };
 | |
|     my $format = "%-20s %s";
 | |
| 
 | |
|     foreach $key (sort { $a <=> $b } keys %{ $sessdata->{fru_hash} }) {
 | |
|         my $fru = $sessdata->{fru_hash}->{$key};
 | |
|         my $type;
 | |
|         foreach $type (split /,/, $fru->rec_type) {
 | |
|             if (grep { $_ eq $type } @types) {
 | |
|                 my $bmcifo = "";
 | |
|                 if ($sessdata->{bmcnum} != 1) {
 | |
|                     $bmcifo = " on BMC " . $sessdata->{bmcnum};
 | |
|                 }
 | |
|                 xCAT::SvrUtils::sendmsg(sprintf($format . $bmcifo, $sessdata->{fru_hash}->{$key}->desc . ":", $sessdata->{fru_hash}->{$key}->value), $callback, $sessdata->{node}, %allerrornodes);
 | |
|                 if ($up_group and $type eq "model" and $fru->desc =~ /MTM/) {
 | |
|                     my $tmp_pre = xCAT::data::ibmhwtypes::parse_group($fru->value);
 | |
|                     if (defined($tmp_pre)) {
 | |
|                         xCAT::TableUtils->updatenodegroups($sessdata->{node}, $tmp_pre);
 | |
|                     }
 | |
|                 }
 | |
|                 last;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     if ($sessdata->{isite} and (grep { $_ eq "mac" } @types)) {
 | |
|         $needbladeinv{$_} = "mac";
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub add_textual_fru {
 | |
|     my $parsedfru   = shift;
 | |
|     my $description = shift;
 | |
|     my $category    = shift;
 | |
|     my $subcategory = shift;
 | |
|     my $types       = shift;
 | |
|     my $sessdata    = shift;
 | |
|     my %args        = @_;
 | |
| 
 | |
|     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})
 | |
|         }
 | |
|         my $index = 0;
 | |
|         foreach (@subfrus) {
 | |
|             $index++;
 | |
|             $fru = FRU->new();
 | |
|             $fru->rec_type($types);
 | |
|             if ($args{addnumber}) {
 | |
|                 $fru->desc($description . " " . $index);
 | |
|             } else {
 | |
|                 $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'; }
 | |
| 
 | |
|     if ($desc =~ /System Firmware/i and $category =~ /product/i) {
 | |
|         $type = 'firmware,bmc';
 | |
|     }
 | |
|     if ( ($desc =~ /NODE \d+/ or $desc =~ /Backplane/) and $category =~ /chassis/) {
 | |
|         add_textual_fru($parsedfru, $desc . " " . $categorydesc . "Part Number", $category, "partnumber", 'model', $sessdata);
 | |
|         add_textual_fru($parsedfru, $desc . " " . $categorydesc . "Serial Number", $category, "serialnumber", 'serial', $sessdata);
 | |
|     } else {
 | |
|         add_textual_fru($parsedfru, $desc . " " . $categorydesc . "Part Number", $category, "partnumber", $type, $sessdata);
 | |
|         add_textual_fru($parsedfru, $desc . " " . $categorydesc . "Serial Number", $category, "serialnumber", $type, $sessdata);
 | |
|     }
 | |
| 
 | |
|     add_textual_fru($parsedfru, $desc . " " . $categorydesc . "Manufacturer", $category, "manufacturer", $type, $sessdata);
 | |
|     add_textual_fru($parsedfru, $desc . " " . $categorydesc . "FRU Number", $category, "frunum", $type, $sessdata);
 | |
|     add_textual_fru($parsedfru, $desc . " " . $categorydesc . "Version", $category, "version", $type, $sessdata);
 | |
|     add_textual_fru($parsedfru, $desc . " " . $categorydesc . "MAC Address", $category, "macaddrs", "mac", $sessdata, addnumber => 1);
 | |
|     add_textual_fru($parsedfru, $desc . " " . $categorydesc . "WWN", $category, "wwns", "wwn", $sessdata, addnumber => 1);
 | |
|     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 or $prefix =~ /1aoo/i or $prefix =~ /tcoo/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;
 | |
|     $sessdata->{isanimm} = $isanimm;
 | |
|     if ($isanimm) {
 | |
| 
 | |
|         #get_imm_property(property=>"/v2/bios/build_id",callback=>\&got_bios_buildid,sessdata=>$sessdata);
 | |
|         check_for_ite(sessdata => $sessdata);
 | |
|     } else {
 | |
|         initfru_with_mprom($sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub got_bios_buildid {
 | |
|     my %res      = @_;
 | |
|     my $sessdata = $res{sessdata};
 | |
|     if ($res{data}) {
 | |
|         $sessdata->{biosbuildid} = $res{data};
 | |
|         get_imm_property(property => "/v2/bios/pending_build_id", callback => \&got_bios_pending_buildid, sessdata => $sessdata);
 | |
|     } else {
 | |
|         initfru_with_mprom($sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub got_bios_pending_buildid {
 | |
|     my %res      = @_;
 | |
|     my $sessdata = $res{sessdata};
 | |
|     $sessdata->{biosbuildpending} = 0;
 | |
|     if ($res{data} and $res{data} ne '  ') {
 | |
|         $sessdata->{biosbuildpending} = $res{data};
 | |
|     }
 | |
|     get_imm_property(property => "/v2/bios/build_version", callback => \&got_bios_version, sessdata => $sessdata);
 | |
| }
 | |
| 
 | |
| sub got_bios_version {
 | |
|     my %res      = @_;
 | |
|     my $sessdata = $res{sessdata};
 | |
|     if ($res{data}) {
 | |
|         $sessdata->{biosbuildversion} = $res{data};
 | |
|         get_imm_property(property => "/v2/bios/build_date", callback => \&got_bios_date, sessdata => $sessdata);
 | |
|     } else {
 | |
|         initfru_with_mprom($sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub got_bios_date {
 | |
|     my %res      = @_;
 | |
|     my $sessdata = $res{sessdata};
 | |
|     if ($res{data}) {
 | |
|         $sessdata->{biosbuilddate} = $res{data};
 | |
|         my $fru = FRU->new();
 | |
|         $fru->rec_type("bios,uefi,firmware");
 | |
|         $fru->desc("UEFI Version");
 | |
|         my $pending = "";
 | |
|         if ($sessdata->{biosbuildpending}) {
 | |
|             $pending = " [PENDING: $sessdata->{biosbuildpending}]";
 | |
|         }
 | |
|         my $value = $sessdata->{biosbuildversion} . " (" . $sessdata->{biosbuildid} . " " . $sessdata->{biosbuilddate} . ")$pending";
 | |
|         $fru->value($value);
 | |
|         $sessdata->{fru_hash}->{uefi} = $fru;
 | |
|         get_imm_property(property => "/v2/fpga/build_id", callback => \&got_fpga_buildid, sessdata => $sessdata);
 | |
|     } else {
 | |
|         initfru_with_mprom($sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub got_fpga_buildid {
 | |
|     my %res      = @_;
 | |
|     my $sessdata = $res{sessdata};
 | |
|     if ($res{data}) {
 | |
|         $sessdata->{fpgabuildid} = $res{data};
 | |
|         get_imm_property(property => "/v2/fpga/build_version", callback => \&got_fpga_version, sessdata => $sessdata);
 | |
|     } else {
 | |
|         get_imm_property(property => "/v2/ibmc/dm/fw/bios/backup_build_id", callback => \&got_backup_bios_buildid, sessdata => $sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub got_backup_bios_buildid {
 | |
|     my %res      = @_;
 | |
|     my $sessdata = $res{sessdata};
 | |
|     if ($res{data}) {
 | |
|         $sessdata->{backupbiosbuild} = $res{data};
 | |
|         get_imm_property(property => "/v2/ibmc/dm/fw/bios/backup_build_version", callback => \&got_backup_bios_version, sessdata => $sessdata);
 | |
|     } else {
 | |
|         initfru_with_mprom($sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub got_backup_bios_version {
 | |
|     my %res      = @_;
 | |
|     my $sessdata = $res{sessdata};
 | |
|     if ($res{data}) {
 | |
|         $sessdata->{backupbiosversion} = $res{data};
 | |
|         my $fru = FRU->new();
 | |
|         $fru->rec_type("bios,uefi,firmware");
 | |
|         $fru->desc("Backup UEFI Version");
 | |
|         $fru->value($sessdata->{backupbiosversion} . " (" . $sessdata->{backupbiosbuild} . ")");
 | |
|         $sessdata->{fru_hash}->{backupuefi} = $fru;
 | |
|         get_imm_property(property => "/v2/ibmc/dm/fw/imm2/backup_build_id", callback => \&got_backup_imm_buildid, sessdata => $sessdata);
 | |
|     } else {
 | |
|         initfru_with_mprom($sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub got_backup_imm_buildid {
 | |
|     my %res      = @_;
 | |
|     my $sessdata = $res{sessdata};
 | |
|     if ($res{data}) {
 | |
|         $sessdata->{backupimmbuild} = $res{data};
 | |
|         get_imm_property(property => "/v2/ibmc/dm/fw/imm2/backup_build_version", callback => \&got_backup_imm_version, sessdata => $sessdata);
 | |
|     } else {
 | |
|         initfru_with_mprom($sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub got_backup_imm_version {
 | |
|     my %res      = @_;
 | |
|     my $sessdata = $res{sessdata};
 | |
|     if ($res{data}) {
 | |
|         $sessdata->{backupimmversion} = $res{data};
 | |
|         get_imm_property(property => "/v2/ibmc/dm/fw/imm2/backup_build_date", callback => \&got_backup_imm_builddate, sessdata => $sessdata);
 | |
|     } else {
 | |
|         initfru_with_mprom($sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub got_backup_imm_builddate {
 | |
|     my %res      = @_;
 | |
|     my $sessdata = $res{sessdata};
 | |
|     if ($res{data}) {
 | |
|         $sessdata->{backupimmdate} = $res{data};
 | |
|         my $fru = FRU->new();
 | |
|         $fru->rec_type("bios,uefi,firmware");
 | |
|         $fru->desc("Backup IMM Version");
 | |
|         $fru->value($sessdata->{backupimmversion} . " (" . $sessdata->{backupimmbuild} . " " . $sessdata->{backupimmdate} . ")");
 | |
|         $sessdata->{fru_hash}->{backupimm} = $fru;
 | |
|         get_imm_property(property => "/v2/ibmc/trusted_buildid", callback => \&got_trusted_imm, sessdata => $sessdata);
 | |
|     } else {
 | |
|         initfru_with_mprom($sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub got_trusted_imm {
 | |
|     my %res      = @_;
 | |
|     my $sessdata = $res{sessdata};
 | |
|     if ($res{data}) {
 | |
|         my $fru = FRU->new();
 | |
|         $fru->rec_type("bios,uefi,firmware");
 | |
|         $fru->desc("Trusted IMM Build");
 | |
|         $fru->value($res{data});
 | |
|         $sessdata->{fru_hash}->{trustedimm} = $fru;
 | |
|     }
 | |
|     initfru_with_mprom($sessdata);
 | |
| }
 | |
| 
 | |
| sub got_fpga_version {
 | |
|     my %res      = @_;
 | |
|     my $sessdata = $res{sessdata};
 | |
|     if ($res{data}) {
 | |
|         $sessdata->{fpgabuildversion} = $res{data};
 | |
|         get_imm_property(property => "/v2/fpga/build_date", callback => \&got_fpga_date, sessdata => $sessdata);
 | |
|     } else {
 | |
|         initfru_with_mprom($sessdata);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub got_fpga_date {
 | |
|     my %res      = @_;
 | |
|     my $sessdata = $res{sessdata};
 | |
|     if ($res{data}) {
 | |
|         $sessdata->{fpgabuilddate} = $res{data};
 | |
|         my $fru = FRU->new();
 | |
|         $fru->rec_type("fpga,firmware");
 | |
|         $fru->desc("FPGA Version");
 | |
|         $fru->value($sessdata->{fpgabuildversion} . " (" . $sessdata->{fpgabuildid} . " " . $sessdata->{fpgabuilddate} . ")");
 | |
|         $sessdata->{fru_hash}->{fpga} = $fru;
 | |
|     }
 | |
|     initfru_with_mprom($sessdata);
 | |
| }
 | |
| 
 | |
| sub check_for_ite {
 | |
|     my %args = @_;
 | |
|     my @getpropertycommand;
 | |
|     my $sessdata = $args{sessdata};
 | |
|     $sessdata->{property_callback} = \&got_ite_check;    #$args{callback};
 | |
|     @getpropertycommand = unpack("C*", "/v2/cmm/");
 | |
|     my $length = 0b10000000 | (scalar @getpropertycommand); #use length to store tlv
 | |
|     unshift @getpropertycommand, $length;
 | |
| 
 | |
|     #command also needs the overall length
 | |
|     $length = (scalar @getpropertycommand);
 | |
|     unshift @getpropertycommand, 0; #do not recurse, though it's not going to matter anyway since we are just checking for the existence of the category
 | |
|     unshift @getpropertycommand, $length & 0xff;
 | |
|     unshift @getpropertycommand, ($length >> 8) & 0xff;
 | |
|     unshift @getpropertycommand, 2;    #get all properties command,
 | |
|     $sessdata->{ipmisession}->subcmd(netfn => 0x3a, command => 0xc4, data => \@getpropertycommand, callback => \&got_imm_property, callback_args => $sessdata);
 | |
| }
 | |
| 
 | |
| sub got_ite_check {
 | |
|     my %res      = @_;
 | |
|     my $sessdata = $res{sessdata};
 | |
|     if ($res{ccode} == 9) {    #success, end of tree means an ITE, remember this
 | |
|         $sessdata->{isite} = 1;
 | |
|     } else {
 | |
|         $sessdata->{isite} = 0;
 | |
|     }
 | |
|     get_imm_property(property => "/v2/bios/build_id", callback => \&got_bios_buildid, sessdata => $sessdata);
 | |
| }
 | |
| 
 | |
| sub get_imm_property {
 | |
|     my %args = @_;
 | |
|     my @getpropertycommand;
 | |
|     my $sessdata = $args{sessdata};
 | |
|     $sessdata->{property_callback} = $args{callback};
 | |
|     @getpropertycommand = unpack("C*", $args{property});
 | |
|     my $length = 0b10000000 | (scalar @getpropertycommand); #use length to store tlv
 | |
|     unshift @getpropertycommand, $length;
 | |
| 
 | |
|     #command also needs the overall length
 | |
|     $length = (scalar @getpropertycommand);
 | |
|     unshift @getpropertycommand, $length & 0xff;
 | |
|     unshift @getpropertycommand, ($length >> 8) & 0xff;
 | |
|     unshift @getpropertycommand, 0;    #the actual 'get proprety' command is 0.
 | |
|     $sessdata->{ipmisession}->subcmd(netfn => 0x3a, command => 0xc4, data => \@getpropertycommand, callback => \&got_imm_property, callback_args => $sessdata);
 | |
| }
 | |
| 
 | |
| sub got_imm_property {
 | |
|     if (check_rsp_errors(@_)) {
 | |
|         return;
 | |
|     }
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     my @data     = @{ $rsp->{data} };
 | |
|     my $propval  = shift @data;
 | |
|     my %res;
 | |
|     $res{sessdata} = $sessdata;
 | |
|     $res{ccode}    = $propval;
 | |
| 
 | |
|     if ($propval == 0) {    #success
 | |
|         shift @data;        #discard payload size
 | |
|         shift @data;        #discard payload size
 | |
|         while (@data) {
 | |
|             my $tlv = shift @data;
 | |
|             if ($tlv & 0b10000000) {
 | |
|                 $tlv = $tlv & 0b1111111;
 | |
|                 my @val = splice(@data, 0, $tlv);
 | |
|                 $res{data} = unpack("Z*", pack("C*", @val));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     $sessdata->{property_callback}->(%res);
 | |
| }
 | |
| 
 | |
| sub initfru_withguid {
 | |
|     my $sessdata = shift;
 | |
|     my $mfg_id   = $sessdata->{mfg_id};
 | |
|     my $prod_id  = $sessdata->{prod_id};
 | |
|     my $mprom;
 | |
| 
 | |
|     if (is_systemx($sessdata) && $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 ($rsp and $rsp->{code}) {    #non-zero return code..
 | |
|         $sessdata->{currfrudata} = "Unable to read";
 | |
|         if ($codes{ $rsp->{code} }) {
 | |
|             $sessdata->{currfrudata} .= " (" . $codes{ $rsp->{code} } . ")";
 | |
|         } else {
 | |
|             $sessdata->{currfrudata} .= sprintf(" (Unknown reason %02xh)", $rsp->{code});
 | |
|         }
 | |
|         $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}->{manufacturer}->{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}->{frunum}) {
 | |
|         $fru = FRU->new();
 | |
|         $fru->rec_type("misc");
 | |
|         $fru->desc("Board FRU Number");
 | |
|         $fru->value($fruhash->{board}->{frunum});
 | |
|         $sessdata->{fru_hash}->{ $frudex++ } = $fru;
 | |
|     }
 | |
|     if ($fruhash->{board}->{revision}) {
 | |
|         $fru = FRU->new();
 | |
|         $fru->rec_type("misc");
 | |
|         $fru->desc("Board Revision");
 | |
|         $fru->value($fruhash->{board}->{revision});
 | |
|         $sessdata->{fru_hash}->{ $frudex++ } = $fru;
 | |
|     }
 | |
|     if ($fruhash->{board}->{macaddrs}) {
 | |
|         my $macindex = 1;
 | |
|         foreach my $mac (@{ $fruhash->{board}->{macaddrs} }) {
 | |
|             $fru = FRU->new();
 | |
|             $fru->rec_type("mac");
 | |
|             $fru->desc("MAC Address $macindex");
 | |
|             $macindex++;
 | |
|             $fru->value($mac);
 | |
|             $sessdata->{fru_hash}->{ $frudex++ } = $fru;
 | |
|         }
 | |
|     }
 | |
|     if ($fruhash->{board}->{wwns}) {
 | |
|         my $macindex = 1;
 | |
|         foreach my $mac (@{ $fruhash->{board}->{wwns} }) {
 | |
|             $fru = FRU->new();
 | |
|             $fru->rec_type("wwn");
 | |
|             $fru->desc("WWN $macindex");
 | |
|             $macindex++;
 | |
|             $fru->value($mac);
 | |
|             $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
 | |
| 
 | |
|         if ($sessdata->{skipotherfru} and isopenpower($sessdata)) {
 | |
|             # For openpower Big Data servers, fru 2 has MTM/Serial and fru 43 has firmware information
 | |
|             # For openpower HPC servers, fru 3 has MTM/Serial and fru 47 has firmware information
 | |
|             @{$sessdata->{frus_for_openpower}} = qw(2 3 43 47);
 | |
|             my %fruids_hash = map {$_ => 1} @{$sessdata->{frus_for_openpower}};
 | |
|             foreach my $key (keys %{ $sessdata->{sdr_hash} }) {
 | |
|                 my $sdr = $sessdata->{sdr_hash}->{$key};
 | |
|                 unless ($sdr->rec_type == 0x11) {
 | |
|                     next;
 | |
|                 }
 | |
|                 my $fru_id = $sdr->sensor_number;
 | |
|                 if (defined($fruids_hash{$fru_id})) {
 | |
|                     $sessdata->{sdr_info_for_openpower}->{$fru_id} = $sdr; 
 | |
|                 }
 | |
|             }
 | |
|             $sessdata->{currfruid} = shift @{$sessdata->{frus_for_openpower}};
 | |
|             $sessdata->{currfrusdr} = $sessdata->{sdr_info_for_openpower}->{$sessdata->{currfruid}}; 
 | |
|             $sessdata->{ipmisession}->subcmd(netfn => 0xa, command => 0x10, data => [ $sessdata->{currfruid} ], callback => \&process_currfruid, callback_args => $sessdata);
 | |
|             return;
 | |
|         }
 | |
|         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) {    #skip non fru sdr stuff
 | |
|             next;
 | |
|         }
 | |
| 
 | |
|         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} };
 | |
|         while ($sessdata->{currfrusdr}->sensor_number == 0 and scalar @{ $sessdata->{dimmfru} }) {
 | |
|             $sessdata->{currfrusdr} = shift @{ $sessdata->{dimmfru} };
 | |
|         }
 | |
|         if ($sessdata->{currfrusdr}->sensor_number != 0) {
 | |
|             $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);
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     if (scalar @{ $sessdata->{genhwfru} }) {
 | |
|         $sessdata->{currfrusdr} = shift @{ $sessdata->{genhwfru} };
 | |
|         while ($sessdata->{currfrusdr}->sensor_number == 0 and scalar @{ $sessdata->{genhwfru} }) {
 | |
|             $sessdata->{currfrusdr} = shift @{ $sessdata->{genhwfru} };
 | |
|         }
 | |
|         if ($sessdata->{currfrusdr}->sensor_number != 0) {
 | |
|             $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);
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     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});
 | |
|         if ($sessdata->{currfrusdr}) {
 | |
|             $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;
 | |
|         $global_sessdata = $sessdata; #pass by global, evil, but practical this time
 | |
|         ($err, $fruhash) = parsefru($sessdata->{currfrudata});
 | |
|         $global_sessdata = undef;     #revert state of global
 | |
|         if ($err) {
 | |
|             my $fru = FRU->new();
 | |
|             if ($sessdata->{currfrutype} and $sessdata->{currfrutype} eq 'dimm') {
 | |
|                 $fru->rec_type("dimm,hw");
 | |
|             } else {
 | |
|                 $fru->rec_type("hw");
 | |
|             }
 | |
|             $fru->value($err);
 | |
|             if ($sessdata->{currfrusdr}) {
 | |
|                 $fru->desc($sessdata->{currfrusdr}->id_string);
 | |
|             }
 | |
|             if (exists($sessdata->{frudex})) {
 | |
|                 $sessdata->{fru_hash}->{ $sessdata->{frudex} } = $fru;
 | |
|                 $sessdata->{frudex} += 1;
 | |
|             }
 | |
|             undef $sessdata->{currfrudata}; #skip useless calls to add more frus when parsing failed miserably anyway
 | |
| 
 | |
|             #xCAT::SvrUtils::sendmsg([1,":Error reading fru area ".$sessdata->{currfruid}.": $err"],$callback);
 | |
|             #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 ($sessdata->{skipotherfru}) {
 | |
|         if (scalar @{$sessdata->{frus_for_openpower}}) {
 | |
|             $sessdata->{currfruid} = shift @{$sessdata->{frus_for_openpower}};
 | |
|             $sessdata->{currfrusdr} = $sessdata->{sdr_info_for_openpower}->{$sessdata->{currfruid}}; 
 | |
|             $sessdata->{ipmisession}->subcmd(netfn => 0xa, command => 0x10, data => [ $sessdata->{currfruid} ], callback => \&process_currfruid, callback_args => $sessdata);
 | |
|             return;
 | |
|         }
 | |
|         fru_initted($sessdata);
 | |
|         return; 
 | |
|     }
 | |
| 
 | |
|     if (scalar @{ $sessdata->{dimmfru} }) {
 | |
|         $sessdata->{currfrusdr} = shift @{ $sessdata->{dimmfru} };
 | |
|         while ($sessdata->{currfrusdr}->sensor_number == 0 and scalar @{ $sessdata->{dimmfru} }) {
 | |
|             $sessdata->{currfrusdr} = shift @{ $sessdata->{dimmfru} };
 | |
|         }
 | |
|         if ($sessdata->{currfrusdr}->sensor_number != 0) {
 | |
|             $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);
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     if (scalar @{ $sessdata->{genhwfru} }) {
 | |
|         $sessdata->{currfrusdr} = shift @{ $sessdata->{genhwfru} };
 | |
|         while ($sessdata->{currfrusdr}->sensor_number == 0 and scalar @{ $sessdata->{genhwfru} }) {
 | |
|             $sessdata->{currfrusdr} = shift @{ $sessdata->{genhwfru} };
 | |
|         }
 | |
|         if ($sessdata->{currfrusdr}->sensor_number != 0) {
 | |
|             $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);
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     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}) {
 | |
| 
 | |
|             # Fix FRU 43,48 and 49 for GRS server that they can not return as much data as shall return
 | |
|             if ($data[0] ge 0) {
 | |
|                 $sessdata->{currfrudone} = 1;
 | |
|             } else {
 | |
|                 my $text = "Received incorrect data from BMC for FRU ID: " . $sessdata->{currfruid};
 | |
|                 xCAT::SvrUtils::sendmsg($text, $callback, $sessdata->{node}, %allerrornodes);
 | |
|                 add_fruhash($sessdata);
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|         shift @data;
 | |
|         push @{ $sessdata->{currfrudata} }, @data;
 | |
|         if ($sessdata->{currfrudone}) {
 | |
|             if ($sessdata->{isite} and $sessdata->{currfrusdr} and ($sessdata->{currfrusdr}->fru_oem & 0x80)) {
 | |
| 
 | |
|                 #IBM OEM command, d0,51,0 further qualifies the command name, we'll first take a stop at block 0, offset 2, one byte, to get VPD version number
 | |
|                 #command structured as:
 | |
|                 #d0,51,0 = command set identifier
 | |
|                 #lsb of offset
 | |
|                 #msb of offset
 | |
|                 #address type (1 for fru id)
 | |
|                 #address (fru id for our use)
 | |
|                 #1 - fixed value
 | |
|                 #lsb - size
 | |
|                 #msb - size
 | |
|                 #vpd_base_specivication_ver2.x
 | |
|                 $sessdata->{ipmisession}->subcmd(netfn => 0x2e, command => 0x51, data => [ 0xd0, 0x51, 0, 0x2, 0x0, 1, $sessdata->{currfruid}, 1, 1, 0 ], callback => \&got_vpd_version, callback_args => $sessdata);
 | |
|             } else {
 | |
|                 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 got_vpd_version {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     unless ($rsp and not $rsp->{error} and $rsp->{code} == 0 and $rsp->{data}->[5] == 2) { #unless the query was successful and major vpd version was 2
 | |
|             #short over to adding the fru hash as-is
 | |
|         add_fruhash($sessdata);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     #making it this far, we have affirmative confirmation of ibm oem vpd data, time to chase component mac, wwpn, and maybe mezz firmware
 | |
|     #will need:
 | |
|     #	block 0, offset 0c8h use the offset to add to block 1 offsets (usually 400h), denoting as $blone
 | |
|     #       block 1, $blone+6 - 216 bytes: 6 sets of 36 byte version information (TODO)
 | |
|     #	block 1, $blone+0x1d0: port type first 4 bits protocol, last 3 bits addressing
 | |
|     #	block 1, $blone+0x240: 64 bytes, up to 8 sets of addresses, mac is left aligned
 | |
|     #	block 1, $blone+0x300: if mac+wwn, grab wwn
 | |
|     $sessdata->{ipmisession}->subcmd(netfn => 0x2e, command => 0x51, data => [ 0xd0, 0x51, 0, 0xc8, 0x0, 1, $sessdata->{currfruid}, 1, 2, 0 ], callback => \&got_vpd_block1, callback_args => $sessdata);
 | |
| }
 | |
| 
 | |
| sub got_vpd_block1 {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     unless ($rsp and not $rsp->{error} and $rsp->{code} == 0) { # if this should go wonky, jump ahead
 | |
|         add_fruhash($sessdata);
 | |
|         return;
 | |
|     }
 | |
|     $sessdata->{vpdblock1offset} = $rsp->{data}->[5] << 8 + $rsp->{data}->[6];
 | |
|     my $ptoffset = $sessdata->{vpdblock1offset} + 0x1d0;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn => 0x2e, command => 0x51, data => [ 0xd0, 0x51, 0, $ptoffset & 0xff, $ptoffset >> 8, 1, $sessdata->{currfruid}, 1, 1, 0 ], callback => \&got_portaddr_type, callback_args => $sessdata);
 | |
| }
 | |
| 
 | |
| sub got_portaddr_type {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     unless ($rsp and not $rsp->{error} and $rsp->{code} == 0) { # if this should go wonky, jump ahead
 | |
|         add_fruhash($sessdata);
 | |
|         return;
 | |
|     }
 | |
|     my $addrtype = $rsp->{data}->[5] & 0b111;
 | |
|     if ($addrtype == 0b101) {
 | |
|         $sessdata->{needmultiaddr} = 1;
 | |
|         $sessdata->{curraddrtype}  = "mac";
 | |
|     } elsif ($addrtype == 0b1) {
 | |
|         $sessdata->{curraddrtype} = "mac";
 | |
|     } elsif ($addrtype == 0b10) {
 | |
|         $sessdata->{curraddrtype} = "wwn";
 | |
|     } else {    #for now, skip polling addresses I haven't examined directly
 | |
|         add_fruhash($sessdata);
 | |
|         return;
 | |
|     }
 | |
|     my $addroffset = $sessdata->{vpdblock1offset} + 0x240;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn => 0x2e, command => 0x51, data => [ 0xd0, 0x51, 0, $addroffset & 0xff, $addroffset >> 8, 1, $sessdata->{currfruid}, 1, 64, 0 ], callback => \&got_vpd_addresses, callback_args => $sessdata);
 | |
| 
 | |
| 
 | |
| }
 | |
| 
 | |
| sub got_vpd_addresses {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     unless ($rsp and not $rsp->{error} and $rsp->{code} == 0) { # if this should go wonky, jump ahead
 | |
|         add_fruhash($sessdata);
 | |
|         return;
 | |
|     }
 | |
|     my @addrdata = @{ $rsp->{data} };
 | |
|     splice @addrdata, 0, 5;    # remove the header info
 | |
|     my $macstring = "1";
 | |
|     while ($macstring !~ /^00:00:00:00:00:00/) {
 | |
|         my @currmac = splice @addrdata, 0, 8;
 | |
|         unless ((scalar @currmac) == 8) {
 | |
|             last;
 | |
|         }
 | |
|         $macstring = sprintf("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", @currmac);
 | |
|         if ($macstring =~ /^00:00:00:00:00:00:00:00/) {
 | |
|             last;
 | |
|         }
 | |
|         if ($sessdata->{curraddrtype} eq "mac") {
 | |
|             $macstring =~ s/:..:..$//;
 | |
|             push @{ $sessdata->{currmacs} }, $macstring;
 | |
|         } elsif ($sessdata->{curraddrtype} eq "wwn") {
 | |
|             push @{ $sessdata->{currwwns} }, $macstring;
 | |
|         }
 | |
|     }
 | |
|     if ($sessdata->{needmultiaddr}) {
 | |
|         $sessdata->{needmultiaddr} = 0;
 | |
|         $sessdata->{curraddrtype}  = "wwn";
 | |
|         my $addroffset = $sessdata->{vpdblock1offset} + 0x300;
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0x2e, command => 0x51, data => [ 0xd0, 0x51, 0, $addroffset & 0xff, $addroffset >> 8, 1, $sessdata->{currfruid}, 1, 64, 0 ], callback => \&got_vpd_addresses, callback_args => $sessdata);
 | |
|         return;
 | |
|     }
 | |
|     add_fruhash($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 (ref $bytes) {
 | |
|         return $bytes, undef;
 | |
|     }
 | |
|     if (!defined $bytes->[0]) {
 | |
|         return "clear", undef;
 | |
|     }
 | |
|     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 "Unrecognized FRU format", undef;
 | |
|         }
 | |
|     }
 | |
|     elsif (!$bytes->[1] and !$bytes->[2] and !$bytes->[3] and !$bytes->[4] and !$bytes->[5]) {
 | |
|         return "No data available", 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;
 | |
|         if ($currsize > 0) {
 | |
|             @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;
 | |
| 
 | |
|         # some systems have malformed board info
 | |
|         # Just in case, give the board area parser access to end
 | |
|         # of total area unless product or multirecord is there
 | |
|         my $endidx = $#{$bytes};
 | |
|         if ($bytes->[4] or $bytes->[5]) {
 | |
|             $endidx = $curridx + $currsize - 1;
 | |
|         }
 | |
|         if ($currsize > 0) {
 | |
|             @currarea = @{$bytes}[ $curridx .. $endidx ];
 | |
|             $fruhash->{board} = parseboard(@currarea);
 | |
|         }
 | |
|     }
 | |
|     if (ref $global_sessdata->{currmacs}) {
 | |
|         $fruhash->{board}->{macaddrs} = [];
 | |
|         push @{ $fruhash->{board}->{macaddrs} }, @{ $global_sessdata->{currmacs} };
 | |
|         delete $global_sessdata->{currmacs}; # consume the accumulated mac addresses to avoid afflicting subsequent fru
 | |
|     }
 | |
|     if (ref $global_sessdata->{currwwns}) {
 | |
|         push @{ $fruhash->{board}->{wwns} }, @{ $global_sessdata->{currwwns} };
 | |
|         delete $global_sessdata->{currwwns};    # consume wwns
 | |
|     }
 | |
|     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;
 | |
|         if ($currsize > 0) {
 | |
|             @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;
 | |
|         if ($bytes->[$curridx] <= 5) {    #don't even try to parse unknown stuff
 | |
|                                           #some records don't comply to any SPEC
 | |
|             while (not $last and $curridx < (scalar @$bytes)) {
 | |
|                 if ($bytes->[ $curridx + 1 ] & 128) {
 | |
|                     $last = 1;
 | |
|                 }
 | |
|                 $currsize = $bytes->[ $curridx + 2 ];
 | |
|                 push @{ $fruhash->{extra} }, @{$bytes}[ $curridx .. $curridx + 4 + $currsize - 1 ];
 | |
|                 $curridx += 5 + $currsize;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     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);
 | |
|         if ($currsize < 0) { last }
 | |
|     }
 | |
|     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);
 | |
|         if ($currsize < 0) { last }
 | |
|     }
 | |
|     if ($global_sessdata->{isanimm}) { #we can understand more specifically some of the extra fields...
 | |
|         $boardinf{frunum}   = $boardinf{extra}->[0]->{value};
 | |
|         $boardinf{revision} = $boardinf{extra}->[4]->{value};
 | |
| 
 | |
|         #time to process the mac field...
 | |
|         my $macdata   = $boardinf{extra}->[6]->{value};
 | |
|         my $macstring = "1";
 | |
|         my $macprefix;
 | |
|         while ($macdata and ref $macdata and $macstring !~ /00:00:00:00:00:00/ and not ref $global_sessdata->{currmacs}) {
 | |
|             my @currmac = splice @$macdata, 0, 6;
 | |
|             unless ((scalar @currmac) == 6) {
 | |
|                 last;
 | |
|             }
 | |
|             $macstring = sprintf("%02x:%02x:%02x:%02x:%02x:%02x", @currmac);
 | |
|             unless ($macprefix) {
 | |
|                 $macprefix = substr($macstring, 0, 8);
 | |
|             }
 | |
|             if ($macstring !~ /00:00:00:00:00:00/ and $macstring =~ /^$macprefix/) {
 | |
|                 push @{ $boardinf{macaddrs} }, $macstring;
 | |
|             }
 | |
|         }
 | |
|         delete $boardinf{extra};
 | |
|     }
 | |
|     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);
 | |
|         if ($currsize < 0) { last }
 | |
|     }
 | |
|     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) {
 | |
|         # The global_sessdata store the sessdata for a node when parsefru, and it is cleaned after parsefru
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Error encountered when parsing FRU data from BMC" ], $callback, $global_sessdata->{node}, %allerrornodes);
 | |
|         return 0, 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 and $sdr->rec_type != 192 and $sdr->rec_type != 17) {
 | |
|             $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};
 | |
|     my $cmdargv    = $sessdata->{extraargs};
 | |
|     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;
 | |
| 
 | |
|     #reventlog <node> [[all] [-s] | <num> [-s]| clear]
 | |
|     $subcommand = undef;
 | |
|     my $arg = shift(@$cmdargv);
 | |
|     while ($arg) {
 | |
|         if ($arg eq "all" or $arg eq "clear" or $arg =~ /^\d+$/) {
 | |
|             if (defined($subcommand)) {
 | |
|                 return (1, "revenglog $subcommand $arg invalid");
 | |
|             }
 | |
|             $subcommand = $arg;
 | |
|         } elsif ($arg =~ /^-s$/) {
 | |
|             $sessdata->{sort} = 1;
 | |
|         } else {
 | |
|             return (1, "unsupported command eventlog $arg");
 | |
|         }
 | |
|         $arg = shift(@$cmdargv);
 | |
|     }
 | |
| 
 | |
|     unless (defined($subcommand)) {
 | |
|         $subcommand = 'all';
 | |
|     }
 | |
|     if ($subcommand eq "all") {
 | |
|         $sessdata->{fullsel} = 1;
 | |
|     }
 | |
|     elsif ($subcommand eq "clear") {
 | |
|         if (exists($sessdata->{sort})) {
 | |
|             return (1, "option \"first\" can not work with $subcommand");
 | |
|         }
 | |
|     }
 | |
|     elsif ($subcommand =~ /^\d+$/) {
 | |
|         $sessdata->{numevents}       = $subcommand;
 | |
|         $sessdata->{displayedevents} = 0;
 | |
|     }
 | |
|     else {
 | |
|         return (1, "unsupported command eventlog $subcommand");
 | |
|     }
 | |
|     $sessdata->{subcommand} = $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) {
 | |
|         xCAT::SvrUtils::sendmsg(sprintf("SEL version 51h support only, version reported: %x", $sel_version), $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     hexdump(\@returnd);
 | |
|     my $num_entries = ($returnd[3] << 8) + $returnd[2];
 | |
|     if ($num_entries <= 0) {
 | |
|         xCAT::SvrUtils::sendmsg("no SEL entries", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     my $canres = $returnd[14] & 0b00000010;
 | |
|     if (!$canres) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "SEL reservation not supported" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         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;
 | |
|     if (exists($sessdata->{sort})) {
 | |
|         $sessdata->{ipmisession}->subcmd(netfn => 0xa, command => 0x43, data => [ 0, 0, 0xFF, 0xFF, 0x00, 0xFF ], callback => \&got_sel, callback_args => $sessdata);
 | |
|     } else {
 | |
|         $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;
 | |
|     my $next_rec_ms;
 | |
|     my @sel_data = @returnd[ 3 .. 19 ];
 | |
|     if (exists($sessdata->{sort})) {
 | |
|         $next_rec_ls = $sel_data[0] - 1;
 | |
|         $next_rec_ms = $sel_data[1];
 | |
|         if (($next_rec_ls < 0) && ($next_rec_ms > 0)) {
 | |
|             $next_rec_ls += 256;
 | |
|             $next_rec_ms -= 1;
 | |
|         }
 | |
|     } else {
 | |
|         $next_rec_ls = $returnd[1];
 | |
|         $next_rec_ms = $returnd[2];
 | |
|     }
 | |
|     $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}) {
 | |
|             xCAT::SvrUtils::sendmsg($text, $callback, $sessdata->{node}, %allerrornodes);
 | |
|         } else {
 | |
|             push(@{ $sessdata->{selentries} }, $text);
 | |
|         }
 | |
|         if (($next_rec_ms == 0xFF && $next_rec_ls == 0xFF) or
 | |
|             ($next_rec_ms == 0x0 && $next_rec_ls == 0x0)) {
 | |
|             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->{bmcnum} != 1) {
 | |
|         $text .= " on BMC " . $sessdata->{bmcnum};
 | |
|     }
 | |
|     if ($sessdata->{auxloginfo} and $sessdata->{auxloginfo}->{$entry}) {
 | |
|         $text .= " with additional data:";
 | |
|         if ($sessdata->{fullsel} || ($sessdata->{numevents}
 | |
|                 && $sessdata->{numevents} > $sessdata->{displayedevents})) {
 | |
|             xCAT::SvrUtils::sendmsg($text, $callback, $sessdata->{node}, %allerrornodes);
 | |
|             foreach (split /\n/, $sessdata->{auxloginfo}->{$entry}) {
 | |
|                 xCAT::SvrUtils::sendmsg($_, $sessdata->{node});
 | |
|             }
 | |
|             $sessdata->{displayedevents}++;
 | |
|         } else {
 | |
|             push(@{ $sessdata->{selentries} }, $text);
 | |
|             push @{ $sessdata->{selentries} }, split /\n/, $sessdata->{auxloginfo}->{$entry};
 | |
|         }
 | |
| 
 | |
|     } else {
 | |
|         if ($sessdata->{fullsel} || ($sessdata->{numevents}
 | |
|                 && $sessdata->{numevents} > $sessdata->{displayedevents})) {
 | |
|             xCAT::SvrUtils::sendmsg($text, $callback, $sessdata->{node}, %allerrornodes);
 | |
|             $sessdata->{displayedevents}++;
 | |
|         } else {
 | |
|             push(@{ $sessdata->{selentries} }, $text);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (($next_rec_ms == 0xFF && $next_rec_ls == 0xFF) or
 | |
|         ($next_rec_ms == 0x0 && $next_rec_ls == 0x0)) {
 | |
|         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;
 | |
|     xCAT::SvrUtils::sendmsg("SEL cleared", $callback, $sessdata->{node}, %allerrornodes);
 | |
| }
 | |
| 
 | |
| #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",
 | |
|             0x3 => "Power Supply rating mismatch",
 | |
|             0x4 => "Voltage rating mismatch",
 | |
|         );
 | |
|         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 operating system wake-up vector",
 | |
|                 0x13 => "Starting operating 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";
 | |
|         } elsif ($offset == 0x06) {
 | |
|             if (defined $event_data_2) {
 | |
|                 if (defined($event_data_3) and ($event_data_3 & 0x80 == 0x80)) {
 | |
|                     $text = "Vendor-specific processor number:";
 | |
|                 } else {
 | |
|                     $text = "Entity Instance number:";
 | |
|                 }
 | |
|                 $text .= sprintf("%02xh", $event_data_2 & 0xff);
 | |
|             } else {
 | |
|                 $text = "for all Processor sensors";
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if ($sensor_type == 0x12) {
 | |
|         if ($offset == 0x03) {
 | |
|             my %extra = {
 | |
|                 0x0 => "Log Entry Action: entry added",
 | |
|                 0x1 => "Log Entry Action: entry added because event did not be map to standard IPMI event",
 | |
|                 0x2 => "Log Entry Action: entry added along with one or more corresponding SEL entries",
 | |
|                 0x3 => "Log Entry Action: log cleared",
 | |
|                 0x4 => "Log Entry Action: log disabled",
 | |
|                 0x5 => "Log Entry Action: log enabled",
 | |
|             };
 | |
|             $text = "$text, " . $extra{ ($event_data_2 >> 4) & 0x0f };
 | |
|             %extra = {
 | |
|                 0x0 => "Log Type:MCA Log",
 | |
|                 0x1 => "Log Type:OEM 1",
 | |
|                 0x2 => "Log Type:OEM 2",
 | |
|             };
 | |
|             $text = "$text, " . $extra{ ($event_data_2) & 0x0f };
 | |
|             $text =~ s/^, //;
 | |
|         }
 | |
| 
 | |
|         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 ($offset == 0x05) {
 | |
|             if ($event_data_2 & 0x80) {
 | |
|                 $text = "$text, event is second of pair";
 | |
|             } elsif ($event_data_2 & 0x80 == 0) {
 | |
|                 $text = "$text, event is first of pair";
 | |
|             }
 | |
|             if ($event_data_2 & 0x0F == 0x1) {
 | |
|                 $text = "$text, SDR Timestamp Clock updated";
 | |
|             } elsif ($event_data_2 & 0x0F == 0x0) {
 | |
|                 $text = "$text, SEL Timestamp Clock updated";
 | |
|             }
 | |
|             $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) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, ":IBM::EnergyManager package required for this value" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     my @entries;
 | |
|     $sessdata->{iemcallback} = \&readenergy_withiem;
 | |
|     initiem($sessdata);
 | |
| }
 | |
| 
 | |
| sub readenergy_withiem {
 | |
|     my $sessdata = shift;
 | |
|     $sessdata->{iem}->prep_get_precision();
 | |
|     $sessdata->{iemcallback} = \&got_precision;
 | |
|     execute_iem_commands($sessdata); #this gets all precision data initialized for AC and DC
 | |
|      # we need not make use of the generic extraction function, so we call execute_iem instead of process_data
 | |
|      #sorry the perl api I wrote sucks..
 | |
| }
 | |
| 
 | |
| sub got_precision {
 | |
|     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;
 | |
|     unless ($sessdata->{abortediem}) {
 | |
|         $sessdata->{iemtextdata} .= sprintf(" +/-%.1f%%", $sessdata->{iem}->energy_ac_precision() * 0.1); #note while \x{B1} would be cool, it's non-trivial to support
 | |
|         xCAT::SvrUtils::sendmsg($sessdata->{iemtextdata}, $callback, $sessdata->{node}, %allerrornodes);
 | |
|         $sessdata->{abortediem} = 0;
 | |
|     }
 | |
| 
 | |
|     #this would be 'if sessdata->{abortediem}'.  Thus far this is only triggered in the case of a system that fairly obviously
 | |
|     #shouldn't have an AC meter.  As a consequence, don't output data that would suggest a user might actually get it
 | |
|     #in that case, another entity can provide a measure of the AC usage, but only an aggregate measure not an individual measure
 | |
|     #        $sessdata->{iemtextdata} = "AC Energy Usage: ";
 | |
|     #        if ($sessdata->{abortediemreason}) {
 | |
|     #             $sessdata->{iemtextdata} .= $sessdata->{abortediemreason};
 | |
|     #         }
 | |
|     #        xCAT::SvrUtils::sendmsg($sessdata->{iemtextdata},$callback,$sessdata->{node},%allerrornodes);
 | |
|     $sessdata->{iem}->prep_get_dc_energy();
 | |
|     $sessdata->{iemcallback} = \&got_dc_energy;
 | |
|     process_data_from_iem($sessdata);
 | |
| }
 | |
| 
 | |
| 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
 | |
|     xCAT::SvrUtils::sendmsg($sessdata->{iemtextdata}, $callback, $sessdata->{node}, %allerrornodes);
 | |
|     $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);
 | |
|     xCAT::SvrUtils::sendmsg($sessdata->{iemtextdata}, $callback, $sessdata->{node}, %allerrornodes);
 | |
|     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 {
 | |
|     my $rsp      = $_[0];
 | |
|     my $sessdata = $_[1];
 | |
|     if ($rsp->{code} == 0xcb) {
 | |
|         $sessdata->{abortediem}       = 1;
 | |
|         $sessdata->{abortediemreason} = "Not Present";
 | |
|         $sessdata->{iemcallback}->($sessdata);
 | |
|         return;
 | |
|     }
 | |
|     if (check_rsp_errors(@_)) { #error while in an IEM transaction, skip to the end
 | |
|         $sessdata->{abortediem} = 1;
 | |
|         $sessdata->{iemcallback}->($sessdata);
 | |
|         return;
 | |
|     }
 | |
|     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 gotchassis {    #get chassis status command
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     unless (check_rsp_errors($rsp, $sessdata)) {
 | |
|         my @data = @{ $rsp->{data} };
 | |
|         my $powerstat;
 | |
|         if ($data[0] & 1) {
 | |
|             $powerstat = "on";
 | |
|         } else {
 | |
|             $powerstat = "off";
 | |
|         }
 | |
|         xCAT::SvrUtils::sendmsg("Power Status: $powerstat", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         if ($data[0] & 0b10) {
 | |
|             $powerstat = "true";
 | |
|         } else {
 | |
|             $powerstat = "false";
 | |
|         }
 | |
|         xCAT::SvrUtils::sendmsg("Power Overload: $powerstat", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         if ($data[0] & 0b100) {
 | |
|             $powerstat = "active";
 | |
|         } else {
 | |
|             $powerstat = "inactive";
 | |
|         }
 | |
|         xCAT::SvrUtils::sendmsg("Power Interlock: $powerstat", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         if ($data[0] & 0b1000) {
 | |
|             $sessdata->{healthsummary} &= 2;    #set to critical state
 | |
|             $powerstat = "true";
 | |
|         } else {
 | |
|             $powerstat = "false";
 | |
|         }
 | |
|         xCAT::SvrUtils::sendmsg("Power Fault: $powerstat", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         if ($data[0] & 0b10000) {
 | |
|             $sessdata->{healthsummary} &= 2;    #set to critical state
 | |
|             $powerstat = "true";
 | |
|         } else {
 | |
|             $powerstat = "false";
 | |
|         }
 | |
|         xCAT::SvrUtils::sendmsg("Power Control Fault: $powerstat", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         my $powpolicy = ($data[0] & 0b1100000) >> 5;
 | |
|         my %powpolicies = (0 => "Always off", 1 => "Last State", 2 => "Always on", 3 => "Unknown");
 | |
|         xCAT::SvrUtils::sendmsg("Power Restore Policy: " . $powpolicies{$powpolicy}, $callback, $sessdata->{node}, %allerrornodes);
 | |
|         my @lastevents;
 | |
|         if ($data[1] & 0b1) {
 | |
|             push @lastevents, "AC failed";
 | |
|         }
 | |
|         if ($data[1] & 0b10) {
 | |
|             $sessdata->{healthsummary} &= 2;    #set to critical state
 | |
|             push @lastevents, "Power overload";
 | |
|         }
 | |
|         if ($data[1] & 0b100) {
 | |
|             $sessdata->{healthsummary} &= 1;    #set to critical state
 | |
|             push @lastevents, "Interlock activated";
 | |
|         }
 | |
|         if ($data[1] & 0b1000) {
 | |
|             $sessdata->{healthsummary} &= 2;    #set to critical state
 | |
|             push @lastevents, "Power Fault";
 | |
|         }
 | |
|         if ($data[1] & 0b10000) {
 | |
|             push @lastevents, "By Request";
 | |
|         }
 | |
|         my $lastevent = join(",", @lastevents);
 | |
|         xCAT::SvrUtils::sendmsg("Last Power Event: $lastevent", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         if ($data[2] & 0b1) {
 | |
|             $sessdata->{healthsummary} &= 1;    #set to warn state
 | |
|             $powerstat = "active";
 | |
|         } else {
 | |
|             $powerstat = "inactive";
 | |
|         }
 | |
|         xCAT::SvrUtils::sendmsg("Chassis intrusion: $powerstat", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         if ($data[2] & 0b10) {
 | |
|             $powerstat = "active";
 | |
|         } else {
 | |
|             $powerstat = "inactive";
 | |
|         }
 | |
|         xCAT::SvrUtils::sendmsg("Front Panel Lockout: $powerstat", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         if ($data[2] & 0b100) {                 # drive fault
 | |
|             $sessdata->{healthsummary} &= 2;    #set to critical state
 | |
|             $powerstat = "true";
 | |
|         } else {
 | |
|             $powerstat = "false";
 | |
|         }
 | |
|         xCAT::SvrUtils::sendmsg("Drive Fault: $powerstat", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         if ($data[2] & 0b1000) {                # fan fault
 | |
|             $sessdata->{healthsummary} &= 1;    #set to warn state
 | |
|             $powerstat = "true";
 | |
|         } else {
 | |
|             $powerstat = "false";
 | |
|         }
 | |
|         xCAT::SvrUtils::sendmsg("Cooling Fault: $powerstat", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         if ($data[2] & 0b1000000) {             #can look at light status
 | |
|             my $idstat = ($data[2] & 0b110000) >> 4;
 | |
|             my %idstats = (0 => "off", 1 => "on", 2 => "on", 3 => "unknown");
 | |
|             xCAT::SvrUtils::sendmsg("Identify Light: " . $idstats{$idstat}, $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #$sessdata->{powerstatprefix}="Power Status: ";
 | |
|     if ($sessdata->{sensorstoread} and scalar @{ $sessdata->{sensorstoread} }) {
 | |
|         $sessdata->{currsdr} = shift @{ $sessdata->{sensorstoread} };
 | |
|         readsensor($sessdata);                  #next sensor
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub readchassis {
 | |
|     my $sessdata = shift;
 | |
|     $sessdata->{ipmisession}->subcmd(netfn => 0x0, command => 0x1, data => [], callback => \&gotchassis, callback_args => $sessdata);
 | |
| 
 | |
| }
 | |
| 
 | |
| sub checkleds {
 | |
|     my $sessdata = shift;
 | |
|     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
 | |
|     unless (is_systemx($sessdata)) {
 | |
|         xCAT::SvrUtils::sendmsg("LED status not supported on this system", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         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 {
 | |
|         xCAT::SvrUtils::sendmsg("No supported LEDs found in system", $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
| 
 | |
|     #	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) {
 | |
|             xCAT::SvrUtils::sendmsg(sprintf("BIOS or admininstrator has %s lit", getsensorname($mfg_id, $prod_id, $sdr->led_id, "ibmleds", $sdr)), $callback, $sessdata->{node}, %allerrornodes);
 | |
|             $sessdata->{activeleds} = 1;
 | |
|         }
 | |
|         elsif ($returnd[6] == 3) {
 | |
|             xCAT::SvrUtils::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", $sdr)), $callback, $sessdata->{node}, %allerrornodes);
 | |
|             $sessdata->{activeleds} = 1;
 | |
|         }
 | |
|         elsif ($returnd[6] == 1 && $sdr->led_id != 0) {
 | |
|             xCAT::SvrUtils::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", $sdr), $returnd[4], $returnd[5], getsensorname($mfg_id, $prod_id, ($returnd[4] << 8) + $returnd[5], "ibmleds")), $callback, $sessdata->{node}, %allerrornodes);
 | |
|             $sessdata->{activeleds} = 1;
 | |
|         }
 | |
|         elsif ($sdr->led_id == 0 and $led_id_ms == 0 and $led_id_ls == 0) {
 | |
|             xCAT::SvrUtils::sendmsg(sprintf("LED 0x0000 (%s) active to indicate system error condition.", getsensorname($mfg_id, $prod_id, $sdr->led_id, "ibmleds", $sdr)), $callback, $sessdata->{node}, %allerrornodes);
 | |
|             $sessdata->{activeleds} = 1;
 | |
|             if ($returnd[6] == 1 and $returnd[4] == 0xf and $returnd[5] == 0xff) {
 | |
|                 $sessdata->{doled} = [ $returnd[4], $returnd[5] ];
 | |
|                 $sessdata->{ipmisession}->subcmd(netfn => 0x3a, command => 0xc0, data => $sessdata->{doled}, callback => \&did_led, callback_args => $sessdata);
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|         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 and $osdr->rec_type != 192 and $osdr->rec_type != 17) {
 | |
|                     $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));
 | |
|             if ($led_id_ms == 0xf and $led_id_ls == 0xff) {
 | |
|                 xCAT::SvrUtils::sendmsg(sprintf("LED active to indicate Sensor 0x%02x (%s) error.", $sensor_num, $sensor_desc), $callback, $sessdata->{node}, %allerrornodes);
 | |
|             } else {
 | |
|                 xCAT::SvrUtils::sendmsg(sprintf("LED %02x%02x (%s) active to indicate Sensor 0x%02x (%s) error.", $led_id_ms, $led_id_ls, getsensorname($mfg_id, $prod_id, $sdr->led_id, "ibmleds", $sdr), $sensor_num, $sensor_desc), $callback, $sessdata->{node}, %allerrornodes);
 | |
|             }
 | |
|             $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}) {
 | |
|         xCAT::SvrUtils::sendmsg("No active error LEDs detected", $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
|     if (scalar @{ $sessdata->{sensorstoread} }) {
 | |
|         $sessdata->{currsdr} = shift @{ $sessdata->{sensorstoread} };
 | |
|         readsensor($sessdata);    #next sensor
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub done_powerusage {
 | |
|     my $rsp      = shift;
 | |
|     my $sessdata = shift;
 | |
|     if ($rsp->{error}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Get Power Reading failed" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|     } else {
 | |
|         my $e_id = shift(@{ $rsp->{data} });
 | |
|         if ($e_id ne 0xdc) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, "The Power Reading response is incorrect" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         } else {
 | |
|             my $curr_power = $rsp->{data}->[0] + $rsp->{data}->[1] * 0x100;
 | |
|             my $mini_power = $rsp->{data}->[2] + $rsp->{data}->[3] * 0x100;
 | |
|             my $max_power  = $rsp->{data}->[4] + $rsp->{data}->[5] * 0x100;
 | |
|             my $aver_power = $rsp->{data}->[6] + $rsp->{data}->[7] * 0x100;
 | |
|             my $time_stamp = $rsp->{data}->[8] + $rsp->{data}->[9] * 0x100 +
 | |
|               $rsp->{data}->[10] * 0x10000 + $rsp->{data}->[11] * 0x1000000;
 | |
|             my ($sec, $min, $hour, $day, $mon, $year) = localtime($time_stamp);
 | |
|             $mon  += 1;
 | |
|             $year += 1900;
 | |
|             my $time_period = $rsp->{data}->[12] + $rsp->{data}->[13] * 0x100 +
 | |
|               $rsp->{data}->[14] * 0x10000 + $rsp->{data}->[15] * 0x1000000;
 | |
|             my $reading_state = (($rsp->{data}->[16] & 0x40) >> 6) ? "Active" : "Inactivate";
 | |
|             xCAT::SvrUtils::sendmsg("Current Power                        : $curr_power" . "W", $callback, $sessdata->{node}, %allerrornodes);
 | |
|             xCAT::SvrUtils::sendmsg("Minimum Power over sampling duration : $mini_power" . "W", $callback, $sessdata->{node}, %allerrornodes);
 | |
|             xCAT::SvrUtils::sendmsg("Maximum Power over sampling duration : $max_power" . "W", $callback, $sessdata->{node}, %allerrornodes);
 | |
|             xCAT::SvrUtils::sendmsg("Average Power over sampling duration : $aver_power" . "W", $callback, $sessdata->{node}, %allerrornodes);
 | |
|             xCAT::SvrUtils::sendmsg("Time Stamp                           : $mon/$day/$year - $hour:$min:$sec", $callback, $sessdata->{node}, %allerrornodes);
 | |
|             xCAT::SvrUtils::sendmsg("Statistics reporting time period     : $time_period milliseconds", $callback, $sessdata->{node}, %allerrornodes);
 | |
|             xCAT::SvrUtils::sendmsg("Power Measurement                    : $reading_state", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|     }
 | |
|     do_dcmi_operating($sessdata);
 | |
| }
 | |
| 
 | |
| my %dcmi_sensors = (
 | |
|     0x37 => "Inlet Temperature",
 | |
|     0x03 => "CPU Temperature",
 | |
|     0x07 => "Baseboard temperature",
 | |
| );
 | |
| 
 | |
| sub do_temperature {
 | |
|     my $rsp        = shift;
 | |
|     my $sessdata   = shift;
 | |
|     my $cur_sensor = $sessdata->{dcmi_sensor}->{cur_sensor};
 | |
|     my $sensor_dis = "unknown sensor";
 | |
|     if (defined($cur_sensor) and exists($dcmi_sensors{$cur_sensor})) {
 | |
|         $sensor_dis = $dcmi_sensors{$cur_sensor};
 | |
|     }
 | |
|     if ($rsp->{error}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Get Power Reading failed for $sensor_dis" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|     } elsif (exists($rsp->{data})) {
 | |
|         my $e_id = shift(@{ $rsp->{data} });
 | |
|         if ($e_id ne 0xdc) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, "The Power Reading response is incorrect for $sensor_dis" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         } else {
 | |
|             my $num_of_instances = shift(@{ $rsp->{data} });
 | |
|             my $num_cur          = shift(@{ $rsp->{data} });
 | |
|             if ($num_cur) {
 | |
|                 while (scalar(@{ $rsp->{data} })) {
 | |
|                     my $temp = shift(@{ $rsp->{data} });
 | |
|                     my $iid  = shift(@{ $rsp->{data} });
 | |
|                     my $flag = ($temp & 0x80) >> 7;
 | |
|                     $temp &= 0x7f;
 | |
|                     if ($flag) {
 | |
|                         $sessdata->{dcmi_sensor_instances}->{$cur_sensor}->{$iid} = "-$temp Centigrade";
 | |
|                     } else {
 | |
|                         $sessdata->{dcmi_sensor_instances}->{$cur_sensor}->{$iid} = "+$temp Centigrade";
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     {
 | |
|         my $sensor_eid = shift(@{ $sessdata->{dcmi_sensor}->{sensors} });
 | |
|         if ($sensor_eid) {
 | |
|             $sessdata->{dcmi_sensor}->{cur_sensor} = $sensor_eid;
 | |
| 
 | |
|             # The raw format for dcmi command Get Temperature Reading
 | |
|             #  ipmitool-xcat -I lanplus -H <bmc_ip> -U <bmc_username> -P <bmc_password> raw 0x2c 0x10 0xdc 0x01 <sensor entity_id> 0 0
 | |
|             $sessdata->{ipmisession}->subcmd(netfn => 0x2c, command => 0x10,
 | |
|                 data => [ 0xdc, 0x01, $sensor_eid, 0, 0 ],
 | |
|                 callback      => \&do_temperature,
 | |
|                 callback_args => $sessdata);
 | |
| 
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     done_temperature($sessdata);
 | |
| }
 | |
| 
 | |
| sub done_temperature {
 | |
|     my $sessdata = shift;
 | |
|     foreach my $sid (sort keys %{ $sessdata->{dcmi_sensor_instances} }) {
 | |
|         my $sensor_dis = "unknown sensor";
 | |
|         if (exists($dcmi_sensors{$sid})) {
 | |
|             $sensor_dis = $dcmi_sensors{$sid};
 | |
|         }
 | |
|         foreach my $instance_id (sort keys %{ $sessdata->{dcmi_sensor_instances}->{$sid} }) {
 | |
|             my $temp = $sessdata->{dcmi_sensor_instances}->{$sid}->{$instance_id};
 | |
|             my $string = sprintf("%-36s", "$sensor_dis Instance $instance_id");
 | |
|             xCAT::SvrUtils::sendmsg("$string : $temp", $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|     }
 | |
|     do_dcmi_operating($sessdata);
 | |
| }
 | |
| 
 | |
| sub do_dcmi_operating {
 | |
|     my $sessdata = shift;
 | |
|     unless (scalar(@{ $sessdata->{energy_options} })) {
 | |
|         return;
 | |
|     } else {
 | |
|         my $cur_op = shift(@{ $sessdata->{energy_options} });
 | |
|         if ($cur_op eq "powerusage") {
 | |
| 
 | |
|             # The raw format for dcmi command Get Power Reading:
 | |
|             #  ipmitool-xcat -I lanplus -H <bmc_ip> -U <bmc_username> -P <bmc_password> raw 0x2c 0x02 0xdc 1 0 0
 | |
|             $sessdata->{ipmisession}->subcmd(netfn => 0x2c, command => 0x02,
 | |
|                 data => [ 0xdc, 1, 0, 0 ],
 | |
|                 callback      => \&done_powerusage,
 | |
|                 callback_args => $sessdata);
 | |
|         } elsif ($cur_op eq "temperature") {
 | |
| 
 | |
|             # DCMI Sensor Entity ID
 | |
|             # 0x37/0x40 55 for Inlet Temperature
 | |
|             # 0x03/0x41 for CPU Temperature
 | |
|             # 0x07/0x42 for Baseboard temperature
 | |
|             @{ $sessdata->{dcmi_sensor}->{sensors} } = qw/55 3 7/;
 | |
|             &do_temperature({ startpoint => 1 }, $sessdata);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub renergy {
 | |
|     my $sessdata    = shift;
 | |
|     my @subcommands = @{ $sessdata->{extraargs} };
 | |
|     if (isopenpower($sessdata)) {
 | |
|         unless (@subcommands) {
 | |
|             @subcommands = qw/powerusage temperature/;
 | |
|         }
 | |
|         foreach (@subcommands) {
 | |
|             if ($_ eq "powerusage") {
 | |
|                 push @{ $sessdata->{energy_options} }, 'powerusage';
 | |
|             } elsif ($_ eq "temperature") {
 | |
|                 push @{ $sessdata->{energy_options} }, 'temperature';
 | |
|             } else {
 | |
|                 if ($_ =~ /=/) {
 | |
|                     xCAT::SvrUtils::sendmsg([ 1, "Only Query is supported" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|                 } else {
 | |
|                     xCAT::SvrUtils::sendmsg([ 1, "The option $_ is not supported" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|                 }
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         # DCMI -- Data Center Manageability Interface, for more info, pls reference http://www.intel.com/content/www/us/en/data-center/dcmi/data-center-manageability-interface.html
 | |
|         do_dcmi_operating($sessdata);
 | |
|         return;
 | |
|     } else {
 | |
|         if (grep /powerusage/, @subcommands) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, "The option 'powerusage' is only supported for OpenPOWER servers" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             return;
 | |
|         }
 | |
|         if (grep /temperature/, @subcommands) {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, "The option 'temperatue' is only supported for OpenPOWER servers" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     unless ($iem_support) {
 | |
|         xCAT::SvrUtils::sendmsg(":Command unsupported without IBM::EnergyManager installed", $callback, $sessdata->{node});
 | |
|         return;
 | |
|     }
 | |
|     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}) {
 | |
|         xCAT::SvrUtils::sendmsg($sessdata->{iemtextdata}, $callback, $sessdata->{node}, %allerrornodes);
 | |
|         $sessdata->{iemtextdata} = "";
 | |
|     }
 | |
|     if ($sessdata->{gotcapstatus}) {
 | |
|         $sessdata->{gotcapstatus} = 0;
 | |
|         my $capenabled = $sessdata->{iem}->capping_enabled();
 | |
|         xCAT::SvrUtils::sendmsg("cappingstatus: " . ($capenabled ? "on" : "off"), $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
|     if ($sessdata->{gothistogram}) {
 | |
|         $sessdata->{gothistogram} = 0;
 | |
|         my @histdata = $sessdata->{iem}->extract_relative_histogram;
 | |
|         foreach (sort { $a <=> $b } keys %{ $histdata[0] }) {
 | |
|             xCAT::SvrUtils::sendmsg("$_: " . $histdata[0]->{$_}, $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     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;
 | |
|     $sessdata->{healthsummary} = 0;    #0 means healthy for now
 | |
|     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;
 | |
|         $sensor_filters{chassis} = 1;
 | |
|         $sensor_filters{leds}    = 1;
 | |
|         $doall                   = 1;
 | |
|     }
 | |
|     if (grep /^temp$/, @textfilters) {
 | |
|         $sensor_filters{0x01} = 1;
 | |
|     }
 | |
|     if (grep /^voltage$/, @textfilters) {
 | |
|         $sensor_filters{0x02} = 1;
 | |
|     }
 | |
|     if (grep /^wattage$/, @textfilters) {
 | |
|         $sensor_filters{watt} = 1;
 | |
|     }
 | |
|     if (grep /^fanspeed$/, @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 /^leds$/, @textfilters) {
 | |
|         $sensor_filters{leds} = 1;
 | |
|     }
 | |
|     if (grep /^chassis$/, @textfilters) {
 | |
|         $sensor_filters{chassis} = 1;
 | |
|     }
 | |
|     unless (keys %sensor_filters) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Unrecognized rvitals arguments " . join(" ", @{ $sessdata->{extraargs} }) ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|     }
 | |
| 
 | |
|     $sessdata->{sensorstoread} = [];
 | |
|     my %usedkeys;
 | |
|     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) {
 | |
|             if ($usedkeys{$key}) { next; } #avoid duplicate requests for sensor data
 | |
|             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;
 | |
|                 $usedkeys{$key} = 1;
 | |
|             } elsif ($filter eq "watt" and $sdr->sensor_units_2 and $sdr->sensor_units_2 == 0x06) {
 | |
|                 push @{ $sessdata->{sensorstoread} }, $sdr;
 | |
|                 $usedkeys{$key} = 1;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if ($sensor_filters{leds}) {
 | |
|         push @{ $sessdata->{sensorstoread} }, "leds";
 | |
| 
 | |
|         #my @cleds;
 | |
|         #($rc,@cleds) = checkleds();
 | |
|         #push @output,@cleds;
 | |
|     }
 | |
|     if ($sensor_filters{powerstate} and not $sensor_filters{chassis}) {
 | |
|         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) {
 | |
|             #xCAT::SvrUtils::sendmsg([ 1, ":Energy data requires additional IBM::EnergyManager plugin in conjunction with IMM managed IBM equipment" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         #}
 | |
| 
 | |
|         #my @energies;
 | |
|         #($rc,@energies)=readenergy();
 | |
|         #push @output,@energies;
 | |
|     }
 | |
|     if ($sensor_filters{chassis}) {
 | |
|         unshift @{ $sessdata->{sensorstoread} }, "chassis";
 | |
|     }
 | |
|     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)";
 | |
|     }
 | |
|     if ($sessdata->{bmcnum} != 1) { $text .= " on BMC " . $sessdata->{bmcnum}; }
 | |
|     xCAT::SvrUtils::sendmsg($text, $callback, $sessdata->{node}, %allerrornodes);
 | |
|     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 "chassis") {
 | |
|             readchassis($sessdata);
 | |
|             return;
 | |
|         } elsif ($sessdata->{currsdr} eq "energy") {
 | |
|             readenergy($sessdata);
 | |
|             return;
 | |
|         } else {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, "TODO: make " . $sessdata->{currsdr} . " work again" ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         }
 | |
|         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}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $rsp->{error} ], $callback, $sessdata->{node}, %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     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) {
 | |
|         @exparts = ();
 | |
|         if ($sdr->sensor_type == 0x10) {
 | |
|             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";
 | |
|             }
 | |
|         } 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";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 11) {
 | |
|                 push @exparts, "Uncorrectable Machine Check Exception";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 12) {
 | |
|                 push @exparts, "Correctable Machine Check Error";
 | |
|             }
 | |
|         } 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 ($exdata1 & 1 << 7) {
 | |
|                 push @exparts, "Power Supply Inactive";
 | |
|             }
 | |
|             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 ($exdata1 & 1 << 11) {
 | |
|                 push @exparts, "Bus Degraded";
 | |
|             }
 | |
|         } 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 ($exdata1 & 1 << 9) {
 | |
|                 push @exparts, "Memory Automatically Throttled";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 10) {
 | |
|                 push @exparts, "Critical Overtemperature";
 | |
|             }
 | |
|             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";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0x9) {
 | |
|             @exparts = ();
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "Power off";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "Power Cycle";
 | |
|             }
 | |
|             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";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0x12) {
 | |
|             @exparts = ();
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "System Reconfigured";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "OEM System Boot Event";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 2) {
 | |
|                 push @exparts, "Undetermined system hardware failure";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 3) {
 | |
|                 push @exparts, "Aux log manipulated";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 4) {
 | |
|                 push @exparts, "PEF Action";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 5) {
 | |
|                 push @exparts, "Timestamp Clock Synch";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0x1e) {
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "No bootable media";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "Non-bootable diskette left in drive";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 2) {
 | |
|                 push @exparts, "PXE Server not found";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 3) {
 | |
|                 push @exparts, "Invalid boot sector";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 4) {
 | |
|                 push @exparts, "Timeout waiting for user selection";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0x1f) {
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "boot completed";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "boot completed";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 2) {
 | |
|                 push @exparts, "PXE boot completed";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 3) {
 | |
|                 push @exparts, "Diagnostic boot completed";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 4) {
 | |
|                 push @exparts, "CD-ROM boot completed";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 5) {
 | |
|                 push @exparts, "ROM boot completed";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 6) {
 | |
|                 push @exparts, "boot completed-device not specified";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 7) {
 | |
|                 push @exparts, "Base OS Installation started";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 8) {
 | |
|                 push @exparts, "Base OS Installation completed";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 9) {
 | |
|                 push @exparts, "Base OS Installation aborted";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 9) {
 | |
|                 push @exparts, "Base OS Installation failed";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0x25) {
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "Present";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "Absent";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 2) {
 | |
|                 push @exparts, "Disabled";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0x23) {
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "Expired";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "Hard Reset";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 2) {
 | |
|                 push @exparts, "Power Down";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 3) {
 | |
|                 push @exparts, "Power Cycle";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 8) {
 | |
|                 push @exparts, "Timer interrupt";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0xd) {
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "Present";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "Fault";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 2) {
 | |
|                 push @exparts, "Failure Predicted";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 3) {
 | |
|                 push @exparts, "Hot Spare";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 4) {
 | |
|                 push @exparts, "Consistency Check";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 5) {
 | |
|                 push @exparts, "Critical Array";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 6) {
 | |
|                 push @exparts, "Failed Array";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 7) {
 | |
|                 push @exparts, "Rebuilding";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 8) {
 | |
|                 push @exparts, "Rebuild aborted";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0x20) {
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "Critical stop during OS load";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "Runtime Critical Stop";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 2) {
 | |
|                 push @exparts, "OS Graceful Stop";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 3) {
 | |
|                 push @exparts, "OS Graceful Shutdown";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 4) {
 | |
|                 push @exparts, "Soft Shutdown";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 5) {
 | |
|                 push @exparts, "Agent Not Responding";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0x22) {
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "working";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "sleeping";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 2) {
 | |
|                 push @exparts, "processor context lost";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 3) {
 | |
|                 push @exparts, "memory retained";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 4) {
 | |
|                 push @exparts, "non-volatile sleep";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 5) {
 | |
|                 push @exparts, "soft-off";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 6) {
 | |
|                 push @exparts, "soft-off";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 7) {
 | |
|                 push @exparts, "Mechanical off";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 8) {
 | |
|                 push @exparts, "Sleeping in as S1,S2, or S3 states";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 9) {
 | |
|                 push @exparts, "sleeping";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 10) {
 | |
|                 push @exparts, "entered by override";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 11) {
 | |
|                 push @exparts, "Legacy ON";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 12) {
 | |
|                 push @exparts, "Legacy OFF";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 13) {
 | |
|                 push @exparts, "Unknown";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0x28) {
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "Degraded or unavailable";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "Degraded or unavailable";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 2) {
 | |
|                 push @exparts, "Offline";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 3) {
 | |
|                 push @exparts, "Unavailable";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 4) {
 | |
|                 push @exparts, "Sensor Failure";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 5) {
 | |
|                 push @exparts, "FRU Failure";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0x29) {
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "battery low";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "battery failed";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 2) {
 | |
|                 push @exparts, "battery presence detected";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0x2a) {
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "Session Activated";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "Session Deactivated";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 2) {
 | |
|                 push @exparts, "Invalid username or password";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 3) {
 | |
|                 push @exparts, "Invalid password disable";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0x2b) {
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "Change detected";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "Firmware change detected";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 2) {
 | |
|                 push @exparts, "Hardware incompatibility detected";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 3) {
 | |
|                 push @exparts, "Firmware incompatibility detected";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 4) {
 | |
|                 push @exparts, "Unsupported hardware version";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 5) {
 | |
|                 push @exparts, "Unsupported firmware verion";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 6) {
 | |
|                 push @exparts, "Hardware change successful";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 7) {
 | |
|                 push @exparts, "Firmware change successful";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0x1b) {
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "Cable connected";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "Incorrect cable connection";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type == 0x2c) {
 | |
|             if ($exdata1 & 1) {
 | |
|                 push @exparts, "FRU Not Installed";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 1) {
 | |
|                 push @exparts, "FRU Inactive";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 2) {
 | |
|                 push @exparts, "FRU Activation Requested";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 3) {
 | |
|                 push @exparts, "FRU Activation In Progress";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 4) {
 | |
|                 push @exparts, "FRU Active";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 5) {
 | |
|                 push @exparts, "FRU Deactivation Requested";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 6) {
 | |
|                 push @exparts, "FRU Deactivation In Progress";
 | |
|             }
 | |
|             if ($exdata1 & 1 << 7) {
 | |
|                 push @exparts, "FRU Communication Lost";
 | |
|             }
 | |
|         } elsif ($sdr->sensor_type >= 0xc0) {
 | |
|             $extext = "OEM Reserved " . $sdr->sensor_type;
 | |
|         } else {
 | |
|             $extext = "xCAT needs to add support for " . $sdr->sensor_type;
 | |
|         }
 | |
|         if (@exparts) {
 | |
|             $extext = join(",", @exparts);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     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";
 | |
|     $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"}) {
 | |
|             $sessdata->{sdr_hash} = $sdr_caches{"$mfg_id.$prod_id.$device_id.$dev_rev.$fw_rev1.$fw_rev2"};
 | |
|             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"} = $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} ]; #seems that an extra zero is prepended to allow other code to do 1 based counting out of laziness to match our index to the spec indicated index
 | |
|     $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 (is_systemx($sessdata) && $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]);
 | |
|         }
 | |
|         if (scalar(@sdr_data) > 17) { #well what do you know, we have an ascii description, probably...
 | |
|             my $id = unpack("Z*", pack("C*", @sdr_data[ 16 .. $#sdr_data ]));
 | |
|             if ($id) { $sdr->id_string($id); }
 | |
|         }
 | |
| 
 | |
|         #$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 $sdr    = shift;
 | |
| 
 | |
|     my $mfg;
 | |
|     my $prod;
 | |
|     my $type;
 | |
|     my $desc;
 | |
|     my $name = "";
 | |
| 
 | |
|     if ($file and $file eq "ibmleds") {
 | |
|         if ($sdr and $sdr->id_string ne "LED") { return $sdr->id_string; } # this is preferred mechanism
 | |
|         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("UUID/GUID");
 | |
|     my $binuuid = pack("C*", @{ $rsp->{data} });
 | |
|     my @pieces = unpack("VvvNNN", $binuuid);
 | |
|     my @uuid = unpack("C*", pack("NnnNNN", @pieces));
 | |
|     $fru->value(sprintf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", @uuid));
 | |
|     $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] !~ /[\S]/) {
 | |
|             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 ($_ < 0x20 or $_ > 0x7e) {
 | |
|             $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);
 | |
| 
 | |
|     my $hdr;
 | |
|     $hdr->{xcat_sdrcacheversion} = $cache_version;
 | |
|     nstore_fd($hdr, $fh);
 | |
|     foreach $key (keys %{ $sessdata->{sdr_hash} }) {
 | |
|         my $r = $sessdata->{sdr_hash}->{$key};
 | |
|         nstore_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);
 | |
|     }
 | |
|     $r = retrieve_fd($fh);
 | |
|     unless ($r) { close($fh); return 1; }
 | |
|     unless ($r->{xcat_sdrcacheversion} and $r->{xcat_sdrcacheversion} == $cache_version) { close($fh); return 1; } #version mismatch
 | |
| 
 | |
|     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 randomizelist {    #in place shuffle of list
 | |
|     my $list  = shift;
 | |
|     my $index = @$list;
 | |
|     while ($index--) {
 | |
|         my $swap = int(rand($index + 1));
 | |
|         @$list[ $index, $swap ] = @$list[ $swap, $index ];
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub preprocess_request {
 | |
|     my $request = shift;
 | |
|     if (defined $request->{_xcatpreprocessed}->[0] and $request->{_xcatpreprocessed}->[0] == 1) {
 | |
| 
 | |
|         # exit if preprocessed
 | |
|         return [$request];
 | |
|     }
 | |
|     my $callback = shift;
 | |
|     my @requests;
 | |
| 
 | |
|     my $realnoderange  = $request->{node};           #Should be arrayref
 | |
|     my $command        = $request->{command}->[0];
 | |
|     my $extrargs       = $request->{arg};
 | |
|     my @exargs         = ($request->{arg});
 | |
|     my $delay          = 0;
 | |
|     my $delayincrement = 0;
 | |
|     my $chunksize      = 0;
 | |
|     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]});
 | |
|             #Above statement will miss error code, so replaced by the below statement
 | |
|             $callback->({ errorcode => [1], data => [ "Please enter an action (eg: boot,off,on, etc)", $usage_string ] });
 | |
|             $request = {};
 | |
|             return 0;
 | |
|         }
 | |
| 
 | |
|         #pdu commands will be handled in the pdu plugin
 | |
|         if(($subcmd eq 'pduoff') || ($subcmd eq 'pduon') || ($subcmd eq 'pdustat') || ($subcmd eq 'pdureset')){
 | |
|              return 0;
 | |
|         }
 | |
| 
 | |
|         if (($subcmd ne 'reseat') && ($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') && ($subcmd ne 'wake') && ($subcmd ne 'suspend')) {
 | |
| 
 | |
|             #$callback->({data=>["Unsupported command: $command $subcmd", $usage_string]});
 | |
|             #Above statement will miss error code, so replaced by the below statement
 | |
|             $callback->({ errorcode => [1], data => [ "Unsupported command: $command $subcmd", $usage_string ] });
 | |
|             $request = {};
 | |
|             return;
 | |
|         }
 | |
|         if (($subcmd eq 'on' or $subcmd eq 'reset' or $subcmd eq 'boot') and $::XCATSITEVALS{syspowerinterval}) {
 | |
|             unless ($::XCATSITEVALS{syspowermaxnodes}) {
 | |
|                 $callback->({ errorcode => [1], error => ["IPMI plugin requires syspowermaxnodes be defined if syspowerinterval is defined"] });
 | |
|                 $request = {};
 | |
|                 return 0;
 | |
|             }
 | |
|             $chunksize      = $::XCATSITEVALS{syspowermaxnodes};
 | |
|             $delayincrement = $::XCATSITEVALS{syspowerinterval};
 | |
|         }
 | |
|     } elsif ($command eq "renergy") {
 | |
| 
 | |
|         # filter out the nodes which should be handled by ipmi.pm
 | |
|         my (@bmcnodes, @nohandle);
 | |
|         xCAT::Utils->filter_nodes($request, undef, undef, \@bmcnodes, \@nohandle);
 | |
|         $realnoderange = \@bmcnodes;
 | |
|     } elsif ($command eq "rspconfig") {
 | |
|         # filter out the nodes which should be handled by ipmi.pm
 | |
|         my (@bmcnodes, @nohandle);
 | |
|         xCAT::Utils->filter_nodes($request, undef, undef, \@bmcnodes, \@nohandle);
 | |
|         $realnoderange = \@bmcnodes;
 | |
| 
 | |
|         if ($realnoderange) {
 | |
|             my $optset;
 | |
|             my $option;
 | |
|             foreach (@exargs) {
 | |
|                 if ($_ =~ /^(\w+)=(.*)/) {
 | |
|                     if ($optset eq 0) {
 | |
|                         $callback->({ errorcode => [1], data => [ "Usage Error: Cannot display and change attributes on the same command."] });
 | |
|                         $request = {};
 | |
|                         return;
 | |
|                     }
 | |
|                     $optset = 1;
 | |
|                     $option = $1;
 | |
|                 } else {
 | |
|                     if ($optset eq 1) {
 | |
|                         $callback->({ errorcode => [1], data => [ "Usage Error: Cannot display and change attributes on the same command."] });
 | |
|                         $request = {};
 | |
|                         return;
 | |
|                     }
 | |
|                     $option = $_;
 | |
|                     $optset = 0;
 | |
|                 }
 | |
|                 unless ($option =~ /^USERID$|^ip$|^netmask$|^gateway$|^vlan$|^userid$|^username$|^password$|^snmpdest|^thermprofile$|^alert$|^garp$|^community$|^backupgateway$/) {
 | |
|                     $callback->({ errorcode => [1], data => [ "Unsupported command: $command $_"] });
 | |
|                     $request = {};
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     } elsif ($command eq "rinv") {
 | |
|         if ($exargs[0] eq "-t" and $#exargs == 0) {
 | |
|             unshift @{ $request->{arg} }, 'all';
 | |
|         } elsif ((grep /-t/, @exargs) and !(grep /(all|vpd)/, @exargs)) {
 | |
|             $callback->({ errorcode => [1], error => ["option '-t' can only work with 'all' or 'vpd'"] });
 | |
|             $request = {};
 | |
|             return 0;
 | |
|         }
 | |
|     }
 | |
|     if (!$realnoderange) {
 | |
|         $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 @noderanges;
 | |
|     srand();
 | |
|     if ($chunksize) {
 | |
| 
 | |
|         #first, we try to spread out the chunks so they don't happen to correlate to constrained service nodes or circuits
 | |
|         #for now, will get the sn map for all of them and interleave if dispatching
 | |
|         #if not dispatching, will randomize the noderange instead to lower likelihood of turning everything on a circuit at once
 | |
|         if (defined $::XCATSITEVALS{ipmidispatch} and $::XCATSITEVALS{ipmidispatch} =~ /0|n/i) { #no SN indicated, instead do randomize
 | |
|             randomizelist($realnoderange);
 | |
|         } else {    # sn is indicated
 | |
|             my $bigsnmap = xCAT::ServiceNodeUtils->get_ServiceNode($realnoderange, "xcat", "MN");
 | |
|             foreach my $servicenode (keys %$bigsnmap) { #let's also shuffle within each service node responsibliity
 | |
|                 randomizelist($bigsnmap->{$servicenode})
 | |
|             }
 | |
| 
 | |
|             #now merge the per-servicenode list into a big list again
 | |
|             $realnoderange = [];
 | |
|             while (keys %$bigsnmap) {
 | |
|                 foreach my $servicenode (keys %$bigsnmap) {
 | |
|                     if (@{ $bigsnmap->{$servicenode} }) {
 | |
|                         push(@$realnoderange, pop(@{ $bigsnmap->{$servicenode} }));
 | |
|                     } else {
 | |
|                         delete $bigsnmap->{$servicenode};
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         while (scalar(@$realnoderange)) {
 | |
|             my @tmpnoderange;
 | |
|             while (scalar(@$realnoderange) and $chunksize) {
 | |
|                 push @tmpnoderange, (shift @$realnoderange);
 | |
|                 $chunksize--;
 | |
|             }
 | |
|             push @noderanges, \@tmpnoderange;
 | |
|             $chunksize = $::XCATSITEVALS{syspowermaxnodes};
 | |
|         }
 | |
|     } else {
 | |
|         @noderanges = ($realnoderange);
 | |
|     }
 | |
|     foreach my $noderange (@noderanges) {
 | |
|         my $sn;
 | |
|         if (defined $::XCATSITEVALS{ipmidispatch} and $::XCATSITEVALS{ipmidispatch} =~ /0|n/i) {
 | |
|             $sn = { '!xcatlocal!' => $noderange };
 | |
|         } else {
 | |
|             $sn = xCAT::ServiceNodeUtils->get_ServiceNode($noderange, "xcat", "MN");
 | |
|         }
 | |
| 
 | |
|         # build each request for each service node
 | |
| 
 | |
|         foreach my $snkey (keys %$sn) {
 | |
| 
 | |
|             #print "snkey=$snkey\n";
 | |
|             my $reqcopy = {%$request};
 | |
|             $reqcopy->{node} = $sn->{$snkey};
 | |
|             unless ($snkey eq '!xcatlocal!') {
 | |
|                 $reqcopy->{'_xcatdest'} = $snkey;
 | |
|             }
 | |
|             $reqcopy->{_xcatpreprocessed}->[0] = 1;
 | |
|             if ($delay) {
 | |
|                 $reqcopy->{'_xcatdelay'} = $delay;
 | |
|             }
 | |
|             push @requests, $reqcopy;
 | |
|         }
 | |
|         $delay += $delayincrement;
 | |
|     }
 | |
|     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];
 | |
|     my $ipmisess = xCAT::IPMI->new(bmc => $argr->[1], userid => $argr->[2], password => $argr->[3]);
 | |
|     if ($ipmisess->{error}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $ipmisess->{error} ], $cb, $argr->[0], %allerrornodes);
 | |
|         return;
 | |
|     }
 | |
|     $ipmisess->{ipmicons} = $ipmicons;
 | |
|     $ipmisess->{cb}       = $cb;
 | |
|     $ipmisess->subcmd(netfn => 0x6, command => 0x38, data => [ 0x0e, 0x04 ], callback => \&got_channel_auth_cap_foripmicons, callback_args => $ipmisess);
 | |
| }
 | |
| 
 | |
| sub got_channel_auth_cap_foripmicons {
 | |
|     my $rsp   = shift;
 | |
|     my $ipmis = shift;
 | |
|     if ($rsp->{error}) {
 | |
|         return;
 | |
|     }
 | |
|     if ($rsp->{code} != 0) { return; }
 | |
|     my $cb = $ipmis->{cb};
 | |
|     $cb->($ipmis->{ipmicons});    #ipmicons);
 | |
| }
 | |
| 
 | |
| 
 | |
| # scan subroutine is used to scan the hardware devices which installed on the host node
 | |
| # In current implementation, only the mic cards will be scanned.
 | |
| # scan
 | |
| # scan -u/-w/-z
 | |
| my @rscan_header = (
 | |
|     [ "type", "%-8s" ],
 | |
|     [ "name", "" ],
 | |
|     [ "id",   "%-8s" ],
 | |
|     [ "host", "" ]);
 | |
| 
 | |
| sub scan {
 | |
|     my $request = shift;
 | |
|     my $subreq  = shift;
 | |
|     my $nodes   = shift;
 | |
|     my $args    = shift;
 | |
| 
 | |
|     my $usage_string = "rscan [-u][-w][-z]";
 | |
| 
 | |
|     my ($update, $write, $stanza);
 | |
|     foreach (@$args) {
 | |
|         if (/-w/) {
 | |
|             $write = 1;
 | |
|         } elsif (/-u/) {
 | |
|             $update = 1;
 | |
|         } elsif (/-z/) {
 | |
|             $stanza = 1;
 | |
|         } else {
 | |
|             $callback->({ error => [$usage_string] });
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     my $output = xCAT::Utils->runxcmd({ command => ['xdsh'],
 | |
|             node => $nodes,
 | |
|             arg => [ 'micinfo', '-listDevices' ] }, $subreq, 0, 1);
 | |
| 
 | |
|     # parse the output from 'xdsh micinfo -listDevices'
 | |
|     my %host2mic;
 | |
|     my $maxhostname = 0;
 | |
|     foreach (@$output) {
 | |
|         foreach (split /\n/, $_) {
 | |
|             if (/([^:]*):\s+(\d+)\s*\|/) {
 | |
|                 my $host     = $1;
 | |
|                 my $deviceid = $2;
 | |
|                 push @{ $host2mic{$host} }, $deviceid;
 | |
|                 if (length($host) > $maxhostname) {
 | |
|                     $maxhostname = length($host);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # generate the display message
 | |
|     my @displaymsg;
 | |
|     my $format = sprintf "%%-%ds", ($maxhostname + 10);
 | |
|     $rscan_header[1][1] = $format;
 | |
|     $format = sprintf "%%-%ds", ($maxhostname + 2);
 | |
|     $rscan_header[3][1] = $format;
 | |
|     if ($stanza) {
 | |
| 
 | |
|         # generate the stanza for each mic
 | |
|         foreach (keys %host2mic) {
 | |
|             my $host = $_;
 | |
|             foreach (@{ $host2mic{$host} }) {
 | |
|                 my $micid = $_;
 | |
|                 push @displaymsg, "$host-mic$micid:";
 | |
|                 push @displaymsg, "\tobjtype=node";
 | |
|                 push @displaymsg, "\tmichost=$host";
 | |
|                 push @displaymsg, "\tmicid=$micid";
 | |
|                 push @displaymsg, "\thwtype=mic";
 | |
|                 push @displaymsg, "\tmgt=mic";
 | |
|             }
 | |
|         }
 | |
|     } else {
 | |
| 
 | |
|         # generate the headers for scan message
 | |
|         my $header;
 | |
|         foreach (@rscan_header) {
 | |
|             $header .= sprintf @$_[1], @$_[0];
 | |
|         }
 | |
|         push @displaymsg, $header;
 | |
| 
 | |
|         # generate every entries
 | |
|         foreach (keys %host2mic) {
 | |
|             my $host = $_;
 | |
|             foreach (@{ $host2mic{$host} }) {
 | |
|                 my $micid = $_;
 | |
|                 my @data  = ("mic", "$host-mic$micid", "$micid", "$host");
 | |
|                 my $i     = 0;
 | |
|                 my $entry;
 | |
|                 foreach (@rscan_header) {
 | |
|                     $entry .= sprintf @$_[1], $data[ $i++ ];
 | |
|                 }
 | |
|                 push @displaymsg, $entry;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     $callback->({ data => \@displaymsg });
 | |
| 
 | |
|     unless ($update || $write) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # for -u / -w, write or update the mic node in the xCAT DB
 | |
|     my $nltab  = xCAT::Table->new('nodelist');
 | |
|     my $mictab = xCAT::Table->new('mic');
 | |
|     my $nhmtab = xCAT::Table->new('nodehm');
 | |
|     if (!$nltab || !$mictab || !$nhmtab) {
 | |
|         $callback->({ error => ["Open database table failed."], errorcode => 1 });
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     # update the node to the database
 | |
|     foreach (keys %host2mic) {
 | |
|         my $host = $_;
 | |
|         foreach (@{ $host2mic{$host} }) {
 | |
|             my $micid   = $_;
 | |
|             my $micname = "$host-mic$micid";
 | |
| 
 | |
|             # update the nodelist table
 | |
|             $nltab->setAttribs({ node => $micname }, { groups => "all,mic" });
 | |
| 
 | |
|             # update the mic table
 | |
|             $mictab->setAttribs({ node => $micname }, { host => $host, id => $micid, nodetype => 'mic' });
 | |
| 
 | |
|             # update the nodehm table
 | |
|             $nhmtab->setAttribs({ node => $micname }, { mgt => 'mic', cons => 'mic' });
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #----------------------------------------------------------------#
 | |
| # HPM file format:
 | |
| #     Upgrade image header:
 | |
| #         0:8  --> Upgrade Image signature
 | |
| #         8:1  --> Format Version
 | |
| #         9:1  --> Device ID
 | |
| #        10:3  --> Manufacture ID
 | |
| #        13:2  --> Product ID
 | |
| #        15:4  --> Img created Time
 | |
| #        19:1  --> Img capabilites
 | |
| #        20:1  --> Components.
 | |
| #        21:1  --> Self-test timeout.
 | |
| #        22:1  --> Rollback timeout
 | |
| #        23:1  --> Inaccessibility timeout
 | |
| #        24:2  --> Earliest Compatible Revision.            0-6 Major, 8-15 Minor
 | |
| #        26:6  --> Firmware revision
 | |
| #        32:2  --> OEM data descriptor length.
 | |
| #        34:n  --> OEM data descriptor
 | |
| #                        0:1  --> Descriptor Type ID.
 | |
| #                        1:1  --> End of List or Version
 | |
| #                        2:2  --> Length (8+m)
 | |
| #                        4:3  --> Manufacutre ID
 | |
| #                        7:1  --> Descriptor checksum
 | |
| #                        8:m  --> Data.
 | |
| #      34+n:1  --> Header checksum
 | |
| #     Upgrade action 1
 | |
| #         0:1  --> Upgrade action type. 0: Backup component, 0x01: Prepare components, 0x02: Upload firmware image.
 | |
| #         1:1  --> Components. The selected component
 | |
| #         2:1  --> header checksum
 | |
| #              The following bytes 3:(34+m) are only present if action type is 0x02.
 | |
| #         3:6  --> Firmware version.
 | |
| #         9:21 --> Firmware description string.
 | |
| #        30:4  --> Firmware length.
 | |
| #        34:m  --> Firmware Image data.
 | |
| #     Upgrade action N
 | |
| #     Image checksum 0:16
 | |
| #
 | |
| #----------------------------------------------------------------#
 | |
| 
 | |
| sub hpm_data_parse {
 | |
| 
 | |
|     sub _read_hpm_file {
 | |
|         my ($hpm_filefd, $size, $pos_ptr, $hpm_context_string_ptr) = @_;
 | |
|         my $tmp = undef;
 | |
|         my $rt  = 0;
 | |
|         while ($rt = read($hpm_filefd, $tmp, $size)) {
 | |
|             $size -= $rt;
 | |
|             $$pos_ptr += $rt;
 | |
|             $$hpm_context_string_ptr .= $tmp;
 | |
|             if ($size == 0) {
 | |
|                 last;
 | |
|             }
 | |
|         }
 | |
|         if ($size != 0) {
 | |
|             $callback->({ error => "Parse hpm file error.",
 | |
|                     errorcode => 1 });
 | |
|             return -1;
 | |
|         }
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     my $hpm_file_name      = shift;
 | |
|     my $hpm_context_string = "";
 | |
|     my $hpm_filefd;
 | |
| 
 | |
|     #relative path
 | |
|     if ($hpm_file_name !~ /^\//) {
 | |
|         $hpm_file_name = xCAT::Utils->full_path($hpm_file_name, $::cwd);
 | |
|     }
 | |
|     unless (-f $hpm_file_name) {
 | |
|         $callback->({ error => "File $hpm_file_name can not be found.",
 | |
|                 errorcode => 1 });
 | |
|         return -1;
 | |
|     }
 | |
|     unless (open($hpm_filefd, "<", $hpm_file_name)) {
 | |
|         $callback->({ error => "Open file $hpm_file_name failed.",
 | |
|                 errorcode => 1 });
 | |
|         return -1;
 | |
|     }
 | |
|     binmode($hpm_filefd);
 | |
| 
 | |
|     my $size = 34;
 | |
|     my $pos  = 0;
 | |
|     my $ret  = 0;
 | |
| 
 | |
|     seek($hpm_filefd, 0, 2);
 | |
|     my $filesize = tell($hpm_filefd);
 | |
|     seek($hpm_filefd, 0, 0);
 | |
|     $ret = _read_hpm_file($hpm_filefd, $size, \$pos, \$hpm_context_string);
 | |
|     return $ret if $ret < 0;
 | |
| 
 | |
|     # Parse hpm image header
 | |
|     my @hpm_context = unpack("C34", $hpm_context_string);
 | |
|     $hpm_data_hash{deviceID} = $hpm_context[9];
 | |
|     $hpm_data_hash{manufactureID} = $hpm_context[10] + $hpm_context[11] * 0x100 +
 | |
|       $hpm_context[12] * 0x10000;
 | |
|     $hpm_data_hash{productID}   = $hpm_context[13] + $hpm_context[14] * 0x100;
 | |
|     $hpm_data_hash{componentes} = $hpm_context[20];
 | |
|     $hpm_data_hash{compatible_rev_min} = $hpm_context[24];
 | |
|     $hpm_data_hash{compatible_rev_maj} = $hpm_context[25];
 | |
|     $hpm_data_hash{oem_des_len} = $hpm_context[32] + $hpm_context[33] * 0x100;
 | |
| 
 | |
|     seek($hpm_filefd, 34 + $hpm_data_hash{oem_des_len} + 1, 0);
 | |
| 
 | |
|     # parse hpm action structure
 | |
|     $pos = 34 + $hpm_data_hash{oem_des_len} + 1;
 | |
| 
 | |
|     while ($pos < $filesize - 16) {
 | |
|         $size               = 3;
 | |
|         $hpm_context_string = "";
 | |
|         $ret = _read_hpm_file($hpm_filefd, $size, \$pos, \$hpm_context_string);
 | |
|         return $ret if $ret < 0;
 | |
|         @hpm_context = unpack("C3", $hpm_context_string);
 | |
|         my $action_type  = $hpm_context[0];
 | |
|         my $component_id = $hpm_context[1];
 | |
|         $hpm_data_hash{$component_id}{component_id} = $component_id;
 | |
|         $hpm_data_hash{$component_id}{action_type}  = $action_type;
 | |
| 
 | |
|         if ($action_type eq '2') {
 | |
|             $hpm_context_string = "";
 | |
|             $size               = 31;
 | |
|             $ret = _read_hpm_file($hpm_filefd, $size, \$pos, \$hpm_context_string);
 | |
|             return $ret if $ret < 0;
 | |
| 
 | |
|             @hpm_context = unpack("C31", $hpm_context_string);
 | |
|             @{ $hpm_data_hash{$component_id}{action_version} } = splice(@hpm_context, 0, 6);
 | |
|             @{ $hpm_data_hash{$component_id}{action_des_str} } = splice(@hpm_context, 0, 21);
 | |
|             $hpm_data_hash{$component_id}{action_length} = $hpm_context[0] + $hpm_context[1] * 0x100 +
 | |
|               $hpm_context[2] * 0x10000 +
 | |
|               $hpm_context[3] * 0x1000000;
 | |
|             seek($hpm_filefd, $hpm_data_hash{$component_id}{action_length}, 1);
 | |
|             $pos += $hpm_data_hash{$component_id}{action_length};
 | |
|         }
 | |
|     }
 | |
|     close($hpm_filefd);
 | |
| 
 | |
|     # We suppose component 2 and component 4 must exists in the HPM file
 | |
|     if (!exists($hpm_data_hash{2}) || !exists($hpm_data_hash{4})) {
 | |
|         $callback->({ error => "Parse hpm file error, component 2 and component 4 do not exist",
 | |
|                 errorcode => 1 });
 | |
|         return -1;
 | |
|     }
 | |
|     if (!exists($hpm_data_hash{deviceID}) || !exists($hpm_data_hash{manufactureID})
 | |
|         || !exists($hpm_data_hash{productID})) {
 | |
|         $callback->({ error => "Parse hpm file error",
 | |
|                 errorcode => 1 });
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     # The last 16 bytes are image checksum
 | |
|     @hpm_context = ();
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| sub hpm_action_version {
 | |
|     if (!exists($hpm_data_hash{2}) || !exists($hpm_data_hash{4})) {
 | |
|         return -1;
 | |
|     }
 | |
|     my $version = $hpm_data_hash{1}{action_version};
 | |
|     my $ver = sprintf("%3d.%02x.%d", $version->[0], $version->[1],
 | |
|         $version->[5]*0x1000000+$version->[4]*0x10000+$version->[3]*0x100+$version->[2]);
 | |
|     $callback->({ data => "HPM firmware version for BOOT component:$ver" });
 | |
|     $version = $hpm_data_hash{2}{action_version};
 | |
|     $ver = sprintf("%3d.%02x.%d", $version->[0], $version->[1],
 | |
|         $version->[5]*0x1000000+$version->[4]*0x10000+$version->[3]*0x100+$version->[2]);
 | |
|     $callback->({ data => "HPM firmware version for APP  component:$ver" });
 | |
|     $version = $hpm_data_hash{4}{action_version};
 | |
|     $ver = sprintf("%3d.%02x.%d", $version->[0], $version->[1],
 | |
|         $version->[5]*0x1000000+$version->[4]*0x10000+$version->[3]*0x100+$version->[2]);
 | |
|     $callback->({ data => "HPM firmware version for BIOS component:$ver" });
 | |
| }
 | |
| 
 | |
| 
 | |
| sub process_request {
 | |
|     my $request = shift;
 | |
|     $callback = shift;
 | |
|     my $subreq    = shift;
 | |
|     my $noderange = $request->{node};           #Should be arrayref
 | |
|     my $command   = $request->{command}->[0];
 | |
|     my $extrargs  = $request->{arg};
 | |
|     my @exargs    = ($request->{arg});
 | |
|     $::cwd = $request->{cwd}->[0];
 | |
| 
 | |
|     if (ref($extrargs)) {
 | |
|         @exargs = @$extrargs;
 | |
|     }
 | |
|     my $ipmiuser    = 'USERID';
 | |
|     my $ipmipass    = 'PASSW0RD';
 | |
|     my $ipmitrys    = 3;
 | |
|     my $ipmitimeout = 2;
 | |
|     my $ipmitab     = xCAT::Table->new('ipmi');
 | |
|     my $tmp;
 | |
|     if ($::XCATSITEVALS{ipmitimeout}) { $ipmitimeout = $::XCATSITEVALS{ipmitimeout} }
 | |
|     if ($::XCATSITEVALS{ipmiretries}) { $ipmitrys = $::XCATSITEVALS{ipmitretries} }
 | |
|     if ($::XCATSITEVALS{ipmisdrcache}) { $enable_cache = $::XCATSITEVALS{ipmisdrcache} }
 | |
|     if ($::XCATSITEVALS{xcatdebugmode}) { $xcatdebugmode = $::XCATSITEVALS{xcatdebugmode} }
 | |
| 
 | |
|     #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' ]);
 | |
|     my $authdata = xCAT::PasswordUtils::getIPMIAuth(noderange => $noderange, ipmihash => $ipmihash);
 | |
|     foreach (@$noderange) {
 | |
|         my $node     = $_;
 | |
|         my $nodeuser = $authdata->{$node}->{username};
 | |
|         my $nodepass = $authdata->{$node}->{password};
 | |
|         my $nodeip   = $node;
 | |
|         my $ent;
 | |
|         if (defined($ipmitab)) {
 | |
|             $ent = $ipmihash->{$node}->[0];
 | |
|             if (ref($ent) and defined $ent->{bmc}) { $nodeip = $ent->{bmc}; }
 | |
|         }
 | |
|         if ($nodeip =~ /,/ and grep ({ $_ eq $request->{command}->[0] } qw/rinv reventlog rvitals rspconfig/)) { #multi-node x3950 X5, for example
 | |
|             my $bmcnum = 1;
 | |
|             foreach (split /,/, $nodeip) {
 | |
|                 push @donargs, [ $node, $_, $nodeuser, $nodepass, $bmcnum ];
 | |
|                 $bmcnum += 1;
 | |
|             }
 | |
|         } else {
 | |
|             $nodeip =~ s/,.*//;    #stri
 | |
|             push @donargs, [ $node, $nodeip, $nodeuser, $nodepass, 1 ];
 | |
|         }
 | |
|     }
 | |
|     if ($request->{command}->[0] eq "getipmicons") {
 | |
|         foreach (@donargs) {
 | |
|             getipmicons($_, $callback);
 | |
|         }
 | |
|         while (xCAT::IPMI->waitforrsp()) { yield }
 | |
|         return;
 | |
|     }
 | |
|     if ($request->{command}->[0] eq "rspconfig") {
 | |
|         my $updatepasswd = 0;
 | |
|         my $index        = 0;
 | |
|         foreach (@{ $request->{arg} }) {
 | |
|             if ($_ =~ /^USERID=\*$/) {
 | |
|                 $updatepasswd = 1;
 | |
|                 last;
 | |
|             }
 | |
|             $index++;
 | |
|         }
 | |
|         if ($updatepasswd) {
 | |
|             splice(@{ $request->{arg} }, $index, 1);
 | |
|             @exargs = @{ $request->{arg} };
 | |
|             foreach (@donargs) {
 | |
|                 my $cliuser = $authdata->{ $_->[0] }->{cliusername};
 | |
|                 my $clipass = $authdata->{ $_->[0] }->{clipassword};
 | |
|                 xCAT::IMMUtils::setupIMM($_->[0], curraddr => $_->[1], skipbmcidcheck => 1, skipnetconfig => 1, cliusername => $cliuser, clipassword => $clipass, callback => $callback);
 | |
|             }
 | |
|             if ($#exargs == -1) {
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # handle the rscan to scan the mic on the target host
 | |
|     if ($request->{command}->[0] eq "rscan") {
 | |
|         scan($request, $subreq, $noderange, $extrargs);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if ($request->{command}->[0] eq "rflash") {
 | |
|         my %args_hash;
 | |
|         if (!defined($extrargs)) {
 | |
|             $callback->({ error => "No option or hpm file is provided.",
 | |
|                     errorcode => 1 });
 | |
|             return;
 | |
|         }
 | |
|         foreach my $opt (@$extrargs) {
 | |
|             if ($opt =~ /^(-c|--check)$/i) {
 | |
|                 if (exists($args_hash{check})) {
 | |
|                     $callback->({ error => "Error command: Multiple opption $opt is given.",
 | |
|                             errorcode => 1 });
 | |
|                     return;
 | |
|                 }
 | |
|                 $args_hash{check} = 1;
 | |
|             } elsif ($opt =~ /.*\.hpm$/i) {
 | |
|                 if (exists($args_hash{hpm})) {
 | |
|                     $callback->({ error => "Error command: Multiple hpm file is given.",
 | |
|                             errorcode => 1 });
 | |
|                     return;
 | |
|                 }
 | |
|                 $args_hash{hpm} = $opt;
 | |
|             }
 | |
|         }
 | |
|         if (exists($args_hash{hpm})) {
 | |
|             if (hpm_data_parse($args_hash{hpm}) < 0) {
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (exists($args_hash{check})) {
 | |
|             hpm_action_version();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #get new node status
 | |
|     my %oldnodestatus = ();    #saves the old node status
 | |
|     my $check         = 0;
 | |
|     my $global_check  = 1;
 | |
|     if (defined $::XCATSITEVALS{nodestatus} and $::XCATSITEVALS{nodestatus} =~ /0|n|N/) { $global_check = 0; }
 | |
| 
 | |
| 
 | |
|     if ($command eq 'rpower') {
 | |
|         %newnodestatus = ();
 | |
|     }
 | |
| 
 | |
|     # NOTE (chenglch) rflash for one node need about 5-10 minutes. There is no need to rflash node
 | |
|     # one by one, fork a process for each node.
 | |
|     if ($command eq 'rflash') {
 | |
|         start_rflash_processes(\@donargs, $ipmitimeout, $ipmitrys, $command, -args => \@exargs);
 | |
|     }
 | |
|     else {
 | |
|         foreach (@donargs) {
 | |
|             donode($_->[0], $_->[1], $_->[2], $_->[3], $_->[4], $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 }
 | |
| 
 | |
|     if ($command eq 'rpower' and %newnodestatus) {
 | |
|         xCAT_monitoring::monitorctrl::setNodeStatusAttributes(\%newnodestatus, 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 $bmcnum    = 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
 | |
|         bmcnum => $bmcnum,
 | |
|         #ipmisession => xCAT::IPMI->new(bmc => $bmcip, userid => $user, password => $pass),
 | |
|         command    => $command,
 | |
|         extraargs  => \@exargs,
 | |
|         subcommand => $exargs[0],
 | |
|         xcatdebugmode => $xcatdebugmode,
 | |
|         outfunc => $callback,
 | |
|     };
 | |
|     if ($command eq "rpower" and $exargs[0] eq "reseat") {
 | |
|         on_bmc_connect(0, $sessiondata{$node});
 | |
|         return 0;
 | |
|     }
 | |
|     $sessiondata{$node}->{ipmisession} = xCAT::IPMI->new(bmc => $bmcip, userid => $user, password => $pass);
 | |
|     if ($sessiondata{$node}->{ipmisession}->{error}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, $sessiondata{$node}->{ipmisession}->{error} ], $callback, $node, %allerrornodes);
 | |
|     } else {
 | |
|         my ($rc, @output) = ipmicmd($sessiondata{$node});
 | |
|         sendoutput($rc, @output);
 | |
|         yield;
 | |
|         return $rc;
 | |
|     }
 | |
| 
 | |
|     #my $msgtoparent=freeze(\@outhashes);
 | |
|     # print $outfd $msgtoparent;
 | |
| }
 | |
| 
 | |
| 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);
 | |
|     }
 | |
| }
 | |
| 
 | |
| ##########################################################################
 | |
| # generate hardware tree, called from lstree.
 | |
| ##########################################################################
 | |
| sub genhwtree
 | |
| {
 | |
|     my $nodelist = shift;    # array ref
 | |
|     my $callback = shift;
 | |
|     my %hwtree;
 | |
| 
 | |
|     my $bmchash;
 | |
| 
 | |
|     # read ipmi.bmc
 | |
|     my $ipmitab = xCAT::Table->new('ipmi');
 | |
|     if ($ipmitab)
 | |
|     {
 | |
|         $bmchash = $ipmitab->getNodesAttribs($nodelist, ['bmc']);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         my $rsp = {};
 | |
|         $rsp->{data}->[0] = "Can not open ipmi table.\n";
 | |
|         xCAT::MsgUtils->message("E", $rsp, $callback, 1);
 | |
|     }
 | |
| 
 | |
|     foreach my $node (@$nodelist)
 | |
|     {
 | |
|         if ($bmchash->{$node}->[0]->{'bmc'})
 | |
|         {
 | |
|             push @{ $hwtree{ $bmchash->{$node}->[0]->{'bmc'} } }, $node;
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     return \%hwtree;
 | |
| 
 | |
| }
 | |
| 
 | |
| 1;
 |