From dc31f1cd3841d486e920e59ce42e888ca94e4289 Mon Sep 17 00:00:00 2001 From: Gavin Carr Date: Wed, 21 Mar 2007 00:52:56 +0000 Subject: Finished initial --extra-opts support; added Getopt spec-to-help and multiline help support. git-svn-id: https://nagiosplug.svn.sourceforge.net/svnroot/nagiosplug/Nagios-Plugin/trunk@1643 f882894a-f735-0410-b71e-b25c423dba1c --- lib/Nagios/Plugin/Getopt.pm | 263 ++++++++++++++++++++++++++++++++------------ 1 file changed, 194 insertions(+), 69 deletions(-) (limited to 'lib/Nagios/Plugin/Getopt.pm') diff --git a/lib/Nagios/Plugin/Getopt.pm b/lib/Nagios/Plugin/Getopt.pm index 806a6a0..743bf7e 100644 --- a/lib/Nagios/Plugin/Getopt.pm +++ b/lib/Nagios/Plugin/Getopt.pm @@ -10,15 +10,13 @@ use File::Basename; use Getopt::Long qw(:config no_ignore_case bundling); use Carp; use Params::Validate qw(:all); -use Config::Tiny; use base qw(Class::Accessor); use Nagios::Plugin::Functions; -use vars qw($VERSION $DEFAULT_CONFIG_FILE); +use Nagios::Plugin::Config; +use vars qw($VERSION); $VERSION = $Nagios::Plugin::Functions::VERSION; -$DEFAULT_CONFIG_FILE = '/etc/nagios/plugins.cfg'; - # Standard defaults my %DEFAULT = ( timeout => 15, @@ -39,8 +37,8 @@ my @ARGS = ({ spec => 'version|V', help => "-V, --version\n Print version information", }, { - spec => 'default-opts:s@', - help => "--default-opts=[
[@]]\n Section and/or config_file from which to load default options (may repeat)", + spec => 'extra-opts:s@', + help => "--extra-opts=[
[@]]\n Section and/or config_file from which to load extra options (may repeat)", }, { spec => 'timeout|t=i', help => "-t, --timeout=INTEGER\n Seconds before plugin times out (default: %s)", @@ -77,6 +75,37 @@ sub _attr $self->{_attr}->{$item} . "\n" . $extra; } +# Turn argument spec into help-style output +sub _spec_to_help +{ + my ($self, $spec, $label) = @_; + + my ($opts, $type) = split /=/, $spec, 2; + my (@short, @long); + for (split /\|/, $opts) { + if (length $_ == 1) { + push @short, "-$_"; + } else { + push @long, "--$_"; + } + } + + my $help = join(', ', @short, @long); + if ($type) { + if ($label) { + $help .= '=' . $label; + } + else { + $help .= $type eq 'i' ? '=INTEGER' : '=STRING'; + } + } + elsif ($label) { + carp "Label specified, but there's no type in spec '$spec'"; + } + $help .= "\n "; + return $help; +} + # Options output for plugin -h sub _options { @@ -94,10 +123,29 @@ sub _options my @options = (); for my $arg (@args, @defer) { - if ($arg->{help} =~ m/%s/) { - push @options, sprintf($arg->{help}, $arg->{default} || ''); + my $help_array = ref $arg->{help} && ref $arg->{help} eq 'ARRAY' ? $arg->{help} : [ $arg->{help} ]; + my $label_array = $arg->{label} && ref $arg->{label} && ref $arg->{label} eq 'ARRAY' ? $arg->{label} : [ $arg->{label} ]; + my $help_string = ''; + for (my $i = 0; $i <= $#$help_array; $i++) { + my $help = $help_array->[$i]; + # Add spec arguments to help if not already there + if ($help =~ m/^\s*-/) { + $help_string .= $help; + } + else { + $help_string .= $self->_spec_to_help($arg->{spec}, $label_array->[$i]) . $help; + $help_string .= "\n " if $i < $#$help_array; + } + } + + # Add help_string to @options + if ($help_string =~ m/%s/) { + my $default = defined $arg->{default} ? $arg->{default} : ''; + # We only handle '%s' formats here, so escape everything else + $help_string =~ s/%(?!s)/%%/g; + push @options, sprintf($help_string, $default, $default, $default, $default); } else { - push @options, $arg->{help}; + push @options, $help_string; } } @@ -195,15 +243,12 @@ sub _load_config_section my $self = shift; my ($section, $file, $flags) = @_; $section ||= $self->{_attr}->{plugin}; - $file ||= $DEFAULT_CONFIG_FILE; - - $self->_die("Cannot find config file '$file'") if $flags->{fatal} && ! -f $file; - my $Config = Config::Tiny->read($file); - $self->_die("Cannot read config file '$file'") unless defined $Config; + my $Config = Nagios::Plugin::Config->read($file); + # TODO: is this check sane? Does --extra-opts=foo require a [foo] section? $self->_die("Invalid section '$section' in config file '$file'") - if $flags->{fatal} && ! exists $Config->{$section}; + unless exists $Config->{$section}; return $Config->{$section}; } @@ -248,7 +293,7 @@ sub _cmdline # Skip defaults and internals next if exists $DEFAULT{$key} && $hash->{$key} eq $DEFAULT{$key}; - next if grep { $key eq $_ } qw(help usage version default-opts); + next if grep { $key eq $_ } qw(help usage version extra-opts); next unless defined $hash->{$key}; # Render arg @@ -275,44 +320,37 @@ sub _cmdline return wantarray ? @args : join(' ', @args); } -# Process and load default-opts sections -sub _process_default_opts +# Process and load extra-opts sections +sub _process_extra_opts { my $self = shift; my ($args) = @_; - my $defopts_list = $args->{'default-opts'}; - my $defopts_explicit = 1; - - # If no default_opts defined, force one implicitly - if (! $defopts_list) { - $defopts_list = [ '' ]; - $defopts_explicit = 0; - } + my $extopts_list = $args->{'extra-opts'}; my @sargs = (); - for my $defopts (@$defopts_list) { - $defopts ||= $self->{_attr}->{plugin}; - my $section = $defopts; + for my $extopts (@$extopts_list) { + $extopts ||= $self->{_attr}->{plugin}; + my $section = $extopts; my $file = ''; # Parse section@file - if ($defopts =~ m/^(\w*)@(.*?)\s*$/) { + if ($extopts =~ m/^(\w*)@(.*?)\s*$/) { $section = $1; $file = $2; } # Load section args - my $shash = $self->_load_config_section($section, $file, { fatal => $defopts_explicit }); + my $shash = $self->_load_config_section($section, $file); # Turn $shash into a series of commandline-like arguments push @sargs, $self->_cmdline($shash); } - # Reset ARGV to default-opts + original + # Reset ARGV to extra-opts + original @ARGV = ( @sargs, @{$self->{_attr}->{argv}} ); - printf "[default-opts] %s %s\n", $self->{_attr}->{plugin}, join(' ', @ARGV) + printf "[extra-opts] %s %s\n", $self->{_attr}->{plugin}, join(' ', @ARGV) if $args->{verbose} && $args->{verbose} >= 3; } @@ -332,17 +370,19 @@ sub arg help => 1, default => 0, required => 0, + label => 0, }); } # Positional args else { - my @args = validate_pos(@_, 1, 1, 0, 0); + my @args = validate_pos(@_, 1, 1, 0, 0, 0); %args = ( spec => $args[0], help => $args[1], default => $args[2], required => $args[3], + label => $args[4], ); } @@ -358,7 +398,7 @@ sub getopts # Collate spec arguments for Getopt::Long my @opt_array = $self->_process_specs_getopt_long; - # Capture original @ARGV (for default-opts games) + # Capture original @ARGV (for extra-opts games) $self->{_attr}->{argv} = [ @ARGV ]; # Call GetOptions using @opt_array @@ -367,10 +407,10 @@ sub getopts # Invalid options - give usage message and exit $self->_die($self->_usage) unless $ok; - # Process default-opts - $self->_process_default_opts($args1); + # Process extra-opts + $self->_process_extra_opts($args1); - # Call GetOptions again, this time including default-opts + # Call GetOptions again, this time including extra-opts $ok = GetOptions($self, @opt_array); # Invalid options - give usage message and exit $self->_die($self->_usage) unless $ok; @@ -410,7 +450,7 @@ sub _init plugin => { default => $plugin }, blurb => 0, extra => 0, - 'default-opts' => 0, + 'extra-opts' => 0, license => { default => $DEFAULT{license} }, timeout => { default => $DEFAULT{timeout} }, }); @@ -452,17 +492,16 @@ processing for Nagios plugins # Instantiate object (usage is mandatory) $ng = Nagios::Plugin::Getopt->new( - usage => "Usage: %s -H -w - -c ", - version => '0.01', + usage => "Usage: %s -H -w -c ", + version => '0.1', url => 'http://www.openfusion.com.au/labs/nagios/', blurb => 'This plugin tests various stuff.', ); # Add argument - named parameters (spec and help are mandatory) $ng->arg( - spec => 'critical|c=s', - help => qq(-c, --critical=INTEGER\n Exit with CRITICAL status if fewer than INTEGER foobars are free), + spec => 'critical|c=i', + help => q(Exit with CRITICAL status if fewer than INTEGER foobars are free), required => 1, default => 10, ); @@ -470,8 +509,8 @@ processing for Nagios plugins # Add argument - positional parameters - arg spec, help text, # default value, required? (first two mandatory) $ng->arg( - 'warning|w=s', - qq(-w, --warning=INTEGER\n Exit with WARNING status if fewer than INTEGER foobars are free), + 'warning|w=i', + q(Exit with WARNING status if fewer than INTEGER foobars are free), 5, 1); @@ -545,9 +584,10 @@ License text, included in the longer --help output (see below for an example). By default, this is set to the standard nagios plugins GPL license text: - This nagios plugin is free software, and comes with ABSOLUTELY NO WARRANTY. - It may be used, redistributed and/or modified under the terms of the GNU - General Public Licence (see http://www.fsf.org/licensing/licenses/gpl.txt). + This nagios plugin is free software, and comes with ABSOLUTELY + NO WARRANTY. It may be used, redistributed and/or modified under + the terms of the GNU General Public Licence (see + http://www.fsf.org/licensing/licenses/gpl.txt). Provide your own to replace this text in the help output. @@ -603,8 +643,8 @@ example: -H, --hostname=ADDRESS Host name or IP address -p, --ports=STRING - Port numbers to check. Format: comma-separated, colons or hyphens for ranges, - no spaces e.g. 8700:8705,8710-8715,8760 + Port numbers to check. Format: comma-separated, colons for ranges, + no spaces e.g. 8700:8705,8710:8715,8760 -t, --timeout=INTEGER Seconds before plugin times out (default: 15) -v, --verbose @@ -615,21 +655,25 @@ example: You can define arguments for your plugin using the arg() method, which supports both named and positional arguments. In both cases -the 'spec' and 'help' arguments are required, while the 'default' -and 'required' arguments are optional: +the C and C arguments are required, while the C