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

# 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

# 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.size_trend import *  # pylint: disable=wildcard-import,unused-wildcard-import
from cmk.base.plugins.agent_based.utils.df import df_discovery, FSBlocks


def _sanitize_line(line):
    """Merges units to values in case values and units are contained in separate line elements."""
    units = ("k", "K", "B", "M", "G", "T", "P", "E", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB")
    sanitized_line = []
    for word in line:
        if word in units and sanitized_line:
            sanitized_line[-1] += word
        else:
            sanitized_line.append(word)
    return sanitized_line


def parse_ceph_df(info) -> FSBlocks:
    parsed = {}
    section = None
    global_headers = None
    pools_headers = None

    for line in info:
        if line[0] in ["GLOBAL:", "RAW"] or re.sub("[-: ]", "", "".join(line)) == "RAWSTORAGE":
            section = "global"
            continue
        elif re.sub("[-: ]", "", "".join(line)) == "POOLS":
            section = "pools"
            continue

        line = _sanitize_line(line)
        if section == "global":
            if line == ["SIZE", "AVAIL", "RAW", "USED", "%RAW", "USED", "OBJECTS"]:
                global_headers = ["SIZE", "AVAIL", "RAW USED", "%RAW USED", "OBJECTS"]

            elif line == ["CLASS", "SIZE", "AVAIL", "USED", "RAW", "USED", "%RAW", "USED"]:
                global_headers = ["CLASS", "SIZE", "AVAIL", "USED", "RAW USED", "%RAW USED"]

            elif global_headers is not None:
                parsed.setdefault("SUMMARY", dict(zip(global_headers, line)))

        elif section == "pools":
            if line == [
                "NAME",
                "ID",
                "CATEGORY",
                "QUOTA",
                "OBJECTS",
                "QUOTA",
                "BYTES",
                "USED",
                "%USED",
                "MAX",
                "AVAIL",
                "OBJECTS",
                "DIRTY",
                "READ",
                "WRITE",
                "RAW",
                "USED",
            ]:
                pools_headers = [
                    "NAME",
                    "ID",
                    "CATEGORY",
                    "QUOTA OBJECTS",
                    "QUOTA BYTES",
                    "USED",
                    "%USED",
                    "MAX AVAIL",
                    "OBJECTS",
                    "DIRTY",
                    "READ",
                    "WRITE",
                    "RAW USED",
                ]

            elif line == [
                "NAME",
                "ID",
                "QUOTA",
                "OBJECTS",
                "QUOTA",
                "BYTES",
                "USED",
                "%USED",
                "MAX",
                "AVAIL",
                "OBJECTS",
                "DIRTY",
                "READ",
                "WRITE",
                "RAW",
                "USED",
            ]:
                pools_headers = [
                    "NAME",
                    "ID",
                    "QUOTA OBJECTS",
                    "QUOTA BYTES",
                    "USED",
                    "%USED",
                    "MAX AVAIL",
                    "OBJECTS",
                    "DIRTY",
                    "READ",
                    "WRITE",
                    "RAW USED",
                ]

            elif line == [
                "POOL",
                "ID",
                "STORED",
                "OBJECTS",
                "USED",
                "%USED",
                "MAX",
                "AVAIL",
                "QUOTA",
                "OBJECTS",
                "QUOTA",
                "BYTES",
                "DIRTY",
                "USED",
                "COMPR",
                "UNDER",
                "COMPR",
            ]:
                pools_headers = [
                    "POOL",
                    "ID",
                    "STORED",
                    "OBJECTS",
                    "USED",
                    "%USED",
                    "MAX AVAIL",
                    "QUOTA OBJECTS",
                    "QUOTA BYTES",
                    "DIRTY",
                    "USED COMPR",
                    "UNDER COMPR",
                ]

            elif line == [
                "POOL",
                "ID",
                "PGS",
                "STORED",
                "OBJECTS",
                "USED",
                "%USED",
                "MAX",
                "AVAIL",
                "QUOTA",
                "OBJECTS",
                "QUOTA",
                "BYTES",
                "DIRTY",
                "USED",
                "COMPR",
                "UNDER",
                "COMPR",
            ]:
                pools_headers = [
                    "POOL",
                    "ID",
                    "PGS",
                    "STORED",
                    "OBJECTS",
                    "USED",
                    "%USED",
                    "MAX AVAIL",
                    "QUOTA OBJECTS",
                    "QUOTA BYTES",
                    "DIRTY",
                    "USED COMPR",
                    "UNDER COMPR",
                ]

            elif line == [
                "POOL",
                "ID",
                "STORED",
                "(DATA)",
                "(OMAP)",
                "OBJECTS",
                "USED",
                "(DATA)",
                "(OMAP)",
                "%USED",
                "MAX",
                "AVAIL",
                "QUOTA",
                "OBJECTS",
                "QUOTA",
                "BYTES",
                "DIRTY",
                "USED",
                "COMPR",
                "UNDER",
                "COMPR",
            ]:
                pools_headers = [
                    "POOL",
                    "ID",
                    "STORED",
                    "(DATA)",
                    "(OMAP)",
                    "OBJECTS",
                    "USED",
                    "(DATA)",
                    "(OMAP)",
                    "%USED",
                    "MAX AVAIL",
                    "QUOTA OBJECTS",
                    "QUOTA BYTES",
                    "DIRTY",
                    "USED COMPR",
                    "UNDER COMPR",
                ]

            elif line == [
                "POOL",
                "ID",
                "PGS",
                "STORED",
                "(DATA)",
                "(OMAP)",
                "OBJECTS",
                "USED",
                "(DATA)",
                "(OMAP)",
                "%USED",
                "MAX",
                "AVAIL",
                "QUOTA",
                "OBJECTS",
                "QUOTA",
                "BYTES",
                "DIRTY",
                "USED",
                "COMPR",
                "UNDER",
                "COMPR",
            ]:
                pools_headers = [
                    "POOL",
                    "ID",
                    "PGS",
                    "STORED",
                    "STORED (DATA)",
                    "STORED (OMAP)",
                    "OBJECTS",
                    "USED",
                    "USED (DATA)",
                    "USED (OMAP)",
                    "%USED",
                    "MAX AVAIL",
                    "QUOTA OBJECTS",
                    "QUOTA BYTES",
                    "DIRTY",
                    "USED COMPR",
                    "UNDER COMPR",
                ]

            elif pools_headers is not None:
                parsed.setdefault(line[0], dict(zip(pools_headers[1:], line[1:])))

    def parse_byte_values(value_str):
        """
        Returns the used storage in mebibytes.
        """
        # sanitize to possible units to single representation
        value_str = value_str.rstrip("iB")

        if value_str.endswith("E"):
            return float(value_str[:-1]) * 1024**4
        elif value_str.endswith("P"):
            return float(value_str[:-1]) * 1024**3
        elif value_str.endswith("T"):
            return float(value_str[:-1]) * 1024**2
        elif value_str.endswith("G"):
            return float(value_str[:-1]) * 1024
        elif value_str.endswith("M"):
            return float(value_str[:-1])
        elif value_str.lower().endswith("k"):
            return float(value_str[:-1]) / 1024
        elif value_str == "N/A":
            return 0.0
        return float(value_str) / (1024**2)

    mps = []
    for mp, data in parsed.items():
        # http://docs.ceph.com/docs/master/rados/operations/monitoring/
        # GLOBAL section:
        #   SIZE: The overall storage capacity of the cluster.
        #   AVAIL: The amount of free space available in the cluster.
        # POOLS section:
        #   USED: The notional amount of data stored in kilobytes, unless the number appends M for megabytes or G for gigabytes.
        #   MAX AVAIL: An estimate of the notional amount of data that can be written to this pool.
        if mp == "SUMMARY":
            size_mb = parse_byte_values(data["SIZE"])
            avail_mb = parse_byte_values(data["AVAIL"])
        else:
            avail_mb = parse_byte_values(data["MAX AVAIL"])

            # from ceph version pacific STORED is a notional amount of data stored
            # USED is the total data stored (incl. replication)
            # https://docs.ceph.com/en/latest/rados/operations/monitoring/
            # werk 12199
            used_size = data["STORED"] if "STORED" in data else data["USED"]
            size_mb = avail_mb + parse_byte_values(used_size)
        mps.append((mp, size_mb, avail_mb, 0))
    return mps


def inventory_ceph_df(mps):
    return df_discovery(host_extra_conf(host_name(), filesystem_groups), [x[0] for x in mps])


factory_settings["filesystem_default_levels"] = FILESYSTEM_DEFAULT_LEVELS


check_info["ceph_df"] = {
    "parse_function": parse_ceph_df,
    "inventory_function": inventory_ceph_df,
    "check_function": df_check_filesystem_list,
    "service_description": "Ceph Pool %s",
    "has_perfdata": True,
    "group": "filesystem",
    "default_levels_variable": "filesystem_default_levels",
}
