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