#!/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;
$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;
$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;
$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;
$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;
$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;
$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;
}