Add support for SOURCE_IP session persistence
This patch adds support to configure ovn loadbalancer affinity_timeout option based on the pool session persistence timeout. Change-Id: I07c8f3492e62576f66008e8ea1ef9846bed8c6fa
This commit is contained in:
parent
20997b185f
commit
382ddb0329
@ -112,3 +112,7 @@ HM_EVENT_MEMBER_PORT_OFFLINE = ['offline']
|
||||
|
||||
# max timeout for request
|
||||
MAX_TIMEOUT_REQUEST = 5
|
||||
|
||||
AFFINITY_TIMEOUT = "affinity_timeout"
|
||||
# This driver only supports SOURCE_IP sesssion persistency option
|
||||
OVN_NATIVE_SESSION_PERSISTENCE = [constants.SESSION_PERSISTENCE_SOURCE_IP]
|
||||
|
@ -68,6 +68,16 @@ class OvnProviderDriver(driver_base.ProviderDriver):
|
||||
user_fault_string=msg,
|
||||
operator_fault_string=msg)
|
||||
|
||||
def _check_for_supported_session_persistence(self, session):
|
||||
if (session and
|
||||
session.get("type") not in
|
||||
ovn_const.OVN_NATIVE_SESSION_PERSISTENCE):
|
||||
msg = _('OVN provider does not support %s session persistence. '
|
||||
'Only SOURCE_IP type is supported.') % session.type
|
||||
raise driver_exceptions.UnsupportedOptionError(
|
||||
user_fault_string=msg,
|
||||
operator_fault_string=msg)
|
||||
|
||||
def _check_for_allowed_cidrs(self, allowed_cidrs):
|
||||
# TODO(haleyb): add support for this
|
||||
if isinstance(allowed_cidrs, o_datamodels.UnsetType):
|
||||
@ -140,6 +150,11 @@ class OvnProviderDriver(driver_base.ProviderDriver):
|
||||
'admin_state_up': admin_state_up}
|
||||
request = {'type': ovn_const.REQ_TYPE_POOL_CREATE,
|
||||
'info': request_info}
|
||||
if not isinstance(
|
||||
pool.session_persistence, o_datamodels.UnsetType):
|
||||
self._check_for_supported_session_persistence(
|
||||
pool.session_persistence)
|
||||
request['info']['session_persistence'] = pool.session_persistence
|
||||
self._ovn_helper.add_request(request)
|
||||
if pool.healthmonitor is not None and not isinstance(
|
||||
pool.healthmonitor, o_datamodels.UnsetType):
|
||||
@ -170,6 +185,12 @@ class OvnProviderDriver(driver_base.ProviderDriver):
|
||||
|
||||
if not isinstance(new_pool.admin_state_up, o_datamodels.UnsetType):
|
||||
request_info['admin_state_up'] = new_pool.admin_state_up
|
||||
if not isinstance(
|
||||
new_pool.session_persistence, o_datamodels.UnsetType):
|
||||
self._check_for_supported_session_persistence(
|
||||
new_pool.session_persistence)
|
||||
request_info['session_persistence'] = (
|
||||
new_pool.session_persistence)
|
||||
request = {'type': ovn_const.REQ_TYPE_POOL_UPDATE,
|
||||
'info': request_info}
|
||||
self._ovn_helper.add_request(request)
|
||||
|
@ -1605,10 +1605,25 @@ class OvnProviderHelper():
|
||||
if listener_key in ovn_lb.external_ids:
|
||||
external_ids[listener_key] = str(
|
||||
external_ids[listener_key]) + str(pool_key)
|
||||
persistence_timeout = None
|
||||
if pool.get(constants.SESSION_PERSISTENCE):
|
||||
persistence_timeout = pool[constants.SESSION_PERSISTENCE].get(
|
||||
constants.PERSISTENCE_TIMEOUT, '360')
|
||||
try:
|
||||
self.ovn_nbdb_api.db_set(
|
||||
commands = []
|
||||
commands.append(self.ovn_nbdb_api.db_set(
|
||||
'Load_Balancer', ovn_lb.uuid,
|
||||
('external_ids', external_ids)).execute(check_error=True)
|
||||
('external_ids', external_ids)))
|
||||
|
||||
if persistence_timeout:
|
||||
options = copy.deepcopy(ovn_lb.options)
|
||||
options[ovn_const.AFFINITY_TIMEOUT] = str(persistence_timeout)
|
||||
|
||||
commands.append(self.ovn_nbdb_api.db_set(
|
||||
'Load_Balancer', ovn_lb.uuid,
|
||||
('options', options)))
|
||||
|
||||
self._execute_commands(commands)
|
||||
|
||||
# Pool status will be set to Online after a member is added to it
|
||||
# or when it is created with listener.
|
||||
@ -1693,6 +1708,11 @@ class OvnProviderHelper():
|
||||
'Load_Balancer', ovn_lb.uuid,
|
||||
'external_ids', (pool_key_when_disabled)))
|
||||
|
||||
if ovn_const.AFFINITY_TIMEOUT in ovn_lb.options:
|
||||
commands.append(
|
||||
self.ovn_nbdb_api.db_remove('Load_Balancer', ovn_lb.uuid,
|
||||
'options',
|
||||
(ovn_const.AFFINITY_TIMEOUT)))
|
||||
commands.extend(
|
||||
self._clean_lb_if_empty(
|
||||
ovn_lb, pool[constants.LOADBALANCER_ID], external_ids)[0])
|
||||
@ -1725,7 +1745,8 @@ class OvnProviderHelper():
|
||||
status = {
|
||||
constants.POOLS: [pool_status],
|
||||
constants.LOADBALANCERS: [lbalancer_status]}
|
||||
if constants.ADMIN_STATE_UP not in pool:
|
||||
if (constants.ADMIN_STATE_UP not in pool and
|
||||
constants.SESSION_PERSISTENCE not in pool):
|
||||
return status
|
||||
try:
|
||||
ovn_lb = self._find_ovn_lbs(
|
||||
@ -1746,37 +1767,54 @@ class OvnProviderHelper():
|
||||
p_key_to_add = {}
|
||||
|
||||
pool_listeners = []
|
||||
commands = []
|
||||
|
||||
try:
|
||||
pool_listeners = self._get_pool_listeners(ovn_lb, pool_key)
|
||||
if pool[constants.ADMIN_STATE_UP]:
|
||||
if p_key_when_disabled in external_ids:
|
||||
p_key_to_add[pool_key] = external_ids[p_key_when_disabled]
|
||||
external_ids[pool_key] = external_ids[p_key_when_disabled]
|
||||
del external_ids[p_key_when_disabled]
|
||||
p_key_to_remove = p_key_when_disabled
|
||||
else:
|
||||
if pool_key in external_ids:
|
||||
p_key_to_add[p_key_when_disabled] = external_ids[pool_key]
|
||||
external_ids[p_key_when_disabled] = external_ids[pool_key]
|
||||
del external_ids[pool_key]
|
||||
p_key_to_remove = pool_key
|
||||
admin_state_up = pool.get(constants.ADMIN_STATE_UP)
|
||||
if admin_state_up is not None:
|
||||
if admin_state_up:
|
||||
if p_key_when_disabled in external_ids:
|
||||
p_key_to_add[pool_key] = external_ids[
|
||||
p_key_when_disabled]
|
||||
external_ids[pool_key] = external_ids[
|
||||
p_key_when_disabled]
|
||||
del external_ids[p_key_when_disabled]
|
||||
p_key_to_remove = p_key_when_disabled
|
||||
else:
|
||||
if pool_key in external_ids:
|
||||
p_key_to_add[p_key_when_disabled] = external_ids[
|
||||
pool_key]
|
||||
external_ids[p_key_when_disabled] = external_ids[
|
||||
pool_key]
|
||||
del external_ids[pool_key]
|
||||
p_key_to_remove = pool_key
|
||||
|
||||
if p_key_to_remove:
|
||||
commands = []
|
||||
commands.append(
|
||||
self.ovn_nbdb_api.db_remove(
|
||||
'Load_Balancer', ovn_lb.uuid, 'external_ids',
|
||||
(p_key_to_remove)))
|
||||
if p_key_to_remove:
|
||||
commands.append(
|
||||
self.ovn_nbdb_api.db_remove(
|
||||
'Load_Balancer', ovn_lb.uuid, 'external_ids',
|
||||
(p_key_to_remove)))
|
||||
|
||||
commands.append(
|
||||
self.ovn_nbdb_api.db_set(
|
||||
'Load_Balancer', ovn_lb.uuid,
|
||||
('external_ids', p_key_to_add)))
|
||||
commands.append(
|
||||
self.ovn_nbdb_api.db_set(
|
||||
'Load_Balancer', ovn_lb.uuid,
|
||||
('external_ids', p_key_to_add)))
|
||||
|
||||
commands.extend(
|
||||
self._refresh_lb_vips(ovn_lb, external_ids))
|
||||
|
||||
if pool.get(constants.SESSION_PERSISTENCE):
|
||||
new_timeout = pool[constants.SESSION_PERSISTENCE].get(
|
||||
constants.PERSISTENCE_TIMEOUT, '360')
|
||||
options = copy.deepcopy(ovn_lb.options)
|
||||
options[ovn_const.AFFINITY_TIMEOUT] = str(new_timeout)
|
||||
commands.append(self.ovn_nbdb_api.db_set(
|
||||
'Load_Balancer', ovn_lb.uuid,
|
||||
('options', options)))
|
||||
|
||||
self._execute_commands(commands)
|
||||
|
||||
commands.extend(
|
||||
self._refresh_lb_vips(ovn_lb, external_ids))
|
||||
self._execute_commands(commands)
|
||||
if pool[constants.ADMIN_STATE_UP]:
|
||||
operating_status = constants.ONLINE
|
||||
else:
|
||||
|
@ -89,7 +89,7 @@ class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
|
||||
members=[self.ref_member],
|
||||
pool_id=self.pool_id,
|
||||
protocol='TCP',
|
||||
session_persistence={'type': 'fix'})
|
||||
session_persistence={'type': 'SOURCE_IP'})
|
||||
self.ref_pool = data_models.Pool(
|
||||
admin_state_up=True,
|
||||
description='pool',
|
||||
@ -100,7 +100,7 @@ class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
|
||||
members=[self.ref_member],
|
||||
pool_id=self.pool_id,
|
||||
protocol='TCP',
|
||||
session_persistence={'type': 'fix'})
|
||||
session_persistence={'type': 'SOURCE_IP'})
|
||||
self.ref_http_pool = data_models.Pool(
|
||||
admin_state_up=True,
|
||||
description='pool',
|
||||
@ -689,7 +689,8 @@ class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
|
||||
'listener_id': self.ref_pool.listener_id,
|
||||
'protocol': self.ref_pool.protocol,
|
||||
'lb_algorithm': constants.LB_ALGORITHM_SOURCE_IP_PORT,
|
||||
'admin_state_up': self.ref_pool.admin_state_up}
|
||||
'admin_state_up': self.ref_pool.admin_state_up,
|
||||
'session_persistence': {'type': 'SOURCE_IP'}}
|
||||
info_member = {
|
||||
'id': self.ref_member.member_id,
|
||||
'address': self.ref_member.address,
|
||||
@ -758,7 +759,8 @@ class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
|
||||
'listener_id': self.ref_pool.listener_id,
|
||||
'protocol': self.ref_pool.protocol,
|
||||
'lb_algorithm': constants.LB_ALGORITHM_SOURCE_IP_PORT,
|
||||
'admin_state_up': self.ref_pool.admin_state_up}
|
||||
'admin_state_up': self.ref_pool.admin_state_up,
|
||||
'session_persistence': {'type': 'SOURCE_IP'}}
|
||||
info_member = {
|
||||
'id': self.ref_member.member_id,
|
||||
'address': self.ref_member.address,
|
||||
@ -866,7 +868,8 @@ class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
|
||||
'listener_id': self.ref_pool.listener_id,
|
||||
'protocol': self.ref_pool.protocol,
|
||||
'lb_algorithm': constants.LB_ALGORITHM_SOURCE_IP_PORT,
|
||||
'admin_state_up': self.ref_pool.admin_state_up}
|
||||
'admin_state_up': self.ref_pool.admin_state_up,
|
||||
'session_persistence': {'type': 'SOURCE_IP'}}
|
||||
expected_dict = {'type': ovn_const.REQ_TYPE_POOL_CREATE,
|
||||
'info': info}
|
||||
self.driver.pool_create(self.ref_pool)
|
||||
@ -879,7 +882,8 @@ class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
|
||||
'listener_id': self.ref_pool.listener_id,
|
||||
'protocol': self.ref_pool.protocol,
|
||||
'lb_algorithm': constants.LB_ALGORITHM_SOURCE_IP_PORT,
|
||||
'admin_state_up': self.ref_pool.admin_state_up}
|
||||
'admin_state_up': self.ref_pool.admin_state_up,
|
||||
'session_persistence': {'type': 'SOURCE_IP'}}
|
||||
info_hm = {'id': self.ref_health_monitor.healthmonitor_id,
|
||||
'pool_id': self.ref_health_monitor.pool_id,
|
||||
'type': self.ref_health_monitor.type,
|
||||
@ -905,7 +909,21 @@ class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
|
||||
'protocol': self.ref_pool.protocol,
|
||||
'lb_algorithm': constants.LB_ALGORITHM_SOURCE_IP_PORT,
|
||||
'listener_id': self.ref_pool.listener_id,
|
||||
'admin_state_up': True}
|
||||
'admin_state_up': True,
|
||||
'session_persistence': {'type': 'SOURCE_IP'}}
|
||||
expected_dict = {'type': ovn_const.REQ_TYPE_POOL_CREATE,
|
||||
'info': info}
|
||||
self.driver.pool_create(self.ref_pool)
|
||||
self.mock_add_request.assert_called_once_with(expected_dict)
|
||||
|
||||
def test_pool_create_unset_session_persistence(self):
|
||||
self.ref_pool.session_persistence = data_models.UnsetType()
|
||||
info = {'id': self.ref_pool.pool_id,
|
||||
'loadbalancer_id': self.ref_pool.loadbalancer_id,
|
||||
'protocol': self.ref_pool.protocol,
|
||||
'lb_algorithm': constants.LB_ALGORITHM_SOURCE_IP_PORT,
|
||||
'listener_id': self.ref_pool.listener_id,
|
||||
'admin_state_up': self.ref_pool.admin_state_up}
|
||||
expected_dict = {'type': ovn_const.REQ_TYPE_POOL_CREATE,
|
||||
'info': info}
|
||||
self.driver.pool_create(self.ref_pool)
|
||||
@ -956,7 +974,8 @@ class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
|
||||
info = {'id': self.ref_update_pool.pool_id,
|
||||
'loadbalancer_id': self.ref_update_pool.loadbalancer_id,
|
||||
'protocol': self.ref_pool.protocol,
|
||||
'admin_state_up': self.ref_update_pool.admin_state_up}
|
||||
'admin_state_up': self.ref_update_pool.admin_state_up,
|
||||
'session_persistence': {'type': 'SOURCE_IP'}}
|
||||
expected_dict = {'type': ovn_const.REQ_TYPE_POOL_UPDATE,
|
||||
'info': info}
|
||||
self.driver.pool_update(self.ref_pool, self.ref_update_pool)
|
||||
@ -967,7 +986,8 @@ class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
|
||||
info = {'id': self.ref_update_pool.pool_id,
|
||||
'loadbalancer_id': self.ref_update_pool.loadbalancer_id,
|
||||
'protocol': self.ref_pool.protocol,
|
||||
'admin_state_up': self.ref_update_pool.admin_state_up}
|
||||
'admin_state_up': self.ref_update_pool.admin_state_up,
|
||||
'session_persistence': {'type': 'SOURCE_IP'}}
|
||||
expected_dict = {'type': ovn_const.REQ_TYPE_POOL_UPDATE,
|
||||
'info': info}
|
||||
self.driver.pool_update(self.ref_pool, self.ref_update_pool)
|
||||
@ -978,7 +998,8 @@ class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
|
||||
info = {'id': self.ref_update_pool.pool_id,
|
||||
'loadbalancer_id': self.ref_update_pool.loadbalancer_id,
|
||||
'protocol': self.ref_pool.protocol,
|
||||
'admin_state_up': self.ref_update_pool.admin_state_up}
|
||||
'admin_state_up': self.ref_update_pool.admin_state_up,
|
||||
'session_persistence': {'type': 'SOURCE_IP'}}
|
||||
expected_dict = {'type': ovn_const.REQ_TYPE_POOL_UPDATE,
|
||||
'info': info}
|
||||
self.driver.pool_update(self.ref_pool, self.ref_update_pool)
|
||||
@ -988,7 +1009,19 @@ class TestOvnProviderDriver(ovn_base.TestOvnOctaviaBase):
|
||||
self.ref_update_pool.admin_state_up = data_models.UnsetType()
|
||||
info = {'id': self.ref_update_pool.pool_id,
|
||||
'loadbalancer_id': self.ref_update_pool.loadbalancer_id,
|
||||
'protocol': self.ref_pool.protocol}
|
||||
'protocol': self.ref_pool.protocol,
|
||||
'session_persistence': {'type': 'SOURCE_IP'}}
|
||||
expected_dict = {'type': ovn_const.REQ_TYPE_POOL_UPDATE,
|
||||
'info': info}
|
||||
self.driver.pool_update(self.ref_pool, self.ref_update_pool)
|
||||
self.mock_add_request.assert_called_once_with(expected_dict)
|
||||
|
||||
def test_pool_update_unset_new_session_timeout(self):
|
||||
self.ref_update_pool.session_persistence = data_models.UnsetType()
|
||||
info = {'id': self.ref_update_pool.pool_id,
|
||||
'loadbalancer_id': self.ref_update_pool.loadbalancer_id,
|
||||
'protocol': self.ref_pool.protocol,
|
||||
'admin_state_up': self.ref_update_pool.admin_state_up}
|
||||
expected_dict = {'type': ovn_const.REQ_TYPE_POOL_UPDATE,
|
||||
'info': info}
|
||||
self.driver.pool_update(self.ref_pool, self.ref_update_pool)
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Now the OVN Octavia provider uses the affinity_timeout option of OVN
|
||||
Load Balancers to support pools sessions persistence. It only supports
|
||||
the SOURCE_IP option type. If not timeout is set, by default 360 seconds
|
||||
is set if the session persistence is enabled.
|
Loading…
Reference in New Issue
Block a user