Refactored cinder tempest tests to cinder_tempest_plugin
* As per the current codebase of cinder tempest plugin, it is getting
dumped in the cinder/tests and the package name is setup.cfg is
cinder_tempest_plugin which does not exists. So i refactored to
give a proper tempest plugin shape.
Change-Id: Ia78ea53a99923844f40fb4e9fe22ec0da4b0c335
Chandan Kumar
8 years ago
| 0 | # Copyright 2017 NEC Corporation. | |
| 1 | # All Rights Reserved. | |
| 2 | # | |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 4 | # not use this file except in compliance with the License. You may obtain | |
| 5 | # a copy of the License at | |
| 6 | # | |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | # | |
| 9 | # Unless required by applicable law or agreed to in writing, software | |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 12 | # License for the specific language governing permissions and limitations | |
| 13 | # under the License. | |
| 14 | ||
| 15 | from tempest.api.volume import api_microversion_fixture | |
| 16 | from tempest.common import compute | |
| 17 | from tempest.common import waiters | |
| 18 | from tempest import config | |
| 19 | from tempest.lib.common import api_version_utils | |
| 20 | from tempest.lib.common.utils import data_utils | |
| 21 | from tempest.lib.common.utils import test_utils | |
| 22 | from tempest.lib import exceptions | |
| 23 | from tempest import test | |
| 24 | ||
| 25 | CONF = config.CONF | |
| 26 | ||
| 27 | ||
| 28 | class BaseVolumeTest(api_version_utils.BaseMicroversionTest, | |
| 29 | test.BaseTestCase): | |
| 30 | """Base test case class for all Cinder API tests.""" | |
| 31 | ||
| 32 | _api_version = 2 | |
| 33 | credentials = ['primary'] | |
| 34 | ||
| 35 | @classmethod | |
| 36 | def skip_checks(cls): | |
| 37 | super(BaseVolumeTest, cls).skip_checks() | |
| 38 | ||
| 39 | if not CONF.service_available.cinder: | |
| 40 | skip_msg = ("%s skipped as Cinder is not available" % cls.__name__) | |
| 41 | raise cls.skipException(skip_msg) | |
| 42 | if cls._api_version == 2: | |
| 43 | if not CONF.volume_feature_enabled.api_v2: | |
| 44 | msg = "Volume API v2 is disabled" | |
| 45 | raise cls.skipException(msg) | |
| 46 | elif cls._api_version == 3: | |
| 47 | if not CONF.volume_feature_enabled.api_v3: | |
| 48 | msg = "Volume API v3 is disabled" | |
| 49 | raise cls.skipException(msg) | |
| 50 | else: | |
| 51 | msg = ("Invalid Cinder API version (%s)" % cls._api_version) | |
| 52 | raise exceptions.InvalidConfiguration(msg) | |
| 53 | ||
| 54 | api_version_utils.check_skip_with_microversion( | |
| 55 | cls.min_microversion, cls.max_microversion, | |
| 56 | CONF.volume.min_microversion, CONF.volume.max_microversion) | |
| 57 | ||
| 58 | @classmethod | |
| 59 | def setup_clients(cls): | |
| 60 | super(BaseVolumeTest, cls).setup_clients() | |
| 61 | if cls._api_version == 3: | |
| 62 | cls.backups_client = cls.os_primary.backups_v3_client | |
| 63 | cls.volumes_client = cls.os_primary.volumes_v3_client | |
| 64 | else: | |
| 65 | cls.backups_client = cls.os_primary.backups_v2_client | |
| 66 | cls.volumes_client = cls.os_primary.volumes_v2_client | |
| 67 | ||
| 68 | cls.snapshots_client = cls.os_primary.snapshots_v2_client | |
| 69 | ||
| 70 | @classmethod | |
| 71 | def setup_credentials(cls): | |
| 72 | cls.set_network_resources() | |
| 73 | super(BaseVolumeTest, cls).setup_credentials() | |
| 74 | ||
| 75 | def setUp(self): | |
| 76 | super(BaseVolumeTest, self).setUp() | |
| 77 | self.useFixture(api_microversion_fixture.APIMicroversionFixture( | |
| 78 | self.request_microversion)) | |
| 79 | ||
| 80 | @classmethod | |
| 81 | def resource_setup(cls): | |
| 82 | super(BaseVolumeTest, cls).resource_setup() | |
| 83 | cls.request_microversion = ( | |
| 84 | api_version_utils.select_request_microversion( | |
| 85 | cls.min_microversion, | |
| 86 | CONF.volume.min_microversion)) | |
| 87 | ||
| 88 | @classmethod | |
| 89 | def create_volume(cls, wait_until='available', **kwargs): | |
| 90 | """Wrapper utility that returns a test volume. | |
| 91 | ||
| 92 | :param wait_until: wait till volume status. | |
| 93 | """ | |
| 94 | if 'size' not in kwargs: | |
| 95 | kwargs['size'] = CONF.volume.volume_size | |
| 96 | ||
| 97 | if 'imageRef' in kwargs: | |
| 98 | image = cls.os_primary.image_client_v2.show_image( | |
| 99 | kwargs['imageRef']) | |
| 100 | min_disk = image['min_disk'] | |
| 101 | kwargs['size'] = max(kwargs['size'], min_disk) | |
| 102 | ||
| 103 | if 'name' not in kwargs: | |
| 104 | name = data_utils.rand_name(cls.__name__ + '-Volume') | |
| 105 | kwargs['name'] = name | |
| 106 | ||
| 107 | volume = cls.volumes_client.create_volume(**kwargs)['volume'] | |
| 108 | cls.addClassResourceCleanup( | |
| 109 | cls.volumes_client.wait_for_resource_deletion, volume['id']) | |
| 110 | cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, | |
| 111 | cls.volumes_client.delete_volume, | |
| 112 | volume['id']) | |
| 113 | waiters.wait_for_volume_resource_status(cls.volumes_client, | |
| 114 | volume['id'], wait_until) | |
| 115 | return volume | |
| 116 | ||
| 117 | @classmethod | |
| 118 | def create_snapshot(cls, volume_id=1, **kwargs): | |
| 119 | """Wrapper utility that returns a test snapshot.""" | |
| 120 | if 'name' not in kwargs: | |
| 121 | name = data_utils.rand_name(cls.__name__ + '-Snapshot') | |
| 122 | kwargs['name'] = name | |
| 123 | ||
| 124 | snapshot = cls.snapshots_client.create_snapshot( | |
| 125 | volume_id=volume_id, **kwargs)['snapshot'] | |
| 126 | cls.addClassResourceCleanup( | |
| 127 | cls.snapshots_client.wait_for_resource_deletion, snapshot['id']) | |
| 128 | cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, | |
| 129 | cls.snapshots_client.delete_snapshot, | |
| 130 | snapshot['id']) | |
| 131 | waiters.wait_for_volume_resource_status(cls.snapshots_client, | |
| 132 | snapshot['id'], 'available') | |
| 133 | return snapshot | |
| 134 | ||
| 135 | def create_backup(self, volume_id, backup_client=None, **kwargs): | |
| 136 | """Wrapper utility that returns a test backup.""" | |
| 137 | if backup_client is None: | |
| 138 | backup_client = self.backups_client | |
| 139 | if 'name' not in kwargs: | |
| 140 | name = data_utils.rand_name(self.__class__.__name__ + '-Backup') | |
| 141 | kwargs['name'] = name | |
| 142 | ||
| 143 | backup = backup_client.create_backup( | |
| 144 | volume_id=volume_id, **kwargs)['backup'] | |
| 145 | self.addCleanup(backup_client.delete_backup, backup['id']) | |
| 146 | waiters.wait_for_volume_resource_status(backup_client, backup['id'], | |
| 147 | 'available') | |
| 148 | return backup | |
| 149 | ||
| 150 | def create_server(self, wait_until='ACTIVE', **kwargs): | |
| 151 | name = kwargs.pop( | |
| 152 | 'name', | |
| 153 | data_utils.rand_name(self.__class__.__name__ + '-instance')) | |
| 154 | ||
| 155 | tenant_network = self.get_tenant_network() | |
| 156 | body, _ = compute.create_test_server( | |
| 157 | self.os_primary, | |
| 158 | tenant_network=tenant_network, | |
| 159 | name=name, | |
| 160 | wait_until=wait_until, | |
| 161 | **kwargs) | |
| 162 | ||
| 163 | self.addCleanup(test_utils.call_and_ignore_notfound_exc, | |
| 164 | waiters.wait_for_server_termination, | |
| 165 | self.os_primary.servers_client, body['id']) | |
| 166 | self.addCleanup(test_utils.call_and_ignore_notfound_exc, | |
| 167 | self.os_primary.servers_client.delete_server, | |
| 168 | body['id']) | |
| 169 | return body |
| 0 | # Copyright (C) 2015 EMC Corporation. | |
| 1 | # Copyright (C) 2016 Pure Storage, Inc. | |
| 2 | # All Rights Reserved. | |
| 3 | # | |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 5 | # not use this file except in compliance with the License. You may obtain | |
| 6 | # a copy of the License at | |
| 7 | # | |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 9 | # | |
| 10 | # Unless required by applicable law or agreed to in writing, software | |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 13 | # License for the specific language governing permissions and limitations | |
| 14 | # under the License. | |
| 15 | ||
| 16 | from tempest.common import waiters | |
| 17 | from tempest import config | |
| 18 | from tempest.lib.common.utils import data_utils | |
| 19 | from tempest.lib import decorators | |
| 20 | ||
| 21 | from cinder.tests.tempest.api.volume import base | |
| 22 | from cinder.tests.tempest import cinder_clients | |
| 23 | ||
| 24 | CONF = config.CONF | |
| 25 | ||
| 26 | ||
| 27 | class ConsistencyGroupsV2Test(base.BaseVolumeTest): | |
| 28 | @classmethod | |
| 29 | def setup_clients(cls): | |
| 30 | cls._api_version = 2 | |
| 31 | super(ConsistencyGroupsV2Test, cls).setup_clients() | |
| 32 | cls.admin_volume_client = cls.os_admin.volumes_v2_client | |
| 33 | ||
| 34 | manager = cinder_clients.Manager(cls.os_admin) | |
| 35 | cls.consistencygroups_adm_client = manager.consistencygroups_adm_client | |
| 36 | ||
| 37 | @classmethod | |
| 38 | def skip_checks(cls): | |
| 39 | super(ConsistencyGroupsV2Test, cls).skip_checks() | |
| 40 | if not CONF.volume_feature_enabled.consistency_group: | |
| 41 | raise cls.skipException("Cinder consistency group " | |
| 42 | "feature disabled") | |
| 43 | ||
| 44 | def _delete_consistencygroup(self, cg_id): | |
| 45 | self.consistencygroups_adm_client.delete_consistencygroup(cg_id) | |
| 46 | vols = self.admin_volume_client.list_volumes(detail=True)['volumes'] | |
| 47 | for vol in vols: | |
| 48 | if vol['consistencygroup_id'] == cg_id: | |
| 49 | self.admin_volume_client.wait_for_resource_deletion(vol['id']) | |
| 50 | self.consistencygroups_adm_client.wait_for_consistencygroup_deletion( | |
| 51 | cg_id) | |
| 52 | ||
| 53 | def _delete_cgsnapshot(self, cgsnapshot_id, cg_id): | |
| 54 | self.consistencygroups_adm_client.delete_cgsnapshot(cgsnapshot_id) | |
| 55 | vols = self.admin_volume_client.list_volumes(detail=True)['volumes'] | |
| 56 | snapshots = self.os_admin.snapshots_v2_client.list_snapshots( | |
| 57 | detail=True)['snapshots'] | |
| 58 | for vol in vols: | |
| 59 | for snap in snapshots: | |
| 60 | if (vol['consistencygroup_id'] == cg_id and | |
| 61 | vol['id'] == snap['volume_id']): | |
| 62 | (self.snapshots_client. | |
| 63 | wait_for_resource_deletion(snap['id'])) | |
| 64 | self.consistencygroups_adm_client.wait_for_cgsnapshot_deletion( | |
| 65 | cgsnapshot_id) | |
| 66 | ||
| 67 | @decorators.idempotent_id('3fe776ba-ec1f-4e6c-8d78-4b14c3a7fc44') | |
| 68 | def test_consistencygroup_create_delete(self): | |
| 69 | # Create volume type | |
| 70 | name = data_utils.rand_name("volume-type") | |
| 71 | volume_type = self.os_admin.volume_types_v2_client.create_volume_type( | |
| 72 | name=name)['volume_type'] | |
| 73 | ||
| 74 | # Create CG | |
| 75 | cg_name = data_utils.rand_name('CG') | |
| 76 | create_consistencygroup = ( | |
| 77 | self.consistencygroups_adm_client.create_consistencygroup) | |
| 78 | cg = create_consistencygroup(volume_type['id'], | |
| 79 | name=cg_name)['consistencygroup'] | |
| 80 | vol_name = data_utils.rand_name("volume") | |
| 81 | params = {'name': vol_name, | |
| 82 | 'volume_type': volume_type['id'], | |
| 83 | 'consistencygroup_id': cg['id'], | |
| 84 | 'size': CONF.volume.volume_size} | |
| 85 | ||
| 86 | # Create volume | |
| 87 | volume = self.admin_volume_client.create_volume(**params)['volume'] | |
| 88 | ||
| 89 | waiters.wait_for_volume_resource_status(self.admin_volume_client, | |
| 90 | volume['id'], 'available') | |
| 91 | self.consistencygroups_adm_client.wait_for_consistencygroup_status( | |
| 92 | cg['id'], 'available') | |
| 93 | self.assertEqual(cg_name, cg['name']) | |
| 94 | ||
| 95 | # Get a given CG | |
| 96 | cg = self.consistencygroups_adm_client.show_consistencygroup( | |
| 97 | cg['id'])['consistencygroup'] | |
| 98 | self.assertEqual(cg_name, cg['name']) | |
| 99 | ||
| 100 | # Get all CGs with detail | |
| 101 | cgs = self.consistencygroups_adm_client.list_consistencygroups( | |
| 102 | detail=True)['consistencygroups'] | |
| 103 | self.assertIn((cg['name'], cg['id']), | |
| 104 | [(m['name'], m['id']) for m in cgs]) | |
| 105 | ||
| 106 | # Clean up | |
| 107 | self._delete_consistencygroup(cg['id']) | |
| 108 | self.os_admin.volume_types_v2_client.delete_volume_type( | |
| 109 | volume_type['id']) | |
| 110 | ||
| 111 | @decorators.idempotent_id('2134dd52-f333-4456-bb05-6cb0f009a44f') | |
| 112 | def test_consistencygroup_cgsnapshot_create_delete(self): | |
| 113 | # Create volume type | |
| 114 | name = data_utils.rand_name("volume-type") | |
| 115 | volume_type = self.admin_volume_types_client.create_volume_type( | |
| 116 | name=name)['volume_type'] | |
| 117 | ||
| 118 | # Create CG | |
| 119 | cg_name = data_utils.rand_name('CG') | |
| 120 | create_consistencygroup = ( | |
| 121 | self.consistencygroups_adm_client.create_consistencygroup) | |
| 122 | cg = create_consistencygroup(volume_type['id'], | |
| 123 | name=cg_name)['consistencygroup'] | |
| 124 | vol_name = data_utils.rand_name("volume") | |
| 125 | params = {'name': vol_name, | |
| 126 | 'volume_type': volume_type['id'], | |
| 127 | 'consistencygroup_id': cg['id'], | |
| 128 | 'size': CONF.volume.volume_size} | |
| 129 | ||
| 130 | # Create volume | |
| 131 | volume = self.admin_volume_client.create_volume(**params)['volume'] | |
| 132 | waiters.wait_for_volume_resource_status(self.admin_volume_client, | |
| 133 | volume['id'], 'available') | |
| 134 | self.consistencygroups_adm_client.wait_for_consistencygroup_status( | |
| 135 | cg['id'], 'available') | |
| 136 | self.assertEqual(cg_name, cg['name']) | |
| 137 | ||
| 138 | # Create cgsnapshot | |
| 139 | cgsnapshot_name = data_utils.rand_name('cgsnapshot') | |
| 140 | create_cgsnapshot = ( | |
| 141 | self.consistencygroups_adm_client.create_cgsnapshot) | |
| 142 | cgsnapshot = create_cgsnapshot(cg['id'], | |
| 143 | name=cgsnapshot_name)['cgsnapshot'] | |
| 144 | snapshots = self.os_admin.snapshots_v2_client.list_snapshots( | |
| 145 | detail=True)['snapshots'] | |
| 146 | for snap in snapshots: | |
| 147 | if volume['id'] == snap['volume_id']: | |
| 148 | waiters.wait_for_volume_resource_status( | |
| 149 | self.os_admin.snapshots_v2_client, | |
| 150 | snap['id'], 'available') | |
| 151 | self.consistencygroups_adm_client.wait_for_cgsnapshot_status( | |
| 152 | cgsnapshot['id'], 'available') | |
| 153 | self.assertEqual(cgsnapshot_name, cgsnapshot['name']) | |
| 154 | ||
| 155 | # Get a given CG snapshot | |
| 156 | cgsnapshot = self.consistencygroups_adm_client.show_cgsnapshot( | |
| 157 | cgsnapshot['id'])['cgsnapshot'] | |
| 158 | self.assertEqual(cgsnapshot_name, cgsnapshot['name']) | |
| 159 | ||
| 160 | # Get all CG snapshots with detail | |
| 161 | cgsnapshots = self.consistencygroups_adm_client.list_cgsnapshots( | |
| 162 | detail=True)['cgsnapshots'] | |
| 163 | self.assertIn((cgsnapshot['name'], cgsnapshot['id']), | |
| 164 | [(m['name'], m['id']) for m in cgsnapshots]) | |
| 165 | ||
| 166 | # Clean up | |
| 167 | self._delete_cgsnapshot(cgsnapshot['id'], cg['id']) | |
| 168 | self._delete_consistencygroup(cg['id']) | |
| 169 | self.admin_volume_types_client.delete_volume_type(volume_type['id']) | |
| 170 | ||
| 171 | @decorators.idempotent_id('3a6a5525-25ca-4a6c-aac4-cac6fa8f5b43') | |
| 172 | def test_create_consistencygroup_from_cgsnapshot(self): | |
| 173 | # Create volume type | |
| 174 | name = data_utils.rand_name("volume-type") | |
| 175 | volume_type = self.admin_volume_types_client.create_volume_type( | |
| 176 | name=name)['volume_type'] | |
| 177 | ||
| 178 | # Create CG | |
| 179 | cg_name = data_utils.rand_name('CG') | |
| 180 | create_consistencygroup = ( | |
| 181 | self.consistencygroups_adm_client.create_consistencygroup) | |
| 182 | cg = create_consistencygroup(volume_type['id'], | |
| 183 | name=cg_name)['consistencygroup'] | |
| 184 | vol_name = data_utils.rand_name("volume") | |
| 185 | params = {'name': vol_name, | |
| 186 | 'volume_type': volume_type['id'], | |
| 187 | 'consistencygroup_id': cg['id'], | |
| 188 | 'size': CONF.volume.volume_size} | |
| 189 | ||
| 190 | # Create volume | |
| 191 | volume = self.admin_volume_client.create_volume(**params)['volume'] | |
| 192 | waiters.wait_for_volume_resource_status(self.admin_volume_client, | |
| 193 | volume['id'], 'available') | |
| 194 | self.consistencygroups_adm_client.wait_for_consistencygroup_status( | |
| 195 | cg['id'], 'available') | |
| 196 | self.assertEqual(cg_name, cg['name']) | |
| 197 | ||
| 198 | # Create cgsnapshot | |
| 199 | cgsnapshot_name = data_utils.rand_name('cgsnapshot') | |
| 200 | create_cgsnapshot = ( | |
| 201 | self.consistencygroups_adm_client.create_cgsnapshot) | |
| 202 | cgsnapshot = create_cgsnapshot(cg['id'], | |
| 203 | name=cgsnapshot_name)['cgsnapshot'] | |
| 204 | snapshots = self.snapshots_client.list_snapshots( | |
| 205 | detail=True)['snapshots'] | |
| 206 | for snap in snapshots: | |
| 207 | if volume['id'] == snap['volume_id']: | |
| 208 | waiters.wait_for_volume_resource_status( | |
| 209 | self.os_admin.snapshots_v2_client, snap['id'], 'available') | |
| 210 | self.consistencygroups_adm_client.wait_for_cgsnapshot_status( | |
| 211 | cgsnapshot['id'], 'available') | |
| 212 | self.assertEqual(cgsnapshot_name, cgsnapshot['name']) | |
| 213 | ||
| 214 | # Create CG from CG snapshot | |
| 215 | cg_name2 = data_utils.rand_name('CG_from_snap') | |
| 216 | create_consistencygroup2 = ( | |
| 217 | self.consistencygroups_adm_client.create_consistencygroup_from_src) | |
| 218 | cg2 = create_consistencygroup2(cgsnapshot_id=cgsnapshot['id'], | |
| 219 | name=cg_name2)['consistencygroup'] | |
| 220 | vols = self.admin_volume_client.list_volumes( | |
| 221 | detail=True)['volumes'] | |
| 222 | for vol in vols: | |
| 223 | if vol['consistencygroup_id'] == cg2['id']: | |
| 224 | waiters.wait_for_volume_resource_status( | |
| 225 | self.admin_volume_client, vol['id'], 'available') | |
| 226 | self.consistencygroups_adm_client.wait_for_consistencygroup_status( | |
| 227 | cg2['id'], 'available') | |
| 228 | self.assertEqual(cg_name2, cg2['name']) | |
| 229 | ||
| 230 | # Clean up | |
| 231 | self._delete_consistencygroup(cg2['id']) | |
| 232 | self._delete_cgsnapshot(cgsnapshot['id'], cg['id']) | |
| 233 | self._delete_consistencygroup(cg['id']) | |
| 234 | self.admin_volume_types_client.delete_volume_type(volume_type['id']) | |
| 235 | ||
| 236 | @decorators.idempotent_id('556121ae-de9c-4342-9897-e54260447a19') | |
| 237 | def test_create_consistencygroup_from_consistencygroup(self): | |
| 238 | # Create volume type | |
| 239 | name = data_utils.rand_name("volume-type") | |
| 240 | volume_type = self.admin_volume_types_client.create_volume_type( | |
| 241 | name=name)['volume_type'] | |
| 242 | ||
| 243 | # Create CG | |
| 244 | cg_name = data_utils.rand_name('CG') | |
| 245 | create_consistencygroup = ( | |
| 246 | self.consistencygroups_adm_client.create_consistencygroup) | |
| 247 | cg = create_consistencygroup(volume_type['id'], | |
| 248 | name=cg_name)['consistencygroup'] | |
| 249 | vol_name = data_utils.rand_name("volume") | |
| 250 | params = {'name': vol_name, | |
| 251 | 'volume_type': volume_type['id'], | |
| 252 | 'consistencygroup_id': cg['id'], | |
| 253 | 'size': CONF.volume.volume_size} | |
| 254 | ||
| 255 | # Create volume | |
| 256 | volume = self.admin_volume_client.create_volume(**params)['volume'] | |
| 257 | waiters.wait_for_volume_resource_status(self.admin_volume_client, | |
| 258 | volume['id'], 'available') | |
| 259 | self.consistencygroups_adm_client.wait_for_consistencygroup_status( | |
| 260 | cg['id'], 'available') | |
| 261 | self.assertEqual(cg_name, cg['name']) | |
| 262 | ||
| 263 | # Create CG from CG | |
| 264 | cg_name2 = data_utils.rand_name('CG_from_cg') | |
| 265 | create_consistencygroup2 = ( | |
| 266 | self.consistencygroups_adm_client.create_consistencygroup_from_src) | |
| 267 | cg2 = create_consistencygroup2(source_cgid=cg['id'], | |
| 268 | name=cg_name2)['consistencygroup'] | |
| 269 | vols = self.admin_volume_client.list_volumes( | |
| 270 | detail=True)['volumes'] | |
| 271 | for vol in vols: | |
| 272 | if vol['consistencygroup_id'] == cg2['id']: | |
| 273 | waiters.wait_for_volume_resource_status( | |
| 274 | self.admin_volume_client, vol['id'], 'available') | |
| 275 | self.consistencygroups_adm_client.wait_for_consistencygroup_status( | |
| 276 | cg2['id'], 'available') | |
| 277 | self.assertEqual(cg_name2, cg2['name']) | |
| 278 | ||
| 279 | # Clean up | |
| 280 | self._delete_consistencygroup(cg2['id']) | |
| 281 | self._delete_consistencygroup(cg['id']) | |
| 282 | self.admin_volume_types_client.delete_volume_type(volume_type['id']) |
| 0 | # Copyright (c) 2016 Mirantis Inc. | |
| 1 | # All Rights Reserved. | |
| 2 | # | |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 4 | # not use this file except in compliance with the License. You may obtain | |
| 5 | # a copy of the License at | |
| 6 | # | |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | # | |
| 9 | # Unless required by applicable law or agreed to in writing, software | |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 12 | # License for the specific language governing permissions and limitations | |
| 13 | # under the License. | |
| 14 | ||
| 15 | from tempest.common import waiters | |
| 16 | from tempest import config | |
| 17 | from tempest.lib.common.utils import data_utils | |
| 18 | from tempest.lib import decorators | |
| 19 | ||
| 20 | from cinder.tests.tempest.api.volume import base | |
| 21 | ||
| 22 | CONF = config.CONF | |
| 23 | ||
| 24 | ||
| 25 | class VolumesBackupsTest(base.BaseVolumeTest): | |
| 26 | ||
| 27 | @classmethod | |
| 28 | def skip_checks(cls): | |
| 29 | super(VolumesBackupsTest, cls).skip_checks() | |
| 30 | if not CONF.volume_feature_enabled.backup: | |
| 31 | raise cls.skipException("Cinder backup feature disabled") | |
| 32 | ||
| 33 | @decorators.idempotent_id('885410c6-cd1d-452c-a409-7c32b7e0be15') | |
| 34 | def test_volume_snapshot_backup(self): | |
| 35 | """Create backup from snapshot.""" | |
| 36 | volume = self.create_volume() | |
| 37 | # Create snapshot | |
| 38 | snapshot = self.create_snapshot(volume['id']) | |
| 39 | # Create backup | |
| 40 | backup = self.create_backup( | |
| 41 | volume_id=volume['id'], | |
| 42 | snapshot_id=snapshot['id']) | |
| 43 | # Get a given backup | |
| 44 | backup = self.backups_client.show_backup( | |
| 45 | backup['id'])['backup'] | |
| 46 | waiters.wait_for_volume_resource_status( | |
| 47 | self.backups_client, | |
| 48 | backup['id'], 'available') | |
| 49 | self.assertEqual(volume['id'], backup['volume_id']) | |
| 50 | self.assertEqual(snapshot['id'], backup['snapshot_id']) | |
| 51 | ||
| 52 | self.snapshots_client.delete_snapshot(snapshot['id']) | |
| 53 | self.snapshots_client.wait_for_resource_deletion(snapshot['id']) | |
| 54 | ||
| 55 | self.volumes_client.delete_volume(volume['id']) | |
| 56 | self.volumes_client.wait_for_resource_deletion(volume['id']) | |
| 57 | ||
| 58 | @decorators.idempotent_id('b5d837b0-7066-455d-88fc-4a721a899306') | |
| 59 | def test_backup_create_and_restore_to_an_existing_volume(self): | |
| 60 | """Test backup create and restore to an existing volume.""" | |
| 61 | # Create volume | |
| 62 | src_vol = self.create_volume() | |
| 63 | self.addCleanup(self.volumes_client.delete_volume, | |
| 64 | src_vol['id']) | |
| 65 | # Create backup | |
| 66 | backup = self.backups_client.create_backup( | |
| 67 | volume_id=src_vol['id'])['backup'] | |
| 68 | self.addCleanup(self.backups_client.delete_backup, backup['id']) | |
| 69 | waiters.wait_for_volume_resource_status( | |
| 70 | self.backups_client, | |
| 71 | backup['id'], 'available') | |
| 72 | # Restore to existing volume | |
| 73 | restore = self.backups_client.restore_backup( | |
| 74 | backup_id=backup['id'], | |
| 75 | volume_id=src_vol['id'])['restore'] | |
| 76 | waiters.wait_for_volume_resource_status( | |
| 77 | self.backups_client, | |
| 78 | backup['id'], 'available') | |
| 79 | waiters.wait_for_volume_resource_status( | |
| 80 | self.volumes_client, | |
| 81 | src_vol['id'], 'available') | |
| 82 | self.assertEqual(src_vol['id'], restore['volume_id']) | |
| 83 | self.assertEqual(backup['id'], restore['backup_id']) | |
| 84 | ||
| 85 | @decorators.idempotent_id('c810fe2c-cb40-43ab-96aa-471b74516a98') | |
| 86 | def test_incremental_backup(self): | |
| 87 | """Test create incremental backup.""" | |
| 88 | # Create volume from image | |
| 89 | volume = self.create_volume(size=CONF.volume.volume_size, | |
| 90 | imageRef=CONF.compute.image_ref) | |
| 91 | self.addCleanup(self.volumes_client.delete_volume, | |
| 92 | volume['id']) | |
| 93 | ||
| 94 | # Create backup | |
| 95 | backup = self.backups_client.create_backup( | |
| 96 | volume_id=volume['id'])['backup'] | |
| 97 | waiters.wait_for_volume_resource_status(self.backups_client, | |
| 98 | backup['id'], 'available') | |
| 99 | # Create a server | |
| 100 | bd_map = [{'volume_id': volume['id'], | |
| 101 | 'delete_on_termination': '0'}] | |
| 102 | ||
| 103 | server_name = data_utils.rand_name('instance') | |
| 104 | server = self.create_server( | |
| 105 | name=server_name, | |
| 106 | block_device_mapping=bd_map, | |
| 107 | wait_until='ACTIVE') | |
| 108 | ||
| 109 | # Delete VM | |
| 110 | self.os_primary.servers_client.delete_server(server['id']) | |
| 111 | # Create incremental backup | |
| 112 | waiters.wait_for_volume_resource_status(self.volumes_client, | |
| 113 | volume['id'], 'available') | |
| 114 | backup_incr = self.backups_client.create_backup( | |
| 115 | volume_id=volume['id'], | |
| 116 | incremental=True)['backup'] | |
| 117 | ||
| 118 | waiters.wait_for_volume_resource_status(self.backups_client, | |
| 119 | backup_incr['id'], | |
| 120 | 'available') | |
| 121 | ||
| 122 | is_incremental = self.backups_client.show_backup( | |
| 123 | backup_incr['id'])['backup']['is_incremental'] | |
| 124 | self.assertTrue(is_incremental) | |
| 125 | ||
| 126 | self.backups_client.delete_backup(backup_incr['id']) | |
| 127 | self.backups_client.wait_for_resource_deletion(backup_incr['id']) | |
| 128 | self.backups_client.delete_backup(backup['id']) | |
| 129 | self.backups_client.wait_for_resource_deletion(backup['id']) |
| 0 | # Copyright (c) 2017 Huawei. | |
| 1 | # All Rights Reserved. | |
| 2 | # | |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 4 | # not use this file except in compliance with the License. You may obtain | |
| 5 | # a copy of the License at | |
| 6 | # | |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | # | |
| 9 | # Unless required by applicable law or agreed to in writing, software | |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 12 | # License for the specific language governing permissions and limitations | |
| 13 | # under the License. | |
| 14 | ||
| 15 | from tempest.common import waiters | |
| 16 | from tempest import config | |
| 17 | from tempest.lib import decorators | |
| 18 | ||
| 19 | from cinder.tests.tempest.api.volume import base | |
| 20 | from cinder.tests.tempest import cinder_clients | |
| 21 | ||
| 22 | CONF = config.CONF | |
| 23 | ||
| 24 | ||
| 25 | class VolumeRevertTests(base.BaseVolumeTest): | |
| 26 | min_microversion = '3.40' | |
| 27 | ||
| 28 | @classmethod | |
| 29 | def skip_checks(cls): | |
| 30 | super(VolumeRevertTests, cls).skip_checks() | |
| 31 | if not CONF.volume_feature_enabled.volume_revert: | |
| 32 | raise cls.skipException("Cinder volume revert feature disabled") | |
| 33 | ||
| 34 | @classmethod | |
| 35 | def setup_clients(cls): | |
| 36 | cls._api_version = 3 | |
| 37 | super(VolumeRevertTests, cls).setup_clients() | |
| 38 | ||
| 39 | manager = cinder_clients.Manager(cls.os_primary) | |
| 40 | cls.volume_revert_client = manager.volume_revert_client | |
| 41 | ||
| 42 | def setUp(self): | |
| 43 | super(VolumeRevertTests, self).setUp() | |
| 44 | # Create volume | |
| 45 | self.volume = self.create_volume(size=1) | |
| 46 | # Create snapshot | |
| 47 | self.snapshot = self.create_snapshot(self.volume['id']) | |
| 48 | ||
| 49 | @decorators.idempotent_id('87b7dcb7-4950-4a3a-802c-ece55491846d') | |
| 50 | def test_volume_revert_to_snapshot(self): | |
| 51 | """Test revert to snapshot""" | |
| 52 | # Revert to snapshot | |
| 53 | self.volume_revert_client.revert_to_snapshot(self.volume, | |
| 54 | self.snapshot['id']) | |
| 55 | waiters.wait_for_volume_resource_status( | |
| 56 | self.volumes_client, | |
| 57 | self.volume['id'], 'available') | |
| 58 | waiters.wait_for_volume_resource_status( | |
| 59 | self.snapshots_client, | |
| 60 | self.snapshot['id'], 'available') | |
| 61 | volume = self.volumes_client.show_volume(self.volume['id'])['volume'] | |
| 62 | ||
| 63 | self.assertEqual(1, volume['size']) | |
| 64 | ||
| 65 | @decorators.idempotent_id('4e8b0788-87fe-430d-be7a-444d7f8e0347') | |
| 66 | def test_volume_revert_to_snapshot_after_extended(self): | |
| 67 | """Test revert to snapshot after extended""" | |
| 68 | # Extend the volume | |
| 69 | self.volumes_client.extend_volume(self.volume['id'], new_size=2) | |
| 70 | waiters.wait_for_volume_resource_status(self.volumes_client, | |
| 71 | self.volume['id'], 'available') | |
| 72 | # Revert to snapshot | |
| 73 | self.volume_revert_client.revert_to_snapshot(self.volume, | |
| 74 | self.snapshot['id']) | |
| 75 | waiters.wait_for_volume_resource_status( | |
| 76 | self.volumes_client, | |
| 77 | self.volume['id'], 'available') | |
| 78 | waiters.wait_for_volume_resource_status( | |
| 79 | self.snapshots_client, | |
| 80 | self.snapshot['id'], 'available') | |
| 81 | volume = self.volumes_client.show_volume(self.volume['id'])['volume'] | |
| 82 | self.assertEqual(2, volume['size']) |
| 0 | # -*- coding: utf-8 -*- | |
| 1 | # Copyright 2016 Red Hat, Inc. | |
| 2 | # All Rights Reserved. | |
| 3 | # | |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 5 | # not use this file except in compliance with the License. You may obtain | |
| 6 | # a copy of the License at | |
| 7 | # | |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 9 | # | |
| 10 | # Unless required by applicable law or agreed to in writing, software | |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 13 | # License for the specific language governing permissions and limitations | |
| 14 | # under the License. | |
| 15 | ||
| 16 | from tempest.common import waiters | |
| 17 | from tempest import config | |
| 18 | from tempest.lib.common.utils import data_utils | |
| 19 | from tempest.lib.common.utils import test_utils | |
| 20 | ||
| 21 | from cinder.tests.tempest.api.volume import base | |
| 22 | ||
| 23 | CONF = config.CONF | |
| 24 | ||
| 25 | ||
| 26 | class CinderUnicodeTest(base.BaseVolumeTest): | |
| 27 | ||
| 28 | @classmethod | |
| 29 | def resource_setup(cls): | |
| 30 | super(CinderUnicodeTest, cls).resource_setup() | |
| 31 | ||
| 32 | # Stick to three-byte unicode here, since four+ byte | |
| 33 | # chars require utf8mb4 database support which may not | |
| 34 | # be configured. | |
| 35 | cls.volume_name = u"CinderUnicodeTest塵㼗‽" | |
| 36 | cls.volume = cls.create_volume_with_args(name=cls.volume_name) | |
| 37 | ||
| 38 | @classmethod | |
| 39 | def create_volume_with_args(cls, **kwargs): | |
| 40 | if 'name' not in kwargs: | |
| 41 | kwargs['name'] = data_utils.rand_name('Volume') | |
| 42 | ||
| 43 | kwargs['size'] = CONF.volume.volume_size | |
| 44 | ||
| 45 | volume = cls.volumes_client.create_volume(**kwargs)['volume'] | |
| 46 | cls.addClassResourceCleanup( | |
| 47 | cls.volumes_client.wait_for_resource_deletion, volume['id']) | |
| 48 | cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, | |
| 49 | cls.volumes_client.delete_volume, | |
| 50 | volume['id']) | |
| 51 | waiters.wait_for_volume_resource_status(cls.volumes_client, | |
| 52 | volume['id'], | |
| 53 | 'available') | |
| 54 | ||
| 55 | return volume | |
| 56 | ||
| 57 | def test_create_delete_unicode_volume_name(self): | |
| 58 | """Create a volume with a unicode name and view it.""" | |
| 59 | ||
| 60 | result = self.volumes_client.show_volume(self.volume['id']) | |
| 61 | fetched_volume = result['volume'] | |
| 62 | self.assertEqual(fetched_volume['name'], | |
| 63 | self.volume_name) |
| 0 | # Copyright (c) 2016 Pure Storage, Inc. | |
| 1 | # All Rights Reserved. | |
| 2 | # | |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 4 | # not use this file except in compliance with the License. You may obtain | |
| 5 | # a copy of the License at | |
| 6 | # | |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | # | |
| 9 | # Unless required by applicable law or agreed to in writing, software | |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 12 | # License for the specific language governing permissions and limitations | |
| 13 | # under the License. | |
| 14 | ||
| 15 | from tempest import config | |
| 16 | ||
| 17 | from cinder.tests.tempest.services import consistencygroups_client | |
| 18 | from cinder.tests.tempest.services import volume_revert_client | |
| 19 | ||
| 20 | CONF = config.CONF | |
| 21 | ||
| 22 | ||
| 23 | class Manager(object): | |
| 24 | def __init__(self, base_manager): | |
| 25 | params = { | |
| 26 | 'service': CONF.volume.catalog_type, | |
| 27 | 'region': CONF.volume.region or CONF.identity.region, | |
| 28 | 'endpoint_type': CONF.volume.endpoint_type, | |
| 29 | 'build_interval': CONF.volume.build_interval, | |
| 30 | 'build_timeout': CONF.volume.build_timeout | |
| 31 | } | |
| 32 | params.update(base_manager.default_params) | |
| 33 | auth_provider = base_manager.auth_provider | |
| 34 | ||
| 35 | self.consistencygroups_adm_client = ( | |
| 36 | consistencygroups_client.ConsistencyGroupsClient(auth_provider, | |
| 37 | **params)) | |
| 38 | self.volume_revert_client = ( | |
| 39 | volume_revert_client.VolumeRevertClient(auth_provider, **params)) |
| 0 | # Copyright 2016 | |
| 1 | # All Rights Reserved. | |
| 2 | # | |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 4 | # not use this file except in compliance with the License. You may obtain | |
| 5 | # a copy of the License at | |
| 6 | # | |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | # | |
| 9 | # Unless required by applicable law or agreed to in writing, software | |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 12 | # License for the specific language governing permissions and limitations | |
| 13 | # under the License. | |
| 14 | ||
| 15 | from oslo_config import cfg | |
| 16 | ||
| 17 | cinder_option = [ | |
| 18 | cfg.BoolOpt('consistency_group', | |
| 19 | default=False, | |
| 20 | help='Enable to run Cinder volume consistency group tests'), | |
| 21 | cfg.BoolOpt('volume_revert', | |
| 22 | default=False, | |
| 23 | help='Enable to run Cinder volume revert tests'), | |
| 24 | ] |
| 0 | # Copyright 2015 | |
| 1 | # All Rights Reserved. | |
| 2 | # | |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 4 | # not use this file except in compliance with the License. You may obtain | |
| 5 | # a copy of the License at | |
| 6 | # | |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | # | |
| 9 | # Unless required by applicable law or agreed to in writing, software | |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 12 | # License for the specific language governing permissions and limitations | |
| 13 | # under the License. | |
| 14 | ||
| 15 | import cinder | |
| 16 | import os | |
| 17 | ||
| 18 | from cinder.tests.tempest import config as project_config | |
| 19 | ||
| 20 | from tempest import config | |
| 21 | from tempest.test_discover import plugins | |
| 22 | ||
| 23 | ||
| 24 | class CinderTempestPlugin(plugins.TempestPlugin): | |
| 25 | def load_tests(self): | |
| 26 | base_path = os.path.split(os.path.dirname( | |
| 27 | os.path.abspath(cinder.__file__)))[0] | |
| 28 | test_dir = "cinder/tests/tempest" | |
| 29 | full_test_dir = os.path.join(base_path, test_dir) | |
| 30 | return full_test_dir, base_path | |
| 31 | ||
| 32 | def register_opts(self, conf): | |
| 33 | config.register_opt_group( | |
| 34 | conf, config.volume_feature_group, | |
| 35 | project_config.cinder_option | |
| 36 | ) | |
| 37 | ||
| 38 | def get_opt_lists(self): | |
| 39 | return [ | |
| 40 | (config.volume_feature_group.name, | |
| 41 | project_config.cinder_option), | |
| 42 | ] |
| 0 | # Copyright (C) 2015 EMC Corporation. | |
| 1 | # Copyright (C) 2016 Pure Storage, Inc. | |
| 2 | # All Rights Reserved. | |
| 3 | # | |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 5 | # not use this file except in compliance with the License. You may obtain | |
| 6 | # a copy of the License at | |
| 7 | # | |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 9 | # | |
| 10 | # Unless required by applicable law or agreed to in writing, software | |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 13 | # License for the specific language governing permissions and limitations | |
| 14 | # under the License. | |
| 15 | ||
| 16 | import time | |
| 17 | ||
| 18 | from oslo_serialization import jsonutils as json | |
| 19 | from six.moves import http_client | |
| 20 | from tempest import exceptions | |
| 21 | from tempest.lib.common import rest_client | |
| 22 | from tempest.lib import exceptions as lib_exc | |
| 23 | ||
| 24 | ||
| 25 | class ConsistencyGroupsClient(rest_client.RestClient): | |
| 26 | """Client class to send CRUD Volume ConsistencyGroup API requests""" | |
| 27 | ||
| 28 | def __init__(self, auth_provider, service, region, **kwargs): | |
| 29 | super(ConsistencyGroupsClient, self).__init__( | |
| 30 | auth_provider, service, region, **kwargs) | |
| 31 | ||
| 32 | def create_consistencygroup(self, volume_types, **kwargs): | |
| 33 | """Creates a consistency group.""" | |
| 34 | post_body = {'volume_types': volume_types} | |
| 35 | if kwargs.get('availability_zone'): | |
| 36 | post_body['availability_zone'] = kwargs.get('availability_zone') | |
| 37 | if kwargs.get('name'): | |
| 38 | post_body['name'] = kwargs.get('name') | |
| 39 | if kwargs.get('description'): | |
| 40 | post_body['description'] = kwargs.get('description') | |
| 41 | post_body = json.dumps({'consistencygroup': post_body}) | |
| 42 | resp, body = self.post('consistencygroups', post_body) | |
| 43 | body = json.loads(body) | |
| 44 | self.expected_success(http_client.ACCEPTED, resp.status) | |
| 45 | return rest_client.ResponseBody(resp, body) | |
| 46 | ||
| 47 | def create_consistencygroup_from_src(self, **kwargs): | |
| 48 | """Creates a consistency group from source.""" | |
| 49 | post_body = {} | |
| 50 | if kwargs.get('cgsnapshot_id'): | |
| 51 | post_body['cgsnapshot_id'] = kwargs.get('cgsnapshot_id') | |
| 52 | if kwargs.get('source_cgid'): | |
| 53 | post_body['source_cgid'] = kwargs.get('source_cgid') | |
| 54 | if kwargs.get('name'): | |
| 55 | post_body['name'] = kwargs.get('name') | |
| 56 | if kwargs.get('description'): | |
| 57 | post_body['description'] = kwargs.get('description') | |
| 58 | post_body = json.dumps({'consistencygroup-from-src': post_body}) | |
| 59 | resp, body = self.post('consistencygroups/create_from_src', post_body) | |
| 60 | body = json.loads(body) | |
| 61 | self.expected_success(http_client.ACCEPTED, resp.status) | |
| 62 | return rest_client.ResponseBody(resp, body) | |
| 63 | ||
| 64 | def delete_consistencygroup(self, cg_id): | |
| 65 | """Delete a consistency group.""" | |
| 66 | post_body = {'force': True} | |
| 67 | post_body = json.dumps({'consistencygroup': post_body}) | |
| 68 | resp, body = self.post('consistencygroups/%s/delete' % cg_id, | |
| 69 | post_body) | |
| 70 | self.expected_success(http_client.ACCEPTED, resp.status) | |
| 71 | return rest_client.ResponseBody(resp, body) | |
| 72 | ||
| 73 | def show_consistencygroup(self, cg_id): | |
| 74 | """Returns the details of a single consistency group.""" | |
| 75 | url = "consistencygroups/%s" % str(cg_id) | |
| 76 | resp, body = self.get(url) | |
| 77 | body = json.loads(body) | |
| 78 | self.expected_success(http_client.OK, resp.status) | |
| 79 | return rest_client.ResponseBody(resp, body) | |
| 80 | ||
| 81 | def list_consistencygroups(self, detail=False): | |
| 82 | """Information for all the tenant's consistency groups.""" | |
| 83 | url = "consistencygroups" | |
| 84 | if detail: | |
| 85 | url += "/detail" | |
| 86 | resp, body = self.get(url) | |
| 87 | body = json.loads(body) | |
| 88 | self.expected_success(http_client.OK, resp.status) | |
| 89 | return rest_client.ResponseBody(resp, body) | |
| 90 | ||
| 91 | def create_cgsnapshot(self, consistencygroup_id, **kwargs): | |
| 92 | """Creates a consistency group snapshot.""" | |
| 93 | post_body = {'consistencygroup_id': consistencygroup_id} | |
| 94 | if kwargs.get('name'): | |
| 95 | post_body['name'] = kwargs.get('name') | |
| 96 | if kwargs.get('description'): | |
| 97 | post_body['description'] = kwargs.get('description') | |
| 98 | post_body = json.dumps({'cgsnapshot': post_body}) | |
| 99 | resp, body = self.post('cgsnapshots', post_body) | |
| 100 | body = json.loads(body) | |
| 101 | self.expected_success(http_client.ACCEPTED, resp.status) | |
| 102 | return rest_client.ResponseBody(resp, body) | |
| 103 | ||
| 104 | def delete_cgsnapshot(self, cgsnapshot_id): | |
| 105 | """Delete a consistency group snapshot.""" | |
| 106 | resp, body = self.delete('cgsnapshots/%s' % (str(cgsnapshot_id))) | |
| 107 | self.expected_success(http_client.ACCEPTED, resp.status) | |
| 108 | return rest_client.ResponseBody(resp, body) | |
| 109 | ||
| 110 | def show_cgsnapshot(self, cgsnapshot_id): | |
| 111 | """Returns the details of a single consistency group snapshot.""" | |
| 112 | url = "cgsnapshots/%s" % str(cgsnapshot_id) | |
| 113 | resp, body = self.get(url) | |
| 114 | body = json.loads(body) | |
| 115 | self.expected_success(http_client.OK, resp.status) | |
| 116 | return rest_client.ResponseBody(resp, body) | |
| 117 | ||
| 118 | def list_cgsnapshots(self, detail=False): | |
| 119 | """Information for all the tenant's consistency group snapshotss.""" | |
| 120 | url = "cgsnapshots" | |
| 121 | if detail: | |
| 122 | url += "/detail" | |
| 123 | resp, body = self.get(url) | |
| 124 | body = json.loads(body) | |
| 125 | self.expected_success(http_client.OK, resp.status) | |
| 126 | return rest_client.ResponseBody(resp, body) | |
| 127 | ||
| 128 | def wait_for_consistencygroup_status(self, cg_id, status): | |
| 129 | """Waits for a consistency group to reach a given status.""" | |
| 130 | body = self.show_consistencygroup(cg_id)['consistencygroup'] | |
| 131 | cg_status = body['status'] | |
| 132 | start = int(time.time()) | |
| 133 | ||
| 134 | while cg_status != status: | |
| 135 | time.sleep(self.build_interval) | |
| 136 | body = self.show_consistencygroup(cg_id)['consistencygroup'] | |
| 137 | cg_status = body['status'] | |
| 138 | if cg_status == 'error': | |
| 139 | raise exceptions.ConsistencyGroupException(cg_id=cg_id) | |
| 140 | ||
| 141 | if int(time.time()) - start >= self.build_timeout: | |
| 142 | message = ('Consistency group %s failed to reach %s status ' | |
| 143 | '(current %s) within the required time (%s s).' % | |
| 144 | (cg_id, status, cg_status, | |
| 145 | self.build_timeout)) | |
| 146 | raise exceptions.TimeoutException(message) | |
| 147 | ||
| 148 | def wait_for_consistencygroup_deletion(self, cg_id): | |
| 149 | """Waits for consistency group deletion""" | |
| 150 | start_time = int(time.time()) | |
| 151 | while True: | |
| 152 | try: | |
| 153 | self.show_consistencygroup(cg_id) | |
| 154 | except lib_exc.NotFound: | |
| 155 | return | |
| 156 | if int(time.time()) - start_time >= self.build_timeout: | |
| 157 | raise exceptions.TimeoutException | |
| 158 | time.sleep(self.build_interval) | |
| 159 | ||
| 160 | def wait_for_cgsnapshot_status(self, cgsnapshot_id, status): | |
| 161 | """Waits for a consistency group snapshot to reach a given status.""" | |
| 162 | body = self.show_cgsnapshot(cgsnapshot_id)['cgsnapshot'] | |
| 163 | cgsnapshot_status = body['status'] | |
| 164 | start = int(time.time()) | |
| 165 | ||
| 166 | while cgsnapshot_status != status: | |
| 167 | time.sleep(self.build_interval) | |
| 168 | body = self.show_cgsnapshot(cgsnapshot_id)['cgsnapshot'] | |
| 169 | cgsnapshot_status = body['status'] | |
| 170 | if cgsnapshot_status == 'error': | |
| 171 | raise exceptions.ConsistencyGroupSnapshotException( | |
| 172 | cgsnapshot_id=cgsnapshot_id) | |
| 173 | ||
| 174 | if int(time.time()) - start >= self.build_timeout: | |
| 175 | message = ('Consistency group snapshot %s failed to reach ' | |
| 176 | '%s status (current %s) within the required time ' | |
| 177 | '(%s s).' % | |
| 178 | (cgsnapshot_id, status, cgsnapshot_status, | |
| 179 | self.build_timeout)) | |
| 180 | raise exceptions.TimeoutException(message) | |
| 181 | ||
| 182 | def wait_for_cgsnapshot_deletion(self, cgsnapshot_id): | |
| 183 | """Waits for consistency group snapshot deletion""" | |
| 184 | start_time = int(time.time()) | |
| 185 | while True: | |
| 186 | try: | |
| 187 | self.show_cgsnapshot(cgsnapshot_id) | |
| 188 | except lib_exc.NotFound: | |
| 189 | return | |
| 190 | if int(time.time()) - start_time >= self.build_timeout: | |
| 191 | raise exceptions.TimeoutException | |
| 192 | time.sleep(self.build_interval) |
| 0 | # Copyright (C) 2017 Huawei. | |
| 1 | # All Rights Reserved. | |
| 2 | # | |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 4 | # not use this file except in compliance with the License. You may obtain | |
| 5 | # a copy of the License at | |
| 6 | # | |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | # | |
| 9 | # Unless required by applicable law or agreed to in writing, software | |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 12 | # License for the specific language governing permissions and limitations | |
| 13 | # under the License. | |
| 14 | ||
| 15 | from oslo_serialization import jsonutils as json | |
| 16 | from tempest.lib.common import rest_client | |
| 17 | from tempest.lib.services.volume import base_client | |
| 18 | ||
| 19 | ||
| 20 | class VolumeRevertClient(base_client.BaseClient): | |
| 21 | """Client class to send revert to snapshot action API request""" | |
| 22 | ||
| 23 | def __init__(self, auth_provider, service, region, **kwargs): | |
| 24 | super(VolumeRevertClient, self).__init__( | |
| 25 | auth_provider, service, region, **kwargs) | |
| 26 | ||
| 27 | def revert_to_snapshot(self, volume, snapshot_id): | |
| 28 | """Revert a volume to snapshot.""" | |
| 29 | post_body = {'snapshot_id': snapshot_id} | |
| 30 | post_body = json.dumps({'revert': post_body}) | |
| 31 | resp, body = self.post('volumes/%s/action' % volume['id'], | |
| 32 | post_body) | |
| 33 | return rest_client.ResponseBody(resp, body) |
| 0 | # Copyright 2017 NEC Corporation. | |
| 1 | # All Rights Reserved. | |
| 2 | # | |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 4 | # not use this file except in compliance with the License. You may obtain | |
| 5 | # a copy of the License at | |
| 6 | # | |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | # | |
| 9 | # Unless required by applicable law or agreed to in writing, software | |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 12 | # License for the specific language governing permissions and limitations | |
| 13 | # under the License. | |
| 14 | ||
| 15 | from tempest.api.volume import api_microversion_fixture | |
| 16 | from tempest.common import compute | |
| 17 | from tempest.common import waiters | |
| 18 | from tempest import config | |
| 19 | from tempest.lib.common import api_version_utils | |
| 20 | from tempest.lib.common.utils import data_utils | |
| 21 | from tempest.lib.common.utils import test_utils | |
| 22 | from tempest.lib import exceptions | |
| 23 | from tempest import test | |
| 24 | ||
| 25 | CONF = config.CONF | |
| 26 | ||
| 27 | ||
| 28 | class BaseVolumeTest(api_version_utils.BaseMicroversionTest, | |
| 29 | test.BaseTestCase): | |
| 30 | """Base test case class for all Cinder API tests.""" | |
| 31 | ||
| 32 | _api_version = 2 | |
| 33 | credentials = ['primary'] | |
| 34 | ||
| 35 | @classmethod | |
| 36 | def skip_checks(cls): | |
| 37 | super(BaseVolumeTest, cls).skip_checks() | |
| 38 | ||
| 39 | if not CONF.service_available.cinder: | |
| 40 | skip_msg = ("%s skipped as Cinder is not available" % cls.__name__) | |
| 41 | raise cls.skipException(skip_msg) | |
| 42 | if cls._api_version == 2: | |
| 43 | if not CONF.volume_feature_enabled.api_v2: | |
| 44 | msg = "Volume API v2 is disabled" | |
| 45 | raise cls.skipException(msg) | |
| 46 | elif cls._api_version == 3: | |
| 47 | if not CONF.volume_feature_enabled.api_v3: | |
| 48 | msg = "Volume API v3 is disabled" | |
| 49 | raise cls.skipException(msg) | |
| 50 | else: | |
| 51 | msg = ("Invalid Cinder API version (%s)" % cls._api_version) | |
| 52 | raise exceptions.InvalidConfiguration(msg) | |
| 53 | ||
| 54 | api_version_utils.check_skip_with_microversion( | |
| 55 | cls.min_microversion, cls.max_microversion, | |
| 56 | CONF.volume.min_microversion, CONF.volume.max_microversion) | |
| 57 | ||
| 58 | @classmethod | |
| 59 | def setup_clients(cls): | |
| 60 | super(BaseVolumeTest, cls).setup_clients() | |
| 61 | if cls._api_version == 3: | |
| 62 | cls.backups_client = cls.os_primary.backups_v3_client | |
| 63 | cls.volumes_client = cls.os_primary.volumes_v3_client | |
| 64 | else: | |
| 65 | cls.backups_client = cls.os_primary.backups_v2_client | |
| 66 | cls.volumes_client = cls.os_primary.volumes_v2_client | |
| 67 | ||
| 68 | cls.snapshots_client = cls.os_primary.snapshots_v2_client | |
| 69 | ||
| 70 | @classmethod | |
| 71 | def setup_credentials(cls): | |
| 72 | cls.set_network_resources() | |
| 73 | super(BaseVolumeTest, cls).setup_credentials() | |
| 74 | ||
| 75 | def setUp(self): | |
| 76 | super(BaseVolumeTest, self).setUp() | |
| 77 | self.useFixture(api_microversion_fixture.APIMicroversionFixture( | |
| 78 | self.request_microversion)) | |
| 79 | ||
| 80 | @classmethod | |
| 81 | def resource_setup(cls): | |
| 82 | super(BaseVolumeTest, cls).resource_setup() | |
| 83 | cls.request_microversion = ( | |
| 84 | api_version_utils.select_request_microversion( | |
| 85 | cls.min_microversion, | |
| 86 | CONF.volume.min_microversion)) | |
| 87 | ||
| 88 | @classmethod | |
| 89 | def create_volume(cls, wait_until='available', **kwargs): | |
| 90 | """Wrapper utility that returns a test volume. | |
| 91 | ||
| 92 | :param wait_until: wait till volume status. | |
| 93 | """ | |
| 94 | if 'size' not in kwargs: | |
| 95 | kwargs['size'] = CONF.volume.volume_size | |
| 96 | ||
| 97 | if 'imageRef' in kwargs: | |
| 98 | image = cls.os_primary.image_client_v2.show_image( | |
| 99 | kwargs['imageRef']) | |
| 100 | min_disk = image['min_disk'] | |
| 101 | kwargs['size'] = max(kwargs['size'], min_disk) | |
| 102 | ||
| 103 | if 'name' not in kwargs: | |
| 104 | name = data_utils.rand_name(cls.__name__ + '-Volume') | |
| 105 | kwargs['name'] = name | |
| 106 | ||
| 107 | volume = cls.volumes_client.create_volume(**kwargs)['volume'] | |
| 108 | cls.addClassResourceCleanup( | |
| 109 | cls.volumes_client.wait_for_resource_deletion, volume['id']) | |
| 110 | cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, | |
| 111 | cls.volumes_client.delete_volume, | |
| 112 | volume['id']) | |
| 113 | waiters.wait_for_volume_resource_status(cls.volumes_client, | |
| 114 | volume['id'], wait_until) | |
| 115 | return volume | |
| 116 | ||
| 117 | @classmethod | |
| 118 | def create_snapshot(cls, volume_id=1, **kwargs): | |
| 119 | """Wrapper utility that returns a test snapshot.""" | |
| 120 | if 'name' not in kwargs: | |
| 121 | name = data_utils.rand_name(cls.__name__ + '-Snapshot') | |
| 122 | kwargs['name'] = name | |
| 123 | ||
| 124 | snapshot = cls.snapshots_client.create_snapshot( | |
| 125 | volume_id=volume_id, **kwargs)['snapshot'] | |
| 126 | cls.addClassResourceCleanup( | |
| 127 | cls.snapshots_client.wait_for_resource_deletion, snapshot['id']) | |
| 128 | cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, | |
| 129 | cls.snapshots_client.delete_snapshot, | |
| 130 | snapshot['id']) | |
| 131 | waiters.wait_for_volume_resource_status(cls.snapshots_client, | |
| 132 | snapshot['id'], 'available') | |
| 133 | return snapshot | |
| 134 | ||
| 135 | def create_backup(self, volume_id, backup_client=None, **kwargs): | |
| 136 | """Wrapper utility that returns a test backup.""" | |
| 137 | if backup_client is None: | |
| 138 | backup_client = self.backups_client | |
| 139 | if 'name' not in kwargs: | |
| 140 | name = data_utils.rand_name(self.__class__.__name__ + '-Backup') | |
| 141 | kwargs['name'] = name | |
| 142 | ||
| 143 | backup = backup_client.create_backup( | |
| 144 | volume_id=volume_id, **kwargs)['backup'] | |
| 145 | self.addCleanup(backup_client.delete_backup, backup['id']) | |
| 146 | waiters.wait_for_volume_resource_status(backup_client, backup['id'], | |
| 147 | 'available') | |
| 148 | return backup | |
| 149 | ||
| 150 | def create_server(self, wait_until='ACTIVE', **kwargs): | |
| 151 | name = kwargs.pop( | |
| 152 | 'name', | |
| 153 | data_utils.rand_name(self.__class__.__name__ + '-instance')) | |
| 154 | ||
| 155 | tenant_network = self.get_tenant_network() | |
| 156 | body, _ = compute.create_test_server( | |
| 157 | self.os_primary, | |
| 158 | tenant_network=tenant_network, | |
| 159 | name=name, | |
| 160 | wait_until=wait_until, | |
| 161 | **kwargs) | |
| 162 | ||
| 163 | self.addCleanup(test_utils.call_and_ignore_notfound_exc, | |
| 164 | waiters.wait_for_server_termination, | |
| 165 | self.os_primary.servers_client, body['id']) | |
| 166 | self.addCleanup(test_utils.call_and_ignore_notfound_exc, | |
| 167 | self.os_primary.servers_client.delete_server, | |
| 168 | body['id']) | |
| 169 | return body |
| 0 | # Copyright (C) 2015 EMC Corporation. | |
| 1 | # Copyright (C) 2016 Pure Storage, Inc. | |
| 2 | # All Rights Reserved. | |
| 3 | # | |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 5 | # not use this file except in compliance with the License. You may obtain | |
| 6 | # a copy of the License at | |
| 7 | # | |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 9 | # | |
| 10 | # Unless required by applicable law or agreed to in writing, software | |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 13 | # License for the specific language governing permissions and limitations | |
| 14 | # under the License. | |
| 15 | ||
| 16 | from tempest.common import waiters | |
| 17 | from tempest import config | |
| 18 | from tempest.lib.common.utils import data_utils | |
| 19 | from tempest.lib import decorators | |
| 20 | ||
| 21 | from cinder_tempest_plugin.api.volume import base | |
| 22 | from cinder_tempest_plugin import cinder_clients | |
| 23 | ||
| 24 | CONF = config.CONF | |
| 25 | ||
| 26 | ||
| 27 | class ConsistencyGroupsV2Test(base.BaseVolumeTest): | |
| 28 | @classmethod | |
| 29 | def setup_clients(cls): | |
| 30 | cls._api_version = 2 | |
| 31 | super(ConsistencyGroupsV2Test, cls).setup_clients() | |
| 32 | cls.admin_volume_client = cls.os_admin.volumes_v2_client | |
| 33 | ||
| 34 | manager = cinder_clients.Manager(cls.os_admin) | |
| 35 | cls.consistencygroups_adm_client = manager.consistencygroups_adm_client | |
| 36 | ||
| 37 | @classmethod | |
| 38 | def skip_checks(cls): | |
| 39 | super(ConsistencyGroupsV2Test, cls).skip_checks() | |
| 40 | if not CONF.volume_feature_enabled.consistency_group: | |
| 41 | raise cls.skipException("Cinder consistency group " | |
| 42 | "feature disabled") | |
| 43 | ||
| 44 | def _delete_consistencygroup(self, cg_id): | |
| 45 | self.consistencygroups_adm_client.delete_consistencygroup(cg_id) | |
| 46 | vols = self.admin_volume_client.list_volumes(detail=True)['volumes'] | |
| 47 | for vol in vols: | |
| 48 | if vol['consistencygroup_id'] == cg_id: | |
| 49 | self.admin_volume_client.wait_for_resource_deletion(vol['id']) | |
| 50 | self.consistencygroups_adm_client.wait_for_consistencygroup_deletion( | |
| 51 | cg_id) | |
| 52 | ||
| 53 | def _delete_cgsnapshot(self, cgsnapshot_id, cg_id): | |
| 54 | self.consistencygroups_adm_client.delete_cgsnapshot(cgsnapshot_id) | |
| 55 | vols = self.admin_volume_client.list_volumes(detail=True)['volumes'] | |
| 56 | snapshots = self.os_admin.snapshots_v2_client.list_snapshots( | |
| 57 | detail=True)['snapshots'] | |
| 58 | for vol in vols: | |
| 59 | for snap in snapshots: | |
| 60 | if (vol['consistencygroup_id'] == cg_id and | |
| 61 | vol['id'] == snap['volume_id']): | |
| 62 | (self.snapshots_client. | |
| 63 | wait_for_resource_deletion(snap['id'])) | |
| 64 | self.consistencygroups_adm_client.wait_for_cgsnapshot_deletion( | |
| 65 | cgsnapshot_id) | |
| 66 | ||
| 67 | @decorators.idempotent_id('3fe776ba-ec1f-4e6c-8d78-4b14c3a7fc44') | |
| 68 | def test_consistencygroup_create_delete(self): | |
| 69 | # Create volume type | |
| 70 | name = data_utils.rand_name("volume-type") | |
| 71 | volume_type = self.os_admin.volume_types_v2_client.create_volume_type( | |
| 72 | name=name)['volume_type'] | |
| 73 | ||
| 74 | # Create CG | |
| 75 | cg_name = data_utils.rand_name('CG') | |
| 76 | create_consistencygroup = ( | |
| 77 | self.consistencygroups_adm_client.create_consistencygroup) | |
| 78 | cg = create_consistencygroup(volume_type['id'], | |
| 79 | name=cg_name)['consistencygroup'] | |
| 80 | vol_name = data_utils.rand_name("volume") | |
| 81 | params = {'name': vol_name, | |
| 82 | 'volume_type': volume_type['id'], | |
| 83 | 'consistencygroup_id': cg['id'], | |
| 84 | 'size': CONF.volume.volume_size} | |
| 85 | ||
| 86 | # Create volume | |
| 87 | volume = self.admin_volume_client.create_volume(**params)['volume'] | |
| 88 | ||
| 89 | waiters.wait_for_volume_resource_status(self.admin_volume_client, | |
| 90 | volume['id'], 'available') | |
| 91 | self.consistencygroups_adm_client.wait_for_consistencygroup_status( | |
| 92 | cg['id'], 'available') | |
| 93 | self.assertEqual(cg_name, cg['name']) | |
| 94 | ||
| 95 | # Get a given CG | |
| 96 | cg = self.consistencygroups_adm_client.show_consistencygroup( | |
| 97 | cg['id'])['consistencygroup'] | |
| 98 | self.assertEqual(cg_name, cg['name']) | |
| 99 | ||
| 100 | # Get all CGs with detail | |
| 101 | cgs = self.consistencygroups_adm_client.list_consistencygroups( | |
| 102 | detail=True)['consistencygroups'] | |
| 103 | self.assertIn((cg['name'], cg['id']), | |
| 104 | [(m['name'], m['id']) for m in cgs]) | |
| 105 | ||
| 106 | # Clean up | |
| 107 | self._delete_consistencygroup(cg['id']) | |
| 108 | self.os_admin.volume_types_v2_client.delete_volume_type( | |
| 109 | volume_type['id']) | |
| 110 | ||
| 111 | @decorators.idempotent_id('2134dd52-f333-4456-bb05-6cb0f009a44f') | |
| 112 | def test_consistencygroup_cgsnapshot_create_delete(self): | |
| 113 | # Create volume type | |
| 114 | name = data_utils.rand_name("volume-type") | |
| 115 | volume_type = self.admin_volume_types_client.create_volume_type( | |
| 116 | name=name)['volume_type'] | |
| 117 | ||
| 118 | # Create CG | |
| 119 | cg_name = data_utils.rand_name('CG') | |
| 120 | create_consistencygroup = ( | |
| 121 | self.consistencygroups_adm_client.create_consistencygroup) | |
| 122 | cg = create_consistencygroup(volume_type['id'], | |
| 123 | name=cg_name)['consistencygroup'] | |
| 124 | vol_name = data_utils.rand_name("volume") | |
| 125 | params = {'name': vol_name, | |
| 126 | 'volume_type': volume_type['id'], | |
| 127 | 'consistencygroup_id': cg['id'], | |
| 128 | 'size': CONF.volume.volume_size} | |
| 129 | ||
| 130 | # Create volume | |
| 131 | volume = self.admin_volume_client.create_volume(**params)['volume'] | |
| 132 | waiters.wait_for_volume_resource_status(self.admin_volume_client, | |
| 133 | volume['id'], 'available') | |
| 134 | self.consistencygroups_adm_client.wait_for_consistencygroup_status( | |
| 135 | cg['id'], 'available') | |
| 136 | self.assertEqual(cg_name, cg['name']) | |
| 137 | ||
| 138 | # Create cgsnapshot | |
| 139 | cgsnapshot_name = data_utils.rand_name('cgsnapshot') | |
| 140 | create_cgsnapshot = ( | |
| 141 | self.consistencygroups_adm_client.create_cgsnapshot) | |
| 142 | cgsnapshot = create_cgsnapshot(cg['id'], | |
| 143 | name=cgsnapshot_name)['cgsnapshot'] | |
| 144 | snapshots = self.os_admin.snapshots_v2_client.list_snapshots( | |
| 145 | detail=True)['snapshots'] | |
| 146 | for snap in snapshots: | |
| 147 | if volume['id'] == snap['volume_id']: | |
| 148 | waiters.wait_for_volume_resource_status( | |
| 149 | self.os_admin.snapshots_v2_client, | |
| 150 | snap['id'], 'available') | |
| 151 | self.consistencygroups_adm_client.wait_for_cgsnapshot_status( | |
| 152 | cgsnapshot['id'], 'available') | |
| 153 | self.assertEqual(cgsnapshot_name, cgsnapshot['name']) | |
| 154 | ||
| 155 | # Get a given CG snapshot | |
| 156 | cgsnapshot = self.consistencygroups_adm_client.show_cgsnapshot( | |
| 157 | cgsnapshot['id'])['cgsnapshot'] | |
| 158 | self.assertEqual(cgsnapshot_name, cgsnapshot['name']) | |
| 159 | ||
| 160 | # Get all CG snapshots with detail | |
| 161 | cgsnapshots = self.consistencygroups_adm_client.list_cgsnapshots( | |
| 162 | detail=True)['cgsnapshots'] | |
| 163 | self.assertIn((cgsnapshot['name'], cgsnapshot['id']), | |
| 164 | [(m['name'], m['id']) for m in cgsnapshots]) | |
| 165 | ||
| 166 | # Clean up | |
| 167 | self._delete_cgsnapshot(cgsnapshot['id'], cg['id']) | |
| 168 | self._delete_consistencygroup(cg['id']) | |
| 169 | self.admin_volume_types_client.delete_volume_type(volume_type['id']) | |
| 170 | ||
| 171 | @decorators.idempotent_id('3a6a5525-25ca-4a6c-aac4-cac6fa8f5b43') | |
| 172 | def test_create_consistencygroup_from_cgsnapshot(self): | |
| 173 | # Create volume type | |
| 174 | name = data_utils.rand_name("volume-type") | |
| 175 | volume_type = self.admin_volume_types_client.create_volume_type( | |
| 176 | name=name)['volume_type'] | |
| 177 | ||
| 178 | # Create CG | |
| 179 | cg_name = data_utils.rand_name('CG') | |
| 180 | create_consistencygroup = ( | |
| 181 | self.consistencygroups_adm_client.create_consistencygroup) | |
| 182 | cg = create_consistencygroup(volume_type['id'], | |
| 183 | name=cg_name)['consistencygroup'] | |
| 184 | vol_name = data_utils.rand_name("volume") | |
| 185 | params = {'name': vol_name, | |
| 186 | 'volume_type': volume_type['id'], | |
| 187 | 'consistencygroup_id': cg['id'], | |
| 188 | 'size': CONF.volume.volume_size} | |
| 189 | ||
| 190 | # Create volume | |
| 191 | volume = self.admin_volume_client.create_volume(**params)['volume'] | |
| 192 | waiters.wait_for_volume_resource_status(self.admin_volume_client, | |
| 193 | volume['id'], 'available') | |
| 194 | self.consistencygroups_adm_client.wait_for_consistencygroup_status( | |
| 195 | cg['id'], 'available') | |
| 196 | self.assertEqual(cg_name, cg['name']) | |
| 197 | ||
| 198 | # Create cgsnapshot | |
| 199 | cgsnapshot_name = data_utils.rand_name('cgsnapshot') | |
| 200 | create_cgsnapshot = ( | |
| 201 | self.consistencygroups_adm_client.create_cgsnapshot) | |
| 202 | cgsnapshot = create_cgsnapshot(cg['id'], | |
| 203 | name=cgsnapshot_name)['cgsnapshot'] | |
| 204 | snapshots = self.snapshots_client.list_snapshots( | |
| 205 | detail=True)['snapshots'] | |
| 206 | for snap in snapshots: | |
| 207 | if volume['id'] == snap['volume_id']: | |
| 208 | waiters.wait_for_volume_resource_status( | |
| 209 | self.os_admin.snapshots_v2_client, snap['id'], 'available') | |
| 210 | self.consistencygroups_adm_client.wait_for_cgsnapshot_status( | |
| 211 | cgsnapshot['id'], 'available') | |
| 212 | self.assertEqual(cgsnapshot_name, cgsnapshot['name']) | |
| 213 | ||
| 214 | # Create CG from CG snapshot | |
| 215 | cg_name2 = data_utils.rand_name('CG_from_snap') | |
| 216 | create_consistencygroup2 = ( | |
| 217 | self.consistencygroups_adm_client.create_consistencygroup_from_src) | |
| 218 | cg2 = create_consistencygroup2(cgsnapshot_id=cgsnapshot['id'], | |
| 219 | name=cg_name2)['consistencygroup'] | |
| 220 | vols = self.admin_volume_client.list_volumes( | |
| 221 | detail=True)['volumes'] | |
| 222 | for vol in vols: | |
| 223 | if vol['consistencygroup_id'] == cg2['id']: | |
| 224 | waiters.wait_for_volume_resource_status( | |
| 225 | self.admin_volume_client, vol['id'], 'available') | |
| 226 | self.consistencygroups_adm_client.wait_for_consistencygroup_status( | |
| 227 | cg2['id'], 'available') | |
| 228 | self.assertEqual(cg_name2, cg2['name']) | |
| 229 | ||
| 230 | # Clean up | |
| 231 | self._delete_consistencygroup(cg2['id']) | |
| 232 | self._delete_cgsnapshot(cgsnapshot['id'], cg['id']) | |
| 233 | self._delete_consistencygroup(cg['id']) | |
| 234 | self.admin_volume_types_client.delete_volume_type(volume_type['id']) | |
| 235 | ||
| 236 | @decorators.idempotent_id('556121ae-de9c-4342-9897-e54260447a19') | |
| 237 | def test_create_consistencygroup_from_consistencygroup(self): | |
| 238 | # Create volume type | |
| 239 | name = data_utils.rand_name("volume-type") | |
| 240 | volume_type = self.admin_volume_types_client.create_volume_type( | |
| 241 | name=name)['volume_type'] | |
| 242 | ||
| 243 | # Create CG | |
| 244 | cg_name = data_utils.rand_name('CG') | |
| 245 | create_consistencygroup = ( | |
| 246 | self.consistencygroups_adm_client.create_consistencygroup) | |
| 247 | cg = create_consistencygroup(volume_type['id'], | |
| 248 | name=cg_name)['consistencygroup'] | |
| 249 | vol_name = data_utils.rand_name("volume") | |
| 250 | params = {'name': vol_name, | |
| 251 | 'volume_type': volume_type['id'], | |
| 252 | 'consistencygroup_id': cg['id'], | |
| 253 | 'size': CONF.volume.volume_size} | |
| 254 | ||
| 255 | # Create volume | |
| 256 | volume = self.admin_volume_client.create_volume(**params)['volume'] | |
| 257 | waiters.wait_for_volume_resource_status(self.admin_volume_client, | |
| 258 | volume['id'], 'available') | |
| 259 | self.consistencygroups_adm_client.wait_for_consistencygroup_status( | |
| 260 | cg['id'], 'available') | |
| 261 | self.assertEqual(cg_name, cg['name']) | |
| 262 | ||
| 263 | # Create CG from CG | |
| 264 | cg_name2 = data_utils.rand_name('CG_from_cg') | |
| 265 | create_consistencygroup2 = ( | |
| 266 | self.consistencygroups_adm_client.create_consistencygroup_from_src) | |
| 267 | cg2 = create_consistencygroup2(source_cgid=cg['id'], | |
| 268 | name=cg_name2)['consistencygroup'] | |
| 269 | vols = self.admin_volume_client.list_volumes( | |
| 270 | detail=True)['volumes'] | |
| 271 | for vol in vols: | |
| 272 | if vol['consistencygroup_id'] == cg2['id']: | |
| 273 | waiters.wait_for_volume_resource_status( | |
| 274 | self.admin_volume_client, vol['id'], 'available') | |
| 275 | self.consistencygroups_adm_client.wait_for_consistencygroup_status( | |
| 276 | cg2['id'], 'available') | |
| 277 | self.assertEqual(cg_name2, cg2['name']) | |
| 278 | ||
| 279 | # Clean up | |
| 280 | self._delete_consistencygroup(cg2['id']) | |
| 281 | self._delete_consistencygroup(cg['id']) | |
| 282 | self.admin_volume_types_client.delete_volume_type(volume_type['id']) |
| 0 | # Copyright (c) 2016 Mirantis Inc. | |
| 1 | # All Rights Reserved. | |
| 2 | # | |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 4 | # not use this file except in compliance with the License. You may obtain | |
| 5 | # a copy of the License at | |
| 6 | # | |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | # | |
| 9 | # Unless required by applicable law or agreed to in writing, software | |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 12 | # License for the specific language governing permissions and limitations | |
| 13 | # under the License. | |
| 14 | ||
| 15 | from tempest.common import waiters | |
| 16 | from tempest import config | |
| 17 | from tempest.lib.common.utils import data_utils | |
| 18 | from tempest.lib import decorators | |
| 19 | ||
| 20 | from cinder_tempest_plugin.api.volume import base | |
| 21 | ||
| 22 | CONF = config.CONF | |
| 23 | ||
| 24 | ||
| 25 | class VolumesBackupsTest(base.BaseVolumeTest): | |
| 26 | ||
| 27 | @classmethod | |
| 28 | def skip_checks(cls): | |
| 29 | super(VolumesBackupsTest, cls).skip_checks() | |
| 30 | if not CONF.volume_feature_enabled.backup: | |
| 31 | raise cls.skipException("Cinder backup feature disabled") | |
| 32 | ||
| 33 | @decorators.idempotent_id('885410c6-cd1d-452c-a409-7c32b7e0be15') | |
| 34 | def test_volume_snapshot_backup(self): | |
| 35 | """Create backup from snapshot.""" | |
| 36 | volume = self.create_volume() | |
| 37 | # Create snapshot | |
| 38 | snapshot = self.create_snapshot(volume['id']) | |
| 39 | # Create backup | |
| 40 | backup = self.create_backup( | |
| 41 | volume_id=volume['id'], | |
| 42 | snapshot_id=snapshot['id']) | |
| 43 | # Get a given backup | |
| 44 | backup = self.backups_client.show_backup( | |
| 45 | backup['id'])['backup'] | |
| 46 | waiters.wait_for_volume_resource_status( | |
| 47 | self.backups_client, | |
| 48 | backup['id'], 'available') | |
| 49 | self.assertEqual(volume['id'], backup['volume_id']) | |
| 50 | self.assertEqual(snapshot['id'], backup['snapshot_id']) | |
| 51 | ||
| 52 | self.snapshots_client.delete_snapshot(snapshot['id']) | |
| 53 | self.snapshots_client.wait_for_resource_deletion(snapshot['id']) | |
| 54 | ||
| 55 | self.volumes_client.delete_volume(volume['id']) | |
| 56 | self.volumes_client.wait_for_resource_deletion(volume['id']) | |
| 57 | ||
| 58 | @decorators.idempotent_id('b5d837b0-7066-455d-88fc-4a721a899306') | |
| 59 | def test_backup_create_and_restore_to_an_existing_volume(self): | |
| 60 | """Test backup create and restore to an existing volume.""" | |
| 61 | # Create volume | |
| 62 | src_vol = self.create_volume() | |
| 63 | self.addCleanup(self.volumes_client.delete_volume, | |
| 64 | src_vol['id']) | |
| 65 | # Create backup | |
| 66 | backup = self.backups_client.create_backup( | |
| 67 | volume_id=src_vol['id'])['backup'] | |
| 68 | self.addCleanup(self.backups_client.delete_backup, backup['id']) | |
| 69 | waiters.wait_for_volume_resource_status( | |
| 70 | self.backups_client, | |
| 71 | backup['id'], 'available') | |
| 72 | # Restore to existing volume | |
| 73 | restore = self.backups_client.restore_backup( | |
| 74 | backup_id=backup['id'], | |
| 75 | volume_id=src_vol['id'])['restore'] | |
| 76 | waiters.wait_for_volume_resource_status( | |
| 77 | self.backups_client, | |
| 78 | backup['id'], 'available') | |
| 79 | waiters.wait_for_volume_resource_status( | |
| 80 | self.volumes_client, | |
| 81 | src_vol['id'], 'available') | |
| 82 | self.assertEqual(src_vol['id'], restore['volume_id']) | |
| 83 | self.assertEqual(backup['id'], restore['backup_id']) | |
| 84 | ||
| 85 | @decorators.idempotent_id('c810fe2c-cb40-43ab-96aa-471b74516a98') | |
| 86 | def test_incremental_backup(self): | |
| 87 | """Test create incremental backup.""" | |
| 88 | # Create volume from image | |
| 89 | volume = self.create_volume(size=CONF.volume.volume_size, | |
| 90 | imageRef=CONF.compute.image_ref) | |
| 91 | self.addCleanup(self.volumes_client.delete_volume, | |
| 92 | volume['id']) | |
| 93 | ||
| 94 | # Create backup | |
| 95 | backup = self.backups_client.create_backup( | |
| 96 | volume_id=volume['id'])['backup'] | |
| 97 | waiters.wait_for_volume_resource_status(self.backups_client, | |
| 98 | backup['id'], 'available') | |
| 99 | # Create a server | |
| 100 | bd_map = [{'volume_id': volume['id'], | |
| 101 | 'delete_on_termination': '0'}] | |
| 102 | ||
| 103 | server_name = data_utils.rand_name('instance') | |
| 104 | server = self.create_server( | |
| 105 | name=server_name, | |
| 106 | block_device_mapping=bd_map, | |
| 107 | wait_until='ACTIVE') | |
| 108 | ||
| 109 | # Delete VM | |
| 110 | self.os_primary.servers_client.delete_server(server['id']) | |
| 111 | # Create incremental backup | |
| 112 | waiters.wait_for_volume_resource_status(self.volumes_client, | |
| 113 | volume['id'], 'available') | |
| 114 | backup_incr = self.backups_client.create_backup( | |
| 115 | volume_id=volume['id'], | |
| 116 | incremental=True)['backup'] | |
| 117 | ||
| 118 | waiters.wait_for_volume_resource_status(self.backups_client, | |
| 119 | backup_incr['id'], | |
| 120 | 'available') | |
| 121 | ||
| 122 | is_incremental = self.backups_client.show_backup( | |
| 123 | backup_incr['id'])['backup']['is_incremental'] | |
| 124 | self.assertTrue(is_incremental) | |
| 125 | ||
| 126 | self.backups_client.delete_backup(backup_incr['id']) | |
| 127 | self.backups_client.wait_for_resource_deletion(backup_incr['id']) | |
| 128 | self.backups_client.delete_backup(backup['id']) | |
| 129 | self.backups_client.wait_for_resource_deletion(backup['id']) |
| 0 | # Copyright (c) 2017 Huawei. | |
| 1 | # All Rights Reserved. | |
| 2 | # | |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 4 | # not use this file except in compliance with the License. You may obtain | |
| 5 | # a copy of the License at | |
| 6 | # | |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | # | |
| 9 | # Unless required by applicable law or agreed to in writing, software | |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 12 | # License for the specific language governing permissions and limitations | |
| 13 | # under the License. | |
| 14 | ||
| 15 | from tempest.common import waiters | |
| 16 | from tempest import config | |
| 17 | from tempest.lib import decorators | |
| 18 | ||
| 19 | from cinder_tempest_plugin.api.volume import base | |
| 20 | from cinder_tempest_plugin import cinder_clients | |
| 21 | ||
| 22 | CONF = config.CONF | |
| 23 | ||
| 24 | ||
| 25 | class VolumeRevertTests(base.BaseVolumeTest): | |
| 26 | min_microversion = '3.40' | |
| 27 | ||
| 28 | @classmethod | |
| 29 | def skip_checks(cls): | |
| 30 | super(VolumeRevertTests, cls).skip_checks() | |
| 31 | if not CONF.volume_feature_enabled.volume_revert: | |
| 32 | raise cls.skipException("Cinder volume revert feature disabled") | |
| 33 | ||
| 34 | @classmethod | |
| 35 | def setup_clients(cls): | |
| 36 | cls._api_version = 3 | |
| 37 | super(VolumeRevertTests, cls).setup_clients() | |
| 38 | ||
| 39 | manager = cinder_clients.Manager(cls.os_primary) | |
| 40 | cls.volume_revert_client = manager.volume_revert_client | |
| 41 | ||
| 42 | def setUp(self): | |
| 43 | super(VolumeRevertTests, self).setUp() | |
| 44 | # Create volume | |
| 45 | self.volume = self.create_volume(size=1) | |
| 46 | # Create snapshot | |
| 47 | self.snapshot = self.create_snapshot(self.volume['id']) | |
| 48 | ||
| 49 | @decorators.idempotent_id('87b7dcb7-4950-4a3a-802c-ece55491846d') | |
| 50 | def test_volume_revert_to_snapshot(self): | |
| 51 | """Test revert to snapshot""" | |
| 52 | # Revert to snapshot | |
| 53 | self.volume_revert_client.revert_to_snapshot(self.volume, | |
| 54 | self.snapshot['id']) | |
| 55 | waiters.wait_for_volume_resource_status( | |
| 56 | self.volumes_client, | |
| 57 | self.volume['id'], 'available') | |
| 58 | waiters.wait_for_volume_resource_status( | |
| 59 | self.snapshots_client, | |
| 60 | self.snapshot['id'], 'available') | |
| 61 | volume = self.volumes_client.show_volume(self.volume['id'])['volume'] | |
| 62 | ||
| 63 | self.assertEqual(1, volume['size']) | |
| 64 | ||
| 65 | @decorators.idempotent_id('4e8b0788-87fe-430d-be7a-444d7f8e0347') | |
| 66 | def test_volume_revert_to_snapshot_after_extended(self): | |
| 67 | """Test revert to snapshot after extended""" | |
| 68 | # Extend the volume | |
| 69 | self.volumes_client.extend_volume(self.volume['id'], new_size=2) | |
| 70 | waiters.wait_for_volume_resource_status(self.volumes_client, | |
| 71 | self.volume['id'], 'available') | |
| 72 | # Revert to snapshot | |
| 73 | self.volume_revert_client.revert_to_snapshot(self.volume, | |
| 74 | self.snapshot['id']) | |
| 75 | waiters.wait_for_volume_resource_status( | |
| 76 | self.volumes_client, | |
| 77 | self.volume['id'], 'available') | |
| 78 | waiters.wait_for_volume_resource_status( | |
| 79 | self.snapshots_client, | |
| 80 | self.snapshot['id'], 'available') | |
| 81 | volume = self.volumes_client.show_volume(self.volume['id'])['volume'] | |
| 82 | self.assertEqual(2, volume['size']) |
| 0 | # -*- coding: utf-8 -*- | |
| 1 | # Copyright 2016 Red Hat, Inc. | |
| 2 | # All Rights Reserved. | |
| 3 | # | |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 5 | # not use this file except in compliance with the License. You may obtain | |
| 6 | # a copy of the License at | |
| 7 | # | |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 9 | # | |
| 10 | # Unless required by applicable law or agreed to in writing, software | |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 13 | # License for the specific language governing permissions and limitations | |
| 14 | # under the License. | |
| 15 | ||
| 16 | from tempest.common import waiters | |
| 17 | from tempest import config | |
| 18 | from tempest.lib.common.utils import data_utils | |
| 19 | from tempest.lib.common.utils import test_utils | |
| 20 | ||
| 21 | from cinder_tempest_plugin.api.volume import base | |
| 22 | ||
| 23 | CONF = config.CONF | |
| 24 | ||
| 25 | ||
| 26 | class CinderUnicodeTest(base.BaseVolumeTest): | |
| 27 | ||
| 28 | @classmethod | |
| 29 | def resource_setup(cls): | |
| 30 | super(CinderUnicodeTest, cls).resource_setup() | |
| 31 | ||
| 32 | # Stick to three-byte unicode here, since four+ byte | |
| 33 | # chars require utf8mb4 database support which may not | |
| 34 | # be configured. | |
| 35 | cls.volume_name = u"CinderUnicodeTest塵㼗‽" | |
| 36 | cls.volume = cls.create_volume_with_args(name=cls.volume_name) | |
| 37 | ||
| 38 | @classmethod | |
| 39 | def create_volume_with_args(cls, **kwargs): | |
| 40 | if 'name' not in kwargs: | |
| 41 | kwargs['name'] = data_utils.rand_name('Volume') | |
| 42 | ||
| 43 | kwargs['size'] = CONF.volume.volume_size | |
| 44 | ||
| 45 | volume = cls.volumes_client.create_volume(**kwargs)['volume'] | |
| 46 | cls.addClassResourceCleanup( | |
| 47 | cls.volumes_client.wait_for_resource_deletion, volume['id']) | |
| 48 | cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, | |
| 49 | cls.volumes_client.delete_volume, | |
| 50 | volume['id']) | |
| 51 | waiters.wait_for_volume_resource_status(cls.volumes_client, | |
| 52 | volume['id'], | |
| 53 | 'available') | |
| 54 | ||
| 55 | return volume | |
| 56 | ||
| 57 | def test_create_delete_unicode_volume_name(self): | |
| 58 | """Create a volume with a unicode name and view it.""" | |
| 59 | ||
| 60 | result = self.volumes_client.show_volume(self.volume['id']) | |
| 61 | fetched_volume = result['volume'] | |
| 62 | self.assertEqual(fetched_volume['name'], | |
| 63 | self.volume_name) |
| 0 | # Copyright (c) 2016 Pure Storage, Inc. | |
| 1 | # All Rights Reserved. | |
| 2 | # | |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 4 | # not use this file except in compliance with the License. You may obtain | |
| 5 | # a copy of the License at | |
| 6 | # | |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | # | |
| 9 | # Unless required by applicable law or agreed to in writing, software | |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 12 | # License for the specific language governing permissions and limitations | |
| 13 | # under the License. | |
| 14 | ||
| 15 | from tempest import config | |
| 16 | ||
| 17 | from cinder_tempest_plugin.services import consistencygroups_client | |
| 18 | from cinder_tempest_plugin.services import volume_revert_client | |
| 19 | ||
| 20 | CONF = config.CONF | |
| 21 | ||
| 22 | ||
| 23 | class Manager(object): | |
| 24 | def __init__(self, base_manager): | |
| 25 | params = { | |
| 26 | 'service': CONF.volume.catalog_type, | |
| 27 | 'region': CONF.volume.region or CONF.identity.region, | |
| 28 | 'endpoint_type': CONF.volume.endpoint_type, | |
| 29 | 'build_interval': CONF.volume.build_interval, | |
| 30 | 'build_timeout': CONF.volume.build_timeout | |
| 31 | } | |
| 32 | params.update(base_manager.default_params) | |
| 33 | auth_provider = base_manager.auth_provider | |
| 34 | ||
| 35 | self.consistencygroups_adm_client = ( | |
| 36 | consistencygroups_client.ConsistencyGroupsClient(auth_provider, | |
| 37 | **params)) | |
| 38 | self.volume_revert_client = ( | |
| 39 | volume_revert_client.VolumeRevertClient(auth_provider, **params)) |
| 0 | # Copyright 2016 | |
| 1 | # All Rights Reserved. | |
| 2 | # | |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 4 | # not use this file except in compliance with the License. You may obtain | |
| 5 | # a copy of the License at | |
| 6 | # | |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | # | |
| 9 | # Unless required by applicable law or agreed to in writing, software | |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 12 | # License for the specific language governing permissions and limitations | |
| 13 | # under the License. | |
| 14 | ||
| 15 | from oslo_config import cfg | |
| 16 | ||
| 17 | cinder_option = [ | |
| 18 | cfg.BoolOpt('consistency_group', | |
| 19 | default=False, | |
| 20 | help='Enable to run Cinder volume consistency group tests'), | |
| 21 | cfg.BoolOpt('volume_revert', | |
| 22 | default=False, | |
| 23 | help='Enable to run Cinder volume revert tests'), | |
| 24 | ] |
| 0 | # Copyright 2015 | |
| 1 | # All Rights Reserved. | |
| 2 | # | |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 4 | # not use this file except in compliance with the License. You may obtain | |
| 5 | # a copy of the License at | |
| 6 | # | |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | # | |
| 9 | # Unless required by applicable law or agreed to in writing, software | |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 12 | # License for the specific language governing permissions and limitations | |
| 13 | # under the License. | |
| 14 | ||
| 15 | import cinder | |
| 16 | import os | |
| 17 | ||
| 18 | from cinder_tempest_plugin import config as project_config | |
| 19 | ||
| 20 | from tempest import config | |
| 21 | from tempest.test_discover import plugins | |
| 22 | ||
| 23 | ||
| 24 | class CinderTempestPlugin(plugins.TempestPlugin): | |
| 25 | def load_tests(self): | |
| 26 | base_path = os.path.split(os.path.dirname( | |
| 27 | os.path.abspath(cinder.__file__)))[0] | |
| 28 | test_dir = "cinder_tempest_plugin" | |
| 29 | full_test_dir = os.path.join(base_path, test_dir) | |
| 30 | return full_test_dir, base_path | |
| 31 | ||
| 32 | def register_opts(self, conf): | |
| 33 | config.register_opt_group( | |
| 34 | conf, config.volume_feature_group, | |
| 35 | project_config.cinder_option | |
| 36 | ) | |
| 37 | ||
| 38 | def get_opt_lists(self): | |
| 39 | return [ | |
| 40 | (config.volume_feature_group.name, | |
| 41 | project_config.cinder_option), | |
| 42 | ] |
| 0 | # Copyright (C) 2015 EMC Corporation. | |
| 1 | # Copyright (C) 2016 Pure Storage, Inc. | |
| 2 | # All Rights Reserved. | |
| 3 | # | |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 5 | # not use this file except in compliance with the License. You may obtain | |
| 6 | # a copy of the License at | |
| 7 | # | |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 9 | # | |
| 10 | # Unless required by applicable law or agreed to in writing, software | |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 13 | # License for the specific language governing permissions and limitations | |
| 14 | # under the License. | |
| 15 | ||
| 16 | import time | |
| 17 | ||
| 18 | from oslo_serialization import jsonutils as json | |
| 19 | from six.moves import http_client | |
| 20 | from tempest import exceptions | |
| 21 | from tempest.lib.common import rest_client | |
| 22 | from tempest.lib import exceptions as lib_exc | |
| 23 | ||
| 24 | ||
| 25 | class ConsistencyGroupsClient(rest_client.RestClient): | |
| 26 | """Client class to send CRUD Volume ConsistencyGroup API requests""" | |
| 27 | ||
| 28 | def __init__(self, auth_provider, service, region, **kwargs): | |
| 29 | super(ConsistencyGroupsClient, self).__init__( | |
| 30 | auth_provider, service, region, **kwargs) | |
| 31 | ||
| 32 | def create_consistencygroup(self, volume_types, **kwargs): | |
| 33 | """Creates a consistency group.""" | |
| 34 | post_body = {'volume_types': volume_types} | |
| 35 | if kwargs.get('availability_zone'): | |
| 36 | post_body['availability_zone'] = kwargs.get('availability_zone') | |
| 37 | if kwargs.get('name'): | |
| 38 | post_body['name'] = kwargs.get('name') | |
| 39 | if kwargs.get('description'): | |
| 40 | post_body['description'] = kwargs.get('description') | |
| 41 | post_body = json.dumps({'consistencygroup': post_body}) | |
| 42 | resp, body = self.post('consistencygroups', post_body) | |
| 43 | body = json.loads(body) | |
| 44 | self.expected_success(http_client.ACCEPTED, resp.status) | |
| 45 | return rest_client.ResponseBody(resp, body) | |
| 46 | ||
| 47 | def create_consistencygroup_from_src(self, **kwargs): | |
| 48 | """Creates a consistency group from source.""" | |
| 49 | post_body = {} | |
| 50 | if kwargs.get('cgsnapshot_id'): | |
| 51 | post_body['cgsnapshot_id'] = kwargs.get('cgsnapshot_id') | |
| 52 | if kwargs.get('source_cgid'): | |
| 53 | post_body['source_cgid'] = kwargs.get('source_cgid') | |
| 54 | if kwargs.get('name'): | |
| 55 | post_body['name'] = kwargs.get('name') | |
| 56 | if kwargs.get('description'): | |
| 57 | post_body['description'] = kwargs.get('description') | |
| 58 | post_body = json.dumps({'consistencygroup-from-src': post_body}) | |
| 59 | resp, body = self.post('consistencygroups/create_from_src', post_body) | |
| 60 | body = json.loads(body) | |
| 61 | self.expected_success(http_client.ACCEPTED, resp.status) | |
| 62 | return rest_client.ResponseBody(resp, body) | |
| 63 | ||
| 64 | def delete_consistencygroup(self, cg_id): | |
| 65 | """Delete a consistency group.""" | |
| 66 | post_body = {'force': True} | |
| 67 | post_body = json.dumps({'consistencygroup': post_body}) | |
| 68 | resp, body = self.post('consistencygroups/%s/delete' % cg_id, | |
| 69 | post_body) | |
| 70 | self.expected_success(http_client.ACCEPTED, resp.status) | |
| 71 | return rest_client.ResponseBody(resp, body) | |
| 72 | ||
| 73 | def show_consistencygroup(self, cg_id): | |
| 74 | """Returns the details of a single consistency group.""" | |
| 75 | url = "consistencygroups/%s" % str(cg_id) | |
| 76 | resp, body = self.get(url) | |
| 77 | body = json.loads(body) | |
| 78 | self.expected_success(http_client.OK, resp.status) | |
| 79 | return rest_client.ResponseBody(resp, body) | |
| 80 | ||
| 81 | def list_consistencygroups(self, detail=False): | |
| 82 | """Information for all the tenant's consistency groups.""" | |
| 83 | url = "consistencygroups" | |
| 84 | if detail: | |
| 85 | url += "/detail" | |
| 86 | resp, body = self.get(url) | |
| 87 | body = json.loads(body) | |
| 88 | self.expected_success(http_client.OK, resp.status) | |
| 89 | return rest_client.ResponseBody(resp, body) | |
| 90 | ||
| 91 | def create_cgsnapshot(self, consistencygroup_id, **kwargs): | |
| 92 | """Creates a consistency group snapshot.""" | |
| 93 | post_body = {'consistencygroup_id': consistencygroup_id} | |
| 94 | if kwargs.get('name'): | |
| 95 | post_body['name'] = kwargs.get('name') | |
| 96 | if kwargs.get('description'): | |
| 97 | post_body['description'] = kwargs.get('description') | |
| 98 | post_body = json.dumps({'cgsnapshot': post_body}) | |
| 99 | resp, body = self.post('cgsnapshots', post_body) | |
| 100 | body = json.loads(body) | |
| 101 | self.expected_success(http_client.ACCEPTED, resp.status) | |
| 102 | return rest_client.ResponseBody(resp, body) | |
| 103 | ||
| 104 | def delete_cgsnapshot(self, cgsnapshot_id): | |
| 105 | """Delete a consistency group snapshot.""" | |
| 106 | resp, body = self.delete('cgsnapshots/%s' % (str(cgsnapshot_id))) | |
| 107 | self.expected_success(http_client.ACCEPTED, resp.status) | |
| 108 | return rest_client.ResponseBody(resp, body) | |
| 109 | ||
| 110 | def show_cgsnapshot(self, cgsnapshot_id): | |
| 111 | """Returns the details of a single consistency group snapshot.""" | |
| 112 | url = "cgsnapshots/%s" % str(cgsnapshot_id) | |
| 113 | resp, body = self.get(url) | |
| 114 | body = json.loads(body) | |
| 115 | self.expected_success(http_client.OK, resp.status) | |
| 116 | return rest_client.ResponseBody(resp, body) | |
| 117 | ||
| 118 | def list_cgsnapshots(self, detail=False): | |
| 119 | """Information for all the tenant's consistency group snapshotss.""" | |
| 120 | url = "cgsnapshots" | |
| 121 | if detail: | |
| 122 | url += "/detail" | |
| 123 | resp, body = self.get(url) | |
| 124 | body = json.loads(body) | |
| 125 | self.expected_success(http_client.OK, resp.status) | |
| 126 | return rest_client.ResponseBody(resp, body) | |
| 127 | ||
| 128 | def wait_for_consistencygroup_status(self, cg_id, status): | |
| 129 | """Waits for a consistency group to reach a given status.""" | |
| 130 | body = self.show_consistencygroup(cg_id)['consistencygroup'] | |
| 131 | cg_status = body['status'] | |
| 132 | start = int(time.time()) | |
| 133 | ||
| 134 | while cg_status != status: | |
| 135 | time.sleep(self.build_interval) | |
| 136 | body = self.show_consistencygroup(cg_id)['consistencygroup'] | |
| 137 | cg_status = body['status'] | |
| 138 | if cg_status == 'error': | |
| 139 | raise exceptions.ConsistencyGroupException(cg_id=cg_id) | |
| 140 | ||
| 141 | if int(time.time()) - start >= self.build_timeout: | |
| 142 | message = ('Consistency group %s failed to reach %s status ' | |
| 143 | '(current %s) within the required time (%s s).' % | |
| 144 | (cg_id, status, cg_status, | |
| 145 | self.build_timeout)) | |
| 146 | raise exceptions.TimeoutException(message) | |
| 147 | ||
| 148 | def wait_for_consistencygroup_deletion(self, cg_id): | |
| 149 | """Waits for consistency group deletion""" | |
| 150 | start_time = int(time.time()) | |
| 151 | while True: | |
| 152 | try: | |
| 153 | self.show_consistencygroup(cg_id) | |
| 154 | except lib_exc.NotFound: | |
| 155 | return | |
| 156 | if int(time.time()) - start_time >= self.build_timeout: | |
| 157 | raise exceptions.TimeoutException | |
| 158 | time.sleep(self.build_interval) | |
| 159 | ||
| 160 | def wait_for_cgsnapshot_status(self, cgsnapshot_id, status): | |
| 161 | """Waits for a consistency group snapshot to reach a given status.""" | |
| 162 | body = self.show_cgsnapshot(cgsnapshot_id)['cgsnapshot'] | |
| 163 | cgsnapshot_status = body['status'] | |
| 164 | start = int(time.time()) | |
| 165 | ||
| 166 | while cgsnapshot_status != status: | |
| 167 | time.sleep(self.build_interval) | |
| 168 | body = self.show_cgsnapshot(cgsnapshot_id)['cgsnapshot'] | |
| 169 | cgsnapshot_status = body['status'] | |
| 170 | if cgsnapshot_status == 'error': | |
| 171 | raise exceptions.ConsistencyGroupSnapshotException( | |
| 172 | cgsnapshot_id=cgsnapshot_id) | |
| 173 | ||
| 174 | if int(time.time()) - start >= self.build_timeout: | |
| 175 | message = ('Consistency group snapshot %s failed to reach ' | |
| 176 | '%s status (current %s) within the required time ' | |
| 177 | '(%s s).' % | |
| 178 | (cgsnapshot_id, status, cgsnapshot_status, | |
| 179 | self.build_timeout)) | |
| 180 | raise exceptions.TimeoutException(message) | |
| 181 | ||
| 182 | def wait_for_cgsnapshot_deletion(self, cgsnapshot_id): | |
| 183 | """Waits for consistency group snapshot deletion""" | |
| 184 | start_time = int(time.time()) | |
| 185 | while True: | |
| 186 | try: | |
| 187 | self.show_cgsnapshot(cgsnapshot_id) | |
| 188 | except lib_exc.NotFound: | |
| 189 | return | |
| 190 | if int(time.time()) - start_time >= self.build_timeout: | |
| 191 | raise exceptions.TimeoutException | |
| 192 | time.sleep(self.build_interval) |
| 0 | # Copyright (C) 2017 Huawei. | |
| 1 | # All Rights Reserved. | |
| 2 | # | |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may | |
| 4 | # not use this file except in compliance with the License. You may obtain | |
| 5 | # a copy of the License at | |
| 6 | # | |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 | # | |
| 9 | # Unless required by applicable law or agreed to in writing, software | |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
| 12 | # License for the specific language governing permissions and limitations | |
| 13 | # under the License. | |
| 14 | ||
| 15 | from oslo_serialization import jsonutils as json | |
| 16 | from tempest.lib.common import rest_client | |
| 17 | from tempest.lib.services.volume import base_client | |
| 18 | ||
| 19 | ||
| 20 | class VolumeRevertClient(base_client.BaseClient): | |
| 21 | """Client class to send revert to snapshot action API request""" | |
| 22 | ||
| 23 | def __init__(self, auth_provider, service, region, **kwargs): | |
| 24 | super(VolumeRevertClient, self).__init__( | |
| 25 | auth_provider, service, region, **kwargs) | |
| 26 | ||
| 27 | def revert_to_snapshot(self, volume, snapshot_id): | |
| 28 | """Revert a volume to snapshot.""" | |
| 29 | post_body = {'snapshot_id': snapshot_id} | |
| 30 | post_body = json.dumps({'revert': post_body}) | |
| 31 | resp, body = self.post('volumes/%s/action' % volume['id'], | |
| 32 | post_body) | |
| 33 | return rest_client.ResponseBody(resp, body) |