#!/usr/bin/env python
# Copyright European Organization for Nuclear Research (CERN) 2013
#
# Licensed under the Apache License, Version 2.0 (the "License");
# You may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Authors:
# - Ralph Vigne, <ralph.vigne@cern.ch>, 2014
# - Mario Lassnig, <mario.lassnig@cern.ch>, 2015

import argparse
import json
import logging
import requests
import sys

from pystatsd import Client
from sys import stdout

logging.basicConfig(stream=stdout,
                    level=logging.ERROR,
                    format='%(asctime)s\t%(process)d\t%(levelname)s\t%(message)s')

logger = logging.getLogger(__name__)


def monitor_agent(uri):
    logger.info('Requesting status from Apache running on %s' % uri)

    response = requests.get(uri, timeout=1)
    if response.status_code != 200:
        logger.error('Invalid HTTP status code')
        logger.error(response)

    all_stats = json.loads(response.text)
    INCLUDE = ['ChannelFillPercentage', 'EventDrainSuccessCount', 'EventAcceptedCount', 'ConnectionFailedCount']
    # Proposed thresholds:  CHANNEL.ChannelFillPercentage 50%
    #                       SINK.ConnectionFailedCount > 0
    #                       SINK.EventAcceptedCount - SINK.EventDrainSuccessCount > 200 (becaus read in chunks of hundred)

    server_name = get_server_name(uri)
    stats = dict()
    stats['server_name'] = server_name
    for s in all_stats:
        for m in INCLUDE:
            if m in all_stats[s]:
                stats['%s.%s' % (s, m)] = all_stats[s][m]
    logger.debug(stats)
    return stats


def backend_graphite(url, stats, prefix):
    server, port = url.split(':')
    try:
        pystatsd_client = Client(host=server, port=port, prefix='%s.%s' % (prefix, stats['server_name']))
    except Exception, e:
        logger.error('Unable to connect to Graphite backend %s: %s' % (url, e))
        raise
    for s in stats:
        if s in ['server_name']:
            continue
        try:
            logger.debug('%s.%s.%s => %s' % (prefix, stats['server_name'], s, float(stats[s])))
            pystatsd_client.gauge(s, float(stats[s]))
        except Exception as e:
            logger.error('Failed reporting %s (%s): %s' % (s, stats[s], e))


def get_server_name(fqdn):
    return fqdn.split('//')[1].split('.')[0]

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Takes backend classes and Apaches URLs')
    parser.add_argument('--backend', metavar='B', type=str, nargs='+', help='a list of type::URL[:port][::scope] to which the script will report to. E.g. --backend graphite::rucio-graphite-int.cern.ch:8025/listen/now::rucio.backends xsls::xsls.cern.ch')
    parser.add_argument('--server', metavar='S', type=str, nargs='+', help='a list of FQDNs. --server http://rucio-daemon-prod-01.cern.ch:34545/metrics')
    parser.add_argument('--verbose', help='makes it chatty', action="store_true")
    args = parser.parse_args()

    if args.verbose:
        logger.setLevel(level=logging.DEBUG)
    args = vars(args)

    servers = args['server']
    if not len(servers):
        logger.critical("No server to monitor provided. Exiting.")
        sys.exit(1)

    backends = []
    for backend in args['backend']:
        try:
            tmp = backend.split('::')
            if len(tmp) < 2 or len(tmp) > 3:
                raise ValueError
            t, url, prefix = tmp if len(tmp) == 3 else [tmp[0], tmp[1], None]
            logger.debug('Reporting to backend => Type: %s\tURL: %s\tPrefix: %s' % (t, url, prefix))
            backends.append((t, url, prefix))
        except ValueError:
            logger.critical('Can not unpack backend information: %s' % backend)
            sys.exit(1)
    if not len(backends):
        logger.critical("No backend provided. Exiting.")
        sys.exit(1)

    for server in servers:
        try:
            url = server
            stats = monitor_agent(url)
            logger.info('Stats gathering from %s OK.' % url)
        except Exception as e:
            logger.error('Failed requesting data from %s' % (server))
            logger.error(e)
            stats = {'server_name': get_server_name(server),
                     'availability': 0}  # Resetting Gauge values in Graphite

        for backend in backends:
            t, url, prefix = backend
            if t == 'graphite':
                if not prefix:
                    logger.critical('Can not report to graphite without prefix')
                    sys.exit(1)
                try:
                    backend_graphite(url, stats, prefix)
                    logger.info('Reporting to %s OK.' % url)
                except Exception as e:
                    logger.error('Unable to report to Graphite backend: %s' % e)
            else:
                logger.critical('Can not report to backend of type %s: Not supported' % type)
                sys.exit(1)
    sys.exit(0)
