Merge "Add compare header version to create_image_from_server helper"
diff --git a/releasenotes/notes/fix-list-group-snapshots-api-969d9321002c566c.yaml b/releasenotes/notes/fix-list-group-snapshots-api-969d9321002c566c.yaml
new file mode 100644
index 0000000..775a383
--- /dev/null
+++ b/releasenotes/notes/fix-list-group-snapshots-api-969d9321002c566c.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+  - |
+    Fix list_group_snapshots API in v3 group_snapshots_client: Bug#1715786.
+    The url path for list group snapshots with details API is changed from
+    ``?detail=True`` to ``/detail``.
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 4c49b2a..24c9c24 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -43,7 +43,7 @@
     for cont in containers:
         try:
             params = {'limit': 9999, 'format': 'json'}
-            _, objlist = container_client.list_container_contents(cont, params)
+            _, objlist = container_client.list_container_objects(cont, params)
             # delete every object in the container
             for obj in objlist:
                 test_utils.call_and_ignore_notfound_exc(
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
index 9abd59e..6599e43 100644
--- a/tempest/api/object_storage/test_account_bulk.py
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -96,7 +96,7 @@
         self.assertIn(container_name, [b['name'] for b in body])
 
         param = {'format': 'json'}
-        resp, contents_list = self.container_client.list_container_contents(
+        resp, contents_list = self.container_client.list_container_objects(
             container_name, param)
 
         self.assertHeaders(resp, 'Container', 'GET')
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
index 4b66ebf..765bc6d 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -41,10 +41,11 @@
         tenant_name = self.os_roles_operator_alt.credentials.tenant_name
         username = self.os_roles_operator_alt.credentials.username
         cont_headers = {'X-Container-Read': tenant_name + ':' + username}
+        container_client = self.os_roles_operator.container_client
         resp_meta, _ = (
-            self.os_roles_operator.container_client.update_container_metadata(
-                self.container_name, metadata=cont_headers,
-                metadata_prefix=''))
+            container_client.create_update_or_delete_container_metadata(
+                self.container_name, create_update_metadata=cont_headers,
+                create_update_metadata_prefix=''))
         self.assertHeaders(resp_meta, 'Container', 'POST')
         # create object
         object_name = data_utils.rand_name(name='Object')
@@ -68,10 +69,11 @@
         tenant_name = self.os_roles_operator_alt.credentials.tenant_name
         username = self.os_roles_operator_alt.credentials.username
         cont_headers = {'X-Container-Write': tenant_name + ':' + username}
+        container_client = self.os_roles_operator.container_client
         resp_meta, _ = (
-            self.os_roles_operator.container_client.update_container_metadata(
-                self.container_name, metadata=cont_headers,
-                metadata_prefix=''))
+            container_client.create_update_or_delete_container_metadata(
+                self.container_name, create_update_metadata=cont_headers,
+                create_update_metadata_prefix=''))
         self.assertHeaders(resp_meta, 'Container', 'POST')
         # set alternative authentication data; cannot simply use the
         # other object client.
diff --git a/tempest/api/object_storage/test_container_acl_negative.py b/tempest/api/object_storage/test_container_acl_negative.py
index e064753..03a5879 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -133,9 +133,10 @@
         # attempt to read object using non-authorized user
         # update X-Container-Read metadata ACL
         cont_headers = {'X-Container-Read': 'badtenant:baduser'}
-        resp_meta, _ = self.container_client.update_container_metadata(
-            self.container_name, metadata=cont_headers,
-            metadata_prefix='')
+        resp_meta, _ = (
+            self.container_client.create_update_or_delete_container_metadata(
+                self.container_name, create_update_metadata=cont_headers,
+                create_update_metadata_prefix=''))
         self.assertHeaders(resp_meta, 'Container', 'POST')
         # create object
         object_name = data_utils.rand_name(name='Object')
@@ -157,9 +158,10 @@
         # attempt to write object using non-authorized user
         # update X-Container-Write metadata ACL
         cont_headers = {'X-Container-Write': 'badtenant:baduser'}
-        resp_meta, _ = self.container_client.update_container_metadata(
-            self.container_name, metadata=cont_headers,
-            metadata_prefix='')
+        resp_meta, _ = (
+            self.container_client.create_update_or_delete_container_metadata(
+                self.container_name, create_update_metadata=cont_headers,
+                create_update_metadata_prefix=''))
         self.assertHeaders(resp_meta, 'Container', 'POST')
         # Trying to write the object without rights
         self.object_client.auth_provider.set_alt_auth_data(
@@ -182,9 +184,10 @@
         cont_headers = {'X-Container-Read':
                         tenant_name + ':' + username,
                         'X-Container-Write': ''}
-        resp_meta, _ = self.container_client.update_container_metadata(
-            self.container_name, metadata=cont_headers,
-            metadata_prefix='')
+        resp_meta, _ = (
+            self.container_client.create_update_or_delete_container_metadata(
+                self.container_name, create_update_metadata=cont_headers,
+                create_update_metadata_prefix=''))
         self.assertHeaders(resp_meta, 'Container', 'POST')
         # Trying to write the object without write rights
         self.object_client.auth_provider.set_alt_auth_data(
@@ -207,9 +210,10 @@
         cont_headers = {'X-Container-Read':
                         tenant_name + ':' + username,
                         'X-Container-Write': ''}
-        resp_meta, _ = self.container_client.update_container_metadata(
-            self.container_name, metadata=cont_headers,
-            metadata_prefix='')
+        resp_meta, _ = (
+            self.container_client.create_update_or_delete_container_metadata(
+                self.container_name, create_update_metadata=cont_headers,
+                create_update_metadata_prefix=''))
         self.assertHeaders(resp_meta, 'Container', 'POST')
         # create object
         object_name = data_utils.rand_name(name='Object')
diff --git a/tempest/api/object_storage/test_container_quotas.py b/tempest/api/object_storage/test_container_quotas.py
index c87bed5..982c4a1 100644
--- a/tempest/api/object_storage/test_container_quotas.py
+++ b/tempest/api/object_storage/test_container_quotas.py
@@ -40,8 +40,8 @@
         self.container_name = self.create_container()
         metadata = {"quota-bytes": str(QUOTA_BYTES),
                     "quota-count": str(QUOTA_COUNT), }
-        self.container_client.update_container_metadata(
-            self.container_name, metadata)
+        self.container_client.create_update_or_delete_container_metadata(
+            self.container_name, create_update_metadata=metadata)
 
     def tearDown(self):
         """Cleans the container of any object after each test."""
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index 76fe8d4..c6f21ec 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -130,7 +130,7 @@
         container_name = self.create_container()
         object_name, _ = self.create_object(container_name)
 
-        resp, object_list = self.container_client.list_container_contents(
+        resp, object_list = self.container_client.list_container_objects(
             container_name)
         self.assertHeaders(resp, 'Container', 'GET')
         self.assertEqual([object_name], object_list)
@@ -140,7 +140,7 @@
         # get empty container contents list
         container_name = self.create_container()
 
-        resp, object_list = self.container_client.list_container_contents(
+        resp, object_list = self.container_client.list_container_objects(
             container_name)
         self.assertHeaders(resp, 'Container', 'GET')
         self.assertEmpty(object_list)
@@ -153,7 +153,7 @@
         self.create_object(container_name, object_name)
 
         params = {'delimiter': '/'}
-        resp, object_list = self.container_client.list_container_contents(
+        resp, object_list = self.container_client.list_container_objects(
             container_name,
             params=params)
         self.assertHeaders(resp, 'Container', 'GET')
@@ -166,7 +166,7 @@
         object_name, _ = self.create_object(container_name)
 
         params = {'end_marker': object_name + 'zzzz'}
-        resp, object_list = self.container_client.list_container_contents(
+        resp, object_list = self.container_client.list_container_objects(
             container_name,
             params=params)
         self.assertHeaders(resp, 'Container', 'GET')
@@ -179,7 +179,7 @@
         self.create_object(container_name)
 
         params = {'format': 'json'}
-        resp, object_list = self.container_client.list_container_contents(
+        resp, object_list = self.container_client.list_container_objects(
             container_name,
             params=params)
         self.assertHeaders(resp, 'Container', 'GET')
@@ -198,7 +198,7 @@
         self.create_object(container_name)
 
         params = {'format': 'xml'}
-        resp, object_list = self.container_client.list_container_contents(
+        resp, object_list = self.container_client.list_container_objects(
             container_name,
             params=params)
         self.assertHeaders(resp, 'Container', 'GET')
@@ -222,7 +222,7 @@
         object_name, _ = self.create_object(container_name)
 
         params = {'limit': data_utils.rand_int_id(1, 10000)}
-        resp, object_list = self.container_client.list_container_contents(
+        resp, object_list = self.container_client.list_container_objects(
             container_name,
             params=params)
         self.assertHeaders(resp, 'Container', 'GET')
@@ -235,7 +235,7 @@
         object_name, _ = self.create_object(container_name)
 
         params = {'marker': 'AaaaObject1234567890'}
-        resp, object_list = self.container_client.list_container_contents(
+        resp, object_list = self.container_client.list_container_objects(
             container_name,
             params=params)
         self.assertHeaders(resp, 'Container', 'GET')
@@ -250,7 +250,7 @@
         self.create_object(container_name, object_name)
 
         params = {'path': 'Swift'}
-        resp, object_list = self.container_client.list_container_contents(
+        resp, object_list = self.container_client.list_container_objects(
             container_name,
             params=params)
         self.assertHeaders(resp, 'Container', 'GET')
@@ -264,7 +264,7 @@
 
         prefix_key = object_name[0:8]
         params = {'prefix': prefix_key}
-        resp, object_list = self.container_client.list_container_contents(
+        resp, object_list = self.container_client.list_container_objects(
             container_name,
             params=params)
         self.assertHeaders(resp, 'Container', 'GET')
@@ -277,9 +277,9 @@
         container_name = self.create_container()
 
         metadata = {'name': 'Pictures'}
-        self.container_client.update_container_metadata(
+        self.container_client.create_update_or_delete_container_metadata(
             container_name,
-            metadata=metadata)
+            create_update_metadata=metadata)
 
         resp, _ = self.container_client.list_container_metadata(
             container_name)
@@ -307,10 +307,11 @@
         self.containers.append(container_name)
 
         metadata_2 = {'test-container-meta2': 'Meta2'}
-        resp, _ = self.container_client.update_container_metadata(
-            container_name,
-            metadata=metadata_2,
-            remove_metadata=metadata_1)
+        resp, _ = (
+            self.container_client.create_update_or_delete_container_metadata(
+                container_name,
+                create_update_metadata=metadata_2,
+                delete_metadata=metadata_1))
         self.assertHeaders(resp, 'Container', 'POST')
 
         resp, _ = self.container_client.list_container_metadata(
@@ -326,9 +327,10 @@
         container_name = self.create_container()
 
         metadata = {'test-container-meta1': 'Meta1'}
-        resp, _ = self.container_client.update_container_metadata(
-            container_name,
-            metadata=metadata)
+        resp, _ = (
+            self.container_client.create_update_or_delete_container_metadata(
+                container_name,
+                create_update_metadata=metadata))
         self.assertHeaders(resp, 'Container', 'POST')
 
         resp, _ = self.container_client.list_container_metadata(
@@ -346,9 +348,10 @@
                                                metadata=metadata)
         self.containers.append(container_name)
 
-        resp, _ = self.container_client.delete_container_metadata(
-            container_name,
-            metadata=metadata)
+        resp, _ = (
+            self.container_client.create_update_or_delete_container_metadata(
+                container_name,
+                delete_metadata=metadata))
         self.assertHeaders(resp, 'Container', 'POST')
 
         resp, _ = self.container_client.list_container_metadata(
@@ -361,9 +364,10 @@
         container_name = self.create_container()
 
         metadata = {'test-container-meta1': ''}
-        resp, _ = self.container_client.update_container_metadata(
-            container_name,
-            metadata=metadata)
+        resp, _ = (
+            self.container_client.create_update_or_delete_container_metadata(
+                container_name,
+                create_update_metadata=metadata))
         self.assertHeaders(resp, 'Container', 'POST')
 
         resp, _ = self.container_client.list_container_metadata(
@@ -380,9 +384,10 @@
         self.containers.append(container_name)
 
         metadata = {'test-container-meta1': ''}
-        resp, _ = self.container_client.delete_container_metadata(
-            container_name,
-            metadata=metadata)
+        resp, _ = (
+            self.container_client.create_update_or_delete_container_metadata(
+                container_name,
+                delete_metadata=metadata))
         self.assertHeaders(resp, 'Container', 'POST')
 
         resp, _ = self.container_client.list_container_metadata(container_name)
diff --git a/tempest/api/object_storage/test_container_services_negative.py b/tempest/api/object_storage/test_container_services_negative.py
index 387b7b6..707c016 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -120,9 +120,10 @@
         # Attempts to update metadata using a nonexistent container name.
         metadata = {'animal': 'penguin'}
 
-        self.assertRaises(exceptions.NotFound,
-                          self.container_client.update_container_metadata,
-                          'nonexistent_container_name', metadata)
+        self.assertRaises(
+            exceptions.NotFound,
+            self.container_client.create_update_or_delete_container_metadata,
+            'nonexistent_container_name', create_update_metadata=metadata)
 
     @decorators.attr(type=["negative"])
     @decorators.idempotent_id('65387dbf-a0e2-4aac-9ddc-16eb3f1f69ba')
@@ -130,9 +131,10 @@
         # Attempts to delete metadata using a nonexistent container name.
         metadata = {'animal': 'penguin'}
 
-        self.assertRaises(exceptions.NotFound,
-                          self.container_client.delete_container_metadata,
-                          'nonexistent_container_name', metadata)
+        self.assertRaises(
+            exceptions.NotFound,
+            self.container_client.create_update_or_delete_container_metadata,
+            'nonexistent_container_name', delete_metadata=metadata)
 
     @decorators.attr(type=["negative"])
     @decorators.idempotent_id('14331d21-1e81-420a-beea-19cb5e5207f5')
@@ -141,7 +143,7 @@
         # that doesn't exist.
         params = {'limit': 9999, 'format': 'json'}
         self.assertRaises(exceptions.NotFound,
-                          self.container_client.list_container_contents,
+                          self.container_client.list_container_objects,
                           'nonexistent_container_name', params)
 
     @decorators.attr(type=["negative"])
@@ -155,7 +157,7 @@
         self.assertHeaders(resp, 'Container', 'DELETE')
         params = {'limit': 9999, 'format': 'json'}
         self.assertRaises(exceptions.NotFound,
-                          self.container_client.list_container_contents,
+                          self.container_client.list_container_objects,
                           container_name, params)
 
     @decorators.attr(type=["negative"])
diff --git a/tempest/api/object_storage/test_container_staticweb.py b/tempest/api/object_storage/test_container_staticweb.py
index 92fa690..1243b83 100644
--- a/tempest/api/object_storage/test_container_staticweb.py
+++ b/tempest/api/object_storage/test_container_staticweb.py
@@ -34,10 +34,10 @@
         cls.object_name, cls.object_data = cls.create_object(
             cls.container_name)
 
-        cls.container_client.update_container_metadata(
+        cls.container_client.create_update_or_delete_container_metadata(
             cls.container_name,
-            metadata=headers_public_read_acl,
-            metadata_prefix="X-Container-")
+            create_update_metadata=headers_public_read_acl,
+            create_update_metadata_prefix="X-Container-")
 
     @classmethod
     def resource_cleanup(cls):
@@ -49,8 +49,8 @@
     def test_web_index(self):
         headers = {'web-index': self.object_name}
 
-        self.container_client.update_container_metadata(
-            self.container_name, metadata=headers)
+        self.container_client.create_update_or_delete_container_metadata(
+            self.container_name, create_update_metadata=headers)
 
         # Maintain original headers, no auth added
         self.account_client.auth_provider.set_alt_auth_data(
@@ -68,8 +68,9 @@
         self.assertEqual(body, self.object_data)
 
         # clean up before exiting
-        self.container_client.update_container_metadata(self.container_name,
-                                                        {'web-index': ""})
+        self.container_client.create_update_or_delete_container_metadata(
+            self.container_name,
+            create_update_metadata={'web-index': ""})
 
         _, body = self.container_client.list_container_metadata(
             self.container_name)
@@ -80,8 +81,8 @@
     def test_web_listing(self):
         headers = {'web-listings': 'true'}
 
-        self.container_client.update_container_metadata(
-            self.container_name, metadata=headers)
+        self.container_client.create_update_or_delete_container_metadata(
+            self.container_name, create_update_metadata=headers)
 
         # test GET on http://account_url/container_name
         # we should retrieve a listing of objects
@@ -100,9 +101,9 @@
         self.assertIn(self.object_name, body.decode())
 
         # clean up before exiting
-        self.container_client.update_container_metadata(self.container_name,
-                                                        {'web-listings': ""})
-
+        self.container_client.create_update_or_delete_container_metadata(
+            self.container_name,
+            create_update_metadata={'web-listings': ""})
         _, body = self.container_client.list_container_metadata(
             self.container_name)
         self.assertNotIn('x-container-meta-web-listings', body)
@@ -113,8 +114,8 @@
         headers = {'web-listings': 'true',
                    'web-listings-css': 'listings.css'}
 
-        self.container_client.update_container_metadata(
-            self.container_name, metadata=headers)
+        self.container_client.create_update_or_delete_container_metadata(
+            self.container_name, create_update_metadata=headers)
 
         # Maintain original headers, no auth added
         self.account_client.auth_provider.set_alt_auth_data(
@@ -136,8 +137,8 @@
         headers = {'web-listings': 'true',
                    'web-error': self.object_name}
 
-        self.container_client.update_container_metadata(
-            self.container_name, metadata=headers)
+        self.container_client.create_update_or_delete_container_metadata(
+            self.container_name, create_update_metadata=headers)
 
         # Create object to return when requested object not found
         object_name_404 = "404" + self.object_name
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 7665b48..042d288 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -102,7 +102,7 @@
         while self.attempts > 0:
             object_lists = []
             for c_client, cont in zip(cont_client, self.containers):
-                resp, object_list = c_client.list_container_contents(
+                resp, object_list = c_client.list_container_objects(
                     cont, params=params)
                 object_lists.append(dict(
                     (obj['name'], obj) for obj in object_list))
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index d3cdb72..836a875 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -990,8 +990,11 @@
 
         # update container metadata to make it publicly readable
         cont_headers = {'X-Container-Read': '.r:*,.rlistings'}
-        resp_meta, body = self.container_client.update_container_metadata(
-            self.container_name, metadata=cont_headers, metadata_prefix='')
+        resp_meta, body = (
+            self.container_client.create_update_or_delete_container_metadata(
+                self.container_name,
+                create_update_metadata=cont_headers,
+                create_update_metadata_prefix=''))
         self.assertHeaders(resp_meta, 'Container', 'POST')
 
         # create object
@@ -1025,9 +1028,10 @@
         # make container public-readable and access an object in it using
         # another user's credentials
         cont_headers = {'X-Container-Read': '.r:*,.rlistings'}
-        resp_meta, body = self.container_client.update_container_metadata(
-            self.container_name, metadata=cont_headers,
-            metadata_prefix='')
+        resp_meta, body = (
+            self.container_client.create_update_or_delete_container_metadata(
+                self.container_name, create_update_metadata=cont_headers,
+                create_update_metadata_prefix=''))
         self.assertHeaders(resp_meta, 'Container', 'POST')
 
         # create object
diff --git a/tempest/api/object_storage/test_object_slo.py b/tempest/api/object_storage/test_object_slo.py
index 65da63d..c66776e 100644
--- a/tempest/api/object_storage/test_object_slo.py
+++ b/tempest/api/object_storage/test_object_slo.py
@@ -172,6 +172,6 @@
         # Check only the format of common headers with custom matcher
         self.assertThat(resp, custom_matchers.AreAllWellFormatted())
 
-        resp, body = self.container_client.list_container_contents(
+        resp, body = self.container_client.list_container_objects(
             self.container_name)
         self.assertEqual(int(resp['x-container-object-count']), 0)
diff --git a/tempest/api/volume/admin/test_groups.py b/tempest/api/volume/admin/test_groups.py
index d4b2faa..3a91642 100644
--- a/tempest/api/volume/admin/test_groups.py
+++ b/tempest/api/volume/admin/test_groups.py
@@ -171,18 +171,20 @@
             group_snapshot['id'])['group_snapshot']
         self.assertEqual(group_snapshot_name, group_snapshot['name'])
 
-        # Get all group snapshots with detail
-        group_snapshots = (
-            self.group_snapshots_client.list_group_snapshots(
-                detail=True)['group_snapshots'])
+        # Get all group snapshots with details, check some detail-specific
+        # elements, and look for the created group snapshot
+        group_snapshots = (self.group_snapshots_client.list_group_snapshots(
+            detail=True)['group_snapshots'])
+        for grp_snapshot in group_snapshots:
+            self.assertIn('created_at', grp_snapshot)
+            self.assertIn('group_id', grp_snapshot)
         self.assertIn((group_snapshot['name'], group_snapshot['id']),
                       [(m['name'], m['id']) for m in group_snapshots])
 
         # Delete group snapshot
         self._delete_group_snapshot(group_snapshot['id'], grp['id'])
-        group_snapshots = (
-            self.group_snapshots_client.list_group_snapshots(
-                detail=True)['group_snapshots'])
+        group_snapshots = (self.group_snapshots_client.list_group_snapshots()
+                           ['group_snapshots'])
         self.assertEmpty(group_snapshots)
 
     @decorators.idempotent_id('eff52c70-efc7-45ed-b47a-4ad675d09b81')
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index a8a6ff0..a430d5d 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -52,9 +52,5 @@
                "the configured network")
 
 
-class RFCViolation(exceptions.RestClientException):
-    message = "RFC Violation"
-
-
 class InvalidServiceTag(exceptions.TempestException):
     message = "Invalid service tag"
diff --git a/tempest/lib/services/volume/v3/group_snapshots_client.py b/tempest/lib/services/volume/v3/group_snapshots_client.py
index e644f02..d74e68a 100644
--- a/tempest/lib/services/volume/v3/group_snapshots_client.py
+++ b/tempest/lib/services/volume/v3/group_snapshots_client.py
@@ -60,7 +60,7 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def list_group_snapshots(self, **params):
+    def list_group_snapshots(self, detail=False, **params):
         """Information for all the tenant's group snapshots.
 
         For more information, please refer to the official API reference:
@@ -68,6 +68,8 @@
         https://developer.openstack.org/api-ref/block-storage/v3/#list-group-snapshots-with-details
         """
         url = "group_snapshots"
+        if detail:
+            url += "/detail"
         if params:
             url += '?%s' % urllib.urlencode(params)
         resp, body = self.get(url)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 2843222..52ec9a3 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -858,22 +858,6 @@
                         floating_ip['id'])
         return floating_ip
 
-    def _associate_floating_ip(self, floating_ip, server):
-        port_id, _ = self._get_server_port_id_and_ip4(server)
-        kwargs = dict(port_id=port_id)
-        floating_ip = self.floating_ips_client.update_floatingip(
-            floating_ip['id'], **kwargs)['floatingip']
-        self.assertEqual(port_id, floating_ip['port_id'])
-        return floating_ip
-
-    def _disassociate_floating_ip(self, floating_ip):
-        """:param floating_ip: floating_ips_client.create_floatingip"""
-        kwargs = dict(port_id=None)
-        floating_ip = self.floating_ips_client.update_floatingip(
-            floating_ip['id'], **kwargs)['floatingip']
-        self.assertIsNone(floating_ip['port_id'])
-        return floating_ip
-
     def check_floating_ip_status(self, floating_ip, status):
         """Verifies floatingip reaches the given status
 
@@ -1183,12 +1167,6 @@
                         router['id'])
         return router
 
-    def _update_router_admin_state(self, router, admin_state_up):
-        kwargs = dict(admin_state_up=admin_state_up)
-        router = self.routers_client.update_router(
-            router['id'], **kwargs)['router']
-        self.assertEqual(admin_state_up, router['admin_state_up'])
-
     def create_networks(self, networks_client=None,
                         routers_client=None, subnets_client=None,
                         tenant_id=None, dns_nameservers=None,
@@ -1355,7 +1333,7 @@
             present_obj = []
         if not_present_obj is None:
             not_present_obj = []
-        _, object_list = self.container_client.list_container_contents(
+        _, object_list = self.container_client.list_container_objects(
             container_name)
         if present_obj:
             for obj in present_obj:
@@ -1367,8 +1345,8 @@
     def change_container_acl(self, container_name, acl):
         metadata_param = {'metadata_prefix': 'x-container-',
                           'metadata': {'read': acl}}
-        self.container_client.update_container_metadata(container_name,
-                                                        **metadata_param)
+        self.container_client.create_update_or_delete_container_metadata(
+            container_name, create_update_metadata=metadata_param)
         resp, _ = self.container_client.list_container_metadata(container_name)
         self.assertEqual(resp['x-container-read'], acl)
 
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 2d38b06..0c3bf23 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -213,17 +213,20 @@
 
     def _disassociate_floating_ips(self):
         floating_ip, _ = self.floating_ip_tuple
-        self._disassociate_floating_ip(floating_ip)
-        self.floating_ip_tuple = Floating_IP_tuple(
-            floating_ip, None)
+        floating_ip = self.floating_ips_client.update_floatingip(
+            floating_ip['id'], port_id=None)['floatingip']
+        self.assertIsNone(floating_ip['port_id'])
+        self.floating_ip_tuple = Floating_IP_tuple(floating_ip, None)
 
     def _reassociate_floating_ips(self):
         floating_ip, server = self.floating_ip_tuple
         # create a new server for the floating ip
         server = self._create_server(self.network)
-        self._associate_floating_ip(floating_ip, server)
-        self.floating_ip_tuple = Floating_IP_tuple(
-            floating_ip, server)
+        port_id, _ = self._get_server_port_id_and_ip4(server)
+        floating_ip = self.floating_ips_client.update_floatingip(
+            floating_ip['id'], port_id=port_id)['floatingip']
+        self.assertEqual(port_id, floating_ip['port_id'])
+        self.floating_ip_tuple = Floating_IP_tuple(floating_ip, server)
 
     def _create_new_network(self, create_gateway=False):
         self.new_net = self._create_network()
@@ -355,6 +358,12 @@
             self.check_remote_connectivity(ssh_source, remote_ip,
                                            should_connect)
 
+    def _update_router_admin_state(self, router, admin_state_up):
+        kwargs = dict(admin_state_up=admin_state_up)
+        router = self.routers_client.update_router(
+            router['id'], **kwargs)['router']
+        self.assertEqual(admin_state_up, router['admin_state_up'])
+
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('f323b3ba-82f8-4db7-8ea6-6a895869ec49')
     @utils.services('compute', 'network')
diff --git a/tempest/services/object_storage/container_client.py b/tempest/services/object_storage/container_client.py
index afedd36..a253599 100644
--- a/tempest/services/object_storage/container_client.py
+++ b/tempest/services/object_storage/container_client.py
@@ -15,6 +15,7 @@
 
 from xml.etree import ElementTree as etree
 
+import debtcollector.moves
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
@@ -54,83 +55,57 @@
         self.expected_success(204, resp.status)
         return resp, body
 
-    def update_container_metadata(
+    def create_update_or_delete_container_metadata(
             self, container_name,
-            metadata=None,
-            remove_metadata=None,
-            metadata_prefix='X-Container-Meta-',
-            remove_metadata_prefix='X-Remove-Container-Meta-'):
-        """Updates arbitrary metadata on container."""
+            create_update_metadata=None,
+            delete_metadata=None,
+            create_update_metadata_prefix='X-Container-Meta-',
+            delete_metadata_prefix='X-Remove-Container-Meta-'):
+        """Creates, Updates or deletes an containter metadata entry.
+
+        Container Metadata can be created, updated or deleted based on
+        metadata header or value. For detailed info, please refer to the
+        official API reference:
+        https://developer.openstack.org/api-ref/object-store/#create-update-or-delete-container-metadata
+        """
         url = str(container_name)
         headers = {}
+        if create_update_metadata:
+            for key in create_update_metadata:
+                metadata_header_name = create_update_metadata_prefix + key
+                headers[metadata_header_name] = create_update_metadata[key]
+        if delete_metadata:
+            for key in delete_metadata:
+                headers[delete_metadata_prefix + key] = delete_metadata[key]
 
-        if metadata is not None:
-            for key in metadata:
-                headers[metadata_prefix + key] = metadata[key]
-        if remove_metadata is not None:
-            for key in remove_metadata:
-                headers[remove_metadata_prefix + key] = remove_metadata[key]
-
-        resp, body = self.post(url, body=None, headers=headers)
+        resp, body = self.post(url, headers=headers, body=None)
         self.expected_success(204, resp.status)
         return resp, body
 
-    def delete_container_metadata(self, container_name, metadata,
-                                  metadata_prefix='X-Remove-Container-Meta-'):
-        """Deletes arbitrary metadata on container."""
-        url = str(container_name)
-        headers = {}
-
-        if metadata is not None:
-            for item in metadata:
-                headers[metadata_prefix + item] = metadata[item]
-
-        resp, body = self.post(url, body=None, headers=headers)
-        self.expected_success(204, resp.status)
-        return resp, body
+    update_container_metadata = debtcollector.moves.moved_function(
+        create_update_or_delete_container_metadata,
+        'update_container_metadata', __name__,
+        version='Queens', removal_version='Rocky')
 
     def list_container_metadata(self, container_name):
-        """Retrieves container metadata headers"""
+        """List all container metadata."""
         url = str(container_name)
         resp, body = self.head(url)
         self.expected_success(204, resp.status)
         return resp, body
 
-    def list_container_contents(self, container, params=None):
+    def list_container_objects(self, container_name, params=None):
         """List the objects in a container, given the container name
 
-           Returns the container object listing as a plain text list, or as
-           xml or json if that option is specified via the 'format' argument.
+        Returns the container object listing as a plain text list, or as
+        xml or json if that option is specified via the 'format' argument.
 
-           Optional Arguments:
-           limit = integer
-               For an integer value n, limits the number of results to at most
-               n values.
-
-           marker = 'string'
-               Given a string value x, return object names greater in value
-               than the specified marker.
-
-           prefix = 'string'
-               For a string value x, causes the results to be limited to names
-               beginning with the substring x.
-
-           format = 'json' or 'xml'
-               Specify either json or xml to return the respective serialized
-               response.
-               If json, returns a list of json objects
-               if xml, returns a string of xml
-
-           path = 'string'
-               For a string value x, return the object names nested in the
-               pseudo path (assuming preconditions are met - see below).
-
-           delimiter = 'character'
-               For a character c, return all the object names nested in the
-               container (without the need for the directory marker objects).
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/object-storage/?expanded=show-container-details-and-list-objects-detail
         """
 
-        url = str(container)
+        url = str(container_name)
         if params:
             url += '?'
             url += '&%s' % urllib.urlencode(params)
@@ -148,3 +123,7 @@
 
         self.expected_success([200, 204], resp.status)
         return resp, body
+
+    list_container_contents = debtcollector.moves.moved_function(
+        list_container_objects, 'list_container_contents', __name__,
+        version='Queens', removal_version='Rocky')
diff --git a/tempest/tests/lib/services/volume/v3/test_group_snapshots_client.py b/tempest/tests/lib/services/volume/v3/test_group_snapshots_client.py
index 5ac5c08..840617c 100644
--- a/tempest/tests/lib/services/volume/v3/test_group_snapshots_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_group_snapshots_client.py
@@ -93,7 +93,8 @@
             bytes_body,
             group_snapshot_id="3fbbcccf-d058-4502-8844-6feeffdf4cb5")
 
-    def _test_list_group_snapshots(self, bytes_body=False, detail=False):
+    def _test_list_group_snapshots(self, detail=False, bytes_body=False,
+                                   mock_args='group_snapshots', **params):
         resp_body = []
         if detail:
             resp_body = self.FAKE_LIST_GROUP_SNAPSHOTS
@@ -111,8 +112,10 @@
             self.client.list_group_snapshots,
             'tempest.lib.common.rest_client.RestClient.get',
             resp_body,
-            bytes_body,
-            detail=detail)
+            to_utf=bytes_body,
+            mock_args=[mock_args],
+            detail=detail,
+            **params)
 
     def test_create_group_snapshot_with_str_body(self):
         self._test_create_group_snapshot()
@@ -132,6 +135,25 @@
     def test_list_group_snapshots_with_bytes_body(self):
         self._test_list_group_snapshots(bytes_body=True)
 
+    def test_list_group_snapshots_with_detail_with_str_body(self):
+        mock_args = "group_snapshots/detail"
+        self._test_list_group_snapshots(detail=True, mock_args=mock_args)
+
+    def test_list_group_snapshots_with_detail_with_bytes_body(self):
+        mock_args = "group_snapshots/detail"
+        self._test_list_group_snapshots(detail=True, bytes_body=True,
+                                        mock_args=mock_args)
+
+    def test_list_group_snapshots_with_params(self):
+        # Run the test separately for each param, to avoid assertion error
+        # resulting from randomized params order.
+        mock_args = 'group_snapshots?sort_key=name'
+        self._test_list_group_snapshots(mock_args=mock_args, sort_key='name')
+
+        mock_args = 'group_snapshots/detail?limit=10'
+        self._test_list_group_snapshots(detail=True, bytes_body=True,
+                                        mock_args=mock_args, limit=10)
+
     def test_delete_group_snapshot(self):
         self.check_service_client_function(
             self.client.delete_group_snapshot,