#!/usr/bin/perl -w
# Nagios plugin that checks LSI SAS2 controllers RAID status via sas2ircu
# program

# Copyright (C) 2011 Emmanuel Lacour <elacour@home-dn.net>
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any
# later version.
#
# This file is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this file; see the file COPYING.  If not, write to the Free
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
# 02110-1301, USA.



use strict;
use lib qw(/usr/lib/nagios/plugins /usr/lib64/nagios/plugins);
use utils qw(%ERRORS);

# some constants.
our $OPTIMAL='Optimal (OPT)';

my $sas2ircu = '/usr/sbin/sas2ircu';
my $status = $ERRORS{'OK'};
my $disk_stat = $ERRORS{'OK'};
my %controllers;
my $errors_message;
my @perfdata = ();
my %disks_in_vols;

## needed to make perl-suidperl happy.
$ENV{PATH}="";

unless ( -x $sas2ircu ) {
    print "$sas2ircu not found or not executable\n";
    exit ($ERRORS{'UNKNOWN'});
}

# since we're running w/ setuid, don't perform this check
unless ( 1==1 or $> == 0 ) {
    print "This program must be run as root. You may use wrappers like sudo to do this.\n";
    exit ($ERRORS{'UNKNOWN'});
}

foreach my $line (split /\n/, `$sas2ircu LIST`) {
    if ( $line =~ /^\s+(\d+)\s+(.+)\s+(.+)\s+(.+)\s+(.+)\s+(.+)\s+(.+)\s*$/ ) {
        my $ctrl_id = $1;
        my $ctrl_type = $2;
        $controllers{$ctrl_id}{type} = $ctrl_type;
    }
}

unless ( scalar keys %controllers ) {
    print "No controller found\n";
    exit ($ERRORS{'UNKNOWN'});
}

foreach my $ctrl_id (keys %controllers) {
    my $infos = `$sas2ircu $ctrl_id DISPLAY`;

    my %ctrl_infos;
    my $ctrl_start;
    foreach my $line (split /\n/, $infos) {
	chomp($line);
        if ( $line =~ /^Controller information/ ) {
            $ctrl_start = 1;
            next;
        }
	next unless ( $ctrl_start );
        if ( $line =~ /^-/ && $ctrl_start == 1 ) {
            $ctrl_start = 2;
            next;
        }
        if ( $line =~ /^-/ && $ctrl_start == 2 ) {
            $ctrl_start = 0;
            next;
        }
        next unless ( $ctrl_start == 2 );
        if ( $line =~ /^\s*(.*)\s+:\s+(.*)$/ ) {
            my $key = $1;
            my $value = $2;
            $key =~ s/\s+$//;
            $key =~ s/^\s+//;
            $value =~ s/\s+$//;
            $value =~ s/^\s+//;
            $ctrl_infos{$key} = $value;
        }
    }

    # Volumes
    my %volumes_infos;
    my $volume_id = undef;
    foreach my $line (split /\n/, $infos) {
	chomp($line);
        if ( $line =~ /^IR volume (\d+)\s*$/ ) {
            $volume_id = $1;
            next;
        }
        if ( defined($volume_id) && $line =~ /^\s*$/ ) {
            $volume_id = undef;
            next;
        }
        next unless ( defined($volume_id) );
        if ( $line =~ /^\s*(.*)\s+:\s+(.*)\s*$/ ) {
            my $key = $1;
            my $value = $2;
            $key =~ s/\s+$//;
            $key =~ s/^\s+//;
            $value =~ s/\s+$//;
            $value =~ s/^\s+//;
            if ( $key =~ /^PHY/ ) {
                push @{$volumes_infos{$volume_id}{'disks'}}, $value;
            } else {
                $volumes_infos{$volume_id}{$key} = $value;
            }
        }
    }

    unless ( scalar keys %volumes_infos ) {
        print "\tNo volume found\n";
        next;
    }

    # Disks
    my %disks_infos;
    my $disk_start = 0;
    my $disk_encl = undef;
    my $disk_slot = undef;
    foreach my $line (split /\n/, $infos) {
	chomp($line);
        if ( $line =~ /^Device is a Hard disk/ ) {
            $disk_start = 1;
            next;
        }
        if ( $disk_start && $line =~ /^\s*$/ ) {
            $disk_start = 0;
            $disk_encl = undef;
            $disk_slot = undef;
            next;
        }
        next unless ( $disk_start );
        if ( $line =~ /^\s*(.*)\s+:\s+(.*)\s*$/ ) {
            my $key = $1;
            my $value = $2;
            $key =~ s/\s+$//;
            $key =~ s/^\s+//;
            $value =~ s/\s+$//;
            $value =~ s/^\s+//;
            if ( $key eq 'Enclosure #' ) {
                $disk_encl = $value;
                next;
            }
            if ( $key eq 'Slot #' ) {
                $disk_slot = $value;
                next;
            }
            if ( defined($disk_encl) && defined($disk_slot) ) {
                $disks_infos{$disk_encl.':'.$disk_slot}{$key} = $value;
            }
        }
    }

    # if any of the disks in a volume are less than optimal, consider the volume in critical state
    foreach my $volume_id (keys %volumes_infos) {
	my %vol_info = %{$volumes_infos{$volume_id}};
        unless ( $vol_info{'Status of volume'} eq 'Okay (OKY)' ) {
            $errors_message .= "Ctrl $ctrl_id: vol $volume_id ".$vol_info{'Status of volume'};
            $status = $ERRORS{'CRITICAL'} ;
        }

	my $disk_err_msg ;
        foreach my $disk (@{$vol_info{disks}}) {
            unless ( $disks_infos{$disk} ) {
                $disk_err_msg .= " $disk not found!";
                $status = $ERRORS{'CRITICAL'};
                next;
            }
            unless ( $disks_infos{$disk}{'State'} eq $OPTIMAL ) {
                $disk_err_msg .= " disk: ".$disk." : ".$disks_infos{$disk}{'State'};
                $status = $ERRORS{'CRITICAL'} ;
            }
        }
	if (defined($disk_err_msg)) {
	    $errors_message .= "CRIT: volume ". $volume_id .":" . $disk_err_msg;
	}
	
	push(@perfdata, "volume:${volume_id}-disks:" . join(",",@{$vol_info{disks}}));
	map { $disks_in_vols{$_} = 1 } @{$vol_info{disks}}; ## remember the disks in volumes
    }

    # if any of the disks that are not in a volume are less than optimal, consider it a warning.
    my $disk_err;
    my $disk_perf ="";
    #print join("  ",keys %disks_in_vols). "\n";
    while (my ($disk_id, $disk_info) = each %disks_infos) {
	next if defined($disks_in_vols{$disk_id});
	unless ($disk_info->{'State'} eq $OPTIMAL) {
	    $disk_stat = $ERRORS{'WARNING'}; 
	    $disk_err .= "$disk_id : $disk_info->{'State'}";
	}
	$disk_perf .= "$disk_id ";

    }
    push(@perfdata, "Disks:".$disk_perf);
    $errors_message .= "WARN: $disk_err" if defined($disk_err);
}

my $msg;
if ( $status == $ERRORS{'OK'} ) {
    $msg = "All arrays OK";
} elsif ( $errors_message ) {
    $msg = $errors_message;
}

print "$msg |".join(" ",@perfdata). "\n";
exit ($status);
