#!/usr/bin/perl -w

use strict;
use Date::Format qw(time2str);
use Getopt::Long qw(GetOptions);

use constant ALARM_COLUMNS => qw(target host service status plugin message);

my %List;

sub add_value {
    my ( $option, $column, $value ) = @_;
    $List{$option}{$column}{$value} = undef;
}

GetOptions(
    'ge=s%'              => \my %Ge,
    'lt=s%'              => \my %Lt,
    'min-abstand=f'      => \my $MinAbstand,
    'office-hours!'      => \my $Office,
    'template=s'         => \my $Template,
    'output-separator=s' => \( my $OutputSeparator = "\t" ),
    'print=s'            => \my @Print,
    'variable=s%'        => \my %Variable,
    'help|?'             => sub {
        exec perldoc => -F => $0 or die qq(Cannot exec "perldoc -F $0": $!\n);
    },
    map +( "$_=s%" => \&add_value ),
    qw(is isnt match nomatch)
) or exit 1;

$MinAbstand *= 60 if $MinAbstand;

{
    my $calendar;

    sub is_holiday($) {
        require Date::Calendar;
        require Date::Calendar::Profiles;
        no warnings 'once';
        $calendar = Date::Calendar->new(
            $Date::Calendar::Profiles::Profiles->{'DE-BY'}
              or return -1
        ) or return -1;
        {
            no warnings 'redefine';
            *is_holiday = \&_is_holiday;
        }
        goto &_is_holiday;
    }

    sub _is_holiday($) {
        my ($time) = @_;
        my @date = ( localtime $time )[ 5, 4, 3 ];
        $date[0] += 1900;
        $date[1]++;
        if    ( $calendar->is_full(@date) ) { 1 }
        elsif ( $calendar->is_half(@date) ) { 2 }
        else                                { '' }
    }
}

sub is_office_hours($) {
    ( undef, undef, my $hour, undef, undef, undef, my $wday ) =
      localtime( my $time = shift );
    not $wday < 1 || $wday > 5 || $hour < 8 || $hour > 17 || is_holiday($time);
}

my ( @alarme, %Benachrichtigungen, $last );
Line: while (<>) {

    my ( $timestamp, $type, $details ) =
      /^\[(\d+)\] (HOST|SERVICE) NOTIFICATION: (.*)/
      or next;

    next if defined $Office && ( $Office xor is_office_hours($timestamp) );

    my %alarm;
    {
        chomp $details;
        my @details = split /;/, $details;
        splice @details, 2, 0, '' if $type eq 'HOST';
        @alarm{ (ALARM_COLUMNS) } = @details;
    }

    $alarm{timestamp} = $timestamp;
    $alarm{time}      = time2str( '%X', $timestamp );
    $alarm{date}      = time2str( '%Y-%m-%d', $timestamp );

    keys %{ $List{is} };
    while ( my ( $key, $value ) = each %{ $List{is} } ) {
        next Line unless exists $value->{ $alarm{$key} };
    }

    keys %{ $List{isnt} };
    while ( my ( $key, $value ) = each %{ $List{isnt} } ) {
        next Line if exists $value->{ $alarm{$key} };
    }

    keys %Ge;
    while ( my ( $key, $value ) = each %Ge ) {
        next Line if $alarm{$key} lt $value;
    }

    keys %Lt;
    while ( my ( $key, $value ) = each %Lt ) {
        next Line if $alarm{$key} ge $value;
    }

    keys %{ $List{match} };
    while ( my ( $key, $value ) = each %{ $List{match} } ) {
        $alarm{$key} =~ $_ or next Line for keys %$value;
    }

    keys %{ $List{nomatch} };
    while ( my ( $key, $value ) = each %{ $List{nomatch} } ) {
      Match: {
            $alarm{$key} !~ $_ and last Match for keys %$value;
            next Line;
        }
    }

    unless ( defined $last && $MinAbstand && $timestamp - $last < $MinAbstand )
    {
        if ( defined $Template ) { push @alarme, \%alarm }
        else {
            print @Print ? join( $OutputSeparator, @alarm{@Print} ) . "\n" : $_;
        }
    }

    $last = $timestamp;
}

if ( defined $Template ) {
    require Template;
    my $template = Template->new( { ABSOLUTE => 1 } );
    $template->process( $Template,
        { alarme => \@alarme, variable => \%Variable, } )
      or die $template->error;
}

__END__

=head1 NAME

analysiere_nagioslog - Auswertung Alarmierungen anhand nagios.log

=head1 SYNOPSE

    analysiere_nagioslog -no-office-hours \
                         -is target=hotline_voice \
                         -isnt status=OK -isnt status=UP \
                         -print host -print service \
                         /var/log/nagios/archives/nagios-06-??-2007-??.log

=head1 BESCHREIBUNG

Das Script sucht aus den auf der Kommandozeile angegebenen oder ber die
Standardeingabe verftterten C<nagios.log>-Daten alle Benachrichtigungen
heraus.
Diese knnen durch verschiedenen L</OPTIONEN> weiter eingegrenzt werden.
Die schlielich ausgewhlten Benachrichtigungen werden in einem fr eine
Nachverarbeitung (vgl. RT#349530) geeigneten Format ausgegeben.

=head1 ATTRIBUTE EINER BENACHRICHTIGUNG

=over 4

=item timestamp

in Unixzeit (Sekunden seit 1970)

=item date

im Format JJJJ-MM-TT

=item time

im Format HH:MM:SS

=item target

=item host

=item service

ist leer bei Host-Benachrichtigungen

=item status

=item plugin

=item message

=back

=head1 OPTIONEN

=head2 zur Selektion der zu betrachtenden Benachrichtigungen

=over 4

=item -is Attribut=Wert

nur Benachrichtigungen betrachten, bei denen das angegebene Attribut den
spezifizierten Wert hat.
Die Option kann mehrfach fr dasselbe Attribut verwendet werden, um
unterschiedliche Werte auszuwhlen.

=item -isnt Attribut=Wert

Benachrichtigungen ausschlieen, bei denen das angegebene Attribut den
spefizierten Wert hat.
Die Option kann mehrfach fr dasselbe Attribut verwendet werden, um mehrere
Werte auszuschlieen.

=item -ge Attribut=Wert

Benachrichtigungen ausschlieen, bei denen das angegebene Attribut einen
asciibetisch kleineren als den spezifizierten Wert hat.

=item -lt Attribut=Wert

Benachrichtigungen ausschlieen, bei denen das angegebene Attribut den
spezifizierten oder asciibetisch greren Wert hat.

=item -match Attribut=RegExp

nur Benachrichtigungen betrachten, bei denen das angegebene Attribut zum
spezifizierten regulren Ausdruck passt.
Wird die Option mehrfach fr dasselbe Attribut verwendet, werden nur
Benachrichtigungen ausgewhlt, bei denen das Attribut zu I<allen> angegebenen
regulren Ausdrcken passt.

=item -nomatch Attribut=RegExp

Benachrichtigungen ausschlieen, bei denen das angegebene Attribut zum
spezifizierten regulren Ausdruck passt.
Wird die Option mehrfach fr dasselbe Attribut verwendet, werden nur
Benachrichtigungen ausgeschlossen, bei denen das Attribut zu I<allen>
angegebenen regulren Ausdrcken passt.

=item -office-hours

Nur Benachrichtigungen auswhlen, die whrend unserer Geschftszeiten erfolgten.

=item -no-office-hours

Nur Benachrichtigungen auswhlen, die auerhalb unserer Geschftszeiten
erfolgten.

=back

=head2 zur Festlegung des Ausgabeformats

=head3 mittels Template

=over 4

=item -template Template-Datei

Dem L<Template> wird ggf. eine Liste C<alarme> bergeben, die pro Alarm
einen Hash mit jeweils den o.g. Attributen als gleichnamige Variablen enthlt.

Bei Verwendung dieser Option werden die folgenden Optionen ignoriert.

=item -variable Name=Wert

Variable, die direkt ans L<Template|/-template> bergeben wird.

=back

=head3 ohne Template

Wir kein L<Template|/-template> angegeben, so wird zu jedem ausgewhlten Alarm
eine Zeile ausgegeben, deren Inhalt durch die folgenden Optionen festgelegt
werden kann.

=over 4

=item -print Attribut

angegebenes Attribut ausgeben.
Kann mehrfach verwendet werden, um mehrere Attribute in der entsprechenden
Reihenfolge auszugeben.

=item -output-separator Zeichenkette

Feldtrenner, wenn L</-print> mehrfach verwendet wird.
Default: TAB

=back

Wird keine dieser Optionen verwendet, so werden die ausgewhlten Alarme im
Original-C<nagios.log>-Format ausgegeben.

=head2 sonstige

=over 4

=item -help

=item -?

um (nur) diese Dokumentation anzeigen zu lassen

=back

=head1 AUTOR

 Martin H. Sluka <fany@noris.net>
 fr die noris network AG
 RT#349530, RT#394262

