#!/usr/bin/env python2
#
# Copyright 2014 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import kombu
import logging
import os
from subprocess import Popen, PIPE, check_output
import time
import yaml


CLOUD_INSTALL_DIR = os.path.expanduser("~/.cloud-install/")
LOG_FILE_NAME = os.path.join(CLOUD_INSTALL_DIR, "status-listener.log")
STATUS_FILE_NAME = os.path.join(CLOUD_INSTALL_DIR, "sync-status")


def get_info():
    "Read rabbitmq config from remote unit's relation data"

    juju_path = check_output('openstack-install -g juju_path',
                             shell=True)
    new_env = os.environ
    new_env['JUJU_HOME'] = juju_path
    read_id_proc = Popen(['juju', 'run', '--unit',
                          'glance-simplestreams-sync/0',
                          'cat /etc/glance-simplestreams-sync/identity.yaml'],
                         env=new_env,
                         stdout=PIPE, stderr=PIPE)

    (id_stdout, id_stderr) = read_id_proc.communicate()

    if read_id_proc.returncode != 0:
        logging.error("error return from reading config: {}"
                      "\nout{}\nerr:{}".format(
                          read_id_proc.returncode, id_stdout, id_stderr))
        raise Exception("Error in reading config from /0 unit")

    id_conf = yaml.load(id_stdout)
    hosts = id_conf.get('rabbit_hosts', None)
    if hosts is not None:
        host = hosts[0]
    else:
        host = id_conf['rabbit_host']

    return (id_conf['rabbit_userid'],
            id_conf['rabbit_password'],
            host,
            id_conf['rabbit_virtual_host'])


status_message_exchange = kombu.Exchange("glance-simplestreams-sync-status")

status_message_queue = kombu.Queue("glance-simplestreams-sync-status",
                                   exchange=status_message_exchange)


def atomic_write_file(filename, data):
    tempname = filename + ".tmp"
    with open(tempname, 'w') as f:
        f.write(data)
        f.flush()

    os.rename(tempname, filename)


def process_message(body, message):
    if body['status'] == 'Error':
        atomic_write_file(STATUS_FILE_NAME,
                          "Sync status error, see\n{}".format(LOG_FILE_NAME))
        logging.error("Received error message: {}".format(body['message']))
    else:
        atomic_write_file(STATUS_FILE_NAME, body['message'])
        logging.info("Received message {}".format(body['message']))

    message.ack()


# Listen forever on rabbitmq port
def main():
    fmt = "%(levelname)s %(asctime)s %(funcName)s %(lineno)d %(msg)s"
    logging.basicConfig(filename=LOG_FILE_NAME,
                        format=fmt,
                        level=logging.INFO)

    info_found = False
    while not info_found:
        try:
            username, password, host, virtual_host = get_info()
            info_found = True
        except:
            logging.exception("Error connecting. Will reattempt after 2sec.")
            time.sleep(2)

    try:
        url = "amqp://{}:{}@{}/{}".format(username, password, host,
                                          virtual_host)

        with kombu.BrokerConnection(url) as conn:
            status_message_queue(conn.channel()).declare()

            with conn.Consumer(status_message_queue,
                               callbacks=[process_message]) as consumer:
                consumer        # pyflakes
                while True:
                    conn.drain_events()
    except:
        atomic_write_file(STATUS_FILE_NAME,
                          "Sync status error, see\n{}".format(LOG_FILE_NAME))
        logging.exception("Exception listening for status.")

if __name__ == "__main__":
    try:
        main()
    except:
        logging.exception("uncaught exception in status-listener main")
