check_ldap_repl.pl
changeset 0 a04ea5e7c15b
child 1 36f66a013838
equal deleted inserted replaced
-1:000000000000 0:a04ea5e7c15b
       
     1 #! /usr/bin/perl -w
       
     2 
       
     3 #    Copyright (C) 2012  Christian Arnold
       
     4 #
       
     5 #    This program is free software: you can redistribute it and/or modify
       
     6 #    it under the terms of the GNU General Public License as published by
       
     7 #    the Free Software Foundation, either version 3 of the License, or
       
     8 #    (at your option) any later version.
       
     9 #
       
    10 #    This program is distributed in the hope that it will be useful,
       
    11 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    13 #    GNU General Public License for more details.
       
    14 #
       
    15 #    You should have received a copy of the GNU General Public License
       
    16 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
       
    17 #
       
    18 #    Christian Arnold <arnold@schlittermann.de>
       
    19 
       
    20 # packages: libnet-ldap-perl
       
    21 # packages: libio-prompt-perl
       
    22 # packages: libio-socket-ssl-perl
       
    23 # packages: libconfig-inifiles-perl
       
    24 # packages: perl-doc (optional - for man page)
       
    25 
       
    26 use strict;
       
    27 use File::Basename;
       
    28 use Getopt::Long;
       
    29 use Config::IniFiles;
       
    30 use Net::LDAP;
       
    31 use IO::Prompt;
       
    32 use File::stat;
       
    33 use Pod::Usage;
       
    34 use if $ENV{DEBUG} => "Smart::Comments";
       
    35 
       
    36 sub version($$);
       
    37 sub read_config();
       
    38 sub ldap_object($);
       
    39 sub get_stamp($$);
       
    40 sub compare_results(%);
       
    41 
       
    42 my %ERRORS = (
       
    43 	OK        => 0,
       
    44 	WARNING   => 1,
       
    45 	CRITICAL  => 2,
       
    46 	UNKNOWN   => 3,
       
    47 	DEPENDENT => 4
       
    48 );
       
    49 
       
    50 my $ME      = basename $0;
       
    51 my $NAME    = "LDAPREPL";
       
    52 my $VERSION = "0.1";
       
    53 
       
    54 my %opt = (
       
    55 	init	=> 0,
       
    56 	delete  => 0,
       
    57 	refresh => 0,
       
    58 	cn      => "replcheck",
       
    59 	wait    => 1,
       
    60 	file    => "/etc/nagios/ius/plugins/config/check_ldap_repl.cfg",
       
    61 	master	=> "ldap://ldap-master:389/",
       
    62 	slave	=> "ldap://ldap-slave:389/"
       
    63 );
       
    64 
       
    65 MAIN: {
       
    66 	Getopt::Long::Configure('bundling');
       
    67 	GetOptions(
       
    68 		"i|init"       => \$opt{init},
       
    69 		"d|delete"     => \$opt{delete},
       
    70 		"r|refresh"    => \$opt{refresh},
       
    71 		"b|binddn=s"   => \$opt{binddn},
       
    72 		"p|password=s" => \$opt{password},
       
    73 		"c|cn=s"       => \$opt{cn},
       
    74 		"w|wait=i"     => \$opt{wait},
       
    75 		"M|master=s"   => \$opt{master},
       
    76 		"S|slave=s"    => \$opt{slave},
       
    77 		"f|file=s"     => \$opt{file},
       
    78 		"h|help"       => sub { pod2usage( -verbose => 1, -exitval => $ERRORS{OK} ) },
       
    79 		"m|man"        => sub { pod2usage( -verbose => 2, -exitval => $ERRORS{OK} ) },
       
    80 		"V|version"    => sub { version( $ME, $VERSION ); exit $ERRORS{OK}; }
       
    81 	) or pod2usage( -verbose => 1, -exitval => $ERRORS{CRITICAL} );
       
    82 
       
    83 	if ($opt{init}) {
       
    84 		ldap_object("init");
       
    85 		print "new object successfully initialized\n";
       
    86 		exit $ERRORS{OK};
       
    87 	} elsif ($opt{delete}) {
       
    88 		ldap_object("delete");
       
    89 		print "object successfully deleted\n";
       
    90 		exit $ERRORS{OK};
       
    91 	}
       
    92 
       
    93 	ldap_object("refresh") if ($opt{refresh});
       
    94 
       
    95 	my ($master, $slave, $cn) = undef;
       
    96 	my @slaves = ();
       
    97 	my %results = ();
       
    98 
       
    99 	if (-r $opt{file}) {
       
   100 		(undef, undef, $master, $slave, $cn) = read_config();
       
   101 		@slaves = split(/,/, $slave);
       
   102 	} else {
       
   103 		$master = $opt{master};
       
   104 		@slaves = split(/,/, $opt{slave});
       
   105 		$cn = $opt{cn};
       
   106 	}
       
   107 
       
   108 	$results{$master}{'master'} = get_stamp($master, $cn);
       
   109 	sleep $opt{wait};
       
   110 	foreach (@slaves) {
       
   111 		$results{$_}{'slave'} = get_stamp($_, $cn);
       
   112 	}
       
   113 
       
   114 	compare_results(\%results);
       
   115 }
       
   116 
       
   117 sub compare_results(%) {
       
   118 	my @output = ();
       
   119 	my %stamps = ();
       
   120 	my (%results) = %{$_[0]};
       
   121 	for my $server ( keys %results ) {
       
   122 		for my $type ( keys %{ $results{$server} } ) {
       
   123 			$stamps{$results{$server}{$type}} = "";
       
   124 			push @output, "$type: $server = $results{$server}{$type}";
       
   125 		}
       
   126 	}
       
   127 
       
   128 	@output = sort(@output);
       
   129 	if (scalar(keys(%stamps)) != 1 ) {
       
   130 		print "$NAME CRITICAL: server are not in sync @output\n";
       
   131 		exit $ERRORS{CRITICAL};
       
   132 	} else {
       
   133 		print "$NAME OK: servers are in sync @output\n";
       
   134 		exit $ERRORS{OK};
       
   135 	}
       
   136 }
       
   137 
       
   138 sub read_config() {
       
   139 	my ($binddn, $password, $cn, $master, $slave);
       
   140 	my $cfg = new Config::IniFiles( -file => "$opt{file}");
       
   141 	$binddn = $cfg->val('bind', 'dn')?$cfg->val('bind', 'dn'):$opt{binddn};
       
   142 	$password = $cfg->val('bind', 'password')?$cfg->val('bind', 'password'):$opt{password};
       
   143 	$master = $cfg->val('master', 'server')?$cfg->val('master', 'server'):$opt{master};
       
   144 	$slave = $cfg->val('slave', 'server')?$cfg->val('slave', 'server'):$opt{slave};
       
   145 	$cn = $cfg->val('object', 'cn')? $cfg->val('object', 'cn'):$opt{cn};
       
   146 
       
   147 	return ($binddn, $password, $master, $slave, $cn);
       
   148 }
       
   149 
       
   150 sub ldap_object($) {
       
   151 	my $type = shift;
       
   152 	my ($binddn, $password) = undef;
       
   153 
       
   154 	my $master = $opt{master};
       
   155 	my $cn = $opt{cn};
       
   156 
       
   157 	if ( ($type eq "init") || ($type eq "delete") ) {
       
   158 		$binddn = prompt('BindDN: ');
       
   159 		$password = prompt('Password: ', -e => '*');
       
   160 	} else {
       
   161 		if ($opt{binddn} && $opt{password}) {
       
   162 			$binddn = $opt{binddn};
       
   163 			$password = $opt{password};
       
   164 		} elsif (-r $opt{file}) {
       
   165 			($binddn, $password, $master, undef, $cn) = read_config();
       
   166 		} else {
       
   167 			$binddn = prompt('BindDN: ');
       
   168 			$password = prompt('Password: ', -e => '*');
       
   169 		}
       
   170 	}
       
   171 	
       
   172 	my $ldap = Net::LDAP->new( $master ) or die "$@";
       
   173 
       
   174 	my $mesg = $ldap->bind("$binddn", password => $password);
       
   175 	if ($mesg->code) {
       
   176 		$ldap->unbind() if ($ldap);
       
   177 		print "$NAME CRITICAL: " . $mesg->error . "\n";
       
   178 		exit $ERRORS{CRITICAL};
       
   179 	}
       
   180 
       
   181 	# get ldap naming context
       
   182 	my $dse = $ldap->root_dse();
       
   183 	my $context = $dse->get_value('namingContexts');
       
   184 
       
   185 	if (! defined $context) {
       
   186 		print "$NAME CRITICAL: can't determine ldap 'naming context'\n";
       
   187 		exit $ERRORS{CRITICAL};
       
   188 	}
       
   189 
       
   190 	if ($mesg->code) {
       
   191 		print "$NAME CRITICAL: " . $mesg->error . "\n";
       
   192 		exit $ERRORS{CRITICAL};
       
   193 	}
       
   194 
       
   195 	# initialize check object
       
   196 	$mesg = $ldap->add(
       
   197 		"cn=$cn,$context",
       
   198 		attr => [
       
   199 			'objectclass' => [ 'top', 'person' ],
       
   200 			'cn' => "$cn",
       
   201 			'sn' => "$cn",
       
   202 			'description' => time()
       
   203 			],
       
   204 	) if ($type eq "init");
       
   205 
       
   206 	# delete check object
       
   207 	$mesg = $ldap->delete("cn=$cn,$context") if ($type eq "delete");
       
   208 
       
   209 	if ($mesg->code && ($type eq "delete" || $type eq "init")) {
       
   210 		print "$NAME CRITICAL: " . $mesg->error . "\n";
       
   211 		exit $ERRORS{CRITICAL};
       
   212 	}
       
   213 
       
   214 	# refresh check object
       
   215 	$mesg = $ldap->modify(
       
   216 		"cn=$cn,$context",
       
   217 		replace => {
       
   218 			description => time()
       
   219 		}
       
   220 	) if ($opt{refresh});
       
   221 
       
   222 	$ldap->unbind() if ($ldap);
       
   223 	return 0;
       
   224 }
       
   225 
       
   226 sub get_stamp($$) {
       
   227 	my ($server, $cn) = @_;
       
   228 	my $ldap = Net::LDAP->new( $server ) or die "$@";
       
   229 	my $mesg = $ldap->bind();
       
   230 
       
   231 	if ($mesg->code) {
       
   232 		$ldap->unbind() if ($ldap);
       
   233 		print "$NAME CRITICAL: " . $mesg->error . "\n";
       
   234 		exit $ERRORS{CRITICAL};
       
   235 	}
       
   236 
       
   237 	# get ldap naming context
       
   238 	my $dse = $ldap->root_dse();
       
   239 	my $context = $dse->get_value('namingContexts');
       
   240 
       
   241 	if (! defined $context) {
       
   242 		print "$NAME CRITICAL: can't determine ldap 'naming context'\n";
       
   243 		exit $ERRORS{CRITICAL};
       
   244 	}
       
   245 
       
   246 	$mesg = $ldap->search(
       
   247 		base => "cn=replcheck,$context",
       
   248 		scope => "base",
       
   249 		filter => "(cn=$cn)",
       
   250 		attr => [ 'description' ]
       
   251 
       
   252 	);
       
   253 
       
   254 	if ($mesg->code) {
       
   255 		$ldap->unbind() if ($ldap);
       
   256 		print "$NAME CRITICAL: " . $mesg->error . "\n";
       
   257 		exit $ERRORS{CRITICAL};
       
   258 	}
       
   259 
       
   260 	if ($mesg->count != 1) {
       
   261 		print "$NAME CRITICAL: \n";
       
   262 		exit $ERRORS{CRITICAL};
       
   263 	}
       
   264 
       
   265 	my $entry = $mesg->entry(0);
       
   266 	return $entry->get_value("description");
       
   267 
       
   268 	$ldap->unbind() if ($ldap);
       
   269 }
       
   270 
       
   271 sub version($$) {
       
   272 	my ( $progname, $version ) = @_;
       
   273 	
       
   274 	print <<_VERSION;
       
   275 $progname version $version
       
   276 Copyright (C) 2012 by Christian Arnold and Schlittermann internet & unix support.
       
   277 
       
   278 $ME comes with ABSOLUTELY NO WARRANTY. This is free software,
       
   279 and you are welcome to redistribute it under certain conditions.
       
   280 See the GNU General Public Licence for details.
       
   281 _VERSION
       
   282 }
       
   283 
       
   284 __END__
       
   285 
       
   286 =head1 NAME
       
   287 
       
   288 check_ldap_repl - nagios/icinga plugin to check correctly working of ldap replication.
       
   289 
       
   290 =head1 SYNOPSIS
       
   291 
       
   292 check_ldap_repl [-i|--init]
       
   293                 [-d|--delete]
       
   294                 [-r|--refresh]
       
   295                 [-c|--cn string]
       
   296                 [-b|--binddn string]
       
   297                 [-p|--password string]
       
   298                 [-f|--file string]
       
   299                 [-M|--master string]
       
   300                 [-S|--slave string]
       
   301                 [-w|--wait integer]
       
   302                 [-h|--help]
       
   303                 [-m|--man]
       
   304                 [-V|--version]
       
   305 
       
   306 =head1 OPTIONS
       
   307 
       
   308 =over
       
   309 
       
   310 =item B<-i>|B<--init>
       
   311 
       
   312 Add the check object cn=replcheck,I<namingContext> to the master server if not existis. See also the B<--cn> option.
       
   313 You will ask for an B<binddn> and B<password>, if not given B<--binddn> and B<--password> options.
       
   314 Your B<binddn> must have write permission to the ldap master server.
       
   315 
       
   316 =item B<-d>|B<--delete>
       
   317 
       
   318 Delete the check object from the ldap master server if existis. See also the B<--cn> option.
       
   319 You will ask for an B<binddn> and B<password>, if not given B<--binddn> and B<--password> options.
       
   320 Your B<binddn> must have write permission to the ldap master server.
       
   321 
       
   322 =item B<-r>|B<--refresh>
       
   323 
       
   324 Refresh the stamp attribute of the check attribute with current unix time.
       
   325 You will ask for an B<binddn> and B<password>, if not given B<--binddn> and B<--password> options. See also B<--file> option.
       
   326 Your B<binddn> must have write permission to the ldap master server.
       
   327 
       
   328 =item B<-c>|B<--cn> I<string>
       
   329 
       
   330 cn for the initialized object. See also the B<--init> option. (default: replcheck)
       
   331 
       
   332 =item B<-b>|B<--binddn> I<string>
       
   333 
       
   334 DN to bind to ldap master server.
       
   335 
       
   336 =item B<-p>|B<--password> I<string>
       
   337 
       
   338 Password for binddn to ldap master server. B<PASSWORD IS SHOWN IN PROCESSES, USE CONFIG FILE!>
       
   339 
       
   340 =item B<-M>|B<--master> I<string>
       
   341 
       
   342 LDAP master server (provider) (default: ldap://ldap-master:389/)
       
   343 
       
   344 =item B<-S>|B<--slave> I<string>
       
   345 
       
   346 LDAP slave server (consumer), multiple slaves can be specified as a comma-separate list (default: ldap://ldap-slave:389/)
       
   347 
       
   348 =item B<-f>|B<--file> I<string>
       
   349 
       
   350 Config file with B<binddn> and B<password>. Verify the file B<owner>/B<group> and B<permissions>, B<(0400)> is a good choice!
       
   351 You can also set B<master,slave> and B<cn> options. (default: /etc/nagios/ius/plugins/config/check_ldap_repl.cfg)
       
   352 
       
   353  [bind]
       
   354  dn = cn=admin,dc=local,dc=site
       
   355  password = secret
       
   356 
       
   357  [master]
       
   358  server = ldap://ldap-master:389/
       
   359 
       
   360  [slave]
       
   361  server = ldap://ldap-slave01:389/,ldap://ldap-slave02:389/,...
       
   362 
       
   363  [object]
       
   364  cn = replcheck
       
   365 
       
   366 =item B<-w>|B<--wait> I<integer>
       
   367 
       
   368 Wait I<n> seconds before check the slave servers. (default: 1)
       
   369 
       
   370 =item B<-h>|B<--help>
       
   371 
       
   372 Print detailed help screen.
       
   373 
       
   374 =item B<-m>|B<--man>
       
   375 
       
   376 Print manual page.
       
   377 
       
   378 =item B<-V>|B<--version>
       
   379 
       
   380 Print version information.
       
   381 
       
   382 =back
       
   383 
       
   384 =head1 DESCRIPTION
       
   385 
       
   386 This plugin checks if the ldap replication works correctly.
       
   387 
       
   388 =head1 VERSION
       
   389 
       
   390 This man page is current for version 0.1 of B<check_ldap_repl>.
       
   391 
       
   392 =head1 AUTHOR
       
   393 
       
   394 Written by Christian Arnold L<arnold@schlittermann.de>
       
   395 
       
   396 =head1 COPYRIGHT
       
   397 
       
   398 Copyright (C) 2012 by Christian Arnold and Schlittermann internet & unix support.
       
   399 This is free software, and you are welcome to redistribute it under certain conditions.
       
   400 See the GNU General Public Licence for details.
       
   401 
       
   402 =cut