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

#  $Name:  $ 
#  $Id: HOST.pm,v 1.21 2006/07/19 14:53:25 mckenney Exp $
use Agent;
use Carp;
use Report;
use TO;
use vars qw($AUTOLOAD);
use System;
use base 'Agent';
use Agent::T3;
use strict;
use Luxadm;

sub isSelectable {"Local Host"}
sub revision {'$Revision: 1.21 $'}

use vars qw ( $DB);

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

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


sub RUN {
  my($agent, $static_list, $pass) = @_;
  my($showrev, $backup, $report);
  my $renv = System->get_renv();
  my $mgmtLevel = "D";
  my($log_err, $lines);
  $DB::single = 1;
  my $processed_list = [];
  return if ($renv->{active} eq "N");


  # RUN ONCE A DAY ONLY
  if (Timer->isXdays('readPatch', 2)) {

    $report = {};
    if (System->get_testMode()) {
        my $dev= {  type =>  Report::CAT_BACKUP, 
                     key => $renv->{hostname},
                    name => $renv->{hostname} };
        $report = Report->readTest($dev);
    } else {
        $agent->showrev($report);
        $agent->backup($report);
    }

    $report->{"id.mgmtLevel"} = $mgmtLevel;

    $agent->addIdentification($report);

    my $id = {  
              deviceName  => $renv->{hostname},
              display     => $renv->{hostname},
              active      => "Y",
              logFile     => "",
              class       => "backup",
              name        => $renv->{hostname},
              category    => Report::CAT_BACKUP,
              ip          => System->hostname(),
           };

    my $new_rep = Report->new($id, $report , undef);
    require Health::Host;
    Health::Host->run_backup($new_rep);
    PDM->saveReport($new_rep);

  }
  #
  # RUN SAN when there is a 'host' entry in the static_list
  #
  my $run;
  foreach my $d (@$static_list) {
     if ($d->{key} eq $renv->{hostname}) {
        $run = 1; last;
     }
  }
  return [] if (!$run);
  Timelapse->start("host:$renv->{hostname}");

  Debug->print1("-> HOST : Reading Host $renv->{hostname}");

  # 
  # INSTRUMENTATION
  #
  my($id);
  my $report = $agent->INSTRUMENTATION();

  my $problems = PDM->get_last_event();

  my $id = {     
              deviceName  => $renv->{hostname},
              display     => $renv->{hostname},
              active      => "Y",
              logFile     => "",
              class       => "server.host",
              name        => $renv->{hostname},
              category    => Report::CAT_HOST,
              ip          => System->hostname(),
        };

  $report->{"id.device_type"} = "host";
  my $new_rep = Report->new($id, $report , undef);

  require Health::Host;
  Health::Host->run_host($new_rep);

  PDM->saveReport($new_rep);

  my $device = ConfigDev->new({type   => "host", key  => $renv->{hostname}, 
                               active => "Y",    name => $renv->{hostname}} 
                             );
  my ($broke, $abort) = $agent->new_events($problems, $device, 1);
  push(@$processed_list, $device);

  Timelapse->stop("host:$renv->{hostname}");
  return $processed_list;
}


sub INSTRUMENTATION {
  my($agent) = @_;

  my $renv = System->get_renv();
  my $mgmtLevel = "D";
  my $device = { type => 'host', key => $renv->{hostname},
                 name => $renv->{hostname} };

  return Report->readTest($device) if (System->get_testMode());
  my($err, $cache1, %ENC, @HBA, %MPX);
  my $report = {};
  $agent->readFreeSpace($report);
  $agent->agent_patches($report);

  if (index("|$renv->{categories}|", "|san|") >= 0) {
     $report->{"FC_COUNTERS"} = $agent->FCfromDevice();
  }
  
  my $hba_list = $agent->hba_info($report);
  Discover->updateTopoDevice($device, {hba_list => $hba_list});
    
  if (0 && !$renv->{skip_luxadm}) {
    $agent->readPorts($report);
    $agent->readLuns($report);
    $err = Agent::T3->readT3IB($cache1, \%ENC, \@HBA, \%MPX, 1);
    foreach my $x (keys %MPX) {
      $report->{"mpxio.$x"} = $MPX{$x};
    }
  }

  $report->{"id.ipno"} = System->get_ipno();
  $report->{"id.mgmtLevel"} = $mgmtLevel;

  $agent->addIdentification($report);

  return $report;

}

sub hba_info {
  my($class, $report) = @_;
  
  my $renv = System->get_renv();
  my($hba_list, $switch_list);

  my $inband_off = System->get_home()  . "/DATA/inband_off";
  if (-e $inband_off ) {
       Debug->print1("-> Skipping running hba_info in HOST.pm since inband monitoring/discovery is off.");
       return $hba_list ;
  }

  ($hba_list, $switch_list) = HBAApi->run({option => "-oh"});

  # UPDATE HOST IN TOPOLOGY
  my $dev = {type => "host", 
             name => $renv->{hostname},
             key  => $renv->{hostname},
            };
  Discover->updateTopoDevice($dev, {hba_list => $hba_list});
  require TO::HOST;
  TO::HOST->addHBAs($hba_list);

  
  my($x, $p);
  my($hba_list, $switch_list) = HBAApi->run();

  for ($x=0; $x <= $#$switch_list; $x++) {
      my $sw = $switch_list->[$x];
      my $info = $sw->{info};
       foreach my $el (keys %$info) {
         $report->{"switch.$x.Info.$el"} = $info->{$el};
      }
      my $ports = $sw->{portInfo};
      for ($p=0; $p<= $#$ports; $p++) {
         my $port = $ports->[$p];
         my $p1 = sprintf("%3.3d", $p);
         foreach my $el (keys %$port) {
           $report->{"switch.$x.fcport.$p1.$el"} = $port->{$el};
         }
      }
  }

  $report->{"hba.count"} = $#$hba_list + 1;

  for ($x=0; $x <= $#$hba_list; $x++) {
    my $hba = $hba_list->[$x];
    $hba->{PortState} =~ s/HBA_PORTSTATE_//;
    foreach my $el (keys %$hba) {
       if ($el eq "deviceInfo") {
         my $hba_key = $hba->{PortWWN};
         my $devs = $hba->{$el};
         my($y);
         for ($y=0; $y <= $#$devs; $y++) {
             my $dev = $devs->[$y];
             foreach my $el2 (keys %$dev) {
                $report->{"storage.$hba_key-$y.$el2"} = $dev->{$el2} if (length($dev->{$el2}) > 0);
             }
             $report->{"storage.$hba_key-$y.hba_path"} = $hba->{path};
         }
       } else {
          $report->{"hba.$x.$el"} = $hba->{$el};
       }
    }

    my $devs = $hba->{deviceInfo};
    my($y);
    for ($y=0; $y <= $#$devs; $y++) {
       my $d = $devs->[$y];
       my $l;
       my $lun = $d->{ProductID};
       $lun =~ s/\./-/g;
       my @LUNS = split(/\|/, $d->{LogicalPath});
  
       for ($l = 0; $l <= $#LUNS; $l++ ) {
           my $lun0 = $LUNS[$l];
           if ($lun0) {
             my $i = $lun0;
             $i =~ s/\./-/g;
	     my $type;
	     if($i =~ /ses/){
	        $type =  "SES";
	     }else{
	        $type =  "lun";
	     }

             $report->{"$type.$i.$type"}     =  $lun0;
             $report->{"$type.$i.product"} =  $lun;
             $report->{"$type.$i.wwn"}     =  $d->{NodeWWN};
             $report->{"$type.$i.devWwn"}  =  $d->{NodeWWN};
             $report->{"$type.$i.devPortWwn"}  =  $d->{PortWWN};
             $report->{"$type.$i.wwnid"}   = $d->{PortFcId};
             $report->{"$type.$i.status"}  = $d->{PortState};
             $report->{"$type.$i.hbaWWN"}  = $hba->{NodeWWN};
           } else {
             last;
           }
       }
     }
   }

   return $hba_list;
}




# '3|50020f20000009b7|port.0|t3' => '0    44      0       72      44      0',

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

  my $renv = System->get_renv();

  my($hba_list, $switch_list) = HBAApi->run({option => "-oh"});

  my ($x, %X);
  my $key = $renv->{hostname};
  for ($x=0; $x <= $#$hba_list; $x++) {
     my $hba = $hba_list->[$x];
     my $val = $hba->{Linkfailures}    . "\t" .
               $hba->{Signalloss}     . "\t" .
               $hba->{Sequenceerror} . "\t" .
               $hba->{CRCerrors}     . "\t" .
               $hba->{SyncLoss}      . "\t" .
               $hba->{Invalidwords};

     $X{"0|$key|port.$x|host"} = $val;
  }
  return {data => \%X, hba => {}} ;
}



#  NEW FORMAT
#  luxadm  display /dev/rdsk/c23t50020F2300004331d1s2
#  DEVICE PROPERTIES for disk: /dev/rdsk/c23t50020F2300004331d1s2
#    Vendor:               SUN     
#    Product ID:           T300            
#    Revision:             0200
#    Serial Num:           Unsupported
#    Unformatted capacity: 125320.039 MBytes
#    Write Cache:          Enabled
#    Read Cache:           Enabled
#      Minimum prefetch:   0x0
#      Maximum prefetch:   0x0
#    Device Type:          Disk device
#    Path(s):
#  
#    /dev/rdsk/c23t50020F2300004331d1s2
#    /devices/pci@b,2000/fibre-channel@2/fp@0,0/ssd@w50020f2300004331,1:c,raw
#      LUN path port WWN:          50020f2300004331
#      Host controller port WWN:   210000e08b05041c
#      Path status:                O.K.
#    /dev/rdsk/c23t50020F23000068CCd1s2
#    /devices/pci@b,2000/fibre-channel@2/fp@0,0/ssd@w50020f23000068cc,1:c,raw
#      LUN path port WWN:          50020f23000068cc
#      Host controller port WWN:   210000e08b05041c
#      Path status:                O.K.



sub readLuns {
  my($class, $report) = @_;
  my($lux) = System->find_luxadm;
  my($renv) = System->get_renv();
  my($TO) = $renv->{'timeout.luxadm'};

  my $PATHS = Luxadm->luxadmDiskPaths({} );
  my %W;
  foreach my $p (@$PATHS) {
     $W{$p->[0]} = 1;  # use patch, not wwn
  }
  foreach my $w (keys %W) {
    my $rep = Luxadm->luxadmDisplay($w);
    foreach my $l (@$rep) {
         my $prod = $l->{ProductID};
         $prod =~ s/\W/_/g;
         my $lun;
         if ($prod =~ /SESS/) {
            $lun = "lun.VE";
         } else {
            $lun = "lun.$prod";
         }
         my $i = $l->{logical};
         $i = substr($i,1) if (substr($i,0,1) eq "/");
         $i =~ s/dev\/rdsk\///g;
         $i =~ s/\///g;
         foreach my $A ('A', 'B') {
            if (exists($l->{"StatusPort$A"})) {
               my $v = $l->{"StatusPort$A"};
               $v =~ s/ /_/g;
               $report->{"$lun.$i.status$A"} = $v;
            }
         }
	 my $node = $l->{"WWNPort"};
         $node = "29" . substr($node,2) if ($prod =~ /SESS/);

         $report->{"$lun.$i.wwn"} =  $node;
	 $report->{"$lun.$i.wwnid"} =  $l->{"WWNPORTID"};

     }
  }
  my $done = 1;
}

#   'ProductID' => 'T300'
#   'Revision' => 0117
#   'SerialNum' => 'Unsupported'
#   'StatusPortB' => 'O.K.'
#   'Vendor' => 'SUN'
#   'WWNNode' => '50020f2000003ee5'
#   'WWNPortB' => '50020f2300003ee5'
#   'logical' => '/dev/rdsk/c14t50020F2300003EE5d1s2'
#   'physical' => '/devices/pci@f,2000/pci@2/SUNW,qlc@4/fp@0,0/ssd@w50020f2300003ee5,1:c,raw'


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

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

  my ($err, $data) = Util->run_command("$lux -e port","port", $TO);
  foreach my $l (@$data) {
     my($path, $status) = split(/\s+/, $l, 2);
     next if ($status !~ /CONNEC/);
     $report->{"ports.$path"} = $status;
  }
}


sub readFreeSpace {
  my($class, $report) = @_;
  my($renv) = System->get_renv();
  my($TO) = $renv->{'timeout.luxadm'};
  my $df = System->find_command("df");

  my ($err, $data) = Util->run_command("$df -k /var/opt/SUNWstade","cap.txt", $TO);
  foreach my $l (@$data) {
     next if ($l !~ /^\/dev/);
     my($path, $kbyts, $used, $avail, $capacity, $mount) = split(/\s+/, $l, 6);
     $report->{"freeSpace./var/opt/SUNWstade"} = $capacity;
  }
  my ($err, $data) = Util->run_command("$df -k","cap.txt", $TO);
  foreach my $l (@$data) {
     my($path, $kbyts, $used, $avail, $capacity, $mount) = split(/\s+/, $l, 6);
     next if ($path eq "Filesystem");
     $report->{"freeSpace.$path|$mount"} = $capacity;
  }
}


sub backup {
  my($agent, $report) = @_;
  my($out, $l);
  return {} if (Util->findMaster() ) ;

  open(O, System->get_home() . "/DATA/rasagent.conf");
  while ($l = <O>) {
     $out .= " " . $l;
  }
  close(O);
  $report->{"backup_config"} = $out;

  my $D = System->get_home() . "/DATA/topo";
  $report->{"backup.master_topo.age"} = Util->getFileAge("$D/MERGE-MASTER");
}


sub agent_patches {
  my($agent, $report) = @_;
  my ($data, %R, $out);

  my($err, $sr) = Util->run_command("/bin/showrev -p", "installed_patches.text");

  foreach my $l (@$sr) {
     if($l =~ /SUNWstad[ems]/){
       my($head, $a,$b,$obs) = split(/\s+/, $l);
       $out .= "$a|";
     }
  }
  $report->{"id.patches.local"} = $out;
}

   
sub showrev {
  my($agent, $report) = @_;
  my($patch, $ob, $req, $inc, $pack);
  my %R ;

  my ($err, $com) = Util->run_command("/usr/bin/showrev -p", "test", 100);

  foreach my $l (@$com) {
     if ($l =~ /Patch: (.+) Obsoletes: (.*) Requires: (.*) Incompatibles: (.*) Packages: (.*)/) {
       $patch = $1;
       $ob    = $2;
       $req   = $3;
       $inc   = $4;
       $pack  = $5;
       $pack =~ s/ //g;
       $req  =~ s/ //g;
       $report->{"showrev.patch.$patch"} = "$ob|$req|$inc|$pack";
     }
  }
 
  ($err, $com) = Util->run_command("/usr/bin/pkginfo -l", "test", 100);
  my($sys, $pk, $name, $vers, $install);

  foreach my $l (@$com) {
     my(@a) = split(/:\s+/, $l, 2);
     $a[0] = Util->ltrim($a[0]);

     if ($a[0] eq "PKGINST") {
        $pk = $a[1];

     } elsif ($a[0] eq "NAME") {
        $name = $a[1];

     } elsif ($a[0] eq "CATEGORY") {
        $sys = $a[1];

     } elsif ($a[0] eq "VERSION") {
        $vers = $a[1];

     } elsif ($a[0] eq "PSTAMP") {
        $vers = $a[1];

     } elsif ($a[0] eq "INSTDATE") {
        $install = $a[1];
        $report->{"showrev.package.$pk"} = "$sys|$vers|$install|";    # $name";
     }
  }
}

sub FRUS {
  my($class, $r, $name) = @_;
  my($v) = $r->{_value};
  my @FRUS;
  my $renv = System->get_renv();
  my $devtype = "Host";

  push(@FRUS, [ $name , $devtype, $devtype, $renv->{hostname}, "SUN",
                      System->model(), System->hostid(),
                      System->os_version(), "N/A" ]);
  return \@FRUS;
}




sub REPORT {
  my($class, $host, $r, $arg) = @_;
  
  my($renv, $devs, $hosts,$notifs, $Config) = PDM::ConfigFile->read();

  my $SLICE = Util->deserialize("slice_map");
  my $typ   = "Host";

  #my $line1;
  my $out = "<table border=1 cellspacing=0 width=$Style::WIDTH bgcolor=white>
  <tr><td colspan=2 bgcolor=$Style::DARK><font color=black><b>$typ $host";
  #$out .= $line1;

  my $to = TO->readExistingTopo($arg->{host});
  if ($to) {
     my $node = $to->nodeByName("host:$arg->{host}");
     my $ip = $r ? $r->value("id.ipno") : $node->{info}{ipno};
     if ($node) {
       my $ports = $node->portInfo();
       $out .= "
        <tr><td bgcolor=white width=40% align=right>IP: <td>$ip&nbsp;</td>
        <tr><td bgcolor=white width=40% align=right>Model: <td>$node->{info}{model}</td>";
       $out .= "<tr><td colspan=2><font color=red><b>Warning:</b>
                  $node->{info}{errors}</td>"  if ($node->{info}{errors});

       $out .= "</table>
      <table border=1 cellspacing=0 width=$Style::WIDTH bgcolor=white>
       <tr bgcolor=$Style::LIGHT>
       <th>#</th>
       <th>Reg</th>
       <th>Slot</th>
       <th>Port-WWN</th>
       ";
       my ($x);
       for ($x=0; $x <= $#$ports; $x++) {
         my $p = $ports->[$x];
         my($p1, $p2, $p3) =  split(/:/, $p->{BoardSlotPort});
         my ($wwn, $bsp);
         if ($p->{PortWWN} !~ /^0*$/) {
            $wwn = $p->{PortWWN};
         } else {
            next;
         }
         my $pp = $p->{RegisterName};$pp =~ s/\-/- /;
	 my $apath = $p->{path};
	 $apath =~ s/ //g;
	 my $port_path = "ports.$apath";
	 my $port_status;
	 if($r){
	    $port_status = $r->{_value}{$port_path};
	 }
         my $bg = ($port_status eq "CONNECTED") ? "<b><font color=blue>":"";

         $out .= "<tr><td>$x</td>
                    <td>$bg$pp</td>
                    <td>Board:$p1 Slot:$p2 Port:$p3</td>
                    <td>$bg<small>$wwn</td>
               ";
       }
       $out .= "<tr><td colspan=5>&nbsp;No HBA found!</td>" if ($#$ports < 0);
       $out .= "</table>";
     } else {
       $out .= "</table>";
     }
  } else {
    $out .= "</table>";
  }
  if ($r) {
    my($v) = $r->{_value};
    $out .= "
    <table border=0><tr><td></table>
    <table border=1 width=$Style::WIDTH bgcolor=white cellspacing=0 cellpadding=0>
    <tr bgcolor=$Style::LIGHT><th>LUN<th>Product<th>WWN<th>ID";
    my $cnt;
    foreach my $x (sort keys %$v) {
      if (substr($x,0,4) eq "lun." && substr($x,-4) eq ".wwn") {
         my($l1, $type, $dev, $st) = split(/\./, $x);
         my $wwn = $v->{$x};
         my $cdev = $Config->deviceByWWN($wwn);
         my $name;
         my $type1  = $type;
         my $wwnid  = hex($v->{"$l1.$type.$dev.wwnid"});
         my $ID = $v->{"$l1.$type.wwnid"};
	 my $prod = $v->{"$l1.$type.product"};

         if ($cdev) {
           $name = $cdev->{name};
           $type1 = $cdev->{userLabel} || $cdev->{type};
           $ID    = $SLICE->{"$cdev->{key}.slice.$wwnid"};
         }
         
         my $len1 =length($dev)/ 2;
         $dev = substr($dev,0,$len1) ." " . substr($dev,$len1) if ($len1 > 10);
         $wwn = substr($wwn, 0, 8) . " " . substr($wwn,8) if ($wwn);
         $out .= "<tr><td><center>$type1 $name<td><small>&nbsp;$prod<td>$wwn</td><td>$ID&nbsp;</td>\n";
         $cnt++;
      }
    }
    $out .= "<tr><td colspan=4>&nbsp;No Luns found!</td>" if ($cnt == 0);
    $out .= "</table>";

    my ($k, $x);

# FREE SPACE
    my $first = 1;
    foreach my $x (sort keys %$v) {
        if (substr($x,0,10) eq "freeSpace.") {
           next if ($x =~ /\w{30}/);
           if ($first) {
              $out .= "
               <table border=0><tr><td></table>
                <table border=1 width=$Style::WIDTH bgcolor=white cellspacing=0 cellpadding=1>
                <tr bgcolor=$Style::LIGHT><td><b>&nbsp;Filesystem<th>Mounted<th>Capacity";
              $first=0;
           }
           my ($fs,$mounted) = split(/\|/, substr($x,10));
           $fs =~ s/:/: /;
            
           $out .= "<tr><td>&nbsp;$fs<td>&nbsp;$mounted<td align=right>$v->{$x}&nbsp;</td>";
        }
    }
    $out .= "</table>";

# PORT OFFLINE 
    $first = 1;
    foreach my $x (sort keys %$v) {
        if (substr($x,0,6) eq "ports.") {
           if ($first) {
              $out .= "
               <table border=0><tr><td></table>
                <table border=1 width=$Style::WIDTH bgcolor=white cellspacing=0 cellpadding=1>
                <tr bgcolor=$Style::LIGHT><td><b>&nbsp;Hba Status<th>Status";
              $first=0;
           }
           my $path = substr($x,6);
           $path =~ s/:devctl$//;
           my $col = "<b>" if ($v->{$x} !~ /NOT/);
           $out .= "<tr><td>&nbsp;$path<td>$col &nbsp;" . lc($v->{$x}) ."</td>";
        }
    }
    $out .= "</table>";

#  PATCHES
    my $valu = $r->{"_value"};
    my @B = split(/\|/, $valu->{"id.patches.local"});
    my $pkgname;
    $pkgname = "SUNWstade";

    $out .= "
       <table border=0><tr><td></table>
       <table border=1 width=$Style::WIDTH bgcolor=white cellspacing=0 cellpadding=0>
       <tr bgcolor=$Style::LIGHT><td><b>&nbsp;Storage Automated Diagnostic Environment Patches";
    for ($x=0; $x <= $#B; $x++) {
      $out .= "<tr><td><center>$B[$x]&nbsp;</td>\n";
    }
    if ($#B < 0) {
      $out .= "<tr><td colspan=2>&nbsp;No $pkgname Patches Installed!</td>";
    }
    $out .= "</table>";
  }

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