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

#  $Name:  $ 
#  $Id: MESSAGE.pm,v 1.3 2004/10/11 19:19:53 ccadieux Exp $

use strict;
use Agent;
use base 'Agent';

sub isSelectable {0}
sub revision {'$Revision: 1.3 $'}

use vars qw(%DB %WARN %NOTICE %ERR $error_cnt);

sub clear {
  %WARN = ();
  %NOTICE = ();
  %ERR = ();
  $error_cnt = 0;
}

sub new {
  my($self) = Agent->new();

  bless ($self, 'Agent::MESSAGE');
  return $self;
}


sub RUN {
  my($agent) = @_;

  my($report, $log_err, $lines, $x, $id);
  $DB::single=1;
  my $renv = System->get_renv();

  #my($today) = Util->today("YMDH");
  #my($lux) = System->find_luxadm;

  my($logfile)   = $renv->{logfile};
  my $processed_list = [];

  %DB = ();
  if (-r $logfile) {
    Debug->print1("Executing agent MESSAGE");
    Timelapse->start("host:logfile");
    ($log_err, $lines) = $agent->read_log_file($logfile,'NEW');
    Debug->err('CANNOT_READ', $logfile) if ($log_err);
    $error_cnt = 0;

    $agent->read_thresholds( $lines);
    $agent->log_thresholds();
    Timelapse->stop("host:logfile");

    if ($error_cnt > 0) {
       my $name   = $renv->{hostname};
       my $hostid = System->hostid();

       $id = {
              category    => Report::CAT_MESSAGE,
              deviceName  => ($renv->{solution} eq "N") ? $name : $hostid,
              display     => $name,
              logFile     => $logfile,
              active      => "Y",
              class       => "host",
              name        => $name,
              ip          => "",
           };

       $report->{"id.name"} = $name;
       $report->{"id.ipno"} = System->get_ipno();
       $report->{warnings} = \%WARN;
       $report->{errors}   = \%ERR;
       $report->{notices}  = \%NOTICE;
       my $new_rep = Report->new($id, $report , undef, Report::STATUS_OK);
       require Health::Message;
       my $broke_list = Health::Message->all_logic($new_rep);
       foreach my $dev (@$broke_list) {
         $dev->{problems} = 2; # broke but not processed yet.
         push(@$processed_list, $dev);
       }
       PDM->saveReport($new_rep);

    }
  }
  return $processed_list;
}


############################################################################
#
# FIND THRESHOLD INFO FROM LOG FILE.
#
sub read_thresholds {
 my($agent, $array) = @_;
 my(@a, $d1, $line, $next, $mysfwarn, $lookahead, $mywarn, $nextline);
 my($myoff, $found, $x);

 $agent->{VM_PRESENT} = "";
 for ($x=0; $x <= $#$array; $x++) {
    if ($array->[$x] =~ /vxdmp\:/ || $array->[$x] =~ /vxio\:/) {
       $agent->{VM_PRESENT} = "VM_";
       last;
    }
 } 

 for ($x=0; $x <= $#$array; $x++) {
   while (1) {
    $line = $array->[$x];

    if ($line =~ /([\w\.]+) nfs: NFS write error on host/) {
       $agent->logfile_inc('NFS_write', "", $line, $1);
       last;
    }
    if ($line =~ / Corrupt label/) {
       $agent->logfile_inc('CORRUPT_LABEL', "", $line);
       last;

    } elsif ($line =~ /SCSI transport failed: reason/) {
       $agent->logfile_inc('SCSI_TRAN_FAILED', "", $line);
       last;

    } elsif ($line =~ /USB flash disk/) {
       $agent->logfile_inc('USB_FLASH_DISK', 0, $line);
       last;

    } elsif ($line =~ /ASC: 0x/ ) {
       $agent->logfile_inc('SCSI_ASC', "", $line);

    } elsif ($line =~ /WARNING: vxvm:vxio:/ ) {
       $agent->writeEvent("W", "VXVM:Volume_Manager", $line);

    } elsif ($line =~ /The recommended firmware version for this application/) {
       $agent->writeEvent("W", "Firmware_Change", $line);
       last;

    } elsif ($line =~ /PWWN=([0-9a-f]+) .*appeared /) {
       my $wwn = $1;
       $agent->writeEvent("W", "Fabric_Warning", $line, $wwn);
       last;

    } elsif ($line =~ /[Ff]ailover/ && $line =~ /scsi_vhci/) {
       my $wwn;
       if ($line =~ /GUID ([0-9a-f]+)/ ) {
          $wwn = uc(substr($1, -18));
       }
       $agent->writeEvent("W", "MPXIO", $line, $wwn);
       last;

    } elsif ($line =~ /mpxio: .*target address: ([0-9a-fA-F]+).*offline/) {
      
       $agent->writeEvent("E", "MPXIO_offline", $line, $1);
       last;

    } elsif ($line =~ /failure prediction threshold exceeded/) {
       my(@m) = split(/ +/, $line);
       my($date) = "$m[0] $m[1] $m[2]";
       my($info) = "";
       foreach my $y  (5,4,3,2,1,0) {
          if ($x - $y >= 0) {
            my($l) = $array->[$x - $y];
            next if ($l =~ /Informational/ || $l =~ /Requested Block:/);
            my($t) = substr($l,16); $t =~ s/ +/ /g;
            $info .= " $t\n";
          }
       }
       $agent->logfile_inc('PFA', 0, $line);
       last;

    } elsif ( ($line =~ /NOTICE: .* OFFLINE/) && ($line =~ /qlc/)) {
       $agent->logfile_inc('QLC_LOOP_OFFLINE', 0, $line);
       last;
    
    } elsif ( ($line =~ /NOTICE: .* ONLINE/) && ($line =~ /qlc/)) {
       $agent->logfile_inc('QLC_LOOP_ONLINE', 0, $line);
       last;
    } elsif ($line =~ /NOTICE: .* OFFLINE/) {
       $agent->logfile_inc('LOOP_OFFLINE', 0, $line);
       last;

    } elsif ($line =~ /NOTICE: .* ONLINE/) {
       $agent->logfile_inc('LOOP_ONLINE', 0, $line);
       last;
    } 
    # JNI messages
    elsif ($line =~ /(jfca.*): Link Down/) {
       $agent->logfile_inc('LINK_DOWN', $1, $line);
       last;

    } elsif ($line =~ /(jfca.*): Link Up/) {
       $agent->logfile_inc('LINK_UP', $1, $line);
       last;
    }

    # Loop Offlines
    if ($line =~ /(socal\d+:\s+port\s*\d+):.*OFFLINE$/) {  
                                                   # port\s+\d+
        $agent->logfile_inc('SF_OFFLINE', $1, $line);
        $agent->logfile_inc('SF_OFFLALERT', $1, $line);
        last;
    }

    if ($line =~ /Error for Command: read\(\d+\)\s+Error Level: Retryable/) {
        $agent->logfile_inc('SCSI_TR_READ', $1, $line);
        last;
    }

    # FC transient write
    if ($line =~ /Error for Command: write\(\d+\)\s+Error Level: Retryable/) {
        $agent->logfile_inc('SCSI_TR_WRITE', $1, $line);
        last;
    }

    # SSD Warnings
    if ($line =~ /WARNING.*\((ssd\d+)\)/) {
        $agent->logfile_inc('SSD_WARN', $1, $line);
        $agent->logfile_inc('SSD_ALERT', $1, $line);
        last;
    }
    # SD Warnings
    if ($line =~ /WARNING.*\((sd\d+)\)/) {
        $agent->logfile_inc('SD_ALERT', $1, $line);
        last;
    }

    # Loop CRC, DMA and Timeout Warnings
    if ($line =~ /WARNING.*\((sf\d+)\)/) {
       $mysfwarn = $1;
       $x++;
       $next = $array->[$x];
       if ($next =~ /CRC/) {
          $agent->logfile_inc('SF_CRC_WARN', $mysfwarn, $line);
          $agent->logfile_inc('SF_CRC_ALERT', $mysfwarn, $line);
          last;
       }
       if ($next =~ /Offline Timeout/) {
          $agent->logfile_inc('SFOFFTOWARN', $mysfwarn, $line);
          last;
       }
       if ($next =~ /INCOMPLETE DMA XFER/) {
          $agent->logfile_inc('SF_DMA_WARN', $mysfwarn, $line);
          last;
  	           }
       # SF Resets
       if ($next =~ /(sf\d+):.*sf_reset/) {
          $agent->logfile_inc('SF_RESET', $mysfwarn, $line);
       }
       # Target and Loop ELS Retry
       if ($next =~ /(sf\d+):\s+ELS\s+.*(target\s+0x[0-9a-f]+)\s+retrying/) {
          $agent->logfile_inc('ELS_RETRY', "$mysfwarn $2", $line);
          $agent->logfile_inc('SF_RETRY', $mysfwarn, $line);
          last;
       }
  
       # Target and Loop ELS Time Outs 
       if ($next =~ /(sf\d+):\s+ELS\s+.*(target\s+0x[0-9a-f]+)\s+timed out/) {
          $agent->logfile_inc('TOELS', "$mysfwarn $2", $line);
          $agent->logfile_inc('SFTOELS', $2, $line);
          last; 
       }
    } 
  
    # Target Offlines
    if ($line =~ /(sf\d+):\s+(target 0x[0-9a-f]+)\s+offline/) {
        $agent->logfile_inc('DDOFFL', "$1 $2", $line);
        last;
    }

    if (1) {
        $lookahead = $array->[$x + 1];
        if ($line =~ /WARNING/ || $line =~ /LIP reset occured/ || 
                  $lookahead =~ /LIP reset occured/ ) {
             if ( $line =~ /\((ssd\d+)\)/ ) {
                ($mywarn) = $1;
                $agent->logfile_inc('SSD_WARN', $1, $line);
                $agent->logfile_inc('SSD_ALERT', $1, $line);
             } elsif ( $line =~ /ifp/ ) {
                 $x++;
                 $nextline = $array->[$x];
                 if ( $line =~ /LIP reset occured/ ) {
                     ($myoff) = $line =~ /(ifp\d+)/;
                     $agent->logfile_inc('SF_OFFLALERT', $myoff, $line);
                     $x--;
                 } elsif ($nextline =~ /LIP reset occured/) {       
                     ($myoff) = $line =~ /(ifp\d+)/;
                     $agent->logfile_inc('SF_OFFLALERT', $myoff, $line);
                 } elsif ($nextline =~ /CRC/) {       
                     # count it against the ifp
                     $agent->logfile_inc('SF_CRC_WARN', $mysfwarn, $line);
                     $agent->logfile_inc('SF_CRC_ALERT', $mysfwarn, $line);
                     my ($tmpt) = $line =~ /target=(0x[0-9a-f]+):/;
                     my $mytarg = "${mysfwarn} target ${tmpt}";

                     # count it against the ifp targ
                     $agent->logfile_inc('DDCRCWARN', $mytarg, $line);
                 } elsif ($nextline =~ /DMA Failure/) {
                     $agent->logfile_inc('SF_DMA_WARN', $mysfwarn, $line);
                 } else {
                     $x--;
                 }
  				
             } elsif ($line =~ /offline/) {
                 my($mysf, $mytarg, $myidx);
                 ($mysf) = $line =~ /(ifp\d+)/;
                 ($mytarg) = $line =~ /(target 0x[0-9a-f]+)/;
                 ($myidx) = "$mysf $mytarg";
                 $agent->logfile_inc('DDOFFL', $myidx, $line);
             }
        }
    }
    last;
   }
  }
}

#
#  $out .= $agent->writeEvent([W/E], "Error xyz", $line);
#  $agent->writeEvent("W", "MPXIO", $line, $wwn);
#
sub writeEvent {
  my($agent, $level, $category,  $mline, $wwn) = @_;

  my(@m) = split(/ +/, $mline, 6);
  my($date) = "$m[0] $m[1] $m[2]";
  my($info) = "$m[3] $m[4] $m[5] $m[6]";
  $info =~ s/"/ /g;
  my($enc_type, $enc, $details, $key) = Agent->wwnInfo(substr($wwn,2)) if ($wwn);
  my($i) = "($details in $enc_type:$enc)" if ($enc);

  my $k0 = substr($key,0,length($enc_type)+1) eq "$enc_type:" ? $key : "$enc_type:$key";

  &store_mess($level, $category, $k0, "$date\tWWN:$wwn\t$info");

  $error_cnt++;
}

%DB = ();

sub logfile_inc {
  my($agent, $type, $idx, $mline, $ip) = @_;

  my($key) = "driver|$agent->{VM_PRESENT}$type|$idx";
  my($wwn);
  my(@m) = split(/ +/, $mline, 6);

  my($p) =  $DB{$key};
  my($date) = "$m[0] $m[1] $m[2]";
  my($info) = "$m[3] $m[4] $m[5] $m[6]";

  if ($idx && $mline =~ /\@w([0-9a-fA-F]+)/) {
     $wwn = $1;
  }

  if (defined($p)) {
    $p->[0] = $date;
    $p->[1] = $info;
    $p->[2]++;
    $p->[3] = $wwn;
    $p->[4] = $ip;

  } else {
    $DB{$key} = [$date, $info, 1, $wwn, $ip]; 
  }

}

#ERR     Mar 6 10:38:25  socal5: port 1: Received 21 fibre.SFOFFLALERT messages (threshold=15)    diag239.Central.Sun.COM socal: [ID 403145 kern.info] ID[SUNWssa.socal.link.5010] socal5: port 1: Fibre Channel is OFFLINE 

sub log_thresholds {
  my($agent, $host_id) = @_;
  my(%MAP) = (W => 'Threshold-Warning', E => 'Threshold-Error');

  my($renv, $devs) = PDM::ConfigFile->read();
  my(%H);
  foreach my $d (@$devs) {
    $H{$d->{ipno}} = $d;
  }

  my($out);
  foreach my $p (keys %DB) {
     my($cat, $type, $idx) = split(/\|/, $p);  #  "driver|$type|$idx";
     my($enc, $details, $enc_type, $key);

     my($val)  = $DB{$p};
     my($cnt)  = $val->[2];
     my($date) = $val->[0];
     my($info) = $val->[1];
     my($wwn)  = $val->[3];
     my $ip    = $val->[4];
     if ($ip) {
        my $ip2 = Util->name2ip($ip);
        if ($H{$ip2}) {
          my $d = $H{$ip2};
          $enc_type = $d->{type};
          $enc  = $d->{name};
          $key  = $d->{key};
        }
     }
     if (!$key) {
        if (length($wwn) > 8 ) {
          ($enc_type, $enc, $details, $key) = Agent->wwnInfo(substr($wwn,2));
          $details =~ s/\.wwn$//;
        }
     }
     my($i) = "($details in $enc_type:$enc)" if ($enc);

     my($level, $rcnt, $desc, $mins, $thr, $thr_time) = 
                     Thresholds->test($cat, $type, $idx.$host_id, $cnt);
     next if (!$level); # N/W/E
     
     my($tx) = "Received $rcnt '$desc' message(s)";
     $tx .= " on '$idx'" if ($idx);
     $tx .= " $i";
     $tx .= " in " . sprintf("%.1f", $mins) . " mins" if ($mins && $thr > 1);
     $thr_time .= ($thr_time =~ /h$/) ? "ours":"ins";
     $tx .= " [threshold is $thr in $thr_time]";
     $info =~ s/"/ /g;
     my $k0;
     if (!$key) {
       $k0 = $key;
     } else {
       $k0 = substr($key,0,length($enc_type)+1) eq "$enc_type:" ? $key : "$enc_type:$key";
     }
     &store_mess($level, $type, $k0, "$date\tWWN:$wwn\t$tx\tLast-Message: '$info'");
     $error_cnt++;
  }
  foreach my $k (keys %ERR) {
    if ($k =~ /^(.+)SF_OFFLALERT$/) {
       my $start = $1;
       delete $WARN{"${start}SF_OFFLINE"};
    }
    if ($k =~ /^(.+)SSD_ALERT$/) {
       my $start = $1;
       delete $WARN{"${start}SSD_WARN"};
    }
    if ($k =~ /^(.+)SF_CRC_ALERT$/) {
       my $start = $1;
       delete $WARN{"${start}SF_CRC_WARN"};
    }
  }
  return (\%NOTICE, \%WARN, \%ERR, $error_cnt);
}

#
# $NOTICE{"t3:t3-serial-no:MPXIO"}
# $NOTICE{"DRIVER::MPXIO"}

sub store_mess {
  my($level, $mess_type, $key, $info) = @_;

  if ($level eq "I") {
     if ($key) {
       $NOTICE{"$key:$mess_type"} .= "$info\n";
     } else {
       $NOTICE{"DRIVER::$mess_type"} .= "$info\n";
     }
  } elsif ($level eq "W") {
     if ($key) {
       $WARN{"$key:$mess_type"} .= "$info\n";
     } else {
       $WARN{"DRIVER::$mess_type"} .= "$info\n";
     }
  } else {
     if ($key) {
       $ERR{"$key:$mess_type"} .= "$info\n";
     } else {
       $ERR{"DRIVER::$mess_type"} .= "$info\n";
     }
  }
}

  


1;

