예제 #1
0
    def test_update_health_monitor(self):
        """
        `:func:update_health_monitor` will call
        ``PUT .../loadbalancers/lb_id/healthmonitor`` with udpated config,
        succeeds on 202, and retries on pending update for 60 seconds.
        It does not retry if the error is not PENDING_UPDATE.
        """
        main_treq_args = [
            'put', 'clburl/loadbalancers/12345/healthmonitor',
            (('{"healthMonitor": {"type": "CONNECT"}}', ),
             self.expected_kwargs)
        ]

        def update(clb, clock):
            return clb.update_health_monitor(self.rcs, {"type": "CONNECT"},
                                             clock=clock)

        self.assert_mutate_function_retries_until_success(
            update, main_treq_args, (Response(202), ""), None)

        self.assert_mutate_function_retries_until_timeout(
            update, main_treq_args, 60)

        self.assert_mutate_function_does_not_retry_if_not_pending_update(
            update, main_treq_args)
예제 #2
0
    def get_fake_treq_for_delete(self, get_response, del_response=None):
        """
        Return a CLB for use with deleting a CLB - this is different than
        the one returned by `get_clb` because it requires stubbing out two
        treq requests.
        """
        del_response = del_response or Response(202)

        class FakeTreq(object):
            def delete(cls, _url, *args, **kwargs):
                # args and kwargs are the same as the get ones
                self.assertEqual(args, ())
                self.assertEqual(kwargs, self.expected_kwargs)
                self.assertEqual(
                    _url, 'clburl/loadbalancers/{0}'.format(self.clb_id))
                return succeed(del_response)

            def get(cls, _url, *args, **kwargs):
                cls.delete(_url, *args, **kwargs)
                return succeed(get_response)

            def content(cls, resp):
                return succeed(resp.strbody)

            def json_content(cls, resp):
                return succeed(json.loads(resp.strbody))

        return FakeTreq()
예제 #3
0
    def assert_mutate_function_does_not_retry_if_not_pending_update(
            self, mutate_callable, expected_args):
        """
        Assert that some CLB function that mutates the CLB will not retry if
        the error is not a pending update.

        :param mutate_callable: a callable which takes a clb argument and
            a clock argument - this callable should call the CLB's mutate
            function with the required arguments and return the function's
            return value.  For example:
            ``lambda clb, clk: clb.update_node(..., clock=clk)``
        :param expected_args: What are the expected treq arguments?  This
            should be an array of
            [method, url, (expected args, expected kwargs)]
        """
        clock = Clock()
        pending_delete = {
            "message": ("Load Balancer '12345' has a status of "
                        "'PENDING_DELETE' and is considered immutable."),
            "code":
            422
        }
        clb = self.get_clb(
            *(expected_args +
              [Response(422), json.dumps(pending_delete)]))
        d = mutate_callable(clb, clock)
        self.failureResultOf(d, UpstreamError)
예제 #4
0
 def get(cls, url, headers, pool):
     self.get_calls += 1
     self.assertIs(self.pool, pool)
     self.assertEqual(["token"], headers.get('x-auth-token'))
     self.assertEqual(
         ['clburl', 'loadbalancers', 'clb_id', 'nodes'],
         url.split('/'))
     return succeed(Response(200))
예제 #5
0
 def test_list_nodes(self):
     """
     Listing nodes calls the right endpoint and succeeds on 200.
     """
     clb = self.get_clb('get', 'clburl/loadbalancers/12345/nodes',
                        ((), self.expected_kwargs), Response(200),
                        '{"nodes": []}')
     d = clb.list_nodes(self.rcs)
     self.assertEqual({'nodes': []}, self.successResultOf(d))
예제 #6
0
    def setUp(self):
        """
        Set up fake pool, treq, responses, and RCS.
        """
        self.pool = object()
        self.rcs = object()
        self.expected_kwargs = {'pool': self.pool}

        self.delete_treq = get_fake_treq(
            self, 'DELETE',
            "/mimic/v1.1/IdentityControlAPI/behaviors/some_event/behavior_id",
            ((), self.expected_kwargs),
            (Response(204), "successfully deleted behavior"))
예제 #7
0
    def test_delete_clb_retries_until_timeout(self):
        """
        Deleting a CLB will retry if the state wonky until it times out.
        """
        clock = Clock()
        self.clb_id = 12345
        _treq = self.get_fake_treq_for_delete(Response(
            200, strbody='{"loadBalancer": {"status": "PENDING_UPDATE"}}'),
                                              del_response=Response(400))

        clb = CloudLoadBalancer(pool=self.pool, treq=_treq)
        clb.clb_id = self.clb_id
        d = clb.delete(self.rcs, clock=clock)
        self.assertNoResult(d)

        timeout = 60
        for _ in range((timeout - 1) / 3):
            clock.pump([3])
            self.assertNoResult(d)

        clock.pump([3])
        self.failureResultOf(d, TimedOutError)
예제 #8
0
    def test_delete_clb_does_not_retry_on_get_failure(self):
        """
        Deleting a CLB will retry if the state wonky until it times out.
        """
        clock = Clock()
        self.clb_id = 12345
        _treq = self.get_fake_treq_for_delete(
            Response(400, strbody="Something is wrong"))

        clb = CloudLoadBalancer(pool=self.pool, treq=_treq)
        clb.clb_id = self.clb_id

        d = clb.delete(self.rcs, clock=clock)
        self.failureResultOf(d, UpstreamError)
예제 #9
0
    def setUp(self):
        """
        Set up fake pool, treq, responses, and RCS.
        """
        self.pool = object()

        class FakeRCS(object):
            endpoints = {'mimic_nova': 'mimicnovaurl'}

        self.rcs = FakeRCS()
        self.server_id = 'server_id'
        self.expected_kwargs = {'pool': self.pool}

        self.delete_treq = get_fake_treq(
            self, 'DELETE', "mimicnovaurl/behaviors/some_event/behavior_id",
            ((), self.expected_kwargs),
            (Response(204), "successfully deleted behavior"))
예제 #10
0
    def test_change_server_statuses(self):
        """
        Change server statuses calls the right endpoint and succeeds on 201.
        """
        _treq = get_fake_treq(
            self, 'POST', "mimicnovaurl/attributes",
            ((json.dumps({'status': {
                'id1': 'ERROR',
                'id2': 'DELETED'
            }}), ), self.expected_kwargs),
            (Response(201), "successful change response"))

        d = MimicNova(pool=self.pool, treq=_treq).change_server_statuses(
            self.rcs, {
                'id1': 'ERROR',
                'id2': 'DELETED'
            })
        self.assertEqual('successful change response', self.successResultOf(d))
예제 #11
0
    def test_sequenced_behaviors(self):
        """
        Cause a sequence of behaviors, and succeeds on 201.  When a test case
        is provided for which a cleanup should be added, delete is added as
        a cleanup.
        """
        criteria = [{"username": "******"}]
        behaviors = [{
            'name': "behavior name",
            'parameters': {
                "behavior": "params"
            }
        }]

        _treq = get_fake_treq(
            self, 'POST',
            "/mimic/v1.1/IdentityControlAPI/behaviors/some_event",
            ((json.dumps({
                'criteria': criteria,
                'name': "sequence",
                'parameters': {
                    "behaviors": behaviors
                }
            }), ), self.expected_kwargs),
            (Response(201), '{"id": "behavior_id"}'))

        test_case = _get_fake_test_case(_treq, self.delete_treq)

        mimic_identity = MimicIdentity(pool=self.pool,
                                       test_case=test_case,
                                       treq=_treq)
        d = mimic_identity.sequenced_behaviors("/identity/v2.0",
                                               criteria,
                                               behaviors,
                                               event_description="some_event")
        self.assertEqual("behavior_id", self.successResultOf(d))

        self.assertEqual("successfully deleted behavior",
                         self.successResultOf(test_case.cleanup()))
예제 #12
0
    def test_update_node(self):
        """
        Update node calls the right endpoint, succeeds on 202, and retries
        on pending update for 60 seconds. It does not retry if the error is
        not PENDING_UPDATE.
        """
        main_treq_args = [
            'put', 'clburl/loadbalancers/12345/nodes/54321',
            (('{"node": {"weight": 5}}', ), self.expected_kwargs)
        ]

        def update(clb, clock):
            return clb.update_node(self.rcs, 54321, weight=5, clock=clock)

        self.assert_mutate_function_retries_until_success(
            update, main_treq_args, (Response(202), ""), "")

        self.assert_mutate_function_retries_until_timeout(
            update, main_treq_args, 60)

        self.assert_mutate_function_does_not_retry_if_not_pending_update(
            update, main_treq_args)
예제 #13
0
    def test_delete_node(self):
        """
        Deleting one or more nodes calls the right endpoint, succeeds on
        202, and retries on pending update for 60 seconds. It does not
        retry if the error is not PENDING_UPDATE.
        """
        self.expected_kwargs['params'] = [("id", 11111), ("id", 22222)]
        main_treq_args = [
            'delete', 'clburl/loadbalancers/12345/nodes',
            ((), self.expected_kwargs)
        ]

        def delete(clb, clock):
            return clb.delete_nodes(self.rcs, (11111, 22222), clock=clock)

        self.assert_mutate_function_retries_until_success(
            delete, main_treq_args, (Response(202), ""), "")

        self.assert_mutate_function_retries_until_timeout(
            delete, main_treq_args, 60)

        self.assert_mutate_function_does_not_retry_if_not_pending_update(
            delete, main_treq_args)
예제 #14
0
    def test_add_node(self):
        """
        Adding one or more nodes calls the right endpoint, succeeds on
        202, and retries on pending update for 60 seconds.  It does not
        retry if the error is not PENDING_UPDATE.
        """
        nodes_to_add = {
            "nodes": [{
                "address": "10.2.2.3",
                "port": 80,
                "condition": "ENABLED",
                "type": "PRIMARY"
            }, {
                "address": "10.2.2.4",
                "port": 81,
                "condition": "ENABLED",
                "type": "SECONDARY"
            }]
        }

        main_treq_args = [
            'post', 'clburl/loadbalancers/12345/nodes',
            ((json.dumps(nodes_to_add), ), self.expected_kwargs)
        ]

        def add(clb, clock):
            return clb.add_nodes(self.rcs, nodes_to_add["nodes"], clock=clock)

        self.assert_mutate_function_retries_until_success(
            add, main_treq_args, (Response(202), json.dumps(nodes_to_add)),
            nodes_to_add)

        self.assert_mutate_function_retries_until_timeout(
            add, main_treq_args, 60)

        self.assert_mutate_function_does_not_retry_if_not_pending_update(
            add, main_treq_args)
예제 #15
0
    def test_delete_clb_retries_until_success(self):
        """
        Deleting a CLB will retry until the CLB is deleted (or in error or
        suspended mode, in which case it will give up).
        """
        self.clb_id = 12345

        success_treqs = [
            # All of these particular immutable states count as success.
            self.get_fake_treq_for_delete(Response(
                200, strbody=json.dumps({"loadBalancer": {
                    "status": state
                }})),
                                          del_response=Response(400))
            for state in ("PENDING_DELETE", "DELETED", "ERROR", "SUSPENDED")
        ] + [
            # 404 from get-ting the server, meaning it's already gone.
            self.get_fake_treq_for_delete(Response(
                404,
                strbody=('{"message": "No such load balancer", "code": 404}')),
                                          del_response=Response(400))
        ]

        for success_treq in success_treqs:
            clock = Clock()
            _treq = self.get_fake_treq_for_delete(Response(
                200, strbody='{"loadBalancer": {"status": "PENDING_UPDATE"}}'),
                                                  del_response=Response(400))

            clb = CloudLoadBalancer(pool=self.pool, treq=_treq)
            clb.clb_id = self.clb_id

            d = clb.delete(self.rcs, clock=clock)

            self.assertNoResult(d)
            clock.pump([3])
            self.assertNoResult(d)

            clb.treq = success_treq
            clock.pump([3])
            self.assertEqual(self.successResultOf(d), None)
예제 #16
0
from otter.integration.lib.cloud_load_balancer import (CloudLoadBalancer,
                                                       ContainsAllIPs,
                                                       ExcludesAllIPs,
                                                       HasLength)
from otter.integration.lib.test_nova import Response, get_fake_treq
from otter.util.deferredutils import TimedOutError
from otter.util.http import UpstreamError, headers


class _FakeRCS(object):
    endpoints = {'loadbalancers': 'clburl'}
    token = "token"


pending_update_response = [
    Response(422),
    json.dumps({
        "message": ("Load Balancer '12345' has a status of "
                    "'PENDING_UPDATE' and is considered immutable."),
        "code":
        422
    })
]


class CLBTests(SynchronousTestCase):
    """
    Tests for the :class:`CloudLoadBalancer` API calls.
    """
    def setUp(self):
        """