diff --git a/neutron/common/ovn/constants.py b/neutron/common/ovn/constants.py index 8c860843101..70c44b9c65c 100644 --- a/neutron/common/ovn/constants.py +++ b/neutron/common/ovn/constants.py @@ -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-' diff --git a/neutron/conf/plugins/ml2/drivers/ovn/ovn_conf.py b/neutron/conf/plugins/ml2/drivers/ovn/ovn_conf.py index 41c49cd235b..c1a4222c23f 100644 --- a/neutron/conf/plugins/ml2/drivers/ovn/ovn_conf.py +++ b/neutron/conf/plugins/ml2/drivers/ovn/ovn_conf.py @@ -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 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 df763bcea90..a7a3df8dd4e 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py @@ -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: diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py index aa1c989a63a..fc341b35ee7 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py @@ -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) 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 0ffb5e0db34..162894d7cd5 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 @@ -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): 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 a891e99a3d1..613c6563a2d 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 @@ -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() diff --git a/releasenotes/notes/add-dns_records_ovn_owned-config-120ef08d5cf659f2.yaml b/releasenotes/notes/add-dns_records_ovn_owned-config-120ef08d5cf659f2.yaml new file mode 100644 index 00000000000..107bac991fc --- /dev/null +++ b/releasenotes/notes/add-dns_records_ovn_owned-config-120ef08d5cf659f2.yaml @@ -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 `_. + Default is False. diff --git a/requirements.txt b/requirements.txt index 4a54a021e3e..c9774e9d1b2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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