File check_dothill of Package monitoring-plugins-dothill

#!/usr/bin/perl
# nagios: -epn
#
# check_dothill - nagios plugin
#
# Copyright (C) 2012, SUSE Linux Products GmbH
# Author: Lars Vogdt <lars@linux-schulserver.de>
#
# All rights reserved.
#   
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#   
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#   
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#   
# * Neither the name of the Novell nor the names of its contributors may be
#   used to endorse or promote products derived from this software without
#   specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#

use strict;
use warnings;
use Getopt::Long;
use Net::Telnet;
use Pod::Usage;

our $conf={
    VERSION  => '0.1.0',
    PROGNAME => 'check_dothill',
    config   => "/etc/check_dothill.conf",
    debug    => 0,
    logfile  => '/var/log/check_dothill.log',
    loglevel => 1,
    host     => 'localhost',
    username => 'manage',
    password => 'manage',
    timeout  => 60,
    no_sensors => 0,
    no_controllers=>0,
    no_vdisks=>0,
    no_ports=>0,
    no_disks=>0, 
};
our %ERRORS = (
    'OK'        => 0,
    'WARNING'   => 1,
    'CRITICAL'  => 2,
    'UNKNOWN'   => 3,
    'DEPENDENT' => 4,
);
my $print_help = 0;
my $exitcode   = 0;

#######################################################################
## Function Area
#######################################################################

sub unknown_option {
	print "Problem with options, try to -h to get full help about all options \n";
	exit "$ERRORS{'UNKNOWN'}";
}

sub check_vdisks($){
	my ($conf)=@_;
	my $output='';
 	my $perfdata='';
	my $exitcode='OK';
	my $vdisk_count=0;
	#
	# LOGIN
	# dump_log($conf->{'logfile'})
	my $t = new Net::Telnet (	-Timeout => $conf->{'timeout'},
					-Errmode => 'return',
					-Prompt  =>  '/\# /') or return("Could not open socket",'CRITICAL');
	my $ret = $t->open($conf->{'host'}) or return("Could not connect to $conf->{'host'}",'CRITICAL');
	if($conf->{'debug'}){
		$t->dump_log($conf->{'logfile'});
		print STDERR "Connected - waiting for password prompt\n";
	}
	$ret = $t->login($conf->{'username'}, $conf->{'password'}) or return("Could not login to $conf->{'host'}",'CRITICAL');
	if($conf->{'debug'}){
		$t->dump_log($conf->{'logfile'});
		print STDERR "Logged in - waiting for prompt\n";
	}
	#
	# SHOW VDISKS
	#
	my @lines=$t->cmd("show vdisks\n");

	if($conf->{'debug'}){
		$t->dump_log($conf->{'logfile'});
		print STDERR "Showing vdisks\n";
	}
	foreach (@lines){
		if($conf->{'debug'}){
			print STDERR "$_\n";
		}
		next if (/^ /);
		next if (/^--/);
		next if (/^Success:/);
		next if (/^\s*$/);
		next if (/^Name /);
		my ($name,$size,$free,$Own,$pref,$raid,$disks,$spr,$chk,$status,$jobs,$jobar,$serial,$spin_down,$spin_down_delay)=split(/\s+/,$_);
		if ($_ =~ /Crit/){
			$output.="vdisk error on $name; ";
			$exitcode='CRITICAL';
		}
		$vdisk_count++;
		$perfdata.="'$name'=$size;$free; ";
	}
	#
	# EXIT
	#
	$output.="$vdisk_count vdisks defined; ";
        $t->cmd("exit");	
	$t->close;
	return("$output","$exitcode","$perfdata");
}

sub check_controllers($){
        my ($conf)=@_;
        my $output='';
        my $exitcode='OK';
        #
        # LOGIN
        #
        my $t = new Net::Telnet (       -Timeout => $conf->{'timeout'},
                                        -Errmode => 'return',
                                        -Prompt  =>  '/\# /') or return("Could not open socket",'CRITICAL');
        my $ret = $t->open($conf->{'host'}) or return("Could not connect to $conf->{'host'}",'CRITICAL');
        if($conf->{'debug'}){
                $t->dump_log($conf->{'logfile'});
                print STDERR "Connected - waiting for password prompt\n";
        }
        $ret = $t->login($conf->{'username'}, $conf->{'password'}) or return("Could not login to $conf->{'host'}",'CRITICAL');
        if($conf->{'debug'}){
                $t->dump_log($conf->{'logfile'});
                print STDERR "Logged in - waiting for prompt\n";
        }
        # 
        # SHOW CONTROLLERS
        #
        my @lines = $t->cmd("show controllers\n");
        if($conf->{'debug'}){
                $t->dump_log($conf->{'logfile'});
                print STDERR "Showing controllers\n";
        }
	my %controller_info;
	my $controller_id;
        foreach my $line (@lines){
                if($conf->{'debug'}){
                        print STDERR "LINE: $line\n";
                }
                next if ($line =~ /^ /);
		if ($line =~ /Controller ID: (.*)/){
			$controller_id=$1;
			$controller_info{$controller_id}->{'id'}="$controller_id";
		}
		elsif ($line =~ /Serial Number: (.*)/){
			$controller_info{$controller_id}->{'serial'}=$1;			
		}
		elsif ($line =~ /Status: (.*)/){
			$controller_info{$controller_id}->{'status'}=$1;
		}
		elsif ($line =~ /Failed Over: (.*)/){
			$controller_info{$controller_id}->{'failed_over'}=$1;
		}
		elsif ($line =~ /Position: (.*)/){
			$controller_info{$controller_id}->{'position'}=$1;
		}
	}
	foreach my $id (sort(keys(%controller_info))){
		if ($controller_info{$id}->{'status'} !~ /Operational/){
			$output.="Controller $id (Serial: ".$controller_info{$id}->{'serial'}."; Position: ".$controller_info{$id}->{'position'}.") is in state ".$controller_info{$id}->{'status'}."; ";
			$exitcode='CRITICAL';
		}
		elsif ($controller_info{$id}->{'failed_over'} !~ /No/){
			$output.="Controller $id (Serial: ".$controller_info{$id}->{'serial'}."; Position: ".$controller_info{$id}->{'position'}.") state ".$controller_info{$id}->{'status'}." is running in fail over mode; ";
			$exitcode='CRITICAL';
		}
		else {
			$output.="Controller $id (Serial: ".$controller_info{$id}->{'serial'}."; Position: ".$controller_info{$id}->{'position'}.") state OK; ";
		}
	}
	return("$output","$exitcode");
}

sub check_disks($){
	my ($conf)=@_;
	my $output='OK';
	my $exitcode='OK';
#	my $disk_count=0;
# 	my $perfdata='';
#	my ($location,$serial,$vendor,$rev,$spare_type,$used_as,$type,$size,$rate,$health,$health_reason)='';
	#
	# LOGIN
	#
	my $t = new Net::Telnet (	-Timeout => $conf->{'timeout'},
					-Errmode => 'return',
					-Prompt  =>  '/\# /') or return("Could not open socket",'CRITICAL');
	my $ret = $t->open($conf->{'host'}) or return("Could not connect to $conf->{'host'}",'CRITICAL');
	if($conf->{'debug'}){
		$t->dump_log($conf->{'logfile'});
		print STDERR "Connected - waiting for password prompt\n";
	}
	$ret = $t->login($conf->{'username'}, $conf->{'password'}) or return("Could not login to $conf->{'host'}",'CRITICAL');
	if($conf->{'debug'}){
		$t->dump_log($conf->{'logfile'});
		print STDERR "Logged in - waiting for prompt\n";
	}
	# 
	# SHOW DISKS
	#
	my @lines = $t->cmd("show disks\n");
	if($conf->{'debug'}){
		$t->dump_log($conf->{'logfile'});
		print STDERR "Showing disks\n";
	}

	foreach my $line (@lines){
		if($conf->{'debug'}){
			print STDERR "LINE: $line\n";
		}
		next if ($line =~ /^ /);
                next if ($line =~ /^--/);
                next if ($line =~ /^Success:/);
                next if ($line =~ /^Info:/);
		next if ($line =~ /^\s*Size/);
                next if ($line =~ /^\s*$/);
		next if ($line =~ /^Location/);
		if ($line !~ /OK/){
			$output.="disk critical: $line";
			$exitcode='CRITICAL';
		}
	}
	#
	# EXIT
	#
        $t->cmd("exit");	
	$t->close;
	return("$output","$exitcode");
}

sub check_ports($){
	my ($conf)=@_;
	my $output='OK';
	my $exitcode='OK';
	my $portcount=0;
	#
	# LOGIN
	#
	my $t = new Net::Telnet (	-Timeout => $conf->{'timeout'},
					-Errmode => 'return',
					-Prompt  =>  '/\# /') or return("Could not open socket",'CRITICAL');
	my $ret = $t->open($conf->{'host'}) or return("Could not connect to $conf->{'host'}",'CRITICAL');
	if($conf->{'debug'}){
		$t->dump_log($conf->{'logfile'});
		print STDERR "Connected - waiting for password prompt\n";
	}
	$ret = $t->login($conf->{'username'}, $conf->{'password'}) or return("Could not login to $conf->{'host'}",'CRITICAL');
	if($conf->{'debug'}){
		$t->dump_log($conf->{'logfile'});
		print STDERR "Logged in - waiting for prompt\n";
	}
	#
	# SHOW PORTS
	#
	my @ports=$t->cmd("show ports\n");
	if($conf->{'debug'}){
		$t->dump_log($conf->{'logfile'});
		print STDERR "Showing ports\n";
	}
	foreach (@ports){
		if($conf->{'debug'}){
			print STDERR "$_\n";
		}
		next if (/^ /);
                next if (/^--/);
                next if (/^Success:/);
                next if (/^\s*$/);
                next if (/^\sPorts/);
		my ($port,$media,$targetid,$status,$speeda,$speedc)=split(/\s+/,$_);
		if ($_ =~ /Disconnected/){
			$output.="Port disconnected: $port; ";
			$exitcode='CRITICAL';	
		}
		elsif ($_ =~ /Up/){
			$portcount++;
		}
	}
	$output="$portcount Ports are in state 'Up'; ";
	#
	# EXIT
	#
        $t->cmd("exit");	
	$t->close;
	return("$output","$exitcode");
}

sub trim($){
	my ($string)=@_;
	$string =~ s/^\s+//;
	$string =~ s/\s+$//;
	return $string;
}

sub check_sensors($){
        my ($conf)=@_;
        my $output='';
	my $master_output='';
        my $exitcode='OK';
        my $perfdata='';
        #
        # LOGIN
        #
        my $t = new Net::Telnet (       -Timeout => $conf->{'timeout'},
                                        -Errmode => 'return',
                                        -Prompt  =>  '/\# /') or return("Could not open socket",'CRITICAL');
        my $ret = $t->open($conf->{'host'}) or return("Could not connect to $conf->{'host'}",'CRITICAL');
        if($conf->{'debug'}){
                $t->dump_log($conf->{'logfile'});
                print STDERR "Connected - waiting for password prompt\n";
        }
        $ret = $t->login($conf->{'username'}, $conf->{'password'}) or return("Could not login to $conf->{'host'}",'CRITICAL');
        if($conf->{'debug'}){
                $t->dump_log($conf->{'logfile'});
                print STDERR "Logged in - waiting for prompt\n";
        }
        # 
        # SHOW SENSORS
        #
        my @lines = $t->cmd("show sensor-status\n");
        if($conf->{'debug'}){
                $t->dump_log($conf->{'logfile'});
                print STDERR "Showing sensor-status\n";
        }
        foreach my $line (@lines){
                if($conf->{'debug'}){
                        print STDERR "LINE: $line\n";
                }
                next if ($line =~ /^ /);
                next if ($line =~ /^--/);
                next if ($line =~ /^Success/);
                next if ($line =~ /^Sensor/);
		if ($line =~ /^Temperature Loc: (.*)\s+(\d+) C\s+(.*)/){
			my $loc=trim($1);
			my $state=trim($3);
			my $temp=trim($2);
			$output.="Temp $loc $state: $temp C; ";
			$perfdata.="'temp $loc'=$temp; ";
		}
		elsif ($line =~ /On-Board Temperature (.*)\s+(\d+) C\s+(.*)/){
                        my $loc=trim($1);
                        my $state=trim($3);
                        my $temp=trim($2);
                        $output.="On-Board Temp $loc $state: $temp C; ";
                        $perfdata.="'temp $loc'=$temp; ";

		}
		elsif ($line =~ /Disk Controller (.*)\s+(\d+) C\s+(.*)/){
                        my $loc=trim($1);
                        my $state=trim($3);
                        my $temp=trim($2);
                        $output.="Disk Controller Temp $loc $state: $temp C; ";
                        $perfdata.="'disk contr. $loc'=$temp; ";
		}
		elsif ($line =~ /Memory Controller (.*)\s+(\d+) C\s+(.*)/){
                        my $loc=trim($1);
                        my $state=trim($3);
                        my $temp=trim($2);
                        $output.="Memory Controller Temp $loc $state: $temp C; ";
                        $perfdata.="'mem contr. $loc'=$temp; ";
		}
		# Overall Unit Status               OK       OK
		elsif ($line =~ /Overall Unit Status\s+(\w+)\s+(\w+)/){
			my $state=trim($1);
			if ($state !~ /OK/){
				$exitcode='CRITICAL';
			}
			$master_output="Overall Unit Status $state;";
		}
	}
	$output="$master_output $output";
	#
	# EXIT
	#
    $t->cmd("exit");	
	$t->close;
	return("$output","$exitcode", "$perfdata");
}


#######################################################################
## Main Area
#######################################################################

my $number_of_options = @ARGV;

pod2usage (  -exitstatus => 0,
	-verbose => 2,  # 2 to print full pod
        ) if $number_of_options == 0;

Getopt::Long::Configure('bundling');
GetOptions( "h|help"         => \$print_help,
            "c|config=s"     => \$conf->{'config'},
            "d|debug"        => \$conf->{'debug'},
            "logfile=s"      => \$conf->{'logfile'},
            "loglevel=i"     => \$conf->{'loglevel'},
            "t|timeout=i"    => \$conf->{'timeout'},
            "u|username=s"   => \$conf->{'username'},
            "p|password=s"   => \$conf->{'password'},
            "H|host=s"       => \$conf->{'host'},
            "S|no-sensors"      => \$conf->{'no_sensors'},
            "C|no-controllers"  => \$conf->{'no_controllers'},
            "V|no-vdisks"	=> \$conf->{'no_vdisks'},
            "P|no-ports"	=> \$conf->{'no_ports'},
            "D|no-disks"	=> \$conf->{'no_disks'},
) or (unknown_option);	

pod2usage (  -exitstatus => 0,
	-verbose => 2,  # 2 to print full pod
        ) if $print_help;

if ($conf->{'debug'}){
    use Data::Dumper;
    print STDERR "Config:\n".Data::Dumper->Dump([$conf])."\n";
}

# Just in case of problems, let's not hang Nagios
$SIG{'ALRM'} = sub {
    print "UNKNOWN - Plugin timed out\n";
    exit $ERRORS{'UNKNOWN'};
};
alarm($conf->{'timeout'});

my $exit='OK';
my $output='';
my $perfdata='';

if (! $conf->{'no_sensors'}){
	# sensor output
	my ($sensor_output,$sensor_exit,$sensor_perfdata)=check_sensors($conf);
	if ($sensor_exit !~ /OK/){
		$exit=$sensor_exit;
	}
	$output.=$sensor_output;
	$perfdata.=$sensor_perfdata;
}
if (! $conf->{'no_controllers'}){
	# check controllers
	my ($controller_output,$controller_exit)=check_controllers($conf);
	if ($controller_exit !~ /OK/){
		$exit=$controller_exit;
	}
	$output.=$controller_output;
}
if (! $conf->{'no_vdisks'}){
	# check vdisks
	my ($vdisk_output,$vdisk_exit,$vdisk_perfdata)=check_vdisks($conf);
	if ($vdisk_exit !~ /OK/){
		$exit=$vdisk_exit;
	}
	$output.=$vdisk_output;
}
if (! $conf->{'no_ports'}){
	# check ports
	my ($portoutput,$portexit)=check_ports($conf);
	if ($portexit !~ /OK/){
		$exit=$portexit;
	}
	$output.=$portoutput;
}
#if (! $conf->{'no_disks'}){
# disk output
#my ($disk_output,$disk_exit)=check_disks($conf);
#if ($disk_exit !~ /OK/){
#	$exit=$disk_exit;
#}
#$output.=$disk_output;
#}

print "$output | $perfdata\n";

alarm(0);

exit $ERRORS{$exit}


__END__

=head1 Check Dothill RAID Array

Check Dot Hill Revolution Storage Arrays.

=head1 SYNOPSIS

./check_dothill -H <hostname> -u <username> -p <password>  <OPTIONS>

Options:

    -c <file>      | --config <file>
    -H <host>      | --host <host>
    -u <username>  | --username <username>
    -p <password>  | --password <password>

     -S            | --no-sensors
     -C            | --no-controllers
     -V            | --no-vdisks
     -P            | --no-ports
     -D            | --no-disks

                   | --logfile <file>
                   | --loglevel <int>

    -h             | --help
    -d             | --debug

=head1 OPTIONS

=over 8

=item B<--config> F<file>

Use configfile F<file> to get needed options.

=item B<--host> F<HOST>

The hostname or IP address.

=item B<--username> F<USERNAME>

The username to login to the console.

=item B<--password> F<PASSWORD>

The password to login to the console.

=item B<--no-sensors>

Disable sensor check.

=item B<--no-controllers>

Disable controller check.

=item B<--no-vdisks>

Disable virtual disk check.

=item B<--no-ports>

Disable port check.

=item B<--no-disks>

Disable phisical disk check.


=back

=head1 DESCRIPTION

Use check_dothill if you want to get some information about your Dot Hill
Revolution Storage Arrays. The pluing uses a Telnet connection and the given
username/password pair to login and execute some commands. Afterwards the
output is parsed, condensed and re-arranged for a Nagios Plugin compatible
output.

=head1 AUTHORS

=over 8

=item Lars Vogdt <lars@linux-schulserver.de> 2012

=back

=cut
openSUSE Build Service is sponsored by