#!/usr/bin/env python

import os
import sys
import traceback
import XenAPI
import XenAPIPlugin
import xmlrpclib

import xscontainer.api_helper as api_helper
import xscontainer.coreos as coreos
import xscontainer.docker as docker
import xscontainer.util.log as log

from xscontainer.docker_monitor import api as docker_monitor_api

ENABLE_DEV_CALLS_FILE = "/opt/xensource/packages/files/xscontainer/devmode_enabled"


def log_and_raise_exception(func):
    """
    Decorator method for logging exceptions before passing them on
    """
    def decorated(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception:
            exception_type, exception_value, exception_traceback = (
                sys.exc_info())
            log.log_unhandled_exception("plugin",
                                        exception_type,
                                        exception_value,
                                        exception_traceback)
            raise
    return decorated


def devmode_only(func):
    """
    Decorator method for returning an exception if 'dev mode' is not enabled
    in this plugin (global variable).
    """
    def decorated(*args, **kwargs):
        if not os.path.exists(ENABLE_DEV_CALLS_FILE):
            return "ERROR: This call is only enabled when in dev mode."
        return func(*args, **kwargs)
    return decorated


def check_args(reqs, args):
    for req in reqs:
        if req not in args:
            raise Exception('INVALID_ARGUMENTS')


def prepare_output(output):
    return "True%s" % (output)


@log_and_raise_exception
@devmode_only
def install_vm(session, args):
    check_args(['url', 'sruuid'],  args)
    return prepare_output(coreos.install_vm(session,
                                            args['url'],
                                            args['sruuid']))


@log_and_raise_exception
def create_config_drive(session, args):
    check_args(['vmuuid', 'sruuid', 'configuration'], args)
    configuration = args['configuration'].replace("%BR%", "\r\n")
    return prepare_output(coreos.create_config_drive(session,
                                                     args['vmuuid'],
                                                     args['sruuid'],
                                                     configuration))


@log_and_raise_exception
def get_config_drive_default(session, args):
    check_args(['templateuuid'], args)
    return prepare_output(coreos.get_config_drive_default(session))


@log_and_raise_exception
def get_config_drive_configuration(session, args):
    check_args(['vdiuuid'], args)
    return prepare_output(coreos.get_config_drive_configuration(session,
                                                                args['vdiuuid']
                                                                ))


@log_and_raise_exception
def get_inspect(session, args):
    check_args(['vmuuid', 'object'], args)
    return prepare_output(docker.get_inspect_xml(session, args['vmuuid'],
                                                 args['object']))


@log_and_raise_exception
def get_top(session, args):
    check_args(['vmuuid', 'object'], args)
    return prepare_output(docker.get_top_xml(session, args['vmuuid'],
                                             args['object']))


@log_and_raise_exception
def register(session, args):
    check_args(['vmuuid'], args)
    return prepare_output(docker_monitor_api.register_vm(args['vmuuid'],
                                                         session))


@log_and_raise_exception
def deregister(session, args):
    check_args(['vmuuid'], args)
    return prepare_output(docker_monitor_api.deregister_vm(args['vmuuid'],
                                                           session))


@log_and_raise_exception
def start(session, args):
    check_args(['vmuuid', 'container'], args)
    return prepare_output(docker.start(session, args['vmuuid'],
                                       args['container']))


@log_and_raise_exception
def stop(session, args):
    check_args(['vmuuid', 'container'], args)
    return prepare_output(docker.stop(session, args['vmuuid'],
                                      args['container']))


@log_and_raise_exception
def restart(session, args):
    check_args(['vmuuid', 'container'], args)
    return prepare_output(docker.restart(session, args['vmuuid'],
                                         args['container']))


@log_and_raise_exception
def pause(session, args):
    check_args(['vmuuid', 'container'], args)
    return prepare_output(docker.pause(session, args['vmuuid'],
                                       args['container']))


@log_and_raise_exception
def unpause(session, args):
    check_args(['vmuuid', 'container'], args)
    return prepare_output(docker.unpause(session, args['vmuuid'],
                                         args['container']))


@log_and_raise_exception
@devmode_only
def passthrough(session, args):
    check_args(['vmuuid', 'command'], args)
    return prepare_output(docker.passthrough(session, args['vmuuid'],
                                             args['command']))


@log_and_raise_exception
def redirect_operation_owned_by_other_slave(argv):
    params, methodname = xmlrpclib.loads(argv[1])
    session_id = params[0]
    session = XenAPI.xapi_local()
    session._session = session_id
    args = params[1]
    redirect_criteria = None
    redirect_to_host_ref = None
    """ If the request concerns a VM, the host which runs the VM should handle
        the request"""
    if not redirect_to_host_ref and 'vmuuid' in args:
        redirect_criteria = ("vmuuid %s" % args['vmuuid'])
        redirect_to_host_ref = api_helper.get_host_ref_for_vm_uuid(
            session, args['vmuuid'])
    """ If the request concerns a VDI, a host that has access to the VDI
        should handle the request"""
    if not redirect_to_host_ref and 'vdiuuid' in args:
        redirect_criteria = ("vdiuuid %s" % args['vdiuuid'])
        redirect_to_host_ref = api_helper.get_host_ref_for_vdi_uuid(
            session, args['vdiuuid'])
    """ If the request concerns a SR, a host that has access to the SR
        should handle the request"""
    if not redirect_to_host_ref and 'sruuid' in args:
        redirect_criteria = ("sruuid %s" % args['sruuid'])
        redirect_to_host_ref = api_helper.get_host_ref_for_sr_uuid(
            session, args['sruuid'])
    this_host_ref = api_helper.get_this_host_ref(session)
    if (redirect_to_host_ref != None
            and redirect_to_host_ref != this_host_ref):
        # Redirect the call
        try:
            log.info("Forwarding request %s from %s to %s based on %s"
                     % (args, this_host_ref, redirect_to_host_ref, redirect_criteria))
            result = session.xenapi.host.call_plugin(
                redirect_to_host_ref, 'xscontainer', methodname, args)
            print XenAPIPlugin.success_message(result)
            sys.exit(0)
        except SystemExit:
            raise
        except Exception, e:
            print XenAPIPlugin.failure_message(['XENAPI_PLUGIN_FAILURE',
                                                methodname,
                                                e.__class__.__name__, str(e)])
            sys.exit(1)


if __name__ == "__main__":
    redirect_operation_owned_by_other_slave(sys.argv)

    # Now we can be certain that this message ought to be handled by this slave
    XenAPIPlugin.dispatch({
        'install_vm': install_vm,
        'create_config_drive': create_config_drive,
        'get_config_drive_default': get_config_drive_default,
        'get_config_drive_configuration': get_config_drive_configuration,
        'get_inspect': get_inspect,
        'get_top': get_top,
        'register': register,
        'deregister': deregister,
        'start': start,
        'stop': stop,
        'restart': restart,
        'pause': pause,
        'unpause': unpause,
        'passthrough': passthrough, })
