#!/usr/local/bin/perl -w
#
# Papercut health monitoring
#
# By Igor V. Gubenko (igubenko@Princeton.EDU), 08/03/2016
#
# The program requires the JSON, REST::Client, and Try:Tiny libraries
# Data::Dumper is for debugging, and is not required for normal operation
#
# The program assumes that HTML output is desired. If this is not the case, light modification (commenting out) of HTML markup is needed
# Nagios requires "escape_html_tags=0" in cgi.cfg for HTML output
#
# Released under BSD 2-clause license
#

use strict;
use warnings;
use JSON;
use REST::Client;
use Data::Dumper;
use Try::Tiny;

# printserv => hostname or fqdn of the Papercut server
# apikey => the apikey of Papercut installation
# what => what to check - any of the keys of %queries below
# whatarg => unused for now
my ($printserv, $apikey, $what, $whatarg) = @ARGV;
my ($respcode, $data);

unless (@ARGV > 2) {
	print STDERR "Usage: $0 {printserv} {api_auth_key} {health|printers_in_error|printer_urls|device_urls|sixtyminutes|queue|sysinfo}\n";
	exit (2);
}

# Associates queries with corresponding URLs
my %queries = (
	health => 'https://' . $printserv . ':9192/api/health/status?Authorization=' . $apikey,
	printers_in_error => 'https://' . $printserv . ':9192/api/health/printers/status?in-error-threshold=50&Authorization=' . $apikey,
	printer_urls => 'https://' . $printserv . ':9192/api/health/printers/urls?Authorization=' . $apikey,
	device_urls => 'https://' . $printserv . ':9192/api/health/devices/urls?Authorization=' . $apikey,
	sixtyminutes => 'https://' . $printserv . ':9192/api/stats/recent-pages-count?minutes=60&Authorization=' . $apikey,
	queue => 'https://' . $printserv . ':9192/api/stats/held-jobs-count?Authorization=' . $apikey,
	sysinfo => 'https://' . $printserv . ':9192/api/health?Authorization=' . $apikey,
);

# Associates the functions to run to parse each query type
my %functions;
foreach (keys %queries) {
	$functions{$_} = \&$_;
}

## In all of the functions below, if $data is not a hash, then we assume that $data is a string, and simply return it for output

sub health {
	return (ref $data eq ref {}) ? (exists $data->{message} ? $data->{message} : $data->{comment}) . "\n" : $data;
}

sub printers_in_error {
	return (ref $data eq ref {}) ? (exists $data->{message} ? $data->{message} : $data->{comment}) . "\n" : $data;
}

sub printer_urls {
#	return (ref $data eq ref {}) ? $data->{printer_urls} . "\n" : $data;

	if (ref $data eq ref {}) {
		return "";
	} else {
		# Remove the Unicode character
		$data =~ s/^\x{ef}\x{bb}\x{bf}//;
		my @rows = split /\n/, $data;

		# Create HTML table markups for nice output
		$data = "<table>";
		foreach (@rows) {
			my @cols = split /,/;
			$data = $data . "<tr><td>" . join ("</td><td>", @cols[0,1,2]) . "</td></tr>";
		}
		$data .= "</table>";
		return $data;
	}
}

sub device_urls {
	return (ref $data eq ref {}) ? $data->{device_urls} . "\n" : $data;
}

sub sixtyminutes {
	return (ref $data eq ref {}) ? "In the last 60 minutes there were " . $data->{recentPagesCount} . " pages printed\n" : $data;
}

sub queue {
	return (ref $data eq ref {}) ? "There are " . $data->{heldJobsCount} . " jobs currently held for release\n" : $data;
}

sub sysinfo {
	if (ref $data eq ref {}) {
		# Create nice, indented JSON output
		my $jsonny = JSON->new;
		my $jpretty = $jsonny->pretty->encode($data);

		# Replace newlines with HTML line breaks
		$jpretty =~ s#\n#<br>#g;
		# Replace each space with 2 HTML spaces for nice indent
		$jpretty =~ s#\s#\&nbsp;\&nbsp;#g;
		return $jpretty . "\n";
	}

	return $data;
}

### This is for reference

# health => Status summary: primary health check
#curl -k 'https://ss219w:9192/api/health/status?Authorization=apikey'

# printers_in_error => Status summary: alert if >1 printers are in error
#curl -k 'https://ss219w:9192/api/health/printers/status?in-error-threshold=1&Authorization=apikey'

# printer_urls => Status per printer: display printer status URL's
#curl -k 'https://ss219w:9192/api/health/printers/urls?Authorization=apikey'

# device_urls => Status per device: display device status URL's
#curl -k 'https://ss219w:9192/api/health/devices/urls?Authorization=apikey'

# sixtyminutes => Real time system stats: pages printed in the last 60 minutes
#curl -k 'https://ss219w:9192/api/stats/recent-pages-count?minutes=60&Authorization=apikey'

# queue => Real time system stats: # of jobs in hold/release queue
#curl -k 'https://ss219w:9192/api/stats/held-jobs-count?Authorization=apikey'

# sysinfo => Detailed system info
#curl -k 'https://ss219w:9192/api/health?Authorization=apikey'

# Don't verify SSL hostname in case the certificate is self-signed
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}=0;

my $client = REST::Client->new();

# Retrieve the relevant query using a REST client, since the output is almost always JSON
$client -> GET ($queries{$what});
#print $client->responseCode, "\n";
#print $client->responseContent, "\n";

$respcode = $client->responseCode;

try {
	# Try to decode the JSON into a hash
	$data = decode_json($client->responseContent);
	#print Dumper ($data);
} catch {
	# Unable to decode JSON - take the response as a string
	$data = $client->responseContent;
};

# Run the relevant parsing function
print $functions{$what}();

# Depending on the HTTP return code, we return either OK or CRITICAL
$respcode = ($respcode eq '200') ? 0 : 2;

exit ($respcode);

