#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import argparse
import yaml
import json
import subprocess
import glob
import datetime
import logging
import re
import direct_juggler.juggler as dj


def parse_options():
    parser = argparse.ArgumentParser(
        description=u'Аггрегирующий скрипт для проверок, использующий логи direct-spec'
    )
    parser.add_argument(
        '-c', '--config', type=str, required=True,
        help=u"путь к конфигу (для direct-spec-log-handler)"
    )
    parser.add_argument(
        '--checks-config', type=str, required=True,
        help=u"путь к конфигам проверки (для direct-spec, принимает glob)"
    )
    parser.add_argument(
        '--logs-dir', type=str, required=True,
        help=u"директория с логами direct-spec'а"
    )
    parser.add_argument(
        '-s', '--service', type=str,
        help=u"название juggler проверки"
    )
    return parser.parse_args()


def get_checks_list(checks_config):
    return yaml.load(subprocess.check_output(["/usr/local/bin/direct-spec", "--list-yaml"] + glob.glob(checks_config)))


def get_container_time_alive():
    return int(re.search(r'[0-9]+', subprocess.check_output(['ps', '-p', '1', '-o', 'etimes'])).group(0))


def run():
    container_time_alive = get_container_time_alive()

    opts = parse_options()

    with open(opts.config, 'r') as fh:
        config = yaml.load(fh)

    checks = {}
    for check in get_checks_list(opts.checks_config):
        check_name = check['name']
        checks[check_name] = {}
        for prop in config['default']:
            if check_name in config.get('checks', {}) and prop in config['checks'][check_name]:
                checks[check_name][prop] = config['checks'][check_name][prop]
            else:
                checks[check_name][prop] = config['default'][prop]
            checks[check_name]['juggler_event_name'] = check['juggler_event_name']

    checks = {name: props for name, props in checks.items() if int(props['period']) * 60 < container_time_alive}

    cur_dt = datetime.datetime.now()
    for check in checks:
        checks[check].update({
            'last_dt': cur_dt - datetime.timedelta(minutes=checks[check]['period']),
            'finished': False,
            'fails': 0,
            'occurrence': 0,
            'expected': checks[check]['period']
        })

    log_files = sorted(os.listdir(opts.logs_dir), reverse=True)
    count_left = len(checks)

    for log_file in log_files:
        if count_left == 0:
            break

        with open(os.path.join(opts.logs_dir, log_file), 'r') as fh:
            log_data = fh.read().strip().split('\n')

        for line in reversed(log_data):
            if not line:
                continue

            event_dt = datetime.datetime.strptime(line[:19], "%Y-%m-%d %H:%M:%S")
            data = json.loads(line[20:])

            if data[0] not in checks:
                continue

            if event_dt < checks[data[0]]['last_dt']:
                if not checks[data[0]]['finished']:
                    checks[data[0]]['finished'] = True
                    count_left -= 1

                continue

            checks[data[0]]['occurrence'] += 1
            if isinstance(data[1], list):
                checks[data[0]]['fails'] += 1

    crit_messages = []
    events = []
    for check in checks:
        fail_prop = 1.0 * checks[check]['fails'] / checks[check]['occurrence']
        crit_messages_for_check = []
        if fail_prop > checks[check]['fail_threshold']:
            crit_message = "%s: unavailability %.2f%%/%dm" % (check, fail_prop * 100, checks[check]['period'])
            crit_messages.append(crit_message)
            crit_messages_for_check.append(crit_message)
        missing_prop = 1.0 * (checks[check]['expected'] - checks[check]['occurrence']) / checks[check]['expected']
        if missing_prop > checks[check]['missing_threshold']:
            crit_message = "%s: missing %.2f%%/%dm" % (check, missing_prop * 100, checks[check]['period'])
            crit_messages.append(crit_message)
            crit_messages_for_check.append(crit_message)
 
        if opts.service:
            status_for_check = 'CRIT' if crit_messages_for_check else 'OK'
            description = ', '.join(crit_messages_for_check)
            events.append({
                'service': opts.service + '.' + checks[check]['juggler_event_name'],
                'status': status_for_check,
                'description': description
            })

    status = 'OK'
    description = ''
    if crit_messages:
        status = 'CRIT'
        description = ", ".join(crit_messages)

    if opts.service:
        dj.queue_events([{
            'service': opts.service,
            'status': status,
            'description': description
        }])
        dj.queue_events(events)     # DIRECT-125404
    else:
        print status, "\n", description

    logging.info("%s %s(%s)" % (status, opts.service + " " if opts.service else "", description))

    return


if __name__ == '__main__':
    logging.basicConfig(
        format=u'%(levelname)-8s [%(asctime)s] %(message)s',
        level=logging.INFO,
        filename="/var/log/yandex/direct-spec-log-handler/log.%s" % (datetime.datetime.now().day)
    )
    try:
        logging.info("start")
        run()
        logging.info("end")
    except:
        logging.exception("unexpected exception")

