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