#! /usr/bin/perl -w

#####################################################################
# What's this ?
#####################################################################
# Checks the health of RAID disks
# Requires aacraid driver and afacli program
# More infos on http://public.www.planetmirror.com/pub/pub/domsch/
# Script designed for Nagios/NRPE
# 
# Usage :
# You can set warning level and cirtical level with -w and -c
# This level corresponds to the number of disks in failure
# Option -h displays a small usage message
# 
# Copyright (C) 2004 Clement OUDOT <clement.oudot@linagora.com>
#
# This program 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
# of the License, or (at your option) any later version.
#
# This program 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.
#
# GPL Licence : http://www.gnu.org/licenses/gpl.txt
#
# More contributions on http://www.linagora.org
 

#####################################################################
# Modules
#####################################################################

use strict ;
use Getopt::Std ;

#####################################################################
# Configuration
#####################################################################

# Path to afacli program
my $afacli = "/usr/sbin/afacli" ;

# Name of Array Device
my $array_name = "afa0" ;
my $array_device = "/dev/".$array_name ;

# Name of Instructions file for afacli
my $infile = "/tmp/afacli.in" ;

# Name of Log file of afacli
my $outfile = "/tmp/afacli.out" ;

# Nagios codes
my %code = ( 'Ok' => 0, 'Warning' => 1, 'Critical' => 2, 'Unknown' => 3) ;

# Set TERM env (required by afacli program)
$ENV{TERM} = "xterm" ;

#####################################################################
# Command line parameters
#####################################################################

# Init Options Hash Table
my %opts ;
getopt('wch',\%opts) ;
&usage if exists $opts{"h"} ;
$opts{"w"} = 1 unless (exists($opts{"w"})) ;
$opts{"c"} = 1 unless (exists($opts{"c"})) ;

my $warning_level = $opts{"w"} ;
my $critical_level = $opts{"c"} ;

#####################################################################
# Usage function
#####################################################################

sub usage {
	print "Usage : $0 -w <warning level> -c <critical level>\n" ;
	exit $code{'Unknown'} ;
}

#####################################################################
# Tests
#####################################################################

# Is afacli executable ?
unless (-x $afacli) {
	print "Can't execute $afacli\n" ;
	exit $code{'Unknown'} ;
}

# Does Array Device exists ?
unless (-e $array_device ) {
	print "SCSI Device $array_name Unknown\n" ;
	exit $code{'Unknown'} ;
}

# Do we have enough rights to read/write the device ?
# Note : Although we just need to read the device, afacli requires
# read/write rights on the device
unless ( (-r $array_device ) && (-w $array_device ) ) {
	print "Not enough rights on device $array_name\n" ;
	exit $code{'Unknown'} ;
}

#####################################################################
# Creation of Instructions File
#####################################################################

# Opening of infile
my $infile_test = open (INFILE, ">$infile") ;
unless ($infile_test) {
	print "Can't open $infile for writing\n";
	exit $code{'Unknown'} ;
}

# Adding instructions
print INFILE "logfile start \"$outfile\"\n" ;
print INFILE "open /readonly=TRUE $array_name\n" ;
print INFILE "disk list\n" ;
print INFILE "disk show space\n" ;
print INFILE "close\n" ;
print INFILE "logfile end\n" ;
print INFILE "exit\n" ;

# Close the infile
close INFILE ;

#####################################################################
# Execution of afacli
#####################################################################

`$afacli < $infile` ;

#####################################################################
# Log File Analyse
#####################################################################

# Opening of outfile
my $outfile_test = open (OUTFILE, "$outfile") ;
unless ($outfile_test) {
	print "Can't open $outfile for reading\n" ;
	exit $code{'Unknown'};
}

# Init hash table
my %disks ;

# Set flags
my ( $flag_disk_list , $flag_disk_show_space ) = ( 0 , 0 ) ;

# Parsing outfile
while (<OUTFILE>) {
	$flag_disk_list = 1 if (/COMMAND: disk list/) ;
	$flag_disk_show_space = 1 if (/COMMAND: disk show space/) ;
	if ( $flag_disk_show_space ) {
		# Result of disk show space command
		if (/\d:\d\d:\d/) {
			# This line is a disk line !
			my @line = split ;
			my ($disk_nb) = ( $line[0] =~ /\d:(\d\d):\d/ ) ;
			my $disk_status = $line[1] ;
			# Add this disk to the hash table
			push @{$disks{$disk_nb}} , $disk_status ;
		}
	}
	elsif ( $flag_disk_list ) {
		# Result of disk list command
		if (/^\d:\d\d:\d\s*Disk/) {
			# This line is a disk line !
			my @line = split ;
			my ($disk_nb) = ( $line[0] =~ /\d:(\d\d):\d/ ) ;
			my $disk_status = $line[4] ;
			# Add this disk to the hash table
			push @{$disks{$disk_nb}} , $disk_status ;
		}
	}
}

# Close the outfile
close OUTFILE ;

#####################################################################
# File deletion
#####################################################################

unlink $infile, $outfile ;

#####################################################################
# Exit with Nagios Code
#####################################################################

# Find disks in error and print a message
# <disk_nb: error type>
# [disk_nb: Ok]
my $nb_disks_error = 0 ;
my %status ;

foreach my $nb (sort keys %disks) {
	my $reftable = $disks{$nb} ;
	$status{$nb} = 0 ;
	foreach (@$reftable) {
		# Good Status are : Initialized, Free, Container
		$status{$nb}++ unless (/Initialized|Free|Container/i) ;
	}
	if ( $status{$nb} ) {
		$nb_disks_error++ ;
		print "<$nb: " ;
		foreach (@$reftable) {
			print "$_ " unless (/Initialized|Free|Container/i) ;
		}
		print "> ";
	}
	else {
		print "[$nb: Ok] " ;
	}
}

print "\n" ;

exit $code{'Critical'} if ($nb_disks_error >= $critical_level) ;
exit $code{'Warning'} if ($nb_disks_error >= $warning_level) ;
exit $code{'Ok'} ;
