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

#  $Name:  $ 
#  $Id: Host.pm,v 1.19 2006/05/26 19:19:53 mckenney Exp $

 
use Health;
use PDM;
use Carp;
use Data::Dumper;
use Counter;
use Report;
use strict;
use CIM::Instance;
use Message;
use Debug;
use TO;
use Health::Server;
use base 'Health::Server';

sub revision {'$Revision: 1.19 $'}
 

# runs every time
#
sub run_host {
  my($hm, $report) = @_;
  my($run, $orep, $ompxio, $x);
  my($renv)    = System->get_renv();
  my $Config   = PDM::ConfigFile->read();

  CIM->version("1.1");
  $DB::single = 1;

  my $rep     = $report->content();
  my $mpxio   = $rep->{mpxio};
  my $oreport = PDM->getOldReport($report->fileKey);
  my $orep    = $oreport->content()  if ($oreport);
  my $to      = $report->id("class") eq "host.datahost" ? $rep->{topo} : TO->readExistingTopo();
  my $id      = $report->deviceName();

  $hm->fcEvent($rep->{FC_COUNTERS}, $orep->{FC_COUNTERS});

  my $freq  = System->get_renv()->{audit_freq} || 7;
  my $audit = System->get_audit() ? "YES" : Timer->isXdays("Audit$id", $freq);

  if (!$oreport || $audit eq "YES") {
     $hm->discoveryEvent($report, "NWS::Host", $audit, {noHost => 1});
  }

  $hm->hbaHealth($report, $oreport);
 
# ONLINE OFFLINE STANDBY

  if ($oreport) {
    $orep    = $oreport->content() ;

    foreach my $k (keys %$orep) {
      next if (substr($k,0,6) ne "mpxio.");
      if (!exists $rep->{$k}) {
        my($t, $wwn) = split(/\./, $k);
        my ($dev, $port) = $Config->deviceByWWN($wwn);
        if ($dev) {
           my $o_type = $report->id('category',   $dev->{type});
           my $o_dev  = $report->id('deviceName', $dev->{key});
           Grid->setCode("$dev->{type}.AlarmEvent.device_path");
           $hm->valueChangeEvent("port.$port.device_path", "", $report, $dev->{key}, 
             "Path $wwn to $dev->{type} $dev->{name}/$dev->{ipno} dissapeared from the host", 2, 
             "port.$port.device_path");
           $report->id('category',   $o_type);
           $report->id('deviceName', $o_dev);
        }
      }
    }
  }

  if ($to) {
    foreach my $k (keys %$rep) {
      next if (substr($k,0,6) ne "mpxio.");
      my($state, $desc) = split(/\t/, $rep->{$k});
      my $v = "MPXIO: $state on $desc";

      my($ostate, $odesc) = split(/\t/, $orep->{$k});
      if (!$oreport && ($state eq "OFFLINE")) {
        &saveMpx($to,"t3",$k, Message::SEVERITY_ERROR, $v);

      } elsif ($oreport && ($state ne $ostate)) {
         if ($state eq "OFFLINE") {
            &saveMpx($to,"t3",$k, Message::SEVERITY_ERROR, $v);
         } elsif ($state eq "ONLINE") {
            &saveMpx($to,"t3",$k, Message::SEVERITY_GREEN, $v);
         }
      }
    }
  }


  $hm->lunHealth($report, $oreport, $Config);

  $hm->free_space($report, $orep);
}


#
# LogicalLUN monitoring
#
sub lunHealth {
  my($hm, $report, $oreport, $Config) = @_;

  return if (!$oreport);
  my $rep     = $report->content();

  my $SUPPORTED_TYPES = ",SE6320,SE6920,StorEdgeA3500FCd,StorEdge_3510,StorageA3500FCd,Storage_3510,T300,T4,VE,";

  my $cimkey = $report->deviceName();
  my $id     = $cimkey;
  my $orep   = $oreport->content() ;

  my $cnt;
  foreach my $key (keys %$orep) {
      if (substr($key,0,4) eq "lun." && substr($key, -3) eq "wwn" ) {
        my($a1, $type, $dev0, $a2) = split(/\./, $key);
        my $wwn    = $orep->{$key};
        if (!exists($rep->{$key}) ) {
           my $dev = $Config->deviceByWWN($wwn, 4); # check only last 4 digits of wwn
           my $info =  "target=$dev->{type}:$dev->{name}/$dev->{ipno}" if ($dev);
           my $name = $id;
           if (index($SUPPORTED_TYPES, ",$type,") >= 0) {
              Grid->setCode("host.AlarmEvent.lun.$type");
           } else {
              Grid->setCode("host.AlarmEvent.lun");
           }

           $hm->mapValueChangeEvent($report, $orep, "host", "lun.$type.$dev0", 
                "status", $name, $name , $cimkey, "lun.status", 
                {use_map_id => 1, info => $info }
               );
           last if ($cnt++ > 10);
        }
      }
  }
}

#
# COMPARE HBA STATUS/REVISION
#
sub hbaHealth {
  my($hm, $report, $oreport) = @_;

  my $rep     = $report->content();
  my $orep    = $oreport ? $oreport->content()  : $rep;
  my $wwn     = $report->deviceName();
  my $id      = $report->deviceName();

 

  my($physicalFrame) = CIM::Key->new( ['CIM_PhysicalFrame',   # key of the frame
                        Tag               => $wwn ,
                        CreationClassName => 'CIM_PhysicalFrame' ]);

  my($INSERTS, $DELETES, $UPDATES) = $hm->idu_map($rep, $orep, "fcport",
                                     "NodeWWN");

  foreach my $el (keys %$DELETES) {
     my $comp = $DELETES->{$el};                       # el is the _name value.
     my $oserial = $orep->{"$comp.NodeWWN"};

     Grid->setCode("host.ComponentRemoveEvent.hba");
     $hm->removeCompEvent($comp,$physicalFrame, $report, 
                           $orep, "CIM_Card", $oserial, $wwn );
  }

  foreach my $el (keys %$INSERTS) {
    my $comp = $INSERTS->{$el};                       #
    my $nserial = $orep->{"$comp.NodeWWN"};
    Grid->setCode("host.ComponentInsertEvent.hba");

    #my $hba = NWS::T3->newHBA($rep, $comp, $x);
    #$hm->insertCompEvent($comp, $physicalFrame, $report, $orep, $hba, 
    #                     $nserial, $wwn)
  }

  foreach my $comp (keys %$UPDATES) {
     my $serial = $orep->{"$comp.NodeWWN"};
     Grid->setCode("host.StateChangeEvent.hba");
     $hm->stateChangeEvent($comp, 'status', $report, $rep, $orep, "CIM_Card", $serial, $wwn);

     my $new =  $rep->{"$comp.adapter_DriverVersion"};
     my $old = $orep->{"$comp.adapter_DriverVersion"};
     if ($new ne $old) {
        Grid->setCode("host.AlarmEvent.driver");
        $hm->valueChangeEvent($comp, "", $report, $wwn,
           "Driver version of $comp($serial) in host $id changed from ".
           "'$old' to '$new'", 1, "driver");
     }
     my $new =  $rep->{"$comp.adapter_FirmwareVersion"};
     my $old = $orep->{"$comp.adapter_FirmwareVersion"};
     if ($new ne $old) {
        Grid->setCode("host.AlarmEvent.firmware");
        $hm->valueChangeEvent($comp, "", $report, $wwn,
          "Firmware version of $comp($serial) in host $id changed from ".
          "'$old' to '$new'", 1, "firmware");
     }
  }
}

sub status {
   my($hm, $rep, $orep, $comp) = @_;
   my($map) = PDM->getDeviceStateMap("host.availability");

   my $status  = $rep->getState("$comp.PortState");
   my $ostatus = $orep->getState("$comp.PortState");
   my($old, $new, $sev, $act) = $map->transition("hbaStatus.$ostatus", 
                                                 "hbaStatus.$status", $orep, $rep);

   return ("$ostatus", "$status", $old, $new, $sev, $act);
}




# k = mpxio.wwn

sub saveMpx {
  my($to, $cat, $k, $sev, $desc) = @_;
  my($x,$y);
  my $L = $to->storageList();
  my $done = 0;
  for ($x=0; $x <= $#$L; $x++) {
     my $st = $L->[$x];
     next if ($st->{info}{type} ne $cat);
     my $k1 = $st->{info}{name};
     my $pi = $st->{portInfo};
     my $po = $st->{port};
     next if (!$pi);
     for ($y=0; $y <= $#$pi; $y++) {
       my $pw = $pi->[$y]{PortWWN};
       if ($k =~ /$pw/) {
          my($t2, $k2) = split(/\:/, $po->[$y]);
          State->saveState($cat, $k1, "port.$y.fc_link", 
                             $st->{info}{BoxName} || $k1, $sev, "mpxio to $po->[$y]");
          $done = 1 ;last;
       }
     }
     last if ($done);
  }
        
  State->saveState($cat,  $k, "mpxio", undef, $sev, $desc, 0);
}



sub run_backup {
  my($hm, $report ) = @_;

  my $renv = System->get_renv();
  CIM->version("1.1");
  $DB::single = 1;
  my $mgmtLevel = $report->value('id.mgmtLevel') || 'D';
  my($rep)     = $report->content();

  my($oreport) = PDM->getOldReport($report->fileKey);

  my($orep, $desc, $data, $run, $disco, $obackup, $x);

  if (!$oreport) {
     $run = 1;
  } else {
     $orep  = $oreport->content();

     my $old_backup = $orep->{backup_config};
     $old_backup =~ s/^\s*//gm;

     my $new_backup = $rep->{backup_config};
     $new_backup =~ s/^\s*//gm;

     if ($old_backup ne $new_backup) {
        $run = 1;
     }
  }

#-----------------------
#   CONFIG BACKUP
#-----------------------
  if ($run) {
       $hm->backupEvent($report);
  }


#-------------------------------------
#   TOPOLOGY BACKUP
#   Sends topology data structure in DATA field.perl 
#-------------------------------------

  my $MASTER = System->get_home() . "/DATA/topo/MERGE-MASTER";
  my ($topo, $l);
  my $file   = $MASTER;
  my $age    = Util->getFileAge($MASTER); 

  if ($file && (!$orep || $orep->{"backup.master_topo.age"} ne $age)) {
    open(O, $file);
    while ($l = <O>) {
       $topo .= " " . $l;
    }
    close(O);
    Grid->setCode("host.backup.topo");
    my $ev = CIM::Instance->new('NWS_Event', [
                  [ EventType   => "host.topoBackup"  ],
                  [ EventId     => PDM->getEventSequence  ],
                  [ MgmtLevel   => $mgmtLevel         ],
                  [ Description => "Topology backup"  ],
                  [ Component   => "backup_topo"      ],
                  [ Target      => $report->fileKey() ],
                  [ Data        => $topo              ]
                         ]);

    my $sd = Events->sourceDetector({ event => $ev , host => 1, rep => $rep});

    my $pertains = CIM::Instance->new('NWS_EventPertainsToSystem', [
                  [ Event       => $ev  ],
                  [ Element     => $sd->[3]]
                    ]);

    my $ed = Message->new( {  id   => $report->id(),
                            instances => [$ev, @$sd , $pertains],
                            severity  => Message::SEVERITY_NORMAL });

    PDM->saveMessage($ed);
  }


#-----------------------
#     PACKAGES
#-----------------------

  $run = 0;
  if (!$oreport || Timer->isXdays('sendPatchInfo', 10) ) {
     $desc  = "New Patch and Package Information generated on $renv->{hostname}";
     $disco = "D";
     foreach my $k (sort keys %$rep) {
       next if (substr($k,0,15) ne "showrev.package" &&
                 substr($k,0,14) ne "showrev.patch." );
       $data .= "ADD=$k|$rep->{$k}\n";
     }
     $run = 1; 
  } else {
     $desc  = "Patch and/or Package Information on $renv->{hostname} has changed:";
     $orep  = $oreport->content();
     $disco = "A";
     foreach my $k (keys %$rep) {
        next if (substr($k,0,15) ne "showrev.package" &&
                 substr($k,0,14) ne "showrev.patch." );
        if (!$orep->{$k}) {
           $data .= "ADD=$k|$rep->{$k}\n";
           $run = 1;
        } elsif ($rep->{$k} ne $orep->{$k}) {
           $data .= "UPDATE=$k|$rep->{$k}\n";
           $run = 1;
        }
     }
     foreach my $k (keys %$orep) {
        next if (substr($k,0,15) ne "showrev.package" &&
                 substr($k,0,14) ne "showrev.patch." );
        if (!$rep->{$k}) {
           $data .= "DELETE=$k|$orep->{$k}\n";
           $run = 1;
        }
     }
   }
   if ($run) {
       $hm->patchEvent($report, $desc, $data, $disco);
   }
}





sub patchEvent {
  my($hm, $report, $desc, $data, $disco) = @_;

  my $rep       = $report->content();
  my $mgmtLevel = $report->value('id.mgmtLevel') || 'D';
  Grid->setCode("host.PatchInfo");

  my $ev = CIM::Instance->new('NWS_Event', [
                  [ EventType   => "host.PatchInfo"  ],
                  [ EventId     => PDM->getEventSequence  ],
                  [ Description => $desc],
                  [ Target      => $report->fileKey ],
                  [ Component   => "showrev"],
                  [ MgmtLevel   => $mgmtLevel ],
                  [ Data        => $data]
                         ]);

  my $sd = Events->sourceDetector({ event => $ev , host => 1, rep => $rep});
 
  my $pertains = CIM::Instance->new('NWS_EventPertainsToSystem', [
                  [ Event       => $ev  ],
                  [ Element     => $sd->[3]],
                  [ DiscoveryType => $disco],
                    ]);

  my $ed = Message->new( {  id        => $report->id,
                            instances => [$ev, @$sd , $pertains],
                            severity  => Message::SEVERITY_NORMAL });

  PDM->saveMessage($ed);
}

sub backupEvent {
  my($hm, $report) = @_;

  my $rep       = $report->content();
  my $mgmtLevel = $report->value('id.mgmtLevel') || 'D';

  Grid->setCode("host.backup");

  my $ev = CIM::Instance->new('NWS_Event', [
                  [ EventType   => "host.backup"  ],
                  [ EventId     => PDM->getEventSequence  ],
                  [ MgmtLevel   => $mgmtLevel ],
                  [ Description => "Agent backup"],
                  [ Component   => "backup_config"],
                  [ Target      => $report->fileKey ],
                  [ Data        => $rep->{backup_config}]
                         ]);

  my $sd = Events->sourceDetector({ event => $ev , host => 1, rep => $rep});
 
  my $pertains = CIM::Instance->new('NWS_EventPertainsToSystem', [
                  [ Event       => $ev  ],
                  [ Element     => $sd->[3]]
                    ]);

  my $ed = Message->new( {  id   => $report->id(),
                            instances => [$ev, @$sd , $pertains],
                            severity  => Message::SEVERITY_NORMAL });

  PDM->saveMessage($ed);
}

# HEARTBEAT EVENT
# need to send the config file along with last_report_date and last_comm_lost date
# from the transition database for each device.
#
sub heartbeat {
  my($class, $desc, $long) = @_;

  Grid->setCode("agent.HeartbeatEvent");
  my $data;
  $data = $class->config_details() if ($long);

  my $ev = CIM::Instance->new('NWS_HeartbeatEvent', [
           [ EventType   => 'agent.HeartbeatEvent' ],
           [ EventId     => PDM->getEventSequence  ],
           [ Description => "Heartbeat $desc"   ],
           [ Data        => $data               ],
           [ MgmtLevel   => ""                  ],
           [ Interval    => 24 * 60             ],
                         ]);

  my $sd = Events->sourceDetector({ event => $ev });

  my $pertains = CIM::Instance->new('NWS_EventPertainsToSoftwareElement', [
            [ Event       => $ev  ],
            [ Element     => $sd->[0] ],
              ]);
  my $ed = Message->new( { id   => {},
                      instances => [$ev, @$sd, $pertains ],
                      severity  => Message::SEVERITY_NORMAL });
  return $ed;
}

sub config_details {
  my($class) = @_;
  my (%D, $data);
  my $DB = RasDB->new("CACHE");
  my $h  = $DB->hash();
  my $master = Util->findMaster() ;

  foreach my $el (keys %$h) {
     if (substr($el,0,10) eq "transition") {
       my $v  = substr($el,11);
       my $ix = rindex($v, ".");
       my $k  = substr($v, 0, $ix);
       my $status = substr($v, $ix+1);
       $D{$k}{$status} = $h->{$el};
     }
  }

  my($renv, $devs, $hosts)= PDM::ConfigFile->read();
  foreach my $k (sort keys %$renv) {
     $data .= "$k=$renv->{$k}\n";
  }

  $data .= "AGENT_HOSTID="   . System->hostid()   . "\n";
  $data .= "AGENT_HOSTNAME=" . System->hostname() . "\n";
  $data .= "AGENT_MASTER=$master\n";

  my $cnt;
  if (!$master) {  # on the master, send the host agent info.
     foreach my $h (@$hosts) {
        $cnt++;
        $data .= "\n[host$cnt]\n";
        foreach my $el (keys %$h) {
           $data .= "$el=$h->{$el}\n";
        }
     }
  }

  $cnt=0;
  foreach my $d (@$devs) {
     next if (!Util->isMineToMonitor($d));
     $cnt++;
     $data .= "\n[device$cnt]\n";
     foreach my $el (keys %$d) {
       $data .= "$el=$d->{$el}\n";
     }
     my $key = "$d->{type}:$d->{key}";
     my $age = Util->getFileAge(System->get_home() . "/DATA/OLD_REPORTS/$key");
     $data .= "last_report.minutes=$age\n";
     my $dk = $D{$d->{key}};
     if ($dk) {
       foreach my $el (keys %$dk) {
           my $v = $dk->{$el};
           foreach my $att (sort keys %$v) {
              my $v1 = $v->{$att};
              if ($att eq 'time') {
                $v1 = int(time/60) - $v1;
                $data .= "communication.$el.minutes=$v1\n";
              } else {
                $data .= "communication.$el.$att=$v1\n";
              }
           }
       }
     }
  }
  return $data;
}

#---------------------------------
# RUNS after all HEALTH MODULES RAN
# always runs
#---------------------------------

sub all_logic {
  my($hm ) = @_;
  my($ev, $ed, $x, $pertains , $sd);
  my($HB) = 'heartbeat';
  CIM->version("1.1");
  my $renv = System->get_renv();
  $DB::single = 1;

  my($hb_delay) = $renv->{heartbeat} || 23; # hours
  $DB::single=1;
  $hb_delay *= 60;
  
  if (Timer->getTimer($HB) > $hb_delay) { #it's been a day with no transmission.
     Timer->resetTimer($HB);
     $ed = $hm->heartbeat("Device heartbeat", 1);
     PDM->saveMessage($ed);
  }

# GET ALL SYSTEM ERRORS AND SEND AS EN EVENT.
# send events the first time and every 8 hours, stop after 24 hours.
# restart after a clean run.

  my $SE   = "system_errors";
  my @errs = Debug->get_system_errors();
  my $EF   = System->get_home(). "/DATA/save_err";
  my $WF   = System->get_home(). "/DATA/save_warn";

  my($new_data);
  if ($errs[1]) {
    my $size = Util->fileSize($WF);
    if ($size < 100000) {
      open(W, ">>$WF"); print W $errs[1]; close(W);
    }
    $new_data = 1;
  }
  if ($errs[2]) {
    my $size = Util->fileSize($EF);
    if ($size < 100000) {
      open(W, ">>$EF"); print W $errs[2]; close(W);
    }
    $new_data = 1;
  }
  my $count;
  if ($new_data) {
     $count = Counter->count($SE);
  } else {
     $count = Counter->set($SE, 1);  # reset on a good run.
  }
  if ($count > 12*24) {  # stop completely after 24 hours (until a clean run occurs).
     return;

  } elsif ($count % (12*8) == 1) {  # send what was accumulated so far.
     open(O, $EF); my @E = <O>; close(O);
     open(O, $WF); my @W = <O>; close(O);
     unlink $EF; unlink $WF;
     $#E = 500 if ($#E > 500);
     $#W = 500 if ($#W > 500);
     $hm->sysErrEvent(2, join("", @E)) if ($#E >= 0);
     $hm->sysErrEvent(1, join("", @W)) if ($#W >= 0);
  }
} 




sub sysErrEvent {
  my($hm, $sev, $data) = @_;

  my $topic = $sev == 1 ? "system_warnings" : "system_errors";
  Grid->setCode("agent.AlarmEvent.$topic");

  my $desc = substr($data,0,200);
  my $renv = System->get_renv();
  my $target;
  if ($renv->{solution} eq "N"){
    $target = "host";
  }else{
    $target = "sp";
  }
    
  my $ev = CIM::Instance->new('NWS_Event', [
                [ EventType   => 'agent.ValueChangeEvent' ],
                [ EventId     => PDM->getEventSequence  ],
                [ Severity    => $sev        ],
                [ Actionable  => $sev == 2   ],
                [ MgmtLevel   => ""          ],
                [ Caption     => $topic      ],
                [ Component   => $topic      ],
                [ Data        => $data       ],
                [ Target      => "$target:$renv->{hostname}" ],
                [ TargetName  => "$target:$renv->{hostname}" ],
                [ Description => "$desc:"    ] ,
                       ]);
   my $sd = Events->sourceDetector({ event => $ev });
  
   my $pertains = CIM::Instance->new('NWS_EventPertainsToSoftwareElement', [
                [ Event           => $ev  ],
                [ Element         => $sd->[0] ],
                  ]);
  
   my $ed = Message->new( { id   => { deviceName => System->serverName() },
                          instances => [$ev, @$sd, $pertains ],
                          severity  => $sev});
   PDM->saveMessage($ed);
}

1;

