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