diff --git a/doc/source/user/proxies/dns.rst b/doc/source/user/proxies/dns.rst index 7e61198b5..a629375ed 100644 --- a/doc/source/user/proxies/dns.rst +++ b/doc/source/user/proxies/dns.rst @@ -51,6 +51,13 @@ FloatingIP Operations :noindex: :members: floating_ips, get_floating_ip, update_floating_ip +TLD Operations +^^^^^^^^^^^^^^ + +.. autoclass:: openstack.dns.v2._proxy.Proxy + :noindex: + :members: create_tld, delete_tld, get_tld, find_tld, tlds + Zone Transfer Operations ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/source/user/resources/dns/index.rst b/doc/source/user/resources/dns/index.rst index e1826ca61..90a58a82d 100644 --- a/doc/source/user/resources/dns/index.rst +++ b/doc/source/user/resources/dns/index.rst @@ -10,6 +10,7 @@ DNS Resources v2/zone_import v2/zone_share v2/floating_ip + v2/tld v2/recordset v2/limit v2/service_status diff --git a/doc/source/user/resources/dns/v2/tld.rst b/doc/source/user/resources/dns/v2/tld.rst new file mode 100644 index 000000000..bcafc5cc9 --- /dev/null +++ b/doc/source/user/resources/dns/v2/tld.rst @@ -0,0 +1,12 @@ +openstack.dns.v2.tld +============================== + +.. automodule:: openstack.dns.v2.tld + +The TLD Class +-------------- + +The ``DNS`` class inherits from :class:`~openstack.resource.Resource`. + +.. autoclass:: openstack.dns.v2.tld.TLD + :members: diff --git a/openstack/dns/v2/_proxy.py b/openstack/dns/v2/_proxy.py index a3d376b24..a28f235f0 100644 --- a/openstack/dns/v2/_proxy.py +++ b/openstack/dns/v2/_proxy.py @@ -17,6 +17,7 @@ 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 from openstack.dns.v2 import service_status as _svc_status +from openstack.dns.v2 import tld as _tld from openstack.dns.v2 import tsigkey as _tsigkey from openstack.dns.v2 import zone as _zone from openstack.dns.v2 import zone_export as _zone_export @@ -42,6 +43,7 @@ class Proxy(proxy.Proxy): "zone_nameserver": _zone_nameserver.ZoneNameserver, "zone_share": _zone_share.ZoneShare, "zone_transfer_request": _zone_transfer.ZoneTransferRequest, + "tld": _tld.TLD, } # ======== Zones ======== @@ -717,6 +719,87 @@ class Proxy(proxy.Proxy): """ return self._get(_svc_status.ServiceStatus, service) + # ======== TLDs ======== + def tlds(self, **query): + """Retrieve a generator of tlds + + :param dict query: Optional query parameters to be sent to limit the + resources being returned. + + * `name`: TLD Name field. + + :returns: A generator of tld + :class:`~openstack.dns.v2.tld.TLD` instances. + """ + return self._list(_tld.TLD, **query) + + def create_tld(self, **attrs): + """Create a new tld from attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~openstack.dns.v2.tld.TLD`, + comprised of the properties on the TLD class. + :returns: The results of TLD creation. + :rtype: :class:`~openstack.dns.v2.tld.TLD` + """ + return self._create(_tld.TLD, prepend_key=False, **attrs) + + def get_tld(self, tld): + """Get a tld + + :param tld: The value can be the ID of a tld + or a :class:`~openstack.dns.v2.tld.TLD` instance. + :returns: tld instance. + :rtype: :class:`~openstack.dns.v2.tld.TLD` + """ + return self._get(_tld.TLD, tld) + + def delete_tld(self, tld, ignore_missing=True): + """Delete a tld + + :param tld: The value can be the ID of a tld + or a :class:`~openstack.dns.v2.tld.TLD` instance. + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.NotFoundException` will be raised when + the tld does not exist. + When set to ``True``, no exception will be set when attempting to + delete a nonexistent tld. + + :returns: TLD been deleted + :rtype: :class:`~openstack.dns.v2.tld.TLD` + """ + return self._delete( + _tld.TLD, + tld, + ignore_missing=ignore_missing, + ) + + def update_tld(self, tld, **attrs): + """Update tld attributes + + :param tld: The id or an instance of + :class:`~openstack.dns.v2.tld.TLD`. + :param dict attrs: attributes for update on + :class:`~openstack.dns.v2.tld.TLD`. + + :rtype: :class:`~openstack.dns.v2.tld.TLD` + """ + return self._update(_tld.TLD, tld, **attrs) + + def find_tld(self, name_or_id, ignore_missing=True): + """Find a single tld + + :param name_or_id: The name or ID of a tld + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.NotFoundException` will be raised + when the tld does not exist. + When set to ``True``, no exception will be set when attempting + to delete a nonexistent tld. + + :returns: :class:`~openstack.dns.v2.tld.TLD` + """ + return self._find(_tld.TLD, name_or_id, ignore_missing=ignore_missing) + # ========== Utilities ========== def wait_for_status( self, diff --git a/openstack/dns/v2/tld.py b/openstack/dns/v2/tld.py new file mode 100644 index 000000000..0a96481b8 --- /dev/null +++ b/openstack/dns/v2/tld.py @@ -0,0 +1,49 @@ +# 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 TLD(_base.Resource): + """DNS TLD Resource""" + + resources_key = "tlds" + base_path = "/tlds" + + # capabilities + allow_create = True + allow_fetch = True + allow_commit = True + allow_delete = True + allow_list = True + + commit_method = "PATCH" + + _query_mapping = resource.QueryParameters( + "name", + "description", + "limit", + "marker", + ) + + #: TLD name + name = resource.Body("name") + #: TLD description + description = resource.Body("description") + #: Timestamp when the tld was created + created_at = resource.Body("created_at") + #: Timestamp when the tld was last updated + updated_at = resource.Body("updated_at") + #: Links contains a `self` pertaining to this tld or a `next` pertaining + #: to next page + links = resource.Body("links", type=dict) diff --git a/openstack/tests/functional/dns/v2/test_tld.py b/openstack/tests/functional/dns/v2/test_tld.py new file mode 100644 index 000000000..ffb40de1b --- /dev/null +++ b/openstack/tests/functional/dns/v2/test_tld.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. + +from openstack.dns.v2 import tld as _tld +from openstack.tests.functional import base + + +class TestTLD(base.BaseFunctionalTest): + def setUp(self): + super().setUp() + + self.require_service('dns') + + self.tld_name = 'xyz' + self.tld_description = 'The xyz TLD' + + def test_tld(self): + # create the tld + + tld = self.operator_cloud.dns.create_tld( + name=self.tld_name, description=self.tld_description + ) + self.assertIsInstance(tld, _tld.TLD) + self.assertEqual(self.tld_description, tld.description) + self.addCleanup(self.operator_cloud.dns.delete_tld, tld) + + # update the tld + + tld = self.operator_cloud.dns.update_tld( + tld, description=self.tld_description + ) + self.assertIsInstance(tld, _tld.TLD) + self.assertEqual(self.tld_description, tld.description) + + # retrieve details of the (updated) tld by ID + + tld = self.operator_cloud.dns.get_tld(tld.id) + self.assertIsInstance(tld, _tld.TLD) + self.assertEqual(self.tld_description, tld.description) + + # retrieve details of the (updated) tld by name + + tld = self.operator_cloud.dns.find_tld(tld.name) + self.assertIsInstance(tld, _tld.TLD) + self.assertEqual(self.tld_description, tld.description) + + # list all tlds + tlds = list(self.operator_cloud.dns.tlds()) + self.assertIsInstance(tlds[0], _tld.TLD) + self.assertIn( + self.tld_name, {x.name for x in self.operator_cloud.dns.tlds()} + ) diff --git a/openstack/tests/unit/dns/v2/test_proxy.py b/openstack/tests/unit/dns/v2/test_proxy.py index cb9541617..14773738d 100644 --- a/openstack/tests/unit/dns/v2/test_proxy.py +++ b/openstack/tests/unit/dns/v2/test_proxy.py @@ -15,6 +15,7 @@ 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 +from openstack.dns.v2 import tld from openstack.dns.v2 import tsigkey from openstack.dns.v2 import zone from openstack.dns.v2 import zone_export @@ -393,3 +394,33 @@ class TestDnsBlacklist(TestDnsProxy): def test_blacklists(self): self.verify_list(self.proxy.blacklists, blacklist.Blacklist) + + +class TestDnsTLD(TestDnsProxy): + def test_tld_create(self): + self.verify_create( + self.proxy.create_tld, + tld.TLD, + method_kwargs={"name": "id"}, + expected_kwargs={"name": "id", "prepend_key": False}, + ) + + def test_tld_delete(self): + self.verify_delete( + self.proxy.delete_tld, + tld.TLD, + True, + expected_kwargs={"ignore_missing": True}, + ) + + def test_tld_find(self): + self.verify_find(self.proxy.find_tld, tld.TLD) + + def test_tld_get(self): + self.verify_get(self.proxy.get_tld, tld.TLD) + + def test_tlds(self): + self.verify_list(self.proxy.tlds, tld.TLD) + + def test_tld_update(self): + self.verify_update(self.proxy.update_tld, tld.TLD) diff --git a/openstack/tests/unit/dns/v2/test_tld.py b/openstack/tests/unit/dns/v2/test_tld.py new file mode 100644 index 000000000..c8d714eba --- /dev/null +++ b/openstack/tests/unit/dns/v2/test_tld.py @@ -0,0 +1,66 @@ +# 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 + +from keystoneauth1 import adapter + +from openstack.dns.v2 import tld +from openstack.tests.unit import base + +IDENTIFIER = "NAME" +EXAMPLE = { + "id": IDENTIFIER, + "name": "com", + "description": "tld description", +} + + +class TestTLD(base.TestCase): + def setUp(self): + super().setUp() + self.resp = mock.Mock() + self.resp.body = None + self.resp.json = mock.Mock(return_value=self.resp.body) + self.resp.status_code = 200 + self.sess = mock.Mock(spec=adapter.Adapter) + self.sess.post = mock.Mock(return_value=self.resp) + self.sess.default_microversion = None + + def test_basic(self): + sot = tld.TLD() + self.assertEqual(None, sot.resource_key) + self.assertEqual("tlds", sot.resources_key) + self.assertEqual("/tlds", 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) + + self.assertDictEqual( + { + "description": "description", + "name": "name", + "limit": "limit", + "marker": "marker", + }, + sot._query_mapping._mapping, + ) + + def test_make_it(self): + sot = tld.TLD(**EXAMPLE) + self.assertEqual(IDENTIFIER, sot.id) + self.assertEqual(EXAMPLE["description"], sot.description) + self.assertEqual(EXAMPLE["name"], sot.name) diff --git a/releasenotes/notes/add-dns-tld-d3cfac70f76637e3.yaml b/releasenotes/notes/add-dns-tld-d3cfac70f76637e3.yaml new file mode 100644 index 000000000..d33e47f44 --- /dev/null +++ b/releasenotes/notes/add-dns-tld-d3cfac70f76637e3.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Adds support for `dns tld + `_ service.