#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2019             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_aws_elb(info):
    metrics = _extract_aws_metrics_by_labels([
        "RequestCount",
        "SurgeQueueLength",
        "SpilloverCount",
        "Latency",
        "HTTPCode_ELB_4XX",
        "HTTPCode_ELB_5XX",
        "HTTPCode_Backend_2XX",
        "HTTPCode_Backend_3XX",
        "HTTPCode_Backend_4XX",
        "HTTPCode_Backend_5XX",
        "HealthyHostCount",
        "UnHealthyHostCount",
        "BackendConnectionErrors",
    ], parse_aws(info))
    # We get exactly one entry: {INST-ID: METRICS}
    # INST-ID is the piggyback host name
    try:
        return metrics.values()[-1]
    except IndexError:
        return {}


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

# SpilloverCount: When the SurgeQueueLength reaches the maximum of 1,024 queued
# Requests, new requests are dropped, the user receives a 503 error, and the
# Spillover count metric is incremented. In a healthy system, this metric is
# Always equal to zero.

factory_settings['aws_elb_statistics'] = {
    'levels_surge_queue_length': (1024, 1024),
    'levels_spillover': (1, 1),
}


def check_aws_elb_statistics(item, params, parsed):
    queue_length = parsed.get('SurgeQueueLength')
    if queue_length is not None:
        yield check_levels_backport(
            queue_length,
            'aws_surge_queue_length',
            params['levels_surge_queue_length'],
            infoname='Surge queue length')

    spillover = parsed.get('SpilloverCount')
    if spillover is not None:
        yield check_levels_backport(
            spillover, 'aws_spillover', params['levels_spillover'], infoname='Spillover')


check_info['aws_elb'] = {
    'parse_function': parse_aws_elb,
    'inventory_function': lambda p: inventory_aws_generic_single(
        p, ['SurgeQueueLength', 'SpilloverCount']),
    'check_function': check_aws_elb_statistics,
    'service_description': 'AWS/ELB Statistics',
    'includes': ['aws.include', 'backport.include'],
    'has_perfdata': True,
    'default_levels_variable': 'aws_elb_statistics',
    'group': 'aws_elb_statistics',
}

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


def check_aws_elb_latency(item, params, parsed):
    latency = parsed.get("Latency")
    if latency is not None:
        yield check_levels_backport(
            latency,
            'aws_load_balancer_latency',
            params.get('levels_latency'),
            human_readable_func=get_age_human_readable,
            infoname="Latency")


check_info['aws_elb.latency'] = {
    'inventory_function': lambda p: inventory_aws_generic_single(p, ['Latency']),
    'check_function': check_aws_elb_latency,
    'service_description': 'AWS/ELB Latency',
    'includes': ['aws.include', 'backport.include'],
    'has_perfdata': True,
    'group': 'aws_elb_latency',
}

#.
#   .--HTTP ELB------------------------------------------------------------.
#   |             _   _ _____ _____ ____    _____ _     ____               |
#   |            | | | |_   _|_   _|  _ \  | ____| |   | __ )              |
#   |            | |_| | | |   | | | |_) | |  _| | |   |  _ \              |
#   |            |  _  | | |   | | |  __/  | |___| |___| |_) |             |
#   |            |_| |_| |_|   |_| |_|     |_____|_____|____/              |
#   |                                                                      |
#   '----------------------------------------------------------------------'


def check_aws_elb_http_elb(item, params, parsed):
    now = time.time()
    request_count = parsed.get('RequestCount')
    if request_count is not None:
        request_rate = get_rate('aws_elb_statistics', now, request_count)
        yield 0, 'Requests: %s/s' % request_rate, [('requests_per_second', request_rate)]
    else:
        request_rate = 0

    for http_errors_nr in ["4", "5"]:
        http_errors = parsed.get('HTTPCode_ELB_%sXX' % http_errors_nr)
        if http_errors is None:
            continue

        http_errors_rate = get_rate('aws_elb_http_backend.%sxx' % http_errors_nr, now, http_errors)
        yield (0, '%s00-Errors: %s/s' % (http_errors_nr, http_errors_rate),
               [('http_%sxx_rate' % http_errors_nr, http_errors_rate)])

        try:
            http_errors_perc = 100.0 * http_errors_rate / request_rate
        except ZeroDivisionError:
            pass
        else:
            yield check_levels_backport(
                http_errors_perc,
                'http_%sxx_perc' % http_errors_nr,
                params.get('levels_http_%sxx_perc' % http_errors_nr),
                unit='%',
                infoname="%s00-Errors of total requests" % http_errors_nr)


check_info['aws_elb.http_elb'] = {
    'inventory_function': lambda p: inventory_aws_generic_single(
        p, ['RequestCount', 'HTTPCode_ELB_4XX', 'HTTPCode_ELB_5XX']),
    'check_function': check_aws_elb_http_elb,
    'service_description': 'AWS/ELB HTTP ELB',
    'includes': ['aws.include', 'backport.include'],
    'has_perfdata': True,
    'group': 'aws_elb_http',
}

#.
#   .--HTTP Backend--------------------------------------------------------.
#   |   _   _ _____ _____ ____    ____             _                  _    |
#   |  | | | |_   _|_   _|  _ \  | __ )  __ _  ___| | _____ _ __   __| |   |
#   |  | |_| | | |   | | | |_) | |  _ \ / _` |/ __| |/ / _ \ '_ \ / _` |   |
#   |  |  _  | | |   | | |  __/  | |_) | (_| | (__|   <  __/ | | | (_| |   |
#   |  |_| |_| |_|   |_| |_|     |____/ \__,_|\___|_|\_\___|_| |_|\__,_|   |
#   |                                                                      |
#   '----------------------------------------------------------------------'


def check_aws_elb_http_backend(item, params, parsed):
    now = time.time()
    request_count = parsed.get('RequestCount')
    if request_count is not None:
        request_rate = get_rate('aws_elb_statistics', now, request_count)
        yield 0, 'Requests: %s/s' % request_rate, [('requests_per_second', request_rate)]
    else:
        request_rate = 0

    for http_errors_nr in ["4", "5"]:
        http_errors = parsed.get('HTTPCode_ELB_%sXX' % http_errors_nr)
        if http_errors is None:
            continue

        http_errors_rate = get_rate('aws_elb_http_backend.%sxx' % http_errors_nr, now, http_errors)
        yield (0, '%s00-Errors: %s/s' % (http_errors_nr, http_errors_rate),
               [('http_%sxx_rate' % http_errors_nr, http_errors_rate)])

        try:
            http_errors_perc = 100.0 * http_errors_rate / request_rate
        except ZeroDivisionError:
            pass
        else:
            yield check_levels_backport(
                http_errors_perc,
                'http_%sxx_perc' % http_errors_nr,
                params.get('levels_http_%sxx_perc' % http_errors_nr),
                unit='%',
                infoname="%s00-Errors of total requests" % http_errors_nr)

    http_backend_2xx = parsed.get('HTTPCode_Backend_2XX')
    if http_backend_2xx is not None:
        yield 0, '200-Requests: %s/s' % get_rate('aws_elb_http_backend.2xx', now, http_backend_2xx)

    http_backend_3xx = parsed.get('HTTPCode_Backend_3XX')
    if http_backend_3xx is not None:
        yield 0, '300-Requests: %s/s' % get_rate('aws_elb_http_backend.3xx', now, http_backend_3xx)


check_info['aws_elb.http_backend'] = {
    'inventory_function': lambda p: inventory_aws_generic_single(p, [
        'RequestCount', 'HTTPCode_Backend_2XX', 'HTTPCode_Backend_3XX', 'HTTPCode_Backend_4XX',
        'HTTPCode_Backend_5XX'
    ]),
    'check_function': check_aws_elb_http_backend,
    'service_description': 'AWS/ELB HTTP Backend',
    'includes': ['aws.include', 'backport.include'],
    'has_perfdata': True,
    'group': 'aws_elb_http',
}

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


def check_aws_elb_healthy_hosts(item, params, parsed):
    try:
        healthy_hosts = int(parsed["HealthyHostCount"])
    except (KeyError, ValueError):
        healthy_hosts = None

    try:
        unhealthy_hosts = int(parsed["UnHealthyHostCount"])
    except (KeyError, ValueError):
        unhealthy_hosts = None

    if healthy_hosts is not None:
        yield 0, 'Healthy hosts: %s' % healthy_hosts

    if unhealthy_hosts is not None:
        yield 0, 'Unhealthy hosts: %s' % unhealthy_hosts

    if healthy_hosts is not None and unhealthy_hosts is not None:
        total_hosts = unhealthy_hosts + healthy_hosts
        yield 0, 'Total: %s' % total_hosts

        try:
            perc = 100.0 * healthy_hosts / total_hosts
        except ZeroDivisionError:
            perc = None

        if perc is not None:
            yield check_levels_backport(
                perc,
                'aws_overall_hosts_health_perc',
                params.get('levels_overall_hosts_health_perc'),
                human_readable_func=get_percent_human_readable,
                infoname="Proportion of healthy hosts")


check_info['aws_elb.healthy_hosts'] = {
    'inventory_function': lambda p: inventory_aws_generic_single(
        p, ['HealthyHostCount', 'UnHealthyHostCount']),
    'check_function': check_aws_elb_healthy_hosts,
    'service_description': 'AWS/ELB Healthy Hosts',
    'includes': ['aws.include', 'backport.include'],
    'group': 'aws_elb_healthy_hosts',
}

#.
#   .--Backend errors------------------------------------------------------.
#   |                ____             _                  _                 |
#   |               | __ )  __ _  ___| | _____ _ __   __| |                |
#   |               |  _ \ / _` |/ __| |/ / _ \ '_ \ / _` |                |
#   |               | |_) | (_| | (__|   <  __/ | | | (_| |                |
#   |               |____/ \__,_|\___|_|\_\___|_| |_|\__,_|                |
#   |                                                                      |
#   |                                                                      |
#   |                     ___ _ __ _ __ ___  _ __ ___                      |
#   |                    / _ \ '__| '__/ _ \| '__/ __|                     |
#   |                   |  __/ |  | | | (_) | |  \__ \                     |
#   |                    \___|_|  |_|  \___/|_|  |___/                     |
#   |                                                                      |
#   '----------------------------------------------------------------------'


def check_aws_elb_backend_connection_errors(item, params, parsed):
    now = time.time()
    backend_connection_errors = parsed.get("BackendConnectionErrors")
    if backend_connection_errors is not None:
        yield check_levels_backport(
            get_rate('aws_elb_backend_connection_errors', now, backend_connection_errors),
            'backend_connection_errors_rate', params.get('levels_backend_connection_errors_rate'))


check_info['aws_elb.backend_connection_errors'] = {
    'inventory_function': lambda p: inventory_aws_generic_single(p, ['BackendConnectionErrors']),
    'check_function': check_aws_elb_backend_connection_errors,
    'service_description': 'AWS/ELB Backend Connection Errors',
    'includes': ['aws.include', 'backport.include'],
    'has_perfdata': True,
    'group': 'aws_elb_backend_connection_errors',
}
