bgp: Fix waiting for port on BGP bridge

If there is a very slow or a busy machine the ovs-vswitchd
process may take some time before assigning an ofport number
to the new port. The change makes waiting for update too in case
the ofport is added later.

Assisted-By: Claude Opus 4.6

Change-Id: I07fa5f6d62aaa2ebf225388c6e2d5ba80c6e7441
Signed-off-by: Jakub Libosvar <libosvar@redhat.com>
This commit is contained in:
Jakub Libosvar
2026-05-13 14:39:55 -04:00
parent c936572033
commit 703aacb180
2 changed files with 85 additions and 1 deletions
+7 -1
View File
@@ -224,7 +224,7 @@ class PortBindingLrpMacEvent(BGPAgentEvent):
class BGPBridgePortCreatedEvent(BGPAgentEvent):
EVENTS = (BGPAgentEvent.ROW_CREATE,)
EVENTS = (BGPAgentEvent.ROW_CREATE, BGPAgentEvent.ROW_UPDATE,)
TABLE = 'Interface'
ONETIME = True
@@ -250,6 +250,12 @@ class BGPBridgePortCreatedEvent(BGPAgentEvent):
if row.type not in self.port_types:
return False
# On slow machines, vswitchd may not have assigned the ofport yet
# when the Interface row is created. Wait for the ofport to be
# assigned (via ROW_UPDATE) before matching.
if not row.ofport:
return False
try:
port_bridge_name = self._get_port_bridge(row.name)
except StopIteration:
@@ -16,6 +16,7 @@
import threading
from unittest import mock
from oslo_utils import uuidutils
import testtools
from neutron.agent.ovn.extensions.bgp import events
@@ -168,6 +169,83 @@ class NewBgpBridgeEventTestCase(BaseBgpEventsTestCase):
self._check_event_not_triggered()
class BGPBridgePortCreatedEventTestCase(BaseBgpEventsTestCase):
def setUp(self):
super().setUp()
self.bgp_ext = mock.Mock()
self.agent_api = {constants.AGENT_BGP_EXT_NAME: self.bgp_ext}
self.bgp_bridge_name = 'br-bgp'
self.bridge_mock = mock.Mock()
self.bridge_mock.check_requirements_for_flows_met.return_value = True
self.bgp_ext.bgp_bridges = {self.bgp_bridge_name: self.bridge_mock}
self.int_bridge_name = 'br-int-%s' % uuidutils.generate_uuid()[:8]
for br in (self.bgp_bridge_name, self.int_bridge_name):
self.ovs_api.add_br(br).execute(check_error=True)
def _register_event(self, *port_types):
ev = events.BGPBridgePortCreatedEvent(
self.agent_api, self.bgp_bridge_name, *port_types)
ev._get_port_bridge = mock.Mock(return_value=self.bgp_bridge_name)
self.ovs_api.idl.notify_handler.watch_event(ev)
return ev
def _add_patch_ports(self):
suffix = uuidutils.generate_uuid()[:8]
port_name = 'bgp-patch-%s' % suffix
peer_name = 'int-patch-%s' % suffix
with self.ovs_api.transaction(check_error=True) as txn:
txn.add(self.ovs_api.add_port(
self.bgp_bridge_name, port_name))
txn.add(self.ovs_api.add_port(self.int_bridge_name, peer_name))
txn.add(self.ovs_api.db_set(
'Interface', port_name, type='patch',
options={'peer': peer_name}))
txn.add(self.ovs_api.db_set(
'Interface', peer_name, type='patch',
options={'peer': port_name}))
def _check_flows_applied(self):
utils.wait_until_true(
lambda: self.bridge_mock.configure_flows.called,
timeout=5,
exception=Exception("configure_flows was not called"))
def _check_flows_not_applied(self):
with testtools.ExpectedException(Exception):
utils.wait_until_true(
lambda: self.bridge_mock.configure_flows.called,
sleep=0.5,
timeout=2,
exception=Exception("configure_flows was unexpectedly called"))
def test_patch_port_created_configures_flows(self):
self._register_event('patch')
self._add_patch_ports()
self._check_flows_applied()
def test_nic_port_created_configures_flows(self):
self._register_event(*constants.BGP_BRIDGE_NIC_TYPES)
fake_nic = self.useFixture(net_helpers.VethFixture()).ports[0]
self.ovs_api.add_port(
self.bgp_bridge_name, fake_nic.name).execute(check_error=True)
self._check_flows_applied()
def test_wrong_port_type_does_not_trigger_event(self):
self._register_event('patch')
fake_nic = self.useFixture(net_helpers.VethFixture()).ports[0]
self.ovs_api.add_port(
self.bgp_bridge_name, fake_nic.name).execute(check_error=True)
self._check_flows_not_applied()
def test_wrong_bridge_does_not_trigger_event(self):
ev = self._register_event('patch')
ev._get_port_bridge = mock.Mock(return_value='other-bridge')
self._add_patch_ports()
self._check_flows_not_applied()
class BgpBridgeMappingsBase(BaseBgpEventsTestCase):
def setUp(self):
super().setUp()