#!/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-
# ails.  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.

# This map converts between the SAP color codes (key values) and the
# nagios state codes
sap_nagios_state_map = [
    0, # GRAY  (inactive or no current info available) -> OK
    0, # GREEN  -> OK
    1, # YELLOW -> WARNING
    2, # RED    -> CRITICAL
]

def inventory_sap_dialog(info):
    inv = []
    for line in info:
        if line[3] == 'SAP CCMS Monitor Templates/Dialog Overview/Dialog Response Time/ResponseTime':
            inv.append((line[0], {}))
    return inv

def check_sap_dialog(item, params, info):
    # First xtract all infos
    dialog = {}
    for line in info:
        if line[0] == item and line[3].startswith('SAP CCMS Monitor Templates/Dialog Overview/'):
            key = line[3].split('/')[-1]
            perfval = savefloat(line[4])
            uom = line[5]
            dialog[key] = perfval, uom

    if not dialog:
        return (3, 'Unable to find dialog monitoring data in agent output')

    def perf_clean_key(s):
        return s.replace('(', '_').replace(')', '_').replace(' ', '_').replace('.', '_').rstrip('_')

    # {'UsersLoggedIn': (2, '-'), 'ResponseTime(StandardTran.)': (6, 'msec'), 'FrontEndNetTime': (0, 'msec'), 'ResponseTime': (77, 'msec')}
    perfdata = [ (perf_clean_key(k), v[0]) for k, v in dialog.items() ]
    output = ', '.join([ '%s: %d%s' % (k, v[0], (v[1] != '-' and v[1] or '')) for k, v in dialog.items() ])
    return (0, output, perfdata)

check_info['sap.dialog'] = {
    "check_function"      : check_sap_dialog,
    "inventory_function"  : inventory_sap_dialog,
    "service_description" : "%s Dialog",
    "has_perfdata"        : True,
}

#
# Simple processing of nodes reported by sap agent
#

# Holds a list of rules which are matching hosts by names or tags
# and where each rule holds a dictionary of paramteters
#
# at the moment the following parameters are supported:
# 1. match: the sap-path matching definition
inventory_sap_values = []
sap_value_groups = []
def inventory_sap_value(info):
    inv = []

    patterns = []
    for rule in inventory_sap_values:
        taglist, hostlist = rule[1:3]
        # Filter out entries with do not match our current host
        if not hosttags_match_taglist(tags_of_host(g_hostname), taglist) \
           or not in_extraconf_hostlist(hostlist, g_hostname):
            continue

        v = rule[0]
        patterns.append((v['match'], v.get('limit_item_levels')))

    for line in info:
        for pattern, limit_item_levels in patterns:
            if sap_value_path_matches(line[3], pattern):
                params = {}
                if limit_item_levels:
                    path = '/'.join(line[3].split('/')[-limit_item_levels:])
                    params['limit_item_levels'] = limit_item_levels
                else:
                    path = line[3]
                inv.append((line[0] + ' ' + path, params))

    return inv

def sap_value_path_matches(path, pattern):
    if pattern is None:
        return True
    elif pattern[0] != '~' and path == pattern:
        # exact path match
        return True
    elif pattern[0] == '~':
        # regex matching
        pattern = pattern[1:]
        reg = compiled_regexes.get(pattern)
        if not reg:
            reg = re.compile(pattern)
            compiled_regexes[pattern] = reg
        matchobject = reg.match(path)
        if matchobject:
            return True
    return False

def check_sap_value(item, params, info):
    status = None
    perfdata = []
    for line in info:
        if params.get('limit_item_levels'):
            this_path = '/'.join(line[3].split('/')[-params.get('limit_item_levels'):])
        else:
            this_path = line[3]

        if line[0] + ' ' + this_path == item:
            status  = sap_nagios_state_map[int(line[1])]
            if line[4] != '-':
                # This is a performance value, has no output
                perfval  = savefloat(line[4])
                perfdata = [('value', perfval)]
                uom      = line[5]
                output   = "%0.2f%s" % (perfval, uom)
            else:
                # This is a status field without perfdata
                output  = ' '.join(line[6:])
            break

    if status is None:
        return (3, 'Unable to find the required value in agent output')

    return (status, output, perfdata)

check_info['sap.value'] = {
    "check_function"      : check_sap_value,
    "inventory_function"  : inventory_sap_value,
    "service_description" : "%s",
    "has_perfdata"        : True,
}

def sap_value_groups_precompile(hostname, item, _unused):
    return host_extra_conf(hostname, sap_value_groups)

def sap_groups_of_value( value_name, patterns=False ):
    groups = []
    if not patterns:
        patterns = host_extra_conf(g_hostname, sap_value_groups)

    for line in patterns:
        for group_name, pattern in line:
            no_match = False
            inclusion, exclusion = pattern
            # Exclude:
            if exclusion:
                reg = compiled_regexes.get(exclusion)
                if not reg:
                    reg = re.compile(exclusion)
                    compiled_regexes[exclusion] = reg
                if reg.match(value_name):
                    no_match = True
                    break
            #no match for this group, go on with the next group
            if no_match:
                break

            # Include
            reg = compiled_regexes.get(inclusion)
            if not reg:
                reg = re.compile(inclusion)
                compiled_regexes[inclusion] = reg
            if reg.match(value_name):
                groups.append(group_name)
    return groups

def inventory_sap_value_groups( info ):
    inventory = []
    found_groups = []
    for line in info:
        path = line[3]
        for group in sap_groups_of_value( path ):
            if group not in found_groups:
                inventory.append(( group, None ))
                found_groups.append(group)
    return inventory

def check_sap_value_groups( item, params, info ):
    frontend = []
    backend = []
    count_ok = 0
    count_crit = 0
    state = 0
    for line in info:
        path = line[3]
        if item in sap_groups_of_value( path, params ):
            status = sap_nagios_state_map[int(line[1])]
            output = ''
            if line[4] == '-':
                output  = ' '.join(line[6:])
            backend.append( path + output + " "  + nagios_state_names[status] )
            state = max(state, status)
            if status > 0:
                count_crit += 1
                frontend.append( path + output + " "  + nagios_state_names[status] )
            else:
                count_ok += 1

    return state, "%s OK, %s Crit %s\n%s" % ( count_ok, count_crit, ", ".join(frontend), "\n".join(backend) )


check_info['sap.value-groups'] = {
    'check_function':      check_sap_value_groups,
    'inventory_function':  inventory_sap_value_groups,
    'service_description': "%s",
}
precompile_params['sap.value-groups'] = sap_value_groups_precompile
