#!/usr/bin/python
# encoding: utf-8
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | 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.

import sys, getopt, urllib2, traceback
import os
import time


# tell urllib2 not to honour "http(s)_proxy" env variables
urllib2.getproxies = lambda: {}

def usage():
    sys.stderr.write("""
USAGE: check_bi_aggr -b <BASE_URL> -a <AGGR_NAME> -u <USER> -s <SECRET>
                     [-m <AUTH_MODE>] [-r] [-n <HOSTNAME>] [-t <TIMEOUT>] [-d]

OPTIONS:
  -b BASE_URL       The base URL to the monitoring environment, e.g.
                    http://<hostname>/<site-id>
  -a AGGR_NAME      Name of the aggregation, not the aggregation group.
                    It is possible that there are multiple aggregations
                    with an equal name, but you should ensure, that it
                    is a unique one to prevent confusions
  -u USER           User-ID of an automation user which is permitted to
                    see all contents of the aggregation
  -s SECRET         Automation secret of the user
  -m AUTH_MODE      Authentication mode, either "cookie", "basic" or "digest",
                    defaults to "cookie"
  -t TIMEOUT        HTTP connect timeout in seconds (Default: 60)
  -r                track downtimes. This requires the hostname to be set.
  -n HOSTNAME       The hostname for which this check is run.
  --in-downtime S   S can be "ok" or "warn". Force this state if the
                    aggregate is in scheduled downtime. OK states will always
                    be unchanged.
  --acknowledged S  Same as --in-downtime, but for acknowledged aggregates.
  -d                Enable debug mode
  -h, --help        Show this help message and exit

""")

short_options = 'b:a:u:s:m:t:n:dhr'
long_options = [ "help", "in-downtime=", "acknowledged=" ]

try:
    opts, args = getopt.getopt(sys.argv[1:], short_options, long_options)
except getopt.GetoptError, err:
    sys.stderr.write("%s\n" % err)
    sys.exit(1)

base_url         = None
aggr_name        = None
username         = None
password         = None
auth_mode        = 'cookie'
timeout          = 60
debug            = False
opt_in_downtime  = None
opt_acknowledged = None
track_downtime   = False
hostname         = None


for o,a in opts:
    if o in [ '-h', '--help' ]:
        usage()
        sys.exit(0)
    elif o == '-b':
        base_url = a
    elif o == '-a':
        aggr_name = a
    elif o == '-u':
        username = a
    elif o == '-s':
        password = a
    elif o == '-m':
        auth_mode = a
    elif o == '-t':
        timeout = int(a)
    elif o == '-r':
        track_downtime = True
    elif o == '-n':
        hostname = a
    elif o == '-d':
        debug = True
    elif o == '--in-downtime':
        opt_in_downtime = a
    elif o == '--acknowledged':
        opt_acknowledged = a


if not base_url:
    sys.stderr.write('Please provide the URL to the monitoring instance.\n')
    usage()
    sys.exit(1)

if not aggr_name:
    sys.stderr.write('Please provide the name of the aggregation.\n')
    usage()
    sys.exit(1)

if not username or not password:
    sys.stderr.write('Please provide valid user credentials.\n')
    usage()
    sys.exit(1)

if track_downtime and not hostname:
    sys.stderr.write('Please provide a hostname when using '
                     'downtime tracking.\n')
    usage()
    sys.exit(1)


def init_auth():
    if username and password:
        passwdmngr = urllib2.HTTPPasswordMgrWithDefaultRealm()
        passwdmngr.add_password(None, base_url, username, password)
        if auth_mode == 'digest':
            authhandler = urllib2.HTTPDigestAuthHandler(passwdmngr)
        else:
            authhandler = urllib2.HTTPBasicAuthHandler(passwdmngr)
        opener = urllib2.build_opener(authhandler)
        urllib2.install_opener(opener)

url = "%s/check_mk/view.py" \
      "?view_name=aggr_single_api" \
      "&aggr_name=%s&output_format=python" % \
      (base_url.rstrip('/'), urllib2.quote(aggr_name))

if auth_mode in ['basic', 'digest']:
    init_auth()
else:
    url += "&_username=%s&_secret=%s" % \
        (urllib2.quote(username), urllib2.quote(password))

if debug:
    sys.stderr.write('URL: %s\n' % url)

try:
    json = urllib2.urlopen(url, timeout = timeout).read()
except urllib2.socket.timeout:
    sys.stdout.write('ERROR: Socket timeout while opening URL: %s\n' % (url))
    sys.exit(3)
except urllib2.URLError, e:
    sys.stdout.write("UNKNOWN: %s\n" % e)
    sys.exit(3)
except Exception, e:
    sys.stdout.write('ERROR: Exception while opening URL (%s): %s\n' %
                                            (url, traceback.format_exc()))
    sys.exit(3)

try:
    obj = eval(json)
except Exception, e:
    sys.stdout.write('ERROR: Invalid json response (%s): %s\n' % (e, json))
    sys.exit(3)

if len(obj) == 1:
    sys.stdout.write('ERROR: Aggregation "%s" does not exist or user is not permitted\n' % aggr_name)
    sys.exit(3)

if type(obj) != list or len(obj) != 2:
    sys.stdout.write('ERROR: Invalid response: %s\n' % (json.replace("\n", "")))
    sys.exit(3)

headers = obj[0]
row = dict(zip(headers, obj[1]))

aggr_output = row["aggr_output"]
aggr_state = int(row["aggr_state_num"])

if aggr_state == -1:
    aggr_state = 3

if aggr_output == '':
    aggr_output = 'Aggregation state is %s' % ['OK', 'WARN', 'CRIT', 'UNKNOWN'][aggr_state]

# Handle downtimes and acknowledgements
if opt_in_downtime and row["aggr_in_downtime"] == '1':
    aggr_output += ", currently in downtime"
    if opt_in_downtime == "ok":
        aggr_state = 0
    else: # "warn"
        aggr_state = min(aggr_state, 1)

if track_downtime:
    # connect to livestatus
    try:
        import livestatus
    except ImportError:
        sys.stderr.write('The python livestatus api module is missing. Please install from\n'
                        'Check_MK livestatus sources to a python import path.\n')
        sys.exit(1)

    socket_path = os.environ['OMD_ROOT'] + '/tmp/run/live'

    conn = livestatus.SingleSiteConnection('unix:' + socket_path)

    now = time.time()
    # find out if, according to previous tracking, there already is a downtime
    ids = conn.query_table(("GET downtimes\n"
                            "Columns: id\n"
                            "Filter: service_description = Aggr Host %s\n"
                            "Filter: author = tracking\n"
                            "Filter: end_time > %d") % (hostname, now))
    downtime_tracked = len(ids) > 0
    if downtime_tracked != (row["aggr_in_downtime"] == '1'):
        # there is a discrepance between tracked downtime state and the real state
        if row["aggr_in_downtime"] == '1':
            # need to track downtime
            conn.command("[%d] SCHEDULE_SVC_DOWNTIME;%s;Aggr Host %s;%d;%d;1;0;0;"
                         "tracking;Automatic downtime" % (now, hostname, hostname, now, 2147483647))
        else:
            for dt_id in ids:
                conn.command("[%d] DEL_SVC_DOWNTIME;%d" % (now, dt_id[0]))


if opt_acknowledged and row["aggr_acknowledged"] == '1':
    aggr_output += ", is acknowledged"
    if opt_acknowledged == "ok":
        aggr_state = 0
    else: # "warn"
        aggr_state = min(aggr_state, 1)


sys.stdout.write('%s\n' % aggr_output)
sys.exit(aggr_state)

