#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2014             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.

factory_settings['cisco_ip_sla_default_levels'] = {
    'state': 'active',
    'connection_lost_occured': 'no',
    'timeout_occured': 'no',
    'completion_time_over_treshold_occured': 'no',
    'latest_rtt_completion_time': (250, 500),
    'latest_rtt_state': 'ok',
}


def parse_cisco_ip_sla(info):
    # since the RTT Index, Owner and Tag don't have to be set we use
    # indices for the different items
    precisions = [list_.pop() for list_ in info[0]]
    precisions = {str(i): 'ms' if p[0] == '1' else 'us' for i, p in enumerate(precisions)}

    rtt_types = {
        '1': 'echo',
        '2': 'path echo',
        '3': 'file IO',
        '4': 'script',
        '5': 'UDP echo',
        '6': 'TCP connect',
        '7': 'HTTP',
        '8': 'DNS',
        '9': 'jitter',
        '10': 'DLSw',
        '11': 'DHCP',
        '12': 'FTP',
        '13': 'VoIP',
        '14': 'RTP',
        '15': 'LSP group',
        '16': 'ICMP jitter',
        '17': 'LSP ping',
        '18': 'LSP trace',
        '19': 'ethernet ping',
        '20': 'ethernet jitter',
        '21': 'LSP ping pseudowire',
    }

    states = {
        '1': 'reset',
        '2': 'orderly stop',
        '3': 'immediate stop',
        '4': 'pending',
        '5': 'inactive',
        '6': 'active',
        '7': 'restart',
    }

    rtt_states = {
        '0': 'other',
        '1': 'ok',
        '2': 'disconnected',
        '3': 'over threshold',
        '4': 'timeout',
        '5': 'busy',
        '6': 'not connected',
        '7': 'dropped',
        '8': 'sequence error',
        '9': 'verify error',
        '10': 'application specific error',
    }

    def to_ip_address(int_list):
        if len(int_list) == 4:
            return '%d.%d.%d.%d' % tuple(int_list)
        elif len(int_list) == 6:
            return '%d:%d:%d:%d:%d:%d' % tuple(int_list)
        else:
            return ''

    #contains description, parse function, unit and type
    contents = [
        (  # rttMonEchoAdminEntry
            ('Target address', to_ip_address, '', None),
            ('Source address', to_ip_address, '', None),
        ),
        (  # rttMonCtrlAdminEntry
            ('Owner', None, '', None),
            ('Tag', None, '', None),
            ('RTT type', lambda x: rtt_types.get(x, 'unknown'), '', 'option'),
            ('Threshold', int, 'ms', 'option'),
        ),
        (  # rttMonCtrlOperEntry
            ('State', lambda x: states.get(x, 'unknown'), '', 'option'),
            ('Text', None, '', None),
            ('Connection lost occured', lambda x: 'yes' if x == '1' else 'no', '', 'option'),
            ('Timeout occured', lambda x: 'yes' if x == '1' else 'no', '', 'option'),
            ('Completion time over treshold occured', lambda x: 'yes' if x == '1' else 'no', '',
             'option'),
        ),
        (  # rttMonLatestRttOperEntry
            ('Latest RTT completion time', int, 'ms/us', 'level'),
            ('Latest RTT state', lambda x: rtt_states.get(x, 'unknown'), '', 'option'),
        ),
    ]

    parsed = {}
    for content, entries in zip(contents, info):
        if not entries:
            continue

        for index, entry in enumerate(entries):
            index = str(index)
            parsed.setdefault(index, [])
            for (description, parser, unit, type_), value in zip(content, entry):
                if parser:
                    value = parser(value)
                if unit == 'ms/us':
                    unit = precisions[index]
                parsed[index].append((description, value, unit, type_))

    return parsed


def inventory_cisco_ip_sla(parsed):
    for index in parsed:
        yield index, {}


@get_parsed_item_data
def check_cisco_ip_sla(_item, params, data):
    for description, value, unit, type_ in data:
        if not value:
            continue

        state = 0
        if unit:
            infotext = '%s: %s %s' % (description, value, unit)
        else:
            infotext = '%s: %s' % (description, value)
        perfdata = []

        param = params.get(description.lower().replace(' ', '_'))

        if type_ == 'option':
            if param and param != value:
                state = 1
                infotext += ' (expected %s)' % param
        elif type_ == 'level':
            warn, crit = param  # a default level hat to exist
            if value >= crit:
                state = 2
            elif value >= warn:
                state = 1

            if state:
                infotext += ' (warn/crit at %s/%s)' % (warn, crit)
            factor = 1e3 if unit == 'ms' else 1e6
            perfdata = [('rtt', value / factor, warn / factor, crit / factor)]

        yield state, infotext, perfdata


check_info['cisco_ip_sla'] = {
    'parse_function':
        parse_cisco_ip_sla,
    'inventory_function':
        inventory_cisco_ip_sla,
    'check_function':
        check_cisco_ip_sla,
    'service_description':
        'Cisco IP SLA %s',
    'group':
        'cisco_ip_sla',
    'default_levels_variable':
        'cisco_ip_sla_default_levels',
    'has_perfdata':
        True,
    'snmp_scan_function':
        lambda oid: oid('.1.3.6.1.2.1.1.2.0') in [
            '.1.3.6.1.4.1.9.1.2068',
            '.1.3.6.1.4.1.9.1.1858',
        ],
    'snmp_info': [
        (
            '.1.3.6.1.4.1.9.9.42.1.2.2.1',
            [
                BINARY(2),  # rttMonEchoAdminTargetAddress
                BINARY(6),  # rttMonEchoAdminSourceAddress
                # only needed to determine the unit (ms/us)
                37,  # rttMonEchoAdminPrecision
            ]),
        (
            '.1.3.6.1.4.1.9.9.42.1.2.1.1',
            [
                2,  # rttMonCtrlAdminOwner
                3,  # rttMonCtrlAdminTag
                4,  # rttMonCtrlAdminRttType
                5,  # rttMonCtrlAdminThreshold
            ]),
        (
            '.1.3.6.1.4.1.9.9.42.1.2.9.1',
            [
                10,  # rttMonCtrlOperState
                2,  # rttMonCtrlOperDiagText
                5,  # rttMonCtrlOperConnectionLostOccurred
                6,  # rttMonCtrlOperTimeoutOccurred
                7,  # rttMonCtrlOperOverThresholdOccurred
            ]),
        (
            '.1.3.6.1.4.1.9.9.42.1.2.10.1',
            [
                1,  # rttMonLatestRttOperCompletionTime
                2,  # rttMonLatestRttOperSense
            ]),
    ],
}
