#!/usr/bin/perl
# Nagios
#
# $Id: munin-nagios.in,v 1.4.2.2 2004/06/08 15:26:04 jimmyo Exp $
#
# $Log: munin-nagios.in,v $
# Revision 1.4.2.2  2004/06/08 15:26:04  jimmyo
# The server programs now open the log file at an earlier point.
#
# Revision 1.4.2.1  2004/06/08 14:55:41  jimmyo
# Noise reduction in munin-update, when plugins say strange things (only log, don\'t complain on stderr).
#
# Revision 1.4  2004/01/29 17:40:10  jimmyo
# Fixed pod typos patched by Lupe Christoph (SF#884092)
#
# Revision 1.3  2004/01/29 17:34:06  jimmyo
# Updated copyright information
#
# Revision 1.2  2004/01/15 15:20:01  jimmyo
# Making things workable after name change. Upping for test verwion.
#
# Revision 1.1  2004/01/02 18:50:01  jimmyo
# Renamed occurrances of lrrd -> munin
#
# Revision 1.1.1.1  2004/01/02 15:18:08  jimmyo
# Import of LRRD CVS tree after renaming to Munin
#
# Revision 1.14  2003/12/10 11:59:39  jimmyo
# Enable/disable notifications at any level
#
# Revision 1.13  2003/11/12 12:28:37  jimmyo
# Only send ok-messages if lockfile was created.
#
# Revision 1.12  2003/11/12 12:19:29  jimmyo
# Fix typo
#
# Revision 1.11  2003/11/12 12:17:16  jimmyo
# Fix typo
#
# Revision 1.10  2003/11/12 12:07:46  jimmyo
# Off by one error. Cosmetic only.
#
# Revision 1.9  2003/11/12 12:04:45  jimmyo
# Make sure extinfo comes accross
#
# Revision 1.8  2003/11/10 16:09:00  jimmyo
# Be nice to Nagios - don't DOS it.
#
# Revision 1.7  2003/11/07 20:46:12  jimmyo
# Only require Config::General if using old config format.
#
# Revision 1.6  2003/11/07 19:00:17  jimmyo
# Put lockfiles in the right place
#
# Revision 1.5  2003/11/07 17:43:16  jimmyo
# Cleanups and log entries
#
#

use strict;
use Munin;

use POSIX qw(strftime);
use Getopt::Long;
my $VERSION="1.0.2";
my $DEBUG=0;
my $conffile = "/etc/munin/munin.conf";
my $do_usage = 0;
my @limit_hosts = ();
my @limit_services = ();
my $force_sendmsg = 0;
my $force_root = 0;
my $removeok = 0;
my $do_version = 0;

my $log = new IO::Handle;

# Get options
$do_usage=1  unless 
GetOptions ( "force!"       => \$force_sendmsg,
             "force-root!"  => \$force_root,
	     "host=s"       => \@limit_hosts,
	     "service=s"    => \@limit_services,
	     "config=s"     => \$conffile,
	     "debug!"       => \$DEBUG,
	     "version!"     => \$do_version,
	     "removeok"     => \$removeok,
	     "help"         => \$do_usage );

if ($do_usage)
{
    print "Usage: $0 [options]

Options:
    --[no]force		Force sending of messages even if you normally
    			wouldn't. [--noforce]
    --[no]force-root    Force running, even as root. [--noforce-root]
    --help		View this message.
    --version		View version information.
    --debug		View debug messages.
    --service <service>	Limit notified services to <service>. Multiple 
    			--service options may be supplied.
    --host <host>	Limit notified hosts to <host>. Multiple --host 
    			options may be supplied.
    --config <file>	Use <file> as configuration file. 
    			[/etc/munin/munin.conf]
    --removeok		Reset warning status (remove .ok-files).

";
    exit 0;
}

if ($do_version)
{
    print "munin-nagios version $VERSION.\n";
    print "Written by Audun Ytterdal, Jimmy Olsen, Tore Anderson / Linpro AS\n";
    print "\n";
    print "Copyright (C) 2002-2004\n";
    print "This is free software released under the GNU Public License. There is NO\n";
    print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n";
    exit 0;
}

if ($> == 0 and !$force_root)
{
    print "You are running this program as root, which is neither smart nor necessary.
If you really want to run it as root, use the --force-root option. Else, run
it as the user \"munin\". Aborting.\n\n";
    exit (1);
}

my $config = &munin_config ($conffile);
my $modified=0;

logger("Starting munin-nagios, checking lock");
munin_runlock("$config->{rundir}/munin-nagios.lock");
logger("Created lock: $config->{rundir}/munin-nagios.lock");
unless ($config->{nsca}) {
  logger ("No nsca-configuration found, exiting");
  exit;
}

	


for my $domain ( keys %{$config->{domain}}) {
  if ($removeok) {
     my $deletepath = "$config->{dbdir}/$domain";
     if (@limit_hosts)
     {
	 foreach my $host (@limit_hosts)
	 {
	     my $deleted = unlink <$deletepath/$host-*.ok>;
	     logger("$deleted .ok-files successfully deleted for \"$host\"");
	 }
     }
     else
     {
	 logger("Removing nagios .ok-files for $domain");
	 my $deleted = unlink <$deletepath/*.ok>;
	 logger("$deleted .ok-files successfully deleted");
     }
  } else {
     logger ("processing domain: $domain");
     process_domain($domain);
  }
  logger("munin-nagios finished.");
}


sub process_domain {
    my ($domain) = @_;
    for my $node ( keys %{$config->{domain}->{$domain}->{node}}) {
    	if (@limit_hosts and !grep (/^$node$/, @limit_hosts))
	{
		logger ("skipping node: $node");
		next;
	}
	logger ("processing node: $node");
	process_node($domain,$node ,$config->{domain}->{$domain}->{node}->{$node} );
    }
}

sub process_node {
  my ($domain,$name,$node) = @_;
  for my $client (keys %{$node->{client}}) {
      logger ("processing node $client") if $DEBUG;
      process_service($domain,$name,$client,$node->{client}->{$client});
  }
}

sub process_service {
  my $critical= undef;
  my ($domain, $name,$clientname,$client) = @_;
  return unless $client;

  my @crits    = ();
  my @warns    = ();
  my @unknowns = ();
  my @oks      = ();
  my $update   = 0;

  for my $service (keys %$client) {
    logger ("processing service: $service") if $DEBUG;
    if ($service =~ /(^.*)\.label/) {
	my $key = $1;
	next unless ((exists $client->{"$key.warning"}) || ($client->{"$key.critical"}));
	next unless (munin_get_bool ($config, "nagios", 1, $domain, $name, $clientname, $key));
    	if (@limit_services and !grep (/^$service$/, @limit_services))
	{
		next;
	}

	my @critical = (undef, undef);
	my @warning  = (undef, undef);

	if (defined $client->{"$key.critical"} and 
	    $client->{"$key.critical"} =~ /^\s*([-\d]*):([-\d]*)\s*$/)
	{
		$critical[0] = $1 if defined $1;
		$critical[1] = $2 if defined $2;
	}
	elsif (defined $client->{"$key.critical"} and
	    $client->{"$key.critical"} =~ /^\s*([-\d]+)\s*$/)
	{
		$critical[1] = $1 if defined $1;
	}
	elsif (defined $client->{"$key.critical"})
	{
	    @critical = (0, 0);
	}
	if (defined $client->{"$key.warning"} and 
	    $client->{"$key.warning"} =~ /^\s*([-\d]*):([-\d]*)\s*$/)
	{
		$warning[0] = $1 if defined $1;
		$warning[1] = $2 if defined $2;
	}
	elsif (defined $client->{"$key.warning"} and
	    $client->{"$key.warning"} =~ /^\s*([-\d]+)\s*$/)
	{
		$warning[1] = $1 if defined $1;
	}
	elsif (defined $client->{"$key.warning"})
	{
	    @warning = (0, 0);
	}
	my $filename = "$config->{dbdir}/$domain/$name-$clientname-$key-".
	    lc substr (($client->{"$key.type"}||"GAUGE"),0,1) . ".rrd";
	my $value = sprintf "%.2f",munin_fetch("$filename");
	
	if ((defined ($critical[0]) and $value < $critical[0]) or
	    (defined ($critical[1]) and $value > $critical[1])) {
#	    print "Sending; $name, $clientname ", $client->{"$key.label"}, ",2,Value is $value. Critical range ($critical[0]:$critical[1]) exceeded\n";
	  # munin_nscasend($name,$clientname,$client->{"$key.label"},2,
	  push (@crits, $client->{"$key.label"} . 
		  (defined $client->{"$key.extinfo"}?
		   	" ($value ($critical[0]:$critical[1]): ".
			$client->{"$key.extinfo"} . ")":
			" $value ($critical[0]:$critical[1])"));
	  munin_removelock("$config->{dbdir}/$domain/$name-$clientname-$key.ok");
	}
	elsif ((defined ($warning[0]) and $value < $warning[0]) or
	       (defined ($warning[1]) and $value > $warning[1])) {
#	    print "Sending; $name, $clientname ", $client->{"$key.label"}, ",2,Value is $value. Warning range ($warning[0]:$warning[1]) exceeded\n";
#	    print "Extinfo; $name, $clientname ", $client->{"$key.extinfo"}, "\n";
	  #munin_nscasend($name,$clientname,$client->{"$key.label"},1,
	  push (@warns, $client->{"$key.label"}.
		  (defined $client->{"$key.extinfo"}?
		  	" ($value ($warning[0]:$warning[1]): ".
		   	$client->{"$key.extinfo"} . ")":
		   	" $value ($warning[0]:$warning[1])"));
	  munin_removelock("$config->{dbdir}/$domain/$name-$clientname-$key.ok");
	}
	elsif ($value eq "U") {
          #munin_nscasend($name,$clientname,$client->{"$key.label"},1,
	  push (@unknowns, $client->{"$key.label"});
	  munin_removelock("$config->{dbdir}/$domain/$name-$clientname-$key.ok");
	}
	else {
	  push (@oks, $client->{"$key.label"}.
		  " ($value)");
	  if (munin_createlock("$config->{dbdir}/$domain/$name-$clientname-$key.ok"))
	  {
	    $update ++;
	  }
	}
      }
  }
  if (@crits)
  {
    munin_nscasend ($name, $clientname, "", 2, 
	scalar (@crits) . " CRITICALs: ". join (',', @crits[0 .. (@crits>20?20:@crits)]) .
	(@warns    ? "; " . scalar (@warns)    . " WARNINGs: " . join (', ', @warns[0 .. (@warns>20?20:@warns-1)]) : "") .
	(@unknowns ? "; " . scalar (@unknowns) . " UNKNOWNs: " . join (', ', @unknowns[0 .. (@unknowns>20?20:@unknowns-1)]) : "") .
	(@oks      ? "; " . scalar (@oks)      . " OKs: "      . join (', ', @oks[0 .. (@oks>20?20:@oks-1)]) : "") .
	".");
  }
  elsif (@warns)
  {
    munin_nscasend ($name, $clientname, "", 1, 
	scalar (@warns) . " WARNINGs: " . join (',', @warns[0 .. (@warns>20?20:@warns)]) .
	(@unknowns ? "; " . scalar (@unknowns) . " UNKNOWNs: "  . join (', ', @unknowns[0 .. (@unknowns>20?20:@unknowns-1)]) : "") .
	(@oks      ? "; " . scalar (@oks)      . " OKs: "       . join (', ', @oks[0 .. (@oks>20?20:@oks-1)]) : "") .
	".");
  }
  elsif (@unknowns)
  {
    munin_nscasend ($name, $clientname, "", 3, 
	scalar (@unknowns) . " UNKNOWNs: " . join (',', @unknowns[0 .. (@unknowns>20?20:@unknowns-1)]) .
	(@oks      ? "; " . scalar (@oks)      . " OKs: "       . join (', ', @oks[0 .. (@oks>20?20:@oks-1)]) : "") .
	".");
  }
  elsif (@oks and $update)
  {
    munin_nscasend ($name, $clientname, "", 0, 
	scalar (@oks) . " OKs: " . join (', ', @oks[0 .. (@oks>20?20:@oks-1)]) .
	".");
  }
}

sub logger_open {
    my $dirname = shift;

    if (!$log->opened)
    {
	unless (open ($log, ">>$dirname/munin-nagios.log"))
	{
	    print STDERR "Warning: Could not open log file \"$dirname/munin-nagios.log\" for writing: $!";
	}
    }
}


sub logger {
    my ($comment) = @_;
    my $now = strftime "%b %d %H:%M:%S", localtime;
 
    if ($log->opened)
    {
	print $log "$now - $comment\n";
    }
    else
    {
	if (defined $config->{logdir})
	{
	    if (open ($log, ">>$config->{logdir}/munin-nagios.log"))
	    {
		 print $log "$now - $comment\n";
	    }
	    else
	    {
		 print STDERR "Warning: Could not open log file \"$config->{logdir}/munin-nagios.log\" for writing: $!";
		 print STDERR "$now - $comment\n";
	    }
	}
	else
	{
	    print STDERR "$now - $comment\n";
	}
    }
}

close $log;

=head1 NAME

munin-nagios - A program to warn nagios of any off-limit values

=head1 SYNOPSIS

munin-nagios [options]

=head1 OPTIONS

=over 5

=item B<< --service <service> >>

Limit services to those of E<lt>serviceE<gt>. Multiple --service options may be supplied. [unset]

=item B<< --host <host> >>

Limit hosts to those of E<lt>host<gt>. Multiple --host options may be supplied. [unset]

=item B<< --config <file> >>

Use E<lt>fileE<gt> as configuration file. [/etc/munin/munin.conf]

=item B<< --[no]force >>

Force sending of messages ieven if you normally wouldn't. [--noforce]

=item B<< --[no]force-root >>

Force running as root (stupid and unnecessary). [--noforce-root]

=item B<< --removeok >>

Reset warning status (remove .ok-files).

=item B<< --help >>

View help message.

=item B<< --[no]debug >>

If set, view debug messages. [--nodebug]

=back

=head1 DESCRIPTION

Munin-nagios is a part of the package Munin, which is used in
combination with Munin's node.  Munin is a group of programs to gather
data from Munin's nodes, graph them, create html-pages, and optionally
warn Nagios about any off-limit values.

Munin-nagios can warn any nagios-servers about off-limit values.

If a service has fields with "warning" or "critical"-options (e.g.
"load.warning 10"), and the munin configuration file contains the
necessary configuration options, munin-nagios will warn the
nagios-server.

=head1 CONFIGURATION

The configuration file "munin.conf" must have the following optinons set correctly for munin-nagios to work:

	nsca         /usr/bin/send_nsca
	nsca_server  nagios-server.your.dom
	nsca_config  /etc/nagios/send_nsca.cfg
	
In addition NSCA must be installed and configured correctly.

=head1 FILES

	/etc/munin/munin.conf
	/var/lib/munin/*
	/var/log/munin/munin-nagios
	/var/run/munin/*

=head1 VERSION

This is munin-nagios version 1.0.2

=head1 AUTHORS

Audun Ytterdal and Jimmy Olsen.

=head1 BUGS

munin-nagios does, as of now, not check the syntax of the configuration file.

Please report other bugs in the bug tracker at L<http://munin.sf.net/>.

=head1 COPYRIGHT

Copyright  2002-2004 Knut Haugen, Audun Ytterdal, and Jimmy Olsen / Linpro AS.

This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

This program is released under the GNU General Public License

=cut

