From ff57491a976e0a8b4945f7dd55096370c744c76a Mon Sep 17 00:00:00 2001 From: Martin Kalcok Date: Wed, 27 Aug 2025 19:17:04 +0200 Subject: [PATCH] ovn_db_sync: Improve coexistence support. neutron-ovn-db-sync-util synchronizes content between neutron's database and OVN NB/SB databases. As a side-effect, it can sometimes remove resources from OVN database that were not meant for neutron to manage. Coexistence support [0] aims to avoid these scenarios. The ovn_db_sync script already tries to avoid these unwanted removals by checking for presence of well known neutron external_ids for resources like "Logical Switch" and "Logical Switch Port" [1]. This change adds similar checks for: * Logical Router Port * Static Route * Port Group NAT rules are still missing the check because they don't have "neutron:" external_ids to check. In addition to the ovn_db_sync script, there is a 'maintenance' process that periodically updates OVN resources. This change also update its methods to not alter resources not owned by the neutron. [0] https://specs.openstack.org/openstack/neutron-specs/specs/2024.1/ml2ovn-coexistence-support-ovn-ext-resources.html [1] https://opendev.org/openstack/neutron/src/commit/f9067a719084710ee4f46fa31edb6a938e0dbbb0/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py#L308-L316 Closes-bug: #2027742 Change-Id: I1434700928779577073d1369c0a2983a4076cc0e Signed-off-by: Martin Kalcok --- .../ovn/mech_driver/ovsdb/impl_idl_ovn.py | 16 ++- .../ovn/mech_driver/ovsdb/maintenance.py | 11 +- .../ovn/mech_driver/ovsdb/ovn_db_sync.py | 6 +- .../ovn/mech_driver/ovsdb/test_ovn_db_sync.py | 124 ++++++++++++++++-- .../mech_driver/ovsdb/test_impl_idl_ovn.py | 34 ++++- .../ovn/mech_driver/ovsdb/test_maintenance.py | 34 +++-- .../ovn/mech_driver/ovsdb/test_ovn_db_sync.py | 11 +- 7 files changed, 196 insertions(+), 40 deletions(-) diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py index 8f0e464f573..2a997fc6a1e 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py @@ -339,11 +339,17 @@ class OvsdbNbOvnIdl(nb_impl_idl.OvnNbApiIdlImpl, Backend): if ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY not in ( lrouter.external_ids): continue - lrports = {lrport.name.replace('lrp-', ''): lrport.networks - for lrport in getattr(lrouter, 'ports', [])} - sroutes = [{'destination': sroute.ip_prefix, - 'nexthop': sroute.nexthop} - for sroute in getattr(lrouter, 'static_routes', [])] + lrports = { + lrport.name.replace('lrp-', ''): lrport.networks + for lrport in getattr(lrouter, 'ports', []) + if ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY in lrport.external_ids + } + sroutes = [ + {'destination': route.ip_prefix, 'nexthop': route.nexthop} + for route in getattr(lrouter, 'static_routes', []) + if any(eid.startswith(constants.DEVICE_OWNER_NEUTRON_PREFIX) + for eid in route.external_ids) + ] dnat_and_snats = [] snat = [] diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py index e640581c7e6..a19bbe240f8 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py @@ -573,6 +573,8 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase): cmds = [] for ls in self._nb_idl.ls_list().execute(check_error=True): + if ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY not in ls.external_ids: + continue snooping = ls.other_config.get(ovn_const.MCAST_SNOOP) flood = ls.other_config.get(ovn_const.MCAST_FLOOD_UNREGISTERED) @@ -612,9 +614,11 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase): context = n_context.get_admin_context() with self._nb_idl.transaction(check_error=True) as txn: for port in external_ports: - network_id = port.external_ids[ - ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY].replace( + network_id = port.external_ids.get( + ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY, '').replace( ovn_const.OVN_NAME_PREFIX, '') + if not network_id: + continue ha_ch_grp, high_prio_ch = utils.sync_ha_chassis_group_network( context, self._nb_idl, self._sb_idl, port.name, network_id, txn) @@ -1045,6 +1049,9 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase): for seg in net_segments} cmds = [] for ls in self._nb_idl.ls_list().execute(check_error=True): + if ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY not in ls.external_ids: + continue + if ovn_const.OVN_NETTYPE_EXT_ID_KEY not in ls.external_ids: net_id = utils.get_neutron_name(ls.name) external_ids = { 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 f00e120fcfe..05fd1044893 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 @@ -205,7 +205,11 @@ class OvnNbSynchronizer(OvnDbSynchronizer): ovn_pgs = set() port_groups = self.ovn_api.db_list_rows('Port_Group').execute() or [] for pg in port_groups: - ovn_pgs.add(pg.name) + # Default neutron "drop pg" does NOT have any external IDs, but + # we still want to manage it, so we match it on its name. + if (ovn_const.OVN_SG_EXT_ID_KEY in pg.external_ids or + pg.name == ovn_const.OVN_DROP_PORT_GROUP_NAME): + ovn_pgs.add(pg.name) add_pgs = neutron_pgs.difference(ovn_pgs) remove_pgs = ovn_pgs.difference(neutron_pgs) 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 2c2118aca7a..3edddc1ba80 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 @@ -99,6 +99,12 @@ class TestOvnNbSync(testlib_api.MySQLTestCaseMixin, self.expected_dns_records = [] self.expected_ports_with_unknown_addr = [] self.expected_qos_records = [] + # Set of externally managed resources that should not + # be cleaned up by the sync_db + self.create_ext_port_groups = [] + self.create_ext_lrouter_ports = [] + self.create_ext_lrouter_routes = [] + self.ctx = context.get_admin_context() ovn_config.cfg.CONF.set_override('ovn_metadata_enabled', True, group='ovn') @@ -516,6 +522,9 @@ class TestOvnNbSync(testlib_api.MySQLTestCaseMixin, self.create_lrouter_routes.append(('neutron-' + r1['id'], '10.13.0.0/24', '20.0.0.13')) + self.create_ext_lrouter_routes.append(('neutron-' + r1['id'], + '10.14.0.0/24', + '20.0.0.14')) self.delete_lrouter_routes.append(('neutron-' + r1['id'], '10.10.0.0/24', '20.0.0.10')) @@ -663,10 +672,18 @@ class TestOvnNbSync(testlib_api.MySQLTestCaseMixin, 'neutron-' + r1['id'])) self.create_lrouter_ports.append(('lrp-' + uuidutils.generate_uuid(), 'neutron-' + r1['id'])) + self.create_ext_lrouter_ports.append( + ('ext-lrp-' + uuidutils.generate_uuid(), 'neutron-' + r1['id']) + ) + self.create_ext_lrouter_ports.append( + ('ext-lrp-' + uuidutils.generate_uuid(), 'neutron-' + r1['id']) + ) self.delete_lrouters.append('neutron-' + r2['id']) self.create_port_groups.extend([{'name': 'pg1', 'acls': []}, {'name': 'pg2', 'acls': []}]) + self.create_ext_port_groups.extend([{'name': 'ext-pg1', 'acls': []}, + {'name': 'ext-pg2', 'acls': []}]) self.delete_port_groups.append( utils.ovn_port_group_name(n1_prtr['port']['security_groups'][0])) # Create a network and subnet with orphaned OVN resources. @@ -791,7 +808,14 @@ class TestOvnNbSync(testlib_api.MySQLTestCaseMixin, txn.add(self.nb_api.lr_del(lrouter_name, if_exists=True)) for lrport, lrouter_name in self.create_lrouter_ports: - txn.add(self.nb_api.add_lrouter_port(lrport, lrouter_name)) + external_ids = {ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: + lrouter_name} + txn.add(self.nb_api.add_lrouter_port( + lrport, lrouter_name, True, external_ids=external_ids)) + + for lrport, lrouter_name in self.create_ext_lrouter_ports: + txn.add(self.nb_api.add_lrouter_port( + lrport, lrouter_name, True)) for lrport, lrouter_name, networks in self.update_lrouter_ports: txn.add(self.nb_api.update_lrouter_port( @@ -808,6 +832,10 @@ class TestOvnNbSync(testlib_api.MySQLTestCaseMixin, ip_prefix=ip_prefix, nexthop=nexthop, **columns)) + for lr_name, ip_prefix, nexthop in self.create_ext_lrouter_routes: + txn.add(self.nb_api.add_static_route(lr_name, + ip_prefix=ip_prefix, + nexthop=nexthop)) routers = defaultdict(list) for lrouter_name, ip_prefix, nexthop in self.delete_lrouter_routes: routers[lrouter_name].append((ip_prefix, nexthop)) @@ -840,7 +868,12 @@ class TestOvnNbSync(testlib_api.MySQLTestCaseMixin, txn.add(self.nb_api.delete_acl(lswitch_name, lport_name, True)) + columns = { + 'external_ids': {ovn_const.OVN_SG_EXT_ID_KEY: 'sg_uuid'}, + } for pg in self.create_port_groups: + txn.add(self.nb_api.pg_add(**pg, **columns)) + for pg in self.create_ext_port_groups: txn.add(self.nb_api.pg_add(**pg)) for pg in self.delete_port_groups: txn.add(self.nb_api.pg_del(pg)) @@ -1298,6 +1331,7 @@ class TestOvnNbSync(testlib_api.MySQLTestCaseMixin, self.ctx, port)) return ipv6_ra_configs + neutron_prefix = constants.DEVICE_OWNER_NEUTRON_PREFIX for router_id in db_router_ids: r_ports = self._list('ports', query_params='device_id=%s' % (router_id)) @@ -1316,18 +1350,27 @@ class TestOvnNbSync(testlib_api.MySQLTestCaseMixin, lrouter = idlutils.row_by_value( self.mech_driver.nb_ovn.idl, 'Logical_Router', 'name', 'neutron-' + str(router_id), None) - lports = getattr(lrouter, 'ports', []) + all_lports = getattr(lrouter, 'ports', []) + managed_lports = [ + lport for lport in all_lports + if (ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY in + lport.external_ids) + ] + plugin_lrouter_port_ids = [lport.name.replace('lrp-', '') - for lport in lports] + for lport in managed_lports] plugin_lport_networks = { lport.name.replace('lrp-', ''): lport.networks - for lport in lports} + for lport in managed_lports} plugin_lport_ra_configs = { lport.name.replace('lrp-', ''): lport.ipv6_ra_configs - for lport in lports} + for lport in managed_lports} sroutes = getattr(lrouter, 'static_routes', []) - plugin_routes = [sroute.ip_prefix + sroute.nexthop - for sroute in sroutes] + plugin_routes = [] + for sroute in sroutes: + if any(e_id.startswith(neutron_prefix) + for e_id in sroute.external_ids): + plugin_routes.append(sroute.ip_prefix + sroute.nexthop) nats = getattr(lrouter, 'nat', []) plugin_nats = [ nat.external_ip + nat.logical_ip + nat.type + @@ -1343,18 +1386,29 @@ class TestOvnNbSync(testlib_api.MySQLTestCaseMixin, lrouter = idlutils.row_by_value( self.nb_api.idl, 'Logical_Router', 'name', 'neutron-' + router_id, None) - lports = getattr(lrouter, 'ports', []) + all_lports = getattr(lrouter, 'ports', []) + managed_lports = [ + lport for lport in all_lports + if (ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY in + lport.external_ids) + ] monitor_lrouter_port_ids = [lport.name.replace('lrp-', '') - for lport in lports] + for lport in managed_lports] monitor_lport_networks = { lport.name.replace('lrp-', ''): lport.networks - for lport in lports} + for lport in managed_lports} monitor_lport_ra_configs = { lport.name.replace('lrp-', ''): lport.ipv6_ra_configs - for lport in lports} + for lport in managed_lports} sroutes = getattr(lrouter, 'static_routes', []) - monitor_routes = [sroute.ip_prefix + sroute.nexthop - for sroute in sroutes] + monitor_routes = [] + for sroute in sroutes: + if any(e_id.startswith(neutron_prefix) + for e_id in sroute.external_ids): + monitor_routes.append( + sroute.ip_prefix + sroute.nexthop + ) + nats = getattr(lrouter, 'nat', []) monitor_nats = [ nat.external_ip + nat.logical_ip + nat.type + @@ -1512,7 +1566,9 @@ class TestOvnNbSync(testlib_api.MySQLTestCaseMixin, mn_pgs = [] for row in self.nb_api.tables['Port_Group'].rows.values(): - mn_pgs.append(getattr(row, 'name', '')) + if (ovn_const.OVN_SG_EXT_ID_KEY in row.external_ids or + row.name == ovn_const.OVN_DROP_PORT_GROUP_NAME): + mn_pgs.append(getattr(row, 'name', '')) if should_match: self.assertCountEqual(nb_pgs, db_pgs) @@ -1618,6 +1674,46 @@ class TestOvnNbSync(testlib_api.MySQLTestCaseMixin, self._sync_resources(mode) self._validate_resources(should_match=should_match_after_sync) + if not restart_ovsdb_processes: + # Restarting ovsdb-server removes all its previous content. + # We can not expect to find external resources in the DB + # if it was wiped out. + self._validate_external_resources() + + def _validate_external_resources(self): + """Ensure that resources not owned by Neutron are in the OVN DB. + + This function is useful to validate that external resources survived + ovn_db_sync. + """ + db_routers = self._list('routers') + db_router_ids = [router['id'] for router in db_routers['routers']] + + pgs = [] + for pg in self.nb_api.tables['Port_Group'].rows.values(): + pgs.append(pg.name) + + lrports = [] + sroutes = [] + for router_id in db_router_ids: + lrouter = idlutils.row_by_value( + self.mech_driver.nb_ovn.idl, 'Logical_Router', 'name', + 'neutron-' + str(router_id), None) + + for lrport in getattr(lrouter, 'ports', []): + lrports.append(lrport.name) + + for route in getattr(lrouter, 'static_routes', []): + sroutes.append(route.ip_prefix + route.nexthop) + + for port_name, _ in self.create_ext_lrouter_ports: + self.assertIn(port_name, lrports) + + for _, prefix, next_hop in self.create_ext_lrouter_routes: + self.assertIn(prefix + next_hop, sroutes) + + for ext_pg in self.create_ext_port_groups: + self.assertIn(ext_pg['name'], pgs) def test_ovn_nb_sync_repair(self): self._test_ovn_nb_sync_helper(ovn_const.OVN_DB_SYNC_MODE_REPAIR) diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py index 0e3f6a360ef..f578e9988d2 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py +++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_impl_idl_ovn.py @@ -189,12 +189,21 @@ class TestNBImplIdlOvn(TestDBImplIdlOvn): 'networks': ['10.0.3.0/24'], 'options': {ovn_const.OVN_GATEWAY_CHASSIS_KEY: None}}, {'name': 'xrp-id-b1', - 'external_ids': {}, 'networks': ['20.0.1.0/24']}, + 'external_ids': { + ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: + utils.ovn_name('lr-id-b'), + }, 'networks': ['20.0.1.0/24']}, {'name': utils.ovn_lrouter_port_name('orp-id-b2'), - 'external_ids': {}, 'networks': ['20.0.2.0/24'], + 'external_ids': { + ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: + utils.ovn_name('lr-id-b'), + }, 'networks': ['20.0.2.0/24'], 'options': {ovn_const.OVN_GATEWAY_CHASSIS_KEY: 'host-2'}}, {'name': utils.ovn_lrouter_port_name('orp-id-b3'), - 'external_ids': {}, 'networks': ['20.0.3.0/24'], + 'external_ids': { + ovn_const.OVN_ROUTER_NAME_EXT_ID_KEY: + utils.ovn_name('lr-id-b'), + }, 'networks': ['20.0.3.0/24'], 'options': {}}, {'name': utils.ovn_lrouter_port_name('gwc'), 'external_ids': { @@ -202,14 +211,27 @@ class TestNBImplIdlOvn(TestDBImplIdlOvn): utils.ovn_name('lr-id-f'), ovn_const.OVN_ROUTER_IS_EXT_GW: str(True)}, 'networks': ['10.0.4.0/24'], + 'options': {}}, + {'name': utils.ovn_lrouter_port_name('not-managed'), + 'external_ids': { + 'owner': 'not-owned-by-neutron', + }, + 'networks': ['10.0.5.0/24'], 'options': {}}], 'gateway_chassis': [ {'chassis_name': 'fake-chassis', 'name': utils.ovn_lrouter_port_name('gwc') + '_fake-chassis'}], 'static_routes': [{'ip_prefix': '20.0.0.0/16', - 'nexthop': '10.0.3.253'}, + 'nexthop': '10.0.3.253', + 'external_ids': { + ovn_const.OVN_SUBNET_EXT_ID_KEY: 'uuid_1'}}, {'ip_prefix': '10.0.0.0/16', - 'nexthop': '20.0.2.253'}], + 'nexthop': '20.0.2.253', + 'external_ids': { + ovn_const.OVN_SUBNET_EXT_ID_KEY: 'uuid_2'}}, + {'ip_prefix': '30.0.0.0/16', + 'nexthop': '30.0.4.253', + 'external_ids': {'owner': 'not-owned-by-neutron'}}], 'nats': [{'external_ip': '10.0.3.1', 'logical_ip': '20.0.0.0/16', 'type': 'snat'}, {'external_ip': '20.0.2.1', 'logical_ip': '10.0.0.0/24', @@ -489,7 +511,7 @@ class TestNBImplIdlOvn(TestDBImplIdlOvn): def _test_get_all_logical_routers_with_rports(self, is_gw_port): # Test empty - mapping = self.nb_ovn_idl.get_all_logical_switches_with_ports() + mapping = self.nb_ovn_idl.get_all_logical_routers_with_rports() self.assertCountEqual(mapping, {}) # Test loaded values self._load_nb_db() diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py index aaaf6176179..fd2204a6dd5 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py +++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py @@ -428,30 +428,39 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight, attrs={'name': 'ls0', 'other_config': { constants.MCAST_SNOOP: 'false', - constants.MCAST_FLOOD_UNREGISTERED: 'false'}}) + constants.MCAST_FLOOD_UNREGISTERED: 'false'}, + 'external_ids': {constants.OVN_NETWORK_NAME_EXT_ID_KEY: 'port0'}}) ls1 = fakes.FakeOvsdbRow.create_one_ovsdb_row( attrs={'name': 'ls1', - 'other_config': {}}) + 'other_config': {}, + 'external_ids': {constants.OVN_NETWORK_NAME_EXT_ID_KEY: 'port1'}}) ls2 = fakes.FakeOvsdbRow.create_one_ovsdb_row( attrs={'name': 'ls2', 'other_config': { constants.MCAST_SNOOP: 'true', - constants.MCAST_FLOOD_UNREGISTERED: 'false'}}) + constants.MCAST_FLOOD_UNREGISTERED: 'false'}, + 'external_ids': {constants.OVN_NETWORK_NAME_EXT_ID_KEY: 'port2'}}) ls3 = fakes.FakeOvsdbRow.create_one_ovsdb_row( attrs={'name': '', - 'other_config': {}}) + 'other_config': {}, + 'external_ids': {constants.OVN_NETWORK_NAME_EXT_ID_KEY: 'port3'}}) ls4 = fakes.FakeOvsdbRow.create_one_ovsdb_row( attrs={'name': '', - 'other_config': {constants.MCAST_SNOOP: 'false'}}) - + 'other_config': {constants.MCAST_SNOOP: 'false'}, + 'external_ids': {constants.OVN_NETWORK_NAME_EXT_ID_KEY: 'port4'}}) + ls5 = fakes.FakeOvsdbRow.create_one_ovsdb_row( + attrs={'name': 'ls5', + 'other_config': {}, + 'external_ids': {}}) nb_idl.ls_list.return_value.execute.return_value = [ls0, ls1, ls2, ls3, - ls4] + ls4, ls5] self.assertRaises(periodics.NeverAgain, self.periodic.check_for_igmp_snoop_support) # "ls2" is not part of the transaction because it already - # have the right value set; "ls3" and "ls4" do not have a name set. + # have the right value set; "ls3" and "ls4" do not have a name set; + # "ls5" is not managed by neutron. expected_calls = [ mock.call('Logical_Switch', 'ls0', ('other_config', { @@ -506,7 +515,14 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight, 'external_ids': { constants.OVN_NETWORK_NAME_EXT_ID_KEY: 'neutron-net1'}}) - nb_idl.db_find_rows.return_value.execute.return_value = [p0, p1] + # Port p2 is not owned by Neutron and should not be affected. + p2 = fakes.FakeOvsdbRow.create_one_ovsdb_row( + attrs={'type': constants.LSP_TYPE_EXTERNAL, + 'name': 'p2', + 'ha_chassis_group': [hcg1], + 'external_ids': {}}) + + nb_idl.db_find_rows.return_value.execute.return_value = [p0, p1, p2] mock_sync_ha_chassis_group_network.return_value = hcg0.uuid, mock.ANY # Invoke the periodic method, it meant to run only once at startup 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 370bfd06d85..7a39a7ae8e6 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 @@ -147,7 +147,7 @@ class TestOvnNbSyncML2(test_mech_driver.OVNMechanismDriverTestCase): 'security_group_id': 'sg2'}], 'name': 'all-tcpe'}] - self.sg_port_groups_ovn = [mock.Mock(), mock.Mock(), mock.Mock()] + self.sg_port_groups_ovn = [mock.Mock(), mock.Mock(), mock.Mock(), mock.Mock()] self.sg_port_groups_ovn[0].configure_mock( name='pg_sg1', external_ids={ovn_const.OVN_SG_EXT_ID_KEY: 'sg1'}, @@ -159,8 +159,13 @@ class TestOvnNbSyncML2(test_mech_driver.OVNMechanismDriverTestCase): ports=[], acls=[]) self.sg_port_groups_ovn[2].configure_mock( - name='neutron_pg_drop', - external_ids=[], + name=ovn_const.OVN_DROP_PORT_GROUP_NAME, + external_ids={}, + ports=[], + acls=[]) + self.sg_port_groups_ovn[3].configure_mock( + name='external_pg', + external_ids={'owner': 'not-owned-by-neutron'}, ports=[], acls=[])