Example #1
0
    def test_error_per_tenant(self):
        """
        When a request for servers fails, the associated effect results in
        None, and an error is logged.
        """
        log = mock_log()
        log.err.return_value = None

        groups = {
            "t1": [{
                'tenantId': 't1',
                'groupId': 'g1',
                'desired': 0
            }],
            "t2": [{
                'tenantId': 't2',
                'groupId': 'g2',
                'desired': 0
            }]
        }
        effs = get_all_metrics_effects(groups, log)
        results = []
        for eff in effs:
            if eff.intent.tenant_id == 't1':
                results.append(resolve_effect(eff, {}))
            elif eff.intent.tenant_id == 't2':
                err = (ZeroDivisionError, ZeroDivisionError('foo bar'), None)
                results.append(resolve_effect(eff, err, is_error=True))
        self.assertEqual(
            results,
            [None, [GroupMetrics('t1', 'g1', desired=0, actual=0, pending=0)]])
        log.err.assert_called_once_with(
            CheckFailureValue(ZeroDivisionError('foo bar')))
Example #2
0
 def test_delete_and_verify_verify_404(self):
     """
     :func:`delete_and_verify` gets server details after successful delete
     and succeeds if get server details returns 404
     """
     eff = delete_and_verify('sid')
     eff = resolve_effect(
         eff, service_request_error_response(APIError(204, {})),
         is_error=True)
     r = resolve_effect(eff, (StubResponse(404, {}), {"itemNotFound": {}}))
     self.assertIsNone(r)
Example #3
0
    def test_no_json_parsing_on_error(self):
        """
        Whatever ``json_response`` is set to, it is ignored, if the response
        does not pass the success predicate (because errors may just be
        HTML or otherwise not JSON parsable, even if the success response
        would have been).
        """
        svcreq = service_request(ServiceType.CLOUD_SERVERS, "GET", "servers",
                                 json_response=True).intent
        eff = self._concrete(svcreq)
        next_eff = resolve_authenticate(eff)
        stub_response = stub_pure_response("THIS IS A FAILURE", 500)
        with self.assertRaises(APIError) as cm:
            resolve_effect(next_eff, stub_response)

        self.assertEqual(cm.exception.body, "THIS IS A FAILURE")
Example #4
0
    def test_get_all_metrics(self):
        """
        Metrics are returned based on the requests done to get server info.
        """
        # Maybe this could use a parameterized "get_all_scaling_group_servers"
        # call to avoid needing to stub the nova responses, but it seems okay.
        servers_t1 = {
            'g1': ([_server('g1', 'ACTIVE')] * 3 +
                   [_server('g1', 'BUILD')] * 2),
            'g2': [_server('g2', 'ACTIVE')]}

        servers_t2 = {
            'g4': [_server('g4', 'ACTIVE'),
                   _server('g4', 'BUILD')]}

        groups = {
            "t1": [{'tenantId': 't1', 'groupId': 'g1', 'desired': 3},
                   {'tenantId': 't1', 'groupId': 'g2', 'desired': 4}],
            "t2": [{'tenantId': 't2', 'groupId': 'g4', 'desired': 2}]}

        tenant_servers = {'t1': servers_t1, 't2': servers_t2}

        effs = get_all_metrics_effects(groups, mock_log())
        # All the effs are wrapped in TenantScopes to indicate the tenant
        # of ServiceRequests made under them. We use that tenant to get the
        # stubbed result of get_all_scaling_group_servers.
        results = [
            resolve_effect(eff, tenant_servers[eff.intent.tenant_id])
            for eff in effs]

        self.assertEqual(
            set(reduce(operator.add, results)),
            set([GroupMetrics('t1', 'g1', desired=3, actual=3, pending=2),
                 GroupMetrics('t1', 'g2', desired=4, actual=1, pending=0),
                 GroupMetrics('t2', 'g4', desired=2, actual=1, pending=1)]))
Example #5
0
    def _assert_create_server_with_errs_has_status(self, exceptions, status):
        """
        Helper function to make a :class:`CreateServer` effect, and resolve
        it with the provided exceptions, asserting that the result is the
        provided status, with the reason being the exception.
        """
        eff = CreateServer(
            server_config=freeze({'server': {'flavorRef': '1'}})).as_effect()
        eff = resolve_effect(eff, 'random-name')

        for exc in exceptions:
            self.assertEqual(
                resolve_effect(eff, service_request_error_response(exc),
                               is_error=True),
                (status, [ErrorReason.Exception(
                    matches(ContainsAll([type(exc), exc])))])
            )
Example #6
0
    def test_no_json_parsing_on_error(self):
        """
        Whatever ``json_response`` is set to, it is ignored, if the response
        does not pass the success predicate (because errors may just be
        HTML or otherwise not JSON parsable, even if the success response
        would have been).
        """
        svcreq = service_request(ServiceType.CLOUD_SERVERS,
                                 "GET",
                                 "servers",
                                 json_response=True).intent
        eff = self._concrete(svcreq)
        next_eff = resolve_authenticate(eff)
        stub_response = stub_pure_response("THIS IS A FAILURE", 500)
        with self.assertRaises(APIError) as cm:
            resolve_effect(next_eff, stub_response)

        self.assertEqual(cm.exception.body, "THIS IS A FAILURE")
Example #7
0
 def test_delete_and_verify_del_404(self):
     """
     :func:`delete_and_verify` invokes server delete and succeeds on 404
     """
     eff = delete_and_verify('sid')
     self.assertEqual(
         eff.intent,
         service_request(
             ServiceType.CLOUD_SERVERS, 'DELETE', 'servers/sid',
             success_pred=has_code(404)).intent)
     self.assertEqual(resolve_effect(eff, (ANY, {})), (ANY, {}))
Example #8
0
 def test_should_retry(self):
     """
     When called and can_retry returns True, a Delay based on next_interval
     is executed and the ultimate result is True.
     """
     sdar = ShouldDelayAndRetry(can_retry=lambda f: True,
                                next_interval=lambda f: 1.5)
     eff = sdar(get_exc_info())
     next_eff = _perform_func_intent(eff)
     self.assertEqual(next_eff.intent, Delay(delay=1.5))
     self.assertEqual(resolve_effect(next_eff, None), True)
Example #9
0
 def test_invalidate_on_auth_error_code(self):
     """
     Upon authentication error, the auth cache is invalidated.
     """
     eff = self._concrete(self.svcreq)
     next_eff = resolve_authenticate(eff)
     # When the HTTP response is an auth error, the auth cache is
     # invalidated, by way of the next effect:
     invalidate_eff = resolve_effect(next_eff, stub_pure_response("", 401))
     expected_intent = InvalidateToken(self.authenticator, 1)
     self.assertEqual(invalidate_eff.intent, expected_intent)
     self.assertRaises(APIError, resolve_effect, invalidate_eff, None)
Example #10
0
    def test_delete_and_verify_verifies(self):
        """
        :func:`delete_and_verify` verifies if the server task_state has changed
        to "deleting" after successful delete server call and succeeds if that
        has happened. The details call succeeds if it returns 404
        """
        eff = delete_and_verify('sid')
        eff = resolve_effect(
            eff, service_request_error_response(APIError(204, {})),
            is_error=True)

        self.assertEqual(
            eff.intent,
            service_request(
                ServiceType.CLOUD_SERVERS, 'GET', 'servers/sid',
                success_pred=has_code(200, 404)).intent)
        r = resolve_effect(
            eff,
            (StubResponse(200, {}),
             {'server': {"OS-EXT-STS:task_state": 'deleting'}}))
        self.assertIsNone(r)
Example #11
0
 def test_no_json_response(self):
     """
     ``json_response`` can be set to :data:`False` to get the response
     object and the plaintext body of the response.
     """
     svcreq = service_request(ServiceType.CLOUD_SERVERS, "GET", "servers",
                              json_response=False).intent
     eff = self._concrete(svcreq)
     next_eff = resolve_authenticate(eff)
     stub_response = stub_pure_response("foo")
     result = resolve_effect(next_eff, stub_response)
     self.assertEqual(result, stub_response)
Example #12
0
 def test_invalidate_on_auth_error_code(self):
     """
     Upon authentication error, the auth cache is invalidated.
     """
     eff = self._concrete(self.svcreq)
     next_eff = resolve_authenticate(eff)
     # When the HTTP response is an auth error, the auth cache is
     # invalidated, by way of the next effect:
     invalidate_eff = resolve_effect(next_eff, stub_pure_response("", 401))
     expected_intent = InvalidateToken(self.authenticator, 1)
     self.assertEqual(invalidate_eff.intent, expected_intent)
     self.assertRaises(APIError, resolve_effect, invalidate_eff, None)
Example #13
0
 def test_should_retry(self):
     """
     When called and can_retry returns True, a Delay based on next_interval
     is executed and the ultimate result is True.
     """
     sdar = ShouldDelayAndRetry(can_retry=lambda f: True,
                                next_interval=lambda f: 1.5)
     eff = sdar(get_exc_info())
     next_eff = _perform_func_intent(eff)
     self.assertEqual(next_eff.intent, Delay(delay=1.5))
     self.assertEqual(resolve_effect(next_eff, None),
                      True)
Example #14
0
    def resolve_service_request(service_request_intent):
        eff = concretize_service_request(
            authenticator=object(),
            log=object(),
            service_configs=make_service_configs(),
            throttler=lambda stype, method, tid: None,
            tenant_id='000000',
            service_request=service_request_intent)

        # "authenticate"
        eff = resolve_authenticate(eff)
        # make request
        return resolve_effect(eff, stub_response)
Example #15
0
    def resolve_service_request(service_request_intent):
        eff = concretize_service_request(
            authenticator=object(),
            log=object(),
            service_configs=make_service_configs(),
            throttler=lambda stype, method, tid: None,
            tenant_id='000000',
            service_request=service_request_intent)

        # "authenticate"
        eff = resolve_authenticate(eff)
        # make request
        return resolve_effect(eff, stub_response)
Example #16
0
    def test_error_per_tenant(self):
        """
        When a request for servers fails, the associated effect results in
        None, and an error is logged.
        """
        log = mock_log()
        log.err.return_value = None

        groups = {
            "t1": [{"tenantId": "t1", "groupId": "g1", "desired": 0}],
            "t2": [{"tenantId": "t2", "groupId": "g2", "desired": 0}],
        }
        effs = get_all_metrics_effects(groups, log)
        results = []
        for eff in effs:
            if eff.intent.tenant_id == "t1":
                results.append(resolve_effect(eff, {}))
            elif eff.intent.tenant_id == "t2":
                err = (ZeroDivisionError, ZeroDivisionError("foo bar"), None)
                results.append(resolve_effect(eff, err, is_error=True))
        self.assertEqual(results, [None, [GroupMetrics("t1", "g1", desired=0, actual=0, pending=0)]])
        log.err.assert_called_once_with(CheckFailureValue(ZeroDivisionError("foo bar")))
Example #17
0
 def test_no_json_response(self):
     """
     ``json_response`` can be set to :data:`False` to get the response
     object and the plaintext body of the response.
     """
     svcreq = service_request(ServiceType.CLOUD_SERVERS,
                              "GET",
                              "servers",
                              json_response=False).intent
     eff = self._concrete(svcreq)
     next_eff = resolve_authenticate(eff)
     stub_response = stub_pure_response("foo")
     result = resolve_effect(next_eff, stub_response)
     self.assertEqual(result, stub_response)
Example #18
0
    def test_get_all_metrics(self):
        """
        Metrics are returned based on the requests done to get server info.
        """
        # Maybe this could use a parameterized "get_all_scaling_group_servers"
        # call to avoid needing to stub the nova responses, but it seems okay.
        servers_t1 = {
            'g1':
            ([_server('g1', 'ACTIVE')] * 3 + [_server('g1', 'BUILD')] * 2),
            'g2': [_server('g2', 'ACTIVE')]
        }

        servers_t2 = {'g4': [_server('g4', 'ACTIVE'), _server('g4', 'BUILD')]}

        groups = {
            "t1": [{
                'tenantId': 't1',
                'groupId': 'g1',
                'desired': 3
            }, {
                'tenantId': 't1',
                'groupId': 'g2',
                'desired': 4
            }],
            "t2": [{
                'tenantId': 't2',
                'groupId': 'g4',
                'desired': 2
            }]
        }

        tenant_servers = {'t1': servers_t1, 't2': servers_t2}

        effs = get_all_metrics_effects(groups, mock_log())
        # All the effs are wrapped in TenantScopes to indicate the tenant
        # of ServiceRequests made under them. We use that tenant to get the
        # stubbed result of get_all_scaling_group_servers.
        results = [
            resolve_effect(eff, tenant_servers[eff.intent.tenant_id])
            for eff in effs
        ]

        self.assertEqual(
            set(reduce(operator.add, results)),
            set([
                GroupMetrics('t1', 'g1', desired=3, actual=3, pending=2),
                GroupMetrics('t1', 'g2', desired=4, actual=1, pending=0),
                GroupMetrics('t2', 'g4', desired=2, actual=1, pending=1)
            ]))
Example #19
0
 def test_delete_and_verify_verify_unexpectedstatus(self):
     """
     :func:`delete_and_verify` raises `UnexpectedServerStatus` error
     if server status returned after deleting is not "deleting"
     """
     eff = delete_and_verify('sid')
     eff = resolve_effect(
         eff, service_request_error_response(APIError(204, {})),
         is_error=True)
     self.assertRaises(
         UnexpectedServerStatus,
         resolve_effect,
         eff,
         (StubResponse(200, {}),
          {'server': {"OS-EXT-STS:task_state": 'bad'}})
     )
Example #20
0
    def test_json(self):
        """
        JSON-serializable requests are dumped before being sent, and
        JSON-serialized responses are parsed.
        """
        input_json = {"a": 1}
        output_json = {"b": 2}
        svcreq = service_request(ServiceType.CLOUD_SERVERS, "GET", "servers",
                                 data=input_json).intent
        eff = self._concrete(svcreq)

        # Input is serialized
        next_eff = resolve_authenticate(eff)
        self.assertEqual(next_eff.intent.data, json.dumps(input_json))

        # Output is parsed
        response, body = stub_pure_response(json.dumps(output_json))
        result = resolve_effect(next_eff, (response, body))
        self.assertEqual(result, (response, output_json))
Example #21
0
    def test_delete_server(self, mock_dav):
        """
        :obj:`DeleteServer.as_effect` calls `delete_and_verify` with
        retries. It returns SUCCESS on completion and RETRY on failure
        """
        mock_dav.side_effect = lambda sid: Effect(sid)
        eff = DeleteServer(server_id='abc123').as_effect()
        self.assertIsInstance(eff.intent, Retry)
        self.assertEqual(
            eff.intent.should_retry,
            ShouldDelayAndRetry(can_retry=retry_times(3),
                                next_interval=exponential_backoff_interval(2)))
        self.assertEqual(eff.intent.effect.intent, 'abc123')

        self.assertEqual(
            resolve_effect(eff, (None, {})),
            (StepResult.RETRY,
             [ErrorReason.String('must re-gather after deletion in order to '
                                 'update the active cache')]))
Example #22
0
    def test_json(self):
        """
        JSON-serializable requests are dumped before being sent, and
        JSON-serialized responses are parsed.
        """
        input_json = {"a": 1}
        output_json = {"b": 2}
        svcreq = service_request(ServiceType.CLOUD_SERVERS,
                                 "GET",
                                 "servers",
                                 data=input_json).intent
        eff = self._concrete(svcreq)

        # Input is serialized
        next_eff = resolve_authenticate(eff)
        self.assertEqual(next_eff.intent.data, json.dumps(input_json))

        # Output is parsed
        response, body = stub_pure_response(json.dumps(output_json))
        result = resolve_effect(next_eff, (response, body))
        self.assertEqual(result, (response, output_json))
Example #23
0
 def test_create_server_request_with_name(self):
     """
     :obj:`CreateServer.as_effect` produces a request for creating a server.
     If the name is given, a randomly generated suffix is appended to the
     server name.
     """
     create = CreateServer(
         server_config=freeze({'server': {'name': 'myserver',
                                          'flavorRef': '1'}}))
     eff = create.as_effect()
     self.assertEqual(eff.intent, Func(generate_server_name))
     eff = resolve_effect(eff, 'random-name')
     self.assertEqual(
         eff.intent,
         service_request(
             ServiceType.CLOUD_SERVERS,
             'POST',
             'servers',
             data={'server': {'name': 'myserver-random-name',
                              'flavorRef': '1'}},
             success_pred=has_code(202),
             reauth_codes=(401,)).intent)
Example #24
0
    def test_create_server_noname(self):
        """
        :obj:`CreateServer.as_effect`, when no name is provided in the launch
        config, will generate the name will from scratch.

        This only verifies intent; result reporting is tested in
        :meth:`test_create_server`.
        """
        create = CreateServer(
            server_config=freeze({'server': {'flavorRef': '1'}}))
        eff = create.as_effect()
        self.assertEqual(eff.intent, Func(generate_server_name))
        eff = resolve_effect(eff, 'random-name')
        self.assertEqual(
            eff.intent,
            service_request(
                ServiceType.CLOUD_SERVERS,
                'POST',
                'servers',
                data={'server': {'name': 'random-name', 'flavorRef': '1'}},
                success_pred=has_code(202),
                reauth_codes=(401,)).intent)
Example #25
0
def resolve_authenticate(eff, token='token'):
    """Resolve an Authenticate effect with test data."""
    return resolve_effect(eff, (token, fake_service_catalog))
Example #26
0
def resolve_authenticate(eff, token='token'):
    """Resolve an Authenticate effect with test data."""
    return resolve_effect(eff, (token, fake_service_catalog))