File check_racktables_problem of Package monitoring-plugins-racktables_problem
#!/usr/bin/perl -w
# nagios: -epn
#
# check_racktables_problem - nagios plugin
#
# Copyright (C) SUSE Linux GmbH, Nuremberg
# Author: Lars Vogdt <Lars.Vogdt@suse.com>
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of the Novell nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
use strict;
use warnings;
use Carp;
use Getopt::Long;
use Pod::Usage;
# cleanup the environment
$ENV{'PATH'} = '/bin:/usr/bin:/sbin:/usr/sbin:';
$ENV{'BASH_ENV'} = '';
$ENV{'ENV'} = '';
our $conf = {
VERSION => '1.4',
PROGNAME => 'check_racktables_problem',
mysql_config => '/etc/monitoring-plugins/check_racktables_problem.ini',
html_output => 0,
timeout => 120,
critical => 4,
warning => 8,
debug => 0
};
our $print_version = 0;
our $print_help = 0;
our $exitcode = 0;
our $hostname = '';
our $tag = '';
our %ERRORS = (
'OK' => 0,
'WARNING' => 1,
'CRITICAL' => 2,
'UNKNOWN' => 3,
'DEPENDENT' => 4
);
#######################################################################
# Functions
#######################################################################
sub DEBUG($) {
my ($output) = @_;
print "DEBUG: $output\n" if ( $conf->{'debug'} );
}
sub read_data_from_racktables_db($$$) {
use Config::Simple;
use DBI;
my ( $mysql_config, $host, $tag ) = @_;
my %data;
$data{'description'} = "UNKNOWN";
$data{'errorcode'} = $ERRORS{"UNKNOWN"};
$data{'problemcount'} = 0;
my $cfg = new Config::Simple("$mysql_config")
or croak "Could not open $mysql_config : $!\n";
my $dbHost = $cfg->{'_DATA'}{'default'}{'MYSQL_HOST'}[0]
if ( defined( $cfg->{'_DATA'}{'default'}{'MYSQL_HOST'}[0] ) );
my $dbName = $cfg->{'_DATA'}{'default'}{'MYSQL_DB'}[0]
if ( defined( $cfg->{'_DATA'}{'default'}{'MYSQL_DB'}[0] ) );
my $dbUser = $cfg->{'_DATA'}{'default'}{'MYSQL_USER'}[0]
if ( defined( $cfg->{'_DATA'}{'default'}{'MYSQL_USER'}[0] ) );
my $dbPass = $cfg->{'_DATA'}{'default'}{'MYSQL_PASS'}[0]
if ( defined( $cfg->{'_DATA'}{'default'}{'MYSQL_PASS'}[0] ) );
my $db_ssl_ca_file = $cfg->{'_DATA'}{'default'}{'MYSQL_CA_FILE'}[0]
if (
defined( $cfg->{'_DATA'}{'default'}{'MYSQL_CA_FILE'}[0] ) );
my $url = $cfg->{'_DATA'}{'default'}{'RACKTABLES_URL'}[0]
|| 'http://localhost/';
# if ( defined( $cfg->{'_DATA'}{'default'}{'RACKTABLES_URL'}[0];
my %db_opts = ( 'RaiseError' => 1 );
if ( defined($db_ssl_ca_file) && -r "$db_ssl_ca_file" ) {
$db_opts{'mysql_ssl'} = 1;
$db_opts{'mysql_ssl_ca_file'} = "$db_ssl_ca_file";
DEBUG(
"Connecting via SSL to $dbName on $dbHost using $db_ssl_ca_file"
);
}
my $dbh = DBI->connect( "DBI:mysql:database=$dbName;host=$dbHost",
"$dbUser", "$dbPass", \%db_opts )
or croak "Can't connect to MySQL-Database: "
. $DBI::errstr . "\n";
my $query;
if ("$hostname" ne ""){
$query="SELECT id,name,comment FROM RackObject WHERE name=?";
}
elsif ("$tag" ne ""){
$query="SELECT RackObject.id AS id,
RackObject.name AS name,
RackObject.comment AS comment,
TagTree.tag AS tag
FROM RackObject
LEFT JOIN TagStorage
ON TagStorage.entity_id = RackObject.id
LEFT JOIN TagTree
ON TagTree.id = TagStorage.tag_id
WHERE has_problems='yes'
AND TagTree.tag=?";
}
else {
$query="SELECT id,name,comment FROM RackObject WHERE has_problems='yes'";
}
my $sth=$dbh->prepare($query);
if ("$hostname" ne ""){
$sth->execute($hostname);
}
elsif ("$tag" ne ""){
$sth->execute($tag);
}
else {
$sth->execute();
}
my $results=$sth->fetchall_hashref('id');
if ( defined($results) ){
$data{'db_results'} = $results;
my $count=keys %$results;
if (($count == 0) && ("$hostname" ne "")){
$data{'description'} = "UNKOWN: object $hostname not found in RackTables";
$data{'errorcode'} = $ERRORS{"UNKNOWN"};
$data{'problemcount'} = 0;
}
else {
$data{'description'} = "CRITICAL: Found ".$count." objects with problems in RackTables:";
foreach my $problem (sort(keys %$results)){
if ($count == 1){
if (defined($results->{$problem}->{'comment'})){
$data{'problemcount'} = 1;
$data{'description'} = "CRITICAL: Problem description for object ";
if ($conf->{'html_output'}){
$data{'description'} .= "<a href=\"$url/index.php?page=object&tab=default&object_id=$problem\">"
.$results->{$problem}->{'name'}.
"</a> (ID: <a href=\"$url/index.php?page=object&tab=default&object_id=$problem\">".$problem."</a>): ";
}
else {
$data{'description'} .= $results->{$problem}->{'name'}." (ID: ".$problem."): ";
}
$data{'description'} .= $results->{$problem}->{'comment'}.";";
$data{'errorcode'} = $ERRORS{"CRITICAL"};
} else {
$data{'description'} = "OK: no problems found for object ";
if ($conf->{'html_output'}){
$data{'description'} .= "<a href=\"$url/index.php?page=object&tab=default&object_id=$problem\">"
.$results->{$problem}->{'name'}.
"</a> (ID: <a href=\"$url/index.php?page=object&tab=default&object_id=$problem\">".$problem."</a>)";
}
else {
$data{'description'} .= $results->{$problem}->{'name'}." (ID: ".$problem.")";
}
$data{'problemcount'} = 0;
$data{'errorcode'} = $ERRORS{"OK"};
}
}
else {
$data{'problemcount'}++;
$results->{$problem}->{'name'} = "unknown" if (!defined($results->{$problem}->{'name'}));
$results->{$problem}->{'comment'} = "" if (!defined($results->{$problem}->{'comment'}));
$data{'description'} .= "\n$data{'problemcount'}. Problem description for object ";
if ($conf->{'html_output'}){
$data{'description'} .= "<a href=\"$url/index.php?page=object&tab=default&object_id=$problem\">"
.$results->{$problem}->{'name'}.
"</a> (ID: <a href=\"$url/index.php?page=object&tab=default&object_id=$problem\">".$problem."</a>): ";
}
else {
$data{'description'} .= $results->{$problem}->{'name'}." (ID: ".$problem."): ";
}
if ($conf->{'html_output'}){
$data{'description'} .= $results->{$problem}->{'comment'}.";<br>";
} else {
$data{'description'} .= $results->{$problem}->{'comment'}.";";
}
$data{'errorcode'} = $ERRORS{"CRITICAL"};
}
}
}
}
else {
$data{'problemcount'} = 0;
$data{'description'} = "OK: no objects with problem reports found in RackTables\n";
$data{'errorcode'} = $ERRORS{"OK"};
}
$dbh->disconnect();
return \%data;
}
sub print_myrevision ($$) {
my $commandName = shift;
my $pluginRevision = shift;
print "$commandName v$pluginRevision\n";
}
#######################################################################
# Main
#######################################################################
Getopt::Long::Configure('bundling');
GetOptions(
"H=s" => \$hostname,
"hostname=s" => \$hostname,
"T=s" => \$tag,
"Tag=s" => \$tag,
"v" => \$print_version,
"version" => \$print_version,
"h" => \$print_help,
"help" => \$print_help,
"d" => \$conf->{'debug'},
"debug" => \$conf->{'debug'},
"i" => \$conf->{'ignore'},
"ignore" => \$conf->{'ignore'},
"w=i" => \$conf->{'warning'},
"warning=i" => \$conf->{'warning'},
"c=i" => \$conf->{'critical'},
"critical=i" => \$conf->{'critical'},
"m=s" => \$conf->{'mysql_config'},
"mysql=s" => \$conf->{'mysql_config'},
"html" => \$conf->{'html_output'},
"t=i" => \$conf->{'timeout'},
"timeout=i" => \$conf->{'timeout'},
) or pod2usage(2);
pod2usage(
-exitstatus => 0,
-verbose => 2, # 2 to print full pod
) if $print_help;
# Just in case of problems, let's not hang Nagios
$SIG{'ALRM'} = sub {
print "UNKNOWN - Plugin timed out\n";
exit $ERRORS{"UNKNOWN"};
};
alarm( $conf->{'timeout'} );
if ($print_version) {
print_myrevision( $conf->{'PROGNAME'}, $conf->{'VERSION'} );
exit $ERRORS{'OK'};
}
# check the options...
if ( !defined( $conf->{'mysql_config'} ) || ! -r "$conf->{'mysql_config'}" ){
print "CONFIG ERROR - file "
. $conf->{'mysql_config'}
. " does not exist or is not readable\n";
pod2usage(2);
alarm(0);
exit $ERRORS{"UNKNOWN"};
}
if ( $conf->{'warning'} <= $conf->{'critical'} ) {
print "CONFIG ERROR - critical value ("
. $conf->{'critical'}
. ") must be lower than warning value ("
. $conf->{'warning'} . ")\n";
pod2usage(2);
alarm(0);
exit $ERRORS{"UNKNOWN"};
}
my $dataref;
if ("$hostname" ne ""){
$hostname = lc($hostname);
print STDERR "Checking Racktables for host: $hostname\n" if ( $conf->{'debug'} );
}
elsif ("$tag" ne ""){
print STDERR "Checking Racktables for hosts with tag: $tag\n" if ( $conf->{'debug'} );
}
else {
print STDERR "Checking Racktables for all problems\n" if ( $conf->{'debug'} );
}
$dataref = read_data_from_racktables_db($conf->{'mysql_config'}, $hostname, $tag);
if ( $conf->{'debug'} ) {
use Data::Dumper;
print STDERR "Config:\n" . Data::Dumper->Dump( [$conf] ) . "\n";
print STDERR "Config:\n" . Data::Dumper->Dump( [$dataref] ) . "\n";
}
print $dataref->{'description'}." | problems=".$dataref->{'problemcount'}."\n";
exit $dataref->{'errorcode'};
__END__
=head1 check_racktables_problem
check_racktables_problem is a Nagios plugin, allowing to check if a RackTables object
has a known problem.
=head1 SYNOPSIS
./check_racktables_problem [-H $HOSTNAME$] -m <file_with_database_information>
Options:
-H <HOSTNAME> | --hostname <HOSTNAME>
-T <Tag> | --Tag <Tag>
-m <file> | --mysql <file>
| --html
-i | --ignore
-h | --help
-d | --debug
-v | --version
=head1 OPTIONS
=over 4
=item B<--hostname> F<hostname>
The name of one host. The check will check if there is a problem reported for this single host
and return with "OK" if no problem is found - or "CRITICAL" if a problem is found.
=item B<--Tag> F<tagname>
Report all problems reported for machines with a specific 'tag'. The Tag is a string that could
be copied directly from the tag list inside Racktables.
=item B<--mysql> F<file>
Read all data from a MySQL database (currently only RackTables >= 0.20.10 is supported). The file contains the login
data for the MySQL database. The standard place/name should be /etc/monitoring-plugins/check_maintenance.ini.
Example:
=over 8
MYSQL_USER=maint_checker
MYSQL_DB=racktables_db
MYSQL_PASS=r@ckta8les
MYSQL_HOST=localhost
RACKTABLES_URL=http://<FQDN_TO_YOUR_RACKTABLES_INSTALLATION>/
=back
The last configuration option in this file only makes sense if you also use the B<--html> option to generate links from the Object-IDs to your Racktables installation.
=item B<--html>
Include HTML links in the detailed status information (please note that this has to be enabled on the monitoring server side).
=item B<--help>
Produces this output.
=item B<--debug>
Print debug output on console.
=item B<--version>
Print version information on console.
=back
=head1 DESCRIPTION
B<check_racktables_problem> allows you to concentrate all problems in your Monitoring
infrastructure. Often enough, there is a problem with a machine or infrastructure related
hardware that can not be found by the monitoring itself. If you entered the informatoin
in RackTables, this check will allow you to get aware of those problems also in your
monitoring.
=head1 AUTHORS
Written by Lars Vogdt <Lars.Vogdt@suse.com>
=head1 SUPPORT
Please use https://bugzilla.opensuse.org/ to submit patches or suggest improvements.
Include version information with all correspondence (when possible use output from
the --version option of the plugin itself).
=cut