File Coverage

blib/lib/Nagios/Plugin/LDAP.pm
Criterion Covered Total %
statement 24 125 19.2
branch 0 48 0.0
condition 0 21 0.0
subroutine 8 16 50.0
pod 2 2 100.0
total 34 212 16.0


line stmt bran cond sub pod time code
1             package Nagios::Plugin::LDAP;
2              
3 1     1   944 use strict;
  1         3  
  1         39  
4 1     1   6 use warnings;
  1         3  
  1         35  
5              
6 1     1   15 use base qw(Nagios::Plugin);
  1         2  
  1         942  
7              
8 1     1   90188 use Net::LDAP;
  1         307548  
  1         12  
9 1     1   7120 use Net::LDAP::Util;
  1         106  
  1         83  
10 1     1   6 use Nagios::Plugin;
  1         2  
  1         71  
11 1     1   11113 use Time::HiRes qw(time);
  1         7036  
  1         8  
12 1     1   3493 use DateTime;
  1         783078  
  1         4942  
13              
14             our $VERSION = '0.04';
15             our $TIMEOUT = 4;
16              
17              
18             sub new {
19 0     0 1   my $class = shift;
20              
21 0           my $usage = <<'USAGE';
22             Usage: check_ldap -H -b [-p ] [-a ] [-D ]
23             [-P ] [-w ] [-c ] [-t timeout]
24             [-2|-3] [-4|-6]
25             USAGE
26              
27 0           my $self = $class->SUPER::new(
28             shortname => 'LDAP',
29             usage => $usage,
30             version => $VERSION,
31             url => 'http://search.cpan.org/dist/Nagios-Plugin-LDAP/bin/check_ldap_repl',
32             license =>
33             qq|This library is free software, you can redistribute it and/or modify\nit under the same terms as Perl itself.|,
34             );
35              
36 0           $self->_add_ldap_options;
37              
38 0           return $self;
39             }
40              
41              
42             sub _add_ldap_options {
43 0     0     my ($self) = @_;
44              
45 0           my @args = (
46             { spec => 'master|M=s',
47             help =>
48             qq|-M, --master=ADDRESS\n Host name or IP Address of master LDAP server to check replication|,
49             required => 1,
50             },
51             { spec => 'hostname|H=s',
52             help =>
53             qq|-H, --hostname=ADDRESS\n Host name, IP Address, or unix socket (must be an absolute path)|,
54             required => 1,
55             },
56             { spec => 'port|p=i',
57             help => qq|-p, --port=INTEGER\n Port number (default: 389)|,
58             },
59             { spec => 'use-ipv4|4!',
60             help => qq|-4, --use-ipv4\n Use IPv4 connection|,
61             },
62             { spec => 'use-ipv6|6!',
63             help => qq|-6, --use-ipv6\n Use IPv6 connection|,
64             },
65             { spec => 'attr|a=s',
66             help => qq|-a [--attr]\n ldap attribute to search (default: "(objectclass=*)")|,
67             },
68             { spec => 'base|b=s',
69             help => qq|-b [--base]\n ldap base (eg. ou=my unit, o=my org, c=at)|,
70             },
71             { spec => 'bind|D=s',
72             help => qq|-D [--bind]\n ldap bind DN (if required)|,
73             },
74             { spec => 'pass|P=s',
75             help => qq|-P [--pass]\n ldap password (if required)|,
76             },
77             { spec => 'starttls|T!',
78             help => qq|-T [--starttls]\n use starttls mechanism introduced in protocol version 3|,
79             },
80             { spec => 'ssl|S!',
81             help =>
82             qq|-S [--ssl]\n use ldaps (ldap v2 ssl method). this also sets the default port to %s|,
83             },
84             { spec => 'ver2|2!',
85             help => qq|-2 [--ver2]\n use ldap protocol version 2|,
86             },
87             { spec => 'ver3|3!',
88             help => qq|-3 [--ver3]\n use ldap protocol version 3\n (default protocol version: 2)|,
89             },
90             { spec => 'repl-warning=i',
91             help =>
92             qq|--repl-warning=INTEGER\n Replication time delta to result in warning status (seconds)|,
93             },
94             { spec => 'repl-critical=i',
95             help =>
96             qq|-c, --critical=DOUBLE\n Replication time delta to result in critical status (seconds)|,
97             },
98             { spec => 'warning|w=f',
99             help => qq|-w, --warning=DOUBLE\n Response time to result in warning status (seconds)|,
100             },
101             { spec => 'critical|c=f',
102             help => qq|-c, --critical=DOUBLE\n Response time to result in critical status (seconds)|,
103             },
104             );
105              
106 0           $self->add_arg(%$_) for (@args);
107             }
108              
109              
110             sub run {
111 0     0 1   my ($self) = @_;
112              
113 0           $self->getopts;
114              
115 0           my $opts = $self->opts;
116              
117 0   0       my $hostname = $opts->get('hostname') || 'localhost';
118              
119 0 0         if (my $ldap = $self->_ldap_connect($hostname)) {
120 0 0         if ($self->_ldap_bind($ldap)) {
121 0           $self->_ldap_search($ldap);
122             }
123 0           $self->_ldap_check_repl($ldap);
124             }
125              
126 0           $self->nagios_exit($self->check_messages(join => ", "));
127 0           return;
128             }
129              
130             sub _ldap_connect {
131 0     0     my ($self, $hostname) = @_;
132 0           my $opts = $self->opts;
133              
134 0   0       my $timeout = $opts->get('timeout') || 4;
135 0           my $ssl = $opts->get('ssl');
136 0   0       my $port = $opts->get('port') || ($ssl ? 636 : 389);
137 0   0       my $starttls = $opts->get('starttls') && !$ssl;
138 0 0         my $version = $opts->get('ver3') ? 3 : 2;
139 0           my $ipv6 = $opts->get('use-ipv6');
140              
141 0 0         my $class = $ssl ? 'Net::LDAPS' : 'Net::LDAP';
142              
143 0 0 0       if ($ssl and !eval { require Net::LDAPS }) {
  0            
144 0           my $err = $@;
145 0           $self->add_message(WARNING, $err);
146 0           return;
147             }
148              
149 0           my $ldap = $class->new(
150             $hostname,
151             timeout => $timeout,
152             version => $version,
153             inet6 => $ipv6,
154             );
155              
156 0 0         unless ($ldap) {
157 0           my $err = $@;
158 0           $self->add_message(CRITICAL, "$hostname: " . $err);
159 0           return;
160             }
161              
162 0 0         if ($starttls) {
163 0           my $mesg = $ldap->start_tls;
164 0 0         if ($mesg->code) {
165 0           $self->add_message(WARNING, "starttls: [$hostname] " . $mesg->error);
166 0           return;
167             }
168             }
169              
170 0           return $ldap;
171             }
172              
173              
174             sub _ldap_bind {
175 0     0     my ($self, $ldap) = @_;
176 0           my $opts = $self->opts;
177 0 0         my $bind = $opts->get('bind') or return 1;
178              
179 0           my $pass = $opts->get('pass');
180 0 0         my @auth = $pass ? (password => $pass) : (noauth => 1);
181 0           my $mesg = $ldap->bind($bind, @auth);
182              
183 0 0         if ($mesg->code) {
184 0           $self->add_message(WARNING, "bind: " . $mesg->error_desc);
185 0           return 0;
186             }
187              
188 0           return 1;
189             }
190              
191             sub _ldap_do_search {
192 0     0     my ($self, $ldap, $filter, @attrs) = @_;
193 0           my $opts = $self->opts;
194              
195 0           my $base = $opts->get('base');
196 0 0         unless ($base) {
197 0           $self->add_message(WARNING, "No search base");
198 0           return 0;
199             }
200              
201 0 0         my $mesg = $ldap->search(
    0          
202             scope => (@attrs ? 'base' : 'subtree'),
203             base => $base,
204             filter => $filter,
205             (@attrs ? (attrs => \@attrs) : ()),
206             );
207 0 0         if ($mesg->code) {
208 0           $self->add_message(WARNING, "search: " . $mesg->error_desc);
209 0           return;
210             }
211              
212 0 0         unless ($mesg->count) {
213 0           $self->add_message(WARNING, "search: No entries found @attrs");
214 0           return;
215             }
216              
217 0           return $mesg->pop_entry;
218             }
219              
220             sub _ldap_search {
221 0     0     my ($self, $ldap) = @_;
222 0           my $opts = $self->opts;
223              
224 0           my $warning = $opts->get('warning');
225 0           my $critical = $opts->get('critical');
226              
227 0 0 0       return 1 unless $warning or $critical;
228              
229 0   0       my $attr = $opts->get('attr') || '(objectClass=*)';
230              
231 0           my $start = time;
232 0           $self->_ldap_do_search($ldap, $attr);
233 0           my $delta = time - $start;
234              
235 0           $self->add_message($self->check_threshold($delta), sprintf("%.3f seconds response time", $delta));
236              
237 0           $self->add_perfdata(
238             label => 'time',
239             value => sprintf("%.4f", $delta),
240             uom => 's',
241             threshold => $self->threshold
242             );
243              
244 0           return 1;
245             }
246              
247             sub _ldap_check_repl {
248 0     0     my ($self, $dst_ldap) = @_;
249 0           my $opts = $self->opts;
250              
251 0 0         my $master = $opts->get('master') or return 1;
252              
253 0           my $warning = $opts->get('repl-warning');
254 0           my $critical = $opts->get('repl-critical');
255 0           my $verbose = $opts->get('verbose');
256              
257 0 0 0       return 1 unless $warning or $critical;
258              
259 0 0         my $src_ldap = $self->_ldap_connect($master) or return;
260              
261 0 0         my $src_entry =
262             $self->_ldap_do_search($src_ldap, '(&(objectClass=*)(contextCSN=*))', 'contextCSN')
263             or return;
264 0 0         my $dst_entry =
265             $self->_ldap_do_search($dst_ldap, '(&(objectClass=*)(contextCSN=*))', 'contextCSN')
266             or return;
267              
268 0           my $src_csn = $src_entry->get_value('contextCSN');
269 0           my $dst_csn = $dst_entry->get_value('contextCSN');
270              
271 0 0         print "Master CSN = $src_csn\n" if $verbose;
272 0 0         print "Slave CSN = $dst_csn\n" if $verbose;
273              
274 0           my ($YYYY, $MM, $DD, $hh, $mm, $ss);
275 0           ($YYYY, $MM, $DD, $hh, $mm, $ss) = $src_csn =~ /^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
276 0           my $src_dt = DateTime->new(
277             year => $YYYY,
278             month => $MM,
279             day => $DD,
280             hour => $hh,
281             minute => $mm,
282             second => $ss
283             );
284 0           ($YYYY, $MM, $DD, $hh, $mm, $ss) = $dst_csn =~ /^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
285 0           my $dst_dt = DateTime->new(
286             year => $YYYY,
287             month => $MM,
288             day => $DD,
289             hour => $hh,
290             minute => $mm,
291             second => $ss
292             );
293              
294 0           my $delta = abs($src_dt->epoch - $dst_dt->epoch);
295              
296 0           $self->add_message(
297             $self->check_threshold(check => $delta, warning => $warning, critical => $critical),
298             sprintf("%d seconds replication delta", $delta));
299              
300 0           $self->add_perfdata(
301             label => 'repl',
302             value => $delta,
303             uom => 's',
304             threshold => $self->threshold
305             );
306              
307             }
308              
309             __END__