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

# <<<job>>>
# ==> asd ASD <==
# start_time 1389355839
# exit_code 0
# real_time 0:00.00
# user_time 0.00
# system_time 0.00
# reads 0
# writes 0
# max_res_kbytes 1968
# avg_mem_kbytes 0
#
#
# ==> test <==
# start_time 1389352839
# exit_code 0
# real_time 0:00.00
# user_time 0.00
# system_time 0.00
# reads 0
# writes 0
# max_res_kbytes 1984
# avg_mem_kbytes 0


factory_settings["job_default_levels"] = {
    "age": (0, 0) # disabled as default
}


def parse_job(info):
    def job_parse_real_time(s):
        parts = s.split(':')
        min_sec, hour_sec = 0, 0
        if len(parts) == 3:
            hour_sec = int(parts[0]) * 60 * 60
        if len(parts) >= 2:
            min_sec = int(parts[-2]) * 60
        return float(parts[-1]) + min_sec + hour_sec

    parsed = {}
    job = None
    prefix = None
    for line in info:
        node_info = line[0]
        line = line[1:]
        if line[0] == "==>" and line[-1] == "<==":
            jobname = " ".join(line[1:-1])
            if jobname.endswith(".running"):
                jobname, jobstate = jobname.rsplit(".", 1)
                prefix = "running_"
            else:
                jobstate = None
                prefix = ""
            job = parsed.setdefault(jobname, {}).setdefault(node_info, {
                "state": jobstate,
            })

        elif job is not None and prefix is not None and len(line) == 2:
            key, val = line
            # Convert several keys/values
            if key == 'real_time':
                val = job_parse_real_time(val)
            elif key in [ 'user_time', 'system_time' ]:
                val = float(val)
            elif key in [ 'exit_code', 'invol_context_switches', 'vol_context_switches', 'start_time' ]:
                val = int(val)
            elif key in [ 'max_res_kbytes', 'avg_mem_kbytes' ]:
                key = key.replace('kbytes', 'bytes')
                val = int(val) * 1000
            # TODO: Fix the code and remove the pragma below!
            job[prefix+key] = val   # pylint: disable=unsupported-assignment-operation

    return parsed


def inventory_job(parsed):
    for jobname, nodes in parsed.iteritems():
        for jobattrs in nodes.values():
            if jobattrs["state"] != "running":
                yield jobname, {}


def check_job(item, params, parsed):
    def process_start_time(value, state, warn, crit):
        display_value = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(value) )
        job_age = time.time() - value
        if crit > 0 and job_age >= crit:
            state = max(state, 2)
            display_value += "(!!) (more than %s ago)" % get_age_human_readable(crit)
        elif warn > 0 and job_age >= warn:
            state = max(state, 1)
            display_value += "(!!) (more than %s ago)" % get_age_human_readable(warn)
        return state, display_value

    if item not in parsed:
        yield 3, 'Got no information for this job'
        return

    states = []
    nodes = parsed[item]
    warn, crit = params.get('age')
    results = []
    for node_info, job in nodes.iteritems():
        if node_info:
            node_infotext = "[%s] " % node_info
        else:
            node_infotext = ""


        if job.get("exit_code") is None:
            results.append((3, '%sGot incomplete information for this job' % node_infotext))
            states.append(3)
            continue

        state = 0
        output = []
        perfdata = []

        if 'running_start_time' in job:
            output.append('%sCurrently running' % node_infotext)
            state, display_value = process_start_time(job['running_start_time'], state, warn, crit)
            output.append('(Started: %s)' % display_value)
            results.append((state, ' '.join(output)))
            states.append(state)
            continue

        txt = '%sExit-Code: %d' % (node_infotext, job['exit_code'])
        if job['exit_code'] != 0:
            state = max(state, 2)
            txt += ' (!!)'
        output.append(txt)

        for key, title in [
            ('start_time',             'Started'),
            ('real_time',              'Real-Time'),
            ('user_time',              'User-Time'),
            ('system_time',            'System-Time'),
            ('reads',                  'Filesystem Reads'),
            ('writes',                 'Filesystem Writes'),
            ('max_res_bytes',          'Max. Memory'),
            ('avg_mem_bytes',          'Avg. Memory'),
            ('vol_context_switches',   'Vol. Context Switches'),
            ('invol_context_switches', 'Invol. Context Switches'),
          ]:
            value = job.get(key)
            if value is None:
                continue

            if key in [ 'max_res_bytes', 'avg_mem_bytes' ]:
                display_value = get_bytes_human_readable(value, 1000)
            elif key in [ 'real_time', 'user_time', 'system_time' ]:
                display_value = get_age_human_readable(value)
            elif key == 'start_time':
                state, display_value = process_start_time(value, state, warn, crit)
            else:
                display_value = value

            output.append('%s: %s' % (title, display_value))
            perfdata.append((key, value))
        results.append((state, ', '.join(output), perfdata))
        states.append(state)

    if params.get("outcome_on_cluster", "worst") == "best":
        infotexts = []
        perfdata = []
        for result in results:
            infotexts.append(result[1])
            if len(result) > 2:
                perfdata += result[2]
        yield min(states), ", ".join(infotexts), perfdata
    else:
        for result in results:
            yield result


check_info["job"] = {
    'parse_function'            : parse_job,
    'check_function'            : check_job,
    'inventory_function'        : inventory_job,
    'service_description'       : 'Job %s',
    'default_levels_variable'   : 'job_default_levels',
    'group'                     : 'job',
    'has_perfdata'              : True,
    'node_info'                 : True,
}
