Esempio n. 1
0
def convergence_remove_server_from_group(log, transaction_id, server_id,
                                         replace, purge, group, state):
    """
    Remove a specific server from the group, optionally decrementing the
    desired capacity.

    The server may just be scheduled for deletion, or it may be evicted from
    the group by removing otter-specific metdata from the server.

    :param log: A bound logger
    :param bytes trans_id: The transaction id for this operation.
    :param bytes server_id: The id of the server to be removed.
    :param bool replace: Should the server be replaced?
    :param bool purge: Should the server be deleted from Nova?
    :param group: The scaling group to remove a server from.
    :type group: :class:`~otter.models.interface.IScalingGroup`
    :param state: The current state of the group.
    :type state: :class:`~otter.models.interface.GroupState`

    :return: The updated state.
    :rtype: Effect of :class:`~otter.models.interface.GroupState`

    :raise: :class:`CannotDeleteServerBelowMinError` if the server cannot
        be deleted without replacement, and :class:`ServerNotFoundError` if
        there is no such server to be deleted.
    """
    effects = [_is_server_in_group(group, server_id)]
    if not replace:
        effects.append(_can_scale_down(group, server_id))

    # the (possibly) two checks can happen in parallel, but we want
    # ServerNotFoundError to take precedence over
    # CannotDeleteServerBelowMinError
    both_checks = yield parallel_all_errors(effects)
    for is_error, result in both_checks:
        if is_error:
            reraise(*result)

    # Remove the server
    if purge:
        eff = set_nova_metadata_item(server_id, *DRAINING_METADATA)
    else:
        eff = Effect(
            EvictServerFromScalingGroup(log=log,
                                        transaction_id=transaction_id,
                                        scaling_group=group,
                                        server_id=server_id))
    yield Effect(
        TenantScope(
            retry_effect(eff, retry_times(3), exponential_backoff_interval(2)),
            group.tenant_id))

    if not replace:
        yield do_return(assoc_obj(state, desired=state.desired - 1))
    else:
        yield do_return(state)
Esempio n. 2
0
def convergence_remove_server_from_group(
        log, transaction_id, server_id, replace, purge, group, state):
    """
    Remove a specific server from the group, optionally decrementing the
    desired capacity.

    The server may just be scheduled for deletion, or it may be evicted from
    the group by removing otter-specific metdata from the server.

    :param log: A bound logger
    :param bytes trans_id: The transaction id for this operation.
    :param bytes server_id: The id of the server to be removed.
    :param bool replace: Should the server be replaced?
    :param bool purge: Should the server be deleted from Nova?
    :param group: The scaling group to remove a server from.
    :type group: :class:`~otter.models.interface.IScalingGroup`
    :param state: The current state of the group.
    :type state: :class:`~otter.models.interface.GroupState`

    :return: The updated state.
    :rtype: Effect of :class:`~otter.models.interface.GroupState`

    :raise: :class:`CannotDeleteServerBelowMinError` if the server cannot
        be deleted without replacement, and :class:`ServerNotFoundError` if
        there is no such server to be deleted.
    """
    effects = [_is_server_in_group(group, server_id)]
    if not replace:
        effects.append(_can_scale_down(group, server_id))

    # the (possibly) two checks can happen in parallel, but we want
    # ServerNotFoundError to take precedence over
    # CannotDeleteServerBelowMinError
    both_checks = yield parallel_all_errors(effects)
    for is_error, result in both_checks:
        if is_error:
            reraise(*result)

    # Remove the server
    if purge:
        eff = set_nova_metadata_item(server_id, *DRAINING_METADATA)
    else:
        eff = Effect(
            EvictServerFromScalingGroup(log=log,
                                        transaction_id=transaction_id,
                                        scaling_group=group,
                                        server_id=server_id))
    yield Effect(TenantScope(
        retry_effect(eff, retry_times(3), exponential_backoff_interval(2)),
        group.tenant_id))

    if not replace:
        yield do_return(assoc_obj(state, desired=state.desired - 1))
    else:
        yield do_return(state)
Esempio n. 3
0
def add_event(event, admin_tenant_id, region, log):
    """
    Add event to cloud feeds
    """
    event, error, timestamp, event_tenant_id, event_id = sanitize_event(event)
    req = prepare_request(request_format, event, error, timestamp, region, event_tenant_id, event_id)

    eff = retry_effect(
        publish_autoscale_event(req, log=log),
        compose_retries(lambda f: (not f.check(APIError) or f.value.code < 400 or f.value.code >= 500), retry_times(5)),
        exponential_backoff_interval(2),
    )
    return Effect(TenantScope(tenant_id=admin_tenant_id, effect=eff))
Esempio n. 4
0
    def as_effect(self):
        """Produce a :obj:`Effect` to delete a server."""

        eff = retry_effect(
            delete_and_verify(self.server_id), can_retry=retry_times(3),
            next_interval=exponential_backoff_interval(2))

        def report_success(result):
            return StepResult.RETRY, [
                ErrorReason.String(
                    'must re-gather after deletion in order to update the '
                    'active cache')]

        return eff.on(success=report_success)
Esempio n. 5
0
    def as_effect(self):
        """Produce a :obj:`Effect` to delete a server."""

        eff = retry_effect(
            delete_and_verify(self.server_id), can_retry=retry_times(3),
            next_interval=exponential_backoff_interval(2))

        def report_success(result):
            return StepResult.RETRY, [
                ErrorReason.String(
                    'must re-gather after deletion in order to update the '
                    'active cache')]

        return eff.on(success=report_success)
Esempio n. 6
0
 def test_retry_effect(self):
     """
     :func:`retry_effect` takes an effect and returns an :obj:`Effect` of
     :obj:`Retry`, with a :obj:`ShouldDelayAndRetry` as the should_retry
     callable.
     """
     can_retry = lambda f: True
     next_interval = lambda f: 1
     eff = retry_effect(STUB, can_retry, next_interval)
     self.assertEqual(
         eff,
         Effect(
             Retry(effect=STUB,
                   should_retry=ShouldDelayAndRetry(
                       can_retry=can_retry, next_interval=next_interval))))
Esempio n. 7
0
 def test_retry_effect(self):
     """
     :func:`retry_effect` takes an effect and returns an :obj:`Effect` of
     :obj:`Retry`, with a :obj:`ShouldDelayAndRetry` as the should_retry
     callable.
     """
     can_retry = lambda f: True
     next_interval = lambda f: 1
     eff = retry_effect(STUB, can_retry, next_interval)
     self.assertEqual(
         eff,
         Effect(Retry(
             effect=STUB,
             should_retry=ShouldDelayAndRetry(can_retry=can_retry,
                                              next_interval=next_interval))))
Esempio n. 8
0
def add_event(event, admin_tenant_id, region, log):
    """
    Add event to cloud feeds
    """
    event, error, timestamp, event_tenant_id, event_id = sanitize_event(event)
    req = prepare_request(request_format, event, error, timestamp, region,
                          event_tenant_id, event_id)

    eff = retry_effect(
        publish_autoscale_event(req, log=log),
        compose_retries(
            lambda f: (not f.check(APIError) or
                       f.value.code < 400 or
                       f.value.code >= 500),
            retry_times(5)),
        exponential_backoff_interval(2))
    return Effect(TenantScope(tenant_id=admin_tenant_id, effect=eff))
Esempio n. 9
0
def _is_server_in_group(group, server_id):
    """
    Given a group and server ID, determines if the server is a member of
    the group.  If it isn't, it raises a :class:`ServerNotFoundError`.
    """
    try:
        response, server_info = yield Effect(
            TenantScope(
                retry_effect(get_server_details(server_id), retry_times(3), exponential_backoff_interval(2)),
                group.tenant_id,
            )
        )
    except NoSuchServerError:
        raise ServerNotFoundError(group.tenant_id, group.uuid, server_id)

    group_id = group_id_from_metadata(get_in(("server", "metadata"), server_info, {}))

    if group_id != group.uuid:
        raise ServerNotFoundError(group.tenant_id, group.uuid, server_id)
Esempio n. 10
0
def _is_server_in_group(group, server_id):
    """
    Given a group and server ID, determines if the server is a member of
    the group.  If it isn't, it raises a :class:`ServerNotFoundError`.
    """
    try:
        response, server_info = yield Effect(
            TenantScope(
                retry_effect(get_server_details(server_id), retry_times(3),
                             exponential_backoff_interval(2)),
                group.tenant_id))
    except NoSuchServerError:
        raise ServerNotFoundError(group.tenant_id, group.uuid, server_id)

    group_id = group_id_from_metadata(
        get_in(('server', 'metadata'), server_info, {}))

    if group_id != group.uuid:
        raise ServerNotFoundError(group.tenant_id, group.uuid, server_id)
Esempio n. 11
0
def _retry(eff):
    """Retry an effect with a common policy."""
    return retry_effect(
        eff, retry_times(5), exponential_backoff_interval(2))
Esempio n. 12
0
def _retry(eff):
    """Retry an effect with a common policy."""
    return retry_effect(eff, retry_times(5), exponential_backoff_interval(2))