#!/usr/bin/perl
#
# check_thecus.pl
# - A nagios plugin to monitor a THECUS NAS applicance
#
# Usage:
#  check_thecus.pl [--user <username>] [--password <password>] [--running <Service1,Service2,...>] [--stopped <Service1,Service2,...>] <host>
# 
# where
#  <host>			is the network address of the appliance
#  --user <username>		specifies the username for authentication (default: admin)
#  --password <pass>		specifies the password for authentication (default: admin)
#  --running <Service,...>	specifies a list of service names which should be running
#  --stopped <Service,...>	specifies a list of service names which should not be running
# 
# Returns 
#  CRITICAL if 
#   - web interface cannot be accessed,
#   - drives have errors
#   - services are running which should not, or not running if they should
#   - fan is running at too slow, fast (< 1000, > 2000 rpm, TODO: make configurable)
#  WARNING if the CPU load is over 80%
#  OK otherwise.
#
# WARNING: As the applicance provides no real monitoring interface this plugin has to rely on
#  parsing the data out of the web-interface. That might cause errors when using different
#  firmware versions or even languages.
#
# Author: Moritz Bechler <mbechler@eenterphace.org> for Schmieder IT Solutions (http://www.schmieder.de)
# License: MIT License
#
# Copyright (c) 2010 Moritz Bechler
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#  
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
use strict;
use FindBin;
use lib "$FindBin::Bin";
use Getopt::Long;
use LWP;
use LWP::UserAgent;
use Data::Dumper;
use HTTP::Cookies;


my $ua = new LWP::UserAgent;
$ua->agent('check_thecus Nagios plugin');
#$ua->cookie_jar(new HTTP::Cookies(file => '/tmp/cookies', ignore_discard => 1));
$ua->cookie_jar( { } );


my $user = 'admin';
my $pass = 'admin';
my $running = '';
my $stopped = '';

GetOptions("user=s" => \$user, "password=s" => \$pass, "running=s" => \$running, "stopped=s" => \$stopped);

my $host = shift;

if(!$host) {
	die "Usage: $0 [--user <username>] [--password <password>] [--running <Service1,Service2,...>] [--stopped <Service1,Service2,...>] <host>\n";
}



my $res = $ua->post("http://$host/usr/login.html", [ 'username' => $user, 'pwd' => $pass ]);

if($res->is_error()) {
	print "CRITICAL: Thecus login failed (" . $res->status_line . ")\n";
	exit 2;
}

my $aboutRes = $ua->get("http://$host/adm/getform.html?name=about");

if(!$aboutRes->is_success()) {
	print "CRITICAL: Could not fetch status information (" . $aboutRes->status_line . ")\n";
	exit 2;
}

if(!$aboutRes =~ m/^\<html\>/s) {
	print "UNKNOWN: Could not log in to Thecus NAS";
	exit 3;
}


my $sysStatRes = $ua->get("http://$host/adm/getform.html?name=system");


if(!$sysStatRes->is_success()) {
	print "CRITICAL: Could not fetch status information (" . $sysStatRes->status_line . ")\n";
	exit 2;
}
my $sysStatStr = $sysStatRes->content();


$sysStatStr =~ s/^.*<form[^>]*id="sysform"[^>]*>(.*)<\/form>.*$/\1/s;
$sysStatStr =~ s/<tr>\s*<\/tr>//igs;
$sysStatStr =~ s/\s*<\/tr>\s*<tr[^>]*>\s*/#/igs;
$sysStatStr =~ s/\s*<\/(td|th)>\s*<(td|th)[^>]*>\s*/%%/igs;

$sysStatStr =~ s/\s*<(div|center|font|a|input|table|td|tr|th)[^>]*>\s*|\s*<\/(div|center|font|a|input|table|td|tr|th)>\s*//igs;
$sysStatStr =~ s/\s+/ /gs;

my @tmp = ();
my @lines = split "#", $sysStatStr;

for my $line (@lines) {
	my @linetmp = split "%%", $line;
	if(@linetmp == 2) {
		push @tmp, { key => $linetmp[0], value => $linetmp[1] };
	}
}

my $cpuLoad = int($tmp[0]{value});
my $fanRpm = int($tmp[1]{value});

my $diskStatRes = $ua->get("http://$host/adm/getform.html?name=disks");

if(!$diskStatRes->is_success()) {
	print "CRITICAL: Could not fetch disk status information (" . $diskStatRes->status_line . ")\n";
	exit 2;
}

my $diskStatStr =  $diskStatRes->content();


$diskStatStr =~ s/^.*<form[^>]*id="disksform"[^>]*>(.*)<\/form>.*$/\1/is;
$diskStatStr =~ s/<th[^>]*>(.*?)<\/th>//igs;
$diskStatStr =~ s/<tr>\s*<\/tr>//igs;
$diskStatStr =~ s/\s*<\/tr>\s*<tr[^>]*>\s*/#/igs;
$diskStatStr =~ s/\s*<\/td>\s*<td[^>]*>\s*/%%/igs;

$diskStatStr =~ s/\s*<(div|center|font|a|input|table|td|tr)[^>]*>\s*|\s*<\/(div|center|font|a|input|table|td|tr)>\s*//igs;
$diskStatStr =~ s/\s+/ /gs;

my @diskStatus = ();
my @diskInfo = split '#', $diskStatStr;

for my $diskStr (@diskInfo) {
	my @tmp = split '%%', $diskStr;

	if($tmp[0] =~ m/^\s*\d+\s*$/) {
		my $diskNum = int($tmp[0]);
		$diskStatus[$diskNum] = {
			size => $tmp[1],
			type => $tmp[2],
			fw => $tmp[3],
			status => $tmp[4]
		}
	}
}

my @errors = ();

for (my $disk = 1; $disk < scalar @diskStatus; $disk++) {
	if(@diskStatus[$disk]->{status} !~ m/OK/) {
		push @errors, "Disk $disk (" . @diskStatus[$disk]->{type} . ") has state: " . @diskStatus[$disk]->{status};
	}
}

if($running) {
	for my $service (split ",", $running) {
		my $found = 0;
		for my $toMatch (@tmp) {
			if($toMatch->{key} =~ m/$service/) {
				if($toMatch->{value} !~ m/Running/) {
					push @errors, "$service not running";
				}
				$found = 1;
			}
		}

		if(!$found) {
			push @errors, "$service not found";
		}
	}
}


if($stopped) {
	for my $service (split ",", $stopped) {
		my $found = 0;
		for my $toMatch (@tmp) {
			if($toMatch->{key} =~ m/$service/) {
				$found = 1;
				if($toMatch->{value} !~ m/Stopped/) {
					push @errors, "$service is running";
				}
			}
		}

		if(!$found) {
			push @errors, "$service not found";
		}
	}
}


if($fanRpm > 2000 || $fanRpm < 1000) {
	push @errors, "FAN is running at $fanRpm, should be between 1000 and 2000 RPM\n";
}


if(@errors > 0) {
	print "CRITICAL: Thecus NAS has errors:|cpuLoad=$cpuLoad,fanRpm=$fanRpm\n";
	for my $error (@errors) {
		print "$error\n";
	}
	exit 2;
} else {
	if($cpuLoad >= 80) {
		print "WARNING: High CPU Load|cpuLoad=$cpuLoad,fanRpm=$fanRpm\n";
		exit 1;
	}

	print "OK: Thecus NAS running normally|cpuLoad=$cpuLoad,fanRpm=$fanRpm\n";
	exit 0;
}
