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

This patch fixes this issue by allowing to transition to
ACTIVE only ports which are administratively enabled.

Change-Id: If506e0ff68fc49748f19618470c85901339a419b
Closes-Bug: #1757089
(cherry picked from commit 2a1319ab7a)
Sławek Kapłoński 5 years ago committed by Slawek Kaplonski
parent 8dc15cc053
commit eb7ee4d8eb

@ -253,6 +253,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)
if not port.admin_state_up:
LOG.debug("Port %s is administratively disabled so it will "
"not transition to active.", port_id)
self.update_port_status(context, port_id, const.PORT_STATUS_ACTIVE)

@ -720,6 +720,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',
plugin._port_provisioned('port', 'evt', 'trigger',
self.context, port_id)
def test_port_after_create_outside_transaction(self):
self.tx_open = True
receive = lambda *a, **k: setattr(self, 'tx_open',