Merge "NSX-V3: Prevent deletion of router gw/interface used by LB"
This commit is contained in:
commit
28b82d20c0
@ -14,9 +14,10 @@
|
||||
# under the License.
|
||||
|
||||
from neutron_lib.callbacks import events
|
||||
from neutron_lib.callbacks import exceptions as nc_exc
|
||||
from neutron_lib.callbacks import registry
|
||||
from neutron_lib.callbacks import resources
|
||||
from neutron_lib import exceptions as n_exc
|
||||
from neutron_lib.plugins import constants as plugin_const
|
||||
from oslo_log import helpers as log_helpers
|
||||
from oslo_log import log as logging
|
||||
|
||||
@ -64,30 +65,77 @@ class EdgeLoadbalancerDriverV2(object):
|
||||
|
||||
def _subscribe_router_delete_callback(self):
|
||||
# Check if there is any LB attachment for the NSX router.
|
||||
# This callback is subscribed here to prevent router deletion
|
||||
# if it still has LB service attached to it.
|
||||
# This callback is subscribed here to prevent router/GW/interface
|
||||
# deletion if it still has LB service attached to it.
|
||||
registry.subscribe(self._check_lb_service_on_router,
|
||||
resources.ROUTER, events.BEFORE_DELETE)
|
||||
registry.subscribe(self._check_lb_service_on_router,
|
||||
resources.ROUTER_GATEWAY, events.BEFORE_DELETE)
|
||||
registry.subscribe(self._check_lb_service_on_router_interface,
|
||||
resources.ROUTER_INTERFACE, events.BEFORE_DELETE)
|
||||
|
||||
def _unsubscribe_router_delete_callback(self):
|
||||
registry.unsubscribe(self._check_lb_service_on_router,
|
||||
resources.ROUTER, events.BEFORE_DELETE)
|
||||
registry.unsubscribe(self._check_lb_service_on_router,
|
||||
resources.ROUTER_GATEWAY, events.BEFORE_DELETE)
|
||||
registry.unsubscribe(self._check_lb_service_on_router_interface,
|
||||
resources.ROUTER_INTERFACE, events.BEFORE_DELETE)
|
||||
|
||||
def _get_lb_ports(self, context, subnet_ids):
|
||||
dev_owner = 'neutron:' + plugin_const.LOADBALANCERV2
|
||||
filters = {'device_owner': [dev_owner],
|
||||
'fixed_ips': {'subnet_id': subnet_ids}}
|
||||
return self.loadbalancer.core_plugin.get_ports(
|
||||
context, filters=filters)
|
||||
|
||||
def _check_lb_service_on_router(self, resource, event, trigger,
|
||||
**kwargs):
|
||||
"""Check if there is any lb service on nsx router"""
|
||||
"""Prevent removing a router GW or deleting a router used by LB"""
|
||||
router_id = kwargs.get('router_id')
|
||||
context = kwargs['context']
|
||||
nsx_router_id = nsx_db.get_nsx_router_id(kwargs['context'].session,
|
||||
router_id)
|
||||
if not nsx_router_id:
|
||||
# Skip non-v3 routers (could be a V router in case of TVD plugin)
|
||||
return
|
||||
nsxlib = self.loadbalancer.core_plugin.nsxlib
|
||||
service_client = nsxlib.load_balancer.service
|
||||
# Check if there is any lb service on nsx router
|
||||
lb_service = service_client.get_router_lb_service(nsx_router_id)
|
||||
if lb_service:
|
||||
msg = _('Cannot delete a %s as it still has lb service '
|
||||
'attachment') % resource
|
||||
raise n_exc.BadRequest(resource='lbaas-lb', msg=msg)
|
||||
|
||||
# Also check if there are any loadbalancers attached to this router
|
||||
# subnets
|
||||
router_subnets = self.loadbalancer.core_plugin._find_router_subnets(
|
||||
context.elevated(), router_id)
|
||||
subnet_ids = [subnet['id'] for subnet in router_subnets]
|
||||
if self._get_lb_ports(context.elevated(), subnet_ids):
|
||||
msg = (_('Cannot delete a %s as it used by a loadbalancer') %
|
||||
resource)
|
||||
raise n_exc.BadRequest(resource='lbaas-lb', msg=msg)
|
||||
|
||||
def _check_lb_service_on_router_interface(self, *args, **kwargs):
|
||||
# Prevent removing the interface of an LB subnet from a router
|
||||
router_id = kwargs.get('router_id')
|
||||
subnet_id = kwargs.get('subnet_id')
|
||||
if not router_id or not subnet_id:
|
||||
return
|
||||
|
||||
nsx_router_id = nsx_db.get_nsx_router_id(kwargs['context'].session,
|
||||
kwargs['router_id'])
|
||||
if not nsx_router_id:
|
||||
# Skip non-v3 routers (could be a V router in case of TVD plugin)
|
||||
return
|
||||
nsxlib = self.loadbalancer.core_plugin.nsxlib
|
||||
service_client = nsxlib.load_balancer.service
|
||||
lb_service = service_client.get_router_lb_service(nsx_router_id)
|
||||
if lb_service:
|
||||
msg = _('Cannot delete router as it still has lb service '
|
||||
'attachment')
|
||||
raise nc_exc.CallbackFailure(msg)
|
||||
|
||||
# get LB ports and check if any loadbalancer is using this subnet
|
||||
if self._get_lb_ports(kwargs['context'].elevated(), [subnet_id]):
|
||||
msg = _('Cannot delete a router interface as it used by a '
|
||||
'loadbalancer')
|
||||
raise n_exc.BadRequest(resource='lbaas-lb', msg=msg)
|
||||
|
||||
|
||||
class DummyLoadbalancerDriverV2(object):
|
||||
|
@ -1406,10 +1406,14 @@ class L3NatTest(test_l3_plugin.L3BaseForIntTests, NsxV3PluginTestCaseMixin,
|
||||
new=lambda v: True)
|
||||
mock_nsx_version.start()
|
||||
# Make sure the LB callback is not called on router deletion
|
||||
self.lb_mock = mock.patch(
|
||||
self.lb_mock1 = mock.patch(
|
||||
"vmware_nsx.services.lbaas.nsx_v3.lb_driver_v2."
|
||||
"EdgeLoadbalancerDriverV2._check_lb_service_on_router")
|
||||
self.lb_mock.start()
|
||||
self.lb_mock1.start()
|
||||
self.lb_mock2 = mock.patch(
|
||||
"vmware_nsx.services.lbaas.nsx_v3.lb_driver_v2."
|
||||
"EdgeLoadbalancerDriverV2._check_lb_service_on_router_interface")
|
||||
self.lb_mock2.start()
|
||||
|
||||
super(L3NatTest, self).setUp(
|
||||
plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins)
|
||||
@ -1495,19 +1499,23 @@ class TestL3NatTestCase(L3NatTest,
|
||||
self.skipTest('not supported')
|
||||
|
||||
def test_router_delete_with_lb_service(self):
|
||||
self.lb_mock.stop()
|
||||
self.lb_mock1.stop()
|
||||
self.lb_mock2.stop()
|
||||
# Create the LB object - here the delete callback is registered
|
||||
lb_driver = lb_driver_v2.EdgeLoadbalancerDriverV2()
|
||||
with self.router() as router:
|
||||
with mock.patch('vmware_nsxlib.v3.load_balancer.Service.'
|
||||
'get_router_lb_service'):
|
||||
'get_router_lb_service'),\
|
||||
mock.patch('vmware_nsx.db.db.get_nsx_router_id',
|
||||
return_value=1):
|
||||
self.assertRaises(nc_exc.CallbackFailure,
|
||||
self.plugin_instance.delete_router,
|
||||
context.get_admin_context(),
|
||||
router['router']['id'])
|
||||
# Unregister callback
|
||||
lb_driver._unsubscribe_router_delete_callback()
|
||||
self.lb_mock.start()
|
||||
self.lb_mock1.start()
|
||||
self.lb_mock2.start()
|
||||
|
||||
def test_multiple_subnets_on_different_routers(self):
|
||||
with self.network() as network:
|
||||
|
Loading…
Reference in New Issue
Block a user