# $Id: Catalog.pm,v 1.1 2004/11/11 19:54:20 jkremer Exp $
# Copyright 2004 Sun Microsystems, Inc., All Rights Reserved.
# Sun Proprietary/Confidential: For Internal Use Only
package Health::Catalog;

use strict;
use Health;
use base 'Health';

use Catalog::ReportClass;
use Catalog::HealthConfig;
use Catalog::Model;
use Data::Dumper;
use Debug;
use Message;
use NWS::COMMON;
sub NWS_module {
  return "NWS::COMMON";
}

sub new {
  my ($class, $type, $catdir) = @_;
  my $this = {
              type => $type,
              hc   => new Catalog::HealthConfig( $type, $catdir)
             };
  bless($this, "Health::Catalog");
  return $this;
}

sub LOGIC {
  my($hm, $report, $device) = @_;
  my $oreport    = PDM->getOldReport($report->fileKey);
  my $rep        = $report->content;
  my $orep       = ($oreport) ? $oreport->content() : $rep;
  my $deviceName = $report->id('deviceName');
  my $type       = $hm->{type};
  my $hc         = $hm->{hc};

  $hm->{device}     = $device;
  $hm->{deviceName} = $deviceName;
  $hm->{report}     = $report;
  $hm->{rep}        = $rep;
  $hm->{orep}       = $orep;
  if ($hm->connectionEvent($deviceName, $report, {method => 3} )) {  # IB / OOB
    return;
  }

  my $rc  = createFromReport Catalog::ReportClass($rep);
  my $orc = createFromReport Catalog::ReportClass($orep);

  $hm->doAudit($report, $oreport);
  $hm->locationChangeEvent($report, $rep, $orep, $deviceName);

  my $ip  = $rep->get('system._IPAdress') || $rep->get('id.device_ipno');
  my $sysName = $rep->get('system._Name') || $rep->get('id.device_name');

  $hm->{nwssystem} = CIM::Instance->new
    ('NWS_System',
     [
      [Name                 => $deviceName    ],
      [OtherIdentifyingInfo => $rep->get("id.device_ipno") ],
      [SystemName           => $sysName ],
      [LogInfo              => ""  ],
      [IP                   => $ip ],
      [FruCount             => $rep->get('fru.count')  ],
      [FruCtlrCount         => $rep->get('ctrl.count') ],
      [FruDiskDriveCount    => $rep->get('disk.count') ],
      [FruPowerCount        => $rep->get('pcu.count')    ],
      [VolCount             => $rep->get('volume.count') ],
      [SysRevision          => $rep->get('system._Version')],
      [Caption              => uc($type) ],
     ]);
  $hm->{physicalFrame} = CIM::Key->new
    (['CIM_PhysicalFrame',
      Tag               => $deviceName ,
      CreationClassName => 'CIM_PhysicalFrame' ]);

  my $start;
  if (!$oreport){
    $start = 1;
	  Debug->print3("Running health for the first time.");
  }
  my($insert, $delete, $update) = $hc->getIDU($rc, $orc);

  my $rmap = $rc->getObjectMap();
  my $omap = $orc->getObjectMap();

  foreach my $comp (keys %$insert) {
    my $events = $hc->evaluateInsert($insert->{$comp}, $start);
	 Debug->print3("Insert " . $insert->{$comp}->{className} .".".
						$comp . " " . $#$events+1 . " Events");
	 $hm->genEvents($events, $rmap, $omap);
  }
  foreach my $comp (keys %$delete) {
    my $events = $hc->evaluateRemoval($delete->{$comp});
	 Debug->print3("Delete " . $delete->{$comp}->{className} .".".
						$comp . " " . $#$events+1 . " Events");
	 $hm->genEvents($events, $rmap, $omap);
  }
  foreach my $comp (keys %$update) {
    my $comps = $update->{$comp};
    my $events = $hc->evaluateChange($comps->[0], $comps->[1], $start);
	 if ($#$events >= 0){
		Debug->print3("Update " . $comps->[0]->{className} ."." . $comp .
						  " " . $#$events+1 . " Events");
	 }
	 $hm->genEvents($events, $rmap, $omap);
  }
}

sub genEvents {
  my($this, $events, $rmap, $omap) = @_;

  if($#$events >=0){
	 for(my $i = 0; $i <= $#$events; $i++){
		my $event = $events->[$i];
		my $comp = $rmap->{$event->{component}};
		my $oldc = $omap->{$event->{component}};
		$this->handleEvent($event, $comp, $oldc);
	 }
  }
}

sub handleEvent {
  my($this, $event, $comp, $ocomp) = @_;

  my $etype = $event->{type};
  if ("ComponentRemoveEvent" eq $etype){
    $this->genDeleteCompEvent($event, $ocomp);
  }
  elsif ("ComponentInsertEvent" eq $etype){
    $this->genInsertCompEvent($event, $comp);
  }
  elsif ("StateChangeEvent" eq $etype){
    $this->genChangeCompEvent($event, $comp, $ocomp);
  }
  elsif ("ValueChangeEvent" eq $etype){
    $this->genChangeCompEvent($event, $comp, $ocomp);
  }
}

sub eventSeverity {
  my($this, $event) = @_;
  return $this->mapSeverity($event->{severity});
}
sub eventActionable {
  my($this, $event) = @_;
  return $this->mapActionable($event->{actionable}, $event->{severity});
}
sub eventAvailable {
  my($this, $event) = @_;
  if ($event->{available} eq "true"){
	 return 1;
  }
  return 0;
}
sub mapSeverity {
  my($this, $severity) = @_;
  if ("warn" eq $severity){
    return Message::SEVERITY_WARNING;
  }
  elsif ("error" eq $severity){
    return Message::SEVERITY_ERROR;
  }
  elsif ("down" eq $severity){
    return Message::SEVERITY_DOWN;
  }
  return 0;
}

sub mapActionable {
  my($this, $actionable, $severity) = @_;
  if ($actionable){
    if ("true" eq $actionable){
      return 1;
    }
    return 0;
  }
  if ($severity > 0){
    return 1;
  }
  return 0;
}


# Generate a delete component event.
sub genDeleteCompEvent {
  my($this, $event, $comp) = @_;
  my $device     = $this->{device};
  my $report     = $this->{report};
  my $severity   = $this->eventSeverity($event);
  my $compName   = $comp->getDisplayName();
  my $compKey    = $comp->getComponentKey();
  my $phyName    = $comp->getProperty("_Realized");
  my $mgmtLevel  = $device->{mgmtLevel} || 'D';
  my $part       = NWS::COMMON->insert($report->content, $comp->{path},
						 						  $this->{nwssystem},
												  $this->{physicalFrame});
  $this->gridCode($event);

  my $ev =  CIM::Instance->new
	 ('NWS_Event',
	  [
		[ EventType    => $this->{type} ."." . $event->{type} ],
		[ Caption      => $event->{topic} ],
		[ DisplayTopic => $compName ],
		[ Description  => $event->{description} ],
		[ DescArgs     => $event->{descArgs} ],
		[ MgmtLevel    => $mgmtLevel ],
		[ PriorValue   => $compKey ],
		[ Target       => $device->{type} . ":" . $device->{key}],
		[ TargetName   => $device->{name}],
		[ Severity     => $severity ],
		[ Actionable   => $this->eventActionable($event) ],
		[ Component    => $compName ],
		[ SourceIP     => $device->{ipno} || $device->{ip} ],
		[ EventId      => PDM->getEventSequence ]
	  ]
	 );
  my $sd = Events->sourceDetector({ event => $ev });

  $DB::single = 1;

  my @pert;
  if (!$phyName) {
	 my $class = Catalog::Model->getNWSPhysicalClassName($comp->{className});

	 my $pertains = CIM::Instance->new
		 ('NWS_EventPertainsToLogicalDevice',
		  [
			[ Event       => $ev  ],
			[ Element     => $part->[0] ],
		  ]
		 );
     push(@pert, $pertains);
  } else {
	 my $class = Catalog::Model->getNWSPhysicalClassName($comp->{className});
	 my $key   = CIM::Key->new
		( [$class,
			CreationClassName => $class,
			Tag               => $phyName]);
	 my $location = $comp->getFRUProperty("_Location") || $compName;
	 my $contain = CIM::Instance->new
		( 'CIM_Container',
		  [
			[GroupComponent => $this->{physicalFrame}], # frame is a package
			[PartComponent           => $key],          # package is an element
			[LocationWithinContainer => $location ],
			[DiscoveryType           => "D"   ],        # delete
		  ]
		);

	 my $pertains = CIM::Instance->new
		 ('NWS_EventPertainsToPhysicalElement',
		  [
			[Event          => $ev   ],
			[Element        => $key  ],
		  ]
		 );
	 push(@pert, $contain, $pertains);
  }

  my $ed = Message->new( { id        => $report->id,
									state     => [$compName, 0],
									instances => [$ev, @$sd, @pert],
									severity  => $severity } );

  PDM->saveMessage($ed);
}

# Handle a componet insert.
# comp is a ReportClass instance of the new component from the new report.
sub genInsertCompEvent {
  my($this, $event, $comp) = @_;

  my $device     = $this->{device};
  my $report     = $this->{report};
  my $severity   = $this->eventSeverity($event);
  my $compName   = $comp->getDisplayName();
  my $compKey    = $comp->getComponentKey();
  my $phyName    = $comp->getProperty("_Realized");
  my $mgmtLevel  = $device->{mgmtLevel} || 'D';
  my $part       = NWS::COMMON->insert($report->content, $comp->{path},
						 						  $this->{nwssystem},
												  $this->{physicalFrame});

  $this->gridCode($event);

  my $ev =  CIM::Instance->new
	 ('NWS_Event',
	  [
		[ EventType    => $this->{type} ."." . $event->{type} ],
		[ Caption      => $event->{topic} ],
		[ DisplayTopic => $compName ],
		[ Description  => $event->{description} ],
		[ DescArgs     => $event->{descArgs} ],
		[ MgmtLevel    => $mgmtLevel ],
		[ CurrentValue => $compKey ],
		[ Target       => $device->{type} . ":" . $device->{key}],
		[ TargetName   => $device->{name}],
		[ Severity     => $severity ],
		[ Actionable   => $this->eventActionable($event) ],
		[ Component    => $compName ],
		[ SourceIP     => $device->{ipno} || $device->{ip} ],
		[ EventId      => PDM->getEventSequence ]
	  ]
	 );
  my $sd = Events->sourceDetector({ event => $ev });
 $DB::single = 1;
  my @pert;
  if (!$phyName) {
     my $pertains = CIM::Instance->new
		 ('NWS_EventPertainsToLogicalDevice',
		  [
			[ Event       => $ev  ],
			[ Element     => $part->[0]],
		  ]
		 );
     push(@pert, $pertains);
  } else {
    my $pertains = CIM::Instance->new
		('NWS_EventPertainsToPhysicalElement',
		 [
		  [ Event       => $ev  ],
		  [ Element     => $part->[1]],
		 ]
		);
	 my $location = $comp->getFRUProperty("_Location") || $compName;
    my $contain = CIM::Instance->new
		('CIM_Container',
		 [
		  [GroupComponent           => $this->{physicalFrame} ],
		  [PartComponent            => $part->[1] ],
		  [LocationWithinContainer  => $location ],
		  [DiscoveryType            => "A" ],
		 ]
		);

     push(@pert, $pertains, $contain);
  }

  my $ed = Message->new( { id     => $report->id,
                        state     => [$compName, 1],
                        instances => [$ev, @$sd, @$part, @pert],
                        severity  => $severity } );

  PDM->saveMessage($ed);
}


# Generate a component value or state change event.
# comp is a ReportClass instance of the new component from the new report.
# data is supporting data of the change:
#   "Received $cnt '$pre0$desc' in $mins mins (value=$new)\n";
sub genChangeCompEvent {
  my($this, $event, $comp) = @_;

  my $device     = $this->{device};
  my $report     = $this->{report};
  my $severity   = $this->eventSeverity($event);
  my $compName   = $comp->getDisplayName();
  my $compKey    = $comp->getComponentKey();
  my $phyName    = $comp->getProperty("_Realized");
  my $mgmtLevel  = $device->{mgmtLevel} || 'D';
  my $part       = NWS::COMMON->insert($report->content, $comp->{path},
						 						  $this->{nwssystem},
												  $this->{physicalFrame});
  $this->gridCode($event);

  my $ev =  CIM::Instance->new
	 ('NWS_Event',
	  [
		[ EventType    => $this->{type} ."." . $event->{type} ],
		[ Caption      => $event->{topic} ],
		[ DisplayTopic => $compName ],
		[ Description  => $event->{description} ],
		[ DescArgs     => $event->{descArgs} ],
		[ MgmtLevel    => $mgmtLevel ],
		[ CurrentValue => $event->{value} ],
		[ PriorValue   => $event->{previous}],
		[ Target       => $device->{type} . ":" . $device->{key}],
		[ TargetName   => $device->{name}],
		[ Severity     => $severity ],
		[ Actionable   => $this->eventActionable($event) ],
		[ Component    => $compName ],
		[ SourceIP     => $device->{ipno} || $device->{ip} ],
		[ EventId      => PDM->getEventSequence ],
		[ Data         => $event->{info} ],
	  ]
	 );

  my $sd = Events->sourceDetector({ event => $ev });
  my ($key, $pertainsName);
 $DB::single = 1;
  if ($phyName) {
	 $pertainsName = "NWS_EventPertainsToPhysicalElement";
	 $key = CIM::Key->new(['CIM_PhysicalPackage',
									  Tag               => $phyName,
									  CreationClassName => 'CIM_PhysicalPackage']);
  } else {
	 $pertainsName = "NWS_EventPertainsToSystem";
	 $key = CIM::Key->new( ['NWS_System',
									'Name'       => $device->{key},
									CreationClassName => 'NWS_System']);
  }
  my $pertains = CIM::Instance->new
	 ($pertainsName, [[ Event => $ev ], [ Element => $key ] ]);

  my $ed = Message->new
	 ({ id        => $report->id,
		 state     => [ $compName, $this->eventAvailable($event)],
		 instances => [$ev, @$sd, $pertains ],
	  });
  PDM->saveMessage($ed);
}

sub check_rev {
  my ($this, $comp, $ocomp);

  my $serial  = $comp->getProperty("_Realized");
  return if (!$serial);

  my $display = $comp->getProperty("_ElementName");

  my $rev  = $comp->getFRUProperty("_Firmware");
  my $orev = $ocomp->getFRUProperty("_Firmware");

  if ($rev && $orev && ($rev ne $orev) ) {

    my $cat = $this->{type};
    my $id  = $this->{report}->id('display');
    my $LB  = Labels->read("CommonDesc");
    my($desc, $descE) = $LB->expand2(revision => "$display($serial)",
                                     "$cat $id", "'$orev'", "'$rev'");

    Grid->setCode("$cat.AlarmEvent.revision");
    $this->valueChangeEvent($comp->{path}, "", $this->{report},
                          $this->{displayName}, $desc, 1, "revision", 
                          { display_comp => $display, descE => $descE } );
  }
}

# Define the Grid Code for an Event
sub gridCode {
  my($this, $event) = @_;
  my $code = $this->{type} . "." . $event->{type} . "." . $event->{topic};
  Grid->setCode($code);
}

# Check and do Audit event if needed.
sub doAudit {
  my ($this, $report, $oreport) = @_;
  my $key   = $this->{deviceName};
  my($freq) = 7; # $renv->{audit_freq} || 7;
  my $audit = System->get_audit() ? "YES" : Timer->isXdays("Audit$key", $freq);
  my $run_audit;

  if (!$oreport || $audit eq "YES" ) {
     my $nws_module = $this->NWS_module();
     $this->discoveryEvent($report, $nws_module, $audit);
  }
}


1;
