# Device State library
#<copyright>
# ----------------------------------------------------------
# Sun Proprietary/Confidential Code
# Copyright 2001, Sun Microsystems, Inc. All rights reserved.
# ----------------------------------------------------------
#</copyright>

package State;

use Util;
#use Time::HiRes qw (usleep);
use strict;
use System;
use Util::Http;
use Data::Dumper;
use Debug;

use vars qw($STATE $state_changed);

# read state file and push to master, must write first.
#
sub push {
  my($class, $master, $force) = @_;

  $master = Util->findMaster;

  return if (!$master);
  return if (!$state_changed && !$force);

  my($renv) = System->get_renv();
  my($F) = System->get_home() . "/DATA/state/" . $renv->{hostname} . ".slave";


  open(O, $F);
  my(@a)  = <O>; close(O);
  Debug->print2("  State: push State to master");

  my($err, $ans)  = Util::Http->appendFile($master, "state/$renv->{hostname}.push",
                              join("", @a), 20);

  if ($ans !~ /OK/) {
     Debug->errNoRepeat(HTTP_STATE => undef, 2, $err);
  } else {
     unlink $F;
  }
}

#
# will merge from a list of hosts and returned a merged state object.
#
sub mergeC {
  my($class, $STATE, $key, $sev1, $date, $mess, $name, $avail, $summ) = @_;

  if (exists $STATE->{C}{$key}) {
     my($sev0) = $STATE->{C}{$key}[0];

     if ($sev1 == 0 || $sev1 > $sev0) {
        $STATE->{C}{$key} = [$sev1, "$date $mess", $name, $avail, $summ];
     }
  } else {
     $STATE->{C}{$key}    = [$sev1, "$date $mess", $name, $avail, $summ];
  }
}

sub mergeL {
  my($class, $STATE, $host, $key, $sev1, $date, $desc, $pc, $ra) = @_;

  if (exists $STATE->{L}{$key}) {
     my($o) = $STATE->{L}{$key};
     $STATE->{L}{$key} = [$sev1, "$date $desc", [$pc, $ra], $o->[3] + 1, $host];
  } else {
     $STATE->{L}{$key} = [$sev1, "$date $desc", [$pc, $ra], 1, $host];
  }
}


sub init {
  my($class) = @_;
  my($master) = Util->findMaster;
  my($D)      = System->get_home() . "/DATA/state";
  my($renv)   = System->get_renv();
  my $host    = $renv->{hostname};
}



sub linkLog {
  my($class, $host, $key) = @_;
  my($renv) = System->get_renv();
  my($q);
  if ($renv->{hostname} eq $host) {
     $q = {key => $key};
     return &get_linkLog($q);
  } else {
     print "accessing $host..<br>\n";
     my $data = Util::Http->getCommand($host, "State::linkLog&key=$key&HTML=1", 30);
     return $data;
  }
}

sub get_linkLog {
  my($q) = @_;
  my($key)  = $q->{key};
  my($out, $l, $x);
  my(@A);
  my $MAX = 3;
  open(O, System->get_home() . "/DATA/LinkStateLog");
  my $in = 0;
  my $cnt = -1;
  while ($l = <O>) {
    chop($l);
    if (substr($l,0,1) eq "L") {
      my @a = split(/\t/, $l);
      $in = ($a[1] eq $key);
      if ($in) {
         my $ii = index($a[4], ")");
         $cnt++;
         $cnt = 0 if ($cnt > $MAX);
         if ($ii > 0) {
           $A[$cnt] = "<b>$a[3]</b> " . substr($a[4],0,$ii+1) . "\n";
         } else {
           $A[$cnt] = "<b>$a[3]</b> $a[4]\n";
         }
      }
    } elsif ($in) {
      $A[$cnt] .= "$l\n";
    }
  }
  for ($x = $cnt; $x >= 0; $x--) {
    $out .= $A[$x] . "\n";
  }
  for ($x = $MAX ; $x > $cnt; $x--) {
    $out .= $A[$x] . "\n";
  }

  if ($q->{HTML}) {
     print $out;
  } else {
     return $out;
  }
}


sub compare {
  my($k, $comp, $DB) = @_;
  if ($comp->[0] > $DB->{$k}[0]) {
     $DB->{$k} = $comp;
  } elsif ($comp->[0] == $DB->{$k}[0]) {
     if ($comp->[1] gt $DB->{$k}[1]) {
        $DB->{$k} = $comp;
     }
  }
}

#
# sends a hash with the highest severity problem for each component
# return $DB : $DB->{"t3:key"} = [highest-sev,desc];
# includeLinks=1, links are attached to both enclosures
# includeLinks=2, links are attached to key=LINK
#
sub getComponentState {
  my($class, $includeLinks) = @_;

  State->read() if (!$STATE);
  my $Comp = $STATE->components();
  my %DB = ();

  foreach my $q (keys %$Comp) {
     if ($Comp->{$q}[0] >= 1) {
       my($t, $n, $p) = split(/:/, $q);
       my $k = "$t:$n";
       &compare($k, $Comp->{$q}, \%DB);
     }
  }
  if ($includeLinks) {
     my $Links = $STATE->links();
     foreach my $el (keys %$Links) {
        my($f1,$f2) = split(/\|/, $el);
        my($t,$n,$p) = split(/:/, $f1);
        my $k = $includeLinks == 2? "LINK" : "$t:$n";
        &compare($k, $Links->{$el}, \%DB);

        ($t,$n,$p) = split(/:/, $f2);
        $k = $includeLinks == 2? "LINK" : "$t:$n";
        &compare($k, $Links->{$el}, \%DB);
     }
  }
  return \%DB;
}

sub getStateList {
  my($class) = @_;

  State->read() if (!$STATE);
  my $Comp = $STATE->components();
  my @ERR;
  foreach my $q (sort keys %$Comp) {
     my $sev = $Comp->{$q}[0];
     next if ($sev < 1);
     my $el = $Comp->{$q};
     #                      dev        desc       name      avail  ev_summ
     push(@ERR, ['C', $sev, $q, undef, $el->[1] , $el->[2], $el->[3], $el->[4]]);
  }
  my $Links = $STATE->links();
  foreach my $el (keys %$Links) {
     my $sev = $Links->{$el}[0];
     next if ($sev < 1);
     my($f1,$f2) = split(/\|/, $el);
     push(@ERR, ['L', $sev, $f1, $f2, $Links->{$el}[1]] );
  }
     
  return \@ERR;
}

sub current {
  my($class) = @_;
  return $STATE if ($STATE);
  return $class->read();
}

#
# read from a state file on a foreign host
sub readFromDevice {
  my($class, $ip, $filename) = @_;
  my $VAR1;
  my($err, $rc) = Util::Http->readFile($ip, "/state/$filename");
  eval $rc;
  return undef if (!$VAR1);
  bless($VAR1, 'State');
  return $VAR1;
}
  
 

#
#  read state for a master/slave host
#
sub read {
  my($class, $host) = @_;
  my($l);
  my($D) = System->get_home() . "/DATA/state";
  my($master) = Util->findMaster;

  my($renv) = System->get_renv();
  $host = $renv->{hostname};
  my($VAR1, @a);
  if (open(O, "$D/$host")) {
    @a = <O>; 
    close(O);
  }
  eval "@a";
  $STATE = $VAR1 || {};

  if ($master) {
     bless($STATE, 'State');
     return $STATE;  
  }

# MASTER: merge all state files into one, the master's 

  opendir(O, $D);
  my @states = readdir(O); close(O);

  my $in = 0;
  my ($last);
  foreach my $st (@states) {
    next if ($st !~ /\.push$/);
    rename "$D/$st", "$D/$st.2";
#    usleep (50000);   # wait 0.05 second
    open(O, "$D/$st.2");
    my $host = substr($st, 0, -5);
    while ($l = <O>) {
       chop($l);
       $in = 1;
       my(@x) = split(/\t/, $l);
       # "C\t$cat:$keyval:$comp\t$sev\t$date\t$mess\t$name\t$avail\n";
       if ($x[0] eq "C") {
          $class->mergeC($STATE, $x[1], $x[2], $x[3], $x[4], $x[5], $x[6], $x[7]);

       } elsif ($x[0] eq "L") {
       # "L\t$key\t$sev\t$date\t$desc\t$pc\t$ra\n";
          $class->mergeL($STATE, $host, $x[1], $x[2], $x[3], $x[4], $x[5], $x[6]);

       } elsif ($x[0] eq "R" || $x[0] eq "W") {
       }
      
    }
    close(O);
  }

  $class->write() if ($in);
  bless($STATE, 'State');
  return $STATE;
}

# 'a5k:5080020000084168:disk_rear.6' => [
#      '0',
#      '08-08 16:44:45 \'disk_rear.6\'(20000020375ba56d) in A5K d (wwn=5080020000084168) is now Available (status-state changed from \'No_path_found-On\' to \'OK-On\'):',
#      'd'
#    ],
#
#   $comps = $state->components();
#   foreach $cname (keys %$comps) {
#      $c = $comps->{$cname};
#      $c->[0]   : state
#      $c->[1]   : message
#      $c->[2]   : enclosure name

sub components {
  my($class) = @_;
  my $comp;
  if ($class eq "State") {
     $class->read() if (!$STATE);
     $class = $STATE;
  }
     
  if (exists($class->{C})) {
    $comp = $class->{C};
  } else {
    $comp = {};
  }
  bless ($comp, "ST_comp");
  return $comp;
}


sub links {
  my($class) = @_;
 
  return $class->{L};
}

#  CLEAR ALL, or ONLY a certain severity
#
sub clearAllComponents {
  my($class, $sev) = @_;

  if (!$STATE) {
     $class->read();
  }
  my $C = $STATE->{C};
  foreach my $k (keys %$C) {
    if (!defined($sev) || $STATE->{C}{$k}[0] == $sev) {
      $STATE->{C}{$k}[0] = -2;
    }
  }
}

sub clearAllLinks {
  my($class, $sev, $pattern) = @_;

  if (!$STATE) {
     $class->read();
  }
  my $L = $STATE->{L};
  foreach my $k (keys %$L) {
    next if ($pattern && index($k, $pattern) < 0);
    if (!defined($sev) || $STATE->{L}{$k}[0] == $sev) {
      $STATE->{L}{$k}[0] = -2;
    }
  }
}



sub clearComponent {
  my($class, $obj) = @_;

  if (!$STATE) {
     $class->read();
  }
  my $C = $STATE->{C};
  foreach my $k (keys %$C) {
    if (substr($k,0,length($obj)) eq $obj) {
       $STATE->{C}{$k}[0] = -2;
    }
  }
}


sub clear {
  my($class, $obj) = @_;

  $class->{C}{$obj}[0] = -2;
}

sub clearLink {
  my($class, $obj) = @_;

  if (defined($class->{L}{$obj})) {
    $class->{L}{$obj}[0] = -2;
    if (defined( $class->{L}{$obj}[3])) {
      $class->{L}{$obj}[3] = 0;
    }
  }
}



#
# only writes local state table.
#
sub write {
  my($class, $host) = @_;
  my($master) = Util->findMaster;
 # return if ($master); 

  return if (!$STATE);
  my($renv) = System->get_renv();
  my($F) = System->get_home() . "/DATA/state/" . $renv->{hostname};
  open(O, ">$F");
  $Data::Dumper::Indent = 1;
  print O Dumper($STATE);
  close(O);
}


# this runs on the host that has data-path access.

# State->saveLinkState($type1, $key1, $type2, $key2,
#         Message::SEVERITY_WARNING, $desc, $reads, $writes, $pc, $ra);
# probable cause, recomm actions

sub saveLinkState {
  my($class, $cat1, $node1, $cat2, $node2, $sev, $desc, $reads, $writes, $ev) = @_;

  $cat1 = lc($cat1);
  $cat2 = lc($cat2);
  my($key) = "$node1|$node2";
  my($data, $x);

  my($master) = Util->findMaster;
  my($D)      = System->get_home() . "/DATA/state/";
  my($renv)   = System->get_renv();
  my $host    = $renv->{hostname};
  my $date    = Util->today();
  $desc       =~ s/[\|,]/ /g;
  $state_changed = 1;

# 
# SAVE LOCAL LINK LOG INFO
#
  my $stateLog = System->get_home() . "/DATA/LinkStateLog";
  open(W, ">>$stateLog");
  print W "L\t$node1|$node2\t$sev\t$date\t$desc\n";

  for ($x=0; $x <= $#$reads && $x < 10; $x++) {
    print W "R\t$reads->[$x]\n";
  }
  for ($x=0; $x <= $#$writes && $x < 10; $x++) {
    print W "W\t$writes->[$x]\n";
  }
  close(O);
  Debug->truncate($stateLog, 1);

# SLAVE
  if ($master) {
     open(O, ">>$D/$host.slave");
     print O "L\t$key\t$sev\t$date\t$desc\t\t\n";
     close(O);
     #return;
  }

# MASTER or SLAVE

  if (!$STATE) {
     $class->read();
  }
  my($sev0)  = $STATE->{L}{$key}[0];
  my($cnt)   = $STATE->{L}{$key}[3];

  $STATE->{L}{$key} =
     [$sev,"$date $desc", ["",""], $cnt+1, $host];  # 0=ok, 1=w, 2=err,3=down
}

# ->saveState($cat, $name, $keyval,
#         Message::SEVERITY_DOWN, "Lost Communication");
#            4=notpresent, 3=down, 2=error,1=warn,0=ok
#  this runs on the host that has instrumentation agent running.

sub saveState {
  my($class, $cat, $keyval, $comp, $name, $sev, $mess, $avail, $ev) = @_;

  my $summ;
  $cat = lc($cat);
  $name = $keyval if (!$name);
  $comp = "e" if (!$comp);  # enclosure
  
  if ($ev) {
    $summ = "EventType="  . $ev->value("EventType") . 
            "|Target="    . $ev->value("Target") .
            "|TargetName=". $ev->value("TargetName") .
            "|Caption="   . $ev->value("Caption") .
            "|Actionable=". $ev->value("Actionable") .
            "|SourceIP="  . $ev->value("SourceIP") .
            "|Severity="  . $ev->value("Severity") .
            "|PriorState="    . $ev->value("PriorState") .
            "|CurrentState="  . $ev->value("CurrentState");

    my $org = $class->eventHash($ev->value("OriginalEvent"));
    if ($org->{Target}) {
      $summ .= "|OrigEventType="  . $org->{EventType}  .
               "|OrigTarget="     . $org->{Target}     .
               "|OrigTargetName=" . $org->{TargetName} .
               "|OrigCaption="    . $org->{Caption};
    } else {
      $summ .= "|CurrentValue="  . $ev->value("CurrentValue");
    }
  }

  my($master) = Util->findMaster;
  my($D)      = System->get_home() . "/DATA/state";
  my($renv)   = System->get_renv();
  my $host    = $renv->{hostname};
  my $date    = Util->today();

  if ($master) { # a slave
     $state_changed = 1;
     open(O, ">>$D/$host.slave");
     print O "C\t$cat:$keyval:$comp\t$sev\t$date\t$mess\t$name\t$avail\t$summ\n";
     close(O);
     #return;
  }
  if (!$STATE) {
     $class->read();
  }
  my($sev0) = $STATE->{C}{"$cat:$keyval:$comp"}[0];

  if ($sev == 0 || $sev >= $sev0) {
    $state_changed = 1;
    $mess =~ s/[\|,]/ /g;
                                        # 0=ok, 1=w, 2=err,3=d own
    $STATE->{C}{"$cat:$keyval:$comp"} = [$sev,"$date $mess", $name, $avail, $summ];  
  }
  $comp = $comp;
}

sub eventHash {
  my($class, $comp) = @_;
  my %H;
  my @L= split(/\|/, $comp);
  foreach my $el (@L) {
    my $ix = index($el, "=");
    $H{substr($el,0,$ix)} = substr($el,$ix+1) if ($ix > 0);
  }
  return \%H;
}


sub isChanged {
  return $state_changed;
}

package ST_comp;

sub value {
  my($comp, $val, $ix) = @_;

  if (exists($comp->{$val})) {
    if (defined($ix)) {
      return $comp->{$val}[$ix];
    } else {
      return $comp->{$val};
    }
  }
  if (defined($ix)) {
    return undef;
  } else {
    return [];
  }
}





1;



