#!/usr/bin/perl -w
#
# Plugin to check status of linux mdstat raid partitions
#   Some code taken from check_linux_raid.pl by Steve Milton, 
#     Copyright (c) 2002 ISOMEDIA, Inc.
#

use strict;
use File::Basename;
use Nagios::Plugin::Getopt;
use Nagios::Plugin 0.1301;

my $ng = Nagios::Plugin::Getopt->new(
  usage => q(Usage: %s [-e] [-v]),
  version => '0.03',
  url => 'https://github.com/gavincarr/nagios-of-plugins',
  blurb => q(This plugin checks the status of linux mdstat raid partitions.),
);
$ng->arg("errors-only|e", 
  qq(-e, --errors-only
   Display only devices with errors));
$ng->arg(
  spec => "missing-ok",
  help => qq(--missing-ok
   Don't give warnings if no raid devices exist.));
$ng->getopts;

my $np = Nagios::Plugin->new;

# Exit with an error if we're not on Linux
$np->nagios_exit(UNKNOWN, $np->shortname . " only works on Linux") 
    unless $^O eq 'linux';

open(MDSTAT, "</proc/mdstat") or
    $np->nagios_exit(CRITICAL, "Failed to open /proc/mdstat: $!");

my %md = ();
my $current = '';
while (<MDSTAT>) {
    chomp;
    if (/^(md\d+)\s*:/) {
        my $dev = $1;
        $md{$dev} ||= {};
        $md{$dev}->{line1} = $_;
        $md{$dev}->{line1} =~ s/\s+$//;
        $md{$dev}->{active} = 1 if m/active/;
        $current = $dev;
    }
    elsif (m/^\s*$/) {
        undef $current;
    }
    elsif ($current) {
        if (/(\[[_U]+\])/) {
            $md{$current}->{status} = $1;
            $md{$current}->{line2} = $_;
            $md{$current}->{line2} =~ s/^\s+//;
            $md{$current}->{line2} =~ s/\s+$//;
        }
        elsif (/recovery\s*=\s*(\S+)/) {  
            $md{$current}->{recovery} = $1;
            $md{$current}->{line3} = $_;
            $md{$current}->{line3} =~ s/^\s+//;
            $md{$current}->{line3} =~ s/\s+$//;
            $md{$current}->{finish} = $1 if /finish\s*=\s*(\S+)/;
        }
        elsif (/resync\s*=\s*(\S+)/) {  
            $md{$current}->{resync} = $1;
            $md{$current}->{line3} = $_;
            $md{$current}->{line3} =~ s/^\s+//;
            $md{$current}->{line3} =~ s/\s+$//;
            $md{$current}->{finish} = $1 if /finish\s*=\s*(\S+)/;
        }
    }
}

eval { require YAML; print "md data:\n" . YAML::Dump(\%md) } if $ng->verbose;

my @check;
if (@ARGV) {
    my @bogus = grep { ! exists $md{$_} } @ARGV;
    if (@bogus) {
        $np->nagios_exit(CRITICAL, 
            sprintf("Unknown md device%s: %s", 
                (@bogus > 1 ? 's' : ''), 
                join(' ', @bogus)
            )
        );
    }
    @check = @ARGV;
}
else {
    @check = sort keys %md;
}
$np->nagios_exit($ng->get('missing-ok') ? OK : UNKNOWN, "No raid devices found")
  if ! @check;

my (@crit, @warn, @ok);
for my $dev (@check) {
    my $status = sprintf("%s%s%s", 
        $md{$dev}->{line1} || '',
        $md{$dev}->{line2} ? ' ' . $md{$dev}->{line2} : '',
        $md{$dev}->{line3} ? ' ' . $md{$dev}->{line3} : '',
    );
    if ($md{$dev}->{status} && $md{$dev}->{status} =~ m/_/) {
        if ($md{$dev}->{recovery}) {
            push @warn, $status;
        }
        else {
            push @crit, $status;
        }
    }
    elsif ($md{$dev}->{status} && $md{$dev}->{status} =~ m/\[U+\]/) {
        push @ok, $status unless $ng->get('errors-only');
    }
    elsif ($md{$dev}->{active}) {
        push @ok, $status unless $ng->get('errors-only');
    }
    else {
        push @crit, $status;
    }
}

my $results = join ' :: ', @crit, @warn, @ok;
$results ||= sprintf "%s raid device%s found, all OK", 
    scalar(keys %md), (scalar(keys %md) == 1 ? '' : 's');
my $code = $np->check_messages(critical => \@crit, warning => \@warn);
$np->nagios_exit($code, $results);

# vim:ft=perl:ai:sw=4