summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.openstack.org>2019-02-22 12:07:13 +0000
committerGerrit Code Review <review@openstack.org>2019-02-22 12:07:13 +0000
commit8c4136be0356629309be58a6927dde7b84d215ba (patch)
treebcb76f76bcba6e159aa75ebdc5e68134c347c50f
parentf0843c198890fdc811a5bd8eee716769609b4b9f (diff)
parente89a8a25633117c2bed2fd87a2451a571d33cb62 (diff)
Merge "Add support for Member Batch Update"HEADmaster
-rw-r--r--networking_ovn/octavia/ovn_driver.py107
-rw-r--r--networking_ovn/tests/functional/octavia/test_ovn_driver.py76
2 files changed, 161 insertions, 22 deletions
diff --git a/networking_ovn/octavia/ovn_driver.py b/networking_ovn/octavia/ovn_driver.py
index 4fbd5a4..b9a5a9f 100644
--- a/networking_ovn/octavia/ovn_driver.py
+++ b/networking_ovn/octavia/ovn_driver.py
@@ -1018,7 +1018,7 @@ class OvnProviderHelper(object):
1018 'provisioning_status': constants.ACTIVE}) 1018 'provisioning_status': constants.ACTIVE})
1019 status['listeners'] = listener_status 1019 status['listeners'] = listener_status
1020 except Exception: 1020 except Exception:
1021 LOG.exception('Exception during pool delete') 1021 LOG.exception('Exception during pool update')
1022 status = { 1022 status = {
1023 'pools': [{"id": pool['id'], 1023 'pools': [{"id": pool['id'],
1024 'provisioning_status': constants.ERROR}], 1024 'provisioning_status': constants.ERROR}],
@@ -1192,6 +1192,52 @@ class OvnProviderHelper(object):
1192 'provisioning_status': constants.ACTIVE}]} 1192 'provisioning_status': constants.ACTIVE}]}
1193 return status 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
1196class OvnProviderDriver(driver_base.ProviderDriver): 1242class OvnProviderDriver(driver_base.ProviderDriver):
1197 def __init__(self): 1243 def __init__(self):
@@ -1375,5 +1421,62 @@ class OvnProviderDriver(driver_base.ProviderDriver):
1375 self._ovn_helper.add_request(request) 1421 self._ovn_helper.add_request(request)
1376 1422
1377 def member_batch_update(self, members): 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 for member in members: 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)
diff --git a/networking_ovn/tests/functional/octavia/test_ovn_driver.py b/networking_ovn/tests/functional/octavia/test_ovn_driver.py
index e7291af..d824fcd 100644
--- a/networking_ovn/tests/functional/octavia/test_ovn_driver.py
+++ b/networking_ovn/tests/functional/octavia/test_ovn_driver.py
@@ -195,7 +195,8 @@ class TestOctaviaOvnProviderDriver(base.TestOVNFunctionalBase):
195 else: 195 else:
196 del lb_data[ovn_driver.LB_EXT_IDS_LS_REFS_KEY][net_id] 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 call_count = len(expected_status) 200 call_count = len(expected_status)
200 expected_calls = [mock.call(status) for status in expected_status] 201 expected_calls = [mock.call(status) for status in expected_status]
201 update_loadbalancer_status = ( 202 update_loadbalancer_status = (
@@ -203,12 +204,13 @@ class TestOctaviaOvnProviderDriver(base.TestOVNFunctionalBase):
203 n_utils.wait_until_true( 204 n_utils.wait_until_true(
204 lambda: update_loadbalancer_status.call_count == call_count, 205 lambda: update_loadbalancer_status.call_count == call_count,
205 timeout=10) 206 timeout=10)
206 try: 207 if check_call:
207 self._o_driver_lib.update_loadbalancer_status.assert_has_calls( 208 try:
208 expected_calls, any_order=True) 209 self._o_driver_lib.update_loadbalancer_status.assert_has_calls(
209 except Exception: 210 expected_calls, any_order=True)
210 raise Exception( 211 except Exception:
211 self._o_driver_lib.update_loadbalancer_status.mock_calls) 212 raise Exception(
213 self._o_driver_lib.update_loadbalancer_status.mock_calls)
212 expected_lbs = self._make_expected_lbs(lb_data) 214 expected_lbs = self._make_expected_lbs(lb_data)
213 self._validate_loadbalancers(expected_lbs) 215 self._validate_loadbalancers(expected_lbs)
214 216
@@ -545,14 +547,12 @@ class TestOctaviaOvnProviderDriver(base.TestOVNFunctionalBase):
545 self._wait_for_status_and_validate(lb_data, [expected_status]) 547 self._wait_for_status_and_validate(lb_data, [expected_status])
546 548
547 def _update_members_in_batch_and_validate(self, lb_data, pool_id, 549 def _update_members_in_batch_and_validate(self, lb_data, pool_id,
548 member_addresses): 550 members):
549 pool = self._get_pool_from_lb_data(lb_data, pool_id=pool_id) 551 pool = self._get_pool_from_lb_data(lb_data, pool_id=pool_id)
550
551 members = []
552 expected_status = [] 552 expected_status = []
553 for addr in member_addresses: 553 self._o_driver_lib.update_loadbalancer_status.reset_mock()
554 member = self._get_pool_member(pool, addr) 554 self.ovn_driver.member_batch_update(members)
555 members.append(member) 555 for member in members:
556 expected_status.append( 556 expected_status.append(
557 {'pools': [{'id': pool.pool_id, 557 {'pools': [{'id': pool.pool_id,
558 'provisioning_status': 'ACTIVE'}], 558 'provisioning_status': 'ACTIVE'}],
@@ -562,9 +562,23 @@ class TestOctaviaOvnProviderDriver(base.TestOVNFunctionalBase):
562 'loadbalancers': [{'id': pool.loadbalancer_id, 562 'loadbalancers': [{'id': pool.loadbalancer_id,
563 'provisioning_status': 'ACTIVE'}], 563 'provisioning_status': 'ACTIVE'}],
564 'listeners': []}) 564 'listeners': []})
565 self._o_driver_lib.update_loadbalancer_status.reset_mock() 565 for m in pool.members:
566 self.ovn_driver.member_batch_update(members) 566 found = False
567 self._wait_for_status_and_validate(lb_data, expected_status) 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 def _delete_member_and_validate(self, lb_data, pool_id, network_id, 583 def _delete_member_and_validate(self, lb_data, pool_id, network_id,
570 member_address): 584 member_address):
@@ -707,10 +721,6 @@ class TestOctaviaOvnProviderDriver(base.TestOVNFunctionalBase):
707 # Enable loadbalancer back 721 # Enable loadbalancer back
708 self._update_load_balancer_and_validate(lb_data, 722 self._update_load_balancer_and_validate(lb_data,
709 admin_state_up=True) 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 self._delete_member_and_validate(lb_data, pool_id, 724 self._delete_member_and_validate(lb_data, pool_id,
715 lb_data['vip_net_info'][0], 725 lb_data['vip_net_info'][0],
716 '10.0.0.10') 726 '10.0.0.10')
@@ -829,3 +839,29 @@ class TestOctaviaOvnProviderDriver(base.TestOVNFunctionalBase):
829 lb_data['listeners'][0].listener_id) 839 lb_data['listeners'][0].listener_id)
830 self._delete_listener_and_validate(lb_data) 840 self._delete_listener_and_validate(lb_data)
831 self._delete_load_balancer_and_validate(lb_data) 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])