package Agent::T3;
#<copyright>
# ----------------------------------------------------------
# Sun Proprietary/Confidential Code
# Copyright 2001, Sun Microsystems, Inc. All rights reserved.
# ----------------------------------------------------------
#</copyright>
#  $Name:  $ 
#  $Id: T3.pm,v 1.15 2004/09/28 00:06:29 xy128994 Exp $
use strict;
use State;
use PDM::Parse;
use Report;
use Agent;
use base 'Agent';
use Policies;
use RasThread;

sub isSelectable { "Sun T3"}
sub revision     { '$Revision: 1.15 $'}
sub type         { Report::CAT_T3 }
sub diskTotal    { 9    }

use vars qw(%N2IP $ERR %FOUND %SKIP %LAST_SHELL $DB);

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

  bless ($self, 'Agent::T3');
  return $self;
}
use vars qw ($DONE);

sub printit {
  my($dev) = @_;
  Debug->print1("-> Reading $dev->{type}:$dev->{name}/$dev->{ip}");
}

sub RUN {
  my($agent, $static_list, $pass) = @_;
  my($err_lines, $warn_lines,  $out,$device, $connect_errs);
  my($report ,  $id, $lines, $log_err, $type, $timelapse);
  my( $wwn, $portWWN, $port2WWN);
  $DB::single = 1;
  my $renv           = System->get_renv();
  my $type           = uc($agent->type());
  my $processed_list = [];
  my $dc             = 0;
  my $syslog_info    = System->grep("/etc/syslog.conf", "\.t3");
  my $http_to        = $renv->{'timeout.http'} || 60;

  my $se_conflict = System->get_se_conflict();
  my $health  = State->getComponentState();
  my @devices = $agent->deviceList($static_list);
  my $run_cnt = ($#devices+1) * $renv->{frequency} / (6 * 60);
  $run_cnt    = int($run_cnt+0.5);
  $run_cnt    = 1 if (!$run_cnt);
  my $MAX     = $renv->{max_discovery} || 75;

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

  Debug->print1("-> Doing $renv->{array_max_thread} devices at a time...") if ($renv->{array_max_thread} > 1);

   my (@LIST);
  foreach $device ( @devices ) {
     next if ($device->{active} eq "N" || $SKIP{$device->{ipno}});
     my $key = "$device->{type}:$device->{key}";
     if ($se_conflict && index(",$se_conflict,", ",$device->{name},") >= 0 ) {
       Debug->print2(
           "-> $type: skipping $device->{name}, detected device lockfile");
       next;
     }
     $dc++;
     $id  = $device->{_name};

     #Debug->print1("-> Reading $type:$device->{name}/$device->{ip}");

     if (!$device->{ip}) {
        Debug->errNoRepeat( T3_IP => $device->{name} , 24);
        next;
     }
     push(@LIST, $device);
  }
  my $thread = RasThread->new({ 
                TO => $http_to * 8, 
       device_list => \@LIST, 
   displayFunction => \&printit,
        debugLevel =>  Debug->level(),
               max => $renv->{array_max_thread} 
                    });
  while (1) {
     my $run_list = $thread->run();
     last if ($#$run_list < 0);
     my ($brokes, $aborts);
     foreach my $run (@$run_list) {
         $report = $run->[1];
         $device = $run->[0];
         Debug->print1("-> Processing $device->{type}:$device->{name}/$device->{ip} ($run->[2] secs)") if ($renv->{array_max_thread} > 1);
          
         $connect_errs = $report->{"rc.error"};
         $wwn          = lc($report->{"rc.key"});

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

         if (!$connect_errs) {
           if (!$wwn) {
              Debug->errNoRepeat(T3_NO_KEY => $device->{name}, 24, "$device->{name} returned no midplane serial#");
           } elsif ($wwn ne $device->{key}) {
              Debug->errNoRepeat(T3_NEW_KEY => $device->{name}, 24);
              PDM::ConfigFile->updateDevice($device->{key}, { key => $wwn});
              $device->{key} = $wwn;
           }
         }

         my $t1 = $device->{ipno} || $device->{ip};

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

         $report->{"info.last_shell_date"} = $LAST_SHELL{$device->{ipno}} if ($LAST_SHELL{$device->{ipno}});
         $report->{"id.name"}        = $device->{name};
         $report->{"id.mgmtLevel"}   = $device->{mgmtLevel};
         $report->{"id.ip"}          = $device->{ip};
         $report->{"id.ipno"}        = $device->{ipno};
         $report->{"id.wwn"}         = $device->{key};

         $report->{"info.syslog"}    = $syslog_info;
         $report->{"info.OOB_date"} = Util->get_today();
         my $new_rep;
         if ($connect_errs || !$wwn) {
            $report->{"info.OOB_connection"} = $connect_errs;
            $report->{'id.connect_errs'} = $connect_errs;
            $new_rep = Report->new($id, $report , "", Report::STATUS_CANNOT_CONNECT);
         } else {
            Discover->updateTopoDevice($device, {report => $report});
            $report->{"info.OOB_connection"} = "OK";
            $new_rep = Report->new($id, $report , "");
         }
         require Health::T3;
         my $problems = PDM->get_last_event();
         Health::T3->all_logic($new_rep);
         PDM->saveReport($new_rep);

         my ($broke, $abort) = $agent->new_events($problems, $device, 1);
         push(@$processed_list, $device);
         $brokes += $broke; $aborts += $abort;
     }
     last if ($brokes && $pass == 1 || $aborts);
  }
  return $processed_list;
}




# GET SLICE INFO from ARRAY REPORTS
sub slice_map {
  require Report;
  my $dev_list = Report->list("t3,6020", 1);  # details
  my %SLICE;
  foreach my $d (@$dev_list) {
     my $rep = $d->[5];
     my $key = $d->[1];
     foreach my $el (keys %$rep) {
        if ($el =~ /slice\.(\d+)\.volSliceName/) {
           my $id = $1;
           $SLICE{"$key.slice.$id"} =  $rep->{"slice.$id.volId"} . ", " .
                                 $rep->{"slice.$id.volSliceName"};
        }
     }
  }
  Util->serialize("slice_map", \%SLICE);
}



sub getUserLabel {
  my($class, $ctrl) = @_;
  my ($label);

  if ($ctrl !~ /^501/) {
    $label = "t3";
  } else {
    $label = "t3b";
  }
  return $label;
}
sub disk_map {
  my($class, $rc) = @_;
  my $disk_map;
  foreach my $el (sort keys %$rc) {
     if ($el =~ /FRU.u(\d)d(\d+).fruModel/) {
        my $unit = $1;
        my $d    = $2 + 0;
        my $val = $rc->{$el} eq "-" ? -1 : 0;
        $disk_map .= ",$unit-$d" if ($val < 0);
     }
  }
  return "$disk_map,";
}



sub getWWN {
  my($class, $ip, $host, $TO, $frus) = @_;
  my $rc;
  $TO = 300 if (!$TO);
  my $VAR1;
  $ERR = undef;
  if ($host) {
      $rc = Util::Http->getCommand($host, "Agent::T3::WWN&ip=$ip&HTTP=1&TO=$TO", $TO+5);
      if (substr($rc,0,3) eq "ERR") {
         $ERR = $rc;
         return undef;
      } else {
         eval substr($rc,3);
         $rc = $VAR1;
      }
  } else {
      $rc = get_WWN({ip => $ip, TO => $TO, frus => $frus});
  }
  if ($rc->{type} eq "t3") {
    $rc->{userLabel} = $class->getUserLabel($rc->{ctrl_model});
  }
  
  return $rc;
}

sub get_WWN {
  my($q) = @_;
  my $ip = $q->{ip};
  my $TO = $q->{TO};
  my $frus = $q->{frus};
  my $renv = System->get_renv();
  my %H;
  my($key, $wwn1, $wwn2, $ctr_model);
  my $alive = Util->ping($ip, 10);
  my $type = "t3";
  my $ucnt = 1;
  my ($elemprop0, $elemprop1, $system, $units, $name, $data);
  my $error;
  if ($alive) { #check if its alive
    $elemprop0=Agent::T3->getTokens($ip,0,$TO, "portprop");  # get system info

    if ($#$elemprop0 > 2) {
      $elemprop1=Agent::T3->getTokens($ip,0,$TO, "fruprop");  # get model/vendor..
      $error .= "fruprop.htm," if (!$elemprop1);
      $system = Agent::T3->getTokens($ip, 0, $TO, "sysprop");  # get system info
      $error .= "sysprop.htm," if (!$system);
      $data = Agent::T3->getTokens($ip, 0, $TO, "frudata") if ($frus);
    } else { 
      $elemprop0 = Agent::T3->getTokens($ip, 0, $TO);  # get portWWN
      $error .= "elemprop.htm," if (!$elemprop0);
      $elemprop1 = Agent::T3->getTokens($ip, 1, $TO);
      $system = Agent::T3->getTokens($ip, 0, $TO, "sysprop");
      $error .= "sysprop.htm," if (!$system);
    }         
    if($elemprop1){
      push(@$elemprop0, @$elemprop1);
    }
    if($system){
      push(@$elemprop0, @$system);
    }
    if($data){
      push(@$elemprop0, @$data);
    }
    if((!$elemprop0 && !$elemprop1 && !$system) || $error){
      $H{err} = "Can ping $ip but cannot get html tokens from these files: $error. Verify device is ".
                "not booting and html files are correctly installed in the web directory on the device.";
 
    }
    
    foreach my $l (@$elemprop0) {
        $l = Util->ltrim($l);
        if ($l =~ /^;u(\d)ctr/) {
           my $u0 = $1;
           $units = $u0 if ($u0 > $units);
        }
        if ($frus) {
           if ($l =~ /;(u\d.*),(fruVendor),(.*);/ ||
               $l =~ /;(u\d.*),(fruModel),(.*);/ ||
               $l =~ /;(u\d.*),(\w+Version),(.*);/ ||
               $l =~ /;(u\d.*),(fruRevision),(.*);/ ||
               $l =~ /;(u\d.*),(fruType),(.*);/ ||
               $l =~ /;(u\d.*),(fruSerialNo),(.*);/) {
               $H{"FRU.$1.$2"} = Util->rtrim($3);

           } elsif ($l =~ /sysRevision,(.*);/) {
               $H{"SYSTEM.sysRevision"} = Util->rtrim($1);
           }
               
        }
        if ($l =~ /,sysId,(.*);/) {
           $name = $1;
        } elsif ($l =~ /;u1mpn,fruVendor,(.*);/) {
           $key = lc($1) . ".";
        } elsif  ($l =~ /;u1mpn,fruModel,(.*);/) {
           $key .= lc($1) . ".";
        } elsif  ($l =~ /u1ctr,fruModel,(.*);/) {
           $H{ctrl_model} = $1;
        } elsif  ($l =~ /;u1mpn,fruSerialNo,(.*);/) {
           $key .= lc($1);
        } elsif ($l =~ /;u(\d)p1,portWWN,(.*);/) {
           if ($ucnt == 1) {
             $H{"wwn"} = lc($2);
           } else {
             $H{"wwn$ucnt"} = lc($2);
           }
           $ucnt++;
        } elsif ($l =~ /;u1d14,fruType,diskDrive;/){
            #should be: $type = "6120" elsif ($l =~ /;sys,sysModel,6120/);
           $type = $renv->{solution} eq "N" ? "6120" : "6020";
        }
    }
    $key =~ s/ //g;
    $key = "" if ($key eq ".." || $key eq ".");
    $H{key} = $key;
    $H{type} = $type;
    $H{units} = $units;
  } else {
    $H{err} = "Cannot ping";
  }

  my $dir = System->get_home();
  my $ix = index($ip, ":");
  if ($ix > 0) {
    $ip = substr($ip,0,$ix);
  }

  if ($name !~ /^\d+\.\d+\./) {
     my $ix = index($name, ".");
     $name = substr($name,0,$ix) if ($ix > 0);
  }
  $H{name} = $name if ($name);
  $H{ipno} = Util->name2ip($ip);

  if ($q->{HTTP}) {
    require Data::Dumper;
    print "OK " . Data::Dumper::Dumper(\%H);
  } else {
    return \%H;
  }
}


############################################################
#                    SUBROUTINES
############################################################

use Cache;

sub count_empty_lines {
  my($class, $L) = @_;
  my ($x, $empty_lines);
  for ($x=0; $x <= $#$L; $x++) {
     my $line = $L->[$x];
     $line =~ s/^\s+//;
     if (substr($line,0,1) eq ";") {
       my @V = split(/\,/, substr($line,1,-1));
       if ($V[0] && $V[1] && !$V[2]) {
           $empty_lines++;
       }
     }
  }
  return $empty_lines;
}


%N2IP = ();

# get a page of tokens, call back if the page look suspicious
#
sub getPage {
  my($class, $url, $to) = @_;
  my($x);
  my $url1 = $url;
  $url1 =~ s/\W/_/g;
  $url1 = substr($url1,6);
  my $page = Util::Http->get($url, $to);

  my @L = split(/\n/, $page);
  my $line_count = $#L;
  my $empty_lines = $class->count_empty_lines(\@L);

  my $save = Cache->read2("tokenPage", $url1);
  if (!exists $save->{lines}) {
     $save->{lines}       = $line_count;
     $save->{empty_lines} = $empty_lines;
     return $page;
  } 

  if ($save->{lines} > $line_count || $save->{empty_lines} <  $empty_lines ) {
     sleep 5;
     Debug->print2("Calling again page '$url': (lines:$save->{lines},$line_count) (empty_lines:$save->{empty_lines},$empty_lines)");
     $page = Util::Http->get($url, $to);
     @L    = split(/\n/, $page);
     $save->{empty_lines} = $class->count_empty_lines(\@L);
     $save->{lines}       = $#L;
     return $page;
  }
  return $page;
}

sub getTokens {
   my($class, $ip, $unit, $to, $page) = @_;

   $page = "elemprop" if (!$page);

   my $out = $class->getPage("http://guest\@$ip/$page.htm?unitIndex=$unit",$to);
   return [] if (length($out) < 10);

   my @x = split(/\n/, $out);
   return \@x;
}


sub tokens {
  my($class, $ip, $unit, $to, $page, $rep) = @_;
  my ($x);
  $page = "elemprop" if (!$page);
  $rep = {} if (!$rep);
  my $lines = $class->getTokens($ip, $unit, $to, $page);
  for ($x=0; $x <= $#$lines; $x++)  {
       my $l = Util->ltrim($lines->[$x]);
       next if (substr($l,0,1) ne ";" || substr($l,-1) ne ";");
       $l = substr($l,1,-1);
       my @a = split(/\,/, $l);
       $rep->{"$a[0].$a[1]"} = $a[2];
  }
  return $rep;
}
use Timer;
  
sub INSTRUMENTATION0 {
  my($agent, $device, $delay) = @_;
  my $renv     = System->get_renv();

  return Report->readTest($device) if (System->get_testMode());

  my $key = "$device->{type}:$device->{key}";
  my $http_to  = $renv->{'timeout.http'} || 60;
  $delay = 1 if (!$delay);

  return RM->readReport(\&INSTRUMENTATION0, [$agent, $device], $key, $delay,  $http_to * 4);
}

sub INSTRUMENTATION {
  my($agent, $device) = @_;
  my($mode, $connect_err, $stats, $config, $config_errors);
  my($l, $config2, $config_rep, $stats_rep);
  my($sysprop, $elemprop0, $elemprop1, $key , $portWWN, $port2WWN, $frudata, 
     $slices, $luns, $vols, $volprop, $loops, $diskstats, $diskperfs);
  $DB::single = 1;

  return Report->readTest($device) if (System->get_testMode());

  my $renv     = System->get_renv();
  my $ping_to  = $renv->{'timeout.ping'} || 10;
  my $http_to  = $renv->{'timeout.http'} || 60;

  if (!Util->testIp($device->{ip},$ping_to)) {
     $config_rep->{"rc.error"} = "Ping: failed to ping $device->{ip}/$device->{name} (to=$ping_to)";
     $config_rep->{"rc.key"} =   $device->{key};
     return $config_rep;
  }
  Timelapse->startDev( $device);
  $elemprop0 = $agent->getTokens($device->{ip}, 0, $http_to);
  if ($elemprop0 && ($#$elemprop0 >= 0)) {
     $elemprop1 = $agent->getTokens($device->{ip}, 1, $http_to);
     $frudata   = $agent->getTokens($device->{ip}, 1, $http_to, "frudata");
     $sysprop   = $agent->getTokens($device->{ip}, 1, $http_to, "sysprop");
     $slices    = $agent->getTokens($device->{ip}, 0, $http_to, "slice");
     $volprop   = $agent->getTokens($device->{ip}, 0, $http_to, "volprop");
     $vols      = $agent->getTokens($device->{ip}, 0, $http_to, "vol");
     $luns      = $agent->getTokens($device->{ip}, 0, $http_to, "lun");
     $loops     = $agent->getTokens($device->{ip}, 0, $http_to, "loopinf"); # only on t3
     $diskstats = $agent->getTokens($device->{ip}, 0, $http_to, "diskstat"); 
     $diskperfs = $agent->getTokens($device->{ip}, 0, $http_to, "diskperf"); 

     push(@$elemprop0, @$elemprop1) if ($elemprop1); 
     push(@$elemprop0, @$frudata) if ( $frudata ); 
     push(@$elemprop0, @$loops) if ($loops); 
     ($portWWN, $port2WWN, $key, $config_rep) = $agent->parse_elem($elemprop0, $device);
     $agent->parse_slice($config_rep, $slices);
     $config_rep->{"id.timestamp"} = time;
     $agent->parse_vol($config_rep, $vols);
     $agent->parse_vol($config_rep, $volprop);
     $agent->parse_lun($config_rep, $luns);
     $agent->parse_sys($config_rep, $sysprop);
     $agent->volVerify($config_rep, $device);  
     $agent->parse_stat($config_rep, $diskstats);
     $agent->parse_stat($config_rep, $diskperfs);

  } else {
     $config_rep->{"rc.error"} = "Tokens: No tokens returned from $device->{ip}/$device->{name}";
     $config_rep->{"rc.key"} =   $device->{key};
     return $config_rep;
  }
  #if (Timer->isXhours("telnet" . $device->{key}, 6)) {
  #   my $t_err = $agent->telnet_monitor($device, $config_rep);
  #}

  $config_rep->{"rc.error"}       = $connect_err;
  $config_rep->{"rc.key"}         = $key;
  $config_rep->{"rc.portWWN"}     = $portWWN;
  $config_rep->{"rc.port2WWN"}    = $port2WWN;

#
# SAVE TIMEZONE
#
  require DevDB;
  my @tz = split(/\s+/, $config_rep->{"system.sysCtime"});
  DevDB->save($device->{key}, "TimeZone", $tz[4]);

  Timelapse->stopDev( $device);
  return $config_rep;
}

sub getNextMounted {
  my($class, $rep, $CNT, $start) = @_;
  my($unit, $mounted, $first, $vol);
  
  for ($unit = 1; $unit <= $CNT; $unit++) {
     for ($vol=1; $vol <= 2; $vol++) {
       my $v = "u${unit}vol$vol";
       if ($rep->{"volume.$v.volStatus"} eq "mounted") {
          $first = $v if (!$first);
          if  ($v gt $start) {
            return $v;
          }
       }
     }
  } 
  return $first;
}


sub volVerify {
  my($class, $rep, $device) = @_;

  my $renv   = System->get_renv();
  my $verify = $device->{volVerify} || $renv->{volVerify};
  my $fix    = $renv->{'volVerify.fix'} eq "Y" ? 1 : 0;
  my $rate   = $renv->{'volVerify.rate'} || 1;
  return       if ($verify ne "Y");
  my $DIR    = System->get_home() . '/DATA/VolVerify';
  mkdir $DIR, 0777 if (!-d $DIR);
  my $FILE   = "VolVerify/$device->{key}";

  my $telnet = System->getDevicePassword($device, "array");

  my $db  = Util->deserialize($FILE) || {};

  my $CNT = $rep->{'unit.count'} || $rep->{'controller.count'};
  my $scrubber = $rep->{"system.sysDiskScrubber"};
  if ($scrubber eq "on") {
     Debug->warningNoRepeat(VOL_VERIFY => $device->{name}, 48);
     return;
  }
  my $key = $device->{ipno};

  my ($running, $unit);
  for ($unit = 1; $unit <= $CNT; $unit++) {
     if ($rep->{"volume.u${unit}vol1.volOper"}) {
        $running = "u${unit}vol1"; 
        last;
     } elsif ($rep->{"volume.u${unit}vol2.volOper"}) {
        $running = "u${unit}vol2"; 
        last;
     }
  }
  my ($diag_vol);
  my $today = Util->get_today();
  if ($running) {
     if (exists $db->{$key}) {
         my $diag_vol = $db->{$key}{volume};
         $db->{$key}{progress}    = $rep->{"volume.$diag_vol.volOperProgress"};
         $db->{$key}{VOL}{$diag_vol}{progress}   = $db->{$key}{progress};
         # check if too old
     } else {
         # started by somebody else, do nothing
     }
  } else {
     if (exists $db->{$key}) {
         $diag_vol = $db->{$key}{volume};
         my $db_el = $db->{$key};
         # maybe done
         my $soft = $rep->{"volume.$diag_vol.volSoftErrors"} - $db_el->{soft};
         my $firm = $rep->{"volume.$diag_vol.volFirmErrors"} - $db_el->{firm};
         my $hard = $rep->{"volume.$diag_vol.volHardErrors"} - $db_el->{hard};
        
         if ($soft + $firm + $hard) {
           my $err = "vol-verify on volume $db_el->{volume} failed: SoftError: $soft, FirmError: $firm, HardError: $hard";
           $rep->{"diagnostic.error.desc"}  = $err;
           $rep->{"diagnostic.error.volume"}= $db_el->{volume};
           $rep->{"diagnostic.error.start"} = $db_el->{start};
           $rep->{"diagnostic.error.end"}   = $today;
         }
         $db_el->{end}                      = $today;
         $rep->{"diagnostic.duration"}      = time - $db_el->{start_time};
         $db_el->{VOL}{$diag_vol}{end}      = $today;
         $db_el->{VOL}{$diag_vol}{duration} = $rep->{"diagnostic.duration"};
         $db_el->{VOL}{$diag_vol}{soft}     = $soft;
         $db_el->{VOL}{$diag_vol}{firm}     = $firm;
         $db_el->{VOL}{$diag_vol}{hard}     = $hard;

         $diag_vol = $class->getNextMounted($rep, $CNT, $db_el->{volume});
         
     } else {
         $diag_vol = $class->getNextMounted($rep, $CNT);
     }
  }
  if ($diag_vol && &valid_period($renv, $db->{$key}, $diag_vol) ) {  # start diag
     my $vol_name = $rep->{"volume.$diag_vol.volName"};
     my($unit, $vol) = ($diag_vol =~ /u(\d)vol(\d)/);
     $unit--; $vol--;
     my $command  = "update?unitIndex=$unit&volIndex=$vol".
                    "&volVerifyRate=$rate&volSafety=$fix&volVerify=Verify";
     my $url = "http://$device->{ipno}/$command";

     # for bug 5099055: the original timeout (20 sec) is too short to cause 
     # time out error return, which will make this job not listed in the vol 
     # verify monitor. To fix it, change timeout value to 120.

     my($err, $out) = Util::Http->get($url, 120, {password => "root:$telnet"});
     if ($err) {
       Debug->errNoRepeat(VERIFY => $device->{ipno}, 24, $err);
       $db->{$key}{progress}    = "ERR: $err";
     } else {
       Debug->print2("   Start vol-verify on $device->{name} - $diag_vol");
	       open(LOG, ">>" . System->get_home() . "/log/volverify.log");
       print LOG "$today $device->{name} - $diag_vol\n";
       close(LOG);
       my $soft = $rep->{"volume.$diag_vol.volSoftErrors"};
       my $firm = $rep->{"volume.$diag_vol.volFirmErrors"};
       my $hard = $rep->{"volume.$diag_vol.volHardErrors"};
       $db->{$key}{start}       = $today;
       $db->{$key}{end}         = undef;
       $db->{$key}{progress}    = "Started";
       $db->{$key}{start_time}  = time;
       $db->{$key}{soft}        = $soft;
       $db->{$key}{firm}        = $firm;
       $db->{$key}{hard}        = $hard;
       $db->{$key}{volume}      = $diag_vol;
       $db->{$key}{volume_name} = $vol_name;

       $db->{$key}{VOL}{$diag_vol} = {
                      volume_name => $vol_name,
                            start => $today,
                       start_time => time,
                         progress => "Started",
                                  };
     }
  }

  Util->serialize($FILE, $db);
}

sub valid_period {
  my($renv, $el, $vol) = @_;
  my ($today, $year, $month, $day) = Util->get_today_array();
  my ($jd, $dow, $wk) = Util->julian($year, $month, $day);

  my $time = $renv->{"volVerify.time$dow"};  
  my $hour = substr( $today , 11,2);
  $hour += 1;
  return 0 if ($time != 99 && index("\t$time\t", "\t$hour\t") < 0);

  if (index($el->{vols}, $vol) < 0) { # new vol, add to list
      $el->{vols} .= "$vol,";
  } else {
    if ($renv->{'volVerify.frequency'} && $el->{VOL}{$vol}{start}) {
      my @start = split(/[ \-]/, $el->{VOL}{$vol}{start});
      my ($jd1) = Util->julian(@start);
      if ($jd - $jd1 < $renv->{'volVerify.frequency'}) {
        return 0 ;
      } else {
        $el->{vols} = "$vol,";
      }
    } else {
      $el->{vols} = "$vol,";
    }
  }
  return 1;
}


# return error, loads hash pointer $R
#
sub telnet_monitor {
  my($agent, $device, $R) = @_;
  return;
  my $renv = System->get_renv();

  require Net::Telnet;
  my $dt = $agent->diskTotal();

  return if ($renv->{'t3.telnet_monitor'} ne "Y");

  my($t) = new Net::Telnet (
             errmode => "return",
             Timeout   => 30,
             Prompt    => '/<\d+\>/',
             );
  my $ipno = $device->{ipno};

  my $ix = index($ipno, ":");
  if ($ix > 0) {
    $ipno = substr($ipno,0, $ix);
  }

  if (!defined($t->open($ipno))) {
      return "Cannot open $ipno";
  }
  my $pass = System->getDevicePassword($device, "array");

  $t->login("root", $pass);
  if ($t->errmsg()) {
     return $t->errmsg();
  }
  if ($dt == 14) {
    $t->cmd("sun");
    $t->cmd("arrayservice");
  }
  foreach my $u (1,2) {
    my @l = $t->cmd(".disk pathstat u${u}d1-$dt");
    foreach my $l (@l) {
      chop($l);
      next if (substr($l,0,1) ne "u");

      my($d, $rest) = split(/\s/, $l, 2);
      $R->{"disk.$d.pathstat"} = ($rest =~ /\[-1/)? "INVALID":"OK";
    }

    @l = $t->cmd(".disk glist u${u}d1-$dt");
    foreach my $l (@l) {
      chop($l);
      next if (substr($l,0,1) ne "u");
      my($d, $rest) = split(/\s+/, $l, 2);
      $d =~ /u(\d+)d(\d+)/;
      $d = "u$1d" . ($2+0);
      $R->{"disk.$d.glist"} = $rest + 0;
    }
  }
  $t->close();
  return undef;
}

sub generic_parse {
  my($class, $rep, $data, $prefix, $exclude) = @_;
  $prefix = "$prefix." if ($prefix);
  foreach my $l (@$data) {
       $l = Util->ltrim($l);
       if ($l =~ /^;u\dvol\d/) {
          $l = substr($l,1,-1);
          my($vol, $name, $val) = split(/\,/, $l);
          $rep->{"$prefix$vol.$name"} = $val if (!$exclude || $name !~ /$exclude/);
       }
  }
}

sub port_parse {
  my($class, $rep, $data, $prefix, $exclude) = @_;
  $prefix = "$prefix." if ($prefix);
  foreach my $l (@$data) {
	$l = Util->ltrim($l);
	if ($l =~ /^;u\dp\d/) {
	  $l = substr($l,1,-1);
	  my($port, $name, $val) = split(/\,/, $l);
	  $rep->{"$prefix$port.$name"} = $val if (!$exclude || $name !~ /$exclude/);
	}
  }
}

# ;u1lp1,loopStatus,available;


sub parse_slice {
  my($class, $rep, $slices) = @_;

  my %LIST;
  foreach my $l (@$slices) {
       $l = Util->ltrim($l);
       if ($l =~ /^;sys,(.*);/) {
          my($name, $val) = split(/\,/, $1);
          $rep->{"sys.$name"} = $val;

       } elsif ($l =~ /^;slice(\d+),(.*);/) {
          my $no = $1;
          $LIST{$no} = 1;
          my $rest = $2;
          my($name, $val) = split(/\,/, $rest);
          $rep->{"slice.$no.$name"} = $val;
       }
  }
  my ($cnt);
  foreach my $e (keys %LIST) {
    $cnt++;
  }
  $rep->{"slice.count"} = $cnt;
}



sub parse_vol {
  my($class, $rep, $vols) = @_;
  foreach my $l (@$vols) {
    $l = Util->ltrim($l);
        #convert ;u1vol1,volName,vol1; to  'volume.u1vol1.volName' => 'vol1',
    if ($l =~ /^;u(\d+)vol(\d+),(\w+),(.*);/) {
      my $unitnum = $1;
      my $volnum = $2;
      my $volkey=$3;
      my $val =$4;
      $rep->{"volume.u$unitnum"."vol$volnum.$volkey"} = $val;

    } elsif ($l =~ /^;u(\d+)vol(\d+)\-d(\d+),(\w+),(.*);/) {
      my $unitnum = $1;
      my $volnum  = $2;
      my $voldisk = $3;
      my $volkey  = $4;
      my $val     = $5;
      $rep->{"volume_disk.u${unitnum}vol${volnum}-d-$voldisk.$volkey"} = $val;

    }elsif ($l =~ /^;u(\d+)d(\d+),(\w+),(.*);/) {
      my $unitnum = $1;
      my $disknum = ($2+0);
      my $diskkey=$3;
      my $val =$4;
      $rep->{"disk.u$unitnum"."d$disknum.$diskkey"} = $val;
    } elsif ($l =~ /^;sys,glbNumStandby,(.*)/) {
      $rep->{"glbStandby.count"} = $1;
    }elsif(($l =~ /^;u(\d)p(\d)\-a\-(\d),(\w+),(.*);/ ) ||
           ($l =~ /^;u(\d)p(\d)\-a(\d),(\w+),(.*);/)) {
      my $unitnum = $1;
      my $portnum = ($2+0);
      my $portkey=$3;
      my $token =$4;
      my $val =$5;
      $rep->{"port.u$unitnum"."p$portnum-a-$portkey.$token"} = $val;
    }
  }
}

sub parse_stat {
  my($class, $rep, $stats) = @_;
  foreach my $l (@$stats) {
    $l = Util->ltrim($l);
        #convert ;Unit0Path0Disk0,SimErr,0 disk.u1d1.path0.simErr=0
    if ($l =~ /^;Unit(\d+)Path(\d+)Disk(\d+),(\w+),(.*);/) {
      my $unitnum = ($1+1);
      my $pathnum = $2;
      my $disknum=($3+1);
      my $errkey=$4;
      my $val =$5;
      $rep->{"disk.u$unitnum"."d$disknum."."path$pathnum"."$errkey"} = $val;
    }elsif ($l =~ /^;u(\d+)d(\d+),(\w+),(.*);/) {
      my $unitnum = $1;
      my $disknum = ($2+0);
      my $diskkey=$3;
      my $val =$4;
      $rep->{"disk.u$unitnum"."d$disknum.$diskkey"} = $val;
    }
  }
}

sub parse_lun {
  my($class, $rep, $luns) = @_;

  my $max = -1;
  foreach my $l (@$luns) {
       $l = Util->ltrim($l);
       if ($l =~ /^;sys,(.*);/) {
          my($name, $val) = split(/\,/, $1);
          $rep->{"sys.$name"} = $val;

       } elsif ($l =~ /^;lun(\d+),(.*);/) {
          my $no = $1;
          $max = $no if ($no > $max);
          my $rest = $2;
          my($name, $val) = split(/\,/, $rest);
          $rep->{"lun.$no.$name"} = $val;
       } elsif ($l =~ /^;lun(\d+)Initiator(\d+),(.*);/) {
          my $lunno = $1;
          my $initiator_no = $2;
          $max = $lunno if ($lunno > $max);
          my $rest = $3;
          my($name, $val) = split(/\,/, $rest);
          $rep->{"lun.$lunno.Initiator.$initiator_no.$name"} = $val;
       } elsif ($l =~ /^;slice(\d+),(.*);/) {
          my $no = $1;
          my $rest = $2;
          my($name, $val) = split(/\,/, $rest);
          $rep->{"slice.$no.lun-$name"} = $val;
       }
  }
  $rep->{"lun.count"} = $max + 1;
  $rep->{"volume.count"} = $max + 1;
}


sub parse_elem {
  my($agent, $sys, $device) = @_;
  my(%V, $l);
  my($in, $disk_cnt, $ctrl_cnt, $type, $unit_cnt);

  $in = 0; $disk_cnt = 0; $ctrl_cnt = 0; $unit_cnt = 0;
  foreach $l (@$sys) {
     $l =~ s/^\s+//g;
     $l =~ s/\s+$//;
     next if (substr($l,0,1) ne ";");
     my(@b) = split(/,/, $l);
     next if ($#b <= 1);
     next if (substr($b[1],0,6) eq "volSec" || substr($b[1],0,7) eq "portSec");

     chop($b[2]) if (substr($b[2],-1) eq ";");
     $b[2] =~ s/ +/ /g;
     $b[2] =~ s/\c[//g;
     $b[0] = substr($b[0],1);  # remove leading ;
     $type = "";
     if (substr($b[0],0,4) eq "unit") {
         $type = "unit";

     } elsif ($b[0] =~ /^u(\d)d(\d+)/) {
         $type = "disk";
         my $uu = $1;
         $unit_cnt = $uu if ($uu > $unit_cnt);
         my $dd = $2;
         $disk_cnt = $dd+0 if ($dd > $disk_cnt);
         $b[0] = "u$uu" . "d" . ($dd+0);
         #print "$b[0] $b[1] $b[2]\n";

     } elsif ($b[0] =~ /^u(\d)ctr/) {
         $type = "controller";

     } elsif ($b[0] =~ /^u(\d)l\d/) {
         $type = "loopcard";

     } elsif ($b[0] =~ /^u(\d)pcu\d/) {
         $type = "power";

     } elsif ($b[0] =~ /^u(\d)p\d/) {
         $type = "port"; 

     } elsif ($b[0] =~ /^u(\d)lp\d/) {
         if (index(",loopStatus,loopIsIsolated,", $b[1]) >= 0) {
           $V{"loop.$b[0].$b[1]"} = Util->trim($b[2]);
         }
         next;
     } elsif ($b[0] =~ /^u(\d)vol(\d)-d-(\d)/) {
         $type = "voldisk";
     } elsif ($b[0] =~ /^u(\d)mpn/) {
         $type = "midplane";
     } elsif ($b[0] =~ /^u(\d)vol\d/) {
         $type = (substr($b[0],6,1) eq "-")? "volume_disk":"volume";
     }         
# l=;u1ctr,fruId,u1ctr; $b[0]=u1ctr, $b[1]=fruId, $b[2] = u1ctr
     
     my $val = Util->trim($b[2]);
     $val = "" if ($b[1] eq "portWWN" && $val =~ /^0+$/);
     $val = "" if (index("fruVendor,fruModel,fruSerialNo,fruRevision",$b[1]) >= 0 &&
                     $val eq "-");
     if ($type) {
       $V{"$type.$b[0].$b[1]"} = $val;
       $V{"$type.$b[0].fruId"} = $b[0] if (!exists($V{"$type.$b[0].fruId"}));
     }
  }

  my ($x);
  for ($x=1; $x <= 6; $x++) {
    if (exists ($V{"controller.u${x}ctr.fruCtlrRole"})) {
      $ctrl_cnt++;
    }
  }
  $V{"disk.count"} = $disk_cnt;
  $V{"unit.count"} = $unit_cnt;
  $V{"controller.count"} = $ctrl_cnt;
  $V{"system.configuration"} = "$ctrl_cnt";
  $V{"volume.u1vol1.volWWN"} =~ s/00000000000000/0/g;
  my($portWWN, $port2WWN);

  $portWWN  = $V{"port.u1p1.portWWN"};
  if (exists($V{"port.u2p1.portWWN"})) {
    $port2WWN = $V{"port.u2p1.portWWN"};
  } elsif (exists($V{"port.u3p1.portWWN"})) {
    $port2WWN = $V{"port.u3p1.portWWN"};
  } elsif (exists($V{"port.u4p1.portWWN"})) {
    $port2WWN = $V{"port.u4p1.portWWN"};
  }
#  substr($portWWN,-9,1) = "0" if (length($portWWN) > 10);
#  substr($port2WWN,-9,1) = "0" if (length($port2WWN) > 10);

  my($key);
  if ( $V{"midplane.u1mpn.fruVendor"} ) {
    $key = $V{"midplane.u1mpn.fruVendor"} . "." .  
           $V{"midplane.u1mpn.fruModel"} . "." .
           $V{"midplane.u1mpn.fruSerialNo"};
    $key =~ s/[^\w\-\.]/_/g;
  } else {
    $key = $device->{ipno};   # $portWWN;
  }
  $key = lc($key);

  $V{"loopcard.header"} = "loopcard";
  $V{"midplane.header"} = "midplane";
  $V{"power.header"} = "power";
  $V{"port.header"} = "port";
  $V{"unit.header"} = "unit";

# fix the missing serialNo
  foreach my $k ('u1pcu1','u1pcu2','u2pcu1','u2pcu2') {
    if ($V{"power.$k.fruSerialNo"} =~ /refer to/) {
       $V{"power.$k.fruSerialNo"} = "$key.$k";
    }
  }
  $agent->addIdentification(\%V);
  return ($portWWN,$port2WWN, $key, \%V);

}

sub parse_sys {
  my($agent, $report, $sys) = @_;
  my($l, @b);
  my $in = 0;

  foreach $l (@$sys) {
     next if (substr($l,0,1) ne ";");
     @b = split(/,/, $l);
     if ($#b > 1) {
       chop($b[2]) if (substr($b[2],-1) eq ";");
       $b[2] =~ s/ +/ /g;
       $b[2] =~ s/\c[//g;
       next if (substr($b[1],0,6) eq "sysSec");
       $report->{"system.$b[1]"} = Util->rtrim($b[2]);
     }
  }
}

# get CIM-Key using snmp locally or from the right host
# .iso.3.6.1.4.1.42.2.28.2.2.3.2.1.6.1.14
# .iso.3.6.1.4.1.42.2.28.2.2.3.2.1.7.1.14
# .iso.3.6.1.4.1.42.2.28.2.2.3.2.1.9.0.14 

sub getCimKey {
  my($class, $ip, $host) = @_;

  
}

#  name, type, category, fruid, 
#  vendor, model, serial, rev,
#  status, temperature, 
#  battery_life||reboot_time,
#  glist  (applies to disks)
#
  
sub FRUS {
  my($class, $r, $name) = @_;
  my($v) = $r->{_value};
  my @FRUS;
  my $devtype = $r->category();
  foreach my $el (sort keys %$v) {
     if ($el =~ /([^\.]+)\.([^\.]+)\.fruId/ && $el !~ /volume/) {
        my $cat = $1;
        my $fruid = $2;
        my $model = $v->{"$cat.$fruid.fruModel"};
        my $vendor = $v->{"$cat.$fruid.fruVendor"};
        my $serial = $v->{"$cat.$fruid.fruSerialNo"};
        my $rev    = $v->{"$cat.$fruid.fruCtlrVersion"} ||
                     $v->{"$cat.$fruid.fruLoopVersion"} ||
                     $v->{"$cat.$fruid.fruRevision"};
        my $status = $v->{"$cat.$fruid.fruStatus"} . "-".
                     $v->{"$cat.$fruid.fruState"};
        foreach my $el ('fan1=Fan1State','fan2=Fan2State',
                        'output=PowOutput','temp=PowTemp') {
          my($el1, $el2) = split(/\=/, $el);
          my $st1 = $v->{"$cat.$fruid.fruPower$el2"};
          if ($st1 && $st1 ne "normal") {
             $status .= " $el1=$st1,";
          }
        }
        my $fan2 = $v->{"$cat.$fruid.fruPowerFan2State"};
        my $pow  = $v->{"$cat.$fruid.fruPowerPowOutput"};
        my $temp = $v->{"$cat.$fruid.fruDiskTemp"} || 
                   $v->{"$cat.$fruid.fruCtlrTemp"} ||
                   $v->{"$cat.$fruid.fruLoopTemp"} ;
	if($temp eq "65535"){
	  $temp = undef;
	}

        my $reboot;
        if ($cat eq "controller") {
	   my $a1 = $v->{"system.sysCtime"};   # 'Mon May 26 12:09:48 GMT 2003'
	   my $a2 = $v->{"system.sysLastRestart"};
	   $reboot = $class->tok2days($a2, $a1);
        }
        my $glist = $v->{"$cat.$fruid.glist"} if (exists $v->{"$cat.$fruid.glist"});
        
        push(@FRUS, [ $name ,$devtype, $cat, $fruid, $vendor || " N/A",
                      $model||" N/A", $serial||" N/A", 
                      $rev, $status, $temp, $reboot, $glist]);
        if ($fruid =~ /u\d+pcu\d/) {
          my $status= $v->{"$cat.$fruid.fruPowerBatState"} || " N/A";
	  my $life =  $v->{"$cat.$fruid.fruPowerBatLife"};
	  my $used =  $v->{"$cat.$fruid.fruPowerBatUsed"};

          my $model  = $v->{"$cat.$fruid.fruPowerBatModel"}    || " N/A";
          my $vendor = $v->{"$cat.$fruid.fruPowerBatVendor"}   || " N/A";
          my $serial = $v->{"$cat.$fruid.fruPowerBatSerialNo"} || " N/A";
          my $rev    = $v->{"$cat.$fruid.fruPowerBatRevision"} || " N/A";

          push(@FRUS, [ $name, $devtype, "$cat.battery", $fruid, 
	                $vendor, $model, $serial, $rev,
		        $status, undef, $life - $used, undef]);
        }

     }
  }
  return \@FRUS;
}

sub tok2days {
  my($class, $tok1, $tok2) = @_;

   my($day, $hour, $min, $sec, $zone, $year) = split(/[ :]/, substr($tok1,8));
   $year = $zone if ($zone =~ /^20/);

   my $month = $Util::MTH{substr($tok1, 4,3)};
   my $jul1 = Util->julian($year, $month, $day);

   ($day, $hour, $min, $sec, $zone, $year) = split(/[ :]/, substr($tok2,8));
   $year = $zone if ($zone =~ /^20/);

   my $month = $Util::MTH{substr($tok2, 4,3)};
   my $jul2 = Util->julian($year, $month, $day);

   return $jul2 - $jul1;
}

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

  my($name) = $r->{_id}{name};
  my $renv = System->get_renv();
  my $LIGHT = $Style::LIGHT;
  #my $L = Labels->read("Agent::T3");
  my($comm);
  my($out);
  my($tableCnt) = $arg->{tableCnt};
  my($host0) = $host || System->hostname();
  $comm = ($r->{_status} eq "CC") ? "<br><font color=red>Management-Path Lost!</font>" : "OK";
  my $se = $renv->{solution} eq "N" ? "" : ".$renv->{solution}";

  my($v) = $r->{_value};
  my $units = $v->{"unit.count"} || $v->{"controller.count"};
  my $cu = $v->{"controller.count"} . "x$units";
  my $pg = "PG" if ($units > 1);

  #my $cat = $r->id("category");

  my $cat = $r->id("category");

  my $rev = $v->{"system.sysRevision"};
  my $mp_support = $v->{"system.sysMpSupport"};
  my $disk_scrubber = $v->{"system.sysDiskScrubber"} || "off";

  $out .= $class->reportHead($class->isSelectable(), $r);
  $out .= "
   <tr><td bgcolor=#F0F0F0 align=right><b>Name:</td><td width=25%>&nbsp;$r->{_id}{display}</td>
       <td bgcolor=#F0F0F0 align=right><b>WWN:</td><td>$v->{'port.u1p1.portWWN'} $v->{'port.u2p1.portWWN'} $v->{'port.u3p1.portWWN'} $v->{'port.u4p1.portWWN'}
   <tr><td bgcolor=#F0F0F0 align=right><b>Agent:</td><td>&nbsp;$host0</td>
       <td bgcolor=#F0F0F0 align=right><b>Comm:</td><td>&nbsp;$comm</td>
   <tr><td bgcolor=#F0F0F0 align=right><b>Type:</td><td>&nbsp;$cat $pg ($cu)</td>
       <td bgcolor=#F0F0F0 align=right><b>Revision:</td><td>&nbsp;$rev</td>
   <tr><td bgcolor=#F0F0F0 align=right><b>mp_support:</td><td>&nbsp;$mp_support</td>
<td bgcolor=#F0F0F0 align=right><b>disk_scrubber:</td><td>&nbsp;$disk_scrubber</td>

  </table>
  <table border=0 cellspacing=0 width=95% bgcolor=white><tr><td></table>
  <table border=1 cellspacing=0 width=$Style::WIDTH bgcolor=white>

  <tr bgcolor=$LIGHT>
    <th><font color=black>Type</th>
    <th><font color=black>Identification</th>
    <th><font color=black>Location</th>
    <th><font color=black>Status</th>
    <th><font color=black>Rev./Info</th>
  ";

#  my($mon) = &mon('t3',$name);

  my($bg, $cnt, $u, $d, $comp, $status, $id, $rev, $type, $bat, $fans);
  $cnt = 0;
  my $max = $v->{"unit.count"} || $v->{"controller.count"};
  my @spares; 
  for ($u=1; $u <= $max; $u++) {
     $out .= "<tr bgcolor=#FFFFE0><td colspan=5 ><b>&nbsp;Unit $u</td>\n";
     last if (!$v->{"disk.u${u}d1.fruId"});
     for ($d=1; $d <= $class->diskTotal(); $d++) {
        $comp="disk.u${u}d$d";
        $status = $v->{"$comp.fruStatus"} . "- " . $v->{"$comp.fruState"};
        $bg = ($status eq "ready- enabled")? "":"bgcolor=#FFD0D0";
        $id = $v->{"$comp.fruVendor"} . "/ " . $v->{"$comp.fruModel"};
        $rev = $v->{"$comp.fruRevision"};
        my $pathstat;
        if (exists($v->{"$comp.pathstat"}) && $v->{"$comp.pathstat"} ne "OK") {
          $pathstat = " <font color=red>pathstat:" . $v->{"$comp.pathstat"}
        }
        $out .=  
            "<tr><td><center>Disk".
            "<td><small>$id".
            "<td>u${u}d$d</td>";
        $out .= $rev ? "<td $bg>$status$pathstat</td><td>$rev&nbsp;</td>\n":
                      "<td $bg colspan=2>$status$pathstat</td>\n";

        if ($v->{"$comp.fruDiskRole"} =~ /andbyDisk/) { 
          push(@spares, $comp);
        }
        $cnt++;
     }
     $id =  $v->{"controller.u${u}ctr.fruVendor"} . "/ " .
            $v->{"controller.u${u}ctr.fruModel"};

     $status = $v->{"controller.u${u}ctr.fruStatus"} . "- ".
               $v->{"controller.u${u}ctr.fruState"};
     $bg = ($status eq "ready- enabled")? "":"bgcolor=#FFD0D0";
     $rev = $v->{"controller.u${u}ctr.fruCtlrVersion"} || 
            $v->{"controller.u${u}ctr.fruRevision"};
     $out .= "\n<tr><td><center>Cntrl<td><small>$id<td>u$u.ctrl<td $bg>$status<td>$rev&nbsp;</td>";

     $id = $v->{"midplane.u${u}mpn.fruVendor"} . "/ " . $v->{"midplane.u${u}mpn.fruSerialNo"};
     $status = $v->{"midplane.u${u}mpn.fruStatus"} . "- " . $v->{"midplane.u${u}mpn.fruState"};
     $bg = ($status eq "ready- enabled")? "":"bgcolor=#FFD0D0";
     $rev = $v->{"midplane.u${u}mpn.fruRevision"};
     $out .= "\n<tr><td><center>Midplane<td><small>$id&nbsp;<td>u$u.mpn<td $bg>$status<td>$rev&nbsp;</td>";

    for ($d=1; $d <= 2; $d++) {
        $comp = "loopcard.u${u}l$d";
        $status = $v->{"$comp.fruStatus"} . "- " . $v->{"$comp.fruState"};
        $bg = ($status eq "ready- enabled")? "":"bgcolor=#FFD0D0";
        $id = $v->{"$comp.fruSerialNo"};
        $rev = $v->{"$comp.fruLoopVersion"} || $v->{"$comp.fruRevision"};
        $out .= "<tr><td><center>Loop<td><small>$id&nbsp;<td>u${u}l$d</td>";
        $out .= "<td $bg>$status</td><td>$rev&nbsp;</td>\n";

        my($c);
        for ($c=1; $c <= 2; $c++) {
          my $cable = "Cable1:" . $v->{"$comp.fruLoopCable${c}State"};
          my $bg2   = ($cable =~ /not/) ? "bgcolor=#FFE0E0" : "";
          $out .= "<tr><td><center>Loop</td><td>Cable</td>
                     <td>u${u}l$d (Cable-$c)</td><td $bg2>$cable</td><td>&nbsp;</td>\n";
        }
        $comp = "loop.u${u}lp$d";
        $status = $v->{"$comp.loopStatus"};
        my $split  = $v->{"$comp.loopIsIsolated"};
    }
    for ($d=1; $d<= 1; $d++) {
        $comp = "port.u${u}p$d";
        $status = $v->{"$comp.portStatus"};
        $bg = ($status eq "online")? "":"bgcolor=#FFD0D0";
        $id  = $v->{"$comp.portWWN"};
        $rev = $v->{"$comp.fruRevision"};
        $type = $v->{"$comp.portType"};
        $out .= "<tr><td><center>Port<td><small>$id&nbsp;<td>u${u}p$d</td><td $bg>$status&nbsp;</td><td>$rev&nbsp;</td>\n";
     }

     for ($d=1; $d<= 2; $d++) {
        $comp = "power.u${u}pcu$d";
        $status = $v->{"$comp.fruStatus"} . "- " . $v->{"$comp.fruState"};
        $bg = ($status eq "ready- enabled")? "":"bgcolor=#FFD0D0";
        $id = lc($v->{"$comp.fruVendor"}) . "- " . $v->{"$comp.fruModel"};
        $bat = $v->{"$comp.fruPowerBatState"};
	my $life = $v->{"$comp.fruPowerBatLife"};
	my $used = $v->{"$comp.fruPowerBatUsed"};
	my $err2;
	my $exp = $life - $used;
        if ($life) {
	  if ($exp < 0) {
	    $err2 = "<font color=red>$comp battery expired " . (-$exp) . 
	          " days ago.";
          } elsif ($exp < 30) {
	    $err2 = "<font color=red>$comp battery will expire in " . ($life-$used) . 
	          " days.";
          }
        }
        $fans = "fan1:" . $v->{"$comp.fruPowerFan1State"} . ", fan2:" . $v->{"$comp.fruPowerFan2State"};
        $rev = $v->{"$comp.fruRevision"};
        $out .= "<tr><td><center>Power<td><small>$id<td>u${u}pcu$d<td $bg>$status</td><td>$rev&nbsp;</td>\n";
        my $bg1 = "$bat, $fans, $err2";
        $bg1 =~ s/fault/<font color=red>fault<\/font>/g;
        $out .= "<tr><td colspan=1>&nbsp;<td colspan=4>Battery:$bg1</td>\n";

        $out .= "<tr bgcolor=#FFFFE0><td colspan=5><b>Volumes</td></tr>";
     for ($d=1; $d<= 2; $d++) {
        $comp = "volume.u${u}vol$d";
        $status = $v->{"$comp.volStatus"};
        $name   = $v->{"$comp.volName"};
        next if (!$name && !$status );
        next if ($status eq "deleted");
        $out .= "<tr><td colspan=5><b>Volume $name</td></tr>";
        my $mode = $v->{"$comp.volCacheMode"};
        my $st2 = $v->{"$comp.volOper"} . " " . $v->{"$comp.volOperProgress"}
                 . "%" if ($v->{"$comp.volOperProgress"});
        my $raid =  $v->{"$comp.volRaidLevel"};
        $raid = "($raid)" if ($raid);
        my $cap  =  sprintf("%.3f", ($v->{"$comp.volCapacity"} / 1024));
        $out .= "<tr><td>$name<td>$raid $mode<td>u${u}vol$d</td>".
                "<td>$status $st2<td align=right>$cap GB</td></tr>";

        for (my $disk =0; $disk<=13; $disk++){
          my ($vlsss, $cmp) = split(/volume./, $comp);
          my $diskName = $v->{"volume_disk.$cmp-d-$disk.volDiskFruId"};
          next if (!$diskName);
          my $diskRole = "";
          if ($v->{"volume_disk.$cmp-d-$disk.volDiskIsRoleStandby"} eq "no"){
            $diskRole = "Data" 
          }elsif (
              $v->{"volume_disk.$cmp-d-$disk.volDiskIsRoleStandby"} eq "yes"){
              $diskRole = "Standby";
          }
          $diskRole = "GlobalStandby" if($v->{"$diskName.glbSubstitutedDrv"});
          my $Status = $v->{"volume_disk.$cmp-d-$disk.volDiskStatus"} ."- ".
                       $v->{"volume_disk.$cmp-d-$disk.volDiskState"};
          $out.="<tr><td></td><td><td>$diskName<td>$Status<td>$diskRole";
        }
     }
     }
  }
  my $done;
  my $lun;
  for ($d=0; $d < 256; $d++) {
     $comp = "slice.$d";
     $name = $v->{"$comp.volSliceName"};
     last if (!$name);
     if (!$done) {
         $out .= "<tr bgcolor=#FFFFE0><td colspan=5><b>Slices";
         $out .= "<tr bgcolor=$LIGHT><th>&nbsp;<th>Name<th>Volume<th colspan=2>Size<br></td>";
         $done = 1;
     }
     my $vol  = $v->{"$comp.volId"};
     my $size;
     $size = $v->{"$comp.volSliceSize"};
     $size = sprintf("%.3f", $size / 1024/1024/2);
     $out .= "<tr><td>slice $d<td>$name<td>$vol
             <td colspan=2 align=right nowrap>$size GB</td>";
  }
  #$out .= "<tr><td td colspan=5><small>Note: Slice refers to the portion of the Volume that the host will see as a LUN. Volumes are also referred to as Pools.</td>";

  if (!$tableCnt) {
    $out .= "</table>&nbsp;<p>";
  }
  return $out ;

}


# DISK LINKFAIL LOSSSYNC LOSSSIG PROTOERR INVTXWORD INVCRC
# --------------------------------------------------------
# u1d1  1        13       0       0        145       0       
#      $lnk,    $sync,   $signal, $seq,   $word,    $crc)
#       link, signal, seq, crc, sync, word
#

sub FCCounters_NU {
  my($class ,$arg) = @_;
  my(%ENC, $disks, $in, $l, $node, %WWN, @HBA, $num, $x, %ALPA, %MPX);

  my($confDevs) = $arg->{devs}; 
  my($report) = 1 if ($arg->{report});
  my($encs)   = $arg->{encs};
  my($renv) = System->get_renv();
  my($TO) = $renv->{'timeout.luxadm'};

  if ($renv->{skip_luxadm}) {
    return { data => {},  hba => {} };
  }
  if ($renv->{solution} eq "se2") {
    return { data => {},  hba => {} };
  }

  my($lux) = "/usr/sbin/luxadm";
  my($err,$probe);
  
#
# Find ENCLOSURES AND PATHS with PROBE
#
  $err = $class->readT3IB(0, \%ENC, \@HBA, \%MPX);

  if (defined($encs)) {
    %ENC = ();
    my(@a) = split(/,/, $encs);
    foreach my $enc (@a) {
        $ENC{$enc}  = 1;
    }
  }
  
  if (1) {

  foreach my $wwn (keys %ENC) {
    $WWN{$wwn}{enc} = $wwn ; # $ENC{$wwn};
    if ($confDevs) {
       foreach my $d (@$confDevs) {
          if ($d->{type} eq "t3" && 
               lc(substr($d->{wwn},-4)) eq lc(substr($wwn,-4))) {
               $WWN{$wwn}{enc} = $d->{name};
          }
       }
    } 
  }
  }

#
# FOR EACH HBA, GET COUNTERS 
#
  my($map, $err2, $rdls, @c, $hba);
  for ($x=0; $x <= $#HBA; $x++ ) {
     $hba = $HBA[$x];
     next if ($hba =~ /SUNW,socal/);
     Debug->print2("FC-T3 : reading hba $x ($hba)");
     my($err,$map) = Util->run_command("$lux -e dump_map $hba","", $TO, {cache => 0});
     my($last) = substr($hba,-1);
     my($err2,$rdls) = Util->run_command("$lux -e rdls $hba","", $TO, {cache => 0});
     $in =0;
     my($fabric) = 0;
     foreach $l (@$map) {
        next if ($l =~ /^ *$/);
        if ($l =~ /^Pos/) {
            $in = 1;
            $fabric = 1 if ($l =~ /Port_ID/);
        } elsif ($in) {
          my($n,$alpa,$id,$hard,$port,$node,$type,$desc);
          if ($fabric) {
            ($n,$alpa,$hard,$port,$node,$type,$desc) = split(/\s+/, $l,7) ;
          } else {
            ($n,$alpa,$id,$hard,$port,$node,$type,$desc) = split(/\s+/, $l,8) ;
          }
          $alpa = sprintf("%02s", $alpa);
          $ALPA{$x}{$alpa}{port} = $port; 
          $ALPA{$x}{$alpa}{node} = $node;  # wwn
          $ALPA{$x}{$alpa}{type} = $type;
          @c = split(/ /, substr($desc,1));
          $ALPA{$x}{$alpa}{desc} = $c[0];
        }
     }
     $in =0; 
     foreach $l (@$rdls) {
        next if ($l =~ /^ *$/);
        if ($l =~ /^al_pa/) {
           $in = 1;
        } elsif ($l =~ /^NOTE:/) {
           $in = 0;
        } elsif ($in) {
           my($alpa,$lnk,$sync,$signal,$seq,$word,$crc) = split(/\s+/, $l);
           $alpa = sprintf("%02s", $alpa);
           $ALPA{$x}{$alpa}{link}   = $lnk;
           $ALPA{$x}{$alpa}{sync}   = $sync;
           $ALPA{$x}{$alpa}{signal} = $signal;
           $ALPA{$x}{$alpa}{seq}    = $seq;
           $ALPA{$x}{$alpa}{word}   = $word;
           $ALPA{$x}{$alpa}{crc}    = $crc;
        }
     }
     
  }
  my($out, @X, %SUMM,  $form0, $form, $form2, $al, $hba0);
  my(%X, %H1);
  my(%ses_cnt) = 0;
  if (!$report) {
    for ($x=0; $x <= $#HBA; $x++ ) {
       my($hb) = $HBA[$x];
       $hb = substr($hb, 0, -7) if (substr($hb, -7) eq ":devctl");
       $H1{$x} = $hb;
    }
  } 
  for ($x=0; $x <= $#HBA; $x++ ) {
     $hba = $hba0 = $HBA[$x];
     $hba =~ s/\/devices\///;
     if ($report) {
       @X = ();
       $out .= "\nHBA ($x): $hba \n";
       $form0 = "%-5.5s %-10.10s %-10.10s %-4.4s %-16.16s %-12.12s %-6.6s";
       $form  = "$form0 %6s %6s %6s %6s %6s %6s\n";
       $form2 = "$form0 %6d %6d %6d %6d %6d %6d\n";
       $out .= sprintf($form, "Path",
              "Enclosure","Disk","Alpa","Wwn","Logical","Desc","Link","Signal","Seq","Crc",
              "Sync","Word");
       $out .= "-" x 111 . "\n";
     }
     my($y) = $ALPA{$x};

     my($al2, $wwn, $enc, $disk, $device);
     my($hbadone)= 0;
     foreach $al (reverse sort keys %$y) {  # read each element on this hba(ctrl/hba)
         $al2 = $y->{$al};
         $wwn  = $al2->{node};
         if ($WWN{$wwn}{enc}) {
             $disk = "port.0";
             $enc = $WWN{$wwn}{enc};
         } else {
             next if ($hbadone);
             $hbadone = 1;
             #$enc = "hba";
             $disk = "hba";
         }
         my($path) = $x;
         if ($report) {
           my($enc0) = $enc;
           $enc0 = substr($enc0,-10) if (length($enc0) > 10) ;
           push(@X, sprintf($form2, $path, $enc0, $disk, $al, $wwn, $device, $al2->{desc},
                        $al2->{link}, $al2->{signal}, $al2->{seq}, $al2->{crc},$al2->{sync},$al2->{word}));
         } else {
           my($key) = "$path|$enc|$disk|t3";
           $X{$key} = "$al2->{link}\t$al2->{signal}\t$al2->{seq}\t$al2->{crc}\t$al2->{sync}\t$al2->{word}";
         }
     }
     if ($report) {
       foreach $l (sort @X) {
         $out .= $l;
       }
     }
  }
  $err = undef;


  if (!$report) {
    return { data => \%X,  hba => \%H1 };
  } else {
    return $out;
  }
}

#  1         2         3       4         5       6
# LINKFAIL LOSSSYNC LOSSSIG PROTOERR INVTXWORD INVCRC
# link, sig, seq, crc, sync, word
#  1     3    4    6    2     5
#  ($sys_err, $hash, .command_error)

sub FCCountersOOB {
  my($class, $dev) = @_;
  my(@L, @l, %X, %W);
  my($err, $key);
  my($unit, $path, $l, $disk, $enc, $sim);

require "Net/Telnet.pm";

  if (!$dev->{telnet}) {
    return ("No telnet password",{});
  } elsif ($dev->{type} ne "t3") {
    return ("$dev->{name} not a T3", {} );
  }
  
  my($ip) = $dev->{ip};
  my($pass) = System->getDevicePassword($dev, "array");
  my $diskTotal = $class->diskTotal();
  my($t) = new Net::Telnet (
             errmode => "return",
             Timeout   => 30,
             Prompt    => '/<\d+\>/',
             );
  if (!defined($t->open($ip))) {
      return ("Cannot open $ip", {});
  }
  $t->login("root", $pass);
  if ($t->errmsg()) {
     return ($t->errmsg(), {});
  }


 if (0) {
  @l = $t->cmd("port list");
  foreach my $port (@l) {
     my(@b) = split(/\s+/, $port);
     $W{substr($b[0],0,2)} = $b[5];
  }
 }
  foreach $path ('0','1') {
    foreach $unit ('1','2') {
       @l = $t->cmd(".disk linkstat u${unit}d1-$diskTotal path $path");
       foreach $l (@l) {
         if ($l =~ /Failed/) {
            $err .= $l;
         } elsif ($l =~ /^u\dd\d/) {
           my(@a) = split(/\s+/, $l);
           $disk = $a[0];
           $key = "$path|$dev->{ipno}|$disk|t3";
           $X{$key} =  "$a[1]\t$a[3]\t$a[4]\t$a[6]\t$a[2]\t$a[5]";
         }
       }
    }
  }

  foreach $sim ('0','1', '2') {
      @l = $t->cmd(".sim -f num $sim linkstat");
      foreach $l (@l) {
        if ($l =~ /Error/) {
            $err .= "sim$sim: $l";
        } elsif ($l =~ /^\d+\s+\d+/) {
          my(@a) = split(/\s+/, $l);
          $key = "|$dev->{ip}|sim$sim|t3";
          $X{$key} =  "$a[0]\t$a[2]\t$a[3]\t$a[5]\t$a[1]\t$a[4]";
        }
     }
  }

  $t->close;
  return (undef,  \%X, $err);
}


sub inbandProbe {
  my($class) = @_;

  my($lux) = "/usr/sbin/luxadm";

  my($err,$probe) = Util->run_command("$lux probe -p", "luxprobe.txt", 40);
  my($in, $l, @list, $disks);
  my($hbalist, $t3, $ix);
  for ($ix=0; $ix <= $#$probe; $ix++) {
      $l = $probe->[$ix];
      if ($l =~ /Found/) {
        $in = 1;
      } elsif ($in) {
        if ($l =~ / Node WWN:(.+)\s+Device Type:Disk/) { # t3 or internal..
          my($wwn) = Util->trim($1);
          ($err, $disks) = Util->run_command("$lux display $wwn","lux",40);
          my($rep) = "@$disks";
          if ($rep =~ /T300/) {
             push(@list, $wwn);
          }
        }
      }
  }
  return @list;
}

#
# T3 InBand
# flag == 1 : only look for mpxio path 
# like  Logical Path:/dev/rdsk/c14t60020F2000003EE53AAF7A09000DA257d0s2

sub readT3IB {
  my($agent, $cache1, $ENC, $HBA, $MPX, $flag) = @_;
  my($err, $probe, $disks, $mpx_wwn, $mpx_class);

  my($renv) = System->get_renv();
  my($TO) = $renv->{'timeout.luxadm'};

  my($lux) = "/usr/sbin/luxadm";
  ($err,$probe) = Util->run_command("$lux probe -p","", $TO);

  return $err if ($err);
  my($in, $l);
  my($hbalist, $t3, $ix, $h2, $h3);
  for ($ix=0; $ix <= $#$probe; $ix++) {
      $l = $probe->[$ix];
      if ($l =~ /Found/) {
        $in = 1;
      } elsif ($in) {
        if ($l =~ / Node WWN:(.+)\s+Device Type:Disk/) { # t3 or internal..
          if ($flag) {
             next if ($probe->[$ix+3] !~ /scsi_v/);
          }
          my($wwn) = Util->trim($1); 
          ($err, $disks) = Util->run_command("$lux display $wwn","", $TO);
          my($rep) = "@$disks";
          if ($rep =~ /T300/) {
             $rep =~ /Serial Num:\s+(\d+)/;
             my($name) = $1;
             foreach my $el ('A','B') {
               if ($rep =~ /WWN\(Port $el\):\s+(\w+)/) {
                  my $w = $1;
                  substr($w,7,1) = "0" if (length($w) > 7);
                  $ENC->{$w} = $name;
               }
             }

             while ($probe->[$ix+1] =~ /Logical Path:(.*)/) {
               my($hba) = Util->trim($probe->[$ix+3]);
               if ($hba !~ /devices\/scsi_/) {
                    my($ii) = rindex($hba, "/");
                    $hba = substr($hba, 0 , $ii);
                    if (index($hbalist, "$hba,") < 0) {
                      push(@$HBA, $hba);
                      $hbalist .= "$hba,";
                    }
               } else {
                   $h3 = $h2 = $mpx_wwn = $mpx_class = "";
                   foreach my $ll (@$disks) {
                      if ($ll =~ /^\s+Controller\s+(.*)/) {
                         $h3 = $1;
                         $h2 = $1 . ":devctl";
                         if (index($hbalist, "$h2,") < 0) {
                            push(@$HBA, $h2);
                            $hbalist .= "$h2,";
                         }
                      } elsif ($ll =~ /Device Address\s+([0-9a-f]+)/) {
                         $mpx_wwn = $1;
                      } elsif ($ll =~ /Class\s+(.*)/) {
                         $mpx_class = $1;
                      } elsif ($ll =~ /State\s+(.*)/) {
                         $MPX->{$mpx_wwn} = "$1\t$h3" if ($mpx_class eq "primary");
                      }
                   }
           
               }
               $ix += 3;
             }
          }
        }
     }
  }
  return ;
}


1;
