Remove experimental flag from share groups feature

The share groups functionality will no longer be considered
experimental. The existent functional tests were modified to accomplish
with this feature graduation.

Change-Id: Ideba68c0481345e808f185195eea68e879155cf1
Partially-Implements: bp graduate-share-groups-feature
diff --git a/manila_tempest_tests/common/constants.py b/manila_tempest_tests/common/constants.py
index 8791837..cadab0a 100644
--- a/manila_tempest_tests/common/constants.py
+++ b/manila_tempest_tests/common/constants.py
@@ -70,6 +70,7 @@
 
 # Share groups
 MIN_SHARE_GROUP_MICROVERSION = '2.31'
+SHARE_GROUPS_GRADUATION_VERSION = '2.55'
 SHARE_GROUP_SIMPLE_KEYS = {
     'id', 'name', 'links',
 }
diff --git a/manila_tempest_tests/config.py b/manila_tempest_tests/config.py
index 042be5a..6973092 100644
--- a/manila_tempest_tests/config.py
+++ b/manila_tempest_tests/config.py
@@ -30,7 +30,7 @@
                help="The minimum api microversion is configured to be the "
                     "value of the minimum microversion supported by Manila."),
     cfg.StrOpt("max_api_microversion",
-               default="2.53",
+               default="2.55",
                help="The maximum api microversion is configured to be the "
                     "value of the latest microversion supported by Manila."),
     cfg.StrOpt("region",
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 d8b7ed4..09308b8 100644
--- a/manila_tempest_tests/services/share/v2/json/shares_client.py
+++ b/manila_tempest_tests/services/share/v2/json/shares_client.py
@@ -1078,6 +1078,8 @@
         """Create a new share group."""
         uri = 'share-groups'
         post_body = {}
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
         if name:
             post_body['name'] = name
         if description:
@@ -1095,8 +1097,8 @@
             post_body['availability_zone'] = availability_zone
         body = json.dumps({'share_group': post_body})
 
-        resp, body = self.post(uri, body, headers=EXPERIMENTAL,
-                               extra_headers=True, version=version)
+        resp, body = self.post(uri, body, headers=headers,
+                               extra_headers=extra_headers, version=version)
 
         self.expected_success(202, resp.status)
         return self._parse_resp(body)
@@ -1104,8 +1106,10 @@
     def delete_share_group(self, share_group_id, version=LATEST_MICROVERSION):
         """Delete a share group."""
         uri = 'share-groups/%s' % share_group_id
-        resp, body = self.delete(uri, headers=EXPERIMENTAL,
-                                 extra_headers=True, version=version)
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
+        resp, body = self.delete(uri, headers=headers,
+                                 extra_headers=extra_headers, version=version)
         self.expected_success(202, resp.status)
         return self._parse_resp(body)
 
@@ -1114,16 +1118,20 @@
         """Get list of share groups w/o filters."""
         uri = 'share-groups%s' % ('/detail' if detailed else '')
         uri += '?%s' % (parse.urlencode(params) if params else '')
-        resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
-                              version=version)
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
+        resp, body = self.get(uri, headers=headers,
+                              extra_headers=extra_headers, version=version)
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
     def get_share_group(self, share_group_id, version=LATEST_MICROVERSION):
         """Get share group info."""
         uri = 'share-groups/%s' % share_group_id
-        resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
-                              version=version)
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
+        resp, body = self.get(uri, headers=headers,
+                              extra_headers=extra_headers, version=version)
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
@@ -1131,6 +1139,8 @@
                            version=LATEST_MICROVERSION, **kwargs):
         """Update an existing share group."""
         uri = 'share-groups/%s' % share_group_id
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
         post_body = {}
         if name:
             post_body['name'] = name
@@ -1140,21 +1150,25 @@
             post_body.update(kwargs)
         body = json.dumps({'share_group': post_body})
 
-        resp, body = self.put(uri, body, headers=EXPERIMENTAL,
-                              extra_headers=True, version=version)
+        resp, body = self.put(uri, body, headers=headers,
+                              extra_headers=extra_headers, version=version)
 
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
     def share_group_reset_state(self, share_group_id, status='error',
                                 version=LATEST_MICROVERSION):
+        headers, _junk = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
         self.reset_state(share_group_id, status=status, s_type='groups',
-                         headers=EXPERIMENTAL, version=version)
+                         headers=headers, version=version)
 
     def share_group_force_delete(self, share_group_id,
                                  version=LATEST_MICROVERSION):
+        headers, _junk = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
         self.force_delete(share_group_id, s_type='share-groups',
-                          headers=EXPERIMENTAL, version=version)
+                          headers=headers, version=version)
 
     def wait_for_share_group_status(self, share_group_id, status):
         """Waits for a share group to reach a given status."""
@@ -1186,6 +1200,8 @@
                                 version=LATEST_MICROVERSION):
         """Create a new share group type."""
         uri = 'share-group-types'
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
         post_body = {}
         if isinstance(share_types, (tuple, list)):
             share_types = list(share_types)
@@ -1200,8 +1216,8 @@
         if group_specs:
             post_body['group_specs'] = group_specs
         body = json.dumps({'share_group_type': post_body})
-        resp, body = self.post(uri, body, headers=EXPERIMENTAL,
-                               extra_headers=True, version=version)
+        resp, body = self.post(uri, body, headers=headers,
+                               extra_headers=extra_headers, version=version)
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
@@ -1210,8 +1226,10 @@
         """Get list of share group types."""
         uri = 'share-group-types%s' % ('/detail' if detailed else '')
         uri += '?%s' % (parse.urlencode(params) if params else '')
-        resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
-                              version=version)
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
+        resp, body = self.get(uri, headers=headers,
+                              extra_headers=extra_headers, version=version)
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
@@ -1219,16 +1237,20 @@
                              version=LATEST_MICROVERSION):
         """Get share group type info."""
         uri = 'share-group-types/%s' % share_group_type_id
-        resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
-                              version=version)
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
+        resp, body = self.get(uri, headers=headers,
+                              extra_headers=extra_headers, version=version)
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
     def get_default_share_group_type(self, version=LATEST_MICROVERSION):
         """Get default share group type info."""
         uri = 'share-group-types/default'
-        resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
-                              version=version)
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
+        resp, body = self.get(uri, headers=headers,
+                              extra_headers=extra_headers, version=version)
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
@@ -1236,18 +1258,22 @@
                                 version=LATEST_MICROVERSION):
         """Delete an existing share group type."""
         uri = 'share-group-types/%s' % share_group_type_id
-        resp, body = self.delete(uri, headers=EXPERIMENTAL,
-                                 extra_headers=True, version=version)
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
+        resp, body = self.delete(uri, headers=headers,
+                                 extra_headers=extra_headers, version=version)
         self.expected_success(204, resp.status)
         return self._parse_resp(body)
 
     def add_access_to_share_group_type(self, share_group_type_id, project_id,
                                        version=LATEST_MICROVERSION):
         uri = 'share-group-types/%s/action' % share_group_type_id
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
         post_body = {'project': project_id}
         post_body = json.dumps({'addProjectAccess': post_body})
-        resp, body = self.post(uri, post_body, headers=EXPERIMENTAL,
-                               extra_headers=True, version=version)
+        resp, body = self.post(uri, post_body, headers=headers,
+                               extra_headers=extra_headers, version=version)
         self.expected_success(202, resp.status)
         return self._parse_resp(body)
 
@@ -1255,18 +1281,22 @@
                                             project_id,
                                             version=LATEST_MICROVERSION):
         uri = 'share-group-types/%s/action' % share_group_type_id
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
         post_body = {'project': project_id}
         post_body = json.dumps({'removeProjectAccess': post_body})
-        resp, body = self.post(uri, post_body, headers=EXPERIMENTAL,
-                               extra_headers=True, version=version)
+        resp, body = self.post(uri, post_body, headers=headers,
+                               extra_headers=extra_headers, version=version)
         self.expected_success(202, resp.status)
         return self._parse_resp(body)
 
     def list_access_to_share_group_type(self, share_group_type_id,
                                         version=LATEST_MICROVERSION):
         uri = 'share-group-types/%s/access' % share_group_type_id
-        resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
-                              version=version)
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
+        resp, body = self.get(uri, headers=headers,
+                              extra_headers=extra_headers, version=version)
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
@@ -1276,9 +1306,11 @@
                                       group_specs_dict,
                                       version=LATEST_MICROVERSION):
         url = "share-group-types/%s/group-specs" % share_group_type_id
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
         post_body = json.dumps({'group_specs': group_specs_dict})
-        resp, body = self.post(url, post_body, headers=EXPERIMENTAL,
-                               extra_headers=True, version=version)
+        resp, body = self.post(url, post_body, headers=headers,
+                               extra_headers=extra_headers, version=version)
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
@@ -1286,18 +1318,22 @@
                                   version=LATEST_MICROVERSION):
         uri = "group-types/%s/group_specs/%s" % (
             share_group_type_id, group_spec_key)
-        resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
-                              version=version)
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
+        resp, body = self.get(uri, headers=headers,
+                              extra_headers=extra_headers, version=version)
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
     def list_share_group_type_specs(self, share_group_type_id, params=None,
                                     version=LATEST_MICROVERSION):
         uri = "share-group-types/%s/group_specs" % share_group_type_id
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
         if params is not None:
             uri += '?%s' % parse.urlencode(params)
-        resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
-                              version=version)
+        resp, body = self.get(uri, headers=headers,
+                              extra_headers=extra_headers, version=version)
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
@@ -1306,10 +1342,12 @@
                                      version=LATEST_MICROVERSION):
         uri = "share-group-types/%s/group-specs/%s" % (
             share_group_type_id, group_spec_key)
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
         group_spec = {group_spec_key: group_spec_value}
         post_body = json.dumps(group_spec)
-        resp, body = self.put(uri, post_body, headers=EXPERIMENTAL,
-                              extra_headers=True, version=version)
+        resp, body = self.put(uri, post_body, headers=headers,
+                              extra_headers=extra_headers, version=version)
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
@@ -1323,8 +1361,10 @@
                                      version=LATEST_MICROVERSION):
         uri = "share-group-types/%s/group-specs/%s" % (
             share_type_id, group_spec_key)
-        resp, body = self.delete(uri, headers=EXPERIMENTAL, extra_headers=True,
-                                 version=version)
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
+        resp, body = self.delete(uri, headers=headers,
+                                 extra_headers=extra_headers, version=version)
         self.expected_success(204, resp.status)
         return body
 
@@ -1335,14 +1375,16 @@
                                     version=LATEST_MICROVERSION):
         """Create a new share group snapshot of an existing share group."""
         uri = 'share-group-snapshots'
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
         post_body = {'share_group_id': share_group_id}
         if name:
             post_body['name'] = name
         if description:
             post_body['description'] = description
         body = json.dumps({'share_group_snapshot': post_body})
-        resp, body = self.post(uri, body, headers=EXPERIMENTAL,
-                               extra_headers=True, version=version)
+        resp, body = self.post(uri, body, headers=headers,
+                               extra_headers=extra_headers, version=version)
         self.expected_success(202, resp.status)
         return self._parse_resp(body)
 
@@ -1350,8 +1392,10 @@
                                     version=LATEST_MICROVERSION):
         """Delete an existing share group snapshot."""
         uri = 'share-group-snapshots/%s' % share_group_snapshot_id
-        resp, body = self.delete(uri, headers=EXPERIMENTAL,
-                                 extra_headers=True, version=version)
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
+        resp, body = self.delete(uri, headers=headers,
+                                 extra_headers=extra_headers, version=version)
         self.expected_success(202, resp.status)
         return body
 
@@ -1360,8 +1404,10 @@
         """Get list of share group snapshots w/o filters."""
         uri = 'share-group-snapshots%s' % ('/detail' if detailed else '')
         uri += '?%s' % (parse.urlencode(params) if params else '')
-        resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
-                              version=version)
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
+        resp, body = self.get(uri, headers=headers,
+                              extra_headers=extra_headers, version=version)
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
@@ -1369,8 +1415,10 @@
                                  version=LATEST_MICROVERSION):
         """Get share group snapshot info."""
         uri = 'share-group-snapshots/%s' % share_group_snapshot_id
-        resp, body = self.get(uri, headers=EXPERIMENTAL, extra_headers=True,
-                              version=version)
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
+        resp, body = self.get(uri, headers=headers,
+                              extra_headers=extra_headers, version=version)
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
@@ -1379,29 +1427,35 @@
                                     version=LATEST_MICROVERSION):
         """Update an existing share group snapshot."""
         uri = 'share-group-snapshots/%s' % share_group_snapshot_id
+        headers, extra_headers = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
         post_body = {}
         if name:
             post_body['name'] = name
         if description:
             post_body['description'] = description
         body = json.dumps({'share_group_snapshot': post_body})
-        resp, body = self.put(uri, body, headers=EXPERIMENTAL,
-                              extra_headers=True, version=version)
+        resp, body = self.put(uri, body, headers=headers,
+                              extra_headers=extra_headers, version=version)
         self.expected_success(200, resp.status)
         return self._parse_resp(body)
 
     def share_group_snapshot_reset_state(self, share_group_snapshot_id,
                                          status='error',
                                          version=LATEST_MICROVERSION):
+        headers, _junk = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
         self.reset_state(
             share_group_snapshot_id, status=status,
-            s_type='group-snapshots', headers=EXPERIMENTAL, version=version)
+            headers=headers, s_type='group-snapshots', version=version)
 
     def share_group_snapshot_force_delete(self, share_group_snapshot_id,
                                           version=LATEST_MICROVERSION):
+        headers, _junk = utils.get_extra_headers(
+            version, constants.SHARE_GROUPS_GRADUATION_VERSION)
         self.force_delete(
             share_group_snapshot_id, s_type='share-group-snapshots',
-            headers=EXPERIMENTAL, version=version)
+            headers=headers, version=version)
 
     def wait_for_share_group_snapshot_status(self, share_group_snapshot_id,
                                              status):
diff --git a/manila_tempest_tests/tests/api/admin/test_share_group_types.py b/manila_tempest_tests/tests/api/admin/test_share_group_types.py
index 6474f59..d05c079 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_group_types.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_group_types.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 import ddt
+import itertools
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from testtools import testcase as tc
@@ -54,8 +55,13 @@
         cls.share_type2 = share_type['share_type']
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_API)
-    @ddt.data('id', 'name')
-    def test_create_get_delete_share_group_type_min(self, st_key):
+    @ddt.data(
+        *itertools.product(('id', 'name'), set(
+            [LATEST_MICROVERSION, constants.MIN_SHARE_GROUP_MICROVERSION,
+             constants.SHARE_GROUPS_GRADUATION_VERSION])))
+    @ddt.unpack
+    def test_create_get_delete_share_group_type(self, st_key, version):
+        self.skip_if_microversion_not_supported(version)
         name = data_utils.rand_name("tempest-manila")
 
         # Create share group type
@@ -63,7 +69,7 @@
             name=name,
             share_types=self.share_type[st_key],
             cleanup_in_class=False,
-            version=constants.MIN_SHARE_GROUP_MICROVERSION)
+            version=version)
 
         self.assertEqual(
             [self.share_type['id']],
@@ -71,7 +77,8 @@
             'Share type not applied correctly.')
 
         # Read share group type
-        sg_type_r = self.shares_v2_client.get_share_group_type(sg_type_c['id'])
+        sg_type_r = self.shares_v2_client.get_share_group_type(
+            sg_type_c['id'], version=version)
         keys = set(sg_type_r.keys())
         self.assertTrue(
             constants.SHARE_GROUP_TYPE_REQUIRED_KEYS.issubset(keys),
@@ -82,7 +89,7 @@
 
         # Delete share group type
         self.shares_v2_client.delete_share_group_type(
-            sg_type_r['id'], version=constants.MIN_SHARE_GROUP_MICROVERSION)
+            sg_type_r['id'], version=version)
         self.shares_v2_client.wait_for_resource_deletion(
             share_group_type_id=sg_type_r['id'])
 
@@ -131,7 +138,11 @@
         self.assertDictMatch(group_specs, sg_type['group_specs'])
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_API)
-    def test_update_single_share_group_type_spec_min(self):
+    @ddt.data(
+        *set([constants.MIN_SHARE_GROUP_MICROVERSION,
+              constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION]))
+    def test_update_single_share_group_type_spec(self, version):
+        self.skip_if_microversion_not_supported(version)
         name = data_utils.rand_name("tempest-manila")
         group_specs = {'key1': 'value1', 'key2': 'value2'}
 
@@ -140,14 +151,14 @@
             share_types=self.share_type['id'],
             group_specs=group_specs,
             cleanup_in_class=False,
-            version=constants.MIN_SHARE_GROUP_MICROVERSION)
+            version=version)
 
         self.assertDictMatch(group_specs, sg_type['group_specs'])
 
         group_specs = {'key1': 'value1', 'key2': 'value2'}
 
         self.shares_v2_client.update_share_group_type_spec(
-            sg_type['id'], 'key1', 'value3')
+            sg_type['id'], 'key1', 'value3', version=version)
         sg_type = self.shares_v2_client.get_share_group_type(sg_type['id'])
 
         self.assertIn('key1', sg_type['group_specs'])
@@ -180,7 +191,11 @@
             self.assertEqual(v, sg_type['group_specs'][k])
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_API)
-    def test_delete_single_share_group_type_spec_min(self):
+    @ddt.data(
+        *set([constants.MIN_SHARE_GROUP_MICROVERSION,
+              constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION]))
+    def test_delete_single_share_group_type_spec_min(self, version):
+        self.skip_if_microversion_not_supported(version)
         name = data_utils.rand_name("tempest-manila")
         group_specs = {'key1': 'value1', 'key2': 'value2'}
 
@@ -189,7 +204,7 @@
             share_types=self.share_type['id'],
             group_specs=group_specs,
             cleanup_in_class=False,
-            version=constants.MIN_SHARE_GROUP_MICROVERSION)
+            version=version)
 
         self.assertDictMatch(group_specs, sg_type['group_specs'])
 
@@ -197,14 +212,18 @@
         group_specs.pop(key_to_delete)
 
         self.shares_v2_client.delete_share_group_type_spec(
-            sg_type['id'], key_to_delete)
+            sg_type['id'], key_to_delete, version=version)
         sg_type = self.shares_v2_client.get_share_group_type(
-            sg_type['id'])
+            sg_type['id'], version=version)
 
         self.assertDictMatch(group_specs, sg_type['group_specs'])
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_API)
-    def test_private_share_group_type_access(self):
+    @ddt.data(
+        *set([constants.MIN_SHARE_GROUP_MICROVERSION,
+              constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION]))
+    def test_private_share_group_type_access(self, version):
+        self.skip_if_microversion_not_supported(version)
         name = data_utils.rand_name("tempest-manila")
         group_specs = {"key1": "value1", "key2": "value2"}
         project_id = self.shares_v2_client.tenant_id
@@ -215,41 +234,48 @@
             share_types=[self.share_type['id']],
             is_public=False,
             group_specs=group_specs,
+            version=version
         )
         self.assertEqual(name, sgt_create['name'])
         sgt_id = sgt_create["id"]
 
         # It should not be listed without access
-        sgt_list = self.shares_v2_client.list_share_group_types()
+        sgt_list = self.shares_v2_client.list_share_group_types(
+            version=version)
         self.assertFalse(any(sgt_id == sgt["id"] for sgt in sgt_list))
 
         # List projects that have access for share group type - none expected
-        access = self.shares_v2_client.list_access_to_share_group_type(sgt_id)
+        access = self.shares_v2_client.list_access_to_share_group_type(
+            sgt_id, version=version)
         self.assertEmpty(access)
 
         # Add project access to share group type
         access = self.shares_v2_client.add_access_to_share_group_type(
-            sgt_id, project_id)
+            sgt_id, project_id, version=version)
 
         # Now it should be listed
-        sgt_list = self.shares_v2_client.list_share_group_types()
+        sgt_list = self.shares_v2_client.list_share_group_types(
+            version=version)
         self.assertTrue(any(sgt_id == sgt["id"] for sgt in sgt_list))
 
         # List projects that have access for share group type - one expected
-        access = self.shares_v2_client.list_access_to_share_group_type(sgt_id)
+        access = self.shares_v2_client.list_access_to_share_group_type(
+            sgt_id, version=version)
         expected = [{'share_group_type_id': sgt_id, 'project_id': project_id}]
         self.assertEqual(expected, access)
 
         # Remove project access from share group type
         access = self.shares_v2_client.remove_access_from_share_group_type(
-            sgt_id, project_id)
+            sgt_id, project_id, version=version)
 
         # It should not be listed without access
-        sgt_list = self.shares_v2_client.list_share_group_types()
+        sgt_list = self.shares_v2_client.list_share_group_types(
+            version=version)
         self.assertFalse(any(sgt_id == sgt["id"] for sgt in sgt_list))
 
         # List projects that have access for share group type - none expected
-        access = self.shares_v2_client.list_access_to_share_group_type(sgt_id)
+        access = self.shares_v2_client.list_access_to_share_group_type(
+            sgt_id, version=version)
         self.assertEmpty(access)
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_API)
diff --git a/manila_tempest_tests/tests/api/admin/test_share_groups.py b/manila_tempest_tests/tests/api/admin/test_share_groups.py
index 39925ba..c7b3a42 100644
--- a/manila_tempest_tests/tests/api/admin/test_share_groups.py
+++ b/manila_tempest_tests/tests/api/admin/test_share_groups.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import ddt
 from tempest import config
 from tempest.lib.common.utils import data_utils
 import testtools
@@ -23,8 +24,10 @@
 from manila_tempest_tests import utils
 
 CONF = config.CONF
+LATEST_MICROVERSION = CONF.share.max_api_microversion
 
 
+@ddt.ddt
 class ShareGroupsTest(base.BaseSharesAdminTest):
 
     @classmethod
@@ -56,12 +59,16 @@
         cls.sg_type_id = cls.sg_type['id']
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
-    def test_create_share_group_with_single_share_type_min(self):
+    @ddt.data(
+        *set([constants.MIN_SHARE_GROUP_MICROVERSION,
+              constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION]))
+    def test_create_share_group_with_single_share_type_min(self, version):
+        self.skip_if_microversion_not_supported(version)
         share_group = self.create_share_group(
             share_group_type_id=self.sg_type_id,
             cleanup_in_class=False,
             share_type_ids=[self.share_type_id],
-            version=constants.MIN_SHARE_GROUP_MICROVERSION)
+            version=version)
 
         keys = set(share_group.keys())
         self.assertTrue(
@@ -124,14 +131,21 @@
     @testtools.skipUnless(
         CONF.share.default_share_type_name, "Only if defaults are defined.")
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
-    def test_default_share_group_type_applied(self):
-        default_type = self.shares_v2_client.get_default_share_group_type()
+    @ddt.data(
+        *set([constants.MIN_SHARE_GROUP_MICROVERSION,
+              constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION]))
+    def test_default_share_group_type_applied(self, version):
+        self.skip_if_microversion_not_supported(version)
+
+        default_type = self.shares_v2_client.get_default_share_group_type(
+            version=version
+        )
         default_share_types = default_type['share_types']
 
         share_group = self.create_share_group(
             cleanup_in_class=False,
             share_type_ids=default_share_types,
-            version=constants.MIN_SHARE_GROUP_MICROVERSION)
+            version=version)
 
         keys = set(share_group.keys())
         self.assertTrue(
diff --git a/manila_tempest_tests/tests/api/base.py b/manila_tempest_tests/tests/api/base.py
index 22b1d72..8c18a4b 100644
--- a/manila_tempest_tests/tests/api/base.py
+++ b/manila_tempest_tests/tests/api/base.py
@@ -609,8 +609,7 @@
         if kwargs.get('source_share_group_snapshot_id'):
             new_share_group_shares = client.list_shares(
                 detailed=True,
-                params={'share_group_id': share_group['id']},
-                experimental=True)
+                params={'share_group_id': share_group['id']})
 
             for share in new_share_group_shares:
                 resource = {"type": "share",
diff --git a/manila_tempest_tests/tests/api/test_share_group_actions.py b/manila_tempest_tests/tests/api/test_share_group_actions.py
index 169d767..c1f4e6f 100644
--- a/manila_tempest_tests/tests/api/test_share_group_actions.py
+++ b/manila_tempest_tests/tests/api/test_share_group_actions.py
@@ -24,6 +24,7 @@
 from manila_tempest_tests import utils
 
 CONF = config.CONF
+LATEST_MICROVERSION = CONF.share.max_api_microversion
 
 
 @ddt.ddt
@@ -81,7 +82,6 @@
                 'size': size,
                 'share_type_id': cls.share_type_id,
                 'share_group_id': sg_id,
-                'experimental': True,
             }} for size, sg_id in ((cls.share_size, cls.share_group['id']),
                                    (cls.share_size2, cls.share_group['id']),
                                    (cls.share_size, cls.share_group2['id']))
@@ -104,13 +104,15 @@
         )
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
-    def test_get_share_group_min_supported_sg_microversion(self):
+    @ddt.data(
+        *set([constants.MIN_SHARE_GROUP_MICROVERSION,
+              constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION]))
+    def test_get_share_group(self, version):
+        self.skip_if_microversion_not_supported(version)
 
         # Get share group
         share_group = self.shares_v2_client.get_share_group(
-            self.share_group['id'],
-            version=constants.MIN_SHARE_GROUP_MICROVERSION,
-        )
+            self.share_group['id'], version=version)
 
         # Verify keys
         actual_keys = set(share_group.keys())
@@ -132,8 +134,7 @@
         # Get share
         share = self.shares_v2_client.get_share(
             self.shares[0]['id'],
-            version=constants.MIN_SHARE_GROUP_MICROVERSION,
-            experimental=True)
+            version=constants.MIN_SHARE_GROUP_MICROVERSION)
 
         # Verify keys
         expected_keys = {
@@ -155,11 +156,15 @@
         self.assertEqual(self.share_group["id"], share["share_group_id"])
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
-    def test_list_share_groups_min(self):
+    @ddt.data(
+        *set([constants.MIN_SHARE_GROUP_MICROVERSION,
+              constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION]))
+    def test_list_share_groups(self, version):
+        self.skip_if_microversion_not_supported(version)
 
         # List share groups
         share_groups = self.shares_v2_client.list_share_groups(
-            version=constants.MIN_SHARE_GROUP_MICROVERSION)
+            version=version)
 
         # Verify keys
         self.assertGreater(len(share_groups), 0)
@@ -181,8 +186,11 @@
             self.assertEqual(1, len(gen), msg)
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
-    @ddt.data(constants.MIN_SHARE_GROUP_MICROVERSION, '2.36')
+    @ddt.data(
+        *set([constants.MIN_SHARE_GROUP_MICROVERSION, '2.36',
+              constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION]))
     def test_list_share_groups_with_detail_min(self, version):
+        self.skip_if_microversion_not_supported(version)
         params = None
         if utils.is_microversion_ge(version, '2.36'):
             params = {'name~': 'tempest', 'description~': 'tempest'}
@@ -218,7 +226,6 @@
             detailed=True,
             params={'share_group_id': self.share_group['id']},
             version=constants.MIN_SHARE_GROUP_MICROVERSION,
-            experimental=True,
         )
 
         share_ids = [share['id'] for share in shares]
@@ -237,11 +244,16 @@
                 self.shares[0]['id'], share_ids))
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
-    def test_get_share_group_snapshot_min(self):
+    @ddt.data(
+        *set([constants.MIN_SHARE_GROUP_MICROVERSION,
+              constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION]))
+    def test_get_share_group_snapshot(self, version):
+        self.skip_if_microversion_not_supported(version)
+
         # Get share group snapshot
         sg_snapshot = self.shares_v2_client.get_share_group_snapshot(
             self.sg_snapshot['id'],
-            version=constants.MIN_SHARE_GROUP_MICROVERSION,
+            version=version,
         )
 
         # Verify keys
@@ -286,24 +298,29 @@
                     self.assertEqual(share['size'], member['size'])
 
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
-    def test_create_share_group_from_populated_share_group_snapshot_min(self):
+    @ddt.data(
+        *set([constants.MIN_SHARE_GROUP_MICROVERSION,
+              constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION]))
+    def test_create_share_group_from_populated_share_group_snapshot(self,
+                                                                    version):
+        self.skip_if_microversion_not_supported(version)
 
         sg_snapshot = self.shares_v2_client.get_share_group_snapshot(
             self.sg_snapshot['id'],
-            version=constants.MIN_SHARE_GROUP_MICROVERSION,
+            version=version,
         )
         snapshot_members = sg_snapshot['members']
 
         new_share_group = self.create_share_group(
             cleanup_in_class=False,
             source_share_group_snapshot_id=self.sg_snapshot['id'],
-            version=constants.MIN_SHARE_GROUP_MICROVERSION,
+            version=version,
             share_group_type_id=self.share_group_type_id,
         )
 
         new_share_group = self.shares_v2_client.get_share_group(
             new_share_group['id'],
-            version=constants.MIN_SHARE_GROUP_MICROVERSION,
+            version=version,
         )
 
         # Verify that share_network information matches source share group
@@ -314,8 +331,7 @@
         new_shares = self.shares_v2_client.list_shares(
             params={'share_group_id': new_share_group['id']},
             detailed=True,
-            version=constants.MIN_SHARE_GROUP_MICROVERSION,
-            experimental=True,
+            version=version,
         )
 
         # Verify each new share is available
@@ -343,6 +359,7 @@
                         share['share_network_id'])
 
 
+@ddt.ddt
 class ShareGroupRenameTest(base.BaseSharesMixedTest):
 
     @classmethod
@@ -376,13 +393,25 @@
             share_type_ids=[cls.share_type_id]
         )
 
+    def _rollback_share_group_update(self, version):
+        self.shares_v2_client.update_share_group(
+            self.share_group["id"],
+            name=self.share_group_name,
+            description=self.share_group_desc,
+            version=version,
+        )
+
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
-    def test_update_share_group_min(self):
+    @ddt.data(
+        *set([constants.MIN_SHARE_GROUP_MICROVERSION,
+              constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION]))
+    def test_update_share_group(self, version):
+        self.skip_if_microversion_not_supported(version)
 
         # Get share_group
         share_group = self.shares_v2_client.get_share_group(
             self.share_group['id'],
-            version=constants.MIN_SHARE_GROUP_MICROVERSION
+            version=version
         )
         self.assertEqual(self.share_group_name, share_group["name"])
         self.assertEqual(self.share_group_desc, share_group["description"])
@@ -394,7 +423,7 @@
             share_group["id"],
             name=new_name,
             description=new_desc,
-            version=constants.MIN_SHARE_GROUP_MICROVERSION,
+            version=version,
         )
         self.assertEqual(new_name, updated["name"])
         self.assertEqual(new_desc, updated["description"])
@@ -402,13 +431,22 @@
         # Get share_group
         share_group = self.shares_v2_client.get_share_group(
             self.share_group['id'],
-            version=constants.MIN_SHARE_GROUP_MICROVERSION,
+            version=version,
         )
         self.assertEqual(new_name, share_group["name"])
         self.assertEqual(new_desc, share_group["description"])
 
+        # Rollback the update since this is a ddt and the class resources are
+        # going to be reused
+        self._rollback_share_group_update(version)
+
     @tc.attr(base.TAG_POSITIVE, base.TAG_API_WITH_BACKEND)
-    def test_create_update_read_share_group_with_unicode_min(self):
+    @ddt.data(
+        *set([constants.MIN_SHARE_GROUP_MICROVERSION,
+              constants.SHARE_GROUPS_GRADUATION_VERSION, LATEST_MICROVERSION]))
+    def test_create_update_read_share_group_with_unicode(self, version):
+        self.skip_if_microversion_not_supported(version)
+
         value1 = u'ಠ_ಠ'
         value2 = u'ಠ_ರೃ'
 
@@ -417,7 +455,7 @@
             cleanup_in_class=False,
             name=value1,
             description=value1,
-            version=constants.MIN_SHARE_GROUP_MICROVERSION,
+            version=version,
             share_group_type_id=self.share_group_type_id,
             share_type_ids=[self.share_type_id]
         )
@@ -429,13 +467,17 @@
             share_group["id"],
             name=value2,
             description=value2,
-            version=constants.MIN_SHARE_GROUP_MICROVERSION,
+            version=version,
         )
         self.assertEqual(value2, updated["name"])
         self.assertEqual(value2, updated["description"])
 
         # Get share group
         share_group = self.shares_v2_client.get_share_group(
-            share_group['id'], version=constants.MIN_SHARE_GROUP_MICROVERSION)
+            share_group['id'], version=version)
         self.assertEqual(value2, share_group["name"])
         self.assertEqual(value2, share_group["description"])
+
+        # Rollback the update since this is a ddt and the class resources are
+        # going to be reused
+        self._rollback_share_group_update(version)
diff --git a/manila_tempest_tests/tests/api/test_share_groups.py b/manila_tempest_tests/tests/api/test_share_groups.py
index 330c40e..5b2ae9f 100644
--- a/manila_tempest_tests/tests/api/test_share_groups.py
+++ b/manila_tempest_tests/tests/api/test_share_groups.py
@@ -72,8 +72,7 @@
             share_type_id=self.share_type_id,
             share_group_id=share_group['id'],
             cleanup_in_class=False,
-            version=constants.MIN_SHARE_GROUP_MICROVERSION,
-            experimental=True)
+            version=constants.MIN_SHARE_GROUP_MICROVERSION)
 
         # Delete
         params = {"share_group_id": share_group['id']}
@@ -163,7 +162,7 @@
 
         new_shares = self.shares_v2_client.list_shares(
             params={'share_group_id': new_share_group['id']},
-            version=constants.MIN_SHARE_GROUP_MICROVERSION, experimental=True)
+            version=constants.MIN_SHARE_GROUP_MICROVERSION)
 
         self.assertEmpty(
             new_shares, 'Expected 0 new shares, got %s' % len(new_shares))
@@ -236,7 +235,6 @@
             'share_group_id': share_group['id'],
             'version': '2.33',
             'cleanup_in_class': False,
-            'experimental': True,
         }
         if where_specify_az == 'sg_and_share':
             s_kwargs['availability_zone'] = azs[0]
diff --git a/manila_tempest_tests/tests/api/test_share_groups_negative.py b/manila_tempest_tests/tests/api/test_share_groups_negative.py
index 60e7b18..a0a6b4b 100644
--- a/manila_tempest_tests/tests/api/test_share_groups_negative.py
+++ b/manila_tempest_tests/tests/api/test_share_groups_negative.py
@@ -67,7 +67,6 @@
             size=cls.share_size,
             share_type_id=cls.share_type_id,
             share_group_id=cls.share_group['id'],
-            experimental=True,
         )
         # Create a share group snapshot of the share group
         cls.sg_snap_name = data_utils.rand_name("tempest-sg-snap-name")
diff --git a/manila_tempest_tests/utils.py b/manila_tempest_tests/utils.py
index b22a5c1..e842a33 100644
--- a/manila_tempest_tests/utils.py
+++ b/manila_tempest_tests/utils.py
@@ -24,6 +24,7 @@
 CONF = config.CONF
 SHARE_NETWORK_SUBNETS_MICROVERSION = '2.51'
 SHARE_REPLICA_QUOTAS_MICROVERSION = "2.53"
+EXPERIMENTAL = {'X-OpenStack-Manila-API-Experimental': 'True'}
 
 
 def get_microversion_as_tuple(microversion_str):
@@ -211,3 +212,12 @@
     return next((
         subnet for subnet in share_network.get('share_network_subnets', [])
         if subnet['availability_zone'] is None), None)
+
+
+def get_extra_headers(request_version, graduation_version):
+    headers = None
+    extra_headers = False
+    if is_microversion_lt(request_version, graduation_version):
+        headers = EXPERIMENTAL
+        extra_headers = True
+    return headers, extra_headers