summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README72
-rw-r--r--files/configs/Debian/nagios.cfg3
l---------files/configs/Debian/private/resource.cfg.amd641
l---------[-rw-r--r--]files/configs/Debian/private/resource.cfg.x86_6432
-rw-r--r--files/htpasswd.users0
-rw-r--r--files/irc_bot/riseup-nagios-client.pl40
-rw-r--r--files/irc_bot/riseup-nagios-server.pl157
-rw-r--r--files/plugins/check_dns2102
-rw-r--r--files/plugins/check_jabber_login30
-rwxr-xr-xfiles/plugins/check_mysql_health3342
-rw-r--r--lib/facter/mountpoints.rb50
-rw-r--r--manifests/apache.pp2
-rw-r--r--manifests/base.pp137
-rw-r--r--manifests/centos.pp2
-rw-r--r--manifests/command/imap_pop3.pp14
-rw-r--r--manifests/command/smtp.pp14
-rw-r--r--manifests/debian.pp2
-rw-r--r--manifests/defaults.pp2
-rw-r--r--manifests/defaults/commands.pp106
-rw-r--r--manifests/defaults/host_templates.pp12
-rw-r--r--manifests/defaults/hostgroups.pp20
-rw-r--r--manifests/defaults/plugins.pp10
-rw-r--r--manifests/defaults/service_templates.pp18
-rw-r--r--manifests/defaults/templates.pp13
-rw-r--r--manifests/defaults/vars.pp10
-rw-r--r--manifests/irc_bot.pp71
-rw-r--r--manifests/nsca/client.pp6
-rw-r--r--manifests/nsca/server.pp6
-rw-r--r--manifests/plugin.pp9
-rw-r--r--manifests/plugin/deploy.pp30
-rw-r--r--manifests/plugin/scriptpaths.pp6
-rw-r--r--manifests/service/mysql.pp83
-rw-r--r--manifests/service/smtp.pp50
-rw-r--r--manifests/service/ssmtp.pp32
-rw-r--r--manifests/target/fqdn.pp (renamed from manifests/target/nat.pp)2
-rw-r--r--templates/irc_bot/nagios-nsa.sh.erb71
-rw-r--r--templates/irc_bot/nsa.cfg.erb13
37 files changed, 4336 insertions, 234 deletions
diff --git a/README b/README
index cdcb5d3..4ca4c3e 100644
--- a/README
+++ b/README
@@ -12,7 +12,10 @@ In it's current form, this module can be used on CentOS and Debian.
Overview
========
-To use the nagios resources, activate storeconfigs on the puppetmaster.
+To use the nagios resources, activate storeconfigs on the
+puppetmaster.
+
+You need to be running verison 0.25 or later of puppet.
Monitor
@@ -28,8 +31,11 @@ Hosts
-----
On a node which shall be monitored with nagios, include the "nagios::target".
-This just creates a host declaration for this hosts "$fqdn".
-Set the $nagios_parent variable in the node scope for enabling the reachability
+This just creates a host declaration for this host's "$ipaddress" fact. If
+the $ipaddress of your target is not the one you wish to modify, you can use
+"nagios::target::fqdn" instead, which will use the $fqdn fact of the host instead.
+
+Set the $nagios_parents variable in the node scope for enabling the reachability
features of nagios. If a node needs more customisation, use the
native "@@nagios_host" type directly (the double-ampersand declares the object
as an exported resource).
@@ -60,6 +66,37 @@ Obviously, the check command must either be defined using nagios_command objects
files directly.
+Upgrade Notes
+=============
+
+The nagios::target bits have been reworked, the notable changes that
+may affect an upgrade are:
+
+. previous versions had nagios::target::nat which used the $fqdn for
+the address part of nagios::target, this has been renamed to
+nagios::target::fqdn to be more clear. if you were using
+nagios::target::nat then you will need to change those references to
+::fqdn
+
+. previous versions of this module used $fqdn for the nagios::target
+address, now it is using $ipaddress. If you need $fqdn, use
+nagios::target::fqdn instead of nagios::target
+
+. previous versions of nagios_host used the parameter named 'ip', that
+has been changed to 'address'
+
+
+IRC bot
+=======
+
+Notifications can easily be sent to an IRC channel by using a bot. To do so,
+simply include 'nagios::irc_bot' on the nagios server and define the right
+$nagios_nsa_* variables (see the 'Variables' section below).
+
+You can then use the notification commands 'notify-by-irc' and
+'host-notify-by-irc' with service and host definitions to make them report
+state changes over IRC.
+
Caveats
=======
@@ -102,6 +139,29 @@ Options to change the behavior of the nagios module:
may also need to flip "check_external_commands" in
"nagios.cfg" to enable this functionality.
+- nagios_nsa_socket: This optional variable can be used to specify the path to
+ the socket file that the IRC daemon should use.
+
+- nagios_nsa_server: When using the IRC bot, this defines the server address of
+ the IRC network on which the bot will connect.
+
+- nagios_nsa_port: Defines the port number on the IRC server on which the bot
+ should connect. When this variable is not set, the port used
+ by default is 6667.
+
+- nagios_nsa_nickname: This is the nickname that the IRC bot will take.
+
+- nagios_nsa_password: Some networks require a password to connect to them.
+ This defines such a password.
+
+- nagios_nsa_channel: The name of the channel that the IRC bot will join and
+ will post notifications to.
+
+- nagios_nsa_pidfile: This optional variable can be used to define the path to
+ the file that will contain the process ID of the IRC bot
+ daemon.
+- nagios_nsa_realname: The IRC bot user's real name that will be displayed. By
+ default, the real name is 'Nagios'.
Examples
========
@@ -137,8 +197,8 @@ $HOSTADDRESS$'
node target {
- # Monitor this host
- $nagios_parent = 'router01'
+ # Monitor th () is host
+ $nagios_parents = 'router01'
include nagios::target
# monitor a service
@@ -167,3 +227,5 @@ License
Copyright (C) 2007 David Schmitt <david@schmitt.edv-bus.at>
See the file LICENSE in the top directory for the full license.
+Copyright (C) 2010 Riseup Networks <micah@riseup.net>
+
diff --git a/files/configs/Debian/nagios.cfg b/files/configs/Debian/nagios.cfg
index a588f55..8eae393 100644
--- a/files/configs/Debian/nagios.cfg
+++ b/files/configs/Debian/nagios.cfg
@@ -20,13 +20,14 @@ log_file=/var/log/nagios3/nagios.log
# host groups, contacts, contact groups, services, etc.
# You can split your object definitions across several config files
# if you wish (as shown below), or keep them all in a single config file.
+cfg_file=/etc/nagios3/commands.cfg
# Puppet-managed configuration files
cfg_dir=/etc/nagios3/conf.d
# Debian also defaults to using the check commands defined by the debian
# nagios-plugins package
-#cfg_dir=/etc/nagios-plugins/config
+cfg_dir=/etc/nagios-plugins/config
diff --git a/files/configs/Debian/private/resource.cfg.amd64 b/files/configs/Debian/private/resource.cfg.amd64
new file mode 120000
index 0000000..4d5f0a3
--- /dev/null
+++ b/files/configs/Debian/private/resource.cfg.amd64
@@ -0,0 +1 @@
+resource.cfg.i386 \ No newline at end of file
diff --git a/files/configs/Debian/private/resource.cfg.x86_64 b/files/configs/Debian/private/resource.cfg.x86_64
index 3ed732b..4d5f0a3 100644..120000
--- a/files/configs/Debian/private/resource.cfg.x86_64
+++ b/files/configs/Debian/private/resource.cfg.x86_64
@@ -1,31 +1 @@
-###########################################################################
-#
-# RESOURCE.CFG - Resource File for Nagios
-#
-# You can define $USERx$ macros in this file, which can in turn be used
-# in command definitions in your host config file(s). $USERx$ macros are
-# useful for storing sensitive information such as usernames, passwords,
-# etc. They are also handy for specifying the path to plugins and
-# event handlers - if you decide to move the plugins or event handlers to
-# a different directory in the future, you can just update one or two
-# $USERx$ macros, instead of modifying a lot of command definitions.
-#
-# The CGIs will not attempt to read the contents of resource files, so
-# you can set restrictive permissions (600 or 660) on them.
-#
-# Nagios supports up to 32 $USERx$ macros ($USER1$ through $USER32$)
-#
-# Resource files may also be used to store configuration directives for
-# external data sources like MySQL...
-#
-###########################################################################
-
-# Sets $USER1$ to be the path to the plugins
-$USER1$=/usr/lib/nagios/plugins
-
-# Sets $USER2$ to be the path to event handlers
-#$USER2$=/usr/lib/nagios/plugins/eventhandlers
-
-# Store some usernames and passwords (hidden from the CGIs)
-#$USER3$=someuser
-#$USER4$=somepassword
+resource.cfg.i386 \ No newline at end of file
diff --git a/files/htpasswd.users b/files/htpasswd.users
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/files/htpasswd.users
diff --git a/files/irc_bot/riseup-nagios-client.pl b/files/irc_bot/riseup-nagios-client.pl
new file mode 100644
index 0000000..fd28996
--- /dev/null
+++ b/files/irc_bot/riseup-nagios-client.pl
@@ -0,0 +1,40 @@
+#!/usr/bin/perl -w
+
+# ##############################################################################
+# Infrabot-Client - a simple Infrabot client which sends it's whole command
+# line arguments to a local UNIX domain socket.
+# ##############################################################################
+
+use strict;
+use IO::Socket;
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# >> CONFIGURATION >>
+
+my $SOCKET = '/var/run/nagios/nsa.socket';
+
+# << CONFIGURATION <<
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+if (@ARGV == 0) {
+ print "Hey - specify a message, sucker!\n";
+ exit(1);
+}
+
+unless (-S $SOCKET) {
+ die "Socket '$SOCKET' doesn't exist or isn't a socket!\n";
+}
+
+unless (-r $SOCKET) {
+ die "Socket '$SOCKET' can't be read!\n";
+}
+
+my $sock = IO::Socket::UNIX->new (
+ Peer => $SOCKET,
+ Type => SOCK_DGRAM,
+ Timeout => 10
+) || die "Can't open socket '$SOCKET'!\n";
+
+print $sock "@ARGV";
+close($sock);
diff --git a/files/irc_bot/riseup-nagios-server.pl b/files/irc_bot/riseup-nagios-server.pl
new file mode 100644
index 0000000..7880dde
--- /dev/null
+++ b/files/irc_bot/riseup-nagios-server.pl
@@ -0,0 +1,157 @@
+#!/usr/bin/perl -w
+
+# ##############################################################################
+# a simple IRC bot which dispatches messages received via local domain sockets
+# ##############################################################################
+
+use strict;
+use File::Basename;
+
+BEGIN {
+ unshift @INC, dirname($0);
+}
+
+my $VERSION = '0.2';
+my $running = 1;
+
+# Read a configuration file
+# The arg can be a relative or full path, or
+# it can be a file located somewhere in @INC.
+sub ReadCfg
+{
+ my $file = $_[0];
+
+ our $err;
+
+ { # Put config data into a separate namespace
+ package CFG;
+
+ # Process the contents of the config file
+ my $rc = do($file);
+
+ # Check for errors
+ if ($@) {
+ $::err = "ERROR: Failure compiling '$file' - $@";
+ } elsif (! defined($rc)) {
+ $::err = "ERROR: Failure reading '$file' - $!";
+ } elsif (! $rc) {
+ $::err = "ERROR: Failure processing '$file'";
+ }
+ }
+
+ return ($err);
+}
+
+# Get our configuration information
+if (my $err = ReadCfg('/etc/nagios_nsa.cfg')) {
+ print(STDERR $err, "\n");
+ exit(1);
+}
+
+use POSIX qw(setsid);
+use IO::Socket;
+use Net::IRC;
+
+sub new {
+ my $self = {
+ socket => undef,
+ irc => undef,
+ conn => undef
+ };
+
+ return bless($self, __PACKAGE__);
+}
+
+sub daemonize {
+ my $self = shift;
+ my $pid;
+
+ chdir '/' or die "Can't chdir to /: $!";
+
+ open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
+ open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";
+
+ defined ($pid = fork) or die "Can't fork: $!";
+
+ if ($pid && $CFG::Nsa{'pidfile'}) { # write pid of child
+ open PID, ">$CFG::Nsa{'pidfile'}" or die "Can't open pid file: $!";
+ print PID $pid;
+ close PID;
+ }
+ exit if $pid;
+ setsid or die "Can't start a new session: $!";
+
+ #open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
+}
+
+sub run {
+ my $self = shift;
+
+ $self->{irc}->do_one_loop();
+}
+
+sub shutdown {
+ my $sig = shift;
+
+ print STDERR "Received SIG$sig, shutting down...\n";
+ $running = 0;
+}
+
+sub socket_has_data {
+ my $self = shift;
+
+ $self->{socket}->recv(my $data, 1024);
+ $self->{conn}->privmsg($CFG::Nsa{'channel'}, $data);
+}
+
+sub irc_on_connect {
+ my $self = shift;
+
+ print STDERR "Joining channel '$CFG::Nsa{'channel'}'...\n";
+ $self->join($CFG::Nsa{'channel'});
+}
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+my $bot = &new;
+
+if (-e $CFG::Nsa{'socket'}) {
+ die "Socket '$CFG::Nsa{'socket'}' exists!\n";
+}
+
+$bot->{socket} = IO::Socket::UNIX->new (
+ Local => $CFG::Nsa{'socket'},
+ Type => SOCK_DGRAM,
+ Listen => 5
+) || die "Can't create socket '$CFG::Nsa{'socket'}'!\n";
+
+$SIG{INT} = $SIG{TERM} = \&shutdown;
+
+$bot->daemonize();
+$bot->{irc} = new Net::IRC;
+
+$bot->{conn} = $bot->{irc}->newconn (
+ Server => $CFG::Nsa{'server'},
+ Port => $CFG::Nsa{'port'},
+ Nick => $CFG::Nsa{'nickname'},
+ Username => $CFG::Nsa{'nickname'},
+ Password => $CFG::Nsa{'password'},
+ Ircname => $CFG::Nsa{'realname'} . " (NSA $VERSION)",
+) || die "Can't connect to server '$CFG::Nsa{'server'}'!\n";
+
+$bot->{conn}->add_global_handler(376, \&irc_on_connect);
+$bot->{conn}->add_global_handler('nomotd', \&irc_on_connect);
+$bot->{irc}->addfh($bot->{socket}, \&socket_has_data, 'r', $bot);
+
+while ($running) {
+ $bot->run();
+}
+
+close($bot->{socket});
+unlink($CFG::Nsa{'socket'});
+
+exit(0);
+
+1;
+
+__END__
diff --git a/files/plugins/check_dns2 b/files/plugins/check_dns2
new file mode 100644
index 0000000..2195631
--- /dev/null
+++ b/files/plugins/check_dns2
@@ -0,0 +1,102 @@
+#!/bin/bash
+# Written by Damien Gy
+# damien.gy+nagiosexchange(AT)gmail.com
+# 2007-09-28
+
+PROGNAME=`basename $0`
+REVISION=1.00
+TMP=/tmp/tmpdig
+DIG=/usr/bin/dig
+
+print_revision() {
+ echo $PROGNAME $REVISION
+}
+
+print_usage() {
+ echo "Usage:"
+ echo " $PROGNAME -c|--check <host> <type> <server>"
+ echo " $PROGNAME -h|--help"
+ echo " $PROGNAME -v|--version"
+}
+
+print_help() {
+ print_revision
+ echo ""
+ print_usage
+ echo "Where:"
+ echo " host the name of the resource record to be looked up"
+ echo " type indicates the query required (any, a, mx, etc.)"
+ echo " server the name or IP address of the name server to query"
+ echo ""
+ echo " -h|--help prints this help screen"
+ echo ""
+ echo " -v|--version prints version and license information"
+ echo ""
+ echo " Created by Damien Gy, questions or problems e-mail damien.gy+nagiosexchange(AT)gmail.com"
+ echo ""
+}
+
+check_dns() {
+
+ if [ $# -ne 3 ]
+ then
+ echo "Number of arguments incorrect"
+ exit 3
+ fi
+ if [ ! -e $DIG ]
+ then
+ echo "$DIG not found"
+ exit 3
+ fi
+ $DIG $1 $2 @$3 > $TMP
+
+ if ( grep "status" $TMP > /dev/null )
+ then
+ # DNS server answered
+ if ( grep "NOERROR" $TMP > /dev/null )
+ then
+ echo "DNS OK "`grep "time:" $TMP`
+ rm -f $TMP
+ exit 0
+ else
+ echo "WARNING "`grep "time:" $TMP`
+ rm -f $TMP
+ exit 1
+ fi
+
+ else
+ # no answer
+ echo "CRITICAL - Connection timed out"
+ rm -f $TMP
+ exit 2
+ fi
+}
+
+case "$1" in
+--help)
+ print_help
+ exit 0
+ ;;
+-h)
+ print_help
+ exit 0
+ ;;
+--version)
+ print_revision
+ exit 0
+ ;;
+-v)
+ print_revision
+ exit 0
+ ;;
+--check)
+ check_dns $2 $3 $4
+ ;;
+-c)
+ check_dns $2 $3 $4
+ ;;
+*)
+ print_usage
+ exit 3
+
+esac
diff --git a/files/plugins/check_jabber_login b/files/plugins/check_jabber_login
deleted file mode 100644
index dac0e1f..0000000
--- a/files/plugins/check_jabber_login
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env ruby
-require 'rubygems'
-require 'xmpp4r'
-
-
-def usage
- puts "#{$0} jabberid password"
- exit 3
-end
-
-usage unless ARGV.size == 2
-
-begin
- my_client = Jabber::Client.new(ARGV[0])
- my_client.connect
- my_client.auth(ARGV[1])
-rescue Jabber::ClientAuthenticationFailure => detail
- puts "CRITICAL: Login Error"
- exit 2
-rescue Errno::ECONNREFUSED => detail
- puts "CRITICAL: Connection refused"
- exit 2
-rescue SocketError => detail
- puts "CRITICAL: Socket Error"
- exit 2
-#rescue
-# puts "CRITICAL: Unknown Error"
-# exit 2
-end
-puts "OK: Login for #{ARGV[0]} successfull"
diff --git a/files/plugins/check_mysql_health b/files/plugins/check_mysql_health
new file mode 100755
index 0000000..f0599f2
--- /dev/null
+++ b/files/plugins/check_mysql_health
@@ -0,0 +1,3342 @@
+#! /usr/bin/perl -w
+# nagios: -epn
+
+my %ERRORS=( OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 );
+my %ERRORCODES=( 0 => 'OK', 1 => 'WARNING', 2 => 'CRITICAL', 3 => 'UNKNOWN' );
+package DBD::MySQL::Server::Instance::Innodb;
+
+use strict;
+
+our @ISA = qw(DBD::MySQL::Server::Instance);
+
+
+sub new {
+ my $class = shift;
+ my %params = @_;
+ my $self = {
+ handle => $params{handle},
+ internals => undef,
+ warningrange => $params{warningrange},
+ criticalrange => $params{criticalrange},
+ };
+ bless $self, $class;
+ $self->init(%params);
+ return $self;
+}
+
+sub init {
+ my $self = shift;
+ my %params = @_;
+ $self->init_nagios();
+ if ($params{mode} =~ /server::instance::innodb/) {
+ $self->{internals} =
+ DBD::MySQL::Server::Instance::Innodb::Internals->new(%params);
+ }
+}
+
+sub nagios {
+ my $self = shift;
+ my %params = @_;
+ if ($params{mode} =~ /server::instance::innodb/) {
+ $self->{internals}->nagios(%params);
+ $self->merge_nagios($self->{internals});
+ }
+}
+
+
+package DBD::MySQL::Server::Instance::Innodb::Internals;
+
+use strict;
+
+our @ISA = qw(DBD::MySQL::Server::Instance::Innodb);
+
+our $internals; # singleton, nur ein einziges mal instantiierbar
+
+sub new {
+ my $class = shift;
+ my %params = @_;
+ unless ($internals) {
+ $internals = {
+ handle => $params{handle},
+ bufferpool_hitrate => undef,
+ wait_free => undef,
+ log_waits => undef,
+ warningrange => $params{warningrange},
+ criticalrange => $params{criticalrange},
+ };
+ bless($internals, $class);
+ $internals->init(%params);
+ }
+ return($internals);
+}
+
+sub init {
+ my $self = shift;
+ my %params = @_;
+ my $dummy;
+ $self->debug("enter init");
+ $self->init_nagios();
+ if ($params{mode} =~ /server::instance::innodb::bufferpool::hitrate/) {
+ ($dummy, $self->{bufferpool_reads})
+ = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Innodb_buffer_pool_reads'
+ });
+ ($dummy, $self->{bufferpool_read_requests})
+ = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Innodb_buffer_pool_read_requests'
+ });
+ if (! defined $self->{bufferpool_reads}) {
+ $self->add_nagios_critical("no innodb buffer pool info available");
+ } else {
+ $self->valdiff(\%params, qw(bufferpool_reads
+ bufferpool_read_requests));
+ $self->{bufferpool_hitrate_now} =
+ $self->{delta_bufferpool_read_requests} > 0 ?
+ 100 - (100 * $self->{delta_bufferpool_reads} /
+ $self->{delta_bufferpool_read_requests}) : 100;
+ $self->{bufferpool_hitrate} =
+ $self->{bufferpool_read_requests} > 0 ?
+ 100 - (100 * $self->{bufferpool_reads} /
+ $self->{bufferpool_read_requests}) : 100;
+ }
+ } elsif ($params{mode} =~ /server::instance::innodb::bufferpool::waitfree/) {
+ ($dummy, $self->{bufferpool_wait_free})
+ = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Innodb_buffer_pool_wait_free'
+ });
+ if (! defined $self->{bufferpool_wait_free}) {
+ $self->add_nagios_critical("no innodb buffer pool info available");
+ } else {
+ $self->valdiff(\%params, qw(bufferpool_wait_free));
+ $self->{bufferpool_wait_free_rate} =
+ $self->{delta_bufferpool_wait_free} / $self->{delta_timestamp};
+ }
+ } elsif ($params{mode} =~ /server::instance::innodb::logwaits/) {
+ ($dummy, $self->{log_waits})
+ = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Innodb_log_waits'
+ });
+ if (! defined $self->{log_waits}) {
+ $self->add_nagios_critical("no innodb log info available");
+ } else {
+ $self->valdiff(\%params, qw(log_waits));
+ $self->{log_waits_rate} =
+ $self->{delta_log_waits} / $self->{delta_timestamp};
+ }
+ }
+}
+
+sub nagios {
+ my $self = shift;
+ my %params = @_;
+ my $now = $params{lookback} ? '_now' : '';
+ if (! $self->{nagios_level}) {
+ if ($params{mode} =~ /server::instance::innodb::bufferpool::hitrate/) {
+ my $refkey = 'bufferpool_hitrate'.($params{lookback} ? '_now' : '');
+ $self->add_nagios(
+ $self->check_thresholds($self->{$refkey}, "99:", "95:"),
+ sprintf "innodb buffer pool hitrate at %.2f%%", $self->{$refkey});
+ $self->add_perfdata(sprintf "bufferpool_hitrate=%.2f%%;%s;%s;0;100",
+ $self->{bufferpool_hitrate},
+ $self->{warningrange}, $self->{criticalrange});
+ $self->add_perfdata(sprintf "bufferpool_hitrate_now=%.2f%%",
+ $self->{bufferpool_hitrate_now});
+ } elsif ($params{mode} =~ /server::instance::innodb::bufferpool::waitfree/) {
+ $self->add_nagios(
+ $self->check_thresholds($self->{bufferpool_wait_free_rate}, "1", "10"),
+ sprintf "%ld innodb buffer pool waits in %ld seconds (%.4f/sec)",
+ $self->{delta_bufferpool_wait_free}, $self->{delta_timestamp},
+ $self->{bufferpool_wait_free_rate});
+ $self->add_perfdata(sprintf "bufferpool_free_waits_rate=%.4f;%s;%s;0;100",
+ $self->{bufferpool_wait_free_rate},
+ $self->{warningrange}, $self->{criticalrange});
+ } elsif ($params{mode} =~ /server::instance::innodb::logwaits/) {
+ $self->add_nagios(
+ $self->check_thresholds($self->{log_waits_rate}, "1", "10"),
+ sprintf "%ld innodb log waits in %ld seconds (%.4f/sec)",
+ $self->{delta_log_waits}, $self->{delta_timestamp},
+ $self->{log_waits_rate});
+ $self->add_perfdata(sprintf "innodb_log_waits_rate=%.4f;%s;%s;0;100",
+ $self->{log_waits_rate},
+ $self->{warningrange}, $self->{criticalrange});
+ }
+ }
+}
+
+
+
+
+package DBD::MySQL::Server::Instance::MyISAM;
+
+use strict;
+
+our @ISA = qw(DBD::MySQL::Server::Instance);
+
+
+sub new {
+ my $class = shift;
+ my %params = @_;
+ my $self = {
+ handle => $params{handle},
+ internals => undef,
+ warningrange => $params{warningrange},
+ criticalrange => $params{criticalrange},
+ };
+ bless $self, $class;
+ $self->init(%params);
+ return $self;
+}
+
+sub init {
+ my $self = shift;
+ my %params = @_;
+ $self->init_nagios();
+ if ($params{mode} =~ /server::instance::myisam/) {
+ $self->{internals} =
+ DBD::MySQL::Server::Instance::MyISAM::Internals->new(%params);
+ }
+}
+
+sub nagios {
+ my $self = shift;
+ my %params = @_;
+ if ($params{mode} =~ /server::instance::myisam/) {
+ $self->{internals}->nagios(%params);
+ $self->merge_nagios($self->{internals});
+ }
+}
+
+
+package DBD::MySQL::Server::Instance::MyISAM::Internals;
+
+use strict;
+
+our @ISA = qw(DBD::MySQL::Server::Instance::MyISAM);
+
+our $internals; # singleton, nur ein einziges mal instantiierbar
+
+sub new {
+ my $class = shift;
+ my %params = @_;
+ unless ($internals) {
+ $internals = {
+ handle => $params{handle},
+ keycache_hitrate => undef,
+ warningrange => $params{warningrange},
+ criticalrange => $params{criticalrange},
+ };
+ bless($internals, $class);
+ $internals->init(%params);
+ }
+ return($internals);
+}
+
+sub init {
+ my $self = shift;
+ my %params = @_;
+ my $dummy;
+ $self->debug("enter init");
+ $self->init_nagios();
+ if ($params{mode} =~ /server::instance::myisam::keycache::hitrate/) {
+ ($dummy, $self->{key_reads})
+ = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Key_reads'
+ });
+ ($dummy, $self->{key_read_requests})
+ = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Key_read_requests'
+ });
+ if (! defined $self->{key_read_requests}) {
+ $self->add_nagios_critical("no myisam keycache info available");
+ } else {
+ $self->valdiff(\%params, qw(key_reads key_read_requests));
+ $self->{keycache_hitrate} =
+ $self->{key_read_requests} > 0 ?
+ 100 - (100 * $self->{key_reads} /
+ $self->{key_read_requests}) : 100;
+ $self->{keycache_hitrate_now} =
+ $self->{delta_key_read_requests} > 0 ?
+ 100 - (100 * $self->{delta_key_reads} /
+ $self->{delta_key_read_requests}) : 100;
+ }
+ } elsif ($params{mode} =~ /server::instance::myisam::sonstnochwas/) {
+ }
+}
+
+sub nagios {
+ my $self = shift;
+ my %params = @_;
+ if (! $self->{nagios_level}) {
+ if ($params{mode} =~ /server::instance::myisam::keycache::hitrate/) {
+ my $refkey = 'keycache_hitrate'.($params{lookback} ? '_now' : '');
+ $self->add_nagios(
+ $self->check_thresholds($self->{$refkey}, "99:", "95:"),
+ sprintf "myisam keycache hitrate at %.2f%%", $self->{$refkey});
+ $self->add_perfdata(sprintf "keycache_hitrate=%.2f%%;%s;%s",
+ $self->{keycache_hitrate},
+ $self->{warningrange}, $self->{criticalrange});
+ $self->add_perfdata(sprintf "keycache_hitrate_now=%.2f%%;%s;%s",
+ $self->{keycache_hitrate_now},
+ $self->{warningrange}, $self->{criticalrange});
+ }
+ }
+}
+
+
+package DBD::MySQL::Server::Instance::Replication;
+
+use strict;
+
+our @ISA = qw(DBD::MySQL::Server::Instance);
+
+
+sub new {
+ my $class = shift;
+ my %params = @_;
+ my $self = {
+ handle => $params{handle},
+ internals => undef,
+ warningrange => $params{warningrange},
+ criticalrange => $params{criticalrange},
+ };
+ bless $self, $class;
+ $self->init(%params);
+ return $self;
+}
+
+sub init {
+ my $self = shift;
+ my %params = @_;
+ $self->init_nagios();
+ if ($params{mode} =~ /server::instance::replication/) {
+ $self->{internals} =
+ DBD::MySQL::Server::Instance::Replication::Internals->new(%params);
+ }
+}
+
+sub nagios {
+ my $self = shift;
+ my %params = @_;
+ if ($params{mode} =~ /server::instance::replication/) {
+ $self->{internals}->nagios(%params);
+ $self->merge_nagios($self->{internals});
+ }
+}
+
+
+package DBD::MySQL::Server::Instance::Replication::Internals;
+
+use strict;
+
+our @ISA = qw(DBD::MySQL::Server::Instance::Replication);
+
+our $internals; # singleton, nur ein einziges mal instantiierbar
+
+sub new {
+ my $class = shift;
+ my %params = @_;
+ unless ($internals) {
+ $internals = {
+ handle => $params{handle},
+ seconds_behind_master => undef,
+ slave_io_running => undef,
+ slave_sql_running => undef,
+ warningrange => $params{warningrange},
+ criticalrange => $params{criticalrange},
+ };
+ bless($internals, $class);
+ $internals->init(%params);
+ }
+ return($internals);
+}
+
+sub init {
+ my $self = shift;
+ my %params = @_;
+ $self->debug("enter init");
+ $self->init_nagios();
+ if ($params{mode} =~ /server::instance::replication::slavelag/) {
+ # "show slave status", "Seconds_Behind_Master"
+ my $slavehash = $self->{handle}->selectrow_hashref(q{
+ SHOW SLAVE STATUS
+ });
+ if ((! defined $slavehash->{Seconds_Behind_Master}) &&
+ (lc $slavehash->{Slave_IO_Running} eq 'no')) {
+ $self->add_nagios_critical(
+ "unable to get slave lag, because io thread is not running");
+ } elsif (! defined $slavehash->{Seconds_Behind_Master}) {
+ $self->add_nagios_critical(sprintf "unable to get replication info%s",
+ $self->{handle}->{errstr} ? $self->{handle}->{errstr} : "");
+ } else {
+ $self->{seconds_behind_master} = $slavehash->{Seconds_Behind_Master};
+ }
+ } elsif ($params{mode} =~ /server::instance::replication::slaveiorunning/) {
+ # "show slave status", "Slave_IO_Running"
+ my $slavehash = $self->{handle}->selectrow_hashref(q{
+ SHOW SLAVE STATUS
+ });
+ if (! defined $slavehash->{Slave_IO_Running}) {
+ $self->add_nagios_critical(sprintf "unable to get replication info%s",
+ $self->{handle}->{errstr} ? $self->{handle}->{errstr} : "");
+ } else {
+ $self->{slave_io_running} = $slavehash->{Slave_IO_Running};
+ }
+ } elsif ($params{mode} =~ /server::instance::replication::slavesqlrunning/) {
+ # "show slave status", "Slave_SQL_Running"
+ my $slavehash = $self->{handle}->selectrow_hashref(q{
+ SHOW SLAVE STATUS
+ });
+ if (! defined $slavehash->{Slave_SQL_Running}) {
+ $self->add_nagios_critical(sprintf "unable to get replication info%s",
+ $self->{handle}->{errstr} ? $self->{handle}->{errstr} : "");
+ } else {
+ $self->{slave_sql_running} = $slavehash->{Slave_SQL_Running};
+ }
+ }
+}
+
+sub nagios {
+ my $self = shift;
+ my %params = @_;
+ if (! $self->{nagios_level}) {
+ if ($params{mode} =~ /server::instance::replication::slavelag/) {
+ $self->add_nagios(
+ $self->check_thresholds($self->{seconds_behind_master}, "10", "20"),
+ sprintf "Slave is %d seconds behind master",
+ $self->{seconds_behind_master});
+ $self->add_perfdata(sprintf "slave_lag=%d;%s;%s",
+ $self->{seconds_behind_master},
+ $self->{warningrange}, $self->{criticalrange});
+ } elsif ($params{mode} =~ /server::instance::replication::slaveiorunning/) {
+ if (lc $self->{slave_io_running} eq "yes") {
+ $self->add_nagios_ok("Slave io is running");
+ } else {
+ $self->add_nagios_critical("Slave io is not running");
+ }
+ } elsif ($params{mode} =~ /server::instance::replication::slavesqlrunning/) {
+ if (lc $self->{slave_sql_running} eq "yes") {
+ $self->add_nagios_ok("Slave sql is running");
+ } else {
+ $self->add_nagios_critical("Slave sql is not running");
+ }
+ }
+ }
+}
+
+
+
+package DBD::MySQL::Server::Instance;
+
+use strict;
+
+our @ISA = qw(DBD::MySQL::Server);
+
+
+sub new {
+ my $class = shift;
+ my %params = @_;
+ my $self = {
+ handle => $params{handle},
+ uptime => $params{uptime},
+ warningrange => $params{warningrange},
+ criticalrange => $params{criticalrange},
+ threads_connected => undef,
+ threads_created => undef,
+ connections => undef,
+ threadcache_hitrate => undef,
+ querycache_hitrate => undef,
+ lowmem_prunes_per_sec => undef,
+ slow_queries_per_sec => undef,
+ longrunners => undef,
+ tablecache_hitrate => undef,
+ index_usage => undef,
+ engine_innodb => undef,
+ engine_myisam => undef,
+ replication => undef,
+ };
+ bless $self, $class;
+ $self->init(%params);
+ return $self;
+}
+
+sub init {
+ my $self = shift;
+ my %params = @_;
+ my $dummy;
+ $self->init_nagios();
+ if ($params{mode} =~ /server::instance::connectedthreads/) {
+ ($dummy, $self->{threads_connected}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Threads_connected'
+ });
+ } elsif ($params{mode} =~ /server::instance::threadcachehitrate/) {
+ ($dummy, $self->{threads_created}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Threads_created'
+ });
+ ($dummy, $self->{connections}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Connections'
+ });
+ $self->valdiff(\%params, qw(threads_created connections));
+ if ($self->{delta_connections} > 0) {
+ $self->{threadcache_hitrate_now} =
+ 100 - ($self->{delta_threads_created} * 100.0 /
+ $self->{delta_connections});
+ } else {
+ $self->{threadcache_hitrate_now} = 100;
+ }
+ $self->{threadcache_hitrate} = 100 -
+ ($self->{threads_created} * 100.0 / $self->{connections});
+ $self->{connections_per_sec} = $self->{delta_connections} /
+ $self->{delta_timestamp};
+ } elsif ($params{mode} =~ /server::instance::querycachehitrate/) {
+ ($dummy, $self->{com_select}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Com_select'
+ });
+ ($dummy, $self->{qcache_hits}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Qcache_hits'
+ });
+ # SHOW VARIABLES WHERE Variable_name = 'have_query_cache' for 5.x, but LIKE is compatible
+ ($dummy, $self->{have_query_cache}) = $self->{handle}->fetchrow_array(q{
+ SHOW VARIABLES LIKE 'have_query_cache'
+ });
+ # SHOW VARIABLES WHERE Variable_name = 'query_cache_size'
+ ($dummy, $self->{query_cache_size}) = $self->{handle}->fetchrow_array(q{
+ SHOW VARIABLES LIKE 'query_cache_size'
+ });
+ $self->valdiff(\%params, qw(com_select qcache_hits));
+ $self->{querycache_hitrate_now} =
+ ($self->{delta_com_select} + $self->{delta_qcache_hits}) > 0 ?
+ 100 * $self->{delta_qcache_hits} /
+ ($self->{delta_com_select} + $self->{delta_qcache_hits}) :
+ 0;
+ $self->{querycache_hitrate} =
+ 100 * $self->{qcache_hits} / ($self->{com_select} + $self->{qcache_hits});
+ $self->{selects_per_sec} =
+ $self->{delta_com_select} / $self->{delta_timestamp};
+ } elsif ($params{mode} =~ /server::instance::querycachelowmemprunes/) {
+ ($dummy, $self->{lowmem_prunes}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Qcache_lowmem_prunes'
+ });
+ $self->valdiff(\%params, qw(lowmem_prunes));
+ $self->{lowmem_prunes_per_sec} = $self->{delta_lowmem_prunes} /
+ $self->{delta_timestamp};
+ } elsif ($params{mode} =~ /server::instance::slowqueries/) {
+ ($dummy, $self->{slow_queries}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Slow_queries'
+ });
+ $self->valdiff(\%params, qw(slow_queries));
+ $self->{slow_queries_per_sec} = $self->{delta_slow_queries} /
+ $self->{delta_timestamp};
+ } elsif ($params{mode} =~ /server::instance::longprocs/) {
+ if (DBD::MySQL::Server::return_first_server()->version_is_minimum("5.1")) {
+ ($self->{longrunners}) = $self->{handle}->fetchrow_array(q{
+ SELECT
+ COUNT(*)
+ FROM
+ information_schema.processlist
+ WHERE user <> 'replication'
+ AND id <> CONNECTION_ID()
+ AND time > 60
+ AND command <> 'Sleep'
+ });
+ } else {
+ $self->{longrunners} = 0 if ! defined $self->{longrunners};
+ foreach ($self->{handle}->fetchall_array(q{
+ SHOW PROCESSLIST
+ })) {
+ my($id, $user, $host, $db, $command, $tme, $state, $info) = @{$_};
+ if (($user ne 'replication') &&
+ ($tme > 60) &&
+ ($command ne 'Sleep')) {
+ $self->{longrunners}++;
+ }
+ }
+ }
+ } elsif ($params{mode} =~ /server::instance::tablecachehitrate/) {
+ ($dummy, $self->{open_tables}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Open_tables'
+ });
+ ($dummy, $self->{opened_tables}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Opened_tables'
+ });
+ if (DBD::MySQL::Server::return_first_server()->version_is_minimum("5.1.3")) {
+ # SHOW VARIABLES WHERE Variable_name = 'table_open_cache'
+ ($dummy, $self->{table_cache}) = $self->{handle}->fetchrow_array(q{
+ SHOW VARIABLES LIKE 'table_open_cache'
+ });
+ } else {
+ # SHOW VARIABLES WHERE Variable_name = 'table_cache'
+ ($dummy, $self->{table_cache}) = $self->{handle}->fetchrow_array(q{
+ SHOW VARIABLES LIKE 'table_cache'
+ });
+ }
+ $self->{table_cache} ||= 0;
+ #$self->valdiff(\%params, qw(open_tables opened_tables table_cache));
+ # _now ist hier sinnlos, da opened_tables waechst, aber open_tables wieder
+ # schrumpfen kann weil tabellen geschlossen werden.
+ if ($self->{opened_tables} != 0 && $self->{table_cache} != 0) {
+ $self->{tablecache_hitrate} =
+ 100 * $self->{open_tables} / $self->{opened_tables};
+ $self->{tablecache_fillrate} =
+ 100 * $self->{open_tables} / $self->{table_cache};
+ } elsif ($self->{opened_tables} == 0 && $self->{table_cache} != 0) {
+ $self->{tablecache_hitrate} = 100;
+ $self->{tablecache_fillrate} =
+ 100 * $self->{open_tables} / $self->{table_cache};
+ } else {
+ $self->{tablecache_hitrate} = 0;
+ $self->{tablecache_fillrate} = 0;
+ $self->add_nagios_critical("no table cache");
+ }
+ } elsif ($params{mode} =~ /server::instance::tablelockcontention/) {
+ ($dummy, $self->{table_locks_waited}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Table_locks_waited'
+ });
+ ($dummy, $self->{table_locks_immediate}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Table_locks_immediate'
+ });
+ $self->valdiff(\%params, qw(table_locks_waited table_locks_immediate));
+ $self->{table_lock_contention} =
+ ($self->{table_locks_waited} + $self->{table_locks_immediate}) > 0 ?
+ 100 * $self->{table_locks_waited} /
+ ($self->{table_locks_waited} + $self->{table_locks_immediate}) :
+ 100;
+ $self->{table_lock_contention_now} =
+ ($self->{delta_table_locks_waited} + $self->{delta_table_locks_immediate}) > 0 ?
+ 100 * $self->{delta_table_locks_waited} /
+ ($self->{delta_table_locks_waited} + $self->{delta_table_locks_immediate}) :
+ 100;
+ } elsif ($params{mode} =~ /server::instance::tableindexusage/) {
+ # http://johnjacobm.wordpress.com/2007/06/
+ # formula for calculating the percentage of full table scans
+ ($dummy, $self->{handler_read_first}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Handler_read_first'
+ });
+ ($dummy, $self->{handler_read_key}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Handler_read_key'
+ });
+ ($dummy, $self->{handler_read_next}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Handler_read_next'
+ });
+ ($dummy, $self->{handler_read_prev}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Handler_read_prev'
+ });
+ ($dummy, $self->{handler_read_rnd}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Handler_read_rnd'
+ });
+ ($dummy, $self->{handler_read_rnd_next}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Handler_read_rnd_next'
+ });
+ $self->valdiff(\%params, qw(handler_read_first handler_read_key
+ handler_read_next handler_read_prev handler_read_rnd
+ handler_read_rnd_next));
+ $self->{index_usage_now} = 100 - (100.0 * ($self->{delta_handler_read_rnd} +
+ $self->{delta_handler_read_rnd_next}) /
+ ($self->{delta_handler_read_first} +
+ $self->{delta_handler_read_key} +
+ $self->{delta_handler_read_next} +
+ $self->{delta_handler_read_prev} +
+ $self->{delta_handler_read_rnd} +
+ $self->{delta_handler_read_rnd_next}));
+ $self->{index_usage} = 100 - (100.0 * ($self->{handler_read_rnd} +
+ $self->{handler_read_rnd_next}) /
+ ($self->{handler_read_first} +
+ $self->{handler_read_key} +
+ $self->{handler_read_next} +
+ $self->{handler_read_prev} +
+ $self->{handler_read_rnd} +
+ $self->{handler_read_rnd_next}));
+ } elsif ($params{mode} =~ /server::instance::tabletmpondisk/) {
+ ($dummy, $self->{created_tmp_tables}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Created_tmp_tables'
+ });
+ ($dummy, $self->{created_tmp_disk_tables}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Created_tmp_disk_tables'
+ });
+ $self->valdiff(\%params, qw(created_tmp_tables created_tmp_disk_tables));
+ $self->{pct_tmp_on_disk} = $self->{created_tmp_tables} > 0 ?
+ 100 * $self->{created_tmp_disk_tables} / $self->{created_tmp_tables} :
+ 100;
+ $self->{pct_tmp_on_disk_now} = $self->{delta_created_tmp_tables} > 0 ?
+ 100 * $self->{delta_created_tmp_disk_tables} / $self->{delta_created_tmp_tables} :
+ 100;
+ } elsif ($params{mode} =~ /server::instance::openfiles/) {
+ ($dummy, $self->{open_files_limit}) = $self->{handle}->fetchrow_array(q{
+ SHOW VARIABLES LIKE 'open_files_limit'
+ });
+ ($dummy, $self->{open_files}) = $self->{handle}->fetchrow_array(q{
+ SHOW /*!50000 global */ STATUS LIKE 'Open_files'
+ });
+ $self->{pct_open_files} = 100 * $self->{open_files} / $self->{open_files_limit};
+ } elsif ($params{mode} =~ /server::instance::myisam/) {
+ $self->{engine_myisam} = DBD::MySQL::Server::Instance::MyISAM->new(
+ %params
+ );
+ } elsif ($params{mode} =~ /server::instance::innodb/) {
+ $self->{engine_innodb} = DBD::MySQL::Server::Instance::Innodb->new(
+ %params
+ );
+ } elsif ($params{mode} =~ /server::instance::replication/) {
+ $self->{replication} = DBD::MySQL::Server::Instance::Replication->new(
+ %params
+ );
+ }
+}
+
+sub nagios {
+ my $self = shift;
+ my %params = @_;
+ if (! $self->{nagios_level}) {
+ if ($params{mode} =~ /server::instance::connectedthreads/) {
+ $self->add_nagios(
+ $self->check_thresholds($self->{threads_connected}, 10, 20),
+ sprintf "%d client connection threads", $self->{threads_connected});
+ $self->add_perfdata(sprintf "threads_connected=%d;%d;%d",
+ $self->{threads_connected},
+ $self->{warningrange}, $self->{criticalrange});
+ } elsif ($params{mode} =~ /server::instance::threadcachehitrate/) {
+ my $refkey = 'threadcache_hitrate'.($params{lookback} ? '_now' : '');
+ $self->add_nagios(
+ $self->check_thresholds($self->{$refkey}, "90:", "80:"),
+ sprintf "thread cache hitrate %.2f%%", $self->{$refkey});
+ $self->add_perfdata(sprintf "thread_cache_hitrate=%.2f%%;%s;%s",
+ $self->{threadcache_hitrate},
+ $self->{warningrange}, $self->{criticalrange});
+ $self->add_perfdata(sprintf "thread_cache_hitrate_now=%.2f%%",
+ $self->{threadcache_hitrate_now});
+ $self->add_perfdata(sprintf "connections_per_sec=%.2f",
+ $self->{connections_per_sec});
+ } elsif ($params{mode} =~ /server::instance::querycachehitrate/) {
+ my $refkey = 'querycache_hitrate'.($params{lookback} ? '_now' : '');
+ if ((lc $self->{have_query_cache} eq 'yes') && ($self->{query_cache_size})) {
+ $self->add_nagios(
+ $self->check_thresholds($self->{$refkey}, "90:", "80:"),
+ sprintf "query cache hitrate %.2f%%", $self->{$refkey});
+ } else {
+ $self->check_thresholds($self->{$refkey}, "90:", "80:");
+ $self->add_nagios_ok(
+ sprintf "query cache hitrate %.2f%% (because it's turned off)",
+ $self->{querycache_hitrate});
+ }
+ $self->add_perfdata(sprintf "qcache_hitrate=%.2f%%;%s;%s",
+ $self->{querycache_hitrate},
+ $self->{warningrange}, $self->{criticalrange});
+ $self->add_perfdata(sprintf "qcache_hitrate_now=%.2f%%",
+ $self->{querycache_hitrate_now});
+ $self->add_perfdata(sprintf "selects_per_sec=%.2f",
+ $self->{selects_per_sec});
+ } elsif ($params{mode} =~ /server::instance::querycachelowmemprunes/) {
+ $self->add_nagios(
+ $self->check_thresholds($self->{lowmem_prunes_per_sec}, "1", "10"),
+ sprintf "%d query cache lowmem prunes in %d seconds (%.2f/sec)",
+ $self->{delta_lowmem_prunes}, $self->{delta_timestamp},
+ $self->{lowmem_prunes_per_sec});
+ $self->add_perfdata(sprintf "qcache_lowmem_prunes_rate=%.2f;%s;%s",
+ $self->{lowmem_prunes_per_sec},
+ $self->{warningrange}, $self->{criticalrange});
+ } elsif ($params{mode} =~ /server::instance::slowqueries/) {
+ $self->add_nagios(
+ $self->check_thresholds($self->{slow_queries_per_sec}, "0.1", "1"),
+ sprintf "%d slow queries in %d seconds (%.2f/sec)",
+ $self->{delta_slow_queries}, $self->{delta_timestamp},
+ $self->{slow_queries_per_sec});
+ $self->add_perfdata(sprintf "slow_queries_rate=%.2f%%;%s;%s",
+ $self->{slow_queries_per_sec},
+ $self->{warningrange}, $self->{criticalrange});
+ } elsif ($params{mode} =~ /server::instance::longprocs/) {
+ $self->add_nagios(
+ $self->check_thresholds($self->{longrunners}, 10, 20),
+ sprintf "%d long running processes", $self->{longrunners});
+ $self->add_perfdata(sprintf "long_running_procs=%d;%d;%d",
+ $self->{longrunners},
+ $self->{warningrange}, $self->{criticalrange});
+ } elsif ($params{mode} =~ /server::instance::tablecachehitrate/) {
+ if ($self->{tablecache_fillrate} < 95) {
+ $self->add_nagios_ok(
+ sprintf "table cache hitrate %.2f%%, %.2f%% filled",
+ $self->{tablecache_hitrate},
+ $self->{tablecache_fillrate});
+ $self->check_thresholds($self->{tablecache_hitrate}, "99:", "95:");
+ } else {
+ $self->add_nagios(
+ $self->check_thresholds($self->{tablecache_hitrate}, "99:", "95:"),
+ sprintf "table cache hitrate %.2f%%", $self->{tablecache_hitrate});
+ }
+ $self->add_perfdata(sprintf "tablecache_hitrate=%.2f%%;%s;%s",
+ $self->{tablecache_hitrate},
+ $self->{warningrange}, $self->{criticalrange});
+ $self->add_perfdata(sprintf "tablecache_fillrate=%.2f%%",
+ $self->{tablecache_fillrate});
+ } elsif ($params{mode} =~ /server::instance::tablelockcontention/) {
+ my $refkey = 'table_lock_contention'.($params{lookback} ? '_now' : '');
+ if ($self->{uptime} > 10800) { # MySQL Bug #30599
+ $self->add_nagios(
+ $self->check_thresholds($self->{$refkey}, "1", "2"),
+ sprintf "table lock contention %.2f%%", $self->{$refkey});
+ } else {
+ $self->check_thresholds($self->{$refkey}, "1", "2");
+ $self->add_nagios_ok(
+ sprintf "table lock contention %.2f%% (uptime < 10800)",
+ $self->{refkey});
+ }
+ $self->add_perfdata(sprintf "tablelock_contention=%.2f%%;%s;%s",
+ $self->{table_lock_contention},
+ $self->{warningrange}, $self->{criticalrange});
+ $self->add_perfdata(sprintf "tablelock_contention_now=%.2f%%",
+ $self->{table_lock_contention_now});
+ } elsif ($params{mode} =~ /server::instance::tableindexusage/) {
+ my $refkey = 'index_usage'.($params{lookback} ? '_now' : '');
+ $self->add_nagios(
+ $self->check_thresholds($self->{$refkey}, "90:", "80:"),
+ sprintf "index usage %.2f%%", $self->{$refkey});
+ $self->add_perfdata(sprintf "index_usage=%.2f%%;%s;%s",
+ $self->{index_usage},
+ $self->{warningrange}, $self->{criticalrange});
+ $self->add_perfdata(sprintf "index_usage_now=%.2f%%",
+ $self->{index_usage_now});
+ } elsif ($params{mode} =~ /server::instance::tabletmpondisk/) {
+ my $refkey = 'pct_tmp_on_disk'.($params{lookback} ? '_now' : '');
+ $self->add_nagios(
+ $self->check_thresholds($self->{$refkey}, "25", "50"),
+ sprintf "%.2f%% of %d tables were created on disk",
+ $self->{$refkey}, $self->{delta_created_tmp_tables});
+ $self->add_perfdata(sprintf "pct_tmp_table_on_disk=%.2f%%;%s;%s",
+ $self->{pct_tmp_on_disk},
+ $self->{warningrange}, $self->{criticalrange});
+ $self->add_perfdata(sprintf "pct_tmp_table_on_disk_now=%.2f%%",
+ $self->{pct_tmp_on_disk_now});
+ } elsif ($params{mode} =~ /server::instance::openfiles/) {
+ $self->add_nagios(
+ $self->check_thresholds($self->{pct_open_files}, 80, 95),
+ sprintf "%.2f%% of the open files limit reached (%d of max. %d)",
+ $self->{pct_open_files},
+ $self->{open_files}, $self->{open_files_limit});
+ $self->add_perfdata(sprintf "pct_open_files=%.3f%%;%.3f;%.3f",
+ $self->{pct_open_files},
+ $self->{warningrange},
+ $self->{criticalrange});
+ $self->add_perfdata(sprintf "open_files=%d;%d;%d",
+ $self->{open_files},
+ $self->{open_files_limit} * $self->{warningrange} / 100,
+ $self->{open_files_limit} * $self->{criticalrange} / 100);
+ } elsif ($params{mode} =~ /server::instance::myisam/) {
+ $self->{engine_myisam}->nagios(%params);
+ $self->merge_nagios($self->{engine_myisam});
+ } elsif ($params{mode} =~ /server::instance::innodb/) {
+ $self->{engine_innodb}->nagios(%params);
+ $self->merge_nagios($self->{engine_innodb});
+ } elsif ($params{mode} =~ /server::instance::replication/) {
+ $self->{replication}->nagios(%params);
+ $self->merge_nagios($self->{replication});
+ }
+ }
+}
+
+
+
+package DBD::MySQL::Server;
+
+use strict;
+use Time::HiRes;
+use IO::File;
+use File::Copy 'cp';
+use Data::Dumper;
+
+
+{
+ our $verbose = 0;
+ our $scream = 0; # scream if something is not implemented
+ our $access = "dbi"; # how do we access the database.
+ our $my_modules_dyn_dir = ""; # where we look for self-written extensions
+
+ my @servers = ();
+ my $initerrors = undef;
+
+ sub add_server {
+ push(@servers, shift);
+ }
+
+ sub return_servers {
+ return @servers;
+ }
+
+ sub return_first_server() {
+ return $servers[0];
+ }
+
+}
+
+sub new {
+ my $class = shift;
+ my %params = @_;
+ my $self = {
+ access => $params{method} || 'dbi',
+ hostname => $params{hostname},
+ database => $params{database} || 'information_schema',
+ port => $params{port},
+ socket => $params{socket},
+ username => $params{username},
+ password => $params{password},
+ timeout => $params{timeout},
+ warningrange => $params{warningrange},
+ criticalrange => $params{criticalrange},
+ version => 'unknown',
+ instance => undef,
+ handle => undef,
+ };
+ bless $self, $class;
+ $self->init_nagios();
+ if ($self->dbconnect(%params)) {
+ ($self->{dummy}, $self->{version}) = $self->{handle}->fetchrow_array(
+ #q{ SHOW VARIABLES WHERE Variable_name = 'version' }
+ q{ SHOW VARIABLES LIKE 'version' }
+ );
+ $self->{version} = (split "-", $self->{version})[0];
+ ($self->{dummy}, $self->{uptime}) = $self->{handle}->fetchrow_array(
+ q{ SHOW STATUS LIKE 'Uptime' }
+ );
+ DBD::MySQL::Server::add_server($self);
+ $self->init(%params);
+ }
+ return $self;
+}
+
+sub init {
+ my $self = shift;
+ my %params = @_;
+ $params{handle} = $self->{handle};
+ $params{uptime} = $self->{uptime};
+ if ($params{mode} =~ /^server::instance/) {
+ $self->{instance} = DBD::MySQL::Server::Instance->new(%params);
+ } elsif ($params{mode} =~ /^server::sql/) {
+ $self->{genericsql} = $self->{handle}->fetchrow_array($params{selectname});
+ if ($self->{genericsql} =~ /^\s*\d+\s*$/) {
+ $self->{genericsql} = sprintf "%d", $self->{genericsql};
+ } elsif ($self->{genericsql} =~ /^\s*\d*\.\d+\s*$/) {
+ $self->{genericsql} = sprintf "%.2f", $self->{genericsql};
+ } else {
+ $self->add_nagios_unknown(sprintf "got no valid response for %s",
+ $params{selectname});
+ }
+ } elsif ($params{mode} =~ /^server::uptime/) {
+ # already set with the connection. but use minutes here
+ } elsif ($params{mode} =~ /^server::connectiontime/) {
+ $self->{connection_time} = $self->{tac} - $self->{tic};
+ } elsif ($params{mode} =~ /^my::([^:.]+)/) {
+ my $class = $1;
+ my $loaderror = undef;
+ substr($class, 0, 1) = uc substr($class, 0, 1);
+ foreach my $libpath (split(":", $DBD::MySQL::Server::my_modules_dyn_dir)) {
+ foreach my $extmod (glob $libpath."/CheckMySQLHealth*.pm") {
+ eval {
+ $self->trace(sprintf "loading module %s", $extmod);
+ require $extmod;
+ };
+ if ($@) {
+ $loaderror = $extmod;
+ $self->trace(sprintf "failed loading module %s: %s", $extmod, $@);
+ }
+ }
+ }
+ my $obj = {
+ handle => $params{handle},
+ warningrange => $params{warningrange},
+ criticalrange => $params{criticalrange},
+ };
+ bless $obj, "My$class";
+ $self->{my} = $obj;
+ if ($self->{my}->isa("DBD::MySQL::Server")) {
+ my $dos_init = $self->can("init");
+ my $dos_nagios = $self->can("nagios");
+ my $my_init = $self->{my}->can("init");
+ my $my_nagios = $self->{my}->can("nagios");
+ if ($my_init == $dos_init) {
+ $self->add_nagios_unknown(
+ sprintf "Class %s needs an init() method", ref($self->{my}));
+ } elsif ($my_nagios == $dos_nagios) {
+ $self->add_nagios_unknown(
+ sprintf "Class %s needs a nagios() method", ref($self->{my}));
+ } else {
+ $self->{my}->init_nagios(%params);
+ $self->{my}->init(%params);
+ }
+ } else {
+ $self->add_nagios_unknown(
+ sprintf "Class %s is not a subclass of DBD::MySQL::Server%s",
+ ref($self->{my}),
+ $loaderror ? sprintf " (syntax error in %s?)", $loaderror : "" );
+ }
+ } else {
+ printf "broken mode %s\n", $params{mode};
+ }
+}
+
+sub dump {
+ my $self = shift;
+ my $message = shift || "";
+ printf "%s %s\n", $message, Data::Dumper::Dumper($self);
+}
+
+sub nagios {
+ my $self = shift;
+ my %params = @_;
+ if (! $self->{nagios_level}) {
+ if ($params{mode} =~ /^server::instance/) {
+ $self->{instance}->nagios(%params);
+ $self->merge_nagios($self->{instance});
+ } elsif ($params{mode} =~ /^server::database/) {
+ $self->{database}->nagios(%params);
+ $self->merge_nagios($self->{database});
+ } elsif ($params{mode} =~ /^server::uptime/) {
+ $self->add_nagios(
+ $self->check_thresholds($self->{uptime} / 60, "10:", "5:"),
+ sprintf "database is up since %d minutes", $self->{uptime} / 60);
+ $self->add_perfdata(sprintf "uptime=%ds",
+ $self->{uptime});
+ } elsif ($params{mode} =~ /^server::connectiontime/) {
+ $self->add_nagios(
+ $self->check_thresholds($self->{connection_time}, 1, 5),
+ sprintf "%.2f seconds to connect as %s",
+ $self->{connection_time}, ($self->{username} || getpwuid($<)));
+ $self->add_perfdata(sprintf "connection_time=%.4fs;%d;%d",
+ $self->{connection_time},
+ $self->{warningrange}, $self->{criticalrange});
+ } elsif ($params{mode} =~ /^server::sql/) {
+ $self->add_nagios(
+ $self->check_thresholds($self->{genericsql}, 1, 5),
+ sprintf "%s: %s%s",
+ $params{name2} ? lc $params{name2} : lc $params{selectname},
+ $self->{genericsql},
+ $params{units} ? $params{units} : "");
+ $self->add_perfdata(sprintf "\'%s\'=%s%s;%s;%s",
+ $params{name2} ? lc $params{name2} : lc $params{selectname},
+ $self->{genericsql},
+ $params{units} ? $params{units} : "",
+ $self->{warningrange}, $self->{criticalrange});
+ } elsif ($params{mode} =~ /^my::([^:.]+)/) {
+ $self->{my}->nagios(%params);
+ $self->merge_nagios($self->{my});
+ }
+ }
+}
+
+
+sub init_nagios {
+ my $self = shift;
+ no strict 'refs';
+ if (! ref($self)) {
+ my $nagiosvar = $self."::nagios";
+ my $nagioslevelvar = $self."::nagios_level";
+ $$nagiosvar = {
+ messages => {
+ 0 => [],
+ 1 => [],
+ 2 => [],
+ 3 => [],
+ },
+ perfdata => [],
+ };
+ $$nagioslevelvar = $ERRORS{OK},
+ } else {
+ $self->{nagios} = {
+ messages => {
+ 0 => [],
+ 1 => [],
+ 2 => [],
+ 3 => [],
+ },
+ perfdata => [],
+ };
+ $self->{nagios_level} = $ERRORS{OK},
+ }
+}
+
+sub check_thresholds {
+ my $self = shift;
+ my $value = shift;
+ my $defaultwarningrange = shift;
+ my $defaultcriticalrange = shift;
+ my $level = $ERRORS{OK};
+ $self->{warningrange} = defined $self->{warningrange} ?
+ $self->{warningrange} : $defaultwarningrange;
+ $self->{criticalrange} = defined $self->{criticalrange} ?
+ $self->{criticalrange} : $defaultcriticalrange;
+ if ($self->{warningrange} !~ /:/ && $self->{criticalrange} !~ /:/) {
+ # warning = 10, critical = 20, warn if > 10, crit if > 20
+ $level = $ERRORS{WARNING} if $value > $self->{warningrange};
+ $level = $ERRORS{CRITICAL} if $value > $self->{criticalrange};
+ } elsif ($self->{warningrange} =~ /([\d\.]+):/ &&
+ $self->{criticalrange} =~ /([\d\.]+):/) {
+ # warning = 98:, critical = 95:, warn if < 98, crit if < 95
+ $self->{warningrange} =~ /([\d\.]+):/;
+ $level = $ERRORS{WARNING} if $value < $1;
+ $self->{criticalrange} =~ /([\d\.]+):/;
+ $level = $ERRORS{CRITICAL} if $value < $1;
+ }
+ return $level;
+ #
+ # syntax error must be reported with returncode -1
+ #
+}
+
+sub add_nagios {
+ my $self = shift;
+ my $level = shift;
+ my $message = shift;
+ push(@{$self->{nagios}->{messages}->{$level}}, $message);
+ # recalc current level
+ foreach my $llevel qw(CRITICAL WARNING UNKNOWN OK) {
+ if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$llevel}}})) {
+ $self->{nagios_level} = $ERRORS{$llevel};
+ }
+ }
+}
+
+sub add_nagios_ok {
+ my $self = shift;
+ my $message = shift;
+ $self->add_nagios($ERRORS{OK}, $message);
+}
+
+sub add_nagios_warning {
+ my $self = shift;
+ my $message = shift;
+ $self->add_nagios($ERRORS{WARNING}, $message);
+}
+
+sub add_nagios_critical {
+ my $self = shift;
+ my $message = shift;
+ $self->add_nagios($ERRORS{CRITICAL}, $message);
+}
+
+sub add_nagios_unknown {
+ my $self = shift;
+ my $message = shift;
+ $self->add_nagios($ERRORS{UNKNOWN}, $message);
+}
+
+sub add_perfdata {
+ my $self = shift;
+ my $data = shift;
+ push(@{$self->{nagios}->{perfdata}}, $data);
+}
+
+sub merge_nagios {
+ my $self = shift;
+ my $child = shift;
+ foreach my $level (0..3) {
+ foreach (@{$child->{nagios}->{messages}->{$level}}) {
+ $self->add_nagios($level, $_);
+ }
+ #push(@{$self->{nagios}->{messages}->{$level}},
+ # @{$child->{nagios}->{messages}->{$level}});
+ }
+ push(@{$self->{nagios}->{perfdata}}, @{$child->{nagios}->{perfdata}});
+}
+
+
+sub calculate_result {
+ my $self = shift;
+ if ($ENV{NRPE_MULTILINESUPPORT} &&
+ length join(" ", @{$self->{nagios}->{perfdata}}) > 200) {
+ foreach my $level ("CRITICAL", "WARNING", "UNKNOWN", "OK") {
+ # first the bad news
+ if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
+ $self->{nagios_message} .=
+ "\n".join("\n", @{$self->{nagios}->{messages}->{$ERRORS{$level}}});
+ }
+ }
+ $self->{nagios_message} =~ s/^\n//g;
+ $self->{perfdata} = join("\n", @{$self->{nagios}->{perfdata}});
+ } else {
+ foreach my $level ("CRITICAL", "WARNING", "UNKNOWN", "OK") {
+ # first the bad news
+ if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
+ $self->{nagios_message} .=
+ join(", ", @{$self->{nagios}->{messages}->{$ERRORS{$level}}}).", ";
+ }
+ }
+ $self->{nagios_message} =~ s/, $//g;
+ $self->{perfdata} = join(" ", @{$self->{nagios}->{perfdata}});
+ }
+ foreach my $level ("OK", "UNKNOWN", "WARNING", "CRITICAL") {
+ if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
+ $self->{nagios_level} = $ERRORS{$level};
+ }
+ }
+}
+
+sub debug {
+ my $self = shift;
+ my $msg = shift;
+ if ($DBD::MySQL::Server::verbose) {
+ printf "%s %s\n", $msg, ref($self);
+ }
+}
+
+sub dbconnect {
+ my $self = shift;
+ my %params = @_;
+ my $retval = undef;
+ $self->{tic} = Time::HiRes::time();
+ $self->{handle} = DBD::MySQL::Server::Connection->new(%params);
+ if ($self->{handle}->{errstr}) {
+ if ($params{mode} =~ /^server::tnsping/ &&
+ $self->{handle}->{errstr} =~ /ORA-01017/) {
+ $self->add_nagios($ERRORS{OK},
+ sprintf "connection established to %s.", $self->{connect});
+ $retval = undef;
+ } elsif ($self->{handle}->{errstr} eq "alarm\n") {
+ $self->add_nagios($ERRORS{CRITICAL},
+ sprintf "connection could not be established within %d seconds",
+ $self->{timeout});
+ } else {
+ $self->add_nagios($ERRORS{CRITICAL},
+ sprintf "cannot connect to %s. %s",
+ $self->{database}, $self->{handle}->{errstr});
+ $retval = undef;
+ }
+ } else {
+ $retval = $self->{handle};
+ }
+ $self->{tac} = Time::HiRes::time();
+ return $retval;
+}
+
+sub trace {
+ my $self = shift;
+ my $format = shift;
+ $self->{trace} = -f "/tmp/check_mysql_health.trace" ? 1 : 0;
+ if ($self->{verbose}) {
+ printf("%s: ", scalar localtime);
+ printf($format, @_);
+ }
+ if ($self->{trace}) {
+ my $logfh = new IO::File;
+ $logfh->autoflush(1);
+ if ($logfh->open("/tmp/check_mysql_health.trace", "a")) {
+ $logfh->printf("%s: ", scalar localtime);
+ $logfh->printf($format, @_);
+ $logfh->printf("\n");
+ $logfh->close();
+ }
+ }
+}
+
+sub DESTROY {
+ my $self = shift;
+ my $handle1 = "null";
+ my $handle2 = "null";
+ if (defined $self->{handle}) {
+ $handle1 = ref($self->{handle});
+ if (defined $self->{handle}->{handle}) {
+ $handle2 = ref($self->{handle}->{handle});
+ }
+ }
+ $self->trace(sprintf "DESTROY %s with handle %s %s", ref($self), $handle1, $handle2);
+ if (ref($self) eq "DBD::MySQL::Server") {
+ }
+ $self->trace(sprintf "DESTROY %s exit with handle %s %s", ref($self), $handle1, $handle2);
+ if (ref($self) eq "DBD::MySQL::Server") {
+ #printf "humpftata\n";
+ }
+}
+
+sub save_state {
+ my $self = shift;
+ my %params = @_;
+ my $extension = "";
+ mkdir $params{statefilesdir} unless -d $params{statefilesdir};
+ my $statefile = sprintf "%s/%s_%s",
+ $params{statefilesdir}, $params{hostname}, $params{mode};
+ $extension .= $params{differenciator} ? "_".$params{differenciator} : "";
+ $extension .= $params{socket} ? "_".$params{socket} : "";
+ $extension .= $params{port} ? "_".$params{port} : "";
+ $extension .= $params{database} ? "_".$params{database} : "";
+ $extension .= $params{tablespace} ? "_".$params{tablespace} : "";
+ $extension .= $params{datafile} ? "_".$params{datafile} : "";
+ $extension .= $params{name} ? "_".$params{name} : "";
+ $extension =~ s/\//_/g;
+ $extension =~ s/\(/_/g;
+ $extension =~ s/\)/_/g;
+ $extension =~ s/\*/_/g;
+ $extension =~ s/\s/_/g;
+ $statefile .= $extension;
+ $statefile = lc $statefile;
+ open(STATE, ">$statefile");
+ if ((ref($params{save}) eq "HASH") && exists $params{save}->{timestamp}) {
+ $params{save}->{localtime} = scalar localtime $params{save}->{timestamp};
+ }
+ printf STATE Data::Dumper::Dumper($params{save});
+ close STATE;
+ $self->debug(sprintf "saved %s to %s",
+ Data::Dumper::Dumper($params{save}), $statefile);
+}
+
+sub load_state {
+ my $self = shift;
+ my %params = @_;
+ my $extension = "";
+ my $statefile = sprintf "%s/%s_%s",
+ $params{statefilesdir}, $params{hostname}, $params{mode};
+ $extension .= $params{differenciator} ? "_".$params{differenciator} : "";
+ $extension .= $params{socket} ? "_".$params{socket} : "";
+ $extension .= $params{port} ? "_".$params{port} : "";
+ $extension .= $params{database} ? "_".$params{database} : "";
+ $extension .= $params{tablespace} ? "_".$params{tablespace} : "";
+ $extension .= $params{datafile} ? "_".$params{datafile} : "";
+ $extension .= $params{name} ? "_".$params{name} : "";
+ $extension =~ s/\//_/g;
+ $extension =~ s/\(/_/g;
+ $extension =~ s/\)/_/g;
+ $extension =~ s/\*/_/g;
+ $extension =~ s/\s/_/g;
+ $statefile .= $extension;
+ $statefile = lc $statefile;
+ if ( -f $statefile) {
+ our $VAR1;
+ eval {
+ require $statefile;
+ };
+ if($@) {
+printf "rumms\n";
+ }
+ $self->debug(sprintf "load %s", Data::Dumper::Dumper($VAR1));
+ return $VAR1;
+ } else {
+ return undef;
+ }
+}
+
+sub valdiff {
+ my $self = shift;
+ my $pparams = shift;
+ my %params = %{$pparams};
+ my @keys = @_;
+ my $now = time;
+ my $last_values = $self->load_state(%params) || eval {
+ my $empty_events = {};
+ foreach (@keys) {
+ $empty_events->{$_} = 0;
+ }
+ $empty_events->{timestamp} = 0;
+ if ($params{lookback}) {
+ $empty_events->{lookback_history} = {};
+ }
+ $empty_events;
+ };
+ foreach (@keys) {
+ if ($params{lookback}) {
+ # find a last_value in the history which fits lookback best
+ # and overwrite $last_values->{$_} with historic data
+ if (exists $last_values->{lookback_history}->{$_}) {
+ foreach my $date (sort {$a <=> $b} keys %{$last_values->{lookback_history}->{$_}}) {
+ if ($date >= ($now - $params{lookback})) {
+ $last_values->{$_} = $last_values->{lookback_history}->{$_}->{$date};
+ $last_values->{timestamp} = $date;
+ last;
+ } else {
+ delete $last_values->{lookback_history}->{$_}->{$date};
+ }
+ }
+ }
+ }
+ $last_values->{$_} = 0 if ! exists $last_values->{$_};
+ if ($self->{$_} >= $last_values->{$_}) {
+ $self->{'delta_'.$_} = $self->{$_} - $last_values->{$_};
+ } else {
+ # vermutlich db restart und zaehler alle auf null
+ $self->{'delta_'.$_} = $self->{$_};
+ }
+ $self->debug(sprintf "delta_%s %f", $_, $self->{'delta_'.$_});
+ }
+ $self->{'delta_timestamp'} = $now - $last_values->{timestamp};
+ $params{save} = eval {
+ my $empty_events = {};
+ foreach (@keys) {
+ $empty_events->{$_} = $self->{$_};
+ }
+ $empty_events->{timestamp} = $now;
+ if ($params{lookback}) {
+ $empty_events->{lookback_history} = $last_values->{lookback_history};
+ foreach (@keys) {
+ $empty_events->{lookback_history}->{$_}->{$now} = $self->{$_};
+ }
+ }
+ $empty_events;
+ };
+ $self->save_state(%params);
+}
+
+sub requires_version {
+ my $self = shift;
+ my $version = shift;
+ my @instances = DBD::MySQL::Server::return_servers();
+ my $instversion = $instances[0]->{version};
+ if (! $self->version_is_minimum($version)) {
+ $self->add_nagios($ERRORS{UNKNOWN},
+ sprintf "not implemented/possible for MySQL release %s", $instversion);
+ }
+}
+
+sub version_is_minimum {
+ # the current version is newer or equal
+ my $self = shift;
+ my $version = shift;
+ my $newer = 1;
+ my @instances = DBD::MySQL::Server::return_servers();
+ my @v1 = map { $_ eq "x" ? 0 : $_ } split(/\./, $version);
+ my @v2 = split(/\./, $instances[0]->{version});
+ if (scalar(@v1) > scalar(@v2)) {
+ push(@v2, (0) x (scalar(@v1) - scalar(@v2)));
+ } elsif (scalar(@v2) > scalar(@v1)) {
+ push(@v1, (0) x (scalar(@v2) - scalar(@v1)));
+ }
+ foreach my $pos (0..$#v1) {
+ if ($v2[$pos] > $v1[$pos]) {
+ $newer = 1;
+ last;
+ } elsif ($v2[$pos] < $v1[$pos]) {
+ $newer = 0;
+ last;
+ }
+ }
+ #printf STDERR "check if %s os minimum %s\n", join(".", @v2), join(".", @v1);
+ return $newer;
+}
+
+sub instance_thread {
+ my $self = shift;
+ my @instances = DBD::MySQL::Server::return_servers();
+ return $instances[0]->{thread};
+}
+
+sub windows_server {
+ my $self = shift;
+ my @instances = DBD::MySQL::Server::return_servers();
+ if ($instances[0]->{os} =~ /Win/i) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+sub system_vartmpdir {
+ my $self = shift;
+ if ($^O =~ /MSWin/) {
+ return $self->system_tmpdir();
+ } else {
+ return "/var/tmp/check_mysql_health";
+ }
+}
+
+sub system_oldvartmpdir {
+ my $self = shift;
+ return "/tmp";
+}
+
+sub system_tmpdir {
+ my $self = shift;
+ if ($^O =~ /MSWin/) {
+ return $ENV{TEMP} if defined $ENV{TEMP};
+ return $ENV{TMP} if defined $ENV{TMP};
+ return File::Spec->catfile($ENV{windir}, 'Temp')
+ if defined $ENV{windir};
+ return 'C:\Temp';
+ } else {
+ return "/tmp";
+ }
+}
+
+
+package DBD::MySQL::Server::Connection;
+
+use strict;
+
+our @ISA = qw(DBD::MySQL::Server);
+
+
+sub new {
+ my $class = shift;
+ my %params = @_;
+ my $self = {
+ mode => $params{mode},
+ timeout => $params{timeout},
+ access => $params{method} || "dbi",
+ hostname => $params{hostname},
+ database => $params{database} || "information_schema",
+ port => $params{port},
+ socket => $params{socket},
+ username => $params{username},
+ password => $params{password},
+ handle => undef,
+ };
+ bless $self, $class;
+ if ($params{method} eq "dbi") {
+ bless $self, "DBD::MySQL::Server::Connection::Dbi";
+ } elsif ($params{method} eq "mysql") {
+ bless $self, "DBD::MySQL::Server::Connection::Mysql";
+ } elsif ($params{method} eq "sqlrelay") {
+ bless $self, "DBD::MySQL::Server::Connection::Sqlrelay";
+ }
+ $self->init(%params);
+ return $self;
+}
+
+
+package DBD::MySQL::Server::Connection::Dbi;
+
+use strict;
+use Net::Ping;
+
+our @ISA = qw(DBD::MySQL::Server::Connection);
+
+
+sub init {
+ my $self = shift;
+ my %params = @_;
+ my $retval = undef;
+ if ($self->{mode} =~ /^server::tnsping/) {
+ if (! $self->{connect}) {
+ $self->{errstr} = "Please specify a database";
+ } else {
+ $self->{sid} = $self->{connect};
+ $self->{username} ||= time; # prefer an existing user
+ $self->{password} = time;
+ }
+ } else {
+ if (($self->{hostname} ne 'localhost') && (! $self->{username} || ! $self->{password})) {
+ $self->{errstr} = "Please specify hostname, username and password";
+ return undef;
+ }
+ $self->{dsn} = "DBI:mysql:";
+ $self->{dsn} .= sprintf "database=%s", $self->{database};
+ $self->{dsn} .= sprintf ";host=%s", $self->{hostname};
+ $self->{dsn} .= sprintf ";port=%s", $self->{port}
+ unless $self->{socket} || $self->{hostname} eq 'localhost';
+ $self->{dsn} .= sprintf ";mysql_socket=%s", $self->{socket}
+ if $self->{socket};
+ }
+ if (! exists $self->{errstr}) {
+ eval {
+ require DBI;
+ use POSIX ':signal_h';
+ local $SIG{'ALRM'} = sub {
+ die "alarm\n";
+ };
+ my $mask = POSIX::SigSet->new( SIGALRM );
+ my $action = POSIX::SigAction->new(
+ sub { die "alarm\n" ; }, $mask);
+ my $oldaction = POSIX::SigAction->new();
+ sigaction(SIGALRM ,$action ,$oldaction );
+ alarm($self->{timeout} - 1); # 1 second before the global unknown timeout
+ if ($self->{handle} = DBI->connect(
+ $self->{dsn},
+ $self->{username},
+ $self->{password},
+ { RaiseError => 0, AutoCommit => 0, PrintError => 0 })) {
+# $self->{handle}->do(q{
+# ALTER SESSION SET NLS_NUMERIC_CHARACTERS=".," });
+ $retval = $self;
+ } else {
+ $self->{errstr} = DBI::errstr();
+ }
+ };
+ if ($@) {
+ $self->{errstr} = $@;
+ $retval = undef;
+ }
+ }
+ $self->{tac} = Time::HiRes::time();
+ return $retval;
+}
+
+sub selectrow_hashref {
+ my $self = shift;
+ my $sql = shift;
+ my @arguments = @_;
+ my $sth = undef;
+ my $hashref = undef;
+ eval {
+ $self->trace(sprintf "SQL:\n%s\nARGS:\n%s\n",
+ $sql, Data::Dumper::Dumper(\@arguments));
+ # helm auf! jetzt wirds dreckig.
+ if ($sql =~ /^\s*SHOW/) {
+ $hashref = $self->{handle}->selectrow_hashref($sql);
+ } else {
+ $sth = $self->{handle}->prepare($sql);
+ if (scalar(@arguments)) {
+ $sth->execute(@arguments);
+ } else {
+ $sth->execute();
+ }
+ $hashref = $sth->selectrow_hashref();
+ }
+ $self->trace(sprintf "RESULT:\n%s\n",
+ Data::Dumper::Dumper($hashref));
+ };
+ if ($@) {
+ $self->debug(sprintf "bumm %s", $@);
+ }
+ if (-f "/tmp/check_mysql_health_simulation/".$self->{mode}) {
+ my $simulation = do { local (@ARGV, $/) =
+ "/tmp/check_mysql_health_simulation/".$self->{mode}; <> };
+ # keine lust auf den scheiss
+ }
+ return $hashref;
+}
+
+sub fetchrow_array {
+ my $self = shift;
+ my $sql = shift;
+ my @arguments = @_;
+ my $sth = undef;
+ my @row = ();
+ eval {
+ $self->trace(sprintf "SQL:\n%s\nARGS:\n%s\n",
+ $sql, Data::Dumper::Dumper(\@arguments));
+ $sth = $self->{handle}->prepare($sql);
+ if (scalar(@arguments)) {
+ $sth->execute(@arguments);
+ } else {
+ $sth->execute();
+ }
+ @row = $sth->fetchrow_array();
+ $self->trace(sprintf "RESULT:\n%s\n",
+ Data::Dumper::Dumper(\@row));
+ };
+ if ($@) {
+ $self->debug(sprintf "bumm %s", $@);
+ }
+ if (-f "/tmp/check_mysql_health_simulation/".$self->{mode}) {
+ my $simulation = do { local (@ARGV, $/) =
+ "/tmp/check_mysql_health_simulation/".$self->{mode}; <> };
+ @row = split(/\s+/, (split(/\n/, $simulation))[0]);
+ }
+ return $row[0] unless wantarray;
+ return @row;
+}
+
+sub fetchall_array {
+ my $self = shift;
+ my $sql = shift;
+ my @arguments = @_;
+ my $sth = undef;
+ my $rows = undef;
+ eval {
+ $self->trace(sprintf "SQL:\n%s\nARGS:\n%s\n",
+ $sql, Data::Dumper::Dumper(\@arguments));
+ $sth = $self->{handle}->prepare($sql);
+ if (scalar(@arguments)) {
+ $sth->execute(@arguments);
+ } else {
+ $sth->execute();
+ }
+ $rows = $sth->fetchall_arrayref();
+ $self->trace(sprintf "RESULT:\n%s\n",
+ Data::Dumper::Dumper($rows));
+ };
+ if ($@) {
+ printf STDERR "bumm %s\n", $@;
+ }
+ if (-f "/tmp/check_mysql_health_simulation/".$self->{mode}) {
+ my $simulation = do { local (@ARGV, $/) =
+ "/tmp/check_mysql_health_simulation/".$self->{mode}; <> };
+ @{$rows} = map { [ split(/\s+/, $_) ] } split(/\n/, $simulation);
+ }
+ return @{$rows};
+}
+
+sub func {
+ my $self = shift;
+ $self->{handle}->func(@_);
+}
+
+
+sub execute {
+ my $self = shift;
+ my $sql = shift;
+ eval {
+ my $sth = $self->{handle}->prepare($sql);
+ $sth->execute();
+ };
+ if ($@) {
+ printf STDERR "bumm %s\n", $@;
+ }
+}
+
+sub errstr {
+ my $self = shift;
+ return $self->{errstr};
+}
+
+sub DESTROY {
+ my $self = shift;
+ $self->trace(sprintf "disconnecting DBD %s",
+ $self->{handle} ? "with handle" : "without handle");
+ $self->{handle}->disconnect() if $self->{handle};
+}
+
+package DBD::MySQL::Server::Connection::Mysql;
+
+use strict;
+use File::Temp qw/tempfile/;
+
+our @ISA = qw(DBD::MySQL::Server::Connection);
+
+
+sub init {
+ my $self = shift;
+ my %params = @_;
+ my $retval = undef;
+ $self->{loginstring} = "traditional";
+ ($self->{sql_commandfile_handle}, $self->{sql_commandfile}) =
+ tempfile($self->{mode}."XXXXX", SUFFIX => ".sql",
+ DIR => $self->system_tmpdir() );
+ close $self->{sql_commandfile_handle};
+ ($self->{sql_resultfile_handle}, $self->{sql_resultfile}) =
+ tempfile($self->{mode}."XXXXX", SUFFIX => ".out",
+ DIR => $self->system_tmpdir() );
+ close $self->{sql_resultfile_handle};
+ if ($self->{mode} =~ /^server::tnsping/) {
+ if (! $self->{connect}) {
+ $self->{errstr} = "Please specify a database";
+ } else {
+ $self->{sid} = $self->{connect};
+ $self->{username} ||= time; # prefer an existing user
+ $self->{password} = time;
+ }
+ } else {
+ if (! $self->{username} || ! $self->{password}) {
+ $self->{errstr} = "Please specify database, username and password";
+ return undef;
+ } elsif (! (($self->{hostname} && $self->{port}) || $self->{socket})) {
+ $self->{errstr} = "Please specify hostname and port or socket";
+ return undef;
+ }
+ }
+ if (! exists $self->{errstr}) {
+ eval {
+ my $mysql = '/'.'usr'.'/'.'bin'.'/'.'mysql';
+ if (! -x $mysql) {
+ die "nomysql\n";
+ }
+ if ($self->{loginstring} eq "traditional") {
+ $self->{sqlplus} = sprintf "%s ", $mysql;
+ $self->{sqlplus} .= sprintf "--batch --raw --skip-column-names ";
+ $self->{sqlplus} .= sprintf "--database=%s ", $self->{database};
+ $self->{sqlplus} .= sprintf "--host=%s ", $self->{hostname};
+ $self->{sqlplus} .= sprintf "--port=%s ", $self->{port}
+ unless $self->{socket} || $self->{hostname} eq "localhost";
+ $self->{sqlplus} .= sprintf "--socket=%s ", $self->{socket}
+ if $self->{socket};
+ $self->{sqlplus} .= sprintf "--user=%s --password=%s < %s > %s",
+ $self->{username}, $self->{password},
+ $self->{sql_commandfile}, $self->{sql_resultfile};
+ }
+
+ use POSIX ':signal_h';
+ local $SIG{'ALRM'} = sub {
+ die "alarm\n";
+ };
+ my $mask = POSIX::SigSet->new( SIGALRM );
+ my $action = POSIX::SigAction->new(
+ sub { die "alarm\n" ; }, $mask);
+ my $oldaction = POSIX::SigAction->new();
+ sigaction(SIGALRM ,$action ,$oldaction );
+ alarm($self->{timeout} - 1); # 1 second before the global unknown timeout
+
+ my $answer = $self->fetchrow_array(
+ q{ SELECT 42 FROM dual});
+ die unless defined $answer and $answer == 42;
+ $retval = $self;
+ };
+ if ($@) {
+ $self->{errstr} = $@;
+ $self->{errstr} =~ s/at $0 .*//g;
+ chomp $self->{errstr};
+ $retval = undef;
+ }
+ }
+ $self->{tac} = Time::HiRes::time();
+ return $retval;
+}
+
+sub selectrow_hashref {
+ my $self = shift;
+ my $sql = shift;
+ my @arguments = @_;
+ my $sth = undef;
+ my $hashref = undef;
+ foreach (@arguments) {
+ # replace the ? by the parameters
+ if (/^\d+$/) {
+ $sql =~ s/\?/$_/;
+ } else {
+ $sql =~ s/\?/'$_'/;
+ }
+ }
+ if ($sql =~ /^\s*SHOW/) {
+ $sql .= '\G'; # http://dev.mysql.com/doc/refman/5.1/de/show-slave-status.html
+ }
+ $self->trace(sprintf "SQL (? resolved):\n%s\nARGS:\n%s\n",
+ $sql, Data::Dumper::Dumper(\@arguments));
+ $self->create_commandfile($sql);
+ my $exit_output = `$self->{sqlplus}`;
+ if ($?) {
+ printf STDERR "fetchrow_array exit bumm \n";
+ my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
+ my @oerrs = map {
+ /((ERROR \d+).*)/ ? $1 : ();
+ } split(/\n/, $output);
+ $self->{errstr} = join(" ", @oerrs);
+ } else {
+ my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
+ if ($sql =~ /^\s*SHOW/) {
+ map {
+ if (/^\s*([\w_]+):\s*(.*)/) {
+ $hashref->{$1} = $2;
+ }
+ } split(/\n/, $output);
+ } else {
+ # i dont mess around here and you shouldn't either
+ }
+ $self->trace(sprintf "RESULT:\n%s\n",
+ Data::Dumper::Dumper($hashref));
+ }
+ unlink $self->{sql_commandfile};
+ unlink $self->{sql_resultfile};
+ return $hashref;
+}
+
+sub fetchrow_array {
+ my $self = shift;
+ my $sql = shift;
+ my @arguments = @_;
+ my $sth = undef;
+ my @row = ();
+ foreach (@arguments) {
+ # replace the ? by the parameters
+ if (/^\d+$/) {
+ $sql =~ s/\?/$_/;
+ } else {
+ $sql =~ s/\?/'$_'/;
+ }
+ }
+ $self->trace(sprintf "SQL (? resolved):\n%s\nARGS:\n%s\n",
+ $sql, Data::Dumper::Dumper(\@arguments));
+ $self->create_commandfile($sql);
+ my $exit_output = `$self->{sqlplus}`;
+ if ($?) {
+ printf STDERR "fetchrow_array exit bumm \n";
+ my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
+ my @oerrs = map {
+ /((ERROR \d+).*)/ ? $1 : ();
+ } split(/\n/, $output);
+ $self->{errstr} = join(" ", @oerrs);
+ } else {
+ my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
+ @row = map { convert($_) }
+ map { s/^\s+([\.\d]+)$/$1/g; $_ } # strip leading space from numbers
+ map { s/\s+$//g; $_ } # strip trailing space
+ split(/\t/, (split(/\n/, $output))[0]);
+ $self->trace(sprintf "RESULT:\n%s\n",
+ Data::Dumper::Dumper(\@row));
+ }
+ if ($@) {
+ $self->debug(sprintf "bumm %s", $@);
+ }
+ unlink $self->{sql_commandfile};
+ unlink $self->{sql_resultfile};
+ return $row[0] unless wantarray;
+ return @row;
+}
+
+sub fetchall_array {
+ my $self = shift;
+ my $sql = shift;
+ my @arguments = @_;
+ my $sth = undef;
+ my $rows = undef;
+ foreach (@arguments) {
+ # replace the ? by the parameters
+ if (/^\d+$/) {
+ $sql =~ s/\?/$_/;
+ } else {
+ $sql =~ s/\?/'$_'/;
+ }
+ }
+ $self->trace(sprintf "SQL (? resolved):\n%s\nARGS:\n%s\n",
+ $sql, Data::Dumper::Dumper(\@arguments));
+ $self->create_commandfile($sql);
+ my $exit_output = `$self->{sqlplus}`;
+ if ($?) {
+ printf STDERR "fetchrow_array exit bumm %s\n", $exit_output;
+ my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
+ my @oerrs = map {
+ /((ERROR \d+).*)/ ? $1 : ();
+ } split(/\n/, $output);
+ $self->{errstr} = join(" ", @oerrs);
+ } else {
+ my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
+ my @rows = map { [
+ map { convert($_) }
+ map { s/^\s+([\.\d]+)$/$1/g; $_ }
+ map { s/\s+$//g; $_ }
+ split /\t/
+ ] } grep { ! /^\d+ rows selected/ }
+ grep { ! /^Elapsed: / }
+ grep { ! /^\s*$/ } split(/\n/, $output);
+ $rows = \@rows;
+ $self->trace(sprintf "RESULT:\n%s\n",
+ Data::Dumper::Dumper($rows));
+ }
+ if ($@) {
+ $self->debug(sprintf "bumm %s", $@);
+ }
+ unlink $self->{sql_commandfile};
+ unlink $self->{sql_resultfile};
+ return @{$rows};
+}
+
+sub func {
+ my $self = shift;
+ my $function = shift;
+ $self->{handle}->func(@_);
+}
+
+sub convert {
+ my $n = shift;
+ # mostly used to convert numbers in scientific notation
+ if ($n =~ /^\s*\d+\s*$/) {
+ return $n;
+ } elsif ($n =~ /^\s*([-+]?)(\d*[\.,]*\d*)[eE]{1}([-+]?)(\d+)\s*$/) {
+ my ($vor, $num, $sign, $exp) = ($1, $2, $3, $4);
+ $n =~ s/E/e/g;
+ $n =~ s/,/\./g;
+ $num =~ s/,/\./g;
+ my $sig = $sign eq '-' ? "." . ($exp - 1 + length $num) : '';
+ my $dec = sprintf "%${sig}f", $n;
+ $dec =~ s/\.[0]+$//g;
+ return $dec;
+ } elsif ($n =~ /^\s*([-+]?)(\d+)[\.,]*(\d*)\s*$/) {
+ return $1.$2.".".$3;
+ } elsif ($n =~ /^\s*(.*?)\s*$/) {
+ return $1;
+ } else {
+ return $n;
+ }
+}
+
+
+sub execute {
+ my $self = shift;
+ my $sql = shift;
+ eval {
+ my $sth = $self->{handle}->prepare($sql);
+ $sth->execute();
+ };
+ if ($@) {
+ printf STDERR "bumm %s\n", $@;
+ }
+}
+
+sub errstr {
+ my $self = shift;
+ return $self->{errstr};
+}
+
+sub DESTROY {
+ my $self = shift;
+ $self->trace("try to clean up command and result files");
+ unlink $self->{sql_commandfile} if -f $self->{sql_commandfile};
+ unlink $self->{sql_resultfile} if -f $self->{sql_resultfile};
+}
+
+sub create_commandfile {
+ my $self = shift;
+ my $sql = shift;
+ open CMDCMD, "> $self->{sql_commandfile}";
+ printf CMDCMD "%s\n", $sql;
+ close CMDCMD;
+}
+
+
+package DBD::MySQL::Server::Connection::Sqlrelay;
+
+use strict;
+use Net::Ping;
+
+our @ISA = qw(DBD::MySQL::Server::Connection);
+
+
+sub init {
+ my $self = shift;
+ my %params = @_;
+ my $retval = undef;
+ if ($self->{mode} =~ /^server::tnsping/) {
+ if (! $self->{connect}) {
+ $self->{errstr} = "Please specify a database";
+ } else {
+ if ($self->{connect} =~ /([\.\w]+):(\d+)/) {
+ $self->{host} = $1;
+ $self->{port} = $2;
+ $self->{socket} = "";
+ } elsif ($self->{connect} =~ /([\.\w]+):([\w\/]+)/) {
+ $self->{host} = $1;
+ $self->{socket} = $2;
+ $self->{port} = "";
+ }
+ }
+ } else {
+ if (! $self->{hostname} || ! $self->{username} || ! $self->{password}) {
+ if ($self->{hostname} && $self->{hostname} =~ /(\w+)\/(\w+)@([\.\w]+):(\d+)/) {
+ $self->{username} = $1;
+ $self->{password} = $2;
+ $self->{hostname} = $3;
+ $self->{port} = $4;
+ $self->{socket} = "";
+ } elsif ($self->{hostname} && $self->{hostname} =~ /(\w+)\/(\w+)@([\.\w]+):([\w\/]+)/) {
+ $self->{username} = $1;
+ $self->{password} = $2;
+ $self->{hostname} = $3;
+ $self->{socket} = $4;
+ $self->{port} = "";
+ } else {
+ $self->{errstr} = "Please specify database, username and password";
+ return undef;
+ }
+ } else {
+ if ($self->{hostname} =~ /([\.\w]+):(\d+)/) {
+ $self->{hostname} = $1;
+ $self->{port} = $2;
+ $self->{socket} = "";
+ } elsif ($self->{hostname} =~ /([\.\w]+):([\w\/]+)/) {
+ $self->{hostname} = $1;
+ $self->{socket} = $2;
+ $self->{port} = "";
+ } else {
+ $self->{errstr} = "Please specify hostname, username, password and port/socket";
+ return undef;
+ }
+ }
+ }
+ if (! exists $self->{errstr}) {
+ eval {
+ require DBI;
+ use POSIX ':signal_h';
+ local $SIG{'ALRM'} = sub {
+ die "alarm\n";
+ };
+ my $mask = POSIX::SigSet->new( SIGALRM );
+ my $action = POSIX::SigAction->new(
+ sub { die "alarm\n" ; }, $mask);
+ my $oldaction = POSIX::SigAction->new();
+ sigaction(SIGALRM ,$action ,$oldaction );
+ alarm($self->{timeout} - 1); # 1 second before the global unknown timeout
+ if ($self->{handle} = DBI->connect(
+ sprintf("DBI:SQLRelay:host=%s;port=%d;socket=%s",
+ $self->{hostname}, $self->{port}, $self->{socket}),
+ $self->{username},
+ $self->{password},
+ { RaiseError => 1, AutoCommit => 0, PrintError => 1 })) {
+ $retval = $self;
+ if ($self->{mode} =~ /^server::tnsping/ && $self->{handle}->ping()) {
+ # database connected. fake a "unknown user"
+ $self->{errstr} = "ORA-01017";
+ }
+ } else {
+ $self->{errstr} = DBI::errstr();
+ }
+ };
+ if ($@) {
+ $self->{errstr} = $@;
+ $self->{errstr} =~ s/at [\w\/\.]+ line \d+.*//g;
+ $retval = undef;
+ }
+ }
+ $self->{tac} = Time::HiRes::time();
+ return $retval;
+}
+
+sub fetchrow_array {
+ my $self = shift;
+ my $sql = shift;
+ my @arguments = @_;
+ my $sth = undef;
+ my @row = ();
+ $self->trace(sprintf "fetchrow_array: %s", $sql);
+ eval {
+ $sth = $self->{handle}->prepare($sql);
+ if (scalar(@arguments)) {
+ $sth->execute(@arguments);
+ } else {
+ $sth->execute();
+ }
+ @row = $sth->fetchrow_array();
+ };
+ if ($@) {
+ $self->debug(sprintf "bumm %s", $@);
+ }
+ if (-f "/tmp/check_mysql_health_simulation/".$self->{mode}) {
+ my $simulation = do { local (@ARGV, $/) =
+ "/tmp/check_mysql_health_simulation/".$self->{mode}; <> };
+ @row = split(/\s+/, (split(/\n/, $simulation))[0]);
+ }
+ return $row[0] unless wantarray;
+ return @row;
+}
+
+sub fetchall_array {
+ my $self = shift;
+ my $sql = shift;
+ my @arguments = @_;
+ my $sth = undef;
+ my $rows = undef;
+ $self->trace(sprintf "fetchall_array: %s", $sql);
+ eval {
+ $sth = $self->{handle}->prepare($sql);
+ if (scalar(@arguments)) {
+ $sth->execute(@arguments);
+ } else {
+ $sth->execute();
+ }
+ $rows = $sth->fetchall_arrayref();
+ };
+ if ($@) {
+ printf STDERR "bumm %s\n", $@;
+ }
+ if (-f "/tmp/check_mysql_health_simulation/".$self->{mode}) {
+ my $simulation = do { local (@ARGV, $/) =
+ "/tmp/check_mysql_health_simulation/".$self->{mode}; <> };
+ @{$rows} = map { [ split(/\s+/, $_) ] } split(/\n/, $simulation);
+ }
+ return @{$rows};
+}
+
+sub func {
+ my $self = shift;
+ $self->{handle}->func(@_);
+}
+
+sub execute {
+ my $self = shift;
+ my $sql = shift;
+ eval {
+ my $sth = $self->{handle}->prepare($sql);
+ $sth->execute();
+ };
+ if ($@) {
+ printf STDERR "bumm %s\n", $@;
+ }
+}
+
+sub DESTROY {
+ my $self = shift;
+ #$self->trace(sprintf "disconnecting DBD %s",
+ # $self->{handle} ? "with handle" : "without handle");
+ #$self->{handle}->disconnect() if $self->{handle};
+}
+
+
+
+
+package DBD::MySQL::Cluster;
+
+use strict;
+use Time::HiRes;
+use IO::File;
+use Data::Dumper;
+
+
+{
+ our $verbose = 0;
+ our $scream = 0; # scream if something is not implemented
+ our $access = "dbi"; # how do we access the database.
+ our $my_modules_dyn_dir = ""; # where we look for self-written extensions
+
+ my @clusters = ();
+ my $initerrors = undef;
+
+ sub add_cluster {
+ push(@clusters, shift);
+ }
+
+ sub return_clusters {
+ return @clusters;
+ }
+
+ sub return_first_cluster() {
+ return $clusters[0];
+ }
+
+}
+
+sub new {
+ my $class = shift;
+ my %params = @_;
+ my $self = {
+ hostname => $params{hostname},
+ port => $params{port},
+ username => $params{username},
+ password => $params{password},
+ timeout => $params{timeout},
+ warningrange => $params{warningrange},
+ criticalrange => $params{criticalrange},
+ version => 'unknown',
+ nodes => [],
+ ndbd_nodes => 0,
+ ndb_mgmd_nodes => 0,
+ mysqld_nodes => 0,
+ };
+ bless $self, $class;
+ $self->init_nagios();
+ if ($self->connect(%params)) {
+ DBD::MySQL::Cluster::add_cluster($self);
+ $self->init(%params);
+ }
+ return $self;
+}
+
+sub init {
+ my $self = shift;
+ my %params = @_;
+ if ($self->{show}) {
+ my $type = undef;
+ foreach (split /\n/, $self->{show}) {
+ if (/\[(\w+)\((\w+)\)\]\s+(\d+) node/) {
+ $type = uc $2;
+ } elsif (/id=(\d+)(.*)/) {
+ push(@{$self->{nodes}}, DBD::MySQL::Cluster::Node->new(
+ type => $type,
+ id => $1,
+ status => $2,
+ ));
+ }
+ }
+ } else {
+ }
+ if ($params{mode} =~ /^cluster::ndbdrunning/) {
+ foreach my $node (@{$self->{nodes}}) {
+ $node->{type} eq "NDB" && $node->{status} eq "running" && $self->{ndbd_nodes}++;
+ $node->{type} eq "MGM" && $node->{status} eq "running" && $self->{ndb_mgmd_nodes}++;
+ $node->{type} eq "API" && $node->{status} eq "running" && $self->{mysqld_nodes}++;
+ }
+ } else {
+ printf "broken mode %s\n", $params{mode};
+ }
+}
+
+sub dump {
+ my $self = shift;
+ my $message = shift || "";
+ printf "%s %s\n", $message, Data::Dumper::Dumper($self);
+}
+
+sub nagios {
+ my $self = shift;
+ my %params = @_;
+ my $dead_ndb = 0;
+ my $dead_api = 0;
+ if (! $self->{nagios_level}) {
+ if ($params{mode} =~ /^cluster::ndbdrunning/) {
+ foreach my $node (grep { $_->{type} eq "NDB"} @{$self->{nodes}}) {
+ next if $params{selectname} && $params{selectname} ne $_->{id};
+ if (! $node->{connected}) {
+ $self->add_nagios_critical(
+ sprintf "ndb node %d is not connected", $node->{id});
+ $dead_ndb++;
+ }
+ }
+ foreach my $node (grep { $_->{type} eq "API"} @{$self->{nodes}}) {
+ next if $params{selectname} && $params{selectname} ne $_->{id};
+ if (! $node->{connected}) {
+ $self->add_nagios_critical(
+ sprintf "api node %d is not connected", $node->{id});
+ $dead_api++;
+ }
+ }
+ if (! $dead_ndb) {
+ $self->add_nagios_ok("all ndb nodes are connected");
+ }
+ if (! $dead_api) {
+ $self->add_nagios_ok("all api nodes are connected");
+ }
+ }
+ }
+ $self->add_perfdata(sprintf "ndbd_nodes=%d ndb_mgmd_nodes=%d mysqld_nodes=%d",
+ $self->{ndbd_nodes}, $self->{ndb_mgmd_nodes}, $self->{mysqld_nodes});
+}
+
+
+sub init_nagios {
+ my $self = shift;
+ no strict 'refs';
+ if (! ref($self)) {
+ my $nagiosvar = $self."::nagios";
+ my $nagioslevelvar = $self."::nagios_level";
+ $$nagiosvar = {
+ messages => {
+ 0 => [],
+ 1 => [],
+ 2 => [],
+ 3 => [],
+ },
+ perfdata => [],
+ };
+ $$nagioslevelvar = $ERRORS{OK},
+ } else {
+ $self->{nagios} = {
+ messages => {
+ 0 => [],
+ 1 => [],
+ 2 => [],
+ 3 => [],
+ },
+ perfdata => [],
+ };
+ $self->{nagios_level} = $ERRORS{OK},
+ }
+}
+
+sub check_thresholds {
+ my $self = shift;
+ my $value = shift;
+ my $defaultwarningrange = shift;
+ my $defaultcriticalrange = shift;
+ my $level = $ERRORS{OK};
+ $self->{warningrange} = $self->{warningrange} ?
+ $self->{warningrange} : $defaultwarningrange;
+ $self->{criticalrange} = $self->{criticalrange} ?
+ $self->{criticalrange} : $defaultcriticalrange;
+ if ($self->{warningrange} !~ /:/ && $self->{criticalrange} !~ /:/) {
+ # warning = 10, critical = 20, warn if > 10, crit if > 20
+ $level = $ERRORS{WARNING} if $value > $self->{warningrange};
+ $level = $ERRORS{CRITICAL} if $value > $self->{criticalrange};
+ } elsif ($self->{warningrange} =~ /([\d\.]+):/ &&
+ $self->{criticalrange} =~ /([\d\.]+):/) {
+ # warning = 98:, critical = 95:, warn if < 98, crit if < 95
+ $self->{warningrange} =~ /([\d\.]+):/;
+ $level = $ERRORS{WARNING} if $value < $1;
+ $self->{criticalrange} =~ /([\d\.]+):/;
+ $level = $ERRORS{CRITICAL} if $value < $1;
+ }
+ return $level;
+ #
+ # syntax error must be reported with returncode -1
+ #
+}
+
+sub add_nagios {
+ my $self = shift;
+ my $level = shift;
+ my $message = shift;
+ push(@{$self->{nagios}->{messages}->{$level}}, $message);
+ # recalc current level
+ foreach my $llevel qw(CRITICAL WARNING UNKNOWN OK) {
+ if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$llevel}}})) {
+ $self->{nagios_level} = $ERRORS{$llevel};
+ }
+ }
+}
+
+sub add_nagios_ok {
+ my $self = shift;
+ my $message = shift;
+ $self->add_nagios($ERRORS{OK}, $message);
+}
+
+sub add_nagios_warning {
+ my $self = shift;
+ my $message = shift;
+ $self->add_nagios($ERRORS{WARNING}, $message);
+}
+
+sub add_nagios_critical {
+ my $self = shift;
+ my $message = shift;
+ $self->add_nagios($ERRORS{CRITICAL}, $message);
+}
+
+sub add_nagios_unknown {
+ my $self = shift;
+ my $message = shift;
+ $self->add_nagios($ERRORS{UNKNOWN}, $message);
+}
+
+sub add_perfdata {
+ my $self = shift;
+ my $data = shift;
+ push(@{$self->{nagios}->{perfdata}}, $data);
+}
+
+sub merge_nagios {
+ my $self = shift;
+ my $child = shift;
+ foreach my $level (0..3) {
+ foreach (@{$child->{nagios}->{messages}->{$level}}) {
+ $self->add_nagios($level, $_);
+ }
+ #push(@{$self->{nagios}->{messages}->{$level}},
+ # @{$child->{nagios}->{messages}->{$level}});
+ }
+ push(@{$self->{nagios}->{perfdata}}, @{$child->{nagios}->{perfdata}});
+}
+
+
+sub calculate_result {
+ my $self = shift;
+ if ($ENV{NRPE_MULTILINESUPPORT} &&
+ length join(" ", @{$self->{nagios}->{perfdata}}) > 200) {
+ foreach my $level ("CRITICAL", "WARNING", "UNKNOWN", "OK") {
+ # first the bad news
+ if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
+ $self->{nagios_message} .=
+ "\n".join("\n", @{$self->{nagios}->{messages}->{$ERRORS{$level}}});
+ }
+ }
+ $self->{nagios_message} =~ s/^\n//g;
+ $self->{perfdata} = join("\n", @{$self->{nagios}->{perfdata}});
+ } else {
+ foreach my $level ("CRITICAL", "WARNING", "UNKNOWN", "OK") {
+ # first the bad news
+ if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
+ $self->{nagios_message} .=
+ join(", ", @{$self->{nagios}->{messages}->{$ERRORS{$level}}}).", ";
+ }
+ }
+ $self->{nagios_message} =~ s/, $//g;
+ $self->{perfdata} = join(" ", @{$self->{nagios}->{perfdata}});
+ }
+ foreach my $level ("OK", "UNKNOWN", "WARNING", "CRITICAL") {
+ if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
+ $self->{nagios_level} = $ERRORS{$level};
+ }
+ }
+}
+
+sub debug {
+ my $self = shift;
+ my $msg = shift;
+ if ($DBD::MySQL::Cluster::verbose) {
+ printf "%s %s\n", $msg, ref($self);
+ }
+}
+
+sub connect {
+ my $self = shift;
+ my %params = @_;
+ my $retval = undef;
+ $self->{tic} = Time::HiRes::time();
+ eval {
+ use POSIX ':signal_h';
+ local $SIG{'ALRM'} = sub {
+ die "alarm\n";
+ };
+ my $mask = POSIX::SigSet->new( SIGALRM );
+ my $action = POSIX::SigAction->new(
+ sub { die "connection timeout\n" ; }, $mask);
+ my $oldaction = POSIX::SigAction->new();
+ sigaction(SIGALRM ,$action ,$oldaction );
+ alarm($self->{timeout} - 1); # 1 second before the global unknown timeout
+ my $ndb_mgm = "ndb_mgm";
+ $params{hostname} = "127.0.0.1" if ! $params{hostname};
+ $ndb_mgm .= sprintf " --ndb-connectstring=%s", $params{hostname}
+ if $params{hostname};
+ $ndb_mgm .= sprintf ":%d", $params{port}
+ if $params{port};
+ $self->{show} = `$ndb_mgm -e show 2>&1`;
+ if ($? == -1) {
+ $self->add_nagios_critical("ndb_mgm failed to execute $!");
+ } elsif ($? & 127) {
+ $self->add_nagios_critical("ndb_mgm failed to execute $!");
+ } elsif ($? >> 8 != 0) {
+ $self->add_nagios_critical("ndb_mgm unable to connect");
+ } else {
+ if ($self->{show} !~ /Cluster Configuration/) {
+ $self->add_nagios_critical("got no cluster configuration");
+ } else {
+ $retval = 1;
+ }
+ }
+ };
+ if ($@) {
+ $self->{errstr} = $@;
+ $self->{errstr} =~ s/at $0 .*//g;
+ chomp $self->{errstr};
+ $self->add_nagios_critical($self->{errstr});
+ $retval = undef;
+ }
+ $self->{tac} = Time::HiRes::time();
+ return $retval;
+}
+
+sub trace {
+ my $self = shift;
+ my $format = shift;
+ $self->{trace} = -f "/tmp/check_mysql_health.trace" ? 1 : 0;
+ if ($self->{verbose}) {
+ printf("%s: ", scalar localtime);
+ printf($format, @_);
+ }
+ if ($self->{trace}) {
+ my $logfh = new IO::File;
+ $logfh->autoflush(1);
+ if ($logfh->open("/tmp/check_mysql_health.trace", "a")) {
+ $logfh->printf("%s: ", scalar localtime);
+ $logfh->printf($format, @_);
+ $logfh->printf("\n");
+ $logfh->close();
+ }
+ }
+}
+
+sub DESTROY {
+ my $self = shift;
+ my $handle1 = "null";
+ my $handle2 = "null";
+ if (defined $self->{handle}) {
+ $handle1 = ref($self->{handle});
+ if (defined $self->{handle}->{handle}) {
+ $handle2 = ref($self->{handle}->{handle});
+ }
+ }
+ $self->trace(sprintf "DESTROY %s with handle %s %s", ref($self), $handle1, $handle2);
+ if (ref($self) eq "DBD::MySQL::Cluster") {
+ }
+ $self->trace(sprintf "DESTROY %s exit with handle %s %s", ref($self), $handle1, $handle2);
+ if (ref($self) eq "DBD::MySQL::Cluster") {
+ #printf "humpftata\n";
+ }
+}
+
+sub save_state {
+ my $self = shift;
+ my %params = @_;
+ my $extension = "";
+ mkdir $params{statefilesdir} unless -d $params{statefilesdir};
+ my $statefile = sprintf "%s/%s_%s",
+ $params{statefilesdir}, $params{hostname}, $params{mode};
+ $extension .= $params{differenciator} ? "_".$params{differenciator} : "";
+ $extension .= $params{socket} ? "_".$params{socket} : "";
+ $extension .= $params{port} ? "_".$params{port} : "";
+ $extension .= $params{database} ? "_".$params{database} : "";
+ $extension .= $params{tablespace} ? "_".$params{tablespace} : "";
+ $extension .= $params{datafile} ? "_".$params{datafile} : "";
+ $extension .= $params{name} ? "_".$params{name} : "";
+ $extension =~ s/\//_/g;
+ $extension =~ s/\(/_/g;
+ $extension =~ s/\)/_/g;
+ $extension =~ s/\*/_/g;
+ $extension =~ s/\s/_/g;
+ $statefile .= $extension;
+ $statefile = lc $statefile;
+ open(STATE, ">$statefile");
+ if ((ref($params{save}) eq "HASH") && exists $params{save}->{timestamp}) {
+ $params{save}->{localtime} = scalar localtime $params{save}->{timestamp};
+ }
+ printf STATE Data::Dumper::Dumper($params{save});
+ close STATE;
+ $self->debug(sprintf "saved %s to %s",
+ Data::Dumper::Dumper($params{save}), $statefile);
+}
+
+sub load_state {
+ my $self = shift;
+ my %params = @_;
+ my $extension = "";
+ my $statefile = sprintf "%s/%s_%s",
+ $params{statefilesdir}, $params{hostname}, $params{mode};
+ $extension .= $params{differenciator} ? "_".$params{differenciator} : "";
+ $extension .= $params{socket} ? "_".$params{socket} : "";
+ $extension .= $params{port} ? "_".$params{port} : "";
+ $extension .= $params{database} ? "_".$params{database} : "";
+ $extension .= $params{tablespace} ? "_".$params{tablespace} : "";
+ $extension .= $params{datafile} ? "_".$params{datafile} : "";
+ $extension .= $params{name} ? "_".$params{name} : "";
+ $extension =~ s/\//_/g;
+ $extension =~ s/\(/_/g;
+ $extension =~ s/\)/_/g;
+ $extension =~ s/\*/_/g;
+ $extension =~ s/\s/_/g;
+ $statefile .= $extension;
+ $statefile = lc $statefile;
+ if ( -f $statefile) {
+ our $VAR1;
+ eval {
+ require $statefile;
+ };
+ if($@) {
+printf "rumms\n";
+ }
+ $self->debug(sprintf "load %s", Data::Dumper::Dumper($VAR1));
+ return $VAR1;
+ } else {
+ return undef;
+ }
+}
+
+sub valdiff {
+ my $self = shift;
+ my $pparams = shift;
+ my %params = %{$pparams};
+ my @keys = @_;
+ my $last_values = $self->load_state(%params) || eval {
+ my $empty_events = {};
+ foreach (@keys) {
+ $empty_events->{$_} = 0;
+ }
+ $empty_events->{timestamp} = 0;
+ $empty_events;
+ };
+ foreach (@keys) {
+ $self->{'delta_'.$_} = $self->{$_} - $last_values->{$_};
+ $self->debug(sprintf "delta_%s %f", $_, $self->{'delta_'.$_});
+ }
+ $self->{'delta_timestamp'} = time - $last_values->{timestamp};
+ $params{save} = eval {
+ my $empty_events = {};
+ foreach (@keys) {
+ $empty_events->{$_} = $self->{$_};
+ }
+ $empty_events->{timestamp} = time;
+ $empty_events;
+ };
+ $self->save_state(%params);
+}
+
+sub requires_version {
+ my $self = shift;
+ my $version = shift;
+ my @instances = DBD::MySQL::Cluster::return_clusters();
+ my $instversion = $instances[0]->{version};
+ if (! $self->version_is_minimum($version)) {
+ $self->add_nagios($ERRORS{UNKNOWN},
+ sprintf "not implemented/possible for MySQL release %s", $instversion);
+ }
+}
+
+sub version_is_minimum {
+ # the current version is newer or equal
+ my $self = shift;
+ my $version = shift;
+ my $newer = 1;
+ my @instances = DBD::MySQL::Cluster::return_clusters();
+ my @v1 = map { $_ eq "x" ? 0 : $_ } split(/\./, $version);
+ my @v2 = split(/\./, $instances[0]->{version});
+ if (scalar(@v1) > scalar(@v2)) {
+ push(@v2, (0) x (scalar(@v1) - scalar(@v2)));
+ } elsif (scalar(@v2) > scalar(@v1)) {
+ push(@v1, (0) x (scalar(@v2) - scalar(@v1)));
+ }
+ foreach my $pos (0..$#v1) {
+ if ($v2[$pos] > $v1[$pos]) {
+ $newer = 1;
+ last;
+ } elsif ($v2[$pos] < $v1[$pos]) {
+ $newer = 0;
+ last;
+ }
+ }
+ #printf STDERR "check if %s os minimum %s\n", join(".", @v2), join(".", @v1);
+ return $newer;
+}
+
+sub instance_rac {
+ my $self = shift;
+ my @instances = DBD::MySQL::Cluster::return_clusters();
+ return (lc $instances[0]->{parallel} eq "yes") ? 1 : 0;
+}
+
+sub instance_thread {
+ my $self = shift;
+ my @instances = DBD::MySQL::Cluster::return_clusters();
+ return $instances[0]->{thread};
+}
+
+sub windows_cluster {
+ my $self = shift;
+ my @instances = DBD::MySQL::Cluster::return_clusters();
+ if ($instances[0]->{os} =~ /Win/i) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+sub system_vartmpdir {
+ my $self = shift;
+ if ($^O =~ /MSWin/) {
+ return $self->system_tmpdir();
+ } else {
+ return "/var/tmp/check_mysql_health";
+ }
+}
+
+sub system_oldvartmpdir {
+ my $self = shift;
+ return "/tmp";
+}
+
+sub system_tmpdir {
+ my $self = shift;
+ if ($^O =~ /MSWin/) {
+ return $ENV{TEMP} if defined $ENV{TEMP};
+ return $ENV{TMP} if defined $ENV{TMP};
+ return File::Spec->catfile($ENV{windir}, 'Temp')
+ if defined $ENV{windir};
+ return 'C:\Temp';
+ } else {
+ return "/tmp";
+ }
+}
+
+
+package DBD::MySQL::Cluster::Node;
+
+use strict;
+
+our @ISA = qw(DBD::MySQL::Cluster);
+
+
+sub new {
+ my $class = shift;
+ my %params = @_;
+ my $self = {
+ mode => $params{mode},
+ timeout => $params{timeout},
+ type => $params{type},
+ id => $params{id},
+ status => $params{status},
+ };
+ bless $self, $class;
+ $self->init(%params);
+ if ($params{type} eq "NDB") {
+ bless $self, "DBD::MySQL::Cluster::Node::NDB";
+ $self->init(%params);
+ }
+ return $self;
+}
+
+sub init {
+ my $self = shift;
+ my %params = @_;
+ if ($self->{status} =~ /@(\d+\.\d+\.\d+\.\d+)\s/) {
+ $self->{addr} = $1;
+ $self->{connected} = 1;
+ } elsif ($self->{status} =~ /accepting connect from (\d+\.\d+\.\d+\.\d+)/) {
+ $self->{addr} = $1;
+ $self->{connected} = 0;
+ }
+ if ($self->{status} =~ /starting,/) {
+ $self->{status} = "starting";
+ } elsif ($self->{status} =~ /shutting,/) {
+ $self->{status} = "shutting";
+ } else {
+ $self->{status} = $self->{connected} ? "running" : "dead";
+ }
+}
+
+
+package DBD::MySQL::Cluster::Node::NDB;
+
+use strict;
+
+our @ISA = qw(DBD::MySQL::Cluster::Node);
+
+
+sub init {
+ my $self = shift;
+ my %params = @_;
+ if ($self->{status} =~ /Nodegroup:\s*(\d+)/) {
+ $self->{nodegroup} = $1;
+ }
+ $self->{master} = ($self->{status} =~ /Master\)/) ? 1 : 0;
+}
+
+
+package Extraopts;
+
+use strict;
+use File::Basename;
+use Data::Dumper;
+
+sub new {
+ my $class = shift;
+ my %params = @_;
+ my $self = {
+ file => $params{file},
+ commandline => $params{commandline},
+ config => {},
+ section => 'default_no_section',
+ };
+ bless $self, $class;
+ $self->prepare_file_and_section();
+ $self->init();
+ return $self;
+}
+
+sub prepare_file_and_section {
+ my $self = shift;
+ if (! defined $self->{file}) {
+ # ./check_stuff --extra-opts
+ $self->{section} = basename($0);
+ $self->{file} = $self->get_default_file();
+ } elsif ($self->{file} =~ /^[^@]+$/) {
+ # ./check_stuff --extra-opts=special_opts
+ $self->{section} = $self->{file};
+ $self->{file} = $self->get_default_file();
+ } elsif ($self->{file} =~ /^@(.*)/) {
+ # ./check_stuff --extra-opts=@/etc/myconfig.ini
+ $self->{section} = basename($0);
+ $self->{file} = $1;
+ } elsif ($self->{file} =~ /^(.*?)@(.*)/) {
+ # ./check_stuff --extra-opts=special_opts@/etc/myconfig.ini
+ $self->{section} = $1;
+ $self->{file} = $2;
+ }
+}
+
+sub get_default_file {
+ my $self = shift;
+ foreach my $default (qw(/etc/nagios/plugins.ini
+ /usr/local/nagios/etc/plugins.ini
+ /usr/local/etc/nagios/plugins.ini
+ /etc/opt/nagios/plugins.ini
+ /etc/nagios-plugins.ini
+ /usr/local/etc/nagios-plugins.ini
+ /etc/opt/nagios-plugins.ini)) {
+ if (-f $default) {
+ return $default;
+ }
+ }
+ return undef;
+}
+
+sub init {
+ my $self = shift;
+ if (! defined $self->{file}) {
+ $self->{errors} = sprintf 'no extra-opts file specified and no default file found';
+ } elsif (! -f $self->{file}) {
+ $self->{errors} = sprintf 'could not open %s', $self->{file};
+ } else {
+ my $data = do { local (@ARGV, $/) = $self->{file}; <> };
+ my $in_section = 'default_no_section';
+ foreach my $line (split(/\n/, $data)) {
+ if ($line =~ /\[(.*)\]/) {
+ $in_section = $1;
+ } elsif ($line =~ /(.*)=(.*)/) {
+ $self->{config}->{$in_section}->{$1} = $2;
+ }
+ }
+ }
+}
+
+sub is_valid {
+ my $self = shift;
+ return ! exists $self->{errors};
+}
+
+sub overwrite {
+ my $self = shift;
+ my %commandline = ();
+ if (scalar(keys %{$self->{config}->{default_no_section}}) > 0) {
+ foreach (keys %{$self->{config}->{default_no_section}}) {
+ $commandline{$_} = $self->{config}->{default_no_section}->{$_};
+ }
+ }
+ if (exists $self->{config}->{$self->{section}}) {
+ foreach (keys %{$self->{config}->{$self->{section}}}) {
+ $commandline{$_} = $self->{config}->{$self->{section}}->{$_};
+ }
+ }
+ foreach (keys %commandline) {
+ if (! exists $self->{commandline}->{$_}) {
+ $self->{commandline}->{$_} = $commandline{$_};
+ }
+ }
+}
+
+
+
+package main;
+
+use strict;
+use Getopt::Long qw(:config no_ignore_case);
+use File::Basename;
+use lib dirname($0);
+
+
+
+use vars qw ($PROGNAME $REVISION $CONTACT $TIMEOUT $STATEFILESDIR $needs_restart %commandline);
+
+$PROGNAME = "check_mysql_health";
+$REVISION = '$Revision: 2.1.3 $';
+$CONTACT = 'gerhard.lausser@consol.de';
+$TIMEOUT = 60;
+$STATEFILESDIR = '/var/run';
+$needs_restart = 0;
+
+my @modes = (
+ ['server::connectiontime',
+ 'connection-time', undef,
+ 'Time to connect to the server' ],
+ ['server::uptime',
+ 'uptime', undef,
+ 'Time the server is running' ],
+ ['server::instance::connectedthreads',
+ 'threads-connected', undef,
+ 'Number of currently open connections' ],
+ ['server::instance::threadcachehitrate',
+ 'threadcache-hitrate', undef,
+ 'Hit rate of the thread-cache' ],
+ ['server::instance::replication::slavelag',
+ 'slave-lag', ['replication-slave-lag'],
+ 'Seconds behind master' ],
+ ['server::instance::replication::slaveiorunning',
+ 'slave-io-running', ['replication-slave-io-running'],
+ 'Slave io running: Yes' ],
+ ['server::instance::replication::slavesqlrunning',
+ 'slave-sql-running', ['replication-slave-sql-running'],
+ 'Slave sql running: Yes' ],
+ ['server::instance::querycachehitrate',
+ 'qcache-hitrate', ['querycache-hitrate'],
+ 'Query cache hitrate' ],
+ ['server::instance::querycachelowmemprunes',
+ 'qcache-lowmem-prunes', ['querycache-lowmem-prunes'],
+ 'Query cache entries pruned because of low memory' ],
+ ['server::instance::myisam::keycache::hitrate',
+ 'keycache-hitrate', ['myisam-keycache-hitrate'],
+ 'MyISAM key cache hitrate' ],
+ ['server::instance::innodb::bufferpool::hitrate',
+ 'bufferpool-hitrate', ['innodb-bufferpool-hitrate'],
+ 'InnoDB buffer pool hitrate' ],
+ ['server::instance::innodb::bufferpool::waitfree',
+ 'bufferpool-wait-free', ['innodb-bufferpool-wait-free'],
+ 'InnoDB buffer pool waits for clean page available' ],
+ ['server::instance::innodb::logwaits',
+ 'log-waits', ['innodb-log-waits'],
+ 'InnoDB log waits because of a too small log buffer' ],
+ ['server::instance::tablecachehitrate',
+ 'tablecache-hitrate', undef,
+ 'Table cache hitrate' ],
+ ['server::instance::tablelockcontention',
+ 'table-lock-contention', undef,
+ 'Table lock contention' ],
+ ['server::instance::tableindexusage',
+ 'index-usage', undef,
+ 'Usage of indices' ],
+ ['server::instance::tabletmpondisk',
+ 'tmp-disk-tables', undef,
+ 'Percent of temp tables created on disk' ],
+ ['server::instance::openfiles',
+ 'open-files', undef,
+ 'Percent of opened files' ],
+ ['server::instance::slowqueries',
+ 'slow-queries', undef,
+ 'Slow queries' ],
+ ['server::instance::longprocs',
+ 'long-running-procs', undef,
+ 'long running processes' ],
+ ['cluster::ndbdrunning',
+ 'cluster-ndbd-running', undef,
+ 'ndnd nodes are up and running' ],
+ ['server::sql',
+ 'sql', undef,
+ 'any sql command returning a single number' ],
+);
+
+sub print_usage () {
+ print <<EOUS;
+ Usage:
+ $PROGNAME [-v] [-t <timeout>] [[--hostname <hostname>]
+ [--port <port> | --socket <socket>]
+ --username <username> --password <password>] --mode <mode>
+ [--method mysql]
+ $PROGNAME [-h | --help]
+ $PROGNAME [-V | --version]
+
+ Options:
+ --hostname
+ the database server's hostname
+ --port
+ the database's port. (default: 3306)
+ --socket
+ the database's unix socket.
+ --username
+ the mysql db user
+ --password
+ the mysql db user's password
+ --database
+ the database's name. (default: information_schema)
+ --warning
+ the warning range
+ --critical
+ the critical range
+ --mode
+ the mode of the plugin. select one of the following keywords:
+EOUS
+ my $longest = length ((reverse sort {length $a <=> length $b} map { $_->[1] } @modes)[0]);
+ my $format = " %-".
+ (length ((reverse sort {length $a <=> length $b} map { $_->[1] } @modes)[0])).
+ "s\t(%s)\n";
+ foreach (@modes) {
+ printf $format, $_->[1], $_->[3];
+ }
+ printf "\n";
+ print <<EOUS;
+ --name
+ the name of something that needs to be further specified,
+ currently only used for sql statements
+ --name2
+ if name is a sql statement, this statement would appear in
+ the output and the performance data. This can be ugly, so
+ name2 can be used to appear instead.
+ --regexp
+ if this parameter is used, name will be interpreted as a
+ regular expression.
+ --units
+ one of %, KB, MB, GB. This is used for a better output of mode=sql
+ and for specifying thresholds for mode=tablespace-free
+
+ In mode sql you can url-encode the statement so you will not have to mess
+ around with special characters in your Nagios service definitions.
+ Instead of
+ --name="select count(*) from v\$session where status = 'ACTIVE'"
+ you can say
+ --name=select%20count%28%2A%29%20from%20v%24session%20where%20status%20%3D%20%27ACTIVE%27
+ For your convenience you can call check_mysql_health with the --mode encode
+ option and it will encode the standard input.
+
+ You can find the full documentation at
+ http://www.consol.de/opensource/nagios/check-mysql-health
+
+EOUS
+
+}
+
+sub print_help () {
+ print "Copyright (c) 2009 Gerhard Lausser\n\n";
+ print "\n";
+ print " Check various parameters of MySQL databases \n";
+ print "\n";
+ print_usage();
+ support();
+}
+
+
+sub print_revision ($$) {
+ my $commandName = shift;
+ my $pluginRevision = shift;
+ $pluginRevision =~ s/^\$Revision: //;
+ $pluginRevision =~ s/ \$\s*$//;
+ print "$commandName ($pluginRevision)\n";
+ print "This nagios plugin comes with ABSOLUTELY NO WARRANTY. You may redistribute\ncopies of this plugin under the terms of the GNU General Public License.\n";
+}
+
+sub support () {
+ my $support='Send email to gerhard.lausser@consol.de if you have questions\nregarding use of this software. \nPlease include version information with all correspondence (when possible,\nuse output from the --version option of the plugin itself).\n';
+ $support =~ s/@/\@/g;
+ $support =~ s/\\n/\n/g;
+ print $support;
+}
+
+sub contact_author ($$) {
+ my $item = shift;
+ my $strangepattern = shift;
+ if ($commandline{verbose}) {
+ printf STDERR
+ "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n".
+ "You found a line which is not recognized by %s\n".
+ "This means, certain components of your system cannot be checked.\n".
+ "Please contact the author %s and\nsend him the following output:\n\n".
+ "%s /%s/\n\nThank you!\n".
+ "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n",
+ $PROGNAME, $CONTACT, $item, $strangepattern;
+ }
+}
+
+%commandline = ();
+my @params = (
+ "timeout|t=i",
+ "version|V",
+ "help|h",
+ "verbose|v",
+ "debug|d",
+ "hostname|H=s",
+ "database=s",
+ "port|P=s",
+ "socket|S=s",
+ "username|u=s",
+ "password|p=s",
+ "mode|m=s",
+ "name=s",
+ "name2=s",
+ "regexp",
+ "perfdata",
+ "warning=s",
+ "critical=s",
+ "absolute|a",
+ "environment|e=s%",
+ "method=s",
+ "runas|r=s",
+ "scream",
+ "shell",
+ "eyecandy",
+ "encode",
+ "units=s",
+ "lookback=i",
+ "3",
+ "with-mymodules-dyn-dir=s",
+ "extra-opts:s");
+
+if (! GetOptions(\%commandline, @params)) {
+ print_help();
+ exit $ERRORS{UNKNOWN};
+}
+
+if (exists $commandline{'extra-opts'}) {
+ # read the extra file and overwrite other parameters
+ my $extras = Extraopts->new(file => $commandline{'extra-opts'}, commandline =>
+ \%commandline);
+ if (! $extras->is_valid()) {
+ printf "extra-opts are not valid: %s\n", $extras->{errors};
+ exit $ERRORS{UNKNOWN};
+ } else {
+ $extras->overwrite();
+ }
+}
+
+if (exists $commandline{version}) {
+ print_revision($PROGNAME, $REVISION);
+ exit $ERRORS{OK};
+}
+
+if (exists $commandline{help}) {
+ print_help();
+ exit $ERRORS{OK};
+} elsif (! exists $commandline{mode}) {
+ printf "Please select a mode\n";
+ print_help();
+ exit $ERRORS{OK};
+}
+
+if ($commandline{mode} eq "encode") {
+ my $input = <>;
+ chomp $input;
+ $input =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
+ printf "%s\n", $input;
+ exit $ERRORS{OK};
+}
+
+if (exists $commandline{3}) {
+ $ENV{NRPE_MULTILINESUPPORT} = 1;
+}
+
+if (exists $commandline{timeout}) {
+ $TIMEOUT = $commandline{timeout};
+}
+
+if (exists $commandline{verbose}) {
+ $DBD::MySQL::Server::verbose = exists $commandline{verbose};
+}
+
+if (exists $commandline{scream}) {
+# $DBD::MySQL::Server::hysterical = exists $commandline{scream};
+}
+
+if (exists $commandline{method}) {
+ # snmp or mysql cmdline
+} else {
+ $commandline{method} = "dbi";
+}
+
+if (exists $commandline{'with-mymodules-dyn-dir'}) {
+ $DBD::MYSQL::Server::my_modules_dyn_dir = $commandline{'with-mymodules-dyn-dir
+'};
+} else {
+ $DBD::MYSQL::Server::my_modules_dyn_dir = '/usr/local/nagios/libexec';
+}
+
+if (exists $commandline{environment}) {
+ # if the desired environment variable values are different from
+ # the environment of this running script, then a restart is necessary.
+ # because setting $ENV does _not_ change the environment of the running script.
+ foreach (keys %{$commandline{environment}}) {
+ if ((! $ENV{$_}) || ($ENV{$_} ne $commandline{environment}->{$_})) {
+ $needs_restart = 1;
+ $ENV{$_} = $commandline{environment}->{$_};
+ printf STDERR "new %s=%s forces restart\n", $_, $ENV{$_}
+ if $DBD::MySQL::Server::verbose;
+ }
+ }
+ # e.g. called with --runas dbnagio. shlib_path environment variable is stripped
+ # during the sudo.
+ # so the perl interpreter starts without a shlib_path. but --runas cares for
+ # a --environment shlib_path=...
+ # so setting the environment variable in the code above and restarting the
+ # perl interpreter will help it find shared libs
+}
+
+if (exists $commandline{runas}) {
+ # remove the runas parameter
+ # exec sudo $0 ... the remaining parameters
+ $needs_restart = 1;
+ # if the calling script has a path for shared libs and there is no --environment
+ # parameter then the called script surely needs the variable too.
+ foreach my $important_env qw(LD_LIBRARY_PATH SHLIB_PATH
+ ORACLE_HOME TNS_ADMIN ORA_NLS ORA_NLS33 ORA_NLS10) {
+ if ($ENV{$important_env} && ! scalar(grep { /^$important_env=/ }
+ keys %{$commandline{environment}})) {
+ $commandline{environment}->{$important_env} = $ENV{$important_env};
+ printf STDERR "add important --environment %s=%s\n",
+ $important_env, $ENV{$important_env} if $DBD::MySQL::Server::verbose;
+ }
+ }
+}
+
+if ($needs_restart) {
+ my @newargv = ();
+ my $runas = undef;
+ if (exists $commandline{runas}) {
+ $runas = $commandline{runas};
+ delete $commandline{runas};
+ }
+ foreach my $option (keys %commandline) {
+ if (grep { /^$option/ && /=/ } @params) {
+ if (ref ($commandline{$option}) eq "HASH") {
+ foreach (keys %{$commandline{$option}}) {
+ push(@newargv, sprintf "--%s", $option);
+ push(@newargv, sprintf "%s=%s", $_, $commandline{$option}->{$_});
+ }
+ } else {
+ push(@newargv, sprintf "--%s", $option);
+ push(@newargv, sprintf "%s", $commandline{$option});
+ }
+ } else {
+ push(@newargv, sprintf "--%s", $option);
+ }
+ }
+ if ($runas) {
+ exec "sudo", "-S", "-u", $runas, $0, @newargv;
+ } else {
+ exec $0, @newargv;
+ # this makes sure that even a SHLIB or LD_LIBRARY_PATH are set correctly
+ # when the perl interpreter starts. Setting them during runtime does not
+ # help loading e.g. libclntsh.so
+ }
+ exit;
+}
+
+if (exists $commandline{shell}) {
+ # forget what you see here.
+ system("/bin/sh");
+}
+
+if (exists $commandline{name}) {
+ # objects can be encoded like an url
+ # with s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
+ if (($commandline{mode} ne "sql") ||
+ (($commandline{mode} eq "sql") &&
+ ($commandline{name} =~ /select%20/i))) { # protect ... like '%cac%' ... from decoding
+ $commandline{name} =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg;
+ }
+ if ($commandline{name} =~ /^0$/) {
+ # without this, $params{selectname} would be treated like undef
+ $commandline{name} = "00";
+ }
+}
+
+$SIG{'ALRM'} = sub {
+ printf "UNKNOWN - %s timed out after %d seconds\n", $PROGNAME, $TIMEOUT;
+ exit $ERRORS{UNKNOWN};
+};
+alarm($TIMEOUT);
+
+my $nagios_level = $ERRORS{UNKNOWN};
+my $nagios_message = "";
+my $perfdata = "";
+if ($commandline{mode} =~ /^my-([^\-.]+)/) {
+ my $param = $commandline{mode};
+ $param =~ s/\-/::/g;
+ push(@modes, [$param, $commandline{mode}, undef, 'my extension']);
+} elsif ((! grep { $commandline{mode} eq $_ } map { $_->[1] } @modes) &&
+ (! grep { $commandline{mode} eq $_ } map { defined $_->[2] ? @{$_->[2]} : () } @modes)) {
+ printf "UNKNOWN - mode %s\n", $commandline{mode};
+ print_usage();
+ exit 3;
+}
+
+$commandline{mode} = (
+ map { $_->[0] }
+ grep {
+ ($commandline{mode} eq $_->[1]) ||
+ ( defined $_->[2] && grep { $commandline{mode} eq $_ } @{$_->[2]})
+ } @modes
+)[0];
+my %params = (
+ timeout => $TIMEOUT,
+ mode => $commandline{mode},
+ method => $commandline{method} ||
+ $ENV{NAGIOS__SERVICEMYSQL_METH} ||
+ $ENV{NAGIOS__HOSTMYSQL_METH} || 'dbi',
+ hostname => $commandline{hostname} ||
+ $ENV{NAGIOS__SERVICEMYSQL_HOST} ||
+ $ENV{NAGIOS__HOSTMYSQL_HOST} || 'localhost',
+ database => $commandline{database} ||
+ $ENV{NAGIOS__SERVICEMYSQL_DATABASE} ||
+ $ENV{NAGIOS__HOSTMYSQL_DATABASE} || 'information_schema',
+ port => $commandline{port} || (($commandline{mode} =~ /^cluster/) ?
+ ($ENV{NAGIOS__SERVICENDBMGM_PORT} || $ENV{NAGIOS__HOSTNDBMGM_PORT} || 1186) :
+ ($ENV{NAGIOS__SERVICEMYSQL_PORT} || $ENV{NAGIOS__HOSTMYSQL_PORT} || 3306)),
+ socket => $commandline{socket} ||
+ $ENV{NAGIOS__SERVICEMYSQL_SOCKET} ||
+ $ENV{NAGIOS__HOSTMYSQL_SOCKET},
+ username => $commandline{username} ||
+ $ENV{NAGIOS__SERVICEMYSQL_USER} ||
+ $ENV{NAGIOS__HOSTMYSQL_USER},
+ password => $commandline{password} ||
+ $ENV{NAGIOS__SERVICEMYSQL_PASS} ||
+ $ENV{NAGIOS__HOSTMYSQL_PASS},
+ warningrange => $commandline{warning},
+ criticalrange => $commandline{critical},
+ absolute => $commandline{absolute},
+ lookback => $commandline{lookback},
+ selectname => $commandline{name} || $commandline{tablespace} || $commandline{datafile},
+ regexp => $commandline{regexp},
+ name => $commandline{name},
+ name2 => $commandline{name2} || $commandline{name},
+ units => $commandline{units},
+ lookback => $commandline{lookback} || 0,
+ eyecandy => $commandline{eyecandy},
+ statefilesdir => $STATEFILESDIR,
+);
+
+my $server = undef;
+my $cluster = undef;
+
+if ($params{mode} =~ /^(server|my)/) {
+ $server = DBD::MySQL::Server->new(%params);
+ $server->nagios(%params);
+ $server->calculate_result();
+ $nagios_message = $server->{nagios_message};
+ $nagios_level = $server->{nagios_level};
+ $perfdata = $server->{perfdata};
+} elsif ($params{mode} =~ /^cluster/) {
+ $cluster = DBD::MySQL::Cluster->new(%params);
+ $cluster->nagios(%params);
+ $cluster->calculate_result();
+ $nagios_message = $cluster->{nagios_message};
+ $nagios_level = $cluster->{nagios_level};
+ $perfdata = $cluster->{perfdata};
+}
+
+printf "%s - %s", $ERRORCODES{$nagios_level}, $nagios_message;
+printf " | %s", $perfdata if $perfdata;
+printf "\n";
+exit $nagios_level;
+
+
+__END__
+
+
diff --git a/lib/facter/mountpoints.rb b/lib/facter/mountpoints.rb
new file mode 100644
index 0000000..c51e6e6
--- /dev/null
+++ b/lib/facter/mountpoints.rb
@@ -0,0 +1,50 @@
+begin
+
+ mountpoints = []
+ # we show devices, but we avoid outputing duplicate devices
+ devices = []
+ Facter.add("mountpoints") do
+ ignorefs = ["NFS", "nfs", "nfs4", "nfsd", "afs", "binfmt_misc", "proc", "smbfs",
+ "autofs", "iso9660", "ncpfs", "coda", "devpts", "ftpfs", "devfs",
+ "mfs", "shfs", "sysfs", "cifs", "lustre_lite", "tmpfs", "usbfs", "udf",
+ "fusectl", "fuse.snapshotfs", "rpc_pipefs"]
+ begin
+ require 'filesystem'
+ rescue Exception => e
+ confine :kernel => :linux
+ ENV["PATH"]="/bin:/sbin:/usr/bin:/usr/sbin"
+ fs_source = nil
+ if FileTest.exists?("/etc/mtab")
+ fs_source = "/etc/mtab"
+ elsif FileTest.exists?("/proc/mounts")
+ fs_source = "/proc/mounts"
+ end
+
+ mounts = File.read(fs_source).split("\n")
+ mounts.each do |mount|
+ mount = mount.split(" ")
+ if ((not ignorefs.include?(mount[2])) && (mount[3] !~ /bind/) && (not devices.include?(mount[0])) && (not mountpoints.include?(mount[1])))
+ mountpoints.push(mount[1])
+ end
+ devices.push(mount[0]) if not devices.include?(mount[0])
+ end
+ else
+ FileSystem.mounts.each do |m|
+ if ((not ignorefs.include?(m.fstype)) && (m.options !~ /bind/) && !devices.include?(mount[0]))
+ mountpoints.push(m.mount)
+ end
+ devices.push(m.mount) if not devices.include?(m.mount)
+ end
+ end
+ setcode do
+ mountpoints.join(",")
+ end
+ end
+ Facter.add("devices") do
+ setcode do
+ devices.join(",")
+ end
+ end
+
+rescue Exception => e
+end
diff --git a/manifests/apache.pp b/manifests/apache.pp
index 36994f8..c00b1d9 100644
--- a/manifests/apache.pp
+++ b/manifests/apache.pp
@@ -1,4 +1,4 @@
-class nagios::apache {
+class nagios::apache inherits ::apache {
$nagios_httpd = 'apache'
include nagios
}
diff --git a/manifests/base.pp b/manifests/base.pp
index 6106ab2..d5feb32 100644
--- a/manifests/base.pp
+++ b/manifests/base.pp
@@ -1,4 +1,6 @@
class nagios::base {
+ # include the variables
+ include nagios::defaults::vars
package { 'nagios':
alias => 'nagios',
@@ -14,37 +16,37 @@ class nagios::base {
# this file should contain all the nagios_puppet-paths:
file { 'nagios_main_cfg':
- path => "${nagios_cfgdir}/nagios.cfg",
- source => [ "puppet://$server/modules/site-nagios/configs/${fqdn}/nagios.cfg",
- "puppet://$server/modules/site-nagios/configs/${operatingsystem}/nagios.cfg",
- "puppet://$server/modules/site-nagios/configs/nagios.cfg",
- "puppet://$server/modules/nagios/configs/${operatingsystem}/nagios.cfg",
- "puppet://$server/modules/nagios/configs/nagios.cfg" ],
+ path => "${nagios::defaults::vars::int_nagios_cfgdir}/nagios.cfg",
+ source => [ "puppet:///modules/site-nagios/configs/${fqdn}/nagios.cfg",
+ "puppet:///modules/site-nagios/configs/${operatingsystem}/nagios.cfg",
+ "puppet:///modules/site-nagios/configs/nagios.cfg",
+ "puppet:///modules/nagios/configs/${operatingsystem}/nagios.cfg",
+ "puppet:///modules/nagios/configs/nagios.cfg" ],
notify => Service['nagios'],
mode => 0644, owner => root, group => root;
}
file { 'nagios_cgi_cfg':
- path => "${nagios_cfgdir}/cgi.cfg",
- source => [ "puppet://$server/modules/site-nagios/configs/${fqdn}/cgi.cfg",
- "puppet://$server/modules/site-nagios/configs/${operatingsystem}/cgi.cfg",
- "puppet://$server/modules/site-nagios/configs/cgi.cfg",
- "puppet://$server/modules/nagios/configs/${operatingsystem}/cgi.cfg",
- "puppet://$server/modules/nagios/configs/cgi.cfg" ],
+ path => "${nagios::defaults::vars::int_nagios_cfgdir}/cgi.cfg",
+ source => [ "puppet:///modules/site-nagios/configs/${fqdn}/cgi.cfg",
+ "puppet:///modules/site-nagios/configs/${operatingsystem}/cgi.cfg",
+ "puppet:///modules/site-nagios/configs/cgi.cfg",
+ "puppet:///modules/nagios/configs/${operatingsystem}/cgi.cfg",
+ "puppet:///modules/nagios/configs/cgi.cfg" ],
mode => '0644', owner => 'root', group => 0,
notify => Service['apache'],
}
file { 'nagios_htpasswd':
- path => "${nagios_cfgdir}/htpasswd.users",
- source => [ "puppet://$server/modules/site-nagios/htpasswd.users",
- "puppet://$server/modules/nagios/htpasswd.users" ],
+ path => "${nagios::defaults::vars::int_nagios_cfgdir}/htpasswd.users",
+ source => [ "puppet:///modules/site-nagios/htpasswd.users",
+ "puppet:///modules/nagios/htpasswd.users" ],
mode => 0640, owner => root, group => apache;
}
file { 'nagios_private':
- path => "${nagios_cfgdir}/private/",
- source => "puppet://$server/modules/common/empty",
+ path => "${nagios::defaults::vars::int_nagios_cfgdir}/private/",
+ source => "puppet:///modules/common/empty",
ensure => directory,
purge => true,
recurse => true,
@@ -53,96 +55,123 @@ class nagios::base {
}
file { 'nagios_private_resource_cfg':
- path => "${nagios_cfgdir}/private/resource.cfg",
- source => "puppet://$server/nagios/configs/${operatingsystem}/private/resource.cfg.${architecture}",
+ path => "${nagios::defaults::vars::int_nagios_cfgdir}/private/resource.cfg",
+ source => [ "puppet:///modules/nagios/configs/${operatingsystem}/private/resource.cfg.${architecture}",
+ "puppet:///modules/site-nagios/configs/${operatingsystem}/private/resource.cfg.${architecture}" ],
notify => Service['nagios'],
owner => root, group => nagios, mode => '0640';
}
file { 'nagios_confd':
- path => "${nagios_cfgdir}/conf.d/",
- source => "puppet://$server/modules/common/empty",
+ path => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/",
+ source => "puppet:///modules/common/empty",
ensure => directory,
purge => true,
recurse => true,
notify => Service['nagios'],
mode => '0750', owner => root, group => nagios;
}
-
+ Package <<| tag == 'nagios::plugin::deploy::package' |>>
+ File <<| tag == 'nagios::plugin::deploy::file' |>>
Nagios_command <<||>>
- Nagios_contact <<||>>
Nagios_contactgroup <<||>>
- Nagios_host <<||>>
+ Nagios_contact <<||>>
+ Nagios_hostdependency <<||>>
+ Nagios_hostescalation <<||>>
Nagios_hostextinfo <<||>>
Nagios_hostgroup <<||>>
- Nagios_hostgroupescalation <<||>>
- Nagios_service <<||>>
+ Nagios_host <<||>>
Nagios_servicedependency <<||>>
Nagios_serviceescalation <<||>>
+ Nagios_servicegroup <<||>>
Nagios_serviceextinfo <<||>>
+ Nagios_service <<||>>
Nagios_timeperiod <<||>>
+ File <<| tag == 'nagios_plugin' |>>
Nagios_command <||> {
- target => "${nagios_cfgdir}/conf.d/nagios_command.cfg",
+ target => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_command.cfg",
+ require => File['nagios_confd'],
notify => Service['nagios'],
}
Nagios_contact <||> {
- target => "${nagios_cfgdir}/conf.d/nagios_contact.cfg",
+ target => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_contact.cfg",
+ require => File['nagios_confd'],
notify => Service['nagios'],
}
Nagios_contactgroup <||> {
- target => "${nagios_cfgdir}/conf.d/nagios_contactgroup.cfg",
+ target => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_contactgroup.cfg",
+ require => File['nagios_confd'],
notify => Service['nagios'],
}
Nagios_host <||> {
- target => "${nagios_cfgdir}/conf.d/nagios_host.cfg",
+ target => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_host.cfg",
+ require => File['nagios_confd'],
+ notify => Service['nagios'],
+ }
+ Nagios_hostdependency <||> {
+ target => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_hostdependency.cfg",
+ notify => Service['nagios'],
+ }
+ Nagios_hostescalation <||> {
+ target => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_hostescalation.cfg",
notify => Service['nagios'],
}
Nagios_hostextinfo <||> {
- target => "${nagios_cfgdir}/conf.d/nagios_hostextinfo.cfg",
+ target => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_hostextinfo.cfg",
+ require => File['nagios_confd'],
notify => Service['nagios'],
}
Nagios_hostgroup <||> {
- target => "${nagios_cfgdir}/conf.d/nagios_hostgroup.cfg",
+ target => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_hostgroup.cfg",
+ require => File['nagios_confd'],
notify => Service['nagios'],
}
- Nagios_hostgroupescalation <||> {
- target => "${nagios_cfgdir}/conf.d/nagios_hostgroupescalation.cfg",
+ Nagios_service <||> {
+ target => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_service.cfg",
+ require => File['nagios_confd'],
notify => Service['nagios'],
}
- Nagios_service <||> {
- target => "${nagios_cfgdir}/conf.d/nagios_service.cfg",
+ Nagios_servicegroup <||> {
+ target => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_servicegroup.cfg",
notify => Service['nagios'],
}
Nagios_servicedependency <||> {
- target => "${nagios_cfgdir}/conf.d/nagios_servicedependency.cfg",
+ target => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_servicedependency.cfg",
+ require => File['nagios_confd'],
notify => Service['nagios'],
}
Nagios_serviceescalation <||> {
- target => "${nagios_cfgdir}/conf.d/nagios_serviceescalation.cfg",
+ target => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_serviceescalation.cfg",
+ require => File['nagios_confd'],
notify => Service['nagios'],
}
Nagios_serviceextinfo <||> {
- target => "${nagios_cfgdir}/conf.d/nagios_serviceextinfo.cfg",
+ target => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_serviceextinfo.cfg",
+ require => File['nagios_confd'],
notify => Service['nagios'],
}
Nagios_timeperiod <||> {
- target => "${nagios_cfgdir}/conf.d/nagios_timeperiod.cfg",
+ target => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_timeperiod.cfg",
+ require => File['nagios_confd'],
notify => Service['nagios'],
}
- file{[ "${nagios_cfgdir}/conf.d/nagios_command.cfg",
- "${nagios_cfgdir}/conf.d/nagios_contact.cfg",
- "${nagios_cfgdir}/conf.d/nagios_contactgroup.cfg",
- "${nagios_cfgdir}/conf.d/nagios_host.cfg",
- "${nagios_cfgdir}/conf.d/nagios_hostextinfo.cfg",
- "${nagios_cfgdir}/conf.d/nagios_hostgroup.cfg",
- "${nagios_cfgdir}/conf.d/nagios_hostgroupescalation.cfg",
- "${nagios_cfgdir}/conf.d/nagios_service.cfg",
- "${nagios_cfgdir}/conf.d/nagios_servicedependency.cfg",
- "${nagios_cfgdir}/conf.d/nagios_serviceescalation.cfg",
- "${nagios_cfgdir}/conf.d/nagios_serviceextinfo.cfg",
- "${nagios_cfgdir}/conf.d/nagios_timeperiod.cfg" ]:
+ file{[ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_command.cfg",
+ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_contact.cfg",
+ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_contactgroup.cfg",
+ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_host.cfg",
+ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_hostdependency.cfg",
+ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_hostescalation.cfg",
+ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_hostextinfo.cfg",
+ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_hostgroup.cfg",
+ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_hostgroupescalation.cfg",
+ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_service.cfg",
+ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_servicedependency.cfg",
+ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_serviceescalation.cfg",
+ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_serviceextinfo.cfg",
+ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_servicegroup.cfg",
+ "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_timeperiod.cfg" ]:
ensure => file,
replace => false,
notify => Service['nagios'],
@@ -152,8 +181,8 @@ class nagios::base {
# manage nagios cfg files
# must be defined after exported resource overrides and cfg file defs
file { 'nagios_cfgdir':
- path => "${nagios_cfgdir}/",
- source => "puppet://$server/modules/common/empty",
+ path => "${nagios::defaults::vars::int_nagios_cfgdir}/",
+ source => "puppet:///modules/common/empty",
ensure => directory,
recurse => true,
purge => true,
diff --git a/manifests/centos.pp b/manifests/centos.pp
index 4d9507b..35379b8 100644
--- a/manifests/centos.pp
+++ b/manifests/centos.pp
@@ -1,6 +1,6 @@
class nagios::centos inherits nagios::base {
- package { [ 'nagios-plugins', 'nagios-plugins-smtp','nagios-plugins-http', 'nagios-plugins-ssh', 'nagios-plugins-udp', 'nagios-plugins-tcp', 'nagios-plugins-dig', 'nagios-plugins-nrpe', 'nagios-plugins-load', 'nagios-plugins-dns', 'nagios-plugins-ping', 'nagios-plugins-procs', 'nagios-plugins-users', 'nagios-plugins-ldap', 'nagios-plugins-disk', 'nagios-plugins-swap', 'nagios-plugins-nagios', 'nagios-plugins-perl', 'nagios-plugins-ntp', 'nagios-plugins-snmp' ]:
+ package { [ 'nagios-plugins', 'nagios-plugins-smtp','nagios-plugins-http', 'nagios-plugins-ssh', 'nagios-plugins-tcp', 'nagios-plugins-dig', 'nagios-plugins-nrpe', 'nagios-plugins-load', 'nagios-plugins-dns', 'nagios-plugins-ping', 'nagios-plugins-procs', 'nagios-plugins-users', 'nagios-plugins-ldap', 'nagios-plugins-disk', 'nagios-plugins-swap', 'nagios-plugins-nagios', 'nagios-plugins-perl', 'nagios-plugins-ntp', 'nagios-plugins-snmp' ]:
ensure => 'present',
notify => Service['nagios'],
}
diff --git a/manifests/command/imap_pop3.pp b/manifests/command/imap_pop3.pp
new file mode 100644
index 0000000..174b09c
--- /dev/null
+++ b/manifests/command/imap_pop3.pp
@@ -0,0 +1,14 @@
+class nagios::command::imap_pop3 {
+ nagios_command{
+ 'check_imap':
+ command_line => '$USER1$/check_imap -H $ARG1$ -p $ARG2$';
+ 'check_imap_ssl':
+ command_line => '$USER1$/check_imap -H $ARG1$ -p $ARG2$ -S';
+ 'check_pop3':
+ command_line => '$USER1$/check_pop -H $ARG1$ -p $ARG2$';
+ 'check_pop3_ssl':
+ command_line => '$USER1$/check_pop -H $ARG1$ -p $ARG2$ -S';
+ 'check_managesieve':
+ command_line => '$USER1$/check_tcp -H $ARG1$ -p 2000';
+ }
+}
diff --git a/manifests/command/smtp.pp b/manifests/command/smtp.pp
new file mode 100644
index 0000000..04f8543
--- /dev/null
+++ b/manifests/command/smtp.pp
@@ -0,0 +1,14 @@
+class nagios::command::smtp {
+ nagios_command{
+ 'check_smtp':
+ command_line => '$USER1$/check_smtp -H $ARG1$ -p $ARG2$';
+ 'check_smtp_tls':
+ command_line => '$USER1$/check_smtp -H $ARG1$ -p $ARG2$ -S';
+ 'check_smtp_cert':
+ command_line => '$USER1$/check_smtp -H $ARG1$ -p $ARG2$ -S -D $ARG3$';
+ 'check_ssmtp':
+ command_line => '$USER1$/check_ssmtp -H $ARG1$ -p $ARG2$ -S';
+ 'check_ssmtp_cert':
+ command_line => '$USER1$/check_ssmtp -H $ARG1$ -p $ARG2$ -S -D $ARG3$';
+ }
+}
diff --git a/manifests/debian.pp b/manifests/debian.pp
index a503324..b9e87ec 100644
--- a/manifests/debian.pp
+++ b/manifests/debian.pp
@@ -14,7 +14,7 @@ class nagios::debian inherits nagios::base {
File['nagios_htpasswd', 'nagios_cgi_cfg'] { group => 'www-data' }
- file { "${nagios_cfgdir}/stylesheets":
+ file { "${nagios::defaults::vars::int_nagios_cfgdir}/stylesheets":
ensure => directory,
purge => false,
recurse => true,
diff --git a/manifests/defaults.pp b/manifests/defaults.pp
index 32ba335..7c25ac9 100644
--- a/manifests/defaults.pp
+++ b/manifests/defaults.pp
@@ -8,5 +8,5 @@ class nagios::defaults {
include nagios::defaults::hostgroups
include nagios::defaults::templates
include nagios::defaults::timeperiods
-
+ include nagios::defaults::plugins
}
diff --git a/manifests/defaults/commands.pp b/manifests/defaults/commands.pp
index c3190af..933f3b9 100644
--- a/manifests/defaults/commands.pp
+++ b/manifests/defaults/commands.pp
@@ -1,55 +1,91 @@
class nagios::defaults::commands {
- # common service commands
+ include nagios::command::smtp
+ include nagios::command::imap_pop3
- nagios_command {
- check_dummy:
+ # common service commands
+ case $operatingsystem {
+ debian,ubuntu: {
+ nagios_command {
+ check_dummy:
command_line => '$USER1$/check_dummy $ARG1$';
- check_ping:
+ check_https_cert:
+ command_line => '$USER1$/check_http --ssl -C 20 -H $HOSTADDRESS$ -I $HOSTADDRESS$';
+ check_http_url:
+ command_line => '$USER1$/check_http -H $ARG1$ -u $ARG2$';
+ check_http_url_regex:
+ command_line => '$USER1$/check_http -H $ARG1$ -u $ARG2$ -e $ARG3$';
+ check_https_url:
+ command_line => '$USER1$/check_http --ssl -H $ARG1$ -u $ARG2$';
+ check_https_url_regex:
+ command_line => '$USER1$/check_http --ssl -H $ARG1$ -u $ARG2$ -e $ARG3$';
+ check_mysql_db:
+ command_line => '$USER1$/check_mysql -H $ARG1$ -P $ARG2$ -u $ARG3$ -p $ARG4$ -d $ARG5$';
+ check_ntp_time:
+ command_line => '$USER1$/check_ntp_time -H $HOSTADDRESS$ -w 0.5 -c 1';
+ check_silc:
+ command_line => '$USER1$/check_tcp -p 706 -H $ARG1$';
+ check_sobby:
+ command_line => '$USER1$/check_tcp -H $ARG1$ -p $ARG2$';
+ check_jabber:
+ command_line => '$USER1$/check_jabber -H $ARG1$';
+ }
+ }
+ default: {
+ nagios_command {
+ check_dummy:
+ command_line => '$USER1$/check_dummy $ARG1$';
+ check_ping:
command_line => '$USER1$/check_ping -H $HOSTADDRESS$ -w $ARG1$ -c $ARG2$';
- check-host-alive:
+ check-host-alive:
command_line => '$USER1$/check_ping -H $HOSTADDRESS$ -w 5000,100% -c 5000,100% -p 1';
- check_tcp:
+ check_tcp:
command_line => '$USER1$/check_tcp -H $HOSTADDRESS$ -p $ARG1$';
- check_udp:
+ check_udp:
command_line => '$USER1$/check_udp -H $HOSTADDRESS$ -p $ARG1$';
- check_load:
+ check_load:
command_line => '$USER1$/check_load --warning=$ARG1$,$ARG2$,$ARG3$ --critical=$ARG4$,$ARG5$,$ARG6$';
- check_disk:
+ check_disk:
command_line => '$USER1$/check_disk -w $ARG1$ -c $ARG2$ -e -p $ARG3$';
- check_all_disks:
+ check_all_disks:
command_line => '$USER1$/check_disk -w $ARG1$ -c $ARG2$ -e';
- check_ssh:
+ check_ssh:
command_line => '$USER1$/check_ssh $HOSTADDRESS$';
- check_ssh_port:
+ check_ssh_port:
command_line => '$USER1$/check_ssh -p $ARG1$ $HOSTADDRESS$';
- check_http:
+ check_ssh_port_host:
+ command_line => '$USER1$/check_ssh -p $ARG1$ $ARG2$';
+ check_http:
command_line => '$USER1$/check_http -H $HOSTADDRESS$ -I $HOSTADDRESS$';
- check_https:
+ check_https:
command_line => '$USER1$/check_http --ssl -H $HOSTADDRESS$ -I $HOSTADDRESS$';
- check_https_cert:
+ check_https_cert:
command_line => '$USER1$/check_http --ssl -C 20 -H $HOSTADDRESS$ -I $HOSTADDRESS$';
- check_http_url:
+ check_http_url:
command_line => '$USER1$/check_http -H $ARG1$ -u $ARG2$';
- check_http_url_regex:
+ check_http_url_regex:
command_line => '$USER1$/check_http -H $ARG1$ -u $ARG2$ -e $ARG3$';
- check_https_url:
+ check_https_url:
command_line => '$USER1$/check_http --ssl -H $ARG1$ -u $ARG2$';
- check_https_url_regex:
+ check_https_url_regex:
command_line => '$USER1$/check_http --ssl -H $ARG1$ -u $ARG2$ -e $ARG3$';
- check_mysql:
+ check_mysql:
command_line => '$USER1$/check_mysql -H $ARG1$ -P $ARG2$ -u $ARG3$ -p $ARG4$';
- check_mysql_db:
+ check_mysql_db:
command_line => '$USER1$/check_mysql -H $ARG1$ -P $ARG2$ -u $ARG3$ -p $ARG4$ -d $ARG5$';
- check_ntp_time:
+ check_ntp_time:
command_line => '$USER1$/check_ntp_time -H $HOSTADDRESS$ -w 0.5 -c 1';
- check_silc:
+ check_silc:
command_line => '$USER1$/check_tcp -p 706 -H $ARG1$';
- check_sobby:
+ check_sobby:
command_line => '$USER1$/check_tcp -H $ARG1$ -p $ARG2$';
- check_jabber:
+ check_jabber:
command_line => '$USER1$/check_jabber -H $ARG1$';
- }
+ check_git:
+ command_line => '$USER1$/check_tcp -H $ARG1$ -p 9418';
+ }
+ }
+ }
# commands for services defined by other modules
@@ -57,9 +93,27 @@ class nagios::defaults::commands {
# from apache module
http_port:
command_line => '$USER1$/check_http -p $ARG1$ -H $HOSTADDRESS$ -I $HOSTADDRESS$';
+
+ check_http_port_url_content:
+ command_line => '$USER1$/check_http -H $ARG1$ -p $ARG2$ -u $ARG3$ -s $ARG4$';
+ check_https_port_url_content:
+ command_line => '$USER1$/check_http --ssl -H $ARG1$ -p $ARG2$ -u $ARG3$ -s $ARG4$';
+ check_http_url_content:
+ command_line => '$USER1$/check_http -H $ARG1$ -u $ARG2$ -s $ARG3$';
+ check_https_url_content:
+ command_line => '$USER1$/check_http --ssl -H $ARG1$ -u $ARG2$ -s $ARG3$';
+
# from bind module
check_dig2:
command_line => '$USER1$/check_dig -H $HOSTADDRESS$ -l $ARG1$ --record_type=$ARG2$';
+
+ # from mysql module
+ check_mysql_health:
+ command_line => '$USER1$/check_mysql_health --hostname $ARG1$ --port $ARG2$ --username $ARG3$ --password $ARG4$ --mode $ARG5$ --database $ARG6$';
+
+ # better check_dns
+ check_dns2:
+ command_line => '$USER1$/check_dns2 -c $ARG1$ A $ARG2$';
}
# notification commands
diff --git a/manifests/defaults/host_templates.pp b/manifests/defaults/host_templates.pp
index 699b965..0f47324 100644
--- a/manifests/defaults/host_templates.pp
+++ b/manifests/defaults/host_templates.pp
@@ -12,12 +12,12 @@ class nagios::defaults::host_templates {
process_perf_data => '1',
retain_status_information => '1',
retain_nonstatus_information => '1',
- check_command => 'check-host-alive',
- max_check_attempts => '10',
- notification_interval => '0',
- notification_period => '24x7',
- notification_options => 'd,u,r',
- contact_groups => 'admins',
+ check_command => 'check-host-alive',
+ max_check_attempts => '10',
+ notification_interval => '0',
+ notification_period => '24x7',
+ notification_options => 'd,u,r',
+ contact_groups => 'admins',
register => '0',
}
diff --git a/manifests/defaults/hostgroups.pp b/manifests/defaults/hostgroups.pp
index 3a4f4d1..8715ade 100644
--- a/manifests/defaults/hostgroups.pp
+++ b/manifests/defaults/hostgroups.pp
@@ -1,13 +1,11 @@
class nagios::defaults::hostgroups {
-
- nagios_hostgroup {
- 'all':
- alias => 'All Servers',
- members => '*';
- 'debian-servers':
- alias => 'Debian GNU/Linux Servers';
- 'centos-servers':
- alias => 'CentOS GNU/Linux Servers';
- }
-
+ nagios_hostgroup {
+ 'all':
+ alias => 'All Servers',
+ members => '*';
+ 'debian-servers':
+ alias => 'Debian GNU/Linux Servers';
+ 'centos-servers':
+ alias => 'CentOS GNU/Linux Servers';
+ }
}
diff --git a/manifests/defaults/plugins.pp b/manifests/defaults/plugins.pp
new file mode 100644
index 0000000..842f9ce
--- /dev/null
+++ b/manifests/defaults/plugins.pp
@@ -0,0 +1,10 @@
+class nagios::defaults::plugins {
+
+ nagios::plugin {
+ 'check_mysql_health':
+ source => 'nagios/plugins/check_mysql_health';
+ 'check_dns2':
+ source => 'nagios/plugins/check_dns2';
+ }
+
+}
diff --git a/manifests/defaults/service_templates.pp b/manifests/defaults/service_templates.pp
index 326ce05..84740af 100644
--- a/manifests/defaults/service_templates.pp
+++ b/manifests/defaults/service_templates.pp
@@ -17,15 +17,15 @@ class nagios::defaults::service_templates {
process_perf_data => '1',
retain_status_information => '1',
retain_nonstatus_information => '1',
- notification_interval => '0',
- is_volatile => '0',
- check_period => '24x7',
- normal_check_interval => '5',
- retry_check_interval => '1',
- max_check_attempts => '4',
- notification_period => '24x7',
- notification_options => 'w,u,c,r',
- contact_groups => 'admins',
+ notification_interval => '0',
+ is_volatile => '0',
+ check_period => '24x7',
+ normal_check_interval => '5',
+ retry_check_interval => '1',
+ max_check_attempts => '4',
+ notification_period => '24x7',
+ notification_options => 'w,u,c,r',
+ contact_groups => 'admins',
register => '0',
}
diff --git a/manifests/defaults/templates.pp b/manifests/defaults/templates.pp
index 5a3d23d..c97eee4 100644
--- a/manifests/defaults/templates.pp
+++ b/manifests/defaults/templates.pp
@@ -1,12 +1,13 @@
class nagios::defaults::templates {
+ include nagios::defaults::vars
file { 'nagios_templates':
- path => "${nagios::nagios_cfgdir}/conf.d/nagios_templates.cfg",
- source => [ "puppet://$server/modules/site-nagios/configs/${fqdn}/nagios_templates.cfg",
- "puppet://$server/modules/site-nagios/configs/${operatingsystem}/nagios_templates.cfg",
- "puppet://$server/modules/site-nagios/configs/nagios_templates.cfg",
- "puppet://$server/modules/nagios/configs/${operatingsystem}/nagios_templates.cfg",
- "puppet://$server/modules/nagios/configs/nagios_templates.cfg" ],
+ path => "${nagios::defaults::vars::int_nagios_cfgdir}/conf.d/nagios_templates.cfg",
+ source => [ "puppet:///modules/site-nagios/configs/${fqdn}/nagios_templates.cfg",
+ "puppet:///modules/site-nagios/configs/${operatingsystem}/nagios_templates.cfg",
+ "puppet:///modules/site-nagios/configs/nagios_templates.cfg",
+ "puppet:///modules/nagios/configs/${operatingsystem}/nagios_templates.cfg",
+ "puppet:///modules/nagios/configs/nagios_templates.cfg" ],
notify => Service['nagios'],
mode => 0644, owner => root, group => root;
}
diff --git a/manifests/defaults/vars.pp b/manifests/defaults/vars.pp
new file mode 100644
index 0000000..6302f13
--- /dev/null
+++ b/manifests/defaults/vars.pp
@@ -0,0 +1,10 @@
+class nagios::defaults::vars {
+ case $nagios_cfgdir {
+ '': { $int_nagios_cfgdir = $operatingsystem ? {
+ centos => '/etc/nagios/',
+ default => '/etc/nagios3'
+ }
+ }
+ default: { $int_nagios_cfgdir = $nagios_cfgdir }
+ }
+}
diff --git a/manifests/irc_bot.pp b/manifests/irc_bot.pp
new file mode 100644
index 0000000..4092e67
--- /dev/null
+++ b/manifests/irc_bot.pp
@@ -0,0 +1,71 @@
+class nagios::irc_bot {
+ if ( ! ($nagios_nsa_server and $nagios_nsa_nickname and $nagios_nsa_channel) ) {
+ fail("Please provide values at least for \$nagios_nsa_server, \$nagios_nsa_nickname and \$nagios_nsa_channel")
+ }
+
+ $nagios_nsa_socket = $nagios_nsa_socket ? {
+ '' => '/var/run/nagios3/nsa.socket',
+ default => $nagios_nsa_socket,
+ }
+ $nagios_nsa_pidfile = $nagios_nsa_pidfile ? {
+ '' => '/var/run/nagios3/nsa.pid',
+ default => $nagios_nsa_pidfile,
+ }
+ $nagios_nsa_port = $nagios_nsa_port ? {
+ '' => '6667',
+ default => $nagios_nsa_port,
+ }
+ $nagios_nsa_realname = $nagios_nsa_realname ? {
+ '' => 'Nagios',
+ default => $nagios_nsa_realname,
+ }
+
+ if (! $nagios_nsa_password) {
+ $nagios_nsa_password = ''
+ }
+
+ file { "/usr/local/bin/riseup-nagios-client.pl":
+ owner => root, group => root, mode => 0755,
+ source => "puppet:///modules/nagios/irc_bot/riseup-nagios-client.pl",
+ }
+ file { "/usr/local/bin/riseup-nagios-server.pl":
+ owner => root, group => root, mode => 0755,
+ source => "puppet:///modules/nagios/irc_bot/riseup-nagios-server.pl",
+ }
+ file { "/etc/init.d/nagios-nsa":
+ owner => root, group => root, mode => 0755,
+ content => template('nagios/irc_bot/nagios-nsa.sh.erb'),
+ require => File["/usr/local/bin/riseup-nagios-server.pl"],
+ }
+ file { "/etc/nagios_nsa.cfg":
+ ensure => present,
+ owner => nagios, group => root, mode => 0400,
+ content => template('nagios/irc_bot/nsa.cfg.erb'),
+ }
+
+ package { "libnet-irc-perl":
+ ensure => present,
+ }
+
+ exec { "nagios_nsa_init_script":
+ command => "/usr/sbin/update-rc.d nagios-nsa defaults",
+ unless => "/bin/ls /etc/rc3.d/ | /bin/grep nagios-nsa",
+ require => File["/etc/init.d/nagios-nsa"],
+ }
+ service { "nagios-nsa":
+ ensure => "running",
+ pattern => "riseup-nagios-server.pl",
+ hasstatus => true,
+ require => [File["/etc/nagios_nsa.cfg"],
+ Exec["nagios_nsa_init_script"],
+ Package["libnet-irc-perl"],
+ Service['nagios'] ],
+ }
+
+ nagios_command {
+ "notify-by-irc":
+ command_line => '/usr/local/bin/riseup-nagios-client.pl "$HOSTNAME$ ($SERVICEDESC$) $NOTIFICATIONTYPE$ n.$SERVICEATTEMPT$ $SERVICESTATETYPE$ $SERVICEEXECUTIONTIME$s $SERVICELATENCY$s $SERVICEOUTPUT$ $SERVICEPERFDATA$"';
+ "host-notify-by-irc":
+ command_line => '/usr/local/bin/riseup-nagios-client.pl "$HOSTNAME$ ($HOSTALIAS$) $NOTIFICATIONTYPE$ n.$HOSTATTEMPT$ $HOSTSTATETYPE$ took $HOSTEXECUTIONTIME$s $HOSTOUTPUT$ $HOSTPERFDATA$ $HOSTLATENCY$s"';
+ }
+}
diff --git a/manifests/nsca/client.pp b/manifests/nsca/client.pp
index b4ca878..6e2c472 100644
--- a/manifests/nsca/client.pp
+++ b/manifests/nsca/client.pp
@@ -3,9 +3,9 @@ class nagios::nsca::client {
package { 'nsca': ensure => installed }
file { '/etc/send_nsca.cfg':
- source => [ "puppet://${server}/site-nagios/nsca/{$fqdn}/send_nsca.cfg",
- "puppet://${server}/site-nagios/nsca/send_nsca.cfg",
- "puppet://${server}/nagios/nsca/send_nsca.cfg" ],
+ source => [ "puppet:///modules/site-nagios/nsca/{$fqdn}/send_nsca.cfg",
+ "puppet:///modules/site-nagios/nsca/send_nsca.cfg",
+ "puppet:///modules/nagios/nsca/send_nsca.cfg" ],
owner => 'nagios',
group => 'nogroup',
mode => '400',
diff --git a/manifests/nsca/server.pp b/manifests/nsca/server.pp
index c682e24..9239f21 100644
--- a/manifests/nsca/server.pp
+++ b/manifests/nsca/server.pp
@@ -9,9 +9,9 @@ class nagios::nsca::server {
}
file { '/etc/nsca.cfg':
- source => [ "puppet://${server}/site-nagios/nsca/{$fqdn}/nsca.cfg",
- "puppet://${server}/site-nagios/nsca/nsca.cfg",
- "puppet://${server}/nagios/nsca/nsca.cfg" ],
+ source => [ "puppet:///modules/site-nagios/nsca/{$fqdn}/nsca.cfg",
+ "puppet:///modules/site-nagios/nsca/nsca.cfg",
+ "puppet:///modules/nagios/nsca/nsca.cfg" ],
owner => 'nagios',
group => 'nogroup',
mode => '400',
diff --git a/manifests/plugin.pp b/manifests/plugin.pp
index 4789345..abaa6e5 100644
--- a/manifests/plugin.pp
+++ b/manifests/plugin.pp
@@ -1,13 +1,18 @@
define nagios::plugin(
+ $source = 'absent',
$ensure = present
){
- file{$name:
+ @@file{$name:
path => $hardwaremodel ? {
'x86_64' => "/usr/lib64/nagios/plugins/$name",
default => "/usr/lib/nagios/plugins/$name",
},
ensure => $ensure,
- source => "puppet://$server/modules/nagios/plugins/$name",
+ source => $source ? {
+ 'absent' => "puppet:///modules/nagios/plugins/$name",
+ default => "puppet:///modules/$source"
+ },
+ tag => 'nagios_plugin',
require => Package['nagios-plugins'],
owner => root, group => 0, mode => 0755;
}
diff --git a/manifests/plugin/deploy.pp b/manifests/plugin/deploy.pp
new file mode 100644
index 0000000..75f87d7
--- /dev/null
+++ b/manifests/plugin/deploy.pp
@@ -0,0 +1,30 @@
+define nagios::plugin::deploy($source = '', $ensure = 'present', $config = '', $require_package = 'nagios-plugins') {
+ $plugin_src = $ensure ? {
+ 'present' => $name,
+ 'absent' => $name,
+ default => $ensure
+ }
+ $real_source = $source ? {
+ '' => "nagios/plugins/$plugin_src",
+ default => $source
+ }
+
+ if !defined(Package[$require_package]) {
+ @@package { $require_package:
+ ensure => installed,
+ tag => "nagios::plugin::deploy::package";
+ }
+ }
+
+ include nagios::plugin::scriptpaths
+ @@file { "nagios_plugin_${name}":
+ path => "$nagios::plugin::scriptpaths::script_path/${name}",
+ source => "puppet://$server/modules/$real_source",
+ mode => 0755, owner => root, group => 0,
+ require => Package[$require_package],
+ tag => "nagios::plugin::deploy::file";
+ }
+
+ # register the plugin
+ @@nagios::plugin{$name: ensure => $ensure, require => Package['nagios-plugins'] }
+}
diff --git a/manifests/plugin/scriptpaths.pp b/manifests/plugin/scriptpaths.pp
new file mode 100644
index 0000000..6065709
--- /dev/null
+++ b/manifests/plugin/scriptpaths.pp
@@ -0,0 +1,6 @@
+class nagios::plugin::scriptpaths {
+ case $hardwaremodel {
+ x86_64: { $script_path = "/usr/lib64/nagios/plugins/" }
+ default: { $script_path = "/usr/lib/nagios/plugins" }
+ }
+}
diff --git a/manifests/service/mysql.pp b/manifests/service/mysql.pp
index ca2ae15..d501f06 100644
--- a/manifests/service/mysql.pp
+++ b/manifests/service/mysql.pp
@@ -1,48 +1,47 @@
-# Checks a mysql instance via tcp or socket
+# Checks a mysql instance via tcp or socket
define nagios::service::mysql(
- $ensure = present,
- $check_hostname = 'absent',
- $check_port = '3306',
- $check_username = 'nagios',
- $check_password = '',
- $check_database = 'absent',
- $check_mode = 'tcp'
-){
- if ($check_hostname == 'absent') {
- fail("Please specify a hostname, ip address or socket to check a mysql instance.")
- }
-
- case $check_mode {
- 'tcp': {
- if ($check_hostname == 'localhost') {
- $real_check_hostname = '127.0.0.1'
- }
- else {
- $real_check_hostname = $check_hostname
- }
- }
- default: {
- if ($check_hostname == '127.0.0.1') {
- $real_check_hostname = 'localhost'
- }
- else {
- $real_check_hostname = $check_hostname
- }
- }
- }
+ $ensure = present,
+ $check_host = 'absent',
+ $check_port = '3306',
+ $check_username = 'nagios',
+ $check_password = $nagios_mysql_password,
+ $check_database = 'information_schema',
+ $check_warning = undef,
+ $check_critical = undef,
+ $check_health_mode = $name,
+ $check_name = undef,
+ $check_name2 = undef,
+ $check_regexp = undef,
+ $check_units = undef,
+ $check_mode = 'tcp' )
+{
- if ($check_database == 'absent') {
- nagios::service { 'mysql':
- ensure => $ensure,
- check_command => "check_mysql!${real_check_hostname}!${check_port}!${check_username}!${check_password}",
- }
+ if ($check_host == 'absent') {
+ fail("Please specify a hostname, ip address or socket to check a mysql instance.")
+ }
+
+ case $check_mode {
+ 'tcp': {
+ if ($check_host == 'localhost') {
+ $real_check_host = '127.0.0.1'
+ }
+ else {
+ $real_check_host = $check_host
+ }
}
- else {
- nagios::service { "mysql_${check_database}":
- ensure => $ensure,
- check_command => "check_mysql_db!${real_check_hostname}!${check_port}!${check_username}!${check_password}!${check_database}",
- }
+ default: {
+ if ($check_host == '127.0.0.1') {
+ $real_check_host = 'localhost'
+ }
+ else {
+ $real_check_host = $check_host
+ }
}
-
+ }
+
+ nagios::service { "mysql_health_${name}":
+ ensure => $ensure,
+ check_command => "check_mysql_health!${check_host}!${check_port}!${check_username}!${check_password}!${name}!${check_database}",
+ }
}
diff --git a/manifests/service/smtp.pp b/manifests/service/smtp.pp
new file mode 100644
index 0000000..14237a9
--- /dev/null
+++ b/manifests/service/smtp.pp
@@ -0,0 +1,50 @@
+# true:
+# - true : check tls and plain connect *defualt*
+# - false : check plain connection only
+# cert_days:
+# If tls is used add an additionl check
+# to check for validity for cert.
+# - 'absent' : do not execute that check
+# - INTEGER : Minimum number of days a certificate
+# has to be valid. Default: 10
+define nagios::service::smtp(
+ $ensure = 'present',
+ $host = 'absent',
+ $port = '25',
+ $tls = true,
+ $cert_days = 10
+){
+ $real_host = $host ? {
+ 'absent' => $name,
+ default => $host
+ }
+
+ nagios::service{
+ "smtp_${name}_${port}":
+ ensure => $ensure;
+ "smtp_tls_${name}_${port}":
+ ensure => $tls ? {
+ true => $ensure,
+ default => 'absent'
+ };
+ "smtp_tls_cert_${name}_${port}":
+ ensure => $cert_days ? {
+ 'absent' => 'absent',
+ default => $ensure
+ };
+ }
+
+ if $ensure != 'absent' {
+ Nagios::Service["smtp_${name}_${port}"]{
+ check_command => "check_smtp!${real_host}!${port}",
+ }
+ Nagios::Service["smtp_tls_${name}_${port}"]{
+ check_command => "check_smtp_tls!${real_host}!${port}",
+ }
+ if $cert_days != 'absent' {
+ Nagios::Service["smtp_tls_cert_${name}_${port}"]{
+ check_command => "check_smtp_cert!${real_host}!${port}!${cert_days}",
+ }
+ }
+ }
+}
diff --git a/manifests/service/ssmtp.pp b/manifests/service/ssmtp.pp
new file mode 100644
index 0000000..b05678a
--- /dev/null
+++ b/manifests/service/ssmtp.pp
@@ -0,0 +1,32 @@
+define nagios::service::ssmtp(
+ $ensure = 'present',
+ $host = 'absent',
+ $port = '465',
+ $cert_days = 10
+){
+ $real_host = $host ? {
+ 'absent' => $name,
+ default => $host
+ }
+
+ nagios::service{
+ "ssmtp_${name}_${port}":
+ ensure => $ensure;
+ "ssmtp_cert_${name}_${port}":
+ ensure => $cert_days ? {
+ 'absent' => 'absent',
+ default => $ensure
+ };
+ }
+
+ if $ensure != 'absent' {
+ Nagios::Service["ssmtp_${name}_${port}"]{
+ check_command => "check_ssmtp!${real_host}!${port}",
+ }
+ if $cert_days != 'absent' {
+ Nagios::Service["ssmtp_cert_${name}_${port}"]{
+ check_command => "check_ssmtp_cert!${real_host}!${port}!${cert_days}",
+ }
+ }
+ }
+}
diff --git a/manifests/target/nat.pp b/manifests/target/fqdn.pp
index abb0a1e..acac5d6 100644
--- a/manifests/target/nat.pp
+++ b/manifests/target/fqdn.pp
@@ -1,4 +1,4 @@
-class nagios::target::nat inherits nagios::target {
+class nagios::target::fqdn inherits nagios::target {
Nagios_host["${fqdn}"] { address => "${fqdn}" }
diff --git a/templates/irc_bot/nagios-nsa.sh.erb b/templates/irc_bot/nagios-nsa.sh.erb
new file mode 100644
index 0000000..6e608db
--- /dev/null
+++ b/templates/irc_bot/nagios-nsa.sh.erb
@@ -0,0 +1,71 @@
+#! /bin/sh
+
+### BEGIN INIT INFO
+# Provides: nagios-nsa
+# Required-Start: $remote_fs $syslog $nagios
+# Required-Stop: $remote_fs $syslog $nagios
+# Default-Start: 2 3 4 5
+# Default-Stop: 1 6 0
+# Short-Description: Nagios Simple IRC Agent
+### END INIT INFO
+
+PIDFILE=<%= nagios_nsa_pidfile %>
+SOCKFILE=<%= nagios_nsa_socket %>
+
+. /lib/lsb/init-functions
+
+start() {
+ log_daemon_msg "Starting nagios IRC bot" "nagios-nsa"
+ if start-stop-daemon --start --quiet --oknodo --pidfile $PIDFILE --user nagios --chuid nagios --exec /usr/local/bin/riseup-nagios-server.pl; then
+ log_end_msg 0
+ else
+ log_end_msg 1
+ fi
+}
+
+stop () {
+ log_daemon_msg "Stopping nagios IRC bot" "nagios-nsa"
+ if start-stop-daemon --stop --quiet --pidfile $PIDFILE; then
+ log_end_msg 0
+ else
+ log_end_msg 1
+ fi
+}
+
+remove_socket() {
+ [ -e $SOCKFILE ] && rm $SOCKFILE
+}
+
+cleanup() {
+ if [ -r $PIDFILE ]; then
+ ps -p `cat $PIDFILE` | grep -v 'PID' || {
+ echo "not running"
+ remove_socket
+ }
+ else
+ echo "no pid file"
+ remove_socket
+ fi
+}
+
+case $1
+in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ stop
+ cleanup
+ start
+ ;;
+ status)
+ status_of_proc -p $PIDFILE /usr/local/bin/riseup-nagios-server.pl && exit 0 || exit $?
+ ;;
+ *)
+ log_action_msg "Usage: /etc/init.d/nagios-nsa {start|stop|restart|status}"
+ exit 1
+esac
+
diff --git a/templates/irc_bot/nsa.cfg.erb b/templates/irc_bot/nsa.cfg.erb
new file mode 100644
index 0000000..7cba62c
--- /dev/null
+++ b/templates/irc_bot/nsa.cfg.erb
@@ -0,0 +1,13 @@
+%Nsa = (
+ 'socket' => '<%= nagios_nsa_socket %>',
+ 'server' => '<%= nagios_nsa_server %>',
+ 'port' => '<%= nagios_nsa_port %>',
+ 'nickname' => '<%= nagios_nsa_nickname %>',
+ 'password' => '<%= nagios_nsa_password %>',
+ # this needs libio-socket-ssl-perl
+ # doesn't actually works because Net::IRC is braindead and tries to use IO::Socket::SSL->read/write instead of the builtin print, see http://search.cpan.org/dist/IO-Socket-SSL/SSL.pm
+ #'SSL' => 0,
+ 'channel' => '<%= nagios_nsa_channel %>',
+ 'pidfile' => '<%= nagios_nsa_pidfile %>', # set to undef to disable
+ 'realname' => "<%= nagios_nsa_realname %>",
+);