#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software;  you can redistribute it and/or modify it
# under the  terms of the  GNU General Public License  as published by
# the Free Software Foundation in version 2.  check_mk is  distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
# out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
# PARTICULAR PURPOSE. See the  GNU General Public License for more de-
# tails. You should have  received  a copy of the  GNU  General Public
# License along with GNU Make; see the file  COPYING.  If  not,  write
# to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
# Boston, MA 02110-1301 USA.
#
# This check created by Shawn McKee / University of Michigan
#   September 13, 2020
#
# This check processes the cvmfs_config.py agent plugin data and
# produces one check result per repo reported upon.
#
# Example output:
#<<<cvmfs_stats>>>
#Repo atlas-nightlies.cern.ch
#    UPTIME(M)=80
#    NOFDUSE=0
#    SPEED(K/S)=116
#    NOFDMAX=65024
#    NOOPEN=0
#    PID=141250
#    CACHEUSE(K)=12964063
#    PROXY=http://192.41.230.185:3128
#    HOST=http://cvmfs-s1bnl.opensciencegrid.org:8000/cvmfs/atlas-nightlies.cern.ch
#    VERSION=2.7.0.0
#    RX(K)=12
#    NOIOERR=0
#    CACHEMAX(K)=21013505
#    EXPIRES(M)=3
#    NOCATALOGS=1
#    ONLINE=1
#    MEM(K)=22260
#    HITRATE(%)=n/a
#    REVISION=37150
#Repo ...
#
 
# DEBUG factory_settings = {}
factory_settings["cvmfs_stats_default_levels"] = {
        "HITRATE_lower":     (50,25),       
        "NOIOERR":           (1,None),              
        "NOOPEN":            (10000,65000),          
        "SPEED_lower":       (0,-1),         
        "CACHERATIO":        (80.,90.)
}

def parse_cvmfs_stats(info):
        parsed = {}
	repo = ""
# parsed will be a dictionary of dictionaries  parsed[repo]=stats
        lines = iter(info)
        try: 
		while True:
			line = lines.next()
        	        if line[0] == "Repo":
				if repo != "":
# DEBUG					print(" Setting parsed to stats for %s" % repo)
					stats["CACHERATIO_units"] = "%"
					stats["CACHERATIO_scale"] = 1
					if "CACHEMAX" not in stats:
						print(" Missing CACHEMAX for %s" % repo)

					try:
						cmax = savefloat(stats["CACHEMAX"])
#						print(" cmax = %f" % cmax)
					except KeyError:
						print(" KeyError on CACHEMAX for %s" % repo)
						cmax = 0.0		
		
					if "CACHEUSE" not in stats:
						print(" Missing CACHEUSE for %s" % repo)

					try:
						cuse = savefloat(stats["CACHEUSE"])
#						print(" cuse = %f" % cuse)
					except KeyError:
						print(" KeyError on CACHEUSE for %s" % repo)
						cuse = 0.0				

					if cmax != 0.0:
						ratio = 100*cuse/cmax
					else:
						ratio = 0.0
#					print("ratio %f" % ratio)
					stats["CACHERATIO"] = str(ratio)	
					parsed[repo]=stats
				repo = line[1]
#				print(" repo11 %s" % repo)
				stats = {}
				continue
			else:
				kv = line[0].split("=", 1)
#				print(" kv[0] %s" % kv[0])
				if "(" in kv[0]:
					keyunits = kv[0].lstrip().rstrip(")").split("(", 1)
					key = keyunits[0].strip()
					keyunits = keyunits[1]
					if keyunits == "M":
						units = " s"
						scale = 60
					elif keyunits == "K/S":
						units = " B/s"
						scale = 1024
					elif keyunits == "K":
						units = " Bytes"
						scale = 1024
					elif keyunits == "%":
						units = "%"
						scale = 1
					else:
						scale = 1
					stats[key+"_units"] = units
					stats[key+"_scale"] = scale
				else:
					key = kv[0].strip()
				value = kv[1].strip()
				stats[key] = value.replace("http://", "")

	except StopIteration:
		pass

	if repo != "":
		stats["CACHERATIO_units"] = "%"
		stats["CACHERATIO_scale"] = 1

		if "CACHEMAX" not in stats:
			print(" Missing CACHEMAX for %s" % repo)

		try:
			cmax = savefloat(stats["CACHEMAX"])
#			print(" cmax = %f" % cmax)
		except KeyError:
			print(" KeyError on CACHEMAX for %s" % repo)
			cmax = 0.0				

		if "CACHEUSE" not in stats:
			print(" Missing CACHEUSE for %s" % repo)

		try:
			cuse = savefloat(stats["CACHEUSE"])
#			print(" cuse = %f" % cuse)
		except KeyError:
			print(" KeyError on CACHEUSE for %s" % repo)
			cuse = 0.0				

		if cmax != 0:
			ratio = 100*cuse/cmax
		else:
			ratio = 0.0
#		print("ratio %f" % ratio)
		stats["CACHERATIO"] = str(ratio)
		parsed[repo]=stats


        return parsed


def inventory_cvmfs_stats(parsed):
    return  [ (repo, {}) for repo in parsed.iterkeys()]

# the check function
def check_cvmfs_stats(repo, params, parsed):
#    print(" check repo parsed[repo]: %s %s " % (repo, parsed[repo]))
    if repo not in parsed:
	message = "WARN - Repo: %s is missing" % repo
	status = 2
    else:
	status = 0
	stats = parsed[repo]
	perfdata = []
	message = "Repo: %s, Version: %s," % (repo, str(stats.get("VERSION")))

	for field, level in [
		("HITRATE", "HITRATE_lower"),
		("NOIOERR", "NOIOERR"),
		("NOOPEN", "NOOPEN"),
                ("CACHERATIO", "CACHERATIO"),
		("SPEED", "SPEED_lower")]:
			warn, crit = params[level]
				
			if field in stats.keys():
				v = stats[field]

				try:
					scale = stats[field+"_scale"]
				except KeyError:
					scale = 1

				try:
					units = str(stats[field+"_units"])
				except KeyError:
					units = ""

#				print(" field: %s, units: %s" % (field,units))
				if v == "n/a":
					value = None
				else:
					try:
						value = savefloat(v)*scale
# DEBUG						value = float(v)*scale
					except:
						value = saveint(v)*scale
# DEBUG						value = int(v)*scale


				if "lower" in level:
					if value != None and crit != None and value <= crit:
						message += " %s: %d!!%s," % (field, value, units)
						status = max(2, status)
					elif value != None and warn != None and value <= warn:
						message += " %s: %d!%s," % (field, value, units)
						status = max(1, status)
					else:
						if value != None:
							message += " %s: %d%s," % (field, value, units)
						else:
							message += " %s: N/A%s," % (field, units)
					if isinstance(value, int):
						perfdata.append((field, "%d" % value, '', '', warn, crit))
					elif isinstance(value, float):
						perfdata.append((field, "%f" % value, '', '', warn, crit))
				else:

					if value != None and crit != None and value >= crit:
						message += " %s: %d!!%s," % (field, value, units)
						status = max(2, status)
					elif value != None and warn != None and value >= warn:
						message += " %s: %d!%s," % (field, value, units)
						status = max(1, status)
					else:
						if value != None:
							message += " %s: %d%s," % (field, value, units)
						else:
							message += " %s: N/A%s," % (field, units)


# Syntax for perfdata:   valuename value warn_lower warn_upper crit_lower crit_upper
					if isinstance(value, int):
						perfdata.append((field, "%d" % value, warn, crit))
					elif isinstance(value, float):
						perfdata.append((field, "%f" % value, warn, crit))
			else:
				message += " %s missing/malformed in puppet status," % field
				status = max(3, status)
# Construct long output component by component
	long = ""
	message += "\n"
	for field, fstring in [
		("HOST", "Host: '%s'%s, "),
		("PROXY", "Proxy: '%s'%s, "),
		("REVISION", "Revision: %s%s, "),
		("NOCATALOGS", "CatalogCnt: %s%s, "),
		("UPTIME", "Uptime: %s%s, "),
		("EXPIRES", "Expires: %s%s, "),
		("MEM", "Mem: %s%s, "),
		("CACHEUSE", "Cache used: %s%s, "),
		("CACHEMAX", "Cache max: %s%s, "),
		("NOFDUSE", "Filedesc used: %s%s, "),
		("NOFDMAX", "Filedesc max: %s%s, "),
		("RX", "RX: %s%s, "),
		("ONLINE", "Online: %s%s, "),
		("PID", "PID: %s%s, "),
		]:
		if field+"_units" in stats:
			units = stats[field+"_units"]
		else:
			units = ""

		if field in stats:
			message += fstring % (stats[field],units) + "\n"

		message += long.rstrip(",")

	return (status, message, perfdata)


# declare the check to Check_MK
# DEBUG check_info = {}
check_info["cvmfs_stats"] = {
    'parse_function': parse_cvmfs_stats,
    'check_function': check_cvmfs_stats,
    'inventory_function': inventory_cvmfs_stats,
    'service_description': 'CVMFS Stats %s',
    'default_levels_variable': 'cvmfs_stats_default_levels',
    'has_perfdata': True,
}

# DEBUG from collections import OrderedDict
#
# DEBUG with open('cvmfs_config.data') as f:
# DEBUG	info = f.readlines()
# DEBUG	parsed = parse_cvmfs_stats(info)
# DEBUG	for keys,ds in parsed.items():
# DEBUG		print(" Repo %s" % keys)
# DEBUG		dso = OrderedDict(sorted(ds.items()))
# DEBUG		for k, v in dso.items():
# DEBUG			print("    {} = {}".format(k,v))
#
# DEBUG	inv = inventory_cvmfs_stats(parsed)
# DEBUG	print(" inv: " + str(inv))
# DEBUG	for item, params in inv:
#		print("Checking repo %s" % item)
# DEBUG		params = factory_settings['cvmfs_stats_default_levels']
# DEBUG		rcode,rtext,perfdata = check_cvmfs_stats(item, params, parsed)
# DEBUG        	print("%d - %s" % (rcode,rtext))
# DEBUG	        print("perfdata: %s\n" %str(perfdata))
	
