Merge "NSX|V3: Forbid multiple listeners per LB pool"

This commit is contained in:
Zuul 2019-03-31 12:42:55 +00:00 committed by Gerrit Code Review
commit 9187915342
3 changed files with 178 additions and 10 deletions

View File

@ -109,6 +109,38 @@ class EdgeListenerManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
'tag': listener['loadbalancer_id']})
return tags
def _validate_default_pool(self, context, listener, vs_id, completor):
if listener.get('default_pool_id'):
pool_binding = nsx_db.get_nsx_lbaas_pool_binding(
context.session, listener['loadbalancer']['id'],
listener['default_pool_id'])
if (pool_binding and pool_binding['lb_vs_id'] and
(vs_id is None or pool_binding['lb_vs_id'] != vs_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)
def _update_default_pool_binding(self, context, listener, vs_id):
if listener.get('default_pool_id'):
lb_id = listener['loadbalancer']['id']
pool_id = listener['default_pool_id']
pool_binding = nsx_db.get_nsx_lbaas_pool_binding(
context.session, lb_id, pool_id)
if pool_binding:
nsx_db.update_nsx_lbaas_pool_binding(
context.session, lb_id, pool_id, vs_id)
def _remove_default_pool_binding(self, context, listener):
if listener.get('default_pool_id'):
lb_id = listener['loadbalancer']['id']
pool_id = listener['default_pool_id']
pool_binding = nsx_db.get_nsx_lbaas_pool_binding(
context.session, lb_id, pool_id)
if pool_binding:
nsx_db.update_nsx_lbaas_pool_binding(
context.session, lb_id, pool_id, None)
@log_helpers.log_method_call
def create(self, context, listener, completor,
certificate=None):
@ -128,11 +160,14 @@ class EdgeListenerManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
listener['protocol'] == lb_const.LB_PROTOCOL_HTTPS):
profile_type = lb_const.LB_TCP_PROFILE
else:
completor(success=False)
msg = (_('Cannot create listener %(listener)s with '
'protocol %(protocol)s') %
{'listener': listener['id'],
'protocol': listener['protocol']})
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
# Validate default pool
self._validate_default_pool(context, listener, None, completor)
try:
app_profile = app_client.create(
display_name=vs_name, resource_type=profile_type, tags=tags)
@ -167,6 +202,8 @@ class EdgeListenerManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
nsx_db.add_nsx_lbaas_listener_binding(
context.session, lb_id, listener['id'], app_profile_id,
virtual_server['id'])
self._update_default_pool_binding(
context, listener, virtual_server['id'])
completor(success=True)
@log_helpers.log_method_call
@ -190,6 +227,11 @@ class EdgeListenerManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
msg = (_('Cannot find listener %(listener)s binding on NSX '
'backend'), {'listener': old_listener['id']})
raise n_exc.BadRequest(resource='lbaas-listener', msg=msg)
# Validate default pool
self._validate_default_pool(
context, new_listener, binding['lb_vs_id'], completor)
try:
vs_id = binding['lb_vs_id']
app_profile_id = binding['app_profile_id']
@ -207,6 +249,10 @@ class EdgeListenerManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
LOG.error('Failed to update listener %(listener)s with '
'error %(error)s',
{'listener': old_listener['id'], 'error': e})
if (old_listener.get('default_pool_id') !=
new_listener.get('default_pool_id')):
self._remove_default_pool_binding(context, old_listener)
self._update_default_pool_binding(context, new_listener, vs_id)
@log_helpers.log_method_call
def delete(self, context, listener, completor):
@ -245,12 +291,7 @@ class EdgeListenerManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
if listener.get('default_pool_id'):
vs_client.update(vs_id, pool_id='')
# Update pool binding to disassociate virtual server
pool_binding = nsx_db.get_nsx_lbaas_pool_binding(
context.session, lb_id, listener['default_pool_id'])
if pool_binding:
nsx_db.update_nsx_lbaas_pool_binding(
context.session, lb_id,
listener['default_pool_id'], None)
self._remove_default_pool_binding(context, listener)
vs_client.delete(vs_id)
except nsx_exc.NsxResourceNotFound:
msg = (_("virtual server not found on nsx: %(vs)s") %

View File

@ -230,6 +230,12 @@ class EdgePoolManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
tags = self._get_pool_tags(context, pool)
description = pool.get('description')
lb_algorithm = lb_const.LB_POOL_ALGORITHM_MAP.get(pool['lb_algorithm'])
if pool.get('listeners') and len(pool['listeners']) > 1:
completor(success=False)
msg = (_('Failed to create pool: Multiple listeners are not '
'supported.'))
raise n_exc.BadRequest(resource='lbaas-pool', msg=msg)
# NOTE(salv-orlando): Guard against accidental compat breakages
try:
listener = pool['listener'] or pool['listeners'][0]
@ -268,6 +274,7 @@ class EdgePoolManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
nsx_db.update_nsx_lbaas_pool_binding(
context.session, lb_id, pool['id'], vs_id)
else:
completor(success=False)
msg = (_("Couldn't find binding on the listener: %s") %
listener['id'])
raise nsx_exc.NsxPluginException(err_msg=msg)
@ -292,9 +299,15 @@ class EdgePoolManagerFromDict(base_mgr.Nsxv3LoadbalancerBaseManager):
binding = nsx_db.get_nsx_lbaas_pool_binding(
context.session, old_pool['loadbalancer_id'], old_pool['id'])
if not binding:
completor(success=False)
msg = (_('Cannot find pool %(pool)s binding on NSX db '
'mapping') % {'pool': old_pool['id']})
raise n_exc.BadRequest(resource='lbaas-pool', msg=msg)
if new_pool.get('listeners') and len(new_pool['listeners']) > 1:
completor(success=False)
msg = (_('Failed to update pool %s: Multiple listeners are not '
'supported.') % new_pool['id'])
raise n_exc.BadRequest(resource='lbaas-pool', msg=msg)
# NOTE(salv-orlando): Guard against accidental compat breakages
try:
listener = new_pool['listener'] or new_pool['listeners'][0]

View File

@ -46,6 +46,8 @@ LB_NETWORK = {'router:external': False,
'id': 'xxxxx',
'name': 'network-1'}
LISTENER_ID = 'listener-x'
HTTP_LISTENER_ID = 'listener-http'
HTTPS_LISTENER_ID = 'listener-https'
APP_PROFILE_ID = 'appp-x'
LB_VS_ID = 'vs-x'
LB_APP_PROFILE = {
@ -162,10 +164,10 @@ class BaseTestEdgeLbaasV2(base.BaseTestCase):
'HTTP', protocol_port=80,
loadbalancer=self.lb)
self.https_listener = lb_models.Listener(
LISTENER_ID, LB_TENANT_ID, 'listener1', '', None, LB_ID,
HTTP_LISTENER_ID, LB_TENANT_ID, 'listener2', '', None, LB_ID,
'HTTPS', protocol_port=443, loadbalancer=self.lb)
self.terminated_https_listener = lb_models.Listener(
LISTENER_ID, LB_TENANT_ID, 'listener1', '', None, LB_ID,
HTTPS_LISTENER_ID, LB_TENANT_ID, 'listener3', '', None, LB_ID,
'TERMINATED_HTTPS', protocol_port=443, loadbalancer=self.lb)
self.pool = lb_models.Pool(POOL_ID, LB_TENANT_ID, 'pool1', '',
None, 'HTTP', 'ROUND_ROBIN',
@ -449,7 +451,7 @@ class TestEdgeLbaasV2Listener(BaseTestEdgeLbaasV2):
mock_add_virtual_server.assert_called_with(LB_SERVICE_ID,
LB_VS_ID)
mock_add_listener_binding.assert_called_with(
self.context.session, LB_ID, LISTENER_ID, APP_PROFILE_ID,
self.context.session, LB_ID, listener.id, APP_PROFILE_ID,
LB_VS_ID)
mock_successful_completion = (
self.lbv2_driver.listener.successful_completion)
@ -489,7 +491,7 @@ class TestEdgeLbaasV2Listener(BaseTestEdgeLbaasV2):
mock_add_virtual_server.assert_called_with(LB_SERVICE_ID,
LB_VS_ID)
mock_add_listener_binding.assert_called_with(
self.context.session, LB_ID, LISTENER_ID, APP_PROFILE_ID,
self.context.session, LB_ID, HTTPS_LISTENER_ID, APP_PROFILE_ID,
LB_VS_ID)
mock_successful_completion = (
@ -498,6 +500,65 @@ class TestEdgeLbaasV2Listener(BaseTestEdgeLbaasV2):
self.context, self.terminated_https_listener,
delete=False)
def test_create_listener_with_default_pool(self):
listener = lb_models.Listener(LISTENER_ID, LB_TENANT_ID,
'listener1', 'Dummy', self.pool.id,
LB_ID, 'HTTP', protocol_port=80,
loadbalancer=self.lb,
default_pool=self.pool)
with mock.patch.object(self.core_plugin, 'get_floatingips'
) as mock_get_floatingips, \
mock.patch.object(self.app_client, 'create'
) as mock_create_app_profile, \
mock.patch.object(self.vs_client, 'create'
) as mock_create_virtual_server, \
mock.patch.object(nsx_db, 'get_nsx_lbaas_loadbalancer_binding'
) as mock_get_lb_binding, \
mock.patch.object(self.service_client, 'add_virtual_server'
) as mock_add_virtual_server, \
mock.patch.object(nsx_db, 'add_nsx_lbaas_listener_binding'
) as mock_add_listener_binding,\
mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding'
) as mock_get_pool_binding:
mock_get_floatingips.return_value = []
mock_create_app_profile.return_value = {'id': APP_PROFILE_ID}
mock_create_virtual_server.return_value = {'id': LB_VS_ID}
mock_get_lb_binding.return_value = LB_BINDING
mock_get_pool_binding.return_value = None
self.edge_driver.listener.create(self.context, listener)
mock_add_virtual_server.assert_called_with(LB_SERVICE_ID,
LB_VS_ID)
mock_add_listener_binding.assert_called_with(
self.context.session, LB_ID, LISTENER_ID, APP_PROFILE_ID,
LB_VS_ID)
mock_successful_completion = (
self.lbv2_driver.listener.successful_completion)
mock_successful_completion.assert_called_with(self.context,
listener,
delete=False)
def test_create_listener_with_used_default_pool(self):
listener = lb_models.Listener(LISTENER_ID, LB_TENANT_ID,
'listener1', 'Dummy', self.pool.id,
LB_ID, 'HTTP', protocol_port=80,
loadbalancer=self.lb,
default_pool=self.pool)
with mock.patch.object(self.core_plugin, 'get_floatingips'
) as mock_get_floatingips, \
mock.patch.object(nsx_db, 'get_nsx_lbaas_loadbalancer_binding'
) as mock_get_lb_binding, \
mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding'
) as mock_get_pool_binding:
mock_get_floatingips.return_value = []
mock_get_lb_binding.return_value = LB_BINDING
mock_get_pool_binding.return_value = POOL_BINDING
self.assertRaises(n_exc.BadRequest,
self.edge_driver.listener.create,
self.context, listener)
def test_update(self):
new_listener = lb_models.Listener(LISTENER_ID, LB_TENANT_ID,
'listener1-new', 'new-description',
@ -519,6 +580,32 @@ class TestEdgeLbaasV2Listener(BaseTestEdgeLbaasV2):
new_listener,
delete=False)
def test_update_with_default_pool(self):
new_listener = lb_models.Listener(LISTENER_ID, LB_TENANT_ID,
'listener1-new', 'new-description',
self.pool, LB_ID, protocol_port=80,
loadbalancer=self.lb,
default_pool=self.pool)
with mock.patch.object(self.core_plugin, 'get_floatingips'
) as mock_get_floatingips, \
mock.patch.object(nsx_db, 'get_nsx_lbaas_listener_binding'
) as mock_get_listener_binding,\
mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding'
) as mock_get_pool_binding,\
mock.patch.object(nsx_db, 'update_nsx_lbaas_pool_binding'):
mock_get_floatingips.return_value = []
mock_get_listener_binding.return_value = LISTENER_BINDING
mock_get_pool_binding.return_value = POOL_BINDING
self.edge_driver.listener.update(self.context, self.listener,
new_listener)
mock_successful_completion = (
self.lbv2_driver.listener.successful_completion)
mock_successful_completion.assert_called_with(self.context,
new_listener,
delete=False)
def test_delete(self):
with mock.patch.object(nsx_db, 'get_nsx_lbaas_listener_binding'
) as mock_get_listener_binding, \
@ -686,6 +773,18 @@ class TestEdgeLbaasV2Pool(BaseTestEdgeLbaasV2):
self.pool_persistency.listeners = []
self._test_create_with_persistency(vs_data, verify_func)
def test_create_multiple_listeners(self):
"""Verify creation will fail if multiple listeners are set"""
pool = lb_models.Pool(POOL_ID, LB_TENANT_ID, 'pool1', '',
None, 'HTTP', 'ROUND_ROBIN',
loadbalancer_id=LB_ID,
listeners=[self.listener,
self.https_listener],
loadbalancer=self.lb)
self.assertRaises(n_exc.BadRequest,
self.edge_driver.pool.create,
self.context, pool)
def test_update(self):
new_pool = lb_models.Pool(POOL_ID, LB_TENANT_ID, 'pool-name', '',
None, 'HTTP', 'LEAST_CONNECTIONS',
@ -702,6 +801,21 @@ class TestEdgeLbaasV2Pool(BaseTestEdgeLbaasV2):
new_pool,
delete=False)
def test_update_multiple_listeners(self):
"""Verify update action will fail if multiple listeners are set"""
new_pool = lb_models.Pool(POOL_ID, LB_TENANT_ID, 'pool1', '',
None, 'HTTP', 'ROUND_ROBIN',
loadbalancer_id=LB_ID,
listeners=[self.listener,
self.https_listener],
loadbalancer=self.lb)
with mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding'
) as mock_get_pool_binding:
mock_get_pool_binding.return_value = POOL_BINDING
self.assertRaises(n_exc.BadRequest,
self.edge_driver.pool.update,
self.context, self.pool, new_pool)
def _test_update_with_persistency(self, vs_data, old_pool, new_pool,
verify_func):
with mock.patch.object(nsx_db, 'get_nsx_lbaas_pool_binding'