#!/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 for output from agent (contents of /proc/mdstat):
# ---------------------------------------------------------
#    Personalities : [raid1]
#    md1 : active raid1 dm-19[0] dm-9[1]
#          20971456 blocks [2/2] [UU]
#          20971456 blocks super 1.2 [2/2] [UU]
#
#    md2 : active (auto-read-only) raid1 sda6[0] sdb6[1]
#          4200952 blocks super 1.0 [2/2] [UU]
#          bitmap: 0/9 pages [0KB], 256KB chunk
#
#          unused devices: <none>
# ---------------------------------------------------------

# Another example (with RAID 5 and spare disk (md2) and a RAID-0
# device (md3)
# ---------------------------------------------------------
# Personalities : [raid1] [raid6] [raid5] [raid4]
# md2 : active raid5 sde1[3](S) sdd1[0] sdg1[2] sdf1[1]
#       976767872 blocks level 5, 64k chunk, algorithm 2 [3/3] [UUU]
#
# md0 : active raid1 sdb1[1] sda1[0]
#       104320 blocks [2/2] [UU]
#
# md1 : active raid1 sdb3[1] sda3[0]
#       486239232 blocks [2/2] [UU]
#
# md4 : active (auto-read-only) raid1 sda6[0] sdb6[1]
#       4200952 blocks super 1.0 [2/2] [UU]
#         resync=PENDING
#       bitmap: 9/9 pages [36KB], 256KB chunk
#
# md3 : active raid0 sdb3[0] sda3[1]
#       16386048 blocks 64k chunks
#
# unused devices: <none>
# ---------------------------------------------------------

# Another example with RAID1 replacement gone wrong
# ---------------------------------------------------------
# Personalities : [raid1]
# md0 : active raid1 sdc3[3] sda3[2](F) sdb3[1]
#       48837528 blocks super 1.0 [2/2] [UU]
#
# md1 : active raid1 sdc4[3] sda4[2](F) sdb4[1]
#       193277940 blocks super 1.0 [2/2] [UU]
#
# unused devices: <none>
# ----------------------------------------------------------

# Another example with RAID5 being recovered
# ---------------------------------------------------------
# Personalities : [raid1] [raid6] [raid5] [raid4]
# md1 : active raid1 sdd1[1] sdc1[0]
#       10484668 blocks super 1.1 [2/2] [UU]
#       bitmap: 1/1 pages [4KB], 65536KB chunk
#
# md127 : active raid5 sda3[0] sdb3[1] sdd3[4] sdc3[2]
#       11686055424 blocks super 1.2 level 5, 512k chunk, algorithm 2 [4/3] [UUU_]
#       [======>..............]  recovery = 31.8% (1241578496/3895351808) finish=746.8min speed=59224K/sec
#
# md0 : active raid1 sdb1[1] sda1[0]
#       10485688 blocks super 1.0 [2/2] [UU]
#       bitmap: 0/1 pages [0KB], 65536KB chunk
#
# unused devices: <none>
# ----------------------------------------------------------

# And now for something completely different:
# ---------------------------------------------------------
# Personalities : [raid1] [raid10]
# md1 : active raid10 sdd6[3] sdb6[1] sda6[0]
#       1463055360 blocks 64K chunks 2 near-copies [4/3] [UU_U]
#
# md0 : active raid1 sdd1[3] sdb1[1] sda1[0]
#       104320 blocks [4/3] [UU_U]
#
# unused devices: <none>
# ---------------------------------------------------------

# TODO: Write a parse function!

def inventory_md(info):
    inventory = []
    for line in info:
        if len(line) < 3:
            continue
        if line[0].startswith("md") and line[1] == ':':
            device = line[0]
            raid_state = line[2]
        elif line[1] == 'blocks':
            if line[-1] != "chunks": # ignore RAID 0 devices
                disk_state = line[-1][1:-1]
                inventory.append( (device, None) )
    return inventory


def check_md(item, _no_params, info):
    raid_state = ''
    its_next = False
    state_next = False
    for line in info:
        if line[0] == item and line[1] == ':':
            raid_state = line[2]
            if raid_state != 'active' and raid_state != 'active(auto-read-only)':
                return (2, "raid state is '%s' (should be 'active')" % (raid_state,))
            # Usually (auto-read-only) sticks to active without a space.
            # But on some kernels it appears separated by a space
            if line[3] == '(auto-read-only)':
                del line[3]
            all_disks = len([x for x in line[4:]]) # all disks
            spare_disks = len([x for x in line[4:] if x.endswith("(S)") ]) # spare disks
            failed_disks = len([x for x in line[4:] if x.endswith("(F)") ]) # failed disks
            active_disks = all_disks - spare_disks - failed_disks
            its_next = True
        elif its_next:
            disk_state_1 = line[-2]
            (num_disks, expected_disks) = map(int,disk_state_1[1:-1].split('/'))
            disk_state_2 = line[-1]
            working_disks = disk_state_2.count('U')
            state_next = True
            its_next = False
        elif state_next:
            if num_disks == expected_disks and active_disks == working_disks:
                return (0, 'raid active, disk state is %s %s' % (disk_state_1, disk_state_2))
            if len(line) > 6 and line[-6] != '' and "speed=" in line[-1]:
                build_state_1 = line[-6]
                build_state_2 = line[-4]
                build_est = line[-2].partition('=')[2]
                build_speed = float(line[-1].partition('=')[2][:-5]) / 1024
                return (1, 'disk state is %s %s (expected %d disks to be up) - %s %s @ %.1fMB/s (%s)' %
                           (disk_state_1, disk_state_2, expected_disks, build_state_1, build_state_2, build_speed, build_est))
            return (2, 'disk state is %s %s (expected %d disks to be up)' %
                            (disk_state_1, disk_state_2, expected_disks))
    return (2, 'no RAID device %s' % item)



check_info["md"] = {
    'check_function':          check_md,
    'inventory_function':      inventory_md,
    'service_description':     'MD Softraid %s',
    'group':                   'raid',
}
