#!/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'],
}
