diff options
-rw-r--r-- | .zuul.yaml | 2 | ||||
-rw-r--r-- | designate/backend/impl_infoblox/connector.py | 5 | ||||
-rw-r--r-- | designate/backend/impl_infoblox/object_manipulator.py | 26 | ||||
-rw-r--r-- | designate/common/decorators/lock.py | 4 | ||||
-rw-r--r-- | designate/tests/unit/backend/test_infoblox.py | 5 | ||||
-rw-r--r-- | designate/tests/unit/common/__init__.py | 0 | ||||
-rw-r--r-- | designate/tests/unit/common/test_zone_lock.py | 110 | ||||
-rw-r--r-- | releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po | 12 | ||||
-rw-r--r-- | setup.cfg | 12 |
9 files changed, 159 insertions, 17 deletions
@@ -1,6 +1,7 @@ - job: name: designate-base parent: devstack-tempest + nodeset: openstack-single-node-jammy vars: &base_vars devstack_localrc: DESIGNATE_SERVICE_PORT_DNS: 5322 @@ -35,6 +36,7 @@ - job: name: designate-base-ipv6-only parent: devstack-tempest-ipv6 + nodeset: openstack-single-node-jammy description: | Designate devstack-tempest base job for IPv6-only deployment irrelevant-files: *base_irrelevant_files diff --git a/designate/backend/impl_infoblox/connector.py b/designate/backend/impl_infoblox/connector.py index fab9d784..aaf1456f 100644 --- a/designate/backend/impl_infoblox/connector.py +++ b/designate/backend/impl_infoblox/connector.py @@ -131,7 +131,10 @@ class Infoblox(object): headers = {'Content-type': 'application/json'} - data = jsonutils.dump_as_bytes(payload) + # NOTE (scottsol): This can trigger an internal error in Infoblox if + # jsonutils sets it to 'null' (a string with quotes). Setting to None + # works around this and returns a valid response from Infoblox + data = jsonutils.dump_as_bytes(payload) if payload else None url = self._construct_url(objtype, query_params, extattrs) r = self.session.get(url, diff --git a/designate/backend/impl_infoblox/object_manipulator.py b/designate/backend/impl_infoblox/object_manipulator.py index a2d4fdf5..dbbb431d 100644 --- a/designate/backend/impl_infoblox/object_manipulator.py +++ b/designate/backend/impl_infoblox/object_manipulator.py @@ -122,14 +122,16 @@ class InfobloxObjectManipulator(object): 'zone_auth', {'fqdn': fqdn, 'view': dns_view}, {'ns_group': self.connector.ns_group, - 'restart_if_needed': True, 'zone_format': zone_format}, + 'zone_format': zone_format}, check_if_exists=True) + self._restart_if_needed() except exc.InfobloxCannotCreateObject as e: LOG.warning(e) def delete_zone_auth(self, fqdn): self._delete_infoblox_object( 'zone_auth', {'fqdn': fqdn}) + self._restart_if_needed() def _create_infoblox_object(self, obj_type, payload, additional_create_kwargs=None, @@ -205,3 +207,25 @@ class InfobloxObjectManipulator(object): if ib_object_ref: self.connector.delete_object(ib_object_ref) LOG.info('Infoblox object was deleted: %s', ib_object_ref) + + def _restart_if_needed(self): + ib_object_ref = None + obj_type = 'grid' + warn_msg = ('Infoblox %(obj_type)s will not be restarted because' + ' the API object reference cannot be found') + try: + ib_object_ref = self._get_infoblox_object_or_none(obj_type) + if not ib_object_ref: + LOG.warning(warn_msg, {'obj_type': obj_type}) + except exc.InfobloxSearchError as e: + LOG.warning(warn_msg, {'obj_type': obj_type}) + LOG.info(e) + + if ib_object_ref: + payload = { + "restart_option": "RESTART_IF_NEEDED", + "mode": "GROUPED", + "services": ["DNS"], + } + self.connector.call_func( + 'restartservices', ib_object_ref, payload) diff --git a/designate/common/decorators/lock.py b/designate/common/decorators/lock.py index f633fa4d..97010492 100644 --- a/designate/common/decorators/lock.py +++ b/designate/common/decorators/lock.py @@ -52,7 +52,7 @@ def extract_zone_id(args, kwargs): for arg in itertools.chain(args, kwargs.values()): if not isinstance(arg, objects.DesignateObject): continue - if isinstance(arg, objects.Zone): + elif isinstance(arg, objects.Zone): zone_id = arg.id if zone_id: break @@ -68,8 +68,6 @@ def extract_zone_id(args, kwargs): arg = args[1] if isinstance(arg, str): zone_id = arg - elif isinstance(zone_id, objects.Zone): - zone_id = arg.id return zone_id diff --git a/designate/tests/unit/backend/test_infoblox.py b/designate/tests/unit/backend/test_infoblox.py index 81c8351b..731757c4 100644 --- a/designate/tests/unit/backend/test_infoblox.py +++ b/designate/tests/unit/backend/test_infoblox.py @@ -88,6 +88,11 @@ class InfobloxBackendTestCase(oslotest.base.BaseTestCase): json={}, ) + req_mock.get( + '%s/v2.0/grid' % self.base_address, + json={}, + ) + self.backend.create_zone(self.context, self.zone) self.backend.delete_zone(self.context, self.zone) diff --git a/designate/tests/unit/common/__init__.py b/designate/tests/unit/common/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/designate/tests/unit/common/__init__.py diff --git a/designate/tests/unit/common/test_zone_lock.py b/designate/tests/unit/common/test_zone_lock.py new file mode 100644 index 00000000..e2d7f4b1 --- /dev/null +++ b/designate/tests/unit/common/test_zone_lock.py @@ -0,0 +1,110 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +from unittest import mock + +import oslotest.base + +from designate.common.decorators import lock +from designate import objects + + +class TestExtractZoneId(oslotest.base.BaseTestCase): + def setUp(self): + super(TestExtractZoneId, self).setUp() + + def test_extract_zone_id_empty(self): + self.assertIsNone(lock.extract_zone_id([], {})) + + def test_extract_zone_id_no_valid_objects(self): + self.assertIsNone( + lock.extract_zone_id([], { + 'ptr': objects.PTRList(), 'a': objects.AList()}) + ) + + def test_extract_zone_id_kwargs(self): + self.assertEqual( + 'test', + lock.extract_zone_id([], {'zone_id': 'test'}) + ) + self.assertEqual( + 'test', + lock.extract_zone_id([], {'zone': mock.Mock(id='test')}) + ) + self.assertEqual( + 'test', + lock.extract_zone_id([], {'recordset': mock.Mock(zone_id='test')}) + ) + self.assertEqual( + 'test', + lock.extract_zone_id([], {'record': mock.Mock(zone_id='test')}) + ) + + def test_extract_zone_id_from_zone(self): + self.assertEqual( + '123', + lock.extract_zone_id(['a', 'b', 'c'], {'x': objects.Zone(id=123)}) + ) + self.assertEqual( + '123', + lock.extract_zone_id([objects.Zone(id=123)], {}) + ) + + def test_extract_zone_id_from_recordset(self): + self.assertEqual( + '123', + lock.extract_zone_id([], {'x': objects.RecordSet(zone_id=123)}) + ) + self.assertEqual( + '123', + lock.extract_zone_id([objects.RecordSet(zone_id=123)], {}) + ) + + def test_extract_zone_id_from_record(self): + self.assertEqual( + '123', + lock.extract_zone_id([], {'x': objects.Record(zone_id=123)}) + ) + self.assertEqual( + '123', + lock.extract_zone_id([objects.Record(zone_id=123)], {}) + ) + + def test_extract_zone_id_from_zone_transfer_request(self): + self.assertEqual( + '123', + lock.extract_zone_id( + [], {'x': objects.ZoneTransferRequest(zone_id=123)}) + ) + self.assertEqual( + '123', + lock.extract_zone_id( + [objects.ZoneTransferRequest(zone_id=123)], {}) + ) + + def test_extract_zone_id_from_zone_transfer_accept(self): + self.assertEqual( + '123', + lock.extract_zone_id( + [], {'x': objects.ZoneTransferAccept(zone_id=123)}) + ) + self.assertEqual( + '123', + lock.extract_zone_id([objects.ZoneTransferAccept(zone_id=123)], {}) + ) + + def test_extract_zone_id_from_second_argument(self): + self.assertEqual('456', lock.extract_zone_id(['123', '456'], {})) + + def test_extract_zone_id_when_second_argument_is_a_zone(self): + self.assertEqual( + '456', lock.extract_zone_id(['123', objects.Zone(id=456)], {}) + ) diff --git a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po index 7ff248ae..22363700 100644 --- a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po +++ b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po @@ -8,11 +8,11 @@ msgid "" msgstr "" "Project-Id-Version: Designate Release Notes\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-10-15 00:52+0000\n" +"POT-Creation-Date: 2022-10-25 21:48+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2022-10-16 10:42+0000\n" +"PO-Revision-Date: 2022-11-04 10:30+0000\n" "Last-Translator: Andi Chandler <andi@gowling.com>\n" "Language-Team: English (United Kingdom)\n" "Language: en_GB\n" @@ -46,8 +46,8 @@ msgstr "12.0.0" msgid "12.1.0" msgstr "12.1.0" -msgid "12.1.0-4" -msgstr "12.1.0-4" +msgid "12.1.0-5" +msgstr "12.1.0-5" msgid "13.0.0" msgstr "13.0.0" @@ -70,8 +70,8 @@ msgstr "14.0.1-5" msgid "15.0.0" msgstr "15.0.0" -msgid "15.0.0.0rc1-11" -msgstr "15.0.0.0rc1-11" +msgid "15.0.0.0rc1-14" +msgstr "15.0.0.0rc1-14" msgid "2.0.0" msgstr "2.0.0" @@ -1,13 +1,13 @@ [metadata] name = designate -summary = DNS as a Service -description_file = - README.rst +description = DNS as a Service +long_description = file: README.rst + author = OpenStack author_email = openstack-discuss@lists.openstack.org -home_page = https://docs.openstack.org/designate/latest/ +url = https://docs.openstack.org/designate/latest/ python_requires = >=3.8 -classifier = +classifiers = Environment :: OpenStack Environment :: No Input/Output (Daemon) Intended Audience :: Information Technology @@ -19,7 +19,7 @@ classifier = Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 Topic :: Internet :: Name Service (DNS) [files] |