Skip to content

Commit 897b474

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Add an active wait during the port provisioning event" into stable/yoga
2 parents 6aee0e5 + 2d2d650 commit 897b474

File tree

3 files changed

+79
-25
lines changed

3 files changed

+79
-25
lines changed

neutron/plugins/ml2/plugin.py

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145
LOG = log.getLogger(__name__)
146146

147147
MAX_BIND_TRIES = 10
148+
MAX_PROVISIONING_TRIES = MAX_BIND_TRIES
148149

149150

150151
SERVICE_PLUGINS_REQUIRED_DRIVERS = {
@@ -331,37 +332,55 @@ def _filter_extensions_by_mech_driver(self, aliases):
331332
[provisioning_blocks.PROVISIONING_COMPLETE])
332333
def _port_provisioned(self, rtype, event, trigger, payload=None):
333334
port_id = payload.resource_id
334-
port = db.get_port(payload.context, port_id)
335-
port_binding = p_utils.get_port_binding_by_status_and_host(
336-
getattr(port, 'port_bindings', []), const.ACTIVE)
337-
if not port or not port_binding:
338-
LOG.debug("Port %s was deleted so its status cannot be updated.",
339-
port_id)
340-
return
341-
if port_binding.vif_type in (portbindings.VIF_TYPE_BINDING_FAILED,
342-
portbindings.VIF_TYPE_UNBOUND):
343-
# NOTE(kevinbenton): we hit here when a port is created without
344-
# a host ID and the dhcp agent notifies that its wiring is done
345-
LOG.debug("Port %s cannot update to ACTIVE because it "
346-
"is not bound.", port_id)
347-
return
348-
else:
349-
# port is bound, but we have to check for new provisioning blocks
350-
# one last time to detect the case where we were triggered by an
351-
# unbound port and the port became bound with new provisioning
352-
# blocks before 'get_port' was called above
353-
if provisioning_blocks.is_object_blocked(payload.context, port_id,
354-
resources.PORT):
355-
LOG.debug("Port %s had new provisioning blocks added so it "
356-
"will not transition to active.", port_id)
335+
for count in range(1, MAX_PROVISIONING_TRIES + 1):
336+
LOG.info('Attempt %(count)s to provision port %(port)s',
337+
{'count': count, 'port': port_id})
338+
port = db.get_port(payload.context, port_id)
339+
port_bindings = getattr(port, 'port_bindings', [])
340+
port_binding = p_utils.get_port_binding_by_status_and_host(
341+
port_bindings, const.ACTIVE)
342+
343+
if not port or not port_binding:
344+
LOG.debug("Port %s was deleted so its status cannot be "
345+
"updated.", port_id)
357346
return
347+
348+
if port_binding.vif_type == portbindings.VIF_TYPE_BINDING_FAILED:
349+
LOG.debug('Port %s cannot update to ACTIVE because it failed.',
350+
port_id)
351+
return
352+
353+
if port_binding.vif_type == portbindings.VIF_TYPE_UNBOUND:
354+
# NOTE(kevinbenton): we hit here when a port is created without
355+
# a host ID and the dhcp agent notifies that its wiring is done
356+
LOG.debug('Port %s cannot update to ACTIVE because it '
357+
'is not bound.', port_id)
358+
if count == MAX_PROVISIONING_TRIES:
359+
return
360+
361+
# Wait 0.5 seconds before checking again if the port is bound.
362+
# We could hit this during a live-migration.
363+
greenthread.sleep(0.5)
364+
continue
365+
366+
break
367+
368+
# port is bound, but we have to check for new provisioning blocks
369+
# one last time to detect the case where we were triggered by an
370+
# unbound port and the port became bound with new provisioning
371+
# blocks before 'get_port' was called above
372+
if provisioning_blocks.is_object_blocked(payload.context, port_id,
373+
resources.PORT):
374+
LOG.debug("Port %s had new provisioning blocks added so it "
375+
"will not transition to active.", port_id)
376+
return
377+
358378
if not port.admin_state_up:
359379
LOG.debug("Port %s is administratively disabled so it will "
360380
"not transition to active.", port_id)
361381
return
362382

363-
host_migrating = agent_rpc.migrating_to_host(
364-
getattr(port, 'port_bindings', []))
383+
host_migrating = agent_rpc.migrating_to_host(port_bindings)
365384
if (host_migrating and cfg.CONF.nova.live_migration_events and
366385
self.nova_notifier):
367386
send_nova_event = bool(trigger ==

neutron/tests/unit/plugins/ml2/test_plugin.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import webob
4848

4949
from neutron._i18n import _
50+
from neutron.agent import rpc as agent_rpc
5051
from neutron.common import utils
5152
from neutron.db import agents_db
5253
from neutron.db import provisioning_blocks
@@ -1112,6 +1113,32 @@ def getitem(key):
11121113
self.context, resource_id=port_id))
11131114
self.assertFalse(ups.called)
11141115

1116+
@staticmethod
1117+
def _set_max_provisioning_tries():
1118+
ml2_plugin.MAX_PROVISIONING_TRIES = ml2_plugin.MAX_BIND_TRIES
1119+
1120+
@mock.patch.object(agent_rpc, 'migrating_to_host', return_value=None)
1121+
@mock.patch('neutron.plugins.ml2.plugin.db.get_port')
1122+
@mock.patch.object(p_utils, 'get_port_binding_by_status_and_host')
1123+
def test__port_provisioned_port_retry_port_binding_unbound(
1124+
self, mock_get_pb, mock_get_port, *args):
1125+
self.addCleanup(self._set_max_provisioning_tries)
1126+
ml2_plugin.MAX_PROVISIONING_TRIES = 2
1127+
plugin = directory.get_plugin()
1128+
port_id = 'fake_port_id'
1129+
port = mock.Mock(id=port_id, admin_state_up=True)
1130+
mock_get_port.return_value = port
1131+
with mock.patch.object(plugin, 'update_port_status') as mock_pstatus:
1132+
pb1 = mock.MagicMock(vif_type=portbindings.VIF_TYPE_UNBOUND)
1133+
pb2 = mock.MagicMock(vif_type=portbindings.VIF_TYPE_OVS)
1134+
pb2.__iter__.return_value = []
1135+
mock_get_pb.side_effect = [pb1, pb2]
1136+
plugin._port_provisioned('port', 'evt', 'trigger',
1137+
payload=events.DBEventPayload(
1138+
self.context, resource_id=port_id))
1139+
mock_pstatus.assert_called_once_with(self.context, port_id,
1140+
constants.PORT_STATUS_ACTIVE)
1141+
11151142
def test_port_after_create_outside_transaction(self):
11161143
self.tx_open = True
11171144

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
features:
3+
- |
4+
After the port is considered as provisioned, the Nova port binding update
5+
could have not been received, leaving the port as not bound. Now the
6+
port provisioning method has an active wait that will retry several times,
7+
waiting for the port binding update. If received, the port status will be
8+
set as active if the admin state flag is set.

0 commit comments

Comments
 (0)