#!/usr/bin/perl -w
# vim: set ts=3 autoindent syntax=ON ft=perl:
# $Id$
# check_summary_rewrite.pl -- check nagios for a percentage of services of hosts
# in WARN or CRIT states.
# peter@hungry.com
# Many thanks Steve Shipway for the original idea.
# use at your own risk, etc.

use strict;
use Getopt::Std;


our ($opt_c, $opt_d, $opt_h, $opt_H, $opt_i, $opt_s, $opt_S, $opt_w);

getopts('hd:c:s:H:S:iw:');

my ($svc,$hst,$perc,%hosts,%services); 
my $hostnameregex = $opt_H;
my $servicenameregex = $opt_s;
my $statuslog = $opt_S || "/usr/local/var/nagios/status.log";
my $dbug = $opt_d || 0;
my $total = 0;
my $bad = 0;
my $countwarn = $opt_i;
my $percenttocritical = $opt_c || 75;
my $percenttowarning = $opt_w || 25;
my $exitmsg = "";

if ($opt_h) {
	&usage();
	exit 3;
};

sub usage {
	print "$0 -c critical -w warn -H hostregex -s serviceregex -S status.log [-dhi]\n";
	print << 'EOF';
	-c the percentage to go CRITICAL on
	-d debug
	-h this noise
	-H a PCRE regex to match a hostname
	-i include WARNINGs in the percentage calculation
	-s a PCRE regex to match a service name
	-S path to status.log
	-w the percentage to go WARNING on

awesome
EOF
};

sub readstatus() {

# map nagios WARN, CRIT, etc to numbers.
my(%map) = ( 'OK'=>0, 'WARN'=>1, 'WARNING'=>1, 'CRIT'=>2, 'CRITICAL'=>2, 'UNKNOWN'=>3, 'UNK'=>3, 'PENDING'=>4, 'UP'=>0, 'DOWN'=>2, 'UNREACHABLE'=>3, 0=>0, 1=>1, 2=>2, 3=>3 );

	open STATUSLOG, "<$statuslog" or die("cant open $statuslog");

	while (<STATUSLOG>) {
		chomp;
		@_ = split /;/;
		# skip the first line of the status.log file
		next if ($_[0] =~ (m/(Nagios|PROGRAM)/));
		#my $i = 0; for (@_) { print "$i $_\n"; $i++; }

		if ($_[0] =~ (m/HOST/)) {
			# We only care about hosts which are not ACK'ed and are notifying
			#print "$_[0] $_[5]\n";
			next if ($_[5] == 1);
			# We only care about hosts with notifications enabled.
			next if ($_[11] == 0);
			# We only care about hosts with checks enabled.
			next if ($_[13] == 0);

			$hosts{$_[1]} = $map{$_[2]};
		} 
		if ($_[0] =~ (m/SERVICE/)) {
			# We only care about services and hosts in HARD state.
			next if ($_[5] !~ (m/HARD/));
			# We only care about services with checks enabled.
			next if ($_[9] == 0);
			# We only care about services which are not ACK'ed and are notifying
			next if ($_[13] == 1);
			# We only care about services with notifications enabled.
			next if ($_[21] == 0);
		
			$services{$_[2]}{$_[1]} = $map{$_[3]};
		}	
	}
};


sub hostpercent() {
	foreach $hst (sort (keys (%hosts))) {
		# skip the key if it's not matched by user supplied regex
		next unless ($hst =~ m/$hostnameregex/);
		# increment total for every match
		$total++;
		# we should never hit this if block.
		if (defined ($countwarn)) {
			if ($hosts{$hst} == 1) {
				$exitmsg .= "$hst is DOWN<br>";
				$bad++;
			}
		}
		# if the host is down
		if ($hosts{$hst} == 2) {
			# add a not to $exitmsg
			$exitmsg .= "$hst is DOWN<br>";
			# and increment $bad
			$bad++;
		}
	}
	# calculate the percentage of bad hosts.
	$perc = ($bad/$total*100);
};


sub servicepercent() {
	# each service is a hash of hashes:
	# %services key is the service name, with a value of another hash
	# containing a hostname and a %map'ed value for the status of the
	# service on that host.
	# ex: service1 = host1 = 2 -- or service1 is CRITICAL on host1.
  foreach $svc (keys (%services)) {
	  for $hst ( keys %{ $services{$svc} } ) {
	  	next unless ($svc =~ m/$servicenameregex/);
		# skip the key if the user has defined a hostname regex and it doesnt match
		next if ((defined $hostnameregex) && ($hst !~ m/$hostnameregex/));
		$total++;
		# increment $bad if the user wants WARN counted.
		if (defined ($countwarn)) {
			if ($services{$svc}{$hst} == 1) {
				$exitmsg .= "<br>$svc on $hst is WARN";
				$bad++;
			}
		}
		# if the service is critical
		if ($services{$svc}{$hst} == 2) {
			# append to $exitmsg
			$exitmsg .= "<br>$svc on $hst is CRIT";
			# and increment $bad
			$bad++;
		}
	}
	}
# calculate the percentage
$perc = ($bad/$total*100);
};

sub dumpstate() {
	print "========================================================\n";
	print "Dumping state\n";
	print "========================================================\n";
	foreach $svc (keys (%services)) {
		for $hst ( keys %{ $services{$svc} } ) {
			print "$svc $hst $services{$svc}{$hst}\n";
		}
	}

	print "\n\n\n===========\nhosts.\n===========\n\n\n";

	foreach ( sort (keys (%hosts))) {
		print "$_ $hosts{$_}\n";
		}
	print "========================================================\n";
	print "Finished dumping state\n";
	print "========================================================\n";
};

sub do_exit() {
	# print percentage
	if ($dbug == 1) {
		dumpstate;
		print "Calculated percent is $perc\n";
	}
	# print $exit msg
	if ($exitmsg =~ /^$/) {
		$exitmsg = "All ok.";
	};
	print "$exitmsg\n";
	# exit CRITICAL if we blow the threshold
	exit 2 if ($perc >= $percenttocritical);
	# exit WARN if we blow the threshold
	exit 1 if ($perc >= $percenttowarning);
};

readstatus;


# We only run the subroutines the user requests.
if (defined $servicenameregex) {
	servicepercent;
	do_exit;
};

if (defined $hostnameregex && not defined $servicenameregex) {
	hostpercent;
	do_exit;
};



__END__

Format of status.log:


All Host Lines:

0 [Time of last update] HOST;
1 Host Name (string);
2 Status (OK/DOWN/UNREACHABLE);
3 Last Check Time (long time);
4 Last State Change (long time);
5 Acknowledged (0/1);
6 Time Up (long time);
7 Time Down (long time);
8 Time Unreachable (long time);
9 Last Notification Time (long time);
10 Current Notification Number (#);
11 Notifications Enabled (0/1);
12 Event Handlers Enabled (0/1);
13 Checks Enabled (0/1);
14 Flap Detection Enabled (0/1);
15 Host is Flapping (0/1);
16 Percent State Change (###.##);
17 Scheduled downtime depth (#);
18 Failure Prediction Enabled (0/1);
19 Process Performance Data(0/1);
20 Plugin Output (string)

Service Lines:

0 [Time of last update] SERVICE;
1 Host Name (string);
2 Service Description (string);
3 Status (OK/WARNING/CRITICAL/UNKNOWN);
4 Retry number (#/#);
5 State Type (SOFT/HARD);
6 Last check time (long time);
7 Next check time (long time);
8 Check type (ACTIVE/PASSIVE);
9 Checks enabled (0/1);
10 Accept Passive Checks (0/1);
11 Event Handlers Enabled (0/1);
12 Last state change (long time);
13 Problem acknowledged (0/1);
14 Last Hard State (OK/WARNING/CRITICAL/UNKNOWN);
15 Time OK (long time);
16 Time Unknown (long time);
17 Time Warning (long time);
18 Time Critical (long time);
19 Last Notification Time (long time);
20 Current Notification Number (#);
21 Notifications Enabled (0/1);
22 Latency (#);
23 Execution Time (#);
24 Flap Detection Enabled (0/1);
25 Service is Flapping (0/1);
26 Percent State Change (###.##);
27 Scheduled Downtime Depth (#);
28 Failure Prediction Enabled (0/1);
29 Process Performance Date (0/1);
30 Obsess Over Service (0/1);
31 Plugin Output (string)

Program line (second line of the status log):

0 [Current Time] PROGRAM;
1 Program Start Time (long time);
2 Nagios PID (#);
3 Daemon Mode (0/1);
4 Last Command Check (long time);
5 Last Log Rotation (long time);
6 Notifications Enabled (0/1);
7 Execute Service Checks (0/1);
8 Accept Passive Service Checks (0/1);
9 Enable Event Handlers (0/1);
10 Obsess Over Services (0/1);
11 Enable Flap Detection (0/1);
12 Enable Failure Prediction (0/1);
13 Process Performance Data (0/1)
