From e4536fff7995d2d1b5f2fb3708498fc9691ba5c2 Mon Sep 17 00:00:00 2001 From: goldmung Date: Tue, 1 Oct 2024 15:01:36 +0900 Subject: [PATCH] Add TSIG key support for DNS zones in SDK This commit adds functionality for managing TSIG keys for DNS zones, which was missing in the SDK despite being documented in the DNS API. - Implemented create, delete, and list operations for TSIG keys related to zones. Change-Id: Id13e434aa1497a46b60d35fe03188d30e974b50e --- openstack/dns/v2/_proxy.py | 75 +++++++++++++++++++++ openstack/dns/v2/tsigkey.py | 62 +++++++++++++++++ openstack/tests/unit/dns/v2/test_proxy.py | 28 ++++++++ openstack/tests/unit/dns/v2/test_tsigkey.py | 58 ++++++++++++++++ 4 files changed, 223 insertions(+) create mode 100644 openstack/dns/v2/tsigkey.py create mode 100644 openstack/tests/unit/dns/v2/test_tsigkey.py diff --git a/openstack/dns/v2/_proxy.py b/openstack/dns/v2/_proxy.py index a5497da62..f07895fd5 100644 --- a/openstack/dns/v2/_proxy.py +++ b/openstack/dns/v2/_proxy.py @@ -13,6 +13,7 @@ from openstack.dns.v2 import floating_ip as _fip from openstack.dns.v2 import recordset as _rs from openstack.dns.v2 import service_status as _svc_status +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 from openstack.dns.v2 import zone_import as _zone_import @@ -27,6 +28,7 @@ class Proxy(proxy.Proxy): "recordset": _rs.Recordset, "service_status": _svc_status.ServiceStatus, "zone": _zone.Zone, + "tsigkey": _tsigkey.TSIGKey, "zone_export": _zone_export.ZoneExport, "zone_import": _zone_import.ZoneImport, "zone_share": _zone_share.ZoneShare, @@ -719,3 +721,76 @@ class Proxy(proxy.Proxy): filters=filters, resource_evaluation_fn=resource_evaluation_fn, ) + + # ====== TSIG keys ====== + def tsigkeys(self, **query): + """Retrieve a generator of zones + + :param dict query: Optional query parameters to be sent to limit the + resources being returned. + + :returns: A generator of zone + :class: `~openstack.dns.v2.tsigkey.TSIGKey` instances. + """ + return self._list(_tsigkey.TSIGKey, **query) + + def create_tsigkey(self, **attrs): + """Create a new tsigkey from attributes + + :param dict attrs: Keyword arguments which will be used to create + a :class:`~openstack.dns.v2.tsigkey.Tsigkey`, + comprised of the properties on the Tsigkey class. + :returns: The results of zone creation. + :rtype: :class:`~openstack.dns.v2.tsigkey.Tsigkey` + """ + return self._create(_tsigkey.TSIGKey, prepend_key=False, **attrs) + + def get_tsigkey(self, tsigkey): + """Get a zone + + :param tsigkey: The value can be the ID of a tsigkey + or a :class:'~openstack.dns.v2.tsigkey.TSIGKey' instance. + :returns: A generator of tsigkey + :class:'~openstack.dns.v2.tsigkey.TSIGKey' instances. + """ + return self._get(_tsigkey.TSIGKey, tsigkey) + + def delete_tsigkey( + self, tsigkey, ignore_missing=True, delete_shares=False + ): + """Delete a TSIG key + + :param tsigkey: The value can be the ID of a TSIG key + or a :class:`~openstack.dns.v2.tsigkey.TSIGKey` instance. + :param bool ignore_missing: When set to ``False`` + :class:`~openstack.exceptions.ResourceNotFound` will be raised when + the TSIG key does not exist. + When set to ``True``, no exception will be set when attempting to + delete a nonexistent TSIG key. + + :returns: TSIG Key that has been deleted + :rtype: :class:`~openstack.dns.v2.tsigkey.TSIGKey` + """ + + return self._delete( + _tsigkey.TSIGKey, + tsigkey, + ignore_missing=ignore_missing, + delete_shares=delete_shares, + ) + + def find_tsigkey(self, name_or_id, ignore_missing=True): + """Find a single tsigkey + + :param name_or_id: The name or ID of a tsigkey + :param bool ignore_missing: When set to ``False`` + :class: `!openstack.exceptions.ResourceNotFound` will be raised + when the tsigkey does not exit. + Wehn set to ``True``, no exception will be set when attempting + to delete a nonexitstent zone. + + :returns::class:`~openstack.dns.v2.tsigkey.TSIGKey` + """ + return self._find( + _tsigkey.TSIGKey, name_or_id, ignore_missing=ignore_missing + ) diff --git a/openstack/dns/v2/tsigkey.py b/openstack/dns/v2/tsigkey.py new file mode 100644 index 000000000..089642f21 --- /dev/null +++ b/openstack/dns/v2/tsigkey.py @@ -0,0 +1,62 @@ +# 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 +# from openstack import exceptions +# from openstack import utils + + +class TSIGKey(_base.Resource): + """DNS TSIGKEY Resource""" + + resources_key = 'tsigkeys' + base_path = '/tsigkeys' + + # capabilities + allow_create = True + allow_fetch = True + allow_commit = True + allow_delete = True + allow_list = True + + commit_method = "PATCH" + + _query_mapping = resource.QueryParameters( + 'name', + 'algorithm', + 'scope', + 'limit', + 'marker', + ) + + #: Properties + + #: ID for the resource + id = resource.Body('id') + #: resource id for this tsigkey which can be either zone or pool id + resource_id = resource.Body('resource_id') + #: TSIGKey name + name = resource.Body('name') + #: scope for this tsigkey which can be either ZONE or POOL scope + scope = resource.Body('scope') + #: The actual key to be used + secret = resource.Body('secret') + #: The encryption algorithm for this tsigkey + algorithm = resource.Body('algorithm') + #: Timestamp when the tsigkey was created + created_at = resource.Body('created_at') + #: Timestamp when the tsigkey was last updated + updated_at = resource.Body('updated_at') + #: Links contains a 'self' pertaining to this tsigkey or a 'next' pertaining + #: to next page + links = resource.Body('links', type=dict) diff --git a/openstack/tests/unit/dns/v2/test_proxy.py b/openstack/tests/unit/dns/v2/test_proxy.py index 97b60fe2f..c08d5cc6a 100644 --- a/openstack/tests/unit/dns/v2/test_proxy.py +++ b/openstack/tests/unit/dns/v2/test_proxy.py @@ -14,6 +14,7 @@ from openstack.dns.v2 import _proxy 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 tsigkey from openstack.dns.v2 import zone from openstack.dns.v2 import zone_export from openstack.dns.v2 import zone_import @@ -324,3 +325,30 @@ class TestDnsServiceStatus(TestDnsProxy): self.verify_get( self.proxy.get_service_status, service_status.ServiceStatus ) + + +class TestDnsTsigKey(TestDnsProxy): + def test_tsigkey_create(self): + self.verify_create( + self.proxy.create_tsigkey, + tsigkey.TSIGKey, + method_kwargs={'name': 'id'}, + expected_kwargs={'name': 'id', 'prepend_key': False}, + ) + + def test_tsigkey_delete(self): + self.verify_delete( + self.proxy.delete_tsigkey, + tsigkey.TSIGKey, + True, + expected_kwargs={'ignore_missing': True, 'delete_shares': False}, + ) + + def test_tsigkey_find(self): + self.verify_find(self.proxy.find_tsigkey, tsigkey.TSIGKey) + + def test_tsigkey_get(self): + self.verify_get(self.proxy.get_tsigkey, tsigkey.TSIGKey) + + def test_tesigkeys(self): + self.verify_list(self.proxy.tsigkeys, tsigkey.TSIGKey) diff --git a/openstack/tests/unit/dns/v2/test_tsigkey.py b/openstack/tests/unit/dns/v2/test_tsigkey.py new file mode 100644 index 000000000..bf4d4ba91 --- /dev/null +++ b/openstack/tests/unit/dns/v2/test_tsigkey.py @@ -0,0 +1,58 @@ +# 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 tsigkey +from openstack.tests.unit import base + +IDENTIFIER = '4c72c7d3-6cfa-4fe1-9984-7705119f0228' +EXAMPLE = { + "id": IDENTIFIER, + "name": 'test-key', + "algorithm": 'hmac-sha512', + "secret": 'test-secret', + "scope": 'POOL', + "resource_id": IDENTIFIER, +} + + +class TestTsigKey(base.TestCase): + def test_basic(self): + sot = tsigkey.TSIGKey() + self.assertEqual(None, sot.resource_key) + self.assertEqual('tsigkeys', sot.resources_key) + self.assertEqual('/tsigkeys', sot.base_path) + self.assertTrue(sot.allow_create) + self.assertTrue(sot.allow_fetch) + self.assertTrue(sot.allow_commit) + self.assertTrue(sot.allow_delete) + self.assertTrue(sot.allow_list) + self.assertEqual('PATCH', sot.commit_method) + + self.assertDictEqual( + { + 'name': 'name', + 'algorithm': 'algorithm', + 'scope': 'scope', + 'limit': 'limit', + 'marker': 'marker', + }, + sot._query_mapping._mapping, + ) + + def test_make_it(self): + sot = tsigkey.TSIGKey(**EXAMPLE) + self.assertEqual(IDENTIFIER, sot.id) + self.assertEqual(EXAMPLE['name'], sot.name) + self.assertEqual(EXAMPLE['algorithm'], sot.algorithm) + self.assertEqual(EXAMPLE['scope'], sot.scope) + self.assertEqual(EXAMPLE['resource_id'], sot.resource_id) + self.assertEqual(EXAMPLE['secret'], sot.secret)