From 121c2fbe6486e65e5c7f17d23c3a17fd8852c13f Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Mon, 2 Jan 2012 17:08:19 +0100 Subject: [PATCH] Added Nagios::Plugin::JUNOS perl module. This module is a subclass of Nagios::Plugin providing additional functions common to all JUNOS plugins. For now, check_junos.pl has been "ported" to use the new plugin. --- check_junos.pl | 217 ++---------------------- perl/lib/Nagios/Plugin/JUNOS.pm | 286 ++++++++++++++++++++++++++++++++ 2 files changed, 302 insertions(+), 201 deletions(-) create mode 100644 perl/lib/Nagios/Plugin/JUNOS.pm diff --git a/check_junos.pl b/check_junos.pl index e36d9d6..1e6d1f9 100755 --- a/check_junos.pl +++ b/check_junos.pl @@ -2,8 +2,8 @@ ############################################################################# # (c) 2001, 2003 Juniper Networks, Inc. # -# (c) 2011 Sebastian "tokkee" Harl # -# and team(ix) GmbH, Nuernberg, Germany # +# (c) 2011-2012 Sebastian "tokkee" Harl # +# and team(ix) GmbH, Nuernberg, Germany # # # # This file is part of "team(ix) Monitoring Plugins" # # URL: http://oss.teamix.org/projects/monitoringplugins/ # @@ -39,11 +39,12 @@ use warnings; use utf8; -use POSIX qw( :termios_h ); -use Nagios::Plugin; - use JUNOS::Device; +use FindBin qw( $Bin ); +use lib "$Bin/perl/lib"; +use Nagios::Plugin::JUNOS; + binmode STDOUT, ":utf8"; my $valid_checks = "interfaces|chassis_environment|system_storage"; @@ -51,7 +52,7 @@ my $valid_checks = "interfaces|chassis_environment|system_storage"; # TODO: # * chassis_routing_engine: show chassis routing-engine (-> number and status) -my $plugin = Nagios::Plugin->new( +my $plugin = Nagios::Plugin::JUNOS->new( plugin => 'check_junos', shortname => 'check_junos', version => '0.1', @@ -91,16 +92,6 @@ Warning and critical thresholds may be specified in the format documented at http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT.", ); -# Predefined arguments (by Nagios::Plugin) -my @predefined_args = qw( - usage - help - version - extra-opts - timeout - verbose -); - my @args = ( { spec => 'host|H=s', @@ -128,59 +119,17 @@ my @args = ( }, ); -my %conf = (); my $junos = undef; foreach my $arg (@args) { - add_arg($plugin, $arg); + $plugin->add_arg($arg); } -$plugin->getopts; -# Initialize this first, so it may be used right away. -$conf{'verbose'} = $plugin->opts->verbose; +$plugin->configure(); +$plugin->add_checks($valid_checks, 'chassis_environment', @ARGV); +$junos = $plugin->connect(); -foreach my $arg (@args) { - my @c = get_conf($plugin, $arg); - $conf{$c[0]} = $c[1]; -} - -foreach my $arg (@predefined_args) { - $conf{$arg} = $plugin->opts->$arg; -} - -add_checks(\%conf, @ARGV); - -if (! $plugin->opts->password) { - my $term = POSIX::Termios->new(); - my $lflag; - - print "Password: "; - - $term->getattr(fileno(STDIN)); - $lflag = $term->getlflag; - $term->setlflag($lflag & ~POSIX::ECHO); - $term->setattr(fileno(STDIN), TCSANOW); - - $conf{'password'} = ; - chomp($conf{'password'}); - - $term->setlflag($lflag | POSIX::ECHO); - print "\n"; -} - -verbose(1, "Connecting to host $conf{'host'} as user $conf{'user'}."); -$junos = JUNOS::Device->new( - hostname => $conf{'host'}, - login => $conf{'user'}, - password => $conf{'password'}, - access => 'ssh', - 'ssh-compress' => 0); - -if (! ref $junos) { - $plugin->die("ERROR: failed to connect to " . $conf{'host'} . "!"); -} - -foreach my $check (@{$conf{'checks'}}) { +foreach my $check ($plugin->get_checks()) { my $code; my $value; @@ -245,7 +194,7 @@ foreach my $check (@{$conf{'checks'}}) { my $phy_name = get_iface_name($marker); $phy_name =~ s/\.\d+$//; - verbose(3, "Quering physical interface '$phy_name' " + $plugin->verbose(3, "Quering physical interface '$phy_name' " . "for $name."); my @phy_interfaces = get_interfaces($junos, {}, $phy_name); @@ -425,7 +374,7 @@ sub send_query my $res; my $err; - verbose(3, "Sending query '$query' " + $plugin->verbose(3, "Sending query '$query' " . join(", ", map { "$_ => $queryargs->{$_}" } keys %$queryargs) . " to router."); @@ -534,10 +483,10 @@ sub get_interfaces } } - if ($conf{'verbose'} >= 3) { + { my @i = map { get_iface_name($_) . " => " . get_iface_status($_) } @ret; - verbose(3, "Interfaces: " . join(", ", @i)); + $plugin->verbose(3, "Interfaces: " . join(", ", @i)); } return @ret; } @@ -669,137 +618,3 @@ sub get_liface_marker return @markers; } -sub add_arg -{ - my $plugin = shift; - my $arg = shift; - - my $spec = $arg->{'spec'}; - my $help = $arg->{'usage'}; - - if (defined $arg->{'desc'}) { - my @desc; - - if (ref($arg->{'desc'})) { - @desc = @{$arg->{'desc'}}; - } - else { - @desc = ( $arg->{'desc'} ); - } - - foreach my $d (@desc) { - $help .= "\n $d"; - } - - if (defined $arg->{'default'}) { - $help .= " (default: $arg->{'default'})"; - } - } - elsif (defined $arg->{'default'}) { - $help .= "\n (default: $arg->{'default'})"; - } - - $plugin->add_arg( - spec => $spec, - help => $help, - ); -} - -sub get_conf -{ - my $plugin = shift; - my $arg = shift; - - my ($name, undef) = split(m/\|/, $arg->{'spec'}); - my $value = $plugin->opts->$name || $arg->{'default'}; - - if ($name eq 'password') { - verbose(3, "conf: password => " - . (($value eq '') ? '' : '')); - } - else { - verbose(3, "conf: $name => $value"); - } - return ($name => $value); -} - -sub add_single_check -{ - my $conf = shift; - my @check = split(m/,/, shift); - - my %c = (); - - if ($check[0] !~ m/\b(?:$valid_checks)\b/) { - return "ERROR: invalid check '$check[0]'"; - } - - $c{'name'} = $check[0]; - - $c{'target'} = undef; - if (defined($check[1])) { - $c{'target'} = [ split(m/\+/, $check[1]) ]; - } - - $c{'warning'} = $check[2]; - $c{'critical'} = $check[3]; - - # check for valid thresholds - # set_threshold() will die if any threshold is not valid - $plugin->set_thresholds( - warning => $c{'warning'}, - critical => $c{'critical'}, - ) || $plugin->die("ERROR: Invalid thresholds: " - . "warning => $c{'warning'}, critical => $c{'critical'}"); - - push @{$conf->{'checks'}}, \%c; -} - -sub add_checks -{ - my $conf = shift; - my @checks = @_; - - my $err_str = "ERROR:"; - - if (scalar(@checks) == 0) { - $conf->{'checks'}[0] = { - name => 'chassis_environment', - target => [], - warning => undef, - critical => undef, - }; - return 1; - } - - $conf->{'checks'} = []; - - foreach my $check (@checks) { - my $e; - - $e = add_single_check($conf, $check); - if ($e =~ m/^ERROR: (.*)$/) { - $err_str .= " $1,"; - } - } - - if ($err_str ne "ERROR:") { - $err_str =~ s/,$//; - $plugin->die($err_str); - } -} - -sub verbose -{ - my $level = shift; - my @msgs = @_; - - if ($level > $conf{'verbose'}) { - return; - } - - foreach my $msg (@msgs) { - print "V$level: $msg\n"; - } -} - diff --git a/perl/lib/Nagios/Plugin/JUNOS.pm b/perl/lib/Nagios/Plugin/JUNOS.pm new file mode 100644 index 0000000..a62e323 --- /dev/null +++ b/perl/lib/Nagios/Plugin/JUNOS.pm @@ -0,0 +1,286 @@ +############################################################################# +# (c) 2012 Sebastian "tokkee" Harl # +# and team(ix) GmbH, Nuernberg, Germany # +# # +# This file is part of "team(ix) Monitoring Plugins" # +# URL: http://oss.teamix.org/projects/monitoringplugins/ # +# # +# All rights reserved. # +# Redistribution and use in source and binary forms, with or without # +# modification, are permitted provided that the following conditions # +# are met: # +# 1. Redistributions of source code must retain the above copyright # +# notice, this list of conditions and the following disclaimer. # +# 2. Redistributions in binary form must reproduce the above copyright # +# notice, this list of conditions and the following disclaimer in the # +# documentation and/or other materials provided with the distribution. # +# 3. The name of the copyright owner may not be used to endorse or # +# promote products derived from this software without specific prior # +# written permission. # +# # +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # +# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, # +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, # +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING # +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # +# POSSIBILITY OF SUCH DAMAGE. # +############################################################################# + +package Nagios::Plugin::JUNOS; + +use POSIX qw( :termios_h ); + +use Nagios::Plugin; +use JUNOS::Device; + +use Nagios::Plugin::Functions qw( %ERRORS %STATUS_TEXT @STATUS_CODES ); + +# re-export Nagios::Plugin's (default) exports +use Exporter; +our @ISA = qw( Nagios::Plugin Exporter ); +our @EXPORT = (@STATUS_CODES); +our @EXPORT_OK = qw( %ERRORS %STATUS_TEXT ); + +sub new +{ + my $class = shift; + my %args = @_; + + my $self = Nagios::Plugin->new(%args); + + $self->{'conf'} = { verbose => 0 }; + $self->{'cl_args'} = []; + $self->{'junos'} = undef; + + return bless($self, $class); +} + +sub add_arg +{ + my $self = shift; + my $arg = shift; + + my $spec = $arg->{'spec'}; + my $help; + + push @{$self->{'cl_args'}}, $arg; + + if (defined $arg->{'usage'}) { + $help = $arg->{'usage'}; + } + else { + $help = $arg->{'help'}; + } + + if (defined $arg->{'desc'}) { + my @desc; + + if (ref($arg->{'desc'})) { + @desc = @{$arg->{'desc'}}; + } + else { + @desc = ( $arg->{'desc'} ); + } + + foreach my $d (@desc) { + $help .= "\n $d"; + } + + if (defined $arg->{'default'}) { + $help .= " (default: $arg->{'default'})"; + } + } + elsif (defined $arg->{'default'}) { + $help .= "\n (default: $arg->{'default'})"; + } + + $self->SUPER::add_arg( + spec => $spec, + help => $help, + ); +} + +sub configure +{ + my $self = shift; + + # Predefined arguments (by Nagios::Plugin) + my @predefined_args = qw( + usage + help + version + extra-opts + timeout + verbose + ); + + $self->getopts; + # Initialize this first, so it may be used right away. + $self->{'conf'}->{'verbose'} = $self->opts->verbose; + + foreach my $arg (@{$self->{'cl_args'}}) { + my @c = $self->_get_conf($arg); + $self->{'conf'}->{$c[0]} = $c[1]; + } + + foreach my $arg (@predefined_args) { + $self->{'conf'}->{$arg} = $self->opts->$arg; + } +} + +sub _get_conf +{ + my $self = shift; + my $arg = shift; + + my ($name, undef) = split(m/\|/, $arg->{'spec'}); + my $value = $self->opts->$name || $arg->{'default'}; + + if ($name eq 'password') { + $self->verbose(3, "conf: password => " + . (($value eq '') ? '' : '')); + } + else { + $self->verbose(3, "conf: $name => $value"); + } + return ($name => $value); +} + +sub _add_single_check +{ + my $self = shift; + my $valid_checks = shift; + my @check = split(m/,/, shift); + + my %c = (); + + if ($check[0] !~ m/\b(?:$valid_checks)\b/) { + return "ERROR: invalid check '$check[0]'"; + } + + $c{'name'} = $check[0]; + + $c{'target'} = undef; + if (defined($check[1])) { + $c{'target'} = [ split(m/\+/, $check[1]) ]; + } + + $c{'warning'} = $check[2]; + $c{'critical'} = $check[3]; + + # check for valid thresholds + # set_threshold() will die if any threshold is not valid + $self->set_thresholds( + warning => $c{'warning'}, + critical => $c{'critical'}, + ) || $self->die("ERROR: Invalid thresholds: " + . "warning => $c{'warning'}, critical => $c{'critical'}"); + + push @{$self->{'conf'}->{'checks'}}, \%c; +} + +sub add_checks +{ + my $self = shift; + my $valid_checks = shift; + my $default = shift; + my @checks = @_; + + my $err_str = "ERROR:"; + + if (scalar(@checks) == 0) { + $self->{'conf'}->{'checks'}[0] = { + name => $default, + target => [], + warning => undef, + critical => undef, + }; + return 1; + } + + $self->{'conf'}->{'checks'} = []; + + foreach my $check (@checks) { + my $e; + + $e = $self->_add_single_check($valid_checks, $check); + if ($e =~ m/^ERROR: (.*)$/) { + $err_str .= " $1,"; + } + } + + if ($err_str ne "ERROR:") { + $err_str =~ s/,$//; + $self->die($err_str); + } +} + +sub get_checks +{ + my $self = shift; + return @{$self->{'conf'}->{'checks'}}; +} + +sub connect +{ + my $self = shift; + + my $host = $self->{'conf'}->{'host'}; + my $user = $self->{'conf'}->{'user'}; + + if (! $self->opts->password) { + my $term = POSIX::Termios->new(); + my $lflag; + + print "Password: "; + + $term->getattr(fileno(STDIN)); + $lflag = $term->getlflag; + $term->setlflag($lflag & ~POSIX::ECHO); + $term->setattr(fileno(STDIN), TCSANOW); + + $self->{'conf'}->{'password'} = ; + chomp($self->{'conf'}->{'password'}); + + $term->setlflag($lflag | POSIX::ECHO); + print "\n"; + } + + $self->verbose(1, "Connecting to host $host as user $user."); + $junos = JUNOS::Device->new( + hostname => $host, + login => $user, + password => $self->{'conf'}->{'password'}, + access => 'ssh', + 'ssh-compress' => 0); + + if (! ref $junos) { + $self->die("ERROR: failed to connect to $host!"); + } + + $self->{'junos'} = $junos; + return $junos; +} + +sub verbose +{ + my $self = shift; + my $level = shift; + my @msgs = @_; + + if ($level > $self->{'conf'}->{'verbose'}) { + return; + } + + foreach my $msg (@msgs) { + print "V$level: $msg\n"; + } +} + +return 1; +