package SWITCHAgent;
#  $Name:  $ 
#  $Id: SWITCHAgent.pm,v 1.25 2001/08/23 00:24:21 ccadieux Exp $

use Agent;
use Carp;
use strict;
use Catalog;
use vars qw($AUTOLOAD);
use Debug;
use base 'Agent';

sub isSelectable {"Sun_Switch"}
sub revision {'$Revision: 1.25 $'}

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

  bless ($self, 'SWITCHAgent');
  return $self;
}

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

  
sub RUN {
  my($agent,$ras_flag) = @_;
  my(%DONE, $HBA, $logfile, $r, @results);
  my( @all_connect_errors);
  my($xml, @lux_dif, $dif, $xml, @all_dif);
  my($ev, @stats, $id, $log_lines, $sdt, $pdm);
  my($pdm) = $agent->{pdm};
  my($renv) = System->get_renv();

  if (!$agent->category_selected) {
     Debug->print2("Skipping " . ref($agent)  . ".");
     return;
  }
  $DB::single=1;
  Timelapse->start(ref($agent));

  my($today) = Util->today("YMDH");
  my($dc) = 0;

  foreach my $device ( $agent->deviceList()) {
    next if ($device->{active} eq "N"); # skip extra switches

    $dc++;
    if (($sdt = Util->sdt($device)) ) {
        Debug->print('SDT', $sdt);
        next;
    }
    Debug->print1("-> Switch: Reading device $device->{name}: $device->{ip}");

    if (!$device->{ip}) {
        Debug->err( 'IP_NOT_FOUND',"Switch at $device->{name}" );
        next;
    }

    my($connect_errs, $wwn, $report) = $agent->read_switch($device);
    $wwn = lc($wwn);

    if ($wwn) {
      Catalog->deviceWrite( $wwn, ['switch' ,$device->{name}, $wwn, undef, $device->{ip},  $device->{ipno} ]);
    } else {
      $wwn = Catalog->deviceRead('switch', Catalog::Name, $device->{name}, Catalog::Key);
    }
    if (!$wwn) {
       Debug->err(TEXT => "Cannot identify the switch '$device->{name}': no wwn: $connect_errs");
       next;
    }
    my $t1 = $device->{ipno} || $device->{ip};
    $id = {   accessName  => $device->{ip},
              deviceName  => $wwn,
              active      => $device->{active},
              name        => $device->{name},
              display     => "$device->{name} (ip=$t1)",
              category    => Report::CAT_SWITCH,
              ip          => $device->{ip},
           };


    $report->{"id.name"} = $device->{name};
    $report->{"id.ip"} = $device->{ip};
    $report->{"id.ipno"} = $device->{ipno};
    if ($device->{primary}) {
      $report->{'id.primary'} = 
          $device->{primary} . ":" . $device->{fc} . ":" . $device->{port};
    }

    if ($connect_errs || !$wwn) {
        $pdm->saveReport(
          Report->new($id, $report , [], Report::STATUS_CANNOT_CONNECT));

    } else {
        $pdm->saveReport(Report->new($id, $report , $log_lines));
    }
  }
  Debug->print2("  No devices found") if (!$dc);

  Timelapse->stop(ref($agent));
}




sub read_switch {
  my($agent, $device) = @_;
  my(@s, %dev, $in, %info);
  my($connect_err, %C);
  my($chassis, $port, $version, $loop, $config, $network, $toggle);
  
  my($ip)    = $device->{ip};
  my($s_ip) = $ip;
  if ($device->{primary}) {
     $s_ip = "-f $device->{fc} $device->{primary}";
  }
     
  my(@counts, @stats, $stat, $x);
  my($DIR) = System->get_home() . "/bin";
  my($TO) = $agent->{renv}{'timeout.snmp'};

  my($err0,$version) = Util->run_command("$DIR/sanbox version $s_ip 2>&1", "sanbox_version.txt", $TO);
  if ($err0) {
    Debug->err(SANBOX => "Cannot run $DIR/sanbox $ip:$err0");
    return ($err0,"", undef) 
  }

  if ("@$version" =~ /failed exch/ || "@$version" =~ /bad hostname:/ ||
      $version->[0] =~ /Error:/) {
     $connect_err = "ERROR: @$version";
     return ($connect_err, "", undef);
  }
     
  my($err0,$chassis) = 
     Util->run_command("$DIR/sanbox chassis_status $s_ip", "sanbox_chassis.txt", $TO);

  my($err2,$counts) = Util->run_command("$DIR/sanbox port_counts $s_ip all", "sanbox_counts.txt", $TO);
  my($err3,$state); #  = Util->run_command("$DIR/sanbox port_state $s_ip all", "sanbox_state.txt", $TO);

  my($error, $wwn, $report) = $agent->parse2($version, $counts, $state, $chassis, $DIR, $s_ip, $TO);
  $report->{'id.name'} = $device->{name};

  return ($error, $wwn, $report);
}

sub getWWN {
  my($class, $DIR, $ip, $TO) = @_;

  my($err1,$info) = Util->run_command("$DIR/sanbox version $ip ", "sanbox_status.txt", $TO);
  foreach my $l (@$info) {
     if ($l =~ /WWN:\s+(.*)/) {
        return $1;
     }
  }
  return undef;
}


sub getZones {
  my($class, $DIR, $s_ip, $TO, $V) = @_;

  foreach my $type ('hd','ns','sl') {
     my($err1,$info) = Util->run_command("$DIR/sanbox get_zone $s_ip $type all", "sanbox_status.txt", $TO);
     my $z = -1;
     my $enabled;
     foreach my $l (@$info) {
        if ($l =~ /Zone:\s+(\d+), Enabled: yes/) {
           $z = $1;
        } elsif ($l =~ /Port: (\d+)/) {
           my $p = $1;
           if ($type ne 'sl' || $z < 100) {
             my $vz = " " . $V->{"zone.$type.$z.members"};
             my $po = $1;
             if (index($vz, " $po ") < 0) {
               $V->{"zone.$type.$z.members"} .= "$po ";
             }
           }
        }
     }
  }

}

#Port  Address  Type  Port and Node WWN                   FC-4 Types
#----  -------  ----  ----------------------------------  ----------------------
#03    1043E4   NL    50020f2300000e23  50020f2000000e23  SCSI_FCP
#07    1047E8   NL    50020f2300000ca7  50020f2000000ca7  SCSI_FCP

sub getNodesInfo {
  my ($class, $s_ip, $TO) = @_;
  my($x, @V); 
  my($DIR) = System->get_home() . "/bin";
  $TO = 20 if (!$TO);
  my($err1,$status) = Util->run_command("$DIR/sanbox nameserver $s_ip", "sanbox_status.txt", $TO);

  for ($x=2; $x <= $#$status; $x++) {
     my($port, $add, $type, $ww, $nodewwn, $fc) = split(/\s+/, $status->[$x]);
     $V[$port] = [$nodewwn, $add, $type];
  }
  return \@V;
}
  
sub getDevStatus {
  my($class, $device) = @_;

  my($ip)    = $device->{ip};
  my($s_ip) = $ip;
  if ($device->{primary}) {
     $s_ip = "-f $device->{fc} $device->{primary}";
  }
  my $V = {};
  my $D = System->get_home() . "/bin";
  $class->getStatus($D, $s_ip, 20, $V, 1);
  #$class->getLinks($D, $s_ip, 20, $V) ;
  return $V;
}

sub getLinks {
  my($class, $DIR, $s_ip, $TO, $V) = @_;
  
  
  my($err1,$status) = Util->run_command("$DIR/sanbox links $s_ip all", "sanbox_status.txt", $TO);
  my $in = 0;
  my($x, $l);
  for ($x=0; $x <= $#$status; $x++) {
    $l = $status->[$x];
    chomp($l);
    if ($l =~ /^Chassis:/) {
       last if ($in);
       $in = 1;
    } elsif ($l =~ /\s+Port:/) {
       my($a, $port, $chassis, $port2, $fc, $wwn) = split(/\s+/, Util->ltrim($l));
       $port += 0;
       $V->{"port.$port.link_wwn"} = "$wwn:" . ($port2+0);
    }
  }
}
  

sub getStatus {
  my($class, $DIR, $s_ip, $TO, $V, $flag) = @_;
  
  my $ma = "-a" if (!$flag);
  my($err1,$status) = Util->run_command("$DIR/sanbox port_status $ma $s_ip all", "sanbox_status.txt", $TO);

  my $in = ""; my($p) = "8";
  my($x, $l);
  my($totzones) = 0;
  for ($x=0; $x <= $#$status; $x++) {
    $l = $status->[$x];
    chomp($l);
    my @b = split(/\s+/, $l);
    if ($l =~ /Port:/) {
       $p      = $b[1]; # sprintf("%3.3d", $b[1]);
       $V->{"port.$p.type"}   = $b[2];
       if ($l =~ /Offline/ || $l =~ /Not-logged-in/) {
         $V->{"port.$p.mode"}   =  $b[3];
       } else {
         $V->{"port.$p.mode"}   = "Online";
       }
       if ($status->[$x+1] =~ /^\s+0x/) {
          my($alpa, $wwn, $wwn2) = split(/\s+/, Util->ltrim($status->[$x+1]));
          $V->{"port.$p.link_wwn"} = $wwn if ($wwn !~ /\</);
       }

    } elsif ($l =~ /Zone: (\d+), Ports:(.*)/) {
       my($zone) = $1; 
       if ($zone < 100) {
         my($members) = $2;
         $V->{"zone.sl.$zone.members"} = Util->trim($members) . " ";
         $totzones++;
       }
    }
  }
  $V->{"version.ports"} = $p;
  $V->{"zone.total"} = $totzones;
}

sub parse2 {
  my($agent, $version, $counts, $state, $chassis, 
      $DIR, $s_ip, $TO) = @_;
  my($in, %V, $enabled, $mfs, $fan, $attr, $l, @b);

  foreach $l (@$version) {
    chomp($l);
    @b = split(/\s*:\s*/, $l, 2);

    $V{"version.$b[0]"} = $b[1];
    if ($b[0] eq "WWN" || $b[0] eq "MAC") {
        $V{"id." . lc($b[0]) } = lc($b[1]);
    }
  }

# CHASSIS

  foreach $l (@$chassis) {
    chomp($l);
    if ($l =~ /Power:\s+(.+)/) {
        $V{"chassis.power.status"} = $1;

    } elsif ($l =~ /Temp:\s+(.+)/) {
        $V{"chassis.temp.status"} = $1;

    } elsif ($l =~ /Temp =\s+(.+)/) {
        $V{"chassis.temp.value"} = $1;

    } elsif ($l =~ /Uptime:\s+(.+)/) {
        $V{"chassis.uptime"} = $1;

    } elsif ($l =~ /Fan (\d+):\s+(.+)/) {
        $V{"chassis.fan.$1.status"} = $2;
    }
  }

  $agent->getStatus($DIR, $s_ip, $TO, \%V);
  $agent->getZones($DIR, $s_ip, $TO, \%V);

#
# COUNTS
#
  my($STATE_LIST) = "LinkFails,Total_LIP_Rcvd,InvalidTxWds,SyncLosses,CRC_Errs," .
                "Prim_Seq_Errs,AL_Init_Errs,AddressIdErrs,short_frame_err_cnt,"  .
                "long_frame_err_cnt,loss_of_signal_cnt,sync_loss,Discards," .
                "AL_Inits,LIF_flow_cntrl_err_cnt,lof_timeout_els,lof_timeout,";
  $in = "";
  foreach $l (@$counts) {
     chomp($l);
     @b = split(/\s*:\s*/, $l);
     next if ($b[0] =~ /-----------/);
     if (substr($l,0,5) eq "Port:") {
        $in = $b[1]; # sprintf("%3.3d", $b[1]);  
     } elsif ($in) {
        my($k) = $b[0];
        $k =~ s/ /_/g;
        $k =~ s/,/_/g;
        if (index($STATE_LIST, $k) >= 0) {
             $V{"port.$in.error.$b[0]"}   = $b[1];
        } else {
             $V{"port.$in.stats.$b[0]"}   = $b[1];
        }
     }

  }

#
# PORT STATE
#
  $in = ""; my($port, $admin, $op );
  foreach $l (@$state) {
    chomp($l);
    $port = "";
    $l =~ /Port: *(\d+) +Admin: *(\w+), *Operational: *(\w+)/;
    $port = $1;
    $admin = $2;
    $op = $3;
    if ($port) {
       $port = $port ; # sprintf("%3.3d", $port);
       $V{"port.$port.admin"}         = $admin;
       $V{"port.$port.port"}          = $port;
       $V{"port.$port.operational"}   = $op;
    }
  }
  Debug->dump('switchConfig', \%V);

  $agent->addIdentification(\%V);

  return ("", $V{"id.wwn"}, \%V );
}

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

  my($out);
  my($tableCnt) = $arg->{tableCnt};
  my($v) = $r->{_value};

  $out .= "
 <table border=1 cellspacing=0 width=100% bgcolor=white>
  <tr><td colspan=4 bgcolor=#666699>
  <table border=0><tr><td><font color=white><b>&nbsp;Switch $r->{_id}{name} ($host)</td>
  </table></td>";

  my($version) = $v->subset("version.");
  my($cnt) =0;
  foreach my $x (keys %$version) {
    $out .= "<tr>" if ($cnt++ % 2 == 0);
    $out .= "<td align=right bgcolor=#F0F0F0><b>$x:<td>$version->{$x}</td>";
  }
      
  $out .= "
  </table>
  <table border=1 cellspacing=0 cellpadding=1 width=100% bgcolor=white>
  <tr bgcolor=#E0E0E0>
    <th><font >Component</th>
    <th><font >Type</th>
    <th><font >Info</th>
  ";

  my($name) = $r->{_id}{name};
#  my($mon) = &mon('switch',$name);

  my($b, $status, $mode, $type, $p0, $p);
  my($cnt) = 0;
  for ($p0=1; $p0 <= 16; $p0++) {
     $p = $p0; # sprintf("%3.3d", $p0);
     last if (!$v->{"port.$p.type"});

     $status = $v->{"port.$p.mode"};
     $mode = $v->{"port.$p.operational"};
     $mode = "(op:$mode)" if ($mode);
     $type = $v->{"port.$p.type"};
     $out .= "<tr>
  <td>&nbsp;port." . ($p+0) .
  "<td>&nbsp;$type</td>
  <td>$status $mode</td>
  \n";
     $cnt++;
  }
  my($zones) = $v->{'zone.total'};
  my($val);

  # zone.hd.0.members
  my $Z = $v->subset('zone');
  foreach my $x (keys %$Z) {
     next if ($x eq "total");
     my $x0 = $x;
     $x0 =~ s/\.members//;
     $val = $Z->{$x};
     $val =~ s/ /, /g;
     $out .= "<tr>
 <td>&nbsp;$x0</td>
 <td>&nbsp;Zone</td>
 <td>&nbsp;members: $val</td>
 \n";
     $cnt++;
  }

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

}

sub FCCounters {
  my($class,$arg) = @_;
  my(@SW);
  if ($arg->{ip}) {
     return SWITCHAgent->FC1($arg);
  }
  my($renv, $devs, $host,$notifs) =PDM::ConfigFile->read;
  my(%SW);
  my($master) = Util->findMaster;

  foreach my $d (@$devs) {
    next if ($d->{type} ne "switch");
#    next if (!Util->isMineToMonitor($d));

    my($primary) = "-f $d->{fc} $d->{primary}" if ($d->{primary});

    my($sw, $sum) = SWITCHAgent->FC1( {
                       ip => $d->{ip},
                  primary => $primary,
                   report => $arg->{report},
                    cache => $arg->{cache},
                     name => $d->{name}
                       } );

    foreach my $l (keys %{$sw->{enc}}) {
        $SW{enc}{$l} = $sw->{enc}{$l};
    }
    foreach my $l (keys %{$sw->{data}}) {
        $SW{data}{$l} = $sw->{data}{$l};
    }
  }
  return \%SW;
}

sub FC1 {
  my($class, $arg) = @_;

  my($l, @b, @ST , @CNT, $cache);
  
  if ($arg->{cache} == 1) {
    $cache = 1;
  } else {
    $cache = 0;
  }
#  $cache = 0; #  temporary
  my($report) = 1 if ($arg->{report});

  my($ip)     = $arg->{ip};
  my($name)   = $arg->{name} || $ip;
  Debug->print2("FC-Switch: Reading switch $name");

  my($s_ip) = $arg->{primary} || $ip; # thru primary switch if available

  my($sb) = System->get_home() . "/bin/sanbox";

  my($err0,$version) = FC->run_command("$sb version $s_ip", "","",$cache);
  my($v) = "@$version";
  $v =~ /WWN:\s+([0-9a-f]+)/;
  my($wwn) = $1;
  return undef if (!$wwn);

  my($err,$status) = FC->run_command("$sb port_status $s_ip all","","", $cache);
  my($err1,$counts) = FC->run_command("$sb port_counts $s_ip all","","", $cache);

  my($in) = ""; my($p) = "8";

  foreach $l (@$status) {
    chomp($l);
    @b = split(/\s+/, $l);
    if ($l =~ /Port:/) {
       $p      = $b[1]; # sprintf("%3.3d", $b[1]);
       if ($l =~ /Offline/ || $l =~ /Not-logged-in/ ) {
         $ST[$p]   = "Offline";
       } else {
         $ST[$p]   = "Online";
       }
    }
  }

#Port:           2
#Inframes:       10470092 
#Outframes:      17900116 
#Discards:       14 
#Fbsyframes:     0 
#C2Rjtframes:    0 
#LinkFails:      9 
#SyncLosses:     9 
#Prim Seq Errs:  0 
#InvalidTxWds:   217101 
#CRC Errs:       33 
#DelimiterErrs:  27 
#AddressIdErrs:  0 
#Link Reset In:  0 
#Link Reset Out: 0 
#OLS In:         0 
#OLS Out:        0 
#Total LIP Rcvd: 155 
#LIP F7 F7:      153 
#LIP F8 F7:      0 

  my($STATE_LIST) = "LinkFails,Total_LIP_Rcvd,InvalidTxWds,SyncLosses,CRC_Errs,".
                "Prim_Seq_Errs,AL_Init_Errs,AddressIdErrs,short_frame_err_cnt," .
                "Inframes,Outframes,long_frame_err_cnt,loss_of_signal_cnt,sync_loss";
  $in = "";
  foreach $l (@$counts) {
     chomp($l);
     @b = split(/\s*:\s*/, $l);
     next if ($b[0] =~ /-----------/);
     if (substr($l,0,5) eq "Port:") {
        $in = $b[1]; # sprintf("%3.3d", $b[1]);
     } elsif ($in) {
        my($k) = $b[0];
        $k =~ s/ /_/g;
        $k =~ s/,/_/g;
        if ($ST[$in] ne "Offline" && index($STATE_LIST, $k) >= 0) {
             $CNT[$in]{$k}   = $b[1]+0;
        }
     }
  }
#  push(@X, "$x|$enc|$disk\t$al2->{link}\t$al2->{signal}\t$al2->{seq}\t$al2->{crc}\t$al2->{sync}\t$al2->{word}");
  my($out, $form, $form2);
  $form  = "%-4.4s %6s %6s %6s %6s %6s %6s\n";
  $form2 = "%-4d %6d %6d %6d %6d %6d %6d\n";
  my($port, $c);
  my(%X);
  if ($report) {
      $out .= sprintf($form, "Port", "Link","Signal","Seq","Crc", "Sync","Word");
      $out .= "-" x 110 . "\n";
      for ($port=0; $port <= $#CNT; $port++) {
           $c = $CNT[$port];
           $out .= sprintf($form2, $port, 
               $c->{LinkFails}, 
               $c->{loss_of_signal_cnt}, 
               $c->{Prim_Seq_Errs}, 
               $c->{CRC_Errs},
               $c->{sync_loss},
               $c->{InvalidTxWds});
      }
      return $out;
   } else {
      my(@X) = ();
      for ($port=1; $port <= $#CNT; $port++) {
           $c = $CNT[$port];
           next if (!$c);
           my($port0) = $port; # sprintf("%3.3d", $port);
           my($key) = "|$wwn|port.$port0|switch";
           $X{$key} = "$c->{LinkFails}\t$c->{loss_of_signal_cnt}\t$c->{Prim_Seq_Errs}\t$c->{CRC_Errs}\t$c->{sync_loss}\t$c->{InvalidTxWds}\t$c->{Inframes}\t$c->{Outframes}";
      }
      my($out) = { enc => {$wwn => $name} , data => \%X };
      return ($out);
   }
}


1;
