diff --git a/cinder/tests/tempest/__init__.py b/cinder/tests/tempest/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/cinder/tests/tempest/api/__init__.py b/cinder/tests/tempest/api/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/cinder/tests/tempest/api/volume/__init__.py b/cinder/tests/tempest/api/volume/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/cinder/tests/tempest/api/volume/base.py b/cinder/tests/tempest/api/volume/base.py deleted file mode 100644 index b3765e0..0000000 --- a/cinder/tests/tempest/api/volume/base.py +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright 2017 NEC Corporation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from tempest.api.volume import api_microversion_fixture -from tempest.common import compute -from tempest.common import waiters -from tempest import config -from tempest.lib.common import api_version_utils -from tempest.lib.common.utils import data_utils -from tempest.lib.common.utils import test_utils -from tempest.lib import exceptions -from tempest import test - -CONF = config.CONF - - -class BaseVolumeTest(api_version_utils.BaseMicroversionTest, - test.BaseTestCase): - """Base test case class for all Cinder API tests.""" - - _api_version = 2 - credentials = ['primary'] - - @classmethod - def skip_checks(cls): - super(BaseVolumeTest, cls).skip_checks() - - if not CONF.service_available.cinder: - skip_msg = ("%s skipped as Cinder is not available" % cls.__name__) - raise cls.skipException(skip_msg) - if cls._api_version == 2: - if not CONF.volume_feature_enabled.api_v2: - msg = "Volume API v2 is disabled" - raise cls.skipException(msg) - elif cls._api_version == 3: - if not CONF.volume_feature_enabled.api_v3: - msg = "Volume API v3 is disabled" - raise cls.skipException(msg) - else: - msg = ("Invalid Cinder API version (%s)" % cls._api_version) - raise exceptions.InvalidConfiguration(msg) - - api_version_utils.check_skip_with_microversion( - cls.min_microversion, cls.max_microversion, - CONF.volume.min_microversion, CONF.volume.max_microversion) - - @classmethod - def setup_clients(cls): - super(BaseVolumeTest, cls).setup_clients() - if cls._api_version == 3: - cls.backups_client = cls.os_primary.backups_v3_client - cls.volumes_client = cls.os_primary.volumes_v3_client - else: - cls.backups_client = cls.os_primary.backups_v2_client - cls.volumes_client = cls.os_primary.volumes_v2_client - - cls.snapshots_client = cls.os_primary.snapshots_v2_client - - @classmethod - def setup_credentials(cls): - cls.set_network_resources() - super(BaseVolumeTest, cls).setup_credentials() - - def setUp(self): - super(BaseVolumeTest, self).setUp() - self.useFixture(api_microversion_fixture.APIMicroversionFixture( - self.request_microversion)) - - @classmethod - def resource_setup(cls): - super(BaseVolumeTest, cls).resource_setup() - cls.request_microversion = ( - api_version_utils.select_request_microversion( - cls.min_microversion, - CONF.volume.min_microversion)) - - @classmethod - def create_volume(cls, wait_until='available', **kwargs): - """Wrapper utility that returns a test volume. - - :param wait_until: wait till volume status. - """ - if 'size' not in kwargs: - kwargs['size'] = CONF.volume.volume_size - - if 'imageRef' in kwargs: - image = cls.os_primary.image_client_v2.show_image( - kwargs['imageRef']) - min_disk = image['min_disk'] - kwargs['size'] = max(kwargs['size'], min_disk) - - if 'name' not in kwargs: - name = data_utils.rand_name(cls.__name__ + '-Volume') - kwargs['name'] = name - - volume = cls.volumes_client.create_volume(**kwargs)['volume'] - cls.addClassResourceCleanup( - cls.volumes_client.wait_for_resource_deletion, volume['id']) - cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, - cls.volumes_client.delete_volume, - volume['id']) - waiters.wait_for_volume_resource_status(cls.volumes_client, - volume['id'], wait_until) - return volume - - @classmethod - def create_snapshot(cls, volume_id=1, **kwargs): - """Wrapper utility that returns a test snapshot.""" - if 'name' not in kwargs: - name = data_utils.rand_name(cls.__name__ + '-Snapshot') - kwargs['name'] = name - - snapshot = cls.snapshots_client.create_snapshot( - volume_id=volume_id, **kwargs)['snapshot'] - cls.addClassResourceCleanup( - cls.snapshots_client.wait_for_resource_deletion, snapshot['id']) - cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, - cls.snapshots_client.delete_snapshot, - snapshot['id']) - waiters.wait_for_volume_resource_status(cls.snapshots_client, - snapshot['id'], 'available') - return snapshot - - def create_backup(self, volume_id, backup_client=None, **kwargs): - """Wrapper utility that returns a test backup.""" - if backup_client is None: - backup_client = self.backups_client - if 'name' not in kwargs: - name = data_utils.rand_name(self.__class__.__name__ + '-Backup') - kwargs['name'] = name - - backup = backup_client.create_backup( - volume_id=volume_id, **kwargs)['backup'] - self.addCleanup(backup_client.delete_backup, backup['id']) - waiters.wait_for_volume_resource_status(backup_client, backup['id'], - 'available') - return backup - - def create_server(self, wait_until='ACTIVE', **kwargs): - name = kwargs.pop( - 'name', - data_utils.rand_name(self.__class__.__name__ + '-instance')) - - tenant_network = self.get_tenant_network() - body, _ = compute.create_test_server( - self.os_primary, - tenant_network=tenant_network, - name=name, - wait_until=wait_until, - **kwargs) - - self.addCleanup(test_utils.call_and_ignore_notfound_exc, - waiters.wait_for_server_termination, - self.os_primary.servers_client, body['id']) - self.addCleanup(test_utils.call_and_ignore_notfound_exc, - self.os_primary.servers_client.delete_server, - body['id']) - return body diff --git a/cinder/tests/tempest/api/volume/test_consistencygroups.py b/cinder/tests/tempest/api/volume/test_consistencygroups.py deleted file mode 100644 index 18ba654..0000000 --- a/cinder/tests/tempest/api/volume/test_consistencygroups.py +++ /dev/null @@ -1,283 +0,0 @@ -# Copyright (C) 2015 EMC Corporation. -# Copyright (C) 2016 Pure Storage, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from tempest.common import waiters -from tempest import config -from tempest.lib.common.utils import data_utils -from tempest.lib import decorators - -from cinder.tests.tempest.api.volume import base -from cinder.tests.tempest import cinder_clients - -CONF = config.CONF - - -class ConsistencyGroupsV2Test(base.BaseVolumeTest): - @classmethod - def setup_clients(cls): - cls._api_version = 2 - super(ConsistencyGroupsV2Test, cls).setup_clients() - cls.admin_volume_client = cls.os_admin.volumes_v2_client - - manager = cinder_clients.Manager(cls.os_admin) - cls.consistencygroups_adm_client = manager.consistencygroups_adm_client - - @classmethod - def skip_checks(cls): - super(ConsistencyGroupsV2Test, cls).skip_checks() - if not CONF.volume_feature_enabled.consistency_group: - raise cls.skipException("Cinder consistency group " - "feature disabled") - - def _delete_consistencygroup(self, cg_id): - self.consistencygroups_adm_client.delete_consistencygroup(cg_id) - vols = self.admin_volume_client.list_volumes(detail=True)['volumes'] - for vol in vols: - if vol['consistencygroup_id'] == cg_id: - self.admin_volume_client.wait_for_resource_deletion(vol['id']) - self.consistencygroups_adm_client.wait_for_consistencygroup_deletion( - cg_id) - - def _delete_cgsnapshot(self, cgsnapshot_id, cg_id): - self.consistencygroups_adm_client.delete_cgsnapshot(cgsnapshot_id) - vols = self.admin_volume_client.list_volumes(detail=True)['volumes'] - snapshots = self.os_admin.snapshots_v2_client.list_snapshots( - detail=True)['snapshots'] - for vol in vols: - for snap in snapshots: - if (vol['consistencygroup_id'] == cg_id and - vol['id'] == snap['volume_id']): - (self.snapshots_client. - wait_for_resource_deletion(snap['id'])) - self.consistencygroups_adm_client.wait_for_cgsnapshot_deletion( - cgsnapshot_id) - - @decorators.idempotent_id('3fe776ba-ec1f-4e6c-8d78-4b14c3a7fc44') - def test_consistencygroup_create_delete(self): - # Create volume type - name = data_utils.rand_name("volume-type") - volume_type = self.os_admin.volume_types_v2_client.create_volume_type( - name=name)['volume_type'] - - # Create CG - cg_name = data_utils.rand_name('CG') - create_consistencygroup = ( - self.consistencygroups_adm_client.create_consistencygroup) - cg = create_consistencygroup(volume_type['id'], - name=cg_name)['consistencygroup'] - vol_name = data_utils.rand_name("volume") - params = {'name': vol_name, - 'volume_type': volume_type['id'], - 'consistencygroup_id': cg['id'], - 'size': CONF.volume.volume_size} - - # Create volume - volume = self.admin_volume_client.create_volume(**params)['volume'] - - waiters.wait_for_volume_resource_status(self.admin_volume_client, - volume['id'], 'available') - self.consistencygroups_adm_client.wait_for_consistencygroup_status( - cg['id'], 'available') - self.assertEqual(cg_name, cg['name']) - - # Get a given CG - cg = self.consistencygroups_adm_client.show_consistencygroup( - cg['id'])['consistencygroup'] - self.assertEqual(cg_name, cg['name']) - - # Get all CGs with detail - cgs = self.consistencygroups_adm_client.list_consistencygroups( - detail=True)['consistencygroups'] - self.assertIn((cg['name'], cg['id']), - [(m['name'], m['id']) for m in cgs]) - - # Clean up - self._delete_consistencygroup(cg['id']) - self.os_admin.volume_types_v2_client.delete_volume_type( - volume_type['id']) - - @decorators.idempotent_id('2134dd52-f333-4456-bb05-6cb0f009a44f') - def test_consistencygroup_cgsnapshot_create_delete(self): - # Create volume type - name = data_utils.rand_name("volume-type") - volume_type = self.admin_volume_types_client.create_volume_type( - name=name)['volume_type'] - - # Create CG - cg_name = data_utils.rand_name('CG') - create_consistencygroup = ( - self.consistencygroups_adm_client.create_consistencygroup) - cg = create_consistencygroup(volume_type['id'], - name=cg_name)['consistencygroup'] - vol_name = data_utils.rand_name("volume") - params = {'name': vol_name, - 'volume_type': volume_type['id'], - 'consistencygroup_id': cg['id'], - 'size': CONF.volume.volume_size} - - # Create volume - volume = self.admin_volume_client.create_volume(**params)['volume'] - waiters.wait_for_volume_resource_status(self.admin_volume_client, - volume['id'], 'available') - self.consistencygroups_adm_client.wait_for_consistencygroup_status( - cg['id'], 'available') - self.assertEqual(cg_name, cg['name']) - - # Create cgsnapshot - cgsnapshot_name = data_utils.rand_name('cgsnapshot') - create_cgsnapshot = ( - self.consistencygroups_adm_client.create_cgsnapshot) - cgsnapshot = create_cgsnapshot(cg['id'], - name=cgsnapshot_name)['cgsnapshot'] - snapshots = self.os_admin.snapshots_v2_client.list_snapshots( - detail=True)['snapshots'] - for snap in snapshots: - if volume['id'] == snap['volume_id']: - waiters.wait_for_volume_resource_status( - self.os_admin.snapshots_v2_client, - snap['id'], 'available') - self.consistencygroups_adm_client.wait_for_cgsnapshot_status( - cgsnapshot['id'], 'available') - self.assertEqual(cgsnapshot_name, cgsnapshot['name']) - - # Get a given CG snapshot - cgsnapshot = self.consistencygroups_adm_client.show_cgsnapshot( - cgsnapshot['id'])['cgsnapshot'] - self.assertEqual(cgsnapshot_name, cgsnapshot['name']) - - # Get all CG snapshots with detail - cgsnapshots = self.consistencygroups_adm_client.list_cgsnapshots( - detail=True)['cgsnapshots'] - self.assertIn((cgsnapshot['name'], cgsnapshot['id']), - [(m['name'], m['id']) for m in cgsnapshots]) - - # Clean up - self._delete_cgsnapshot(cgsnapshot['id'], cg['id']) - self._delete_consistencygroup(cg['id']) - self.admin_volume_types_client.delete_volume_type(volume_type['id']) - - @decorators.idempotent_id('3a6a5525-25ca-4a6c-aac4-cac6fa8f5b43') - def test_create_consistencygroup_from_cgsnapshot(self): - # Create volume type - name = data_utils.rand_name("volume-type") - volume_type = self.admin_volume_types_client.create_volume_type( - name=name)['volume_type'] - - # Create CG - cg_name = data_utils.rand_name('CG') - create_consistencygroup = ( - self.consistencygroups_adm_client.create_consistencygroup) - cg = create_consistencygroup(volume_type['id'], - name=cg_name)['consistencygroup'] - vol_name = data_utils.rand_name("volume") - params = {'name': vol_name, - 'volume_type': volume_type['id'], - 'consistencygroup_id': cg['id'], - 'size': CONF.volume.volume_size} - - # Create volume - volume = self.admin_volume_client.create_volume(**params)['volume'] - waiters.wait_for_volume_resource_status(self.admin_volume_client, - volume['id'], 'available') - self.consistencygroups_adm_client.wait_for_consistencygroup_status( - cg['id'], 'available') - self.assertEqual(cg_name, cg['name']) - - # Create cgsnapshot - cgsnapshot_name = data_utils.rand_name('cgsnapshot') - create_cgsnapshot = ( - self.consistencygroups_adm_client.create_cgsnapshot) - cgsnapshot = create_cgsnapshot(cg['id'], - name=cgsnapshot_name)['cgsnapshot'] - snapshots = self.snapshots_client.list_snapshots( - detail=True)['snapshots'] - for snap in snapshots: - if volume['id'] == snap['volume_id']: - waiters.wait_for_volume_resource_status( - self.os_admin.snapshots_v2_client, snap['id'], 'available') - self.consistencygroups_adm_client.wait_for_cgsnapshot_status( - cgsnapshot['id'], 'available') - self.assertEqual(cgsnapshot_name, cgsnapshot['name']) - - # Create CG from CG snapshot - cg_name2 = data_utils.rand_name('CG_from_snap') - create_consistencygroup2 = ( - self.consistencygroups_adm_client.create_consistencygroup_from_src) - cg2 = create_consistencygroup2(cgsnapshot_id=cgsnapshot['id'], - name=cg_name2)['consistencygroup'] - vols = self.admin_volume_client.list_volumes( - detail=True)['volumes'] - for vol in vols: - if vol['consistencygroup_id'] == cg2['id']: - waiters.wait_for_volume_resource_status( - self.admin_volume_client, vol['id'], 'available') - self.consistencygroups_adm_client.wait_for_consistencygroup_status( - cg2['id'], 'available') - self.assertEqual(cg_name2, cg2['name']) - - # Clean up - self._delete_consistencygroup(cg2['id']) - self._delete_cgsnapshot(cgsnapshot['id'], cg['id']) - self._delete_consistencygroup(cg['id']) - self.admin_volume_types_client.delete_volume_type(volume_type['id']) - - @decorators.idempotent_id('556121ae-de9c-4342-9897-e54260447a19') - def test_create_consistencygroup_from_consistencygroup(self): - # Create volume type - name = data_utils.rand_name("volume-type") - volume_type = self.admin_volume_types_client.create_volume_type( - name=name)['volume_type'] - - # Create CG - cg_name = data_utils.rand_name('CG') - create_consistencygroup = ( - self.consistencygroups_adm_client.create_consistencygroup) - cg = create_consistencygroup(volume_type['id'], - name=cg_name)['consistencygroup'] - vol_name = data_utils.rand_name("volume") - params = {'name': vol_name, - 'volume_type': volume_type['id'], - 'consistencygroup_id': cg['id'], - 'size': CONF.volume.volume_size} - - # Create volume - volume = self.admin_volume_client.create_volume(**params)['volume'] - waiters.wait_for_volume_resource_status(self.admin_volume_client, - volume['id'], 'available') - self.consistencygroups_adm_client.wait_for_consistencygroup_status( - cg['id'], 'available') - self.assertEqual(cg_name, cg['name']) - - # Create CG from CG - cg_name2 = data_utils.rand_name('CG_from_cg') - create_consistencygroup2 = ( - self.consistencygroups_adm_client.create_consistencygroup_from_src) - cg2 = create_consistencygroup2(source_cgid=cg['id'], - name=cg_name2)['consistencygroup'] - vols = self.admin_volume_client.list_volumes( - detail=True)['volumes'] - for vol in vols: - if vol['consistencygroup_id'] == cg2['id']: - waiters.wait_for_volume_resource_status( - self.admin_volume_client, vol['id'], 'available') - self.consistencygroups_adm_client.wait_for_consistencygroup_status( - cg2['id'], 'available') - self.assertEqual(cg_name2, cg2['name']) - - # Clean up - self._delete_consistencygroup(cg2['id']) - self._delete_consistencygroup(cg['id']) - self.admin_volume_types_client.delete_volume_type(volume_type['id']) diff --git a/cinder/tests/tempest/api/volume/test_volume_backup.py b/cinder/tests/tempest/api/volume/test_volume_backup.py deleted file mode 100644 index 765585e..0000000 --- a/cinder/tests/tempest/api/volume/test_volume_backup.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright (c) 2016 Mirantis Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from tempest.common import waiters -from tempest import config -from tempest.lib.common.utils import data_utils -from tempest.lib import decorators - -from cinder.tests.tempest.api.volume import base - -CONF = config.CONF - - -class VolumesBackupsTest(base.BaseVolumeTest): - - @classmethod - def skip_checks(cls): - super(VolumesBackupsTest, cls).skip_checks() - if not CONF.volume_feature_enabled.backup: - raise cls.skipException("Cinder backup feature disabled") - - @decorators.idempotent_id('885410c6-cd1d-452c-a409-7c32b7e0be15') - def test_volume_snapshot_backup(self): - """Create backup from snapshot.""" - volume = self.create_volume() - # Create snapshot - snapshot = self.create_snapshot(volume['id']) - # Create backup - backup = self.create_backup( - volume_id=volume['id'], - snapshot_id=snapshot['id']) - # Get a given backup - backup = self.backups_client.show_backup( - backup['id'])['backup'] - waiters.wait_for_volume_resource_status( - self.backups_client, - backup['id'], 'available') - self.assertEqual(volume['id'], backup['volume_id']) - self.assertEqual(snapshot['id'], backup['snapshot_id']) - - self.snapshots_client.delete_snapshot(snapshot['id']) - self.snapshots_client.wait_for_resource_deletion(snapshot['id']) - - self.volumes_client.delete_volume(volume['id']) - self.volumes_client.wait_for_resource_deletion(volume['id']) - - @decorators.idempotent_id('b5d837b0-7066-455d-88fc-4a721a899306') - def test_backup_create_and_restore_to_an_existing_volume(self): - """Test backup create and restore to an existing volume.""" - # Create volume - src_vol = self.create_volume() - self.addCleanup(self.volumes_client.delete_volume, - src_vol['id']) - # Create backup - backup = self.backups_client.create_backup( - volume_id=src_vol['id'])['backup'] - self.addCleanup(self.backups_client.delete_backup, backup['id']) - waiters.wait_for_volume_resource_status( - self.backups_client, - backup['id'], 'available') - # Restore to existing volume - restore = self.backups_client.restore_backup( - backup_id=backup['id'], - volume_id=src_vol['id'])['restore'] - waiters.wait_for_volume_resource_status( - self.backups_client, - backup['id'], 'available') - waiters.wait_for_volume_resource_status( - self.volumes_client, - src_vol['id'], 'available') - self.assertEqual(src_vol['id'], restore['volume_id']) - self.assertEqual(backup['id'], restore['backup_id']) - - @decorators.idempotent_id('c810fe2c-cb40-43ab-96aa-471b74516a98') - def test_incremental_backup(self): - """Test create incremental backup.""" - # Create volume from image - volume = self.create_volume(size=CONF.volume.volume_size, - imageRef=CONF.compute.image_ref) - self.addCleanup(self.volumes_client.delete_volume, - volume['id']) - - # Create backup - backup = self.backups_client.create_backup( - volume_id=volume['id'])['backup'] - waiters.wait_for_volume_resource_status(self.backups_client, - backup['id'], 'available') - # Create a server - bd_map = [{'volume_id': volume['id'], - 'delete_on_termination': '0'}] - - server_name = data_utils.rand_name('instance') - server = self.create_server( - name=server_name, - block_device_mapping=bd_map, - wait_until='ACTIVE') - - # Delete VM - self.os_primary.servers_client.delete_server(server['id']) - # Create incremental backup - waiters.wait_for_volume_resource_status(self.volumes_client, - volume['id'], 'available') - backup_incr = self.backups_client.create_backup( - volume_id=volume['id'], - incremental=True)['backup'] - - waiters.wait_for_volume_resource_status(self.backups_client, - backup_incr['id'], - 'available') - - is_incremental = self.backups_client.show_backup( - backup_incr['id'])['backup']['is_incremental'] - self.assertTrue(is_incremental) - - self.backups_client.delete_backup(backup_incr['id']) - self.backups_client.wait_for_resource_deletion(backup_incr['id']) - self.backups_client.delete_backup(backup['id']) - self.backups_client.wait_for_resource_deletion(backup['id']) diff --git a/cinder/tests/tempest/api/volume/test_volume_revert.py b/cinder/tests/tempest/api/volume/test_volume_revert.py deleted file mode 100644 index d82ada6..0000000 --- a/cinder/tests/tempest/api/volume/test_volume_revert.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (c) 2017 Huawei. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from tempest.common import waiters -from tempest import config -from tempest.lib import decorators - -from cinder.tests.tempest.api.volume import base -from cinder.tests.tempest import cinder_clients - -CONF = config.CONF - - -class VolumeRevertTests(base.BaseVolumeTest): - min_microversion = '3.40' - - @classmethod - def skip_checks(cls): - super(VolumeRevertTests, cls).skip_checks() - if not CONF.volume_feature_enabled.volume_revert: - raise cls.skipException("Cinder volume revert feature disabled") - - @classmethod - def setup_clients(cls): - cls._api_version = 3 - super(VolumeRevertTests, cls).setup_clients() - - manager = cinder_clients.Manager(cls.os_primary) - cls.volume_revert_client = manager.volume_revert_client - - def setUp(self): - super(VolumeRevertTests, self).setUp() - # Create volume - self.volume = self.create_volume(size=1) - # Create snapshot - self.snapshot = self.create_snapshot(self.volume['id']) - - @decorators.idempotent_id('87b7dcb7-4950-4a3a-802c-ece55491846d') - def test_volume_revert_to_snapshot(self): - """Test revert to snapshot""" - # Revert to snapshot - self.volume_revert_client.revert_to_snapshot(self.volume, - self.snapshot['id']) - waiters.wait_for_volume_resource_status( - self.volumes_client, - self.volume['id'], 'available') - waiters.wait_for_volume_resource_status( - self.snapshots_client, - self.snapshot['id'], 'available') - volume = self.volumes_client.show_volume(self.volume['id'])['volume'] - - self.assertEqual(1, volume['size']) - - @decorators.idempotent_id('4e8b0788-87fe-430d-be7a-444d7f8e0347') - def test_volume_revert_to_snapshot_after_extended(self): - """Test revert to snapshot after extended""" - # Extend the volume - self.volumes_client.extend_volume(self.volume['id'], new_size=2) - waiters.wait_for_volume_resource_status(self.volumes_client, - self.volume['id'], 'available') - # Revert to snapshot - self.volume_revert_client.revert_to_snapshot(self.volume, - self.snapshot['id']) - waiters.wait_for_volume_resource_status( - self.volumes_client, - self.volume['id'], 'available') - waiters.wait_for_volume_resource_status( - self.snapshots_client, - self.snapshot['id'], 'available') - volume = self.volumes_client.show_volume(self.volume['id'])['volume'] - self.assertEqual(2, volume['size']) diff --git a/cinder/tests/tempest/api/volume/test_volume_unicode.py b/cinder/tests/tempest/api/volume/test_volume_unicode.py deleted file mode 100644 index db280d3..0000000 --- a/cinder/tests/tempest/api/volume/test_volume_unicode.py +++ /dev/null @@ -1,64 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2016 Red Hat, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from tempest.common import waiters -from tempest import config -from tempest.lib.common.utils import data_utils -from tempest.lib.common.utils import test_utils - -from cinder.tests.tempest.api.volume import base - -CONF = config.CONF - - -class CinderUnicodeTest(base.BaseVolumeTest): - - @classmethod - def resource_setup(cls): - super(CinderUnicodeTest, cls).resource_setup() - - # Stick to three-byte unicode here, since four+ byte - # chars require utf8mb4 database support which may not - # be configured. - cls.volume_name = u"CinderUnicodeTest塵㼗‽" - cls.volume = cls.create_volume_with_args(name=cls.volume_name) - - @classmethod - def create_volume_with_args(cls, **kwargs): - if 'name' not in kwargs: - kwargs['name'] = data_utils.rand_name('Volume') - - kwargs['size'] = CONF.volume.volume_size - - volume = cls.volumes_client.create_volume(**kwargs)['volume'] - cls.addClassResourceCleanup( - cls.volumes_client.wait_for_resource_deletion, volume['id']) - cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, - cls.volumes_client.delete_volume, - volume['id']) - waiters.wait_for_volume_resource_status(cls.volumes_client, - volume['id'], - 'available') - - return volume - - def test_create_delete_unicode_volume_name(self): - """Create a volume with a unicode name and view it.""" - - result = self.volumes_client.show_volume(self.volume['id']) - fetched_volume = result['volume'] - self.assertEqual(fetched_volume['name'], - self.volume_name) diff --git a/cinder/tests/tempest/cinder_clients.py b/cinder/tests/tempest/cinder_clients.py deleted file mode 100644 index 8d9abe7..0000000 --- a/cinder/tests/tempest/cinder_clients.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2016 Pure Storage, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from tempest import config - -from cinder.tests.tempest.services import consistencygroups_client -from cinder.tests.tempest.services import volume_revert_client - -CONF = config.CONF - - -class Manager(object): - def __init__(self, base_manager): - params = { - 'service': CONF.volume.catalog_type, - 'region': CONF.volume.region or CONF.identity.region, - 'endpoint_type': CONF.volume.endpoint_type, - 'build_interval': CONF.volume.build_interval, - 'build_timeout': CONF.volume.build_timeout - } - params.update(base_manager.default_params) - auth_provider = base_manager.auth_provider - - self.consistencygroups_adm_client = ( - consistencygroups_client.ConsistencyGroupsClient(auth_provider, - **params)) - self.volume_revert_client = ( - volume_revert_client.VolumeRevertClient(auth_provider, **params)) diff --git a/cinder/tests/tempest/config.py b/cinder/tests/tempest/config.py deleted file mode 100644 index e15a399..0000000 --- a/cinder/tests/tempest/config.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2016 -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_config import cfg - -cinder_option = [ - cfg.BoolOpt('consistency_group', - default=False, - help='Enable to run Cinder volume consistency group tests'), - cfg.BoolOpt('volume_revert', - default=False, - help='Enable to run Cinder volume revert tests'), -] diff --git a/cinder/tests/tempest/plugin.py b/cinder/tests/tempest/plugin.py deleted file mode 100644 index b816963..0000000 --- a/cinder/tests/tempest/plugin.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright 2015 -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import cinder -import os - -from cinder.tests.tempest import config as project_config - -from tempest import config -from tempest.test_discover import plugins - - -class CinderTempestPlugin(plugins.TempestPlugin): - def load_tests(self): - base_path = os.path.split(os.path.dirname( - os.path.abspath(cinder.__file__)))[0] - test_dir = "cinder/tests/tempest" - full_test_dir = os.path.join(base_path, test_dir) - return full_test_dir, base_path - - def register_opts(self, conf): - config.register_opt_group( - conf, config.volume_feature_group, - project_config.cinder_option - ) - - def get_opt_lists(self): - return [ - (config.volume_feature_group.name, - project_config.cinder_option), - ] diff --git a/cinder/tests/tempest/scenario/__init__.py b/cinder/tests/tempest/scenario/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/cinder/tests/tempest/services/__init__.py b/cinder/tests/tempest/services/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/cinder/tests/tempest/services/consistencygroups_client.py b/cinder/tests/tempest/services/consistencygroups_client.py deleted file mode 100644 index 10415d4..0000000 --- a/cinder/tests/tempest/services/consistencygroups_client.py +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright (C) 2015 EMC Corporation. -# Copyright (C) 2016 Pure Storage, Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import time - -from oslo_serialization import jsonutils as json -from six.moves import http_client -from tempest import exceptions -from tempest.lib.common import rest_client -from tempest.lib import exceptions as lib_exc - - -class ConsistencyGroupsClient(rest_client.RestClient): - """Client class to send CRUD Volume ConsistencyGroup API requests""" - - def __init__(self, auth_provider, service, region, **kwargs): - super(ConsistencyGroupsClient, self).__init__( - auth_provider, service, region, **kwargs) - - def create_consistencygroup(self, volume_types, **kwargs): - """Creates a consistency group.""" - post_body = {'volume_types': volume_types} - if kwargs.get('availability_zone'): - post_body['availability_zone'] = kwargs.get('availability_zone') - if kwargs.get('name'): - post_body['name'] = kwargs.get('name') - if kwargs.get('description'): - post_body['description'] = kwargs.get('description') - post_body = json.dumps({'consistencygroup': post_body}) - resp, body = self.post('consistencygroups', post_body) - body = json.loads(body) - self.expected_success(http_client.ACCEPTED, resp.status) - return rest_client.ResponseBody(resp, body) - - def create_consistencygroup_from_src(self, **kwargs): - """Creates a consistency group from source.""" - post_body = {} - if kwargs.get('cgsnapshot_id'): - post_body['cgsnapshot_id'] = kwargs.get('cgsnapshot_id') - if kwargs.get('source_cgid'): - post_body['source_cgid'] = kwargs.get('source_cgid') - if kwargs.get('name'): - post_body['name'] = kwargs.get('name') - if kwargs.get('description'): - post_body['description'] = kwargs.get('description') - post_body = json.dumps({'consistencygroup-from-src': post_body}) - resp, body = self.post('consistencygroups/create_from_src', post_body) - body = json.loads(body) - self.expected_success(http_client.ACCEPTED, resp.status) - return rest_client.ResponseBody(resp, body) - - def delete_consistencygroup(self, cg_id): - """Delete a consistency group.""" - post_body = {'force': True} - post_body = json.dumps({'consistencygroup': post_body}) - resp, body = self.post('consistencygroups/%s/delete' % cg_id, - post_body) - self.expected_success(http_client.ACCEPTED, resp.status) - return rest_client.ResponseBody(resp, body) - - def show_consistencygroup(self, cg_id): - """Returns the details of a single consistency group.""" - url = "consistencygroups/%s" % str(cg_id) - resp, body = self.get(url) - body = json.loads(body) - self.expected_success(http_client.OK, resp.status) - return rest_client.ResponseBody(resp, body) - - def list_consistencygroups(self, detail=False): - """Information for all the tenant's consistency groups.""" - url = "consistencygroups" - if detail: - url += "/detail" - resp, body = self.get(url) - body = json.loads(body) - self.expected_success(http_client.OK, resp.status) - return rest_client.ResponseBody(resp, body) - - def create_cgsnapshot(self, consistencygroup_id, **kwargs): - """Creates a consistency group snapshot.""" - post_body = {'consistencygroup_id': consistencygroup_id} - if kwargs.get('name'): - post_body['name'] = kwargs.get('name') - if kwargs.get('description'): - post_body['description'] = kwargs.get('description') - post_body = json.dumps({'cgsnapshot': post_body}) - resp, body = self.post('cgsnapshots', post_body) - body = json.loads(body) - self.expected_success(http_client.ACCEPTED, resp.status) - return rest_client.ResponseBody(resp, body) - - def delete_cgsnapshot(self, cgsnapshot_id): - """Delete a consistency group snapshot.""" - resp, body = self.delete('cgsnapshots/%s' % (str(cgsnapshot_id))) - self.expected_success(http_client.ACCEPTED, resp.status) - return rest_client.ResponseBody(resp, body) - - def show_cgsnapshot(self, cgsnapshot_id): - """Returns the details of a single consistency group snapshot.""" - url = "cgsnapshots/%s" % str(cgsnapshot_id) - resp, body = self.get(url) - body = json.loads(body) - self.expected_success(http_client.OK, resp.status) - return rest_client.ResponseBody(resp, body) - - def list_cgsnapshots(self, detail=False): - """Information for all the tenant's consistency group snapshotss.""" - url = "cgsnapshots" - if detail: - url += "/detail" - resp, body = self.get(url) - body = json.loads(body) - self.expected_success(http_client.OK, resp.status) - return rest_client.ResponseBody(resp, body) - - def wait_for_consistencygroup_status(self, cg_id, status): - """Waits for a consistency group to reach a given status.""" - body = self.show_consistencygroup(cg_id)['consistencygroup'] - cg_status = body['status'] - start = int(time.time()) - - while cg_status != status: - time.sleep(self.build_interval) - body = self.show_consistencygroup(cg_id)['consistencygroup'] - cg_status = body['status'] - if cg_status == 'error': - raise exceptions.ConsistencyGroupException(cg_id=cg_id) - - if int(time.time()) - start >= self.build_timeout: - message = ('Consistency group %s failed to reach %s status ' - '(current %s) within the required time (%s s).' % - (cg_id, status, cg_status, - self.build_timeout)) - raise exceptions.TimeoutException(message) - - def wait_for_consistencygroup_deletion(self, cg_id): - """Waits for consistency group deletion""" - start_time = int(time.time()) - while True: - try: - self.show_consistencygroup(cg_id) - except lib_exc.NotFound: - return - if int(time.time()) - start_time >= self.build_timeout: - raise exceptions.TimeoutException - time.sleep(self.build_interval) - - def wait_for_cgsnapshot_status(self, cgsnapshot_id, status): - """Waits for a consistency group snapshot to reach a given status.""" - body = self.show_cgsnapshot(cgsnapshot_id)['cgsnapshot'] - cgsnapshot_status = body['status'] - start = int(time.time()) - - while cgsnapshot_status != status: - time.sleep(self.build_interval) - body = self.show_cgsnapshot(cgsnapshot_id)['cgsnapshot'] - cgsnapshot_status = body['status'] - if cgsnapshot_status == 'error': - raise exceptions.ConsistencyGroupSnapshotException( - cgsnapshot_id=cgsnapshot_id) - - if int(time.time()) - start >= self.build_timeout: - message = ('Consistency group snapshot %s failed to reach ' - '%s status (current %s) within the required time ' - '(%s s).' % - (cgsnapshot_id, status, cgsnapshot_status, - self.build_timeout)) - raise exceptions.TimeoutException(message) - - def wait_for_cgsnapshot_deletion(self, cgsnapshot_id): - """Waits for consistency group snapshot deletion""" - start_time = int(time.time()) - while True: - try: - self.show_cgsnapshot(cgsnapshot_id) - except lib_exc.NotFound: - return - if int(time.time()) - start_time >= self.build_timeout: - raise exceptions.TimeoutException - time.sleep(self.build_interval) diff --git a/cinder/tests/tempest/services/volume_revert_client.py b/cinder/tests/tempest/services/volume_revert_client.py deleted file mode 100644 index 0d54701..0000000 --- a/cinder/tests/tempest/services/volume_revert_client.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (C) 2017 Huawei. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_serialization import jsonutils as json -from tempest.lib.common import rest_client -from tempest.lib.services.volume import base_client - - -class VolumeRevertClient(base_client.BaseClient): - """Client class to send revert to snapshot action API request""" - - def __init__(self, auth_provider, service, region, **kwargs): - super(VolumeRevertClient, self).__init__( - auth_provider, service, region, **kwargs) - - def revert_to_snapshot(self, volume, snapshot_id): - """Revert a volume to snapshot.""" - post_body = {'snapshot_id': snapshot_id} - post_body = json.dumps({'revert': post_body}) - resp, body = self.post('volumes/%s/action' % volume['id'], - post_body) - return rest_client.ResponseBody(resp, body) diff --git a/cinder_tempest_plugin/__init__.py b/cinder_tempest_plugin/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cinder_tempest_plugin/api/__init__.py b/cinder_tempest_plugin/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cinder_tempest_plugin/api/volume/__init__.py b/cinder_tempest_plugin/api/volume/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cinder_tempest_plugin/api/volume/base.py b/cinder_tempest_plugin/api/volume/base.py new file mode 100644 index 0000000..b3765e0 --- /dev/null +++ b/cinder_tempest_plugin/api/volume/base.py @@ -0,0 +1,170 @@ +# Copyright 2017 NEC Corporation. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from tempest.api.volume import api_microversion_fixture +from tempest.common import compute +from tempest.common import waiters +from tempest import config +from tempest.lib.common import api_version_utils +from tempest.lib.common.utils import data_utils +from tempest.lib.common.utils import test_utils +from tempest.lib import exceptions +from tempest import test + +CONF = config.CONF + + +class BaseVolumeTest(api_version_utils.BaseMicroversionTest, + test.BaseTestCase): + """Base test case class for all Cinder API tests.""" + + _api_version = 2 + credentials = ['primary'] + + @classmethod + def skip_checks(cls): + super(BaseVolumeTest, cls).skip_checks() + + if not CONF.service_available.cinder: + skip_msg = ("%s skipped as Cinder is not available" % cls.__name__) + raise cls.skipException(skip_msg) + if cls._api_version == 2: + if not CONF.volume_feature_enabled.api_v2: + msg = "Volume API v2 is disabled" + raise cls.skipException(msg) + elif cls._api_version == 3: + if not CONF.volume_feature_enabled.api_v3: + msg = "Volume API v3 is disabled" + raise cls.skipException(msg) + else: + msg = ("Invalid Cinder API version (%s)" % cls._api_version) + raise exceptions.InvalidConfiguration(msg) + + api_version_utils.check_skip_with_microversion( + cls.min_microversion, cls.max_microversion, + CONF.volume.min_microversion, CONF.volume.max_microversion) + + @classmethod + def setup_clients(cls): + super(BaseVolumeTest, cls).setup_clients() + if cls._api_version == 3: + cls.backups_client = cls.os_primary.backups_v3_client + cls.volumes_client = cls.os_primary.volumes_v3_client + else: + cls.backups_client = cls.os_primary.backups_v2_client + cls.volumes_client = cls.os_primary.volumes_v2_client + + cls.snapshots_client = cls.os_primary.snapshots_v2_client + + @classmethod + def setup_credentials(cls): + cls.set_network_resources() + super(BaseVolumeTest, cls).setup_credentials() + + def setUp(self): + super(BaseVolumeTest, self).setUp() + self.useFixture(api_microversion_fixture.APIMicroversionFixture( + self.request_microversion)) + + @classmethod + def resource_setup(cls): + super(BaseVolumeTest, cls).resource_setup() + cls.request_microversion = ( + api_version_utils.select_request_microversion( + cls.min_microversion, + CONF.volume.min_microversion)) + + @classmethod + def create_volume(cls, wait_until='available', **kwargs): + """Wrapper utility that returns a test volume. + + :param wait_until: wait till volume status. + """ + if 'size' not in kwargs: + kwargs['size'] = CONF.volume.volume_size + + if 'imageRef' in kwargs: + image = cls.os_primary.image_client_v2.show_image( + kwargs['imageRef']) + min_disk = image['min_disk'] + kwargs['size'] = max(kwargs['size'], min_disk) + + if 'name' not in kwargs: + name = data_utils.rand_name(cls.__name__ + '-Volume') + kwargs['name'] = name + + volume = cls.volumes_client.create_volume(**kwargs)['volume'] + cls.addClassResourceCleanup( + cls.volumes_client.wait_for_resource_deletion, volume['id']) + cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, + cls.volumes_client.delete_volume, + volume['id']) + waiters.wait_for_volume_resource_status(cls.volumes_client, + volume['id'], wait_until) + return volume + + @classmethod + def create_snapshot(cls, volume_id=1, **kwargs): + """Wrapper utility that returns a test snapshot.""" + if 'name' not in kwargs: + name = data_utils.rand_name(cls.__name__ + '-Snapshot') + kwargs['name'] = name + + snapshot = cls.snapshots_client.create_snapshot( + volume_id=volume_id, **kwargs)['snapshot'] + cls.addClassResourceCleanup( + cls.snapshots_client.wait_for_resource_deletion, snapshot['id']) + cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, + cls.snapshots_client.delete_snapshot, + snapshot['id']) + waiters.wait_for_volume_resource_status(cls.snapshots_client, + snapshot['id'], 'available') + return snapshot + + def create_backup(self, volume_id, backup_client=None, **kwargs): + """Wrapper utility that returns a test backup.""" + if backup_client is None: + backup_client = self.backups_client + if 'name' not in kwargs: + name = data_utils.rand_name(self.__class__.__name__ + '-Backup') + kwargs['name'] = name + + backup = backup_client.create_backup( + volume_id=volume_id, **kwargs)['backup'] + self.addCleanup(backup_client.delete_backup, backup['id']) + waiters.wait_for_volume_resource_status(backup_client, backup['id'], + 'available') + return backup + + def create_server(self, wait_until='ACTIVE', **kwargs): + name = kwargs.pop( + 'name', + data_utils.rand_name(self.__class__.__name__ + '-instance')) + + tenant_network = self.get_tenant_network() + body, _ = compute.create_test_server( + self.os_primary, + tenant_network=tenant_network, + name=name, + wait_until=wait_until, + **kwargs) + + self.addCleanup(test_utils.call_and_ignore_notfound_exc, + waiters.wait_for_server_termination, + self.os_primary.servers_client, body['id']) + self.addCleanup(test_utils.call_and_ignore_notfound_exc, + self.os_primary.servers_client.delete_server, + body['id']) + return body diff --git a/cinder_tempest_plugin/api/volume/test_consistencygroups.py b/cinder_tempest_plugin/api/volume/test_consistencygroups.py new file mode 100644 index 0000000..d6b8e4a --- /dev/null +++ b/cinder_tempest_plugin/api/volume/test_consistencygroups.py @@ -0,0 +1,283 @@ +# Copyright (C) 2015 EMC Corporation. +# Copyright (C) 2016 Pure Storage, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from tempest.common import waiters +from tempest import config +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators + +from cinder_tempest_plugin.api.volume import base +from cinder_tempest_plugin import cinder_clients + +CONF = config.CONF + + +class ConsistencyGroupsV2Test(base.BaseVolumeTest): + @classmethod + def setup_clients(cls): + cls._api_version = 2 + super(ConsistencyGroupsV2Test, cls).setup_clients() + cls.admin_volume_client = cls.os_admin.volumes_v2_client + + manager = cinder_clients.Manager(cls.os_admin) + cls.consistencygroups_adm_client = manager.consistencygroups_adm_client + + @classmethod + def skip_checks(cls): + super(ConsistencyGroupsV2Test, cls).skip_checks() + if not CONF.volume_feature_enabled.consistency_group: + raise cls.skipException("Cinder consistency group " + "feature disabled") + + def _delete_consistencygroup(self, cg_id): + self.consistencygroups_adm_client.delete_consistencygroup(cg_id) + vols = self.admin_volume_client.list_volumes(detail=True)['volumes'] + for vol in vols: + if vol['consistencygroup_id'] == cg_id: + self.admin_volume_client.wait_for_resource_deletion(vol['id']) + self.consistencygroups_adm_client.wait_for_consistencygroup_deletion( + cg_id) + + def _delete_cgsnapshot(self, cgsnapshot_id, cg_id): + self.consistencygroups_adm_client.delete_cgsnapshot(cgsnapshot_id) + vols = self.admin_volume_client.list_volumes(detail=True)['volumes'] + snapshots = self.os_admin.snapshots_v2_client.list_snapshots( + detail=True)['snapshots'] + for vol in vols: + for snap in snapshots: + if (vol['consistencygroup_id'] == cg_id and + vol['id'] == snap['volume_id']): + (self.snapshots_client. + wait_for_resource_deletion(snap['id'])) + self.consistencygroups_adm_client.wait_for_cgsnapshot_deletion( + cgsnapshot_id) + + @decorators.idempotent_id('3fe776ba-ec1f-4e6c-8d78-4b14c3a7fc44') + def test_consistencygroup_create_delete(self): + # Create volume type + name = data_utils.rand_name("volume-type") + volume_type = self.os_admin.volume_types_v2_client.create_volume_type( + name=name)['volume_type'] + + # Create CG + cg_name = data_utils.rand_name('CG') + create_consistencygroup = ( + self.consistencygroups_adm_client.create_consistencygroup) + cg = create_consistencygroup(volume_type['id'], + name=cg_name)['consistencygroup'] + vol_name = data_utils.rand_name("volume") + params = {'name': vol_name, + 'volume_type': volume_type['id'], + 'consistencygroup_id': cg['id'], + 'size': CONF.volume.volume_size} + + # Create volume + volume = self.admin_volume_client.create_volume(**params)['volume'] + + waiters.wait_for_volume_resource_status(self.admin_volume_client, + volume['id'], 'available') + self.consistencygroups_adm_client.wait_for_consistencygroup_status( + cg['id'], 'available') + self.assertEqual(cg_name, cg['name']) + + # Get a given CG + cg = self.consistencygroups_adm_client.show_consistencygroup( + cg['id'])['consistencygroup'] + self.assertEqual(cg_name, cg['name']) + + # Get all CGs with detail + cgs = self.consistencygroups_adm_client.list_consistencygroups( + detail=True)['consistencygroups'] + self.assertIn((cg['name'], cg['id']), + [(m['name'], m['id']) for m in cgs]) + + # Clean up + self._delete_consistencygroup(cg['id']) + self.os_admin.volume_types_v2_client.delete_volume_type( + volume_type['id']) + + @decorators.idempotent_id('2134dd52-f333-4456-bb05-6cb0f009a44f') + def test_consistencygroup_cgsnapshot_create_delete(self): + # Create volume type + name = data_utils.rand_name("volume-type") + volume_type = self.admin_volume_types_client.create_volume_type( + name=name)['volume_type'] + + # Create CG + cg_name = data_utils.rand_name('CG') + create_consistencygroup = ( + self.consistencygroups_adm_client.create_consistencygroup) + cg = create_consistencygroup(volume_type['id'], + name=cg_name)['consistencygroup'] + vol_name = data_utils.rand_name("volume") + params = {'name': vol_name, + 'volume_type': volume_type['id'], + 'consistencygroup_id': cg['id'], + 'size': CONF.volume.volume_size} + + # Create volume + volume = self.admin_volume_client.create_volume(**params)['volume'] + waiters.wait_for_volume_resource_status(self.admin_volume_client, + volume['id'], 'available') + self.consistencygroups_adm_client.wait_for_consistencygroup_status( + cg['id'], 'available') + self.assertEqual(cg_name, cg['name']) + + # Create cgsnapshot + cgsnapshot_name = data_utils.rand_name('cgsnapshot') + create_cgsnapshot = ( + self.consistencygroups_adm_client.create_cgsnapshot) + cgsnapshot = create_cgsnapshot(cg['id'], + name=cgsnapshot_name)['cgsnapshot'] + snapshots = self.os_admin.snapshots_v2_client.list_snapshots( + detail=True)['snapshots'] + for snap in snapshots: + if volume['id'] == snap['volume_id']: + waiters.wait_for_volume_resource_status( + self.os_admin.snapshots_v2_client, + snap['id'], 'available') + self.consistencygroups_adm_client.wait_for_cgsnapshot_status( + cgsnapshot['id'], 'available') + self.assertEqual(cgsnapshot_name, cgsnapshot['name']) + + # Get a given CG snapshot + cgsnapshot = self.consistencygroups_adm_client.show_cgsnapshot( + cgsnapshot['id'])['cgsnapshot'] + self.assertEqual(cgsnapshot_name, cgsnapshot['name']) + + # Get all CG snapshots with detail + cgsnapshots = self.consistencygroups_adm_client.list_cgsnapshots( + detail=True)['cgsnapshots'] + self.assertIn((cgsnapshot['name'], cgsnapshot['id']), + [(m['name'], m['id']) for m in cgsnapshots]) + + # Clean up + self._delete_cgsnapshot(cgsnapshot['id'], cg['id']) + self._delete_consistencygroup(cg['id']) + self.admin_volume_types_client.delete_volume_type(volume_type['id']) + + @decorators.idempotent_id('3a6a5525-25ca-4a6c-aac4-cac6fa8f5b43') + def test_create_consistencygroup_from_cgsnapshot(self): + # Create volume type + name = data_utils.rand_name("volume-type") + volume_type = self.admin_volume_types_client.create_volume_type( + name=name)['volume_type'] + + # Create CG + cg_name = data_utils.rand_name('CG') + create_consistencygroup = ( + self.consistencygroups_adm_client.create_consistencygroup) + cg = create_consistencygroup(volume_type['id'], + name=cg_name)['consistencygroup'] + vol_name = data_utils.rand_name("volume") + params = {'name': vol_name, + 'volume_type': volume_type['id'], + 'consistencygroup_id': cg['id'], + 'size': CONF.volume.volume_size} + + # Create volume + volume = self.admin_volume_client.create_volume(**params)['volume'] + waiters.wait_for_volume_resource_status(self.admin_volume_client, + volume['id'], 'available') + self.consistencygroups_adm_client.wait_for_consistencygroup_status( + cg['id'], 'available') + self.assertEqual(cg_name, cg['name']) + + # Create cgsnapshot + cgsnapshot_name = data_utils.rand_name('cgsnapshot') + create_cgsnapshot = ( + self.consistencygroups_adm_client.create_cgsnapshot) + cgsnapshot = create_cgsnapshot(cg['id'], + name=cgsnapshot_name)['cgsnapshot'] + snapshots = self.snapshots_client.list_snapshots( + detail=True)['snapshots'] + for snap in snapshots: + if volume['id'] == snap['volume_id']: + waiters.wait_for_volume_resource_status( + self.os_admin.snapshots_v2_client, snap['id'], 'available') + self.consistencygroups_adm_client.wait_for_cgsnapshot_status( + cgsnapshot['id'], 'available') + self.assertEqual(cgsnapshot_name, cgsnapshot['name']) + + # Create CG from CG snapshot + cg_name2 = data_utils.rand_name('CG_from_snap') + create_consistencygroup2 = ( + self.consistencygroups_adm_client.create_consistencygroup_from_src) + cg2 = create_consistencygroup2(cgsnapshot_id=cgsnapshot['id'], + name=cg_name2)['consistencygroup'] + vols = self.admin_volume_client.list_volumes( + detail=True)['volumes'] + for vol in vols: + if vol['consistencygroup_id'] == cg2['id']: + waiters.wait_for_volume_resource_status( + self.admin_volume_client, vol['id'], 'available') + self.consistencygroups_adm_client.wait_for_consistencygroup_status( + cg2['id'], 'available') + self.assertEqual(cg_name2, cg2['name']) + + # Clean up + self._delete_consistencygroup(cg2['id']) + self._delete_cgsnapshot(cgsnapshot['id'], cg['id']) + self._delete_consistencygroup(cg['id']) + self.admin_volume_types_client.delete_volume_type(volume_type['id']) + + @decorators.idempotent_id('556121ae-de9c-4342-9897-e54260447a19') + def test_create_consistencygroup_from_consistencygroup(self): + # Create volume type + name = data_utils.rand_name("volume-type") + volume_type = self.admin_volume_types_client.create_volume_type( + name=name)['volume_type'] + + # Create CG + cg_name = data_utils.rand_name('CG') + create_consistencygroup = ( + self.consistencygroups_adm_client.create_consistencygroup) + cg = create_consistencygroup(volume_type['id'], + name=cg_name)['consistencygroup'] + vol_name = data_utils.rand_name("volume") + params = {'name': vol_name, + 'volume_type': volume_type['id'], + 'consistencygroup_id': cg['id'], + 'size': CONF.volume.volume_size} + + # Create volume + volume = self.admin_volume_client.create_volume(**params)['volume'] + waiters.wait_for_volume_resource_status(self.admin_volume_client, + volume['id'], 'available') + self.consistencygroups_adm_client.wait_for_consistencygroup_status( + cg['id'], 'available') + self.assertEqual(cg_name, cg['name']) + + # Create CG from CG + cg_name2 = data_utils.rand_name('CG_from_cg') + create_consistencygroup2 = ( + self.consistencygroups_adm_client.create_consistencygroup_from_src) + cg2 = create_consistencygroup2(source_cgid=cg['id'], + name=cg_name2)['consistencygroup'] + vols = self.admin_volume_client.list_volumes( + detail=True)['volumes'] + for vol in vols: + if vol['consistencygroup_id'] == cg2['id']: + waiters.wait_for_volume_resource_status( + self.admin_volume_client, vol['id'], 'available') + self.consistencygroups_adm_client.wait_for_consistencygroup_status( + cg2['id'], 'available') + self.assertEqual(cg_name2, cg2['name']) + + # Clean up + self._delete_consistencygroup(cg2['id']) + self._delete_consistencygroup(cg['id']) + self.admin_volume_types_client.delete_volume_type(volume_type['id']) diff --git a/cinder_tempest_plugin/api/volume/test_volume_backup.py b/cinder_tempest_plugin/api/volume/test_volume_backup.py new file mode 100644 index 0000000..4eb7703 --- /dev/null +++ b/cinder_tempest_plugin/api/volume/test_volume_backup.py @@ -0,0 +1,130 @@ +# Copyright (c) 2016 Mirantis Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from tempest.common import waiters +from tempest import config +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators + +from cinder_tempest_plugin.api.volume import base + +CONF = config.CONF + + +class VolumesBackupsTest(base.BaseVolumeTest): + + @classmethod + def skip_checks(cls): + super(VolumesBackupsTest, cls).skip_checks() + if not CONF.volume_feature_enabled.backup: + raise cls.skipException("Cinder backup feature disabled") + + @decorators.idempotent_id('885410c6-cd1d-452c-a409-7c32b7e0be15') + def test_volume_snapshot_backup(self): + """Create backup from snapshot.""" + volume = self.create_volume() + # Create snapshot + snapshot = self.create_snapshot(volume['id']) + # Create backup + backup = self.create_backup( + volume_id=volume['id'], + snapshot_id=snapshot['id']) + # Get a given backup + backup = self.backups_client.show_backup( + backup['id'])['backup'] + waiters.wait_for_volume_resource_status( + self.backups_client, + backup['id'], 'available') + self.assertEqual(volume['id'], backup['volume_id']) + self.assertEqual(snapshot['id'], backup['snapshot_id']) + + self.snapshots_client.delete_snapshot(snapshot['id']) + self.snapshots_client.wait_for_resource_deletion(snapshot['id']) + + self.volumes_client.delete_volume(volume['id']) + self.volumes_client.wait_for_resource_deletion(volume['id']) + + @decorators.idempotent_id('b5d837b0-7066-455d-88fc-4a721a899306') + def test_backup_create_and_restore_to_an_existing_volume(self): + """Test backup create and restore to an existing volume.""" + # Create volume + src_vol = self.create_volume() + self.addCleanup(self.volumes_client.delete_volume, + src_vol['id']) + # Create backup + backup = self.backups_client.create_backup( + volume_id=src_vol['id'])['backup'] + self.addCleanup(self.backups_client.delete_backup, backup['id']) + waiters.wait_for_volume_resource_status( + self.backups_client, + backup['id'], 'available') + # Restore to existing volume + restore = self.backups_client.restore_backup( + backup_id=backup['id'], + volume_id=src_vol['id'])['restore'] + waiters.wait_for_volume_resource_status( + self.backups_client, + backup['id'], 'available') + waiters.wait_for_volume_resource_status( + self.volumes_client, + src_vol['id'], 'available') + self.assertEqual(src_vol['id'], restore['volume_id']) + self.assertEqual(backup['id'], restore['backup_id']) + + @decorators.idempotent_id('c810fe2c-cb40-43ab-96aa-471b74516a98') + def test_incremental_backup(self): + """Test create incremental backup.""" + # Create volume from image + volume = self.create_volume(size=CONF.volume.volume_size, + imageRef=CONF.compute.image_ref) + self.addCleanup(self.volumes_client.delete_volume, + volume['id']) + + # Create backup + backup = self.backups_client.create_backup( + volume_id=volume['id'])['backup'] + waiters.wait_for_volume_resource_status(self.backups_client, + backup['id'], 'available') + # Create a server + bd_map = [{'volume_id': volume['id'], + 'delete_on_termination': '0'}] + + server_name = data_utils.rand_name('instance') + server = self.create_server( + name=server_name, + block_device_mapping=bd_map, + wait_until='ACTIVE') + + # Delete VM + self.os_primary.servers_client.delete_server(server['id']) + # Create incremental backup + waiters.wait_for_volume_resource_status(self.volumes_client, + volume['id'], 'available') + backup_incr = self.backups_client.create_backup( + volume_id=volume['id'], + incremental=True)['backup'] + + waiters.wait_for_volume_resource_status(self.backups_client, + backup_incr['id'], + 'available') + + is_incremental = self.backups_client.show_backup( + backup_incr['id'])['backup']['is_incremental'] + self.assertTrue(is_incremental) + + self.backups_client.delete_backup(backup_incr['id']) + self.backups_client.wait_for_resource_deletion(backup_incr['id']) + self.backups_client.delete_backup(backup['id']) + self.backups_client.wait_for_resource_deletion(backup['id']) diff --git a/cinder_tempest_plugin/api/volume/test_volume_revert.py b/cinder_tempest_plugin/api/volume/test_volume_revert.py new file mode 100644 index 0000000..15e4aed --- /dev/null +++ b/cinder_tempest_plugin/api/volume/test_volume_revert.py @@ -0,0 +1,83 @@ +# Copyright (c) 2017 Huawei. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from tempest.common import waiters +from tempest import config +from tempest.lib import decorators + +from cinder_tempest_plugin.api.volume import base +from cinder_tempest_plugin import cinder_clients + +CONF = config.CONF + + +class VolumeRevertTests(base.BaseVolumeTest): + min_microversion = '3.40' + + @classmethod + def skip_checks(cls): + super(VolumeRevertTests, cls).skip_checks() + if not CONF.volume_feature_enabled.volume_revert: + raise cls.skipException("Cinder volume revert feature disabled") + + @classmethod + def setup_clients(cls): + cls._api_version = 3 + super(VolumeRevertTests, cls).setup_clients() + + manager = cinder_clients.Manager(cls.os_primary) + cls.volume_revert_client = manager.volume_revert_client + + def setUp(self): + super(VolumeRevertTests, self).setUp() + # Create volume + self.volume = self.create_volume(size=1) + # Create snapshot + self.snapshot = self.create_snapshot(self.volume['id']) + + @decorators.idempotent_id('87b7dcb7-4950-4a3a-802c-ece55491846d') + def test_volume_revert_to_snapshot(self): + """Test revert to snapshot""" + # Revert to snapshot + self.volume_revert_client.revert_to_snapshot(self.volume, + self.snapshot['id']) + waiters.wait_for_volume_resource_status( + self.volumes_client, + self.volume['id'], 'available') + waiters.wait_for_volume_resource_status( + self.snapshots_client, + self.snapshot['id'], 'available') + volume = self.volumes_client.show_volume(self.volume['id'])['volume'] + + self.assertEqual(1, volume['size']) + + @decorators.idempotent_id('4e8b0788-87fe-430d-be7a-444d7f8e0347') + def test_volume_revert_to_snapshot_after_extended(self): + """Test revert to snapshot after extended""" + # Extend the volume + self.volumes_client.extend_volume(self.volume['id'], new_size=2) + waiters.wait_for_volume_resource_status(self.volumes_client, + self.volume['id'], 'available') + # Revert to snapshot + self.volume_revert_client.revert_to_snapshot(self.volume, + self.snapshot['id']) + waiters.wait_for_volume_resource_status( + self.volumes_client, + self.volume['id'], 'available') + waiters.wait_for_volume_resource_status( + self.snapshots_client, + self.snapshot['id'], 'available') + volume = self.volumes_client.show_volume(self.volume['id'])['volume'] + self.assertEqual(2, volume['size']) diff --git a/cinder_tempest_plugin/api/volume/test_volume_unicode.py b/cinder_tempest_plugin/api/volume/test_volume_unicode.py new file mode 100644 index 0000000..84d43e4 --- /dev/null +++ b/cinder_tempest_plugin/api/volume/test_volume_unicode.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 Red Hat, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from tempest.common import waiters +from tempest import config +from tempest.lib.common.utils import data_utils +from tempest.lib.common.utils import test_utils + +from cinder_tempest_plugin.api.volume import base + +CONF = config.CONF + + +class CinderUnicodeTest(base.BaseVolumeTest): + + @classmethod + def resource_setup(cls): + super(CinderUnicodeTest, cls).resource_setup() + + # Stick to three-byte unicode here, since four+ byte + # chars require utf8mb4 database support which may not + # be configured. + cls.volume_name = u"CinderUnicodeTest塵㼗‽" + cls.volume = cls.create_volume_with_args(name=cls.volume_name) + + @classmethod + def create_volume_with_args(cls, **kwargs): + if 'name' not in kwargs: + kwargs['name'] = data_utils.rand_name('Volume') + + kwargs['size'] = CONF.volume.volume_size + + volume = cls.volumes_client.create_volume(**kwargs)['volume'] + cls.addClassResourceCleanup( + cls.volumes_client.wait_for_resource_deletion, volume['id']) + cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc, + cls.volumes_client.delete_volume, + volume['id']) + waiters.wait_for_volume_resource_status(cls.volumes_client, + volume['id'], + 'available') + + return volume + + def test_create_delete_unicode_volume_name(self): + """Create a volume with a unicode name and view it.""" + + result = self.volumes_client.show_volume(self.volume['id']) + fetched_volume = result['volume'] + self.assertEqual(fetched_volume['name'], + self.volume_name) diff --git a/cinder_tempest_plugin/cinder_clients.py b/cinder_tempest_plugin/cinder_clients.py new file mode 100644 index 0000000..f4c7c0e --- /dev/null +++ b/cinder_tempest_plugin/cinder_clients.py @@ -0,0 +1,40 @@ +# Copyright (c) 2016 Pure Storage, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from tempest import config + +from cinder_tempest_plugin.services import consistencygroups_client +from cinder_tempest_plugin.services import volume_revert_client + +CONF = config.CONF + + +class Manager(object): + def __init__(self, base_manager): + params = { + 'service': CONF.volume.catalog_type, + 'region': CONF.volume.region or CONF.identity.region, + 'endpoint_type': CONF.volume.endpoint_type, + 'build_interval': CONF.volume.build_interval, + 'build_timeout': CONF.volume.build_timeout + } + params.update(base_manager.default_params) + auth_provider = base_manager.auth_provider + + self.consistencygroups_adm_client = ( + consistencygroups_client.ConsistencyGroupsClient(auth_provider, + **params)) + self.volume_revert_client = ( + volume_revert_client.VolumeRevertClient(auth_provider, **params)) diff --git a/cinder_tempest_plugin/config.py b/cinder_tempest_plugin/config.py new file mode 100644 index 0000000..e15a399 --- /dev/null +++ b/cinder_tempest_plugin/config.py @@ -0,0 +1,25 @@ +# Copyright 2016 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo_config import cfg + +cinder_option = [ + cfg.BoolOpt('consistency_group', + default=False, + help='Enable to run Cinder volume consistency group tests'), + cfg.BoolOpt('volume_revert', + default=False, + help='Enable to run Cinder volume revert tests'), +] diff --git a/cinder_tempest_plugin/plugin.py b/cinder_tempest_plugin/plugin.py new file mode 100644 index 0000000..eed4016 --- /dev/null +++ b/cinder_tempest_plugin/plugin.py @@ -0,0 +1,43 @@ +# Copyright 2015 +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import cinder +import os + +from cinder_tempest_plugin import config as project_config + +from tempest import config +from tempest.test_discover import plugins + + +class CinderTempestPlugin(plugins.TempestPlugin): + def load_tests(self): + base_path = os.path.split(os.path.dirname( + os.path.abspath(cinder.__file__)))[0] + test_dir = "cinder_tempest_plugin" + full_test_dir = os.path.join(base_path, test_dir) + return full_test_dir, base_path + + def register_opts(self, conf): + config.register_opt_group( + conf, config.volume_feature_group, + project_config.cinder_option + ) + + def get_opt_lists(self): + return [ + (config.volume_feature_group.name, + project_config.cinder_option), + ] diff --git a/cinder_tempest_plugin/scenario/__init__.py b/cinder_tempest_plugin/scenario/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cinder_tempest_plugin/services/__init__.py b/cinder_tempest_plugin/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cinder_tempest_plugin/services/consistencygroups_client.py b/cinder_tempest_plugin/services/consistencygroups_client.py new file mode 100644 index 0000000..10415d4 --- /dev/null +++ b/cinder_tempest_plugin/services/consistencygroups_client.py @@ -0,0 +1,193 @@ +# Copyright (C) 2015 EMC Corporation. +# Copyright (C) 2016 Pure Storage, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import time + +from oslo_serialization import jsonutils as json +from six.moves import http_client +from tempest import exceptions +from tempest.lib.common import rest_client +from tempest.lib import exceptions as lib_exc + + +class ConsistencyGroupsClient(rest_client.RestClient): + """Client class to send CRUD Volume ConsistencyGroup API requests""" + + def __init__(self, auth_provider, service, region, **kwargs): + super(ConsistencyGroupsClient, self).__init__( + auth_provider, service, region, **kwargs) + + def create_consistencygroup(self, volume_types, **kwargs): + """Creates a consistency group.""" + post_body = {'volume_types': volume_types} + if kwargs.get('availability_zone'): + post_body['availability_zone'] = kwargs.get('availability_zone') + if kwargs.get('name'): + post_body['name'] = kwargs.get('name') + if kwargs.get('description'): + post_body['description'] = kwargs.get('description') + post_body = json.dumps({'consistencygroup': post_body}) + resp, body = self.post('consistencygroups', post_body) + body = json.loads(body) + self.expected_success(http_client.ACCEPTED, resp.status) + return rest_client.ResponseBody(resp, body) + + def create_consistencygroup_from_src(self, **kwargs): + """Creates a consistency group from source.""" + post_body = {} + if kwargs.get('cgsnapshot_id'): + post_body['cgsnapshot_id'] = kwargs.get('cgsnapshot_id') + if kwargs.get('source_cgid'): + post_body['source_cgid'] = kwargs.get('source_cgid') + if kwargs.get('name'): + post_body['name'] = kwargs.get('name') + if kwargs.get('description'): + post_body['description'] = kwargs.get('description') + post_body = json.dumps({'consistencygroup-from-src': post_body}) + resp, body = self.post('consistencygroups/create_from_src', post_body) + body = json.loads(body) + self.expected_success(http_client.ACCEPTED, resp.status) + return rest_client.ResponseBody(resp, body) + + def delete_consistencygroup(self, cg_id): + """Delete a consistency group.""" + post_body = {'force': True} + post_body = json.dumps({'consistencygroup': post_body}) + resp, body = self.post('consistencygroups/%s/delete' % cg_id, + post_body) + self.expected_success(http_client.ACCEPTED, resp.status) + return rest_client.ResponseBody(resp, body) + + def show_consistencygroup(self, cg_id): + """Returns the details of a single consistency group.""" + url = "consistencygroups/%s" % str(cg_id) + resp, body = self.get(url) + body = json.loads(body) + self.expected_success(http_client.OK, resp.status) + return rest_client.ResponseBody(resp, body) + + def list_consistencygroups(self, detail=False): + """Information for all the tenant's consistency groups.""" + url = "consistencygroups" + if detail: + url += "/detail" + resp, body = self.get(url) + body = json.loads(body) + self.expected_success(http_client.OK, resp.status) + return rest_client.ResponseBody(resp, body) + + def create_cgsnapshot(self, consistencygroup_id, **kwargs): + """Creates a consistency group snapshot.""" + post_body = {'consistencygroup_id': consistencygroup_id} + if kwargs.get('name'): + post_body['name'] = kwargs.get('name') + if kwargs.get('description'): + post_body['description'] = kwargs.get('description') + post_body = json.dumps({'cgsnapshot': post_body}) + resp, body = self.post('cgsnapshots', post_body) + body = json.loads(body) + self.expected_success(http_client.ACCEPTED, resp.status) + return rest_client.ResponseBody(resp, body) + + def delete_cgsnapshot(self, cgsnapshot_id): + """Delete a consistency group snapshot.""" + resp, body = self.delete('cgsnapshots/%s' % (str(cgsnapshot_id))) + self.expected_success(http_client.ACCEPTED, resp.status) + return rest_client.ResponseBody(resp, body) + + def show_cgsnapshot(self, cgsnapshot_id): + """Returns the details of a single consistency group snapshot.""" + url = "cgsnapshots/%s" % str(cgsnapshot_id) + resp, body = self.get(url) + body = json.loads(body) + self.expected_success(http_client.OK, resp.status) + return rest_client.ResponseBody(resp, body) + + def list_cgsnapshots(self, detail=False): + """Information for all the tenant's consistency group snapshotss.""" + url = "cgsnapshots" + if detail: + url += "/detail" + resp, body = self.get(url) + body = json.loads(body) + self.expected_success(http_client.OK, resp.status) + return rest_client.ResponseBody(resp, body) + + def wait_for_consistencygroup_status(self, cg_id, status): + """Waits for a consistency group to reach a given status.""" + body = self.show_consistencygroup(cg_id)['consistencygroup'] + cg_status = body['status'] + start = int(time.time()) + + while cg_status != status: + time.sleep(self.build_interval) + body = self.show_consistencygroup(cg_id)['consistencygroup'] + cg_status = body['status'] + if cg_status == 'error': + raise exceptions.ConsistencyGroupException(cg_id=cg_id) + + if int(time.time()) - start >= self.build_timeout: + message = ('Consistency group %s failed to reach %s status ' + '(current %s) within the required time (%s s).' % + (cg_id, status, cg_status, + self.build_timeout)) + raise exceptions.TimeoutException(message) + + def wait_for_consistencygroup_deletion(self, cg_id): + """Waits for consistency group deletion""" + start_time = int(time.time()) + while True: + try: + self.show_consistencygroup(cg_id) + except lib_exc.NotFound: + return + if int(time.time()) - start_time >= self.build_timeout: + raise exceptions.TimeoutException + time.sleep(self.build_interval) + + def wait_for_cgsnapshot_status(self, cgsnapshot_id, status): + """Waits for a consistency group snapshot to reach a given status.""" + body = self.show_cgsnapshot(cgsnapshot_id)['cgsnapshot'] + cgsnapshot_status = body['status'] + start = int(time.time()) + + while cgsnapshot_status != status: + time.sleep(self.build_interval) + body = self.show_cgsnapshot(cgsnapshot_id)['cgsnapshot'] + cgsnapshot_status = body['status'] + if cgsnapshot_status == 'error': + raise exceptions.ConsistencyGroupSnapshotException( + cgsnapshot_id=cgsnapshot_id) + + if int(time.time()) - start >= self.build_timeout: + message = ('Consistency group snapshot %s failed to reach ' + '%s status (current %s) within the required time ' + '(%s s).' % + (cgsnapshot_id, status, cgsnapshot_status, + self.build_timeout)) + raise exceptions.TimeoutException(message) + + def wait_for_cgsnapshot_deletion(self, cgsnapshot_id): + """Waits for consistency group snapshot deletion""" + start_time = int(time.time()) + while True: + try: + self.show_cgsnapshot(cgsnapshot_id) + except lib_exc.NotFound: + return + if int(time.time()) - start_time >= self.build_timeout: + raise exceptions.TimeoutException + time.sleep(self.build_interval) diff --git a/cinder_tempest_plugin/services/volume_revert_client.py b/cinder_tempest_plugin/services/volume_revert_client.py new file mode 100644 index 0000000..0d54701 --- /dev/null +++ b/cinder_tempest_plugin/services/volume_revert_client.py @@ -0,0 +1,34 @@ +# Copyright (C) 2017 Huawei. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo_serialization import jsonutils as json +from tempest.lib.common import rest_client +from tempest.lib.services.volume import base_client + + +class VolumeRevertClient(base_client.BaseClient): + """Client class to send revert to snapshot action API request""" + + def __init__(self, auth_provider, service, region, **kwargs): + super(VolumeRevertClient, self).__init__( + auth_provider, service, region, **kwargs) + + def revert_to_snapshot(self, volume, snapshot_id): + """Revert a volume to snapshot.""" + post_body = {'snapshot_id': snapshot_id} + post_body = json.dumps({'revert': post_body}) + resp, body = self.post('volumes/%s/action' % volume['id'], + post_body) + return rest_client.ResponseBody(resp, body) diff --git a/setup.cfg b/setup.cfg index 879c210..ff06dac 100644 --- a/setup.cfg +++ b/setup.cfg @@ -47,4 +47,4 @@ [entry_points] tempest.test_plugins = - cinder_tests = cinder.tests.tempest.plugin:CinderTempestPlugin + cinder_tests = cinder_tempest_plugin.plugin:CinderTempestPlugin