Add functional testing for the v2 API quotas endpoint
This is disabled by default, I7a0b828824ad6f274d922748f5f9a68157cd939a will enable it. Change-Id: I06180a7402fc45940d4b312666cf2dfd33af1305
This commit is contained in:
parent
a4601046f8
commit
c811498e62
@ -27,7 +27,7 @@ from designate_tempest_plugin.services.dns.v2.json.zone_imports_client import \
|
||||
ZoneImportsClient
|
||||
from designate_tempest_plugin.services.dns.v2.json.blacklists_client import \
|
||||
BlacklistsClient
|
||||
from designate_tempest_plugin.services.dns.admin.json.quotas_client import \
|
||||
from designate_tempest_plugin.services.dns.v2.json.quotas_client import \
|
||||
QuotasClient
|
||||
from designate_tempest_plugin.services.dns.v2.json.zone_exports_client import \
|
||||
ZoneExportsClient
|
||||
@ -37,6 +37,8 @@ from designate_tempest_plugin.services.dns.v2.json.pool_client import \
|
||||
PoolClient
|
||||
from designate_tempest_plugin.services.dns.v2.json.tld_client import \
|
||||
TldClient
|
||||
from designate_tempest_plugin.services.dns.admin.json.quotas_client import \
|
||||
QuotasClient as AdminQuotaClient
|
||||
from designate_tempest_plugin.services.dns.query.query_client import \
|
||||
QueryClient
|
||||
from designate_tempest_plugin.services.dns.v2.json.transfer_request_client \
|
||||
@ -112,6 +114,28 @@ class ManagerV2(clients.Manager):
|
||||
return params
|
||||
|
||||
|
||||
class ManagerAdmin(clients.Manager):
|
||||
|
||||
def __init__(self, credentials=None, service=None):
|
||||
super(ManagerAdmin, self).__init__(credentials, service)
|
||||
self._init_clients(self._get_params())
|
||||
|
||||
def _init_clients(self, params):
|
||||
self.quotas_client = AdminQuotaClient(**params)
|
||||
|
||||
def _get_params(self):
|
||||
params = dict(self.default_params)
|
||||
params.update({
|
||||
'auth_provider': self.auth_provider,
|
||||
'service': CONF.dns.catalog_type,
|
||||
'region': CONF.identity.region,
|
||||
'endpoint_type': CONF.dns.endpoint_type,
|
||||
'build_interval': CONF.dns.build_interval,
|
||||
'build_timeout': CONF.dns.build_timeout
|
||||
})
|
||||
return params
|
||||
|
||||
|
||||
class ManagerV2Unauthed(ManagerV2):
|
||||
|
||||
def __init__(self, credentials=None, service=None):
|
||||
|
@ -71,4 +71,11 @@ DnsFeatureGroup = [
|
||||
cfg.BoolOpt('api_v2_root_recordsets',
|
||||
default=False,
|
||||
help="Is the v2 root recordsets API enabled."),
|
||||
cfg.BoolOpt('api_v2_quotas',
|
||||
default=False,
|
||||
help="Is the v2 quota API enabled."),
|
||||
cfg.BoolOpt('bug_1573141_fixed',
|
||||
default=False,
|
||||
help="Is https://bugs.launchpad.net/designate/+bug/1573141 "
|
||||
"fixed"),
|
||||
]
|
||||
|
@ -81,24 +81,26 @@ def rand_zonefile_data(name=None, ttl=None):
|
||||
|
||||
def rand_quotas(zones=None, zone_records=None, zone_recordsets=None,
|
||||
recordset_records=None, api_export_size=None):
|
||||
LOG.warn("Leaving `api_export_size` out of quota data due to: "
|
||||
"https://bugs.launchpad.net/designate/+bug/1573141")
|
||||
return {
|
||||
'quota': {
|
||||
'zones':
|
||||
zones or data_utils.rand_int_id(100, 999999),
|
||||
'zone_records':
|
||||
zone_records or data_utils.rand_int_id(100, 999999),
|
||||
'zone_recordsets':
|
||||
zone_recordsets or data_utils.rand_int_id(100, 999999),
|
||||
'recordset_records':
|
||||
recordset_records or data_utils.rand_int_id(100, 999999),
|
||||
# https://bugs.launchpad.net/designate/+bug/1573141
|
||||
# 'api_export_size':
|
||||
# api_export_size or data_utils.rand_int_id(100, 999999),
|
||||
}
|
||||
quotas_dict = {
|
||||
'zones':
|
||||
zones or data_utils.rand_int_id(100, 999999),
|
||||
'zone_records':
|
||||
zone_records or data_utils.rand_int_id(100, 999999),
|
||||
'zone_recordsets':
|
||||
zone_recordsets or data_utils.rand_int_id(100, 999999),
|
||||
'recordset_records':
|
||||
recordset_records or data_utils.rand_int_id(100, 999999),
|
||||
}
|
||||
|
||||
if CONF.dns_feature_enabled.bug_1573141_fixed:
|
||||
quotas_dict['api_export_size'] = \
|
||||
api_export_size or data_utils.rand_int_id(100, 999999)
|
||||
else:
|
||||
LOG.warn("Leaving `api_export_size` out of quota data due to: "
|
||||
"https://bugs.launchpad.net/designate/+bug/1573141")
|
||||
|
||||
return quotas_dict
|
||||
|
||||
|
||||
def rand_zone_data(name=None, email=None, ttl=None, description=None):
|
||||
"""Generate random zone data, with optional overrides
|
||||
|
@ -53,7 +53,9 @@ class QuotasClient(base.DnsClientAdminBase):
|
||||
api_export_size=api_export_size,
|
||||
)
|
||||
|
||||
resp, body = self._update_request('quotas', project_id, quotas,
|
||||
quotas_dict = {'quota': quotas}
|
||||
|
||||
resp, body = self._update_request('quotas', project_id, quotas_dict,
|
||||
params=params)
|
||||
|
||||
self.expected_success(200, resp.status)
|
||||
|
@ -129,17 +129,24 @@ class DnsClientBase(rest_client.RestClient):
|
||||
|
||||
return resp, self.deserialize(resp, body)
|
||||
|
||||
def _show_request(self, resource, uuid, headers=None, params=None):
|
||||
def _show_request(self, resource, uuid, headers=None, params=None,
|
||||
extra_headers=False):
|
||||
"""Gets a specific object of the specified type.
|
||||
:param resource: The name of the REST resource, e.g., 'zones'.
|
||||
:param uuid: Unique identifier of the object in UUID format.
|
||||
:param params: A Python dict that represents the query paramaters to
|
||||
include in the request URI.
|
||||
:param extra_headers (bool): Boolean value than indicates if the
|
||||
headers returned by the get_headers()
|
||||
method are to be used but additional
|
||||
headers are needed in the request
|
||||
pass them in as a dict.
|
||||
:returns: Serialized object as a dictionary.
|
||||
"""
|
||||
uri = self.get_uri(resource, uuid=uuid, params=params)
|
||||
|
||||
resp, body = self.get(uri, headers=headers)
|
||||
resp, body = self.get(
|
||||
uri, headers=headers, extra_headers=extra_headers)
|
||||
|
||||
self.expected_success(self.SHOW_STATUS_CODES, resp.status)
|
||||
|
||||
@ -179,7 +186,8 @@ class DnsClientBase(rest_client.RestClient):
|
||||
|
||||
return resp, self.deserialize(resp, body)
|
||||
|
||||
def _update_request(self, resource, uuid, data, params=None):
|
||||
def _update_request(self, resource, uuid, data, params=None, headers=None,
|
||||
extra_headers=False):
|
||||
"""Updates the specified object using PATCH request.
|
||||
:param resource: The name of the REST resource, e.g., 'zones'
|
||||
:param uuid: Unique identifier of the object in UUID format.
|
||||
@ -188,28 +196,44 @@ class DnsClientBase(rest_client.RestClient):
|
||||
is sent as-is.
|
||||
:param params: A Python dict that represents the query paramaters to
|
||||
include in the request URI.
|
||||
:param headers (dict): The headers to use for the request.
|
||||
:param extra_headers (bool): Boolean value than indicates if the
|
||||
headers returned by the get_headers()
|
||||
method are to be used but additional
|
||||
headers are needed in the request
|
||||
pass them in as a dict.
|
||||
:returns: Serialized object as a dictionary.
|
||||
"""
|
||||
body = self.serialize(data)
|
||||
uri = self.get_uri(resource, uuid=uuid, params=params)
|
||||
|
||||
resp, body = self.patch(uri, body=body)
|
||||
resp, body = self.patch(uri, body=body,
|
||||
headers=headers, extra_headers=True)
|
||||
|
||||
self.expected_success(self.UPDATE_STATUS_CODES, resp.status)
|
||||
|
||||
return resp, self.deserialize(resp, body)
|
||||
|
||||
def _delete_request(self, resource, uuid, params=None):
|
||||
def _delete_request(self, resource, uuid, params=None, headers=None,
|
||||
extra_headers=False):
|
||||
"""Deletes the specified object.
|
||||
:param resource: The name of the REST resource, e.g., 'zones'.
|
||||
:param uuid: The unique identifier of an object in UUID format.
|
||||
:param params: A Python dict that represents the query paramaters to
|
||||
include in the request URI.
|
||||
:param headers (dict): The headers to use for the request.
|
||||
:param extra_headers (bool): Boolean value than indicates if the
|
||||
headers returned by the get_headers()
|
||||
method are to be used but additional
|
||||
headers are needed in the request
|
||||
pass them in as a dict.
|
||||
:returns: A tuple with the server response and the response body.
|
||||
"""
|
||||
uri = self.get_uri(resource, uuid=uuid, params=params)
|
||||
|
||||
resp, body = self.delete(uri)
|
||||
resp, body = self.delete(
|
||||
uri, headers=headers, extra_headers=extra_headers)
|
||||
|
||||
self.expected_success(self.DELETE_STATUS_CODES, resp.status)
|
||||
if resp.status == 202:
|
||||
body = self.deserialize(resp, body)
|
||||
|
101
designate_tempest_plugin/services/dns/v2/json/quotas_client.py
Normal file
101
designate_tempest_plugin/services/dns/v2/json/quotas_client.py
Normal file
@ -0,0 +1,101 @@
|
||||
# Copyright 2016 Rackspace
|
||||
#
|
||||
# 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 oslo_log import log as logging
|
||||
|
||||
from designate_tempest_plugin import data_utils as dns_data_utils
|
||||
from designate_tempest_plugin.services.dns.v2.json import base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class QuotasClient(base.DnsClientV2Base):
|
||||
|
||||
@base.handle_errors
|
||||
def update_quotas(self, zones=None, zone_records=None,
|
||||
zone_recordsets=None, recordset_records=None,
|
||||
api_export_size=None, project_id=None, params=None,
|
||||
headers=None):
|
||||
"""Update the quotas for the project id
|
||||
|
||||
:param zones: The limit on zones per tenant
|
||||
Default: Random Value
|
||||
:param zone_records: The limit on records per zone
|
||||
Default: Random Value
|
||||
:param zone_recordsets: The limit recordsets per zone
|
||||
Default: Random Value
|
||||
:param recordset_records: The limit on records per recordset
|
||||
Default: Random Value
|
||||
:param api_export_size: The limit on size of on exported zone
|
||||
Default: Random Value
|
||||
:param project_id: Apply the quotas to this project id
|
||||
Default: The project id of this client
|
||||
:param params: A Python dict that represents the query paramaters to
|
||||
include in the request URI.
|
||||
:param headers (dict): The headers to use for the request.
|
||||
:return: A tuple with the server response and the created quota.
|
||||
"""
|
||||
project_id = project_id or self.tenant_id
|
||||
|
||||
quotas = dns_data_utils.rand_quotas(
|
||||
zones=zones,
|
||||
zone_records=zone_records,
|
||||
zone_recordsets=zone_recordsets,
|
||||
recordset_records=recordset_records,
|
||||
api_export_size=api_export_size,
|
||||
)
|
||||
|
||||
resp, body = self._update_request('quotas', project_id, quotas,
|
||||
params=params, headers=headers,
|
||||
extra_headers=True)
|
||||
|
||||
self.expected_success(200, resp.status)
|
||||
|
||||
return resp, body
|
||||
|
||||
@base.handle_errors
|
||||
def show_quotas(self, project_id=None, params=None, headers=None):
|
||||
"""Gets a specific quota.
|
||||
|
||||
:param project_id: Show the quotas of this project id
|
||||
Default: The project id of this client
|
||||
:param params: A Python dict that represents the query paramaters to
|
||||
include in the request URI.
|
||||
:param headers (dict): The headers to use for the request.
|
||||
:return: Serialized quota as a dictionary.
|
||||
"""
|
||||
project_id = project_id or self.tenant_id
|
||||
return self._show_request('quotas', project_id, params=params,
|
||||
headers=headers, extra_headers=True)
|
||||
|
||||
@base.handle_errors
|
||||
def delete_quotas(self, project_id=None, params=None, headers=None):
|
||||
"""Resets the quotas for the specified project id
|
||||
|
||||
:param project_id: Reset the quotas of this project id
|
||||
Default: The project id of this client
|
||||
:param params: A Python dict that represents the query paramaters to
|
||||
include in the request URI.
|
||||
:param headers (dict): The headers to use for the request.
|
||||
:return: A tuple with the server response and the response body.
|
||||
"""
|
||||
project_id = project_id or self.tenant_id
|
||||
|
||||
resp, body = self._delete_request(
|
||||
'quotas', project_id,
|
||||
params=params, headers=headers,
|
||||
extra_headers=True)
|
||||
|
||||
self.expected_success(204, resp.status)
|
||||
|
||||
return resp, body
|
@ -12,17 +12,24 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from oslo_log import log as logging
|
||||
from tempest import config
|
||||
from tempest.lib import decorators
|
||||
|
||||
from designate_tempest_plugin.tests import base
|
||||
from designate_tempest_plugin import data_utils as dns_data_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class BaseQuotasTest(base.BaseDnsAdminTest):
|
||||
# see: https://bugs.launchpad.net/designate/+bug/1573141
|
||||
excluded_keys = ['api_expected_size']
|
||||
|
||||
excluded_keys = []
|
||||
|
||||
def setUp(self):
|
||||
if CONF.dns_feature_enabled.bug_1573141_fixed:
|
||||
self.excluded_keys = ['api_export_size']
|
||||
super(BaseQuotasTest, self).setUp()
|
||||
|
||||
|
||||
class QuotasAdminTest(BaseQuotasTest):
|
||||
@ -39,14 +46,14 @@ class QuotasAdminTest(BaseQuotasTest):
|
||||
def test_show_quotas(self):
|
||||
LOG.info("Updating quotas")
|
||||
quotas = dns_data_utils.rand_quotas()
|
||||
_, body = self.admin_client.update_quotas(**quotas['quota'])
|
||||
_, body = self.admin_client.update_quotas(**quotas)
|
||||
self.addCleanup(self.admin_client.delete_quotas)
|
||||
|
||||
LOG.info("Fetching quotas")
|
||||
_, body = self.admin_client.show_quotas()
|
||||
|
||||
LOG.info("Ensuring the response has all quota types")
|
||||
self.assertExpected(quotas['quota'], body['quota'], self.excluded_keys)
|
||||
self.assertExpected(quotas, body['quota'], self.excluded_keys)
|
||||
|
||||
@decorators.idempotent_id('33e0affb-5d66-4216-881c-f101a779851a')
|
||||
def test_delete_quotas(self):
|
||||
@ -60,8 +67,8 @@ class QuotasAdminTest(BaseQuotasTest):
|
||||
def test_update_quotas(self):
|
||||
LOG.info("Updating quotas")
|
||||
quotas = dns_data_utils.rand_quotas()
|
||||
_, body = self.admin_client.update_quotas(**quotas['quota'])
|
||||
_, body = self.admin_client.update_quotas(**quotas)
|
||||
self.addCleanup(self.admin_client.delete_quotas)
|
||||
|
||||
LOG.info("Ensuring the response has all quota types")
|
||||
self.assertExpected(quotas['quota'], body['quota'], self.excluded_keys)
|
||||
self.assertExpected(quotas, body['quota'], self.excluded_keys)
|
||||
|
124
designate_tempest_plugin/tests/api/v2/test_quotas.py
Normal file
124
designate_tempest_plugin/tests/api/v2/test_quotas.py
Normal file
@ -0,0 +1,124 @@
|
||||
# Copyright 2016 Rackspace
|
||||
#
|
||||
# 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 oslo_log import log as logging
|
||||
from tempest import config
|
||||
from tempest.lib import decorators
|
||||
|
||||
from designate_tempest_plugin.tests import base
|
||||
from designate_tempest_plugin import data_utils as dns_data_utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class QuotasV2Test(base.BaseDnsV2Test):
|
||||
|
||||
credentials = ['primary', 'admin']
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(QuotasV2Test, cls).skip_checks()
|
||||
|
||||
if not CONF.dns_feature_enabled.api_v2_quotas:
|
||||
skip_msg = ("%s skipped as designate V2 Quotas API is not "
|
||||
"available" % cls.__name__)
|
||||
raise cls.skipException(skip_msg)
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(QuotasV2Test, cls).setup_clients()
|
||||
|
||||
cls.quotas_client = cls.os.quotas_client
|
||||
cls.admin_client = cls.os_adm.quotas_client
|
||||
|
||||
@decorators.idempotent_id('1dac991a-9e2e-452c-a47a-26ac37381ec5')
|
||||
def test_show_quotas(self):
|
||||
LOG.info("Updating quotas")
|
||||
quotas = dns_data_utils.rand_quotas()
|
||||
_, body = self.admin_client.update_quotas(**quotas)
|
||||
self.addCleanup(self.admin_client.delete_quotas)
|
||||
|
||||
LOG.info("Fetching quotas")
|
||||
_, body = self.admin_client.show_quotas()
|
||||
|
||||
LOG.info("Ensuring the response has all quota types")
|
||||
self.assertExpected(quotas, body, [])
|
||||
|
||||
@decorators.idempotent_id('0448b089-5803-4ce3-8a6c-5c15ff75a2cc')
|
||||
def test_delete_quotas(self):
|
||||
LOG.info("Deleting quotas")
|
||||
_, body = self.admin_client.delete_quotas()
|
||||
|
||||
LOG.info("Ensuring an empty response body")
|
||||
self.assertEqual(body.strip(), "")
|
||||
|
||||
@decorators.idempotent_id('76d24c87-1b39-4e19-947c-c08e1380dc61')
|
||||
def test_update_quotas(self):
|
||||
LOG.info("Updating quotas")
|
||||
quotas = dns_data_utils.rand_quotas()
|
||||
_, body = self.admin_client.update_quotas(**quotas)
|
||||
self.addCleanup(self.admin_client.delete_quotas)
|
||||
|
||||
LOG.info("Ensuring the response has all quota types")
|
||||
self.assertExpected(quotas, body, [])
|
||||
|
||||
@decorators.idempotent_id('76d24c87-1b39-4e19-947c-c08e1380dc61')
|
||||
def test_update_quotas_other_project(self):
|
||||
|
||||
project_id = self.quotas_client.tenant_id
|
||||
|
||||
LOG.info("Updating quotas for %s ", project_id)
|
||||
|
||||
quotas = dns_data_utils.rand_quotas()
|
||||
request = quotas.copy()
|
||||
request['project_id'] = project_id
|
||||
request['headers'] = {'X-Auth-All-Projects': True}
|
||||
_, body = self.admin_client.update_quotas(**request)
|
||||
self.addCleanup(self.admin_client.delete_quotas, project_id=project_id)
|
||||
|
||||
LOG.info("Ensuring the response has all quota types")
|
||||
self.assertExpected(quotas, body, [])
|
||||
|
||||
_, client_body = self.quotas_client.show_quotas()
|
||||
|
||||
self.assertExpected(quotas, client_body, [])
|
||||
|
||||
@decorators.idempotent_id('21e45d30-dbc1-4173-9d6b-9b6813ef514b')
|
||||
def test_reset_quotas_other_project(self):
|
||||
|
||||
# Use a fake project for this
|
||||
project_id = '21e45d30-dbc1-4173-9d6b-9b6813ef514b'
|
||||
|
||||
_, original_quotas = self.admin_client.show_quotas(
|
||||
project_id=project_id, headers={'X-Auth-All-Projects': True})
|
||||
|
||||
LOG.info("Updating quotas for %s ", project_id)
|
||||
|
||||
quotas = dns_data_utils.rand_quotas()
|
||||
request = quotas.copy()
|
||||
request['project_id'] = project_id
|
||||
request['headers'] = {'X-Auth-All-Projects': True}
|
||||
_, body = self.admin_client.update_quotas(**request)
|
||||
self.addCleanup(self.admin_client.delete_quotas, project_id=project_id)
|
||||
|
||||
self.admin_client.delete_quotas(
|
||||
project_id=project_id,
|
||||
headers={'X-Auth-All-Projects': True})
|
||||
|
||||
_, final_quotas = self.admin_client.show_quotas(
|
||||
project_id=project_id, headers={'X-Auth-All-Projects': True})
|
||||
|
||||
self.assertExpected(original_quotas, final_quotas, [])
|
@ -85,8 +85,8 @@ class BaseDnsV2Test(BaseDnsTest):
|
||||
class BaseDnsAdminTest(BaseDnsTest):
|
||||
"""Base class for DNS Admin API tests."""
|
||||
|
||||
# Use the Designate V2 Client Manager
|
||||
client_manager = clients.ManagerV2
|
||||
# Use the Designate Admin Client Manager
|
||||
client_manager = clients.ManagerAdmin
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
|
Loading…
Reference in New Issue
Block a user