├── .gitignore ├── .travis.yml ├── Changes ├── MANIFEST ├── META.yml ├── Makefile.PL ├── README ├── README.md ├── lib └── Nagios │ └── Monitoring │ ├── Plugin.pm │ └── Plugin │ ├── Config.pm │ ├── ExitResult.pm │ ├── Functions.pm │ ├── Getopt.pm │ ├── Performance.pm │ ├── Range.pm │ └── Threshold.pm ├── notes └── t ├── Nagios-Monitoring-Plugin-01.t ├── Nagios-Monitoring-Plugin-02.t ├── Nagios-Monitoring-Plugin-03.t ├── Nagios-Monitoring-Plugin-04.t ├── Nagios-Monitoring-Plugin-05.t ├── Nagios-Monitoring-Plugin-Functions-01.t ├── Nagios-Monitoring-Plugin-Functions-02.t ├── Nagios-Monitoring-Plugin-Functions-03.t ├── Nagios-Monitoring-Plugin-Functions-04.t ├── Nagios-Monitoring-Plugin-Getopt-01.t ├── Nagios-Monitoring-Plugin-Getopt-02.t ├── Nagios-Monitoring-Plugin-Getopt-03.t ├── Nagios-Monitoring-Plugin-Getopt-04.t ├── Nagios-Monitoring-Plugin-Performance-02.t ├── Nagios-Monitoring-Plugin-Performance.t ├── Nagios-Monitoring-Plugin-Range.t ├── Nagios-Monitoring-Plugin-Threshold.t ├── check_stuff.pl ├── check_stuff.t └── npg03 ├── README ├── expected ├── 00_basic ├── 00_noextra ├── 01_override1 ├── 02_override2 ├── 05_disk1 ├── 05_disk2 ├── 05_disk3 ├── 05_disk4 ├── 05_disk5 ├── 05_disk6 ├── 05_disk7 ├── 09_funnystuff ├── 12_nosection_implicit └── 15_badsection_catch ├── input ├── 00_basic ├── 00_noextra ├── 01_override1 ├── 02_override2 ├── 05_disk1 ├── 05_disk2 ├── 05_disk3 ├── 05_disk4 ├── 05_disk5 ├── 05_disk6 ├── 05_disk7 ├── 09_funnystuff ├── 12_nosection_implicit ├── 13_nosection_explicit_dies ├── 14_badsection_dies └── 15_badsection_catch └── plugins.ini /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # / 3 | /Makefile 4 | /blib 5 | /pm_to_blib 6 | /.bzr 7 | /.bzrignore 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: perl 2 | perl: 3 | - "5.10" 4 | - "5.16" 5 | - "5.22" 6 | -------------------------------------------------------------------------------- /Changes: -------------------------------------------------------------------------------- 1 | Revision history for Perl module Nagios::Monitoring::Plugin. 2 | 3 | 0.52 ?? 4 | - Updated Makefile.PL with contributions from @chorny on GitHub, PR #1 5 | - Updated README to reflect proper installation instructions 6 | 7 | 0.51 17th September 2015 8 | - Remove usage of defined %hash in test for perl 5.21.x and later 9 | 10 | 0.50 16th September 2015 11 | - Rename package to Nagios::Monitoring::Plugin 12 | 13 | 0.36 22nd December 2011 14 | - Updated check_threshold to allow multiple check values to be checked at once 15 | 16 | 0.35 3rd December 2010 17 | - Fixed test failures with Test::More 0.96 (Slaven Rezic and Peter John Edwards - RT57709) 18 | 19 | 0.34 15th April 2010 20 | - Amended standard --extra-opts help 21 | - pod fix (Frank Wiegand - RT51872) 22 | - Added %STATUS_TEXT to valid possible exports (Andrew Ford - RT46048) 23 | 24 | 0.33 5th June 2009 25 | - Fixed infinite loop when invalid performance data with multiple = were present 26 | 27 | 0.32 3rd March 2009 28 | - Handle performance data with quotes in the label (thanks to Kang) 29 | - Die if default config file is not available and --extra-opts is set 30 | 31 | 0.31 5th January 2009 32 | - Check for valid numerical value before returning perfdata object 33 | 34 | 0.30 13th December 2008 35 | - Fixed performance parsing when numeric fields had commas instead of periods due to locale settings 36 | - If a performance set is not parseable, instead of returning an empty array, will return all the successfully 37 | parsed sets 38 | - Fixed test plan for Nagios-Monitoring-Plugin-Performance.t 39 | 40 | 0.29 2nd December 2008 41 | - clean_label, for cleaning up a label for RRD, but without truncation 42 | 43 | 0.28 21st November 2008 44 | - Fixed test problems when run against Test::More 0.86 45 | - Added max_state_* wrappers 46 | 47 | 0.27 14th May 2008 48 | - Fixed parsing of performance data with scientific notation 49 | 50 | 0.26 28th March 2008 51 | - Fixed test failure in t/Nagios-Monitoring-Plugin-Getopt-03.t (Thomas Guyot-Sionnest) 52 | 53 | 0.25 17th March 2008 54 | - Fixed parsing of performance data with negative values and full range definitions 55 | 56 | 0.24 1st February 2008 57 | - Fixed a test failure which highlighted a precision rounding within hashes 58 | 59 | 0.23 18th December 2007 60 | - Use $^X for perl in check_stuff.t test, due to lots of failing in CPAN Testers 61 | 62 | 0.22 13th December 2007 63 | - Fixed handling of repeated ini arguments 64 | 65 | 0.21 24th September 2007 66 | - Help, usage and version output now goes to stdout, not stderr 67 | 68 | 0.20 5th September 2007 69 | - Version bump because of CPAN permission problems 70 | 71 | 0.19 4th September 2007 72 | - Fix test failures due to bad MANIFEST file 73 | - Fixed performance parsing where uom = % 74 | - Fixed version numbering 75 | 76 | 0.18 31st August 2007 77 | - Fix error when parsing performance data where warn or crit are 0 78 | - Optional _use_die flag to force nagios_die to call die instead of exit, so 79 | exceptions can be caught with an eval 80 | - Convenience function to set use_die so you can run 'use Nagios::Monitoring::Plugin::Performance use_die => 1' 81 | 82 | 0.17 23rd March 2007 83 | - bump version number again due to cpan indexing stupidity (Gavin) 84 | 85 | 0.16 23rd March 2007 86 | - added support for multi-entry help output (e.g. two separate help entries for --warning) (Gavin) 87 | - added automatic spec-to-help-text support to N::P::Getopt (Gavin) 88 | - added initial --extra-opts support to N::P::Getopt (Gavin) 89 | - removed default use of Threshold from N::P::Performance (Gavin) 90 | - removed remaining Class::Struct usages from Performance, Threshold, and Range (Gavin) 91 | - fixed warnings when no uom specified for add_perfdata (Ton) 92 | - added max_state function in N::P::Functions (Ton) 93 | 94 | 0.15 19th December 2006 95 | - exposed Getopt and Threshold functionality from top level Nagios::Monitoring::Plugin 96 | - exchanged Class::Struct for Class::Accessor 97 | 98 | 0.14 18th October 2006 99 | - Fixed version number due to CPAN upload 100 | 101 | 0.13 18th October 2006 102 | - Lots of extra tests and fixes from Nathan Vonnahme 103 | - Nagios::Monitoring::Plugin::Getopt, Functions and ExitResult added by Gavin Carr 104 | 105 | 0.12 15th June 2006 106 | - rrdlabel method available to get a performance label, 107 | converted to something rrd can use 108 | - fixes to parse_perfstring routine if values are 0 109 | - is_set method for range object to see if warning/critical range is set 110 | 111 | 0.11 14th June 2006 112 | - Interface changed for parse_perfstring, returning empty 113 | array if not parseable 114 | - Fixed problem when parsing nagiosgraph data (linefeed at end 115 | of perfdata) 116 | 117 | 0.10 8th June 2006 118 | First release to CPAN 119 | 120 | 0.01 Fri Jun 2 14:10:58 2006 121 | - original version; created by h2xs 1.23 with options 122 | -X -n Nagios::Monitoring::Plugin 123 | 124 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | Changes 2 | lib/Nagios/Monitoring/Plugin.pm 3 | lib/Nagios/Monitoring/Plugin/Config.pm 4 | lib/Nagios/Monitoring/Plugin/ExitResult.pm 5 | lib/Nagios/Monitoring/Plugin/Functions.pm 6 | lib/Nagios/Monitoring/Plugin/Getopt.pm 7 | lib/Nagios/Monitoring/Plugin/Performance.pm 8 | lib/Nagios/Monitoring/Plugin/Range.pm 9 | lib/Nagios/Monitoring/Plugin/Threshold.pm 10 | Makefile.PL 11 | MANIFEST This list of files 12 | META.yml 13 | README 14 | t/check_stuff.pl 15 | t/check_stuff.t 16 | t/Nagios-Monitoring-Plugin-01.t 17 | t/Nagios-Monitoring-Plugin-02.t 18 | t/Nagios-Monitoring-Plugin-03.t 19 | t/Nagios-Monitoring-Plugin-04.t 20 | t/Nagios-Monitoring-Plugin-Functions-01.t 21 | t/Nagios-Monitoring-Plugin-Functions-02.t 22 | t/Nagios-Monitoring-Plugin-Functions-03.t 23 | t/Nagios-Monitoring-Plugin-Getopt-01.t 24 | t/Nagios-Monitoring-Plugin-Getopt-02.t 25 | t/Nagios-Monitoring-Plugin-Getopt-03.t 26 | t/Nagios-Monitoring-Plugin-Getopt-04.t 27 | t/Nagios-Monitoring-Plugin-Performance-02.t 28 | t/Nagios-Monitoring-Plugin-Performance.t 29 | t/Nagios-Monitoring-Plugin-Range.t 30 | t/Nagios-Monitoring-Plugin-Threshold.t 31 | t/npg03/expected/00_basic 32 | t/npg03/expected/00_noextra 33 | t/npg03/expected/01_override1 34 | t/npg03/expected/02_override2 35 | t/npg03/expected/05_disk1 36 | t/npg03/expected/05_disk2 37 | t/npg03/expected/05_disk3 38 | t/npg03/expected/05_disk4 39 | t/npg03/expected/05_disk5 40 | t/npg03/expected/05_disk6 41 | t/npg03/expected/09_funnystuff 42 | t/npg03/expected/12_nosection_implicit 43 | t/npg03/input/00_basic 44 | t/npg03/input/00_noextra 45 | t/npg03/input/01_override1 46 | t/npg03/input/02_override2 47 | t/npg03/input/05_disk1 48 | t/npg03/input/05_disk2 49 | t/npg03/input/05_disk3 50 | t/npg03/input/05_disk4 51 | t/npg03/input/05_disk5 52 | t/npg03/input/05_disk6 53 | t/npg03/input/09_funnystuff 54 | t/npg03/input/12_nosection_implicit 55 | t/npg03/input/13_nosection_explicit_dies 56 | t/npg03/input/14_badsection_dies 57 | t/npg03/plugins.ini 58 | t/npg03/README 59 | -------------------------------------------------------------------------------- /META.yml: -------------------------------------------------------------------------------- 1 | # http://module-build.sourceforge.net/META-spec.html 2 | #XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# 3 | name: Nagios-Monitoring-Plugin 4 | version: 0.51 5 | version_from: lib/Nagios/Monitoring/Plugin/Functions.pm 6 | installdirs: site 7 | requires: 8 | Carp: 0 9 | Class::Accessor: 0 10 | Config::Tiny: 0 11 | File::Basename: 0 12 | File::Spec: 0 13 | IO::File: 0 14 | Math::Calc::Units: 0 15 | Params::Validate: 0 16 | Test::More: 0.62 17 | 18 | distribution_type: module 19 | generated_by: ExtUtils::MakeMaker version 6.17 20 | -------------------------------------------------------------------------------- /Makefile.PL: -------------------------------------------------------------------------------- 1 | use 5.006; 2 | use ExtUtils::MakeMaker; 3 | # See lib/ExtUtils/MakeMaker.pm for details of how to influence 4 | # the contents of the Makefile that is written. 5 | WriteMakefile( 6 | NAME => 'Nagios::Monitoring::Plugin', 7 | AUTHOR => 'Nagios Plugin Development Team ', 8 | VERSION_FROM => 'lib/Nagios/Monitoring/Plugin/Functions.pm', # finds $VERSION 9 | ABSTRACT_FROM => 'lib/Nagios/Monitoring/Plugin.pm', # retrieve abstract from module 10 | LICENSE => 'perl', 11 | MIN_PERL_VERSION => '5.006', 12 | META_MERGE => { 13 | resources => { 14 | repository => 'https://github.com/nagios-plugins/nagios-plugin-perl', 15 | }, 16 | }, 17 | PREREQ_PM => { 18 | Params::Validate => 0, 19 | Class::Accessor => 0, 20 | Test::More => 0.62, 21 | Carp => 0, 22 | Config::Tiny => 0, 23 | File::Spec => 0, 24 | File::Basename => 0, 25 | IO::File => 0, 26 | Math::Calc::Units => 0, # used in N::P::Performance 27 | }, # e.g., Module::Name => 1.1 28 | ); 29 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ![Nagios!](https://www.nagios.com/wp-content/uploads/2015/05/Nagios-Black-500x124.png) 2 | 3 | [![Build Status](https://travis-ci.org/nagios-plugins/nagios-plugin-perl.svg?branch=master)](https://travis-ci.org/nagios-plugins/nagios-plugin-perl) 4 | 5 | Nagios::Monitoring::Plugin 6 | ========================== 7 | 8 | * These modules are meant for perl developers of [Nagios plugins](https://github.com/nagios-plugins/nagios-plugin-perl). 9 | 10 | * It is meant to simplify a lot of the common functions required to do checking of a particular service. 11 | 12 | * **The modules are still in an experimental stage and will be considered 13 | stable when it reaches version 1.0.** 14 | 15 | Installing 16 | ---------- 17 | 18 | You may need some prerequisites first. If you're on CentOS/RHEL, make sure to 19 | install CPAN and use those packages, since yum no longer supports some 20 | required packages. 21 | For CentOS 6/7: 22 | 23 | yum install -y perl-devel cpan make 24 | cpan Test::More 25 | cpan Params::Validate Math::Calc::Units Class::Accessor::Fast Config::Tiny 26 | 27 | For Debian 7+/Ubuntu 14.04+: 28 | 29 | apt-get install make libperl-dev libparams-validate-perl libmath-calc-units-perl libclass-accessor-perl libconfig-tiny-perl 30 | 31 | For Fedora 25+: 32 | 33 | dnf install -y make perl-devel 'perl(Params::Validate)' 'perl(Math::Calc::Units)' 'perl(Class::Accessor::Fast)' 'perl(Config::Tiny)' 'perl(Test::More)' 34 | 35 | To install this module type the following: 36 | 37 | perl Makefile.PL 38 | make 39 | make test 40 | make install 41 | 42 | Example 43 | ------- 44 | 45 | > Enough talk! Show me where to start! 46 | 47 | See the file `check_stuff.pl` in the `t/` directory for a complete 48 | working example of a plugin script. 49 | 50 | 51 | License Notice 52 | -------------- 53 | 54 | Copyright (C) 2006-2017 by Nagios Plugin Development Team 55 | 56 | This library is free software; you can redistribute it and/or modify 57 | it under the same terms as Perl itself, either Perl version 5.8.4 or, 58 | at your option, any later version of Perl 5 you may have available. 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | README -------------------------------------------------------------------------------- /lib/Nagios/Monitoring/Plugin.pm: -------------------------------------------------------------------------------- 1 | 2 | package Nagios::Monitoring::Plugin; 3 | 4 | use Nagios::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 | Nagios::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 $Nagios::Monitoring::Plugin::Functions::VERSION too 29 | our $VERSION = "0.51"; 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 = Nagios::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 Nagios::Monitoring::Plugin::Getopt; 65 | $self->opts( new Nagios::Monitoring::Plugin::Getopt(%args) ); 66 | } 67 | return $self; 68 | } 69 | 70 | sub add_perfdata { 71 | my ($self, %args) = @_; 72 | require Nagios::Monitoring::Plugin::Performance; 73 | my $perf = Nagios::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 Nagios::Monitoring::Plugin::Threshold; 84 | return $self->threshold( Nagios::Monitoring::Plugin::Threshold->set_thresholds(@_)); 85 | } 86 | 87 | # NP::Functions wrappers 88 | sub nagios_exit { 89 | my $self = shift; 90 | Nagios::Monitoring::Plugin::Functions::nagios_exit(@_, { plugin => $self }); 91 | } 92 | sub nagios_die { 93 | my $self = shift; 94 | Nagios::Monitoring::Plugin::Functions::nagios_die(@_, { plugin => $self }); 95 | } 96 | sub die { 97 | my $self = shift; 98 | Nagios::Monitoring::Plugin::Functions::nagios_die(@_, { plugin => $self }); 99 | } 100 | sub max_state { 101 | Nagios::Monitoring::Plugin::Functions::max_state(@_); 102 | } 103 | sub max_state_alt { 104 | Nagios::Monitoring::Plugin::Functions::max_state_alt(@_); 105 | } 106 | 107 | # top level interface to Nagios::Monitoring::Plugin::Threshold 108 | sub check_threshold { 109 | my $self = shift; 110 | 111 | my %args; 112 | 113 | if ( $#_ == 0 && (! ref $_[0] || ref $_[0] eq "ARRAY" )) { # one positional param 114 | %args = (check => shift); 115 | } 116 | else { 117 | %args = validate ( @_, { # named params 118 | check => 1, 119 | warning => 0, 120 | critical => 0, 121 | } ); 122 | } 123 | 124 | # in order of preference, get warning and critical from 125 | # 1. explicit arguments to check_threshold 126 | # 2. previously explicitly set threshold object 127 | # 3. implicit options from Getopts object 128 | if ( exists $args{warning} || exists $args{critical} ) { 129 | $self->set_thresholds( 130 | warning => $args{warning}, 131 | critical => $args{critical}, 132 | ); 133 | } 134 | elsif ( defined $self->threshold ) { 135 | # noop 136 | } 137 | elsif ( defined $self->opts ) { 138 | $self->set_thresholds( 139 | warning => $self->opts->warning, 140 | critical => $self->opts->critical, 141 | ); 142 | } 143 | else { 144 | return UNKNOWN; 145 | } 146 | 147 | return $self->threshold->get_status($args{check}); 148 | } 149 | 150 | # top level interface to my Nagios::Monitoring::Plugin::Getopt object 151 | sub add_arg { 152 | my $self = shift; 153 | $self->opts->arg(@_) if $self->_check_for_opts; 154 | } 155 | sub getopts { 156 | my $self = shift; 157 | $self->opts->getopts(@_) if $self->_check_for_opts; 158 | } 159 | 160 | sub _check_for_opts { 161 | my $self = shift; 162 | croak 163 | "You have to supply a 'usage' param to Nagios::Monitoring::Plugin::new() if you want to use Getopts from your Nagios::Monitoring::Plugin object." 164 | unless ref $self->opts() eq 'Nagios::Monitoring::Plugin::Getopt'; 165 | return $self; 166 | } 167 | 168 | 169 | 170 | # ------------------------------------------------------------------------- 171 | # NP::Functions::check_messages helpers and wrappers 172 | 173 | sub add_message { 174 | my $self = shift; 175 | my ($code, @messages) = @_; 176 | 177 | croak "Invalid error code '$code'" 178 | unless defined($ERRORS{uc $code}) || defined($STATUS_TEXT{$code}); 179 | 180 | # Store messages using strings rather than numeric codes 181 | $code = $STATUS_TEXT{$code} if $STATUS_TEXT{$code}; 182 | $code = lc $code; 183 | croak "Error code '$code' not supported by add_message" 184 | if $code eq 'unknown' || $code eq 'dependent'; 185 | 186 | $self->messages($code, []) unless $self->messages->{$code}; 187 | push @{$self->messages->{$code}}, @messages; 188 | } 189 | 190 | sub check_messages { 191 | my $self = shift; 192 | my %args = @_; 193 | 194 | # Add object messages to any passed in as args 195 | for my $code (qw(critical warning ok)) { 196 | my $messages = $self->messages->{$code} || []; 197 | if ($args{$code}) { 198 | unless (ref $args{$code} eq 'ARRAY') { 199 | if ($code eq 'ok') { 200 | $args{$code} = [ $args{$code} ]; 201 | } else { 202 | croak "Invalid argument '$code'" 203 | } 204 | } 205 | push @{$args{$code}}, @$messages; 206 | } 207 | else { 208 | $args{$code} = $messages; 209 | } 210 | } 211 | 212 | Nagios::Monitoring::Plugin::Functions::check_messages(%args); 213 | } 214 | 215 | # ------------------------------------------------------------------------- 216 | 217 | 1; 218 | 219 | #vim:et:sw=4 220 | 221 | __END__ 222 | 223 | =head1 NAME 224 | 225 | Nagios::Monitoring::Plugin - A family of perl modules to streamline writing Nagios 226 | plugins 227 | 228 | =head1 SYNOPSIS 229 | 230 | # Constants OK, WARNING, CRITICAL, and UNKNOWN are exported by default 231 | # See also Nagios::Monitoring::Plugin::Functions for a functional interface 232 | use Nagios::Monitoring::Plugin; 233 | 234 | # Constructor 235 | $np = Nagios::Monitoring::Plugin->new; # OR 236 | $np = Nagios::Monitoring::Plugin->new( shortname => "PAGESIZE" ); # OR 237 | 238 | 239 | # use Nagios::Monitoring::Plugin::Getopt to process the @ARGV command line options: 240 | # --verbose, --help, --usage, --timeout and --host are defined automatically. 241 | $np = Nagios::Monitoring::Plugin->new( 242 | usage => "Usage: %s [ -v|--verbose ] [-H ] [-t ] " 243 | . "[ -c|--critical= ] [ -w|--warning= ]", 244 | ); 245 | 246 | # add valid command line options and build them into your usage/help documentation. 247 | $np->add_arg( 248 | spec => 'warning|w=s', 249 | help => '-w, --warning=INTEGER:INTEGER . See ' 250 | . 'https://nagios-plugins.org/doc/guidelines.html#THRESHOLDFORMAT ' 251 | . 'for the threshold format. ', 252 | ); 253 | 254 | # Parse @ARGV and process standard arguments (e.g. usage, help, version) 255 | $np->getopts; 256 | 257 | 258 | # Exit/return value methods - nagios_exit( CODE, MESSAGE ), 259 | # nagios_die( MESSAGE, [CODE]) 260 | $page = retrieve_page($page1) 261 | or $np->nagios_exit( UNKNOWN, "Could not retrieve page" ); 262 | # Return code: 3; 263 | # output: PAGESIZE UNKNOWN - Could not retrieve page 264 | test_page($page) 265 | or $np->nagios_exit( CRITICAL, "Bad page found" ); 266 | 267 | # nagios_die() is just like nagios_exit(), but return code defaults 268 | # to UNKNOWN 269 | $page = retrieve_page($page2) 270 | or $np->nagios_die( "Could not retrieve page" ); 271 | # Return code: 3; 272 | # output: PAGESIZE UNKNOWN - Could not retrieve page 273 | 274 | # Threshold methods 275 | $code = $np->check_threshold( 276 | check => $value, 277 | warning => $warning_threshold, 278 | critical => $critical_threshold, 279 | ); 280 | $np->nagios_exit( $code, "Threshold check failed" ) if $code != OK; 281 | 282 | 283 | # Message methods (EXPERIMENTAL AND SUBJECT TO CHANGE) - 284 | # add_message( CODE, $message ); check_messages() 285 | for (@collection) { 286 | if (m/Error/) { 287 | $np->add_message( CRITICAL, $_ ); 288 | } else { 289 | $np->add_message( OK, $_ ); 290 | } 291 | } 292 | ($code, $message) = $np->check_messages(); 293 | nagios_exit( $code, $message ); 294 | # If any items in collection matched m/Error/, returns CRITICAL and 295 | # the joined set of Error messages; otherwise returns OK and the 296 | # joined set of ok messages 297 | 298 | 299 | # Perfdata methods 300 | $np->add_perfdata( 301 | label => "size", 302 | value => $value, 303 | uom => "kB", 304 | threshold => $threshold, 305 | ); 306 | $np->add_perfdata( label => "time", ... ); 307 | $np->nagios_exit( OK, "page size at http://... was ${value}kB" ); 308 | # Return code: 0; 309 | # output: PAGESIZE OK - page size at http://... was 36kB \ 310 | # | size=36kB;10:25;25: time=... 311 | 312 | 313 | =head1 DESCRIPTION 314 | 315 | Nagios::Monitoring::Plugin and its associated Nagios::Monitoring::Plugin::* modules are a 316 | family of perl modules to streamline writing Nagios plugins. The main 317 | end user modules are Nagios::Monitoring::Plugin, providing an object-oriented 318 | interface to the entire Nagios::Monitoring::Plugin::* collection, and 319 | Nagios::Monitoring::Plugin::Functions, providing a simpler functional interface to 320 | a useful subset of the available functionality. 321 | 322 | The purpose of the collection is to make it as simple as possible for 323 | developers to create plugins that conform the Nagios Plugin guidelines 324 | (https://nagios-plugins.org/doc/guidelines.html). 325 | 326 | 327 | =head2 EXPORTS 328 | 329 | Nagios status code constants are exported by default: 330 | 331 | OK 332 | WARNING 333 | CRITICAL 334 | UNKNOWN 335 | DEPENDENT 336 | 337 | The following variables are also exported on request: 338 | 339 | =over 4 340 | 341 | =item %ERRORS 342 | 343 | A hash mapping error strings ("CRITICAL", "UNKNOWN", etc.) to the 344 | corresponding status code. 345 | 346 | =item %STATUS_TEXT 347 | 348 | A hash mapping status code constants (OK, WARNING, CRITICAL, etc.) to the 349 | corresponding error string ("OK", "WARNING, "CRITICAL", etc.) i.e. the 350 | reverse of %ERRORS. 351 | 352 | =back 353 | 354 | 355 | =head2 CONSTRUCTOR 356 | 357 | Nagios::Monitoring::Plugin->new; 358 | 359 | Nagios::Monitoring::Plugin->new( shortname => 'PAGESIZE' ); 360 | 361 | Nagios::Monitoring::Plugin->new( 362 | usage => "Usage: %s [ -v|--verbose ] [-H ] [-t ] 363 | [ -c|--critical= ] [ -w|--warning= ] ", 364 | version => $VERSION, 365 | blurb => $blurb, 366 | extra => $extra, 367 | url => $url, 368 | license => $license, 369 | plugin => basename $0, 370 | timeout => 15, 371 | ); 372 | 373 | Instantiates a new Nagios::Monitoring::Plugin object. Accepts the following named 374 | arguments: 375 | 376 | =over 4 377 | 378 | =item shortname 379 | 380 | The 'shortname' for this plugin, used as the first token in the plugin 381 | output by the various exit methods. Default: uc basename $0. 382 | 383 | =item usage ("Usage: %s --foo --bar") 384 | 385 | Passing a value for the usage() argument makes Nagios::Monitoring::Plugin 386 | instantiate its own C object so you can start 387 | doing command line argument processing. See 388 | L for more about "usage" and the 389 | following options: 390 | 391 | =item version 392 | 393 | =item url 394 | 395 | =item blurb 396 | 397 | =item license 398 | 399 | =item extra 400 | 401 | =item plugin 402 | 403 | =item timeout 404 | 405 | =back 406 | 407 | =head2 OPTION HANDLING METHODS 408 | 409 | C provides these methods for accessing the functionality in C. 410 | 411 | =over 4 412 | 413 | =item add_arg 414 | 415 | Examples: 416 | 417 | # Define --hello argument (named parameters) 418 | $plugin->add_arg( 419 | spec => 'hello=s', 420 | help => "--hello\n Hello string", 421 | required => 1, 422 | ); 423 | 424 | # Define --hello argument (positional parameters) 425 | # Parameter order is 'spec', 'help', 'default', 'required?' 426 | $plugin->add_arg('hello=s', "--hello\n Hello string", undef, 1); 427 | 428 | See L for more details. 429 | 430 | =item getopts() 431 | 432 | Parses and processes the command line options you've defined, 433 | automatically doing the right thing with help/usage/version arguments. 434 | 435 | See L for more details. 436 | 437 | =item opts() 438 | 439 | Assuming you've instantiated it by passing 'usage' to new(), opts() 440 | returns the Nagios::Monitoring::Plugin object's C object, 441 | with which you can do lots of great things. 442 | 443 | E.g. 444 | 445 | if ( $plugin->opts->verbose ) { 446 | print "yah yah YAH YAH YAH!!!"; 447 | } 448 | 449 | # start counting down to timeout 450 | alarm $plugin->opts->timeout; 451 | your_long_check_step_that_might_time_out(); 452 | 453 | # access any of your custom command line options, 454 | # assuming you've done these steps above: 455 | # $plugin->add_arg('my_argument=s', '--my_argument [STRING]'); 456 | # $plugin->getopts; 457 | print $plugin->opts->my_argument; 458 | 459 | Again, see L. 460 | 461 | =back 462 | 463 | =head2 EXIT METHODS 464 | 465 | =over 4 466 | 467 | =item nagios_exit( , $message ) 468 | 469 | Exit with return code CODE, and a standard nagios message of the 470 | form "SHORTNAME CODE - $message". 471 | 472 | =item nagios_die( $message, [] ) 473 | 474 | Same as nagios_exit(), except that CODE is optional, defaulting 475 | to UNKNOWN. NOTE: exceptions are not raised by default to calling code. 476 | Set C<$_use_die> flag if this functionality is required (see test code). 477 | 478 | =item die( $message, [] ) 479 | 480 | Alias for nagios_die(). Deprecated. 481 | 482 | =item max_state, max_state_alt 483 | 484 | These are wrapper function for Nagios::Monitoring::Plugin::Functions::max_state and 485 | Nagios::Monitoring::Plugin::Functions::max_state_alt. 486 | 487 | =back 488 | 489 | =head2 THRESHOLD METHODS 490 | 491 | These provide a top level interface to the 492 | C module; for more details, see 493 | L and L. 494 | 495 | =over 4 496 | 497 | =item check_threshold( $value ) 498 | 499 | =item check_threshold( check => $value, warning => $warn, critical => $crit ) 500 | 501 | Evaluates $value against the thresholds and returns OK, CRITICAL, or 502 | WARNING constant. The thresholds may be: 503 | 504 | 1. explicitly set by passing 'warning' and/or 'critical' parameters to 505 | C, or, 506 | 507 | 2. explicitly set by calling C before C, or, 508 | 509 | 3. implicitly set by command-line parameters -w, -c, --critical or 510 | --warning, if you have run C<< $plugin->getopts() >>. 511 | 512 | You can specify $value as an array of values and each will be checked against 513 | the thresholds. 514 | 515 | The return value is ready to pass to C , e . g ., 516 | 517 | $p->nagios_exit( 518 | return_code => $p->check_threshold($result), 519 | message => " sample result was $result" 520 | ); 521 | 522 | 523 | =item set_thresholds(warning => "10:25", critical => "~:25") 524 | 525 | Sets the acceptable ranges and creates the plugin's 526 | Nagios::Monitoring::Plugins::Threshold object. See 527 | https://nagios-plugins.org/doc/guidelines.html#THRESHOLDFORMAT 528 | for details and examples of the threshold format. 529 | 530 | =item threshold() 531 | 532 | Returns the object's C object, if it has 533 | been defined by calling set_thresholds(). You can pass a new 534 | Threshold object to it to replace the old one too, but you shouldn't 535 | need to do that from a plugin script. 536 | 537 | =back 538 | 539 | =head2 MESSAGE METHODS 540 | 541 | EXPERIMENTAL AND SUBJECT TO CHANGE 542 | 543 | add_messages and check_messages are higher-level convenience methods to add 544 | and then check a set of messages, returning an appropriate return code 545 | and/or result message. They are equivalent to maintaining a set of @critical, 546 | @warning, and and @ok message arrays (add_message), and then doing a final 547 | if test (check_messages) like this: 548 | 549 | if (@critical) { 550 | nagios_exit( CRITICAL, join(' ', @critical) ); 551 | } 552 | elsif (@warning) { 553 | nagios_exit( WARNING, join(' ', @warning) ); 554 | } 555 | else { 556 | nagios_exit( OK, join(' ', @ok) ); 557 | } 558 | 559 | =over 4 560 | 561 | =item add_message( , $message ) 562 | 563 | Add a message with CODE status to the object. May be called multiple times. 564 | The messages added are checked by check_messages, following. 565 | 566 | Only CRITICAL, WARNING, and OK are accepted as valid codes. 567 | 568 | 569 | =item check_messages() 570 | 571 | Check the current set of messages and return an appropriate nagios return 572 | code and/or a result message. In scalar context, returns only a return 573 | code; in list context returns both a return code and an output message, 574 | suitable for passing directly to nagios_exit() e.g. 575 | 576 | $code = $np->check_messages; 577 | ($code, $message) = $np->check_messages; 578 | 579 | check_messages returns CRITICAL if any critical messages are found, WARNING 580 | if any warning messages are found, and OK otherwise. The message returned 581 | in list context defaults to the joined set of error messages; this may be 582 | customised using the arguments below. 583 | 584 | check_messages accepts the following named arguments (none are required): 585 | 586 | =over 4 587 | 588 | =item join => SCALAR 589 | 590 | A string used to join the relevant array to generate the message 591 | string returned in list context i.e. if the 'critical' array @crit 592 | is non-empty, check_messages would return: 593 | 594 | join( $join, @crit ) 595 | 596 | as the result message. Default: ' ' (space). 597 | 598 | =item join_all => SCALAR 599 | 600 | By default, only one set of messages are joined and returned in the 601 | result message i.e. if the result is CRITICAL, only the 'critical' 602 | messages are included in the result; if WARNING, only the 'warning' 603 | messages are included; if OK, the 'ok' messages are included (if 604 | supplied) i.e. the default is to return an 'errors-only' type 605 | message. 606 | 607 | If join_all is supplied, however, it will be used as a string to 608 | join the resultant critical, warning, and ok messages together i.e. 609 | all messages are joined and returned. 610 | 611 | =item critical => ARRAYREF 612 | 613 | Additional critical messages to supplement any passed in via add_message(). 614 | 615 | =item warning => ARRAYREF 616 | 617 | Additional warning messages to supplement any passed in via add_message(). 618 | 619 | =item ok => ARRAYREF | SCALAR 620 | 621 | Additional ok messages to supplement any passed in via add_message(). 622 | 623 | =back 624 | 625 | =back 626 | 627 | 628 | =head2 PERFORMANCE DATA METHODS 629 | 630 | =over 4 631 | 632 | =item add_perfdata( label => "size", value => $value, uom => "kB", threshold => $threshold ) 633 | 634 | Add a set of performance data to the object. May be called multiple times. 635 | The performance data is included in the standard plugin output messages by 636 | the various exit methods. 637 | 638 | See the Nagios::Monitoring::Plugin::Performance documentation for more information on 639 | performance data and the various field definitions, as well as the relevant 640 | section of the Nagios Plugin guidelines 641 | (https://nagios-plugins.org/doc/guidelines.html#AEN202). 642 | 643 | =back 644 | 645 | 646 | =head1 EXAMPLES 647 | 648 | "Enough talk! Show me some examples!" 649 | 650 | See the file 'check_stuff.pl' in the 't' directory included with the 651 | Nagios::Monitoring::Plugin distribution for a complete working example of a plugin 652 | script. 653 | 654 | 655 | =head1 VERSIONING 656 | 657 | The Nagios::Monitoring::Plugin::* modules are currently experimental and so the 658 | interfaces may change up until Nagios::Monitoring::Plugin hits version 1.0, although 659 | every attempt will be made to keep them as backwards compatible as 660 | possible. 661 | 662 | 663 | =head1 SEE ALSO 664 | 665 | See L for a simple functional interface to a subset 666 | of the available Nagios::Monitoring::Plugin functionality. 667 | 668 | See also L, L, 669 | L, L, and 670 | L. 671 | 672 | The Nagios Plugin project page is at https://nagios-plugins.org. 673 | 674 | 675 | =head1 BUGS 676 | 677 | Please report bugs in these modules to the Nagios Plugin development team: 678 | nagiosplug-devel@lists.sourceforge.net. 679 | 680 | 681 | =head1 AUTHOR 682 | 683 | Maintained by the Nagios Plugin development team - 684 | https://nagios-plugins.org. 685 | 686 | Originally by Ton Voon, Eton.voon@altinity.comE. 687 | 688 | =head1 COPYRIGHT AND LICENSE 689 | 690 | Copyright (C) 2006-2015 by Nagios Plugin Development Team 691 | 692 | This library is free software; you can redistribute it and/or modify it 693 | under the same terms as Perl itself, either Perl version 5.8.4 or, at your 694 | option, any later version of Perl 5 you may have available. 695 | 696 | =cut 697 | 698 | -------------------------------------------------------------------------------- /lib/Nagios/Monitoring/Plugin/Config.pm: -------------------------------------------------------------------------------- 1 | package Nagios::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 $CURRENT_FILE = undef; 11 | 12 | # Config paths ending in nagios (search for $FILENAME1) 13 | my @NAGIOS_CONFIG_PATH = qw(/etc/nagios /usr/local/nagios/etc /usr/local/etc/nagios /etc/opt/nagios); 14 | # Config paths not ending in nagios (search for $FILENAME2) 15 | my @CONFIG_PATH = qw(/etc /usr/local/etc /etc/opt); 16 | 17 | # Override Config::Tiny::read to default the filename, if not given 18 | sub read 19 | { 20 | my $class = shift; 21 | 22 | unless ($_[0]) { 23 | SEARCH: { 24 | if ($ENV{NAGIOS_CONFIG_PATH}) { 25 | for (split /:/, $ENV{NAGIOS_CONFIG_PATH}) { 26 | my $file = File::Spec->catfile($_, $FILENAME1); 27 | unshift(@_, $file), last SEARCH if -f $file; 28 | $file = File::Spec->catfile($_, $FILENAME2); 29 | unshift(@_, $file), last SEARCH if -f $file; 30 | } 31 | } 32 | for (@NAGIOS_CONFIG_PATH) { 33 | my $file = File::Spec->catfile($_, $FILENAME1); 34 | unshift(@_, $file), last SEARCH if -f $file; 35 | } 36 | for (@CONFIG_PATH) { 37 | my $file = File::Spec->catfile($_, $FILENAME2); 38 | unshift(@_, $file), last SEARCH if -f $file; 39 | } 40 | } 41 | 42 | # Use die instead of croak, so we can pass a clean message downstream 43 | die "Cannot find '$FILENAME1' or '$FILENAME2' in any standard location.\n" unless $_[0]; 44 | } 45 | 46 | $CURRENT_FILE = $_[0]; 47 | $class->SUPER::read( @_ ); 48 | } 49 | 50 | # Straight from Config::Tiny - only changes are repeated property key support 51 | # Would be nice if we could just override the per-line handling ... 52 | sub read_string 53 | { 54 | my $class = ref $_[0] ? ref shift : shift; 55 | my $self = bless {}, $class; 56 | return undef unless defined $_[0]; 57 | 58 | # Parse the file 59 | my $ns = '_'; 60 | my $counter = 0; 61 | foreach ( split /(?:\015{1,2}\012|\015|\012)/, shift ) { 62 | $counter++; 63 | 64 | # Skip comments and empty lines 65 | next if /^\s*(?:\#|\;|$)/; 66 | 67 | # Handle section headers 68 | if ( /^\s*\[\s*(.+?)\s*\]\s*$/ ) { 69 | # Create the sub-hash if it doesn't exist. 70 | # Without this sections without keys will not 71 | # appear at all in the completed struct. 72 | $self->{$ns = $1} ||= {}; 73 | next; 74 | } 75 | 76 | # Handle properties 77 | if ( /^\s*([^=]+?)\s*=\s*(.*?)\s*$/ ) { 78 | push @{$self->{$ns}->{$1}}, $2; 79 | next; 80 | } 81 | 82 | return $self->_error( "Syntax error at line $counter: '$_'" ); 83 | } 84 | 85 | $self; 86 | } 87 | 88 | sub write { croak "Write access not permitted" } 89 | 90 | # Return last file used by read(); 91 | sub np_getfile { return $CURRENT_FILE; } 92 | 93 | 1; 94 | 95 | =head1 NAME 96 | 97 | Nagios::Monitoring::Plugin::Config - read nagios plugin .ini style config files 98 | 99 | =head1 SYNOPSIS 100 | 101 | # Read given nagios plugin config file 102 | $Config = Nagios::Monitoring::Plugin::Config->read( '/etc/Nagios/Monitoring/Plugins.ini' ); 103 | 104 | # Search for and read default nagios plugin config file 105 | $Config = Nagios::Monitoring::Plugin::Config->read(); 106 | 107 | # Access sections and properties (returns scalars or arrayrefs) 108 | $rootproperty = $Config->{_}->{rootproperty}; 109 | $one = $Config->{section}->{one}; 110 | $Foo = $Config->{section}->{Foo}; 111 | 112 | =head1 DESCRIPTION 113 | 114 | Nagios::Monitoring::Plugin::Config is a subclass of the excellent Config::Tiny, 115 | with the following changes: 116 | 117 | =over 4 118 | 119 | =item 120 | 121 | Repeated keys are allowed within sections, returning lists instead of scalars 122 | 123 | =item 124 | 125 | Write functionality has been removed i.e. access is read only 126 | 127 | =item 128 | 129 | Nagios::Monitoring::Plugin::Config searches for a default nagios plugins file if no explicit 130 | filename is given to C. The current standard locations checked are: 131 | 132 | =over 4 133 | 134 | =item /etc/Nagios/Monitoring/Plugins.ini 135 | 136 | =item /usr/local/nagios/etc/plugins.ini 137 | 138 | =item /usr/local/etc/nagios /etc/opt/Nagios/Monitoring/Plugins.ini 139 | 140 | =item /etc/nagios-plugins.ini 141 | 142 | =item /usr/local/etc/nagios-plugins.ini 143 | 144 | =item /etc/opt/nagios-plugins.ini 145 | 146 | =back 147 | 148 | To use a custom location, set a C environment variable 149 | to the set of directories that should be checked. The first C or 150 | C file found will be used. 151 | 152 | =back 153 | 154 | 155 | =head1 SEE ALSO 156 | 157 | L, L 158 | 159 | 160 | =head1 AUTHORS 161 | 162 | This code is maintained by the Nagios Plugin Development Team: 163 | L. 164 | 165 | 166 | =head1 COPYRIGHT and LICENCE 167 | 168 | Copyright (C) 2006-2015 by Nagios Plugin Development Team 169 | 170 | This library is free software; you can redistribute it and/or modify 171 | it under the same terms as Perl itself. 172 | 173 | =cut 174 | 175 | -------------------------------------------------------------------------------- /lib/Nagios/Monitoring/Plugin/ExitResult.pm: -------------------------------------------------------------------------------- 1 | # Tiny helper class to return both output and return_code when testing 2 | 3 | package Nagios::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 | Nagios::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 Nagios::Monitoring::Plugin::Functions; 34 | 35 | # In a test file somewhere 36 | Nagios::Monitoring::Plugin::Functions::_fake_exit(1); 37 | 38 | # Later ... 39 | $e = nagios_exit( CRITICAL, 'aiiii ...' ); 40 | print $e->message; 41 | print $e->return_code; 42 | 43 | # NP::ExitResult also stringifies to the message output 44 | like(nagios_exit( WARNING, 'foobar'), qr/^foo/, 'matches!'); 45 | 46 | 47 | 48 | =head1 DESCRIPTION 49 | 50 | Nagios::Monitoring::Plugin::ExitResult is a tiny helper class intended for use 51 | when testing other Nagios::Monitoring::Plugin modules. A Nagios::Monitoring::Plugin::ExitResult 52 | object is returned by nagios_exit() and friends when 53 | Nagios::Monitoring::Plugin::Functions::_fake_exit has been set, instead of doing a 54 | conventional print + exit. 55 | 56 | =head1 AUTHOR 57 | 58 | Gavin Carr , Egavin@openfusion.com.auE 59 | 60 | =head1 COPYRIGHT AND LICENSE 61 | 62 | Copyright (C) 2006-2015 by Nagios Plugin Development Team 63 | 64 | This library is free software; you can redistribute it and/or modify 65 | it under the same terms as Perl itself. 66 | 67 | =cut 68 | -------------------------------------------------------------------------------- /lib/Nagios/Monitoring/Plugin/Functions.pm: -------------------------------------------------------------------------------- 1 | # Functional interface to basic Nagios::Monitoring::Plugin constants, exports, 2 | # and functions 3 | 4 | package Nagios::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 Nagios::Monitoring::Plugins as well 15 | our $VERSION = "0.51"; 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(nagios_exit nagios_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(nagios_exit nagios_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{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 | # nagios_exit( $code, $message ) 89 | sub nagios_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 Nagios::Monitoring::Plugin::ExitResult; 135 | return Nagios::Monitoring::Plugin::ExitResult->new($code, $output); 136 | } 137 | 138 | _nagios_exit($code, $output); 139 | } 140 | 141 | sub _nagios_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 | # nagios_die( $message, [ $code ]) OR nagios_die( $code, $message ) 159 | # Default $code: UNKNOWN 160 | sub nagios_die { 161 | my ($arg1, $arg2, $rest) = @_; 162 | 163 | # Named parameters 164 | if (defined $arg1 && ($arg1 eq 'return_code' || $arg1 eq 'message')) { 165 | return nagios_exit(@_); 166 | } 167 | 168 | # ($code, $message) 169 | elsif (defined $arg1 && (exists $ERRORS{$arg1} || exists $STATUS_TEXT{$arg1})) { 170 | return nagios_exit(@_); 171 | } 172 | 173 | # ($message, $code) 174 | elsif (defined $arg2 && (exists $ERRORS{$arg2} || exists $STATUS_TEXT{$arg2})) { 175 | return nagios_exit($arg2, $arg1, $rest); 176 | } 177 | 178 | # Else just assume $arg1 is the message and hope for the best 179 | else { 180 | return nagios_exit( UNKNOWN, $arg1, $arg2 ); 181 | } 182 | } 183 | 184 | # For backwards compatibility 185 | sub die { nagios_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 | Nagios::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 Nagios::Monitoring::Plugin::Functions; 262 | 263 | # nagios_exit( CODE, $message ) - exit with error code CODE, 264 | # and message "PLUGIN CODE - $message" 265 | nagios_exit( CRITICAL, $critical_error ) if $critical_error; 266 | nagios_exit( WARNING, $warning_error ) if $warning_error; 267 | nagios_exit( OK, $result ); 268 | 269 | # nagios_die( $message, [$CODE] ) - just like nagios_exit(), 270 | # but CODE is optional, defaulting to UNKNOWN 271 | do_something() 272 | or nagios_die("do_something() failed horribly"); 273 | do_something_critical() 274 | or nagios_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 nagios_exit/die; not exported by default) 285 | $shortname = get_shortname(); 286 | 287 | 288 | =head1 DESCRIPTION 289 | 290 | This module is part of the Nagios::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 | Nagios::Monitoring::Plugin. It is intended for those who prefer a simpler 294 | functional interface, and who do not need the additional 295 | functionality of Nagios::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 | nagios_exit 310 | nagios_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 nagios_exit( , $message ) 329 | 330 | Exit with return code CODE, and a standard nagios message of the 331 | form "PLUGIN CODE - $message". 332 | 333 | =item nagios_die( $message, [CODE] ) 334 | 335 | Same as nagios_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 nagios_exit/nagios_die. The default is basically 401 | 402 | uc basename( $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 | Nagios::Monitoring::Plugin; the nagios plugin developer guidelines at 431 | https://nagios-plugins.org/doc/guidelines.html. 432 | 433 | 434 | =head1 AUTHORS 435 | 436 | This code is maintained by the Nagios Plugin Development Team: https://nagios-plugins.org/ 437 | 438 | 439 | =head1 COPYRIGHT AND LICENSE 440 | 441 | Copyright (C) 2006-2015 by Nagios Plugin Development Team 442 | 443 | This library is free software; you can redistribute it and/or modify 444 | it under the same terms as Perl itself. 445 | 446 | =cut 447 | -------------------------------------------------------------------------------- /lib/Nagios/Monitoring/Plugin/Getopt.pm: -------------------------------------------------------------------------------- 1 | # 2 | # Nagios::Monitoring::Plugin::Getopt - OO perl module providing standardised argument 3 | # processing for nagios plugins 4 | # 5 | 6 | package Nagios::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 Nagios::Monitoring::Plugin::Functions; 16 | use Nagios::Monitoring::Plugin::Config; 17 | use vars qw($VERSION); 18 | $VERSION = $Nagios::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 https://nagios-plugins.org/doc/extra-opts.html\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 | Nagios::Monitoring::Plugin::Functions::_nagios_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 = Nagios::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{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 | Nagios::Monitoring::Plugin::Getopt - OO perl module providing standardised argument 489 | processing for Nagios plugins 490 | 491 | 492 | =head1 SYNOPSIS 493 | 494 | use Nagios::Monitoring::Plugin::Getopt; 495 | 496 | # Instantiate object (usage is mandatory) 497 | $ng = Nagios::Monitoring::Plugin::Getopt->new( 498 | usage => "Usage: %s -H -w -c ", 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 | Nagios::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 = Nagios::Monitoring::Plugin::Getopt->new( 543 | usage => 'Usage: %s --hello', 544 | version => '0.01', 545 | ); 546 | 547 | The Nagios::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 -p [-v]), 560 | 561 | might be displayed as: 562 | 563 | $ ./check_tcp_range --usage 564 | Usage: check_tcp_range -H -p [-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 -p [-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 and C arguments are required, while the C