Merge "Add access rules restriction tests"
diff --git a/babel.cfg b/babel.cfg
deleted file mode 100644
index 15cd6cb..0000000
--- a/babel.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-[python: **.py]
-
diff --git a/manila_tempest_tests/common/constants.py b/manila_tempest_tests/common/constants.py
index 3488bc5..416de56 100644
--- a/manila_tempest_tests/common/constants.py
+++ b/manila_tempest_tests/common/constants.py
@@ -108,3 +108,6 @@
 SERVER_STATE_UNMANAGE_STARTING = 'unmanage_starting'
 STATUS_SERVER_MIGRATING = 'server_migrating'
 STATUS_SERVER_MIGRATING_TO = 'server_migrating_to'
+
+# Share transfer
+SHARE_TRANSFER_VERSION = "2.77"
diff --git a/manila_tempest_tests/common/waiters.py b/manila_tempest_tests/common/waiters.py
index 8a97c8e..a11b620 100644
--- a/manila_tempest_tests/common/waiters.py
+++ b/manila_tempest_tests/common/waiters.py
@@ -45,7 +45,8 @@
                              resource_name='share', rule_id=None,
                              status_attr='status',
                              raise_rule_in_error_state=True,
-                             version=LATEST_MICROVERSION):
+                             version=LATEST_MICROVERSION,
+                             timeout=None):
     """Waits for a resource to reach a given status."""
 
     get_resource_action = {
@@ -59,6 +60,7 @@
         'share_group': 'get_share_group',
         'share_group_snapshot': 'get_share_group_snapshot',
         'share_replica': 'get_share_replica',
+        'share_backup': 'get_share_backup'
     }
 
     action_name = get_resource_action[resource_name]
@@ -86,6 +88,9 @@
     start = int(time.time())
 
     exp_status = status if isinstance(status, list) else [status]
+    resource_status_check_time_out = client.build_timeout
+    if timeout is not None:
+        resource_status_check_time_out = timeout
     while resource_status not in exp_status:
         time.sleep(client.build_interval)
         body = resource_action(*method_args, **method_kwargs)[rn]
@@ -102,11 +107,11 @@
             raise_method = _get_name_of_raise_method(resource_name)
             resource_exception = getattr(share_exceptions, raise_method)
             raise resource_exception(resource_id=resource_id)
-        if int(time.time()) - start >= client.build_timeout:
+        if int(time.time()) - start >= resource_status_check_time_out:
             message = ('%s %s failed to reach %s status (current %s) '
                        'within the required time (%s s).' %
                        (resource_name.replace('_', ' '), resource_id, status,
-                        resource_status, client.build_timeout))
+                        resource_status, resource_status_check_time_out))
             raise exceptions.TimeoutException(message)
 
 
diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py
index 91d03d9..0a98ad5 100644
--- a/manila_tempest_tests/config.py
+++ b/manila_tempest_tests/config.py
@@ -265,6 +265,9 @@
                 default=False,
                 help="Enable or disable migration with "
                      "preserve_snapshots tests set to True."),
+    cfg.BoolOpt("run_driver_assisted_backup_tests",
+                default=False,
+                help="Enable or disable share backup tests."),
     cfg.BoolOpt("run_manage_unmanage_tests",
                 default=False,
                 help="Defines whether to run manage/unmanage tests or not. "
@@ -314,6 +317,10 @@
                default=1500,
                help="Time to wait for share migration before "
                     "timing out (seconds)."),
+    cfg.IntOpt("share_backup_timeout",
+               default=1500,
+               help="Time to wait for share backup before "
+                    "timing out (seconds)."),
     cfg.IntOpt("share_server_migration_timeout",
                default="1500",
                help="Time to wait for share server migration before "
@@ -336,4 +343,20 @@
                      "attempt to create an IPv6 subnet on the project network "
                      "they create for ping and SSH to the client test VM "
                      "where data path testing is performed."),
+    cfg.StrOpt("dd_input_file",
+               default="/dev/zero",
+               help="The input file (if) in the dd command specifies the "
+                    "source of data that dd will read and process, which can "
+                    "be a device, a regular file, or even standard input "
+                    "(stdin). dd copies, transforms, or performs actions on "
+                    "this data based on provided options and then writes it "
+                    "to an output file or device (of). When using /dev/zero "
+                    "in storage systems with default compression, although "
+                    "it generates highly compressible null bytes (zeros), "
+                    "writing data from /dev/zero might not yield significant "
+                    "space savings as these systems are already optimized for "
+                    "efficient compression."),
+    cfg.DictOpt("driver_assisted_backup_test_driver_options",
+                default={'dummy': True},
+                help="Share backup driver options specified as dict."),
 ]
diff --git a/manila_tempest_tests/services/share/json/shares_client.py b/manila_tempest_tests/services/share/json/shares_client.py
index 6871eda..fb03afc 100644
--- a/manila_tempest_tests/services/share/json/shares_client.py
+++ b/manila_tempest_tests/services/share/json/shares_client.py
@@ -323,6 +323,9 @@
         elif "server_id" in kwargs:
             return self._is_resource_deleted(
                 self.show_share_server, kwargs.get("server_id"))
+        elif "backup_id" in kwargs:
+            return self._is_resource_deleted(
+                self.get_share_backup, kwargs.get("backup_id"))
         else:
             raise share_exceptions.InvalidResource(
                 message=str(kwargs))
diff --git a/manila_tempest_tests/services/share/v2/json/shares_client.py b/manila_tempest_tests/services/share/v2/json/shares_client.py
index 5dd5793..e3a022e 100644
--- a/manila_tempest_tests/services/share/v2/json/shares_client.py
+++ b/manila_tempest_tests/services/share/v2/json/shares_client.py
@@ -373,6 +373,61 @@
         return rest_client.ResponseBody(resp, body)
 
 ###############
+    def create_share_transfer(self, share_id, name=None,
+                              version=LATEST_MICROVERSION):
+        if name is None:
+            name = data_utils.rand_name("tempest-created-share-transfer")
+        post_body = {
+            "transfer": {
+                "share_id": share_id,
+                "name": name
+            }
+        }
+        body = json.dumps(post_body)
+        resp, body = self.post("share-transfers", body, version=version)
+        self.expected_success(202, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_share_transfer(self, transfer_id, version=LATEST_MICROVERSION):
+        resp, body = self.delete("share-transfers/%s" % transfer_id,
+                                 version=version)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_share_transfers(self, detailed=False, params=None,
+                             version=LATEST_MICROVERSION):
+        """Get list of share transfers w/o filters."""
+        uri = 'share-transfers/detail' if detailed else 'share-transfers'
+        uri += '?%s' % parse.urlencode(params) if params else ''
+        resp, body = self.get(uri, version=version)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def get_share_transfer(self, transfer_id, version=LATEST_MICROVERSION):
+        resp, body = self.get("share-transfers/%s" % transfer_id,
+                              version=version)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def accept_share_transfer(self, transfer_id, auth_key,
+                              clear_access_rules=False,
+                              version=LATEST_MICROVERSION):
+        post_body = {
+            "accept": {
+                "auth_key": auth_key,
+                "clear_access_rules": clear_access_rules
+            }
+        }
+        body = json.dumps(post_body)
+        resp, body = self.post("share-transfers/%s/accept" % transfer_id,
+                               body, version=version)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+###############
 
     def get_instances_of_share(self, share_id, version=LATEST_MICROVERSION):
         resp, body = self.get("shares/%s/instances" % share_id,
@@ -1789,6 +1844,109 @@
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
+###############
+
+    def get_share_backup(self, backup_id, version=LATEST_MICROVERSION):
+        """Returns the details of a single backup."""
+        resp, body = self.get("share-backups/%s" % backup_id,
+                              headers=EXPERIMENTAL,
+                              extra_headers=True,
+                              version=version)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_share_backups(self, share_id=None, version=LATEST_MICROVERSION):
+        """Get list of backups."""
+        uri = "share-backups/detail"
+        if share_id:
+            uri += (f'?share_id={share_id}')
+        resp, body = self.get(uri, headers=EXPERIMENTAL,
+                              extra_headers=True, version=version)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_share_backup(self, share_id, name=None, description=None,
+                            backup_options=None, version=LATEST_MICROVERSION):
+        """Create a share backup."""
+        if name is None:
+            name = data_utils.rand_name("tempest-created-share-backup")
+        if description is None:
+            description = data_utils.rand_name(
+                "tempest-created-share-backup-desc")
+        post_body = {
+            'share_backup': {
+                'name': name,
+                'description': description,
+                'share_id': share_id,
+                'backup_options': backup_options,
+            }
+        }
+        body = json.dumps(post_body)
+        resp, body = self.post('share-backups', body,
+                               headers=EXPERIMENTAL,
+                               extra_headers=True,
+                               version=version)
+
+        self.expected_success(202, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_share_backup(self, backup_id, version=LATEST_MICROVERSION):
+        """Delete share backup."""
+        uri = "share-backups/%s" % backup_id
+        resp, body = self.delete(uri,
+                                 headers=EXPERIMENTAL,
+                                 extra_headers=True,
+                                 version=version)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def restore_share_backup(self, backup_id, version=LATEST_MICROVERSION):
+        """Restore share backup."""
+        uri = "share-backups/%s/action" % backup_id
+        body = {'restore': None}
+        resp, body = self.post(uri, json.dumps(body),
+                               headers=EXPERIMENTAL,
+                               extra_headers=True,
+                               version=version)
+        self.expected_success(202, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_share_backup(self, backup_id, name=None, description=None,
+                            version=LATEST_MICROVERSION):
+        """Update share backup."""
+        uri = "share-backups/%s" % backup_id
+        post_body = {}
+        if name:
+            post_body['name'] = name
+        if description:
+            post_body['description'] = description
+
+        body = json.dumps({'share_backup': post_body})
+        resp, body = self.put(uri, body,
+                              headers=EXPERIMENTAL,
+                              extra_headers=True,
+                              version=version)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def reset_state_share_backup(self, backup_id,
+                                 status=constants.STATUS_AVAILABLE,
+                                 version=LATEST_MICROVERSION):
+
+        uri = "share-backups/%s/action" % backup_id
+        body = {'reset_status': {'status': status}}
+        resp, body = self.post(uri, json.dumps(body),
+                               headers=EXPERIMENTAL,
+                               extra_headers=True,
+                               version=LATEST_MICROVERSION)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
 ################
 
     def create_snapshot_access_rule(self, snapshot_id, access_type="ip",
diff --git a/manila_tempest_tests/share_exceptions.py b/manila_tempest_tests/share_exceptions.py
index efa61b5..7620109 100644
--- a/manila_tempest_tests/share_exceptions.py
+++ b/manila_tempest_tests/share_exceptions.py
@@ -86,3 +86,7 @@
 class ShareServerMigrationException(exceptions.TempestException):
     message = ("Share server %(server_id)s failed to migrate and is in ERROR "
                "status")
+
+
+class ShareBackupBuildErrorException(exceptions.TempestException):
+    message = ("Share backup %(backup_id)s failed and is in ERROR status")
diff --git a/manila_tempest_tests/tests/api/admin/test_share_servers_manage_negative.py b/manila_tempest_tests/tests/api/admin/test_share_servers_manage_negative.py
index 1e9073d..09a40d9 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_servers_manage_negative.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_servers_manage_negative.py
@@ -333,7 +333,8 @@
             invalid_params)
 
         # try with part of the identifier
-        invalid_params['identifier'] = share_server['identifier'].split("-")[2]
+        invalid_params['identifier'] = (
+            share_server['identifier'].split("-")[-1])
 
         self.assertRaises(
             lib_exc.BadRequest,
diff --git a/manila_tempest_tests/tests/api/admin/test_share_servers_migration.py b/manila_tempest_tests/tests/api/admin/test_share_servers_migration.py
index 2535745..d3ec3f1 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_servers_migration.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_servers_migration.py
@@ -282,10 +282,11 @@
             src_server_id, dest_host, preserve_snapshots=preserve_snapshots)
 
         expected_state = constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE
+        timeout = CONF.share.share_server_migration_timeout
         waiters.wait_for_resource_status(
             self.shares_v2_client, src_server_id,
             expected_state, resource_name='share_server',
-            status_attr='task_state'
+            status_attr='task_state', timeout=timeout
         )
 
         # Get for the destination share server.
@@ -352,10 +353,11 @@
             preserve_snapshots=preserve_snapshots)
 
         expected_state = constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE
+        timeout = CONF.share.share_server_migration_timeout
         waiters.wait_for_resource_status(
             self.shares_v2_client, src_server_id,
             expected_state, resource_name='share_server',
-            status_attr='task_state'
+            status_attr='task_state', timeout=timeout
         )
         # Get for the destination share server.
         dest_server_id = self._get_share_server_destination_for_migration(
diff --git a/manila_tempest_tests/tests/api/admin/test_share_types.py b/manila_tempest_tests/tests/api/admin/test_share_types.py
index 3d46c9e..d23171a 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_types.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_types.py
@@ -113,22 +113,26 @@
     @utils.skip_if_microversion_not_supported("2.50")
     @decorators.idempotent_id('a9af19e1-e789-4c4f-a39b-dd8df6ed00b1')
     @tc.attr(base.TAG_POSITIVE, base.TAG_API)
-    @ddt.data(
-        ('2.50', data_utils.rand_name("type_updated"),
+    @ddt.named_data(
+        ('2_50_name_description_public', '2.50',
+         data_utils.rand_name("type_updated"), 'description_updated', True),
+        ('2_50_name', '2.50', data_utils.rand_name("type_updated"), None,
+         None),
+        ('2_50_description_public', '2.50', None, 'description_updated',
+         None),
+        ('2_50_public', '2.50', None, None, True),
+        ('2_50', '2.50', None, None, False),
+        (f'{LATEST_MICROVERSION}_name_description_public',
+         LATEST_MICROVERSION, data_utils.rand_name("type_updated"),
          'description_updated', True),
-        ('2.50', data_utils.rand_name("type_updated"), None, None),
-        ('2.50', None, 'description_updated', None),
-        ('2.50', None, None, True),
-        ('2.50', None, None, False),
-        (LATEST_MICROVERSION, data_utils.rand_name("type_updated"),
-         'description_updated', True),
-        (LATEST_MICROVERSION, data_utils.rand_name("type_updated"),
-         None, None),
-        (LATEST_MICROVERSION, None, 'description_updated', None),
-        (LATEST_MICROVERSION, None, None, True),
-        (LATEST_MICROVERSION, None, None, False),
+        (f'{LATEST_MICROVERSION}_name', LATEST_MICROVERSION,
+         data_utils.rand_name("type_updated"), None, None),
+        (f'{LATEST_MICROVERSION}_description', LATEST_MICROVERSION, None,
+         'description_updated', None),
+        (f'{LATEST_MICROVERSION}_public', LATEST_MICROVERSION, None, None,
+         True),
+        (LATEST_MICROVERSION, LATEST_MICROVERSION, None, None, False),
     )
-    @ddt.unpack
     def test_share_type_create_update(self, version, st_name,
                                       st_description, st_is_public):
         name = data_utils.rand_name("tempest-manila")
diff --git a/manila_tempest_tests/tests/api/base.py b/manila_tempest_tests/tests/api/base.py
index 9bd32a9..2db2353 100755
--- a/manila_tempest_tests/tests/api/base.py
+++ b/manila_tempest_tests/tests/api/base.py
@@ -705,6 +705,31 @@
         return replica
 
     @classmethod
+    def create_backup_wait_for_active(cls, share_id, client=None,
+                                      cleanup_in_class=False, cleanup=True,
+                                      version=CONF.share.max_api_microversion):
+        client = client or cls.shares_v2_client
+        backup_name = data_utils.rand_name('Backup')
+        backup_options = CONF.share.driver_assisted_backup_test_driver_options
+        backup = client.create_share_backup(
+            share_id,
+            name=backup_name,
+            backup_options=backup_options)['share_backup']
+        resource = {
+            "type": "share_backup",
+            "id": backup["id"],
+            "client": client,
+        }
+        if cleanup:
+            if cleanup_in_class:
+                cls.class_resources.insert(0, resource)
+            else:
+                cls.method_resources.insert(0, resource)
+        waiters.wait_for_resource_status(client, backup["id"], "available",
+                                         resource_name='share_backup')
+        return client.get_share_backup(backup['id'])['share_backup']
+
+    @classmethod
     def delete_share_replica(cls, replica_id, client=None,
                              version=CONF.share.max_api_microversion):
         client = client or cls.shares_v2_client
@@ -760,7 +785,7 @@
         share_network_subnet = client.create_subnet(
             **kwargs)['share_network_subnet']
         resource = {
-            "type": "share-network-subnet",
+            "type": "share_network_subnet",
             "id": share_network_subnet["id"],
             "extra_params": {
                 "share_network_id": share_network_subnet["share_network_id"]
@@ -919,6 +944,9 @@
                     elif res["type"] == "share_replica":
                         client.delete_share_replica(res_id)
                         client.wait_for_resource_deletion(replica_id=res_id)
+                    elif res["type"] == "share_backup":
+                        client.delete_share_backup(res_id)
+                        client.wait_for_resource_deletion(backup_id=res_id)
                     elif res["type"] == "share_network_subnet":
                         sn_id = res["extra_params"]["share_network_id"]
                         client.delete_subnet(sn_id, res_id)
diff --git a/manila_tempest_tests/tests/api/test_backup.py b/manila_tempest_tests/tests/api/test_backup.py
new file mode 100644
index 0000000..f443802
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_backup.py
@@ -0,0 +1,117 @@
+# Copyright 2024 Cloudification GmbH
+# 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 tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import waiters
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
+
+
+CONF = config.CONF
+_MIN_SUPPORTED_MICROVERSION = '2.80'
+
+
+class ShareBackupTest(base.BaseSharesMixedTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(ShareBackupTest, cls).skip_checks()
+        if not CONF.share.run_driver_assisted_backup_tests:
+            raise cls.skipException("Share backup tests are disabled.")
+        utils.check_skip_if_microversion_not_supported(
+            _MIN_SUPPORTED_MICROVERSION)
+
+    def setUp(self):
+        super(ShareBackupTest, self).setUp()
+        extra_specs = {
+            'snapshot_support': True,
+            'mount_snapshot_support': True,
+        }
+        share_type = self.create_share_type(extra_specs=extra_specs)
+        share = self.create_share(self.shares_v2_client.share_protocol,
+                                  share_type_id=share_type['id'])
+        self.share_id = share["id"]
+
+    @decorators.idempotent_id('12c36c97-faf4-4fec-9a9b-7cff0d2035cd')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+    def test_create_share_backup(self):
+        backup = self.create_backup_wait_for_active(self.share_id)
+
+        # Verify backup create API response
+        expected_keys = ["id", "share_id", "status",
+                         "availability_zone", "created_at", "updated_at",
+                         "size", "progress", "restore_progress",
+                         "name", "description"]
+
+        # Strict key check
+        actual_backup = self.shares_v2_client.get_share_backup(
+            backup['id'])['share_backup']
+        actual_keys = actual_backup.keys()
+        self.assertEqual(backup['id'], actual_backup['id'])
+        self.assertEqual(set(expected_keys), set(actual_keys))
+
+    @decorators.idempotent_id('34c36c97-faf4-4fec-9a9b-7cff0d2035cd')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+    def test_delete_share_backup(self):
+        backup = self.create_backup_wait_for_active(
+            self.share_id, cleanup=False)
+
+        # Delete share backup
+        self.shares_v2_client.delete_share_backup(backup['id'])
+        self.shares_v2_client.wait_for_resource_deletion(
+            backup_id=backup['id'])
+        self.assertRaises(
+            lib_exc.NotFound,
+            self.shares_v2_client.get_share_backup,
+            backup['id'])
+
+    @decorators.idempotent_id('56c36c97-faf4-4fec-9a9b-7cff0d2035cd')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+    def test_restore_share_backup(self):
+        backup = self.create_backup_wait_for_active(self.share_id)
+
+        # Restore share backup
+        restore = self.shares_v2_client.restore_share_backup(
+            backup['id'])['restore']
+        waiters.wait_for_resource_status(
+            self.shares_v2_client, backup['id'], 'available',
+            resource_name='share_backup')
+
+        self.assertEqual(restore['share_id'], self.share_id)
+
+    @decorators.idempotent_id('78c36c97-faf4-4fec-9a9b-7cff0d2035cd')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+    def test_update_share_backup(self):
+        backup = self.create_backup_wait_for_active(self.share_id)
+
+        # Update share backup name
+        backup_name2 = data_utils.rand_name('Backup')
+        backup = self.shares_v2_client.update_share_backup(
+            backup['id'], name=backup_name2)['share_backup']
+        updated_backup = self.shares_v2_client.get_share_backup(
+            backup['id'])['share_backup']
+        self.assertEqual(backup_name2, updated_backup['name'])
+
+    @decorators.idempotent_id('19c36c97-faf4-4fec-9a9b-7cff0d2045af')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
+    def test_list_share_backups(self):
+        self.create_backup_wait_for_active(self.share_id)
+        backups = self.shares_v2_client.list_share_backups()
+        self.assertEqual(1, len(backups))
diff --git a/manila_tempest_tests/tests/api/test_backup_negative.py b/manila_tempest_tests/tests/api/test_backup_negative.py
new file mode 100644
index 0000000..743c195
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_backup_negative.py
@@ -0,0 +1,153 @@
+# Copyright 2024 Cloudification GmbH
+# 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 tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import waiters
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
+
+
+CONF = config.CONF
+_MIN_SUPPORTED_MICROVERSION = '2.80'
+
+
+class ShareBackupNegativeTest(base.BaseSharesMixedTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(ShareBackupNegativeTest, cls).skip_checks()
+        if not CONF.share.run_driver_assisted_backup_tests:
+            raise cls.skipException("Share backup tests are disabled.")
+        utils.check_skip_if_microversion_not_supported(
+            _MIN_SUPPORTED_MICROVERSION)
+
+    def setUp(self):
+        super(ShareBackupNegativeTest, self).setUp()
+        extra_specs = {
+            'snapshot_support': True,
+            'mount_snapshot_support': True,
+        }
+        share_type = self.create_share_type(extra_specs=extra_specs)
+        share = self.create_share(self.shares_v2_client.share_protocol,
+                                  share_type_id=share_type['id'])
+        self.share_id = share["id"]
+        self.backup_options = (
+            CONF.share.driver_assisted_backup_test_driver_options)
+
+    @decorators.idempotent_id('58c36c97-faf4-4fec-9a9b-7cff0d2035ab')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_BACKEND)
+    def test_create_backup_when_share_is_in_backup_creating_state(self):
+        backup_name1 = data_utils.rand_name('Backup')
+        backup1 = self.shares_v2_client.create_share_backup(
+            self.share_id,
+            name=backup_name1,
+            backup_options=self.backup_options)['share_backup']
+
+        # try create backup when share state is busy
+        backup_name2 = data_utils.rand_name('Backup')
+        self.assertRaises(lib_exc.BadRequest,
+                          self.shares_v2_client.create_share_backup,
+                          self.share_id,
+                          name=backup_name2,
+                          backup_options=self.backup_options)
+        waiters.wait_for_resource_status(
+            self.shares_v2_client, backup1['id'], "available",
+            resource_name='share_backup')
+
+        # delete the share backup
+        self.shares_v2_client.delete_share_backup(backup1['id'])
+        self.shares_v2_client.wait_for_resource_deletion(
+            backup_id=backup1['id'])
+
+    @decorators.idempotent_id('58c36c97-faf4-4fec-9a9b-7cff0d2012ab')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_BACKEND)
+    def test_create_backup_when_share_is_in_error_state(self):
+        self.admin_shares_v2_client.reset_state(self.share_id,
+                                                status='error')
+
+        # try create backup when share is not available
+        backup_name = data_utils.rand_name('Backup')
+        self.assertRaises(lib_exc.BadRequest,
+                          self.shares_v2_client.create_share_backup,
+                          self.share_id,
+                          name=backup_name,
+                          backup_options=self.backup_options)
+
+        self.admin_shares_v2_client.reset_state(self.share_id,
+                                                status='available')
+
+    @decorators.idempotent_id('58c36c97-faf4-4fec-9a9b-7cff0d2012de')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_BACKEND)
+    def test_create_backup_when_share_has_snapshots(self):
+        self.create_snapshot_wait_for_active(self.share_id,
+                                             cleanup_in_class=False)
+
+        # try create backup when share has snapshots
+        backup_name = data_utils.rand_name('Backup')
+        self.assertRaises(lib_exc.BadRequest,
+                          self.shares_v2_client.create_share_backup,
+                          self.share_id,
+                          name=backup_name,
+                          backup_options=self.backup_options)
+
+    @decorators.idempotent_id('58c12c97-faf4-4fec-9a9b-7cff0d2012de')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_BACKEND)
+    def test_delete_backup_when_backup_is_not_available(self):
+        backup = self.create_backup_wait_for_active(self.share_id)
+        self.admin_shares_v2_client.reset_state_share_backup(
+            backup['id'], status='creating')
+
+        # try delete backup when share backup is not available
+        self.assertRaises(lib_exc.BadRequest,
+                          self.shares_v2_client.delete_share_backup,
+                          backup['id'])
+
+        self.admin_shares_v2_client.reset_state_share_backup(
+            backup['id'], status='available')
+
+    @decorators.idempotent_id('58c56c97-faf4-4fec-9a9b-7cff0d2012de')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_BACKEND)
+    def test_restore_backup_when_share_is_not_available(self):
+        backup = self.create_backup_wait_for_active(self.share_id)
+        self.admin_shares_v2_client.reset_state(self.share_id,
+                                                status='error')
+
+        # try restore backup when share is not available
+        self.assertRaises(lib_exc.BadRequest,
+                          self.shares_v2_client.restore_share_backup,
+                          backup['id'])
+
+        self.admin_shares_v2_client.reset_state(self.share_id,
+                                                status='available')
+
+    @decorators.idempotent_id('58c12998-faf4-4fec-9a9b-7cff0d2012de')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_BACKEND)
+    def test_restore_backup_when_backup_is_not_available(self):
+        backup = self.create_backup_wait_for_active(self.share_id)
+        self.admin_shares_v2_client.reset_state_share_backup(
+            backup['id'], status='creating')
+
+        # try restore backup when backup is not available
+        self.assertRaises(lib_exc.BadRequest,
+                          self.shares_v2_client.restore_share_backup,
+                          backup['id'])
+
+        self.admin_shares_v2_client.reset_state_share_backup(
+            backup['id'], status='available')
diff --git a/manila_tempest_tests/tests/api/test_metadata.py b/manila_tempest_tests/tests/api/test_metadata.py
index bd73963..862de50 100644
--- a/manila_tempest_tests/tests/api/test_metadata.py
+++ b/manila_tempest_tests/tests/api/test_metadata.py
@@ -33,12 +33,6 @@
 
     def _verify_share_metadata(self, share, md):
 
-        # get metadata of share
-        metadata = self.shares_v2_client.get_metadata(share["id"])['metadata']
-
-        # verify metadata
-        self.assertEqual(md, metadata)
-
         # verify metadata items
         for key in md:
             get_value = self.shares_v2_client.get_metadata_item(
@@ -82,7 +76,8 @@
         # verify deletion of metadata
         get_metadata = self.shares_v2_client.get_metadata(share["id"])[
             'metadata']
-        self.assertEmpty(get_metadata)
+        for key in md.keys():
+            self.assertNotIn(key, list(get_metadata.keys()))
 
     @decorators.idempotent_id('4e5f8159-62b6-4d5c-f729-d8b1f029d7de')
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
@@ -117,7 +112,8 @@
         # verify deletion of metadata
         get_metadata = self.shares_v2_client.get_metadata(
             share["id"])['metadata']
-        self.assertEmpty(get_metadata)
+        for key in md.keys():
+            self.assertNotIn(key, list(get_metadata.keys()))
 
     @decorators.idempotent_id('2ec70ba5-050b-3b17-c862-c149e53543c0')
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
@@ -150,7 +146,8 @@
         # verify deletion of metadata
         get_metadata = self.shares_v2_client.get_metadata(
             share["id"])['metadata']
-        self.assertEmpty(get_metadata)
+        for key in md.keys():
+            self.assertNotIn(key, list(get_metadata.keys()))
 
     @decorators.idempotent_id('c94851f4-2559-4712-9297-9912db1da7ff')
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
@@ -228,7 +225,7 @@
 
         body_get = self.shares_v2_client.get_metadata(
             self.share["id"])['metadata']
-        self.assertEqual(data, body_get)
+        self.assertEqual(data["k"], body_get["k"])
 
     @decorators.idempotent_id('5eff5619-b7cd-42f1-85e0-47d3d47098dd')
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
@@ -240,7 +237,9 @@
 
         body_get = self.shares_v2_client.get_metadata(
             self.share["id"])['metadata']
-        self.assertEqual(data, body_get)
+        body_get_keys = list(body_get.keys())
+        self.assertIn(max_key, body_get_keys)
+        self.assertEqual(data[max_key], body_get[max_key])
 
     @decorators.idempotent_id('44a572f1-6b5c-49d0-8f2e-1583ec3428d8')
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
@@ -251,7 +250,7 @@
 
         body_get = self.shares_v2_client.get_metadata(
             self.share["id"])['metadata']
-        self.assertEqual(data, body_get)
+        self.assertEqual(data["key"], body_get["key"])
 
     @decorators.idempotent_id('694d95e1-ba8c-49fc-a888-6f9f0d51d77d')
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
@@ -263,4 +262,4 @@
 
         body_get = self.shares_v2_client.get_metadata(
             self.share["id"])['metadata']
-        self.assertEqual(data, body_get)
+        self.assertEqual(max_value, body_get["key"])
diff --git a/manila_tempest_tests/tests/api/test_revert_to_snapshot.py b/manila_tempest_tests/tests/api/test_revert_to_snapshot.py
index dca394a..5eb6915 100644
--- a/manila_tempest_tests/tests/api/test_revert_to_snapshot.py
+++ b/manila_tempest_tests/tests/api/test_revert_to_snapshot.py
@@ -109,8 +109,8 @@
     @decorators.idempotent_id('196f2bc5-e13a-4730-ac51-61e339068a06')
     @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
     @ddt.data(
-        *{constants.REVERT_TO_SNAPSHOT_MICROVERSION,
-          CONF.share.max_api_microversion}
+        *utils.deduplicate([constants.REVERT_TO_SNAPSHOT_MICROVERSION,
+                            CONF.share.max_api_microversion])
     )
     def test_revert_to_latest_snapshot(self, version):
         snapshot = self.create_snapshot_wait_for_active(self.share['id'],
@@ -129,8 +129,8 @@
     @decorators.idempotent_id('09bd9942-7ef9-4d24-b2dd-f83bdda27b50')
     @tc.attr(base.TAG_POSITIVE, base.TAG_BACKEND)
     @ddt.data(
-        *{constants.REVERT_TO_SNAPSHOT_MICROVERSION,
-          CONF.share.max_api_microversion}
+        *utils.deduplicate([constants.REVERT_TO_SNAPSHOT_MICROVERSION,
+                            CONF.share.max_api_microversion])
     )
     def test_revert_to_previous_snapshot(self, version):
         snapshot1 = self.create_snapshot_wait_for_active(
@@ -157,8 +157,8 @@
     @tc.skipUnless(CONF.share.run_replication_tests,
                    'Replication tests are disabled.')
     @ddt.data(
-        *{constants.REVERT_TO_SNAPSHOT_MICROVERSION,
-          CONF.share.max_api_microversion}
+        *utils.deduplicate([constants.REVERT_TO_SNAPSHOT_MICROVERSION,
+                            CONF.share.max_api_microversion])
     )
     def test_revert_to_replicated_snapshot(self, version):
         """Test reverting to a replicated snapshot."""
diff --git a/manila_tempest_tests/tests/api/test_revert_to_snapshot_negative.py b/manila_tempest_tests/tests/api/test_revert_to_snapshot_negative.py
index 96d7f04..95e2c72 100644
--- a/manila_tempest_tests/tests/api/test_revert_to_snapshot_negative.py
+++ b/manila_tempest_tests/tests/api/test_revert_to_snapshot_negative.py
@@ -80,8 +80,8 @@
     @decorators.idempotent_id('21dd8561-8913-42a2-a95c-74b536964c94')
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
     @ddt.data(
-        *{constants.REVERT_TO_SNAPSHOT_MICROVERSION,
-          CONF.share.max_api_microversion}
+        *utils.deduplicate([constants.REVERT_TO_SNAPSHOT_MICROVERSION,
+                            CONF.share.max_api_microversion])
     )
     def test_revert_to_second_latest_snapshot(self, version):
         snapshot1 = self.create_snapshot_wait_for_active(
@@ -98,8 +98,8 @@
     @decorators.idempotent_id('7360ee16-ac7d-46ce-9c81-251d64fb7434')
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
     @ddt.data(
-        *{constants.REVERT_TO_SNAPSHOT_MICROVERSION,
-          CONF.share.max_api_microversion}
+        *utils.deduplicate([constants.REVERT_TO_SNAPSHOT_MICROVERSION,
+                            CONF.share.max_api_microversion])
     )
     def test_revert_to_error_snapshot(self, version):
         snapshot = self.create_snapshot_wait_for_active(self.share['id'],
@@ -118,8 +118,8 @@
     @decorators.idempotent_id('108a451e-a8e9-450f-8f75-53883d58c6be')
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
     @ddt.data(
-        *{constants.REVERT_TO_SNAPSHOT_MICROVERSION,
-          CONF.share.max_api_microversion}
+        *utils.deduplicate([constants.REVERT_TO_SNAPSHOT_MICROVERSION,
+                            CONF.share.max_api_microversion])
     )
     def test_revert_error_share_to_snapshot(self, version):
         snapshot = self.create_snapshot_wait_for_active(self.share['id'],
@@ -143,8 +143,8 @@
     @decorators.idempotent_id('29024057-dbbd-4cf6-a796-207dfbd4c3ff')
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
     @ddt.data(
-        *{constants.REVERT_TO_SNAPSHOT_MICROVERSION,
-          CONF.share.max_api_microversion}
+        *utils.deduplicate([constants.REVERT_TO_SNAPSHOT_MICROVERSION,
+                            CONF.share.max_api_microversion])
     )
     def test_revert_to_missing_snapshot(self, version):
         self.assertRaises(exceptions.BadRequest,
@@ -156,8 +156,8 @@
     @decorators.idempotent_id('ef706fad-5ac4-41dc-af81-5aa0331560cf')
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
     @ddt.data(
-        *{constants.REVERT_TO_SNAPSHOT_MICROVERSION,
-          CONF.share.max_api_microversion}
+        *utils.deduplicate([constants.REVERT_TO_SNAPSHOT_MICROVERSION,
+                            CONF.share.max_api_microversion])
     )
     def test_revert_to_invalid_snapshot(self, version):
         snapshot = self.create_snapshot_wait_for_active(
diff --git a/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet_negative.py b/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet_negative.py
index 522f34e..a230163 100644
--- a/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet_negative.py
+++ b/manila_tempest_tests/tests/api/test_share_servers_multiple_subnet_negative.py
@@ -32,13 +32,17 @@
         super(ShareServerMultipleSubNegativeTest, cls).skip_checks()
         if not CONF.share.multitenancy_enabled:
             raise cls.skipException('Multitenancy tests are disabled.')
+        if not CONF.share.run_share_server_multiple_subnet_tests:
+            raise cls.skipException(
+                'Share server multiple subnets and network allocation '
+                'update tests are disabled.')
         utils.check_skip_if_microversion_not_supported("2.70")
 
     @classmethod
     def resource_setup(cls):
         super(ShareServerMultipleSubNegativeTest, cls).resource_setup()
-        cls.share_network = cls.alt_shares_v2_client.get_share_network(
-            cls.alt_shares_v2_client.share_network_id)['share_network']
+        cls.share_network = cls.shares_v2_client.get_share_network(
+            cls.shares_v2_client.share_network_id)['share_network']
 
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
     @decorators.idempotent_id('1e2a9415-b02f-4c02-812d-bedc361f92ce')
@@ -53,9 +57,9 @@
         zones = self.get_availability_zones_matching_share_type(
             share_type)
         if not pools or not zones:
-            raise self.skipException("At least one backend that supports "
-                                     "adding multiple subnets into a share "
-                                     "network is needed for this test.")
+            raise self.skipException("At least one backend that does not "
+                                     "support adding multiple subnets into a "
+                                     "share network is needed for this test.")
         extra_specs = {'pool_name': pools[0]['pool'],
                        'availability_zone': zones[0]}
         self.admin_shares_v2_client.update_share_type_extra_specs(
diff --git a/manila_tempest_tests/tests/api/test_share_transfers.py b/manila_tempest_tests/tests/api/test_share_transfers.py
new file mode 100644
index 0000000..56f8e0c
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_share_transfers.py
@@ -0,0 +1,107 @@
+# Copyright (C) 2022 China Telecom Digital Intelligence.
+# 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 tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import constants
+from manila_tempest_tests.common import waiters
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
+
+CONF = config.CONF
+
+
+class ShareTransferTest(base.BaseSharesMixedTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(ShareTransferTest, cls).skip_checks()
+        utils.check_skip_if_microversion_not_supported(
+            constants.SHARE_TRANSFER_VERSION)
+        if CONF.share.multitenancy_enabled:
+            raise cls.skipException(
+                'Only for driver_handles_share_servers = False driver mode.')
+
+    @classmethod
+    def resource_setup(cls):
+        super(ShareTransferTest, cls).resource_setup()
+        # create share_type with dhss=False
+        extra_specs = cls.add_extra_specs_to_dict()
+        cls.share_type = cls.create_share_type(extra_specs=extra_specs)
+        cls.share_type_id = cls.share_type['id']
+
+    @decorators.idempotent_id('716e71a0-8265-4410-9170-08714095d9e8')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+    def test_create_and_delete_share_transfer(self):
+        # create share
+        share_name = data_utils.rand_name("tempest-share-name")
+        share = self.create_share(name=share_name,
+                                  share_type_id=self.share_type_id,
+                                  cleanup_in_class=False)
+
+        # create share transfer
+        transfer = self.shares_v2_client.create_share_transfer(
+            share['id'], name='tempest_share_transfer')['transfer']
+        waiters.wait_for_resource_status(
+            self.shares_client, share['id'], 'awaiting_transfer')
+
+        # check transfer exists and show transfer
+        transfer_show = self.shares_v2_client.get_share_transfer(
+            transfer['id'])['transfer']
+        self.assertEqual(transfer_show['name'], 'tempest_share_transfer')
+
+        # delete share transfer
+        self.shares_v2_client.delete_share_transfer(transfer['id'])
+        waiters.wait_for_resource_status(
+            self.shares_client, share['id'], 'available')
+
+        # check transfer not in transfer list
+        transfers = self.shares_v2_client.list_share_transfers()['transfers']
+        transfer_ids = [tf['id'] for tf in transfers]
+        self.assertNotIn(transfer['id'], transfer_ids)
+
+    @decorators.idempotent_id('3c2622ab-3368-4693-afb6-e60bd27e61ef')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+    def test_create_and_accept_share_transfer(self):
+        # create share
+        share_name = data_utils.rand_name("tempest-share-name")
+        share = self.create_share(name=share_name,
+                                  share_type_id=self.share_type_id)
+
+        # create share transfer
+        transfer = self.shares_v2_client.create_share_transfer(
+            share['id'])['transfer']
+        waiters.wait_for_resource_status(
+            self.shares_client, share['id'], 'awaiting_transfer')
+
+        # accept share transfer by alt project
+        self.alt_shares_v2_client.accept_share_transfer(transfer['id'],
+                                                        transfer['auth_key'])
+        waiters.wait_for_resource_status(
+            self.alt_shares_client, share['id'], 'available')
+
+        # check share in alt project
+        shares = self.alt_shares_v2_client.list_shares(
+            detailed=True)['shares']
+        share_ids = [sh['id'] for sh in shares] if shares else []
+        self.assertIn(share['id'], share_ids)
+
+        # delete the share
+        self.alt_shares_v2_client.delete_share(share['id'])
+        self.alt_shares_v2_client.wait_for_resource_deletion(
+            share_id=share["id"])
diff --git a/manila_tempest_tests/tests/api/test_share_transfers_negative.py b/manila_tempest_tests/tests/api/test_share_transfers_negative.py
new file mode 100644
index 0000000..0672459
--- /dev/null
+++ b/manila_tempest_tests/tests/api/test_share_transfers_negative.py
@@ -0,0 +1,137 @@
+# Copyright (C) 2022 China Telecom Digital Intelligence.
+# 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_utils import uuidutils
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import constants
+from manila_tempest_tests.common import waiters
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests import utils
+
+CONF = config.CONF
+
+
+class ShareTransferNegativeTest(base.BaseSharesMixedTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(ShareTransferNegativeTest, cls).skip_checks()
+        utils.check_skip_if_microversion_not_supported(
+            constants.SHARE_TRANSFER_VERSION)
+        if CONF.share.multitenancy_enabled:
+            raise cls.skipException(
+                'Only for driver_handles_share_servers = False driver mode.')
+
+    @classmethod
+    def resource_setup(cls):
+        super(ShareTransferNegativeTest, cls).resource_setup()
+        # create share_type with dhss=False
+        extra_specs = cls.add_extra_specs_to_dict()
+        cls.share_type = cls.create_share_type(extra_specs=extra_specs)
+        cls.share_type_id = cls.share_type['id']
+
+    def _create_share_transfer(self, share):
+        transfer = self.shares_v2_client.create_share_transfer(
+            share['id'])['transfer']
+        waiters.wait_for_resource_status(
+            self.shares_client, share['id'], 'awaiting_transfer')
+        self.addCleanup(waiters.wait_for_resource_status, self.shares_client,
+                        share['id'], 'available')
+        self.addCleanup(self.shares_v2_client.delete_share_transfer,
+                        transfer['id'])
+        return transfer
+
+    @decorators.idempotent_id('baf66f62-253e-40dd-a6a9-109bc7613e52')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_show_transfer_of_other_tenants(self):
+        # create share
+        share_name = data_utils.rand_name("tempest-share-name")
+        share = self.create_share(
+            name=share_name,
+            share_type_id=self.share_type_id)
+
+        # create share transfer
+        transfer = self._create_share_transfer(share)
+
+        self.assertRaises(lib_exc.NotFound,
+                          self.alt_shares_v2_client.get_share_transfer,
+                          transfer['id'])
+
+    @decorators.idempotent_id('4b9e75b1-4ac6-4111-b09e-e6dacd0ac2c3')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_show_nonexistent_transfer(self):
+        self.assertRaises(lib_exc.NotFound,
+                          self.shares_v2_client.get_share_transfer,
+                          str(uuidutils.generate_uuid()))
+
+    @decorators.idempotent_id('b3e26356-5eb0-4f73-b5a7-d3594cc2f30e')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_delete_transfer_of_other_tenants(self):
+        # create share
+        share_name = data_utils.rand_name("tempest-share-name")
+        share = self.create_share(
+            name=share_name,
+            share_type_id=self.share_type_id)
+
+        # create share transfer
+        transfer = self._create_share_transfer(share)
+
+        self.assertRaises(lib_exc.NotFound,
+                          self.alt_shares_v2_client.delete_share_transfer,
+                          transfer['id'])
+
+    @decorators.idempotent_id('085d5971-fe6e-4497-93cb-f1eb176a10da')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_delete_nonexistent_transfer(self):
+        self.assertRaises(lib_exc.NotFound,
+                          self.shares_v2_client.delete_share_transfer,
+                          str(uuidutils.generate_uuid()))
+
+    @decorators.idempotent_id('cc7af032-0504-417e-8ab9-73b37bed7f85')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    def test_accept_transfer_without_auth_key(self):
+        # create share
+        share_name = data_utils.rand_name("tempest-share-name")
+        share = self.create_share(
+            name=share_name,
+            share_type_id=self.share_type_id)
+
+        # create share transfer
+        transfer = self._create_share_transfer(share)
+
+        self.assertRaises(lib_exc.BadRequest,
+                          self.alt_shares_v2_client.accept_share_transfer,
+                          transfer['id'], "")
+
+    @decorators.idempotent_id('05a6a345-7609-421f-be21-d79041970674')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_accept_transfer_with_incorrect_auth_key(self):
+        # create share
+        share_name = data_utils.rand_name("tempest-share-name")
+        share = self.create_share(
+            name=share_name,
+            share_type_id=self.share_type_id)
+
+        # create share transfer
+        transfer = self._create_share_transfer(share)
+
+        self.assertRaises(lib_exc.BadRequest,
+                          self.alt_shares_v2_client.accept_share_transfer,
+                          transfer['id'], "incorrect_auth_key")
diff --git a/manila_tempest_tests/tests/api/test_share_types_negative.py b/manila_tempest_tests/tests/api/test_share_types_negative.py
index 046af45..af4815f 100644
--- a/manila_tempest_tests/tests/api/test_share_types_negative.py
+++ b/manila_tempest_tests/tests/api/test_share_types_negative.py
@@ -83,15 +83,17 @@
     @utils.skip_if_microversion_not_supported("2.50")
     @decorators.idempotent_id('4a22945c-8988-43a1-88c9-eb86e6abcd8e')
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
-    @ddt.data(
-        ('2.50', '', None, None),
-        (LATEST_MICROVERSION, '', None, None),
-        ('2.50', None, None, 'not_bool'),
-        (LATEST_MICROVERSION, None, None, 'not_bool'),
-        ('2.50', None, generate_long_description(256), None),
-        (LATEST_MICROVERSION, None, generate_long_description(256), None),
+    @ddt.named_data(
+        ('2_50', '2.50', '', None, None),
+        (LATEST_MICROVERSION, LATEST_MICROVERSION, '', None, None),
+        ('2_50_bad_public', '2.50', None, None, 'not_bool'),
+        (f'{LATEST_MICROVERSION}_bad_public', LATEST_MICROVERSION, None, None,
+         'not_bool'),
+        ('2_50_description', '2.50', None, generate_long_description(256),
+         None),
+        (f'{LATEST_MICROVERSION}_description', LATEST_MICROVERSION, None,
+         generate_long_description(256), None),
     )
-    @ddt.unpack
     def test_share_type_update_bad_request(
             self, version, st_name, st_description, st_is_public):
         st_id = self.st['id']
diff --git a/manila_tempest_tests/tests/api/test_shares_actions_negative.py b/manila_tempest_tests/tests/api/test_shares_actions_negative.py
index 977d9a1..da85f2e 100644
--- a/manila_tempest_tests/tests/api/test_shares_actions_negative.py
+++ b/manila_tempest_tests/tests/api/test_shares_actions_negative.py
@@ -288,7 +288,6 @@
 
         self.assertEqual(0, len(shares))
 
-    @decorators.skip_because(bug='1914363')
     @decorators.idempotent_id('e8f857f1-ec32-4f81-9e09-26065891dc93')
     @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
     def test_get_share_from_other_project(self):
diff --git a/manila_tempest_tests/tests/rbac/base.py b/manila_tempest_tests/tests/rbac/base.py
index b1eba9b..d3c63de 100644
--- a/manila_tempest_tests/tests/rbac/base.py
+++ b/manila_tempest_tests/tests/rbac/base.py
@@ -53,7 +53,7 @@
             'sn': 'share_network',
         }
         key, resource_id = list(kwargs.items())[0]
-        key = key.split('_')[0]
+        key = key.rsplit('_', 1)[0]
         resource_name = key_names[key] if key in key_names else key
 
         del_action = getattr(client, 'delete_{}'.format(resource_name))
@@ -116,6 +116,33 @@
         return share_type
 
     @classmethod
+    def create_share_group_type(cls, share_types, is_public=True,
+                                group_specs=None):
+        name = data_utils.rand_name('share-group-type')
+        share_group_type = (
+            cls.admin_shares_v2_client.create_share_group_type(
+                name=name, share_types=share_types, is_public=is_public,
+                group_specs=group_specs))['share_group_type']
+        cls.addClassResourceCleanup(
+            cls.delete_resource, cls.admin_shares_v2_client,
+            share_group_type_id=share_group_type['id'])
+        return share_group_type
+
+    @classmethod
+    def create_share_group(cls, client, share_group_type_id, share_type_ids):
+        name = data_utils.rand_name('share-group')
+        share_group = client.create_share_group(
+            name=name, share_group_type_id=share_group_type_id,
+            share_type_ids=share_type_ids)['share_group']
+        waiters.wait_for_resource_status(
+            client, share_group['id'], 'available',
+            resource_name='share_group')
+        cls.addClassResourceCleanup(
+            cls.delete_resource, client,
+            share_group_id=share_group['id'])
+        return share_group
+
+    @classmethod
     def get_share_type(cls):
         return cls.shares_v2_client.get_default_share_type()['share_type']
 
diff --git a/manila_tempest_tests/tests/rbac/test_share_group_types.py b/manila_tempest_tests/tests/rbac/test_share_group_types.py
new file mode 100644
index 0000000..ac2f54b
--- /dev/null
+++ b/manila_tempest_tests/tests/rbac/test_share_group_types.py
@@ -0,0 +1,372 @@
+# 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.
+
+import abc
+
+from tempest import config
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import constants
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests.tests.rbac import base as rbac_base
+from manila_tempest_tests import utils
+
+CONF = config.CONF
+
+
+class ShareRbacShareGroupTypesTests(rbac_base.ShareRbacBaseTests,
+                                    metaclass=abc.ABCMeta):
+
+    @classmethod
+    def setup_clients(cls):
+        super(ShareRbacShareGroupTypesTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.share_v2.SharesV2Client()
+        cls.admin_shares_v2_client = (
+            cls.os_project_admin.share_v2.SharesV2Client())
+
+    @classmethod
+    def skip_checks(cls):
+        super(ShareRbacShareGroupTypesTests, cls).skip_checks()
+        if not CONF.share.run_share_group_tests:
+            raise cls.skipException('Share Group tests disabled.')
+
+        utils.check_skip_if_microversion_not_supported(
+            constants.MIN_SHARE_GROUP_MICROVERSION)
+
+    @classmethod
+    def resource_setup(cls):
+        super(ShareRbacShareGroupTypesTests, cls).resource_setup()
+        cls.group_specs1 = {u'key1': u'value1'}
+        cls.group_specs2 = {u'key2': u'value2'}
+        cls.share_type = cls.create_share_type()
+        cls.share_group_type = cls.create_share_group_type(
+            cls.share_type['id'], group_specs=cls.group_specs1)
+        cls.private_share_group_type = cls.create_share_group_type(
+            cls.share_type['id'], is_public=False)
+
+    @abc.abstractmethod
+    def test_create_share_group_type(self):
+        pass
+
+    @abc.abstractmethod
+    def test_get_share_group_type(self):
+        pass
+
+    @abc.abstractmethod
+    def test_list_share_group_types(self):
+        pass
+
+    @abc.abstractmethod
+    def test_delete_share_group_type(self):
+        pass
+
+    @abc.abstractmethod
+    def test_create_share_group_type_extra_specs(self):
+        pass
+
+    @abc.abstractmethod
+    def test_update_share_group_type_extra_spec(self):
+        pass
+
+    @abc.abstractmethod
+    def test_delete_share_group_type_extra_spec(self):
+        pass
+
+    @abc.abstractmethod
+    def test_add_share_group_type_access(self):
+        pass
+
+    @abc.abstractmethod
+    def test_list_share_group_type_access(self):
+        pass
+
+    @abc.abstractmethod
+    def test_remove_share_group_type_access(self):
+        pass
+
+
+class ProjectAdminTests(ShareRbacShareGroupTypesTests, base.BaseSharesTest):
+
+    credentials = ['project_admin']
+
+    @decorators.idempotent_id('9ea9954a-ae09-4d02-a082-9a72b80009fc')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+    def test_create_share_group_type(self):
+        share_group_type = self.do_request(
+            'create_share_group_type', expected_status=200,
+            name='gt', share_types=self.share_type['id'])['share_group_type']
+        self.addCleanup(self.delete_resource, self.client,
+                        share_group_type_id=share_group_type['id'])
+
+    @decorators.idempotent_id('fcad2b86-ca43-42b0-82bd-37e6f760e4d2')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+    def test_get_share_group_type(self):
+        self.do_request(
+            'get_share_group_type', expected_status=200,
+            share_group_type_id=self.share_group_type['id'])
+
+    @decorators.idempotent_id('7871b1b5-610a-425c-9363-d0bcf2beff72')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+    def test_list_share_group_types(self):
+        share_group_type_list = self.do_request(
+            'list_share_group_types', expected_status=200)['share_group_types']
+        share_group_type_id_list = [
+            sgt['id'] for sgt in share_group_type_list
+        ]
+        self.assertIn(self.share_group_type['id'], share_group_type_id_list)
+
+    @decorators.idempotent_id('c23da121-8b1f-4443-80cc-11881745a1c3')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+    def test_delete_share_group_type(self):
+        share_group_type = self.create_share_group_type(
+            share_types=self.share_type['id'])
+        self.do_request(
+            'delete_share_group_type', expected_status=204,
+            share_group_type_id=share_group_type['id'])
+
+    @decorators.idempotent_id('80eb22cb-846a-4b51-a71d-ceef0b804901')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+    def test_create_share_group_type_extra_specs(self):
+        self.do_request(
+            'create_share_group_type_specs', expected_status=200,
+            share_group_type_id=self.share_group_type['id'],
+            group_specs_dict=self.group_specs2)
+        self.addCleanup(
+            self.admin_shares_v2_client.delete_share_group_type_spec,
+            self.share_group_type['id'], group_spec_key='key2')
+
+    @decorators.idempotent_id('fe29877d-2226-42ca-b492-4be0dacd6eaf')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+    def test_update_share_group_type_extra_spec(self):
+        self.do_request(
+            'update_share_group_type_spec', expected_status=200,
+            share_group_type_id=self.share_group_type['id'],
+            group_spec_key='key', group_spec_value='value_updated')
+
+    @decorators.idempotent_id('743c18dc-8c3a-4934-9ef8-8b342daffe7c')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+    def test_delete_share_group_type_extra_spec(self):
+        self.admin_shares_v2_client.create_share_group_type_specs(
+            self.share_group_type['id'], self.group_specs2)
+        self.do_request(
+            'delete_share_group_type_spec', expected_status=204,
+            share_group_type_id=self.share_group_type['id'],
+            group_spec_key='key2')
+
+    @decorators.idempotent_id('89876c46-1167-450d-8b98-746d97fff388')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+    def test_add_share_group_type_access(self):
+        self.do_request(
+            'add_access_to_share_group_type', expected_status=202,
+            share_group_type_id=self.private_share_group_type['id'],
+            project_id=self.client.project_id)
+        self.addCleanup(
+            self.client.remove_access_from_share_group_type,
+            share_group_type_id=self.private_share_group_type['id'],
+            project_id=self.client.project_id)
+
+    @decorators.idempotent_id('64a7be53-d1af-40c3-950b-743a2704ac97')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+    def test_list_share_group_type_access(self):
+        self.client.add_access_to_share_group_type(
+            self.private_share_group_type['id'], self.client.project_id)
+        self.addCleanup(
+            self.client.remove_access_from_share_group_type,
+            share_group_type_id=self.private_share_group_type['id'],
+            project_id=self.client.project_id)
+        access_list = self.do_request(
+            'list_access_to_share_group_type', expected_status=200,
+            share_group_type_id=self.private_share_group_type['id']
+        )['share_group_type_access']
+
+        project_id_list = [
+            access['project_id'] for access in access_list
+        ]
+
+        self.assertIn(self.client.project_id, project_id_list)
+
+    @decorators.idempotent_id('fc67ba18-78e9-4c02-9b37-2bd49c8a4470')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+    def test_remove_share_group_type_access(self):
+        self.client.add_access_to_share_group_type(
+            self.private_share_group_type['id'], self.client.project_id)
+        self.do_request(
+            'remove_access_from_share_group_type', expected_status=202,
+            share_group_type_id=self.private_share_group_type['id'],
+            project_id=self.client.project_id)
+
+
+class ProjectMemberTests(ShareRbacShareGroupTypesTests, base.BaseSharesTest):
+
+    credentials = ['project_member', 'project_admin']
+
+    @decorators.idempotent_id('f9e6f2fd-7c1a-4eee-817c-bf1988904515')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_create_share_group_type(self):
+        self.do_request(
+            'create_share_group_type', expected_status=lib_exc.Forbidden,
+            name='gt', share_types=self.share_type['id'])
+
+    @decorators.idempotent_id('8eaf4a99-9706-41c9-8b12-40856d0900f4')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+    def test_get_share_group_type(self):
+        self.do_request(
+            'get_share_group_type', expected_status=200,
+            share_group_type_id=self.share_group_type['id'])
+
+    @decorators.idempotent_id('d2dd61f4-c763-49f9-9c93-8b587879f554')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+    def test_list_share_group_types(self):
+        share_group_type_list = self.do_request(
+            'list_share_group_types', expected_status=200)['share_group_types']
+        share_group_type_id_list = [
+            sgt['id'] for sgt in share_group_type_list
+        ]
+        self.assertIn(self.share_group_type['id'], share_group_type_id_list)
+
+    @decorators.idempotent_id('8f45798f-717d-41b0-acba-a80dd647cddf')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_delete_share_group_type(self):
+        share_group_type = self.create_share_group_type(
+            share_types=self.share_type['id'])
+        self.do_request(
+            'delete_share_group_type', expected_status=lib_exc.Forbidden,
+            share_group_type_id=share_group_type['id'])
+
+    @decorators.idempotent_id('2fa2f953-f068-4c60-ab52-eeb1bd69a7a4')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_create_share_group_type_extra_specs(self):
+        self.do_request(
+            'create_share_group_type_specs', expected_status=lib_exc.Forbidden,
+            share_group_type_id=self.share_group_type['id'],
+            group_specs_dict=self.group_specs2)
+
+    @decorators.idempotent_id('30c73c94-b7bc-4e1f-8172-192335997879')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_update_share_group_type_extra_spec(self):
+        self.do_request(
+            'update_share_group_type_spec', expected_status=lib_exc.Forbidden,
+            share_group_type_id=self.share_group_type['id'],
+            group_spec_key='key', group_spec_value='value_updated')
+
+    @decorators.idempotent_id('95aa1fbf-3eaf-4d65-96b5-4554b0fb0937')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_delete_share_group_type_extra_spec(self):
+        self.admin_shares_v2_client.create_share_group_type_specs(
+            self.share_group_type['id'], self.group_specs2)
+        self.do_request(
+            'delete_share_group_type_spec', expected_status=lib_exc.Forbidden,
+            share_group_type_id=self.share_group_type['id'],
+            group_spec_key='key2')
+
+    @decorators.idempotent_id('85b69e26-9c67-45c4-80e0-2ce212977c2a')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_add_share_group_type_access(self):
+        self.do_request(
+            'add_access_to_share_group_type',
+            expected_status=lib_exc.Forbidden,
+            share_group_type_id=self.private_share_group_type['id'],
+            project_id=self.client.project_id)
+
+    @decorators.idempotent_id('027314a9-6b14-4ae9-83f2-471e84ccaa01')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_list_share_group_type_access(self):
+        self.admin_shares_v2_client.add_access_to_share_group_type(
+            self.private_share_group_type['id'], self.client.project_id)
+        self.addCleanup(
+            self.admin_shares_v2_client.remove_access_from_share_group_type,
+            share_group_type_id=self.private_share_group_type['id'],
+            project_id=self.client.project_id)
+        self.do_request(
+            'list_access_to_share_group_type',
+            expected_status=lib_exc.Forbidden,
+            share_group_type_id=self.private_share_group_type['id'])
+
+    @decorators.idempotent_id('d8518122-dabd-4d2d-8b6a-4eed975e19ee')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_remove_share_group_type_access(self):
+        self.admin_shares_v2_client.add_access_to_share_group_type(
+            self.private_share_group_type['id'], self.client.project_id)
+        self.addCleanup(
+            self.admin_shares_v2_client.remove_access_from_share_group_type,
+            share_group_type_id=self.private_share_group_type['id'],
+            project_id=self.client.project_id)
+        self.do_request(
+            'remove_access_from_share_group_type',
+            expected_status=lib_exc.Forbidden,
+            share_group_type_id=self.private_share_group_type['id'],
+            project_id=self.client.project_id)
+
+
+class ProjectReaderTests(ProjectMemberTests):
+
+    credentials = ['project_reader', 'project_member', 'project_admin']
+
+    @decorators.idempotent_id('8c47bbe9-f3f1-419e-b00b-97c9c942a48a')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_create_share_group_type(self):
+        super(ProjectReaderTests, self).test_create_share_group_type()
+
+    @decorators.idempotent_id('fe3b28a3-6980-4782-8eaa-518bbd3913d1')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+    def test_get_share_group_type(self):
+        super(ProjectReaderTests, self).test_get_share_group_type()
+
+    @decorators.idempotent_id('47f92f0b-424e-4685-a742-8a4e00cc6901')
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API)
+    def test_list_share_group_types(self):
+        super(ProjectReaderTests, self).test_list_share_group_types()
+
+    @decorators.idempotent_id('e853fd60-8906-4e38-b0b4-ec5723696518')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_delete_share_group_type(self):
+        super(ProjectReaderTests, self).test_delete_share_group_type()
+
+    @decorators.idempotent_id('caa6d960-4f34-4dff-9cc0-9a0cde44c2ae')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_create_share_group_type_extra_specs(self):
+        super(
+            ProjectReaderTests,
+            self).test_create_share_group_type_extra_specs()
+
+    @decorators.idempotent_id('2782c329-2447-49f7-95e7-0c4c766cfda3')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_update_share_group_type_extra_spec(self):
+        super(
+            ProjectReaderTests, self).test_update_share_group_type_extra_spec()
+
+    @decorators.idempotent_id('003a0eee-0075-47b0-ba8a-8e57e958c1d3')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_delete_share_group_type_extra_spec(self):
+        super(
+            ProjectReaderTests, self).test_delete_share_group_type_extra_spec()
+
+    @decorators.idempotent_id('93bba264-104c-48af-af91-5a79ec3e695e')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_add_share_group_type_access(self):
+        super(ProjectReaderTests, self).test_add_share_group_type_access()
+
+    @decorators.idempotent_id('176b280c-7a46-43ae-8164-3bdd945d2dd4')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_list_share_group_type_access(self):
+        super(ProjectReaderTests, self).test_list_share_group_type_access()
+
+    @decorators.idempotent_id('00caa020-3805-4322-aac1-86d4552268a2')
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API)
+    def test_remove_share_group_type_access(self):
+        super(ProjectReaderTests, self).test_remove_share_group_type_access()
diff --git a/manila_tempest_tests/tests/rbac/test_share_groups.py b/manila_tempest_tests/tests/rbac/test_share_groups.py
new file mode 100644
index 0000000..b09a5e4
--- /dev/null
+++ b/manila_tempest_tests/tests/rbac/test_share_groups.py
@@ -0,0 +1,474 @@
+# 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.
+
+import abc
+
+from tempest import config
+from tempest.lib.common.utils import data_utils
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+from testtools import testcase as tc
+
+from manila_tempest_tests.common import waiters
+from manila_tempest_tests.tests.api import base
+from manila_tempest_tests.tests.rbac import base as rbac_base
+
+CONF = config.CONF
+
+
+class ShareRbacShareGroupsTests(rbac_base.ShareRbacBaseTests,
+                                metaclass=abc.ABCMeta):
+
+    @classmethod
+    def skip_checks(cls):
+        super(ShareRbacShareGroupsTests, cls).skip_checks()
+        if cls.protocol not in CONF.share.enable_protocols:
+            message = "%s tests are disabled" % cls.protocol
+            raise cls.skipException(message)
+
+    @classmethod
+    def setup_clients(cls):
+        super(ShareRbacShareGroupsTests, cls).setup_clients()
+        cls.persona = getattr(cls, 'os_%s' % cls.credentials[0])
+        cls.client = cls.persona.share_v2.SharesV2Client()
+        cls.admin_shares_v2_client = (
+            cls.os_project_admin.share_v2.SharesV2Client())
+        cls.alt_project_share_v2_client = (
+            cls.os_project_alt_member.share_v2.SharesV2Client())
+
+    @classmethod
+    def resource_setup(cls):
+        super(ShareRbacShareGroupsTests, cls).resource_setup()
+        cls.share_type = cls.create_share_type()
+        cls.share_group_type = cls.create_share_group_type(
+            cls.share_type['id'])
+
+    def share_group(self, share_group_type_id, share_type_ids):
+        share_group = {}
+        share_group['name'] = data_utils.rand_name('share_group')
+        share_group['share_group_type_id'] = share_group_type_id
+        share_group['share_type_ids'] = [share_type_ids]
+        return share_group
+
+    @abc.abstractmethod
+    def test_get_share_group(self):
+        pass
+
+    @abc.abstractmethod
+    def test_list_share_groups(self):
+        pass
+
+    @abc.abstractmethod
+    def test_create_share_group(self):
+        pass
+
+    @abc.abstractmethod
+    def test_delete_share_group(self):
+        pass
+
+    @abc.abstractmethod
+    def test_force_delete_share_group(self):
+        pass
+
+    @abc.abstractmethod
+    def test_update_share_group(self):
+        pass
+
+    @abc.abstractmethod
+    def test_reset_share_group(self):
+        pass
+
+
+class TestProjectAdminTestsNFS(ShareRbacShareGroupsTests, base.BaseSharesTest):
+
+    credentials = ['project_admin', 'project_alt_member']
+    protocol = 'nfs'
+
+    @classmethod
+    def setup_clients(cls):
+        super(TestProjectAdminTestsNFS, cls).setup_clients()
+        project_member = cls.setup_user_client(
+            cls.persona, project_id=cls.persona.credentials.project_id)
+        cls.share_member_client = project_member.share_v2.SharesV2Client()
+
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('0de993c5-8389-4997-8f7f-345e27f563f1')
+    def test_get_share_group(self):
+        share_group = self.create_share_group(
+            self.share_member_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'get_share_group', expected_status=200,
+            share_group_id=share_group['id'])
+
+        alt_share_group = self.create_share_group(
+            self.alt_project_share_v2_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'get_share_group', expected_status=200,
+            share_group_id=alt_share_group['id'])
+
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('3b277a44-dcae-46da-a58c-f5281d8abc84')
+    def test_list_share_groups(self):
+        share_group = self.create_share_group(
+            self.share_member_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        alt_share_group = self.create_share_group(
+            self.alt_project_share_v2_client, self.share_group_type['id'],
+            [self.share_type['id']])
+
+        params = {"all_tenants": 1}
+        share_group_list = self.do_request(
+            'list_share_groups', expected_status=200,
+            params=params)['share_groups']
+        share_group_id_list = [
+            s['id'] for s in share_group_list
+        ]
+
+        self.assertIn(share_group['id'], share_group_id_list)
+        self.assertIn(alt_share_group['id'], share_group_id_list)
+
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('d060996e-c5f2-4dff-820b-6892a096a425')
+    def test_create_share_group(self):
+        share_group = self.do_request(
+            'create_share_group', expected_status=202,
+            **self.share_group(self.share_group_type['id'],
+                               self.share_type['id']))['share_group']
+        waiters.wait_for_resource_status(
+            self.client, share_group['id'], 'available',
+            resource_name='share_group')
+        self.addCleanup(self.client.wait_for_resource_deletion,
+                        share_group_id=share_group['id'])
+        self.addCleanup(self.client.delete_share_group, share_group['id'])
+
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('ea6cbb78-057e-4fbc-86bf-125b033cb76f')
+    def test_delete_share_group(self):
+        share_group = self.create_share_group(
+            self.share_member_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'delete_share_group', expected_status=202,
+            share_group_id=share_group['id'])
+        self.client.wait_for_resource_deletion(
+            share_group_id=share_group['id'])
+
+        alt_share_group = self.create_share_group(
+            self.alt_project_share_v2_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'delete_share_group', expected_status=202,
+            share_group_id=alt_share_group['id'])
+        self.client.wait_for_resource_deletion(
+            share_group_id=alt_share_group['id'])
+
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('2cb00ffb-47e3-495e-853c-007752c9e679')
+    def test_force_delete_share_group(self):
+        share_group = self.create_share_group(
+            self.share_member_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'share_group_force_delete', expected_status=202,
+            share_group_id=share_group['id'])
+        self.client.wait_for_resource_deletion(
+            share_group_id=share_group['id'])
+
+        alt_share_group = self.create_share_group(
+            self.alt_project_share_v2_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'share_group_force_delete', expected_status=202,
+            share_group_id=alt_share_group['id'])
+        self.client.wait_for_resource_deletion(
+            share_group_id=alt_share_group['id'])
+
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('1bab40d5-bdba-4a23-9300-807fe513bf15')
+    def test_update_share_group(self):
+        share_group = self.create_share_group(
+            self.share_member_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        name = data_utils.rand_name('rename_share')
+        self.do_request(
+            'update_share_group', expected_status=200,
+            share_group_id=share_group['id'], name=name)
+
+        alt_share_group = self.create_share_group(
+            self.share_member_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        name = data_utils.rand_name('rename_share')
+        self.do_request(
+            'update_share_group', expected_status=200,
+            share_group_id=alt_share_group['id'], name=name)
+
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('069bc68e-6411-44b8-abe9-399885f0eee5')
+    def test_reset_share_group(self):
+        share_group = self.create_share_group(
+            self.share_member_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'share_group_reset_state', expected_status=202,
+            share_group_id=share_group['id'], status='error')
+
+        alt_share_group = self.create_share_group(
+            self.share_member_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'share_group_reset_state', expected_status=202,
+            share_group_id=alt_share_group['id'], status='error')
+
+
+class TestProjectMemberTestsNFS(ShareRbacShareGroupsTests,
+                                base.BaseSharesTest):
+
+    credentials = ['project_member', 'project_admin', 'project_alt_member']
+    protocol = 'nfs'
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('a29e1a68-220e-40fc-98ea-9092fd256d07')
+    def test_get_share_group(self):
+        share_client = getattr(self, 'share_member_client', self.client)
+        share_group = self.create_share_group(
+            share_client, self.share_group_type['id'], [self.share_type['id']])
+        self.do_request(
+            'get_share_group', expected_status=200,
+            share_group_id=share_group['id'])
+
+        alt_share_group = self.create_share_group(
+            self.alt_project_share_v2_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'get_share_group', expected_status=lib_exc.NotFound,
+            share_group_id=alt_share_group['id'])
+
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('d9c04932-c47e-46e0-bfcf-79c2af32c4c7')
+    def test_list_share_groups(self):
+        share_client = getattr(self, 'share_member_client', self.client)
+        share_group = self.create_share_group(
+            share_client, self.share_group_type['id'], [self.share_type['id']])
+        alt_share_group = self.create_share_group(
+            self.alt_project_share_v2_client, self.share_group_type['id'],
+            [self.share_type['id']])
+
+        params = {"all_tenants": 1}
+        share_group_list = self.do_request(
+            'list_share_groups', expected_status=200,
+            params=params)['share_groups']
+        share_group_id_list = [
+            s['id'] for s in share_group_list
+        ]
+
+        self.assertIn(share_group['id'], share_group_id_list)
+        self.assertNotIn(alt_share_group['id'], share_group_id_list)
+
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('ebad2242-1fb5-4d99-9a5a-281c1944e03d')
+    def test_create_share_group(self):
+        share_group = self.do_request(
+            'create_share_group', expected_status=202,
+            **self.share_group(self.share_group_type['id'],
+                               self.share_type['id']))['share_group']
+        waiters.wait_for_resource_status(
+            self.client, share_group['id'], 'available',
+            resource_name='share_group')
+        self.addCleanup(self.client.wait_for_resource_deletion,
+                        share_group_id=share_group['id'])
+        self.addCleanup(self.client.delete_share_group, share_group['id'])
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('f5c243e4-5128-4a1c-9a15-8c9f0a44437e')
+    def test_delete_share_group(self):
+        share_group = self.create_share_group(
+            self.client, self.share_group_type['id'], [self.share_type['id']])
+        self.do_request(
+            'delete_share_group', expected_status=202,
+            share_group_id=share_group['id'])
+        self.client.wait_for_resource_deletion(
+            share_group_id=share_group['id'])
+
+        alt_share_group = self.create_share_group(
+            self.alt_project_share_v2_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'delete_share_group', expected_status=lib_exc.NotFound,
+            share_group_id=alt_share_group['id'])
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('36a58d50-1257-479f-80a2-f9b7a00814e2')
+    def test_force_delete_share_group(self):
+        share_client = getattr(self, 'share_member_client', self.client)
+        share_group = self.create_share_group(
+            share_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'share_group_force_delete', expected_status=lib_exc.Forbidden,
+            share_group_id=share_group['id'])
+
+        alt_share_group = self.create_share_group(
+            self.alt_project_share_v2_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'share_group_force_delete', expected_status=lib_exc.Forbidden,
+            share_group_id=alt_share_group['id'])
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('cf9e34b6-6c04-4920-a811-2dbcf07ba14e')
+    def test_update_share_group(self):
+        share_group = self.create_share_group(
+            self.client, self.share_group_type['id'], [self.share_type['id']])
+        name = data_utils.rand_name('rename_share')
+        self.do_request(
+            'update_share_group', expected_status=200,
+            share_group_id=share_group['id'], name=name)
+
+        alt_share_group = self.create_share_group(
+            self.alt_project_share_v2_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        name = data_utils.rand_name('rename_share')
+        self.do_request(
+            'update_share_group', expected_status=lib_exc.NotFound,
+            share_group_id=alt_share_group['id'], name=name)
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('2108c4cd-74e0-467f-823a-e44cf8686afa')
+    def test_reset_share_group(self):
+        share_client = getattr(self, 'share_member_client', self.client)
+        share_group = self.create_share_group(
+            share_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'share_group_reset_state', expected_status=lib_exc.Forbidden,
+            share_group_id=share_group['id'], status='error')
+
+        alt_share_group = self.create_share_group(
+            self.alt_project_share_v2_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'share_group_reset_state', expected_status=lib_exc.Forbidden,
+            share_group_id=alt_share_group['id'], status='error')
+
+
+class TestProjectReaderTestsNFS(TestProjectMemberTestsNFS):
+    """Test suite for basic share group operations by reader user
+
+    In order to test certain share operations we must create a share group
+    resource for this. Since reader user is limited in resources creation, we
+    are forced to use admin credentials, so we can test other share operations.
+    In this class we use admin user to create a member user within reader
+    project. That way we can perform a reader actions on this resource.
+    """
+
+    credentials = ['project_reader', 'project_admin', 'project_alt_member']
+
+    @classmethod
+    def setup_clients(cls):
+        super(TestProjectReaderTestsNFS, cls).setup_clients()
+        project_member = cls.setup_user_client(
+            cls.os_project_admin,
+            project_id=cls.persona.credentials.project_id)
+        cls.share_member_client = project_member.share_v2.SharesV2Client()
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('ec0ecbb0-5d45-4624-bb26-8b2e140e2ea9')
+    def test_get_share_group(self):
+        super(TestProjectReaderTestsNFS, self).test_get_share_group()
+
+    @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('4ac87837-5bdf-4253-ab50-dd6efdcea285')
+    def test_list_share_groups(self):
+        super(TestProjectReaderTestsNFS, self).test_list_share_groups()
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('526dcd91-e789-48f8-b209-c384d77e5803')
+    def test_create_share_group(self):
+        self.do_request(
+            'create_share_group', expected_status=lib_exc.Forbidden,
+            **self.share_group(self.share_group_type['id'],
+                               self.share_type['id']))
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('fdf4d49e-a576-441f-9a3c-e2d58c0d8679')
+    def test_delete_share_group(self):
+        share_group = self.create_share_group(
+            self.share_member_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'delete_share_group', expected_status=lib_exc.Forbidden,
+            share_group_id=share_group['id'])
+
+        alt_share_group = self.create_share_group(
+            self.alt_project_share_v2_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        self.do_request(
+            'delete_share_group', expected_status=lib_exc.Forbidden,
+            share_group_id=alt_share_group['id'])
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('eddca093-e3a1-4a79-a8c7-8fd04c77b02f')
+    def test_force_delete_share_group(self):
+        super(TestProjectReaderTestsNFS, self).test_force_delete_share_group()
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('4530c19d-0aa5-402e-ac83-a3f2333f6c71')
+    def test_update_share_group(self):
+        share_group = self.create_share_group(
+            self.share_member_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        name = data_utils.rand_name('rename_share')
+        self.do_request(
+            'update_share_group', expected_status=lib_exc.Forbidden,
+            share_group_id=share_group['id'], name=name)
+
+        alt_share_group = self.create_share_group(
+            self.alt_project_share_v2_client, self.share_group_type['id'],
+            [self.share_type['id']])
+        name = data_utils.rand_name('rename_share')
+        self.do_request(
+            'update_share_group', expected_status=lib_exc.Forbidden,
+            share_group_id=alt_share_group['id'], name=name)
+
+    @tc.attr(base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND)
+    @decorators.idempotent_id('37f23531-69b5-418d-bd91-7913341586ec')
+    def test_reset_share_group(self):
+        super(TestProjectReaderTestsNFS, self).test_reset_share_group()
+
+
+class TestProjectAdminTestsCEPHFS(TestProjectAdminTestsNFS):
+    protocol = 'cephfs'
+
+
+class TestProjectMemberTestsCEPHFS(TestProjectMemberTestsNFS):
+    protocol = 'cephfs'
+
+
+class TestProjectReaderTestsCEPHFS(TestProjectReaderTestsNFS):
+    protocol = 'cephfs'
+
+
+class TestProjectAdminTestsCIFS(TestProjectAdminTestsNFS):
+    protocol = 'cifs'
+
+
+class TestProjectMemberTestsCIFS(TestProjectMemberTestsNFS):
+    protocol = 'cifs'
+
+
+class TestProjectReaderTestsCIFS(TestProjectReaderTestsNFS):
+    protocol = 'cifs'
diff --git a/manila_tempest_tests/tests/rbac/test_shares.py b/manila_tempest_tests/tests/rbac/test_shares.py
index 2ca3c92..b5ea7d9 100644
--- a/manila_tempest_tests/tests/rbac/test_shares.py
+++ b/manila_tempest_tests/tests/rbac/test_shares.py
@@ -420,7 +420,7 @@
         alt_share = self.create_share(
             self.alt_project_share_v2_client, self.share_type['id'])
         self.do_request(
-            'reset_state', expected_status=lib_exc.NotFound,
+            'reset_state', expected_status=lib_exc.Forbidden,
             s_id=alt_share['id'], status="error")
 
     @decorators.idempotent_id('56a07567-d0a9-460a-9267-fcd82306a371')
@@ -465,7 +465,7 @@
         alt_share = self.create_share(
             self.alt_project_share_v2_client, self.share_type['id'])
         self.do_request(
-            'set_metadata', expected_status=lib_exc.Forbidden,
+            'set_metadata', expected_status=lib_exc.NotFound,
             resource_id=alt_share['id'], metadata={'key': 'value'})
 
     @decorators.idempotent_id('a69a2b85-3374-4621-83a9-89937ddb520b')
@@ -482,7 +482,7 @@
             self.alt_project_share_v2_client, self.share_type['id'],
             metadata=metadata)
         self.do_request(
-            'get_metadata', expected_status=lib_exc.Forbidden,
+            'get_metadata', expected_status=lib_exc.NotFound,
             resource_id=alt_share['id'])
 
     @decorators.idempotent_id('bea5518a-338e-494d-9034-1d03658ed58b')
@@ -498,7 +498,7 @@
             self.alt_project_share_v2_client, self.share_type['id'],
             metadata={'key': 'value'})
         self.do_request(
-            'delete_metadata', expected_status=lib_exc.Forbidden,
+            'delete_metadata', expected_status=lib_exc.NotFound,
             resource_id=alt_share['id'], key='key')
 
 
@@ -551,7 +551,7 @@
         alt_share = self.create_share(
             self.alt_project_share_v2_client, self.share_type['id'])
         self.do_request(
-            'delete_share', expected_status=lib_exc.NotFound,
+            'delete_share', expected_status=lib_exc.Forbidden,
             share_id=alt_share['id'])
 
     @decorators.idempotent_id('cb040955-5897-409f-aea0-84b6ae16b77e')
@@ -596,7 +596,7 @@
             self.alt_project_share_v2_client, self.share_type['id'],
             size=CONF.share.share_size + 1)
         self.do_request(
-            'shrink_share', expected_status=lib_exc.NotFound,
+            'shrink_share', expected_status=lib_exc.Forbidden,
             share_id=alt_share['id'], new_size=CONF.share.share_size)
 
     @decorators.idempotent_id('0b57aedb-6b68-498f-814e-173c47e6c307')
@@ -612,7 +612,7 @@
         alt_share = self.create_share(
             self.alt_project_share_v2_client, self.share_type['id'])
         self.do_request(
-            'extend_share', expected_status=lib_exc.NotFound,
+            'extend_share', expected_status=lib_exc.Forbidden,
             share_id=alt_share['id'], new_size=CONF.share.share_size + 1)
 
     @decorators.idempotent_id('3def3f4e-33fc-4726-8818-6cffbc2cab51')
diff --git a/manila_tempest_tests/tests/rbac/test_snapshots.py b/manila_tempest_tests/tests/rbac/test_snapshots.py
index 810ba9b..1978415 100644
--- a/manila_tempest_tests/tests/rbac/test_snapshots.py
+++ b/manila_tempest_tests/tests/rbac/test_snapshots.py
@@ -497,7 +497,7 @@
         alt_snap = self.create_snapshot(
             self.alt_project_share_v2_client, self.alt_share['id'])
         self.do_request(
-            'delete_snapshot', expected_status=lib_exc.NotFound,
+            'delete_snapshot', expected_status=lib_exc.Forbidden,
             snap_id=alt_snap['id'])
 
     @decorators.idempotent_id('ed0af390-e3d0-432b-9147-c0d569181b92')
@@ -517,7 +517,7 @@
         alt_snap = self.create_snapshot(
             self.alt_project_share_v2_client, self.alt_share['id'])
         self.do_request(
-            'rename_snapshot', expected_status=lib_exc.NotFound,
+            'rename_snapshot', expected_status=lib_exc.Forbidden,
             snapshot_id=alt_snap['id'], name=name)
 
     @decorators.idempotent_id('b8c9c9a4-3b2a-4b1c-80d8-2ec87d708111')
diff --git a/manila_tempest_tests/tests/scenario/manager.py b/manila_tempest_tests/tests/scenario/manager.py
index a73ab44..fe2833a 100644
--- a/manila_tempest_tests/tests/scenario/manager.py
+++ b/manila_tempest_tests/tests/scenario/manager.py
@@ -16,7 +16,6 @@
 
 from oslo_log import log
 from oslo_utils import uuidutils
-from tempest.common import image as common_image
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
@@ -45,24 +44,16 @@
             'name': name,
             'container_format': fmt,
             'disk_format': disk_format or fmt,
+            'visibility': 'private'
         }
-        if CONF.image_feature_enabled.api_v1:
-            params['is_public'] = 'False'
-            params['properties'] = properties
-            params = {'headers': common_image.image_meta_to_headers(**params)}
-        else:
-            params['visibility'] = 'private'
-            # Additional properties are flattened out in the v2 API.
-            params.update(properties)
+        # Additional properties are flattened out in the v2 API.
+        params.update(properties)
         body = self.image_client.create_image(**params)
         image = body['image'] if 'image' in body else body
         self.addCleanup(self.image_client.delete_image, image['id'])
         self.assertEqual("queued", image['status'])
         with open(path, 'rb') as image_file:
-            if CONF.image_feature_enabled.api_v1:
-                self.image_client.update_image(image['id'], data=image_file)
-            else:
-                self.image_client.store_image_file(image['id'], image_file)
+            self.image_client.store_image_file(image['id'], image_file)
         return image['id']
 
     def glance_image_create(self):
diff --git a/manila_tempest_tests/tests/scenario/manager_share.py b/manila_tempest_tests/tests/scenario/manager_share.py
index a847217..3e06323 100644
--- a/manila_tempest_tests/tests/scenario/manager_share.py
+++ b/manila_tempest_tests/tests/scenario/manager_share.py
@@ -84,7 +84,7 @@
         if CONF.share.image_with_share_tools == 'centos':
             self.image_ref = self._create_centos_based_glance_image()
         elif CONF.share.image_with_share_tools:
-            images = self.compute_images_client.list_images()["images"]
+            images = self.image_client.list_images()["images"]
             for img in images:
                 if img["name"] == CONF.share.image_with_share_tools:
                     self.image_id = img['id']
@@ -186,8 +186,7 @@
                         storage_net_nic[0]['addr']
                     )
             # Attach a floating IP
-            self.compute_floating_ips_client.associate_floating_ip_to_server(
-                floating_ip['floating_ip_address'], instance['id'])
+            self.associate_floating_ip(floating_ip, instance)
 
         self.assertIsNotNone(server_ip)
         # Check ssh
@@ -230,8 +229,7 @@
     def write_data_to_mounted_share_using_dd(self, remote_client,
                                              output_file,
                                              block_size,
-                                             block_count,
-                                             input_file='/dev/zero'):
+                                             block_count):
         """Writes data to mounted share using dd command
 
         Example Usage for writing 512Mb to a file on /mnt/
@@ -244,13 +242,12 @@
         :param block_size: The size of an individual block in bytes
         :param block_count: The number of blocks to write
         :param output_file: Path to the file to be written
-        :param input_file: Path to the file to read from
         """
         block_count = int(block_count)
         remote_client.exec_command(
-            "sudo sh -c \"dd bs={} count={} if={} of={} conv=fsync"
-            " iflag=fullblock\""
-            .format(block_size, block_count, input_file, output_file))
+            "sudo sh -c \"dd bs={} count={} if={} of={} iflag=fullblock\""
+            .format(block_size, block_count, CONF.share.dd_input_file,
+                    output_file))
 
     def read_data_from_mounted_share(self,
                                      remote_client,
diff --git a/manila_tempest_tests/tests/scenario/test_share_extend.py b/manila_tempest_tests/tests/scenario/test_share_extend.py
index 595ddd4..450d2d4 100644
--- a/manila_tempest_tests/tests/scenario/test_share_extend.py
+++ b/manila_tempest_tests/tests/scenario/test_share_extend.py
@@ -76,8 +76,7 @@
                   .format(three_quarter_blocks))
         self.write_data_to_mounted_share_using_dd(remote_client,
                                                   '/mnt/t1', '64M',
-                                                  three_quarter_blocks,
-                                                  '/dev/urandom')
+                                                  three_quarter_blocks)
         ls_result = remote_client.exec_command("sudo ls -lAh /mnt/")
         LOG.debug(ls_result)
 
@@ -86,8 +85,7 @@
         self.assertRaises(
             exceptions.SSHExecCommandFailed,
             self.write_data_to_mounted_share_using_dd,
-            remote_client, '/mnt/t2', '64M', over_one_quarter_blocks,
-            '/dev/urandom')
+            remote_client, '/mnt/t2', '64M', over_one_quarter_blocks)
         ls_result = remote_client.exec_command("sudo ls -lAh /mnt/")
         LOG.debug(ls_result)
 
@@ -129,8 +127,7 @@
             self.write_data_to_mounted_share_using_dd(remote_client,
                                                       output_file,
                                                       block_size,
-                                                      block_count,
-                                                      '/dev/urandom')
+                                                      block_count)
         except exceptions.SSHExecCommandFailed as e:
             if 'stale file handle' in str(e).lower():
                 LOG.warning("Client was disconnected during extend process")
@@ -139,8 +136,7 @@
                 self.write_data_to_mounted_share_using_dd(remote_client,
                                                           output_file,
                                                           block_size,
-                                                          block_count,
-                                                          '/dev/urandom')
+                                                          block_count)
             else:
                 raise
 
diff --git a/manila_tempest_tests/tests/scenario/test_share_manage_unmanage.py b/manila_tempest_tests/tests/scenario/test_share_manage_unmanage.py
index ef2a6af..bbf5ba6 100644
--- a/manila_tempest_tests/tests/scenario/test_share_manage_unmanage.py
+++ b/manila_tempest_tests/tests/scenario/test_share_manage_unmanage.py
@@ -105,7 +105,7 @@
         LOG.debug('Step 6b - writing 640mb')
         self.write_data_to_mounted_share_using_dd(remote_client,
                                                   '/mnt/t1', 1024,
-                                                  2048, '/dev/zero')
+                                                  2048)
         ls_result = remote_client.exec_command("sudo ls -lA /mnt/")
         LOG.debug(ls_result)
 
diff --git a/manila_tempest_tests/tests/scenario/test_share_shrink.py b/manila_tempest_tests/tests/scenario/test_share_shrink.py
index a4e59e8..fab7e0a 100644
--- a/manila_tempest_tests/tests/scenario/test_share_shrink.py
+++ b/manila_tempest_tests/tests/scenario/test_share_shrink.py
@@ -79,7 +79,7 @@
         LOG.debug('Step 6 - writing {} * 64MB blocks'.format(blocks))
         self.write_data_to_mounted_share_using_dd(remote_client,
                                                   '/mnt/t1', '64M',
-                                                  blocks, '/dev/urandom')
+                                                  blocks)
         ls_result = remote_client.exec_command("sudo ls -lAh /mnt/")
         LOG.debug(ls_result)
 
@@ -120,7 +120,7 @@
         self.assertRaises(
             exceptions.SSHExecCommandFailed,
             self.write_data_to_mounted_share_using_dd,
-            remote_client, '/mnt/t1', '64M', blocks, '/dev/urandom')
+            remote_client, '/mnt/t1', '64M', blocks)
 
         LOG.debug('Step 12 - unmount')
         self.unmount_share(remote_client)
diff --git a/playbooks/manila-tempest-plugin-standalone/run.yaml b/playbooks/manila-tempest-plugin-standalone/run.yaml
index 8df9205..26ad69a 100644
--- a/playbooks/manila-tempest-plugin-standalone/run.yaml
+++ b/playbooks/manila-tempest-plugin-standalone/run.yaml
@@ -5,7 +5,6 @@
 - hosts: tempest
   roles:
     - setup-tempest-run-dir
-    - set-tempest-config
     - setup-tempest-data-dir
     - acl-devstack-files
     - run-tempest
diff --git a/requirements.txt b/requirements.txt
index 95ad247..108bbee 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,6 +4,6 @@
 
 pbr!=2.1.0,>=2.0.0 # Apache-2.0
 
-ddt>=1.0.1 # MIT
+ddt>=1.6.0 # MIT
 oslo.log>=3.36.0 # Apache-2.0
 tempest>=31.1.0 # Apache-2.0
diff --git a/roles/set-tempest-config/README.rst b/roles/set-tempest-config/README.rst
deleted file mode 100644
index 9402d3c..0000000
--- a/roles/set-tempest-config/README.rst
+++ /dev/null
@@ -1,19 +0,0 @@
-set-tempest-config
-==================
-
-This is a workaround for the `merge_config_file <https://opendev
-.org/openstack/devstack/src/commit/76d7d7c90c3979c72404fddd31ee884c8bfdb1ec
-/inc/meta-config#L82>`_ routine that doesn't working correctly on jobs based on
-the "devstack-minimal" profile.
-
-**Role Variables**
-
-.. zuul:rolevar:: devstack_base_dir
-   :default: /opt/stack
-
-   The devstack base directory.
-
-.. zuul:rolevar:: devstack_local_conf_path
-   :default: "{{ devstack_base_dir }}/devstack/local.conf"
-
-   Where to find the local.conf file
diff --git a/roles/set-tempest-config/defaults/main.yml b/roles/set-tempest-config/defaults/main.yml
deleted file mode 100644
index 5cc7ca6..0000000
--- a/roles/set-tempest-config/defaults/main.yml
+++ /dev/null
@@ -1,3 +0,0 @@
----
-devstack_base_dir: /opt/stack
-devstack_local_conf_path: "{{ devstack_base_dir }}/devstack/local.conf"
diff --git a/roles/set-tempest-config/tasks/main.yml b/roles/set-tempest-config/tasks/main.yml
deleted file mode 100644
index 3572ff5..0000000
--- a/roles/set-tempest-config/tasks/main.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-- name: Install required pip packages
-  pip:
-    name: devstack-tools
-    state: "latest"
-    virtualenv: /var/tmp/venv
-
-- name: Copy tempest config
-  shell: >-
-    . /var/tmp/venv/bin/activate && \
-    dsconf extract {{ devstack_local_conf_path }} \
-      test-config \
-      '$TEMPEST_CONFIG' \
-      {{ devstack_base_dir }}/tempest/etc/tempest.conf
-  become: yes
diff --git a/setup.py b/setup.py
index 566d844..cd35c3c 100644
--- a/setup.py
+++ b/setup.py
@@ -13,17 +13,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
 import setuptools
 
-# In python < 2.7.4, a lazy loading of package `pbr` will break
-# setuptools if some other modules registered functions in `atexit`.
-# solution from: http://bugs.python.org/issue15881#msg170215
-try:
-    import multiprocessing  # noqa
-except ImportError:
-    pass
-
 setuptools.setup(
     setup_requires=['pbr>=2.0.0'],
     pbr=True)
diff --git a/zuul.d/manila-tempest-jobs.yaml b/zuul.d/manila-tempest-jobs.yaml
index 690b8bc..702dcf8 100644
--- a/zuul.d/manila-tempest-jobs.yaml
+++ b/zuul.d/manila-tempest-jobs.yaml
@@ -157,16 +157,18 @@
               backend_names: LONDON,PARIS
               multi_backend: true
               image_password: manila
+              dd_input_file: /dev/urandom
 
 - job:
     name: manila-tempest-plugin-zfsonlinux
     description: Test ZFSOnLinux multibackend (DHSS=False) with postgresql db
     parent: manila-tempest-plugin-zfsonlinux-base
-    branches: &ubuntu_jammy_test_image_branches ^(?!stable/(yoga|xena|wallaby|victoria|ussuri)).*$
+    branches: &ubuntu_jammy_test_image_branches
+      regex: ^stable/(yoga|xena|wallaby|victoria|ussuri)$
+      negate: true
 
 - job:
     name: manila-tempest-plugin-lvm-base
-    nodeset: openstack-single-node-focal
     description: |
       Test LVM multibackend (DHSS=False) in a 4+6 (dual-stack) devstack
       environment with IPv6 control plane endpoints.
@@ -237,6 +239,14 @@
       environment with IPv6 control plane endpoints.
     branches: *ubuntu_jammy_test_image_branches
     parent: manila-tempest-plugin-lvm-base
+    vars:
+      # TODO(gouthamr): some tests are disabled due to bugs
+      # IPv6 Tests: https://bugs.launchpad.net/manila/+bug/1998489
+      # drop these overrides once we address that bug.
+      tempest_exclude_regex: '(^manila_tempest_tests.tests.scenario.*IPv6.*)'
+      devstack_localrc:
+        MANILA_SETUP_IPV6: false
+        NEUTRON_CREATE_INITIAL_NETWORKS: true
 
 - job:
     name: manila-tempest-plugin-container
@@ -307,6 +317,7 @@
         MANILA_USE_SERVICE_INSTANCE_PASSWORD: true
         MANILA_DEFAULT_SHARE_TYPE_EXTRA_SPECS: 'snapshot_support=True create_share_from_snapshot_support=True'
         TEMPEST_USE_TEST_ACCOUNTS: true
+        GLANCE_ENFORCE_SCOPE: false
       devstack_services:
         cinder: true
       devstack_local_conf:
@@ -393,7 +404,6 @@
 
 - job:
     name: manila-tempest-plugin-cephfs-native
-    nodeset: openstack-single-node-focal
     description: Test CephFS Native (DHSS=False)
     parent: manila-tempest-plugin-cephfs-native-base
     branches: *ubuntu_jammy_test_image_branches
@@ -408,7 +418,6 @@
       tempest_exclude_regex: '(^manila_tempest_tests.tests.scenario.*ceph_fuse.*)'
       devstack_localrc:
         ENABLE_CEPH_NOVA: false
-        CONTAINER_IMAGE: 'quay.io/ceph/ceph:v17.2.5'
         CEPHADM_DEPLOY: true
         CEPHADM_DEV_OSD: true
         TARGET_DEV_OSD_DIR: /opt/stack
@@ -481,10 +490,7 @@
     vars:
       # TODO(gouthamr): some tests are disabled due to bugs
       # IPv6 Tests: https://bugs.launchpad.net/manila/+bug/1998489
-      # snapshot clone fs sync: https://bugs.launchpad.net/manila/+bug/1989273
-      tempest_exclude_regex: "\
-          (^manila_tempest_tests.tests.scenario.*IPv6.*)|\
-          (^manila_tempest_tests.tests.scenario.test_share_basic_ops.TestShareBasicOpsNFS.test_write_data_to_share_created_from_snapshot)"
+      tempest_exclude_regex: "(^manila_tempest_tests.tests.scenario.*IPv6.*)"
       devstack_localrc:
         MANILA_OPTGROUP_cephfsnfs_cephfs_ganesha_server_ip: "{{ hostvars[inventory_hostname]['nodepool']['private_ipv4'] }}"
         CEPH_RELEASE: "quincy"
@@ -493,6 +499,102 @@
         IP_VERSION: 4
 
 - job:
+    name: manila-tempest-plugin-multinode-base
+    abstract: true
+    description: |
+      Base job for testing multinode with Manila. Manila is enabled in
+      the controller node; and we have an additional compute node.
+    parent: tempest-multinode-full-py3
+    timeout: 10800
+    irrelevant-files: *irrelevant-files
+    required-projects: *manila-tempest-required-projects
+    vars:
+      tox_envlist: all
+      tempest_test_regex: manila_tempest_tests
+      tempest_plugins:
+        - manila-tempest-plugin
+      tempest_concurrency: 8
+      devstack_services:
+        cinder: false
+        c-bak: false
+        s-account: false
+        s-container: false
+        s-object: false
+        s-proxy: false
+        horizon: false
+        tls-proxy: true
+      devstack_localrc:
+        MANILA_USE_DOWNGRADE_MIGRATIONS: false
+        MANILA_INSTALL_TEMPEST_PLUGIN_SYSTEMWIDE: false
+        MANILA_ALLOW_NAS_SERVER_PORTS_ON_HOST: true
+        MANILA_SHARE_MIGRATION_PERIOD_TASK_INTERVAL: 1
+        MANILA_SERVER_MIGRATION_PERIOD_TASK_INTERVAL: 10
+        MANILA_REPLICA_STATE_UPDATE_INTERVAL: 10
+    group-vars:
+      tempest:
+        devstack_plugins:
+          manila: https://opendev.org/openstack/manila
+      subnode:
+        devstack_services:
+          cinder: false
+          c-bak: false
+
+- job:
+    name: manila-tempest-plugin-multinode-cephfs-nfs-cephadm
+    description: Test CephFS NFS (DHSS=False) in a Multinode devstack env
+    parent: manila-tempest-plugin-multinode-base
+    required-projects:
+      - openstack/devstack-plugin-ceph
+    vars:
+      configure_swap_size: 8192
+      tempest_concurrency: 2
+        # TODO(gouthamr): some tests are disabled due to bugs
+        # IPv6 Tests: https://bugs.launchpad.net/manila/+bug/1998489
+      tempest_exclude_regex: "(^manila_tempest_tests.tests.scenario.*IPv6.*)"
+      devstack_localrc:
+        MYSQL_REDUCE_MEMORY: True
+        CEPHADM_DEPLOY: True
+        CEPHADM_DEV_OSD: true
+        CEPH_LOOPBACK_DISK_SIZE: 40GB
+        ENABLED_SHARE_PROTOCOLS: NFS
+        ENABLE_CEPH_MANILA: True
+        ENABLE_CEPH_NOVA: False
+        MANILA_CEPH_DRIVER: cephfsnfs
+        MANILA_CONFIGURE_DEFAULT_TYPES: true
+        MANILA_DEFAULT_SHARE_TYPE_EXTRA_SPECS: 'snapshot_support=True create_share_from_snapshot_support=True'
+        MANILA_ENABLED_BACKENDS: cephfsnfs
+        MANILA_OPTGROUP_cephfsnfs_cephfs_auth_id: manila
+        MANILA_OPTGROUP_cephfsnfs_cephfs_conf_path: /etc/ceph/ceph.conf
+        MANILA_OPTGROUP_cephfsnfs_cephfs_nfs_cluster_id: cephfs
+        MANILA_OPTGROUP_cephfsnfs_cephfs_protocol_helper_type: NFS
+        MANILA_OPTGROUP_cephfsnfs_driver_handles_share_servers: false
+        MANILA_OPTGROUP_cephfsnfs_share_driver: manila.share.drivers.cephfs.driver.CephFSDriver
+        MANILA_SERVICE_IMAGE_ENABLED: True
+        MANILA_SETUP_IPV6: false
+        SHARE_DRIVER: manila.share.drivers.cephfs.driver.CephFSDriver
+        TARGET_DEV_OSD_DIR: /opt/stack
+      devstack_local_conf:
+        test-config:
+          $TEMPEST_CONFIG:
+            share:
+              backend_names: cephfsnfs
+              capability_storage_protocol: NFS
+              default_share_type_name: default
+              enable_protocols: nfs
+              image_password: manila
+              multitenancy_enabled: false
+              run_share_group_tests: false
+    group-vars:
+      subnode:
+        devstack_plugins:
+          devstack-plugin-ceph: https://opendev.org/openstack/devstack-plugin-ceph
+        devstack_localrc:
+          REMOTE_CEPH: True
+      tempest:
+        devstack_plugins:
+          devstack-plugin-ceph: https://opendev.org/openstack/devstack-plugin-ceph
+
+- job:
     name: manila-tempest-plugin-dummy-no-dhss
     description: Test the Dummy driver with DHSS=False
     parent: manila-tempest-plugin-standalone-base
@@ -524,6 +626,8 @@
         MANILA_OPTGROUP_membernet_standalone_network_plugin_mask: 24
         MANILA_OPTGROUP_membernet_standalone_network_plugin_network_type: vlan
         MANILA_OPTGROUP_membernet_standalone_network_plugin_segmentation_id: 1010
+        MANILA_CREATE_BACKUP_CONTINUE_TASK_INTERVAL: 30
+        MANILA_RESTORE_BACKUP_CONTINUE_TASK_INTERVAL: 30
       devstack_local_conf:
         test-config:
           "$TEMPEST_CONFIG":
@@ -537,6 +641,7 @@
               enable_user_rules_for_protocols: cifs
               multi_backend: true
               multitenancy_enabled: false
+              run_driver_assisted_backup_tests: true
               run_driver_assisted_migration_tests: true
               run_manage_unmanage_snapshot_tests: true
               run_manage_unmanage_tests: true
@@ -619,7 +724,6 @@
     description: |
       Test the GlusterFS driver (DHSS=False) with the native GlusterFS protocol
     parent: manila-tempest-plugin-standalone-base
-    nodeset: openstack-single-node-focal
     required-projects:
       - x/devstack-plugin-glusterfs
     vars:
@@ -652,7 +756,6 @@
     description: |
       Test the GlusterFS driver (DHSS=False) with the native NFS protocol
     parent: manila-tempest-plugin-standalone-base
-    nodeset: openstack-single-node-focal
     required-projects:
       - x/devstack-plugin-glusterfs
     vars:
@@ -723,7 +826,9 @@
 - job:
     name: manila-tempest-plugin-lvm-fips
     parent: manila-tempest-plugin-lvm-fips-base
-    branches: ^(?!stable/(yoga|xena|wallaby|victoria|ussuri)).*$
+    branches:
+      regex: ^stable/(yoga|xena|wallaby|victoria|ussuri)$
+      negate: true
 
 - project-template:
     name: manila-tempest-plugin-jobs-using-service-image
@@ -739,7 +844,7 @@
             voting: false
         - manila-tempest-plugin-cephfs-native-cephadm:
             voting: false
-        - manila-tempest-plugin-cephfs-nfs:
+        - manila-tempest-plugin-multinode-cephfs-nfs-cephadm:
             voting: false
         - manila-tempest-plugin-zfsonlinux:
             voting: false
diff --git a/zuul.d/manila-tempest-stable-jobs.yaml b/zuul.d/manila-tempest-stable-jobs.yaml
index 28db229..a6b5e2b 100644
--- a/zuul.d/manila-tempest-stable-jobs.yaml
+++ b/zuul.d/manila-tempest-stable-jobs.yaml
@@ -1,5 +1,5 @@
-# Stable xena / yoga / zed branch jobs to test the trunk version of
-# manila-tempest-plugin against those released stable branches of manila
+# Stable branch jobs to test the trunk version of manila-tempest-plugin
+# against those released stable branches of manila
 
 - job:
     name: manila-tempest-plugin-generic-scenario-stable
@@ -7,7 +7,7 @@
       Test the scenario test cases on the generic driver multibackend
       (DHSS=True) with NFS and CIFS
     parent: manila-tempest-plugin-generic-scenario-base
-    branches: &manila_tempest_image_pinned_branches ^(stable/(zed|yoga|xena)).*$
+    branches: &manila_tempest_image_pinned_branches ^stable/(2023.1|zed|yoga|xena)$
     vars: &manila_tempest_image_pinned_vars
       devstack_localrc:
         # NOTE(carloss): Pinning manila service image to a Focal version,
@@ -27,15 +27,12 @@
 
 - job:
     name: manila-tempest-plugin-lvm-stable
-    # NOTE(carloss): we are aware that focal is the current default, but
-    # in order to avoid breakages when devstack-minimal switches to a newer
-    # branch, we are pinning focal here.
-    nodeset: openstack-single-node-focal
     description: |
       Test LVM multibackend (DHSS=False) in a 4+6 (dual-stack) devstack
       environment with IPv6 control plane endpoints.
     branches: *manila_tempest_image_pinned_branches
     parent: manila-tempest-plugin-lvm-base
+    nodeset: openstack-single-node-focal
     vars: *manila_tempest_image_pinned_vars
 
 - job:
@@ -68,21 +65,44 @@
 - job:
     name: manila-tempest-plugin-lvm-fips-stable
     parent: manila-tempest-plugin-lvm-fips-base
-    branches: ^(stable/(yoga|xena)).*$
+    branches: ^stable/(yoga|xena)$
     vars: *manila_tempest_image_pinned_vars
 
 - job:
-    name: manila-tempest-plugin-lvm-yoga
+    name: manila-tempest-plugin-lvm-bobcat
     parent: manila-tempest-plugin-lvm-base
-    nodeset: openstack-single-node-focal
-    override-checkout: stable/yoga
-    vars: *manila_tempest_image_pinned_vars
+    override-checkout: stable/2023.2
+    vars:
+      # TODO(gouthamr): some tests are disabled due to bugs
+      # IPv6 Tests: https://bugs.launchpad.net/manila/+bug/1998489
+      # drop these overrides once we address that bug.
+      tempest_exclude_regex: '(^manila_tempest_tests.tests.scenario.*IPv6.*)'
+      devstack_localrc:
+        MANILA_SETUP_IPV6: false
+        NEUTRON_CREATE_INITIAL_NETWORKS: true
 
 - job:
-    name: manila-tempest-plugin-lvm-xena
+    name: manila-tempest-plugin-lvm-antelope
     parent: manila-tempest-plugin-lvm-base
-    nodeset: openstack-single-node-focal
-    override-checkout: stable/xena
+    override-checkout: stable/2023.1
+    vars:
+      # TODO(gouthamr): some tests are disabled due to bugs
+      # IPv6 Tests: https://bugs.launchpad.net/manila/+bug/1998489
+      # drop these overrides once we address that bug.
+      tempest_exclude_regex: '(^manila_tempest_tests.tests.scenario.*IPv6.*)'
+      devstack_localrc:
+        MANILA_SETUP_IPV6: false
+        NEUTRON_CREATE_INITIAL_NETWORKS: true
+        # NOTE(carloss): Pinning manila service image to a Focal version,
+        # since on Zed we moved to Ubuntu Jammy (22), and it requires more
+        # VM resources.
+        MANILA_SERVICE_IMAGE_URL: https://tarballs.opendev.org/openstack/manila-image-elements/images/manila-service-image-1.3.0-76-ga216835.qcow2
+        MANILA_SERVICE_IMAGE_NAME: manila-service-image-1.3.0-76-ga216835
+
+- job:
+    name: manila-tempest-plugin-lvm-zed
+    parent: manila-tempest-plugin-lvm-base
+    override-checkout: stable/zed
     vars: *manila_tempest_image_pinned_vars
 
 - project-template:
diff --git a/zuul.d/project.yaml b/zuul.d/project.yaml
index 48d3d4c..06204d6 100644
--- a/zuul.d/project.yaml
+++ b/zuul.d/project.yaml
@@ -8,8 +8,9 @@
       jobs:
         - manila-tempest-plugin-dummy-no-dhss
         - manila-tempest-plugin-dummy-dhss
-        - manila-tempest-plugin-lvm-yoga
-        - manila-tempest-plugin-lvm-xena
+        - manila-tempest-plugin-lvm-bobcat
+        - manila-tempest-plugin-lvm-antelope
+        - manila-tempest-plugin-lvm-zed
         - manila-tempest-plugin-dummy-no-dhss-rbac
         - manila-tempest-plugin-container:
             voting: false