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
(cherry picked from commit 2a1319ab7a)
This commit is contained in:
Sławek Kapłoński 2018-03-21 15:28:51 +01:00 committed by Slawek Kaplonski
parent 1afa908c7f
commit 39b9197f6f
2 changed files with 17 additions and 0 deletions

View File

@ -237,6 +237,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

View File

@ -691,6 +691,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_create_router_port_and_fail_create_postcommit(self):
with mock.patch.object(managers.MechanismManager,