#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.

# .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"
    or (
        "linux" in oid(".1.3.6.1.2.1.1.1.0").lower()
        and oid(".1.3.6.1.4.1.10876.2.1.1.1.1.2.1") is not None
    ),
    "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, 2, 4],  # diskSerialNumber  # diskName
        ),  # 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 = "°%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,
}
