#!/usr/bin/perl


## imports
use strict;
use Switch;
use Nagios::Plugin;

## init
# setup NP
my $np = Nagios::Plugin->new ( 
	shortname => "ZPOOL",
	version => 1.1,
	usage => "Usage: %s -p <zpool>",
	blurb => "checks a zpool's status"
);

$np->add_arg(
	spec => 'pool|p=s',
	help => '-p, --pool=<zpool>',
	required => 1
);

# main
$np->getopts;

#open(my $zpool, '-|', 'zpool status ' . $np->opts->pool . ' | awk \'{print $1,$2}\'') or 
open(my $zpool, '-|', 'zpool status ' . $np->opts->pool) or 
	$np->nagios_exit( UNKNOWN, "Error executing zpool");

my $pool_state, my $disks = {}, my $has_config = 0;
while(<$zpool>) {
	chomp;

	if (!$has_config) {
		my @line = split(/:/);
		$line[0] =~ s/^\s+//;
		$line[1] =~ s/^\s+//;

		if ($line[0] eq "state") {
			$pool_state = $line[1];
		}

		if ($line[0] eq "config") {
			$has_config = 1;
		}
	} else {
		if ($_ ne "") {
			if (/^\s{3}/) {
				$_ =~ s/^\s+//;
				my @disk = split(/\s+/);
				if (!/^(raidz|mirror)/) {
					if (!exists $disks->{$disk[1]}) {
						$disks->{$disk[1]} = ();
					}
					push(@{$disks->{$disk[1]}}, $disk[0]);
				}
			}
		}
	}
}

close($zpool);

# plugin logic
my $status, my $message;
switch ($pool_state) {
	case "ONLINE"      { $status = OK; }
	case "OFFLINE"     { $status = CRITICAL; }
	case "FAULTED"     { $status = CRITICAL; }
	case "DEGRADED"    { $status = WARNING; }
	case "UNAVAILABLE" { $status = CRITICAL; }
	else               { $status = UNKNOWN; }
}

my $drive_status = "";
foreach my $key (keys %$disks) {
	if ($key ne 'ONLINE' and $key ne 'AVAIL') {
		$drive_status .= sprintf("%s=%s ", $key, join(',', @{$disks->{$key}}));
	}
}

switch ($pool_state) {
	case "ONLINE"      { $message = sprintf("%s is %s.", $np->opts->pool, $pool_state); }
	case "OFFLINE"     { $message = sprintf("%s is %s.", $np->opts->pool, $pool_state); }
	case "FAULTED"     { $message = sprintf("%s is %s, insufficient replicas to keep functioning. %s", $np->opts->pool, $pool_state, $drive_status); }
	case "DEGRADED"    { $message = sprintf("%s is %s, sufficiant replicas excist to keep functioning. %s", $np->opts->pool, $pool_state, $drive_status); }
	case "UNAVAILABLE" { $message = sprintf("%s is %s, unable to open zpool. %s", $np->opts->pool, $pool_state, $drive_status); }
	else               { $message = sprintf("%s is in an unknown state: %s!", $np->opts->pool, $pool_state); }
}

# perf data
my $online = 0; 
if (exists $disks->{'ONLINE'}) {
	$online = 0 + @{$disks->{'ONLINE'}};
}

my $offline = 0; 
if (exists $disks->{'OFFLINE'}) {
	$offline = 0 + @{$disks->{'OFFLINE'}};
}

my $removed = 0; 
if (exists $disks->{'REMOVED'}) {
	$removed = 0 + @{$disks->{'REMOVED'}};
}

my $unavail = 0; 
if (exists $disks->{'UNAVAIL'}) {
	$unavail = 0 + @{$disks->{'UNAVAIL'}};
}

my $faulted = 0; 
if (exists $disks->{'FAULTED'}) {
	$faulted = 0 + @{$disks->{'FAULTED'}};
}

my $degraded = 0; 
if (exists $disks->{'DEGRADED'}) {
	$degraded = 0 + @{$disks->{'DEGRADED'}};
}

my $avail = 0; 
if (exists $disks->{'AVAIL'}) {
	$avail = 0 + @{$disks->{'AVAIL'}};
}

$np->add_perfdata( label => "online", value => $online );
$np->add_perfdata( label => "offline", value => $offline );
$np->add_perfdata( label => "removed", value => $removed );
$np->add_perfdata( label => "available", value => $avail );
$np->add_perfdata( label => "unavailable", value => $unavail );
$np->add_perfdata( label => "faulted", value => $faulted );
$np->add_perfdata( label => "degraded", value => $degraded );

# return result
$np->nagios_exit( $status, $message);

## TODO
# handle resilvering/scrubbing
#zpool list -o name,health,size
