From 1763c80711993c55f4f13afe56f449b1dd6d3d3a Mon Sep 17 00:00:00 2001 From: Oleg Bondarev Date: Wed, 23 Oct 2013 12:54:38 +0400 Subject: [PATCH] Delete disassociated floating ips on external network deletion Also remove redundant check for network in use in bigswitch plugin Closes-Bug: #1238439 Closes-Bug: #1259144 Change-Id: I7586f43c2e99be9df491c68bf1e8658994ffd263 --- neutron/db/external_net_db.py | 8 +++++++ neutron/db/l3_db.py | 8 +++++++ neutron/plugins/bigswitch/plugin.py | 24 ++++++++++--------- neutron/plugins/brocade/NeutronPlugin.py | 1 + .../plugins/cisco/n1kv/n1kv_neutron_plugin.py | 2 ++ .../plugins/hyperv/hyperv_neutron_plugin.py | 1 + neutron/plugins/ibm/sdnve_neutron_plugin.py | 6 ++++- .../plugins/linuxbridge/lb_neutron_plugin.py | 1 + neutron/plugins/midonet/plugin.py | 4 +++- neutron/plugins/ml2/plugin.py | 2 ++ neutron/plugins/mlnx/mlnx_plugin.py | 1 + neutron/plugins/nec/nec_plugin.py | 13 +++++++--- neutron/plugins/nuage/plugin.py | 12 ++++++---- neutron/plugins/oneconvergence/plugin.py | 1 + .../plugins/openvswitch/ovs_neutron_plugin.py | 1 + .../plumgrid_plugin/plumgrid_plugin.py | 1 + neutron/plugins/ryu/ryu_neutron_plugin.py | 1 + neutron/plugins/vmware/plugins/base.py | 5 +++- neutron/tests/unit/metaplugin/fake_plugin.py | 5 +++- neutron/tests/unit/test_extension_ext_net.py | 13 ++++++++++ neutron/tests/unit/test_l3_plugin.py | 12 ++++++++++ 21 files changed, 99 insertions(+), 23 deletions(-) diff --git a/neutron/db/external_net_db.py b/neutron/db/external_net_db.py index 96602eca63..8fe1b91a46 100644 --- a/neutron/db/external_net_db.py +++ b/neutron/db/external_net_db.py @@ -27,6 +27,8 @@ from neutron.db import db_base_plugin_v2 from neutron.db import model_base from neutron.db import models_v2 from neutron.extensions import external_net +from neutron import manager +from neutron.plugins.common import constants as service_constants DEVICE_OWNER_ROUTER_GW = l3_constants.DEVICE_OWNER_ROUTER_GW @@ -137,6 +139,12 @@ class External_net_db_mixin(object): network_id=net_id).delete() net_data[external_net.EXTERNAL] = False + def _process_l3_delete(self, context, network_id): + l3plugin = manager.NeutronManager.get_service_plugins().get( + service_constants.L3_ROUTER_NAT) + if l3plugin: + l3plugin.delete_disassociated_floatingips(context, network_id) + def _filter_nets_l3(self, context, nets, filters): vals = filters and filters.get(external_net.EXTERNAL, []) if not vals: diff --git a/neutron/db/l3_db.py b/neutron/db/l3_db.py index 6096b19d3c..40789817b1 100644 --- a/neutron/db/l3_db.py +++ b/neutron/db/l3_db.py @@ -718,6 +718,14 @@ class L3_NAT_db_mixin(l3.RouterPluginBase): marker_obj=marker_obj, page_reverse=page_reverse) + def delete_disassociated_floatingips(self, context, network_id): + query = self._model_query(context, FloatingIP) + query = query.filter_by(floating_network_id=network_id, + fixed_port_id=None, + router_id=None) + for fip in query: + self.delete_floatingip(context, fip.id) + def get_floatingips_count(self, context, filters=None): return self._get_collection_count(context, FloatingIP, filters=filters) diff --git a/neutron/plugins/bigswitch/plugin.py b/neutron/plugins/bigswitch/plugin.py index a5ba320813..7377598a08 100644 --- a/neutron/plugins/bigswitch/plugin.py +++ b/neutron/plugins/bigswitch/plugin.py @@ -609,18 +609,8 @@ class NeutronRestProxyV2(NeutronRestProxyV2Base, # Validate args orig_net = super(NeutronRestProxyV2, self).get_network(context, net_id) - - filter = {'network_id': [net_id]} - ports = self.get_ports(context, filters=filter) - - # check if there are any tenant owned ports in-use - auto_delete_port_owners = db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS - only_auto_del = all(p['device_owner'] in auto_delete_port_owners - for p in ports) - - if not only_auto_del: - raise exceptions.NetworkInUse(net_id=net_id) with context.session.begin(subtransactions=True): + self._process_l3_delete(context, net_id) ret_val = super(NeutronRestProxyV2, self).delete_network(context, net_id) self._send_delete_network(orig_net, context) @@ -1075,6 +1065,18 @@ class NeutronRestProxyV2(NeutronRestProxyV2Base, port_id) self._send_floatingip_update(context) + # overriding method from l3_db as original method calls + # self.delete_floatingip() which in turn calls self.delete_port() which + # is locked with 'bsn-port-barrier' + def delete_disassociated_floatingips(self, context, network_id): + query = self._model_query(context, l3_db.FloatingIP) + query = query.filter_by(floating_network_id=network_id, + fixed_port_id=None, + router_id=None) + for fip in query: + context.session.delete(fip) + self._delete_port(context.elevated(), fip['floating_port_id']) + def _send_floatingip_update(self, context): try: ext_net_id = self.get_external_network_id(context) diff --git a/neutron/plugins/brocade/NeutronPlugin.py b/neutron/plugins/brocade/NeutronPlugin.py index ed27f2881a..3190c0ef3c 100644 --- a/neutron/plugins/brocade/NeutronPlugin.py +++ b/neutron/plugins/brocade/NeutronPlugin.py @@ -318,6 +318,7 @@ class BrocadePluginV2(db_base_plugin_v2.NeutronDbPluginV2, """ with context.session.begin(subtransactions=True): + self._process_l3_delete(context, net_id) result = super(BrocadePluginV2, self).delete_network(context, net_id) # we must delete all ports in db first (foreign key constraint) diff --git a/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py b/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py index 028808b9f2..a2004764d9 100644 --- a/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py +++ b/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py @@ -1100,6 +1100,8 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2, # the network_binding record is deleted via cascade from # the network record, so explicit removal is not necessary self._send_delete_network_request(context, network) + + self._process_l3_delete(context, id) super(N1kvNeutronPluginV2, self).delete_network(context, id) LOG.debug(_("Deleted network: %s"), id) diff --git a/neutron/plugins/hyperv/hyperv_neutron_plugin.py b/neutron/plugins/hyperv/hyperv_neutron_plugin.py index b9ccca2944..e18bad8c72 100644 --- a/neutron/plugins/hyperv/hyperv_neutron_plugin.py +++ b/neutron/plugins/hyperv/hyperv_neutron_plugin.py @@ -276,6 +276,7 @@ class HyperVNeutronPlugin(agents_db.AgentDbMixin, session = context.session with session.begin(subtransactions=True): binding = self._db.get_network_binding(session, id) + self._process_l3_delete(context, id) super(HyperVNeutronPlugin, self).delete_network(context, id) p = self._network_providers_map[binding.network_type] p.delete_network(session, binding) diff --git a/neutron/plugins/ibm/sdnve_neutron_plugin.py b/neutron/plugins/ibm/sdnve_neutron_plugin.py index fda586eb7e..6b2178001f 100644 --- a/neutron/plugins/ibm/sdnve_neutron_plugin.py +++ b/neutron/plugins/ibm/sdnve_neutron_plugin.py @@ -236,7 +236,11 @@ class SdnvePluginV2(db_base_plugin_v2.NeutronDbPluginV2, @_ha def delete_network(self, context, id): LOG.debug(_("Delete network in progress: %s"), id) - super(SdnvePluginV2, self).delete_network(context, id) + session = context.session + + with session.begin(subtransactions=True): + self._process_l3_delete(context, id) + super(SdnvePluginV2, self).delete_network(context, id) (res, data) = self.sdnve_client.sdnve_delete('network', id) if res not in constants.HTTP_ACCEPTABLE: diff --git a/neutron/plugins/linuxbridge/lb_neutron_plugin.py b/neutron/plugins/linuxbridge/lb_neutron_plugin.py index 52fbdcaddd..1a9c7367ed 100644 --- a/neutron/plugins/linuxbridge/lb_neutron_plugin.py +++ b/neutron/plugins/linuxbridge/lb_neutron_plugin.py @@ -444,6 +444,7 @@ class LinuxBridgePluginV2(db_base_plugin_v2.NeutronDbPluginV2, session = context.session with session.begin(subtransactions=True): binding = db.get_network_binding(session, id) + self._process_l3_delete(context, id) super(LinuxBridgePluginV2, self).delete_network(context, id) if binding.vlan_id != constants.LOCAL_VLAN_ID: db.release_network(session, binding.physical_network, diff --git a/neutron/plugins/midonet/plugin.py b/neutron/plugins/midonet/plugin.py index 5188ba0363..baa3da0f80 100644 --- a/neutron/plugins/midonet/plugin.py +++ b/neutron/plugins/midonet/plugin.py @@ -516,7 +516,9 @@ class MidonetPluginV2(db_base_plugin_v2.NeutronDbPluginV2, LOG.debug(_("MidonetPluginV2.delete_network called: id=%r"), id) self.client.delete_bridge(id) try: - super(MidonetPluginV2, self).delete_network(context, id) + with context.session.begin(subtransactions=True): + self._process_l3_delete(context, id) + super(MidonetPluginV2, self).delete_network(context, id) except Exception: with excutils.save_and_reraise_exception(): LOG.error(_('Failed to delete neutron db, while Midonet ' diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index 4080ea07e5..7284c49224 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -444,6 +444,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, while True: try: with session.begin(subtransactions=True): + self._process_l3_delete(context, id) + # Get ports to auto-delete. ports = (session.query(models_v2.Port). enable_eagerloads(False). diff --git a/neutron/plugins/mlnx/mlnx_plugin.py b/neutron/plugins/mlnx/mlnx_plugin.py index 920038f5bc..48e8ae7959 100644 --- a/neutron/plugins/mlnx/mlnx_plugin.py +++ b/neutron/plugins/mlnx/mlnx_plugin.py @@ -379,6 +379,7 @@ class MellanoxEswitchPlugin(db_base_plugin_v2.NeutronDbPluginV2, session = context.session with session.begin(subtransactions=True): binding = db.get_network_binding(session, net_id) + self._process_l3_delete(context, net_id) super(MellanoxEswitchPlugin, self).delete_network(context, net_id) if binding.segmentation_id != constants.LOCAL_VLAN_ID: diff --git a/neutron/plugins/nec/nec_plugin.py b/neutron/plugins/nec/nec_plugin.py index 94220c4e42..d0f45608c0 100644 --- a/neutron/plugins/nec/nec_plugin.py +++ b/neutron/plugins/nec/nec_plugin.py @@ -373,17 +373,24 @@ class NECPluginV2(db_base_plugin_v2.NeutronDbPluginV2, tenant_id = net_db['tenant_id'] ports = self.get_ports(context, filters={'network_id': [id]}) - # check if there are any tenant owned ports in-use + # check if there are any tenant owned ports in-use; + # consider ports owned by floating ips as auto_delete as if there are + # no other tenant owned ports, those floating ips are disassociated + # and will be auto deleted with self._process_l3_delete() only_auto_del = all(p['device_owner'] in - db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS + db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS or + p['device_owner'] == const.DEVICE_OWNER_FLOATINGIP for p in ports) if not only_auto_del: raise n_exc.NetworkInUse(net_id=id) + self._process_l3_delete(context, id) + # Make sure auto-delete ports on OFC are deleted. # If an error occurs during port deletion, # delete_network will be aborted. - for port in ports: + for port in [p for p in ports if p['device_owner'] + in db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS]: port = self.deactivate_port(context, port) # delete all packet_filters of the network from the controller diff --git a/neutron/plugins/nuage/plugin.py b/neutron/plugins/nuage/plugin.py index 9821048ab9..294cb17b16 100644 --- a/neutron/plugins/nuage/plugin.py +++ b/neutron/plugins/nuage/plugin.py @@ -304,11 +304,13 @@ class NuagePlugin(db_base_plugin_v2.NeutronDbPluginV2, return net def delete_network(self, context, id): - filter = {'network_id': [id]} - subnets = self.get_subnets(context, filters=filter) - for subnet in subnets: - self.delete_subnet(context, subnet['id']) - super(NuagePlugin, self).delete_network(context, id) + with context.session.begin(subtransactions=True): + self._process_l3_delete(context, id) + filter = {'network_id': [id]} + subnets = self.get_subnets(context, filters=filter) + for subnet in subnets: + self.delete_subnet(context, subnet['id']) + super(NuagePlugin, self).delete_network(context, id) def _get_net_partition_for_subnet(self, context, subnet): subn = subnet['subnet'] diff --git a/neutron/plugins/oneconvergence/plugin.py b/neutron/plugins/oneconvergence/plugin.py index daf85ee6d9..2b1b6edcef 100644 --- a/neutron/plugins/oneconvergence/plugin.py +++ b/neutron/plugins/oneconvergence/plugin.py @@ -219,6 +219,7 @@ class OneConvergencePluginV2(db_base_plugin_v2.NeutronDbPluginV2, #get all the subnets under the network to delete them subnets = self._get_subnets_by_network(context, net_id) + self._process_l3_delete(context, net_id) super(OneConvergencePluginV2, self).delete_network(context, net_id) diff --git a/neutron/plugins/openvswitch/ovs_neutron_plugin.py b/neutron/plugins/openvswitch/ovs_neutron_plugin.py index 2cfe2674b6..5987e0d198 100644 --- a/neutron/plugins/openvswitch/ovs_neutron_plugin.py +++ b/neutron/plugins/openvswitch/ovs_neutron_plugin.py @@ -526,6 +526,7 @@ class OVSNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2, session = context.session with session.begin(subtransactions=True): binding = ovs_db_v2.get_network_binding(session, id) + self._process_l3_delete(context, id) super(OVSNeutronPluginV2, self).delete_network(context, id) if binding.network_type in constants.TUNNEL_NETWORK_TYPES: ovs_db_v2.release_tunnel(session, binding.segmentation_id, diff --git a/neutron/plugins/plumgrid/plumgrid_plugin/plumgrid_plugin.py b/neutron/plugins/plumgrid/plumgrid_plugin/plumgrid_plugin.py index 2e41aadae1..619dd1e63d 100644 --- a/neutron/plugins/plumgrid/plumgrid_plugin/plumgrid_plugin.py +++ b/neutron/plugins/plumgrid/plumgrid_plugin/plumgrid_plugin.py @@ -156,6 +156,7 @@ class NeutronPluginPLUMgridV2(db_base_plugin_v2.NeutronDbPluginV2, self).get_network(context, net_id) with context.session.begin(subtransactions=True): + self._process_l3_delete(context, net_id) # Plugin DB - Network Delete super(NeutronPluginPLUMgridV2, self).delete_network(context, net_id) diff --git a/neutron/plugins/ryu/ryu_neutron_plugin.py b/neutron/plugins/ryu/ryu_neutron_plugin.py index 9259d771ff..2279abbc42 100644 --- a/neutron/plugins/ryu/ryu_neutron_plugin.py +++ b/neutron/plugins/ryu/ryu_neutron_plugin.py @@ -202,6 +202,7 @@ class RyuNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2, session = context.session with session.begin(subtransactions=True): self.tunnel_key.delete(session, id) + self._process_l3_delete(context, id) super(RyuNeutronPluginV2, self).delete_network(context, id) def create_port(self, context, port): diff --git a/neutron/plugins/vmware/plugins/base.py b/neutron/plugins/vmware/plugins/base.py index 457fa8bc04..12a4cffa0a 100644 --- a/neutron/plugins/vmware/plugins/base.py +++ b/neutron/plugins/vmware/plugins/base.py @@ -1029,7 +1029,10 @@ class NsxPluginV2(addr_pair_db.AllowedAddressPairsMixin, if not external: lswitch_ids = nsx_utils.get_nsx_switch_ids( context.session, self.cluster, id) - super(NsxPluginV2, self).delete_network(context, id) + with context.session.begin(subtransactions=True): + self._process_l3_delete(context, id) + super(NsxPluginV2, self).delete_network(context, id) + # clean up network owned ports for port in router_iface_ports: try: diff --git a/neutron/tests/unit/metaplugin/fake_plugin.py b/neutron/tests/unit/metaplugin/fake_plugin.py index 6653ea25b5..1ba3c15609 100644 --- a/neutron/tests/unit/metaplugin/fake_plugin.py +++ b/neutron/tests/unit/metaplugin/fake_plugin.py @@ -43,7 +43,10 @@ class Fake1(db_base_plugin_v2.NeutronDbPluginV2, return net def delete_network(self, context, id): - return super(Fake1, self).delete_network(context, id) + session = context.session + with session.begin(subtransactions=True): + self._process_l3_delete(context, id) + return super(Fake1, self).delete_network(context, id) def create_port(self, context, port): port = super(Fake1, self).create_port(context, port) diff --git a/neutron/tests/unit/test_extension_ext_net.py b/neutron/tests/unit/test_extension_ext_net.py index 24578b8690..1a8db1d2f0 100644 --- a/neutron/tests/unit/test_extension_ext_net.py +++ b/neutron/tests/unit/test_extension_ext_net.py @@ -18,6 +18,7 @@ import contextlib import itertools +import mock import testtools from webob import exc @@ -158,6 +159,18 @@ class ExtNetDBTestCase(test_db_plugin.NeutronDbPluginV2TestCase): self.assertEqual(ext_net['network'][external_net.EXTERNAL], True) + def test_delete_network_check_disassociated_floatingips(self): + with mock.patch.object(NeutronManager, + 'get_service_plugins') as srv_plugins: + l3_mock = mock.Mock() + srv_plugins.return_value = {'L3_ROUTER_NAT': l3_mock} + with self.network(do_delete=False) as net: + req = self.new_delete_request('networks', net['network']['id']) + res = req.get_response(self.api) + self.assertEqual(res.status_int, exc.HTTPNoContent.code) + (l3_mock.delete_disassociated_floatingips + .assert_called_once_with(mock.ANY, net['network']['id'])) + class ExtNetDBTestCaseXML(ExtNetDBTestCase): fmt = 'xml' diff --git a/neutron/tests/unit/test_l3_plugin.py b/neutron/tests/unit/test_l3_plugin.py index 05aa3220ec..c968aa5518 100644 --- a/neutron/tests/unit/test_l3_plugin.py +++ b/neutron/tests/unit/test_l3_plugin.py @@ -234,6 +234,11 @@ class TestL3NatBasePlugin(db_base_plugin_v2.NeutronDbPluginV2, self._process_l3_update(context, net, network['network']) return net + def delete_network(self, context, id): + with context.session.begin(subtransactions=True): + self._process_l3_delete(context, id) + super(TestL3NatBasePlugin, self).delete_network(context, id) + def delete_port(self, context, id, l3_port_check=True): plugin = NeutronManager.get_service_plugins().get( service_constants.L3_ROUTER_NAT) @@ -1618,6 +1623,13 @@ class L3NatTestCaseBase(L3NatTestCaseMixin): s['subnet']['id'], None) + def test_delete_ext_net_with_disassociated_floating_ips(self): + with self.network() as net: + net_id = net['network']['id'] + self._set_net_external(net_id) + with self.subnet(network=net, do_delete=False): + self._make_floatingip(self.fmt, net_id) + class L3AgentDbTestCaseBase(L3NatTestCaseMixin):