#!/usr/bin/perl -w
# $Id: check_munin.pl
# 2007/05/20 01:40:47
#
# check_munin.pl Copyright (C) 2003 Julien Rottenberg <julien@rottenberg.info>
#
# check_munin.pl can check various modules of a server running munin-node.
#
#
# 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

# Globals
$PROGNAME = "check_munin.pl";

my $host 		=  	undef;  # hostname
my $port 		= 	4949;   # port
my $module	= 	undef;  # module to load
my $timeout = 	5;      # basic timeout


my $DEBUG = 0;

my $REVISION = "0.8";


use strict;
use Getopt::Long;
use vars qw($opt_V $opt_h $opt_w $opt_c $opt_H $opt_P $opt_M $PROGNAME);
use lib "/usr/lib/nagios/plugins" ;
#use lib "./" ;
use utils qw(%ERRORS &print_revision &support &usage);
use IO::Socket::INET;



$ENV{'PATH'}='';
$ENV{'BASH_ENV'}='';
$ENV{'ENV'}='';

Getopt::Long::Configure('bundling');
GetOptions
       ("V"   => \$opt_V, "version"    => \$opt_V,
        "h"   => \$opt_h, "help"       => \$opt_h,
        "w=s" => \$opt_w, "warning=s"  => \$opt_w,
        "c=s" => \$opt_c, "critical=s" => \$opt_c,
        "H=s" => \$opt_H, "hostname=s" => \$opt_H,
        "P=s" => \$opt_P, "port=s"     => \$opt_P,
        "M=s" => \$opt_M, "module=s"   => \$opt_M);


##
##
## Functions
##
##

sub check_options {
	 # Basic checks
	 if ($opt_V) {
			 print_revision($PROGNAME,'$Revision: '.$REVISION.' $');
			 exit $ERRORS{'UNKNOWN'};
	 }

	 if ($opt_h) {
			 print_help ();
			 print_revision($PROGNAME,'$Revision: '.$REVISION. ' $');
			 exit $ERRORS{'UNKNOWN'};
	 }


   # Check Hostname and module
   if ( ! defined($opt_H) || ! defined($opt_M) )
       {
					 print "Hostname and module are requested !\n";
					 print_usage();
					 exit $ERRORS{"UNKNOWN"}
       }
   else {
   					$host = $opt_H if (utils::is_hostname($opt_H));
   					($host) || usage("Invalid hostname or address : $opt_H\n");
   					$module = $opt_M;
   			}

  # Check warnings and critical
   if (!defined($opt_w) || !defined($opt_c))
       {
               print "put warning and critical info !\n";
               print_usage();
               exit $ERRORS{"UNKNOWN"}
       }

  # Check Port number
   if (defined($opt_P))
       {
       		if ($opt_P !~ m/^[0-9]*$/)
       		{
						print "Port must be a number ! (4949 is the default)\n";
						print_usage();
						exit $ERRORS{"UNKNOWN"}
					}
					else  {$port = $opt_P;}
				}
}


sub print_usage () {
   print "Usage: $0  -H <host> -M <Module> [-P <port>] -w <warn level> -c <crit level> [-V]\n";
}


sub print_help () {
  print "\nMonitor remote server via Munin-node agent\n";
  print_usage();
  print <<EOT;
-h, --help
       print this help message
-H, --hostname=HOST
       name or IP address of host to check
-M, --module=MUNIN MODULE
       Munin module value to fetch
-P, --port=PORT
       Munin-node port (Default 4949)
-w, --warn=INTEGER
       warning level
-c, --crit=INTEGER
       critical level
-t, --timeout=INTEGER
       timeout for Munin-node connection in seconds (Default: 5)
-V, --version
       prints version number
EOT
}

sub in ($$);
$ENV{'BASH_ENV'}='';
$ENV{'ENV'}='';
$ENV{'PATH'}='';
$ENV{'LC_ALL'}='C';


##
##
## Let's go !
##
##

# check if everything is ok
check_options();

# Connect to host
my ($munin,$error);
my %data;
my $status = "0"; # status is ok by default, 1 = warning, 2 = critical
my ($object_name, $object_value);
my $response_text = '';

$munin = IO::Socket::INET->new("PeerAddr" => $host, "PeerPort" => $port, "Timeout" => $timeout);
if (!defined($munin)) {
	printf("ERROR opening session on munin-node, try yourself : telnet $host $port\n", $error);
  exit $ERRORS{"UNKNOWN"};
}

$munin->autoflush(1);		# This is the default but for version >= 1.18

# Call the module to load
print $munin "fetch $module\n";

# get data and parse it
# all munin plugins call have to
# 	- start with a prompt : # munin node at $hostname
#		- end with a line only containg a dot
my $line = "";
while ($line !~ m/^\./) {
       $line = $munin -> getline();
       print $line if $DEBUG;
       if (($line !~ /^\#\ munin\ node\ at\ /) && ($line !~ /^\.$/) )  # Get rid of the first line : "# munin node at HOSTNAME" and of the last one (.)
       {
					 my @couple = split(' ', $line);
					 $couple[0] = sanitize($couple[0]);			# Let's have a nicer output
					 print "$couple[0]\n" if $DEBUG;				# Some lines from Munin are not useful var_run for module df for example
					 if ($couple[0])
					 {
									$couple[0] =~ s/\.value//;      # We don't need that part anyway
									$data{$couple[0]} = $couple[1];
									print "|$couple[0] - $couple[1]|\n" if $DEBUG;
					 }
       }
}

if (($data{'#'}) &&  ($data{'#'} eq 'Unknown'))		# if module does not exist munin-node reports : # Unknown service
{   print "ERROR No such module : $module on munin-node $host.\n";
   exit $ERRORS{"UNKNOWN"};
}

print $munin "quit\n";						# Let's get out of here
close $munin;											# Close the connection nicely


# check data
# Structure %data : name ==> value

foreach my $name ( keys %data) {
       my $value = $data{$name};
       print "$name $value\n" if $DEBUG;
       if (($value> $opt_w) && ($status ne 2))
       {
               $status = "1";
               $object_name = $name;
               $object_value = $value;
       }
       if ( $value > $opt_c)
       {
               $status = "2";
               $object_name = $name;
               $object_value = $value;
       }
       $response_text = $name.": ".$value." ".$response_text ;         # this one will print all objects munin see
       print "$response_text\n" if $DEBUG;
}



if ($status eq 1)
{
       print "$object_name value, $object_value, is above warning treshold $opt_w\n";
       $status = $ERRORS{"WARNING"};

}
elsif ($status eq 2)
{
       print "$object_name value, $object_value,  is above critical treshold $opt_c\n";
       $status = $ERRORS{"CRITICAL"};

}
else
{
       print "$response_text  \n";
       $status = $ERRORS{"OK"};
}

exit $status;



sub sanitize {
	my $var = shift;
	if ($opt_M eq "df")
	{
						if (($var !~ m/dev/ ) || ($var =~ m/udev/) || ($var =~ m/shm/))	# Get rid of non physical drives
						{
							$var = undef;
						}
						else
						{
							$var =~ s/\_/\//g;
						}
	}
	return $var;


}

