NSX|P: Support listener default pool with session persistence

Set/update/delete NSX session persistence profile, upon setting/removing lbaas
listener default pool.

Change-Id: I2a007a11ae30b166c0dd4e01525a5917da5ff1d4
This commit is contained in:
Adit Sarfaty 2019-06-06 14:52:59 +03:00
parent 248744b7c5
commit 2be77ef707
4 changed files with 92 additions and 46 deletions

View File

@ -19,7 +19,6 @@ from oslo_log import log as logging
from oslo_utils import excutils
from vmware_nsx._i18n import _
from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.services.lbaas import base_mgr
from vmware_nsx.services.lbaas.nsx_p.implementation import lb_utils
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
@ -79,7 +78,7 @@ class EdgeL7PolicyManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
try:
vs_client.remove_lb_rule(policy['listener_id'],
policy_name)
except nsx_exc.NsxResourceNotFound:
except nsxlib_exc.ResourceNotFound:
pass
except nsxlib_exc.ManagerError:
completor(success=False)

View File

@ -216,11 +216,9 @@ def setup_session_persistence(nsxpolicy, pool, pool_tags, listener, vs_data):
else:
pp_client = lb_client.lb_source_ip_persistence_profile
pers_type = nsxlib_lb.PersistenceProfileTypes.SOURCE_IP
if pers_type:
# There is a profile to create or update
pp_kwargs = {
'persistence_profile_id': pool['id'],
'name': "persistence_%s" % utils.get_name_and_uuid(
pool['name'] or 'pool', pool['id'], maxlen=235),
'tags': lb_utils.build_persistence_profile_tags(
@ -230,8 +228,8 @@ def setup_session_persistence(nsxpolicy, pool, pool_tags, listener, vs_data):
pp_kwargs['cookie_name'] = cookie_name
pp_kwargs['cookie_mode'] = cookie_mode
persistence_profile_id = p_utils.path_to_id(
vs_data.get('lb_persistence_profile_path', ''))
profile_path = vs_data.get('lb_persistence_profile_path', '')
persistence_profile_id = p_utils.path_to_id(profile_path)
if persistence_profile_id:
# NOTE: removal of the persistence profile must be executed
# after the virtual server has been updated
@ -246,7 +244,6 @@ def setup_session_persistence(nsxpolicy, pool, pool_tags, listener, vs_data):
return persistence_profile_id, None
else:
# Prepare removal of persistence profile
profile_path = vs_data.get('lb_persistence_profile_path', '')
return (None, functools.partial(delete_persistence_profile,
nsxpolicy, profile_path))
elif pers_type:

View File

@ -21,8 +21,8 @@ from oslo_log import log as logging
from oslo_utils import excutils
from vmware_nsx._i18n import _
from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.services.lbaas import base_mgr
from vmware_nsx.services.lbaas import lb_common
from vmware_nsx.services.lbaas import lb_const
from vmware_nsx.services.lbaas.nsx_p.implementation import lb_utils
from vmware_nsxlib.v3 import exceptions as nsxlib_exc
@ -135,19 +135,29 @@ class EdgeListenerManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
return app_client
def _validate_default_pool(self, listener, completor):
l_pool_id = listener.get('default_pool_id')
if l_pool_id:
def _validate_default_pool(self, listener, completor,
old_listener=None):
def_pool_id = listener.get('default_pool_id')
if def_pool_id:
vs_client = self.core_plugin.nsxpolicy.load_balancer.virtual_server
vs_list = vs_client.list()
for vs in vs_list:
pool_id = p_utils.path_to_id(vs.get('pool_path', ''))
if pool_id == l_pool_id:
if pool_id == def_pool_id:
completor(success=False)
msg = (_('Default pool %s is already used by another '
'listener') % listener['default_pool_id'])
raise n_exc.BadRequest(resource='lbaas-pool', msg=msg)
# Perform additional validation for session persistence before
# creating resources in the backend
old_pool = None
if old_listener:
old_pool = old_listener.get('default_pool')
lb_common.validate_session_persistence(
listener.get('default_pool'), listener, completor,
old_pool=old_pool)
@log_helpers.log_method_call
def create(self, context, listener, completor,
certificate=None):
@ -169,8 +179,54 @@ class EdgeListenerManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
msg = _('Failed to create virtual server at NSX backend')
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
self._update_default_pool(context, listener, completor)
completor(success=True)
def _get_pool_tags(self, context, pool):
return lb_utils.get_tags(self.core_plugin, pool['id'],
lb_const.LB_POOL_TYPE, pool['tenant_id'],
context.project_name)
def _update_default_pool(self, context, listener, completor):
if not listener.get('default_pool_id'):
return
nsxlib_lb = self.core_plugin.nsxpolicy.load_balancer
vs_client = nsxlib_lb.virtual_server
vs_data = vs_client.get(listener['id'])
pool_id = listener['default_pool_id']
pool = listener['default_pool']
try:
(persistence_profile_id,
post_process_func) = lb_utils.setup_session_persistence(
self.core_plugin.nsxpolicy,
pool,
self._get_pool_tags(context, pool),
listener, vs_data)
except nsxlib_exc.ManagerError:
with excutils.save_and_reraise_exception():
completor(success=False)
LOG.error("Failed to configure session persistence "
"profile for listener %s", listener['id'])
try:
# Update persistence profile and pool on virtual server
vs_client.update(
listener['id'],
pool_id=pool_id,
lb_persistence_profile_id=persistence_profile_id)
LOG.debug("Updated NSX virtual server %(vs_id)s with "
"persistence profile %(prof)s",
{'vs_id': listener['id'],
'prof': persistence_profile_id})
if post_process_func:
post_process_func()
except nsxlib_exc.ManagerError:
with excutils.save_and_reraise_exception():
completor(success=False)
LOG.error("Failed to attach persistence profile %s to "
"virtual server %s",
persistence_profile_id, listener['id'])
@log_helpers.log_method_call
def update(self, context, old_listener, new_listener, completor,
certificate=None):
@ -180,7 +236,8 @@ class EdgeListenerManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
vs_name = None
tags = None
self._validate_default_pool(new_listener, completor)
self._validate_default_pool(new_listener, completor,
old_listener=old_listener)
if new_listener['name'] != old_listener['name']:
vs_name = utils.get_name_and_uuid(
new_listener['name'] or 'listener',
@ -201,6 +258,11 @@ class EdgeListenerManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
LOG.error('Failed to update listener %(listener)s with '
'error %(error)s',
{'listener': old_listener['id'], 'error': e})
# Update default pool and session persistence
if (old_listener.get('default_pool_id') !=
new_listener.get('default_pool_id')):
self._update_default_pool(context, new_listener, completor)
completor(success=True)
@log_helpers.log_method_call
@ -213,8 +275,16 @@ class EdgeListenerManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
app_profile_id = listener['id']
try:
profile_path = None
if listener.get('default_pool_id'):
vs_data = vs_client.get(vs_id)
profile_path = vs_data.get('lb_persistence_profile_path', '')
vs_client.delete(vs_id)
except nsx_exc.NsxResourceNotFound:
# Also delete the old session persistence profile
if profile_path:
lb_utils.delete_persistence_profile(
self.core_plugin.nsxpolicy, profile_path)
except nsxlib_exc.ResourceNotFound:
LOG.error("virtual server not found on nsx: %(vs)s", {'vs': vs_id})
except nsxlib_exc.ManagerError:
completor(success=False)
@ -224,10 +294,9 @@ class EdgeListenerManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
try:
app_client.delete(app_profile_id)
except nsx_exc.NsxResourceNotFound:
except nsxlib_exc.ResourceNotFound:
LOG.error("application profile not found on nsx: %s",
app_profile_id)
except nsxlib_exc.ManagerError:
completor(success=False)
msg = (_('Failed to delete application profile: %(app)s') %
@ -239,7 +308,7 @@ class EdgeListenerManagerFromDict(base_mgr.NsxpLoadbalancerBaseManager):
cert_client = self.core_plugin.nsxpolicy.certificate
try:
cert_client.delete(listener['id'])
except nsx_exc.NsxResourceNotFound:
except nsxlib_exc.ResourceNotFound:
LOG.error("Certificate not found on nsx: %s", listener['id'])
except nsxlib_exc.ManagerError:

View File

@ -272,7 +272,7 @@ class BaseTestEdgeLbaasV2(base.BaseTestCase):
self.pp_cookie_client = mock.patch.object(
load_balancer, 'lb_cookie_persistence_profile').start()
self.pp_generic_client = mock.patch.object(
load_balancer, 'lb_generic_persistence_profile').start()
load_balancer, 'lb_persistence_profile').start()
self.tm_client = mock.patch.object(nsxpolicy,
'trust_management').start()
self.nsxpolicy = nsxpolicy
@ -520,9 +520,6 @@ class TestEdgeLbaasV2Listener(BaseTestEdgeLbaasV2):
self.completor)
def test_create_listener_with_session_persistence(self):
# TODO(asarfaty): add this test after supporting default pool
# session persistence
return
listener = lb_models.Listener(LISTENER_ID, LB_TENANT_ID,
'listener1', 'Dummy',
self.pool_persistency.id,
@ -537,7 +534,9 @@ class TestEdgeLbaasV2Listener(BaseTestEdgeLbaasV2):
return_value=(None, None)), \
mock.patch.object(self.vs_client, 'create_or_overwrite'
) as mock_add_virtual_server,\
mock.patch.object(self.pp_client, 'create_or_overwrite'
mock.patch.object(self.vs_client, 'get', return_value={}),\
mock.patch.object(self.edge_driver.listener, '_get_pool_tags'),\
mock.patch.object(self.pp_cookie_client, 'create_or_overwrite'
) as mock_create_pp:
mock_get_floatingips.return_value = []
@ -559,9 +558,6 @@ class TestEdgeLbaasV2Listener(BaseTestEdgeLbaasV2):
self.assertTrue(self.last_completor_succees)
def test_create_listener_with_session_persistence_fail(self):
# TODO(asarfaty): add this test after supporting default pool
# session persistence
return
listener = lb_models.Listener(LISTENER_ID, LB_TENANT_ID,
'listener1', 'Dummy',
self.pool_persistency.id,
@ -623,9 +619,6 @@ class TestEdgeLbaasV2Listener(BaseTestEdgeLbaasV2):
self.assertTrue(self.last_completor_succees)
def test_update_with_session_persistence(self):
# TODO(asarfaty): add this test after supporting default pool
# session persistence
return
new_listener = lb_models.Listener(LISTENER_ID, LB_TENANT_ID,
'listener1-new', 'new-description',
self.pool_persistency.id,
@ -640,9 +633,11 @@ class TestEdgeLbaasV2Listener(BaseTestEdgeLbaasV2):
mock.patch.object(self.core_plugin,
'get_waf_profile_path_and_mode',
return_value=(None, None)), \
mock.patch.object(self.edge_driver.listener, '_get_pool_tags'),\
mock.patch.object(self.vs_client, 'get', return_value={}),\
mock.patch.object(self.vs_client, 'update',
return_value={'id': LB_VS_ID}), \
mock.patch.object(self.pp_client, 'create_or_overwrite'
mock.patch.object(self.pp_cookie_client, 'create_or_overwrite'
) as mock_create_pp:
mock_get_floatingips.return_value = []
@ -653,9 +648,6 @@ class TestEdgeLbaasV2Listener(BaseTestEdgeLbaasV2):
self.assertTrue(self.last_completor_succees)
def test_update_with_session_persistence_fail(self):
# TODO(asarfaty): add this test after supporting default pool
# session persistence
return
old_listener = lb_models.Listener(LISTENER_ID, LB_TENANT_ID,
'listener1', 'description',
self.pool_persistency.id,
@ -781,8 +773,7 @@ class TestEdgeLbaasV2Pool(BaseTestEdgeLbaasV2):
cookie_mode='INSERT',
cookie_name='meh_cookie',
name=mock.ANY,
tags=mock.ANY,
persistence_profile_id=POOL_ID)
tags=mock.ANY)
mock_vs_update.assert_called_once_with(
LB_VS_ID, pool_id=LB_POOL_ID,
lb_persistence_profile_id=LB_PP_ID)
@ -897,10 +888,6 @@ class TestEdgeLbaasV2Pool(BaseTestEdgeLbaasV2):
cookie=True)
def test_update_remove_persistency(self):
# TODO(asarfaty): add this test after supporting default pool
# session persistence
return
def verify_func(mock_create_pp, mock_update_pp,
mock_delete_pp, mock_vs_update):
mock_create_pp.assert_not_called()
@ -929,10 +916,6 @@ class TestEdgeLbaasV2Pool(BaseTestEdgeLbaasV2):
self.assertTrue(self.last_completor_succees)
def test_delete_with_persistency(self):
# TODO(asarfaty): add this test after supporting default pool
# session persistence
return
with mock.patch.object(self.vs_client, 'get'
) as mock_vs_get, \
mock.patch.object(self.vs_client, 'update', return_value=None
@ -982,14 +965,12 @@ class TestEdgeLbaasV2Pool(BaseTestEdgeLbaasV2):
cookie_name=cookie_name,
cookie_mode=cookie_mode,
name=mock.ANY,
tags=mock.ANY,
persistence_profile_id=POOL_ID)
tags=mock.ANY)
else:
mock_update_pp.assert_called_once_with(
LB_PP_ID,
name=mock.ANY,
tags=mock.ANY,
persistence_profile_id=POOL_ID)
tags=mock.ANY)
# Compare tags - kw args are the last item of a mock call tuple
self.assertItemsEqual(mock_update_pp.mock_calls[0][-1]['tags'],
[{'scope': 'os-lbaas-lb-id', 'tag': 'xxx-xxx'},