#!/usr/bin/perl

=head1 NAME 

check_jmx4perl - Nagios check using jmx4perl for accessing JMX information 

=head1 SYNOPSIS

=head1 DESCRIPTION

=cut

use FindBin qw ($Bin);
use lib qq($Bin/../lib);
use JMX::Jmx4Perl;
use JMX::Jmx4Perl::Request;
use JMX::Jmx4Perl::Response;
use strict;
use Data::Dumper;
use Nagios::Plugin;
use Time::HiRes qw(gettimeofday);

*Nagios::Plugin::Functions::get_shortname = sub {
    return undef;
};

my $np = Nagios::Plugin->
  new(
      usage => 
      "Usage: %s -u <agent-url> -m <mbean> -a <attribute> -c <threshold critical> -w <threshold warning> -n <label>\n" . 
      "                      [--user <user>] [--password <password>] [-v] [--help]\n",
      version => "0.01",
      url => "http://www.consol.com/opensource/nagios/",
      plugin => "check_jmx4perl",
      blurb => "This plugin checks for JMX attribute values on a remote Java application server",
      extra => "\n\nYou need to deploy j4p-agent.war on the target application server.\n" .
      "Please refer to the documentation of JMX::Jmx4Perl for further details"
     );
$np->shortname(undef);
$np->add_arg(
         spec => "url|u=s",
         help => "URL to agent web application (e.g. http://server:8080/j4p-agent/)",
         required => 1
        );
$np->add_arg(
         spec => "mbean|m=s",
         help => "MBean name (e.g. \"java.lang:type=Memory\")",
         required => 1
        );
$np->add_arg(
         spec => "attribute|a=s",
         help => "Attribute name (e.g. \"HeapMemoryUsage\")",
         required => 1
        );
$np->add_arg(
         spec => "path|p=s",
         help => "Inner path for extracting a single value from a complex attribute (e.g. \"used\")",
        );
$np->add_arg(
         spec => "critical|c=s",
         help => "Critical Threshold for value. " . 
         "See http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT " .
         "for the threshold format.",
        );
$np->add_arg(
         spec => "user=s",
         help => "User for HTTP authentication"
        );
$np->add_arg(
         spec => "password=s",
         help => "Password for HTTP authentication"
        );
$np->add_arg(
         spec => "warning|w=s",
         help => "Warning Threshold for value.",
        );
$np->add_arg(
         spec => "verbose|v",
         help => "Print verbose output "
        );
$np->add_arg(
             spec => "name|n=s",
             help => "Name to use for output. Optional, by default a standard value based on the MBean ".
             "and attribute will be used"
            );
$np->getopts();

$np->nagios_die("At least a critical or warning threshold must be given") 
  if (!$np->opts->critical && !$np->opts->warning);
$np->set_thresholds
  (
   $np->opts->critical ? (critical => $np->opts->critical) : (),
   $np->opts->warning ? (warning => $np->opts->warning) : ()
  );

my $o = $np->opts;
eval {
    my $start_time = (gettimeofday)[1];
    my $jmx = JMX::Jmx4Perl->new(mode => "agent", url => $o->url, user => $o->user, password => $o->password);
    my $jmx_request = 
      JMX::Jmx4Perl::Request->new(READ_ATTRIBUTE,$o->mbean,$o->attribute,$o->path);
    if ($o->verbose) {
        print "Request URL: ",$jmx->request_url($jmx_request),"\n";
        if ($o->user) {
            print "Remote User: ",$o->user,"\n";
        }
    }
    my $resp = $jmx->request($jmx_request);
    my $duration = int ((gettimeofday)[1] - $start_time) / 1000;

    $np->add_perfdata(label => "duration", value => $duration, uom => "ms");
    if (!defined($resp->{value})) {
        $np->nagios_die("JMX Request " . &format($o) . " failed" . Dumper($resp));
    }
    if ($o->verbose) {
        print "Result fetched in ",$duration,"ms:\n";
        print Dumper($resp);
    }
    my $value = $resp->{value};
    if (ref($value)) { 
        $np->nagios_die("Response value is a ".ref($value).
                  ", not a plain value. Did you forget a --path parameter ?","Value: " . Dumper($value));
    }
    $np->add_perfdata(label => &format($o),value => $value, critical => $o->critical, warning => $o->warning);
    my $code = $np->check_threshold($value);    
    $np->nagios_exit($code,&format($o). " : Threshold " . ($code == CRITICAL ? $o->critical : $o->warning) . 
                     " failed for value $value") if $code != OK;
    $np->nagios_exit(OK,&format($o) . " : $value in range");
};
if ($@) {
    $np->nagios_die("Error: $@");
}

sub format { 
    my $o = shift;
    if ($o->name) {
        return $o->name;
    } else {
        return "[".$o->mbean.",".$o->attribute.($o->path ? "," . $o->path : "")."]";
    }
}

