#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2013             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.

# <<<diskstat>>>
# 1300264105
#    8       0 sda 691860 951191 13559915 491748 234686 197346 3359512 94944 0 56844 586312
#    8      32 sdb 791860 91191 23589915 491748 234686 197346 3359512 94944 0 56844 586312

# Newer agent output also dm-* and Veritas devices and if
# available the following additional information for name rewriting:

# <<<diskstat>>>
# 1338931242
#    8       0 sda 6142 327 219612 2244 3190 6233 74075 8206 0 6523 10446
#  253       0 dm-0 4579 0 181754 2343 9249 0 73960 259491 0 1208 261833
#  253       1 dm-1 342 0 2736 47 3 0 11796464 5016 0 5063 5063
#  253       2 dm-2 160 0 1274 27 11 0 56 3 0 27 30
#    8      16 sdb 464 858 7717 336 1033 0 311454 3899 0 3007 4231
#    8      32 sdc 855 13352 106777 1172 915 0 154467 2798 0 3012 3967
#    8      48 sdd 1217 861 109802 1646 118 0 56151 1775 0 2736 3420
#    8      80 sdf 359 1244 58323 792 66 0 4793 388 0 765 1178
#    8      64 sde 310 1242 6964 268 118 0 56151 1607 0 1307 1872
#    8      96 sdg 1393 1242 314835 3759 129 0 56172 1867 0 4027 5619
#  199   27000 VxVM27000 131 0 990 61 11 0 21 29 0 89 90
#  199   27001 VxVM27001 0 0 0 0 0 0 0 0 0 0 0
# [dmsetup_info]
# vg_zwei-lv_home 253:2 vg_zwei lv_home
# vg_zwei-lv_swap 253:1 vg_zwei lv_swap
# vg_zwei-lv_root 253:0 vg_zwei lv_root
# [vx_dsk]
# c7 6978 /dev/vx/dsk/datadg/lalavol
# c7 6979 /dev/vx/dsk/datadg/oravol

# Fields in /proc/diskstats
#  Index 0 -- major number
#  Index 1 -- minor number
#  Index 2 -- device name                        --> used by check
#  Index 3 -- # of reads issued
#  Index 4 -- # of reads merged
#  Index 5 -- # of sectors read (a 512 Byte)     --> used by check
#  Index 6 -- # of milliseconds spent reading
#  Index 7 -- # of writes completed
#  Index 8 -- # of writes merged
#  Index 9 -- # of sectors written (a 512 Byte)  --> used by check
#  Index 10 -- # of milliseconds spent writing
#  Index 11 -- # of I/Os currently in progress
#  Index 12 -- # of milliseconds spent doing I/Os
#  Index 13 -- weighted # of milliseconds spent doing I/Os

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

# Convert information to generic format also generated
# by winperf_phydisk
# [ now, [( disk, readctr, writectr ), ... ]]
# where counters are in sectors (512 bytes)

def diskstat_parse_info(info):
    info_plain = []
    nameinfo = {}
    phase = 'info'
    for line in info:
        if line[0] == '[dmsetup_info]':
            phase = 'dmsetup_info'
        elif line[0] == '[vx_dsk]':
            phase = 'vx_dsk'
        else:
            if phase == 'info':
                info_plain.append(line)
            elif phase == 'dmsetup_info':
                try:
                    majmin = tuple(map(int, line[1].split(':')))
                    if len(line) == 4:
                        name = "LVM %s" % line[0]
                    else:
                        name = "DM %s" % line[0]
                    nameinfo[majmin] = name
                except:
                    pass # ignore such crap as "No Devices Found"
            elif phase == 'vx_dsk':
                maj = int(line[0], 16)
                min = int(line[1], 16)
                group, disk = line[2].split('/')[-2:]
                name = "VxVM %s-%s" % (group, disk)
                nameinfo[(maj, min)] = name

    return info_plain, nameinfo

def diskstat_rewrite_device(nameinfo, linestart):
    major, minor = map(int, linestart[:2])
    device = linestart[2]
    return nameinfo.get((major, minor), device)

def linux_diskstat_convert(info):
    info, nameinfo = diskstat_parse_info(info)
    # The generic function takes the following values per line:
    #  0: devname
    #  1: read bytes counter
    #  2: write bytes counter
    # Optional ones:
    #  3: number of reads
    #  4: number of writes
    #  5: timems
    #  6: read queue length *counters*
    #  7: write queue length *counters*
    rewritten = [
        ( diskstat_rewrite_device(nameinfo, l[0:3]),
        int(l[5]),
        int(l[9]),
        int(l[3]),
        int(l[7]),
        int(l[12])
        ) for l in info[1:] if len(l) >= 13
    ]

    # Remove device mapper devices without a translated name
    return [ line for line in rewritten if not line[0].startswith("dm-") ]

def inventory_diskstat(info):
    return inventory_diskstat_generic(linux_diskstat_convert(info))

def check_diskstat(item, params, info):
    return check_diskstat_generic(item, params, int(info[0][0]), linux_diskstat_convert(info))

check_info['diskstat'] = (check_diskstat, "Disk IO %s", 1,  inventory_diskstat)
checkgroup_of["diskstat"] = "disk_io"

