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

# <<<veritas_vcs>>>
# ClusState        RUNNING
# ClusterName      minions
# #System          Attribute      Value
# dave             SysState       RUNNING
# stuart           SysState       RUNNING
# #Group           Attribute      System      Value
# ClusterService   State          stuart      |OFFLINE|
# nepharius        State          stuart      |ONLINE|
# lan              State          stuart      |ONLINE|
# omd              State          stuart      |ONLINE|
# #Resource        Attribute      System       Value
# nepharius_mrs    State          stuart      ONLINE
# nepharius_dr     State          stuart      ONLINE
# cs_ip            State          stuart      OFFLINE
# cs_proxy         State          stuart      ONLINE
# lan_nic          State          stuart      ONLINE
# lan_phantom      State          stuart      ONLINE
# omd_apache       State          stuart      ONLINE
# omd_appl         State          stuart      ONLINE
# omd_dg           State          stuart      ONLINE
# omd_proxy        State          stuart      ONLINE
# omd_srdf         State          stuart      ONLINE
# omd_uc4ps1_agt   State          stuart      ONLINE
# omdp_ip          State          stuart      ONLINE
# omdp_mnt         State          stuart      ONLINE
# #Group           Attribute      System      Value
# ClusterService   Frozen         global      0
# ClusterService   TFrozen        global      0
# #
# nepharius        Frozen         global      0
# nepharius        TFrozen        global      1
# #
# lan              Frozen         global      0
# lan              TFrozen        global      0
# #
# omd              Frozen         global      1
# omd              TFrozen        global      0

# parsed in case above (single node):
# parsed = {
#    'cluster': {u'minions': [(None, u'ClusState', u'RUNNING', None)]},
#    u'group': {u'ClusterService': [(None, u'State', u'OFFLINE', u'minions'),
#                                  (None, u'Frozen', u'0', u'minions'),
#                                  (None, u'TFrozen', u'0', u'minions')],
#              u'lan': [(None, u'State', u'ONLINE', u'minions'),
#                       (None, u'Frozen', u'0', u'minions'),
#                       (None, u'TFrozen', u'0', u'minions')],
#              u'nepharius': [(None, u'State', u'ONLINE', u'minions'),
#                             (None, u'Frozen', u'0', u'minions'),
#                             (None, u'TFrozen', u'1', u'minions')],
#              u'omd': [(None, u'State', u'ONLINE', u'minions'),
#                       (None, u'Frozen', u'1', u'minions'),
#                       (None, u'TFrozen', u'0', u'minions')]},
#    u'resource': {u'cs_ip': [(None, u'State', u'OFFLINE', u'minions')],
#                  u'cs_proxy': [(None, u'State', u'ONLINE', u'minions')],
#                  u'lan_nic': [(None, u'State', u'ONLINE', u'minions')],
#                  u'lan_phantom': [(None, u'State', u'ONLINE', u'minions')],
#                  u'nepharius_dr': [(None, u'State', u'ONLINE', u'minions')],
#                  u'nepharius_mrs': [(None, u'State', u'ONLINE', u'minions')],
#                  u'omd_apache': [(None, u'State', u'ONLINE', u'minions')],
#                  u'omd_appl': [(None, u'State', u'ONLINE', u'minions')],
#                  u'omd_dg': [(None, u'State', u'ONLINE', u'minions')],
#                  u'omd_proxy': [(None, u'State', u'ONLINE', u'minions')],
#                  u'omd_srdf': [(None, u'State', u'ONLINE', u'minions')],
#                  u'omd_uc4ps1_agt': [(None, u'State', u'ONLINE', u'minions')],
#                  u'omdp_ip': [(None, u'State', u'ONLINE', u'minions')],
#                  u'omdp_mnt': [(None, u'State', u'ONLINE', u'minions')]},
#    u'system': {u'dave': [(None, u'SysState', u'RUNNING', u'minions')],
#                u'stuart': [(None, u'SysState', u'RUNNING', u'minions')]}}
#

# parsed in case of Check_MK cluster definition:
# parsed = {
#    "cluster" : {u'minions' : [('server-1', 'State', 'RUNNING', None),
#                               ('server-2', 'State', 'OFFLINE', None),]},
#    "resource": {u'cs-ip': [('server-1', 'State', 'ONLINE',  u'minions'),
#                            ('server-2', 'State', 'OFFLINE', u'minions'),]},
#    "group": {...},
#    "system": {...},
# }


# Possible values for ClusState: RUNNING
# Possible values for SysState: RUNNING, FAULTED, EXITED
# Possible values for SG State: ONLINE, OFFLINE, FAULTED
# Possible values for resource State: ONLINE, OFFLINE, FAULTED, OFFLINE|STATE UNKNOWN, ONLINE|STATE UNKNOWN
# the STATE UNKNOWN is treated as UNKNOWN
#
#  NOTE: It seems to me there are way more possible values.
#        In the older version, all of these go to WARN(1).
#        We keep it that way, but make it configurable.

factory_settings['veritas_vcs_defaults'] = {
    'map_frozen': {
        'temporarily frozen': 1,
        'frozen': 2,
    },
    'map_states': {
        "ONLINE"  : 0,
        "RUNNING" : 0,
        "OK"      : 0,
        "OFFLINE" : 1,
        "EXITED"  : 1,
        "PARTIAL" : 1,
        "FAULTED" : 2,
        "UNKNOWN" : 3,
        "default" : 1,
    },
}


def parse_veritas_vcs(info):
    parsed = {}

    for line in info:
        if len(line) == 2 and line[1] == '#':
            continue
        node = line[0]

        if line[1] == "ClusState":
            section = parsed.setdefault('cluster', {})
            attr = line[1]
            value = line[2]

        elif line[1] == "ClusterName":
            cluster_name = line[2]
            section.setdefault(cluster_name, []).append((node, attr, value, None))

        elif line[1].startswith('#'):
            section = parsed.setdefault(line[1][1:].lower(), {})
            attr_idx = line.index('Attribute')
            value_idx = line.index('Value')

        elif len(line) > 3:
            item_name = line[1]
            attr = line[attr_idx]
            value = line[value_idx].replace("|","")
            if "UNKNOWN" in value:
                value = "UNKNOWN"
            section.setdefault(item_name, []).append((node, attr, value, cluster_name))

    return parsed


def veritas_vcs_boil_down_states_in_cluster(states):
    _stat = set(states)
    if len(_stat) == 1:
        return _stat.pop()
    for dominant in ("FAULTED", "UNKNOWN", "ONLINE", "RUNNING"):
        if dominant in _stat:
            return dominant
    return "AGGREGATION: %s" % ', '.join(_stat)


def inventory_veritas_vcs(parsed, item_type):
    for item_name in parsed[item_type]:
        yield item_name, None


def check_veritas_vcs(item, params, parsed, item_type):
    if item not in parsed[item_type]:
        return # vanished

    states = []
    infotexts = []
    cluster_info = ""
    map_frozen = params['map_frozen']
    map_states = params['map_states']

    for node, attr, value, cluster_name in parsed[item_type][item]:
        if attr.endswith('State'):
            states.append(value)

            infotext = value.lower()
            if node:
                infotext = "%s: %s" % (node, infotext)
            infotexts.append(infotext)

            if cluster_name:
                cluster_info = ", cluster: %s" % cluster_name

        if attr.endswith('Frozen') and value != '0':
            frozen_txt = attr.lower().replace('t', 'temporarily ')
            yield map_frozen.get(frozen_txt, 3), frozen_txt

    state_txt = veritas_vcs_boil_down_states_in_cluster(states)
    state = map_states.get(state_txt, map_states['default'])

    yield state, "%s%s" % (", ".join(infotexts), cluster_info)


#   .--cluster - main check -----------------------------------------------.
#   |                         _           _                                |
#   |                     ___| |_   _ ___| |_ ___ _ __                     |
#   |                    / __| | | | / __| __/ _ \ '__|                    |
#   |                   | (__| | |_| \__ \ ||  __/ |                       |
#   |                    \___|_|\__,_|___/\__\___|_|                       |
#   |                                                                      |
#   +----------------------------------------------------------------------+
#   |                           main check                                 |
#   '----------------------------------------------------------------------'


check_info['veritas_vcs'] = {
    'group'                  : 'veritas_vcs',
    'parse_function'         : parse_veritas_vcs,
    'inventory_function'     : lambda parsed: inventory_veritas_vcs(parsed, 'cluster'),
    'check_function'         : lambda item, _no_params, parsed: \
                                   check_veritas_vcs(item, _no_params, parsed, "cluster"),
    'service_description'    : 'VCS Cluster %s',
    'node_info'              : True,
    'default_levels_variable': 'veritas_vcs_defaults',
}

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

check_info['veritas_vcs.system'] = {
    'group'                  : 'veritas_vcs',
    'inventory_function'     : lambda parsed: inventory_veritas_vcs(parsed, "system"),
    'check_function'         : lambda item, _no_params, parsed: \
                                   check_veritas_vcs(item, _no_params, parsed, "system"),
    'service_description'    : 'VCS System %s',
    'node_info'              : True,
    'default_levels_variable': 'veritas_vcs_defaults',
}

#.
#   .--service group-------------------------------------------------------.
#   |                        _                                             |
#   |    ___  ___ _ ____   _(_) ___ ___    __ _ _ __ ___  _   _ _ __       |
#   |   / __|/ _ \ '__\ \ / / |/ __/ _ \  / _` | '__/ _ \| | | | '_ \      |
#   |   \__ \  __/ |   \ V /| | (_|  __/ | (_| | | | (_) | |_| | |_) |     |
#   |   |___/\___|_|    \_/ |_|\___\___|  \__, |_|  \___/ \__,_| .__/      |
#   |                                     |___/                |_|         |
#   '----------------------------------------------------------------------'

check_info['veritas_vcs.servicegroup'] = {
    'group'                  : 'veritas_vcs',
    'inventory_function'     : lambda parsed: inventory_veritas_vcs(parsed, 'group'),
    'check_function'         : lambda item, _no_params, parsed: \
                                   check_veritas_vcs(item, _no_params, parsed, 'group'),
    'service_description'    : 'VCS Service Group %s',
    'node_info'              : True,
    'default_levels_variable': 'veritas_vcs_defaults',
}

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

check_info['veritas_vcs.resource'] = {
    'group'                  : 'veritas_vcs',
    'inventory_function'     : lambda parsed: inventory_veritas_vcs(parsed, 'resource'),
    'check_function'         : lambda item, _no_params, parsed: \
                                   check_veritas_vcs(item, _no_params, parsed, 'resource'),
    'service_description'    : 'VCS Resource %s',
    'node_info'              : True,
    'default_levels_variable': 'veritas_vcs_defaults',
}
