#!/usr/local/groundwork/perl/bin/perl -- # # Copyright 2007-2008 GroundWork Open Source Solutions, Inc. (GroundWork) # All rights reserved. Use is subject to GroundWork commercial license terms. # #use strict; use Time::Local; use IO::Socket; use CollageQuery; my $socket; my $debug = 0; # 1=summary output; 2=output XML message; 3=print input lines my $eventfile = '/usr/local/groundwork/nagios/var/nagios.log'; my $seekfile = '/usr/local/groundwork/nagios/var/nagios_seek.tmp'; my $logfile = '/usr/local/groundwork/foundation//container/logs/nagios2collage_eventlog.log'; my $thisnagios = "localhost"; my $remote_host = $thisnagios; my $remote_port = 4913; my %hostipaddress = (); # xml batching for performance in large installations my @xml_messages = (); my $xml_bundle_size = 1; # this is NOT the minimum size my $xml_bundle_max_size = 1; # but this is the maximum size my $sync_timeout_seconds = 2; my $next_sync_timeout = time + $sync_timeout_seconds; my $message_counter = 0; # should rename this: it does not include ACKNOWLEDGE messages. if ( !open( LOG, ">>$logfile" ) ) { print "Can't open logfile $logfile.\n"; } if ( $socket = IO::Socket::INET->new( PeerAddr => $remote_host, PeerPort => $remote_port, Proto => "tcp", Type => SOCK_STREAM ) ) { print $socket ""; close($socket); } else { print LOG "Listener socket not available. Retrying in 10 seconds.\n"; sleep 10; exit(0); } print LOG "Listener socket successfully opened.\n"; # Get hosts->IPaddress from Monarch my ( $Database_Name, $Database_Host, $Database_User, $Database_Password ) = CollageQuery::readGroundworkDBConfig("monarch"); my $dbh = DBI->connect( "DBI:mysql:$Database_Name:$Database_Host", $Database_User, $Database_Password ) or die "Can't connect to database $Database_Name. Error:" . $DBI::errstr; my $query = "select name,address from hosts; "; my $sth = $dbh->prepare($query); $sth->execute() or die $@; while ( my $row = $sth->fetchrow_hashref() ) { $hostipaddress{ $$row{name} } = $$row{address}; } $sth->finish(); $dbh->disconnect(); my $socket; $SIG{"PIPE"} = \&sig_pipe_handler; # handle broken pipe error chomp $thisnagios; if ( !open( LOG, ">>$logfile" ) ) { print "Can't open logfile $logfile.\n"; } while (1) { my $LoopCount = 0; my $SkipCount = 0; if ( !open LOG_FILE, $eventfile ) { print LOG "Unable to open log file $eventfile: $!"; exit; } LOG->autoflush(1); # Try to open log seek file. If open fails, we seek from beginning of # file by default. if ( open( SEEK_FILE, $seekfile ) ) { chomp( @seek_pos = ); close(SEEK_FILE); # If file is empty, no need to seek... if ( $seek_pos[0] != 0 ) { # Compare seek position to actual file size. If file size is smaller # then we just start from beginning i.e. file was rotated, etc. my ( $dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, @rest ) = stat(LOG_FILE); if ( $seek_pos[0] <= $size ) { seek( LOG_FILE, $seek_pos[0], 0 ); } } } if ( !( $socket = IO::Socket::INET->new( PeerAddr => $remote_host, PeerPort => $remote_port, Proto => "tcp", Type => SOCK_STREAM ) ) ) { print LOG "Couldn't connect to $remote_host:$remote_port : $@\n"; exit; } $socket->autoflush(); print LOG "Connected to $remote_host:$remote_port : $@\n" if $debug; while ( my $line = ) { chomp $line; # Sample Events below next if ( $line =~ /^\s*\#]/ ); @field = split /;/, $line; if ( $field[0] =~ /\[(\d+)\]\s([\w\s]+):\s(.*)/ ) { $timestamp = $1; $msgtype = $2; $host = $3; } else { # Parse other formats here if necessary $SkipCount++; next; } my $xml_message = "/ /ig; $tmp =~ s/&/&/g; $tmp =~ s/"/"/g; $tmp =~ s/'/'/g; $tmp =~ s//>/g; $xml_message .= "TextMessage=\"$tmp\" "; $tmp = time_text(time); $xml_message .= "ReportDate=\"$tmp\" "; $tmp = time_text($timestamp); $xml_message .= "LastInsertDate=\"$tmp\" "; $xml_message .= "SubComponent=\"$host\" "; $xml_message .= "ErrorType=\"HOST ALERT\" "; $xml_message .= "/>"; } elsif ( ( $msgtype =~ /SERVICE ALERT/ ) and ( $field[3] eq "HARD" ) ) { # [1110304792] SERVICE ALERT: peter;icmp_ping;OK;HARD;1;PING OK - Packet loss = 0%, RTA = 1.05 ms # default identification - should set to IP address if known $xml_message .= "Host=\"$host\" "; if ( $hostipaddress{$host} ) { # no IP address; set to host name $xml_message .= "Device=\"" . $hostipaddress{$host} . "\" "; } else { # no IP address; set to host name $xml_message .= "Device=\"$host\" "; } $xml_message .= "ServiceDescription=\"$field[1]\" "; # Invalid field?? $xml_message .= "Severity=\"$field[2]\" "; $xml_message .= "MonitorStatus=\"$field[2]\" "; $tmp = $field[5]; $tmp =~ s/\n/ /g; $tmp =~ s/\f/ /g; $tmp =~ s/
/ /ig; $tmp =~ s/&/&/g; $tmp =~ s/"/"/g; $tmp =~ s/'/'/g; $tmp =~ s//>/g; $xml_message .= "TextMessage=\"$tmp\" "; $tmp = time_text(time); $xml_message .= "ReportDate=\"$tmp\" "; $tmp = time_text($timestamp); $xml_message .= "LastInsertDate=\"$tmp\" "; $xml_message .= "SubComponent=\"$host:$field[1]\" "; $xml_message .= "ErrorType=\"SERVICE ALERT\" "; $xml_message .= "/>"; } elsif ( $msgtype =~ /HOST NOTIFICATION/ ) { # [1110304792] HOST NOTIFICATION: nagios;peter;UP;host-notify-by-epager;PING OK - Packet loss = 0%, RTA = 0.88 ms $xml_message .= "Host=\"$field[1]\" "; if ( $hostipaddress{ $field[1] } ) { # no IP address; set to host name $xml_message .= "Device=\"" . $hostipaddress{ $field[1] } . "\" "; } else { # no IP address; set to host name $xml_message .= "Device=\"$field[1]\" "; } $xml_message .= "LoggerName=\"$host\" "; if ( $field[2] eq "DOWN" ) { $xml_message .= "Severity=\"CRITICAL\" "; } elsif ( $field[2] eq "UP" ) { $xml_message .= "Severity=\"OK\" "; } else { $xml_message .= "Severity=\"$field[2]\" "; } $xml_message .= "MonitorStatus=\"$field[2]\" "; $xml_message .= "ApplicationName=\"$field[3]\" "; $tmp = $field[4]; $tmp =~ s/\n/ /g; $tmp =~ s/\f/ /g; $tmp =~ s/
/ /ig; $tmp =~ s/&/&/g; $tmp =~ s/"/"/g; $tmp =~ s/'/'/g; $tmp =~ s//>/g; $xml_message .= "TextMessage=\"$tmp\" "; $tmp = time_text(time); $xml_message .= "ReportDate=\"$tmp\" "; $tmp = time_text($timestamp); $xml_message .= "LastInsertDate=\"$tmp\" "; $xml_message .= "SubComponent=\"$field[1]\" "; $xml_message .= "ErrorType=\"HOST NOTIFICATION\" "; $xml_message .= "/>"; } elsif ( $msgtype =~ /SERVICE NOTIFICATION/ ) { # [1110304792] SERVICE NOTIFICATION: nagios;peter;check_http;CRITICAL;notify-by-epager;A HREF=http://192.168.2.146:80/ target=_blankConnection refused $xml_message .= "Host=\"$field[1]\" "; if ( $hostipaddress{ $field[1] } ) { # no IP address; set to host name $xml_message .= "Device=\"" . $hostipaddress{ $field[1] } . "\" "; } else { # no IP address; set to host name $xml_message .= "Device=\"$field[1]\" "; } $xml_message .= "LoggerName=\"$host\" "; $xml_message .= "ServiceDescription=\"$field[2]\" "; # invalid field? $xml_message .= "Severity=\"$field[3]\" "; $xml_message .= "MonitorStatus=\"$field[3]\" "; $xml_message .= "ApplicationName=\"$field[4]\" "; $tmp = $field[5]; $tmp =~ s/\n/ /g; $tmp =~ s/\f/ /g; $tmp =~ s/
/ /ig; $tmp =~ s/&/&/g; $tmp =~ s/"/"/g; $tmp =~ s/'/'/g; $tmp =~ s//>/g; $xml_message .= "TextMessage=\"$tmp\" "; $tmp = time_text(time); $xml_message .= "ReportDate=\"$tmp\" "; $tmp = time_text($timestamp); $xml_message .= "LastInsertDate=\"$tmp\" "; $xml_message .= "SubComponent=\"$field[1]:$field[2]\" "; $xml_message .= "ErrorType=\"SERVICE NOTIFICATION\" "; $xml_message .= "/>"; } elsif ( ( $msgtype =~ /EXTERNAL COMMAND/ ) and ( $host =~ /ACKNOWLEDGE_HOST_PROBLEM/ ) ) { # Host field not host for this msg type # [1153242440] EXTERNAL COMMAND: ACKNOWLEDGE_HOST_PROBLEM;localhost;1;1;1;joe;This is a test # # ServiceDescription, AcknowledgeComment are optional # If TypeRule is UNACKNOWLEDGE AcknowledgedBy and AcknowledgeComment will be cleared. $msgtype = "ACKNOWLEDGE_HOST_PROBLEM"; my $ack_message = "/ /ig; $tmp =~ s/&/&/g; $tmp =~ s/"/"/g; $tmp =~ s/'/'/g; $tmp =~ s//>/g; $ack_message .= "AcknowledgeComment=\"$tmp\" "; $ack_message .= " TypeRule=\"ACKNOWLEDGE\" "; $ack_message .= "/>"; $xml_message = ''; # clear this for if () below output_message_to_socket($socket, $ack_message); } elsif ( ( $msgtype =~ /EXTERNAL COMMAND/ ) and ( $host =~ /ACKNOWLEDGE_SVC_PROBLEM/ ) ) { # Host field not host for this msg type # [1153242140] EXTERNAL COMMAND: ACKNOWLEDGE_SVC_PROBLEM;localhost;Current Users;1;1;1;joe;This is a test acknowledge. # # ServiceDescription, AcknowledgeComment are optional # If TypeRule is UNACKNOWLEDGE AcknowledgedBy and AcknowledgeComment will be cleared. $msgtype = "ACKNOWLEDGE_SVC_PROBLEM"; my $ack_message = "/ /ig; $tmp =~ s/&/&/g; $tmp =~ s/"/"/g; $tmp =~ s/'/'/g; $tmp =~ s//>/g; $ack_message .= "AcknowledgeComment=\"$tmp\" "; $ack_message .= " TypeRule=\"ACKNOWLEDGE\" "; $ack_message .= "/>"; $xml_message = ''; # clear this for if () below output_message_to_socket($socket, $ack_message); } elsif ( ( $msgtype =~ /EXTERNAL COMMAND/ ) and ( $host =~ /REMOVE_HOST_ACKNOWLEDGEMENT/ ) ) { # Host field not host for this msg type # [1153257740] EXTERNAL COMMAND: REMOVE_HOST_ACKNOWLEDGEMENT;localhost # # ServiceDescription, AcknowledgeComment are optional # If TypeRule is UNACKNOWLEDGE AcknowledgedBy and AcknowledgeComment will be cleared. $msgtype = "REMOVE_HOST_ACKNOWLEDGEMENT"; my $ack_message = " # ServiceDescription, AcknowledgeComment are optional # If TypeRule is UNACKNOWLEDGE AcknowledgedBy and AcknowledgeComment will be cleared. $msgtype = "REMOVE_SVC_ACKNOWLEDGEMENT"; my $ack_message = "2) ; $SkipCount++; next; } $LoopCount++; if ($xml_message ne '') { push(@xml_messages, $xml_message); if (@xml_messages >= $xml_bundle_size || (@xml_messages > 0 && time >= $next_sync_timeout)) { $message_counter = output_bundle_to_socket($socket, \@xml_messages, $message_counter); @xml_messages = (); $next_sync_timeout = time + $sync_timeout_seconds; } } print LOG "Line: $line \n" if ( $debug > 2 ); } # All events read CommandClose($socket); close($socket); # Overwrite log seek file and print the byte position we have seeked to. if ( !open( SEEK_FILE, "> $seekfile" ) ) { print LOG "Unable to open seek count file $seekfile: $!"; exit; } else { print LOG "Writing to seek file $seekfile - " . tell(LOG_FILE) . "\n" if ( $debug > 1 ); } print SEEK_FILE tell(LOG_FILE); # Close seek file close(SEEK_FILE); # Close the log file close(LOG_FILE); print LOG "Processed $LoopCount records. Skipped $SkipCount.\n" if $debug; sleep 5; } exit; sub output_bundle_to_socket { my $socket = shift; my $msg_ref = shift; my $series_num = shift; my @messages = @{$msg_ref}; while (@messages) { my $element_begin = qq(\n); my $element_end = "\n"; print $socket $element_begin; print LOG "$element_begin\n" if ($debug > 1); my $num_msgs_output = 0; while (@messages && $num_msgs_output < $xml_bundle_max_size) { $num_msgs_output++; my $message = shift(@messages); print $socket $message; print LOG "$message\n" if ($debug > 1); } print $socket $element_end; print LOG "$element_end\n" if ($debug > 1); $series_num++; } return $series_num; } sub output_message_to_socket { my $socket = shift; my $message = shift; print $socket $message; print LOG "$message\n" if ($debug > 1); } sub time_text { my $timestamp = shift; if ( $timestamp <= 0 ) { return "none"; } else { my ( $sec, $min, $hour, $dom, $mon, $year, $wday, $yday, $dst ) = localtime($timestamp); return sprintf "%02d-%02d-%02d %02d:%02d:%02d", $year + 1900, $mon + 1, $dom, $hour, $min, $sec; } } sub CommandClose { my $socket = shift; # Create XML stream - Format: # my $xml_message = ""; # print $xml_message."\n\n" if $debug; print $socket $xml_message; return; } sub FormatTime { my $intimestring = shift; my $outtimestring; if ( $intimestring =~ /(\d{2})\/(\d{2})\/(\d{4}) (\d{2}:\d{2}:\d{2})/ ) { $outtimestring = "$3-$1-$2 $4"; } return $outtimestring; } sub sig_pipe_handler { sleep 2; }