dsa-check-libs: also ignore files that are open via normal file handles on NFS.
[mirror/dsa-nagios.git] / dsa-nagios-checks / checks / dsa-check-drbd
1 #!/usr/bin/perl -w
2
3 ####################################################
4 # check_drbd v0.5.3                                #
5 # by Brandon Lee Poyner    bpoyner / CCAC.edu      #
6 ####################################################
7
8 use strict;
9 use File::Basename;
10 use Getopt::Long;
11
12 my $drbd_proc='/proc/drbd';
13 my $drbd_devices=0;
14 my ($drbd_expect, $drbd_role, $drbd_version, $debug_mode); 
15 my (%options, %cs, %st, %ld, %ds, %check, %warning, %critical);
16
17 my $prog_name=basename($0);
18 my $prog_revision='0.5.3';
19
20 my %errorcodes = (
21         'OK' => { 'retvalue' => 0 },
22         'WARNING' => { 'retvalue' => 1 },
23         'CRITICAL' => { 'retvalue' => 2 },
24         'UNKNOWN' => { 'retvalue' => 3 }
25 );
26
27
28 # Define various states and default alarm values
29 #
30 my %state = ( 
31               'Primary' => { 'value' => 'OK', 'type' => 'st' },
32               'Secondary' => { 'value' => 'OK', 'type' => 'st' },
33               'Unknown' => { 'value' => 'CRITICAL', 'type' => 'st' },
34               'StandAlone' => { 'value' => 'WARNING', 'type' => 'cs' },
35               'Unconnected' => { 'value' => 'CRITICAL', 'type' => 'cs' },
36               'Timeout' => { 'value' => 'CRITICAL', 'type' => 'cs' },
37               'BrokenPipe' => { 'value' => 'CRITICAL', 'type' => 'cs' },
38               'WFConnection' => { 'value' => 'CRITICAL', 'type' => 'cs' },
39               'WFReportParams' => { 'value' => 'CRITICAL', 'type' => 'cs' },
40               'Connected' => { 'value' => 'OK', 'type' => 'cs' },
41               'Unconfigured' => { 'value' => 'OK', 'type' => 'cs' },
42               # DRBD 0.6
43               'SyncingAll' => { 'value' => 'WARNING', 'type' => 'cs' },
44               'SyncingQuick' => { 'value' => 'WARNING', 'type' => 'cs' },
45               'SyncPaused' => { 'value' => 'CRITICAL', 'type' => 'cs' },
46               # DRBD 0.7
47               'WFBitMapS' => { 'value' => 'CRITICAL', 'type' => 'cs' },
48               'WFBitMapT' => { 'value' => 'CRITICAL', 'type' => 'cs' },
49               'SyncSource' => { 'value' => 'WARNING', 'type' => 'cs' }, 
50               'SyncTarget' => { 'value' => 'WARNING', 'type' => 'cs' },
51               'PausedSyncS' => { 'value' => 'CRITICAL', 'type' => 'cs' },
52               'PausedSyncT' => { 'value' => 'CRITICAL', 'type' => 'cs' },
53               'NetworkFailure' => { 'value' => 'CRITICAL', 'type' => 'cs' },
54               'SkippedSyncS' => { 'value' => 'CRITICAL', 'type' => 'cs' },
55               'SkippedSyncT' => { 'value' => 'CRITICAL', 'type' => 'cs' },
56               'Consistent' => { 'value' => 'OK', 'type' => 'ld' }, 
57               'Inconsistent' => { 'value' => 'CRITICAL', 'type' => 'ld' },
58               # DRBD 8.0
59               'UpToDate' => { 'value' => 'OK', 'type' => 'ds' },
60               'Consistent' => { 'value' => 'OK', 'type' => 'ds' },
61               'Negotiating' => { 'value' => 'WARNING', 'type' => 'ds' },
62               'Attaching' => { 'value' => 'WARNING', 'type' => 'ds' },
63               'Diskless' => { 'value' => 'CRITICAL', 'type' => 'ds' },
64               'Failed' => { 'value' => 'CRITICAL', 'type' => 'ds' },
65               'Outdated' => { 'value' => 'CRITICAL', 'type' => 'ds' },
66               'Inconsistent' => { 'value' => 'CRITICAL', 'type' => 'ds' },
67               'DUnknown' => { 'value' => 'CRITICAL', 'type' => 'ds' },
68               # DRBD 8.2
69               'VerifyS' => { 'value' => 'WARNING', 'type' => 'cs' },
70               'VerifyT' => { 'value' => 'WARNING', 'type' => 'cs' },
71               # DRBD 8.3
72               'Disconnecting' => { 'value' => 'WARNING', 'type' => 'cs' },
73               'ProtocolError' => { 'value' => 'CRITICAL', 'type' => 'cs' },
74               'TearDown' => { 'value' => 'WARNING', 'type' => 'cs' },
75               'StartingSyncS' => { 'value' => 'WARNING', 'type' => 'cs' },
76               'StartingSyncT' => { 'value' => 'WARNING', 'type' => 'cs' },
77               'WFSyncUUID' => { 'value' => 'WARNING', 'type' => 'cs' }
78 );
79
80 &parse_options;
81 &parse_proc;
82 &parse_drbd_devices;
83 &check_drbd_state;
84 &report_status;
85 &myexit('UNKNOWN',"$prog_name should never reach here");
86
87 sub print_usage {
88         print <<EOF
89 Usage: $prog_name [-d <All|Configured|...>] [-e expect] [-p proc] [-r role] [-o states] [-w states] [-c states] [--debug]
90         Options:
91         -d STRING [default: $drbd_devices.  Example: 0,1,2 ]
92         -p STRING [default: $drbd_proc.  Use '-' for stdin]
93         -e STRING [Must be this connected state. Example: Connected]
94         -r STRING [Must be this node state. Example: Primary]
95         -o STRING [Change value to OK. Example: StandAlone]
96         -w STRING [Change value to WARNING. Example: SyncingAll]
97         -c STRING [Change value to CRITICAL. Example: Inconsistent,WFConnection]
98 EOF
99 }
100
101 sub print_revision {
102         print <<EOF;
103 $prog_name $prog_revision
104
105 The nagios plugins come with ABSOLUTELY NO WARRANTY. You may redistribute
106 copies of the plugins under the terms of the GNU General Public License.
107 For more information about these matters, see the file named COPYING.
108 EOF
109
110 }
111
112 sub print_help {
113         &print_revision;
114         print "\n";
115         &print_usage;
116         print <<EOF;
117
118 Send email to nagios-users\@lists.sourceforge.net if you have questions
119 regarding use of this software. To submit patches or suggest improvements,
120 send email to bpoyner\@ccac.edu
121 EOF
122         exit $errorcodes{'UNKNOWN'}->{'retvalue'};
123 }
124
125 sub parse_options {
126         my ($help, $version, $debug, $ok_string, $warning_string, 
127             $critical_string); 
128         #
129         # Get command line options
130         #
131         GetOptions("h|help" => \$help,
132                 "V|version" => \$version,
133                 "d|device|devices=s" => \$drbd_devices,
134                 "e|expect=s" => \$drbd_expect,
135                 "p|proc=s" => \$drbd_proc,
136                 "r|role=s" => \$drbd_role,
137                 "o|ok=s" => \$ok_string,
138                 "w|warning=s" => \$warning_string,
139                 "c|critical=s" => \$critical_string,
140                 "debug" => \$debug);
141         if (defined($help) && ($help ne "")) {
142                 &print_help;
143                 exit $errorcodes{'UNKNOWN'}->{'retvalue'};
144         }
145         if (defined($version) && ($version ne "")) {
146                 &print_revision;
147                 exit $errorcodes{'UNKNOWN'}->{'retvalue'};
148         }
149         if (defined($drbd_expect) && ($drbd_expect ne "")) {
150                 # User requested the connected state to be very specific
151                 &change_values($drbd_expect,'cs','expect','connected state');
152         }
153         if (defined($drbd_role) && ($drbd_role ne "")) {
154                 # User requested the node state to be very specific
155                 &change_values($drbd_role,'st','role','node state');
156         }
157         if (defined($ok_string) && ($ok_string ne "")) {
158                 # User requested certain values to be OK
159                 &set_values($ok_string,'OK');
160         }
161         if (defined($warning_string) && ($warning_string ne "")) {
162                 # User requested certain values to be WARNING
163                 &set_values($warning_string,'WARNING');
164         }
165         if (defined($critical_string) && ($critical_string ne "")) {
166                 # User requested certain values to be CRITICAL
167                 &set_values($critical_string,'CRITICAL');
168         }
169         if (defined($debug) && ($debug ne "")) {
170                 # 
171                 # Debugging information
172                 #
173                 $debug_mode=1;
174                 print STDERR "<$prog_name settings>\n";
175                 print STDERR "DRBD Devices: $drbd_devices\n";
176                 printf STDERR "DRBD Proc: %s\n", defined($drbd_proc)?$drbd_proc:"";
177                 printf STDERR "DRBD Expect: %s\n", defined($drbd_expect)?$drbd_expect:"";
178                 printf STDERR "DRBD Role: %s\n", defined($drbd_role)?$drbd_role:"";
179                 my (@ok, @critical, @warning);
180                 for my $key ( keys %state ) {
181                         if ($state{$key}->{'value'} eq 'OK') {
182                                 push(@ok,$key);
183                         }
184                         if ($state{$key}->{'value'} eq 'WARNING') {
185                                 push(@warning,$key);
186                         }
187                         if ($state{$key}->{'value'} eq 'CRITICAL') {
188                                 push(@critical,$key);
189                         }
190                 }
191                 printf STDERR "DRBD OK: %s\n", join(" ",sort(@ok));
192                 printf STDERR "DRBD WARNING: %s\n", join(" ",sort(@warning));
193                 printf STDERR "DRBD CRITICAL: %s\n", join(" ",sort(@critical));
194                 print STDERR "</$prog_name settings>\n";
195         }
196 }
197
198 sub parse_proc {
199         #
200         # Read in contents of proc file, feed results into hashes
201         #
202         my $input;
203         if ( $drbd_proc ne "-" ) {
204                 $input = "DRBD";
205                 if ( ! -e $drbd_proc ) {
206                         &myexit('UNKNOWN',"No such file $drbd_proc");
207                 }
208                 open(DRBD, "$drbd_proc") || 
209                         &myexit('UNKNOWN',"Could not open $drbd_proc");
210         } else {
211                 $input = "STDIN";
212         }
213         while(<$input>) {
214                 if (/^version: (\d+).(\d+)/) {
215                         $drbd_version = "$1.$2";
216                 }
217                 if (/^\s?(\d+):.* cs:(\w+)/) {
218                         $cs{$1} = $2;
219                 }
220                 if (/^\s?(\d+):.* st:(\w+)\//) {
221                         $st{$1} = $2;
222                 }
223                 if (/^\s?(\d+):.* ld:(\w+)/) {
224                         $ld{$1} = $2;
225                 }
226                 if (/^\s?(\d+):.* ds:(\w+)/) {
227                         $ds{$1} = $2;
228                 }
229         }
230         if ( $drbd_proc ne "-" ) {
231                 close(DRBD);
232         }
233         if (defined($debug_mode) && ($debug_mode == 1)) {
234                 # 
235                 # Debugging information
236                 #
237                 print STDERR "<$prog_name devices found>\n";
238                 for my $key ( sort keys %cs ) {
239                         printf STDERR "Found Device $key $cs{$key}%s%s%s\n", defined($st{$key})?" $st{$key}":"", defined($ld{$key})?" $ld{$key}":"", defined($ds{$key})?" $ds{$key}":"";
240                 }
241                 print STDERR "</$prog_name devices found>\n";
242         }
243 }
244
245 sub parse_drbd_devices {
246         #
247         # Determine which DRBD devices to monitor
248         #
249         my @devices;
250         if ($drbd_devices =~ /^all$/i) {
251                 for my $device ( keys %cs ) {
252                         push(@devices,$device);
253                 }
254         } elsif ($drbd_devices =~ /^configured$/i) {
255                 for my $device ( keys %cs ) {
256                         next if ($cs{$device} eq "Unconfigured");
257                         push(@devices,$device);
258                 }
259         } else {
260                 @devices = split(/,/,$drbd_devices);
261         }
262         foreach my $device (@devices) {
263                 if (!(defined($cs{$device}))) {
264                         &myexit('OK',"Could not find device $device");
265                 }
266                 $check{$device} = 1;
267         }
268         if (int(keys %check) == 0) {
269                 &myexit('UNKNOWN',"No configured devices found");
270         }
271         if (defined($debug_mode) && ($debug_mode == 1)) {
272                 # 
273                 # Debugging information
274                 #
275                 print STDERR "<$prog_name devices to check>\n";
276                 for my $key ( sort keys %check ) {
277                         printf STDERR "Checking enabled for device $key\n";
278                 }
279                 print STDERR "</$prog_name devices to check>\n";
280         }
281 }
282
283 sub check_drbd_state {
284         for my $drbd_device ( sort keys %check ) {
285                 if ((defined($drbd_version)) && ($drbd_version >= '8.0')) {
286                         #
287                         # We're dealing with version 8.0 or greater 
288                         # Set data state
289                         #
290                         if ((defined($ds{$drbd_device})) &&
291                             (defined($state{$ds{$drbd_device}}))) {
292                                 $state{$ds{$drbd_device}}->{$drbd_device}->{'level'} = 1;
293                         } elsif (defined($ds{$drbd_device})) {
294                                 &myexit('CRITICAL',"Data state unknown value '$ds{$drbd_device}' for device $drbd_device");
295                         }
296                 }
297                 if ((defined($drbd_version)) && ($drbd_version == '0.7')) {
298                         #
299                         # We're dealing with version 0.7 
300                         # Set local data consistency
301                         #
302                         if ((defined($ld{$drbd_device})) &&
303                             (defined($state{$ld{$drbd_device}}))) {
304                                 $state{$ld{$drbd_device}}->{$drbd_device}->{'level'} = 1;
305                         } elsif (defined($ld{$drbd_device})) {
306                                 &myexit('CRITICAL',"Local data consistency unknown value '$ld{$drbd_device}' for device $drbd_device");
307                         }
308                 }
309                 #
310                 # Check for a state value (Primary, Secondary, etc)
311                 #
312                 if ((defined($st{$drbd_device})) &&
313                     (defined($state{$st{$drbd_device}}))) {
314                         $state{$st{$drbd_device}}->{$drbd_device}->{'level'} = 1;
315                 } elsif (defined($st{$drbd_device})) {
316                         &myexit('CRITICAL',"Node state unknown value '$st{$drbd_device}' for device $drbd_device");
317                 }
318                 # 
319                 # Check for a connected state value (Connected, StandAlone, etc)
320                 #
321                 if (defined($state{$cs{$drbd_device}})) {
322                         $state{$cs{$drbd_device}}->{$drbd_device}->{'level'} = 1;
323                 } else {
324                         &myexit('CRITICAL',"Connection state unknown value '$cs{$drbd_device}' for device $drbd_device");
325                 }
326                 # 
327                 # Debugging information
328                 #
329                 if (defined($debug_mode) && ($debug_mode == 1)) {
330                         print STDERR "<$prog_name device $drbd_device status>\n";
331                         for my $key ( keys %state ) {
332                                 if (defined($state{$key}->{$drbd_device}->{'level'})) {
333                                         print STDERR "$key $state{$key}->{'value'}\n";
334                                 }
335                         }
336                         print STDERR "</$prog_name device $drbd_device status>\n";
337                 }
338                 #
339                 # Determine if any values are CRITICAL or WARNING
340                 #
341                 for my $key ( keys %state ) {
342                         if (defined($state{$key}->{$drbd_device}->{'level'})) {
343                                 if ($state{$key}->{'value'} eq "CRITICAL") {
344                                         $critical{$drbd_device} = 1;
345                                 }
346                                 if ($state{$key}->{'value'} eq "WARNING") {
347                                         $warning{$drbd_device} = 1;
348                                 }
349                         }
350                 }
351         }
352 }
353
354 sub report_status {
355         my $message;
356         my $critical_count=int(keys %critical);
357         my $warning_count=int(keys %warning);
358         if ($critical_count > 0) {
359                 #
360                 # We found a CRITICAL situation
361                 #
362                 my $i = 0;
363                 for my $device (sort keys %critical) {
364                         $message.=sprintf("Device %d%s $cs{$device}%s%s", $device,defined($st{$device})?" $st{$device}":"",defined($ld{$device})?" $ld{$device}":"",defined($ds{$device})?" $ds{$device}":""); 
365                         $i++;
366                         if ($i != $critical_count) {
367                                 $message.=", ";
368                         }
369                 }
370                 &myexit('CRITICAL',$message);
371         } elsif ($warning_count > 0) {
372                 #
373                 # We found a WARNING situation
374                 #
375                 my $i = 0;
376                 for my $device (sort keys %warning) {
377                         $message.=sprintf("Device %d%s $cs{$device}%s%s", $device,defined($st{$device})?" $st{$device}":"",defined($ld{$device})?" $ld{$device}":"",defined($ds{$device})?" $ds{$device}":""); 
378                         $i++;
379                         if ($i != $warning_count) {
380                                 $message.=", ";
381                         }
382                 }
383                 &myexit('WARNING',$message);
384         } else {
385                 #
386                 # Everything checks out OK
387                 #
388                 my $device_count=int(keys %check);
389                 if ($device_count == 1) {
390                         for my $device ( sort keys %check ) {
391                                 $message=sprintf("Device %d%s $cs{$device}%s%s", $device,defined($st{$device})?" $st{$device}":"",defined($ld{$device})?" $ld{$device}":"",defined($ds{$device})?" $ds{$device}":"");
392                         }
393                 } else {
394                         my $i = 0;
395                         for my $device ( sort keys %check ) {
396                                 $message.=sprintf("Dev %d %0.3s%0.3s%0.3s%0.3s", $device,defined($st{$device})?"$st{$device}":"",$cs{$device},defined($ld{$device})?"$ld{$device}":"",defined($ds{$device})?"$ds{$device}":"");
397                                 $i++;
398                                 if ($i != $device_count) {
399                                         $message.=", ";
400                                 }
401                         }
402                 }
403                 &myexit('OK',$message);
404         }
405 }
406
407 sub set_values {
408         #
409         # Set item to value requested
410         #
411         my ($items,$value) = @_;
412         my @items = split(/,/,$items);
413         foreach my $item (@items) {
414                 if (defined($state{$item})) {
415                         $state{$item}->{'value'} = "$value";
416                 } else {
417                         print STDERR "State '$item' not found\n"; 
418                 }
419         }
420 }
421
422 sub change_values {
423         #
424         # Look for all values of a given type, set requested value to OK
425         # and all other values to CRITICAL
426         #
427         my ($argument,$type,$error1,$error2) = @_;
428         if ((defined($state{$argument})) && 
429             ($state{$argument}->{'type'} eq "$type")) {
430                 for my $key ( keys %state ) {
431                         if ($state{$key}->{'type'} eq "$type") {
432                                 if ($key eq $argument) {
433                                         &set_values($argument,'OK');
434                                 } else {
435                                         &set_values($key,'CRITICAL');
436                                 }
437                         } 
438                 }
439         } else {
440                 &myexit('UNKNOWN',"$error1 option only works for $error2");
441         }
442 }
443
444 sub myexit {
445         #
446         # Print error message and exit
447         #
448         my ($error, $message) = @_;
449         if (!(defined($errorcodes{$error}))) {
450                 printf STDERR "Error code $error not known\n";
451                 print "DRBD UNKNOWN: $message\n";
452                 exit $errorcodes{'UNKNOWN'}->{'retvalue'};
453         }
454         print "DRBD $error: $message\n";
455         exit $errorcodes{$error}->{'retvalue'};
456 }