#!/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.


# In order to stay compatible we have to build the items
# this complicated way :(


def cmciii_temp_get_item(container, device):
    device   = device.replace("Liquid_Cooling_Package", "LCP")
    itemname = container[1].replace("Temperature", "")
    if itemname == "":
        itemname = "Ambient"
    if device is not None:
        itemname += " %s" % device
    if container[-1].startswith("In-") or container[-1].startswith("Out-"):
        itemname += " %s" % container[-1].split("-")[0]
    return itemname


def cmciii_phase_get_item_and_key(container, device, var_type):
    itemname = [ device ]
    if container[1]:
        itemname.append(container[1])
    itemname += [ "Phase", container[2].replace("Phase", "")\
                                       .replace("L", "").strip() ]
    itemname  = " ".join(itemname)
    if var_type == "2":
        key = "_".join(container[3:]).lower()
        if key == "power_apparent":
            key = "appower"
        elif key.endswith("_active"):
            key = key.replace("_active", "")
    else:
        key = " ".join(container[3:])
    return itemname, key


def parse_cmciii(info):
    devices = {}
    parsed  = {
        "state"       : {},
        "psm_current" : {},
        "psm_plugs"   : {},
        "io"          : {},
        "access"      : {},
        "temp"        : {},
        "temp_in_out" : {},
        "can_current" : {},
        "humidity"    : {},
        "phase"       : {}, }

    dev_table, var_table = info

    num = 0
    for endoid, name, alias, status in dev_table:
        # no blanks in names since we use blanks in items
        # later to split between unit_name and item_name
        dev_name = alias.replace(" ", "_")
        num += 1
        if not dev_name:
            dev_name = name + "-" + str(num)

        if dev_name in parsed["state"]:
            dev_name = "%s %s" % (alias, endoid)

        devices.setdefault(endoid, dev_name)

        if dev_name in parsed["state"] and \
           parsed["state"][dev_name]["_location_"] != endoid:
            dev_name += " %s" % endoid

        parsed["state"].setdefault(dev_name, {
            "status"     : status,
            "_location_" : endoid, })

    for oidend, variable, var_type, scale, value_str, value_int in var_table:
        location, index   = oidend.split(".")
        variable_splitted = variable.split(".")
        device            = devices.get(location)
        container         = [location] + variable_splitted[:-1]
        if len(container) == 2:
            container += [ "" ]

        key = variable_splitted[-1]
        if container[1].startswith("PSM_") and "Unit" in container:
            this_table = "psm_current"
            itemname   = "%s %s" % (device, container[1])

        elif container[1].startswith("PSM_") and container[2].startswith("Plug"):
            this_table = "psm_plugs"
            itemname   = "%s %s" % (device, ".".join(container[1:]))

        elif container[1].startswith("Input") or container[1].startswith("Output"):
            this_table = "io"
            itemname   = "%s %s" % (device, container[1])

        elif "Access" in container:
            this_table = "access"
            itemname   = "%s %s" % (device, container[1])

        elif "Humidity" in container:
            this_table = "humidity"
            itemname   = "%s %s" % (device, container[1])

        elif "Temperature" in container or "Dew Point" in container or \
             container[2].endswith("Temperature"):
            this_table = "temp"
            itemname   = cmciii_temp_get_item(container, device)

        elif container[2].startswith("CAN") and container[2].endswith("Current"):
            this_table = "can_current"
            itemname   = "%s %s" % (device, ".".join(container[1:]))

        elif container[1].startswith("Phase"):
            this_table = "phase"
            container     = [ container[0], "" ] + container[1:]
            itemname, key = cmciii_phase_get_item_and_key(container, device, var_type)

        elif container[2].startswith("Phase"):
            this_table = "phase"
            itemname, key = cmciii_phase_get_item_and_key(container, device, var_type)

        else:
            this_table = None

        if this_table is None:
            continue

        if var_type in [ "1", "7", "15", "20", "21", "90", "92", "93" ]:
            value = value_str

        else:
            # neg. scale: "-X" => "/ X"
            # pos. scale: "X"  => "* X"
            # else:            => "* 1"
            if int(scale) < 0:
                scale_f = lambda a: -1.0 * float(a) / float(scale)
            elif int(scale) > 0:
                scale_f = lambda a: float(a) * float(scale)
            else:
                scale_f = int
            value = scale_f(value_int)

        if "Power" in container or "Energy" in container:
            value = value * 1000 # kW, kWh

        if itemname in parsed[this_table] and \
           parsed[this_table][itemname]["_location_"] != location:
            itemname += " %s" % location

        parsed[this_table].setdefault(itemname, {"_device_"   : device,
                                                 "_location_" : location})
        parsed[this_table][itemname].setdefault(key, value)

    for entry, attrs in parsed.get("temp", {}).items():
        found_temp_in_out = False
        for attr, val in attrs.items():
            if attr.startswith("In-") or attr.startswith("Out-"):
                parsed["temp_in_out"].setdefault("%s %s" % \
                    (entry, attr.replace("-", " ")\
                                .replace("Bot", "Bottom")\
                                .replace("Mid", "Middle")), {
                    "Value"      : val,
                    "_device_"   : device,
                    "_location_" : location})
                found_temp_in_out = True
        if found_temp_in_out:
            del parsed["temp"][entry]

    return parsed


#   .--state---------------------------------------------------------------.
#   |                            _        _                                |
#   |                        ___| |_ __ _| |_ ___                          |
#   |                       / __| __/ _` | __/ _ \                         |
#   |                       \__ \ || (_| | ||  __/                         |
#   |                       |___/\__\__,_|\__\___|                         |
#   |                                                                      |
#   +----------------------------------------------------------------------+
#   |                             main check                               |
#   '----------------------------------------------------------------------'


def inventory_cmciii_state(parsed):
    for entry in parsed["state"]:
        yield entry, None


def check_cmciii_state(item, params, parsed):
    map_states = {
       '1' : (3, "not available"),
       '2' : (0, "OK"),
       '3' : (1, "detect"),
       '4' : (2, "lost"),
       '5' : (1, "changed"),
       '6' : (2, "error"), }

    if item in parsed["state"]:
        state, state_readable = map_states[parsed["state"][item]["status"]]
        return state, "Status: %s" % state_readable


check_info['cmciii'] = {
    "parse_function"      : parse_cmciii,
    "inventory_function"  : inventory_cmciii_state,
    "check_function"      : check_cmciii_state,
    "service_description" : "State %s",
    "snmp_scan_function"  : lambda oid: ".1.3.6.1.4.1.2606.7" in oid(".1.3.6.1.2.1.1.2.0"),
    "includes"            : [ "cmciii.include" ],
    "snmp_info"           : [( ".1.3.6.1.4.1.2606.7.4.1.2.1", [
                                OID_END,
                                "2",    # RITTAL-CMC-III-MIB::cmcIIIDevName
                                "3",    # RITTAL-CMC-III-MIB::cmcIIIDevAlias
                                "6",    # RITTAL-CMC-III-MIB::cmcIIIDevStatus
                            ]),
                            ( ".1.3.6.1.4.1.2606.7.4.2.2.1", [
                                OID_END,
                                "3",    # RITTAL-CMC-III-MIB::cmcIIIVarName
                                "4",    # RITTAL-CMC-III-MIB::cmcIIIVarType
                                "7",    # RITTAL-CMC-III-MIB::cmcIIIVarScale
                                "10",   # RITTAL-CMC-III-MIB::cmcIIIVarValueStr
                                "11",   # RITTAL-CMC-III-MIB::cmcIIIVarValueInt
                            ])],
}


#.
#   .--sensors-------------------------------------------------------------.
#   |                                                                      |
#   |                  ___  ___ _ __  ___  ___  _ __ ___                   |
#   |                 / __|/ _ \ '_ \/ __|/ _ \| '__/ __|                  |
#   |                 \__ \  __/ | | \__ \ (_) | |  \__ \                  |
#   |                 |___/\___|_| |_|___/\___/|_|  |___/                  |
#   |                                                                      |
#   '----------------------------------------------------------------------'


def inventory_cmciii_sensor(parsed):
    return []


def check_cmciii_sensor(item, params, parsed):
    # Older check plugin versions worked as follows:
    # We've generated items out of device table for the service
    # cmciii and if device name starts with "CMCIII-SEN", we added
    # an additional service cmciii.sensor. Then we connected these
    # names with data from the first variables table (by OID) and took
    # the status from there. We got:
    # 1 service cmciii:        'State Zugang_Rack_1_LCP_1_hinten'  (OK)
    # 1 service cmciii.sensor: 'Zugang_Rack_1_LCP_1_hinten Sensor' (Open)
    # But device table sends already its status, thus we had one service
    # overmuch (with different readable status).
    return 1, "This check is deprecated. Please rediscover the services of that host."


check_info['cmciii.sensor'] = {
    "inventory_function"  : inventory_cmciii_sensor,
    "check_function"      : check_cmciii_sensor,
    "service_description" : "%s",
}


#.}
#   .--PSM current---------------------------------------------------------.
#   |       ____  ____  __  __                                  _          |
#   |      |  _ \/ ___||  \/  |   ___ _   _ _ __ _ __ ___ _ __ | |_        |
#   |      | |_) \___ \| |\/| |  / __| | | | '__| '__/ _ \ '_ \| __|       |
#   |      |  __/ ___) | |  | | | (__| |_| | |  | | |  __/ | | | |_        |
#   |      |_|   |____/|_|  |_|  \___|\__,_|_|  |_|  \___|_| |_|\__|       |
#   |                                                                      |
#   '----------------------------------------------------------------------'


def inventory_cmciii_psm_current(parsed):
    for entry in parsed.get("psm_current", {}):
        yield entry, {}


def check_cmciii_psm_current(item, params, parsed):
    if item in parsed.get("psm_current", {}):
        attrs       = parsed["psm_current"][item]
        current     = attrs["Value"]
        min_current = attrs["SetPtHighAlarm"]
        max_current = attrs["SetPtLowAlarm"]

        if attrs["Status"] == "OK":
            state = 0
        else:
            state = 2

        return state, "Current: %s (%s/%s), Type: %s, Serial: %s, Position: %s" % \
               (current, min_current, max_current, attrs["Unit Type"], \
                attrs["Serial Number"], attrs["Mounting Position"]), \
               [( "current", current, 0, 0, min_current, max_current)]


check_info['cmciii.psm_current'] = {
    "inventory_function"  : inventory_cmciii_psm_current,
    "check_function"      : check_cmciii_psm_current,
    "service_description" : "Current %s",
    "has_perfdata"        : True,
}


#.
#   .--PSM plugs-----------------------------------------------------------.
#   |            ____  ____  __  __         _                              |
#   |           |  _ \/ ___||  \/  |  _ __ | |_   _  __ _ ___              |
#   |           | |_) \___ \| |\/| | | '_ \| | | | |/ _` / __|             |
#   |           |  __/ ___) | |  | | | |_) | | |_| | (_| \__ \             |
#   |           |_|   |____/|_|  |_| | .__/|_|\__,_|\__, |___/             |
#   |                                |_|            |___/                  |
#   '----------------------------------------------------------------------'


def inventory_cmciii_psm_plugs(parsed):
    for entry in parsed.get("psm_plugs", {}):
        yield entry, {}


def check_cmciii_psm_plugs(item, params, parsed):
    if item in parsed.get("psm_plugs", {}):
        state_readable = parsed["psm_plugs"][item]["Status"]
        if state_readable == "OK":
            state = 0
        else:
            state = 2
        return state, "Status: %s" % state_readable


check_info['cmciii.psm_plugs'] = {
    "inventory_function"  : inventory_cmciii_psm_plugs,
    "check_function"      : check_cmciii_psm_plugs,
    "service_description" : "%s",
}


#.
#   .--IO------------------------------------------------------------------.
#   |                              ___ ___                                 |
#   |                             |_ _/ _ \                                |
#   |                              | | | | |                               |
#   |                              | | |_| |                               |
#   |                             |___\___/                                |
#   |                                                                      |
#   '----------------------------------------------------------------------'


def inventory_cmciii_io(parsed):
    for entry in parsed.get("io", {}):
        yield entry, {}


def check_cmciii_io(item, params, parsed):
    if item in parsed.get("io", {}):
        attrs          = parsed["io"][item]
        state_readable = attrs["Status"]
        relay          = attrs.get("Relay")
        logic          = attrs.get("Logic")
        infotext       = "Status: %s" % state_readable

        if logic is not None:
            infotext += ", Logic: %s" % attrs["Logic"]

        if relay is not None:
            if state_readable == "OK":
                state = 0
            else:
                state = 2
            infotext += ", Relay: %s" % relay

        else: # input relay
            if state_readable in [ "OK", "Off" ]:
                state = 0
            elif state_readable == "On":
                state = 1
            else:
                state = 2
            infotext += ", Delay %s" % attrs.get("Delay")

        return state, infotext


check_info['cmciii.io'] = {
    "inventory_function"  : inventory_cmciii_io,
    "check_function"      : check_cmciii_io,
    "service_description" : "%s",
}


#.
#   .--access--------------------------------------------------------------.
#   |                                                                      |
#   |                      __ _  ___ ___ ___  ___ ___                      |
#   |                     / _` |/ __/ __/ _ \/ __/ __|                     |
#   |                    | (_| | (_| (_|  __/\__ \__ \                     |
#   |                     \__,_|\___\___\___||___/___/                     |
#   |                                                                      |
#   '----------------------------------------------------------------------'


def inventory_cmciii_access(parsed):
    for entry in parsed.get("access", {}):
        yield entry, None


def check_cmciii_access(item, params, parsed):
    if item in parsed.get("access", {}):
        attrs          = parsed["access"][item]
        state_readable = attrs["Status"]

        if state_readable == "Closed":
            state = 0
        else:
            state = 2

        return state, "%s: %s, Delay: %s, Sensitivity: %s" % \
               (attrs["DescName"], state_readable, \
                attrs["Delay"], \
                attrs["Sensitivity"])


check_info['cmciii.access'] = {
    "inventory_function"  : inventory_cmciii_access,
    "check_function"      : check_cmciii_access,
    "service_description" : "%s",
}


#.
#   .--temperature---------------------------------------------------------.
#   |      _                                      _                        |
#   |     | |_ ___ _ __ ___  _ __   ___ _ __ __ _| |_ _   _ _ __ ___       |
#   |     | __/ _ \ '_ ` _ \| '_ \ / _ \ '__/ _` | __| | | | '__/ _ \      |
#   |     | ||  __/ | | | | | |_) |  __/ | | (_| | |_| |_| | | |  __/      |
#   |      \__\___|_| |_| |_| .__/ \___|_|  \__,_|\__|\__,_|_|  \___|      |
#   |                       |_|                                            |
#   '----------------------------------------------------------------------'


def inventory_cmciii_temp(parsed):
    for entry, attrs in parsed.get("temp", {}).items():
        yield entry, {}


def check_cmciii_temp(item, params, parsed):
    if item in parsed.get("temp", {}):
        attrs = parsed["temp"][item]
        state, infotext, perfdata = check_temperature(attrs["Value"],
                                    params, "cmciii.temp.%s" % item,
                                    dev_levels = (attrs.get("SetPtHighWarning"),
                                                  attrs.get("SetPtHighAlarm")),
                                    dev_levels_lower = (attrs.get("SetPtLowWarning"),
                                                        attrs.get("SetPtLowAlarm")),
                                    dev_status_name = attrs.get("Status"))

        descr = attrs.get("DescName", "").replace("Temperature", "")
        if descr and descr not in item:
            infotext = "[%s] %s" % (descr, infotext)
        return state, infotext, perfdata


check_info['cmciii.temp'] = {
    "inventory_function"  : inventory_cmciii_temp,
    "check_function"      : check_cmciii_temp,
    "service_description" : "Temperature %s",
    "has_perfdata"        : True,
    "group"               : "temperature",
    "includes"            : [ "temperature.include" ]
}


#.
#   .--temp. in/out--------------------------------------------------------.
#   |        _                          _          __          _           |
#   |       | |_ ___ _ __ ___  _ __    (_)_ __    / /__  _   _| |_         |
#   |       | __/ _ \ '_ ` _ \| '_ \   | | '_ \  / / _ \| | | | __|        |
#   |       | ||  __/ | | | | | |_) |  | | | | |/ / (_) | |_| | |_         |
#   |        \__\___|_| |_| |_| .__(_) |_|_| |_/_/ \___/ \__,_|\__|        |
#   |                         |_|                                          |
#   '----------------------------------------------------------------------'


def inventory_cmciii_temp_in_out(parsed):
    for entry in parsed.get("temp_in_out", {}):
        yield entry, {}


def check_cmciii_temp_in_out(item, params, parsed):
    if item in parsed.get("temp_in_out", {}):
        attrs = parsed["temp_in_out"][item]
        return check_temperature(attrs["Value"],
               params, "cmciii.temp_in_out.%s" % item)


check_info['cmciii.temp_in_out'] = {
    "inventory_function"  : inventory_cmciii_temp_in_out,
    "check_function"      : check_cmciii_temp_in_out,
    "service_description" : "Temperature %s",
    "has_perfdata"        : True,
    "group"               : "temperature",
    "includes"            : [ "temperature.include" ]
}


#.
#   .--CAN current---------------------------------------------------------.
#   |       ____    _    _   _                                  _          |
#   |      / ___|  / \  | \ | |   ___ _   _ _ __ _ __ ___ _ __ | |_        |
#   |     | |     / _ \ |  \| |  / __| | | | '__| '__/ _ \ '_ \| __|       |
#   |     | |___ / ___ \| |\  | | (__| |_| | |  | | |  __/ | | | |_        |
#   |      \____/_/   \_\_| \_|  \___|\__,_|_|  |_|  \___|_| |_|\__|       |
#   |                                                                      |
#   '----------------------------------------------------------------------'


def inventory_cmciii_can_current(parsed):
    for entry in parsed.get("can_current", {}):
        yield entry, None


def check_cmciii_can_current(item, params, parsed):
    if item in parsed.get("can_current", {}):
        attrs          = parsed["can_current"][item]
        state_readable = attrs["Status"]
        value          = attrs["Value"]
        warn           = attrs["SetPtHighWarning"]
        crit           = attrs["SetPtHighAlarm"]

        if state_readable == "OK":
            state = 0
        else:
            state = 2

        return state, "Status: %s, Current: %s mA (warn/crit at %s/%s mA)" % \
               (state_readable, value, warn, crit), \
               [("current", value / 1000, warn, crit )]



check_info['cmciii.can_current'] = {
    "inventory_function"  : inventory_cmciii_can_current,
    "check_function"      : check_cmciii_can_current,
    "service_description" : "%s",
    "has_perfdata"        : True,
}


#.
#   .--humidity------------------------------------------------------------.
#   |              _                     _     _ _ _                       |
#   |             | |__  _   _ _ __ ___ (_) __| (_) |_ _   _               |
#   |             | '_ \| | | | '_ ` _ \| |/ _` | | __| | | |              |
#   |             | | | | |_| | | | | | | | (_| | | |_| |_| |              |
#   |             |_| |_|\__,_|_| |_| |_|_|\__,_|_|\__|\__, |              |
#   |                                                  |___/               |
#   '----------------------------------------------------------------------'


def inventory_cmciii_humidity(parsed):
    for entry in parsed.get("humidity", {}):
        yield entry, {}


def check_cmciii_humidity(item, params, parsed):
    if item in parsed.get("humidity", {}):
        attrs          = parsed["humidity"][item]
        state_readable = attrs["Status"]

        if state_readable == "OK":
            state = 0
        else:
            state = 2

        yield state, "Status: %s" % state_readable
        yield check_humidity(attrs["Value"], params)


check_info['cmciii.humidity'] = {
    "inventory_function"  : inventory_cmciii_humidity,
    "check_function"      : check_cmciii_humidity,
    "service_description" : "%s",
    "has_perfdata"        : True,
    "group"               : "humidity",
    "includes"            : [ "humidity.include" ]
}


#.
#   .--phase---------------------------------------------------------------.
#   |                           _                                          |
#   |                     _ __ | |__   __ _ ___  ___                       |
#   |                    | '_ \| '_ \ / _` / __|/ _ \                      |
#   |                    | |_) | | | | (_| \__ \  __/                      |
#   |                    | .__/|_| |_|\__,_|___/\___|                      |
#   |                    |_|                                               |
#   '----------------------------------------------------------------------'


def inventory_cmciii_phase(parsed):
    return inventory_elphase(parsed.get("phase", {}))


def check_cmciii_phase(item, params, parsed):
    return check_elphase(item, params, parsed.get("phase", {}))


check_info['cmciii.phase'] = {
    "inventory_function"  : inventory_cmciii_phase,
    "check_function"      : check_cmciii_phase,
    "service_description" : "Input %s",
    "has_perfdata"        : True,
    "group"               : "el_inphase",
    "includes"            : [ "elphase.include" ],
}
