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

#  $Name:  $ 
#  $Id: PDM.pm,v 1.17 2004/10/22 20:20:36 ccadieux Exp $

use strict;
use MaxEvents;
use Thresholds;
use Carp;
use Report;
use Debug;
use Util::Http;
use PDM::ConfigFile;
use System;
use Map;
use State;
use RasDB;
use LockManager;

#
#  Interface:
#   new
#   serialize : called by the displatcher.
#   saveReport:
#   getDocSequence:
#   getLastReport:
#   saveReport:
#   saveMessage:
#   getCacheHandle;
#   inCache :  boolean
#      
use vars qw(@EVENT_DOCS $Error %V @Reports_CB $CACHE %GLOBAL $TOT_DISCO $ED $ED_SEQ $EVENTS $CNT);

$Error      = 0;
%V          = ();
@Reports_CB = ();

sub error {
  return $Error;
}


#  my($pdm) = PDM->new({  dir => "$HOME/DATA",
#                      renv => $renv ,
#                   devices => $devices,
#                     hosts => $hosts,
#                    notifs => $notifs, http => 1});

sub new {
  my($class, $arg) = @_;
  my( $N, %T);
  my($pdm) = $arg;
  my $renv = System->get_renv();
  require NWS::Schema;

  my $D = System->get_home() . "/DATA";

  $pdm->{dir} = System->get_home() . "/DATA" if (!$pdm->{dir});

  if ($arg->{http}) {
    $class->readAllCache();
 
  } else {
#    $N = "$D/OLD_REPORTS";
#    $OLD_REPORTS = &_read($N, ":");

    $class->readAllCache();
    State->init(); 
    NWS::Schema->init();
  }

  my $master = Util->findMaster();  # erase master catalog if config was pushed recently.
  if ($master) {
    my $config_data_age  = (stat(System->get_home() . "/DATA/rasagent.conf"))[9];

    TO->resetIncomplete();
  }

  my($devs) = System->get_configDevices();
  foreach my $d (@$devs) {
     next if (!Util->isMineToMonitor($d));
     $T{lc($d->{type})}{$d->{key}} = 1;
  }
  if (!$renv->{keep_old_reports}) {
    $D = System->get_home() . "/DATA/OLD_REPORTS";
    mkdir $D, 0777 if (!-d $D);
    #
    # REMOVE OLD_REPORTS
    #
    opendir(O1, $D) ;
    my(@a) = readdir(O1); closedir(O1);
    my($VAR1);
    my $max_age = $renv->{"storage_remove_days"} || 40;
    foreach my $f (@a) {
       next if ($f !~ /.+:.+/);
       open(O1, "$D/$f");
       my(@b) = <O1>; close(O1);
       eval "@b";
       my($name) = $VAR1->{_id}{deviceName};
       my($type) = lc($VAR1->{_id}{category});
       my $type0 = $type; $type0 =~ s/message$//;
       my $age   = Util->getFileAge("$D/$f", "d");
       my $rack = ",se,se2" if ($renv->{solution} ne "N");
       my $rack = $renv->{solution} eq "N" ? ",host," : ",se,se2,sp,";

       if ( (index("$rack,hbastorage,hbaswitch,message,san,topo,datahost,master,backup,", ",$type,") < 0) &&
             $name && $type && !$T{$type}{$name} && !$T{$type0}{$name}) {
         unlink "$D/$f";
         State->clearComponent($f);
         Debug->print2("Report $f not in config file, removed.");
       }
       if (index(",hbastorage,hbaswitch,", ",$type,") >= 0 &&  $age > $max_age) {
         unlink "$D/$f";
         State->clearComponent($f);
         Debug->print2("Report $f is $age days old, removed.");
       }
    }
    my $hosts = System->get_configHosts();
    #
    # REMOVE OLD REMOTE_LISTS
    #
    $D = System->get_home() . "/DATA/REMOTE_LISTS";
    opendir(O1, $D) ;
    my @REM = readdir(O1); closedir(O1);
    foreach my $f (@REM) {
       next if (substr($f,0,1) eq ".");
       my $found;
       foreach my $h (@$hosts) {
           if ($h->{hostname} eq $f) {
              $found = 1; last;
           }
       }
       if (!$found && ($f ne $renv->{hostname}) ) {
         unlink "$D/$f";
         Debug->print2("$f removed from REMOTE_LISTS");
       }
    }
  }
  bless($pdm, "PDM");
  return $pdm;
}


sub serialize {
  my($pdm) = @_;

# Each reports has it's own file in the OLD_REPORTS dir.

  $DB::single = 1;
  $pdm->_write($CACHE, "CACHE");
  State->write(1);
}

sub _write {
  my($pdm, $x, $dir) = @_;
  require Data::Dumper;
  my($k);
  my($f) = System->get_home() . "/DATA/$dir";
  mkdir $f, 0777 if (!-d $f);
  $Data::Dumper::Indent = 1;
  foreach $k (keys %$x) {
     open(O, ">$f/$k");
     print O Data::Dumper::Dumper($x->{$k});
     close(O);
  }
}


sub _read {
  my($dir, $pat) = @_;
  my(%O) = ();
  my($file, $v, $l1);
  my($f) = $dir;
  opendir(O, $f);
  my(@f) = readdir(O); closedir(O);

  foreach $file (@f) {
    next if (substr($file, 0,1) eq ".");
    next if ($pat && $file !~ /$pat/);
    open(O, "$f/$file");
    my($VAR1) = undef;
    my($l) = "";
    while ($l1 = <O>) { $l .= $l1}
    close(O);
    eval "$l";
    $O{$file} = $VAR1;
  }
  return \%O; 
    
}

sub writeAllCache {
  my($class) = @_;
  $class->_write($CACHE, "CACHE");
}

sub readAllCache {
  my($class) = @_;
  $CACHE = &_read( System->get_home() . "/DATA/CACHE");
}

sub readCache {
  my($class, $name) = @_;
  my($VAR1);
  my($file) = System->get_home() . "/DATA/CACHE/$name";
  if (open(O, $file)) {
    my(@L) = <O>; close(O);
    eval "@L";
    return $VAR1;
  } else {
    return undef;
  }
}

sub writeCache {
  my($class, $cache, $file) = @_;
  require Data::Dumper;
  open(O, ">" . System->get_home() . "/DATA/CACHE/$file");
  print O Data::Dumper::Dumper($cache);
  close(O);
}

sub writeCacheObject {
  my($class, $file) = @_;
  require Data::Dumper;
  open(O, ">" . System->get_home() . "/DATA/CACHE/$file");
  print O Data::Dumper::Dumper($CACHE->{$file});
  close(O);
}

use vars qw(%Stale);

sub setStaleCatalog {
  my($class ,$host) = @_;

  $Stale{$host} = 1;
}

sub getStaleCatalog {
  my($class) = @_;
  my(@a);
  foreach my $k (keys %Stale) {
    push(@a, $k);
  }
  return @a;
}

# change the key of a  component in the configfile

sub fixConfig {
  my($class, $type, $old, $new, $extra) = @_;
  my $rc;
  my $master  = Util->findMaster();
  if (!$master) {
     $rc = &get_fixConfig({ type => $type, old => $old, new => $new, extra => $extra});
  } else {
     Util::Http->runit($master, "/sbin/change_wwn -o $old -n $new", 20);
  }
  return $rc;
}

sub get_fixConfig {
  my($q) = @_;
  my $warning;
  my ($renv, $devs, $hosts, $notifs) = PDM::ConfigFile->read();
  foreach my $dev (@$devs) {
     if ($dev->{key} eq $q->{old}) {
       $dev->{wwn} = $dev->{key} = $q->{new};
       $warning .= "wwn of device $dev->{name}/$dev->{ipno} changed from $q->{old} to $q->{new}\n";
     }
  }
  if ($warning) {
    open(O, ">>" . System->get_home() . "/DATA/master_warning");
    print O $warning;
    close(O);
    PDM::ConfigFile->write($renv, $devs, $hosts, $notifs);
  }
  my $rc = $warning ? "OK" : "ERR No device found";
  if ($q->{HTTP}) {
     print "$rc\n";
  } else {
     return $rc;
  }
}
  


###########################################################
#  DEVICE STATE
###########################################################

# static method

sub getDeviceStateMap {
  my($pdm, $section) = @_;
  return Map->getDeviceStateMap($section);
}


###########################################################
#  REPORTS STUFF
###########################################################


sub getOldReport {
  my($pdm, $key) = @_;
  return Report->readNew($key);
}

#  name, category, ip, key_type/key_value, report_type, report_value => {};
# Save a new report.
# $arg = {skip_health => 1}

sub saveReport {
  my($pdm, $report) = @_;

# Only update reports if you got a good one.

  return if ($report->status eq Report->STATUS_DONT_SAVE);

  if ($report->status eq Report->STATUS_OK) {
      $report->saveNew();

  } else {  # update report
      $report->updateStatus(); # change the status of the last good report.
  }
  return;
}



###########################################################
#  EVENT STUFF
###########################################################
# 
# Executed on the slave, create a file on the master.
#
sub push {
  my($class, $master) = @_;

  my $renv = System->get_renv();
  return if (!$master);

  my $S = $class->getMessages({severityCount => 1});
  if ($S->[10] > 0) {
    my $text = "TOTAL:$S->[10]\nINFO:$S->[0]\nWARNING:$S->[1]\nERROR:$S->[2]\nDOWN:$S->[3]\n";
    my($err, $ans)  = Util::Http->saveFile($master, "Events/$renv->{hostname}.push",
                          $text, 20);
  }
}

# save locally in case the master is down, 
# send everything when the master is back
#
sub sendMessages {
  my($pdm, $MASTER) = @_;
  my($x, $ed);
  my $renv = System->get_renv();

  my $SD = System->get_home() . "/DATA/SLAVEDATA/$renv->{hostname}";

  if (!open(O3,  ">>$SD")) {
    Debug->err(CANNOT_READ => "SLAVE_DATA $!");
    return;
  }

  $ED = RasDB->new("EDOCS");
  my $hash = $ED->hash();
  $ED->ReadLock();
  foreach my $el (sort keys %$hash) {
     $ed = $hash->{$el};
     print O3 $ed->toC;
  }
  $ED->UnLock();
  close(O3);
       
  my $avail =  Util::Http->getCommand($MASTER, "INFO1", 10);

  if ($avail =~ /version/) {
     my($l, $out);
     if (open(O3, $SD)) {
        while ($l = <O3>) {
          $out .= $l;
        }
        if ($out) {
           Util::Http->appendFile($MASTER, "SLAVEDATA/$renv->{hostname}",
                        $out, 30);
        }
        close(O3);
        unlink $SD;
     }
  }
}


sub storeMessages {
  my($pdm) = @_;
  my($x, $ed);
  my $renv = System->get_renv();

  my $F =  System->get_home() . "/DATA/SLAVE_DATA";
  if (open(O, ">>$F")) {
    $ED = RasDB->new("EDOCS");
    my $hash = $ED->hash();
    $ED->ReadLock();
    foreach my $el (sort keys %$hash) {
       $ed = $hash->{$el};
       my $ev = $ed->instances(0);

       if ($renv->{solution} ne "N") { # racks
          next if ($ev->value("MgmtLevel") eq "DS");
          my $agg_list  = $ed->id("agg_list");
          my $agg       = $ev->value("Aggregate");  # value = 5
          my $desc      = $ev->value("Description");
          my $g_code    = $ev->value("GridCode");
          my $gridInfo  = Grid->getInfoString(undef, undef,  $g_code);
          my $last      = substr($desc,-1);
          if ($gridInfo) {
             $desc     .= "\n$gridInfo" ;
             chomp($desc);
             $desc    .= ":" if ($last eq ":");
          }
          if ($agg > 0) {
             require EventDB;
             my($involved, $sub) = EventDB->sub_events($agg_list, 0);
             $ev->setValue("Data", $sub);
             $desc    .= ":" if (substr($desc,-1) ne ":");
          }
          $ev->setValue("Description", $desc);
       }  
       print O $ed->toC;
    }
    $ED->UnLock();
    close(O);
    System->rotate($F, 10, 1);
  } else {
    Debug->err(CANNOT_READ => "SLAVE_DATA $!");
  }
}

#
# store toString as html file
#
sub docMessage {
  my($class, $edoc) = @_;
  my($etype, $desc);
  my($ev)   = $edoc->instances(0);
  my($cat)  = $edoc->id('category');
  my($renv) = System->get_renv;

  return if (Debug->level() <= 2 && $renv->{hostname} !~ /sun.com/);

  if ($edoc->type() eq 'E') {
    $etype = $ev->propertyByName('EventType')->value;
    $desc = $ev->propertyByName('Caption')->value;
    $desc = substr($desc,0,8) if (substr($etype,0,5) eq "host." && index($desc,".lun.") >= 0);
    $etype .= ".$desc" if ($desc);
    my $D;
    $D = System->get_home() . "/DATA/tos";
    if (-d $D) {
       open(O2, ">$D/$etype.toc");
       print O2 $edoc->toString();
       close(O2);
    }

  } else {
    $etype = $ev->className();
    $desc = $ev->propertyByName('AlertType')->value;
    $etype .= ".$desc" if ($desc);
  }
}

sub getDiscoveryCount {
   return $TOT_DISCO;
}

sub saveMessage {
  my($class, $edoc, $fromSlave, $ignoreMax) = @_;
  my($xx);
  if (!$edoc->instances(0)->type()) {
     Debug->err(CIM_ERR => "Event has no type");
     return;
  }
  $fromSlave = "local" if (!$fromSlave);
  croak("Invalid type for event: " . ref($edoc)) if (ref($edoc) ne "Message");
  $class->docMessage($edoc) ;
  $edoc->id(fromSlave => $fromSlave);

  my $ev      = $edoc->instances(0);
  my $time    = $ev->value("EventTime");
  my $eventId = $ev->value("EventId");
  my $desc    = $ev->value("Description");
  my $target  = $ev->value('Target');
  my $sev     = int($ev->value("Severity")+0.5);
  my $caption = $ev->value('Caption');
  my $deviceName =  $edoc->id('deviceName');
  my $et      = $ev->value("EventType") ;
  $TOT_DISCO++ if ($et =~ /DiscoveryEvent/ || $et =~ /AuditEvent/ );
  my $code    = $ev->value("GridCode");
  my $grid_no = $ev->value("GridNo");
  my $action  = $ev->value("Actionable");
  my $renv    = System->get_renv();

  # 
  # DIAGNOSTIC DISCLAIMER
  #
  if ($action eq "TRUE") { # check if diags were running in last hour
     my $diags = PDM->getCacheHandle("last_diag_run");

     if (exists($diags->{$target}) && (time - $diags->{$target}) < 60 * 60) {
       $ev->setValue("Actionable", "FALSE");
       my $col = substr($desc, -1);
       $desc .= " (This event is now non-actionable because a diagnostic was running in the last hour)";
       $desc .= $col if ($col eq ":");
       $ev->setValue("Description", $desc);
     }
  } 
  # 
  # SERVICE ACTION DISCLAIMER
  #
  if ($sev >= 1) {
     my $lock = LockManager->new();
     my $lock_key = $renv->{solution} eq "N" ?  $target : "system";
     my $locki = $lock->exists($lock_key, 1) || {} ;  # clean up expired  locks
     if ($locki->{info} && index($desc, "action in progress") < 0) {
       my($app, $key0 , $desc2, $date, $expires, $ip, $name) = split(/\|/, $locki->{info});
       my $col = substr($desc, -1);
       my $discl = "$app action in progress [user:$name, start:$date, desc=$desc2]";
       $ev->setValue("Description", $desc);
       $ev->setValue("Disclaimer", $discl);
     }
  }

  if ($fromSlave eq "local") {
       my ($last, $last_email) = MaxEvents->maxEvents("events", $ev, "", 4, 24) if (!$ignoreMax);
       my $col = substr($desc,-1);
       $col = "" if ($col ne ":");
       if ($last_email) {
          $ev->setValue("Description", "$desc (last event on this subject for next 24 hours)$col");
        
       } elsif ($last) {
          Debug->print2("  ==>No event generated: Exceeded 4 events in 24 hours for $target/$caption");
          return undef;
       }
  }

  my $ev_name = $ev->value("EventType");
  my $ev_desc = $ev->value("Description");
  my $ev_sev  = $ev->value("Severity");
  my $ev_act  = $ev->value("Actionable");
  my $ev_cap  = $ev->value("Caption");
  my $agg     = $ev->value("Aggregate");
  my $ix      = index($ev_desc, "INFORMATION:");
  $ix         = index($ev_desc, "PROBABLE-CAUSE:")     if ($ix < 0);
  $ix         = index($ev_desc, "RECOMMENDED-ACTION:") if ($ix < 0);
  $ev_desc    = substr($ev_desc, 0, $ix)               if ($ix > 0);
  Debug->print1("==> New Event: $ev_name: $ev_desc (sev=$ev_sev, act=$ev_act, cap=$ev_cap, etype=$et, no=$grid_no, agg=$agg)");

  if ($renv->{show_cim_events}) {
       print "=====================================\n";
       print $edoc->toString();
  }
  if (System->get_trace_dir()) {
     my $D = $renv->{event_trace_dir} = System->get_home() . "/DATA/TOC";
     mkdir $D, 0777 if (!-d $D);
  }
     
  if ($renv->{event_trace_dir} && -d $renv->{event_trace_dir}) {
        my $et = $ev->value("EventType") . "." . $ev->value("Caption");
        my $out = $edoc->toC();
        my $ts = Util->get_today();
        $ts =~ s/[ \:]//g;
        if (open(OO, ">$renv->{event_trace_dir}/$ts$CNT.$et")) {
           print OO $out;
           close(OO);
           $CNT++;
        }
  }
  push(@EVENT_DOCS, {    Severity => $sev,
                          Caption => $caption, 
                         GridCode => $code,
                          EventId => $eventId,
                        EventType => $et,
                           Target => $target,
                              Key => $deviceName,
                        Component => $ev->value("Component"),
                       });

  $ED = RasDB->new("EDOCS");
  my $hash = $ED->hash();

  $edoc->id("edoc_id", $eventId);
  $hash->{$eventId} = $edoc;
  return $eventId;
}


#
# CALL saveState and log events
#
use vars qw($STATE_DONE);

sub saveStateLog {
  my($pdm, $arg) = @_;
  require EventDB;

  $ED = RasDB->new("EDOCS");
  my $renv = System->get_renv();
  $DB::single = 1;

  my $hash = $ED->hash();
  $ED->ReadLock();
  my (@LIST, @SEV);

  foreach my $el (sort keys %$hash) {
     my $ed        = $hash->{$el};

     my $id        = $ed->id("edoc_id");
     next if (index(",$STATE_DONE,", ",$id,") >= 0);  # don't do the same one twice
     $STATE_DONE .= "$id,";

     my $state     = $ed->state();
     my $ev        = $ed->instances(0);
     my $time      = $ev->value("EventTime");
     my $fromSlave = $ed->id('fromSlave');
     my $desc      = $ev->value("Description");
     my $target    = $ev->value('Target');
     my $display   = $ev->value('DisplayTopic');
     my $sev       = int($ev->value("Severity")+0.5);
     my $caption   = $ev->value('Caption');
     my $deviceName= $ed->id('deviceName');
     my $et        = $ev->value("EventType") ;
     my @et0       = split(/\./, $et);
     my $code      = $ev->value("GridCode");
     my $action    = $ev->value("Actionable");
     my $agg_list  = $ed->id("agg_list");
     my $agg       = $ed->id("aggregate");
     my $solid     = $ev->value("SolutionId");
     my $parent    = $ev->value("Parent");
     my $mgmtLevel = $ev->value("MgmtLevel") ;
     next if ($mgmtLevel eq "DS");

     $agg        = -1  if ($parent);

     my $grid      = Grid->getInfo(undef, undef, {code => $code});

     # LOG EVENT
     $desc =~ s/\n/ /g;
     my $data = "$time\t$et\t$caption\t$desc" .  "\tSev:$sev" .
                "\tAction:$action" .
                "\tEnc:$deviceName"  .  
                "\tmgmtLevel:"  . $ev->value('MgmtLevel') .
                "\tAgentH:"  . $fromSlave  .  
                "\tAgg:"  . $agg .
                "\tECode:"   . $code  . 
                "\tTopic:"   . $display;

     if (substr($desc,-1) eq ":") {  # get more
       my $v1 = substr($ev->value("Data"), 0, 1000);
       my @d1 = split(/\n/, $v1);
       my ($xx);
       if ($#d1 >= 0) {
         $data .= "\n";
         for ($xx = 0; $xx <= $#d1 && $xx < 30; $xx++) {
           last if (index($d1[$xx] , "#INSTRUMENTATION_REPORT") >= 0);
           $data .= "L$d1[$xx]\n";
         }
       }
     }

     next if (index($pdm->remoteOnlyEvents(), ",$et0[1],") >= 0);

     if ($solid && $renv->{solution} eq "N") {
       $pdm->writeTruncate(System->get_home() . "/DATA/EventsSys.log", $data);
       next;
       # no need to run saveState
     } else {
       $pdm->writeTruncate(System->get_home() . "/DATA/Events.log", $data);
     }

     next if (index(",DiscoveryEvent,", ",$et0[1],") >= 0);

     next if ($et0[0] eq "agent" && $sev == 0);  # ship agent events from ALARMS db

     my($key, $keyval);
     if ($state->[2]) {
       ($key, $keyval) = split(/\:/, $state->[2]);
     } else {
       ($key, $keyval) = split(/\:/, $target);
     }
     State->saveState($key, $keyval, $state->[0], $deviceName, 
                      $ed->severity2(), 
                      $desc, $state->[1], $ev , 
		      {   absolute => $state->[3], 
		         aggregate => $agg,
	 		   service => $grid->{service} ? 1:0,
   	                  agg_list => $agg_list,
	              } );
     EventDB->write($ed);
  }

  $ED->UnLock();
  State->cleanState();
}

sub remoteOnlyEvents {
  return ",backup,Backup,PatchInfo,HeartbeatEvent,topoBackup,AuditEvent,Statistics,";
}



#######################
#  EVENT_DOCS functions: these are not CIM events anymore
#######################

sub get_event {
  my($class, $no) = @_;
  return $EVENT_DOCS[$no];
}

sub get_events {
  my($class, $no, $no2) = @_;
  $no2 = $#EVENT_DOCS if (!defined($no2));

  my ($x,@list);

  for ($x=$no+1; $x <= $no2; $x++) {
     push(@list, $EVENT_DOCS[$x]);
  }
  return \@list;
}

sub get_last_event {
  my($class) = @_;
  return $#EVENT_DOCS;
}
###############################




sub setMessages {
  my($pdm, $MESSAGES) = @_;
  return;
  foreach my $m (@$MESSAGES) {
     #push(@EVENT_DOCS, $m);
  }
}


sub writeTruncate {
  my($pdm, $file, $data) = @_;
  if ($data) {
     if (open(O,">>$file")) {
       print O "$data\n";
       close(O);
     } else {
       Debug->err(CANNOT_WRITE => $file);
     }
  }
}


#  Request event-docs from the PDM, executed by providers.
#  $edocs = $pdm->getMessages({ ClassName => "className" });
#  $edocs = $pdm->getMessages({ ClassName => "className" });
#  $edocs = $pdm->getMessages({ ClassName => [<set of classes>] });
#  foreach $ed (@$edocs) {
#    ...
#
sub getMessages {
  my($pdm, $arg) = @_;

  $ED = RasDB->new("EDOCS");
  my $sevCnt = $arg->{severityCount};

  my $hash = $ED->hash();
  $ED->ReadLock();
  my (@LIST, @SEV, $total);

  foreach my $el (sort keys %$hash) {
     my $ed = $hash->{$el};
     if ($sevCnt) {
       my $ev = $ed->instances(0);
       my $sev = $ev->value("Severity");
       $SEV[int($sev)]++;
       $total++;
     } else {
       push(@LIST, $ed);
     }
  }
  $ED->UnLock();
  if ($sevCnt) {
    $SEV[10] = $total;
    return \@SEV;
  } else {
    return \@LIST;
  }
} 

sub getMessagesFromList {
  my($pdm, $idlist) = @_;

  $ED = RasDB->new("EDOCS");
  my $hash = $ED->hash();

  my (@LIST, @SEV);
  $ED->ReadLock();

  foreach my $el (@$idlist) {
     my $ed = $hash->{$el};
     push(@LIST, $ed);
  }
  $ED->UnLock();

  return \@LIST;
} 


sub deleteMessage {
  my($pdm, $ed) = @_;
  $ED = RasDB->new("EDOCS");
  my $hash = $ED->hash();
  my $key = $ed->id("edoc_id");
  delete $hash->{$key};
}

sub deleteMessages {
  my($pdm) = @_;
  $ED = RasDB->new("EDOCS");
  my $hash = $ED->hash();

  $ED->Lock();
  foreach my $el (keys %$hash) {
    delete $hash->{$el};
  }
  $ED->UnLock();
}

sub getEventIdList {
  my($pdm) = @_;
  $ED = RasDB->new("EDOCS");
  my $hash = $ED->hash();
  $ED->ReadLock();
  my @L = (sort keys %$hash);
  $ED->UnLock();
  return \@L;
}

sub getEventCount {
  my($pdm) = @_;
  $ED = RasDB->new("EDOCS");
  my $hash = $ED->hash();
  $ED->ReadLock();
  my @L = (keys %$hash);
  $ED->UnLock();
  return $#L+1;
}

#
# Reports_CB = [health_monitor, $function, $filter]
# filter can be on category, report_type or 'INITIAL'
# INITIAL : will execute automatically at the request of the dispatcher.
#

sub reportRequest {
  my($pdm, $hm, $callback, $filter) = @_;

  push(@Reports_CB, [$hm, $callback, $filter]);

}



%GLOBAL= ();

sub getGlobal {
  my($pdm, $key) = @_;
  my($obj) = {};

  if (!defined($GLOBAL{$key})) {
    $GLOBAL{$key} = $obj;
    return $obj;
  } else {
    return $GLOBAL{$key};
  }
}

  
sub dumpCache {
  my($pdm) = @_;
  $CACHE = undef;
}

# always returns a handle to an object or a new empty object.
#
sub getCacheHandle {
  my($pdm, $key) = @_;
  my($obj) = {};

  if (!$CACHE) {
    my($D) = System->get_home() . "/DATA/CACHE";
    $CACHE = &_read($D);
  }

  if (!defined($CACHE->{$key})) {
    $CACHE->{$key} = $obj;
    return $obj;
  } else {
    return $CACHE->{$key};
  }
}

sub inCache {
  my($pdm, $key) = @_;
  if ($CACHE->{$key}) {
    return 1;
  } else {
    return 0;
  }
}

###########################################################
# MISC.
###########################################################
use vars qw($SEQ);


sub getEventSequence {
  my($pdm) = @_;
  my($s);
 
  $SEQ++;
  return time . "." . System->hostid() .  ".$$.".  sprintf("%.3d",$SEQ);
  
  my($file) = "EventSequence";
  if (open(O, System->get_home() . "/System/$file") ) {
    $s = <O>;
    $s++;
    close(O);
  } else {
    $s = 1;
  }
  open(O , ">" . System->get_home() . "/System/$file");
  print O $s;
  close(O);
  return System->hostid . "." . $s;
}



1;

__END__

=head1 NAME

PDM.pm - Persistent Data Manager


=head3 SYNOPSIS

 use PDM;

 $rep = PDM->new( {...} );


=head3 DESCRIPTION

This module include persistence functions.


=head3 CONSTRUCTOR

=over 1

=item new()


 $pdm => PDM->new( {dir => "cache dir", renv => , $devices => , hosts => , notifs => });

=back 1

=head3 METHODS

=over 4

=item serialize();

Serialize the PDM data stores, called by the dispatcher.

  $pdm->serialize();


=item saveReport();

Saves a report, called by instrumentation

=item getLastReport();

returns an old report.

=item initialHealth();

Execute initial health modules, created with the INITIAL flag.

=item finalHealth();

Execute final health modules, created with the FINAL flag.



=item saveMessage();

Store one Message in memory.

=item storeMessages();

Serialize to the disk all eventDocs, called by slaves dispatchers.

=item getMessages();

returns a list of eventDocs available.

=item reportRequest();

called at the creation of a health monitor to register a callback.

=item getCacheHandle();

Returns a handle to an object given a cache key. If the object does not
exist, it will create an empty one and return a handle so that the caller
can still use the handle to store stuff. This will be serialized by the
dispatcher at the end of the rasagent run.

  Example:
    $h1 = PDM->getCacheHandle('savethis');
    $h1->{'here'} = "some stuff";
    $h1->{'there'} = "more stuff";
    $h1->{'count'}++;

    # This information will be available on the next run of rasagent.>

=item inCache();

returns TRUE if the key has an object in the cache.

=item getTimer($timer);

Will initialize a timer or return the number of minutes since the last reset
of the timer. By default, the timer is 'heartbeat' unless 'timer' is defined.

=item resetTimer($timer);

Will reset a timer to zero. Called manually and by getTimer when no timer is
found.

=item getEventSequence();

Get a unique event sequence.

=item getDocSequence();

Get a unique Document sequence for a set of events.

=back 4

=head3 SEE ALSO




=head3 AUTHOR

 Christian Cadieux (ccadieux@central.sun)



=head3 COPYRIGHT

Copyright (c) 2000 Sun Microsystems

=cut
