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

#  $Name:  $ 
#  $Id: SE.pm,v 1.31 2002/10/31 19:53:54 ccadieux Exp $

use Catalog;
use strict;
use Report;
use Agent;
use State;
use base 'Agent';
use Agent::SWITCH;

sub isSelectable {"Sun 3900/6900" }

sub revision {'$Revision: 1.31 $'}


use vars qw(%N2IP $ERR);

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

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

sub RUN {
  my($agent,$ras_flag) = @_;
  my($err_lines, $warn_lines,  $out,$device, $connect_errs);
  my($report ,  $id, $lines, $log_err, $type);
  my($onedayold, $wwn, $portWWN, $port2WWN);
  $DB::single = 1;
  my ($renv, $devs, $hosts, $notifs, $Config) = PDM::ConfigFile->read();

#  return if (!$renv->{include_se_device});

  Timelapse->start(ref($agent));

  my($sdt);
  my($dc) = 0;


#   INDY ONLY
#   RUN IF AGENT IS ON THE RACK
#
  if ($renv->{solution} eq "Y") {
     my $report = $agent->INSTRUMENTATION($renv->{solution_model}, $devs);

     my $key = ($renv->{solution_model}||"SE") . "." . System->hostid();
     my $name = $renv->{solution_model} . "-" . System->shortHostname($renv->{hostname});

     $id =  { 
                deviceName  => $key,
                active      => "Y",
                name        => $name,
                class       => "storage.se",
                display     => $renv->{solution_model} . " " . $renv->{hostname},
                category    => Report::CAT_SE,
                ip          => $renv->{ipno} || $renv->{hostname},
              };
     $report->{"id.name"}        = $renv->{hostname};
     $report->{"id.hostid"}      = System->hostid();
     $report->{"id.ip"}          = $renv->{ip};
     $report->{"id.ipno"}        = Util->name2ip();
     $report->{"id.wwn"}         = $key;
     $report->{'id.mgmtLevel'}   = 'DS';
  
     PDM->saveReport(Report->new($id, $report , ""));
  }
#
#  RUN ON CONFIGFILE DEVICES
#
  foreach $device ( $agent->deviceList() ) { # get other racks that are yours to monitor 
       next if ($device->{mgmtLevel} ne "D");
       my $sol = $device->{solution} || "se";
       my($err, $report) = Report->readReport("$sol:" . $device->{key}, $device->{ipno});
       $id = { 
              deviceName  =>  $device->{key},
              active      =>  $device->{active},
              name        =>  $device->{name},
              class       => "storage.se",
              display     => "$device->{name} (ip=$device->{ipno})",
              category    => Report::CAT_SE,
              ip          => $device->{ip},
          };
       if ($err) {
          PDM->saveReport(Report->new($id, $report , "", Report::STATUS_CANNOT_CONNECT));
       } else {
         $report = $report->value();
         $report->{"id.name"}        = $device->{name};
         $report->{'id.mgmtLevel'}   = "D";
         $report->{"id.ip"}          = $device->{ip};
         $report->{"id.ipno"}        = $device->{ipno};
         $report->{"id.wwn"}         = $device->{key};
         $report->{'id.connect_errs'} = $err;
         $agent->copyDev($device, $report);
         PDM->saveReport(Report->new($id, $report, ""));
       }
  }

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

#  http or sub call, expect the topo to exist already
sub getLinks {
  my($class, $dev, $to) = @_;
  my $VAR1;
  if ($dev->{mgmtLevel} eq "D") {  # remote 
    my ($err, $data) = Util::Http->getCommand($dev->{ip}, "Agent::SE::links&HTTP=1");
    if ($err || substr($data,0,2) ne "OK") {
       Debug->err(HTTP_ERROR => "$err $data");
       return {};
    } 
    eval substr($data, 3);
    return $VAR1;
  } else {
    return &get_links({ to => $to });
  }
}

# may need to add one argument to specify which rack if there are slaves.
# will change readTopo(<slave>)
#
sub get_links {
  my($q) = @_;
  my (%R, $x);
  my $HTTP = $q->{HTTP};
  my $renv   = System->get_renv();
  my $Config = PDM::ConfigFile->read();
  my $devs   = $Config->devices();

  my $model = $renv->{solution_model};
require TO;
  my $to    = $q->{to} || TO->readExistingTopo();
  my $Comp  = $q->{Comp};
  my $switches = $to->switchList();
  my $storages = $to->storageList();
  my $portCnt = 0;
  my $slotTotal;

  foreach my $d (@$devs) {
    my $h = $d->{host} || $renv->{hostname};
    next if ($h ne $renv->{hostname});
    $slotTotal++;
  }
  $R{"info.slotTotal"} = $slotTotal;

  if ($model eq "3910" || $model eq "3960") {
     foreach my $sw (@$switches) {
        my $name = $sw->name();
        my $rep1 = Report->readNew($name) if (!$HTTP);
        my $pi = $sw->portInfo();
        my $po = $sw->port();
        for ($x=0; $x <= $#$pi; $x++) {
            my $po0 = $po->[$x];
            my $pi0 = $pi->[$x];
            if (defined($pi0->{PortWWN}) && (!$po0 || substr($po0,0,2) ne "t3") ) {
               $R{"fcPort.$portCnt.PortWWN"}      = $pi0->{RemotePortWWN} || $pi0->{PortWWN};
               $R{"fcPort.$portCnt.nodeId"}         = "$name:$x";
               $R{"fcPort.$portCnt.LocalPortWWN"} = $pi0->{LocalPortWWN};
               $R{"fcPort.$portCnt.portType"}     = $sw->type() . "." . $pi0->{sw_PortType};
               $R{"fcPort.$portCnt.encName"}       = $sw->info("BoxName");
               if ($rep1) {
                 $R{"fcPort.$portCnt.CRC"}   = $rep1->value("port.$x.error.CRC Errs");
               }
               if ($Comp) {
                 my $comp0 = $Comp->value("$name:port.$x");
                 $R{"fcPort.$portCnt.status-sev"}     = $comp0->[0];
                 $R{"fcPort.$portCnt.status-desc"}    = $comp0->[1];
                 $R{"fcPort.$portCnt.status-avail"}   = $comp0->[3];
                 $R{"fcPort.$portCnt.status-details"} = $comp0->[4];
               }
               $portCnt++;
            }
        }
     }
  } elsif ($model eq "3900SL") {
     foreach my $st (@$storages) {
        my $name = $st->name();
        next if ($st->type() !~ /t3/);
        my $rep1 = Report->readNew($name) if (!$HTTP);
        my $pi = $st->portInfo();
        for ($x=0; $x <= $#$pi; $x++) {
          my $pi0 = $pi->[$x];
          if (exists $pi0->{PortWWN}) {
             $R{"fcPort.$portCnt.PortWWN"}      = $pi0->{PortWWN};
             $R{"fcPort.$portCnt.LocalPortWWN"} = $pi0->{PortWWN};
             $R{"fcPort.$portCnt.nodeId"}         = "$name:$x";
             $R{"fcPort.$portCnt.encName"}      = $st->info("BoxName");
             $R{"fcPort.$portCnt.portType"}     = $st->type();
             if ($rep1) {
               $R{"fcPort.$portCnt.status"} = $rep1->value("controller.u" . ($x+1) . "ctr");
             }
             if ($Comp) {
               my $comp0 = $Comp->value("$name:controller.u" . ($x+1) . "ctr");
               $R{"fcPort.$portCnt.status-sev"}     = $comp0->[0];
               $R{"fcPort.$portCnt.status-desc"}    = $comp0->[1];
               $R{"fcPort.$portCnt.status-avail"}   = $comp0->[3];
               $R{"fcPort.$portCnt.status-details"} = $comp0->[4];
             }
             $portCnt++;
          }
        }
     }
  } elsif ($model eq "6910" || $model eq "6960") {
     foreach my $sw (@$switches) {
        next if ($sw->type() eq "ve");
        my $ip3 = substr($sw->info('sw_ipAddr'), -2);
        next if ($ip3 ne "30" && $ip3 ne "31");
        my $name = $sw->name();
        my $rep1 = Report->readNew($name) if (!$HTTP);
        my $pi = $sw->portInfo();
        my $po = $sw->port();
        for ($x=0; $x <= $#$pi; $x++) {
            my $po0 = $po->[$x];
            my $pi0 = $pi->[$x];
            if (defined($pi0->{PortWWN}) && (!$po0 || substr($po0,0,2) ne "ve") ) {
               $R{"fcPort.$portCnt.PortWWN"}      = $pi0->{RemotePortWWN} || $pi0->{PortWWN};
               $R{"fcPort.$portCnt.LocalPortWWN"} = $pi0->{LocalPortWWN};
               $R{"fcPort.$portCnt.nodeId"}         = "$name:$x";
               $R{"fcPort.$portCnt.portType"}     = $sw->type() . "." . $pi0->{sw_PortType};
               $R{"fcPort.$portCnt.encName"}      = $sw->info("BoxName");
               if ($rep1) {
                 $R{"fcPort.$portCnt.status"} = $rep1->value("port.$x.mode");
                 $R{"fcPort.$portCnt.CRC"}    = $rep1->value("port.$x.error.CRC Errs");
               }
               if ($Comp) {
                 my $comp0 = $Comp->value("$name:port.$x");
                 $R{"fcPort.$portCnt.status-sev"}   = $comp0->[0];
                 $R{"fcPort.$portCnt.status-desc"}  = $comp0->[1];
                 $R{"fcPort.$portCnt.status-avail"} = $comp0->[3];
                 $R{"fcPort.$portCnt.status-details"}= $comp0->[4];
               }
               $portCnt++;
            }
        }
     }
  } elsif ($model eq "6910SL" || $model eq "6960SL") {
     foreach my $sw (@$switches) {
        next if ($sw->type() ne "ve");
        my $pi  = $sw->portInfo();
        my $pi0 = $pi->[1];
        if ($pi0->{PortWWN}) {
           $R{"fcPort.$portCnt.nodeId"}         = $sw->{name} . ":1";
           $R{"fcPort.$portCnt.PortWWN"}      = $pi0->{PortWWN};
           $R{"fcPort.$portCnt.LocalPortWWN"} = $pi0->{LocalPortWWN};
           $R{"fcPort.$portCnt.encName"}      = $sw->info("BoxName");
           $R{"fcPort.$portCnt.portType"}     = $sw->type();
           $R{"fcPort.$portCnt.status-sev"}   = 0;
           $R{"fcPort.$portCnt.status-avail"}  = 1;
           $portCnt++;
        }
     }
  }
  $R{"info.portTotal"} = $portCnt;
  if ($HTTP) {
require Data::Dumper;
     print "OK " . Data::Dumper::Dumper(\%R);
  } else {
     return \%R;
  }
}

sub updStatus {
  my($slot, $key, $R, $State) = @_;

  if (exists $State->{$key}) {
     $R->{"$slot.status-sev"}    = $State->{$key}[0];
     $R->{"$slot.status-desc"}   = $State->{$key}[1];
     $R->{"$slot.status-avail"}  = $State->{$key}[3] + 0;
     $R->{"$slot.status-details"}= $State->{$key}[4];
  } else {
     $R->{"$slot.status-sev"}    = 0;
     $R->{"$slot.status-desc"}   = "OK";
     $R->{"$slot.status-avail"}  = 1;
  }
}

sub compare {
  my($k, $comp, $DB) = @_;
  if (! exists($DB->{$k}) || $comp->[0] > $DB->{$k}[0] ) {
     $DB->{$k} = $comp;
  } elsif ($comp->[0] == $DB->{$k}[0]) {
     if ($comp->[1] gt $DB->{$k}[1]) {
        $DB->{$k} = $comp;
     }
  }
}


# state of slot
# down stays down
# errors on ctrl stay
# error on one disk,loop,midplane in one t3 brick -> warning
# more that one disk/loop/mid error in same brick -> error

sub slotStatus {
  my($class, $devs) = @_;
  my $Comps = State->components();
  my (%T3, %STATUS, %MAP);
  foreach my $d (@$devs) {
     $MAP{$d->{key}} = 1 if ($d->{mgmtLevel} eq "C");
  }
  my ($ix, $c1);
  foreach my $c (keys %$Comps) {
    my ($type, $wwn, $comp1) = split(/\:/, $c);
    next if (!$MAP{$wwn}); # skip devices that are not rack components.
    my $comp = $Comps->{$c};
    # count errors against the same unit/component type (like 'u1disk')
    if ($type eq "t3" && $comp->[0] == 2 && ($ix = index($comp1, ".")) > 0) {
       if (index("loopcard,disk,power", substr($comp1, 0, $ix)) >= 0) {
          $c1 = substr($comp1,0,$ix+3);
          $T3{"t3:$wwn:$c1"}++;
       }
    }
  }

  foreach my $c (keys %$Comps) {
    my ($type, $wwn, $comp1) = split(/\:/, $c);
    next if (!$MAP{$wwn}); # skip devices that are not rack components.
    my $comp = $Comps->{$c};
    # try to downgrade the errors
    if ($type eq "t3" && $comp->[0] == 2  && ($ix = index($comp1, ".")) > 0) {
       if (index("loopcard,disk,power", substr($comp1, 0, $ix)) >= 0) {
          $c1 = substr($comp1,0,$ix+3);
          if ($T3{"t3:$wwn:$c1"} == 1) { # only one loopcard.u1 , disk.u1 or loopcard.u1
            $comp->[0] = 1;
          }
       }
    }
    &compare("$type:$wwn", $comp, \%STATUS);
  }

  return \%STATUS;
}


sub INSTRUMENTATION {
  my($class, $model, $devs) = @_;
  my (%R, @MAP1, @MAP2, @MAP3, @MAP4, $x);
  my $renv = System->get_renv();
  foreach my $d (@$devs) {
    my $h = $d->{host} || $renv->{hostname};
    next if ($h ne $renv->{hostname});
    my $last = substr($d->{ipno},-2);
    if ($last ==30 || $last == 31) {
        push(@MAP1, $d);
    } elsif (substr($last,0,1) eq "2") {
        push(@MAP2, $d);
    } elsif ($last == 32 || $last == 33) {
        push(@MAP3, $d);
    } else {
        push(@MAP4, $d);
    }
  }
  my $rev = System->get_se_version() || "N/A";

  $R{"info.category"}              = "RAID";
  $R{"info.model"}                 =  $model;
  $R{"info.isRedundantController"} = "TRUE";
  $R{"info.supportsLUNMasking"}    = "TRUE";
  $R{"info.HGType"}                = "RAID";
  $R{"info.vendor"}                = "SUN";
  $R{"info.serialNumber"}          = "N/A";
  $R{"info.firmwareLevel"}         = $rev;
  $R{"info.spRevision"}            = $rev;
  my $first_ve  = 1;
  my $lun       = 1;
  my $total_cap = 0;
  my $State =  $class->slotStatus($devs); # State->getComponentState(2);  # with links
  my $Comp  = State->components();
  my $x;
  my $to       = TO->readTopo();

  my $host_info = Agent->hostInfo();

# SERVICE PROCESSOR is slot-0/fru-0

  $R{'slot.0.dev-type'} = 'sp';   # slot0 is SP
  $R{'slot.0.dev-name'} = $host_info->{hostname};
  $R{'slot.0.dev-key'} = $host_info->{hostname};
  $R{'fru.0.fruSlot'}    = 0; 
  $R{'fru.0.fruType'}   = 'sp';
  foreach my $el (keys %$host_info) {
     $R{'fru.0.info-' . $el} = $host_info->{$el};
  }
 
# EXTERNAL PORTS

  my $LINKS = &get_links({ to => $to, Comp => $Comp });

  foreach my $el (keys %$LINKS) {
     $R{$el} = $LINKS->{$el};
  }

  &updStatus("links", "LINK", \%R, $State);

# DEVICES / SLOTS
  my $fru  = 1;
  my $slot = 1;

  foreach my $dev (@MAP1, @MAP2, @MAP3, @MAP4) {
    my $key = $dev->{type} . ":" . $dev->{key};
    my $rep1 = Report->readNew($key);
    next if (!$rep1);
    #$rep1 = $rep1->content();

    foreach my $x (keys %$dev) {
      $R{"slot.$slot.dev-$x"} = $dev->{$x} if (substr($x,0,1) ne "_");
    }
    
    &updStatus("slot.$slot", $key, \%R, $State);

# SWITCH
    if (substr($dev->{type},0,6) eq "switch") {
      $R{"fru.$fru.version-HW"}    = $rep1->value("version.HW");
      $R{"fru.$fru.version-FLASH"} = $rep1->value("version.FLASH");
      $R{"fru.$fru.version-PROM"}  = $rep1->value("version.PROM");
      $R{"fru.$fru.fruSlot"}     = $slot;
      $R{"fru.$fru.fruType"}       = "switch";
      $fru++;

# T3
    } elsif ($dev->{type} eq "t3") { 
      foreach my $u ('u1ctr', 'u2ctr') {
        $R{"slot.$slot.fruId"}       = $u;
        $R{"slot.$slot.fruModel"}    = $rep1->value("controller.$u.fruModel");
        $R{"slot.$slot.fruRevision"} = $rep1->value("controller.$u.fruRevision");
        $R{"slot.$slot.fruSerialNo"} = $rep1->value("controller.$u.fruSerialNo");
        $R{"slot.$slot.fruVendor"}   = $rep1->value("controller.$u.fruVendor");
      }
      my ($c, $d, $l);
      for ($c =1; $c <= 2; $c++) {
         $fru = copy($slot, $fru, \%R, "controller.u${c}ctr", $rep1);
         $fru = copy($slot, $fru, \%R, "midplane.u${c}mpn", $rep1);
         $fru = copy($slot, $fru, \%R, "power.u${c}pcu1", $rep1);
         $fru = copy($slot, $fru, \%R, "power.u${c}pcu2", $rep1);
         for ($d=1; $d <= 9; $d++) {
            $fru = copy($slot, $fru, \%R, "disk.u${c}d$d", $rep1);
         }
         for ($l=1; $l <= 2; $l++) {
            $fru = copy($slot, $fru, \%R, "loopcard.u${c}l$l", $rep1);
         }
      }
      if (substr($model,0,2) eq "39") { # no vicoms, luns from t3
         my $v;
         foreach my $u ('u1','u2') {
             for ($v=1; $v <= 10; $v++) {
                my $id = "volume.${u}vol$v";
                last if (!$rep1->value("$id.volRaidLevel"));
                $R{"lun.$lun.lunIndex"}   = $lun;
                $R{"lun.$lun.lunName"}       = $dev->{name} . "-vol$v";
                $R{"lun.$lun.lunWwn"}        = $rep1->value("$id.volWWN");
                $R{"lun.$lun.lunRaidLevel"}  = $rep1->value("$id.volRaidLevel");
                $R{"lun.$lun.lunCapacity"}   = $rep1->value("$id.volCapacity");
                $R{"lun.$lun.lunType"}       = "T3";
                $R{"lun.$lun.lunStatus"}     = $rep1->value("$id.volStatus");
                $R{"lun.$lun.lunRemoteCopy"}   = "FALSE";
                $R{"lun.$lun.lunBeingBackedUp"}= "FALSE";
                $total_cap +=  $R{"lun.$lun.lunCapacity"};
                $lun++;
             }
         }
      }
# VE
    } elsif ($dev->{type} eq "ve") { # luns from vicoms
      $R{"fru.$fru.ipNo"}       = $rep1->id('ip');
      $R{"fru.$fru.version"}    = $rep1->value('initiator.version');
      $R{"fru.$fru.fruSlot"}  = $slot;
      $R{"fru.$fru.fruType"}    = "ve";
      $fru++;
      if ($first_ve) {
        $first_ve = 0;
        $rep1 = $rep1->subset("logical");
        foreach my $ix (keys %$rep1) {
          if ($ix =~ /(\w+)\.capacity/) {
             my $id = $1;
             $R{"lun.$lun.lunIndex"} = $lun;
             $R{"lun.$lun.lunName"}     = $dev->{name} . "-vol$lun";            
             $R{"lun.$lun.lunType"}     = $rep1->value("$id.type");
             $R{"lun.$lun.lunCapacity"} = $rep1->value("$id.capacity");
             $R{"lun.$lun.lunId"}       = $rep1->value("$id.name");
             my $ww = $rep1->value("$id.serial");
             $ww =~ s/-//;
             $R{"lun.$lun.lunWwn"}      = $ww;
             $R{"lun.$lun.lunStatus"}   = $rep1->value("$id.status") || "N/A";
             $total_cap +=  $R{"lun.$lun.lunCapacity"};
             $lun++;
          }
        }
      }
    }
    $slot++;
  }
  $R{"info.totalCapacity"} = $total_cap;
  $R{"info.slotTotal"} = $slot;  # slot 0 exists (sp)
  $R{"info.fruTotal"} = $fru;    # fru 0 exists
  $R{"info.lunTotal"} = $lun-1;

  if (substr($model,0,2) eq "39") {
     $R{"info.supportedRAIDType"}    = "RAID1,RAID5";
  } else {
     $R{"info.supportedRAIDType"}    = "RAID1,RAID5";
  }
  Agent->addIdentification(\%R);
  return \%R;
}
sub copy {
   my($slot, $fru, $R, $key, $rep1) = @_;

   $R->{"fru.$fru.fruSlot"} = $slot;
   my $ix = index($key, ".");
   my $key0 = substr($key,$ix+1);
   $R->{"fru.$fru.fruId"} = $key0;
   foreach my $el ('fruType','fruModel','fruRevision','fruSerialNo','fruVendor','fruState','fruStatus') {
      $R->{"fru.$fru.$el"} = $rep1->value("$key.$el");
   }
   $fru++;
   return $fru;
}

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

  my($name) = $r->{_id}{name};
  my($comm);
  my($out);
  my($tableCnt) = $arg->{tableCnt};
  my $G = Labels->read();
  my($host0) = $host || System->hostname();
  $comm = "<br><font color=red>Management-Path Lost!</font>" if ($r->{_status} eq "CC");

  my($v) = $r->{_value};
  $out .= $class->reportHead($v->{'info.model'}, $r);
  $out .= "
   <tr><td bgcolor=#D0D0F0 align=right width=10%><b>Name:</td><td width=25%>&nbsp;$r->{_id}{display}</td>
   <tr><td bgcolor=#D0D0F0 align=right width=10%><b>Rev:</td><td width=25%>&nbsp;$v->{'info.firmwareLevel'}</td>
   <tr><td bgcolor=#D0D0F0 align=right width=10%><b>Capacity:</td><td width=25%>&nbsp;$v->{'info.totalCapacity'}</td>
   <tr><td bgcolor=#D0D0F0 align=right width=10%><b>Totals:</td><td width=25%>&nbsp;Slots:$v->{'info.slotTotal'}, Luns: $v->{'info.lunTotal'}, Ports: $v->{'info.portTotal'}</td>
  </table>
  <table border=0 cellspacing=0 width=100% bgcolor=white><tr><td></table>
  <table border=1 cellspacing=0 width=95% bgcolor=white>
   <tr bgcolor=#D0D0F0><th>Slot
       <th>Name
       <th>Status
  ";
  my($x);
  my @E = ('Normal','Warning','<font color=red>Error','<font color=red>Down');
  my @GIF = ("", # "<img src=/gif/al_ok3.gif alt=\"$G->{ok}, $G->{details}\" border=0>",
             "<img src=/gif/al_alert3.gif alt=\"$G->{warning}, $G->{details}\" border=0>",
             "<img src=/gif/al_crit3.gif alt=\"$G->{error}, $G->{details}\" border=0>",
             "<img src=/gif/al_down3.gif alt=\"$G->{down}, $G->{details}\" border=0>"
             );

  for ($x=0; $x < $v->{'info.slotTotal'}; $x++) {
     my $type = $v->{"slot.$x.dev-type"};
     my $type0 = $type eq "sp" ? "host": $type;
     my $name = $v->{"slot.$x.dev-name"};
     my $status = $v->{"slot.$x.status-sev"};
     my $info = $v->{"slot.$x.status-desc"};
     my $k    = "$type0:" . $v->{"slot.$x.dev-key"};
     my $h1 =  ($v->{"id.mgmtLevel"} eq "D") ? $v->{"id.ipno"} : $host;
     my $u = "<a href=$Http::WEBPROC?GO=GUI::Instr::rStart&file=$k&host=$h1>$name</a>";
     my $u2 = "<a href=$Http::WEBPROC?GO=GUI::Instr::rStart&file=$k&Roption=2>$GIF[$status]</a>";
     $out .= "<tr><td><b>&nbsp;$x-$type<td>&nbsp;$u<td><center>$u2&nbsp;";
     #$out .= "<tr><td colspan=3><small>$info" if (length($info) > 2);
  }
  $out .= "</table>
  <table border=0 cellspacing=0 width=100% bgcolor=white><tr><td></table>
  <table border=1 cellspacing=0 width=95% bgcolor=white>
   <tr bgcolor=#D0D0F0><th>Port
       <th>Type
       <th>WWN
       <th>Status
  ";
  for ($x=0; $x < $v->{'info.portTotal'}; $x++) {
     my $type   = $v->{"fcPort.$x.portType"};
     my $status = $GIF[$v->{"fcPort.$x.status-sev"}];
     my $wwn    = $v->{"fcPort.$x.LocalPortWWN"};
     $out .= "<tr><td>&nbsp;<b>$x<td>&nbsp;$type<td>&nbsp;$wwn<td><center>$status&nbsp;</td>";
     my $info = $v->{"fcPort.$x.status-desc"};
     #$out .= "<tr><td colspan=4><small>$info" if ($info);

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