#!/usr/bin/perl
#
# Monitor diskspace via SNMP
# (based on process.monitor by Brian Moore)
#
# Originally written by SATOH Fumiyasu <fumiya@samba.gr.jp>.
# Modified Oct 2001 by Dan Urist <durist@world.std.com>
#   Changes: added usage, SNMP v.3 support, -T threshold option and
#            unique-ified errors
#
# Usage:
#       [-h]                # Usage
#       [-t Timeout]        # Timeout in ms (default: 1000000)
#       [-r Retries]        # Retries before failure (default: 5)
#       [-v SNMPversion]    # 1,2,2c or 3 (default: 1)
#       [-c Community]      # For SNMP v.1,2,2c (default: public)
#       [-u snmpuser]       # For SNMP v.3 (default: initial)
#       [-l seclevel]       # For SNMP v.3 (default: noAuthNoPriv)
#       [-A authpassphrase] # For SNMP v.3 
#       [-T threshold]      # If a disk threshold is given, the script
#                           # will exit with the value of the highest
#                           # disk percentage found that is over the
#                           # threshold; if no disks are over the threshold
#                           # it will exit with value 0, and it will exit
#                           # with value 2 for SNMP error
#       host [host ...]
#
#
# This script will exit with value 1 if host:community has dskErrorFlag
# set.  The summary output line will be the host names that failed
# and the disk information.  The detail lines are what UCD snmp returns
# for an dskErrMessage. ('/filesystem: less than WATERMARK free (= CURRENT)').
# If there is an SNMP error (either a problem with the SNMP libraries,
# or a problem communicating via SNMP with the destination host),
# this script will exit with a warning value of 2.
#
# If the -T threshold option is used, the script will exit with the
# highest disk percentage found that is over the threshold. The intent
# is to allow use with mon's "alert exit=value" parameter to allow for
# finer-grained alerts based on disk usage.  If no disks are over the
# threshold, the script will exit with value 0; if an SNMP error
# occurs (and there are no other errors), the script will exit with
# value 2.
#
#    Copyright (C) 2001 SATOH Fumiyasu <fumiya@samba.gr.jp>
#
#    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::Std;

$ENV{'MIBS'} = "UCD-SNMP-MIB";

getopts("ht:r:v:c:u:l:A:T:");
my $VERSION = "0.1";
if( $opt_h || (scalar @ARGV == 0) ){
  print <<"USAGE";
$0 Version $VERSION; original version by SATOH Fumiyasu <fumiya@samba.gr.jp>,
SNMP v.3 support by Daniel J. Urist <durist\@world.std.com>.

Usage:
      [-h]                # Usage
      [-t Timeout]        # Timeout in ms (default: 1000000)
      [-r Retries]        # Retries before failure (default: 5)
      [-v SNMPversion]    # 1,2,2c or 3 (default: 1)
      [-c Community]      # For SNMP v.1,2,2c (default: public)
      [-u snmpuser]       # For SNMP v.3 (default: initial)
      [-l seclevel]       # For SNMP v.3 (default: noAuthNoPriv)
      [-A authpassphrase] # For SNMP v.3 
      [-T threshold]      # If a disk threshold is given, the script will exit
                          # with the value of the highest disk percentage found
                          # that is over the threshold; if no disks are over the
                          # the threshold it will exit with value 0, and it will
                          # exit with value 2 for SNMP error
      host [host ...]

USAGE
  exit;
}

# FIXME we should probably offer all the v3 options that the SNMP module does
my $Timeout = $opt_t || 1000000;
my $Retries = $opt_r || 5;
my $SNMPVersion = $opt_v || 1;
my $Community = $opt_c || 'public';
my $SecName = $opt_u || 'initial'; 
my $SecLevel = $opt_l || 'noAuthNoPriv';
my $Authpass = $opt_A || '';
my $Threshold = $opt_T if defined($opt_T);

my %SNMPARGS = (
		Timeout => $Timeout,
		Version => $SNMPVersion,
	       );

if ($SNMPVersion eq "3"){
  $SNMPARGS{SecName} = $SecName;
  $SNMPARGS{SecLevel} = $SecLevel;
  $SNMPARGS{AuthPass} = $Authpass;
}
else{
  $SNMPARGS{Community} = $Community;
}

my $RETVAL = 0;
my %Failures;
my %Longerr;
my $Session;

foreach $host (@ARGV) {
  $Session = new SNMP::Session(
			       DestHost => $host,
			       %SNMPARGS,
			      );
  unless( defined($Session) ) {
    $RETVAL = 2 if $RETVAL == 0; # Other errors take precedence over SNMP error
    $Failures{"$host session error"} = "";
    $Longerr{"$host could not get SNMP session"} = "";
    next;
  }

  my $v = new SNMP::Varbind (["dskIndex"]);
  $Session->getnext ($v);

  while (!$Session->{"ErrorStr"} && $v->tag eq "dskIndex") {
    my @q = $Session->get ([
			    ["dskPath", $v->iid],	# 0
			    ["dskDevice", $v->iid],	# 1
			    ["dskMinimum", $v->iid],	# 2
			    ["dskMinPercent", $v->iid],	# 3
			    ["dskTotal", $v->iid],	# 4
			    ["dskAvail", $v->iid],	# 5
			    ["dskUsed", $v->iid],	# 6
			    ["dskPercent", $v->iid],	# 7
			    ["dskPercentNode", $v->iid],# 8
			    ["dskErrorFlag", $v->iid],	# 9
			    ["dskErrorMsg", $v->iid],	# 10
			   ]);
    last if ($Session->{"ErrorStr"});

    if( defined $Threshold ){
      if($q[7] > $Threshold){
	$RETVAL = $q[7] if $q[7] > $RETVAL;
	my ($t, $u, $a) = map { int($_/1024) } @q[4, 6, 5];
	$Failures{$host} = "";
	$Longerr{"$host:$q[0]($q[1]) total=$t used=$u($q[7]%) free=$a threshold=$Threshold%"} = "";
      }
    }
    elsif ($q[9] > 0) {
      $RETVAL = 1;	    
      my ($t, $u, $a) = map { int($_/1024) } @q[4, 6, 5];
      $Failures{$host} = "";
      $Longerr{"$host:$q[0]($q[1]) total=$t used=$u($q[7]%) free=$a err=$q[10]"} = "";
    }

    $Session->getnext ($v);
  }
  
  if ($Session->{"ErrorStr"}) {
    $RETVAL = 2 if $RETVAL == 0; # Other errors take precedence over SNMP error
    $Failures{$host} = "";
    $Longerr{"$host returned an SNMP error: " . $Session->{"ErrorStr"}} = "";
  }
}

if (scalar keys %Failures) {
    print join (", ", sort keys %Failures), "\n", "\n";
    print join ("\n", sort keys %Longerr), "\n";
}

exit $RETVAL;

