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

#Name:  $
#  $Id: T3.pm,v 1.20 2004/11/09 22:38:25 kdesai Exp $


use base 'Health';
use NWS::T3;
use DevDB;
use Ilist;
use Message;
use Catalog;
use Report;
use System;
use Events;
use strict;
use Debug;

sub revision {'$Revision: 1.20 $'}

sub is_state_change {
  my($nrep, $orep, $comp) = @_;
  my $ostatus = $orep->{"$comp.fruStatus"};
  my $nstatus = $nrep->{"$comp.fruStatus"};
  if ($ostatus ne $nstatus && index($ostatus.$nstatus, "notInstalled") >= 0) {
     return 0;  # insert or remove event
  }
  return 1;     # possible state change event
}


sub all_logic {
  my($hm, $report) = @_;
  my($ev, $ed, $pertains, $x ,$y, $contain, $sd);
  my($orep, $key);
  my $oreport  = PDM->getOldReport($report->fileKey);
  my $rep      = $report->content;
  my($ip)      = $report->id('ip');
  $DB::single = 1;
  my $wwn      = $report->id('deviceName');
  my $id       = $report->id('display');
  my $t3       = $report->id('category');
  my $volVerify= $report->id('volVerify');
  my $shortid  = $report->name();
  my $log      = $report->log();
  my $renv     = System->get_renv();
  my $mgmtLevel = $report->value('id.mgmtLevel') || 'D';
  if (!$wwn) {
     Debug->errNoRepeat(T3_NO_KEY => $ip, 24, "This $t3 ($ip) cannot be identified");
     return;
  }

  CIM->version("1.1");

  if ($hm->connectionEvent($wwn, $report, {method => 3} )) {  # IB / OOB
     return;
  }
  my($freq) = $renv->{audit_freq} || 7;
  my $audit = System->get_audit() ? "YES" : Timer->isXdays("Audit$wwn", $freq);
  my $run_audit;

  if (!$oreport || $audit eq "YES" ) {  # new t300 or audit time.
     my($etype) = $audit eq "YES" ? "$t3.AuditEvent" : "$t3.DiscoveryEvent";
     my($title) = $audit eq "YES" ? "Auditing a":"Discovered a new";
     my($dt)    = "A" if (!$audit);
     $run_audit = 1;
     Grid->setCode($etype);
     $ev = CIM::Instance->new('NWS_Event', [
                  [ EventType   => $etype  ],
                  [ Target      => "$t3:$wwn"    ],
                  [ TargetName  => $id     ],
                  [ SourceIP    => $ip     ],
                  [ MgmtLevel   => $mgmtLevel ],
                  [ EventId     => PDM->getEventSequence  ],
                  [ Description => "$title $t3 called $id"],
                  [ Data        => $hm->serial($rep) ],
                         ]);

     $sd = Events->sourceDetector({ event => $ev , host => 1, rep => $rep});
      
     my($p) = NWS::T3->newSystem($rep);

     $pertains = CIM::Instance->new('NWS_EventPertainsToSystem', [
                  [ Event       => $ev  ],
                  [ Element     => $p->[0] ],
                  [ DiscoveryType => $dt],
                    ]);
     my($monitors) = CIM::Instance->new('NWS_AgentMonitors', [
                  [ Agent       => $sd->[0]  ],
                  [ Element     => $p->[0] ],
                    ]);

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

     PDM->saveMessage($ed);

     if ($audit) {  # dump stats.

        Grid->setCode("$t3.Statistics");
        $ev = CIM::Instance->new('NWS_Event', [
                  [ EventType   => "$t3.Statistics"  ],
                  [ Target      => "$t3:$wwn"    ],
                  [ TargetName  => $id     ],
                  [ SourceIP    => $ip     ],
                  [ MgmtLevel   => $mgmtLevel ],
                  [ EventId     => PDM->getEventSequence  ],
                  [ Description => "Statistics about $t3 $id :"],
                  [ Data        => Util->nnl($rep->toString('system.'))],
                         ]);

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

        my($system_key) = CIM::Key->new( ['NWS_System', 
                    'Name'            =>$rep->get('id.wwn'),  
                    CreationClassName => 'NWS_System']);

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

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

        PDM->saveMessage($ed);

     }
  }

#
#    sysCtime,Fri Oct 26 09:48:27 2001
#

  my $now    = Util->get_today();  # YYYY-MM-DD HH:MM:SS
  my $t3time = $rep->{'system.sysCtime'};
  my($t3_day, $t3_hour, $t3_min, $t3_sec, $t3_zone, $t3_year, $t3_month);

  if ($t3time) {
    ($t3_day, $t3_hour, $t3_min, $t3_sec, $t3_zone, $t3_year) = split(/[ :]/, substr($t3time,8));
    $t3_year  = $t3_zone if ($t3_zone =~ /^20/);
    $t3_month = $Util::MTH{substr($t3time, 4,3)};

    if ($run_audit && $t3_year =~ /\d\d\d/ && $t3_sec =~ /\d+/ && $t3_min =~ /\d+/ ) {
         my $now_mins = Util->julianMins(substr($now,0,4), substr($now,5,2), 
                               substr($now,8,2), substr($now,11));
         my $t3_mins = Util->julianMins($t3_year,$t3_month, $t3_day, substr($t3time,11,8));
         my $tolerance = $renv->{time_diff_tolerance} || 1;
         if (abs($t3_mins - $now_mins) > $tolerance) {
            my $tt = "$t3_year-$t3_month-$t3_day " . substr($t3time,11,8);
            Grid->setCode("$t3.AlarmEvent.time_diff");
            $hm->valueChangeEvent("system.sysCtime", "", $report, $wwn, 
           "Time of $t3 $id is different from host: $t3 time='$tt', Host time='$now'", 
                            0, "time_diff");
         }
     }
  }
  
  $orep = ($oreport)? $oreport->content() : $rep; # same report if new disco.

  if (!$rep->{"info.last_shell_date"} && $orep->{"info.last_shell_date"}) {
    $rep->{"info.last_shell_date"} = $orep->{"info.last_shell_date"};
  }
  my $recent_shell;
  if ($rep->{"info.last_shell_date"}) {
     my $t  = Util->get_today();
     my $h  = (substr($t,5,2) *24*31*60) + substr($t,8,2)*24*60 + substr($t,11,2)*60 +
                substr($t,14,2);
     my $sh = $rep->{"info.last_shell_date"};
     my $sh1= (substr($sh,0,2) *24*31*60) + substr($sh,3,2)*24*60 + substr($sh,6,2)*60
                 + substr($sh,9,2);
     my $sh_freq = $renv->{t3_shell_frequency} || 60;
     if ($h - $sh1 < $sh_freq) { # shell activity in the last X mins.
        $rep->{'info.Disclaimer'} = "telnet shell activity in the last $sh_freq minutes";
     }
  }

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

  my $reboot  = $rep->{'system.sysLastRestart'};
  my $oreboot = $orep->{'system.sysLastRestart'};
  my $reboot_month  = $Util::MTH{substr($reboot, 4,3)};
  my $oreboot_month = $Util::MTH{substr($oreboot, 4,3)};

# System reboot for t3 crash3-t300-10 (ip=172.20.104.10) changed from 'Wed Dec 04 09:40:36 2002' to 'Fri Dec 06 21:32:47 2002'

  if ("$reboot$oreboot" =~ /(\d\d) (\d+:\d+:\d+).*(20\d+).*(\d\d) (\d+:\d+:\d+).*(20\d+)/) {
      my $reboot_day   = $1;
      my $reboot_hms   = $2;
      my $reboot_year  = $3;
      my $oreboot_day  = $4;
      my $oreboot_hms  = $5;
      my $oreboot_year = $6;
      my $reboot_mins  = Util->julianMins($reboot_year, $reboot_month, 
                            substr($reboot,8,2), substr($reboot,11,8) );
      my $oreboot_mins = Util->julianMins($oreboot_year, $oreboot_month,
                            substr($oreboot,8,2), substr($oreboot,11,8) );
      my $t3_mins = Util->julianMins($t3_year,$t3_month, $t3_day, substr($t3time,11,8));

      my $old_display  = $oreboot_year  ."-" . $oreboot_month . "-" . $oreboot_day . " " . 
                         $oreboot_hms;
      my $new_display  = $reboot_year  ."-" . $reboot_month . "-" . $reboot_day . " " . 
                         $reboot_hms;
                  
      if (abs($reboot_mins - $oreboot_mins) > 5) {
         Grid->setCode("$t3.AlarmEvent.system_reboot");
         $hm->valueChangeEvent("system.sysLastRestart", "", $report, $wwn, 
                      "System reboot for $t3 $id changed from '$old_display' to '$new_display'", 
                      1, "system_reboot");
      }
      if (abs($t3_mins - $reboot_mins) < 20) {
         $report->copyOld(); # take old report data into this one.
         $report->value('system.sysLastRestart', $reboot);
         return;
      }
  }elsif(!defined($rep->{'system.sysLastRestart'})){
      Grid->setCode("$t3.AlarmEvent.system_time_token");
      $hm->valueChangeEvent("system.sysLastRestart", "", $report, $wwn, 
                       "$t3 $id did not report a reboot time.", 
                      1, "system_reboot");
      $report->copyOld(); # take old report data into this one.
      $report->value('system.sysLastRestart', $reboot);
      return;
  }


  $hm->locationChangeEvent($report, $rep, $orep, $wwn);
  $hm->performance($rep, $orep);

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

  if ($rep->get("diagnostic.error.desc")) {
     my $command  = "vol verify " . $rep->get("diagnostic.error.volume");
     my $start = $rep->get("diagnostic.error.start");
     my $end   = $rep->get("diagnostic.error.end");
     my $desc  = "diagnostic error on $id: " . $rep->get("diagnostic.error.desc");
     $hm->diagEvent($report, 2, "volverify", $command, $wwn, $start, $end, $desc);
  }

  my($UNITS) = $rep->get('unit.count') || $rep->get('controller.count');
  my($comp, $fc_err, $fc_err2 ,%FC);

#  PORT EVENTS
  foreach my $e (keys %$rep) {
     if ($e =~ /port\.(u.*)\.attachVolOwner/) {
        my $u = $1;
        my $owner = $rep->{$e};
        my $oowner= $orep->{$e};
        my $mode  = $rep->{"port.$u.attachMode"};
        my $omode = $orep->{"port.$u.attachMode"};
        if ($owner ne $oowner && $oowner) {
          $comp = "port.$u";
          my $volid    = $rep->{"port.$u.attachVolId"};
          Grid->setCode("$t3.AlarmEvent.volOwner");
          my $sev = (substr($owner,0,2) eq substr($volid,0,2)) ? 0 : 2;
          $hm->valueChangeEvent($comp, "", $report, $wwn, "Volume Attach owner for $volid (port '$u') on $t3 $id changed from '$oowner/$omode' to '$owner/$mode'", $sev, "$volid.volOwner" );
        }

     # For 6120, the fc-stat format is fc_stats.unit0-ctrl1-loop0.InvCRCCnt
     } elsif ($e =~ /fc_stats.(unit\d+)-ctrl(\d+)-loop(\d+).(.*)/) {
        my $unit  = $1;
        my $ctrl  = $2;
        my $loop  = $3;
        my $fc    = $4;
        my $o_crc = $orep->{$e};
        my $n_crc = $rep->{$e};
        my $diff  = $n_crc - $o_crc;

	#next if (index("InvTxWord,InvCRCCnt,LossSignalCnt", $fc) < 0);
	next if ($diff <= 0);
        if (exists $orep->{$e} &&  exists $orep->{$e} && $n_crc > $o_crc) {
	   if (($fc eq "InvTxWord" && $diff >= 10) ||
	       ($fc eq "InvCRCCnt" && $diff >= 1) ||
	       ($fc eq "LossSignalCnt" && $diff >= 10))  {
           
   	       $FC{$ctrl} .= "'$fc' on $unit-ctrl$ctrl-$loop of $t3 $id  went from '$o_crc' to '$n_crc'\n";
	   }
        }
     }
  }

  foreach my $c (keys %FC) {
     $fc_err2 = $FC{$c};
     chop($fc_err2);
     Grid->setCode("$t3.AlarmEvent.loop.statistics");
     $hm->valueChangeEvent("controller.u${c}ctr",$fc_err2, $report, $wwn, "Internal Loop statistics changed on 'ctrl$c' of $t3 $id: $fc_err2" , 1,"u$c.loop.statistics");
  }

  my $booting;
  my $comp_list;

# check if disks were replaced or their state has changed.

  for ($x=1; $x <= $UNITS; $x++) {
     my($oserial, $nserial, $oport , $nport);
# CONTROLLER (1)
     if (&controller_present($UNITS, $x)) {
       $comp = "controller.u${x}ctr";
       $booting++ if ($rep->{"$comp.fruStatus"} eq "booting");
       $oserial = NWS::T3->getKey($orep, $comp); 
       $nserial = NWS::T3->getKey($rep, $comp);  

       if (!$nserial && defined($nserial)) {# move the key from old to new
         NWS::T3->copyKey($comp, $orep, $rep);
         $nserial = NWS::T3->getKey($rep, $comp);  
       }

    

       if (&is_state_change($rep, $orep, $comp)) {
         Grid->setCode("$t3.StateChangeEvent.controller");
         $hm->stateChangeEvent($comp, 'status', $report, $rep, $orep, 'CIM_Card', $oserial, $wwn);
       } else {
         if ($rep->{"$comp.fruStatus"} eq "notInstalled") {
           Grid->setCode("$t3.ComponentRemoveEvent.controller");
           $hm->removeCompEvent($comp,$physicalFrame, $report, $orep, 'CIM_Card',$oserial, $wwn,
              {desc => "($oserial -> $nserial)"} );
         } else {
           my($ctrl) = NWS::T3->newController($rep, $comp, $x);
           Grid->setCode("$t3.ComponentInsertEvent.controller");
           $hm->insertCompEvent($comp,$physicalFrame,$report, $orep, $ctrl, $nserial, $wwn,
                 {desc => "($oserial -> $nserial)"} );
         }
       }
       $hm->check_rev($orep, $rep, $report, $wwn, $id, $comp, $nserial);
     }
     if ($x > 1) {
        $comp = "midplane.u${x}mpn";
        $oserial = NWS::T3->getKey($orep, $comp); 
        $nserial = NWS::T3->getKey($rep, $comp);  
        if (&is_state_change($rep, $orep, $comp)) {
           Grid->setCode("$t3.StateChangeEvent.midplane");
           my $e2 = $hm->stateChangeEvent($comp, 'status', $report, $rep, $orep, 'CIM_Card', $nserial, $wwn);
        } else {
           if ($rep->{"$comp.fruStatus"} eq "notInstalled") {
             Grid->setCode("$t3.ComponentRemoveEvent.midplane");
             $hm->removeCompEvent($comp,$physicalFrame,$report, $orep, 'CIM_Card',$oserial, $wwn);
           } else {
             my($part) = NWS::T3->newLoopCard($rep, $comp);
             Grid->setCode("$t3.ComponentInsertEvent.midplane");
             $hm->insertCompEvent($comp,$physicalFrame,$report, $orep, $part, $nserial, $wwn);
           }
        }
     }
# POWER SUPPLY (2)
     for ($y=1; $y <= 2 ; $y++) {
        $comp = "power.u${x}pcu$y";
        $oserial = NWS::T3->getKey($orep, $comp); 
        $nserial = NWS::T3->getKey($rep, $comp);  
        my $devdb = DevDB->read($wwn);
        my $extra;
        if (time - $devdb->{"$comp.refresh_time"} <= 6*60*60) {
           $extra = 'in refreshing cycle';
        }

        if (!$nserial && defined($nserial)) {# move the key from old to new
           NWS::T3->copyKey($comp, $orep, $rep);
           $nserial = NWS::T3->getKey($rep, $comp);  
        }
        my $check_subcomp;
        if (&is_state_change($rep, $orep, $comp)) {
           Grid->setCode("$t3.StateChangeEvent.power");
           $hm->stateChangeEvent($comp, 'status', $report, $rep, $orep, 'CIM_PhysicalPackage', $nserial, $wwn, { info => $extra, noAction => $extra } );
           $check_subcomp = 1;
        } else {
           if ($rep->{"$comp.fruStatus"} eq "notInstalled") {
             Grid->setCode("$t3.ComponentRemoveEvent.power");
             $hm->removeCompEvent($comp, $physicalFrame, $report, $orep, 'CIM_PhysicalPackage',$oserial, $wwn);
           } else {
             my($part) = NWS::T3->newPowerUnit($rep, $comp);
             Grid->setCode("$t3.ComponentInsertEvent.power");
             $hm->insertCompEvent($comp,$physicalFrame,$report, $orep, $part, $nserial, $wwn);
             $check_subcomp = 1;
           }
        }
        if ($check_subcomp) {
           foreach my $el0 ('BatState=battery','Fan1State=fan','Fan2State=fan','PowOutput=output','PowTemp=temp') {
                my($el, $eg) = split(/\=/, $el0);
                Grid->setCode("$t3.AlarmEvent.power.$eg");
                $hm->mapValueChangeEvent($report,  $orep, uc($t3), $comp, "fruPower$el", $shortid, 
                     $id, $wwn, undef, {state => "$comp.fruPower$el", info => $extra} );
           }
        }
        $hm->check_rev($orep, $rep, $report, $wwn, $id, $comp, $nserial);
     }
# LOOP (2)
     for ($y=1; $y <= 2; $y++) {
        $comp = "loop.u${x}lp$y";
        next if (!exists $rep->{"$comp.loopStatus"});

        my $old = $orep->get("$comp.loopStatus");
        my $new = $rep->get("$comp.loopStatus");
        Grid->setCode("$t3.AlarmEvent.backend_loop");
        $hm->mapValueChangeEvent($report, $orep, uc($t3), $comp, "loopStatus", $shortid, $id, $wwn);

        my $old = $orep->get("$comp.loopIsIsolated");
        my $new = $rep->get("$comp.loopIsIsolated");
        #Grid->setCode("$t3.AlarmEvent.backend_loop_split");
        #if ($rep ne $orep || $old) {
        #  $hm->mapValueChangeEvent($report, $orep, uc($t3), $comp, "loopIsIsolated", $shortid, $id, $wwn);
        #}
     }
     my $skip_disks;

# LOOP CARDS (2)
     for ($y=1; $y <= 2; $y++) {
        $comp = "loopcard.u${x}l$y";

        $oserial = NWS::T3->getKey($orep, $comp); 
        $nserial = NWS::T3->getKey($rep, $comp);  
        my $nfruStatus = $rep->get("$comp.fruStatus");  

        if (!$nserial && defined($nserial)) {# move the key from old to new
           NWS::T3->copyKey($comp, $orep, $rep);
           $nserial = NWS::T3->getKey($rep, $comp);  
        }

        if ($UNITS > 1) {
           my $cable  = ($x==1)? 2: 1;
           Grid->setCode("$t3.AlarmEvent.interface.loopcard.cable");
           $hm->mapValueChangeEvent($report, $orep, uc($t3), $comp,
                     "fruLoopCable${cable}State", $shortid, 
                     $id, $wwn, undef, {state => "$comp.fruLoopCable${cable}State"} );
        }
        if (&is_state_change($rep, $orep, $comp)) {
           Grid->setCode("$t3.StateChangeEvent.interface.loopcard");
           my $e2 = $hm->stateChangeEvent($comp, 'status', $report, $rep, $orep, 'CIM_Card', $nserial, $wwn);
           $skip_disks = 1 if ($e2 && $e2->value("Severity") > 1);

        } else {
           if ($rep->{"$comp.fruStatus"} eq "notInstalled") {
             Grid->setCode("$t3.ComponentRemoveEvent.interface.loopcard");
             $hm->removeCompEvent($comp,$physicalFrame,$report, $orep, 'CIM_Card',$oserial, $wwn);
           } else {
             my($part) = NWS::T3->newLoopCard($rep, $comp);
             Grid->setCode("$t3.ComponentInsertEvent.interface.loopcard");
             $hm->insertCompEvent($comp,$physicalFrame,$report, $orep, $part, $nserial, $wwn);
           }
        }
        $hm->check_rev($orep, $rep, $report, $wwn, $id, $comp, $nserial);
     }
     next if ($skip_disks);
# DISKS (9/14)
     my $diskTotal = $rep->get('disk.count') || 9;

     my $devdb = DevDB->read($wwn);
     for ($y=1; $y <= $diskTotal; $y++) {
        $comp = "disk.u${x}d$y";

        $hm->bypass_patch($rep, $orep, $comp);
        $oserial   = NWS::T3->getKey($orep, $comp); 
        $nserial   = NWS::T3->getKey($rep, $comp);  
        my $status  = $rep->get("disk.$comp.fruStatus");

        if (!$nserial && $oserial && $status eq "fault") {# move the key from old to new
           NWS::T3->copyKey($comp, $orep, $rep);
           $nserial = NWS::T3->getKey($rep, $comp);  
        }
        my $p;
        for ($p=0; $p <= 1; $p++) {
          my $crc  = $rep->{"$comp.path${p}InvCRCCnt"};
          my $ocrc = $orep->{"$comp.path${p}InvCRCCnt"};
          if ($crc > $ocrc) {
             $hm->valueChangeEvent($comp,"", $report, $wwn, "CRC of $comp (path $p) on $id changed from $ocrc to $crc", 1,"$comp.statistics");
          }
        }

        if (&is_state_change($rep, $orep, $comp)) {
           $nserial = $wwn if (!$nserial); # Attach event to system if there is no choice
           Grid->setCode("$t3.StateChangeEvent.disk");
           my $ev0 = $hm->stateChangeEvent($comp, 'status', $report, $rep, $orep, 'CIM_PhysicalPackage', $nserial, $wwn);
           $comp_list .= "$comp," if ($ev0 && $ev0->value("Severity") > 1);
           foreach my $el ('Port1State','Port2State') {
             Grid->setCode("$t3.AlarmEvent.disk.port");
             $hm->mapValueChangeEvent($report, $orep, uc($t3), $comp, "fruDisk$el", $shortid, $id, $wwn);
           }
           if ($rep->{"$comp.pathstat"} && $orep->{"$comp.pathstat"}) {
             Grid->setCode("$t3.AlarmEvent.disk.pathstat");
             $hm->mapValueChangeEvent($report, $orep, uc($t3), $comp, "pathstat", $shortid, $id, $wwn);
           }
           
           Grid->setCode("$t3.AlarmEvent.disk.temperature");
           $hm->tempEvent($report, $orep, uc($t3), $comp, "fruDiskTemp", 
                          $shortid, $id, $wwn, 55, 52);
    
        } else {
           if ($rep->{"$comp.fruStatus"} eq "notInstalled") {
               Grid->setCode("$t3.ComponentRemoveEvent.disk");
               $hm->removeCompEvent($comp,$physicalFrame,$report, $orep, 'CIM_PhysicalPackage',$oserial, $wwn);
           } else {
             if ($nserial) {	     
 	       my($part) = NWS::T3->newDiskDrive($rep, $comp);
               Grid->setCode("$t3.ComponentInsertEvent.disk");
               $hm->insertCompEvent($comp,$physicalFrame, $report, $orep, $part, $nserial, $wwn);
	     }  
           }
        }
        $hm->check_rev($orep, $rep, $report, $wwn, $id, $comp, $nserial);
     }

     # DISK THRESHOLDS: GLIST
     #
     Thresholds->Lock();
     for ($y=1; $y <= $diskTotal; $y++) {
        $comp     = "disk.u${x}d$y";
        $oserial  = NWS::T3->getKey($orep, $comp); 
        $nserial  = NWS::T3->getKey($rep, $comp);  

        if ($oserial eq $nserial) {
           Grid->setCode("$t3.AlarmEvent.disk.Glist");
           $hm->thresholdEvent($report, $orep, uc($t3), $comp, "fruDiskGlist", $shortid, $id, $wwn);
        }
     }
     Thresholds->UnLock();

# Volumes (3)
     for ($y=1; $y <= 2; $y++) {
        $comp = "volume.u${x}vol$y";
        my $ostatus  = $orep->{"$comp.volStatus"};
        my $nstatus  = $rep->{"$comp.volStatus"};
        if ($ostatus ne $nstatus) {
           if ($ostatus =~ /mounted/i && $nstatus =~ /deleted/i) {  # remove
              Grid->setCode("$t3.ComponentRemoveEvent.volume");
              $hm->removeCompEvent($comp, $physicalFrame, $report, $orep, 'NWS_StorageVolume',
                       "$wwn.$comp", $wwn, {comp_info => "u${x}vol$y", logicalDevice => 1});
           } elsif ($ostatus =~ /deleted/i && $nstatus != /deleted/i) {                       # insert
              Grid->setCode("$t3.ComponentInsertEvent.volume");
              my $newvol = NWS::T3->newVolume($x, $y, $wwn, $rep);

              $hm->insertCompEvent($comp, $physicalFrame, $report, $orep, $newvol, 
                       "$wwn.$comp", $wwn, { comp_info => "u${x}vol$y",  logicalDevice => 1});
           } else {
              Grid->setCode("$t3.StateChangeEvent.volume");              
	      $hm->stateChangeEvent($comp, 'volume_status', $report, $rep, $orep, 
                         'CIM_PhysicalPackage', "$wwn.u${x}vol$y",  $wwn); # NOTICE
           }
	}

        if (($nstatus) && ($rep->{"$comp.volStatus"} ne "deleted")) { 
          Grid->setCode("$t3.AlarmEvent.cacheMode");
          $hm->mapValueChangeEvent($report, $orep, uc($t3), $comp, "volCacheMode", $shortid, $id, $wwn);
	  
	  Grid->setCode("$t3.AlarmEvent.cacheMirror");
          $hm->mapValueChangeEvent($report, $orep, uc($t3), $comp, "volCacheMirror", $shortid, $id, $wwn) ;

         if (($volVerify ne "Y" && $rep->{"system.sysDiskScrubber"} ne "on")||
              $rep->{"$comp.volOper"} eq "reconstructing" ||
              $rep->{"$comp.volOper"} eq "initializing"  ) {
            $orep->{"$comp.volOper"} = "OK" if (!$orep->{"$comp.volOper"});
            $rep->{"$comp.volOper"}  = "OK" if (!$rep->{"$comp.volOper"});
            my($ostatus) = $orep->{"$comp.volOper"};
            my($nstatus) = $rep->{"$comp.volOper"};
            Grid->setCode("$t3.AlarmEvent.volOper");
            $hm->mapValueChangeEvent($report,$orep,uc($t3),$comp,"volOper",$shortid,$id,$wwn);
          }
        }
     }
  }
  if ($renv->{array_stats_dir} && Timer->isXhours("StatsAudit$wwn", 3)) {
    require Stats;
    my $ST = Stats->read("$t3:$wwn", $renv->{array_stats_dir});
    for ($x=1; $x <= $UNITS; $x++) {
       my $diskTotal = $rep->get('disk.count') || 9;
       for ($y=1; $y <= $diskTotal; $y++) {
          $comp = "disk.u${x}d$y";
          next if (!$rep->{"$comp.fruModel"});
          $ST->update($comp, $rep);
          if (index($comp_list, "$comp,") >= 0) {
             $ST->snapshot($comp);
          }
       }
    }
    $ST->write();
  }

  return if ($booting); # end now if ctrl is booting.

#<Volume Slicing and Masking>
  my $WARNING = $renv->{solution} eq "N" ? 1 : 0;

  if($rep->{"sys.sysVolSliceIsEnabled"} || $orep->{"sys.sysVolSliceIsEnabled"}) {
     Grid->setCode("$t3.AlarmEvent.sysvolslice");
     $hm->mapValueChangeEvent($report,$orep,uc($t3),"sys","sysVolSliceIsEnabled", $shortid,$id,$wwn);
    
    #<lun count> 
    $comp = "volume.count";
    my $ocount = $orep->{$comp};
    my $ncount = $rep->{$comp};
    if ($ocount > $ncount) {
      Grid->setCode("$t3.AlarmEvent.volCount");
      $hm->valueChangeEvent($comp, "", $report, $wwn, 
                "Number of Volume(s) on $t3 $id changed from $ocount to $ncount"  , 
                 0, "volCount");
    }
    #</lun count> 

    #<slice count>
    $comp = "slice.count";
    my $ocount = $orep->{$comp};
    my $ncount = $rep->{$comp};
    if ($ocount > $ncount) {
      Grid->setCode("$t3.AlarmEvent.lunCount");
      $hm->alarmEvent($comp, "", $report, $wwn,
                "Number of LUN(s) on $t3 $id changed from $ocount to $ncount"  , 0, "lunCount");
    }
    #</slice count>

  
    #<lun permision changes>
    for($y = 1; $y <= 16; $y++) {
      #<lun default permision changes>
      my $lun = "lun.".$y;
      my $comp = $lun.".lunMaskAccessDefault";
      if($orep->{$comp} ne $rep->{$comp} && $orep->{$comp} && $rep->{$comp}){
        Grid->setCode("$t3.AlarmEvent.lunPermission");
        $hm->valueChangeEvent($lun, "", $report, $wwn, 
        "Default permission of LUN $comp on $t3 $id have been changed from $orep->{$comp} to $rep->{$comp}.",
           $WARNING, "lunPermission");
      }
      #</lun default permision changes>
    }
  
    #<lun masking permision changes>

    my (@changed_initiators, @union_initiators, %count, $rm_cnt, $ins_cnt);

    my @old_initiators=(grep {/lun.\d+.Initiator.\d+.lunMaskWWN/} (keys %$orep));
    my @new_initiators=(grep {/lun.\d+.Initiator.\d+.lunMaskWWN/} (keys %$rep));

    #find Initiators that were in old but not new

    if ($rep ne $orep) {
      foreach my $e (@old_initiators, @new_initiators) {$count{$e}++}
    }
    foreach my $e (keys %count){
      if ($count{$e} == 1) {
        if (exists $orep->{$e}){
          $rm_cnt++;
        } elsif (exists $rep->{$e}){
          $ins_cnt++;
        }
      } elsif ($count{$e} == 2) {
        push @union_initiators, $e;
      }
    }
  
    foreach my $initiator (@union_initiators){
      if ($orep->{$initiator} ne $rep->{$initiator}){
        Grid->setCode("$t3.AlarmEvent.initiators");
        $hm->valueChangeEvent(undef, "", $report, $wwn, 
          "$initiator on $t3 $id changed from $orep->{$initiator} to $rep->{$initiator}" , $WARNING , "initiators");
      }
    }
    if ($rm_cnt) {
      Grid->setCode("$t3.AlarmEvent.remove_initiators");
      $hm->valueChangeEvent(undef, "", $report, $wwn, "removed $rm_cnt initiators from $t3 $id" , $WARNING, "initiators");
    }
    if ($ins_cnt) {
      Grid->setCode("$t3.AlarmEvent.add_initiators");
      $hm->valueChangeEvent(undef, "", $report, $wwn, "Added $ins_cnt initiators from $t3 $id" , $WARNING, "initiators");
    }
 
    foreach my $initiator (@old_initiators){
      ($comp, my $junk) = split(/lun.\d+.Initiator.\d+/, $initiator);
      my $old_access = $orep->{$comp.".lunMaskAccess"}; 
      my $new_access = $rep->{$initiator.".lunMaskAccess"};
  
      if ($old_access ne $new_access){
        Grid->setCode("$t3.AlarmEvent.lunPermission");
        $hm->valueChangeEvent(undef, "", $report, $wwn, 
          "The access permission on $t3 $id has changed for LUN $comp from $old_access to $new_access.", 
           $WARNING, "lunPermission");
      }
    }
    #</lun masking permision changes>
    #</lun permision changes>
  }
#</Volume Slicing and Masking>
}

# 
# save data from old report in new report if the data is blank
# and the fru is not in 'missing' state.
sub bypass_patch {
  my($class, $rep, $orep, $comp) = @_;
  return if ($orep eq $rep);

  if ($rep->{"$comp.fruState"} &&
        $rep->{"$comp.fruState"} ne "missing" && 
        !$rep->{"$comp.fruSerialNo"} ) {
     foreach my $el ('fruVendor','fruModel','fruSerialNo','fruRevision', 'fruDiskTemp') {
        $rep->{"$comp.$el"} = $orep->{"$comp.$el"} if (exists $orep->{"$comp.$el"});
     }
  }
}

sub controller_present {
  my($tot, $x) = @_;

  if ($tot == 4) {
     return 1 if ($x == 1 || $x == 3);
  } elsif ($tot == 6) {
     return 1 if ($x == 1 || $x == 4);
  } else {
     return 1;
  }
  return 0;
}

sub performance {
  my($class, $rep, $orep, $file, $prefix, $clean) = @_;
  
  my $renv = System->get_renv();
  return if ($renv->{solution} eq "N");  # run only on racks

  my $ARRAY_BLOCK = 512;  # arrays report 512 bytes blocks
            # Array: 0=BlocksRead, 1=BlocksWritten,  =512bytes, / 2048 = megs

  return if (!exists $rep->{"volume.u1vol1.volBlocksRead"});
  return if ($rep eq $orep);
  return if (!exists $orep->{"id.timestamp"});
  my $name = $rep->{'id.device_name'};
  my $time = $rep->{'id.timestamp'} - $orep->{'id.timestamp'};
  return if ($time <= 0);
  my $today = Util->get_today();
  my $hour  = substr($today, 5,8);
  my($x, $u, $v);
  my $UNITS = $rep->get('unit.count') || $rep->get('controller.count');

  require Perf;
  my ($stat1,  $stat3);
  if ($file) {
    $stat1 = Perf->read($file);
    $stat1->cleanOld($clean) if ($clean);
  } else {
    $stat1 = Perf->read("stat1");
    $stat3 = Perf->read("stat3");
    $stat3->cleanHours();
  }
  my $skip;
  for ($u=1; $u <= $UNITS; $u++) {
     for($v=1; $v <= 2; $v++) {
        my $k0 = "u${u}vol${v}";
        my $k1 = "u${u}v${v}";
        my $k = "volume.$k0.vol";
        my @N;
        my $b1 = $rep->{$k . "BlocksRead"};
        my $b2 = $orep->{$k . "BlocksRead"};
        if ($b1 < $b2) {
           $skip = 1; last;
        }
        # BLOCKS CONVERTED to MEGS
        $N[1] = ($b1 - $b2) * $ARRAY_BLOCK / 1024 /1024;
        $N[2] = ($rep->{$k . "BlocksWritten"} - $orep->{$k . "BlocksWritten"})
                 * $ARRAY_BLOCK / 1024 /1024;
        # Cache Misses
        $N[3] = $rep->{$k . "CacheReadMisses"} - $orep->{$k . "CacheReadMisses"};
        $N[4] = $rep->{$k . "CacheWriteMisses"}- $orep->{$k . "CacheWriteMisses"};
        # Soft/Hard Errors
        $N[5] = $rep->{$k . "SoftErrors"} - $orep->{$k . "SoftErrors"};
        $N[6] = $rep->{$k . "HardErrors"} - $orep->{$k . "HardErrors"};
	
	#IOPS
        $N[7] = $rep->{$k . "ReadRequests"} - $orep->{$k . "ReadRequests"};
        $N[8] = $rep->{$k . "WriteRequests"} - $orep->{$k . "WriteRequests"};

        if ($file) {
          $stat1->{"$prefix.$name.$k1"}[0]    += $time;
          for ($x=1; $x <= 8; $x++) {
             $stat1->{"$prefix.$name.$k1"}[$x] += $N[$x];
          }
        } else {
          $stat1->{"total.$name.$k1"}[0]    = $time;
          $stat3->{"$hour.$name.$k1"}[0]    += $time;
          for ($x=1; $x <= 8; $x++) {
             $stat1->{"total.$name.$k1"}[$x] = $N[$x];
             $stat3->{"$hour.$name.$k1"}[$x] += $N[$x];
          }
        }
     }
     last if ($skip);
  }
  if (!$skip) {
    if ($file) {
      $stat1->write($file);
    } else {
      $stat1->write("stat1");
      $stat3->write("stat3");
    }
  }
}

sub check_rev {
  my($hm, $orep, $rep, $report, $wwn, $id, $comp, $serial) = @_;
  my $rev  =  $rep->get("$comp.fruRevision");
  my $orev = $orep->get("$comp.fruRevision");
  my $t3   = $report->id('category');

  if ($rev && $orev && ($rev ne $orev) ) {
     Grid->setCode("$t3.AlarmEvent.revision");
     $hm->valueChangeEvent($comp, "", $report, $wwn, 
                   "Revision of $comp in $t3 $id changed from '$orev' to '$rev'", 1, "revision",
                      { FRU => $serial } );
  }
}

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

   my $state   = $rep->getState("$comp.fruState");
   my $ostate  = $orep->getState("$comp.fruState");
   my $status  = $rep->getState("$comp.fruStatus");
   my $ostatus = $orep->getState("$comp.fruStatus");

   my($old, $new, $sev, $act) = $map->transition("fruStatus.$ostatus-$ostate",
                                       "fruStatus.$status-$state", $orep, $rep);

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

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

   my $status  = $rep->getState("$comp.volStatus");
   my $ostatus = $orep->getState("$comp.volStatus");

   my($old, $new, $sev, $act) = $map->transition("volStatus.$ostatus", "volStatus.$status", $orep, $rep);
   return ("$ostatus", "$status", $old, $new, $sev, $act);
}

  

# static method
# return status-state and 0/1
#  =controller.u1ctr
#  =power.u1pcu1
#  =power.u1pcu2
#  =loopcard.u1l1
#  =loopcard.u1l2
#  =disk.u1d1
#  =disk.u1d2
#  =disk.u1d3
#  =disk.u1d4
#  =disk.u1d5
#  =disk.u1d6
#  =disk.u1d7
#  =disk.u1d8
#  =disk.u1d9


sub compStatus {
   my($hm, $rep, $comp) = @_;

   my($map) = PDM->getDeviceStateMap("t3.availability");

   my($state) = $rep->get("$comp.fruState");
   my($status) = $rep->get("$comp.fruStatus");

   my($new) = $map->get("fruStatus.$status") . $map->get("fruState.$state");

   $new = ($new eq "11")? 1:0;
   return ("$status-$state", $new);
}

1;
