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 | ||