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 bf3721495b5..1b50088df7c 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 @@ -863,10 +863,20 @@ def _sync_subnet_dhcp_options(self, ctx, db_networks, LOG.warning('DHCP options for subnet %s is present in ' 'Neutron but out of sync for OVN', subnet_id) if self.mode == SYNC_MODE_REPAIR: + # If neutron-server is running we could race and find a + # subnet without a cached network, just skip it to avoid + # a KeyError below. + network_id = utils.ovn_name(subnet['network_id']) + if network_id not in db_networks: + LOG.warning('Network %s for subnet %s not found in OVN NB ' + 'DB network cache, possible race condition, ' + 'please check that neutron-server is stopped! ' + 'Skipping subnet.', network_id, subnet_id) + continue try: - LOG.debug('Adding/Updating DHCP options for subnet %s in ' - ' OVN NB DB', subnet_id) - network = db_networks[utils.ovn_name(subnet['network_id'])] + LOG.warning('Adding/Updating DHCP options for subnet %s ' + 'in OVN NB DB', subnet_id) + network = db_networks[network_id] # _ovn_client._add_subnet_dhcp_options doesn't create # a new row in DHCP_Options if the row already exists. # See commands.AddDHCPOptionsCommand. 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 0c81c6a0576..f73eada8290 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 @@ -104,6 +104,17 @@ def setUp(self): 'gateway_ip': '20.0.0.1', 'dns_nameservers': [], 'host_routes': [], + 'ip_version': 4}, + # A subnet without a known network should be skipped, + # see bug #2045811 + {'id': 'notfound', + 'network_id': 'notfound', + 'enable_dhcp': True, + 'cidr': '30.0.0.0/24', + 'tenant_id': 'tenant1', + 'gateway_ip': '30.0.0.1', + 'dns_nameservers': [], + 'host_routes': [], 'ip_version': 4}] self.security_groups = [ diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py index e1c39a5ea61..b0c77ecd4d2 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py +++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py @@ -1515,9 +1515,10 @@ def test_enable_subnet_dhcp_options_in_ovn_ipv4(self, grm, gps): self.mech_driver.nb_ovn.set_lswitch_port.assert_has_calls( set_lsp_calls, any_order=True) + @mock.patch.object(ovn_utils, 'get_system_dns_resolvers') @mock.patch.object(db_base_plugin_v2.NeutronDbPluginV2, 'get_ports') @mock.patch.object(n_net, 'get_random_mac') - def test_enable_subnet_dhcp_options_in_ovn_ipv6(self, grm, gps): + def test_enable_subnet_dhcp_options_in_ovn_ipv6(self, grm, gps, gsd): grm.return_value = '01:02:03:04:05:06' gps.return_value = [ {'id': 'port-id-1', 'device_owner': 'nova:compute'}, @@ -1530,6 +1531,7 @@ def test_enable_subnet_dhcp_options_in_ovn_ipv6(self, grm, gps): {'opt_value': '10::34', 'ip_version': 6, 'opt_name': 'dns-server'}]}, {'id': 'port-id-10', 'device_owner': 'network:foo'}] + gsd.return_value = [] subnet = {'id': 'subnet-id', 'ip_version': 6, 'cidr': '10::0/64', 'gateway_ip': '10::1', 'enable_dhcp': True, 'ipv6_address_mode': 'dhcpv6-stateless', @@ -1696,7 +1698,9 @@ def test_update_subnet_dhcp_options_in_ovn_ipv6(self): self.mech_driver.nb_ovn.add_dhcp_options.assert_called_once_with( subnet['id'], **new_options) - def test_update_subnet_dhcp_options_in_ovn_ipv6_not_change(self): + @mock.patch.object(ovn_utils, 'get_system_dns_resolvers') + def test_update_subnet_dhcp_options_in_ovn_ipv6_not_change(self, gsd): + gsd.return_value = [] subnet = {'id': 'subnet-id', 'ip_version': 6, 'cidr': '10::0/64', 'gateway_ip': '10::1', 'enable_dhcp': True, 'ipv6_address_mode': 'dhcpv6-stateless',