#!/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.2.1.47.1.1.1.1.7.1 PA-3020 --> ENTITY-MIB::entPhysicalName.1
# .1.3.6.1.2.1.47.1.1.1.1.7.2 Fan #1 RPM --> ENTITY-MIB::entPhysicalName.2
# .1.3.6.1.2.1.47.1.1.1.1.7.3 Fan #2 RPM --> ENTITY-MIB::entPhysicalName.3
# .1.3.6.1.2.1.47.1.1.1.1.7.4 Fan #3 RPM --> ENTITY-MIB::entPhysicalName.4
# .1.3.6.1.2.1.47.1.1.1.1.7.5 Fan #4 RPM --> ENTITY-MIB::entPhysicalName.5
# .1.3.6.1.2.1.47.1.1.1.1.7.6 Temperature @ Ocelot --> ENTITY-MIB::entPhysicalName.6
# .1.3.6.1.2.1.47.1.1.1.1.7.7 Temperature @ Switch --> ENTITY-MIB::entPhysicalName.7
# .1.3.6.1.2.1.47.1.1.1.1.7.8 Temperature @ Cavium --> ENTITY-MIB::entPhysicalName.8
# .1.3.6.1.2.1.47.1.1.1.1.7.9 Temperature @ Intel PHY --> ENTITY-MIB::entPhysicalName.9
# .1.3.6.1.2.1.47.1.1.1.1.7.10 Temperature @ Switch Core --> ENTITY-MIB::entPhysicalName.10
# .1.3.6.1.2.1.47.1.1.1.1.7.11 Temperature @ Cavium Core --> ENTITY-MIB::entPhysicalName.11

# .1.3.6.1.2.1.99.1.1.1.1.2 10 --> ENTITY-SENSOR-MIB::entPhySensorType.2
# .1.3.6.1.2.1.99.1.1.1.1.3 10 --> ENTITY-SENSOR-MIB::entPhySensorType.3
# .1.3.6.1.2.1.99.1.1.1.1.4 10 --> ENTITY-SENSOR-MIB::entPhySensorType.4
# .1.3.6.1.2.1.99.1.1.1.1.5 10 --> ENTITY-SENSOR-MIB::entPhySensorType.5
# .1.3.6.1.2.1.99.1.1.1.1.6 8 --> ENTITY-SENSOR-MIB::entPhySensorType.6
# .1.3.6.1.2.1.99.1.1.1.1.7 8 --> ENTITY-SENSOR-MIB::entPhySensorType.7
# .1.3.6.1.2.1.99.1.1.1.1.8 8 --> ENTITY-SENSOR-MIB::entPhySensorType.8
# .1.3.6.1.2.1.99.1.1.1.1.9 8 --> ENTITY-SENSOR-MIB::entPhySensorType.9
# .1.3.6.1.2.1.99.1.1.1.1.10 8 --> ENTITY-SENSOR-MIB::entPhySensorType.10
# .1.3.6.1.2.1.99.1.1.1.1.11 8 --> ENTITY-SENSOR-MIB::entPhySensorType.11
# .1.3.6.1.2.1.99.1.1.1.2.2 9 --> ENTITY-SENSOR-MIB::entPhySensorScale.2
# .1.3.6.1.2.1.99.1.1.1.2.3 9 --> ENTITY-SENSOR-MIB::entPhySensorScale.3
# .1.3.6.1.2.1.99.1.1.1.2.4 9 --> ENTITY-SENSOR-MIB::entPhySensorScale.4
# .1.3.6.1.2.1.99.1.1.1.2.5 9 --> ENTITY-SENSOR-MIB::entPhySensorScale.5
# .1.3.6.1.2.1.99.1.1.1.2.6 9 --> ENTITY-SENSOR-MIB::entPhySensorScale.6
# .1.3.6.1.2.1.99.1.1.1.2.7 9 --> ENTITY-SENSOR-MIB::entPhySensorScale.7
# .1.3.6.1.2.1.99.1.1.1.2.8 9 --> ENTITY-SENSOR-MIB::entPhySensorScale.8
# .1.3.6.1.2.1.99.1.1.1.2.9 9 --> ENTITY-SENSOR-MIB::entPhySensorScale.9
# .1.3.6.1.2.1.99.1.1.1.2.10 9 --> ENTITY-SENSOR-MIB::entPhySensorScale.10
# .1.3.6.1.2.1.99.1.1.1.2.11 9 --> ENTITY-SENSOR-MIB::entPhySensorScale.11
# .1.3.6.1.2.1.99.1.1.1.4.2 9665 --> ENTITY-SENSOR-MIB::entPhySensorValue.2
# .1.3.6.1.2.1.99.1.1.1.4.3 9529 --> ENTITY-SENSOR-MIB::entPhySensorValue.3
# .1.3.6.1.2.1.99.1.1.1.4.4 9552 --> ENTITY-SENSOR-MIB::entPhySensorValue.4
# .1.3.6.1.2.1.99.1.1.1.4.5 9225 --> ENTITY-SENSOR-MIB::entPhySensorValue.5
# .1.3.6.1.2.1.99.1.1.1.4.6 32 --> ENTITY-SENSOR-MIB::entPhySensorValue.6
# .1.3.6.1.2.1.99.1.1.1.4.7 40 --> ENTITY-SENSOR-MIB::entPhySensorValue.7
# .1.3.6.1.2.1.99.1.1.1.4.8 42 --> ENTITY-SENSOR-MIB::entPhySensorValue.8
# .1.3.6.1.2.1.99.1.1.1.4.9 28 --> ENTITY-SENSOR-MIB::entPhySensorValue.9
# .1.3.6.1.2.1.99.1.1.1.4.10 61 --> ENTITY-SENSOR-MIB::entPhySensorValue.10
# .1.3.6.1.2.1.99.1.1.1.4.11 48 --> ENTITY-SENSOR-MIB::entPhySensorValue.11
# .1.3.6.1.2.1.99.1.1.1.5.2 1 --> ENTITY-SENSOR-MIB::entPhySensorOperStatus.2
# .1.3.6.1.2.1.99.1.1.1.5.3 1 --> ENTITY-SENSOR-MIB::entPhySensorOperStatus.3
# .1.3.6.1.2.1.99.1.1.1.5.4 1 --> ENTITY-SENSOR-MIB::entPhySensorOperStatus.4
# .1.3.6.1.2.1.99.1.1.1.5.5 1 --> ENTITY-SENSOR-MIB::entPhySensorOperStatus.5
# .1.3.6.1.2.1.99.1.1.1.5.6 1 --> ENTITY-SENSOR-MIB::entPhySensorOperStatus.6
# .1.3.6.1.2.1.99.1.1.1.5.7 1 --> ENTITY-SENSOR-MIB::entPhySensorOperStatus.7
# .1.3.6.1.2.1.99.1.1.1.5.8 1 --> ENTITY-SENSOR-MIB::entPhySensorOperStatus.8
# .1.3.6.1.2.1.99.1.1.1.5.9 1 --> ENTITY-SENSOR-MIB::entPhySensorOperStatus.9
# .1.3.6.1.2.1.99.1.1.1.5.10 1 --> ENTITY-SENSOR-MIB::entPhySensorOperStatus.10
# .1.3.6.1.2.1.99.1.1.1.5.11 1 --> ENTITY-SENSOR-MIB::entPhySensorOperStatus.11


def parse_entity_sensors(info):
    map_types = {
        "1": ("other", "other"),
        "2": ("unknown", "unknown"),
        "3": ("voltage", "V"),
        "4": ("voltage", "V"),
        "5": ("current", "A"),
        "6": ("power", "W"),
        "7": ("freqeuncy", "hz"),
        "8": ("temp", "c"),
        "9": ("percent", "%"),
        "10": ("fan", "RPM"),
        "11": ("volume", "cmm"),  # cubic decimetre dm^3
        "12": ("binary", "")
    }
    map_scaling = {
        "1": 10**(-24),
        "2": 10**(-21),
        "3": 10**(-18),
        "4": 10**(-15),
        "5": 10**(-12),
        "6": 10**(-9),
        "7": 10**(-6),
        "8": 10**(-3),
        "9": 1,
        "10": 10**(3),
        "11": 10**(6),
        "12": 10**(9),
        "13": 10**(12),
        "14": 10**(15),
        "15": 10**(18),
        "16": 10**(21),
        "17": 10**(24),
    }
    map_operstate = {"1": (0, "OK"), "2": (2, "unavailable"), "3": (1, "nonoperational")}
    # Some devices such as Palo Alto Network series 3000 support
    # the ENTITY-MIB including sensor/entity names.
    # Others (e.g. Palo Alto Networks Series 200) do not support
    # this MIB, thus we use OID as item instead
    pre_parsed = {}
    for oid, name in info[0]:
        sensor_name = ""
        if name:
            sensor_name = "Sensor %s" % \
                (name.replace("Temperature", "") \
                     .replace("Fan", "") \
                     .replace("@", "") \
                     .replace("#", "").strip())

        pre_parsed.setdefault(oid, sensor_name)

    parsed = {}
    for oid, sensor_type_nr, sensor_scale, sensor_reading, operstate in info[1]:
        if pre_parsed.get(oid, ""):
            sensor_name = pre_parsed[oid]
        else:
            sensor_name = "Sensor %s" % oid

        sensor_type, sensor_unit = map_types[sensor_type_nr]
        parsed.setdefault(sensor_type, {})
        factor = float(map_scaling[sensor_scale])
        parsed[sensor_type][sensor_name] = \
            (map_operstate[operstate],
             float(sensor_reading) * factor, sensor_unit)

    return parsed


def inventory_entity_sensors(parsed, what):
    for sensor, sensor_info in parsed.get(what, {}).items():
        if sensor_info[0][1] == "OK":
            yield (sensor, {})


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

# Asked customer, oriented by other temperature sensor checks
factory_settings['entity_sensors_temp_default_variables'] = {
    'levels': (35, 40),
}


def check_entity_sensors_temp(item, params, parsed):
    if item in parsed.get("temp", {}):
        operstate, reading, unit = parsed["temp"][item]
        state, state_readable = operstate
        return check_temperature(reading,
                                 params,
                                 "palo_alto_temperature_%s" % item,
                                 dev_unit=unit,
                                 dev_status=state,
                                 dev_status_name=state_readable)


check_info['entity_sensors'] = {
    'parse_function': parse_entity_sensors,
    'inventory_function': lambda parsed: inventory_entity_sensors(parsed, "temp"),
    'check_function': check_entity_sensors_temp,
    'service_description': 'Temperature %s',
    'has_perfdata': True,
    'snmp_info': [
        (
            ".1.3.6.1.2.1.47.1.1.1.1",
            [
                OID_END,
                "7",  # entPhysicalName
            ]),
        (
            ".1.3.6.1.2.1.99.1.1.1",
            [
                OID_END,
                "1",  # entPhySensorType
                "2",  # entPhySensorScale
                "4",  # entPhySensorValue
                "5",  # entPhySensorOperStatus
            ])
    ],
    'snmp_scan_function': lambda oid: "palo alto networks" in oid(".1.3.6.1.2.1.1.1.0").lower(),
    'default_levels_variable': 'entity_sensors_temp_default_variables',
    'group': 'temperature',
    'includes': ['temperature.include'],
}

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

# Asked customer, oriented by other fan sensor checks
factory_settings['entity_sensors_fan_default_variables'] = {
    'lower': (2000, 1000),
}


def check_entity_sensors_fan(item, params, parsed):
    if item in parsed.get("fan", {}):
        operstate, reading, _unit = parsed["fan"][item]
        state, state_readable = operstate
        yield state, "Operational status: %s" % state_readable
        yield check_fan(reading, params)


check_info['entity_sensors.fan'] = {
    'inventory_function': lambda parsed: inventory_entity_sensors(parsed, "fan"),
    'check_function': check_entity_sensors_fan,
    'service_description': 'Fan %s',
    'has_perfdata': True,
    'default_levels_variable': 'entity_sensors_fan_default_variables',
    'group': 'hw_fans',
    'includes': ['fan.include'],
}
