diff -r 000000000000 -r a04ea5e7c15b check_ldap_repl.pl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/check_ldap_repl.pl Thu Nov 29 12:12:29 2012 +0100 @@ -0,0 +1,402 @@ +#! /usr/bin/perl -w + +# Copyright (C) 2012 Christian Arnold +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Christian Arnold + +# packages: libnet-ldap-perl +# packages: libio-prompt-perl +# packages: libio-socket-ssl-perl +# packages: libconfig-inifiles-perl +# packages: perl-doc (optional - for man page) + +use strict; +use File::Basename; +use Getopt::Long; +use Config::IniFiles; +use Net::LDAP; +use IO::Prompt; +use File::stat; +use Pod::Usage; +use if $ENV{DEBUG} => "Smart::Comments"; + +sub version($$); +sub read_config(); +sub ldap_object($); +sub get_stamp($$); +sub compare_results(%); + +my %ERRORS = ( + OK => 0, + WARNING => 1, + CRITICAL => 2, + UNKNOWN => 3, + DEPENDENT => 4 +); + +my $ME = basename $0; +my $NAME = "LDAPREPL"; +my $VERSION = "0.1"; + +my %opt = ( + init => 0, + delete => 0, + refresh => 0, + cn => "replcheck", + wait => 1, + file => "/etc/nagios/ius/plugins/config/check_ldap_repl.cfg", + master => "ldap://ldap-master:389/", + slave => "ldap://ldap-slave:389/" +); + +MAIN: { + Getopt::Long::Configure('bundling'); + GetOptions( + "i|init" => \$opt{init}, + "d|delete" => \$opt{delete}, + "r|refresh" => \$opt{refresh}, + "b|binddn=s" => \$opt{binddn}, + "p|password=s" => \$opt{password}, + "c|cn=s" => \$opt{cn}, + "w|wait=i" => \$opt{wait}, + "M|master=s" => \$opt{master}, + "S|slave=s" => \$opt{slave}, + "f|file=s" => \$opt{file}, + "h|help" => sub { pod2usage( -verbose => 1, -exitval => $ERRORS{OK} ) }, + "m|man" => sub { pod2usage( -verbose => 2, -exitval => $ERRORS{OK} ) }, + "V|version" => sub { version( $ME, $VERSION ); exit $ERRORS{OK}; } + ) or pod2usage( -verbose => 1, -exitval => $ERRORS{CRITICAL} ); + + if ($opt{init}) { + ldap_object("init"); + print "new object successfully initialized\n"; + exit $ERRORS{OK}; + } elsif ($opt{delete}) { + ldap_object("delete"); + print "object successfully deleted\n"; + exit $ERRORS{OK}; + } + + ldap_object("refresh") if ($opt{refresh}); + + my ($master, $slave, $cn) = undef; + my @slaves = (); + my %results = (); + + if (-r $opt{file}) { + (undef, undef, $master, $slave, $cn) = read_config(); + @slaves = split(/,/, $slave); + } else { + $master = $opt{master}; + @slaves = split(/,/, $opt{slave}); + $cn = $opt{cn}; + } + + $results{$master}{'master'} = get_stamp($master, $cn); + sleep $opt{wait}; + foreach (@slaves) { + $results{$_}{'slave'} = get_stamp($_, $cn); + } + + compare_results(\%results); +} + +sub compare_results(%) { + my @output = (); + my %stamps = (); + my (%results) = %{$_[0]}; + for my $server ( keys %results ) { + for my $type ( keys %{ $results{$server} } ) { + $stamps{$results{$server}{$type}} = ""; + push @output, "$type: $server = $results{$server}{$type}"; + } + } + + @output = sort(@output); + if (scalar(keys(%stamps)) != 1 ) { + print "$NAME CRITICAL: server are not in sync @output\n"; + exit $ERRORS{CRITICAL}; + } else { + print "$NAME OK: servers are in sync @output\n"; + exit $ERRORS{OK}; + } +} + +sub read_config() { + my ($binddn, $password, $cn, $master, $slave); + my $cfg = new Config::IniFiles( -file => "$opt{file}"); + $binddn = $cfg->val('bind', 'dn')?$cfg->val('bind', 'dn'):$opt{binddn}; + $password = $cfg->val('bind', 'password')?$cfg->val('bind', 'password'):$opt{password}; + $master = $cfg->val('master', 'server')?$cfg->val('master', 'server'):$opt{master}; + $slave = $cfg->val('slave', 'server')?$cfg->val('slave', 'server'):$opt{slave}; + $cn = $cfg->val('object', 'cn')? $cfg->val('object', 'cn'):$opt{cn}; + + return ($binddn, $password, $master, $slave, $cn); +} + +sub ldap_object($) { + my $type = shift; + my ($binddn, $password) = undef; + + my $master = $opt{master}; + my $cn = $opt{cn}; + + if ( ($type eq "init") || ($type eq "delete") ) { + $binddn = prompt('BindDN: '); + $password = prompt('Password: ', -e => '*'); + } else { + if ($opt{binddn} && $opt{password}) { + $binddn = $opt{binddn}; + $password = $opt{password}; + } elsif (-r $opt{file}) { + ($binddn, $password, $master, undef, $cn) = read_config(); + } else { + $binddn = prompt('BindDN: '); + $password = prompt('Password: ', -e => '*'); + } + } + + my $ldap = Net::LDAP->new( $master ) or die "$@"; + + my $mesg = $ldap->bind("$binddn", password => $password); + if ($mesg->code) { + $ldap->unbind() if ($ldap); + print "$NAME CRITICAL: " . $mesg->error . "\n"; + exit $ERRORS{CRITICAL}; + } + + # get ldap naming context + my $dse = $ldap->root_dse(); + my $context = $dse->get_value('namingContexts'); + + if (! defined $context) { + print "$NAME CRITICAL: can't determine ldap 'naming context'\n"; + exit $ERRORS{CRITICAL}; + } + + if ($mesg->code) { + print "$NAME CRITICAL: " . $mesg->error . "\n"; + exit $ERRORS{CRITICAL}; + } + + # initialize check object + $mesg = $ldap->add( + "cn=$cn,$context", + attr => [ + 'objectclass' => [ 'top', 'person' ], + 'cn' => "$cn", + 'sn' => "$cn", + 'description' => time() + ], + ) if ($type eq "init"); + + # delete check object + $mesg = $ldap->delete("cn=$cn,$context") if ($type eq "delete"); + + if ($mesg->code && ($type eq "delete" || $type eq "init")) { + print "$NAME CRITICAL: " . $mesg->error . "\n"; + exit $ERRORS{CRITICAL}; + } + + # refresh check object + $mesg = $ldap->modify( + "cn=$cn,$context", + replace => { + description => time() + } + ) if ($opt{refresh}); + + $ldap->unbind() if ($ldap); + return 0; +} + +sub get_stamp($$) { + my ($server, $cn) = @_; + my $ldap = Net::LDAP->new( $server ) or die "$@"; + my $mesg = $ldap->bind(); + + if ($mesg->code) { + $ldap->unbind() if ($ldap); + print "$NAME CRITICAL: " . $mesg->error . "\n"; + exit $ERRORS{CRITICAL}; + } + + # get ldap naming context + my $dse = $ldap->root_dse(); + my $context = $dse->get_value('namingContexts'); + + if (! defined $context) { + print "$NAME CRITICAL: can't determine ldap 'naming context'\n"; + exit $ERRORS{CRITICAL}; + } + + $mesg = $ldap->search( + base => "cn=replcheck,$context", + scope => "base", + filter => "(cn=$cn)", + attr => [ 'description' ] + + ); + + if ($mesg->code) { + $ldap->unbind() if ($ldap); + print "$NAME CRITICAL: " . $mesg->error . "\n"; + exit $ERRORS{CRITICAL}; + } + + if ($mesg->count != 1) { + print "$NAME CRITICAL: \n"; + exit $ERRORS{CRITICAL}; + } + + my $entry = $mesg->entry(0); + return $entry->get_value("description"); + + $ldap->unbind() if ($ldap); +} + +sub version($$) { + my ( $progname, $version ) = @_; + + print <<_VERSION; +$progname version $version +Copyright (C) 2012 by Christian Arnold and Schlittermann internet & unix support. + +$ME comes with ABSOLUTELY NO WARRANTY. This is free software, +and you are welcome to redistribute it under certain conditions. +See the GNU General Public Licence for details. +_VERSION +} + +__END__ + +=head1 NAME + +check_ldap_repl - nagios/icinga plugin to check correctly working of ldap replication. + +=head1 SYNOPSIS + +check_ldap_repl [-i|--init] + [-d|--delete] + [-r|--refresh] + [-c|--cn string] + [-b|--binddn string] + [-p|--password string] + [-f|--file string] + [-M|--master string] + [-S|--slave string] + [-w|--wait integer] + [-h|--help] + [-m|--man] + [-V|--version] + +=head1 OPTIONS + +=over + +=item B<-i>|B<--init> + +Add the check object cn=replcheck,I to the master server if not existis. See also the B<--cn> option. +You will ask for an B and B, if not given B<--binddn> and B<--password> options. +Your B must have write permission to the ldap master server. + +=item B<-d>|B<--delete> + +Delete the check object from the ldap master server if existis. See also the B<--cn> option. +You will ask for an B and B, if not given B<--binddn> and B<--password> options. +Your B must have write permission to the ldap master server. + +=item B<-r>|B<--refresh> + +Refresh the stamp attribute of the check attribute with current unix time. +You will ask for an B and B, if not given B<--binddn> and B<--password> options. See also B<--file> option. +Your B must have write permission to the ldap master server. + +=item B<-c>|B<--cn> I + +cn for the initialized object. See also the B<--init> option. (default: replcheck) + +=item B<-b>|B<--binddn> I + +DN to bind to ldap master server. + +=item B<-p>|B<--password> I + +Password for binddn to ldap master server. B + +=item B<-M>|B<--master> I + +LDAP master server (provider) (default: ldap://ldap-master:389/) + +=item B<-S>|B<--slave> I + +LDAP slave server (consumer), multiple slaves can be specified as a comma-separate list (default: ldap://ldap-slave:389/) + +=item B<-f>|B<--file> I + +Config file with B and B. Verify the file B/B and B, B<(0400)> is a good choice! +You can also set B and B options. (default: /etc/nagios/ius/plugins/config/check_ldap_repl.cfg) + + [bind] + dn = cn=admin,dc=local,dc=site + password = secret + + [master] + server = ldap://ldap-master:389/ + + [slave] + server = ldap://ldap-slave01:389/,ldap://ldap-slave02:389/,... + + [object] + cn = replcheck + +=item B<-w>|B<--wait> I + +Wait I seconds before check the slave servers. (default: 1) + +=item B<-h>|B<--help> + +Print detailed help screen. + +=item B<-m>|B<--man> + +Print manual page. + +=item B<-V>|B<--version> + +Print version information. + +=back + +=head1 DESCRIPTION + +This plugin checks if the ldap replication works correctly. + +=head1 VERSION + +This man page is current for version 0.1 of B. + +=head1 AUTHOR + +Written by Christian Arnold L + +=head1 COPYRIGHT + +Copyright (C) 2012 by Christian Arnold and Schlittermann internet & unix support. +This is free software, and you are welcome to redistribute it under certain conditions. +See the GNU General Public Licence for details. + +=cut