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

# .1.3.6.1.4.1.10876.2.1.1.1.1.2.1 Fan1 Fan Speed
# .1.3.6.1.4.1.10876.2.1.1.1.1.2.2 Fan2 Fan Speed
# ...
# .1.3.6.1.4.1.10876.2.1.1.1.1.2.6 Vcore Voltage
# .1.3.6.1.4.1.10876.2.1.1.1.1.2.7 CPU VTT Voltage
# .1.3.6.1.4.1.10876.2.1.1.1.1.3.1 0
# .1.3.6.1.4.1.10876.2.1.1.1.1.3.2 0
# ...
# .1.3.6.1.4.1.10876.2.1.1.1.1.3.6 1
# .1.3.6.1.4.1.10876.2.1.1.1.1.3.7 1
# .1.3.6.1.4.1.10876.2.1.1.1.1.4.1 3760
# .1.3.6.1.4.1.10876.2.1.1.1.1.4.2 1909
# ...
# .1.3.6.1.4.1.10876.2.1.1.1.1.4.6 1080
# .1.3.6.1.4.1.10876.2.1.1.1.1.4.7 1056
# ...
# .1.3.6.1.4.1.10876.2.1.1.1.1.5.6 1488
# .1.3.6.1.4.1.10876.2.1.1.1.1.5.7 1344
# .1.3.6.1.4.1.10876.2.1.1.1.1.6.1 291
# .1.3.6.1.4.1.10876.2.1.1.1.1.6.2 291
# ...
# .1.3.6.1.4.1.10876.2.1.1.1.1.11.1 RPM
# .1.3.6.1.4.1.10876.2.1.1.1.1.11.2 RPM
# ...
# .1.3.6.1.4.1.10876.2.1.1.1.1.11.6 mV
# .1.3.6.1.4.1.10876.2.1.1.1.1.11.7 mV
# .1.3.6.1.4.1.10876.2.1.1.1.1.12.1 0
# .1.3.6.1.4.1.10876.2.1.1.1.1.12.2 0
# ...
# .1.3.6.1.4.1.10876.2.1.1.1.1.12.6 0
# .1.3.6.1.4.1.10876.2.1.1.1.1.12.7 0
# .1.3.6.1.4.1.10876.2.2 0
# .1.3.6.1.4.1.10876.2.3 No problem.

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


def inventory_supermicro_health(info):
    if info[1]:
        return [(None, None)]


def check_supermicro_health(_no_item, _no_params, info):
    return int(info[1][0][0]), info[1][0][1]


check_info['supermicro'] = {
    'check_function': check_supermicro_health,
    'inventory_function': inventory_supermicro_health,
    'service_description': "Overall Hardware Health",
    'snmp_scan_function': lambda oid: oid(".1.3.6.1.2.1.1.2.0") == ".1.3.6.1.4.1.311.1.1.3.1.2",
    'snmp_info': [
        (
            ".1.3.6.1.4.1.10876.2.1.1.1.1",
            [
                2,  # smHealthMonitorName
                3,  # smHealthMonitorType
                4,  # smHealthMonitorReading
                5,  # smHealthMonitorHighLimit
                6,  # smHealthMonitorLowLimit
                11,  # smHealthMonitorReadingUnit
                12,  # smHealthMonitorStatus
            ]),
        (
            ".1.3.6.1.4.1.10876.2",
            [
                2,  # smHealthAllinoneStatus
                3,  # smHealthAllinoneMsg
            ]),
        (
            ".1.3.6.1.4.1.10876.100.1.4.1",
            [
                1,  # diskSerialNumber
                2,  # diskName
                4
            ]),  # diskSmartStatus
    ]
}

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


def inventory_supermicro_sensors(info):
    for name, _sensor_type, _reading, _high, _low, _unit, _status in info[0]:
        yield name, None


def check_supermicro_sensors(item, _no_params, info):
    class Type(object):
        Fan, Voltage, Temperature, Status = ('0', '1', '2', '3')

    def worst_status(*args):
        order = [0, 1, 3, 2]
        return sorted(args, key=lambda x: order[x], reverse=True)[0]

    def expect_order(*args):
        return max(
            [abs(x[0] - x[1][0]) for x in enumerate(sorted(enumerate(args), key=lambda x: x[1]))])

    for name, sensor_type, reading, high, low, unit, dev_status in info[0]:
        if name == item:
            reading = float(reading)
            dev_status = int(dev_status)

            crit_upper = warn_upper = None
            status_high = status_low = 0
            if high:
                crit_upper = float(high)
                warn_upper = crit_upper * 0.95
                status_high = expect_order(reading, warn_upper, crit_upper)
            if low:
                crit_lower = float(low)
                warn_lower = crit_lower * 1.05
                status_low = expect_order(crit_lower, warn_lower, reading)

            perfvar = None

            # normalize values depending on sensor type
            if sensor_type == Type.Temperature:
                unit = u"°%s" % unit
                perfvar = "temp"
            elif sensor_type == Type.Voltage:
                if unit == "mV":
                    reading, warn_upper, crit_upper =\
                        [x / 1000.0 for x in (reading, warn_upper, crit_upper)]
                    unit = "V"
                perfvar = "voltage"
            elif sensor_type == Type.Status:
                reading = "State %d" % int(reading)
                unit = ""

            perfdata = []

            if perfvar:
                if crit_upper is not None:
                    perfdata = [(perfvar, reading, warn_upper, crit_upper)]
                else:
                    perfdata = [(perfvar, reading)]

            return (worst_status(status_high, status_low,
                                 dev_status), "%s%s" % (reading, unit), perfdata)


check_info['supermicro.sensors'] = {
    'check_function': check_supermicro_sensors,
    'inventory_function': inventory_supermicro_sensors,
    'service_description': "Sensor %s",
    'has_perfdata': True,
}

#.
#   .--SMART---------------------------------------------------------------.
#   |                   ____  __  __    _    ____ _____                    |
#   |                  / ___||  \/  |  / \  |  _ \_   _|                   |
#   |                  \___ \| |\/| | / _ \ | |_) || |                     |
#   |                   ___) | |  | |/ ___ \|  _ < | |                     |
#   |                  |____/|_|  |_/_/   \_\_| \_\|_|                     |
#   |                                                                      |
#   '----------------------------------------------------------------------'


def format_item_supermicro_smart(name):
    return name.replace(r"\\\\.\\", "")


def inventory_supermicro_smart(info):
    for _serial, name, _status in info[2]:
        yield format_item_supermicro_smart(name), None


def check_supermicro_smart(item, _no_params, info):
    # note (only status 0 (OK) and 2 (Crit) are documented.
    # status 3 appears to indicate "unknown" as observed by a user.
    # It's likely - but not verified - that status 1 would indicate a non-
    # critical problem if it's used at all)
    status_map = {"0": "Healthy", "1": "Warning", "2": "Critical", "3": "Unknown"}
    for serial, name, status in info[2]:
        if format_item_supermicro_smart(name) == item:
            return int(status), "(S/N %s) %s" % (serial, status_map[status])


check_info['supermicro.smart'] = {
    'check_function': check_supermicro_smart,
    'inventory_function': inventory_supermicro_smart,
    'service_description': "SMART Health %s",
    'has_perfdata': True,
}
