Merge "Remove unused babel.cfg"
diff --git a/.zuul.yaml b/.zuul.yaml
index 986b1ce..03bf740 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -21,13 +21,11 @@
         - cinder-tempest-plugin-cbak-ceph
         - cinder-tempest-plugin-cbak-s3
         # As per the Tempest "Stable Branch Support Policy", Tempest will only
-        # support the "Maintained" stable branches and not the "Extended Maintained"
-        # branches. That is what we need to do for all tempest plugins. Only jobs
-        # for the current releasable ("Maintained") stable branches should be listed
-        # here.
-        - cinder-tempest-plugin-basic-zed
-        - cinder-tempest-plugin-basic-yoga
-        - cinder-tempest-plugin-basic-xena
+        # support the "Maintained" stable branches, so only jobs for the
+        # current stable branches should be listed here.
+        - 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:
@@ -38,9 +36,9 @@
         - cinder-tempest-plugin-cbak-ceph
     experimental:
       jobs:
-        - cinder-tempest-plugin-cbak-ceph-zed
-        - cinder-tempest-plugin-cbak-ceph-yoga
-        - cinder-tempest-plugin-cbak-ceph-xena
+        - 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
@@ -51,10 +49,13 @@
     vars:
       tox_envlist: all
       tempest_test_regex: 'cinder_tempest_plugin.rbac'
+      devstack_localrc:
+        KEYSTONE_ENFORCE_SCOPE: True
       devstack_local_conf:
         test-config:
           $CINDER_CONF:
             oslo_policy:
+              enforce_scope: True
               enforce_new_defaults: True
           $TEMPEST_CONFIG:
             enforce_scope:
@@ -72,6 +73,7 @@
       - opendev.org/openstack/cinder-tempest-plugin
       - opendev.org/openstack/cinder
     vars:
+      configure_swap_size: 8192
       tempest_test_regex: '(^tempest\.(api|scenario)|(^cinder_tempest_plugin))'
       tempest_test_exclude_list: '{{ ansible_user_dir }}/{{ zuul.projects["opendev.org/openstack/tempest"].src_dir }}/tools/tempest-integrated-gate-storage-exclude-list.txt'
       # Temporarily exclude TestMultiAttachVolumeSwap until LP bug #1980816 is resolved.
@@ -106,6 +108,7 @@
         devstack_plugins:
           barbican: https://opendev.org/openstack/barbican
     vars:
+      configure_swap_size: 8192
       tempest_test_regex: '(^tempest\.(api|scenario)|(^cinder_tempest_plugin))'
       tox_envlist: all
       devstack_localrc:
@@ -134,9 +137,22 @@
 - job:
     name: cinder-tempest-plugin-lvm-barbican-base
     description: |
+      This is a base job for lvm with lio & tgt targets.
+      No cinderlib testing beginning with 2024.1 development.
+    # FIXME: the following RE2 expression won't work after the 9999.2 release.
+    # If you are reading this during the 9999.2 development cycle, greetings
+    # from the 21st century!
+    branches: ^(master|(stable/(202[4-9]|20[3-9]\d|2[1-9]\d\d|[3-9]\d\d\d))\.[12])$
+    parent: cinder-tempest-plugin-lvm-barbican-base-abstract
+    vars:
+      tempest_test_exclude_list: '{{ ansible_user_dir }}/{{ zuul.projects["opendev.org/openstack/tempest"].src_dir }}/tools/tempest-integrated-gate-storage-exclude-list.txt'
+
+- job:
+    name: cinder-tempest-plugin-lvm-barbican-base
+    description: |
       This is a base job for lvm with lio & tgt targets
       with cinderlib tests.
-    branches: ^(?!stable/(ocata|pike|queens|rocky|stein|train|ussuri|victoria|wallaby)).*$
+    branches: ^(stable/(xena|yoga|zed|2023\.[12]))$
     parent: cinder-tempest-plugin-lvm-barbican-base-abstract
     roles:
       - zuul: opendev.org/openstack/cinderlib
@@ -214,7 +230,6 @@
       - stable/ussuri
     parent: cinder-tempest-plugin-lvm-barbican-base-abstract
     required-projects:
-      - opendev.org/openstack/cinderlib
       - name: opendev.org/openstack/cinder-tempest-plugin
         override-checkout: 1.3.0
     vars:
@@ -224,7 +239,7 @@
     name: cinder-tempest-plugin-lvm-barbican-base
     description: |
       This is a base job for lvm with lio & tgt targets
-    branches: ^(?=stable/(ocata|pike|queens|rocky|stein)).*$
+    branches: ^stable/(ocata|pike|queens|rocky|stein).*$
     parent: cinder-tempest-plugin-lvm-barbican-base-abstract
     required-projects:
       - name: opendev.org/openstack/cinder-tempest-plugin
@@ -239,6 +254,11 @@
       Integration tests that runs with the ceph devstack plugin, py3
       and enable the backup service.
     vars:
+      # FIXME: change I29b1af0a4034decad to tempest added image format tests that
+      # cannot pass in this job because the image data takes a optimized path that
+      # bypasses nova's checks.  Until the nova team decides on a strategy to handle
+      # this issue, we skip these tests.
+      tempest_exclude_regex: (tempest.api.image.v2.test_images_formats.ImagesFormatTest.test_compute_rejects)
       configure_swap_size: 4096
       devstack_local_conf:
         test-config:
@@ -252,34 +272,28 @@
     timeout: 10800
 
 - job:
-    name: cinder-tempest-plugin-cbak-ceph-zed
+    name: cinder-tempest-plugin-cbak-ceph-2024-1
     parent: cinder-tempest-plugin-cbak-ceph
-    nodeset: openstack-single-node-focal
-    override-checkout: stable/zed
+    nodeset: openstack-single-node-jammy
+    override-checkout: stable/2024.1
 
 - job:
-    name: cinder-tempest-plugin-cbak-ceph-yoga
+    name: cinder-tempest-plugin-cbak-ceph-2023-2
     parent: cinder-tempest-plugin-cbak-ceph
-    nodeset: openstack-single-node-focal
-    override-checkout: stable/yoga
+    nodeset: openstack-single-node-jammy
+    override-checkout: stable/2023.2
 
 - job:
-    name: cinder-tempest-plugin-cbak-ceph-xena
+    name: cinder-tempest-plugin-cbak-ceph-2023-1
     parent: cinder-tempest-plugin-cbak-ceph
-    nodeset: openstack-single-node-focal
-    override-checkout: stable/xena
-
-- job:
-    name: cinder-tempest-plugin-cbak-ceph-wallaby
-    parent: cinder-tempest-plugin-cbak-ceph
-    nodeset: openstack-single-node-focal
-    override-checkout: stable/wallaby
+    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
 - job:
     name: cinder-tempest-plugin-cbak-ceph
-    branches: ^(?=stable/(ocata|pike|queens|rocky|stein|train)).*$
+    branches: ^stable/(ocata|pike|queens|rocky|stein|train).*$
     vars:
       devstack_local_conf:
         test-config:
@@ -295,7 +309,7 @@
     parent: cinder-tempest-plugin-lvm-barbican-base
     vars:
       devstack_localrc:
-        CINDER_ISCSI_HELPER: lioadm
+        CINDER_TARGET_HELPER: lioadm
 
 - job:
     name: cinder-tempest-plugin-lvm-lio-barbican-centos-8-stream
@@ -322,7 +336,6 @@
       under FIPS mode
     pre-run: playbooks/enable-fips.yaml
     vars:
-      configure_swap_size: 4096
       nslookup_target: 'opendev.org'
       tempest_exclude_regex: 'test_encrypted_cinder_volumes_cryptsetup'
 
@@ -368,7 +381,7 @@
     parent: cinder-tempest-plugin-lvm-barbican-base
     vars:
       devstack_localrc:
-        CINDER_ISCSI_HELPER: tgtadm
+        CINDER_TARGET_HELPER: tgtadm
 
 - job:
     name: cinder-tempest-plugin-cbak-s3
@@ -406,19 +419,19 @@
       - ^releasenotes/.*$
 
 - job:
-    name: cinder-tempest-plugin-basic-zed
+    name: cinder-tempest-plugin-basic-2024-1
     parent: cinder-tempest-plugin-basic
-    nodeset: openstack-single-node-focal
-    override-checkout: stable/zed
+    nodeset: openstack-single-node-jammy
+    override-checkout: stable/2024.1
 
 - job:
-    name: cinder-tempest-plugin-basic-yoga
+    name: cinder-tempest-plugin-basic-2023-2
     parent: cinder-tempest-plugin-basic
-    nodeset: openstack-single-node-focal
-    override-checkout: stable/yoga
+    nodeset: openstack-single-node-jammy
+    override-checkout: stable/2023.2
 
 - job:
-    name: cinder-tempest-plugin-basic-xena
+    name: cinder-tempest-plugin-basic-2023-1
     parent: cinder-tempest-plugin-basic
-    nodeset: openstack-single-node-focal
-    override-checkout: stable/xena
+    nodeset: openstack-single-node-jammy
+    override-checkout: stable/2023.1
diff --git a/cinder_tempest_plugin/api/volume/admin/test_consistencygroups.py b/cinder_tempest_plugin/api/volume/admin/test_consistencygroups.py
index 7dff494..42c78f1 100644
--- a/cinder_tempest_plugin/api/volume/admin/test_consistencygroups.py
+++ b/cinder_tempest_plugin/api/volume/admin/test_consistencygroups.py
@@ -28,9 +28,7 @@
 class ConsistencyGroupsV2Test(base.BaseVolumeAdminTest):
     @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
diff --git a/cinder_tempest_plugin/api/volume/admin/test_volume_backup.py b/cinder_tempest_plugin/api/volume/admin/test_volume_backup.py
index e5ded52..41cfc91 100644
--- a/cinder_tempest_plugin/api/volume/admin/test_volume_backup.py
+++ b/cinder_tempest_plugin/api/volume/admin/test_volume_backup.py
@@ -24,13 +24,6 @@
 
 class VolumesBackupsTest(base.BaseVolumeAdminTest):
     @classmethod
-    def setup_clients(cls):
-        super(VolumesBackupsTest, cls).setup_clients()
-        cls.admin_volume_client = cls.os_admin.volumes_client_latest
-        cls.backups_client = cls.os_primary.backups_client_latest
-        cls.volumes_client = cls.os_primary.volumes_client_latest
-
-    @classmethod
     def skip_checks(cls):
         super(VolumesBackupsTest, cls).skip_checks()
         if not CONF.volume_feature_enabled.backup:
diff --git a/cinder_tempest_plugin/api/volume/base.py b/cinder_tempest_plugin/api/volume/base.py
index ea6bd2e..c0f53bd 100644
--- a/cinder_tempest_plugin/api/volume/base.py
+++ b/cinder_tempest_plugin/api/volume/base.py
@@ -13,10 +13,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import io
+
 from tempest.common import compute
 from tempest.common import waiters
 from tempest import config
-from tempest.lib.common import api_microversion_fixture
 from tempest.lib.common import api_version_utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
@@ -57,8 +58,6 @@
 
     def setUp(self):
         super(BaseVolumeTest, self).setUp()
-        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
-            volume_microversion=self.request_microversion))
 
     @classmethod
     def resource_setup(cls):
@@ -67,6 +66,8 @@
             api_version_utils.select_request_microversion(
                 cls.min_microversion,
                 CONF.volume.min_microversion))
+        cls.setup_api_microversion_fixture(
+            volume_microversion=cls.request_microversion)
 
     @classmethod
     def create_volume(cls, wait_until='available', **kwargs):
@@ -159,6 +160,29 @@
                         body['id'])
         return body
 
+    @classmethod
+    def create_image_with_data(cls, **kwargs):
+        # we do this as a class method so we can use the
+        # addClassResourceCleanup functionality of tempest.test.BaseTestCase
+        images_client = cls.os_primary.image_client_v2
+        if 'min_disk' not in kwargs:
+            kwargs['min_disk'] = 1
+        response = images_client.create_image(**kwargs)
+        image_id = response['id']
+        cls.addClassResourceCleanup(
+            images_client.wait_for_resource_deletion, image_id)
+        cls.addClassResourceCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            images_client.delete_image, image_id)
+
+        # upload "data" to image
+        image_file = io.BytesIO(data_utils.random_bytes(size=1024))
+        images_client.store_image_file(image_id, image_file)
+
+        waiters.wait_for_image_status(images_client, image_id, 'active')
+        image = images_client.show_image(image_id)
+        return image
+
 
 class BaseVolumeAdminTest(BaseVolumeTest):
     """Base test case class for all Volume Admin API tests."""
@@ -171,7 +195,7 @@
 
         cls.admin_volume_types_client = cls.os_admin.volume_types_client_latest
         cls.admin_backups_client = cls.os_admin.backups_client_latest
-        cls.admin_volumes_client = cls.os_admin.volumes_client_latest
+        cls.admin_volume_client = cls.os_admin.volumes_client_latest
 
     @classmethod
     def create_volume_type(cls, name=None, **kwargs):
@@ -194,12 +218,12 @@
         type_id = volume_type['id']
         type_name = volume_type['name']
 
-        volumes = cls.admin_volumes_client.list_volumes(
+        volumes = cls.admin_volume_client.list_volumes(
             detail=True, params={'all_tenants': 1})['volumes']
         for volume in [v for v in volumes if v['volume_type'] == type_name]:
             test_utils.call_and_ignore_notfound_exc(
-                cls.admin_volumes_client.delete_volume, volume['id'])
-            cls.admin_volumes_client.wait_for_resource_deletion(volume['id'])
+                cls.admin_volume_client.delete_volume, volume['id'])
+            cls.admin_volume_client.wait_for_resource_deletion(volume['id'])
 
         test_utils.call_and_ignore_notfound_exc(
             cls.admin_volume_types_client.delete_volume_type, type_id)
diff --git a/cinder_tempest_plugin/api/volume/test_create_from_image.py b/cinder_tempest_plugin/api/volume/test_create_from_image.py
index acb1943..f44f630 100644
--- a/cinder_tempest_plugin/api/volume/test_create_from_image.py
+++ b/cinder_tempest_plugin/api/volume/test_create_from_image.py
@@ -10,12 +10,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import io
-
-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 tempest.lib import decorators
 
 from cinder_tempest_plugin.api.volume import base
@@ -32,29 +27,6 @@
         if not CONF.service_available.glance:
             raise cls.skipException("Glance service is disabled")
 
-    @classmethod
-    def create_image_with_data(cls, **kwargs):
-        # we do this as a class method so we can use the
-        # addClassResourceCleanup functionality of tempest.test.BaseTestCase
-        images_client = cls.os_primary.image_client_v2
-        if 'min_disk' not in kwargs:
-            kwargs['min_disk'] = 1
-        response = images_client.create_image(**kwargs)
-        image_id = response['id']
-        cls.addClassResourceCleanup(
-            images_client.wait_for_resource_deletion, image_id)
-        cls.addClassResourceCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            images_client.delete_image, image_id)
-
-        # upload "data" to image
-        image_file = io.BytesIO(data_utils.random_bytes(size=1024))
-        images_client.store_image_file(image_id, image_file)
-
-        waiters.wait_for_image_status(images_client, image_id, 'active')
-        image = images_client.show_image(image_id)
-        return image
-
     @decorators.idempotent_id('6e9266ff-a917-4dd5-aa4a-c36e59e7a2a6')
     def test_create_from_image_with_volume_type_image_property(self):
         """Verify that the cinder_img_volume_type image property works.
diff --git a/cinder_tempest_plugin/api/volume/test_volume_dependency.py b/cinder_tempest_plugin/api/volume/test_volume_dependency.py
new file mode 100644
index 0000000..5ea067f
--- /dev/null
+++ b/cinder_tempest_plugin/api/volume/test_volume_dependency.py
@@ -0,0 +1,255 @@
+# Copyright 2022 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 utils
+from tempest.common import waiters
+from tempest import config
+from tempest.lib import decorators
+import testtools
+
+from cinder_tempest_plugin.api.volume import base
+
+CONF = config.CONF
+
+
+class VolumeDependencyTests(base.BaseVolumeTest):
+    min_microversion = '3.40'
+
+    @classmethod
+    def setup_clients(cls):
+        super(VolumeDependencyTests, cls).setup_clients()
+
+    @decorators.idempotent_id('42e9df95-854b-4840-9d55-ae62f65e9b8e')
+    def test_delete_source_volume(self):
+        """Test basic dependency deletion
+
+        * Create a volume with source_volid
+        * Delete the source volume
+        """
+        source_volume = self.create_volume()
+        kwargs = {'source_volid': source_volume['id']}
+        cloned_volume = self.create_volume(**kwargs)
+        self.assertEqual(source_volume['id'], cloned_volume['source_volid'])
+        self.volumes_client.delete_volume(source_volume['id'])
+        self.volumes_client.wait_for_resource_deletion(source_volume['id'])
+
+    @decorators.idempotent_id('900d8ea5-2afd-4fe5-a0c3-fab4744f0d40')
+    def test_delete_source_snapshot(self):
+        """Test basic dependency deletion with snapshot
+
+        * Create a snapshot from source volume
+        * Create a volume from that snapshot
+        * Delete the source snapshot
+        * Delete the source volume
+        """
+        source_volume = self.create_volume()
+        snapshot_source_volume = self.create_snapshot(source_volume['id'])
+        kwargs = {'snapshot_id': snapshot_source_volume['id']}
+        volume_from_snapshot = self.create_volume(**kwargs)
+        self.assertEqual(volume_from_snapshot['snapshot_id'],
+                         snapshot_source_volume['id'])
+
+        self.snapshots_client.delete_snapshot(snapshot_source_volume['id'])
+        self.snapshots_client.wait_for_resource_deletion(
+            snapshot_source_volume['id'])
+        self.volumes_client.delete_volume(source_volume['id'])
+        self.volumes_client.wait_for_resource_deletion(source_volume['id'])
+
+    def _delete_vol_and_wait(self, vol_id):
+        self.volumes_client.delete_volume(vol_id)
+
+        self.volumes_client.wait_for_resource_deletion(vol_id)
+
+    def _delete_snap_and_wait(self, snap_id):
+        self.snapshots_client.delete_snapshot(snap_id)
+
+        self.snapshots_client.wait_for_resource_deletion(snap_id)
+
+    @decorators.idempotent_id('f8278e5c-50ff-4a1d-8670-3ca0866d411a')
+    def test_delete_dep_chain(self):
+        """Test a complex chain of volume and snapshot dependency deletion."""
+        volume_1 = self.create_volume()['id']
+        snapshot_of_vol_1 = self.create_snapshot(volume_1)['id']
+
+        volume_2_args = {'snapshot_id': snapshot_of_vol_1}
+        volume_2 = self.create_volume(**volume_2_args)['id']
+
+        snapshot_of_vol_2 = self.create_snapshot(volume_2)['id']
+
+        volume_3_args = {'snapshot_id': snapshot_of_vol_2}
+        volume_3 = self.create_volume(**volume_3_args)['id']
+
+        volume_4_args = {'source_volid': volume_3}
+        volume_4 = self.create_volume(**volume_4_args)['id']
+
+        self._delete_snap_and_wait(snapshot_of_vol_1)
+        self._delete_snap_and_wait(snapshot_of_vol_2)
+
+        self._delete_vol_and_wait(volume_3)
+        self._delete_vol_and_wait(volume_1)
+        self._delete_vol_and_wait(volume_2)
+        self._delete_vol_and_wait(volume_4)
+
+    @decorators.idempotent_id('63447ef8-e667-4796-ba66-1b9b883af1f1')
+    def test_delete_dep_chain_2(self):
+        """Test a different chain of volume/snapshot dependency deletion."""
+        volume_1 = self.create_volume()['id']
+        snapshot_of_vol_1 = self.create_snapshot(volume_1)['id']
+
+        volume_2_args = {'snapshot_id': snapshot_of_vol_1}
+        volume_2 = self.create_volume(**volume_2_args)['id']
+
+        snapshot_of_vol_2 = self.create_snapshot(volume_2)['id']
+
+        volume_3_args = {'snapshot_id': snapshot_of_vol_2}
+        volume_3 = self.create_volume(**volume_3_args)['id']
+
+        self._delete_snap_and_wait(snapshot_of_vol_1)
+        self._delete_snap_and_wait(snapshot_of_vol_2)
+
+        self._delete_vol_and_wait(volume_1)
+        self._delete_vol_and_wait(volume_2)
+        self._delete_vol_and_wait(volume_3)
+
+
+class VolumeImageDependencyTests(base.BaseVolumeTest):
+    """Volume<->image dependency tests.
+
+    These tests perform clones to/from volumes and images,
+    deleting images/volumes that other volumes were cloned from.
+
+    Images and volumes are expected to be independent at the OpenStack
+    level, but in some configurations (i.e. when using Ceph as storage
+    for both Cinder and Glance) it was possible to end up with images
+    or volumes that could not be deleted.  This was fixed for RBD in
+    Cinder 2024.1 change I009d0748f.
+
+    """
+
+    min_microversion = '3.40'
+
+    @classmethod
+    def del_image(cls, image_id):
+        images_client = cls.os_primary.image_client_v2
+        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.')
+    @utils.services('image', 'volume')
+    @decorators.idempotent_id('7a9fba78-2e4b-42b1-9898-bb4a60685320')
+    def test_image_volume_dependencies_1(self):
+        # image -> volume
+        image_args = {
+            'disk_format': 'raw',
+            'container_format': 'bare',
+            'name': 'image-for-test-7a9fba78-2e4b-42b1-9898-bb4a60685320'
+        }
+        image = self.create_image_with_data(**image_args)
+
+        # create a volume from the image
+        vol_args = {'name': ('volume1-for-test'
+                             '7a9fba78-2e4b-42b1-9898-bb4a60685320'),
+                    'imageRef': image['id']}
+        volume1 = self.create_volume(**vol_args)
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume1['id'],
+                                                'available')
+
+        self.volumes_client.delete_volume(volume1['id'])
+        self.volumes_client.wait_for_resource_deletion(volume1['id'])
+
+        self.del_image(image['id'])
+
+    @testtools.skipUnless(CONF.volume_feature_enabled.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):
+        # image -> volume -> volume
+
+        image_args = {
+            'disk_format': 'raw',
+            'container_format': 'bare',
+            'name': 'image-for-test-0e20bd6e-440f-41d8-9b5d-fc047ac00423'
+        }
+        image = self.create_image_with_data(**image_args)
+
+        # create a volume from the image
+        vol_args = {'name': ('volume1-for-test'
+                             '0e20bd6e-440f-41d8-9b5d-fc047ac00423'),
+                    'imageRef': image['id']}
+        volume1 = self.create_volume(**vol_args)
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume1['id'],
+                                                'available')
+
+        vol2_args = {'name': ('volume2-for-test-'
+                              '0e20bd6e-440f-41d8-9b5d-fc047ac00423'),
+                     'source_volid': volume1['id']}
+        volume2 = self.create_volume(**vol2_args)
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume2['id'],
+                                                'available')
+
+        self.volumes_client.delete_volume(volume1['id'])
+        self.volumes_client.wait_for_resource_deletion(volume1['id'])
+
+        self.del_image(image['id'])
+
+    @testtools.skipUnless(CONF.volume_feature_enabled.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):
+        # image -> volume -> snap -> volume
+
+        image_args = {
+            'disk_format': 'raw',
+            'container_format': 'bare',
+            'name': 'image-for-test-e6050452-06bd-4c7f-9912-45178c83e379'
+        }
+        image = self.create_image_with_data(**image_args)
+
+        # create a volume from the image
+        vol_args = {'name': ('volume1-for-test'
+                             'e6050452-06bd-4c7f-9912-45178c83e379'),
+                    'imageRef': image['id']}
+        volume1 = self.create_volume(**vol_args)
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume1['id'],
+                                                'available')
+
+        snapshot1 = self.create_snapshot(volume1['id'])
+
+        vol2_args = {'name': ('volume2-for-test-'
+                              'e6050452-06bd-4c7f-9912-45178c83e379'),
+                     'snapshot_id': snapshot1['id']}
+        volume2 = self.create_volume(**vol2_args)
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume2['id'],
+                                                'available')
+
+        self.snapshots_client.delete_snapshot(snapshot1['id'])
+        self.snapshots_client.wait_for_resource_deletion(snapshot1['id'])
+
+        self.volumes_client.delete_volume(volume2['id'])
+        self.volumes_client.wait_for_resource_deletion(volume2['id'])
+
+        self.del_image(image['id'])
+
+        self.volumes_client.delete_volume(volume1['id'])
+        self.volumes_client.wait_for_resource_deletion(volume1['id'])
diff --git a/cinder_tempest_plugin/api/volume/test_volume_revert.py b/cinder_tempest_plugin/api/volume/test_volume_revert.py
index bf3d806..2c4d6bd 100644
--- a/cinder_tempest_plugin/api/volume/test_volume_revert.py
+++ b/cinder_tempest_plugin/api/volume/test_volume_revert.py
@@ -35,7 +35,6 @@
 
     @classmethod
     def setup_clients(cls):
-        cls._api_version = 3
         super(VolumeRevertTests, cls).setup_clients()
 
         manager = cinder_clients.Manager(cls.os_primary)
diff --git a/cinder_tempest_plugin/config.py b/cinder_tempest_plugin/config.py
index 78dd6ea..53222b8 100644
--- a/cinder_tempest_plugin/config.py
+++ b/cinder_tempest_plugin/config.py
@@ -22,6 +22,9 @@
     cfg.BoolOpt('volume_revert',
                 default=False,
                 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')
 ]
 
 # The barbican service is discovered by config_tempest [1], and will appear
diff --git a/cinder_tempest_plugin/rbac/v3/base.py b/cinder_tempest_plugin/rbac/v3/base.py
index af724a3..6fc6be5 100644
--- a/cinder_tempest_plugin/rbac/v3/base.py
+++ b/cinder_tempest_plugin/rbac/v3/base.py
@@ -13,7 +13,6 @@
 
 from tempest.common import waiters
 from tempest import config
-from tempest.lib.common import api_microversion_fixture
 from tempest.lib.common import api_version_utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
@@ -51,8 +50,6 @@
 
     def setUp(self):
         super(VolumeV3RbacBaseTests, self).setUp()
-        self.useFixture(api_microversion_fixture.APIMicroversionFixture(
-            volume_microversion=self.request_microversion))
 
     @classmethod
     def resource_setup(cls):
@@ -61,6 +58,8 @@
             api_version_utils.select_request_microversion(
                 cls.min_microversion,
                 CONF.volume.min_microversion))
+        cls.setup_api_microversion_fixture(
+            volume_microversion=cls.request_microversion)
 
     def do_request(self, method, expected_status=200, client=None, **payload):
         """Perform API call
diff --git a/cinder_tempest_plugin/rbac/v3/test_snapshots.py b/cinder_tempest_plugin/rbac/v3/test_snapshots.py
index f11da42..14377a9 100644
--- a/cinder_tempest_plugin/rbac/v3/test_snapshots.py
+++ b/cinder_tempest_plugin/rbac/v3/test_snapshots.py
@@ -17,6 +17,8 @@
 from tempest.lib import decorators
 from tempest.lib import exceptions
 
+import testtools
+
 from cinder_tempest_plugin.rbac.v3 import base as rbac_base
 
 CONF = config.CONF
@@ -309,10 +311,15 @@
     def test_force_delete_snapshot(self):
         self._force_delete_snapshot(expected_status=exceptions.Forbidden)
 
+    # We don't need to skip the unmanage because the failure should happen at
+    # the API level, so it should fail regardless of the driver support for
+    # unmanaging snapshots.
     @decorators.idempotent_id('35495666-b663-4c68-ba44-0695e30a6838')
     def test_unmanage_snapshot(self):
         self._unmanage_snapshot(expected_status=exceptions.Forbidden)
 
+    @testtools.skipUnless(CONF.volume_feature_enabled.manage_snapshot,
+                          'manage snapshots are disabled')
     @decorators.idempotent_id('d2d1326d-fb47-4448-a1e1-2d1219d30fd5')
     def test_manage_snapshot(self):
         self._manage_snapshot(
@@ -362,10 +369,15 @@
     def test_force_delete_snapshot(self):
         self._force_delete_snapshot(expected_status=exceptions.Forbidden)
 
+    # We don't need to skip the unmanage because the failure should happen at
+    # the API level, so it should fail regardless of the driver support for
+    # unmanaging snapshots.
     @decorators.idempotent_id('dd7da3da-68ef-42f5-af1d-29803a4a04fd')
     def test_unmanage_snapshot(self):
         self._unmanage_snapshot(expected_status=exceptions.Forbidden)
 
+    @testtools.skipUnless(CONF.volume_feature_enabled.manage_snapshot,
+                          'manage snapshots are disabled')
     @decorators.idempotent_id('c2501d05-9bca-42d7-9ab5-c0d9133e762f')
     def test_manage_snapshot(self):
         self._manage_snapshot(
diff --git a/cinder_tempest_plugin/scenario/manager.py b/cinder_tempest_plugin/scenario/manager.py
index 8598ade..cffa044 100644
--- a/cinder_tempest_plugin/scenario/manager.py
+++ b/cinder_tempest_plugin/scenario/manager.py
@@ -110,7 +110,6 @@
                 (mount_path, filename))
             md5 = ssh_client.exec_command(
                 'sudo md5sum -b %s/%s|cut -c 1-32' % (mount_path, filename))
-            ssh_client.exec_command('sudo sync')
         return md5
 
     def get_md5_from_file(self, instance, instance_ip, filename,
diff --git a/cinder_tempest_plugin/scenario/test_snapshots.py b/cinder_tempest_plugin/scenario/test_snapshots.py
index f376954..02cd6bd 100644
--- a/cinder_tempest_plugin/scenario/test_snapshots.py
+++ b/cinder_tempest_plugin/scenario/test_snapshots.py
@@ -14,10 +14,16 @@
 #    under the License.
 
 from tempest.common import utils
+from tempest.common import waiters
+from tempest import config
 from tempest.lib import decorators
 
+import testtools
+
 from cinder_tempest_plugin.scenario import manager
 
+CONF = config.CONF
+
 
 class SnapshotDataIntegrityTests(manager.ScenarioTest):
 
@@ -121,3 +127,33 @@
 
             self.assertEqual(count_snap, i)
             self.assertEqual(file_map[i], md5_file)
+
+
+class SnapshotDependencyTests(manager.ScenarioTest):
+    @testtools.skipUnless(CONF.volume_feature_enabled.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):
+        volume_1 = self.create_volume()
+
+        snapshot_1 = self.create_volume_snapshot(volume_1['id'], force=True)
+        waiters.wait_for_volume_resource_status(
+            self.snapshots_client, snapshot_1['id'], 'available')
+
+        clone_kwargs = {'snapshot_id': snapshot_1['id'],
+                        'size': volume_1['size']}
+        volume_2 = self.volumes_client.create_volume(**clone_kwargs)['volume']
+
+        waiters.wait_for_volume_resource_status(
+            self.volumes_client, volume_2['id'], 'available')
+        volume_2 = self.volumes_client.show_volume(volume_2['id'])['volume']
+
+        self.snapshots_client.delete_snapshot(snapshot_1['id'])
+        self.snapshots_client.wait_for_resource_deletion(snapshot_1['id'])
+
+        self.volumes_client.delete_volume(volume_1['id'])
+        self.volumes_client.wait_for_resource_deletion(volume_1['id'])
+
+        self.volumes_client.delete_volume(volume_2['id'])
+        self.volumes_client.wait_for_resource_deletion(volume_2['id'])
diff --git a/cinder_tempest_plugin/scenario/test_volume_multiattach.py b/cinder_tempest_plugin/scenario/test_volume_multiattach.py
index e04610f..4fb266e 100644
--- a/cinder_tempest_plugin/scenario/test_volume_multiattach.py
+++ b/cinder_tempest_plugin/scenario/test_volume_multiattach.py
@@ -71,18 +71,25 @@
         # Create a multiattach volume
         volume = self.create_volume(volume_type=multiattach_vol_type['id'])
 
-        # Create encrypted volume
-        encrypted_volume = self.create_encrypted_volume(
-            'luks', volume_type='luks')
+        # Create a volume with the default volume type
+        default_volume = self.create_volume()
 
-        # Create a normal volume
-        simple_volume = self.create_volume()
+        # Create other volume
+        if CONF.compute_feature_enabled.attach_encrypted_volume:
+            other_volume = self.create_encrypted_volume(
+                'luks', volume_type='luks')
+        else:
+            # Create secondary volume type
+            second_vol_type = self.create_volume_type()
 
-        # Attach normal and encrypted volumes (These volumes are not used in
+            other_volume = self.create_volume(
+                volume_type=second_vol_type['id'])
+
+        # Attach default and secondary volumes (These volumes are not used in
         # the current test but is used to emulate a real world scenario
         # where different types of volumes will be attached to the server)
-        self.attach_volume(server_1, simple_volume)
-        self.attach_volume(server_1, encrypted_volume)
+        self.attach_volume(server_1, default_volume)
+        self.attach_volume(server_1, other_volume)
 
         instance_ip = self.get_server_ip(server_1)
 
diff --git a/setup.cfg b/setup.cfg
index f224c5c..3d74cb9 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -20,6 +20,7 @@
     Programming Language :: Python :: 3.8
     Programming Language :: Python :: 3.9
     Programming Language :: Python :: 3.10
+    Programming Language :: Python :: 3.11
 
 [files]
 packages =
diff --git a/test-requirements.txt b/test-requirements.txt
index 905ad51..cb279bc 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -2,7 +2,7 @@
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
 
-hacking>=3.0.1,<3.1 # Apache-2.0
+hacking>=6.1.0,<7.0 # Apache-2.0
 
 coverage!=4.4,>=4.0 # Apache-2.0
 python-subunit>=1.0.0 # Apache-2.0/BSD