#!/usr/bin/perl -w
# nagios: -epn
#
# schedule_downtime - nagios eventhandler
#
# Copyright (C) 2009, Novell, Inc.
# Author: Lars Vogdt
#
# 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.
#
# $Id$
#

use strict;
use warnings;
use lib "/usr/lib/nagios/plugins";
use utils qw{$TIMEOUT %ERRORS print_revision};
use Time::HiRes qw{time alarm};
use Getopt::Long;
use File::Basename;
use Date::Parse qw/str2time/;


our $nagcmd="/var/spool/nagios/nagios.cmd";

our $version="0.5";
our $help=0;
our $rev=0;
our $hgtype="";
our $sgtype="";
our $host="";
our $service="";
our $group="";
our $fixed=0;
our $duration=0;
our $start="";
our $end="";
our $author="";
our $comment="";
our $dryrun=0;
our $start_time="";
our $end_time="";
our $cmd="";

# nagios requires a 3 for unknown errors.
$SIG{__DIE__} = sub {
  print @_;
  exit 3;
};

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

sub print_usage {
    print "This eventhandler allows you to schedule any type of downtime as long as the user invocing the script has\n";
	print "access to write to the nagios command file.\n\n";
	print "Usage:   ".basename($0)." [-H <s|h>]|-S <s|h>] [-g group] [-h host] [-s service] <-f|-d duration> <-b begin> <-e end> <-a author> <-c comment> [-n]\n";
	print "Options: -H <s|h>      :    Schedule Downtime for \"h\"osts or \"s\"ervices in a specific HostGroup\n";
    print "         -S <s|h>      :    Schedule Downtime for \"h\"osts or \"s\"ervices in a specific ServiceGroup\n";
    print "         -g <group>    :    Host or Service Group associated with -H or -S request\n";
    print "         -h <host>     :    Schedule downtime for host, except when used with -s.  Cannot be used with -H or -S\n";
    print "         -s <service>  :    Schedule downtime for service, cannot be used with -H or -S.  May be \"all\" when used with -h.\n";
    print "         -f <duration> :    Set a fixed schedule, otherwise downtime runs for <duration> seconds\n";
    print "         -d <duration> :    Duration of downtime, in seconds.  Cannot be used with -f\n";
    print "         -b <begin>    :    Beginning of Downtime.\n";
    print "         -e <end>      :    End of Downtime.\n";
    print "         -a <author>   :    Author: Who is scheduling the downtime?\n";
    print "         -c <comment>  :    Comment: Reason for scheduling the downtime.\n";
    print "         -n            :    Dry run.  Don't actually schedule anything, but show what would have been scheduled.\n";
	print "         -p <file>     :    Full path to the nagios.cmd file (default: $nagcmd)\n";
    print "\n";
    print "                 -b and -e options can take any string recognized by Perl's Date::Parse function.\n";
    print "                 Example: \"21-dec 17:05\", \"Wed, 16 Jun 09 07:29:35 CST\", etc...\n";
}

sub print_help {
    my $exitcode=shift || 1;
    print "Copyright (c) 2009, Novell, Inc.\n\n";
    print_usage();
    print "\n";
    exit $exitcode;
}

sub print_error {
    my $error=shift || '';
    print STDERR "\nERROR: $error\n\n";
    &print_usage;
    exit $ERRORS{'UNKNOWN'};
}

Getopt::Long::Configure('bundling');
if(!GetOptions( 	'H|HostGroup=s' => \$hgtype,
					'S|ServiceGroup=s' => \$sgtype,
					'g|group=s' => \$group,
					'h|host=s' => \$host,
					's|service=s' => \$service,
					'f|fixed=s' => \$fixed,
					'd|duration=s' => \$duration,
					'b|begin=s' => \$start,
					'e|end=s' => \$end,
					'a|author=s' => \$author,
					'c|comment=s' => \$comment,
					'n|dryrun'   => \$dryrun,
                    'help'=> \$help,
					'v|version' => \$rev,
					't|timeout=i' => \$TIMEOUT,
					'p|nagcmd=s' => \$nagcmd,
	)){
	&print_help(1);
}
&print_help(0) if ($help);
if ($rev){
    &print_revision(basename($0),$version);
    exit $ERRORS{'UNKNOWN'};
}

# "Sanitize" the arguments...
print_error "$nagcmd does not exist or is unreachable" if (!-e "$nagcmd");
if (("$sgtype" ne "") && ("$hgtype" ne "")){
	print_error "-S and -H cannot be used together";
}
if (("$sgtype" ne "") || ("$hgtype" ne "")){
	print_error "-g must be passed when using -H or -S" if ("$group" ne "");
}
if (("$group" ne "") && (("$host" ne "") || ("$service" ne ""))){
	print_error "-g cannot be used with -h or -s, only -H or -S";
}
if (("$host" eq "") && ("$hgtype" eq "") && ("$sgtype" eq "")){
	print_error "Must specify at least one of -H, -S or -h";
}
if (("$hgtype" ne "") && ("$hgtype" !~ /^[sShH]+$/)){
	print_error "Invalid usage of -H. Must be followed by either \"h\" or \"s\" - not $hgtype.";
}
if (("$sgtype" ne "") && ("$sgtype" !~ /^[sShH]+$/)){
    print_error "Invalid usage of -S.  Must be followed by either \"h\" or \"s\" - not $sgtype";
}
if ( $fixed > 0 && $duration > 0 ){
	print_error "-f and -d cannot be used together";
}
if ($fixed == 0 && $duration == 0 ){
	print_error "Must specify one of -d or -f";
}
if ( "$start" eq "" ){
	print_error "Must specify a start time (-b)";
} else {
	if ("$start" =~ /[nN][oO][wW]/){
		$start=localtime();
	}
	if ( $start_time = str2time($start) <= 0){
		print_error "\"$start\" is not a valid starting time";
	}
}
if (( "$end" eq "" ) && (!$duration)){
	print_error "Must specify an end time";
} else {
	if (!$duration){
		if ( $end_time = str2time($end) <= 0){
			print_error "\"$end\" is not a valid ending time";
		}
	}
}
$duration = $end_time - $start_time if ( $fixed );
print_error "Must specify an event author (-a)" if ( "$author" eq "");
print_error "Must specify an event comment (-c)" if ( "$comment" eq "");

if ("$group" ne ""){
	if ("$hgtype" ne ""){
		$cmd="SCHEDULE_SERVICEGROUP_SVC_DOWNTIME" if ($sgtype =~ /[sS]+/);
		$cmd="SCHEDULE_SERVICEGROUP_HOST_DOWNTIME" if ($sgtype =~ /[hH]+/);
	}
	if ("$sgtype" ne ""){
		$cmd="SCHEDULE_SERVICEGROUP_SVC_DOWNTIME" if ($sgtype =~ /[sS]+/);
		$cmd="SCHEDULE_SERVICEGROUP_HOST_DOWNTIME" if ($sgtype =~ /[hH]+/);
	}
} else {
	if ("$host" ne ""){
		if ("$service" ne ""){
			if ($service =~ /^[aA][lL][lL]$/){
				$cmd="SCHEDULE_HOST_SVC_DOWNTIME";
			} else {
				$cmd="SCHEDULE_SVC_DOWNTIME";
			}
		} else {
			$cmd = "SCHEDULE_HOST_DOWNTIME";
		}
	}
}

our $exec="";
if ( $cmd =~ /GROUP/){
	$exec = "[".int(time())."] $cmd;$group;$start_time;$end_time;$fixed;0;$duration;$author;$comment\n";
} elsif ( $cmd =~ /^SCHEDULE_HOST/ ){
	$exec = "[".int(time())."] $cmd;$host;$start_time;$end_time;$fixed;0;$duration;$author;$comment\n";
} else {
	$exec = "[".int(time())."] $cmd;$host;$service;$start_time;$end_time;$fixed;0;$duration;$author;$comment\n";
}

if ($dryrun){
	print "Here is what WOULD have happened...\n\n$exec";
} else {
	open(NAGCMD,">>$nagcmd") or die "Could not open $nagcmd: $!\n";
	print NAGCMD "$exec";
	close(NAGCMD);
}
