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

use strict;

use Catalog::Entry;
use Catalog::Message;
use Catalog::Model;
use Catalog::ReportClass;
use Data::Dumper;
use Debug;
use Labels;
use RasDB;
use Thresholds;
use XML::LibXML;

use base 'Catalog::Entry';

sub new {
  my ($class, $type, $dir) = @_;

  my $this = { "type"   => $type,
               "entry"  => "health.xml",
					"dir"    => $dir,
					"model"  => new Catalog::Model($type, $dir)
				 };
  bless($this, "Catalog::HealthConfig");
  return $this->loadEntry();
}

sub load {
  my ($this) = @_;

  my $parser = XML::LibXML->new();
  my $dom;
  eval {
	 $dom  = $parser->parse_file($this->{file});
  };
  if ((!$dom)||($@)){
	 $this->setError("Error parsing XML document element. $@");
	 return;
  }
  my $cmap = {};
  my $dmap = {};
  my $elem = $dom->getDocumentElement();

  foreach my $el ($elem->getChildNodes()){
	 if ($el->getType() == &XML::LibXML::ELEMENT_NODE() ){
		if ($el->getName() eq "class"){
		  $cmap->{$el->getAttribute("name")} = $el;
		}
		if ($el->getName() eq "define"){
		  $dmap->{$el->getAttribute("name")} = $el;
		}
	 }
  }
  $this->{cmap} = $cmap;
  $this->{dmap} = $dmap;
}

sub _eventDescription {
  my ($this, $event, $comp) = @_;
  my $msgs = new Catalog::Message($this->{type}, $this->{dir});
  my $desc = $event->{description};
  my($descArgs, $description) = 
	 $msgs->getComponentDescription($comp, $desc);
  $event->{description} = $description;
  $event->{descArgs}    = "@".$descArgs;
}

sub evaluateRemoval {
  my ($this, $orep) = @_;
  my $cmap       = $this->{cmap};
  my $celem      = $cmap->{$orep->{className}};
  
  my @events;
  my $event = { type      => "ComponentRemoveEvent",
					 component => $orep->getKey(),
				    severity  => "warn",
					 topic     => "remove".$orep->{className},
					 previous  => $orep->getComponentKey()
				  };
  if ($celem){
	 foreach my $el ($celem->getChildNodes()){
		if ($el->getType() == &XML::LibXML::ELEMENT_NODE() ){
		  if ($el->getName() eq "event"){
			 if ("ComponentRemoveEvent" eq $el->getAttribute("type")){
				my $topic = $el->getAttribute("topic");
				my $severity = $el->getAttribute("severity");
				my $description = $el->getAttribute("description");
				$event->{topic}    = $topic if ($topic);
				$event->{severity} = $severity if ($severity);
				$event->{description} = $description if ($description);
			 }	
		  }
		}
	 }
  }

  if (!$event->{description}){
	 my $LB = Labels->read("CommonDesc");
	 my $elemName = $orep->getDisplayName();
	 my $sysName  = $orep->getSystemDisplayName();
	 my $compKey  = $orep->getComponentKey();
	 my($descArgs, $description) =
		$LB->expand2(removeComp => "$elemName", "$sysName", "$compKey", undef);
	 $event->{description} = $description;
	 $event->{descArgs}    = $descArgs;
  }
  else {
	 $this->_eventDescription($event, $orep);
  }

  push (@events, $event);
  return \@events;
}

# Default is an info event.
# Event not generated is start is set because a discovery event is issued.
sub evaluateInsert {
  my ($this, $rep, $start) = @_;
  my $cmap       = $this->{cmap};
  my $celem      = $cmap->{$rep->{className}};
  my $component  = $rep->getKey();

  my @events;
  if (!$start){
	 my $event = { type      => "ComponentInsertEvent",
						component => $component,
						severity  => "info",
						topic     => "add.".$rep->{className},
						value     => $rep->getComponentKey()
					 };
	 if ($celem){
		foreach my $el ($celem->getChildNodes()){
		  if ($el->getType() == &XML::LibXML::ELEMENT_NODE() ){
			 if ($el->getName() eq "event"){
				if ("ComponentInsertEvent" eq $el->getAttribute("type")){
				  my $topic = $el->getAttribute("topic");
				  my $severity = $el->getAttribute("severity");
				  my $description = $el->getAttribute("description");
				  $event->{topic}    = $topic if ($topic);
				  $event->{severity} = $severity if ($severity);
				  $event->{description} = $description if ($description);
				}	
			 }
		  }
		}
	 }
	 if (!$event->{description}){
		my $LB = Labels->read("CommonDesc");
		my $elemName = $rep->getDisplayName();
		my $sysName  = $rep->getSystemDisplayName();
		my $compKey  = $rep->getComponentKey();
		my $status   = $rep->getStatus();
		my($descArgs, $description) =
		  $LB->expand2(insertComp2 => "$elemName", "$sysName", 
							"$status", "$compKey", undef);
		$event->{description} = $description;
		$event->{descArgs}    = $descArgs;
	 }
	 else {
		$this->_eventDescription($event, $rep);
	 }
	 push (@events, $event);
  }

  my $changes = $this->evaluateChange($rep, undef, 1);
  if ($#$changes >= 0){
	 push(@events, @$changes);
  }
  return \@events;
}

sub evaluateChange {
  my ($this, $rep, $orep, $start) = @_;

  my $changeMap  = $this->_getDiffMap($rep, $orep);
  my $cmap       = $this->{cmap};
  my $celem      = $cmap->{$rep->{className}};
  if (!$celem){
	 return undef;
  }
  if ($changeMap){
	 Debug->print2("Change:".$rep->{className}.":".Dumper($changeMap));
  }

  my @events;
  foreach my $el ($celem->getChildNodes()){
	 if ($el->getType() == &XML::LibXML::ELEMENT_NODE() ){
		if ($el->getName() eq "event"){
		  my $event = $this->_handleEventNode
			 ($el, $rep, $changeMap, $start);
		  push (@events, $event) if ($event);
		}
		elsif ($el->getName() eq "property"){
		  my $pname  = $el->getAttribute("name");
		  my $pvalue = $el->getAttribute("value");
		  my $pvalueNot = $el->getAttribute("valueNot");
		  next if (($pvalue) && (!($pvalue eq $rep->getProperty($pname))));
		  next if (($pvalueNot) && ($pvalueNot eq $rep->getProperty($pname)));
		  foreach my $cel ($el->getChildNodes()){
			 if ($cel->getType() == &XML::LibXML::ELEMENT_NODE() ){
				if ($cel->getName() eq "event"){
				  my $event = $this->_handleEventNode
					 ($cel, $rep, $changeMap, $start);
				  push (@events, $event) if ($event);
				}
			 }
		  }
		}
	 }
  }
  return \@events;
}

sub _mapThreshSev {
  my ($this, $sev);
  my $map = { "error" => "E", warn => "W", info => "I" };
  my $msev = $map->{$sev};
  if ($msev){
	 return $msev;
  }
  return "W";
}

# Handle Events
sub _handleEventNode {
  my ($this, $enode, $comp, $changeMap, $start) = @_;

  my $property = $enode->getAttribute("property");
  if (!$changeMap->{$property}){
	 return undef;
  }

  my $values = $changeMap->{$property};
  my $event = { component => $comp->getKey(),
					 property  => $property,
					 available => $enode->getAttribute("available"),
					 type      => $enode->getAttribute("type"),
					 topic     => $enode->getAttribute("topic") ||
					              $comp->getClassName(),
					 severity  => $enode->getAttribute("severity"),
					 previous  => $values->[1],
					 value     => $values->[0]
				  };
  my $condition = 0;
  foreach my $el ($enode->getChildNodes()){
	 if ($el->getType() == &XML::LibXML::ELEMENT_NODE() ){
		if ($el->getName() eq "threshold"){
		  my $count       = $el->getAttribute("count");
		  my $period      = $el->getAttribute("period");
		  my $quietPeriod = $el->getAttribute("quietPeriod");
		  my $severity    = $this->_mapThreshSev($el->getAttribute("severity"));
		  my $text        = $el->getAttribute("description") || $property;
		  my $th   = "$count,$period,$quietPeriod,$severity,$text";
		  return $this->_checkThresholdEvent($event, $comp, $th, $start);
		}
		elsif ($el->getName() eq "change"){
		  next if (($el->getAttribute("start") eq "true") && (!$start));
		  next if (!($event->{value} eq $el->getAttribute("value") ));
		  if ($el->getAttribute("transition") eq "true"){
			 next if (!($event->{previous} eq
							$el->getAttribute("previous")));
		  }
		  return $this->_genValEvent($event, $el, $comp, $start);
		}
		elsif ($el->getName() eq "default"){
		  return $this->_genValEvent($event, $el, $comp, $start);
		}
	 }
  }
  return undef;
}

sub _genValEvent {
  my ($this, $event, $el, $comp, $start) = @_;
  my $severity = $el->getAttribute("severity");
  $event->{severity} = $severity if ($severity);
  if ($event->{severity} eq "ignore"){
	 return undef;
  }
  if (($start) && ($event->{severity} eq "OK")){
	 return undef;
  }

  my $topic = $el->getAttribute("topic");
  if ($topic){
	 $event->{topic} = $topic;
  }

  my $description = $el->getAttribute("description");
  if ($description){
	 $event->{description} = $description;
	 $this->_eventDescription($event, $comp);
  }
  else {
	 my $LB    = Labels->read("CommonDesc");
	 my $propName = $event->{property};
	 my $value    = $event->{value};
	 my $previous = $event->{previous};
	 my $elemName = $comp->getDisplayName();
	 my $sysName  = $comp->getSystemDisplayName();
	 my $compKey  = $comp->getComponentKey();

    #valueChange =  The $1 of $2 on $3 is $4 (previous $5 was $6) $7
	 my($descArgs, $description);
	 if ($previous){
		($descArgs, $description)=
		  $LB->expand2(valueChange => "$propName", "$elemName", "$sysName",
							"$value", "$propName", "$previous", undef);
	 }
	 else {
		($descArgs, $description)=
		  $LB->expand2(valueChange2 => "$propName", "$elemName", "$sysName",
							"$value", undef);
	 }
	 $event->{description} = $description;
	 $event->{descArgs}    = $descArgs;
  }
  return $event;
}

sub _checkThresholdEvent {
  my ($this, $event, $comp, $th, $start) = @_;

  my $cat  = $this->{type};
  my $val  = $event->{property};
  my $type = $comp->{className} . ".$val.";
  my $idx  = $comp->getComponentKey();
  my $idx2 = $comp->getSystemKey();

  my $new = $event->{value};
  my $old = $event->{previous};
  my $cnt = $new - $old;

  Debug->print3("Check Threshold: cat=$cat type=$type cnt=$cnt idx=$idx idx2=$idx2");

  Thresholds->init();
  Thresholds->add($cat, $type, $th);
  my ($sev, $ret, $desc, $mins, $th_count, $th_period) =
	 Thresholds->test($cat, $type, $idx, $cnt, $idx2);

  Debug->print3("Thresh: sev=$sev ret=$ret desc=$desc min=$mins th_count=$th_count th_period=$th_period");
  if ($sev){
	 $event->{info} = "Received $ret $val in $mins mins (value=$new)";
	 return $event;
  }
}


# return has of values that contain [new, old] array.
sub _getDiffMap {
  my ($this, $cur, $old) = @_;
  my %map;

  my $v  = $cur->{vars};

  if (!$old){
	 foreach my $name (keys %$v) {
		$map{$name} = [$v->{$name}, undef];
	 }
  }
  else {
	 my $ov = $old->{vars};

	 foreach my $name (keys %$v) {
		if (!($ov->{$name} eq $v->{$name})){
		  $map{$name} = [$v->{$name}, $ov->{$name}];
		}
	 }
	 foreach my $name (keys %$ov) {
		if (!exists $v->{$name}) {
		  $map{$name} = [undef, $ov->{$name}];
		}
	 }
  }
  return \%map;
}

sub getIDU {
  my($this, $rep, $orep) = @_;

  my $rmap = $rep->getObjectMap();
  if (!$orep){
	 return ($rmap, undef, undef);
  }
  my (%INSERT, %DELETE, %UPDATE);
  my $omap = $orep->getObjectMap();

  foreach my $name (keys %$rmap) {
	 my $comp = $rmap->{$name};
	 next if (! $this->{model}->isDevice($comp->getClassName()));
	 if (!exists $omap->{$name}) {
		$INSERT{$name} = $comp;
    } else {
		my $ocomp      = $omap->{$name};
		$UPDATE{$name} = [$comp, $ocomp];
    }
  }
  foreach my $name (keys %$omap) {
	 my $ocomp = $omap->{$name};
	 next if (! $this->{model}->isDevice($ocomp->getClassName()));
    if (!exists $rmap->{$name}) {
		$DELETE{$name} = $omap->{$name};
	 }
  }
  return(\%INSERT, \%DELETE, \%UPDATE);
}

1;
