diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py index 4a38142aef0..19a707f96c5 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py @@ -2096,6 +2096,30 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, skipped_devices = set() binding_no_activated_devices = set() start = time.time() + if re_added: + # NOTE(slaweq): to make sure that devices which were deleted and + # then quickly added back (e.g. during rebuild of the VM by nova), + # we will update all such devices to be DOWN first. That way when + # we will process those devices as added/updated later in + # process_network_ports, we can be sure that update state to UP + # will trigger send notification about port status to Nova + re_added_devices = self.plugin_rpc.update_device_list( + context=self.context, + devices_up=[], + devices_down=re_added, + agent_id=self.agent_id, + host=self.conf.host) + failed_re_added_down = re_added_devices.get('failed_devices_down') + if failed_re_added_down: + LOG.debug("Status updated failed for re_added devices: %s", + failed_re_added_down) + LOG.info("process_network_ports - iteration:%(iter_num)d - " + "reporting re_added devices DOWN completed. " + "Number of readded devices: %(re_added)s. " + "Time elapsed: %(elapsed).3f", + {'iter_num': self.iter_num, + 're_added': len(re_added), + 'elapsed': time.time() - start}) if devices_added_updated: (skipped_devices, binding_no_activated_devices, need_binding_devices, failed_devices['added']) = ( diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py index 07eab84ca36..f3253945b53 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py @@ -1126,6 +1126,7 @@ class TestOvsNeutronAgent(object): skipped_devices = skipped_devices or [] binding_no_activated_devices = binding_no_activated_devices or set() added_devices = port_info.get('added', set()) + re_added_devices = port_info.get('re_added', set()) with mock.patch.object(self.agent.sg_agent, "setup_port_filters") as setup_port_filters,\ mock.patch.object( @@ -1143,7 +1144,9 @@ class TestOvsNeutronAgent(object): mock.patch.object(self.agent, "treat_devices_skipped", return_value=( - skipped_devices)) as device_skipped: + skipped_devices)) as device_skipped,\ + mock.patch.object(self.agent.plugin_rpc, + 'update_device_list') as update_device_list: self.assertEqual( failed_devices, self.agent.process_network_ports(port_info, False)) @@ -1155,11 +1158,20 @@ class TestOvsNeutronAgent(object): port_info.get('updated', set())) if devices_added_updated: device_added_updated.assert_called_once_with( - devices_added_updated, False, set()) + devices_added_updated, False, re_added_devices) if port_info.get('removed', set()): device_removed.assert_called_once_with(port_info['removed']) if skipped_devices: device_skipped.assert_called_once_with(set(skipped_devices)) + if port_info.get('re_added'): + update_device_list.assert_called_once_with( + context=self.agent.context, + devices_up=[], + devices_down=port_info['re_added'], + agent_id=self.agent.agent_id, + host=self.agent.conf.host) + else: + update_device_list.assert_not_called() def test_process_network_ports(self): self._test_process_network_ports( @@ -1191,6 +1203,13 @@ class TestOvsNeutronAgent(object): def test_process_network_port_with_empty_port(self): self._test_process_network_ports({}) + def test_process_network_ports_with_re_added_ports(self): + self._test_process_network_ports( + {'current': set(['tap0']), + 'removed': set([]), + 'added': set(['eth1']), + 're_added': set(['eth1'])}) + def test_hybrid_plug_flag_based_on_firewall(self): cfg.CONF.set_default( 'firewall_driver',