Browse Source

Merge "Add support for Member Batch Update"

tags/6.0.0.0b1
Zuul 3 months ago
parent
commit
8c4136be03

+ 105
- 2
networking_ovn/octavia/ovn_driver.py View File

@@ -1018,7 +1018,7 @@ class OvnProviderHelper(object):
1018 1018
                      'provisioning_status': constants.ACTIVE})
1019 1019
             status['listeners'] = listener_status
1020 1020
         except Exception:
1021
-            LOG.exception('Exception during pool delete')
1021
+            LOG.exception('Exception during pool update')
1022 1022
             status = {
1023 1023
                 'pools': [{"id": pool['id'],
1024 1024
                            'provisioning_status': constants.ERROR}],
@@ -1192,6 +1192,52 @@ class OvnProviderHelper(object):
1192 1192
                                    'provisioning_status': constants.ACTIVE}]}
1193 1193
         return status
1194 1194
 
1195
+    def _get_existing_pool_members(self, pool_id):
1196
+        pool_key = self._get_pool_key(pool_id)
1197
+        ovn_lb = self._find_ovn_lb_with_pool_key(pool_key)
1198
+        if not ovn_lb:
1199
+            pool_key = self._get_pool_key(pool_id, is_enabled=False)
1200
+            ovn_lb = self._find_ovn_lb_with_pool_key(pool_key)
1201
+            if not ovn_lb:
1202
+                msg = _("Loadbalancer with pool %s does not exist") % pool_key
1203
+                raise driver_exceptions.DriverError(msg)
1204
+        external_ids = dict(ovn_lb.external_ids)
1205
+        return external_ids[pool_key]
1206
+
1207
+    def get_pool_member_id(self, pool_id, mem_addr_port=None):
1208
+        '''Gets Member information
1209
+
1210
+        :param pool_id: ID of the Pool whose member information is reqd.
1211
+        :param mem_addr_port: Combination of Member Address+Port. Default=None
1212
+        :returns: UUID -- ID of the Member if member exists in pool.
1213
+        :returns: None -- if no member exists in the pool
1214
+        :raises: Exception if Loadbalancer is not found for a Pool ID
1215
+        '''
1216
+        existing_members = self._get_existing_pool_members(pool_id)
1217
+        # Members are saved in OVN in the form of
1218
+        # member1_UUID_IP:Port, member2_UUID_IP:Port
1219
+        # Match the IP:Port for all members with the mem_addr_port
1220
+        # information and return the UUID.
1221
+        for meminf in existing_members.split(','):
1222
+            if mem_addr_port == meminf.split('_')[2]:
1223
+                return meminf.split('_')[1]
1224
+
1225
+    def get_member_info(self, pool_id):
1226
+        '''Gets Member information
1227
+
1228
+        :param pool_id: ID of the Pool whose member information is reqd.
1229
+        :param mem_addr_port: Combination of Member Address+Port. Default=None
1230
+        :returns: List -- List of Member Address+Pool of all members in pool.
1231
+        :returns:[None] -- if no member exists in the pool.
1232
+        :raises: Exception if Loadbalancer is not found for a Pool ID
1233
+        '''
1234
+        existing_members = self._get_existing_pool_members(pool_id)
1235
+        # Members are saved in OVN in the form of
1236
+        # member1_UUID_IP:Port, member2_UUID_IP:Port
1237
+        # Return the list of (UUID,IP:Port) for all members.
1238
+        return [(meminf.split('_')[1], meminf.split(
1239
+            '_')[2]) for meminf in existing_members.split(',')]
1240
+
1195 1241
 
1196 1242
 class OvnProviderDriver(driver_base.ProviderDriver):
1197 1243
     def __init__(self):
@@ -1375,5 +1421,62 @@ class OvnProviderDriver(driver_base.ProviderDriver):
1375 1421
         self._ovn_helper.add_request(request)
1376 1422
 
1377 1423
     def member_batch_update(self, members):
1424
+        # Note(rbanerje): all members belong to the same pool.
1425
+        request_list = []
1426
+        skipped_members = []
1427
+        pool_id = None
1428
+        try:
1429
+            pool_id = members[0].pool_id
1430
+        except IndexError:
1431
+            msg = (_('No member information has been passed'))
1432
+            raise driver_exceptions.UnsupportedOptionError(
1433
+                user_fault_string=msg,
1434
+                operator_fault_string=msg)
1435
+        except AttributeError:
1436
+            msg = (_('Member does not have proper pool information'))
1437
+            raise driver_exceptions.UnsupportedOptionError(
1438
+                user_fault_string=msg,
1439
+                operator_fault_string=msg)
1440
+        current_members = self._ovn_helper.get_member_info(pool_id)
1441
+        # current_members gets a list of tuples (ID, IP:Port) for pool members
1378 1442
         for member in members:
1379
-            self.member_update(member, member)
1443
+            if member.monitor_address or member.monitor_port:
1444
+                skipped_members.append(member.member_id)
1445
+                continue
1446
+            admin_state_up = member.admin_state_up
1447
+            if isinstance(admin_state_up, o_datamodels.UnsetType):
1448
+                admin_state_up = True
1449
+            mem_addr_port = str(member.address) + ':' + str(
1450
+                member.protocol_port)
1451
+            if (member.member_id, mem_addr_port) not in current_members:
1452
+                req_type = REQ_TYPE_MEMBER_CREATE
1453
+            else:
1454
+                # If member exists in pool, then Update
1455
+                req_type = REQ_TYPE_MEMBER_UPDATE
1456
+                current_members.remove((member.member_id, mem_addr_port))
1457
+                # Remove all updating members so only deleted ones are left
1458
+            request_info = {'id': member.member_id,
1459
+                            'address': member.address,
1460
+                            'protocol_port': member.protocol_port,
1461
+                            'pool_id': member.pool_id,
1462
+                            'subnet_id': member.subnet_id,
1463
+                            'admin_state_up': admin_state_up}
1464
+            request = {'type': req_type,
1465
+                       'info': request_info}
1466
+            request_list.append(request)
1467
+        for cmember in current_members:
1468
+            request_info = {'id': cmember[0],
1469
+                            'address': cmember[1].split(':')[0],
1470
+                            'protocol_port': cmember[1].split(':')[1],
1471
+                            'pool_id': pool_id}
1472
+            request = {'type': REQ_TYPE_MEMBER_DELETE,
1473
+                       'info': request_info}
1474
+            request_list.append(request)
1475
+        for request in request_list:
1476
+            self._ovn_helper.add_request(request)
1477
+        if skipped_members:
1478
+            msg = (_('OVN provider does not support monitor options, '
1479
+                     'so following members skipped: %s') % skipped_members)
1480
+            raise driver_exceptions.UnsupportedOptionError(
1481
+                user_fault_string=msg,
1482
+                operator_fault_string=msg)

+ 56
- 20
networking_ovn/tests/functional/octavia/test_ovn_driver.py View File

@@ -195,7 +195,8 @@ class TestOctaviaOvnProviderDriver(base.TestOVNFunctionalBase):
195 195
             else:
196 196
                 del lb_data[ovn_driver.LB_EXT_IDS_LS_REFS_KEY][net_id]
197 197
 
198
-    def _wait_for_status_and_validate(self, lb_data, expected_status):
198
+    def _wait_for_status_and_validate(self, lb_data, expected_status,
199
+                                      check_call=True):
199 200
         call_count = len(expected_status)
200 201
         expected_calls = [mock.call(status) for status in expected_status]
201 202
         update_loadbalancer_status = (
@@ -203,12 +204,13 @@ class TestOctaviaOvnProviderDriver(base.TestOVNFunctionalBase):
203 204
         n_utils.wait_until_true(
204 205
             lambda: update_loadbalancer_status.call_count == call_count,
205 206
             timeout=10)
206
-        try:
207
-            self._o_driver_lib.update_loadbalancer_status.assert_has_calls(
208
-                expected_calls, any_order=True)
209
-        except Exception:
210
-            raise Exception(
211
-                self._o_driver_lib.update_loadbalancer_status.mock_calls)
207
+        if check_call:
208
+            try:
209
+                self._o_driver_lib.update_loadbalancer_status.assert_has_calls(
210
+                    expected_calls, any_order=True)
211
+            except Exception:
212
+                raise Exception(
213
+                    self._o_driver_lib.update_loadbalancer_status.mock_calls)
212 214
         expected_lbs = self._make_expected_lbs(lb_data)
213 215
         self._validate_loadbalancers(expected_lbs)
214 216
 
@@ -545,14 +547,12 @@ class TestOctaviaOvnProviderDriver(base.TestOVNFunctionalBase):
545 547
         self._wait_for_status_and_validate(lb_data, [expected_status])
546 548
 
547 549
     def _update_members_in_batch_and_validate(self, lb_data, pool_id,
548
-                                              member_addresses):
550
+                                              members):
549 551
         pool = self._get_pool_from_lb_data(lb_data, pool_id=pool_id)
550
-
551
-        members = []
552 552
         expected_status = []
553
-        for addr in member_addresses:
554
-            member = self._get_pool_member(pool, addr)
555
-            members.append(member)
553
+        self._o_driver_lib.update_loadbalancer_status.reset_mock()
554
+        self.ovn_driver.member_batch_update(members)
555
+        for member in members:
556 556
             expected_status.append(
557 557
                 {'pools': [{'id': pool.pool_id,
558 558
                            'provisioning_status': 'ACTIVE'}],
@@ -562,9 +562,23 @@ class TestOctaviaOvnProviderDriver(base.TestOVNFunctionalBase):
562 562
                  'loadbalancers': [{'id': pool.loadbalancer_id,
563 563
                                    'provisioning_status': 'ACTIVE'}],
564 564
                  'listeners': []})
565
-        self._o_driver_lib.update_loadbalancer_status.reset_mock()
566
-        self.ovn_driver.member_batch_update(members)
567
-        self._wait_for_status_and_validate(lb_data, expected_status)
565
+        for m in pool.members:
566
+            found = False
567
+            for member in members:
568
+                if member.member_id == m.member_id:
569
+                    found = True
570
+                    break
571
+            if not found:
572
+                expected_status.append(
573
+                    {'pools': [{'id': pool.pool_id,
574
+                                'provisioning_status': 'ACTIVE'}],
575
+                     'members': [{'id': m.member_id,
576
+                                  'provisioning_status': 'DELETED'}],
577
+                     'loadbalancers': [{'id': pool.loadbalancer_id,
578
+                                        'provisioning_status': 'ACTIVE'}],
579
+                     'listeners': []})
580
+        self._wait_for_status_and_validate(lb_data, expected_status,
581
+                                           check_call=False)
568 582
 
569 583
     def _delete_member_and_validate(self, lb_data, pool_id, network_id,
570 584
                                     member_address):
@@ -707,10 +721,6 @@ class TestOctaviaOvnProviderDriver(base.TestOVNFunctionalBase):
707 721
         # Enable loadbalancer back
708 722
         self._update_load_balancer_and_validate(lb_data,
709 723
                                                 admin_state_up=True)
710
-        # Test member_batch_update
711
-        self._update_members_in_batch_and_validate(lb_data, pool_id,
712
-                                                   ['10.0.0.10', '10.0.0.11'])
713
-
714 724
         self._delete_member_and_validate(lb_data, pool_id,
715 725
                                          lb_data['vip_net_info'][0],
716 726
                                          '10.0.0.10')
@@ -829,3 +839,29 @@ class TestOctaviaOvnProviderDriver(base.TestOVNFunctionalBase):
829 839
                                        lb_data['listeners'][0].listener_id)
830 840
         self._delete_listener_and_validate(lb_data)
831 841
         self._delete_load_balancer_and_validate(lb_data)
842
+
843
+    def test_lb_member_batch_update(self):
844
+        # Create a LoadBalancer
845
+        lb_data = self._create_load_balancer_and_validate(
846
+            {'vip_network': 'vip_network',
847
+             'cidr': '10.0.0.0/24'})
848
+        # Create a pool
849
+        self._create_pool_and_validate(lb_data, "p1")
850
+        pool_id = lb_data['pools'][0].pool_id
851
+        # Create Member-1 and associate it with lb_data
852
+        self._create_member_and_validate(
853
+            lb_data, pool_id, lb_data['vip_net_info'][1],
854
+            lb_data['vip_net_info'][0], '10.0.0.10')
855
+        # Create Member-2
856
+        m_member = self._create_member_model(pool_id,
857
+                                             lb_data['vip_net_info'][1],
858
+                                             '10.0.0.12')
859
+        # Update ovn's Logical switch reference
860
+        self._update_ls_refs(lb_data, lb_data['vip_net_info'][0])
861
+        lb_data['pools'][0].members.append(m_member)
862
+        # Add a new member to the LB
863
+        members = [m_member] + [lb_data['pools'][0].members[0]]
864
+        self._update_members_in_batch_and_validate(lb_data, pool_id, members)
865
+        # Deleting one member, while keeping the other member available
866
+        self._update_members_in_batch_and_validate(lb_data, pool_id,
867
+                                                   [m_member])

Loading…
Cancel
Save