|
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 |