Ejemplo n.º 1
0
    def test_list_servers_details_all_blows_up_if_got_same_link_twice(self):
        """
        :func:`list_servers_details_all` raises an exception if Nova returns
        the same next link twice in a row.
        """
        bodies = [{
            'servers': ['1', '2'],
            'servers_links': [{
                'href': 'doesnt_matter_url?marker=3',
                'rel': 'next'
            }]
        }, {
            'servers': ['3', '4'],
            'servers_links': [{
                'href': 'doesnt_matter_url?marker=3',
                'rel': 'next'
            }, {
                'href': 'doesnt_matter_url?marker=1',
                'rel': 'prev'
            }]
        }]
        resps = [json.dumps(d) for d in bodies]

        eff = list_servers_details_all({'marker': ['1']})
        seq = [
            (self._list_server_details_intent({'marker': ['1']}),
             service_request_eqf(stub_pure_response(resps[0], 200))),
            (self._list_server_details_log_intent(bodies[0]), lambda _: None),
            (self._list_server_details_intent({'marker': ['3']}),
             service_request_eqf(stub_pure_response(resps[1], 200))),
            (self._list_server_details_log_intent(bodies[1]), lambda _: None)
        ]
        self.assertRaises(NovaComputeFaultError, perform_sequence, seq, eff)
Ejemplo n.º 2
0
    def test_change_clb_node_default_type(self):
        """
        Produce a request for modifying a node on a load balancer with the
        default type, which returns a successful result on 202.
        """
        eff = change_clb_node(lb_id=self.lb_id,
                              node_id='1234',
                              condition="DRAINING",
                              weight=50)
        expected = service_request(ServiceType.CLOUD_LOAD_BALANCERS,
                                   'PUT',
                                   'loadbalancers/{0}/nodes/1234'.format(
                                       self.lb_id),
                                   data={
                                       'node': {
                                           'condition': 'DRAINING',
                                           'weight': 50,
                                           'type': 'PRIMARY'
                                       }
                                   },
                                   success_pred=has_code(202))

        dispatcher = EQFDispatcher([
            (expected.intent, service_request_eqf(stub_pure_response('', 202)))
        ])
        self.assertEqual(sync_perform(dispatcher, eff),
                         stub_pure_response(None, 202))
Ejemplo n.º 3
0
    def test_list_servers_details_all_gets_until_no_next_link(self):
        """
        :func:`list_servers_details_all` follows the servers links until there
        are no more links, and returns a list of servers as the result.  It
        ignores any non-next links.
        """
        bodies = [
            {'servers': ['1', '2'],
             'servers_links': [{'href': 'doesnt_matter_url?marker=3',
                                'rel': 'next'}]},
            {'servers': ['3', '4'],
             'servers_links': [{'href': 'doesnt_matter_url?marker=5',
                                'rel': 'next'},
                               {'href': 'doesnt_matter_url?marker=1',
                                'rel': 'prev'}]},
            {'servers': ['5', '6'],
             'servers_links': [{'href': 'doesnt_matter_url?marker=3',
                                'rel': 'prev'}]}
        ]
        resps = [json.dumps(d) for d in bodies]

        eff = list_servers_details_all({'marker': ['1']})
        seq = [
            (self._list_server_details_intent({'marker': ['1']}),
             service_request_eqf(stub_pure_response(resps[0], 200))),
            (self._list_server_details_log_intent(bodies[0]), lambda _: None),
            (self._list_server_details_intent({'marker': ['3']}),
             service_request_eqf(stub_pure_response(resps[1], 200))),
            (self._list_server_details_log_intent(bodies[1]), lambda _: None),
            (self._list_server_details_intent({'marker': ['5']}),
             service_request_eqf(stub_pure_response(resps[2], 200))),
            (self._list_server_details_log_intent(bodies[2]), lambda _: None)
        ]
        result = perform_sequence(seq, eff)
        self.assertEqual(result, ['1', '2', '3', '4', '5', '6'])
Ejemplo n.º 4
0
    def test_list_servers_details_all_blows_up_if_got_same_link_twice(self):
        """
        :func:`list_servers_details_all` raises an exception if Nova returns
        the same next link twice in a row.
        """
        bodies = [
            {'servers': ['1', '2'],
             'servers_links': [{'href': 'doesnt_matter_url?marker=3',
                                'rel': 'next'}]},
            {'servers': ['3', '4'],
             'servers_links': [{'href': 'doesnt_matter_url?marker=3',
                                'rel': 'next'},
                               {'href': 'doesnt_matter_url?marker=1',
                                'rel': 'prev'}]}
        ]
        resps = [json.dumps(d) for d in bodies]

        eff = list_servers_details_all({'marker': ['1']})
        seq = [
            (self._list_server_details_intent({'marker': ['1']}),
             service_request_eqf(stub_pure_response(resps[0], 200))),
            (self._list_server_details_log_intent(bodies[0]), lambda _: None),
            (self._list_server_details_intent({'marker': ['3']}),
             service_request_eqf(stub_pure_response(resps[1], 200))),
            (self._list_server_details_log_intent(bodies[1]), lambda _: None)
        ]
        self.assertRaises(NovaComputeFaultError, perform_sequence, seq, eff)
Ejemplo n.º 5
0
    def test_publish_autoscale_event(self):
        """
        Publish an event to cloudfeeds.  Successfully handle non-JSON data.
        """
        _log = object()
        eff = cf.publish_autoscale_event({'event': 'stuff'}, log=_log)
        expected = service_request(
            ServiceType.CLOUD_FEEDS, 'POST',
            'autoscale/events',
            headers={'content-type': ['application/vnd.rackspace.atom+json']},
            data={'event': 'stuff'}, log=_log, success_pred=has_code(201),
            json_response=False)

        # success
        dispatcher = EQFDispatcher([(
            expected.intent,
            service_request_eqf(stub_pure_response('<this is xml>', 201)))])
        resp, body = sync_perform(dispatcher, eff)
        self.assertEqual(body, '<this is xml>')

        # Add regression test that 202 should be an API error because this
        # is a bug in CF
        dispatcher = EQFDispatcher([(
            expected.intent,
            service_request_eqf(stub_pure_response('<this is xml>', 202)))])
        self.assertRaises(APIError, sync_perform, dispatcher, eff)
Ejemplo n.º 6
0
    def test_add_clb_nodes(self):
        """
        Produce a request for adding nodes to a load balancer, which returns
        a successful result on a 202.

        Parse the common CLB errors, and a :class:`CLBDuplicateNodesError`.
        """
        nodes = [{"address": "1.1.1.1", "port": 80, "condition": "ENABLED"},
                 {"address": "1.1.1.2", "port": 80, "condition": "ENABLED"},
                 {"address": "1.1.1.5", "port": 81, "condition": "ENABLED"}]

        eff = add_clb_nodes(lb_id=self.lb_id, nodes=nodes)
        expected = service_request(
            ServiceType.CLOUD_LOAD_BALANCERS,
            'POST',
            'loadbalancers/{0}/nodes'.format(self.lb_id),
            data={'nodes': nodes},
            success_pred=has_code(202))

        # success
        seq = [
            (expected.intent, lambda i: stub_json_response({}, 202, {})),
            (log_intent('request-add-clb-nodes', {}), lambda _: None)]
        self.assertEqual(perform_sequence(seq, eff),
                         (StubResponse(202, {}), {}))

        # CLBDuplicateNodesError failure
        msg = ("Duplicate nodes detected. One or more nodes already "
               "configured on load balancer.")
        duplicate_nodes = stub_pure_response(
            json.dumps({'message': msg, 'code': 422}), 422)
        dispatcher = EQFDispatcher([(
            expected.intent, service_request_eqf(duplicate_nodes))])

        with self.assertRaises(CLBDuplicateNodesError) as cm:
            sync_perform(dispatcher, eff)
        self.assertEqual(
            cm.exception,
            CLBDuplicateNodesError(msg, lb_id=six.text_type(self.lb_id)))

        # CLBNodeLimitError failure
        msg = "Nodes must not exceed 25 per load balancer."
        limit = stub_pure_response(
            json.dumps({'message': msg, 'code': 413}), 413)
        dispatcher = EQFDispatcher([(
            expected.intent, service_request_eqf(limit))])

        with self.assertRaises(CLBNodeLimitError) as cm:
            sync_perform(dispatcher, eff)
        self.assertEqual(
            cm.exception,
            CLBNodeLimitError(msg, lb_id=six.text_type(self.lb_id),
                              node_limit=25))

        # all the common failures
        assert_parses_common_clb_errors(self, expected.intent, eff, "123456")
Ejemplo n.º 7
0
    def test_change_clb_node(self):
        """
        Produce a request for modifying a node on a load balancer, which
        returns a successful result on 202.

        Parse the common CLB errors, and :class:`NoSuchCLBNodeError`.
        """
        eff = change_clb_node(lb_id=self.lb_id,
                              node_id='1234',
                              condition="DRAINING",
                              weight=50,
                              _type='SECONDARY')
        expected = service_request(ServiceType.CLOUD_LOAD_BALANCERS,
                                   'PUT',
                                   'loadbalancers/{0}/nodes/1234'.format(
                                       self.lb_id),
                                   data={
                                       'node': {
                                           'condition': 'DRAINING',
                                           'weight': 50,
                                           'type': 'SECONDARY'
                                       }
                                   },
                                   success_pred=has_code(202))

        # success
        dispatcher = EQFDispatcher([
            (expected.intent, service_request_eqf(stub_pure_response('', 202)))
        ])
        self.assertEqual(sync_perform(dispatcher, eff),
                         stub_pure_response(None, 202))

        # NoSuchCLBNode failure
        msg = "Node with id #1234 not found for loadbalancer #{0}".format(
            self.lb_id)
        no_such_node = stub_pure_response(
            json.dumps({
                'message': msg,
                'code': 404
            }), 404)
        dispatcher = EQFDispatcher([(expected.intent,
                                     service_request_eqf(no_such_node))])

        with self.assertRaises(NoSuchCLBNodeError) as cm:
            sync_perform(dispatcher, eff)
        self.assertEqual(
            cm.exception,
            NoSuchCLBNodeError(msg,
                               lb_id=six.text_type(self.lb_id),
                               node_id=u'1234'))

        # all the common failures
        assert_parses_common_clb_errors(self, expected.intent, eff, "123456")
Ejemplo n.º 8
0
 def test_add_json_response(self):
     """The produced request function results in a parsed data structure."""
     response = stub_pure_response('{"a": "b"}', 200)
     request_ = add_json_response(stub_request(response))
     self.assertEqual(resolve_stubs(request_('m', 'u')), (response[0], {
         'a': 'b'
     }))
Ejemplo n.º 9
0
    def assert_handles_nova_rate_limiting(self, intent, effect):
        """
        If the provided intent returns a response consistent with Nova
        rate-limiting requests, then performing the effect will raise a
        :class:`NovaRateLimitError`.
        """
        failure_body = {
            "overLimit": {
                "code": 413,
                "message": "OverLimit Retry...",
                "details": "Error Details...",
                "retryAfter": "2015-02-27T23:42:27Z"
            }
        }
        dispatcher = EQFDispatcher([
            (intent,
             service_request_eqf(
                 stub_pure_response(json.dumps(failure_body), 413)))
        ])

        with self.assertRaises(NovaRateLimitError) as cm:
            sync_perform(dispatcher, effect)

        self.assertEqual(cm.exception,
                         NovaRateLimitError("OverLimit Retry..."))
Ejemplo n.º 10
0
    def test_throttling(self):
        """
        When the throttler function returns a bracketing function, it's used to
        throttle the request.
        """
        def throttler(stype, method, tid):
            if (stype == ServiceType.CLOUD_SERVERS and method == 'get'
                    and tid == 1):
                return bracket

        bracket = object()
        svcreq = service_request(ServiceType.CLOUD_SERVERS, 'GET',
                                 'servers').intent

        response = stub_pure_response({}, 200)
        seq = SequenceDispatcher([
            (_Throttle(bracket=bracket, effect=mock.ANY),
             nested_sequence([
                 (Authenticate(authenticator=self.authenticator,
                               tenant_id=1,
                               log=self.log), lambda i:
                  ('token', fake_service_catalog)),
                 (Request(method='GET',
                          url='http://dfw.openstack/servers',
                          headers=headers('token'),
                          log=self.log), lambda i: response),
             ])),
        ])

        eff = self._concrete(svcreq, throttler=throttler)
        with seq.consume():
            result = sync_perform(seq, eff)
        self.assertEqual(result, (response[0], {}))
Ejemplo n.º 11
0
    def test_throttling(self):
        """
        When the throttler function returns a bracketing function, it's used to
        throttle the request.
        """
        def throttler(stype, method, tid):
            if (stype == ServiceType.CLOUD_SERVERS and
                    method == 'get' and tid == 1):
                return bracket
        bracket = object()
        svcreq = service_request(
            ServiceType.CLOUD_SERVERS, 'GET', 'servers').intent

        response = stub_pure_response({}, 200)
        seq = SequenceDispatcher([
            (_Throttle(bracket=bracket, effect=mock.ANY),
             nested_sequence([
                 (Authenticate(authenticator=self.authenticator,
                               tenant_id=1,
                               log=self.log),
                  lambda i: ('token', fake_service_catalog)),
                 (Request(method='GET', url='http://dfw.openstack/servers',
                          headers=headers('token'), log=self.log),
                  lambda i: response),
             ])),
        ])

        eff = self._concrete(svcreq, throttler=throttler)
        with seq.consume():
            result = sync_perform(seq, eff)
        self.assertEqual(result, (response[0], {}))
Ejemplo n.º 12
0
    def _test_reauth(self, code, reauth_codes=None):
        reauth_effect = Effect(Constant(None))

        def get_auth_headers():
            return Effect(Constant(headers("first-token")))

        def refresh_auth_info():
            return reauth_effect

        # First we try to make a simple request.
        kwargs = {}
        if reauth_codes is not None:
            kwargs['reauth_codes'] = reauth_codes
        eff = request("get", "/foo",
                      get_auth_headers=get_auth_headers,
                      refresh_auth_info=refresh_auth_info,
                      **kwargs)

        # The initial (cached) auth headers are retrieved.
        eff = resolve_stubs(eff)

        # when an authentication error is returned from the HTTP server,
        # the auth info is automitacally refreshed:
        stub_result = stub_pure_response("badauth!", code=code)
        reauth_effect_result = resolve_effect(eff, stub_result)
        self.assertIs(reauth_effect_result.intent, reauth_effect.intent)

        # And the original auth error HTTP response is still returned.
        api_error = self.assertRaises(APIError, resolve_stubs, reauth_effect_result)
        self.assertEqual(api_error.code, code)
        self.assertEqual(api_error.body, "badauth!")
        self.assertEqual(api_error.headers, {})
Ejemplo n.º 13
0
 def test_json_response(self):
     """The JSON response is decoded into Python objects."""
     request_ = self._no_reauth_client()
     eff = request_("get", "/foo")
     self.assertEqual(
         resolve_effect(eff, stub_pure_response({"foo": "bar"})),
         {'foo': 'bar'})
Ejemplo n.º 14
0
 def test_add_effect_on_response(self):
     """Test the decorator :func:`add_effect_on_response`."""
     badauth = stub_pure_response("badauth!", code=401)
     request_ = add_effect_on_response(self.invalidate_effect, (401, ),
                                       stub_request(badauth))
     eff = request_('m', 'u')
     self.assertEqual(resolve_stubs(eff), badauth)
     self.assertEqual(self.invalidations, [True])
Ejemplo n.º 15
0
 def test_remove_clb_nodes_non_202(self):
     """Any random HTTP response code is bubbled up as an APIError."""
     eff = remove_clb_nodes(self.lb_id, ["1", "2"])
     seq = [
         (self.expected_node_removal_req().intent,
          service_request_eqf(stub_pure_response({}, 200))),
     ]
     self.assertRaises(APIError, perform_sequence, seq, eff)
Ejemplo n.º 16
0
 def test_add_effect_on_response(self):
     """Test the decorator :func:`add_effect_on_response`."""
     badauth = stub_pure_response("badauth!", code=401)
     request_ = add_effect_on_response(
         self.invalidate_effect, (401,), stub_request(badauth))
     eff = request_('m', 'u')
     self.assertEqual(resolve_stubs(eff), badauth)
     self.assertEqual(self.invalidations, [True])
Ejemplo n.º 17
0
 def test_success(self):
     """
     :func:`check_response` returns the value passed into it if the
     predicate likes the response.
     """
     pred = lambda _response, _content: True
     result = stub_pure_response(None)
     self.assertIdentical(check_response(pred, result), result)
Ejemplo n.º 18
0
 def test_error(self):
     """
     :func:`check_response` raises :class:`APIError` if the predicate
     doesn't like the response.
     """
     pred = lambda _response, _content: False
     result = stub_pure_response(None)
     self.assertRaises(APIError, check_response, pred, result)
Ejemplo n.º 19
0
 def test_error(self):
     """
     :func:`add_error_handling` ostensibly invokes :func:`check_response`.
     """
     response = stub_pure_response("", code=404)
     request_fn = add_error_handling(has_code(200), stub_request(response))
     eff = request_fn('GET', '/xyzzy')
     self.assertRaises(APIError, resolve_stubs, eff)
Ejemplo n.º 20
0
 def test_error(self):
     """
     :func:`add_error_handling` ostensibly invokes :func:`check_response`.
     """
     response = stub_pure_response("", code=404)
     request_fn = add_error_handling(has_code(200), stub_request(response))
     eff = request_fn('GET', '/xyzzy')
     self.assertRaises(APIError, resolve_stubs, eff)
Ejemplo n.º 21
0
 def test_remove_clb_nodes_non_202(self):
     """Any random HTTP response code is bubbled up as an APIError."""
     eff = remove_clb_nodes(self.lb_id, ["1", "2"])
     seq = [
         (self.expected_node_removal_req().intent,
          service_request_eqf(stub_pure_response({}, 200))),
     ]
     self.assertRaises(APIError, perform_sequence, seq, eff)
Ejemplo n.º 22
0
 def test_error(self):
     """
     :func:`check_response` raises :class:`APIError` if the predicate
     doesn't like the response.
     """
     pred = lambda _response, _content: False
     result = stub_pure_response(None)
     self.assertRaises(APIError, check_response, pred, result)
Ejemplo n.º 23
0
 def test_success(self):
     """
     :func:`check_response` returns the value passed into it if the
     predicate likes the response.
     """
     pred = lambda _response, _content: True
     result = stub_pure_response(None)
     self.assertIdentical(check_response(pred, result), result)
Ejemplo n.º 24
0
 def test_empty_json_response(self):
     """
     If the body is empty, it will be turned into :data:`None`, and not
     passed to the JSON parser.
     """
     response = stub_pure_response('', 204)
     request_ = add_json_response(stub_request(response))
     self.assertEqual(resolve_stubs(request_('m', 'u')),
                      (response[0], None))
Ejemplo n.º 25
0
 def test_invalidate_unnecessary(self):
     """
     The result is returned immediately and the provided effect is not
     invoked when the HTTP response code is not in ``codes``.
     """
     good = stub_pure_response("okay!", code=200)
     result = effect_on_response((401, ), self.invalidate_effect, good)
     self.assertEqual(result, good)
     self.assertEqual(self.invalidations, [])
Ejemplo n.º 26
0
 def test_empty_json_response(self):
     """
     If the body is empty, it will be turned into :data:`None`, and not
     passed to the JSON parser.
     """
     response = stub_pure_response('', 204)
     request_ = add_json_response(stub_request(response))
     self.assertEqual(resolve_stubs(request_('m', 'u')),
                      (response[0], None))
Ejemplo n.º 27
0
 def test_invalidate_unnecessary(self):
     """
     The result is returned immediately and the provided effect is not
     invoked when the HTTP response code is not in ``codes``.
     """
     good = stub_pure_response("okay!", code=200)
     result = effect_on_response((401,), self.invalidate_effect, good)
     self.assertEqual(result, good)
     self.assertEqual(self.invalidations, [])
Ejemplo n.º 28
0
    def test_list_servers_details_all_gets_until_no_next_link(self):
        """
        :func:`list_servers_details_all` follows the servers links until there
        are no more links, and returns a list of servers as the result.  It
        ignores any non-next links.
        """
        bodies = [{
            'servers': ['1', '2'],
            'servers_links': [{
                'href': 'doesnt_matter_url?marker=3',
                'rel': 'next'
            }]
        }, {
            'servers': ['3', '4'],
            'servers_links': [{
                'href': 'doesnt_matter_url?marker=5',
                'rel': 'next'
            }, {
                'href': 'doesnt_matter_url?marker=1',
                'rel': 'prev'
            }]
        }, {
            'servers': ['5', '6'],
            'servers_links': [{
                'href': 'doesnt_matter_url?marker=3',
                'rel': 'prev'
            }]
        }]
        resps = [json.dumps(d) for d in bodies]

        eff = list_servers_details_all({'marker': ['1']})
        seq = [
            (self._list_server_details_intent({'marker': ['1']}),
             service_request_eqf(stub_pure_response(resps[0], 200))),
            (self._list_server_details_log_intent(bodies[0]), lambda _: None),
            (self._list_server_details_intent({'marker': ['3']}),
             service_request_eqf(stub_pure_response(resps[1], 200))),
            (self._list_server_details_log_intent(bodies[1]), lambda _: None),
            (self._list_server_details_intent({'marker': ['5']}),
             service_request_eqf(stub_pure_response(resps[2], 200))),
            (self._list_server_details_log_intent(bodies[2]), lambda _: None)
        ]
        result = perform_sequence(seq, eff)
        self.assertEqual(result, ['1', '2', '3', '4', '5', '6'])
Ejemplo n.º 29
0
 def test_add_event_succeeds_if_request_succeeds(self):
     """
     Adding an event succeeds without retrying if the service request
     succeeds.  Testing what response code causes a service request to
     succeeds is beyond the scope of this test.
     """
     body = "<some xml>"
     resp = stub_pure_response(body, 201)
     response = [lambda _: (resp, body)]
     self.assertEqual(self._perform_add_event(response), (resp, body))
Ejemplo n.º 30
0
 def test_add_event_succeeds_if_request_succeeds(self):
     """
     Adding an event succeeds without retrying if the service request
     succeeds.  Testing what response code causes a service request to
     succeeds is beyond the scope of this test.
     """
     body = "<some xml>"
     resp = stub_pure_response(body, 201)
     response = [lambda _: (resp, body)]
     self.assertEqual(self._perform_add_event(response), (resp, body))
Ejemplo n.º 31
0
 def test_api_error_default(self):
     """
     APIError is raised when the response code isn't 200, by default.
     """
     request_ = self._no_reauth_client()
     eff = request_("get", "/foo")
     self.assertRaises(
         APIError,
         resolve_effect, eff,
         stub_pure_response({"foo": "bar"}, code=404))
Ejemplo n.º 32
0
 def test_invalidate(self):
     """
     :func:`effect_on_response` invokes the provided effect and
     returns an Effect of the original response.
     """
     badauth = stub_pure_response("badauth!", code=401)
     eff = effect_on_response((401, ), self.invalidate_effect, badauth)
     self.assertEqual(eff.intent, self.invalidate_effect.intent)
     self.assertEqual(resolve_stubs(eff), badauth)
     self.assertEqual(self.invalidations, [True])
Ejemplo n.º 33
0
 def test_invalidate(self):
     """
     :func:`effect_on_response` invokes the provided effect and
     returns an Effect of the original response.
     """
     badauth = stub_pure_response("badauth!", code=401)
     eff = effect_on_response((401,), self.invalidate_effect, badauth)
     self.assertEqual(eff.intent, self.invalidate_effect.intent)
     self.assertEqual(resolve_stubs(eff), badauth)
     self.assertEqual(self.invalidations, [True])
Ejemplo n.º 34
0
 def test_remove_clb_nodes_retry_on_some_invalid_nodes(self):
     """
     When CLB returns an error indicating that some of the nodes are
     invalid, the request is retried without the offending nodes.
     """
     node_ids = map(str, range(1, 5))
     eff = remove_clb_nodes(self.lb_id, node_ids)
     response = stub_pure_response(
         {'validationErrors': {'messages': [
             'Node ids 1,3 are not a part of your loadbalancer']}},
         400)
     response2 = stub_pure_response({}, 202)
     seq = [
         (self.expected_node_removal_req(node_ids).intent,
          service_request_eqf(response)),
         (self.expected_node_removal_req(["2", "4"]).intent,
          service_request_eqf(response2))
     ]
     self.assertIs(perform_sequence(seq, eff), None)
Ejemplo n.º 35
0
    def test_change_clb_node(self):
        """
        Produce a request for modifying a node on a load balancer, which
        returns a successful result on 202.

        Parse the common CLB errors, and :class:`NoSuchCLBNodeError`.
        """
        eff = change_clb_node(lb_id=self.lb_id, node_id='1234',
                              condition="DRAINING", weight=50,
                              _type='SECONDARY')
        expected = service_request(
            ServiceType.CLOUD_LOAD_BALANCERS,
            'PUT',
            'loadbalancers/{0}/nodes/1234'.format(self.lb_id),
            data={'node': {'condition': 'DRAINING',
                           'weight': 50, 'type': 'SECONDARY'}},
            success_pred=has_code(202))

        # success
        dispatcher = EQFDispatcher([(
            expected.intent,
            service_request_eqf(stub_pure_response('', 202)))])
        self.assertEqual(sync_perform(dispatcher, eff),
                         stub_pure_response(None, 202))

        # NoSuchCLBNode failure
        msg = "Node with id #1234 not found for loadbalancer #{0}".format(
            self.lb_id)
        no_such_node = stub_pure_response(
            json.dumps({'message': msg, 'code': 404}), 404)
        dispatcher = EQFDispatcher([(
            expected.intent, service_request_eqf(no_such_node))])

        with self.assertRaises(NoSuchCLBNodeError) as cm:
            sync_perform(dispatcher, eff)
        self.assertEqual(
            cm.exception,
            NoSuchCLBNodeError(msg, lb_id=six.text_type(self.lb_id),
                               node_id=u'1234'))

        # all the common failures
        assert_parses_common_clb_errors(self, expected.intent, eff, "123456")
Ejemplo n.º 36
0
 def test_remove_clb_nodes_retry_on_some_invalid_nodes(self):
     """
     When CLB returns an error indicating that some of the nodes are
     invalid, the request is retried without the offending nodes.
     """
     node_ids = map(str, range(1, 5))
     eff = remove_clb_nodes(self.lb_id, node_ids)
     response = stub_pure_response(
         {
             'validationErrors': {
                 'messages':
                 ['Node ids 1,3 are not a part of your loadbalancer']
             }
         }, 400)
     response2 = stub_pure_response({}, 202)
     seq = [(self.expected_node_removal_req(node_ids).intent,
             service_request_eqf(response)),
            (self.expected_node_removal_req(["2", "4"]).intent,
             service_request_eqf(response2))]
     self.assertIs(perform_sequence(seq, eff), None)
Ejemplo n.º 37
0
    def test_change_clb_node_default_type(self):
        """
        Produce a request for modifying a node on a load balancer with the
        default type, which returns a successful result on 202.
        """
        eff = change_clb_node(lb_id=self.lb_id, node_id='1234',
                              condition="DRAINING", weight=50)
        expected = service_request(
            ServiceType.CLOUD_LOAD_BALANCERS,
            'PUT',
            'loadbalancers/{0}/nodes/1234'.format(self.lb_id),
            data={'node': {'condition': 'DRAINING',
                           'weight': 50, 'type': 'PRIMARY'}},
            success_pred=has_code(202))

        dispatcher = EQFDispatcher([(
            expected.intent,
            service_request_eqf(stub_pure_response('', 202)))])
        self.assertEqual(sync_perform(dispatcher, eff),
                         stub_pure_response(None, 202))
Ejemplo n.º 38
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)
Ejemplo n.º 39
0
    def test_check_stack_201(self):
        """Check stack calls Heat and returns None on a 201."""
        stack_name = 'foostack'
        stack_id = 'foo_id'
        eff = check_stack(stack_name, stack_id)
        expected_intent = self._check_stack_intent(stack_name, stack_id)

        seq = [(expected_intent,
                service_request_eqf(stub_pure_response(b'', 201))),
               (log_intent('request-check-stack', b'', False), lambda _: None)]
        result = perform_sequence(seq, eff)
        self.assertEqual(result, None)
Ejemplo n.º 40
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)
Ejemplo n.º 41
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)
Ejemplo n.º 42
0
 def test_create_stack(self):
     """Create stack calls Heat and returns the new stack details on 201."""
     data = {'data': 'data'}
     body = {'stack': 'the_stack_json'}
     eff = create_stack(data)
     expected_intent = self._create_stack_intent(data)
     seq = [(expected_intent,
             service_request_eqf(stub_pure_response(json.dumps(body),
                                                    201))),
            (log_intent('request-create-stack', body), lambda _: None)]
     result = perform_sequence(seq, eff)
     self.assertEqual(result, 'the_stack_json')
Ejemplo n.º 43
0
    def test_performs_tenant_scope(self, deferred_lock_run):
        """
        :func:`perform_tenant_scope` performs :obj:`TenantScope`, and uses the
        default throttler
        """
        # We want to ensure
        # 1. the TenantScope can be performed
        # 2. the ServiceRequest is run within a lock, since it matches the
        #    default throttling policy

        set_config_data({
            "cloud_client": {
                "throttling": {
                    "create_server_delay": 1,
                    "delete_server_delay": 0.4
                }
            }
        })
        self.addCleanup(set_config_data, {})
        clock = Clock()
        authenticator = object()
        log = object()
        dispatcher = get_cloud_client_dispatcher(clock, authenticator, log,
                                                 make_service_configs())
        svcreq = service_request(ServiceType.CLOUD_SERVERS, 'POST', 'servers')
        tscope = TenantScope(tenant_id='111', effect=svcreq)

        def run(f, *args, **kwargs):
            result = f(*args, **kwargs)
            result.addCallback(lambda x: (x[0], assoc(x[1], 'locked', True)))
            return result

        deferred_lock_run.side_effect = run

        response = stub_pure_response({}, 200)
        seq = SequenceDispatcher([
            (Authenticate(authenticator=authenticator,
                          tenant_id='111',
                          log=log), lambda i: ('token', fake_service_catalog)),
            (Request(method='POST',
                     url='http://dfw.openstack/servers',
                     headers=headers('token'),
                     log=log), lambda i: response),
        ])

        disp = ComposedDispatcher([seq, dispatcher])
        with seq.consume():
            result = perform(disp, Effect(tscope))
            self.assertNoResult(result)
            clock.advance(1)
            self.assertEqual(self.successResultOf(result), (response[0], {
                'locked': True
            }))
Ejemplo n.º 44
0
 def test_remove_clb_nodes_success(self):
     """
     A DELETE request is sent, and the Effect returns None if 202 is
     returned.
     """
     eff = remove_clb_nodes(self.lb_id, ["1", "2"])
     seq = [
         (self.expected_node_removal_req().intent,
          service_request_eqf(stub_pure_response({}, 202))),
     ]
     result = perform_sequence(seq, eff)
     self.assertIs(result, None)
Ejemplo n.º 45
0
 def test_remove_clb_nodes_success(self):
     """
     A DELETE request is sent, and the Effect returns None if 202 is
     returned.
     """
     eff = remove_clb_nodes(self.lb_id, ["1", "2"])
     seq = [
         (self.expected_node_removal_req().intent,
          service_request_eqf(stub_pure_response({}, 202))),
     ]
     result = perform_sequence(seq, eff)
     self.assertIs(result, None)
Ejemplo n.º 46
0
 def test_create_stack(self):
     """Create stack calls Heat and returns the new stack details on 201."""
     data = {'data': 'data'}
     body = {'stack': 'the_stack_json'}
     eff = create_stack(data)
     expected_intent = self._create_stack_intent(data)
     seq = [
         (expected_intent,
          service_request_eqf(stub_pure_response(json.dumps(body), 201))),
         (log_intent('request-create-stack', body), lambda _: None)
     ]
     result = perform_sequence(seq, eff)
     self.assertEqual(result, 'the_stack_json')
Ejemplo n.º 47
0
 def test_list_stacks_all(self):
     """
     :func:`list_stacks_all` returns the JSON response from listing stacks.
     """
     body = {'stacks': ['foo', 'bar']}
     eff = list_stacks_all()
     expected_intent = self._list_stacks_all_intent(None)
     seq = [(expected_intent,
             service_request_eqf(stub_pure_response(json.dumps(body),
                                                    200))),
            (log_intent('request-list-stacks-all', body), lambda _: None)]
     stacks = perform_sequence(seq, eff)
     self.assertEqual(stacks, ['foo', 'bar'])
Ejemplo n.º 48
0
 def test_list_stacks_all(self):
     """
     :func:`list_stacks_all` returns the JSON response from listing stacks.
     """
     body = {'stacks': ['foo', 'bar']}
     eff = list_stacks_all()
     expected_intent = self._list_stacks_all_intent(None)
     seq = [
         (expected_intent,
          service_request_eqf(stub_pure_response(json.dumps(body), 200))),
         (log_intent('request-list-stacks-all', body), lambda _: None)
     ]
     stacks = perform_sequence(seq, eff)
     self.assertEqual(stacks, ['foo', 'bar'])
Ejemplo n.º 49
0
    def test_check_stack_200(self):
        """Check stack calls Heat and returns None on a 200."""
        stack_name = 'foostack'
        stack_id = 'foo_id'
        eff = check_stack(stack_name, stack_id)
        expected_intent = self._check_stack_intent(stack_name, stack_id)

        seq = [
            (expected_intent,
             service_request_eqf(stub_pure_response('200!', 200))),
            (log_intent('request-check-stack', '200!', False), lambda _: None)
        ]
        result = perform_sequence(seq, eff)
        self.assertEqual(result, None)
Ejemplo n.º 50
0
    def test_delete_stack(self):
        """Delete stack calls Heat and returns None on a 204."""
        stack_name = 'foostack'
        stack_id = 'foo_id'
        eff = delete_stack(stack_name, stack_id)
        expected_intent = self._delete_stack_intent(stack_name, stack_id)

        seq = [
            (expected_intent,
             service_request_eqf(stub_pure_response(b'', 204))),
            (log_intent('request-delete-stack', b'', False), lambda _: None)
        ]
        result = perform_sequence(seq, eff)
        self.assertEqual(result, None)
Ejemplo n.º 51
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)
Ejemplo n.º 52
0
def _perform_one_request(intent, effect, response_code, response_body,
                         log_intent=None):
    """
    Perform a request effect using EQFDispatcher, providing the given
    body and status code.
    """
    seq = [(
        intent,
        service_request_eqf(
            stub_pure_response(response_body, response_code))
    )]
    if log_intent is not None:
        seq.append((log_intent, lambda _: None))
    return perform_sequence(seq, effect)
Ejemplo n.º 53
0
 def test_update_stack(self):
     """Update stack calls Heat and returns None on a 202."""
     stack_name = 'foostack'
     stack_id = 'foo_id'
     stack_args = {'stack': 'data'}
     eff = update_stack(stack_name, stack_id, stack_args)
     expected_intent = self._update_stack_intent(stack_name, stack_id,
                                                 stack_args)
     seq = [(expected_intent,
             service_request_eqf(stub_pure_response(b'', 202))),
            (log_intent('request-update-stack', b'',
                        False), lambda _: None)]
     result = perform_sequence(seq, eff)
     self.assertEqual(result, None)
Ejemplo n.º 54
0
 def test_update_stack_with_response_body(self):
     """Update stack succeeds even if there is content in the response."""
     stack_name = 'foostack'
     stack_id = 'foo_id'
     stack_args = {'stack': 'data'}
     eff = update_stack(stack_name, stack_id, stack_args)
     expected_intent = self._update_stack_intent(stack_name, stack_id,
                                                 stack_args)
     seq = [(expected_intent,
             service_request_eqf(stub_pure_response('xyz', 202))),
            (log_intent('request-update-stack', 'xyz',
                        False), lambda _: None)]
     result = perform_sequence(seq, eff)
     self.assertEqual(result, None)
Ejemplo n.º 55
0
 def test_get_server_details_success(self):
     """
     Produce a request getting a Nova server's details, which
     returns a successful result on 200.
     """
     server_id, expected, real = self._setup_for_get_server_details()
     body = {"so much": "data"}
     seq = [(expected.intent,
             service_request_eqf(stub_pure_response(json.dumps(body),
                                                    200))),
            (log_intent('request-one-server-details',
                        body), lambda _: None)]
     resp, response_json = perform_sequence(seq, real)
     self.assertEqual(resp, StubResponse(200, {}))
     self.assertEqual(response_json, body)
Ejemplo n.º 56
0
 def test_get_server_details_success(self):
     """
     Produce a request getting a Nova server's details, which
     returns a successful result on 200.
     """
     server_id, expected, real = self._setup_for_get_server_details()
     body = {"so much": "data"}
     seq = [
         (expected.intent,
          service_request_eqf(stub_pure_response(json.dumps(body), 200))),
         (log_intent('request-one-server-details', body), lambda _: None)
     ]
     resp, response_json = perform_sequence(seq, real)
     self.assertEqual(resp, StubResponse(200, {}))
     self.assertEqual(response_json, body)
Ejemplo n.º 57
0
 def test_remove_clb_nodes_random_400(self):
     """Random 400s that can't be parsed are bubbled up as an APIError."""
     error_bodies = [
         {'validationErrors': {'messages': ['bar']}},
         {'messages': 'bar'},
         {'validationErrors': {'messages': []}},
         "random non-json"
     ]
     for body in error_bodies:
         eff = remove_clb_nodes(self.lb_id, ["1", "2"])
         seq = [
             (self.expected_node_removal_req().intent,
              service_request_eqf(stub_pure_response(body, 400))),
         ]
         self.assertRaises(APIError, perform_sequence, seq, eff)
Ejemplo n.º 58
0
    def test_set_nova_metadata_item_success(self):
        """
        Produce a request setting a metadata item on a Nova server, which
        returns a successful result on 200.
        """
        server_id, expected, real = self._setup_for_set_nova_metadata_item()
        body = {"meta": {"k": "v"}}

        seq = [(expected.intent,
                service_request_eqf(stub_pure_response(json.dumps(body),
                                                       200))),
               (log_intent('request-set-metadata-item', body), lambda _: None)]
        resp, response_json = perform_sequence(seq, real)
        self.assertEqual(resp, StubResponse(200, {}))
        self.assertEqual(response_json, body)
Ejemplo n.º 59
0
def _perform_one_request(intent,
                         effect,
                         response_code,
                         response_body,
                         log_intent=None):
    """
    Perform a request effect using EQFDispatcher, providing the given
    body and status code.
    """
    seq = [
        (intent,
         service_request_eqf(stub_pure_response(response_body, response_code)))
    ]
    if log_intent is not None:
        seq.append((log_intent, lambda _: None))
    return perform_sequence(seq, effect)