diff --git a/vmware_nsx/db/nsxv_db.py b/vmware_nsx/db/nsxv_db.py index b64ff4a427..e45558e9fd 100644 --- a/vmware_nsx/db/nsxv_db.py +++ b/vmware_nsx/db/nsxv_db.py @@ -412,6 +412,12 @@ def get_nsxv_internal_networks(session, network_purpose): filter_by(network_purpose=network_purpose).all()) +def get_nsxv_internal_network_by_id(session, network_id): + with session.begin(subtransactions=True): + return (session.query(nsxv_models.NsxvInternalNetworks). + filter_by(network_id=network_id).first()) + + def delete_nsxv_internal_network(session, network_purpose, network_id): with session.begin(subtransactions=True): return (session.query(nsxv_models.NsxvInternalNetworks). @@ -453,6 +459,12 @@ def get_nsxv_internal_edges_by_purpose(session, purpose): filter_by(purpose=purpose).all()) +def get_nsxv_internal_edge_by_router(session, router_id): + with session.begin(subtransactions=True): + return (session.query(nsxv_models.NsxvInternalEdges). + filter_by(router_id=router_id).first()) + + def delete_nsxv_internal_edge(session, ext_ip_address): with session.begin(subtransactions=True): return (session.query(nsxv_models.NsxvInternalEdges). diff --git a/vmware_nsx/plugins/nsx_v/md_proxy.py b/vmware_nsx/plugins/nsx_v/md_proxy.py index ba70de93d6..7582311c6a 100644 --- a/vmware_nsx/plugins/nsx_v/md_proxy.py +++ b/vmware_nsx/plugins/nsx_v/md_proxy.py @@ -524,7 +524,8 @@ class NsxVMetadataProxyHandler(object): for port in ports: self.nsxv_plugin.delete_port(context, port['id'], l3_port_check=True, - nw_gw_port_check=True) + nw_gw_port_check=True, + allow_delete_internal=True) nsxv_db.delete_nsxv_internal_edge( context.session, diff --git a/vmware_nsx/plugins/nsx_v/plugin.py b/vmware_nsx/plugins/nsx_v/plugin.py index 28c7ce1599..b81a815cf5 100644 --- a/vmware_nsx/plugins/nsx_v/plugin.py +++ b/vmware_nsx/plugins/nsx_v/plugin.py @@ -1394,6 +1394,13 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, return True return False + def _validate_internal_network(self, context, network_id): + if nsxv_db.get_nsxv_internal_network_by_id( + context.elevated().session, network_id): + msg = (_("Cannot delete internal network %s or its subnets and " + "ports") % network_id) + raise n_exc.InvalidInput(error_message=msg) + def delete_network(self, context, id): mappings = nsx_db.get_nsx_network_mappings(context.session, id) bindings = nsxv_db.get_network_bindings(context.session, id) @@ -1401,6 +1408,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, sg_policy_id = nsxv_db.get_spoofguard_policy_id( context.session, id) + self._validate_internal_network(context, id) + # Update the DHCP edge for metadata and clean the vnic in DHCP edge # if there is only no other existing port besides DHCP port filters = {'network_id': [id]} @@ -2336,10 +2345,15 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, return db_utils.resource_fields(port, fields) def delete_port(self, context, id, l3_port_check=True, - nw_gw_port_check=True, force_delete_dhcp=False): + nw_gw_port_check=True, force_delete_dhcp=False, + allow_delete_internal=False): neutron_db_port = self.get_port(context, id) device_id = neutron_db_port['device_id'] is_compute_port = self._is_compute_port(neutron_db_port) + if not allow_delete_internal: + self._validate_internal_network( + context, neutron_db_port['network_id']) + if is_compute_port and device_id: # Lock on the device ID to make sure we do not change/delete # ports of the same device at the same time @@ -2374,6 +2388,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, neutron_db_port['device_owner'] in [constants.DEVICE_OWNER_DHCP]): msg = (_('Can not delete DHCP port %s') % neutron_db_port['id']) raise n_exc.BadRequest(resource='port', msg=msg) + # If this port is attached to a device, remove the corresponding vnic # from all NSXv Security-Groups and the spoofguard policy port_index = neutron_db_port.get(ext_vnic_idx.VNIC_INDEX) @@ -2431,6 +2446,8 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, # deleting subnet's corresponding dhcp interface rest call and lead to # overlap response from backend. network_id = subnet['network_id'] + self._validate_internal_network(context, network_id) + with locking.LockManager.get_lock(network_id): with db_api.context_manager.writer.using(context): self.base_delete_subnet(context, id) @@ -3204,6 +3221,11 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, if ports: raise l3_exc.RouterInUse(router_id=router_id) + if nsxv_db.get_nsxv_internal_edge_by_router( + context.elevated().session, router_id): + msg = _("Cannot delete internal router %s") % router_id + raise n_exc.InvalidInput(error_message=msg) + def delete_router(self, context, id): self._check_router_in_use(context, id) router_driver = self._find_router_driver(context, id) diff --git a/vmware_nsx/tests/unit/nsx_v/test_md_proxy.py b/vmware_nsx/tests/unit/nsx_v/test_md_proxy.py index f1b2bc9539..f9b39dff12 100644 --- a/vmware_nsx/tests/unit/nsx_v/test_md_proxy.py +++ b/vmware_nsx/tests/unit/nsx_v/test_md_proxy.py @@ -17,8 +17,12 @@ import mock from oslo_config import cfg +from neutron_lib import context + from vmware_nsx.db import nsxv_db from vmware_nsx.db import nsxv_models +from vmware_nsx.plugins.nsx_v.vshield.common import ( + constants as vcns_const) from vmware_nsx.plugins.nsx_v.vshield import edge_utils from vmware_nsx.tests.unit.nsx_v import test_plugin @@ -53,6 +57,12 @@ class NsxVPluginWithMdV2TestCase(test_plugin.NsxVPluginV2TestCase): plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins) + self.context = context.get_admin_context() + self.internal_net_id = nsxv_db.get_nsxv_internal_network_for_az( + self.context.session, + vcns_const.InternalEdgePurposes.INTER_EDGE_PURPOSE, + 'default')['network_id'] + class TestNetworksWithMdV2(test_plugin.TestNetworksV2, NsxVPluginWithMdV2TestCase): @@ -107,6 +117,11 @@ class TestNetworksWithMdV2(test_plugin.TestNetworksV2, def test_create_networks_bulk_emulated_plugin_failure(self): self.skipTest("The test is not suitable for the metadata test case") + def test_cannot_delete_md_net(self): + req = self.new_delete_request('networks', self.internal_net_id) + net_del_res = req.get_response(self.api) + self.assertEqual(net_del_res.status_int, 400) + class TestSubnetsWithMdV2(test_plugin.TestSubnetsV2, NsxVPluginWithMdV2TestCase): @@ -145,6 +160,16 @@ class TestSubnetsWithMdV2(test_plugin.TestSubnetsV2, def test_create_subnets_bulk_emulated_plugin_failure(self): self.skipTest("The test is not suitable for the metadata test case") + def test_cannot_delete_md_subnet(self): + query_params = "network_id=%s" % self.internal_net_id + res = self._list('subnets', + neutron_context=self.context, + query_params=query_params) + internal_sub = res['subnets'][0]['id'] + req = self.new_delete_request('subnets', internal_sub) + net_del_res = req.get_response(self.api) + self.assertEqual(net_del_res.status_int, 400) + class TestExclusiveRouterWithMdTestCase( test_plugin.TestExclusiveRouterTestCase,