#!/usr/bin/python
#
# Copyright (C) 2009 Andreas Thienemann <andreas@bawue.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Library General Public License as published by
# the Free Software Foundation; version 2 only
#
# 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 Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#

#
# Nagios script to query a munin host for plugin values
#
# Can be used as an active check instead of check_dummy
#

import optparse
import socket
import pprint
import sys
import re

parser = optparse.OptionParser("usage: %prog -H <Host> -M <Module> [-P <Port>] -D [<warn>] [<crit>]")
parser.add_option("-H", "--host", dest="host", type="string",
			help="specify host to poll")
parser.add_option("-M", "--module", dest="module", type="string",
			help="munin module to poll")
parser.add_option("-P", "--port", dest="port", default=4949,
			type="int", help="port number to poll")
parser.add_option("-D", "--debug", action="store_true", dest="debug", default=False,
			help="Debug output")

(options, args) = parser.parse_args()

HOST = options.host
PORT = options.port
MODULE = options.module
DEBUG = options.debug

if HOST == None or MODULE == None:
	parser.error("options -H and -M are required.")

def compare(val, thresh):
	# Compare value to warning and critical threshoulds
	# Handle different threshold formats: max, :max, min:, min:max

	val = float(val)

	# max
	match = re.match("^[:]?([-+]?[0-9]+)$", str(thresh))
	if match:
		max = float(match.group(1))
		if val > max:
			return 3


	# min
	match = re.match("^([-+]?[0-9]+):$", str(thresh))
	if match:
		min = float(match.group(1))
		if val < min:
			return 2

	# min:max
	match = re.match("^([-+]?[0-9]+):([-+]?[0-9]+)$", str(thresh))
	if match:
		min, max = float(match.group(1)), float(match.group(2))
		if val <  min or val > max:
			return 1

	# okay
	return 0

def output(l, cat, desc, ret):
	if len(l[cat]) > 0:
		print MODULE, desc + ";"
		for line in l["critical"]:
			print "CRITICAL: " + line + ";"
		for line in l["warning"]:
			print "WARNING: " + line + ";"
		for line in l["ok"]:
			print "OK: " + line + ";"
		sys.exit(ret)

try:
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect((HOST, PORT))
	conn = s.makefile('wb', 0)
except:
	print "Couldn't connect to requested host"
	sys.exit(3)


if conn.readline().startswith("# munin node at"):
	conn.writelines("config" + MODULE + "\n")
	order = []
	data = {}
	while True:
		line = conn.readline()
		if DEBUG:
			pprint.pprint(line)
		# Last message, bail
		if line == ".\n":
			break

		label = ""

		key, val = line.split(" ", 1)
		if key.find(".") is not -1:
			label = key.split(".")[0]
			if label not in data:
				data[label] = { "warning" : "", "critical" : "", "value" : "" }
				order.append(label)
				# No thresholds passed on the command line
				if len(args) == 2:
					data[label]["warning"] = args[0]
					data[label]["critical"] = args[1]

		# No thresholds passed on the command line, take the munin supplied ones
		if len(args) < 2:
			if key.endswith("warning"):
				data[label]["warning"] = val[:-1]
			if key.endswith("critical"):
				data[label]["critical"] = val[:-1]

	if data[label]["warning"] == "" or data[label]["critical"] == "":
		print "UNKNOWN - Couldn't retrieve thresholds, pass some on the command line"
		sys.exit(3)


	conn.writelines("fetch " + MODULE + "\n")
	while True:
		line = conn.readline()
		# Last line, bail
		if line == ".\n":
			if DEBUG:
				pprint.pprint(data)
			break

		key, val = line.split(" ", 1)
		label = key.split(".")[0]
		if key.endswith("value"):
			data[label]["value"] = val[:-1]

	conn.writelines("quit\n")

else:
	print "UNKNOWN - No munin node detected"
	sys.exit(3)

conn.close()
s.close()

l = { "ok" : [], "warning" : [], "critical" : [] }
for entry in order:
	# compare actual data: 3 max exceeded, 2 minimum underrun, 1 outside limit, 0 okay
	for tresh in ["critical", "warning"]:
		val = data[entry]["value"]
		tval = data[entry][tresh]
		tmp = ""
		if compare(val, tval) == 3:
			tmp = entry + ": " + val + " has exceeded the maximum threshold of " + tval
			break
		elif compare(val, tval) == 2:
			tmp = entry + ": " + val + " has underrun the minimum threshold of " + tval
			break
		elif compare(val, tval) == 1:
			tmp = entry + ": " + val + " is outside of range " + tval
			break

	if tmp != "":
		l[tresh].append(tmp)
	else:
		l["ok"].append(entry + ": " + val + " is okay")


output(l, "critical", "CRITICAL", 2)
output(l, "warning", "WARNING", 1)
output(l, "ok", "OK", 0)
