Skip to content

Commit 137949e

Browse files
committed
Search for active port binding when updating LSP host info
When updating LSP host info for an UP port, the code always uses the first port binding in the DB entry (db_port.port_bindings[0].host). Since there could be multiple port bindings present, search for the active one when determining which host value to use. Closes-bug: #2134504 Change-Id: Ia07610a17afb55b802290c8910a17dace7794c4f Signed-off-by: Brian Haley <haleyb.dev@gmail.com>
1 parent a2c59eb commit 137949e

2 files changed

Lines changed: 50 additions & 23 deletions

File tree

neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ def _get_port_dhcp_options(self, port, ip_version):
262262
wait=tenacity.wait_random(min=2, max=3),
263263
stop=tenacity.stop_after_attempt(3),
264264
reraise=True)
265-
def _wait_for_port_bindings_host(self, context, port_id):
265+
def _wait_for_active_port_bindings_host(self, context, port_id):
266266
db_port = ml2_db.get_port(context, port_id)
267267
# This is already checked previously but, just to stay on
268268
# the safe side in case the port is deleted mid-operation
@@ -275,11 +275,18 @@ def _wait_for_port_bindings_host(self, context, port_id):
275275
_('No port bindings information found for '
276276
'port %s') % port_id)
277277

278-
if not db_port.port_bindings[0].host:
278+
active_binding = p_utils.get_port_binding_by_status_and_host(
279+
db_port.port_bindings, const.ACTIVE)
280+
if not active_binding:
281+
raise RuntimeError(
282+
_('No active port bindings information found for '
283+
'port %s') % port_id)
284+
285+
if not active_binding.host:
279286
raise RuntimeError(
280287
_('No hosting information found for port %s') % port_id)
281288

282-
return db_port
289+
return active_binding
283290

284291
def update_lsp_host_info(self, context, db_port, up=True):
285292
"""Update the binding hosting information for the LSP.
@@ -308,18 +315,23 @@ def update_lsp_host_info(self, context, db_port, up=True):
308315
if not db_port.port_bindings:
309316
return
310317

311-
if not db_port.port_bindings[0].host:
318+
# There could be more than one port binding present, we need
319+
# to find the active one
320+
active_binding = p_utils.get_port_binding_by_status_and_host(
321+
db_port.port_bindings, const.ACTIVE)
322+
323+
if not active_binding or not active_binding.host:
312324
# NOTE(lucasgomes): There might be a sync issue between
313325
# the moment that this port was fetched from the database
314326
# and the hosting information being set, retry a few times
315327
try:
316-
db_port = self._wait_for_port_bindings_host(
328+
active_binding = self._wait_for_active_port_bindings_host(
317329
context, db_port.id)
318330
except RuntimeError as e:
319331
LOG.warning(e)
320332
return
321333

322-
host = db_port.port_bindings[0].host
334+
host = active_binding.host
323335
ext_ids = ('external_ids',
324336
{ovn_const.OVN_HOST_ID_EXT_ID_KEY: host})
325337
cmd.append(

neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_client.py

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,13 @@ def setUp(self):
9595
super().setUp()
9696
self.get_plugin = mock.patch(
9797
'neutron_lib.plugins.directory.get_plugin').start()
98+
self.get_pb_bsah = mock.patch(
99+
'neutron_lib.plugins.utils.'
100+
'get_port_binding_by_status_and_host').start()
98101

99102
# Disable tenacity wait for UT
100-
self.ovn_client._wait_for_port_bindings_host.retry.wait = wait_none()
103+
self.ovn_client._wait_for_active_port_bindings_host.retry.wait = (
104+
wait_none())
101105

102106
def test__add_router_ext_gw_default_route(self):
103107
plugin = mock.MagicMock()
@@ -245,8 +249,9 @@ def test_update_lsp_host_info_up(self):
245249
context = mock.MagicMock()
246250
host_id = 'fake-binding-host-id'
247251
port_id = 'fake-port-id'
248-
db_port = mock.Mock(
249-
id=port_id, port_bindings=[mock.Mock(host=host_id)])
252+
port_binding = mock.Mock(host=host_id)
253+
db_port = mock.Mock(id=port_id, port_bindings=[port_binding])
254+
self.get_pb_bsah.return_value = port_binding
250255

251256
self.ovn_client.update_lsp_host_info(context, db_port)
252257

@@ -258,14 +263,16 @@ def test_update_lsp_host_info_up_retry(self):
258263
context = mock.MagicMock()
259264
host_id = 'fake-binding-host-id'
260265
port_id = 'fake-port-id'
266+
port_binding = mock.Mock(host=host_id)
267+
port_binding_no_host = mock.Mock(host="")
261268
db_port_no_host = mock.Mock(
262-
id=port_id, port_bindings=[mock.Mock(host="")])
263-
db_port = mock.Mock(
264-
id=port_id, port_bindings=[mock.Mock(host=host_id)])
269+
id=port_id, port_bindings=[port_binding_no_host])
270+
self.get_pb_bsah.return_value = None
265271

266272
with mock.patch.object(
267-
self.ovn_client, '_wait_for_port_bindings_host') as mock_wait:
268-
mock_wait.return_value = db_port
273+
self.ovn_client,
274+
'_wait_for_active_port_bindings_host') as mock_wait:
275+
mock_wait.return_value = port_binding
269276
self.ovn_client.update_lsp_host_info(context, db_port_no_host)
270277

271278
# Assert _wait_for_port_bindings_host was called
@@ -281,9 +288,11 @@ def test_update_lsp_host_info_up_retry_fail(self):
281288
port_id = 'fake-port-id'
282289
db_port_no_host = mock.Mock(
283290
id=port_id, port_bindings=[mock.Mock(host="")])
291+
self.get_pb_bsah.return_value = None
284292

285293
with mock.patch.object(
286-
self.ovn_client, '_wait_for_port_bindings_host') as mock_wait:
294+
self.ovn_client,
295+
'_wait_for_active_port_bindings_host') as mock_wait:
287296
mock_wait.side_effect = RuntimeError("boom")
288297
self.ovn_client.update_lsp_host_info(context, db_port_no_host)
289298

@@ -315,39 +324,45 @@ def test_update_lsp_host_info_trunk_subport(self):
315324
self.nb_idl.db_set.assert_not_called()
316325

317326
@mock.patch.object(ml2_db, 'get_port')
318-
def test__wait_for_port_bindings_host(self, mock_get_port):
327+
def test__wait_for_active_port_bindings_host(self, mock_get_port):
319328
context = mock.MagicMock()
320329
host_id = 'fake-binding-host-id'
321330
port_id = 'fake-port-id'
331+
port_binding = mock.Mock(host=host_id)
332+
port_binding_no_host = mock.Mock(host="")
322333
db_port_no_host = mock.Mock(
323-
id=port_id, port_bindings=[mock.Mock(host="")])
334+
id=port_id, port_bindings=[port_binding_no_host])
324335
db_port = mock.Mock(
325-
id=port_id, port_bindings=[mock.Mock(host=host_id)])
336+
id=port_id, port_bindings=[port_binding])
337+
# no active binding, no binding with host, binding with host
338+
self.get_pb_bsah.side_effect = (None, port_binding_no_host,
339+
port_binding)
326340

327-
mock_get_port.side_effect = (db_port_no_host, db_port)
341+
mock_get_port.side_effect = (db_port_no_host, db_port_no_host, db_port)
328342

329-
ret = self.ovn_client._wait_for_port_bindings_host(
343+
ret = self.ovn_client._wait_for_active_port_bindings_host(
330344
context, port_id)
331345

332-
self.assertEqual(ret, db_port)
346+
self.assertEqual(ret, port_binding)
333347

334348
expected_calls = [mock.call(context, port_id),
335349
mock.call(context, port_id)]
336350
mock_get_port.assert_has_calls(expected_calls)
337351

338352
@mock.patch.object(ml2_db, 'get_port')
339-
def test__wait_for_port_bindings_host_fail(self, mock_get_port):
353+
def test__wait_for_active_port_bindings_host_fail(self, mock_get_port):
340354
context = mock.MagicMock()
341355
port_id = 'fake-port-id'
342356
db_port_no_pb = mock.Mock(id=port_id, port_bindings=[])
343357
db_port_no_host = mock.Mock(
344358
id=port_id, port_bindings=[mock.Mock(host="")])
359+
self.get_pb_bsah.return_value = None
345360

346361
mock_get_port.side_effect = (
347362
db_port_no_pb, db_port_no_host, db_port_no_host)
348363

349364
self.assertRaises(
350-
RuntimeError, self.ovn_client._wait_for_port_bindings_host,
365+
RuntimeError, self.ovn_client._wait_for_active_port_bindings_host,
351366
context, port_id)
352367

353368
expected_calls = [mock.call(context, port_id),

0 commit comments

Comments
 (0)