#!/usr/bin/env python
#
#    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
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import json
import logging
import os
import sys

import requests

from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

try:
    from heatclient import client as heatclient
except ImportError:
    heatclient = None

try:
    from keystoneclient.v3 import client as ksclient
except ImportError:
    ksclient = None

try:
    from zaqarclient.queues.v1 import client as zaqarclient
except ImportError:
    zaqarclient = None


MAX_RESPONSE_SIZE = 950000


def init_logging():
    log = logging.getLogger('heat-config-notify')
    handler = logging.StreamHandler(sys.stderr)
    handler.setFormatter(
        logging.Formatter(
            '[%(asctime)s] (%(name)s) [%(levelname)s] %(message)s'))
    log.addHandler(handler)
    log.setLevel('DEBUG')
    return log


def trim_response(response, trimmed_values=None):
    """Trim selected values from response.

    Makes given response smaller or the same size as MAX_RESPONSE_SIZE by
    trimming given trimmed_values from response dict from the left side
    (beginning). Returns trimmed and serialized JSON response itself.
    """

    trimmed_values = trimmed_values or ('deploy_stdout', 'deploy_stderr')
    str_response = json.dumps(response, ensure_ascii=True)
    len_total = len(str_response)
    offset = MAX_RESPONSE_SIZE - len_total
    if offset >= 0:
        return str_response
    offset = abs(offset)
    for key in trimmed_values:
        len_value = len(response[key])
        cut = int(round(float(len_value) / len_total * offset))
        response[key] = response[key][cut:]
    str_response = json.dumps(response, ensure_ascii=True, encoding='utf-8')
    return str_response


def main(argv=sys.argv, stdin=sys.stdin):

    log = init_logging()
    usage = ('Usage:\n  heat-config-notify /path/to/config.json '
             '< /path/to/signal_data.json')

    if len(argv) < 2:
        log.error(usage)
        return 1

    try:
        signal_data = json.load(stdin)
    except ValueError:
        log.warn('No valid json found on stdin')
        signal_data = {}

    conf_file = argv[1]
    if not os.path.exists(conf_file):
        log.error('No config file %s' % conf_file)
        log.error(usage)
        return 1

    c = json.load(open(conf_file))

    iv = dict((i['name'], i['value']) for i in c['inputs'])

    if 'deploy_signal_id' in iv:
        sigurl = iv.get('deploy_signal_id')
        sigverb = iv.get('deploy_signal_verb', 'POST')
        log.debug('Signaling to %s via %s' % (sigurl, sigverb))
        # we need to trim log content because Heat response size is limited
        # by max_json_body_size = 1048576
        str_signal_data = trim_response(signal_data)
        session = requests.Session()
        # Retry if connection issues occur or the service is returning a 5xx
        retry = Retry(
            total=10,
            read=10,
            connect=10,
            backoff_factor=0.5,
            status_forcelist=(500, 502, 503, 504)
        )
        adapter = HTTPAdapter(max_retries=retry)
        session.mount('http://', adapter)
        session.mount('https://', adapter)

        if sigverb == 'PUT':
            r = session.put(sigurl, data=str_signal_data,
                            headers={'content-type': 'application/json'})
        else:
            r = session.post(sigurl, data=str_signal_data,
                             headers={'content-type': 'application/json'})
        log.debug('Response %s ' % r)

    if 'deploy_queue_id' in iv:
        queue_id = iv.get('deploy_queue_id')
        log.debug('Signaling to queue %s' % (queue_id,))

        ks = ksclient.Client(
            auth_url=iv['deploy_auth_url'],
            user_id=iv['deploy_user_id'],
            password=iv['deploy_password'],
            project_id=iv['deploy_project_id'])
        endpoint = ks.service_catalog.url_for(
            service_type='messaging', endpoint_type='publicURL',
            region_name=iv.get('deploy_region_name'))

        conf = {
            'auth_opts': {
                'backend': 'keystone',
                'options': {
                    'os_auth_token': ks.auth_token,
                    'os_project_id': iv['deploy_project_id'],
                }
            }
        }
        cli = zaqarclient.Client(endpoint, conf=conf, version=1.1)
        queue = cli.queue(queue_id)
        r = queue.post({'body': signal_data, 'ttl': 600})
        log.debug('Response %s ' % r)

    elif 'deploy_auth_url' in iv:
        ks = ksclient.Client(
            auth_url=iv['deploy_auth_url'],
            user_id=iv['deploy_user_id'],
            password=iv['deploy_password'],
            project_id=iv['deploy_project_id'])
        endpoint = ks.service_catalog.url_for(
            service_type='orchestration', endpoint_type='publicURL',
            region_name=iv.get('deploy_region_name'))
        log.debug('Signalling to %s' % endpoint)
        heat = heatclient.Client(
            '1', endpoint, token=ks.auth_token)
        r = heat.resources.signal(
            iv.get('deploy_stack_id'),
            iv.get('deploy_resource_name'),
            data=signal_data)
        log.debug('Response %s ' % r)

    return 0


if __name__ == '__main__':
    sys.exit(main(sys.argv, sys.stdin))
