#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2014             mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# 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.

# Example output from agent:
# 0 Service_FOO V=1 This Check is OK
# 1 Bar_Service - This is WARNING and has no performance data
# 2 NotGood V=120;50;100;0;1000 A critical check
# P Some_other_Service value1=10;30;50|value2=20;10:20;0:50;0;100 Result is computed from two values
# P This_is_OK foo=18;20;50
# P Some_yet_other_Service temp=40;30;50|humidity=28;50:100;0:50;0;100
# P Has-no-var - This has no variable
# P No-Text hirn=-8;-20


# Compute state according to warn/crit levels contained in the
# performance data.
def local_compute_state(perfdata):
    texts = []

    # 16MB -> 16.0
    def float_ignore_uom(value):
        while value:
            if value[-1] not in "0123456789.-":
                value = value[:-1]
            else:
                return float(value)
        return 0.0


    def outof_levels(value, levels):
        if levels == None:
            return

        if ':' in levels:
            lower, upper = map(float, levels.split(':'))
        else:
            lower = None
            upper = float(levels)
        if value > upper:
            return " %s > %s" % (value, upper)
        elif lower != None and value < lower:
            return " %s < %s" % (value, lower)

    worst = 0
    for entry in perfdata:
        if len(entry) < 3:
            continue # No levels attached
        varname = entry[0]
        value = float_ignore_uom(entry[1])
        warn = entry[2]
        if len(entry) >= 4:
            crit = entry[3]
        else:
            crit = None

        text = outof_levels(value, crit)
        if text:
            worst = 2
            text = "%s%s(!!)" % (varname, text)
            texts.append(text)

        else:
            text = outof_levels(value, warn)
            if text:
                worst = max(worst, 1)
                text = "%s%s(!)" % (varname, text)
                texts.append(text)

            else:
                texts.append("%s is %s(.)" % (varname, value))

    return worst, texts


def inventory_local(info):
    inventory = []
    # Lines with P do not need to supply a text
    for line in info:
        if len(line) >= 4 or len(line) == 3 and line[0] == 'P':
            inventory.append( (line[1], None) )
        else:
            raise MKGeneralException("Invalid line in agent section <<<local>>>: %s" % " ".join(line))
    return inventory


def check_local(item, params, info):
    for line in info:
        # Ignore invalid lines, tolerate bugs in local checks
        # of unexperienced users
        if len(line) >= 2 and line[1] == item:
            if not (len(line) >= 4 or (len(line) == 3 and line[0] == 'P')):
                return 3, "Incomplete line in local check output: %s" % " ".join(line)

            statechar = line[0]
            perftxt = line[2]

            output = " ".join(line[3:])
            # convert eventually escaped newline chars to real newlines
            # (will be converted back later individually for the different cores)
            output = output.replace("\\n", "\n")

            perfdata = []
            if perftxt != "-":
                # new: allow multiple perfdata by using | as separator
                for entry in perftxt.split('|'):
                    try:
                        varname, valuetxt = entry.split('=')
                        values = valuetxt.split(';')
                        perfdata.append(tuple( [varname] + values ))
                    except ValueError:
                        return 3, "Invalid performance data %s in local check output %s" % \
                                  (perftxt, " ".join(line))
            if statechar == 'P':
                state, texts = local_compute_state(perfdata)
                if output:
                    texts = [output] + texts
                output = ", ".join(texts)
            else:
                try:
                    state = int(statechar)
                except:
                    return 3, "Invalid state %s in local check output %s: must be P, 0, 1, 2 or 3" % \
                             (statechar, " ".join(line))

                if state not in range(0, 4):
                    output += ", local check has sent invalid state %d" % state
                    state = 3
            return (state, output, perfdata)


check_info["local"] = {
    'check_function':          check_local,
    'inventory_function':      inventory_local,
    'service_description':     '%s',
    'has_perfdata':            True,
}
