#!/usr/local/bin/perl
#
# $Header: emdb/sysman/admin/scripts/alertlogAdr_util.pl /st_emdbsa_11.2/1 2009/03/12 10:39:00 fagonzal Exp $
#
# alertlogAdr_util.pl
#
# Copyright (c) 2006, 2009, Oracle and/or its affiliates.All rights reserved. 
#
#    NAME
#      alertlogAdr_util.pl - ADR alert log monitoring utilities
#
#    DESCRIPTION
#      Utilities for reading the ADR alert log
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
#    MODIFIED   (MM/DD/YY)
#       fagonzal 02/18/09 - Parsing number of ASM checker failures
#       keiwong  04/03/07 - fix bug 5898320
#       keiwong  02/19/07 - add adrAlertLogDataFailure metric
#       keiwong  09/07/06 - Fixed ADR_HOME path
#       keiwong  07/19/06 - Creation
#

require "emd_common.pl";

use strict;
use File::Spec;
use Time::Local;
use Time::localtime;

use vars qw($STACK_BEGIN_PATTERN $STACK_TIME_PATTERN $STACK_DETAIL_PATH_PATTERN $STACK_TRACE_FILE_PATTERN);
use vars qw($STACK_PROB_KEY_PATTERN $STACK_TXT_BEGIN_PATTERN $STACK_TXT_END_PATTERN $TIMESTAMP_PATTERN);
use vars qw($STACK_ATTR_BEGIN_PATTERN $STACK_ATTR_NAME_PATTERN $STACK_ATTR_VALUE_PATTERN $STACK_ERRID_PATTERN $STACK_NUMBER_OF_FAILURES_PATTERN);

$STACK_BEGIN_PATTERN = q/<msg\b/;
$STACK_TIME_PATTERN = q/\btime=["']([^"']*)["']/;
$STACK_DETAIL_PATH_PATTERN = q#\bdetail_path=["']([^"']*)["']#;
$STACK_TRACE_FILE_PATTERN = q#(.*[/\\\]trace[/\\\].*)#;
$STACK_PROB_KEY_PATTERN = q/\bprob_key=["']([^"']*)["']/;
$STACK_TXT_BEGIN_PATTERN = q/<txt>/;
$STACK_TXT_END_PATTERN = q#</txt>#;
$STACK_ATTR_BEGIN_PATTERN = q/<attr\b/;
$STACK_ATTR_NAME_PATTERN = q/\bname=["']([^"']*)["']/;
$STACK_ATTR_VALUE_PATTERN = q/\bvalue=["']([^"']*)["']/;
$STACK_ERRID_PATTERN = q/\berrid=["']([^"']*)["']/;
#$STACK_NUMBER_OF_FAILURES_PATTERN = q/Checker run found (\d*) new persistent data failure/;
$STACK_NUMBER_OF_FAILURES_PATTERN = q/Checker.*found (\d*) new.*failure/;


$TIMESTAMP_PATTERN = "(\\d+)-(\\d\\d)-(\\d\\d)T(\\d\\d):(\\d\\d):(\\d\\d)\.(\\d+)[-+](\\d\\d):(\\d\\d)";

#
# subroutine get_adr_alert_dir
#  Construct the location of the alert directory in ADR home
#
# $_[0] - value of ADR home
#
sub get_adr_alert_dir
{
    my ($adr_home) = @_;

    my $alert_dir;

    if (!$adr_home)
    {
        return $alert_dir;
    }

    $alert_dir = File::Spec->catdir($adr_home, 'alert');

    EMD_PERL_DEBUG("get_adr_alert_dir: alert_dir=$alert_dir");

    return $alert_dir;
}

#
# subroutine get_adr_alert_logs
#  Return the alert log files in alert directory of ADR, sorted by suffix
#
# $_[0] - value of ADR home
# $_[1] - exclude (1) or include (0, default) directory path
#
sub get_adr_alert_logs
{
    my ($adr_home, $exclude_path) = @_;
    my @log_files = ();

    if (!$adr_home)
    {
        return @log_files;
    }

    my $alert_dir = get_adr_alert_dir($adr_home);

    if (!$alert_dir)
    {
	return @log_files;
    }

    chdir $alert_dir || return;
    opendir ALERT_DIR, "." || return;

    @log_files = grep -f && /^log\.xml(|\.\d+)$/, readdir ALERT_DIR;

    closedir ALERT_DIR;

    if (!$exclude_path)
    {
	@log_files = map { File::Spec->catfile($alert_dir, $_) } @log_files;
    }

    # sort files by suffix
    @log_files = sort { (my $x = $a) =~ s/[^\d]*(|\.(\d+))$/\2/; (my $y = $b) =~ s/[^\d]*(|\.(\d+))$/\2/; $x <=> $y; } @log_files;

    EMD_PERL_DEBUG("get_adr_alert_files: log_files=" . join ',', @log_files);

    return @log_files;
}

#
# subroutine get_first_timestamp
#  Return the timestamp of the first record in the log file
#
# $_[0] - log file
#
sub get_first_timestamp
{
    my ($logFile) = @_;
    my $timestamp;

    if (!open(LOG, "< $logFile"))
    {
        EMD_PERL_WARN("Cannnot open log file $logFile: $!");
	return $timestamp;
    }

    my $stackFound = 0;

    while (<LOG>)
    {
	if (/$STACK_BEGIN_PATTERN/)
	{
	    if ($stackFound)
	    {
		# second stack found, end search
		last;
	    }

	    $stackFound = 1;
	}

	if ($stackFound && /$STACK_TIME_PATTERN/)
	{
	    $timestamp = $1;
	    last;
	}
    }

    close LOG;

    EMD_PERL_DEBUG("get_first_timestamp: timestamp=$timestamp logFile=$logFile");

    return $timestamp;
}

#
# subroutine get_timestamp_number
#  Return a timestamp number for comparison
#
# note: timezone is ignored
#
# $_[0] - timestamp
#
sub get_timestamp_number
{
    my ($timestamp) = @_;
    my $timestampNumber = 0;

    if ($timestamp)
    {
        $timestamp =~ s/-\d+:\d+$//;
        ($timestampNumber = $timestamp) =~ s#[^\d]##g;
    }

    EMD_PERL_DEBUG("get_first_timestamp: timestamp=$timestamp");

    return $timestampNumber;
}

#
# subroutine get_current_alert_logs
#  Return files after a certain timestamp, sorted by first timestamp
#
# note: current log files are defined as files with timestamp in the first
# error stack equal to or greater than the timestamp in consideration
#
# $_[0] - ADR home
# $_[1] - timestamp
#
sub get_current_alert_logs
{
    my ($adrHome, $timestamp) = @_;
    my @currentLogs = ();
    my %timestampNumbers = ();

    my @logs = get_adr_alert_logs($adrHome);

    if (@logs > 0 && $timestamp)
    {
        my $timestampNumber = get_timestamp_number($timestamp);

        foreach my $log (@logs)
        {
            my $firstTimestamp = get_first_timestamp($log);

            if ($firstTimestamp)
            {
                my $firstTimestampNumber = get_timestamp_number($firstTimestamp);
        
                if ($timestampNumber <= $firstTimestampNumber)
                {
                    push @currentLogs, $log;
                    $timestampNumbers{$log} = $firstTimestampNumber;
                }
            }
        }
    }

    # sort files by first timestamp
    @currentLogs = sort { $timestampNumbers{$a} <=> $timestampNumbers{$b}; } @currentLogs;

    EMD_PERL_DEBUG("get_current_alert_files: currentLogs=" . join ',', @currentLogs);

    return @currentLogs;
}

#
# subroutine get_detail_path
#  Return value of the detail_path attribute in a log record
#
# $_[0] - log record
#
sub get_detail_path
{
    my ($logRecord) = @_;

    my $detail_path;

    if ($logRecord =~ /$STACK_DETAIL_PATH_PATTERN/)
    {
        $detail_path = $1;
    }

    EMD_PERL_DEBUG("get_detail_path: detail_path=$detail_path");

    return $detail_path;
}

#
# subroutine get_trace_file
#  Return the trace file in a log record
#
# $_[0] - log record
# $_[1] - ADR home
#
sub get_trace_file
{
    my ($logRecord, $adrHome) = @_;

    my $trace_file = get_detail_path($logRecord);

    if ($trace_file =~ /$STACK_TRACE_FILE_PATTERN/)
    {
        $trace_file =~ s/\?/$adrHome/;
    }

    EMD_PERL_DEBUG("get_trace_file: trace_file=$trace_file");

    return $trace_file;
}

#
# subroutine get_prob_key
#  Return value of the prob_key attribute in a log record
#
# $_[0] - log record
#
sub get_prob_key
{
    my ($logRecord) = @_;

    my $prob_key;

    if ($logRecord =~ /$STACK_PROB_KEY_PATTERN/)
    {
        $prob_key = $1;
    }

    EMD_PERL_DEBUG("get_prob_key: prob_key=$prob_key");

    return $prob_key;
}

#
# subroutine get_errid
#  Return value of the errid attribute in a log record
#
# $_[0] - log record
#
sub get_errid
{
    my ($logRecord) = @_;

    my $errid;

    if ($logRecord =~ /$STACK_ERRID_PATTERN/)
    {
        $errid = $1;
    }

    EMD_PERL_DEBUG("get_errid: errid=$errid");

    return $errid;
}

#
# subroutine get_number_of_failures
#  Return value of the number of failures in the message text
#
# $_[0] - message text
#
sub get_number_of_failures
{
    my ($messageText) = @_;

    my $numberOfFailures;

    if ($messageText =~ /$STACK_NUMBER_OF_FAILURES_PATTERN/)
    {
        $numberOfFailures = $1;
    }

    EMD_PERL_DEBUG("get_number_of_failures: numberOfFailures=$numberOfFailures");

    return $numberOfFailures;
}

#
# subroutine uncdata
#  Return value without CDATA
#
# $_[0] - text
#
sub uncdata
{
    my ($text) = @_;

    $text =~ s# *<!\[CDATA\[##;
    $text =~ s#\]\]> *##;

    EMD_PERL_DEBUG("uncdata: text=$text");

    return $text;
}

#
# subroutine to_ctime
#  Convert timestamp from ISO 8061 format to ctime
#
# $_[0] - timestamp
#
sub to_ctime
{
    my ($timestamp) = @_;

    my $convertedTime;

    if ($timestamp =~ /$TIMESTAMP_PATTERN/)
    {
        $convertedTime = ctime(timelocal($6, $5, $4, $3, $2 - 1, $1));
    }

    EMD_PERL_DEBUG("to_ctime: convertedTime=$convertedTime");

    return $convertedTime;
}

1;
