From 19804bb5c5ad8ba10fbda1bf213f698d97c137e7 Mon Sep 17 00:00:00 2001 From: Bob Kukura Date: Fri, 27 Sep 2013 17:54:45 -0400 Subject: [PATCH] Fix auto-deletion of ports and subnets in ML2 When a network is deleted, certain ports and any subnets referencing it are auto-deleted. The implementation of NeutronDBPluginV2.delete_network() does this at the DB level, so ML2's mechanism drivers were not being called. Ml2Plugin.delete_network() is changed to not use the base class's method, and to auto-delete ports and subnets by calling its own delete_port() and delete_subnet() methods outside of the transaction. A loop avoids race conditions with ports or subnets being asynchronously added to the network. Closes-Bug: 1230330 Change-Id: Icf21400c9938eec29d70da8497b9ef92642131e2 --- neutron/plugins/ml2/plugin.py | 56 ++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index fbdfb38ca..3c8a7d5f2 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -368,17 +368,53 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, return [self._fields(net, fields) for net in nets] def delete_network(self, context, id): + # REVISIT(rkukura) The super(Ml2Plugin, self).delete_network() + # function is not used because it auto-deletes ports and + # subnets from the DB without invoking the derived class's + # delete_port() or delete_subnet(), preventing mechanism + # drivers from being called. This approach should be revisited + # when the API layer is reworked during icehouse. + session = context.session - with session.begin(subtransactions=True): - network = self.get_network(context, id) - mech_context = driver_context.NetworkContext(self, context, - network) - self.mechanism_manager.delete_network_precommit(mech_context) - super(Ml2Plugin, self).delete_network(context, id) - for segment in mech_context.network_segments: - self.type_manager.release_segment(session, segment) - # The segment records are deleted via cascade from the - # network record, so explicit removal is not necessary. + while True: + with session.begin(subtransactions=True): + filter = {'network_id': [id]} + + # Get ports to auto-delete. + ports = self.get_ports(context, filters=filter) + only_auto_del = all(p['device_owner'] + in db_base_plugin_v2. + AUTO_DELETE_PORT_OWNERS + for p in ports) + if not only_auto_del: + raise exc.NetworkInUse(net_id=id) + + # Get subnets to auto-delete. + subnets = self.get_subnets(context, filters=filter) + + if not ports or subnets: + network = self.get_network(context, id) + mech_context = driver_context.NetworkContext(self, + context, + network) + self.mechanism_manager.delete_network_precommit( + mech_context) + + record = self._get_network(context, id) + context.session.delete(record) + + for segment in mech_context.network_segments: + self.type_manager.release_segment(session, segment) + + # The segment records are deleted via cascade from the + # network record, so explicit removal is not necessary. + break + + for port in ports: + self.delete_port(context, port['id']) + + for subnet in subnets: + self.delete_subnet(context, subnet['id']) try: self.mechanism_manager.delete_network_postcommit(mech_context) -- 2.45.2