1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
#!/usr/bin/perl
#
# Monitor snmp processes
#
# Arguments are:
#
# [options] --host hostname interface [interface ...]
#
# This script will exit with value 1 if the named interface ('Serial0'
# or 'Serial1.2' or whatever on a Cisco) on the specified host is down.
# The summary output line will be the host names that failed
# and the name of the port.
#
# Since interface names are looked up by their Cisco interface name,
# you don't need to worry about SNMP indices getting renumbered when
# interfaces are added or deleted. A local cache of the interface
# names is maintained in the mon state directory.
#
# Copyright (C) 1998, Brian Moore <bem@cmc.net>
#
# This program 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 program 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 program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# modified July 2000 by Ed Ravin <eravin@panix.com>
# * added cache for ifDescr names so we don't need to dump the router's
# table every time (very slow for routers with lots of interfaces)
# * switched to long-name options
# * added timeout option
# * created --host option for host so hostname can go at end
# sample entry in mon.cf:
#
# watch main-router.yourcompany.com
# service interfaces
# description SNMP status of router
# interval 5m
# monitor snmp_interface.mon Serial0/1 Serial0/3 --host
# bugs: unlike most mon scripts, this one only accepts one hostname,
# and there's no easy way for the script to detect that you're calling
# it improperly. Caveat emptor.
use strict;
use SNMP;
use DB_File;
use Getopt::Long;
# let's not load the whole mib....
$ENV{'MIBS'} = '';
# just fake what we want
my $ifDescr = '.1.3.6.1.2.1.2.2.1.2';
my $ifOperStatus = '.1.3.6.1.2.1.2.2.1.8';
my $ifAdminStatus = '.1.3.6.1.2.1.2.2.1.7';
my $ifXEntry = '.1.3.6.1.2.1.31.1.1.1.18';
my %opt;
my @failures= ();
my %ifindices;
my ($community, $timeout, $dir, $file, $statefile, $maxindex, $debug, $host);
my $myname="snmp_interface.mon";
my $usage="usage: $myname [--community=xxx] [--timeout=sec] [--dir=statefile-dir] [--statefile=statefile-basename] [--maxindex=nn] --host <hostname> ifname ...\n";
GetOptions(\%opt, "community=s", "timeout=i", "dir=s", "statefile=s", "maxindex=i", "host=s", "debug");
$host = $opt{'host'} || die $usage;
$community = $opt{'community'} || 'public';
$timeout= ($opt{'timeout'} || 5) * 1000 * 1000;
$dir= $ENV{"MON_STATEDIR"} || $opt{'dir'} || "/usr/lib/mon/state.d";
$file= $opt{'statefile'} || "$host.interfaces.state";
$maxindex= $opt{'maxindex'} || 0;
$debug= $opt{'debug'} || 0;
$statefile= $dir . "/" . $file;
tie %ifindices, "DB_File", $statefile, O_RDWR|O_CREAT, 0644, $DB_HASH
or die "$myname: cannot tie to $statefile: $!\n";
$SNMP::use_long_names = 1;
my $session = new SNMP::Session(DestHost => $host,
Community => $community,
Timeout => $timeout) ||
die "$host (cannot initialize SNMP session)\n";
foreach my $interface (@ARGV) {
if (defined($ifindices{$interface})) # already in cache?
{ # yes, see if the index still matches
my $var = new SNMP::Varbind([$ifDescr, $ifindices{$interface}]);
my $desc= $session->get($var);
if ( ($session->{ErrorNum}) || ($interface !~ /^$desc/i) ) {
print "removing stale entry: $interface\n" if $debug;
delete $ifindices{$interface}; # cache no good, try again.
}
}
# if the interface name is not already cached, rebuild the cache
if (!defined($ifindices{$interface})) {
my $var = new SNMP::Varbind([$ifDescr, 0]);
print "name $interface not in cache, rebuilding..." if $debug;
%ifindices= {};
while (1) { # search for the interface name, caching along the way
my $desc = $session->getnext($var);
last if ( $var->[$SNMP::Varbind::tag_f] !~ /^$ifDescr/ );
# no response is bad community or dead daemon or other failure...
if ( $session->{ErrorNum} ) {
push @failures, "$host $session->{ErrorStr}";
last;
}
my ($part_number) = ($var->[$SNMP::Varbind::tag_f] =~ /\.(\d+)$/);
print "adding $desc to cache as index $part_number\n" if $debug;
$ifindices{$desc}= $part_number; # cache this entry
last if $maxindex and $part_number >= $maxindex;
}
}
if (defined($ifindices{$interface})) {
my $state= $session->get([$ifOperStatus, $ifindices{$interface}]);
my $admin= $session->get([$ifAdminStatus, $ifindices{$interface}]);
if ( $state ne 1 and $admin eq 1) {
my $description = $session->get([$ifXEntry, $ifindices{$interface}]);
push (@failures, "$host $interface $description");
}
} else {
push (@failures, "$host $interface cannot find matching ifDescr");
}
}
if (@failures) {
print join (", ", @failures), "\n";
exit 1;
}
exit 0;
|