#!/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.aws 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.cpu_util 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.diskstat import *  # pylint: disable=wildcard-import,unused-wildcard-import

#   .--CPU utilization-----------------------------------------------------.
#   |    ____ ____  _   _         _   _ _ _          _   _                 |
#   |   / ___|  _ \| | | |  _   _| |_(_) (_)______ _| |_(_) ___  _ __      |
#   |  | |   | |_) | | | | | | | | __| | | |_  / _` | __| |/ _ \| '_ \     |
#   |  | |___|  __/| |_| | | |_| | |_| | | |/ / (_| | |_| | (_) | | | |    |
#   |   \____|_|    \___/   \__,_|\__|_|_|_/___\__,_|\__|_|\___/|_| |_|    |
#   |                                                                      |
#   '----------------------------------------------------------------------'

factory_settings["aws_rds_cpu_util"] = {
    "levels": (80.0, 90.0),
}


@get_parsed_item_data
def check_aws_rds(item, params, metrics):
    return check_cpu_util(metrics["CPUUtilization"], params, time.time())


check_info["aws_rds"] = {
    "inventory_function": lambda p: inventory_aws_generic(p, ["CPUUtilization"]),
    "check_function": check_aws_rds,
    "service_description": "AWS/RDS %s CPU Utilization",
    "group": "cpu_utilization_multiitem",
    "default_levels_variable": "aws_rds_cpu_util",
    "has_perfdata": True,
}

# .
#   .--CPU credits---------------------------------------------------------.
#   |           ____ ____  _   _                     _ _ _                 |
#   |          / ___|  _ \| | | |   ___ _ __ ___  __| (_) |_ ___           |
#   |         | |   | |_) | | | |  / __| '__/ _ \/ _` | | __/ __|          |
#   |         | |___|  __/| |_| | | (__| | |  __/ (_| | | |_\__ \          |
#   |          \____|_|    \___/   \___|_|  \___|\__,_|_|\__|___/          |
#   |                                                                      |
#   '----------------------------------------------------------------------'

# CPU credit balance:
# For standard T2 instances with bursting, a burst can continue only as long as
# there are available CPU credits, so it’s important to monitor your instance’s
# balance. Credits are earned any time the instance is running below its baseline
# CPU performance level. The initial balance, accrual rate, and maximum possible
# balance are all dependent on the instance level.
#
# CPU credit usage:
# One CPU credit is equivalent to one minute of 100 percent CPU utilization (or
# two minutes at 50 percent, etc.). Whenever an instance requires CPU performance
# above that instance type’s baseline, it will burst, consuming CPU credits until
# the demand lessens or the credit balance runs out. Keeping an eye on your
# instances’ credit usage will help you identify whether you might need to switch
# to an instance type that is optimized for CPU-intensive workloads. Or, you can
# create an alert for when your credit balance drops below a threshold while CPU
# usage remains above baseline.


@get_parsed_item_data
def check_aws_rds_cpu_credits(item, params, metrics):
    yield 0, "CPU Credit Usage: %.2f" % metrics["CPUCreditUsage"]
    warn, crit = params.get("balance_levels_lower", (None, None))
    yield check_levels(
        metrics["CPUCreditBalance"],
        "aws_cpu_credit_balance",
        (None, None, warn, crit),
        human_readable_func=lambda x: "%.2f" % x,
        infoname="CPU Credit Balance",
    )

    burst_balance = metrics.get("BurstBalance")
    if burst_balance is not None:
        warn, crit = params.get("burst_balance_levels_lower", (None, None))
        yield check_levels(
            metrics["BurstBalance"],
            "aws_burst_balance",
            (None, None, warn, crit),
            human_readable_func=get_percent_human_readable,
            infoname="Burst Balance",
        )


check_info["aws_rds.cpu_credits"] = {
    "inventory_function": lambda p: inventory_aws_generic(
        p,
        [
            "CPUCreditUsage",
            "CPUCreditBalance",
        ],
    ),
    "check_function": check_aws_rds_cpu_credits,
    "service_description": "AWS/RDS %s CPU Credits",
    "group": "aws_rds_cpu_credits",
    "has_perfdata": True,
}

# .
#   .--bin log usage-------------------------------------------------------.
#   |     _     _         _                                                |
#   |    | |__ (_)_ __   | | ___   __ _   _   _ ___  __ _  __ _  ___       |
#   |    | '_ \| | '_ \  | |/ _ \ / _` | | | | / __|/ _` |/ _` |/ _ \      |
#   |    | |_) | | | | | | | (_) | (_| | | |_| \__ \ (_| | (_| |  __/      |
#   |    |_.__/|_|_| |_| |_|\___/ \__, |  \__,_|___/\__,_|\__, |\___|      |
#   |                             |___/                   |___/            |
#   '----------------------------------------------------------------------'


@get_parsed_item_data
def check_aws_rds_bin_log_usage(item, params, metrics):
    bin_log_usage = metrics["BinLogDiskUsage"]
    yield 0, get_bytes_human_readable(bin_log_usage)

    try:
        usage = 100.0 * bin_log_usage / metrics["AllocatedStorage"]
    except (KeyError, ZeroDivisionError):
        yield 1, "Cannot calculate usage"
    else:
        yield check_levels(
            usage,
            "aws_rds_bin_log_disk_usage",
            params.get("levels"),
            human_readable_func=get_percent_human_readable,
        )


check_info["aws_rds.bin_log_usage"] = {
    "inventory_function": lambda p: inventory_aws_generic(p, ["BinLogDiskUsage"]),
    "check_function": check_aws_rds_bin_log_usage,
    "service_description": "AWS/RDS %s Binary Log Usage",
    "has_perfdata": True,
    "group": "aws_rds_disk_usage",
}

# .
#   .--transaction logs usage----------------------------------------------.
#   |        _                                  _   _                      |
#   |       | |_ _ __ __ _ _ __  ___  __ _  ___| |_(_) ___  _ __           |
#   |       | __| '__/ _` | '_ \/ __|/ _` |/ __| __| |/ _ \| '_ \          |
#   |       | |_| | | (_| | | | \__ \ (_| | (__| |_| | (_) | | | |         |
#   |        \__|_|  \__,_|_| |_|___/\__,_|\___|\__|_|\___/|_| |_|         |
#   |                                                                      |
#   |           _                                                          |
#   |          | | ___   __ _ ___   _   _ ___  __ _  __ _  ___             |
#   |          | |/ _ \ / _` / __| | | | / __|/ _` |/ _` |/ _ \            |
#   |          | | (_) | (_| \__ \ | |_| \__ \ (_| | (_| |  __/            |
#   |          |_|\___/ \__, |___/  \__,_|___/\__,_|\__, |\___|            |
#   |                   |___/                       |___/                  |
#   '----------------------------------------------------------------------'


@get_parsed_item_data
def check_aws_rds_transaction_logs_usage(item, params, metrics):
    transaction_logs_space = metrics["TransactionLogsDiskUsage"]
    yield 0, get_bytes_human_readable(transaction_logs_space)

    try:
        usage = 100.0 * transaction_logs_space / metrics["AllocatedStorage"]
    except (KeyError, ZeroDivisionError):
        yield 1, "Cannot calculate usage"
    else:
        yield check_levels(
            usage,
            "aws_rds_transaction_logs_disk_usage",
            params.get("levels"),
            human_readable_func=get_percent_human_readable,
        )

    generation = metrics.get("TransactionLogsGeneration")
    if generation:
        yield 0, "Generation rate: %s" % aws_get_bytes_rate_human_readable(generation)


check_info["aws_rds.transaction_logs_usage"] = {
    "inventory_function": lambda p: inventory_aws_generic(p, ["TransactionLogsDiskUsage"]),
    "check_function": check_aws_rds_transaction_logs_usage,
    "service_description": "AWS/RDS %s Transaction Logs Usage",
    "has_perfdata": True,
    "group": "aws_rds_disk_usage",
}

# .
#   .--replication slot usage----------------------------------------------.
#   |                 _ _           _   _                   _       _      |
#   |  _ __ ___ _ __ | (_) ___ __ _| |_(_) ___  _ __    ___| | ___ | |_    |
#   | | '__/ _ \ '_ \| | |/ __/ _` | __| |/ _ \| '_ \  / __| |/ _ \| __|   |
#   | | | |  __/ |_) | | | (_| (_| | |_| | (_) | | | | \__ \ | (_) | |_    |
#   | |_|  \___| .__/|_|_|\___\__,_|\__|_|\___/|_| |_| |___/_|\___/ \__|   |
#   |          |_|                                                         |
#   |                                                                      |
#   |                     _   _ ___  __ _  __ _  ___                       |
#   |                    | | | / __|/ _` |/ _` |/ _ \                      |
#   |                    | |_| \__ \ (_| | (_| |  __/                      |
#   |                     \__,_|___/\__,_|\__, |\___|                      |
#   |                                     |___/                            |
#   '----------------------------------------------------------------------'


@get_parsed_item_data
def check_aws_rds_replication_slot_usage(item, params, metrics):
    replication_slot_space = metrics["ReplicationSlotDiskUsage"]
    yield 0, get_bytes_human_readable(replication_slot_space)

    try:
        usage = 100.0 * replication_slot_space / metrics["AllocatedStorage"]
    except (KeyError, ZeroDivisionError):
        yield 1, "Cannot calculate usage"
    else:
        yield check_levels(
            usage,
            "aws_rds_replication_slot_disk_usage",
            params.get("levels"),
            human_readable_func=get_percent_human_readable,
        )


check_info["aws_rds.replication_slot_usage"] = {
    "inventory_function": lambda p: inventory_aws_generic(p, ["ReplicationSlotDiskUsage"]),
    "check_function": check_aws_rds_replication_slot_usage,
    "service_description": "AWS/RDS %s Replication Slot Usage",
    "has_perfdata": True,
    "group": "aws_rds_disk_usage",
}

# .
#   .--disk IO-------------------------------------------------------------.
#   |                         _ _     _      ___ ___                       |
#   |                      __| (_)___| | __ |_ _/ _ \                      |
#   |                     / _` | / __| |/ /  | | | | |                     |
#   |                    | (_| | \__ \   <   | | |_| |                     |
#   |                     \__,_|_|___/_|\_\ |___\___/                      |
#   |                                                                      |
#   '----------------------------------------------------------------------'


def check_aws_rds_disk_io(item, params, parsed):
    disks = {}
    for disk_name, metrics in parsed.items():
        disk = disks.setdefault(disk_name, {})
        for key, metric_key, scale in [
            ("read_ios", "ReadIOPS", 1.0),
            ("write_ios", "WriteIOPS", 1.0),
            ("read_throughput", "ReadThroughput", 1.0),
            ("write_throughput", "WriteThroughput", 1.0),
            ("read_latency", "ReadLatency", 1000.0),
            ("write_latency", "WriteLatency", 1000.0),
        ]:
            metric = metrics.get(metric_key)
            if metric is None:
                continue
            disk[key] = metric * scale
    return check_diskstat_dict(item, params, disks)


check_info["aws_rds.disk_io"] = {
    "inventory_function": lambda p: inventory_aws_generic(
        p,
        [
            "DiskQueueDepth",
            "ReadIOPS",
            "ReadLatency",
            "ReadThroughput",
            "WriteIOPS",
            "WriteLatency",
            "WriteThroughput",
        ],
    ),
    "check_function": check_aws_rds_disk_io,
    "service_description": "AWS/RDS %s Disk IO",
    "group": "diskstat",
    "has_perfdata": True,
}

# .
#   .--connections---------------------------------------------------------.
#   |                                        _   _                         |
#   |         ___ ___  _ __  _ __   ___  ___| |_(_) ___  _ __  ___         |
#   |        / __/ _ \| '_ \| '_ \ / _ \/ __| __| |/ _ \| '_ \/ __|        |
#   |       | (_| (_) | | | | | | |  __/ (__| |_| | (_) | | | \__ \        |
#   |        \___\___/|_| |_|_| |_|\___|\___|\__|_|\___/|_| |_|___/        |
#   |                                                                      |
#   '----------------------------------------------------------------------'


@get_parsed_item_data
def check_aws_rds_connections(item, params, metrics):
    yield check_levels(
        metrics["DatabaseConnections"],
        "aws_rds_connections",
        params.get("levels"),
        infoname="In use",
    )


check_info["aws_rds.connections"] = {
    "inventory_function": lambda p: inventory_aws_generic(p, ["DatabaseConnections"]),
    "check_function": check_aws_rds_connections,
    "service_description": "AWS/RDS %s Connections",
    "has_perfdata": True,
    "group": "aws_rds_connections",
}

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


@get_parsed_item_data
def check_aws_rds_agent_jobs(item, params, metrics):
    failed_agent_jobs = metrics["FailedSQLServerAgentJobsCount"]
    if failed_agent_jobs > 0:
        state = 1
    else:
        state = 0
    yield state, "Rate of failing jobs: %s" % aws_get_counts_rate_human_readable(failed_agent_jobs)


check_info["aws_rds.agent_jobs"] = {
    "inventory_function": lambda p: inventory_aws_generic(p, ["FailedSQLServerAgentJobsCount"]),
    "check_function": check_aws_rds_agent_jobs,
    "service_description": "AWS/RDS %s SQL Server Agent Jobs",
}

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


@get_parsed_item_data
def check_aws_rds_replica_lag(item, params, metrics):
    yield check_levels(
        metrics["ReplicaLag"],
        "aws_rds_replica_lag",
        params.get("lag_levels"),
        human_readable_func=get_age_human_readable,
        infoname="Lag",
    )

    oldest_replica_lag_space = metrics.get("OldestReplicationSlotLag")
    if oldest_replica_lag_space is not None:
        yield check_levels(
            oldest_replica_lag_space,
            "aws_rds_oldest_replication_slot_lag",
            params.get(
                "slot_levels",
            ),
            human_readable_func=get_bytes_human_readable,
            infoname="Oldest replication slot lag",
        )


check_info["aws_rds.replica_lag"] = {
    "inventory_function": lambda p: inventory_aws_generic(p, ["ReplicaLag"]),
    "check_function": check_aws_rds_replica_lag,
    "service_description": "AWS/RDS %s Replica Lag",
    "has_perfdata": True,
    "group": "aws_rds_replica_lag",
}
