#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
# conditions defined in the file COPYING, which is part of this source code package.


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("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:
        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,
}
