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


def parse_emcvnx_storage_pools(info):
    parsed = {}
    section = None
    pool_name = None
    tier_name = None
    for line in info:
        line = [x.strip() for x in line]

        if line[0].startswith("[[[") and line[0].endswith("]]]"):
            section = line[0][3:-3]

        elif (section == "storage_pools" and line[0] == "Pool Name") or \
             (section == "auto_tiering"  and line[0] == "Storage Pool Name"):
            pool_name = line[1]
            parsed.setdefault(pool_name, {})

        elif section == "auto_tiering" and line[0].startswith(u'Auto-tiering is not supported'):
            pool_name = None

        elif pool_name is not None and len(line) == 2:
            if line[0] == "Tier Name":
                tier_name = line[1]
                parsed[pool_name].setdefault("tier_names", [])
                parsed[pool_name]["tier_names"].append(tier_name)

            elif line[0] == "Disks (Type)":
                tier_name = None

            elif tier_name is not None:
                parsed[pool_name].setdefault("%s_%s" % (tier_name, line[0]), line[1])

            else:
                parsed[pool_name].setdefault(line[0], line[1])

    return parsed


def inventory_emcvnx_storage_pools(parsed):
    for pool_name in parsed:
        yield pool_name, {}


#   .--general-------------------------------------------------------------.
#   |                                                  _                   |
#   |                   __ _  ___ _ __   ___ _ __ __ _| |                  |
#   |                  / _` |/ _ \ '_ \ / _ \ '__/ _` | |                  |
#   |                 | (_| |  __/ | | |  __/ | | (_| | |                  |
#   |                  \__, |\___|_| |_|\___|_|  \__,_|_|                  |
#   |                  |___/                                               |
#   '----------------------------------------------------------------------'

# Suggested by customer
factory_settings["emcvnx_storage_pools_default_levels"] = {"percent_full": (70.0, 90.0)}


def check_emcvnx_storage_pools(item, params, parsed):
    # Better readable names in web GUI of device:
    # "User Capacity (GBs)"             : "Physical Capacity: Total",
    # "Consumed Capacity (GBs)"         : "Physical Capacity: Total Allocation",
    # "Available Capacity (GBs)"        : "Physical Capacity: Free",
    # "Percent Full"                    : "Physical Capacity: Percent Full",
    # "Percent Subscribed"              : "Virtual Capacity: Percent Subscribed",
    # "Oversubscribed by (GBs)"         : "Virtual Capacity: Oversubscribed by",
    # "Total Subscribed Capacity (GBs)" : "Virtual Capacity: Total Subscription",

    if item in parsed:
        data = parsed[item]
        state = data["State"]
        status = data["Status"]
        user_capacity = float(data["User Capacity (GBs)"]) * 1024**3
        consumed_capacity = float(data["Consumed Capacity (GBs)"]) * 1024**3
        avail_capacity = float(data["Available Capacity (GBs)"]) * 1024**3
        percent_full = float(data["Percent Full"])
        percent_subscribed = float(data["Percent Subscribed"])
        over_subscribed = float(data["Oversubscribed by (GBs)"]) * 1024**3
        total_subscribed_capacity = float(data["Total Subscribed Capacity (GBs)"]) * 1024**3

        yield 0, ("State: %s, Status: %s, [Phys. capacity] User capacity: %s, " +\
             "Consumed capacity: %s, Available capacity: %s") %\
            (state, status, get_bytes_human_readable(user_capacity),
             get_bytes_human_readable(consumed_capacity),
             get_bytes_human_readable(avail_capacity))

        state = 0
        infotext = "Percent full: %s" % get_percent_human_readable(percent_full)
        if "percent_full" in params:
            perc_full_warn, perc_full_crit = params["percent_full"]
            if percent_full >= perc_full_crit:
                state = 2
            elif percent_full >= perc_full_warn:
                state = 1
            if state:
                infotext += " (warn/crit at %s/%s)" % \
                            (get_bytes_human_readable(perc_full_warn),
                             get_bytes_human_readable(perc_full_crit))

        yield state, infotext
        yield 0, ("[Virt. capacity] Percent subscribed: %s, Oversubscribed by: %s, " +\
             "Total subscribed capacity: %s") %\
            (get_percent_human_readable(percent_subscribed),
             get_bytes_human_readable(over_subscribed),
             get_bytes_human_readable(total_subscribed_capacity)), [
            ("emcvnx_consumed_capacity",         consumed_capacity),
            ("emcvnx_avail_capacity",            avail_capacity),
            ("emcvnx_perc_full",                 percent_full),
            ("emcvnx_perc_subscribed",           percent_subscribed),
            ("emcvnx_over_subscribed",           over_subscribed),
            ("emcvnx_total_subscribed_capacity", total_subscribed_capacity),
        ]


check_info['emcvnx_storage_pools'] = {
    'parse_function': parse_emcvnx_storage_pools,
    'inventory_function': inventory_emcvnx_storage_pools,
    'check_function': check_emcvnx_storage_pools,
    'service_description': 'Pool %s General',
    'group': 'emcvnx_storage_pools',
    'has_perfdata': True,
    'default_levels_variable': 'emcvnx_storage_pools_default_levels',
}

#.
#   .--tiering-------------------------------------------------------------.
#   |                  _                  _                                |
#   |                 | |_ ___  __ _ _ __(_)_ __   __ _                    |
#   |                 | __/ _ \/ _` | '__| | '_ \ / _` |                   |
#   |                 | ||  __/ (_| | |  | | | | | (_| |                   |
#   |                  \__\___|\__,_|_|  |_|_| |_|\__, |                   |
#   |                                             |___/                    |
#   '----------------------------------------------------------------------'

# Suggested by customer
factory_settings['emcvnx_storage_pools_tiering_default_levels'] = {
    "time_to_complete": (21 * 60 * 60 * 24, 28 * 60 * 60 * 24),
}


def parse_emcvnx_time_to_complete(time_to_complete):
    map_units = {
        "day": 24 * 60 * 60,
        "hour": 60 * 60,
        "minute": 60,
    }
    # 11 days, 17 hours, 24 minutes
    # 17 hours, 24 minutes
    # 1 hour, 43 minutes
    # ...
    try:
        seconds = 0
        for value, unit in [x.strip().split() for x in time_to_complete.split(",")]:
            if unit.endswith("s"):
                unit = unit[:-1]
            seconds += int(value) * map_units[unit]
        return seconds
    except (ValueError, KeyError, AttributeError):
        return None


def inventory_emcvnx_storage_pools_tiering(parsed):
    for pool_name in parsed.iterkeys():
        yield pool_name, {}


@get_parsed_item_data
def check_emcvnx_storage_pools_tiering(item, params, data):

    for key in ("FAST Cache", "Relocation Status", "Relocation Rate"):
        if key in data:
            yield 0, "%s: %s" % (key.capitalize(), data[key])

    for direction in ("Up", "Down", "Within Tiers"):
        value_raw = data.get("Data to Move %s (GBs)" % direction)
        if value_raw is not None:
            value = float(value_raw) * 1024**3
            short_dir = direction.split()[0].lower()
            yield check_levels(value,
                               "emcvnx_move_%s" % short_dir,
                               None,
                               infoname="Move %s" % short_dir,
                               human_readable_func=get_bytes_human_readable)

    move_completed_raw = data.get("Data Movement Completed (GBs)")
    if move_completed_raw is not None:
        move_completed = float(move_completed_raw) * 1024**3
        yield check_levels(move_completed,
                           "emcvnx_move_completed",
                           None,
                           infoname="Movement completed",
                           human_readable_func=get_bytes_human_readable)

    time_to_complete = data.get("Estimated Time to Complete")
    age = parse_emcvnx_time_to_complete(time_to_complete)
    if age is not None:
        yield 0, "Estimated time to complete: %s" % time_to_complete
        yield check_levels(age,
                           "emcvnx_time_to_complete",
                           params["time_to_complete"],
                           infoname="Age",
                           human_readable_func=get_age_human_readable)


check_info['emcvnx_storage_pools.tiering'] = {
    'inventory_function': inventory_emcvnx_storage_pools_tiering,
    'check_function': check_emcvnx_storage_pools_tiering,
    'service_description': 'Pool %s Tiering Status',
    'has_perfdata': True,
    'group': 'emcvnx_storage_pools_tiering',
    'default_levels_variable': 'emcvnx_storage_pools_tiering_default_levels',
}


def inventory_emcvnx_storage_pools_tieringtypes(parsed):
    for pool_name, data in parsed.items():
        for tier_name in data.get("tier_names", []):
            yield "%s %s" % (pool_name, tier_name), {}


def _get_item_data_and_tier(item, parsed):
    for pool_name, data in parsed.items():
        for tier_name in data.get("tier_names", []):
            if item == "%s %s" % (pool_name, tier_name):
                return data, tier_name
    return None, None


def check_emcvnx_storage_pools_tieringtypes(item, params, parsed):
    data, tier_name = _get_item_data_and_tier(item, parsed)
    if data is None:
        return

    user_capacity_raw = data.get("%s_User Capacity (GBs)" % tier_name)
    if user_capacity_raw is not None:
        user_capacity = float(user_capacity_raw) * 1024**3
        yield check_levels(user_capacity,
                           None,
                           None,
                           infoname="User capacity",
                           human_readable_func=get_bytes_human_readable)

    consumed_capacity_raw = data.get("%s_Consumed Capacity (GBs)" % tier_name)
    if consumed_capacity_raw is not None:
        consumed_capacity = float(consumed_capacity_raw) * 1024**3
        yield check_levels(consumed_capacity,
                           "emcvnx_consumed_capacity",
                           None,
                           infoname="Consumed capacity",
                           human_readable_func=get_bytes_human_readable)

    avail_capacity_raw = data.get("%s_Available Capacity (GBs)" % tier_name)
    if avail_capacity_raw is not None:
        avail_capacity = float(avail_capacity_raw) * 1024**3
        yield check_levels(avail_capacity,
                           "emcvnx_avail_capacity",
                           None,
                           infoname="Available capacity",
                           human_readable_func=get_bytes_human_readable)

    percent_subscribed_raw = data.get("%s_Percent Subscribed" % tier_name)
    if percent_subscribed_raw is not None:
        percent_subscribed = float(percent_subscribed_raw.replace("%", ""))
        yield check_levels(percent_subscribed,
                           "emcvnx_perc_subscribed",
                           None,
                           infoname="Percent subscribed",
                           human_readable_func=get_percent_human_readable)

    for direction in ("for Higher", "for Lower", "Within"):
        value_raw = data.get("%s_Data Targeted %s Tier (GBs)" % (tier_name, direction))
        if value_raw is not None:
            value = float(value_raw) * 1024**3
            short_dir = direction.split()[-1].lower()
            yield check_levels(value,
                               "emcvnx_targeted_%s" % short_dir,
                               None,
                               infoname="Move %s" % short_dir,
                               human_readable_func=get_bytes_human_readable)


check_info['emcvnx_storage_pools.tieringtypes'] = {
    'inventory_function': inventory_emcvnx_storage_pools_tieringtypes,
    'check_function': check_emcvnx_storage_pools_tieringtypes,
    'service_description': 'Pool %s tiering',
    'has_perfdata': True,
}

#.
#   .--deduplication-------------------------------------------------------.
#   |        _          _             _ _           _   _                  |
#   |     __| | ___  __| |_   _ _ __ | (_) ___ __ _| |_(_) ___  _ __       |
#   |    / _` |/ _ \/ _` | | | | '_ \| | |/ __/ _` | __| |/ _ \| '_ \      |
#   |   | (_| |  __/ (_| | |_| | |_) | | | (_| (_| | |_| | (_) | | | |     |
#   |    \__,_|\___|\__,_|\__,_| .__/|_|_|\___\__,_|\__|_|\___/|_| |_|     |
#   |                          |_|                                         |
#   '----------------------------------------------------------------------'


def _emcvnx_get_text_perf(data, key, perfname, format_func=get_bytes_human_readable,
                          factor=1024**3):
    field = data.get(key, "unknown")
    try:
        value = float(field) * factor
        return format_func(value), [(perfname, value)]
    except ValueError:
        return str(field), []


@get_parsed_item_data
def check_emcvnx_storage_pools_deduplication(_no_item, _no_params, data):

    yield 0, "State: %s" % data.get("Deduplication State", "unknown")
    yield 0, "Status: %s" % data.get("Deduplication Status", "unknown").split("(")[0]
    yield 0, "Rate: %s" % data.get("Deduplication Rate", "unknown")

    txt, perf = _emcvnx_get_text_perf(data, "Efficiency Savings (GBs)",
                                      "emcvnx_dedupl_efficiency_savings")
    yield 0, "Efficiency savings: %s" % txt, perf

    txt, perf = _emcvnx_get_text_perf(data,
                                      "Deduplication Percent Completed",
                                      "emcvnx_dedupl_perc_completed",
                                      format_func=get_percent_human_readable,
                                      factor=1.0)
    yield 0, "Percent completed: %s" % txt, perf

    txt, perf = _emcvnx_get_text_perf(data, "Deduplication Remaining Size (GBs)",
                                      "emcvnx_dedupl_remaining_size")
    yield 0, "Remaining size: %s" % txt, perf

    txt, perf = _emcvnx_get_text_perf(data, "Deduplication Shared Capacity (GBs)",
                                      "emcvnx_dedupl_shared_capacity")
    yield 0, "Shared capacity: %s" % txt, perf


check_info['emcvnx_storage_pools.deduplication'] = {
    'inventory_function': inventory_emcvnx_storage_pools,
    'check_function': check_emcvnx_storage_pools_deduplication,
    'service_description': 'Pool %s Deduplication',
    'has_perfdata': True,
}
