Add test for compute API os-quota-class-sets
This tests showing and updating the 'default' quota class, which is really all this API is used for. It was removed in Icehouse in part to not knowing it was used even though python-novaclient and Horizon were using it. The removal was reverted and this change is to enforce what the API is used for. The extension only lives in the Nova v2 API, it was removed in the v3 API in Icehouse. This change adds new quota client and test classes for the new API. An external lock fixture is used in all compute API quota tests to avoid introducing race bugs with the existing tenant-specific os-quota-sets tests. Closes-Bug: #1325727 Change-Id: Ib0cde08dfaa0f6a5e180d247864fb59d76eca903
This commit is contained in:
parent
ffe30e0729
commit
848805f21b
@ -13,14 +13,26 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import six
|
||||
from testtools import matchers
|
||||
|
||||
from tempest.api.compute import base
|
||||
from tempest.common import tempest_fixtures as fixtures
|
||||
from tempest.common.utils import data_utils
|
||||
from tempest.openstack.common import log as logging
|
||||
from tempest import test
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class QuotasAdminTestJSON(base.BaseV2ComputeAdminTest):
|
||||
force_tenant_isolation = True
|
||||
|
||||
def setUp(self):
|
||||
# NOTE(mriedem): Avoid conflicts with os-quota-class-sets tests.
|
||||
self.useFixture(fixtures.LockFixture('compute_quotas'))
|
||||
super(QuotasAdminTestJSON, self).setUp()
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(QuotasAdminTestJSON, cls).setUpClass()
|
||||
@ -134,3 +146,49 @@ class QuotasAdminTestJSON(base.BaseV2ComputeAdminTest):
|
||||
|
||||
class QuotasAdminTestXML(QuotasAdminTestJSON):
|
||||
_interface = 'xml'
|
||||
|
||||
|
||||
class QuotaClassesAdminTestJSON(base.BaseV2ComputeAdminTest):
|
||||
"""Tests the os-quota-class-sets API to update default quotas.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
# All test cases in this class need to externally lock on doing
|
||||
# anything with default quota values.
|
||||
self.useFixture(fixtures.LockFixture('compute_quotas'))
|
||||
super(QuotaClassesAdminTestJSON, self).setUp()
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(QuotaClassesAdminTestJSON, cls).setUpClass()
|
||||
cls.adm_client = cls.os_adm.quota_classes_client
|
||||
|
||||
def _restore_default_quotas(self, original_defaults):
|
||||
LOG.debug("restoring quota class defaults")
|
||||
resp, body = self.adm_client.update_quota_class_set(
|
||||
'default', **original_defaults)
|
||||
self.assertEqual(200, resp.status)
|
||||
|
||||
@test.attr(type='gate')
|
||||
def test_update_default_quotas(self):
|
||||
LOG.debug("get the current 'default' quota class values")
|
||||
resp, body = self.adm_client.get_quota_class_set('default')
|
||||
self.assertEqual(200, resp.status)
|
||||
self.assertIn('id', body)
|
||||
self.assertEqual('default', body.pop('id'))
|
||||
# restore the defaults when the test is done
|
||||
self.addCleanup(self._restore_default_quotas, body.copy())
|
||||
# increment all of the values for updating the default quota class
|
||||
for quota, default in six.iteritems(body):
|
||||
body[quota] = default + 1
|
||||
LOG.debug("update limits for the default quota class set")
|
||||
resp, update_body = self.adm_client.update_quota_class_set('default',
|
||||
**body)
|
||||
self.assertEqual(200, resp.status)
|
||||
LOG.debug("assert that the response has all of the changed values")
|
||||
self.assertThat(update_body.items(),
|
||||
matchers.ContainsAll(body.items()))
|
||||
|
||||
|
||||
class QuotaClassesAdminTestXML(QuotaClassesAdminTestJSON):
|
||||
_interface = 'xml'
|
||||
|
@ -67,6 +67,8 @@ class BaseComputeTest(tempest.test.BaseTestCase):
|
||||
cls.keypairs_client = cls.os.keypairs_client
|
||||
cls.security_groups_client = cls.os.security_groups_client
|
||||
cls.quotas_client = cls.os.quotas_client
|
||||
# NOTE(mriedem): os-quota-class-sets is v2 API only
|
||||
cls.quota_classes_client = cls.os.quota_classes_client
|
||||
cls.limits_client = cls.os.limits_client
|
||||
cls.volumes_extensions_client = cls.os.volumes_extensions_client
|
||||
cls.volumes_client = cls.os.volumes_client
|
||||
|
@ -14,11 +14,17 @@
|
||||
# under the License.
|
||||
|
||||
from tempest.api.compute import base
|
||||
from tempest.common import tempest_fixtures as fixtures
|
||||
from tempest import test
|
||||
|
||||
|
||||
class QuotasTestJSON(base.BaseV2ComputeTest):
|
||||
|
||||
def setUp(self):
|
||||
# NOTE(mriedem): Avoid conflicts with os-quota-class-sets tests.
|
||||
self.useFixture(fixtures.LockFixture('compute_quotas'))
|
||||
super(QuotasTestJSON, self).setUp()
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(QuotasTestJSON, cls).setUpClass()
|
||||
|
@ -14,11 +14,17 @@
|
||||
# under the License.
|
||||
|
||||
from tempest.api.compute import base
|
||||
from tempest.common import tempest_fixtures as fixtures
|
||||
from tempest import test
|
||||
|
||||
|
||||
class QuotasV3Test(base.BaseV3ComputeTest):
|
||||
|
||||
def setUp(self):
|
||||
# NOTE(mriedem): Avoid conflicts with os-quota-class-sets tests.
|
||||
self.useFixture(fixtures.LockFixture('compute_quotas'))
|
||||
super(QuotasV3Test, self).setUp()
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(QuotasV3Test, cls).setUpClass()
|
||||
|
31
tempest/api_schema/compute/v2/quota_classes.py
Normal file
31
tempest/api_schema/compute/v2/quota_classes.py
Normal file
@ -0,0 +1,31 @@
|
||||
# Copyright 2014 IBM Corporation.
|
||||
# All rights reserved.
|
||||
#
|
||||
# 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 copy
|
||||
|
||||
from tempest.api_schema.compute.v2 import quotas
|
||||
|
||||
# NOTE(mriedem): os-quota-class-sets responses are the same as os-quota-sets
|
||||
# except for the key in the response body is quota_class_set instead of
|
||||
# quota_set, so update this copy of the schema from os-quota-sets.
|
||||
quota_set = copy.deepcopy(quotas.quota_set)
|
||||
quota_set['response_body']['properties']['quota_class_set'] = (
|
||||
quota_set['response_body']['properties'].pop('quota_set'))
|
||||
quota_set['response_body']['required'] = ['quota_class_set']
|
||||
|
||||
quota_set_update = copy.deepcopy(quotas.quota_set_update)
|
||||
quota_set_update['response_body']['properties']['quota_class_set'] = (
|
||||
quota_set_update['response_body']['properties'].pop('quota_set'))
|
||||
quota_set_update['response_body']['required'] = ['quota_class_set']
|
@ -50,6 +50,7 @@ from tempest.services.compute.json.keypairs_client import KeyPairsClientJSON
|
||||
from tempest.services.compute.json.limits_client import LimitsClientJSON
|
||||
from tempest.services.compute.json.migrations_client import \
|
||||
MigrationsClientJSON
|
||||
from tempest.services.compute.json.quotas_client import QuotaClassesClientJSON
|
||||
from tempest.services.compute.json.quotas_client import QuotasClientJSON
|
||||
from tempest.services.compute.json.security_groups_client import \
|
||||
SecurityGroupsClientJSON
|
||||
@ -105,6 +106,7 @@ from tempest.services.compute.xml.interfaces_client import \
|
||||
InterfacesClientXML
|
||||
from tempest.services.compute.xml.keypairs_client import KeyPairsClientXML
|
||||
from tempest.services.compute.xml.limits_client import LimitsClientXML
|
||||
from tempest.services.compute.xml.quotas_client import QuotaClassesClientXML
|
||||
from tempest.services.compute.xml.quotas_client import QuotasClientXML
|
||||
from tempest.services.compute.xml.security_groups_client \
|
||||
import SecurityGroupsClientXML
|
||||
@ -220,6 +222,8 @@ class Manager(manager.Manager):
|
||||
self.images_client = ImagesClientXML(self.auth_provider)
|
||||
self.keypairs_client = KeyPairsClientXML(self.auth_provider)
|
||||
self.quotas_client = QuotasClientXML(self.auth_provider)
|
||||
self.quota_classes_client = QuotaClassesClientXML(
|
||||
self.auth_provider)
|
||||
self.flavors_client = FlavorsClientXML(self.auth_provider)
|
||||
self.extensions_client = ExtensionsClientXML(self.auth_provider)
|
||||
self.volumes_extensions_client = VolumesExtensionsClientXML(
|
||||
@ -288,6 +292,8 @@ class Manager(manager.Manager):
|
||||
self.keypairs_v3_client = KeyPairsV3ClientJSON(
|
||||
self.auth_provider)
|
||||
self.quotas_client = QuotasClientJSON(self.auth_provider)
|
||||
self.quota_classes_client = QuotaClassesClientJSON(
|
||||
self.auth_provider)
|
||||
self.quotas_v3_client = QuotasV3ClientJSON(self.auth_provider)
|
||||
self.flavors_client = FlavorsClientJSON(self.auth_provider)
|
||||
self.flavors_v3_client = FlavorsV3ClientJSON(self.auth_provider)
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
import json
|
||||
|
||||
from tempest.api_schema.compute.v2 import quota_classes as classes_schema
|
||||
from tempest.api_schema.compute.v2 import quotas as schema
|
||||
from tempest.common import rest_client
|
||||
from tempest import config
|
||||
@ -118,3 +119,32 @@ class QuotasClientJSON(rest_client.RestClient):
|
||||
resp, body = self.delete('os-quota-sets/%s' % str(tenant_id))
|
||||
self.validate_response(schema.delete_quota, resp, body)
|
||||
return resp, body
|
||||
|
||||
|
||||
class QuotaClassesClientJSON(rest_client.RestClient):
|
||||
|
||||
def __init__(self, auth_provider):
|
||||
super(QuotaClassesClientJSON, self).__init__(auth_provider)
|
||||
self.service = CONF.compute.catalog_type
|
||||
|
||||
def get_quota_class_set(self, quota_class_id):
|
||||
"""List the quota class set for a quota class."""
|
||||
|
||||
url = 'os-quota-class-sets/%s' % str(quota_class_id)
|
||||
resp, body = self.get(url)
|
||||
body = json.loads(body)
|
||||
self.validate_response(classes_schema.quota_set, resp, body)
|
||||
return resp, body['quota_class_set']
|
||||
|
||||
def update_quota_class_set(self, quota_class_id, **kwargs):
|
||||
"""
|
||||
Updates the quota class's limits for one or more resources.
|
||||
"""
|
||||
post_body = json.dumps({'quota_class_set': kwargs})
|
||||
|
||||
resp, body = self.put('os-quota-class-sets/%s' % str(quota_class_id),
|
||||
post_body)
|
||||
|
||||
body = json.loads(body)
|
||||
self.validate_response(classes_schema.quota_set_update, resp, body)
|
||||
return resp, body['quota_class_set']
|
||||
|
@ -22,14 +22,7 @@ from tempest import config
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class QuotasClientXML(rest_client.RestClient):
|
||||
TYPE = "xml"
|
||||
|
||||
def __init__(self, auth_provider):
|
||||
super(QuotasClientXML, self).__init__(auth_provider)
|
||||
self.service = CONF.compute.catalog_type
|
||||
|
||||
def _format_quota(self, q):
|
||||
def format_quota(q):
|
||||
quota = {}
|
||||
for k, v in q.items():
|
||||
try:
|
||||
@ -41,6 +34,14 @@ class QuotasClientXML(rest_client.RestClient):
|
||||
|
||||
return quota
|
||||
|
||||
|
||||
class QuotasClientXML(rest_client.RestClient):
|
||||
TYPE = "xml"
|
||||
|
||||
def __init__(self, auth_provider):
|
||||
super(QuotasClientXML, self).__init__(auth_provider)
|
||||
self.service = CONF.compute.catalog_type
|
||||
|
||||
def get_quota_set(self, tenant_id, user_id=None):
|
||||
"""List the quota set for a tenant."""
|
||||
|
||||
@ -49,7 +50,7 @@ class QuotasClientXML(rest_client.RestClient):
|
||||
url += '?user_id=%s' % str(user_id)
|
||||
resp, body = self.get(url)
|
||||
body = xml_utils.xml_to_json(etree.fromstring(body))
|
||||
body = self._format_quota(body)
|
||||
body = format_quota(body)
|
||||
return resp, body
|
||||
|
||||
def get_default_quota_set(self, tenant_id):
|
||||
@ -58,7 +59,7 @@ class QuotasClientXML(rest_client.RestClient):
|
||||
url = 'os-quota-sets/%s/defaults' % str(tenant_id)
|
||||
resp, body = self.get(url)
|
||||
body = xml_utils.xml_to_json(etree.fromstring(body))
|
||||
body = self._format_quota(body)
|
||||
body = format_quota(body)
|
||||
return resp, body
|
||||
|
||||
def update_quota_set(self, tenant_id, user_id=None,
|
||||
@ -124,9 +125,41 @@ class QuotasClientXML(rest_client.RestClient):
|
||||
str(xml_utils.Document(post_body)))
|
||||
|
||||
body = xml_utils.xml_to_json(etree.fromstring(body))
|
||||
body = self._format_quota(body)
|
||||
body = format_quota(body)
|
||||
return resp, body
|
||||
|
||||
def delete_quota_set(self, tenant_id):
|
||||
"""Delete the tenant's quota set."""
|
||||
return self.delete('os-quota-sets/%s' % str(tenant_id))
|
||||
|
||||
|
||||
class QuotaClassesClientXML(rest_client.RestClient):
|
||||
TYPE = "xml"
|
||||
|
||||
def __init__(self, auth_provider):
|
||||
super(QuotaClassesClientXML, self).__init__(auth_provider)
|
||||
self.service = CONF.compute.catalog_type
|
||||
|
||||
def get_quota_class_set(self, quota_class_id):
|
||||
"""List the quota class set for a quota class."""
|
||||
|
||||
url = 'os-quota-class-sets/%s' % str(quota_class_id)
|
||||
resp, body = self.get(url)
|
||||
body = xml_utils.xml_to_json(etree.fromstring(body))
|
||||
body = format_quota(body)
|
||||
return resp, body
|
||||
|
||||
def update_quota_class_set(self, quota_class_id, **kwargs):
|
||||
"""
|
||||
Updates the quota class's limits for one or more resources.
|
||||
"""
|
||||
post_body = xml_utils.Element("quota_class_set",
|
||||
xmlns=xml_utils.XMLNS_11,
|
||||
**kwargs)
|
||||
|
||||
resp, body = self.put('os-quota-class-sets/%s' % str(quota_class_id),
|
||||
str(xml_utils.Document(post_body)))
|
||||
|
||||
body = xml_utils.xml_to_json(etree.fromstring(body))
|
||||
body = format_quota(body)
|
||||
return resp, body
|
||||
|
Loading…
Reference in New Issue
Block a user