Nagios-Plugin-0.36/0000755000015100001540000000000011675044021013427 5ustar nagiosnagiosNagios-Plugin-0.36/MANIFEST0000644000015100001540000000271711476154215014575 0ustar nagiosnagiosChanges lib/Nagios/Plugin.pm lib/Nagios/Plugin/Config.pm lib/Nagios/Plugin/ExitResult.pm lib/Nagios/Plugin/Functions.pm lib/Nagios/Plugin/Getopt.pm lib/Nagios/Plugin/Performance.pm lib/Nagios/Plugin/Range.pm lib/Nagios/Plugin/Threshold.pm Makefile.PL MANIFEST This list of files META.yml README t/check_stuff.pl t/check_stuff.t t/Nagios-Plugin-01.t t/Nagios-Plugin-02.t t/Nagios-Plugin-03.t t/Nagios-Plugin-04.t t/Nagios-Plugin-Functions-01.t t/Nagios-Plugin-Functions-02.t t/Nagios-Plugin-Functions-03.t t/Nagios-Plugin-Getopt-01.t t/Nagios-Plugin-Getopt-02.t t/Nagios-Plugin-Getopt-03.t t/Nagios-Plugin-Getopt-04.t t/Nagios-Plugin-Performance-02.t t/Nagios-Plugin-Performance.t t/Nagios-Plugin-Range.t t/Nagios-Plugin-Threshold.t t/npg03/expected/00_basic t/npg03/expected/00_noextra t/npg03/expected/01_override1 t/npg03/expected/02_override2 t/npg03/expected/05_disk1 t/npg03/expected/05_disk2 t/npg03/expected/05_disk3 t/npg03/expected/05_disk4 t/npg03/expected/05_disk5 t/npg03/expected/05_disk6 t/npg03/expected/09_funnystuff t/npg03/expected/12_nosection_implicit t/npg03/input/00_basic t/npg03/input/00_noextra t/npg03/input/01_override1 t/npg03/input/02_override2 t/npg03/input/05_disk1 t/npg03/input/05_disk2 t/npg03/input/05_disk3 t/npg03/input/05_disk4 t/npg03/input/05_disk5 t/npg03/input/05_disk6 t/npg03/input/09_funnystuff t/npg03/input/12_nosection_implicit t/npg03/input/13_nosection_explicit_dies t/npg03/input/14_badsection_dies t/npg03/plugins.ini t/npg03/README Nagios-Plugin-0.36/lib/0000755000015100001540000000000011675044021014175 5ustar nagiosnagiosNagios-Plugin-0.36/lib/Nagios/0000755000015100001540000000000011675044021015415 5ustar nagiosnagiosNagios-Plugin-0.36/lib/Nagios/Plugin.pm0000644000015100001540000004456211675043631017232 0ustar nagiosnagios package Nagios::Plugin; use Nagios::Plugin::Functions qw(:codes %ERRORS %STATUS_TEXT @STATUS_CODES); use Params::Validate qw(:all); use strict; use warnings; use Carp; use base qw(Class::Accessor::Fast); Nagios::Plugin->mk_accessors(qw( shortname perfdata messages opts threshold )); use Exporter; our @ISA = qw(Exporter); our @EXPORT = (@STATUS_CODES); our @EXPORT_OK = qw(%ERRORS %STATUS_TEXT); # CPAN stupidly won't index this module without a literal $VERSION here, # so we're forced to duplicate it explicitly # Make sure you update $Nagios::Plugin::Functions::VERSION too our $VERSION = "0.36"; sub new { my $class = shift; # my %args = @_; my %args = validate( @_, { shortname => 0, usage => 0, version => 0, url => 0, plugin => 0, blurb => 0, extra => 0, license => 0, timeout => 0 }, ); my $shortname = Nagios::Plugin::Functions::get_shortname(\%args); delete $args{shortname} if (exists $args{shortname}); my $self = { shortname => $shortname, perfdata => [], # to be added later messages => { warning => [], critical => [], ok => [] }, opts => undef, # see below threshold => undef, # defined later }; bless $self, $class; if (exists $args{usage}) { require Nagios::Plugin::Getopt; $self->opts( new Nagios::Plugin::Getopt(%args) ); } return $self; } sub add_perfdata { my ($self, %args) = @_; require Nagios::Plugin::Performance; my $perf = Nagios::Plugin::Performance->new(%args); push @{$self->perfdata}, $perf; } sub all_perfoutput { my $self = shift; return join(" ", map {$_->perfoutput} (@{$self->perfdata})); } sub set_thresholds { my $self = shift; require Nagios::Plugin::Threshold; return $self->threshold( Nagios::Plugin::Threshold->set_thresholds(@_)); } # NP::Functions wrappers sub nagios_exit { my $self = shift; Nagios::Plugin::Functions::nagios_exit(@_, { plugin => $self }); } sub nagios_die { my $self = shift; Nagios::Plugin::Functions::nagios_die(@_, { plugin => $self }); } sub die { my $self = shift; Nagios::Plugin::Functions::nagios_die(@_, { plugin => $self }); } sub max_state { Nagios::Plugin::Functions::max_state(@_); } sub max_state_alt { Nagios::Plugin::Functions::max_state_alt(@_); } # top level interface to Nagios::Plugin::Threshold sub check_threshold { my $self = shift; my %args; if ( $#_ == 0 && (! ref $_[0] || ref $_[0] eq "ARRAY" )) { # one positional param %args = (check => shift); } else { %args = validate ( @_, { # named params check => 1, warning => 0, critical => 0, } ); } # in order of preference, get warning and critical from # 1. explicit arguments to check_threshold # 2. previously explicitly set threshold object # 3. implicit options from Getopts object if ( exists $args{warning} || exists $args{critical} ) { $self->set_thresholds( warning => $args{warning}, critical => $args{critical}, ); } elsif ( defined $self->threshold ) { # noop } elsif ( defined $self->opts ) { $self->set_thresholds( warning => $self->opts->warning, critical => $self->opts->critical, ); } else { return UNKNOWN; } return $self->threshold->get_status($args{check}); } # top level interface to my Nagios::Plugin::Getopt object sub add_arg { my $self = shift; $self->opts->arg(@_) if $self->_check_for_opts; } sub getopts { my $self = shift; $self->opts->getopts(@_) if $self->_check_for_opts; } sub _check_for_opts { my $self = shift; croak "You have to supply a 'usage' param to Nagios::Plugin::new() if you want to use Getopts from your Nagios::Plugin object." unless ref $self->opts() eq 'Nagios::Plugin::Getopt'; return $self; } # ------------------------------------------------------------------------- # NP::Functions::check_messages helpers and wrappers sub add_message { my $self = shift; my ($code, @messages) = @_; croak "Invalid error code '$code'" unless defined($ERRORS{uc $code}) || defined($STATUS_TEXT{$code}); # Store messages using strings rather than numeric codes $code = $STATUS_TEXT{$code} if $STATUS_TEXT{$code}; $code = lc $code; croak "Error code '$code' not supported by add_message" if $code eq 'unknown' || $code eq 'dependent'; $self->messages($code, []) unless $self->messages->{$code}; push @{$self->messages->{$code}}, @messages; } sub check_messages { my $self = shift; my %args = @_; # Add object messages to any passed in as args for my $code (qw(critical warning ok)) { my $messages = $self->messages->{$code} || []; if ($args{$code}) { unless (ref $args{$code} eq 'ARRAY') { if ($code eq 'ok') { $args{$code} = [ $args{$code} ]; } else { croak "Invalid argument '$code'" } } push @{$args{$code}}, @$messages; } else { $args{$code} = $messages; } } Nagios::Plugin::Functions::check_messages(%args); } # ------------------------------------------------------------------------- 1; #vim:et:sw=4 __END__ =head1 NAME Nagios::Plugin - A family of perl modules to streamline writing Nagios plugins =head1 SYNOPSIS # Constants OK, WARNING, CRITICAL, and UNKNOWN are exported by default # See also Nagios::Plugin::Functions for a functional interface use Nagios::Plugin; # Constructor $np = Nagios::Plugin->new; # OR $np = Nagios::Plugin->new( shortname => "PAGESIZE" ); # OR # use Nagios::Plugin::Getopt to process the @ARGV command line options: # --verbose, --help, --usage, --timeout and --host are defined automatically. $np = Nagios::Plugin->new( usage => "Usage: %s [ -v|--verbose ] [-H ] [-t ] " . "[ -c|--critical= ] [ -w|--warning= ]", ); # add valid command line options and build them into your usage/help documentation. $np->add_arg( spec => 'warning|w=s', help => '-w, --warning=INTEGER:INTEGER . See ' . 'http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT ' . 'for the threshold format. ', ); # Parse @ARGV and process standard arguments (e.g. usage, help, version) $np->getopts; # Exit/return value methods - nagios_exit( CODE, MESSAGE ), # nagios_die( MESSAGE, [CODE]) $page = retrieve_page($page1) or $np->nagios_exit( UNKNOWN, "Could not retrieve page" ); # Return code: 3; # output: PAGESIZE UNKNOWN - Could not retrieve page test_page($page) or $np->nagios_exit( CRITICAL, "Bad page found" ); # nagios_die() is just like nagios_exit(), but return code defaults # to UNKNOWN $page = retrieve_page($page2) or $np->nagios_die( "Could not retrieve page" ); # Return code: 3; # output: PAGESIZE UNKNOWN - Could not retrieve page # Threshold methods $code = $np->check_threshold( check => $value, warning => $warning_threshold, critical => $critical_threshold, ); $np->nagios_exit( $code, "Threshold check failed" ) if $code != OK; # Message methods (EXPERIMENTAL AND SUBJECT TO CHANGE) - # add_message( CODE, $message ); check_messages() for (@collection) { if (m/Error/) { $np->add_message( CRITICAL, $_ ); } else { $np->add_message( OK, $_ ); } } ($code, $message) = $np->check_messages(); nagios_exit( $code, $message ); # If any items in collection matched m/Error/, returns CRITICAL and # the joined set of Error messages; otherwise returns OK and the # joined set of ok messages # Perfdata methods $np->add_perfdata( label => "size", value => $value, uom => "kB", threshold => $threshold, ); $np->add_perfdata( label => "time", ... ); $np->nagios_exit( OK, "page size at http://... was ${value}kB" ); # Return code: 0; # output: PAGESIZE OK - page size at http://... was 36kB \ # | size=36kB;10:25;25: time=... =head1 DESCRIPTION Nagios::Plugin and its associated Nagios::Plugin::* modules are a family of perl modules to streamline writing Nagios plugins. The main end user modules are Nagios::Plugin, providing an object-oriented interface to the entire Nagios::Plugin::* collection, and Nagios::Plugin::Functions, providing a simpler functional interface to a useful subset of the available functionality. The purpose of the collection is to make it as simple as possible for developers to create plugins that conform the Nagios Plugin guidelines (http://nagiosplug.sourceforge.net/developer-guidelines.html). =head2 EXPORTS Nagios status code constants are exported by default: OK WARNING CRITICAL UNKNOWN DEPENDENT The following variables are also exported on request: =over 4 =item %ERRORS A hash mapping error strings ("CRITICAL", "UNKNOWN", etc.) to the corresponding status code. =item %STATUS_TEXT A hash mapping status code constants (OK, WARNING, CRITICAL, etc.) to the corresponding error string ("OK", "WARNING, "CRITICAL", etc.) i.e. the reverse of %ERRORS. =back =head2 CONSTRUCTOR Nagios::Plugin->new; Nagios::Plugin->new( shortname => 'PAGESIZE' ); Nagios::Plugin->new( usage => "Usage: %s [ -v|--verbose ] [-H ] [-t ] [ -c|--critical= ] [ -w|--warning= ] ", version => $VERSION, blurb => $blurb, extra => $extra, url => $url, license => $license, plugin => basename $0, timeout => 15, ); Instantiates a new Nagios::Plugin object. Accepts the following named arguments: =over 4 =item shortname The 'shortname' for this plugin, used as the first token in the plugin output by the various exit methods. Default: uc basename $0. =item usage ("Usage: %s --foo --bar") Passing a value for the usage() argument makes Nagios::Plugin instantiate its own C object so you can start doing command line argument processing. See L for more about "usage" and the following options: =item version =item url =item blurb =item license =item extra =item plugin =item timeout =back =head2 OPTION HANDLING METHODS C provides these methods for accessing the functionality in C. =over 4 =item add_arg Examples: # Define --hello argument (named parameters) $plugin->add_arg( spec => 'hello=s', help => "--hello\n Hello string", required => 1, ); # Define --hello argument (positional parameters) # Parameter order is 'spec', 'help', 'default', 'required?' $plugin->add_arg('hello=s', "--hello\n Hello string", undef, 1); See L for more details. =item getopts() Parses and processes the command line options you've defined, automatically doing the right thing with help/usage/version arguments. See L for more details. =item opts() Assuming you've instantiated it by passing 'usage' to new(), opts() returns the Nagios::Plugin object's C object, with which you can do lots of great things. E.g. if ( $plugin->opts->verbose ) { print "yah yah YAH YAH YAH!!!"; } # start counting down to timeout alarm $plugin->opts->timeout; your_long_check_step_that_might_time_out(); # access any of your custom command line options, # assuming you've done these steps above: # $plugin->add_arg('my_argument=s', '--my_argument [STRING]'); # $plugin->getopts; print $plugin->opts->my_argument; Again, see L. =back =head2 EXIT METHODS =over 4 =item nagios_exit( , $message ) Exit with return code CODE, and a standard nagios message of the form "SHORTNAME CODE - $message". =item nagios_die( $message, [] ) Same as nagios_exit(), except that CODE is optional, defaulting to UNKNOWN. NOTE: exceptions are not raised by default to calling code. Set C<$_use_die> flag if this functionality is required (see test code). =item die( $message, [] ) Alias for nagios_die(). Deprecated. =item max_state, max_state_alt These are wrapper function for Nagios::Plugin::Functions::max_state and Nagios::Plugin::Functions::max_state_alt. =back =head2 THRESHOLD METHODS These provide a top level interface to the C module; for more details, see L and L. =over 4 =item check_threshold( $value ) =item check_threshold( check => $value, warning => $warn, critical => $crit ) Evaluates $value against the thresholds and returns OK, CRITICAL, or WARNING constant. The thresholds may be: 1. explicitly set by passing 'warning' and/or 'critical' parameters to C, or, 2. explicitly set by calling C before C, or, 3. implicitly set by command-line parameters -w, -c, --critical or --warning, if you have run C<< $plugin->getopts() >>. You can specify $value as an array of values and each will be checked against the thresholds. The return value is ready to pass to C , e . g ., $p->nagios_exit( return_code => $p->check_threshold($result), message => " sample result was $result" ); =item set_thresholds(warning => "10:25", critical => "~:25") Sets the acceptable ranges and creates the plugin's Nagios::Plugins::Threshold object. See http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT for details and examples of the threshold format. =item threshold() Returns the object's C object, if it has been defined by calling set_thresholds(). You can pass a new Threshold object to it to replace the old one too, but you shouldn't need to do that from a plugin script. =back =head2 MESSAGE METHODS EXPERIMENTAL AND SUBJECT TO CHANGE add_messages and check_messages are higher-level convenience methods to add and then check a set of messages, returning an appropriate return code and/or result message. They are equivalent to maintaining a set of @critical, @warning, and and @ok message arrays (add_message), and then doing a final if test (check_messages) like this: if (@critical) { nagios_exit( CRITICAL, join(' ', @critical) ); } elsif (@warning) { nagios_exit( WARNING, join(' ', @warning) ); } else { nagios_exit( OK, join(' ', @ok) ); } =over 4 =item add_message( , $message ) Add a message with CODE status to the object. May be called multiple times. The messages added are checked by check_messages, following. Only CRITICAL, WARNING, and OK are accepted as valid codes. =item check_messages() Check the current set of messages and return an appropriate nagios return code and/or a result message. In scalar context, returns only a return code; in list context returns both a return code and an output message, suitable for passing directly to nagios_exit() e.g. $code = $np->check_messages; ($code, $message) = $np->check_messages; check_messages returns CRITICAL if any critical messages are found, WARNING if any warning messages are found, and OK otherwise. The message returned in list context defaults to the joined set of error messages; this may be customised using the arguments below. check_messages accepts the following named arguments (none are required): =over 4 =item join => SCALAR A string used to join the relevant array to generate the message string returned in list context i.e. if the 'critical' array @crit is non-empty, check_messages would return: join( $join, @crit ) as the result message. Default: ' ' (space). =item join_all => SCALAR By default, only one set of messages are joined and returned in the result message i.e. if the result is CRITICAL, only the 'critical' messages are included in the result; if WARNING, only the 'warning' messages are included; if OK, the 'ok' messages are included (if supplied) i.e. the default is to return an 'errors-only' type message. If join_all is supplied, however, it will be used as a string to join the resultant critical, warning, and ok messages together i.e. all messages are joined and returned. =item critical => ARRAYREF Additional critical messages to supplement any passed in via add_message(). =item warning => ARRAYREF Additional warning messages to supplement any passed in via add_message(). =item ok => ARRAYREF | SCALAR Additional ok messages to supplement any passed in via add_message(). =back =back =head2 PERFORMANCE DATA METHODS =over 4 =item add_perfdata( label => "size", value => $value, uom => "kB", threshold => $threshold ) Add a set of performance data to the object. May be called multiple times. The performance data is included in the standard plugin output messages by the various exit methods. See the Nagios::Plugin::Performance documentation for more information on performance data and the various field definitions, as well as the relevant section of the Nagios Plugin guidelines (http://nagiosplug.sourceforge.net/developer-guidelines.html#AEN202). =back =head1 EXAMPLES "Enough talk! Show me some examples!" See the file 'check_stuff.pl' in the 't' directory included with the Nagios::Plugin distribution for a complete working example of a plugin script. =head1 VERSIONING The Nagios::Plugin::* modules are currently experimental and so the interfaces may change up until Nagios::Plugin hits version 1.0, although every attempt will be made to keep them as backwards compatible as possible. =head1 SEE ALSO See L for a simple functional interface to a subset of the available Nagios::Plugin functionality. See also L, L, L, L, and L. The Nagios Plugin project page is at http://nagiosplug.sourceforge.net. =head1 BUGS Please report bugs in these modules to the Nagios Plugin development team: nagiosplug-devel@lists.sourceforge.net. =head1 AUTHOR Maintained by the Nagios Plugin development team - http://nagiosplug.sourceforge.net. Originally by Ton Voon, Eton.voon@altinity.comE. =head1 COPYRIGHT AND LICENSE Copyright (C) 2006 by Nagios Plugin Development Team This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.4 or, at your option, any later version of Perl 5 you may have available. =cut Nagios-Plugin-0.36/lib/Nagios/Plugin/0000755000015100001540000000000011675044021016653 5ustar nagiosnagiosNagios-Plugin-0.36/lib/Nagios/Plugin/Threshold.pm0000644000015100001540000000613111674657627021173 0ustar nagiosnagiospackage Nagios::Plugin::Threshold; use 5.006; use strict; use warnings; use base qw(Class::Accessor::Fast); __PACKAGE__->mk_accessors(qw(warning critical)); use Nagios::Plugin::Range; use Nagios::Plugin::Functions qw(:codes nagios_die); our ($VERSION) = $Nagios::Plugin::Functions::VERSION; sub get_status { my ($self, $value) = @_; $value = [ $value ] if (ref $value eq ""); foreach my $v (@$value) { if ($self->critical->is_set) { return CRITICAL if $self->critical->check_range($v); } } foreach my $v (@$value) { if ($self->warning->is_set) { return WARNING if $self->warning->check_range($v); } } return OK; } sub _inflate { my ($self, $value, $key) = @_; # Return an undefined range if $value is undef return Nagios::Plugin::Range->new if ! defined $value; # For refs, check isa N::P::Range if (ref $value) { nagios_die("Invalid $key object: type " . ref $value) unless $value->isa("Nagios::Plugin::Range"); return $value; } # Another quick exit if $value is an empty string return Nagios::Plugin::Range->new if $value eq ""; # Otherwise parse $value my $range = Nagios::Plugin::Range->parse_range_string($value); nagios_die("Cannot parse $key range: '$value'") unless(defined($range)); return $range; } sub set_thresholds { my ($self, %arg) = @_; # Equals new() as a class method return $self->new(%arg) unless ref $self; # On an object, just acts as special mutator $self->set($_, $arg{$_}) foreach qw(warning critical); } sub set { my $self = shift; my ($key, $value) = @_; $self->SUPER::set($key, $self->_inflate($value, $key)); } # Constructor - inflate scalars to N::P::Range objects sub new { my ($self, %arg) = @_; $self->SUPER::new({ map { $_ => $self->_inflate($arg{$_}, $_) } qw(warning critical) }); } 1; __END__ =head1 NAME Nagios::Plugin::Threshold - class for handling Nagios::Plugin thresholds. =head1 SYNOPSIS # NB: This is an internal Nagios::Plugin class. # See Nagios::Plugin itself for public interfaces. # Constructor $t = Nagios::Plugin::Threshold->set_thresholds( warning => $warning_range_string, critical => $critical_range_string, ); # Value checking - returns CRITICAL if in the critical range, # WARNING if in the warning range, and OK otherwise $status = $t->get_status($value); # Accessors - return the associated N::P::Range object $warning_range = $t->warning; $critical_range = $t->critical; =head1 DESCRIPTION Internal Nagios::Plugin class for handling threshold data. See Nagios::Plugin for public interfaces. A threshold object contains (typically) a pair of ranges, associated with a particular severity e.g. warning => range1 critical => range2 =head1 AUTHOR This code is maintained by the Nagios Plugin Development Team: see http://nagiosplug.sourceforge.net. =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2007 Nagios Plugin Development Team This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Nagios-Plugin-0.36/lib/Nagios/Plugin/Functions.pm0000644000015100001540000003121411675043640021170 0ustar nagiosnagios# Functional interface to basic Nagios::Plugin constants, exports, # and functions package Nagios::Plugin::Functions; use 5.006; use strict; use warnings; use File::Basename; use Params::Validate qw(:types validate); use Math::Calc::Units; # Remember to update Nagios::Plugins as well our $VERSION = "0.36"; our @STATUS_CODES = qw(OK WARNING CRITICAL UNKNOWN DEPENDENT); require Exporter; our @ISA = qw(Exporter); our @EXPORT = (@STATUS_CODES, qw(nagios_exit nagios_die check_messages)); our @EXPORT_OK = qw(%ERRORS %STATUS_TEXT @STATUS_CODES get_shortname max_state max_state_alt convert $value_re); our %EXPORT_TAGS = ( all => [ @EXPORT, @EXPORT_OK ], codes => [ @STATUS_CODES ], functions => [ qw(nagios_exit nagios_die check_messages max_state max_state_alt convert) ], ); use constant OK => 0; use constant WARNING => 1; use constant CRITICAL => 2; use constant UNKNOWN => 3; use constant DEPENDENT => 4; our %ERRORS = ( 'OK' => OK, 'WARNING' => WARNING, 'CRITICAL' => CRITICAL, 'UNKNOWN' => UNKNOWN, 'DEPENDENT' => DEPENDENT, ); our %STATUS_TEXT = reverse %ERRORS; my $value = qr/[-+]?[\d\.]+/; our $value_re = qr/$value(?:e$value)?/; # _fake_exit flag and accessor/mutator, for testing my $_fake_exit = 0; sub _fake_exit { @_ ? $_fake_exit = shift : $_fake_exit }; # _use_die flag and accessor/mutator, so exceptions can be raised correctly my $_use_die = 0; sub _use_die { @_ ? $_use_die = shift : $_use_die }; sub get_shortname { my $arg = shift; my $shortname = undef; return $arg->{shortname} if (defined($arg->{shortname})); $shortname = $arg->{plugin} if (defined( $arg->{plugin})); $shortname = uc basename($shortname || $ENV{NAGIOS_PLUGIN} || $0); $shortname =~ s/^CHECK_(?:BY_)?//; # Remove any leading CHECK_[BY_] $shortname =~ s/\..*$//; # Remove any trailing suffix return $shortname; } sub max_state { return CRITICAL if grep { $_ == CRITICAL } @_; return WARNING if grep { $_ == WARNING } @_; return OK if grep { $_ == OK } @_; return UNKNOWN if grep { $_ == UNKNOWN } @_; return DEPENDENT if grep { $_ == DEPENDENT } @_; return UNKNOWN; } sub max_state_alt { return CRITICAL if grep { $_ == CRITICAL } @_; return WARNING if grep { $_ == WARNING } @_; return UNKNOWN if grep { $_ == UNKNOWN } @_; return DEPENDENT if grep { $_ == DEPENDENT } @_; return OK if grep { $_ == OK } @_; return UNKNOWN; } # nagios_exit( $code, $message ) sub nagios_exit { my ($code, $message, $arg) = @_; # Handle named parameters if (defined $code && ($code eq 'return_code' || $code eq 'message')) { # Remove last argument if odd no and last is ref if (int(@_ / 2) != @_ / 2 && ref $_[$#_]) { $arg = pop @_; } else { undef $arg; } my %arg = @_; $code = $arg{return_code}; $message = $arg{message}; } $arg ||= {}; # Handle string codes $code = $ERRORS{$code} if defined $code && exists $ERRORS{$code}; # Set defaults $code = UNKNOWN unless defined $code && exists $STATUS_TEXT{$code}; $message = '' unless defined $message; if (ref $message && ref $message eq 'ARRAY') { $message = join(' ', map { chomp; $_ } @$message); } else { chomp $message; } # Setup output my $output = "$STATUS_TEXT{$code}"; $output .= " - $message" if defined $message && $message ne ''; my $shortname = ($arg->{plugin} ? $arg->{plugin}->shortname : undef); $shortname ||= get_shortname(); # Should happen only if funnctions are called directly $output = "$shortname $output" if $shortname; if ($arg->{plugin}) { my $plugin = $arg->{plugin}; $output .= " | ". $plugin->all_perfoutput if $plugin->perfdata && $plugin->all_perfoutput; } $output .= "\n"; # Don't actually exit if _fake_exit set if ($_fake_exit) { require Nagios::Plugin::ExitResult; return Nagios::Plugin::ExitResult->new($code, $output); } _nagios_exit($code, $output); } sub _nagios_exit { my ($code, $output) = @_; # Print output and exit; die if flag set and called via a die in stack backtrace if ($_use_die) { for (my $i = 0;; $i++) { @_ = caller($i); last unless @_; if ($_[3] =~ m/die/) { $! = $code; die($output); } } } print $output; exit $code; } # nagios_die( $message, [ $code ]) OR nagios_die( $code, $message ) # Default $code: UNKNOWN sub nagios_die { my ($arg1, $arg2, $rest) = @_; # Named parameters if (defined $arg1 && ($arg1 eq 'return_code' || $arg1 eq 'message')) { return nagios_exit(@_); } # ($code, $message) elsif (defined $arg1 && (exists $ERRORS{$arg1} || exists $STATUS_TEXT{$arg1})) { return nagios_exit(@_); } # ($message, $code) elsif (defined $arg2 && (exists $ERRORS{$arg2} || exists $STATUS_TEXT{$arg2})) { return nagios_exit($arg2, $arg1, $rest); } # Else just assume $arg1 is the message and hope for the best else { return nagios_exit( UNKNOWN, $arg1, $arg2 ); } } # For backwards compatibility sub die { nagios_die(@_); } # ------------------------------------------------------------------------ # Utility functions # Simple wrapper around Math::Calc::Units::convert sub convert { my ($value, $from, $to) = @_; my ($newval) = Math::Calc::Units::convert("$value $from", $to, 'exact'); return $newval; } # ------------------------------------------------------------------------ # check_messages - return a status and/or message based on a set of # message arrays. # Returns a nagios status code in scalar context. # Returns a code and a message in list context. # The message is join($join, @array) for the relevant array for the code, # or join($join_all, $message) for all arrays if $join_all is set. sub check_messages { my %arg = validate( @_, { critical => { type => ARRAYREF }, warning => { type => ARRAYREF }, ok => { type => ARRAYREF | SCALAR, optional => 1 }, 'join' => { default => ' ' }, join_all => 0, }); $arg{join} = ' ' unless defined $arg{join}; # Decide $code my $code = OK; $code ||= CRITICAL if @{$arg{critical}}; $code ||= WARNING if @{$arg{warning}}; return $code unless wantarray; # Compose message my $message = ''; if ($arg{join_all}) { $message = join( $arg{join_all}, map { @$_ ? join( $arg{'join'}, @$_) : () } $arg{critical}, $arg{warning}, $arg{ok} ? (ref $arg{ok} ? $arg{ok} : [ $arg{ok} ]) : [] ); } else { $message ||= join( $arg{'join'}, @{$arg{critical}} ) if $code == CRITICAL; $message ||= join( $arg{'join'}, @{$arg{warning}} ) if $code == WARNING; $message ||= ref $arg{ok} ? join( $arg{'join'}, @{$arg{ok}} ) : $arg{ok} if $arg{ok}; } return ($code, $message); } # ------------------------------------------------------------------------ 1; # vim:sw=4:sm:et __END__ =head1 NAME Nagios::Plugin::Functions - functions to simplify the creation of Nagios plugins =head1 SYNOPSIS # Constants OK, WARNING, CRITICAL, and UNKNOWN exported by default use Nagios::Plugin::Functions; # nagios_exit( CODE, $message ) - exit with error code CODE, # and message "PLUGIN CODE - $message" nagios_exit( CRITICAL, $critical_error ) if $critical_error; nagios_exit( WARNING, $warning_error ) if $warning_error; nagios_exit( OK, $result ); # nagios_die( $message, [$CODE] ) - just like nagios_exit(), # but CODE is optional, defaulting to UNKNOWN do_something() or nagios_die("do_something() failed horribly"); do_something_critical() or nagios_die("do_something_critical() failed", CRITICAL); # check_messages - check a set of message arrays, returning a # CODE and/or a result message $code = check_messages(critical => \@crit, warning => \@warn); ($code, $message) = check_messages( critical => \@crit, warning => \@warn, ok => \@ok ); # get_shortname - return the default short name for this plugin # (as used by nagios_exit/die; not exported by default) $shortname = get_shortname(); =head1 DESCRIPTION This module is part of the Nagios::Plugin family, a set of modules for simplifying the creation of Nagios plugins. This module exports convenience functions for the class methods provided by Nagios::Plugin. It is intended for those who prefer a simpler functional interface, and who do not need the additional functionality of Nagios::Plugin. =head2 EXPORTS Nagios status code constants are exported by default: OK WARNING CRITICAL UNKNOWN DEPENDENT as are the following functions: nagios_exit nagios_die check_messages The following variables and functions are exported only on request: %ERRORS %STATUS_TEXT get_shortname max_state max_state_alt =head2 FUNCTIONS The following functions are supported: =over 4 =item nagios_exit( , $message ) Exit with return code CODE, and a standard nagios message of the form "PLUGIN CODE - $message". =item nagios_die( $message, [CODE] ) Same as nagios_exit(), except that CODE is optional, defaulting to UNKNOWN. NOTE: exceptions are not raised by default to calling code. Set C<$_use_die> flag if this functionality is required (see test code). =item check_messages( critical => \@crit, warning => \@warn ) Convenience function to check a set of message arrays and return an appropriate nagios return code and/or a result message. Returns only a return code in scalar context; returns a return code and an error message in list context i.e. # Scalar context $code = check_messages(critical => \@crit, warning => \@warn); # List context ($code, $msg) = check_messages(critical => \@crit, warning => \@warn); check_messages() accepts the following named arguments: =over 4 =item critical => ARRAYREF An arrayref of critical error messages - check_messages() returns CRITICAL if this arrayref is non-empty. Mandatory. =item warning => ARRAYREF An arrayref of warning error messages - check_messages() returns WARNING if this arrayref is non-empty ('critical' is checked first). Mandatory. =item ok => ARRAYREF | SCALAR An arrayref of informational messages (or a single scalar message), used in list context if both the 'critical' and 'warning' arrayrefs are empty. Optional. =item join => SCALAR A string used to join the relevant array to generate the message string returned in list context i.e. if the 'critical' array @crit is non-empty, check_messages would return: join( $join, @crit ) as the result message. Optional; default: ' ' (space). =item join_all => SCALAR By default, only one set of messages are joined and returned in the result message i.e. if the result is CRITICAL, only the 'critical' messages are included in the result; if WARNING, only the 'warning' messages are included; if OK, the 'ok' messages are included (if supplied) i.e. the default is to return an 'errors-only' type message. If join_all is supplied, however, it will be used as a string to join the resultant critical, warning, and ok messages together i.e. all messages are joined and returned. =back =item get_shortname Return the default shortname used for this plugin i.e. the first token reported by nagios_exit/nagios_die. The default is basically uc basename( $ENV{NAGIOS_PLUGIN} || $0 ) with any leading 'CHECK_' and trailing file suffixes removed. get_shortname is not exported by default, so must be explicitly imported. =item max_state(@a) Returns the worst state in the array. Order is: CRITICAL, WARNING, OK, UNKNOWN, DEPENDENT The typical usage of max_state is to initialise the state as UNKNOWN and use it on the result of various test. If no test were performed successfully the state will still be UNKNOWN. =item max_state_alt(@a) Returns the worst state in the array. Order is: CRITICAL, WARNING, UNKNOWN, DEPENDENT, OK This is a true definition of a max state (OK last) and should be used if the internal tests performed can return UNKNOWN. =back =head1 SEE ALSO Nagios::Plugin; the nagios plugin developer guidelines at http://nagiosplug.sourceforge.net/developer-guidelines.html. =head1 AUTHORS This code is maintained by the Nagios Plugin Development Team: http://nagiosplug.sourceforge.net =head1 COPYRIGHT AND LICENSE Copyright (C) 2006 by Nagios Plugin Development Team This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Nagios-Plugin-0.36/lib/Nagios/Plugin/Performance.pm0000644000015100001540000001762411476154215021472 0ustar nagiosnagiospackage Nagios::Plugin::Performance; use 5.006; use strict; use warnings; use Carp; use base qw(Class::Accessor::Fast); __PACKAGE__->mk_ro_accessors( qw(label value uom warning critical min max) ); use Nagios::Plugin::Functions; use Nagios::Plugin::Threshold; use Nagios::Plugin::Range; our ($VERSION) = $Nagios::Plugin::Functions::VERSION; sub import { my ($class, %attr) = @_; $_ = $attr{use_die} || 0; Nagios::Plugin::Functions::_use_die($_); } # This is NOT the same as N::P::Functions::value_re. We leave that to be the strict # version. This one allows commas to be part of the numeric value. my $value = qr/[-+]?[\d\.,]+/; my $value_re = qr/$value(?:e$value)?/; my $value_with_negative_infinity = qr/$value_re|~/; sub _parse { my $class = shift; my $string = shift; $string =~ /^'?([^'=]+)'?=($value_re)([\w%]*);?($value_with_negative_infinity\:?$value_re?)?;?($value_with_negative_infinity\:?$value_re?)?;?($value_re)?;?($value_re)?/o; return undef unless ((defined $1 && $1 ne "") && (defined $2 && $2 ne "")); my @info = ($1, $2, $3, $4, $5, $6, $7); # We convert any commas to periods, in the value fields map { defined $info[$_] && $info[$_] =~ s/,/./go } (1, 3, 4, 5, 6); # Check that $info[1] is an actual value # We do this by returning undef if a warning appears my $performance_value; { my $not_value; local $SIG{__WARN__} = sub { $not_value++ }; $performance_value = $info[1]+0; return undef if $not_value; } my $p = $class->new( label => $info[0], value => $performance_value, uom => $info[2], warning => $info[3], critical => $info[4], min => $info[5], max => $info[6] ); return $p; } # Map undef to '' sub _nvl { my ($self, $value) = @_; defined $value ? $value : '' } sub perfoutput { my $self = shift; # Add quotes if label contains a space character my $label = $self->label; if ($label =~ / /) { $label = "'$label'"; } my $out = sprintf "%s=%s%s;%s;%s;%s;%s", $label, $self->value, $self->_nvl($self->uom), $self->_nvl($self->warning), $self->_nvl($self->critical), $self->_nvl($self->min), $self->_nvl($self->max); # Previous implementation omitted trailing ;; - do we need this? $out =~ s/;;$//; return $out; } sub parse_perfstring { my ($class, $perfstring) = @_; my @perfs = (); my $obj; while ($perfstring) { $perfstring =~ s/^\s*//; # If there is more than 1 equals sign, split it out and parse individually if (@{[$perfstring =~ /=/g]} > 1) { $perfstring =~ s/^(.*?=.*?)\s//; if (defined $1) { $obj = $class->_parse($1); } else { # This could occur if perfdata was soemthing=value= # Since this is invalid, we reset the string and continue $perfstring = ""; $obj = $class->_parse($perfstring); } } else { $obj = $class->_parse($perfstring); $perfstring = ""; } push @perfs, $obj if $obj; } return @perfs; } sub rrdlabel { my $self = shift; my $name = $self->clean_label; # Shorten return substr( $name, 0, 19 ); } sub clean_label { my $self = shift; my $name = $self->label; if ($name eq "/") { $name = "root"; } elsif ( $name =~ s/^\/// ) { $name =~ s/\//_/g; } # Convert all other characters $name =~ s/\W/_/g; return $name; } # Backward compatibility: create a threshold object on the fly as requested sub threshold { my $self = shift; return Nagios::Plugin::Threshold->set_thresholds( warning => $self->warning, critical => $self->critical ); } # Constructor - unpack thresholds, map args to hashref sub new { my $class = shift; my %arg = @_; # Convert thresholds if (my $threshold = delete $arg{threshold}) { $arg{warning} ||= $threshold->warning . ""; $arg{critical} ||= $threshold->critical . ""; } $class->SUPER::new(\%arg); } 1; __END__ =head1 NAME Nagios::Plugin::Performance - class for handling Nagios::Plugin performance data. =head1 SYNOPSIS use Nagios::Plugin::Performance use_die => 1; # Constructor (also accepts a 'threshold' obj instead of warning/critical) $p = Nagios::Plugin::Performance->new( label => 'size', value => $value, uom => "kB", warning => $warning, critical => $critical, min => $min, max => $max, ); # Parser @perf = Nagios::Plugin::Performance->parse_perfstring( "/=382MB;15264;15269;; /var=218MB;9443;9448" ) or warn("Failed to parse perfstring"); # Accessors for $p (@perf) { printf "label: %s\n", $p->label; printf "value: %s\n", $p->value; printf "uom: %s\n", $p->uom; printf "warning: %s\n", $p->warning; printf "critical: %s\n", $p->critical; printf "min: %s\n", $p->min; printf "max: %s\n", $p->max; # Special accessor returning a threshold obj containing warning/critical $threshold = $p->threshold; } # Perfdata output format i.e. label=value[uom];[warn];[crit];[min];[max] print $p->perfoutput; =head1 DESCRIPTION Nagios::Plugin class for handling performance data. This is a public interface because it could be used by performance graphing routines, such as nagiostat (http://nagiostat.sourceforge.net), perfparse (http://perfparse.sourceforge.net), nagiosgraph (http://nagiosgraph.sourceforge.net) or NagiosGrapher (http://www.nagiosexchange.org/NagiosGrapher.84.0.html). Nagios::Plugin::Performance offers both a parsing interface (via parse_perfstring), for turning nagios performance output strings into their components, and a composition interface (via new), for turning components into perfdata strings. =head1 USE'ING THE MODULE If you are using this module for the purposes of parsing perf data, you will probably want to set use_die => 1 at use time. This forces &Nagios::Plugin::Functions::nagios_exit to call die() - rather than exit() - when an error occurs. This is then trappable by an eval. If you don't set use_die, then an error in these modules will cause your script to exit =head1 CLASS METHODS =over 4 =item Nagios::Plugin::Performance->new(%attributes) Instantiates a new Nagios::Plugin::Performance object with the given attributes. =item Nagios::Plugin::Performance->parse_perfstring($string) Returns an array of Nagios::Plugin::Performance objects based on the string entered. If there is an error parsing the string - which may consists of several sets of data - will return an array with all the successfully parsed sets. If values are input with commas instead of periods, due to different locale settings, then it will still be parsed, but the commas will be converted to periods. =back =head1 OBJECT METHODS (ACCESSORS) =over 4 =item label, value, uom, warning, critical, min, max These all return scalars. min and max are not well supported yet. =item threshold Returns a Nagios::Plugin::Threshold object holding the warning and critical ranges for this performance data (if any). =item rrdlabel Returns a string based on 'label' that is suitable for use as dataset name of an RRD i.e. munges label to be 1-19 characters long with only characters [a-zA-Z0-9_]. This calls $self->clean_label and then truncates to 19 characters. There is no guarantee that multiple N:P:Performance objects will have unique rrdlabels. =item clean_label Returns a "clean" label for use as a dataset name in RRD, ie, it converts characters that are not [a-zA-Z0-9_] to _. It also converts "/" to "root" and "/{name}" to "{name}". =item perfoutput Outputs the data in Nagios::Plugin perfdata format i.e. label=value[uom];[warn];[crit];[min];[max]. =back =head1 SEE ALSO Nagios::Plugin, Nagios::Plugin::Threshold, http://nagiosplug.sourceforge.net. =head1 AUTHOR This code is maintained by the Nagios Plugin Development Team: see http://nagiosplug.sourceforge.net. =head1 COPYRIGHT AND LICENSE Copyright (C) 2006-2007 Nagios Plugin Development Team This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Nagios-Plugin-0.36/lib/Nagios/Plugin/Getopt.pm0000644000015100001540000005460011476154215020466 0ustar nagiosnagios# # Nagios::Plugin::Getopt - OO perl module providing standardised argument # processing for nagios plugins # package Nagios::Plugin::Getopt; use strict; use File::Basename; use Getopt::Long qw(:config no_ignore_case bundling); use Carp; use Params::Validate qw(:all); use base qw(Class::Accessor); use Nagios::Plugin::Functions; use Nagios::Plugin::Config; use vars qw($VERSION); $VERSION = $Nagios::Plugin::Functions::VERSION; # Standard defaults my %DEFAULT = ( timeout => 15, verbose => 0, license => "This nagios plugin is free software, and comes with ABSOLUTELY NO WARRANTY. It may be used, redistributed and/or modified under the terms of the GNU General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt).", ); # Standard arguments my @ARGS = ({ spec => 'usage|?', help => "-?, --usage\n Print usage information", }, { spec => 'help|h', help => "-h, --help\n Print detailed help screen", }, { spec => 'version|V', help => "-V, --version\n Print version information", }, { spec => 'extra-opts:s@', help => "--extra-opts=[section][\@file]\n Read options from an ini file. See http://nagiosplugins.org/extra-opts\n for usage and examples.", }, { spec => 'timeout|t=i', help => "-t, --timeout=INTEGER\n Seconds before plugin times out (default: %s)", default => $DEFAULT{timeout}, }, { spec => 'verbose|v+', help => "-v, --verbose\n Show details for command-line debugging (can repeat up to 3 times)", default => $DEFAULT{verbose}, }, ); # Standard arguments we traditionally display last in the help output my %DEFER_ARGS = map { $_ => 1 } qw(timeout verbose); # ------------------------------------------------------------------------- # Private methods sub _die { my $self = shift; my ($msg) = @_; $msg .= "\n" unless substr($msg, -1) eq "\n"; Nagios::Plugin::Functions::_nagios_exit(3, $msg); } # Return the given attribute, if set, including a final newline sub _attr { my $self = shift; my ($item, $extra) = @_; $extra = '' unless defined $extra; return '' unless $self->{_attr}->{$item}; $self->{_attr}->{$item} . "\n" . $extra; } # Turn argument spec into help-style output sub _spec_to_help { my ($self, $spec, $label) = @_; my ($opts, $type) = split /=/, $spec, 2; my (@short, @long); for (split /\|/, $opts) { if (length $_ == 1) { push @short, "-$_"; } else { push @long, "--$_"; } } my $help = join(', ', @short, @long); if ($type) { if ($label) { $help .= '=' . $label; } else { $help .= $type eq 'i' ? '=INTEGER' : '=STRING'; } } elsif ($label) { carp "Label specified, but there's no type in spec '$spec'"; } $help .= "\n "; return $help; } # Options output for plugin -h sub _options { my $self = shift; my @args = (); my @defer = (); for (@{$self->{_args}}) { if (exists $DEFER_ARGS{$_->{name}}) { push @defer, $_; } else { push @args, $_; } } my @options = (); for my $arg (@args, @defer) { my $help_array = ref $arg->{help} && ref $arg->{help} eq 'ARRAY' ? $arg->{help} : [ $arg->{help} ]; my $label_array = $arg->{label} && ref $arg->{label} && ref $arg->{label} eq 'ARRAY' ? $arg->{label} : [ $arg->{label} ]; my $help_string = ''; for (my $i = 0; $i <= $#$help_array; $i++) { my $help = $help_array->[$i]; # Add spec arguments to help if not already there if ($help =~ m/^\s*-/) { $help_string .= $help; } else { $help_string .= $self->_spec_to_help($arg->{spec}, $label_array->[$i]) . $help; $help_string .= "\n " if $i < $#$help_array; } } # Add help_string to @options if ($help_string =~ m/%s/) { my $default = defined $arg->{default} ? $arg->{default} : ''; # We only handle '%s' formats here, so escape everything else $help_string =~ s/%(?!s)/%%/g; push @options, sprintf($help_string, $default, $default, $default, $default); } else { push @options, $help_string; } } return ' ' . join("\n ", @options); } # Output for plugin -? (or missing/invalid args) sub _usage { my $self = shift; sprintf $self->_attr('usage'), $self->{_attr}->{plugin}; } # Output for plugin -V sub _revision { my $self = shift; my $revision = sprintf "%s %s", $self->{_attr}->{plugin}, $self->{_attr}->{version}; $revision .= sprintf " [%s]", $self->{_attr}->{url} if $self->{_attr}->{url}; $revision .= "\n"; $revision; } # Output for plugin -h sub _help { my $self = shift; my $help = ''; $help .= $self->_revision . "\n"; $help .= $self->_attr('license', "\n"); $help .= $self->_attr('blurb', "\n"); $help .= $self->_usage ? $self->_usage . "\n" : ''; $help .= $self->_options ? $self->_options . "\n" : ''; $help .= $self->_attr('extra', "\n"); return $help; } # Return a Getopt::Long-compatible option array from the current set of specs sub _process_specs_getopt_long { my $self = shift; my @opts = (); for my $arg (@{$self->{_args}}) { push @opts, $arg->{spec}; # Setup names and defaults my $spec = $arg->{spec}; # Use first arg as name (like Getopt::Long does) $spec =~ s/[=:].*$//; my $name = (split /\s*\|\s*/, $spec)[0]; $arg->{name} = $name; if (defined $self->{$name}) { $arg->{default} = $self->{$name}; } else { $self->{$name} = $arg->{default}; } } return @opts; } # Check for existence of required arguments sub _check_required_opts { my $self = shift; my @missing = (); for my $arg (@{$self->{_args}}) { if ($arg->{required} && ! defined $self->{$arg->{name}}) { push @missing, $arg->{name}; } } if (@missing) { $self->_die($self->_usage . "\n" . join("\n", map { sprintf "Missing argument: %s", $_ } @missing) . "\n"); } } # Process and handle any immediate options sub _process_opts { my $self = shift; # Print message and exit for usage, version, help $self->_die($self->_usage) if $self->{usage}; $self->_die($self->_revision) if $self->{version}; $self->_die($self->_help) if $self->{help}; } # ------------------------------------------------------------------------- # Default opts methods sub _load_config_section { my $self = shift; my ($section, $file, $flags) = @_; $section ||= $self->{_attr}->{plugin}; my $Config; eval { $Config = Nagios::Plugin::Config->read($file); }; $self->_die($@) if ($@); #TODO: add test? # TODO: is this check sane? Does --extra-opts=foo require a [foo] section? ## Nevertheless, if we die as UNKNOWN here we should do the same on default ## file *added eval/_die above*. $self->_die("Invalid section '$section' in config file '$file'") unless exists $Config->{$section}; return $Config->{$section}; } # Helper method to setup a hash of spec definitions for _cmdline sub _setup_spec_index { my $self = shift; return if defined $self->{_spec}; $self->{_spec} = { map { $_->{name} => $_->{spec} } @{$self->{_args}} }; } # Quote values that require it sub _cmdline_value { my $self = shift; local $_ = shift; if (m/\s/ && (m/^[^"']/ || m/[^"']$/)) { return qq("$_"); } elsif ($_ eq '') { return q(""); } else { return $_; } } # Helper method to format key/values in $hash in a quasi-commandline format sub _cmdline { my $self = shift; my ($hash) = @_; $hash ||= $self; $self->_setup_spec_index; my @args = (); for my $key (sort keys %$hash) { # Skip internal keys next if $key =~ m/^_/; # Skip defaults and internals next if exists $DEFAULT{$key} && $hash->{$key} eq $DEFAULT{$key}; next if grep { $key eq $_ } qw(help usage version extra-opts); next unless defined $hash->{$key}; # Render arg my $spec = $self->{_spec}->{$key} || ''; if ($spec =~ m/[=:].+$/) { # Arg takes value - may be a scalar or an arrayref for my $value (ref $hash->{$key} eq 'ARRAY' ? @{$hash->{$key}} : ( $hash->{$key} )) { $value = $self->_cmdline_value($value); if (length($key) > 1) { push @args, sprintf "--%s=%s", $key, $value; } else { push @args, "-$key", $value; } } } else { # Flag - render long or short based on option length push @args, (length($key) > 1 ? '--' : '-') . $key; } } return wantarray ? @args : join(' ', @args); } # Process and load extra-opts sections sub _process_extra_opts { my $self = shift; my ($args) = @_; my $extopts_list = $args->{'extra-opts'}; my @sargs = (); for my $extopts (@$extopts_list) { $extopts ||= $self->{_attr}->{plugin}; my $section = $extopts; my $file = ''; # Parse section@file if ($extopts =~ m/^(\w*)@(.*?)\s*$/) { $section = $1; $file = $2; } # Load section args my $shash = $self->_load_config_section($section, $file); # Turn $shash into a series of commandline-like arguments push @sargs, $self->_cmdline($shash); } # Reset ARGV to extra-opts + original @ARGV = ( @sargs, @{$self->{_attr}->{argv}} ); printf "[extra-opts] %s %s\n", $self->{_attr}->{plugin}, join(' ', @ARGV) if $args->{verbose} && $args->{verbose} >= 3; } # ------------------------------------------------------------------------- # Public methods # Define plugin argument sub arg { my $self = shift; my %args; # Named args if ($_[0] =~ m/^(spec|help|required|default)$/ && scalar(@_) % 2 == 0) { %args = validate( @_, { spec => 1, help => 1, default => 0, required => 0, label => 0, }); } # Positional args else { my @args = validate_pos(@_, 1, 1, 0, 0, 0); %args = ( spec => $args[0], help => $args[1], default => $args[2], required => $args[3], label => $args[4], ); } # Add to private args arrayref push @{$self->{_args}}, \%args; } # Process the @ARGV array using the current _args list (possibly exiting) sub getopts { my $self = shift; # Collate spec arguments for Getopt::Long my @opt_array = $self->_process_specs_getopt_long; # Capture original @ARGV (for extra-opts games) $self->{_attr}->{argv} = [ @ARGV ]; # Call GetOptions using @opt_array my $args1 = {}; my $ok = GetOptions($args1, @opt_array); # Invalid options - give usage message and exit $self->_die($self->_usage) unless $ok; # Process extra-opts $self->_process_extra_opts($args1); # Call GetOptions again, this time including extra-opts $ok = GetOptions($self, @opt_array); # Invalid options - give usage message and exit $self->_die($self->_usage) unless $ok; # Process immediate options (possibly exiting) $self->_process_opts; # Required options (possibly exiting) $self->_check_required_opts; # Setup accessors for options $self->mk_ro_accessors(grep ! /^_/, keys %$self); # Setup default alarm handler for alarm($ng->timeout) in plugin $SIG{ALRM} = sub { my $plugin = uc $self->{_attr}->{plugin}; $plugin =~ s/^check_//; $self->_die( sprintf("%s UNKNOWN - plugin timed out (timeout %ss)", $plugin, $self->timeout)); }; } # ------------------------------------------------------------------------- # Constructor sub _init { my $self = shift; # Check params my $plugin = basename($ENV{NAGIOS_PLUGIN} || $0); my %attr = validate( @_, { usage => 1, version => 0, url => 0, plugin => { default => $plugin }, blurb => 0, extra => 0, 'extra-opts' => 0, license => { default => $DEFAULT{license} }, timeout => { default => $DEFAULT{timeout} }, }); # Add attr to private _attr hash (except timeout) $self->{timeout} = delete $attr{timeout}; $self->{_attr} = { %attr }; # Chomp _attr values chomp foreach values %{$self->{_attr}}; # Setup initial args list $self->{_args} = [ @ARGS ]; $self } sub new { my $class = shift; my $self = bless {}, $class; $self->_init(@_); } # ------------------------------------------------------------------------- 1; __END__ =head1 NAME Nagios::Plugin::Getopt - OO perl module providing standardised argument processing for Nagios plugins =head1 SYNOPSIS use Nagios::Plugin::Getopt; # Instantiate object (usage is mandatory) $ng = Nagios::Plugin::Getopt->new( usage => "Usage: %s -H -w -c ", version => '0.1', url => 'http://www.openfusion.com.au/labs/nagios/', blurb => 'This plugin tests various stuff.', ); # Add argument - named parameters (spec and help are mandatory) $ng->arg( spec => 'critical|c=i', help => q(Exit with CRITICAL status if fewer than INTEGER foobars are free), required => 1, default => 10, ); # Add argument - positional parameters - arg spec, help text, # default value, required? (first two mandatory) $ng->arg( 'warning|w=i', q(Exit with WARNING status if fewer than INTEGER foobars are free), 5, 1); # Parse arguments and process standard ones (e.g. usage, help, version) $ng->getopts; # Access arguments using named accessors or or via the generic get() print $ng->warning; print $ng->get('critical'); =head1 DESCRIPTION Nagios::Plugin::Getopt is an OO perl module providing standardised and simplified argument processing for Nagios plugins. It implements a number of standard arguments itself (--help, --version, --usage, --timeout, --verbose, and their short form counterparts), produces standardised nagios plugin help output, and allows additional arguments to be easily defined. =head2 CONSTRUCTOR # Instantiate object (usage is mandatory) $ng = Nagios::Plugin::Getopt->new( usage => 'Usage: %s --hello', version => '0.01', ); The Nagios::Plugin::Getopt constructor accepts the following named arguments: =over 4 =item usage (required) Short usage message used with --usage/-? and with missing required arguments, and included in the longer --help output. Can include a '%s' sprintf placeholder which will be replaced with the plugin name e.g. usage => qq(Usage: %s -H -p [-v]), might be displayed as: $ ./check_tcp_range --usage Usage: check_tcp_range -H -p [-v] =item version (required) Plugin version number, included in the --version/-V output, and in the longer --help output. e.g. $ ./check_tcp_range --version check_tcp_range 0.2 [http://www.openfusion.com.au/labs/nagios/] =item url URL for info about this plugin, included in the --version/-V output, and in the longer --help output (see preceding 'version' example). =item blurb Short plugin description, included in the longer --help output (see below for an example). =item license License text, included in the longer --help output (see below for an example). By default, this is set to the standard nagios plugins GPL license text: This nagios plugin is free software, and comes with ABSOLUTELY NO WARRANTY. It may be used, redistributed and/or modified under the terms of the GNU General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt). Provide your own to replace this text in the help output. =item extra Extra text to be appended at the end of the longer --help output. =item plugin Plugin name. This defaults to the basename of your plugin, which is usually correct, but you can set it explicitly if not. =item timeout Timeout period in seconds, overriding the standard timeout default (15 seconds). =back The full --help output has the following form: version string license string blurb usage string options list extra text The 'blurb' and 'extra text' sections are omitted if not supplied. For example: $ ./check_tcp_range -h check_tcp_range 0.2 [http://www.openfusion.com.au/labs/nagios/] This nagios plugin is free software, and comes with ABSOLUTELY NO WARRANTY. It may be used, redistributed and/or modified under the terms of the GNU General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt). This plugin tests arbitrary ranges/sets of tcp ports for a host. Usage: check_tcp_range -H -p [-v] Options: -h, --help Print detailed help screen -V, --version Print version information -H, --hostname=ADDRESS Host name or IP address -p, --ports=STRING Port numbers to check. Format: comma-separated, colons for ranges, no spaces e.g. 8700:8705,8710:8715,8760 -t, --timeout=INTEGER Seconds before plugin times out (default: 15) -v, --verbose Show details for command-line debugging (can repeat up to 3 times) =head2 ARGUMENTS You can define arguments for your plugin using the arg() method, which supports both named and positional arguments. In both cases the C and C arguments are required, while the C