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

import getopt
import os
import sys
import time
import traceback
import urllib
import ast

import requests
import urllib3  # type: ignore
from pathlib2 import Path

import cmk.utils.password_store

cmk.utils.password_store.replace_passwords()

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# tell requests not to honour "http(s)_proxy" env variables
proxies = {
    'http': None,
    'https': None,
}


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
  --use-automation-user Use credentials from the local "automation" user
  -m AUTH_MODE          Authentication mode, either "cookie", "basic", "digest"
                        or "kerberos", 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=", "use-automation-user"]

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
use_automation_user = False

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
    elif o == '--use-automation-user':
        use_automation_user = True

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 use_automation_user:
    username = "automation"
    secret_file_path = Path(cmk.utils.paths.var_dir) / "web" / username / "automation.secret"
    try:
        with secret_file_path.open(encoding="utf-8") as f:  # pylint: disable=no-member
            password = f.read()
    except IOError:
        sys.stderr.write('Unable to read credentials for "automation" user.\n')
        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():
    auth_obj = None
    if username and password:
        if auth_mode == 'kerberos':
            from requests_kerberos import HTTPKerberosAuth  # type: ignore

            from subprocess import Popen, PIPE
            kinit = Popen(["kinit", username], stdin=PIPE, stdout=PIPE, stderr=PIPE)
            output, errors = kinit.communicate("%s\n" % password)
            kinit.wait()
            if kinit.returncode or errors:
                sys.stderr.write("Error getting Kerberos Ticket:\n")
                sys.stderr.write("stdout: %s\nstderr: %s\nrc: %s" %
                                 (output, errors, kinit.returncode))
                sys.exit(1)

            auth_obj = HTTPKerberosAuth(principal=username)
        elif auth_mode == 'digest':
            auth_obj = requests.auth.HTTPDigestAuth(username, password)
        else:
            auth_obj = requests.auth.HTTPBasicAuth(username, password)
    return auth_obj

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

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

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

try:
    r = requests.get(url, timeout=timeout, auth=auth, proxies=proxies)
    r.raise_for_status()
    raw_response = r.text
except requests.Timeout:
    sys.stdout.write('ERROR: Socket timeout while opening URL: %s\n' % (url))
    sys.exit(3)
except requests.URLRequired, 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%s' %
                     (url, e, traceback.format_exc()))
    sys.exit(3)

if raw_response.startswith("ERROR:"):
    sys.stdout.write(raw_response.rstrip() + "\n")
    sys.exit(3)

try:
    obj = ast.literal_eval(raw_response)
except Exception, e:
    sys.stdout.write('ERROR: Invalid response (%s): %s\n' % (e, raw_response))
    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 not isinstance(obj, list):
    sys.stdout.write('ERROR: Invalid response: %s\n' % (raw_response.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)
