Merge "Add compute test for ServerGroupAntiAffinityFilter"
diff --git a/HACKING.rst b/HACKING.rst
index e5f45ac..446d865 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -24,6 +24,7 @@
 - [T114] Check that tempest.lib does not use tempest config
 - [T115] Check that admin tests should exist under admin path
 - [N322] Method's default argument shouldn't be mutable
+- [T116] Unsupported 'message' Exception attribute in PY3
 
 Test Data/Configuration
 -----------------------
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 4e7a72d..0cfdf34 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -25,8 +25,6 @@
 import os
 import subprocess
 
-import openstackdocstheme
-
 # Build the plugin registry
 def build_plugin_registry(app):
     root_dir = os.path.dirname(
diff --git a/doc/source/library/credential_providers.rst b/doc/source/library/credential_providers.rst
index 7e831cc..f4eb37d 100644
--- a/doc/source/library/credential_providers.rst
+++ b/doc/source/library/credential_providers.rst
@@ -6,7 +6,7 @@
 These library interfaces are used to deal with allocating credentials on demand
 either dynamically by calling keystone to allocate new credentials, or from
 a list of preprovisioned credentials. These 2 modules are implementations of
-the same abstract credential providers class and can be used interchangably.
+the same abstract credential providers class and can be used interchangeably.
 However, each implementation has some additional parameters that are used to
 influence the behavior of the modules. The API reference at the bottom of this
 doc shows the interface definitions for both modules, however that may be a bit
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index 336aa5b..d80081d 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -330,13 +330,9 @@
 
  .. _2.47: http://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id42
 
- * `2.52`_
+ * `2.48`_
 
- .. _2.52: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id47
-
- * `2.53`_
-
- .. _2.53: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id48
+ .. _2.48: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id43
 
 * Volume
 
diff --git a/doc/source/plugin.rst b/doc/source/plugin.rst
index b3af92f..77ef9ed 100644
--- a/doc/source/plugin.rst
+++ b/doc/source/plugin.rst
@@ -28,6 +28,7 @@
 * tempest.lib.*
 * tempest.config
 * tempest.test_discover.plugins
+* tempest.common.credentials_factory
 
 If there is an interface from tempest that you need to rely on in your plugin
 which is not listed above, it likely needs to be migrated to tempest.lib. In
diff --git a/releasenotes/notes/add-create-group-from-src-tempest-tests-9eb8b0b4b5c52055.yaml b/releasenotes/notes/add-create-group-from-src-tempest-tests-9eb8b0b4b5c52055.yaml
new file mode 100644
index 0000000..dec4a27
--- /dev/null
+++ b/releasenotes/notes/add-create-group-from-src-tempest-tests-9eb8b0b4b5c52055.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Add create_group_from_source to groups_client in the volume service library.
diff --git a/tempest/releasenotes/notes/add-return-value-to-retype-volume-a401aa619aaa2457.yaml b/releasenotes/notes/add-return-value-to-retype-volume-a401aa619aaa2457.yaml
similarity index 100%
rename from tempest/releasenotes/notes/add-return-value-to-retype-volume-a401aa619aaa2457.yaml
rename to releasenotes/notes/add-return-value-to-retype-volume-a401aa619aaa2457.yaml
diff --git a/releasenotes/notes/add-show-volume-image-metadata-api-to-v2-volumes-client-ee3c027f35276561.yaml b/releasenotes/notes/add-show-volume-image-metadata-api-to-v2-volumes-client-ee3c027f35276561.yaml
new file mode 100644
index 0000000..ac7c74e
--- /dev/null
+++ b/releasenotes/notes/add-show-volume-image-metadata-api-to-v2-volumes-client-ee3c027f35276561.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Add show volume image metadata API to v2 volumes_client library.
+    This feature enables the possibility to show volume's image metadata.
diff --git a/releasenotes/notes/add-update-group-tempest-tests-72f8ec19b2809849.yaml b/releasenotes/notes/add-update-group-tempest-tests-72f8ec19b2809849.yaml
new file mode 100644
index 0000000..23c30af
--- /dev/null
+++ b/releasenotes/notes/add-update-group-tempest-tests-72f8ec19b2809849.yaml
@@ -0,0 +1,4 @@
+---
+features:
+  - |
+    Add update_group to groups_client in the volume service library.
diff --git a/releasenotes/notes/credentials-factory-stable-c8037bd9ae642482.yaml b/releasenotes/notes/credentials-factory-stable-c8037bd9ae642482.yaml
new file mode 100644
index 0000000..6faa536
--- /dev/null
+++ b/releasenotes/notes/credentials-factory-stable-c8037bd9ae642482.yaml
@@ -0,0 +1,10 @@
+---
+features:
+  - |
+    The credentials_factory.py module is now marked as stable for Tempest
+    plugins. It provides helpers that can be used by Tempest plugins to
+    obtain test credentials for their test cases in a format that honors the
+    Tempest configuration in use.
+    Credentials may be provisioned on the fly during the test run, or they
+    can be setup in advance and fed to test via a YAML file; they can be
+    setup for identity v2 or identity v3.
diff --git a/releasenotes/notes/plugin-client-registration-enhancements-e09131742391225b.yaml b/releasenotes/notes/plugin-client-registration-enhancements-e09131742391225b.yaml
new file mode 100644
index 0000000..b6391b6
--- /dev/null
+++ b/releasenotes/notes/plugin-client-registration-enhancements-e09131742391225b.yaml
@@ -0,0 +1,12 @@
+---
+features:
+  - |
+    When registering service clients from installed plugins, all registrations
+    are now processed, even if one or more fails. All exceptions encountered
+    during the registration process are recorded.  If at least one exception
+    was encountered, the registration process fails and all interim errors are
+    reported.
+  - |
+    The __repr__ method is now implemented for the base `tempest.Exception`
+    class, its implementation is identical to __str__: it reports the error
+    message merged with input parameters.
diff --git a/tempest/api/compute/admin/test_agents.py b/tempest/api/compute/admin/test_agents.py
index 69cbfb5..0901374 100644
--- a/tempest/api/compute/admin/test_agents.py
+++ b/tempest/api/compute/admin/test_agents.py
@@ -86,7 +86,7 @@
         body = self.client.create_agent(**self.params_agent)['agent']
         self.addCleanup(self.client.delete_agent, body['agent_id'])
         agents = self.client.list_agents()['agents']
-        self.assertNotEmpty(agents, 'Cannot get any agents.(%s)' % agents)
+        self.assertNotEmpty(agents, 'Cannot get any agents.')
         self.assertIn(body['agent_id'], map(lambda x: x['agent_id'], agents))
 
     @decorators.idempotent_id('eabadde4-3cd7-4ec4-a4b5-5a936d2d4408')
@@ -104,7 +104,7 @@
         agent_id_xen = agent_xen['agent_id']
         agents = (self.client.list_agents(hypervisor=agent_xen['hypervisor'])
                   ['agents'])
-        self.assertNotEmpty(agents, 'Cannot get any agents.(%s)' % agents)
+        self.assertNotEmpty(agents, 'Cannot get any agents.')
         self.assertIn(agent_id_xen, map(lambda x: x['agent_id'], agents))
         self.assertNotIn(body['agent_id'], map(lambda x: x['agent_id'],
                                                agents))
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 902ea9a..57d3983 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -125,7 +125,6 @@
             name=aggregate_name, availability_zone=az_name)
 
         self.assertEqual(az_name, aggregate['availability_zone'])
-        self.assertIsNotNone(aggregate['id'])
 
         aggregate_id = aggregate['id']
         new_aggregate_name = aggregate_name + '_new'
diff --git a/tempest/api/compute/admin/test_create_server.py b/tempest/api/compute/admin/test_create_server.py
index 3449aba..66bedd9 100644
--- a/tempest/api/compute/admin/test_create_server.py
+++ b/tempest/api/compute/admin/test_create_server.py
@@ -25,8 +25,6 @@
 
 
 class ServersWithSpecificFlavorTestJSON(base.BaseV2ComputeAdminTest):
-    disk_config = 'AUTO'
-
     @classmethod
     def setup_credentials(cls):
         cls.prepare_instance_network()
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index 0e1e7ed..00f3256 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -65,9 +65,4 @@
             resources = self.client.show_host(hostname)['host']
             self.assertNotEmpty(resources)
             host_resource = resources[0]['resource']
-            self.assertIsNotNone(host_resource)
-            self.assertIsNotNone(host_resource['cpu'])
-            self.assertIsNotNone(host_resource['disk_gb'])
-            self.assertIsNotNone(host_resource['memory_mb'])
-            self.assertIsNotNone(host_resource['project'])
             self.assertEqual(hostname, host_resource['host'])
diff --git a/tempest/api/compute/admin/test_hypervisor.py b/tempest/api/compute/admin/test_hypervisor.py
index 0db802c..404fd94 100644
--- a/tempest/api/compute/admin/test_hypervisor.py
+++ b/tempest/api/compute/admin/test_hypervisor.py
@@ -30,26 +30,23 @@
         hypers = self.client.list_hypervisors()['hypervisors']
         return hypers
 
-    def assertHypervisors(self, hypers):
-        self.assertNotEmpty(hypers, "No hypervisors found: %s" % hypers)
-
     @decorators.idempotent_id('7f0ceacd-c64d-4e96-b8ee-d02943142cc5')
     def test_get_hypervisor_list(self):
         # List of hypervisor and available hypervisors hostname
         hypers = self._list_hypervisors()
-        self.assertHypervisors(hypers)
+        self.assertNotEmpty(hypers, "No hypervisors found.")
 
     @decorators.idempotent_id('1e7fdac2-b672-4ad1-97a4-bad0e3030118')
     def test_get_hypervisor_list_details(self):
         # Display the details of the all hypervisor
         hypers = self.client.list_hypervisors(detail=True)['hypervisors']
-        self.assertHypervisors(hypers)
+        self.assertNotEmpty(hypers, "No hypervisors found.")
 
     @decorators.idempotent_id('94ff9eae-a183-428e-9cdb-79fde71211cc')
     def test_get_hypervisor_show_details(self):
         # Display the details of the specified hypervisor
         hypers = self._list_hypervisors()
-        self.assertHypervisors(hypers)
+        self.assertNotEmpty(hypers, "No hypervisors found.")
 
         details = self.client.show_hypervisor(hypers[0]['id'])['hypervisor']
         self.assertNotEmpty(details)
@@ -60,7 +57,7 @@
     def test_get_hypervisor_show_servers(self):
         # Show instances about the specific hypervisors
         hypers = self._list_hypervisors()
-        self.assertHypervisors(hypers)
+        self.assertNotEmpty(hypers, "No hypervisors found.")
 
         hostname = hypers[0]['hypervisor_hostname']
         hypervisors = (self.client.list_servers_on_hypervisor(hostname)
@@ -116,7 +113,7 @@
     @decorators.idempotent_id('d7e1805b-3b14-4a3b-b6fd-50ec6d9f361f')
     def test_search_hypervisor(self):
         hypers = self._list_hypervisors()
-        self.assertHypervisors(hypers)
+        self.assertNotEmpty(hypers, "No hypervisors found.")
         hypers = self.client.search_hypervisor(
             hypers[0]['hypervisor_hostname'])['hypervisors']
-        self.assertHypervisors(hypers)
+        self.assertNotEmpty(hypers, "No hypervisors found.")
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index 3f1bdce..256a267 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -163,7 +163,7 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.serial_console,
                           'Serial console not supported.')
     @testtools.skipUnless(
-        test.is_scheduler_filter_enabled("DifferentHostFilter"),
+        compute.is_scheduler_filter_enabled("DifferentHostFilter"),
         'DifferentHostFilter is not available.')
     def test_live_migration_serial_console(self):
         """Test the live-migration of an instance which has a serial console
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 937540e..1aa9227 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -169,7 +169,6 @@
         LOG.debug("get the current 'default' quota class values")
         body = (self.adm_client.show_quota_class_set('default')
                 ['quota_class_set'])
-        self.assertIn('id', body)
         self.assertEqual('default', body.pop('id'))
         # restore the defaults when the test is done
         self.addCleanup(self._restore_default_quotas, body.copy())
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index d9a7800..3f06c4e 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -13,9 +13,7 @@
 #    under the License.
 
 from tempest.api.compute import base
-from tempest.common import compute
 from tempest.common import waiters
-from tempest.lib.common import fixed_network
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
@@ -126,26 +124,17 @@
     @decorators.idempotent_id('86c7a8f7-50cf-43a9-9bac-5b985317134f')
     def test_list_servers_filter_by_exist_host(self):
         # Filter the list of servers by existent host
-        name = data_utils.rand_name(self.__class__.__name__ + '-server')
-        network = self.get_tenant_network()
-        network_kwargs = fixed_network.set_networks_kwarg(network)
-        # We need to create the server as an admin, so we can't use
-        # self.create_test_server() here as this method creates the server
-        # in the "primary" (i.e non-admin) tenant.
-        test_server, _ = compute.create_test_server(
-            self.os_admin, wait_until="ACTIVE", name=name, **network_kwargs)
-        self.addCleanup(self.client.delete_server, test_server['id'])
-        server = self.client.show_server(test_server['id'])['server']
-        self.assertEqual(server['status'], 'ACTIVE')
+        server = self.client.show_server(self.s1_id)['server']
         hostname = server['OS-EXT-SRV-ATTR:host']
-        params = {'host': hostname}
-        body = self.client.list_servers(**params)
-        servers = body['servers']
-        nonexistent_params = {'host': 'nonexistent_host'}
+        params = {'host': hostname, 'all_tenants': '1'}
+        servers = self.client.list_servers(**params)['servers']
+        self.assertIn(server['id'], map(lambda x: x['id'], servers))
+
+        nonexistent_params = {'host': 'nonexistent_host',
+                              'all_tenants': '1'}
         nonexistent_body = self.client.list_servers(**nonexistent_params)
         nonexistent_servers = nonexistent_body['servers']
-        self.assertIn(test_server['id'], map(lambda x: x['id'], servers))
-        self.assertNotIn(test_server['id'],
+        self.assertNotIn(server['id'],
                          map(lambda x: x['id'], nonexistent_servers))
 
     @decorators.idempotent_id('ee8ae470-db70-474d-b752-690b7892cab1')
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index feabe35..746f83a 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -97,8 +97,8 @@
         cls.security_group_default_rules_client = (
             cls.os_primary.security_group_default_rules_client)
         cls.versions_client = cls.os_primary.compute_versions_client
-
-        cls.volumes_client = cls.os_primary.volumes_v2_client
+        if CONF.service_available.cinder:
+            cls.volumes_client = cls.os_primary.volumes_client_latest
 
     @classmethod
     def resource_setup(cls):
diff --git a/tempest/api/compute/certificates/test_certificates.py b/tempest/api/compute/certificates/test_certificates.py
index a39fec9..0e6c016 100644
--- a/tempest/api/compute/certificates/test_certificates.py
+++ b/tempest/api/compute/certificates/test_certificates.py
@@ -31,14 +31,9 @@
     @decorators.idempotent_id('c070a441-b08e-447e-a733-905909535b1b')
     def test_create_root_certificate(self):
         # create certificates
-        body = self.certificates_client.create_certificate()['certificate']
-        self.assertIn('data', body)
-        self.assertIn('private_key', body)
+        self.certificates_client.create_certificate()
 
     @decorators.idempotent_id('3ac273d0-92d2-4632-bdfc-afbc21d4606c')
     def test_get_root_certificate(self):
         # get the root certificate
-        body = (self.certificates_client.show_certificate('root')
-                ['certificate'])
-        self.assertIn('data', body)
-        self.assertIn('private_key', body)
+        self.certificates_client.show_certificate('root')
diff --git a/tempest/api/compute/keypairs/test_keypairs.py b/tempest/api/compute/keypairs/test_keypairs.py
index 0b7a967..3a54d51 100644
--- a/tempest/api/compute/keypairs/test_keypairs.py
+++ b/tempest/api/compute/keypairs/test_keypairs.py
@@ -51,13 +51,10 @@
         # Keypair should be created, verified and deleted
         k_name = data_utils.rand_name('keypair')
         keypair = self.create_keypair(k_name)
-        private_key = keypair['private_key']
         key_name = keypair['name']
         self.assertEqual(key_name, k_name,
                          "The created keypair name is not equal "
                          "to the requested name")
-        self.assertIsNotNone(private_key,
-                             "Field private_key is empty or not found.")
 
     @decorators.idempotent_id('a4233d5d-52d8-47cc-9a25-e1864527e3df')
     def test_get_keypair_detail(self):
@@ -65,14 +62,9 @@
         k_name = data_utils.rand_name('keypair')
         self.create_keypair(k_name)
         keypair_detail = self.client.show_keypair(k_name)['keypair']
-        self.assertIn('name', keypair_detail)
-        self.assertIn('public_key', keypair_detail)
         self.assertEqual(keypair_detail['name'], k_name,
                          "The created keypair name is not equal "
                          "to requested name")
-        public_key = keypair_detail['public_key']
-        self.assertIsNotNone(public_key,
-                             "Field public_key is empty or not found.")
 
     @decorators.idempotent_id('39c90c6a-304a-49dd-95ec-2366129def05')
     def test_keypair_create_with_pub_key(self):
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 930a58e..a101a19 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -69,7 +69,6 @@
         # leading and trailing spaces
         s_name = ' %s ' % data_utils.rand_name('securitygroup ')
         securitygroup = self.create_security_group(name=s_name)
-        self.assertIn('name', securitygroup)
         securitygroup_name = securitygroup['name']
         self.assertEqual(securitygroup_name, s_name,
                          "The created Security Group name is "
@@ -131,7 +130,6 @@
         # Update security group name and description
         # Create a security group
         securitygroup = self.create_security_group()
-        self.assertIn('id', securitygroup)
         securitygroup_id = securitygroup['id']
         # Update the name and description
         s_new_name = data_utils.rand_name('sg-hth')
diff --git a/tempest/api/compute/security_groups/test_security_groups_negative.py b/tempest/api/compute/security_groups/test_security_groups_negative.py
index 207778a..c4dff15 100644
--- a/tempest/api/compute/security_groups/test_security_groups_negative.py
+++ b/tempest/api/compute/security_groups/test_security_groups_negative.py
@@ -154,7 +154,6 @@
     def test_update_security_group_with_invalid_sg_name(self):
         # Update security_group with invalid sg_name should fail
         securitygroup = self.create_security_group()
-        self.assertIn('id', securitygroup)
         securitygroup_id = securitygroup['id']
         # Update Security Group with group name longer than 255 chars
         s_new_name = 'securitygroup-'.ljust(260, '0')
@@ -170,7 +169,6 @@
     def test_update_security_group_with_invalid_sg_des(self):
         # Update security_group with invalid sg_des should fail
         securitygroup = self.create_security_group()
-        self.assertIn('id', securitygroup)
         securitygroup_id = securitygroup['id']
         # Update Security Group with group description longer than 255 chars
         s_new_des = 'des-'.ljust(260, '0')
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index 65d5042..bfde847 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -15,6 +15,8 @@
 
 import time
 
+import six
+
 from tempest.api.compute import base
 from tempest.common import compute
 from tempest.common.utils import net_utils
@@ -195,7 +197,7 @@
         except lib_exc.BadRequest as e:
             msg = ('Multiple possible networks found, use a Network ID to be '
                    'more specific.')
-            if not CONF.compute.fixed_network_name and e.message == msg:
+            if not CONF.compute.fixed_network_name and six.text_type(e) == msg:
                 raise
         else:
             ifs.append(iface)
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index aa5c43d..b727ddd 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -17,6 +17,7 @@
 import testtools
 
 from tempest.api.compute import base
+from tempest.common import compute
 from tempest.common.utils.linux import remote_client
 from tempest import config
 from tempest.lib.common.utils import data_utils
@@ -134,7 +135,7 @@
 
     @decorators.idempotent_id('ed20d3fb-9d1f-4329-b160-543fbd5d9811')
     @testtools.skipUnless(
-        test.is_scheduler_filter_enabled("ServerGroupAffinityFilter"),
+        compute.is_scheduler_filter_enabled("ServerGroupAffinityFilter"),
         'ServerGroupAffinityFilter is not available.')
     def test_create_server_with_scheduler_hint_group(self):
         # Create a server with the scheduler hint "group".
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index a4ed8e1..14aecfd 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -262,7 +262,7 @@
         # so as to ensure only one server is returned.
         ip_list = {}
         self.s1 = self.client.show_server(self.s1['id'])['server']
-        # Get first ip address inspite of v4 or v6
+        # Get first ip address in spite of v4 or v6
         ip_addr = self.s1['addresses'][self.fixed_network_name][0]['addr']
         ip_list[ip_addr] = self.s1['id']
 
diff --git a/tempest/api/compute/servers/test_novnc.py b/tempest/api/compute/servers/test_novnc.py
index 90b0665..d9581e3 100644
--- a/tempest/api/compute/servers/test_novnc.py
+++ b/tempest/api/compute/servers/test_novnc.py
@@ -166,7 +166,7 @@
         self._validate_novnc_html(body['url'])
         # Do the WebSockify HTTP Request to novncproxy to do the RFB connection
         self._websocket = compute.create_websocket(body['url'])
-        # Validate that we succesfully connected and upgraded to Web Sockets
+        # Validate that we successfully connected and upgraded to Web Sockets
         self._validate_websocket_upgrade()
         # Validate the RFB Negotiation to determine if a valid VNC session
         self._validate_rfb_negotiation()
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index cd09177..d1d29af 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -33,8 +33,6 @@
 
 
 class ServerActionsTestJSON(base.BaseV2ComputeTest):
-    run_ssh = CONF.validation.run_validation
-
     def setUp(self):
         # NOTE(afazekas): Normally we use the same server with all test cases,
         # but if it has an issue, we build a new one
@@ -166,8 +164,7 @@
                .format(image_ref, rebuilt_server['image']['id']))
         self.assertEqual(image_ref, rebuilt_server['image']['id'], msg)
 
-    @decorators.idempotent_id('aaa6cdf3-55a7-461a-add9-1c8596b9a07c')
-    def test_rebuild_server(self):
+    def _test_rebuild_server(self):
         # Get the IPs the server has before rebuilding it
         original_addresses = (self.client.show_server(self.server_id)['server']
                               ['addresses'])
@@ -218,6 +215,10 @@
                 servers_client=self.client)
             linux_client.validate_authentication()
 
+    @decorators.idempotent_id('aaa6cdf3-55a7-461a-add9-1c8596b9a07c')
+    def test_rebuild_server(self):
+        self._test_rebuild_server()
+
     @decorators.idempotent_id('30449a88-5aff-4f9b-9866-6ee9b17f906d')
     def test_rebuild_server_in_stop_state(self):
         # The server in stop state  should be rebuilt using the provided
@@ -260,7 +261,7 @@
         self.attach_volume(server, volume)
 
         # run general rebuild test
-        self.test_rebuild_server()
+        self._test_rebuild_server()
 
         # make sure the volume is attached to the instance after rebuild
         vol_after_rebuild = self.volumes_client.show_volume(volume['id'])
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index 6b625d9..a42b968 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -56,7 +56,6 @@
                 self.client.list_virtual_interfaces(self.server['id'])
         else:
             output = self.client.list_virtual_interfaces(self.server['id'])
-            self.assertIsNotNone(output)
             virt_ifaces = output
             self.assertNotEmpty(virt_ifaces['virtual_interfaces'],
                                 'Expected virtual interfaces, got 0 '
diff --git a/tempest/api/compute/volumes/test_volumes_get.py b/tempest/api/compute/volumes/test_volumes_get.py
index 01cfb5f..d83d49e 100644
--- a/tempest/api/compute/volumes/test_volumes_get.py
+++ b/tempest/api/compute/volumes/test_volumes_get.py
@@ -52,13 +52,9 @@
         volume = self.create_volume(size=CONF.volume.volume_size,
                                     display_name=v_name,
                                     metadata=metadata)
-        self.assertIn('id', volume)
-        self.assertIn('displayName', volume)
         self.assertEqual(volume['displayName'], 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
         fetched_volume = self.volumes_client.show_volume(
             volume['id'])['volume']
diff --git a/tempest/api/identity/admin/v2/test_services.py b/tempest/api/identity/admin/v2/test_services.py
index 634cf21..e2ed5ef 100644
--- a/tempest/api/identity/admin/v2/test_services.py
+++ b/tempest/api/identity/admin/v2/test_services.py
@@ -41,7 +41,6 @@
         self.assertIsNotNone(service_data['id'])
         self.addCleanup(self._del_service, service_data['id'])
         # Verifying response body of create service
-        self.assertIn('id', service_data)
         self.assertIn('name', service_data)
         self.assertEqual(name, service_data['name'])
         self.assertIn('type', service_data)
diff --git a/tempest/api/identity/admin/v3/test_domains.py b/tempest/api/identity/admin/v3/test_domains.py
index 9fe978c..bf04ede 100644
--- a/tempest/api/identity/admin/v3/test_domains.py
+++ b/tempest/api/identity/admin/v3/test_domains.py
@@ -93,7 +93,6 @@
             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)
         self.assertIn('description', domain)
         self.assertIn('name', domain)
         self.assertIn('enabled', domain)
@@ -147,7 +146,6 @@
         d_name = data_utils.rand_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.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 1a0b851..56f7d32 100644
--- a/tempest/api/identity/admin/v3/test_domains_negative.py
+++ b/tempest/api/identity/admin/v3/test_domains_negative.py
@@ -20,7 +20,6 @@
 
 
 class DomainsNegativeTestJSON(base.BaseIdentityV3AdminTest):
-    _interface = 'json'
 
     @decorators.attr(type=['negative', 'gate'])
     @decorators.idempotent_id('1f3fbff5-4e44-400d-9ca1-d953f05f609b')
diff --git a/tempest/api/identity/admin/v3/test_endpoints.py b/tempest/api/identity/admin/v3/test_endpoints.py
index c9faa9a..5d48f68 100644
--- a/tempest/api/identity/admin/v3/test_endpoints.py
+++ b/tempest/api/identity/admin/v3/test_endpoints.py
@@ -117,7 +117,6 @@
 
         self.setup_endpoint_ids.append(endpoint['id'])
         # Asserting Create Endpoint response body
-        self.assertIn('id', endpoint)
         self.assertEqual(region, endpoint['region'])
         self.assertEqual(url, endpoint['url'])
 
diff --git a/tempest/api/identity/admin/v3/test_inherits.py b/tempest/api/identity/admin/v3/test_inherits.py
index 49b6585..e61dbc8 100644
--- a/tempest/api/identity/admin/v3/test_inherits.py
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -16,17 +16,17 @@
 from tempest import test
 
 
-class BaseInheritsV3Test(base.BaseIdentityV3AdminTest):
+class InheritsV3TestJSON(base.BaseIdentityV3AdminTest):
 
     @classmethod
     def skip_checks(cls):
-        super(BaseInheritsV3Test, cls).skip_checks()
+        super(InheritsV3TestJSON, cls).skip_checks()
         if not test.is_extension_enabled('OS-INHERIT', 'identity'):
             raise cls.skipException("Inherits aren't enabled")
 
     @classmethod
     def resource_setup(cls):
-        super(BaseInheritsV3Test, cls).resource_setup()
+        super(InheritsV3TestJSON, cls).resource_setup()
         u_name = data_utils.rand_name('user-')
         u_desc = '%s description' % u_name
         u_email = '%s@testmail.tm' % u_name
@@ -51,15 +51,12 @@
         cls.projects_client.delete_project(cls.project['id'])
         cls.domains_client.update_domain(cls.domain['id'], enabled=False)
         cls.domains_client.delete_domain(cls.domain['id'])
-        super(BaseInheritsV3Test, cls).resource_cleanup()
+        super(InheritsV3TestJSON, cls).resource_cleanup()
 
     def _list_assertions(self, body, fetched_role_ids, role_id):
         self.assertEqual(len(body), 1)
         self.assertIn(role_id, fetched_role_ids)
 
-
-class InheritsV3TestJSON(BaseInheritsV3Test):
-
     @decorators.idempotent_id('4e6f0366-97c8-423c-b2be-41eae6ac91c8')
     def test_inherit_assign_list_check_revoke_roles_on_domains_user(self):
         # Create role
diff --git a/tempest/api/identity/admin/v3/test_policies.py b/tempest/api/identity/admin/v3/test_policies.py
index 960e2cb..2908fc4 100644
--- a/tempest/api/identity/admin/v3/test_policies.py
+++ b/tempest/api/identity/admin/v3/test_policies.py
@@ -52,7 +52,6 @@
         policy = self.policies_client.create_policy(blob=blob,
                                                     type=policy_type)['policy']
         self.addCleanup(self._delete_policy, policy['id'])
-        self.assertIn('id', policy)
         self.assertIn('type', policy)
         self.assertIn('blob', policy)
         self.assertIsNotNone(policy['id'])
diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py
index 6d42b2a..ec904e6 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -33,7 +33,6 @@
             role_name = data_utils.rand_name(name='role')
             role = cls.roles_client.create_role(name=role_name)['role']
             cls.roles.append(role)
-        cls.fetched_role_ids = list()
         u_name = data_utils.rand_name('user')
         u_desc = '%s description' % u_name
         u_email = '%s@testmail.tm' % u_name
@@ -67,10 +66,6 @@
             cls.roles_client.delete_role(role['id'])
         super(RolesV3TestJSON, cls).resource_cleanup()
 
-    def _list_assertions(self, body, fetched_role_ids, role_id):
-        self.assertEqual(len(body), 1)
-        self.assertIn(role_id, fetched_role_ids)
-
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('18afc6c0-46cf-4911-824e-9989cc056c3a')
     def test_role_create_update_show_list(self):
@@ -104,11 +99,8 @@
         roles = self.roles_client.list_user_roles_on_project(
             self.project['id'], self.user_body['id'])['roles']
 
-        for i in roles:
-            self.fetched_role_ids.append(i['id'])
-
-        self._list_assertions(roles, self.fetched_role_ids,
-                              self.role['id'])
+        self.assertEqual(1, len(roles))
+        self.assertEqual(self.role['id'], roles[0]['id'])
 
         self.roles_client.check_user_role_existence_on_project(
             self.project['id'], self.user_body['id'], self.role['id'])
@@ -124,11 +116,8 @@
         roles = self.roles_client.list_user_roles_on_domain(
             self.domain['id'], self.user_body['id'])['roles']
 
-        for i in roles:
-            self.fetched_role_ids.append(i['id'])
-
-        self._list_assertions(roles, self.fetched_role_ids,
-                              self.role['id'])
+        self.assertEqual(1, len(roles))
+        self.assertEqual(self.role['id'], roles[0]['id'])
 
         self.roles_client.check_user_role_existence_on_domain(
             self.domain['id'], self.user_body['id'], self.role['id'])
@@ -145,11 +134,9 @@
         roles = self.roles_client.list_group_roles_on_project(
             self.project['id'], self.group_body['id'])['roles']
 
-        for i in roles:
-            self.fetched_role_ids.append(i['id'])
+        self.assertEqual(1, len(roles))
+        self.assertEqual(self.role['id'], roles[0]['id'])
 
-        self._list_assertions(roles, self.fetched_role_ids,
-                              self.role['id'])
         # Add user to group, and insure user has role on project
         self.groups_client.add_group_user(self.group_body['id'],
                                           self.user_body['id'])
@@ -179,11 +166,8 @@
         roles = self.roles_client.list_group_roles_on_domain(
             self.domain['id'], self.group_body['id'])['roles']
 
-        for i in roles:
-            self.fetched_role_ids.append(i['id'])
-
-        self._list_assertions(roles, self.fetched_role_ids,
-                              self.role['id'])
+        self.assertEqual(1, len(roles))
+        self.assertEqual(self.role['id'], roles[0]['id'])
 
         self.roles_client.check_role_from_group_on_domain_existence(
             self.domain['id'], self.group_body['id'], self.role['id'])
diff --git a/tempest/api/identity/admin/v3/test_services.py b/tempest/api/identity/admin/v3/test_services.py
index 20c8a44..5afeb98 100644
--- a/tempest/api/identity/admin/v3/test_services.py
+++ b/tempest/api/identity/admin/v3/test_services.py
@@ -69,7 +69,6 @@
         service = self.services_client.create_service(
             type=serv_type, name=name)['service']
         self.addCleanup(self.services_client.delete_service, service['id'])
-        self.assertIn('id', service)
         expected_data = {'name': name, 'type': serv_type}
         self.assertDictContainsSubset(expected_data, service)
 
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 3e6a2de..2530072 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -26,22 +26,27 @@
 CONF = config.CONF
 
 
-class BaseTrustsV3Test(base.BaseIdentityV3AdminTest):
+class TrustsV3TestJSON(base.BaseIdentityV3AdminTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(TrustsV3TestJSON, cls).skip_checks()
+        if not CONF.identity_feature_enabled.trust:
+            raise cls.skipException("Trusts aren't enabled")
 
     def setUp(self):
-        super(BaseTrustsV3Test, self).setUp()
+        super(TrustsV3TestJSON, self).setUp()
         # Use alt_username as the trustee
-        if not CONF.identity_feature_enabled.trust:
-            raise self.skipException("Trusts aren't enabled")
-
         self.trust_id = None
+        self.create_trustor_and_roles()
+        self.addCleanup(self.cleanup_user_and_roles)
 
     def tearDown(self):
         if self.trust_id:
             # Do the delete in tearDown not addCleanup - we want the test to
             # fail in the event there is a bug which causes undeletable trusts
             self.delete_trust()
-        super(BaseTrustsV3Test, self).tearDown()
+        super(TrustsV3TestJSON, self).tearDown()
 
     def create_trustor_and_roles(self):
         # create a project that trusts will be granted on
@@ -190,14 +195,6 @@
                           self.trust_id)
         self.trust_id = None
 
-
-class TrustsV3TestJSON(BaseTrustsV3Test):
-
-    def setUp(self):
-        super(TrustsV3TestJSON, self).setUp()
-        self.create_trustor_and_roles()
-        self.addCleanup(self.cleanup_user_and_roles)
-
     @decorators.idempotent_id('5a0a91a4-baef-4a14-baba-59bf4d7fcace')
     def test_trust_impersonate(self):
         # Test case to check we can create, get and delete a trust
diff --git a/tempest/api/identity/admin/v3/test_users.py b/tempest/api/identity/admin/v3/test_users.py
index 409d4f8..3813568 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -41,31 +41,26 @@
             email=u_email, enabled=False)['user']
         # Delete the User at the end of this method
         self.addCleanup(self.users_client.delete_user, user['id'])
+
         # Creating second project for updation
         project = self.setup_test_project()
+
         # Updating user details with new values
-        u_name2 = data_utils.rand_name('user2')
-        u_email2 = u_name2 + '@testmail.tm'
-        u_description2 = u_name2 + ' description'
-        update_user = self.users_client.update_user(
-            user['id'], name=u_name2, description=u_description2,
-            project_id=project['id'],
-            email=u_email2, enabled=False)['user']
-        self.assertEqual(u_name2, update_user['name'])
-        self.assertEqual(u_description2, update_user['description'])
-        self.assertEqual(project['id'],
-                         update_user['project_id'])
-        self.assertEqual(u_email2, update_user['email'])
-        self.assertEqual(False, update_user['enabled'])
-        # GET by id after updation
+        update_kwargs = {'name': data_utils.rand_name('user2'),
+                         'description': data_utils.rand_name('desc2'),
+                         'project_id': project['id'],
+                         'email': 'user2@testmail.tm',
+                         'enabled': False}
+        updated_user = self.users_client.update_user(
+            user['id'], **update_kwargs)['user']
+        for field in update_kwargs:
+            self.assertEqual(update_kwargs[field], updated_user[field])
+
+        # GET by id after updating
         new_user_get = self.users_client.show_user(user['id'])['user']
         # Assert response body of GET after updation
-        self.assertEqual(u_name2, new_user_get['name'])
-        self.assertEqual(u_description2, new_user_get['description'])
-        self.assertEqual(project['id'],
-                         new_user_get['project_id'])
-        self.assertEqual(u_email2, new_user_get['email'])
-        self.assertEqual(False, new_user_get['enabled'])
+        for field in update_kwargs:
+            self.assertEqual(update_kwargs[field], new_user_get[field])
 
     @decorators.idempotent_id('2d223a0e-e457-4a70-9fb1-febe027a0ff9')
     def test_update_user_password(self):
diff --git a/tempest/api/network/admin/test_routers_dvr.py b/tempest/api/network/admin/test_routers_dvr.py
index f9a0cfb..b6772b1 100644
--- a/tempest/api/network/admin/test_routers_dvr.py
+++ b/tempest/api/network/admin/test_routers_dvr.py
@@ -24,7 +24,8 @@
 class RoutersTestDVR(base.BaseAdminNetworkTest):
 
     @classmethod
-    def resource_setup(cls):
+    def skip_checks(cls):
+        super(RoutersTestDVR, cls).skip_checks()
         for ext in ['router', 'dvr']:
             if not test.is_extension_enabled(ext, 'network'):
                 msg = "%s extension not enabled." % ext
@@ -35,6 +36,9 @@
         # admin credentials to create router with distributed=True attribute
         # and checking for BadRequest exception and that the resulting router
         # has a distributed attribute.
+
+    @classmethod
+    def resource_setup(cls):
         super(RoutersTestDVR, cls).resource_setup()
         name = data_utils.rand_name('pretest-check')
         router = cls.admin_routers_client.create_router(name=name)
diff --git a/tempest/api/network/test_routers_negative.py b/tempest/api/network/test_routers_negative.py
index 72face8..db165ab 100644
--- a/tempest/api/network/test_routers_negative.py
+++ b/tempest/api/network/test_routers_negative.py
@@ -128,13 +128,6 @@
             msg = "DVR extension not enabled."
             raise cls.skipException(msg)
 
-    @classmethod
-    def resource_setup(cls):
-        super(DvrRoutersNegativeTest, cls).resource_setup()
-        cls.router = cls.create_router()
-        cls.network = cls.create_network()
-        cls.subnet = cls.create_subnet(cls.network)
-
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('4990b055-8fc7-48ab-bba7-aa28beaad0b9')
     def test_router_create_tenant_distributed_returns_forbidden(self):
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index b29a77f..556ca2f 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -973,7 +973,7 @@
     @classmethod
     def setup_clients(cls):
         super(PublicObjectTest, cls).setup_clients()
-        cls.identity_client_alt = cls.os_alt.identity_client
+        cls.object_client_alt = cls.os_alt.object_client
 
     def setUp(self):
         super(PublicObjectTest, self).setUp()
@@ -1047,7 +1047,7 @@
         self.assertEqual(resp['x-container-read'], '.r:*,.rlistings')
 
         # get auth token of alternative user
-        alt_auth_data = self.identity_client_alt.auth_provider.auth_data
+        alt_auth_data = self.object_client_alt.auth_provider.auth_data
         self.object_client.auth_provider.set_alt_auth_data(
             request_part='headers',
             auth_data=alt_auth_data
diff --git a/tempest/api/volume/admin/test_groups.py b/tempest/api/volume/admin/test_groups.py
index 4c1a3eb..baea37b 100644
--- a/tempest/api/volume/admin/test_groups.py
+++ b/tempest/api/volume/admin/test_groups.py
@@ -17,6 +17,7 @@
 from tempest.common import waiters
 from tempest import config
 from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 CONF = config.CONF
@@ -27,34 +28,43 @@
     min_microversion = '3.14'
     max_microversion = 'latest'
 
-    @classmethod
-    def setup_clients(cls):
-        super(GroupsTest, cls).setup_clients()
-        cls.volumes_client = cls.admin_volume_client
-
     def _delete_group(self, grp_id, delete_volumes=True):
-        self.admin_groups_client.delete_group(grp_id, delete_volumes)
-        vols = self.admin_volume_client.list_volumes(detail=True)['volumes']
+        self.groups_client.delete_group(grp_id, delete_volumes)
+        vols = self.volumes_client.list_volumes(detail=True)['volumes']
         for vol in vols:
             if vol['group_id'] == grp_id:
-                self.admin_volume_client.wait_for_resource_deletion(vol['id'])
-        self.admin_groups_client.wait_for_resource_deletion(grp_id)
+                self.volumes_client.wait_for_resource_deletion(vol['id'])
+        self.groups_client.wait_for_resource_deletion(grp_id)
 
     def _delete_group_snapshot(self, group_snapshot_id, grp_id):
-        self.admin_group_snapshots_client.delete_group_snapshot(
+        self.group_snapshots_client.delete_group_snapshot(
             group_snapshot_id)
-        vols = self.admin_volume_client.list_volumes(detail=True)['volumes']
-        snapshots = self.admin_snapshots_client.list_snapshots(
+        vols = self.volumes_client.list_volumes(detail=True)['volumes']
+        snapshots = self.snapshots_client.list_snapshots(
             detail=True)['snapshots']
         for vol in vols:
             for snap in snapshots:
                 if (vol['group_id'] == grp_id and
                         vol['id'] == snap['volume_id']):
-                    self.admin_snapshots_client.wait_for_resource_deletion(
+                    self.snapshots_client.wait_for_resource_deletion(
                         snap['id'])
-        self.admin_group_snapshots_client.wait_for_resource_deletion(
+        self.group_snapshots_client.wait_for_resource_deletion(
             group_snapshot_id)
 
+    def _create_group(self, group_type, volume_type, grp_name=None):
+        if not grp_name:
+            grp_name = data_utils.rand_name('Group')
+        grp = self.groups_client.create_group(
+            group_type=group_type['id'],
+            volume_types=[volume_type['id']],
+            name=grp_name)['group']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self._delete_group, grp['id'])
+        waiters.wait_for_volume_resource_status(
+            self.groups_client, grp['id'], 'available')
+        self.assertEqual(grp_name, grp['name'])
+        return grp
+
     @decorators.idempotent_id('4b111d28-b73d-4908-9bd2-03dc2992e4d4')
     def test_group_create_show_list_delete(self):
         # Create volume type
@@ -65,21 +75,13 @@
 
         # Create group
         grp1_name = data_utils.rand_name('Group1')
-        grp1 = self.admin_groups_client.create_group(
-            group_type=group_type['id'],
-            volume_types=[volume_type['id']],
-            name=grp1_name)['group']
-        waiters.wait_for_volume_resource_status(
-            self.admin_groups_client, grp1['id'], 'available')
+        grp1 = self._create_group(group_type, volume_type,
+                                  grp_name=grp1_name)
         grp1_id = grp1['id']
 
         grp2_name = data_utils.rand_name('Group2')
-        grp2 = self.admin_groups_client.create_group(
-            group_type=group_type['id'],
-            volume_types=[volume_type['id']],
-            name=grp2_name)['group']
-        waiters.wait_for_volume_resource_status(
-            self.admin_groups_client, grp2['id'], 'available')
+        grp2 = self._create_group(group_type, volume_type,
+                                  grp_name=grp2_name)
         grp2_id = grp2['id']
 
         # Create volume
@@ -88,23 +90,23 @@
                   'volume_type': volume_type['id'],
                   'group_id': grp1['id'],
                   'size': CONF.volume.volume_size}
-        vol1 = self.admin_volume_client.create_volume(**params)['volume']
+        vol1 = self.volumes_client.create_volume(**params)['volume']
         self.assertEqual(grp1['id'], vol1['group_id'])
         waiters.wait_for_volume_resource_status(
-            self.admin_volume_client, vol1['id'], 'available')
+            self.volumes_client, vol1['id'], 'available')
         vol1_id = vol1['id']
 
         # Get a given group
-        grp1 = self.admin_groups_client.show_group(grp1['id'])['group']
+        grp1 = self.groups_client.show_group(grp1['id'])['group']
         self.assertEqual(grp1_name, grp1['name'])
         self.assertEqual(grp1_id, grp1['id'])
 
-        grp2 = self.admin_groups_client.show_group(grp2['id'])['group']
+        grp2 = self.groups_client.show_group(grp2['id'])['group']
         self.assertEqual(grp2_name, grp2['name'])
         self.assertEqual(grp2_id, grp2['id'])
 
         # Get all groups with detail
-        grps = self.admin_groups_client.list_groups(
+        grps = self.groups_client.list_groups(
             detail=True)['groups']
         filtered_grps = [g for g in grps if g['id'] in [grp1_id, grp2_id]]
         self.assertEqual(2, len(filtered_grps))
@@ -112,7 +114,7 @@
             self.assertEqual([volume_type['id']], grp['volume_types'])
             self.assertEqual(group_type['id'], grp['group_type'])
 
-        vols = self.admin_volume_client.list_volumes(
+        vols = self.volumes_client.list_volumes(
             detail=True)['volumes']
         filtered_vols = [v for v in vols if v['id'] in [vol1_id]]
         self.assertEqual(1, len(filtered_vols))
@@ -124,7 +126,7 @@
         self._delete_group(grp1_id)
         # grp2 is empty so delete_volumes flag can be set to False
         self._delete_group(grp2_id, delete_volumes=False)
-        grps = self.admin_groups_client.list_groups(
+        grps = self.groups_client.list_groups(
             detail=True)['groups']
         self.assertEmpty(grps)
 
@@ -137,15 +139,7 @@
         group_type = self.create_group_type()
 
         # Create group
-        grp_name = data_utils.rand_name('Group')
-        grp = self.admin_groups_client.create_group(
-            group_type=group_type['id'],
-            volume_types=[volume_type['id']],
-            name=grp_name)['group']
-        waiters.wait_for_volume_resource_status(
-            self.admin_groups_client, grp['id'], 'available')
-        self.addCleanup(self._delete_group, grp['id'])
-        self.assertEqual(grp_name, grp['name'])
+        grp = self._create_group(group_type, volume_type)
 
         # Create volume
         vol = self.create_volume(volume_type=volume_type['id'],
@@ -154,28 +148,28 @@
         # Create group snapshot
         group_snapshot_name = data_utils.rand_name('group_snapshot')
         group_snapshot = (
-            self.admin_group_snapshots_client.create_group_snapshot(
+            self.group_snapshots_client.create_group_snapshot(
                 group_id=grp['id'],
                 name=group_snapshot_name)['group_snapshot'])
-        snapshots = self.admin_snapshots_client.list_snapshots(
+        snapshots = self.snapshots_client.list_snapshots(
             detail=True)['snapshots']
         for snap in snapshots:
             if vol['id'] == snap['volume_id']:
                 waiters.wait_for_volume_resource_status(
-                    self.admin_snapshots_client, snap['id'], 'available')
+                    self.snapshots_client, snap['id'], 'available')
         waiters.wait_for_volume_resource_status(
-            self.admin_group_snapshots_client,
+            self.group_snapshots_client,
             group_snapshot['id'], 'available')
         self.assertEqual(group_snapshot_name, group_snapshot['name'])
 
         # Get a given group snapshot
-        group_snapshot = self.admin_group_snapshots_client.show_group_snapshot(
+        group_snapshot = self.group_snapshots_client.show_group_snapshot(
             group_snapshot['id'])['group_snapshot']
         self.assertEqual(group_snapshot_name, group_snapshot['name'])
 
         # Get all group snapshots with detail
         group_snapshots = (
-            self.admin_group_snapshots_client.list_group_snapshots(
+            self.group_snapshots_client.list_group_snapshots(
                 detail=True)['group_snapshots'])
         self.assertIn((group_snapshot['name'], group_snapshot['id']),
                       [(m['name'], m['id']) for m in group_snapshots])
@@ -183,6 +177,146 @@
         # Delete group snapshot
         self._delete_group_snapshot(group_snapshot['id'], grp['id'])
         group_snapshots = (
-            self.admin_group_snapshots_client.list_group_snapshots(
+            self.group_snapshots_client.list_group_snapshots(
                 detail=True)['group_snapshots'])
         self.assertEmpty(group_snapshots)
+
+    @decorators.idempotent_id('eff52c70-efc7-45ed-b47a-4ad675d09b81')
+    def test_create_group_from_group_snapshot(self):
+        # Create volume type
+        volume_type = self.create_volume_type()
+
+        # Create group type
+        group_type = self.create_group_type()
+
+        # Create Group
+        grp = self._create_group(group_type, volume_type)
+
+        # Create volume
+        vol = self.create_volume(volume_type=volume_type['id'],
+                                 group_id=grp['id'])
+
+        # Create group_snapshot
+        group_snapshot_name = data_utils.rand_name('group_snapshot')
+        group_snapshot = (
+            self.group_snapshots_client.create_group_snapshot(
+                group_id=grp['id'],
+                name=group_snapshot_name)['group_snapshot'])
+        self.addCleanup(self._delete_group_snapshot,
+                        group_snapshot['id'], grp['id'])
+        self.assertEqual(group_snapshot_name, group_snapshot['name'])
+        snapshots = self.snapshots_client.list_snapshots(
+            detail=True)['snapshots']
+        for snap in snapshots:
+            if vol['id'] == snap['volume_id']:
+                waiters.wait_for_volume_resource_status(
+                    self.snapshots_client, snap['id'], 'available')
+        waiters.wait_for_volume_resource_status(
+            self.group_snapshots_client,
+            group_snapshot['id'], 'available')
+
+        # Create Group from Group snapshot
+        grp_name2 = data_utils.rand_name('Group_from_snap')
+        grp2 = self.groups_client.create_group_from_source(
+            group_snapshot_id=group_snapshot['id'],
+            name=grp_name2)['group']
+        self.addCleanup(self._delete_group, grp2['id'])
+        self.assertEqual(grp_name2, grp2['name'])
+        vols = self.volumes_client.list_volumes(detail=True)['volumes']
+        for vol in vols:
+            if vol['group_id'] == grp2['id']:
+                waiters.wait_for_volume_resource_status(
+                    self.volumes_client, vol['id'], 'available')
+        waiters.wait_for_volume_resource_status(
+            self.groups_client, grp2['id'], 'available')
+
+    @decorators.idempotent_id('2424af8c-7851-4888-986a-794b10c3210e')
+    def test_create_group_from_group(self):
+        # Create volume type
+        volume_type = self.create_volume_type()
+
+        # Create group type
+        group_type = self.create_group_type()
+
+        # Create Group
+        grp = self._create_group(group_type, volume_type)
+
+        # Create volume
+        self.create_volume(volume_type=volume_type['id'], group_id=grp['id'])
+
+        # Create Group from Group
+        grp_name2 = data_utils.rand_name('Group_from_grp')
+        grp2 = self.groups_client.create_group_from_source(
+            source_group_id=grp['id'], name=grp_name2)['group']
+        self.addCleanup(self._delete_group, grp2['id'])
+        self.assertEqual(grp_name2, grp2['name'])
+        vols = self.volumes_client.list_volumes(
+            detail=True)['volumes']
+        for vol in vols:
+            if vol['group_id'] == grp2['id']:
+                waiters.wait_for_volume_resource_status(
+                    self.volumes_client, vol['id'], 'available')
+        waiters.wait_for_volume_resource_status(
+            self.groups_client, grp2['id'], 'available')
+
+    @decorators.idempotent_id('4a8a6fd2-8b3b-4641-8f54-6a6f99320006')
+    def test_group_update(self):
+        # Create volume type
+        volume_type = self.create_volume_type()
+
+        # Create group type
+        group_type = self.create_group_type()
+
+        # Create Group
+        grp = self._create_group(group_type, volume_type)
+
+        # Create volumes
+        grp_vols = []
+        for _ in range(2):
+            vol = self.create_volume(volume_type=volume_type['id'],
+                                     group_id=grp['id'])
+            grp_vols.append(vol)
+        vol2 = grp_vols[1]
+
+        # Remove a volume from group and update name and description
+        new_grp_name = 'new_group'
+        new_desc = 'This is a new group'
+        grp_params = {'name': new_grp_name,
+                      'description': new_desc,
+                      'remove_volumes': vol2['id']}
+        self.groups_client.update_group(grp['id'], **grp_params)
+
+        # Wait for group status to become available
+        waiters.wait_for_volume_resource_status(
+            self.groups_client, grp['id'], 'available')
+
+        # Get the updated Group
+        grp = self.groups_client.show_group(grp['id'])['group']
+        self.assertEqual(new_grp_name, grp['name'])
+        self.assertEqual(new_desc, grp['description'])
+
+        # Get volumes in the group
+        vols = self.volumes_client.list_volumes(
+            detail=True)['volumes']
+        grp_vols = []
+        for vol in vols:
+            if vol['group_id'] == grp['id']:
+                grp_vols.append(vol)
+        self.assertEqual(1, len(grp_vols))
+
+        # Add a volume to the group
+        grp_params = {'add_volumes': vol2['id']}
+        self.groups_client.update_group(grp['id'], **grp_params)
+
+        # Wait for group status to become available
+        waiters.wait_for_volume_resource_status(
+            self.groups_client, grp['id'], 'available')
+
+        # Get volumes in the group
+        vols = self.volumes_client.list_volumes(
+            detail=True)['volumes']
+        grp_vols = []
+        for vol in vols:
+            if vol['group_id'] == grp['id']:
+                grp_vols.append(vol)
+        self.assertEqual(2, len(grp_vols))
diff --git a/tempest/api/volume/test_image_metadata.py b/tempest/api/volume/test_image_metadata.py
index 77baf18..129981b 100644
--- a/tempest/api/volume/test_image_metadata.py
+++ b/tempest/api/volume/test_image_metadata.py
@@ -40,7 +40,7 @@
 
     @decorators.idempotent_id('03efff0b-5c75-4822-8f10-8789ac15b13e')
     @test.services('image')
-    def test_update_image_metadata(self):
+    def test_update_show_delete_image_metadata(self):
         # Update image metadata
         image_metadata = {'image_id': '5137a025-3c5f-43c1-bc64-5f41270040a5',
                           'image_name': 'image',
@@ -49,7 +49,7 @@
         self.volumes_client.update_volume_image_metadata(self.volume['id'],
                                                          **image_metadata)
 
-        # Fetch image metadata from the volume
+        # Fetch volume's image metadata by show_volume method
         volume_image_metadata = self.volumes_client.show_volume(
             self.volume['id'])['volume']['volume_image_metadata']
 
@@ -62,9 +62,9 @@
                                                          'ramdisk_id')
         del image_metadata['ramdisk_id']
 
-        # Fetch the new image metadata from the volume
-        volume_image_metadata = self.volumes_client.show_volume(
-            self.volume['id'])['volume']['volume_image_metadata']
+        # Fetch volume's image metadata by show_volume_image_metadata method
+        volume_image_metadata = self.volumes_client.show_volume_image_metadata(
+            self.volume['id'])['metadata']
 
         # Verify image metadata was updated after item deletion
         self.assertThat(volume_image_metadata.items(),
diff --git a/tempest/clients.py b/tempest/clients.py
index d7a52d1..e617c3c 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -263,6 +263,7 @@
 
             # Set default client for users that don't need explicit version
             self.volumes_client_latest = self.volumes_v2_client
+            self.snapshots_client_latest = self.snapshots_v2_client
 
         if CONF.volume_feature_enabled.api_v3:
             self.backups_v3_client = self.volume_v3.BackupsClient()
@@ -277,6 +278,7 @@
 
             # Set default client for users that don't need explicit version
             self.volumes_client_latest = self.volumes_v3_client
+            self.snapshots_client_latest = self.snapshots_v3_client
 
     def _set_object_storage_clients(self):
         # NOTE(andreaf) Load configuration from config. Once object storage
diff --git a/tempest/cmd/subunit_describe_calls.py b/tempest/cmd/subunit_describe_calls.py
index 8ee3055..f9ebe20 100644
--- a/tempest/cmd/subunit_describe_calls.py
+++ b/tempest/cmd/subunit_describe_calls.py
@@ -102,8 +102,8 @@
     response_re = re.compile(r'.* Response - Headers: (?P<headers>.*)')
     body_re = re.compile(r'.*Body: (?P<body>.*)')
 
-    # Based on mitaka defaults:
-    # http://docs.openstack.org/mitaka/config-reference/
+    # Based on newton defaults:
+    # http://docs.openstack.org/newton/config-reference/
     # firewalls-default-ports.html
     services = {
         "8776": "Block Storage",
@@ -122,7 +122,8 @@
         "873": "rsync",
         "3260": "iSCSI",
         "3306": "MySQL",
-        "5672": "AMQP"}
+        "5672": "AMQP",
+        "8082": "murano"}
 
     def __init__(self, services=None):
         super(UrlParser, self).__init__()
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 8e71ecc..a72493d 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -14,6 +14,51 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+"""
+Verifies user's current tempest configuration.
+
+This command is used for updating or user's tempest configuration file based on
+api queries or replacing all option in a tempest configuration file for a full
+list of extensions.
+
+General Options
+===============
+
+-u, --update
+------------
+Update the config file with results from api queries. This assumes whatever is
+set in the config file is incorrect.
+
+-o FILE, --output=FILE
+----------------------
+Output file to write an updated config file to. This has to be a separate file
+from the original one. If one isn't specified with -u the values which should
+be changed will be printed to STDOUT.
+
+-r, --replace-ext
+-----------------
+If specified the all option will be replaced with a full list of extensions.
+
+Environment Variables
+=====================
+
+The command is workspace aware - it uses tempest config file tempest.conf
+located in ./etc/ directory.
+The path to the config file and it's name can be changed through environment
+variables.
+
+TEMPEST_CONFIG_DIR
+------------------
+Path to a directory where tempest configuration file is stored. If the variable
+is set, the default path (./etc/) is overridden.
+
+TEMPEST_CONFIG
+--------------
+Name of a tempest configuration file. If the variable is specified, the default
+name (tempest.conf) is overridden.
+
+"""
+
 import argparse
 import os
 import re
@@ -30,6 +75,8 @@
 from tempest.common import credentials_factory as credentials
 from tempest import config
 import tempest.lib.common.http
+from tempest.lib import exceptions as lib_exc
+from tempest.services import object_storage
 
 
 CONF = config.CONF
@@ -39,8 +86,8 @@
 
 
 def _get_config_file():
-    default_config_dir = os.path.join(os.path.abspath(
-        os.path.dirname(os.path.dirname(os.path.dirname(__file__)))), "etc")
+    config_dir = os.getcwd()
+    default_config_dir = os.path.join(config_dir, "etc")
     default_config_file = "tempest.conf"
 
     conf_dir = os.environ.get('TEMPEST_CONFIG_DIR', default_config_dir)
@@ -69,7 +116,25 @@
 
 def verify_glance_api_versions(os, update):
     # Check glance api versions
-    _, versions = os.image_client.get_versions()
+    # Since we want to verify that the configuration is correct, we cannot
+    # rely on a specific version of the API being available.
+    try:
+        _, versions = os.image_v1.ImagesClient().get_versions()
+    except lib_exc.NotFound:
+        # If not found, we use v2. The assumption is that either v1 or v2
+        # are available, since glance is marked as available in the catalog.
+        # If not, glance should be disabled in Tempest conf.
+        try:
+            versions = os.image_v2.VersionsClient().list_versions()['versions']
+            versions = [x['id'] for x in versions]
+        except lib_exc.NotFound:
+            msg = ('Glance is available in the catalog, but no known version, '
+                   '(v1.x or v2.x) of Glance could be found, so Glance should '
+                   'be configured as not available')
+            LOG.warn(msg)
+            print_and_or_update('glance', 'service-available', False, update)
+            return
+
     if CONF.image_feature_enabled.api_v1 != contains_version('v1.', versions):
         print_and_or_update('api_v1', 'image-feature-enabled',
                             not CONF.image_feature_enabled.api_v1, update)
@@ -92,10 +157,15 @@
 
 
 def _get_api_versions(os, service):
+    # Clients are used to obtain the base_url. Each client applies the
+    # appropriate filters to the catalog to extract a base_url which
+    # matches the configured region and endpoint_type.
+    # The base URL is used to obtain the list of versions available.
     client_dict = {
-        'nova': os.servers_client,
-        'keystone': os.identity_client,
-        'cinder': os.volumes_client_latest,
+        'nova': os.compute.ServersClient(),
+        'keystone': os.identity_v3.IdentityClient(
+            endpoint_type=CONF.identity.v3_endpoint_type),
+        'cinder': os.volume_v3.VolumesClient(),
     }
     if service != 'keystone' and service != 'cinder':
         # Since keystone and cinder may be listening on a path,
@@ -166,14 +236,15 @@
 
 
 def get_extension_client(os, service):
+    params = config.service_client_config('object-storage')
     extensions_client = {
-        'nova': os.extensions_client,
-        'neutron': os.network_extensions_client,
-        'swift': os.capabilities_client,
+        'nova': os.compute.ExtensionsClient(),
+        'neutron': os.network.ExtensionsClient(),
+        'swift': object_storage.CapabilitiesClient(os.auth_provider, **params),
         # NOTE: Cinder v3 API is current and v2 and v1 are deprecated.
         # V3 extension API is the same as v2, so we reuse the v2 client
         # for v3 API also.
-        'cinder': os.volumes_v2_extension_client,
+        'cinder': os.volume_v2.ExtensionsClient(),
     }
 
     if service not in extensions_client:
@@ -344,8 +415,8 @@
                         help="Output file to write an updated config file to. "
                              "This has to be a separate file from the "
                              "original config file. If one isn't specified "
-                             "with -u the new config file will be printed to "
-                             "STDOUT")
+                             "with -u the values which should be changed "
+                             "will be printed to STDOUT")
     parser.add_argument('-r', '--replace-ext', action='store_true',
                         help="If specified the all option will be replaced "
                              "with a full list of extensions")
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index e3fbfb8..47196ec 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -41,6 +41,27 @@
 LOG = logging.getLogger(__name__)
 
 
+def is_scheduler_filter_enabled(filter_name):
+    """Check the list of enabled compute scheduler filters from config.
+
+    This function checks whether the given compute scheduler filter is
+    available and configured in the config file. If the
+    scheduler_available_filters option is set to 'all' (Default value. which
+    means default filters are configured in nova) in tempest.conf then, this
+    function returns True with assumption that requested filter 'filter_name'
+    is one of available filter in nova ("nova.scheduler.filters.all_filters").
+    """
+
+    filters = CONF.compute_feature_enabled.scheduler_available_filters
+    if not filters:
+        return False
+    if 'all' in filters:
+        return True
+    if filter_name in filters:
+        return True
+    return False
+
+
 def create_test_server(clients, validatable=False, validation_resources=None,
                        tenant_network=None, wait_until=None,
                        volume_backed=False, name=None, flavor=None,
diff --git a/tempest/common/credentials_factory.py b/tempest/common/credentials_factory.py
index 5dc1a3e..a340531 100644
--- a/tempest/common/credentials_factory.py
+++ b/tempest/common/credentials_factory.py
@@ -44,6 +44,9 @@
         identity_uri = CONF.identity.uri_v3
     elif identity_version == 'v2':
         identity_uri = CONF.identity.uri
+    else:
+        raise exceptions.InvalidIdentityVersion(
+            identity_version=identity_version)
     return {
         'identity_version': identity_version,
         'identity_uri': identity_uri,
@@ -265,8 +268,6 @@
     return credentials
 
 
-# Wrapper around auth.get_credentials to use the configured identity version
-# if none is specified
 def get_credentials(fill_in=True, identity_version=None, **kwargs):
     """Get credentials from dict based on config
 
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 99a628e..52ccfa9 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -70,7 +70,7 @@
         if selected:
             return "\n".join(selected)
         else:
-            msg = "'TYPE' column is requred but the output doesn't have it: "
+            msg = "'TYPE' column is required but the output doesn't have it: "
             raise tempest.lib.exceptions.TempestException(msg + output)
 
     def get_boot_time(self):
diff --git a/tempest/common/validation_resources.py b/tempest/common/validation_resources.py
index 9e83a07..84f1c9d 100644
--- a/tempest/common/validation_resources.py
+++ b/tempest/common/validation_resources.py
@@ -13,20 +13,14 @@
 
 from oslo_log import log as logging
 
-from tempest import config
-
 from tempest.lib.common.utils import data_utils
 from tempest.lib import exceptions as lib_exc
 
-CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
 
-def _create_neutron_sec_group_rules(os, sec_group):
+def _create_neutron_sec_group_rules(os, sec_group, ethertype='IPv4'):
     sec_group_rules_client = os.security_group_rules_client
-    ethertype = 'IPv4'
-    if CONF.validation.ip_version_for_ssh == 6:
-        ethertype = 'IPv6'
 
     sec_group_rules_client.create_security_group_rule(
         security_group_id=sec_group['id'],
@@ -42,7 +36,8 @@
         direction='ingress')
 
 
-def create_ssh_security_group(os, add_rule=False):
+def create_ssh_security_group(os, add_rule=False, ethertype='IPv4',
+                              use_neutron=True):
     security_groups_client = os.compute_security_groups_client
     security_group_rules_client = os.compute_security_group_rules_client
     sg_name = data_utils.rand_name('securitygroup-')
@@ -50,8 +45,9 @@
     security_group = security_groups_client.create_security_group(
         name=sg_name, description=sg_description)['security_group']
     if add_rule:
-        if CONF.service_available.neutron:
-            _create_neutron_sec_group_rules(os, security_group)
+        if use_neutron:
+            _create_neutron_sec_group_rules(os, security_group,
+                                            ethertype=ethertype)
         else:
             security_group_rules_client.create_security_group_rule(
                 parent_group_id=security_group['id'], ip_protocol='tcp',
@@ -64,7 +60,10 @@
     return security_group
 
 
-def create_validation_resources(os, validation_resources=None):
+def create_validation_resources(os, validation_resources=None,
+                                ethertype='IPv4', use_neutron=True,
+                                floating_network_id=None,
+                                floating_network_name=None):
     # Create and Return the validation resources required to validate a VM
     validation_data = {}
     if validation_resources:
@@ -78,11 +77,12 @@
             if validation_resources['security_group_rules']:
                 add_rule = True
             validation_data['security_group'] = \
-                create_ssh_security_group(os, add_rule)
+                create_ssh_security_group(
+                    os, add_rule, use_neutron=use_neutron, ethertype=ethertype)
         if validation_resources['floating_ip']:
-            if CONF.service_available.neutron:
+            if use_neutron:
                 floatingip = os.floating_ips_client.create_floatingip(
-                    floating_network_id=CONF.network.public_network_id)
+                    floating_network_id=floating_network_id)
                 # validation_resources['floating_ip'] has historically looked
                 # like a compute API POST /os-floating-ips response, so we need
                 # to mangle it a bit for a Neutron response with different
@@ -96,7 +96,7 @@
                 # floating IPs using the compute API should be capped at 2.35.
                 validation_data.update(
                     os.compute_floating_ips_client.create_floating_ip(
-                        pool=CONF.network.floating_network_name))
+                        pool=floating_network_name))
     return validation_data
 
 
diff --git a/tempest/hacking/checks.py b/tempest/hacking/checks.py
index 067da09..aae685c 100644
--- a/tempest/hacking/checks.py
+++ b/tempest/hacking/checks.py
@@ -33,6 +33,7 @@
 METHOD_GET_RESOURCE = re.compile(r"^\s*def (list|show)\_.+")
 METHOD_DELETE_RESOURCE = re.compile(r"^\s*def delete_.+")
 CLASS = re.compile(r"^class .+")
+EX_ATTRIBUTE = re.compile(r'(\s+|\()(e|ex|exc|exception).message(\s+|\))')
 
 
 def import_no_clients_in_api_and_scenario_tests(physical_line, filename):
@@ -294,6 +295,17 @@
         yield(0, msg)
 
 
+def unsupported_exception_attribute_PY3(logical_line):
+    """Check Unsupported 'message' exception attribute in PY3
+
+    T116
+    """
+    result = EX_ATTRIBUTE.search(logical_line)
+    msg = ("[T116] Unsupported 'message' Exception attribute in PY3")
+    if result:
+        yield(0, msg)
+
+
 def factory(register):
     register(import_no_clients_in_api_and_scenario_tests)
     register(scenario_tests_need_service_tags)
@@ -309,3 +321,4 @@
     register(dont_use_config_in_tempest_lib)
     register(use_rand_uuid_instead_of_uuid4)
     register(dont_put_admin_tests_on_nonadmin_path)
+    register(unsupported_exception_attribute_PY3)
diff --git a/tempest/lib/services/compute/services_client.py b/tempest/lib/services/compute/services_client.py
index 857c435..b046c35 100644
--- a/tempest/lib/services/compute/services_client.py
+++ b/tempest/lib/services/compute/services_client.py
@@ -78,7 +78,7 @@
 
         For a full list of available parameters, please refer to the official
         API reference:
-        https://developer.openstack.org/api-ref/compute/#log-disabled-compute-service-information
+        https://developer.openstack.org/api-ref/compute/#disable-scheduling-for-a-compute-service-and-log-disabled-reason
         """
         post_body = json.dumps(kwargs)
         resp, body = self.put('os-services/disable-log-reason', post_body)
diff --git a/tempest/lib/services/identity/v3/catalog_client.py b/tempest/lib/services/identity/v3/catalog_client.py
index 0f9d485..232b85a 100644
--- a/tempest/lib/services/identity/v3/catalog_client.py
+++ b/tempest/lib/services/identity/v3/catalog_client.py
@@ -11,8 +11,7 @@
 #    under the License.
 
 """
-https://developer.openstack.org/api-ref/identity/v3/index.html#\
-get-service-catalog
+https://developer.openstack.org/api-ref/identity/v3/index.html#get-service-catalog
 """
 
 from oslo_serialization import jsonutils as json
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index 62b9992..e932adc 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -355,6 +355,15 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    def show_volume_image_metadata(self, volume_id):
+        """Show image metadata for the volume."""
+        post_body = json.dumps({'os-show_image_metadata': {}})
+        url = "volumes/%s/action" % volume_id
+        resp, body = self.post(url, post_body)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
     @removals.remove(message="use list_pools from tempest.lib.services."
                              "volume.v2.scheduler_stats_client")
     def show_pools(self, detail=False):
diff --git a/tempest/lib/services/volume/v3/groups_client.py b/tempest/lib/services/volume/v3/groups_client.py
index 9b53bb7..b463fdf 100644
--- a/tempest/lib/services/volume/v3/groups_client.py
+++ b/tempest/lib/services/volume/v3/groups_client.py
@@ -84,6 +84,31 @@
         self.expected_success(200, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    def create_group_from_source(self, **kwargs):
+        """Creates a group from source.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#create-group-from-source
+        """
+        post_body = json.dumps({'create-from-src': kwargs})
+        resp, body = self.post('groups/action', post_body)
+        body = json.loads(body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def update_group(self, group_id, **kwargs):
+        """Updates the specified group.
+
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://developer.openstack.org/api-ref/block-storage/v3/#update-group
+        """
+        put_body = json.dumps({'group': kwargs})
+        resp, body = self.put('groups/%s' % group_id, put_body)
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
     def is_resource_deleted(self, id):
         try:
             self.show_group(id)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 9b8c7a0..2843222 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -79,8 +79,10 @@
         cls.security_groups_client = cls.os_primary.security_groups_client
         cls.security_group_rules_client = (
             cls.os_primary.security_group_rules_client)
-        cls.volumes_client = cls.os_primary.volumes_v2_client
-        cls.snapshots_client = cls.os_primary.snapshots_v2_client
+        # Use the latest available volume clients
+        if CONF.service_available.cinder:
+            cls.volumes_client = cls.os_primary.volumes_client_latest
+            cls.snapshots_client = cls.os_primary.snapshots_client_latest
 
     # ## Test functions library
     #
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 41c60f1..51716e8 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -15,6 +15,7 @@
 from oslo_log import log
 import testtools
 
+from tempest.common import compute
 from tempest.common.utils import net_info
 from tempest import config
 from tempest.lib.common.utils import data_utils
@@ -162,7 +163,7 @@
         super(TestSecurityGroupsBasicOps, cls).resource_setup()
 
         cls.multi_node = CONF.compute.min_compute_nodes > 1 and \
-            test.is_scheduler_filter_enabled("DifferentHostFilter")
+            compute.is_scheduler_filter_enabled("DifferentHostFilter")
         if cls.multi_node:
             LOG.info("Working in Multi Node mode")
         else:
diff --git a/tempest/test.py b/tempest/test.py
index 317c0a7..47cbb5e 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -44,11 +44,6 @@
     version='Mitaka', removal_version='?')
 
 
-related_bug = debtcollector.moves.moved_function(
-    decorators.related_bug, 'related_bug', __name__,
-    version='Pike', removal_version='?')
-
-
 attr = debtcollector.moves.moved_function(
     decorators.attr, 'attr', __name__,
     version='Pike', removal_version='?')
@@ -144,27 +139,6 @@
     return False
 
 
-def is_scheduler_filter_enabled(filter_name):
-    """Check the list of enabled compute scheduler filters from config.
-
-    This function checks whether the given compute scheduler filter is
-    available and configured in the config file. If the
-    scheduler_available_filters option is set to 'all' (Default value. which
-    means default filters are configured in nova) in tempest.conf then, this
-    function returns True with assumption that requested filter 'filter_name'
-    is one of available filter in nova ("nova.scheduler.filters.all_filters").
-    """
-
-    filters = CONF.compute_feature_enabled.scheduler_available_filters
-    if not filters:
-        return False
-    if 'all' in filters:
-        return True
-    if filter_name in filters:
-        return True
-    return False
-
-
 at_exit_set = set()
 
 
@@ -399,9 +373,18 @@
     @classmethod
     def resource_setup(cls):
         """Class level resource setup for test cases."""
+        if (CONF.validation.ip_version_for_ssh not in (4, 6) and
+            CONF.service_available.neutron):
+            msg = "Invalid IP version %s in ip_version_for_ssh. Use 4 or 6"
+            raise lib_exc.InvalidConfiguration(
+                msg % CONF.validation.ip_version_for_ssh)
         if hasattr(cls, "os_primary"):
             cls.validation_resources = vresources.create_validation_resources(
-                cls.os_primary, cls.validation_resources)
+                cls.os_primary, cls.validation_resources,
+                use_neutron=CONF.service_available.neutron,
+                ethertype='IPv' + str(CONF.validation.ip_version_for_ssh),
+                floating_network_id=CONF.network.public_network_id,
+                floating_network_name=CONF.network.floating_network_name)
         else:
             LOG.warning("Client manager not found, validation resources not"
                         " created")
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 1415111..810f9e5 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -16,9 +16,13 @@
 import mock
 from oslo_serialization import jsonutils as json
 
+from tempest import clients
 from tempest.cmd import verify_tempest_config
+from tempest.common import credentials_factory
 from tempest import config
+from tempest.lib.common import rest_client
 from tempest.lib.common.utils import data_utils
+from tempest.lib import exceptions as lib_exc
 from tempest.tests import base
 from tempest.tests import fake_config
 
@@ -234,10 +238,15 @@
         print_mock.assert_not_called()
 
     def test_verify_glance_version_no_v2_with_v1_1(self):
-        def fake_get_versions():
-            return (None, ['v1.1'])
+        # This test verifies that wrong config api_v2 = True is detected
+        class FakeClient(object):
+            def get_versions(self):
+                return (None, ['v1.0'])
+
         fake_os = mock.MagicMock()
-        fake_os.image_client.get_versions = fake_get_versions
+        fake_module = mock.MagicMock()
+        fake_module.ImagesClient = FakeClient
+        fake_os.image_v1 = fake_module
         with mock.patch.object(verify_tempest_config,
                                'print_and_or_update') as print_mock:
             verify_tempest_config.verify_glance_api_versions(fake_os, True)
@@ -245,10 +254,15 @@
                                            False, True)
 
     def test_verify_glance_version_no_v2_with_v1_0(self):
-        def fake_get_versions():
-            return (None, ['v1.0'])
+        # This test verifies that wrong config api_v2 = True is detected
+        class FakeClient(object):
+            def get_versions(self):
+                return (None, ['v1.0'])
+
         fake_os = mock.MagicMock()
-        fake_os.image_client.get_versions = fake_get_versions
+        fake_module = mock.MagicMock()
+        fake_module.ImagesClient = FakeClient
+        fake_os.image_v1 = fake_module
         with mock.patch.object(verify_tempest_config,
                                'print_and_or_update') as print_mock:
             verify_tempest_config.verify_glance_api_versions(fake_os, True)
@@ -256,24 +270,59 @@
                                            False, True)
 
     def test_verify_glance_version_no_v1(self):
-        def fake_get_versions():
-            return (None, ['v2.0'])
+        # This test verifies that wrong config api_v1 = True is detected
+        class FakeClient(object):
+            def get_versions(self):
+                raise lib_exc.NotFound()
+
+            def list_versions(self):
+                return {'versions': [{'id': 'v2.0'}]}
+
         fake_os = mock.MagicMock()
-        fake_os.image_client.get_versions = fake_get_versions
+        fake_module = mock.MagicMock()
+        fake_module.ImagesClient = FakeClient
+        fake_module.VersionsClient = FakeClient
+        fake_os.image_v1 = fake_module
+        fake_os.image_v2 = fake_module
         with mock.patch.object(verify_tempest_config,
                                'print_and_or_update') as print_mock:
             verify_tempest_config.verify_glance_api_versions(fake_os, True)
         print_mock.assert_called_once_with('api_v1', 'image-feature-enabled',
                                            False, True)
 
+    def test_verify_glance_version_no_version(self):
+        # This test verifies that wrong config api_v1 = True is detected
+        class FakeClient(object):
+            def get_versions(self):
+                raise lib_exc.NotFound()
+
+            def list_versions(self):
+                raise lib_exc.NotFound()
+
+        fake_os = mock.MagicMock()
+        fake_module = mock.MagicMock()
+        fake_module.ImagesClient = FakeClient
+        fake_module.VersionsClient = FakeClient
+        fake_os.image_v1 = fake_module
+        fake_os.image_v2 = fake_module
+        with mock.patch.object(verify_tempest_config,
+                               'print_and_or_update') as print_mock:
+            verify_tempest_config.verify_glance_api_versions(fake_os, True)
+        print_mock.assert_called_once_with('glance',
+                                           'service-available',
+                                           False, True)
+
     def test_verify_extensions_neutron(self):
         def fake_list_extensions():
             return {'extensions': [{'alias': 'fake1'},
                                    {'alias': 'fake2'},
                                    {'alias': 'not_fake'}]}
         fake_os = mock.MagicMock()
-        fake_os.network_extensions_client.list_extensions = (
-            fake_list_extensions)
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
         self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['fake1', 'fake2', 'fake3'])))
@@ -295,8 +344,11 @@
                                    {'alias': 'fake2'},
                                    {'alias': 'not_fake'}]}
         fake_os = mock.MagicMock()
-        fake_os.network_extensions_client.list_extensions = (
-            fake_list_extensions)
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
         self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['all'])))
@@ -313,15 +365,17 @@
                                    {'alias': 'fake2'},
                                    {'alias': 'not_fake'}]}
         fake_os = mock.MagicMock()
-        # NOTE (e0ne): mock both v1 and v2 APIs
-        fake_os.volumes_extension_client.list_extensions = fake_list_extensions
-        fake_os.volumes_v2_extension_client.list_extensions = (
-            fake_list_extensions)
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
         self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['fake1', 'fake2', 'fake3'])))
         results = verify_tempest_config.verify_extensions(fake_os,
                                                           'cinder', {})
+
         self.assertIn('cinder', results)
         self.assertIn('fake1', results['cinder'])
         self.assertTrue(results['cinder']['fake1'])
@@ -338,10 +392,11 @@
                                    {'alias': 'fake2'},
                                    {'alias': 'not_fake'}]}
         fake_os = mock.MagicMock()
-        # NOTE (e0ne): mock both v1 and v2 APIs
-        fake_os.volumes_extension_client.list_extensions = fake_list_extensions
-        fake_os.volumes_v2_extension_client.list_extensions = (
-            fake_list_extensions)
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
         self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['all'])))
@@ -357,7 +412,11 @@
             return ([{'alias': 'fake1'}, {'alias': 'fake2'},
                      {'alias': 'not_fake'}])
         fake_os = mock.MagicMock()
-        fake_os.extensions_client.list_extensions = fake_list_extensions
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
         self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['fake1', 'fake2', 'fake3'])))
@@ -379,7 +438,11 @@
                                     {'alias': 'fake2'},
                                     {'alias': 'not_fake'}]})
         fake_os = mock.MagicMock()
-        fake_os.extensions_client.list_extensions = fake_list_extensions
+        fake_client = mock.MagicMock()
+        fake_client.list_extensions = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
         self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['all'])))
@@ -397,7 +460,11 @@
                     'not_fake': 'metadata',
                     'swift': 'metadata'}
         fake_os = mock.MagicMock()
-        fake_os.capabilities_client.list_capabilities = fake_list_extensions
+        fake_client = mock.MagicMock()
+        fake_client.list_capabilities = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
         self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['fake1', 'fake2', 'fake3'])))
@@ -419,7 +486,11 @@
                     'not_fake': 'metadata',
                     'swift': 'metadata'}
         fake_os = mock.MagicMock()
-        fake_os.capabilities_client.list_capabilities = fake_list_extensions
+        fake_client = mock.MagicMock()
+        fake_client.list_capabilities = fake_list_extensions
+        self.useFixture(fixtures.MockPatchObject(
+            verify_tempest_config, 'get_extension_client',
+            return_value=fake_client))
         self.useFixture(fixtures.MockPatchObject(
             verify_tempest_config, 'get_enabled_extensions',
             return_value=(['all'])))
@@ -429,3 +500,13 @@
         self.assertIn('extensions', results['swift'])
         self.assertEqual(sorted(['not_fake', 'fake1', 'fake2']),
                          sorted(results['swift']['extensions']))
+
+    def test_get_extension_client(self):
+        creds = credentials_factory.get_credentials(
+            fill_in=False, username='fake_user', project_name='fake_project',
+            password='fake_password')
+        os = clients.Manager(creds)
+        for service in ['nova', 'neutron', 'swift', 'cinder']:
+            extensions_client = verify_tempest_config.get_extension_client(
+                os, service)
+            self.assertIsInstance(extensions_client, rest_client.RestClient)
diff --git a/tempest/tests/common/test_credentials_factory.py b/tempest/tests/common/test_credentials_factory.py
new file mode 100644
index 0000000..020818e
--- /dev/null
+++ b/tempest/tests/common/test_credentials_factory.py
@@ -0,0 +1,279 @@
+# Copyright 2017 IBM Corp.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import mock
+from oslo_config import cfg
+import testtools
+
+from tempest.common import credentials_factory as cf
+from tempest import config
+from tempest.lib.common import dynamic_creds
+from tempest.lib.common import preprov_creds
+from tempest.lib import exceptions
+from tempest.tests import base
+from tempest.tests import fake_config
+from tempest.tests.lib import fake_credentials
+
+
+class TestCredentialsFactory(base.TestCase):
+
+    def setUp(self):
+        super(TestCredentialsFactory, self).setUp()
+        self.useFixture(fake_config.ConfigFixture())
+        self.patchobject(config, 'TempestConfigPrivate',
+                         fake_config.FakePrivate)
+
+    def test_get_dynamic_provider_params_creds_v2(self):
+        expected_uri = 'EXPECTED_V2_URI'
+        cfg.CONF.set_default('uri', expected_uri, group='identity')
+        admin_creds = fake_credentials.FakeCredentials()
+        params = cf.get_dynamic_provider_params('v2', admin_creds=admin_creds)
+        expected_params = dict(identity_uri=expected_uri,
+                               admin_creds=admin_creds)
+        for key in expected_params:
+            self.assertIn(key, params)
+            self.assertEqual(expected_params[key], params[key])
+
+    def test_get_dynamic_provider_params_creds_v3(self):
+        expected_uri = 'EXPECTED_V3_URI'
+        cfg.CONF.set_default('uri_v3', expected_uri, group='identity')
+        admin_creds = fake_credentials.FakeCredentials()
+        params = cf.get_dynamic_provider_params('v3', admin_creds=admin_creds)
+        expected_params = dict(identity_uri=expected_uri,
+                               admin_creds=admin_creds)
+        for key in expected_params:
+            self.assertIn(key, params)
+            self.assertEqual(expected_params[key], params[key])
+
+    def test_get_dynamic_provider_params_creds_vx(self):
+        admin_creds = fake_credentials.FakeCredentials()
+        invalid_version = 'invalid_version_x'
+        with testtools.ExpectedException(
+                exc_type=exceptions.InvalidIdentityVersion,
+                value_re='Invalid version ' + invalid_version):
+            cf.get_dynamic_provider_params(invalid_version,
+                                           admin_creds=admin_creds)
+
+    def test_get_dynamic_provider_params_no_creds(self):
+        expected_identity_version = 'v3'
+        with mock.patch.object(
+                cf, 'get_configured_admin_credentials') as admin_creds_mock:
+            cf.get_dynamic_provider_params(expected_identity_version)
+            admin_creds_mock.assert_called_once_with(
+                fill_in=True, identity_version=expected_identity_version)
+
+    def test_get_preprov_provider_params_creds_v2(self):
+        expected_uri = 'EXPECTED_V2_URI'
+        cfg.CONF.set_default('uri', expected_uri, group='identity')
+        params = cf.get_preprov_provider_params('v2')
+        self.assertIn('identity_uri', params)
+        self.assertEqual(expected_uri, params['identity_uri'])
+
+    def test_get_preprov_provider_params_creds_v3(self):
+        expected_uri = 'EXPECTED_V3_URI'
+        cfg.CONF.set_default('uri_v3', expected_uri, group='identity')
+        params = cf.get_preprov_provider_params('v3')
+        self.assertIn('identity_uri', params)
+        self.assertEqual(expected_uri, params['identity_uri'])
+
+    def test_get_preprov_provider_params_creds_vx(self):
+        invalid_version = 'invalid_version_x'
+        with testtools.ExpectedException(
+                exc_type=exceptions.InvalidIdentityVersion,
+                value_re='Invalid version ' + invalid_version):
+            cf.get_dynamic_provider_params(invalid_version)
+
+    @mock.patch.object(dynamic_creds, 'DynamicCredentialProvider')
+    @mock.patch.object(cf, 'get_dynamic_provider_params')
+    def test_get_credentials_provider_dynamic(
+            self, mock_dynamic_provider_params,
+            mock_dynamic_credentials_provider_class):
+        cfg.CONF.set_default('use_dynamic_credentials', True, group='auth')
+        expected_params = {'foo': 'bar'}
+        mock_dynamic_provider_params.return_value = expected_params
+        expected_name = 'my_name'
+        expected_network_resources = {'network': 'resources'}
+        expected_identity_version = 'identity_version'
+        cf.get_credentials_provider(
+            expected_name,
+            network_resources=expected_network_resources,
+            force_tenant_isolation=False,
+            identity_version=expected_identity_version)
+        mock_dynamic_provider_params.assert_called_once_with(
+            expected_identity_version)
+        mock_dynamic_credentials_provider_class.assert_called_once_with(
+            name=expected_name, network_resources=expected_network_resources,
+            **expected_params)
+
+    @mock.patch.object(preprov_creds, 'PreProvisionedCredentialProvider')
+    @mock.patch.object(cf, 'get_preprov_provider_params')
+    def test_get_credentials_provider_preprov(
+            self, mock_preprov_provider_params,
+            mock_preprov_credentials_provider_class):
+        cfg.CONF.set_default('use_dynamic_credentials', False, group='auth')
+        cfg.CONF.set_default('test_accounts_file', '/some/file', group='auth')
+        expected_params = {'foo': 'bar'}
+        mock_preprov_provider_params.return_value = expected_params
+        expected_name = 'my_name'
+        expected_identity_version = 'identity_version'
+        cf.get_credentials_provider(
+            expected_name,
+            force_tenant_isolation=False,
+            identity_version=expected_identity_version)
+        mock_preprov_provider_params.assert_called_once_with(
+            expected_identity_version)
+        mock_preprov_credentials_provider_class.assert_called_once_with(
+            name=expected_name, **expected_params)
+
+    def test_get_credentials_provider_preprov_no_file(self):
+        cfg.CONF.set_default('use_dynamic_credentials', False, group='auth')
+        cfg.CONF.set_default('test_accounts_file', None, group='auth')
+        with testtools.ExpectedException(
+                exc_type=exceptions.InvalidConfiguration):
+            cf.get_credentials_provider(
+                'some_name',
+                force_tenant_isolation=False,
+                identity_version='some_version')
+
+    @mock.patch.object(dynamic_creds, 'DynamicCredentialProvider')
+    @mock.patch.object(cf, 'get_dynamic_provider_params')
+    def test_get_credentials_provider_force_dynamic(
+            self, mock_dynamic_provider_params,
+            mock_dynamic_credentials_provider_class):
+        cfg.CONF.set_default('use_dynamic_credentials', False, group='auth')
+        expected_params = {'foo': 'bar'}
+        mock_dynamic_provider_params.return_value = expected_params
+        expected_name = 'my_name'
+        expected_network_resources = {'network': 'resources'}
+        expected_identity_version = 'identity_version'
+        cf.get_credentials_provider(
+            expected_name,
+            network_resources=expected_network_resources,
+            force_tenant_isolation=True,
+            identity_version=expected_identity_version)
+        mock_dynamic_provider_params.assert_called_once_with(
+            expected_identity_version)
+        mock_dynamic_credentials_provider_class.assert_called_once_with(
+            name=expected_name, network_resources=expected_network_resources,
+            **expected_params)
+
+    @mock.patch.object(cf, 'get_credentials')
+    def test_get_configured_admin_credentials(self, mock_get_credentials):
+        cfg.CONF.set_default('auth_version', 'v3', 'identity')
+        all_params = [('admin_username', 'username', 'my_name'),
+                      ('admin_password', 'password', 'secret'),
+                      ('admin_project_name', 'project_name', 'my_pname'),
+                      ('admin_domain_name', 'domain_name', 'my_dname')]
+        expected_result = 'my_admin_credentials'
+        mock_get_credentials.return_value = expected_result
+        for config_item, _, value in all_params:
+            cfg.CONF.set_default(config_item, value, 'auth')
+        # Build the expected params
+        expected_params = dict(
+            [(field, value) for _, field, value in all_params])
+        expected_params.update(cf.DEFAULT_PARAMS)
+        admin_creds = cf.get_configured_admin_credentials()
+        mock_get_credentials.assert_called_once_with(
+            fill_in=True, identity_version='v3', **expected_params)
+        self.assertEqual(expected_result, admin_creds)
+
+    @mock.patch.object(cf, 'get_credentials')
+    def test_get_configured_admin_credentials_not_fill_valid(
+            self, mock_get_credentials):
+        cfg.CONF.set_default('auth_version', 'v2', 'identity')
+        all_params = [('admin_username', 'username', 'my_name'),
+                      ('admin_password', 'password', 'secret'),
+                      ('admin_project_name', 'project_name', 'my_pname'),
+                      ('admin_domain_name', 'domain_name', 'my_dname')]
+        expected_result = mock.Mock()
+        expected_result.is_valid.return_value = True
+        mock_get_credentials.return_value = expected_result
+        for config_item, _, value in all_params:
+            cfg.CONF.set_default(config_item, value, 'auth')
+        # Build the expected params
+        expected_params = dict(
+            [(field, value) for _, field, value in all_params])
+        expected_params.update(cf.DEFAULT_PARAMS)
+        admin_creds = cf.get_configured_admin_credentials(
+            fill_in=False, identity_version='v3')
+        mock_get_credentials.assert_called_once_with(
+            fill_in=False, identity_version='v3', **expected_params)
+        self.assertEqual(expected_result, admin_creds)
+        expected_result.is_valid.assert_called_once()
+
+    @mock.patch.object(cf, 'get_credentials')
+    def test_get_configured_admin_credentials_not_fill_not_valid(
+            self, mock_get_credentials):
+        cfg.CONF.set_default('auth_version', 'v2', 'identity')
+        expected_result = mock.Mock()
+        expected_result.is_valid.return_value = False
+        mock_get_credentials.return_value = expected_result
+        with testtools.ExpectedException(exceptions.InvalidConfiguration,
+                                         value_re='.*\n.*identity version v2'):
+            cf.get_configured_admin_credentials(fill_in=False)
+
+    @mock.patch('tempest.lib.auth.get_credentials')
+    def test_get_credentials_v2(self, mock_auth_get_credentials):
+        expected_uri = 'V2_URI'
+        expected_result = 'my_creds'
+        mock_auth_get_credentials.return_value = expected_result
+        cfg.CONF.set_default('uri', expected_uri, 'identity')
+        params = {'foo': 'bar'}
+        expected_params = params.copy()
+        expected_params.update(cf.DEFAULT_PARAMS)
+        result = cf.get_credentials(identity_version='v2', **params)
+        self.assertEqual(expected_result, result)
+        mock_auth_get_credentials.assert_called_once_with(
+            expected_uri, fill_in=True, identity_version='v2',
+            **expected_params)
+
+    @mock.patch('tempest.lib.auth.get_credentials')
+    def test_get_credentials_v3_no_domain(self, mock_auth_get_credentials):
+        expected_uri = 'V3_URI'
+        expected_result = 'my_creds'
+        expected_domain = 'my_domain'
+        mock_auth_get_credentials.return_value = expected_result
+        cfg.CONF.set_default('uri_v3', expected_uri, 'identity')
+        cfg.CONF.set_default('default_credentials_domain_name',
+                             expected_domain, 'auth')
+        params = {'foo': 'bar'}
+        expected_params = params.copy()
+        expected_params['domain_name'] = expected_domain
+        expected_params.update(cf.DEFAULT_PARAMS)
+        result = cf.get_credentials(fill_in=False, identity_version='v3',
+                                    **params)
+        self.assertEqual(expected_result, result)
+        mock_auth_get_credentials.assert_called_once_with(
+            expected_uri, fill_in=False, identity_version='v3',
+            **expected_params)
+
+    @mock.patch('tempest.lib.auth.get_credentials')
+    def test_get_credentials_v3_domain(self, mock_auth_get_credentials):
+        expected_uri = 'V3_URI'
+        expected_result = 'my_creds'
+        expected_domain = 'my_domain'
+        mock_auth_get_credentials.return_value = expected_result
+        cfg.CONF.set_default('uri_v3', expected_uri, 'identity')
+        cfg.CONF.set_default('default_credentials_domain_name',
+                             expected_domain, 'auth')
+        params = {'foo': 'bar', 'user_domain_name': expected_domain}
+        expected_params = params.copy()
+        expected_params.update(cf.DEFAULT_PARAMS)
+        result = cf.get_credentials(fill_in=False, identity_version='v3',
+                                    **params)
+        self.assertEqual(expected_result, result)
+        mock_auth_get_credentials.assert_called_once_with(
+            expected_uri, fill_in=False, identity_version='v3',
+            **expected_params)
diff --git a/tempest/tests/lib/common/test_api_version_utils.py b/tempest/tests/lib/common/test_api_version_utils.py
index 6206379..c063556 100644
--- a/tempest/tests/lib/common/test_api_version_utils.py
+++ b/tempest/tests/lib/common/test_api_version_utils.py
@@ -12,6 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import six
 import testtools
 
 from tempest.lib.common import api_version_utils
@@ -30,7 +31,7 @@
                                                            cfg_max_version)
         except testtools.TestCase.skipException as e:
             if not expected_skip:
-                raise testtools.TestCase.failureException(e.message)
+                raise testtools.TestCase.failureException(six.text_type(e))
 
     def test_version_min_in_range(self):
         self._test_version('2.2', '2.10', '2.1', '2.7')
diff --git a/tempest/tests/lib/services/image/v2/test_images_client.py b/tempest/tests/lib/services/image/v2/test_images_client.py
index 9648985..ee4d4cb 100644
--- a/tempest/tests/lib/services/image/v2/test_images_client.py
+++ b/tempest/tests/lib/services/image/v2/test_images_client.py
@@ -12,6 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import six
+
+from tempest.lib.common.utils import data_utils
 from tempest.lib.services.image.v2 import images_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
@@ -42,6 +45,57 @@
         "container_format": None
     }
 
+    FAKE_LIST_IMAGES = {
+        "images": [
+            {
+                "status": "active",
+                "name": "cirros-0.3.2-x86_64-disk",
+                "tags": [],
+                "container_format": "bare",
+                "created_at": "2014-11-07T17:07:06Z",
+                "disk_format": "qcow2",
+                "updated_at": "2014-11-07T17:19:09Z",
+                "visibility": "public",
+                "self": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27",
+                "min_disk": 0,
+                "protected": False,
+                "id": "1bea47ed-f6a9-463b-b423-14b9cca9ad27",
+                "file": "/v2/images/1bea47ed-f6a9-463b-b423-14b9cca9ad27/file",
+                "checksum": "64d7c1cd2b6f60c92c14662941cb7913",
+                "owner": "5ef70662f8b34079a6eddb8da9d75fe8",
+                "size": 13167616,
+                "min_ram": 0,
+                "schema": "/v2/schemas/image",
+                "virtual_size": None
+            },
+            {
+                "status": "active",
+                "name": "F17-x86_64-cfntools",
+                "tags": [],
+                "container_format": "bare",
+                "created_at": "2014-10-30T08:23:39Z",
+                "disk_format": "qcow2",
+                "updated_at": "2014-11-03T16:40:10Z",
+                "visibility": "public",
+                "self": "/v2/images/781b3762-9469-4cec-b58d-3349e5de4e9c",
+                "min_disk": 0,
+                "protected": False,
+                "id": "781b3762-9469-4cec-b58d-3349e5de4e9c",
+                "file": "/v2/images/781b3762-9469-4cec-b58d-3349e5de4e9c/file",
+                "checksum": "afab0f79bac770d61d24b4d0560b5f70",
+                "owner": "5ef70662f8b34079a6eddb8da9d75fe8",
+                "size": 476704768,
+                "min_ram": 0,
+                "schema": "/v2/schemas/image",
+                "virtual_size": None
+            }
+        ],
+        "schema": "/v2/schemas/images",
+        "first": "/v2/images"
+    }
+
+    FAKE_TAG_NAME = "fake tag"
+
     def setUp(self):
         super(TestImagesClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -74,6 +128,14 @@
             bytes_body,
             image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8")
 
+    def _test_list_images(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_images,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_LIST_IMAGES,
+            bytes_body,
+            mock_args=['images'])
+
     def test_create_image_with_str_body(self):
         self._test_create_image()
 
@@ -104,8 +166,56 @@
             'tempest.lib.common.rest_client.RestClient.delete',
             {}, image_id="e485aab9-0907-4973-921c-bb6da8a8fcf8", status=204)
 
+    def test_store_image_file(self):
+        data = six.BytesIO(data_utils.random_bytes())
+
+        self.check_service_client_function(
+            self.client.store_image_file,
+            'tempest.lib.common.rest_client.RestClient.raw_request',
+            {},
+            image_id=self.FAKE_CREATE_UPDATE_SHOW_IMAGE["id"],
+            status=204,
+            data=data)
+
+    def test_show_image_file(self):
+        # NOTE: The response for this API returns raw binary data, but an error
+        # is thrown if random bytes are used for the resp body since
+        # ``create_response`` then calls ``json.dumps``.
+        self.check_service_client_function(
+            self.client.show_image_file,
+            'tempest.lib.common.rest_client.RestClient.get',
+            {},
+            resp_as_string=True,
+            image_id=self.FAKE_CREATE_UPDATE_SHOW_IMAGE["id"],
+            headers={'Content-Type': 'application/octet-stream'},
+            status=200)
+
+    def test_add_image_tag(self):
+        self.check_service_client_function(
+            self.client.add_image_tag,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            image_id=self.FAKE_CREATE_UPDATE_SHOW_IMAGE["id"],
+            status=204,
+            tag=self.FAKE_TAG_NAME)
+
+    def test_delete_image_tag(self):
+        self.check_service_client_function(
+            self.client.delete_image_tag,
+            'tempest.lib.common.rest_client.RestClient.delete',
+            {},
+            image_id=self.FAKE_CREATE_UPDATE_SHOW_IMAGE["id"],
+            status=204,
+            tag=self.FAKE_TAG_NAME)
+
     def test_show_image_with_str_body(self):
         self._test_show_image()
 
     def test_show_image_with_bytes_body(self):
         self._test_show_image(bytes_body=True)
+
+    def test_list_images_with_str_body(self):
+        self._test_list_images()
+
+    def test_list_images_with_bytes_body(self):
+        self._test_list_images(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_availability_zone_client.py b/tempest/tests/lib/services/volume/v2/test_availability_zone_client.py
new file mode 100644
index 0000000..770565c
--- /dev/null
+++ b/tempest/tests/lib/services/volume/v2/test_availability_zone_client.py
@@ -0,0 +1,51 @@
+# Copyright 2017 FiberHome Telecommunication Technologies CO.,LTD
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.services.volume.v2 import availability_zone_client
+from tempest.tests.lib import fake_auth_provider
+from tempest.tests.lib.services import base
+
+
+class TestAvailabilityZoneClient(base.BaseServiceTest):
+
+    FAKE_AZ_LIST = {
+        "availabilityZoneInfo": [
+            {
+                "zoneState": {
+                    "available": True
+                },
+                "zoneName": "nova"
+            }
+        ]
+    }
+
+    def setUp(self):
+        super(TestAvailabilityZoneClient, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = availability_zone_client.AvailabilityZoneClient(
+            fake_auth, 'volume', 'regionOne')
+
+    def _test_list_availability_zones(self, bytes_body=False):
+        self.check_service_client_function(
+            self.client.list_availability_zones,
+            'tempest.lib.common.rest_client.RestClient.get',
+            self.FAKE_AZ_LIST,
+            bytes_body)
+
+    def test_list_availability_zones_with_str_body(self):
+        self._test_list_availability_zones()
+
+    def test_list_availability_zones_with_bytes_body(self):
+        self._test_list_availability_zones(bytes_body=True)
diff --git a/tempest/tests/lib/services/volume/v2/test_volumes_client.py b/tempest/tests/lib/services/volume/v2/test_volumes_client.py
index e53e0a2..d7b042e 100644
--- a/tempest/tests/lib/services/volume/v2/test_volumes_client.py
+++ b/tempest/tests/lib/services/volume/v2/test_volumes_client.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from oslo_serialization import jsonutils as json
+
 from tempest.lib.services.volume.v2 import volumes_client
 from tempest.tests.lib import fake_auth_provider
 from tempest.tests.lib.services import base
@@ -26,6 +28,19 @@
         }
     }
 
+    FAKE_VOLUME_IMAGE_METADATA = {
+        "metadata": {
+            "container_format": "bare",
+            "min_ram": "0",
+            "disk_format": "raw",
+            "image_name": "xly-ubuntu16-server",
+            "image_id": "3e087b0c-10c5-4255-b147-6e8e9dbad6fc",
+            "checksum": "008f5d22fe3cb825d714da79607a90f9",
+            "min_disk": "0",
+            "size": "8589934592"
+        }
+    }
+
     def setUp(self):
         super(TestVolumesClient, self).setUp()
         fake_auth = fake_auth_provider.FakeAuthProvider()
@@ -76,6 +91,17 @@
             volume_id="a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8",
             id="key1")
 
+    def _test_show_volume_image_metadata(self, bytes_body=False):
+        fake_volume_id = "a3be971b-8de5-4bdf-bdb8-3d8eb0fb69f8"
+        self.check_service_client_function(
+            self.client.show_volume_image_metadata,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_VOLUME_IMAGE_METADATA,
+            to_utf=bytes_body,
+            mock_args=['volumes/%s/action' % fake_volume_id,
+                       json.dumps({"os-show_image_metadata": {}})],
+            volume_id=fake_volume_id)
+
     def test_force_detach_volume_with_str_body(self):
         self._test_force_detach_volume()
 
@@ -88,6 +114,12 @@
     def test_show_volume_metadata_item_with_bytes_body(self):
         self._test_show_volume_metadata_item(bytes_body=True)
 
+    def test_show_volume_image_metadata_with_str_body(self):
+        self._test_show_volume_image_metadata()
+
+    def test_show_volume_image_metadata_with_bytes_body(self):
+        self._test_show_volume_image_metadata(bytes_body=True)
+
     def test_retype_volume_with_str_body(self):
         self._test_retype_volume()
 
diff --git a/tempest/tests/lib/services/volume/v3/test_groups_client.py b/tempest/tests/lib/services/volume/v3/test_groups_client.py
index 00db5b4..0884e5a 100644
--- a/tempest/tests/lib/services/volume/v3/test_groups_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_groups_client.py
@@ -28,6 +28,33 @@
         }
     }
 
+    FAKE_CREATE_GROUP_FROM_GROUP_SNAPSHOT = {
+        "create-from-src": {
+            "name": "group-002",
+            "description": "Test group 2",
+            "group_snapshot_id": "79c9afdb-7e46-4d71-9249-1f022886963c",
+        }
+    }
+
+    FAKE_CREATE_GROUP_FROM_GROUP = {
+        "create-from-src": {
+            "name": "group-003",
+            "description": "Test group 3",
+            "source_group_id": "e92f9dc7-0b20-492d-8ab2-3ad8fdac270e",
+        }
+    }
+
+    FAKE_UPDATE_GROUP = {
+        "group": {
+            "name": "new-group",
+            "description": "New test group",
+            "add_volumes": "27d45037-ade3-4a87-b729-dba3293c06f3,"
+                           "6e7cd916-d961-41cc-b3bd-0601ca0c701f",
+            "remove_volumes": "4d580519-6467-448e-95e9-5b25c94d83c7,"
+                              "ea22464c-f095-4a87-a31f-c5d34e0c6fc9"
+        }
+    }
+
     FAKE_INFO_GROUP = {
         "group": {
             "id": "0e701ab8-1bec-4b9f-b026-a7ba4af13578",
@@ -134,3 +161,26 @@
             {},
             group_id='0e701ab8-1bec-4b9f-b026-a7ba4af13578',
             status=202)
+
+    def test_create_group_from_group_snapshot(self):
+        self.check_service_client_function(
+            self.client.create_group_from_source,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_GROUP_FROM_GROUP_SNAPSHOT,
+            status=202)
+
+    def test_create_group_from_group(self):
+        self.check_service_client_function(
+            self.client.create_group_from_source,
+            'tempest.lib.common.rest_client.RestClient.post',
+            self.FAKE_CREATE_GROUP_FROM_GROUP,
+            status=202)
+
+    def test_update_group(self):
+        self.check_service_client_function(
+            self.client.update_group,
+            'tempest.lib.common.rest_client.RestClient.put',
+            {},
+            group_id='0e701ab8-1bec-4b9f-b026-a7ba4af13578',
+            status=202,
+            **self.FAKE_UPDATE_GROUP['group'])
diff --git a/tempest/tests/test_hacking.py b/tempest/tests/test_hacking.py
index f005c21..c04d933 100644
--- a/tempest/tests/test_hacking.py
+++ b/tempest/tests/test_hacking.py
@@ -180,3 +180,15 @@
             'from oslo_config import cfg', './tempest/lib/decorators.py')))
         self.assertTrue(list(checks.dont_use_config_in_tempest_lib(
             'import tempest.config', './tempest/lib/common/rest_client.py')))
+
+    def test_unsupported_exception_attribute_PY3(self):
+        self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
+            "raise TestCase.failureException(e.message)"))), 1)
+        self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
+            "raise TestCase.failureException(ex.message)"))), 1)
+        self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
+            "raise TestCase.failureException(exc.message)"))), 1)
+        self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
+            "raise TestCase.failureException(exception.message)"))), 1)
+        self.assertEqual(len(list(checks.unsupported_exception_attribute_PY3(
+            "raise TestCase.failureException(ee.message)"))), 0)
diff --git a/tempest/tests/test_microversions.py b/tempest/tests/test_microversions.py
index 173accb..ee6db71 100644
--- a/tempest/tests/test_microversions.py
+++ b/tempest/tests/test_microversions.py
@@ -13,6 +13,7 @@
 #    under the License.
 
 from oslo_config import cfg
+import six
 import testtools
 
 from tempest.api.compute import base as compute_base
@@ -74,7 +75,7 @@
                 self.assertRaises(testtools.TestCase.skipException,
                                   test_class.skip_checks)
         except testtools.TestCase.skipException as e:
-            raise testtools.TestCase.failureException(e.message)
+            raise testtools.TestCase.failureException(six.text_type(e))
 
     def test_config_version_none_none(self):
         expected_pass_tests = [VersionTestNoneTolatest, VersionTestNoneTo2_2]