#!/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-------------------------------------------------------------.
#   |                  _   _      _                                        |
#   |                 | | | | ___| |_ __   ___ _ __ ___                    |
#   |                 | |_| |/ _ \ | '_ \ / _ \ '__/ __|                   |
#   |                 |  _  |  __/ | |_) |  __/ |  \__ \                   |
#   |                 |_| |_|\___|_| .__/ \___|_|  |___/                   |
#   |                              |_|                                     |
#   '----------------------------------------------------------------------'

def inventory_mysql(parsed):
    for instance, values in parsed.items():
        if len(values.keys()) > 200: # should be at least 321 line
            return [(instance, {})]

# FIXME: Crapy copy n paste! Consolidate with other mysql_* parse functions
def parse_mysql(info):
    def parse_line(line):
       if len(line) == 2:
           varname, value = line
           try:
               value = int(value)
           except:
                pass
       else:
           varname = line[0]
           value = None
       return varname, value

    parsed = {}
    instance = False
    for line in info:
        if line[0].startswith("[["):
            instance = line[0][2:-2]
            if instance == "":
                instance = "mysql"
            parsed[instance] = {}
        elif instance:
            varname, value = parse_line(line)
            parsed[instance][varname] = value

    # Old Agent Plugin, no Instances in output
    if not instance:
        parsed['mysql'] = {}
        for line in info:
            varname, value = parse_line(line)
            parsed['mysql'][varname] = value

    return parsed

def inventory_mysql_version(parsed):
    for instance, values in parsed.items():
        if 'version' in values:
            return [(instance, {})]

def check_mysql_version(item, _no_params, parsed):
    if item in parsed:
        values = parsed[item]
        return 0, "Version: " + values['version']


check_info['mysql'] = {
    "parse_function"          : parse_mysql,
    "inventory_function"      : inventory_mysql_version,
    "check_function"          : check_mysql_version,
    "service_description"     : "MySQL Version %s",
}


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

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


def check_mysql_sessions(item, params, parsed):
    if item in parsed:
        values = parsed[item]
        total_sessions = values["Threads_connected"]
        running_sessions = values["Threads_running"]
        connects = get_rate("mysql.sessions", time.time(), values["Connections"])

        infotext = " - %d sessions (%d running), %.2f connects/s" % \
                   (total_sessions, running_sessions, connects)

        infos = []
        perfdata = []
        status = 0

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

        return status, ", ".join(infos), perfdata


check_info['mysql.sessions'] = {
    "inventory_function"      : inventory_mysql,
    "check_function"          : check_mysql_sessions,
    "service_description"     : "MySQL Sessions %s",
    "has_perfdata"            : True,
    "group"                   : "mysql_sessions",
}

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

def inventory_mysql_iostat(parsed):
    for instance, values in parsed.items():
        if "Innodb_data_read" in values.keys():
            yield instance, {}

def check_mysql_iostat(item, params, parsed):
    if item in parsed:
        values = parsed[item]
        line = [ None, None, values["Innodb_data_read"] / 512, values["Innodb_data_written"] / 512]
        return check_diskstat_line(time.time(), 'innodb_io'+item, params, line)


check_info['mysql.innodb_io'] = {
    "inventory_function"      : inventory_mysql_iostat,
    "check_function"          : check_mysql_iostat,
    "service_description"     : "MySQL InnoDB IO %s",
    "has_perfdata"            : True,
    "group"                   : "mysql_innodb_io",
    "includes"                : [ "diskstat.include" ],
}

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

def inventory_mysql_connections(parsed):
    for instance, values in parsed.items():
        if 'Max_used_connections' in values and 'max_connections' in values:
            yield instance, {}

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

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

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

        perc_used = conn / max_conn * 100

        status = 0
        status_txt = ''

        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)


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

