diff --git a/.zuul.yaml b/.zuul.yaml index c089396..5d0c956 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -26,7 +26,6 @@ - cinder-tempest-plugin-basic-2024-2 - cinder-tempest-plugin-basic-2024-1 - cinder-tempest-plugin-basic-2023-2 - - cinder-tempest-plugin-basic-2023-1 - cinder-tempest-plugin-protection-functional gate: jobs: @@ -40,7 +39,6 @@ - cinder-tempest-plugin-cbak-ceph-2024-2 - cinder-tempest-plugin-cbak-ceph-2024-1 - cinder-tempest-plugin-cbak-ceph-2023-2 - - cinder-tempest-plugin-cbak-ceph-2023-1 - job: name: cinder-tempest-plugin-protection-functional @@ -130,6 +128,8 @@ volume_revert: True devstack_services: barbican: true + # explicitly enable c-bak, as it may be disabled in the parent job + c-bak: true tempest_plugins: - cinder-tempest-plugin irrelevant-files: @@ -284,12 +284,6 @@ parent: cinder-tempest-plugin-cbak-ceph nodeset: openstack-single-node-jammy override-checkout: stable/2023.2 - -- job: - name: cinder-tempest-plugin-cbak-ceph-2023-1 - parent: cinder-tempest-plugin-cbak-ceph - nodeset: openstack-single-node-jammy - override-checkout: stable/2023.1 # variant for pre-Ussuri branches (no volume revert for Ceph), # should this job be used on those branches @@ -429,9 +423,3 @@ parent: cinder-tempest-plugin-basic nodeset: openstack-single-node-jammy override-checkout: stable/2023.2 - -- job: - name: cinder-tempest-plugin-basic-2023-1 - parent: cinder-tempest-plugin-basic - nodeset: openstack-single-node-jammy - override-checkout: stable/2023.1 diff --git a/cinder_tempest_plugin/api/volume/base.py b/cinder_tempest_plugin/api/volume/base.py index 1c64973..e049ff3 100644 --- a/cinder_tempest_plugin/api/volume/base.py +++ b/cinder_tempest_plugin/api/volume/base.py @@ -91,6 +91,10 @@ name = data_utils.rand_name(cls.__name__ + '-Volume') kwargs['name'] = name + if CONF.compute.compute_volume_common_az: + kwargs.setdefault('availability_zone', + CONF.compute.compute_volume_common_az) + volume = cls.volumes_client.create_volume(**kwargs)['volume'] cls.addClassResourceCleanup( cls.volumes_client.wait_for_resource_deletion, volume['id']) diff --git a/cinder_tempest_plugin/api/volume/test_volume_dependency.py b/cinder_tempest_plugin/api/volume/test_volume_dependency.py index 5ea067f..0b0cc8e 100644 --- a/cinder_tempest_plugin/api/volume/test_volume_dependency.py +++ b/cinder_tempest_plugin/api/volume/test_volume_dependency.py @@ -147,8 +147,9 @@ images_client.delete_image(image_id) images_client.wait_for_resource_deletion(image_id) - @testtools.skipUnless(CONF.volume_feature_enabled.volume_image_dep_tests, - reason='Volume/image dependency tests not enabled.') + @testtools.skipUnless( + CONF.volume_feature_enabled.enable_volume_image_dep_tests, + reason='Volume/image dependency tests not enabled.') @utils.services('image', 'volume') @decorators.idempotent_id('7a9fba78-2e4b-42b1-9898-bb4a60685320') def test_image_volume_dependencies_1(self): @@ -174,8 +175,9 @@ self.del_image(image['id']) - @testtools.skipUnless(CONF.volume_feature_enabled.volume_image_dep_tests, - reason='Volume/image dependency tests not enabled.') + @testtools.skipUnless( + CONF.volume_feature_enabled.enable_volume_image_dep_tests, + reason='Volume/image dependency tests not enabled.') @utils.services('image', 'volume') @decorators.idempotent_id('0e20bd6e-440f-41d8-9b5d-fc047ac00423') def test_image_volume_dependencies_2(self): @@ -210,8 +212,9 @@ self.del_image(image['id']) - @testtools.skipUnless(CONF.volume_feature_enabled.volume_image_dep_tests, - reason='Volume/image dependency tests not enabled.') + @testtools.skipUnless( + CONF.volume_feature_enabled.enable_volume_image_dep_tests, + reason='Volume/image dependency tests not enabled.') @decorators.idempotent_id('e6050452-06bd-4c7f-9912-45178c83e379') @utils.services('image', 'volume') def test_image_volume_dependencies_3(self): diff --git a/cinder_tempest_plugin/config.py b/cinder_tempest_plugin/config.py index 53222b8..3d5eb0e 100644 --- a/cinder_tempest_plugin/config.py +++ b/cinder_tempest_plugin/config.py @@ -24,7 +24,13 @@ help='Enable to run Cinder volume revert tests'), cfg.BoolOpt('volume_image_dep_tests', default=True, - help='Run tests for dependencies between images and volumes') + help='Run tests for dependencies between images and volumes', + deprecated_for_removal=True, + deprecated_reason='Dependency test config setting ' + '`volume_image_dep_tests` ' + 'in cinder-tempest-plugin is deprecated.Alternatively ' + '`CONF.volume_feature_enabled.enable_volume_image_dep_tests` ' + 'can be used for dependency tests.') ] # The barbican service is discovered by config_tempest [1], and will appear diff --git a/cinder_tempest_plugin/rbac/v3/test_volume_types.py b/cinder_tempest_plugin/rbac/v3/test_volume_types.py index cdbc341..e4e161e 100644 --- a/cinder_tempest_plugin/rbac/v3/test_volume_types.py +++ b/cinder_tempest_plugin/rbac/v3/test_volume_types.py @@ -20,7 +20,24 @@ class RbacV3VolumeTypesTests(rbac_base.VolumeV3RbacBaseTests): min_microversion = '3.3' - extra_spec_key = 'key1' + extra_specs = { + 'key1': 'value1', + 'multiattach': ' False', + 'volume_backend_name': 'test-backend-name', + 'RESKEY:availability_zones': 'test-az', + 'replication_enabled': ' False' + } + extra_specs_keys = list(extra_specs.keys()) + expected_extra_specs = { + "reader": [ + 'multiattach', 'RESKEY:availability_zones', 'replication_enabled' + ], + "member": [ + 'multiattach', 'RESKEY:availability_zones', 'replication_enabled' + ], + "admin": extra_specs_keys + } + encryption_type_key_cipher = 'cipher' create_kwargs = { 'provider': 'LuksEncryptor', @@ -52,10 +69,9 @@ # create a volume type if not name: name = data_utils.rand_name("volume-type") - extra_specs = {cls.extra_spec_key: 'value1'} params = {'name': name, 'description': "description", - 'extra_specs': extra_specs, + 'extra_specs': cls.extra_specs, 'os-volume-type-access:is_public': True} volume_type = cls.admin_types_client.create_volume_type( **params @@ -66,6 +82,9 @@ cls.encryption_type = \ cls.admin_encryption_types_client.create_encryption_type( volume_type['id'], **cls.create_kwargs)['encryption'] + # NOTE: strictly speaking, this is NOT a volume_type field; + # we save it for convenience in these tests + volume_type['encryption_id'] = cls.encryption_type['encryption_id'] if cleanup: cls.addClassResourceCleanup( @@ -73,6 +92,28 @@ ) return volume_type + + def _extra_specs_content_validator(self, client, extra_specs): + """Validation of volume type's extra specs content + + Addition for feature: + https://specs.openstack.org/openstack/cinder-specs/specs/xena/ + expose-cinder-user-visible-extra-specs-spec.html + + This feature allows 'readers' and 'members' to "see" volume type's + extra specs: + 'multiattach', 'RESKEY:availability_zones' and 'replication_enabled' + + Args: + client: Client object to be used + extra_specs: extra_specs dict from response + + Returns: + Boolean: True if lists are equal, false otherwise + """ + role = client.user.split('-')[-1] + return (sorted(list(extra_specs.keys())) == + sorted(self.expected_extra_specs[role])) def _update_volume_type(self, expected_status): """Update volume type""" @@ -112,30 +153,65 @@ expected_status=expected_status, volume_type_id=self.volume_type['id'] )['extra_specs'] - self.assertIn( - self.extra_spec_key, - list(extra_specs.keys()), - message=f"Key '{self.extra_spec_key}' not found in extra_specs." + self.assertTrue( + self._extra_specs_content_validator( + client=self.client, extra_specs=extra_specs + ) ) def _show_extra_spec_for_volume_type(self, expected_status): """Show extra_spec for a volume type""" - self.do_request( + + # Using 'multiattach' extra spec because all admin, member and readers + # should be able to "see". + spec = self.do_request( method='show_volume_type_extra_specs', expected_status=expected_status, volume_type_id=self.volume_type['id'], - extra_specs_name=self.extra_spec_key + extra_specs_name='multiattach' + ) + self.assertEqual(spec['multiattach'], self.extra_specs['multiattach']) + + # Using 'volume_backend_name' extra spec because only admin should + # "see" it. + role = self.client.user.split('-')[-1] + # 'reader' and 'member' will get 404 (NotFound) if they try to show + # the extra spec 'volume_backend_name' + try: + spec = self.do_request( + method='show_volume_type_extra_specs', + expected_status=expected_status, + volume_type_id=self.volume_type['id'], + extra_specs_name='volume_backend_name' + ) + except exceptions.NotFound: + # NotFound exception should be thrown for + # 'reader' and 'member' only + self.assertNotEqual( + role, 'admin', + "NotFound exception was thrown for admin" + ) + return + + # If no exception thrown, then check the content + # Only admin should reach to this point + self.assertNotIn( + role, ['reader', 'member'], + "NotFound should be thrown for non admin role" + ) + self.assertEqual( + spec['volume_backend_name'], + self.extra_specs['volume_backend_name'] ) def _update_extra_spec_for_volume_type(self, expected_status): """Update extra_spec for a volume type""" - spec_name = self.extra_spec_key - extra_spec = {spec_name: 'updated value'} + extra_spec = {'key1': 'key1 updated value'} self.do_request( method='update_volume_type_extra_specs', expected_status=expected_status, volume_type_id=self.volume_type['id'], - extra_spec_name=spec_name, + extra_spec_name='key1', extra_specs=extra_spec ) @@ -147,15 +223,20 @@ method='delete_volume_type_extra_specs', expected_status=expected_status, volume_type_id=volume_type['id'], - extra_spec_name=self.extra_spec_key + extra_spec_name='key1' ) def _show_volume_type_detail(self, expected_status): """Show volume type""" - self.do_request( + details = self.do_request( method='show_volume_type', expected_status=expected_status, volume_type_id=self.volume_type['id'] + )['volume_type'] + self.assertTrue( + self._extra_specs_content_validator( + client=self.client, extra_specs=details['extra_specs'] + ) ) def _show_default_volume_type(self, expected_status): @@ -181,10 +262,15 @@ def _list_volume_types(self, expected_status): """List all volume types""" - self.do_request( + volume_types = self.do_request( method='list_volume_types', expected_status=expected_status - ) + )['volume_types'] + for volume_type in volume_types: + if volume_type['id'] == self.volume_type['id']: + self._extra_specs_content_validator( + client=self.client, extra_specs=volume_type['extra_specs'] + ) def _create_volume_type(self, expected_status): """Create a volume type""" @@ -226,7 +312,8 @@ method='delete_encryption_type', expected_status=expected_status, client=self.encryption_types_client, - volume_type_id=volume_type['id'] + volume_type_id=volume_type['id'], + encryption_id=volume_type['encryption_id'] ) def _create_encryption_type(self, expected_status): @@ -250,6 +337,7 @@ expected_status=expected_status, client=self.encryption_types_client, volume_type_id=self.volume_type['id'], + encryption_id=self.volume_type['encryption_id'], **update_kwargs ) @@ -275,14 +363,12 @@ expected_status=exceptions.Forbidden ) - @decorators.skip_because(bug='2018467') @decorators.idempotent_id('9499752c-3b27-41a3-8f55-4bdba7297f92') def test_list_all_extra_specs_for_volume_type(self): self._list_all_extra_specs_for_volume_type( expected_status=200 ) - @decorators.skip_because(bug='2018467') @decorators.idempotent_id('a38f7248-3a5b-4e51-8e32-d2dcf9c771ea') def test_show_extra_spec_for_volume_type(self): self._show_extra_spec_for_volume_type(expected_status=200) @@ -364,14 +450,12 @@ expected_status=exceptions.Forbidden ) - @decorators.skip_because(bug='2018467') @decorators.idempotent_id('82fd0d34-17b3-4f45-bd2e-728c9a8bff8c') def test_list_all_extra_specs_for_volume_type(self): self._list_all_extra_specs_for_volume_type( expected_status=200 ) - @decorators.skip_because(bug='2018467') @decorators.idempotent_id('67aa0b40-7c0a-4ae7-8682-fb4f20abd390') def test_show_extra_spec_for_volume_type(self): self._show_extra_spec_for_volume_type(expected_status=200) @@ -457,7 +541,6 @@ expected_status=200 ) - @decorators.skip_because(bug='2018467') @decorators.idempotent_id('a2cca7b6-0af9-47e5-b8c1-4e0f01822d4e') def test_show_extra_spec_for_volume_type(self): self._show_extra_spec_for_volume_type(expected_status=200) diff --git a/cinder_tempest_plugin/scenario/test_snapshots.py b/cinder_tempest_plugin/scenario/test_snapshots.py index 02cd6bd..7b8191b 100644 --- a/cinder_tempest_plugin/scenario/test_snapshots.py +++ b/cinder_tempest_plugin/scenario/test_snapshots.py @@ -130,8 +130,9 @@ class SnapshotDependencyTests(manager.ScenarioTest): - @testtools.skipUnless(CONF.volume_feature_enabled.volume_image_dep_tests, - 'dependency tests not enabled') + @testtools.skipUnless( + CONF.volume_feature_enabled.enable_volume_image_dep_tests, + 'dependency tests not enabled') @decorators.idempotent_id('e7028f52-f6d4-479c-8809-6f6cf96cfe0f') @utils.services('image', 'volume') def test_snapshot_removal(self): diff --git a/releasenotes/notes/cinder-tempest-plugin-volume_image_dep_tests-ba46faab68dfb799.yaml b/releasenotes/notes/cinder-tempest-plugin-volume_image_dep_tests-ba46faab68dfb799.yaml new file mode 100644 index 0000000..9390dd9 --- /dev/null +++ b/releasenotes/notes/cinder-tempest-plugin-volume_image_dep_tests-ba46faab68dfb799.yaml @@ -0,0 +1,6 @@ +deprecations: + - | + Dependency test config setting 'volume_image_dep_tests' + in cinder-tempest-plugin is deprecated.Alternatively tempest + `CONF.volume_feature_enabled.enable_volume_image_dep_tests` can be used + to for dependency tests. diff --git a/requirements.txt b/requirements.txt index c25d1c5..aa631de 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,4 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 oslo.config>=5.1.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 -tempest>=34.2.0 # Apache-2.0 +tempest>=40.0.0 # Apache-2.0