Merge "Add parent_id to create_project"
diff --git a/.gitignore b/.gitignore
index 5b87cec..e96deb1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@
 !.coveragerc
 cover/
 doc/source/_static/tempest.conf.sample
+doc/source/plugin-registry.rst
 
 # Files created by releasenotes build
 releasenotes/build
diff --git a/README.rst b/README.rst
index 725a890..fe65dcd 100644
--- a/README.rst
+++ b/README.rst
@@ -70,12 +70,12 @@
    it's recommended that you copy or rename tempest.conf.sample to tempest.conf
    and make those changes to that file in /etc/tempest
 
-#. Setup a local working Tempest dir. This is done by using the tempest init
+#. Setup a local Tempest workspace. This is done by using the tempest init
    command::
 
     $ tempest init cloud-01
 
-   works the same as::
+   which also works the same as::
 
     $ mkdir cloud-01 && cd cloud-01 && tempest init
 
@@ -91,8 +91,19 @@
    any changes to it otherwise Tempest will not know how to load it.
 
 #. Once the configuration is done you're now ready to run Tempest. This can
-   be done with testr directly or any `testr`_ based test runner, like
-   `ostestr`_. For example, from the working dir running::
+   be done using the :ref:`tempest_run` command. This can be done by either
+   running::
+
+     $ tempest run
+
+   from the Tempest workspace directory. Or you can use the ``--workspace``
+   argument to run in the workspace you created regarless of your current
+   working directory. For example::
+
+     $ tempest run --workspace cloud-01
+
+   There is also the option to use testr directly, or any `testr`_ based test
+   runner, like `ostestr`_. For example, from the workspace dir run::
 
      $ ostestr --regex '(?!.*\[.*\bslow\b.*\])(^tempest\.(api|scenario))'
 
diff --git a/doc/source/conf.py b/doc/source/conf.py
index eef3620..127613d 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -15,6 +15,17 @@
 import os
 import subprocess
 
+# Build the plugin registry
+def build_plugin_registry(app):
+    root_dir = os.path.dirname(
+        os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+    subprocess.call(['tools/generate-tempest-plugins-list.sh'], cwd=root_dir)
+
+def setup(app):
+    app.connect('builder-inited', build_plugin_registry)
+
+
+
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 27f4fab..f1ede06 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -2,19 +2,14 @@
 Tempest Testing Project
 =======================
 
-Contents:
+--------
+Overview
+--------
 
 .. toctree::
    :maxdepth: 2
 
    overview
-   HACKING
-   REVIEWING
-   plugin
-   plugin-registry
-   library
-   microversion_testing
-   test-removal
 
 ------------
 Field Guides
@@ -32,6 +27,10 @@
    field_guide/stress
    field_guide/unit_tests
 
+=========
+For users
+=========
+
 ---------------------------
 Tempest Configuration Guide
 ---------------------------
@@ -52,9 +51,45 @@
    account_generator
    cleanup
    javelin
+   subunit_describe_calls
    workspace
    run
 
+==============
+For developers
+==============
+
+-----------
+Development
+-----------
+
+.. toctree::
+   :maxdepth: 2
+
+   HACKING
+   REVIEWING
+   microversion_testing
+   test-removal
+
+-------
+Plugins
+-------
+
+.. toctree::
+   :maxdepth: 2
+
+   plugin
+   plugin-registry
+
+-------
+Library
+-------
+
+.. toctree::
+   :maxdepth: 2
+
+   library
+
 ==================
 Indices and tables
 ==================
diff --git a/doc/source/plugin-registry.rst b/doc/source/plugin-registry.rst
deleted file mode 100644
index 517e5b8..0000000
--- a/doc/source/plugin-registry.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-..
-  Note to patch submitters: this file is covered by a periodic proposal
-  job.  You should edit the files data/tempest-plugins-registry.footer
-  data/tempest-plugins-registry.header instead of this one.
-
-==========================
- Tempest Plugin Registry
-==========================
-
-Since we've created the external plugin mechanism, it's gotten used by
-a lot of projects. The following is a list of plugins that currently
-exist.
-
-Detected Plugins
-================
-
-The following will list plugins that a script has found in the openstack/
-namespace, which includes but is not limited to official OpenStack
-projects.
-
-+----------------------------+-------------------------------------------------------------------------+
-|Plugin Name                 |URL                                                                      |
-+----------------------------+-------------------------------------------------------------------------+
diff --git a/doc/source/run.rst b/doc/source/run.rst
index 07fa5f7..ce7f03e 100644
--- a/doc/source/run.rst
+++ b/doc/source/run.rst
@@ -1,3 +1,5 @@
+.. _tempest_run:
+
 -----------
 Tempest Run
 -----------
diff --git a/doc/source/subunit_describe_calls.rst b/doc/source/subunit_describe_calls.rst
new file mode 100644
index 0000000..2bda50c
--- /dev/null
+++ b/doc/source/subunit_describe_calls.rst
@@ -0,0 +1,5 @@
+------------------------------
+Subunit Describe Calls Utility
+------------------------------
+
+.. automodule:: tempest.cmd.subunit_describe_calls
diff --git a/releasenotes/notes/.placeholder b/releasenotes/notes/.placeholder
deleted file mode 100644
index e69de29..0000000
--- a/releasenotes/notes/.placeholder
+++ /dev/null
diff --git a/releasenotes/notes/add_subunit_describe_calls-5498a37e6cd66c4b.yaml b/releasenotes/notes/add_subunit_describe_calls-5498a37e6cd66c4b.yaml
new file mode 100644
index 0000000..b457ddd
--- /dev/null
+++ b/releasenotes/notes/add_subunit_describe_calls-5498a37e6cd66c4b.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - Adds subunit-describe-calls. A parser for subunit streams to determine what
+    REST API calls are made inside of a test and in what order they are called.
diff --git a/releasenotes/notes/identity-clients-as-library-e663c6132fcac6c2.yaml b/releasenotes/notes/identity-clients-as-library-e663c6132fcac6c2.yaml
index b7850d0..f9173a0 100644
--- a/releasenotes/notes/identity-clients-as-library-e663c6132fcac6c2.yaml
+++ b/releasenotes/notes/identity-clients-as-library-e663c6132fcac6c2.yaml
@@ -7,3 +7,7 @@
     any maintenance changes.
 
       * endpoints_client(v2)
+      * roles_client(v2)
+      * services_client(v2)
+      * tenants_client(v2)
+      * users_client(v2)
diff --git a/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml b/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml
index 90a5056..1fa4ddd 100644
--- a/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml
+++ b/releasenotes/notes/image-clients-as-library-86d17caa26ce3961.yaml
@@ -7,6 +7,7 @@
     without any maintenance changes.
 
       * image_members_client(v1)
+      * images_client(v1)
       * image_members_client(v2)
       * images_client(v2)
       * namespaces_client(v2)
diff --git a/releasenotes/notes/remove-trove-tests-666522e9113549f9.yaml b/releasenotes/notes/remove-trove-tests-666522e9113549f9.yaml
new file mode 100644
index 0000000..1157a4f
--- /dev/null
+++ b/releasenotes/notes/remove-trove-tests-666522e9113549f9.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+  - All tests for the Trove project have been removed from tempest. They now
+    live as a tempest plugin in the the trove project.
diff --git a/requirements.txt b/requirements.txt
index 45fe345..7d01f69 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,7 +5,7 @@
 cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0
 jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
 testtools>=1.4.0 # MIT
-paramiko>=2.0 # LGPL
+paramiko>=2.0 # LGPLv2.1+
 netaddr!=0.7.16,>=0.7.12 # BSD
 testrepository>=0.0.18 # Apache-2.0/BSD
 pyOpenSSL>=0.14 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
index 66a8743..2a3000d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -34,6 +34,7 @@
     tempest = tempest.cmd.main:main
     skip-tracker = tempest.lib.cmd.skip_tracker:main
     check-uuid = tempest.lib.cmd.check_uuid:run
+    subunit-describe-calls = tempest.cmd.subunit_describe_calls:entry_point
 tempest.cm =
     account-generator = tempest.cmd.account_generator:TempestAccountGenerator
     init = tempest.cmd.init:TempestInit
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index 427a748..6f80730 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -16,6 +16,7 @@
 import six
 
 from tempest.api.compute import base
+from tempest.common import image as common_image
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
@@ -55,15 +56,18 @@
         super(ImagesMetadataTestJSON, cls).resource_setup()
         cls.image_id = None
 
-        name = data_utils.rand_name('image')
+        params = {
+            'name': data_utils.rand_name('image'),
+            'container_format': 'bare',
+            'disk_format': 'raw'
+        }
         if CONF.image_feature_enabled.api_v1:
-            kwargs = dict(is_public=False)
+            params.update({'is_public': False})
+            params = {'headers': common_image.image_meta_to_headers(**params)}
         else:
-            kwargs = dict(visibility='private')
-        body = cls.glance_client.create_image(name=name,
-                                              container_format='bare',
-                                              disk_format='raw',
-                                              **kwargs)
+            params.update({'visibility': 'private'})
+
+        body = cls.glance_client.create_image(**params)
         body = body['image'] if 'image' in body else body
         cls.image_id = body['id']
         cls.images.append(cls.image_id)
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index e74ca67..9017461 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -19,6 +19,7 @@
 import testtools
 
 from tempest.api.compute import base
+from tempest.common import image as common_image
 from tempest.common.utils import data_utils
 from tempest.common import waiters
 from tempest import config
@@ -58,15 +59,19 @@
         super(ListImageFiltersTestJSON, cls).resource_setup()
 
         def _create_image():
-            name = data_utils.rand_name('image')
+            params = {
+                'name': data_utils.rand_name('image'),
+                'container_format': 'bare',
+                'disk_format': 'raw'
+            }
             if CONF.image_feature_enabled.api_v1:
-                kwargs = dict(is_public=False)
+                params.update({'is_public': False})
+                params = {'headers':
+                          common_image.image_meta_to_headers(**params)}
             else:
-                kwargs = dict(visibility='private')
-            body = cls.glance_client.create_image(name=name,
-                                                  container_format='bare',
-                                                  disk_format='raw',
-                                                  **kwargs)
+                params.update({'visibility': 'private'})
+
+            body = cls.glance_client.create_image(**params)
             body = body['image'] if 'image' in body else body
             image_id = body['id']
             cls.images.append(image_id)
diff --git a/tempest/api/compute/servers/test_disk_config.py b/tempest/api/compute/servers/test_disk_config.py
index 617cdd5..aba0240 100644
--- a/tempest/api/compute/servers/test_disk_config.py
+++ b/tempest/api/compute/servers/test_disk_config.py
@@ -37,17 +37,11 @@
         super(ServerDiskConfigTestJSON, cls).setup_clients()
         cls.client = cls.os.servers_client
 
-    @classmethod
-    def resource_setup(cls):
-        super(ServerDiskConfigTestJSON, cls).resource_setup()
-        server = cls.create_test_server(wait_until='ACTIVE')
-        cls.server_id = server['id']
-
-    def _update_server_with_disk_config(self, disk_config):
-        server = self.client.show_server(self.server_id)['server']
+    def _update_server_with_disk_config(self, server_id, disk_config):
+        server = self.client.show_server(server_id)['server']
         if disk_config != server['OS-DCF:diskConfig']:
             server = self.client.update_server(
-                self.server_id, disk_config=disk_config)['server']
+                server_id, disk_config=disk_config)['server']
             waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
             server = self.client.show_server(server['id'])['server']
             self.assertEqual(disk_config, server['OS-DCF:diskConfig'])
@@ -55,9 +49,12 @@
     @test.idempotent_id('bef56b09-2e8c-4883-a370-4950812f430e')
     def test_rebuild_server_with_manual_disk_config(self):
         # A server should be rebuilt using the manual disk config option
-        self._update_server_with_disk_config(disk_config='AUTO')
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.addCleanup(self.client.delete_server, server['id'])
+        self._update_server_with_disk_config(server['id'],
+                                             disk_config='AUTO')
 
-        server = self.client.rebuild_server(self.server_id,
+        server = self.client.rebuild_server(server['id'],
                                             self.image_ref_alt,
                                             disk_config='MANUAL')['server']
 
@@ -71,9 +68,12 @@
     @test.idempotent_id('9c9fae77-4feb-402f-8450-bf1c8b609713')
     def test_rebuild_server_with_auto_disk_config(self):
         # A server should be rebuilt using the auto disk config option
-        self._update_server_with_disk_config(disk_config='MANUAL')
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.addCleanup(self.client.delete_server, server['id'])
+        self._update_server_with_disk_config(server['id'],
+                                             disk_config='MANUAL')
 
-        server = self.client.rebuild_server(self.server_id,
+        server = self.client.rebuild_server(server['id'],
                                             self.image_ref_alt,
                                             disk_config='AUTO')['server']
 
@@ -84,31 +84,24 @@
         server = self.client.show_server(server['id'])['server']
         self.assertEqual('AUTO', server['OS-DCF:diskConfig'])
 
-    def _get_alternative_flavor(self):
-        server = self.client.show_server(self.server_id)['server']
-
-        if server['flavor']['id'] == self.flavor_ref:
-            return self.flavor_ref_alt
-        else:
-            return self.flavor_ref
-
     @test.idempotent_id('414e7e93-45b5-44bc-8e03-55159c6bfc97')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize not available.')
     def test_resize_server_from_manual_to_auto(self):
         # A server should be resized from manual to auto disk config
-        self._update_server_with_disk_config(disk_config='MANUAL')
-
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.addCleanup(self.client.delete_server, server['id'])
+        self._update_server_with_disk_config(server['id'],
+                                             disk_config='MANUAL')
         # Resize with auto option
-        flavor_id = self._get_alternative_flavor()
-        self.client.resize_server(self.server_id, flavor_id,
+        self.client.resize_server(server['id'], self.flavor_ref_alt,
                                   disk_config='AUTO')
-        waiters.wait_for_server_status(self.client, self.server_id,
+        waiters.wait_for_server_status(self.client, server['id'],
                                        'VERIFY_RESIZE')
-        self.client.confirm_resize_server(self.server_id)
-        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+        self.client.confirm_resize_server(server['id'])
+        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
 
-        server = self.client.show_server(self.server_id)['server']
+        server = self.client.show_server(server['id'])['server']
         self.assertEqual('AUTO', server['OS-DCF:diskConfig'])
 
     @test.idempotent_id('693d16f3-556c-489a-8bac-3d0ca2490bad')
@@ -116,27 +109,31 @@
                           'Resize not available.')
     def test_resize_server_from_auto_to_manual(self):
         # A server should be resized from auto to manual disk config
-        self._update_server_with_disk_config(disk_config='AUTO')
-
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.addCleanup(self.client.delete_server, server['id'])
+        self._update_server_with_disk_config(server['id'],
+                                             disk_config='AUTO')
         # Resize with manual option
-        flavor_id = self._get_alternative_flavor()
-        self.client.resize_server(self.server_id, flavor_id,
+        self.client.resize_server(server['id'], self.flavor_ref_alt,
                                   disk_config='MANUAL')
-        waiters.wait_for_server_status(self.client, self.server_id,
+        waiters.wait_for_server_status(self.client, server['id'],
                                        'VERIFY_RESIZE')
-        self.client.confirm_resize_server(self.server_id)
-        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+        self.client.confirm_resize_server(server['id'])
+        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
 
-        server = self.client.show_server(self.server_id)['server']
+        server = self.client.show_server(server['id'])['server']
         self.assertEqual('MANUAL', server['OS-DCF:diskConfig'])
 
     @test.idempotent_id('5ef18867-358d-4de9-b3c9-94d4ba35742f')
     def test_update_server_from_auto_to_manual(self):
         # A server should be updated from auto to manual disk config
-        self._update_server_with_disk_config(disk_config='AUTO')
+        server = self.create_test_server(wait_until='ACTIVE')
+        self.addCleanup(self.client.delete_server, server['id'])
+        self._update_server_with_disk_config(server['id'],
+                                             disk_config='AUTO')
 
         # Update the disk_config attribute to manual
-        server = self.client.update_server(self.server_id,
+        server = self.client.update_server(server['id'],
                                            disk_config='MANUAL')['server']
         waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
 
diff --git a/tempest/api/database/__init__.py b/tempest/api/database/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/database/__init__.py
+++ /dev/null
diff --git a/tempest/api/database/base.py b/tempest/api/database/base.py
deleted file mode 100644
index 01e05db..0000000
--- a/tempest/api/database/base.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright 2014 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 tempest import config
-import tempest.test
-
-CONF = config.CONF
-
-
-class BaseDatabaseTest(tempest.test.BaseTestCase):
-    """Base test case class for all Database API tests."""
-
-    credentials = ['primary']
-
-    @classmethod
-    def skip_checks(cls):
-        super(BaseDatabaseTest, cls).skip_checks()
-        if not CONF.service_available.trove:
-            skip_msg = ("%s skipped as trove is not available" % cls.__name__)
-            raise cls.skipException(skip_msg)
-
-    @classmethod
-    def setup_clients(cls):
-        super(BaseDatabaseTest, cls).setup_clients()
-        cls.database_flavors_client = cls.os.database_flavors_client
-        cls.os_flavors_client = cls.os.flavors_client
-        cls.database_limits_client = cls.os.database_limits_client
-        cls.database_versions_client = cls.os.database_versions_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(BaseDatabaseTest, cls).resource_setup()
-
-        cls.catalog_type = CONF.database.catalog_type
-        cls.db_flavor_ref = CONF.database.db_flavor_ref
-        cls.db_current_version = CONF.database.db_current_version
diff --git a/tempest/api/database/flavors/__init__.py b/tempest/api/database/flavors/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/database/flavors/__init__.py
+++ /dev/null
diff --git a/tempest/api/database/flavors/test_flavors.py b/tempest/api/database/flavors/test_flavors.py
deleted file mode 100644
index bb7a0a4..0000000
--- a/tempest/api/database/flavors/test_flavors.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# Copyright 2014 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 tempest.api.database import base
-from tempest.lib import decorators
-from tempest import test
-
-
-class DatabaseFlavorsTest(base.BaseDatabaseTest):
-
-    @classmethod
-    def setup_clients(cls):
-        super(DatabaseFlavorsTest, cls).setup_clients()
-        cls.client = cls.database_flavors_client
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('c94b825e-0132-4686-8049-8a4a2bc09525')
-    @decorators.skip_because(bug='1567134')
-    def test_get_db_flavor(self):
-        # The expected flavor details should be returned
-        flavor = (self.client.show_db_flavor(self.db_flavor_ref)
-                  ['flavor'])
-        self.assertEqual(self.db_flavor_ref, str(flavor['id']))
-        self.assertIn('ram', flavor)
-        self.assertIn('links', flavor)
-        self.assertIn('name', flavor)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('685025d6-0cec-4673-8a8d-995cb8e0d3bb')
-    @decorators.skip_because(bug='1567134')
-    def test_list_db_flavors(self):
-        flavor = (self.client.show_db_flavor(self.db_flavor_ref)
-                  ['flavor'])
-        # List of all flavors should contain the expected flavor
-        flavors = self.client.list_db_flavors()['flavors']
-        self.assertIn(flavor, flavors)
-
-    def _check_values(self, names, db_flavor, os_flavor, in_db=True):
-        for name in names:
-            self.assertIn(name, os_flavor)
-            if in_db:
-                self.assertIn(name, db_flavor)
-                self.assertEqual(str(db_flavor[name]), str(os_flavor[name]),
-                                 "DB flavor differs from OS on '%s' value"
-                                 % name)
-            else:
-                self.assertNotIn(name, db_flavor)
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('afb2667f-4ec2-4925-bcb7-313fdcffb80d')
-    @test.services('compute')
-    @decorators.skip_because(bug='1567134')
-    def test_compare_db_flavors_with_os(self):
-        db_flavors = self.client.list_db_flavors()['flavors']
-        os_flavors = (self.os_flavors_client.list_flavors(detail=True)
-                      ['flavors'])
-        self.assertEqual(len(os_flavors), len(db_flavors),
-                         "OS flavors %s do not match DB flavors %s" %
-                         (os_flavors, db_flavors))
-        for os_flavor in os_flavors:
-            db_flavor =\
-                self.client.show_db_flavor(os_flavor['id'])['flavor']
-            self._check_values(['id', 'name', 'ram'], db_flavor, os_flavor)
-            self._check_values(['disk', 'vcpus', 'swap'], db_flavor, os_flavor,
-                               in_db=False)
diff --git a/tempest/api/database/flavors/test_flavors_negative.py b/tempest/api/database/flavors/test_flavors_negative.py
deleted file mode 100644
index cd2981b..0000000
--- a/tempest/api/database/flavors/test_flavors_negative.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright 2014 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 tempest.api.database import base
-from tempest.lib import exceptions as lib_exc
-from tempest import test
-
-
-class DatabaseFlavorsNegativeTest(base.BaseDatabaseTest):
-
-    @classmethod
-    def setup_clients(cls):
-        super(DatabaseFlavorsNegativeTest, cls).setup_clients()
-        cls.client = cls.database_flavors_client
-
-    @test.attr(type=['negative'])
-    @test.idempotent_id('f8e7b721-373f-4a64-8e9c-5327e975af3e')
-    def test_get_non_existent_db_flavor(self):
-        # flavor details are not returned for non-existent flavors
-        self.assertRaises(lib_exc.NotFound,
-                          self.client.show_db_flavor, -1)
diff --git a/tempest/api/database/limits/__init__.py b/tempest/api/database/limits/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/database/limits/__init__.py
+++ /dev/null
diff --git a/tempest/api/database/limits/test_limits.py b/tempest/api/database/limits/test_limits.py
deleted file mode 100644
index ee51b1d..0000000
--- a/tempest/api/database/limits/test_limits.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2014 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 tempest.api.database import base
-from tempest import test
-
-
-class DatabaseLimitsTest(base.BaseDatabaseTest):
-
-    @classmethod
-    def resource_setup(cls):
-        super(DatabaseLimitsTest, cls).resource_setup()
-        cls.client = cls.database_limits_client
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('73024538-f316-4829-b3e9-b459290e137a')
-    def test_absolute_limits(self):
-        # Test to verify if all absolute limit parameters are
-        # present when verb is ABSOLUTE
-        limits = self.client.list_db_limits()['limits']
-        expected_abs_limits = ['max_backups', 'max_volumes',
-                               'max_instances', 'verb']
-        absolute_limit = [l for l in limits
-                          if l['verb'] == 'ABSOLUTE']
-        self.assertEqual(1, len(absolute_limit), "One ABSOLUTE limit "
-                         "verb is allowed. Fetched %s"
-                         % len(absolute_limit))
-        actual_abs_limits = absolute_limit[0].keys()
-        missing_abs_limit = set(expected_abs_limits) - set(actual_abs_limits)
-        self.assertEmpty(missing_abs_limit,
-                         "Failed to find the following absolute limit(s)"
-                         " in a fetched list: %s" %
-                         ', '.join(str(a) for a in missing_abs_limit))
diff --git a/tempest/api/database/versions/__init__.py b/tempest/api/database/versions/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/api/database/versions/__init__.py
+++ /dev/null
diff --git a/tempest/api/database/versions/test_versions.py b/tempest/api/database/versions/test_versions.py
deleted file mode 100644
index ae568b1..0000000
--- a/tempest/api/database/versions/test_versions.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2014 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 tempest.api.database import base
-from tempest import test
-
-
-class DatabaseVersionsTest(base.BaseDatabaseTest):
-
-    @classmethod
-    def setup_clients(cls):
-        super(DatabaseVersionsTest, cls).setup_clients()
-        cls.client = cls.database_versions_client
-
-    @test.attr(type='smoke')
-    @test.idempotent_id('6952cd77-90cd-4dca-bb60-8e2c797940cf')
-    def test_list_db_versions(self):
-        versions = self.client.list_db_versions()['versions']
-        self.assertTrue(len(versions) > 0, "No database versions found")
-        # List of all versions should contain the current version, and there
-        # should only be one 'current' version
-        current_versions = list()
-        for version in versions:
-            if 'CURRENT' == version['status']:
-                current_versions.append(version['id'])
-        self.assertEqual(1, len(current_versions))
-        self.assertIn(self.db_current_version, current_versions)
diff --git a/tempest/api/identity/admin/v3/test_default_project_id.py b/tempest/api/identity/admin/v3/test_default_project_id.py
index 18a50d0..a540da7 100644
--- a/tempest/api/identity/admin/v3/test_default_project_id.py
+++ b/tempest/api/identity/admin/v3/test_default_project_id.py
@@ -9,13 +9,11 @@
 #    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.api.identity import base
 from tempest import clients
 from tempest.common.utils import data_utils
 from tempest import config
 from tempest.lib import auth
-from tempest import manager
 from tempest import test
 
 CONF = config.CONF
@@ -78,7 +76,7 @@
         creds = auth.KeystoneV3Credentials(username=user_name,
                                            password=user_name,
                                            user_domain_name=dom_name)
-        auth_provider = manager.get_auth_provider(creds)
+        auth_provider = clients.get_auth_provider(creds)
         creds = auth_provider.fill_credentials()
         admin_client = clients.Manager(credentials=creds)
 
diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py
index 7a1e3a5..df39390 100644
--- a/tempest/api/identity/base.py
+++ b/tempest/api/identity/base.py
@@ -218,21 +218,21 @@
     def teardown_all(self):
         for user in self.users:
             test_utils.call_and_ignore_notfound_exc(
-                self.users_client.delete_user, user)
+                self.users_client.delete_user, user['id'])
         for tenant in self.tenants:
             test_utils.call_and_ignore_notfound_exc(
-                self.projects_client.delete_tenant, tenant)
+                self.projects_client.delete_tenant, tenant['id'])
         for project in reversed(self.projects):
             test_utils.call_and_ignore_notfound_exc(
-                self.projects_client.delete_project, project)
+                self.projects_client.delete_project, project['id'])
         for role in self.roles:
             test_utils.call_and_ignore_notfound_exc(
-                self.roles_client.delete_role, role)
+                self.roles_client.delete_role, role['id'])
         for domain in self.domains:
             test_utils.call_and_ignore_notfound_exc(
-                self.domains_client.update_domain, domain, enabled=False)
+                self.domains_client.update_domain, domain['id'], enabled=False)
             test_utils.call_and_ignore_notfound_exc(
-                self.domains_client.delete_domain, domain)
+                self.domains_client.delete_domain, domain['id'])
 
 
 class DataGeneratorV2(BaseDataGenerator):
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 3fefc81..6fd6ea6 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -14,6 +14,7 @@
 
 from six import moves
 
+from tempest.common import image as common_image
 from tempest.common.utils import data_utils
 from tempest import config
 from tempest.lib.common.utils import test_utils
@@ -55,14 +56,20 @@
         super(BaseImageTest, cls).resource_cleanup()
 
     @classmethod
-    def create_image(cls, **kwargs):
+    def create_image(cls, data=None, **kwargs):
         """Wrapper that returns a test image."""
 
         if 'name' not in kwargs:
             name = data_utils.rand_name(cls.__name__ + "-instance")
             kwargs['name'] = name
 
-        image = cls.client.create_image(**kwargs)
+        params = cls._get_create_params(**kwargs)
+        if data:
+            # NOTE: On glance v1 API, the data should be passed on
+            # a header. Then here handles the data separately.
+            params['data'] = data
+
+        image = cls.client.create_image(**params)
         # Image objects returned by the v1 client have the image
         # data inside a dict that is keyed against 'image'.
         if 'image' in image:
@@ -70,6 +77,10 @@
         cls.created_images.append(image['id'])
         return image
 
+    @classmethod
+    def _get_create_params(cls, **kwargs):
+        return kwargs
+
 
 class BaseV1ImageTest(BaseImageTest):
 
@@ -85,6 +96,10 @@
         super(BaseV1ImageTest, cls).setup_clients()
         cls.client = cls.os.image_client
 
+    @classmethod
+    def _get_create_params(cls, **kwargs):
+        return {'headers': common_image.image_meta_to_headers(**kwargs)}
+
 
 class BaseV1ImageMembersTest(BaseV1ImageTest):
 
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 59ac646..def7750 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -320,8 +320,10 @@
         metadata = common_image.get_image_meta_from_headers(resp)
         self.assertEqual(metadata['properties'], {'key1': 'value1'})
         metadata['properties'].update(req_metadata)
-        metadata = self.client.update_image(
-            self.image_id, properties=metadata['properties'])['image']
+        headers = common_image.image_meta_to_headers(
+            properties=metadata['properties'])
+        metadata = self.client.update_image(self.image_id,
+                                            headers=headers)['image']
 
         resp = self.client.check_image(self.image_id)
         resp_metadata = common_image.get_image_meta_from_headers(resp)
diff --git a/tempest/api/image/v1/test_images_negative.py b/tempest/api/image/v1/test_images_negative.py
index babee74..9e67c25 100644
--- a/tempest/api/image/v1/test_images_negative.py
+++ b/tempest/api/image/v1/test_images_negative.py
@@ -27,17 +27,17 @@
     def test_register_with_invalid_container_format(self):
         # Negative tests for invalid data supplied to POST /images
         self.assertRaises(lib_exc.BadRequest, self.client.create_image,
-                          name='test',
-                          container_format='wrong',
-                          disk_format='vhd',)
+                          headers={'x-image-meta-name': 'test',
+                                   'x-image-meta-container_format': 'wrong',
+                                   'x-image-meta-disk_format': 'vhd'})
 
     @test.attr(type=['negative'])
     @test.idempotent_id('993face5-921d-4e84-aabf-c1bba4234a67')
     def test_register_with_invalid_disk_format(self):
         self.assertRaises(lib_exc.BadRequest, self.client.create_image,
-                          name='test',
-                          container_format='bare',
-                          disk_format='wrong',)
+                          headers={'x-image-meta-name': 'test',
+                                   'x-image-meta-container_format': 'bare',
+                                   'x-image-meta-disk_format': 'wrong'})
 
     @test.attr(type=['negative'])
     @test.idempotent_id('bb016f15-0820-4f27-a92d-09b2f67d2488')
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index 5e1c20b..5615cf3 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -63,9 +63,8 @@
             extra_specs = {spec_key_with_prefix: backend_name_key}
         else:
             extra_specs = {spec_key_without_prefix: backend_name_key}
-        self.type = self.admin_volume_types_client.create_volume_type(
-            name=type_name, extra_specs=extra_specs)['volume_type']
-        self.volume_type_id_list.append(self.type['id'])
+        self.type = self.create_volume_type(name=type_name,
+                                            extra_specs=extra_specs)
 
         params = {self.name_field: vol_name, 'volume_type': type_name}
 
@@ -92,11 +91,6 @@
             cls.admin_volume_client.delete_volume(volume_id)
             cls.admin_volume_client.wait_for_resource_deletion(volume_id)
 
-        # volume types deletion
-        volume_type_id_list = getattr(cls, 'volume_type_id_list', [])
-        for volume_type_id in volume_type_id_list:
-            cls.admin_volume_types_client.delete_volume_type(volume_type_id)
-
         super(VolumeMultiBackendV2Test, cls).resource_cleanup()
 
     @test.idempotent_id('c1a41f3f-9dad-493e-9f09-3ff197d477cc')
diff --git a/tempest/api/volume/admin/test_qos.py b/tempest/api/volume/admin/test_qos.py
index 68e57f5..9402668 100644
--- a/tempest/api/volume/admin/test_qos.py
+++ b/tempest/api/volume/admin/test_qos.py
@@ -50,14 +50,6 @@
         list_qos = self.admin_volume_qos_client.list_qos()['qos_specs']
         self.assertNotIn(body, list_qos)
 
-    def _create_test_volume_type(self):
-        vol_type_name = utils.rand_name("volume-type")
-        vol_type = self.admin_volume_types_client.create_volume_type(
-            name=vol_type_name)['volume_type']
-        self.addCleanup(self.admin_volume_types_client.delete_volume_type,
-                        vol_type['id'])
-        return vol_type
-
     def _test_associate_qos(self, vol_type_id):
         self.admin_volume_qos_client.associate_qos(
             self.created_qos['id'], vol_type_id)
@@ -146,7 +138,7 @@
         # create a test volume-type
         vol_type = []
         for _ in range(0, 3):
-            vol_type.append(self._create_test_volume_type())
+            vol_type.append(self.create_volume_type())
 
         # associate the qos-specs with volume-types
         for i in range(0, 3):
diff --git a/tempest/api/volume/admin/test_volume_type_access.py b/tempest/api/volume/admin/test_volume_type_access.py
new file mode 100644
index 0000000..fac71a8
--- /dev/null
+++ b/tempest/api/volume/admin/test_volume_type_access.py
@@ -0,0 +1,94 @@
+# Copyright 2016 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.
+
+import operator
+
+from tempest.api.volume import base
+from tempest.common import waiters
+from tempest.lib import exceptions as lib_exc
+from tempest import test
+
+
+class VolumeTypesAccessV2Test(base.BaseVolumeAdminTest):
+
+    credentials = ['primary', 'alt', 'admin']
+
+    @classmethod
+    def setup_clients(cls):
+        super(VolumeTypesAccessV2Test, cls).setup_clients()
+        cls.alt_client = cls.os_alt.volumes_client
+
+    @test.idempotent_id('d4dd0027-835f-4554-a6e5-50903fb79184')
+    def test_volume_type_access_add(self):
+        # Creating a NON public volume type
+        params = {'os-volume-type-access:is_public': False}
+        volume_type = self.create_volume_type(**params)
+
+        # Try creating a volume from volume type in primary tenant
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
+                          volume_type=volume_type['id'])
+
+        # Adding volume type access for primary tenant
+        self.admin_volume_types_client.add_type_access(
+            volume_type['id'], project=self.volumes_client.tenant_id)
+        self.addCleanup(self.admin_volume_types_client.remove_type_access,
+                        volume_type['id'],
+                        project=self.volumes_client.tenant_id)
+
+        # Creating a volume from primary tenant
+        volume = self.volumes_client.create_volume(
+            volume_type=volume_type['id'])['volume']
+        self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
+        waiters.wait_for_volume_status(self.volumes_client, volume['id'],
+                                       'available')
+
+        # Validating the created volume is based on the volume type
+        self.assertEqual(volume_type['name'], volume['volume_type'])
+
+    @test.idempotent_id('5220eb28-a435-43ce-baaf-ed46f0e95159')
+    def test_volume_type_access_list(self):
+        # Creating a NON public volume type
+        params = {'os-volume-type-access:is_public': False}
+        volume_type = self.create_volume_type(**params)
+
+        # Adding volume type access for primary tenant
+        self.admin_volume_types_client.add_type_access(
+            volume_type['id'], project=self.volumes_client.tenant_id)
+        self.addCleanup(self.admin_volume_types_client.remove_type_access,
+                        volume_type['id'],
+                        project=self.volumes_client.tenant_id)
+
+        # Adding volume type access for alt tenant
+        self.admin_volume_types_client.add_type_access(
+            volume_type['id'], project=self.alt_client.tenant_id)
+        self.addCleanup(self.admin_volume_types_client.remove_type_access,
+                        volume_type['id'],
+                        project=self.alt_client.tenant_id)
+
+        # List tenant access for the given volume type
+        type_access_list = self.admin_volume_types_client.list_type_access(
+            volume_type['id'])['volume_type_access']
+        volume_type_ids = [
+            vol_type['volume_type_id'] for vol_type in type_access_list
+        ]
+
+        # Validating volume type available for only two tenants
+        self.assertEqual(2, volume_type_ids.count(volume_type['id']))
+
+        # Validating the permitted tenants are the expected tenants
+        self.assertIn(self.volumes_client.tenant_id,
+                      map(operator.itemgetter('project_id'), type_access_list))
+        self.assertIn(self.alt_client.tenant_id,
+                      map(operator.itemgetter('project_id'), type_access_list))
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index 9023037..27f6ccb 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -44,12 +44,10 @@
         # Create two volume_types
         for i in range(2):
             vol_type_name = data_utils.rand_name("volume-type")
-            vol_type = self.admin_volume_types_client.create_volume_type(
+            vol_type = self.create_volume_type(
                 name=vol_type_name,
-                extra_specs=extra_specs)['volume_type']
+                extra_specs=extra_specs)
             volume_types.append(vol_type)
-            self.addCleanup(self.admin_volume_types_client.delete_volume_type,
-                            vol_type['id'])
         params = {self.name_field: vol_name,
                   'volume_type': volume_types[0]['id']}
 
@@ -94,12 +92,10 @@
         vendor = CONF.volume.vendor_name
         extra_specs = {"storage_protocol": proto,
                        "vendor_name": vendor}
-        body = self.admin_volume_types_client.create_volume_type(
+        body = self.create_volume_type(
             name=name,
-            extra_specs=extra_specs)['volume_type']
+            extra_specs=extra_specs)
         self.assertIn('id', body)
-        self.addCleanup(self.admin_volume_types_client.delete_volume_type,
-                        body['id'])
         self.assertIn('name', body)
         self.assertEqual(body['name'], name,
                          "The created volume_type name is not equal "
@@ -124,11 +120,7 @@
         provider = "LuksEncryptor"
         control_location = "front-end"
         name = data_utils.rand_name("volume-type")
-        body = self.admin_volume_types_client.create_volume_type(
-            name=name)['volume_type']
-        self.addCleanup(self.admin_volume_types_client.delete_volume_type,
-                        body['id'])
-
+        body = self.create_volume_type(name=name)
         # Create encryption type
         encryption_type = \
             self.admin_volume_types_client.create_encryption_type(
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs.py b/tempest/api/volume/admin/test_volume_types_extra_specs.py
index a45bee0..9e49b94 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs.py
@@ -24,14 +24,7 @@
     def resource_setup(cls):
         super(VolumeTypesExtraSpecsV2Test, cls).resource_setup()
         vol_type_name = data_utils.rand_name('Volume-type')
-        cls.volume_type = \
-            cls.admin_volume_types_client.create_volume_type(
-                name=vol_type_name)['volume_type']
-
-    @classmethod
-    def resource_cleanup(cls):
-        cls.admin_volume_types_client.delete_volume_type(cls.volume_type['id'])
-        super(VolumeTypesExtraSpecsV2Test, cls).resource_cleanup()
+        cls.volume_type = cls.create_volume_type(name=vol_type_name)
 
     @test.idempotent_id('b42923e9-0452-4945-be5b-d362ae533e60')
     def test_volume_type_extra_specs_list(self):
@@ -54,20 +47,19 @@
             self.volume_type['id'], extra_specs)['extra_specs']
         self.assertEqual(extra_specs, body,
                          "Volume type extra spec incorrectly created")
-
-        extra_spec = {"spec2": "val2"}
+        spec_key = "spec2"
+        extra_spec = {spec_key: "val2"}
         body = self.admin_volume_types_client.update_volume_type_extra_specs(
-            self.volume_type['id'],
-            extra_spec.keys()[0],
-            extra_spec)
-        self.assertIn('spec2', body)
-        self.assertEqual(extra_spec['spec2'], body['spec2'],
+            self.volume_type['id'], spec_key, extra_spec)
+        self.assertIn(spec_key, body)
+        self.assertEqual(extra_spec[spec_key], body[spec_key],
                          "Volume type extra spec incorrectly updated")
 
     @test.idempotent_id('d4772798-601f-408a-b2a5-29e8a59d1220')
     def test_volume_type_extra_spec_create_get_delete(self):
         # Create/Get/Delete volume type extra spec.
-        extra_specs = {"spec3": "val1"}
+        spec_key = "spec3"
+        extra_specs = {spec_key: "val1"}
         body = self.admin_volume_types_client.create_volume_type_extra_specs(
             self.volume_type['id'],
             extra_specs)['extra_specs']
@@ -76,13 +68,11 @@
 
         self.admin_volume_types_client.show_volume_type_extra_specs(
             self.volume_type['id'],
-            extra_specs.keys()[0])
+            spec_key)
         self.assertEqual(extra_specs, body,
                          "Volume type extra spec incorrectly fetched")
-
         self.admin_volume_types_client.delete_volume_type_extra_specs(
-            self.volume_type['id'],
-            extra_specs.keys()[0])
+            self.volume_type['id'], spec_key)
 
 
 class VolumeTypesExtraSpecsV1Test(VolumeTypesExtraSpecsV2Test):
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
index 6983974..2193aa6 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
@@ -26,23 +26,16 @@
         super(ExtraSpecsNegativeV2Test, cls).resource_setup()
         vol_type_name = data_utils.rand_name('Volume-type')
         cls.extra_specs = {"spec1": "val1"}
-        cls.volume_type = cls.admin_volume_types_client.create_volume_type(
-            name=vol_type_name,
-            extra_specs=cls.extra_specs)['volume_type']
-
-    @classmethod
-    def resource_cleanup(cls):
-        cls.admin_volume_types_client.delete_volume_type(cls.volume_type['id'])
-        super(ExtraSpecsNegativeV2Test, cls).resource_cleanup()
+        cls.volume_type = cls.create_volume_type(name=vol_type_name,
+                                                 extra_specs=cls.extra_specs)
 
     @test.idempotent_id('08961d20-5cbb-4910-ac0f-89ad6dbb2da1')
     def test_update_no_body(self):
         # Should not update volume type extra specs with no body
-        extra_spec = {"spec1": "val2"}
         self.assertRaises(
             lib_exc.BadRequest,
             self.admin_volume_types_client.update_volume_type_extra_specs,
-            self.volume_type['id'], extra_spec.keys()[0], None)
+            self.volume_type['id'], "spec1", None)
 
     @test.idempotent_id('25e5a0ee-89b3-4c53-8310-236f76c75365')
     def test_update_nonexistent_extra_spec_id(self):
@@ -71,7 +64,7 @@
         self.assertRaises(
             lib_exc.BadRequest,
             self.admin_volume_types_client.update_volume_type_extra_specs,
-            self.volume_type['id'], extra_spec.keys()[0],
+            self.volume_type['id'], list(extra_spec)[0],
             extra_spec)
 
     @test.idempotent_id('49d5472c-a53d-4eab-a4d3-450c4db1c545')
@@ -103,12 +96,11 @@
     @test.idempotent_id('031cda8b-7d23-4246-8bf6-bbe73fd67074')
     def test_delete_nonexistent_volume_type_id(self):
         # Should not delete volume type extra spec for nonexistent
-            # type id.
-        extra_specs = {"spec1": "val1"}
+        # type id.
         self.assertRaises(
             lib_exc.NotFound,
             self.admin_volume_types_client.delete_volume_type_extra_specs,
-            data_utils.rand_uuid(), extra_specs.keys()[0])
+            data_utils.rand_uuid(), "spec1")
 
     @test.idempotent_id('dee5cf0c-cdd6-4353-b70c-e847050d71fb')
     def test_list_nonexistent_volume_type_id(self):
@@ -121,11 +113,10 @@
     @test.idempotent_id('9f402cbd-1838-4eb4-9554-126a6b1908c9')
     def test_get_nonexistent_volume_type_id(self):
         # Should not get volume type extra spec for nonexistent type id.
-        extra_specs = {"spec1": "val1"}
         self.assertRaises(
             lib_exc.NotFound,
             self.admin_volume_types_client.show_volume_type_extra_specs,
-            data_utils.rand_uuid(), extra_specs.keys()[0])
+            data_utils.rand_uuid(), "spec1")
 
     @test.idempotent_id('c881797d-12ff-4f1a-b09d-9f6212159753')
     def test_get_nonexistent_extra_spec_id(self):
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 665036b..e9be529 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -214,11 +214,13 @@
         super(BaseVolumeAdminTest, cls).resource_setup()
 
         cls.qos_specs = []
+        cls.volume_types = []
 
     @classmethod
     def resource_cleanup(cls):
         cls.clear_qos_specs()
         super(BaseVolumeAdminTest, cls).resource_cleanup()
+        cls.clear_volume_types()
 
     @classmethod
     def create_test_qos_specs(cls, name=None, consumer=None, **kwargs):
@@ -231,6 +233,15 @@
         return qos_specs
 
     @classmethod
+    def create_volume_type(cls, name=None, **kwargs):
+        """Create a test volume-type"""
+        name = name or data_utils.rand_name('volume-type')
+        volume_type = cls.admin_volume_types_client.create_volume_type(
+            name=name, **kwargs)['volume_type']
+        cls.volume_types.append(volume_type['id'])
+        return volume_type
+
+    @classmethod
     def clear_qos_specs(cls):
         for qos_id in cls.qos_specs:
             test_utils.call_and_ignore_notfound_exc(
@@ -239,3 +250,17 @@
         for qos_id in cls.qos_specs:
             test_utils.call_and_ignore_notfound_exc(
                 cls.admin_volume_qos_client.wait_for_resource_deletion, qos_id)
+
+    @classmethod
+    def clear_volume_types(cls):
+        for vol_type in cls.volume_types:
+            test_utils.call_and_ignore_notfound_exc(
+                cls.admin_volume_types_client.delete_volume_type, vol_type)
+
+        for vol_type in cls.volume_types:
+            # Resource dictionary uses for is_resource_deleted method,
+            # to distinguish between volume-type to encryption-type.
+            resource = {'id': vol_type, 'type': 'volume-type'}
+            test_utils.call_and_ignore_notfound_exc(
+                cls.admin_volume_types_client.wait_for_resource_deletion,
+                resource)
diff --git a/tempest/api/volume/test_volumes_clone.py b/tempest/api/volume/test_volumes_clone.py
new file mode 100644
index 0000000..f38a068
--- /dev/null
+++ b/tempest/api/volume/test_volumes_clone.py
@@ -0,0 +1,44 @@
+# Copyright 2016 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 tempest.api.volume import base
+from tempest import config
+from tempest import test
+
+
+CONF = config.CONF
+
+
+class VolumesCloneTest(base.BaseVolumeTest):
+
+    @test.idempotent_id('9adae371-a257-43a5-9555-dc7c88e66e0e')
+    def test_create_from_volume(self):
+        # Creates a volume from another volume passing a size different from
+        # the source volume.
+        src_size = CONF.volume.volume_size
+
+        src_vol = self.create_volume(size=src_size)
+        # Destination volume bigger than source
+        dst_vol = self.create_volume(source_volid=src_vol['id'],
+                                     size=src_size + 1)
+
+        volume = self.volumes_client.show_volume(dst_vol['id'])['volume']
+        # Should allow
+        self.assertEqual(volume['source_volid'], src_vol['id'])
+        self.assertEqual(int(volume['size']), src_size + 1)
+
+
+class VolumesV1CloneTest(VolumesCloneTest):
+    _api_version = 1
diff --git a/tempest/api/volume/test_volumes_clone_negative.py b/tempest/api/volume/test_volumes_clone_negative.py
new file mode 100644
index 0000000..ee51e00
--- /dev/null
+++ b/tempest/api/volume/test_volumes_clone_negative.py
@@ -0,0 +1,42 @@
+# Copyright 2016 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 tempest.api.volume import base
+from tempest import config
+from tempest.lib import exceptions
+from tempest import test
+
+
+CONF = config.CONF
+
+
+class VolumesCloneTest(base.BaseVolumeTest):
+
+    @test.idempotent_id('9adae371-a257-43a5-459a-dc7c88e66e0e')
+    def test_create_from_volume_decreasing_size(self):
+        # Creates a volume from another volume passing a size different from
+        # the source volume.
+        src_size = CONF.volume.volume_size + 1
+        src_vol = self.create_volume(size=src_size)
+
+        # Destination volume smaller than source
+        self.assertRaises(exceptions.BadRequest,
+                          self.volumes_client.create_volume,
+                          size=src_size - 1,
+                          source_volid=src_vol['id'])
+
+
+class VolumesV1CloneTest(VolumesCloneTest):
+    _api_version = 1
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 0f7c4f6..c7f1e6e 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -178,16 +178,19 @@
 
     @test.idempotent_id('677863d1-3142-456d-b6ac-9924f667a7f4')
     def test_volume_from_snapshot(self):
-        # Create a temporary snap using wrapper method from base, then
-        # create a snap based volume and deletes it
-        snapshot = self.create_snapshot(self.volume_origin['id'])
-        # NOTE(gfidente): size is required also when passing snapshot_id
-        volume = self.volumes_client.create_volume(
-            snapshot_id=snapshot['id'])['volume']
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       volume['id'], 'available')
-        self.delete_volume(self.volumes_client, volume['id'])
-        self.cleanup_snapshot(snapshot)
+        # Creates a volume a snapshot passing a size different from the source
+        src_size = CONF.volume.volume_size
+
+        src_vol = self.create_volume(size=src_size)
+        src_snap = self.create_snapshot(src_vol['id'])
+        # Destination volume bigger than source snapshot
+        dst_vol = self.create_volume(snapshot_id=src_snap['id'],
+                                     size=src_size + 1)
+
+        volume = self.volumes_client.show_volume(dst_vol['id'])['volume']
+        # Should allow
+        self.assertEqual(volume['snapshot_id'], src_snap['id'])
+        self.assertEqual(int(volume['size']), src_size + 1)
 
     @test.idempotent_id('db4d8e0a-7a2e-41cc-a712-961f6844e896')
     def test_snapshot_list_param_limit(self):
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index 374979c..2df9523 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -46,6 +46,20 @@
                           self.snapshots_client.create_snapshot,
                           volume_id=None, display_name=s_name)
 
+    @test.idempotent_id('677863d1-34f9-456d-b6ac-9924f667a7f4')
+    def test_volume_from_snapshot_decreasing_size(self):
+        # Creates a volume a snapshot passing a size different from the source
+        src_size = CONF.volume.volume_size + 1
+
+        src_vol = self.create_volume(size=src_size)
+        src_snap = self.create_snapshot(src_vol['id'])
+
+        # Destination volume smaller than source
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.create_volume,
+                          size=src_size - 1,
+                          snapshot_id=src_snap['id'])
+
 
 class VolumesV1SnapshotNegativeTestJSON(VolumesV2SnapshotNegativeTestJSON):
     _api_version = 1
diff --git a/tempest/clients.py b/tempest/clients.py
index b7bc4fa..ef03e80 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -20,14 +20,14 @@
 from tempest.common import negative_rest_client
 from tempest import config
 from tempest import exceptions
+from tempest.lib import auth
 from tempest.lib.services import compute
+from tempest.lib.services import image
 from tempest.lib.services import network
-from tempest import manager
+from tempest import service_clients
 from tempest.services import baremetal
 from tempest.services import data_processing
-from tempest.services import database
 from tempest.services import identity
-from tempest.services import image
 from tempest.services import object_storage
 from tempest.services import orchestration
 from tempest.services import volume
@@ -36,7 +36,7 @@
 LOG = logging.getLogger(__name__)
 
 
-class Manager(manager.Manager):
+class Manager(service_clients.ServiceClients):
     """Top level manager for OpenStack tempest clients"""
 
     default_params = {
@@ -62,9 +62,11 @@
         :param service: Service name
         :param scope: default scope for tokens produced by the auth provider
         """
-        super(Manager, self).__init__(credentials=credentials, scope=scope)
+        _, identity_uri = get_auth_provider_class(credentials)
+        super(Manager, self).__init__(
+            credentials=credentials, identity_uri=identity_uri, scope=scope,
+            region=CONF.identity.region, **self.default_params)
         self._set_compute_clients()
-        self._set_database_clients()
         self._set_identity_clients()
         self._set_volume_clients()
         self._set_object_storage_clients()
@@ -243,23 +245,6 @@
         self.snapshots_extensions_client = compute.SnapshotsClient(
             self.auth_provider, **params_volume)
 
-    def _set_database_clients(self):
-        self.database_flavors_client = database.DatabaseFlavorsClient(
-            self.auth_provider,
-            CONF.database.catalog_type,
-            CONF.identity.region,
-            **self.default_params_with_timeout_values)
-        self.database_limits_client = database.DatabaseLimitsClient(
-            self.auth_provider,
-            CONF.database.catalog_type,
-            CONF.identity.region,
-            **self.default_params_with_timeout_values)
-        self.database_versions_client = database.DatabaseVersionsClient(
-            self.auth_provider,
-            CONF.database.catalog_type,
-            CONF.identity.region,
-            **self.default_params_with_timeout_values)
-
     def _set_identity_clients(self):
         params = {
             'service': CONF.identity.catalog_type,
@@ -409,3 +394,30 @@
             self.auth_provider, **params)
         self.object_client = object_storage.ObjectClient(self.auth_provider,
                                                          **params)
+
+
+def get_auth_provider_class(credentials):
+    if isinstance(credentials, auth.KeystoneV3Credentials):
+        return auth.KeystoneV3AuthProvider, CONF.identity.uri_v3
+    else:
+        return auth.KeystoneV2AuthProvider, CONF.identity.uri
+
+
+def get_auth_provider(credentials, pre_auth=False, scope='project'):
+    default_params = {
+        'disable_ssl_certificate_validation':
+            CONF.identity.disable_ssl_certificate_validation,
+        'ca_certs': CONF.identity.ca_certificates_file,
+        'trace_requests': CONF.debug.trace_requests
+    }
+    if credentials is None:
+        raise exceptions.InvalidCredentials(
+            'Credentials must be specified')
+    auth_provider_class, auth_url = get_auth_provider_class(
+        credentials)
+    _auth_provider = auth_provider_class(credentials, auth_url,
+                                         scope=scope,
+                                         **default_params)
+    if pre_auth:
+        _auth_provider.set_auth()
+    return _auth_provider
diff --git a/tempest/cmd/javelin.py b/tempest/cmd/javelin.py
index 08ad94f..a9e5167 100755
--- a/tempest/cmd/javelin.py
+++ b/tempest/cmd/javelin.py
@@ -126,15 +126,15 @@
 from tempest.lib.services.compute import security_group_rules_client
 from tempest.lib.services.compute import security_groups_client
 from tempest.lib.services.compute import servers_client
+from tempest.lib.services.identity.v2 import roles_client
+from tempest.lib.services.identity.v2 import tenants_client
+from tempest.lib.services.identity.v2 import users_client
 from tempest.lib.services.image.v2 import images_client
 from tempest.lib.services.network import networks_client
 from tempest.lib.services.network import ports_client
 from tempest.lib.services.network import routers_client
 from tempest.lib.services.network import subnets_client
 from tempest.services.identity.v2.json import identity_client
-from tempest.services.identity.v2.json import roles_client
-from tempest.services.identity.v2.json import tenants_client
-from tempest.services.identity.v2.json import users_client
 from tempest.services.object_storage import container_client
 from tempest.services.object_storage import object_client
 from tempest.services.volume.v1.json import volumes_client
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
index 26bd418..5270cac 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -23,6 +23,27 @@
                    any tests that match on re.match() with the regex
  * **--smoke**: Run all the tests tagged as smoke
 
+There are also the **--blacklist_file** and **--whitelist_file** options that
+let you pass a filepath to tempest run with the file format being a line
+seperated regex, with '#' used to signify the start of a comment on a line.
+For example::
+
+    # Regex file
+    ^regex1 # Match these tests
+    .*regex2 # Match those tests
+
+The blacklist file will be used to construct a negative lookahead regex and
+the whitelist file will simply OR all the regexes in the file. The whitelist
+and blacklist file options are mutually exclusive so you can't use them
+together. However, you can combine either with a normal regex or the *--smoke*
+flag. When used with a blacklist file the generated regex will be combined to
+something like::
+
+    ^((?!black_regex1|black_regex2).)*$cli_regex1
+
+When combined with a whitelist file all the regexes from the file and the CLI
+regexes will be ORed.
+
 You can also use the **--list-tests** option in conjunction with selection
 arguments to list which tests will be run.
 
@@ -33,6 +54,24 @@
 If you want to adjust the number of workers use the **--concurrency** option
 and if you want to run tests serially use **--serial**
 
+Running with Workspaces
+-----------------------
+Tempest run enables you to run your tempest tests from any setup tempest
+workspace it relies on you having setup a tempest workspace with either the
+``tempest init`` or ``tempest workspace`` commands. Then using the
+``--workspace`` CLI option you can specify which one of your workspaces you
+want to run tempest from. Using this option you don't have to run Tempest
+directly with you current working directory being the workspace, Tempest will
+take care of managing everything to be executed from there.
+
+Running from Anywhere
+---------------------
+Tempest run provides you with an option to execute tempest from anywhere on
+your system. You are required to provide a config file in this case with the
+``--config-file`` option. When run tempest will create a .testrepository
+directory and a .testr.conf file in your current working directory. This way
+you can use testr commands directly to inspect the state of the previous run.
+
 Test Output
 ===========
 By default tempest run's output to STDOUT will be generated using the
@@ -47,10 +86,13 @@
 import threading
 
 from cliff import command
+from os_testr import regex_builder
 from os_testr import subunit_trace
 from oslo_log import log as logging
 from testrepository.commands import run_argv
 
+from tempest.cmd import init
+from tempest.cmd import workspace
 from tempest import config
 
 
@@ -60,7 +102,9 @@
 
 class TempestRun(command.Command):
 
-    def _set_env(self):
+    def _set_env(self, config_file=None):
+        if config_file:
+            CONF.set_config_path(os.path.abspath(config_file))
         # NOTE(mtreinish): This is needed so that testr doesn't gobble up any
         # stacktraces on failure.
         if 'TESTR_PDB' in os.environ:
@@ -68,18 +112,45 @@
         else:
             os.environ["TESTR_PDB"] = ""
 
+    def _create_testrepository(self):
+        if not os.path.isdir('.testrepository'):
+            returncode = run_argv(['testr', 'init'], sys.stdin, sys.stdout,
+                                  sys.stderr)
+            if returncode:
+                sys.exit(returncode)
+
+    def _create_testr_conf(self):
+        top_level_path = os.path.dirname(os.path.dirname(__file__))
+        discover_path = os.path.join(top_level_path, 'test_discover')
+        file_contents = init.TESTR_CONF % (top_level_path, discover_path)
+        with open('.testr.conf', 'w+') as testr_conf_file:
+                testr_conf_file.write(file_contents)
+
     def take_action(self, parsed_args):
-        self._set_env()
         returncode = 0
+        if parsed_args.config_file:
+            self._set_env(parsed_args.config_file)
+        else:
+            self._set_env()
+        # Workspace execution mode
+        if parsed_args.workspace:
+            workspace_mgr = workspace.WorkspaceManager(
+                parsed_args.workspace_path)
+            path = workspace_mgr.get_workspace(parsed_args.workspace)
+            os.chdir(path)
+            # NOTE(mtreinish): tempest init should create a .testrepository dir
+            # but since workspaces can be imported let's sanity check and
+            # ensure that one is created
+            self._create_testrepository()
         # Local execution mode
-        if os.path.isfile('.testr.conf'):
+        elif os.path.isfile('.testr.conf'):
             # If you're running in local execution mode and there is not a
             # testrepository dir create one
-            if not os.path.isdir('.testrepository'):
-                returncode = run_argv(['testr', 'init'], sys.stdin, sys.stdout,
-                                      sys.stderr)
-                if returncode:
-                    sys.exit(returncode)
+            self._create_testrepository()
+        # local execution with config file mode
+        elif parsed_args.config_file:
+            self._create_testr_conf()
+            self._create_testrepository()
         else:
             print("No .testr.conf file was found for local execution")
             sys.exit(2)
@@ -102,6 +173,18 @@
         return parser
 
     def _add_args(self, parser):
+        # workspace args
+        parser.add_argument('--workspace', default=None,
+                            help='Name of tempest workspace to use for running'
+                                 ' tests. You can see a list of workspaces '
+                                 'with tempest workspace list')
+        parser.add_argument('--workspace-path', default=None,
+                            dest='workspace_path',
+                            help="The path to the workspace file, the default "
+                                 "is ~/.tempest/workspace.yaml")
+        # Configuration flags
+        parser.add_argument('--config-file', default=None, dest='config_file',
+                            help='Configuration file to run tempest with')
         # test selection args
         regex = parser.add_mutually_exclusive_group()
         regex.add_argument('--smoke', action='store_true',
@@ -109,6 +192,15 @@
         regex.add_argument('--regex', '-r', default='',
                            help='A normal testr selection regex used to '
                                 'specify a subset of tests to run')
+        list_selector = parser.add_mutually_exclusive_group()
+        list_selector.add_argument('--whitelist_file',
+                                   help="Path to a whitelist file, this file "
+                                        "contains a seperate regex on each "
+                                        "newline.")
+        list_selector.add_argument('--blacklist_file',
+                                   help='Path to a blacklist file, this file '
+                                        'contains a separate regex exclude on '
+                                        'each newline')
         # list only args
         parser.add_argument('--list-tests', '-l', action='store_true',
                             help='List tests',
@@ -138,6 +230,10 @@
             regex = 'smoke'
         elif parsed_args.regex:
             regex = parsed_args.regex
+        if parsed_args.whitelist_file or parsed_args.blacklist_file:
+            regex = regex_builder.construct_regex(parsed_args.blacklist_file,
+                                                  parsed_args.whitelist_file,
+                                                  regex, False)
         return regex
 
     def _build_options(self, parsed_args):
diff --git a/tempest/cmd/subunit_describe_calls.py b/tempest/cmd/subunit_describe_calls.py
new file mode 100644
index 0000000..da7f426
--- /dev/null
+++ b/tempest/cmd/subunit_describe_calls.py
@@ -0,0 +1,262 @@
+# Copyright 2016 Rackspace
+#
+# 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.
+
+"""
+subunit-describe-calls is a parser for subunit streams to determine what REST
+API calls are made inside of a test and in what order they are called.
+
+Runtime Arguments
+-----------------
+
+**--subunit, -s**: (Required) The path to the subunit file being parsed
+
+**--non-subunit-name, -n**: (Optional) The file_name that the logs are being
+stored in
+
+**--output-file, -o**: (Required) The path where the JSON output will be
+written to
+
+**--ports, -p**: (Optional) The path to a JSON file describing the ports being
+used by different services
+
+Usage
+-----
+
+subunit-describe-calls will take in a file path via the --subunit parameter
+which contains either a subunit v1 or v2 stream. This is then parsed checking
+for details contained in the file_bytes of the --non-subunit-name parameter
+(the default is pythonlogging which is what Tempest uses to store logs). By
+default the OpenStack Kilo release port defaults (http://bit.ly/22jpF5P)
+are used unless a file is provided via the --ports option. The resulting output
+is dumped in JSON output to the path provided in the --output-file option.
+
+Ports file JSON structure
+^^^^^^^^^^^^^^^^^^^^^^^^^
+::
+
+  {
+      "<port number>": "<name of service>",
+      ...
+  }
+
+
+Output file JSON structure
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+::
+
+  {
+      "full_test_name[with_id_and_tags]": [
+          {
+              "name": "The ClassName.MethodName that made the call",
+              "verb": "HTTP Verb",
+              "service": "Name of the service",
+              "url": "A shortened version of the URL called",
+              "status_code": "The status code of the response"
+          }
+      ]
+  }
+"""
+import argparse
+import collections
+import io
+import json
+import os
+import re
+
+import subunit
+import testtools
+
+
+class UrlParser(testtools.TestResult):
+    uuid_re = re.compile(r'(^|[^0-9a-f])[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-'
+                         '[0-9a-f]{4}-[0-9a-f]{12}([^0-9a-f]|$)')
+    id_re = re.compile(r'(^|[^0-9a-z])[0-9a-z]{8}[0-9a-z]{4}[0-9a-z]{4}'
+                       '[0-9a-z]{4}[0-9a-z]{12}([^0-9a-z]|$)')
+    ip_re = re.compile(r'(^|[^0-9])[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]'
+                       '{1,3}([^0-9]|$)')
+    url_re = re.compile(r'.*INFO.*Request \((?P<name>.*)\): (?P<code>[\d]{3}) '
+                        '(?P<verb>\w*) (?P<url>.*) .*')
+    port_re = re.compile(r'.*:(?P<port>\d+).*')
+    path_re = re.compile(r'http[s]?://[^/]*/(?P<path>.*)')
+
+    # Based on mitaka defaults:
+    # http://docs.openstack.org/mitaka/config-reference/
+    # firewalls-default-ports.html
+    services = {
+        "8776": "Block Storage",
+        "8774": "Nova",
+        "8773": "Nova-API", "8775": "Nova-API",
+        "8386": "Sahara",
+        "35357": "Keystone", "5000": "Keystone",
+        "9292": "Glance", "9191": "Glance",
+        "9696": "Neutron",
+        "6000": "Swift", "6001": "Swift", "6002": "Swift",
+        "8004": "Heat", "8000": "Heat", "8003": "Heat",
+        "8777": "Ceilometer",
+        "80": "Horizon",
+        "8080": "Swift",
+        "443": "SSL",
+        "873": "rsync",
+        "3260": "iSCSI",
+        "3306": "MySQL",
+        "5672": "AMQP"}
+
+    def __init__(self, services=None):
+        super(UrlParser, self).__init__()
+        self.test_logs = {}
+        self.services = services or self.services
+
+    def addSuccess(self, test, details=None):
+        output = test.shortDescription() or test.id()
+        calls = self.parse_details(details)
+        self.test_logs.update({output: calls})
+
+    def addSkip(self, test, err, details=None):
+        output = test.shortDescription() or test.id()
+        calls = self.parse_details(details)
+        self.test_logs.update({output: calls})
+
+    def addError(self, test, err, details=None):
+        output = test.shortDescription() or test.id()
+        calls = self.parse_details(details)
+        self.test_logs.update({output: calls})
+
+    def addFailure(self, test, err, details=None):
+        output = test.shortDescription() or test.id()
+        calls = self.parse_details(details)
+        self.test_logs.update({output: calls})
+
+    def stopTestRun(self):
+        super(UrlParser, self).stopTestRun()
+
+    def startTestRun(self):
+        super(UrlParser, self).startTestRun()
+
+    def parse_details(self, details):
+        if details is None:
+            return
+
+        calls = []
+        for _, detail in details.items():
+            for line in detail.as_text().split("\n"):
+                match = self.url_re.match(line)
+                if match is not None:
+                    calls.append({
+                        "name": match.group("name"),
+                        "verb": match.group("verb"),
+                        "status_code": match.group("code"),
+                        "service": self.get_service(match.group("url")),
+                        "url": self.url_path(match.group("url"))})
+
+        return calls
+
+    def get_service(self, url):
+        match = self.port_re.match(url)
+        if match is not None:
+            return self.services.get(match.group("port"), "Unknown")
+        return "Unknown"
+
+    def url_path(self, url):
+        match = self.path_re.match(url)
+        if match is not None:
+            path = match.group("path")
+            path = self.uuid_re.sub(r'\1<uuid>\2', path)
+            path = self.ip_re.sub(r'\1<ip>\2', path)
+            path = self.id_re.sub(r'\1<id>\2', path)
+            return path
+        return url
+
+
+class FileAccumulator(testtools.StreamResult):
+
+    def __init__(self, non_subunit_name='pythonlogging'):
+        super(FileAccumulator, self).__init__()
+        self.route_codes = collections.defaultdict(io.BytesIO)
+        self.non_subunit_name = non_subunit_name
+
+    def status(self, **kwargs):
+        if kwargs.get('file_name') != self.non_subunit_name:
+            return
+        file_bytes = kwargs.get('file_bytes')
+        if not file_bytes:
+            return
+        route_code = kwargs.get('route_code')
+        stream = self.route_codes[route_code]
+        stream.write(file_bytes)
+
+
+class ArgumentParser(argparse.ArgumentParser):
+    def __init__(self):
+        desc = "Outputs all HTTP calls a given test made that were logged."
+        super(ArgumentParser, self).__init__(description=desc)
+
+        self.prog = "Argument Parser"
+
+        self.add_argument(
+            "-s", "--subunit", metavar="<subunit file>", required=True,
+            default=None, help="The path to the subunit output file.")
+
+        self.add_argument(
+            "-n", "--non-subunit-name", metavar="<non subunit name>",
+            default="pythonlogging",
+            help="The name used in subunit to describe the file contents.")
+
+        self.add_argument(
+            "-o", "--output-file", metavar="<output file>", default=None,
+            help="The output file name for the json.", required=True)
+
+        self.add_argument(
+            "-p", "--ports", metavar="<ports file>", default=None,
+            help="A JSON file describing the ports for each service.")
+
+
+def parse(subunit_file, non_subunit_name, ports):
+    if ports is not None and os.path.exists(ports):
+        ports = json.loads(open(ports).read())
+
+    url_parser = UrlParser(ports)
+    stream = open(subunit_file, 'rb')
+    suite = subunit.ByteStreamToStreamResult(
+        stream, non_subunit_name=non_subunit_name)
+    result = testtools.StreamToExtendedDecorator(url_parser)
+    accumulator = FileAccumulator(non_subunit_name)
+    result = testtools.StreamResultRouter(result)
+    result.add_rule(accumulator, 'test_id', test_id=None)
+    result.startTestRun()
+    suite.run(result)
+
+    for bytes_io in accumulator.route_codes.values():  # v1 processing
+        bytes_io.seek(0)
+        suite = subunit.ProtocolTestCase(bytes_io)
+        suite.run(url_parser)
+    result.stopTestRun()
+
+    return url_parser
+
+
+def output(url_parser, output_file):
+    with open(output_file, "w") as outfile:
+        outfile.write(json.dumps(url_parser.test_logs))
+
+
+def entry_point():
+    cl_args = ArgumentParser().parse_args()
+    parser = parse(cl_args.subunit, cl_args.non_subunit_name, cl_args.ports)
+    output(parser, cl_args.output_file)
+
+
+if __name__ == "__main__":
+    entry_point()
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 4b12ecb..77b88f9 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -285,7 +285,6 @@
         'data_processing': 'sahara',
         'baremetal': 'ironic',
         'identity': 'keystone',
-        'database': 'trove'
     }
     # Get catalog list for endpoints to use for validation
     _token, auth_data = os.auth_provider.get_auth()
diff --git a/tempest/common/dynamic_creds.py b/tempest/common/dynamic_creds.py
index b0c01f5..c9b9db1 100644
--- a/tempest/common/dynamic_creds.py
+++ b/tempest/common/dynamic_creds.py
@@ -159,7 +159,10 @@
         # it must beassigned a role on the project. So we need to ensure that
         # our newly created user has a role on the newly created project.
         if self.identity_version == 'v3' and not role_assigned:
-            self.creds_client.create_user_role('Member')
+            try:
+                self.creds_client.create_user_role('Member')
+            except lib_exc.Conflict:
+                LOG.warning('Member role already exists, ignoring conflict.')
             self.creds_client.assign_user_role(user, project, 'Member')
 
         creds = self.creds_client.get_credentials(user, project, user_password)
diff --git a/tempest/common/image.py b/tempest/common/image.py
index 42ce5ac..72e3a72 100644
--- a/tempest/common/image.py
+++ b/tempest/common/image.py
@@ -13,6 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import copy
+
+import six
+
 
 def get_image_meta_from_headers(resp):
     meta = {'properties': {}}
@@ -36,3 +40,23 @@
             except ValueError:
                 pass
     return meta
+
+
+def image_meta_to_headers(**metadata):
+    headers = {}
+    fields_copy = copy.deepcopy(metadata)
+
+    copy_from = fields_copy.pop('copy_from', None)
+    if copy_from is not None:
+        headers['x-glance-api-copy-from'] = copy_from
+
+    for key, value in six.iteritems(fields_copy.pop('properties', {})):
+        headers['x-image-meta-property-%s' % key] = str(value)
+
+    for key, value in six.iteritems(fields_copy.pop('api', {})):
+        headers['x-glance-api-property-%s' % key] = str(value)
+
+    for key, value in six.iteritems(fields_copy):
+        headers['x-image-meta-%s' % key] = str(value)
+
+    return headers
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index d8dad69..e083167 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -20,7 +20,7 @@
 from tempest import exceptions
 from tempest.lib.common.utils import misc as misc_utils
 from tempest.lib import exceptions as lib_exc
-from tempest.services.image.v1.json import images_client as images_v1_client
+from tempest.lib.services.image.v1 import images_client as images_v1_client
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
diff --git a/tempest/config.py b/tempest/config.py
index caaebea..8ec8b24 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -816,21 +816,6 @@
                 help="Execute discoverability tests"),
 ]
 
-database_group = cfg.OptGroup(name='database',
-                              title='Database Service Options')
-
-DatabaseGroup = [
-    cfg.StrOpt('catalog_type',
-               default='database',
-               help="Catalog type of the Database service."),
-    cfg.StrOpt('db_flavor_ref',
-               default="1",
-               help="Valid primary flavor to use in database tests."),
-    cfg.StrOpt('db_current_version',
-               default="v1.0",
-               help="Current database version to use in database tests."),
-]
-
 orchestration_group = cfg.OptGroup(name='orchestration',
                                    title='Orchestration Service Options')
 
@@ -1007,9 +992,6 @@
     cfg.BoolOpt('ironic',
                 default=False,
                 help="Whether or not Ironic is expected to be available"),
-    cfg.BoolOpt('trove',
-                default=False,
-                help="Whether or not Trove is expected to be available"),
 ]
 
 debug_group = cfg.OptGroup(name="debug",
@@ -1146,7 +1128,6 @@
     (volume_feature_group, VolumeFeaturesGroup),
     (object_storage_group, ObjectStoreGroup),
     (object_storage_feature_group, ObjectStoreFeaturesGroup),
-    (database_group, DatabaseGroup),
     (orchestration_group, OrchestrationGroup),
     (data_processing_group, DataProcessingGroup),
     (data_processing_feature_group, DataProcessingFeaturesGroup),
@@ -1213,7 +1194,6 @@
         self.object_storage = _CONF['object-storage']
         self.object_storage_feature_enabled = _CONF[
             'object-storage-feature-enabled']
-        self.database = _CONF.database
         self.orchestration = _CONF.orchestration
         self.data_processing = _CONF['data-processing']
         self.data_processing_feature_enabled = _CONF[
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 09106d1..e2d6585 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -19,7 +19,7 @@
 
 
 PYTHON_CLIENTS = ['cinder', 'glance', 'keystone', 'nova', 'swift', 'neutron',
-                  'trove', 'ironic', 'savanna', 'heat', 'sahara']
+                  'ironic', 'savanna', 'heat', 'sahara']
 
 PYTHON_CLIENT_RE = re.compile('import (%s)client' % '|'.join(PYTHON_CLIENTS))
 TEST_DEFINITION = re.compile(r'^\s*def test.*')
diff --git a/tempest/lib/services/compute/aggregates_client.py b/tempest/lib/services/compute/aggregates_client.py
index ae747d8..7ad14bc 100644
--- a/tempest/lib/services/compute/aggregates_client.py
+++ b/tempest/lib/services/compute/aggregates_client.py
@@ -41,7 +41,7 @@
         """Create a new aggregate.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#createaggregate
+                              api-ref-compute-v2.1.html#createAggregate
         """
         post_body = json.dumps({'aggregate': kwargs})
         resp, body = self.post('os-aggregates', post_body)
@@ -54,7 +54,7 @@
         """Update an aggregate.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#updateaggregate
+                              api-ref-compute-v2.1.html#updateAggregate
         """
         put_body = json.dumps({'aggregate': kwargs})
         resp, body = self.put('os-aggregates/%s' % aggregate_id, put_body)
@@ -85,7 +85,7 @@
         """Add a host to the given aggregate.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#addhost
+                              api-ref-compute-v2.1.html#addHost
         """
         post_body = json.dumps({'add_host': kwargs})
         resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
@@ -98,7 +98,7 @@
         """Remove a host from the given aggregate.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#removehost
+                              api-ref-compute-v2.1.html#removeAggregateHost
         """
         post_body = json.dumps({'remove_host': kwargs})
         resp, body = self.post('os-aggregates/%s/action' % aggregate_id,
diff --git a/tempest/lib/services/compute/flavors_client.py b/tempest/lib/services/compute/flavors_client.py
index 0d80a82..5be8272 100644
--- a/tempest/lib/services/compute/flavors_client.py
+++ b/tempest/lib/services/compute/flavors_client.py
@@ -91,7 +91,7 @@
         """Set extra Specs to the mentioned flavor.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#updateFlavorExtraSpec
+                              api-ref-compute-v2.1.html#createFlavorExtraSpec
         """
         post_body = json.dumps({'extra_specs': kwargs})
         resp, body = self.post('flavors/%s/os-extra_specs' % flavor_id,
@@ -123,7 +123,7 @@
         """Update specified extra Specs of the mentioned flavor and key.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#updateflavorspec
+                              api-ref-compute-v2.1.html#updateFlavorExtraSpec
         """
         resp, body = self.put('flavors/%s/os-extra_specs/%s' %
                               (flavor_id, key), json.dumps(kwargs))
diff --git a/tempest/lib/services/compute/migrations_client.py b/tempest/lib/services/compute/migrations_client.py
index 5eae8aa..62246d3 100644
--- a/tempest/lib/services/compute/migrations_client.py
+++ b/tempest/lib/services/compute/migrations_client.py
@@ -26,7 +26,7 @@
         """List all migrations.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#returnmigrations
+                              api-ref-compute-v2.1.html#listMigrations
         """
 
         url = 'os-migrations'
diff --git a/tempest/lib/services/compute/quotas_client.py b/tempest/lib/services/compute/quotas_client.py
index 184a3d7..6d41f4b 100644
--- a/tempest/lib/services/compute/quotas_client.py
+++ b/tempest/lib/services/compute/quotas_client.py
@@ -46,7 +46,7 @@
         """Updates the tenant's quota limits for one or more resources.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-compute-v2.1.html#updatesquotatenant
+                              api-ref-compute-v2.1.html#updateQuota
         """
 
         post_body = json.dumps({'quota_set': kwargs})
diff --git a/tempest/lib/services/identity/v2/roles_client.py b/tempest/lib/services/identity/v2/roles_client.py
new file mode 100644
index 0000000..15c8834
--- /dev/null
+++ b/tempest/lib/services/identity/v2/roles_client.py
@@ -0,0 +1,107 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class RolesClient(rest_client.RestClient):
+    api_version = "v2.0"
+
+    def create_role(self, **kwargs):
+        """Create a role.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v2-ext.html#createRole
+        """
+        post_body = json.dumps({'role': kwargs})
+        resp, body = self.post('OS-KSADM/roles', post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_role(self, role_id_or_name):
+        """Get a role by its id or name.
+
+        Available params: see
+            http://developer.openstack.org/
+            api-ref-identity-v2-ext.html#showRoleByID
+            OR
+            http://developer.openstack.org/
+            api-ref-identity-v2-ext.html#showRoleByName
+        """
+        resp, body = self.get('OS-KSADM/roles/%s' % role_id_or_name)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_roles(self, **params):
+        """Returns roles.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v2-ext.html#listRoles
+        """
+        url = 'OS-KSADM/roles'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_role(self, role_id):
+        """Delete a role.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v2-ext.html#deleteRole
+        """
+        resp, body = self.delete('OS-KSADM/roles/%s' % str(role_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_user_role_on_project(self, tenant_id, user_id, role_id):
+        """Add roles to a user on a tenant.
+
+        Available params: see
+            http://developer.openstack.org/
+            api-ref-identity-v2-ext.html#grantRoleToUserOnTenant
+        """
+        resp, body = self.put('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
+                              (tenant_id, user_id, role_id), "")
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_user_roles_on_project(self, tenant_id, user_id, **params):
+        """Returns a list of roles assigned to a user for a tenant."""
+        # TODO(gmann): Need to write API-ref link, Bug# 1592711
+        url = '/tenants/%s/users/%s/roles' % (tenant_id, user_id)
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_role_from_user_on_project(self, tenant_id, user_id, role_id):
+        """Removes a role assignment for a user on a tenant.
+
+        Available params: see
+            http://developer.openstack.org/
+            api-ref-identity-v2-ext.html#revokeRoleFromUserOnTenant
+        """
+        resp, body = self.delete('/tenants/%s/users/%s/roles/OS-KSADM/%s' %
+                                 (tenant_id, user_id, role_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/json/services_client.py b/tempest/lib/services/identity/v2/services_client.py
similarity index 100%
rename from tempest/services/identity/v2/json/services_client.py
rename to tempest/lib/services/identity/v2/services_client.py
diff --git a/tempest/lib/services/identity/v2/tenants_client.py b/tempest/lib/services/identity/v2/tenants_client.py
new file mode 100644
index 0000000..77ddaa5
--- /dev/null
+++ b/tempest/lib/services/identity/v2/tenants_client.py
@@ -0,0 +1,98 @@
+# Copyright 2015 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class TenantsClient(rest_client.RestClient):
+    api_version = "v2.0"
+
+    def create_tenant(self, **kwargs):
+        """Create a tenant
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v2-ext.html#createTenant
+        """
+        post_body = json.dumps({'tenant': kwargs})
+        resp, body = self.post('tenants', post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_tenant(self, tenant_id):
+        """Delete a tenant.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v2-ext.html#deleteTenant
+        """
+        resp, body = self.delete('tenants/%s' % str(tenant_id))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_tenant(self, tenant_id):
+        """Get tenant details.
+
+        Available params: see
+            http://developer.openstack.org/
+            api-ref-identity-v2-ext.html#admin-showTenantById
+        """
+        resp, body = self.get('tenants/%s' % str(tenant_id))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_tenants(self, **params):
+        """Returns tenants.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v2-ext.html#admin-listTenants
+        """
+        url = 'tenants'
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_tenant(self, tenant_id, **kwargs):
+        """Updates a tenant.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v2-ext.html#updateTenant
+        """
+        if 'id' not in kwargs:
+            kwargs['id'] = tenant_id
+        post_body = json.dumps({'tenant': kwargs})
+        resp, body = self.post('tenants/%s' % tenant_id, post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_tenant_users(self, tenant_id, **params):
+        """List users for a Tenant.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v2-ext.html#listUsersForTenant
+        """
+        url = '/tenants/%s/users' % tenant_id
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/identity/v2/users_client.py b/tempest/lib/services/identity/v2/users_client.py
new file mode 100644
index 0000000..4ea17f9
--- /dev/null
+++ b/tempest/lib/services/identity/v2/users_client.py
@@ -0,0 +1,152 @@
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+from six.moves.urllib import parse as urllib
+
+from tempest.lib.common import rest_client
+
+
+class UsersClient(rest_client.RestClient):
+    api_version = "v2.0"
+
+    def create_user(self, **kwargs):
+        """Create a user.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-admin-v2.html#admin-createUser
+        """
+        post_body = json.dumps({'user': kwargs})
+        resp, body = self.post('users', post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_user(self, user_id, **kwargs):
+        """Updates a user.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-admin-v2.html#admin-updateUser
+        """
+        put_body = json.dumps({'user': kwargs})
+        resp, body = self.put('users/%s' % user_id, put_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_user(self, user_id):
+        """GET a user.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-admin-v2.html#admin-showUser
+        """
+        resp, body = self.get("users/%s" % user_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_user(self, user_id):
+        """Delete a user.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-admin-v2.html#admin-deleteUser
+        """
+        resp, body = self.delete("users/%s" % user_id)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_users(self, **params):
+        """Get the list of users.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-admin-v2.html#admin-listUsers
+        """
+        url = "users"
+        if params:
+            url += '?%s' % urllib.urlencode(params)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_user_enabled(self, user_id, **kwargs):
+        """Enables or disables a user.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-identity-v2-ext.html#enableUser
+        """
+        # NOTE: The URL (users/<id>/enabled) is different from the api-site
+        # one (users/<id>/OS-KSADM/enabled) , but they are the same API
+        # because of the fact that in keystone/contrib/admin_crud/core.py
+        # both api use same action='set_user_enabled'
+        put_body = json.dumps({'user': kwargs})
+        resp, body = self.put('users/%s/enabled' % user_id, put_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_user_password(self, user_id, **kwargs):
+        """Update User Password."""
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1524147
+        put_body = json.dumps({'user': kwargs})
+        resp, body = self.put('users/%s/OS-KSADM/password' % user_id, put_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_user_own_password(self, user_id, **kwargs):
+        """User updates own password"""
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        # LP: https://bugs.launchpad.net/openstack-api-site/+bug/1524153
+        # NOTE: This API is used for updating user password by itself.
+        # Ref: http://lists.openstack.org/pipermail/openstack-dev/2015-December
+        #      /081803.html
+        patch_body = json.dumps({'user': kwargs})
+        resp, body = self.patch('OS-KSCRUD/users/%s' % user_id, patch_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_user_ec2_credential(self, user_id, **kwargs):
+        # TODO(piyush): Current api-site doesn't contain this API description.
+        # After fixing the api-site, we need to fix here also for putting the
+        # link to api-site.
+        post_body = json.dumps(kwargs)
+        resp, body = self.post('/users/%s/credentials/OS-EC2' % user_id,
+                               post_body)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_user_ec2_credential(self, user_id, access):
+        resp, body = self.delete('/users/%s/credentials/OS-EC2/%s' %
+                                 (user_id, access))
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_user_ec2_credentials(self, user_id):
+        resp, body = self.get('/users/%s/credentials/OS-EC2' % user_id)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_user_ec2_credential(self, user_id, access):
+        resp, body = self.get('/users/%s/credentials/OS-EC2/%s' %
+                              (user_id, access))
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/image/__init__.py b/tempest/lib/services/image/__init__.py
index e69de29..4b01663 100644
--- a/tempest/lib/services/image/__init__.py
+++ b/tempest/lib/services/image/__init__.py
@@ -0,0 +1,18 @@
+# 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.image import v1
+from tempest.lib.services.image import v2
+
+__all__ = ['v1', 'v2']
diff --git a/tempest/lib/services/image/v1/__init__.py b/tempest/lib/services/image/v1/__init__.py
index e69de29..9bd8262 100644
--- a/tempest/lib/services/image/v1/__init__.py
+++ b/tempest/lib/services/image/v1/__init__.py
@@ -0,0 +1,19 @@
+# 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.image.v1.image_members_client import \
+    ImageMembersClient
+from tempest.lib.services.image.v1.images_client import ImagesClient
+
+__all__ = ['ImageMembersClient', 'ImagesClient']
diff --git a/tempest/services/image/v1/json/images_client.py b/tempest/lib/services/image/v1/images_client.py
similarity index 84%
rename from tempest/services/image/v1/json/images_client.py
rename to tempest/lib/services/image/v1/images_client.py
index ed0a676..0db98f8 100644
--- a/tempest/services/image/v1/json/images_client.py
+++ b/tempest/lib/services/image/v1/images_client.py
@@ -13,11 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import copy
 import functools
 
 from oslo_serialization import jsonutils as json
-import six
 from six.moves.urllib import parse as urllib
 
 from tempest.lib.common import rest_client
@@ -29,20 +27,6 @@
 class ImagesClient(rest_client.RestClient):
     api_version = "v1"
 
-    def _image_meta_to_headers(self, fields):
-        headers = {}
-        fields_copy = copy.deepcopy(fields)
-        copy_from = fields_copy.pop('copy_from', None)
-        if copy_from is not None:
-            headers['x-glance-api-copy-from'] = copy_from
-        for key, value in six.iteritems(fields_copy.pop('properties', {})):
-            headers['x-image-meta-property-%s' % key] = str(value)
-        for key, value in six.iteritems(fields_copy.pop('api', {})):
-            headers['x-glance-api-property-%s' % key] = str(value)
-        for key, value in six.iteritems(fields_copy):
-            headers['x-image-meta-%s' % key] = str(value)
-        return headers
-
     def _create_with_data(self, headers, data):
         # We are going to do chunked transfert, so split the input data
         # info fixed-sized chunks.
@@ -74,14 +58,14 @@
             self._http = self._get_http()
         return self._http
 
-    def create_image(self, data=None, **kwargs):
+    def create_image(self, data=None, headers=None):
         """Create an image.
 
         Available params: http://developer.openstack.org/
                           api-ref-image-v1.html#createImage-v1
         """
-        headers = {}
-        headers.update(self._image_meta_to_headers(kwargs))
+        if headers is None:
+            headers = {}
 
         if data is not None:
             return self._create_with_data(headers, data)
@@ -91,14 +75,14 @@
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
-    def update_image(self, image_id, data=None, **kwargs):
+    def update_image(self, image_id, data=None, headers=None):
         """Update an image.
 
         Available params: http://developer.openstack.org/
                           api-ref-image-v1.html#updateImage-v1
         """
-        headers = {}
-        headers.update(self._image_meta_to_headers(kwargs))
+        if headers is None:
+            headers = {}
 
         if data is not None:
             return self._update_with_data(image_id, headers, data)
diff --git a/tempest/lib/services/network/routers_client.py b/tempest/lib/services/network/routers_client.py
index 78ffa77..2ba1938 100644
--- a/tempest/lib/services/network/routers_client.py
+++ b/tempest/lib/services/network/routers_client.py
@@ -55,7 +55,7 @@
         """Remove router interface.
 
         Available params: see http://developer.openstack.org/
-                              api-ref-networking-v2-ext.html#removeRouterInterface
+                              api-ref-networking-v2-ext.html#deleteRouterInterface
         """
         uri = '/routers/%s/remove_router_interface' % router_id
         return self.update_resource(uri, kwargs)
diff --git a/tempest/manager.py b/tempest/manager.py
index f2659a8..3d495b6 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -13,61 +13,50 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_log import log as logging
+
+from tempest import clients
 from tempest import config
-from tempest.lib import auth
-from tempest.lib import exceptions
+from tempest import service_clients
 
 CONF = config.CONF
+LOG = logging.getLogger(__name__)
 
 
-class Manager(object):
-    """Base manager class
+class Manager(service_clients.ServiceClients):
+    """Service client manager class for backward compatibility
 
-    Manager objects are responsible for providing a configuration object
-    and a client object for a test case to use in performing actions.
+    The former manager.Manager is not a stable interface in Tempest,
+    nonetheless it is consumed by a number of plugins already. This class
+    exists to provide some grace time for the move to tempest.lib.
     """
 
     def __init__(self, credentials, scope='project'):
-        """Initialization of base manager class
-
-        Credentials to be used within the various client classes managed by the
-        Manager object must be defined.
-
-        :param credentials: An instance of `auth.Credentials`
-        :param scope: default scope for tokens produced by the auth provider
-        """
-        self.credentials = credentials
-        # Check if passed or default credentials are valid
-        if not self.credentials.is_valid():
-            raise exceptions.InvalidCredentials()
-        self.auth_version = CONF.identity.auth_version
-        # Creates an auth provider for the credentials
-        self.auth_provider = get_auth_provider(
-            self.credentials, pre_auth=True, scope=scope)
-
-
-def get_auth_provider_class(credentials):
-    if isinstance(credentials, auth.KeystoneV3Credentials):
-        return auth.KeystoneV3AuthProvider, CONF.identity.uri_v3
-    else:
-        return auth.KeystoneV2AuthProvider, CONF.identity.uri
+        msg = ("tempest.manager.Manager is not a stable interface and as such "
+               "it should not imported directly. It will be removed as "
+               "soon as the client manager becomes available in tempest.lib.")
+        LOG.warning(msg)
+        dscv = CONF.identity.disable_ssl_certificate_validation
+        _, uri = clients.get_auth_provider_class(credentials)
+        super(Manager, self).__init__(
+            credentials=credentials, scope=scope,
+            identity_uri=uri,
+            disable_ssl_certificate_validation=dscv,
+            ca_certs=CONF.identity.ca_certificates_file,
+            trace_requests=CONF.debug.trace_requests)
 
 
 def get_auth_provider(credentials, pre_auth=False, scope='project'):
-    default_params = {
-        'disable_ssl_certificate_validation':
-            CONF.identity.disable_ssl_certificate_validation,
-        'ca_certs': CONF.identity.ca_certificates_file,
-        'trace_requests': CONF.debug.trace_requests
-    }
-    if credentials is None:
-        raise exceptions.InvalidCredentials(
-            'Credentials must be specified')
-    auth_provider_class, auth_url = get_auth_provider_class(
-        credentials)
-    _auth_provider = auth_provider_class(credentials, auth_url,
-                                         scope=scope,
-                                         **default_params)
-    if pre_auth:
-        _auth_provider.set_auth()
-    return _auth_provider
+    """Shim to get_auth_provider in clients.py
+
+    get_auth_provider used to be hosted in this module, but it has been
+    moved to clients.py now as a more permanent location.
+    This module will be removed eventually, and this shim is only
+    maintained for the benefit of plugins already consuming this interface.
+    """
+    msg = ("tempest.manager.get_auth_provider is not a stable interface and "
+           "as such it should not imported directly. It will be removed as "
+           "the client manager becomes available in tempest.lib.")
+    LOG.warning(msg)
+    return clients.get_auth_provider(credentials=credentials,
+                                     pre_auth=pre_auth, scope=scope)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index dd6e0e5..f889c44 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -79,8 +79,6 @@
         cls.security_groups_client = cls.manager.security_groups_client
         cls.security_group_rules_client = (
             cls.manager.security_group_rules_client)
-        # Heat client
-        cls.orchestration_client = cls.manager.orchestration_client
 
         if CONF.volume_feature_enabled.api_v1:
             cls.volumes_client = cls.manager.volumes_client
@@ -388,6 +386,7 @@
         if CONF.image_feature_enabled.api_v1:
             params['is_public'] = 'False'
             params['properties'] = properties
+            params = {'headers': common_image.image_meta_to_headers(**params)}
         else:
             params['visibility'] = 'private'
             # Additional properties are flattened out in the v2 API.
@@ -1037,7 +1036,7 @@
         if not tenant_id:
             tenant_id = client.tenant_id
         sgs = [
-            sg for sg in client.list_security_groups().values()[0]
+            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)
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 80728dc..446c87a 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 import json
+import re
 
 from oslo_log import log as logging
 
@@ -83,9 +84,9 @@
     def verify_metadata_on_config_drive(self):
         if self.run_ssh and CONF.compute_feature_enabled.config_drive:
             # Verify metadata on config_drive
-            cmd_blkid = 'blkid -t LABEL=config-2 -o device'
-            dev_name = self.ssh_client.exec_command(cmd_blkid)
-            dev_name = dev_name.rstrip()
+            cmd_blkid = 'blkid | grep -i config-2'
+            result = self.ssh_client.exec_command(cmd_blkid)
+            dev_name = re.match('([^:]+)', result).group()
             self.ssh_client.exec_command('sudo mount %s /mnt' % dev_name)
             cmd_md = 'sudo cat /mnt/openstack/latest/meta_data.json'
             result = self.ssh_client.exec_command(cmd_md)
diff --git a/tempest/service_clients.py b/tempest/service_clients.py
new file mode 100644
index 0000000..3208c8d
--- /dev/null
+++ b/tempest/service_clients.py
@@ -0,0 +1,90 @@
+# Copyright 2012 OpenStack Foundation
+# Copyright (c) 2016 Hewlett-Packard Enterprise Development Company, L.P.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib import auth
+from tempest.lib import exceptions
+
+
+class ServiceClients(object):
+    """Service client provider class
+
+    The ServiceClients object provides a useful means for tests to access
+    service clients configured for a specified set of credentials.
+    It hides some of the complexity from the authorization and configuration
+    layers.
+
+    Examples:
+
+        >>> from tempest import service_clients
+        >>> johndoe = cred_provider.get_creds_by_role(['johndoe'])
+        >>> johndoe_clients = service_clients.ServiceClients(johndoe)
+        >>> johndoe_servers = johndoe_clients.servers_client.list_servers()
+
+    """
+    # NOTE(andreaf) This class does not depend on tempest configuration
+    # and its meant for direct consumption by external clients such as tempest
+    # plugins. Tempest provides a wrapper class, `clients.Manager`, that
+    # initialises this class using values from tempest CONF object. The wrapper
+    # class should only be used by tests hosted in Tempest.
+
+    def __init__(self, credentials, identity_uri, region=None,
+                 scope='project', disable_ssl_certificate_validation=True,
+                 ca_certs=None, trace_requests=''):
+        """Service Clients provider
+
+        Instantiate a `ServiceClients` object, from a set of credentials and an
+        identity URI. The identity version is inferred from the credentials
+        object. Optionally auth scope can be provided.
+        Parameters dscv, ca_certs and trace_requests all apply to the auth
+        provider as well as any service clients provided by this manager.
+
+        :param credentials: An instance of `auth.Credentials`
+        :param identity_uri: URI of the identity API. This should be a
+                             mandatory parameter, and it will so soon.
+        :param region: Default value of region for service clients.
+        :param scope: default scope for tokens produced by the auth provider
+        :param disable_ssl_certificate_validation Applies to auth and to all
+                                                  service clients.
+        :param ca_certs Applies to auth and to all service clients.
+        :param trace_requests Applies to auth and to all service clients.
+        """
+        self.credentials = credentials
+        self.identity_uri = identity_uri
+        if not identity_uri:
+            raise exceptions.InvalidCredentials(
+                'Manager requires a non-empty identity_uri.')
+        self.region = region
+        # Check if passed or default credentials are valid
+        if not self.credentials.is_valid():
+            raise exceptions.InvalidCredentials()
+        # Get the identity classes matching the provided credentials
+        # TODO(andreaf) Define a new interface in Credentials to get
+        # the API version from an instance
+        identity = [(k, auth.IDENTITY_VERSION[k][1]) for k in
+                    auth.IDENTITY_VERSION.keys() if
+                    isinstance(self.credentials, auth.IDENTITY_VERSION[k][0])]
+        # Zero matches or more than one are both not valid.
+        if len(identity) != 1:
+            raise exceptions.InvalidCredentials()
+        self.auth_version, auth_provider_class = identity[0]
+        self.dscv = disable_ssl_certificate_validation
+        self.ca_certs = ca_certs
+        self.trace_requests = trace_requests
+        # 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)
diff --git a/tempest/services/database/__init__.py b/tempest/services/database/__init__.py
deleted file mode 100644
index 9a742d8..0000000
--- a/tempest/services/database/__init__.py
+++ /dev/null
@@ -1,23 +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.services.database.json.flavors_client import \
-    DatabaseFlavorsClient
-from tempest.services.database.json.limits_client import \
-    DatabaseLimitsClient
-from tempest.services.database.json.versions_client import \
-    DatabaseVersionsClient
-
-__all__ = ['DatabaseFlavorsClient', 'DatabaseLimitsClient',
-           'DatabaseVersionsClient']
diff --git a/tempest/services/database/json/__init__.py b/tempest/services/database/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/database/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/database/json/flavors_client.py b/tempest/services/database/json/flavors_client.py
deleted file mode 100644
index bd8ffb0..0000000
--- a/tempest/services/database/json/flavors_client.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2014 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 oslo_serialization import jsonutils as json
-from six.moves import urllib
-
-from tempest.lib.common import rest_client
-
-
-class DatabaseFlavorsClient(rest_client.RestClient):
-
-    def list_db_flavors(self, params=None):
-        url = 'flavors'
-        if params:
-            url += '?%s' % urllib.parse.urlencode(params)
-
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
-
-    def show_db_flavor(self, db_flavor_id):
-        resp, body = self.get("flavors/%s" % db_flavor_id)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/database/json/limits_client.py b/tempest/services/database/json/limits_client.py
deleted file mode 100644
index a1c58c2..0000000
--- a/tempest/services/database/json/limits_client.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright 2014 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 oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
-
-
-class DatabaseLimitsClient(rest_client.RestClient):
-
-    def list_db_limits(self, params=None):
-        """List all limits."""
-        url = 'limits'
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/database/json/versions_client.py b/tempest/services/database/json/versions_client.py
deleted file mode 100644
index 2f28203..0000000
--- a/tempest/services/database/json/versions_client.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2014 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 oslo_serialization import jsonutils as json
-from six.moves.urllib import parse as urllib
-
-from tempest.lib.common import rest_client
-
-
-class DatabaseVersionsClient(rest_client.RestClient):
-
-    def __init__(self, auth_provider, service, region, **kwargs):
-        super(DatabaseVersionsClient, self).__init__(
-            auth_provider, service, region, **kwargs)
-        self.skip_path()
-
-    def list_db_versions(self, params=None):
-        """List all versions."""
-        url = ''
-        if params:
-            url += '?%s' % urllib.urlencode(params)
-
-        resp, body = self.get(url)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/identity/v2/__init__.py b/tempest/services/identity/v2/__init__.py
index 6f4ebcf..ac2a874 100644
--- a/tempest/services/identity/v2/__init__.py
+++ b/tempest/services/identity/v2/__init__.py
@@ -13,12 +13,12 @@
 # the License.
 
 from tempest.lib.services.identity.v2.endpoints_client import EndpointsClient
+from tempest.lib.services.identity.v2.roles_client import RolesClient
+from tempest.lib.services.identity.v2.services_client import ServicesClient
+from tempest.lib.services.identity.v2.tenants_client import TenantsClient
 from tempest.lib.services.identity.v2.token_client import TokenClient
+from tempest.lib.services.identity.v2.users_client import UsersClient
 from tempest.services.identity.v2.json.identity_client import IdentityClient
-from tempest.services.identity.v2.json.roles_client import RolesClient
-from tempest.services.identity.v2.json.services_client import ServicesClient
-from tempest.services.identity.v2.json.tenants_client import TenantsClient
-from tempest.services.identity.v2.json.users_client import UsersClient
 
 __all__ = ['EndpointsClient', 'TokenClient', 'IdentityClient', 'RolesClient',
            'ServicesClient', 'TenantsClient', 'UsersClient']
diff --git a/tempest/services/image/__init__.py b/tempest/services/image/__init__.py
deleted file mode 100644
index 7ff0886..0000000
--- a/tempest/services/image/__init__.py
+++ /dev/null
@@ -1,18 +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.image import v2
-from tempest.services.image import v1
-
-__all__ = ['v1', 'v2']
diff --git a/tempest/services/image/v1/__init__.py b/tempest/services/image/v1/__init__.py
deleted file mode 100644
index 67dca39..0000000
--- a/tempest/services/image/v1/__init__.py
+++ /dev/null
@@ -1,19 +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.image.v1.image_members_client import \
-    ImageMembersClient
-from tempest.services.image.v1.json.images_client import ImagesClient
-
-__all__ = ['ImageMembersClient', 'ImagesClient']
diff --git a/tempest/services/image/v1/json/__init__.py b/tempest/services/image/v1/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/image/v1/json/__init__.py
+++ /dev/null
diff --git a/tempest/services/volume/base/admin/base_types_client.py b/tempest/services/volume/base/admin/base_types_client.py
index 95ddff6..e4d9014 100644
--- a/tempest/services/volume/base/admin/base_types_client.py
+++ b/tempest/services/volume/base/admin/base_types_client.py
@@ -179,3 +179,37 @@
             "/types/%s/encryption/provider" % str(vol_type_id))
         self.expected_success(202, resp.status)
         return rest_client.ResponseBody(resp, body)
+
+    def add_type_access(self, volume_type_id, **kwargs):
+        """Adds volume type access for the given project.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html
+                              #createVolumeTypeAccessExt
+        """
+        post_body = json.dumps({'addProjectAccess': kwargs})
+        url = 'types/%s/action' % (volume_type_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def remove_type_access(self, volume_type_id, **kwargs):
+        """Removes volume type access for the given project.
+
+        Available params: see http://developer.openstack.org/
+                              api-ref-blockstorage-v2.html
+                              #removeVolumeTypeAccessExt
+        """
+        post_body = json.dumps({'removeProjectAccess': kwargs})
+        url = 'types/%s/action' % (volume_type_id)
+        resp, body = self.post(url, post_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def list_type_access(self, volume_type_id):
+        """Print access information about the given volume type."""
+        url = 'types/%s/os-volume-type-access' % (volume_type_id)
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/test.py b/tempest/test.py
index 4e06db8..97ab25c 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -72,14 +72,9 @@
         'image': CONF.service_available.glance,
         'baremetal': CONF.service_available.ironic,
         'volume': CONF.service_available.cinder,
-        'orchestration': CONF.service_available.heat,
-        # NOTE(mtreinish) nova-network will provide networking functionality
-        # if neutron isn't available, so always set to True.
         'network': True,
         'identity': True,
         'object_storage': CONF.service_available.swift,
-        'data_processing': CONF.service_available.sahara,
-        'database': CONF.service_available.trove
     }
     return service_list
 
@@ -91,9 +86,8 @@
     exercised by a test case.
     """
     def decorator(f):
-        services = ['compute', 'image', 'baremetal', 'volume', 'orchestration',
-                    'network', 'identity', 'object_storage', 'data_processing',
-                    'database']
+        services = ['compute', 'image', 'baremetal', 'volume',
+                    'network', 'identity', 'object_storage']
         for service in args:
             if service not in services:
                 raise exceptions.InvalidServiceTag('%s is not a valid '
diff --git a/tempest/tests/cmd/sample_streams/calls.subunit b/tempest/tests/cmd/sample_streams/calls.subunit
new file mode 100644
index 0000000..d5b4790
--- /dev/null
+++ b/tempest/tests/cmd/sample_streams/calls.subunit
Binary files differ
diff --git a/tempest/tests/cmd/test_run.py b/tempest/tests/cmd/test_run.py
index dcffd21..772391f 100644
--- a/tempest/tests/cmd/test_run.py
+++ b/tempest/tests/cmd/test_run.py
@@ -46,18 +46,24 @@
         args = mock.Mock(spec=argparse.Namespace)
         setattr(args, 'smoke', False)
         setattr(args, 'regex', '')
+        setattr(args, 'whitelist_file', None)
+        setattr(args, 'blacklist_file', None)
         self.assertEqual('', self.run_cmd._build_regex(args))
 
     def test__build_regex_smoke(self):
         args = mock.Mock(spec=argparse.Namespace)
         setattr(args, "smoke", True)
         setattr(args, 'regex', '')
+        setattr(args, 'whitelist_file', None)
+        setattr(args, 'blacklist_file', None)
         self.assertEqual('smoke', self.run_cmd._build_regex(args))
 
     def test__build_regex_regex(self):
         args = mock.Mock(spec=argparse.Namespace)
         setattr(args, 'smoke', False)
         setattr(args, "regex", 'i_am_a_fun_little_regex')
+        setattr(args, 'whitelist_file', None)
+        setattr(args, 'blacklist_file', None)
         self.assertEqual('i_am_a_fun_little_regex',
                          self.run_cmd._build_regex(args))
 
diff --git a/tempest/tests/cmd/test_subunit_describe_calls.py b/tempest/tests/cmd/test_subunit_describe_calls.py
new file mode 100644
index 0000000..43b417a
--- /dev/null
+++ b/tempest/tests/cmd/test_subunit_describe_calls.py
@@ -0,0 +1,83 @@
+# Copyright 2016 Rackspace
+#
+# 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 os
+import subprocess
+import tempfile
+
+from tempest.cmd import subunit_describe_calls
+from tempest.tests import base
+
+
+class TestSubunitDescribeCalls(base.TestCase):
+    def test_return_code(self):
+        subunit_file = os.path.join(
+            os.path.dirname(os.path.abspath(__file__)),
+            'sample_streams/calls.subunit')
+        p = subprocess.Popen([
+            'subunit-describe-calls', '-s', subunit_file,
+            '-o', tempfile.mkstemp()[1]], stdin=subprocess.PIPE)
+        p.communicate()
+        self.assertEqual(0, p.returncode)
+
+    def test_parse(self):
+        subunit_file = os.path.join(
+            os.path.dirname(os.path.abspath(__file__)),
+            'sample_streams/calls.subunit')
+        parser = subunit_describe_calls.parse(
+            subunit_file, "pythonlogging", None)
+        expected_result = {
+            'bar': [{'name': 'AgentsAdminTestJSON:setUp',
+                     'service': 'Nova',
+                     'status_code': '200',
+                     'url': 'v2.1/<id>/os-agents',
+                     'verb': 'POST'},
+                    {'name': 'AgentsAdminTestJSON:test_create_agent',
+                     'service': 'Nova',
+                     'status_code': '200',
+                     'url': 'v2.1/<id>/os-agents',
+                     'verb': 'POST'},
+                    {'name': 'AgentsAdminTestJSON:tearDown',
+                     'service': 'Nova',
+                     'status_code': '200',
+                     'url': 'v2.1/<id>/os-agents/1',
+                     'verb': 'DELETE'},
+                    {'name': 'AgentsAdminTestJSON:_run_cleanups',
+                     'service': 'Nova',
+                     'status_code': '200',
+                     'url': 'v2.1/<id>/os-agents/2',
+                     'verb': 'DELETE'}],
+            'foo': [{'name': 'AgentsAdminTestJSON:setUp',
+                     'service': 'Nova',
+                     'status_code': '200',
+                     'url': 'v2.1/<id>/os-agents',
+                     'verb': 'POST'},
+                    {'name': 'AgentsAdminTestJSON:test_delete_agent',
+                     'service': 'Nova',
+                     'status_code': '200',
+                     'url': 'v2.1/<id>/os-agents/3',
+                     'verb': 'DELETE'},
+                    {'name': 'AgentsAdminTestJSON:test_delete_agent',
+                     'service': 'Nova',
+                     'status_code': '200',
+                     'url': 'v2.1/<id>/os-agents',
+                     'verb': 'GET'},
+                    {'name': 'AgentsAdminTestJSON:tearDown',
+                     'service': 'Nova',
+                     'status_code': '404',
+                     'url': 'v2.1/<id>/os-agents/3',
+                     'verb': 'DELETE'}]}
+        self.assertEqual(expected_result, parser.test_logs)
diff --git a/tempest/tests/common/test_dynamic_creds.py b/tempest/tests/common/test_dynamic_creds.py
index e97f65f..b7cc05d 100644
--- a/tempest/tests/common/test_dynamic_creds.py
+++ b/tempest/tests/common/test_dynamic_creds.py
@@ -21,14 +21,15 @@
 from tempest import config
 from tempest import exceptions
 from tempest.lib.common import rest_client
+from tempest.lib import exceptions as lib_exc
+from tempest.lib.services.identity.v2 import roles_client as v2_roles_client
+from tempest.lib.services.identity.v2 import tenants_client as \
+    v2_tenants_client
 from tempest.lib.services.identity.v2 import token_client as v2_token_client
+from tempest.lib.services.identity.v2 import users_client as v2_users_client
 from tempest.lib.services.identity.v3 import token_client as v3_token_client
 from tempest.lib.services.network import routers_client
 from tempest.services.identity.v2.json import identity_client as v2_iden_client
-from tempest.services.identity.v2.json import roles_client as v2_roles_client
-from tempest.services.identity.v2.json import tenants_client as \
-    v2_tenants_client
-from tempest.services.identity.v2.json import users_client as v2_users_client
 from tempest.services.identity.v3.json import domains_client
 from tempest.services.identity.v3.json import identity_client as v3_iden_client
 from tempest.services.identity.v3.json import projects_client as \
@@ -635,3 +636,15 @@
             return_value=(rest_client.ResponseBody
                           (200, {'project': {'id': id, 'name': name}}))))
         return project_fix
+
+    @mock.patch('tempest.lib.common.rest_client.RestClient')
+    def test_member_role_creation_with_duplicate(self, rest_client_mock):
+        creds = dynamic_creds.DynamicCredentialProvider(**self.fixed_params)
+        creds.creds_client = mock.MagicMock()
+        creds.creds_client.create_user_role.side_effect = lib_exc.Conflict
+        with mock.patch('tempest.common.dynamic_creds.LOG') as log_mock:
+            creds._create_creds()
+            log_mock.warning.assert_called_once_with(
+                "Member role already exists, ignoring conflict.")
+        creds.creds_client.assign_user_role.assert_called_once_with(
+            mock.ANY, mock.ANY, 'Member')
diff --git a/tempest/tests/common/test_image.py b/tempest/tests/common/test_image.py
index fdd0ae8..34772a2 100644
--- a/tempest/tests/common/test_image.py
+++ b/tempest/tests/common/test_image.py
@@ -38,3 +38,22 @@
             'name': 'New Http Image'
         }
         self.assertEqual(expected, observed)
+
+    def test_image_meta_to_headers(self):
+        observed = image.image_meta_to_headers(
+            name='test',
+            container_format='wrong',
+            disk_format='vhd',
+            copy_from='http://localhost/images/10',
+            properties={'foo': 'bar'},
+            api={'abc': 'def'})
+
+        expected = {
+            'x-image-meta-name': 'test',
+            'x-image-meta-container_format': 'wrong',
+            'x-image-meta-disk_format': 'vhd',
+            'x-glance-api-copy-from': 'http://localhost/images/10',
+            'x-image-meta-property-foo': 'bar',
+            'x-glance-api-property-abc': 'def'
+        }
+        self.assertEqual(expected, observed)
diff --git a/tempest/tests/services/identity/v2/test_roles_client.py b/tempest/tests/lib/services/identity/v2/test_roles_client.py
similarity index 98%
rename from tempest/tests/services/identity/v2/test_roles_client.py
rename to tempest/tests/lib/services/identity/v2/test_roles_client.py
index e36ec18..464a715 100644
--- a/tempest/tests/services/identity/v2/test_roles_client.py
+++ b/tempest/tests/lib/services/identity/v2/test_roles_client.py
@@ -12,7 +12,7 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-from tempest.services.identity.v2.json import roles_client
+from tempest.lib.services.identity.v2 import roles_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
 
diff --git a/tempest/tests/lib/services/identity/v2/test_services_client.py b/tempest/tests/lib/services/identity/v2/test_services_client.py
new file mode 100644
index 0000000..bafb6b1
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v2/test_services_client.py
@@ -0,0 +1,97 @@
+# Copyright 2016 NEC Corporation.  All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.services.identity.v2 import services_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestServicesClient(base.BaseServiceTest):
+    FAKE_SERVICE_INFO = {
+        "OS-KSADM:service": {
+            "id": "1",
+            "name": "test",
+            "type": "compute",
+            "description": "test_description"
+        }
+    }
+
+    FAKE_LIST_SERVICES = {
+        "OS-KSADM:services": [
+            {
+                "id": "1",
+                "name": "test",
+                "type": "compute",
+                "description": "test_description"
+            },
+            {
+                "id": "2",
+                "name": "test2",
+                "type": "compute",
+                "description": "test2_description"
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestServicesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = services_client.ServicesClient(fake_auth,
+                                                     'identity', 'regionOne')
+
+    def _test_create_service(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_service,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_SERVICE_INFO,
+            bytes_body,
+            id="1",
+            name="test",
+            type="compute",
+            description="test_description")
+
+    def _test_show_service(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_service,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SERVICE_INFO,
+            bytes_body,
+            service_id="1")
+
+    def _test_list_services(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_services,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_SERVICES,
+            bytes_body)
+
+    def test_create_service_with_str_body(self):
+        self._test_create_service()
+
+    def test_create_service_with_bytes_body(self):
+        self._test_create_service(bytes_body=True)
+
+    def test_show_service_with_str_body(self):
+        self._test_show_service()
+
+    def test_show_service_with_bytes_body(self):
+        self._test_show_service(bytes_body=True)
+
+    def test_delete_service(self):
+        self.check_service_client_function(
+            self.client.delete_service,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            service_id="1",
+            status=204)
diff --git a/tempest/tests/lib/services/identity/v2/test_tenants_client.py b/tempest/tests/lib/services/identity/v2/test_tenants_client.py
new file mode 100644
index 0000000..ae3d13a
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v2/test_tenants_client.py
@@ -0,0 +1,131 @@
+# Copyright 2016 NEC Corporation.  All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.services.identity.v2 import tenants_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestTenantsClient(base.BaseServiceTest):
+    FAKE_TENANT_INFO = {
+        "tenant": {
+            "id": "1",
+            "name": "test",
+            "description": "test_description",
+            "enabled": True
+        }
+    }
+
+    FAKE_LIST_TENANTS = {
+        "tenants": [
+            {
+                "id": "1",
+                "name": "test",
+                "description": "test_description",
+                "enabled": True
+            },
+            {
+                "id": "2",
+                "name": "test2",
+                "description": "test2_description",
+                "enabled": True
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestTenantsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = tenants_client.TenantsClient(fake_auth,
+                                                   'identity', 'regionOne')
+
+    def _test_create_tenant(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_tenant,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_TENANT_INFO,
+            bytes_body,
+            name="test",
+            description="test_description")
+
+    def _test_show_tenant(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_tenant,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_TENANT_INFO,
+            bytes_body,
+            tenant_id="1")
+
+    def _test_update_tenant(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_tenant,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_TENANT_INFO,
+            bytes_body,
+            tenant_id="1",
+            name="test",
+            description="test_description")
+
+    def _test_list_tenants(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_tenants,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_TENANTS,
+            bytes_body)
+
+    def _test_list_tenant_users(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_tenant_users,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_TENANTS,
+            bytes_body,
+            tenant_id="1")
+
+    def test_create_tenant_with_str_body(self):
+        self._test_create_tenant()
+
+    def test_create_tenant_with_bytes_body(self):
+        self._test_create_tenant(bytes_body=True)
+
+    def test_show_tenant_with_str_body(self):
+        self._test_show_tenant()
+
+    def test_show_tenant_with_bytes_body(self):
+        self._test_show_tenant(bytes_body=True)
+
+    def test_update_tenant_with_str_body(self):
+        self._test_update_tenant()
+
+    def test_update_tenant_with_bytes_body(self):
+        self._test_update_tenant(bytes_body=True)
+
+    def test_list_tenants_with_str_body(self):
+        self._test_list_tenants()
+
+    def test_list_tenants_with_bytes_body(self):
+        self._test_list_tenants(bytes_body=True)
+
+    def test_delete_tenant(self):
+        self.check_service_client_function(
+            self.client.delete_tenant,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            tenant_id="1",
+            status=204)
+
+    def test_list_tenant_users_with_str_body(self):
+        self._test_list_tenant_users()
+
+    def test_list_tenant_users_with_bytes_body(self):
+        self._test_list_tenant_users(bytes_body=True)
diff --git a/tempest/tests/lib/services/identity/v2/test_users_client.py b/tempest/tests/lib/services/identity/v2/test_users_client.py
new file mode 100644
index 0000000..9534e44
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v2/test_users_client.py
@@ -0,0 +1,243 @@
+# Copyright 2016 NEC Corporation.  All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from tempest.lib.services.identity.v2 import users_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestUsersClient(base.BaseServiceTest):
+    FAKE_USER_INFO = {
+        "user": {
+            "id": "1",
+            "name": "test",
+            "email": "john.smith@example.org",
+            "enabled": True
+        }
+    }
+
+    FAKE_LIST_USERS = {
+        "users": [
+            {
+                "id": "1",
+                "name": "test",
+                "email": "john.smith@example.org",
+                "enabled": True
+            },
+            {
+                "id": "2",
+                "name": "test2",
+                "email": "john.smith@example.org",
+                "enabled": True
+            }
+        ]
+    }
+
+    FAKE_USER_EC2_CREDENTIAL_INFO = {
+        "credential": {
+            'user_id': '9beb0e12f3e5416db8d7cccfc785db3b',
+            'access': '79abf59acc77492a86170cbe2f1feafa',
+            'secret': 'c4e7d3a691fd4563873d381a40320f46',
+            'trust_id': None,
+            'tenant_id': '596557269d7b4dd78631a602eb9f151d'
+        }
+    }
+
+    FAKE_LIST_USER_EC2_CREDENTIALS = {
+        "credentials": [
+            {
+                'user_id': '9beb0e12f3e5416db8d7cccfc785db3b',
+                'access': '79abf59acc77492a86170cbe2f1feafa',
+                'secret': 'c4e7d3a691fd4563873d381a40320f46',
+                'trust_id': None,
+                'tenant_id': '596557269d7b4dd78631a602eb9f151d'
+            },
+            {
+                'user_id': '3beb0e12f3e5416db8d7cccfc785de4r',
+                'access': '45abf59acc77492a86170cbe2f1fesde',
+                'secret': 'g4e7d3a691fd4563873d381a40320e45',
+                'trust_id': None,
+                'tenant_id': '123557269d7b4dd78631a602eb9f112f'
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestUsersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = users_client.UsersClient(fake_auth,
+                                               'identity', 'regionOne')
+
+    def _test_create_user(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_user,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_USER_INFO,
+            bytes_body,
+            name="test",
+            email="john.smith@example.org")
+
+    def _test_update_user(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_user,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_USER_INFO,
+            bytes_body,
+            user_id="1",
+            name="test")
+
+    def _test_show_user(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_user,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_USER_INFO,
+            bytes_body,
+            user_id="1")
+
+    def _test_list_users(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_users,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_USERS,
+            bytes_body)
+
+    def _test_update_user_enabled(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_user_enabled,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_USER_INFO,
+            bytes_body,
+            user_id="1",
+            enabled=True)
+
+    def _test_update_user_password(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_user_password,
+            'tempest.lib.common.rest_client.RestClient.put',
+            self.FAKE_USER_INFO,
+            bytes_body,
+            user_id="1",
+            password="pass")
+
+    def _test_update_user_own_password(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.update_user_own_password,
+            'tempest.lib.common.rest_client.RestClient.patch',
+            self.FAKE_USER_INFO,
+            bytes_body,
+            user_id="1",
+            password="pass")
+
+    def _test_create_user_ec2_credential(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.create_user_ec2_credential,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_USER_EC2_CREDENTIAL_INFO,
+            bytes_body,
+            user_id="1",
+            tenant_id="123")
+
+    def _test_show_user_ec2_credential(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_user_ec2_credential,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_USER_EC2_CREDENTIAL_INFO,
+            bytes_body,
+            user_id="1",
+            access="123")
+
+    def _test_list_user_ec2_credentials(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_user_ec2_credentials,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_USER_EC2_CREDENTIALS,
+            bytes_body,
+            user_id="1")
+
+    def test_create_user_with_str_body(self):
+        self._test_create_user()
+
+    def test_create_user_with_bytes_body(self):
+        self._test_create_user(bytes_body=True)
+
+    def test_update_user_with_str_body(self):
+        self._test_update_user()
+
+    def test_update_user_with_bytes_body(self):
+        self._test_update_user(bytes_body=True)
+
+    def test_show_user_with_str_body(self):
+        self._test_show_user()
+
+    def test_show_user_with_bytes_body(self):
+        self._test_show_user(bytes_body=True)
+
+    def test_list_users_with_str_body(self):
+        self._test_list_users()
+
+    def test_list_users_with_bytes_body(self):
+        self._test_list_users(bytes_body=True)
+
+    def test_delete_user(self):
+        self.check_service_client_function(
+            self.client.delete_user,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            user_id="1",
+            status=204)
+
+    def test_update_user_enabled_with_str_body(self):
+        self._test_update_user_enabled()
+
+    def test_update_user_enabled_with_bytes_body(self):
+        self._test_update_user_enabled(bytes_body=True)
+
+    def test_update_user_password_with_str_body(self):
+        self._test_update_user_password()
+
+    def test_update_user_password_with_bytes_body(self):
+        self._test_update_user_password(bytes_body=True)
+
+    def test_update_user_own_password_with_str_body(self):
+        self._test_update_user_own_password()
+
+    def test_update_user_own_password_with_bytes_body(self):
+        self._test_update_user_own_password(bytes_body=True)
+
+    def test_create_user_ec2_credential_with_str_body(self):
+        self._test_create_user_ec2_credential()
+
+    def test_create_user_ec2_credential_with_bytes_body(self):
+        self._test_create_user_ec2_credential(bytes_body=True)
+
+    def test_show_user_ec2_credential_with_str_body(self):
+        self._test_show_user_ec2_credential()
+
+    def test_show_user_ec2_credential_with_bytes_body(self):
+        self._test_show_user_ec2_credential(bytes_body=True)
+
+    def test_list_user_ec2_credentials_with_str_body(self):
+        self._test_list_user_ec2_credentials()
+
+    def test_list_user_ec2_credentials_with_bytes_body(self):
+        self._test_list_user_ec2_credentials(bytes_body=True)
+
+    def test_delete_user_ec2_credential(self):
+        self.check_service_client_function(
+            self.client.delete_user_ec2_credential,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            user_id="123",
+            access="1234",
+            status=204)
diff --git a/tempest/tests/services/identity/__init__.py b/tempest/tests/services/identity/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/tests/services/identity/__init__.py
+++ /dev/null
diff --git a/tempest/tests/services/identity/v2/__init__.py b/tempest/tests/services/identity/v2/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/tests/services/identity/v2/__init__.py
+++ /dev/null
diff --git a/tempest/tests/test_service_clients.py b/tempest/tests/test_service_clients.py
new file mode 100644
index 0000000..f67781c
--- /dev/null
+++ b/tempest/tests/test_service_clients.py
@@ -0,0 +1,62 @@
+# 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.
+
+import testtools
+
+from tempest.lib import auth
+from tempest.lib import exceptions
+from tempest import service_clients
+from tempest.tests import base
+from tempest.tests.lib import fake_credentials
+
+
+class TestServiceClients(base.TestCase):
+
+    def test__init__creds_v2_uri(self):
+        # Verify that no API request is made, since no mock
+        # is required to run the test successfully
+        creds = fake_credentials.FakeKeystoneV2Credentials()
+        uri = 'fake_uri'
+        _manager = service_clients.ServiceClients(creds, identity_uri=uri)
+        self.assertIsInstance(_manager.auth_provider,
+                              auth.KeystoneV2AuthProvider)
+
+    def test__init__creds_v3_uri(self):
+        # Verify that no API request is made, since no mock
+        # is required to run the test successfully
+        creds = fake_credentials.FakeKeystoneV3Credentials()
+        uri = 'fake_uri'
+        _manager = service_clients.ServiceClients(creds, identity_uri=uri)
+        self.assertIsInstance(_manager.auth_provider,
+                              auth.KeystoneV3AuthProvider)
+
+    def test__init__base_creds_uri(self):
+        creds = fake_credentials.FakeCredentials()
+        uri = 'fake_uri'
+        with testtools.ExpectedException(exceptions.InvalidCredentials):
+            service_clients.ServiceClients(creds, identity_uri=uri)
+
+    def test__init__invalid_creds_uri(self):
+        creds = fake_credentials.FakeKeystoneV2Credentials()
+        delattr(creds, 'username')
+        uri = 'fake_uri'
+        with testtools.ExpectedException(exceptions.InvalidCredentials):
+            service_clients.ServiceClients(creds, identity_uri=uri)
+
+    def test__init__creds_uri_none(self):
+        creds = fake_credentials.FakeKeystoneV2Credentials()
+        msg = "Invalid Credentials\nDetails: Manager requires a non-empty"
+        with testtools.ExpectedException(exceptions.InvalidCredentials,
+                                         value_re=msg):
+            service_clients.ServiceClients(creds, None)
diff --git a/test-requirements.txt b/test-requirements.txt
index 763f0ba..04c3d6d 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,12 +1,12 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-hacking<0.11,>=0.10.0
+hacking<0.12,>=0.11.0 # Apache-2.0
 # needed for doc build
-sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD
+sphinx!=1.3b1,<1.3,>=1.2.1 # BSD
 python-subunit>=0.0.18 # Apache-2.0/BSD
 oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
-reno>=1.6.2 # Apache2
+reno>=1.8.0 # Apache2
 mock>=2.0 # BSD
 coverage>=3.6 # Apache-2.0
 oslotest>=1.10.0 # Apache-2.0