dns: Add support for quotas
This patch add quota support for designate(DNS) API - add dns quota resource & proxy methods - add unit & functional test - add docs and release note - fix tld functional test code Change-Id: Id194b6871e2bb80d08b0bb0113bf3fa64e419385 Signed-off-by: choieastsea <choieastsea@gmail.com>
This commit is contained in:

committed by
Stephen Finucane

parent
633c73018e
commit
c826687bbf
@@ -83,6 +83,13 @@ Limit Operations
|
||||
:noindex:
|
||||
:members: limits
|
||||
|
||||
Quota Operations
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autoclass:: openstack.dns.v2._proxy.Proxy
|
||||
:noindex:
|
||||
:members: quotas, get_quota, update_quota, delete_quota
|
||||
|
||||
Service Status Operations
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@@ -13,5 +13,6 @@ DNS Resources
|
||||
v2/tld
|
||||
v2/recordset
|
||||
v2/limit
|
||||
v2/quota
|
||||
v2/service_status
|
||||
v2/blacklist
|
||||
|
12
doc/source/user/resources/dns/v2/quota.rst
Normal file
12
doc/source/user/resources/dns/v2/quota.rst
Normal file
@@ -0,0 +1,12 @@
|
||||
openstack.dns.v2.quota
|
||||
======================
|
||||
|
||||
.. automodule:: openstack.dns.v2.quota
|
||||
|
||||
The Quota Class
|
||||
---------------
|
||||
|
||||
The ``Quota`` class inherits from :class:`~openstack.resource.Resource`.
|
||||
|
||||
.. autoclass:: openstack.dns.v2.quota.Quota
|
||||
:members:
|
@@ -15,6 +15,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 quota as _quota
|
||||
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
|
||||
@@ -34,6 +35,7 @@ class Proxy(proxy.Proxy):
|
||||
"blacklist": _blacklist.Blacklist,
|
||||
"floating_ip": _fip.FloatingIP,
|
||||
"limits": _limit.Limit,
|
||||
"quota": _quota.Quota,
|
||||
"recordset": _rs.Recordset,
|
||||
"service_status": _svc_status.ServiceStatus,
|
||||
"zone": _zone.Zone,
|
||||
@@ -699,6 +701,60 @@ class Proxy(proxy.Proxy):
|
||||
"""
|
||||
return self._list(_limit.Limit, **query)
|
||||
|
||||
# ======== Quotas ========
|
||||
def quotas(self, **query):
|
||||
"""Return a generator of quotas
|
||||
|
||||
:param dict query: Optional query parameters to be sent to limit the
|
||||
resources being returned.
|
||||
|
||||
:returns: A generator of quota objects
|
||||
:rtype: :class:`~openstack.dns.v2.quota.Quota`
|
||||
"""
|
||||
return self._list(_quota.Quota, **query)
|
||||
|
||||
def get_quota(self, quota):
|
||||
"""Get a quota
|
||||
|
||||
:param quota: The value can be the ID of a quota or a
|
||||
:class:`~openstack.dns.v2.quota.Quota` instance.
|
||||
The ID of a quota is the same as the project ID for the quota.
|
||||
|
||||
:returns: One :class:`~openstack.dns.v2.quota.Quota`
|
||||
:raises: :class:`~openstack.exceptions.ResourceNotFound`
|
||||
"""
|
||||
return self._get(_quota.Quota, quota)
|
||||
|
||||
def update_quota(self, quota, **attrs):
|
||||
"""Update a quota
|
||||
|
||||
:param quota: Either the ID of a quota or a
|
||||
:class:`~openstack.dns.v2.quota.Quota` instance. The ID of a quota
|
||||
is the same as the project ID for the quota.
|
||||
:param dict attrs: The attributes to update on the quota represented
|
||||
by ``quota``.
|
||||
|
||||
:returns: The updated quota
|
||||
:rtype: :class:`~openstack.dns.v2.quota.Quota`
|
||||
"""
|
||||
return self._update(_quota.Quota, quota, **attrs)
|
||||
|
||||
def delete_quota(self, quota, ignore_missing=True):
|
||||
"""Delete a quota (i.e. reset to the default quota)
|
||||
|
||||
:param quota: The value can be the ID of a quota or a
|
||||
:class:`~openstack.dns.v2.quota.Quota` instance.
|
||||
The ID of a quota is the same as the project ID for the quota.
|
||||
:param bool ignore_missing: When set to ``False``,
|
||||
:class:`~openstack.exceptions.ResourceNotFound` will be raised when
|
||||
the quota does not exist.
|
||||
When set to ``True``, no exception will be set when attempting to
|
||||
delete a nonexistent quota.
|
||||
|
||||
:returns: ``None``
|
||||
"""
|
||||
return self._delete(_quota.Quota, quota, ignore_missing=ignore_missing)
|
||||
|
||||
# ======== Service Statuses ========
|
||||
def service_statuses(self):
|
||||
"""Retrieve a generator of service statuses
|
||||
|
106
openstack/dns/v2/quota.py
Normal file
106
openstack/dns/v2/quota.py
Normal file
@@ -0,0 +1,106 @@
|
||||
# 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 typing as ty
|
||||
|
||||
from keystoneauth1 import adapter
|
||||
import typing_extensions as ty_ext
|
||||
|
||||
from openstack.dns.v2 import _base
|
||||
from openstack import resource
|
||||
|
||||
|
||||
class Quota(_base.Resource):
|
||||
"""DNS Quota Resource"""
|
||||
|
||||
base_path = "/quotas"
|
||||
|
||||
# capabilities
|
||||
allow_fetch = True
|
||||
allow_commit = True
|
||||
allow_delete = True
|
||||
allow_list = True
|
||||
commit_method = "PATCH"
|
||||
|
||||
# Properties
|
||||
#: The ID of the project.
|
||||
project = resource.URI("project", alternate_id=True)
|
||||
#: The maximum amount of recordsets allowed in a zone export. *Type: int*
|
||||
api_export_size = resource.Body("api_export_size", type=int)
|
||||
#: The maximum amount of records allowed per recordset. *Type: int*
|
||||
recordset_records = resource.Body("recordset_records", type=int)
|
||||
#: The maximum amount of records allowed per zone. *Type: int*
|
||||
zone_records = resource.Body("zone_records", type=int)
|
||||
#: The maximum amount of recordsets allowed per zone. *Type: int*
|
||||
zone_recordsets = resource.Body("zone_recordsets", type=int)
|
||||
#: The maximum amount of zones allowed per project. *Type: int*
|
||||
zones = resource.Body("zones", type=int)
|
||||
|
||||
def _prepare_request(
|
||||
self,
|
||||
requires_id=True,
|
||||
prepend_key=False,
|
||||
patch=False,
|
||||
base_path=None,
|
||||
params=None,
|
||||
*,
|
||||
resource_request_key=None,
|
||||
**kwargs,
|
||||
):
|
||||
_request = super()._prepare_request(
|
||||
requires_id, prepend_key, base_path=base_path
|
||||
)
|
||||
if self.resource_key in _request.body:
|
||||
_body = _request.body[self.resource_key]
|
||||
else:
|
||||
_body = _request.body
|
||||
if "id" in _body:
|
||||
del _body["id"]
|
||||
_request.headers = {'x-auth-sudo-project-id': self.id}
|
||||
return _request
|
||||
|
||||
def fetch(
|
||||
self,
|
||||
session: adapter.Adapter,
|
||||
requires_id: bool = True,
|
||||
base_path: str | None = None,
|
||||
error_message: str | None = None,
|
||||
skip_cache: bool = False,
|
||||
*,
|
||||
resource_response_key: str | None = None,
|
||||
microversion: str | None = None,
|
||||
**params: ty.Any,
|
||||
) -> ty_ext.Self:
|
||||
request = self._prepare_request(
|
||||
requires_id=requires_id,
|
||||
base_path=base_path,
|
||||
)
|
||||
session = self._get_session(session)
|
||||
if microversion is None:
|
||||
microversion = self._get_microversion(session)
|
||||
self.microversion = microversion
|
||||
|
||||
response = session.get(
|
||||
request.url,
|
||||
microversion=microversion,
|
||||
params=params,
|
||||
skip_cache=skip_cache,
|
||||
headers=request.headers,
|
||||
)
|
||||
|
||||
self._translate_response(
|
||||
response,
|
||||
error_message=error_message,
|
||||
resource_response_key=resource_response_key,
|
||||
)
|
||||
|
||||
return self
|
71
openstack/tests/functional/dns/v2/test_quota.py
Normal file
71
openstack/tests/functional/dns/v2/test_quota.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# 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.tests.functional import base
|
||||
|
||||
|
||||
class TestQuota(base.BaseFunctionalTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.require_service("dns")
|
||||
if not self._operator_cloud_name:
|
||||
self.skip("Operator cloud must be set for this test")
|
||||
|
||||
self.project = self.create_temporary_project()
|
||||
|
||||
def test_quota(self):
|
||||
# set quota
|
||||
|
||||
attrs = {
|
||||
"api_export_size": 1,
|
||||
"recordset_records": 2,
|
||||
"zone_records": 3,
|
||||
"zone_recordsets": 4,
|
||||
"zones": 5,
|
||||
}
|
||||
new_quota = self.operator_cloud.dns.update_quota(
|
||||
self.project.id, **attrs
|
||||
)
|
||||
self.assertEqual(attrs["api_export_size"], new_quota.api_export_size)
|
||||
self.assertEqual(
|
||||
attrs["recordset_records"], new_quota.recordset_records
|
||||
)
|
||||
self.assertEqual(attrs["zone_records"], new_quota.zone_records)
|
||||
self.assertEqual(attrs["zone_recordsets"], new_quota.zone_recordsets)
|
||||
self.assertEqual(attrs["zones"], new_quota.zones)
|
||||
|
||||
# get quota
|
||||
|
||||
expected_keys = [
|
||||
"id",
|
||||
"api_export_size",
|
||||
"recordset_records",
|
||||
"zone_records",
|
||||
"zone_recordsets",
|
||||
"zones",
|
||||
]
|
||||
test_quota = self.operator_cloud.dns.get_quota(self.project.id)
|
||||
for actual_key in test_quota._body.attributes.keys():
|
||||
self.assertIn(actual_key, expected_keys)
|
||||
self.assertEqual(self.project.id, test_quota.id)
|
||||
self.assertEqual(attrs["api_export_size"], test_quota.api_export_size)
|
||||
self.assertEqual(
|
||||
attrs["recordset_records"], test_quota.recordset_records
|
||||
)
|
||||
self.assertEqual(attrs["zone_records"], test_quota.zone_records)
|
||||
self.assertEqual(attrs["zone_recordsets"], test_quota.zone_recordsets)
|
||||
self.assertEqual(attrs["zones"], test_quota.zones)
|
||||
|
||||
# reset quota
|
||||
|
||||
self.operator_cloud.dns.delete_quota(self.project.id)
|
@@ -13,6 +13,7 @@
|
||||
from openstack.dns.v2 import _proxy
|
||||
from openstack.dns.v2 import blacklist
|
||||
from openstack.dns.v2 import floating_ip
|
||||
from openstack.dns.v2 import quota
|
||||
from openstack.dns.v2 import recordset
|
||||
from openstack.dns.v2 import service_status
|
||||
from openstack.dns.v2 import tld
|
||||
@@ -424,3 +425,30 @@ class TestDnsTLD(TestDnsProxy):
|
||||
|
||||
def test_tld_update(self):
|
||||
self.verify_update(self.proxy.update_tld, tld.TLD)
|
||||
|
||||
|
||||
class TestDnsQuota(TestDnsProxy):
|
||||
def test_quotas(self):
|
||||
self.verify_list(self.proxy.quotas, quota.Quota)
|
||||
|
||||
def test_quota_get(self):
|
||||
self.verify_get(self.proxy.get_quota, quota.Quota)
|
||||
|
||||
def test_quota_update(self):
|
||||
self.verify_update(self.proxy.update_quota, quota.Quota)
|
||||
|
||||
def test_quota_delete(self):
|
||||
self.verify_delete(
|
||||
self.proxy.delete_quota,
|
||||
quota.Quota,
|
||||
False,
|
||||
expected_kwargs={'ignore_missing': False},
|
||||
)
|
||||
|
||||
def test_quota_delete_ignore(self):
|
||||
self.verify_delete(
|
||||
self.proxy.delete_quota,
|
||||
quota.Quota,
|
||||
True,
|
||||
expected_kwargs={'ignore_missing': True},
|
||||
)
|
||||
|
51
openstack/tests/unit/dns/v2/test_quota.py
Normal file
51
openstack/tests/unit/dns/v2/test_quota.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# 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 quota
|
||||
from openstack.tests.unit import base
|
||||
|
||||
IDENTIFIER = "IDENTIFIER"
|
||||
EXAMPLE = {
|
||||
"zones": 10,
|
||||
"zone_recordsets": 500,
|
||||
"zone_records": 500,
|
||||
"recordset_records": 20,
|
||||
"api_export_size": 1000,
|
||||
}
|
||||
|
||||
|
||||
class TestQuota(base.TestCase):
|
||||
def test_basic(self):
|
||||
sot = quota.Quota()
|
||||
self.assertIsNone(sot.resources_key)
|
||||
self.assertIsNone(sot.resource_key)
|
||||
self.assertEqual("/quotas", sot.base_path)
|
||||
self.assertTrue(sot.allow_fetch)
|
||||
self.assertTrue(sot.allow_commit)
|
||||
self.assertTrue(sot.allow_delete)
|
||||
self.assertTrue(sot.allow_list)
|
||||
self.assertTrue(sot.commit_method, "PATCH")
|
||||
|
||||
def test_make_it(self):
|
||||
sot = quota.Quota(project='FAKE_PROJECT', **EXAMPLE)
|
||||
self.assertEqual(EXAMPLE['zones'], sot.zones)
|
||||
self.assertEqual(EXAMPLE['zone_recordsets'], sot.zone_recordsets)
|
||||
self.assertEqual(EXAMPLE['zone_records'], sot.zone_records)
|
||||
self.assertEqual(EXAMPLE['recordset_records'], sot.recordset_records)
|
||||
self.assertEqual(EXAMPLE['api_export_size'], sot.api_export_size)
|
||||
self.assertEqual('FAKE_PROJECT', sot.project)
|
||||
|
||||
def test_prepare_request(self):
|
||||
body = {'id': 'ABCDEFGH', 'zones': 20}
|
||||
quota_obj = quota.Quota(**body)
|
||||
response = quota_obj._prepare_request()
|
||||
self.assertNotIn('id', response)
|
4
releasenotes/notes/add-dns-quota-49ae659a88eeeab9.yaml
Normal file
4
releasenotes/notes/add-dns-quota-49ae659a88eeeab9.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add quota support for designate(DNS) API.
|
Reference in New Issue
Block a user