diff options
author | Sven Nierlein <sven@nierlein.de> | 2014-01-20 00:54:34 +0100 |
---|---|---|
committer | Sven Nierlein <sven@nierlein.de> | 2014-01-20 00:54:34 +0100 |
commit | b418181dfe80dd75169b6e8a619ac1932155dea2 (patch) | |
tree | cad9c0ae0eae8e800cfff60555ead06ad33c6856 /lib/Monitoring/Plugin/Getopt.pm | |
parent | 1cd8d1c52cbd47121f344c4074aec84653f412ce (diff) | |
download | monitoring-plugin-perl-b418181dfe80dd75169b6e8a619ac1932155dea2.tar.gz |
renamed module into Monitoring::Plugin
since the complete monitoring team has been renamed, we
also rename this module.
Signed-off-by: Sven Nierlein <sven@nierlein.de>
Diffstat (limited to 'lib/Monitoring/Plugin/Getopt.pm')
-rw-r--r-- | lib/Monitoring/Plugin/Getopt.pm | 869 |
1 files changed, 869 insertions, 0 deletions
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 | ||