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


# Example for output from agent
# ---------------------------------------------------------
#<<<mtr>>>
#www.google.com|1451237587|8|80.69.76.120|0.0%|10|39.2|7.0|0.3|39.2|14.2|80.249.209.100|0.0%|10|1.2|1.2|1.0|1.4|0.0|209.85.240.61|0.0%|10|1.4|1.6|1.2|2.4|0.0|209.85.248.247|0.0%|10|1.6|1.7|1.5|2.1|0.0|216.239.51.17|0.0%|10|4.8|5.0|4.7|5.3|0.0|216.239.49.36|0.0%|10|6.1|5.5|4.7|8.8|1.1|???|100.0|10|0.0|0.0|0.0|0.0|0.0|74.125.136.105|0.0%|10|4.7|5.0|4.7|5.4|0.0
# ---------------------------------------------------------

factory_settings["mtr_default_levels"] = {
    "pl"         : (10 , 25),  # warn/crit for loss percentage
    "rta"        : (150, 250), # warn/crit for average roundtrip time
    "rtstddev"   : (150, 250), # warn/crit for standard deviation
}

def parse_mtr(info):
    hosts = {}
    for line in info:
        if line[0].startswith("**ERROR**"):
            continue

        ret = {'hops':{} }
        if not len(line) > 0:
            return ret
        hostname = line[0]
        ret['hostname']   = hostname
        ret['last_check'] = line[1]
        ret['hopcount']   = int(float(line[2]))
        rest = line[3:]

        if ret['hopcount'] > 0:
            for hopnum in range(0, ret['hopcount']):
                ret['hops'][hopnum + 1] = {
                    'hopname'       : rest[0 + 8*hopnum],
                    'pl'            : rest[1 + 8*hopnum].replace("%","").rstrip(),
                    'snt'           : rest[2 + 8*hopnum],
                    'response_time' : rest[3 + 8*hopnum],
                    'rta'           : rest[4 + 8*hopnum],
                    'rtmin'         : rest[5 + 8*hopnum],
                    'rtmax'         : rest[6 + 8*hopnum],
                    'rtstddev'      : rest[7 + 8*hopnum],
                }
        hosts[hostname] = ret
    return hosts

def inventory_mtr(parsed):
    for host in parsed.keys():
        yield host, {}

def check_mtr(item, params, parsed):
    hostinfo = parsed.get(item)
    if not hostinfo:
        return

    if hostinfo["hopcount"] == 0:
        yield 3, "Insufficient data: No hop information available"
        return

    hopnames = []
    perfdata = []
    hops = hostinfo['hops']

    def get_hop_info(hops, idx, apply_levels = False):
        if apply_levels:
            pl_warn,       pl_crit        = params["pl"]
            rta_warn,      rta_crit       = params["rta"]
            rtstddev_warn, rtstddev_crit  = params["rtstddev"]
        else:
            pl_warn,       pl_crit        = None, None
            rta_warn,      rta_crit       = None, None
            rtstddev_warn, rtstddev_crit  = None, None

        hop = hops[idx]
        hopnames.append(hop["hopname"])
        rta           = float(hop['rta'])
        rtmin         = float(hop['rtmin'])
        rtmax         = float(hop['rtmax'])
        rtstddev      = float(hop['rtstddev'])
        response_time = float(hop['response_time'])
        pl            = float(hop['pl'])
#        snt    = float(hop['snt'])
#        perfdata.append( ( 'hop_%d_snt' % idx, snt, "", "", "", "" ) )  # packets sent
        perfdata.append( ('hop_%d_rta'           % idx, rta / 1000 , rta_warn and rta_warn / 1000,\
                                                   rta_crit and rta_crit / 1000) )
        perfdata.append( ('hop_%d_rtmin'         % idx, rtmin / 1000) )
        perfdata.append( ('hop_%d_rtmax'         % idx, rtmax / 1000) )
        perfdata.append( ('hop_%d_rtstddev'      % idx, rtstddev / 1000, rtstddev_warn and rtstddev_warn / 1000,\
                                                   rtstddev_crit and rtstddev_crit / 1000) )
        perfdata.append( ('hop_%d_response_time' % idx, response_time / 1000) )
        perfdata.append( ('hop_%d_pl'            % idx, pl, pl_warn, pl_crit) )

        if apply_levels: # the params only apply to the last host
            hop_state = 0
            hop_text_entries  = []
            for what, unit, value, warn, crit in [ ("Packet loss", "%", pl, pl_warn, pl_crit),
                                                   ("Round trip average", "ms", rta, rta_warn, rta_crit),
                                                   ("Standard deviation", "ms", rtstddev, rtstddev_warn, rtstddev_crit) ]:
                what_state = 0
                hop_text   = ""
                if value >= crit:
                    what_state = 2
                elif value >= warn:
                    what_state = 1
                hop_text = "%s %s%s%s" % (what, value, unit, what_state and state_markers[what_state] or "")
                if what_state:
                    hop_text += " (Levels at %d%s/%d%s)" % (warn, unit, crit, unit)
                hop_text_entries.append(hop_text)

            return what_state, ", ".join(hop_text_entries), perfdata
        else:
            return None, None, perfdata

    hc = int(hostinfo['hopcount'])
    hop_perfdata = [('hops', hc)]

    def format_hopnames(hopnames):
        result = "Hops in last check:\n"
        for idx, name in enumerate(hopnames):
            result += "Hop %d: %s\n" % (idx + 1, name)
        return result

    for idx in range(0, hc):
        state, text, perfdata = get_hop_info(hops, idx + 1, apply_levels = (idx + 1 == hc))
        if state == None:
            # This is perfdata from a hop, we do not yield it yet (looks ugly..)
            hop_perfdata.extend(perfdata)
        else:
            # Last hop with state, text and perfdata. Yield everything we have
            yield 0, "Number of Hops: %d" % hc, hop_perfdata
            yield state, text + "\r\n%s" % format_hopnames(hopnames), perfdata


check_info['mtr'] = {
    "parse_function"          : parse_mtr,
    "check_function"          : check_mtr,
    "inventory_function"      : inventory_mtr,
    "service_description"     : "Mtr to %s",
    "has_perfdata"            : True,
    "group"                   : "mtr",
    "default_levels_variable" : "mtr_default_levels",
}

