Add test cases for volume quota class
Now tempest has provided tests for tenant's quota in volume, but
lacks tests for volume quota class. This patch adds this support.
Including:
[1] Add volume v2 quota class client as library
[2] Add release note
[3] Add test cases: show & update volume 'default' quota class
[4] Add unit tests for volume v2 quota class client
[5] Fix for test_volume_quotas.py
Change-Id: I30bac79b986e6e90d43dcc6f9d247e74a314bf3c
diff --git a/releasenotes/notes/add-volume-quota-class-client-as-library-c4c2b22c36ff807e.yaml b/releasenotes/notes/add-volume-quota-class-client-as-library-c4c2b22c36ff807e.yaml
new file mode 100644
index 0000000..e6847eb
--- /dev/null
+++ b/releasenotes/notes/add-volume-quota-class-client-as-library-c4c2b22c36ff807e.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ Define v2 quota_classes_client for the volume service as library
+ interfaces, allowing other projects to use this module as stable libraries
+ without maintenance changes.
+
+ * quota_classes_client(v2)
diff --git a/tempest/api/volume/admin/test_volume_quota_classes.py b/tempest/api/volume/admin/test_volume_quota_classes.py
new file mode 100644
index 0000000..016d87a
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_quota_classes.py
@@ -0,0 +1,89 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# 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.
+
+from oslo_log import log as logging
+from testtools import matchers
+
+from tempest.api.volume import base
+from tempest.common import tempest_fixtures as fixtures
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+
+LOG = logging.getLogger(__name__)
+QUOTA_KEYS = ['gigabytes', 'snapshots', 'volumes', 'backups',
+ 'backup_gigabytes', 'per_volume_gigabytes']
+
+
+class VolumeQuotaClassesTest(base.BaseVolumeAdminTest):
+
+ def setUp(self):
+ # Note(jeremy.zhang): All test cases in this class need to externally
+ # lock on doing anything with default quota values.
+ self.useFixture(fixtures.LockFixture('volume_quotas'))
+ super(VolumeQuotaClassesTest, self).setUp()
+
+ def _restore_default_quotas(self, original_defaults):
+ LOG.debug("Restoring volume quota class defaults")
+ self.admin_quota_classes_client.update_quota_class_set(
+ 'default', **original_defaults)
+
+ @decorators.idempotent_id('abb9198e-67d0-4b09-859f-4f4a1418f176')
+ def test_show_default_quota(self):
+ default_quotas = self.admin_quota_classes_client.show_quota_class_set(
+ 'default')['quota_class_set']
+ self.assertIn('id', default_quotas)
+ self.assertEqual('default', default_quotas.pop('id'))
+ for key in QUOTA_KEYS:
+ self.assertIn(key, default_quotas)
+
+ @decorators.idempotent_id('a7644c63-2669-467a-b00e-452dd5c5397b')
+ def test_update_default_quota(self):
+ LOG.debug("Get the current default quota class values")
+ body = self.admin_quota_classes_client.show_quota_class_set(
+ 'default')['quota_class_set']
+ body.pop('id')
+
+ # Restore the defaults when the test is done
+ self.addCleanup(self._restore_default_quotas, body.copy())
+
+ # Increment some of the values for updating the default quota class.
+ # For safety, only items with value >= 0 will be updated, and items
+ # with value < 0 (-1 means unlimited) will be ignored.
+ for quota, default in body.items():
+ if default >= 0:
+ body[quota] = default + 1
+
+ LOG.debug("Update limits for the default quota class set")
+ update_body = self.admin_quota_classes_client.update_quota_class_set(
+ 'default', **body)['quota_class_set']
+ self.assertThat(update_body.items(),
+ matchers.ContainsAll(body.items()))
+
+ # Verify current project's default quotas
+ default_quotas = self.admin_quotas_client.show_default_quota_set(
+ self.os_adm.credentials.tenant_id)['quota_set']
+ self.assertThat(default_quotas.items(),
+ matchers.ContainsAll(body.items()))
+
+ # Verify a new project's default quotas
+ project_name = data_utils.rand_name('quota_class_tenant')
+ description = data_utils.rand_name('desc_')
+ project_id = self.identity_utils.create_project(
+ name=project_name, description=description)['id']
+ self.addCleanup(self.identity_utils.delete_project, project_id)
+ default_quotas = self.admin_quotas_client.show_default_quota_set(
+ project_id)['quota_set']
+ self.assertThat(default_quotas.items(),
+ matchers.ContainsAll(body.items()))
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index 58ca92f..48941c6 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -13,6 +13,7 @@
# under the License.
from tempest.api.volume import base
+from tempest.common import tempest_fixtures as fixtures
from tempest.common import waiters
from tempest.lib.common.utils import data_utils
from tempest.lib import decorators
@@ -26,6 +27,11 @@
credentials = ['primary', 'alt', 'admin']
+ def setUp(self):
+ # NOTE(jeremy.zhang): Avoid conflicts with volume quota class tests.
+ self.useFixture(fixtures.LockFixture('volume_quotas'))
+ super(BaseVolumeQuotasAdminTestJSON, self).setUp()
+
@classmethod
def setup_credentials(cls):
super(BaseVolumeQuotasAdminTestJSON, cls).setup_credentials()
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index a19af5d..0ba5c6f 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -255,6 +255,8 @@
cls.admin_backups_client = cls.os_adm.backups_v2_client
cls.admin_encryption_types_client = \
cls.os_adm.encryption_types_v2_client
+ cls.admin_quota_classes_client = \
+ cls.os_adm.volume_quota_classes_v2_client
cls.admin_quotas_client = cls.os_adm.volume_quotas_v2_client
cls.admin_volume_limits_client = cls.os_adm.volume_v2_limits_client
cls.admin_capabilities_client = \
diff --git a/tempest/clients.py b/tempest/clients.py
index d21e6a7..5ba888d 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -274,6 +274,8 @@
self.volume_hosts_v2_client = self.volume_v2.HostsClient()
self.volume_quotas_client = self.volume_v1.QuotasClient()
self.volume_quotas_v2_client = self.volume_v2.QuotasClient()
+ self.volume_quota_classes_v2_client = \
+ self.volume_v2.QuotaClassesClient()
self.volumes_extension_client = self.volume_v1.ExtensionsClient()
self.volumes_v2_extension_client = self.volume_v2.ExtensionsClient()
self.volume_availability_zone_client = \
diff --git a/tempest/lib/services/volume/v2/__init__.py b/tempest/lib/services/volume/v2/__init__.py
index 9434896..68982d9 100644
--- a/tempest/lib/services/volume/v2/__init__.py
+++ b/tempest/lib/services/volume/v2/__init__.py
@@ -23,6 +23,8 @@
from tempest.lib.services.volume.v2.hosts_client import HostsClient
from tempest.lib.services.volume.v2.limits_client import LimitsClient
from tempest.lib.services.volume.v2.qos_client import QosSpecsClient
+from tempest.lib.services.volume.v2.quota_classes_client import \
+ QuotaClassesClient
from tempest.lib.services.volume.v2.quotas_client import QuotasClient
from tempest.lib.services.volume.v2.scheduler_stats_client import \
SchedulerStatsClient
@@ -40,4 +42,5 @@
'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
'LimitsClient', 'CapabilitiesClient', 'SchedulerStatsClient',
- 'SnapshotManageClient', 'VolumeManageClient', 'TransfersClient']
+ 'SnapshotManageClient', 'VolumeManageClient', 'TransfersClient',
+ 'QuotaClassesClient']
diff --git a/tempest/lib/services/volume/v2/quota_classes_client.py b/tempest/lib/services/volume/v2/quota_classes_client.py
new file mode 100644
index 0000000..d40d2d9
--- /dev/null
+++ b/tempest/lib/services/volume/v2/quota_classes_client.py
@@ -0,0 +1,49 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# 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.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class QuotaClassesClient(rest_client.RestClient):
+ """Volume quota class V2 client."""
+
+ api_version = "v2"
+
+ def show_quota_class_set(self, quota_class_id):
+ """List quotas for a quota class.
+
+ TODO: Current api-site doesn't contain this API description.
+ LP: https://bugs.launchpad.net/nova/+bug/1602400
+ """
+ url = 'os-quota-class-sets/%s' % quota_class_id
+ resp, body = self.get(url)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
+
+ def update_quota_class_set(self, quota_class_id, **kwargs):
+ """Update quotas for a quota class.
+
+ TODO: Current api-site doesn't contain this API description.
+ LP: https://bugs.launchpad.net/nova/+bug/1602400
+ """
+ url = 'os-quota-class-sets/%s' % quota_class_id
+ put_body = json.dumps({'quota_class_set': kwargs})
+ resp, body = self.put(url, put_body)
+ self.expected_success(200, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/tests/lib/services/volume/v2/test_quota_classes_client.py b/tempest/tests/lib/services/volume/v2/test_quota_classes_client.py
new file mode 100644
index 0000000..e715fcc
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_quota_classes_client.py
@@ -0,0 +1,71 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# 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.lib.services.volume.v2 import quota_classes_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestQuotaClassesClient(base.BaseServiceTest):
+
+ FAKE_QUOTA_CLASS_SET = {
+ "id": "test",
+ "gigabytes": 2000,
+ "volumes": 200,
+ "snapshots": 50,
+ "backups": 20,
+ "backup_gigabytes": 1500,
+ "per_volume_gigabytes": 500,
+ }
+
+ def setUp(self):
+ super(TestQuotaClassesClient, self).setUp()
+ fake_auth = fake_auth_provider.FakeAuthProvider()
+ self.client = quota_classes_client.QuotaClassesClient(
+ fake_auth, 'volume', 'regionOne')
+
+ def _test_show_quota_class_set(self, bytes_body=False):
+ fake_body = {'quota_class_set': self.FAKE_QUOTA_CLASS_SET}
+ self.check_service_client_function(
+ self.client.show_quota_class_set,
+ 'tempest.lib.common.rest_client.RestClient.get',
+ fake_body,
+ bytes_body,
+ quota_class_id="test")
+
+ def _test_update_quota_class_set(self, bytes_body=False):
+ fake_quota_class_set = copy.deepcopy(self.FAKE_QUOTA_CLASS_SET)
+ fake_quota_class_set.pop("id")
+ fake_body = {'quota_class_set': fake_quota_class_set}
+ self.check_service_client_function(
+ self.client.update_quota_class_set,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ fake_body,
+ bytes_body,
+ quota_class_id="test")
+
+ def test_show_quota_class_set_with_str_body(self):
+ self._test_show_quota_class_set()
+
+ def test_show_quota_class_set_with_bytes_body(self):
+ self._test_show_quota_class_set(bytes_body=True)
+
+ def test_update_quota_class_set_with_str_boy(self):
+ self._test_update_quota_class_set()
+
+ def test_update_quota_class_set_with_bytes_body(self):
+ self._test_update_quota_class_set(bytes_body=True)