package Agent::DSP;
#<copyright>
# ----------------------------------------------------------
# Sun Proprietary/Confidential Code
# Copyright 2001, Sun Microsystems, Inc. All rights reserved.
# ----------------------------------------------------------
#</copyright>

#  $Name: bld21_017 $ 
#  $Id: DSP.pm,v 1.20 2005/05/11 17:02:02 edunning Exp $
use strict;
use Agent;
use base 'Agent';
use Report;

sub isSelectable { "Sun DSP" }

sub revision {'$Revision: 1.20 $'}

sub category { Report::CAT_DSP }
use vars qw ($ERR);

my $DSP_TO =  60;

sub module_list { ('SRC','SFC','SIO', 'MIC') }

sub new {
  my($self) = Agent->new();

  bless ($self, 'Agent::DSP');
  return $self;
}

# translate port to the right  CIM key 

sub getPortKey {
  my($class, $wwn, $port) = @_;
  return "$wwn.$port";
}

sub RUN {
  my($agent, $static_list, $pass) = @_;
  my($HBA, $logfile, $r, @results, $timelapse);
  my($xml, @lux_dif, $dif, $ev, $found);
  my($log_err, $lines);
  my(%DONE, $report, $device, $key, $id, $x, $l, $connect_errs);
  $DB::single=1;
  my $processed_list = [];
  
  my $se_conflict = System->get_se_conflict();
  my $renv = System->get_renv();
  my $type = $agent->category();
  my $today = Util->today("YMDH");
  my($dc)  = 0;

  foreach $device ( $agent->deviceList($static_list)) {
    next if ($device->{active} eq "N");
    if ($se_conflict && index(",$se_conflict,", ",$device->{name},") >= 0 ) {
       Debug->print2(
           "-> $type: skipping $device->{name}, detected device lockfile");
       next;
    }

    if (!$timelapse) {
       $timelapse = 1;
       Timelapse->start(ref($agent));
    }

    $dc++;
    Debug->print1("-> Reading " . uc($device->{type}) . ": $device->{name}/$device->{ip}");

    ($key, $connect_errs, $report) = $agent->INSTRUMENTATION($device);

    next if ($agent->lost_comm($connect_errs, $device, $type, 'Probing failed'));

    $key= lc($key);
    $id = {  
              deviceName  => $device->{key},
              active      => $device->{active},
              logFile     => "",
              display     => $device->{name},
              name        => $device->{name},
              class       => $device->{class},
              category    => $type,
              ip          => "",
           };
     $report = {} if (!$report);
     $agent->copyDev($device, $report);

     $HBA = $device->{hba} || System->get_hba;
     $report->{"id.name"} = $device->{name};
     $report->{"id.ipno"} = $device->{ipno};
     $report->{"id.wwn"}  = $device->{key};

     my $new_rep;
     if ($connect_errs || !$key) {
        $report->{'id.connect_errs'} = $connect_errs;
        $new_rep = Report->new($id, $report , undef, Report::STATUS_CANNOT_CONNECT);
     } else {
        Discover->updateTopoDevice($device, {report => $report});
        $new_rep = Report->new($id, $report , undef);
     }
     my $problems = PDM->get_last_event();
     require Health::DSP;
     Health::DSP->all_logic($new_rep);
     PDM->saveReport($new_rep);

     my ($broke, $abort) = $agent->new_events($problems, $device, 1);
     push(@$processed_list, $device);
     last if ($broke && $pass == 1 || $abort);
  } 
  Debug->print2("  No devices to do.") if ($dc == 0 && $pass == 1);
  Timelapse->stop(ref($agent));
  return $processed_list;
}

############################################################################



sub getWWN {
  my($agent, $name, $host, $TO, $frus) = @_;
  my($wwn);
  $ERR = undef;
  $TO  = $DSP_TO + 5;

  if (!$host) {
     return &get_WWN({name => $name, FRUS => $frus});
  } else {
     my $rc = Util::Http->getCommand($host, "Agent::DSP::WWN&name=$name&HTTP=1&FRUS=$frus" , $TO);
     if ($rc !~ /OK /) {
        $ERR = "DSP: getWWN: cannot identify $name on $host";
        return undef;
     } else {
        my $VAR1;
        my $ix = index($rc, "OK ");
        eval substr($rc,$ix+3);
        return $VAR1;
     }
  }
}



sub get_WWN {
  my($q) = @_;
  my $report = {};
  my $rc     = {};

  if (Util->ping($q->{name}, 10)) {
    my $lerr = Agent::DSP->getConfig({ipno => $q->{name} }, $report, 1);
    $rc->{error} = $lerr ;
    my $sn =  ($report->{'info.unique_id'});
    $rc->{key}      = $report->{"fibrePort.m1p1.NodeWwn"} || 
                      $report->{"fibrePort.m2p1.NodeWwn"} || 
                      $report->{"fibrePort.m3p1.NodeWwn"} ||
                      $report->{"fibrePort.m4p1.NodeWwn"};
    $rc->{key}      =~ s/\://g;
    $rc->{key}      = lc($rc->{key});
  
    my($m, $p, $wwns, $i );
    my $port=0;
    for ($m=1; $m <= 10; $m++) {
       next if (!exists $report->{"fibrePort.m${m}p1.Name"});
       for ($p=1; $p <= 8; $p++) {
          my $el = "m${m}p$p";
          my $wwn = lc($report->{"fibrePort.$el.PortWwn"});
          $wwn =~ s/\://g;
          $wwns .= "$wwn|";
       }
    }
    my (%RP_WWN);
    for ($m=1; $m <= 1000; $m++) {
       next if (!$report->{"fcserver.$m.ElementName"});
       for ($i=1; $i < 100; $i++) {
          my $el = "fcserver.$m.instance.$i";
          next if (!$report->{"$el.InstanceName"});
          my @L = split(/\//, $report->{"$el.InstanceName"});
          my $port = "m" . $L[1] . "p" . $L[2];
          if ($report->{"$el.InstanceStatus"} eq "Online") {
            $RP_WWN{$port} = $report->{"fcserver.$m.Wwn"};
          }
       }
    }
    my $pcnt = 0;
    for ($m=1; $m <= 10; $m++) {
      for ($p=1; $p <= 64; $p++) {
          my $mp = "m${m}p$p";
          my $el = "fibrePort.$mp";
          if ($report->{"$el.PortType"}) {
            foreach my $a ('PortType','FcUnitType','TypeName',
                    'ProtocolOperational','Name',
                    'NodeWwn','PortState','PortStatus',
                    'RemoteWWN', 'RemoteVendor','RemoteProduct','RemoteRevision') {
              $rc->{"fcPort.$pcnt.$a"} = $report->{"$el.$a"};
            }
            $rc->{"fcPort.$pcnt.LocalPortWWN"} = $report->{"$el.PortWwn"};
            $rc->{"fcPort.$pcnt.PortWWN"}      = $RP_WWN{$mp};
            $rc->{"fcPort.$pcnt.PortId"}       = $mp;
            $pcnt++;
          }

          $el    = "etherPort.$mp";
          if ($report->{"$el.PortType"}) {
            foreach my $a ('PortType','MacAddr','Name','NodeName','PeerPortEnabled',
                    'PortState','RealPortNum','CpuNum', 'HardwareState') {
              $rc->{"etherPort.$mp.$a"} = $report->{"$el.$a"};
            }
            $rc->{"etherPort.$mp.PortWWN"}    = $rc->{"etherPort.$mp.NodeName"};
          }
      }
    }
    $rc->{"total.fcPort"} = $pcnt;

    $rc->{ipno}     = Util->name2ip($q->{name});
    $rc->{wwn}      = $rc->{key};
    $rc->{type}     = "dsp";
    $rc->{model}    = "DSP1200";
    $rc->{units}    = 1;
    $rc->{wwns}     = $wwns;
    $rc->{revision} = $report->{"MIC.Rear5.cpuImageVersion.1.Revision"};
    $rc->{serial}   = $report->{'SIO.Front3.SerialNumber'};
    $rc->{report}   = $report;
    if ($q->{FRUS}) {
       $rc->{"SYSTEM.sysRevision"} =  $rc->{revision};
       foreach my $module (&module_list()) {
         foreach my $side ('Front','Rear') {
           my($m);
           for ($m=1; $m <= 10; $m++) {
               my $el = $side . $m;
               next if (!exists $report->{"$module.$el.CardNum"});
               foreach my $i ('SerialNumber','Class','PartNumber','State',
                              'Type','HWRev', 'PCBRev','State') {
                 $rc->{"FRU.$el.$i"} = $report->{"$module.$el.$i"};
               }
           }
         }
       }
    }
  } else {
    $rc->{error} = "Cannot ping $q->{name}";
  }

  if ($q->{HTTP}) {
    $Data::Dumper::Indent = 0;
    print "\nOK " . Data::Dumper::Dumper($rc) . "\n";
  } else {
    return $rc;
  }
}



###############################
#  INSTRUMENTATION

sub INSTRUMENTATION {
  my($agent, $device) = @_;
  my(@s, %dev, $in, $key, $state, $num);
  my $report = {};
  my $renv = System->get_renv();

  if (System->get_testMode()) {
     # do not change FC_COUNTERS to generate fake FC counters, 
     # rather, change directly  the fibrePort.mxpx.CRC entries.
     #
     my $testrep = Report->readTest($device);
     $testrep->{"FC_COUNTERS"} = $agent->FCfromReport($testrep,$device);
     return ($device->{key}, $testrep->{"id.connect_errs"}, $testrep);
  }

  my $ping_to  = $renv->{'timeout.ping'} || 10;

  $report->{"id.timestamp"} = time;
  $report->{'info.ethernet_status'} = "ok";
  my $err2;
  if ($device->{ipno} && !Util->testIp($device->{ipno},$ping_to)) {
     $report->{'info.ethernet_errs'} = "Ping failed to $device->{ipno} (timeout=$ping_to)";
     $err2 = $report->{'info.ethernet_status'} = "ping_failed";
  } else {
     $err2 = $agent->getConfig($device, $report);
     if (index("|$renv->{categories}|", "|san|") >= 0) {
       $report->{"FC_COUNTERS"} = $agent->FCfromReport($report,$device);
     }
  }
  return ($device->{key}, $err2, $report);
}

########################
#   FC COUNTERS
########################
sub FCfromDevice {
  my($class, $dev) = @_;
  my($db, $VAR1);
  $ERR = undef;
  my $renv = System->get_renv();
  my $rc;
  if ($dev->{host} && ($dev->{host} ne $renv->{hostname} )) {
    $rc = Util::Http->getCommand($dev->{hostIpno}, 
                "Agent::DSP::FC1&path=$dev->{path}&ipno=$dev->{ipno}".
                "&type=$dev->{type}&key=$dev->{key}&HTTP=1", 30);
    $rc = "ERR $Util::Http::ERROR" if (!defined($rc));

    if (substr($rc,0,3) eq "ERR" ) {
      $ERR = $rc;
      return undef;
    } else {
      eval substr($rc,3);
      $ERR = $rc if (!$VAR1);
      return $VAR1;
    }
  } else {
    return &get_FC1($dev);
  }
}

sub get_FC1{

  my($q) = @_;

  my($db, $VAR1);
  my $renv = System->get_renv();
  my $rc;
  my $report = {};
  my $dev = {};
  $dev->{path} = $q->{path};
  $dev->{ipno} = $q->{ipno};
  my $err2 = Agent::DSP->getConfig($dev, $report);
  my $out = Agent::DSP->FCfromReport($report,$q);
  if ($q->{HTTP}) {
     require Data::Dumper;
     print "OK " . Data::Dumper::Dumper($out);
  } else {
     return $out;
  }

}
sub FCfromReport{
  my($class ,$report, $dev) = @_;
  my (%X, $x);
  require Discman;
  my $renv = System->get_renv();
  my $to = TO->readExistingTopo();
  return { error => "No topology"}  if (!$to);
  my $dsp = $to->nodeByName("$dev->{type}:$dev->{key}");

  my ($m, $p, %MAP);
  my $port = 0;
  for ($m=1; $m <= 10; $m++) {
     next if (!exists $report->{"fibrePort.m${m}p1.Name"});
     for ($p=1; $p <= 8; $p++) {
         my $el = "fibrePort.m${m}p$p";
         my $wwn = lc($report->{"$el.PortWwn"});
         $wwn =~ s/\://g;
         $port = Discman::find_port2($dsp, $wwn);

         my $val =  $report->{"$el.LinkFailures"}     . "\t" .
                    "0\t" .
                    $report->{"$el.PrimitiveSequenceProtocolErrors"} . "\t" .
                    $report->{"$el.InvalidCRC"} . "\t" .
                    $report->{"$el.LossofSynchronization"} . "\t" .
                    "0\t" .
		    $report->{"$el.BytesWritten"} . "\t" .
		    $report->{"$el.BytesRead"};
         #$X{"0|$dev->{key}|m${m}p$p|$dev->{type}"} = $val;
         $X{"0|$dev->{key}|$port|$dev->{type}"} = $val;
	 $MAP{$port} = "m${m}p${p}";
         $port++;
     }
  }
  return {enc => {$dev->{key} => $dev->{ipno}}, data => \%X, hba => {} , map => \%MAP} ;
}



my @FILES = ( "sa_chas.xml", "sa_disks.xml", 
              "sa_sys.xml", "sa_vols.xml",
              "sa_fctrg.xml", "sa_fcsrv.xml",
              "sa_fcprt.xml", "sa_eprt.xml"
                );

sub check_char {
 my($c) = @_;
 return (ord($c) > 128) ? "" : $c;
}


#
# when called with linkOnly=1, this function will not extract volume/lun information
# for better performance.
#
sub getConfig {
  my($class, $device, $report0, $linkOnly) = @_;

  my $report = {};

  my $name = $device->{path} || $device->{ipno};
  my ($err,$com);
  my @URLS;

  my $renv = System->get_renv();
  my $TO   = $renv->{"timeout.http"} ||  $DSP_TO;

  require XML::LibXML;
  my $parser = XML::LibXML->new();
  my $dom;
  my $elem;

  foreach my $file (@FILES){
     next if (index("sa_vols.xml,sa_fctrg.xml", $file) >= 0  && $linkOnly);

     my $xml = Util::Http->get("http://$name/$file",$TO);

     $xml =~ s/<InitSize>(\d+).*GB<\/InitSize>/<InitSize>$1 GB<\/InitSize>/g;
     $xml =~ s/<RawSize>(\d+).*GB<\/RawSize>/<RawSize>$1 GB<\/RawSize>/g;

     if(length($xml) > 5){
      eval {
        $dom = $parser->parse_string($xml);
      };
      if ($@) {
        Debug->print2("Error parsing XML from \"$name/$file\".\n$@");
        print("Error parsing XML from \"$name/$file\".");
      }else{
        eval{
          $elem = $dom->getDocumentElement();
          &xml2report( $report, $elem, "", 0);
        };
        if ($@) {
          Debug->print2("Error parsing XML from \"$name/$file\".\n$@");
          print("Error parsing XML from \"$name/$file\".");
        }
      }
    } else {
       $err .= "Probing failed on http://$name/$file ";
       print   "Probing failed on http://$name/$file ";
    }
  }
  my($x, %SLOT);

  # FIX SLOT FOR MODULE and PORTS

	#build chassis
  $report0->{"SRC.Front1.CardNum"} = "1";
  $report0->{"SRC.Front2.CardNum"} = "2";
  $report0->{"SRC.Front3.CardNum"} = "3";
  $report0->{"SRC.Front4.CardNum"} = "4";
  $report0->{"SFC.Front5.CardNum"} = "5";
  $report0->{"SFC.Front6.CardNum"} = "6";

  $report0->{"SIO.Rear1.CardNum"} = "1";
  $report0->{"SIO.Rear2.CardNum"} = "2";
  $report0->{"SIO.Rear3.CardNum"} = "3";
  $report0->{"SIO.Rear4.CardNum"} = "4";
  $report0->{"MIC.Rear5.CardNum"} = "5";
  $report0->{"MIC.Rear6.CardNum"} = "6";

  $report0->{"SRC.Front1.availability"} = "Not Installed";
  $report0->{"SRC.Front2.availability"} = "Not Installed";
  $report0->{"SRC.Front3.availability"} = "Not Installed";
  $report0->{"SRC.Front4.availability"} = "Not Installed";
  $report0->{"SFC.Front5.availability"} = "Not Installed";
  $report0->{"SFC.Front6.availability"} = "Not Installed";

  $report0->{"SIO.Rear1.availability"} = "Not Installed";
  $report0->{"SIO.Rear2.availability"} = "Not Installed";
  $report0->{"SIO.Rear3.availability"} = "Not Installed";
  $report0->{"SIO.Rear4.availability"} = "Not Installed";
  $report0->{"MIC.Rear5.availability"} = "Not Installed";
  $report0->{"MIC.Rear6.availability"} = "Not Installed";

  $report0->{"SRC.Front1.State"} = "Not Installed";
  $report0->{"SRC.Front2.State"} = "Not Installed";
  $report0->{"SRC.Front3.State"} = "Not Installed";
  $report0->{"SRC.Front4.State"} = "Not Installed";
  $report0->{"SFC.Front5.State"} = "Not Installed";
  $report0->{"SFC.Front6.State"} = "Not Installed";

  $report0->{"SIO.Rear1.State"} = "Not Installed";
  $report0->{"SIO.Rear2.State"} = "Not Installed";
  $report0->{"SIO.Rear3.State"} = "Not Installed";
  $report0->{"SIO.Rear4.State"} = "Not Installed";
  $report0->{"MIC.Rear5.State"} = "Not Installed";
  $report0->{"MIC.Rear6.State"} = "Not Installed";

  $report0->{"SRC.Front1.Class"} = "Storage Resource Card";
  $report0->{"SRC.Front2.Class"} = "Storage Resource Card";
  $report0->{"SRC.Front3.Class"} = "Storage Resource Card";
  $report0->{"SRC.Front4.Class"} = "Storage Resource Card";
  $report0->{"SFC.Front5.Class"} = "Switch Fabric Card";
  $report0->{"SFC.Front6.Class"} = "Switch Fabric Card";


  # IO card is special because sometime it could be a combo IO card.
  # So everytime, try to put lastest valid card type into it.
  my $save = &getLastIOCardType();
  $report0->{"SIO.Rear1.Class"} = $save->{"SIO.Rear1.Class"};
  $report0->{"SIO.Rear2.Class"} = $save->{"SIO.Rear2.Class"};
  $report0->{"SIO.Rear3.Class"} = $save->{"SIO.Rear3.Class"};
  $report0->{"SIO.Rear4.Class"} = $save->{"SIO.Rear4.Class"};
  $report0->{"MIC.Rear5.Class"} = "Managment Card";
  $report0->{"MIC.Rear6.Class"} = "Managment Card";


  foreach my $el (keys %$report) {
      my($type, $no, $rest) = split(/\./, $el, 3);
      next if($no eq "total");
      my $slot;
      my $val = $report->{$el};
      if ($type eq "module") {
         if ($SLOT{"$type.$no"}) {
           $slot = $SLOT{"$type.$no"};
         } else {
           my @C = split(/\_/, $report->{"$type.$no.Type"});
           $SLOT{"$type.$no"} = $slot = "$C[0].".$report->{"$type.$no.Panel"} . 
                                $report->{"module.$no.CardNum"};
         }
         #change type if it is a combocard
         if($rest eq "Class" && $report->{"$type.$no.Type"}eq"SIO_2GFC_GE_CMB"){
           $val = "I/O Combo Card";
         }
      } elsif ($type eq "fibrePort" || $type eq "etherPort") {
         if ($SLOT{"$type.$no"}) {
           $slot = $SLOT{"$type.$no"};
         } else {
           $SLOT{"$type.$no"} = $slot = "$type.m" . 
                                $report->{"$type.$no.ModuleNum"} . 
                                "p" . $report->{"$type.$no.ModulePortNum"};
         }
      }
      if ($rest =~ /wwn/i) {
         $val =~ s/\://g ;
         $val = lc($val);
      }
      if ($slot) {
         $report0->{"$slot.$rest"} = $val;
         $report0->{"$slot.availability"} = "Installed";
      } else {
         $report0->{$el} = $val;
      }
  }
  my($m, $p,$path, $d, %D);

  for ($d=1; $d <= 1000; $d++) {
     last if (!exists $report0->{"disk.$d.NodeWWN"});
     for ($path=1; $path <= 2; $path++) {
         next if (!exists $report0->{"disk.$d.path.$path.Name"});
         my @info = split(/\//, $report0->{"disk.$d.path.$path.Name"});
         my $el = "m$info[1]p$info[2]";
         $report0->{"fibrePort.$el.RemoteWWN"} =
                    substr($report0->{"disk.$d.path.$path.PortWWN"},0,16);
         $report0->{"fibrePort.$el.RemoteVendor"} = $report0->{"disk.$d.Vendor"};
         $report0->{"fibrePort.$el.RemoteProduct"}= $report0->{"disk.$d.Product"};
         $report0->{"fibrePort.$el.RemoteRevision"}=$report0->{"disk.$d.Revision"};
     }

  }
  $report0->{"psu.1.PsuState"} =~ s/ \/ /\-/;
  $report0->{"psu.2.PsuState"} =~ s/ \/ /\-/;

  &saveIOCardType($report0);

  return $err;
}


# 'fcserver.2.Description' => 'N/A',
# 'fcserver.2.ElementName' => 'server1',
# 'fcserver.2.instance.1.InstanceName' => 'server1/4/5',
# 'fcserver.2.instance.1.InstanceState' => 'Allocated',
# 'fcserver.2.instance.1.InstanceStatus' => 'Online',
# 'fcserver.2.Status' => 'Online',
# 'fcserver.2.SVSD' => 'DEFAULT',
# 'fcserver.2.Wwn' => '21:00:00:E0:8B:07:7D:2E',


sub xml2report {
  my( $report, $node, $prefix, $level)  = @_;
  my(%MAP);
  my $prefix0 = "$prefix." if ($prefix);
  require XML::LibXML;

  foreach my $el ($node->getChildNodes()) {
    next if ($el->getType() != &XML::LibXML::ELEMENT_NODE() );
    my $name = $el->getName();
    my $pr;
    my @ch1 = $el->getChildNodes();

    if (index(",fcserver,instance,lun,fctarget,server,fru,etherPort,fibrePort,path,fibrePort,volume,psu,cpuImageVersion,disk,module,slice,PeerLinkISCSI,ARP,", ",$name,") >= 0) {
      $MAP{$name}++;
      $pr = "$prefix0$name." . $MAP{$name};
      if($MAP{$name} > $report->{"$prefix0$name.total"}){
        $report->{"$prefix0$name.total"} = $MAP{$name} ;
      }
    } elsif ($level == 0 && $#ch1 == 0) {
      $pr = "info.$name";
    } else {
      $pr = "$prefix0$name";
    }
    if ($#ch1 == 0) {
      if(Util->trim($el->textContent()) ne "."){
        $report->{$pr} = Util->rtrim($el->textContent());
      }
    } else {
       &xml2report($report, $el, $pr, $level+1);
    }
  }

  foreach my $el (keys %MAP) {
     $report->{"totals.$el"} = $MAP{$el};
  }
  my $totalFRUs = 1; # count the fan
  $totalFRUs += $report->{"totals.module"}; # count the cards
  $totalFRUs += $report->{"totals.psu"}; # count the power supplys 
  $report->{"totals.fru"} = $totalFRUs;

}

sub FRUS {
  my($class, $r, $name) = @_;
  my($v) = $r->{_value};
  my @FRUS;
  my $devtype = $r->category();
  foreach my $el (sort keys %$v) {
     if ($el =~ /^(SRC|SFC|SIO|MIC)\.(\w+)\.Class/) {
        my $cat = $1;
        my $mod = $2;
        my $fruid = $mod;
        my $vendor = $v->{"$cat.$mod.Vendor"}       || "SUN";
        my $model  = $v->{"$cat.$mod.PartNumber"}   || "N/A";
        my $serial = $v->{"$cat.$mod.SerialNumber"} || "N/A";
        my $rev    = $v->{"$cat.$mod.HWRev"} . "/" . $v->{"$cat.$mod.PCBRev"};
        my $status = $v->{"$cat.$mod.State"};

        push(@FRUS, [ $name ,$devtype, $cat, $fruid, $vendor,
                      $model , $serial, $rev, $status]);
     }
  }
  push(@FRUS, [ $name ,$devtype, "Power1", $v->{"psu.1.Label"}, "SUN",
                      "N/A", "N/A", "N/A", $v->{"psu.1.PsuState"} ]);
  push(@FRUS, [ $name ,$devtype, "Power2", $v->{"psu.2.Label"}, "SUN",
                      "N/A", "N/A", "N/A", $v->{"psu.2.PsuState"} ]);

  push(@FRUS, [ $name ,$devtype, "Fan", "Fan", "SUN",
                      "N/A", "N/A", "N/A", $v->{"fantray.FanState"} ]);
  return \@FRUS;
}



####################################
#          REPORT
####################################
  
sub REPORT {
  my($class, $host, $r, $arg) = @_;

  my($v) = $r->{_value};
  my($out);
  my($tableCnt) = $arg->{tableCnt};
  my($host0) = $host || System->hostname();
  my $Config = PDM::ConfigFile->read();

  $out .= $class->reportHead($v->{'system.description'}, $r);

  my $cnt;
  my $power =  $v->{'psu.1.Label'} . ":" . $v->{'psu.1.PsuState'} . ", " .
               $v->{'psu.2.Label'} . ":" . $v->{'psu.2.PsuState'};
  my $rev = $v->{'MIC.Rear5.cpuImageVersion.1.Revision'} || $v->{'MIC.Rear6.cpuImageVersion.1.Revision'} || $v->{'SIO.Rear3.cpuImageVersion.1.Revision'} || $v->{'SIO.Rear4.cpuImageVersion.1.Revision'};
  my $revd = $v->{'SIO.Rear3.cpuImageVersion.1.BuildDate'} || $v->{'SIO.Rear4.cpuImageVersion.1.BuildDate'};

  $out .= <<EOF;
 <tr><td bgcolor=$Style::LIGHT align=right>Name:</td><td>$v->{'system.Name'}</td>
     <td bgcolor=$Style::LIGHT align=right>IP:</td><td>$v->{'id.device_ip'}</td>

 <tr><td bgcolor=$Style::LIGHT align=right>Power:</td><td>$power</td>
     <td bgcolor=$Style::LIGHT align=right>Volumes:</td><td>$v->{'totals.volume'}</td>

 <tr><td bgcolor=$Style::LIGHT align=right>Revision:</td><td>$rev&nbsp;</td>
     <td bgcolor=$Style::LIGHT align=right>RevDate</td><td>$revd&nbsp;</td>

 <tr><td bgcolor=$Style::LIGHT align=right>Fan:</td><td>$v->{'fantray.FanState'}</td>
     <td bgcolor=$Style::LIGHT align=right>Ports:</td><td>$v->{'totals.fibrePort'}</td>

 <tr><td bgcolor=$Style::LIGHT align=right>Virtual Disks:</td><td>$v->{'totals.disk'}</td>
     <td bgcolor=$Style::LIGHT align=right>Cards:</td><td>$v->{'totals.module'}</td>

  </table>
EOF
 

# FRUs

  $out .= "<table border=1 cellspacing=0 width=$Style::WIDTH bgcolor=white>
    <tr bgcolor=$Style::DARK><td colspan=8><font color=white><b>FRUs</td>
    <tr bgcolor=$Style::LIGHT>
        <td>Location</td>
        <td>Type</td>
        <td>Class</td>
        <td>Model/ Serial</td>
        <td>HW/ PCB</td>
        <td>#Cpus/ Ports</td>
        <td>Status</td>
        <td>Temp</td>
   ";

  my($x, $c);
  my $port= 0;
  foreach my $module (&module_list()) {
   foreach my $side ('Front','Rear') {
    for ($c=1; $c <= 10; $c++) {
     my $x = "$side$c";
     next if (!exists $v->{"$module.$x.Class"});
     my $Ready = $v->{"$module.$x.Ready"};
     $Ready = "<font color=red>".$Ready."</font>" if($Ready =~ /Not/);
     my $State = $v->{"$module.$x.State"};
     $State = "<font color=red>".$State."</font>" if($Ready =~ /Off/);

     $out .=<<EOF;
        <tr>
          <td><center>
              $v->{"$module.$x.Panel"} slot $v->{"$module.$x.CardNum"}</td>
          <td><center>$v->{"$module.$x.Type"}</td> 
          <td><center><small>$v->{"$module.$x.Class"}</td>
          <td><center><small>$v->{"$module.$x.PartNumber"}/ $v->{"$module.$x.SerialNumber"}</td>
          <td><center>$v->{"$module.$x.HWRev"}/ $v->{"$module.$x.PCBRev"}</td>
          <td><center>$v->{"$module.$x.NumCpus"}/ $v->{"$module.$x.NumPorts"}</td>
          <td><center> $Ready / $State </td>
          <td><center>$v->{"$module.$x.TempF"} F</td>
EOF
     my($y);
     if ($v->{"$module.$x.cpuImageVersion.1.BuildDate"}) {
       $out .=<<EOF;
       <tr><td colspan=2>&nbsp;</td><td colspan=6><table border=0 cellspacing=1 cellpadding=0 width=100%>
           <tr bgcolor=$Style::LIGHT>
              <td>&nbsp;Proc</td>
              <td>&nbsp;Rev</td>
              <td>&nbsp;Build</td>
EOF
       for ($y=1; $y <= 10; $y++) {
          my $el = "$module.$x.cpuImageVersion.$y";
          last if (!$v->{"$el.BuildDate"});
         
          $out .=<<EOF;
           <tr>
             <td>&nbsp;$v->{"$el.CardNum"}-$v->{"$el.ProcNum"}</td>
             <td>&nbsp;$v->{"$el.Revision"}</td>
             <td>&nbsp;$v->{"$el.BuildDate"}</td>
           </tr>
EOF
       }
       $out .= "</table>";
     }
     if ($v->{"$module.$x.fru.1.PartNumber"}) {
       $out .=<<EOF;
       <tr><td colspan=2></td><td colspan=7><table border=0 cellspacing=1 cellpadding=0 width=100%>
           <tr bgcolor=$Style::LIGHT>
              <td>&nbsp;Type</td>
              <td>&nbsp;HW - PCB</td>
              <td>&nbsp;PartNo</td>
              <td>&nbsp;Serial</td>
EOF
       for ($y=1; $y <= 10; $y++) {
          my $el = "$module.$x.fru.$y";
          last if (!$v->{"$el.PartNumber"});
         
          $out .=<<EOF;
           <tr>
             <td>&nbsp;$v->{"$el.Type"}</td>
             <td>&nbsp;$v->{"$el.HWRev"} - $v->{"$el.PCBRev"}</td>
             <td><small>&nbsp;$v->{"$el.PartNumber"}</td>
             <td><small>&nbsp;$v->{"$el.SerialNumber"}</td>
           </tr>
EOF
       }
       $out .= "</table>";
     }
     $out .= "<tr><td></td>";
    }
   }
  }
  $out .=<<EOF;
    <tr>
      <td><center>Front Left Side</td>
      <td><small><center>Fan</td>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
      <td><center>$v->{'fantray.FanState'}</td>
      <td>&nbsp;</td>
    </tr>
    <tr>
      <td><center>Rear Top Module $v->{'psu.1.Label'}</td>
      <td><center>PSU</td>
      <td><small><center>Power Supply </td>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
      <td><center>$v->{'psu.1.PsuState'}</td>
      <td>&nbsp;</td>
    </tr>
    <tr>
      <td><center>Rear Top Module $v->{'psu.2.Label'}</td>
      <td><center>PSU</td>
      <td><small><center>Power Supply </td>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
      <td>&nbsp;</td>
      <td><center>$v->{'psu.2.PsuState'}</td>
      <td>&nbsp;</td>
    </tr>
EOF
  $out .= "</table>";

###################################################
# PORTS 

  $out .= "<table border=1 cellspacing=0 width=$Style::WIDTH bgcolor=white>
    <tr bgcolor=$Style::DARK><td colspan=5><font color=white><b>Ports</td>
    <tr bgcolor=$Style::LIGHT>
        <td>Port#</td>
        <td><center>Slot /Port</td>
        <td>Type</td>
        <td>State</td>
        <td>&nbsp;WWN / Remote WWN</td>
   ";

  my($m,$p,$x);
  my $cnt = 0;
  for ($m=1; $m <= 10; $m++) {
     for ($p=1; $p <= 16; $p++) {
       my $x = "m${m}p$p";
       my $x0 = "$m$p";
       my $x1 = "$m/$p";
       next if (!exists $v->{"fibrePort.$x.Name"});
       my $port = $v->{"fibrePort.$x.PortWwn"}; $port =~ s/\://g;
       $port .=" / " . $v->{"fibrePort.$x.RemoteWWN"} if ($v->{"fibrePort.$x.RemoteWWN"});
       my $node = $v->{"fibrePort.$x.NodeWwn"}; $node =~ s/\://g;
       my $status = $v->{"fibrePort.$x.PortState"};
       my $col = $status eq "Online" ? "" : "bgcolor=#FFD0D0";
       $out .=<<EOF;
        <tr>
          <td><center>$x0</td>
          <td><center>$x1</td>
          <td width=40%><small>$v->{"fibrePort.$x.ModuleType"} - $v->{"fibrePort.$x.PortType"} - 
                     $v->{"fibrePort.$x.ProtocolOperational"}</td>
          <td $col>$status</td>
          <td><small>$port</td>
EOF
       $cnt++;
     }
  }
  $out .= "</table>";

###################################################
# DISKS

  $out .= "<table border=1 cellspacing=0 width=$Style::WIDTH bgcolor=white>
    <tr bgcolor=$Style::DARK><td colspan=4><font color=white><b>Virtual Disks (Array Slices)</td>
    <tr bgcolor=$Style::LIGHT>
        <td>Product</td>
        <td>WWN</td>
        <td>Revision</td>
        <td>RawSize</td>
   ";

  my(%ARRAY, $x);
  my $port= 0;
  for ($x=1; $x <= 100; $x++) {
     last if (!$v->{"disk.$x.Product"});
     my $wwn = $v->{"disk.$x.LunWWN"};
     $wwn =~ s/://g;
     $wwn = substr($wwn,0,20) . " " . substr($wwn,20);
     $out .=<<EOF;
        <tr>
          <td>$v->{"disk.$x.Vendor"}- $v->{"disk.$x.Product"}</td>
          <td><small>$wwn</td>
          <td><center>$v->{"disk.$x.Revision"}</td>
          <td align=right>$v->{"disk.$x.RawSize"}</td>
EOF
      if ($v->{"disk.$x.path.1.Active"}) {
        $out .=<<EOF;
        <tr><td></td><td colspan=3><table border=0 cellspacing=1 cellpadding=0 width=100%>
          <tr  bgcolor=$Style::LIGHT>
          <td>Name</td>
          <td>Slot</td>
          <td>State</td>
EOF
         my($y);
         for($y=1; $y <= 20; $y++) {
            last if (!$v->{"disk.$x.path.$y.Active"});
            #'disk.1.path.1.PortWWN' => '20030003ba4d2f150000000000000000',
            my ($array, $w2, $val);
            if ($v->{"disk.$x.path.$y.PortWWN"}) {
               $w2 = substr($v->{"disk.$x.path.$y.PortWWN"},0,16);
               my $dev = $Config->deviceByWWN($w2);
               $array = "($dev->{name})" if ($dev);
               my @N = split(/\//, $v->{"disk.$x.path.$y.Name"});
               $ARRAY{ "$N[0]/$N[1]/$N[2]/$N[3]" } = $array;
            }
             $out .=<<EOF;
                   <tr><td>$v->{"disk.$x.path.$y.Name"} $array</td> 
                   <td>$v->{"disk.$x.path.$y.Slot"}</td> 
                   <td>$v->{"disk.$x.path.$y.State"}</td> 
EOF
         }
         $out .= "</table></td><tr><td></td>";
      }
            
  }
  $out .= "</table>";

##############################
# FCSERVER

  $out .= "<table border=1 cellspacing=0 width=$Style::WIDTH bgcolor=white>
    <tr bgcolor=$Style::DARK><td colspan=5><font color=white><b>FC-servers</td>
    <tr bgcolor=$Style::LIGHT>
        <td>Name</td>
        <td>WWN</td>
        <td>Status</td>
        <td>SVSD</td>
   ";

  my($x, $i, $last_fc);

  foreach my $el (keys %$v) {
     if ($el =~ /^fcserver\.(\d+)\./) {
       my $no = $1;
       $last_fc = $no if ($no > $last_fc);
     }
  }
  for ($x=1; $x <= $last_fc; $x++) {
     next if (!exists $v->{"fcserver.$x.ElementName"});
     my $name   = $v->{"fcserver.$x.ElementName"};
     my $wwn    = $v->{"fcserver.$x.Wwn"};
     my $status = $v->{"fcserver.$x.Status"};
     my $svsd   = $v->{"fcserver.$x.SVSD"};
     $out .= "
        <tr>
          <td>$name</td>
          <td>$wwn</td>
          <td>$status</td>
          <td>$svsd</td>
         ";
     my $ins;
     for ($i=1; $i <= 100; $i++) {
       next if (!exists $v->{"fcserver.$x.instance.$i.InstanceName"});
       my $name   = $v->{"fcserver.$x.instance.$i.InstanceName"};
       my $state  = $v->{"fcserver.$x.instance.$i.InstanceState"};
       my $status = $v->{"fcserver.$x.instance.$i.InstanceStatus"};
       $ins .= "<table border=0 cellspacing=1 cellpadding=0 width=80%>
                <tr bgcolor=$Style::LIGHT><td>InstanceName</td><td>Status</td><td>State</td>" if (!$ins);
       $ins .= "<tr>
          <td>$name</td>
          <td>$status</td>
          <td>$state</td>
         ";
     }
     if ($ins) {
        $out .= "<tr><td></td><td colspan=3>$ins</table>
                     <table border=0><tr><td></table>
                     </td>";
     }
 
  }
  $out .= "</table>";



###############################
# VOLUMES

  $out .= "<table border=1 cellspacing=0 width=$Style::WIDTH bgcolor=white>
    <tr bgcolor=$Style::DARK><td colspan=6><font color=white><b>Mapped LUNs</td>
    <tr bgcolor=$Style::LIGHT>
        <td>Element</td>
        <td>State</td>
        <td>Type</td>
        <td>Condition</td>
        <td>Capacity</td>
        <td>WWN</td>
   ";
  my($last_vol, $x);
  my $port= 0;
  foreach my $el (keys %$v) {
     if ($el =~ /^volume\.(\d+)\./) {
       my $no = $1;
       $last_vol = $no if ($no > $last_vol);
     }
  }
  for ($x=1; $x <= $last_vol; $x++) {
     next if (!$v->{"volume.$x.DeviceID"});
     my $dev = $v->{"volume.$x.DeviceID"};
     $dev =~ s/\://g;
     $out .=<<EOF;
        <tr>
          <td>$v->{"volume.$x.ElementName"}</td>
          <td>$v->{"volume.$x.State"}</td>
          <td>$v->{"volume.$x.Type"}</td>
          <td>$v->{"volume.$x.Condition"}</td>
          <td>$v->{"volume.$x.VolumeSize"}</td>
          <td>$dev</td>
        </tr>
EOF
      my ($s);
      my $first=1;
      for ($s=1; $s <= 200; $s++) {
          last if (!$v->{"volume.$x.slice.$s.SliceName"});
          if ($first) {
            $out .= "<tr bgcolor=#F0F0F0><td></td><td colspan=3>Slice(s)</td><td>Size</td><td>Status</td>";
            $first = 0;
          }
          my $name = $v->{"volume.$x.slice.$s.SliceName"};
          my $size = $v->{"volume.$x.slice.$s.SliceSize"};
          my $st   = $v->{"volume.$x.slice.$s.SliceStatus"};
          my $n = substr($name, 0,-4);
          $out .= "<tr><td></td><td colspan=3>$name $ARRAY{$n}</td><td>$size</td><td>$st</td>";

      }

  }
  $out .= "</table>";




  $out .= "</table>&nbsp;<p>&nbsp;<p>";

  return $out;

}

sub saveIOCardType {
  my($report)  = @_;
  my $save;
  $save->{"SIO.Rear1.Class"} = $report->{"SIO.Rear1.Class"};
  $save->{"SIO.Rear2.Class"} = $report->{"SIO.Rear2.Class"};
  $save->{"SIO.Rear3.Class"} = $report->{"SIO.Rear3.Class"};
  $save->{"SIO.Rear4.Class"} = $report->{"SIO.Rear4.Class"};

  Util->serialize("siotype", $save);
}

sub getLastIOCardType {
 my $save = Util->deserialize("siotype");
 if(!$save) {
   $save->{"SIO.Rear1.Class"} = "I/O Card";
   $save->{"SIO.Rear2.Class"} = "I/O Card";
   $save->{"SIO.Rear3.Class"} = "I/O Card";
   $save->{"SIO.Rear4.Class"} = "I/O Card";
 }
 return $save;
}

1;

