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

# WARNING: These checks are deprecated and will be removed soon.
# Please use jolokia_* instead

# MB warn, crit
j4p_performance_mem_default_levels      = (1000, 2000)
# Number of threads warn, crit
j4p_performance_threads_default_levels  = (80, 100)
# Number of sessions low crit, low warn, high warn, high crit
j4p_performance_app_sess_default_levels = (-1, -1, 800, 1000)
# Number of requests low crit, low warn, high warn, high crit
j4p_performance_serv_req_default_levels = (-1, -1, 5000, 6000)


def j4p_performance_parse(info):
    parsed = {}
    for inst, var, value in info:
        app, servlet = None, None
        if ',' in inst:
            parts = inst.split(',')
            if len(parts) == 3:
                inst, app, servlet = parts
            else:
                inst, app = parts

        parsed.setdefault(inst, {})
        if servlet:
            parsed[inst].setdefault('apps', {})
            parsed[inst]['apps'][app].setdefault('servlets', {})
            parsed[inst]['apps'][app]['servlets'].setdefault(servlet, {})
            parsed[inst]['apps'][app]['servlets'][servlet][var] = value
        elif app:
            parsed[inst].setdefault('apps', {})
            parsed[inst]['apps'].setdefault(app, {})
            parsed[inst]['apps'][app][var] = value
        else:
            parsed[inst][var] = value
    return parsed


def j4p_performance_app(info, (inst, app)):
    parsed = j4p_performance_parse(info)
    if not inst in parsed \
       or not app in parsed[inst].get('apps', {}):
        return None
    return parsed[inst]['apps'][app]


def j4p_performance_serv(info, (inst, app, serv)):
    app = j4p_performance_app(info, (inst, app))
    if not app or not serv in app.get('servlets', {}):
        return None
    return app['servlets'][serv]


def inventory_j4p_performance(info, what):
    parsed = j4p_performance_parse(info)
    levels = None
    if what == 'mem':
        levels = 'j4p_performance_mem_default_levels'
    elif what == 'threads':
        levels = 'j4p_performance_threads_default_levels'
    return [ (k, levels) for k in parsed ]


def inventory_j4p_performance_apps(info, what):
    inv = []
    parsed = j4p_performance_parse(info)
    levels = None
    if what == 'app_sess':
        levels = 'j4p_performance_app_sess_default_levels'
    for inst, vals in parsed.iteritems():
        for app in vals.get('apps', {}).keys():
            inv.append(('%s %s' % (inst, app), levels))
    return inv


def inventory_j4p_performance_serv(info, what):
    inv = []
    parsed = j4p_performance_parse(info)
    levels = None
    if what == 'serv_req':
        levels = 'j4p_performance_serv_req_default_levels'
    for inst, vals in parsed.iteritems():
        for app, val in vals.get('apps', {}).iteritems():
            for serv in val.get('servlets', {}).keys():
                inv.append(('%s %s %s' % (inst, app, serv), levels))
    return inv


def check_j4p_performance_mem(item, params, info):
    warn, crit = params
    parsed = j4p_performance_parse(info)
    if item not in parsed:
        return (3, "data not found in agent output")
    d = parsed[item]
    mb = 1024 * 1024.0
    heap = saveint(d["HeapMemoryUsage"]) / mb
    non_heap = saveint(d["NonHeapMemoryUsage"]) / mb
    total = heap + non_heap
    perfdata = [ ("heap",    heap,     warn, crit),
                 ("nonheap", non_heap, warn, crit) ]
    infotext = "%.0f MB total (%.0f MB heap, %.0f MB non-heap) (warn/crit at %.0f/%.0f)" % (total, heap, non_heap, warn, crit)
    if total >= crit:
        return (2, infotext, perfdata)
    elif total >= warn:
        return (1, infotext, perfdata)
    else:
        return (0, infotext, perfdata)

check_info["j4p_performance.mem"] = {'check_function': check_j4p_performance_mem,
 'default_levels_variable': None,
 'group': 'j4p_performance.mem',
 'has_perfdata': True,
 'inventory_function': lambda i: inventory_j4p_performance(i, "mem"),
 'node_info': False,
 'service_description': 'JMX %s Memory',
 'snmp_info': None,
 'snmp_scan_function': None}


def check_j4p_performance_threads(item, params, info):
    warn, crit = params
    parsed = j4p_performance_parse(info)
    if item not in parsed:
        return (3, "data not found in agent output")
    d = parsed[item]

    this_time = time.time()
    perfdata = []
    output   = []
    status   = 0
    for key in [ 'ThreadCount', 'DeamonThreadCount', 'PeakThreadCount', 'TotalStartedThreadCount' ]:
        val = saveint(d[key])
        if key == 'ThreadCount':
            # Thread count might lead to a warn/crit state
            if val >= crit:
                status = 2
            elif val >= warn:
                status = 1

            # Calculate the thread increase rate
            rate = get_rate("j4p_performance.threads.%s" % item, this_time, val)
            output.append('ThreadRate: %0.2f' % rate)
            perfdata.append(('ThreadRate', rate))

        perfdata.append((key, val))
        output.append('%s: %d' % (key, val))

    return (status, ', '.join(output), perfdata)

check_info["j4p_performance.threads"] = {'check_function': check_j4p_performance_threads,
 'default_levels_variable': None,
 'group': 'j4p_performance.threads',
 'has_perfdata': True,
 'inventory_function': lambda i: inventory_j4p_performance(i, "threads"),
 'node_info': False,
 'service_description': 'JMX %s Threads',
 'snmp_info': None,
 'snmp_scan_function': None}


def check_j4p_performance_uptime(item, _unused, info):
    parsed = j4p_performance_parse(info)
    if item not in parsed:
        return (3, "data not found in agent output")
    uptime = saveint(parsed[item]['Uptime']) / 1000

    seconds = uptime % 60
    rem = uptime / 60
    minutes = rem % 60
    hours = (rem % 1440) / 60
    days = rem / 1440
    now = int(time.time())
    since = time.strftime("%c", time.localtime(now - uptime))
    return (0, "up since %s (%dd %02d:%02d:%02d)" % (since, days, hours, minutes, seconds), [ ("uptime", uptime) ])

check_info["j4p_performance.uptime"] = {'check_function': check_j4p_performance_uptime,
 'default_levels_variable': None,
 'group': 'j4p_performance.uptime',
 'has_perfdata': True,
 'inventory_function': lambda i: inventory_j4p_performance(i, "uptime"),
 'node_info': False,
 'service_description': 'JMX %s Uptime',
 'snmp_info': None,
 'snmp_scan_function': None}


def check_j4p_performance_app_state(item, _unused, info):
    app = j4p_performance_app(info, item.split())
    if not app or not 'Running' in app:
        return (3, "data not found in agent output")

    if app['Running'] == '1':
        return (0, 'application is running')
    else:
        return (2, 'application is not running (Running: %s)')


check_info["j4p_performance.app_state"] = {'check_function': check_j4p_performance_app_state,
 'default_levels_variable': None,
 'group': 'j4p_performance.app_state',
 'has_perfdata': False,
 'inventory_function': lambda i: inventory_j4p_performance_apps(i, "app_state"),
 'node_info': False,
 'service_description': 'JMX %s State',
 'snmp_info': None,
 'snmp_scan_function': None}


def check_j4p_performance_app_sess(item, params, info):
    lo_crit, lo_warn, hi_warn, hi_crit = params
    app = j4p_performance_app(info, item.split())
    if not app or not 'Sessions' in app:
        return (3, "data not found in agent output")
    sess = saveint(app['Sessions'])

    status = 0
    status_txt = ''
    if lo_crit is not None and sess <= lo_crit:
        status = 2
        status_txt = ' (Below or equal %d)' % lo_crit
    elif lo_warn is not None and sess <= lo_warn:
        status = 1
        status_txt = ' (Below or equal %d)' % lo_warn
    elif hi_crit is not None and sess >= hi_crit:
        status = 2
        status_txt = ' (Above or equal %d)' % lo_warn
    elif hi_warn is not None and sess >= hi_warn:
        status = 1
        status_txt = ' (Above or equal %d)' % lo_crit

    return (status, '%d Sessions%s' % (sess, status_txt),
            [('sessions', sess, hi_warn, hi_crit)])

check_info["j4p_performance.app_sess"] = {'check_function': check_j4p_performance_app_sess,
 'default_levels_variable': None,
 'group': 'j4p_performance.app_sess',
 'has_perfdata': True,
 'inventory_function': lambda i: inventory_j4p_performance_apps(i, "app_sess"),
 'node_info': False,
 'service_description': 'JMX %s Sessions',
 'snmp_info': None,
 'snmp_scan_function': None}


def check_j4p_performance_serv_req(item, params, info):
    lo_crit, lo_warn, hi_warn, hi_crit = params
    serv = j4p_performance_serv(info, item.split())
    if not serv or not 'Requests' in serv:
        return (3, "data not found in agent output")
    req = saveint(serv['Requests'])

    status    = 0
    status_txt = ''
    if lo_crit is not None and req <= lo_crit:
        status = 2
        status_txt = ' (Below or equal %d)' % lo_crit
    elif lo_warn is not None and req <= lo_warn:
        status = 1
        status_txt = ' (Below or equal %d)' % lo_warn
    elif hi_crit is not None and req >= hi_crit:
        status = 2
        status_txt = ' (Above or equal %d)' % lo_warn
    elif hi_warn is not None and req >= hi_warn:
        status = 1
        status_txt = ' (Above or equal %d)' % lo_crit

    output    = ['Requests: %d%s' % (req, status_txt)]
    perfdata  = [('Requests', req, hi_warn, hi_crit)]
    this_time = time.time()
    rate = get_rate("j4p_performance.serv_req.%s" % item, this_time, req)
    output.append('RequestRate: %0.2f' % rate)
    perfdata.append(('RequestRate', rate))
    return (status, ', '.join(output), perfdata)

check_info["j4p_performance.serv_req"] = {'check_function': check_j4p_performance_serv_req,
 'default_levels_variable': None,
 'group': 'j4p_performance.serv_req',
 'has_perfdata': True,
 'inventory_function': lambda i: inventory_j4p_performance_serv(i, "serv_req"),
 'node_info': False,
 'service_description': 'JMX %s Requests',
 'snmp_info': None,
 'snmp_scan_function': None}
