#!/usr/bin/perl -w

# check_esx_vdf.pl v0.02
#
# Copyright (C) 2011 Frederic Krueger <igetspam@bigfoot.com>
#
# 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 (or with Nagios);  if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA


# This script needs a running snmpd on the ESX server you use for checking
# with the following addition to its snmpd.conf:
#
#exec .1.3.6.1.4.1.6876.99999.1 vdf /usr/sbin/vdf
#
# A pnp4nagios template can be found at the end of this file!


# Usage examples:
#
#C:\Programme\NSClient++\scripts>perl check_esx_vdf.pl -H esxhostname -w 85 -c 90 -s vmfsvolumename -f
#OK - vmfs partition '/vmfs/volumes/vmfsvolumename' is at 737/999G (73% in use) | vmfs_vmfsvolumename=772988904;891065777;995868163;0;
#C:\Programme\NSClient++\scripts>
#
#C:\Programme\NSClient++\scripts>perl check_esx_vdf.pl -H esxhostname -w 85 -c 90 -s vmfsvolumename
#CRITICAL - vmfs partition '/vmfs/volumes/vmfsvolumename' is at 902/999G (90% in use)
#C:\Programme\NSClient++\scripts>





use Getopt::Long;


#
# configuration
#

our $BIN_SNMPWALK = "C:\\snmp\\snmpwalk.exe";	# default setup is to run on a (windows) vmware virtual center

#
# globals
#

our ($PROGNAME, $PROGVER) = ("check_esx_vdf.pl", "0.02");

our %ERRORS = ( 'DEPENDENT' => 4, 'UNKNOWN' => 3, 'OK' => 0, 'WARNING' => 1, 'CRITICAL' => 2 );

our $hostname = "";
our $community = "public";
our $oid = ".1.3.6.1.4.1.6876.99999.1.101";
our $warn = 80;
our $crit = 90;
our $search = "/vmfs/volumes/";
our $exactmatch = 0;
our $show_perfdata = 0;
our $verbose = 0;

our %perfhash = ();

#
# subs
#

sub getvdfinfo($$)
{
  my ($host, $comm) = @_;
  my %h = ();
  return ()	if ((!defined($host)) or (!defined($comm)));

  my $cmd = "$BIN_SNMPWALK -v 1 -c $comm $host $oid";

  if (open (VDF, "$cmd |"))
  {
    while (defined (my $inp = <VDF>))
    {
      chomp ($inp);
      if ($inp =~ /^SNMPv2-SMI::enterprises\.6876\.99999\.1\.101\.(\d+) \= STRING: "([^"]+)"$/i)
      {
        my $lineno = $1;   $lineout = $2;

        if ($lineout =~ m#^\s+(\d+) +(\d+) +(\d+) +(\d+)\% +(/vmfs/volumes/.*)$#i)	# 768432 = 768M	# vdf-default
        {
           if (!defined($h{$5}))
           {
             $h{$5}{'size'} = $1;
             $h{$5}{'used'} = $2;
             $h{$5}{'avail'} = $3;
             $h{$5}{'useperc'} = $4;
             $h{$5}{'name'} = $5;
           }
           else
           {
              print "XXX Got dupe entry for name '$5'.. Call the admin!\n";
           }
        }

        else
        {
        }
      }
    } # end foreach line
    close(VDF);
  } # end if open succeeded

  return ( { %h } );
} # end sub getvdfinfo




sub get_output
{
  my $warnlevel = shift;
  my $critlevel = shift;
  my $dref = shift;
  my %data = ();
  %data = %{$dref}	if ((defined($dref)) and (ref($dref) eq "HASH"));
  return ("UNKNOWN", "Invalid data gotten from vdf")	if (!defined($data{'size'}));

  if ($show_perfdata)
  {
    if (!defined($perfhash{$data{'name'}}))
    {
      $perfhash{$data{'name'}} = { %data };
    }
  }

  my $unit = "M"; my $unitfac = 1024;
  if ($data{'size'} > 1024*1024) { $unit = "G"; $unitfac = 1024*1024; }
  if ($data{'size'} > 1024*1024*1024) { $unit = "T"; $unitfac = 1024*1024*1024; }

  my ($rc, $msg) = ("OK", sprintf ("vmfs partition '%s' is at %i/%i%s (%i%% in use)", $data{'name'}, ($data{'used'}/$unitfac), ($data{'size'}/$unitfac), $unit, $data{'useperc'}));

  if ($data{'useperc'} >= $warnlevel)
  {
    $rc = "WARNING";
  }
  if ($data{'useperc'} >= $critlevel)
  {
    $rc = "CRITICAL";
  }

  return ($rc, $msg);
} # end sub get_output



sub check_options {

  our ($o_verbose, $o_help, $o_version, $o_crit, $o_warn, $o_perfdata, $o_search, $o_exactmatch, $o_community, $o_hostname) = (undef, undef, undef, undef, undef, undef, undef, undef, undef, undef);

  Getopt::Long::Configure ("bundling");
  GetOptions(
    'H:s'   => \$o_hostname,    'hostname:s'	=> \$o_hostname,
    'C:s'   => \$o_community,   'community:s'	=> \$o_community,
    'c:s'   => \$o_crit,        'critical:s'	=> \$o_crit,
    'w:s'   => \$o_warn,        'warning:s'	=> \$o_warn,
    's:s'   => \$o_search,      'search:s'	=> \$o_search,
    'e'     => \$o_exactmatch,  'exactmatch'	=> \$o_exactmatch,
    'f'     => \$o_perfdata,    'perfdata'	=> \$o_perfdata,
    'v'     => \$o_verbose,	'verbose'	=> \$o_verbose,
    'V'     => \$o_version,     'version'       => \$o_version,
    'h'     => \$o_help,	'help'		=> \$o_help
  );

  if (defined($o_help)) { help(); exit ($ERRORS{"UNKNOWN"}); }

  if (defined($o_version)) { print_version(); exit ($ERRORS{"UNKNOWN"}); }


  if ((!defined($o_hostname)) or ($o_hostname eq ""))
  {
    help ("Please specify the hostname of the snmp v1 host!");
    exit ($ERRORS{"UNKNOWN"});
  }
  $hostname = $o_hostname;

  if ((defined($o_community)) and ($o_community ne ""))
  {
    $community = $o_community;
  }

  if ( (!defined($o_warn)) or (!defined($o_crit)) )
  {
    help ("You must specify a warning and a critical threshold (in percent).");
    exit ($ERRORS{"UNKNOWN"});
  }
  if (    ( (defined($o_warn)) and ($o_warn !~ /^\d+$/) )
       or ( (defined($o_crit)) and ($o_crit !~ /^\d+$/) )
     )
  {
    help ("Warning or critical threshold must be a number (in percent).");
    exit ($ERRORS{"UNKNOWN"});
  }

  if (($o_warn < 0) or ($o_warn > 100))
  {
    help ("Your specified warning threshold \"$o_warn\" is wrong.");
    exit ($ERRORS{"UNKNOWN"});
  }
  $warn = $o_warn;

  if (($o_crit < 0) or ($o_crit > 100))
  {
    help ("Your specified critical threshold \"$o_crit\" is wrong.");
    exit ($ERRORS{"UNKNOWN"});
  }
  $crit = $o_crit;

  if ((defined($o_search)) and ($o_search ne ""))
  {
    $search = $o_search;
  }

  if (defined($o_exactmatch))
  {
    $exactmatch = ($o_exactmatch == 1 ? 1 : 0);
  }

  if (defined($o_perfdata))
  {
    $show_perfdata = ($o_perfdata == 1 ? 1 : 0);
  }

  if (defined($o_verbose))
  {
    $verbose = ($o_verbose == 1 ? 1 : 0);
  }

  if ($verbose)
  {
    print "check_options: hostname '$hostname', community '$community', crit '$crit', warn '$warn', search '$search', exactmatch '$exactmatch', showperfdata? " .($show_perfdata ? "yes":"no"). ", verbose? " .($verbose ? "yes":"no"). "\n";
  }
  
} # end sub check_options




sub help
{
  my $msg = shift;
  $msg = ""	if (!defined($msg));

  print "\nusage: $0 -H <hostname> [-C <community>] -w <warnpercent> -c <critpercent> [-s [searchstring]] [-e]\n";
  print "\n";
  print "  -H    --hostname    Hostname to check via snmp\n";
  print "  -C    --community   SNMP V1-Communitystring (default: public)\n";
  print "  -s    --search      searchstring for the vmfs volume name (first match wins!)\n";
  print "  -e    --exactmatch  Exact match only (ie. ^/vmfs/volumes/....\$)\n";
  print "  -w    --warn        Warning Level for vmfs diskspace\n";
  print "  -c    --crit        Critical Level for vmfs diskspace\n";
  print "  -f    --perfdata    Add perfdata to output\n";
  print "  -h    --help        Print this screen\n";
  print "  -v    --verbose     Verbose output\n";
  print "\n";
  print "If -s is not set, ALL volumes starting with /vmfs/volumes/ will be checked,\n";
  print "and warn/crit returncode will be shown if just one is below said threshold.\n";

  print "\nError: $msg\n\n"	if ((defined($msg)) and ($msg ne ""));
} # end sub help


sub print_version
{
  print "$PROGNAME $PROGVER\n";
  print "Copyright (C) 2011 Frederic Krueger <igetspam\@bigfoot.com>\n\n";
}



#
# main
#

my ($rc, $msg) = ("default", "Nuttin");

check_options();

my %vdftable = ();

if (($hostname ne "") and ($community ne "") and ($oid ne ""))
{
  my $href = getvdfinfo ($hostname, $community);
  if ((defined($href)) and (ref($href) eq "HASH"))
  {
    %vdftable = %{$href};
  }
  else
  {
    $rc  = "UNKNOWN";
    $msg = "Couldn't parse vdf info";
  }
}
else
{
  $rc = "UNKNOWN";
  $msg = "No usable data gotten";
}


# now use the info for output
if ($rc eq "default")
{
  if (! $exactmatch)
  {
    foreach my $vdfname (sort keys %vdftable)
    {
      if ($vdfname =~ /$search/i)
      {
        my %tmp = %{$vdftable{$vdfname}};
        ($rc, $msg) = get_output ($warn, $crit, \%tmp);
        last;
      }
    }

    if ($rc eq "default")	# if nothing matched
    {
      $rc = "UNKNOWN";
      $msg = "No such vmfs-partition '$search' (fuzzy matching)";
    }
  }
  else
  {
    if (defined($vdftable{$search}))
    {
      my %tmp = %{$vdftable{$search}};
      ($rc, $msg) = get_output ($warn, $crit, \%tmp);
    }
    else
    {
      $rc = "UNKNOWN";
      $msg = "No such vmfs partition '$search' (exact match) found.";
    }
  }
} # end if rc wasnt set yet (ie. no error occured yet)



# debug
#$rc = "OK";
#$msg = "message";

my $perfdata = "";
if ($show_perfdata)
{
  $perfdata = " | ";
  foreach my $entry (sort keys %perfhash)
  {
    my $showentry = lc($entry);	$showentry =~ s#^/vmfs/volumes/##g; $showentry =~ s/-/_/g;

    my %entryhash = %{$perfhash{$entry}};
    my $segment = "vmfs_" .$showentry. "=" .$entryhash{'used'}. ";" .int(($warn/100)*$entryhash{'size'}). ";" .int(($crit/100)*$entryhash{'size'}). ";0;";
    $perfdata .= "$segment ";
  }
}

# bye bye.
print "$rc - $msg$perfdata\n";
exit ($ERRORS{$rc});







#
# pnp4nagios template for check_esx_vdf.pl
#

# put the following in /usr/share/pnp/templates/check_esx_vdf.php
print <<EOF;
<?php

# Copyright (c) 2011 Frederic Krueger (igetspam@bigfoot.com)
# Template for check_esx_vdf.pl

\$opt[1] = "--vertical-label GB -l 0 -u 100 --title \\"Disk space usage for \$hostname / \$NAGIOS_SERVICEDESC\\" ";

\$def[1] = "DEF:used=\$rrdfile:\$DS[1]:AVERAGE ";

\$def[1] .= "CDEF:pu=used,1048576,/ ";
\$warnlevel = ceil(\$WARN[1]/1048576);
\$def[1] .= "CDEF:pw=used,used,/,\$warnlevel,* ";
\$critlevel = ceil(\$CRIT[1]/1048576);
\$def[1] .= "CDEF:pc=used,used,/,\$critlevel,* ";

\$def[1] .= "AREA:pu#AABBFF: ";
\$def[1] .= "LINE:pu#000000: ";
\$def[1] .= "LINE:pw#FFDD66: ";
\$def[1] .= "LINE:pc#FF0000: ";

\$def[1] .= "GPRINT:pu:LAST:\\"Currently used\\:  %6.2lf GB \\\\n\\" ";
\$def[1] .= "HRULE:\$WARN[1]#ffff00:\\"Warning on %6.2lf GB \\" ";
\$def[1] .= "HRULE:\$CRIT[1]#ff0000:\\"Critical on %6.2lf GB \\\\n\\" ";
\$def[1] .= "GPRINT:pw:AVERAGE:\\"Warning  level\\:  %6.2lf GB\\\\n\\" ";
\$def[1] .= "GPRINT:pc:AVERAGE:\\"Critical level\\:  %6.2lf GB\\\\n\\" ";

?>
EOF
