Пример #1
0
 def __init__(self, *args, **kwargs):
     """
     Initialize the fake supervisor.
     """
     self.args = args
     self.deferred_pool = DeferredPool()
     self.exec_calls = []
     self.exec_defs = []
     self.del_index = 0
     self.del_calls = []
     self.scrub_calls = []
     self.scrub_defs = []
Пример #2
0
 def setUp(self):
     """
     Default DeferredPool for each case
     """
     self.pool = DeferredPool()
Пример #3
0
class DeferredPoolTests(SynchronousTestCase):
    """
    Tests for :class:`DeferredPool`
    """
    def setUp(self):
        """
        Default DeferredPool for each case
        """
        self.pool = DeferredPool()

    def test_notify_when_empty_happens_immediately(self):
        """
        When ``notify_when_empty`` is called, if the pool is empty, the
        deferred returned callbacks immediately.
        """
        d = self.pool.notify_when_empty()
        self.successResultOf(d)

    def test_notify_when_empty_does_not_callback_previous_waiting(self):
        """
        The second time ``notify_when_empty`` is called, it only callback the
        deferreds that were created after the first call.
        """
        d1 = self.pool.notify_when_empty()
        self.successResultOf(d1)

        d2 = self.pool.notify_when_empty()
        self.successResultOf(d2)
        # no AlreadyCalledError?

    def test_notify_does_not_notify_until_pooled_deferreds_callback(self):
        """
        If there are one or more deferreds in the pool, ``notify_when_empty``
        does not notify until they are callbacked.
        """
        holdup = Deferred()
        self.pool.add(holdup)

        d = self.pool.notify_when_empty()
        self.assertNoResult(d)

        holdup.callback('done')
        self.successResultOf(d)

    def test_notify_does_not_notify_until_pooled_deferreds_errbacks(self):
        """
        If there are one or more deferreds in the pool, ``notify_when_empty``
        does not notify until they are fired - works with errbacks too.
        """
        holdup = Deferred()
        self.pool.add(holdup)

        d = self.pool.notify_when_empty()
        self.assertNoResult(d)

        holdup.errback(DummyException('hey'))
        self.successResultOf(d)

        # don't leave unhandled Deferred lying around
        self.failureResultOf(holdup)

    def test_notify_when_empty_notifies_all_waiting(self):
        """
        All waiting Deferreds resulting from previous calls to
        ``notify_when_empty`` will callback as soon as the pool is empty.
        """
        holdup = Deferred()
        self.pool.add(holdup)

        previous = [self.pool.notify_when_empty() for i in range(5)]
        for d in previous:
            self.assertNoResult(d)

        holdup.callback('done')

        for d in previous:
            self.successResultOf(d)

    def test_pooled_deferred_callbacks_not_obscured(self):
        """
        The callbacks of pooled deferreds are not obscured by removing them
        from the pool.
        """
        holdup = Deferred()
        self.pool.add(holdup)
        holdup.callback('done')
        self.assertEqual(self.successResultOf(holdup), 'done')

    def test_pooled_deferred_errbbacks_not_obscured(self):
        """
        The errbacks of pooled deferreds are not obscured by removing them
        from the pool.
        """
        holdup = Deferred()
        self.pool.add(holdup)
        holdup.errback(DummyException('hey'))
        self.failureResultOf(holdup, DummyException)

    def test_len(self):
        """
        len(pool) returns number of deferreds waiting in the pool
        """
        self.assertEqual(len(self.pool), 0)
        d = Deferred()
        self.pool.add(d)
        self.assertEqual(len(self.pool), 1)
        d.callback(None)
        self.assertEqual(len(self.pool), 0)
Пример #4
0
 def __init__(self, auth_function, region, coiterate):
     self.auth_function = auth_function
     self.region = region
     self.coiterate = coiterate
     self.deferred_pool = DeferredPool()
Пример #5
0
class SupervisorService(object, Service):
    """
    A service which manages execution of launch configurations.

    :ivar callable auth_function: authentication function to use to obtain an
        auth token and service catalog.  Should accept a tenant ID.

    :ivar callable coiterate: coiterate function that will be passed to
        InMemoryUndoStack.

    :ivar DeferredPool deferred_pool: a pool in which to store deferreds that
        should be waited on
    """
    name = "supervisor"

    def __init__(self, auth_function, region, coiterate):
        self.auth_function = auth_function
        self.region = region
        self.coiterate = coiterate
        self.deferred_pool = DeferredPool()

    def execute_config(self, log, transaction_id, scaling_group, launch_config):
        """
        see :meth:`ISupervisor.execute_config`
        """
        job_id = generate_job_id(scaling_group.uuid)
        completion_d = Deferred()

        log = log.bind(job_id=job_id,
                       worker=launch_config['type'],
                       tenant_id=scaling_group.tenant_id)

        assert launch_config['type'] == 'launch_server'

        undo = InMemoryUndoStack(self.coiterate)

        def when_fails(result):
            log.msg("Encountered an error, rewinding {worker!r} job undo stack.",
                    exc=result.value)
            ud = undo.rewind()
            ud.addCallback(lambda _: result)
            return ud

        completion_d.addErrback(when_fails)

        log.msg("Authenticating for tenant")

        d = self.auth_function(scaling_group.tenant_id, log=log)

        def when_authenticated((auth_token, service_catalog)):
            log.msg("Executing launch config.")
            return launch_server_v1.launch_server(
                log,
                self.region,
                scaling_group,
                service_catalog,
                auth_token,
                launch_config['args'], undo)

        d.addCallback(when_authenticated)

        def when_launch_server_completed(result):
            # XXX: Something should be done with this data. Currently only enough
            # to pass to the controller to store in the active state is returned
            server_details, lb_info = result
            log.msg("Done executing launch config.",
                    server_id=server_details['server']['id'])
            return {
                'id': server_details['server']['id'],
                'links': server_details['server']['links'],
                'name': server_details['server']['name'],
                'lb_info': lb_info
            }

        d.addCallback(when_launch_server_completed)

        self.deferred_pool.add(d)

        d.chainDeferred(completion_d)

        return succeed((job_id, completion_d))

    def execute_delete_server(self, log, transaction_id, scaling_group, server):
        """
        see :meth:`ISupervisor.execute_delete_server`
        """
        log = log.bind(server_id=server['id'], tenant_id=scaling_group.tenant_id)

        # authenticate for tenant
        def when_authenticated((auth_token, service_catalog)):
            return launch_server_v1.delete_server(
                log,
                self.region,
                service_catalog,
                auth_token,
                (server['id'], server['lb_info']))

        d = self.auth_function(scaling_group.tenant_id, log=log)
        log.msg("Authenticating for tenant")
        d.addCallback(when_authenticated)
        self.deferred_pool.add(d)

        return d

    def validate_launch_config(self, log, tenant_id, launch_config):
        """
        Validate launch config for a tenant
        """
        def when_authenticated((auth_token, service_catalog)):
            log.msg('Validating launch server config')
            return validate_config.validate_launch_server_config(
                log,
                self.region,
                service_catalog,
                auth_token,
                launch_config['args'])

        if launch_config['type'] != 'launch_server':
            raise NotImplementedError('Validating launch config for launch_server only')

        log = log.bind(system='otter.supervisor.validate_launch_config',
                       tenant_id=tenant_id)
        d = self.auth_function(tenant_id, log=log)
        log.msg('Authenticating for tenant')
        return d.addCallback(when_authenticated)

    def stopService(self):
        """
        Returns a deferred that succeeds when the :class:`DeferredPool` is
        empty
        """
        super(SupervisorService, self).stopService()
        return self.deferred_pool.notify_when_empty()

    def health_check(self):
        """
        Check if supervisor is healthy. In this case, just return number of jobs
        currently running.
        """
        return True, {'jobs': len(self.deferred_pool)}
Пример #6
0
 def __init__(self, authenticator, region, coiterate, service_configs):
     self.authenticator = authenticator
     self.region = region
     self.coiterate = coiterate
     self.deferred_pool = DeferredPool()
     self.service_configs = service_configs
Пример #7
0
class SupervisorService(Service, object):
    """
    A service which manages execution of launch configurations.

    :ivar IAuthenticator authenticator: Authenticator to use to obtain an
        auth token and service catalog.
    :ivar callable coiterate: coiterate function that will be passed to
        InMemoryUndoStack.
    :ivar str region: The region in which this supervisor is operating.
    :ivar DeferredPool deferred_pool: a pool in which to store deferreds that
        should be waited on
    """
    name = "supervisor"

    def __init__(self, authenticator, region, coiterate, service_configs):
        self.authenticator = authenticator
        self.region = region
        self.coiterate = coiterate
        self.deferred_pool = DeferredPool()
        self.service_configs = service_configs

    def _get_request_bag(self, log, scaling_group):
        """
        Builds :obj:`RequestBag` containing a bunch of useful stuff for making
        HTTP requests.
        """
        tenant_id = scaling_group.tenant_id
        dispatcher = get_legacy_dispatcher(reactor, self.authenticator, log,
                                           self.service_configs)
        lb_region = config_value('regionOverrides.cloudLoadBalancers')

        def authenticate():
            log.msg("Authenticating for tenant")
            d = self.authenticator.authenticate_tenant(tenant_id, log=log)

            def when_authenticated((auth_token, service_catalog)):
                bag = RequestBag(
                    lb_region=lb_region or self.region,
                    region=self.region,
                    dispatcher=dispatcher,
                    tenant_id=tenant_id,
                    auth_token=auth_token,
                    service_catalog=service_catalog,
                    re_auth=authenticate,
                )
                return bag

            return d.addCallback(when_authenticated)

        return authenticate()

    def execute_config(self, log, transaction_id, scaling_group,
                       launch_config):
        """
        see :meth:`ISupervisor.execute_config`
        """
        log = log.bind(worker=launch_config['type'],
                       tenant_id=scaling_group.tenant_id)

        assert launch_config['type'] == 'launch_server'

        undo = InMemoryUndoStack(self.coiterate)

        d = self._get_request_bag(log, scaling_group)

        def got_request_bag(request_bag):
            log.msg("Executing launch config.")
            return launch_server_v1.launch_server(log,
                                                  request_bag,
                                                  scaling_group,
                                                  launch_config['args'],
                                                  undo)

        d.addCallback(got_request_bag)

        def when_launch_server_completed(result):
            # XXX: Something should be done with this data. Currently only
            # enough to pass to the controller to store in the active state is
            # returned
            server_details, lb_info = result
            log.msg("Done executing launch config.",
                    server_id=server_details['server']['id'])
            return {
                'id': server_details['server']['id'],
                'links': server_details['server']['links'],
                'name': server_details['server']['name'],
                'lb_info': lb_info
            }

        d.addCallback(when_launch_server_completed)

        def when_fails(result):
            log.msg("Encountered an error, rewinding {worker!r} job undo stack.",
                    exc=result.value)
            ud = undo.rewind()
            ud.addCallback(lambda _: result)
            return ud

        d.addErrback(when_fails)
        return d

    def execute_delete_server(self, log, transaction_id, scaling_group, server):
        """
        See :meth:`ISupervisor.execute_delete_server`
        """
        log = log.bind(server_id=server['id'],
                       tenant_id=scaling_group.tenant_id)

        d = self._get_request_bag(log, scaling_group)

        def got_request_bag(request_bag):
            log.msg("Executing delete server.")
            instance_details = server['id'], server['lb_info']
            return launch_server_v1.delete_server(log, request_bag,
                                                  instance_details)

        return d.addCallback(got_request_bag)

    def scrub_otter_metadata(self, log, transaction_id, tenant_id, server_id):
        """
        See :meth:`ISupervisor.scrub_otter_metadata`.
        """
        log = log.bind(server_id=server_id, tenant_id=tenant_id)

        d = self.authenticator.authenticate_tenant(tenant_id, log=log)
        log.msg("Authenticating for tenant")

        def when_authenticated((auth_token, service_catalog)):
            d = launch_server_v1.scrub_otter_metadata(log,
                                                      auth_token,
                                                      service_catalog,
                                                      self.region,
                                                      server_id)
            return d
        d.addCallback(when_authenticated)

        return d

    def validate_launch_config(self, log, tenant_id, launch_config):
        """
        Validate launch config for a tenant
        """
        def when_authenticated((auth_token, service_catalog)):
            log.msg('Validating launch server config')
            return validate_config.validate_launch_server_config(
                log,
                self.region,
                service_catalog,
                auth_token,
                launch_config['args'])

        if launch_config['type'] != 'launch_server':
            raise NotImplementedError('Validating launch config for launch_server only')

        log = log.bind(system='otter.supervisor.validate_launch_config',
                       tenant_id=tenant_id)
        d = self.authenticator.authenticate_tenant(tenant_id, log=log)
        log.msg('Authenticating for tenant')
        return d.addCallback(when_authenticated)

    def stopService(self):
        """
        Returns a deferred that succeeds when the :class:`DeferredPool` is
        empty
        """
        super(SupervisorService, self).stopService()
        return self.deferred_pool.notify_when_empty()

    def health_check(self):
        """
        Check if supervisor is healthy. In this case, just return number of jobs
        currently running.
        """
        return True, {'jobs': len(self.deferred_pool)}
Пример #8
0
 def __init__(self, authenticator, region, coiterate, service_configs):
     self.authenticator = authenticator
     self.region = region
     self.coiterate = coiterate
     self.deferred_pool = DeferredPool()
     self.service_configs = service_configs
Пример #9
0
class SupervisorService(Service, object):
    """
    A service which manages execution of launch configurations.

    :ivar IAuthenticator authenticator: Authenticator to use to obtain an
        auth token and service catalog.
    :ivar callable coiterate: coiterate function that will be passed to
        InMemoryUndoStack.
    :ivar str region: The region in which this supervisor is operating.
    :ivar DeferredPool deferred_pool: a pool in which to store deferreds that
        should be waited on
    """
    name = "supervisor"

    def __init__(self, authenticator, region, coiterate, service_configs):
        self.authenticator = authenticator
        self.region = region
        self.coiterate = coiterate
        self.deferred_pool = DeferredPool()
        self.service_configs = service_configs

    def _get_request_bag(self, log, scaling_group):
        """
        Builds :obj:`RequestBag` containing a bunch of useful stuff for making
        HTTP requests.
        """
        tenant_id = scaling_group.tenant_id
        dispatcher = get_legacy_dispatcher(reactor, self.authenticator, log,
                                           self.service_configs)
        lb_region = config_value('regionOverrides.cloudLoadBalancers')

        def authenticate():
            log.msg("Authenticating for tenant")
            d = self.authenticator.authenticate_tenant(tenant_id, log=log)

            def when_authenticated((auth_token, service_catalog)):
                bag = RequestBag(
                    lb_region=lb_region or self.region,
                    region=self.region,
                    dispatcher=dispatcher,
                    tenant_id=tenant_id,
                    auth_token=auth_token,
                    service_catalog=service_catalog,
                    re_auth=authenticate,
                )
                return bag

            return d.addCallback(when_authenticated)

        return authenticate()

    def execute_config(self, log, transaction_id, scaling_group,
                       launch_config):
        """
        see :meth:`ISupervisor.execute_config`
        """
        log = log.bind(worker=launch_config['type'],
                       tenant_id=scaling_group.tenant_id)

        assert launch_config['type'] == 'launch_server'

        undo = InMemoryUndoStack(self.coiterate)

        d = self._get_request_bag(log, scaling_group)

        def got_request_bag(request_bag):
            log.msg("Executing launch config.")
            return launch_server_v1.launch_server(log,
                                                  request_bag,
                                                  scaling_group,
                                                  launch_config['args'],
                                                  undo)

        d.addCallback(got_request_bag)

        def when_launch_server_completed(result):
            # XXX: Something should be done with this data. Currently only
            # enough to pass to the controller to store in the active state is
            # returned
            server_details, lb_info = result
            log.msg("Done executing launch config.",
                    server_id=server_details['server']['id'])
            return {
                'id': server_details['server']['id'],
                'links': server_details['server']['links'],
                'name': server_details['server']['name'],
                'lb_info': lb_info
            }

        d.addCallback(when_launch_server_completed)

        def when_fails(result):
            log.msg("Encountered an error, rewinding {worker!r} job undo stack.",
                    exc=result.value)
            ud = undo.rewind()
            ud.addCallback(lambda _: result)
            return ud

        d.addErrback(when_fails)
        return d

    def execute_delete_server(self, log, transaction_id, scaling_group, server):
        """
        See :meth:`ISupervisor.execute_delete_server`
        """
        log = log.bind(server_id=server['id'],
                       tenant_id=scaling_group.tenant_id)

        d = self._get_request_bag(log, scaling_group)

        def got_request_bag(request_bag):
            log.msg("Executing delete server.")
            instance_details = server['id'], server['lb_info']
            return launch_server_v1.delete_server(log, request_bag,
                                                  instance_details)

        return d.addCallback(got_request_bag)

    def scrub_otter_metadata(self, log, transaction_id, tenant_id, server_id):
        """
        See :meth:`ISupervisor.scrub_otter_metadata`.
        """
        log = log.bind(server_id=server_id, tenant_id=tenant_id)

        d = self.authenticator.authenticate_tenant(tenant_id, log=log)
        log.msg("Authenticating for tenant")

        def when_authenticated((auth_token, service_catalog)):
            d = launch_server_v1.scrub_otter_metadata(log,
                                                      auth_token,
                                                      service_catalog,
                                                      self.region,
                                                      server_id)
            return d
        d.addCallback(when_authenticated)

        return d

    def validate_launch_config(self, log, tenant_id, launch_config):
        """
        Validate launch config for a tenant
        """
        def do_validate(validation_method, auth_token, service_catalog):
            return validation_method(log, self.region, service_catalog,
                                     auth_token, launch_config['args'])

        def validate_launch_server_config((auth_token, service_catalog)):
            log.msg('Validating launch server config')
            return do_validate(validate_config.validate_launch_server_config,
                               auth_token, service_catalog)

        def validate_launch_stack_config((auth_token, service_catalog)):
            log.msg('Validating launch stack config')
            return do_validate(validate_config.validate_launch_stack_config,
                               auth_token, service_catalog)

        if launch_config['type'] == 'launch_server':
            when_authenticated = validate_launch_server_config
        elif launch_config['type'] == 'launch_stack':
            when_authenticated = validate_launch_stack_config
        else:
            raise NotImplementedError('Validating launch config for '
                                      'launch_server or launch_stack only')

        log = log.bind(system='otter.supervisor.validate_launch_config',
                       tenant_id=tenant_id)
        d = self.authenticator.authenticate_tenant(tenant_id, log=log)
        log.msg('Authenticating for tenant')
        return d.addCallback(when_authenticated)

    def stopService(self):
        """
        Returns a deferred that succeeds when the :class:`DeferredPool` is
        empty
        """
        super(SupervisorService, self).stopService()
        return self.deferred_pool.notify_when_empty()

    def health_check(self):
        """
        Check if supervisor is healthy. In this case, just return number of jobs
        currently running.
        """
        return True, {'jobs': len(self.deferred_pool)}