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

# In cooperation with Thorsten Bruhns

# <<<oracle_instance:sep(124)>>>
# TUX2|12.1.0.1.0|OPEN|ALLOWED|STARTED|6735|1297771692|ARCHIVELOG|PRIMARY|NO|TUX2
# TUX5|12.1.0.1.1|MOUNTED|ALLOWED|STARTED|82883|1297771692|NOARCHIVELOG|PRIMARY|NO|0|TUX5

factory_settings["oracle_instance_defaults"] = {
    "logins"  : 2,
    "noforcelogging"  : 1,
    "noarchivelog"  : 1,
    "primarynotopen"  : 2,
}


def inventory_oracle_instance(info):
    # Skip ORA- error messages from broken old oracle agent
    # <<<oracle_instance:sep(124)>>>
    # ORA-99999 tnsping failed for +ASM1
    return [ (line[0], {} ) for line in info
             if not (line[0].startswith('ORA-') and line[0][4].isdigit())
                 and len(line[0]) < 20 ]


def check_oracle_instance(item, params, info):

    def state_marker(state, infotext, param, column, data):
	value = params.get(param)
	if value != None and column == data:
            state = max(state, value)
            if value == 1:
                infotext += '(!)'
            elif value == 2:
                infotext += '(!!)'
        return state, infotext

    state = 0
    for line in info:
        if line[0] == item:
            # In case of a general error (e.g. authentication failed), the second
            # column contains the word "FAILURE"
            if line[1] == 'FAILURE':
                return 2, " ".join(line[2:])

            state = 0

            # Be compatible to old oracle agent plugin output
            if len(line) == 6:
                sid, version, openmode, logins, _unused, _unused2 = line
                infotext = 'Status %s, Version %s, Logins %s' % (openmode, version, logins.lower())
                state, infotext = state_marker(state, infotext, 'logins', logins, 'RESTRICTED')
                return state, infotext

            sid, version, openmode, logins, archiver, up_seconds, dbid, \
                log_mode, database_role, force_logging, name = line

            infotext = "Database Name %s, Status %s" % \
                       (name, openmode)

            # Check state for PRIMARY Database. Normaly there are always OPEN
            if database_role == 'PRIMARY' and openmode != 'OPEN':
                state = params.get('primarynotopen')
                if state == 1:
                    infotext += '(!)'
                elif state == 2:
                    infotext += '(!!)'
                elif state == 0:
                    infotext += ' (allowed by rule)'

            # ORACLE is sick and cannot handle timezone changes >:-P
            up_seconds = max(0, int(up_seconds))
            infotext += ", Role %s, Version %s, " \
                       "Up since %s (%s)" % (
                       database_role, version, \
                       time.strftime("%F %T", time.localtime(time.time() - up_seconds)), \
                       get_age_human_readable(up_seconds))

            if params.get('uptime_min'):
                warn, crit = params.get('uptime_min')
                warn = int(warn)
                crit = int(crit)

                infotext += ' (warn/crit at %s/%s)' % (get_age_human_readable(warn), get_age_human_readable(crit))
                if up_seconds <= crit:
                    state = 2
                    infotext += '(!!) not long enough up'
                elif up_seconds <= warn:
                    state = max(state, 1)
                    infotext += '(!) not long enough up'

            # ASM has no login and archivelog check
            if database_role != 'ASM':

                # logins are only possible when the database is open
                if openmode == 'OPEN':
                    infotext += ', Logins %s' % (logins.lower())
                    state, infotext = state_marker(state, infotext, 'logins', logins, 'RESTRICTED')

                # the new internal database _MGMTDB from 12.1.0.2 is always in NOARCHIVELOG mode
                if name != '_MGMTDB' and sid != '-MGMTDB':
                    infotext += ', Log Mode %s' % (log_mode.lower())
                    state, infotext = state_marker(state, infotext, 'archivelog', log_mode, 'ARCHIVELOG')
                    state, infotext = state_marker(state, infotext, 'noarchivelog',log_mode,  'NOARCHIVELOG')

                    # force logging is only usable when archivelog is enabled
                    if log_mode == 'ARCHIVELOG':
                        if archiver != 'STARTED':
                            infotext += '. Archiver %s(!!)' % (archiver.lower())
                            state = 2

                        infotext += ', Force Logging %s' % (force_logging.lower())
                        state, infotext = state_marker(state, infotext, 'forcelogging', force_logging, 'YES')
                        state, infotext = state_marker(state, infotext, 'noforcelogging', force_logging, 'NO')

            perfdata = [('uptime', up_seconds)]
            return state, infotext, perfdata

    return 2, "Database not running or login failed"


check_info['oracle_instance'] = {
    "check_function"          : check_oracle_instance,
    "inventory_function"      : inventory_oracle_instance,
    "service_description"     : "ORA %s Instance",
    "default_levels_variable" : "oracle_instance_defaults",
    "group"                   : "oracle_instance",
    "has_perfdata"            : True,
}
