Merge "Add namespace property unittest"
diff --git a/README.rst b/README.rst
index bc58cc0..281516b 100644
--- a/README.rst
+++ b/README.rst
@@ -4,6 +4,7 @@
 
 .. image:: http://governance.openstack.org/badges/tempest.svg
     :target: http://governance.openstack.org/reference/tags/index.html
+    :remote:
 
 .. Change things from this point on
 
diff --git a/bindep.txt b/bindep.txt
new file mode 100644
index 0000000..6a28348
--- /dev/null
+++ b/bindep.txt
@@ -0,0 +1,11 @@
+# This file contains runtime (non-python) dependencies
+# More info at: http://docs.openstack.org/infra/bindep/readme.html
+
+libffi-dev [platform:dpkg]
+libffi-devel [platform:rpm]
+gcc [platform:rpm]
+gcc [platform:dpkg]
+python-dev [platform:dpkg]
+python-devel [platform:rpm]
+openssl-devel [platform:rpm]
+libssl-dev [platform:dpkg]
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/13.1.0-volume-clients-as-library-309030c7a16e62ab.yaml b/releasenotes/notes/13.1.0-volume-clients-as-library-309030c7a16e62ab.yaml
index 056e199..6babd93 100644
--- a/releasenotes/notes/13.1.0-volume-clients-as-library-309030c7a16e62ab.yaml
+++ b/releasenotes/notes/13.1.0-volume-clients-as-library-309030c7a16e62ab.yaml
@@ -8,3 +8,5 @@
 
     * volumes_client(v1)
     * volumes_client(v2)
+    * capabilities_client(v2)
+    * scheduler_stats_client(v2)
diff --git a/releasenotes/notes/add-service-provider-client-cbba77d424a30dd3.yaml b/releasenotes/notes/add-service-provider-client-cbba77d424a30dd3.yaml
new file mode 100644
index 0000000..b504c78
--- /dev/null
+++ b/releasenotes/notes/add-service-provider-client-cbba77d424a30dd3.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - A Neutron Service Providers client is added to deal with resources
+    of the '/service-providers' route.
diff --git a/releasenotes/notes/remove-negative-test-generator-1653f4c0f86ccf75.yaml b/releasenotes/notes/remove-negative-test-generator-1653f4c0f86ccf75.yaml
new file mode 100644
index 0000000..a734d15
--- /dev/null
+++ b/releasenotes/notes/remove-negative-test-generator-1653f4c0f86ccf75.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+  - The Negative Tests Generator has been removed (it was not used by any
+    Tempest tests).
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_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_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
index 55134b1..1f043dc 100644
--- a/tempest/api/compute/admin/test_hypervisor.py
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -87,7 +87,7 @@
         ironic_only = True
         hypers_without_ironic = []
         for hyper in hypers:
-            details = (self.client.show_hypervisor(hypers[0]['id'])
+            details = (self.client.show_hypervisor(hyper['id'])
                        ['hypervisor'])
             if details['hypervisor_type'] != 'ironic':
                 hypers_without_ironic.append(hyper)
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/base.py b/tempest/api/compute/base.py
index d8294f7..f6a6040 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,21 @@
             self.request_microversion))
 
     @classmethod
-    def create_volume(cls):
+    def create_volume(cls, image_ref=None, **kwargs):
         """Create a volume and wait for it to become 'available'.
 
+        :param image_ref: Specify an image id to create a bootable volume.
+        :**kwargs: other parameters to create 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']
+        if 'size' not in kwargs:
+            kwargs['size'] = CONF.volume.volume_size
+        if 'display_name' not in kwargs:
+            vol_name = data_utils.rand_name(cls.__name__ + '-volume')
+            kwargs['display_name'] = vol_name
+        if image_ref is not None:
+            kwargs['imageRef'] = image_ref
+        volume = cls.volumes_client.create_volume(**kwargs)['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.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index fdf1e93..dcb2d2c 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -14,7 +14,6 @@
 #    under the License.
 
 from tempest.api.compute.floating_ips import base
-from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import test_utils
 from tempest.lib import exceptions as lib_exc
@@ -111,9 +110,7 @@
         # positive test:Association of an already associated floating IP
         # to specific server should change the association of the Floating IP
         # Create server so as to use for Multiple association
-        body = self.create_test_server()
-        waiters.wait_for_server_status(self.servers_client,
-                                       body['id'], 'ACTIVE')
+        body = self.create_test_server(wait_until='ACTIVE')
         self.new_server_id = body['id']
         self.addCleanup(self.servers_client.delete_server, self.new_server_id)
 
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..549262e 100644
--- a/tempest/api/compute/images/test_images_negative.py
+++ b/tempest/api/compute/images/test_images_negative.py
@@ -46,7 +46,7 @@
         # An image should not be created if the server instance is removed
         server = self.create_test_server(wait_until='ACTIVE')
 
-        # Delete server before trying to create server
+        # Delete server before trying to create image
         self.servers_client.delete_server(server['id'])
         waiters.wait_for_server_termination(self.servers_client, server['id'])
         # Create a new image after server is deleted
@@ -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..1c9b3f1 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
@@ -98,9 +93,9 @@
     @test.attr(type=['negative'])
     @test.idempotent_id('3d24d11f-5366-4536-bd28-cff32b748eca')
     def test_create_image_specify_metadata_over_limits(self):
-        # Return an error when creating image with meta data over 256 chars
+        # Return an error when creating image with meta data over 255 chars
         snapshot_name = data_utils.rand_name('test-snap')
-        meta = {'a' * 260: 'b' * 260}
+        meta = {'a' * 256: 'b' * 256}
         self.assertRaises(lib_exc.BadRequest, self.client.create_image,
                           self.server_id, name=snapshot_name, metadata=meta)
 
@@ -123,10 +118,10 @@
 
     @test.attr(type=['negative'])
     @test.idempotent_id('084f0cbc-500a-4963-8a4e-312905862581')
-    def test_create_image_specify_name_over_256_chars(self):
-        # Return an error if snapshot name over 256 characters is passed
+    def test_create_image_specify_name_over_character_limit(self):
+        # Return an error if snapshot name over 255 characters is passed
 
-        snapshot_name = data_utils.rand_name('a' * 260)
+        snapshot_name = ('a' * 256)
         self.assertRaises(lib_exc.BadRequest, self.client.create_image,
                           self.server_id, name=snapshot_name)
 
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/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 755336f..4184afa 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -94,10 +94,8 @@
 
         # Create server and add the security group created
         # above to the server we just created
-        server = self.create_test_server()
+        server = self.create_test_server(wait_until='ACTIVE')
         server_id = server['id']
-        waiters.wait_for_server_status(self.servers_client, server_id,
-                                       'ACTIVE')
         self.servers_client.add_security_group(server_id, name=sg['name'])
 
         # Check that we are not able to delete the security
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 8342a3e..a21ce94 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -46,7 +46,6 @@
     @classmethod
     def setup_clients(cls):
         super(AttachInterfacesTestJSON, cls).setup_clients()
-        cls.networks_client = cls.os.networks_client
         cls.subnets_client = cls.os.subnets_client
         cls.ports_client = cls.os.ports_client
 
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_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/test_live_block_migration_negative.py b/tempest/api/compute/test_live_block_migration_negative.py
index bd2b185..f072b81 100644
--- a/tempest/api/compute/test_live_block_migration_negative.py
+++ b/tempest/api/compute/test_live_block_migration_negative.py
@@ -33,7 +33,6 @@
     @classmethod
     def setup_clients(cls):
         super(LiveBlockMigrationNegativeTestJSON, cls).setup_clients()
-        cls.admin_hosts_client = cls.os_adm.hosts_client
         cls.admin_servers_client = cls.os_adm.servers_client
 
     def _migrate_server_to(self, server_id, dest_host):
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index 122e7cc..b9e0c35 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -48,7 +48,8 @@
                                      'fixed_ips', 'key_pairs',
                                      'injected_file_path_bytes',
                                      'instances', 'security_group_rules',
-                                     'cores', 'security_groups'))
+                                     'cores', 'security_groups',
+                                     'server_group_members', 'server_groups'))
 
     @test.idempotent_id('f1ef0a97-dbbb-4cca-adc5-c9fbc4f76107')
     def test_get_quotas(self):
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 065e183..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,51 +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, device=None):
-        # 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
-        kwargs = {'volumeId': volume['id']}
+        kwargs = {'volumeId': volume_id}
         if device:
             kwargs.update({'device': '/dev/%s' % device})
-        self.attachment = self.servers_client.attach_volume(
-            server['id'], **kwargs)['volumeAttachment']
+        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, device=self.device)
+        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'],
@@ -114,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')
@@ -138,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, device=self.device)
-
-        # 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):
@@ -220,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, device=self.device)
+        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'])
@@ -241,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, device=self.device)
-
+        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_attach_volume_negative.py b/tempest/api/compute/volumes/test_attach_volume_negative.py
index b7fa0fe..1f18bfe 100644
--- a/tempest/api/compute/volumes/test_attach_volume_negative.py
+++ b/tempest/api/compute/volumes/test_attach_volume_negative.py
@@ -29,6 +29,7 @@
             skip_msg = ("%s skipped as Cinder is not available" % cls.__name__)
             raise cls.skipException(skip_msg)
 
+    @test.related_bug('1630783', status_code=500)
     @test.idempotent_id('a313b5cd-fbd0-49cc-94de-870e99f763c7')
     def test_delete_attached_volume(self):
         server = self.create_test_server(wait_until='ACTIVE')
diff --git a/tempest/api/compute/volumes/test_volumes_list.py b/tempest/api/compute/volumes/test_volumes_list.py
index 7d76731..e4e625b 100644
--- a/tempest/api/compute/volumes/test_volumes_list.py
+++ b/tempest/api/compute/volumes/test_volumes_list.py
@@ -16,8 +16,6 @@
 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
 from tempest import config
 from tempest import test
 
@@ -51,34 +49,11 @@
         cls.volume_list = []
         cls.volume_id_list = []
         for i in range(3):
-            v_name = data_utils.rand_name(cls.__name__ + '-volume')
             metadata = {'Type': 'work'}
-            try:
-                volume = cls.client.create_volume(size=CONF.volume.volume_size,
-                                                  display_name=v_name,
-                                                  metadata=metadata)['volume']
-                waiters.wait_for_volume_status(cls.client,
-                                               volume['id'], 'available')
-                volume = cls.client.show_volume(volume['id'])['volume']
-                cls.volume_list.append(volume)
-                cls.volume_id_list.append(volume['id'])
-            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.
-                    for volume in cls.volume_list:
-                        cls.delete_volume(volume['id'])
-                raise exc
-
-    @classmethod
-    def resource_cleanup(cls):
-        # Delete the created Volumes
-        for volume in cls.volume_list:
-            cls.delete_volume(volume['id'])
-        super(VolumesTestJSON, cls).resource_cleanup()
+            volume = cls.create_volume(metadata=metadata)
+            volume = cls.client.show_volume(volume['id'])['volume']
+            cls.volume_list.append(volume)
+            cls.volume_id_list.append(volume['id'])
 
     @test.idempotent_id('bc2dd1a0-15af-48e5-9990-f2e75a48325d')
     def test_volume_list(self):
diff --git a/tempest/api/identity/admin/v3/test_default_project_id.py b/tempest/api/identity/admin/v3/test_default_project_id.py
index 59ffc19..361ff31 100644
--- a/tempest/api/identity/admin/v3/test_default_project_id.py
+++ b/tempest/api/identity/admin/v3/test_default_project_id.py
@@ -36,7 +36,8 @@
     def test_default_project_id(self):
         # create a domain
         dom_name = data_utils.rand_name('dom')
-        domain_body = self.domains_client.create_domain(dom_name)['domain']
+        domain_body = self.domains_client.create_domain(
+            name=dom_name)['domain']
         dom_id = domain_body['id']
         self.addCleanup(self._delete_domain, dom_id)
 
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index cbf1439..e71341f 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -32,7 +32,7 @@
         cls.setup_domains = list()
         for i in range(3):
             domain = cls.domains_client.create_domain(
-                data_utils.rand_name('domain'),
+                name=data_utils.rand_name('domain'),
                 description=data_utils.rand_name('domain-desc'),
                 enabled=i < 2)['domain']
             cls.setup_domains.append(domain)
@@ -67,7 +67,7 @@
         # List domains filtering by name
         params = {'name': self.setup_domains[0]['name']}
         fetched_domains = self.domains_client.list_domains(
-            params=params)['domains']
+            **params)['domains']
         # Verify the filtered list is correct, domain names are unique
         # so exactly one domain should be found with the provided name
         self.assertEqual(1, len(fetched_domains))
@@ -79,7 +79,7 @@
         # List domains filtering by enabled domains
         params = {'enabled': True}
         fetched_domains = self.domains_client.list_domains(
-            params=params)['domains']
+            **params)['domains']
         # Verify the filtered list is correct
         self.assertIn(self.setup_domains[0], fetched_domains)
         self.assertIn(self.setup_domains[1], fetched_domains)
@@ -93,7 +93,7 @@
         d_name = data_utils.rand_name('domain')
         d_desc = data_utils.rand_name('domain-desc')
         domain = self.domains_client.create_domain(
-            d_name, description=d_desc)['domain']
+            name=d_name, description=d_desc)['domain']
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self._delete_domain, domain['id'])
         self.assertIn('id', domain)
@@ -138,7 +138,7 @@
         d_name = data_utils.rand_name('domain')
         d_desc = data_utils.rand_name('domain-desc')
         domain = self.domains_client.create_domain(
-            d_name, description=d_desc, enabled=False)['domain']
+            name=d_name, description=d_desc, enabled=False)['domain']
         self.addCleanup(self.domains_client.delete_domain, domain['id'])
         self.assertEqual(d_name, domain['name'])
         self.assertFalse(domain['enabled'])
@@ -148,11 +148,15 @@
     def test_create_domain_without_description(self):
         # Create domain only with name
         d_name = data_utils.rand_name('domain')
-        domain = self.domains_client.create_domain(d_name)['domain']
+        domain = self.domains_client.create_domain(name=d_name)['domain']
         self.addCleanup(self._delete_domain, domain['id'])
         self.assertIn('id', domain)
         expected_data = {'name': d_name, 'enabled': True}
-        self.assertIsNone(domain['description'])
+        # TODO(gmann): there is bug in keystone liberty version where
+        # description is not being returned if it is not being passed in
+        # request. Bug#1649245. Once bug is fixed then we can enable the below
+        # check.
+        # self.assertEqual('', domain['description'])
         self.assertDictContainsSubset(expected_data, domain)
 
 
diff --git a/tempest/api/identity/admin/v3/test_domains_negative.py b/tempest/api/identity/admin/v3/test_domains_negative.py
index 4330cab..100a121 100644
--- a/tempest/api/identity/admin/v3/test_domains_negative.py
+++ b/tempest/api/identity/admin/v3/test_domains_negative.py
@@ -28,7 +28,7 @@
         d_name = data_utils.rand_name('domain')
         d_desc = data_utils.rand_name('domain-desc')
         domain = self.domains_client.create_domain(
-            d_name,
+            name=d_name,
             description=d_desc)['domain']
         domain_id = domain['id']
 
@@ -51,7 +51,8 @@
         # Domain name length should not ne greater than 64 characters
         d_name = 'a' * 65
         self.assertRaises(lib_exc.BadRequest,
-                          self.domains_client.create_domain, d_name)
+                          self.domains_client.create_domain,
+                          name=d_name)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('43781c07-764f-4cf2-a405-953c1916f605')
@@ -64,9 +65,10 @@
     @test.idempotent_id('e6f9e4a2-4f36-4be8-bdbc-4e199ae29427')
     def test_domain_create_duplicate(self):
         domain_name = data_utils.rand_name('domain-dup')
-        domain = self.domains_client.create_domain(domain_name)['domain']
+        domain = self.domains_client.create_domain(name=domain_name)['domain']
         domain_id = domain['id']
         self.addCleanup(self.delete_domain, domain_id)
         # Domain name should be unique
         self.assertRaises(
-            lib_exc.Conflict, self.domains_client.create_domain, domain_name)
+            lib_exc.Conflict, self.domains_client.create_domain,
+            name=domain_name)
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 c7e8411..33fce8d 100644
--- a/tempest/api/identity/admin/v3/test_inherits.py
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -31,7 +31,7 @@
         u_email = '%s@testmail.tm' % u_name
         u_password = data_utils.rand_name('pass-')
         cls.domain = cls.domains_client.create_domain(
-            data_utils.rand_name('domain-'),
+            name=data_utils.rand_name('domain-'),
             description=data_utils.rand_name('domain-desc-'))['domain']
         cls.project = cls.projects_client.create_project(
             data_utils.rand_name('project-'),
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index f5bf923..670cb2f 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -34,7 +34,7 @@
         u_email = '%s@testmail.tm' % u_name
         cls.u_password = data_utils.rand_password()
         cls.domain = cls.domains_client.create_domain(
-            data_utils.rand_name('domain'),
+            name=data_utils.rand_name('domain'),
             description=data_utils.rand_name('domain-desc'))['domain']
         cls.project = cls.projects_client.create_project(
             data_utils.rand_name('project'),
diff --git a/tempest/api/identity/v3/test_tokens.py b/tempest/api/identity/v3/test_tokens.py
index d5bed96..b410da6 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -34,6 +34,7 @@
         # it to be 'default'
         token_id, resp = self.non_admin_token.get_token(
             user_id=user_id,
+            username=username,
             user_domain_id=user_domain_id,
             password=password,
             auth_data=True)
@@ -49,9 +50,19 @@
         self.assertGreater(expires_at, now)
 
         subject_id = resp['user']['id']
-        self.assertEqual(subject_id, user_id)
+        if user_id:
+            self.assertEqual(subject_id, user_id)
+        else:
+            # Expect a user ID, but don't know what it will be.
+            self.assertGreaterEqual(len(subject_id), 0,
+                                    'Expected user ID in token.')
 
         subject_name = resp['user']['name']
-        self.assertEqual(subject_name, username)
+        if username:
+            self.assertEqual(subject_name, username)
+        else:
+            # Expect a user name, but don't know what it will be.
+            self.assertGreaterEqual(len(subject_name), 0,
+                                    'Expected user name in token.')
 
         self.assertEqual(resp['methods'][0], 'password')
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_schema.py b/tempest/api/image/v2/test_images_metadefs_schema.py
new file mode 100644
index 0000000..7edf1af
--- /dev/null
+++ b/tempest/api/image/v2/test_images_metadefs_schema.py
@@ -0,0 +1,81 @@
+# 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 tempest.api.image import base
+from tempest import test
+
+
+class MetadataSchemaTest(base.BaseV2ImageTest):
+    """Test to get metadata schema"""
+
+    @test.idempotent_id('e9e44891-3cb8-3b40-a532-e0a39fea3dab')
+    def test_get_metadata_namespace_schema(self):
+        # Test to get namespace schema
+        body = self.schemas_client.show_schema("metadefs/namespace")
+        self.assertEqual("namespace", body['name'])
+
+    @test.idempotent_id('ffe44891-678b-3ba0-a3e2-e0a3967b3aeb')
+    def test_get_metadata_namespaces_schema(self):
+        # Test to get namespaces schema
+        body = self.schemas_client.show_schema("metadefs/namespaces")
+        self.assertEqual("namespaces", body['name'])
+
+    @test.idempotent_id('fde34891-678b-3b40-ae32-e0a3e67b6beb')
+    def test_get_metadata_resource_type_schema(self):
+        # Test to get resource_type schema
+        body = self.schemas_client.show_schema("metadefs/resource_type")
+        self.assertEqual("resource_type_association", body['name'])
+
+    @test.idempotent_id('dfe4a891-b38b-3bf0-a3b2-e03ee67b3a3a')
+    def test_get_metadata_resources_types_schema(self):
+        # Test to get resource_types schema
+        body = self.schemas_client.show_schema("metadefs/resource_types")
+        self.assertEqual("resource_type_associations", body['name'])
+
+    @test.idempotent_id('dff4a891-b38b-3bf0-a3b2-e03ee67b3a3b')
+    def test_get_metadata_object_schema(self):
+        # Test to get object schema
+        body = self.schemas_client.show_schema("metadefs/object")
+        self.assertEqual("object", body['name'])
+
+    @test.idempotent_id('dee4a891-b38b-3bf0-a3b2-e03ee67b3a3c')
+    def test_get_metadata_objects_schema(self):
+        # Test to get objects schema
+        body = self.schemas_client.show_schema("metadefs/objects")
+        self.assertEqual("objects", body['name'])
+
+    @test.idempotent_id('dae4a891-b38b-3bf0-a3b2-e03ee67b3a3d')
+    def test_get_metadata_property_schema(self):
+        # Test to get property schema
+        body = self.schemas_client.show_schema("metadefs/property")
+        self.assertEqual("property", body['name'])
+
+    @test.idempotent_id('dce4a891-b38b-3bf0-a3b2-e03ee67b3a3e')
+    def test_get_metadata_properties_schema(self):
+        # Test to get properties schema
+        body = self.schemas_client.show_schema("metadefs/properties")
+        self.assertEqual("properties", body['name'])
+
+    @test.idempotent_id('dde4a891-b38b-3bf0-a3b2-e03ee67b3a3e')
+    def test_get_metadata_tag_schema(self):
+        # Test to get tag schema
+        body = self.schemas_client.show_schema("metadefs/tag")
+        self.assertEqual("tag", body['name'])
+
+    @test.idempotent_id('cde4a891-b38b-3bf0-a3b2-e03ee67b3a3a')
+    def test_get_metadata_tags_schema(self):
+        # Test to get tags schema
+        body = self.schemas_client.show_schema("metadefs/tags")
+        self.assertEqual("tags", body['name'])
diff --git a/tempest/api/network/admin/test_negative_quotas.py b/tempest/api/network/admin/test_negative_quotas.py
index 7b037d5..c256b5b 100644
--- a/tempest/api/network/admin/test_negative_quotas.py
+++ b/tempest/api/network/admin/test_negative_quotas.py
@@ -38,11 +38,6 @@
             msg = "quotas extension not enabled."
             raise cls.skipException(msg)
 
-    @classmethod
-    def setup_clients(cls):
-        super(QuotasNegativeTest, cls).setup_clients()
-        cls.identity_admin_client = cls.os_adm.identity_client
-
     @test.idempotent_id('644f4e1b-1bf9-4af0-9fd8-eb56ac0f51cf')
     def test_network_quota_exceeding(self):
         # Set the network quota to two
diff --git a/tempest/api/network/admin/test_quotas.py b/tempest/api/network/admin/test_quotas.py
index 3a264ff..978fb8f 100644
--- a/tempest/api/network/admin/test_quotas.py
+++ b/tempest/api/network/admin/test_quotas.py
@@ -43,11 +43,6 @@
             msg = "quotas extension not enabled."
             raise cls.skipException(msg)
 
-    @classmethod
-    def setup_clients(cls):
-        super(QuotasTest, cls).setup_clients()
-        cls.identity_admin_client = cls.os_adm.identity_client
-
     def _check_quotas(self, new_quotas):
         # Add a project to conduct the test
         project = data_utils.rand_name('test_project_')
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index 629926d..c2c42bb 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -81,6 +81,7 @@
         cls.security_group_rules_client = (
             cls.os.security_group_rules_client)
         cls.network_versions_client = cls.os.network_versions_client
+        cls.service_providers_client = cls.os.service_providers_client
 
     @classmethod
     def resource_setup(cls):
@@ -135,12 +136,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_ports.py b/tempest/api/network/test_ports.py
index 15d289d..5b46088 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -360,11 +360,6 @@
 class PortsAdminExtendedAttrsTestJSON(base.BaseAdminNetworkTest):
 
     @classmethod
-    def setup_clients(cls):
-        super(PortsAdminExtendedAttrsTestJSON, cls).setup_clients()
-        cls.identity_client = cls.os_adm.identity_client
-
-    @classmethod
     def resource_setup(cls):
         super(PortsAdminExtendedAttrsTestJSON, cls).resource_setup()
         cls.network = cls.create_network()
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index e989b69..f2170ad 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -34,11 +34,6 @@
             raise cls.skipException(msg)
 
     @classmethod
-    def setup_clients(cls):
-        super(RoutersTest, cls).setup_clients()
-        cls.identity_admin_client = cls.os_adm.identity_client
-
-    @classmethod
     def resource_setup(cls):
         super(RoutersTest, cls).resource_setup()
         cls.tenant_cidr = (CONF.network.project_network_cidr
diff --git a/tempest/api/network/test_service_providers.py b/tempest/api/network/test_service_providers.py
new file mode 100644
index 0000000..09fb5fe
--- /dev/null
+++ b/tempest/api/network/test_service_providers.py
@@ -0,0 +1,23 @@
+#    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.network import base
+from tempest import test
+
+
+class ServiceProvidersTest(base.BaseNetworkTest):
+
+    @test.idempotent_id('2cbbeea9-f010-40f6-8df5-4eaa0c918ea6')
+    def test_service_providers_list(self):
+        body = self.service_providers_client.list_service_providers()
+        self.assertIn('service_providers', body)
+        self.assertIsInstance(body['service_providers'], list)
diff --git a/tempest/api/network/test_service_type_management.py b/tempest/api/network/test_service_type_management.py
deleted file mode 100644
index f49f082..0000000
--- a/tempest/api/network/test_service_type_management.py
+++ /dev/null
@@ -1,31 +0,0 @@
-#    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.network import base
-from tempest.lib import decorators
-from tempest import test
-
-
-class ServiceTypeManagementTestJSON(base.BaseNetworkTest):
-
-    @classmethod
-    def skip_checks(cls):
-        super(ServiceTypeManagementTestJSON, cls).skip_checks()
-        if not test.is_extension_enabled('service-type', 'network'):
-            msg = "Neutron Service Type Management not enabled."
-            raise cls.skipException(msg)
-
-    @decorators.skip_because(bug="1400370")
-    @test.idempotent_id('2cbbeea9-f010-40f6-8df5-4eaa0c918ea6')
-    def test_service_provider_list(self):
-        body = self.client.list_service_providers()
-        self.assertIsInstance(body['service_providers'], list)
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 1b1ffd1..eb313d2 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import time
+
 from tempest.common import custom_matchers
 from tempest import config
 from tempest.lib.common.utils import data_utils
@@ -48,6 +50,7 @@
         cls.object_client = cls.os.object_client
         cls.container_client = cls.os.container_client
         cls.account_client = cls.os.account_client
+        cls.capabilities_client = cls.os.capabilities_client
 
     @classmethod
     def resource_setup(cls):
@@ -63,7 +66,7 @@
         cls.policies = None
 
         if CONF.object_storage_feature_enabled.discoverability:
-            _, body = cls.account_client.list_extensions()
+            _, body = cls.capabilities_client.list_capabilities()
 
             if 'swift' in body and 'policies' in body['swift']:
                 cls.policies = body['swift']['policies']
@@ -102,6 +105,10 @@
         The containers should be visible from the container_client given.
         Will not throw any error if the containers don't exist.
         Will not check that object and container deletions succeed.
+        After delete all the objects from a container, it will wait 2
+        seconds before delete the container itself, in order to deployments
+        using HA proxy sync the deletion properly, otherwise, the container
+        might fail to be deleted because it's not empty.
 
         :param container_client: if None, use cls.container_client, this means
             that the default testing user will be used (see 'username' in
@@ -121,6 +128,9 @@
                 for obj in objlist:
                     test_utils.call_and_ignore_notfound_exc(
                         object_client.delete_object, cont, obj['name'])
+                # sleep 2 seconds to sync the deletion of the objects
+                # in HA deployment
+                time.sleep(2)
                 container_client.delete_container(cont)
             except lib_exc.NotFound:
                 pass
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index eda4568..59129e5 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -15,6 +15,7 @@
 
 import random
 
+import six
 import testtools
 
 from tempest.api.object_storage import base
@@ -60,8 +61,10 @@
         self.assertHeaders(resp, 'Account', 'GET')
 
         self.assertIsNotNone(container_list)
+
         for container_name in self.containers:
-            self.assertIn(container_name, container_list)
+            self.assertIn(six.text_type(container_name).encode('utf-8'),
+                          container_list)
 
     @test.idempotent_id('884ec421-fbad-4fcc-916b-0580f2699565')
     def test_list_no_containers(self):
@@ -132,7 +135,7 @@
         not CONF.object_storage_feature_enabled.discoverability,
         'Discoverability function is disabled')
     def test_list_extensions(self):
-        resp, extensions = self.account_client.list_extensions()
+        resp, extensions = self.capabilities_client.list_capabilities()
 
         self.assertThat(resp, custom_matchers.AreAllWellFormatted())
 
@@ -246,7 +249,8 @@
             params=params)
         self.assertHeaders(resp, 'Account', 'GET')
         for container in container_list:
-            self.assertEqual(True, container.startswith(prefix))
+            self.assertEqual(True, container.decode(
+                'utf-8').startswith(prefix))
 
     @test.attr(type='smoke')
     @test.idempotent_id('4894c312-6056-4587-8d6f-86ffbf861f80')
diff --git a/tempest/api/object_storage/test_container_services_negative.py b/tempest/api/object_storage/test_container_services_negative.py
index e6c53ec..f63c518 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -13,11 +13,16 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.object_storage import base
+from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import exceptions
 from tempest import test
 
+CONF = config.CONF
+
 
 class ContainerNegativeTest(base.BaseObjectTest):
 
@@ -25,12 +30,16 @@
     def resource_setup(cls):
         super(ContainerNegativeTest, cls).resource_setup()
 
-        # use /info to get default constraints
-        _, body = cls.account_client.list_extensions()
-        cls.constraints = body['swift']
+        if CONF.object_storage_feature_enabled.discoverability:
+            # use /info to get default constraints
+            _, body = cls.capabilities_client.list_capabilities()
+            cls.constraints = body['swift']
 
     @test.attr(type=["negative"])
     @test.idempotent_id('30686921-4bed-4764-a038-40d741ed4e78')
+    @testtools.skipUnless(
+        CONF.object_storage_feature_enabled.discoverability,
+        'Discoverability function is disabled')
     def test_create_container_name_exceeds_max_length(self):
         # Attempts to create a container name that is longer than max
         max_length = self.constraints['max_container_name_length']
@@ -44,6 +53,9 @@
 
     @test.attr(type=["negative"])
     @test.idempotent_id('41e645bf-2e68-4f84-bf7b-c71aa5cd76ce')
+    @testtools.skipUnless(
+        CONF.object_storage_feature_enabled.discoverability,
+        'Discoverability function is disabled')
     def test_create_container_metadata_name_exceeds_max_length(self):
         # Attempts to create container with metadata name
         # that is longer than max.
@@ -58,6 +70,9 @@
 
     @test.attr(type=["negative"])
     @test.idempotent_id('81e36922-326b-4b7c-8155-3bbceecd7a82')
+    @testtools.skipUnless(
+        CONF.object_storage_feature_enabled.discoverability,
+        'Discoverability function is disabled')
     def test_create_container_metadata_value_exceeds_max_length(self):
         # Attempts to create container with metadata value
         # that is longer than max.
@@ -72,6 +87,9 @@
 
     @test.attr(type=["negative"])
     @test.idempotent_id('ac666539-d566-4f02-8ceb-58e968dfb732')
+    @testtools.skipUnless(
+        CONF.object_storage_feature_enabled.discoverability,
+        'Discoverability function is disabled')
     def test_create_container_metadata_exceeds_overall_metadata_count(self):
         # Attempts to create container with metadata that exceeds the
         # default count
diff --git a/tempest/api/orchestration/stacks/test_neutron_resources.py b/tempest/api/orchestration/stacks/test_neutron_resources.py
index bffcb64..6d27502 100644
--- a/tempest/api/orchestration/stacks/test_neutron_resources.py
+++ b/tempest/api/orchestration/stacks/test_neutron_resources.py
@@ -77,7 +77,7 @@
             cls.client.wait_for_stack_status(cls.stack_id, 'CREATE_COMPLETE')
             resources = (cls.client.list_resources(cls.stack_identifier)
                          ['resources'])
-        except exceptions.TimeoutException as e:
+        except exceptions.TimeoutException:
             if CONF.compute_feature_enabled.console_output:
                 # attempt to log the server console to help with debugging
                 # the cause of the server not signalling the waitcondition
@@ -89,7 +89,7 @@
                 output = cls.servers_client.get_console_output(
                     server_id)['output']
                 LOG.debug(output)
-            raise e
+            raise
 
         cls.test_resources = {}
         for resource in resources:
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_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/test_backends_capabilities.py b/tempest/api/volume/admin/v2/test_backends_capabilities.py
similarity index 90%
rename from tempest/api/volume/admin/test_backends_capabilities.py
rename to tempest/api/volume/admin/v2/test_backends_capabilities.py
index 8a21853..fc9066c 100644
--- a/tempest/api/volume/admin/test_backends_capabilities.py
+++ b/tempest/api/volume/admin/v2/test_backends_capabilities.py
@@ -38,13 +38,13 @@
         # Get host list, formation: host@backend-name
         cls.hosts = [
             pool['name'] for pool in
-            cls.admin_volume_client.show_pools()['pools']
+            cls.admin_scheduler_stats_client.list_pools()['pools']
         ]
 
     @test.idempotent_id('3750af44-5ea2-4cd4-bc3e-56e7e6caf854')
     def test_get_capabilities_backend(self):
         # Test backend properties
-        backend = self.admin_volume_client.show_backend_capabilities(
+        backend = self.admin_capabilities_client.show_backend_capabilities(
             self.hosts[0])
 
         # Verify getting capabilities parameters from a backend
@@ -62,12 +62,12 @@
         # Get list backend capabilities using show_pools
         cinder_pools = [
             pool['capabilities'] for pool in
-            self.admin_volume_client.show_pools(detail=True)['pools']
+            self.admin_scheduler_stats_client.list_pools(detail=True)['pools']
         ]
 
         # Get list backends capabilities using show_backend_capabilities
         capabilities = [
-            self.admin_volume_client.show_backend_capabilities(
+            self.admin_capabilities_client.show_backend_capabilities(
                 host=host) for host in self.hosts
         ]
 
diff --git a/tempest/api/volume/admin/v2/test_volume_pools.py b/tempest/api/volume/admin/v2/test_volume_pools.py
index c662e8c..e460278 100644
--- a/tempest/api/volume/admin/v2/test_volume_pools.py
+++ b/tempest/api/volume/admin/v2/test_volume_pools.py
@@ -29,7 +29,7 @@
     def test_get_pools_without_details(self):
         volume_info = self.admin_volume_client. \
             show_volume(self.volume['id'])['volume']
-        cinder_pools = self.admin_volume_client.show_pools()['pools']
+        cinder_pools = self.admin_scheduler_stats_client.list_pools()['pools']
         self.assertIn(volume_info['os-vol-host-attr:host'],
                       [pool['name'] for pool in cinder_pools])
 
@@ -37,7 +37,7 @@
     def test_get_pools_with_details(self):
         volume_info = self.admin_volume_client. \
             show_volume(self.volume['id'])['volume']
-        cinder_pools = self.admin_volume_client.\
-            show_pools(detail=True)['pools']
+        cinder_pools = self.admin_scheduler_stats_client.\
+            list_pools(detail=True)['pools']
         self.assertIn(volume_info['os-vol-host-attr:host'],
                       [pool['name'] for pool in cinder_pools])
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index 178b0b1..7cd72e1 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -150,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.
 
@@ -254,6 +266,10 @@
                 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
+            cls.admin_capabilities_client = \
+                cls.os_adm.volume_capabilities_v2_client
+            cls.admin_scheduler_stats_client = \
+                cls.os_adm.volume_scheduler_stats_v2_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_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 38f1082..d8d6b9a 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -74,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')
@@ -136,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]:
@@ -148,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 6ed6b9c..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,7 +52,8 @@
         # 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'])
@@ -67,7 +63,8 @@
         # 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/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/clients.py b/tempest/clients.py
index b3290ac..4a30f6f 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -127,6 +127,7 @@
             self.network.SecurityGroupRulesClient())
         self.security_groups_client = self.network.SecurityGroupsClient()
         self.network_versions_client = self.network.NetworkVersionsClient()
+        self.service_providers_client = self.network.ServiceProvidersClient()
 
     def _set_image_clients(self):
         if CONF.service_available.glance:
@@ -303,6 +304,10 @@
             self.volume_v2.AvailabilityZoneClient()
         self.volume_limits_client = self.volume_v1.LimitsClient()
         self.volume_v2_limits_client = self.volume_v2.LimitsClient()
+        self.volume_capabilities_v2_client = \
+            self.volume_v2.CapabilitiesClient()
+        self.volume_scheduler_stats_v2_client = \
+            self.volume_v2.SchedulerStatsClient()
 
     def _set_object_storage_clients(self):
         # Mandatory parameters (always defined)
@@ -310,6 +315,8 @@
 
         self.account_client = object_storage.AccountClient(self.auth_provider,
                                                            **params)
+        self.capabilities_client = object_storage.CapabilitiesClient(
+            self.auth_provider, **params)
         self.container_client = object_storage.ContainerClient(
             self.auth_provider, **params)
         self.object_client = object_storage.ObjectClient(self.auth_provider,
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 381f3df..0a1881c 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -169,7 +169,7 @@
         'nova': os.extensions_client,
         'cinder': os.volumes_extension_client,
         'neutron': os.network_extensions_client,
-        'swift': os.account_client,
+        'swift': os.capabilities_client,
     }
     # NOTE (e0ne): Use Cinder API v2 by default because v1 is deprecated
     if CONF.volume_feature_enabled.api_v2:
@@ -201,7 +201,7 @@
     if service != 'swift':
         resp = extensions_client.list_extensions()
     else:
-        __, resp = extensions_client.list_extensions()
+        __, resp = extensions_client.list_capabilities()
     # For Nova, Cinder and Neutron we use the alias name rather than the
     # 'name' field because the alias is considered to be the canonical
     # name.
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/custom_matchers.py b/tempest/common/custom_matchers.py
index b6ff241..8410541 100644
--- a/tempest/common/custom_matchers.py
+++ b/tempest/common/custom_matchers.py
@@ -85,7 +85,7 @@
                     return NonExistentHeader('x-account-container-count')
                 if 'x-account-object-count' not in actual:
                     return NonExistentHeader('x-account-object-count')
-                if actual['x-account-container-count'] > 0:
+                if int(actual['x-account-container-count']) > 0:
                     acct_header = "x-account-storage-policy-"
                     matched_policy_count = 0
 
diff --git a/tempest/common/generator/__init__.py b/tempest/common/generator/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tempest/common/generator/__init__.py
+++ /dev/null
diff --git a/tempest/common/generator/base_generator.py b/tempest/common/generator/base_generator.py
deleted file mode 100644
index 0647edb..0000000
--- a/tempest/common/generator/base_generator.py
+++ /dev/null
@@ -1,176 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# 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 copy
-import functools
-
-import jsonschema
-import six
-
-
-def _check_for_expected_result(name, schema):
-    expected_result = None
-    if "results" in schema:
-        if name in schema["results"]:
-            expected_result = schema["results"][name]
-    return expected_result
-
-
-def generator_type(*args, **kwargs):
-    def wrapper(func):
-        func.types = args
-        for key in kwargs:
-            setattr(func, key, kwargs[key])
-        return func
-    return wrapper
-
-
-def simple_generator(fn):
-    """Decorator for simple generators that return one value"""
-    @functools.wraps(fn)
-    def wrapped(self, schema):
-        result = fn(self, schema)
-        if result is not None:
-            expected_result = _check_for_expected_result(fn.__name__, schema)
-            return (fn.__name__, result, expected_result)
-        return
-    return wrapped
-
-
-class BasicGeneratorSet(object):
-    _instance = None
-
-    schema = {
-        "type": "object",
-        "properties": {
-            "name": {"type": "string"},
-            "http-method": {
-                "enum": ["GET", "PUT", "HEAD",
-                         "POST", "PATCH", "DELETE", 'COPY']
-            },
-            "admin_client": {"type": "boolean"},
-            "url": {"type": "string"},
-            "default_result_code": {"type": "integer"},
-            "json-schema": {},
-            "resources": {
-                "type": "array",
-                "items": {
-                    "oneOf": [
-                        {"type": "string"},
-                        {
-                            "type": "object",
-                            "properties": {
-                                "name": {"type": "string"},
-                                "expected_result": {"type": "integer"}
-                            }
-                        }
-                    ]
-                }
-            },
-            "results": {
-                "type": "object",
-                "properties": {}
-            }
-        },
-        "required": ["name", "http-method", "url"],
-        "additionalProperties": False,
-    }
-
-    def __init__(self):
-        self.types_dict = {}
-        for m in dir(self):
-            if callable(getattr(self, m)) and not'__' in m:
-                method = getattr(self, m)
-                if hasattr(method, "types"):
-                    for type in method.types:
-                        if type not in self.types_dict:
-                            self.types_dict[type] = []
-                        self.types_dict[type].append(method)
-
-    def validate_schema(self, schema):
-        if "json-schema" in schema:
-            jsonschema.Draft4Validator.check_schema(schema['json-schema'])
-        jsonschema.validate(schema, self.schema)
-
-    def generate_scenarios(self, schema, path=None):
-        """Generate scenario (all possible test cases) out of the given schema
-
-        :param schema: a dict style schema (see ``BasicGeneratorSet.schema``)
-        :param path: the schema path if the given schema is a subschema
-        """
-        schema_type = schema['type']
-        scenarios = []
-
-        if schema_type == 'object':
-            properties = schema["properties"]
-            for attribute, definition in six.iteritems(properties):
-                current_path = copy.copy(path)
-                if path is not None:
-                    current_path.append(attribute)
-                else:
-                    current_path = [attribute]
-                scenarios.extend(
-                    self.generate_scenarios(definition, current_path))
-        elif isinstance(schema_type, list):
-            if "integer" in schema_type:
-                schema_type = "integer"
-            else:
-                raise Exception("non-integer list types not supported")
-        for generator in self.types_dict[schema_type]:
-            if hasattr(generator, "needed_property"):
-                prop = generator.needed_property
-                if (prop not in schema or
-                    schema[prop] is None or
-                    schema[prop] is False):
-                    continue
-
-            name = generator.__name__
-            if ("exclude_tests" in schema and
-               name in schema["exclude_tests"]):
-                continue
-            if path is not None:
-                name = "%s_%s" % ("_".join(path), name)
-            scenarios.append({
-                "_negtest_name": name,
-                "_negtest_generator": generator,
-                "_negtest_schema": schema,
-                "_negtest_path": path})
-        return scenarios
-
-    def generate_payload(self, test, schema):
-        """Generates one jsonschema out of the given test.
-
-        It's mandatory to use generate_scenarios before to register all needed
-        variables to the test.
-
-        :param test: A test object (scenario) with all _negtest variables on it
-        :param schema: schema for the test
-        """
-        generator = test._negtest_generator
-        ret = generator(test._negtest_schema)
-        path = copy.copy(test._negtest_path)
-        expected_result = None
-
-        if ret is not None:
-            generator_result = generator(test._negtest_schema)
-            invalid_snippet = generator_result[1]
-            expected_result = generator_result[2]
-            element = path.pop()
-            if len(path) > 0:
-                schema_snip = six.moves.reduce(dict.get, path, schema)
-                schema_snip[element] = invalid_snippet
-            else:
-                schema[element] = invalid_snippet
-        return expected_result
diff --git a/tempest/common/generator/negative_generator.py b/tempest/common/generator/negative_generator.py
deleted file mode 100644
index 67ace54..0000000
--- a/tempest/common/generator/negative_generator.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# 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 copy
-
-import tempest.common.generator.base_generator as base
-import tempest.common.generator.valid_generator as valid
-
-
-class NegativeTestGenerator(base.BasicGeneratorSet):
-    @base.generator_type("string")
-    @base.simple_generator
-    def gen_int(self, _):
-        return 4
-
-    @base.generator_type("integer")
-    @base.simple_generator
-    def gen_string(self, _):
-        return "XXXXXX"
-
-    @base.generator_type("integer", "string")
-    def gen_none(self, schema):
-        # Note(mkoderer): it's not using the decorator otherwise it'd be
-        # filtered
-        expected_result = base._check_for_expected_result('gen_none', schema)
-        return ('gen_none', None, expected_result)
-
-    @base.generator_type("string")
-    @base.simple_generator
-    def gen_str_min_length(self, schema):
-        min_length = schema.get("minLength", 0)
-        if min_length > 0:
-            return "x" * (min_length - 1)
-
-    @base.generator_type("string", needed_property="maxLength")
-    @base.simple_generator
-    def gen_str_max_length(self, schema):
-        max_length = schema.get("maxLength", -1)
-        return "x" * (max_length + 1)
-
-    @base.generator_type("integer", needed_property="minimum")
-    @base.simple_generator
-    def gen_int_min(self, schema):
-        minimum = schema["minimum"]
-        if "exclusiveMinimum" not in schema:
-            minimum -= 1
-        return minimum
-
-    @base.generator_type("integer", needed_property="maximum")
-    @base.simple_generator
-    def gen_int_max(self, schema):
-        maximum = schema["maximum"]
-        if "exclusiveMaximum" not in schema:
-            maximum += 1
-        return maximum
-
-    @base.generator_type("object", needed_property="additionalProperties")
-    @base.simple_generator
-    def gen_obj_add_attr(self, schema):
-        valid_schema = valid.ValidTestGenerator().generate_valid(schema)
-        new_valid = copy.deepcopy(valid_schema)
-        new_valid["$$$$$$$$$$"] = "xxx"
-        return new_valid
diff --git a/tempest/common/generator/valid_generator.py b/tempest/common/generator/valid_generator.py
deleted file mode 100644
index 3070489..0000000
--- a/tempest/common/generator/valid_generator.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# 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 six
-
-import tempest.common.generator.base_generator as base
-
-
-class ValidTestGenerator(base.BasicGeneratorSet):
-    @base.generator_type("string")
-    @base.simple_generator
-    def generate_valid_string(self, schema):
-        size = schema.get("minLength", 1)
-        # TODO(dkr mko): handle format and pattern
-        return "x" * size
-
-    @base.generator_type("integer")
-    @base.simple_generator
-    def generate_valid_integer(self, schema):
-        # TODO(dkr mko): handle multipleOf
-        if "minimum" in schema:
-            minimum = schema["minimum"]
-            if "exclusiveMinimum" not in schema:
-                return minimum
-            else:
-                return minimum + 1
-        if "maximum" in schema:
-            maximum = schema["maximum"]
-            if "exclusiveMaximum" not in schema:
-                return maximum
-            else:
-                return maximum - 1
-        return 0
-
-    @base.generator_type("object")
-    @base.simple_generator
-    def generate_valid_object(self, schema):
-        obj = {}
-        for k, v in six.iteritems(schema["properties"]):
-            obj[k] = self.generate_valid(v)
-        return obj
-
-    def generate(self, schema):
-        schema_type = schema["type"]
-        if isinstance(schema_type, list):
-            if "integer" in schema_type:
-                schema_type = "integer"
-            else:
-                raise Exception("non-integer list types not supported")
-        result = []
-        if schema_type not in self.types_dict:
-            raise TypeError("generator (%s) doesn't support type: %s"
-                            % (self.__class__.__name__, schema_type))
-        for generator in self.types_dict[schema_type]:
-            ret = generator(schema)
-            if ret is not None:
-                if isinstance(ret, list):
-                    result.extend(ret)
-                elif isinstance(ret, tuple):
-                    result.append(ret)
-                else:
-                    raise Exception("generator (%s) returns invalid result: %s"
-                                    % (generator, ret))
-        return result
-
-    def generate_valid(self, schema):
-        return self.generate(schema)[0][1]
diff --git a/tempest/common/preprov_creds.py b/tempest/common/preprov_creds.py
index 5e23696..3f68ae8 100644
--- a/tempest/common/preprov_creds.py
+++ b/tempest/common/preprov_creds.py
@@ -304,7 +304,9 @@
         if exist_creds and not force_new:
             return exist_creds
         elif exist_creds and force_new:
-            new_index = six.text_type(roles).encode('utf-8') + '-' + \
+            # NOTE(andreaf) In py3.x encode returns bytes, and b'' is bytes
+            # In py2.7 encode returns strings, and b'' is still string
+            new_index = six.text_type(roles).encode('utf-8') + b'-' + \
                 six.text_type(len(self._creds)).encode('utf-8')
             self._creds[new_index] = exist_creds
         net_creds = self._get_creds(roles=roles)
diff --git a/tempest/config.py b/tempest/config.py
index 70ede55..47eb427 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. "
@@ -1070,14 +1075,6 @@
                     "step in Node cleaning.")
 ]
 
-negative_group = cfg.OptGroup(name='negative', title="Negative Test Options")
-
-NegativeGroup = [
-    cfg.StrOpt('test_generator',
-               default='tempest.common.' +
-               'generator.negative_generator.NegativeTestGenerator',
-               help="Test generator class for all negative tests"),
-]
 
 DefaultGroup = [
     cfg.StrOpt('resources_prefix',
@@ -1110,7 +1107,6 @@
     (debug_group, DebugGroup),
     (baremetal_group, BaremetalGroup),
     (input_scenario_group, InputScenarioGroup),
-    (negative_group, NegativeGroup),
     (None, DefaultGroup)
 ]
 
@@ -1174,7 +1170,6 @@
         self.debug = _CONF.debug
         self.baremetal = _CONF.baremetal
         self.input_scenario = _CONF['input-scenario']
-        self.negative = _CONF.negative
         logging.tempest_set_log_file('tempest.log')
 
     def __init__(self, parse_conf=True, config_path=None):
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/cmd/check_uuid.py b/tempest/lib/cmd/check_uuid.py
index 1239ac5..88ce775 100755
--- a/tempest/lib/cmd/check_uuid.py
+++ b/tempest/lib/cmd/check_uuid.py
@@ -23,6 +23,7 @@
 import unittest
 import uuid
 
+from oslo_utils import uuidutils
 import six.moves.urllib.parse as urlparse
 
 DECORATOR_MODULE = 'test'
@@ -61,7 +62,7 @@
         if filename not in self.source_files:
             with open(filename) as f:
                 self.source_files[filename] = self._quote(f.read())
-        patch_id = str(uuid.uuid4())
+        patch_id = uuidutils.generate_uuid()
         if not patch.endswith('\n'):
             patch += '\n'
         self.patches[patch_id] = self._quote(patch)
diff --git a/tempest/lib/common/cred_client.py b/tempest/lib/common/cred_client.py
index ad968f1..3f10dee 100644
--- a/tempest/lib/common/cred_client.py
+++ b/tempest/lib/common/cred_client.py
@@ -140,7 +140,7 @@
             # Domain names must be unique, in any case a list is returned,
             # selecting the first (and only) element
             self.creds_domain = self.domains_client.list_domains(
-                params={'name': domain_name})['domains'][0]
+                name=domain_name)['domains'][0]
         except lib_exc.NotFound:
             # TODO(andrea) we could probably create the domain on the fly
             msg = "Requested domain %s could not be found" % domain_name
diff --git a/tempest/lib/common/utils/data_utils.py b/tempest/lib/common/utils/data_utils.py
index 4095c77..75c2e51 100644
--- a/tempest/lib/common/utils/data_utils.py
+++ b/tempest/lib/common/utils/data_utils.py
@@ -21,6 +21,7 @@
 
 from debtcollector import removals
 from oslo_utils import netutils
+from oslo_utils import uuidutils
 import six.moves
 
 
@@ -30,7 +31,7 @@
     :return: a random UUID (e.g. '1dc12c7d-60eb-4b61-a7a2-17cf210155b6')
     :rtype: string
     """
-    return str(uuid.uuid4())
+    return uuidutils.generate_uuid()
 
 
 def rand_uuid_hex():
diff --git a/tempest/lib/exceptions.py b/tempest/lib/exceptions.py
index a6c01bb..108ba70 100644
--- a/tempest/lib/exceptions.py
+++ b/tempest/lib/exceptions.py
@@ -71,54 +71,70 @@
     message = "The success code is different than the expected one"
 
 
-class NotFound(ClientRestClientException):
-    message = "Object not found"
+class BadRequest(ClientRestClientException):
+    status_code = 400
+    message = "Bad request"
 
 
 class Unauthorized(ClientRestClientException):
+    status_code = 401
     message = 'Unauthorized'
 
 
 class Forbidden(ClientRestClientException):
+    status_code = 403
     message = "Forbidden"
 
 
-class TimeoutException(OtherRestClientException):
-    message = "Request timed out"
-
-
-class BadRequest(ClientRestClientException):
-    message = "Bad request"
-
-
-class UnprocessableEntity(ClientRestClientException):
-    message = "Unprocessable entity"
-
-
-class RateLimitExceeded(ClientRestClientException):
-    message = "Rate limit exceeded"
-
-
-class OverLimit(ClientRestClientException):
-    message = "Request entity is too large"
-
-
-class ServerFault(ServerRestClientException):
-    message = "Got server fault"
-
-
-class NotImplemented(ServerRestClientException):
-    message = "Got NotImplemented error"
+class NotFound(ClientRestClientException):
+    status_code = 404
+    message = "Object not found"
 
 
 class Conflict(ClientRestClientException):
+    status_code = 409
     message = "An object with that identifier already exists"
 
 
 class Gone(ClientRestClientException):
+    status_code = 410
     message = "The requested resource is no longer available"
 
 
+class RateLimitExceeded(ClientRestClientException):
+    status_code = 413
+    message = "Rate limit exceeded"
+
+
+class OverLimit(ClientRestClientException):
+    status_code = 413
+    message = "Request entity is too large"
+
+
+class InvalidContentType(ClientRestClientException):
+    status_code = 415
+    message = "Invalid content type provided"
+
+
+class UnprocessableEntity(ClientRestClientException):
+    status_code = 422
+    message = "Unprocessable entity"
+
+
+class ServerFault(ServerRestClientException):
+    status_code = 500
+    message = "Got server fault"
+
+
+class NotImplemented(ServerRestClientException):
+    status_code = 501
+    message = "Got NotImplemented error"
+
+
+class TimeoutException(OtherRestClientException):
+    message = "Request timed out"
+
+
 class ResponseWithNonEmptyBody(OtherRestClientException):
     message = ("RFC Violation! Response with %(status)d HTTP Status Code "
                "MUST NOT have a body")
@@ -137,10 +153,6 @@
     message = "HTTP response header is invalid"
 
 
-class InvalidContentType(ClientRestClientException):
-    message = "Invalid content type provided"
-
-
 class UnexpectedContentType(OtherRestClientException):
     message = "Unexpected content type provided"
 
diff --git a/tempest/lib/services/network/__init__.py b/tempest/lib/services/network/__init__.py
index c466f07..19e5463 100644
--- a/tempest/lib/services/network/__init__.py
+++ b/tempest/lib/services/network/__init__.py
@@ -27,6 +27,8 @@
     SecurityGroupRulesClient
 from tempest.lib.services.network.security_groups_client import \
     SecurityGroupsClient
+from tempest.lib.services.network.service_providers_client import \
+    ServiceProvidersClient
 from tempest.lib.services.network.subnetpools_client import SubnetpoolsClient
 from tempest.lib.services.network.subnets_client import SubnetsClient
 from tempest.lib.services.network.versions_client import NetworkVersionsClient
@@ -35,4 +37,5 @@
            'MeteringLabelRulesClient', 'MeteringLabelsClient',
            'NetworksClient', 'PortsClient', 'QuotasClient', 'RoutersClient',
            'SecurityGroupRulesClient', 'SecurityGroupsClient',
-           'SubnetpoolsClient', 'SubnetsClient', 'NetworkVersionsClient']
+           'ServiceProvidersClient', 'SubnetpoolsClient', 'SubnetsClient',
+           'NetworkVersionsClient']
diff --git a/tempest/lib/services/network/service_providers_client.py b/tempest/lib/services/network/service_providers_client.py
new file mode 100644
index 0000000..0ee9bc3
--- /dev/null
+++ b/tempest/lib/services/network/service_providers_client.py
@@ -0,0 +1,21 @@
+#    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.network import base
+
+
+class ServiceProvidersClient(base.BaseNetworkClient):
+
+    def list_service_providers(self, **filters):
+        """Lists service providers."""
+        uri = '/service-providers'
+        return self.list_resources(uri, **filters)
diff --git a/tempest/lib/services/volume/v2/__init__.py b/tempest/lib/services/volume/v2/__init__.py
index eaaafa5..837b4f6 100644
--- a/tempest/lib/services/volume/v2/__init__.py
+++ b/tempest/lib/services/volume/v2/__init__.py
@@ -15,6 +15,8 @@
 from tempest.lib.services.volume.v2.availability_zone_client \
     import AvailabilityZoneClient
 from tempest.lib.services.volume.v2.backups_client import BackupsClient
+from tempest.lib.services.volume.v2.capabilities_client import \
+    CapabilitiesClient
 from tempest.lib.services.volume.v2.encryption_types_client import \
     EncryptionTypesClient
 from tempest.lib.services.volume.v2.extensions_client import ExtensionsClient
@@ -22,6 +24,8 @@
 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.scheduler_stats_client import \
+    SchedulerStatsClient
 from tempest.lib.services.volume.v2.services_client import ServicesClient
 from tempest.lib.services.volume.v2.snapshots_client import SnapshotsClient
 from tempest.lib.services.volume.v2.types_client import TypesClient
@@ -30,4 +34,4 @@
 __all__ = ['AvailabilityZoneClient', 'BackupsClient', 'EncryptionTypesClient',
            'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
            'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
-           'LimitsClient']
+           'LimitsClient', 'CapabilitiesClient', 'SchedulerStatsClient']
diff --git a/tempest/lib/services/volume/v2/capabilities_client.py b/tempest/lib/services/volume/v2/capabilities_client.py
new file mode 100644
index 0000000..b6de5b9
--- /dev/null
+++ b/tempest/lib/services/volume/v2/capabilities_client.py
@@ -0,0 +1,34 @@
+# 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 CapabilitiesClient(rest_client.RestClient):
+
+    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
+        """
+        url = 'capabilities/%s' % host
+        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/scheduler_stats_client.py b/tempest/lib/services/volume/v2/scheduler_stats_client.py
new file mode 100644
index 0000000..637254b
--- /dev/null
+++ b/tempest/lib/services/volume/v2/scheduler_stats_client.py
@@ -0,0 +1,35 @@
+# 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 SchedulerStatsClient(rest_client.RestClient):
+
+    def list_pools(self, detail=False):
+        """List all the volumes pools (hosts).
+
+        Output params: see http://developer.openstack.org/
+                           api-ref-blockstorage-v2.html#listPools
+        """
+        url = 'scheduler-stats/get_pools'
+        if detail:
+            url += '?detail=True'
+        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/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index ce97adb..8b8e249 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from debtcollector import removals
 from oslo_serialization import jsonutils as json
 import six
 from six.moves.urllib import parse as urllib
@@ -323,6 +324,8 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    @removals.remove(message="use show_pools from tempest.lib.services."
+                             "volume.v2.scheduler_stats_client")
     def show_pools(self, detail=False):
         # List all the volumes pools (hosts)
         url = 'scheduler-stats/get_pools'
@@ -334,6 +337,8 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    @removals.remove(message="use show_backend_capabilities from tempest.lib."
+                             "services.volume.v2.capabilities_client")
     def show_backend_capabilities(self, host):
         """Shows capabilities for a storage back end.
 
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_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 32f5d9f..1360b09 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -15,7 +15,6 @@
 from oslo_log import log
 import testtools
 
-from tempest import clients
 from tempest.common.utils import data_utils
 from tempest.common.utils import net_info
 from tempest import config
@@ -113,9 +112,9 @@
             access point
         """
 
-        def __init__(self, credentials):
-            self.manager = clients.Manager(credentials)
+        def __init__(self, clients):
             # Credentials from manager are filled with both names and IDs
+            self.manager = clients
             self.creds = self.manager.credentials
             self.network = None
             self.subnet = None
@@ -152,11 +151,6 @@
         # Create no network resources for these tests.
         cls.set_network_resources()
         super(TestSecurityGroupsBasicOps, cls).setup_credentials()
-        # TODO(mnewby) Consider looking up entities as needed instead
-        # of storing them as collections on the class.
-
-        # Credentials from the manager are filled with both IDs and Names
-        cls.alt_creds = cls.alt_manager.credentials
 
     @classmethod
     def resource_setup(cls):
@@ -171,9 +165,8 @@
 
         cls.floating_ips = {}
         cls.tenants = {}
-        creds = cls.manager.credentials
-        cls.primary_tenant = cls.TenantProperties(creds)
-        cls.alt_tenant = cls.TenantProperties(cls.alt_creds)
+        cls.primary_tenant = cls.TenantProperties(cls.os)
+        cls.alt_tenant = cls.TenantProperties(cls.os_alt)
         for tenant in [cls.primary_tenant, cls.alt_tenant]:
             cls.tenants[tenant.creds.tenant_id] = tenant
 
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 3aab2b8..2d2f7df 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -18,8 +18,8 @@
 
 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_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index db5e009..46aebfe 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -148,10 +148,13 @@
 
         # create a 3rd instance from snapshot
         LOG.info("Creating third instance from snapshot: %s" % snapshot['id'])
-        volume = self.create_volume(snapshot_id=snapshot['id'])
+        volume = self.create_volume(snapshot_id=snapshot['id'],
+                                    size=snapshot['size'])
+        LOG.info("Booting third instance from snapshot")
         server_from_snapshot = (
             self._boot_instance_from_volume(volume['id'],
                                             keypair, security_group))
+        LOG.info("Booted third instance %s", server_from_snapshot)
 
         # check the content of written file
         LOG.info("Logging into third instance to get timestamp: %s" %
diff --git a/tempest/services/identity/v3/json/domains_client.py b/tempest/services/identity/v3/json/domains_client.py
index fe929a5..43cb62c 100644
--- a/tempest/services/identity/v3/json/domains_client.py
+++ b/tempest/services/identity/v3/json/domains_client.py
@@ -21,29 +21,36 @@
 class DomainsClient(rest_client.RestClient):
     api_version = "v3"
 
-    def create_domain(self, name, **kwargs):
-        """Creates a domain."""
-        description = kwargs.get('description', None)
-        en = kwargs.get('enabled', True)
-        post_body = {
-            'description': description,
-            'enabled': en,
-            'name': name
-        }
-        post_body = json.dumps({'domain': post_body})
+    def create_domain(self, **kwargs):
+        """Creates a domain.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#create-domain
+        """
+        post_body = json.dumps({'domain': kwargs})
         resp, body = self.post('domains', post_body)
         self.expected_success(201, resp.status)
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_domain(self, domain_id):
-        """Deletes a domain."""
+        """Deletes a domain.
+
+        For APi details, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#delete-domain
+        """
         resp, body = self.delete('domains/%s' % domain_id)
         self.expected_success(204, resp.status)
         return rest_client.ResponseBody(resp, body)
 
-    def list_domains(self, params=None):
-        """List Domains."""
+    def list_domains(self, **params):
+        """List Domains.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#list-domains
+        """
         url = 'domains'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -53,24 +60,24 @@
         return rest_client.ResponseBody(resp, body)
 
     def update_domain(self, domain_id, **kwargs):
-        """Updates a domain."""
-        body = self.show_domain(domain_id)['domain']
-        description = kwargs.get('description', body['description'])
-        en = kwargs.get('enabled', body['enabled'])
-        name = kwargs.get('name', body['name'])
-        post_body = {
-            'description': description,
-            'enabled': en,
-            'name': name
-        }
-        post_body = json.dumps({'domain': post_body})
+        """Updates a domain.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#update-domain
+        """
+        post_body = json.dumps({'domain': kwargs})
         resp, body = self.patch('domains/%s' % domain_id, post_body)
         self.expected_success(200, resp.status)
         body = json.loads(body)
         return rest_client.ResponseBody(resp, body)
 
     def show_domain(self, domain_id):
-        """Get Domain details."""
+        """Get Domain details.
+
+        For API details, please refer to the official API reference:
+        http://developer.openstack.org/api-ref/identity/v3/index.html#show-domain-details
+        """
         resp, body = self.get('domains/%s' % domain_id)
         self.expected_success(200, resp.status)
         body = json.loads(body)
diff --git a/tempest/services/object_storage/__init__.py b/tempest/services/object_storage/__init__.py
index 96fe4a3..d1a61d6 100644
--- a/tempest/services/object_storage/__init__.py
+++ b/tempest/services/object_storage/__init__.py
@@ -13,7 +13,10 @@
 # the License.
 
 from tempest.services.object_storage.account_client import AccountClient
+from tempest.services.object_storage.capabilities_client import \
+    CapabilitiesClient
 from tempest.services.object_storage.container_client import ContainerClient
 from tempest.services.object_storage.object_client import ObjectClient
 
-__all__ = ['AccountClient', 'ContainerClient', 'ObjectClient']
+__all__ = ['AccountClient', 'CapabilitiesClient', 'ContainerClient',
+           'ObjectClient']
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index 6012a92..9932b4a 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -144,13 +144,3 @@
             body = body.strip().splitlines()
         self.expected_success([200, 204], resp.status)
         return resp, body
-
-    def list_extensions(self):
-        self.skip_path()
-        try:
-            resp, body = self.get('info')
-        finally:
-            self.reset_path()
-        body = json.loads(body)
-        self.expected_success(200, resp.status)
-        return resp, body
diff --git a/tempest/services/object_storage/capabilities_client.py b/tempest/services/object_storage/capabilities_client.py
new file mode 100644
index 0000000..0fe437f
--- /dev/null
+++ b/tempest/services/object_storage/capabilities_client.py
@@ -0,0 +1,31 @@
+# Copyright 2012 OpenStack Foundation
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class CapabilitiesClient(rest_client.RestClient):
+
+    def list_capabilities(self):
+        self.skip_path()
+        try:
+            resp, body = self.get('info')
+        finally:
+            self.reset_path()
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return resp, body
diff --git a/tempest/test.py b/tempest/test.py
index 93fbed3..51f0a6a 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -141,6 +141,28 @@
     return False
 
 
+def related_bug(bug, status_code=None):
+    """A decorator useful to know solutions from launchpad bug reports
+
+    @param bug: The launchpad bug number causing the test
+    @param status_code: The status code related to the bug report
+    """
+    def decorator(f):
+        @functools.wraps(f)
+        def wrapper(self, *func_args, **func_kwargs):
+            try:
+                return f(self, *func_args, **func_kwargs)
+            except Exception as exc:
+                exc_status_code = getattr(exc, 'status_code', None)
+                if status_code is None or status_code == exc_status_code:
+                    LOG.error('Hints: This test was made for the bug %s. '
+                              'The failure could be related to '
+                              'https://launchpad.net/bugs/%s' % (bug, bug))
+                raise exc
+        return wrapper
+    return decorator
+
+
 def is_scheduler_filter_enabled(filter_name):
     """Check the list of enabled compute scheduler filters from config. """
 
@@ -504,8 +526,12 @@
             else:
                 raise lib_exc.InvalidCredentials(
                     "Invalid credentials type %s" % credential_type)
-        return cls.client_manager(credentials=creds.credentials,
-                                  service=cls._service)
+        manager = cls.client_manager(credentials=creds.credentials,
+                                     service=cls._service)
+        # NOTE(andreaf) Ensure credentials have user and project id fields.
+        # It may not be the case when using pre-provisioned credentials.
+        manager.auth_provider.set_auth()
+        return manager
 
     @classmethod
     def clear_credentials(cls):
@@ -532,18 +558,17 @@
         """
         if not CONF.validation.run_validation:
             return
+
         if keypair is None:
-            if CONF.validation.auth_method.lower() == "keypair":
-                keypair = True
-            else:
-                keypair = False
+            keypair = (CONF.validation.auth_method.lower() == "keypair")
+
         if floating_ip is None:
-            if CONF.validation.connect_method.lower() == "floating":
-                floating_ip = True
-            else:
-                floating_ip = False
+            floating_ip = (CONF.validation.connect_method.lower() ==
+                           "floating")
+
         if security_group is None:
             security_group = CONF.validation.security_group
+
         if security_group_rules is None:
             security_group_rules = CONF.validation.security_group_rules
 
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 00b4542..1af0d95 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -401,7 +401,7 @@
                            'not_fake': 'metadata',
                            'swift': 'metadata'})
         fake_os = mock.MagicMock()
-        fake_os.account_client.list_extensions = fake_list_extensions
+        fake_os.capabilities_client.list_capabilities = fake_list_extensions
         self.useFixture(mockpatch.PatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['fake1', 'fake2', 'fake3'])))
@@ -423,7 +423,7 @@
                            'not_fake': 'metadata',
                            'swift': 'metadata'})
         fake_os = mock.MagicMock()
-        fake_os.account_client.list_extensions = fake_list_extensions
+        fake_os.capabilities_client.list_capabilities = fake_list_extensions
         self.useFixture(mockpatch.PatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['all'])))
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/network/test_service_providers_client.py b/tempest/tests/lib/services/network/test_service_providers_client.py
new file mode 100644
index 0000000..ae11ef0
--- /dev/null
+++ b/tempest/tests/lib/services/network/test_service_providers_client.py
@@ -0,0 +1,37 @@
+#    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.network import service_providers_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestServiceProvidersClient(base.BaseServiceTest):
+    def setUp(self):
+        super(TestServiceProvidersClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = service_providers_client.ServiceProvidersClient(
+            fake_auth, 'network', 'regionOne')
+
+    def _test_list_service_providers(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_service_providers,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {"service_providers": []},
+            bytes_body)
+
+    def test_list_service_providers_with_str_body(self):
+        self._test_list_service_providers()
+
+    def test_list_service_providers_with_bytes_body(self):
+        self._test_list_service_providers(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v3/test_user_messages.py b/tempest/tests/lib/services/volume/v3/test_user_messages_client.py
similarity index 100%
rename from tempest/tests/lib/services/volume/v3/test_user_messages.py
rename to tempest/tests/lib/services/volume/v3/test_user_messages_client.py
diff --git a/tempest/tests/lib/test_auth.py b/tempest/tests/lib/test_auth.py
index 6da7e41..2f975d2 100644
--- a/tempest/tests/lib/test_auth.py
+++ b/tempest/tests/lib/test_auth.py
@@ -458,6 +458,56 @@
         expected = 'http://fake_url/v2.0'
         self._test_base_url_helper(expected, filters, ('token', auth_data))
 
+    def test_base_url_with_extra_path_endpoint(self):
+        auth_data = {
+            'serviceCatalog': [
+                {
+                    'type': 'compute',
+                    'endpoints': [
+                        {
+                            'region': 'FakeRegion',
+                            'publicURL': 'http://fake_url/some_path/v2.0'
+                        }
+                    ]
+                }
+            ]
+        }
+
+        filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'api_version': 'v2.0'
+        }
+
+        expected = 'http://fake_url/some_path/v2.0'
+        self._test_base_url_helper(expected, filters, ('token', auth_data))
+
+    def test_base_url_with_unversioned_extra_path_endpoint(self):
+        auth_data = {
+            'serviceCatalog': [
+                {
+                    'type': 'compute',
+                    'endpoints': [
+                        {
+                            'region': 'FakeRegion',
+                            'publicURL': 'http://fake_url/some_path'
+                        }
+                    ]
+                }
+            ]
+        }
+
+        filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'api_version': 'v2.0'
+        }
+
+        expected = 'http://fake_url/some_path/v2.0'
+        self._test_base_url_helper(expected, filters, ('token', auth_data))
+
     def test_token_not_expired(self):
         expiry_data = datetime.datetime.utcnow() + datetime.timedelta(days=1)
         self._verify_expiry(expiry_data=expiry_data, should_be_expired=False)
@@ -592,6 +642,58 @@
         expected = 'http://fake_url/v3'
         self._test_base_url_helper(expected, filters, ('token', auth_data))
 
+    def test_base_url_with_extra_path_endpoint(self):
+        auth_data = {
+            'catalog': [
+                {
+                    'type': 'compute',
+                    'endpoints': [
+                        {
+                            'region': 'FakeRegion',
+                            'url': 'http://fake_url/some_path/v2.0',
+                            'interface': 'public'
+                        }
+                    ]
+                }
+            ]
+        }
+
+        filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'api_version': 'v2.0'
+        }
+
+        expected = 'http://fake_url/some_path/v2.0'
+        self._test_base_url_helper(expected, filters, ('token', auth_data))
+
+    def test_base_url_with_unversioned_extra_path_endpoint(self):
+        auth_data = {
+            'catalog': [
+                {
+                    'type': 'compute',
+                    'endpoints': [
+                        {
+                            'region': 'FakeRegion',
+                            'url': 'http://fake_url/some_path',
+                            'interface': 'public'
+                        }
+                    ]
+                }
+            ]
+        }
+
+        filters = {
+            'service': 'compute',
+            'endpoint_type': 'publicURL',
+            'region': 'FakeRegion',
+            'api_version': 'v2.0'
+        }
+
+        expected = 'http://fake_url/some_path/v2.0'
+        self._test_base_url_helper(expected, filters, ('token', auth_data))
+
     # Base URL test with scope only for V3
     def test_base_url_scope_project(self):
         self.auth_provider.scope = 'project'
diff --git a/tempest/tests/negative/test_negative_generators.py b/tempest/tests/negative/test_negative_generators.py
deleted file mode 100644
index 7e1ee2c..0000000
--- a/tempest/tests/negative/test_negative_generators.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# Copyright 2014 Deutsche Telekom AG
-# 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 copy
-
-import jsonschema
-import mock
-import six
-
-from tempest.common.generator import base_generator
-from tempest.common.generator import negative_generator
-from tempest.common.generator import valid_generator
-from tempest.tests import base
-
-
-class TestNegativeBasicGenerator(base.TestCase):
-    valid_desc = {
-        "name": "list-flavors-with-detail",
-        "http-method": "GET",
-        "url": "flavors/detail",
-        "json-schema": {
-            "type": "object",
-            "properties": {
-                "minRam": {"type": "integer"},
-                "minDisk": {"type": "integer"}
-            }
-        },
-        "resources": ["flavor", "volume", "image"]
-    }
-
-    minimal_desc = {
-        "name": "list-flavors-with-detail",
-        "http-method": "GET",
-        "url": "flavors/detail",
-    }
-
-    add_prop_desc = {
-        "name": "list-flavors-with-detail",
-        "http-method": "GET",
-        "url": "flavors/detail",
-        "unknown_field": [12]
-    }
-
-    invalid_json_schema_desc = {
-        "name": "list-flavors-with-detail",
-        "http-method": "GET",
-        "url": "flavors/detail",
-        "json-schema": {"type": "NotExistingType"}
-    }
-
-    def setUp(self):
-        super(TestNegativeBasicGenerator, self).setUp()
-        self.generator = base_generator.BasicGeneratorSet()
-
-    def _assert_valid_jsonschema_call(self, jsonschema_mock, desc):
-        self.assertEqual(jsonschema_mock.call_count, 1)
-        jsonschema_mock.assert_called_with(desc, self.generator.schema)
-
-    @mock.patch('jsonschema.validate', wraps=jsonschema.validate)
-    def test_validate_schema_with_valid_input(self, jsonschema_mock):
-        self.generator.validate_schema(self.valid_desc)
-        self._assert_valid_jsonschema_call(jsonschema_mock, self.valid_desc)
-
-    @mock.patch('jsonschema.validate', wraps=jsonschema.validate)
-    def test_validate_schema_with_minimal_input(self, jsonschema_mock):
-        self.generator.validate_schema(self.minimal_desc)
-        self._assert_valid_jsonschema_call(jsonschema_mock, self.minimal_desc)
-
-    def test_validate_schema_with_invalid_input(self):
-        self.assertRaises(jsonschema.ValidationError,
-                          self.generator.validate_schema, self.add_prop_desc)
-        self.assertRaises(jsonschema.SchemaError,
-                          self.generator.validate_schema,
-                          self.invalid_json_schema_desc)
-
-
-class BaseNegativeGenerator(object):
-    types = ['string', 'integer', 'object']
-
-    fake_input_obj = {"type": "object",
-                      "properties": {"minRam": {"type": "integer"},
-                                     "diskName": {"type": "string"},
-                                     "maxRam": {"type": "integer", }
-                                     }
-                      }
-
-    unknown_type_schema = {
-        "type": "not_defined"
-    }
-
-    class fake_test_class(object):
-        def __init__(self, scenario):
-            for k, v in six.iteritems(scenario):
-                setattr(self, k, v)
-
-    def _validate_result(self, valid_schema, invalid_schema):
-        for k, v in six.iteritems(valid_schema):
-            self.assertIn(k, invalid_schema)
-
-    def test_generator_mandatory_functions(self):
-        for data_type in self.types:
-            self.assertIn(data_type, self.generator.types_dict)
-
-    def test_generate_with_unknown_type(self):
-        self.assertRaises(TypeError, self.generator.generate_payload,
-                          self.unknown_type_schema)
-
-
-class TestNegativeValidGenerator(base.TestCase, BaseNegativeGenerator):
-    def setUp(self):
-        super(TestNegativeValidGenerator, self).setUp()
-        self.generator = valid_generator.ValidTestGenerator()
-
-    def test_generate_valid(self):
-        result = self.generator.generate_valid(self.fake_input_obj)
-        self.assertIn("minRam", result)
-        self.assertIsInstance(result["minRam"], int)
-        self.assertIn("diskName", result)
-        self.assertIsInstance(result["diskName"], str)
-
-
-class TestNegativeNegativeGenerator(base.TestCase, BaseNegativeGenerator):
-    def setUp(self):
-        super(TestNegativeNegativeGenerator, self).setUp()
-        self.generator = negative_generator.NegativeTestGenerator()
-
-    def test_generate_obj(self):
-        schema = self.fake_input_obj
-        scenarios = self.generator.generate_scenarios(schema)
-        for scenario in scenarios:
-            test = self.fake_test_class(scenario)
-            valid_schema = \
-                valid_generator.ValidTestGenerator().generate_valid(schema)
-            schema_under_test = copy.copy(valid_schema)
-            expected_result = \
-                self.generator.generate_payload(test, schema_under_test)
-            self.assertIsNone(expected_result)
-            self._validate_result(valid_schema, schema_under_test)
diff --git a/tox.ini b/tox.ini
index 82dba92..7a36e84 100644
--- a/tox.ini
+++ b/tox.ini
@@ -143,10 +143,10 @@
 # E125 is a won't fix until https://github.com/jcrocholl/pep8/issues/126 is resolved.  For further detail see https://review.openstack.org/#/c/36788/
 # E123 skipped because it is ignored by default in the default pep8
 # E129 skipped because it is too limiting when combined with other rules
-# Skipped because of new hacking 0.9: H405
 ignore = E125,E123,E129
 show-source = True
 exclude = .git,.venv,.tox,dist,doc,*egg
+enable-extensions = H106,H203
 
 [testenv:releasenotes]
 commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
@@ -161,3 +161,12 @@
 commands=
     pip-extra-reqs -d --ignore-file=tempest/tests/* tempest
     pip-missing-reqs -d --ignore-file=tempest/tests/* tempest
+
+
+[testenv:bindep]
+# Do not install any requirements. We want this to be fast and work even if
+# system dependencies are missing, since it's used to tell you what system
+# dependencies are missing! This also means that bindep must be installed
+# separately, outside of the requirements files.
+deps = bindep
+commands = bindep test