42 DEPENDENT => 4 |
39 DEPENDENT => 4 |
43 ); |
40 ); |
44 |
41 |
45 my $ME = basename $0; |
42 my $ME = basename $0; |
46 my $NAME = "LDAPREPL"; |
43 my $NAME = "LDAPREPL"; |
47 my $VERSION = "0.3.2"; |
44 my $VERSION = "0.3.3"; |
48 |
45 |
49 my $master_default = "ldap://ldap-master:389/"; |
46 my $defaults = { |
50 my $slave_default = "ldap://ldap-slave:389/"; |
47 'init|i!' => 0, |
51 my $cn_default = "replcheck"; |
48 'delete|d!' => 0, |
52 |
49 'refresh|r!' => 1, |
53 my %opt = ( |
50 'dn=s' => undef, |
54 init => 0, |
51 'binddn|D=s' => undef, |
55 delete => 0, |
52 'password=s' => undef, |
56 refresh => 0, |
53 'wait|w=i' => 1, |
57 cn => $cn_default, |
54 'config=s' => '/etc/nagios/ius/plugins/config/check_ldap_repl.cfg', |
58 wait => 1, |
55 'provider|p=s' => 'ldap://provider:389', |
59 file => "/etc/nagios/ius/plugins/config/check_ldap_repl.cfg", |
56 'consumer|c=s@' => 'ldap://consumer:389', |
60 master => $master_default, |
57 'help|h!' => sub { pod2usage(-verbose => 1, -exitval => $ERRORS{OK}) }, |
61 slave => $slave_default |
58 'man|m!' => sub { pod2usage(-verbose => 2, -exitval => $ERRORS{OK}) }, |
62 ); |
59 'version|V!' => sub { version($ME, $VERSION); exit $ERRORS{OK}; } |
63 |
60 }; |
64 MAIN: { |
61 |
65 Getopt::Long::Configure('bundling'); |
62 my $attr = 'description'; |
66 GetOptions( |
63 |
67 "i|init" => \$opt{init}, |
64 sub critical { print STDERR "$NAME CRITICAL: ", @_; exit $ERRORS{CRITICAL}; } |
68 "d|delete" => \$opt{delete}, |
65 $SIG{__DIE__} = sub { print STDERR "$NAME UNKNOWN: ", @_; exit $ERRORS{UNKNOWN}; }; |
69 "r|refresh" => \$opt{refresh}, |
66 |
70 "b|binddn=s" => \$opt{binddn}, |
67 sub stamp { |
71 "p|password=s" => \$opt{password}, |
68 my ($u, $dn) = @_; |
72 "c|cn=s" => \$opt{cn}, |
69 |
73 "w|wait=i" => \$opt{wait}, |
70 my $l = ref $u eq 'Net::LDAP' ? $u : Net::LDAP->new($u, onerror => 'die') or die "$@"; |
74 "M|master=s" => \$opt{master}, |
71 my $r = $l->search(base => $dn, scope => 'base', filter => '(objectClass=*)'); |
75 "S|slave=s" => \$opt{slave}, |
72 die "unexpected result count: ", $r->count unless $r->count == 1; |
76 "f|file=s" => \$opt{file}, |
73 my @v = $r->entry(0)->get_value($attr); |
77 "h|help" => sub { pod2usage( -verbose => 1, -exitval => $ERRORS{OK} ) }, |
74 die "unexpected value count [@v]" unless @v == 1; |
78 "m|man" => sub { pod2usage( -verbose => 2, -exitval => $ERRORS{OK} ) }, |
75 return $v[0]; |
79 "V|version" => sub { version( $ME, $VERSION ); exit $ERRORS{OK}; } |
76 |
80 ) or pod2usage( -verbose => 1, -exitval => $ERRORS{CRITICAL} ); |
|
81 |
|
82 # init or delete the ldap object |
|
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 # refresh our ldap object |
|
94 ldap_object("refresh") if ($opt{refresh}); |
|
95 |
|
96 my ($master, $slave, $cn) = undef; |
|
97 my @slaves = (); |
|
98 my %results = (); |
|
99 |
|
100 # preparing for the comparison of ldap entries |
|
101 if (($opt{master} ne $master_default) || ($opt{slave} ne $slave_default) || ($opt{cn} ne $cn_default)) { |
|
102 $master = $opt{master}; |
|
103 @slaves = split(/,/, $opt{slave}); |
|
104 $cn = $opt{cn}; |
|
105 } elsif (-r $opt{file}) { |
|
106 (undef, undef, $master, $slave, $cn) = read_config(); |
|
107 @slaves = split(/,/, $slave); |
|
108 } else { |
|
109 $master = $opt{master}; |
|
110 @slaves = split(/,/, $opt{slave}); |
|
111 $cn = $opt{cn}; |
|
112 } |
|
113 |
|
114 # get the values from the ldap |
|
115 $results{$master}{'master'} = get_stamp($master, $cn); |
|
116 sleep $opt{wait}; |
|
117 foreach (@slaves) { |
|
118 $results{$_}{'slave'} = get_stamp($_, $cn); |
|
119 } |
|
120 |
|
121 # compare the time stamps and generate the output |
|
122 compare_results(\%results); |
|
123 } |
77 } |
124 |
78 |
125 sub compare_results(%) { |
79 sub version { |
126 my @output = (); |
|
127 my %stamps = (); |
|
128 my (%results) = %{$_[0]}; |
|
129 for my $server ( keys %results ) { |
|
130 for my $type ( keys %{ $results{$server} } ) { |
|
131 $stamps{$results{$server}{$type}} = ""; |
|
132 push @output, "$type: $server = $results{$server}{$type}"; |
|
133 } |
|
134 } |
|
135 |
|
136 @output = sort(@output); |
|
137 if (scalar(keys(%stamps)) != 1 ) { |
|
138 print "$NAME CRITICAL: server are not in sync @output\n"; |
|
139 exit $ERRORS{CRITICAL}; |
|
140 } else { |
|
141 print "$NAME OK: servers are in sync @output\n"; |
|
142 exit $ERRORS{OK}; |
|
143 } |
|
144 } |
|
145 |
|
146 sub read_config() { |
|
147 my ($binddn, $password, $cn, $master, $slave); |
|
148 my $cfg = new Config::IniFiles( -file => "$opt{file}"); |
|
149 $binddn = $cfg->val('bind', 'dn')?$cfg->val('bind', 'dn'):$opt{binddn}; |
|
150 $password = $cfg->val('bind', 'password')?$cfg->val('bind', 'password'):$opt{password}; |
|
151 $master = $cfg->val('master', 'server')?$cfg->val('master', 'server'):$opt{master}; |
|
152 $slave = $cfg->val('slave', 'server')?$cfg->val('slave', 'server'):$opt{slave}; |
|
153 $cn = $cfg->val('object', 'cn')? $cfg->val('object', 'cn'):$opt{cn}; |
|
154 |
|
155 return ($binddn, $password, $master, $slave, $cn); |
|
156 } |
|
157 |
|
158 sub ldap_object($) { |
|
159 my $type = shift; |
|
160 my ($binddn, $password) = undef; |
|
161 |
|
162 my $master = $opt{master}; |
|
163 my $cn = $opt{cn}; |
|
164 |
|
165 # ldap object init/delete is only allowed at the prompt |
|
166 if ( ($type eq "init") || ($type eq "delete") ) { |
|
167 $binddn = prompt('BindDN: '); |
|
168 $password = prompt('Password: ', -e => '*'); |
|
169 if (($opt{master} ne $master_default) || ($opt{cn} ne $cn_default)) { |
|
170 $master = $opt{master}; |
|
171 $cn = $opt{cn}; |
|
172 } elsif ( -r $opt{file} ) { |
|
173 (undef, undef, $master, undef, $cn) = read_config(); |
|
174 } |
|
175 } else { |
|
176 if ($opt{binddn} && $opt{password}) { |
|
177 $binddn = $opt{binddn}; |
|
178 $password = $opt{password}; |
|
179 } elsif ( -r $opt{file} ) { |
|
180 ($binddn, $password, undef, undef, undef) = read_config(); |
|
181 } else { |
|
182 $binddn = prompt('BindDN: '); |
|
183 $password = prompt('Password: ', -e => '*'); |
|
184 } |
|
185 |
|
186 if (($opt{master} ne $master_default) || ($opt{cn} ne $cn_default)) { |
|
187 $master = $opt{master}; |
|
188 $cn = $opt{cn}; |
|
189 } elsif ( -r $opt{file} ) { |
|
190 (undef, undef, $master, undef, $cn) = read_config(); |
|
191 } |
|
192 } |
|
193 |
|
194 my $ldap = Net::LDAP->new( $master ); |
|
195 |
|
196 if (!$ldap) { |
|
197 print "$NAME CRITICAL: [$master] $!\n"; |
|
198 exit $ERRORS{CRITICAL}; |
|
199 } |
|
200 |
|
201 my $mesg = $ldap->bind("$binddn", password => $password); |
|
202 if ($mesg->code) { |
|
203 $ldap->unbind() if ($ldap); |
|
204 print "$NAME CRITICAL: " . $mesg->error . "\n"; |
|
205 exit $ERRORS{CRITICAL}; |
|
206 } |
|
207 |
|
208 # get ldap naming context |
|
209 my $dse = $ldap->root_dse(); |
|
210 my $context = $dse->get_value('namingContexts'); |
|
211 |
|
212 if (! defined $context) { |
|
213 print "$NAME CRITICAL: can't determine ldap 'naming context'\n"; |
|
214 exit $ERRORS{CRITICAL}; |
|
215 } |
|
216 |
|
217 if ($mesg->code) { |
|
218 print "$NAME CRITICAL: " . $mesg->error . "\n"; |
|
219 exit $ERRORS{CRITICAL}; |
|
220 } |
|
221 |
|
222 # initialize check object |
|
223 $mesg = $ldap->add( |
|
224 "cn=$cn,$context", |
|
225 attr => [ |
|
226 'objectclass' => [ 'top', 'person' ], |
|
227 'cn' => "$cn", |
|
228 'sn' => "$cn", |
|
229 'description' => time() |
|
230 ], |
|
231 ) if ($type eq "init"); |
|
232 |
|
233 # delete check object |
|
234 $mesg = $ldap->delete("cn=$cn,$context") if ($type eq "delete"); |
|
235 |
|
236 # refresh check object |
|
237 $mesg = $ldap->modify( |
|
238 "cn=$cn,$context", |
|
239 replace => { |
|
240 description => time() |
|
241 } |
|
242 ) if ($opt{refresh}); |
|
243 |
|
244 if ($mesg->code && ($type eq "delete" || $type eq "init" || $type eq "refresh")) { |
|
245 print "$NAME WARNING: [ldapt] " . $mesg->error . "\n"; |
|
246 exit $ERRORS{WARNING}; |
|
247 } |
|
248 |
|
249 $ldap->unbind() if ($ldap); |
|
250 return 0; |
|
251 } |
|
252 |
|
253 sub get_stamp($$) { |
|
254 my ($server, $cn) = @_; |
|
255 |
|
256 my $ldap = Net::LDAP->new( $server ); |
|
257 if (!$ldap) { |
|
258 print "$NAME CRITICAL: [$server] $!\n"; |
|
259 exit $ERRORS{CRITICAL}; |
|
260 } |
|
261 |
|
262 my $mesg = $ldap->bind(); |
|
263 |
|
264 if ($mesg->code) { |
|
265 $ldap->unbind() if ($ldap); |
|
266 print "$NAME CRITICAL: " . $mesg->error . "\n"; |
|
267 exit $ERRORS{CRITICAL}; |
|
268 } |
|
269 |
|
270 # get ldap naming context |
|
271 my $dse = $ldap->root_dse(); |
|
272 my $context = $dse->get_value('namingContexts'); |
|
273 |
|
274 if (! defined $context) { |
|
275 print "$NAME CRITICAL: can't determine ldap 'naming context'\n"; |
|
276 exit $ERRORS{CRITICAL}; |
|
277 } |
|
278 |
|
279 $mesg = $ldap->search( |
|
280 base => "cn=replcheck,$context", |
|
281 scope => "base", |
|
282 filter => "(cn=$cn)", |
|
283 attr => [ 'description' ] |
|
284 |
|
285 ); |
|
286 |
|
287 if ($mesg->code) { |
|
288 $ldap->unbind() if ($ldap); |
|
289 print "$NAME CRITICAL: " . $mesg->error . "\n"; |
|
290 exit $ERRORS{CRITICAL}; |
|
291 } |
|
292 |
|
293 if ($mesg->count != 1) { |
|
294 print "$NAME CRITICAL: \n"; |
|
295 exit $ERRORS{CRITICAL}; |
|
296 } |
|
297 |
|
298 my $entry = $mesg->entry(0); |
|
299 return $entry->get_value("description"); |
|
300 |
|
301 $ldap->unbind() if ($ldap); |
|
302 } |
|
303 |
|
304 sub version($$) { |
|
305 my ( $progname, $version ) = @_; |
80 my ( $progname, $version ) = @_; |
306 |
81 |
307 print <<_VERSION; |
82 print <<_VERSION; |
308 $progname version $version |
83 $progname version $version |
309 Copyright (C) 2012 by Christian Arnold and Schlittermann internet & unix support. |
84 Copyright (C) 2012 by Christian Arnold and Schlittermann internet & unix support. |