Imported Upstream version 1.12
This commit is contained in:
96
lib/JMX/Jmx4Perl/Nagios/CactiJmx4Perl.pm
Normal file
96
lib/JMX/Jmx4Perl/Nagios/CactiJmx4Perl.pm
Normal file
@@ -0,0 +1,96 @@
|
||||
package JMX::Jmx4Perl::Nagios::CactiJmx4Perl;
|
||||
|
||||
use strict;
|
||||
use base qw(JMX::Jmx4Perl::Nagios::CheckJmx4Perl);
|
||||
use Data::Dumper;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
JMX::Jmx4Perl::Nagios::CactiJmx4Perl - Module for encapsulating the functionality of
|
||||
L<cacti_jmx4perl>
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
# One line in check_jmx4perl to rule them all
|
||||
JMX::Jmx4Perl::Nagios::CactiJmx4Perl->new(@ARGV)->execute();
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
=cut
|
||||
|
||||
sub create_nagios_plugin {
|
||||
my $self = shift;
|
||||
|
||||
my $np = Monitoring::Plugin->
|
||||
new(
|
||||
usage =>
|
||||
"Usage: %s -u <agent-url> [-m <mbean>] [-a <attribute>]\n" .
|
||||
" [--alias <alias>] [--value <shortcut>] [--base <alias/number/mbean>] [--delta <time-base>]\n" .
|
||||
" [--name <name>] [--product <product>]\n".
|
||||
" [--user <user>] [--password <password>] [--proxy <proxy>]\n" .
|
||||
" [--target <target-url>] [--target-user <user>] [--target-password <password>]\n" .
|
||||
" [--legacy-escape]\n" .
|
||||
" [--config <config-file>] [--check <check-name>] [--server <server-alias>] [-v] [--help]\n" .
|
||||
" arg1 arg2 ....",
|
||||
version => $JMX::Jmx4Perl::VERSION,
|
||||
url => "http://www.jmx4perl.org",
|
||||
plugin => "cacti_jmx4perl",
|
||||
license => undef,
|
||||
blurb => "This script can be used as an script for a Cacti Data Input Method",
|
||||
extra => "\n\nYou need to deploy jolokia.war on the target application server or an intermediate proxy.\n" .
|
||||
"Please refer to the documentation for JMX::Jmx4Perl for further details.\n\n" .
|
||||
"For a complete documentation please consult the man page of cacti_jmx4perl or use the option --doc"
|
||||
);
|
||||
$np->shortname(undef);
|
||||
$self->add_common_np_args($np);
|
||||
# Add dummy thresholds to keep Nagios plugin happy
|
||||
$np->set_thresholds(warning => undef, critical => undef);
|
||||
$np->getopts();
|
||||
return $np;
|
||||
}
|
||||
|
||||
sub verify_check {
|
||||
# Not needed
|
||||
}
|
||||
|
||||
sub do_exit {
|
||||
my $self = shift;
|
||||
my $error_stat = shift;
|
||||
my $np = $self->{np};
|
||||
|
||||
my $perf = $np->perfdata;
|
||||
my @res;
|
||||
for my $p (@$perf) {
|
||||
my $label = $p->label;
|
||||
$label =~ s/\s/_/g;
|
||||
push @res,@$perf > 1 ? $label . ":" . $p->value : $p->value;
|
||||
}
|
||||
print join(" ",@res),"\n";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
|
||||
=head1 LICENSE
|
||||
|
||||
This file is part of jmx4perl.
|
||||
|
||||
Jmx4perl 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.
|
||||
|
||||
jmx4perl 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 jmx4perl. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
roland@cpan.org
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
979
lib/JMX/Jmx4Perl/Nagios/CheckJmx4Perl.pm
Normal file
979
lib/JMX/Jmx4Perl/Nagios/CheckJmx4Perl.pm
Normal file
@@ -0,0 +1,979 @@
|
||||
package JMX::Jmx4Perl::Nagios::CheckJmx4Perl;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use JMX::Jmx4Perl::Nagios::SingleCheck;
|
||||
use JMX::Jmx4Perl::Nagios::MessageHandler;
|
||||
use JMX::Jmx4Perl;
|
||||
use JMX::Jmx4Perl::Request;
|
||||
use JMX::Jmx4Perl::Response;
|
||||
use Data::Dumper;
|
||||
use Monitoring::Plugin;
|
||||
use Monitoring::Plugin::Functions qw(:codes %ERRORS %STATUS_TEXT);
|
||||
use Time::HiRes qw(gettimeofday tv_interval);
|
||||
use Carp;
|
||||
use Text::ParseWords;
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
JMX::Jmx4Perl::Nagios::CheckJmx4Perl - Module for encapsulating the functionality of
|
||||
L<check_jmx4perl>
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
# One line in check_jmx4perl to rule them all
|
||||
JMX::Jmx4Perl::Nagios::CheckJmx4Perl->new()->execute();
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The purpose of this module is to encapsulate a single run of L<check_jmx4perl>
|
||||
in a perl object. This allows for C<check_jmx4perl> to run within the embedded
|
||||
Nagios perl interpreter (ePN) wihout interfering with other, potential
|
||||
concurrent, runs of this check. Please refer to L<check_jmx4perl> for
|
||||
documentation on how to use this check. This module is probably I<not> of
|
||||
general interest and serves only the purpose described above.
|
||||
|
||||
Its main task is to set up one ore more L<JMX::Jmx4Perl::Nagios::SingleCheck>
|
||||
objects from command line arguments and optionally from a configuration file.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item $check = new $JMX::Jmx4Perl::Nagios::CheckJmx4Perl()
|
||||
|
||||
Set up a object used for a single check. It will parse the command line
|
||||
arguments and any configuation file given.
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $self = { };
|
||||
bless $self,(ref($class) || $class);
|
||||
$self->{np} = $self->create_nagios_plugin();
|
||||
$self->{cmd_args} = [ @ARGV ];
|
||||
|
||||
$self->_print_doc_and_exit($self->{np}->opts->{doc}) if defined $self->{np}->opts->{doc};
|
||||
$self->_verify_and_initialize();
|
||||
return $self;
|
||||
}
|
||||
|
||||
=back
|
||||
|
||||
=head1 $check->execute()
|
||||
|
||||
Send the JMX request to the server monitored and print out a nagios output.
|
||||
|
||||
=cut
|
||||
|
||||
sub execute {
|
||||
my $self = shift;
|
||||
my $np = $self->{np};
|
||||
eval {
|
||||
# Request
|
||||
my @optional = ();
|
||||
|
||||
my $error_stat = { };
|
||||
my $target_config = $self->target_config;
|
||||
my $jmx = JMX::Jmx4Perl->new(mode => "agent", url => $self->url, user => $self->user,
|
||||
password => $self->password,
|
||||
product => $self->product,
|
||||
proxy => $self->proxy_config,
|
||||
timeout => $np->opts->{timeout} || 180,
|
||||
target => $target_config,
|
||||
# For Jolokia agents < 1.0
|
||||
'legacy-escape' => $self->legacy_escape);
|
||||
my @requests;
|
||||
for my $check (@{$self->{checks}}) {
|
||||
push @requests,@{$check->get_requests($jmx,\@ARGV)};
|
||||
}
|
||||
my $responses = $self->_send_requests($jmx,@requests);
|
||||
#print Dumper($responses);
|
||||
my @extra_requests = ();
|
||||
my $nr_checks = scalar(@{$self->{checks}});
|
||||
if ($nr_checks == 1) {
|
||||
eval {
|
||||
my @r = $self->{checks}->[0]->extract_responses($responses,\@requests,{ target => $target_config });
|
||||
push @extra_requests,@r if @r;
|
||||
};
|
||||
$self->nagios_die($@) if $@;
|
||||
} else {
|
||||
my $i = 1;
|
||||
for my $check (@{$self->{checks}}) {
|
||||
# A check can consume more than one response
|
||||
my $prefix = $self->_multi_check_prefix($check,$i++,$nr_checks);
|
||||
eval {
|
||||
my @r = $check->extract_responses($responses,\@requests,
|
||||
{
|
||||
target => $target_config,
|
||||
prefix => $prefix,
|
||||
error_stat => $error_stat
|
||||
});
|
||||
push @extra_requests,@r if @r;
|
||||
};
|
||||
if ($@) {
|
||||
my $txt = $@;
|
||||
$txt =~ s/^(.*?)\n.*$/$1/s;
|
||||
my $code = $np->opts->{'unknown-is-critical'} ? CRITICAL : UNKNOWN;
|
||||
$check->update_error_stats($error_stat,$code);
|
||||
$prefix =~ s/\%c/$STATUS_TEXT{$code}/g;
|
||||
my $msg_handler = $np->{msg_handler} || $np;
|
||||
$msg_handler->add_message($code,$prefix . $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
# Send extra requests, e.g. for switching on the history
|
||||
if (@extra_requests) {
|
||||
$self->_send_requests($jmx,@extra_requests);
|
||||
}
|
||||
|
||||
# Different outputs for multi checks/single checks
|
||||
$self->do_exit($error_stat);
|
||||
};
|
||||
if ($@) {
|
||||
# p1.pl, the executing script of the embedded nagios perl interpreter
|
||||
# uses this tag to catch an exit code of a plugin. We rethrow this
|
||||
# exception if we detect this pattern.
|
||||
if ($@ !~ /^ExitTrap:/) {
|
||||
$self->nagios_die("Error: $@");
|
||||
} else {
|
||||
die $@;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
=head1 $check->exit()
|
||||
|
||||
Write out result and exit. This method can be overridden to provide a custom
|
||||
output, which can be extracted from NagiosPlugin object.
|
||||
|
||||
=cut
|
||||
|
||||
sub do_exit {
|
||||
my $self = shift;
|
||||
my $error_stat = shift;
|
||||
my $np = $self->{np};
|
||||
|
||||
my $msg_handler = $np->{msg_handler} || $np;
|
||||
my ($code,$message) = $msg_handler->check_messages(join => "\n", join_all => "\n");
|
||||
($code,$message) = $self->_prepare_multicheck_message($np,$code,$message,$error_stat) if scalar(@{$self->{checks}}) > 1;
|
||||
|
||||
$np->nagios_exit($code, $message);
|
||||
}
|
||||
|
||||
sub _prepare_multicheck_message {
|
||||
my $self = shift;
|
||||
my $np = shift;
|
||||
my $code = shift;
|
||||
my $message = shift;
|
||||
my $error_stat = shift;
|
||||
|
||||
my $summary;
|
||||
my $labels = $self->{multi_check_labels} || {};
|
||||
my $nr_checks = scalar(@{$self->{checks}});
|
||||
$code = $self->_check_for_UNKNOWN($error_stat,$code);
|
||||
if ($code eq OK) {
|
||||
$summary = $self->_format_multicheck_ok_summary($labels->{summary_ok} ||
|
||||
"All %n checks OK",$nr_checks);
|
||||
} else {
|
||||
$summary = $self->_format_multicheck_failure_summary($labels->{summary_failure} ||
|
||||
"%e of %n checks failed [%d]",
|
||||
$nr_checks,
|
||||
$error_stat);
|
||||
}
|
||||
return ($code,$summary . "\n" . $message);
|
||||
}
|
||||
|
||||
# UNKNOWN shadows everything else
|
||||
sub _check_for_UNKNOWN {
|
||||
my $self = shift;
|
||||
my $error_stat = shift;
|
||||
my $code = shift;
|
||||
return $error_stat->{UNKNOWN} && scalar(@$error_stat->{UNKNOWN}) ? UNKNOWN : $code;
|
||||
}
|
||||
|
||||
sub _format_multicheck_ok_summary {
|
||||
my $self = shift;
|
||||
my $format = shift;
|
||||
my $nr_checks = shift;
|
||||
my $ret = $format;
|
||||
$ret =~ s/\%n/$nr_checks/g;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub _format_multicheck_failure_summary {
|
||||
my $self = shift;
|
||||
my $format = shift;
|
||||
my $nr_checks = shift;
|
||||
my $error_stat = shift;
|
||||
|
||||
my $ret = $format;
|
||||
|
||||
my $details = "";
|
||||
my $total_errors = 0;
|
||||
for my $code (UNKNOWN,CRITICAL,WARNING) {
|
||||
if (my $errs = $error_stat->{$code}) {
|
||||
$details .= scalar(@$errs) . " " . $STATUS_TEXT{$code} . " (" . join (",",@$errs) . "), ";
|
||||
$total_errors += scalar(@$errs);
|
||||
}
|
||||
}
|
||||
if ($total_errors > 0) {
|
||||
# Cut off extra chars at the end
|
||||
$details = substr($details,0,-2);
|
||||
}
|
||||
|
||||
$ret =~ s/\%d/$details/g;
|
||||
$ret =~ s/\%e/$total_errors/g;
|
||||
$ret =~ s/\%n/$nr_checks/g;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
# Create a formatted prefix for multicheck output
|
||||
sub _multi_check_prefix {
|
||||
my $self = shift;
|
||||
my $check = shift;
|
||||
my $idx = shift;
|
||||
my $max = shift;
|
||||
|
||||
my $c = $check->{config};
|
||||
|
||||
my $l = length($max);
|
||||
|
||||
return sprintf("[%$l.${l}s] %%c ",$idx)
|
||||
if (defined($c->{multicheckprefix}) && !length($c->{multicheckprefix}));
|
||||
|
||||
my $label = $c->{multicheckprefix} || $c->{name} || $c->{key} || "";
|
||||
return sprintf("[%$l.${l}s] %%c %s: ",$idx,$label);
|
||||
}
|
||||
|
||||
|
||||
# Send the requests via the build up agent
|
||||
sub _send_requests {
|
||||
my ($self,$jmx,@requests) = @_;
|
||||
my $o = $self->{opts};
|
||||
|
||||
my $start_time;
|
||||
if ($o->verbose) {
|
||||
# TODO: Print summary of request (GET vs POST)
|
||||
if ($self->user) {
|
||||
print "Remote User: ",$o->user,"\n";
|
||||
}
|
||||
$start_time = [gettimeofday];
|
||||
}
|
||||
# Detangle request for direct method calls and JMX requests to call:
|
||||
my $req_map = $self->_detangle_requests(\@requests);
|
||||
my @responses = ();
|
||||
|
||||
$self->_execute_scripts(\@responses,$req_map);
|
||||
$self->_execute_requests(\@responses,$req_map,$jmx);
|
||||
|
||||
if ($o->verbose) {
|
||||
print "Result fetched in ",tv_interval($start_time) * 1000," ms:\n";
|
||||
print Dumper(\@responses);
|
||||
}
|
||||
return \@responses;
|
||||
}
|
||||
|
||||
# Split up request for code-requests (i.e. scripts given in the configuration)
|
||||
# and 'real' requests. Remember the index, too so that the response can be
|
||||
# weave together
|
||||
sub _detangle_requests {
|
||||
my $self = shift;
|
||||
my $requests = shift;
|
||||
my $req_map = {};
|
||||
my $idx = 0;
|
||||
for my $r (@$requests) {
|
||||
push @{$req_map->{ref($r) eq "CODE" ? "code" : "request"}},[$r,$idx];
|
||||
$idx++;
|
||||
}
|
||||
return $req_map;
|
||||
}
|
||||
|
||||
# Execute subrefs created out of scripts. Put it in the right place of the
|
||||
# result array according to the remembered index
|
||||
sub _execute_scripts {
|
||||
my $self = shift;
|
||||
my $responses = shift;
|
||||
my $req_map = shift;
|
||||
for my $e (@{$req_map->{"code"}}) {
|
||||
# Will die on error which will bubble up
|
||||
$responses->[$e->[1]] = &{$e->[0]}();;
|
||||
}
|
||||
}
|
||||
|
||||
# Execute requests and put it in the received responses in the right place for
|
||||
# the returned array. The index has been extracted beforehand and stored in the
|
||||
# given req_map
|
||||
sub _execute_requests {
|
||||
my $self = shift;
|
||||
my $responses = shift;
|
||||
my $req_map = shift;
|
||||
my $jmx = shift;
|
||||
|
||||
# Call remote JMX and weave in
|
||||
my $reqs2send = $req_map->{"request"};
|
||||
if ($reqs2send) {
|
||||
my @resp_received = $jmx->request(map { $_->[0] } @$reqs2send);
|
||||
for my $r (@$reqs2send) {
|
||||
$responses->[$r->[1]] = shift @resp_received;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Print online manual and exit (somewhat crude, I know)
|
||||
sub _print_doc_and_exit {
|
||||
my $self = shift;
|
||||
my $section = shift;
|
||||
if (!eval "require Pod::Usage; Pod::Usage->import(qw(pod2usage)); 1;") {
|
||||
print "Please install Pod::Usage for creating the online help\n";
|
||||
exit 1;
|
||||
}
|
||||
if ($section) {
|
||||
my %sects = (
|
||||
tutorial => "TUTORIAL",
|
||||
reference => "REFERENCE",
|
||||
options => "COMMAND LINE",
|
||||
config => "CONFIGURATION",
|
||||
);
|
||||
my $real_section = $sects{lc $section};
|
||||
if ($real_section) {
|
||||
pod2usage(-verbose => 99, -sections => $real_section );
|
||||
} else {
|
||||
print "Unknown documentation section '$section' (known: ",join (",",sort keys %sects),")\n";
|
||||
exit 1;
|
||||
}
|
||||
} else {
|
||||
pod2usage(-verbose => 99);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Initialize this object and validate the mandatory parameters (obtained from
|
||||
# the command line or a configuration file). It will also build up
|
||||
# one or more SingleCheck which are later on sent as a bulk request to
|
||||
# the server.
|
||||
sub _verify_and_initialize {
|
||||
my $self = shift;
|
||||
my $np = $self->{np};
|
||||
my $o = $np->opts;
|
||||
|
||||
$self->{opts} = $self->{np}->opts;
|
||||
|
||||
# Fetch configuration
|
||||
my $config = $self->_get_config($o->config);
|
||||
# Now, if a specific check is given, extract it, too.
|
||||
my $check_configs;
|
||||
#print Dumper($config);
|
||||
$check_configs = $self->_extract_checks($config,$o->check);
|
||||
#print Dumper($check_configs);
|
||||
if ($check_configs) {
|
||||
for my $c (@$check_configs) {
|
||||
my $s_c = new JMX::Jmx4Perl::Nagios::SingleCheck($np,$c);
|
||||
push @{$self->{checks}},$s_c;
|
||||
}
|
||||
} else {
|
||||
$self->{checks} = [ new JMX::Jmx4Perl::Nagios::SingleCheck($np) ];
|
||||
}
|
||||
# If a server name is given, we use that for the connection parameters
|
||||
if ($o->server) {
|
||||
$self->{server_config} = $config->get_server_config($o->server)
|
||||
|| $self->nagios_die("No server configuration for " . $o->server . " found");
|
||||
}
|
||||
|
||||
# Sanity checks
|
||||
$self->nagios_die("No Server URL given") unless $self->url;
|
||||
|
||||
for my $check (@{$self->{checks}}) {
|
||||
my $name = $check->name ? " [Check: " . $check->name . "]" : "";
|
||||
$self->nagios_die("An MBean name and a attribute/operation must be provided " . $name)
|
||||
if ((!$check->mbean || (!$check->attribute && !$check->operation)) && !$check->alias && !$check->value && !$check->script);
|
||||
}
|
||||
}
|
||||
|
||||
# Extract one or more check configurations which can be
|
||||
# simple <Check>s or <MultiCheck>s
|
||||
sub _extract_checks {
|
||||
my $self = shift;
|
||||
my $config = shift;
|
||||
my $check = shift;
|
||||
|
||||
my $np = $self->{np};
|
||||
if ($check) {
|
||||
$self->nagios_die("No configuration given") unless $config;
|
||||
$self->nagios_die("No checks defined in configuration") unless $config->{check};
|
||||
|
||||
my $check_configs;
|
||||
unless ($config->{check}->{$check}) {
|
||||
$check_configs = $self->_resolve_multicheck($config,$check,$self->{cmd_args});
|
||||
$self->_retrieve_mc_summary_label($config,$check);
|
||||
} else {
|
||||
my $check_config = $config->{check}->{$check};
|
||||
$check_configs = ref($check_config) eq "ARRAY" ? $check_config : [ $check_config ];
|
||||
$check_configs->[0]->{key} = $check;
|
||||
}
|
||||
$self->nagios_die("No check configuration with name " . $check . " found") unless (@{$check_configs});
|
||||
|
||||
#print Dumper($check_configs);
|
||||
|
||||
# Resolve parent values
|
||||
for my $c (@{$check_configs}) {
|
||||
#print "[A] ",Dumper($c);
|
||||
$self->_resolve_check_config($c,$config,$self->{cmd_args});
|
||||
|
||||
#print "[B] ",Dumper($c);
|
||||
# Finally, resolve any left over place holders
|
||||
for my $k (keys(%$c)) {
|
||||
$c->{$k} = $self->_replace_placeholder($c->{$k},undef) unless ref($c->{$k});
|
||||
}
|
||||
#print "[C] ",Dumper($c);
|
||||
}
|
||||
return $check_configs;
|
||||
} else {
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
# Resolve a multicheck configuration (<MultiCheck>)
|
||||
sub _resolve_multicheck {
|
||||
my $self = shift;
|
||||
my $config = shift;
|
||||
my $check = shift;
|
||||
my $args = shift;
|
||||
my $np = $self->{np};
|
||||
my $multi_checks = $config->{multicheck};
|
||||
my $check_config = [];
|
||||
if ($multi_checks) {
|
||||
my $m_check = $multi_checks->{$check};
|
||||
if ($m_check) {
|
||||
# Resolve all checks
|
||||
my $c_names = [];
|
||||
for my $type( qw(check multicheck)) {
|
||||
if ($m_check->{$type}) {
|
||||
push @$c_names, ref($m_check->{$type}) eq "ARRAY" ? @{$m_check->{$type}} : $m_check->{$type};
|
||||
}
|
||||
}
|
||||
for my $name (@$c_names) {
|
||||
my ($c_name,$c_args) = $self->_parse_check_ref($name);
|
||||
my $args_merged = $self->_merge_multicheck_args($c_args,$args);
|
||||
$self->nagios_die("Unknown check '" . $c_name . "' for multi check " . $check)
|
||||
unless defined($config->{check}->{$c_name}) or defined($multi_checks->{$c_name});
|
||||
if ($config->{check}->{$c_name}) {
|
||||
# We need a copy of the check hash to avoid mangling it up
|
||||
# if it is referenced multiple times
|
||||
my $check = { %{$config->{check}->{$c_name}} };
|
||||
$check->{key} = $c_name;
|
||||
$check->{args} = $args_merged;
|
||||
push @{$check_config},$check;
|
||||
} else {
|
||||
# It's a multi check referenced via <Check> or <MultiCheck> ....
|
||||
push @{$check_config},@{$self->_resolve_multicheck($config,$c_name,$args_merged)};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $check_config;
|
||||
}
|
||||
|
||||
sub _retrieve_mc_summary_label {
|
||||
my $self = shift;
|
||||
my $config = shift;
|
||||
my $check = shift;
|
||||
|
||||
my $multi_checks = $config->{multicheck};
|
||||
if ($multi_checks) {
|
||||
my $m_check = $multi_checks->{$check};
|
||||
if ($m_check && ($m_check->{summaryok} || $m_check->{summaryfailure})) {
|
||||
my $mc_labels =
|
||||
$self->{multi_check_labels} = {
|
||||
summary_ok => $m_check->{summaryok},
|
||||
summary_failure => $m_check->{summaryfailure}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub _merge_multicheck_args {
|
||||
my $self = shift;
|
||||
my $check_params = shift;
|
||||
my $args = shift;
|
||||
if (!$args || !$check_params) {
|
||||
return $check_params;
|
||||
}
|
||||
my $ret = [ @$check_params ]; # Copy it over
|
||||
for my $i (0 .. $#$check_params) {
|
||||
if ($check_params->[$i] =~ /^\$(\d+)$/) {
|
||||
my $j = $1;
|
||||
if ($j <= $#$args) {
|
||||
$ret->[$i] = $args->[$j];
|
||||
next;
|
||||
}
|
||||
# Nothing to replace
|
||||
$ret->[$i] = $check_params->[$i];
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
# Resolve a singe <Check> configuration
|
||||
sub _resolve_check_config {
|
||||
my $self = shift;
|
||||
my $check = shift;
|
||||
my $config = shift;
|
||||
# Args can come from the outside, but also as part of a multicheck (stored
|
||||
# in $check->{args})
|
||||
my $args = $check->{args} && @{$check->{args}} ? $check->{args} : shift;
|
||||
my $np = $self->{np};
|
||||
if ($check->{use}) {
|
||||
# Resolve parents
|
||||
my $parents = ref($check->{use}) eq "ARRAY" ? $check->{use} : [ $check->{use} ];
|
||||
my $parent_merged = {};
|
||||
for my $p (@$parents) {
|
||||
my ($p_name,$p_args) = $self->_parse_check_ref($p);
|
||||
$self->nagios_die("Unknown parent check '" . $p_name . "' for check '" .
|
||||
($check->{key} ? $check->{key} : $check->{name}) . "'")
|
||||
unless $config->{check}->{$p_name};
|
||||
# Clone it to avoid side effects when replacing checks inline
|
||||
my $p_check = { %{$config->{check}->{$p_name}} };
|
||||
$p_check->{key} = $p_name;
|
||||
#print "::::: ",Dumper($p_check,$p_args);
|
||||
|
||||
$self->_resolve_check_config($p_check,$config,$p_args);
|
||||
|
||||
#$self->_replace_args($p_check,$config,$p_args);
|
||||
$parent_merged->{$_} = $p_check->{$_} for keys %$p_check;
|
||||
}
|
||||
# Replace inherited values
|
||||
for my $k (keys %$parent_merged) {
|
||||
my $parent_val = defined($parent_merged->{$k}) ? $parent_merged->{$k} : "";
|
||||
if (defined($check->{$k})) {
|
||||
$check->{$k} =~ s/\$BASE/$parent_val/g;
|
||||
} else {
|
||||
$check->{$k} = $parent_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
$self->_replace_args($check,$config,$args);
|
||||
return $check;
|
||||
}
|
||||
|
||||
# Replace argument placeholders with a given list of arguments
|
||||
sub _replace_args {
|
||||
my $self = shift;
|
||||
my $check = shift;
|
||||
my $config = shift;
|
||||
my $args = shift;
|
||||
for my $k (keys(%$check)) {
|
||||
next if $k =~ /^(key|args)$/; # Internal keys
|
||||
$check->{$k} =
|
||||
$self->_replace_placeholder($check->{$k},$args)
|
||||
if ($args && @$args && !ref($check->{$k}));
|
||||
}
|
||||
}
|
||||
|
||||
sub _replace_placeholder {
|
||||
my $self = shift;
|
||||
my $val = shift;
|
||||
my $args = shift;
|
||||
my $index = defined($args) ? join "|",0 ... $#$args : "\\d+";
|
||||
|
||||
my $regexp_s = <<'EOP';
|
||||
^(.*?) # Start containing no args
|
||||
|
||||
\$( # Variable starts with '$'
|
||||
($index) | # $0 without default value
|
||||
\{\s*($index)\s* # ${0:12300} with default value
|
||||
(?: :([^\}]+) )*\} # ?: --> clustering group, optional (${0} is also ok)
|
||||
)
|
||||
|
||||
(.*|$) # The rest which will get parsed next
|
||||
EOP
|
||||
$regexp_s =~ s/\$index/$index/g;
|
||||
my $regexp = qr/$regexp_s/sx;
|
||||
die "Cannot create placeholder regexp" if $@;
|
||||
my $rest = $val;
|
||||
my $ret = "";
|
||||
while (defined($rest) && length($rest) && $rest =~ $regexp) {
|
||||
# $1: start with no placeholder
|
||||
# $2: literal variable as it is defined
|
||||
# $3: variable name (0,1,2,3,...)
|
||||
# $4: same as $3, but either $3 or $4 is defined
|
||||
# $5: default value (if any)
|
||||
# $6: rest which is processed next in the loop
|
||||
my $start = defined($1) ? $1 : "";
|
||||
my $orig_val = '$' . $2;
|
||||
my $i = defined($3) ? $3 : $4;
|
||||
my $default = $5;
|
||||
my $end = defined($6) ? $6 : "";
|
||||
$default =~ s/^\s*(.*)+?\s*$/$1/ if $default; # Trim whitespace
|
||||
#print Dumper({start => $start, orig => $orig_val,end => $end, default=> $default, rest => $rest, i => $i});
|
||||
if (defined($args)) {
|
||||
my $repl = $args->[$i];
|
||||
if (defined($repl)) {
|
||||
if ($repl =~ /^\$(\d+)$/) {
|
||||
my $new_index = $1;
|
||||
#print "============== $val $new_index\n";
|
||||
# Val is a placeholder itself
|
||||
if (defined($default)) {
|
||||
$ret .= $start . '${' . $new_index . ':' . $default . '}';
|
||||
} else {
|
||||
$ret .= $start . '$' . $new_index;
|
||||
}
|
||||
} else {
|
||||
$ret .= $start . $repl;
|
||||
}
|
||||
} else {
|
||||
$ret .= $start . $orig_val;
|
||||
}
|
||||
} else {
|
||||
# We have to replace any left over placeholder either with its
|
||||
# default value or with an empty value
|
||||
if (defined($default)) {
|
||||
$ret .= $start . $default;
|
||||
} elsif (length($start) || length($end)) {
|
||||
$ret .= $start;
|
||||
} else {
|
||||
if (!length($ret)) {
|
||||
# No default value, nothing else for this value. We
|
||||
# consider it undefined
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
}
|
||||
$rest = $end;
|
||||
#print "... $ret$rest\n";
|
||||
}
|
||||
return $ret . (defined($rest) ? $rest : "");
|
||||
}
|
||||
|
||||
# Split up a 'Use' parent config reference, including possibly arguments
|
||||
sub _parse_check_ref {
|
||||
my $self = shift;
|
||||
my $check_ref = shift;
|
||||
if ($check_ref =~/^\s*(.+?)\((.*)\)\s*$/) {
|
||||
my $name = $1;
|
||||
my $args_s = $2;
|
||||
my $args = [ parse_line('\s*,\s*',0,$args_s) ];
|
||||
return ($name,$args);
|
||||
} else {
|
||||
return $check_ref;
|
||||
}
|
||||
}
|
||||
|
||||
# Get the configuration as a hash
|
||||
sub _get_config {
|
||||
my $self = shift;
|
||||
my $path = shift;
|
||||
my $np = $self->{np};
|
||||
$self->nagios_die("No configuration file " . $path . " found")
|
||||
if ($path && ! -e $path);
|
||||
return new JMX::Jmx4Perl::Config($path);
|
||||
}
|
||||
|
||||
# The global server config part
|
||||
sub _server_config {
|
||||
return shift->{server_config};
|
||||
}
|
||||
|
||||
# Create the nagios plugin used for preparing the nagios output
|
||||
sub create_nagios_plugin {
|
||||
my $self = shift;
|
||||
my $np = Monitoring::Plugin->
|
||||
new(
|
||||
usage =>
|
||||
"Usage: %s -u <agent-url> -m <mbean> -a <attribute> -c <threshold critical> -w <threshold warning>\n" .
|
||||
" [--alias <alias>] [--value <shortcut>] [--base <alias/number/mbean>] [--delta <time-base>]\n" .
|
||||
" [--name <perf-data label>] [--label <output-label>] [--product <product>]\n".
|
||||
" [--user <user>] [--password <password>] [--proxy <proxy>]\n" .
|
||||
" [--target <target-url>] [--target-user <user>] [--target-password <password>]\n" .
|
||||
" [--legacy-escape]\n" .
|
||||
" [--config <config-file>] [--check <check-name>] [--server <server-alias>] [-v] [--help]\n" .
|
||||
" arg1 arg2 ....",
|
||||
version => $JMX::Jmx4Perl::VERSION,
|
||||
url => "http://www.jmx4perl.org",
|
||||
plugin => "check_jmx4perl",
|
||||
blurb => "This plugin checks for JMX attribute values on a remote Java application server",
|
||||
extra => "\n\nYou need to deploy jolokia.war on the target application server or an intermediate proxy.\n" .
|
||||
"Please refer to the documentation for JMX::Jmx4Perl for further details.\n\n" .
|
||||
"For a complete documentation please consult the man page of check_jmx4perl or use the option --doc"
|
||||
);
|
||||
$np->shortname(undef);
|
||||
$self->add_common_np_args($np);
|
||||
$self->add_nagios_np_args($np);
|
||||
$np->{msg_handler} = new JMX::Jmx4Perl::Nagios::MessageHandler();
|
||||
$np->getopts();
|
||||
return $np;
|
||||
}
|
||||
|
||||
sub add_common_np_args {
|
||||
my $self = shift;
|
||||
my $np = shift;
|
||||
|
||||
$np->add_arg(
|
||||
spec => "url|u=s",
|
||||
help => "URL to agent web application (e.g. http://server:8080/jolokia/)",
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "product=s",
|
||||
help => "Name of app server product. (e.g. \"jboss\")",
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "alias=s",
|
||||
help => "Alias name for attribte (e.g. \"MEMORY_HEAP_USED\")",
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "mbean|m=s",
|
||||
help => "MBean name (e.g. \"java.lang:type=Memory\")",
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "attribute|a=s",
|
||||
help => "Attribute name (e.g. \"HeapMemoryUsage\")",
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "operation|o=s",
|
||||
help => "Operation to execute",
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "value=s",
|
||||
help => "Shortcut for specifying mbean/attribute/path. Slashes within names must be escaped with \\",
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "delta|d:s",
|
||||
help => "Switches on incremental mode. Optional argument are seconds used for normalizing.",
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "path|p=s",
|
||||
help => "Inner path for extracting a single value from a complex attribute or return value (e.g. \"used\")",
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "target=s",
|
||||
help => "JSR-160 Service URL specifing the target server"
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "target-user=s",
|
||||
help => "Username to use for JSR-160 connection (if --target is set)"
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "target-password=s",
|
||||
help => "Password to use for JSR-160 connection (if --target is set)"
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "proxy=s",
|
||||
help => "Proxy to use"
|
||||
);
|
||||
$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 => "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->add_arg(
|
||||
spec => "legacy-escape!",
|
||||
help => "Use legacy escape mechanism for Jolokia agents < 1.0"
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "config=s",
|
||||
help => "Path to configuration file. Default: ~/.j4p"
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "server=s",
|
||||
help => "Symbolic name of server url to use, which needs to be configured in the configuration file"
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "check=s",
|
||||
help => "Name of a check configuration as defined in the configuration file"
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "method=s",
|
||||
help => "HTTP method to use. Either \"get\" or \"post\""
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "doc:s",
|
||||
help => "Print the documentation of check_jmx4perl, optionally specifying the section (tutorial, args, config)"
|
||||
);
|
||||
}
|
||||
|
||||
sub add_nagios_np_args {
|
||||
my $self = shift;
|
||||
my $np = shift;
|
||||
|
||||
$np->add_arg(
|
||||
spec => "base|base-alias|b=s",
|
||||
help => "Base name, which when given, interprets critical and warning values as relative in the range 0 .. 100%. Must be given in the form mbean/attribute/path",
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "base-mbean=s",
|
||||
help => "Base MBean name, interprets critical and warning values as relative in the range 0 .. 100%. Requires a base-attribute, too",
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "base-attribute=s",
|
||||
help => "Base attribute for a relative check. Used together with base-mbean",
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "base-path=s",
|
||||
help => "Base path for relatie checks, where this path is used on the base attribute's value",
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "unit=s",
|
||||
help => "Unit of measurement of the data retreived. Recognized values are [B|KB|MN|GB|TB] for memory values and [us|ms|s|m|h|d] for time values"
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "null=s",
|
||||
help => "Value which should be used in case of a null return value of an operation or attribute. Is \"null\" by default"
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "string",
|
||||
help => "Force string comparison for critical and warning checks"
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "numeric",
|
||||
help => "Force numeric comparison for critical and warning checks"
|
||||
);
|
||||
$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 => "warning|w=s",
|
||||
help => "Warning Threshold for value.",
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "label|l=s",
|
||||
help => "Label to be used for printing out the result of the check. Placeholders can be used."
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "perfdata=s",
|
||||
help => "Whether performance data should be omitted, which are included by default."
|
||||
);
|
||||
$np->add_arg(
|
||||
spec => "unknown-is-critical",
|
||||
help => "Map UNKNOWN errors to errors with a CRITICAL status"
|
||||
);
|
||||
}
|
||||
|
||||
# Access to configuration informations
|
||||
# Known config options (key: cmd line arguments, values: keys in config);
|
||||
my $SERVER_CONFIG_KEYS = {
|
||||
"url" => "url",
|
||||
"user" => "user",
|
||||
"password" => "password",
|
||||
"product" => "product",
|
||||
"legacy_escape" => "legacyconfig"
|
||||
};
|
||||
|
||||
# Get target configuration or undef if no jmx-proxy mode
|
||||
# is used
|
||||
sub target_config {
|
||||
return shift->_target_or_proxy_config("target","target-user","target-password");
|
||||
}
|
||||
|
||||
# Get proxy configuration or undef if no proxy configuration
|
||||
# is used
|
||||
sub proxy_config {
|
||||
return shift->_target_or_proxy_config("proxy","proxy-user","proxy-password");
|
||||
}
|
||||
|
||||
sub _target_or_proxy_config {
|
||||
my $self = shift;
|
||||
|
||||
my $main_key = shift;
|
||||
my $user_opt = shift;
|
||||
my $password_opt = shift;
|
||||
|
||||
my $np = $self->{np};
|
||||
my $opts = $np->opts;
|
||||
my $server_config = $self->_server_config;
|
||||
if ($opts->{$main_key}) {
|
||||
# Use configuration from the command line:
|
||||
return {
|
||||
url => $opts->{$main_key},
|
||||
user => $opts->{$user_opt},
|
||||
password => $opts->{$password_opt}
|
||||
}
|
||||
} elsif ($server_config && $server_config->{$main_key}) {
|
||||
# Use configuration directly from the server definition:
|
||||
return $server_config->{$main_key}
|
||||
} else {
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
# Autoloading is used to fetch the proper connection parameters
|
||||
sub AUTOLOAD {
|
||||
my $self = shift;
|
||||
my $np = $self->{np};
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://; # strip fully-qualified portion
|
||||
my $opts_name = $name;
|
||||
$opts_name =~ s/_/-/;
|
||||
|
||||
if ($SERVER_CONFIG_KEYS->{$name}) {
|
||||
return $np->opts->{$opts_name} if $np->opts->{$opts_name};
|
||||
my $c = $SERVER_CONFIG_KEYS->{$name};
|
||||
if ($c) {
|
||||
my @parts = split "/",$c;
|
||||
my $h = $self->_server_config ||
|
||||
return undef;
|
||||
while (@parts) {
|
||||
my $p = shift @parts;
|
||||
return undef unless $h->{$p};
|
||||
$h = $h->{$p};
|
||||
return $h unless @parts;
|
||||
}
|
||||
} else {
|
||||
return undef;
|
||||
}
|
||||
} else {
|
||||
$self->nagios_die("No config attribute \"" . $name . "\" known");
|
||||
}
|
||||
}
|
||||
|
||||
sub nagios_die {
|
||||
my $self = shift;
|
||||
my @args = @_;
|
||||
|
||||
my $np = $self->{np};
|
||||
$np->nagios_die(join("",@args),$np->opts->{'unknown-is-critical'} ? CRITICAL : UNKNOWN)
|
||||
}
|
||||
|
||||
# Declared here to avoid AUTOLOAD confusions
|
||||
sub DESTROY {
|
||||
|
||||
}
|
||||
|
||||
=head1 LICENSE
|
||||
|
||||
This file is part of jmx4perl.
|
||||
|
||||
Jmx4perl 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.
|
||||
|
||||
jmx4perl 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 jmx4perl. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
roland@cpan.org
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
60
lib/JMX/Jmx4Perl/Nagios/MessageHandler.pm
Normal file
60
lib/JMX/Jmx4Perl/Nagios/MessageHandler.pm
Normal file
@@ -0,0 +1,60 @@
|
||||
package JMX::Jmx4Perl::Nagios::MessageHandler;
|
||||
|
||||
use Monitoring::Plugin::Functions qw(:codes %ERRORS %STATUS_TEXT);
|
||||
use strict;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
JMX::Jmx4Perl::Nagios::MessageHandler - Handling Nagios exit message (one or
|
||||
many)
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $self = {
|
||||
messages => {}
|
||||
};
|
||||
bless $self,(ref($class) || $class);
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub add_message {
|
||||
my $self = shift;
|
||||
my ($code,@messages) = @_;
|
||||
|
||||
die "Invalid error code '$code'\n"
|
||||
unless defined($ERRORS{uc $code}) || defined($STATUS_TEXT{$code});
|
||||
|
||||
# Store messages using strings rather than numeric codes
|
||||
$code = $STATUS_TEXT{$code} if $STATUS_TEXT{$code};
|
||||
$code = lc $code;
|
||||
|
||||
$self->{messages}->{$code} = [] unless $self->{messages}->{$code};
|
||||
push @{$self->{messages}->{$code}}, @messages;
|
||||
}
|
||||
|
||||
sub check_messages {
|
||||
my $self = shift;
|
||||
my %arg = @_;
|
||||
|
||||
for my $code (qw(critical warning ok unknown)) {
|
||||
$arg{$code} = $self->{messages}->{$code} || [];
|
||||
}
|
||||
|
||||
my $code = OK;
|
||||
$code ||= UNKNOWN if @{$arg{unknown}};
|
||||
$code ||= CRITICAL if @{$arg{critical}};
|
||||
$code ||= WARNING if @{$arg{warning}};
|
||||
|
||||
my $message = join( "\n",
|
||||
map { @$_ ? join( "\n", @$_) : () }
|
||||
$arg{unknown},
|
||||
$arg{critical},
|
||||
$arg{warning},
|
||||
$arg{ok} ? (ref $arg{ok} ? $arg{ok} : [ $arg{ok} ]) : []
|
||||
);
|
||||
return ($code, $message);
|
||||
}
|
||||
|
||||
1;
|
||||
821
lib/JMX/Jmx4Perl/Nagios/SingleCheck.pm
Normal file
821
lib/JMX/Jmx4Perl/Nagios/SingleCheck.pm
Normal file
@@ -0,0 +1,821 @@
|
||||
package JMX::Jmx4Perl::Nagios::SingleCheck;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use JMX::Jmx4Perl;
|
||||
use JMX::Jmx4Perl::Request;
|
||||
use JMX::Jmx4Perl::Response;
|
||||
use JMX::Jmx4Perl::Alias;
|
||||
use Data::Dumper;
|
||||
use Monitoring::Plugin;
|
||||
use Monitoring::Plugin::Functions qw(:codes %STATUS_TEXT);
|
||||
use Carp;
|
||||
use Scalar::Util qw(looks_like_number);
|
||||
use URI::Escape;
|
||||
use Text::ParseWords;
|
||||
use JSON;
|
||||
|
||||
our $AUTOLOAD;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
JMX::Jmx4Perl::Nagios::SingleCheck - A single nagios check
|
||||
|
||||
This is an package used internally by
|
||||
L<JMX::Jmx4Perl::Nagios::CheckJmx4Perl>. It encapsulates the configuration for
|
||||
single checks, which can be combined to a bulk JMX-Request so only a single
|
||||
server turnaround is used to obtain multiple checks results at once.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=over
|
||||
|
||||
=item $single_check = new $JMX::Jmx4Perl::Nagios::SingleCheck($nagios_plugin,$check_config)
|
||||
|
||||
Construct a new single check from a given L<Monitoring::Plugin> object
|
||||
C<$nagios_plugin> and a parsed check configuration $check_config, which is a
|
||||
hash.
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $np = shift || die "No Nagios Plugin given";
|
||||
my $config = shift;
|
||||
my $self = {
|
||||
np => $np,
|
||||
config => $config
|
||||
};
|
||||
bless $self,(ref($class) || $class);
|
||||
return $self;
|
||||
}
|
||||
|
||||
=item $requests = $single_check->get_requests($jmx,$args)
|
||||
|
||||
Called to obtain an arrayref of L<JMX::Jmx4Perl::Request> objects which should
|
||||
be send to the server agent. C<$jmx> ist the L<JMX::Jmx4Perl> agent, C<$args>
|
||||
are additonal arguments used for exec-operations,
|
||||
|
||||
Multiple request object are returned e.g. if a relative check has to be
|
||||
performed in order to get the base value as well.
|
||||
|
||||
The returned array can contain coderefs which should be executed directly and
|
||||
its return value should be used in order to perfoorm the check.
|
||||
|
||||
=cut
|
||||
|
||||
sub get_requests {
|
||||
my $self = shift;
|
||||
my $jmx = shift;
|
||||
my $args = shift;
|
||||
# If a script is given, extract a subref and return it
|
||||
return [ $self->_extract_script_as_subref($jmx) ] if $self->script;
|
||||
|
||||
my $do_read = $self->attribute || $self->value;
|
||||
my $do_exec = $self->operation;
|
||||
if ($self->alias) {
|
||||
my $alias = JMX::Jmx4Perl::Alias->by_name($self->alias);
|
||||
die "No alias '",$self->alias," known" unless $alias;
|
||||
$do_read = $alias->type eq "attribute";
|
||||
}
|
||||
my @requests = ();
|
||||
my $request;
|
||||
if ($do_read) {
|
||||
$request = JMX::Jmx4Perl::Request->new(READ,$self->_prepare_read_args($jmx));
|
||||
} elsif ($do_exec) {
|
||||
$request = JMX::Jmx4Perl::Request->new(EXEC,$self->_prepare_exec_args($jmx,@$args));
|
||||
} else {
|
||||
die "Neither an attribute/value, an operation or a script given";
|
||||
}
|
||||
my $method = $self->{np}->opts->{method} || $self->{config}->{method};
|
||||
if ($method) {
|
||||
$request->method($method);
|
||||
}
|
||||
push @requests,$request;
|
||||
|
||||
if ($self->base || $self->base_mbean) {
|
||||
if (!looks_like_number($self->base)) {
|
||||
# It looks like a number, so we will use the base literally
|
||||
my $alias;
|
||||
|
||||
if ($self->base) {
|
||||
$alias = JMX::Jmx4Perl::Alias->by_name($self->base);
|
||||
}
|
||||
if ($alias) {
|
||||
push @requests,new JMX::Jmx4Perl::Request(READ,$jmx->resolve_alias($self->base));
|
||||
} else {
|
||||
my ($mbean,$attr,$path) = $self->base_mbean ?
|
||||
($self->base_mbean, $self->base_attribute, $self->base_path) :
|
||||
$self->_split_attr_spec($self->base);
|
||||
die "No MBean given in base name ",$self->base unless $mbean;
|
||||
die "No Attribute given in base name ",$self->base unless $attr;
|
||||
|
||||
$mbean = URI::Escape::uri_unescape($mbean);
|
||||
$attr = URI::Escape::uri_unescape($attr);
|
||||
$path = URI::Escape::uri_unescape($path) if $path;
|
||||
push @requests,new JMX::Jmx4Perl::Request(READ,$mbean,$attr,$path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return \@requests;
|
||||
}
|
||||
|
||||
# Create a subref where all params from the outside are available as closures.
|
||||
sub _extract_script_as_subref {
|
||||
my $self = shift;
|
||||
my $jmx = shift;
|
||||
my $script = $self->script || die "No script given";
|
||||
my $full_script = <<"EOT";
|
||||
sub {
|
||||
my \$j4p = shift;
|
||||
return sub {
|
||||
$script
|
||||
}
|
||||
}
|
||||
EOT
|
||||
#print $full_script,"\n";
|
||||
my $sub = eval $full_script;
|
||||
die "Cannot eval script for check ",$self->name,": $@" if $@;
|
||||
return &$sub($jmx);
|
||||
}
|
||||
|
||||
=item $single_check->exract_responses($responses,$requests,$target)
|
||||
|
||||
Extract L<JMX::Jmx4Perl::Response> objects and add the deducted results to
|
||||
the nagios plugin (which was given at construction time).
|
||||
|
||||
C<$responses> is an arrayref to the returned responses, C<$requests> is an
|
||||
arrayref to the original requests. Any response consumed from C<$requests>
|
||||
should be removed from the array, as well as the corresponding request.
|
||||
The requests/responses for this single request are always a the beginning of
|
||||
the arrays.
|
||||
|
||||
C<$target> is an optional target configuration if the request was used in
|
||||
target proxy mode.
|
||||
|
||||
=cut
|
||||
|
||||
sub extract_responses {
|
||||
my $self = shift;
|
||||
my $responses = shift;
|
||||
my $requests = shift;
|
||||
my $opts = shift || {};
|
||||
my $np = $self->{np};
|
||||
my $msg_handler = $np->{msg_handler} || $np;
|
||||
|
||||
# Get response/request pair
|
||||
my $resp = shift @{$responses};
|
||||
my $request = shift @{$requests};
|
||||
#print Dumper($resp);
|
||||
my @extra_requests = ();
|
||||
my $value;
|
||||
my $script_mode = undef;
|
||||
if (ref($request) eq "CODE") {
|
||||
# It's a script, so the 'response' is already the value
|
||||
$script_mode = 1;
|
||||
$value = $resp;
|
||||
} else {
|
||||
$self->_verify_response($request,$resp);
|
||||
$value = $self->_extract_value($request,$resp);
|
||||
}
|
||||
# Delta handling
|
||||
my $delta = $self->delta;
|
||||
if (defined($delta) && !$script_mode) {
|
||||
$value = $self->_delta_value($request,$resp,$delta);
|
||||
unless (defined($value)) {
|
||||
push @extra_requests,$self->_switch_on_history($request,$opts->{target});
|
||||
$value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
# Normalize value
|
||||
my ($value_conv,$unit) = $self->_normalize_value($value);
|
||||
my $label = $self->_get_name(cleanup => 1);
|
||||
if ( ($self->base || $self->base_mbean) && !$script_mode) {
|
||||
# Calc relative value
|
||||
my $base_value = $self->_base_value($self->base,$responses,$requests);
|
||||
my $rel_value = sprintf "%2.2f",$base_value ? (int((($value / $base_value) * 10000) + 0.5) / 100) : 0;
|
||||
|
||||
# Performance data. Convert to absolute values before
|
||||
if ($self->_include_perf_data) {
|
||||
if ($self->perfdata && $self->perfdata =~ /^\s*\%\s*/) {
|
||||
$np->add_perfdata(label => $label, value => $rel_value, uom => '%',
|
||||
critical => $self->critical, warning => $self->warning);
|
||||
} else {
|
||||
my ($critical,$warning) = $self->_convert_relative_to_absolute($base_value,$self->critical,$self->warning);
|
||||
$np->add_perfdata(label => $label,value => $value,
|
||||
critical => $critical,warning => $warning,
|
||||
min => 0,max => $base_value,
|
||||
$self->unit ? (uom => $self->unit) : ());
|
||||
}
|
||||
}
|
||||
# Do the real check.
|
||||
my ($code,$mode) = $self->_check_threshold($rel_value);
|
||||
# For Multichecks, we remember the label of a currently failed check
|
||||
$self->update_error_stats($opts->{error_stat},$code) unless $code == OK;
|
||||
my ($base_conv,$base_unit) = $self->_normalize_value($base_value);
|
||||
$msg_handler->add_message($code,$self->_exit_message(code => $code,mode => $mode,rel_value => $rel_value,
|
||||
value => $value_conv, unit => $unit, base => $base_conv,
|
||||
base_unit => $base_unit, prefix => $opts->{prefix}));
|
||||
} else {
|
||||
# Performance data
|
||||
$value = $self->_sanitize_value($value);
|
||||
if ($self->_include_perf_data) {
|
||||
$np->add_perfdata(label => $label,
|
||||
critical => $self->critical, warning => $self->warning,
|
||||
value => $value,$self->unit ? (uom => $self->unit) : ());
|
||||
}
|
||||
|
||||
# Do the real check.
|
||||
my ($code,$mode) = $self->_check_threshold($value);
|
||||
$self->update_error_stats($opts->{error_stat},$code) unless $code == OK;
|
||||
$msg_handler->add_message($code,$self->_exit_message(code => $code,mode => $mode,value => $value_conv, unit => $unit,
|
||||
prefix => $opts->{prefix}));
|
||||
}
|
||||
return @extra_requests;
|
||||
}
|
||||
|
||||
sub _include_perf_data {
|
||||
my $self = shift;
|
||||
# No perf dara for string based checks by default
|
||||
my $default = not defined($self->string);
|
||||
# If 'PerfData' is set explicitely to false/off/no/0 then no perfdata
|
||||
# will be included
|
||||
return $default unless defined($self->perfdata);
|
||||
return $self->perfdata !~ /^\s*(false|off|no|0)\s*$/i;
|
||||
}
|
||||
|
||||
sub update_error_stats {
|
||||
my $self = shift;
|
||||
my $error_stat = shift || return;
|
||||
my $code = shift;
|
||||
|
||||
my $label = $self->{config}->{name} || $self->{config}->{key};
|
||||
if ($label) {
|
||||
my $arr = $error_stat->{$code} || [];
|
||||
push @$arr,$label;
|
||||
$error_stat->{$code} = $arr;
|
||||
}
|
||||
}
|
||||
|
||||
# Extract a single value, which is different, if the request was a pattern read
|
||||
# request
|
||||
sub _extract_value {
|
||||
my $self = shift;
|
||||
my $req = shift;
|
||||
my $resp = shift;
|
||||
if ($req->get('type') eq READ && $req->is_mbean_pattern) {
|
||||
return $self->_extract_value_from_pattern_request($resp->value);
|
||||
} else {
|
||||
return $self->_null_safe_value($resp->value);
|
||||
}
|
||||
}
|
||||
|
||||
sub _null_safe_value {
|
||||
my $self = shift;
|
||||
my $value = shift;
|
||||
if (defined($value)) {
|
||||
if (JSON::is_bool($value)) {
|
||||
return $value ? "true" : "false";
|
||||
} elsif (ref($value) && $self->string) {
|
||||
# We can deal with complex values withing string comparison
|
||||
if (ref($value) eq "ARRAY") {
|
||||
return join ",",@{$value};
|
||||
} else {
|
||||
return Dumper($value);
|
||||
}
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
} else {
|
||||
# Our null value
|
||||
return defined($self->null) ? $self->null : "null";
|
||||
}
|
||||
}
|
||||
|
||||
sub _extract_value_from_pattern_request {
|
||||
my $self = shift;
|
||||
my $val = shift;
|
||||
my $np = $self->{np};
|
||||
$self->_die("Pattern request does not result in a proper return format: " . Dumper($val))
|
||||
if (ref($val) ne "HASH");
|
||||
$self->_die("More than one MBean found for a pattern request: " . Dumper([keys %$val])) if keys %$val != 1;
|
||||
my $attr_val = (values(%$val))[0];
|
||||
$self->_die("Invalid response for pattern match: " . Dumper($attr_val)) unless ref($attr_val) eq "HASH";
|
||||
$self->_die("Only a single attribute can be used. Given: " . Dumper([keys %$attr_val])) if keys %$attr_val != 1;
|
||||
return $self->_null_safe_value((values(%$attr_val))[0]);
|
||||
}
|
||||
|
||||
sub _delta_value {
|
||||
my ($self,$req,$resp,$delta) = @_;
|
||||
|
||||
my $history = $resp->history;
|
||||
if (!$history) {
|
||||
# No delta on the first run
|
||||
return undef;
|
||||
} else {
|
||||
my $hist_val;
|
||||
if ($req->is_mbean_pattern) {
|
||||
$hist_val = $self->_extract_value_from_pattern_request($history);
|
||||
} else {
|
||||
$hist_val = $history;
|
||||
}
|
||||
if (!@$hist_val) {
|
||||
# Can happen in some scenarios when requesting the first history entry,
|
||||
# we return 0 here
|
||||
return 0;
|
||||
}
|
||||
my $old_value = $hist_val->[0]->{value};
|
||||
my $old_time = $hist_val->[0]->{timestamp};
|
||||
my $value = $self->_extract_value($req,$resp);
|
||||
if ($delta) {
|
||||
# Time average
|
||||
my $time_delta = $resp->timestamp - $old_time;
|
||||
return (($value - $old_value) / ($time_delta ? $time_delta : 1)) * $delta;
|
||||
} else {
|
||||
return $value - $old_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub _switch_on_history {
|
||||
my ($self,$orig_request,$target) = @_;
|
||||
my ($mbean,$operation) = ("jolokia:type=Config","setHistoryEntriesForAttribute");
|
||||
# Set history to 1 (we need only the last)
|
||||
return new JMX::Jmx4Perl::Request
|
||||
(EXEC,$mbean,$operation,
|
||||
$orig_request->get("mbean"),$orig_request->get("attribute"),$orig_request->get("path"),
|
||||
$target ? $target->{url} : undef,1,{target => undef});
|
||||
}
|
||||
|
||||
|
||||
sub _base_value {
|
||||
my $self = shift;
|
||||
my $np = $self->{np};
|
||||
my $name = shift;
|
||||
my $responses = shift;
|
||||
my $requests = shift;
|
||||
|
||||
if (looks_like_number($name)) {
|
||||
# It looks like a number, so we suppose its the base value itself
|
||||
return $name;
|
||||
}
|
||||
my $resp = shift @{$responses};
|
||||
my $req = shift @{$requests};
|
||||
$self->_die($resp->{error}) if $resp->{error};
|
||||
#print Dumper($req,$resp);
|
||||
return $self->_extract_value($req,$resp);
|
||||
}
|
||||
|
||||
# Normalize value if a unit-of-measurement is given.
|
||||
|
||||
# Units and how to convert from one level to the next
|
||||
my @UNITS = ([ qw(ns us ms s m h d) ],[qw(B KB MB GB TB)]);
|
||||
my %UNITS =
|
||||
(
|
||||
ns => 1,
|
||||
us => 10**3,
|
||||
ms => 10**3,
|
||||
s => 10**3,
|
||||
m => 60,
|
||||
h => 60,
|
||||
d => 24,
|
||||
|
||||
B => 1,
|
||||
KB => 2**10,
|
||||
MB => 2**10,
|
||||
GB => 2**10,
|
||||
TB => 2**10
|
||||
);
|
||||
|
||||
sub _normalize_value {
|
||||
my $self = shift;
|
||||
my $value = shift;
|
||||
my $unit = shift || $self->unit || return ($value,undef);
|
||||
|
||||
for my $units (@UNITS) {
|
||||
for my $i (0 .. $#{$units}) {
|
||||
next unless $units->[$i] eq $unit;
|
||||
my $ret = $value;
|
||||
my $u = $unit;
|
||||
if (abs($ret) > 1) {
|
||||
# Go up the scale ...
|
||||
return ($value,$unit) if $i == $#{$units};
|
||||
for my $j ($i+1 .. $#{$units}) {
|
||||
if (abs($ret / $UNITS{$units->[$j]}) >= 1) {
|
||||
$ret /= $UNITS{$units->[$j]};
|
||||
$u = $units->[$j];
|
||||
} else {
|
||||
return ($ret,$u);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# Go down the scale ...
|
||||
return ($value,$unit) if $i == 0;
|
||||
for my $j (reverse(0 .. $i-1)) {
|
||||
if ($ret < 1) {
|
||||
$ret *= $UNITS{$units->[$j+1]};
|
||||
$u = $units->[$j];
|
||||
} else {
|
||||
return ($ret,$u);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return ($ret,$u);
|
||||
}
|
||||
}
|
||||
die "Unknown unit '$unit' for value $value";
|
||||
}
|
||||
|
||||
sub _sanitize_value {
|
||||
my ($self,$value) = @_;
|
||||
if ($value =~ /\de/i) {
|
||||
$value = sprintf("%f", $value);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
sub _verify_response {
|
||||
my ($self,$req,$resp) = @_;
|
||||
my $np = $self->{np};
|
||||
if ($resp->is_error) {
|
||||
my $extra = "";
|
||||
if ($np->opts->{verbose}) {
|
||||
my $stacktrace = $resp->stacktrace;
|
||||
$extra = ref($stacktrace) eq "ARRAY" ? join "\n",@$stacktrace : $stacktrace if $stacktrace;
|
||||
}
|
||||
$self->_die("Error: ".$resp->status." ".$resp->error_text.$extra);
|
||||
}
|
||||
|
||||
if (!$req->is_mbean_pattern && (ref($resp->value) && !$self->string) && !JSON::is_bool($resp->value)) {
|
||||
$self->_die("Response value is a " . ref($resp->value) .
|
||||
", not a plain value. Did you forget a --path parameter ?". " Value: " .
|
||||
Dumper($resp->value));
|
||||
}
|
||||
}
|
||||
|
||||
sub _get_name {
|
||||
my $self = shift;
|
||||
my $args = { @_ };
|
||||
my $name = $args->{name};
|
||||
if (!$name) {
|
||||
if ($self->name) {
|
||||
$name = $self->name;
|
||||
} else {
|
||||
# Default name, tried to be generated from various parts
|
||||
if ($self->alias) {
|
||||
$name = "[".$self->alias.($self->path ? "," . $self->path : "") ."]";
|
||||
} else {
|
||||
my $val = $self->value;
|
||||
if ($val) {
|
||||
$name = "[" . $val . "]";
|
||||
} else {
|
||||
my $a_or_o = $self->attribute || $self->operation || "";
|
||||
my $p = $self->path ? "," . $self->path : "";
|
||||
$name = "[" . $self->mbean . "," . $a_or_o . $p . "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($args->{cleanup}) {
|
||||
# Enable this when '=' gets forbidden
|
||||
$name =~ s/=/#/g;
|
||||
}
|
||||
# Prepare label for usage with Monitoring::Plugin, which will blindly
|
||||
# add quotes if a space is contained in the label.
|
||||
# We are doing the escape of quotes ourself here
|
||||
$name =~ s/'/''/g;
|
||||
return $name;
|
||||
}
|
||||
|
||||
sub _prepare_read_args {
|
||||
my $self = shift;
|
||||
my $np = $self->{np};
|
||||
my $jmx = shift;
|
||||
|
||||
if ($self->alias) {
|
||||
my @req_args = $jmx->resolve_alias($self->alias);
|
||||
$self->_die("Cannot resolve attribute alias ",$self->alias()) unless @req_args > 0;
|
||||
if ($self->path) {
|
||||
@req_args == 2 ? $req_args[2] = $self->path : $req_args[2] .= "/" . $self->path;
|
||||
}
|
||||
return @req_args;
|
||||
} elsif ($self->value) {
|
||||
return $self->_split_attr_spec($self->value);
|
||||
} else {
|
||||
return ($self->mbean,$self->attribute,$self->path);
|
||||
}
|
||||
}
|
||||
|
||||
sub _prepare_exec_args {
|
||||
my $self = shift;
|
||||
my $np = $self->{np};
|
||||
my $jmx = shift;
|
||||
|
||||
#print Dumper($self->{config});
|
||||
# Merge CLI arguments and arguments from the configuration,
|
||||
# with CLI arguments taking precedence
|
||||
my @cli_args = @_;
|
||||
my $config_args = $self->{config}->{argument};
|
||||
|
||||
$config_args = [ $config_args ] if defined($config_args) && ref($config_args) ne "ARRAY";
|
||||
my @args = ();
|
||||
if ($config_args) {
|
||||
my @c_args = (@$config_args);
|
||||
while (@cli_args || @c_args) {
|
||||
my $cli_arg = shift @cli_args;
|
||||
my $config_arg = shift @c_args;
|
||||
push @args, defined($cli_arg) ? $cli_arg : $config_arg;
|
||||
}
|
||||
} else {
|
||||
@args = @cli_args;
|
||||
}
|
||||
if ($self->alias) {
|
||||
my @req_args = $jmx->resolve_alias($self->alias);
|
||||
$self->_die("Cannot resolve operation alias ",$self->alias()) unless @req_args >= 2;
|
||||
return (@req_args,@args);
|
||||
} else {
|
||||
return ($self->mbean,$self->operation,@args);
|
||||
}
|
||||
}
|
||||
|
||||
sub _split_attr_spec {
|
||||
my $self = shift;
|
||||
my $name = shift;
|
||||
my @ret = ();
|
||||
# Text:ParseWords is used for split on "/" taking into account
|
||||
# quoting and escaping
|
||||
for my $p (parse_line("/",1,$name)) {
|
||||
# We need to 'unescape' things ourselves
|
||||
# since we want quotes to remain in the names (using '0'
|
||||
# above would kill those quotes, too).
|
||||
$p =~ s|\\(.)|$1|sg;
|
||||
push @ret,$p;
|
||||
}
|
||||
return (shift(@ret),shift(@ret),@ret ? join("/",@ret) : undef);
|
||||
}
|
||||
|
||||
sub _check_threshold {
|
||||
my $self = shift;
|
||||
my $value = shift;
|
||||
my $np = $self->{np};
|
||||
my $numeric_check;
|
||||
if ($self->numeric || $self->string) {
|
||||
$numeric_check = $self->numeric ? 1 : 0;
|
||||
} else {
|
||||
$numeric_check = looks_like_number($value);
|
||||
}
|
||||
if ($numeric_check) {
|
||||
# Verify numeric thresholds
|
||||
my @ths =
|
||||
(
|
||||
defined($self->critical) ? (critical => $self->critical) : (),
|
||||
defined($self->warning) ? (warning => $self->warning) : ()
|
||||
);
|
||||
#print Dumper({check => $value,@ths});
|
||||
return (@ths ? $np->check_threshold(check => $value,@ths) : OK,"numeric");
|
||||
} else {
|
||||
return
|
||||
($self->_check_string_threshold($value,CRITICAL,$self->critical) ||
|
||||
$self->_check_string_threshold($value,WARNING,$self->warning) ||
|
||||
OK,
|
||||
$value =~ /^true|false$/i ? "boolean" : "string");
|
||||
}
|
||||
}
|
||||
|
||||
sub _check_string_threshold {
|
||||
my $self = shift;
|
||||
my ($value,$level,$check_value) = @_;
|
||||
return undef unless $check_value;
|
||||
if ($check_value =~ m|^\s*qr(.)(.*)\1\s*$|) {
|
||||
return $value =~ m/$2/ ? $level : undef;
|
||||
}
|
||||
if ($check_value =~ s/^\!//) {
|
||||
return $value ne $check_value ? $level : undef;
|
||||
} else {
|
||||
return $value eq $check_value ? $level : undef;
|
||||
}
|
||||
}
|
||||
|
||||
sub _convert_relative_to_absolute {
|
||||
my $self = shift;
|
||||
my ($base_value,@to_convert) = @_;
|
||||
my @ret = ();
|
||||
for my $v (@to_convert) {
|
||||
$v =~ s|([\d\.]+)|($1 / 100) * $base_value|eg if $v;
|
||||
push @ret,$v;
|
||||
}
|
||||
return @ret;
|
||||
}
|
||||
|
||||
# Prepare an exit message depending on the result of
|
||||
# the check itself. Quite evolved, you can overwrite this always via '--label'.
|
||||
sub _exit_message {
|
||||
my $self = shift;
|
||||
my $args = { @_ };
|
||||
# Custom label has precedence
|
||||
return $self->_format_label($self->label,$args) if $self->label;
|
||||
|
||||
my $code = $args->{code};
|
||||
my $mode = $args->{mode};
|
||||
if ($code == CRITICAL || $code == WARNING) {
|
||||
if ($self->base || $self->base_mbean) {
|
||||
return $self->_format_label
|
||||
('%n : Threshold \'%t\' failed for value %.2r% ('. &_placeholder($args,"v") .' %u / '.
|
||||
&_placeholder($args,"b") . ' %u)',$args);
|
||||
} else {
|
||||
if ($mode ne "numeric") {
|
||||
return $self->_format_label('%n : \'%v\' matches threshold \'%t\'',$args);
|
||||
} else {
|
||||
return $self->_format_label
|
||||
('%n : Threshold \'%t\' failed for value '.&_placeholder($args,"v").' %u',$args);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($self->base || $self->base_mbean) {
|
||||
return $self->_format_label('%n : In range %.2r% ('. &_placeholder($args,"v") .' %u / '.
|
||||
&_placeholder($args,"b") . ' %w)',$args);
|
||||
} else {
|
||||
if ($mode ne "numeric") {
|
||||
return $self->_format_label('%n : \'%v\' as expected',$args);
|
||||
} else {
|
||||
return $self->_format_label('%n : Value '.&_placeholder($args,"v").' %u in range',$args);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
sub _placeholder {
|
||||
my ($args,$c) = @_;
|
||||
my $val;
|
||||
if ($c eq "v") {
|
||||
$val = $args->{value};
|
||||
} else {
|
||||
$val = $args->{base};
|
||||
}
|
||||
return ($val =~ /\./ ? "%.2" : "%") . $c;
|
||||
}
|
||||
|
||||
sub _format_label {
|
||||
my $self = shift;
|
||||
my $label = shift;
|
||||
my $args = shift;
|
||||
# %r : relative value (as percent)
|
||||
# %q : relative value (as floating point)
|
||||
# %v : value
|
||||
# %f : value as floating point
|
||||
# %u : unit
|
||||
# %b : base value
|
||||
# %w : base unit
|
||||
# %t : threshold failed ("" for OK or UNKNOWN)
|
||||
# %c : code ("OK", "WARNING", "CRITICAL", "UNKNOWN")
|
||||
# %d : delta
|
||||
#
|
||||
my @parts = split /(\%[\w\.\-]*\w)/,$label;
|
||||
my $ret = "";
|
||||
foreach my $p (@parts) {
|
||||
if ($p =~ /^(\%[\w\.\-]*)(\w)$/) {
|
||||
my ($format,$what) = ($1,$2);
|
||||
if ($what eq "r" || $what eq "q") {
|
||||
my $val = $args->{rel_value} || 0;
|
||||
$val = $what eq "r" ? $val : $val / 100;
|
||||
$ret .= sprintf $format . "f",$val;
|
||||
} elsif ($what eq "b") {
|
||||
$ret .= sprintf $format . &_format_char($args->{base}),($args->{base} || 0);
|
||||
} elsif ($what eq "u" || $what eq "w") {
|
||||
$ret .= sprintf $format . "s",($what eq "u" ? $args->{unit} : $args->{base_unit}) || "";
|
||||
$ret =~ s/\s$//;
|
||||
} elsif ($what eq "f") {
|
||||
$ret .= sprintf $format . "f",$args->{value};
|
||||
} elsif ($what eq "v") {
|
||||
$ret .= &_format_value($format,$args->{mode},$args->{value});
|
||||
} elsif ($what eq "t") {
|
||||
my $code = $args->{code};
|
||||
my $val = $code == CRITICAL ? $self->critical : ($code == WARNING ? $self->warning : "");
|
||||
$ret .= sprintf $format . "s",defined($val) ? $val : "";
|
||||
} elsif ($what eq "c") {
|
||||
$ret .= sprintf $format . "s",$STATUS_TEXT{$args->{code}};
|
||||
} elsif ($what eq "n") {
|
||||
$ret .= sprintf $format . "s",$self->_get_name();
|
||||
} elsif ($what eq "d") {
|
||||
$ret .= sprintf $format . "d",$self->delta;
|
||||
} elsif ($what eq "y") {
|
||||
$ret .= &_format_value($format,$args->{mode},$self->warning);
|
||||
} elsif ($what eq "z") {
|
||||
$ret .= &_format_value($format,$args->{mode},$self->critical);
|
||||
}
|
||||
} else {
|
||||
$ret .= $p;
|
||||
}
|
||||
}
|
||||
if ($args->{prefix}) {
|
||||
my $prefix = $args->{prefix};
|
||||
$prefix =~ s/\%c/$STATUS_TEXT{$args->{code}}/g;
|
||||
return $prefix . $ret;
|
||||
} else {
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
sub _format_value {
|
||||
my $format = shift;
|
||||
my $mode = shift;
|
||||
my $value = shift;
|
||||
if ($mode ne "numeric") {
|
||||
return sprintf $format . "s",$value;
|
||||
} else {
|
||||
return sprintf $format . &_format_char($value),$value;
|
||||
}
|
||||
}
|
||||
sub _format_char {
|
||||
my $val = shift;
|
||||
$val =~ /\./ ? "f" : "d";
|
||||
}
|
||||
|
||||
sub _die {
|
||||
my $self = shift;
|
||||
my $msg = join("",@_);
|
||||
die $msg,"\n";
|
||||
}
|
||||
|
||||
my $CHECK_CONFIG_KEYS = {
|
||||
"critical" => "critical",
|
||||
"warning" => "warning",
|
||||
"mbean" => "mbean",
|
||||
"attribute" => "attribute",
|
||||
"operation" => "operation",
|
||||
"alias" => "alias",
|
||||
"path" => "path",
|
||||
"delta" => "delta",
|
||||
"name" => "name",
|
||||
"base" => "base",
|
||||
"base-mbean" => "basembean",
|
||||
"base-attribute" => "baseattribute",
|
||||
"base-path" => "basepath",
|
||||
"unit" => "unit",
|
||||
"numeric" => "numeric",
|
||||
"string" => "string",
|
||||
"label" => "label",
|
||||
"perfdata" => "perfdata",
|
||||
"value" => "value",
|
||||
"null" => "null",
|
||||
"script" => "script"
|
||||
};
|
||||
|
||||
|
||||
# Get the proper configuration values
|
||||
|
||||
sub AUTOLOAD {
|
||||
my $self = shift;
|
||||
my $np = $self->{np};
|
||||
my $name = $AUTOLOAD;
|
||||
$name =~ s/.*://; # strip fully-qualified portion
|
||||
$name =~ s/_/-/g;
|
||||
|
||||
if ($CHECK_CONFIG_KEYS->{$name}) {
|
||||
return $np->opts->{$name} if defined($np->opts->{$name});
|
||||
if ($self->{config}) {
|
||||
return $self->{config}->{$CHECK_CONFIG_KEYS->{$name}};
|
||||
} else {
|
||||
return undef;
|
||||
}
|
||||
} else {
|
||||
$self->_die("No config attribute \"" . $name . "\" known");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# To keep autoload happy
|
||||
sub DESTROY {
|
||||
|
||||
}
|
||||
|
||||
=back
|
||||
|
||||
=head1 LICENSE
|
||||
|
||||
This file is part of jmx4perl.
|
||||
|
||||
Jmx4perl 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.
|
||||
|
||||
jmx4perl 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 jmx4perl. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
roland@cpan.org
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
||||
Reference in New Issue
Block a user