[OVN] Add option to allow configuring dns ovn-owned

Added a configuration option '[ovn]dns_records_ovn_owned' to
allow setting 'ovn-owned' DNS option added as part of [1].
The Default is False so no change in the current behavior.

If this option is set to True for OVN version 24.03 and above,
DNS records will be treated local to the OVN controller and it
will respond to the queries for the records and record types
known to it else it will forward them to the configured
DNS Server(s).

Also added a maintenance task to update the option in
all the DNS records as per the config option with neutron
restart.

[1] https://github.com/ovn-org/ovn/commit/1622526ff

Depends-On: https://review.opendev.org/c/openstack/requirements/+/942797
Depends-On: https://review.opendev.org/c/openstack/ovsdbapp/+/942367
Related-Issue: https://issues.redhat.com/browse/OSPRH-10758
Related-Bug: #2059405
Change-Id: Ia645e8539753c03eb6ead9a868ba5bf194e9a724
This commit is contained in:
2025-02-20 20:26:54 +05:30
parent 86f94de99a
commit cde5580bf1
8 changed files with 132 additions and 1 deletions

View File

@@ -67,6 +67,7 @@ OVN_GATEWAY_NAT_ADDRESSES_KEY = 'nat-addresses'
OVN_ROUTER_PORT_EXCLUDE_LB_VIPS_GARP = 'exclude-lb-vips-from-garp'
OVN_DROP_PORT_GROUP_NAME = 'neutron_pg_drop'
OVN_ROUTER_PORT_GW_MTU_OPTION = 'gateway_mtu'
OVN_OWNED = 'ovn-owned'
OVN_PROVNET_PORT_NAME_PREFIX = 'provnet-'
OVN_NAME_PREFIX = 'neutron-'

View File

@@ -154,6 +154,14 @@ ovn_opts = [
"field is empty. If both subnet's dns_nameservers and "
"this option are empty, then the DNS resolvers on the "
"host running the neutron server will be used.")),
cfg.BoolOpt('dns_records_ovn_owned',
default=False,
help=_("Whether to consider DNS records local to OVN or not. "
"For OVN version 24.03 and above if this option is set "
"to True, DNS records will be treated local to the OVN "
"controller and it will respond to the queries for the "
"records and record types known to it, else it will "
"forward them to the configured DNS server(s).")),
cfg.DictOpt('ovn_dhcp4_global_options',
default={},
help=_("Dictionary of global DHCPv4 options which will be "
@@ -369,6 +377,10 @@ def get_dns_servers():
return cfg.CONF.ovn.dns_servers
def is_dns_records_ovn_owned():
return cfg.CONF.ovn.dns_records_ovn_owned
def get_global_dhcpv4_opts():
return cfg.CONF.ovn.ovn_dhcp4_global_options

View File

@@ -1111,6 +1111,29 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
check_error=True)
raise periodics.NeverAgain()
@has_lock_periodic(
periodic_run_limit=ovn_const.MAINTENANCE_TASK_RETRY_LIMIT,
spacing=ovn_const.MAINTENANCE_ONE_RUN_TASK_SPACING,
run_immediately=True)
def set_ovn_owned_dns_option(self):
"""Set the ovn_owned option as configured for the DNS records"""
cmds = []
ovn_owned = ('true' if ovn_conf.is_dns_records_ovn_owned()
else 'false')
dns_options = {ovn_const.OVN_OWNED: ovn_owned}
for dns in self._nb_idl.dns_list().execute(check_error=True):
if ('ls_name' in dns.external_ids and
dns.options.get(ovn_const.OVN_OWNED) != ovn_owned):
cmds.append(self._nb_idl.dns_set_options(
dns.uuid, **dns_options))
if cmds:
with self._nb_idl.transaction(check_error=True) as txn:
for cmd in cmds:
txn.add(cmd)
raise periodics.NeverAgain()
class HashRingHealthCheckPeriodics:

View File

@@ -2891,6 +2891,14 @@ class OVNClient:
txn.add(self._nb_idl.ls_set_dns_records(ls.uuid, dns_add_txn))
return
# Only run when options column is available
if hasattr(ls_dns_record, 'options'):
ovn_owned = ('true' if ovn_conf.is_dns_records_ovn_owned()
else 'false')
dns_options = {ovn_const.OVN_OWNED: ovn_owned}
txn.add(self._nb_idl.dns_set_options(ls_dns_record.uuid,
**dns_options))
if original_port:
old_records = self.get_port_dns_records(original_port)

View File

@@ -1425,6 +1425,34 @@ class TestMaintenance(_TestMaintenanceHelper):
original_value=True,
config_value=True)
def test_set_ovn_owned_dns_option(self):
neutron_net = self._create_network('network1')
ls_name = utils.ovn_name(neutron_net['id'])
with mock.patch.object(
self._ovn_client, 'is_dns_required_for_port',
return_value=True):
self._create_port('portdns', neutron_net['id'])
ls, ls_dns_record = self.nb_api.get_ls_and_dns_record(ls_name)
# Assert that option is not set
self.assertNotEqual(
ls_dns_record.options.get('ovn-owned'), 'true')
# Override config
cfg.CONF.set_override(
'dns_records_ovn_owned', True, group='ovn')
# Call the maintenance task and check that the option has been
# updated in the DNS record
self.assertRaises(
periodics.NeverAgain,
self.maint.set_ovn_owned_dns_option)
# Assert that option is not set
self.assertEqual(
ls_dns_record.options.get('ovn-owned'), 'true')
class TestLogMaintenance(_TestMaintenanceHelper,
test_log_driver.LogApiTestCaseBase):

View File

@@ -978,3 +978,50 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
mock.call(sroute_a, external_ids=external_ids),
mock.call(sroute_b, external_ids=external_ids),
])
def _test_set_ovn_owned_dns_option(self, dns):
nb_idl = self.fake_ovn_client._nb_idl
nb_idl.dns_list.return_value.execute.return_value = [dns]
self.assertRaises(
periodics.NeverAgain,
self.periodic.set_ovn_owned_dns_option)
def test_set_ovn_owned_dns_option(self):
cfg.CONF.set_override('dns_records_ovn_owned', 'true',
group='ovn')
dns = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={'external_ids': {'ls_name': 'neutron-foo'},
'options': {constants.OVN_OWNED: 'false'}})
self._test_set_ovn_owned_dns_option(dns)
ovn_owned = ('true' if ovn_conf.is_dns_records_ovn_owned()
else 'false')
dns_options = {constants.OVN_OWNED: ovn_owned}
self.fake_ovn_client._nb_idl.dns_set_options.assert_called_once_with(
dns.uuid, **dns_options)
def test_set_ovn_owned_dns_option_already_set(self):
cfg.CONF.set_override('dns_records_ovn_owned', 'true',
group='ovn')
dns = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={'external_ids': {'ls_name': 'neutron-foo'},
'options': {constants.OVN_OWNED: 'true'}})
self._test_set_ovn_owned_dns_option(dns)
# Assert there was no transactions because the value was already set
self.fake_ovn_client._nb_idl.dns_set_options.assert_not_called()
def test_set_ovn_owned_dns_option_ovn_direct_record(self):
dns = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={'external_ids': {'ovn_direct': 'ovn-foo'},
'options': {constants.OVN_OWNED: 'true'}})
self._test_set_ovn_owned_dns_option(dns)
# Assert there was no transactions because the record directly
# created in ovn i.e not created by neutron
self.fake_ovn_client._nb_idl.dns_set_options.assert_not_called()

View File

@@ -0,0 +1,12 @@
---
features:
- |
For OVN version 24.03 and above you can now configure DNS records to be
local to OVN by setting the new configuration option
``[ovn]dns_records_ovn_owned``.
If this option is set to True, DNS records will be treated local to the OVN
controller and it will respond to the queries for the records and record
types known to it, else it will forward them to the configured DNS server(s).
For more information, see bug
`2059405 <https://bugs.launchpad.net/neutron/+bug/2059405>`_.
Default is False.

View File

@@ -45,7 +45,7 @@ osprofiler>=2.3.0 # Apache-2.0
os-ken>=2.11.2 # Apache-2.0
os-resource-classes>=1.1.0 # Apache-2.0
ovs>=2.12.0 # Apache-2.0
ovsdbapp>=2.7.1 # Apache-2.0
ovsdbapp>=2.11.0 # Apache-2.0
psutil>=6.1.0 # BSD
pyroute2>=0.7.3;sys_platform!='win32' # Apache-2.0 (+ dual licensed GPL2)
pyOpenSSL>=17.1.0 # Apache-2.0