From 41ed715c2306b701fea19adfc9e0f187a5dcf381 Mon Sep 17 00:00:00 2001 From: xing-yang Date: Wed, 3 May 2017 06:52:56 -0400 Subject: [PATCH] Add Tests for Groups Volume APIs - Part 2 Generic volume groups support was added to Cinder in the Newton release: https://blueprints.launchpad.net/cinder/+spec/generic-volume-group This is the 2nd patch that adds the tempest tests for generic volume groups APIs in Cinder. It adds group_snapshots clients. It adds tests for the following APIs: * create group snapshot * delete group snapshot * show group snapshot * list group snapshot Change-Id: I21f07f7b3e8f22055e7978c0bf4aa01b80b493d9 --- ...pshots-tempest-tests-840df3da26590f5e.yaml | 6 + tempest/api/volume/admin/test_groups.py | 79 ++++++++++ tempest/api/volume/base.py | 3 + tempest/clients.py | 3 + tempest/common/waiters.py | 5 +- .../lib/services/volume/v1/backups_client.py | 5 + .../lib/services/volume/v2/backups_client.py | 5 + tempest/lib/services/volume/v3/__init__.py | 9 +- .../volume/v3/group_snapshots_client.py | 88 +++++++++++ .../services/volume/v3/snapshots_client.py | 21 +++ tempest/tests/common/test_waiters.py | 1 + .../volume/v3/test_group_snapshots_client.py | 141 ++++++++++++++++++ 12 files changed, 362 insertions(+), 4 deletions(-) create mode 100644 releasenotes/notes/add-volume-group-snapshots-tempest-tests-840df3da26590f5e.yaml create mode 100644 tempest/lib/services/volume/v3/group_snapshots_client.py create mode 100644 tempest/lib/services/volume/v3/snapshots_client.py create mode 100644 tempest/tests/lib/services/volume/v3/test_group_snapshots_client.py diff --git a/releasenotes/notes/add-volume-group-snapshots-tempest-tests-840df3da26590f5e.yaml b/releasenotes/notes/add-volume-group-snapshots-tempest-tests-840df3da26590f5e.yaml new file mode 100644 index 0000000000..2ca6e5a402 --- /dev/null +++ b/releasenotes/notes/add-volume-group-snapshots-tempest-tests-840df3da26590f5e.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add group_snapshots client for the volume service as library. + Add tempest tests for create group snapshot, delete group snapshot, show + group snapshot, and list group snapshots volume APIs. diff --git a/tempest/api/volume/admin/test_groups.py b/tempest/api/volume/admin/test_groups.py index 8609bdb303..4c1a3ebee0 100644 --- a/tempest/api/volume/admin/test_groups.py +++ b/tempest/api/volume/admin/test_groups.py @@ -27,6 +27,11 @@ class GroupsTest(base.BaseVolumeAdminTest): min_microversion = '3.14' max_microversion = 'latest' + @classmethod + def setup_clients(cls): + super(GroupsTest, cls).setup_clients() + cls.volumes_client = cls.admin_volume_client + def _delete_group(self, grp_id, delete_volumes=True): self.admin_groups_client.delete_group(grp_id, delete_volumes) vols = self.admin_volume_client.list_volumes(detail=True)['volumes'] @@ -35,6 +40,21 @@ class GroupsTest(base.BaseVolumeAdminTest): self.admin_volume_client.wait_for_resource_deletion(vol['id']) self.admin_groups_client.wait_for_resource_deletion(grp_id) + def _delete_group_snapshot(self, group_snapshot_id, grp_id): + self.admin_group_snapshots_client.delete_group_snapshot( + group_snapshot_id) + vols = self.admin_volume_client.list_volumes(detail=True)['volumes'] + snapshots = self.admin_snapshots_client.list_snapshots( + detail=True)['snapshots'] + for vol in vols: + for snap in snapshots: + if (vol['group_id'] == grp_id and + vol['id'] == snap['volume_id']): + self.admin_snapshots_client.wait_for_resource_deletion( + snap['id']) + self.admin_group_snapshots_client.wait_for_resource_deletion( + group_snapshot_id) + @decorators.idempotent_id('4b111d28-b73d-4908-9bd2-03dc2992e4d4') def test_group_create_show_list_delete(self): # Create volume type @@ -107,3 +127,62 @@ class GroupsTest(base.BaseVolumeAdminTest): grps = self.admin_groups_client.list_groups( detail=True)['groups'] self.assertEmpty(grps) + + @decorators.idempotent_id('1298e537-f1f0-47a3-a1dd-8adec8168897') + def test_group_snapshot_create_show_list_delete(self): + # Create volume type + volume_type = self.create_volume_type() + + # Create group type + group_type = self.create_group_type() + + # Create group + grp_name = data_utils.rand_name('Group') + grp = self.admin_groups_client.create_group( + group_type=group_type['id'], + volume_types=[volume_type['id']], + name=grp_name)['group'] + waiters.wait_for_volume_resource_status( + self.admin_groups_client, grp['id'], 'available') + self.addCleanup(self._delete_group, grp['id']) + self.assertEqual(grp_name, grp['name']) + + # Create volume + vol = self.create_volume(volume_type=volume_type['id'], + group_id=grp['id']) + + # Create group snapshot + group_snapshot_name = data_utils.rand_name('group_snapshot') + group_snapshot = ( + self.admin_group_snapshots_client.create_group_snapshot( + group_id=grp['id'], + name=group_snapshot_name)['group_snapshot']) + snapshots = self.admin_snapshots_client.list_snapshots( + detail=True)['snapshots'] + for snap in snapshots: + if vol['id'] == snap['volume_id']: + waiters.wait_for_volume_resource_status( + self.admin_snapshots_client, snap['id'], 'available') + waiters.wait_for_volume_resource_status( + self.admin_group_snapshots_client, + group_snapshot['id'], 'available') + self.assertEqual(group_snapshot_name, group_snapshot['name']) + + # Get a given group snapshot + group_snapshot = self.admin_group_snapshots_client.show_group_snapshot( + group_snapshot['id'])['group_snapshot'] + self.assertEqual(group_snapshot_name, group_snapshot['name']) + + # Get all group snapshots with detail + group_snapshots = ( + self.admin_group_snapshots_client.list_group_snapshots( + detail=True)['group_snapshots']) + self.assertIn((group_snapshot['name'], group_snapshot['id']), + [(m['name'], m['id']) for m in group_snapshots]) + + # Delete group snapshot + self._delete_group_snapshot(group_snapshot['id'], grp['id']) + group_snapshots = ( + self.admin_group_snapshots_client.list_group_snapshots( + detail=True)['group_snapshots']) + self.assertEmpty(group_snapshots) diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py index ef69ba3de8..9142dc39b2 100644 --- a/tempest/api/volume/base.py +++ b/tempest/api/volume/base.py @@ -85,6 +85,7 @@ class BaseVolumeTest(api_version_utils.BaseMicroversionTest, cls.messages_client = cls.os_primary.volume_v3_messages_client cls.versions_client = cls.os_primary.volume_v3_versions_client cls.groups_client = cls.os_primary.groups_v3_client + cls.group_snapshots_client = cls.os_primary.group_snapshots_v3_client def setUp(self): super(BaseVolumeTest, self).setUp() @@ -275,6 +276,8 @@ class BaseVolumeAdminTest(BaseVolumeTest): cls.os_admin.volume_scheduler_stats_v2_client cls.admin_messages_client = cls.os_admin.volume_v3_messages_client cls.admin_groups_client = cls.os_admin.groups_v3_client + cls.admin_group_snapshots_client = \ + cls.os_admin.group_snapshots_v3_client cls.admin_group_types_client = cls.os_admin.group_types_v3_client @classmethod diff --git a/tempest/clients.py b/tempest/clients.py index 85c2242fc1..d7a52d1ce1 100644 --- a/tempest/clients.py +++ b/tempest/clients.py @@ -268,6 +268,9 @@ class Manager(clients.ServiceClients): self.backups_v3_client = self.volume_v3.BackupsClient() self.group_types_v3_client = self.volume_v3.GroupTypesClient() self.groups_v3_client = self.volume_v3.GroupsClient() + self.group_snapshots_v3_client = \ + self.volume_v3.GroupSnapshotsClient() + self.snapshots_v3_client = self.volume_v3.SnapshotsClient() self.volume_v3_messages_client = self.volume_v3.MessagesClient() self.volume_v3_versions_client = self.volume_v3.VersionsClient() self.volumes_v3_client = self.volume_v3.VolumesClient() diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py index cf187e6bf4..f4c2866cfa 100644 --- a/tempest/common/waiters.py +++ b/tempest/common/waiters.py @@ -188,8 +188,9 @@ def wait_for_volume_resource_status(client, resource_id, statuses): """ if not isinstance(statuses, list): statuses = [statuses] - resource_name = re.findall(r'(Volume|Snapshot|Backup|Group)', - client.__class__.__name__)[0].lower() + resource_name = re.findall( + r'(volume|group-snapshot|snapshot|backup|group)', + client.resource_type)[-1].replace('-', '_') show_resource = getattr(client, 'show_' + resource_name) resource_status = show_resource(resource_id)[resource_name]['status'] start = int(time.time()) diff --git a/tempest/lib/services/volume/v1/backups_client.py b/tempest/lib/services/volume/v1/backups_client.py index 8677913740..77c40b3748 100644 --- a/tempest/lib/services/volume/v1/backups_client.py +++ b/tempest/lib/services/volume/v1/backups_client.py @@ -102,3 +102,8 @@ class BackupsClient(rest_client.RestClient): except lib_exc.NotFound: return True return False + + @property + def resource_type(self): + """Returns the primary type of resource this client works with.""" + return 'backup' diff --git a/tempest/lib/services/volume/v2/backups_client.py b/tempest/lib/services/volume/v2/backups_client.py index 830fb82fea..adfa6a6143 100644 --- a/tempest/lib/services/volume/v2/backups_client.py +++ b/tempest/lib/services/volume/v2/backups_client.py @@ -112,3 +112,8 @@ class BackupsClient(base_client.BaseClient): except lib_exc.NotFound: return True return False + + @property + def resource_type(self): + """Returns the primary type of resource this client works with.""" + return 'backup' diff --git a/tempest/lib/services/volume/v3/__init__.py b/tempest/lib/services/volume/v3/__init__.py index ff58fc2e4b..2d85553b46 100644 --- a/tempest/lib/services/volume/v3/__init__.py +++ b/tempest/lib/services/volume/v3/__init__.py @@ -14,11 +14,16 @@ from tempest.lib.services.volume.v3.backups_client import BackupsClient from tempest.lib.services.volume.v3.base_client import BaseClient +from tempest.lib.services.volume.v3.group_snapshots_client import \ + GroupSnapshotsClient from tempest.lib.services.volume.v3.group_types_client import GroupTypesClient from tempest.lib.services.volume.v3.groups_client import GroupsClient from tempest.lib.services.volume.v3.messages_client import MessagesClient +from tempest.lib.services.volume.v3.snapshots_client import SnapshotsClient from tempest.lib.services.volume.v3.versions_client import VersionsClient from tempest.lib.services.volume.v3.volumes_client import VolumesClient -__all__ = ['BackupsClient', 'BaseClient', 'GroupsClient', 'GroupTypesClient', - 'MessagesClient', 'VersionsClient', 'VolumesClient'] +__all__ = ['BackupsClient', 'BaseClient', 'GroupsClient', + 'GroupSnapshotsClient', 'GroupTypesClient', + 'MessagesClient', 'SnapshotsClient', 'VersionsClient', + 'VolumesClient'] diff --git a/tempest/lib/services/volume/v3/group_snapshots_client.py b/tempest/lib/services/volume/v3/group_snapshots_client.py new file mode 100644 index 0000000000..e644f02180 --- /dev/null +++ b/tempest/lib/services/volume/v3/group_snapshots_client.py @@ -0,0 +1,88 @@ +# Copyright (C) 2017 Dell Inc. or its subsidiaries. +# 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 six.moves.urllib import parse as urllib + +from tempest.lib.common import rest_client +from tempest.lib import exceptions as lib_exc +from tempest.lib.services.volume import base_client + + +class GroupSnapshotsClient(base_client.BaseClient): + """Client class to send CRUD Volume Group Snapshot API requests""" + api_version = 'v3' + + def create_group_snapshot(self, **kwargs): + """Creates a group snapshot. + + For a full list of available parameters, please refer to the official + API reference: + https://developer.openstack.org/api-ref/block-storage/v3/#create-group-snapshot + """ + post_body = json.dumps({'group_snapshot': kwargs}) + resp, body = self.post('group_snapshots', post_body) + body = json.loads(body) + self.expected_success(202, resp.status) + return rest_client.ResponseBody(resp, body) + + def delete_group_snapshot(self, group_snapshot_id): + """Deletes a group snapshot. + + For more information, please refer to the official API reference: + https://developer.openstack.org/api-ref/block-storage/v3/#delete-group-snapshot + """ + resp, body = self.delete('group_snapshots/%s' % group_snapshot_id) + self.expected_success(202, resp.status) + return rest_client.ResponseBody(resp, body) + + def show_group_snapshot(self, group_snapshot_id): + """Returns the details of a single group snapshot. + + For more information, please refer to the official API reference: + https://developer.openstack.org/api-ref/block-storage/v3/#show-group-snapshot-details + """ + url = "group_snapshots/%s" % str(group_snapshot_id) + resp, body = self.get(url) + body = json.loads(body) + self.expected_success(200, resp.status) + return rest_client.ResponseBody(resp, body) + + def list_group_snapshots(self, **params): + """Information for all the tenant's group snapshots. + + For more information, please refer to the official API reference: + https://developer.openstack.org/api-ref/block-storage/v3/#list-group-snapshots + https://developer.openstack.org/api-ref/block-storage/v3/#list-group-snapshots-with-details + """ + url = "group_snapshots" + if params: + url += '?%s' % urllib.urlencode(params) + resp, body = self.get(url) + body = json.loads(body) + self.expected_success(200, resp.status) + return rest_client.ResponseBody(resp, body) + + def is_resource_deleted(self, id): + try: + self.show_group_snapshot(id) + except lib_exc.NotFound: + return True + return False + + @property + def resource_type(self): + """Returns the primary type of resource this client works with.""" + return 'group-snapshot' diff --git a/tempest/lib/services/volume/v3/snapshots_client.py b/tempest/lib/services/volume/v3/snapshots_client.py new file mode 100644 index 0000000000..88c094f924 --- /dev/null +++ b/tempest/lib/services/volume/v3/snapshots_client.py @@ -0,0 +1,21 @@ +# Copyright (C) 2017 Dell Inc. or its subsidiaries. +# 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 tempest.lib.services.volume.v2 import snapshots_client + + +class SnapshotsClient(snapshots_client.SnapshotsClient): + """Client class to send CRUD Volume Snapshot V3 API requests.""" + api_version = "v3" diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py index c2f622caba..bc197b524d 100644 --- a/tempest/tests/common/test_waiters.py +++ b/tempest/tests/common/test_waiters.py @@ -59,6 +59,7 @@ class TestImageWaiters(base.TestCase): # Tests that the wait method raises VolumeRestoreErrorException if # the volume status is 'error_restoring'. client = mock.Mock(spec=volumes_client.VolumesClient, + resource_type="volume", build_interval=1) volume1 = {'volume': {'status': 'restoring-backup'}} volume2 = {'volume': {'status': 'error_restoring'}} diff --git a/tempest/tests/lib/services/volume/v3/test_group_snapshots_client.py b/tempest/tests/lib/services/volume/v3/test_group_snapshots_client.py new file mode 100644 index 0000000000..5ac5c08864 --- /dev/null +++ b/tempest/tests/lib/services/volume/v3/test_group_snapshots_client.py @@ -0,0 +1,141 @@ +# Copyright (C) 2017 Dell Inc. or its subsidiaries. +# +# 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 tempest.lib.services.volume.v3 import group_snapshots_client +from tempest.tests.lib import fake_auth_provider +from tempest.tests.lib.services import base + + +class TestGroupSnapshotsClient(base.BaseServiceTest): + FAKE_CREATE_GROUP_SNAPSHOT = { + "group_snapshot": { + "group_id": "49c8c114-0d68-4e89-b8bc-3f5a674d54be", + "name": "group-snapshot-001", + "description": "Test group snapshot 1" + } + } + + FAKE_INFO_GROUP_SNAPSHOT = { + "group_snapshot": { + "id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578", + "group_id": "49c8c114-0d68-4e89-b8bc-3f5a674d54be", + "name": "group-snapshot-001", + "description": "Test group snapshot 1", + "group_type_id": "0e58433f-d108-4bf3-a22c-34e6b71ef86b", + "status": "available", + "created_at": "20127-06-20T03:50:07Z" + } + } + + FAKE_LIST_GROUP_SNAPSHOTS = { + "group_snapshots": [ + { + "id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578", + "group_id": "49c8c114-0d68-4e89-b8bc-3f5a674d54be", + "name": "group-snapshot-001", + "description": "Test group snapshot 1", + "group_type_id": "0e58433f-d108-4bf3-a22c-34e6b71ef86b", + "status": "available", + "created_at": "2017-06-20T03:50:07Z", + }, + { + "id": "e479997c-650b-40a4-9dfe-77655818b0d2", + "group_id": "49c8c114-0d68-4e89-b8bc-3f5a674d54be", + "name": "group-snapshot-002", + "description": "Test group snapshot 2", + "group_type_id": "0e58433f-d108-4bf3-a22c-34e6b71ef86b", + "status": "available", + "created_at": "2017-06-19T01:52:47Z", + }, + { + "id": "c5c4769e-213c-40a6-a568-8e797bb691d4", + "group_id": "49c8c114-0d68-4e89-b8bc-3f5a674d54be", + "name": "group-snapshot-003", + "description": "Test group snapshot 3", + "group_type_id": "0e58433f-d108-4bf3-a22c-34e6b71ef86b", + "status": "available", + "created_at": "2017-06-18T06:34:32Z", + } + ] + } + + def setUp(self): + super(TestGroupSnapshotsClient, self).setUp() + fake_auth = fake_auth_provider.FakeAuthProvider() + self.client = group_snapshots_client.GroupSnapshotsClient( + fake_auth, 'volume', 'regionOne') + + def _test_create_group_snapshot(self, bytes_body=False): + self.check_service_client_function( + self.client.create_group_snapshot, + 'tempest.lib.common.rest_client.RestClient.post', + self.FAKE_CREATE_GROUP_SNAPSHOT, + bytes_body, + group_id="49c8c114-0d68-4e89-b8bc-3f5a674d54be", + status=202) + + def _test_show_group_snapshot(self, bytes_body=False): + self.check_service_client_function( + self.client.show_group_snapshot, + 'tempest.lib.common.rest_client.RestClient.get', + self.FAKE_INFO_GROUP_SNAPSHOT, + bytes_body, + group_snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5") + + def _test_list_group_snapshots(self, bytes_body=False, detail=False): + resp_body = [] + if detail: + resp_body = self.FAKE_LIST_GROUP_SNAPSHOTS + else: + resp_body = { + 'group_snapshots': [{ + 'id': group_snapshot['id'], + 'name': group_snapshot['name'], + 'group_type_id': group_snapshot['group_type_id']} + for group_snapshot in + self.FAKE_LIST_GROUP_SNAPSHOTS['group_snapshots'] + ] + } + self.check_service_client_function( + self.client.list_group_snapshots, + 'tempest.lib.common.rest_client.RestClient.get', + resp_body, + bytes_body, + detail=detail) + + def test_create_group_snapshot_with_str_body(self): + self._test_create_group_snapshot() + + def test_create_group_snapshot_with_bytes_body(self): + self._test_create_group_snapshot(bytes_body=True) + + def test_show_group_snapshot_with_str_body(self): + self._test_show_group_snapshot() + + def test_show_group_snapshot_with_bytes_body(self): + self._test_show_group_snapshot(bytes_body=True) + + def test_list_group_snapshots_with_str_body(self): + self._test_list_group_snapshots() + + def test_list_group_snapshots_with_bytes_body(self): + self._test_list_group_snapshots(bytes_body=True) + + def test_delete_group_snapshot(self): + self.check_service_client_function( + self.client.delete_group_snapshot, + 'tempest.lib.common.rest_client.RestClient.delete', + {}, + group_snapshot_id='0e701ab8-1bec-4b9f-b026-a7ba4af13578', + status=202)