diff options
Diffstat (limited to 'lib/Monitoring')
| -rw-r--r-- | lib/Monitoring/Plugin.pm | 712 | ||||
| -rw-r--r-- | lib/Monitoring/Plugin/Config.pm | 177 | ||||
| -rw-r--r-- | lib/Monitoring/Plugin/ExitResult.pm | 71 | ||||
| -rw-r--r-- | lib/Monitoring/Plugin/Functions.pm | 445 | ||||
| -rw-r--r-- | lib/Monitoring/Plugin/Getopt.pm | 869 | ||||
| -rw-r--r-- | lib/Monitoring/Plugin/Performance.pm | 294 | ||||
| -rw-r--r-- | lib/Monitoring/Plugin/Range.pm | 169 | ||||
| -rw-r--r-- | lib/Monitoring/Plugin/Threshold.pm | 134 |
8 files changed, 2871 insertions, 0 deletions
diff --git a/lib/Monitoring/Plugin.pm b/lib/Monitoring/Plugin.pm new file mode 100644 index 0000000..f9eb49e --- /dev/null +++ b/lib/Monitoring/Plugin.pm | |||
| @@ -0,0 +1,712 @@ | |||
| 1 | |||
| 2 | package Monitoring::Plugin; | ||
| 3 | |||
| 4 | use Monitoring::Plugin::Functions qw(:codes %ERRORS %STATUS_TEXT @STATUS_CODES); | ||
| 5 | use Params::Validate qw(:all); | ||
| 6 | |||
| 7 | use strict; | ||
| 8 | use warnings; | ||
| 9 | |||
| 10 | use Carp; | ||
| 11 | use base qw(Class::Accessor::Fast); | ||
| 12 | |||
| 13 | Monitoring::Plugin->mk_accessors(qw( | ||
| 14 | shortname | ||
| 15 | perfdata | ||
| 16 | messages | ||
| 17 | opts | ||
| 18 | threshold | ||
| 19 | )); | ||
| 20 | |||
| 21 | use Exporter; | ||
| 22 | our @ISA = qw(Exporter); | ||
| 23 | our @EXPORT = (@STATUS_CODES); | ||
| 24 | our @EXPORT_OK = qw(%ERRORS %STATUS_TEXT); | ||
| 25 | |||
| 26 | # CPAN stupidly won't index this module without a literal $VERSION here, | ||
| 27 | # so we're forced to duplicate it explicitly | ||
| 28 | # Make sure you update $Monitoring::Plugin::Functions::VERSION too | ||
| 29 | our $VERSION = "0.37"; | ||
| 30 | |||
| 31 | sub new { | ||
| 32 | my $class = shift; | ||
| 33 | # my %args = @_; | ||
| 34 | |||
| 35 | my %args = validate( @_, | ||
| 36 | { | ||
| 37 | shortname => 0, | ||
| 38 | usage => 0, | ||
| 39 | version => 0, | ||
| 40 | url => 0, | ||
| 41 | plugin => 0, | ||
| 42 | blurb => 0, | ||
| 43 | extra => 0, | ||
| 44 | license => 0, | ||
| 45 | timeout => 0 | ||
| 46 | }, | ||
| 47 | ); | ||
| 48 | |||
| 49 | my $shortname = Monitoring::Plugin::Functions::get_shortname(\%args); | ||
| 50 | delete $args{shortname} if (exists $args{shortname}); | ||
| 51 | my $self = { | ||
| 52 | shortname => $shortname, | ||
| 53 | perfdata => [], # to be added later | ||
| 54 | messages => { | ||
| 55 | warning => [], | ||
| 56 | critical => [], | ||
| 57 | ok => [] | ||
| 58 | }, | ||
| 59 | opts => undef, # see below | ||
| 60 | threshold => undef, # defined later | ||
| 61 | }; | ||
| 62 | bless $self, $class; | ||
| 63 | if (exists $args{usage}) { | ||
| 64 | require Monitoring::Plugin::Getopt; | ||
| 65 | $self->opts( new Monitoring::Plugin::Getopt(%args) ); | ||
| 66 | } | ||
| 67 | return $self; | ||
| 68 | } | ||
| 69 | |||
| 70 | sub add_perfdata { | ||
| 71 | my ($self, %args) = @_; | ||
| 72 | require Monitoring::Plugin::Performance; | ||
| 73 | my $perf = Monitoring::Plugin::Performance->new(%args); | ||
| 74 | push @{$self->perfdata}, $perf; | ||
| 75 | } | ||
| 76 | sub all_perfoutput { | ||
| 77 | my $self = shift; | ||
| 78 | return join(" ", map {$_->perfoutput} (@{$self->perfdata})); | ||
| 79 | } | ||
| 80 | |||
| 81 | sub set_thresholds { | ||
| 82 | my $self = shift; | ||
| 83 | require Monitoring::Plugin::Threshold; | ||
| 84 | return $self->threshold( Monitoring::Plugin::Threshold->set_thresholds(@_)); | ||
| 85 | } | ||
| 86 | |||
| 87 | # MP::Functions wrappers | ||
| 88 | sub plugin_exit { | ||
| 89 | my $self = shift; | ||
| 90 | Monitoring::Plugin::Functions::plugin_exit(@_, { plugin => $self }); | ||
| 91 | } | ||
| 92 | sub plugin_die { | ||
| 93 | my $self = shift; | ||
| 94 | Monitoring::Plugin::Functions::plugin_die(@_, { plugin => $self }); | ||
| 95 | } | ||
| 96 | sub nagios_exit { | ||
| 97 | my $self = shift; | ||
| 98 | Monitoring::Plugin::Functions::plugin_exit(@_, { plugin => $self }); | ||
| 99 | } | ||
| 100 | sub nagios_die { | ||
| 101 | my $self = shift; | ||
| 102 | Monitoring::Plugin::Functions::plugin_die(@_, { plugin => $self }); | ||
| 103 | } | ||
| 104 | sub die { | ||
| 105 | my $self = shift; | ||
| 106 | Monitoring::Plugin::Functions::plugin_die(@_, { plugin => $self }); | ||
| 107 | } | ||
| 108 | sub max_state { | ||
| 109 | Monitoring::Plugin::Functions::max_state(@_); | ||
| 110 | } | ||
| 111 | sub max_state_alt { | ||
| 112 | Monitoring::Plugin::Functions::max_state_alt(@_); | ||
| 113 | } | ||
| 114 | |||
| 115 | # top level interface to Monitoring::Plugin::Threshold | ||
| 116 | sub check_threshold { | ||
| 117 | my $self = shift; | ||
| 118 | |||
| 119 | my %args; | ||
| 120 | |||
| 121 | if ( $#_ == 0 && (! ref $_[0] || ref $_[0] eq "ARRAY" )) { # one positional param | ||
| 122 | %args = (check => shift); | ||
| 123 | } | ||
| 124 | else { | ||
| 125 | %args = validate ( @_, { # named params | ||
| 126 | check => 1, | ||
| 127 | warning => 0, | ||
| 128 | critical => 0, | ||
| 129 | } ); | ||
| 130 | } | ||
| 131 | |||
| 132 | # in order of preference, get warning and critical from | ||
| 133 | # 1. explicit arguments to check_threshold | ||
| 134 | # 2. previously explicitly set threshold object | ||
| 135 | # 3. implicit options from Getopts object | ||
| 136 | if ( exists $args{warning} || exists $args{critical} ) { | ||
| 137 | $self->set_thresholds( | ||
| 138 | warning => $args{warning}, | ||
| 139 | critical => $args{critical}, | ||
| 140 | ); | ||
| 141 | } | ||
| 142 | elsif ( defined $self->threshold ) { | ||
| 143 | # noop | ||
| 144 | } | ||
| 145 | elsif ( defined $self->opts ) { | ||
| 146 | $self->set_thresholds( | ||
| 147 | warning => $self->opts->warning, | ||
| 148 | critical => $self->opts->critical, | ||
| 149 | ); | ||
| 150 | } | ||
| 151 | else { | ||
| 152 | return UNKNOWN; | ||
| 153 | } | ||
| 154 | |||
| 155 | return $self->threshold->get_status($args{check}); | ||
| 156 | } | ||
| 157 | |||
| 158 | # top level interface to my Monitoring::Plugin::Getopt object | ||
| 159 | sub add_arg { | ||
| 160 | my $self = shift; | ||
| 161 | $self->opts->arg(@_) if $self->_check_for_opts; | ||
| 162 | } | ||
| 163 | sub getopts { | ||
| 164 | my $self = shift; | ||
| 165 | $self->opts->getopts(@_) if $self->_check_for_opts; | ||
| 166 | } | ||
| 167 | |||
| 168 | sub _check_for_opts { | ||
| 169 | my $self = shift; | ||
| 170 | croak | ||
| 171 | "You have to supply a 'usage' param to Monitoring::Plugin::new() if you want to use Getopts from your Monitoring::Plugin object." | ||
| 172 | unless ref $self->opts() eq 'Monitoring::Plugin::Getopt'; | ||
| 173 | return $self; | ||
| 174 | } | ||
| 175 | |||
| 176 | |||
| 177 | |||
| 178 | # ------------------------------------------------------------------------- | ||
| 179 | # MP::Functions::check_messages helpers and wrappers | ||
| 180 | |||
| 181 | sub add_message { | ||
| 182 | my $self = shift; | ||
| 183 | my ($code, @messages) = @_; | ||
| 184 | |||
| 185 | croak "Invalid error code '$code'" | ||
| 186 | unless defined($ERRORS{uc $code}) || defined($STATUS_TEXT{$code}); | ||
| 187 | |||
| 188 | # Store messages using strings rather than numeric codes | ||
| 189 | $code = $STATUS_TEXT{$code} if $STATUS_TEXT{$code}; | ||
| 190 | $code = lc $code; | ||
| 191 | croak "Error code '$code' not supported by add_message" | ||
| 192 | if $code eq 'unknown' || $code eq 'dependent'; | ||
| 193 | |||
| 194 | $self->messages($code, []) unless $self->messages->{$code}; | ||
| 195 | push @{$self->messages->{$code}}, @messages; | ||
| 196 | } | ||
| 197 | |||
| 198 | sub check_messages { | ||
| 199 | my $self = shift; | ||
| 200 | my %args = @_; | ||
| 201 | |||
| 202 | # Add object messages to any passed in as args | ||
| 203 | for my $code (qw(critical warning ok)) { | ||
| 204 | my $messages = $self->messages->{$code} || []; | ||
| 205 | if ($args{$code}) { | ||
| 206 | unless (ref $args{$code} eq 'ARRAY') { | ||
| 207 | if ($code eq 'ok') { | ||
| 208 | $args{$code} = [ $args{$code} ]; | ||
| 209 | } else { | ||
| 210 | croak "Invalid argument '$code'" | ||
| 211 | } | ||
| 212 | } | ||
| 213 | push @{$args{$code}}, @$messages; | ||
| 214 | } | ||
| 215 | else { | ||
| 216 | $args{$code} = $messages; | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | Monitoring::Plugin::Functions::check_messages(%args); | ||
| 221 | } | ||
| 222 | |||
| 223 | # ------------------------------------------------------------------------- | ||
| 224 | |||
| 225 | 1; | ||
| 226 | |||
| 227 | #vim:et:sw=4 | ||
| 228 | |||
| 229 | __END__ | ||
| 230 | |||
| 231 | =head1 NAME | ||
| 232 | |||
| 233 | Monitoring::Plugin - A family of perl modules to streamline writing Naemon, Nagios, | ||
| 234 | Icinga or Shinken (and compatible) plugins. | ||
| 235 | |||
| 236 | =head1 SYNOPSIS | ||
| 237 | |||
| 238 | # Constants OK, WARNING, CRITICAL, and UNKNOWN are exported by default | ||
| 239 | # See also Monitoring::Plugin::Functions for a functional interface | ||
| 240 | use Monitoring::Plugin; | ||
| 241 | |||
| 242 | # Constructor | ||
| 243 | $np = Monitoring::Plugin->new; # OR | ||
| 244 | $np = Monitoring::Plugin->new( shortname => "PAGESIZE" ); # OR | ||
| 245 | |||
| 246 | |||
| 247 | # use Monitoring::Plugin::Getopt to process the @ARGV command line options: | ||
| 248 | # --verbose, --help, --usage, --timeout and --host are defined automatically. | ||
| 249 | $np = Monitoring::Plugin->new( | ||
| 250 | usage => "Usage: %s [ -v|--verbose ] [-H <host>] [-t <timeout>] " | ||
| 251 | . "[ -c|--critical=<threshold> ] [ -w|--warning=<threshold> ]", | ||
| 252 | ); | ||
| 253 | |||
| 254 | # add valid command line options and build them into your usage/help documentation. | ||
| 255 | $np->add_arg( | ||
| 256 | spec => 'warning|w=s', | ||
| 257 | help => '-w, --warning=INTEGER:INTEGER . See ' | ||
| 258 | . 'https://www.monitoring-plugins.org/doc/guidelines.html#THRESHOLDFORMAT ' | ||
| 259 | . 'for the threshold format. ', | ||
| 260 | ); | ||
| 261 | |||
| 262 | # Parse @ARGV and process standard arguments (e.g. usage, help, version) | ||
| 263 | $np->getopts; | ||
| 264 | |||
| 265 | |||
| 266 | # Exit/return value methods - plugin_exit( CODE, MESSAGE ), | ||
| 267 | # plugin_die( MESSAGE, [CODE]) | ||
| 268 | $page = retrieve_page($page1) | ||
| 269 | or $np->plugin_exit( UNKNOWN, "Could not retrieve page" ); | ||
| 270 | # Return code: 3; | ||
| 271 | # output: PAGESIZE UNKNOWN - Could not retrieve page | ||
| 272 | test_page($page) | ||
| 273 | or $np->plugin_exit( CRITICAL, "Bad page found" ); | ||
| 274 | |||
| 275 | # plugin_die() is just like plugin_exit(), but return code defaults | ||
| 276 | # to UNKNOWN | ||
| 277 | $page = retrieve_page($page2) | ||
| 278 | or $np->plugin_die( "Could not retrieve page" ); | ||
| 279 | # Return code: 3; | ||
| 280 | # output: PAGESIZE UNKNOWN - Could not retrieve page | ||
| 281 | |||
| 282 | # Threshold methods | ||
| 283 | $code = $np->check_threshold( | ||
| 284 | check => $value, | ||
| 285 | warning => $warning_threshold, | ||
| 286 | critical => $critical_threshold, | ||
| 287 | ); | ||
| 288 | $np->plugin_exit( $code, "Threshold check failed" ) if $code != OK; | ||
| 289 | |||
| 290 | |||
| 291 | # Message methods (EXPERIMENTAL AND SUBJECT TO CHANGE) - | ||
| 292 | # add_message( CODE, $message ); check_messages() | ||
| 293 | for (@collection) { | ||
| 294 | if (m/Error/) { | ||
| 295 | $np->add_message( CRITICAL, $_ ); | ||
| 296 | } else { | ||
| 297 | $np->add_message( OK, $_ ); | ||
| 298 | } | ||
| 299 | } | ||
| 300 | ($code, $message) = $np->check_messages(); | ||
| 301 | plugin_exit( $code, $message ); | ||
| 302 | # If any items in collection matched m/Error/, returns CRITICAL and | ||
| 303 | # the joined set of Error messages; otherwise returns OK and the | ||
| 304 | # joined set of ok messages | ||
| 305 | |||
| 306 | |||
| 307 | # Perfdata methods | ||
| 308 | $np->add_perfdata( | ||
| 309 | label => "size", | ||
| 310 | value => $value, | ||
| 311 | uom => "kB", | ||
| 312 | threshold => $threshold, | ||
| 313 | ); | ||
| 314 | $np->add_perfdata( label => "time", ... ); | ||
| 315 | $np->plugin_exit( OK, "page size at http://... was ${value}kB" ); | ||
| 316 | # Return code: 0; | ||
| 317 | # output: PAGESIZE OK - page size at http://... was 36kB \ | ||
| 318 | # | size=36kB;10:25;25: time=... | ||
| 319 | |||
| 320 | |||
| 321 | =head1 DESCRIPTION | ||
| 322 | |||
| 323 | Monitoring::Plugin and its associated Monitoring::Plugin::* modules are a | ||
| 324 | family of perl modules to streamline writing Monitoring plugins. The main | ||
| 325 | end user modules are Monitoring::Plugin, providing an object-oriented | ||
| 326 | interface to the entire Monitoring::Plugin::* collection, and | ||
| 327 | Monitoring::Plugin::Functions, providing a simpler functional interface to | ||
| 328 | a useful subset of the available functionality. | ||
| 329 | |||
| 330 | The purpose of the collection is to make it as simple as possible for | ||
| 331 | developers to create plugins that conform the Monitoring Plugin guidelines | ||
| 332 | (https://www.monitoring-plugins.org/doc/guidelines.html). | ||
| 333 | |||
| 334 | |||
| 335 | =head2 EXPORTS | ||
| 336 | |||
| 337 | Nagios status code constants are exported by default: | ||
| 338 | |||
| 339 | OK | ||
| 340 | WARNING | ||
| 341 | CRITICAL | ||
| 342 | UNKNOWN | ||
| 343 | DEPENDENT | ||
| 344 | |||
| 345 | The following variables are also exported on request: | ||
| 346 | |||
| 347 | =over 4 | ||
| 348 | |||
| 349 | =item %ERRORS | ||
| 350 | |||
| 351 | A hash mapping error strings ("CRITICAL", "UNKNOWN", etc.) to the | ||
| 352 | corresponding status code. | ||
| 353 | |||
| 354 | =item %STATUS_TEXT | ||
| 355 | |||
| 356 | A hash mapping status code constants (OK, WARNING, CRITICAL, etc.) to the | ||
| 357 | corresponding error string ("OK", "WARNING, "CRITICAL", etc.) i.e. the | ||
| 358 | reverse of %ERRORS. | ||
| 359 | |||
| 360 | =back | ||
| 361 | |||
| 362 | |||
| 363 | =head2 CONSTRUCTOR | ||
| 364 | |||
| 365 | Monitoring::Plugin->new; | ||
| 366 | |||
| 367 | Monitoring::Plugin->new( shortname => 'PAGESIZE' ); | ||
| 368 | |||
| 369 | Monitoring::Plugin->new( | ||
| 370 | usage => "Usage: %s [ -v|--verbose ] [-H <host>] [-t <timeout>] | ||
| 371 | [ -c|--critical=<critical threshold> ] [ -w|--warning=<warning threshold> ] ", | ||
| 372 | version => $VERSION, | ||
| 373 | blurb => $blurb, | ||
| 374 | extra => $extra, | ||
| 375 | url => $url, | ||
| 376 | license => $license, | ||
| 377 | plugin => basename $0, | ||
| 378 | timeout => 15, | ||
| 379 | ); | ||
| 380 | |||
| 381 | Instantiates a new Monitoring::Plugin object. Accepts the following named | ||
| 382 | arguments: | ||
| 383 | |||
| 384 | =over 4 | ||
| 385 | |||
| 386 | =item shortname | ||
| 387 | |||
| 388 | The 'shortname' for this plugin, used as the first token in the plugin | ||
| 389 | output by the various exit methods. Default: uc basename $0. | ||
| 390 | |||
| 391 | =item usage ("Usage: %s --foo --bar") | ||
| 392 | |||
| 393 | Passing a value for the usage() argument makes Monitoring::Plugin | ||
| 394 | instantiate its own C<Monitoring::Plugin::Getopt> object so you can start | ||
| 395 | doing command line argument processing. See | ||
| 396 | L<Monitoring::Plugin::Getopt/CONSTRUCTOR> for more about "usage" and the | ||
| 397 | following options: | ||
| 398 | |||
| 399 | =item version | ||
| 400 | |||
| 401 | =item url | ||
| 402 | |||
| 403 | =item blurb | ||
| 404 | |||
| 405 | =item license | ||
| 406 | |||
| 407 | =item extra | ||
| 408 | |||
| 409 | =item plugin | ||
| 410 | |||
| 411 | =item timeout | ||
| 412 | |||
| 413 | =back | ||
| 414 | |||
| 415 | =head2 OPTION HANDLING METHODS | ||
| 416 | |||
| 417 | C<Monitoring::Plugin> provides these methods for accessing the functionality in C<Monitoring::Plugin::Getopt>. | ||
| 418 | |||
| 419 | =over 4 | ||
| 420 | |||
| 421 | =item add_arg | ||
| 422 | |||
| 423 | Examples: | ||
| 424 | |||
| 425 | # Define --hello argument (named parameters) | ||
| 426 | $plugin->add_arg( | ||
| 427 | spec => 'hello=s', | ||
| 428 | help => "--hello\n Hello string", | ||
| 429 | required => 1, | ||
| 430 | ); | ||
| 431 | |||
| 432 | # Define --hello argument (positional parameters) | ||
| 433 | # Parameter order is 'spec', 'help', 'default', 'required?' | ||
| 434 | $plugin->add_arg('hello=s', "--hello\n Hello string", undef, 1); | ||
| 435 | |||
| 436 | See L<Monitoring::Plugin::Getopt/ARGUMENTS> for more details. | ||
| 437 | |||
| 438 | =item getopts() | ||
| 439 | |||
| 440 | Parses and processes the command line options you've defined, | ||
| 441 | automatically doing the right thing with help/usage/version arguments. | ||
| 442 | |||
| 443 | See L<Monitoring::Plugin::Getopt/GETOPTS> for more details. | ||
| 444 | |||
| 445 | =item opts() | ||
| 446 | |||
| 447 | Assuming you've instantiated it by passing 'usage' to new(), opts() | ||
| 448 | returns the Monitoring::Plugin object's C<Monitoring::Plugin::Getopt> object, | ||
| 449 | with which you can do lots of great things. | ||
| 450 | |||
| 451 | E.g. | ||
| 452 | |||
| 453 | if ( $plugin->opts->verbose ) { | ||
| 454 | print "yah yah YAH YAH YAH!!!"; | ||
| 455 | } | ||
| 456 | |||
| 457 | # start counting down to timeout | ||
| 458 | alarm $plugin->opts->timeout; | ||
| 459 | your_long_check_step_that_might_time_out(); | ||
| 460 | |||
| 461 | # access any of your custom command line options, | ||
| 462 | # assuming you've done these steps above: | ||
| 463 | # $plugin->add_arg('my_argument=s', '--my_argument [STRING]'); | ||
| 464 | # $plugin->getopts; | ||
| 465 | print $plugin->opts->my_argument; | ||
| 466 | |||
| 467 | Again, see L<Monitoring::Plugin::Getopt>. | ||
| 468 | |||
| 469 | =back | ||
| 470 | |||
| 471 | =head2 EXIT METHODS | ||
| 472 | |||
| 473 | =over 4 | ||
| 474 | |||
| 475 | =item plugin_exit( <CODE>, $message ) | ||
| 476 | |||
| 477 | Exit with return code CODE, and a standard nagios message of the | ||
| 478 | form "SHORTNAME CODE - $message". | ||
| 479 | |||
| 480 | =item plugin_die( $message, [<CODE>] ) | ||
| 481 | |||
| 482 | Same as plugin_exit(), except that CODE is optional, defaulting | ||
| 483 | to UNKNOWN. NOTE: exceptions are not raised by default to calling code. | ||
| 484 | Set C<$_use_die> flag if this functionality is required (see test code). | ||
| 485 | |||
| 486 | =item nagios_exit( <CODE>, $message ) | ||
| 487 | |||
| 488 | Alias for plugin_die(). Deprecated. | ||
| 489 | |||
| 490 | =item nagios_die( $message, [<CODE>] ) | ||
| 491 | |||
| 492 | Alias for plugin_die(). Deprecated. | ||
| 493 | |||
| 494 | =item die( $message, [<CODE>] ) | ||
| 495 | |||
| 496 | Alias for plugin_die(). Deprecated. | ||
| 497 | |||
| 498 | =item max_state, max_state_alt | ||
| 499 | |||
| 500 | These are wrapper function for Monitoring::Plugin::Functions::max_state and | ||
| 501 | Monitoring::Plugin::Functions::max_state_alt. | ||
| 502 | |||
| 503 | =back | ||
| 504 | |||
| 505 | =head2 THRESHOLD METHODS | ||
| 506 | |||
| 507 | These provide a top level interface to the | ||
| 508 | C<Monitoring::Plugin::Threshold> module; for more details, see | ||
| 509 | L<Monitoring::Plugin::Threshold> and L<Monitoring::Plugin::Range>. | ||
| 510 | |||
| 511 | =over 4 | ||
| 512 | |||
| 513 | =item check_threshold( $value ) | ||
| 514 | |||
| 515 | =item check_threshold( check => $value, warning => $warn, critical => $crit ) | ||
| 516 | |||
| 517 | Evaluates $value against the thresholds and returns OK, CRITICAL, or | ||
| 518 | WARNING constant. The thresholds may be: | ||
| 519 | |||
| 520 | 1. explicitly set by passing 'warning' and/or 'critical' parameters to | ||
| 521 | C<check_threshold()>, or, | ||
| 522 | |||
| 523 | 2. explicitly set by calling C<set_thresholds()> before C<check_threshold()>, or, | ||
| 524 | |||
| 525 | 3. implicitly set by command-line parameters -w, -c, --critical or | ||
| 526 | --warning, if you have run C<< $plugin->getopts() >>. | ||
| 527 | |||
| 528 | You can specify $value as an array of values and each will be checked against | ||
| 529 | the thresholds. | ||
| 530 | |||
| 531 | The return value is ready to pass to C <plugin_exit>, e . g ., | ||
| 532 | |||
| 533 | $p->plugin_exit( | ||
| 534 | return_code => $p->check_threshold($result), | ||
| 535 | message => " sample result was $result" | ||
| 536 | ); | ||
| 537 | |||
| 538 | |||
| 539 | =item set_thresholds(warning => "10:25", critical => "~:25") | ||
| 540 | |||
| 541 | Sets the acceptable ranges and creates the plugin's | ||
| 542 | Monitoring::Plugins::Threshold object. See | ||
| 543 | https://www.monitoring-plugins.org/doc/guidelines.html#THRESHOLDFORMAT | ||
| 544 | for details and examples of the threshold format. | ||
| 545 | |||
| 546 | =item threshold() | ||
| 547 | |||
| 548 | Returns the object's C<Monitoring::Plugin::Threshold> object, if it has | ||
| 549 | been defined by calling set_thresholds(). You can pass a new | ||
| 550 | Threshold object to it to replace the old one too, but you shouldn't | ||
| 551 | need to do that from a plugin script. | ||
| 552 | |||
| 553 | =back | ||
| 554 | |||
| 555 | =head2 MESSAGE METHODS | ||
| 556 | |||
| 557 | EXPERIMENTAL AND SUBJECT TO CHANGE | ||
| 558 | |||
| 559 | add_messages and check_messages are higher-level convenience methods to add | ||
| 560 | and then check a set of messages, returning an appropriate return code | ||
| 561 | and/or result message. They are equivalent to maintaining a set of @critical, | ||
| 562 | @warning, and and @ok message arrays (add_message), and then doing a final | ||
| 563 | if test (check_messages) like this: | ||
| 564 | |||
| 565 | if (@critical) { | ||
| 566 | plugin_exit( CRITICAL, join(' ', @critical) ); | ||
| 567 | } | ||
| 568 | elsif (@warning) { | ||
| 569 | plugin_exit( WARNING, join(' ', @warning) ); | ||
| 570 | } | ||
| 571 | else { | ||
| 572 | plugin_exit( OK, join(' ', @ok) ); | ||
| 573 | } | ||
| 574 | |||
| 575 | =over 4 | ||
| 576 | |||
| 577 | =item add_message( <CODE>, $message ) | ||
| 578 | |||
| 579 | Add a message with CODE status to the object. May be called multiple times. | ||
| 580 | The messages added are checked by check_messages, following. | ||
| 581 | |||
| 582 | Only CRITICAL, WARNING, and OK are accepted as valid codes. | ||
| 583 | |||
| 584 | |||
| 585 | =item check_messages() | ||
| 586 | |||
| 587 | Check the current set of messages and return an appropriate nagios return | ||
| 588 | code and/or a result message. In scalar context, returns only a return | ||
| 589 | code; in list context returns both a return code and an output message, | ||
| 590 | suitable for passing directly to plugin_exit() e.g. | ||
| 591 | |||
| 592 | $code = $np->check_messages; | ||
| 593 | ($code, $message) = $np->check_messages; | ||
| 594 | |||
| 595 | check_messages returns CRITICAL if any critical messages are found, WARNING | ||
| 596 | if any warning messages are found, and OK otherwise. The message returned | ||
| 597 | in list context defaults to the joined set of error messages; this may be | ||
| 598 | customised using the arguments below. | ||
| 599 | |||
| 600 | check_messages accepts the following named arguments (none are required): | ||
| 601 | |||
| 602 | =over 4 | ||
| 603 | |||
| 604 | =item join => SCALAR | ||
| 605 | |||
| 606 | A string used to join the relevant array to generate the message | ||
| 607 | string returned in list context i.e. if the 'critical' array @crit | ||
| 608 | is non-empty, check_messages would return: | ||
| 609 | |||
| 610 | join( $join, @crit ) | ||
| 611 | |||
| 612 | as the result message. Default: ' ' (space). | ||
| 613 | |||
| 614 | =item join_all => SCALAR | ||
| 615 | |||
| 616 | By default, only one set of messages are joined and returned in the | ||
| 617 | result message i.e. if the result is CRITICAL, only the 'critical' | ||
| 618 | messages are included in the result; if WARNING, only the 'warning' | ||
| 619 | messages are included; if OK, the 'ok' messages are included (if | ||
| 620 | supplied) i.e. the default is to return an 'errors-only' type | ||
| 621 | message. | ||
| 622 | |||
| 623 | If join_all is supplied, however, it will be used as a string to | ||
| 624 | join the resultant critical, warning, and ok messages together i.e. | ||
| 625 | all messages are joined and returned. | ||
| 626 | |||
| 627 | =item critical => ARRAYREF | ||
| 628 | |||
| 629 | Additional critical messages to supplement any passed in via add_message(). | ||
| 630 | |||
| 631 | =item warning => ARRAYREF | ||
| 632 | |||
| 633 | Additional warning messages to supplement any passed in via add_message(). | ||
| 634 | |||
| 635 | =item ok => ARRAYREF | SCALAR | ||
| 636 | |||
| 637 | Additional ok messages to supplement any passed in via add_message(). | ||
| 638 | |||
| 639 | =back | ||
| 640 | |||
| 641 | =back | ||
| 642 | |||
| 643 | |||
| 644 | =head2 PERFORMANCE DATA METHODS | ||
| 645 | |||
| 646 | =over 4 | ||
| 647 | |||
| 648 | =item add_perfdata( label => "size", value => $value, uom => "kB", threshold => $threshold ) | ||
| 649 | |||
| 650 | Add a set of performance data to the object. May be called multiple times. | ||
| 651 | The performance data is included in the standard plugin output messages by | ||
| 652 | the various exit methods. | ||
| 653 | |||
| 654 | See the Monitoring::Plugin::Performance documentation for more information on | ||
| 655 | performance data and the various field definitions, as well as the relevant | ||
| 656 | section of the Monitoring Plugin guidelines | ||
| 657 | (https://www.monitoring-plugins.org/doc/guidelines.html#AEN202). | ||
| 658 | |||
| 659 | =back | ||
| 660 | |||
| 661 | |||
| 662 | =head1 EXAMPLES | ||
| 663 | |||
| 664 | "Enough talk! Show me some examples!" | ||
| 665 | |||
| 666 | See the file 'check_stuff.pl' in the 't' directory included with the | ||
| 667 | Monitoring::Plugin distribution for a complete working example of a plugin | ||
| 668 | script. | ||
| 669 | |||
| 670 | |||
| 671 | =head1 VERSIONING | ||
| 672 | |||
| 673 | The Monitoring::Plugin::* modules are currently experimental and so the | ||
| 674 | interfaces may change up until Monitoring::Plugin hits version 1.0, although | ||
| 675 | every attempt will be made to keep them as backwards compatible as | ||
| 676 | possible. | ||
| 677 | |||
| 678 | |||
| 679 | =head1 SEE ALSO | ||
| 680 | |||
| 681 | See L<Monitoring::Plugin::Functions> for a simple functional interface to a subset | ||
| 682 | of the available Monitoring::Plugin functionality. | ||
| 683 | |||
| 684 | See also L<Monitoring::Plugin::Getopt>, L<Monitoring::Plugin::Range>, | ||
| 685 | L<Monitoring::Plugin::Performance>, L<Monitoring::Plugin::Range>, and | ||
| 686 | L<Monitoring::Plugin::Threshold>. | ||
| 687 | |||
| 688 | The Monitoring Plugin project page is at http://monitoring-plugins.org. | ||
| 689 | |||
| 690 | |||
| 691 | =head1 BUGS | ||
| 692 | |||
| 693 | Please report bugs in these modules to the Monitoring Plugin development team: | ||
| 694 | devel@monitoring-plugins.org. | ||
| 695 | |||
| 696 | |||
| 697 | =head1 AUTHOR | ||
| 698 | |||
| 699 | Maintained by the Monitoring Plugin development team - | ||
| 700 | https://www.monitoring-plugins.org. | ||
| 701 | |||
| 702 | Originally by Ton Voon, E<lt>ton.voon@altinity.comE<gt>. | ||
| 703 | |||
| 704 | =head1 COPYRIGHT AND LICENSE | ||
| 705 | |||
| 706 | Copyright (C) 2006-2014 by Monitoring Plugin Team | ||
| 707 | |||
| 708 | This library is free software; you can redistribute it and/or modify it | ||
| 709 | under the same terms as Perl itself, either Perl version 5.8.4 or, at your | ||
| 710 | option, any later version of Perl 5 you may have available. | ||
| 711 | |||
| 712 | =cut | ||
diff --git a/lib/Monitoring/Plugin/Config.pm b/lib/Monitoring/Plugin/Config.pm new file mode 100644 index 0000000..5e941d4 --- /dev/null +++ b/lib/Monitoring/Plugin/Config.pm | |||
| @@ -0,0 +1,177 @@ | |||
| 1 | package Monitoring::Plugin::Config; | ||
| 2 | |||
| 3 | use strict; | ||
| 4 | use Carp; | ||
| 5 | use File::Spec; | ||
| 6 | use base qw(Config::Tiny); | ||
| 7 | |||
| 8 | my $FILENAME1 = 'plugins.ini'; | ||
| 9 | my $FILENAME2 = 'nagios-plugins.ini'; | ||
| 10 | my $FILENAME3 = 'monitoring-plugins.ini'; | ||
| 11 | my $CURRENT_FILE = undef; | ||
| 12 | |||
| 13 | # Config paths ending in nagios (search for $FILENAME1) | ||
| 14 | my @MONITORING_CONFIG_PATH = qw(/etc/nagios /usr/local/nagios/etc /usr/local/etc/nagios /etc/opt/nagios); | ||
| 15 | # Config paths not ending in nagios (search for $FILENAME2) | ||
| 16 | my @CONFIG_PATH = qw(/etc /usr/local/etc /etc/opt); | ||
| 17 | |||
| 18 | # Override Config::Tiny::read to default the filename, if not given | ||
| 19 | sub read | ||
| 20 | { | ||
| 21 | my $class = shift; | ||
| 22 | |||
| 23 | unless ($_[0]) { | ||
| 24 | SEARCH: { | ||
| 25 | if ($ENV{MONITORING_CONFIG_PATH} || $ENV{NAGIOS_CONFIG_PATH}) { | ||
| 26 | for (split /:/, ($ENV{MONITORING_CONFIG_PATH} || $ENV{NAGIOS_CONFIG_PATH})) { | ||
| 27 | my $file = File::Spec->catfile($_, $FILENAME1); | ||
| 28 | unshift(@_, $file), last SEARCH if -f $file; | ||
| 29 | $file = File::Spec->catfile($_, $FILENAME2); | ||
| 30 | unshift(@_, $file), last SEARCH if -f $file; | ||
| 31 | $file = File::Spec->catfile($_, $FILENAME3); | ||
| 32 | unshift(@_, $file), last SEARCH if -f $file; | ||
| 33 | } | ||
| 34 | } | ||
| 35 | for (@MONITORING_CONFIG_PATH) { | ||
| 36 | my $file = File::Spec->catfile($_, $FILENAME1); | ||
| 37 | unshift(@_, $file), last SEARCH if -f $file; | ||
| 38 | } | ||
| 39 | for (@CONFIG_PATH) { | ||
| 40 | my $file = File::Spec->catfile($_, $FILENAME2); | ||
| 41 | unshift(@_, $file), last SEARCH if -f $file; | ||
| 42 | $file = File::Spec->catfile($_, $FILENAME3); | ||
| 43 | unshift(@_, $file), last SEARCH if -f $file; | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | # Use die instead of croak, so we can pass a clean message downstream | ||
| 48 | die "Cannot find '$FILENAME1', '$FILENAME2' or '$FILENAME3' in any standard location.\n" unless $_[0]; | ||
| 49 | } | ||
| 50 | |||
| 51 | $CURRENT_FILE = $_[0]; | ||
| 52 | $class->SUPER::read( @_ ); | ||
| 53 | } | ||
| 54 | |||
| 55 | # Straight from Config::Tiny - only changes are repeated property key support | ||
| 56 | # Would be nice if we could just override the per-line handling ... | ||
| 57 | sub read_string | ||
| 58 | { | ||
| 59 | my $class = ref $_[0] ? ref shift : shift; | ||
| 60 | my $self = bless {}, $class; | ||
| 61 | return undef unless defined $_[0]; | ||
| 62 | |||
| 63 | # Parse the file | ||
| 64 | my $ns = '_'; | ||
| 65 | my $counter = 0; | ||
| 66 | foreach ( split /(?:\015{1,2}\012|\015|\012)/, shift ) { | ||
| 67 | $counter++; | ||
| 68 | |||
| 69 | # Skip comments and empty lines | ||
| 70 | next if /^\s*(?:\#|\;|$)/; | ||
| 71 | |||
| 72 | # Handle section headers | ||
| 73 | if ( /^\s*\[\s*(.+?)\s*\]\s*$/ ) { | ||
| 74 | # Create the sub-hash if it doesn't exist. | ||
| 75 | # Without this sections without keys will not | ||
| 76 | # appear at all in the completed struct. | ||
| 77 | $self->{$ns = $1} ||= {}; | ||
| 78 | next; | ||
| 79 | } | ||
| 80 | |||
| 81 | # Handle properties | ||
| 82 | if ( /^\s*([^=]+?)\s*=\s*(.*?)\s*$/ ) { | ||
| 83 | push @{$self->{$ns}->{$1}}, $2; | ||
| 84 | next; | ||
| 85 | } | ||
| 86 | |||
| 87 | return $self->_error( "Syntax error at line $counter: '$_'" ); | ||
| 88 | } | ||
| 89 | |||
| 90 | $self; | ||
| 91 | } | ||
| 92 | |||
| 93 | sub write { croak "Write access not permitted" } | ||
| 94 | |||
| 95 | # Return last file used by read(); | ||
| 96 | sub np_getfile { return $CURRENT_FILE; } | ||
| 97 | |||
| 98 | 1; | ||
| 99 | |||
| 100 | =head1 NAME | ||
| 101 | |||
| 102 | Monitoring::Plugin::Config - read nagios plugin .ini style config files | ||
| 103 | |||
| 104 | =head1 SYNOPSIS | ||
| 105 | |||
| 106 | # Read given nagios plugin config file | ||
| 107 | $Config = Monitoring::Plugin::Config->read( '/etc/nagios/plugins.ini' ); | ||
| 108 | |||
| 109 | # Search for and read default nagios plugin config file | ||
| 110 | $Config = Monitoring::Plugin::Config->read(); | ||
| 111 | |||
| 112 | # Access sections and properties (returns scalars or arrayrefs) | ||
| 113 | $rootproperty = $Config->{_}->{rootproperty}; | ||
| 114 | $one = $Config->{section}->{one}; | ||
| 115 | $Foo = $Config->{section}->{Foo}; | ||
| 116 | |||
| 117 | =head1 DESCRIPTION | ||
| 118 | |||
| 119 | Monitoring::Plugin::Config is a subclass of the excellent Config::Tiny, | ||
| 120 | with the following changes: | ||
| 121 | |||
| 122 | =over 4 | ||
| 123 | |||
| 124 | =item | ||
| 125 | |||
| 126 | Repeated keys are allowed within sections, returning lists instead of scalars | ||
| 127 | |||
| 128 | =item | ||
| 129 | |||
| 130 | Write functionality has been removed i.e. access is read only | ||
| 131 | |||
| 132 | =item | ||
| 133 | |||
| 134 | Monitoring::Plugin::Config searches for a default nagios plugins file if no explicit | ||
| 135 | filename is given to C<read()>. The current standard locations checked are: | ||
| 136 | |||
| 137 | =over 4 | ||
| 138 | |||
| 139 | =item /etc/nagios/plugins.ini | ||
| 140 | |||
| 141 | =item /usr/local/nagios/etc/plugins.ini | ||
| 142 | |||
| 143 | =item /usr/local/etc/nagios /etc/opt/nagios/plugins.ini | ||
| 144 | |||
| 145 | =item /etc/nagios-plugins.ini | ||
| 146 | |||
| 147 | =item /usr/local/etc/nagios-plugins.ini | ||
| 148 | |||
| 149 | =item /etc/opt/nagios-plugins.ini | ||
| 150 | |||
| 151 | =back | ||
| 152 | |||
| 153 | To use a custom location, set a C<NAGIOS_CONFIG_PATH> environment variable | ||
| 154 | to the set of directories that should be checked. The first C<plugins.ini> or | ||
| 155 | C<nagios-plugins.ini> file found will be used. | ||
| 156 | |||
| 157 | =back | ||
| 158 | |||
| 159 | |||
| 160 | =head1 SEE ALSO | ||
| 161 | |||
| 162 | L<Config::Tiny>, L<Monitoring::Plugin> | ||
| 163 | |||
| 164 | |||
| 165 | =head1 AUTHOR | ||
| 166 | |||
| 167 | This code is maintained by the Monitoring Plugin Development Team: see | ||
| 168 | https://monitoring-plugins.org | ||
| 169 | |||
| 170 | =head1 COPYRIGHT AND LICENSE | ||
| 171 | |||
| 172 | Copyright (C) 2006-2014 Monitoring Plugin Development Team | ||
| 173 | |||
| 174 | This library is free software; you can redistribute it and/or modify | ||
| 175 | it under the same terms as Perl itself. | ||
| 176 | |||
| 177 | =cut | ||
diff --git a/lib/Monitoring/Plugin/ExitResult.pm b/lib/Monitoring/Plugin/ExitResult.pm new file mode 100644 index 0000000..aa9f5da --- /dev/null +++ b/lib/Monitoring/Plugin/ExitResult.pm | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | # Tiny helper class to return both output and return_code when testing | ||
| 2 | |||
| 3 | package Monitoring::Plugin::ExitResult; | ||
| 4 | |||
| 5 | use strict; | ||
| 6 | |||
| 7 | # Stringify to message | ||
| 8 | use overload '""' => sub { shift->{message} }; | ||
| 9 | |||
| 10 | # Constructor | ||
| 11 | sub new { | ||
| 12 | my $class = shift; | ||
| 13 | return bless { return_code => $_[0], message => $_[1] }, $class; | ||
| 14 | } | ||
| 15 | |||
| 16 | # Accessors | ||
| 17 | sub message { shift->{message} } | ||
| 18 | sub return_code { shift->{return_code} } | ||
| 19 | sub code { shift->{return_code} } | ||
| 20 | |||
| 21 | 1; | ||
| 22 | |||
| 23 | __END__ | ||
| 24 | |||
| 25 | =head1 NAME | ||
| 26 | |||
| 27 | Monitoring::Plugin::ExitResult - Helper class for returning both output and | ||
| 28 | return codes when testing. | ||
| 29 | |||
| 30 | =head1 SYNOPSIS | ||
| 31 | |||
| 32 | use Test::More; | ||
| 33 | use Monitoring::Plugin::Functions; | ||
| 34 | |||
| 35 | # In a test file somewhere | ||
| 36 | Monitoring::Plugin::Functions::_fake_exit(1); | ||
| 37 | |||
| 38 | # Later ... | ||
| 39 | $e = plugin_exit( CRITICAL, 'aiiii ...' ); | ||
| 40 | print $e->message; | ||
| 41 | print $e->return_code; | ||
| 42 | |||
| 43 | # MP::ExitResult also stringifies to the message output | ||
| 44 | like(plugin_exit( WARNING, 'foobar'), qr/^foo/, 'matches!'); | ||
| 45 | |||
| 46 | |||
| 47 | |||
| 48 | =head1 DESCRIPTION | ||
| 49 | |||
| 50 | Monitoring::Plugin::ExitResult is a tiny helper class intended for use | ||
| 51 | when testing other Monitoring::Plugin modules. A Monitoring::Plugin::ExitResult | ||
| 52 | object is returned by plugin_exit() and friends when | ||
| 53 | Monitoring::Plugin::Functions::_fake_exit has been set, instead of doing a | ||
| 54 | conventional print + exit. | ||
| 55 | |||
| 56 | =head1 AUTHOR | ||
| 57 | |||
| 58 | This code is maintained by the Monitoring Plugin Development Team: see | ||
| 59 | https://monitoring-plugins.org | ||
| 60 | |||
| 61 | Originally: | ||
| 62 | Gavin Carr , E<lt>gavin@openfusion.com.auE<gt> | ||
| 63 | |||
| 64 | =head1 COPYRIGHT AND LICENSE | ||
| 65 | |||
| 66 | Copyright (C) 2006-2014 Monitoring Plugin Development Team | ||
| 67 | |||
| 68 | This library is free software; you can redistribute it and/or modify | ||
| 69 | it under the same terms as Perl itself. | ||
| 70 | |||
| 71 | =cut | ||
diff --git a/lib/Monitoring/Plugin/Functions.pm b/lib/Monitoring/Plugin/Functions.pm new file mode 100644 index 0000000..d2856e8 --- /dev/null +++ b/lib/Monitoring/Plugin/Functions.pm | |||
| @@ -0,0 +1,445 @@ | |||
| 1 | # Functional interface to basic Monitoring::Plugin constants, exports, | ||
| 2 | # and functions | ||
| 3 | |||
| 4 | package Monitoring::Plugin::Functions; | ||
| 5 | |||
| 6 | use 5.006; | ||
| 7 | |||
| 8 | use strict; | ||
| 9 | use warnings; | ||
| 10 | use File::Basename; | ||
| 11 | use Params::Validate qw(:types validate); | ||
| 12 | use Math::Calc::Units; | ||
| 13 | |||
| 14 | # Remember to update Monitoring::Plugins as well | ||
| 15 | our $VERSION = "0.37"; | ||
| 16 | |||
| 17 | our @STATUS_CODES = qw(OK WARNING CRITICAL UNKNOWN DEPENDENT); | ||
| 18 | |||
| 19 | require Exporter; | ||
| 20 | our @ISA = qw(Exporter); | ||
| 21 | our @EXPORT = (@STATUS_CODES, qw(plugin_exit plugin_die check_messages)); | ||
| 22 | our @EXPORT_OK = qw(%ERRORS %STATUS_TEXT @STATUS_CODES get_shortname max_state max_state_alt convert $value_re); | ||
| 23 | our %EXPORT_TAGS = ( | ||
| 24 | all => [ @EXPORT, @EXPORT_OK ], | ||
| 25 | codes => [ @STATUS_CODES ], | ||
| 26 | functions => [ qw(plugin_exit plugin_die check_messages max_state max_state_alt convert) ], | ||
| 27 | ); | ||
| 28 | |||
| 29 | use constant OK => 0; | ||
| 30 | use constant WARNING => 1; | ||
| 31 | use constant CRITICAL => 2; | ||
| 32 | use constant UNKNOWN => 3; | ||
| 33 | use constant DEPENDENT => 4; | ||
| 34 | |||
| 35 | our %ERRORS = ( | ||
| 36 | 'OK' => OK, | ||
| 37 | 'WARNING' => WARNING, | ||
| 38 | 'CRITICAL' => CRITICAL, | ||
| 39 | 'UNKNOWN' => UNKNOWN, | ||
| 40 | 'DEPENDENT' => DEPENDENT, | ||
| 41 | ); | ||
| 42 | |||
| 43 | our %STATUS_TEXT = reverse %ERRORS; | ||
| 44 | |||
| 45 | my $value = qr/[-+]?[\d\.]+/; | ||
| 46 | our $value_re = qr/$value(?:e$value)?/; | ||
| 47 | |||
| 48 | # _fake_exit flag and accessor/mutator, for testing | ||
| 49 | my $_fake_exit = 0; | ||
| 50 | sub _fake_exit { @_ ? $_fake_exit = shift : $_fake_exit }; | ||
| 51 | |||
| 52 | # _use_die flag and accessor/mutator, so exceptions can be raised correctly | ||
| 53 | my $_use_die = 0; | ||
| 54 | sub _use_die { @_ ? $_use_die = shift : $_use_die }; | ||
| 55 | |||
| 56 | sub get_shortname { | ||
| 57 | my $arg = shift; | ||
| 58 | |||
| 59 | my $shortname = undef; | ||
| 60 | |||
| 61 | return $arg->{shortname} if (defined($arg->{shortname})); | ||
| 62 | $shortname = $arg->{plugin} if (defined( $arg->{plugin})); | ||
| 63 | |||
| 64 | $shortname = uc basename($shortname || $ENV{PLUGIN_NAME} || $ENV{NAGIOS_PLUGIN} || $0); | ||
| 65 | $shortname =~ s/^CHECK_(?:BY_)?//; # Remove any leading CHECK_[BY_] | ||
| 66 | $shortname =~ s/\..*$//; # Remove any trailing suffix | ||
| 67 | return $shortname; | ||
| 68 | } | ||
| 69 | |||
| 70 | sub max_state { | ||
| 71 | return CRITICAL if grep { $_ == CRITICAL } @_; | ||
| 72 | return WARNING if grep { $_ == WARNING } @_; | ||
| 73 | return OK if grep { $_ == OK } @_; | ||
| 74 | return UNKNOWN if grep { $_ == UNKNOWN } @_; | ||
| 75 | return DEPENDENT if grep { $_ == DEPENDENT } @_; | ||
| 76 | return UNKNOWN; | ||
| 77 | } | ||
| 78 | |||
| 79 | sub max_state_alt { | ||
| 80 | return CRITICAL if grep { $_ == CRITICAL } @_; | ||
| 81 | return WARNING if grep { $_ == WARNING } @_; | ||
| 82 | return UNKNOWN if grep { $_ == UNKNOWN } @_; | ||
| 83 | return DEPENDENT if grep { $_ == DEPENDENT } @_; | ||
| 84 | return OK if grep { $_ == OK } @_; | ||
| 85 | return UNKNOWN; | ||
| 86 | } | ||
| 87 | |||
| 88 | # plugin_exit( $code, $message ) | ||
| 89 | sub plugin_exit { | ||
| 90 | my ($code, $message, $arg) = @_; | ||
| 91 | |||
| 92 | # Handle named parameters | ||
| 93 | if (defined $code && ($code eq 'return_code' || $code eq 'message')) { | ||
| 94 | # Remove last argument if odd no and last is ref | ||
| 95 | if (int(@_ / 2) != @_ / 2 && ref $_[$#_]) { | ||
| 96 | $arg = pop @_; | ||
| 97 | } else { | ||
| 98 | undef $arg; | ||
| 99 | } | ||
| 100 | my %arg = @_; | ||
| 101 | $code = $arg{return_code}; | ||
| 102 | $message = $arg{message}; | ||
| 103 | } | ||
| 104 | $arg ||= {}; | ||
| 105 | |||
| 106 | # Handle string codes | ||
| 107 | $code = $ERRORS{$code} if defined $code && exists $ERRORS{$code}; | ||
| 108 | |||
| 109 | # Set defaults | ||
| 110 | $code = UNKNOWN unless defined $code && exists $STATUS_TEXT{$code}; | ||
| 111 | $message = '' unless defined $message; | ||
| 112 | if (ref $message && ref $message eq 'ARRAY') { | ||
| 113 | $message = join(' ', map { chomp; $_ } @$message); | ||
| 114 | } | ||
| 115 | else { | ||
| 116 | chomp $message; | ||
| 117 | } | ||
| 118 | |||
| 119 | # Setup output | ||
| 120 | my $output = "$STATUS_TEXT{$code}"; | ||
| 121 | $output .= " - $message" if defined $message && $message ne ''; | ||
| 122 | my $shortname = ($arg->{plugin} ? $arg->{plugin}->shortname : undef); | ||
| 123 | $shortname ||= get_shortname(); # Should happen only if funnctions are called directly | ||
| 124 | $output = "$shortname $output" if $shortname; | ||
| 125 | if ($arg->{plugin}) { | ||
| 126 | my $plugin = $arg->{plugin}; | ||
| 127 | $output .= " | ". $plugin->all_perfoutput | ||
| 128 | if $plugin->perfdata && $plugin->all_perfoutput; | ||
| 129 | } | ||
| 130 | $output .= "\n"; | ||
| 131 | |||
| 132 | # Don't actually exit if _fake_exit set | ||
| 133 | if ($_fake_exit) { | ||
| 134 | require Monitoring::Plugin::ExitResult; | ||
| 135 | return Monitoring::Plugin::ExitResult->new($code, $output); | ||
| 136 | } | ||
| 137 | |||
| 138 | _plugin_exit($code, $output); | ||
| 139 | } | ||
| 140 | |||
| 141 | sub _plugin_exit { | ||
| 142 | my ($code, $output) = @_; | ||
| 143 | # Print output and exit; die if flag set and called via a die in stack backtrace | ||
| 144 | if ($_use_die) { | ||
| 145 | for (my $i = 0;; $i++) { | ||
| 146 | @_ = caller($i); | ||
| 147 | last unless @_; | ||
| 148 | if ($_[3] =~ m/die/) { | ||
| 149 | $! = $code; | ||
| 150 | die($output); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | } | ||
| 154 | print $output; | ||
| 155 | exit $code; | ||
| 156 | } | ||
| 157 | |||
| 158 | # plugin_die( $message, [ $code ]) OR plugin_die( $code, $message ) | ||
| 159 | # Default $code: UNKNOWN | ||
| 160 | sub plugin_die { | ||
| 161 | my ($arg1, $arg2, $rest) = @_; | ||
| 162 | |||
| 163 | # Named parameters | ||
| 164 | if (defined $arg1 && ($arg1 eq 'return_code' || $arg1 eq 'message')) { | ||
| 165 | return plugin_exit(@_); | ||
| 166 | } | ||
| 167 | |||
| 168 | # ($code, $message) | ||
| 169 | elsif (defined $arg1 && (exists $ERRORS{$arg1} || exists $STATUS_TEXT{$arg1})) { | ||
| 170 | return plugin_exit(@_); | ||
| 171 | } | ||
| 172 | |||
| 173 | # ($message, $code) | ||
| 174 | elsif (defined $arg2 && (exists $ERRORS{$arg2} || exists $STATUS_TEXT{$arg2})) { | ||
| 175 | return plugin_exit($arg2, $arg1, $rest); | ||
| 176 | } | ||
| 177 | |||
| 178 | # Else just assume $arg1 is the message and hope for the best | ||
| 179 | else { | ||
| 180 | return plugin_exit( UNKNOWN, $arg1, $arg2 ); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | # For backwards compatibility | ||
| 185 | sub die { plugin_die(@_); } | ||
| 186 | |||
| 187 | |||
| 188 | # ------------------------------------------------------------------------ | ||
| 189 | # Utility functions | ||
| 190 | |||
| 191 | # Simple wrapper around Math::Calc::Units::convert | ||
| 192 | sub convert | ||
| 193 | { | ||
| 194 | my ($value, $from, $to) = @_; | ||
| 195 | my ($newval) = Math::Calc::Units::convert("$value $from", $to, 'exact'); | ||
| 196 | return $newval; | ||
| 197 | } | ||
| 198 | |||
| 199 | # ------------------------------------------------------------------------ | ||
| 200 | # check_messages - return a status and/or message based on a set of | ||
| 201 | # message arrays. | ||
| 202 | # Returns a nagios status code in scalar context. | ||
| 203 | # Returns a code and a message in list context. | ||
| 204 | # The message is join($join, @array) for the relevant array for the code, | ||
| 205 | # or join($join_all, $message) for all arrays if $join_all is set. | ||
| 206 | sub check_messages { | ||
| 207 | my %arg = validate( @_, { | ||
| 208 | critical => { type => ARRAYREF }, | ||
| 209 | warning => { type => ARRAYREF }, | ||
| 210 | ok => { type => ARRAYREF | SCALAR, optional => 1 }, | ||
| 211 | 'join' => { default => ' ' }, | ||
| 212 | join_all => 0, | ||
| 213 | }); | ||
| 214 | $arg{join} = ' ' unless defined $arg{join}; | ||
| 215 | |||
| 216 | # Decide $code | ||
| 217 | my $code = OK; | ||
| 218 | $code ||= CRITICAL if @{$arg{critical}}; | ||
| 219 | $code ||= WARNING if @{$arg{warning}}; | ||
| 220 | return $code unless wantarray; | ||
| 221 | |||
| 222 | # Compose message | ||
| 223 | my $message = ''; | ||
| 224 | if ($arg{join_all}) { | ||
| 225 | $message = join( $arg{join_all}, | ||
| 226 | map { @$_ ? join( $arg{'join'}, @$_) : () } | ||
| 227 | $arg{critical}, | ||
| 228 | $arg{warning}, | ||
| 229 | $arg{ok} ? (ref $arg{ok} ? $arg{ok} : [ $arg{ok} ]) : [] | ||
| 230 | ); | ||
| 231 | } | ||
| 232 | |||
| 233 | else { | ||
| 234 | $message ||= join( $arg{'join'}, @{$arg{critical}} ) | ||
| 235 | if $code == CRITICAL; | ||
| 236 | $message ||= join( $arg{'join'}, @{$arg{warning}} ) | ||
| 237 | if $code == WARNING; | ||
| 238 | $message ||= ref $arg{ok} ? join( $arg{'join'}, @{$arg{ok}} ) : $arg{ok} | ||
| 239 | if $arg{ok}; | ||
| 240 | } | ||
| 241 | |||
| 242 | return ($code, $message); | ||
| 243 | } | ||
| 244 | |||
| 245 | # ------------------------------------------------------------------------ | ||
| 246 | |||
| 247 | 1; | ||
| 248 | |||
| 249 | # vim:sw=4:sm:et | ||
| 250 | |||
| 251 | __END__ | ||
| 252 | |||
| 253 | =head1 NAME | ||
| 254 | |||
| 255 | Monitoring::Plugin::Functions - functions to simplify the creation of | ||
| 256 | Nagios plugins | ||
| 257 | |||
| 258 | =head1 SYNOPSIS | ||
| 259 | |||
| 260 | # Constants OK, WARNING, CRITICAL, and UNKNOWN exported by default | ||
| 261 | use Monitoring::Plugin::Functions; | ||
| 262 | |||
| 263 | # plugin_exit( CODE, $message ) - exit with error code CODE, | ||
| 264 | # and message "PLUGIN CODE - $message" | ||
| 265 | plugin_exit( CRITICAL, $critical_error ) if $critical_error; | ||
| 266 | plugin_exit( WARNING, $warning_error ) if $warning_error; | ||
| 267 | plugin_exit( OK, $result ); | ||
| 268 | |||
| 269 | # plugin_die( $message, [$CODE] ) - just like plugin_exit(), | ||
| 270 | # but CODE is optional, defaulting to UNKNOWN | ||
| 271 | do_something() | ||
| 272 | or plugin_die("do_something() failed horribly"); | ||
| 273 | do_something_critical() | ||
| 274 | or plugin_die("do_something_critical() failed", CRITICAL); | ||
| 275 | |||
| 276 | # check_messages - check a set of message arrays, returning a | ||
| 277 | # CODE and/or a result message | ||
| 278 | $code = check_messages(critical => \@crit, warning => \@warn); | ||
| 279 | ($code, $message) = check_messages( | ||
| 280 | critical => \@crit, warning => \@warn, | ||
| 281 | ok => \@ok ); | ||
| 282 | |||
| 283 | # get_shortname - return the default short name for this plugin | ||
| 284 | # (as used by plugin_exit/die; not exported by default) | ||
| 285 | $shortname = get_shortname(); | ||
| 286 | |||
| 287 | |||
| 288 | =head1 DESCRIPTION | ||
| 289 | |||
| 290 | This module is part of the Monitoring::Plugin family, a set of modules | ||
| 291 | for simplifying the creation of Nagios plugins. This module exports | ||
| 292 | convenience functions for the class methods provided by | ||
| 293 | Monitoring::Plugin. It is intended for those who prefer a simpler | ||
| 294 | functional interface, and who do not need the additional | ||
| 295 | functionality of Monitoring::Plugin. | ||
| 296 | |||
| 297 | =head2 EXPORTS | ||
| 298 | |||
| 299 | Nagios status code constants are exported by default: | ||
| 300 | |||
| 301 | OK | ||
| 302 | WARNING | ||
| 303 | CRITICAL | ||
| 304 | UNKNOWN | ||
| 305 | DEPENDENT | ||
| 306 | |||
| 307 | as are the following functions: | ||
| 308 | |||
| 309 | plugin_exit | ||
| 310 | plugin_die | ||
| 311 | check_messages | ||
| 312 | |||
| 313 | The following variables and functions are exported only on request: | ||
| 314 | |||
| 315 | %ERRORS | ||
| 316 | %STATUS_TEXT | ||
| 317 | get_shortname | ||
| 318 | max_state | ||
| 319 | max_state_alt | ||
| 320 | |||
| 321 | |||
| 322 | =head2 FUNCTIONS | ||
| 323 | |||
| 324 | The following functions are supported: | ||
| 325 | |||
| 326 | =over 4 | ||
| 327 | |||
| 328 | =item plugin_exit( <CODE>, $message ) | ||
| 329 | |||
| 330 | Exit with return code CODE, and a standard nagios message of the | ||
| 331 | form "PLUGIN CODE - $message". | ||
| 332 | |||
| 333 | =item plugin_die( $message, [CODE] ) | ||
| 334 | |||
| 335 | Same as plugin_exit(), except that CODE is optional, defaulting | ||
| 336 | to UNKNOWN. NOTE: exceptions are not raised by default to calling code. | ||
| 337 | Set C<$_use_die> flag if this functionality is required (see test code). | ||
| 338 | |||
| 339 | =item check_messages( critical => \@crit, warning => \@warn ) | ||
| 340 | |||
| 341 | Convenience function to check a set of message arrays and return | ||
| 342 | an appropriate nagios return code and/or a result message. Returns | ||
| 343 | only a return code in scalar context; returns a return code and an | ||
| 344 | error message in list context i.e. | ||
| 345 | |||
| 346 | # Scalar context | ||
| 347 | $code = check_messages(critical => \@crit, warning => \@warn); | ||
| 348 | # List context | ||
| 349 | ($code, $msg) = check_messages(critical => \@crit, warning => \@warn); | ||
| 350 | |||
| 351 | check_messages() accepts the following named arguments: | ||
| 352 | |||
| 353 | =over 4 | ||
| 354 | |||
| 355 | =item critical => ARRAYREF | ||
| 356 | |||
| 357 | An arrayref of critical error messages - check_messages() returns | ||
| 358 | CRITICAL if this arrayref is non-empty. Mandatory. | ||
| 359 | |||
| 360 | =item warning => ARRAYREF | ||
| 361 | |||
| 362 | An arrayref of warning error messages - check_messages() returns | ||
| 363 | WARNING if this arrayref is non-empty ('critical' is checked | ||
| 364 | first). Mandatory. | ||
| 365 | |||
| 366 | =item ok => ARRAYREF | SCALAR | ||
| 367 | |||
| 368 | An arrayref of informational messages (or a single scalar message), | ||
| 369 | used in list context if both the 'critical' and 'warning' arrayrefs | ||
| 370 | are empty. Optional. | ||
| 371 | |||
| 372 | =item join => SCALAR | ||
| 373 | |||
| 374 | A string used to join the relevant array to generate the message | ||
| 375 | string returned in list context i.e. if the 'critical' array @crit | ||
| 376 | is non-empty, check_messages would return: | ||
| 377 | |||
| 378 | join( $join, @crit ) | ||
| 379 | |||
| 380 | as the result message. Optional; default: ' ' (space). | ||
| 381 | |||
| 382 | =item join_all => SCALAR | ||
| 383 | |||
| 384 | By default, only one set of messages are joined and returned in the | ||
| 385 | result message i.e. if the result is CRITICAL, only the 'critical' | ||
| 386 | messages are included in the result; if WARNING, only the 'warning' | ||
| 387 | messages are included; if OK, the 'ok' messages are included (if | ||
| 388 | supplied) i.e. the default is to return an 'errors-only' type | ||
| 389 | message. | ||
| 390 | |||
| 391 | If join_all is supplied, however, it will be used as a string to | ||
| 392 | join the resultant critical, warning, and ok messages together i.e. | ||
| 393 | all messages are joined and returned. | ||
| 394 | |||
| 395 | =back | ||
| 396 | |||
| 397 | =item get_shortname | ||
| 398 | |||
| 399 | Return the default shortname used for this plugin i.e. the first | ||
| 400 | token reported by plugin_exit/plugin_die. The default is basically | ||
| 401 | |||
| 402 | uc basename( $ENV{PLUGIN_NAME} || $ENV{NAGIOS_PLUGIN} || $0 ) | ||
| 403 | |||
| 404 | with any leading 'CHECK_' and trailing file suffixes removed. | ||
| 405 | |||
| 406 | get_shortname is not exported by default, so must be explicitly | ||
| 407 | imported. | ||
| 408 | |||
| 409 | =item max_state(@a) | ||
| 410 | |||
| 411 | Returns the worst state in the array. Order is: CRITICAL, WARNING, OK, UNKNOWN, | ||
| 412 | DEPENDENT | ||
| 413 | |||
| 414 | The typical usage of max_state is to initialise the state as UNKNOWN and use | ||
| 415 | it on the result of various test. If no test were performed successfully the | ||
| 416 | state will still be UNKNOWN. | ||
| 417 | |||
| 418 | =item max_state_alt(@a) | ||
| 419 | |||
| 420 | Returns the worst state in the array. Order is: CRITICAL, WARNING, UNKNOWN, | ||
| 421 | DEPENDENT, OK | ||
| 422 | |||
| 423 | This is a true definition of a max state (OK last) and should be used if the | ||
| 424 | internal tests performed can return UNKNOWN. | ||
| 425 | |||
| 426 | =back | ||
| 427 | |||
| 428 | =head1 SEE ALSO | ||
| 429 | |||
| 430 | Monitoring::Plugin; the nagios plugin developer guidelines at | ||
| 431 | https://www.monitoring-plugins.org/doc/guidelines.html. | ||
| 432 | |||
| 433 | =head1 AUTHOR | ||
| 434 | |||
| 435 | This code is maintained by the Monitoring Plugin Development Team: see | ||
| 436 | https://monitoring-plugins.org | ||
| 437 | |||
| 438 | =head1 COPYRIGHT AND LICENSE | ||
| 439 | |||
| 440 | Copyright (C) 2006-2014 Monitoring Plugin Development Team | ||
| 441 | |||
| 442 | This library is free software; you can redistribute it and/or modify | ||
| 443 | it under the same terms as Perl itself. | ||
| 444 | |||
| 445 | =cut | ||
diff --git a/lib/Monitoring/Plugin/Getopt.pm b/lib/Monitoring/Plugin/Getopt.pm new file mode 100644 index 0000000..ce1c0f9 --- /dev/null +++ b/lib/Monitoring/Plugin/Getopt.pm | |||
| @@ -0,0 +1,869 @@ | |||
| 1 | # | ||
| 2 | # Monitoring::Plugin::Getopt - OO perl module providing standardised argument | ||
| 3 | # processing for nagios plugins | ||
| 4 | # | ||
| 5 | |||
| 6 | package Monitoring::Plugin::Getopt; | ||
| 7 | |||
| 8 | use strict; | ||
| 9 | use File::Basename; | ||
| 10 | use Getopt::Long qw(:config no_ignore_case bundling); | ||
| 11 | use Carp; | ||
| 12 | use Params::Validate qw(:all); | ||
| 13 | use base qw(Class::Accessor); | ||
| 14 | |||
| 15 | use Monitoring::Plugin::Functions; | ||
| 16 | use Monitoring::Plugin::Config; | ||
| 17 | use vars qw($VERSION); | ||
| 18 | $VERSION = $Monitoring::Plugin::Functions::VERSION; | ||
| 19 | |||
| 20 | # Standard defaults | ||
| 21 | my %DEFAULT = ( | ||
| 22 | timeout => 15, | ||
| 23 | verbose => 0, | ||
| 24 | license => | ||
| 25 | "This nagios plugin is free software, and comes with ABSOLUTELY NO WARRANTY. | ||
| 26 | It may be used, redistributed and/or modified under the terms of the GNU | ||
| 27 | General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt).", | ||
| 28 | ); | ||
| 29 | # Standard arguments | ||
| 30 | my @ARGS = ({ | ||
| 31 | spec => 'usage|?', | ||
| 32 | help => "-?, --usage\n Print usage information", | ||
| 33 | }, { | ||
| 34 | spec => 'help|h', | ||
| 35 | help => "-h, --help\n Print detailed help screen", | ||
| 36 | }, { | ||
| 37 | spec => 'version|V', | ||
| 38 | help => "-V, --version\n Print version information", | ||
| 39 | }, { | ||
| 40 | spec => 'extra-opts:s@', | ||
| 41 | help => "--extra-opts=[section][\@file]\n Read options from an ini file. See http://nagiosplugins.org/extra-opts\n for usage and examples.", | ||
| 42 | }, { | ||
| 43 | spec => 'timeout|t=i', | ||
| 44 | help => "-t, --timeout=INTEGER\n Seconds before plugin times out (default: %s)", | ||
| 45 | default => $DEFAULT{timeout}, | ||
| 46 | }, { | ||
| 47 | spec => 'verbose|v+', | ||
| 48 | help => "-v, --verbose\n Show details for command-line debugging (can repeat up to 3 times)", | ||
| 49 | default => $DEFAULT{verbose}, | ||
| 50 | }, | ||
| 51 | ); | ||
| 52 | # Standard arguments we traditionally display last in the help output | ||
| 53 | my %DEFER_ARGS = map { $_ => 1 } qw(timeout verbose); | ||
| 54 | |||
| 55 | # ------------------------------------------------------------------------- | ||
| 56 | # Private methods | ||
| 57 | |||
| 58 | sub _die | ||
| 59 | { | ||
| 60 | my $self = shift; | ||
| 61 | my ($msg) = @_; | ||
| 62 | $msg .= "\n" unless substr($msg, -1) eq "\n"; | ||
| 63 | Monitoring::Plugin::Functions::_plugin_exit(3, $msg); | ||
| 64 | } | ||
| 65 | |||
| 66 | # Return the given attribute, if set, including a final newline | ||
| 67 | sub _attr | ||
| 68 | { | ||
| 69 | my $self = shift; | ||
| 70 | my ($item, $extra) = @_; | ||
| 71 | $extra = '' unless defined $extra; | ||
| 72 | return '' unless $self->{_attr}->{$item}; | ||
| 73 | $self->{_attr}->{$item} . "\n" . $extra; | ||
| 74 | } | ||
| 75 | |||
| 76 | # Turn argument spec into help-style output | ||
| 77 | sub _spec_to_help | ||
| 78 | { | ||
| 79 | my ($self, $spec, $label) = @_; | ||
| 80 | |||
| 81 | my ($opts, $type) = split /=/, $spec, 2; | ||
| 82 | my (@short, @long); | ||
| 83 | for (split /\|/, $opts) { | ||
| 84 | if (length $_ == 1) { | ||
| 85 | push @short, "-$_"; | ||
| 86 | } else { | ||
| 87 | push @long, "--$_"; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | my $help = join(', ', @short, @long); | ||
| 92 | if ($type) { | ||
| 93 | if ($label) { | ||
| 94 | $help .= '=' . $label; | ||
| 95 | } | ||
| 96 | else { | ||
| 97 | $help .= $type eq 'i' ? '=INTEGER' : '=STRING'; | ||
| 98 | } | ||
| 99 | } | ||
| 100 | elsif ($label) { | ||
| 101 | carp "Label specified, but there's no type in spec '$spec'"; | ||
| 102 | } | ||
| 103 | $help .= "\n "; | ||
| 104 | return $help; | ||
| 105 | } | ||
| 106 | |||
| 107 | # Options output for plugin -h | ||
| 108 | sub _options | ||
| 109 | { | ||
| 110 | my $self = shift; | ||
| 111 | |||
| 112 | my @args = (); | ||
| 113 | my @defer = (); | ||
| 114 | for (@{$self->{_args}}) { | ||
| 115 | if (exists $DEFER_ARGS{$_->{name}}) { | ||
| 116 | push @defer, $_; | ||
| 117 | } else { | ||
| 118 | push @args, $_; | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | my @options = (); | ||
| 123 | for my $arg (@args, @defer) { | ||
| 124 | my $help_array = ref $arg->{help} && ref $arg->{help} eq 'ARRAY' ? $arg->{help} : [ $arg->{help} ]; | ||
| 125 | my $label_array = $arg->{label} && ref $arg->{label} && ref $arg->{label} eq 'ARRAY' ? $arg->{label} : [ $arg->{label} ]; | ||
| 126 | my $help_string = ''; | ||
| 127 | for (my $i = 0; $i <= $#$help_array; $i++) { | ||
| 128 | my $help = $help_array->[$i]; | ||
| 129 | # Add spec arguments to help if not already there | ||
| 130 | if ($help =~ m/^\s*-/) { | ||
| 131 | $help_string .= $help; | ||
| 132 | } | ||
| 133 | else { | ||
| 134 | $help_string .= $self->_spec_to_help($arg->{spec}, $label_array->[$i]) . $help; | ||
| 135 | $help_string .= "\n " if $i < $#$help_array; | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | # Add help_string to @options | ||
| 140 | if ($help_string =~ m/%s/) { | ||
| 141 | my $default = defined $arg->{default} ? $arg->{default} : ''; | ||
| 142 | # We only handle '%s' formats here, so escape everything else | ||
| 143 | $help_string =~ s/%(?!s)/%%/g; | ||
| 144 | push @options, sprintf($help_string, $default, $default, $default, $default); | ||
| 145 | } else { | ||
| 146 | push @options, $help_string; | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | return ' ' . join("\n ", @options); | ||
| 151 | } | ||
| 152 | |||
| 153 | # Output for plugin -? (or missing/invalid args) | ||
| 154 | sub _usage | ||
| 155 | { | ||
| 156 | my $self = shift; | ||
| 157 | sprintf $self->_attr('usage'), $self->{_attr}->{plugin}; | ||
| 158 | } | ||
| 159 | |||
| 160 | # Output for plugin -V | ||
| 161 | sub _revision | ||
| 162 | { | ||
| 163 | my $self = shift; | ||
| 164 | my $revision = sprintf "%s %s", $self->{_attr}->{plugin}, $self->{_attr}->{version}; | ||
| 165 | $revision .= sprintf " [%s]", $self->{_attr}->{url} if $self->{_attr}->{url}; | ||
| 166 | $revision .= "\n"; | ||
| 167 | $revision; | ||
| 168 | } | ||
| 169 | |||
| 170 | # Output for plugin -h | ||
| 171 | sub _help | ||
| 172 | { | ||
| 173 | my $self = shift; | ||
| 174 | my $help = ''; | ||
| 175 | $help .= $self->_revision . "\n"; | ||
| 176 | $help .= $self->_attr('license', "\n"); | ||
| 177 | $help .= $self->_attr('blurb', "\n"); | ||
| 178 | $help .= $self->_usage ? $self->_usage . "\n" : ''; | ||
| 179 | $help .= $self->_options ? $self->_options . "\n" : ''; | ||
| 180 | $help .= $self->_attr('extra', "\n"); | ||
| 181 | return $help; | ||
| 182 | } | ||
| 183 | |||
| 184 | # Return a Getopt::Long-compatible option array from the current set of specs | ||
| 185 | sub _process_specs_getopt_long | ||
| 186 | { | ||
| 187 | my $self = shift; | ||
| 188 | |||
| 189 | my @opts = (); | ||
| 190 | for my $arg (@{$self->{_args}}) { | ||
| 191 | push @opts, $arg->{spec}; | ||
| 192 | # Setup names and defaults | ||
| 193 | my $spec = $arg->{spec}; | ||
| 194 | # Use first arg as name (like Getopt::Long does) | ||
| 195 | $spec =~ s/[=:].*$//; | ||
| 196 | my $name = (split /\s*\|\s*/, $spec)[0]; | ||
| 197 | $arg->{name} = $name; | ||
| 198 | if (defined $self->{$name}) { | ||
| 199 | $arg->{default} = $self->{$name}; | ||
| 200 | } else { | ||
| 201 | $self->{$name} = $arg->{default}; | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | return @opts; | ||
| 206 | } | ||
| 207 | |||
| 208 | # Check for existence of required arguments | ||
| 209 | sub _check_required_opts | ||
| 210 | { | ||
| 211 | my $self = shift; | ||
| 212 | |||
| 213 | my @missing = (); | ||
| 214 | for my $arg (@{$self->{_args}}) { | ||
| 215 | if ($arg->{required} && ! defined $self->{$arg->{name}}) { | ||
| 216 | push @missing, $arg->{name}; | ||
| 217 | } | ||
| 218 | } | ||
| 219 | if (@missing) { | ||
| 220 | $self->_die($self->_usage . "\n" . | ||
| 221 | join("\n", map { sprintf "Missing argument: %s", $_ } @missing) . "\n"); | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | # Process and handle any immediate options | ||
| 226 | sub _process_opts | ||
| 227 | { | ||
| 228 | my $self = shift; | ||
| 229 | |||
| 230 | # Print message and exit for usage, version, help | ||
| 231 | $self->_die($self->_usage) if $self->{usage}; | ||
| 232 | $self->_die($self->_revision) if $self->{version}; | ||
| 233 | $self->_die($self->_help) if $self->{help}; | ||
| 234 | } | ||
| 235 | |||
| 236 | # ------------------------------------------------------------------------- | ||
| 237 | # Default opts methods | ||
| 238 | |||
| 239 | sub _load_config_section | ||
| 240 | { | ||
| 241 | my $self = shift; | ||
| 242 | my ($section, $file, $flags) = @_; | ||
| 243 | $section ||= $self->{_attr}->{plugin}; | ||
| 244 | |||
| 245 | my $Config; | ||
| 246 | eval { $Config = Monitoring::Plugin::Config->read($file); }; | ||
| 247 | $self->_die($@) if ($@); #TODO: add test? | ||
| 248 | |||
| 249 | # TODO: is this check sane? Does --extra-opts=foo require a [foo] section? | ||
| 250 | ## Nevertheless, if we die as UNKNOWN here we should do the same on default | ||
| 251 | ## file *added eval/_die above*. | ||
| 252 | $file ||= $Config->np_getfile(); | ||
| 253 | $self->_die("Invalid section '$section' in config file '$file'") | ||
| 254 | unless exists $Config->{$section}; | ||
| 255 | |||
| 256 | return $Config->{$section}; | ||
| 257 | } | ||
| 258 | |||
| 259 | # Helper method to setup a hash of spec definitions for _cmdline | ||
| 260 | sub _setup_spec_index | ||
| 261 | { | ||
| 262 | my $self = shift; | ||
| 263 | return if defined $self->{_spec}; | ||
| 264 | $self->{_spec} = { map { $_->{name} => $_->{spec} } @{$self->{_args}} }; | ||
| 265 | } | ||
| 266 | |||
| 267 | # Quote values that require it | ||
| 268 | sub _cmdline_value | ||
| 269 | { | ||
| 270 | my $self = shift; | ||
| 271 | local $_ = shift; | ||
| 272 | if (m/\s/ && (m/^[^"']/ || m/[^"']$/)) { | ||
| 273 | return qq("$_"); | ||
| 274 | } | ||
| 275 | elsif ($_ eq '') { | ||
| 276 | return q(""); | ||
| 277 | } | ||
| 278 | else { | ||
| 279 | return $_; | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | # Helper method to format key/values in $hash in a quasi-commandline format | ||
| 284 | sub _cmdline | ||
| 285 | { | ||
| 286 | my $self = shift; | ||
| 287 | my ($hash) = @_; | ||
| 288 | $hash ||= $self; | ||
| 289 | |||
| 290 | $self->_setup_spec_index; | ||
| 291 | |||
| 292 | my @args = (); | ||
| 293 | for my $key (sort keys %$hash) { | ||
| 294 | # Skip internal keys | ||
| 295 | next if $key =~ m/^_/; | ||
| 296 | |||
| 297 | # Skip defaults and internals | ||
| 298 | next if exists $DEFAULT{$key} && $hash->{$key} eq $DEFAULT{$key}; | ||
| 299 | next if grep { $key eq $_ } qw(help usage version extra-opts); | ||
| 300 | next unless defined $hash->{$key}; | ||
| 301 | |||
| 302 | # Render arg | ||
| 303 | my $spec = $self->{_spec}->{$key} || ''; | ||
| 304 | if ($spec =~ m/[=:].+$/) { | ||
| 305 | # Arg takes value - may be a scalar or an arrayref | ||
| 306 | for my $value (ref $hash->{$key} eq 'ARRAY' ? @{$hash->{$key}} : ( $hash->{$key} )) { | ||
| 307 | $value = $self->_cmdline_value($value); | ||
| 308 | if (length($key) > 1) { | ||
| 309 | push @args, sprintf "--%s=%s", $key, $value; | ||
| 310 | } | ||
| 311 | else { | ||
| 312 | push @args, "-$key", $value; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | else { | ||
| 318 | # Flag - render long or short based on option length | ||
| 319 | push @args, (length($key) > 1 ? '--' : '-') . $key; | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | return wantarray ? @args : join(' ', @args); | ||
| 324 | } | ||
| 325 | |||
| 326 | # Process and load extra-opts sections | ||
| 327 | sub _process_extra_opts | ||
| 328 | { | ||
| 329 | my $self = shift; | ||
| 330 | my ($args) = @_; | ||
| 331 | |||
| 332 | my $extopts_list = $args->{'extra-opts'}; | ||
| 333 | |||
| 334 | my @sargs = (); | ||
| 335 | for my $extopts (@$extopts_list) { | ||
| 336 | $extopts ||= $self->{_attr}->{plugin}; | ||
| 337 | my $section = $extopts; | ||
| 338 | my $file = ''; | ||
| 339 | |||
| 340 | # Parse section@file | ||
| 341 | if ($extopts =~ m/^([^@]*)@(.*?)\s*$/) { | ||
| 342 | $section = $1; | ||
| 343 | $file = $2; | ||
| 344 | } | ||
| 345 | |||
| 346 | # Load section args | ||
| 347 | my $shash = $self->_load_config_section($section, $file); | ||
| 348 | |||
| 349 | # Turn $shash into a series of commandline-like arguments | ||
| 350 | push @sargs, $self->_cmdline($shash); | ||
| 351 | } | ||
| 352 | |||
| 353 | # Reset ARGV to extra-opts + original | ||
| 354 | @ARGV = ( @sargs, @{$self->{_attr}->{argv}} ); | ||
| 355 | |||
| 356 | printf "[extra-opts] %s %s\n", $self->{_attr}->{plugin}, join(' ', @ARGV) | ||
| 357 | if $args->{verbose} && $args->{verbose} >= 3; | ||
| 358 | } | ||
| 359 | |||
| 360 | # ------------------------------------------------------------------------- | ||
| 361 | # Public methods | ||
| 362 | |||
| 363 | # Define plugin argument | ||
| 364 | sub arg | ||
| 365 | { | ||
| 366 | my $self = shift; | ||
| 367 | my %args; | ||
| 368 | |||
| 369 | # Named args | ||
| 370 | if ($_[0] =~ m/^(spec|help|required|default)$/ && scalar(@_) % 2 == 0) { | ||
| 371 | %args = validate( @_, { | ||
| 372 | spec => 1, | ||
| 373 | help => 1, | ||
| 374 | default => 0, | ||
| 375 | required => 0, | ||
| 376 | label => 0, | ||
| 377 | }); | ||
| 378 | } | ||
| 379 | |||
| 380 | # Positional args | ||
| 381 | else { | ||
| 382 | my @args = validate_pos(@_, 1, 1, 0, 0, 0); | ||
| 383 | %args = ( | ||
| 384 | spec => $args[0], | ||
| 385 | help => $args[1], | ||
| 386 | default => $args[2], | ||
| 387 | required => $args[3], | ||
| 388 | label => $args[4], | ||
| 389 | ); | ||
| 390 | } | ||
| 391 | |||
| 392 | # Add to private args arrayref | ||
| 393 | push @{$self->{_args}}, \%args; | ||
| 394 | } | ||
| 395 | |||
| 396 | # Process the @ARGV array using the current _args list (possibly exiting) | ||
| 397 | sub getopts | ||
| 398 | { | ||
| 399 | my $self = shift; | ||
| 400 | |||
| 401 | # Collate spec arguments for Getopt::Long | ||
| 402 | my @opt_array = $self->_process_specs_getopt_long; | ||
| 403 | |||
| 404 | # Capture original @ARGV (for extra-opts games) | ||
| 405 | $self->{_attr}->{argv} = [ @ARGV ]; | ||
| 406 | |||
| 407 | # Call GetOptions using @opt_array | ||
| 408 | my $args1 = {}; | ||
| 409 | my $ok = GetOptions($args1, @opt_array); | ||
| 410 | # Invalid options - give usage message and exit | ||
| 411 | $self->_die($self->_usage) unless $ok; | ||
| 412 | |||
| 413 | # Process extra-opts | ||
| 414 | $self->_process_extra_opts($args1); | ||
| 415 | |||
| 416 | # Call GetOptions again, this time including extra-opts | ||
| 417 | $ok = GetOptions($self, @opt_array); | ||
| 418 | # Invalid options - give usage message and exit | ||
| 419 | $self->_die($self->_usage) unless $ok; | ||
| 420 | |||
| 421 | # Process immediate options (possibly exiting) | ||
| 422 | $self->_process_opts; | ||
| 423 | |||
| 424 | # Required options (possibly exiting) | ||
| 425 | $self->_check_required_opts; | ||
| 426 | |||
| 427 | # Setup accessors for options | ||
| 428 | $self->mk_ro_accessors(grep ! /^_/, keys %$self); | ||
| 429 | |||
| 430 | # Setup default alarm handler for alarm($ng->timeout) in plugin | ||
| 431 | $SIG{ALRM} = sub { | ||
| 432 | my $plugin = uc $self->{_attr}->{plugin}; | ||
| 433 | $plugin =~ s/^check_//; | ||
| 434 | $self->_die( | ||
| 435 | sprintf("%s UNKNOWN - plugin timed out (timeout %ss)", | ||
| 436 | $plugin, $self->timeout)); | ||
| 437 | }; | ||
| 438 | } | ||
| 439 | |||
| 440 | # ------------------------------------------------------------------------- | ||
| 441 | # Constructor | ||
| 442 | |||
| 443 | sub _init | ||
| 444 | { | ||
| 445 | my $self = shift; | ||
| 446 | |||
| 447 | # Check params | ||
| 448 | my $plugin = basename($ENV{PLUGIN_NAME} || $ENV{NAGIOS_PLUGIN} || $0); | ||
| 449 | my %attr = validate( @_, { | ||
| 450 | usage => 1, | ||
| 451 | version => 0, | ||
| 452 | url => 0, | ||
| 453 | plugin => { default => $plugin }, | ||
| 454 | blurb => 0, | ||
| 455 | extra => 0, | ||
| 456 | 'extra-opts' => 0, | ||
| 457 | license => { default => $DEFAULT{license} }, | ||
| 458 | timeout => { default => $DEFAULT{timeout} }, | ||
| 459 | }); | ||
| 460 | |||
| 461 | # Add attr to private _attr hash (except timeout) | ||
| 462 | $self->{timeout} = delete $attr{timeout}; | ||
| 463 | $self->{_attr} = { %attr }; | ||
| 464 | # Chomp _attr values | ||
| 465 | chomp foreach values %{$self->{_attr}}; | ||
| 466 | |||
| 467 | # Setup initial args list | ||
| 468 | $self->{_args} = [ @ARGS ]; | ||
| 469 | |||
| 470 | $self | ||
| 471 | } | ||
| 472 | |||
| 473 | sub new | ||
| 474 | { | ||
| 475 | my $class = shift; | ||
| 476 | my $self = bless {}, $class; | ||
| 477 | $self->_init(@_); | ||
| 478 | } | ||
| 479 | |||
| 480 | # ------------------------------------------------------------------------- | ||
| 481 | |||
| 482 | 1; | ||
| 483 | |||
| 484 | __END__ | ||
| 485 | |||
| 486 | =head1 NAME | ||
| 487 | |||
| 488 | Monitoring::Plugin::Getopt - OO perl module providing standardised argument | ||
| 489 | processing for Nagios plugins | ||
| 490 | |||
| 491 | |||
| 492 | =head1 SYNOPSIS | ||
| 493 | |||
| 494 | use Monitoring::Plugin::Getopt; | ||
| 495 | |||
| 496 | # Instantiate object (usage is mandatory) | ||
| 497 | $ng = Monitoring::Plugin::Getopt->new( | ||
| 498 | usage => "Usage: %s -H <host> -w <warning> -c <critical>", | ||
| 499 | version => '0.1', | ||
| 500 | url => 'http://www.openfusion.com.au/labs/nagios/', | ||
| 501 | blurb => 'This plugin tests various stuff.', | ||
| 502 | ); | ||
| 503 | |||
| 504 | # Add argument - named parameters (spec and help are mandatory) | ||
| 505 | $ng->arg( | ||
| 506 | spec => 'critical|c=i', | ||
| 507 | help => q(Exit with CRITICAL status if fewer than INTEGER foobars are free), | ||
| 508 | required => 1, | ||
| 509 | default => 10, | ||
| 510 | ); | ||
| 511 | |||
| 512 | # Add argument - positional parameters - arg spec, help text, | ||
| 513 | # default value, required? (first two mandatory) | ||
| 514 | $ng->arg( | ||
| 515 | 'warning|w=i', | ||
| 516 | q(Exit with WARNING status if fewer than INTEGER foobars are free), | ||
| 517 | 5, | ||
| 518 | 1); | ||
| 519 | |||
| 520 | # Parse arguments and process standard ones (e.g. usage, help, version) | ||
| 521 | $ng->getopts; | ||
| 522 | |||
| 523 | # Access arguments using named accessors or or via the generic get() | ||
| 524 | print $ng->warning; | ||
| 525 | print $ng->get('critical'); | ||
| 526 | |||
| 527 | |||
| 528 | |||
| 529 | =head1 DESCRIPTION | ||
| 530 | |||
| 531 | Monitoring::Plugin::Getopt is an OO perl module providing standardised and | ||
| 532 | simplified argument processing for Nagios plugins. It implements | ||
| 533 | a number of standard arguments itself (--help, --version, | ||
| 534 | --usage, --timeout, --verbose, and their short form counterparts), | ||
| 535 | produces standardised nagios plugin help output, and allows | ||
| 536 | additional arguments to be easily defined. | ||
| 537 | |||
| 538 | |||
| 539 | =head2 CONSTRUCTOR | ||
| 540 | |||
| 541 | # Instantiate object (usage is mandatory) | ||
| 542 | $ng = Monitoring::Plugin::Getopt->new( | ||
| 543 | usage => 'Usage: %s --hello', | ||
| 544 | version => '0.01', | ||
| 545 | ); | ||
| 546 | |||
| 547 | The Monitoring::Plugin::Getopt constructor accepts the following named | ||
| 548 | arguments: | ||
| 549 | |||
| 550 | =over 4 | ||
| 551 | |||
| 552 | =item usage (required) | ||
| 553 | |||
| 554 | Short usage message used with --usage/-? and with missing required | ||
| 555 | arguments, and included in the longer --help output. Can include | ||
| 556 | a '%s' sprintf placeholder which will be replaced with the plugin | ||
| 557 | name e.g. | ||
| 558 | |||
| 559 | usage => qq(Usage: %s -H <hostname> -p <ports> [-v]), | ||
| 560 | |||
| 561 | might be displayed as: | ||
| 562 | |||
| 563 | $ ./check_tcp_range --usage | ||
| 564 | Usage: check_tcp_range -H <hostname> -p <ports> [-v] | ||
| 565 | |||
| 566 | =item version (required) | ||
| 567 | |||
| 568 | Plugin version number, included in the --version/-V output, and in | ||
| 569 | the longer --help output. e.g. | ||
| 570 | |||
| 571 | $ ./check_tcp_range --version | ||
| 572 | check_tcp_range 0.2 [http://www.openfusion.com.au/labs/nagios/] | ||
| 573 | |||
| 574 | =item url | ||
| 575 | |||
| 576 | URL for info about this plugin, included in the --version/-V output, | ||
| 577 | and in the longer --help output (see preceding 'version' example). | ||
| 578 | |||
| 579 | =item blurb | ||
| 580 | |||
| 581 | Short plugin description, included in the longer --help output | ||
| 582 | (see below for an example). | ||
| 583 | |||
| 584 | =item license | ||
| 585 | |||
| 586 | License text, included in the longer --help output (see below for an | ||
| 587 | example). By default, this is set to the standard nagios plugins | ||
| 588 | GPL license text: | ||
| 589 | |||
| 590 | This nagios plugin is free software, and comes with ABSOLUTELY | ||
| 591 | NO WARRANTY. It may be used, redistributed and/or modified under | ||
| 592 | the terms of the GNU General Public Licence (see | ||
| 593 | http://www.fsf.org/licensing/licenses/gpl.txt). | ||
| 594 | |||
| 595 | Provide your own to replace this text in the help output. | ||
| 596 | |||
| 597 | =item extra | ||
| 598 | |||
| 599 | Extra text to be appended at the end of the longer --help output. | ||
| 600 | |||
| 601 | =item plugin | ||
| 602 | |||
| 603 | Plugin name. This defaults to the basename of your plugin, which is | ||
| 604 | usually correct, but you can set it explicitly if not. | ||
| 605 | |||
| 606 | =item timeout | ||
| 607 | |||
| 608 | Timeout period in seconds, overriding the standard timeout default | ||
| 609 | (15 seconds). | ||
| 610 | |||
| 611 | =back | ||
| 612 | |||
| 613 | The full --help output has the following form: | ||
| 614 | |||
| 615 | version string | ||
| 616 | |||
| 617 | license string | ||
| 618 | |||
| 619 | blurb | ||
| 620 | |||
| 621 | usage string | ||
| 622 | |||
| 623 | options list | ||
| 624 | |||
| 625 | extra text | ||
| 626 | |||
| 627 | The 'blurb' and 'extra text' sections are omitted if not supplied. For | ||
| 628 | example: | ||
| 629 | |||
| 630 | $ ./check_tcp_range -h | ||
| 631 | check_tcp_range 0.2 [http://www.openfusion.com.au/labs/nagios/] | ||
| 632 | |||
| 633 | This nagios plugin is free software, and comes with ABSOLUTELY NO WARRANTY. | ||
| 634 | It may be used, redistributed and/or modified under the terms of the GNU | ||
| 635 | General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt). | ||
| 636 | |||
| 637 | This plugin tests arbitrary ranges/sets of tcp ports for a host. | ||
| 638 | |||
| 639 | Usage: check_tcp_range -H <hostname> -p <ports> [-v] | ||
| 640 | |||
| 641 | Options: | ||
| 642 | -h, --help | ||
| 643 | Print detailed help screen | ||
| 644 | -V, --version | ||
| 645 | Print version information | ||
| 646 | -H, --hostname=ADDRESS | ||
| 647 | Host name or IP address | ||
| 648 | -p, --ports=STRING | ||
| 649 | Port numbers to check. Format: comma-separated, colons for ranges, | ||
| 650 | no spaces e.g. 8700:8705,8710:8715,8760 | ||
| 651 | -t, --timeout=INTEGER | ||
| 652 | Seconds before plugin times out (default: 15) | ||
| 653 | -v, --verbose | ||
| 654 | Show details for command-line debugging (can repeat up to 3 times) | ||
| 655 | |||
| 656 | |||
| 657 | =head2 ARGUMENTS | ||
| 658 | |||
| 659 | You can define arguments for your plugin using the arg() method, which | ||
| 660 | supports both named and positional arguments. In both cases | ||
| 661 | the C<spec> and C<help> arguments are required, while the C<label>, | ||
| 662 | C<default>, and C<required> arguments are optional: | ||
| 663 | |||
| 664 | # Define --hello argument (named parameters) | ||
| 665 | $ng->arg( | ||
| 666 | spec => 'hello|h=s', | ||
| 667 | help => "Hello string", | ||
| 668 | required => 1, | ||
| 669 | ); | ||
| 670 | |||
| 671 | # Define --hello argument (positional parameters) | ||
| 672 | # Parameter order is 'spec', 'help', 'default', 'required?', 'label' | ||
| 673 | $ng->arg('hello|h=s', "Hello parameter (default %s)", 5, 1); | ||
| 674 | |||
| 675 | =over 4 | ||
| 676 | |||
| 677 | =item spec | ||
| 678 | |||
| 679 | The C<spec> argument (the first argument in the positional variant) is a | ||
| 680 | L<Getopt::Long> argument specification. See L<Getopt::Long> for the details, | ||
| 681 | but basically it is a series of one or more argument names for this argument | ||
| 682 | (separated by '|'), suffixed with an '=<type>' indicator if the argument | ||
| 683 | takes a value. '=s' indicates a string argument; '=i' indicates an integer | ||
| 684 | argument; appending an '@' indicates multiple such arguments are accepted; | ||
| 685 | and so on. The following are some examples: | ||
| 686 | |||
| 687 | =over 4 | ||
| 688 | |||
| 689 | =item hello=s | ||
| 690 | |||
| 691 | =item hello|h=s | ||
| 692 | |||
| 693 | =item ports|port|p=i | ||
| 694 | |||
| 695 | =item exclude|X=s@ | ||
| 696 | |||
| 697 | =item verbose|v+ | ||
| 698 | |||
| 699 | =back | ||
| 700 | |||
| 701 | =item help | ||
| 702 | |||
| 703 | The C<help> argument is a string displayed in the --help option list output, | ||
| 704 | or it can be a list (an arrayref) of such strings, for multi-line help (see | ||
| 705 | below). | ||
| 706 | |||
| 707 | The help string is munged in two ways: | ||
| 708 | |||
| 709 | =over 4 | ||
| 710 | |||
| 711 | =item | ||
| 712 | |||
| 713 | First, if the help string does NOT begins with a '-' sign, it is prefixed | ||
| 714 | by an expanded form of the C<spec> argument. For instance, the following | ||
| 715 | hello argument: | ||
| 716 | |||
| 717 | $ng->arg( | ||
| 718 | spec => 'hello|h=s', | ||
| 719 | help => "Hello string", | ||
| 720 | ); | ||
| 721 | |||
| 722 | would be displayed in the help output as: | ||
| 723 | |||
| 724 | -h, --hello=STRING | ||
| 725 | Hello string | ||
| 726 | |||
| 727 | where the '-h, --hello=STRING' part is derived from the spec definition | ||
| 728 | (by convention with short args first, then long, then label/type, if any). | ||
| 729 | |||
| 730 | =item | ||
| 731 | |||
| 732 | Second, if the string contains a '%s' it will be formatted via | ||
| 733 | C<sprintf> with the 'default' as the argument i.e. | ||
| 734 | |||
| 735 | sprintf($help, $default) | ||
| 736 | |||
| 737 | =back | ||
| 738 | |||
| 739 | Multi-line help is useful in cases where an argument can be of different types | ||
| 740 | and you want to make this explicit in your help output e.g. | ||
| 741 | |||
| 742 | $ng->arg( | ||
| 743 | spec => 'warning|w=s', | ||
| 744 | help => [ | ||
| 745 | 'Exit with WARNING status if less than BYTES bytes of disk are free', | ||
| 746 | 'Exit with WARNING status if less than PERCENT of disk is free', | ||
| 747 | ], | ||
| 748 | label => [ 'BYTES', 'PERCENT%' ], | ||
| 749 | ); | ||
| 750 | |||
| 751 | would be displayed in the help output as: | ||
| 752 | |||
| 753 | -w, --warning=BYTES | ||
| 754 | Exit with WARNING status if less than BYTES bytes of disk are free | ||
| 755 | -w, --warning=PERCENT% | ||
| 756 | Exit with WARNING status if less than PERCENT of disk space is free | ||
| 757 | |||
| 758 | Note that in this case we've also specified explicit labels in another | ||
| 759 | arrayref corresponding to the C<help> one - if this had been omitted | ||
| 760 | the types would have defaulted to 'STRING', instead of 'BYTES' and | ||
| 761 | 'PERCENT%'. | ||
| 762 | |||
| 763 | |||
| 764 | =item label | ||
| 765 | |||
| 766 | The C<label> argument is a scalar or an arrayref (see 'Multi-line help' | ||
| 767 | description above) that overrides the standard type expansion when generating | ||
| 768 | help text from the spec definition. By default, C<spec=i> arguments are | ||
| 769 | labelled as C<=INTEGER> in the help text, and C<spec=s> arguments are labelled | ||
| 770 | as C<=STRING>. By supplying your own C<label> argument you can override these | ||
| 771 | standard 'INTEGER' and 'STRING' designations. | ||
| 772 | |||
| 773 | For multi-line help, you can supply an ordered list (arrayref) of labels to | ||
| 774 | match the list of help strings e.g. | ||
| 775 | |||
| 776 | label => [ 'BYTES', 'PERCENT%' ] | ||
| 777 | |||
| 778 | Any labels that are left as undef (or just omitted, if trailing) will just | ||
| 779 | use the default 'INTEGER' or 'STRING' designations e.g. | ||
| 780 | |||
| 781 | label => [ undef, 'PERCENT%' ] | ||
| 782 | |||
| 783 | |||
| 784 | =item default | ||
| 785 | |||
| 786 | The C<default> argument is the default value to be given to this parameter | ||
| 787 | if none is explicitly supplied. | ||
| 788 | |||
| 789 | |||
| 790 | =item required | ||
| 791 | |||
| 792 | The C<required> argument is a boolean used to indicate that this argument | ||
| 793 | is mandatory (Monitoring::Plugin::Getopt will exit with your usage message and | ||
| 794 | a 'Missing argument' indicator if any required arguments are not supplied). | ||
| 795 | |||
| 796 | =back | ||
| 797 | |||
| 798 | Note that --help lists your arguments in the order they are defined, so | ||
| 799 | you should order your C<arg()> calls accordingly. | ||
| 800 | |||
| 801 | |||
| 802 | =head2 GETOPTS | ||
| 803 | |||
| 804 | The main parsing and processing functionality is provided by the getopts() | ||
| 805 | method, which takes no arguments: | ||
| 806 | |||
| 807 | # Parse and process arguments | ||
| 808 | $ng->getopts; | ||
| 809 | |||
| 810 | This parses the command line arguments passed to your plugin using | ||
| 811 | Getopt::Long and the builtin and provided argument specifications. | ||
| 812 | Flags and argument values are recorded within the object, and can | ||
| 813 | be accessed either using the generic get() accessor, or using named | ||
| 814 | accessors corresponding to your argument names. For example: | ||
| 815 | |||
| 816 | print $ng->get('hello'); | ||
| 817 | print $ng->hello(); | ||
| 818 | |||
| 819 | if ($ng->verbose) { | ||
| 820 | # ... | ||
| 821 | } | ||
| 822 | |||
| 823 | if ($ng->get('ports') =~ m/:/) { | ||
| 824 | # ... | ||
| 825 | } | ||
| 826 | |||
| 827 | Note that where you have defined alternate argument names, the first is | ||
| 828 | considered the citation form. All the builtin arguments are available | ||
| 829 | using their long variant names. | ||
| 830 | |||
| 831 | |||
| 832 | =head2 BUILTIN PROCESSING | ||
| 833 | |||
| 834 | The C<getopts()> method also handles processing of the immediate builtin | ||
| 835 | arguments, namely --usage, --version, --help, as well as checking all | ||
| 836 | required arguments have been supplied, so you don't have to handle | ||
| 837 | those yourself. This means that your plugin will exit from the getopts() | ||
| 838 | call in these cases - if you want to catch that you can run getopts() | ||
| 839 | within an eval{}. | ||
| 840 | |||
| 841 | C<getopts()> also sets up a default ALRM timeout handler so you can use an | ||
| 842 | |||
| 843 | alarm $ng->timeout; | ||
| 844 | |||
| 845 | around any blocking operations within your plugin (which you are free | ||
| 846 | to override if you want to use a custom timeout message). | ||
| 847 | |||
| 848 | |||
| 849 | =head1 SEE ALSO | ||
| 850 | |||
| 851 | Monitoring::Plugin, Getopt::Long | ||
| 852 | |||
| 853 | |||
| 854 | =head1 AUTHOR | ||
| 855 | |||
| 856 | This code is maintained by the Monitoring Plugin Development Team: see | ||
| 857 | https://monitoring-plugins.org | ||
| 858 | |||
| 859 | Originally: | ||
| 860 | Gavin Carr <gavin@openfusion.com.au> | ||
| 861 | |||
| 862 | =head1 COPYRIGHT AND LICENSE | ||
| 863 | |||
| 864 | Copyright (C) 2006-2014 Monitoring Plugin Development Team | ||
| 865 | |||
| 866 | This library is free software; you can redistribute it and/or modify | ||
| 867 | it under the same terms as Perl itself. | ||
| 868 | |||
| 869 | =cut | ||
diff --git a/lib/Monitoring/Plugin/Performance.pm b/lib/Monitoring/Plugin/Performance.pm new file mode 100644 index 0000000..90fc9f4 --- /dev/null +++ b/lib/Monitoring/Plugin/Performance.pm | |||
| @@ -0,0 +1,294 @@ | |||
| 1 | package Monitoring::Plugin::Performance; | ||
| 2 | |||
| 3 | use 5.006; | ||
| 4 | |||
| 5 | use strict; | ||
| 6 | use warnings; | ||
| 7 | |||
| 8 | use Carp; | ||
| 9 | use base qw(Class::Accessor::Fast); | ||
| 10 | __PACKAGE__->mk_ro_accessors( | ||
| 11 | qw(label value uom warning critical min max) | ||
| 12 | ); | ||
| 13 | |||
| 14 | use Monitoring::Plugin::Functions; | ||
| 15 | use Monitoring::Plugin::Threshold; | ||
| 16 | use Monitoring::Plugin::Range; | ||
| 17 | our ($VERSION) = $Monitoring::Plugin::Functions::VERSION; | ||
| 18 | |||
| 19 | sub import { | ||
| 20 | my ($class, %attr) = @_; | ||
| 21 | $_ = $attr{use_die} || 0; | ||
| 22 | Monitoring::Plugin::Functions::_use_die($_); | ||
| 23 | } | ||
| 24 | |||
| 25 | # This is NOT the same as N::P::Functions::value_re. We leave that to be the strict | ||
| 26 | # version. This one allows commas to be part of the numeric value. | ||
| 27 | my $value = qr/[-+]?[\d\.,]+/; | ||
| 28 | my $value_re = qr/$value(?:e$value)?/; | ||
| 29 | my $value_with_negative_infinity = qr/$value_re|~/; | ||
| 30 | sub _parse { | ||
| 31 | my $class = shift; | ||
| 32 | my $string = shift; | ||
| 33 | $string =~ /^'?([^'=]+)'?=($value_re)([\w%]*);?($value_with_negative_infinity\:?$value_re?)?;?($value_with_negative_infinity\:?$value_re?)?;?($value_re)?;?($value_re)?/o; | ||
| 34 | return undef unless ((defined $1 && $1 ne "") && (defined $2 && $2 ne "")); | ||
| 35 | my @info = ($1, $2, $3, $4, $5, $6, $7); | ||
| 36 | # We convert any commas to periods, in the value fields | ||
| 37 | map { defined $info[$_] && $info[$_] =~ s/,/./go } (1, 3, 4, 5, 6); | ||
| 38 | |||
| 39 | # Check that $info[1] is an actual value | ||
| 40 | # We do this by returning undef if a warning appears | ||
| 41 | my $performance_value; | ||
| 42 | { | ||
| 43 | my $not_value; | ||
| 44 | local $SIG{__WARN__} = sub { $not_value++ }; | ||
| 45 | $performance_value = $info[1]+0; | ||
| 46 | return undef if $not_value; | ||
| 47 | } | ||
| 48 | my $p = $class->new( | ||
| 49 | label => $info[0], value => $performance_value, uom => $info[2], warning => $info[3], critical => $info[4], | ||
| 50 | min => $info[5], max => $info[6] | ||
| 51 | ); | ||
| 52 | return $p; | ||
| 53 | } | ||
| 54 | |||
| 55 | # Map undef to '' | ||
| 56 | sub _nvl { | ||
| 57 | my ($self, $value) = @_; | ||
| 58 | defined $value ? $value : '' | ||
| 59 | } | ||
| 60 | |||
| 61 | sub perfoutput { | ||
| 62 | my $self = shift; | ||
| 63 | # Add quotes if label contains a space character | ||
| 64 | my $label = $self->label; | ||
| 65 | if ($label =~ / /) { | ||
| 66 | $label = "'$label'"; | ||
| 67 | } | ||
| 68 | my $out = sprintf "%s=%s%s;%s;%s;%s;%s", | ||
| 69 | $label, | ||
| 70 | $self->value, | ||
| 71 | $self->_nvl($self->uom), | ||
| 72 | $self->_nvl($self->warning), | ||
| 73 | $self->_nvl($self->critical), | ||
| 74 | $self->_nvl($self->min), | ||
| 75 | $self->_nvl($self->max); | ||
| 76 | # Previous implementation omitted trailing ;; - do we need this? | ||
| 77 | $out =~ s/;;$//; | ||
| 78 | return $out; | ||
| 79 | } | ||
| 80 | |||
| 81 | sub parse_perfstring { | ||
| 82 | my ($class, $perfstring) = @_; | ||
| 83 | my @perfs = (); | ||
| 84 | my $obj; | ||
| 85 | while ($perfstring) { | ||
| 86 | $perfstring =~ s/^\s*//; | ||
| 87 | # If there is more than 1 equals sign, split it out and parse individually | ||
| 88 | if (@{[$perfstring =~ /=/g]} > 1) { | ||
| 89 | $perfstring =~ s/^(.*?=.*?)\s//; | ||
| 90 | if (defined $1) { | ||
| 91 | $obj = $class->_parse($1); | ||
| 92 | } else { | ||
| 93 | # This could occur if perfdata was soemthing=value= | ||
| 94 | # Since this is invalid, we reset the string and continue | ||
| 95 | $perfstring = ""; | ||
| 96 | $obj = $class->_parse($perfstring); | ||
| 97 | } | ||
| 98 | } else { | ||
| 99 | $obj = $class->_parse($perfstring); | ||
| 100 | $perfstring = ""; | ||
| 101 | } | ||
| 102 | push @perfs, $obj if $obj; | ||
| 103 | } | ||
| 104 | return @perfs; | ||
| 105 | } | ||
| 106 | |||
| 107 | sub rrdlabel { | ||
| 108 | my $self = shift; | ||
| 109 | my $name = $self->clean_label; | ||
| 110 | # Shorten | ||
| 111 | return substr( $name, 0, 19 ); | ||
| 112 | } | ||
| 113 | |||
| 114 | sub clean_label { | ||
| 115 | my $self = shift; | ||
| 116 | my $name = $self->label; | ||
| 117 | if ($name eq "/") { | ||
| 118 | $name = "root"; | ||
| 119 | } elsif ( $name =~ s/^\/// ) { | ||
| 120 | $name =~ s/\//_/g; | ||
| 121 | } | ||
| 122 | # Convert all other characters | ||
| 123 | $name =~ s/\W/_/g; | ||
| 124 | return $name; | ||
| 125 | } | ||
| 126 | |||
| 127 | # Backward compatibility: create a threshold object on the fly as requested | ||
| 128 | sub threshold | ||
| 129 | { | ||
| 130 | my $self = shift; | ||
| 131 | return Monitoring::Plugin::Threshold->set_thresholds( | ||
| 132 | warning => $self->warning, critical => $self->critical | ||
| 133 | ); | ||
| 134 | } | ||
| 135 | |||
| 136 | # Constructor - unpack thresholds, map args to hashref | ||
| 137 | sub new | ||
| 138 | { | ||
| 139 | my $class = shift; | ||
| 140 | my %arg = @_; | ||
| 141 | |||
| 142 | # Convert thresholds | ||
| 143 | if (my $threshold = delete $arg{threshold}) { | ||
| 144 | $arg{warning} ||= $threshold->warning . ""; | ||
| 145 | $arg{critical} ||= $threshold->critical . ""; | ||
| 146 | } | ||
| 147 | |||
| 148 | $class->SUPER::new(\%arg); | ||
| 149 | } | ||
| 150 | |||
| 151 | 1; | ||
| 152 | |||
| 153 | __END__ | ||
| 154 | |||
| 155 | =head1 NAME | ||
| 156 | |||
| 157 | Monitoring::Plugin::Performance - class for handling Monitoring::Plugin | ||
| 158 | performance data. | ||
| 159 | |||
| 160 | =head1 SYNOPSIS | ||
| 161 | |||
| 162 | use Monitoring::Plugin::Performance use_die => 1; | ||
| 163 | |||
| 164 | # Constructor (also accepts a 'threshold' obj instead of warning/critical) | ||
| 165 | $p = Monitoring::Plugin::Performance->new( | ||
| 166 | label => 'size', | ||
| 167 | value => $value, | ||
| 168 | uom => "kB", | ||
| 169 | warning => $warning, | ||
| 170 | critical => $critical, | ||
| 171 | min => $min, | ||
| 172 | max => $max, | ||
| 173 | ); | ||
| 174 | |||
| 175 | # Parser | ||
| 176 | @perf = Monitoring::Plugin::Performance->parse_perfstring( | ||
| 177 | "/=382MB;15264;15269;; /var=218MB;9443;9448" | ||
| 178 | ) | ||
| 179 | or warn("Failed to parse perfstring"); | ||
| 180 | |||
| 181 | # Accessors | ||
| 182 | for $p (@perf) { | ||
| 183 | printf "label: %s\n", $p->label; | ||
| 184 | printf "value: %s\n", $p->value; | ||
| 185 | printf "uom: %s\n", $p->uom; | ||
| 186 | printf "warning: %s\n", $p->warning; | ||
| 187 | printf "critical: %s\n", $p->critical; | ||
| 188 | printf "min: %s\n", $p->min; | ||
| 189 | printf "max: %s\n", $p->max; | ||
| 190 | # Special accessor returning a threshold obj containing warning/critical | ||
| 191 | $threshold = $p->threshold; | ||
| 192 | } | ||
| 193 | |||
| 194 | # Perfdata output format i.e. label=value[uom];[warn];[crit];[min];[max] | ||
| 195 | print $p->perfoutput; | ||
| 196 | |||
| 197 | |||
| 198 | =head1 DESCRIPTION | ||
| 199 | |||
| 200 | Monitoring::Plugin class for handling performance data. This is a public | ||
| 201 | interface because it could be used by performance graphing routines, | ||
| 202 | such as nagiostat (http://nagiostat.sourceforge.net), perfparse | ||
| 203 | (http://perfparse.sourceforge.net), nagiosgraph | ||
| 204 | (http://nagiosgraph.sourceforge.net) or NagiosGrapher | ||
| 205 | (http://www.nagiosexchange.org/NagiosGrapher.84.0.html). | ||
| 206 | |||
| 207 | Monitoring::Plugin::Performance offers both a parsing interface (via | ||
| 208 | parse_perfstring), for turning nagios performance output strings into | ||
| 209 | their components, and a composition interface (via new), for turning | ||
| 210 | components into perfdata strings. | ||
| 211 | |||
| 212 | =head1 USE'ING THE MODULE | ||
| 213 | |||
| 214 | If you are using this module for the purposes of parsing perf data, you | ||
| 215 | will probably want to set use_die => 1 at use time. This forces | ||
| 216 | &Monitoring::Plugin::Functions::plugin_exit to call die() - rather than exit() - | ||
| 217 | when an error occurs. This is then trappable by an eval. If you don't set use_die, | ||
| 218 | then an error in these modules will cause your script to exit | ||
| 219 | |||
| 220 | =head1 CLASS METHODS | ||
| 221 | |||
| 222 | =over 4 | ||
| 223 | |||
| 224 | =item Monitoring::Plugin::Performance->new(%attributes) | ||
| 225 | |||
| 226 | Instantiates a new Monitoring::Plugin::Performance object with the given | ||
| 227 | attributes. | ||
| 228 | |||
| 229 | =item Monitoring::Plugin::Performance->parse_perfstring($string) | ||
| 230 | |||
| 231 | Returns an array of Monitoring::Plugin::Performance objects based on the string | ||
| 232 | entered. If there is an error parsing the string - which may consists of several | ||
| 233 | sets of data - will return an array with all the successfully parsed sets. | ||
| 234 | |||
| 235 | If values are input with commas instead of periods, due to different locale settings, | ||
| 236 | then it will still be parsed, but the commas will be converted to periods. | ||
| 237 | |||
| 238 | =back | ||
| 239 | |||
| 240 | =head1 OBJECT METHODS (ACCESSORS) | ||
| 241 | |||
| 242 | =over 4 | ||
| 243 | |||
| 244 | =item label, value, uom, warning, critical, min, max | ||
| 245 | |||
| 246 | These all return scalars. min and max are not well supported yet. | ||
| 247 | |||
| 248 | =item threshold | ||
| 249 | |||
| 250 | Returns a Monitoring::Plugin::Threshold object holding the warning and critical | ||
| 251 | ranges for this performance data (if any). | ||
| 252 | |||
| 253 | =item rrdlabel | ||
| 254 | |||
| 255 | Returns a string based on 'label' that is suitable for use as dataset name of | ||
| 256 | an RRD i.e. munges label to be 1-19 characters long with only characters | ||
| 257 | [a-zA-Z0-9_]. | ||
| 258 | |||
| 259 | This calls $self->clean_label and then truncates to 19 characters. | ||
| 260 | |||
| 261 | There is no guarantee that multiple N:P:Performance objects will have unique | ||
| 262 | rrdlabels. | ||
| 263 | |||
| 264 | =item clean_label | ||
| 265 | |||
| 266 | Returns a "clean" label for use as a dataset name in RRD, ie, it converts | ||
| 267 | characters that are not [a-zA-Z0-9_] to _. | ||
| 268 | |||
| 269 | It also converts "/" to "root" and "/{name}" to "{name}". | ||
| 270 | |||
| 271 | =item perfoutput | ||
| 272 | |||
| 273 | Outputs the data in Monitoring::Plugin perfdata format i.e. | ||
| 274 | label=value[uom];[warn];[crit];[min];[max]. | ||
| 275 | |||
| 276 | =back | ||
| 277 | |||
| 278 | =head1 SEE ALSO | ||
| 279 | |||
| 280 | Monitoring::Plugin, Monitoring::Plugin::Threshold, https://www.monitoring-plugins.org/doc/guidelines.html | ||
| 281 | |||
| 282 | =head1 AUTHOR | ||
| 283 | |||
| 284 | This code is maintained by the Monitoring Plugin Development Team: see | ||
| 285 | https://monitoring-plugins.org | ||
| 286 | |||
| 287 | =head1 COPYRIGHT AND LICENSE | ||
| 288 | |||
| 289 | Copyright (C) 2006-2014 Monitoring Plugin Development Team | ||
| 290 | |||
| 291 | This library is free software; you can redistribute it and/or modify | ||
| 292 | it under the same terms as Perl itself. | ||
| 293 | |||
| 294 | =cut | ||
diff --git a/lib/Monitoring/Plugin/Range.pm b/lib/Monitoring/Plugin/Range.pm new file mode 100644 index 0000000..af19577 --- /dev/null +++ b/lib/Monitoring/Plugin/Range.pm | |||
| @@ -0,0 +1,169 @@ | |||
| 1 | package Monitoring::Plugin::Range; | ||
| 2 | |||
| 3 | use 5.006; | ||
| 4 | |||
| 5 | use strict; | ||
| 6 | use warnings; | ||
| 7 | |||
| 8 | use Carp; | ||
| 9 | use base qw(Class::Accessor::Fast); | ||
| 10 | __PACKAGE__->mk_accessors( | ||
| 11 | qw(start end start_infinity end_infinity alert_on) | ||
| 12 | ); | ||
| 13 | |||
| 14 | use Monitoring::Plugin::Functions qw(:DEFAULT $value_re); | ||
| 15 | our ($VERSION) = $Monitoring::Plugin::Functions::VERSION; | ||
| 16 | |||
| 17 | use overload | ||
| 18 | 'eq' => sub { shift->_stringify }, | ||
| 19 | '""' => sub { shift->_stringify }; | ||
| 20 | |||
| 21 | # alert_on constants (undef == range not set) | ||
| 22 | use constant OUTSIDE => 0; | ||
| 23 | use constant INSIDE => 1; | ||
| 24 | |||
| 25 | sub _stringify { | ||
| 26 | my $self = shift; | ||
| 27 | return "" unless $self->is_set; | ||
| 28 | return (($self->alert_on) ? "@" : "") . | ||
| 29 | (($self->start_infinity == 1) ? "~:" : (($self->start == 0)?"":$self->start.":")) . | ||
| 30 | (($self->end_infinity == 1) ? "" : $self->end); | ||
| 31 | } | ||
| 32 | |||
| 33 | sub is_set { | ||
| 34 | my $self = shift; | ||
| 35 | (! defined $self->alert_on) ? 0 : 1; | ||
| 36 | } | ||
| 37 | |||
| 38 | sub _set_range_start { | ||
| 39 | my ($self, $value) = @_; | ||
| 40 | $self->start($value+0); # Force scalar into number | ||
| 41 | $self->start_infinity(0); | ||
| 42 | } | ||
| 43 | |||
| 44 | sub _set_range_end { | ||
| 45 | my ($self, $value) = @_; | ||
| 46 | $self->end($value+0); # Force scalar into number | ||
| 47 | $self->end_infinity(0); | ||
| 48 | } | ||
| 49 | |||
| 50 | # Returns a N::P::Range object if the string is a conforms to a Monitoring Plugin range string, otherwise null | ||
| 51 | sub parse_range_string { | ||
| 52 | my ($class, $string) = @_; | ||
| 53 | my $valid = 0; | ||
| 54 | my $range = $class->new( start => 0, start_infinity => 0, end => 0, end_infinity => 1, alert_on => OUTSIDE); | ||
| 55 | |||
| 56 | $string =~ s/\s//g; # strip out any whitespace | ||
| 57 | # check for valid range definition | ||
| 58 | unless ( $string =~ /[\d~]/ && $string =~ m/^\@?($value_re|~)?(:($value_re)?)?$/ ) { | ||
| 59 | carp "invalid range definition '$string'"; | ||
| 60 | return undef; | ||
| 61 | } | ||
| 62 | |||
| 63 | if ($string =~ s/^\@//) { | ||
| 64 | $range->alert_on(INSIDE); | ||
| 65 | } | ||
| 66 | |||
| 67 | if ($string =~ s/^~//) { # '~:x' | ||
| 68 | $range->start_infinity(1); | ||
| 69 | } | ||
| 70 | if ( $string =~ m/^($value_re)?:/ ) { # '10:' | ||
| 71 | my $start = $1; | ||
| 72 | $range->_set_range_start($start) if defined $start; | ||
| 73 | $range->end_infinity(1); # overridden below if there's an end specified | ||
| 74 | $string =~ s/^($value_re)?://; | ||
| 75 | $valid++; | ||
| 76 | } | ||
| 77 | if ($string =~ /^($value_re)$/) { # 'x:10' or '10' | ||
| 78 | $range->_set_range_end($string); | ||
| 79 | $valid++; | ||
| 80 | } | ||
| 81 | |||
| 82 | if ($valid && ($range->start_infinity == 1 || $range->end_infinity == 1 || $range->start <= $range->end)) { | ||
| 83 | return $range; | ||
| 84 | } | ||
| 85 | return undef; | ||
| 86 | } | ||
| 87 | |||
| 88 | # Returns 1 if an alert should be raised, otherwise 0 | ||
| 89 | sub check_range { | ||
| 90 | my ($self, $value) = @_; | ||
| 91 | my $false = 0; | ||
| 92 | my $true = 1; | ||
| 93 | if ($self->alert_on == INSIDE) { | ||
| 94 | $false = 1; | ||
| 95 | $true = 0; | ||
| 96 | } | ||
| 97 | if ($self->end_infinity == 0 && $self->start_infinity == 0) { | ||
| 98 | if ($self->start <= $value && $value <= $self->end) { | ||
| 99 | return $false; | ||
| 100 | } else { | ||
| 101 | return $true; | ||
| 102 | } | ||
| 103 | } elsif ($self->start_infinity == 0 && $self->end_infinity == 1) { | ||
| 104 | if ( $value >= $self->start ) { | ||
| 105 | return $false; | ||
| 106 | } else { | ||
| 107 | return $true; | ||
| 108 | } | ||
| 109 | } elsif ($self->start_infinity == 1 && $self->end_infinity == 0) { | ||
| 110 | if ($value <= $self->end) { | ||
| 111 | return $false; | ||
| 112 | } else { | ||
| 113 | return $true; | ||
| 114 | } | ||
| 115 | } else { | ||
| 116 | return $false; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | # Constructor - map args to hashref for SUPER | ||
| 121 | sub new | ||
| 122 | { | ||
| 123 | shift->SUPER::new({ @_ }); | ||
| 124 | } | ||
| 125 | |||
| 126 | 1; | ||
| 127 | |||
| 128 | __END__ | ||
| 129 | |||
| 130 | =head1 NAME | ||
| 131 | |||
| 132 | Monitoring::Plugin::Range - class for handling Monitoring::Plugin range data. | ||
| 133 | |||
| 134 | =head1 SYNOPSIS | ||
| 135 | |||
| 136 | # NB: This is an internal Monitoring::Plugin class. | ||
| 137 | # See Monitoring::Plugin itself for public interfaces. | ||
| 138 | |||
| 139 | # Instantiate an empty range object | ||
| 140 | $r = Monitoring::Plugin::Range->new; | ||
| 141 | |||
| 142 | # Instantiate by parsing a standard nagios range string | ||
| 143 | $r = Monitoring::Plugin::Range->parse_range_string( $range_str ); | ||
| 144 | |||
| 145 | # Returns true if the range is defined/non-empty | ||
| 146 | $r->is_set; | ||
| 147 | |||
| 148 | # Returns true if $value matches range, false otherwise | ||
| 149 | $r->check_range($value); | ||
| 150 | |||
| 151 | |||
| 152 | =head1 DESCRIPTION | ||
| 153 | |||
| 154 | Internal Monitoring::Plugin class for handling common range data. See | ||
| 155 | Monitoring::Plugin for public interfaces. | ||
| 156 | |||
| 157 | =head1 AUTHOR | ||
| 158 | |||
| 159 | This code is maintained by the Monitoring Plugin Development Team: see | ||
| 160 | https://monitoring-plugins.org | ||
| 161 | |||
| 162 | =head1 COPYRIGHT AND LICENSE | ||
| 163 | |||
| 164 | Copyright (C) 2006-2014 Monitoring Plugin Development Team | ||
| 165 | |||
| 166 | This library is free software; you can redistribute it and/or modify | ||
| 167 | it under the same terms as Perl itself. | ||
| 168 | |||
| 169 | =cut | ||
diff --git a/lib/Monitoring/Plugin/Threshold.pm b/lib/Monitoring/Plugin/Threshold.pm new file mode 100644 index 0000000..e71e21b --- /dev/null +++ b/lib/Monitoring/Plugin/Threshold.pm | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | package Monitoring::Plugin::Threshold; | ||
| 2 | |||
| 3 | use 5.006; | ||
| 4 | |||
| 5 | use strict; | ||
| 6 | use warnings; | ||
| 7 | |||
| 8 | use base qw(Class::Accessor::Fast); | ||
| 9 | __PACKAGE__->mk_accessors(qw(warning critical)); | ||
| 10 | |||
| 11 | use Monitoring::Plugin::Range; | ||
| 12 | use Monitoring::Plugin::Functions qw(:codes plugin_die); | ||
| 13 | our ($VERSION) = $Monitoring::Plugin::Functions::VERSION; | ||
| 14 | |||
| 15 | sub get_status | ||
| 16 | { | ||
| 17 | my ($self, $value) = @_; | ||
| 18 | |||
| 19 | $value = [ $value ] if (ref $value eq ""); | ||
| 20 | foreach my $v (@$value) { | ||
| 21 | if ($self->critical->is_set) { | ||
| 22 | return CRITICAL if $self->critical->check_range($v); | ||
| 23 | } | ||
| 24 | } | ||
| 25 | foreach my $v (@$value) { | ||
| 26 | if ($self->warning->is_set) { | ||
| 27 | return WARNING if $self->warning->check_range($v); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | return OK; | ||
| 31 | } | ||
| 32 | |||
| 33 | sub _inflate | ||
| 34 | { | ||
| 35 | my ($self, $value, $key) = @_; | ||
| 36 | |||
| 37 | # Return an undefined range if $value is undef | ||
| 38 | return Monitoring::Plugin::Range->new if ! defined $value; | ||
| 39 | |||
| 40 | # For refs, check isa N::P::Range | ||
| 41 | if (ref $value) { | ||
| 42 | plugin_die("Invalid $key object: type " . ref $value) | ||
| 43 | unless $value->isa("Monitoring::Plugin::Range"); | ||
| 44 | return $value; | ||
| 45 | } | ||
| 46 | |||
| 47 | # Another quick exit if $value is an empty string | ||
| 48 | return Monitoring::Plugin::Range->new if $value eq ""; | ||
| 49 | |||
| 50 | # Otherwise parse $value | ||
| 51 | my $range = Monitoring::Plugin::Range->parse_range_string($value); | ||
| 52 | plugin_die("Cannot parse $key range: '$value'") unless(defined($range)); | ||
| 53 | return $range; | ||
| 54 | } | ||
| 55 | |||
| 56 | sub set_thresholds | ||
| 57 | { | ||
| 58 | my ($self, %arg) = @_; | ||
| 59 | |||
| 60 | # Equals new() as a class method | ||
| 61 | return $self->new(%arg) unless ref $self; | ||
| 62 | |||
| 63 | # On an object, just acts as special mutator | ||
| 64 | $self->set($_, $arg{$_}) foreach qw(warning critical); | ||
| 65 | } | ||
| 66 | |||
| 67 | sub set | ||
| 68 | { | ||
| 69 | my $self = shift; | ||
| 70 | my ($key, $value) = @_; | ||
| 71 | $self->SUPER::set($key, $self->_inflate($value, $key)); | ||
| 72 | } | ||
| 73 | |||
| 74 | # Constructor - inflate scalars to N::P::Range objects | ||
| 75 | sub new | ||
| 76 | { | ||
| 77 | my ($self, %arg) = @_; | ||
| 78 | $self->SUPER::new({ | ||
| 79 | map { $_ => $self->_inflate($arg{$_}, $_) } qw(warning critical) | ||
| 80 | }); | ||
| 81 | } | ||
| 82 | |||
| 83 | 1; | ||
| 84 | |||
| 85 | __END__ | ||
| 86 | |||
| 87 | =head1 NAME | ||
| 88 | |||
| 89 | Monitoring::Plugin::Threshold - class for handling Monitoring::Plugin thresholds. | ||
| 90 | |||
| 91 | =head1 SYNOPSIS | ||
| 92 | |||
| 93 | # NB: This is an internal Monitoring::Plugin class. | ||
| 94 | # See Monitoring::Plugin itself for public interfaces. | ||
| 95 | |||
| 96 | # Constructor | ||
| 97 | $t = Monitoring::Plugin::Threshold->set_thresholds( | ||
| 98 | warning => $warning_range_string, | ||
| 99 | critical => $critical_range_string, | ||
| 100 | ); | ||
| 101 | |||
| 102 | # Value checking - returns CRITICAL if in the critical range, | ||
| 103 | # WARNING if in the warning range, and OK otherwise | ||
| 104 | $status = $t->get_status($value); | ||
| 105 | |||
| 106 | # Accessors - return the associated N::P::Range object | ||
| 107 | $warning_range = $t->warning; | ||
| 108 | $critical_range = $t->critical; | ||
| 109 | |||
| 110 | |||
| 111 | =head1 DESCRIPTION | ||
| 112 | |||
| 113 | Internal Monitoring::Plugin class for handling threshold data. See | ||
| 114 | Monitoring::Plugin for public interfaces. | ||
| 115 | |||
| 116 | A threshold object contains (typically) a pair of ranges, associated | ||
| 117 | with a particular severity e.g. | ||
| 118 | |||
| 119 | warning => range1 | ||
| 120 | critical => range2 | ||
| 121 | |||
| 122 | =head1 AUTHOR | ||
| 123 | |||
| 124 | This code is maintained by the Monitoring Plugin Development Team: see | ||
| 125 | https://monitoring-plugins.org | ||
| 126 | |||
| 127 | =head1 COPYRIGHT AND LICENSE | ||
| 128 | |||
| 129 | Copyright (C) 2006-2014 Monitoring Plugin Development Team | ||
| 130 | |||
| 131 | This library is free software; you can redistribute it and/or modify | ||
| 132 | it under the same terms as Perl itself. | ||
| 133 | |||
| 134 | =cut | ||
