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

import time

# NOTE: Careful when replacing the *-import below with a more specific import. This can cause
# problems because it might remove variables from the check-context which are necessary for
# resolving legacy discovery results such as [("SUMMARY", "diskstat_default_levels")]. Furthermore,
# it might also remove variables needed for accessing discovery rulesets.
from cmk.base.check_legacy_includes.df import *  # pylint: disable=wildcard-import,unused-wildcard-import
from cmk.base.plugins.agent_based.utils.df import df_discovery
from cmk.base.plugins.agent_based.utils.df import (
    FILESYSTEM_DEFAULT_LEVELS as _FILESYSTEM_DEFAULT_LEVELS,
)
from cmk.base.plugins.agent_based.utils.df import mountpoints_in_group

# <<<netapp_api_volumes:sep(9)>>>
# volume vol0 size-available 556613632    state online    files-total 25876   files-used 8646 size-total 848203776    fcp_write_data 0    fcp_read_data 0cifs_write_data 0    iscsi_read_latency 0    iscsi_write_data 0  read_data 201265528798  nfs_write_latency 977623886 san_write_latency 0 san_write_data 0read_latency 1529821621 cifs_read_latency 0 fcp_write_latency 0 fcp_read_latency 0  iscsi_write_latency 0   nfs_read_latency 1491050012 iscsi_read_data 0   instance_name vol0  cifs_read_data 0    nfs_read_data 197072260981  write_latency 1528780977    san_read_data 0 san_read_latency 0  write_data 13926719804  nfs_write_data 2789744628   cifs_write_latency 0


factory_settings["netapp_api_volumes_default_levels"] = {
    k: v for k, v in _FILESYSTEM_DEFAULT_LEVELS.items() if k not in ("show_levels", "show_reserved")
}


def parse_netapp_api_volumes(info):
    volumes = {}
    for line in info:
        volume = {}
        name = line[0].split(" ", 1)[1]
        for element in line[1:]:
            key, val = element.split(" ", 1)
            try:
                val = int(val)
            except ValueError:
                pass
            volume[key] = val

        # Clustermode specific
        if "vserver_name" in volume:
            name = "%s.%s" % (volume["vserver_name"], volume["name"])

        volumes[name] = volume

    return volumes


def discovery_netapp_api_volumes(parsed):
    return df_discovery(host_extra_conf(host_name(), filesystem_groups), parsed)


def _create_key(protocol, mode, field):
    if protocol:
        return "_".join([protocol, mode, field])
    return "_".join([mode, field])


def _check_single_netapp_api_volume(item, params, volume):
    mega = 1024.0 * 1024.0
    inodes_total = volume["files-total"]
    yield from df_check_filesystem_single_coroutine(
        item,
        volume["size-total"] / mega,
        volume["size-available"] / mega,
        0,
        inodes_total,
        inodes_total - volume["files-used"],
        params,
    )

    yield 0, "", list(_generate_volume_metrics(params, volume))


def _generate_volume_metrics(
    params,
    volume,
):
    now = time.time()
    base = {}
    metrics_map = {"write_ops": "write_ops_s"}

    for protocol in params.get("perfdata", []):
        for mode in ["read", "write", "other"]:
            for field in ["data", "ops", "latency"]:

                key = _create_key(protocol, mode, field)
                value = volume.get(key)
                if value is None:
                    continue

                try:
                    delta = get_rate(key, now, value, onwrap=RAISE)
                except MKCounterWrapped:
                    continue

                # Quite hacky.. this base information is used later on by the "latency" field
                if field == "ops":
                    if delta == 0.0:
                        base[key] = 1.0
                    else:
                        base[key] = float(delta)

                if mode in ["read", "write"] and field == "latency":
                    # See https://library.netapp.com/ecmdocs/ECMP1608437/html/GUID-04407796-688E-489D-901C-A6C9EAC2A7A2.html
                    # for scaling issues:
                    # read_latency           micro
                    # write_latency          micro
                    # other_latency          micro
                    # nfs_read_latency       micro
                    # nfs_write_latency      micro
                    # nfs_other_latency      micro
                    # cifs_read_latency      micro
                    # cifs_write_latency     micro
                    # cifs_other_latency     micro
                    # san_read_latency       micro
                    # san_write_latency      micro
                    # san_other_latency      micro
                    #
                    # === 7-Mode environments only ===
                    # fcp_read_latency       milli
                    # fcp_write_latency      milli
                    # fcp_other_latency      milli
                    # iscsi_read_latency     milli
                    # iscsi_write_latency    milli
                    # iscsi_other_latency    milli
                    #
                    # FIXME The metric system expects milliseconds but should get seconds
                    if protocol in ["fcp", "iscsi"]:
                        divisor = 1.0
                    else:
                        divisor = 1000.0
                    delta /= (
                        divisor
                        * base[
                            _create_key(
                                protocol,
                                mode,
                                "ops",
                            )
                        ]
                    )  # fixed: true-division

                yield metrics_map.get(key, key), delta


def _combine_netapp_api_volumes(volumes_in_group, parsed):
    combined_volume = {}
    volumes_not_online = {}

    for volume_name in volumes_in_group:
        volume = parsed[volume_name]

        state = volume.get("state")
        if volume.get("state") != "online":
            volumes_not_online[volume_name] = state

        else:
            for k, v in volume.items():
                if isinstance(v, int):
                    combined_volume.setdefault(k, 0)
                    combined_volume[k] += v

    n_vols_online = len(volumes_in_group) - len(volumes_not_online)
    if n_vols_online:
        for k, v in combined_volume.items():
            if k.endswith("latency"):
                combined_volume[k] = float(v) / n_vols_online

    return combined_volume, volumes_not_online


# Cannot use decorator get_parsed_item_data for this check function due to the
# specific error message for legacy checks with a UUID as item
def check_netapp_api_volumes(item, params, parsed):
    if "patterns" in params:

        volumes_in_group = mountpoints_in_group(parsed, *params["patterns"])
        if not volumes_in_group:
            yield 3, "No volumes matching the patterns of this group"
            return

        combined_volumes, volumes_not_online = _combine_netapp_api_volumes(
            volumes_in_group,
            parsed,
        )

        for vol, state in volumes_not_online.items():
            yield 1, "Volume %s is %s" % (vol, state)

        if combined_volumes:
            yield from _check_single_netapp_api_volume(item, params, combined_volumes)

        yield 0, "\n%d volume(s) in group" % len(volumes_in_group)

        return

    volume = parsed.get(item)

    if not volume:
        if item.count("-") >= 4:
            yield 3, "Service description with UUID is no longer supported. Please rediscover."
        return

    if volume.get("state") != "online":
        yield 1, "Volume is %s" % volume.get("state")
        return

    yield from _check_single_netapp_api_volume(item, params, volume)


check_info["netapp_api_volumes"] = {
    "check_function": check_netapp_api_volumes,
    "inventory_function": discovery_netapp_api_volumes,
    "parse_function": parse_netapp_api_volumes,
    "service_description": "Volume %s",
    "has_perfdata": True,
    "group": "netapp_volumes",
    "default_levels_variable": "netapp_api_volumes_default_levels",
}
