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

# <<<mssql_tablespaces>>>
# MSSQL_SQLEXPRESS master 5.25 MB 1.59 MB 2464 KB 1096 KB 1024 KB 344 KB
# MSSQL_SQLEXPRESS model 3.00 MB 1.13 MB 1152 KB 472 KB 632 KB 48 KB
# MSSQL_SQLEXPRESS msdb 18.13 MB 4.05 MB 10960 KB 8336 KB 2080 KB 544 KB
# MSSQL_SQLEXPRESS tempdb 2.75 MB 1.08 MB 1200 KB 480 KB 672 KB 48 KB
# MSSQL_SQLEXPRESS test123 4.00 MB 1.78 MB 1248 KB 528 KB 648 KB 72 KB

#  0: process instance
#  1: tablespace name
#  2: db size (Size of the current database in megabytes.
#     database_size includes both data and log files.)
#  3: uom
#  4: unallocated space (Space in the database that has not been reserved for database objects.)
#  5: uom
#  6: reserved space (Total amount of space allocated by objects in the database.)
#  7: uom
#  8: Total amount of space used by data.
#  9: uom
# 10: Total amount of space used by indexes.
# 11: uom
# 12: Total amount of space reserved for objects in the database, but not yet used.
# 13: uom

factory_settings["mssql_tablespace_default_levels"] = {}


def parse_mssql_tablespaces(info):
    def to_bytes(value, uom):
        exponent = {'KB': 1, 'MB': 2, 'GB': 3, 'TB': 4}.get(uom, 0)
        try:
            return float(value) * (1024**exponent)
        except ValueError:
            return None

    parsed = {}
    for line in info:
        if len(line) < 14:
            continue

        pairs = zip(line[:14:2], line[1:14:2])
        item = "%s %s" % pairs[0]
        keys = ('size', 'unallocated', 'reserved', 'data', 'indexes', 'unused')
        values = (to_bytes(*p) for p in pairs[1:])
        data = dict(zip(keys, values))

        if len(line) > 14 and line[14].startswith("ERROR: "):
            data["error"] = line[14][7:]

        parsed.setdefault(item, data)

    return parsed


def check_mssql_tablespaces(item, params, parsed):
    tablespace = parsed.get(item)
    if tablespace is None:
        # Assume general connection problem to the database, which is reported
        # by the "X Instance" service and skip this check.
        raise MKCounterWrapped("Tablespace not found")

    size = tablespace["size"]
    if "error" in tablespace:
        yield 2, tablespace["error"]

    if size is not None:
        levels = params.get("size", (None, None))
        yield check_levels(size,
                           'size',
                           levels,
                           human_readable_func=get_bytes_human_readable,
                           infoname="Size")

    for key, title, is_upper in [
        ('unallocated', 'Unallocated space', False),
        ('reserved', 'Reserved space', True),
        ('data', 'Data', True),
        ('indexes', 'Indexes', True),
        ('unused', 'Unused', True),
    ]:
        value_bytes = tablespace[key]
        if value_bytes is None:
            continue

        infotext = '%s: %s' % (title, get_bytes_human_readable(value_bytes))
        try:
            value_perc = 100.0 * value_bytes / size
            infotext += ", %s" % get_percent_human_readable(value_perc)
        except (TypeError, ZeroDivisionError):
            value_perc = None

        warn, crit = params.get(key, (None, None))
        if isinstance(crit, float) and value_perc is not None:
            value = value_perc
            readable_f = get_percent_human_readable
        elif isinstance(warn, int):
            value = value_bytes
            readable_f = get_bytes_human_readable
        else:
            yield 0, infotext
            continue

        state = 0
        if is_upper and value >= crit or not is_upper and value <= crit:
            state = 2
        elif is_upper and value >= warn or not is_upper and value <= warn:
            state = 1
        if state:
            infotext += " (warn/crit %s %s/%s)" % (
                ('below', 'at')[is_upper], readable_f(warn), readable_f(crit))
        yield state, infotext


check_info['mssql_tablespaces'] = {
    'parse_function': parse_mssql_tablespaces,
    'inventory_function': discover(),
    'check_function': check_mssql_tablespaces,
    'service_description': 'MSSQL %s Sizes',
    'group': 'mssql_tablespaces',
    'has_perfdata': True,
    'default_levels_variable': 'mssql_tablespace_default_levels',
}
