Skip to content

Instantly share code, notes, and snippets.

@n8v
Created June 2, 2011 21:49
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save n8v/1005409 to your computer and use it in GitHub Desktop.
Save n8v/1005409 to your computer and use it in GitHub Desktop.
Nagios Plugin for checking Test Anything Protocol (TAP) output
#!/usr/local/bin/perl
### check_tap.pl
# By Nathan Vonnahme, n8v at users dot sourceforge dot net, June 2 2011
# Allows Nagios to check the output of anything that emits Test Anything
# Protocol.
# See http://en.wikipedia.org/wiki/Test_Anything_Protocol
##############################################################################
# prologue
use strict;
use warnings;
use Nagios::Plugin ;
use TAP::Harness;
use vars qw($VERSION $PROGNAME $verbose $warn $critical $timeout $result);
$VERSION = '1.0';
# get the base name of this script for use in the examples
use File::Basename;
$PROGNAME = basename($0);
##############################################################################
# define and get the command line options.
# see the command line option guidelines at
# http://nagiosplug.sourceforge.net/developer-guidelines.html#PLUGOPTIONS
# Instantiate Nagios::Plugin object (the 'usage' parameter is mandatory)
my $p = Nagios::Plugin->new(
usage =>
"Usage: %s [ -v|--verbose ] [-t <timeout>]
[ -c|--critical=<critical threshold> ]
[ -w|--warning=<warning threshold> ]
[ -s|--script = '</full/path/to/test.t>' ] (Required. multiple OK)
[ -e|--exec = '/full/path/to/runnable ARGS'
[ -l|--lib = '/path/to/perl/libs' ] (Multiple OK)
",
version => $VERSION,
blurb =>
"This plugin allows Nagios to check the output of anything that emits
Test Anything Protocol output. So you can wed Nagios's monitoring and
alerting infrastructure to your unit and functional tests for deep
application-level monitoring in development or even in production.",
extra => qq{
## Verbosity
Use -v to see a bit more info in the one line, including the first
test that failed. This is especially useful because Nagios will
include it in the alert/notification.
Use -vv to see test summary and failures.
Use -vvv to see full test script output.
## Warning and Critical Thresholds
THRESHOLDs for -w and -c specify the allowable amount of test failures
before the plugin returns WARNING or CRITICAL. Use 'max' or 'min:max'.
The default of 0 tolerated failures is good for people like you who
have high standards. But you might want to crank up the CRITICAL
threshold if you want to differentiate between WARNING and CRITICAL
amounts of fail.
See more threshold examples at
http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT
## Examples:
$PROGNAME -s /full/path/to/testfoo.pl
will run 'testfoo.pl' and return OK if 0 tests fail, but CRITICAL if
any fail. Excluding TODO or SKIPped tests, of course.
$PROGNAME -s /full/path/to/testfoo.pl -c 2
will return OK if 0 tests fail, WARNING if more than 0 tests fail,
but CRITICAL if more than 2 fail.
$PROGNAME -s /full/path/to/testfoo.pl -w 2 c 4
will return OK if 0 tests fail, WARNING if more than 2 tests fail,
and CRITICAL if more than 4 fail.
## Non-Perl and remote test scripts
$PROGNAME -e '/usr/bin/ruby -w' -s /full/path/to/testfoo.r
will run 'testfoo.r' using Ruby with the -w flag.
You can use any shell command and argument which produces TAP output,
for example:
$PROGNAME -e '/usr/bin/curl -sk' -s 'http://url/to/mytest.php'
$PROGNAME -e '/usr/bin/cat' -s '/path/to/testoutput.tap'
In fact, anything TAP::Harness or `prove` regards as a source or
executable.
Remember that Nagios or NRPE will likely be running this command as a
different, less-privileged user than you're using now.
}
);
# Define and document the valid command line options
# usage, help, version, timeout and verbose are defined by default.
$p->add_arg(
spec => 'script|s=s@',
required => 1,
help =>
qq{-s, --script="/path/to/executable/test.t args"
REQUIRED. Defines the path to the test script you want to run.
Use multiple -s flags to run multiple tests.
}
);
$p->add_arg(
spec => 'lib|l=s@',
help =>
qq{-l, --lib="/path/to/perl/lib/dir"
Optional path for Perl libs to add to \@INC.
Use multiple -l flags to specify multiple lib dirs.
}
);
$p->add_arg(
spec => 'exec|e=s',
required => 0,
help =>
qq{-e, --exec="/path/to/executable args"
Defines a non-Perl executable with which you want to run the --script.
}
);
$p->add_arg(
spec => 'warning|w=s',
default => 0,
help =>
qq{-w, --warning=INTEGER:INTEGER
Minimum and maximum number of allowable test FAILURES, outside of which a
warning will be generated. Default is 0 tolerable failures.
},
);
$p->add_arg(
spec => 'critical|c=s',
default => 0,
help =>
qq{-c, --critical=INTEGER:INTEGER
Minimum and maximum number of allowable test FAILURES, outside of
which a critical will be generated. Default is 0 tolerable failures.
},
);
# Parse arguments and process standard ones (e.g. usage, help, version)
$p->getopts;
##############################################################################
# Do it, and do it, and do it.
my $message = "";
alarm($p->opts->timeout);
# Capture STDOUT/STDERR to emit if verbose
my $raw_test_output = "";
# opens a virtual filehandle that will write to $raw_test_output
open(my $fh, '>', \$raw_test_output);
# -v and -vv need Harness verbosity = 0
# -vvv+ need verbosity = 1
my $harness = TAP::Harness->new( {
verbosity => ($p->opts->verbose >=3),
failures => 1,
merge => 1,
errors => 1,
stdout => $fh,
formatter_class => 'TAP::Formatter::File',
ignore_exit => 1,
lib => $p->opts->lib,
} );
if ($p->opts->exec) {
# exec => [split /\s+/, $p->opts->exec];
$harness->exec([split /\s+/, $p->opts->exec]);
}
my $aggregator = $harness->runtests( @{$p->opts->script} );
alarm(0);
my $result = $aggregator->failed;
$p->add_perfdata(
label => "total",
value => $aggregator->total,
);
$p->add_perfdata(
label => "failures",
value => $result,
threshold => $p->threshold,
);
$p->add_perfdata(
label => "passed",
value => scalar $aggregator->passed,
);
if ( $aggregator->exit >= 255 ) {
$message = sprintf("Test script failed to exit normally after %d tests (%d failures)",
scalar $aggregator->total, scalar $aggregator->failed);
$message .= "\n$raw_test_output" if $p->opts->verbose > 0;
$p->nagios_exit(
return_code => CRITICAL,
message => $message
);
}
$message = $aggregator->get_status;
if ($p->opts->verbose > 0) {
my $first_failure = "";
if ($aggregator->failed && $raw_test_output =~ /^not ok.*$/m) {
$first_failure = " 1st='$&'";
}
$message .= sprintf(". %d of %d failed. %s%s",
scalar $aggregator->failed,
scalar $aggregator->total,
($p->opts->warning || $p->opts->critical ?
sprintf "Warning/critical at %d/%d.",
$p->opts->warning,
$p->opts->critical :
""),
$first_failure
);
$message .= "\n" . $raw_test_output
if $p->opts->verbose > 1;
}
##############################################################################
# check the result against the defined warning and critical thresholds,
# output the result and exit
$p->nagios_exit(
return_code => $p->check_threshold($result),
message => $message
);
@n8v
Copy link
Author

n8v commented Jun 3, 2011

See my blog post about this plugin for more information.

@n8v
Copy link
Author

n8v commented Jun 8, 2011

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment