I needed to be able to alarm if we ran out of inodes on our Netapps, so
I took the axe to netappfree.monitor. I've attached the new version.
Improvements are:
* inode checking - can check for exceeding NN%, or less than N
* complain if a non-existent filesystem is in config file. This
one hit me - we upgraded the Netapp and the name of the filesystem
changed on the new box, it turned out netappfree.monitor had silently
stopped testing it.
* improved error messages to list the thresholds and other info
* perl -w compliant.
* updated internal docs in program
-- Ed
#!/usr/bin/perl -w
#
# Use SNMP to get free disk space or inode status from a Network Appliance
#
# exit values:
# 1 - free space or inodes on any host dropped below the supplied parameter
# 2 - network or SNMP error (SNMP library error, no response from server)
# 3 - config error - (filesystem in config file does not exist on filer)
# USAGE
# [--community=<SNMP COMMUNITY>] [--timeout=<seconds>]
# [--config=/path/to/configfile] [--list] host1 host2 ...
# EXAMPLES
# --list option will dump current status from requested hosts:
# netappfree.monitor --list filer1 filer2 filer3
# sample output:
# filer ONTAP filesystem KB total KB avail Inode%
# ----------------------------------------------------------------------------
# filer1 6.1.2R3 /vol/vol0/ 61092616 6773416 86
# filer1 6.1.2R3 /vol/vol0/.snaps 2545524 1260240 0
# sample invocation in mon.cf, with local MIB directory for the Netapp MIB
# NETWORK-APPLIANCE-MIB.txt (copy from /etc/mib/netapp.mib on filer):
# service freespace
# description test freespace and inodes on Netapp filers
# depend SELF:ping
# MIBDIRS=/usr/local/share/snmp/mibs
# interval 7m
# monitor netappfree.monitor
# CONFIG FILE FORMAT
#
# Run "netappfree --list host1 host2 ..." first to get list of filesystems
# and whether inodes are properly reported. If you don't want to monitor
# inodes for a particular FS, leave tha column blank.
#
#
# host filesystem freespace [InodeThreshold]
# (in kb, gb, or mb) (in % or k)
#
# filer1 /vol/main/ 5gb 90%
# filer2 /vol/vol0/ 5gb 500k
#!/usr/bin/perl -w
#
# Use SNMP to get free disk space or inode status from a Network Appliance
#
# exit values:
# 1 - free space or inodes on any host dropped below the supplied parameter
# 2 - network or SNMP error (SNMP library error, no response from server)
# 3 - config error - (filesystem in config file does not exist on filer)
# USAGE
# [--community=<SNMP COMMUNITY>] [--timeout=<seconds>]
# [--config=/path/to/configfile] [--list] host1 host2 ...
# EXAMPLES
# --list option will dump current status from requested hosts:
# netappfree.monitor --list filer1 filer2 filer3
# sample output:
# filer ONTAP filesystem KB total KB avail Inode%
# ----------------------------------------------------------------------------
# filer1 6.1.2R3 /vol/vol0/ 61092616 6773416 86
# filer1 6.1.2R3 /vol/vol0/.snaps 2545524 1260240 0
# sample invocation in mon.cf, with local MIB directory for the Netapp MIB
# NETWORK-APPLIANCE-MIB.txt (copy from /etc/mib/netapp.mib on filer):
# service freespace
# description test freespace and inodes on Netapp filers
# depend SELF:ping
# MIBDIRS=/usr/local/share/snmp/mibs
# interval 7m
# monitor netappfree.monitor
# CONFIG FILE FORMAT
#
# Run "netappfree --list host1 host2 ..." first to get list of filesystems
# and whether inodes are properly reported. If you don't want to monitor
# inodes for a particular FS, leave tha column blank.
#
#
# host filesystem freespace [InodeThreshold]
# (in kb, gb, or mb) (in % or k)
#
# filer1 /vol/main/ 5gb 90%
# filer2 /vol/vol0/ 5gb 500k
#
# This requires the UCD SNMP library and G.S. Marzot's Perl SNMP
# module.
#
# Originally by Jim Trocki. Modified by Theo Van Dinter
# ([EMAIL PROTECTED], [EMAIL PROTECTED]) to add verbose error output,
# more error checking, etc. Can be used in conjunction with
# snapdelete.alert to auto-remove snapshots if needed.
# Modified December 2003 by Ed Ravin ([EMAIL PROTECTED]) to add inode
# checking, detect nonexistent filesystem in config file, pass perl -w
# checks, added more info to error messages for clarity, updated doc comments
# above.
# $Id: netappfree.monitor,v 1.2 2003/12/20 20:31:05 root Exp root $
#
# Copyright (C) 1998, Jim Trocki
# Copyright (C) 1999-2001, Theo Van Dinter
#
# 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
#
use SNMP;
use Getopt::Long;
sub list;
sub readcf;
sub toKB;
$ENV{"MIBS"} = 'RFC1213-MIB:NETWORK-APPLIANCE-MIB';
GetOptions (\%opt, "community=s", "timeout=i", "retries=i", "config=s", "list");
die "no host arguments\n" if (@ARGV == 0);
$RET = 0;
@ERRS = ();
%HOSTS = ();
$COMM = $opt{"community"} || "public";
$TIMEOUT = $opt{"timeout"} || 2; $TIMEOUT *= 1000 * 1000;
$RETRIES = $opt{"retries"} || 5;
$CONFIG = $opt{"config"} || (-d "/etc/mon" ? "/etc/mon" : "/usr/lib/mon/etc")
. "/netappfree.cf";
($dfIndex, $dfFileSys, $dfKBytesTotal, $dfKBytesAvail,
$dfInodesFree, $dfPerCentInodeCapacity) = (0..5);
list (@ARGV) if ($opt{"list"});
readcf ($CONFIG) || die "could not read config: $!\n";
foreach $host (@ARGV) {
next if (!defined $FREE{$host});
if (!defined($s = new SNMP::Session (DestHost => $host,
Timeout => $TIMEOUT, Community => $COMM,
Retries => $RETRIES))) {
$RET = ($RET == 1) ? 1 : 2;
$HOSTS{$host} ++;
push (@ERRS, "could not create session to $host: " . $SNMP::Session::ErrorStr);
next;
}
$v = new SNMP::VarList (
['dfIndex'],
['dfFileSys'],
['dfKBytesTotal'],
['dfKBytesAvail'],
['dfInodesFree'],
['dfPerCentInodeCapacity'],
);
if ( $v->[$dfIndex]->tag !~ /^df/ ) {
push(@ERRS,"OIDs not mapping correctly! Check that NetApp MIB is available!");
$RET = 1;
last;
}
while (defined $s->getnext($v)) {
last if ($v->[$dfIndex]->tag !~ /dfIndex/);
my $filesys= $v->[$dfFileSys]->val;
next unless exists($FREE{$host}{$filesys});
if ($v->[$dfKBytesAvail]->val < $FREE{$host}{$filesys}{'bytes'}) {
$HOSTS{$host} ++;
push (@ERRS, sprintf ("%1.1fGB free on %s:%s (threshold %1.1fGB, fs size
%1.1fGB)",
$v->[$dfKBytesAvail]->val / 1024 / 1024,
$host, $filesys,
$FREE{$host}{$filesys}{'bytes'} / 1024 / 1024,
$v->[$dfKBytesTotal]->val / 1024 / 1024)
);
$RET = 1;
}
# mark filesys entry as seen in filer's MIB
$FREE{$host}{$v->[$dfFileSys]->val}{'existsOnFiler'}= 1;
if (defined($FREE{$host}{$v->[$dfFileSys]->val}{'inode'})) {
my $inodefreewanted= $FREE{$host}{$v->[$dfFileSys]->val}{'inode'};
if (0 < $inodefreewanted and $inodefreewanted < 1) { # percentage?
if ($v->[$dfPerCentInodeCapacity]->val > $inodefreewanted *
100) { # percentage exceeded?
$HOSTS{$host} ++;
push (@ERRS, sprintf("%d%% inodes used on %s:%s, over
threshold of %d%%",
$v->[$dfPerCentInodeCapacity]->val,
$host, $v->[$dfFileSys]->val,
$inodefreewanted * 100
));
$RET = 1;
}
}
elsif ($v->[$dfInodesFree]->val < $inodefreewanted) {
$HOSTS{$host} ++;
push (@ERRS, sprintf("%1.1f inodes free on %s:%s,
below threshold of %1.1f",
$v->[$dfInodesFree]->val,
$host, $v->[$dfFileSys]->val,
$inodefreewanted
));
$RET = 1;
}
}
}
if ($s->{ErrorNum}) {
$HOSTS{$host} ++;
push (@ERRS, "could not get dfIndex for $host: " . $s->{ErrorStr});
$RET = ($RET == 1) ? 1 : 2;
}
}
foreach $host (@ARGV)
{
foreach $filesys (keys %{$FREE{$host}})
{
if (! $FREE{$host}{$filesys}{'existsOnFiler'} ) {
$HOSTS{$host} ++;
push (@ERRS, "filesystem $filesys does not exist on $host");
$RET = ($RET == 1) ? 1 : 3;
}
}
}
if ($RET) {
print join(" ", sort keys %HOSTS), "\n\n", join("\n", @ERRS), "\n";
}
exit $RET;
#
# read configuration file
#
sub readcf {
my ($f) = @_;
my ($l, $host, $filesys, $free, $inodefree);
open (CF, $f) || return undef;
while (<CF>) {
next if (/^\s*#/ || /^\s*$/);
chomp;
($host, $filesys, $free, $inodefree) = split;
if (!defined ($FREE{$host}{$filesys}{'bytes'} = toKB ($free))) {
die "error free specification, config $f, line $.\n";
}
if (!defined ($FREE{$host}{$filesys}{'inode'} = toIN ($inodefree))) {
# allow this to be optional for compatibility
# die "error inodefree specification, config $f, line $.\n";
}
$FREE{$host}{$filesys}{'existsOnFiler'}= 0;
}
close (CF);
}
sub toKB {
my ($free) = @_;
my ($n, $u);
if ($free =~ /^(\d+\.\d+)(kb|mb|gb)$/i) {
($n, $u) = ($1, "\L$2");
} elsif ($free =~ /^(\d+)(kb|mb|gb)$/i) {
($n, $u) = ($1, "\L$2");
} else {
return undef;
}
return (int ($n * 1024)) if ($u eq "mb");
return (int ($n * 1024 * 1024)) if ($u eq "gb");
int ($n);
}
sub toIN {
my ($infree) [EMAIL PROTECTED];
return undef unless defined($infree);
if ($infree =~ /^(\d+\.?\d+)%$/) { # percentage
return $1 / 100;
}
if ($infree =~ /^(\d+\.?\d+)(k|kb)$/) { # kilos?
return $1 * 1024;
}
if ($infree =~ /^(\d+\.?\d+)$/) { # bare??
return $1;
}
return undef;
}
sub list {
my (@hosts) = @_;
foreach $host (@hosts) {
if (!defined($s = new SNMP::Session (DestHost => $host,
Timeout => $TIMEOUT,
Community => $COMM,
Retries => $RETRIES))) {
print STDERR "could not create session to $host: " .
$SNMP::Session::ErrorStr, "\n";
next;
}
$ver = $s->get(['sysDescr', 0]);
$ver =~ s/^netapp.*release\s*([^:]+):.*$/$1/i;
$v = new SNMP::VarList (
['dfIndex'],
['dfFileSys'],
['dfKBytesTotal'],
['dfKBytesAvail'],
['dfInodesFree'],
['dfPerCentInodeCapacity'],
);
while (defined $s->getnext($v)) {
last if ($v->[$dfIndex]->tag !~ /dfIndex/);
write;
}
}
exit 0;
}
format STDOUT_TOP =
filer ONTAP filesystem KB total KB avail Inode%
------------------------------------------------------------------------------
.
format STDOUT =
@<<<<<<<<<<<<<< @<<<<<<<<<< @<<<<<<<<<<<<<<< @>>>>>>>>>> @>>>>>>>>>> @>>
$host, $ver, $v->[1]->[2], $v->[2]->[2], $v->[3]->[2], $v->[5]->[2]
.
_______________________________________________
mon mailing list
[EMAIL PROTECTED]
http://linux.kernel.org/mailman/listinfo/mon