#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2017             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["ddn_s2a_statsdelay_default_levels"] = {
    "read_avg"  : (0.1, 0.2),
    "write_avg" : (0.1, 0.2),
}


def parse_ddn_s2a_statsdelay(info):
    parsed = parse_ddn_s2a_api_response(info)

    for key in [ u"host_reads",
                 u"host_writes",
                 u"disk_reads",
                 u"disk_writes" ]:
        parsed[key] = map(int, parsed[key])

    # Regarding the special treatment of the >10.0 value here: This API does not provide
    # more detailed information than this. We assume a value of 30, but we really have no way
    # of knowing. These events are usually very rare (~two in a million).
    parsed[u"time_interval_in_seconds"] = map(lambda x: float(x) if x != ">10.0" else 30,
                                          parsed[u"time_interval_in_seconds"])
    return parsed


def inventory_ddn_s2a_statsdelay(parsed):
    yield "Disk", {}
    yield "Host", {}


def check_ddn_s2a_statsdelay(item, params, parsed):

    # The API gives information about the delay statistics in a histogram,
    # binning read/write events by their delay. The individual bins are
    # counters. To get a picture of the current delay stats, we subtract
    # the previous check period's histogram from the current one. This means
    # that the averaging in particular is always across one check period,
    # and the respective min and max values refer to events within the last
    # check period.

    def subtract_histograms(histogram1, histogram2):
        return [ v1 - v2 for v1, v2 in zip(histogram1, histogram2) ]


    def is_zero(histogram):
        return not any(histogram)


    def histogram_min(time_intervals, values):
        for interval, value in zip(time_intervals, values):
            if value != 0:
                return interval


    def histogram_max(time_intervals, values):
        for interval, value in zip(time_intervals, values)[::-1]:
            if value != 0:
                return interval


    def histogram_avg(time_intervals, values):
        number_of_values = 0
        total_time = 0.0
        for time_interval, value in zip(time_intervals, values):
            number_of_values += value
            total_time += time_interval * value
        return total_time/number_of_values


    def check_levels(infotext_formatstring, value, levels, perfname):
        infotext = infotext_formatstring % value
        if levels:
            warn, crit = levels
            levelstext = " (warn/crit at %.2f/%.2f s)" % (warn, crit)
            perfdata = [ (perfname, value, warn, crit) ]
            if value >= crit:
                status = 2
                infotext += levelstext
            elif value >= warn:
                status = 1
                infotext += levelstext
            else:
                status = 0
        else:
            perfdata = [ (perfname, value) ]
            status = 0
        return status, infotext, perfdata

    time_intervals = parsed[u"time_interval_in_seconds"]
    if item == "Disk":
        reads = parsed[u"disk_reads"]
        writes = parsed[u"disk_writes"]
    elif item == "Host":
        reads = parsed[u"host_reads"]
        writes = parsed[u"host_writes"]

    old_intervals = get_item_state("time_intervals")
    old_reads = get_item_state("reads")
    old_writes = get_item_state("writes")

    set_item_state("time_intervals", time_intervals)
    set_item_state("reads", reads)
    set_item_state("writes", writes)

    if old_intervals is None:
        raise MKCounterWrapped("Initializing")
    elif old_intervals != time_intervals:
        raise MKCounterWrapped("Histograms not comparable - Time intervals have changed. Reinitializing.")

    reads_since_last_check = subtract_histograms(reads, old_reads)
    writes_since_last_check = subtract_histograms(writes, old_writes)
    if is_zero(reads_since_last_check) and is_zero(writes_since_last_check):
        raise MKCounterWrapped("No writes or reads since last check")

    if not is_zero(reads_since_last_check):
        read_min = histogram_min(time_intervals, reads_since_last_check)
        read_max = histogram_max(time_intervals, reads_since_last_check)
        read_avg = histogram_avg(time_intervals, reads_since_last_check)
    else:
        read_min, read_max, read_avg = 0, 0, 0

    yield check_levels("Average read wait: %.2f s", read_avg, params.get("read_avg"), "disk_average_read_wait")
    yield check_levels("Min. read wait: %.2f s", read_min, params.get("read_min"), "disk_min_read_wait")
    yield check_levels("Max. read wait: %.2f s", read_max, params.get("read_max"), "disk_max_read_wait")

    if not is_zero(writes_since_last_check):
        write_min = histogram_min(time_intervals, writes_since_last_check)
        write_max = histogram_max(time_intervals, writes_since_last_check)
        write_avg = histogram_avg(time_intervals, writes_since_last_check)
    else:
        write_min, write_max, write_avg = 0, 0, 0

    yield check_levels("Average write wait: %.2f s", write_avg, params.get("write_avg"), "disk_average_write_wait")
    yield check_levels("Min. write wait: %.2f s", write_min, params.get("write_min"), "disk_min_write_wait")
    yield check_levels("Max. write wait: %.2f s", write_max, params.get("write_max"), "disk_max_write_wait")


check_info['ddn_s2a_statsdelay'] = {
    'default_levels_variable'   : 'ddn_s2a_statsdelay_default_levels',
    'parse_function'            : parse_ddn_s2a_statsdelay,
    'inventory_function'        : inventory_ddn_s2a_statsdelay,
    'check_function'            : check_ddn_s2a_statsdelay,
    'service_description'       : 'DDN S2A Delay %s',
    'includes'                  : [ "ddn_s2a.include" ],
    'group'                     : 'ddn_s2a_wait',
    'has_perfdata'              : True,
}
