Remove old PERL plugins
I'm not able to support them
This commit is contained in:
parent
c3786e5ae1
commit
6b146c728f
602
check_junos.pl
602
check_junos.pl
|
@ -1,602 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
#############################################################################
|
||||
# (c) 2001, 2003 Juniper Networks, Inc. #
|
||||
# (c) 2011-2012 Sebastian "tokkee" Harl <sh@teamix.net> #
|
||||
# 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. #
|
||||
#############################################################################
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use utf8;
|
||||
|
||||
use JUNOS::Device;
|
||||
use JUNOS::Trace;
|
||||
|
||||
use FindBin qw( $Bin );
|
||||
use lib "$Bin/perl/lib";
|
||||
use Nagios::Plugin::JUNOS;
|
||||
|
||||
binmode STDOUT, ":utf8";
|
||||
|
||||
# TODO:
|
||||
# * chassis_routing_engine: show chassis routing-engine (-> number and status)
|
||||
|
||||
my $plugin = Nagios::Plugin::JUNOS->new(
|
||||
plugin => 'check_junos',
|
||||
shortname => 'check_junos',
|
||||
version => '0.1',
|
||||
url => 'http://oss.teamix.org/projects/monitoringplugins',
|
||||
blurb => 'Monitor Juniper™ Switches.',
|
||||
usage =>
|
||||
"Usage: %s [-v|--verbose] [-H <host>] [-p <port>] [-t <timeout]
|
||||
[-C] [-U <user>] [-P <password] check-tuple [...]",
|
||||
license =>
|
||||
"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 3-Clause
|
||||
BSD License (see http://opensource.org/licenses/BSD-3-Clause).",
|
||||
extra => "
|
||||
This plugin connects to a Juniper™ Switch device and checks various of its
|
||||
components.
|
||||
|
||||
A check-tuple consists of the name of the check and, optionally, a \"target\"
|
||||
which more closely specifies which characteristics should be checked, and
|
||||
warning and critical thresholds:
|
||||
checkname[,target[,warning[,critical]]]
|
||||
|
||||
The following checks are available:
|
||||
* interfaces: Status of interfaces. If a target is specified, only the
|
||||
specified interface(s) will be taken into account. The special target
|
||||
'\@with_description' selects all interfaces with a non-empty description.
|
||||
|
||||
If an aggregated interface is encountered, the physical interfaces will
|
||||
be checked as well.
|
||||
|
||||
* interface_forwarding: Check the forwarding state of interfaces as provided
|
||||
by 'show ethernet-switching interfaces'. Storm control, MAC limit and
|
||||
BPDUs will be considered CRITICAL states. If a target is specified, only
|
||||
the specified interface(s) will be taken into account. Targets may be
|
||||
specified as <interface_name>:<forwarding_state> in which case a CRITICAL
|
||||
state is assumed if the specified interface is not in the specified state.
|
||||
|
||||
* chassis_environment: Check the status of verious system components
|
||||
(as provided by 'show chassis environment'). If a target is specified,
|
||||
only the specified component(s) will be taken into account. If specified,
|
||||
the thresholds will be checked against the temperature of the components.
|
||||
|
||||
* system_storage: Check the amount of used space of system filesystems. If a
|
||||
target is specified, only the specified filesystem(s) will be taken into
|
||||
account (specified either by filesystem name or mount point). The
|
||||
threshold will be checked against the amount (percent) of used space.
|
||||
|
||||
Warning and critical thresholds may be specified in the format documented at
|
||||
http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT.",
|
||||
);
|
||||
|
||||
my %checks = (
|
||||
interfaces => \&check_interfaces,
|
||||
interface_forwarding => \&check_interface_forwarding,
|
||||
chassis_environment => \&check_chassis_environment,
|
||||
system_storage => \&check_system_storage,
|
||||
);
|
||||
|
||||
my $junos = undef;
|
||||
|
||||
my $cache = {};
|
||||
|
||||
$plugin->add_common_args();
|
||||
$plugin->add_arg({
|
||||
spec => 'caching|C',
|
||||
usage => '-C, --caching',
|
||||
desc => 'Enabling caching of API results',
|
||||
default => 0,
|
||||
});
|
||||
|
||||
foreach my $check (keys %checks) {
|
||||
$plugin->add_check_impl($check, $checks{$check});
|
||||
}
|
||||
|
||||
$plugin->set_default_check('chassis_environment');
|
||||
|
||||
# configure removes any options from @ARGV
|
||||
$plugin->configure();
|
||||
|
||||
if ($plugin->{'conf'}->{'verbose'} > 3) {
|
||||
JUNOS::Trace::init(1);
|
||||
}
|
||||
|
||||
$plugin->set_checks(@ARGV);
|
||||
$junos = $plugin->connect();
|
||||
|
||||
$plugin->run_checks();
|
||||
|
||||
my ($code, $msg) = $plugin->check_messages(join => ', ');
|
||||
|
||||
$plugin->nagios_exit($code, $msg);
|
||||
|
||||
sub check_interface
|
||||
{
|
||||
my $plugin = shift;
|
||||
my $iface = shift;
|
||||
my $opts = shift || {};
|
||||
my @targets = @_;
|
||||
|
||||
my $name = $plugin->get_query_object_value($iface, 'name');
|
||||
my $admin_status = $plugin->get_query_object_value($iface, 'admin-status');
|
||||
|
||||
if ($admin_status !~ m/^up$/) {
|
||||
if ((grep { $name =~ m/^$_$/; } @targets)
|
||||
|| ($opts->{'with_description'} &&
|
||||
$plugin->get_query_object_value($iface, 'description'))) {
|
||||
$plugin->add_message(CRITICAL,
|
||||
"$name is not enabled");
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($plugin->get_query_object_value($iface, 'oper-status') !~ m/^up$/i) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$plugin->add_perfdata(
|
||||
label => "'$name-input-bytes'",
|
||||
value => $plugin->get_query_object_value($iface,
|
||||
['traffic-statistics', 'input-bytes']),
|
||||
min => 0,
|
||||
max => undef,
|
||||
uom => 'B',
|
||||
threshold => undef,
|
||||
);
|
||||
$plugin->add_perfdata(
|
||||
label => "'$name-output-bytes'",
|
||||
value => $plugin->get_query_object_value($iface,
|
||||
['traffic-statistics', 'output-bytes']),
|
||||
min => 0,
|
||||
max => undef,
|
||||
uom => 'B',
|
||||
threshold => undef,
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub get_interfaces
|
||||
{
|
||||
my $plugin = shift;
|
||||
my $opts = shift || {};
|
||||
my @targets = @_;
|
||||
|
||||
my @ifaces = ();
|
||||
my @ret = ();
|
||||
|
||||
if (defined($cache->{'interfaces'})) {
|
||||
@ifaces = @{$cache->{'interfaces'}};
|
||||
}
|
||||
else {
|
||||
my $cmd = 'get_interface_information';
|
||||
my $res;
|
||||
|
||||
my $args = { detail => 1 };
|
||||
|
||||
if ((scalar(@targets) == 1) && (! $opts->{'with_description'})
|
||||
&& (! $plugin->{'conf'}->{'caching'})) {
|
||||
$args->{'interface_name'} = $targets[0];
|
||||
}
|
||||
$res = $plugin->send_query($cmd, $args);
|
||||
|
||||
if (! ref $res) {
|
||||
$plugin->die($res);
|
||||
}
|
||||
|
||||
@ifaces = $res->getElementsByTagName('physical-interface');
|
||||
}
|
||||
|
||||
if ($plugin->{'conf'}->{'caching'}) {
|
||||
$cache->{'interfaces'} = \@ifaces;
|
||||
}
|
||||
|
||||
@targets = map { s/\*/\.\*/g; s/\?/\./g; $_; } @targets;
|
||||
|
||||
if (scalar(@targets)) {
|
||||
@ret = grep {
|
||||
my $i = $_;
|
||||
grep { $plugin->get_query_object_value($i, 'name') =~ m/^$_$/ } @targets;
|
||||
} @ifaces;
|
||||
}
|
||||
elsif (! $opts->{'with_description'}) {
|
||||
@ret = @ifaces;
|
||||
}
|
||||
|
||||
if ($opts->{'with_description'}) {
|
||||
foreach my $iface (@ifaces) {
|
||||
my $name = $plugin->get_query_object_value($iface, 'name');
|
||||
if ($plugin->get_query_object_value($iface, 'description')
|
||||
&& (! grep { m/^$name$/; } @targets)) {
|
||||
push @ret, $iface;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
my @i = map { $plugin->get_query_object_value($_, 'name'). " => "
|
||||
. $plugin->get_query_object_value($_, 'oper-status') } @ret;
|
||||
$plugin->verbose(3, "Interfaces: " . join(", ", @i));
|
||||
}
|
||||
return @ret;
|
||||
}
|
||||
|
||||
sub check_interfaces
|
||||
{
|
||||
my $plugin = shift;
|
||||
my $junos = shift;
|
||||
my $targets = shift || [];
|
||||
my $exclude = shift || [];
|
||||
|
||||
my $opts = {
|
||||
with_description => 0,
|
||||
};
|
||||
|
||||
if (grep { m/^\@with_description$/; } @$targets) {
|
||||
$opts->{'with_description'} = 1;
|
||||
|
||||
@$targets = grep { ! m/^\@with_description$/; } @$targets;
|
||||
}
|
||||
|
||||
my @interfaces = get_interfaces($plugin, $opts, @$targets);;
|
||||
|
||||
my $down_count = 0;
|
||||
my @down_ifaces = ();
|
||||
|
||||
my $phys_down_count = 0;
|
||||
my @phys_down_ifaces = ();
|
||||
|
||||
my $have_lag_ifaces = 0;
|
||||
|
||||
foreach my $iface (@interfaces) {
|
||||
my $name = $plugin->get_query_object_value($iface, 'name');
|
||||
my $desc = $plugin->get_query_object_value($iface, 'description');
|
||||
my $status;
|
||||
|
||||
if (grep { m/^$name$/ } @$exclude) {
|
||||
next;
|
||||
}
|
||||
|
||||
$status = check_interface($plugin, $iface, $opts, @$targets);
|
||||
|
||||
my $tmp;
|
||||
|
||||
if ($status == 0) {
|
||||
++$down_count;
|
||||
$tmp = $name . ($desc ? " ($desc)" : "");
|
||||
push @down_ifaces, $tmp;
|
||||
}
|
||||
|
||||
if ($status <= 0) {
|
||||
# disabled or down
|
||||
next;
|
||||
}
|
||||
|
||||
if ($name !~ m/^ae/) {
|
||||
next;
|
||||
}
|
||||
|
||||
$have_lag_ifaces = 1;
|
||||
|
||||
my @markers = $plugin->get_query_object($iface,
|
||||
['logical-interface', 'lag-traffic-statistics', 'lag-marker']);
|
||||
|
||||
if (! @markers) {
|
||||
print STDERR "Cannot get marker for non-LACP interfaces yet!\n";
|
||||
next;
|
||||
}
|
||||
|
||||
foreach my $marker (@markers) {
|
||||
my $phy_name = $plugin->get_query_object_value($marker, 'name');
|
||||
$phy_name =~ s/\.\d+$//;
|
||||
|
||||
$plugin->verbose(3, "Quering physical interface '$phy_name' "
|
||||
. "for $name.");
|
||||
|
||||
my @phy_interfaces = get_interfaces($plugin, {}, $phy_name);
|
||||
foreach my $phy_iface (@phy_interfaces) {
|
||||
if (check_interface($plugin, $phy_iface, {}, $phy_name) == 0) {
|
||||
++$phys_down_count;
|
||||
$tmp = $name . ($desc ? " ($desc)" : "");
|
||||
push @phys_down_ifaces, "$tmp -> $phy_name";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($down_count > 0) {
|
||||
$plugin->add_message(CRITICAL, $down_count . " interface"
|
||||
. ($down_count == 1 ? "" : "s")
|
||||
. " down (" . join(", ", @down_ifaces) . ")");
|
||||
}
|
||||
|
||||
if ($phys_down_count > 0) {
|
||||
$plugin->add_message(WARNING, $phys_down_count
|
||||
. " LAG member interface"
|
||||
. ($phys_down_count == 1 ? "" : "s")
|
||||
. " down ("
|
||||
. join(", ", @phys_down_ifaces) . ")");
|
||||
}
|
||||
|
||||
if ((! $down_count) && (! $phys_down_count)) {
|
||||
if ((! scalar(@$targets)) || $opts->{'with_description'}) {
|
||||
$plugin->add_message(OK, "all interfaces up"
|
||||
. ($have_lag_ifaces
|
||||
? " (including all LAG member interfaces)" : ""));
|
||||
}
|
||||
else {
|
||||
$plugin->add_message(OK, "interface"
|
||||
. (scalar(@$targets) == 1 ? " " : "s ")
|
||||
. join(", ", @$targets) . " up"
|
||||
. ($have_lag_ifaces
|
||||
? " (including all LAG member interfaces)" : ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub check_interface_forwarding
|
||||
{
|
||||
my $plugin = shift;
|
||||
my $junos = shift;
|
||||
my $targets = shift || [];
|
||||
my $exclude = shift || [];
|
||||
|
||||
my $res = $plugin->send_query('show ethernet-switching interfaces brief');
|
||||
|
||||
my %critical_map = (
|
||||
'Disabled by bpdu-control' => 1,
|
||||
'MAC limit exceeded' => 1,
|
||||
'MAC move limit exceeded' => 1,
|
||||
'Storm control in effect' => 1,
|
||||
);
|
||||
|
||||
my %targets = map { my @t = split(':', $_); $t[0] => $t[1]; } @$targets;
|
||||
|
||||
my @failed = ();
|
||||
|
||||
{
|
||||
my @i = map {
|
||||
$plugin->get_query_object_value($_, 'interface-name')
|
||||
. " => { " .
|
||||
join(", ", map {
|
||||
$plugin->get_query_object_value($_, 'blocking-status')
|
||||
} $plugin->get_query_object($_,
|
||||
['interface-vlan-member-list', 'interface-vlan-member']))
|
||||
. " }"
|
||||
} $plugin->get_query_object($res, 'interface');
|
||||
$plugin->verbose(3, "Interfaces: " . join(", ", @i));
|
||||
}
|
||||
|
||||
foreach my $iface ($plugin->get_query_object($res, 'interface')) {
|
||||
my $name = $plugin->get_query_object_value($iface, 'interface-name');
|
||||
my $failed_status = undef;
|
||||
|
||||
if (scalar(@$targets) && (! exists($targets{$name}))) {
|
||||
next;
|
||||
}
|
||||
|
||||
if (grep { m/^$name$/ } @$exclude) {
|
||||
next;
|
||||
}
|
||||
|
||||
foreach my $vlan_member ($plugin->get_query_object($iface,
|
||||
['interface-vlan-member-list', 'interface-vlan-member'])) {
|
||||
my $status = $plugin->get_query_object_value($vlan_member,
|
||||
'blocking-status');
|
||||
|
||||
if (defined($targets{$name})) {
|
||||
if ($status ne $targets{$name}) {
|
||||
$failed_status = "$status (should: $targets{$name})";
|
||||
last;
|
||||
}
|
||||
}
|
||||
elsif (defined $critical_map{$status}) {
|
||||
$failed_status = $status;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
if ($failed_status) {
|
||||
push @failed, { name => $name, status => $failed_status };
|
||||
}
|
||||
}
|
||||
|
||||
if (scalar(@failed)) {
|
||||
$plugin->add_message(CRITICAL, scalar(@failed) . " interface"
|
||||
. (scalar(@failed) == 1 ? "" : "s")
|
||||
. " blocked: "
|
||||
. join(", ", map { "$_->{'name'}: $_->{'status'}" } @failed));
|
||||
}
|
||||
else {
|
||||
$plugin->add_message(OK, "forwarding state of all interfaces OK");
|
||||
}
|
||||
}
|
||||
|
||||
sub check_chassis_environment
|
||||
{
|
||||
my $plugin = shift;
|
||||
my $junos = shift;
|
||||
my $targets = shift || [];
|
||||
my $exclude = shift || [];
|
||||
|
||||
my $res = $plugin->send_query('get_environment_information');
|
||||
|
||||
my %status_map = (
|
||||
OK => OK,
|
||||
Testing => UNKNOWN,
|
||||
Check => UNKNOWN,
|
||||
Failed => CRITICAL,
|
||||
Absent => CRITICAL,
|
||||
);
|
||||
|
||||
my $items_count = 0;
|
||||
my $items_ok = 0;
|
||||
|
||||
my $class = "";
|
||||
foreach my $item ($plugin->get_query_object($res, 'environment-item')) {
|
||||
my $name = $plugin->get_query_object_value($item, 'name');
|
||||
|
||||
if (scalar(@$targets) && (! grep { m/^$name$/ } @$targets)) {
|
||||
next;
|
||||
}
|
||||
|
||||
if (grep { m/^$name$/ } @$exclude) {
|
||||
next;
|
||||
}
|
||||
|
||||
if ($plugin->get_query_object_value($item, 'class')) {
|
||||
$class = $plugin->get_query_object_value($item, 'class');
|
||||
}
|
||||
|
||||
my $status = $plugin->get_query_object_value($item, 'status');
|
||||
|
||||
if ($status eq "Absent") {
|
||||
if (! scalar(@$targets)) {
|
||||
next;
|
||||
}
|
||||
# else: check this component
|
||||
}
|
||||
|
||||
my $state = UNKNOWN;
|
||||
if (defined $status_map{$status}) {
|
||||
$state = $status_map{$status};
|
||||
}
|
||||
|
||||
++$items_count;
|
||||
|
||||
if ($state == OK) {
|
||||
++$items_ok;
|
||||
}
|
||||
else {
|
||||
$plugin->add_message($state, $class . " $name: status " .
|
||||
$status);
|
||||
}
|
||||
|
||||
my $temp = $plugin->get_query_object_value($item, 'temperature');
|
||||
if (! $temp) {
|
||||
next;
|
||||
}
|
||||
|
||||
($temp) = $temp =~ m/(\d+) degrees C/;
|
||||
if (! defined($temp)) {
|
||||
next;
|
||||
}
|
||||
|
||||
$state = $plugin->check_threshold($temp);
|
||||
if ($state != OK) {
|
||||
$plugin->add_message($state, $class
|
||||
. " $name: ${temp} degrees C");
|
||||
}
|
||||
|
||||
my $label = "$name-temp";
|
||||
$label =~ s/ /_/g;
|
||||
$plugin->add_perfdata(
|
||||
label => "'$label'",
|
||||
value => $temp,
|
||||
min => undef,
|
||||
max => undef,
|
||||
uom => '',
|
||||
threshold => $plugin->threshold(),
|
||||
);
|
||||
}
|
||||
|
||||
if (! $items_count) {
|
||||
$plugin->add_message(WARNING, "no components found");
|
||||
}
|
||||
elsif ($items_count == $items_ok) {
|
||||
$plugin->add_message(OK, "$items_ok component"
|
||||
. ($items_ok == 1 ? "" : "s")
|
||||
. " OK");
|
||||
}
|
||||
else {
|
||||
$plugin->add_message(WARNING,
|
||||
"$items_ok / $items_count components OK");
|
||||
}
|
||||
}
|
||||
|
||||
sub check_system_storage
|
||||
{
|
||||
my $plugin = shift;
|
||||
my $junos = shift;
|
||||
my $targets = shift || [];
|
||||
my $exclude = shift || [];
|
||||
|
||||
my $res = $plugin->send_query('get_system_storage');
|
||||
|
||||
my $all_ok = 1;
|
||||
|
||||
foreach my $re ($plugin->get_query_object($res,
|
||||
'multi-routing-engine-item')) {
|
||||
my $re_name = $plugin->get_query_object_value($re, 're-name');
|
||||
|
||||
foreach my $fs ($plugin->get_query_object($re,
|
||||
['system-storage-information', 'filesystem'])) {
|
||||
my $name = $plugin->get_query_object_value($fs, 'filesystem-name');
|
||||
my $mnt_pt = $plugin->get_query_object_value($fs, 'mounted-on');
|
||||
|
||||
if (scalar(@$targets) && (! grep { m/^$name$/ } @$targets)
|
||||
&& (! grep { m/^$mnt_pt$/ } @$targets)) {
|
||||
next;
|
||||
}
|
||||
|
||||
if (grep { m/^$mnt_pt$/ } @$exclude) {
|
||||
next;
|
||||
}
|
||||
|
||||
my $used = $plugin->get_query_object_value($fs, 'used-percent') + 0;
|
||||
|
||||
my $state = $plugin->check_threshold($used);
|
||||
if ($state != OK) {
|
||||
$all_ok = 0;
|
||||
$plugin->add_message($state, "$re_name $mnt_pt: "
|
||||
. "$used\% used");
|
||||
}
|
||||
$plugin->add_perfdata(
|
||||
label => "'$re_name-$mnt_pt'",
|
||||
value => $used,
|
||||
min => 0,
|
||||
max => 100,
|
||||
uom => '%',
|
||||
threshold => $plugin->threshold(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($all_ok) {
|
||||
$plugin->add_message(OK, "all filesystems within thresholds");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,581 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
#############################################################################
|
||||
# (c) 2001, 2003 Juniper Networks, Inc. #
|
||||
# (c) 2011 Sebastian "tokkee" Harl <sh@teamix.net> #
|
||||
# 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. #
|
||||
#############################################################################
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use utf8;
|
||||
|
||||
use Data::Dumper;
|
||||
|
||||
use POSIX qw( :termios_h );
|
||||
use Nagios::Plugin;
|
||||
|
||||
use Regexp::Common;
|
||||
use Regexp::IPv6 qw( $IPv6_re );
|
||||
|
||||
use JUNOS::Device;
|
||||
|
||||
binmode STDOUT, ":utf8";
|
||||
|
||||
my $valid_checks = "peers_count|prefix_count";
|
||||
|
||||
my $plugin = Nagios::Plugin->new(
|
||||
plugin => 'check_junos_bgp',
|
||||
shortname => 'check_junos_bgp',
|
||||
version => '0.1',
|
||||
url => 'http://oss.teamix.org/projects/monitoringplugins',
|
||||
blurb => 'Monitor Juniper™ Router\'s BGP tables.',
|
||||
usage =>
|
||||
"Usage: %s [-v|--verbose] [-t <timeout] \
|
||||
[-H <host>] [-p <port>] [-U <user>] [-P <password] \
|
||||
[-L <logical-system-name>] [-I <name of instance>] check-tuple [...] ",
|
||||
license =>
|
||||
"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 3-Clause
|
||||
BSD License (see http://opensource.org/licenses/BSD-3-Clause).",
|
||||
extra => "
|
||||
This plugin connects to a Juniper™ Router device and requests BGP table
|
||||
information using the 'show bgp neighbor' command. It then checks the
|
||||
specified thresholds depending on the specified checks.
|
||||
|
||||
A check-tuple consists of the name of the check and, optionally, a \"target\"
|
||||
(e.g., peer address), and warning and critical thresholds:
|
||||
checkname[,target[,warning[,critical]]]
|
||||
|
||||
The following checks are available:
|
||||
* peers_count: Total number of peers. If a target is specified, only peers
|
||||
matching that target are taken into account.
|
||||
|
||||
* prefix_count: Number of active prefixes for a single peer. If multiple
|
||||
peers match the specified target, each of those is checked against the
|
||||
specified thresholds.
|
||||
|
||||
Targets are either specified as IPv4/IPv6 addresses or regular expressions /
|
||||
strings. In the former case, the target is compared against the peer's
|
||||
address, else against the peer's description. When specifying regular
|
||||
expressions, they have to be enclosed in '/'. Else, the pattern is treated as
|
||||
verbatim string that has to be matched.
|
||||
|
||||
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',
|
||||
usage => '-H, --host=HOSTNAME',
|
||||
desc => 'Hostname/IP of Juniper box to connect to',
|
||||
default => 'localhost',
|
||||
},
|
||||
{
|
||||
spec => 'port|p=i',
|
||||
usage => '-p, --port=PORT',
|
||||
desc => 'Port to connect to',
|
||||
default => 22,
|
||||
},
|
||||
{
|
||||
spec => 'user|U=s',
|
||||
usage => '-U, --user=USERNAME',
|
||||
desc => 'Username to log into box as',
|
||||
default => 'root',
|
||||
},
|
||||
{
|
||||
spec => 'password|P=s',
|
||||
usage => '-P, --password=PASSWORD',
|
||||
desc => 'Password for login username',
|
||||
default => '<prompt>',
|
||||
},
|
||||
{
|
||||
spec => 'logical-router|L=s',
|
||||
usage => '-L, --logical-router=ROUTER',
|
||||
desc => 'Logical Router',
|
||||
default => '',
|
||||
},
|
||||
{
|
||||
spec => 'instance|I=s',
|
||||
usage => '-I, --instance=INSTANCE',
|
||||
desc => 'Instance',
|
||||
default => '',
|
||||
},
|
||||
|
||||
);
|
||||
|
||||
my %conf = ();
|
||||
my $junos = undef;
|
||||
|
||||
my $neigh_info = undef;
|
||||
my @peers = ();
|
||||
|
||||
foreach my $arg (@args) {
|
||||
add_arg($plugin, $arg);
|
||||
}
|
||||
|
||||
$plugin->getopts;
|
||||
# Initialize this first, so it may be used right away.
|
||||
$conf{'verbose'} = $plugin->opts->verbose;
|
||||
|
||||
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'} = <STDIN>;
|
||||
chomp($conf{'password'});
|
||||
|
||||
$term->setlflag($lflag | POSIX::ECHO);
|
||||
$term->setattr(fileno(STDIN), TCSAFLUSH);
|
||||
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'} . "!");
|
||||
}
|
||||
|
||||
verbose(1, "Querying BGP neighbor information.");
|
||||
$neigh_info = get_neighbor_information($junos);
|
||||
if (! ref $neigh_info) {
|
||||
$plugin->die($neigh_info);
|
||||
}
|
||||
|
||||
@peers = $neigh_info->getElementsByTagName('bgp-peer');
|
||||
if ($conf{'verbose'} >= 3) {
|
||||
my @p = map { (get_peer_address($_) // "<unknown address>")
|
||||
. " => " . (get_peer_description($_) // "<unknown description>") } @peers;
|
||||
verbose(3, "Peers: " . join(", ", @p));
|
||||
}
|
||||
|
||||
foreach my $check (@{$conf{'checks'}}) {
|
||||
my $code;
|
||||
my $value;
|
||||
|
||||
my @relevant_peers = get_relevant_peers($check, @peers);
|
||||
if ($conf{'verbose'} >= 2) {
|
||||
my @p = map { (get_peer_address($_) // "<unknown address>")
|
||||
. " => " . (get_peer_description($_) // "<unknown description>") } @relevant_peers;
|
||||
verbose(2, "Relevant peers: " . join(", ", @p));
|
||||
}
|
||||
|
||||
$plugin->set_thresholds(
|
||||
warning => $check->{'warning'},
|
||||
critical => $check->{'critical'},
|
||||
);
|
||||
|
||||
if ($check->{'name'} eq 'peers_count') {
|
||||
$value = scalar(@relevant_peers);
|
||||
$code = $plugin->check_threshold($value);
|
||||
|
||||
$plugin->add_message($code, "$value peer" . (($value == 1) ? "" : "s"));
|
||||
$plugin->add_perfdata(
|
||||
label => 'peers_count',
|
||||
value => $value,
|
||||
min => 0,
|
||||
max => undef,
|
||||
uom => '',
|
||||
threshold => $plugin->threshold(),
|
||||
);
|
||||
}
|
||||
elsif ($check->{'name'} eq 'prefix_count') {
|
||||
foreach my $peer (@relevant_peers) {
|
||||
my $peer_addr = get_peer_address($peer);
|
||||
|
||||
if (! defined($peer_addr)) {
|
||||
$peer_addr = "<unknown address>";
|
||||
}
|
||||
|
||||
$value = get_peer_element($peer, 'peer-state');
|
||||
|
||||
if (! defined($value)) {
|
||||
$value = "<unknown state>";
|
||||
}
|
||||
|
||||
verbose(2, "Peer $peer_addr: peer-state = $value.");
|
||||
|
||||
if ($value eq 'Established') {
|
||||
$value = $peer->getElementsByTagName('bgp-rib');
|
||||
$value = get_peer_element($value->[0], 'active-prefix-count');
|
||||
if (! $value) {
|
||||
$value = 0;
|
||||
}
|
||||
$code = $plugin->check_threshold($value);
|
||||
$plugin->add_message($code, "peer $peer_addr: $value prefix"
|
||||
. (($value == 1) ? "" : "es"));
|
||||
|
||||
verbose(2, "Peer $peer_addr: active-prefix-count = $value.");
|
||||
}
|
||||
else {
|
||||
$value = "";
|
||||
$code = CRITICAL;
|
||||
$plugin->add_message($code,
|
||||
"peer $peer_addr: no established connection");
|
||||
}
|
||||
|
||||
$plugin->add_perfdata(
|
||||
label => '\'prefix_count[' . $peer_addr . ']\'',
|
||||
value => $value,
|
||||
min => 0,
|
||||
max => undef,
|
||||
uom => '',
|
||||
threshold => $plugin->threshold(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my ($code, $msg) = $plugin->check_messages(join => ', ');
|
||||
|
||||
$junos->disconnect();
|
||||
|
||||
$plugin->nagios_exit($code, $msg);
|
||||
|
||||
sub send_query
|
||||
{
|
||||
my $device = shift;
|
||||
my $query = shift;
|
||||
my $queryargs = shift;
|
||||
|
||||
my $res;
|
||||
my $err;
|
||||
|
||||
verbose(3, "Sending query '$query' to router.");
|
||||
|
||||
if (ref $queryargs) {
|
||||
$res = $device->$query(%$queryargs);
|
||||
} else {
|
||||
$res = $device->$query();
|
||||
}
|
||||
|
||||
verbose(5, "Got response: " . Dumper(\$res));
|
||||
|
||||
if (! ref $res) {
|
||||
return "ERROR: Failed to execute query '$query'";
|
||||
}
|
||||
|
||||
$err = $res->getFirstError();
|
||||
if ($err) {
|
||||
return "ERROR: " . $err->{message};
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub get_neighbor_information
|
||||
{
|
||||
my $device = shift;
|
||||
my @table;
|
||||
|
||||
my $query = "get_bgp_summary_information";
|
||||
|
||||
my $res;
|
||||
my %args;
|
||||
|
||||
if (($conf{'logical-router'} ne '') || ($conf{'instance'} ne '')) {
|
||||
if ($conf{'logical-router'} ne '') {
|
||||
$args{'logical-router'} = $conf{'logical-router'};
|
||||
}
|
||||
if ($conf{'instance'} ne '') {
|
||||
$args{'instance'} = $conf{'instance'};
|
||||
}
|
||||
|
||||
$res = send_query($device, $query, \%args);
|
||||
} else {
|
||||
$res = send_query($device, $query);
|
||||
}
|
||||
my $err;
|
||||
|
||||
if (! ref $res) {
|
||||
return $res;
|
||||
}
|
||||
|
||||
$err = $res->getFirstError();
|
||||
if ($err) {
|
||||
return "ERROR: " . $err->{message};
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
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 '<prompt>') ? '<prompt>' : '<hidden>'));
|
||||
}
|
||||
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];
|
||||
|
||||
if ((! defined($check[1])) || ($check[1] eq "")) {
|
||||
$c{'target'} = qr//,
|
||||
$c{'ttype'} = 'address',
|
||||
}
|
||||
elsif ($check[1] =~ m/^(?:$RE{'net'}{'IPv4'}|$IPv6_re)$/) {
|
||||
$c{'target'} = $check[1];
|
||||
$c{'ttype'} = 'address';
|
||||
}
|
||||
elsif ($check[1] =~ m/^\/(.*)\/$/) {
|
||||
$c{'target'} = qr/$1/;
|
||||
$c{'ttype'} = 'description';
|
||||
}
|
||||
else {
|
||||
$c{'target'} = $check[1];
|
||||
$c{'ttype'} = 'description';
|
||||
}
|
||||
|
||||
$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 => 'peers_count',
|
||||
target => qr//,
|
||||
ttype => 'address',
|
||||
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 get_relevant_peers
|
||||
{
|
||||
my $check = shift;
|
||||
my @peers = @_;
|
||||
|
||||
my @rpeers = ();
|
||||
|
||||
my $cmp = sub {
|
||||
my ($a, $b, undef) = @_;
|
||||
if (ref $b) {
|
||||
my $r = $a =~ $b;
|
||||
verbose(3, "Checking peer '$a' against regex '$b' -> "
|
||||
. ($r ? "true" : "false") . ".");
|
||||
return $r;
|
||||
}
|
||||
else {
|
||||
my $r = $a eq $b;
|
||||
verbose(3, "Comparing peer '$a' with string '$b' -> "
|
||||
. ($r ? "true" : "false") . ".");
|
||||
return $r;
|
||||
}
|
||||
};
|
||||
|
||||
my $get_peer_elem;
|
||||
|
||||
if ($check->{'ttype'} eq 'description') {
|
||||
$get_peer_elem = \&get_peer_description;
|
||||
}
|
||||
else {
|
||||
$get_peer_elem = \&get_peer_address;
|
||||
}
|
||||
|
||||
@rpeers = grep { $cmp->($get_peer_elem->($_), $check->{'target'}) } @peers;
|
||||
return @rpeers;
|
||||
}
|
||||
|
||||
sub get_peer_element
|
||||
{
|
||||
my $peer = shift;
|
||||
my $elem = shift;
|
||||
|
||||
my $e;
|
||||
|
||||
if (! $peer) {
|
||||
print STDERR "Cannot retrieve element '$elem' "
|
||||
. "from undefined value.\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$e = $peer->getElementsByTagName($elem);
|
||||
if ((! $e) || (! $e->item(0))) {
|
||||
print STDERR "Attribute '$elem' not found for peer.\n";
|
||||
verbose(4, "Peer was: " . Dumper($peer));
|
||||
return;
|
||||
}
|
||||
|
||||
return $e->item(0)->getFirstChild->getNodeValue;
|
||||
}
|
||||
|
||||
sub get_peer_description
|
||||
{
|
||||
my $peer = shift;
|
||||
return get_peer_element($peer, 'description');
|
||||
}
|
||||
|
||||
sub get_peer_address
|
||||
{
|
||||
my $peer = shift;
|
||||
return get_peer_element($peer, 'peer-address');
|
||||
}
|
||||
|
||||
sub verbose
|
||||
{
|
||||
my $level = shift;
|
||||
my @msgs = @_;
|
||||
|
||||
if ($level > $conf{'verbose'}) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach my $msg (@msgs) {
|
||||
print "V$level: $msg\n";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,836 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
#############################################################################
|
||||
# (c) 2001, 2003 Juniper Networks, Inc. #
|
||||
# (c) 2011 Sebastian "tokkee" Harl <sh@teamix.net> #
|
||||
# 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. #
|
||||
#############################################################################
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use utf8;
|
||||
|
||||
use Data::Dumper;
|
||||
|
||||
use POSIX qw( :termios_h );
|
||||
use Nagios::Plugin;
|
||||
|
||||
use JUNOS::Device;
|
||||
|
||||
binmode STDOUT, ":utf8";
|
||||
|
||||
my $valid_checks = "members_count|master|backup|interfaces|version";
|
||||
|
||||
# TODO:
|
||||
# (on newer JUNOS (10.4r5.5))
|
||||
# request chassis routing-engine master switch check
|
||||
# -> graceful switchover status
|
||||
|
||||
my $plugin = Nagios::Plugin->new(
|
||||
plugin => 'check_junos_vc',
|
||||
shortname => 'check_junos_vc',
|
||||
version => '0.1',
|
||||
url => 'http://oss.teamix.org/projects/monitoringplugins',
|
||||
blurb => 'Monitor Juniper™ Switch Virtual Chassis.',
|
||||
usage =>
|
||||
"Usage: %s [-v|--verbose] [-H <host>] [-p <port>] [-t <timeout]
|
||||
[-U <user>] [-P <password] check-tuple [...]",
|
||||
license =>
|
||||
"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 3-Clause
|
||||
BSD License (see http://opensource.org/licenses/BSD-3-Clause).",
|
||||
extra => "
|
||||
This plugin connects to a Juniper™ Switch device and and checks Virtual
|
||||
Chassis information.
|
||||
|
||||
A check-tuple consists of the name of the check and, optionally, a \"target\"
|
||||
which more closely specifies which characteristics should be checked, and
|
||||
warning and critical thresholds:
|
||||
checkname[,target[,warning[,critical]]]
|
||||
|
||||
The following checks are available:
|
||||
* members_count: Total number of members in the Virtual Chassis. If a target
|
||||
is specified, only peers whose status (NotPrsnt, Prsnt) matches one of the
|
||||
specified targets are taken into account.
|
||||
|
||||
* master, backup: Check the number or assignment of master resp. backup
|
||||
members. If a target is specified, check that those members whose serial
|
||||
number matches the specified target have the requested role (master,
|
||||
backup) assigned to them. Else, check the number of master resp. backup
|
||||
members against the specified thresholds.
|
||||
|
||||
* interfaces: Check that all VCP interfaces are up. If warning or critical
|
||||
thresholds have been specified, also check the number of VCP interfaces
|
||||
against the thresholds.
|
||||
|
||||
* version: Check the version of all physically connected members.
|
||||
|
||||
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',
|
||||
usage => '-H, --host=HOSTNAME',
|
||||
desc => 'Hostname/IP of Juniper box to connect to',
|
||||
default => 'localhost',
|
||||
},
|
||||
{
|
||||
spec => 'port|p=i',
|
||||
usage => '-p, --port=PORT',
|
||||
desc => 'Port to connect to',
|
||||
default => 22,
|
||||
},
|
||||
{
|
||||
spec => 'user|U=s',
|
||||
usage => '-U, --user=USERNAME',
|
||||
desc => 'Username to log into box as',
|
||||
default => 'root',
|
||||
},
|
||||
{
|
||||
spec => 'password|P=s',
|
||||
usage => '-P, --password=PASSWORD',
|
||||
desc => 'Password for login username',
|
||||
default => '<prompt>',
|
||||
},
|
||||
);
|
||||
|
||||
my %conf = ();
|
||||
my $junos = undef;
|
||||
|
||||
foreach my $arg (@args) {
|
||||
add_arg($plugin, $arg);
|
||||
}
|
||||
|
||||
$plugin->getopts;
|
||||
# Initialize this first, so it may be used right away.
|
||||
$conf{'verbose'} = $plugin->opts->verbose;
|
||||
|
||||
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'} = <STDIN>;
|
||||
chomp($conf{'password'});
|
||||
|
||||
$term->setlflag($lflag | POSIX::ECHO);
|
||||
$term->setattr(fileno(STDIN), TCSAFLUSH);
|
||||
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'} . "!");
|
||||
}
|
||||
|
||||
my $vc = undef;
|
||||
my @vc_members = ();
|
||||
my $have_vc_members = 0;
|
||||
|
||||
foreach my $check (@{$conf{'checks'}}) {
|
||||
my $code;
|
||||
my $value;
|
||||
|
||||
my @targets = ();
|
||||
|
||||
if (defined $check->{'target'}) {
|
||||
@targets = @{$check->{'target'}};
|
||||
}
|
||||
|
||||
$plugin->set_thresholds(
|
||||
warning => $check->{'warning'},
|
||||
critical => $check->{'critical'},
|
||||
);
|
||||
|
||||
if ($check->{'name'} eq 'members_count') {
|
||||
my @relevant_members = ();
|
||||
my $value = 0;
|
||||
my $code;
|
||||
|
||||
@vc_members = get_vc_members($junos);
|
||||
|
||||
if (scalar(@targets)) {
|
||||
foreach my $member (@vc_members) {
|
||||
my $role = get_member_status($member);
|
||||
if (scalar(grep { $role eq $_ } @targets)) {
|
||||
push @relevant_members, $member;
|
||||
$value++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@relevant_members = @vc_members;
|
||||
$value = scalar(@vc_members);
|
||||
}
|
||||
|
||||
$code = $plugin->check_threshold($value);
|
||||
|
||||
$plugin->add_message($code, "$value " . join(" + ", @targets)
|
||||
. " member" . (($value == 1) ? "" : "s"));
|
||||
my $label = 'members';
|
||||
if (scalar(@targets)) {
|
||||
$label .= '[' . join('+', @targets) . ']';
|
||||
}
|
||||
$plugin->add_perfdata(
|
||||
label => $label,
|
||||
value => $value,
|
||||
min => 0,
|
||||
max => undef,
|
||||
uom => '',
|
||||
threshold => $plugin->threshold(),
|
||||
);
|
||||
}
|
||||
elsif (($check->{'name'} eq 'master') || ($check->{'name'} eq 'backup')) {
|
||||
my $wanted_role = ($check->{'name'} eq 'master')
|
||||
? 'Master' : 'Backup';
|
||||
|
||||
my @wanted_members = ();
|
||||
|
||||
my $value;
|
||||
my $code;
|
||||
|
||||
@vc_members = get_vc_members($junos);
|
||||
|
||||
foreach my $member (@vc_members) {
|
||||
my $role = get_member_role($member);
|
||||
|
||||
if ($role eq $wanted_role) {
|
||||
push @wanted_members, $member;
|
||||
}
|
||||
}
|
||||
|
||||
if (scalar(@targets)) {
|
||||
my @ok_targets = ();
|
||||
my @fail_targets = ();
|
||||
|
||||
$code = UNKNOWN;
|
||||
|
||||
foreach my $target (@targets) {
|
||||
if (scalar(grep { $target eq get_member_serial($_) } @wanted_members)) {
|
||||
# requested target does have wanted role assigned
|
||||
if (($code == UNKNOWN) || ($code == OK)) {
|
||||
$code = OK;
|
||||
}
|
||||
else {
|
||||
# we've had previous errors
|
||||
$code = WARNING;
|
||||
}
|
||||
|
||||
push @ok_targets, $target;
|
||||
}
|
||||
else {
|
||||
if (($code == OK) || ($code == WARNING)) {
|
||||
# we've had previous success
|
||||
$code = WARNING;
|
||||
}
|
||||
else {
|
||||
$code = CRITICAL;
|
||||
}
|
||||
|
||||
push @fail_targets, $target;
|
||||
}
|
||||
}
|
||||
|
||||
$plugin->add_message($code, scalar(@fail_targets)
|
||||
. " missing/failed-over " . $check->{'name'}
|
||||
. ((scalar(@fail_targets) == 1) ? "" : "s")
|
||||
. (scalar(@fail_targets)
|
||||
? " (" . join(", ", @fail_targets) . ")" : "")
|
||||
. ", " . scalar(@ok_targets) . " active " . $check->{'name'}
|
||||
. ((scalar(@ok_targets) == 1) ? "" : "s")
|
||||
. (scalar(@ok_targets)
|
||||
? " (" . join(", ", @ok_targets) . ")" : ""));
|
||||
$plugin->add_perfdata(
|
||||
label => 'active_' . $check->{'name'},
|
||||
value => scalar(@ok_targets),
|
||||
min => 0,
|
||||
max => undef,
|
||||
uom => '',
|
||||
threshold => undef,
|
||||
);
|
||||
$plugin->add_perfdata(
|
||||
label => 'failed_' . $check->{'name'},
|
||||
value => scalar(@fail_targets),
|
||||
min => 0,
|
||||
max => undef,
|
||||
uom => '',
|
||||
threshold => undef,
|
||||
);
|
||||
}
|
||||
else {
|
||||
$value = scalar @wanted_members;
|
||||
$code = $plugin->check_threshold($value);
|
||||
|
||||
$plugin->add_message($code, "$value " . $check->{'name'} . " member"
|
||||
. (($value == 1) ? "" : "s"));
|
||||
$plugin->add_perfdata(
|
||||
label => $check->{'name'},
|
||||
value => $value,
|
||||
min => 0,
|
||||
max => undef,
|
||||
uom => '',
|
||||
threshold => $plugin->threshold(),
|
||||
);
|
||||
}
|
||||
}
|
||||
elsif ($check->{'name'} eq 'interfaces') {
|
||||
my @up_ifaces = ();
|
||||
my @down_ifaces = ();
|
||||
|
||||
my @vc_interfaces = get_vc_interfaces($junos);
|
||||
|
||||
foreach my $iface (@vc_interfaces) {
|
||||
my $status = get_iface_status($iface);
|
||||
|
||||
if ($status eq 'up') {
|
||||
push @up_ifaces, get_iface_name($iface);
|
||||
next;
|
||||
}
|
||||
# else:
|
||||
|
||||
push @down_ifaces, {
|
||||
name => get_iface_name($iface),
|
||||
status => $status,
|
||||
};
|
||||
}
|
||||
|
||||
if (scalar(@down_ifaces)) {
|
||||
$plugin->add_message(CRITICAL, scalar(@down_ifaces)
|
||||
. " VCP interface" . ((scalar(@down_ifaces) == 1) ? "" : "s")
|
||||
. " not up ("
|
||||
. join(", ",
|
||||
map { "$_->{'name'} $_->{'status'}" } @down_ifaces)
|
||||
. ")");
|
||||
}
|
||||
elsif ($check->{'warning'} || $check->{'critical'}) {
|
||||
my $value = scalar @vc_interfaces;
|
||||
my $code = $plugin->check_threshold($value);
|
||||
|
||||
$plugin->add_message($code, "$value VCP interface"
|
||||
. (($value == 1) ? "" : "s") . " found ("
|
||||
. scalar(@up_ifaces) . " up, "
|
||||
. scalar(@down_ifaces) . " down)");
|
||||
}
|
||||
elsif (! scalar(@up_ifaces)) {
|
||||
# no VCP interfaces at all
|
||||
$plugin->add_message(CRITICAL, "no VCP interfaces found");
|
||||
}
|
||||
else {
|
||||
$plugin->add_message(OK, "all VCP interfaces up");
|
||||
}
|
||||
|
||||
$plugin->add_perfdata(
|
||||
label => 'vcp_interfaces',
|
||||
value => scalar(@vc_interfaces),
|
||||
min => 0,
|
||||
max => undef,
|
||||
uom => '',
|
||||
threshold => $plugin->threshold(),
|
||||
);
|
||||
$plugin->add_perfdata(
|
||||
label => 'up_interfaces',
|
||||
value => scalar(@up_ifaces),
|
||||
min => 0,
|
||||
max => undef,
|
||||
uom => '',
|
||||
threshold => undef,
|
||||
);
|
||||
$plugin->add_perfdata(
|
||||
label => 'down_interfaces',
|
||||
value => scalar(@down_ifaces),
|
||||
min => 0,
|
||||
max => undef,
|
||||
uom => '',
|
||||
threshold => undef,
|
||||
);
|
||||
}
|
||||
elsif ($check->{'name'} eq 'version') {
|
||||
my %versions = get_versions($junos);
|
||||
my @v_keys = keys %versions;
|
||||
|
||||
my $first = undef;
|
||||
|
||||
my @base_mismatch = ();
|
||||
my %mismatches = ();
|
||||
|
||||
foreach my $k (@v_keys) {
|
||||
my $base = $versions{$k}->{'base'};
|
||||
my $other = $versions{$k}->{'other'};
|
||||
|
||||
foreach my $o (keys %$other) {
|
||||
if ($other->{$o} ne $base) {
|
||||
$mismatches{$k}->{$base} = 1;
|
||||
$mismatches{$k}->{$other->{$o}} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$first = shift @v_keys;
|
||||
$first = $versions{$first};
|
||||
foreach my $k (@v_keys) {
|
||||
if ($first->{'base'} ne $versions{$k}->{'base'}) {
|
||||
push @base_mismatch, $k;
|
||||
}
|
||||
}
|
||||
|
||||
if (scalar @base_mismatch) {
|
||||
my @first_match = grep {
|
||||
$versions{$_}->{'base'} eq $first->{'base'}
|
||||
} keys %versions;
|
||||
my %mismatches = ();
|
||||
|
||||
foreach my $m (@base_mismatch) {
|
||||
push @{$mismatches{$versions{$m}->{'base'}}}, $m;
|
||||
}
|
||||
|
||||
$plugin->add_message(CRITICAL, "version mismatch detected: "
|
||||
. $first->{'base'} . " @ ("
|
||||
. join(", ", @first_match) . ") != "
|
||||
. join(" != ", map {
|
||||
$_ . " @ (" . join(", ", @{$mismatches{$_}}) . ")"
|
||||
} keys %mismatches));
|
||||
}
|
||||
elsif (scalar(keys %mismatches)) {
|
||||
$plugin->add_message(WARNING, "version mismatches detected: "
|
||||
. join(" / ", map {
|
||||
"$_: " . join(" != ", keys %{$mismatches{$_}})
|
||||
} keys %mismatches));
|
||||
}
|
||||
else {
|
||||
$plugin->add_message(OK, "all members at version "
|
||||
. $first->{'base'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# add total numbers to perfdata to ease graphing stuff
|
||||
if ($have_vc_members) {
|
||||
$plugin->add_perfdata(
|
||||
label => 'members',
|
||||
value => scalar(@vc_members),
|
||||
min => 0,
|
||||
max => undef,
|
||||
uom => '',
|
||||
threshold => undef,
|
||||
);
|
||||
}
|
||||
|
||||
my ($code, $msg) = $plugin->check_messages(join => ', ');
|
||||
|
||||
$junos->disconnect();
|
||||
|
||||
$plugin->nagios_exit($code, $msg);
|
||||
|
||||
sub send_query
|
||||
{
|
||||
my $device = shift;
|
||||
my $query = shift;
|
||||
my $queryargs = shift;
|
||||
|
||||
my $res;
|
||||
my $err;
|
||||
|
||||
verbose(3, "Sending query '$query' to router.");
|
||||
|
||||
if (ref $queryargs) {
|
||||
$res = $device->$query(%$queryargs);
|
||||
} else {
|
||||
$res = $device->$query();
|
||||
}
|
||||
|
||||
verbose(5, "Got response: " . Dumper(\$res));
|
||||
|
||||
if (! ref $res) {
|
||||
return "ERROR: Failed to execute query '$query'";
|
||||
}
|
||||
|
||||
$err = $res->getFirstError();
|
||||
if ($err) {
|
||||
return "ERROR: " . $err->{message};
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub send_command
|
||||
{
|
||||
my $device = shift;
|
||||
my $cmd = shift;
|
||||
|
||||
my $res;
|
||||
my $err;
|
||||
|
||||
verbose(3, "Sending command '$cmd' to router.");
|
||||
|
||||
$res = $device->command($cmd);
|
||||
|
||||
if (! ref $res) {
|
||||
return "ERROR: Failed to execute command '$cmd'";
|
||||
}
|
||||
|
||||
$err = $res->getFirstError();
|
||||
if ($err) {
|
||||
return "ERROR: " . $err->{message};
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub get_vc_information
|
||||
{
|
||||
my $device = shift;
|
||||
|
||||
my $cmd = "show virtual-chassis status";
|
||||
my $res = send_command($device, $cmd);
|
||||
my $err;
|
||||
|
||||
if (! ref $res) {
|
||||
return $res;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub get_vc_members
|
||||
{
|
||||
my $device = shift;
|
||||
|
||||
if ($have_vc_members) {
|
||||
return @vc_members;
|
||||
}
|
||||
|
||||
$vc = get_vc_information($device);
|
||||
if (! ref $vc) {
|
||||
$plugin->die($vc);
|
||||
}
|
||||
my $vc_id = ($vc->getElementsByTagName('virtual-chassis-id-information'))[0];
|
||||
$vc_id = ($vc_id->getElementsByTagName('virtual-chassis-id'))[0];
|
||||
$vc_id = $vc_id->getFirstChild->getNodeValue;
|
||||
|
||||
verbose(3, "Analyzing data from virtual chassis $vc_id.");
|
||||
|
||||
@vc_members = ($vc->getElementsByTagName('member-list'))[0]->getElementsByTagName('member');
|
||||
if ($conf{'verbose'} >= 3) {
|
||||
my @m = map { get_member_id($_) . " => " . get_member_serial($_) }
|
||||
@vc_members;
|
||||
verbose(3, "Members: " . join(", ", @m));
|
||||
}
|
||||
$have_vc_members = 1;
|
||||
return @vc_members;
|
||||
}
|
||||
|
||||
sub get_vc_interfaces
|
||||
{
|
||||
my $device = shift;
|
||||
my @ifaces = ();
|
||||
|
||||
my $cmd = "get_interface_information";
|
||||
my %args = ( interface_name => 'vcp*' );
|
||||
my $res = send_query($device, $cmd, \%args);
|
||||
|
||||
if (! ref $res) {
|
||||
$plugin->die($res);
|
||||
}
|
||||
|
||||
@ifaces = $res->getElementsByTagName('physical-interface');
|
||||
if ($conf{'verbose'} >= 3) {
|
||||
my @i = map { get_iface_name($_) . " => " . get_iface_status($_) }
|
||||
@ifaces;
|
||||
verbose(3, "VCP Interfaces: " . join(", ", @i));
|
||||
}
|
||||
return @ifaces;
|
||||
}
|
||||
|
||||
sub get_versions
|
||||
{
|
||||
my $device = shift;
|
||||
my %versions = ();
|
||||
|
||||
my $cmd = "show version";
|
||||
my $res = send_command($device, $cmd);
|
||||
|
||||
my @v = ();
|
||||
|
||||
if (! ref $res) {
|
||||
$plugin->die($res);
|
||||
}
|
||||
|
||||
@v = $res->getElementsByTagName('multi-routing-engine-item');
|
||||
|
||||
foreach my $i (@v) {
|
||||
my $name = get_obj_element($i, 're-name');
|
||||
my @infos = $i->getElementsByTagName('software-information');
|
||||
|
||||
@infos = $infos[0]->getElementsByTagName('package-information');
|
||||
|
||||
foreach my $j (@infos) {
|
||||
my $comment = get_obj_element($j, 'comment');
|
||||
my ($desc, $version);
|
||||
|
||||
$comment =~ m/^(.*) \[([^]]+)\]$/;
|
||||
$desc = $1;
|
||||
$version = $2;
|
||||
|
||||
if ($desc eq "JUNOS Base OS boot") {
|
||||
$versions{$name}->{'base'} = $version;
|
||||
}
|
||||
else {
|
||||
$versions{$name}->{'other'}->{$desc} = $version;
|
||||
}
|
||||
}
|
||||
}
|
||||
return %versions;
|
||||
}
|
||||
|
||||
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 '<prompt>') ? '<prompt>' : '<hidden>'));
|
||||
}
|
||||
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 => 'members_count',
|
||||
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 get_obj_element
|
||||
{
|
||||
my $obj = shift;
|
||||
my $elem = shift;
|
||||
|
||||
$elem = $obj->getElementsByTagName($elem);
|
||||
return $elem->item(0)->getFirstChild->getNodeValue;
|
||||
}
|
||||
|
||||
sub get_member_id
|
||||
{
|
||||
my $member = shift;
|
||||
return get_obj_element($member, 'member-id');
|
||||
}
|
||||
|
||||
sub get_member_serial
|
||||
{
|
||||
my $member = shift;
|
||||
return get_obj_element($member, 'member-serial-number');
|
||||
}
|
||||
|
||||
sub get_member_status
|
||||
{
|
||||
my $member = shift;
|
||||
return get_obj_element($member, 'member-status');
|
||||
}
|
||||
|
||||
sub get_member_role
|
||||
{
|
||||
my $member = shift;
|
||||
my $elem;
|
||||
|
||||
$elem = $member->getElementsByTagName('member-role');
|
||||
if ($elem && $elem->item(0)) {
|
||||
$elem = $elem->item(0)->getFirstChild->getNodeValue;
|
||||
# e.g., '*' may be appended to the member-role
|
||||
$elem =~ s/\W//g;
|
||||
return $elem;
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
sub get_iface_name
|
||||
{
|
||||
my $iface = shift;
|
||||
return get_obj_element($iface, 'name');
|
||||
}
|
||||
|
||||
sub get_iface_status
|
||||
{
|
||||
my $iface = shift;
|
||||
return get_obj_element($iface, 'oper-status');
|
||||
}
|
||||
|
||||
sub verbose
|
||||
{
|
||||
my $level = shift;
|
||||
my @msgs = @_;
|
||||
|
||||
if ($level > $conf{'verbose'}) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach my $msg (@msgs) {
|
||||
print "V$level: $msg\n";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,262 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
#####################################################################
|
||||
# (c) 2012 Sebastian "tokkee" Harl <sh@teamix.net> #
|
||||
# and team(ix) GmbH, Nuernberg, Germany #
|
||||
# #
|
||||
# This file is part of "team(ix) Monitoring Plugins" #
|
||||
# URL: http://oss.teamix.org/projects/monitoringplugins/ #
|
||||
# #
|
||||
# This file is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published #
|
||||
# by the Free Software Foundation, either version 2 of the License, #
|
||||
# or (at your option) any later version. #
|
||||
# #
|
||||
# This file is distributed in the hope that it will be useful, but #
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this file. If not, see <http://www.gnu.org/licenses/>. #
|
||||
#####################################################################
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use utf8;
|
||||
|
||||
use FindBin qw( $Bin );
|
||||
use lib "$Bin/perl/lib";
|
||||
use Nagios::Plugin::NetApp;
|
||||
|
||||
binmode STDOUT, ":utf8";
|
||||
|
||||
my $plugin = Nagios::Plugin::NetApp->new(
|
||||
plugin => 'check_naf_exports',
|
||||
shortname => 'check_naf_exports',
|
||||
version => '0.1',
|
||||
url => 'http://oss.teamix.org/projects/monitoringplugins',
|
||||
blurb => 'Monitor NetApp™ NFS exports.',
|
||||
usage =>
|
||||
"Usage: %s [-v|--verbose] [-H <host>] [-p <port>] [-t <timeout]
|
||||
[-U <user>] [-P <password] check-tuple [...]",
|
||||
license =>
|
||||
"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 License, either version 2 or (at your option) any later version
|
||||
(see http://opensource.org/licenses/GPL-2.0).",
|
||||
extra => "
|
||||
This plugin connects to a NetApp™ filer and checks NFS exports related issues.
|
||||
|
||||
A check-tuple consists of the name of the check and, optionally, a \"target\"
|
||||
which more closely specifies which characteristics should be checked, and
|
||||
warning and critical thresholds:
|
||||
checkname[,target[,warning[,critical]]]
|
||||
|
||||
The following checks are available:
|
||||
* exportfs_consistent: Check if /etc/exports is consistent with the currently
|
||||
exported filessytems.
|
||||
|
||||
Warning and critical thresholds may be specified in the format documented at
|
||||
http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT.",
|
||||
);
|
||||
|
||||
my %checks = (
|
||||
exportfs_consistent => \&check_exportfs_consistent,
|
||||
);
|
||||
|
||||
my $srv = undef;
|
||||
|
||||
$plugin->add_common_args();
|
||||
|
||||
foreach my $check (keys %checks) {
|
||||
$plugin->add_check_impl($check, $checks{$check});
|
||||
}
|
||||
|
||||
$plugin->set_default_check('exportfs_consistent');
|
||||
|
||||
# configure removes any options from @ARGV
|
||||
$plugin->configure();
|
||||
$plugin->set_checks(@ARGV);
|
||||
$srv = $plugin->connect();
|
||||
|
||||
$plugin->run_checks();
|
||||
|
||||
my ($code, $msg) = $plugin->check_messages(join => ', ');
|
||||
|
||||
$plugin->nagios_exit($code, $msg);
|
||||
|
||||
sub get_exports_rules
|
||||
{
|
||||
my $srv = shift;
|
||||
my $persistent = shift;
|
||||
|
||||
my $res = undef;
|
||||
|
||||
$res = $srv->invoke('nfs-exportfs-list-rules', 'persistent', $persistent);
|
||||
$plugin->die_on_error($res, "Failed to read exports information");
|
||||
|
||||
if (! $res->child_get('rules')) {
|
||||
return ();
|
||||
}
|
||||
|
||||
my %exports = ();
|
||||
foreach my $rule ($res->child_get('rules')->children_get()) {
|
||||
my %rule;
|
||||
my $tmp;
|
||||
|
||||
foreach my $info (qw( actual-pathname anon nosuid pathname )) {
|
||||
$tmp = $rule->child_get_string($info);
|
||||
if (defined($tmp)) {
|
||||
$rule{$info} = $tmp;
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $info (qw( read-only read-write root )) {
|
||||
my %info = ();
|
||||
|
||||
if (! $rule->child_get($info)) {
|
||||
next;
|
||||
}
|
||||
|
||||
foreach my $host_info ($rule->child_get($info)->children_get()) {
|
||||
my $host;
|
||||
|
||||
my $all_hosts = $host_info->child_get_string('all-hosts');
|
||||
my $negate = $host_info->child_get_string('negate');
|
||||
|
||||
$all_hosts ||= "false";
|
||||
$negate ||= "false";
|
||||
|
||||
if ($all_hosts eq 'true') {
|
||||
$host = '*';
|
||||
}
|
||||
else {
|
||||
$host = $host_info->child_get_string('name');
|
||||
}
|
||||
|
||||
if ($negate eq 'true') {
|
||||
$info{$host} = 1;
|
||||
}
|
||||
else {
|
||||
$info{$host} = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$rule{$info} = \%info;
|
||||
}
|
||||
|
||||
if ($rule->child_get('sec-flavor')) {
|
||||
my %sec_flavors = map {
|
||||
$_->child_get_string('flavor') => 1
|
||||
} $rule->child_get('sec-flavor')->children_get();
|
||||
$rule{'sec-flavor'} = \%sec_flavors;
|
||||
}
|
||||
|
||||
$exports{$rule{'pathname'}} = \%rule;
|
||||
}
|
||||
|
||||
return %exports;
|
||||
}
|
||||
|
||||
sub check_exportfs_consistent
|
||||
{
|
||||
my $plugin = shift;
|
||||
my $srv = shift;
|
||||
my @targets = @_;
|
||||
|
||||
my %exports = get_exports_rules($srv, 'true');
|
||||
my %memory = get_exports_rules($srv, 'false');
|
||||
|
||||
# diff export rules
|
||||
foreach my $path (keys %memory) {
|
||||
if (! defined($exports{$path})) {
|
||||
$plugin->add_message(CRITICAL,
|
||||
"$path not exported in /etc/exports");
|
||||
next;
|
||||
}
|
||||
|
||||
my %export = %{$exports{$path}};
|
||||
my %mem = %{$memory{$path}};
|
||||
|
||||
foreach my $info (qw( actual-pathname anon nosuid pathname )) {
|
||||
my $e = $export{$info};
|
||||
my $m = $mem{$info};
|
||||
|
||||
if ((! defined($e)) && (! defined($m))) {
|
||||
next;
|
||||
}
|
||||
|
||||
$e ||= "<empty>";
|
||||
$m ||= "<empty>";
|
||||
|
||||
if ($e ne $m) {
|
||||
$plugin->add_message(CRITICAL, "$path: $info differ "
|
||||
. "(exports: $e, exportfs: $m)");
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $info (qw( read-only read-write root )) {
|
||||
my $e = $export{$info};
|
||||
my $m = $mem{$info};
|
||||
|
||||
if ((! defined($e)) && (! defined($m))) {
|
||||
next;
|
||||
}
|
||||
|
||||
foreach my $host (keys %$m) {
|
||||
if (! defined($e->{$host})) {
|
||||
$plugin->add_message(CRITICAL, "$path: "
|
||||
. "$host does not have $info access in /etc/exports");
|
||||
next;
|
||||
}
|
||||
|
||||
if ($m->{$host} != $e->{$host}) {
|
||||
$plugin->add_message(CRITICAL, "$path: "
|
||||
. "$host is negated in "
|
||||
. ($m->{$host} ? "/etc/exports" : "exportfs info"));
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $host (keys %$e) {
|
||||
if (! defined($m->{$host})) {
|
||||
$plugin->add_message(CRITICAL, "$path: "
|
||||
. "$host does not have $info access in exportfs info");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $e = $export{'sec-flavor'};
|
||||
my $m = $mem{'sec-flavor'};
|
||||
|
||||
if (! ((! defined($e)) && (! defined($m)))) {
|
||||
$e ||= {};
|
||||
$m ||= {};
|
||||
|
||||
foreach my $flavor (keys %$m) {
|
||||
if (! defined($e->{$flavor})) {
|
||||
$plugin->add_message(CRITICAL, "$path: "
|
||||
. "security flavor $flavor is not specified "
|
||||
. "in /etc/exports");
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $flavor (keys %$e) {
|
||||
if (! defined($m->{$flavor})) {
|
||||
$plugin->add_message(CRITICAL, "$path: "
|
||||
. "security flavor $flavor is not specified "
|
||||
. "in exportfs info");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $path (keys %exports) {
|
||||
if (! defined($memory{$path})) {
|
||||
$plugin->add_message(CRITICAL,
|
||||
"$path not exported (according to 'exportfs')");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,540 +0,0 @@
|
|||
#############################################################################
|
||||
# (c) 2011-2012 Sebastian "tokkee" Harl <sh@teamix.net> #
|
||||
# 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 Carp;
|
||||
|
||||
use Data::Dumper;
|
||||
|
||||
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 add_common_args
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
my @args = (
|
||||
{
|
||||
spec => 'host|H=s',
|
||||
usage => '-H, --host=HOSTNAME',
|
||||
desc => 'Hostname/IP of Juniper box to connect to',
|
||||
default => 'localhost',
|
||||
},
|
||||
{
|
||||
spec => 'port|p=i',
|
||||
usage => '-p, --port=PORT',
|
||||
desc => 'Port to connect to',
|
||||
default => 22,
|
||||
},
|
||||
{
|
||||
spec => 'user|U=s',
|
||||
usage => '-U, --user=USERNAME',
|
||||
desc => 'Username to log into box as',
|
||||
default => 'root',
|
||||
},
|
||||
{
|
||||
spec => 'password|P=s',
|
||||
usage => '-P, --password=PASSWORD',
|
||||
desc => 'Password for login username',
|
||||
default => '<prompt>',
|
||||
},
|
||||
);
|
||||
|
||||
foreach my $arg (@args) {
|
||||
$self->add_arg($arg);
|
||||
}
|
||||
}
|
||||
|
||||
sub add_check_impl
|
||||
{
|
||||
my $self = shift;
|
||||
my $name = shift;
|
||||
my $sub = shift;
|
||||
|
||||
if ((! $name) || (! $sub) || (ref($sub) ne "CODE")) {
|
||||
carp "Invalid check specification: $name -> $sub";
|
||||
return;
|
||||
}
|
||||
|
||||
if (! defined($self->{'check_impls'})) {
|
||||
$self->{'check_impls'} = {};
|
||||
}
|
||||
|
||||
$self->{'check_impls'}->{$name} = $sub;
|
||||
}
|
||||
|
||||
sub get_check_impl
|
||||
{
|
||||
my $self = shift;
|
||||
my $name = shift;
|
||||
|
||||
if (! defined($self->{'check_impls'}->{$name})) {
|
||||
return;
|
||||
}
|
||||
return $self->{'check_impls'}->{$name};
|
||||
}
|
||||
|
||||
sub is_valid_check
|
||||
{
|
||||
my $self = shift;
|
||||
my $name = shift;
|
||||
|
||||
if (defined $self->{'check_impls'}->{$name}) {
|
||||
return 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub set_default_check
|
||||
{
|
||||
my $self = shift;
|
||||
my $def = shift;
|
||||
|
||||
if (! $self->is_valid_check($def)) {
|
||||
carp "set_default_check: Check '$def' does not exist";
|
||||
return;
|
||||
}
|
||||
|
||||
$self->{'default_check'} = $def;
|
||||
}
|
||||
|
||||
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 '<prompt>') ? '<prompt>' : '<hidden>'));
|
||||
}
|
||||
else {
|
||||
$self->verbose(3, "conf: $name => $value");
|
||||
}
|
||||
return ($name => $value);
|
||||
}
|
||||
|
||||
sub _add_single_check
|
||||
{
|
||||
my $self = shift;
|
||||
my @check = split(m/,/, shift);
|
||||
|
||||
my %c = ();
|
||||
|
||||
if (! $self->is_valid_check($check[0])) {
|
||||
return "ERROR: invalid check '$check[0]'";
|
||||
}
|
||||
|
||||
$c{'name'} = $check[0];
|
||||
|
||||
$c{'target'} = undef;
|
||||
if (defined($check[1])) {
|
||||
my @tmp = split(m/(\+|\~)/, $check[1]);
|
||||
|
||||
$c{'target'} = [];
|
||||
$c{'exclude'} = [];
|
||||
|
||||
for (my $i = 0; $i < scalar(@tmp); ++$i) {
|
||||
my $t = $tmp[$i];
|
||||
|
||||
if ((($t ne "+") && ($t ne "~")) || ($i == $#tmp)) {
|
||||
push @{$c{'target'}}, $t;
|
||||
next;
|
||||
}
|
||||
|
||||
++$i;
|
||||
|
||||
if ($t eq "+") {
|
||||
push @{$c{'target'}}, $tmp[$i];
|
||||
}
|
||||
else {
|
||||
push @{$c{'exclude'}}, $tmp[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$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 set_checks
|
||||
{
|
||||
my $self = shift;
|
||||
my @checks = @_;
|
||||
|
||||
my $err_str = "ERROR:";
|
||||
|
||||
if (! defined($self->{'conf'}->{'timeout'})) {
|
||||
croak "No timeout set -- did you call configure()?";
|
||||
}
|
||||
|
||||
if (scalar(@checks) == 0) {
|
||||
if ($self->{'default_check'}) {
|
||||
$self->{'conf'}->{'checks'}->[0] = {
|
||||
name => $self->{'default_check'},
|
||||
target => [],
|
||||
exclude => [],
|
||||
warning => undef,
|
||||
critical => undef,
|
||||
};
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
$self->{'conf'}->{'checks'} = [];
|
||||
|
||||
foreach my $check (@checks) {
|
||||
my $e;
|
||||
|
||||
$e = $self->_add_single_check($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 ((! $host) || (! $user)) {
|
||||
croak "Host and/or user not set -- did you call configure()?";
|
||||
}
|
||||
|
||||
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'} = <STDIN>;
|
||||
chomp($self->{'conf'}->{'password'});
|
||||
|
||||
$term->setlflag($lflag | POSIX::ECHO);
|
||||
$term->setattr(fileno(STDIN), TCSAFLUSH);
|
||||
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 run_checks
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
foreach my $check ($self->get_checks()) {
|
||||
my $targets = [];
|
||||
my $exclude = [];
|
||||
|
||||
if (defined $check->{'target'}) {
|
||||
$targets = $check->{'target'};
|
||||
}
|
||||
|
||||
if (defined $check->{'exclude'}) {
|
||||
$exclude = $check->{'exclude'};
|
||||
}
|
||||
|
||||
$self->set_thresholds(
|
||||
warning => $check->{'warning'},
|
||||
critical => $check->{'critical'},
|
||||
);
|
||||
|
||||
my $sub = $self->get_check_impl($check->{'name'});
|
||||
$sub->($self, $self->{'junos'}, $targets, $exclude);
|
||||
}
|
||||
}
|
||||
|
||||
sub send_query
|
||||
{
|
||||
my $self = shift;
|
||||
my $query = shift;
|
||||
my $queryargs = shift || {};
|
||||
|
||||
my $res;
|
||||
my $err;
|
||||
|
||||
$self->verbose(3, "Sending query '$query' "
|
||||
. join(", ", map { "$_ => $queryargs->{$_}" } keys %$queryargs)
|
||||
. " to router.");
|
||||
|
||||
if (scalar(keys %$queryargs)) {
|
||||
$res = $self->{'junos'}->$query(%$queryargs);
|
||||
} else {
|
||||
eval {
|
||||
$res = $self->{'junos'}->$query();
|
||||
};
|
||||
if ($@) {
|
||||
$res = $self->{'junos'}->command($query);
|
||||
}
|
||||
}
|
||||
|
||||
$self->verbose(5, "Got response: " . Dumper(\$res));
|
||||
|
||||
if (! ref $res) {
|
||||
return "ERROR: Failed to execute query '$query'";
|
||||
}
|
||||
|
||||
$err = $res->getFirstError();
|
||||
if ($err) {
|
||||
return "ERROR: " . $err->{message};
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub get_query_object
|
||||
{
|
||||
my $self = shift;
|
||||
my $res = shift;
|
||||
my $spec = shift;
|
||||
|
||||
if (! $res) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $spec) {
|
||||
return $res;
|
||||
}
|
||||
|
||||
if (! ref($spec)) {
|
||||
$spec = [ $spec ];
|
||||
}
|
||||
|
||||
my $iter = $res;
|
||||
for (my $i = 0; $i < scalar(@$spec) - 1; ++$i) {
|
||||
my $tmp = $iter->getElementsByTagName($spec->[$i]);
|
||||
|
||||
if ((! $tmp) || (! $tmp->item(0))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$iter = $tmp->item(0);
|
||||
}
|
||||
|
||||
if (wantarray) {
|
||||
my @ret = $iter->getElementsByTagName($spec->[scalar(@$spec) - 1]);
|
||||
return @ret;
|
||||
}
|
||||
else {
|
||||
my $ret = $iter->getElementsByTagName($spec->[scalar(@$spec) - 1]);
|
||||
if ((! $ret) || (! $ret->item(0))) {
|
||||
return;
|
||||
}
|
||||
return $ret->item(0);
|
||||
}
|
||||
}
|
||||
|
||||
sub get_query_object_value
|
||||
{
|
||||
my $self = shift;
|
||||
my $res = $self->get_query_object(@_);
|
||||
|
||||
if (! $res) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ref($res) eq "XML::DOM::NodeList") {
|
||||
$res = $res->item(0);
|
||||
}
|
||||
|
||||
return $res->getFirstChild->getNodeValue;
|
||||
}
|
||||
|
||||
sub nagios_exit
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
if ($self->{'junos'}) {
|
||||
$self->{'junos'}->disconnect();
|
||||
}
|
||||
$self->SUPER::nagios_exit(@_);
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -1,453 +0,0 @@
|
|||
#############################################################################
|
||||
# (c) 2011-2012 Sebastian "tokkee" Harl <sh@teamix.net> #
|
||||
# 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::NetApp;
|
||||
|
||||
use Carp;
|
||||
|
||||
use POSIX qw( :termios_h );
|
||||
|
||||
use Nagios::Plugin;
|
||||
use NaServer;
|
||||
|
||||
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->{'srv'} = 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 add_common_args
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
my @args = (
|
||||
{
|
||||
spec => 'host|H=s',
|
||||
usage => '-H, --host=HOSTNAME',
|
||||
desc => 'Hostname/IP of NetApp filer to connect to',
|
||||
default => 'localhost',
|
||||
},
|
||||
{
|
||||
spec => 'port|p=i',
|
||||
usage => '-p, --port=PORT',
|
||||
desc => 'Port to connect to',
|
||||
},
|
||||
{
|
||||
spec => 'user|U=s',
|
||||
usage => '-U, --user=USERNAME',
|
||||
desc => 'Username to log into box as',
|
||||
default => 'root',
|
||||
},
|
||||
{
|
||||
spec => 'password|P=s',
|
||||
usage => '-P, --password=PASSWORD',
|
||||
desc => 'Password for login username',
|
||||
default => '<prompt>',
|
||||
},
|
||||
{
|
||||
spec => 'transport|t=s',
|
||||
usage => '-t, --transport=TRANSPORT',
|
||||
desc => 'Transport for the connection',
|
||||
default => 'HTTP',
|
||||
},
|
||||
);
|
||||
|
||||
foreach my $arg (@args) {
|
||||
$self->add_arg($arg);
|
||||
}
|
||||
}
|
||||
|
||||
sub add_check_impl
|
||||
{
|
||||
my $self = shift;
|
||||
my $name = shift;
|
||||
my $sub = shift;
|
||||
|
||||
if ((! $name) || (! $sub) || (ref($sub) ne "CODE")) {
|
||||
carp "Invalid check specification: $name -> $sub";
|
||||
return;
|
||||
}
|
||||
|
||||
if (! defined($self->{'check_impls'})) {
|
||||
$self->{'check_impls'} = {};
|
||||
}
|
||||
|
||||
$self->{'check_impls'}->{$name} = $sub;
|
||||
}
|
||||
|
||||
sub get_check_impl
|
||||
{
|
||||
my $self = shift;
|
||||
my $name = shift;
|
||||
|
||||
if (! defined($self->{'check_impls'}->{$name})) {
|
||||
return;
|
||||
}
|
||||
return $self->{'check_impls'}->{$name};
|
||||
}
|
||||
|
||||
sub is_valid_check
|
||||
{
|
||||
my $self = shift;
|
||||
my $name = shift;
|
||||
|
||||
if (defined $self->{'check_impls'}->{$name}) {
|
||||
return 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub set_default_check
|
||||
{
|
||||
my $self = shift;
|
||||
my $def = shift;
|
||||
|
||||
if (! $self->is_valid_check($def)) {
|
||||
carp "set_default_check: Check '$def' does not exist";
|
||||
return;
|
||||
}
|
||||
|
||||
$self->{'default_check'} = $def;
|
||||
}
|
||||
|
||||
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 '<prompt>') ? '<prompt>' : '<hidden>'));
|
||||
}
|
||||
else {
|
||||
$self->verbose(3, "conf: $name => $value");
|
||||
}
|
||||
return ($name => $value);
|
||||
}
|
||||
|
||||
sub _add_single_check
|
||||
{
|
||||
my $self = shift;
|
||||
my @check = split(m/,/, shift);
|
||||
|
||||
my %c = ();
|
||||
|
||||
if (! $self->is_valid_check($check[0])) {
|
||||
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 set_checks
|
||||
{
|
||||
my $self = shift;
|
||||
my @checks = @_;
|
||||
|
||||
my $err_str = "ERROR:";
|
||||
|
||||
if (! defined($self->{'conf'}->{'timeout'})) {
|
||||
croak "No timeout set -- did you call configure()?";
|
||||
}
|
||||
|
||||
if (scalar(@checks) == 0) {
|
||||
if ($self->{'default_check'}) {
|
||||
$self->{'conf'}->{'checks'}->[0] = {
|
||||
name => $self->{'default_check'},
|
||||
target => [],
|
||||
warning => undef,
|
||||
critical => undef,
|
||||
};
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
$self->{'conf'}->{'checks'} = [];
|
||||
|
||||
foreach my $check (@checks) {
|
||||
my $e;
|
||||
|
||||
$e = $self->_add_single_check($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'};
|
||||
|
||||
my $srv;
|
||||
|
||||
if ((! $host) || (! $user)) {
|
||||
croak "Host and/or user not set -- did you call configure()?";
|
||||
}
|
||||
|
||||
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'} = <STDIN>;
|
||||
chomp($self->{'conf'}->{'password'});
|
||||
|
||||
$term->setlflag($lflag | POSIX::ECHO);
|
||||
$term->setattr(fileno(STDIN), TCSAFLUSH);
|
||||
print "\n";
|
||||
}
|
||||
|
||||
$self->verbose(1, "Connecting to host $host as user $user.");
|
||||
$srv = new NaServer($host, 1, 7);
|
||||
if (! $srv) {
|
||||
$self->die("ERROR: failed to connect to $host!");
|
||||
}
|
||||
|
||||
$srv->set_admin_user($user, $self->{'conf'}->{'password'});
|
||||
$srv->set_transport_type($self->{'conf'}->{'transport'});
|
||||
|
||||
if ($self->{'conf'}->{'port'}) {
|
||||
$srv->set_port($self->{'conf'}->{'port'});
|
||||
}
|
||||
|
||||
$srv->set_timeout($self->{'conf'}->{'timeout'});
|
||||
|
||||
$self->{'srv'} = $srv;
|
||||
return $srv;
|
||||
}
|
||||
|
||||
sub run_checks
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
foreach my $check ($self->get_checks()) {
|
||||
my @targets = ();
|
||||
|
||||
if (defined $check->{'target'}) {
|
||||
@targets = @{$check->{'target'}};
|
||||
}
|
||||
|
||||
$self->set_thresholds(
|
||||
warning => $check->{'warning'},
|
||||
critical => $check->{'critical'},
|
||||
);
|
||||
|
||||
my $sub = $self->get_check_impl($check->{'name'});
|
||||
$sub->($self, $self->{'srv'}, @targets);
|
||||
}
|
||||
}
|
||||
|
||||
sub get_error
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
my $res = shift;
|
||||
my $msg = shift;
|
||||
|
||||
if (! defined($res)) {
|
||||
return "$msg: Unknown error";
|
||||
}
|
||||
elsif ((ref($res) eq "NaElement") && (($res->results_errno != 0)
|
||||
|| ($res->results_status() eq "failed"))) {
|
||||
return "$msg: " . $res->results_reason();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sub die_on_error
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
if ($self->get_error(@_)) {
|
||||
$self->die($self->get_error(@_));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub nagios_exit
|
||||
{
|
||||
my $self = shift;
|
||||
|
||||
if ($self->{'srv'}) {
|
||||
$self->{'srv'} = undef;
|
||||
}
|
||||
$self->SUPER::nagios_exit(@_);
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
#! /usr/bin/perl
|
||||
|
||||
#####################################################################
|
||||
# (c) 2012 Sebastian "tokkee" Harl <sh@teamix.net> #
|
||||
# and team(ix) GmbH, Nuernberg, Germany #
|
||||
# #
|
||||
# This file is part of "team(ix) Monitoring Plugins" #
|
||||
# URL: http://oss.teamix.org/projects/monitoringplugins/ #
|
||||
# #
|
||||
# This file is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published #
|
||||
# by the Free Software Foundation, either version 2 of the License, #
|
||||
# or (at your option) any later version. #
|
||||
# #
|
||||
# This file is distributed in the hope that it will be useful, but #
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this file. If not, see <http://www.gnu.org/licenses/>. #
|
||||
#####################################################################
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $p_script = shift;
|
||||
my @p_args = @ARGV;
|
||||
|
||||
if (! $p_script) {
|
||||
exit_usage(1);
|
||||
}
|
||||
|
||||
my $p_stdout;
|
||||
my $p_pid = open($p_stdout, '-|', "$p_script @p_args 2>&1");
|
||||
if (! defined($p_pid)) {
|
||||
print "CRITICAL: Failed to execute plugin ($p_script): $!\n";
|
||||
print "Commandline: $p_script @p_args\n";
|
||||
exit 2;
|
||||
}
|
||||
|
||||
my @p_output = <$p_stdout>;
|
||||
if (waitpid($p_pid, 0) != $p_pid) {
|
||||
print "UNKNOWN: Lost track of plugin process\n";
|
||||
exit 3;
|
||||
}
|
||||
my $p_rc = $?;
|
||||
my $p_sig = $p_rc & 127;
|
||||
my $p_cd = $p_rc & 128; # core dumped?
|
||||
$p_rc >>= 8;
|
||||
close $p_stdout;
|
||||
|
||||
if ($p_sig || $p_cd) {
|
||||
print "CRITICAL: Plugin died with signal $p_sig (exit code: $p_rc)"
|
||||
. ($p_cd ? " (core dumped)" : "") . "\n";
|
||||
$p_rc = 2;
|
||||
}
|
||||
|
||||
if ($p_rc == 255) {
|
||||
print "CRITICAL: Plugin died with status 255 "
|
||||
. "(see details for more info)\n";
|
||||
print "Commandline: $p_script @p_args\n";
|
||||
}
|
||||
|
||||
my $p_output = join('', @p_output);
|
||||
print $p_output;
|
||||
|
||||
if (($p_rc < 0) || ($p_rc > 3)) {
|
||||
$p_rc = 3;
|
||||
}
|
||||
|
||||
exit $p_rc;
|
||||
|
||||
sub exit_usage {
|
||||
my $status = shift || 0;
|
||||
print STDERR "Usage: $0 <plugin> [<args>]\n";
|
||||
exit $status;
|
||||
}
|
||||
|
|
@ -1,239 +0,0 @@
|
|||
#! /usr/bin/perl -w
|
||||
|
||||
#############################################################################
|
||||
# (c) 2001, 2003 Juniper Networks, Inc. #
|
||||
# (c) 2011 Sebastian "tokkee" Harl <sh@teamix.net> #
|
||||
# and team(ix) GmbH, Nuernberg, Germany #
|
||||
# #
|
||||
# This file is part of "team(ix) Monitoring Plugins" #
|
||||
# URL: http://oss.teamix.org/projects/monitoringplugins/ #
|
||||
# It is based on the example diagnose_bgp.pl script of the #
|
||||
# JUNOScript distribution. #
|
||||
# #
|
||||
# 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. #
|
||||
#############################################################################
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use JUNOS::Device;
|
||||
use JUNOS::Trace;
|
||||
use Getopt::Std;
|
||||
use Term::ReadKey;
|
||||
use File::Basename;
|
||||
|
||||
use Data::Dumper;
|
||||
|
||||
my $jnx;
|
||||
|
||||
# send a query
|
||||
sub send_query
|
||||
{
|
||||
my $device = shift;
|
||||
my $query = shift;
|
||||
my $href_queryargs = shift;
|
||||
my $res;
|
||||
unless ( ref $href_queryargs ) {
|
||||
eval {
|
||||
$res = $device->$query();
|
||||
};
|
||||
if ($@) {
|
||||
$res = $device->command($query);
|
||||
}
|
||||
} else {
|
||||
my %queryargs = %$href_queryargs;
|
||||
print "$_ => $queryargs{$_}\n" foreach (keys %queryargs);
|
||||
$res = $device->$query(%queryargs);
|
||||
}
|
||||
|
||||
unless ( ref $res ) {
|
||||
print STDERR "ERROR: Failed to execute query '$query'\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
unless (ref $res) {
|
||||
print STDERR "ERROR: failed to execute command $query\n";
|
||||
return undef;
|
||||
}
|
||||
|
||||
my $err = $res->getFirstError();
|
||||
if ($err) {
|
||||
print STDERR "ERROR: ", $err->{message}, "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
# get object identified by the specified spec
|
||||
sub get_object_by_spec
|
||||
{
|
||||
my $res = shift;
|
||||
my $spec = shift;
|
||||
|
||||
my $iter = $res;
|
||||
for (my $i = 0; $i < scalar(@$spec) - 1; ++$i) {
|
||||
my $tmp = $iter->getElementsByTagName($spec->[$i]);
|
||||
|
||||
if ((! $tmp) || (! $tmp->item(0))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$iter = $tmp->item(0);
|
||||
}
|
||||
|
||||
if (wantarray) {
|
||||
my @ret = $iter->getElementsByTagName($spec->[scalar(@$spec) - 1]);
|
||||
return @ret;
|
||||
}
|
||||
else {
|
||||
my $ret = $iter->getElementsByTagName($spec->[scalar(@$spec) - 1]);
|
||||
if ((! $ret) || (! $ret->item(0))) {
|
||||
return;
|
||||
}
|
||||
return $ret->item(0);
|
||||
}
|
||||
}
|
||||
|
||||
# print the usage of this script
|
||||
sub output_usage
|
||||
{
|
||||
my $usage = "Usage: $0 [options] <target> <query> [<arg1>=<value1> [...]]
|
||||
|
||||
Where:
|
||||
|
||||
<target> The hostname of the target router.
|
||||
<query> The query to send to the target router.
|
||||
|
||||
Options:
|
||||
|
||||
-l <login> A login name accepted by the target router.
|
||||
-p <password> The password for the login name.
|
||||
-m <access> Access method. It can be clear-text, ssl, ssh or telnet. Default: telnet.
|
||||
-s <spec> Specify a value to extract from the output.
|
||||
-o <file> Output file. Default: dump.xml.
|
||||
-d Turn on debug, full blast.\n\n";
|
||||
|
||||
die $usage;
|
||||
}
|
||||
|
||||
my %opt;
|
||||
getopts('l:p:dm:x:o:s:h', \%opt) || output_usage();
|
||||
output_usage() if $opt{h};
|
||||
|
||||
# Check whether trace should be turned on
|
||||
JUNOS::Trace::init(1) if $opt{d};
|
||||
|
||||
my $hostname = shift || output_usage();
|
||||
my $query = shift || output_usage();
|
||||
|
||||
my %args = map { split m/=/, $_ } @ARGV;
|
||||
if ($opt{d}) {
|
||||
print "Args:\n";
|
||||
foreach my $key (keys %args) {
|
||||
print "\t$key => $args{$key}\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Retrieve the access method, can only be telnet or ssh.
|
||||
my $access = $opt{m} || "telnet";
|
||||
use constant VALID_ACCESSES => "telnet|ssh|clear-text|ssl";
|
||||
output_usage() unless (VALID_ACCESSES =~ /$access/);
|
||||
|
||||
# Check whether login name has been entered. Otherwise prompt for it
|
||||
my $login = "";
|
||||
if ($opt{l}) {
|
||||
$login = $opt{l};
|
||||
} else {
|
||||
print "login: ";
|
||||
$login = ReadLine 0;
|
||||
chomp $login;
|
||||
}
|
||||
|
||||
# Check whether password has been entered. Otherwise prompt for it
|
||||
my $password = "";
|
||||
if ($opt{p}) {
|
||||
$password = $opt{p};
|
||||
} else {
|
||||
print "password: ";
|
||||
ReadMode 'noecho';
|
||||
$password = ReadLine 0;
|
||||
chomp $password;
|
||||
ReadMode 'normal';
|
||||
print "\n";
|
||||
}
|
||||
|
||||
# Get the name of the output file
|
||||
my $outfile = $opt{o} || 'dump.xml';
|
||||
|
||||
# Retrieve command line arguments
|
||||
my %deviceinfo = (
|
||||
access => $access,
|
||||
login => $login,
|
||||
password => $password,
|
||||
hostname => $hostname,
|
||||
'ssh-compress' => 0,
|
||||
);
|
||||
|
||||
#
|
||||
# CONNECT TO the JUNOScript server
|
||||
# Create a device object that contains all necessary information to
|
||||
# connect to the JUNOScript server at a specific router.
|
||||
#
|
||||
|
||||
$jnx = new JUNOS::Device(%deviceinfo);
|
||||
unless ( ref $jnx ) {
|
||||
die "ERROR: $deviceinfo{hostname}: failed to connect.\n";
|
||||
}
|
||||
|
||||
my $spec = $opt{s} || undef;
|
||||
if ($spec) {
|
||||
$spec = [ split(",", $spec) ];
|
||||
}
|
||||
|
||||
my $res = send_query($jnx, $query, scalar(keys %args) ? \%args : undef);
|
||||
while ($res) {
|
||||
if ($spec) {
|
||||
$res = get_object_by_spec($res, $spec);
|
||||
}
|
||||
|
||||
if (! $res) {
|
||||
print "NO OUTPUT!\n";
|
||||
last;
|
||||
}
|
||||
|
||||
if ($outfile eq "-") {
|
||||
print STDOUT $res->toString;
|
||||
}
|
||||
else {
|
||||
$res->printToFile($outfile);
|
||||
}
|
||||
last;
|
||||
}
|
||||
|
||||
$jnx->request_end_session();
|
||||
$jnx->disconnect();
|
||||
|
Loading…
Reference in a new issue