Merge "Stop passing around dicts in validation resources"
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/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/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-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/raise-exception-when-error-deleting-on-volume-18d0d0c5886212dd.yaml b/releasenotes/notes/raise-exception-when-error-deleting-on-volume-18d0d0c5886212dd.yaml
new file mode 100644
index 0000000..194dbc1
--- /dev/null
+++ b/releasenotes/notes/raise-exception-when-error-deleting-on-volume-18d0d0c5886212dd.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+  - |
+    Tempest checks a volume delete by waiting for NotFound(404) on
+    show_volume(). Sometime a volume delete fails and the volume status
+    becomes error_deleting which means the delete is failed.
+    So Tempest doesn't need to wait anymore. A new release of Tempest
+    raises an exception DeleteErrorException instead of waiting.
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_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_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_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/admin/test_servers_on_multinodes.py b/tempest/api/compute/admin/test_servers_on_multinodes.py
index 72f4ddc..2e7b07b 100644
--- a/tempest/api/compute/admin/test_servers_on_multinodes.py
+++ b/tempest/api/compute/admin/test_servers_on_multinodes.py
@@ -76,3 +76,38 @@
                                            wait_until='ACTIVE')['id']
         host02 = self._get_host(server02)
         self.assertNotEqual(self.host01, host02)
+
+    @decorators.idempotent_id('f8bd0867-e459-45f5-ba53-59134552fe04')
+    @testtools.skipUnless(
+        compute.is_scheduler_filter_enabled("ServerGroupAntiAffinityFilter"),
+        'ServerGroupAntiAffinityFilter is not available.')
+    def test_create_server_with_scheduler_hint_group_anti_affinity(self):
+        """Tests the ServerGroupAntiAffinityFilter
+
+        Creates two servers in an anti-affinity server group and
+        asserts the servers are in the group and on different hosts.
+        """
+        group_id = self.create_test_server_group(
+            policy=['anti-affinity'])['id']
+        hints = {'group': group_id}
+        reservation_id = self.create_test_server(
+            scheduler_hints=hints, wait_until='ACTIVE', min_count=2,
+            return_reservation_id=True)['reservation_id']
+
+        # Get the servers using the reservation_id.
+        servers = self.servers_client.list_servers(
+            detail=True, reservation_id=reservation_id)['servers']
+        self.assertEqual(2, len(servers))
+
+        # Assert the servers are in the group.
+        server_group = self.server_groups_client.show_server_group(
+            group_id)['server_group']
+        hosts = {}
+        for server in servers:
+            self.assertIn(server['id'], server_group['members'])
+            hosts[server['id']] = self._get_host(server['id'])
+
+        # Assert the servers are on different hosts.
+        hostnames = list(hosts.values())
+        self.assertNotEqual(hostnames[0], hostnames[1],
+                            'Servers are on the same host: %s' % hosts)
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/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_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 f41c3fb..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
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_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_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 850e549..2530072 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -26,25 +26,27 @@
 CONF = config.CONF
 
 
-class BaseTrustsV3Test(base.BaseIdentityV3AdminTest):
+class TrustsV3TestJSON(base.BaseIdentityV3AdminTest):
 
     @classmethod
     def skip_checks(cls):
-        super(BaseTrustsV3Test, cls).skip_checks()
+        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
         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
@@ -193,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/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 5f01166..baea37b 100644
--- a/tempest/api/volume/admin/test_groups.py
+++ b/tempest/api/volume/admin/test_groups.py
@@ -258,3 +258,65 @@
                     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/admin/test_volume_retype_with_migration.py b/tempest/api/volume/admin/test_volume_retype_with_migration.py
index 94d5299..f0b3a4f 100644
--- a/tempest/api/volume/admin/test_volume_retype_with_migration.py
+++ b/tempest/api/volume/admin/test_volume_retype_with_migration.py
@@ -85,9 +85,7 @@
         volume_source = self.admin_volume_client.show_volume(
             self.src_vol['id'])['volume']
 
-        # TODO(erlon): change this to volumes_client client after Bug
-        # #1657806 is fixed
-        self.admin_volume_client.retype_volume(
+        self.volumes_client.retype_volume(
             self.src_vol['id'],
             new_type=self.dst_vol_type['name'],
             migration_policy='on-demand')
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/common/credentials_factory.py b/tempest/common/credentials_factory.py
index cecb8e3..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,
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/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/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index e932adc..d13e449 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -197,10 +197,18 @@
         return rest_client.ResponseBody(resp, body)
 
     def is_resource_deleted(self, id):
+        """Check the specified resource is deleted or not.
+
+        :param id: A checked resource id
+        :raises lib_exc.DeleteErrorException: If the specified resource is on
+        the status the delete was failed.
+        """
         try:
-            self.show_volume(id)
+            volume = self.show_volume(id)
         except lib_exc.NotFound:
             return True
+        if volume["volume"]["status"] == "error_deleting":
+            raise lib_exc.DeleteErrorException(resource_id=id)
         return False
 
     @property
diff --git a/tempest/lib/services/volume/v3/groups_client.py b/tempest/lib/services/volume/v3/groups_client.py
index e261e8e..b463fdf 100644
--- a/tempest/lib/services/volume/v3/groups_client.py
+++ b/tempest/lib/services/volume/v3/groups_client.py
@@ -97,6 +97,18 @@
         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/test.py b/tempest/test.py
index 4744f76..78db8e1 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='?')
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/volume/v3/test_groups_client.py b/tempest/tests/lib/services/volume/v3/test_groups_client.py
index 4d0d860..0884e5a 100644
--- a/tempest/tests/lib/services/volume/v3/test_groups_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_groups_client.py
@@ -44,6 +44,17 @@
         }
     }
 
+    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",
@@ -164,3 +175,12 @@
             '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]