diff --git a/neutron/agent/ovn/extensions/qos_hwol.py b/neutron/agent/ovn/extensions/qos_hwol.py index abe0783e2bd..a813418d04c 100644 --- a/neutron/agent/ovn/extensions/qos_hwol.py +++ b/neutron/agent/ovn/extensions/qos_hwol.py @@ -48,7 +48,7 @@ class OVSInterfaceEvent(row_event.RowEvent): def __init__(self, ovn_agent): self.ovn_agent = ovn_agent - events = (self.ROW_CREATE, self.ROW_DELETE) + events = (self.ROW_CREATE, self.ROW_UPDATE, self.ROW_DELETE) table = 'Interface' super().__init__(events, table, None) @@ -58,7 +58,7 @@ def match_fn(self, event, row, old): return True def run(self, event, row, old): - if event == self.ROW_CREATE: + if event in (self.ROW_CREATE, self.ROW_UPDATE): self.ovn_agent.qos_hwol_ext.add_port( row.external_ids['iface-id'], row.name) elif event == self.ROW_DELETE: diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_db_sync.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_db_sync.py index dfd7f91374b..c510b667d80 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_db_sync.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_db_sync.py @@ -1353,7 +1353,8 @@ def sync_hostname_and_physical_networks(self, ctx): LOG.debug('OVN-SB Sync hostname and physical networks started') host_phynets_map = self.ovn_api.get_chassis_hostname_and_physnets() current_hosts = set(host_phynets_map) - previous_hosts = segments_db.get_hosts_mapped_with_segments(ctx) + previous_hosts = segments_db.get_hosts_mapped_with_segments( + ctx, include_agent_types={ovn_const.OVN_CONTROLLER_AGENT}) stale_hosts = previous_hosts - current_hosts for host in stale_hosts: diff --git a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py index 77010bdc546..1479be57852 100644 --- a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py +++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py @@ -908,7 +908,9 @@ def _verify_lb(test, protocol, vip_ext_port, vip_int_port): pf_def.INTERNAL_IP_ADDRESS: p1_ip} pf_obj = self.pf_plugin.create_floatingip_port_forwarding( self.context, fip_id, **fip_attrs(fip_pf_args)) - m_publish.assert_called_once() + call = mock.call('port_forwarding', 'after_create', self.pf_plugin, + payload=mock.ANY) + m_publish.assert_has_calls([call]) # Assert load balancer for port forwarding was not created self.assertFalse(self._find_pf_lb(router_id, fip_id)) @@ -926,7 +928,9 @@ def _verify_lb(test, protocol, vip_ext_port, vip_int_port): m_publish.reset_mock() self.pf_plugin.update_floatingip_port_forwarding( self.context, pf_obj['id'], fip_id, **fip_attrs(fip_pf_args)) - m_publish.assert_called_once() + call = mock.call('port_forwarding', 'after_update', self.pf_plugin, + payload=mock.ANY) + m_publish.assert_has_calls([call]) # Assert load balancer for port forwarding is stale _verify_lb(self, 'tcp', 2222, 22) diff --git a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_db_sync.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_db_sync.py index 5e12d7556a5..c55bd2609b6 100644 --- a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_db_sync.py +++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_db_sync.py @@ -1818,6 +1818,22 @@ def setUp(self): def _sync_resources(self): self.sb_synchronizer.sync_hostname_and_physical_networks(self.ctx) + def create_agent(self, host, bridge_mappings=None, agent_type=None): + if agent_type is None: + agent_type = ovn_const.OVN_CONTROLLER_AGENT + if bridge_mappings is None: + bridge_mappings = {} + agent = { + 'host': host, + 'agent_type': agent_type, + 'binary': '/bin/test', + 'topic': 'test_topic', + 'configurations': {'bridge_mappings': bridge_mappings} + } + _, status = self.plugin.create_or_update_agent(self.context, agent) + + return status['id'] + def create_segment(self, network_id, physical_network, segmentation_id): segment_data = {'network_id': network_id, 'physical_network': physical_network, @@ -1858,6 +1874,7 @@ def test_ovn_sb_sync_delete_stale_host(self): segment = self.create_segment(network_id, 'physnet1', 50) segments_db.update_segment_host_mapping( self.ctx, 'host1', {segment['id']}) + _ = self.create_agent('host1', bridge_mappings={'physnet1': 'eth0'}) segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx) self.assertEqual({'host1'}, segment_hosts) # Since there is no chassis in the sb DB, host1 is the stale host @@ -1866,6 +1883,36 @@ def test_ovn_sb_sync_delete_stale_host(self): segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx) self.assertFalse(segment_hosts) + def test_ovn_sb_sync_host_with_no_agent_not_deleted(self): + with self.network() as network: + network_id = network['network']['id'] + segment = self.create_segment(network_id, 'physnet1', 50) + segments_db.update_segment_host_mapping( + self.ctx, 'host1', {segment['id']}) + _ = self.create_agent('host1', bridge_mappings={'physnet1': 'eth0'}, + agent_type="Not OVN Agent") + segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx) + self.assertEqual({'host1'}, segment_hosts) + # There is no chassis in the sb DB, host1 does not have an agent + # so it is not deleted. + self._sync_resources() + segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx) + self.assertEqual({'host1'}, segment_hosts) + + def test_ovn_sb_sync_host_with_other_agent_type_not_deleted(self): + with self.network() as network: + network_id = network['network']['id'] + segment = self.create_segment(network_id, 'physnet1', 50) + segments_db.update_segment_host_mapping( + self.ctx, 'host1', {segment['id']}) + segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx) + self.assertEqual({'host1'}, segment_hosts) + # There is no chassis in the sb DB, host1 does not have an agent + # so it is not deleted. + self._sync_resources() + segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx) + self.assertEqual({'host1'}, segment_hosts) + def test_ovn_sb_sync(self): with self.network() as network: network_id = network['network']['id'] @@ -1878,6 +1925,9 @@ def test_ovn_sb_sync(self): segments_db.update_segment_host_mapping( self.ctx, 'host3', {seg1['id']}) segment_hosts = segments_db.get_hosts_mapped_with_segments(self.ctx) + _ = self.create_agent('host1') + _ = self.create_agent('host2', bridge_mappings={'physnet2': 'eth0'}) + _ = self.create_agent('host3', bridge_mappings={'physnet3': 'eth0'}) self.assertEqual({'host1', 'host2', 'host3'}, segment_hosts) self.add_fake_chassis('host2', ['physnet2']) self.add_fake_chassis('host3', ['physnet3']) diff --git a/neutron/tests/tools.py b/neutron/tests/tools.py index aca89a216b3..d419ebe8f5a 100644 --- a/neutron/tests/tools.py +++ b/neutron/tests/tools.py @@ -19,6 +19,7 @@ import fixtures import netaddr +from neutron_lib.callbacks import priority_group from neutron_lib import constants from neutron_lib.services.logapi import constants as log_const from neutron_lib.utils import helpers @@ -27,15 +28,15 @@ from oslo_utils import timeutils -# NOTE(yamahata): from neutron-lib 1.9.1, callback priority was added and -# priority_group module was added for constants of priority. -# test the existence of the module of priority_group to check if -# callback priority is supported or not. -_CALLBACK_PRIORITY_SUPPORTED = True +# NOTE(ykarel): from neutron-lib 3.9.0, cancellable flag was added +# test the existence of the is_cancellable_event function to check if +# cancellable flag is supported or not. This compatibility check can +# be removed once neutron-lib >= 3.9.0 in requirements.txt. +_CANCELLABLE_FLAG_SUPPORTED = True try: - from neutron_lib.callbacks import priority_group # noqa + from neutron_lib.callbacks.events import is_cancellable_event # noqa except ImportError: - _CALLBACK_PRIORITY_SUPPORTED = False + _CANCELLABLE_FLAG_SUPPORTED = False LAST_RANDOM_PORT_RANGE_GENERATED = 1 @@ -146,12 +147,14 @@ def make_mock_plugin_json_encodable(plugin_instance_mock): def get_subscribe_args(*args): - # NOTE(yamahata): from neutron-lib 1.9.1, callback priority was added. - # old signature: (callback, resource, event) - # new signature: (callback, resource, event, priority=PRIORITY_DEFAULT) - if len(args) == 3 and _CALLBACK_PRIORITY_SUPPORTED: - args = list(args) # don't modify original list - args.append(priority_group.PRIORITY_DEFAULT) + args = list(args) # don't modify original list + args.append(priority_group.PRIORITY_DEFAULT) + # NOTE(ykarel): from neutron-lib 3.9.0, cancellable flag was added. + # old signature: (callback, resource, event, priority=PRIORITY_DEFAULT) + # new signature: (callback, resource, event, priority=PRIORITY_DEFAULT, + # cancellable=False) + if len(args) == 4 and _CANCELLABLE_FLAG_SUPPORTED: + args.append(False) return args diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_db_sync.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_db_sync.py index 6eb48bcb08e..0c81c6a0576 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_db_sync.py +++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_db_sync.py @@ -1169,8 +1169,10 @@ def test_ovn_sb_sync(self): with mock.patch.object(ovn_db_sync.segments_db, 'get_hosts_mapped_with_segments', - return_value=hosts_in_neutron): + return_value=hosts_in_neutron) as mock_ghmws: ovn_sb_synchronizer.sync_hostname_and_physical_networks(mock.ANY) + mock_ghmws.assert_called_once_with( + mock.ANY, include_agent_types={ovn_const.OVN_CONTROLLER_AGENT}) all_hosts = set(hostname_with_physnets.keys()) | hosts_in_neutron self.assertEqual( len(all_hosts), diff --git a/releasenotes/notes/ovn-db-sync-host-physnet-filter-agent-type-9e22942bed304807.yaml b/releasenotes/notes/ovn-db-sync-host-physnet-filter-agent-type-9e22942bed304807.yaml new file mode 100644 index 00000000000..79a2593174a --- /dev/null +++ b/releasenotes/notes/ovn-db-sync-host-physnet-filter-agent-type-9e22942bed304807.yaml @@ -0,0 +1,10 @@ +--- +fixes: + - | + When synchronizing the OVN databases, either when running the migration + command or during startup, the code responsible for synchronization will + only clean up segment-to-host mappings for hosts with agent_type + ``OVN Controller agent``. Before, the synchronization would clean up + (delete) segment-to-host mappings for non-OVN hosts. Fixes bug: + `2040172 `_. +