Merge "Specify snapshot size when creating volume"
diff --git a/README.rst b/README.rst
index fc4de5e..bc58cc0 100644
--- a/README.rst
+++ b/README.rst
@@ -1,3 +1,12 @@
+========================
+Team and repository tags
+========================
+
+.. image:: http://governance.openstack.org/badges/tempest.svg
+    :target: http://governance.openstack.org/reference/tags/index.html
+
+.. Change things from this point on
+
 Tempest - The OpenStack Integration Test Suite
 ==============================================
 
diff --git a/doc/source/library/api_microversion_testing.rst b/doc/source/library/api_microversion_testing.rst
index b7a4ba8..8be924d 100644
--- a/doc/source/library/api_microversion_testing.rst
+++ b/doc/source/library/api_microversion_testing.rst
@@ -1,6 +1,6 @@
 .. _api_microversion_testing:
 
-API Microversion Testing Support in Temepst
+API Microversion Testing Support in Tempest
 ===========================================
 
 ---------------------------------------------
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index dc73ef2..6b87b4d 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -236,6 +236,10 @@
 
  .. _2.25: http://docs.openstack.org/developer/nova/api_microversion_history.html#maximum-in-mitaka
 
+ * `2.32`_
+
+ .. _2.32: http://docs.openstack.org/developer/nova/api_microversion_history.html#id29
+
  * `2.37`_
 
  .. _2.37: http://docs.openstack.org/developer/nova/api_microversion_history.html#id34
diff --git a/releasenotes/notes/add-image-clients-af94564fb34ddca6.yaml b/releasenotes/notes/add-image-clients-af94564fb34ddca6.yaml
new file mode 100644
index 0000000..7e40fd4
--- /dev/null
+++ b/releasenotes/notes/add-image-clients-af94564fb34ddca6.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    As in the [doc]:
+    http://developer.openstack.org/api-ref/image/v2/metadefs-index.html,
+    there are some apis are not included, add them.
+
+      * namespace_properties_client(v2)
+
diff --git a/releasenotes/notes/add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml b/releasenotes/notes/add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml
new file mode 100644
index 0000000..a1edcc5
--- /dev/null
+++ b/releasenotes/notes/add-role-assignments-client-as-a-library-d34b4fdf376984ad.yaml
@@ -0,0 +1,6 @@
+---
+features:
+    - Define the identity service role_assignments_client as a library.
+      Add role_assignments_client to the library interface so the other
+      projects can use this module as a stable library without any
+      maintenance changes.
diff --git a/releasenotes/notes/move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml b/releasenotes/notes/move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml
new file mode 100644
index 0000000..9223ba5
--- /dev/null
+++ b/releasenotes/notes/move-cinder-v3-to-lib-service-be3ba0c20753b594.yaml
@@ -0,0 +1,7 @@
+features:
+  - |
+    Define the Volume v3 service clients as library interfaces,
+    allowing other projects to use these modules as stable
+    libraries without maintenance changes.
+
+    * messages_client(v3)
diff --git a/releasenotes/notes/new-volume-limit-client-517c17d9090f4df4.yaml b/releasenotes/notes/new-volume-limit-client-517c17d9090f4df4.yaml
new file mode 100644
index 0000000..033e147
--- /dev/null
+++ b/releasenotes/notes/new-volume-limit-client-517c17d9090f4df4.yaml
@@ -0,0 +1,3 @@
+---
+features:
+  - The volume_limits client was added to tempest.lib.
diff --git a/releasenotes/notes/remove-bootable-option-024f8944c056a3e0.yaml b/releasenotes/notes/remove-bootable-option-024f8944c056a3e0.yaml
new file mode 100644
index 0000000..e6e53af
--- /dev/null
+++ b/releasenotes/notes/remove-bootable-option-024f8944c056a3e0.yaml
@@ -0,0 +1,6 @@
+---
+deprecations:
+  - The *bootable* config option in the *volume_feature_enabled* group is
+    removed because the corresponding feature os-set_bootable has been
+    implemented 2.5 years ago and all OpenStack versions which are supported
+    by Tempest should support the feature.
diff --git a/requirements.txt b/requirements.txt
index ea73180..d9a9ebb 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,7 +2,7 @@
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
 pbr>=1.8 # Apache-2.0
-cliff>=2.2.0 # Apache-2.0
+cliff>=2.3.0 # Apache-2.0
 jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
 testtools>=1.4.0 # MIT
 paramiko>=2.0 # LGPLv2.1+
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 667d30b..1233a2b 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -18,9 +18,12 @@
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
 from tempest.common.utils import data_utils
+from tempest import config
 from tempest.lib.common.utils import test_utils
 from tempest import test
 
+CONF = config.CONF
+
 
 class AggregatesAdminTestJSON(base.BaseV2ComputeAdminTest):
     """Tests Aggregates API that require admin privileges"""
@@ -39,14 +42,22 @@
         cls.host = None
         hypers = cls.os_adm.hypervisor_client.list_hypervisors(
             detail=True)['hypervisors']
+
+        if CONF.compute.hypervisor_type:
+            hypers = [hyper for hyper in hypers
+                      if (hyper['hypervisor_type'] ==
+                          CONF.compute.hypervisor_type)]
+
         hosts_available = [hyper['service']['host'] for hyper in hypers
                            if (hyper['state'] == 'up' and
                                hyper['status'] == 'enabled')]
         if hosts_available:
             cls.host = hosts_available[0]
         else:
-            raise testtools.TestCase.failureException(
-                "no available compute node found")
+            msg = "no available compute node found"
+            if CONF.compute.hypervisor_type:
+                msg += " for hypervisor_type %s" % CONF.compute.hypervisor_type
+            raise testtools.TestCase.failureException(msg)
 
     @test.idempotent_id('0d148aa3-d54c-4317-aa8d-42040a475e20')
     def test_aggregate_create_delete(self):
diff --git a/tempest/api/compute/admin/test_aggregates_negative.py b/tempest/api/compute/admin/test_aggregates_negative.py
index 609eae6..85aa301 100644
--- a/tempest/api/compute/admin/test_aggregates_negative.py
+++ b/tempest/api/compute/admin/test_aggregates_negative.py
@@ -177,6 +177,5 @@
     def test_aggregate_remove_nonexistent_host(self):
         aggregate = self._create_test_aggregate()
 
-        non_exist_host = data_utils.rand_name('nonexist_host')
         self.assertRaises(lib_exc.NotFound, self.client.remove_host,
-                          aggregate['id'], host=non_exist_host)
+                          aggregate['id'], host='nonexist_host')
diff --git a/tempest/api/compute/admin/test_auto_allocate_network.py b/tempest/api/compute/admin/test_auto_allocate_network.py
index 4eb3376..8e481fd 100644
--- a/tempest/api/compute/admin/test_auto_allocate_network.py
+++ b/tempest/api/compute/admin/test_auto_allocate_network.py
@@ -19,8 +19,8 @@
 from tempest.common import credentials_factory as credentials
 from tempest.common import waiters
 from tempest import config
-from tempest import exceptions
 from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions as lib_excs
 from tempest import test
 
 CONF = config.CONF
@@ -82,14 +82,14 @@
         nets = cls.networks_client.list_networks(
             **search_opts).get('networks', [])
         if nets:
-            raise exceptions.TempestException(
+            raise lib_excs.TempestException(
                 'Found tenant networks: %s' % nets)
         # (2) Retrieve shared network list.
         search_opts = {'shared': True}
         nets = cls.networks_client.list_networks(
             **search_opts).get('networks', [])
         if nets:
-            raise exceptions.TempestException(
+            raise lib_excs.TempestException(
                 'Found shared networks: %s' % nets)
 
     @classmethod
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
index b0ab9be..7eb4bbf 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
@@ -102,11 +102,10 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('440b9f3f-3c7f-4293-a106-0ceda350f8de')
     def test_flavor_unset_nonexistent_key(self):
-        nonexistent_key = data_utils.rand_name('flavor_key')
         self.assertRaises(lib_exc.NotFound,
                           self.client.unset_flavor_extra_spec,
                           self.flavor['id'],
-                          nonexistent_key)
+                          'nonexistent_key')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('329a7be3-54b2-48be-8052-bf2ce4afd898')
diff --git a/tempest/api/compute/admin/test_floating_ips_bulk.py b/tempest/api/compute/admin/test_floating_ips_bulk.py
index e207aed..a4695b0 100644
--- a/tempest/api/compute/admin/test_floating_ips_bulk.py
+++ b/tempest/api/compute/admin/test_floating_ips_bulk.py
@@ -17,6 +17,7 @@
 
 from tempest.api.compute import base
 from tempest import config
+from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions
 from tempest import test
 
@@ -54,12 +55,6 @@
                 raise exceptions.InvalidConfiguration(msg)
         return
 
-    def _delete_floating_ips_bulk(self, ip_range):
-        try:
-            self.client.delete_floating_ips_bulk(ip_range)
-        except Exception:
-            pass
-
     @test.idempotent_id('2c8f145f-8012-4cb8-ac7e-95a587f0e4ab')
     @test.services('network')
     def test_create_list_delete_floating_ips_bulk(self):
@@ -73,7 +68,8 @@
                                                      pool,
                                                      interface)
                 ['floating_ips_bulk_create'])
-        self.addCleanup(self._delete_floating_ips_bulk, self.ip_range)
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.client.delete_floating_ips_bulk, self.ip_range)
         self.assertEqual(self.ip_range, body['ip_range'])
         ips_list = self.client.list_floating_ips_bulk()['floating_ip_info']
         self.assertNotEqual(0, len(ips_list))
diff --git a/tempest/api/compute/admin/test_hosts_negative.py b/tempest/api/compute/admin/test_hosts_negative.py
index c270829..3821b22 100644
--- a/tempest/api/compute/admin/test_hosts_negative.py
+++ b/tempest/api/compute/admin/test_hosts_negative.py
@@ -13,7 +13,6 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common.utils import data_utils
 from tempest.lib import exceptions as lib_exc
 from tempest import test
 
@@ -27,11 +26,13 @@
         cls.client = cls.os_adm.hosts_client
         cls.non_admin_client = cls.os.hosts_client
 
-    def _get_host_name(self):
-        hosts = self.client.list_hosts()['hosts']
-        self.assertGreaterEqual(len(hosts), 1)
-        hostname = hosts[0]['host_name']
-        return hostname
+    @classmethod
+    def resource_setup(cls):
+        super(HostsAdminNegativeTestJSON, cls).resource_setup()
+        hosts = cls.client.list_hosts()['hosts']
+        if not hosts:
+            raise lib_exc.NotFound("no host found")
+        cls.hostname = hosts[0]['host_name']
 
     @test.attr(type=['negative'])
     @test.idempotent_id('dd032027-0210-4d9c-860e-69b1b8deed5f')
@@ -42,27 +43,22 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('e75b0a1a-041f-47a1-8b4a-b72a6ff36d3f')
     def test_show_host_detail_with_nonexistent_hostname(self):
-        nonexitent_hostname = data_utils.rand_name('rand_hostname')
         self.assertRaises(lib_exc.NotFound,
-                          self.client.show_host, nonexitent_hostname)
+                          self.client.show_host, 'nonexistent_hostname')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('19ebe09c-bfd4-4b7c-81a2-e2e0710f59cc')
     def test_show_host_detail_with_non_admin_user(self):
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.show_host,
-                          hostname)
+                          self.hostname)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('e40c72b1-0239-4ed6-ba21-81a184df1f7c')
     def test_update_host_with_non_admin_user(self):
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.update_host,
-                          hostname,
+                          self.hostname,
                           status='enable',
                           maintenance_mode='enable')
 
@@ -70,11 +66,9 @@
     @test.idempotent_id('fbe2bf3e-3246-4a95-a59f-94e4e298ec77')
     def test_update_host_with_invalid_status(self):
         # 'status' can only be 'enable' or 'disable'
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.BadRequest,
                           self.client.update_host,
-                          hostname,
+                          self.hostname,
                           status='invalid',
                           maintenance_mode='enable')
 
@@ -82,11 +76,9 @@
     @test.idempotent_id('ab1e230e-5e22-41a9-8699-82b9947915d4')
     def test_update_host_with_invalid_maintenance_mode(self):
         # 'maintenance_mode' can only be 'enable' or 'disable'
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.BadRequest,
                           self.client.update_host,
-                          hostname,
+                          self.hostname,
                           status='enable',
                           maintenance_mode='invalid')
 
@@ -94,73 +86,57 @@
     @test.idempotent_id('0cd85f75-6992-4a4a-b1bd-d11e37fd0eee')
     def test_update_host_without_param(self):
         # 'status' or 'maintenance_mode' needed for host update
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.BadRequest,
                           self.client.update_host,
-                          hostname)
+                          self.hostname)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('23c92146-2100-4d68-b2d6-c7ade970c9c1')
     def test_update_nonexistent_host(self):
-        nonexitent_hostname = data_utils.rand_name('rand_hostname')
-
         self.assertRaises(lib_exc.NotFound,
                           self.client.update_host,
-                          nonexitent_hostname,
+                          'nonexistent_hostname',
                           status='enable',
                           maintenance_mode='enable')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('0d981ac3-4320-4898-b674-82b61fbb60e4')
     def test_startup_nonexistent_host(self):
-        nonexitent_hostname = data_utils.rand_name('rand_hostname')
-
         self.assertRaises(lib_exc.NotFound,
                           self.client.startup_host,
-                          nonexitent_hostname)
+                          'nonexistent_hostname')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('9f4ebb7e-b2ae-4e5b-a38f-0fd1bb0ddfca')
     def test_startup_host_with_non_admin_user(self):
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.startup_host,
-                          hostname)
+                          self.hostname)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('9e637444-29cf-4244-88c8-831ae82c31b6')
     def test_shutdown_nonexistent_host(self):
-        nonexitent_hostname = data_utils.rand_name('rand_hostname')
-
         self.assertRaises(lib_exc.NotFound,
                           self.client.shutdown_host,
-                          nonexitent_hostname)
+                          'nonexistent_hostname')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('a803529c-7e3f-4d3c-a7d6-8e1c203d27f6')
     def test_shutdown_host_with_non_admin_user(self):
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.shutdown_host,
-                          hostname)
+                          self.hostname)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('f86bfd7b-0b13-4849-ae29-0322e83ee58b')
     def test_reboot_nonexistent_host(self):
-        nonexitent_hostname = data_utils.rand_name('rand_hostname')
-
         self.assertRaises(lib_exc.NotFound,
                           self.client.reboot_host,
-                          nonexitent_hostname)
+                          'nonexistent_hostname')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('02d79bb9-eb57-4612-abf6-2cb38897d2f8')
     def test_reboot_host_with_non_admin_user(self):
-        hostname = self._get_host_name()
-
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.reboot_host,
-                          hostname)
+                          self.hostname)
diff --git a/tempest/api/compute/admin/test_hypervisor_negative.py b/tempest/api/compute/admin/test_hypervisor_negative.py
index 220ea39..0f35e14 100644
--- a/tempest/api/compute/admin/test_hypervisor_negative.py
+++ b/tempest/api/compute/admin/test_hypervisor_negative.py
@@ -122,12 +122,10 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('19a45cc1-1000-4055-b6d2-28e8b2ec4faa')
     def test_search_nonexistent_hypervisor(self):
-        nonexistent_hypervisor_name = data_utils.rand_name('test_hypervisor')
-
         self.assertRaises(
             lib_exc.NotFound,
             self.client.search_hypervisor,
-            nonexistent_hypervisor_name)
+            'nonexistent_hypervisor_name')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('5b6a6c79-5dc1-4fa5-9c58-9c8085948e74')
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index 4f075eb..c9ba730 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -47,12 +47,7 @@
         server = self.create_test_server(wait_until="ACTIVE")
         server_id = server['id']
 
-        self.servers_client.resize_server(server_id, self.flavor_ref_alt)
-        waiters.wait_for_server_status(self.servers_client,
-                                       server_id, 'VERIFY_RESIZE')
-        self.servers_client.confirm_resize_server(server_id)
-        waiters.wait_for_server_status(self.servers_client,
-                                       server_id, 'ACTIVE')
+        self.resize_server(server_id, self.flavor_ref_alt)
 
         body = self.client.list_migrations()['migrations']
 
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 7d97ce2..ce0adb4 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -74,7 +74,8 @@
                          'ram': 10240, 'floating_ips': 20, 'fixed_ips': 10,
                          'key_pairs': 200, 'injected_file_path_bytes': 512,
                          'instances': 20, 'security_group_rules': 20,
-                         'cores': 2, 'security_groups': 20}
+                         'cores': 2, 'security_groups': 20,
+                         'server_groups': 20, 'server_group_members': 20}
         # Update limits for all quota resources
         quota_set = self.adm_client.update_quota_set(
             self.demo_tenant_id,
@@ -82,13 +83,6 @@
             **new_quota_set)['quota_set']
 
         default_quota_set.pop('id')
-        # NOTE(PhilDay) The following is safe as we're not updating these
-        # two quota values yet.  Once the Nova change to add these is merged
-        # and the client updated to support them this can be removed
-        if 'server_groups' in default_quota_set:
-            default_quota_set.pop('server_groups')
-        if 'server_group_members' in default_quota_set:
-            default_quota_set.pop('server_group_members')
         self.addCleanup(self.adm_client.update_quota_set,
                         self.demo_tenant_id, **default_quota_set)
         for quota in new_quota_set:
diff --git a/tempest/api/compute/admin/test_quotas_negative.py b/tempest/api/compute/admin/test_quotas_negative.py
index d6aba5b..015e14d 100644
--- a/tempest/api/compute/admin/test_quotas_negative.py
+++ b/tempest/api/compute/admin/test_quotas_negative.py
@@ -40,6 +40,17 @@
         # tenant most of them should be skipped if we can't do that
         cls.demo_tenant_id = cls.client.tenant_id
 
+    def _update_quota(self, quota_item, quota_value):
+        quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
+                     ['quota_set'])
+        default_quota_value = quota_set[quota_item]
+
+        self.adm_client.update_quota_set(self.demo_tenant_id,
+                                         force=True,
+                                         **{quota_item: quota_value})
+        self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
+                        **{quota_item: default_quota_value})
+
     @test.attr(type=['negative'])
     @test.idempotent_id('733abfe8-166e-47bb-8363-23dbd7ff3476')
     def test_update_quota_normal_user(self):
@@ -54,17 +65,7 @@
     @test.idempotent_id('91058876-9947-4807-9f22-f6eb17140d9b')
     def test_create_server_when_cpu_quota_is_full(self):
         # Disallow server creation when tenant's vcpu quota is full
-        quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
-                     ['quota_set'])
-        default_vcpu_quota = quota_set['cores']
-        vcpu_quota = 0  # Set the quota to zero to conserve resources
-
-        self.adm_client.update_quota_set(self.demo_tenant_id,
-                                         force=True,
-                                         cores=vcpu_quota)
-
-        self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
-                        cores=default_vcpu_quota)
+        self._update_quota('cores', 0)
         self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
                           self.create_test_server)
 
@@ -72,17 +73,7 @@
     @test.idempotent_id('6fdd7012-584d-4327-a61c-49122e0d5864')
     def test_create_server_when_memory_quota_is_full(self):
         # Disallow server creation when tenant's memory quota is full
-        quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
-                     ['quota_set'])
-        default_mem_quota = quota_set['ram']
-        mem_quota = 0  # Set the quota to zero to conserve resources
-
-        self.adm_client.update_quota_set(self.demo_tenant_id,
-                                         force=True,
-                                         ram=mem_quota)
-
-        self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
-                        ram=default_mem_quota)
+        self._update_quota('ram', 0)
         self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
                           self.create_test_server)
 
@@ -90,16 +81,7 @@
     @test.idempotent_id('7c6be468-0274-449a-81c3-ac1c32ee0161')
     def test_create_server_when_instances_quota_is_full(self):
         # Once instances quota limit is reached, disallow server creation
-        quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
-                     ['quota_set'])
-        default_instances_quota = quota_set['instances']
-        instances_quota = 0  # Set quota to zero to disallow server creation
-
-        self.adm_client.update_quota_set(self.demo_tenant_id,
-                                         force=True,
-                                         instances=instances_quota)
-        self.addCleanup(self.adm_client.update_quota_set, self.demo_tenant_id,
-                        instances=default_instances_quota)
+        self._update_quota('instances', 0)
         self.assertRaises((lib_exc.Forbidden, lib_exc.OverLimit),
                           self.create_test_server)
 
@@ -109,22 +91,10 @@
     @test.services('network')
     def test_security_groups_exceed_limit(self):
         # Negative test: Creation Security Groups over limit should FAIL
-
-        quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
-                     ['quota_set'])
-        default_sg_quota = quota_set['security_groups']
-
         # Set the quota to number of used security groups
         sg_quota = self.limits_client.show_limits()['limits']['absolute'][
             'totalSecurityGroupsUsed']
-
-        self.adm_client.update_quota_set(self.demo_tenant_id,
-                                         force=True,
-                                         security_groups=sg_quota)
-
-        self.addCleanup(self.adm_client.update_quota_set,
-                        self.demo_tenant_id,
-                        security_groups=default_sg_quota)
+        self._update_quota('security_groups', sg_quota)
 
         # Check we cannot create anymore
         # A 403 Forbidden or 413 Overlimit (old behaviour) exception
@@ -141,19 +111,7 @@
     def test_security_groups_rules_exceed_limit(self):
         # Negative test: Creation of Security Group Rules should FAIL
         # when we reach limit maxSecurityGroupRules
-
-        quota_set = (self.adm_client.show_quota_set(self.demo_tenant_id)
-                     ['quota_set'])
-        default_sg_rules_quota = quota_set['security_group_rules']
-        sg_rules_quota = 0  # Set the quota to zero to conserve resources
-
-        self.adm_client.update_quota_set(self.demo_tenant_id,
-                                         force=True,
-                                         security_group_rules=sg_rules_quota)
-
-        self.addCleanup(self.adm_client.update_quota_set,
-                        self.demo_tenant_id,
-                        security_group_rules=default_sg_rules_quota)
+        self._update_quota('security_group_rules', 0)
 
         s_name = data_utils.rand_name('securitygroup')
         s_description = data_utils.rand_name('description')
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index d8294f7..173ee83 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -75,7 +75,6 @@
             cls.os.compute_security_group_rules_client)
         cls.security_groups_client = cls.os.compute_security_groups_client
         cls.quotas_client = cls.os.quotas_client
-        cls.quota_classes_client = cls.os.quota_classes_client
         cls.compute_networks_client = cls.os.compute_networks_client
         cls.limits_client = cls.os.limits_client
         cls.volumes_extensions_client = cls.os.volumes_extensions_client
@@ -345,6 +344,15 @@
             LOG.exception('Failed to delete server %s' % server_id)
 
     @classmethod
+    def resize_server(cls, server_id, new_flavor_id, **kwargs):
+        """resize and confirm_resize an server, waits for it to be ACTIVE."""
+        cls.servers_client.resize_server(server_id, new_flavor_id, **kwargs)
+        waiters.wait_for_server_status(cls.servers_client, server_id,
+                                       'VERIFY_RESIZE')
+        cls.servers_client.confirm_resize_server(server_id)
+        waiters.wait_for_server_status(cls.servers_client, server_id, 'ACTIVE')
+
+    @classmethod
     def delete_volume(cls, volume_id):
         """Deletes the given volume and waits for it to be gone."""
         cls._delete_volume(cls.volumes_extensions_client, volume_id)
@@ -373,14 +381,18 @@
             self.request_microversion))
 
     @classmethod
-    def create_volume(cls):
+    def create_volume(cls, image_ref=None):
         """Create a volume and wait for it to become 'available'.
 
+        :param image_ref: Specify an image id to create a bootable volume.
         :returns: The available volume.
         """
         vol_name = data_utils.rand_name(cls.__name__ + '-volume')
-        volume = cls.volumes_client.create_volume(
-            size=CONF.volume.volume_size, display_name=vol_name)['volume']
+        create_params = dict(size=CONF.volume.volume_size,
+                             display_name=vol_name)
+        if image_ref is not None:
+            create_params['imageRef'] = image_ref
+        volume = cls.volumes_client.create_volume(**create_params)['volume']
         cls.volumes.append(volume)
         waiters.wait_for_volume_status(cls.volumes_client,
                                        volume['id'], 'available')
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
index f71f046..31cf39c 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
@@ -23,7 +23,6 @@
 
 
 class FloatingIPsNegativeTestJSON(base.BaseFloatingIPsTest):
-    server_id = None
 
     @classmethod
     def setup_clients(cls):
@@ -38,15 +37,14 @@
         server = cls.create_test_server(wait_until='ACTIVE')
         cls.server_id = server['id']
         # Generating a nonexistent floatingIP id
-        cls.floating_ip_ids = []
         body = cls.client.list_floating_ips()['floating_ips']
-        for i in range(len(body)):
-            cls.floating_ip_ids.append(body[i]['id'])
+        floating_ip_ids = [floating_ip['id'] for floating_ip in body]
         while True:
-            cls.non_exist_id = data_utils.rand_int_id(start=999)
             if CONF.service_available.neutron:
                 cls.non_exist_id = data_utils.rand_uuid()
-            if cls.non_exist_id not in cls.floating_ip_ids:
+            else:
+                cls.non_exist_id = data_utils.rand_int_id(start=999)
+            if cls.non_exist_id not in floating_ip_ids:
                 break
 
     @test.attr(type=['negative'])
diff --git a/tempest/api/compute/images/test_image_metadata_negative.py b/tempest/api/compute/images/test_image_metadata_negative.py
index 9cb9e03..489bfbc 100644
--- a/tempest/api/compute/images/test_image_metadata_negative.py
+++ b/tempest/api/compute/images/test_image_metadata_negative.py
@@ -19,11 +19,11 @@
 from tempest import test
 
 
-class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
+class ImagesMetadataNegativeTestJSON(base.BaseV2ComputeTest):
 
     @classmethod
     def setup_clients(cls):
-        super(ImagesMetadataTestJSON, cls).setup_clients()
+        super(ImagesMetadataNegativeTestJSON, cls).setup_clients()
         cls.client = cls.compute_images_client
 
     @test.attr(type=['negative'])
diff --git a/tempest/api/compute/images/test_images_negative.py b/tempest/api/compute/images/test_images_negative.py
index 7035401..c48367f 100644
--- a/tempest/api/compute/images/test_images_negative.py
+++ b/tempest/api/compute/images/test_images_negative.py
@@ -96,7 +96,7 @@
     def test_delete_non_existent_image(self):
         # Return an error while trying to delete a non-existent image
 
-        non_existent_image_id = '11a22b9-12a9-5555-cc11-00ab112223fa'
+        non_existent_image_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
                           non_existent_image_id)
 
@@ -110,9 +110,9 @@
     @test.idempotent_id('924540c3-f1f1-444c-8f58-718958b6724e')
     def test_delete_image_non_hex_string_id(self):
         # Return an error while trying to delete an image with non hex id
-        image_id = '11a22b9-120q-5555-cc11-00ab112223gj'
+        invalid_image_id = data_utils.rand_uuid()[:-1] + "j"
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
-                          image_id)
+                          invalid_image_id)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('68e2c175-bd26-4407-ac0f-4ea9ce2139ea')
@@ -122,7 +122,8 @@
 
     @test.attr(type=['negative'])
     @test.idempotent_id('b340030d-82cd-4066-a314-c72fb7c59277')
-    def test_delete_image_id_is_over_35_character_limit(self):
+    def test_delete_image_with_id_over_character_limit(self):
         # Return an error while trying to delete image with id over limit
+        invalid_image_id = data_utils.rand_uuid() + "1"
         self.assertRaises(lib_exc.NotFound, self.client.delete_image,
-                          '11a22b9-12a9-5555-cc11-00ab112223fa-3fac')
+                          invalid_image_id)
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index d9b80e1..039283a 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -69,11 +69,6 @@
             raise cls.skipException(skip_msg)
 
     @classmethod
-    def setup_credentials(cls):
-        cls.prepare_instance_network()
-        super(ImagesOneServerNegativeTestJSON, cls).setup_credentials()
-
-    @classmethod
     def setup_clients(cls):
         super(ImagesOneServerNegativeTestJSON, cls).setup_clients()
         cls.client = cls.compute_images_client
diff --git a/tempest/api/compute/limits/test_absolute_limits.py b/tempest/api/compute/limits/test_absolute_limits.py
index 69811f4..6cc722c 100644
--- a/tempest/api/compute/limits/test_absolute_limits.py
+++ b/tempest/api/compute/limits/test_absolute_limits.py
@@ -35,9 +35,10 @@
                              'maxTotalFloatingIps', 'maxSecurityGroups',
                              'maxSecurityGroupRules', 'maxTotalInstances',
                              'maxTotalKeypairs', 'maxTotalRAMSize',
+                             'maxServerGroups', 'maxServerGroupMembers',
                              'totalCoresUsed', 'totalFloatingIpsUsed',
                              'totalSecurityGroupsUsed', 'totalInstancesUsed',
-                             'totalRAMUsed']
+                             'totalRAMUsed', 'totalServerGroupsUsed']
         # check whether all expected elements exist
         missing_elements =\
             [ele for ele in expected_elements if ele not in absolute_limits]
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index fc6a20f..8342a3e 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -50,30 +50,6 @@
         cls.subnets_client = cls.os.subnets_client
         cls.ports_client = cls.os.ports_client
 
-    def wait_for_interface_status(self, server, port_id, status):
-        """Waits for an interface to reach a given status."""
-        body = (self.interfaces_client.show_interface(server, port_id)
-                ['interfaceAttachment'])
-        interface_status = body['port_state']
-        start = int(time.time())
-
-        while(interface_status != status):
-            time.sleep(self.build_interval)
-            body = (self.interfaces_client.show_interface(server, port_id)
-                    ['interfaceAttachment'])
-            interface_status = body['port_state']
-
-            timed_out = int(time.time()) - start >= self.build_timeout
-
-            if interface_status != status and timed_out:
-                message = ('Interface %s failed to reach %s status '
-                           '(current %s) within the required time (%s s).' %
-                           (port_id, status, interface_status,
-                            self.build_timeout))
-                raise lib_exc.TimeoutException(message)
-
-        return body
-
     # TODO(mriedem): move this into a common waiters utility module
     def wait_for_port_detach(self, port_id):
         """Waits for the port's device_id to be unset.
@@ -118,16 +94,16 @@
         server = self.create_test_server(wait_until='ACTIVE')
         ifs = (self.interfaces_client.list_interfaces(server['id'])
                ['interfaceAttachments'])
-        body = self.wait_for_interface_status(
-            server['id'], ifs[0]['port_id'], 'ACTIVE')
+        body = waiters.wait_for_interface_status(
+            self.interfaces_client, server['id'], ifs[0]['port_id'], 'ACTIVE')
         ifs[0]['port_state'] = body['port_state']
         return server, ifs
 
     def _test_create_interface(self, server):
         iface = (self.interfaces_client.create_interface(server['id'])
                  ['interfaceAttachment'])
-        iface = self.wait_for_interface_status(
-            server['id'], iface['port_id'], 'ACTIVE')
+        iface = waiters.wait_for_interface_status(
+            self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
         self._check_interface(iface)
         return iface
 
@@ -135,8 +111,8 @@
         network_id = ifs[0]['net_id']
         iface = self.interfaces_client.create_interface(
             server['id'], net_id=network_id)['interfaceAttachment']
-        iface = self.wait_for_interface_status(
-            server['id'], iface['port_id'], 'ACTIVE')
+        iface = waiters.wait_for_interface_status(
+            self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
         self._check_interface(iface, network_id=network_id)
         return iface
 
@@ -147,8 +123,8 @@
         self.addCleanup(self.ports_client.delete_port, port_id)
         iface = self.interfaces_client.create_interface(
             server['id'], port_id=port_id)['interfaceAttachment']
-        iface = self.wait_for_interface_status(
-            server['id'], iface['port_id'], 'ACTIVE')
+        iface = waiters.wait_for_interface_status(
+            self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
         self._check_interface(iface, port_id=port_id)
         return iface
 
@@ -166,8 +142,8 @@
             server['id'], net_id=network_id,
             fixed_ips=fixed_ips)['interfaceAttachment']
         self.addCleanup(self.ports_client.delete_port, iface['port_id'])
-        iface = self.wait_for_interface_status(
-            server['id'], iface['port_id'], 'ACTIVE')
+        iface = waiters.wait_for_interface_status(
+            self.interfaces_client, server['id'], iface['port_id'], 'ACTIVE')
         self._check_interface(iface, fixed_ip=ip_list[0])
         return iface
 
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
new file mode 100644
index 0000000..7252e1b
--- /dev/null
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -0,0 +1,268 @@
+# Copyright (C) 2016, 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.
+
+import json
+
+from oslo_log import log as logging
+
+from tempest.api.compute import base
+from tempest.common.utils import data_utils
+from tempest.common.utils.linux import remote_client
+from tempest.common import waiters
+from tempest import config
+from tempest.lib import exceptions
+from tempest import test
+
+
+CONF = config.CONF
+
+LOG = logging.getLogger(__name__)
+
+
+class DeviceTaggingTest(base.BaseV2ComputeTest):
+
+    min_microversion = '2.32'
+    max_microversion = 'latest'
+
+    @classmethod
+    def skip_checks(cls):
+        super(DeviceTaggingTest, cls).skip_checks()
+        if not CONF.service_available.neutron:
+            raise cls.skipException('Neutron is required')
+        if not CONF.validation.run_validation:
+            raise cls.skipException('Validation must be enabled')
+        if (not CONF.compute_feature_enabled.config_drive
+            and not CONF.compute_feature_enabled.metadata_service):
+            raise cls.skipException('One of metadata or config drive must be '
+                                    'enabled')
+
+    @classmethod
+    def setup_clients(cls):
+        super(DeviceTaggingTest, cls).setup_clients()
+        cls.networks_client = cls.os.networks_client
+        cls.ports_client = cls.os.ports_client
+        cls.volumes_client = cls.os.volumes_client
+        cls.subnets_client = cls.os.subnets_client
+        cls.routers_client = cls.os.routers_client
+        cls.interfaces_client = cls.os.interfaces_client
+
+    @classmethod
+    def setup_credentials(cls):
+        cls.set_network_resources(network=True, subnet=True, router=True,
+                                  dhcp=True)
+        super(DeviceTaggingTest, cls).setup_credentials()
+
+    @classmethod
+    def resource_setup(cls):
+        cls.set_validation_resources()
+        super(DeviceTaggingTest, cls).resource_setup()
+
+    def verify_device_metadata(self, md_json):
+        md_dict = json.loads(md_json)
+        for d in md_dict['devices']:
+            if d['type'] == 'nic':
+                if d['mac'] == self.port1['mac_address']:
+                    self.assertEqual(d['tags'], ['port-1'])
+                if d['mac'] == self.port2['mac_address']:
+                    self.assertEqual(d['tags'], ['port-2'])
+                if d['mac'] == self.net_2_100_mac:
+                    self.assertEqual(d['tags'], ['net-2-100'])
+                if d['mac'] == self.net_2_200_mac:
+                    self.assertEqual(d['tags'], ['net-2-200'])
+
+        found_devices = [d['tags'][0] for d in md_dict['devices']]
+        self.assertItemsEqual(found_devices, ['port-1', 'port-2', 'net-1',
+                                              'net-2-100', 'net-2-200',
+                                              'boot', 'other'])
+
+    @test.idempotent_id('a2e65a6c-66f1-4442-aaa8-498c31778d96')
+    @test.services('network', 'volume', 'image')
+    def test_device_tagging(self):
+        # Create volumes
+        # The create_volume methods waits for the volumes to be available and
+        # the base class will clean them up on tearDown.
+        boot_volume = self.create_volume(CONF.compute.image_ref)
+        other_volume = self.create_volume()
+        untagged_volume = self.create_volume()
+
+        # Create networks
+        net1 = self.networks_client.create_network(
+            name=data_utils.rand_name('device-tagging-net1'))['network']
+        self.addCleanup(self.networks_client.delete_network, net1['id'])
+
+        net2 = self.networks_client.create_network(
+            name=data_utils.rand_name('device-tagging-net2'))['network']
+        self.addCleanup(self.networks_client.delete_network, net2['id'])
+
+        # Create subnets
+        subnet1 = self.subnets_client.create_subnet(
+            network_id=net1['id'],
+            cidr='10.1.1.0/24',
+            ip_version=4)['subnet']
+        self.addCleanup(self.subnets_client.delete_subnet, subnet1['id'])
+
+        subnet2 = self.subnets_client.create_subnet(
+            network_id=net2['id'],
+            cidr='10.2.2.0/24',
+            ip_version=4)['subnet']
+        self.addCleanup(self.subnets_client.delete_subnet, subnet2['id'])
+
+        # Create ports
+        self.port1 = self.ports_client.create_port(
+            network_id=net1['id'],
+            fixed_ips=[{'subnet_id': subnet1['id']}])['port']
+        self.addCleanup(self.ports_client.delete_port, self.port1['id'])
+
+        self.port2 = self.ports_client.create_port(
+            network_id=net1['id'],
+            fixed_ips=[{'subnet_id': subnet1['id']}])['port']
+        self.addCleanup(self.ports_client.delete_port, self.port2['id'])
+
+        # Create server
+        admin_pass = data_utils.rand_password()
+        config_drive_enabled = CONF.compute_feature_enabled.config_drive
+
+        server = self.create_test_server(
+            validatable=True,
+            config_drive=config_drive_enabled,
+            adminPass=admin_pass,
+            name=data_utils.rand_name('device-tagging-server'),
+            networks=[
+                # Validation network for ssh
+                {
+                    'uuid': self.get_tenant_network()['id']
+                },
+                # Different tags for different ports
+                {
+                    'port': self.port1['id'],
+                    'tag': 'port-1'
+                },
+                {
+                    'port': self.port2['id'],
+                    'tag': 'port-2'
+                },
+                # Two nics on same net, one tagged one not
+                {
+                    'uuid': net1['id'],
+                    'tag': 'net-1'
+                },
+                {
+                    'uuid': net1['id']
+                },
+                # Two nics on same net, different IP
+                {
+                    'uuid': net2['id'],
+                    'fixed_ip': '10.2.2.100',
+                    'tag': 'net-2-100'
+                },
+                {
+                    'uuid': net2['id'],
+                    'fixed_ip': '10.2.2.200',
+                    'tag': 'net-2-200'
+                }
+            ],
+            block_device_mapping_v2=[
+                # Boot volume
+                {
+                    'uuid': boot_volume['id'],
+                    'source_type': 'volume',
+                    'destination_type': 'volume',
+                    'boot_index': 0,
+                    'tag': 'boot'
+                },
+                # Other volume
+                {
+                    'uuid': other_volume['id'],
+                    'source_type': 'volume',
+                    'destination_type': 'volume',
+                    'boot_index': 1,
+                    'tag': 'other'
+                },
+                # Untagged volume
+                {
+                    'uuid': untagged_volume['id'],
+                    'source_type': 'volume',
+                    'destination_type': 'volume',
+                    'boot_index': 2
+                }
+            ])
+
+        self.addCleanup(waiters.wait_for_server_termination,
+                        self.servers_client, server['id'])
+        self.addCleanup(self.servers_client.delete_server, server['id'])
+
+        self.ssh_client = remote_client.RemoteClient(
+            self.get_server_ip(server),
+            CONF.validation.image_ssh_user,
+            admin_pass,
+            self.validation_resources['keypair']['private_key'],
+            server=server,
+            servers_client=self.servers_client)
+
+        # Find the MAC addresses of our fixed IPs
+        self.net_2_100_mac = None
+        self.net_2_200_mac = None
+        ifaces = self.interfaces_client.list_interfaces(server['id'])
+        for iface in ifaces['interfaceAttachments']:
+            if 'fixed_ips' in iface:
+                for ip in iface['fixed_ips']:
+                    if ip['ip_address'] == '10.2.2.100':
+                        self.net_2_100_mac = iface['mac_addr']
+                    if ip['ip_address'] == '10.2.2.200':
+                        self.net_2_200_mac = iface['mac_addr']
+        # Make sure we have the MACs we need, there's no reason for some to be
+        # missing
+        self.assertTrue(self.net_2_100_mac)
+        self.assertTrue(self.net_2_200_mac)
+
+        # Verify metadata from metadata service
+        if CONF.compute_feature_enabled.metadata_service:
+            md_url = 'http://169.254.169.254/openstack/latest/meta_data.json'
+            LOG.info('Attempting to verify tagged devices in server %s via '
+                     'the metadata service: %s', server['id'], md_url)
+
+            def get_and_verify_metadata():
+                try:
+                    self.ssh_client.exec_command('curl -V')
+                except exceptions.SSHExecCommandFailed:
+                    if not CONF.compute_feature_enabled.config_drive:
+                        raise self.skipException('curl not found in guest '
+                                                 'and config drive is '
+                                                 'disabled')
+                    LOG.warning('curl was not found in the guest, device '
+                                'tagging metadata was not checked in the '
+                                'metadata API')
+                    return True
+                cmd = 'curl %s' % md_url
+                md_json = self.ssh_client.exec_command(cmd)
+                self.verify_device_metadata(md_json)
+                return True
+
+            if not test.call_until_true(get_and_verify_metadata,
+                                        CONF.compute.build_timeout,
+                                        CONF.compute.build_interval):
+                raise exceptions.TimeoutException('Timeout while verifying '
+                                                  'metadata on server.')
+
+        # Verify metadata on config drive
+        if CONF.compute_feature_enabled.config_drive:
+            cmd_blkid = 'blkid -t LABEL=config-2 -o device'
+            LOG.info('Attempting to verify tagged devices in server %s via '
+                     'the config drive.', server['id'])
+            dev_name = self.ssh_client.exec_command(cmd_blkid)
+            dev_name = dev_name.rstrip()
+            self.ssh_client.exec_command('sudo mount %s /mnt' % dev_name)
+            cmd_md = 'sudo cat /mnt/openstack/latest/meta_data.json'
+            md_json = self.ssh_client.exec_command(cmd_md)
+            self.verify_device_metadata(md_json)
diff --git a/tempest/api/compute/servers/test_disk_config.py b/tempest/api/compute/servers/test_disk_config.py
index aba0240..ff8ea6e 100644
--- a/tempest/api/compute/servers/test_disk_config.py
+++ b/tempest/api/compute/servers/test_disk_config.py
@@ -94,12 +94,8 @@
         self._update_server_with_disk_config(server['id'],
                                              disk_config='MANUAL')
         # Resize with auto option
-        self.client.resize_server(server['id'], self.flavor_ref_alt,
-                                  disk_config='AUTO')
-        waiters.wait_for_server_status(self.client, server['id'],
-                                       'VERIFY_RESIZE')
-        self.client.confirm_resize_server(server['id'])
-        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
+        self.resize_server(server['id'], self.flavor_ref_alt,
+                           disk_config='AUTO')
 
         server = self.client.show_server(server['id'])['server']
         self.assertEqual('AUTO', server['OS-DCF:diskConfig'])
@@ -114,12 +110,8 @@
         self._update_server_with_disk_config(server['id'],
                                              disk_config='AUTO')
         # Resize with manual option
-        self.client.resize_server(server['id'], self.flavor_ref_alt,
-                                  disk_config='MANUAL')
-        waiters.wait_for_server_status(self.client, server['id'],
-                                       'VERIFY_RESIZE')
-        self.client.confirm_resize_server(server['id'])
-        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
+        self.resize_server(server['id'], self.flavor_ref_alt,
+                           disk_config='MANUAL')
 
         server = self.client.show_server(server['id'])['server']
         self.assertEqual('MANUAL', server['OS-DCF:diskConfig'])
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index fcd5a24..5a35b4e 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -66,8 +66,7 @@
     @test.idempotent_id('ff01387d-c7ad-47b4-ae9e-64fa214638fe')
     def test_list_servers_by_non_existing_image(self):
         # Listing servers for a non existing image returns empty list
-        non_existing_image = '1234abcd-zzz0-aaa9-ppp3-0987654abcde'
-        body = self.client.list_servers(image=non_existing_image)
+        body = self.client.list_servers(image='non_existing_image')
         servers = body['servers']
         self.assertEqual([], servers)
 
@@ -75,8 +74,7 @@
     @test.idempotent_id('5913660b-223b-44d4-a651-a0fbfd44ca75')
     def test_list_servers_by_non_existing_flavor(self):
         # Listing servers by non existing flavor returns empty list
-        non_existing_flavor = 1234
-        body = self.client.list_servers(flavor=non_existing_flavor)
+        body = self.client.list_servers(flavor='non_existing_flavor')
         servers = body['servers']
         self.assertEqual([], servers)
 
@@ -84,8 +82,7 @@
     @test.idempotent_id('e2c77c4a-000a-4af3-a0bd-629a328bde7c')
     def test_list_servers_by_non_existing_server_name(self):
         # Listing servers for a non existent server name returns empty list
-        non_existing_name = 'junk_server_1234'
-        body = self.client.list_servers(name=non_existing_name)
+        body = self.client.list_servers(name='non_existing_server_name')
         servers = body['servers']
         self.assertEqual([], servers)
 
@@ -93,8 +90,7 @@
     @test.idempotent_id('fcdf192d-0f74-4d89-911f-1ec002b822c4')
     def test_list_servers_status_non_existing(self):
         # Return an empty list when invalid status is specified
-        non_existing_status = 'BALONEY'
-        body = self.client.list_servers(status=non_existing_status)
+        body = self.client.list_servers(status='non_existing_status')
         servers = body['servers']
         self.assertEqual([], servers)
 
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 50910ec..0a94d5e 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import logging
-
+from oslo_log import log as logging
 from six.moves.urllib import parse as urlparse
 import testtools
 
diff --git a/tempest/api/compute/servers/test_server_metadata.py b/tempest/api/compute/servers/test_server_metadata.py
index cb66e81..847b7a1 100644
--- a/tempest/api/compute/servers/test_server_metadata.py
+++ b/tempest/api/compute/servers/test_server_metadata.py
@@ -23,7 +23,6 @@
     def setup_clients(cls):
         super(ServerMetadataTestJSON, cls).setup_clients()
         cls.client = cls.servers_client
-        cls.quotas = cls.quotas_client
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/compute/servers/test_server_metadata_negative.py b/tempest/api/compute/servers/test_server_metadata_negative.py
index aae9101..62b8962 100644
--- a/tempest/api/compute/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/servers/test_server_metadata_negative.py
@@ -25,7 +25,6 @@
     def setup_clients(cls):
         super(ServerMetadataNegativeTestJSON, cls).setup_clients()
         cls.client = cls.servers_client
-        cls.quotas = cls.quotas_client
 
     @classmethod
     def resource_setup(cls):
@@ -134,7 +133,8 @@
         # A 403 Forbidden or 413 Overlimit (old behaviour) exception
         # will be raised while exceeding metadata items limit for
         # tenant.
-        quota_set = self.quotas.show_quota_set(self.tenant_id)['quota_set']
+        quota_set = self.quotas_client.show_quota_set(
+            self.tenant_id)['quota_set']
         quota_metadata = quota_set['metadata_items']
         if quota_metadata == -1:
             raise self.skipException("No limit for metadata_items")
diff --git a/tempest/api/compute/servers/test_server_personality.py b/tempest/api/compute/servers/test_server_personality.py
index e5ad7b4..ab291b4 100644
--- a/tempest/api/compute/servers/test_server_personality.py
+++ b/tempest/api/compute/servers/test_server_personality.py
@@ -118,10 +118,12 @@
             raise self.skipException("No limit for personality files")
         person = []
         for i in range(0, int(max_file_limit)):
-            path = '/etc/test' + str(i) + '.txt'
+            # NOTE(andreaf) The cirros disk image is blank before boot
+            # so we can only inject safely to /
+            path = '/test' + str(i) + '.txt'
             person.append({
                 'path': path,
-                'contents': base64.encode_as_text(file_contents),
+                'contents': base64.encode_as_text(file_contents + str(i)),
             })
         password = data_utils.rand_password()
         created_server = self.create_test_server(personality=person,
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index d4831b1..56ec8c6 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import logging
 import testtools
 
 from tempest.api.compute import base
@@ -20,18 +21,17 @@
 from tempest.common.utils.linux import remote_client
 from tempest.common import waiters
 from tempest import config
+from tempest.lib import exceptions as lib_exc
 from tempest import test
 
 CONF = config.CONF
 
+LOG = logging.getLogger(__name__)
+
 
 class AttachVolumeTestJSON(base.BaseV2ComputeTest):
     max_microversion = '2.19'
 
-    def __init__(self, *args, **kwargs):
-        super(AttachVolumeTestJSON, self).__init__(*args, **kwargs)
-        self.attachment = None
-
     @classmethod
     def skip_checks(cls):
         super(AttachVolumeTestJSON, cls).skip_checks()
@@ -47,50 +47,52 @@
     @classmethod
     def resource_setup(cls):
         cls.set_validation_resources()
-
         super(AttachVolumeTestJSON, cls).resource_setup()
         cls.device = CONF.compute.volume_device_name
 
-    def _detach(self, server_id, volume_id):
-        if self.attachment:
-            self.servers_client.detach_volume(server_id, volume_id)
-            waiters.wait_for_volume_status(self.volumes_client,
-                                           volume_id, 'available')
-
     def _create_server(self):
         # Start a server and wait for it to become ready
         server = self.create_test_server(
             validatable=True,
             wait_until='ACTIVE',
             adminPass=self.image_ssh_password)
-
         # Record addresses so that we can ssh later
         server['addresses'] = self.servers_client.list_addresses(
             server['id'])['addresses']
         return server
 
-    def _create_and_attach_volume(self, server):
-        # Create a volume and wait for it to become ready
-        volume = self.create_volume()
-        self.addCleanup(self.delete_volume, volume['id'])
+    def _detach_volume(self, server_id, volume_id):
+        try:
+            self.servers_client.detach_volume(server_id, volume_id)
+            waiters.wait_for_volume_status(self.volumes_client,
+                                           volume_id, 'available')
+        except lib_exc.NotFound:
+            LOG.warning("Unable to detach volume %s from server %s "
+                        "possibly it was already detached" % (volume_id,
+                                                              server_id))
 
+    def _attach_volume(self, server_id, volume_id, device=None):
         # Attach the volume to the server
-        self.attachment = self.servers_client.attach_volume(
-            server['id'],
-            volumeId=volume['id'],
-            device='/dev/%s' % self.device)['volumeAttachment']
+        kwargs = {'volumeId': volume_id}
+        if device:
+            kwargs.update({'device': '/dev/%s' % device})
+        attachment = self.servers_client.attach_volume(
+            server_id, **kwargs)['volumeAttachment']
         waiters.wait_for_volume_status(self.volumes_client,
-                                       volume['id'], 'in-use')
+                                       volume_id, 'in-use')
+        self.addCleanup(self._detach_volume, server_id,
+                        volume_id)
 
-        self.addCleanup(self._detach, server['id'], volume['id'])
-        return volume
+        return attachment
 
     @test.idempotent_id('52e9045a-e90d-4c0d-9087-79d657faffff')
     def test_attach_detach_volume(self):
         # Stop and Start a server with an attached volume, ensuring that
         # the volume remains attached.
         server = self._create_server()
-        volume = self._create_and_attach_volume(server)
+        volume = self.create_volume()
+        attachment = self._attach_volume(server['id'], volume['id'],
+                                         device=self.device)
 
         self.servers_client.stop_server(server['id'])
         waiters.wait_for_server_status(self.servers_client, server['id'],
@@ -113,8 +115,7 @@
             device_name_to_match = ' ' + self.device + '\n'
             self.assertIn(device_name_to_match, partitions)
 
-        self._detach(server['id'], volume['id'])
-        self.attachment = None
+        self._detach_volume(server['id'], attachment['volumeId'])
         self.servers_client.stop_server(server['id'])
         waiters.wait_for_server_status(self.servers_client, server['id'],
                                        'SHUTOFF')
@@ -137,23 +138,53 @@
 
     @test.idempotent_id('7fa563fe-f0f7-43eb-9e22-a1ece036b513')
     def test_list_get_volume_attachments(self):
-        # Create Server, Volume and attach that Volume to Server
+        # List volume attachment of the server
         server = self._create_server()
-        volume = self._create_and_attach_volume(server)
-
-        # List Volume attachment of the server
+        volume = self.create_volume()
+        attachment = self._attach_volume(server['id'], volume['id'],
+                                         device=self.device)
         body = self.servers_client.list_volume_attachments(
             server['id'])['volumeAttachments']
         self.assertEqual(1, len(body))
-        self.assertIn(self.attachment, body)
+        self.assertIn(attachment, body)
 
-        # Get Volume attachment of the server
+        # Get volume attachment of the server
         body = self.servers_client.show_volume_attachment(
             server['id'],
-            self.attachment['id'])['volumeAttachment']
+            attachment['id'])['volumeAttachment']
         self.assertEqual(server['id'], body['serverId'])
         self.assertEqual(volume['id'], body['volumeId'])
-        self.assertEqual(self.attachment['id'], body['id'])
+        self.assertEqual(attachment['id'], body['id'])
+
+    @test.idempotent_id('757d488b-a951-4bc7-b3cd-f417028da08a')
+    def test_list_get_two_volume_attachments(self):
+        # NOTE: This test is using the volume device auto-assignment
+        # without specifying the device ("/dev/sdb", etc). The feature
+        # is supported since Nova Liberty release or later. So this should
+        # be skipped on older clouds.
+        server = self._create_server()
+        volume_1st = self.create_volume()
+        volume_2nd = self.create_volume()
+        attachment_1st = self._attach_volume(server['id'], volume_1st['id'])
+        attachment_2nd = self._attach_volume(server['id'], volume_2nd['id'])
+
+        body = self.servers_client.list_volume_attachments(
+            server['id'])['volumeAttachments']
+        self.assertEqual(2, len(body))
+
+        body = self.servers_client.show_volume_attachment(
+            server['id'],
+            attachment_1st['id'])['volumeAttachment']
+        self.assertEqual(server['id'], body['serverId'])
+        self.assertEqual(attachment_1st['volumeId'], body['volumeId'])
+        self.assertEqual(attachment_1st['id'], body['id'])
+
+        body = self.servers_client.show_volume_attachment(
+            server['id'],
+            attachment_2nd['id'])['volumeAttachment']
+        self.assertEqual(server['id'], body['serverId'])
+        self.assertEqual(attachment_2nd['volumeId'], body['volumeId'])
+        self.assertEqual(attachment_2nd['id'], body['id'])
 
 
 class AttachVolumeShelveTestJSON(AttachVolumeTestJSON):
@@ -219,19 +250,21 @@
         # Create server, count number of volumes on it, shelve
         # server and attach pre-created volume to shelved server
         server = self._create_server()
+        volume = self.create_volume()
         num_vol = self._count_volumes(server)
         self._shelve_server(server)
-        self._create_and_attach_volume(server)
+        attachment = self._attach_volume(server['id'], volume['id'],
+                                         device=self.device)
 
         # Unshelve the instance and check that attached volume exists
         self._unshelve_server_and_check_volumes(server, num_vol + 1)
 
-        # Get Volume attachment of the server
+        # Get volume attachment of the server
         volume_attachment = self.servers_client.show_volume_attachment(
             server['id'],
-            self.attachment['id'])['volumeAttachment']
+            attachment['id'])['volumeAttachment']
         self.assertEqual(server['id'], volume_attachment['serverId'])
-        self.assertEqual(self.attachment['id'], volume_attachment['id'])
+        self.assertEqual(attachment['id'], volume_attachment['id'])
         # Check the mountpoint is not None after unshelve server even in
         # case of shelved_offloaded.
         self.assertIsNotNone(volume_attachment['device'])
@@ -240,16 +273,15 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
                           'Shelve is not available.')
     def test_detach_volume_shelved_or_offload_server(self):
-        # Create server, count number of volumes on it, shelve
+        # Count number of volumes on instance, shelve
         # server and attach pre-created volume to shelved server
         server = self._create_server()
+        volume = self.create_volume()
         num_vol = self._count_volumes(server)
         self._shelve_server(server)
-        volume = self._create_and_attach_volume(server)
-
+        self._attach_volume(server['id'], volume['id'], device=self.device)
         # Detach the volume
-        self._detach(server['id'], volume['id'])
-        self.attachment = None
+        self._detach_volume(server['id'], volume['id'])
 
         # Unshelve the instance and check that we have the expected number of
         # volume(s)
diff --git a/tempest/api/compute/volumes/test_volume_snapshots.py b/tempest/api/compute/volumes/test_volume_snapshots.py
index 460c882..01718cc 100644
--- a/tempest/api/compute/volumes/test_volume_snapshots.py
+++ b/tempest/api/compute/volumes/test_volume_snapshots.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest.common import waiters
@@ -39,6 +41,8 @@
         cls.snapshots_client = cls.snapshots_extensions_client
 
     @test.idempotent_id('cd4ec87d-7825-450d-8040-6e2068f2da8f')
+    @testtools.skipUnless(CONF.volume_feature_enabled.snapshot,
+                          'Cinder volume snapshots are disabled')
     def test_volume_snapshot_create_get_list_delete(self):
         volume = self.create_volume()
         self.addCleanup(self.delete_volume, volume['id'])
diff --git a/tempest/api/compute/volumes/test_volumes_list.py b/tempest/api/compute/volumes/test_volumes_list.py
index c60fcca..7d76731 100644
--- a/tempest/api/compute/volumes/test_volumes_list.py
+++ b/tempest/api/compute/volumes/test_volumes_list.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_log import log as logging
+
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest.common import waiters
@@ -20,6 +22,7 @@
 from tempest import test
 
 CONF = config.CONF
+LOG = logging.getLogger(__name__)
 
 
 class VolumesTestJSON(base.BaseV2ComputeTest):
@@ -59,21 +62,16 @@
                 volume = cls.client.show_volume(volume['id'])['volume']
                 cls.volume_list.append(volume)
                 cls.volume_id_list.append(volume['id'])
-            except Exception:
+            except Exception as exc:
+                LOG.exception(exc)
                 if cls.volume_list:
                     # We could not create all the volumes, though we were able
                     # to create *some* of the volumes. This is typically
                     # because the backing file size of the volume group is
-                    # too small. So, here, we clean up whatever we did manage
-                    # to create and raise a SkipTest
+                    # too small.
                     for volume in cls.volume_list:
                         cls.delete_volume(volume['id'])
-                    msg = ("Failed to create ALL necessary volumes to run "
-                           "test. This typically means that the backing file "
-                           "size of the nova-volumes group is too small to "
-                           "create the 3 volumes needed by this test case")
-                    raise cls.skipException(msg)
-                raise
+                raise exc
 
     @classmethod
     def resource_cleanup(cls):
diff --git a/tempest/api/compute/volumes/test_volumes_negative.py b/tempest/api/compute/volumes/test_volumes_negative.py
index 5fe4cb3..c4041cb 100644
--- a/tempest/api/compute/volumes/test_volumes_negative.py
+++ b/tempest/api/compute/volumes/test_volumes_negative.py
@@ -66,7 +66,7 @@
 
     @test.attr(type=['negative'])
     @test.idempotent_id('131cb3a1-75cc-4d40-b4c3-1317f64719b0')
-    def test_create_volume_with_out_passing_size(self):
+    def test_create_volume_without_passing_size(self):
         # Negative: Should not be able to create volume without passing size
         # in request
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index 50cf258..655e4ef 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -23,7 +23,6 @@
     @classmethod
     def setup_clients(cls):
         super(EndPointsTestJSON, cls).setup_clients()
-        cls.identity_client = cls.client
         cls.client = cls.endpoints_client
 
     @classmethod
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
index b16605e..f0f8707 100644
--- a/tempest/api/identity/admin/v3/test_endpoints_negative.py
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -25,7 +25,6 @@
     @classmethod
     def setup_clients(cls):
         super(EndpointsNegativeTestJSON, cls).setup_clients()
-        cls.identity_client = cls.client
         cls.client = cls.endpoints_client
 
     @classmethod
diff --git a/tempest/api/identity/admin/v3/test_inherits.py b/tempest/api/identity/admin/v3/test_inherits.py
index 955b6fb..c7e8411 100644
--- a/tempest/api/identity/admin/v3/test_inherits.py
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -168,15 +168,16 @@
             self.domain['id'], self.user['id'], src_role['id'])
 
         # List "effective" role assignments from user on the parent project
-        assignments = (
-            self.role_assignments.list_user_project_effective_assignments(
-                self.project['id'], self.user['id']))['role_assignments']
+        params = {'scope.project.id': self.project['id'],
+                  'user.id': self.user['id']}
+        assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
         self.assertNotEmpty(assignments)
 
         # List "effective" role assignments from user on the leaf project
-        assignments = (
-            self.role_assignments.list_user_project_effective_assignments(
-                leaf_project['id'], self.user['id']))['role_assignments']
+        params['scope.project.id'] = leaf_project['id']
+        assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
         self.assertNotEmpty(assignments)
 
         # Revoke role from domain
@@ -185,16 +186,16 @@
 
         # List "effective" role assignments from user on the parent project
         # should return an empty list
-        assignments = (
-            self.role_assignments.list_user_project_effective_assignments(
-                self.project['id'], self.user['id']))['role_assignments']
+        params['scope.project.id'] = self.project['id']
+        assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
         self.assertEmpty(assignments)
 
         # List "effective" role assignments from user on the leaf project
         # should return an empty list
-        assignments = (
-            self.role_assignments.list_user_project_effective_assignments(
-                leaf_project['id'], self.user['id']))['role_assignments']
+        params['scope.project.id'] = leaf_project['id']
+        assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
         self.assertEmpty(assignments)
 
     @test.idempotent_id('9f02ccd9-9b57-46b4-8f77-dd5a736f3a06')
@@ -217,9 +218,10 @@
             self.project['id'], self.user['id'], src_role['id'])
 
         # List "effective" role assignments from user on the leaf project
-        assignments = (
-            self.role_assignments.list_user_project_effective_assignments(
-                leaf_project['id'], self.user['id']))['role_assignments']
+        params = {'scope.project.id': leaf_project['id'],
+                  'user.id': self.user['id']}
+        assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
         self.assertNotEmpty(assignments)
 
         # Revoke role from parent project
@@ -228,7 +230,6 @@
 
         # List "effective" role assignments from user on the leaf project
         # should return an empty list
-        assignments = (
-            self.role_assignments.list_user_project_effective_assignments(
-                leaf_project['id'], self.user['id']))['role_assignments']
+        assignments = self.role_assignments.list_role_assignments(
+            effective=True, **params)['role_assignments']
         self.assertEmpty(assignments)
diff --git a/tempest/api/image/admin/v2/test_images.py b/tempest/api/image/admin/v2/test_images.py
index 9844a67..f22f321 100644
--- a/tempest/api/image/admin/v2/test_images.py
+++ b/tempest/api/image/admin/v2/test_images.py
@@ -34,11 +34,10 @@
     def test_admin_deactivate_reactivate_image(self):
         # Create image by non-admin tenant
         image_name = data_utils.rand_name('image')
-        image = self.client.create_image(name=image_name,
-                                         container_format='bare',
-                                         disk_format='raw',
-                                         visibility='private')
-        self.addCleanup(self.client.delete_image, image['id'])
+        image = self.create_image(name=image_name,
+                                  container_format='bare',
+                                  disk_format='raw',
+                                  visibility='private')
         # upload an image file
         content = data_utils.random_bytes()
         image_file = six.BytesIO(content)
diff --git a/tempest/api/image/base.py b/tempest/api/image/base.py
index 1cc3fa2..812c436 100644
--- a/tempest/api/image/base.py
+++ b/tempest/api/image/base.py
@@ -141,6 +141,7 @@
         cls.client = cls.os.image_client_v2
         cls.namespaces_client = cls.os.namespaces_client
         cls.resource_types_client = cls.os.resource_types_client
+        cls.namespace_properties_client = cls.os.namespace_properties_client
         cls.schemas_client = cls.os.schemas_client
 
     def create_namespace(cls, namespace_name=None, visibility='public',
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 5cf8084..7b9244b 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -83,10 +83,10 @@
         image_name = data_utils.rand_name('image')
         container_format = CONF.image.container_formats[0]
         disk_format = CONF.image.disk_formats[0]
-        image = self.client.create_image(name=image_name,
-                                         container_format=container_format,
-                                         disk_format=disk_format,
-                                         visibility='private')
+        image = self.create_image(name=image_name,
+                                  container_format=container_format,
+                                  disk_format=disk_format,
+                                  visibility='private')
         # Delete Image
         self.client.delete_image(image['id'])
         self.client.wait_for_resource_deletion(image['id'])
@@ -105,11 +105,10 @@
         image_name = data_utils.rand_name('image')
         container_format = CONF.image.container_formats[0]
         disk_format = CONF.image.disk_formats[0]
-        image = self.client.create_image(name=image_name,
-                                         container_format=container_format,
-                                         disk_format=disk_format,
-                                         visibility='private')
-        self.addCleanup(self.client.delete_image, image['id'])
+        image = self.create_image(name=image_name,
+                                  container_format=container_format,
+                                  disk_format=disk_format,
+                                  visibility='private')
         self.assertEqual('queued', image['status'])
 
         # Now try uploading an image file
@@ -129,7 +128,6 @@
 
 
 class ListImagesTest(base.BaseV2ImageTest):
-    """Here we test the listing of image information"""
 
     @classmethod
     def resource_setup(cls):
@@ -157,23 +155,49 @@
         """
         size = random.randint(1024, 4096)
         image_file = six.BytesIO(data_utils.random_bytes(size))
+        tags = [data_utils.rand_name('tag'), data_utils.rand_name('tag')]
         image = cls.create_image(container_format=container_format,
                                  disk_format=disk_format,
-                                 visibility='private')
+                                 visibility='private',
+                                 tags=tags)
         cls.client.store_image_file(image['id'], data=image_file)
+        # Keep the data of one test image so it can be used to filter lists
+        cls.test_data = image
+        cls.test_data['size'] = size
 
         return image['id']
 
+
+class ListUserImagesTest(ListImagesTest):
+    """Here we test the listing of image information"""
+
     def _list_by_param_value_and_assert(self, params):
         """Perform list action with given params and validates result."""
-
+        # Retrieve the list of images that meet the filter
         images_list = self.client.list_images(params=params)['images']
         # Validating params of fetched images
+        msg = 'No images were found that met the filter criteria.'
+        self.assertNotEmpty(images_list, msg)
         for image in images_list:
             for key in params:
                 msg = "Failed to list images by %s" % key
                 self.assertEqual(params[key], image[key], msg)
 
+    def _list_sorted_by_image_size_and_assert(self, params, desc=False):
+        """Validate an image list that has been sorted by size
+
+        Perform list action with given params and validates the results are
+        sorted by image size in either ascending or descending order.
+        """
+        # Retrieve the list of images that meet the filter
+        images_list = self.client.list_images(params=params)['images']
+        # Validate that the list was fetched sorted accordingly
+        msg = 'No images were found that met the filter criteria.'
+        self.assertNotEmpty(images_list, msg)
+        sorted_list = [image['size'] for image in images_list]
+        msg = 'The list of images was not sorted correctly.'
+        self.assertEqual(sorted(sorted_list, reverse=desc), sorted_list, msg)
+
     @test.idempotent_id('1e341d7a-90a9-494c-b143-2cdf2aeb6aee')
     def test_list_no_params(self):
         # Simple test to see all fixture images returned
@@ -185,8 +209,8 @@
 
     @test.idempotent_id('9959ca1d-1aa7-4b7a-a1ea-0fff0499b37e')
     def test_list_images_param_container_format(self):
-        # Test to get all images with container_format='bare'
-        params = {"container_format": "bare"}
+        # Test to get all images with a specific container_format
+        params = {"container_format": self.test_data['container_format']}
         self._list_by_param_value_and_assert(params)
 
     @test.idempotent_id('4a4735a7-f22f-49b6-b0d9-66e1ef7453eb')
@@ -254,6 +278,37 @@
         params = {"owner": image['owner']}
         self._list_by_param_value_and_assert(params)
 
+    @test.idempotent_id('55c8f5f5-bfed-409d-a6d5-4caeda985d7b')
+    def test_list_images_param_name(self):
+        # Test to get images by name
+        params = {'name': self.test_data['name']}
+        self._list_by_param_value_and_assert(params)
+
+    @test.idempotent_id('aa8ac4df-cff9-418b-8d0f-dd9c67b072c9')
+    def test_list_images_param_tag(self):
+        # Test to get images matching a tag
+        params = {'tag': self.test_data['tags'][0]}
+        images_list = self.client.list_images(params=params)['images']
+        # Validating properties of fetched images
+        self.assertNotEmpty(images_list)
+        for image in images_list:
+            msg = ("The image {image_name} does not have the expected tag "
+                   "{expected_tag} among its tags: {observerd_tags}."
+                   .format(image_name=image['name'],
+                           expected_tag=self.test_data['tags'][0],
+                           observerd_tags=image['tags']))
+            self.assertIn(self.test_data['tags'][0], image['tags'], msg)
+
+    @test.idempotent_id('eeadce49-04e0-43b7-aec7-52535d903e7a')
+    def test_list_images_param_sort(self):
+        params = {'sort': 'size:desc'}
+        self._list_sorted_by_image_size_and_assert(params, desc=True)
+
+    @test.idempotent_id('9faaa0c2-c3a5-43e1-8f61-61c54b409a49')
+    def test_list_images_param_sort_key_dir(self):
+        params = {'sort_key': 'size', 'sort_dir': 'desc'}
+        self._list_sorted_by_image_size_and_assert(params, desc=True)
+
     @test.idempotent_id('622b925c-479f-4736-860d-adeaf13bc371')
     def test_get_image_schema(self):
         # Test to get image schema
@@ -267,3 +322,32 @@
         schema = "images"
         body = self.schemas_client.show_schema(schema)
         self.assertEqual("images", body['name'])
+
+
+class ListSharedImagesTest(ListImagesTest):
+    """Here we test the listing of a shared image information"""
+
+    credentials = ['primary', 'alt']
+
+    @classmethod
+    def setup_clients(cls):
+        super(ListSharedImagesTest, cls).setup_clients()
+        cls.image_member_client = cls.os.image_member_client_v2
+        cls.alt_img_client = cls.os_alt.image_client_v2
+
+    @test.idempotent_id('3fa50be4-8e38-4c02-a8db-7811bb780122')
+    def test_list_images_param_member_status(self):
+        # Share one of the images created with the alt user
+        self.image_member_client.create_image_member(
+            image_id=self.test_data['id'],
+            member=self.alt_img_client.tenant_id)
+        # Update the info on the test data so it remains accurate
+        self.test_data['updated_at'] = self.client.show_image(
+            self.test_data['id'])['updated_at']
+        # As an image consumer you need to provide the member_status parameter
+        # along with the visibility=shared parameter in order for it to show
+        # results
+        params = {'member_status': 'pending', 'visibility': 'shared'}
+        fetched_images = self.alt_img_client.list_images(params)['images']
+        self.assertEqual(1, len(fetched_images))
+        self.assertEqual(self.test_data['id'], fetched_images[0]['id'])
diff --git a/tempest/api/image/v2/test_images_member.py b/tempest/api/image/v2/test_images_member.py
index fe8dd65..8a4b334 100644
--- a/tempest/api/image/v2/test_images_member.py
+++ b/tempest/api/image/v2/test_images_member.py
@@ -21,6 +21,8 @@
         image_id = self._create_image()
         member = self.image_member_client.create_image_member(
             image_id, member=self.alt_tenant_id)
+        self.addCleanup(self.image_member_client.delete_image_member,
+                        image_id, self.alt_tenant_id)
         self.assertEqual(member['member_id'], self.alt_tenant_id)
         self.assertEqual(member['image_id'], image_id)
         self.assertEqual(member['status'], 'pending')
@@ -42,6 +44,8 @@
         image_id = self._create_image()
         member = self.image_member_client.create_image_member(
             image_id, member=self.alt_tenant_id)
+        self.addCleanup(self.image_member_client.delete_image_member,
+                        image_id, self.alt_tenant_id)
         self.assertEqual(member['member_id'], self.alt_tenant_id)
         self.assertEqual(member['image_id'], image_id)
         self.assertEqual(member['status'], 'pending')
@@ -56,6 +60,8 @@
         image_id = self._create_image()
         self.image_member_client.create_image_member(
             image_id, member=self.alt_tenant_id)
+        self.addCleanup(self.image_member_client.delete_image_member,
+                        image_id, self.alt_tenant_id)
         self.alt_image_member_client.update_image_member(image_id,
                                                          self.alt_tenant_id,
                                                          status='accepted')
diff --git a/tempest/api/image/v2/test_images_metadefs_namespace_properties.py b/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
new file mode 100644
index 0000000..7113db4
--- /dev/null
+++ b/tempest/api/image/v2/test_images_metadefs_namespace_properties.py
@@ -0,0 +1,57 @@
+#    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.image import base
+from tempest.common.utils import data_utils
+from tempest import test
+
+
+class MetadataNamespacePropertiesTest(base.BaseV2ImageTest):
+    """Test the Metadata definition namespace property basic functionality"""
+
+    @test.idempotent_id('b1a3765e-3a5d-4f6d-a3a7-3ca3476ae768')
+    def test_basic_meta_def_namespace_property(self):
+        # Get the available resource types and use one resource_type
+        body = self.resource_types_client.list_resource_types()
+        resource_name = body['resource_types'][0]['name']
+        enum = ["xen", "qemu", "kvm", "lxc", "uml", "vmware", "hyperv"]
+        # Create a namespace
+        namespace = self.create_namespace()
+        # Create resource type association
+        body = self.resource_types_client.create_resource_type_association(
+            namespace['namespace'], name=resource_name)
+        # Create a property
+        property_title = data_utils.rand_name('property')
+        body = self.namespace_properties_client.create_namespace_property(
+            namespace=namespace['namespace'], title=property_title,
+            name=resource_name, type="string", enum=enum)
+        self.assertEqual(property_title, body['title'])
+        # Show namespace property
+        body = self.namespace_properties_client.show_namespace_properties(
+            namespace['namespace'], resource_name)
+        self.assertEqual(resource_name, body['name'])
+        # Update namespace property
+        update_property_title = data_utils.rand_name('update-property')
+        body = self.namespace_properties_client.update_namespace_properties(
+            namespace['namespace'], resource_name,
+            title=update_property_title, type="string",
+            enum=enum, name=resource_name)
+        self.assertEqual(update_property_title, body['title'])
+        # Delete namespace property
+        self.namespace_properties_client.delete_namespace_property(
+            namespace['namespace'], resource_name)
+        # List namespace properties and validate deletion
+        namespace_property = [
+            namespace_property['title'] for namespace_property in
+            self.namespace_properties_client.list_namespace_properties(
+                namespace['namespace'])['properties']]
+        self.assertNotIn(update_property_title, namespace_property)
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 629926d..deefbeb 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -135,12 +135,12 @@
         super(BaseNetworkTest, cls).resource_cleanup()
 
     @classmethod
-    def create_network(cls, network_name=None):
+    def create_network(cls, network_name=None, **kwargs):
         """Wrapper utility that returns a test network."""
         network_name = network_name or data_utils.rand_name(
-            cls.__name__ + "-network")
+            cls.__name__ + '-test-network')
 
-        body = cls.networks_client.create_network(name=network_name)
+        body = cls.networks_client.create_network(name=network_name, **kwargs)
         network = body['network']
         cls.networks.append(network)
         return network
diff --git a/tempest/api/network/test_networks.py b/tempest/api/network/test_networks.py
index d2056c4..acac22b 100644
--- a/tempest/api/network/test_networks.py
+++ b/tempest/api/network/test_networks.py
@@ -209,12 +209,16 @@
     def test_show_network_fields(self):
         # Verify specific fields of a network
         fields = ['id', 'name']
+        if test.is_extension_enabled('net-mtu', 'network'):
+            fields.append('mtu')
         body = self.networks_client.show_network(self.network['id'],
                                                  fields=fields)
         network = body['network']
         self.assertEqual(sorted(network.keys()), sorted(fields))
         for field_name in fields:
             self.assertEqual(network[field_name], self.network[field_name])
+        self.assertNotIn('tenant_id', network)
+        self.assertNotIn('project_id', network)
 
     @test.attr(type='smoke')
     @test.idempotent_id('f7ffdeda-e200-4a7a-bcbe-05716e86bf43')
@@ -229,6 +233,8 @@
     def test_list_networks_fields(self):
         # Verify specific fields of the networks
         fields = ['id', 'name']
+        if test.is_extension_enabled('net-mtu', 'network'):
+            fields.append('mtu')
         body = self.networks_client.list_networks(fields=fields)
         networks = body['networks']
         self.assertNotEmpty(networks, "Network list returned is empty")
@@ -385,6 +391,21 @@
             network_id=CONF.network.public_network_id)
         self.assertEmpty(body['subnets'], "Public subnets visible")
 
+    @test.idempotent_id('c72c1c0c-2193-4aca-ccc4-b1442640bbbb')
+    @test.requires_ext(extension="standard-attr-description",
+                       service="network")
+    def test_create_update_network_description(self):
+        body = self.create_network(description='d1')
+        self.assertEqual('d1', body['description'])
+        net_id = body['id']
+        body = self.networks_client.list_networks(id=net_id)['networks'][0]
+        self.assertEqual('d1', body['description'])
+        body = self.networks_client.update_network(body['id'],
+                                                   description='d2')
+        self.assertEqual('d2', body['network']['description'])
+        body = self.networks_client.list_networks(id=net_id)['networks'][0]
+        self.assertEqual('d2', body['description'])
+
 
 class BulkNetworkOpsTest(base.BaseNetworkTest):
     """Tests the following operations in the Neutron API:
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index ed6a302..e989b69 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -49,41 +49,31 @@
     @test.idempotent_id('f64403e2-8483-4b34-8ccd-b09a87bcc68c')
     def test_create_show_list_update_delete_router(self):
         # Create a router
-        # NOTE(salv-orlando): Do not invoke self.create_router
-        # as we need to check the response code
-        name = data_utils.rand_name('router-')
-        create_body = self.routers_client.create_router(
-            name=name, external_gateway_info={
-                "network_id": CONF.network.public_network_id},
-            admin_state_up=False)
-        self.addCleanup(self._delete_router, create_body['router']['id'])
-        self.assertEqual(create_body['router']['name'], name)
+        router = self._create_router(
+            admin_state_up=False,
+            external_network_id=CONF.network.public_network_id)
+        self.assertEqual(router['admin_state_up'], False)
         self.assertEqual(
-            create_body['router']['external_gateway_info']['network_id'],
+            router['external_gateway_info']['network_id'],
             CONF.network.public_network_id)
-        self.assertEqual(create_body['router']['admin_state_up'], False)
         # Show details of the created router
-        show_body = self.routers_client.show_router(
-            create_body['router']['id'])
-        self.assertEqual(show_body['router']['name'], name)
+        router_show = self.routers_client.show_router(
+            router['id'])['router']
+        self.assertEqual(router_show['name'], router['name'])
         self.assertEqual(
-            show_body['router']['external_gateway_info']['network_id'],
+            router_show['external_gateway_info']['network_id'],
             CONF.network.public_network_id)
-        self.assertEqual(show_body['router']['admin_state_up'], False)
         # List routers and verify if created router is there in response
-        list_body = self.routers_client.list_routers()
-        routers_list = list()
-        for router in list_body['routers']:
-            routers_list.append(router['id'])
-        self.assertIn(create_body['router']['id'], routers_list)
+        routers = self.routers_client.list_routers()['routers']
+        self.assertIn(router['id'], map(lambda x: x['id'], routers))
         # Update the name of router and verify if it is updated
-        updated_name = 'updated ' + name
-        update_body = self.routers_client.update_router(
-            create_body['router']['id'], name=updated_name)
-        self.assertEqual(update_body['router']['name'], updated_name)
-        show_body = self.routers_client.show_router(
-            create_body['router']['id'])
-        self.assertEqual(show_body['router']['name'], updated_name)
+        updated_name = 'updated' + router['name']
+        router_update = self.routers_client.update_router(
+            router['id'], name=updated_name)['router']
+        self.assertEqual(router_update['name'], updated_name)
+        router_show = self.routers_client.show_router(
+            router['id'])['router']
+        self.assertEqual(router_show['name'], updated_name)
 
     @test.idempotent_id('e54dd3a3-4352-4921-b09d-44369ae17397')
     def test_create_router_setting_project_id(self):
diff --git a/tempest/api/object_storage/test_container_services_negative.py b/tempest/api/object_storage/test_container_services_negative.py
index 7049db0..e6c53ec 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -92,47 +92,39 @@
     def test_get_metadata_headers_with_invalid_container_name(self):
         # Attempts to retrieve metadata headers with an invalid
         # container name.
-        invalid_name = data_utils.rand_name(name="TestInvalidContainer")
-
         self.assertRaises(exceptions.NotFound,
                           self.container_client.list_container_metadata,
-                          invalid_name)
+                          'invalid_container_name')
 
     @test.attr(type=["negative"])
     @test.idempotent_id('125a24fa-90a7-4cfc-b604-44e49d788390')
     def test_update_metadata_with_nonexistent_container_name(self):
         # Attempts to update metadata using a nonexistent container name.
-        nonexistent_name = data_utils.rand_name(
-            name="TestNonexistentContainer")
         metadata = {'animal': 'penguin'}
 
         self.assertRaises(exceptions.NotFound,
                           self.container_client.update_container_metadata,
-                          nonexistent_name, metadata)
+                          'nonexistent_container_name', metadata)
 
     @test.attr(type=["negative"])
     @test.idempotent_id('65387dbf-a0e2-4aac-9ddc-16eb3f1f69ba')
     def test_delete_with_nonexistent_container_name(self):
         # Attempts to delete metadata using a nonexistent container name.
-        nonexistent_name = data_utils.rand_name(
-            name="TestNonexistentContainer")
         metadata = {'animal': 'penguin'}
 
         self.assertRaises(exceptions.NotFound,
                           self.container_client.delete_container_metadata,
-                          nonexistent_name, metadata)
+                          'nonexistent_container_name', metadata)
 
     @test.attr(type=["negative"])
     @test.idempotent_id('14331d21-1e81-420a-beea-19cb5e5207f5')
     def test_list_all_container_objects_with_nonexistent_container(self):
         # Attempts to get a listing of all objects on a container
         # that doesn't exist.
-        nonexistent_name = data_utils.rand_name(
-            name="TestNonexistentContainer")
         params = {'limit': 9999, 'format': 'json'}
         self.assertRaises(exceptions.NotFound,
                           self.container_client.list_container_contents,
-                          nonexistent_name, params)
+                          'nonexistent_container_name', params)
 
     @test.attr(type=["negative"])
     @test.idempotent_id('86b2ab08-92d5-493d-acd2-85f0c848819e')
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index a3792b4..bffcb64 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -10,10 +10,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-
-import logging
-
 import netaddr
+from oslo_log import log as logging
 
 from tempest.api.orchestration import base
 from tempest.common.utils import data_utils
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index 5af83b3..29a161b 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -28,11 +28,6 @@
             raise cls.skipException("Cinder snapshot feature disabled")
 
     @classmethod
-    def setup_clients(cls):
-        super(SnapshotsActionsV2Test, cls).setup_clients()
-        cls.client = cls.snapshots_client
-
-    @classmethod
     def resource_setup(cls):
         super(SnapshotsActionsV2Test, cls).resource_setup()
 
@@ -59,7 +54,7 @@
                 reset_snapshot_status(temp_snapshot['id'], status)
         self.admin_snapshots_client.\
             force_delete_snapshot(temp_snapshot['id'])
-        self.client.wait_for_resource_deletion(temp_snapshot['id'])
+        self.snapshots_client.wait_for_resource_deletion(temp_snapshot['id'])
 
     def _get_progress_alias(self):
         return 'os-extended-snapshot-attributes:progress'
@@ -85,8 +80,9 @@
         progress = '80%'
         status = 'error'
         progress_alias = self._get_progress_alias()
-        self.client.update_snapshot_status(self.snapshot['id'],
-                                           status=status, progress=progress)
+        self.snapshots_client.update_snapshot_status(self.snapshot['id'],
+                                                     status=status,
+                                                     progress=progress)
         snapshot_get = self.admin_snapshots_client.show_snapshot(
             self.snapshot['id'])['snapshot']
         self.assertEqual(status, snapshot_get['status'])
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index eb6500c..3098cab 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -51,8 +51,7 @@
                   'size': CONF.volume.volume_size}
 
         # Create volume
-        volume = self.volumes_client.create_volume(**params)['volume']
-        self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
+        volume = self.create_volume(**params)
         self.assertEqual(volume_types[0]['name'], volume["volume_type"])
         self.assertEqual(volume[self.name_field], vol_name,
                          "The created volume name is not equal "
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index e7a3f62..261e652 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -23,11 +23,6 @@
 class VolumesActionsV2Test(base.BaseVolumeAdminTest):
 
     @classmethod
-    def setup_clients(cls):
-        super(VolumesActionsV2Test, cls).setup_clients()
-        cls.client = cls.volumes_client
-
-    @classmethod
     def resource_setup(cls):
         super(VolumesActionsV2Test, cls).resource_setup()
 
@@ -47,7 +42,7 @@
             self.admin_volume_client.reset_volume_status(
                 temp_volume['id'], status=status)
         self.admin_volume_client.force_delete_volume(temp_volume['id'])
-        self.client.wait_for_resource_deletion(temp_volume['id'])
+        self.volumes_client.wait_for_resource_deletion(temp_volume['id'])
 
     @test.idempotent_id('d063f96e-a2e0-4f34-8b8a-395c42de1845')
     def test_volume_reset_status(self):
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 73f1f8f..61d4ba7 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -33,12 +33,6 @@
         if not CONF.volume_feature_enabled.backup:
             raise cls.skipException("Cinder backup feature disabled")
 
-    @classmethod
-    def resource_setup(cls):
-        super(VolumesBackupsAdminV2Test, cls).resource_setup()
-
-        cls.volume = cls.create_volume()
-
     def _delete_backup(self, backup_id):
         self.admin_backups_client.delete_backup(backup_id)
         self.admin_backups_client.wait_for_resource_deletion(backup_id)
@@ -62,14 +56,13 @@
         Cinder allows exporting DB backup information through its API so it can
         be imported back in case of a DB loss.
         """
+        volume = self.create_volume()
         # Create backup
         backup_name = data_utils.rand_name(self.__class__.__name__ + '-Backup')
-        backup = (self.admin_backups_client.create_backup(
-            volume_id=self.volume['id'], name=backup_name)['backup'])
-        self.addCleanup(self._delete_backup, backup['id'])
+        backup = (self.create_backup(backup_client=self.admin_backups_client,
+                                     volume_id=volume['id'],
+                                     name=backup_name))
         self.assertEqual(backup_name, backup['name'])
-        waiters.wait_for_backup_status(self.admin_backups_client,
-                                       backup['id'], 'available')
 
         # Export Backup
         export_backup = (self.admin_backups_client.export_backup(backup['id'])
@@ -126,16 +119,15 @@
 
     @test.idempotent_id('47a35425-a891-4e13-961c-c45deea21e94')
     def test_volume_backup_reset_status(self):
+        # Create a volume
+        volume = self.create_volume()
         # Create a backup
         backup_name = data_utils.rand_name(
             self.__class__.__name__ + '-Backup')
-        backup = self.admin_backups_client.create_backup(
-            volume_id=self.volume['id'], name=backup_name)['backup']
-        self.addCleanup(self.admin_backups_client.delete_backup,
-                        backup['id'])
+        backup = self.create_backup(backup_client=self.admin_backups_client,
+                                    volume_id=volume['id'],
+                                    name=backup_name)
         self.assertEqual(backup_name, backup['name'])
-        waiters.wait_for_backup_status(self.admin_backups_client,
-                                       backup['id'], 'available')
         # Reset backup status to error
         self.admin_backups_client.reset_backup_status(backup_id=backup['id'],
                                                       status="error")
diff --git a/tempest/api/volume/admin/v2/test_volume_type_access.py b/tempest/api/volume/admin/v2/test_volume_type_access.py
index 91ff5af..80dbf12 100644
--- a/tempest/api/volume/admin/v2/test_volume_type_access.py
+++ b/tempest/api/volume/admin/v2/test_volume_type_access.py
@@ -16,7 +16,6 @@
 import operator
 
 from tempest.api.volume import base
-from tempest.common import waiters
 from tempest import config
 from tempest.lib import exceptions as lib_exc
 from tempest import test
@@ -52,13 +51,7 @@
                         project=self.volumes_client.tenant_id)
 
         # Creating a volume from primary tenant
-        volume = self.volumes_client.create_volume(
-            volume_type=volume_type['id'],
-            size=CONF.volume.volume_size)['volume']
-        self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
-        waiters.wait_for_volume_status(self.volumes_client, volume['id'],
-                                       'available')
-
+        volume = self.create_volume(volume_type=volume_type['id'])
         # Validating the created volume is based on the volume type
         self.assertEqual(volume_type['name'], volume['volume_type'])
 
diff --git a/tempest/api/volume/admin/v3/test_user_messages.py b/tempest/api/volume/admin/v3/test_user_messages.py
index 39a5dfa..257a434 100755
--- a/tempest/api/volume/admin/v3/test_user_messages.py
+++ b/tempest/api/volume/admin/v3/test_user_messages.py
@@ -15,9 +15,7 @@
 
 from tempest.api.volume.v3 import base
 from tempest.common.utils import data_utils
-from tempest.common import waiters
 from tempest import config
-from tempest import exceptions
 from tempest import test
 
 CONF = config.CONF
@@ -47,21 +45,11 @@
                        'vendor_name': bad_vendor}
         vol_type_name = data_utils.rand_name(
             self.__class__.__name__ + '-volume-type')
-        bogus_type = self.admin_volume_types_client.create_volume_type(
-            name=vol_type_name,
-            extra_specs=extra_specs)['volume_type']
-        self.addCleanup(self.admin_volume_types_client.delete_volume_type,
-                        bogus_type['id'])
+        bogus_type = self.create_volume_type(
+            name=vol_type_name, extra_specs=extra_specs)
         params = {'volume_type': bogus_type['id'],
                   'size': CONF.volume.volume_size}
-        volume = self.volumes_client.create_volume(**params)['volume']
-        self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
-        try:
-            waiters.wait_for_volume_status(self.volumes_client, volume['id'],
-                                           'error')
-        except exceptions.VolumeBuildErrorException:
-            # Error state is expected and desired
-            pass
+        volume = self.create_volume(wait_until="error", **params)
         messages = self.messages_client.list_messages()['messages']
         message_id = None
         for message in messages:
diff --git a/tempest/api/volume/api_microversion_fixture.py b/tempest/api/volume/api_microversion_fixture.py
index 6817eaa..64bc537 100644
--- a/tempest/api/volume/api_microversion_fixture.py
+++ b/tempest/api/volume/api_microversion_fixture.py
@@ -13,7 +13,7 @@
 
 import fixtures
 
-from tempest.services.volume.base import base_v3_client
+from tempest.lib.services.volume.v3 import base_client
 
 
 class APIMicroversionFixture(fixtures.Fixture):
@@ -23,8 +23,8 @@
 
     def _setUp(self):
         super(APIMicroversionFixture, self)._setUp()
-        base_v3_client.VOLUME_MICROVERSION = self.volume_microversion
+        base_client.VOLUME_MICROVERSION = self.volume_microversion
         self.addCleanup(self._reset_volume_microversion)
 
     def _reset_volume_microversion(self):
-        base_v3_client.VOLUME_MICROVERSION = None
+        base_client.VOLUME_MICROVERSION = None
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index b9aeb99..01e2c82 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -73,6 +73,7 @@
             cls.volumes_extension_client = cls.os.volumes_extension_client
             cls.availability_zone_client = (
                 cls.os.volume_availability_zone_client)
+            cls.volume_limits_client = cls.os.volume_limits_client
         else:
             cls.snapshots_client = cls.os.snapshots_v2_client
             cls.volumes_client = cls.os.volumes_v2_client
@@ -80,6 +81,7 @@
             cls.volumes_extension_client = cls.os.volumes_v2_extension_client
             cls.availability_zone_client = (
                 cls.os.volume_v2_availability_zone_client)
+            cls.volume_limits_client = cls.os.volume_v2_limits_client
 
     @classmethod
     def resource_setup(cls):
@@ -108,11 +110,20 @@
         super(BaseVolumeTest, cls).resource_cleanup()
 
     @classmethod
-    def create_volume(cls, **kwargs):
-        """Wrapper utility that returns a test volume."""
+    def create_volume(cls, wait_until='available', **kwargs):
+        """Wrapper utility that returns a test volume.
+
+           :param wait_until: wait till volume status.
+        """
         if 'size' not in kwargs:
             kwargs['size'] = CONF.volume.volume_size
 
+        if 'imageRef' in kwargs:
+            image = cls.compute_images_client.show_image(
+                kwargs['imageRef'])['image']
+            min_disk = image.get('minDisk')
+            kwargs['size'] = max(kwargs['size'], min_disk)
+
         name_field = cls.special_fields['name_field']
         if name_field not in kwargs:
             name = data_utils.rand_name(cls.__name__ + '-Volume')
@@ -120,8 +131,8 @@
 
         volume = cls.volumes_client.create_volume(**kwargs)['volume']
         cls.volumes.append(volume)
-        waiters.wait_for_volume_status(cls.volumes_client,
-                                       volume['id'], 'available')
+        waiters.wait_for_volume_status(cls.volumes_client, volume['id'],
+                                       wait_until)
         return volume
 
     @classmethod
@@ -139,6 +150,18 @@
                                          snapshot['id'], 'available')
         return snapshot
 
+    def create_backup(self, volume_id, backup_client=None, **kwargs):
+        """Wrapper utility that returns a test backup."""
+        if backup_client is None:
+            backup_client = self.backups_client
+
+        backup = backup_client.create_backup(
+            volume_id=volume_id, **kwargs)['backup']
+        self.addCleanup(backup_client.delete_backup, backup['id'])
+        waiters.wait_for_backup_status(backup_client, backup['id'],
+                                       'available')
+        return backup
+
     # NOTE(afazekas): these create_* and clean_* could be defined
     # only in a single location in the source, and could be more general.
 
@@ -148,6 +171,18 @@
         client.delete_volume(volume_id)
         client.wait_for_resource_deletion(volume_id)
 
+    def attach_volume(self, server_id, volume_id):
+        """Attachs a volume to a server"""
+        self.servers_client.attach_volume(
+            server_id, volumeId=volume_id,
+            device='/dev/%s' % CONF.compute.volume_device_name)
+        waiters.wait_for_volume_status(self.volumes_client,
+                                       volume_id, 'in-use')
+        self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
+                        volume_id, 'available')
+        self.addCleanup(self.servers_client.detach_volume, server_id,
+                        self.volume_origin['id'])
+
     @classmethod
     def clear_volumes(cls):
         for volume in cls.volumes:
@@ -217,6 +252,7 @@
             cls.admin_encryption_types_client = \
                 cls.os_adm.encryption_types_client
             cls.admin_quotas_client = cls.os_adm.volume_quotas_client
+            cls.admin_volume_limits_client = cls.os_adm.volume_limits_client
         elif cls._api_version == 2:
             cls.admin_volume_qos_client = cls.os_adm.volume_qos_v2_client
             cls.admin_volume_services_client = \
@@ -229,6 +265,7 @@
             cls.admin_encryption_types_client = \
                 cls.os_adm.encryption_types_v2_client
             cls.admin_quotas_client = cls.os_adm.volume_quotas_v2_client
+            cls.admin_volume_limits_client = cls.os_adm.volume_v2_limits_client
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/volume/test_snapshot_metadata.py b/tempest/api/volume/test_snapshot_metadata.py
index 688baf5..d0fa07e 100644
--- a/tempest/api/volume/test_snapshot_metadata.py
+++ b/tempest/api/volume/test_snapshot_metadata.py
@@ -30,70 +30,53 @@
             raise cls.skipException("Cinder snapshot feature disabled")
 
     @classmethod
-    def setup_clients(cls):
-        super(SnapshotV2MetadataTestJSON, cls).setup_clients()
-        cls.client = cls.snapshots_client
-
-    @classmethod
     def resource_setup(cls):
         super(SnapshotV2MetadataTestJSON, cls).resource_setup()
         # Create a volume
         cls.volume = cls.create_volume()
         # Create a snapshot
         cls.snapshot = cls.create_snapshot(volume_id=cls.volume['id'])
-        cls.snapshot_id = cls.snapshot['id']
 
     def tearDown(self):
         # Update the metadata to {}
-        self.client.update_snapshot_metadata(self.snapshot_id, metadata={})
+        self.snapshots_client.update_snapshot_metadata(
+            self.snapshot['id'], metadata={})
         super(SnapshotV2MetadataTestJSON, self).tearDown()
 
     @test.idempotent_id('a2f20f99-e363-4584-be97-bc33afb1a56c')
-    def test_create_get_delete_snapshot_metadata(self):
+    def test_crud_snapshot_metadata(self):
         # Create metadata for the snapshot
         metadata = {"key1": "value1",
                     "key2": "value2",
                     "key3": "value3"}
-        expected = {"key2": "value2",
-                    "key3": "value3"}
-        body = self.client.create_snapshot_metadata(
-            self.snapshot_id, metadata)['metadata']
-        # Get the metadata of the snapshot
-        body = self.client.show_snapshot_metadata(
-            self.snapshot_id)['metadata']
-        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
-
-        # Delete one item metadata of the snapshot
-        self.client.delete_snapshot_metadata_item(
-            self.snapshot_id, "key1")
-        body = self.client.show_snapshot_metadata(
-            self.snapshot_id)['metadata']
-        self.assertThat(body.items(), matchers.ContainsAll(expected.items()))
-        self.assertNotIn("key1", body)
-
-    @test.idempotent_id('bd2363bc-de92-48a4-bc98-28943c6e4be1')
-    def test_update_snapshot_metadata(self):
-        # Update metadata for the snapshot
-        metadata = {"key1": "value1",
-                    "key2": "value2",
-                    "key3": "value3"}
         update = {"key3": "value3_update",
                   "key4": "value4"}
-        # Create metadata for the snapshot
-        body = self.client.create_snapshot_metadata(
-            self.snapshot_id, metadata)['metadata']
-        # Get the metadata of the snapshot
-        body = self.client.show_snapshot_metadata(
-            self.snapshot_id)['metadata']
-        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
+        expect = {"key4": "value4"}
+        # Create metadata
+        body = self.snapshots_client.create_snapshot_metadata(
+            self.snapshot['id'], metadata)['metadata']
 
-        # Update metadata item
-        body = self.client.update_snapshot_metadata(
-            self.snapshot_id, metadata=update)['metadata']
         # Get the metadata of the snapshot
-        body = self.client.show_snapshot_metadata(
-            self.snapshot_id)['metadata']
-        self.assertEqual(update, body)
+        body = self.snapshots_client.show_snapshot_metadata(
+            self.snapshot['id'])['metadata']
+        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()),
+                        'Create snapshot metadata failed')
+
+        # Update metadata
+        body = self.snapshots_client.update_snapshot_metadata(
+            self.snapshot['id'], metadata=update)['metadata']
+        body = self.snapshots_client.show_snapshot_metadata(
+            self.snapshot['id'])['metadata']
+        self.assertEqual(update, body, 'Update snapshot metadata failed')
+
+        # Delete one item metadata of the snapshot
+        self.snapshots_client.delete_snapshot_metadata_item(
+            self.snapshot['id'], "key3")
+        body = self.snapshots_client.show_snapshot_metadata(
+            self.snapshot['id'])['metadata']
+        self.assertThat(body.items(), matchers.ContainsAll(expect.items()),
+                        'Delete one item metadata of the snapshot failed')
+        self.assertNotIn("key3", body)
 
     @test.idempotent_id('e8ff85c5-8f97-477f-806a-3ac364a949ed')
     def test_update_snapshot_metadata_item(self):
@@ -106,18 +89,18 @@
                   "key2": "value2",
                   "key3": "value3_update"}
         # Create metadata for the snapshot
-        body = self.client.create_snapshot_metadata(
-            self.snapshot_id, metadata)['metadata']
+        body = self.snapshots_client.create_snapshot_metadata(
+            self.snapshot['id'], metadata)['metadata']
         # Get the metadata of the snapshot
-        body = self.client.show_snapshot_metadata(
-            self.snapshot_id)['metadata']
+        body = self.snapshots_client.show_snapshot_metadata(
+            self.snapshot['id'])['metadata']
         self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
         # Update metadata item
-        body = self.client.update_snapshot_metadata_item(
-            self.snapshot_id, "key3", meta=update_item)['meta']
+        body = self.snapshots_client.update_snapshot_metadata_item(
+            self.snapshot['id'], "key3", meta=update_item)['meta']
         # Get the metadata of the snapshot
-        body = self.client.show_snapshot_metadata(
-            self.snapshot_id)['metadata']
+        body = self.snapshots_client.show_snapshot_metadata(
+            self.snapshot['id'])['metadata']
         self.assertThat(body.items(), matchers.ContainsAll(expect.items()))
 
 
diff --git a/tempest/api/volume/test_volume_absolute_limits.py b/tempest/api/volume/test_volume_absolute_limits.py
new file mode 100644
index 0000000..bc7694a
--- /dev/null
+++ b/tempest/api/volume/test_volume_absolute_limits.py
@@ -0,0 +1,49 @@
+# 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 AbsoluteLimitsV2Tests(base.BaseVolumeTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(AbsoluteLimitsV2Tests, cls).resource_setup()
+        # Create a shared volume for tests
+        cls.volume = cls.create_volume()
+
+    @test.idempotent_id('8e943f53-e9d6-4272-b2e9-adcf2f7c29ad')
+    def test_get_volume_absolute_limits(self):
+        # get volume limit for a tenant
+        absolute_limits = \
+            self.volume_limits_client.show_limits(
+            )['limits']['absolute']
+
+        # verify volume limits and defaults per tenants
+        self.assertEqual(absolute_limits['totalGigabytesUsed'],
+                         CONF.volume.volume_size)
+        self.assertEqual(absolute_limits['totalVolumesUsed'], 1)
+        self.assertEqual(absolute_limits['totalSnapshotsUsed'], 0)
+        self.assertEqual(absolute_limits['totalBackupsUsed'], 0)
+        self.assertEqual(absolute_limits['totalBackupGigabytesUsed'], 0)
+
+
+class AbsoluteLimitsV1Tests(AbsoluteLimitsV2Tests):
+    _api_version = 1
diff --git a/tempest/api/volume/test_volume_metadata.py b/tempest/api/volume/test_volume_metadata.py
index ee1744d..c125bb8 100644
--- a/tempest/api/volume/test_volume_metadata.py
+++ b/tempest/api/volume/test_volume_metadata.py
@@ -33,52 +33,39 @@
         super(VolumesV2MetadataTest, self).tearDown()
 
     @test.idempotent_id('6f5b125b-f664-44bf-910f-751591fe5769')
-    def test_create_get_delete_volume_metadata(self):
+    def test_crud_volume_metadata(self):
         # Create metadata for the volume
         metadata = {"key1": "value1",
                     "key2": "value2",
                     "key3": "value3",
                     "key4": "<value&special_chars>"}
+        update = {"key4": "value4",
+                  "key1": "value1_update"}
+        expected = {"key4": "value4"}
 
         body = self.volumes_client.create_volume_metadata(self.volume['id'],
                                                           metadata)['metadata']
         # Get the metadata of the volume
         body = self.volumes_client.show_volume_metadata(
             self.volume['id'])['metadata']
-        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
+        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()),
+                        'Create metadata for the volume failed')
+
+        # Update metadata
+        body = self.volumes_client.update_volume_metadata(
+            self.volume['id'], update)['metadata']
+        body = self.volumes_client.show_volume_metadata(
+            self.volume['id'])['metadata']
+        self.assertEqual(update, body, 'Update metadata failed')
+
         # Delete one item metadata of the volume
         self.volumes_client.delete_volume_metadata_item(
             self.volume['id'], "key1")
         body = self.volumes_client.show_volume_metadata(
             self.volume['id'])['metadata']
         self.assertNotIn("key1", body)
-        del metadata["key1"]
-        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
-
-    @test.idempotent_id('774d2918-9beb-4f30-b3d1-2a4e8179ec0a')
-    def test_update_volume_metadata(self):
-        # Update metadata for the volume
-        metadata = {"key1": "value1",
-                    "key2": "value2",
-                    "key3": "value3"}
-
-        update = {"key4": "value4",
-                  "key1": "value1_update"}
-
-        # Create metadata for the volume
-        body = self.volumes_client.create_volume_metadata(
-            self.volume['id'], metadata)['metadata']
-        # Get the metadata of the volume
-        body = self.volumes_client.show_volume_metadata(
-            self.volume['id'])['metadata']
-        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
-        # Update metadata
-        body = self.volumes_client.update_volume_metadata(
-            self.volume['id'], update)['metadata']
-        # Get the metadata of the volume
-        body = self.volumes_client.show_volume_metadata(
-            self.volume['id'])['metadata']
-        self.assertEqual(update, body)
+        self.assertThat(body.items(), matchers.ContainsAll(expected.items()),
+                        'Delete one item metadata of the volume failed')
 
     @test.idempotent_id('862261c5-8df4-475a-8c21-946e50e36a20')
     def test_update_volume_metadata_item(self):
@@ -93,7 +80,8 @@
         # Create metadata for the volume
         body = self.volumes_client.create_volume_metadata(
             self.volume['id'], metadata)['metadata']
-        self.assertThat(body.items(), matchers.ContainsAll(metadata.items()))
+        self.assertThat(body.items(),
+                        matchers.ContainsAll(metadata.items()))
         # Update metadata item
         body = self.volumes_client.update_volume_metadata_item(
             self.volume['id'], "key3", update_item)['meta']
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 737ce5e..d8d6b9a 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -12,7 +12,6 @@
 #    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.api.volume import base
 from tempest.common.utils import data_utils
@@ -67,8 +66,6 @@
                                        self.volume['id'], 'available')
 
     @test.idempotent_id('63e21b4c-0a0c-41f6-bfc3-7c2816815599')
-    @testtools.skipUnless(CONF.volume_feature_enabled.bootable,
-                          'Update bootable status of a volume is not enabled.')
     def test_volume_bootable(self):
         # Verify that a volume bootable flag is retrieved
         for bool_bootable in [True, False]:
@@ -77,8 +74,11 @@
             fetched_volume = self.client.show_volume(
                 self.volume['id'])['volume']
             # Get Volume information
-            bool_flag = self._is_true(fetched_volume['bootable'])
-            self.assertEqual(bool_bootable, bool_flag)
+            # NOTE(masayukig): 'bootable' is "true" or "false" in the current
+            # cinder implementation. So we need to cast boolean values to str
+            # and make it lower to compare here.
+            self.assertEqual(str(bool_bootable).lower(),
+                             fetched_volume['bootable'])
 
     @test.idempotent_id('9516a2c8-9135-488c-8dd6-5677a7e5f371')
     @test.services('compute')
@@ -139,9 +139,6 @@
         body = self.client.show_volume(self.volume['id'])['volume']
         self.assertIn('available', body['status'])
 
-    def _is_true(self, val):
-        return val in ['true', 'True', True]
-
     @test.idempotent_id('fff74e1e-5bd3-4b33-9ea9-24c103bc3f59')
     def test_volume_readonly_update(self):
         for readonly in [True, False]:
@@ -151,8 +148,11 @@
             # Get Volume information
             fetched_volume = self.client.show_volume(
                 self.volume['id'])['volume']
-            bool_flag = self._is_true(fetched_volume['metadata']['readonly'])
-            self.assertEqual(readonly, bool_flag)
+            # NOTE(masayukig): 'readonly' is "True" or "False" in the current
+            # cinder implementation. So we need to cast boolean values to str
+            # to compare here.
+            self.assertEqual(str(readonly),
+                             fetched_volume['metadata']['readonly'])
 
 
 class VolumesV1ActionsTest(VolumesV2ActionsTest):
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index 141336f..972dd58 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -38,16 +38,11 @@
                         volume['id'])
         backup_name = data_utils.rand_name(
             self.__class__.__name__ + '-Backup')
-        create_backup = self.backups_client.create_backup
-        backup = create_backup(volume_id=volume['id'],
-                               name=backup_name)['backup']
-        self.addCleanup(self.backups_client.delete_backup,
-                        backup['id'])
+        backup = self.create_backup(volume_id=volume['id'],
+                                    name=backup_name)
         self.assertEqual(backup_name, backup['name'])
         waiters.wait_for_volume_status(self.volumes_client,
                                        volume['id'], 'available')
-        waiters.wait_for_backup_status(self.backups_client,
-                                       backup['id'], 'available')
 
         # Get a given backup
         backup = self.backups_client.show_backup(backup['id'])['backup']
@@ -97,12 +92,8 @@
         # Create backup using force flag
         backup_name = data_utils.rand_name(
             self.__class__.__name__ + '-Backup')
-        backup = self.backups_client.create_backup(
-            volume_id=volume['id'],
-            name=backup_name, force=True)['backup']
-        self.addCleanup(self.backups_client.delete_backup, backup['id'])
-        waiters.wait_for_backup_status(self.backups_client,
-                                       backup['id'], 'available')
+        backup = self.create_backup(volume_id=volume['id'],
+                                    name=backup_name, force=True)
         self.assertEqual(backup_name, backup['name'])
 
 
diff --git a/tempest/api/volume/test_volumes_clone.py b/tempest/api/volume/test_volumes_clone.py
index 7529dc2..2cedb4e 100644
--- a/tempest/api/volume/test_volumes_clone.py
+++ b/tempest/api/volume/test_volumes_clone.py
@@ -21,11 +21,11 @@
 CONF = config.CONF
 
 
-class VolumesCloneTest(base.BaseVolumeTest):
+class VolumesV2CloneTest(base.BaseVolumeTest):
 
     @classmethod
     def skip_checks(cls):
-        super(VolumesCloneTest, cls).skip_checks()
+        super(VolumesV2CloneTest, cls).skip_checks()
         if not CONF.volume_feature_enabled.clone:
             raise cls.skipException("Cinder volume clones are disabled")
 
@@ -45,6 +45,22 @@
         self.assertEqual(volume['source_volid'], src_vol['id'])
         self.assertEqual(int(volume['size']), src_size + 1)
 
+    @test.idempotent_id('cbbcd7c6-5a6c-481a-97ac-ca55ab715d16')
+    def test_create_from_bootable_volume(self):
+        # Create volume from image
+        img_uuid = CONF.compute.image_ref
+        src_vol = self.create_volume(imageRef=img_uuid)
 
-class VolumesV1CloneTest(VolumesCloneTest):
+        # Create a volume from the bootable volume
+        cloned_vol = self.create_volume(source_volid=src_vol['id'])
+        cloned_vol_details = self.volumes_client.show_volume(
+            cloned_vol['id'])['volume']
+
+        # Verify cloned volume creation as expected
+        self.assertEqual('true', cloned_vol_details['bootable'])
+        self.assertEqual(src_vol['id'], cloned_vol_details['source_volid'])
+        self.assertEqual(src_vol['size'], cloned_vol_details['size'])
+
+
+class VolumesV1CloneTest(VolumesV2CloneTest):
     _api_version = 1
diff --git a/tempest/api/volume/test_volumes_clone_negative.py b/tempest/api/volume/test_volumes_clone_negative.py
index d1bedb4..5c54e1e 100644
--- a/tempest/api/volume/test_volumes_clone_negative.py
+++ b/tempest/api/volume/test_volumes_clone_negative.py
@@ -22,11 +22,11 @@
 CONF = config.CONF
 
 
-class VolumesCloneTest(base.BaseVolumeTest):
+class VolumesV2CloneNegativeTest(base.BaseVolumeTest):
 
     @classmethod
     def skip_checks(cls):
-        super(VolumesCloneTest, cls).skip_checks()
+        super(VolumesV2CloneNegativeTest, cls).skip_checks()
         if not CONF.volume_feature_enabled.clone:
             raise cls.skipException("Cinder volume clones are disabled")
 
@@ -44,5 +44,5 @@
                           source_volid=src_vol['id'])
 
 
-class VolumesV1CloneTest(VolumesCloneTest):
+class VolumesV1CloneNegativeTest(VolumesV2CloneNegativeTest):
     _api_version = 1
diff --git a/tempest/api/volume/test_volumes_extend.py b/tempest/api/volume/test_volumes_extend.py
index 7aea1c4..c3d6dbb 100644
--- a/tempest/api/volume/test_volumes_extend.py
+++ b/tempest/api/volume/test_volumes_extend.py
@@ -20,20 +20,16 @@
 
 class VolumesV2ExtendTest(base.BaseVolumeTest):
 
-    @classmethod
-    def setup_clients(cls):
-        super(VolumesV2ExtendTest, cls).setup_clients()
-        cls.client = cls.volumes_client
-
     @test.idempotent_id('9a36df71-a257-43a5-9555-dc7c88e66e0e')
     def test_volume_extend(self):
         # Extend Volume Test.
         self.volume = self.create_volume()
         extend_size = int(self.volume['size']) + 1
-        self.client.extend_volume(self.volume['id'], new_size=extend_size)
-        waiters.wait_for_volume_status(self.client,
+        self.volumes_client.extend_volume(self.volume['id'],
+                                          new_size=extend_size)
+        waiters.wait_for_volume_status(self.volumes_client,
                                        self.volume['id'], 'available')
-        volume = self.client.show_volume(self.volume['id'])['volume']
+        volume = self.volumes_client.show_volume(self.volume['id'])['volume']
         self.assertEqual(int(volume['size']), extend_size)
 
 
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 51de2be..65e461c 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -27,39 +27,31 @@
 
 class VolumesV2GetTest(base.BaseVolumeTest):
 
-    @classmethod
-    def setup_clients(cls):
-        super(VolumesV2GetTest, cls).setup_clients()
-        cls.client = cls.volumes_client
-
-    @classmethod
-    def resource_setup(cls):
-        super(VolumesV2GetTest, cls).resource_setup()
-
-        cls.name_field = cls.special_fields['name_field']
-        cls.descrip_field = cls.special_fields['descrip_field']
-
     def _volume_create_get_update_delete(self, **kwargs):
+        name_field = self.special_fields['name_field']
+        descrip_field = self.special_fields['descrip_field']
+
         # Create a volume, Get it's details and Delete the volume
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'Test'}
         # Create a volume
-        kwargs[self.name_field] = v_name
+        kwargs[name_field] = v_name
         kwargs['metadata'] = metadata
-        volume = self.client.create_volume(**kwargs)['volume']
+        volume = self.volumes_client.create_volume(**kwargs)['volume']
         self.assertIn('id', volume)
-        self.addCleanup(self.delete_volume, self.client, volume['id'])
-        waiters.wait_for_volume_status(self.client, volume['id'], 'available')
-        self.assertIn(self.name_field, volume)
-        self.assertEqual(volume[self.name_field], v_name,
+        self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
+        waiters.wait_for_volume_status(self.volumes_client, volume['id'],
+                                       'available')
+        self.assertIn(name_field, volume)
+        self.assertEqual(volume[name_field], v_name,
                          "The created volume name is not equal "
                          "to the requested name")
-        self.assertIsNotNone(volume['id'],
-                             "Field volume id is empty or not found.")
+
         # Get Volume information
-        fetched_volume = self.client.show_volume(volume['id'])['volume']
+        fetched_volume = self.volumes_client.show_volume(
+            volume['id'])['volume']
         self.assertEqual(v_name,
-                         fetched_volume[self.name_field],
+                         fetched_volume[name_field],
                          'The fetched Volume name is different '
                          'from the created Volume')
         self.assertEqual(volume['id'],
@@ -73,53 +65,56 @@
 
         if 'imageRef' in kwargs:
             self.assertEqual('true', fetched_volume['bootable'])
-        if 'imageRef' not in kwargs:
+        else:
             self.assertEqual('false', fetched_volume['bootable'])
 
         # Update Volume
         # Test volume update when display_name is same with original value
-        params = {self.name_field: v_name}
-        self.client.update_volume(volume['id'], **params)
+        params = {name_field: v_name}
+        self.volumes_client.update_volume(volume['id'], **params)
         # Test volume update when display_name is new
         new_v_name = data_utils.rand_name(
             self.__class__.__name__ + '-new-Volume')
         new_desc = 'This is the new description of volume'
-        params = {self.name_field: new_v_name,
-                  self.descrip_field: new_desc}
-        update_volume = self.client.update_volume(
+        params = {name_field: new_v_name,
+                  descrip_field: new_desc}
+        update_volume = self.volumes_client.update_volume(
             volume['id'], **params)['volume']
         # Assert response body for update_volume method
-        self.assertEqual(new_v_name, update_volume[self.name_field])
-        self.assertEqual(new_desc, update_volume[self.descrip_field])
+        self.assertEqual(new_v_name, update_volume[name_field])
+        self.assertEqual(new_desc, update_volume[descrip_field])
         # Assert response body for show_volume method
-        updated_volume = self.client.show_volume(volume['id'])['volume']
+        updated_volume = self.volumes_client.show_volume(
+            volume['id'])['volume']
         self.assertEqual(volume['id'], updated_volume['id'])
-        self.assertEqual(new_v_name, updated_volume[self.name_field])
-        self.assertEqual(new_desc, updated_volume[self.descrip_field])
+        self.assertEqual(new_v_name, updated_volume[name_field])
+        self.assertEqual(new_desc, updated_volume[descrip_field])
         self.assertThat(updated_volume['metadata'].items(),
                         matchers.ContainsAll(metadata.items()),
                         'The fetched Volume metadata misses data '
                         'from the created Volume')
+
         # Test volume create when display_name is none and display_description
         # contains specific characters,
         # then test volume update if display_name is duplicated
         new_v_desc = data_utils.rand_name('@#$%^* description')
-        params = {self.descrip_field: new_v_desc,
+        params = {descrip_field: new_v_desc,
                   'availability_zone': volume['availability_zone'],
                   'size': CONF.volume.volume_size}
-        new_volume = self.client.create_volume(**params)['volume']
+        new_volume = self.volumes_client.create_volume(**params)['volume']
         self.assertIn('id', new_volume)
-        self.addCleanup(self.delete_volume, self.client, new_volume['id'])
-        waiters.wait_for_volume_status(self.client,
+        self.addCleanup(self.delete_volume, self.volumes_client,
+                        new_volume['id'])
+        waiters.wait_for_volume_status(self.volumes_client,
                                        new_volume['id'], 'available')
 
-        params = {self.name_field: volume[self.name_field],
-                  self.descrip_field: volume[self.descrip_field]}
-        self.client.update_volume(new_volume['id'], **params)
+        params = {name_field: volume[name_field],
+                  descrip_field: volume[descrip_field]}
+        self.volumes_client.update_volume(new_volume['id'], **params)
 
         if 'imageRef' in kwargs:
             self.assertEqual('true', updated_volume['bootable'])
-        if 'imageRef' not in kwargs:
+        else:
             self.assertEqual('false', updated_volume['bootable'])
 
     @test.attr(type='smoke')
diff --git a/tempest/api/volume/test_volumes_list.py b/tempest/api/volume/test_volumes_list.py
index 40793ec..030ea6c 100644
--- a/tempest/api/volume/test_volumes_list.py
+++ b/tempest/api/volume/test_volumes_list.py
@@ -55,11 +55,6 @@
                              [str_vol(v) for v in fetched_list]))
 
     @classmethod
-    def setup_clients(cls):
-        super(VolumesV2ListTestJSON, cls).setup_clients()
-        cls.client = cls.volumes_client
-
-    @classmethod
     def resource_setup(cls):
         super(VolumesV2ListTestJSON, cls).resource_setup()
         cls.name = cls.VOLUME_FIELDS[1]
@@ -68,16 +63,17 @@
         cls.metadata = {'Type': 'work'}
         for i in range(3):
             volume = cls.create_volume(metadata=cls.metadata)
-            volume = cls.client.show_volume(volume['id'])['volume']
+            volume = cls.volumes_client.show_volume(volume['id'])['volume']
             cls.volume_list.append(volume)
 
     def _list_by_param_value_and_assert(self, params, with_detail=False):
         """list or list_details with given params and validates result"""
         if with_detail:
             fetched_vol_list = \
-                self.client.list_volumes(detail=True, params=params)['volumes']
+                self.volumes_client.list_volumes(detail=True,
+                                                 params=params)['volumes']
         else:
-            fetched_vol_list = self.client.list_volumes(
+            fetched_vol_list = self.volumes_client.list_volumes(
                 params=params)['volumes']
 
         # Validating params of fetched volumes
@@ -103,7 +99,7 @@
     def test_volume_list(self):
         # Get a list of Volumes
         # Fetch all volumes
-        fetched_list = self.client.list_volumes()['volumes']
+        fetched_list = self.volumes_client.list_volumes()['volumes']
         self.assertVolumesIn(fetched_list, self.volume_list,
                              fields=self.VOLUME_FIELDS)
 
@@ -111,14 +107,15 @@
     def test_volume_list_with_details(self):
         # Get a list of Volumes with details
         # Fetch all Volumes
-        fetched_list = self.client.list_volumes(detail=True)['volumes']
+        fetched_list = self.volumes_client.list_volumes(detail=True)['volumes']
         self.assertVolumesIn(fetched_list, self.volume_list)
 
     @test.idempotent_id('a28e8da4-0b56-472f-87a8-0f4d3f819c02')
     def test_volume_list_by_name(self):
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
         params = {self.name: volume[self.name]}
-        fetched_vol = self.client.list_volumes(params=params)['volumes']
+        fetched_vol = self.volumes_client.list_volumes(
+            params=params)['volumes']
         self.assertEqual(1, len(fetched_vol), str(fetched_vol))
         self.assertEqual(fetched_vol[0][self.name],
                          volume[self.name])
@@ -127,7 +124,7 @@
     def test_volume_list_details_by_name(self):
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
         params = {self.name: volume[self.name]}
-        fetched_vol = self.client.list_volumes(
+        fetched_vol = self.volumes_client.list_volumes(
             detail=True, params=params)['volumes']
         self.assertEqual(1, len(fetched_vol), str(fetched_vol))
         self.assertEqual(fetched_vol[0][self.name],
@@ -136,7 +133,8 @@
     @test.idempotent_id('39654e13-734c-4dab-95ce-7613bf8407ce')
     def test_volumes_list_by_status(self):
         params = {'status': 'available'}
-        fetched_list = self.client.list_volumes(params=params)['volumes']
+        fetched_list = self.volumes_client.list_volumes(
+            params=params)['volumes']
         self._list_by_param_value_and_assert(params)
         self.assertVolumesIn(fetched_list, self.volume_list,
                              fields=self.VOLUME_FIELDS)
@@ -144,7 +142,7 @@
     @test.idempotent_id('2943f712-71ec-482a-bf49-d5ca06216b9f')
     def test_volumes_list_details_by_status(self):
         params = {'status': 'available'}
-        fetched_list = self.client.list_volumes(
+        fetched_list = self.volumes_client.list_volumes(
             detail=True, params=params)['volumes']
         for volume in fetched_list:
             self.assertEqual('available', volume['status'])
@@ -158,7 +156,8 @@
         in volume_list are not a bootable volume.
         """
         params = {'bootable': 'false'}
-        fetched_list = self.client.list_volumes(params=params)['volumes']
+        fetched_list = self.volumes_client.list_volumes(
+            params=params)['volumes']
         self._list_by_param_value_and_assert(params)
         self.assertVolumesIn(fetched_list, self.volume_list,
                              fields=self.VOLUME_FIELDS)
@@ -166,7 +165,7 @@
     @test.idempotent_id('2016a939-72ec-482a-bf49-d5ca06216b9f')
     def test_volumes_list_details_by_bootable(self):
         params = {'bootable': 'false'}
-        fetched_list = self.client.list_volumes(
+        fetched_list = self.volumes_client.list_volumes(
             detail=True, params=params)['volumes']
         for volume in fetched_list:
             self.assertEqual('false', volume['bootable'])
@@ -177,7 +176,8 @@
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
         zone = volume['availability_zone']
         params = {'availability_zone': zone}
-        fetched_list = self.client.list_volumes(params=params)['volumes']
+        fetched_list = self.volumes_client.list_volumes(
+            params=params)['volumes']
         self._list_by_param_value_and_assert(params)
         self.assertVolumesIn(fetched_list, self.volume_list,
                              fields=self.VOLUME_FIELDS)
@@ -187,7 +187,7 @@
         volume = self.volume_list[data_utils.rand_int_id(0, 2)]
         zone = volume['availability_zone']
         params = {'availability_zone': zone}
-        fetched_list = self.client.list_volumes(
+        fetched_list = self.volumes_client.list_volumes(
             detail=True, params=params)['volumes']
         for volume in fetched_list:
             self.assertEqual(zone, volume['availability_zone'])
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index fda0dda..c45ace6 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -22,11 +22,6 @@
 class VolumesV2NegativeTest(base.BaseVolumeTest):
 
     @classmethod
-    def setup_clients(cls):
-        super(VolumesV2NegativeTest, cls).setup_clients()
-        cls.client = cls.volumes_client
-
-    @classmethod
     def resource_setup(cls):
         super(VolumesV2NegativeTest, cls).resource_setup()
 
@@ -40,14 +35,14 @@
     @test.idempotent_id('f131c586-9448-44a4-a8b0-54ca838aa43e')
     def test_volume_get_nonexistent_volume_id(self):
         # Should not be able to get a non-existent volume
-        self.assertRaises(lib_exc.NotFound, self.client.show_volume,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.show_volume,
                           data_utils.rand_uuid())
 
     @test.attr(type=['negative'])
     @test.idempotent_id('555efa6e-efcd-44ef-8a3b-4a7ca4837a29')
     def test_volume_delete_nonexistent_volume_id(self):
         # Should not be able to delete a non-existent Volume
-        self.assertRaises(lib_exc.NotFound, self.client.delete_volume,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.delete_volume,
                           data_utils.rand_uuid())
 
     @test.attr(type=['negative'])
@@ -57,17 +52,19 @@
         # in request
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.create_volume,
                           size='#$%', display_name=v_name, metadata=metadata)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('9387686f-334f-4d31-a439-33494b9e2683')
-    def test_create_volume_with_out_passing_size(self):
+    def test_create_volume_without_passing_size(self):
         # Should not be able to create volume without passing size
         # in request
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.create_volume,
                           size='', display_name=v_name, metadata=metadata)
 
     @test.attr(type=['negative'])
@@ -76,7 +73,8 @@
         # Should not be able to create volume with size zero
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.create_volume,
                           size='0', display_name=v_name, metadata=metadata)
 
     @test.attr(type=['negative'])
@@ -85,7 +83,8 @@
         # Should not be able to create volume with size negative
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.create_volume,
                           size='-1', display_name=v_name, metadata=metadata)
 
     @test.attr(type=['negative'])
@@ -94,7 +93,7 @@
         # Should not be able to create volume with non-existent volume type
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.NotFound, self.client.create_volume,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
                           size='1', volume_type=data_utils.rand_uuid(),
                           display_name=v_name, metadata=metadata)
 
@@ -104,7 +103,7 @@
         # Should not be able to create volume with non-existent snapshot
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.NotFound, self.client.create_volume,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
                           size='1', snapshot_id=data_utils.rand_uuid(),
                           display_name=v_name, metadata=metadata)
 
@@ -114,7 +113,7 @@
         # Should not be able to create volume with non-existent source volume
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.NotFound, self.client.create_volume,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
                           size='1', source_volid=data_utils.rand_uuid(),
                           display_name=v_name, metadata=metadata)
 
@@ -123,7 +122,7 @@
     def test_update_volume_with_nonexistent_volume_id(self):
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.NotFound, self.client.update_volume,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
                           volume_id=data_utils.rand_uuid(),
                           display_name=v_name,
                           metadata=metadata)
@@ -133,7 +132,7 @@
     def test_update_volume_with_invalid_volume_id(self):
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.NotFound, self.client.update_volume,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
                           volume_id='#$%%&^&^', display_name=v_name,
                           metadata=metadata)
 
@@ -142,7 +141,7 @@
     def test_update_volume_with_empty_volume_id(self):
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
-        self.assertRaises(lib_exc.NotFound, self.client.update_volume,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
                           volume_id='', display_name=v_name,
                           metadata=metadata)
 
@@ -150,27 +149,29 @@
     @test.idempotent_id('30799cfd-7ee4-446c-b66c-45b383ed211b')
     def test_get_invalid_volume_id(self):
         # Should not be able to get volume with invalid id
-        self.assertRaises(lib_exc.NotFound, self.client.show_volume,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.show_volume,
                           '#$%%&^&^')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('c6c3db06-29ad-4e91-beb0-2ab195fe49e3')
     def test_get_volume_without_passing_volume_id(self):
         # Should not be able to get volume when empty ID is passed
-        self.assertRaises(lib_exc.NotFound, self.client.show_volume, '')
+        self.assertRaises(lib_exc.NotFound,
+                          self.volumes_client.show_volume, '')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('1f035827-7c32-4019-9240-b4ec2dbd9dfd')
     def test_delete_invalid_volume_id(self):
         # Should not be able to delete volume when invalid ID is passed
-        self.assertRaises(lib_exc.NotFound, self.client.delete_volume,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.delete_volume,
                           '!@#$%^&*()')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('441a1550-5d44-4b30-af0f-a6d402f52026')
     def test_delete_volume_without_passing_volume_id(self):
         # Should not be able to delete volume when empty ID is passed
-        self.assertRaises(lib_exc.NotFound, self.client.delete_volume, '')
+        self.assertRaises(lib_exc.NotFound,
+                          self.volumes_client.delete_volume, '')
 
     @test.attr(type=['negative'])
     @test.idempotent_id('f5e56b0a-5d02-43c1-a2a7-c9b792c2e3f6')
@@ -179,7 +180,7 @@
         server = self.create_server(wait_until='ACTIVE')
 
         self.assertRaises(lib_exc.NotFound,
-                          self.client.attach_volume,
+                          self.volumes_client.attach_volume,
                           data_utils.rand_uuid(),
                           instance_uuid=server['id'],
                           mountpoint=self.mountpoint)
@@ -188,7 +189,7 @@
     @test.idempotent_id('9f9c24e4-011d-46b5-b992-952140ce237a')
     def test_detach_volumes_with_invalid_volume_id(self):
         self.assertRaises(lib_exc.NotFound,
-                          self.client.detach_volume,
+                          self.volumes_client.detach_volume,
                           'xxx')
 
     @test.attr(type=['negative'])
@@ -196,7 +197,8 @@
     def test_volume_extend_with_size_smaller_than_original_size(self):
         # Extend volume with smaller size than original size.
         extend_size = 0
-        self.assertRaises(lib_exc.BadRequest, self.client.extend_volume,
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.extend_volume,
                           self.volume['id'], new_size=extend_size)
 
     @test.attr(type=['negative'])
@@ -204,7 +206,8 @@
     def test_volume_extend_with_non_number_size(self):
         # Extend volume when size is non number.
         extend_size = 'abc'
-        self.assertRaises(lib_exc.BadRequest, self.client.extend_volume,
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.extend_volume,
                           self.volume['id'], new_size=extend_size)
 
     @test.attr(type=['negative'])
@@ -212,7 +215,8 @@
     def test_volume_extend_with_None_size(self):
         # Extend volume with None size.
         extend_size = None
-        self.assertRaises(lib_exc.BadRequest, self.client.extend_volume,
+        self.assertRaises(lib_exc.BadRequest,
+                          self.volumes_client.extend_volume,
                           self.volume['id'], new_size=extend_size)
 
     @test.attr(type=['negative'])
@@ -220,7 +224,7 @@
     def test_volume_extend_with_nonexistent_volume_id(self):
         # Extend volume size when volume is nonexistent.
         extend_size = int(self.volume['size']) + 1
-        self.assertRaises(lib_exc.NotFound, self.client.extend_volume,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.extend_volume,
                           data_utils.rand_uuid(), new_size=extend_size)
 
     @test.attr(type=['negative'])
@@ -228,41 +232,42 @@
     def test_volume_extend_without_passing_volume_id(self):
         # Extend volume size when passing volume id is None.
         extend_size = int(self.volume['size']) + 1
-        self.assertRaises(lib_exc.NotFound, self.client.extend_volume,
+        self.assertRaises(lib_exc.NotFound, self.volumes_client.extend_volume,
                           None, new_size=extend_size)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('ac6084c0-0546-45f9-b284-38a367e0e0e2')
     def test_reserve_volume_with_nonexistent_volume_id(self):
         self.assertRaises(lib_exc.NotFound,
-                          self.client.reserve_volume,
+                          self.volumes_client.reserve_volume,
                           data_utils.rand_uuid())
 
     @test.attr(type=['negative'])
     @test.idempotent_id('eb467654-3dc1-4a72-9b46-47c29d22654c')
     def test_unreserve_volume_with_nonexistent_volume_id(self):
         self.assertRaises(lib_exc.NotFound,
-                          self.client.unreserve_volume,
+                          self.volumes_client.unreserve_volume,
                           data_utils.rand_uuid())
 
     @test.attr(type=['negative'])
     @test.idempotent_id('449c4ed2-ecdd-47bb-98dc-072aeccf158c')
     def test_reserve_volume_with_negative_volume_status(self):
         # Mark volume as reserved.
-        self.client.reserve_volume(self.volume['id'])
+        self.volumes_client.reserve_volume(self.volume['id'])
         # Mark volume which is marked as reserved before
         self.assertRaises(lib_exc.BadRequest,
-                          self.client.reserve_volume,
+                          self.volumes_client.reserve_volume,
                           self.volume['id'])
         # Unmark volume as reserved.
-        self.client.unreserve_volume(self.volume['id'])
+        self.volumes_client.unreserve_volume(self.volume['id'])
 
     @test.attr(type=['negative'])
     @test.idempotent_id('0f4aa809-8c7b-418f-8fb3-84c7a5dfc52f')
     def test_list_volumes_with_nonexistent_name(self):
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         params = {self.name_field: v_name}
-        fetched_volume = self.client.list_volumes(params=params)['volumes']
+        fetched_volume = self.volumes_client.list_volumes(
+            params=params)['volumes']
         self.assertEqual(0, len(fetched_volume))
 
     @test.attr(type=['negative'])
@@ -271,14 +276,16 @@
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         params = {self.name_field: v_name}
         fetched_volume = \
-            self.client.list_volumes(detail=True, params=params)['volumes']
+            self.volumes_client.list_volumes(
+                detail=True, params=params)['volumes']
         self.assertEqual(0, len(fetched_volume))
 
     @test.attr(type=['negative'])
     @test.idempotent_id('143b279b-7522-466b-81be-34a87d564a7c')
     def test_list_volumes_with_invalid_status(self):
         params = {'status': 'null'}
-        fetched_volume = self.client.list_volumes(params=params)['volumes']
+        fetched_volume = self.volumes_client.list_volumes(
+            params=params)['volumes']
         self.assertEqual(0, len(fetched_volume))
 
     @test.attr(type=['negative'])
@@ -286,7 +293,8 @@
     def test_list_volumes_detail_with_invalid_status(self):
         params = {'status': 'null'}
         fetched_volume = \
-            self.client.list_volumes(detail=True, params=params)['volumes']
+            self.volumes_client.list_volumes(detail=True,
+                                             params=params)['volumes']
         self.assertEqual(0, len(fetched_volume))
 
 
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 3c05d3e..3c7a2c8 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -12,7 +12,6 @@
 
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
-from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -46,21 +45,53 @@
         # Create a snapshot when volume status is in-use
         # Create a test instance
         server = self.create_server(wait_until='ACTIVE')
-        self.servers_client.attach_volume(
-            server['id'], volumeId=self.volume_origin['id'],
-            device='/dev/%s' % CONF.compute.volume_device_name)
-        waiters.wait_for_volume_status(self.volumes_client,
-                                       self.volume_origin['id'], 'in-use')
-        self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
-                        self.volume_origin['id'], 'available')
-        self.addCleanup(self.servers_client.detach_volume, server['id'],
-                        self.volume_origin['id'])
+        self.attach_volume(server['id'], self.volume_origin['id'])
+
         # Snapshot a volume even if it's attached to an instance
         snapshot = self.create_snapshot(self.volume_origin['id'],
                                         force=True)
         # Delete the snapshot
         self.cleanup_snapshot(snapshot)
 
+    @test.idempotent_id('8567b54c-4455-446d-a1cf-651ddeaa3ff2')
+    @test.services('compute')
+    def test_snapshot_delete_with_volume_in_use(self):
+        # Create a test instance
+        server = self.create_server(wait_until='ACTIVE')
+        self.attach_volume(server['id'], self.volume_origin['id'])
+
+        # Snapshot a volume attached to an instance
+        snapshot1 = self.create_snapshot(self.volume_origin['id'], force=True)
+        snapshot2 = self.create_snapshot(self.volume_origin['id'], force=True)
+        snapshot3 = self.create_snapshot(self.volume_origin['id'], force=True)
+
+        # Delete the snapshots. Some snapshot implementations can take
+        # different paths according to order they are deleted.
+        self.cleanup_snapshot(snapshot1)
+        self.cleanup_snapshot(snapshot3)
+        self.cleanup_snapshot(snapshot2)
+
+    @test.idempotent_id('5210a1de-85a0-11e6-bb21-641c676a5d61')
+    @test.services('compute')
+    def test_snapshot_create_offline_delete_online(self):
+
+        # Create a snapshot while it is not attached
+        snapshot1 = self.create_snapshot(self.volume_origin['id'])
+
+        # Create a server and attach it
+        server = self.create_server(wait_until='ACTIVE')
+        self.attach_volume(server['id'], self.volume_origin['id'])
+
+        # Now that the volume is attached, create another snapshots
+        snapshot2 = self.create_snapshot(self.volume_origin['id'], force=True)
+        snapshot3 = self.create_snapshot(self.volume_origin['id'], force=True)
+
+        # Delete the snapshots. Some snapshot implementations can take
+        # different paths according to order they are deleted.
+        self.cleanup_snapshot(snapshot3)
+        self.cleanup_snapshot(snapshot1)
+        self.cleanup_snapshot(snapshot2)
+
     @test.idempotent_id('2a8abbe4-d871-46db-b049-c41f5af8216e')
     def test_snapshot_create_get_list_update_delete(self):
         # Create a snapshot
diff --git a/tempest/api/volume/v2/test_volumes_list.py b/tempest/api/volume/v2/test_volumes_list.py
index 03996af..fb8c65d 100644
--- a/tempest/api/volume/v2/test_volumes_list.py
+++ b/tempest/api/volume/v2/test_volumes_list.py
@@ -33,11 +33,6 @@
     """
 
     @classmethod
-    def setup_clients(cls):
-        super(VolumesV2ListTestJSON, cls).setup_clients()
-        cls.client = cls.volumes_client
-
-    @classmethod
     def resource_setup(cls):
         super(VolumesV2ListTestJSON, cls).resource_setup()
 
@@ -45,7 +40,7 @@
         cls.metadata = {'Type': 'work'}
         # NOTE(zhufl): When using pre-provisioned credentials, the project
         # may have volumes other than those created below.
-        existing_volumes = cls.client.list_volumes()['volumes']
+        existing_volumes = cls.volumes_client.list_volumes()['volumes']
         cls.volume_id_list = [vol['id'] for vol in existing_volumes]
         for i in range(3):
             volume = cls.create_volume(metadata=cls.metadata)
@@ -63,7 +58,7 @@
                       'sort_dir': sort_dir,
                       'sort_key': sort_key
                       }
-            fetched_volume = self.client.list_volumes(
+            fetched_volume = self.volumes_client.list_volumes(
                 detail=True, params=params)['volumes']
             self.assertEqual(limit, len(fetched_volume),
                              "The count of volumes is %s, expected:%s " %
@@ -192,8 +187,8 @@
         params = {'marker': random_volume}
 
         # Running volume list using marker parameter
-        vol_with_marker = self.client.list_volumes(detail=True,
-                                                   params=params)['volumes']
+        vol_with_marker = self.volumes_client.list_volumes(
+            detail=True, params=params)['volumes']
 
         # Fetching the index of the random volume from volume_id_list
         index_marker = self.volume_id_list.index(random_volume)
diff --git a/tempest/api/volume/v3/base.py b/tempest/api/volume/v3/base.py
index e38f947..31fc1eb 100644
--- a/tempest/api/volume/v3/base.py
+++ b/tempest/api/volume/v3/base.py
@@ -52,7 +52,8 @@
             self.request_microversion))
 
 
-class VolumesV3AdminTest(VolumesV3Test):
+class VolumesV3AdminTest(VolumesV3Test,
+                         base.BaseVolumeAdminTest):
     """Base test case class for all v3 Volume Admin API tests."""
 
     credentials = ['primary', 'admin']
diff --git a/tempest/clients.py b/tempest/clients.py
index d131dc4..b3290ac 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -17,7 +17,6 @@
 
 from oslo_log import log as logging
 
-from tempest.common import negative_rest_client
 from tempest import config
 from tempest.lib import auth
 from tempest.lib import exceptions as lib_exc
@@ -26,7 +25,6 @@
 from tempest.services import identity
 from tempest.services import object_storage
 from tempest.services import orchestration
-from tempest.services import volume
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
@@ -82,8 +80,6 @@
             build_interval=CONF.orchestration.build_interval,
             build_timeout=CONF.orchestration.build_timeout,
             **self.default_params)
-        self.negative_client = negative_rest_client.NegativeRestClient(
-            self.auth_provider, service, **self.default_params)
 
     def _prepare_configuration(self):
         """Map values from CONF into Manager parameters
@@ -141,6 +137,8 @@
             self.namespaces_client = self.image_v2.NamespacesClient()
             self.resource_types_client = self.image_v2.ResourceTypesClient()
             self.schemas_client = self.image_v2.SchemasClient()
+            self.namespace_properties_client = \
+                self.image_v2.NamespacePropertiesClient()
 
     def _set_compute_clients(self):
         self.agents_client = self.compute.AgentsClient()
@@ -276,8 +274,6 @@
                 raise lib_exc.InvalidConfiguration(msg)
 
     def _set_volume_clients(self):
-        # Mandatory parameters (always defined)
-        params = self.parameters['volume']
 
         self.volume_qos_client = self.volume_v1.QosSpecsClient()
         self.volume_qos_v2_client = self.volume_v2.QosSpecsClient()
@@ -292,8 +288,7 @@
         self.snapshots_v2_client = self.volume_v2.SnapshotsClient()
         self.volumes_client = self.volume_v1.VolumesClient()
         self.volumes_v2_client = self.volume_v2.VolumesClient()
-        self.volume_v3_messages_client = volume.v3.MessagesClient(
-            self.auth_provider, **params)
+        self.volume_v3_messages_client = self.volume_v3.MessagesClient()
         self.volume_types_client = self.volume_v1.TypesClient()
         self.volume_types_v2_client = self.volume_v2.TypesClient()
         self.volume_hosts_client = self.volume_v1.HostsClient()
@@ -306,6 +301,8 @@
             self.volume_v1.AvailabilityZoneClient()
         self.volume_v2_availability_zone_client = \
             self.volume_v2.AvailabilityZoneClient()
+        self.volume_limits_client = self.volume_v1.LimitsClient()
+        self.volume_v2_limits_client = self.volume_v2.LimitsClient()
 
     def _set_object_storage_clients(self):
         # Mandatory parameters (always defined)
diff --git a/tempest/cmd/main.py b/tempest/cmd/main.py
index 641d11c..1090c41 100644
--- a/tempest/cmd/main.py
+++ b/tempest/cmd/main.py
@@ -11,11 +11,11 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-import logging
 import sys
 
 from cliff import app
 from cliff import commandmanager
+from oslo_log import log as logging
 from pbr import version
 
 
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 318eb10..64543fb 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -169,27 +169,28 @@
     return body, servers
 
 
-def shelve_server(client, server_id, force_shelve_offload=False):
+def shelve_server(servers_client, server_id, force_shelve_offload=False):
     """Common wrapper utility to shelve server.
 
     This method is a common wrapper to make server in 'SHELVED'
     or 'SHELVED_OFFLOADED' state.
 
+    :param servers_clients: Compute servers client instance.
     :param server_id: Server to make in shelve state
     :param force_shelve_offload: Forcefully offload shelve server if it
                                  is configured not to offload server
                                  automatically after offload time.
     """
-    client.shelve_server(server_id)
+    servers_client.shelve_server(server_id)
 
     offload_time = CONF.compute.shelved_offload_time
     if offload_time >= 0:
-        waiters.wait_for_server_status(client, server_id,
+        waiters.wait_for_server_status(servers_client, server_id,
                                        'SHELVED_OFFLOADED',
                                        extra_timeout=offload_time)
     else:
-        waiters.wait_for_server_status(client, server_id, 'SHELVED')
+        waiters.wait_for_server_status(servers_client, server_id, 'SHELVED')
         if force_shelve_offload:
-            client.shelve_offload_server(server_id)
-            waiters.wait_for_server_status(client, server_id,
+            servers_client.shelve_offload_server(server_id)
+            waiters.wait_for_server_status(servers_client, server_id,
                                            'SHELVED_OFFLOADED')
diff --git a/tempest/common/negative_rest_client.py b/tempest/common/negative_rest_client.py
deleted file mode 100644
index 3495a24..0000000
--- a/tempest/common/negative_rest_client.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# (c) 2014 Deutsche Telekom AG
-# Copyright 2014 Red Hat, Inc.
-# Copyright 2014 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 import config
-from tempest.lib.common import rest_client
-
-CONF = config.CONF
-
-
-class NegativeRestClient(rest_client.RestClient):
-    """Version of RestClient that does not raise exceptions."""
-    def __init__(self, auth_provider, service, **kwargs):
-        region, endpoint_type = self._get_region_and_endpoint_type(service)
-        super(NegativeRestClient, self).__init__(
-            auth_provider, service, region, endpoint_type=endpoint_type,
-            **kwargs)
-
-    def _get_region_and_endpoint_type(self, service):
-        """Returns the region for a specific service"""
-        service_region = None
-        service_endpoint_type = None
-        for cfgname in dir(CONF._config):
-            # Find all config.FOO.catalog_type and assume FOO is a service.
-            cfg = getattr(CONF, cfgname)
-            catalog_type = getattr(cfg, 'catalog_type', None)
-            if catalog_type == service:
-                service_region = getattr(cfg, 'region', None)
-                service_endpoint_type = getattr(cfg, 'endpoint_type', None)
-        if not service_region:
-            service_region = CONF.identity.region
-        return service_region, service_endpoint_type
-
-    def _error_checker(self, method, url,
-                       headers, body, resp, resp_body):
-        pass
-
-    def send_request(self, method, url_template, resources, body=None):
-        url = url_template % tuple(resources)
-        if method == "GET":
-            resp, body = self.get(url)
-        elif method == "POST":
-            resp, body = self.post(url, body)
-        elif method == "PUT":
-            resp, body = self.put(url, body)
-        elif method == "PATCH":
-            resp, body = self.patch(url, body)
-        elif method == "HEAD":
-            resp, body = self.head(url)
-        elif method == "DELETE":
-            resp, body = self.delete(url)
-        elif method == "COPY":
-            resp, body = self.copy(url)
-        else:
-            assert False
-
-        return resp, body
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 92bce5f..981a922 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -176,7 +176,7 @@
         time.sleep(client.build_interval)
         body = client.show_volume(volume_id)['volume']
         volume_status = body['status']
-        if volume_status == 'error':
+        if volume_status == 'error' and status != 'error':
             raise exceptions.VolumeBuildErrorException(volume_id=volume_id)
         if volume_status == 'error_restoring':
             raise exceptions.VolumeRestoreErrorException(volume_id=volume_id)
@@ -290,3 +290,28 @@
         if int(time.time()) - start_time >= client.build_timeout:
             raise lib_exc.TimeoutException
         time.sleep(client.build_interval)
+
+
+def wait_for_interface_status(client, server, port_id, status):
+    """Waits for an interface to reach a given status."""
+    body = (client.show_interface(server, port_id)
+            ['interfaceAttachment'])
+    interface_status = body['port_state']
+    start = int(time.time())
+
+    while(interface_status != status):
+        time.sleep(client.build_interval)
+        body = (client.show_interface(server, port_id)
+                ['interfaceAttachment'])
+        interface_status = body['port_state']
+
+        timed_out = int(time.time()) - start >= client.build_timeout
+
+        if interface_status != status and timed_out:
+            message = ('Interface %s failed to reach %s status '
+                       '(current %s) within the required time (%s s).' %
+                       (port_id, status, interface_status,
+                        client.build_timeout))
+            raise lib_exc.TimeoutException(message)
+
+    return body
diff --git a/tempest/config.py b/tempest/config.py
index bc9215c..7550287 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -278,6 +278,11 @@
                      'be utilized by some multinode specific tests to ensure '
                      'that requests match the expected size of the cluster '
                      'you are testing with.')),
+    cfg.StrOpt('hypervisor_type',
+               default=None,
+               help="Hypervisor type of the test target on heterogeneous "
+                    "compute environment. The value can be 'QEMU', 'xen' or "
+                    "something."),
     cfg.StrOpt('min_microversion',
                default=None,
                help="Lower version of the test target microversion range. "
@@ -397,7 +402,10 @@
     cfg.BoolOpt('nova_cert',
                 default=False,
                 help='Does the test environment have the nova cert running?',
-                deprecated_for_removal=True),
+                deprecated_for_removal=True,
+                deprecated_reason="On Nova side, the nova-cert service is "
+                                  "deprecated and the service will be removed "
+                                  "as early as Ocata."),
     cfg.BoolOpt('personality',
                 default=False,
                 help='Does the test environment support server personality'),
@@ -780,11 +788,6 @@
     cfg.BoolOpt('api_v3',
                 default=False,
                 help="Is the v3 volume API enabled"),
-    cfg.BoolOpt('bootable',
-                default=True,
-                help='Update bootable status of a volume '
-                     'Not implemented on icehouse ',
-                deprecated_for_removal=True),
     # TODO(ynesenenko): Remove volume_services once liberty-eol happens.
     cfg.BoolOpt('volume_services',
                 default=False,
diff --git a/tempest/lib/cli/base.py b/tempest/lib/cli/base.py
index a43d002..72a15b5 100644
--- a/tempest/lib/cli/base.py
+++ b/tempest/lib/cli/base.py
@@ -29,7 +29,7 @@
 
 
 def execute(cmd, action, flags='', params='', fail_ok=False,
-            merge_stderr=False, cli_dir='/usr/bin'):
+            merge_stderr=False, cli_dir='/usr/bin', prefix=''):
     """Executes specified command for the given action.
 
     :param cmd: command to be executed
@@ -48,9 +48,12 @@
     :type merge_stderr: boolean
     :param cli_dir: The path where the cmd can be executed
     :type cli_dir: string
+    :param prefix: prefix to insert before command
+    :type prefix: string
     """
-    cmd = ' '.join([os.path.join(cli_dir, cmd),
+    cmd = ' '.join([prefix, os.path.join(cli_dir, cmd),
                     flags, action, params])
+    cmd = cmd.strip()
     LOG.info("running: '%s'" % cmd)
     if six.PY2:
         cmd = cmd.encode('utf-8')
@@ -88,10 +91,12 @@
     :type cli_dir: string
     :param insecure: if True, --insecure is passed to python client binaries.
     :type insecure: boolean
+    :param prefix: prefix to insert before commands
+    :type prefix: string
     """
 
     def __init__(self, username='', password='', tenant_name='', uri='',
-                 cli_dir='', insecure=False, *args, **kwargs):
+                 cli_dir='', insecure=False, prefix='', *args, **kwargs):
         """Initialize a new CLIClient object."""
         super(CLIClient, self).__init__()
         self.cli_dir = cli_dir if cli_dir else '/usr/bin'
@@ -100,6 +105,7 @@
         self.password = password
         self.uri = uri
         self.insecure = insecure
+        self.prefix = prefix
 
     def nova(self, action, flags='', params='', fail_ok=False,
              endpoint_type='publicURL', merge_stderr=False):
@@ -365,7 +371,7 @@
         else:
             flags = creds + ' ' + flags
         return execute(cmd, action, flags, params, fail_ok, merge_stderr,
-                       self.cli_dir)
+                       self.cli_dir, prefix=self.prefix)
 
 
 class ClientTestBase(base.BaseTestCase):
diff --git a/tempest/lib/services/clients.py b/tempest/lib/services/clients.py
index 2e5c457..0e8e3c6 100644
--- a/tempest/lib/services/clients.py
+++ b/tempest/lib/services/clients.py
@@ -23,6 +23,7 @@
 from tempest.lib.common.utils import misc
 from tempest.lib import exceptions
 from tempest.lib.services import compute
+from tempest.lib.services import identity
 from tempest.lib.services import image
 from tempest.lib.services import network
 from tempest.lib.services import volume
@@ -39,11 +40,13 @@
     """
     return {
         'compute': compute,
+        'identity.v2': identity.v2,
         'image.v1': image.v1,
         'image.v2': image.v2,
         'network': network,
         'volume.v1': volume.v1,
-        'volume.v2': volume.v2
+        'volume.v2': volume.v2,
+        'volume.v3': volume.v3
     }
 
 
@@ -52,7 +55,7 @@
     # NOTE(andreaf) This list will exists only as long the remain clients
     # are migrated to tempest.lib, and it will then be deleted without
     # deprecation or advance notice
-    return set(['identity.v2', 'identity.v3', 'object-storage', 'volume.v3'])
+    return set(['identity.v3', 'object-storage'])
 
 
 def available_modules():
diff --git a/tempest/lib/services/identity/v3/role_assignments_client.py b/tempest/lib/services/identity/v3/role_assignments_client.py
new file mode 100644
index 0000000..10de03f
--- /dev/null
+++ b/tempest/lib/services/identity/v3/role_assignments_client.py
@@ -0,0 +1,48 @@
+# Copyright 2016 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 RoleAssignmentsClient(rest_client.RestClient):
+    api_version = "v3"
+
+    def list_role_assignments(self, effective=False, **kwargs):
+        """List role assignments.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/?expanded=list-effective-role-assignments-detail
+
+        :param effective: If True, returns the effective assignments, including
+                          any assignments gained by virtue of group membership
+                          or inherited roles.
+        """
+        url = 'role_assignments'
+        if kwargs:
+            # NOTE(rodrigods): "effective" is a key-only query parameter and
+            # is treated below.
+            if 'effective' in kwargs:
+                del kwargs['effective']
+            url += '?%s' % urllib.urlencode(kwargs)
+        if effective:
+            url += '&effective'
+
+        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/image/v2/__init__.py b/tempest/lib/services/image/v2/__init__.py
index 32bad8b..d359d4b 100644
--- a/tempest/lib/services/image/v2/__init__.py
+++ b/tempest/lib/services/image/v2/__init__.py
@@ -15,10 +15,12 @@
 from tempest.lib.services.image.v2.image_members_client import \
     ImageMembersClient
 from tempest.lib.services.image.v2.images_client import ImagesClient
+from tempest.lib.services.image.v2.namespace_properties_client import \
+    NamespacePropertiesClient
 from tempest.lib.services.image.v2.namespaces_client import NamespacesClient
 from tempest.lib.services.image.v2.resource_types_client import \
     ResourceTypesClient
 from tempest.lib.services.image.v2.schemas_client import SchemasClient
 
-__all__ = ['ImageMembersClient', 'ImagesClient', 'NamespacesClient',
-           'ResourceTypesClient', 'SchemasClient']
+__all__ = ['ImageMembersClient', 'ImagesClient', 'NamespacePropertiesClient',
+           'NamespacesClient', 'ResourceTypesClient', 'SchemasClient']
diff --git a/tempest/lib/services/image/v2/namespace_properties_client.py b/tempest/lib/services/image/v2/namespace_properties_client.py
new file mode 100644
index 0000000..1236b2b
--- /dev/null
+++ b/tempest/lib/services/image/v2/namespace_properties_client.py
@@ -0,0 +1,91 @@
+# Copyright 2016 EasyStack.
+# 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 tempest.lib.common import rest_client
+
+
+class NamespacePropertiesClient(rest_client.RestClient):
+    api_version = "v2"
+
+    def list_namespace_properties(self, namespace):
+        """Lists property definitions in a namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#list-properties
+        """
+        url = 'metadefs/namespaces/%s/properties' % namespace
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def create_namespace_property(self, namespace, **kwargs):
+        """Creates a property definition in a namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#create-property
+        """
+        url = 'metadefs/namespaces/%s/properties' % namespace
+        data = json.dumps(kwargs)
+        resp, body = self.post(url, data)
+        self.expected_success(201, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def show_namespace_properties(self, namespace, property_name):
+        """Shows the definition for a property.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#show-property-definition
+        """
+        url = 'metadefs/namespaces/%s/properties/%s' % (namespace,
+                                                        property_name)
+        resp, body = self.get(url)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_namespace_properties(self, namespace, property_name, **kwargs):
+        """Updates a property definition.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#update-property-definition
+        """
+        url = 'metadefs/namespaces/%s/properties/%s' % (namespace,
+                                                        property_name)
+        data = json.dumps(kwargs)
+        resp, body = self.put(url, data)
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def delete_namespace_property(self, namespace, property_name):
+        """Removes a property definition from a namespace.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/image/v2/metadefs-index.html#remove-property-definition
+        """
+        url = 'metadefs/namespaces/%s/properties/%s' % (namespace,
+                                                        property_name)
+        resp, _ = self.delete(url)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp)
diff --git a/tempest/lib/services/volume/__init__.py b/tempest/lib/services/volume/__init__.py
index 11da06c..6855d8e 100644
--- a/tempest/lib/services/volume/__init__.py
+++ b/tempest/lib/services/volume/__init__.py
@@ -14,5 +14,6 @@
 
 from tempest.lib.services.volume import v1
 from tempest.lib.services.volume import v2
+from tempest.lib.services.volume import v3
 
-__all__ = ['v1', 'v2']
+__all__ = ['v1', 'v2', 'v3']
diff --git a/tempest/lib/services/volume/v1/__init__.py b/tempest/lib/services/volume/v1/__init__.py
index 9c98542..7b5991f 100644
--- a/tempest/lib/services/volume/v1/__init__.py
+++ b/tempest/lib/services/volume/v1/__init__.py
@@ -19,6 +19,7 @@
     EncryptionTypesClient
 from tempest.lib.services.volume.v1.extensions_client import ExtensionsClient
 from tempest.lib.services.volume.v1.hosts_client import HostsClient
+from tempest.lib.services.volume.v1.limits_client import LimitsClient
 from tempest.lib.services.volume.v1.qos_client import QosSpecsClient
 from tempest.lib.services.volume.v1.quotas_client import QuotasClient
 from tempest.lib.services.volume.v1.services_client import ServicesClient
@@ -28,4 +29,5 @@
 
 __all__ = ['AvailabilityZoneClient', 'BackupsClient', 'EncryptionTypesClient',
            'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
-           'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient']
+           'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
+           'LimitsClient']
diff --git a/tempest/lib/services/volume/v1/backups_client.py b/tempest/lib/services/volume/v1/backups_client.py
index 2728c67..8677913 100644
--- a/tempest/lib/services/volume/v1/backups_client.py
+++ b/tempest/lib/services/volume/v1/backups_client.py
@@ -26,8 +26,9 @@
     def create_backup(self, **kwargs):
         """Creates a backup of volume.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#createBackup
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-backup
         """
         post_body = json.dumps({'backup': kwargs})
         resp, body = self.post('backups', post_body)
@@ -38,8 +39,9 @@
     def restore_backup(self, backup_id, **kwargs):
         """Restore volume from backup.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#restoreBackup
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#restore-backup
         """
         post_body = json.dumps({'restore': kwargs})
         resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
diff --git a/tempest/lib/services/volume/v1/limits_client.py b/tempest/lib/services/volume/v1/limits_client.py
new file mode 100644
index 0000000..e14b2dc
--- /dev/null
+++ b/tempest/lib/services/volume/v1/limits_client.py
@@ -0,0 +1,32 @@
+# Copyright 2016 Red Hat, Inc.
+# 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 tempest.lib.common import rest_client
+
+
+class LimitsClient(rest_client.RestClient):
+    """Volume V1 limits client."""
+
+    api_version = "v1"
+
+    def show_limits(self):
+        """Returns the details of a volume absolute limits."""
+        url = "limits"
+        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/lib/services/volume/v1/qos_client.py b/tempest/lib/services/volume/v1/qos_client.py
index 65ae274..e247b7b 100644
--- a/tempest/lib/services/volume/v1/qos_client.py
+++ b/tempest/lib/services/volume/v1/qos_client.py
@@ -41,8 +41,9 @@
     def create_qos(self, **kwargs):
         """Create a QoS Specification.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#createQoSSpec
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-qos-specification
         """
         post_body = json.dumps({'qos_specs': kwargs})
         resp, body = self.post('qos-specs', post_body)
@@ -76,8 +77,9 @@
     def set_qos_key(self, qos_id, **kwargs):
         """Set the specified keys/values of QoS specification.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#setQoSKey
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#set-keys-in-qos-specification
         """
         put_body = json.dumps({"qos_specs": kwargs})
         resp, body = self.put('qos-specs/%s' % qos_id, put_body)
diff --git a/tempest/lib/services/volume/v1/snapshots_client.py b/tempest/lib/services/volume/v1/snapshots_client.py
index 1881078..3433e68 100644
--- a/tempest/lib/services/volume/v1/snapshots_client.py
+++ b/tempest/lib/services/volume/v1/snapshots_client.py
@@ -25,8 +25,9 @@
     def list_snapshots(self, detail=False, **params):
         """List all the snapshot.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v1.html#listSnapshots
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#list-snapshots-with-details-v1
         """
         url = 'snapshots'
         if detail:
@@ -42,8 +43,9 @@
     def show_snapshot(self, snapshot_id):
         """Returns the details of a single snapshot.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v1.html#showSnapshot
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#show-snapshot-details-v1
         """
         url = "snapshots/%s" % snapshot_id
         resp, body = self.get(url)
@@ -54,8 +56,9 @@
     def create_snapshot(self, **kwargs):
         """Creates a new snapshot.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v1.html#createSnapshot
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#create-snapshot-v1
         """
         post_body = json.dumps({'snapshot': kwargs})
         resp, body = self.post('snapshots', post_body)
@@ -66,8 +69,9 @@
     def delete_snapshot(self, snapshot_id):
         """Delete Snapshot.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v1.html#deleteSnapshot
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#delete-snapshot-v1
         """
         resp, body = self.delete("snapshots/%s" % snapshot_id)
         self.expected_success(202, resp.status)
@@ -117,9 +121,9 @@
     def update_snapshot(self, snapshot_id, **kwargs):
         """Updates a snapshot.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v1.html#
-                              updateSnapshotMetadata
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#update-snapshot-v1
         """
         put_body = json.dumps({'snapshot': kwargs})
         resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
@@ -130,9 +134,9 @@
     def show_snapshot_metadata(self, snapshot_id):
         """Get metadata of the snapshot.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v1.html#
-                              showSnapshotMetadata
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#show-snapshot-metadata-v1
         """
         url = "snapshots/%s/metadata" % snapshot_id
         resp, body = self.get(url)
@@ -143,9 +147,9 @@
     def update_snapshot_metadata(self, snapshot_id, **kwargs):
         """Update metadata for the snapshot.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v1.html#
-                              updateSnapshotMetadata
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#update-snapshot-metadata-v1
         """
         put_body = json.dumps(kwargs)
         url = "snapshots/%s/metadata" % snapshot_id
diff --git a/tempest/lib/services/volume/v1/types_client.py b/tempest/lib/services/volume/v1/types_client.py
index dce728d..4ae9935 100644
--- a/tempest/lib/services/volume/v1/types_client.py
+++ b/tempest/lib/services/volume/v1/types_client.py
@@ -38,8 +38,9 @@
     def list_volume_types(self, **params):
         """List all the volume_types created.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v1.html#listVolumeTypes
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#list-volume-types-v1
         """
         url = 'types'
         if params:
@@ -53,8 +54,9 @@
     def show_volume_type(self, volume_type_id):
         """Returns the details of a single volume_type.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v1.html#showVolumeType
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#show-volume-type-v1
         """
         url = "types/%s" % volume_type_id
         resp, body = self.get(url)
@@ -65,8 +67,9 @@
     def create_volume_type(self, **kwargs):
         """Create volume type.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v1.html#createVolumeType
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#create-volume-type-v1
         """
         post_body = json.dumps({'volume_type': kwargs})
         resp, body = self.post('types', post_body)
@@ -77,8 +80,9 @@
     def delete_volume_type(self, volume_type_id):
         """Deletes the Specified Volume_type.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v1.html#deleteVolumeType
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#delete-volume-type-v1
         """
         resp, body = self.delete("types/%s" % volume_type_id)
         self.expected_success(202, resp.status)
@@ -131,8 +135,9 @@
     def update_volume_type(self, volume_type_id, **kwargs):
         """Updates volume type name, description, and/or is_public.
 
-        Available params: see http://developer.openstack.org/
-        api-ref-blockstorage-v2.html#updateVolumeType
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#update-volume-type-v1
         """
         put_body = json.dumps({'volume_type': kwargs})
         resp, body = self.put('types/%s' % volume_type_id, put_body)
@@ -148,9 +153,9 @@
         extra_spec_name: Name of the extra spec to be updated.
         extra_spec: A dictionary of with key as extra_spec_name and the
                      updated value.
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#
-                              updateVolumeTypeExtraSpecs
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#update-extra-specs-for-a-volume-type-v1
         """
         url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
         put_body = json.dumps(extra_specs)
diff --git a/tempest/lib/services/volume/v1/volumes_client.py b/tempest/lib/services/volume/v1/volumes_client.py
index cc98c91..7a25697 100644
--- a/tempest/lib/services/volume/v1/volumes_client.py
+++ b/tempest/lib/services/volume/v1/volumes_client.py
@@ -61,8 +61,9 @@
     def create_volume(self, **kwargs):
         """Creates a new Volume.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#createVolume
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#create-volume
         """
         post_body = json.dumps({'volume': kwargs})
         resp, body = self.post('volumes', post_body)
@@ -73,8 +74,9 @@
     def update_volume(self, volume_id, **kwargs):
         """Updates the Specified Volume.
 
-        Available params: see http://developer.openstack.org/
-                                api-ref-blockstorage-v2.html#updateVolume
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#update-volume
         """
         put_body = json.dumps({'volume': kwargs})
         resp, body = self.put('volumes/%s' % volume_id, put_body)
@@ -100,8 +102,9 @@
     def attach_volume(self, volume_id, **kwargs):
         """Attaches a volume to a given instance on a given mountpoint.
 
-        Available params: see http://developer.openstack.org/
-                                api-ref-blockstorage-v2.html#attachVolume
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#attach-volume
         """
         post_body = json.dumps({'os-attach': kwargs})
         url = 'volumes/%s/action' % (volume_id)
@@ -156,8 +159,9 @@
     def extend_volume(self, volume_id, **kwargs):
         """Extend a volume.
 
-        Available params: see http://developer.openstack.org/
-                                api-ref-blockstorage-v2.html#extendVolume
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#extend-volume
         """
         post_body = json.dumps({'os-extend': kwargs})
         url = 'volumes/%s/action' % (volume_id)
@@ -168,8 +172,9 @@
     def reset_volume_status(self, volume_id, **kwargs):
         """Reset the Specified Volume's Status.
 
-        Available params: see http://developer.openstack.org/
-                                api-ref-blockstorage-v2.html#resetVolume
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#reset-volume-status
         """
         post_body = json.dumps({'os-reset_status': kwargs})
         resp, body = self.post('volumes/%s/action' % volume_id, post_body)
@@ -179,8 +184,9 @@
     def create_volume_transfer(self, **kwargs):
         """Create a volume transfer.
 
-        Available params: see http://developer.openstack.org/
-                                api-ref-blockstorage-v2.html#createVolumeTransfer
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#create-volume-transfer
         """
         post_body = json.dumps({'transfer': kwargs})
         resp, body = self.post('os-volume-transfer', post_body)
@@ -199,8 +205,9 @@
     def list_volume_transfers(self, **params):
         """List all the volume transfers created.
 
-        Available params: see http://developer.openstack.org/
-                                api-ref-blockstorage-v2.html#listVolumeTransfer
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#list-volume-transfers
         """
         url = 'os-volume-transfer'
         if params:
@@ -219,8 +226,9 @@
     def accept_volume_transfer(self, transfer_id, **kwargs):
         """Accept a volume transfer.
 
-        Available params: see http://developer.openstack.org/
-                                api-ref-blockstorage-v2.html#acceptVolumeTransfer
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v1/#accept-volume-transfer
         """
         url = 'os-volume-transfer/%s/accept' % transfer_id
         post_body = json.dumps({'accept': kwargs})
diff --git a/tempest/lib/services/volume/v2/__init__.py b/tempest/lib/services/volume/v2/__init__.py
index f547d7d..eaaafa5 100644
--- a/tempest/lib/services/volume/v2/__init__.py
+++ b/tempest/lib/services/volume/v2/__init__.py
@@ -19,6 +19,7 @@
     EncryptionTypesClient
 from tempest.lib.services.volume.v2.extensions_client import ExtensionsClient
 from tempest.lib.services.volume.v2.hosts_client import HostsClient
+from tempest.lib.services.volume.v2.limits_client import LimitsClient
 from tempest.lib.services.volume.v2.qos_client import QosSpecsClient
 from tempest.lib.services.volume.v2.quotas_client import QuotasClient
 from tempest.lib.services.volume.v2.services_client import ServicesClient
@@ -28,4 +29,5 @@
 
 __all__ = ['AvailabilityZoneClient', 'BackupsClient', 'EncryptionTypesClient',
            'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
-           'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient']
+           'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
+           'LimitsClient']
diff --git a/tempest/lib/services/volume/v2/backups_client.py b/tempest/lib/services/volume/v2/backups_client.py
index 61f865d..ab5eefd 100644
--- a/tempest/lib/services/volume/v2/backups_client.py
+++ b/tempest/lib/services/volume/v2/backups_client.py
@@ -26,8 +26,9 @@
     def create_backup(self, **kwargs):
         """Creates a backup of volume.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#createBackup
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-blockstorage-v2.html#createBackup
         """
         post_body = json.dumps({'backup': kwargs})
         resp, body = self.post('backups', post_body)
@@ -38,8 +39,9 @@
     def restore_backup(self, backup_id, **kwargs):
         """Restore volume from backup.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#restoreBackup
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref-blockstorage-v2.html#restoreBackup
         """
         post_body = json.dumps({'restore': kwargs})
         resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
diff --git a/tempest/lib/services/volume/v2/limits_client.py b/tempest/lib/services/volume/v2/limits_client.py
new file mode 100644
index 0000000..ce9fba9
--- /dev/null
+++ b/tempest/lib/services/volume/v2/limits_client.py
@@ -0,0 +1,32 @@
+# Copyright 2016 Red Hat, Inc.
+# 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 tempest.lib.common import rest_client
+
+
+class LimitsClient(rest_client.RestClient):
+    """Volume V2 limits client."""
+
+    api_version = "v2"
+
+    def show_limits(self):
+        """Returns the details of a volume absolute limits."""
+        url = "limits"
+        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/lib/services/volume/v2/snapshots_client.py b/tempest/lib/services/volume/v2/snapshots_client.py
index c84e557..6f51b51 100644
--- a/tempest/lib/services/volume/v2/snapshots_client.py
+++ b/tempest/lib/services/volume/v2/snapshots_client.py
@@ -25,8 +25,9 @@
     def list_snapshots(self, detail=False, **params):
         """List all the snapshot.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#listSnapshots
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-snapshots-v2
         """
         url = 'snapshots'
         if detail:
@@ -42,8 +43,9 @@
     def show_snapshot(self, snapshot_id):
         """Returns the details of a single snapshot.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#showSnapshot
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-v2
         """
         url = "snapshots/%s" % snapshot_id
         resp, body = self.get(url)
@@ -54,8 +56,9 @@
     def create_snapshot(self, **kwargs):
         """Creates a new snapshot.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#createSnapshot
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-snapshot
         """
         post_body = json.dumps({'snapshot': kwargs})
         resp, body = self.post('snapshots', post_body)
@@ -66,8 +69,9 @@
     def update_snapshot(self, snapshot_id, **kwargs):
         """Updates a snapshot.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#updateSnapshot
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-v2
         """
         put_body = json.dumps({'snapshot': kwargs})
         resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
@@ -78,8 +82,9 @@
     def delete_snapshot(self, snapshot_id):
         """Delete Snapshot.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#deleteSnapshot
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#delete-snapshot-v2
         """
         resp, body = self.delete("snapshots/%s" % snapshot_id)
         self.expected_success(202, resp.status)
@@ -129,9 +134,9 @@
     def show_snapshot_metadata(self, snapshot_id):
         """Get metadata of the snapshot.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#
-                              showSnapshotMetadata
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#show-snapshot-metadata-v2
         """
         url = "snapshots/%s/metadata" % snapshot_id
         resp, body = self.get(url)
@@ -142,9 +147,9 @@
     def update_snapshot_metadata(self, snapshot_id, **kwargs):
         """Update metadata for the snapshot.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#
-                              updateSnapshotMetadata
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-snapshot-metadata-v2
         """
         put_body = json.dumps(kwargs)
         url = "snapshots/%s/metadata" % snapshot_id
diff --git a/tempest/lib/services/volume/v2/types_client.py b/tempest/lib/services/volume/v2/types_client.py
index d399e99..31597d7 100644
--- a/tempest/lib/services/volume/v2/types_client.py
+++ b/tempest/lib/services/volume/v2/types_client.py
@@ -39,8 +39,9 @@
     def list_volume_types(self, **params):
         """List all the volume_types created.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#showVolumeTypes
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-types-v2
         """
         url = 'types'
         if params:
@@ -54,8 +55,9 @@
     def show_volume_type(self, volume_type_id):
         """Returns the details of a single volume_type.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#showVolumeType
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#show-volume-type-v2
         """
         url = "types/%s" % volume_type_id
         resp, body = self.get(url)
@@ -66,8 +68,9 @@
     def create_volume_type(self, **kwargs):
         """Create volume type.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#createVolumeType
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-type-v2
         """
         post_body = json.dumps({'volume_type': kwargs})
         resp, body = self.post('types', post_body)
@@ -78,8 +81,9 @@
     def delete_volume_type(self, volume_type_id):
         """Deletes the Specified Volume_type.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#deleteVolumeType
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#delete-volume-type-v2
         """
         resp, body = self.delete("types/%s" % volume_type_id)
         self.expected_success(202, resp.status)
@@ -132,8 +136,9 @@
     def update_volume_type(self, volume_type_id, **kwargs):
         """Updates volume type name, description, and/or is_public.
 
-        Available params: see http://developer.openstack.org/
-        api-ref-blockstorage-v2.html#updateVolumeType
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type-v2
         """
         put_body = json.dumps({'volume_type': kwargs})
         resp, body = self.put('types/%s' % volume_type_id, put_body)
@@ -149,9 +154,9 @@
         extra_spec_name: Name of the extra spec to be updated.
         extra_spec: A dictionary of with key as extra_spec_name and the
                      updated value.
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#
-                              updateVolumeTypeExtraSpecs
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-type-extra-specs-v2
         """
         url = "types/%s/extra_specs/%s" % (volume_type_id, extra_spec_name)
         put_body = json.dumps(extra_specs)
@@ -163,9 +168,9 @@
     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
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#add-type-access-v2
         """
         post_body = json.dumps({'addProjectAccess': kwargs})
         url = 'types/%s/action' % volume_type_id
@@ -176,9 +181,9 @@
     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
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#remove-type-access-v2
         """
         post_body = json.dumps({'removeProjectAccess': kwargs})
         url = 'types/%s/action' % volume_type_id
@@ -189,9 +194,9 @@
     def list_type_access(self, volume_type_id):
         """Print access information about the given volume type.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#
-                              listVolumeTypeAccessExt
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-type-access-v2
         """
         url = 'types/%s/os-volume-type-access' % volume_type_id
         resp, body = self.get(url)
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index b1930e1..ce97adb 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -62,8 +62,9 @@
     def create_volume(self, **kwargs):
         """Creates a new Volume.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html#createVolume
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-v2
         """
         post_body = json.dumps({'volume': kwargs})
         resp, body = self.post('volumes', post_body)
@@ -74,8 +75,9 @@
     def update_volume(self, volume_id, **kwargs):
         """Updates the Specified Volume.
 
-        Available params: see http://developer.openstack.org/
-                                api-ref-blockstorage-v2.html#updateVolume
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-v2
         """
         put_body = json.dumps({'volume': kwargs})
         resp, body = self.put('volumes/%s' % volume_id, put_body)
@@ -101,8 +103,9 @@
     def attach_volume(self, volume_id, **kwargs):
         """Attaches a volume to a given instance on a given mountpoint.
 
-        Available params: see http://developer.openstack.org/
-                                api-ref-blockstorage-v2.html#attachVolume
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#attach-volume-v2
         """
         post_body = json.dumps({'os-attach': kwargs})
         url = 'volumes/%s/action' % (volume_id)
@@ -157,8 +160,9 @@
     def extend_volume(self, volume_id, **kwargs):
         """Extend a volume.
 
-        Available params: see http://developer.openstack.org/
-                                api-ref-blockstorage-v2.html#extendVolume
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#extend-volume-v2
         """
         post_body = json.dumps({'os-extend': kwargs})
         url = 'volumes/%s/action' % (volume_id)
@@ -169,8 +173,9 @@
     def reset_volume_status(self, volume_id, **kwargs):
         """Reset the Specified Volume's Status.
 
-        Available params: see http://developer.openstack.org/
-                                api-ref-blockstorage-v2.html#resetVolume
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#reset-volume-status-v2
         """
         post_body = json.dumps({'os-reset_status': kwargs})
         resp, body = self.post('volumes/%s/action' % volume_id, post_body)
@@ -180,8 +185,9 @@
     def create_volume_transfer(self, **kwargs):
         """Create a volume transfer.
 
-        Available params: see http://developer.openstack.org/
-                                api-ref-blockstorage-v2.html#createVolumeTransfer
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#create-volume-transfer-v2
         """
         post_body = json.dumps({'transfer': kwargs})
         resp, body = self.post('os-volume-transfer', post_body)
@@ -200,8 +206,9 @@
     def list_volume_transfers(self, **params):
         """List all the volume transfers created.
 
-        Available params: see http://developer.openstack.org/
-                                api-ref-blockstorage-v2.html#listVolumeTransfer
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#list-volume-transfers-v2
         """
         url = 'os-volume-transfer'
         if params:
@@ -220,8 +227,9 @@
     def accept_volume_transfer(self, transfer_id, **kwargs):
         """Accept a volume transfer.
 
-        Available params: see http://developer.openstack.org/
-                                api-ref-blockstorage-v2.html#acceptVolumeTransfer
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#accept-volume-transfer-v2
         """
         url = 'os-volume-transfer/%s/accept' % transfer_id
         post_body = json.dumps({'accept': kwargs})
@@ -296,9 +304,9 @@
     def update_volume_image_metadata(self, volume_id, **kwargs):
         """Update image metadata for the volume.
 
-        Available params: see http://developer.openstack.org/
-                              api-ref-blockstorage-v2.html
-                              #setVolumeimagemetadata
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#update-volume-image-metadata-v2
         """
         post_body = json.dumps({'os-set_image_metadata': {'metadata': kwargs}})
         url = "volumes/%s/action" % (volume_id)
@@ -329,9 +337,9 @@
     def show_backend_capabilities(self, host):
         """Shows capabilities for a storage back end.
 
-         Output params: see http://developer.openstack.org/
-                            api-ref-blockstorage-v2.html
-                            #showBackendCapabilities
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/block-storage/v2/#show_backend_capabilities-v2
         """
         url = 'capabilities/%s' % host
         resp, body = self.get(url)
diff --git a/tempest/services/volume/__init__.py b/tempest/lib/services/volume/v3/__init__.py
similarity index 77%
rename from tempest/services/volume/__init__.py
rename to tempest/lib/services/volume/v3/__init__.py
index c62dd53..a4600a8 100644
--- a/tempest/services/volume/__init__.py
+++ b/tempest/lib/services/volume/v3/__init__.py
@@ -12,6 +12,7 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
-from tempest.services.volume import v3
+from tempest.lib.services.volume.v3.base_client import BaseClient
+from tempest.lib.services.volume.v3.messages_client import MessagesClient
 
-__all__ = ['v3']
+__all__ = ['MessagesClient', 'BaseClient']
diff --git a/tempest/services/volume/base/base_v3_client.py b/tempest/lib/services/volume/v3/base_client.py
similarity index 90%
rename from tempest/services/volume/base/base_v3_client.py
rename to tempest/lib/services/volume/v3/base_client.py
index ad6f760..958212a 100644
--- a/tempest/services/volume/base/base_v3_client.py
+++ b/tempest/lib/services/volume/v3/base_client.py
@@ -19,13 +19,13 @@
 VOLUME_MICROVERSION = None
 
 
-class BaseV3Client(rest_client.RestClient):
+class BaseClient(rest_client.RestClient):
     """Base class to handle Cinder v3 client microversion support."""
     api_version = 'v3'
     api_microversion_header_name = 'Openstack-Api-Version'
 
     def get_headers(self, accept_type=None, send_type=None):
-        headers = super(BaseV3Client, self).get_headers(
+        headers = super(BaseClient, self).get_headers(
             accept_type=accept_type, send_type=send_type)
         if VOLUME_MICROVERSION:
             headers[self.api_microversion_header_name] = ('volume %s' %
@@ -35,7 +35,7 @@
     def request(self, method, url, extra_headers=False, headers=None,
                 body=None, chunked=False):
 
-        resp, resp_body = super(BaseV3Client, self).request(
+        resp, resp_body = super(BaseClient, self).request(
             method, url, extra_headers, headers, body, chunked)
         if (VOLUME_MICROVERSION and
             VOLUME_MICROVERSION != api_version_utils.LATEST_MICROVERSION):
diff --git a/tempest/services/volume/v3/json/messages_client.py b/tempest/lib/services/volume/v3/messages_client.py
similarity index 94%
rename from tempest/services/volume/v3/json/messages_client.py
rename to tempest/lib/services/volume/v3/messages_client.py
index 6be6d59..8a01864 100644
--- a/tempest/services/volume/v3/json/messages_client.py
+++ b/tempest/lib/services/volume/v3/messages_client.py
@@ -17,10 +17,10 @@
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
-from tempest.services.volume.base import base_v3_client
+from tempest.lib.services.volume.v3 import base_client
 
 
-class MessagesClient(base_v3_client.BaseV3Client):
+class MessagesClient(base_client.BaseClient):
     """Client class to send user messages API requests."""
 
     def show_message(self, message_id):
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 8b86267..73544d9 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -219,6 +219,10 @@
                       imageRef=None, volume_type=None):
         if size is None:
             size = CONF.volume.volume_size
+        if imageRef:
+            image = self.compute_images_client.show_image(imageRef)['image']
+            min_disk = image.get('minDisk')
+            size = max(size, min_disk)
         if name is None:
             name = data_utils.rand_name(self.__class__.__name__ + "-volume")
         kwargs = {'display_name': name,
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 3ac6759..f2f17d2 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -16,8 +16,8 @@
 from tempest.common import custom_matchers
 from tempest.common import waiters
 from tempest import config
-from tempest import exceptions
 from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
 from tempest.scenario import manager
 from tempest import test
 
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index af313a5..4a076e4 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -748,7 +748,6 @@
         self.check_public_network_connectivity(
             should_connect=False,
             msg='after router unscheduling',
-            should_check_floating_ip_status=False
         )
 
         # schedule resource to new agent
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index c66128d..2d2f7df 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -16,9 +16,10 @@
 import json
 import re
 
+from tempest.common import waiters
 from tempest import config
-from tempest import exceptions
 from tempest.lib.common.utils import test_utils
+from tempest.lib import exceptions
 from tempest.scenario import manager
 from tempest import test
 
@@ -134,3 +135,5 @@
         self.verify_metadata_on_config_drive()
         self.verify_networkdata_on_config_drive()
         self.servers_client.delete_server(self.instance['id'])
+        waiters.wait_for_server_termination(
+            self.servers_client, self.instance['id'], ignore_error=False)
diff --git a/tempest/services/identity/v3/__init__.py b/tempest/services/identity/v3/__init__.py
index 9b40b77..6e64a7d 100644
--- a/tempest/services/identity/v3/__init__.py
+++ b/tempest/services/identity/v3/__init__.py
@@ -22,14 +22,14 @@
 from tempest.lib.services.identity.v3.policies_client import PoliciesClient
 from tempest.lib.services.identity.v3.projects_client import ProjectsClient
 from tempest.lib.services.identity.v3.regions_client import RegionsClient
+from tempest.lib.services.identity.v3.role_assignments_client import \
+    RoleAssignmentsClient
 from tempest.lib.services.identity.v3.roles_client import RolesClient
 from tempest.lib.services.identity.v3.services_client import ServicesClient
 from tempest.lib.services.identity.v3.token_client import V3TokenClient
 from tempest.lib.services.identity.v3.trusts_client import TrustsClient
 from tempest.lib.services.identity.v3.users_client import UsersClient
 from tempest.services.identity.v3.json.domains_client import DomainsClient
-from tempest.services.identity.v3.json.role_assignments_client import \
-    RoleAssignmentsClient
 
 __all__ = ['CredentialsClient', 'EndPointsClient', 'GroupsClient',
            'IdentityClient', 'InheritedRolesClient', 'PoliciesClient',
diff --git a/tempest/services/identity/v3/json/role_assignments_client.py b/tempest/services/identity/v3/json/role_assignments_client.py
deleted file mode 100644
index 9fd7736..0000000
--- a/tempest/services/identity/v3/json/role_assignments_client.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2016 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 tempest.lib.common import rest_client
-
-
-class RoleAssignmentsClient(rest_client.RestClient):
-    api_version = "v3"
-
-    def list_user_project_effective_assignments(
-            self, project_id, user_id):
-        """List the effective role assignments for a user in a project."""
-        resp, body = self.get(
-            "role_assignments?scope.project.id=%s&user.id=%s&effective" %
-            (project_id, user_id))
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return rest_client.ResponseBody(resp, body)
diff --git a/tempest/services/volume/v3/__init__.py b/tempest/services/volume/v3/__init__.py
deleted file mode 100644
index d50098c..0000000
--- a/tempest/services/volume/v3/__init__.py
+++ /dev/null
@@ -1,17 +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.volume.v3.json.messages_client import MessagesClient
-
-__all__ = ['MessagesClient']
diff --git a/tempest/services/volume/v3/json/__init__.py b/tempest/services/volume/v3/json/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/services/volume/v3/json/__init__.py
+++ /dev/null
diff --git a/tempest/tests/lib/cli/test_execute.py b/tempest/tests/lib/cli/test_execute.py
index cc9c94c..aaeb6f4 100644
--- a/tempest/tests/lib/cli/test_execute.py
+++ b/tempest/tests/lib/cli/test_execute.py
@@ -11,7 +11,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-
 import mock
 import subprocess
 
@@ -74,3 +73,20 @@
         self.assertRaises(exceptions.CommandFailed, cli_base.execute,
                           "/bin/ls", action="tempest", flags="--foobar",
                           merge_stderr=True)
+
+    def test_execute_with_prefix(self):
+        result = cli_base.execute("env", action="",
+                                  prefix="env NEW_VAR=1")
+        self.assertIsInstance(result, str)
+        self.assertIn("NEW_VAR=1", result)
+
+
+class TestCLIClient(base.TestCase):
+
+    @mock.patch.object(cli_base, 'execute')
+    def test_execute_with_prefix(self, mock_execute):
+        cli = cli_base.CLIClient(prefix='env LAC_ALL=C')
+        cli.glance('action')
+        self.assertEqual(mock_execute.call_count, 1)
+        self.assertEqual(mock_execute.call_args[1],
+                         {'prefix': 'env LAC_ALL=C'})
diff --git a/tempest/tests/lib/services/identity/v3/test_role_assignments_client.py b/tempest/tests/lib/services/identity/v3/test_role_assignments_client.py
new file mode 100644
index 0000000..7d304c1
--- /dev/null
+++ b/tempest/tests/lib/services/identity/v3/test_role_assignments_client.py
@@ -0,0 +1,206 @@
+# Copyright 2016 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 tempest.lib.services.identity.v3 import role_assignments_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestRoleAssignmentsClient(base.BaseServiceTest):
+
+    FAKE_USER_ID = "313234"
+    FAKE_GROUP_ID = "101112"
+
+    FAKE_ROLE1_ID = "123456"
+    FAKE_ROLE2_ID = "123457"
+
+    FAKE_PROJECT_ID = "456789"
+    FAKE_DOMAIN_ID = "102030"
+
+    FAKE_USER_PROJECT_ASSIGNMENT = {
+        "links": {
+            "assignment": "http://example.com/identity/v3/projects/"
+                          "%s/users/%s/roles/%s" % (FAKE_PROJECT_ID,
+                                                    FAKE_USER_ID,
+                                                    FAKE_ROLE2_ID)
+        },
+        "role": {
+            "id": FAKE_ROLE2_ID
+        },
+        "scope": {
+            "project": {
+                "id": FAKE_PROJECT_ID
+            }
+        },
+        "user": {
+            "id": FAKE_USER_ID
+        }
+    }
+
+    FAKE_GROUP_PROJECT_ASSIGNMENT = {
+        "links": {
+            "assignment": "http://example.com/identity/v3/projects/"
+                          "%s/groups/%s/roles/%s" % (FAKE_PROJECT_ID,
+                                                     FAKE_GROUP_ID,
+                                                     FAKE_ROLE1_ID)
+        },
+        "role": {
+            "id": FAKE_ROLE1_ID
+        },
+        "scope": {
+            "project": {
+                "id": FAKE_PROJECT_ID
+            }
+        },
+        "group": {
+            "id": FAKE_GROUP_ID
+        }
+    }
+
+    FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENT = {
+        "links": {
+            "assignment": "http://example.com/identity/v3/projects/"
+                          "%s/groups/%s/roles/%s" % (FAKE_PROJECT_ID,
+                                                     FAKE_GROUP_ID,
+                                                     FAKE_ROLE1_ID),
+            "membership": "http://example.com/identity/v3/groups/"
+                          "%s/users/%s" % (FAKE_GROUP_ID, FAKE_USER_ID)
+        },
+        "role": {
+            "id": FAKE_ROLE1_ID
+        },
+        "scope": {
+            "project": {
+                "id": FAKE_PROJECT_ID
+            }
+        },
+        "user": {
+            "id": FAKE_USER_ID
+        }
+    }
+
+    FAKE_USER_DOMAIN_ASSIGNMENT = {
+        "links": {
+            "assignment": "http://example.com/identity/v3/domains/"
+                          "%s/users/%s/roles/%s" % (FAKE_DOMAIN_ID,
+                                                    FAKE_USER_ID,
+                                                    FAKE_ROLE1_ID)
+        },
+        "role": {
+            "id": FAKE_ROLE1_ID
+        },
+        "scope": {
+            "domain": {
+                "id": FAKE_DOMAIN_ID
+            }
+        },
+        "user": {
+            "id": FAKE_USER_ID
+        }
+    }
+
+    FAKE_GROUP_PROJECT_ASSIGNMENTS = {
+        "role_assignments": [
+            FAKE_GROUP_PROJECT_ASSIGNMENT
+        ],
+        "links": {
+            "self": "http://example.com/identity/v3/role_assignments?"
+                    "scope.project.id=%s&group.id=%s&effective" % (
+                        FAKE_PROJECT_ID, FAKE_GROUP_ID),
+            "previous": None,
+            "next": None
+        }
+    }
+
+    FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENTS = {
+        "role_assignments": [
+            FAKE_USER_PROJECT_ASSIGNMENT,
+            FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENT
+        ],
+        "links": {
+            "self": "http://example.com/identity/v3/role_assignments?"
+                    "scope.project.id=%s&user.id=%s&effective" % (
+                        FAKE_PROJECT_ID, FAKE_USER_ID),
+            "previous": None,
+            "next": None
+        }
+    }
+
+    FAKE_USER_DOMAIN_ASSIGNMENTS = {
+        "role_assignments": [
+            FAKE_USER_DOMAIN_ASSIGNMENT
+        ],
+        "links": {
+            "self": "http://example.com/identity/v3/role_assignments?"
+                    "scope.domain.id=%s&user.id=%s&effective" % (
+                        FAKE_DOMAIN_ID, FAKE_USER_ID),
+            "previous": None,
+            "next": None
+        }
+    }
+
+    def setUp(self):
+        super(TestRoleAssignmentsClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = role_assignments_client.RoleAssignmentsClient(
+            fake_auth, 'identity', 'regionOne')
+
+    def _test_list_user_project_effective_assignments(self, bytes_body=False):
+        params = {'scope.project.id': self.FAKE_PROJECT_ID,
+                  'user.id': self.FAKE_USER_ID}
+        self.check_service_client_function(
+            self.client.list_role_assignments,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_USER_PROJECT_EFFECTIVE_ASSIGNMENTS,
+            bytes_body,
+            effective=True,
+            **params)
+
+    def test_list_user_project_effective_assignments_with_str_body(self):
+        self._test_list_user_project_effective_assignments()
+
+    def test_list_user_project_effective_assignments_with_bytes_body(self):
+        self._test_list_user_project_effective_assignments(bytes_body=True)
+
+    def _test_list_group_project_assignments(self, bytes_body=False):
+        params = {'scope.project.id': self.FAKE_PROJECT_ID,
+                  'group.id': self.FAKE_GROUP_ID}
+        self.check_service_client_function(
+            self.client.list_role_assignments,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_GROUP_PROJECT_ASSIGNMENTS,
+            bytes_body,
+            **params)
+
+    def test_list_group_project_assignments_with_str_body(self):
+        self._test_list_group_project_assignments()
+
+    def test_list_group_project_assignments_with_bytes_body(self):
+        self._test_list_group_project_assignments(bytes_body=True)
+
+    def _test_list_user_domain_assignments(self, bytes_body=False):
+        params = {'scope.domain.id': self.FAKE_DOMAIN_ID,
+                  'user.id': self.FAKE_USER_ID}
+        self.check_service_client_function(
+            self.client.list_role_assignments,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_GROUP_PROJECT_ASSIGNMENTS,
+            bytes_body,
+            **params)
+
+    def test_list_user_domain_assignments_with_str_body(self):
+        self._test_list_user_domain_assignments()
+
+    def test_list_user_domain_assignments_with_bytes_body(self):
+        self._test_list_user_domain_assignments(bytes_body=True)
diff --git a/tempest/services/volume/base/__init__.py b/tempest/tests/lib/services/volume/v3/__init__.py
similarity index 100%
rename from tempest/services/volume/base/__init__.py
rename to tempest/tests/lib/services/volume/v3/__init__.py
diff --git a/tempest/tests/lib/services/volume/v3/test_user_messages_client.py b/tempest/tests/lib/services/volume/v3/test_user_messages_client.py
new file mode 100644
index 0000000..4aeed5f
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v3/test_user_messages_client.py
@@ -0,0 +1,92 @@
+# Copyright 2016 Red Hat.  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.volume.v3 import messages_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestUserMessagesClient(base.BaseServiceTest):
+    USER_MESSAGE_INFO = {
+        "created_at": "2016-11-21T06:16:34.000000",
+        "guaranteed_until": "2016-12-21T06:16:34.000000",
+        "user_message": "No storage could be allocated for this volume "
+                        "request. You may be able to try another size or"
+                        " volume type.",
+        "resource_uuid": "c570b406-bf0b-4067-9398-f0bb09a7d9d7",
+        "request_id": "req-8f68681e-9b6b-4009-b94c-ac0811595451",
+        "message_level": "ERROR",
+        "id": "9a7dafbd-a156-4540-8996-50e71b5dcadf",
+        "resource_type": "VOLUME",
+        "links": [
+            {"href": "http://192.168.100.230:8776/v3/"
+                     "a678cb65f701462ea2257245cd640829/messages/"
+                     "9a7dafbd-a156-4540-8996-50e71b5dcadf",
+             "rel": "self"},
+            {"href": "http://192.168.100.230:8776/"
+                     "a678cb65f701462ea2257245cd640829/messages/"
+                     "9a7dafbd-a156-4540-8996-50e71b5dcadf",
+             "rel": "bookmark"}]
+        }
+    FAKE_SHOW_USER_MESSAGE = {
+        "message": dict(event_id="000002", **USER_MESSAGE_INFO)}
+
+    FAKE_LIST_USER_MESSAGES = {
+        "messages": [
+            dict(event_id="000003", **USER_MESSAGE_INFO),
+            dict(event_id="000004", **USER_MESSAGE_INFO)
+        ]
+    }
+
+    def setUp(self):
+        super(TestUserMessagesClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = messages_client.MessagesClient(fake_auth,
+                                                     'volume',
+                                                     'regionOne')
+
+    def _test_show_user_message(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.show_message,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_SHOW_USER_MESSAGE,
+            bytes_body,
+            message_id="9a7dafbd-a156-4540-8996-50e71b5dcadf")
+
+    def _test_list_user_message(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_messages,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_USER_MESSAGES,
+            bytes_body)
+
+    def test_list_user_message_with_str_body(self):
+        self._test_list_user_message()
+
+    def test_list_user_message_with_bytes_body(self):
+        self._test_list_user_message(bytes_body=True)
+
+    def test_show_user_message_with_str_body(self):
+        self._test_show_user_message()
+
+    def test_show_user_message_with_bytes_body(self):
+        self._test_show_user_message(bytes_body=True)
+
+    def test_delete_user_message(self):
+        self.check_service_client_function(
+            self.client.delete_message,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            message_id="9a7dafbd-a156-4540-8996-50e71b5dcadf",
+            status=204)
diff --git a/tempest/tests/test_negative_rest_client.py b/tempest/tests/test_negative_rest_client.py
deleted file mode 100644
index 05f9f3e..0000000
--- a/tempest/tests/test_negative_rest_client.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# (c) 2015 Deutsche Telekom AG
-# Copyright 2015 Red Hat, Inc.
-# Copyright 2015 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.
-
-import mock
-from oslotest import mockpatch
-
-from tempest.common import negative_rest_client
-from tempest import config
-from tempest.tests import base
-from tempest.tests import fake_config
-from tempest.tests.lib import fake_auth_provider
-
-
-class TestNegativeRestClient(base.TestCase):
-
-    url = 'fake_endpoint'
-
-    def setUp(self):
-        super(TestNegativeRestClient, self).setUp()
-        self.useFixture(fake_config.ConfigFixture())
-        self.patchobject(config, 'TempestConfigPrivate',
-                         fake_config.FakePrivate)
-        self.negative_rest_client = negative_rest_client.NegativeRestClient(
-            fake_auth_provider.FakeAuthProvider(), None)
-        self.useFixture(mockpatch.PatchObject(self.negative_rest_client,
-                                              '_log_request'))
-
-    @mock.patch('tempest.lib.common.rest_client.RestClient.post',
-                return_value=(mock.Mock(), mock.Mock()))
-    def test_post(self, mock_post):
-        __, return_dict = self.negative_rest_client.send_request('POST',
-                                                                 self.url,
-                                                                 [], {})
-        mock_post.assert_called_once_with(self.url, {})
-
-    @mock.patch('tempest.lib.common.rest_client.RestClient.get',
-                return_value=(mock.Mock(), mock.Mock()))
-    def test_get(self, mock_get):
-        __, return_dict = self.negative_rest_client.send_request('GET',
-                                                                 self.url,
-                                                                 [])
-        mock_get.assert_called_once_with(self.url)
-
-    @mock.patch('tempest.lib.common.rest_client.RestClient.delete',
-                return_value=(mock.Mock(), mock.Mock()))
-    def test_delete(self, mock_delete):
-        __, return_dict = self.negative_rest_client.send_request('DELETE',
-                                                                 self.url,
-                                                                 [])
-        mock_delete.assert_called_once_with(self.url)
-
-    @mock.patch('tempest.lib.common.rest_client.RestClient.patch',
-                return_value=(mock.Mock(), mock.Mock()))
-    def test_patch(self, mock_patch):
-        __, return_dict = self.negative_rest_client.send_request('PATCH',
-                                                                 self.url,
-                                                                 [], {})
-        mock_patch.assert_called_once_with(self.url, {})
-
-    @mock.patch('tempest.lib.common.rest_client.RestClient.put',
-                return_value=(mock.Mock(), mock.Mock()))
-    def test_put(self, mock_put):
-        __, return_dict = self.negative_rest_client.send_request('PUT',
-                                                                 self.url,
-                                                                 [], {})
-        mock_put.assert_called_once_with(self.url, {})
-
-    @mock.patch('tempest.lib.common.rest_client.RestClient.head',
-                return_value=(mock.Mock(), mock.Mock()))
-    def test_head(self, mock_head):
-        __, return_dict = self.negative_rest_client.send_request('HEAD',
-                                                                 self.url,
-                                                                 [])
-        mock_head.assert_called_once_with(self.url)
-
-    @mock.patch('tempest.lib.common.rest_client.RestClient.copy',
-                return_value=(mock.Mock(), mock.Mock()))
-    def test_copy(self, mock_copy):
-        __, return_dict = self.negative_rest_client.send_request('COPY',
-                                                                 self.url,
-                                                                 [])
-        mock_copy.assert_called_once_with(self.url)
-
-    def test_other(self):
-        self.assertRaises(AssertionError,
-                          self.negative_rest_client.send_request,
-                          'OTHER', self.url, [])
diff --git a/test-requirements.txt b/test-requirements.txt
index 3260915..475fb16 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,7 +1,7 @@
 # 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.12.0,<0.13  # Apache-2.0
+hacking<0.13,>=0.12.0 # Apache-2.0
 # needed for doc build
 sphinx!=1.3b1,<1.4,>=1.2.1 # BSD
 oslosphinx>=4.7.0 # Apache-2.0