From 2a1319ab7a21c2492627c38c5549d20eada899ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awek=20Kap=C5=82o=C5=84ski?= Date: Wed, 21 Mar 2018 15:28:51 +0100 Subject: [PATCH] Don't set administratively disabled ports as ACTIVE There was a race condition during port update to disable it. In case when neutron-server receives port update call to set admin_state_down on port, it sends PORT_UPDATE notification to agents. Then both L2 and DHCP agents start doing their job with port. L2 agent asks neutron-server about device details and during processing this call server sets port's status to DOWN if its admin_state_up = False. Problem is that sometimes just after that, DHCP agent will send to neutron server notification that provisioning for this port is finished. As there is no any other provisioning block in db (because it is just port update) neutron-server is setting port's status to ACTIVE. This patch fixes this issue by allowing to transition to ACTIVE only ports which are administratively enabled. Change-Id: If506e0ff68fc49748f19618470c85901339a419b Closes-Bug: #1757089 --- neutron/plugins/ml2/plugin.py | 4 ++++ neutron/tests/unit/plugins/ml2/test_plugin.py | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index 6b100c0e57f..3b2a72fe53a 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -259,6 +259,10 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, LOG.debug("Port %s had new provisioning blocks added so it " "will not transition to active.", port_id) return + if not port.admin_state_up: + LOG.debug("Port %s is administratively disabled so it will " + "not transition to active.", port_id) + return self.update_port_status(context, port_id, const.PORT_STATUS_ACTIVE) @log_helpers.log_method_call diff --git a/neutron/tests/unit/plugins/ml2/test_plugin.py b/neutron/tests/unit/plugins/ml2/test_plugin.py index 767822da245..d8a930c4eaf 100644 --- a/neutron/tests/unit/plugins/ml2/test_plugin.py +++ b/neutron/tests/unit/plugins/ml2/test_plugin.py @@ -721,6 +721,19 @@ class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase): self.assertIsNone(plugin._port_provisioned('port', 'evt', 'trigger', self.context, port_id)) + def test__port_provisioned_port_admin_state_down(self): + plugin = directory.get_plugin() + ups = mock.patch.object(plugin, 'update_port_status').start() + port_id = 'fake_port_id' + binding = mock.Mock(vif_type=portbindings.VIF_TYPE_OVS) + port = mock.Mock( + id=port_id, admin_state_up=False, port_binding=binding) + with mock.patch('neutron.plugins.ml2.plugin.db.get_port', + return_value=port): + plugin._port_provisioned('port', 'evt', 'trigger', + self.context, port_id) + self.assertFalse(ups.called) + def test_port_after_create_outside_transaction(self): self.tx_open = True receive = lambda *a, **k: setattr(self, 'tx_open',