Beispiel #1
0
 def test_equivalent_definition_and_eq(self):
     """
     If every attribute is the same, both :func:`CLBDescription.__eq__` and
     :func:`CLBDescription.equivalent_definition` will return `True`.
     """
     desc1 = CLBDescription(lb_id='12345', port=80)
     desc2 = CLBDescription(lb_id='12345', port=80)
     self.assertTrue(desc1.equivalent_definition(desc2))
     self.assertEquals(desc1, desc2)
Beispiel #2
0
 def test_neither_equivalent_definition_or_eq(self):
     """
     If ``lb_id`` and ``port`` are different, even if everything else are
     the same, neither :func:`CLBDescription.equivalent_definition` nor
     :func:`CLBDescription.__eq__` will return `True`.
     """
     desc1 = CLBDescription(lb_id='12345', port=80)
     desc2 = CLBDescription(lb_id='12345', port=8080)
     self.assertFalse(desc1.equivalent_definition(desc2))
     self.assertNotEqual(desc1, desc2)
Beispiel #3
0
 def test_only_eq_and_equivalent_definition_to_other_CLBDescriptions(self):
     """
     :func:`CLBDescriptionTest.__eq__` and
     :func:`CLBDescriptionTest.equivalent_definition` return false if
     compared to a non-:class:`CLBDescription` :class:`ILBDescription`
     provider.
     """
     desc = CLBDescription(lb_id='12345', port=80)
     fake = DummyLBDescription()
     self.assertNotEqual(desc, fake)
     self.assertFalse(desc.equivalent_definition(fake))
Beispiel #4
0
 def test_equivalent_definition_but_not_eq(self):
     """
     Even if weight, etc. are different, so long as the ``lb_id`` and ``port``
     are identical, :func:`CLBDescription.equivalent_definition` will
     return `True`.  :func:`CLBDescription.__eq__` will not, however.
     """
     desc1 = CLBDescription(lb_id='12345', port=80)
     desc2 = CLBDescription(lb_id='12345', port=80, weight=2,
                            condition=CLBNodeCondition.DISABLED,
                            type=CLBNodeType.SECONDARY)
     self.assertTrue(desc1.equivalent_definition(desc2))
     self.assertNotEqual(desc1, desc2)
Beispiel #5
0
 def test_inactive_if_node_is_disabled(self):
     """
     If the node is DRAINING, :func:`CLBNode.is_active` returns `True`.
     """
     node = CLBNode(node_id='1234',
                    description=CLBDescription(
                        lb_id='12345', port=80,
                        condition=CLBNodeCondition.DISABLED),
                    address='10.1.1.1', drained_at=0.0, connections=1)
     self.assertFalse(node.is_active())
Beispiel #6
0
    def test_add_nodes_to_clb(self):
        """
        :obj:`AddNodesToCLB` produces a request for adding any number of nodes
        to a cloud load balancer.
        """
        lb_id = "12345"
        lb_nodes = pset([
            ('1.2.3.4', CLBDescription(lb_id=lb_id, port=80)),
            ('1.2.3.4', CLBDescription(lb_id=lb_id, port=8080)),
            ('2.3.4.5', CLBDescription(lb_id=lb_id, port=80))
        ])
        step = AddNodesToCLB(lb_id=lb_id, address_configs=lb_nodes)
        eff = step.as_effect()

        self.assertEqual(
            eff.intent,
            service_request(
                ServiceType.CLOUD_LOAD_BALANCERS,
                'POST',
                "loadbalancers/12345/nodes",
                json_response=True,
                success_pred=ANY,
                data={"nodes": ANY}).intent)

        node_data = sorted(eff.intent.data['nodes'],
                           key=lambda n: (n['address'], n['port']))
        self.assertEqual(node_data, [
            {'address': '1.2.3.4',
             'port': 80,
             'condition': 'ENABLED',
             'type': 'PRIMARY',
             'weight': 1},
            {'address': '1.2.3.4',
             'port': 8080,
             'condition': 'ENABLED',
             'type': 'PRIMARY',
             'weight': 1},
            {'address': '2.3.4.5',
             'port': 80,
             'condition': 'ENABLED',
             'type': 'PRIMARY',
             'weight': 1}
        ])
Beispiel #7
0
    def test_success(self):
        """
        The data is returned as a tuple of ([NovaServer], [CLBNode/RCv3Node]).
        """
        clb_nodes = [
            CLBNode(node_id='node1',
                    address='ip1',
                    description=CLBDescription(lb_id='lb1', port=80))
        ]
        rcv3_nodes = [
            RCv3Node(node_id='node2',
                     cloud_server_id='a',
                     description=RCv3Description(lb_id='lb2'))
        ]

        eff = get_all_launch_server_data(
            'tid',
            'gid',
            self.now,
            get_scaling_group_servers=_constant_as_eff(
                ('tid', 'gid', self.now), self.servers),
            get_clb_contents=_constant_as_eff((), (clb_nodes, {
                'lb1': CLB(True),
                'lb2': CLB(False)
            })),
            get_rcv3_contents=_constant_as_eff((), rcv3_nodes))

        expected_servers = [
            server('a',
                   ServerState.ACTIVE,
                   servicenet_address='10.0.0.1',
                   links=freeze([{
                       'href': 'link1',
                       'rel': 'self'
                   }]),
                   json=freeze(self.servers[0])),
            server('b',
                   ServerState.ACTIVE,
                   created=1,
                   servicenet_address='10.0.0.2',
                   links=freeze([{
                       'href': 'link2',
                       'rel': 'self'
                   }]),
                   json=freeze(self.servers[1]))
        ]
        self.assertEqual(
            resolve_stubs(eff), {
                'servers': expected_servers,
                'lb_nodes': clb_nodes + rcv3_nodes,
                'lbs': {
                    'lb1': CLB(True),
                    'lb2': CLB(False)
                }
            })
Beispiel #8
0
    def test_mixed_optimization(self):
        """
        Mixes of optimizable and unoptimizable steps still get optimized
        correctly.
        """
        steps = pbag([
            # CLB adds
            AddNodesToCLB(lb_id='5',
                          address_configs=s(
                              ('1.1.1.1', CLBDescription(lb_id='5',
                                                         port=80)))),
            AddNodesToCLB(lb_id='5',
                          address_configs=s(
                              ('1.1.1.2', CLBDescription(lb_id='5',
                                                         port=80)))),
            AddNodesToCLB(lb_id='6',
                          address_configs=s(
                              ('1.1.1.1', CLBDescription(lb_id='6',
                                                         port=80)))),
            AddNodesToCLB(lb_id='6',
                          address_configs=s(
                              ('1.1.1.2', CLBDescription(lb_id='6',
                                                         port=80)))),
            RemoveNodesFromCLB(lb_id='5', node_ids=s('1')),
            RemoveNodesFromCLB(lb_id='5', node_ids=s('2')),
            RemoveNodesFromCLB(lb_id='6', node_ids=s('3')),
            RemoveNodesFromCLB(lb_id='6', node_ids=s('4')),

            # Unoptimizable steps
            CreateServer(server_config=pmap({})),
        ])

        self.assertEqual(
            optimize_steps(steps),
            pbag([
                # Optimized CLB adds
                AddNodesToCLB(
                    lb_id='5',
                    address_configs=s(
                        ('1.1.1.1', CLBDescription(lb_id='5', port=80)),
                        ('1.1.1.2', CLBDescription(lb_id='5', port=80)))),
                AddNodesToCLB(
                    lb_id='6',
                    address_configs=s(
                        ('1.1.1.1', CLBDescription(lb_id='6', port=80)),
                        ('1.1.1.2', CLBDescription(lb_id='6', port=80)))),
                RemoveNodesFromCLB(lb_id='5', node_ids=s('1', '2')),
                RemoveNodesFromCLB(lb_id='6', node_ids=s('3', '4')),

                # Unoptimizable steps
                CreateServer(server_config=pmap({}))
            ]))
Beispiel #9
0
 def test_mixed(self):
     """
     When there are multiple steps of same CLB then first step of each CLB
     is returned
     """
     steps = [
         CreateServer(server_config=pmap({"name": "server"})),
         DeleteServer(server_id="abc"),
         AddNodesToCLB(lb_id='5',
                       address_configs=s(
                           ('1.1.1.1', CLBDescription(lb_id='5',
                                                      port=80)))),
         RemoveNodesFromCLB(lb_id='5', node_ids=s('1')),
         RemoveNodesFromCLB(lb_id='6', node_ids=s('3')),
         AddNodesToCLB(lb_id='6',
                       address_configs=s(
                           ('2.1.1.1', CLBDescription(lb_id='6',
                                                      port=80)))),
         ChangeCLBNode(lb_id='7',
                       node_id='9',
                       condition=CLBNodeCondition.ENABLED,
                       weight=10,
                       type=CLBNodeType.PRIMARY),
         RemoveNodesFromCLB(lb_id='7', node_ids=s('4')),
         AddNodesToCLB(lb_id='7',
                       address_configs=s(
                           ('3.1.1.1', CLBDescription(lb_id='9',
                                                      port=80)))),
         ChangeCLBNode(lb_id='5',
                       node_id='11',
                       condition=CLBNodeCondition.ENABLED,
                       weight=10,
                       type=CLBNodeType.PRIMARY)
     ]
     self.assertEqual(
         list(one_clb_step(steps)),
         (
             steps[:3] +  # Non-CLB steps and 1 step for CLB 5
             [steps[4]] +  # One step for CLB 6
             [steps[6]])  # One step for CLB 7
     )
Beispiel #10
0
 def test_with_clb_and_rackconnect(self):
     """
     LB config with both CLBs and rackconnect.
     """
     self.assertEqual(
         json_to_LBConfigs(
             [{'loadBalancerId': 20, 'port': 80},
              {'loadBalancerId': 20, 'port': 800},
              {'loadBalancerId': 20, 'type': 'RackConnectV3'},
              {'loadBalancerId': 200, 'type': 'RackConnectV3'},
              {'loadBalancerId': 21, 'port': 81},
              {'loadBalancerId': 'cebdc220-172f-4b10-9f29-9c7e980ba41d',
               'type': 'RackConnectV3'}]),
         pset([
             CLBDescription(lb_id='20', port=80),
             CLBDescription(lb_id='20', port=800),
             CLBDescription(lb_id='21', port=81),
             RCv3Description(lb_id='20'),
             RCv3Description(lb_id='200'),
             RCv3Description(lb_id='cebdc220-172f-4b10-9f29-9c7e980ba41d')
         ]))
Beispiel #11
0
 def test_optimize_clb_adds(self):
     """
     Multiple :class:`AddNodesToCLB` steps for the same LB
     are merged into one.
     """
     steps = pbag([
         AddNodesToCLB(lb_id='5',
                       address_configs=s(
                           ('1.1.1.1', CLBDescription(lb_id='5',
                                                      port=80)))),
         AddNodesToCLB(lb_id='5',
                       address_configs=s(
                           ('1.2.3.4', CLBDescription(lb_id='5', port=80))))
     ])
     self.assertEqual(
         optimize_steps(steps),
         pbag([
             AddNodesToCLB(
                 lb_id='5',
                 address_configs=s(
                     ('1.1.1.1', CLBDescription(lb_id='5', port=80)),
                     ('1.2.3.4', CLBDescription(lb_id='5', port=80))))
         ]))
Beispiel #12
0
def json_to_LBConfigs(lbs_json):
    """
    Convert load balancer config from JSON to :obj:`CLBDescription`

    :param lbs_json: Sequence of load balancer configs
    :return: Sequence of :class:`ILBDescription` providers
    """
    by_type = groupby(lambda lb: lb.get('type', 'CloudLoadBalancer'), lbs_json)
    return pset(
        [CLBDescription(lb_id=str(lb['loadBalancerId']), port=lb['port'])
         for lb in by_type.get('CloudLoadBalancer', [])] +
        [RCv3Description(lb_id=str(lb['loadBalancerId']))
         for lb in by_type.get('RackConnectV3', [])]
    )
Beispiel #13
0
    def test_with_lb_metadata(self):
        """
        Create a server that has load balancer config metadata in it.
        The only desired load balancers created are the ones with valid
        data.
        """
        self.servers[0]['metadata'] = {
            # correct clb config
            'rax:autoscale:lb:CloudLoadBalancer:1':
            '[{"port":80},{"port":90}]',

            # invalid because there is no port
            "rax:autoscale:lb:CloudLoadBalancer:2": '[{}]',
            # two correct lbconfigs and one incorrect one
            'rax:autoscale:lb:CloudLoadBalancer:3':
            '[{"port":80},{"bad":"1"},{"port":90}]',
            # a dictionary instead of a list
            'rax:autoscale:lb:CloudLoadBalancer:4': '{"port": 80}',
            # not even valid json
            'rax:autoscale:lb:CloudLoadBalancer:5': 'invalid json string',
            # RCv3 with same LB id as CLB
            'rax:autoscale:lb:RackConnectV3:1': ''
        }
        self.assertEqual(
            NovaServer.from_server_details_json(self.servers[0]),
            NovaServer(id='a',
                       state=ServerState.ACTIVE,
                       image_id='valid_image',
                       flavor_id='valid_flavor',
                       created=self.createds[0],
                       desired_lbs=pset([
                           CLBDescription(lb_id='1', port=80),
                           CLBDescription(lb_id='1', port=90),
                           RCv3Description(lb_id='1')]),
                       servicenet_address='',
                       links=freeze(self.servers[0]['links']),
                       json=freeze(self.servers[0])))
Beispiel #14
0
    def test_generate_metadata(self):
        """
        :func:`NovaServer.lbs_from_metadata` produces a dictionary with
        metadata for the group ID and any load balancers provided.
        """
        lbs = [
            CLBDescription(port=80, lb_id='123'),
            RCv3Description(lb_id='123'),
            CLBDescription(port=8080, lb_id='123'),
            CLBDescription(port=80, lb_id='234'),
            RCv3Description(lb_id='3232'),
        ]
        expected = {
            'rax:autoscale:group:id': 'group_id',
            'rax:auto_scaling_group_id': 'group_id',
            'rax:autoscale:lb:CloudLoadBalancer:123': (
                '[{"port": 80}, {"port": 8080}]'),
            'rax:autoscale:lb:CloudLoadBalancer:234': '[{"port": 80}]',
            'rax:autoscale:lb:RackConnectV3:123': '',
            'rax:autoscale:lb:RackConnectV3:3232': '',
        }

        self.assertEqual(generate_metadata('group_id', lbs),
                         expected)
Beispiel #15
0
 def test_from_node_json_no_weight(self):
     """
     A node's JSON representation can be parsed to a :obj:`CLBNode` object
     with a `CLBDescription`. When weight is not specified it defaults to 1.
     """
     node_json = {'id': 'node1', 'address': '1.2.3.4', 'port': 20,
                  'condition': 'DRAINING', 'type': 'SECONDARY'}
     node = CLBNode.from_node_json(123, node_json)
     self.assertEqual(
         node,
         CLBNode(node_id='node1', address='1.2.3.4',
                 connections=None, drained_at=0.0,
                 description=CLBDescription(
                     lb_id='123', port=20,
                     weight=1,
                     condition=CLBNodeCondition.DRAINING,
                     type=CLBNodeType.SECONDARY)))
Beispiel #16
0
 def test_optimize_leaves_other_steps(self):
     """
     Unoptimizable steps pass the optimizer unchanged.
     """
     steps = pbag([
         AddNodesToCLB(lb_id='5',
                       address_configs=s(
                           ('1.1.1.1', CLBDescription(lb_id='5',
                                                      port=80)))),
         RemoveNodesFromCLB(lb_id='6', node_ids=s('1')),
         CreateServer(server_config=pmap({})),
         BulkRemoveFromRCv3(lb_node_pairs=pset([("lb-1", "node-a")])),
         BulkAddToRCv3(lb_node_pairs=pset([("lb-2", "node-b")]))
         # Note that the add & remove pair should not be the same;
         # the optimizer might reasonably optimize opposite
         # operations away in the future.
     ])
     self.assertEqual(optimize_steps(steps), steps)
Beispiel #17
0
 def test_clb_adds_multiple_load_balancers(self):
     """
     Aggregation is done on a per-load-balancer basis when adding to a CLB.
     """
     steps = pbag([
         AddNodesToCLB(lb_id='5',
                       address_configs=s(
                           ('1.1.1.1', CLBDescription(lb_id='5',
                                                      port=80)))),
         AddNodesToCLB(lb_id='5',
                       address_configs=s(
                           ('1.1.1.2', CLBDescription(lb_id='5',
                                                      port=80)))),
         AddNodesToCLB(lb_id='6',
                       address_configs=s(
                           ('1.1.1.1', CLBDescription(lb_id='6',
                                                      port=80)))),
         AddNodesToCLB(lb_id='6',
                       address_configs=s(
                           ('1.1.1.2', CLBDescription(lb_id='6',
                                                      port=80)))),
     ])
     self.assertEqual(
         optimize_steps(steps),
         pbag([
             AddNodesToCLB(
                 lb_id='5',
                 address_configs=s(
                     ('1.1.1.1', CLBDescription(lb_id='5', port=80)),
                     ('1.1.1.2', CLBDescription(lb_id='5', port=80)))),
             AddNodesToCLB(
                 lb_id='6',
                 address_configs=s(
                     ('1.1.1.1', CLBDescription(lb_id='6', port=80)),
                     ('1.1.1.2', CLBDescription(lb_id='6', port=80)))),
         ]))
Beispiel #18
0
class CLBNodeTests(SynchronousTestCase):
    """
    Tests for :class:`CLBNode`.
    """
    desc = CLBDescription(lb_id='12345', port=80)
    drain_desc = CLBDescription(lb_id='12345', port=80,
                                condition=CLBNodeCondition.DRAINING)

    def test_provides_ILBDescription_and_IDrainable(self):
        """
        An instance of :class:`CLBNode` provides :class:`ILBNode` and
        :class:`IDrainable`.
        """
        node = CLBNode(node_id='1234', description=self.desc, address='10.1.1.1')
        self.assertTrue(ILBNode.providedBy(node))
        self.assertTrue(IDrainable.providedBy(node))

    def test_matches_only_works_with_NovaServers(self):
        """
        :func:`CLBNode.matches` returns false if the server is not an instance
        of :class:`NovaServer`.
        """
        node = CLBNode(node_id='1234', description=self.desc,
                       address='10.1.1.1')
        self.assertFalse(
            node.matches(DummyServer(servicenet_address="10.1.1.1")))

    def test_matches_only_if_server_address_matches_node_address(self):
        """
        :func:`CLBNode.matches` returns True only if the :class:`NovaServer`
        has the same ServiceNet address as the node address
        """
        node = CLBNode(node_id='1234', description=self.desc,
                       address='10.1.1.1')
        self.assertFalse(node.matches(
            NovaServer(id='1', state=ServerState.ACTIVE, created=0.0,
                       servicenet_address="10.1.1.2",
                       image_id='image', flavor_id='flavor')))
        self.assertTrue(node.matches(
            NovaServer(id='1', state=ServerState.ACTIVE, created=0.0,
                       servicenet_address="10.1.1.1",
                       image_id='image', flavor_id='flavor')))

    def test_current_draining_true_if_description_is_draining(self):
        """
        :func:`CLBNode.currently_draining` returns `True` if
        `CLBNode.description.condition` is :obj:`CLBNodeCondition.DRAINING`
        """
        node = CLBNode(node_id='1234', description=self.drain_desc,
                       address='10.1.1.1')
        self.assertTrue(node.currently_draining())

    def test_current_draining_false_if_description_not_draining(self):
        """
        :func:`CLBNode.currently_draining` returns `False` if
        `CLBNode.description.condition` is not :obj:`CLBNodeCondition.DRAINING`
        """
        node = CLBNode(node_id='1234', description=self.desc, address='10.1.1.1')
        self.assertFalse(node.currently_draining())

    def test_done_draining_past_timeout_even_if_there_are_connections(self):
        """
        If there are still connections, but the node has been in draining past
        the timeout, :func:`CLBNode.is_done_draining` returns `True`.
        """
        node = CLBNode(node_id='1234', description=self.drain_desc,
                       address='10.1.1.1', drained_at=0.0, connections=1)
        self.assertTrue(node.is_done_draining(now=30, timeout=15))

    def test_done_draining_past_timeout_even_if_no_connection_info(self):
        """
        If connection information is not provided, and the node has been in
        draining past the timeout, :func:`CLBNode.is_done_draining` returns
        `True`.
        """
        node = CLBNode(node_id='1234', description=self.drain_desc,
                       address='10.1.1.1', drained_at=0.0)
        self.assertTrue(node.is_done_draining(now=30, timeout=15))

    def test_done_draining_before_timeout_if_there_are_no_connections(self):
        """
        If there are zero connections, but the node has been in draining less
        than the timeout, :func:`CLBNode.is_done_draining` returns `True`.
        """
        node = CLBNode(node_id='1234', description=self.drain_desc,
                       address='10.1.1.1', drained_at=0.0, connections=0)
        self.assertTrue(node.is_done_draining(now=15, timeout=30))

    def test_not_done_draining_before_timeout_if_no_connection_info(self):
        """
        If connection information is not provided, and the node has been in
        draining less than the timeout, :func:`CLBNode.is_done_draining`
        returns `False`.
        """
        node = CLBNode(node_id='1234', description=self.drain_desc,
                       address='10.1.1.1', drained_at=0.0)
        self.assertFalse(node.is_done_draining(now=15, timeout=30))

    def test_active_if_node_is_enabled(self):
        """
        If the node is ENABLED, :func:`CLBNode.is_active` returns `True`.
        """
        node = CLBNode(node_id='1234', description=self.desc,
                       address='10.1.1.1', drained_at=0.0, connections=1)
        self.assertTrue(node.is_active())

    def test_active_if_node_is_draining(self):
        """
        If the node is DRAINING, :func:`CLBNode.is_active` returns `True`.
        """
        node = CLBNode(node_id='1234', description=self.drain_desc,
                       address='10.1.1.1', drained_at=0.0, connections=1)
        self.assertTrue(node.is_active())

    def test_inactive_if_node_is_disabled(self):
        """
        If the node is DRAINING, :func:`CLBNode.is_active` returns `True`.
        """
        node = CLBNode(node_id='1234',
                       description=CLBDescription(
                           lb_id='12345', port=80,
                           condition=CLBNodeCondition.DISABLED),
                       address='10.1.1.1', drained_at=0.0, connections=1)
        self.assertFalse(node.is_active())

    def test_from_node_json_no_weight(self):
        """
        A node's JSON representation can be parsed to a :obj:`CLBNode` object
        with a `CLBDescription`. When weight is not specified it defaults to 1.
        """
        node_json = {'id': 'node1', 'address': '1.2.3.4', 'port': 20,
                     'condition': 'DRAINING', 'type': 'SECONDARY'}
        node = CLBNode.from_node_json(123, node_json)
        self.assertEqual(
            node,
            CLBNode(node_id='node1', address='1.2.3.4',
                    connections=None, drained_at=0.0,
                    description=CLBDescription(
                        lb_id='123', port=20,
                        weight=1,
                        condition=CLBNodeCondition.DRAINING,
                        type=CLBNodeType.SECONDARY)))

    def test_from_node_json_with_weight(self):
        """
        A node's JSON representation can be parsed to a :obj:`CLBNode` object
        with a `CLBDescription`. When weight is not specified it defaults to 1.
        """
        node_json = {'id': 'node1', 'address': '1.2.3.4', 'port': 20,
                     'condition': 'DRAINING', 'type': 'SECONDARY',
                     'weight': 50}
        node = CLBNode.from_node_json(123, node_json)
        self.assertEqual(
            node,
            CLBNode(node_id='node1', address='1.2.3.4',
                    connections=None, drained_at=0.0,
                    description=CLBDescription(
                        lb_id='123', port=20,
                        weight=50,
                        condition=CLBNodeCondition.DRAINING,
                        type=CLBNodeType.SECONDARY)))
Beispiel #19
0
def _clbd(lbid, port):
    return CLBDescription(lb_id=lbid, port=port)
Beispiel #20
0
 def test_provides_ILBDescription(self):
     """
     An instance of :class:`CLBDescription` provides :class:`ILBDescription`.
     """
     desc = CLBDescription(lb_id='12345', port=80)
     self.assertTrue(ILBDescription.providedBy(desc))