#!/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",
    'has_perfdata'        : False,
    '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:
        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 =\
                        map(lambda x: x / 1000.0, (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,
}

#.

