From 1f27246a73dda62e36f26d3a0839068ea7652a9f Mon Sep 17 00:00:00 2001 From: "thor.huhji" Date: Sat, 21 Jun 2025 15:24:13 +0900 Subject: [PATCH] Add support for dns blacklist CRUD - Add dns blacklist CRUD supports - Add unit test code for blacklist CRUD - Add blacklist docs - Add functional test for blacklist CRUD Change-Id: Idc003887abc0a120e382e3f49a77bf7c509b4c15 Signed-off-by: jihyun huh --- doc/source/user/proxies/dns.rst | 9 +++ doc/source/user/resources/dns/index.rst | 1 + .../user/resources/dns/v2/blacklist.rst | 12 ++++ openstack/dns/v2/_proxy.py | 59 ++++++++++++++++++ openstack/dns/v2/blacklist.py | 47 ++++++++++++++ .../tests/functional/dns/v2/test_blacklist.py | 61 +++++++++++++++++++ openstack/tests/unit/dns/v2/test_blacklist.py | 41 +++++++++++++ openstack/tests/unit/dns/v2/test_proxy.py | 30 +++++++++ 8 files changed, 260 insertions(+) create mode 100644 doc/source/user/resources/dns/v2/blacklist.rst create mode 100644 openstack/dns/v2/blacklist.py create mode 100644 openstack/tests/functional/dns/v2/test_blacklist.py create mode 100644 openstack/tests/unit/dns/v2/test_blacklist.py diff --git a/doc/source/user/proxies/dns.rst b/doc/source/user/proxies/dns.rst index cc46163a2..7e61198b5 100644 --- a/doc/source/user/proxies/dns.rst +++ b/doc/source/user/proxies/dns.rst @@ -82,3 +82,12 @@ Service Status Operations .. autoclass:: openstack.dns.v2._proxy.Proxy :noindex: :members: service_statuses, get_service_status + + +Blacklist Operations +^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: openstack.dns.v2._proxy.Proxy + :noindex: + :members: blacklists, get_blacklist, create_blacklist, + update_blacklist, delete_blacklist diff --git a/doc/source/user/resources/dns/index.rst b/doc/source/user/resources/dns/index.rst index f7cb0e96b..e1826ca61 100644 --- a/doc/source/user/resources/dns/index.rst +++ b/doc/source/user/resources/dns/index.rst @@ -13,3 +13,4 @@ DNS Resources v2/recordset v2/limit v2/service_status + v2/blacklist diff --git a/doc/source/user/resources/dns/v2/blacklist.rst b/doc/source/user/resources/dns/v2/blacklist.rst new file mode 100644 index 000000000..d483eb9a1 --- /dev/null +++ b/doc/source/user/resources/dns/v2/blacklist.rst @@ -0,0 +1,12 @@ +openstack.dns.v2.blacklist +========================== + +.. automodule:: openstack.dns.v2.blacklist + +The Blacklist Class +------------------- + +The ``Blacklist`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.dns.v2.blacklist.Blacklist + :members: diff --git a/openstack/dns/v2/_proxy.py b/openstack/dns/v2/_proxy.py index c7c4539c7..a3d376b24 100644 --- a/openstack/dns/v2/_proxy.py +++ b/openstack/dns/v2/_proxy.py @@ -12,6 +12,7 @@ import typing as ty +from openstack.dns.v2 import blacklist as _blacklist from openstack.dns.v2 import floating_ip as _fip from openstack.dns.v2 import limit as _limit from openstack.dns.v2 import recordset as _rs @@ -29,6 +30,7 @@ from openstack import resource class Proxy(proxy.Proxy): _resource_registry = { + "blacklist": _blacklist.Blacklist, "floating_ip": _fip.FloatingIP, "limits": _limit.Limit, "recordset": _rs.Recordset, @@ -891,3 +893,60 @@ class Proxy(proxy.Proxy): return self._find( _tsigkey.TSIGKey, name_or_id, ignore_missing=ignore_missing ) + + # ======== Blacklists ======== + def blacklists(self, **query): + """Retrieve a generator of blacklists + + :returns: A generator of blacklist + (:class:`~openstack.dns.v2.blacklist.Blacklist`) instances + """ + return self._list(_blacklist.Blacklist, **query) + + def get_blacklist(self, blacklist): + """Get a blacklist + + :param blacklist: The value can be the ID of a blacklist + or a :class:`~openstack.dns.v2.blacklist.Blacklist` instance. + + :returns: Blacklist instance. + :rtype: :class:`~openstack.dns.v2.blacklist.Blacklist` + """ + return self._get(_blacklist.Blacklist, blacklist) + + def create_blacklist(self, **attrs): + """Create a new blacklist + + :param attrs: Keyword arguments which will be used to create + a :class:`~openstack.dns.v2.blacklist.Blacklist`, + comprised of the properties on the Blacklist class. + + :returns: The results of blacklist creation. + :rtype: :class:`~openstack.dns.v2.blacklist.Blacklist` + """ + return self._create(_blacklist.Blacklist, prepend_key=False, **attrs) + + def update_blacklist(self, blacklist, **attrs): + """Update blacklist attributes + + :param blacklist: The id or an instance of + :class: `~openstack.dns.v2.blacklist.Blacklist`. + :param attrs: attributes for update on + :class: `~openstack.dns.v2.blacklist.Blacklist`. + + :rtype: :class: `~openstack.dns.v2.blacklist.Blacklist`. + """ + return self._update(_blacklist.Blacklist, blacklist, **attrs) + + def delete_blacklist(self, blacklist, ignore_missing=True): + """Delete a blacklist + + :param blacklist: The id or an instance of + :class: `~openstack.dns.v2.blacklist.Blacklist`. + + :returns: Blacklist been deleted + :rtype: :class:`~openstack.dns.v2.blacklist.Blacklist` + """ + return self._delete( + _blacklist.Blacklist, blacklist, ignore_missing=ignore_missing + ) diff --git a/openstack/dns/v2/blacklist.py b/openstack/dns/v2/blacklist.py new file mode 100644 index 000000000..6c6d048d7 --- /dev/null +++ b/openstack/dns/v2/blacklist.py @@ -0,0 +1,47 @@ +# 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 openstack.dns.v2 import _base +from openstack import resource + + +class Blacklist(_base.Resource): + """DNS Blacklist Resource""" + + resources_key = 'blacklists' + base_path = '/blacklists' + + # capabilities + allow_list = True + allow_create = True + allow_fetch = True + allow_commit = True + allow_delete = True + commit_method = "PATCH" + + _query_mapping = resource.QueryParameters( + 'pattern', + ) + + #: Properties + #: ID for the resource + id = resource.Body('id') + #: Pattern for this blacklist + pattern = resource.Body('pattern') + #: Description for this blacklist + description = resource.Body("description") + #: Timestampe when the blacklist created + created_at = resource.Body("created_at") + #: Timestampe when the blacklist last updated + updated_at = resource.Body("updated_at") + #: Links to the resource, and the other related resources. + links = resource.Body("links") diff --git a/openstack/tests/functional/dns/v2/test_blacklist.py b/openstack/tests/functional/dns/v2/test_blacklist.py new file mode 100644 index 000000000..2ea931ae2 --- /dev/null +++ b/openstack/tests/functional/dns/v2/test_blacklist.py @@ -0,0 +1,61 @@ +# 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. +import uuid + +from openstack.dns.v2 import blacklist as _blacklist +from openstack.tests.functional import base + + +class TestBlackList(base.BaseFunctionalTest): + def setUp(self): + super().setUp() + self.require_service('dns') + + # Note: use a unique UUID pattern to avoid test collisions + self.pattern = rf".*\.test-{uuid.uuid4().hex}.com" + self.description = self.getUniqueString('blacklist') + + def _delete_blacklist(self, blacklist): + ret = self.operator_cloud.dns.delete_blacklist(blacklist.id) + self.assertIsNone(ret) + + def test_blacklist(self): + # create blacklist + blacklist = self.operator_cloud.dns.create_blacklist( + pattern=self.pattern, + description=self.description, + ) + self.assertIsNotNone(blacklist.id) + self.assertIsInstance(blacklist, _blacklist.Blacklist) + self.assertEqual(self.pattern, blacklist.pattern) + self.assertEqual(self.description, blacklist.description) + self.addCleanup(self._delete_blacklist, blacklist) + + # update blacklist + blacklist = self.operator_cloud.dns.update_blacklist( + blacklist, pattern=self.pattern, description=self.description + ) + self.assertIsInstance(blacklist, _blacklist.Blacklist) + self.assertEqual(self.pattern, blacklist.pattern) + self.assertEqual(self.description, blacklist.description) + + # get blacklist + blacklist = self.operator_cloud.dns.get_blacklist(blacklist.id) + self.assertIsInstance(blacklist, _blacklist.Blacklist) + self.assertEqual(self.pattern, blacklist.pattern) + self.assertEqual(self.description, blacklist.description) + + # list all blacklists + blacklists = list(self.operator_cloud.dns.blacklists()) + self.assertIsInstance(blacklists[0], _blacklist.Blacklist) + self.assertIn(self.pattern, {x.pattern for x in blacklists}) + self.operator_cloud.dns.delete_blacklist(blacklist.id) diff --git a/openstack/tests/unit/dns/v2/test_blacklist.py b/openstack/tests/unit/dns/v2/test_blacklist.py new file mode 100644 index 000000000..e7a166d3f --- /dev/null +++ b/openstack/tests/unit/dns/v2/test_blacklist.py @@ -0,0 +1,41 @@ +# 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 openstack.dns.v2 import blacklist +from openstack.tests.unit import base + +IDENTIFIER = '373cb85e-0f4a-487a-846e-dce7a65cca4d' +EXAMPLE = { + 'id': IDENTIFIER, + 'description': 'blacklist test description', + 'pattern': '.*example.com.', +} + + +class TestBlackList(base.TestCase): + def test_basic(self): + sot = blacklist.Blacklist() + self.assertEqual(None, sot.resource_key) + self.assertEqual('blacklists', sot.resources_key) + self.assertEqual('/blacklists', sot.base_path) + self.assertTrue(sot.allow_list) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertTrue(sot.allow_commit) + self.assertTrue(sot.allow_delete) + self.assertEqual('PATCH', sot.commit_method) + + def test_make_it(self): + sot = blacklist.Blacklist(**EXAMPLE) + self.assertEqual(IDENTIFIER, sot.id) + self.assertEqual(EXAMPLE['description'], sot.description) + self.assertEqual(EXAMPLE['pattern'], sot.pattern) diff --git a/openstack/tests/unit/dns/v2/test_proxy.py b/openstack/tests/unit/dns/v2/test_proxy.py index aacc699ae..cb9541617 100644 --- a/openstack/tests/unit/dns/v2/test_proxy.py +++ b/openstack/tests/unit/dns/v2/test_proxy.py @@ -11,6 +11,7 @@ # under the License. from openstack.dns.v2 import _proxy +from openstack.dns.v2 import blacklist from openstack.dns.v2 import floating_ip from openstack.dns.v2 import recordset from openstack.dns.v2 import service_status @@ -363,3 +364,32 @@ class TestDnsTsigKey(TestDnsProxy): def test_tesigkeys(self): self.verify_list(self.proxy.tsigkeys, tsigkey.TSIGKey) + + +class TestDnsBlacklist(TestDnsProxy): + def test_blacklist_create(self): + self.verify_create( + self.proxy.create_blacklist, + blacklist.Blacklist, + method_kwargs={'pattern': '.*\.example\.com'}, + expected_kwargs={ + 'pattern': '.*\.example\.com', + 'prepend_key': False, + }, + ) + + def test_blacklist_delete(self): + self.verify_delete( + self.proxy.delete_blacklist, + blacklist.Blacklist, + ignore_missing=True, + ) + + def test_blacklist_update(self): + self.verify_update(self.proxy.update_blacklist, blacklist.Blacklist) + + def test_blacklist_get(self): + self.verify_get(self.proxy.get_blacklist, blacklist.Blacklist) + + def test_blacklists(self): + self.verify_list(self.proxy.blacklists, blacklist.Blacklist)