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


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

# Target value for agent's IP access configuration. Only if this
# is not None, the inventory will create services
check_mk_only_from_default = None

def inventory_only_from(info):
    if check_mk_only_from_default != None:
        for line in info:
            if line[0] == "OnlyFrom:":
                return [(None, 'check_mk_only_from_default')]

def check_mk_factorize_curly(n):
    # factorize 10.0.0.{1,2,3}
    if '{' in n:
        result = []
        iprange = n[n.find('{') + 1:n.find('}')].split(',')
        prefix = n[:n.find('{')]
        for suffix in iprange:
            result.append(prefix + suffix)
        return result
    else:
        return [n]

def check_mk_normalize_network(n):
    if '/' in n:
        return n
    else:
        return n + "/32"


def check_only_from(item, param, info):
    if param == None:
        return (1, "IP access restriction not monitored for this host")
    for line in info:
        if line[0] == "OnlyFrom:":
            an = []
            for n in line[1:]:
                an += check_mk_factorize_curly(n)

            allowed_nets = map(check_mk_normalize_network, an)
            should_nets = map(check_mk_normalize_network, param)

            too_much = []
            too_few = []

            for net in allowed_nets:
                if net not in should_nets:
                    too_much.append(net)
            for net in should_nets:
                if net not in allowed_nets:
                    too_few.append(net)
            status = 0
            infotexts = []
            if len(too_much) > 0:
                status = 1
                infotexts.append("agent allows extra: %s" % (" ".join(too_much)))
            if len(too_few) > 0:
                status = 1
                infotexts.append("agent blocks: %s" % (" ".join(too_few)))
            if status == 1:
                return (1, "invalid access configuration: %s" % (", ".join(infotexts)))
            else:
                return (0, "allowed IP ranges: %s" % (" ".join(allowed_nets)))
    return (3, "Agent does not send OnlyFrom: header")



check_info["check_mk.only_from"] = {
    'check_function':          check_only_from,
    'inventory_function':      inventory_only_from,
    'service_description':     'Check_MK Agent Access',
}


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

# Example output from agent:
# <<<check_mk>>>
# AgentUpdate: last_check 1447777834.22 last_update 1447776761.52 aghash e33d0cebcf7404d9 error None


def inventory_cmk_agent_update(info):
    for line in info:
        if line[0] == "AgentUpdate:":
            return [ (None, {}) ]

def check_cmk_agent_update(_no_item, _no_params, info):
    for line in info:
        if line[0] == "AgentUpdate:":
            parsed = {}
            parts = line[1:]
            while parts:
                key = parts[0]
                if key == "error":
                    value = " ".join(parts[1:])
                    parts = []
                else:
                    value = parts[1]
                    if value == "None":
                        value = None
                    parts = parts[2:]
                parsed[key] = value

            now = time.time()

            if parsed["error"] != "None":
                yield 1, "error: %s" % parsed["error"]
            else:
                yield 0, "no errors"

            try:
                last_check = float(parsed["last_check"])
                age = now - last_check
                # Currently there is no crit level
                warn = 2 * 3600 * 24
                if age > warn:
                    state = 1
                else:
                    state = 0
                if state:
                    levels_text = " (warn at %s)" % (get_age_human_readable(warn))
                else:
                    levels_text = ""
                yield state, "last update check: " + get_timestamp_human_readable(parsed["last_check"]) + levels_text
            except:
                yield 1, "no successful connect to server yet"

            if parsed["last_update"]:
                yield 0, "last agent update: %s" % get_timestamp_human_readable(parsed["last_update"])

            if parsed["aghash"]:
                yield 0, "agent configuration: %s" % parsed["aghash"][:8]

            return


check_info["check_mk.agent_update"] = {
    'check_function'      : check_cmk_agent_update,
    'inventory_function'  : inventory_cmk_agent_update,
    'service_description' : 'Check_MK Agent'
}
