#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2014             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.

# <<<mysql>>>
# [[mysql]]
# Aborted_clients 0
# Aborted_connects        15
# Binlog_cache_disk_use   0
# Binlog_cache_use        0
# Binlog_stmt_cache_disk_use      0
# Binlog_stmt_cache_use   0
# Bytes_received  7198841
# Bytes_sent      19266624
# Com_admin_commands      200
# Com_assign_to_keycache  0
# Com_alter_db    0
# Com_alter_db_upgrade    0

#   .--Helpers-------------------------------------------------------------.
#   |                  _   _      _                                        |
#   |                 | | | | ___| |_ __   ___ _ __ ___                    |
#   |                 | |_| |/ _ \ | '_ \ / _ \ '__/ __|                   |
#   |                 |  _  |  __/ | |_) |  __/ |  \__ \                   |
#   |                 |_| |_|\___|_| .__/ \___|_|  |___/                   |
#   |                              |_|                                     |
#   '----------------------------------------------------------------------'


@mysql_parse_per_item
def parse_mysql(info):
    data = {}
    for line in info:
        try:
            data[line[0]] = int(line[1])
        except IndexError:
            continue
        except ValueError:
            data[line[0]] = line[1]
    return data


@get_parsed_item_data
def check_mysql_version(_no_item, _no_params, data):
    version = data.get('version')
    if version:
        yield 0, "Version: %s" % version


check_info['mysql'] = {
    "parse_function": parse_mysql,
    "inventory_function": discover(lambda k, values: 'version' in values),
    "check_function": check_mysql_version,
    "service_description": "MySQL Version %s",
    "includes": ['mysql.include'],
}

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

# params:
# { "running" : (20, 40),
#    "total" : (100, 400),
#    "connections" : (3, 5 ),
# }


@get_parsed_item_data
def check_mysql_sessions(_no_item, params, data):
    total_sessions = data["Threads_connected"]
    running_sessions = data["Threads_running"]
    connects = get_rate("mysql.sessions", time.time(), data["Connections"])

    for value, perfvar, what, format_str, unit in [
        (total_sessions, "total_sessions", "total", "%d %s%s", ""),
        (running_sessions, "running_sessions", "running", "%d %s%s", ""),
        (connects, "connect_rate", "connections", "%.2f %s%s", "/s"),
    ]:
        infotext = format_str % (value, what, unit)
        status = 0
        if what in params:
            warn, crit = params[what]
            if value >= crit:
                status = 2
                infotext += "(!!)"
            elif value >= warn:
                status = 1
                infotext += "(!)"
        else:
            warn, crit = None, None

        yield status, infotext, [(perfvar, value, warn, crit)]


check_info['mysql.sessions'] = {
    "inventory_function": discover(lambda k, values: len(values.keys()) > 200),
    "check_function": check_mysql_sessions,
    "service_description": "MySQL Sessions %s",
    "has_perfdata": True,
    "group": "mysql_sessions",
}

#.
#   .--InnoDB-IO-----------------------------------------------------------.
#   |           ___                   ____  ____       ___ ___             |
#   |          |_ _|_ __  _ __   ___ |  _ \| __ )     |_ _/ _ \            |
#   |           | || '_ \| '_ \ / _ \| | | |  _ \ _____| | | | |           |
#   |           | || | | | | | | (_) | |_| | |_) |_____| | |_| |           |
#   |          |___|_| |_|_| |_|\___/|____/|____/     |___\___/            |
#   |                                                                      |
#   '----------------------------------------------------------------------'


@get_parsed_item_data
def check_mysql_iostat(item, params, data):
    if not ("Innodb_data_read" in data and "Innodb_data_written" in data):
        return

    line = [None, None, data["Innodb_data_read"] / 512, data["Innodb_data_written"] / 512]
    return check_diskstat_line(time.time(), 'innodb_io' + item, params, line)


check_info['mysql.innodb_io'] = {
    "inventory_function": discover(lambda k, values: "Innodb_data_read" in values),
    "check_function": check_mysql_iostat,
    "service_description": "MySQL InnoDB IO %s",
    "has_perfdata": True,
    "group": "mysql_innodb_io",
    "includes": ["diskstat.include"],
}

#.
#   .--Connections---------------------------------------------------------.
#   |        ____                            _   _                         |
#   |       / ___|___  _ __  _ __   ___  ___| |_(_) ___  _ __  ___         |
#   |      | |   / _ \| '_ \| '_ \ / _ \/ __| __| |/ _ \| '_ \/ __|        |
#   |      | |__| (_) | | | | | | |  __/ (__| |_| | (_) | | | \__ \        |
#   |       \____\___/|_| |_|_| |_|\___|\___|\__|_|\___/|_| |_|___/        |
#   |                                                                      |
#   +----------------------------------------------------------------------+


# TODO: This check should rather output the current number of connections.
# The historic maximum can be viewed in the RRD data...
@get_parsed_item_data
def check_mysql_connections(_no_item, params, data):
    if 'Max_used_connections' not in data:
        return 3, 'Connection information are missing'

    # The maximum number of connections that have been in use simultaneously
    # since the server started.
    conn = float(data['Max_used_connections'])

    # Maximum number of possible parallel connections
    max_conn = float(data['max_connections'])

    perc_used = conn / max_conn * 100

    status = 0
    status_txt = ''
    perf_data = [
        ("connections_max_used", conn),
        ("connections_max", max_conn),
        ('connections_perc_used', perc_used),
    ]

    if 'perc_used' in params:
        warn, crit = params['perc_used']
        if perc_used >= crit:
            status = 2
            status_txt = ' (Threshold (%0.2f%%) for number of maximum parallel connections ' \
                         'has been reached at least once since program start' % crit
        elif perc_used >= warn:
            status = 1
            status_txt = ' (Threshold (%0.2f%%) for number of maximum parallel connections ' \
                         'has been reached at least once since program start' % warn


    return status, 'Max. parallel Connections: %d (Max.: %d): %0.2f%%%s' % \
            (conn, max_conn, perc_used, status_txt), perf_data


@discover
def mysql_connections(instance, values):
    return all(x in values for x in ['Max_used_connections', 'max_connections'])


check_info['mysql.connections'] = {
    "inventory_function": mysql_connections,
    "check_function": check_mysql_connections,
    "service_description": "MySQL Connections %s",
    "group": "mysql_connections",
    "has_perfdata": True,
}
