Merge "Cap compute floating IPs tests"
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/plugin.rst b/doc/source/plugin.rst
index 77ef9ed..2afb1e5 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -29,6 +29,8 @@
 * tempest.config
 * tempest.test_discover.plugins
 * tempest.common.credentials_factory
+* tempest.clients
+* tempest.test
 
 If there is an interface from tempest that you need to rely on in your plugin
 which is not listed above, it likely needs to be migrated to tempest.lib. In
diff --git a/releasenotes/notes/add-domain-param-in-cliclient-a270fcf35c8f09e6.yaml b/releasenotes/notes/add-domain-param-in-cliclient-a270fcf35c8f09e6.yaml
new file mode 100644
index 0000000..87a6af9
--- /dev/null
+++ b/releasenotes/notes/add-domain-param-in-cliclient-a270fcf35c8f09e6.yaml
@@ -0,0 +1,17 @@
+---
+fixes:
+  - |
+    Allow to specify new domain parameters:
+
+    * `user_domain_name`
+    * `user_domain_id`
+    * `project_domain_name`
+    * `project_domain_id`
+
+    for CLIClient class, whose values will be substituted to
+    ``--os-user-domain-name``, ``--os-user-domain-id``,
+    ``--os-project-domain-name`` and ``--os-project-domain-id`` respectively
+    during command execution.
+
+    This allows to prevent possible test failures with authentication in
+    Keystone v3. Bug: #1719687
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_proxy_url_get_credentials-aef66b085450513f.yaml b/releasenotes/notes/add_proxy_url_get_credentials-aef66b085450513f.yaml
new file mode 100644
index 0000000..94ab462
--- /dev/null
+++ b/releasenotes/notes/add_proxy_url_get_credentials-aef66b085450513f.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Add the proxy_url optional parameter to the get_credentials method in
+    tempest/lib/auth.py so that that helper can be used when going through
+    and HTTP proxy.
diff --git a/releasenotes/notes/disable-identity-v2-testing-4ef1565d1a5aedcf.yaml b/releasenotes/notes/disable-identity-v2-testing-4ef1565d1a5aedcf.yaml
new file mode 100644
index 0000000..e5d4ab7
--- /dev/null
+++ b/releasenotes/notes/disable-identity-v2-testing-4ef1565d1a5aedcf.yaml
@@ -0,0 +1,7 @@
+---
+upgrade:
+  - |
+    As of the Queens release, tempest no longer tests the identity v2.0 API
+    because the majority of the v2.0 API have been removed from the identity
+    project. Once the Queens release reaches end-of-life, we can remove the
+    v2.0 tempest tests and clean up v2.0 testing cruft.
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-object-storage-client-as-stable-interface-d1b07c7e8f17bef6.yaml
similarity index 84%
rename from releasenotes/notes/make-account-client-as-stable-interface-d1b07c7e8f17bef6.yaml
rename to releasenotes/notes/make-object-storage-client-as-stable-interface-d1b07c7e8f17bef6.yaml
index 9d5a1f5..2bba952 100644
--- a/releasenotes/notes/make-account-client-as-stable-interface-d1b07c7e8f17bef6.yaml
+++ b/releasenotes/notes/make-object-storage-client-as-stable-interface-d1b07c7e8f17bef6.yaml
@@ -7,3 +7,5 @@
     without any maintenance changes.
 
       * account_client
+      * container_client
+      * object_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/releasenotes/notes/test-clients-stable-for-plugin-90b1e7dc83f28ccd.yaml b/releasenotes/notes/test-clients-stable-for-plugin-90b1e7dc83f28ccd.yaml
new file mode 100644
index 0000000..e27ee33
--- /dev/null
+++ b/releasenotes/notes/test-clients-stable-for-plugin-90b1e7dc83f28ccd.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Two extra modules are now marked as stable for plugins, test.py and clients.py.
+    The former includes the test base class with its automatic credentials
+    provisioning and test resource managing fixtures.
+    The latter is built on top of ServiceClients and it adds aliases and a few custom
+    configurations to it.
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 5c4767c..705814c 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -301,7 +301,7 @@
         return image
 
     @classmethod
-    def rebuild_server(cls, server_id, validatable=False, **kwargs):
+    def recreate_server(cls, server_id, validatable=False, **kwargs):
         """Destroy an existing class level server and creates a new one
 
         Some test classes use a test server that can be used by multiple
@@ -422,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.
 
@@ -449,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/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index 4de00ce..a2e58c9 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -52,7 +52,7 @@
             self._reset_server()
 
     def _reset_server(self):
-        self.__class__.server_id = self.rebuild_server(self.server_id)
+        self.__class__.server_id = self.recreate_server(self.server_id)
 
     @classmethod
     def skip_checks(cls):
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 4cfc665..6fe4d82 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -55,7 +55,7 @@
             self.__class__.server_id = server['id']
         except Exception:
             # Rebuild server if something happened to it during a test
-            self.__class__.server_id = self.rebuild_server(
+            self.__class__.server_id = self.recreate_server(
                 self.server_id, validatable=True)
 
     def tearDown(self):
@@ -75,7 +75,7 @@
     @classmethod
     def resource_setup(cls):
         super(ServerActionsTestJSON, cls).resource_setup()
-        cls.server_id = cls.rebuild_server(None, validatable=True)
+        cls.server_id = cls.recreate_server(None, validatable=True)
 
     @decorators.idempotent_id('6158df09-4b82-4ab3-af6d-29cf36af858d')
     @testtools.skipUnless(CONF.compute_feature_enabled.change_password,
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/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 8170b28..d067bb3 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -37,7 +37,7 @@
             waiters.wait_for_server_status(self.client, self.server_id,
                                            'ACTIVE')
         except Exception:
-            self.__class__.server_id = self.rebuild_server(self.server_id)
+            self.__class__.server_id = self.recreate_server(self.server_id)
 
     def tearDown(self):
         self.server_check_teardown()
@@ -551,7 +551,7 @@
             waiters.wait_for_server_status(self.servers_client, self.server_id,
                                            'ACTIVE')
         except Exception:
-            self.__class__.server_id = self.rebuild_server(self.server_id)
+            self.__class__.server_id = self.recreate_server(self.server_id)
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/identity/admin/v3/test_oauth_consumers.py b/tempest/api/identity/admin/v3/test_oauth_consumers.py
index 970ead3..062cce5 100644
--- a/tempest/api/identity/admin/v3/test_oauth_consumers.py
+++ b/tempest/api/identity/admin/v3/test_oauth_consumers.py
@@ -17,7 +17,7 @@
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
-from tempest.lib import exceptions as exceptions
+from tempest.lib import exceptions
 
 
 class OAUTHConsumersV3Test(base.BaseIdentityV3AdminTest):
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/test_object_expiry.py b/tempest/api/object_storage/test_object_expiry.py
index ed1be90..86f7c8c 100644
--- a/tempest/api/object_storage/test_object_expiry.py
+++ b/tempest/api/object_storage/test_object_expiry.py
@@ -40,10 +40,10 @@
     def _test_object_expiry(self, metadata):
         # update object metadata
         resp, _ = \
-            self.object_client.update_object_metadata(self.container_name,
-                                                      self.object_name,
-                                                      metadata,
-                                                      metadata_prefix='')
+            self.object_client.create_or_update_object_metadata(
+                self.container_name,
+                self.object_name,
+                headers=metadata)
         # verify object metadata
         resp, _ = \
             self.object_client.list_object_metadata(self.container_name,
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 61cbe30..acb578d 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -48,8 +48,9 @@
         data_segments = [data + str(i) for i in range(segments)]
         # uploading segments
         for i in range(segments):
-            self.object_client.create_object_segments(
-                self.container_name, object_name, i, data_segments[i])
+            obj_name = "%s/%s" % (object_name, i)
+            self.object_client.create_object(
+                self.container_name, obj_name, data_segments[i])
 
         return object_name, data_segments
 
@@ -184,12 +185,15 @@
         # create object with transfer_encoding
         object_name = data_utils.rand_name(name='TestObject')
         data = data_utils.random_bytes(1024)
-        _, _, resp_headers = self.object_client.put_object_with_chunk(
-            container=self.container_name,
-            name=object_name,
-            contents=data_utils.chunkify(data, 512)
-        )
-        self.assertHeaders(resp_headers, 'Object', 'PUT')
+        headers = {'Transfer-Encoding': 'chunked'}
+        resp, _ = self.object_client.create_object(
+            self.container_name,
+            object_name,
+            data=data_utils.chunkify(data, 512),
+            headers=headers,
+            chunked=True)
+
+        self.assertHeaders(resp, 'Object', 'PUT')
 
         # check uploaded content
         _, body = self.object_client.get_object(self.container_name,
@@ -325,11 +329,10 @@
         object_name, _ = self.create_object(self.container_name)
 
         metadata = {'X-Object-Meta-test-meta': 'Meta'}
-        resp, _ = self.object_client.update_object_metadata(
+        resp, _ = self.object_client.create_or_update_object_metadata(
             self.container_name,
             object_name,
-            metadata,
-            metadata_prefix='')
+            headers=metadata)
         self.assertHeaders(resp, 'Object', 'POST')
 
         resp, _ = self.object_client.list_object_metadata(
@@ -350,11 +353,10 @@
                                          metadata=create_metadata)
 
         update_metadata = {'X-Remove-Object-Meta-test-meta1': 'Meta1'}
-        resp, _ = self.object_client.update_object_metadata(
+        resp, _ = self.object_client.create_or_update_object_metadata(
             self.container_name,
             object_name,
-            update_metadata,
-            metadata_prefix='')
+            headers=update_metadata)
         self.assertHeaders(resp, 'Object', 'POST')
 
         resp, _ = self.object_client.list_object_metadata(
@@ -375,11 +377,10 @@
 
         update_metadata = {'X-Object-Meta-test-meta2': 'Meta2',
                            'X-Remove-Object-Meta-test-meta1': 'Meta1'}
-        resp, _ = self.object_client.update_object_metadata(
+        resp, _ = self.object_client.create_or_update_object_metadata(
             self.container_name,
             object_name,
-            update_metadata,
-            metadata_prefix='')
+            headers=update_metadata)
         self.assertHeaders(resp, 'Object', 'POST')
 
         resp, _ = self.object_client.list_object_metadata(
@@ -403,11 +404,10 @@
                                          metadata=None)
         object_prefix = '%s/%s' % (self.container_name, object_name)
         update_metadata = {'X-Object-Manifest': object_prefix}
-        resp, _ = self.object_client.update_object_metadata(
+        resp, _ = self.object_client.create_or_update_object_metadata(
             self.container_name,
             object_name,
-            update_metadata,
-            metadata_prefix='')
+            headers=update_metadata)
         self.assertHeaders(resp, 'Object', 'POST')
 
         resp, _ = self.object_client.list_object_metadata(
@@ -422,11 +422,10 @@
         object_name, _ = self.create_object(self.container_name)
 
         update_metadata = {'X-Object-Meta-test-meta': ''}
-        resp, _ = self.object_client.update_object_metadata(
+        resp, _ = self.object_client.create_or_update_object_metadata(
             self.container_name,
             object_name,
-            update_metadata,
-            metadata_prefix='')
+            headers=update_metadata)
         self.assertHeaders(resp, 'Object', 'POST')
 
         resp, _ = self.object_client.list_object_metadata(
@@ -447,11 +446,10 @@
                                          metadata=create_metadata)
 
         update_metadata = {'X-Remove-Object-Meta-test-meta': ''}
-        resp, _ = self.object_client.update_object_metadata(
+        resp, _ = self.object_client.create_or_update_object_metadata(
             self.container_name,
             object_name,
-            update_metadata,
-            metadata_prefix='')
+            headers=update_metadata)
         self.assertHeaders(resp, 'Object', 'POST')
 
         resp, _ = self.object_client.list_object_metadata(
@@ -728,8 +726,13 @@
                                                    dst_object_name,
                                                    dst_data)
         # copy source object to destination
-        resp, _ = self.object_client.copy_object_in_same_container(
-            self.container_name, src_object_name, dst_object_name)
+        headers = {}
+        headers['X-Copy-From'] = "%s/%s" % (str(self.container_name),
+                                            str(src_object_name))
+        resp, body = self.object_client.create_object(self.container_name,
+                                                      dst_object_name,
+                                                      data=None,
+                                                      headers=headers)
         self.assertHeaders(resp, 'Object', 'PUT')
 
         # check data
@@ -749,8 +752,14 @@
         # change the content type of the object
         metadata = {'content-type': 'text/plain; charset=UTF-8'}
         self.assertNotEqual(resp_tmp['content-type'], metadata['content-type'])
-        resp, _ = self.object_client.copy_object_in_same_container(
-            self.container_name, object_name, object_name, metadata)
+        headers = {}
+        headers['X-Copy-From'] = "%s/%s" % (str(self.container_name),
+                                            str(object_name))
+        resp, body = self.object_client.create_object(self.container_name,
+                                                      object_name,
+                                                      data=None,
+                                                      metadata=metadata,
+                                                      headers=headers)
         self.assertHeaders(resp, 'Object', 'PUT')
 
         # check the content type
@@ -801,16 +810,21 @@
         # set object metadata
         meta_key = data_utils.rand_name(name='test')
         meta_value = data_utils.rand_name(name='MetaValue')
-        orig_metadata = {meta_key: meta_value}
-        resp, _ = self.object_client.update_object_metadata(src_container_name,
-                                                            object_name,
-                                                            orig_metadata)
+        orig_metadata = {'X-Object-Meta-' + meta_key: meta_value}
+        resp, _ = self.object_client.create_or_update_object_metadata(
+            src_container_name,
+            object_name,
+            headers=orig_metadata)
         self.assertHeaders(resp, 'Object', 'POST')
 
         # copy object from source container to destination container
-        resp, _ = self.object_client.copy_object_across_containers(
-            src_container_name, object_name, dst_container_name,
-            object_name)
+        headers = {}
+        headers['X-Copy-From'] = "%s/%s" % (str(src_container_name),
+                                            str(object_name))
+        resp, body = self.object_client.create_object(dst_container_name,
+                                                      object_name,
+                                                      data=None,
+                                                      headers=headers)
         self.assertHeaders(resp, 'Object', 'PUT')
 
         # check if object is present in destination container
@@ -897,8 +911,9 @@
         data_segments = [data + str(i) for i in range(segments)]
         # uploading segments
         for i in range(segments):
-            resp, _ = self.object_client.create_object_segments(
-                self.container_name, object_name, i, data_segments[i])
+            obj_name = "%s/%s" % (object_name, i)
+            resp, _ = self.object_client.create_object(
+                self.container_name, obj_name, data_segments[i])
         # creating a manifest file
         metadata = {'X-Object-Manifest': '%s/%s/'
                     % (self.container_name, object_name)}
@@ -906,8 +921,8 @@
                                                    object_name, data='')
         self.assertHeaders(resp, 'Object', 'PUT')
 
-        resp, _ = self.object_client.update_object_metadata(
-            self.container_name, object_name, metadata, metadata_prefix='')
+        resp, _ = self.object_client.create_or_update_object_metadata(
+            self.container_name, object_name, headers=metadata)
         self.assertHeaders(resp, 'Object', 'POST')
 
         resp, _ = self.object_client.list_object_metadata(
diff --git a/tempest/api/volume/admin/test_groups.py b/tempest/api/volume/admin/test_groups.py
index 68d355c..6b53d85 100644
--- a/tempest/api/volume/admin/test_groups.py
+++ b/tempest/api/volume/admin/test_groups.py
@@ -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:
@@ -299,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))
 
diff --git a/tempest/clients.py b/tempest/clients.py
index e617c3c..ca205c8 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -17,7 +17,6 @@
 from tempest.lib import auth
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services import clients
-from tempest.services import object_storage
 
 CONF = config.CONF
 
@@ -25,8 +24,6 @@
 class Manager(clients.ServiceClients):
     """Top level manager for OpenStack tempest clients"""
 
-    default_params = config.service_client_config()
-
     def __init__(self, credentials, scope='project'):
         """Initialization of Manager class.
 
@@ -47,6 +44,10 @@
         self._set_object_storage_clients()
         self._set_image_clients()
         self._set_network_clients()
+        # TODO(andreaf) This is maintained for backward compatibility
+        # with plugins, but it should removed eventually, since it was
+        # never a stable interface and it's not useful anyways
+        self.default_params = config.service_client_config()
 
     def _set_network_clients(self):
         self.network_agents_client = self.network.AgentsClient()
@@ -281,21 +282,11 @@
             self.snapshots_client_latest = self.snapshots_v3_client
 
     def _set_object_storage_clients(self):
-        # NOTE(andreaf) Load configuration from config. Once object storage
-        # is in lib, configuration will be pulled directly from the registry
-        # and this will not be required anymore.
-        params = config.service_client_config('object-storage')
-
-        self.account_client = object_storage.AccountClient(self.auth_provider,
-                                                           **params)
-        self.bulk_client = object_storage.BulkMiddlewareClient(
-            self.auth_provider, **params)
-        self.capabilities_client = object_storage.CapabilitiesClient(
-            self.auth_provider, **params)
-        self.container_client = object_storage.ContainerClient(
-            self.auth_provider, **params)
-        self.object_client = object_storage.ObjectClient(self.auth_provider,
-                                                         **params)
+        self.account_client = self.object_storage.AccountClient()
+        self.bulk_client = self.object_storage.BulkMiddlewareClient()
+        self.capabilities_client = self.object_storage.CapabilitiesClient()
+        self.container_client = self.object_storage.ContainerClient()
+        self.object_client = self.object_storage.ObjectClient()
 
 
 def get_auth_provider_class(credentials):
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index a72493d..fdf28d5 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -76,7 +76,6 @@
 from tempest import config
 import tempest.lib.common.http
 from tempest.lib import exceptions as lib_exc
-from tempest.services import object_storage
 
 
 CONF = config.CONF
@@ -197,10 +196,6 @@
 def verify_keystone_api_versions(os, update):
     # Check keystone api versions
     versions = _get_api_versions(os, 'keystone')
-    if (CONF.identity_feature_enabled.api_v2 !=
-            contains_version('v2.', versions)):
-        print_and_or_update('api_v2', 'identity-feature-enabled',
-                            not CONF.identity_feature_enabled.api_v2, update)
     if (CONF.identity_feature_enabled.api_v3 !=
             contains_version('v3.', versions)):
         print_and_or_update('api_v3', 'identity-feature-enabled',
@@ -236,11 +231,10 @@
 
 
 def get_extension_client(os, service):
-    params = config.service_client_config('object-storage')
     extensions_client = {
         'nova': os.compute.ExtensionsClient(),
         'neutron': os.network.ExtensionsClient(),
-        'swift': object_storage.CapabilitiesClient(os.auth_provider, **params),
+        'swift': os.object_storage.CapabilitiesClient(),
         # NOTE: Cinder v3 API is current and v2 and v1 are deprecated.
         # V3 extension API is the same as v2, so we reuse the v2 client
         # for v3 API also.
diff --git a/tempest/config.py b/tempest/config.py
index 024a638..0743220 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',
@@ -205,8 +207,14 @@
                 help='Does the identity service have delegation and '
                      'impersonation enabled'),
     cfg.BoolOpt('api_v2',
-                default=True,
-                help='Is the v2 identity API enabled'),
+                default=False,
+                help='Is the v2 identity API enabled',
+                deprecated_for_removal=True,
+                deprecated_reason='The identity v2.0 API was removed in the '
+                                  'Queens release. Tests that exercise the '
+                                  'v2.0 API will be removed from tempest in '
+                                  'the v22.0.0 release. They are kept only to '
+                                  'test stable branches.'),
     cfg.BoolOpt('api_v2_admin',
                 default=True,
                 help="Is the v2 identity admin API available? This setting "
@@ -1308,6 +1316,7 @@
         * `ca_certs`
         * `trace_requests`
         * `http_timeout`
+        * `proxy_url`
 
     The dict returned by this does not fit a few service clients:
 
@@ -1330,7 +1339,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:
@@ -1384,7 +1394,7 @@
         module = service_clients[service_client]
         configs = service_client.split('.')[0]
         service_client_data = dict(
-            name=service_client.replace('.', '_'),
+            name=service_client.replace('.', '_').replace('-', '_'),
             service_version=service_client,
             module_path=module.__name__,
             client_names=module.__all__,
diff --git a/tempest/lib/auth.py b/tempest/lib/auth.py
index ab4308f..2dd9d00 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
@@ -599,7 +600,8 @@
 
 def get_credentials(auth_url, fill_in=True, identity_version='v2',
                     disable_ssl_certificate_validation=None, ca_certs=None,
-                    trace_requests=None, http_timeout=None, **kwargs):
+                    trace_requests=None, http_timeout=None, proxy_url=None,
+                    **kwargs):
     """Builds a credentials object based on the configured auth_version
 
     :param auth_url (string): Full URI of the OpenStack Identity API(Keystone)
@@ -617,6 +619,7 @@
     :param trace_requests: trace in log API requests to the auth system
     :param http_timeout: timeout in seconds to wait for the http request to
            return
+    :param proxy_url: URL of HTTP(s) proxy used when fill_in is True
     :param kwargs (dict): Dict of credential key/value pairs
 
     Examples:
@@ -641,7 +644,7 @@
         auth_provider = auth_provider_class(
             creds, auth_url, disable_ssl_certificate_validation=dscv,
             ca_certs=ca_certs, trace_requests=trace_requests,
-            http_timeout=http_timeout)
+            http_timeout=http_timeout, proxy_url=proxy_url)
         creds = auth_provider.fill_credentials()
     return creds
 
diff --git a/tempest/lib/cli/base.py b/tempest/lib/cli/base.py
index 5468a7b..f39ecbc 100644
--- a/tempest/lib/cli/base.py
+++ b/tempest/lib/cli/base.py
@@ -93,10 +93,20 @@
     :type insecure: boolean
     :param prefix: prefix to insert before commands
     :type prefix: string
+    :param user_domain_name: User's domain name
+    :type user_domain_name: string
+    :param user_domain_id: User's domain ID
+    :type user_domain_id: string
+    :param project_domain_name: Project's domain name
+    :type project_domain_name: string
+    :param project_domain_id: Project's domain ID
+    :type project_domain_id: string
     """
 
     def __init__(self, username='', password='', tenant_name='', uri='',
-                 cli_dir='', insecure=False, prefix='', *args, **kwargs):
+                 cli_dir='', insecure=False, prefix='', user_domain_name=None,
+                 user_domain_id=None, project_domain_name=None,
+                 project_domain_id=None, *args, **kwargs):
         """Initialize a new CLIClient object."""
         super(CLIClient, self).__init__()
         self.cli_dir = cli_dir if cli_dir else '/usr/bin'
@@ -106,6 +116,10 @@
         self.uri = uri
         self.insecure = insecure
         self.prefix = prefix
+        self.user_domain_name = user_domain_name
+        self.user_domain_id = user_domain_id
+        self.project_domain_name = project_domain_name
+        self.project_domain_id = project_domain_id
 
     def nova(self, action, flags='', params='', fail_ok=False,
              endpoint_type='publicURL', merge_stderr=False):
@@ -366,6 +380,14 @@
                   self.tenant_name,
                   self.password,
                   self.uri))
+        if self.user_domain_name is not None:
+            creds += ' --os-user-domain-name %s' % self.user_domain_name
+        if self.user_domain_id is not None:
+            creds += ' --os-user-domain-id %s' % self.user_domain_id
+        if self.project_domain_name is not None:
+            creds += ' --os-project-domain-name %s' % self.project_domain_name
+        if self.project_domain_id is not None:
+            creds += ' --os-project-domain-id %s' % self.project_domain_id
         if self.insecure:
             flags = creds + ' --insecure ' + flags
         else:
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..8918a8c 100644
--- a/tempest/lib/services/clients.py
+++ b/tempest/lib/services/clients.py
@@ -31,6 +31,7 @@
 from tempest.lib.services import identity
 from tempest.lib.services import image
 from tempest.lib.services import network
+from tempest.lib.services import object_storage
 from tempest.lib.services import volume
 
 warnings.simplefilter("once")
@@ -50,20 +51,13 @@
         'image.v1': image.v1,
         'image.v2': image.v2,
         'network': network,
+        'object-storage': object_storage,
         'volume.v1': volume.v1,
         'volume.v2': volume.v2,
         'volume.v3': volume.v3
     }
 
 
-def _tempest_internal_modules():
-    # Set of unstable service clients available in Tempest
-    # NOTE(andreaf) This list will exists only as long the remain clients
-    # are migrated to tempest.lib, and it will then be deleted without
-    # deprecation or advance notice
-    return set(['object-storage'])
-
-
 def available_modules():
     """Set of service client modules available in Tempest and plugins
 
@@ -101,17 +95,6 @@
                                                 plug_service_versions))
                 name_conflicts.append(exceptions.PluginRegistrationException(
                     name=plugin_name, detailed_error=detailed_error))
-            # NOTE(andreaf) Once all tempest clients are stable, the following
-            # if will have to be removed.
-            if not plug_service_versions.isdisjoint(
-                    _tempest_internal_modules()):
-                detailed_error = (
-                    'Plugin %s is trying to register a service %s already '
-                    'claimed by a Tempest one' % (plugin_name,
-                                                  _tempest_internal_modules() &
-                                                  plug_service_versions))
-                name_conflicts.append(exceptions.PluginRegistrationException(
-                    name=plugin_name, detailed_error=detailed_error))
         extra_service_versions |= plug_service_versions
     if name_conflicts:
         LOG.error(
@@ -276,7 +259,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 +319,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,16 +345,20 @@
         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()
+        all_modules = available_modules()
         unversioned_services = set(
             [x.split('.')[0] for x in all_modules])
         for service in unversioned_services:
@@ -420,8 +409,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 +431,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,
@@ -456,9 +446,7 @@
 
     @property
     def registered_services(self):
-        # NOTE(andreaf) Once all tempest modules are stable this needs to
-        # be updated to remove _tempest_internal_modules
-        return self._registered_services | _tempest_internal_modules()
+        return self._registered_services
 
     def _setup_parameters(self, parameters):
         """Setup default values for client parameters
diff --git a/tempest/lib/services/object_storage/__init__.py b/tempest/lib/services/object_storage/__init__.py
index e69de29..4303d09 100644
--- a/tempest/lib/services/object_storage/__init__.py
+++ b/tempest/lib/services/object_storage/__init__.py
@@ -0,0 +1,25 @@
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from tempest.lib.services.object_storage.account_client import AccountClient
+from tempest.lib.services.object_storage.bulk_middleware_client import \
+    BulkMiddlewareClient
+from tempest.lib.services.object_storage.capabilities_client import \
+    CapabilitiesClient
+from tempest.lib.services.object_storage.container_client import \
+    ContainerClient
+from tempest.lib.services.object_storage.object_client import ObjectClient
+
+__all__ = ['AccountClient', 'BulkMiddlewareClient', 'CapabilitiesClient',
+           'ContainerClient', 'ObjectClient']
diff --git a/tempest/services/object_storage/container_client.py b/tempest/lib/services/object_storage/container_client.py
similarity index 100%
rename from tempest/services/object_storage/container_client.py
rename to tempest/lib/services/object_storage/container_client.py
diff --git a/tempest/services/object_storage/object_client.py b/tempest/lib/services/object_storage/object_client.py
similarity index 62%
rename from tempest/services/object_storage/object_client.py
rename to tempest/lib/services/object_storage/object_client.py
index 6d656ec..383aff6 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/lib/services/object_storage/object_client.py
@@ -23,7 +23,8 @@
 class ObjectClient(rest_client.RestClient):
 
     def create_object(self, container, object_name, data,
-                      params=None, metadata=None, headers=None):
+                      params=None, metadata=None, headers=None,
+                      chunked=False):
         """Create storage object."""
 
         if headers is None:
@@ -37,7 +38,7 @@
         if params:
             url += '?%s' % urlparse.urlencode(params)
 
-        resp, body = self.put(url, data, headers)
+        resp, body = self.put(url, data, headers, chunked=chunked)
         self.expected_success(201, resp.status)
         return resp, body
 
@@ -50,28 +51,27 @@
         self.expected_success([200, 204], resp.status)
         return resp, body
 
-    def update_object_metadata(self, container, object_name, metadata,
-                               metadata_prefix='X-Object-Meta-'):
+    def create_or_update_object_metadata(self, container, object_name,
+                                         headers=None):
         """Add, remove, or change X-Object-Meta metadata for storage object."""
 
-        headers = {}
-        for key in metadata:
-            headers["%s%s" % (str(metadata_prefix), str(key))] = metadata[key]
-
         url = "%s/%s" % (str(container), str(object_name))
         resp, body = self.post(url, None, headers=headers)
         self.expected_success(202, resp.status)
         return resp, body
 
-    def list_object_metadata(self, container, object_name):
+    def list_object_metadata(self, container, object_name,
+                             params=None, headers=None):
         """List all storage object X-Object-Meta- metadata."""
 
         url = "%s/%s" % (str(container), str(object_name))
-        resp, body = self.head(url)
+        if params:
+            url += '?%s' % urlparse.urlencode(params)
+        resp, body = self.head(url, headers=headers)
         self.expected_success(200, resp.status)
         return resp, body
 
-    def get_object(self, container, object_name, metadata=None):
+    def get_object(self, container, object_name, metadata=None, params=None):
         """Retrieve object's data."""
 
         headers = {}
@@ -80,45 +80,12 @@
                 headers[str(key)] = metadata[key]
 
         url = "{0}/{1}".format(container, object_name)
+        if params:
+            url += '?%s' % urlparse.urlencode(params)
         resp, body = self.get(url, headers=headers)
         self.expected_success([200, 206], resp.status)
         return resp, body
 
-    def copy_object_in_same_container(self, container, src_object_name,
-                                      dest_object_name, metadata=None):
-        """Copy storage object's data to the new object using PUT."""
-
-        url = "{0}/{1}".format(container, dest_object_name)
-        headers = {}
-        headers['X-Copy-From'] = "%s/%s" % (str(container),
-                                            str(src_object_name))
-        headers['content-length'] = '0'
-        if metadata:
-            for key in metadata:
-                headers[str(key)] = metadata[key]
-
-        resp, body = self.put(url, None, headers=headers)
-        self.expected_success(201, resp.status)
-        return resp, body
-
-    def copy_object_across_containers(self, src_container, src_object_name,
-                                      dst_container, dst_object_name,
-                                      metadata=None):
-        """Copy storage object's data to the new object using PUT."""
-
-        url = "{0}/{1}".format(dst_container, dst_object_name)
-        headers = {}
-        headers['X-Copy-From'] = "%s/%s" % (str(src_container),
-                                            str(src_object_name))
-        headers['content-length'] = '0'
-        if metadata:
-            for key in metadata:
-                headers[str(key)] = metadata[key]
-
-        resp, body = self.put(url, None, headers=headers)
-        self.expected_success(201, resp.status)
-        return resp, body
-
     def copy_object_2d_way(self, container, src_object_name, dest_object_name,
                            metadata=None):
         """Copy storage object's data to the new object using COPY."""
@@ -135,38 +102,6 @@
         self.expected_success(201, resp.status)
         return resp, body
 
-    def create_object_segments(self, container, object_name, segment, data):
-        """Creates object segments."""
-        url = "{0}/{1}/{2}".format(container, object_name, segment)
-        resp, body = self.put(url, data)
-        self.expected_success(201, resp.status)
-        return resp, body
-
-    def put_object_with_chunk(self, container, name, contents):
-        """Put an object with Transfer-Encoding header
-
-        :param container: name of the container
-        :type container: string
-        :param name: name of the object
-        :type name: string
-        :param contents: object data
-        :type contents: iterable
-        """
-        headers = {'Transfer-Encoding': 'chunked'}
-        if self.token:
-            headers['X-Auth-Token'] = self.token
-
-        url = "%s/%s" % (container, name)
-        resp, body = self.put(
-            url, headers=headers,
-            body=contents,
-            chunked=True
-        )
-
-        self._error_checker(resp, body)
-        self.expected_success(201, resp.status)
-        return resp.status, resp.reason, resp
-
     def create_object_continue(self, container, object_name,
                                data, metadata=None):
         """Put an object using Expect:100-continue"""
@@ -183,8 +118,7 @@
         path = str(parsed.path) + "/"
         path += "%s/%s" % (str(container), str(object_name))
 
-        conn = create_connection(parsed)
-
+        conn = _create_connection(parsed)
         # Send the PUT request and the headers including the "Expect" header
         conn.putrequest('PUT', path)
 
@@ -218,7 +152,7 @@
         return resp.status, resp.reason
 
 
-def create_connection(parsed_url):
+def _create_connection(parsed_url):
     """Helper function to create connection with httplib
 
     :param parsed_url: parsed url of the remote location
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/scenario/manager.py b/tempest/scenario/manager.py
index c70ab49..2d8935e 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -89,16 +89,14 @@
     # The create_[resource] functions only return body and discard the
     # resp part which is not used in scenario tests
 
-    def _create_port(self, network_id, client=None, namestart='port-quotatest',
-                     **kwargs):
+    def create_port(self, network_id, client=None, **kwargs):
         if not client:
             client = self.ports_client
-        name = data_utils.rand_name(namestart)
+        name = data_utils.rand_name(self.__class__.__name__)
         result = client.create_port(
             name=name,
             network_id=network_id,
             **kwargs)
-        self.assertIsNotNone(result, 'Unable to allocate port')
         port = result['port']
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         client.delete_port, port['id'])
@@ -147,8 +145,7 @@
         if vnic_type:
             ports = []
 
-            create_port_body = {'binding:vnic_type': vnic_type,
-                                'namestart': 'port-smoke'}
+            create_port_body = {'binding:vnic_type': vnic_type}
             if kwargs:
                 # Convert security group names to security group ids
                 # to pass to create_port
@@ -185,9 +182,9 @@
             for net in networks:
                 net_id = net.get('uuid', net.get('id'))
                 if 'port' not in net:
-                    port = self._create_port(network_id=net_id,
-                                             client=clients.ports_client,
-                                             **create_port_body)
+                    port = self.create_port(network_id=net_id,
+                                            client=clients.ports_client,
+                                            **create_port_body)
                     ports.append({'port': port['id']})
                 else:
                     ports.append({'port': net['port']})
@@ -504,24 +501,6 @@
         waiters.wait_for_volume_resource_status(self.volumes_client,
                                                 volume['id'], 'available')
 
-    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
@@ -904,16 +883,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():
@@ -925,21 +901,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" \
@@ -1324,14 +1287,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.create_update_or_delete_container_metadata(
-            container_name, create_update_metadata=metadata_param)
-        resp, _ = self.container_client.list_container_metadata(container_name)
-        self.assertEqual(resp['x-container-read'], acl)
-
     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_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 0c3bf23..1c4e262 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -113,7 +113,7 @@
         port_id = None
         if boot_with_port:
             # create a port on the network and boot with that
-            port_id = self._create_port(self.network['id'])['id']
+            port_id = self.create_port(self.network['id'])['id']
             self.ports.append({'port': port_id})
 
         server = self._create_server(self.network, port_id)
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
deleted file mode 100644
index 771ed8f..0000000
--- a/tempest/services/object_storage/__init__.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-
-from tempest.lib.services.object_storage.account_client import AccountClient
-from tempest.lib.services.object_storage.bulk_middleware_client import \
-    BulkMiddlewareClient
-from tempest.lib.services.object_storage.capabilities_client import \
-    CapabilitiesClient
-from tempest.services.object_storage.container_client import ContainerClient
-from tempest.services.object_storage.object_client import ObjectClient
-
-__all__ = ['AccountClient', 'BulkMiddlewareClient', 'CapabilitiesClient',
-           'ContainerClient', 'ObjectClient']
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 810f9e5..8641b63 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -176,22 +176,6 @@
                                            False, True)
 
     @mock.patch('tempest.lib.common.http.ClosingHttp.request')
-    def test_verify_keystone_api_versions_no_v2(self, mock_request):
-        self.useFixture(fixtures.MockPatchObject(
-            verify_tempest_config, '_get_unversioned_endpoint',
-            return_value='http://fake_endpoint:5000'))
-        fake_resp = {'versions': {'values': [{'id': 'v3.0'}]}}
-        fake_resp = json.dumps(fake_resp)
-        mock_request.return_value = (None, fake_resp)
-        fake_os = mock.MagicMock()
-        with mock.patch.object(verify_tempest_config,
-                               'print_and_or_update') as print_mock:
-            verify_tempest_config.verify_keystone_api_versions(fake_os, True)
-        print_mock.assert_called_once_with('api_v2',
-                                           'identity-feature-enabled',
-                                           False, True)
-
-    @mock.patch('tempest.lib.common.http.ClosingHttp.request')
     def test_verify_cinder_api_versions_no_v3(self, mock_request):
         self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, '_get_unversioned_endpoint',
diff --git a/tempest/tests/lib/cli/test_execute.py b/tempest/tests/lib/cli/test_execute.py
index 0130454..c276386 100644
--- a/tempest/tests/lib/cli/test_execute.py
+++ b/tempest/tests/lib/cli/test_execute.py
@@ -91,3 +91,37 @@
         self.assertEqual(mock_execute.call_count, 1)
         self.assertEqual(mock_execute.call_args[1],
                          {'prefix': 'env LAC_ALL=C'})
+
+    @mock.patch.object(cli_base, 'execute')
+    def test_execute_with_domain_name(self, mock_execute):
+        cli = cli_base.CLIClient(
+            user_domain_name='default',
+            project_domain_name='default'
+        )
+        cli.glance('action')
+        self.assertEqual(mock_execute.call_count, 1)
+        self.assertIn('--os-user-domain-name default',
+                      mock_execute.call_args[0][2])
+        self.assertIn('--os-project-domain-name default',
+                      mock_execute.call_args[0][2])
+        self.assertNotIn('--os-user-domain-id',
+                         mock_execute.call_args[0][2])
+        self.assertNotIn('--os-project-domain-id',
+                         mock_execute.call_args[0][2])
+
+    @mock.patch.object(cli_base, 'execute')
+    def test_execute_with_domain_id(self, mock_execute):
+        cli = cli_base.CLIClient(
+            user_domain_id='default',
+            project_domain_id='default'
+        )
+        cli.glance('action')
+        self.assertEqual(mock_execute.call_count, 1)
+        self.assertIn('--os-user-domain-id default',
+                      mock_execute.call_args[0][2])
+        self.assertIn('--os-project-domain-id default',
+                      mock_execute.call_args[0][2])
+        self.assertNotIn('--os-user-domain-name',
+                         mock_execute.call_args[0][2])
+        self.assertNotIn('--os-project-domain-name',
+                         mock_execute.call_args[0][2])
diff --git a/tempest/tests/lib/services/object_storage/test_object_client.py b/tempest/tests/lib/services/object_storage/test_object_client.py
new file mode 100644
index 0000000..a16d1d7
--- /dev/null
+++ b/tempest/tests/lib/services/object_storage/test_object_client.py
@@ -0,0 +1,108 @@
+# Copyright 2016 IBM Corp.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+import mock
+
+from tempest.lib import exceptions
+from tempest.lib.services.object_storage import object_client
+from tempest.tests import base
+from tempest.tests.lib import fake_auth_provider
+
+
+class TestObjectClient(base.TestCase):
+
+    def setUp(self):
+        super(TestObjectClient, self).setUp()
+        self.fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.url = self.fake_auth.base_url(None)
+        self.object_client = object_client.ObjectClient(self.fake_auth,
+                                                        'swift', 'region1')
+
+    @mock.patch.object(object_client, '_create_connection')
+    def test_create_object_continue_no_data(self, mock_poc):
+        self._validate_create_object_continue(None, mock_poc)
+
+    @mock.patch.object(object_client, '_create_connection')
+    def test_create_object_continue_with_data(self, mock_poc):
+        self._validate_create_object_continue('hello', mock_poc)
+
+    @mock.patch.object(object_client, '_create_connection')
+    def test_create_continue_with_no_continue_received(self, mock_poc):
+        self._validate_create_object_continue('hello', mock_poc,
+                                              initial_status=201)
+
+    def _validate_create_object_continue(self, req_data,
+                                         mock_poc, initial_status=100):
+
+        expected_hdrs = {
+            'X-Auth-Token': self.fake_auth.get_token(),
+            'content-length': 0 if req_data is None else len(req_data),
+            'Expect': '100-continue'}
+
+        # Setup the Mocks prior to invoking the object creation
+        mock_resp_cls = mock.Mock()
+        mock_resp_cls._read_status.return_value = ("1", initial_status, "OK")
+
+        mock_poc.return_value.response_class.return_value = mock_resp_cls
+
+        # This is the final expected return value
+        mock_poc.return_value.getresponse.return_value.status = 201
+        mock_poc.return_value.getresponse.return_value.reason = 'OK'
+
+        # Call method to PUT object using expect:100-continue
+        cnt = "container1"
+        obj = "object1"
+        path = "/%s/%s" % (cnt, obj)
+
+        # If the expected initial status is not 100, then an exception
+        # should be thrown and the connection closed
+        if initial_status is 100:
+            status, reason = \
+                self.object_client.create_object_continue(cnt, obj, req_data)
+        else:
+            self.assertRaises(exceptions.UnexpectedResponseCode,
+                              self.object_client.create_object_continue, cnt,
+                              obj, req_data)
+            mock_poc.return_value.close.assert_called_once_with()
+
+        # Verify that putrequest is called 1 time with the appropriate values
+        mock_poc.return_value.putrequest.assert_called_once_with('PUT', path)
+
+        # Verify that headers were written, including "Expect:100-continue"
+        calls = []
+
+        for header, value in expected_hdrs.items():
+            calls.append(mock.call(header, value))
+
+        mock_poc.return_value.putheader.assert_has_calls(calls, False)
+        mock_poc.return_value.endheaders.assert_called_once_with()
+
+        # The following steps are only taken if the initial status is 100
+        if initial_status is 100:
+            # Verify that the method returned what it was supposed to
+            self.assertEqual(status, 201)
+
+            # Verify that _safe_read was called once to remove the CRLF
+            # after the 100 response
+            mock_rc = mock_poc.return_value.response_class.return_value
+            mock_rc._safe_read.assert_called_once_with(2)
+
+            # Verify the actual data was written via send
+            mock_poc.return_value.send.assert_called_once_with(req_data)
+
+            # Verify that the getresponse method was called to receive
+            # the final
+            mock_poc.return_value.getresponse.assert_called_once_with()
diff --git a/tempest/tests/lib/services/registry_fixture.py b/tempest/tests/lib/services/registry_fixture.py
index 8484209..1da2112 100644
--- a/tempest/tests/lib/services/registry_fixture.py
+++ b/tempest/tests/lib/services/registry_fixture.py
@@ -38,7 +38,7 @@
         """Initialise the registry fixture"""
         self.services = set(['compute', 'identity.v2', 'identity.v3',
                              'image.v1', 'image.v2', 'network', 'volume.v1',
-                             'volume.v2', 'volume.v3'])
+                             'volume.v2', 'volume.v3', 'object-storage'])
 
     def _setUp(self):
         # Cleanup the registry
@@ -50,7 +50,7 @@
         for sc in self.services:
             sc_module = service_clients[sc]
             sc_unversioned = sc.split('.')[0]
-            sc_name = sc.replace('.', '_')
+            sc_name = sc.replace('.', '_').replace('-', '_')
             # Pass the bare minimum params to satisfy the clients interface
             service_client_data = dict(
                 name=sc_name, service_version=sc, service=sc_unversioned,
diff --git a/tempest/tests/lib/services/test_clients.py b/tempest/tests/lib/services/test_clients.py
index 6d0f27a..43fd88f 100644
--- a/tempest/tests/lib/services/test_clients.py
+++ b/tempest/tests/lib/services/test_clients.py
@@ -189,9 +189,7 @@
     def setUp(self):
         super(TestServiceClients, self).setUp()
         self.useFixture(fixtures.MockPatch(
-            'tempest.lib.services.clients.tempest_modules', return_value={}))
-        self.useFixture(fixtures.MockPatch(
-            'tempest.lib.services.clients._tempest_internal_modules',
+            'tempest.lib.services.clients.tempest_modules',
             return_value=set(['fake_service1'])))
 
     def test___init___creds_v2_uri(self):
@@ -416,6 +414,7 @@
         _manager = self._get_manager()
         duplicate_service = 'fake_service1'
         expected_error = '.*' + duplicate_service
+        _manager._registered_services = [duplicate_service]
         with testtools.ExpectedException(
                 exceptions.ServiceClientRegistrationException, expected_error):
             _manager.register_service_client_module(
diff --git a/tempest/tests/services/__init__.py b/tempest/tests/services/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/tests/services/__init__.py
+++ /dev/null
diff --git a/tempest/tests/services/object_storage/__init__.py b/tempest/tests/services/object_storage/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/tests/services/object_storage/__init__.py
+++ /dev/null
diff --git a/tempest/tests/services/object_storage/test_object_client.py b/tempest/tests/services/object_storage/test_object_client.py
index 748614c..86535f9 100644
--- a/tempest/tests/services/object_storage/test_object_client.py
+++ b/tempest/tests/services/object_storage/test_object_client.py
@@ -31,15 +31,15 @@
         self.object_client = object_client.ObjectClient(self.fake_auth,
                                                         'swift', 'region1')
 
-    @mock.patch.object(object_client, 'create_connection')
+    @mock.patch.object(object_client, '_create_connection')
     def test_create_object_continue_no_data(self, mock_poc):
         self._validate_create_object_continue(None, mock_poc)
 
-    @mock.patch.object(object_client, 'create_connection')
+    @mock.patch.object(object_client, '_create_connection')
     def test_create_object_continue_with_data(self, mock_poc):
         self._validate_create_object_continue('hello', mock_poc)
 
-    @mock.patch.object(object_client, 'create_connection')
+    @mock.patch.object(object_client, '_create_connection')
     def test_create_continue_with_no_continue_received(self, mock_poc):
         self._validate_create_object_continue('hello', mock_poc,
                                               initial_status=201)