"""
rgw multisite testing
"""
import importlib.util
import logging
import nose.core
import nose.config
import sys

from nose.plugins.manager import DefaultPluginManager
from teuthology.config import config as teuth_config
from teuthology.exceptions import ConfigError
from teuthology.repo_utils import fetch_repo
from teuthology.task import Task
from teuthology import misc

log = logging.getLogger(__name__)


class RGWMultisiteTests(Task):
    """
    Runs the rgw_multi tests against a multisite configuration created by the
    rgw-multisite task. Tests are run with nose, using any additional 'args'
    provided. Overrides for tests.Config can be set in 'config'. The 'branch'
    and 'repo' can be overridden to clone the rgw_multi tests from another
    release.

        - rgw-multisite-tests:
            args:
            - tests.py:test_object_sync
            config:
              reconfigure_delay: 60
            branch: octopus
            repo: https://github.com/ceph/ceph.git

    """
    def __init__(self, ctx, config):
        super(RGWMultisiteTests, self).__init__(ctx, config)

    def setup(self):
        super(RGWMultisiteTests, self).setup()

        overrides = self.ctx.config.get('overrides', {})
        misc.deep_merge(self.config, overrides.get('rgw-multisite-tests', {}))

        if not self.ctx.rgw_multisite:
            raise ConfigError('rgw-multisite-tests must run after the rgw-multisite task')
        realm = self.ctx.rgw_multisite.realm
        master_zone = realm.meta_master_zone()

        branch = self.config.get('branch')
        if not branch:
            # run from suite_path
            suite_path = self.ctx.config.get('suite_path')
            self.module_path = suite_path + '/../src/test/rgw/rgw_multi'
        else:
            # clone the qa branch
            repo = self.config.get('repo', teuth_config.get_ceph_qa_suite_git_url())
            log.info("cloning suite branch %s from %s...", branch, repo)
            clonedir = fetch_repo(repo, branch)
            # import its version of rgw_multi
            self.module_path = clonedir + '/src/test/rgw/rgw_multi'

        log.info("importing tests from %s", self.module_path)
        spec = importlib.util.spec_from_file_location('rgw_multi', self.module_path + '/__init__.py')
        module = importlib.util.module_from_spec(spec)
        sys.modules[spec.name] = module
        spec.loader.exec_module(module)

        from rgw_multi import multisite, tests

        # create the test user
        log.info('creating test user..')
        user = multisite.User('rgw-multisite-test-user')
        user.create(master_zone, ['--display-name', 'Multisite Test User',
                                  '--gen-access-key', '--gen-secret', '--caps', 'roles=*'])

        config = self.config.get('config', {})
        tests.init_multi(realm, user, tests.Config(**config))
        tests.realm_meta_checkpoint(realm)

    def begin(self):
        # extra arguments for nose can be passed as a string or list
        extra_args = self.config.get('args', [])
        if not isinstance(extra_args, list):
            extra_args = [extra_args]
        argv = [__name__] + extra_args

        log.info("running rgw multisite tests on '%s' with args=%r",
                 self.module_path, extra_args)

        # run nose tests in the module path
        conf = nose.config.Config(stream=get_log_stream(), verbosity=2, workingDir=self.module_path)
        conf.plugins = DefaultPluginManager() # overrides default = NoPlugins()
        assert nose.run(argv=argv, config=conf), 'rgw multisite test failures'


def get_log_stream():
    """ return a log stream for nose output """
    # XXX: this is a workaround for IOErrors when nose writes to stderr,
    # copied from vstart_runner.py
    class LogStream(object):
        def __init__(self):
            self.buffer = ""

        def write(self, data):
            self.buffer += data
            if "\n" in self.buffer:
                lines = self.buffer.split("\n")
                for line in lines[:-1]:
                    log.info(line)
                self.buffer = lines[-1]

        def flush(self):
            pass

    return LogStream()


task = RGWMultisiteTests
