diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index 411002dc7b3..bc719288cdf 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -1172,6 +1172,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, # fact that an error occurred. LOG.error(_LE("mechanism_manager.delete_port_postcommit failed for" " port %s"), id) + self.notifier.port_delete(context, id) self.notify_security_groups_member_updated(context, port) def get_bound_port_context(self, plugin_context, port_id, host=None): diff --git a/neutron/plugins/ml2/rpc.py b/neutron/plugins/ml2/rpc.py index b08d06be169..6595c8291b9 100644 --- a/neutron/plugins/ml2/rpc.py +++ b/neutron/plugins/ml2/rpc.py @@ -198,6 +198,10 @@ class AgentNotifierApi(dvr_rpc.DVRAgentRpcApiMixin, self.topic_port_update = topics.get_topic_name(topic, topics.PORT, topics.UPDATE) + self.topic_port_delete = topics.get_topic_name(topic, + topics.PORT, + topics.DELETE) + target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) @@ -213,3 +217,8 @@ class AgentNotifierApi(dvr_rpc.DVRAgentRpcApiMixin, cctxt.cast(context, 'port_update', port=port, network_type=network_type, segmentation_id=segmentation_id, physical_network=physical_network) + + def port_delete(self, context, port_id): + cctxt = self.client.prepare(topic=self.topic_port_delete, + fanout=True) + cctxt.cast(context, 'port_delete', port_id=port_id) diff --git a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py index b1a3565bb7c..b3199421be2 100644 --- a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py @@ -294,6 +294,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, self.endpoints = [self] # Define the listening consumers for the agent consumers = [[topics.PORT, topics.UPDATE], + [topics.PORT, topics.DELETE], [topics.NETWORK, topics.DELETE], [constants.TUNNEL, topics.UPDATE], [topics.SECURITY_GROUP, topics.UPDATE], @@ -331,6 +332,13 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, self.updated_ports.add(port['id']) LOG.debug("port_update message processed for port %s", port['id']) + def port_delete(self, context, **kwargs): + port_id = kwargs.get('port_id') + port = self.int_br.get_vif_port_by_id(port_id) + # If port exists, delete it + if port: + self.int_br.delete_port(port.port_name) + def tunnel_update(self, context, **kwargs): LOG.debug("tunnel_update received") if not self.enable_tunneling: diff --git a/neutron/tests/unit/ml2/test_rpcapi.py b/neutron/tests/unit/ml2/test_rpcapi.py index a3b4955f62a..08978f66e77 100644 --- a/neutron/tests/unit/ml2/test_rpcapi.py +++ b/neutron/tests/unit/ml2/test_rpcapi.py @@ -223,6 +223,16 @@ class RpcApiTestCase(base.BaseTestCase): segmentation_id='fake_segmentation_id', physical_network='fake_physical_network') + def test_port_delete(self): + rpcapi = plugin_rpc.AgentNotifierApi(topics.AGENT) + self._test_rpc_api( + rpcapi, + topics.get_topic_name(topics.AGENT, + topics.PORT, + topics.DELETE), + 'port_delete', rpc_method='cast', + fanout=True, port_id='fake_port') + def test_tunnel_update(self): rpcapi = plugin_rpc.AgentNotifierApi(topics.AGENT) self._test_rpc_api( diff --git a/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py b/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py index 3e124fbf69a..a7d90416ff2 100644 --- a/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py +++ b/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py @@ -498,6 +498,20 @@ class TestOvsNeutronAgent(base.BaseTestCase): physical_network="physnet") self.assertEqual(set(['123']), self.agent.updated_ports) + def test_port_delete(self): + port_id = "123" + port_name = "foo" + with contextlib.nested( + mock.patch.object(self.agent.int_br, 'get_vif_port_by_id', + return_value=mock.MagicMock( + port_name=port_name)), + mock.patch.object(self.agent.int_br, "delete_port") + ) as (get_vif_func, del_port_func): + self.agent.port_delete("unused_context", + port_id=port_id) + self.assertTrue(get_vif_func.called) + del_port_func.assert_called_once_with(port_name) + def test_setup_physical_bridges(self): with contextlib.nested( mock.patch.object(ip_lib, "device_exists"),