Merge "Allow to specify user and project domains in CLIclient"
diff --git a/.gitignore b/.gitignore
index 287db4c..7cb052f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,7 @@
 dist
 build
 .testrepository
+.stestr
 .idea
 .project
 .pydevproject
diff --git a/.stestr.conf b/.stestr.conf
new file mode 100644
index 0000000..e3201c1
--- /dev/null
+++ b/.stestr.conf
@@ -0,0 +1,4 @@
+[DEFAULT]
+test_path=./tempest/test_discover
+group_regex=([^\.]*\.)*
+
diff --git a/README.rst b/README.rst
index 17d4cba..c67362a 100644
--- a/README.rst
+++ b/README.rst
@@ -183,11 +183,11 @@
 Tempest also has a set of unit tests which test the Tempest code itself. These
 tests can be run by specifying the test discovery path::
 
-    $ OS_TEST_PATH=./tempest/tests testr run --parallel
+    $ stestr --test-path ./tempest/tests run
 
-By setting OS_TEST_PATH to ./tempest/tests it specifies that test discover
-should only be run on the unit test directory. The default value of OS_TEST_PATH
-is OS_TEST_PATH=./tempest/test_discover which will only run test discover on the
+By setting ``--test-path`` option to ./tempest/tests it specifies that test discover
+should only be run on the unit test directory. The default value of ``test_path``
+is ``test_path=./tempest/test_discover`` which will only run test discover on the
 Tempest suite.
 
 Alternatively, there are the py27 and py35 tox jobs which will run the unit
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 307eb07..acf5593 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -338,4 +338,28 @@
 
  * `3.3`_
 
- .. _3.3:  https://docs.openstack.org/cinder/latest/contributor/api_microversion_history.html#id4
+ .. _3.3:  https://docs.openstack.org/cinder/latest/contributor/api_microversion_history.html#id3
+
+ * `3.9`_
+
+ .. _3.9:  https://docs.openstack.org/cinder/latest/contributor/api_microversion_history.html#id9
+
+ * `3.11`_
+
+ .. _3.11:  https://docs.openstack.org/cinder/latest/contributor/api_microversion_history.html#id11
+
+ * `3.12`_
+
+ .. _3.12:  https://docs.openstack.org/cinder/latest/contributor/api_microversion_history.html#id12
+
+ * `3.14`_
+
+ .. _3.14:  https://docs.openstack.org/cinder/latest/contributor/api_microversion_history.html#id14
+
+ * `3.19`_
+
+ .. _3.19:  https://docs.openstack.org/cinder/latest/contributor/api_microversion_history.html#id18
+
+ * `3.20`_
+
+ .. _3.20:  https://docs.openstack.org/cinder/latest/contributor/api_microversion_history.html#id19
diff --git a/releasenotes/notes/add-load-list-cmd-35a4a2e6ea0a36fd.yaml b/releasenotes/notes/add-load-list-cmd-35a4a2e6ea0a36fd.yaml
new file mode 100644
index 0000000..403bbad
--- /dev/null
+++ b/releasenotes/notes/add-load-list-cmd-35a4a2e6ea0a36fd.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Adds a new cli option to tempest run, --load-list <list-file>
+    to specify target tests to run from a list-file. The list-file
+    supports the output format of the tempest run --list-tests
+    command.
diff --git a/releasenotes/notes/add-reset-group-snapshot-status-api-to-v3-group-snapshots-client-248d41827daf2a0c.yaml b/releasenotes/notes/add-reset-group-snapshot-status-api-to-v3-group-snapshots-client-248d41827daf2a0c.yaml
new file mode 100644
index 0000000..76b395d
--- /dev/null
+++ b/releasenotes/notes/add-reset-group-snapshot-status-api-to-v3-group-snapshots-client-248d41827daf2a0c.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add reset group snapshot status API to v3 group_snapshots_client library,
+    min_microversion of this API is 3.19. This feature enables the possibility
+    to reset group snapshot status.
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/releasenotes/notes/http_proxy_config-cb39b55520e84db5.yaml b/releasenotes/notes/http_proxy_config-cb39b55520e84db5.yaml
new file mode 100644
index 0000000..56969de
--- /dev/null
+++ b/releasenotes/notes/http_proxy_config-cb39b55520e84db5.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - Adds a new config options, ``proxy_url``. This options is used to configure
+    running tempest through a proxy server.
+  - The RestClient class in tempest.lib.rest_client has a new kwarg parameters,
+    ``proxy_url``, that is used to set a proxy server.
+  - A new class was added to tempest.lib.http, ClosingProxyHttp. This behaves
+    identically to ClosingHttp except that it requires a proxy url and will
+    establish a connection through a proxy
diff --git a/releasenotes/notes/make-account-client-as-stable-interface-d1b07c7e8f17bef6.yaml b/releasenotes/notes/make-account-client-as-stable-interface-d1b07c7e8f17bef6.yaml
index 9d5a1f5..5f899cf 100644
--- a/releasenotes/notes/make-account-client-as-stable-interface-d1b07c7e8f17bef6.yaml
+++ b/releasenotes/notes/make-account-client-as-stable-interface-d1b07c7e8f17bef6.yaml
@@ -7,3 +7,4 @@
     without any maintenance changes.
 
       * account_client
+      * container_client
diff --git a/releasenotes/notes/remove-deprecated-apis-from-v2-volumes-client-3ca4a5db5fea518f.yaml b/releasenotes/notes/remove-deprecated-apis-from-v2-volumes-client-3ca4a5db5fea518f.yaml
new file mode 100644
index 0000000..c75da2e
--- /dev/null
+++ b/releasenotes/notes/remove-deprecated-apis-from-v2-volumes-client-3ca4a5db5fea518f.yaml
@@ -0,0 +1,11 @@
+---
+upgrade:
+  - |
+    Remove deprecated APIs from volume v2 volumes_client, and the deprecated
+    APIs are re-realized in volume v2 transfers_client.
+
+    * create_volume_transfer
+    * show_volume_transfer
+    * list_volume_transfers
+    * delete_volume_transfer
+    * accept_volume_transfer
diff --git a/requirements.txt b/requirements.txt
index 911f0e5..8a2fa99 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,7 @@
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
 pbr!=2.1.0,>=2.0.0 # Apache-2.0
-cliff>=2.8.0 # Apache-2.0
+cliff!=2.9.0,>=2.8.0 # Apache-2.0
 jsonschema<3.0.0,>=2.6.0 # MIT
 testtools>=1.4.0 # MIT
 paramiko>=2.0.0 # LGPLv2.1+
diff --git a/tempest/api/compute/admin/test_aggregates_negative.py b/tempest/api/compute/admin/test_aggregates_negative.py
index 41be620..36ff09e 100644
--- a/tempest/api/compute/admin/test_aggregates_negative.py
+++ b/tempest/api/compute/admin/test_aggregates_negative.py
@@ -27,7 +27,6 @@
     def setup_clients(cls):
         super(AggregatesAdminNegativeTestJSON, cls).setup_clients()
         cls.client = cls.os_admin.aggregates_client
-        cls.user_client = cls.aggregates_client
 
     @classmethod
     def resource_setup(cls):
@@ -52,7 +51,7 @@
         # Regular user is not allowed to create an aggregate.
         aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
         self.assertRaises(lib_exc.Forbidden,
-                          self.user_client.create_aggregate,
+                          self.aggregates_client.create_aggregate,
                           name=aggregate_name)
 
     @decorators.attr(type=['negative'])
@@ -87,7 +86,7 @@
         # Regular user is not allowed to delete an aggregate.
         aggregate = self._create_test_aggregate()
         self.assertRaises(lib_exc.Forbidden,
-                          self.user_client.delete_aggregate,
+                          self.aggregates_client.delete_aggregate,
                           aggregate['id'])
 
     @decorators.attr(type=['negative'])
@@ -95,7 +94,7 @@
     def test_aggregate_list_as_user(self):
         # Regular user is not allowed to list aggregates.
         self.assertRaises(lib_exc.Forbidden,
-                          self.user_client.list_aggregates)
+                          self.aggregates_client.list_aggregates)
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('557cad12-34c9-4ff4-95f0-22f0dfbaf7dc')
@@ -103,7 +102,7 @@
         # Regular user is not allowed to get aggregate details.
         aggregate = self._create_test_aggregate()
         self.assertRaises(lib_exc.Forbidden,
-                          self.user_client.show_aggregate,
+                          self.aggregates_client.show_aggregate,
                           aggregate['id'])
 
     @decorators.attr(type=['negative'])
@@ -140,7 +139,7 @@
         # Regular user is not allowed to add a host to an aggregate.
         aggregate = self._create_test_aggregate()
         self.assertRaises(lib_exc.Forbidden,
-                          self.user_client.add_host,
+                          self.aggregates_client.add_host,
                           aggregate['id'], host=self.host)
 
     @decorators.attr(type=['negative'])
@@ -168,7 +167,7 @@
                         host=self.host)
 
         self.assertRaises(lib_exc.Forbidden,
-                          self.user_client.remove_host,
+                          self.aggregates_client.remove_host,
                           aggregate['id'], host=self.host)
 
     @decorators.attr(type=['negative'])
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index 14be947..411159b 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -46,6 +46,18 @@
                 "Less than 2 compute nodes, skipping migration test.")
 
     @classmethod
+    def setup_credentials(cls):
+        # These tests don't attempt any SSH validation nor do they use
+        # floating IPs on the instance, so all we need is a network and
+        # a subnet so the instance being migrated has a single port, but
+        # we need that to make sure we are properly updating the port
+        # host bindings during the live migration.
+        # TODO(mriedem): SSH validation before and after the instance is
+        # live migrated would be a nice test wrinkle addition.
+        cls.set_network_resources(network=True, subnet=True)
+        super(LiveMigrationTest, cls).setup_credentials()
+
+    @classmethod
     def setup_clients(cls):
         super(LiveMigrationTest, cls).setup_clients()
         cls.admin_migration_client = cls.os_admin.migrations_client
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 683d3e9..5504e05 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -262,7 +262,11 @@
 
         image = cls.compute_images_client.create_image(server_id, name=name,
                                                        **kwargs)
-        image_id = data_utils.parse_image_id(image.response['location'])
+        if api_version_utils.compare_version_header_to_response(
+            "OpenStack-API-Version", "compute 2.45", image.response, "lt"):
+            image_id = image['image_id']
+        else:
+            image_id = data_utils.parse_image_id(image.response['location'])
         cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
                                     cls.compute_images_client.delete_image,
                                     image_id)
@@ -418,6 +422,23 @@
                                                 volume['id'], 'available')
         return volume
 
+    def _detach_volume(self, server, volume):
+        """Helper method to detach a volume.
+
+        Ignores 404 responses if the volume or server do not exist, or the
+        volume is already detached from the server.
+        """
+        try:
+            volume = self.volumes_client.show_volume(volume['id'])['volume']
+            # Check the status. You can only detach an in-use volume, otherwise
+            # the compute API will return a 400 response.
+            if volume['status'] == 'in-use':
+                self.servers_client.detach_volume(server['id'], volume['id'])
+        except exceptions.NotFound:
+            # Ignore 404s on detach in case the server is deleted or the volume
+            # is already detached.
+            pass
+
     def attach_volume(self, server, volume, device=None, check_reserved=False):
         """Attaches volume to server and waits for 'in-use' volume status.
 
@@ -445,9 +466,7 @@
                         self.volumes_client, volume['id'], 'available')
         # Ignore 404s on detach in case the server is deleted or the volume
         # is already detached.
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.servers_client.detach_volume,
-                        server['id'], volume['id'])
+        self.addCleanup(self._detach_volume, server, volume)
         statuses = ['in-use']
         if check_reserved:
             statuses.append('reserved')
diff --git a/tempest/api/compute/flavors/test_flavors.py b/tempest/api/compute/flavors/test_flavors.py
index d5bb45a..20294e9 100644
--- a/tempest/api/compute/flavors/test_flavors.py
+++ b/tempest/api/compute/flavors/test_flavors.py
@@ -18,8 +18,6 @@
 
 
 class FlavorsV2TestJSON(base.BaseV2ComputeTest):
-    _min_disk = 'minDisk'
-    _min_ram = 'minRam'
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('e36c0eaa-dff5-4082-ad1f-3f9a80aa3f59')
@@ -89,7 +87,7 @@
         flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
         flavor_id = flavor['id']
 
-        params = {self._min_disk: flavor['disk'] + 1}
+        params = {'minDisk': flavor['disk'] + 1}
         flavors = self.flavors_client.list_flavors(detail=True,
                                                    **params)['flavors']
         self.assertEmpty([i for i in flavors if i['id'] == flavor_id])
@@ -100,7 +98,7 @@
         flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
         flavor_id = flavor['id']
 
-        params = {self._min_ram: flavor['ram'] + 1}
+        params = {'minRam': flavor['ram'] + 1}
         flavors = self.flavors_client.list_flavors(detail=True,
                                                    **params)['flavors']
         self.assertEmpty([i for i in flavors if i['id'] == flavor_id])
@@ -111,7 +109,7 @@
         flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
         flavor_id = flavor['id']
 
-        params = {self._min_disk: flavor['disk'] + 1}
+        params = {'minDisk': flavor['disk'] + 1}
         flavors = self.flavors_client.list_flavors(**params)['flavors']
         self.assertEmpty([i for i in flavors if i['id'] == flavor_id])
 
@@ -121,6 +119,6 @@
         flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
         flavor_id = flavor['id']
 
-        params = {self._min_ram: flavor['ram'] + 1}
+        params = {'minRam': flavor['ram'] + 1}
         flavors = self.flavors_client.list_flavors(**params)['flavors']
         self.assertEmpty([i for i in flavors if i['id'] == flavor_id])
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index 86e244b..8938570 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -26,8 +26,6 @@
 
 
 class FloatingIPsTestJSON(base.BaseFloatingIPsTest):
-    server_id = None
-    floating_ip = None
 
     @classmethod
     def skip_checks(cls):
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index e62e25e..058e7e6 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -15,6 +15,7 @@
 
 from tempest.api.compute import base
 from tempest import config
+from tempest.lib.common import api_version_utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
@@ -86,5 +87,9 @@
         # 4 byte utf-8 character.
         utf8_name = data_utils.rand_name(b'\xe2\x82\xa1'.decode('utf-8'))
         body = self.client.create_image(self.server_id, name=utf8_name)
-        image_id = data_utils.parse_image_id(body.response['location'])
+        if api_version_utils.compare_version_header_to_response(
+            "OpenStack-API-Version", "compute 2.45", body.response, "lt"):
+            image_id = body['image_id']
+        else:
+            image_id = data_utils.parse_image_id(body.response['location'])
         self.addCleanup(self.client.delete_image, image_id)
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index 7ecfa0a..4de00ce 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -19,6 +19,7 @@
 from tempest.api.compute import base
 from tempest.common import waiters
 from tempest import config
+from tempest.lib.common import api_version_utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
@@ -105,7 +106,11 @@
         self.assertRaises(lib_exc.Conflict, self.create_image_from_server,
                           self.server_id)
 
-        image_id = data_utils.parse_image_id(image.response['location'])
+        if api_version_utils.compare_version_header_to_response(
+            "OpenStack-API-Version", "compute 2.45", image.response, "lt"):
+            image_id = image['image_id']
+        else:
+            image_id = data_utils.parse_image_id(image.response['location'])
         self.client.delete_image(image_id)
 
     @decorators.attr(type=['negative'])
@@ -123,7 +128,11 @@
         # Return an error while trying to delete an image what is creating
 
         image = self.create_image_from_server(self.server_id)
-        image_id = data_utils.parse_image_id(image.response['location'])
+        if api_version_utils.compare_version_header_to_response(
+            "OpenStack-API-Version", "compute 2.45", image.response, "lt"):
+            image_id = image['image_id']
+        else:
+            image_id = data_utils.parse_image_id(image.response['location'])
 
         self.addCleanup(self._reset_server)
 
diff --git a/tempest/api/compute/servers/test_server_personality.py b/tempest/api/compute/servers/test_server_personality.py
index 2f0f5ee..6f32b46 100644
--- a/tempest/api/compute/servers/test_server_personality.py
+++ b/tempest/api/compute/servers/test_server_personality.py
@@ -44,7 +44,6 @@
     def setup_clients(cls):
         super(ServerPersonalityTestJSON, cls).setup_clients()
         cls.client = cls.servers_client
-        cls.user_client = cls.limits_client
 
     @decorators.idempotent_id('3cfe87fd-115b-4a02-b942-7dc36a337fdf')
     def test_create_server_with_personality(self):
@@ -104,7 +103,7 @@
         # number of files are injected into the server.
         file_contents = 'This is a test file.'
         personality = []
-        limits = self.user_client.show_limits()['limits']
+        limits = self.limits_client.show_limits()['limits']
         max_file_limit = limits['absolute']['maxPersonality']
         if max_file_limit == -1:
             raise self.skipException("No limit for personality files")
@@ -123,7 +122,7 @@
         # Server should be created successfully if maximum allowed number of
         # files is injected into the server during creation.
         file_contents = 'This is a test file.'
-        limits = self.user_client.show_limits()['limits']
+        limits = self.limits_client.show_limits()['limits']
         max_file_limit = limits['absolute']['maxPersonality']
         if max_file_limit == -1:
             raise self.skipException("No limit for personality files")
diff --git a/tempest/api/network/test_security_groups.py b/tempest/api/network/test_security_groups.py
index 97ccee9..24bd8ea 100644
--- a/tempest/api/network/test_security_groups.py
+++ b/tempest/api/network/test_security_groups.py
@@ -23,7 +23,6 @@
 
 
 class SecGroupTest(base.BaseSecGroupTest):
-    _project_network_cidr = CONF.network.project_network_cidr
 
     @classmethod
     def skip_checks(cls):
@@ -209,7 +208,7 @@
         protocol = 'tcp'
         port_range_min = 76
         port_range_max = 77
-        ip_prefix = self._project_network_cidr
+        ip_prefix = str(self.cidr)
         self._create_verify_security_group_rule(sg_id, direction,
                                                 self.ethertype, protocol,
                                                 port_range_min,
@@ -238,4 +237,3 @@
 
 class SecGroupIPv6Test(SecGroupTest):
     _ip_version = 6
-    _project_network_cidr = CONF.network.project_network_v6_cidr
diff --git a/tempest/api/network/test_security_groups_negative.py b/tempest/api/network/test_security_groups_negative.py
index 435673b..d054865 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -24,7 +24,6 @@
 
 
 class NegativeSecGroupTest(base.BaseSecGroupTest):
-    _project_network_cidr = CONF.network.project_network_cidr
 
     @classmethod
     def skip_checks(cls):
@@ -110,7 +109,7 @@
         sg2_body, _ = self._create_security_group()
 
         # Create rule specifying both remote_ip_prefix and remote_group_id
-        prefix = self._project_network_cidr
+        prefix = str(self.cidr)
         self.assertRaises(
             lib_exc.BadRequest,
             self.security_group_rules_client.create_security_group_rule,
@@ -225,7 +224,6 @@
 
 class NegativeSecGroupIPv6Test(NegativeSecGroupTest):
     _ip_version = 6
-    _project_network_cidr = CONF.network.project_network_v6_cidr
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7607439c-af73-499e-bf64-f687fd12a842')
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 4c49b2a..ee72163 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(
@@ -106,7 +106,7 @@
     def create_container(cls):
         # wrapper that returns a test container
         container_name = data_utils.rand_name(name='TestContainer')
-        cls.container_client.create_container(container_name)
+        cls.container_client.update_container(container_name)
         cls.containers.append(container_name)
 
         return container_name
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_account_services.py b/tempest/api/object_storage/test_account_services.py
index 0f86540..d7c85a2 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -43,7 +43,7 @@
         super(AccountTest, cls).resource_setup()
         for i in range(ord('a'), ord('f') + 1):
             name = data_utils.rand_name(name='%s-' % six.int2byte(i))
-            cls.container_client.create_container(name)
+            cls.container_client.update_container(name)
             cls.containers.append(name)
         cls.containers_count = len(cls.containers)
 
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..90b24b4 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -39,7 +39,7 @@
     def setUp(self):
         super(ObjectACLsNegativeTest, self).setUp()
         self.container_name = data_utils.rand_name(name='TestContainer')
-        self.container_client.create_container(self.container_name)
+        self.container_client.update_container(self.container_name)
 
     def tearDown(self):
         self.delete_containers([self.container_name])
@@ -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..cdc420e 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -27,7 +27,7 @@
     @decorators.idempotent_id('92139d73-7819-4db1-85f8-3f2f22a8d91f')
     def test_create_container(self):
         container_name = data_utils.rand_name(name='TestContainer')
-        resp, _ = self.container_client.create_container(container_name)
+        resp, _ = self.container_client.update_container(container_name)
         self.containers.append(container_name)
         self.assertHeaders(resp, 'Container', 'PUT')
 
@@ -35,20 +35,20 @@
     def test_create_container_overwrite(self):
         # overwrite container with the same name
         container_name = data_utils.rand_name(name='TestContainer')
-        self.container_client.create_container(container_name)
+        self.container_client.update_container(container_name)
         self.containers.append(container_name)
 
-        resp, _ = self.container_client.create_container(container_name)
+        resp, _ = self.container_client.update_container(container_name)
         self.assertHeaders(resp, 'Container', 'PUT')
 
     @decorators.idempotent_id('c2ac4d59-d0f5-40d5-ba19-0635056d48cd')
     def test_create_container_with_metadata_key(self):
         # create container with the blank value of metadata
         container_name = data_utils.rand_name(name='TestContainer')
-        metadata = {'test-container-meta': ''}
-        resp, _ = self.container_client.create_container(
+        headers = {'X-Container-Meta-test-container-meta': ''}
+        resp, _ = self.container_client.update_container(
             container_name,
-            metadata=metadata)
+            **headers)
         self.containers.append(container_name)
         self.assertHeaders(resp, 'Container', 'PUT')
 
@@ -64,10 +64,10 @@
         container_name = data_utils.rand_name(name='TestContainer')
 
         # metadata name using underscores should be converted to hyphens
-        metadata = {'test_container_meta': 'Meta1'}
-        resp, _ = self.container_client.create_container(
+        headers = {'X-Container-Meta-test_container_meta': 'Meta1'}
+        resp, _ = self.container_client.update_container(
             container_name,
-            metadata=metadata)
+            **headers)
         self.containers.append(container_name)
         self.assertHeaders(resp, 'Container', 'PUT')
 
@@ -75,22 +75,20 @@
             container_name)
         self.assertIn('x-container-meta-test-container-meta', resp)
         self.assertEqual(resp['x-container-meta-test-container-meta'],
-                         metadata['test_container_meta'])
+                         headers['X-Container-Meta-test_container_meta'])
 
     @decorators.idempotent_id('24d16451-1c0c-4e4f-b59c-9840a3aba40e')
     def test_create_container_with_remove_metadata_key(self):
         # create container with the blank value of remove metadata
         container_name = data_utils.rand_name(name='TestContainer')
-        metadata_1 = {'test-container-meta': 'Meta1'}
-        self.container_client.create_container(
-            container_name,
-            metadata=metadata_1)
+        headers = {'X-Container-Meta-test-container-meta': 'Meta1'}
+        self.container_client.update_container(container_name, **headers)
         self.containers.append(container_name)
 
-        metadata_2 = {'test-container-meta': ''}
-        resp, _ = self.container_client.create_container(
+        headers = {'X-Remove-Container-Meta-test-container-meta': ''}
+        resp, _ = self.container_client.update_container(
             container_name,
-            remove_metadata=metadata_2)
+            **headers)
         self.assertHeaders(resp, 'Container', 'PUT')
 
         resp, _ = self.container_client.list_container_metadata(
@@ -101,14 +99,13 @@
     def test_create_container_with_remove_metadata_value(self):
         # create container with remove metadata
         container_name = data_utils.rand_name(name='TestContainer')
-        metadata = {'test-container-meta': 'Meta1'}
-        self.container_client.create_container(container_name,
-                                               metadata=metadata)
+        headers = {'X-Container-Meta-test-container-meta': 'Meta1'}
+        self.container_client.update_container(container_name, **headers)
         self.containers.append(container_name)
-
-        resp, _ = self.container_client.create_container(
+        headers = {'X-Remove-Container-Meta-test-container-meta': 'Meta1'}
+        resp, _ = self.container_client.update_container(
             container_name,
-            remove_metadata=metadata)
+            **headers)
         self.assertHeaders(resp, 'Container', 'PUT')
 
         resp, _ = self.container_client.list_container_metadata(
@@ -130,7 +127,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 +137,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 +150,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 +163,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 +176,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 +195,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 +219,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 +232,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 +247,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 +261,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 +274,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)
@@ -301,16 +298,16 @@
     def test_update_container_metadata_with_create_and_delete_metadata(self):
         # Send one request of adding and deleting metadata
         container_name = data_utils.rand_name(name='TestContainer')
-        metadata_1 = {'test-container-meta1': 'Meta1'}
-        self.container_client.create_container(container_name,
-                                               metadata=metadata_1)
+        metadata_1 = {'X-Container-Meta-test-container-meta1': 'Meta1'}
+        self.container_client.update_container(container_name, **metadata_1)
         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={'test-container-meta1': 'Meta1'}))
         self.assertHeaders(resp, 'Container', 'POST')
 
         resp, _ = self.container_client.list_container_metadata(
@@ -326,9 +323,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(
@@ -341,14 +339,14 @@
     def test_update_container_metadata_with_delete_metadata(self):
         # update container metadata using delete metadata
         container_name = data_utils.rand_name(name='TestContainer')
-        metadata = {'test-container-meta1': 'Meta1'}
-        self.container_client.create_container(container_name,
-                                               metadata=metadata)
+        metadata = {'X-Container-Meta-test-container-meta1': 'Meta1'}
+        self.container_client.update_container(container_name, **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={'test-container-meta1': 'Meta1'}))
         self.assertHeaders(resp, 'Container', 'POST')
 
         resp, _ = self.container_client.list_container_metadata(
@@ -361,9 +359,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(
@@ -374,15 +373,15 @@
     def test_update_container_metadata_with_delete_metadata_key(self):
         # update container metadata with a blank value of metadata
         container_name = data_utils.rand_name(name='TestContainer')
-        metadata = {'test-container-meta1': 'Meta1'}
-        self.container_client.create_container(container_name,
-                                               metadata=metadata)
+        headers = {'X-Container-Meta-test-container-meta1': 'Meta1'}
+        self.container_client.update_container(container_name, **headers)
         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..b8c83b7 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -45,9 +45,10 @@
         max_length = self.constraints['max_container_name_length']
         # create a container with long name
         container_name = data_utils.arbitrary_string(size=max_length + 1)
-        ex = self.assertRaises(exceptions.BadRequest,
-                               self.container_client.create_container,
-                               container_name)
+        ex = self.assertRaises(
+            exceptions.BadRequest,
+            self.container_client.update_container,
+            container_name)
         self.assertIn('Container name length of ' + str(max_length + 1) +
                       ' longer than ' + str(max_length), str(ex))
 
@@ -61,11 +62,13 @@
         # that is longer than max.
         max_length = self.constraints['max_meta_name_length']
         container_name = data_utils.rand_name(name='TestContainer')
-        metadata_name = data_utils.arbitrary_string(size=max_length + 1)
+        metadata_name = 'X-Container-Meta-' + data_utils.arbitrary_string(
+            size=max_length + 1)
         metadata = {metadata_name: 'penguin'}
-        ex = self.assertRaises(exceptions.BadRequest,
-                               self.container_client.create_container,
-                               container_name, metadata=metadata)
+        ex = self.assertRaises(
+            exceptions.BadRequest,
+            self.container_client.update_container,
+            container_name, **metadata)
         self.assertIn('Metadata name too long', str(ex))
 
     @decorators.attr(type=["negative"])
@@ -79,10 +82,11 @@
         max_length = self.constraints['max_meta_value_length']
         container_name = data_utils.rand_name(name='TestContainer')
         metadata_value = data_utils.arbitrary_string(size=max_length + 1)
-        metadata = {'animal': metadata_value}
-        ex = self.assertRaises(exceptions.BadRequest,
-                               self.container_client.create_container,
-                               container_name, metadata=metadata)
+        metadata = {'X-Container-Meta-animal': metadata_value}
+        ex = self.assertRaises(
+            exceptions.BadRequest,
+            self.container_client.update_container,
+            container_name, **metadata)
         self.assertIn('Metadata value longer than ' + str(max_length), str(ex))
 
     @decorators.attr(type=["negative"])
@@ -97,11 +101,12 @@
         container_name = data_utils.rand_name(name='TestContainer')
         metadata = {}
         for i in range(max_count + 1):
-            metadata['animal-' + str(i)] = 'penguin'
+            metadata['X-Container-Meta-animal-' + str(i)] = 'penguin'
 
-        ex = self.assertRaises(exceptions.BadRequest,
-                               self.container_client.create_container,
-                               container_name, metadata=metadata)
+        ex = self.assertRaises(
+            exceptions.BadRequest,
+            self.container_client.update_container,
+            container_name, **metadata)
         self.assertIn('Too many metadata items; max ' + str(max_count),
                       str(ex))
 
@@ -120,9 +125,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 +136,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 +148,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 +162,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..61cbe30 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -786,12 +786,12 @@
     def test_copy_object_across_containers(self):
         # create a container to use as a source container
         src_container_name = data_utils.rand_name(name='TestSourceContainer')
-        self.container_client.create_container(src_container_name)
+        self.container_client.update_container(src_container_name)
         self.containers.append(src_container_name)
         # create a container to use as a destination container
         dst_container_name = data_utils.rand_name(
             name='TestDestinationContainer')
-        self.container_client.create_container(dst_container_name)
+        self.container_client.update_container(dst_container_name)
         self.containers.append(dst_container_name)
         # create object in source container
         object_name = data_utils.rand_name(name='Object')
@@ -977,7 +977,7 @@
     def setUp(self):
         super(PublicObjectTest, self).setUp()
         self.container_name = data_utils.rand_name(name='TestContainer')
-        self.container_client.create_container(self.container_name)
+        self.container_client.update_container(self.container_name)
 
     def tearDown(self):
         self.delete_containers([self.container_name])
@@ -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/object_storage/test_object_version.py b/tempest/api/object_storage/test_object_version.py
index 4799053..51b0a1d 100644
--- a/tempest/api/object_storage/test_object_version.py
+++ b/tempest/api/object_storage/test_object_version.py
@@ -51,18 +51,16 @@
     def test_versioned_container(self):
         # create container
         vers_container_name = data_utils.rand_name(name='TestVersionContainer')
-        resp, _ = self.container_client.create_container(
-            vers_container_name)
+        resp, _ = self.container_client.update_container(vers_container_name)
         self.containers.append(vers_container_name)
         self.assertHeaders(resp, 'Container', 'PUT')
         self.assertContainer(vers_container_name, '0', '0', 'Missing Header')
 
         base_container_name = data_utils.rand_name(name='TestBaseContainer')
         headers = {'X-versions-Location': vers_container_name}
-        resp, _ = self.container_client.create_container(
+        resp, _ = self.container_client.update_container(
             base_container_name,
-            metadata=headers,
-            metadata_prefix='')
+            **headers)
         self.containers.append(base_container_name)
         self.assertHeaders(resp, 'Container', 'PUT')
         self.assertContainer(base_container_name, '0', '0',
diff --git a/tempest/api/volume/admin/test_groups.py b/tempest/api/volume/admin/test_groups.py
index d4b2faa..6b53d85 100644
--- a/tempest/api/volume/admin/test_groups.py
+++ b/tempest/api/volume/admin/test_groups.py
@@ -63,9 +63,9 @@
 
 
 class GroupsTest(BaseGroupsTest):
+    _api_version = 3
     min_microversion = '3.14'
     max_microversion = 'latest'
-    _api_version = 3
 
     @decorators.idempotent_id('4b111d28-b73d-4908-9bd2-03dc2992e4d4')
     def test_group_create_show_list_delete(self):
@@ -108,16 +108,16 @@
         self.assertEqual(grp2_id, grp2['id'])
 
         # Get all groups with detail
-        grps = self.groups_client.list_groups(
-            detail=True)['groups']
-        filtered_grps = [g for g in grps if g['id'] in [grp1_id, grp2_id]]
-        self.assertEqual(2, len(filtered_grps))
-        for grp in filtered_grps:
-            self.assertEqual([volume_type['id']], grp['volume_types'])
-            self.assertEqual(group_type['id'], grp['group_type'])
+        grps = self.groups_client.list_groups(detail=True)['groups']
+        for grp_id in [grp1_id, grp2_id]:
+            filtered_grps = [g for g in grps if g['id'] == grp_id]
+            self.assertEqual(1, len(filtered_grps))
+            self.assertEqual([volume_type['id']],
+                             filtered_grps[0]['volume_types'])
+            self.assertEqual(group_type['id'],
+                             filtered_grps[0]['group_type'])
 
-        vols = self.volumes_client.list_volumes(
-            detail=True)['volumes']
+        vols = self.volumes_client.list_volumes(detail=True)['volumes']
         filtered_vols = [v for v in vols if v['id'] in [vol1_id]]
         self.assertEqual(1, len(filtered_vols))
         for vol in filtered_vols:
@@ -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')
@@ -297,8 +299,7 @@
         self.assertEqual(new_desc, grp['description'])
 
         # Get volumes in the group
-        vols = self.volumes_client.list_volumes(
-            detail=True)['volumes']
+        vols = self.volumes_client.list_volumes(detail=True)['volumes']
         grp_vols = [v for v in vols if v['group_id'] == grp['id']]
         self.assertEqual(1, len(grp_vols))
 
@@ -316,6 +317,55 @@
         self.assertEqual(2, len(grp_vols))
 
 
+class GroupsV319Test(BaseGroupsTest):
+    _api_version = 3
+    min_microversion = '3.19'
+    max_microversion = 'latest'
+
+    @decorators.idempotent_id('3b42c9b9-c984-4444-816e-ca2e1ed30b40')
+    def test_reset_group_snapshot_status(self):
+        # Create volume type
+        volume_type = self.create_volume_type()
+
+        # Create group type
+        group_type = self.create_group_type()
+
+        # Create group
+        group = self._create_group(group_type, volume_type)
+
+        # Create volume
+        volume = self.create_volume(volume_type=volume_type['id'],
+                                    group_id=group['id'])
+
+        # Create group snapshot
+        group_snapshot_name = data_utils.rand_name('group_snapshot')
+        group_snapshot = (self.group_snapshots_client.create_group_snapshot(
+            group_id=group['id'], name=group_snapshot_name)['group_snapshot'])
+        self.addCleanup(self._delete_group_snapshot,
+                        group_snapshot['id'], group['id'])
+        snapshots = self.snapshots_client.list_snapshots(
+            detail=True)['snapshots']
+        for snap in snapshots:
+            if volume['id'] == snap['volume_id']:
+                waiters.wait_for_volume_resource_status(
+                    self.snapshots_client, snap['id'], 'available')
+        waiters.wait_for_volume_resource_status(
+            self.group_snapshots_client, group_snapshot['id'], 'available')
+
+        # Reset group snapshot status
+        self.addCleanup(waiters.wait_for_volume_resource_status,
+                        self.group_snapshots_client,
+                        group_snapshot['id'], 'available')
+        self.addCleanup(
+            self.admin_group_snapshots_client.reset_group_snapshot_status,
+            group_snapshot['id'], 'available')
+        for status in ['creating', 'available', 'error']:
+            self.admin_group_snapshots_client.reset_group_snapshot_status(
+                group_snapshot['id'], status)
+            waiters.wait_for_volume_resource_status(
+                self.group_snapshots_client, group_snapshot['id'], status)
+
+
 class GroupsV320Test(BaseGroupsTest):
     _api_version = 3
     min_microversion = '3.20'
diff --git a/tempest/api/volume/test_availability_zone.py b/tempest/api/volume/test_availability_zone.py
index d0a87db..0b6ee38 100644
--- a/tempest/api/volume/test_availability_zone.py
+++ b/tempest/api/volume/test_availability_zone.py
@@ -20,14 +20,10 @@
 class AvailabilityZoneTestJSON(base.BaseVolumeTest):
     """Tests Availability Zone API List"""
 
-    @classmethod
-    def setup_clients(cls):
-        super(AvailabilityZoneTestJSON, cls).setup_clients()
-        cls.client = cls.availability_zone_client
-
     @decorators.idempotent_id('01f1ae88-eba9-4c6b-a011-6f7ace06b725')
     def test_get_availability_zone_list(self):
         # List of availability zone
-        availability_zone = (self.client.list_availability_zones()
-                             ['availabilityZoneInfo'])
+        availability_zone = (
+            self.availability_zone_client.list_availability_zones()
+            ['availabilityZoneInfo'])
         self.assertNotEmpty(availability_zone)
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index 1eb76a0..de28a30 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -13,12 +13,15 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import time
+
 import testtools
 
 from tempest.api.volume import base
 from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 
 CONF = config.CONF
 
@@ -53,3 +56,129 @@
         resized_volume = self.volumes_client.show_volume(
             volume['id'])['volume']
         self.assertEqual(extend_size, resized_volume['size'])
+
+
+class VolumesExtendAttachedTest(base.BaseVolumeTest):
+    """Tests extending the size of an attached volume."""
+
+    # We need admin credentials for getting instance action event details. By
+    # default a non-admin can list and show instance actions if they own the
+    # server instance, but since the event details can contain error messages
+    # and tracebacks, like an instance fault, those are not viewable by
+    # non-admins. This is obviously not a great user experience since the user
+    # may not know when the operation is actually complete. A microversion in
+    # the compute API will be added so that non-admins can see instance action
+    # events but will continue to hide the traceback field.
+    # TODO(mriedem): Change this to not rely on the admin user to get the event
+    # details once that microversion is available in Nova.
+    credentials = ['primary', 'admin']
+
+    _api_version = 3
+    # NOTE(mriedem): The minimum required volume API version is 3.42 and the
+    # minimum required compute API microversion is 2.51, but the compute call
+    # is implicit - Cinder calls Nova at that microversion, Tempest does not.
+    min_microversion = '3.42'
+
+    @classmethod
+    def setup_clients(cls):
+        super(VolumesExtendAttachedTest, cls).setup_clients()
+        cls.admin_servers_client = cls.os_admin.servers_client
+
+    def _find_extend_volume_instance_action(self, server_id):
+        actions = self.servers_client.list_instance_actions(
+            server_id)['instanceActions']
+        for action in actions:
+            if action['action'] == 'extend_volume':
+                return action
+
+    def _find_extend_volume_instance_action_finish_event(self, action):
+        # This has to be called by an admin client otherwise
+        # the events don't show up.
+        action = self.admin_servers_client.show_instance_action(
+            action['instance_uuid'], action['request_id'])['instanceAction']
+        for event in action['events']:
+            if (event['event'] == 'compute_extend_volume' and
+                    event['finish_time']):
+                return event
+
+    @decorators.idempotent_id('301f5a30-1c6f-4ea0-be1a-91fd28d44354')
+    @testtools.skipUnless(CONF.volume_feature_enabled.extend_attached_volume,
+                          "Attached volume extend is disabled.")
+    def test_extend_attached_volume(self):
+        """This is a happy path test which does the following:
+
+        * Create a volume at the configured volume_size.
+        * Create a server instance.
+        * Attach the volume to the server.
+        * Wait for the volume status to be "in-use".
+        * Extend the size of the volume and wait for the volume status to go
+          back to "in-use".
+        * Assert the volume size change is reflected in the volume API.
+        * Wait for the "compute_extend_volume" instance action event to show
+          up in the compute API with the success or failure status. We fail
+          if we timeout waiting for the instance action event to show up, or
+          if the action on the server fails.
+        """
+        # Create a test volume. Will be automatically cleaned up on teardown.
+        volume = self.create_volume()
+        # Create a test server. Will be automatically cleaned up on teardown.
+        server = self.create_server()
+        # Attach the volume to the server and wait for the volume status to be
+        # "in-use".
+        self.attach_volume(server['id'], volume['id'])
+        # Extend the size of the volume. If this is successful, the volume API
+        # will change the status on the volume to "extending" before doing an
+        # RPC cast to the volume manager on the backend. Note that we multiply
+        # the size of the volume since certain Cinder backends, e.g. ScaleIO,
+        # require multiples of 8GB.
+        extend_size = volume['size'] * 2
+        self.volumes_client.extend_volume(volume['id'], new_size=extend_size)
+        # The volume status should go back to in-use since it is still attached
+        # to the server instance.
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'in-use')
+        # Assert that the volume size has changed in the volume API.
+        volume = self.volumes_client.show_volume(volume['id'])['volume']
+        self.assertEqual(extend_size, volume['size'])
+        # Now we wait for the "compute_extend_volume" instance action event
+        # to show up for the server instance. This is our indication that the
+        # asynchronous operation is complete on the compute side.
+        start_time = int(time.time())
+        timeout = self.servers_client.build_timeout
+        action = self._find_extend_volume_instance_action(server['id'])
+        while action is None and int(time.time()) - start_time < timeout:
+            time.sleep(self.servers_client.build_interval)
+            action = self._find_extend_volume_instance_action(server['id'])
+
+        if action is None:
+            msg = ("Timed out waiting to get 'extend_volume' instance action "
+                   "record for server %(server)s after %(timeout)s seconds." %
+                   {'server': server['id'], 'timeout': timeout})
+            raise lib_exc.TimeoutException(msg)
+
+        # Now that we found the extend_volume instance action, we can wait for
+        # the compute_extend_volume instance action event to show up to
+        # indicate the operation is complete.
+        start_time = int(time.time())
+        event = self._find_extend_volume_instance_action_finish_event(action)
+        while event is None and int(time.time()) - start_time < timeout:
+            time.sleep(self.servers_client.build_interval)
+            event = self._find_extend_volume_instance_action_finish_event(
+                action)
+
+        if event is None:
+            msg = ("Timed out waiting to get 'compute_extend_volume' instance "
+                   "action event record for server %(server)s and request "
+                   "%(request_id)s after %(timeout)s seconds." %
+                   {'server': server['id'],
+                    'request_id': action['request_id'],
+                    'timeout': timeout})
+            raise lib_exc.TimeoutException(msg)
+
+        # Finally, assert that the action completed successfully.
+        self.assertTrue(
+            event['result'].lower() == 'success',
+            "Unexpected compute_extend_volume result '%(result)s' for request "
+            "%(request_id)s." %
+            {'result': event['result'],
+             'request_id': action['request_id']})
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
index e71032a..f07f197 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -47,6 +47,12 @@
 You can also use the **--list-tests** option in conjunction with selection
 arguments to list which tests will be run.
 
+You can also use the **--load-list** option that lets you pass a filepath to
+tempest run with the file format being in a non-regex format, similar to the
+tests generated by the **--list-tests** option. You can specify target tests
+by removing unnecessary tests from a list file which is generated from
+**--list-tests** option.
+
 Test Execution
 ==============
 There are several options to control how the tests are executed. By default
@@ -267,6 +273,12 @@
                                    help='Path to a blacklist file, this file '
                                         'contains a separate regex exclude on '
                                         'each newline')
+        list_selector.add_argument('--load-list', '--load_list',
+                                   help='Path to a non-regex whitelist file, '
+                                        'this file contains a seperate test '
+                                        'on each newline. This command'
+                                        'supports files created by the tempest'
+                                        'run ``--list-tests`` command')
         # list only args
         parser.add_argument('--list-tests', '-l', action='store_true',
                             help='List tests',
@@ -318,6 +330,8 @@
             options.append("--parallel")
         if parsed_args.concurrency:
             options.append("--concurrency=%s" % parsed_args.concurrency)
+        if parsed_args.load_list:
+            options.append("--load-list=%s" % parsed_args.load_list)
         return options
 
     def _run(self, regex, options):
diff --git a/tempest/config.py b/tempest/config.py
index 4d0839a..d2765fc 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -194,6 +194,8 @@
                default=60,
                help='Timeout in seconds to wait for the http request to '
                     'return'),
+    cfg.StrOpt('proxy_url',
+               help='Specify an http proxy to use.')
 ]
 
 identity_feature_group = cfg.OptGroup(name='identity-feature-enabled',
@@ -836,7 +838,14 @@
                 help="Is the v2 volume API enabled"),
     cfg.BoolOpt('api_v3',
                 default=True,
-                help="Is the v3 volume API enabled")
+                help="Is the v3 volume API enabled"),
+    cfg.BoolOpt('extend_attached_volume',
+                default=False,
+                help='Does the cloud support extending the size of a volume '
+                     'which is currently attached to a server instance? This '
+                     'depends on the 3.42 volume API microversion and the '
+                     '2.51 compute API microversion. Also, not all volume or '
+                     'compute backends support this operation.')
 ]
 
 
@@ -1301,6 +1310,7 @@
         * `ca_certs`
         * `trace_requests`
         * `http_timeout`
+        * `proxy_url`
 
     The dict returned by this does not fit a few service clients:
 
@@ -1323,7 +1333,8 @@
             CONF.identity.disable_ssl_certificate_validation,
         'ca_certs': CONF.identity.ca_certificates_file,
         'trace_requests': CONF.debug.trace_requests,
-        'http_timeout': CONF.service_clients.http_timeout
+        'http_timeout': CONF.service_clients.http_timeout,
+        'proxy_url': CONF.service_clients.proxy_url,
     }
 
     if service_client_name is None:
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/auth.py b/tempest/lib/auth.py
index ab4308f..a850fe1 100644
--- a/tempest/lib/auth.py
+++ b/tempest/lib/auth.py
@@ -261,12 +261,13 @@
     def __init__(self, credentials, auth_url,
                  disable_ssl_certificate_validation=None,
                  ca_certs=None, trace_requests=None, scope='project',
-                 http_timeout=None):
+                 http_timeout=None, proxy_url=None):
         super(KeystoneAuthProvider, self).__init__(credentials, scope)
         self.dscv = disable_ssl_certificate_validation
         self.ca_certs = ca_certs
         self.trace_requests = trace_requests
         self.http_timeout = http_timeout
+        self.proxy_url = proxy_url
         self.auth_url = auth_url
         self.auth_client = self._auth_client(auth_url)
 
@@ -345,7 +346,7 @@
         return json_v2id.TokenClient(
             auth_url, disable_ssl_certificate_validation=self.dscv,
             ca_certs=self.ca_certs, trace_requests=self.trace_requests,
-            http_timeout=self.http_timeout)
+            http_timeout=self.http_timeout, proxy_url=self.proxy_url)
 
     def _auth_params(self):
         """Auth parameters to be passed to the token request
@@ -433,7 +434,7 @@
         return json_v3id.V3TokenClient(
             auth_url, disable_ssl_certificate_validation=self.dscv,
             ca_certs=self.ca_certs, trace_requests=self.trace_requests,
-            http_timeout=self.http_timeout)
+            http_timeout=self.http_timeout, proxy_url=self.proxy_url)
 
     def _auth_params(self):
         """Auth parameters to be passed to the token request
diff --git a/tempest/lib/common/api_version_utils.py b/tempest/lib/common/api_version_utils.py
index 98f174d..bcb076b 100644
--- a/tempest/lib/common/api_version_utils.py
+++ b/tempest/lib/common/api_version_utils.py
@@ -12,7 +12,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from oslo_log import log as logging
 import testtools
 
 from tempest.lib.common import api_version_request
@@ -20,7 +19,6 @@
 
 
 LATEST_MICROVERSION = 'latest'
-LOG = logging.getLogger(__name__)
 
 
 class BaseMicroversionTest(object):
@@ -166,7 +164,6 @@
     if op is None:
         msg = ("Operation %s is invalid. Valid options include: lt, eq, gt, "
                "le, ne, ge." % operation)
-        LOG.debug(msg)
         raise exceptions.InvalidParam(invalid_param=msg)
 
     # Remove "volume" from "volume <microversion>", for example, so that the
diff --git a/tempest/lib/common/http.py b/tempest/lib/common/http.py
index b4b1fc9..738c37f 100644
--- a/tempest/lib/common/http.py
+++ b/tempest/lib/common/http.py
@@ -17,6 +17,47 @@
 import urllib3
 
 
+class ClosingProxyHttp(urllib3.ProxyManager):
+    def __init__(self, proxy_url, disable_ssl_certificate_validation=False,
+                 ca_certs=None, timeout=None):
+        kwargs = {}
+
+        if disable_ssl_certificate_validation:
+            urllib3.disable_warnings()
+            kwargs['cert_reqs'] = 'CERT_NONE'
+        elif ca_certs:
+            kwargs['cert_reqs'] = 'CERT_REQUIRED'
+            kwargs['ca_certs'] = ca_certs
+
+        if timeout:
+            kwargs['timeout'] = timeout
+
+        super(ClosingProxyHttp, self).__init__(proxy_url, **kwargs)
+
+    def request(self, url, method, *args, **kwargs):
+
+        class Response(dict):
+            def __init__(self, info):
+                for key, value in info.getheaders().items():
+                    self[key.lower()] = value
+                self.status = info.status
+                self['status'] = str(self.status)
+                self.reason = info.reason
+                self.version = info.version
+                self['content-location'] = url
+
+        original_headers = kwargs.get('headers', {})
+        new_headers = dict(original_headers, connection='close')
+        new_kwargs = dict(kwargs, headers=new_headers)
+
+        # Follow up to 5 redirections. Don't raise an exception if
+        # it's exceeded but return the HTTP 3XX response instead.
+        retry = urllib3.util.Retry(raise_on_redirect=False, redirect=5)
+        r = super(ClosingProxyHttp, self).request(method, url, retries=retry,
+                                                  *args, **new_kwargs)
+        return Response(r), r.data
+
+
 class ClosingHttp(urllib3.poolmanager.PoolManager):
     def __init__(self, disable_ssl_certificate_validation=False,
                  ca_certs=None, timeout=None):
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index f58d737..22276d4 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -69,6 +69,7 @@
                               of the request and response payload
     :param str http_timeout: Timeout in seconds to wait for the http request to
                              return
+    :param str proxy_url: http proxy url to use.
     """
 
     # The version of the API this client implements
@@ -80,7 +81,8 @@
                  endpoint_type='publicURL',
                  build_interval=1, build_timeout=60,
                  disable_ssl_certificate_validation=False, ca_certs=None,
-                 trace_requests='', name=None, http_timeout=None):
+                 trace_requests='', name=None, http_timeout=None,
+                 proxy_url=None):
         self.auth_provider = auth_provider
         self.service = service
         self.region = region
@@ -100,9 +102,16 @@
                                        'retry-after', 'server',
                                        'vary', 'www-authenticate'))
         dscv = disable_ssl_certificate_validation
-        self.http_obj = http.ClosingHttp(
-            disable_ssl_certificate_validation=dscv, ca_certs=ca_certs,
-            timeout=http_timeout)
+
+        if proxy_url:
+            self.http_obj = http.ClosingProxyHttp(
+                proxy_url,
+                disable_ssl_certificate_validation=dscv, ca_certs=ca_certs,
+                timeout=http_timeout)
+        else:
+            self.http_obj = http.ClosingHttp(
+                disable_ssl_certificate_validation=dscv, ca_certs=ca_certs,
+                timeout=http_timeout)
 
     def get_headers(self, accept_type=None, send_type=None):
         """Return the default headers which will be used with outgoing requests
diff --git a/tempest/lib/services/clients.py b/tempest/lib/services/clients.py
index 4fa7a7a..c564810 100644
--- a/tempest/lib/services/clients.py
+++ b/tempest/lib/services/clients.py
@@ -276,7 +276,7 @@
     @removals.removed_kwarg('client_parameters')
     def __init__(self, credentials, identity_uri, region=None, scope='project',
                  disable_ssl_certificate_validation=True, ca_certs=None,
-                 trace_requests='', client_parameters=None):
+                 trace_requests='', client_parameters=None, proxy_url=None):
         """Service Clients provider
 
         Instantiate a `ServiceClients` object, from a set of credentials and an
@@ -336,6 +336,8 @@
             name, as declared in `service_clients.available_modules()` except
             for the version. Values are dictionaries of parameters that are
             going to be passed to all clients in the service client module.
+        :param proxy_url: Applies to auth and to all service clients, set a
+            proxy url for the clients to use.
         """
         self._registered_services = set([])
         self.credentials = credentials
@@ -360,14 +362,18 @@
         self.dscv = disable_ssl_certificate_validation
         self.ca_certs = ca_certs
         self.trace_requests = trace_requests
+        self.proxy_url = proxy_url
         # Creates an auth provider for the credentials
         self.auth_provider = auth_provider_class(
             self.credentials, self.identity_uri, scope=scope,
             disable_ssl_certificate_validation=self.dscv,
-            ca_certs=self.ca_certs, trace_requests=self.trace_requests)
+            ca_certs=self.ca_certs, trace_requests=self.trace_requests,
+            proxy_url=proxy_url)
+
         # Setup some defaults for client parameters of registered services
         client_parameters = client_parameters or {}
         self.parameters = {}
+
         # Parameters are provided for unversioned services
         all_modules = available_modules() | _tempest_internal_modules()
         unversioned_services = set(
@@ -420,8 +426,8 @@
             clients in tempest.
         :param client_names: List or set of names of service client classes.
         :param kwargs: Extra optional parameters to be passed to all clients.
-            ServiceClient provides defaults for region, dscv, ca_certs and
-            trace_requests.
+            ServiceClient provides defaults for region, dscv, ca_certs, http
+            proxies and trace_requests.
         :raise ServiceClientRegistrationException: if the provided name is
             already in use or if service_version is already registered.
         :raise ImportError: if module_path cannot be imported.
@@ -442,7 +448,8 @@
         params = dict(region=self.region,
                       disable_ssl_certificate_validation=self.dscv,
                       ca_certs=self.ca_certs,
-                      trace_requests=self.trace_requests)
+                      trace_requests=self.trace_requests,
+                      proxy_url=self.proxy_url)
         params.update(kwargs)
         # Instantiate the client factory
         _factory = ClientsFactory(module_path=module_path,
diff --git a/tempest/lib/services/object_storage/container_client.py b/tempest/lib/services/object_storage/container_client.py
new file mode 100644
index 0000000..2da8e24
--- /dev/null
+++ b/tempest/lib/services/object_storage/container_client.py
@@ -0,0 +1,124 @@
+# Copyright 2012 OpenStack Foundation
+# 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 xml.etree import ElementTree as etree
+
+import debtcollector.moves
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class ContainerClient(rest_client.RestClient):
+
+    def update_container(self, container_name, **headers):
+        """Creates or Updates a container
+
+        with optional metadata passed in as a dictionary.
+        Full list of allowed headers or value, please refer to the
+        official API reference:
+        https://developer.openstack.org/api-ref/object-store/#create-container
+        """
+        url = str(container_name)
+
+        resp, body = self.put(url, body=None, headers=headers)
+        self.expected_success([201, 202], resp.status)
+        return resp, body
+
+    # NOTE: This alias is for the usability because PUT can be used for both
+    # updating/creating a resource and this PUT is mainly used for creating
+    # on Swift container API.
+    create_container = update_container
+
+    def delete_container(self, container_name):
+        """Deletes the container (if it's empty)."""
+        url = str(container_name)
+        resp, body = self.delete(url)
+        self.expected_success(204, resp.status)
+        return resp, body
+
+    def create_update_or_delete_container_metadata(
+            self, container_name,
+            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]
+
+        resp, body = self.post(url, headers=headers, body=None)
+        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):
+        """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_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.
+
+        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_name)
+        if params:
+            url += '?'
+            url += '&%s' % urllib.urlencode(params)
+
+        resp, body = self.get(url, headers={})
+        if params and params.get('format') == 'json':
+            body = json.loads(body)
+        elif params and params.get('format') == 'xml':
+            body = etree.fromstring(body)
+        # Else the content-type is plain/text
+        else:
+            body = [
+                obj_name for obj_name in body.decode().split('\n') if obj_name
+            ]
+
+        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/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index d13e449..79973ee 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from debtcollector import moves
 from debtcollector import removals
 from oslo_serialization import jsonutils as json
 import six
@@ -22,43 +21,12 @@
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.volume import base_client
-from tempest.lib.services.volume.v2 import transfers_client
 
 
 class VolumesClient(base_client.BaseClient):
     """Client class to send CRUD Volume V2 API requests"""
     api_version = "v2"
 
-    create_volume_transfer = moves.moved_function(
-        transfers_client.TransfersClient.create_volume_transfer,
-        'VolumesClient.create_volume_transfer', __name__,
-        message='Use create_volume_transfer from new location.',
-        version='Pike', removal_version='Queens')
-
-    show_volume_transfer = moves.moved_function(
-        transfers_client.TransfersClient.show_volume_transfer,
-        'VolumesClient.show_volume_transfer', __name__,
-        message='Use show_volume_transfer from new location.',
-        version='Pike', removal_version='Queens')
-
-    list_volume_transfers = moves.moved_function(
-        transfers_client.TransfersClient.list_volume_transfers,
-        'VolumesClient.list_volume_transfers', __name__,
-        message='Use list_volume_transfer from new location.',
-        version='Pike', removal_version='Queens')
-
-    delete_volume_transfer = moves.moved_function(
-        transfers_client.TransfersClient.delete_volume_transfer,
-        'VolumesClient.delete_volume_transfer', __name__,
-        message='Use delete_volume_transfer from new location.',
-        version='Pike', removal_version='Queens')
-
-    accept_volume_transfer = moves.moved_function(
-        transfers_client.TransfersClient.accept_volume_transfer,
-        'VolumesClient.accept_volume_transfer', __name__,
-        message='Use accept_volume_transfer from new location.',
-        version='Pike', removal_version='Queens')
-
     def _prepare_params(self, params):
         """Prepares params for use in get or _ext_get methods.
 
diff --git a/tempest/lib/services/volume/v3/group_snapshots_client.py b/tempest/lib/services/volume/v3/group_snapshots_client.py
index e644f02..6e53e3e 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)
@@ -75,6 +77,18 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    def reset_group_snapshot_status(self, group_snapshot_id, status_to_set):
+        """Resets group snapshot status.
+
+        For more information, please refer to the official API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#reset-group-snapshot-status
+        """
+        post_body = json.dumps({'reset_status': {'status': status_to_set}})
+        resp, body = self.post('group_snapshots/%s/action' % group_snapshot_id,
+                               post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
     def is_resource_deleted(self, id):
         try:
             self.show_group_snapshot(id)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 867c0fc..26e1451 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -271,10 +271,8 @@
         if backend_name:
             extra_specs = {"volume_backend_name": backend_name}
 
-        body = client.create_volume_type(name=randomized_name,
-                                         extra_specs=extra_specs)
-        volume_type = body['volume_type']
-        self.assertIn('id', volume_type)
+        volume_type = client.create_volume_type(
+            name=randomized_name, extra_specs=extra_specs)['volume_type']
         self.addCleanup(client.delete_volume_type, volume_type['id'])
         return volume_type
 
@@ -506,27 +504,6 @@
         waiters.wait_for_volume_resource_status(self.volumes_client,
                                                 volume['id'], 'available')
 
-        volume = self.volumes_client.show_volume(volume['id'])['volume']
-        self.assertEqual('available', volume['status'])
-
-    def rebuild_server(self, server_id, image=None,
-                       preserve_ephemeral=False, wait=True,
-                       rebuild_kwargs=None):
-        if image is None:
-            image = CONF.compute.image_ref
-
-        rebuild_kwargs = rebuild_kwargs or {}
-
-        LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
-                  server_id, image, preserve_ephemeral)
-        self.servers_client.rebuild_server(
-            server_id=server_id, image_ref=image,
-            preserve_ephemeral=preserve_ephemeral,
-            **rebuild_kwargs)
-        if wait:
-            waiters.wait_for_server_status(self.servers_client,
-                                           server_id, 'ACTIVE')
-
     def ping_ip_address(self, ip_address, should_succeed=True,
                         ping_timeout=None, mtu=None):
         timeout = ping_timeout or CONF.validation.ping_timeout
@@ -909,16 +886,13 @@
             self._log_net_info(e)
             raise
 
-    def _check_remote_connectivity(self, source, dest, should_succeed=True,
-                                   nic=None):
+    def check_remote_connectivity(self, source, dest, should_succeed=True,
+                                  nic=None):
         """assert ping server via source ssh connection
 
-        Note: This is an internal method.  Use check_remote_connectivity
-        instead.
-
         :param source: RemoteClient: an ssh connection from which to ping
-        :param dest: and IP to ping against
-        :param should_succeed: boolean should ping succeed or not
+        :param dest: an IP to ping against
+        :param should_succeed: boolean: should ping succeed or not
         :param nic: specific network interface to ping from
         """
         def ping_remote():
@@ -930,21 +904,8 @@
                 return not should_succeed
             return should_succeed
 
-        return test_utils.call_until_true(ping_remote,
-                                          CONF.validation.ping_timeout,
-                                          1)
-
-    def check_remote_connectivity(self, source, dest, should_succeed=True,
-                                  nic=None):
-        """assert ping server via source ssh connection
-
-        :param source: RemoteClient: an ssh connection from which to ping
-        :param dest: and IP to ping against
-        :param should_succeed: boolean should ping succeed or not
-        :param nic: specific network interface to ping from
-        """
-        result = self._check_remote_connectivity(source, dest, should_succeed,
-                                                 nic)
+        result = test_utils.call_until_true(ping_remote,
+                                            CONF.validation.ping_timeout, 1)
         source_host = source.ssh_client.host
         if should_succeed:
             msg = "Timed out waiting for %s to become reachable from %s" \
@@ -1008,23 +969,6 @@
                         client.delete_security_group, secgroup['id'])
         return secgroup
 
-    def _default_security_group(self, client=None, tenant_id=None):
-        """Get default secgroup for given tenant_id.
-
-        :returns: default secgroup for given tenant
-        """
-        if client is None:
-            client = self.security_groups_client
-        if not tenant_id:
-            tenant_id = client.tenant_id
-        sgs = [
-            sg for sg in list(client.list_security_groups().values())[0]
-            if sg['tenant_id'] == tenant_id and sg['name'] == 'default'
-        ]
-        msg = "No default security group for tenant %s." % (tenant_id)
-        self.assertNotEmpty(sgs, msg)
-        return sgs[0]
-
     def _create_security_group_rule(self, secgroup=None,
                                     sec_group_rules_client=None,
                                     tenant_id=None,
@@ -1053,8 +997,12 @@
         if not tenant_id:
             tenant_id = security_groups_client.tenant_id
         if secgroup is None:
-            secgroup = self._default_security_group(
-                client=security_groups_client, tenant_id=tenant_id)
+            # Get default secgroup for tenant_id
+            default_secgroups = security_groups_client.list_security_groups(
+                name='default', tenant_id=tenant_id)['security_groups']
+            msg = "No default security group for tenant %s." % (tenant_id)
+            self.assertNotEmpty(default_secgroups, msg)
+            secgroup = default_secgroups[0]
 
         ruleset = dict(security_group_id=secgroup['id'],
                        tenant_id=secgroup['tenant_id'])
@@ -1296,7 +1244,7 @@
     def create_container(self, container_name=None):
         name = container_name or data_utils.rand_name(
             'swift-scenario-container')
-        self.container_client.create_container(name)
+        self.container_client.update_container(name)
         # look for the container to assure it is created
         self.list_and_check_container_objects(name)
         LOG.debug('Container %s created', name)
@@ -1333,7 +1281,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:
@@ -1342,14 +1290,6 @@
             for obj in not_present_obj:
                 self.assertNotIn(obj, object_list)
 
-    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)
-        resp, _ = self.container_client.list_container_metadata(container_name)
-        self.assertEqual(resp['x-container-read'], acl)
-
     def download_and_verify(self, container_name, obj_name, expected_data):
         _, obj = self.object_client.get_object(container_name, obj_name)
         self.assertEqual(obj, expected_data)
diff --git a/tempest/scenario/test_object_storage_basic_ops.py b/tempest/scenario/test_object_storage_basic_ops.py
index da0b1e8..cbe321e 100644
--- a/tempest/scenario/test_object_storage_basic_ops.py
+++ b/tempest/scenario/test_object_storage_basic_ops.py
@@ -58,12 +58,18 @@
         5. Delete the object and container
         """
         container_name = self.create_container()
-        obj_name, _ = self.upload_object_to_container(container_name)
+        obj_name, obj_data = self.upload_object_to_container(container_name)
         obj_url = '%s/%s/%s' % (self.object_client.base_url,
                                 container_name, obj_name)
         resp, _ = self.object_client.raw_request(obj_url, 'GET')
         self.assertEqual(resp.status, 401)
-
-        self.change_container_acl(container_name, '.r:*')
-        resp, _ = self.object_client.raw_request(obj_url, 'GET')
+        metadata_param = {'X-Container-Read': '.r:*'}
+        self.container_client.create_update_or_delete_container_metadata(
+            container_name, create_update_metadata=metadata_param,
+            create_update_metadata_prefix='')
+        resp, _ = self.container_client.list_container_metadata(container_name)
+        self.assertEqual(metadata_param['X-Container-Read'],
+                         resp['x-container-read'])
+        resp, data = self.object_client.raw_request(obj_url, 'GET')
         self.assertEqual(resp.status, 200)
+        self.assertEqual(obj_data, data)
diff --git a/tempest/services/object_storage/__init__.py b/tempest/services/object_storage/__init__.py
index 771ed8f..166e475 100644
--- a/tempest/services/object_storage/__init__.py
+++ b/tempest/services/object_storage/__init__.py
@@ -17,7 +17,8 @@
     BulkMiddlewareClient
 from tempest.lib.services.object_storage.capabilities_client import \
     CapabilitiesClient
-from tempest.services.object_storage.container_client import ContainerClient
+from tempest.lib.services.object_storage.container_client import \
+    ContainerClient
 from tempest.services.object_storage.object_client import ObjectClient
 
 __all__ = ['AccountClient', 'BulkMiddlewareClient', 'CapabilitiesClient',
diff --git a/tempest/services/object_storage/container_client.py b/tempest/services/object_storage/container_client.py
deleted file mode 100644
index afedd36..0000000
--- a/tempest/services/object_storage/container_client.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-# 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 xml.etree import ElementTree as etree
-
-from oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
-
-
-class ContainerClient(rest_client.RestClient):
-
-    def create_container(
-            self, container_name,
-            metadata=None,
-            remove_metadata=None,
-            metadata_prefix='X-Container-Meta-',
-            remove_metadata_prefix='X-Remove-Container-Meta-'):
-        """Creates a container
-
-        with optional metadata passed in as a dictionary
-        """
-        url = str(container_name)
-        headers = {}
-
-        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.put(url, body=None, headers=headers)
-        self.expected_success([201, 202], resp.status)
-        return resp, body
-
-    def delete_container(self, container_name):
-        """Deletes the container (if it's empty)."""
-        url = str(container_name)
-        resp, body = self.delete(url)
-        self.expected_success(204, resp.status)
-        return resp, body
-
-    def update_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."""
-        url = str(container_name)
-        headers = {}
-
-        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)
-        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
-
-    def list_container_metadata(self, container_name):
-        """Retrieves container metadata headers"""
-        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):
-        """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.
-
-           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).
-        """
-
-        url = str(container)
-        if params:
-            url += '?'
-            url += '&%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url, headers={})
-        if params and params.get('format') == 'json':
-            body = json.loads(body)
-        elif params and params.get('format') == 'xml':
-            body = etree.fromstring(body)
-        # Else the content-type is plain/text
-        else:
-            body = [
-                obj_name for obj_name in body.decode().split('\n') if obj_name
-            ]
-
-        self.expected_success([200, 204], resp.status)
-        return resp, body
diff --git a/tempest/tests/cmd/test_run.py b/tempest/tests/cmd/test_run.py
index 6e1250f..0485e14 100644
--- a/tempest/tests/cmd/test_run.py
+++ b/tempest/tests/cmd/test_run.py
@@ -40,6 +40,7 @@
         setattr(args, "subunit", True)
         setattr(args, "parallel", False)
         setattr(args, "concurrency", 10)
+        setattr(args, "load_list", '')
         options = self.run_cmd._build_options(args)
         self.assertEqual(['--subunit',
                           '--concurrency=10'],
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..c2784b2 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,
@@ -139,3 +161,12 @@
             {},
             group_snapshot_id='0e701ab8-1bec-4b9f-b026-a7ba4af13578',
             status=202)
+
+    def test_reset_group_snapshot_status(self):
+        self.check_service_client_function(
+            self.client.reset_group_snapshot_status,
+            'tempest.lib.common.rest_client.RestClient.post',
+            {},
+            status=202,
+            group_snapshot_id='0e701ab8-1bec-4b9f-b026-a7ba4af13578',
+            status_to_set='error')
diff --git a/tempest/tests/test_list_tests.py b/tempest/tests/test_list_tests.py
index a238879..4af7463 100644
--- a/tempest/tests/test_list_tests.py
+++ b/tempest/tests/test_list_tests.py
@@ -23,12 +23,10 @@
 
 class TestTestList(base.TestCase):
 
-    def test_testr_list_tests_no_errors(self):
-        # Remove unit test discover path from env to test tempest tests
+    def test_stestr_list_no_errors(self):
         test_env = os.environ.copy()
-        test_env.pop('OS_TEST_PATH')
         import_failures = []
-        p = subprocess.Popen(['testr', 'list-tests'], stdout=subprocess.PIPE,
+        p = subprocess.Popen(['stestr', 'list'], stdout=subprocess.PIPE,
                              env=test_env)
         ids, err = p.communicate()
         self.assertEqual(0, p.returncode,
diff --git a/tox.ini b/tox.ini
index 7bdc580..21696eb 100644
--- a/tox.ini
+++ b/tox.ini
@@ -16,12 +16,11 @@
 [testenv]
 setenv =
     VIRTUAL_ENV={envdir}
-    OS_TEST_PATH=./tempest/tests
     OS_LOG_CAPTURE=1
     PYTHONWARNINGS=default::DeprecationWarning
     BRANCH_NAME=master
     CLIENT_NAME=tempest
-passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH OS_TEST_PATH TEMPEST_CONFIG TEMPEST_CONFIG_DIR http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY ZUUL_CACHE_DIR REQUIREMENTS_PIP_LOCATION GENERATE_TEMPEST_PLUGIN_LIST
+passenv = OS_STDOUT_CAPTURE OS_STDERR_CAPTURE OS_TEST_TIMEOUT OS_TEST_LOCK_PATH TEMPEST_CONFIG TEMPEST_CONFIG_DIR http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY ZUUL_CACHE_DIR REQUIREMENTS_PIP_LOCATION GENERATE_TEMPEST_PLUGIN_LIST
 usedevelop = True
 install_command =
     {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
@@ -31,7 +30,7 @@
     -r{toxinidir}/test-requirements.txt
 commands =
     find . -type f -name "*.pyc" -delete
-    ostestr {posargs}
+    stestr --test-path ./tempest/tests run {posargs}
 
 [testenv:genconfig]
 commands = oslo-config-generator --config-file tempest/cmd/config-generator.tempest.conf