package Agent;

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

#  $Id: Agent.pm,v 1.16 2006/06/26 18:36:02 mckenney Exp $

use strict;
use Util;
use PDM;
use Carp;
use vars qw($AUTOLOAD);
use System;
use Debug;
use Timelapse;
use Thresholds;
use Report;
use Lease;
use Modules;
use Agent::COMMON;
use Catalog::Entry;
use Catalog::Implementation;

use vars qw(%_FIELDS $WWNINFO $ERROR $DB);

%_FIELDS = ('can_be_turned_off', undef);

#
# $agent = Agent->new({data => $DATA, today => $today, opt_T => $opt_T,
#                   });
# Usage:
# $agent->last_run
#       ->new
#       ->read_log_file 
#       ->format_dev_info 
#       ->ping 
#       ->telnet
#       ->diff 
#       ->load_cache
#       ->save_cache
#

sub new {
  my ($class, $type) = @_;
  if ($type){
	 my $device_type = uc($type);
	 if (!-f System->get_home() . "/lib/Agent/$device_type.pm") {
		return new Agent::COMMON($type);
	 }
	 my $module = "Agent::" . $device_type;
	 my $rc = Modules->loadOne($module);
	 return $module;
  }
  my($this) = {};
  bless($this, 'Agent');
  my $hostid   = `/bin/hostid`; chomp($hostid);

  $this->{hostid} = $hostid;
  return $this;
}

sub init {
  my($agent,$vals) = @_;
  my($x);

  foreach $x (keys %$vals) {
    $agent->{$x} = $vals->{$x};
  }
}

sub setIdSection {
  my($class, $sys_report, $name, $mgmt, $ip, $ipno, $key) = @_;

  $sys_report->{'id.name'}     = $name;
  $sys_report->{"id.mgmtLevel"}= $mgmt;
  $sys_report->{"id.ip"}       = $ip;
  $sys_report->{"id.ipno"}     = $ipno;
  $sys_report->{"id.key"}      = $key;

}


sub copyDev {
  my($class, $dev, $report) = @_;

  foreach my $x (keys %$dev) {
     next if (substr($x,0,1)eq "_");
     $report->{"id.device_$x"} = $dev->{$x};
  }
}

sub new_events {
   my($class, $first_problem, $device, $flag) = @_;

   my $list = PDM->get_events($first_problem);
   my $broke;
   foreach my $ev (@$list) {
     if ($ev->{Severity} >= 1) {
       $broke++;
     }
   }
   $device->{problems}   = $flag if ($broke);

   my $SINFO = System->get_SINFO();
   my $abort;
   if ( exists $SINFO->{inspector_data} ) {
      Debug->print2("Found Inspector_data: aborting agent!");
      $abort = 1;
   }
   return ($broke, $abort);

}


sub lost_comm {
  my($agent, $connect_errs, $device, $type, $string) = @_;

  $DB    = RasDB->new("CACHE");
  my $tk = $DB->hash();
  if ($connect_errs =~ /^$string/){ #can ping , no tokens returned
     # after 3 lost-token in a row, do a comm-lost-event
     my $el = $tk->{"info.$device->{key}"} || {};
     $el->{lost_tokens}++;
     $tk->{"info.$device->{key}"} = $el;
     if ($el->{lost_tokens} <= 2) {
        Debug->print2("   *** can ping this $type but no tokens");
        return 1;
     }
  } else {
     $tk->{"info.$device->{key}"} = {lost_tokens => 0};
  }
  return 0;
}



use vars qw (@FIX);

#################################
##  FIX CONFIG DEVICES
#   relies on ipno to identify the device to update
#################################

sub fixDevice {
  my($class, $h) = @_;
  push(@FIX, $h);
}

# run at the end of the agent.

sub flushFixDevice {
  my($class);
  return if ($#FIX < 0);
  my $renv = System->get_renv();
  my $master = Util->findMaster();
  if (!$master) {
     my($renv, $devs, $hosts, $notifs) = PDM::ConfigFile->read();
     my $update;
     foreach my $fix (@FIX) {
       $update += &fix($devs, $fix);
     }
     PDM::ConfigFile->write($renv, $devs, $hosts, $notifs) if ($update);

  } else {
    require Data::Dumper;
    $Data::Dumper::Indent = 0;
    my($err, $ans)  = Util::Http->appendFile($master, 
                      "tmp/fix_device.info", 
                      Data::Dumper::Dumper(\@FIX), 20);
  }
  @FIX = ();
}

# run at the beginning of the master agent
#
sub processFixDevice {
  my($class) = @_;
  my $master = Util->findMaster();
  return if ($master);
  my $F = System->get_home() . "/DATA/tmp/fix_device.info";
  return if (!-f $F);
  my($renv, $devs, $hosts, $notifs) = PDM::ConfigFile->read();
  rename $F, "$F.save";
  open(O1, "$F.save");
  my($l, $update);
  while ($l = <O1>) {
     if ($l =~ /VAR1/) {
        my $VAR1;
        eval $l;
        foreach my $fix (@$VAR1) {
          $update += &fix($devs, $fix);
        }
     }
  }
  close(O1);
  PDM::ConfigFile->write($renv, $devs, $hosts, $notifs) if ($update);
}

sub fix {
  my($devs, $fix) = @_;
  foreach my $d (@$devs) {
     if ($d->{ipno} eq $fix->{ipno}) {
        foreach my $el (keys %$fix) {
           $d->{$el} = $fix->{$el};
        }
        return 1;
     }
  }
}


#
# returns a list of agents that can be selected (turned on/off)
# used for Email/ hosts etc..
#
sub findSelectableAgents {
  my($class, $solution) = @_;
  my(@MODS, @dirs, $x);
  my $renv = System->get_renv();
  $solution = $renv->{solution} if (!defined($solution));

  my($list) = Util->findMethods("Agent",undef, "isSelectable");
  my $L     = ",6120,6020,t3,switch,switch2,qlogicsw,sve2,host,san,brocade,ve,3510,3511,3320,3310,";
  my $LSE2  = ",dsp,csm200,6020,host,san,ntc,pseq,";
  my $LN    = ",6020,";

  foreach my $f (sort @$list) {
    my(@b) = split(/=/, $f);
    if ($b[1]) {
      my $ag = lc($b[0]);

      if ($solution eq "se") {
         next  if (index($L, ",$ag,") < 0);
      } elsif ($solution eq "se2") {
         next if (index($LSE2, ",$ag,") < 0);
      } elsif ($solution eq "N") {
         next if (index($LN, ",$ag,") >= 0);
      }
      push(@MODS, $ag . "=$b[1]" );
    }
  }

  my $catEntries = Catalog::Entry::listEntries();
  foreach my $catEntry (keys %$catEntries)
  {
     my $catImpl = new Catalog::Implementation($catEntry);
     if ($catImpl->isSelectable() && $catImpl->isEnabled())
     {
     	 my $catType = lc($catEntry);
     	 my $catName = $catImpl->getInfo("displayName");
	 if ($solution eq "se") {
            next  if (index($catType, ",$catType,") < 0);
         } elsif ($solution eq "se2") {
            next if (index($LSE2, ",$catType,") < 0);
         } elsif ($solution eq "N") {
            next if (index($LN, ",$catType,") >= 0);
         }
     	 push(@MODS, $catType . "=" . "$catName");
     }
  }
  return \@MODS;
}



sub newId {
  my($class, $reportCat, $key, $logfile, $displayName) = @_;

  return {category   => $reportCat,
          deviceName => $key,
          logFile    => $logfile,
          display    => $displayName,
         };
}

# sun or brocade

sub switchFCCounters {
  my($class, $TYPE, $arg) = @_;
  require PDM::ConfigFile;

  my($renv, $devs, $host,$notifs) = PDM::ConfigFile->read;
  my(%SW, %DONE);
  my $masterLoc  = Util->findMaster();

  foreach my $d (@$devs) {
    next if ($d->{type} ne $TYPE);
    next if ($d->{active} eq "N");
    next if ($DONE{$d->{wwn}});
    $DONE{$d->{wwn}} = 1;

    next if (!$d->isMineToMonitor() );

    Debug->print2("FC-Switch: Reading switch $d->{name}");

    my $db = $class->FCfromDevice($d);
    next if (!$db);

    foreach my $l (keys %{$db->{enc}}) {
        $SW{enc}{$l} = $db->{enc}{$l};
    }
    foreach my $l (keys %{$db->{data}}) {
        $SW{data}{$l} = $db->{data}{$l};
    }
    #$SW{data_info} = $db->{data_info};
  }
  return \%SW;
}



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

  require Scheduler;
  my $l;
  my($renv)     = System->get_renv();

  my $list = Scheduler->processList("ST", "*", {status => 'O'});
  my (%SC, $sc_list);
  foreach my $l (@$list) {
    $SC{$l->{command}} = 1;
  }
  foreach my $x (keys %SC) {
    $sc_list .= "$x,";
  }

  return $sc_list;

}




sub hostInfo {
  my($class) = @_;
  require PDM;
  my($h) = PDM->getCacheHandle("HostInfo");
  require CIM;
  require Timer;

  if (Timer->isXdays("hostInfo", 1) || !$h->{hostid}) {
    $h->{hostid}   = System->hostid();
    $h->{hostinfo} = System->hostinfo();
    $h->{hostname} = System->hostname();
    $h->{os}       = System->os();
    $h->{os_version} = System->os_version();
    $h->{model}    = System->model();
    $h->{swap}     = System->swap();
    $h->{memory}   = System->memory();
    $h->{domain}   = System->domain();
    $h->{cpus}     = System->cpus();
    $h->{ip}       = Util->name2ip($h->{hostname} );
    $h->{tz}       = CIM->tz();
  }
  return $h;
}



#
# find details about a wwn from OLD_REPORTS

sub wwnInfo {
  my($class, $wwn) = @_;
  my($report, $details, $l, $VAR1);
  my($grepf) = System->get_home() . "/DATA/tmp/WWNINFO";
  my($dir)   = System->get_home() . "/DATA/OLD_REPORTS";

  if (!$WWNINFO) {
     chdir(System->get_home() . "/DATA/OLD_REPORTS/");
     open(O, "/usr/bin/grep -i wwn *:*|");
     open(W, ">$grepf");
     while ($l = <O>) {
       print W $l;
     }
     close(O);
     chmod 0664, $grepf;
     close(W);
     $WWNINFO= 1;
  }
  open(O, $grepf);
  my(@a, $already, @RES);

  while ($l = <O>) {
     if ($l =~ /$wwn/i) {
       @a = split(/:/, $l);
       next if ("$a[0]:$a[1]" =~ /^hbastorage\:hbas\-/);
       next if (index(",master,3310message,3320message,3510message,3511message,message,host,inrange,mcdata,backup,brocade,switch2,qlogicsw", 
                     ",$a[0],") >= 0);
       next if (index($l, "device_wwns") >= 0);
       $report = "$a[0]:$a[1]";
       my $ix = index($l, "=>");
       my $st = length($report);
       if ($ix > $st) {
         $details = Util->ltrim(substr($l, $st+1, $ix - $st - 1));
         $details =~ s/\.portWWN//;
         $details =~ s/\.volWWN//;
       }
       if (index($already, "$a[0]:$a[1],") < 0) {
          $already .= "$a[0]:$a[1],";
          push(@RES, [$report, $details]);
       }
     }
  }
  close(O);
  my($x);
  for ($x=0; $x <= $#RES; $x++) {
     my $res      = $RES[$x];
     my($report)  = $res->[0];
     my($details) = $res->[1];
     next if ($report =~ /^hbastorage/ && $x < $#RES); # don't use hbastorage if there is an
                                                       # alternative
     next if ($report =~ /^hbaswitch/ && $x < $#RES); # don't use hbastorage if there is an
                                                       # alternative

     if (open(O, "$dir/$report")) {
       my @b = <O>; close(O);
       eval "@b";
       if ($VAR1) {
         return ($VAR1->category(), $VAR1->name(), $details, $report);
       }
     } else {
         my($a,$b) = split(/\:/, $report);
         return ($a, $b, $details, $report);
     }
     last;
  }

  return ();
}




#############################################################
#       READK_LOG_FILE
#############################################################


# flag = NEW : new lines since last run
#        24  : lines written in the ast 24 hours.
#
# << Oct092000_16:31:20 >>
# Oct 11 14:52:05
#
  
sub read_log_file {
  my($agent, $file, $flag, $is24, $arg) = @_;
  my(@lines, $startdate, $seek, $size, $tell, $size0, $clear_seek);
  return ('',[]) if (!$file);
  require PDM;
  my $tm = System->get_testMode();
  my $ignoreContent = $arg->{ignoreContent};

  if (!(-r $file && -f $file)) {
    return ("CANNOT_READ $file", []);
  }
  my $lease;
  if (!($lease = Lease->get("logfile.$file", 30, 10)) ) {
    return ("CANNOT_READ $file: lease locked", []);
  }

  $DB = RasDB->new("CACHE");
  my $cache = $DB->hash();

  my $k = "logfile.$file";

  my $today = Util->today;
  if ($cache->{"$k.start"}) {
      $startdate = $cache->{"$k.start"};
  } else {
      $startdate= substr($today,0,5) . " 00:00:00";
  }
  
  Debug->print2("READING_MESSAGE_FILE: $file");

  open(FILE_F1, $file); 
  my $cnt=0;
  $seek = $cache->{"$k.seek"};

  if ($flag eq "NEW") {    # find new stuff.
    $size = (stat($file))[7];
    if ($seek) {
      if ($seek < $size) {
         seek(FILE_F1, $seek, 0);

      } elsif ($seek == $size) {
         close(FILE_F1);
         $lease->release();
         return ('',[]);     # found nothing

      } elsif (-f "$file.0") {
         $size0 = (stat("$file.0"))[7];
         if ($size0 > $seek) {  # do this one also
            open(FILE_F1, "$file.0"); 
            seek(FILE_F1, $seek, 0);
            $clear_seek = 1;
         }
      }
    }         
  }
  my($line, $current);
  my $count;
  for ($tell = tell(FILE_F1); $line = <FILE_F1>; $tell = tell(FILE_F1)) { 
      chop($line);

      if ($ignoreContent) {
         push(@lines, $line) ; $count++;
         next;
      }
      next if (substr($line,0,1) eq "#");
      last if ($line eq "exit"); # for tests only
      if (substr($line,0,1) eq "\t") {
         $lines[$#lines] .=  $line if ($#lines >= 0);
         next;
      }
      my @a = split(/ +/, $line,6);
      if ($a[0] =~ /^\d\d\d\d\-\d\d\-\d\d/) {
        $current = substr($a[0],5) . " " . $a[1];

      } elsif ($a[0] eq "<<") {
        $current = sprintf("%2.2d-%2.2d %s", Util->MTH(substr($a[1],0,3)), 
                       substr($a[1],3,2), substr($a[1],10) );
      } else {
        $current = sprintf("%2.2d-%2.2d %s", Util->MTH($a[0]), $a[1], $a[2]);
      }
      if ($tm) {
         push(@lines, $line); $count++;
         next;
      }
      #next if (substr($current,0,5) gt substr($today,0,5)); # if too big, probably the wrong year.
      if ($flag eq "NEW" &&  $current ge $startdate) {
         push(@lines, $line);
         last if ($count++ > 7000);
      }
      if ($flag eq "24") {
        if ($is24 && $current ge $is24 ) { # find 24 hours for thresholds
          push(@lines , $line);
          last if ($count++ > 7000);
        }
      }
  }
  Debug->print2("Found $count line(s)") if ($count > 0);
  if ($clear_seek) {
    $cache->{"$k.seek"} = 0;
  } elsif ($tell && $tell != $seek) {
    $cache->{"$k.seek"} = $tell;
  }
  close(FILE_F1);
  $cache->{"$k.start"} = $current; # move pointer to end of file.
  $lease->release();

  return ('', \@lines);
}

# sp monitor their devices, master monitor non-sp devices only.
#
sub NUresponsibleForDevice {
  my($class, $d, $masterLoc, $renv) = @_;

  if ($renv->{solution} eq "se") {
    return 0 if (!$d->isMineToMonitor());

  } elsif (!$masterLoc) {  #master
    return 0 if ($d->isInSolution());
  }

  return 1;
}


#
# select devices to monitor

sub deviceList {
  my($class, $static_list, $prefix) = @_;
  my(@LIST);
  my(@a) = split(/=/, $class);
  
  if (!$prefix) {
    $prefix = lc($a[0]);
    $prefix = substr($prefix,7) ;
  }

  my $devs  = System->get_configDevices();
  my $hs    = System->get_configHosts();
  my @hosts;
  foreach my $h (@$hs) {
     push(@hosts, PDM::ConfigFile->hostAsDevice($h));
  }

  foreach my $d (@$devs, @hosts) {
     next if ($d->{type} ne $prefix);
     next if (!Util->isMineToMonitor($d));
     if ($static_list) {
        my $found;
        foreach my $d1 (@$static_list) {
           if ($d1->{key} eq $d->{key}) {
             $found = 1 ; last;
           } 
       }
       next if (!$found);
     }
     push(@LIST, $d);
  }

  return @LIST;
}


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

  return $class->{pdm};
}

sub reportHead {
  my($class, $id, $report, $cols, $errors) = @_;
  my ($name, $cat);
  $cols = 4 if (!$cols);

  if ($report) {
    $name = $report->name();
    $cat = $report->value('id.device_userLabel') || uc($report->category());

  }

  my $e1 = "<tr><td colspan=$cols>
      <font color=red><center>
        Errors during last report generation ($errors), data may be old.</td>" if ($errors);

  return "
   <table border=1 cellspacing=0 cellpadding=1 width=$Style::WIDTH bgcolor=white>
     <tr><td bgcolor=$Style::DARK colspan=$cols>
          <font color=white><b>$cat $name</td>
     $e1
   ";
}
  

sub format_dev_info {
  my($device) = @_;
  my($info);
  if ($device->{serial}) {
     $info = "S" . $device->{serial};
  } else {
     $info = "N" . $device->{type} . "-" . $device->{_name} . "-" . $device->{name} . "-" . $device->{ip};
  }
  $info;
}

sub addIdentification {
  my($agent, $report) = @_;

  my($env) = System->get_renv();
  $report->{'location.customerNo'}   = $env->{cust_no};
  $report->{'location.customerName'} = $env->{customer};
  $report->{'location.contractNo'}   = $env->{contract};
  $report->{'location.hostId'}        = System->hostid;

  $report->{'location.siteName'}      = $env->{site_name};
  $report->{'location.siteAddress'}   = $env->{site_address};
  $report->{'location.siteCity'}      = $env->{site_city};
  $report->{'location.siteState'}     = $env->{site_state};
  $report->{'location.siteCountry'}   = $env->{site_country};
  $report->{'location.siteZip'}       = $env->{site_zip};
  $report->{'location.siteContact'}   = $env->{site_contact};
  $report->{'location.siteEmail'}     = $env->{site_email};
  $report->{'location.ticker'}        = $env->{ticker};
  $report->{'location.production'}    = $env->{production};

}




####################################################
#             DIFF
####################################################
sub get_category {
  my($agent) = @_;
  my($cat) = ref($agent);
  $cat =~ s/Agent//;
  return $cat;
}


sub AUTOLOAD {
  my $self = shift;
  my $type = ref($self) || croak "$self is not an object";

  my $name = $AUTOLOAD;
  $name =~ s/.*://;   # strip fully-qualified portion

  unless (exists $_FIELDS{$name} ) {
   croak "Can't access `$name' field in class $type";
  }
  if (@_) {
     return $self->{$name} = shift;
  } else {
     return $self->{$name};
  }
}


sub found_pattern {
  my($list, $el) = @_;
  my($v);
  my(@a) = split(/,/, $list);
  foreach $v (@a) {
    if ($el =~ /$v/) {
print STDERR "     Ignoring $el in $list \n";
      return 1;
    }
  }
  return 0;
}




#########################################################################
#    CONFIG FILE UTILITIES
#########################################################################

#
# find the right devices in Config for a certain Agent module
# modules are called a5k, a35k, t3, switch, switch.brocade etc..
# types from configfile are
#
sub get_config_devices {
  my($agent) = @_;
  my($d, @results, $type);
  my($ag) = lc(ref($agent));
  $ag = substr($ag, 0, -5) if (substr($ag, -5) eq "agent");

  foreach $d (@{$agent->{devices}} ) {
#     next if ($d->{active} eq "N"); # active is set by the GUI start/stop monitoring
#     and it should only impact the emailing of events, not the monitoring.

     push(@results, $d) if (lc($d->{type}) eq $ag);
  }
  return \@results;
}

sub category_selected {
  my($agent,$cat) = @_;

  my $renv = System->get_renv();
  my $list = lc($renv->{categories});
  $cat = substr($cat,0,-5) if (substr($cat,-5) eq "Agent");

  if (index("|$list|", lc("|$cat|")) >= 0 || lc($cat) eq "events" ||
       index(lc($cat), "message") >= 0) {
     return 1;
  } else {
     return 0;
  }
}

#$log = [ { dsp:2d0000015d208a00:CPCM' => {  # INFO
#               line => '', ip => '', name => '',count = >'', action => 1/0, 
#               egrid => 'log_alert' },
#           dsp:4d0000015d208a00:CPCM' => {  
#               line => '', ip => '', name => '',count = >'', action => 1/0, 
#               egrid => 'log_alert' },
#         },
#         {  dsp:2d0000015d208a00:CPCM' => { # WARNING
#               line => '', ip => '', name => '',count = >'', action => 1/0, 
#               egrid => 'log_alert' },
#         },
#         {  dsp:2d0000015d208a00:CPCM' => { # ERROR
#               line => '', ip => '', name => '',count = >'', action => 1/0, 
#               egrid => 'log_alert' },
#         },
#       ]

sub addMessageLog {
  my($agent, $log, $type, $target, $name, $topic, $sev, $lines, $egrid, $action) = @_;
  my @L = split(/\n/, $lines);
  my $cnt = $#L+1;
  $name = $target if (!$name);
  $action =1 if (!defined($action) && $sev > 1);
  my @T = ('info','warning','error');
  $egrid = $T[$sev] if (!$egrid);

  $log->[$sev]{"$type:$target:$topic"} = {
          line => $lines, 
         count => $cnt, 
         $name => $name,
        action => $action, 
         egrid => $egrid };
}
  
  



1;
