#!/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.1991.1.1.3.3.6.1.1.1  41.4960 C: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.6.1.1.2  50.9531 C: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.6.1.1.65  49.8007 C: Normal
#
# .1.3.6.1.4.1.1991.1.1.3.3.6.1.2.1 007.9643 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.6.1.2.2 007.5898 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.6.1.2.65 006.9644 dBm: Normal
#
# .1.3.6.1.4.1.1991.1.1.3.3.6.1.3.1 000.6744 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.6.1.3.2 -023.0102 dBm: Low-Alarm
# .1.3.6.1.4.1.1991.1.1.3.3.6.1.3.65 -015.6863 dBm: Low-Alarm
#
#
# .1.3.6.1.4.1.1991.1.1.3.3.9.1.1.1 100GBASE-LR4 CFP2
# .1.3.6.1.4.1.1991.1.1.3.3.9.1.1.2 100GBASE-LR4 CFP2
# .1.3.6.1.4.1.1991.1.1.3.3.9.1.1.65 100GBASE-LR4 CFP2
#
# .1.3.6.1.4.1.1991.1.1.3.3.9.1.4.1 12-1234567-01
# .1.3.6.1.4.1.1991.1.1.3.3.9.1.4.2 12-1234567-01
# .1.3.6.1.4.1.1991.1.1.3.3.9.1.4.65 12-1234567-01
#
# .1.3.6.1.4.1.1991.1.1.3.3.9.1.5.1 XXX00000X00X00X
# .1.3.6.1.4.1.1991.1.1.3.3.9.1.5.2 XXX000000000XX0
# .1.3.6.1.4.1.1991.1.1.3.3.9.1.5.65 XXX0000000000X
#
#
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.2.1.1    41.5000 C: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.2.1.2    41.4960 C: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.2.1.3    41.4921 C: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.2.1.4    41.5039 C: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.2.2.1    50.9687 C: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.2.2.2    50.9843 C: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.2.2.3    50.9570 C: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.2.2.4    50.9570 C: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.2.65.1    49.7539 C: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.2.65.2    49.7734 C: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.2.65.3    49.7578 C: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.2.65.4    49.7851 C: Normal
#
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.3.1.1   001.9072 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.3.1.2   002.5098 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.3.1.3   001.3392 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.3.1.4   001.9473 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.3.2.1   001.5615 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.3.2.2   001.4924 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.3.2.3   001.6840 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.3.2.4   001.5421 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.3.65.1   000.0543 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.3.65.2   000.6069 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.3.65.3   001.6307 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.3.65.4   001.3152 dBm: Normal
#
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.4.1.1  -004.9935 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.4.1.2  -005.4030 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.4.1.3  -005.3017 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.4.1.4  -005.6479 dBm: Normal
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.4.2.1  -026.0205 dBm: Low-Alarm
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.4.2.2  -214.3647 dBm: Low-Alarm
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.4.2.3  -214.3647 dBm: Low-Alarm
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.4.2.4  -024.9485 dBm: Low-Alarm
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.4.65.1  -021.4266 dBm: Low-Alarm
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.4.65.2  -020.3621 dBm: Low-Alarm
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.4.65.3  -022.4412 dBm: Low-Alarm
# .1.3.6.1.4.1.1991.1.1.3.3.10.1.4.65.4  -021.8045 dBm: Low-Alarm



def parse_brocade_optical(info):
    def parse_value(value_string):
        if value_string == 'N/A' or value_string.lower() == "not supported":
            return None, None

        try:
            val, unit, status = value_string.split()
            return float(val), status
        except ValueError:
            return None, None

    result = {}

    if_info, if_data, if_ids, lanes = info

    # interpret per-interface data
    for temp, tx_light, rx_light, if_id in if_data:
        result[if_id] = {
            'temp'     : parse_value(temp),
            'tx_light' : parse_value(tx_light),
            'rx_light' : parse_value(rx_light),
        }

    for if_id, if_descr, if_type, if_operstatus in if_info:
        if if_id in result:
            result[if_id].update({
                'port_type' : if_type,
                'description' : if_descr,
                'operational_status' : if_operstatus
            })


    # add informational values
    for media_type, part, serial, if_id in if_ids:
        result[if_id].update({
            'type'     : media_type,
            'part'     : part,
            'serial'   : serial
        })

    # add per-lane data
    for temp, tx_light, rx_light, lane in lanes:
        if_id, lane = lane.split('.')
        result[if_id].setdefault('lanes', {})[int(lane)] = {
            'temp'     : parse_value(temp),
            'tx_light' : parse_value(tx_light),
            'rx_light' : parse_value(rx_light)
        }

    return result


def inventory_brocade_optical(parsed):
    settings            = host_extra_conf_merged(g_hostname, inventory_if_rules)
    porttypes           = set(settings.get('porttypes', if_inventory_porttypes))
    portstates          = settings.get('portstates', if_inventory_portstates)
    match_desc          = settings.get('match_desc')

    pad_width = max([int(len(key)) for key in parsed.keys()])

    def port_match(name, what):
        if what == None:
            return True
        for r in what:
            if regex(r).match(name):
                return True
        return False

    for key, entry in parsed.iteritems():
        if (entry['port_type'] in porttypes and
            entry['operational_status'] in portstates and
            port_match(entry['description'], match_desc)):
            yield "0" * (pad_width - len(key)) + key, {}


def check_brocade_optical(item, params, parsed):
    def nagios_state(entry, key):
        reading = entry[key]
        if reading[0] is None:
            return 3
        elif params.get(key, False):
            state = reading[1].lower()
            if state == "normal":
                return 0
            if state.endswith("warn"):
                return 1
            else:
                return 2
        else:
            return 0

    def infotext(reading, title, unit):
        if reading[0] < -214748.0:
            reading_text = "off"
        else:
            reading_text = "%.1f %s" % (reading[0], unit)
        return "%s %s (%s)" % (title, reading_text, reading[1])

    item = item.lstrip('0')

    if item in parsed:
        iface = parsed[item]
        yield 0, "[S/N %s, P/N %s]" % (iface['serial'], iface['part'])

        state, text, perf = check_temperature(iface['temp'][0], params, "brocade_optical_%s" % item,
                                              dev_status=nagios_state(iface, 'temp'))
        yield state, "Temperature: %s" % text, perf

        if iface['tx_light'][0] is not None:
            yield nagios_state(iface, 'tx_light'), infotext(iface['tx_light'], "TX Light", "dBm"),\
                [("tx_light", iface['tx_light'][0])]

        if iface['rx_light'][0] is not None:
            yield nagios_state(iface, 'rx_light'), infotext(iface['rx_light'], "RX Light", "dBm"),\
                [("rx_light", iface['rx_light'][0])]

        if 'lanes' in iface and params.get('lanes', False):
            for num, lane in iface['lanes'].iteritems():
                state, text, perf = check_temperature(lane['temp'][0], params,
                                                      "brocade_optical_lane%d_%s" % (num, item),
                                                      dev_status=nagios_state(lane, 'temp'))
                perf = [("port_%s_%d" % (perf[0][0], num), perf[0][1])]
                if state in [1, 2]:
                    yield state, "Temperature (Lane %d) %s" % (num, text), perf
                else:
                    yield state, None, perf

                state = nagios_state(lane, 'tx_light')
                if state in [1, 2]:
                    yield state,\
                        infotext(iface['tx_light'], "TX Light (Lane %d)" % num, "dBm"),\
                        [("tx_light_%d" % num, lane['tx_light'][0])]
                else:
                    yield state, None, [("tx_light_%d" % num, lane['tx_light'][0])]

                state = nagios_state(lane, 'rx_light')
                if state in [1, 2]:
                    yield state,\
                        infotext(iface['rx_light'], "RX Light (Lane %d)" % num, "dBm"),\
                        [("rx_light_%d" % num, lane['rx_light'][0])]
                else:
                    yield state, None, [("rx_light_%d" % num, lane['rx_light'][0])]


check_info['brocade_optical'] = {
    'parse_function'      : parse_brocade_optical,
    'check_function'      : check_brocade_optical,
    'inventory_function'  : inventory_brocade_optical,
    'service_description' : "Interface %s Optical",
    'snmp_info'           : [
        (".1.3.6.1.2.1.2.2.1", [1,            # ifIndex
                                2,            # ifDescr
                                3,            # ifType
                                8,            # ifOperStatus
                                ]),
        (".1.3.6.1.4.1.1991.1.1.3.3.6.1", [1,   # temperature
                                           2,   # TX light level
                                           3,   # RX light level
                                           OID_END
                                           ]),
        (".1.3.6.1.4.1.1991.1.1.3.3.9.1", [1,   # media type
                                           4,   # part number
                                           5,   # serial number
                                           OID_END
                                           ]),
        (".1.3.6.1.4.1.1991.1.1.3.3.10.1", [2,   # lane temperature
                                            3,   # lane TX light level
                                            4,   # lane RX light level
                                            OID_END
                                            ]),
    ],
    'snmp_scan_function'  : lambda oid: oid(".1.3.6.1.2.1.1.2.0").startswith(".1.3.6.1.4.1.1991.1."),
    'has_perfdata'        : True,
    'group'               : "brocade_optical",
    'includes'            : ["temperature.include", "if.include"],
}
