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

# Example output from agent
# <<<winperf_phydisk>>>
# 1352457622.31 234
# 2 instances: 0_C: _Total
# -36 0 0 rawcount
# -34 235822560 235822560 type(20570500)
# -34 2664021277 2664021277 type(40030500)
# 1166 235822560 235822560 type(550500)        ----> Average Disk Queue Length
# -32 109877176 109877176 type(20570500)
# -32 2664021277 2664021277 type(40030500)
# 1168 109877176 109877176 type(550500)        ----> Average Disk Read Queue Length
# -30 125945384 125945384 type(20570500)
# -30 2664021277 2664021277 type(40030500)
# 1170 125945384 125945384 type(550500)        ----> Average Disk Write Queue Length
# -28 3526915777 3526915777 average_timer
# -28 36283 36283 average_base
# -26 1888588843 1888588843 average_timer     ----> Average Disk Seconds/Read
# -26 17835 17835 average_base
# -24 1638326933 1638326933 average_timer
# -24 18448 18448 average_base                ----> Average Disk Seconds/Write
# -22 36283 36283 counter
# -20 17835 17835 counter                     ----> Disk Reads/sec
# -18 18448 18448 counter                     ----> Disk Writes/sec
# -16 1315437056 1315437056 bulk_count
# -14 672711680 672711680 bulk_count          ----> Disk Read Bytes/sec
# -12 642725376 642725376 bulk_count          ----> Disk Write Bytes/sec
# -10 1315437056 1315437056 average_bulk
# -10 36283 36283 average_base
# -8 672711680 672711680 average_bulk
# -8 17835 17835 average_base
# -6 642725376 642725376 average_bulk
# -6 18448 18448 average_base
# 1248 769129229 769129229 type(20570500)
# 1248 2664021277 2664021277 type(40030500)
# 1250 1330 1330 counter

check_includes['winperf_phydisk'] = [ "diskstat.include" ]

def winperf_phydisk_convert(info):
    # node_info has been activated. This check simply ignores this
    # for now.
    # In case disk performance counters are not enabled, the agent sends
    # an almost empty section, where the second line is missing completely
    def disk_perfcounters_disabled(info):
        nodes = set([ l[0] for l in info ])
        return len(nodes) == len(info)

    if disk_perfcounters_disabled(info):
        return []

    lines = iter(info)
    entries = []

    def finalize_block(nodename):
        # Missing columns are donted by negative values (Linux sends here latency information)
        return zip(
            [nodename for x in current_disks],
            current_disks                        or [],
            current_disk_read_bytes              or [],
            current_disk_write_bytes             or [],
            current_disk_reads                   or [],
            current_disk_writes                  or [],
            [-1 for x in current_disks],
            current_disk_readq_ctrs              or [],
            current_disk_writeq_ctrs             or [],
            current_disk_seconds_per_read        or [],
            current_disk_seconds_per_read_base   or [],
            current_disk_seconds_per_write       or [],
            current_disk_seconds_per_write_base  or []
        )


    current_node = ""
    try:
        while True:
            line = lines.next()
            if line[2] == "instances:":
                if current_node != "":
                    entries.extend(finalize_block(current_node))
                current_node = line[0]
                current_disks = []
                for disk_id in line[3:-1]:
                    disk_id = disk_id.split('_')

                    if disk_id[-1] not in current_disks:
                        disk_id = disk_id[-1]
                    else:
                        disk_id = "%s_%s" % (disk_id[-1], disk_id[0])
                    current_disks.append(disk_id)
            elif line[1] == '-14':
                current_disk_read_bytes  = map(int, line[2:-2])
            elif line[1] == '-12':
                current_disk_write_bytes = map(int, line[2:-2])
            elif line[1] == '-20':
                current_disk_reads       = map(int, line[2:-2])
            elif line[1] == '-18':
                current_disk_writes      = map(int, line[2:-2])
            elif line[1] == '1168': # Average Disk Read Queue Length
                current_disk_readq_ctrs  = map(int, line[2:-2])
            elif line[1] == '1170': # Average Disk Read Queue Length
                current_disk_writeq_ctrs = map(int, line[2:-2])
            elif line[1] == '-24':
                if line[-1] == 'average_base':
                    current_disk_seconds_per_write_base = map(int, line[2:-2])
                else:
                    current_disk_seconds_per_write = map(int, line[2:-2])
            elif line[1] == '-26':
                if line[-1] == 'average_base':
                    current_disk_seconds_per_read_base = map(int, line[2:-2])
                else:
                    current_disk_seconds_per_read = map(int, line[2:-2])
    except StopIteration:
        if current_node != "":
            entries.extend(finalize_block(current_node))
        pass

    return entries

def inventory_winperf_phydisk(info):
    return inventory_diskstat_generic(winperf_phydisk_convert(info))

def check_winperf_phydisk(item, params, info):
    init_line = info[0]
    if len(init_line) >= 4:
        frequency = int(init_line[3])
    else:
        frequency = None

    this_time = float(init_line[1])

    # convert input data to a dictionary. This also calculates rates, the non-dict
    # version of check_diskstat calculates those itself. Also, this means we don't create
    # useless counters during inventory which is neater
    def dictify(converted_info):
        result = {}

        for disk in converted_info:
            disk_id = "%s_%s" % (disk[0], disk[1])


            result[disk[1]] = {
                'node'               : disk[0],
                'read_ios'           : get_rate("readios_" + disk_id, this_time, disk[4]),
                'read_throughput'    : get_rate("readtp_" + disk_id, this_time, disk[2]),
                'write_ios'          : get_rate("writeios_" + disk_id, this_time, disk[5]),
                'write_throughput'   : get_rate("writetp_" + disk_id, this_time, disk[3]),
            }
            if frequency:
                # using 1 for the base if the counter didn't increase. This makes little to no sense
                sec_per_read_counter  = get_rate("spr_counter_" + disk_id, this_time, disk[9])
                sec_per_read_base     = get_rate("spr_base_" + disk_id, this_time, disk[10]) or 1
                sec_per_write_counter = get_rate("spw_counter_" + disk_id, this_time, disk[11])
                sec_per_write_base    = get_rate("spw_base_" + disk_id, this_time, disk[12]) or 1
                result[disk[1]].update({
                    'average_read_wait'  : (sec_per_read_counter / frequency) / sec_per_read_base,
                    'average_write_wait' : (sec_per_write_counter / frequency) / sec_per_write_base,
                })

        return result

    return check_diskstat_dict(item, params, dictify(winperf_phydisk_convert(info)))



check_info["winperf_phydisk"] = {
    'check_function':          check_winperf_phydisk,
    'inventory_function':      inventory_winperf_phydisk,
    'service_description':     'Disk IO %s',
    'node_info':               True,
    'has_perfdata':            True,
    'group':                   'disk_io',
}
