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

#  $Name: bld61_002 $ 
#  $Id: 
use strict;
use Agent;
use base 'Agent';
use Revision;

use Matrix;
use System;
use Util;

sub isSelectable {"Sun 3310"}

sub revision {'$Revision: 1.70 $'}

sub category {Report::CAT_3310}


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

  bless ($self, 'Agent::3310');
  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);
  my($xml, @lux_dif, $dif, $ev, $found);
  my($log_err, $lines, $timelapse);
  my(%DONE, $report, $device, $key, $id, $x, $l, $connect_errs);
  my @test_err;
  # $DB::single=1;
  my $renv = System->get_renv();
  my $type = $agent->category();
  my $ping_to  = $renv->{'timeout.ping'} || 10;

  my $today = Util->today("YMDH");
  my $processed_list = [];
  my($dc) = 0;
  # print "In 3310 RUN\n";
  foreach $device ( $agent->deviceList($static_list)) {
    # print "IN loop\n";
    my $pre_check_status =1;
    my $retry = 0;
    my $new_rep;
    next if ($device->{active} eq "N");
    $dc++;
    Debug->print1("-> " . uc($device->{type}) . ": Reading device $device->{name}/$device->{wwn}/$device->{path}");
    my $problems = PDM->get_last_event();

    if (($device->{path} =~ /dev/) ||
        ($device->{ipno} && Util->ping2($device->{ipno}, $ping_to))
       ) {
      my($err, $lines) = $agent->readLog($device, $report);
      my($warn_lines)  = $agent->find_alert($lines, $device);
      if ($warn_lines) {
        $id = {
              deviceName  =>  $device->{key},
              active      => "Y",
              display     => $device->{name},
              name        => $device->{name},
              class       => "storage.$type",  # Report::CAT_3310MESSAGE
              category    => $type . "message",
              ip          => "",
           };
        $report->{"id.name"} = $device->{name};
        
        my $new_rep = Report->new($id, $report , $warn_lines, Report::STATUS_OK);
        require Health::Message;
        Health::Message->all_logic($new_rep);
        PDM->saveReport( $new_rep );
      }
    }

    while (($pre_check_status == 1) && ($retry < 3)) {
      ($key, $connect_errs, $report) = $agent->INSTRUMENTATION($device);
      if ($connect_errs eq "BUSY") {
          Debug->print2("Device $device->{name} is busy, skipping!");
          next;
      }

      $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"} = System->get_ipno();

      $report->{"id.key"}  = $device->{key};
      #$report->{"id.wwn"}  = "206000c0ff" . $device->{key};

      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);
      }

      require Health::3310;
      $pre_check_status = Health::3310->pre_check($new_rep);
      $retry++;    
      sleep(10*$retry);
     }

     require Health::3310;
     Health::3310->all_logic($new_rep); # create event
     PDM->saveReport($new_rep);

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

  } 
  return $processed_list;
}


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

use vars qw ($ERR);


#
#  Fast: ONLY CALLS sccli show ip-address and show port-wwn
#
#  This function is used by lib/Discover/3310.pm for 3310 and 3510 discovery
#
#  input $name is ip address: 172.20.32.166
#  input $host can be null  

sub getFastWWN { 
  
  my($class, $name, $host) = @_;
  my($wwn);
  $ERR = undef;
  my($renv) = System->get_renv();
  my($TO) = $renv->{'timeout.sccli'} || 400;


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


sub getWWN {
  my($agent, $name, $host) = @_;
  my($wwn);
  $ERR = undef;
  my($renv) = System->get_renv();
  my($TO) = $renv->{'timeout.sccli'} || 400;


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

sub FCfromDevice {
    my($class, $device, $report) = @_;
    my(%SW, $x , %X, $err);

    my $R = {};

$DB::single=1;
    $err = $class->getTotalFC($device, $R, $report);
    return {} if ($err);
    
    for ($x=1; $x < 7; $x++) {  # reconstruct the key for chan error
        my $key = "|$device->{key}|port.$x|$device->{type}";
        $X{$key} = $R->{"channel.$x.error.link"}   . "\t" .
                      $R->{"channel.$x.error.signal"} . "\t" .
                      $R->{"channel.$x.error.seq"}    . "\t" .
                      $R->{"channel.$x.error.crc"}    . "\t" .
                      $R->{"channel.$x.error.sync"}   . "\t" .
                      $R->{"channel.$x.error.itw"};
    }

    
    return { data => \%X,  hba => {} };
 


}

####################################################################
#getTotalFC():   get all the error counters including disk and chan 
####################################################################

sub getTotalFC {
  my($class, $device, $rep, $report) = @_;
 
  # $rep here is not the final report, 
  # it needs to be dumped into real report,
  # which is finished by calling function.

  # $report this is the real report, that is why disk data can be directly dumped in
 
  my($renv) = System->get_renv();
  my($TO) = $renv->{'timeout.sccli'} || 400;


  my $sccli = &sccli_path();
  my $w = $device->{path} || $device->{ipno};
  my($err, $com, $chan, $chan0);
  my $chan_type;

  for ($chan0=0; $chan0 < 6; $chan0++) {
    ($err,$com) = Util->run_command("$sccli $w diag error -chan $chan0 -target all", "chan.txt" , $TO);
    return $err if ($err);
    my $chan = $chan0 + 1;

    $class->parseChanFC($com, $rep, "channel.$chan.error");

    #check the chan type
    
    $chan_type = $class->getChanType($device,$chan0);
    
    if ($chan_type == "Drive") { # only drive chan can show us the disk statistic data 
       $class->parseDiskFC($com, $report, $chan); # pass $chan in to construct key
    }

  }
  return undef;
}

sub parseChanFC {
   my($class, $com, $rep, $init) = @_;
   my ($tem, $chan, $id, $type, $lip, $link, $sync, $sig, $primi, $txw, $crc);
   foreach my $el (@$com) {
      if ( ( $el =~ /HOST/ ) || ( $el =~ /RAID/ )) {
         ($tem, $chan, $id, $type, $lip, $link, $sync, $sig, $primi, $txw, $crc) = split(/\s+/,$el);
         $rep->{"$init.lip"}    = $lip;
         $rep->{"$init.link"}   = $link;
         $rep->{"$init.sync"}   = $sync;
         $rep->{"$init.signal"} = $sig;
         $rep->{"$init.seq"}    = $primi;
         # $rep->{"$init.itw"}    = $txw;
         $rep->{"$init.crc"}    = $crc;
         $rep->{"$init.chan"}   = $chan;
         $rep->{"$init.target"} = $id;
      } 
   }

}

sub parseDiskFC {
   my($class, $com, $rep, $chan) = @_;
   my ($tem, $chan, $id, $type, $lip, $link, $sync, $sig, $primi, $txw, $crc);
   my $init;
   foreach my $el (@$com) {
      if  ( $el =~ /DISK/ ) {
          ($tem, $chan, $id, $type, $lip, $link, $sync, $sig, $primi, $txw, $crc) = split(/\s+/,$el);
          $init = "components.disk.$id.error.chan_$chan"; # use target id
          # $init = "components.disk.$disk_idx.error.chan_$chan";
          $rep->{"$init.lip"}    = $lip;
          $rep->{"$init.link"}   = $link;
          $rep->{"$init.sync"}   = $sync;
          $rep->{"$init.signal"} = $sig;
          $rep->{"$init.seq"}    = $primi;
          $rep->{"$init.itw"}    = $txw;
          $rep->{"$init.crc"}    = $crc;
          $rep->{"$init.target"} = $id;
      }
   }

}


sub get_drv_chan {
   my ($class, $device) = @_;
   my($err, $com);
   my @list;
 
   my($renv) = System->get_renv();
   my($TO) = $renv->{'timeout.sccli'} || 400;
   my $sccli = &sccli_path();

   ($err,$com) = Util->run_command("$sccli $device show chan", "drv_chan.txt" , $TO);
   foreach my $el (@$com) {
        next if ($el!~ /Drive/);
        my $l = Util->trim($el);
        my ($ch_id, $tem) = split(/\s+/,$l);
        push(@list,$ch_id);
   } 
   return \@list;
}

sub getChanType {
   my ($class, $device, $chan) = @_;
   my($err, $com);
   my @list;

   my $chan_type;
   my $w = $device->{path} || $device->{ipno};

   my($renv) = System->get_renv();
   my($TO) = $renv->{'timeout.sccli'} || 400;
   my $sccli = &sccli_path();

   ($err,$com) = Util->run_command("$sccli $w show chan", "drv_chan.txt" , $TO);
   foreach my $el (@$com) {
        my $l = Util->trim($el);
        my ($ch_id, $chan_type, $tem) = split(/\s+/,$l);
        if ( $ch_id == $chan ) {
             return $chan_type;
        }
   }
}


sub getFC {
  my($class, $device, $rep) = @_;
  
  my($renv) = System->get_renv();
  my($TO) = $renv->{'timeout.sccli'} || 400;


  my $sccli = &sccli_path();
  my $w = $device->{path} || $device->{ipno};
  my($err, $com, $chan, $chan0);
  for ($chan0=0; $chan0 < 6; $chan0++) {
    ($err,$com) = Util->run_command("$sccli $w diag error -chan $chan0", "chan.txt" , $TO);
    return $err if ($err);
    my $chan = $chan0 + 1;
    $class->parseFC($com, $rep, "channel.$chan.error");
  }
  return undef;
}

sub parseFC {
  my($class, $com, $rep, $init) = @_;

  foreach my $el (@$com) {
     last if ($el =~ /Times of/);
      my($el, $v) = split(/\: /, $el, 2);
      if ($el =~ /LIP/){
         $rep->{"$init.lip"} = $v;

      } elsif ($el =~ /Link/) {
         $rep->{"$init.link"} = $v;

      } elsif ($el =~ /Sync/) {
         $rep->{"$init.sync"} = $v;

      } elsif ($el =~ /Signal/) {
         $rep->{"$init.signal"} = $v;

      } elsif ($el =~ /Primitive/) {
         $rep->{"$init.seq"} = $v;

      } elsif ($el =~ /Transmission/) {
         $rep->{"$init.itw"} = $v;

      } elsif ($el =~ /CRC/) {
         $rep->{"$init.crc"} = $v;
      }
  }
}

sub sccli_path {
  return System->get_home() . "/bin/sccli";
}

###########################################
# return undef if PASS or UPREV
# return DOWNREV info if down rev
###########################################

sub check_rev {
    my ($q) = @_;


    my $env = { HBA => {SOCAL => 1, IFP => 1, USOC => 1,  PCI => 1, QLC => 1}};
    my $base = System->get_home() . "/System/BaseMatrix/Config-base";
    my $matrix = Matrix->read($base, $env);
    my $installedp = Revision->readInstalledPatches();
    my $info = &quickCheck($matrix, $installedp,$q);
    if (!$info ) {
        return undef;  # ok, no error
    } else {
        return $info;
    }  
    
}

sub quickCheck {
    my ( $matrix, $install, $q) = @_;
    my $firm_rev;
    my $info;
    my $type;
    my @R;
    my $ptr;

    my $sccli = System->get_home() . "/bin/sccli";
    # first get the device type 
    my $dev = $q->{name};
    # my($err,$com) = Util->run_command("$sccli $dev show ip-address 2>&1", "tem.txt" , 60);  
    # show ip-address does not work for 3120 and 3310 D
    my($err,$com) = Util->run_command("$sccli $dev show inquiry 2>&1","tem.txt",60);
    if($err){
      $info  = "Error on sccli, $err";
      return $info;
    } else {
      if ("@$com" =~ /\[SUN StorEdge (\w+) SN#(\w+)\]/) {
            $type = $1;
      }
    } 

     my($err,$info) = Util->run_command("$sccli $dev show inquiry 2>&1", "tem.txt" , 60);

     foreach my $el (@$com) {
            

            if (($type =~ /3120JBOD/i) || ($type =~ /3310JBOD/i)
               || ($type =~ /3510/) || ($type =~ /3511/) ) { 
               if ($el =~ /Revision/) {
                 $el = Util->ltrim($el);
                 my $tem1;
                 ($tem1, $firm_rev) = split(/\:/,$el); 
                 $firm_rev = Util->ltrim($firm_rev);
              }  
            } else {  # 3310 case, Revision field does not work for this device for IB discovery
              if ($el =~ /NVRAM/i) {  # NVRAM Defaults: 325S 3310 v1.37
                 $el = Util->ltrim($el);
                 my ($tem1, $tem2, $tem4, $tem5);
                 ($tem1,$tem2, $firm_rev, $tem4, $tem5) = split(/\s+/,$el); 
              }
           }

     }

     my $mat = $matrix->{$type};
     if ($type =~ /3310jbod/) {
        require Revision::3310JBOD;
        push(@R, Revision::3310JBOD->check_firmware($type."_Controller_FW",undef, $firm_rev, $q, $mat, "firmrev"));
        $ptr=\@R;
        if (($ptr->[0]->[2] =~ /PASS/ ) || ($ptr->[0]->[2] =~ /UPREV/)) {
          return undef;  #ok
        } else {
          return "Un-supported firmware for this ".$type." device. Please update firmware, then try again";
        }
     } elsif ( $type =~ /3310/) {
        require Revision::3310;
     
        push(@R, Revision::3310->check_firmware($type."_Controller_FW",undef, $firm_rev, $q, $mat, "firmrev"));
        $ptr=\@R;
        if (($ptr->[0]->[2] =~ /PASS/ ) || ($ptr->[0]->[2] =~ /UPREV/)) {
          return undef;  #ok
        } else {
          return "Un-supported firmware for this ".$type." device. Please update firmware, then try again"; 
        } 
     } elsif ($type =~ /3510/)  {
        require Revision::3510;

        push(@R, Revision::3510->check_firmware($type."_Controller_FW",undef, $firm_rev, $q, $mat, "firmrev"));
        $ptr=\@R;
        if (($ptr->[0]->[2] =~ /PASS/ ) || ($ptr->[0]->[2]=~ /UPREV/)) {
          return undef;  #ok
        } else {
           return "Un-supported firmware for this ".$type." device. Please update firmware, then try again";
        }
     } elsif ($type =~ /3511/)  {
        require Revision::3511;

        push(@R, Revision::3511->check_firmware($type."_Controller_FW",undef, $firm_rev, $q, $mat, "firmrev"));
        $ptr=\@R;
        if (($ptr->[0]->[2] =~ /PASS/ ) || ($ptr->[0]->[2]=~ /UPREV/)) {
          return undef;  #ok
        } else {
           return "Un-supported firmware for this ".$type." device. Please update firmware, then try again";
        }
     } elsif ($type =~ /3120jbod/) {
        require Revision::3120JBOD;
        push(@R, Revision::3120JBOD->check_firmware($type."_Controller_FW",undef, $firm_rev, $q, $mat, "firmrev"));
        $ptr=\@R;
         if (($ptr->[0]->[2] =~ /PASS/ ) || ($ptr->[0]->[2]=~ /UPREV/)) {
          return undef;  #ok
        } else {
           return "Un-supported firmware for this ".$type." device. Please update firmware, then try again";
        }
    }
}


sub get_WWNF {  # fast
  my($q) = @_;
  my $report = {};
  my $rc = {};
  if ( ($q->{name} =~ /dev/) || Util->ping($q->{name}, 10) ){
    my $sccli = System->get_home() . "/bin/sccli";

    my $rev_err = &check_rev($q); 

    if ($rev_err) {
        $rc->{error} = $rev_err;
        return ($rc); 
    }
     my($err,$com) = Util->run_command("$sccli $q->{name} show inquiry 2>&1","luxadm.txt" , 30);

    if($err){
       $rc->{error} = "Error on sccli, $err";
    } else {  
       if ("@$com" =~ /\[SUN StorEdge (\w+) SN#(\w+)\]/) {
          my $type = $1;
          my $sn   = lc($2);
          #$rc->{key}      = $sn;
          $rc->{wwn}      = "206000c0ff$sn"; 
          $rc->{userLabel} = $type;
          $type           = "3310" if ($type =~ /N/);

	  my $simpleType  = $type;
	  $simpleType     = "3310" if ($type =~ /3310/);
          $simpleType     = "3510" if ($type =~ /3510/);
          $simpleType     = "3511" if ($type =~ /3511/);
          $rc->{type}     = $simpleType;
          $rc->{key}      = "SUN\." . $simpleType . "\." . $sn;

          $rc->{error}    = $err;
          $rc->{units}    = 1;
          $rc->{ip}       = $report->{'info.ip'};
          my(%HASH);
          foreach my $line (@$com) {
            my($name, $val) = split(/\s*\:\s*/, $line);
            $HASH{Util->trim($name)} = $val;
          }
          $rc->{ipno}     = $HASH{'IP Address'};

          if (($type =~ /3510/) || ($type =~ /3511/)) { 
            my($err, $wwns) = Agent::3310->getPortWWNS({ipno => $q->{name}} , $report);
            $rc->{wwn2} = $wwns;
            Agent::3310->get_wwns($report, $rc);
           }
           $rc->{report}   = $report;
          
       } elsif ("@$com" =~ /\[SUN StorEdge (\w+)  (\w+) SN#(\w+)\]/) {
          my $type0       = $1;
          my $type        = $type0 . "jbod";
          my $sn          = lc($3);
          $rc->{key}      = "SUN.$type.$sn";
          $rc->{wwn}      = "206000c0ff$sn";
          $type           = "3310jbod" if ($type =~ /N/); 
          $rc->{type}     = $type || "3310jbod";
          $rc->{userLabel}= $type;
          $rc->{error}    = $err;
          $rc->{units}    = 1;
          $rc->{report}   = $report;
       }  
    }
  } else {
    $rc->{error} = "Cannot ping $q->{name}";
  }

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

}


sub get_WWN {
  my($q) = @_;
  my $report = {};
  my $rc = {};
  if ( ($q->{name} =~ /dev/) || Util->ping($q->{name}, 10) ){
    my $err = Agent::3310->getConfig({ipno => $q->{name} }, $report);
    if($err){
       $rc->{error} = "Error on getConfig, $err";
    }else{  
       my $sn = sprintf("%6.6x", hex($report->{'info.unique_id'}));
       #$rc->{key}      = $sn if ($report->{'info.unique_id'});
       $rc->{wwn}      = "206000c0ff$sn" if ($report->{'info.unique_id'}); 
       my $type        = $report->{'info.model'};
       $type           =~ s/StorEdge\s+//;
       $rc->{userLabel} = $type;
       $type           = "3310" if ($type =~ /N/);

       my $simpleType  = $type;
       $simpleType     = "3310" if ($type =~ /3310/);
       $simpleType     = "3510" if ($type =~ /3510/);
       $simpleType     = "3511" if ($type =~ /3511/);
       $rc->{type}     = $simpleType;
       $rc->{key}      = "SUN\." . $simpleType . "\." . $sn;

       $rc->{error}    = $err;
       $rc->{model}    = $report->{'info.model'};
       $rc->{disk_map} = $report->{"info.disk_map"};
       $rc->{top_disk} = $report->{"info.top_disk"};
       $rc->{units}    = 1;
       $rc->{wwns}     = "";
       $rc->{revision} = $report->{'info.firmware_version'};
       $rc->{serial}   = $report->{'info.primary_sn'};
       $rc->{ip}       = $report->{'info.ip'};
       $rc->{ipno}     = Util->name2ip($rc->{ip});
       if($type =~ /3510/){
          Agent::3310->getPortWWNS({ipno => $q->{name}} , $report);
          $rc->{wwn2} = Agent::3310->get_wwns($report, $rc);
       } 
       elsif($type =~ /3511/){
          Agent::3310->getPortWWNS({ipno => $q->{name}} , $report);
          $rc->{wwn2} = Agent::3310->get_wwns($report, $rc);
       } 

       $rc->{report}   = $report;
       
     }  
  } else {
    $rc->{error} = "Cannot ping $q->{name}";
  }

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

}

sub get_wwns {
  my($class, $report, $rc) = @_;

  my($x, $wwns);

  for ($x=0; $x <= 10; $x++) {
     my $ww = $report->{"port.$x.wwn"};
     if (exists( $report->{"port.$x.wwn"}) && index($wwns, $ww) < 0 ) {
        $rc->{wwn} = $ww if ($rc && !$rc->{wwn});
        $wwns  .= "$ww,";
     }
  }
  return $wwns;
}


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

  if (System->get_testMode()) {
     my $testrep = Report->readTest($device);
     return ($device->{key}, "", $testrep);
  }

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

  $report->{'info.ethernet_status'} = "ok";

  
  if ($device->{ipno} && !Util->ping($device->{ipno},$ping_to)) {
     $report->{'info.ethernet_errs'} = "Ping failed to $device->{ipno} (timeout=$ping_to)";
     $report->{'info.ethernet_status'} = "ping_failed";
     return ($device->{key}, "Ping failed", $report);
  }
  Timelapse->startDev( $device); 
  $agent->inbandInfo($device, $report);

  my $err1 = $agent->getInquiry($device, $report);
  return ($device->{key}, $err1, $report) if ($err1);

  my $err2 = $agent->getConfig($device, $report);
  my ($wwns);
  return ($device->{key}, $err2, $report) if ($err2);

  if ($report->{'info.Product'} =~ /3510/ ) { # no port wwn for 3310
     ($err2, $wwns)    = $agent->getPortWWNS($device, $report);
     return ($device->{key}, $err2, $report) if ($err2);
  }
  if ($report->{'info.Product'} =~ /3511/ ) { # no port wwn for 3310
     ($err2, $wwns)    = $agent->getPortWWNS($device, $report);
     return ($device->{key}, $err2, $report) if ($err2);
  }


  $err2    = $agent->getLunMaps($device, $report);
  return ($device->{key}, $err2, $report) if ($err2);

  if ( $report->{'info.Product'} =~ /3510/ ) { # no fc counters for 3310 
    if (index("|$renv->{categories}|", "|san|") >= 0) {
     $report->{"FC_COUNTERS"} = $agent->FCfromDevice($device, $report);
    }
  }
  if ( $report->{'info.Product'} =~ /3511/ ) { # no fc counters for 3310 
    if (index("|$renv->{categories}|", "|san|") >= 0) {
     $report->{"FC_COUNTERS"} = $agent->FCfromDevice($device, $report);
    }
  }

  if (( $report->{'info.Product'} =~ /3120/) || 
      ( $report->{'info.Product'} =~ /StorEdge 3310  D/)) {
       delete $report->{'info.ethernet_status'};
  } 

  $agent->addIdentification($report);
 
  $agent-> checkAccessMode($device, $report);

  Timelapse->stopDev( $device); 
  return ($device->{key}, "", $report);
}

use Policies;
sub find_alert {
  my($class, $lines, $device) = @_;
  my @err;
  my $policies = Policies->new("3310_policies");
  my($x, %DONE);
  for ($x = 0; $x <= $#$lines; $x++ ) {
     my $line = $lines->[$x];
     my $comp1 = "other";
     if ($line =~ / Controller /) {
         $comp1  = "controller";
     } elsif ($line =~ / CPU /) {
         $comp1  = "cpu";
     } elsif ($line =~ / PS \d/) {
         $comp1  = "power";
     } elsif ($line =~ / Fan \d/) {
         $comp1  = "fan";
     }
     if ($line =~ /\[([0-9A-F][0-9A-F][0-9A-F][0-9A-F])\]/i) {
        my $error_code = $1;
        next if ($error_code eq "01A5" && $DONE{'0125'});
        $DONE{$error_code} = 1;
     }
     my $key0 = $device->{type} . ":" . $device->{key};
     my $key  = "$key0:$comp1";
     #                            ix  disk    minnow-54        key   
     $policies->run(\@err, $lines,\$x,$comp1, $device->{name}, $key, $device->{ipno}, $key0, 1);
  }
  return ( \@err);

}
use vars qw($DB);

# reset the cache pointed when discovering a device.

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

  my $w = $device->{path} || $device->{ipno};

  $DB = RasDB->new("CACHE");
  my $cache = $DB->hash();
  delete $cache->{"$w.start"};
}


sub readLog {
  my($class, $device, $report) = @_;
  my($renv) = System->get_renv();
  my($TO) = $renv->{'timeout.sccli'} || 400;
  my @lines;

  if (System->get_testMode()) {
     my $l;
     my $F = System->get_snapshot() ? System->get_snapshot() . "/messages.3310" :
              "/var/adm/messages.3310";
     if (open(O, $F)) {
       while ($l= <O>) {
            push(@lines, $l);
       }
       close(O);
     }
     return (undef, \@lines);
         
  }

  my $sccli = &sccli_path();
  my $renv = System->get_renv();
  my $w = $device->{path} || $device->{ipno};
  $DB = RasDB->new("CACHE");
  my $cache = $DB->hash();

  my $last_date = $cache->{"$w.start"};
  # TEST PURPOSE: uncomment the following line to get the agent to read the same
  #               events from the command.
  # $last_date = "";

  my($err,$com) = Util->run_command("$sccli $w show events last 50", "luxadm.txt" , $TO);
  return ($err, $com) if ($err);
  my ($max, $date, $x);
  open(O, ">>/var/adm/messages.3310");

  for ($x=0; $x <= $#$com; $x++) {
     my $l = $com->[$x];
     if ($l =~ /(\w+) +(\w+) +(\d+) (\d\d\:\d\d\:\d\d) (\d+)/) {
        my $mth = $Util::MTH{$2};
        my $time = substr($4,0,8);
        $date = sprintf("%2.2d-%2.2d-%2.2d %s", $5, $mth, $3, $time);
        $max = $date if ($date gt $max);
        $x= $x+2  if ($last_date && $date le $last_date);

     } elsif ($l =~ /\[(\w+)\] \#\d+\: .*SN\#(\w+)/ ) {
        my $sev   = 0;
        $date = Util->get_today() if (!$date);
        push(@lines, "$date $l");
        print O "$date $w $l\n";    
     } elsif ($l =~ /Controller/ ) {
        next if ( $l =~ / Redundant Controller Failure /);
        $date = Util->get_today() if (!$date);
        push(@lines, "$date $l");
        print O "$date $w $l\n";
     }
  }
  my $current_date = Util->get_today();
 
  $cache->{"$w.start"} = &round($current_date);

  Debug->print2("Found " . ($#lines+1) . " entries in 3310/3510/3511 log file.");
  close(O);

  if ($renv->{delete_3300_log}) {
    my($err,$com) = Util->run_command("$sccli $w clear events", "luxadm.txt" , $TO);
  }
  
  return (undef, \@lines);
}

sub round {
  my($d) = @_;

  my $secs = substr($d,11,2) * 3600 + substr($d,14,2) * 60 + substr($d,17);
  $secs += 2;

  my $h = int($secs / 3600);
  $secs -= $h * 3600;
  my $m = int($secs / 60);
  $secs -= $m * 60;

  my $d2 = substr($d, 0, 11) . sprintf("%2.2d:%2.2d:%2.2d\n", $h, $m, $secs);
  return $d2;
}

sub checkAccessMode {
    my ($class, $device, $report) = @_;
    my($renv, $devs, $hosts,$notifs, $Config) = PDM::ConfigFile->read();
    my $field = "access_mode";
    my $val = "info"."\.".$field;
    my $inband_path_valid = "id.inband_path_valid";
    foreach my $d (@$devs) {
        if ($device->{key}  eq $d->{key}) {
           if ($d->{$field}){
              $report->{$val} = $d->{$field} ;
              $report->{$inband_path_valid} = 0  ;  # if OOB discovery, IB path set to invalid
           } else {
               $report->{$val} ="inband"; # if no access mode in conf file, then it is inband
               $report->{$inband_path_valid} = 1 ;
           }
        }
    }

}

# sccli diag229 show port-wwns 
# sccli: selected se3000://172.20.67.229:58632 [SUN StorEdge 3510 SN#000003]
#Ch  Id   WWPN
#-------------------------
# 0  112  216000C0FF000003
# 1  113  226000C0FF100003
# 4  114  256000C0FF200003
# 5  115  266000C0FF300003


sub getPortWWNS {
  my($class, $device, $report) = @_;

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

  my $sccli = &sccli_path();
  my $w = $device->{path} || $device->{ipno};

  my($err,$com) = Util->run_command("$sccli $w show port-wwns", "luxadm.txt" , $TO);

  return ($err) if ($err);

  my $wwns;
  foreach my $l (@$com) {
     $l = Util->ltrim($l);
     if ($l =~ /(\d+)\s+(\d+)\s+(\w+)/) {
         my $port = $1;
         my $id   = $2;
         my $wwn  = $3;
         $wwns .= "$wwn,";
         $report->{"port.$port.id"} = $id;
         $report->{"port.$port.wwn"} = lc($wwn);
     }
  }
  return (undef, lc($wwns) );
}



#sccli diag229 show lun-maps 
#sccli: selected se3000://172.20.67.229:58632 [SUN StorEdge 3510 SN#000003]
#Ch Tgt LUN   ld/lv  ID-Partition  Assigned
#-------------------------------------------
# 0 112   0   ld0    7DE450A8-00   Primary  
# 0 112   3   ld0    7DE450A8-00   Primary  
# 1 113   0   ld1    29947214-00   Secondary
# 4 114   0   ld0    7DE450A8-00   Primary  
# 4 114   1   ld0    7DE450A8-00   Primary  


sub getLunMaps {
  my($class, $device, $report) = @_;

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


  my $sccli = &sccli_path();
  my $w = $device->{path} || $device->{ipno};

  my($err,$com) = Util->run_command("$sccli $w show lun-maps", "luxadm.txt" , $TO);
  return $err if ($err);
  my $lun = 0;
  foreach my $l (@$com) {
     $l = Util->ltrim($l);
     if ($l =~ /(\d+)\s+(\d+)\s+(\d+)\s+(\w+)\s+([^\s]+)\s+(\w+)/) {
         my $ch     = $1;
         my $target = $2;
         my $lun    = $3;
         my $ld     = $4;
         my $part   = $5;
         my $ass    = $6;
         $report->{"lun.$lun.channel"} = $ch;
         $report->{"lun.$lun.target"}  = $target;
         $report->{"lun.$lun.lun"}     = $lun;
         $report->{"lun.$lun.ld"}      = $ld;
         $report->{"lun.$lun.partition"}  = $part;
         $report->{"lun.$lun.assigned"}   = $ass;
         $lun++;
     }
  }
   $report->{"info.total_lun"} = $lun;
  return undef;
}
  
##############################
# FRU TYPES
##############################

# AC_POWER_SUPPLY       power
# BATTERY_BOARD
# EMU_BOARD
# FC_CHASSIS_BKPLN
# FC_JBOD_IOM           controller
# FC_RAID_IOM           controller
# JBOD_CHASSIS_BKPLN
# JBOD_IO_BOARD         controller
# PRI RAID CONTROLLER   controller
# RAID_CHASSIS_BKPLN
# RAID_IO_BOARD         controller
# SEC RAID CONTROLLER   controller
# TERMINATOR_BOARD

#
# FUNCTION: run_command() with standard input for the password
#
# ($err,$com, $std) = $class->run_command($device,  
#                                         "$sccli $ipno", 
#                                         "show configuration -x", 
#                                         $TO);

# Note: the password that passed in run_command () through $device variable
# should be in en-coded form.
# then  System->getDevicePassword() call will return de-coded form.
 
sub run_command {
  my($class, $device, $sccli, $command, $TO) = @_;

  my $password = System->getDevicePassword($device, "minnow");
  
  $password = "" if (!$password);

  return $class->run2($sccli, $command, $password, $TO);
}

# ($err,$com, $std) = $class->run_password_command(
#                                         "$sccli $ipno", 
#                                         "show configuration -x", 
#                                         "diags",
#                                          $TO);
# will work with any $password if the device password is not set.
# Note: the password that passed in run_password_command should be
# in de-coded form, such as "diags" in above example.
#
# As using Util->run_command() will cause time-out core dump
# so run2() module will be used to replace it.
 
sub run_password_command {
  my($class, $sccli, $command, $password, $TO) = @_;

  $sccli .= " > /tmp/minnow_$$";

  my $data = "password $password\n$command\n";

  my($err, $com, $stderr) = Util->run_command($sccli, "minnow", $TO, 
                                {            data => $data,
                                  display_command => "$sccli $command" ,
                                });

  open(O, "/tmp/minnow_$$");
  my($l, @out);
  while ($l = <O>) {
         push(@out, $l);
  }
  close(O);
  unlink "/tmp/minnow_$$";
  return ($err, \@out, $stderr);
}


sub run2 {
  my($class, $sccli, $command, $password, $TO) = @_;


  open(O, ">/tmp/sccli.in");
  print O "password $password\n$command\n";
  close(O);
  my $TEMPIN = "/tmp/sccli.in";
  chmod 0400, $TEMPIN;
  my $OUT = "/tmp/minnow_$$";
  my $ERR = "/tmp/minnow_err_$$";

  $sccli .= "< /tmp/sccli.in > $OUT 2>$ERR";

  my($w,$cnt, $kill,$status, $err, @out, $pid, $err_str);

  if ($pid = fork()) {
    while (1) {
      $w = waitpid($pid, 64);
      $status = $?;
      if ($w == $pid) {
         last;
      } elsif ($w == -1) {
         $err = "ERROR: process $pid does not exist!\n"; last;
      }
      sleep(1);
      if ($cnt++ > $TO) {
         $kill = 1; last;
      }
    }
    if ($kill) {
      kill 9, $pid;
      $err = "ERROR: killed $pid after $TO secs";
    } else {
      open(O, $OUT);
      my $l;
      while ($l = <O>) {
        push(@out, $l);
      }
      close(O);
      unlink $OUT;

      open(O, $ERR);
      while ($l = <O>) {
        $err_str .= $l;
      }
      close(O);
      unlink $ERR;
      unlink $TEMPIN;
    }
    return ($err, \@out, $err_str);

  } else {
    exec $sccli;
  }
}
   

sub getConfig {
  my($class, $device, $report) = @_;

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

  my $sccli = &sccli_path();
  my $w = $device->{path} || $device->{ipno};

  my $type = "minnow";

  my($err,$com, $std) = $class->run_command($device,  
                            "$sccli $w", 
                            "show configuration -x", $TO);

  return $err if ($err);

  my $w2 = $w;
  $w2 =~ s/\W/_/g;
  open(OO, ">" . System->get_home() . "/DATA/tmp/$w2.xml");
  print OO join("\n", @$com);
  close(OO);

  if ($err || $#$com < 0) {
    return "$err $std";
  }
  # $DB::single=1;
  while (($com->[$#$com] !~ /raidbaseview/ && $com->[$#$com] !~ /jbobaseview/ 
      && $com->[$#$com] !~ /jbobbaseview/ &&  $com->[$#$com] !~ /jbodbaseview/) && $#$com > 10) {
       $#$com--;
  }
  my $in;
  require XML::LibXML;
  my $parser = XML::LibXML->new();

  my $dom;
  eval {
    $dom  = $parser->parse_string("@$com");
  };
  if ($@) {
     Debug->print2("Error parsing XML in 'show configuration': " . substr("@$com", 0, 100));
     return "Error parsing XML";
  }
  my $elem = $dom->getDocumentElement();
  if (!$elem) {
     Debug->print2("Error parsing XML in 'show configuration': " . substr("@$com", 0, 100));
     return "Error parsing XML";
  }
  my @raid;
  @raid = $elem->findnodes("raidsystem") ;
  if ($#raid <0) {
      @raid = $elem->findnodes("jbodsystem");
  }
  my $raid1 = $raid[0];
  my %REP;
  &xml2report( \%REP, $raid1, "", 0);

  my (%LD, %DISK_SLOT, %LV, $x, $top_disk, $disk_cnt, $total_logical, $extra_logical,$total_ses, $total_volume);

  for ($x=0; $x <= 256; $x++) {
     if (exists $REP{"logical_drive.$x.ld_id"}) {
       $LD{$x} = $REP{"logical_drive.$x.ld_id"};
         $total_logical++;                 # normal logical drive, not count those in volume yet 
     }
  }

  for ($x=0; $x <= 8; $x++) {
     if (exists $REP{"logical_volume.$x.lv_id"}) {
       $LV{$x} = $REP{"logical_volume.$x.lv_id"};
         $total_volume++;
     }
  }

  # Build a hash of disk slots (targets). Retain the highest
  # numbered slot and the total count of drives.
  for ($x=0; $x <= 125; $x++) {
     if (exists $REP{"components.disk.$x.target"}) {
       $DISK_SLOT{$x} = $REP{"components.disk.$x.target"};
       $top_disk = $DISK_SLOT{$x} if ($DISK_SLOT{$x} > $top_disk);
       $disk_cnt++;
     }
  }


  $report->{"info.top_disk"} = $top_disk;
  $report->{"info.total_disk"} = $disk_cnt;

  if ( $total_volume > 0 ) {
       $extra_logical = $class->getExtraLogicalDrive(\%REP, $report);
       $total_logical = $total_logical+$extra_logical;
  }

  $report->{"info.total_logical_drives"} = $total_logical;


  my @IN;
  foreach my $el (keys %REP) {
     my ($d, $e);
     if ($el =~ /^logical_drive\.(\d+)\.(.*)/) {
        $d = $1;
        $e = $2;
        $report->{"logical_drive.$LD{$d}.$e"} = $REP{$el};  # change logical_drive.1 to logical_drive.2CCA4F3
        delete $report->{$el} if (($report->{$el}) || ($report->{$el} eq "0"));

     } 
     # Replace the index with the slot (target) for that index and store in report
     # ie. components.disk.5.model XYZ  becomes components.disk.17.model XYZ
     elsif ($el =~ /^components\.disk\.(\d+)\.(.*)/) {
        $d = $1;
        $e = $2;
        $report->{"components.disk." . $DISK_SLOT{$d} . ".$e"} = $REP{$el};
        $IN[$DISK_SLOT{$d}+0] = 1;  # Indicates a slot is populated.
     } elsif ($el =~ /^logical_volume\.(\d+)\.(.*)/) { 
        $d = $1;
        $e = $2;
        $report->{"logical_volume.$LV{$d}.$e"} = $REP{$el};  # change logical_volume.1 to logical_volume.2CCA4F3
        delete $report->{$el} if (($report->{$el}) || ($report->{$el} eq "0"));
     } else {
        $report->{$el} = $REP{$el};
     }
  }


  my (%FRU);
  for ($x=0; $x <= 200; $x++) {
     if (exists $REP{"fru.$x.serial_number"}) {
       $FRU{$x} = $REP{"fru.$x.serial_number"};    
     }
  }

  foreach my $ele (keys %REP) {
     if ($ele =~ /^fru\.(\d+)\.(.*)/) {
         my $in = $1;
         my $e = $2;
         $report->{"fru.$FRU{$in}.$e"}= $REP{$ele}; # change fru.1 to fru.006544 
         delete $report->{$ele} if (($report->{$ele}) || ($report->{$ele} eq "0"));
     } 
  }

  for ($x=0; $x <= 20; $x++) {
     if (exists $REP{"ses.$x.pld"}) {
         $total_ses++;
     }
  }
  
  
  $report->{"info.total_ses_devices"} = $total_ses;

  my ($in);
  for ($x=0; $x <= $top_disk; $x++) {
     $in .= $IN[$x] ? "1," : ",";
  }
  $report->{"info.disk_map"} = $in;

  $class->getFruEnclosureId($device,$report);

  $class->getDiskEnclosureId($device, $report);

  $class->getBatteryLife($device,$report);

  $class->normalizeLogic($report);

  if (!($device->{type} =~ /jbod/)) {
    # for 3120/3310 jbod, we can not get the channel mode info from sccli
    # so do not include the channel mode info in report for jbod device
    # for example:
    #  /opt/SUNWstade/bin/sccli /dev/es/ses1 show chan
    #  sccli: selected device /dev/es/ses1 [SUN StorEdge 3120  D SN#006548]
    #  sccli: this operation is only supported on RAID primary controllers

    $class->getChannelMode($device,$report);

  }

  $class->getCtrShutDownStatus($device,$report);

  $class->getSafteStatus($device,$report);

  $class->getCtrRedundancy($device,$report, "enclosure");
  
  if ($device->{type} =~ /3510/ ) {
     $class->getICL($device,$report, "ICL");
  }


  my($power, $ctrl, $other);
  foreach my $key (keys %$report) {
    if (($device->{type} =~ /3510/ ) || ($device->{type} =~ /3511/ )){  #deal with special output for 3510
       if ($key =~ /^fru\.(\w+)\.name/) {
        my $item = $report->{$key};
        next if (!$item);

        if ($item =~ /AC_POWER_SUPPLY/) {
            $power++;
        } elsif ($item =~ /RAID\_IOM/ ) {
            $ctrl++;
        } else {
            $other++;
        }
      }


    } else {
      if ($key =~ /^fru\.(\w+)\.name/) {
        my $item = $report->{$key};
        next if (!$item);

        if ($item =~ /AC_POWER_SUPPLY/) {
            $power++;
        } elsif ( $item =~ /CONTROLLER/) {
            $ctrl++;
        } else {
            $other++;
        }
      }
    }
  }
  $report->{"info.total_power"} = $power;
  $report->{"info.total_ctrl"} =  $ctrl;
  $report->{"info.total_other"} = $other;
  

  if (!($device->{type} =~ /jbod/)) { 

     # do not do the following for 3120 and 3310 jbod as firmware_version value is not available

     $report->{'info.revision'} = $report->{'info.firmware_version'}; 
     $report->{'info.Revision'} = $report->{'info.firmware_version'};
  }
  return undef;   
}

# to add the logical drive that consists of the logical volume into
# logical drive pool

sub getExtraLogicalDrive {
     my ($class, $rep, $report) = @_;
     
     my ($x, $y, $extra_logical);
     my $VLD;

     my $max_volume = 8; 
     my $max_ldr    = 256;

     $extra_logical =0 ;

     for ($y=0; $y<=$max_volume; $y++) {   
         for ($x=0; $x <= $max_ldr; $x++) {   
             if (exists $rep->{"logical_volume.$y.logical_drive.$x.ld_id"}) {
                 $VLD->{$y}{$x} = $rep->{"logical_volume.$y.logical_drive.$x.ld_id"};
                 $extra_logical++;   
             }
         }
     }

     foreach my $el (keys %$rep) {
         my ($d, $e,$f);
         if ( $el =~ /^logical_volume\.(\d+)\.logical_drive\.(\d+)\.(.*)/) {         
              $d = $1;
              $e = $2;
              $f = $3;
              my $key = $VLD->{$d}{$e};
              $report->{"LOGICAL_DRIVE.$key.$f"} = $rep->{$el}; # use upper case LOGICAL_DRIVE TO 
                                                                # differentiate from normal logical drives 
         }
     } 
     return $extra_logical; 
}

sub normalizeLogic {
    my ($class, $report) = @_;
    foreach my $el (keys %$report) {
       my $d;
       if ( $el =~ /LOGICAL_DRIVE\.(.*)/) {
            $d = $1;
            $report->{"logical_drive.$d"} = $report->{$el};
       }
    }
}


# ./sccli 10.0.0.54 show ses
#sccli: selected se3000://10.0.0.54:58632 [SUN StorEdge 3510 SN#002FDD]
#Ch  Id Chassis Vendor/Product ID    Rev  PLD  WWNN             WWPN
#-------------------------------------------------------------------------------
# 2  12 002FDD  SUN StorEdge 3510F A 1040 1000 204000C0FF002FDD 214000C0FF002FDD
#                                                     Topology: loop(a)
# 2  44 002FD3  SUN StorEdge 3510F D 1040 1000 205000C0FF002FD3 225000C0FF002FD3
#                                                     Topology: loop(b)
# 3  12 002FDD  SUN StorEdge 3510F A 1040 1000 204000C0FF002FDD 224000C0FF002FDD
#                                                     Topology: loop(b)
# 3  44 002FD3  SUN StorEdge 3510F D 1040 1000 205000C0FF002FD3 215000C0FF002FD3
#                                                     Topology: loop(a)
# 

sub getFruEnclosureId {
   my ($class, $device, $report) = @_;
   my($renv) = System->get_renv();
   my($TO) = $renv->{'timeout.sccli'} || 400;
 
   my $sccli = &sccli_path();
   my $w = $device->{path} || $device->{ipno};

   my ($err, $com, $std);

   # if ($report->{'info.Product'} =~ /3510/ ) {
   #   ($err,$com) = Util->run_command("$sccli $w show ses", "luxadm.txt" , $TO);
   # } 
   # elsif ($report->{'info.Product'} =~ /3511/ ) {
   #   ($err,$com) = Util->run_command("$sccli $w show ses", "luxadm.txt" , $TO);
   # } 
   # elsif ( $report->{'info.Product'} =~ /3310/ ) {
   #    ($err,$com) = Util->run_command("$sccli $w show safte","luxadm.txt" , $TO);
   # }

   if (($report->{'info.Product'} =~ /3510/ ) ||  ($report->{'info.Product'} =~ /3511/ )) {
       ($err,$com, $std) = $class->run_command($device,
                            "$sccli $w",
                            "show ses", $TO);
 
   } elsif ( $report->{'info.Product'} =~ /3310/ ) {
        ($err,$com, $std) = $class->run_command($device, 
                            "$sccli $w",
                            "show safte", $TO);
   }  

   return $err if ($err);

   my $rc;
   my ($ch, $id, $chassis_id, $tem);
   foreach my $l (@$com) {
     $l = Util->ltrim($l);
     if ( $l =~ /StorEdge/ ) {
          ($ch, $id, $chassis_id, $tem) = split(/\s+/, $l); 
          if (( $report->{'info.Product'} =~ /3510/ ) ||( $report->{'info.Product'} =~ /3511/ )) {
            $rc->{$chassis_id} = int(($id-12)/16) if (!$rc->{$chassis_id}); 
          } elsif ( $report->{'info.Product'} =~ /3310/ ) {
            $rc->{$chassis_id} = int(($id-14)/16) if (!$rc->{$chassis_id});
          }
     } 
   }

   foreach my $el (sort keys %$report ) {
      if ( $el =~ /fru.(\w+).chassis_serial_number/) {
           my $val = $report->{$el};
           $report->{"fru.$1.enclosure_id"} = $rc->{$val};
      }

   }
}


sub getCtrRedundancy {
   my ($class, $device, $report, $field) = @_;
   if ((($device->{type} =~ /3510/i) || ($device->{type} =~ /3511/i)
       || ($device->{type} =~ /3310/) ) && ($device->{type} !~ /jbod/)) {
       $report->{"info.redundancy_status"} = $report->{"redundant_param.1.Redundancy_status"};
       $report->{"$field.controller_redundancy_status"} = $report->{"redundant_param.1.Redundancy_status"};
       $report->{"$field.type"}   = "controller_redundancy";
   }
}

sub getICL {
    my ($class, $device, $report, $field) = @_;
    my $x;
    for ($x=1;$x<=8;$x++) {
             my $key = $report->{"inter_controller_link.$x.slot"}."_channel";
             my $status = $report->{"inter_controller_link.$x.bypass_status"};
             my $ch = $report->{"inter_controller_link.$x.ch"};
             $report->{"$field.$key.$ch.icl_status"} = $status;
             $report->{"$field.type"} = "inter-controller-link";
    }         
}



sub getCtrShutDownStatus {
   my ($class, $device, $report) = @_;
   my($renv) = System->get_renv();
   my($TO) = $renv->{'timeout.sccli'} || 400;
   my $sccli = &sccli_path();
   if ((($device->{type} =~ /3510/i) || ($device->{type} =~ /3511/i)
       || ($device->{type} =~ /3310/) ) && ($device->{type} !~ /jbod/)) {
       my $w = $device->{path};
       if (!$w) {
           $w = $device->{ip}|| $device->{ipno};
        }
       my $field = "array";
       my ($err, $com, $std);
       ($err,$com, $std) = $class->run_command($device,
                            "$sccli $w",
                            "show shutdown", $TO);
      return ($err) if ($err);
      foreach my $l (@$com) {
              if ( $l =~ /not shutdown/i ) {
                    $report->{"info.controller_shutdown_status"} = "online";
                   $report->{"$field.controller_shutdown_status"} = "online";
                    $report->{"$field.type"}   = "controller_shutdown";
              } elsif ( $l =~ /already shutdown/i) {
                   $report->{"info.controller_shutdown_status"} = "shutdown";
                   $report->{"$field.controller_shutdown_status"} = "shutdown"; 
                   $report->{"$field.type"}   = "controller_shutdown";
              }
      }
   }

}


sub getSafteStatus {
    my ($class, $device, $report) = @_;
   my($renv) = System->get_renv();
   my($TO) = $renv->{'timeout.sccli'} || 400;
   my $sccli = &sccli_path();
   if (($device->{type} =~ /3310jbod/i) || ($device->{type} =~ /3120jbod/i)) {
      my $w = $device->{path};
      my $field = "safte";
      my ($err, $com);
      ($err,$com) = Util->run_command("$sccli $w show enclosure-status", "tem", $TO);
    
      return $err if ($err); 
      foreach my $l (@$com) {
         if ( $l =~ /StorEdge/ ) {
            my ($tem1, $tem2, $tem3, $tem4, $tem5, $tem6, $tem7, $tem8, $tem9) = split(/\s+/,Util->ltrim($l));
            $report->{"info.safte_status"} = $tem9;
            $report->{"$field.status"} = $tem9;
            $report->{"$field.type"}   = $field; 
         }
         if ( $l =~ /Enclosure SCSI Channel/i) {
            my ($tem1, $tem2) =  split(/\:/, ,Util->ltrim($l));
            $report->{"info.scsi_chan_type"} = $tem2;
         }
      }
   }
}

sub getDiskEnclosureId {
    my ($class, $device, $report) = @_;
    my($renv) = System->get_renv();
    my $x;
    my $top_disk = $report->{"info.top_disk"};
    for ($x=0; $x <= $top_disk; $x++) {
         next if (!$report->{"components.disk.$x.model"});
         my $id = $report->{"components.disk.$x.target"};
         $report->{"components.disk.$x.enclosure_id"} = int($id/16);
    } 

}


sub getChannelMode {
    my ($class, $device, $report) = @_;
    my($renv) = System->get_renv();
    my $path;

    if ($device->{path}) {
        $path = $device->{path};
    } else {
        $path = $device->{ipno} || $device->{ip};
    } 
   
    my $fabric_mode_ch = $class->getFabricModeChannel($path);
    my $loop_mode_ch   = $class->getLoopModeChannel($path);
    my $x; 
    my $y;
    my $idx;
    my $channel; 
    for ($x=0; $x <= $#$fabric_mode_ch; $x++) {
        $channel = $fabric_mode_ch->[$x]; 
        $idx = $channel+1;
        $report->{"channel.$idx.media_mode"} = "fabric";  
    }
    

    for ($y=0; $y<= $#$loop_mode_ch; $y++) {
        $channel = $loop_mode_ch->[$y];
        $idx = $channel+1;
        $report->{"channel.$idx.media_mode"} = "loop"; 
    }
}

#
# placed_in_service' => 'Thu Feb 12 16:25:26 2004'
#
sub getBatteryLife {
   my ($class, $device, $report) = @_;    
   my $x;
   if (($report->{'info.Product'} =~ /3510/ ) || ($report->{'info.Product'} =~ /3511/ )){
      for ($x=1; $x <= 4; $x++) {
        next if (!$report->{"battery_status.$x.name"});
 
        # construct the expiration date
        # Fri Jun 20 20:08:43 2003
               
        $report->{"battery_status.$x.expiration_date"} = $class->getExpireDate($report->{"battery_status.$x.placed_in_service"});
      }
   }  
}

sub getExpireDate {
    my ( $class, $start_date) = @_;
    my ($date, $start_month, $start_day, $time, $start_year) = split(/\s+/, $start_date);
                  
    my $start_jd = Util->julian($start_year, $Util::MTH{$start_month}, $start_day);
    my $exp_jd = $start_jd+730; 
    
    my $dow = Time::JulianDay::day_of_week($exp_jd);
    my ($exp_year, $exp_month, $exp_day) =  Util->fromJulian($exp_jd);

   my $ndow = $Util::DOW2{$dow};
 
   my $new_date = $ndow." $Util::MTH2{$exp_month}"." $exp_day"." $time"." $exp_year";
   return ($new_date);

}


# translate xml to snmp structure.

sub xml2report {
  my( $report, $node, $prefix, $level)  = @_;
  my(%MAP);
  my $prefix0 = "$prefix." if ($prefix);
  require XML::LibXML;
  require XML::LibXML::NodeList;
  my @nr;
  if ($node) {
   # $DB::single=1;
   my @ch2 = $node->getChildNodes(); 
   foreach my $el (@ch2) {
    next if ($el->getType() != &XML::LibXML::ELEMENT_NODE() );
    my $name = $el->getName();
    my $pr;
    my @ch1 = $el->getChildNodes();

    # try to define $pr 
    # IS it an attribute that should be mapped to an array
    if (index(",enclosure,component,fru,battery_status,inter_controller_link,channel,logical_volume,device,redundant_param,logical_drive,disk,partition,ses,sata_mux,sata_router,", ",$name,") >= 0) {
      $MAP{$name}++;
      $pr = "$prefix0$name." . $MAP{$name};

    # on the first level, add 'info'.
    } elsif ($level == 0 && $#ch1 == 0) {
      $pr = "info.$name";

    # else, add the prefix that was passed recursively.
    } else {
      $name = "components" if ($name eq "config_components");
      $pr = "$prefix0$name";
    }
    my @attributes = $el->attributes();
    foreach my $att (@attributes) {
        my $t = $att->getType();
        my $name = $att->name();
        my $val  = $att->value();
        if ($name) {
          $report->{"$pr.$name"} = $val;
        }
    }

    if ($#ch1 == 0) {
       $report->{$pr} = $el->textContent();
    } else {
       &xml2report($report, $el, $pr, $level+1);
    }
  }
  }
}

# returns array of hashes

sub discover {
  my($class, $host, $args) = @_;
  my($rc);
  my($renv) = System->get_renv();
  my($TO) = $renv->{'timeout.sccli'} || 400;


  my $VAR1 = [];
  if (!$host) {
     return &get_discover($args);
  } else {
     my $rc = Util::Http->getCommand($host, "Agent::3310::discover&HTTP=1" , 
                                       $TO + 10);
     if (substr($rc,0,3) eq "ERR") {
        $ERR = $rc;
        return [];
     } else {
        eval substr($rc,3);
        return $VAR1;
     }
  }
}

sub get_discover {
  my($q) = @_;
 
  my @disco;
  my($renv) = System->get_renv();
  my($TO) = $renv->{'timeout.sccli'} || 400;


  my $sccli = &sccli_path();
  my $master = Util->findMaster();
  my $hname = $master ? $renv->{hostname} : "";

  if (-x $sccli) {
     my ($err, $L) = Util->run_command("$sccli --list", "sccli", $TO);
     foreach my $l (@$L) {
        if ($l =~ /\/dev\/([^ ]+) /) {
           my $path = "/dev/$1";

           # my $rc = &get_WWN({name => $path});
           # use the quick way to do the in-band discovery and add device
           # get_WWN() may be removed later.

           my $rc = Agent::3310->getFastWWN($path);  
           $rc->{path} = $path;
           push(@disco, $rc);
           if ($q->{callback}) {
              my $f = $q->{callback};
              my $type = $rc->{type};
              my $name = "$type-" . substr($rc->{wwn},-4);
              $rc->{wwn2} =~ s/\|/,/g;
              my $line = "storage.$type|$type|$rc->{ip}|$rc->{wwn}|$rc->{key}|$rc->{wwn2}|$path|$name|$rc->{userLabel}||$hname";
	      $line = "ERR|No Key found for $path $rc->{error}" if (!$rc->{key});
              &$f($line);
           }
        }
     }
  }
  if ($q->{HTTP}) {
    require Data::Dumper;
    print "OK " . Data::Dumper::Dumper(\@disco);
  } else {
    return \@disco;
  }
}

#  ADD INBAND INFO TO THE REPORT
#
sub inbandInfo {
  my($class, $device, $report) = @_;

  return if (!$device->{path});

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

  my $sccli = &sccli_path();

  $report->{"info.IB_status"} = "CC";   # cannot connect
  $report->{"info.IB_host"}   = $renv->{hostname};

  my ($err, $L) = Util->run_command("$sccli --list", "sccli", $TO);
  #/dev/rdsk/c4t40d0s2    SUN StorEdge 3510 SN#002FDD
  foreach my $l (@$L) {
     if ($l =~ /StorEdge \d+ SN#(.+)/) {
        my $sn = lc($1);
        if ($device->{key} eq $sn) {
           $report->{"info.IB_status"} = "OK";  # connect inband
           last;
        }
     } elsif ( $l =~ /StorEdge (\w+)  (\w+) SN#(\w+)/) {
        my $sn = lc($3);
        if ($device->{key} eq $sn) {
           $report->{"info.IB_status"} = "OK";  # connect inband
           last;
        }
 
     }
  }
}
  

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

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

  my $sccli = &sccli_path();

  my $w = $device->{path} || $device->{ipno};

  my($err,$com) = Util->run_command("$sccli $w inquiry", "luxadm.txt" , $TO);
  return $err if ($err);
  return "ERR: $device->{name}/$w not responding to sccli inquiry" if ("@$com" =~ /device not found/ || $#$com < 0) ;

  foreach my $l (@$com) {
    if ($l =~ /([^\:]+): (.*)/) {
       my $name = Util->trim($1);
       my $value = Util->trim($2);
       $name =~ s/Page .. //;
       $name =~ s/ /_/g;
       $rep->{"info.$name"}  = $value;
    }
  }
  return undef;
}



sub FRUS {
  my($class, $r, $name) = @_;
  my($v) = $r->{_value};
   my @FRUS;
  my $devtype = $v->{"id.device_type"} || "3310";

  foreach my $el (sort keys %$v) {
     if ($el =~ /components.disk.(\d+).model/) {
        my $ix = $1;
        my $d = "components.disk.$ix";
        my $model  = $v->{"$d.model"};
        my $vendor = $v->{"$d.manufacturer"};
        my $serial = $v->{"$d.serial_number"};
        my $rev    = $v->{"$d.product_revision"};
        my $status = $v->{"$d.status"};

        push(@FRUS, [ $name ,$devtype, "disk", "disk.$ix",
             $vendor || " N/A", $model||" N/A", $serial||" N/A", $rev, $status]);

     } elsif ($el =~ /fru.(\w+).description/) {
        my $ix = $1;
        my $d = "fru.$ix";
        my $model  = $v->{"$d.part_number"};
        my $vendor = "SUN";
        my $serial = $v->{"$d.serial_number"};
        my $rev    = $v->{"$d.revision"};
        my $status = "N/A";
        my $type   = lc($v->{"$d.name"});

        push(@FRUS, [ $name ,$devtype, $type, "fru.$ix",
             $vendor || " N/A", $model ||" N/A", $serial ||" N/A", $rev, $status]);
     }
  }
  return \@FRUS;
}


# all minnow device will use rep->{"info.Revision"} field to show firmware revision
# other revision field such as "revision", "firmware_version" will not be used
# as later two field values are not valid for 3120/3310 jbod 

sub report_exclude {
   return ",IP_Address,firmware_version,id_of_nvram_defaults,Serial_Number,manufacturer,model,bootrecord_version,total_disk,total_other,Vendor,name,sccli,unique_id,Product,revision,disk_map,top_disk,Device_Type,";
}


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

  my($v) = $r->{_value};
  my($out);
  my($tableCnt) = $arg->{tableCnt};
  my($host0) = $host || System->hostname();
  my $comm = ($r->{_status} eq "CC") ? "<font color=red>Communication Lost: Data may be old!</font>" : "";
 

  $out .= $class->reportHead($v->{'info.product'}, $r);
  my $cnt;
  $out .= "<tr> <td colspan=4><center><b>$v->{'info.Product'} $v->{'info.unique_id'}</td>";
  $out .= "<tr> <td colspan=4><b><center>$comm</td>";
  foreach my $el (sort keys %$v) {
     if ($el =~ /^info\.(.+)/)  {
       my $n = $1;
        next if (index($class->report_exclude() , ",$n,") >= 0);
       next if ($n =~ /id_of_/);
       $n =~ s/^total_//;
       $n =~ s/_/ /g;
       my $v1 = $v->{$el};
       next if ($v1 eq " "); 
       my $le = length($v1) ;
       $out .= "<tr>" if ($cnt % 2 == 0);
       if ($v1) { $cnt++};
       
       if ($le > 20) {
         my $half = int($le/2);
         $v1 = "<small>" . substr($v1,0,$half) . " " . substr($v1,$half) if ($v1);
       }
       if ($v1) {
           $out .= "<td bgcolor=$Style::LIGHT align=right>$n:</td>
                   <td>&nbsp;$v1</td>";
       }
     }
  }
  $out .= "</table>";

# CHANNELS

  $out .= "<table border=1 cellspacing=0 width=95% bgcolor=white>
    <tr bgcolor=$Style::DARK><td colspan=7><font color=white><b>Channels</td>
    <tr bgcolor=$Style::LIGHT>
        <th>#
        <th>Port
        <th>Type
        <th>Media
        <th>Speed
        <th>Width
        <th>PID/SID
   ";
  my($x);
  my $port= 0;
  for ($x=1; $x <= 100; $x++) {
     last if (!$v->{"channel.$x.type"});
     my $mode = $v->{"channel.$x.mode"};
     my $p;
     if ($mode eq "Host") {
        $port++;
        $p = $port;
     }
     $out .=<<EOF;
        <tr><td>$v->{"channel.$x.idx"}</td>
         <td>&nbsp;$p</td>
         <td>$mode</td>
         <td><center>$v->{"channel.$x.type"}</td>
         <td><center>$v->{"channel.$x.curclk"} 
         <td><center>$v->{"channel.$x.defwid"}
         <td><center>$v->{"channel.$x.pid"}/$v->{"channel.$x.sid"}&nbsp;
EOF
  }
  $out .= "</table>";

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

  my %DD;
  $out .= "<table border=1 cellspacing=0 width=95% bgcolor=white>
    <tr bgcolor=$Style::DARK><td colspan=7><font color=white><b>Disks</td>
    <tr bgcolor=$Style::LIGHT>
        <th>Ch-Id
        <th> enclosure-id
        <th>Size
        <th>Status
        <th>Model / Serial#
        <th>Rev
   ";
  my $x;
  my $top_disk = $v->{"info.top_disk"};
  for ($x=0; $x <= $top_disk; $x++) {
     next if (!$v->{"components.disk.$x.model"});
     my $s = $v->{"components.disk.$x.capacity"};
     my $ch = $v->{"components.disk.$x.ch"};
     my  $eid = $v->{"components.disk.$x.enclosure_id"};
     my $tg = $v->{"components.disk.$x.target"};
     $DD{"$ch.$tg"} = $v->{"components.disk.$x.model"} . " " . 
                      $v->{"components.disk.$x.serial_number"} . " $s";
     my $st = $v->{"components.disk.$x.status"};
     my $col = "bgcolor=#FFC0C0" if ($st =~ /Bad/i);
     $s="N/A" if ($st =~ /Bad/);
                  
     $out .=<<EOF;
        <tr><td><center>$ch-$tg</td>
         <td><center>$eid</td>
         <td align=right>$s&nbsp;</td>
         <td $col><center>$st</td>
         <td><small>$v->{"components.disk.$x.model"} / 
                    $v->{"components.disk.$x.serial_number"}&nbsp;
         <td>$v->{"components.disk.$x.product_revision"} 
EOF
  }
  $out .= "</table>";


# FRUS

  $out .= "<table border=1 cellspacing=0 width=95% bgcolor=white>
    <tr bgcolor=$Style::DARK><td colspan=7><font color=white><b>Frus</td>
    <tr bgcolor=$Style::LIGHT>
        <th>#
        <th>Name
        <th>enclosure id
        <th>Vendor/Model/Serial
        <th>Chassis Serial #
        <th>Location
        <th>Rev
   ";
  my $x;
  # for ($x=1; $x <= 100; $x++) {
  #   last if (!$v->{"fru.$x.part_number"});

  foreach my $el (keys %$v) {
    if ($el =~ /fru\.(\w+)\.name/) { 
     my $x = $1;
     my $item = $v->{"fru.$x.description"};
     $out .=<<EOF;
        <tr>
              <td>$v->{"fru.$x.idx"}</td>
              <td><small>$item</td>
              <td><small>$v->{"fru.$x.enclosure_id"}</td>
              <td><small>$v->{"fru.$x.manufacturer_jedec_id"}/ $v->{"fru.$x.part_number"}/ $v->{"fru.$x.serial_number"}</td>
              <td><small>$v->{"fru.$x.chassis_serial_number"}</td>
              <td><small>$v->{"fru.$x.fru_location"}</td>
              <td><center>$v->{"fru.$x.revision"}</td>
EOF
    }
  }
  $out .= "</table>";


# Battery Status

  $out .="<table border=1 cellspacing=0 width=95% bgcolor=white>
  <tr bgcolor=$Style::DARK><td colspan=6><font color=white><b>Battery</td>
  <tr bgcolor=$Style::LIGHT>
        <th>Manufacturing date
        <th>Name
        <th>Start service date
        <th>Expire date
        <th>Status
        <th>Type
  ";

  for ($x=1; $x <= 4; $x++) {
     last if (!$v->{"battery_status.$x.manufacturing_date"});
     $out .=<<EOF;
        <tr>
              <td><small>$v->{"battery_status.$x.manufacturing_date"}</td>
              <td><small>$v->{"battery_status.$x.name"}</td>
              <td><small>$v->{"battery_status.$x.placed_in_service"}</td>
              <td><small>$v->{"battery_status.$x.expiration_date"}</td>
              <td><small>$v->{"battery_status.$x.status"}</td>
              <td><small>$v->{"battery_status.$x.type"}</td>
EOF
  }


  $out .= "</table>";
  

######################
# LOGICAL DRIVE

  $out .= "<table border=1 cellspacing=0 width=95% bgcolor=white>
    <tr bgcolor=$Style::DARK><td colspan=6><font color=white><b>Logical Drives</td>
    <tr bgcolor=$Style::LIGHT>
        <th> LD Number 
        <th>Role
        <th>Drives
        <th>Status
        <th>Size
        <th>Raid
   ";
  my ($x, $y);
  foreach my $el (keys %$v) {
     if ($el =~ /logical_drive\.(\w+)\.status/) {
       my $x = $1;
       $out .=<<EOF;
        <tr><td><b><center>$v->{"logical_drive.$x.ld_id"}
            <td><center>$v->{"logical_drive.$x.assignment"}
            <td><center>$v->{"logical_drive.$x.number_of_drives"}
            <td><center>$v->{"logical_drive.$x.status"}
            <td align=right>$v->{"logical_drive.$x.size"}
            <td><center>$v->{"logical_drive.$x.raid_level"}
EOF
       my $details = &logical_drives($v, "logical_drive.$x", \%DD);
       my $parts   = &logical_partitions($v, "logical_drive.$x");

       $out .= "<tr><td>&nbsp;</td><td colspan=5>$details $parts</td>";
     }
  }
  $out .= "</table>";

######################
# LOGICAL VOLUME

  $out .= "<table border=1 cellspacing=0 width=95% bgcolor=white>
    <tr bgcolor=$Style::DARK><td colspan=6><font color=white><b>Logical Volumes</td>
    <tr bgcolor=$Style::LIGHT>
        <th>#
        <th>Assignment
        <th>Status
        <th>Size
   ";
  my ($x, $y, $p, $d);
  for ($x=1; $x <= 2000; $x++) {
     last if (!exists $v->{"logical_volume.$x.idx"});
     my $idx = $v->{"logical_volume.$x.idx"};
     my $ass = $v->{"logical_volume.$x.assignment"};
     my $size= &size($v->{"logical_volume.$x.size"});
     my $status= $v->{"logical_volume.$x.status"};
     my $pre = "logical_volume.$x";
     $out .= "
        <tr><td><b><center><b>lv$idx</td>
            <td><center>$ass</td>
            <td>$status</td>
            <td align=right>$size</td>";

     my $parts   = &logical_partitions($v, "logical_volume.$x");
     my ($details, $dd);
     $details = "<table border=1 cellspacing=0 width=100%><tr bgcolor=$Style::LIGHT>
        <th>Drive#</th>
        <th>Role</th>
        <th>Drives</th>
        <th>Status</th>
        <th>Size</th>
        <th>Raid</th>";

     for ($dd=1; $dd <= 1000; $dd++) {
        last if (!exists $v->{"$pre.logical_drive.$dd.idx"});
        my $idx = $v->{"$pre.logical_drive.$dd.idx"};
        my $ass = $v->{"$pre.logical_drive.$dd.assignment"};
        my $cnt = $v->{"$pre.logical_drive.$dd.number_of_drives"};
        my $level = $v->{"$pre.logical_drive.$dd.raid_level"};
        my $status= $v->{"$pre.logical_drive.$dd.status"};
        my $size  = &size($v->{"$pre.logical_drive.$dd.size"});
        $details .= "<tr>
                   <td><b>ld$idx</td>
                   <td>$ass</td>
                   <td>$cnt</td>
                   <td>$status</td>
                   <td>$size</td>
                   <td>$level</td>";

        $details .= "<tr><td>&nbsp;</td><td colspan=5> " . 
                     &logical_drives($v, "$pre.logical_drive.$dd", \%DD) . "</td>";
     }
     $details .= "</table>";
     $out .= "<tr><td colspan=1>&nbsp;</td><td colspan=3>$parts $details</td>";
  }
  $out .= "</table>&nbsp;<p>&nbsp;<p>";

  return $out;

}



sub logical_drives {
  my($v, $pre, $DD) = @_;
  my $details;
  my @dd = split(/\s+/, $v->{"$pre.physical_drive"});

  foreach my $d (@dd) {
       my $d0 = $d; $d0 =~ s/\./-/;
       my $dinfo = 
       $details .= "&nbsp;[Disk $d0] <small>$DD->{$d}</small> <br>";
  }
  return $details;
}

sub logical_partitions {
  my($v, $pre) = @_;
  my ($details);
  my $part_cnt = $v->{"$pre.total_partitions"};
  my($y);
  for ($y=1; $y <= $part_cnt; $y++) {
       my $eff0 = $v->{"$pre.partition.$y.effective_size"};
       my $map  = $v->{"$pre.partition.$y.mapping"};
       my $eff  = sprintf("%.3f GB", $eff0 / 1024);
       my $idx  = $v->{"$pre.partition.$y.idx"};
       $details .= "&nbsp;Partition-$idx: [$eff] Map: $map<br>";
  }
  return $details;
}



sub size {
  my($v) = @_;
  return sprintf("%.3f GB", $v/1024);
}

sub getLoopModeChannel {
     my ($class, $dev) = @_;
     my $TO = 120;

     my @ch_list;

     my $sccli = &sccli_path();
     my $ip =$dev;
     my ($err, $info) = Util->run_command("$sccli $ip show chan","showchan.txt",$TO);
     if (!$err) {
        foreach my $l (@$info) {
          if ($l =~ /FC\(L\)/i) {
             my (@ch) = split(/\s+/, Util->ltrim($l));
             push(@ch_list,$ch[0]);
          }
        }
     }
     if (scalar(@ch_list) > 0) {
       return \@ch_list;
     } else {
       push (@ch_list, "N/A");
         return \@ch_list;
     }

}

sub getFabricModeChannel {
     my ($class, $dev) = @_;
     my $TO = 120;

     my @ch_list;

     my $sccli = &sccli_path();
     my $ip =$dev;
     my ($err, $info) = Util->run_command("$sccli $ip show chan","showchan.txt",$TO);
     if (!$err) {
        foreach my $l (@$info) {
          if ($l =~ /FC\(P\)/i) {
             my (@ch) = split(/\s+/, Util->ltrim($l));
             push(@ch_list,$ch[0]);
          }
        }
     }
     if (scalar(@ch_list) > 0) {
       return \@ch_list;
     } else {
        push (@ch_list,"N/A");
         return \@ch_list;
     }

}

sub getLoops {
    my ($class, $dev) = @_;
    my $loop = ["loopa","loopb"];
   return $loop;

}



1;

