Merge "Make aggregates_client use **kwargs"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 1095e77..27d65e6 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -432,6 +432,11 @@
 # value)
 #attach_encrypted_volume = true
 
+# Does the test environment support creating instances with multiple
+# ports on the same network? This is only valid when using Neutron.
+# (boolean value)
+#allow_duplicate_networks = false
+
 
 [dashboard]
 
diff --git a/requirements.txt b/requirements.txt
index 66e5b16..d0419f7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,7 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-pbr<2.0,>=0.11
+pbr<2.0,>=1.3
 cliff>=1.13.0 # Apache-2.0
 anyjson>=0.3.3
 httplib2>=0.7.5
@@ -11,13 +11,13 @@
 paramiko>=1.13.0
 netaddr>=0.7.12
 testrepository>=0.0.18
-pyOpenSSL>=0.11
+pyOpenSSL>=0.14
 oslo.concurrency>=2.1.0 # Apache-2.0
 oslo.config>=1.11.0 # Apache-2.0
 oslo.i18n>=1.5.0 # Apache-2.0
-oslo.log>=1.2.0 # Apache-2.0
+oslo.log>=1.6.0 # Apache-2.0
 oslo.serialization>=1.4.0 # Apache-2.0
-oslo.utils>=1.6.0 # Apache-2.0
+oslo.utils>=1.9.0 # Apache-2.0
 six>=1.9.0
 iso8601>=0.1.9
 fixtures>=1.3.1
diff --git a/setup.py b/setup.py
index 056c16c..d8080d0 100644
--- a/setup.py
+++ b/setup.py
@@ -25,5 +25,5 @@
     pass
 
 setuptools.setup(
-    setup_requires=['pbr'],
+    setup_requires=['pbr>=1.3'],
     pbr=True)
diff --git a/tempest/api/compute/admin/test_agents.py b/tempest/api/compute/admin/test_agents.py
index 1aa1615..d9a1ee5 100644
--- a/tempest/api/compute/admin/test_agents.py
+++ b/tempest/api/compute/admin/test_agents.py
@@ -109,8 +109,7 @@
         self.addCleanup(self.client.delete_agent, agent_xen['agent_id'])
 
         agent_id_xen = agent_xen['agent_id']
-        params_filter = {'hypervisor': agent_xen['hypervisor']}
-        agents = self.client.list_agents(params_filter)
+        agents = self.client.list_agents(hypervisor=agent_xen['hypervisor'])
         self.assertTrue(len(agents) > 0, 'Cannot get any agents.(%s)' % agents)
         self.assertIn(agent_id_xen, map(lambda x: x['agent_id'], agents))
         self.assertNotIn(self.agent_id, map(lambda x: x['agent_id'], agents))
diff --git a/tempest/api/compute/admin/test_hosts.py b/tempest/api/compute/admin/test_hosts.py
index 9fee2a1..0dadea5 100644
--- a/tempest/api/compute/admin/test_hosts.py
+++ b/tempest/api/compute/admin/test_hosts.py
@@ -38,9 +38,7 @@
         self.useFixture(fixtures.LockFixture('availability_zone'))
         hosts = self.client.list_hosts()
         host = hosts[0]
-        zone_name = host['zone']
-        params = {'zone': zone_name}
-        hosts = self.client.list_hosts(params)
+        hosts = self.client.list_hosts(zone=host['zone'])
         self.assertTrue(len(hosts) >= 1)
         self.assertIn(host, hosts)
 
@@ -48,16 +46,14 @@
     def test_list_hosts_with_a_blank_zone(self):
         # If send the request with a blank zone, the request will be successful
         # and it will return all the hosts list
-        params = {'zone': ''}
-        hosts = self.client.list_hosts(params)
+        hosts = self.client.list_hosts(zone='')
         self.assertNotEqual(0, len(hosts))
 
     @test.idempotent_id('c6ddbadb-c94e-4500-b12f-8ffc43843ff8')
     def test_list_hosts_with_nonexistent_zone(self):
         # If send the request with a nonexistent zone, the request will be
         # successful and no hosts will be retured
-        params = {'zone': 'xxx'}
-        hosts = self.client.list_hosts(params)
+        hosts = self.client.list_hosts(zone='xxx')
         self.assertEqual(0, len(hosts))
 
     @test.idempotent_id('38adbb12-aee2-4498-8aec-329c72423aa4')
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index 548cd4f..6ffa4e9 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -17,6 +17,7 @@
 import testtools
 
 from tempest.api.compute import base
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -103,10 +104,11 @@
 
         if state == 'PAUSED':
             self.admin_servers_client.pause_server(server_id)
-            self.admin_servers_client.wait_for_server_status(server_id, state)
+            waiters.wait_for_server_status(self.admin_servers_client,
+                                           server_id, state)
 
         self._migrate_server_to(server_id, target_host)
-        self.servers_client.wait_for_server_status(server_id, state)
+        waiters.wait_for_server_status(self.servers_client, server_id, state)
         self.assertEqual(target_host, self._get_host_for_server(server_id))
 
     @test.idempotent_id('1dce86b8-eb04-4c03-a9d8-9c1dc3ee0c7b')
@@ -156,5 +158,6 @@
         self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
 
         self._migrate_server_to(server_id, target_host)
-        self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client,
+                                       server_id, 'ACTIVE')
         self.assertEqual(target_host, self._get_host_for_server(server_id))
diff --git a/tempest/api/compute/admin/test_migrations.py b/tempest/api/compute/admin/test_migrations.py
index 5434d93..5af7406 100644
--- a/tempest/api/compute/admin/test_migrations.py
+++ b/tempest/api/compute/admin/test_migrations.py
@@ -15,6 +15,7 @@
 import testtools
 
 from tempest.api.compute import base
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -42,9 +43,11 @@
         server_id = server['id']
 
         self.servers_client.resize(server_id, self.flavor_ref_alt)
-        self.servers_client.wait_for_server_status(server_id, 'VERIFY_RESIZE')
+        waiters.wait_for_server_status(self.servers_client,
+                                       server_id, 'VERIFY_RESIZE')
         self.servers_client.confirm_resize(server_id)
-        self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client,
+                                       server_id, 'ACTIVE')
 
         body = self.client.list_migrations()
 
diff --git a/tempest/api/compute/admin/test_quotas_negative.py b/tempest/api/compute/admin/test_quotas_negative.py
index 20aa440..798bd30 100644
--- a/tempest/api/compute/admin/test_quotas_negative.py
+++ b/tempest/api/compute/admin/test_quotas_negative.py
@@ -109,7 +109,10 @@
 
         quota_set = self.adm_client.show_quota_set(self.demo_tenant_id)
         default_sg_quota = quota_set['security_groups']
-        sg_quota = 0  # Set the quota to zero to conserve resources
+
+        # Set the quota to number of used security groups
+        sg_quota = self.limits_client.show_limits()['absolute'][
+            'totalSecurityGroupsUsed']
 
         quota_set =\
             self.adm_client.update_quota_set(self.demo_tenant_id,
diff --git a/tempest/api/compute/admin/test_security_groups.py b/tempest/api/compute/admin/test_security_groups.py
index 2c3c629..ff87a4f 100644
--- a/tempest/api/compute/admin/test_security_groups.py
+++ b/tempest/api/compute/admin/test_security_groups.py
@@ -69,8 +69,7 @@
             security_group_list.append(adm_securitygroup)
 
         # Fetch all security groups based on 'all_tenants' search filter
-        param = {'all_tenants': 'true'}
-        fetched_list = self.adm_client.list_security_groups(params=param)
+        fetched_list = self.adm_client.list_security_groups(all_tenants='true')
         sec_group_id_list = map(lambda sg: sg['id'], fetched_list)
         # Now check if all created Security Groups are present in fetched list
         for sec_group in security_group_list:
@@ -78,7 +77,7 @@
 
         # Fetch all security groups for non-admin user with 'all_tenants'
         # search filter
-        fetched_list = self.client.list_security_groups(params=param)
+        fetched_list = self.client.list_security_groups(all_tenants='true')
         # Now check if all created Security Groups are present in fetched list
         for sec_group in fetched_list:
             self.assertEqual(sec_group['tenant_id'], client_tenant_id,
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 51a03f5..1982eda 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -17,6 +17,7 @@
 from tempest.api.compute import base
 from tempest.common import fixed_network
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import test
 
 
@@ -113,7 +114,8 @@
         test_server = self.client.create_server(name, image_id, flavor,
                                                 **network_kwargs)
         self.addCleanup(self.client.delete_server, test_server['id'])
-        self.client.wait_for_server_status(test_server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.client,
+                                       test_server['id'], 'ACTIVE')
         server = self.client.show_server(test_server['id'])
         self.assertEqual(server['status'], 'ACTIVE')
         hostname = server[self._host_key]
@@ -163,7 +165,7 @@
         self.client.reset_state(self.s1_id, state='error')
         rebuilt_server = self.non_admin_client.rebuild(
             self.s1_id, self.image_ref_alt)
-        self.addCleanup(self.non_admin_client.wait_for_server_status,
+        self.addCleanup(waiters.wait_for_server_status, self.non_admin_client,
                         self.s1_id, 'ACTIVE')
         self.addCleanup(self.non_admin_client.rebuild, self.s1_id,
                         self.image_ref)
@@ -173,9 +175,9 @@
         rebuilt_image_id = rebuilt_server['image']['id']
         self.assertEqual(self.image_ref_alt, rebuilt_image_id)
         self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
-        self.non_admin_client.wait_for_server_status(rebuilt_server['id'],
-                                                     'ACTIVE',
-                                                     raise_on_error=False)
+        waiters.wait_for_server_status(self.non_admin_client,
+                                       rebuilt_server['id'], 'ACTIVE',
+                                       raise_on_error=False)
         # Verify the server properties after rebuilding
         server = self.non_admin_client.show_server(rebuilt_server['id'])
         rebuilt_image_id = server['image']['id']
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index 6d3c4a3..b93aaca 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -20,6 +20,7 @@
 from tempest.api.compute import base
 from tempest.common import tempest_fixtures as fixtures
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -151,7 +152,8 @@
         server_id = server['id']
         # suspend the server.
         self.client.suspend_server(server_id)
-        self.client.wait_for_server_status(server_id, 'SUSPENDED')
+        waiters.wait_for_server_status(self.client,
+                                       server_id, 'SUSPENDED')
         # migrate an suspended server should fail
         self.assertRaises(lib_exc.Conflict,
                           self.client.migrate_server,
diff --git a/tempest/api/compute/admin/test_services.py b/tempest/api/compute/admin/test_services.py
index dd3e78c..db22925 100644
--- a/tempest/api/compute/admin/test_services.py
+++ b/tempest/api/compute/admin/test_services.py
@@ -37,8 +37,7 @@
     @test.idempotent_id('f345b1ec-bc6e-4c38-a527-3ca2bc00bef5')
     def test_get_service_by_service_binary_name(self):
         binary_name = 'nova-compute'
-        params = {'binary': binary_name}
-        services = self.client.list_services(params)
+        services = self.client.list_services(binary=binary_name)
         self.assertNotEqual(0, len(services))
         for service in services:
             self.assertEqual(binary_name, service['binary'])
@@ -49,9 +48,8 @@
         host_name = services[0]['host']
         services_on_host = [service for service in services if
                             service['host'] == host_name]
-        params = {'host': host_name}
 
-        services = self.client.list_services(params)
+        services = self.client.list_services(host=host_name)
 
         # we could have a periodic job checkin between the 2 service
         # lookups, so only compare binary lists.
@@ -67,9 +65,9 @@
         services = self.client.list_services()
         host_name = services[0]['host']
         binary_name = services[0]['binary']
-        params = {'host': host_name, 'binary': binary_name}
 
-        services = self.client.list_services(params)
+        services = self.client.list_services(host=host_name,
+                                             binary=binary_name)
         self.assertEqual(1, len(services))
         self.assertEqual(host_name, services[0]['host'])
         self.assertEqual(binary_name, services[0]['binary'])
diff --git a/tempest/api/compute/admin/test_services_negative.py b/tempest/api/compute/admin/test_services_negative.py
index 99f31c5..b9335c9 100644
--- a/tempest/api/compute/admin/test_services_negative.py
+++ b/tempest/api/compute/admin/test_services_negative.py
@@ -41,8 +41,7 @@
     def test_get_service_by_invalid_params(self):
         # return all services if send the request with invalid parameter
         services = self.client.list_services()
-        params = {'xxx': 'nova-compute'}
-        services_xxx = self.client.list_services(params)
+        services_xxx = self.client.list_services(xxx='nova-compute')
         self.assertEqual(len(services), len(services_xxx))
 
     @test.attr(type=['negative'])
@@ -50,8 +49,7 @@
     def test_get_service_by_invalid_service_and_valid_host(self):
         services = self.client.list_services()
         host_name = services[0]['host']
-        params = {'host': host_name, 'binary': 'xxx'}
-        services = self.client.list_services(params)
+        services = self.client.list_services(host=host_name, binary='xxx')
         self.assertEqual(0, len(services))
 
     @test.attr(type=['negative'])
@@ -59,6 +57,5 @@
     def test_get_service_with_valid_service_and_invalid_host(self):
         services = self.client.list_services()
         binary_name = services[0]['binary']
-        params = {'host': 'xxx', 'binary': binary_name}
-        services = self.client.list_services(params)
+        services = self.client.list_services(host='xxx', binary=binary_name)
         self.assertEqual(0, len(services))
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage.py b/tempest/api/compute/admin/test_simple_tenant_usage.py
index 02085e8..204281c 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage.py
@@ -49,28 +49,22 @@
     @test.idempotent_id('062c8ae9-9912-4249-8b51-e38d664e926e')
     def test_list_usage_all_tenants(self):
         # Get usage for all tenants
-        params = {'start': self.start,
-                  'end': self.end,
-                  'detailed': int(bool(True))}
-        tenant_usage = self.adm_client.list_tenant_usages(params)
+        tenant_usage = self.adm_client.list_tenant_usages(
+            start=self.start, end=self.end, detailed="1")
         self.assertEqual(len(tenant_usage), 8)
 
     @test.idempotent_id('94135049-a4c5-4934-ad39-08fa7da4f22e')
     def test_get_usage_tenant(self):
         # Get usage for a specific tenant
-        params = {'start': self.start,
-                  'end': self.end}
         tenant_usage = self.adm_client.show_tenant_usage(
-            self.tenant_id, params)
+            self.tenant_id, start=self.start, end=self.end)
 
         self.assertEqual(len(tenant_usage), 8)
 
     @test.idempotent_id('9d00a412-b40e-4fd9-8eba-97b496316116')
     def test_get_usage_tenant_with_non_admin_user(self):
         # Get usage for a specific tenant with non admin user
-        params = {'start': self.start,
-                  'end': self.end}
         tenant_usage = self.client.show_tenant_usage(
-            self.tenant_id, params)
+            self.tenant_id, start=self.start, end=self.end)
 
         self.assertEqual(len(tenant_usage), 8)
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
index 3c66859..e9b4ad4 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
@@ -48,7 +48,7 @@
                   'end': self.end}
         self.assertRaises(lib_exc.NotFound,
                           self.adm_client.show_tenant_usage,
-                          '', params)
+                          '', **params)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('4079dd2a-9e8d-479f-869d-6fa985ce45b6')
@@ -58,7 +58,7 @@
                   'end': self.start}
         self.assertRaises(lib_exc.BadRequest,
                           self.adm_client.show_tenant_usage,
-                          self.client.tenant_id, params)
+                          self.client.tenant_id, **params)
 
     @test.attr(type=['negative'])
     @test.idempotent_id('bbe6fe2c-15d8-404c-a0a2-44fad0ad5cc7')
@@ -66,6 +66,6 @@
         # Get usage for all tenants with non admin user
         params = {'start': self.start,
                   'end': self.end,
-                  'detailed': int(bool(True))}
+                  'detailed': "1"}
         self.assertRaises(lib_exc.Forbidden,
-                          self.client.list_tenant_usages, params)
+                          self.client.list_tenant_usages, **params)
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index ea8e112..759bb8c 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -144,8 +144,8 @@
         """
         if getattr(cls, 'server_id', None) is not None:
             try:
-                cls.servers_client.wait_for_server_status(cls.server_id,
-                                                          'ACTIVE')
+                waiters.wait_for_server_status(cls.servers_client,
+                                               cls.server_id, 'ACTIVE')
             except Exception as exc:
                 LOG.exception(exc)
                 cls.servers_client.delete_server(cls.server_id)
@@ -266,8 +266,8 @@
 
     @classmethod
     def prepare_instance_network(cls):
-        if (CONF.compute.ssh_auth_method != 'disabled' and
-                CONF.compute.ssh_connect_method == 'floating'):
+        if (CONF.validation.auth_method != 'disabled' and
+                CONF.validation.connect_method == 'floating'):
             cls.set_network_resources(network=True, subnet=True, router=True,
                                       dhcp=True)
 
@@ -289,12 +289,12 @@
 
             if kwargs['wait_until'] == 'ACTIVE':
                 if kwargs.get('wait_for_server', True):
-                    cls.servers_client.wait_for_server_status(server_id,
-                                                              'ACTIVE')
+                    waiters.wait_for_server_status(cls.servers_client,
+                                                   server_id, 'ACTIVE')
         return image
 
     @classmethod
-    def rebuild_server(cls, server_id, **kwargs):
+    def rebuild_server(cls, server_id, validatable=False, **kwargs):
         # Destroy an existing server and creates a new one
         if server_id:
             try:
@@ -302,7 +302,11 @@
                 cls.servers_client.wait_for_server_termination(server_id)
             except Exception:
                 LOG.exception('Failed to delete server %s' % server_id)
-        server = cls.create_test_server(wait_until='ACTIVE', **kwargs)
+
+        server = cls.create_test_server(
+            validatable,
+            wait_until='ACTIVE',
+            **kwargs)
         cls.password = server['adminPass']
         return server['id']
 
@@ -320,6 +324,21 @@
         """Deletes the given volume and waits for it to be gone."""
         cls._delete_volume(cls.volumes_extensions_client, volume_id)
 
+    @classmethod
+    def get_server_ip(cls, server):
+        """Get the server fixed or floating IP.
+
+        For the floating IP, the address created by the validation resources
+        is returned.
+        For the fixed IP, the server is returned and the current mechanism of
+        address extraction in the remote_client is followed.
+        """
+        if CONF.validation.connect_method == 'floating':
+            ip_or_server = cls.validation_resources['floating_ip']['ip']
+        elif CONF.validation.connect_method == 'fixed':
+            ip_or_server = server
+        return ip_or_server
+
 
 class BaseV2ComputeTest(BaseComputeTest):
     _api_version = 2
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index 2cc1b59..8bb4fac 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -17,6 +17,7 @@
 
 from tempest.api.compute.floating_ips import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import test
 
 
@@ -113,7 +114,8 @@
         # Create server so as to use for Multiple association
         new_name = data_utils.rand_name('floating_server')
         body = self.create_test_server(name=new_name)
-        self.servers_client.wait_for_server_status(body['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client,
+                                       body['id'], 'ACTIVE')
         self.new_server_id = body['id']
         self.addCleanup(self.servers_client.delete_server, self.new_server_id)
 
diff --git a/tempest/api/compute/images/test_images_negative.py b/tempest/api/compute/images/test_images_negative.py
index 07eca9e..9721fa5 100644
--- a/tempest/api/compute/images/test_images_negative.py
+++ b/tempest/api/compute/images/test_images_negative.py
@@ -16,6 +16,7 @@
 
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -74,8 +75,8 @@
     def test_create_image_from_stopped_server(self):
         server = self.create_test_server(wait_until='ACTIVE')
         self.servers_client.stop(server['id'])
-        self.servers_client.wait_for_server_status(server['id'],
-                                                   'SHUTOFF')
+        waiters.wait_for_server_status(self.servers_client,
+                                       server['id'], 'SHUTOFF')
         self.addCleanup(self.servers_client.delete_server, server['id'])
         snapshot_name = data_utils.rand_name('test-snap')
         image = self.create_image_from_server(server['id'],
diff --git a/tempest/api/compute/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index 1741e86..40a781c 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -38,8 +38,8 @@
         super(ImagesOneServerTestJSON, self).setUp()
         # Check if the server is in a clean state after test
         try:
-            self.servers_client.wait_for_server_status(self.server_id,
-                                                       'ACTIVE')
+            waiters.wait_for_server_status(self.servers_client,
+                                           self.server_id, 'ACTIVE')
         except Exception:
             LOG.exception('server %s timed out to become ACTIVE. rebuilding'
                           % self.server_id)
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index 1cd1ab7..1a74e52 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -19,6 +19,7 @@
 
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -43,8 +44,8 @@
         super(ImagesOneServerNegativeTestJSON, self).setUp()
         # Check if the server is in a clean state after test
         try:
-            self.servers_client.wait_for_server_status(self.server_id,
-                                                       'ACTIVE')
+            waiters.wait_for_server_status(self.servers_client, self.server_id,
+                                           'ACTIVE')
         except Exception:
             LOG.exception('server %s timed out to become ACTIVE. rebuilding'
                           % self.server_id)
@@ -68,6 +69,11 @@
             raise cls.skipException(skip_msg)
 
     @classmethod
+    def setup_credentials(cls):
+        cls.prepare_instance_network()
+        super(ImagesOneServerNegativeTestJSON, cls).setup_credentials()
+
+    @classmethod
     def setup_clients(cls):
         super(ImagesOneServerNegativeTestJSON, cls).setup_clients()
         cls.client = cls.images_client
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index b0bbc91..2c0ce59 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -81,8 +81,8 @@
         cls.server1 = cls.create_test_server()
         cls.server2 = cls.create_test_server(wait_until='ACTIVE')
         # NOTE(sdague) this is faster than doing the sync wait_util on both
-        cls.servers_client.wait_for_server_status(cls.server1['id'],
-                                                  'ACTIVE')
+        waiters.wait_for_server_status(cls.servers_client,
+                                       cls.server1['id'], 'ACTIVE')
 
         # Create images to be used in the filter tests
         cls.snapshot1 = cls.create_image_from_server(
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 491e52e..bd252b0 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -17,6 +17,7 @@
 
 from tempest.api.compute.security_groups import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import test
 
 
@@ -97,7 +98,8 @@
         server_name = data_utils.rand_name('server')
         server = self.create_test_server(name=server_name)
         server_id = server['id']
-        self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client, server_id,
+                                       'ACTIVE')
         self.servers_client.add_security_group(server_id, sg['name'])
 
         # Check that we are not able to delete the security
@@ -108,7 +110,8 @@
 
         # Reboot and add the other security group
         self.servers_client.reboot(server_id, 'HARD')
-        self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client, server_id,
+                                       'ACTIVE')
         self.servers_client.add_security_group(server_id, sg2['name'])
 
         # Check that we are not able to delete the other security
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index 94b6cf0..23a9cb3 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -65,6 +65,20 @@
         cls.password = cls.server_initial['adminPass']
         cls.server = cls.client.show_server(cls.server_initial['id'])
 
+    def _create_net_subnet_ret_net_from_cidr(self, cidr):
+        name_net = data_utils.rand_name(self.__class__.__name__)
+        net = self.network_client.create_network(name=name_net)
+        self.addCleanup(self.network_client.delete_network,
+                        net['network']['id'])
+
+        subnet = self.network_client.create_subnet(
+            network_id=net['network']['id'],
+            cidr=cidr,
+            ip_version=4)
+        self.addCleanup(self.network_client.delete_subnet,
+                        subnet['subnet']['id'])
+        return net
+
     @test.attr(type='smoke')
     @test.idempotent_id('5de47127-9977-400a-936f-abcfbec1218f')
     def test_verify_server_details(self):
@@ -103,8 +117,11 @@
         # Verify that the number of vcpus reported by the instance matches
         # the amount stated by the flavor
         flavor = self.flavors_client.show_flavor(self.flavor_ref)
-        linux_client = remote_client.RemoteClient(self.server, self.ssh_user,
-                                                  self.password)
+        linux_client = remote_client.RemoteClient(
+            self.get_server_ip(self.server),
+            self.ssh_user,
+            self.password,
+            self.validation_resources['keypair']['private_key'])
         self.assertEqual(flavor['vcpus'], linux_client.get_number_of_vcpus())
 
     @test.idempotent_id('ac1ad47f-984b-4441-9274-c9079b7a0666')
@@ -112,8 +129,11 @@
                           'Instance validation tests are disabled.')
     def test_host_name_is_same_as_server_name(self):
         # Verify the instance host name is the same as the server name
-        linux_client = remote_client.RemoteClient(self.server, self.ssh_user,
-                                                  self.password)
+        linux_client = remote_client.RemoteClient(
+            self.get_server_ip(self.server),
+            self.ssh_user,
+            self.password,
+            self.validation_resources['keypair']['private_key'])
         self.assertTrue(linux_client.hostname_equals_servername(self.name))
 
     @test.idempotent_id('ed20d3fb-9d1f-4329-b160-543fbd5d9811')
@@ -141,29 +161,8 @@
     def test_verify_multiple_nics_order(self):
         # Verify that the networks order given at the server creation is
         # preserved within the server.
-        name_net1 = data_utils.rand_name(self.__class__.__name__)
-        net1 = self.network_client.create_network(name=name_net1)
-        self.addCleanup(self.network_client.delete_network,
-                        net1['network']['id'])
-
-        name_net2 = data_utils.rand_name(self.__class__.__name__)
-        net2 = self.network_client.create_network(name=name_net2)
-        self.addCleanup(self.network_client.delete_network,
-                        net2['network']['id'])
-
-        subnet1 = self.network_client.create_subnet(
-            network_id=net1['network']['id'],
-            cidr='19.80.0.0/24',
-            ip_version=4)
-        self.addCleanup(self.network_client.delete_subnet,
-                        subnet1['subnet']['id'])
-
-        subnet2 = self.network_client.create_subnet(
-            network_id=net2['network']['id'],
-            cidr='19.86.0.0/24',
-            ip_version=4)
-        self.addCleanup(self.network_client.delete_subnet,
-                        subnet2['subnet']['id'])
+        net1 = self._create_net_subnet_ret_net_from_cidr('19.80.0.0/24')
+        net2 = self._create_net_subnet_ret_net_from_cidr('19.86.0.0/24')
 
         networks = [{'uuid': net1['network']['id']},
                     {'uuid': net2['network']['id']}]
@@ -190,24 +189,71 @@
         # other times ['19.80.0.3', '19.86.0.3']. So we check if the first
         # address is in first network, similarly second address is in second
         # network.
-        addr = [addresses[name_net1][0]['addr'],
-                addresses[name_net2][0]['addr']]
+        addr = [addresses[net1['network']['name']][0]['addr'],
+                addresses[net2['network']['name']][0]['addr']]
         networks = [netaddr.IPNetwork('19.80.0.0/24'),
                     netaddr.IPNetwork('19.86.0.0/24')]
         for address, network in zip(addr, networks):
             self.assertIn(address, network)
 
+    @test.idempotent_id('1678d144-ed74-43f8-8e57-ab10dbf9b3c2')
+    @testtools.skipUnless(CONF.service_available.neutron,
+                          'Neutron service must be available.')
+    # The below skipUnless should be removed once Kilo-eol happens.
+    @testtools.skipUnless(CONF.compute_feature_enabled.
+                          allow_duplicate_networks,
+                          'Duplicate networks must be allowed')
+    def test_verify_duplicate_network_nics(self):
+        # Verify that server creation does not fail when more than one nic
+        # is created on the same network.
+        net1 = self._create_net_subnet_ret_net_from_cidr('19.80.0.0/24')
+        net2 = self._create_net_subnet_ret_net_from_cidr('19.86.0.0/24')
+
+        networks = [{'uuid': net1['network']['id']},
+                    {'uuid': net2['network']['id']},
+                    {'uuid': net1['network']['id']}]
+
+        server_multi_nics = self.create_test_server(
+            networks=networks, wait_until='ACTIVE')
+
+        def cleanup_server():
+            self.client.delete_server(server_multi_nics['id'])
+            self.client.wait_for_server_termination(server_multi_nics['id'])
+
+        self.addCleanup(cleanup_server)
+
+        addresses = self.client.list_addresses(server_multi_nics['id'])
+
+        addr = [addresses[net1['network']['name']][0]['addr'],
+                addresses[net2['network']['name']][0]['addr'],
+                addresses[net1['network']['name']][1]['addr']]
+        networks = [netaddr.IPNetwork('19.80.0.0/24'),
+                    netaddr.IPNetwork('19.86.0.0/24'),
+                    netaddr.IPNetwork('19.80.0.0/24')]
+        for address, network in zip(addr, networks):
+            self.assertIn(address, network)
+
 
 class ServersWithSpecificFlavorTestJSON(base.BaseV2ComputeAdminTest):
     disk_config = 'AUTO'
 
     @classmethod
-    def setup_clients(cls):
+    def setup_credentials(cls):
         cls.prepare_instance_network()
+        super(ServersWithSpecificFlavorTestJSON, cls).setup_credentials()
+
+    @classmethod
+    def setup_clients(cls):
         super(ServersWithSpecificFlavorTestJSON, cls).setup_clients()
         cls.flavor_client = cls.os_adm.flavors_client
         cls.client = cls.servers_client
 
+    @classmethod
+    def resource_setup(cls):
+        cls.set_validation_resources()
+
+        super(ServersWithSpecificFlavorTestJSON, cls).resource_setup()
+
     @test.idempotent_id('b3c7bcfc-bb5b-4e22-b517-c7f686b802ca')
     @testtools.skipUnless(CONF.validation.run_validation,
                           'Instance validation tests are disabled.')
@@ -258,24 +304,37 @@
         admin_pass = self.image_ssh_password
 
         server_no_eph_disk = (self.create_test_server(
+                              validatable=True,
                               wait_until='ACTIVE',
                               adminPass=admin_pass,
                               flavor=flavor_no_eph_disk_id))
-        server_with_eph_disk = (self.create_test_server(
-                                wait_until='ACTIVE',
-                                adminPass=admin_pass,
-                                flavor=flavor_with_eph_disk_id))
+
         # Get partition number of server without extra specs.
         server_no_eph_disk = self.client.show_server(
             server_no_eph_disk['id'])
-        linux_client = remote_client.RemoteClient(server_no_eph_disk,
-                                                  self.ssh_user, admin_pass)
+        linux_client = remote_client.RemoteClient(
+            self.get_server_ip(server_no_eph_disk),
+            self.ssh_user,
+            admin_pass,
+            self.validation_resources['keypair']['private_key'])
         partition_num = len(linux_client.get_partitions().split('\n'))
 
+        # Explicit server deletion necessary for Juno compatibility
+        self.client.delete_server(server_no_eph_disk['id'])
+
+        server_with_eph_disk = (self.create_test_server(
+                                validatable=True,
+                                wait_until='ACTIVE',
+                                adminPass=admin_pass,
+                                flavor=flavor_with_eph_disk_id))
+
         server_with_eph_disk = self.client.show_server(
             server_with_eph_disk['id'])
-        linux_client = remote_client.RemoteClient(server_with_eph_disk,
-                                                  self.ssh_user, admin_pass)
+        linux_client = remote_client.RemoteClient(
+            self.get_server_ip(server_with_eph_disk),
+            self.ssh_user,
+            admin_pass,
+            self.validation_resources['keypair']['private_key'])
         partition_num_emph = len(linux_client.get_partitions().split('\n'))
         self.assertEqual(partition_num + 1, partition_num_emph)
 
diff --git a/tempest/api/compute/servers/test_delete_server.py b/tempest/api/compute/servers/test_delete_server.py
index 990ad47..b2acd34 100644
--- a/tempest/api/compute/servers/test_delete_server.py
+++ b/tempest/api/compute/servers/test_delete_server.py
@@ -52,7 +52,7 @@
         # Delete a server while it's VM state is Shutoff
         server = self.create_test_server(wait_until='ACTIVE')
         self.client.stop(server['id'])
-        self.client.wait_for_server_status(server['id'], 'SHUTOFF')
+        waiters.wait_for_server_status(self.client, server['id'], 'SHUTOFF')
         self.client.delete_server(server['id'])
         self.client.wait_for_server_termination(server['id'])
 
@@ -63,7 +63,7 @@
         # Delete a server while it's VM state is Pause
         server = self.create_test_server(wait_until='ACTIVE')
         self.client.pause_server(server['id'])
-        self.client.wait_for_server_status(server['id'], 'PAUSED')
+        waiters.wait_for_server_status(self.client, server['id'], 'PAUSED')
         self.client.delete_server(server['id'])
         self.client.wait_for_server_termination(server['id'])
 
@@ -74,7 +74,7 @@
         # Delete a server while it's VM state is Suspended
         server = self.create_test_server(wait_until='ACTIVE')
         self.client.suspend_server(server['id'])
-        self.client.wait_for_server_status(server['id'], 'SUSPENDED')
+        waiters.wait_for_server_status(self.client, server['id'], 'SUSPENDED')
         self.client.delete_server(server['id'])
         self.client.wait_for_server_termination(server['id'])
 
@@ -88,12 +88,12 @@
 
         offload_time = CONF.compute.shelved_offload_time
         if offload_time >= 0:
-            self.client.wait_for_server_status(server['id'],
-                                               'SHELVED_OFFLOADED',
-                                               extra_timeout=offload_time)
+            waiters.wait_for_server_status(self.client, server['id'],
+                                           'SHELVED_OFFLOADED',
+                                           extra_timeout=offload_time)
         else:
-            self.client.wait_for_server_status(server['id'],
-                                               'SHELVED')
+            waiters.wait_for_server_status(self.client, server['id'],
+                                           'SHELVED')
         self.client.delete_server(server['id'])
         self.client.wait_for_server_termination(server['id'])
 
@@ -104,7 +104,8 @@
         # Delete a server while it's VM state is VERIFY_RESIZE
         server = self.create_test_server(wait_until='ACTIVE')
         self.client.resize(server['id'], self.flavor_ref_alt)
-        self.client.wait_for_server_status(server['id'], 'VERIFY_RESIZE')
+        waiters.wait_for_server_status(self.client, server['id'],
+                                       'VERIFY_RESIZE')
         self.client.delete_server(server['id'])
         self.client.wait_for_server_termination(server['id'])
 
diff --git a/tempest/api/compute/servers/test_disk_config.py b/tempest/api/compute/servers/test_disk_config.py
index 00aa510..3e8a0d2 100644
--- a/tempest/api/compute/servers/test_disk_config.py
+++ b/tempest/api/compute/servers/test_disk_config.py
@@ -16,6 +16,7 @@
 import testtools
 
 from tempest.api.compute import base
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -47,7 +48,7 @@
         if disk_config != server['OS-DCF:diskConfig']:
             server = self.client.update_server(self.server_id,
                                                disk_config=disk_config)
-            self.client.wait_for_server_status(server['id'], 'ACTIVE')
+            waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
             server = self.client.show_server(server['id'])
             self.assertEqual(disk_config, server['OS-DCF:diskConfig'])
 
@@ -61,7 +62,7 @@
                                      disk_config='MANUAL')
 
         # Wait for the server to become active
-        self.client.wait_for_server_status(server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
 
         # Verify the specified attributes are set correctly
         server = self.client.show_server(server['id'])
@@ -77,7 +78,7 @@
                                      disk_config='AUTO')
 
         # Wait for the server to become active
-        self.client.wait_for_server_status(server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
 
         # Verify the specified attributes are set correctly
         server = self.client.show_server(server['id'])
@@ -101,9 +102,10 @@
         # Resize with auto option
         flavor_id = self._get_alternative_flavor()
         self.client.resize(self.server_id, flavor_id, disk_config='AUTO')
-        self.client.wait_for_server_status(self.server_id, 'VERIFY_RESIZE')
+        waiters.wait_for_server_status(self.client, self.server_id,
+                                       'VERIFY_RESIZE')
         self.client.confirm_resize(self.server_id)
-        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
 
         server = self.client.show_server(self.server_id)
         self.assertEqual('AUTO', server['OS-DCF:diskConfig'])
@@ -118,9 +120,10 @@
         # Resize with manual option
         flavor_id = self._get_alternative_flavor()
         self.client.resize(self.server_id, flavor_id, disk_config='MANUAL')
-        self.client.wait_for_server_status(self.server_id, 'VERIFY_RESIZE')
+        waiters.wait_for_server_status(self.client, self.server_id,
+                                       'VERIFY_RESIZE')
         self.client.confirm_resize(self.server_id)
-        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
 
         server = self.client.show_server(self.server_id)
         self.assertEqual('MANUAL', server['OS-DCF:diskConfig'])
@@ -133,7 +136,7 @@
         # Update the disk_config attribute to manual
         server = self.client.update_server(self.server_id,
                                            disk_config='MANUAL')
-        self.client.wait_for_server_status(server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
 
         # Verify the disk_config attribute is set correctly
         server = self.client.show_server(server['id'])
diff --git a/tempest/api/compute/servers/test_instance_actions.py b/tempest/api/compute/servers/test_instance_actions.py
index 6233c58..dc126a5 100644
--- a/tempest/api/compute/servers/test_instance_actions.py
+++ b/tempest/api/compute/servers/test_instance_actions.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest.common import waiters
 from tempest import test
 
 
@@ -35,7 +36,7 @@
     def test_list_instance_actions(self):
         # List actions of the provided server
         self.client.reboot(self.server_id, 'HARD')
-        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
 
         body = self.client.list_instance_actions(self.server_id)
         self.assertTrue(len(body) == 2, str(body))
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 6d546d8..a75cb3e 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -19,6 +19,7 @@
 from tempest.api import utils
 from tempest.common import fixed_network
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -137,12 +138,12 @@
         # Filter the list of servers by server shutoff status
         params = {'status': 'shutoff'}
         self.client.stop(self.s1['id'])
-        self.client.wait_for_server_status(self.s1['id'],
-                                           'SHUTOFF')
+        waiters.wait_for_server_status(self.client, self.s1['id'],
+                                       'SHUTOFF')
         body = self.client.list_servers(**params)
         self.client.start(self.s1['id'])
-        self.client.wait_for_server_status(self.s1['id'],
-                                           'ACTIVE')
+        waiters.wait_for_server_status(self.client, self.s1['id'],
+                                       'ACTIVE')
         servers = body['servers']
 
         self.assertIn(self.s1['id'], map(lambda x: x['id'], servers))
diff --git a/tempest/api/compute/servers/test_server_actions.py b/tempest/api/compute/servers/test_server_actions.py
index 3aff595..f0f6b8c 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -24,6 +24,7 @@
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
 from tempest.common.utils.linux import remote_client
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -41,29 +42,39 @@
         super(ServerActionsTestJSON, self).setUp()
         # Check if the server is in a clean state after test
         try:
-            self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+            waiters.wait_for_server_status(self.client,
+                                           self.server_id, 'ACTIVE')
         except lib_exc.NotFound:
             # The server was deleted by previous test, create a new one
-            server = self.create_test_server(wait_until='ACTIVE')
+            server = self.create_test_server(
+                validatable=True,
+                wait_until='ACTIVE')
             self.__class__.server_id = server['id']
         except Exception:
             # Rebuild server if something happened to it during a test
-            self.__class__.server_id = self.rebuild_server(self.server_id)
+            self.__class__.server_id = self.rebuild_server(self.server_id,
+                                                           validatable=True)
 
     def tearDown(self):
         self.server_check_teardown()
         super(ServerActionsTestJSON, self).tearDown()
 
     @classmethod
+    def setup_credentials(cls):
+        cls.prepare_instance_network()
+        super(ServerActionsTestJSON, cls).setup_credentials()
+
+    @classmethod
     def setup_clients(cls):
         super(ServerActionsTestJSON, cls).setup_clients()
         cls.client = cls.servers_client
 
     @classmethod
     def resource_setup(cls):
-        cls.prepare_instance_network()
+        cls.set_validation_resources()
+
         super(ServerActionsTestJSON, cls).resource_setup()
-        cls.server_id = cls.rebuild_server(None)
+        cls.server_id = cls.rebuild_server(None, validatable=True)
 
     @test.idempotent_id('6158df09-4b82-4ab3-af6d-29cf36af858d')
     @testtools.skipUnless(CONF.compute_feature_enabled.change_password,
@@ -72,30 +83,38 @@
         # The server's password should be set to the provided password
         new_password = 'Newpass1234'
         self.client.change_password(self.server_id, new_password)
-        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
 
-        if self.run_ssh:
+        if CONF.validation.run_validation:
             # Verify that the user can authenticate with the new password
             server = self.client.show_server(self.server_id)
-            linux_client = remote_client.RemoteClient(server, self.ssh_user,
-                                                      new_password)
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(server),
+                self.ssh_user,
+                new_password)
             linux_client.validate_authentication()
 
     def _test_reboot_server(self, reboot_type):
-        if self.run_ssh:
+        if CONF.validation.run_validation:
             # Get the time the server was last rebooted,
             server = self.client.show_server(self.server_id)
-            linux_client = remote_client.RemoteClient(server, self.ssh_user,
-                                                      self.password)
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(server),
+                self.ssh_user,
+                self.password,
+                self.validation_resources['keypair']['private_key'])
             boot_time = linux_client.get_boot_time()
 
         self.client.reboot(self.server_id, reboot_type)
-        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
 
-        if self.run_ssh:
+        if CONF.validation.run_validation:
             # Log in and verify the boot time has changed
-            linux_client = remote_client.RemoteClient(server, self.ssh_user,
-                                                      self.password)
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(server),
+                self.ssh_user,
+                self.password,
+                self.validation_resources['keypair']['private_key'])
             new_boot_time = linux_client.get_boot_time()
             self.assertTrue(new_boot_time > boot_time,
                             '%s > %s' % (new_boot_time, boot_time))
@@ -114,7 +133,7 @@
 
     def _rebuild_server_and_check(self, image_ref):
         rebuilt_server = self.client.rebuild(self.server_id, image_ref)
-        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
         msg = ('Server was not rebuilt to the original image. '
                'The original image: {0}. The current image: {1}'
                .format(image_ref, rebuilt_server['image']['id']))
@@ -148,16 +167,20 @@
         self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
 
         # Verify the server properties after the rebuild completes
-        self.client.wait_for_server_status(rebuilt_server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.client,
+                                       rebuilt_server['id'], 'ACTIVE')
         server = self.client.show_server(rebuilt_server['id'])
         rebuilt_image_id = server['image']['id']
         self.assertTrue(self.image_ref_alt.endswith(rebuilt_image_id))
         self.assertEqual(new_name, server['name'])
 
-        if self.run_ssh:
-            # Verify that the user can authenticate with the provided password
-            linux_client = remote_client.RemoteClient(server, self.ssh_user,
-                                                      password)
+        if CONF.validation.run_validation:
+            # TODO(jlanoux) add authentication with the provided password
+            linux_client = remote_client.RemoteClient(
+                self.get_server_ip(rebuilt_server),
+                self.ssh_user,
+                self.password,
+                self.validation_resources['keypair']['private_key'])
             linux_client.validate_authentication()
 
     @test.idempotent_id('30449a88-5aff-4f9b-9866-6ee9b17f906d')
@@ -169,7 +192,7 @@
         new_image = (self.image_ref_alt
                      if old_image == self.image_ref else self.image_ref)
         self.client.stop(self.server_id)
-        self.client.wait_for_server_status(self.server_id, 'SHUTOFF')
+        waiters.wait_for_server_status(self.client, self.server_id, 'SHUTOFF')
         rebuilt_server = self.client.rebuild(self.server_id, new_image)
         # If the server was rebuilt on a different image, restore it to the
         # original image once the test ends
@@ -183,7 +206,8 @@
         self.assertEqual(self.flavor_ref, rebuilt_server['flavor']['id'])
 
         # Verify the server properties after the rebuild completes
-        self.client.wait_for_server_status(rebuilt_server['id'], 'SHUTOFF')
+        waiters.wait_for_server_status(self.client,
+                                       rebuilt_server['id'], 'SHUTOFF')
         server = self.client.show_server(rebuilt_server['id'])
         rebuilt_image_id = server['image']['id']
         self.assertEqual(new_image, rebuilt_image_id)
@@ -195,16 +219,18 @@
         # the provided flavor
 
         if stop:
-            self.servers_client.stop(self.server_id)
-            self.servers_client.wait_for_server_status(self.server_id,
-                                                       'SHUTOFF')
+            self.client.stop(self.server_id)
+            waiters.wait_for_server_status(self.client, self.server_id,
+                                           'SHUTOFF')
 
         self.client.resize(self.server_id, self.flavor_ref_alt)
-        self.client.wait_for_server_status(self.server_id, 'VERIFY_RESIZE')
+        waiters.wait_for_server_status(self.client, self.server_id,
+                                       'VERIFY_RESIZE')
 
         self.client.confirm_resize(self.server_id)
         expected_status = 'SHUTOFF' if stop else 'ACTIVE'
-        self.client.wait_for_server_status(self.server_id, expected_status)
+        waiters.wait_for_server_status(self.client, self.server_id,
+                                       expected_status)
 
         server = self.client.show_server(self.server_id)
         self.assertEqual(self.flavor_ref_alt, server['flavor']['id'])
@@ -237,10 +263,11 @@
         # values after a resize is reverted
 
         self.client.resize(self.server_id, self.flavor_ref_alt)
-        self.client.wait_for_server_status(self.server_id, 'VERIFY_RESIZE')
+        waiters.wait_for_server_status(self.client, self.server_id,
+                                       'VERIFY_RESIZE')
 
         self.client.revert_resize(self.server_id)
-        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
 
         server = self.client.show_server(self.server_id)
         self.assertEqual(self.flavor_ref, server['flavor']['id'])
@@ -253,10 +280,10 @@
         # Positive test:create backup successfully and rotate backups correctly
         # create the first and the second backup
         backup1 = data_utils.rand_name('backup-1')
-        resp = self.servers_client.create_backup(self.server_id,
-                                                 'daily',
-                                                 2,
-                                                 backup1).response
+        resp = self.client.create_backup(self.server_id,
+                                         'daily',
+                                         2,
+                                         backup1).response
         oldest_backup_exist = True
 
         # the oldest one should be deleted automatically in this test
@@ -276,11 +303,11 @@
         self.os.image_client.wait_for_image_status(image1_id, 'active')
 
         backup2 = data_utils.rand_name('backup-2')
-        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
-        resp = self.servers_client.create_backup(self.server_id,
-                                                 'daily',
-                                                 2,
-                                                 backup2).response
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+        resp = self.client.create_backup(self.server_id,
+                                         'daily',
+                                         2,
+                                         backup2).response
         image2_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(self.os.image_client.delete_image, image2_id)
         self.os.image_client.wait_for_image_status(image2_id, 'active')
@@ -304,15 +331,15 @@
         # create the third one, due to the rotation is 2,
         # the first one will be deleted
         backup3 = data_utils.rand_name('backup-3')
-        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
-        resp = self.servers_client.create_backup(self.server_id,
-                                                 'daily',
-                                                 2,
-                                                 backup3).response
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
+        resp = self.client.create_backup(self.server_id,
+                                         'daily',
+                                         2,
+                                         backup3).response
         image3_id = data_utils.parse_image_id(resp['location'])
         self.addCleanup(self.os.image_client.delete_image, image3_id)
         # the first back up should be deleted
-        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
         self.os.image_client.wait_for_resource_deletion(image1_id)
         oldest_backup_exist = False
         image_list = self.os.image_client.list_images(
@@ -330,7 +357,7 @@
                          (image_list[0]['name'], image_list[1]['name']))
 
     def _get_output(self):
-        output = self.servers_client.get_console_output(
+        output = self.client.get_console_output(
             self.server_id, 10).data
         self.assertTrue(output, "Console output was empty.")
         lines = len(output.split('\n'))
@@ -348,9 +375,8 @@
         # log file is truncated and we cannot get any console log through
         # "console-log" API.
         # The detail is https://bugs.launchpad.net/nova/+bug/1251920
-        self.servers_client.reboot(self.server_id, 'HARD')
-        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
-
+        self.client.reboot(self.server_id, 'HARD')
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
         self.wait_for(self._get_output)
 
     @test.idempotent_id('89104062-69d8-4b19-a71b-f47b7af093d7')
@@ -360,8 +386,8 @@
         server = self.create_test_server(wait_until='ACTIVE')
 
         def _check_full_length_console_log():
-            output = self.servers_client.get_console_output(server['id'],
-                                                            None).data
+            output = self.client.get_console_output(server['id'],
+                                                    None).data
             self.assertTrue(output, "Console output was empty.")
             lines = len(output.split('\n'))
 
@@ -385,9 +411,8 @@
         server = self.create_test_server(wait_until='ACTIVE')
         temp_server_id = server['id']
 
-        self.servers_client.stop(temp_server_id)
-        self.servers_client.wait_for_server_status(temp_server_id, 'SHUTOFF')
-
+        self.client.stop(temp_server_id)
+        waiters.wait_for_server_status(self.client, temp_server_id, 'SHUTOFF')
         self.wait_for(self._get_output)
 
     @test.idempotent_id('bd61a9fd-062f-4670-972b-2d6c3e3b9e73')
@@ -395,18 +420,19 @@
                           'Pause is not available.')
     def test_pause_unpause_server(self):
         self.client.pause_server(self.server_id)
-        self.client.wait_for_server_status(self.server_id, 'PAUSED')
+        waiters.wait_for_server_status(self.client, self.server_id, 'PAUSED')
         self.client.unpause_server(self.server_id)
-        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
 
     @test.idempotent_id('0d8ee21e-b749-462d-83da-b85b41c86c7f')
     @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
                           'Suspend is not available.')
     def test_suspend_resume_server(self):
         self.client.suspend_server(self.server_id)
-        self.client.wait_for_server_status(self.server_id, 'SUSPENDED')
+        waiters.wait_for_server_status(self.client, self.server_id,
+                                       'SUSPENDED')
         self.client.resume_server(self.server_id)
-        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
 
     @test.idempotent_id('77eba8e0-036e-4635-944b-f7a8f3b78dc9')
     @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
@@ -416,16 +442,16 @@
 
         offload_time = CONF.compute.shelved_offload_time
         if offload_time >= 0:
-            self.client.wait_for_server_status(self.server_id,
-                                               'SHELVED_OFFLOADED',
-                                               extra_timeout=offload_time)
+            waiters.wait_for_server_status(self.client, self.server_id,
+                                           'SHELVED_OFFLOADED',
+                                           extra_timeout=offload_time)
         else:
-            self.client.wait_for_server_status(self.server_id,
-                                               'SHELVED')
+            waiters.wait_for_server_status(self.client, self.server_id,
+                                           'SHELVED')
 
             self.client.shelve_offload_server(self.server_id)
-            self.client.wait_for_server_status(self.server_id,
-                                               'SHELVED_OFFLOADED')
+            waiters.wait_for_server_status(self.client, self.server_id,
+                                           'SHELVED_OFFLOADED')
 
         server = self.client.show_server(self.server_id)
         image_name = server['name'] + '-shelved'
@@ -435,29 +461,29 @@
         self.assertEqual(image_name, images[0]['name'])
 
         self.client.unshelve_server(self.server_id)
-        self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
 
     @test.idempotent_id('af8eafd4-38a7-4a4b-bdbc-75145a580560')
     def test_stop_start_server(self):
-        self.servers_client.stop(self.server_id)
-        self.servers_client.wait_for_server_status(self.server_id, 'SHUTOFF')
-        self.servers_client.start(self.server_id)
-        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+        self.client.stop(self.server_id)
+        waiters.wait_for_server_status(self.client, self.server_id, 'SHUTOFF')
+        self.client.start(self.server_id)
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
 
     @test.idempotent_id('80a8094c-211e-440a-ab88-9e59d556c7ee')
     def test_lock_unlock_server(self):
         # Lock the server,try server stop(exceptions throw),unlock it and retry
-        self.servers_client.lock_server(self.server_id)
-        server = self.servers_client.show_server(self.server_id)
+        self.client.lock_server(self.server_id)
+        server = self.client.show_server(self.server_id)
         self.assertEqual(server['status'], 'ACTIVE')
         # Locked server is not allowed to be stopped by non-admin user
         self.assertRaises(lib_exc.Conflict,
-                          self.servers_client.stop, self.server_id)
-        self.servers_client.unlock_server(self.server_id)
-        self.servers_client.stop(self.server_id)
-        self.servers_client.wait_for_server_status(self.server_id, 'SHUTOFF')
-        self.servers_client.start(self.server_id)
-        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+                          self.client.stop, self.server_id)
+        self.client.unlock_server(self.server_id)
+        self.client.stop(self.server_id)
+        waiters.wait_for_server_status(self.client, self.server_id, 'SHUTOFF')
+        self.client.start(self.server_id)
+        waiters.wait_for_server_status(self.client, self.server_id, 'ACTIVE')
 
     def _validate_url(self, url):
         valid_scheme = ['http', 'https']
@@ -473,8 +499,8 @@
         # Get the VNC console of type 'novnc' and 'xvpvnc'
         console_types = ['novnc', 'xvpvnc']
         for console_type in console_types:
-            body = self.servers_client.get_vnc_console(self.server_id,
-                                                       console_type)
+            body = self.client.get_vnc_console(self.server_id,
+                                               console_type)
             self.assertEqual(console_type, body['type'])
             self.assertNotEqual('', body['url'])
             self._validate_url(body['url'])
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 20bc77f..98a2f9d 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -15,6 +15,7 @@
 
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -56,7 +57,8 @@
         server = cls.create_test_server(wait_until='BUILD')
         cls.server_id = server['id']
         cls.password = server['adminPass']
-        cls.servers_client.wait_for_server_status(cls.server_id, 'ACTIVE')
+        waiters.wait_for_server_status(cls.servers_client, cls.server_id,
+                                       'ACTIVE')
 
     def setUp(self):
         super(ServerRescueTestJSON, self).setUp()
@@ -74,22 +76,26 @@
 
     def _unrescue(self, server_id):
         self.servers_client.unrescue_server(server_id)
-        self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client, server_id,
+                                       'ACTIVE')
 
     @test.idempotent_id('fd032140-714c-42e4-a8fd-adcd8df06be6')
     def test_rescue_unrescue_instance(self):
         self.servers_client.rescue_server(
             self.server_id, adminPass=self.password)
-        self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
+        waiters.wait_for_server_status(self.servers_client, self.server_id,
+                                       'RESCUE')
         self.servers_client.unrescue_server(self.server_id)
-        self.servers_client.wait_for_server_status(self.server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client, self.server_id,
+                                       'ACTIVE')
 
     @test.idempotent_id('4842e0cf-e87d-4d9d-b61f-f4791da3cacc')
     def test_rescued_vm_associate_dissociate_floating_ip(self):
         # Rescue the server
         self.servers_client.rescue_server(
             self.server_id, adminPass=self.password)
-        self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
+        waiters.wait_for_server_status(self.servers_client, self.server_id,
+                                       'RESCUE')
         self.addCleanup(self._unrescue, self.server_id)
 
         # Association of floating IP to a rescued vm
@@ -106,7 +112,8 @@
         # Rescue the server
         self.servers_client.rescue_server(
             self.server_id, adminPass=self.password)
-        self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
+        waiters.wait_for_server_status(self.servers_client, self.server_id,
+                                       'RESCUE')
         self.addCleanup(self._unrescue, self.server_id)
 
         # Add Security group
diff --git a/tempest/api/compute/servers/test_server_rescue_negative.py b/tempest/api/compute/servers/test_server_rescue_negative.py
index 4300dd4..2fe63ed 100644
--- a/tempest/api/compute/servers/test_server_rescue_negative.py
+++ b/tempest/api/compute/servers/test_server_rescue_negative.py
@@ -54,8 +54,10 @@
 
         cls.servers_client.rescue_server(
             cls.rescue_id, adminPass=rescue_password)
-        cls.servers_client.wait_for_server_status(cls.rescue_id, 'RESCUE')
-        cls.servers_client.wait_for_server_status(cls.server_id, 'ACTIVE')
+        waiters.wait_for_server_status(cls.servers_client,
+                                       cls.rescue_id, 'RESCUE')
+        waiters.wait_for_server_status(cls.servers_client,
+                                       cls.server_id, 'ACTIVE')
 
     def _create_volume(self):
         volume = self.volumes_extensions_client.create_volume(
@@ -73,11 +75,13 @@
 
     def _unrescue(self, server_id):
         self.servers_client.unrescue_server(server_id)
-        self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client,
+                                       server_id, 'ACTIVE')
 
     def _unpause(self, server_id):
         self.servers_client.unpause_server(server_id)
-        self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client,
+                                       server_id, 'ACTIVE')
 
     @test.idempotent_id('cc3a883f-43c0-4fb6-a9bb-5579d64984ed')
     @testtools.skipUnless(CONF.compute_feature_enabled.pause,
@@ -87,7 +91,8 @@
         # Rescue a paused server
         self.servers_client.pause_server(self.server_id)
         self.addCleanup(self._unpause, self.server_id)
-        self.servers_client.wait_for_server_status(self.server_id, 'PAUSED')
+        waiters.wait_for_server_status(self.servers_client,
+                                       self.server_id, 'PAUSED')
         self.assertRaises(lib_exc.Conflict,
                           self.servers_client.rescue_server,
                           self.server_id)
@@ -124,7 +129,8 @@
         # Rescue the server
         self.servers_client.rescue_server(self.server_id,
                                           adminPass=self.password)
-        self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
+        waiters.wait_for_server_status(self.servers_client,
+                                       self.server_id, 'RESCUE')
         self.addCleanup(self._unrescue, self.server_id)
 
         # Attach the volume to the server
@@ -150,7 +156,8 @@
         # Rescue the server
         self.servers_client.rescue_server(self.server_id,
                                           adminPass=self.password)
-        self.servers_client.wait_for_server_status(self.server_id, 'RESCUE')
+        waiters.wait_for_server_status(self.servers_client,
+                                       self.server_id, 'RESCUE')
         # addCleanup is a LIFO queue
         self.addCleanup(self._detach, self.server_id, volume['id'])
         self.addCleanup(self._unrescue, self.server_id)
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index a7ebc09..2c1e69c 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -15,6 +15,7 @@
 
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import test
 
 
@@ -66,7 +67,7 @@
         self.addCleanup(self.keypairs_client.delete_keypair, key_name)
         self.keypairs_client.list_keypairs()
         server = self.create_test_server(key_name=key_name)
-        self.client.wait_for_server_status(server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
         server = self.client.show_server(server['id'])
         self.assertEqual(key_name, server['key_name'])
 
@@ -76,7 +77,7 @@
         # Update the server with a new name
         self.client.update_server(server_id,
                                   name=new_name)
-        self.client.wait_for_server_status(server_id, status)
+        waiters.wait_for_server_status(self.client, server_id, status)
 
         # Verify the name of the server has changed
         server = self.client.show_server(server_id)
@@ -95,7 +96,7 @@
         # The server name should be changed to the the provided value
         server = self.create_test_server(wait_until='ACTIVE')
         self.client.stop(server['id'])
-        self.client.wait_for_server_status(server['id'], 'SHUTOFF')
+        waiters.wait_for_server_status(self.client, server['id'], 'SHUTOFF')
         updated_server = self._update_server_name(server['id'], 'SHUTOFF')
         self.assertNotIn('progress', updated_server)
 
@@ -108,7 +109,7 @@
         self.client.update_server(server['id'],
                                   accessIPv4='1.1.1.1',
                                   accessIPv6='::babe:202:202')
-        self.client.wait_for_server_status(server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
 
         # Verify the access addresses have been updated
         server = self.client.show_server(server['id'])
@@ -119,6 +120,6 @@
     def test_create_server_with_ipv6_addr_only(self):
         # Create a server without an IPv4 address(only IPv6 address).
         server = self.create_test_server(accessIPv6='2001:2001::3')
-        self.client.wait_for_server_status(server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.client, server['id'], 'ACTIVE')
         server = self.client.show_server(server['id'])
         self.assertEqual('2001:2001::3', server['accessIPv6'])
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index d13d9d6..fe05456 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -20,6 +20,7 @@
 
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -33,7 +34,8 @@
     def setUp(self):
         super(ServersNegativeTestJSON, self).setUp()
         try:
-            self.client.wait_for_server_status(self.server_id, 'ACTIVE')
+            waiters.wait_for_server_status(self.client, self.server_id,
+                                           'ACTIVE')
         except Exception:
             self.__class__.server_id = self.rebuild_server(self.server_id)
 
@@ -157,7 +159,7 @@
     def test_pause_paused_server(self):
         # Pause a paused server.
         self.client.pause_server(self.server_id)
-        self.client.wait_for_server_status(self.server_id, 'PAUSED')
+        waiters.wait_for_server_status(self.client, self.server_id, 'PAUSED')
         self.assertRaises(lib_exc.Conflict,
                           self.client.pause_server,
                           self.server_id)
@@ -384,7 +386,8 @@
     def test_suspend_server_invalid_state(self):
         # suspend a suspended server.
         self.client.suspend_server(self.server_id)
-        self.client.wait_for_server_status(self.server_id, 'SUSPENDED')
+        waiters.wait_for_server_status(self.client, self.server_id,
+                                       'SUSPENDED')
         self.assertRaises(lib_exc.Conflict,
                           self.client.suspend_server,
                           self.server_id)
@@ -465,12 +468,14 @@
 
         offload_time = CONF.compute.shelved_offload_time
         if offload_time >= 0:
-            self.client.wait_for_server_status(self.server_id,
-                                               'SHELVED_OFFLOADED',
-                                               extra_timeout=offload_time)
+            waiters.wait_for_server_status(self.client,
+                                           self.server_id,
+                                           'SHELVED_OFFLOADED',
+                                           extra_timeout=offload_time)
         else:
-            self.client.wait_for_server_status(self.server_id,
-                                               'SHELVED')
+            waiters.wait_for_server_status(self.client,
+                                           self.server_id,
+                                           'SHELVED')
 
         server = self.client.show_server(self.server_id)
         image_name = server['name'] + '-shelved'
diff --git a/tempest/api/compute/test_live_block_migration_negative.py b/tempest/api/compute/test_live_block_migration_negative.py
index 47251b2..fabe55d 100644
--- a/tempest/api/compute/test_live_block_migration_negative.py
+++ b/tempest/api/compute/test_live_block_migration_negative.py
@@ -17,6 +17,7 @@
 
 from tempest.api.compute import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -55,4 +56,5 @@
 
         self.assertRaises(lib_exc.BadRequest, self._migrate_server_to,
                           server_id, target_host)
-        self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client, server_id,
+                                       'ACTIVE')
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 580fb84..8e4278a 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -17,6 +17,7 @@
 
 from tempest.api.compute import base
 from tempest.common.utils.linux import remote_client
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -43,6 +44,8 @@
 
     @classmethod
     def resource_setup(cls):
+        cls.set_validation_resources()
+
         super(AttachVolumeTestJSON, cls).resource_setup()
         cls.device = CONF.compute.volume_device_name
 
@@ -61,8 +64,10 @@
     def _create_and_attach(self):
         # Start a server and wait for it to become ready
         admin_pass = self.image_ssh_password
-        self.server = self.create_test_server(wait_until='ACTIVE',
-                                              adminPass=admin_pass)
+        self.server = self.create_test_server(
+            validatable=True,
+            wait_until='ACTIVE',
+            adminPass=admin_pass)
 
         # Record addresses so that we can ssh later
         self.server['addresses'] = (
@@ -93,30 +98,38 @@
         self._create_and_attach()
 
         self.servers_client.stop(self.server['id'])
-        self.servers_client.wait_for_server_status(self.server['id'],
-                                                   'SHUTOFF')
+        waiters.wait_for_server_status(self.servers_client, self.server['id'],
+                                       'SHUTOFF')
 
         self.servers_client.start(self.server['id'])
-        self.servers_client.wait_for_server_status(self.server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client, self.server['id'],
+                                       'ACTIVE')
 
-        linux_client = remote_client.RemoteClient(self.server,
-                                                  self.image_ssh_user,
-                                                  self.server['adminPass'])
+        linux_client = remote_client.RemoteClient(
+            self.get_server_ip(self.server),
+            self.image_ssh_user,
+            self.server['adminPass'],
+            self.validation_resources['keypair']['private_key'])
+
         partitions = linux_client.get_partitions()
         self.assertIn(self.device, partitions)
 
         self._detach(self.server['id'], self.volume['id'])
         self.attachment = None
         self.servers_client.stop(self.server['id'])
-        self.servers_client.wait_for_server_status(self.server['id'],
-                                                   'SHUTOFF')
+        waiters.wait_for_server_status(self.servers_client, self.server['id'],
+                                       'SHUTOFF')
 
         self.servers_client.start(self.server['id'])
-        self.servers_client.wait_for_server_status(self.server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client, self.server['id'],
+                                       'ACTIVE')
 
-        linux_client = remote_client.RemoteClient(self.server,
-                                                  self.image_ssh_user,
-                                                  self.server['adminPass'])
+        linux_client = remote_client.RemoteClient(
+            self.get_server_ip(self.server),
+            self.image_ssh_user,
+            self.server['adminPass'],
+            self.validation_resources['keypair']['private_key'])
+
         partitions = linux_client.get_partitions()
         self.assertNotIn(self.device, partitions)
 
diff --git a/tempest/api/network/admin/test_quotas.py b/tempest/api/network/admin/test_quotas.py
index cb113a7..63395cc 100644
--- a/tempest/api/network/admin/test_quotas.py
+++ b/tempest/api/network/admin/test_quotas.py
@@ -61,7 +61,7 @@
 
         # Change quotas for tenant
         quota_set = self.admin_client.update_quotas(tenant_id,
-                                                    **new_quotas)
+                                                    **new_quotas)['quota']
         self.addCleanup(self.admin_client.reset_quotas, tenant_id)
         for key, value in six.iteritems(new_quotas):
             self.assertEqual(value, quota_set[key])
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index 4f54562..6a8fbec 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -156,26 +156,21 @@
         network = self.create_network()
         subnet = self.create_subnet(network)
         self.addCleanup(self.client.delete_subnet, subnet['id'])
-        # Create two ports specifying a fixed_ips
-        address = self._get_ipaddress_from_tempest_conf()
-        _fixed_ip_1 = str(address + 3)
-        _fixed_ip_2 = str(address + 4)
-        fixed_ips_1 = [{'ip_address': _fixed_ip_1}]
-        port_1 = self.client.create_port(network_id=network['id'],
-                                         fixed_ips=fixed_ips_1)
+        # Create two ports
+        port_1 = self.client.create_port(network_id=network['id'])
         self.addCleanup(self.client.delete_port, port_1['port']['id'])
-        fixed_ips_2 = [{'ip_address': _fixed_ip_2}]
-        port_2 = self.client.create_port(network_id=network['id'],
-                                         fixed_ips=fixed_ips_2)
+        port_2 = self.client.create_port(network_id=network['id'])
         self.addCleanup(self.client.delete_port, port_2['port']['id'])
         # List ports filtered by fixed_ips
-        fixed_ips = 'ip_address=' + _fixed_ip_1
+        port_1_fixed_ip = port_1['port']['fixed_ips'][0]['ip_address']
+        fixed_ips = 'ip_address=' + port_1_fixed_ip
         port_list = self.client.list_ports(fixed_ips=fixed_ips)
+        # Check that we got the desired port
         ports = port_list['ports']
         self.assertEqual(len(ports), 1)
         self.assertEqual(ports[0]['id'], port_1['port']['id'])
         self.assertEqual(ports[0]['fixed_ips'][0]['ip_address'],
-                         _fixed_ip_1)
+                         port_1_fixed_ip)
         self.assertEqual(ports[0]['network_id'], network['id'])
 
     @test.idempotent_id('5ad01ed0-0e6e-4c5d-8194-232801b15c72')
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 50671d0..78b51c8 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -270,33 +270,57 @@
     @test.idempotent_id('c86ac3a8-50bd-4b00-a6b8-62af84a0765c')
     @test.requires_ext(extension='extraroute', service='network')
     def test_update_extra_route(self):
-        self.network = self.create_network()
-        self.name = self.network['name']
-        self.subnet = self.create_subnet(self.network)
-        # Add router interface with subnet id
+        # Create different cidr for each subnet to avoid cidr duplicate
+        # The cidr starts from tenant_cidr
+        next_cidr = netaddr.IPNetwork(self.tenant_cidr)
+        # Prepare to build several routes
+        test_routes = []
+        routes_num = 5
+        # Create a router
         self.router = self._create_router(
             data_utils.rand_name('router-'), True)
-        self.create_router_interface(self.router['id'], self.subnet['id'])
         self.addCleanup(
             self._delete_extra_routes,
             self.router['id'])
         # Update router extra route, second ip of the range is
         # used as next hop
-        cidr = netaddr.IPNetwork(self.subnet['cidr'])
-        next_hop = str(cidr[2])
-        destination = str(self.subnet['cidr'])
+        for i in range(routes_num):
+            network = self.create_network()
+            subnet = self.create_subnet(network, cidr=next_cidr)
+            next_cidr = next_cidr.next()
+
+            # Add router interface with subnet id
+            self.create_router_interface(self.router['id'], subnet['id'])
+
+            cidr = netaddr.IPNetwork(subnet['cidr'])
+            next_hop = str(cidr[2])
+            destination = str(subnet['cidr'])
+            test_routes.append(
+                {'nexthop': next_hop, 'destination': destination}
+            )
+
+        test_routes.sort(key=lambda x: x['destination'])
         extra_route = self.client.update_extra_routes(self.router['id'],
-                                                      next_hop, destination)
-        self.assertEqual(1, len(extra_route['router']['routes']))
-        self.assertEqual(destination,
-                         extra_route['router']['routes'][0]['destination'])
-        self.assertEqual(next_hop,
-                         extra_route['router']['routes'][0]['nexthop'])
+                                                      test_routes)
         show_body = self.client.show_router(self.router['id'])
-        self.assertEqual(destination,
-                         show_body['router']['routes'][0]['destination'])
-        self.assertEqual(next_hop,
-                         show_body['router']['routes'][0]['nexthop'])
+        # Assert the number of routes
+        self.assertEqual(routes_num, len(extra_route['router']['routes']))
+        self.assertEqual(routes_num, len(show_body['router']['routes']))
+
+        routes = extra_route['router']['routes']
+        routes.sort(key=lambda x: x['destination'])
+        # Assert the nexthops & destination
+        for i in range(routes_num):
+            self.assertEqual(test_routes[i]['destination'],
+                             routes[i]['destination'])
+            self.assertEqual(test_routes[i]['nexthop'], routes[i]['nexthop'])
+
+        routes = show_body['router']['routes']
+        routes.sort(key=lambda x: x['destination'])
+        for i in range(routes_num):
+            self.assertEqual(test_routes[i]['destination'],
+                             routes[i]['destination'])
+            self.assertEqual(test_routes[i]['nexthop'], routes[i]['nexthop'])
 
     def _delete_extra_routes(self, router_id):
         self.client.delete_extra_routes(router_id)
diff --git a/tempest/api/object_storage/test_object_slo.py b/tempest/api/object_storage/test_object_slo.py
index 9aa1b82..5811cb8 100644
--- a/tempest/api/object_storage/test_object_slo.py
+++ b/tempest/api/object_storage/test_object_slo.py
@@ -13,8 +13,8 @@
 #    under the License.
 
 import hashlib
-import json
 
+from oslo_serialization import jsonutils as json
 from tempest_lib import exceptions as lib_exc
 
 from tempest.api.object_storage import base
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 2c545a7..8015c35 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -38,6 +38,10 @@
 
         cls.volume = cls.create_volume()
 
+    def _delete_backup(self, backup_id):
+        self.backups_adm_client.delete_backup(backup_id)
+        self.backups_adm_client.wait_for_backup_deletion(backup_id)
+
     @test.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
     def test_volume_backup_create_get_detailed_list_restore_delete(self):
         # Create backup
@@ -74,6 +78,52 @@
         self.admin_volume_client.wait_for_volume_status(
             restore['volume_id'], 'available')
 
+    @test.idempotent_id('a99c54a1-dd80-4724-8a13-13bf58d4068d')
+    def test_volume_backup_export_import(self):
+        # Create backup
+        backup_name = data_utils.rand_name('Backup')
+        backup = self.backups_adm_client.create_backup(self.volume['id'],
+                                                       name=backup_name)
+        self.addCleanup(self._delete_backup, backup['id'])
+        self.assertEqual(backup_name, backup['name'])
+        self.backups_adm_client.wait_for_backup_status(backup['id'],
+                                                       'available')
+
+        # Export Backup
+        export_backup = self.backups_adm_client.export_backup(backup['id'])
+        self.assertIn('backup_service', export_backup)
+        self.assertIn('backup_url', export_backup)
+        self.assertTrue(export_backup['backup_service'].startswith(
+                        'cinder.backup.drivers'))
+        self.assertIsNotNone(export_backup['backup_url'])
+
+        # Import Backup
+        import_backup = self.backups_adm_client.import_backup(
+            backup_service=export_backup['backup_service'],
+            backup_url=export_backup['backup_url'])
+        self.addCleanup(self._delete_backup, import_backup['id'])
+        self.assertIn("id", import_backup)
+        self.backups_adm_client.wait_for_backup_status(import_backup['id'],
+                                                       'available')
+
+        # Verify Import Backup
+        backups = self.backups_adm_client.list_backups(detail=True)
+        self.assertIn(import_backup['id'], [b['id'] for b in backups])
+
+        # Restore backup
+        restore = self.backups_adm_client.restore_backup(import_backup['id'])
+        self.addCleanup(self.admin_volume_client.delete_volume,
+                        restore['volume_id'])
+        self.assertEqual(import_backup['id'], restore['backup_id'])
+        self.admin_volume_client.wait_for_volume_status(restore['volume_id'],
+                                                        'available')
+
+        # Verify if restored volume is there in volume list
+        volumes = self.admin_volume_client.list_volumes()
+        self.assertIn(restore['volume_id'], [v['id'] for v in volumes])
+        self.backups_adm_client.wait_for_backup_status(import_backup['id'],
+                                                       'available')
+
 
 class VolumesBackupsV1Test(VolumesBackupsV2Test):
     _api_version = 1
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 0ebcf3a..067c0c1 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -15,6 +15,7 @@
 
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 import testtools
@@ -37,7 +38,8 @@
         # Create a test shared instance
         srv_name = data_utils.rand_name(cls.__name__ + '-Instance')
         cls.server = cls.create_server(srv_name)
-        cls.servers_client.wait_for_server_status(cls.server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(cls.servers_client, cls.server['id'],
+                                       'ACTIVE')
 
         # Create a test shared volume for attach/detach tests
         cls.volume = cls.create_volume()
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index da8f52a..5203444 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -19,6 +19,7 @@
 
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import test
 
 
@@ -180,8 +181,11 @@
     def test_attach_volumes_with_nonexistent_volume_id(self):
         srv_name = data_utils.rand_name('Instance')
         server = self.create_server(srv_name)
+        self.addCleanup(self.servers_client.wait_for_server_termination,
+                        server['id'])
         self.addCleanup(self.servers_client.delete_server, server['id'])
-        self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client, server['id'],
+                                       'ACTIVE')
         self.assertRaises(lib_exc.NotFound,
                           self.client.attach_volume,
                           str(uuid.uuid4()),
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index 340f019..1df1896 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -14,6 +14,7 @@
 
 from tempest.api.volume import base
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest import test
 
@@ -70,7 +71,8 @@
         server_name = data_utils.rand_name('instance')
         server = self.create_server(server_name)
         self.addCleanup(self.servers_client.delete_server, server['id'])
-        self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client, server['id'],
+                                       'ACTIVE')
         mountpoint = '/dev/%s' % CONF.compute.volume_device_name
         self.servers_client.attach_volume(
             server['id'], self.volume_origin['id'], mountpoint)
diff --git a/tempest/api_schema/response/compute/v2_1/migrations.py b/tempest/api_schema/response/compute/v2_1/migrations.py
index 722372c..b7d66ea 100644
--- a/tempest/api_schema/response/compute/v2_1/migrations.py
+++ b/tempest/api_schema/response/compute/v2_1/migrations.py
@@ -23,15 +23,15 @@
                     'type': 'object',
                     'properties': {
                         'id': {'type': 'integer'},
-                        'status': {'type': 'string'},
-                        'instance_uuid': {'type': 'string'},
-                        'source_node': {'type': 'string'},
-                        'source_compute': {'type': 'string'},
-                        'dest_node': {'type': 'string'},
-                        'dest_compute': {'type': 'string'},
-                        'dest_host': {'type': 'string'},
-                        'old_instance_type_id': {'type': 'integer'},
-                        'new_instance_type_id': {'type': 'integer'},
+                        'status': {'type': ['string', 'null']},
+                        'instance_uuid': {'type': ['string', 'null']},
+                        'source_node': {'type': ['string', 'null']},
+                        'source_compute': {'type': ['string', 'null']},
+                        'dest_node': {'type': ['string', 'null']},
+                        'dest_compute': {'type': ['string', 'null']},
+                        'dest_host': {'type': ['string', 'null']},
+                        'old_instance_type_id': {'type': ['integer', 'null']},
+                        'new_instance_type_id': {'type': ['integer', 'null']},
                         'created_at': {'type': 'string'},
                         'updated_at': {'type': ['string', 'null']}
                     },
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
index ed6716e..7898035 100755
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -51,10 +51,10 @@
 Please run with **--help** to see full list of options.
 """
 import argparse
-import json
 import sys
 
 from oslo_log import log as logging
+from oslo_serialization import jsonutils as json
 
 from tempest import clients
 from tempest.cmd import cleanup_service
diff --git a/tempest/cmd/run_stress.py b/tempest/cmd/run_stress.py
index 06b338d..0448589 100755
--- a/tempest/cmd/run_stress.py
+++ b/tempest/cmd/run_stress.py
@@ -16,7 +16,6 @@
 
 import argparse
 import inspect
-import json
 import sys
 try:
     from unittest import loader
@@ -25,6 +24,7 @@
     from unittest2 import loader
 
 from oslo_log import log as logging
+from oslo_serialization import jsonutils as json
 from testtools import testsuite
 
 from tempest.stress import driver
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 2269d41..9e7d894 100755
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -15,11 +15,11 @@
 #    under the License.
 
 import argparse
-import json
 import os
 import sys
 
 import httplib2
+from oslo_serialization import jsonutils as json
 from six import moves
 from six.moves.urllib import parse as urlparse
 
diff --git a/tempest/common/accounts.py b/tempest/common/accounts.py
index 650faf1..78e0e72 100644
--- a/tempest/common/accounts.py
+++ b/tempest/common/accounts.py
@@ -31,8 +31,8 @@
 
 
 def read_accounts_yaml(path):
-    yaml_file = open(path, 'r')
-    accounts = yaml.load(yaml_file)
+    with open(path, 'r') as yaml_file:
+        accounts = yaml.load(yaml_file)
     return accounts
 
 
diff --git a/tempest/common/compute.py b/tempest/common/compute.py
index 5de4b0e..06e3493 100644
--- a/tempest/common/compute.py
+++ b/tempest/common/compute.py
@@ -18,6 +18,7 @@
 from tempest_lib.common.utils import data_utils
 
 from tempest.common import fixed_network
+from tempest.common import waiters
 from tempest import config
 
 CONF = config.CONF
@@ -98,8 +99,8 @@
     if 'wait_until' in kwargs:
         for server in servers:
             try:
-                clients.servers_client.wait_for_server_status(
-                    server['id'], kwargs['wait_until'])
+                waiters.wait_for_server_status(
+                    clients.servers_client, server['id'], kwargs['wait_until'])
 
                 # Multiple validatable servers are not supported for now. Their
                 # creation will fail with the condition above (l.58).
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index 5cd844c..4be3da1 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -17,7 +17,6 @@
 
 import copy
 import hashlib
-import json
 import posixpath
 import re
 import socket
@@ -25,6 +24,7 @@
 
 import OpenSSL
 from oslo_log import log as logging
+from oslo_serialization import jsonutils as json
 import six
 from six import moves
 from six.moves import http_client as httplib
diff --git a/tempest/common/isolated_creds.py b/tempest/common/isolated_creds.py
index 2102638..ff4eda9 100644
--- a/tempest/common/isolated_creds.py
+++ b/tempest/common/isolated_creds.py
@@ -51,11 +51,21 @@
     def create_project(self, name, description):
         pass
 
-    def assign_user_role(self, user, project, role_name):
+    def _check_role_exists(self, role_name):
         try:
             roles = self._list_roles()
             role = next(r for r in roles if r['name'] == role_name)
         except StopIteration:
+            return None
+        return role
+
+    def create_user_role(self, role_name):
+        if not self._check_role_exists(role_name):
+            self.identity_client.create_role(role_name)
+
+    def assign_user_role(self, user, project, role_name):
+        role = self._check_role_exists(role_name)
+        if not role:
             msg = 'No "%s" role found' % role_name
             raise lib_exc.NotFound(msg)
         try:
@@ -196,16 +206,27 @@
         email = data_utils.rand_name(root) + suffix + "@example.com"
         user = self.creds_client.create_user(
             username, user_password, project, email)
+        role_assigned = False
         if admin:
             self.creds_client.assign_user_role(user, project,
                                                CONF.identity.admin_role)
+            role_assigned = True
         # Add roles specified in config file
         for conf_role in CONF.auth.tempest_roles:
             self.creds_client.assign_user_role(user, project, conf_role)
+            role_assigned = True
         # Add roles requested by caller
         if roles:
             for role in roles:
                 self.creds_client.assign_user_role(user, project, role)
+                role_assigned = True
+        # NOTE(mtreinish) For a user to have access to a project with v3 auth
+        # it must beassigned a role on the project. So we need to ensure that
+        # our newly created user has a role on the newly created project.
+        if self.identity_version == 'v3' and not role_assigned:
+            self.creds_client.create_user_role('Member')
+            self.creds_client.assign_user_role(user, project, 'Member')
+
         creds = self.creds_client.get_credentials(user, project, user_password)
         return cred_provider.TestResources(creds)
 
diff --git a/tempest/config.py b/tempest/config.py
index 7382088..a9f1e01 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -388,6 +388,14 @@
                      'encrypted volume to a running server instance? This may '
                      'depend on the combination of compute_driver in nova and '
                      'the volume_driver(s) in cinder.'),
+    # TODO(mriedem): Remove allow_duplicate_networks once kilo-eol happens
+    # since the option was removed from nova in Liberty and is the default
+    # behavior starting in Liberty.
+    cfg.BoolOpt('allow_duplicate_networks',
+                default=False,
+                help='Does the test environment support creating instances '
+                     'with multiple ports on the same network? This is only '
+                     'valid when using Neutron.'),
 ]
 
 
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 95a8356..03e572f 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -25,6 +25,7 @@
 from tempest.common import fixed_network
 from tempest.common.utils import data_utils
 from tempest.common.utils.linux import remote_client
+from tempest.common import waiters
 from tempest import config
 from tempest import exceptions
 from tempest.services.network import resources as net_resources
@@ -177,8 +178,9 @@
             cleanup_callable=self.delete_wrapper,
             cleanup_args=[self.servers_client.delete_server, server['id']])
         if wait_on_boot:
-            self.servers_client.wait_for_server_status(server_id=server['id'],
-                                                       status='ACTIVE')
+            waiters.wait_for_server_status(self.servers_client,
+                                           server_id=server['id'],
+                                           status='ACTIVE')
         # The instance retrieved on creation is missing network
         # details, necessitating retrieval after it becomes active to
         # ensure correct details.
@@ -443,7 +445,8 @@
                                     preserve_ephemeral=preserve_ephemeral,
                                     **rebuild_kwargs)
         if wait:
-            self.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+            waiters.wait_for_server_status(self.servers_client,
+                                           server_id, 'ACTIVE')
 
     def ping_ip_address(self, ip_address, should_succeed=True,
                         ping_timeout=None):
@@ -1226,8 +1229,8 @@
                                      BaremetalProvisionStates.ACTIVE,
                                      timeout=CONF.baremetal.active_timeout)
 
-        self.servers_client.wait_for_server_status(self.instance['id'],
-                                                   'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client,
+                                       self.instance['id'], 'ACTIVE')
         self.node = self.get_node(instance_id=self.instance['id'])
         self.instance = self.servers_client.show_server(self.instance['id'])
 
diff --git a/tempest/scenario/test_baremetal_basic_ops.py b/tempest/scenario/test_baremetal_basic_ops.py
index 9fdba39..346f56b 100644
--- a/tempest/scenario/test_baremetal_basic_ops.py
+++ b/tempest/scenario/test_baremetal_basic_ops.py
@@ -15,6 +15,7 @@
 
 from oslo_log import log as logging
 
+from tempest.common import waiters
 from tempest import config
 from tempest.scenario import manager
 from tempest import test
@@ -52,11 +53,13 @@
         self.assertEqual(self.node['uuid'], node['uuid'])
         self.node = node
 
-        self.servers_client.wait_for_server_status(
+        waiters.wait_for_server_status(
+            self.servers_client,
             server_id=self.instance['id'],
             status='REBUILD',
             ready_wait=False)
-        self.servers_client.wait_for_server_status(
+        waiters.wait_for_server_status(
+            self.servers_client,
             server_id=self.instance['id'],
             status='ACTIVE')
 
diff --git a/tempest/scenario/test_large_ops.py b/tempest/scenario/test_large_ops.py
index 1a18be5..fa70d3f 100644
--- a/tempest/scenario/test_large_ops.py
+++ b/tempest/scenario/test_large_ops.py
@@ -18,6 +18,7 @@
 
 from tempest.common import fixed_network
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest.scenario import manager
 from tempest import test
@@ -76,7 +77,8 @@
         for server in self.servers:
             # Make sure nova list keeps working throughout the build process
             self.servers_client.list_servers()
-            self.servers_client.wait_for_server_status(server['id'], status)
+            waiters.wait_for_server_status(self.servers_client,
+                                           server['id'], status)
 
     def nova_boot(self):
         name = data_utils.rand_name('scenario-server')
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 6057cd3..f868382 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -16,6 +16,7 @@
 from oslo_log import log as logging
 
 from tempest.common import custom_matchers
+from tempest.common import waiters
 from tempest import config
 from tempest import exceptions
 from tempest.scenario import manager
@@ -43,7 +44,8 @@
         server_id = self.server['id']
         # Raise on error defaults to True, which is consistent with the
         # original function from scenario tests here
-        self.servers_client.wait_for_server_status(server_id, status)
+        waiters.wait_for_server_status(self.servers_client,
+                                       server_id, status)
 
     def nova_keypair_add(self):
         self.keypair = self.create_keypair()
@@ -76,15 +78,6 @@
         volume = self.volumes_client.show_volume(self.volume['id'])
         self.assertEqual(self.volume, volume)
 
-    def nova_volume_attach(self):
-        volume_device_path = '/dev/' + CONF.compute.volume_device_name
-        volume = self.servers_client.attach_volume(
-            self.server['id'], self.volume['id'], volume_device_path)
-        self.assertEqual(self.volume['id'], volume['id'])
-        self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
-        # Refresh the volume after the attachment
-        self.volume = self.volumes_client.show_volume(volume['id'])
-
     def nova_reboot(self):
         self.servers_client.reboot(self.server['id'], 'SOFT')
         self._wait_for_server_status('ACTIVE')
@@ -94,14 +87,6 @@
         partitions = self.linux_client.get_partitions()
         self.assertEqual(1, partitions.count(CONF.compute.volume_device_name))
 
-    def nova_volume_detach(self):
-        self.servers_client.detach_volume(self.server['id'], self.volume['id'])
-        self.volumes_client.wait_for_volume_status(self.volume['id'],
-                                                   'available')
-
-        volume = self.volumes_client.show_volume(self.volume['id'])
-        self.assertEqual('available', volume['status'])
-
     def create_and_add_security_group(self):
         secgroup = self._create_security_group()
         self.servers_client.add_security_group(self.server['id'],
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index c864379..3d233d8 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -17,6 +17,7 @@
 import testtools
 
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest.scenario import manager
 from tempest import test
@@ -90,7 +91,8 @@
                                                servers=[self.server])
 
     def _wait_server_status_and_check_network_connectivity(self):
-        self.servers_client.wait_for_server_status(self.server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client,
+                                       self.server['id'], 'ACTIVE')
         self._check_network_connectivity()
 
     @test.idempotent_id('61f1aa9a-1573-410e-9054-afa557cab021')
@@ -99,8 +101,8 @@
     def test_server_connectivity_stop_start(self):
         self._setup_network_and_servers()
         self.servers_client.stop(self.server['id'])
-        self.servers_client.wait_for_server_status(self.server['id'],
-                                                   'SHUTOFF')
+        waiters.wait_for_server_status(self.servers_client,
+                                       self.server['id'], 'SHUTOFF')
         self._check_network_connectivity(should_connect=False)
         self.servers_client.start(self.server['id'])
         self._wait_server_status_and_check_network_connectivity()
@@ -128,7 +130,8 @@
     def test_server_connectivity_pause_unpause(self):
         self._setup_network_and_servers()
         self.servers_client.pause_server(self.server['id'])
-        self.servers_client.wait_for_server_status(self.server['id'], 'PAUSED')
+        waiters.wait_for_server_status(self.servers_client,
+                                       self.server['id'], 'PAUSED')
         self._check_network_connectivity(should_connect=False)
         self.servers_client.unpause_server(self.server['id'])
         self._wait_server_status_and_check_network_connectivity()
@@ -140,8 +143,8 @@
     def test_server_connectivity_suspend_resume(self):
         self._setup_network_and_servers()
         self.servers_client.suspend_server(self.server['id'])
-        self.servers_client.wait_for_server_status(self.server['id'],
-                                                   'SUSPENDED')
+        waiters.wait_for_server_status(self.servers_client, self.server['id'],
+                                       'SUSPENDED')
         self._check_network_connectivity(should_connect=False)
         self.servers_client.resume_server(self.server['id'])
         self._wait_server_status_and_check_network_connectivity()
@@ -157,7 +160,7 @@
             raise self.skipException(msg)
         self._setup_network_and_servers()
         self.servers_client.resize(self.server['id'], flavor_ref=resize_flavor)
-        self.servers_client.wait_for_server_status(self.server['id'],
-                                                   'VERIFY_RESIZE')
+        waiters.wait_for_server_status(self.servers_client, self.server['id'],
+                                       'VERIFY_RESIZE')
         self.servers_client.confirm_resize(self.server['id'])
         self._wait_server_status_and_check_network_connectivity()
diff --git a/tempest/scenario/test_server_advanced_ops.py b/tempest/scenario/test_server_advanced_ops.py
index 04d5c8b..e405cf5 100644
--- a/tempest/scenario/test_server_advanced_ops.py
+++ b/tempest/scenario/test_server_advanced_ops.py
@@ -16,6 +16,7 @@
 from oslo_log import log as logging
 import testtools
 
+from tempest.common import waiters
 from tempest import config
 from tempest.scenario import manager
 from tempest import test
@@ -58,14 +59,14 @@
         LOG.debug("Resizing instance %s from flavor %s to flavor %s",
                   instance['id'], instance['flavor']['id'], resize_flavor)
         self.servers_client.resize(instance_id, resize_flavor)
-        self.servers_client.wait_for_server_status(instance_id,
-                                                   'VERIFY_RESIZE')
+        waiters.wait_for_server_status(self.servers_client, instance_id,
+                                       'VERIFY_RESIZE')
 
         LOG.debug("Confirming resize of instance %s", instance_id)
         self.servers_client.confirm_resize(instance_id)
 
-        self.servers_client.wait_for_server_status(instance_id,
-                                                   'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client, instance_id,
+                                       'ACTIVE')
 
     @test.idempotent_id('949da7d5-72c8-4808-8802-e3d70df98e2c')
     @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
@@ -78,23 +79,23 @@
         LOG.debug("Suspending instance %s. Current status: %s",
                   instance_id, instance['status'])
         self.servers_client.suspend_server(instance_id)
-        self.servers_client.wait_for_server_status(instance_id,
-                                                   'SUSPENDED')
+        waiters.wait_for_server_status(self.servers_client, instance_id,
+                                       'SUSPENDED')
         fetched_instance = self.servers_client.show_server(instance_id)
         LOG.debug("Resuming instance %s. Current status: %s",
                   instance_id, fetched_instance['status'])
         self.servers_client.resume_server(instance_id)
-        self.servers_client.wait_for_server_status(instance_id,
-                                                   'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client, instance_id,
+                                       'ACTIVE')
         fetched_instance = self.servers_client.show_server(instance_id)
         LOG.debug("Suspending instance %s. Current status: %s",
                   instance_id, fetched_instance['status'])
         self.servers_client.suspend_server(instance_id)
-        self.servers_client.wait_for_server_status(instance_id,
-                                                   'SUSPENDED')
+        waiters.wait_for_server_status(self.servers_client, instance_id,
+                                       'SUSPENDED')
         fetched_instance = self.servers_client.show_server(instance_id)
         LOG.debug("Resuming instance %s. Current status: %s",
                   instance_id, fetched_instance['status'])
         self.servers_client.resume_server(instance_id)
-        self.servers_client.wait_for_server_status(instance_id,
-                                                   'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client, instance_id,
+                                       'ACTIVE')
diff --git a/tempest/scenario/test_shelve_instance.py b/tempest/scenario/test_shelve_instance.py
index e674101..02ee7b9 100644
--- a/tempest/scenario/test_shelve_instance.py
+++ b/tempest/scenario/test_shelve_instance.py
@@ -16,6 +16,7 @@
 from oslo_log import log
 import testtools
 
+from tempest.common import waiters
 from tempest import config
 from tempest.scenario import manager
 from tempest import test
@@ -50,15 +51,18 @@
         self.servers_client.shelve_server(server['id'])
         offload_time = CONF.compute.shelved_offload_time
         if offload_time >= 0:
-            self.servers_client.wait_for_server_status(
-                server['id'], 'SHELVED_OFFLOADED', extra_timeout=offload_time)
+            waiters.wait_for_server_status(self.servers_client, server['id'],
+                                           'SHELVED_OFFLOADED',
+                                           extra_timeout=offload_time)
         else:
-            self.servers_client.wait_for_server_status(server['id'], 'SHELVED')
+            waiters.wait_for_server_status(self.servers_client,
+                                           server['id'], 'SHELVED')
             self.servers_client.shelve_offload_server(server['id'])
-            self.servers_client.wait_for_server_status(server['id'],
-                                                       'SHELVED_OFFLOADED')
+            waiters.wait_for_server_status(self.servers_client, server['id'],
+                                           'SHELVED_OFFLOADED')
         self.servers_client.unshelve_server(server['id'])
-        self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+        waiters.wait_for_server_status(self.servers_client, server['id'],
+                                       'ACTIVE')
 
     @test.idempotent_id('1164e700-0af0-4a4c-8792-35909a88743c')
     @testtools.skipUnless(CONF.compute_feature_enabled.shelve,
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index 102e9a3..45b7b74 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -13,6 +13,7 @@
 from oslo_log import log
 
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 from tempest.scenario import manager
 from tempest import test
@@ -85,7 +86,8 @@
         for i in instances:
             self.servers_client.stop(i['id'])
         for i in instances:
-            self.servers_client.wait_for_server_status(i['id'], 'SHUTOFF')
+            waiters.wait_for_server_status(self.servers_client,
+                                           i['id'], 'SHUTOFF')
 
     def _detach_volumes(self, volumes):
         # NOTE(gfidente): two loops so we do not wait for the status twice
diff --git a/tempest/scenario/utils.py b/tempest/scenario/utils.py
index cd9d925..b1246d2 100644
--- a/tempest/scenario/utils.py
+++ b/tempest/scenario/utils.py
@@ -13,11 +13,11 @@
 #    under the License.
 
 
-import json
 import re
 import string
 import unicodedata
 
+from oslo_serialization import jsonutils as json
 from tempest_lib.common.utils import misc
 import testscenarios
 import testtools
diff --git a/tempest/services/baremetal/base.py b/tempest/services/baremetal/base.py
index 1461198..2ac3fb2 100644
--- a/tempest/services/baremetal/base.py
+++ b/tempest/services/baremetal/base.py
@@ -11,8 +11,8 @@
 #    under the License.
 
 import functools
-import json
 
+from oslo_serialization import jsonutils as json
 import six
 from six.moves.urllib import parse as urllib
 
diff --git a/tempest/services/compute/json/agents_client.py b/tempest/services/compute/json/agents_client.py
index e0e3ec3..1269991 100644
--- a/tempest/services/compute/json/agents_client.py
+++ b/tempest/services/compute/json/agents_client.py
@@ -12,8 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
 from tempest.api_schema.response.compute.v2_1 import agents as schema
@@ -25,7 +24,7 @@
     Tests Agents API
     """
 
-    def list_agents(self, params=None):
+    def list_agents(self, **params):
         """List all agent builds."""
         url = 'os-agents'
         if params:
diff --git a/tempest/services/compute/json/aggregates_client.py b/tempest/services/compute/json/aggregates_client.py
index eeeceb7..4114b8b 100644
--- a/tempest/services/compute/json/aggregates_client.py
+++ b/tempest/services/compute/json/aggregates_client.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from tempest_lib import exceptions as lib_exc
 
 from tempest.api_schema.response.compute.v2_1 import aggregates as schema
diff --git a/tempest/services/compute/json/availability_zone_client.py b/tempest/services/compute/json/availability_zone_client.py
index 9686854..c74fd10 100644
--- a/tempest/services/compute/json/availability_zone_client.py
+++ b/tempest/services/compute/json/availability_zone_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.api_schema.response.compute.v2_1 import availability_zone \
     as schema
diff --git a/tempest/services/compute/json/baremetal_nodes_client.py b/tempest/services/compute/json/baremetal_nodes_client.py
index 42caa7b..8165292 100644
--- a/tempest/services/compute/json/baremetal_nodes_client.py
+++ b/tempest/services/compute/json/baremetal_nodes_client.py
@@ -12,8 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
 from tempest.api_schema.response.compute.v2_1 import baremetal_nodes \
@@ -26,7 +25,7 @@
     Tests Baremetal API
     """
 
-    def list_baremetal_nodes(self, params=None):
+    def list_baremetal_nodes(self, **params):
         """List all baremetal nodes."""
         url = 'os-baremetal-nodes'
         if params:
diff --git a/tempest/services/compute/json/certificates_client.py b/tempest/services/compute/json/certificates_client.py
index da1aa94..c25b273 100644
--- a/tempest/services/compute/json/certificates_client.py
+++ b/tempest/services/compute/json/certificates_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.api_schema.response.compute.v2_1 import certificates as schema
 from tempest.common import service_client
diff --git a/tempest/services/compute/json/extensions_client.py b/tempest/services/compute/json/extensions_client.py
index ec60872..da342a8 100644
--- a/tempest/services/compute/json/extensions_client.py
+++ b/tempest/services/compute/json/extensions_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.api_schema.response.compute.v2_1 import extensions as schema
 from tempest.common import service_client
diff --git a/tempest/services/compute/json/fixed_ips_client.py b/tempest/services/compute/json/fixed_ips_client.py
index b921107..69de79f 100644
--- a/tempest/services/compute/json/fixed_ips_client.py
+++ b/tempest/services/compute/json/fixed_ips_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.api_schema.response.compute.v2_1 import fixed_ips as schema
 from tempest.common import service_client
diff --git a/tempest/services/compute/json/flavors_client.py b/tempest/services/compute/json/flavors_client.py
index 2f7de3f..b928f9f 100644
--- a/tempest/services/compute/json/flavors_client.py
+++ b/tempest/services/compute/json/flavors_client.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
 from tempest.api_schema.response.compute.v2_1 import flavors as schema
diff --git a/tempest/services/compute/json/floating_ips_client.py b/tempest/services/compute/json/floating_ips_client.py
index 1a32861..2193949 100644
--- a/tempest/services/compute/json/floating_ips_client.py
+++ b/tempest/services/compute/json/floating_ips_client.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 from tempest_lib import exceptions as lib_exc
 
@@ -24,7 +23,7 @@
 
 class FloatingIPsClient(service_client.ServiceClient):
 
-    def list_floating_ips(self, params=None):
+    def list_floating_ips(self, **params):
         """Returns a list of all floating IPs filtered by any parameters."""
         url = 'os-floating-ips'
         if params:
diff --git a/tempest/services/compute/json/hosts_client.py b/tempest/services/compute/json/hosts_client.py
index 0510104..752af68 100644
--- a/tempest/services/compute/json/hosts_client.py
+++ b/tempest/services/compute/json/hosts_client.py
@@ -12,8 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
 from tempest.api_schema.response.compute.v2_1 import hosts as schema
@@ -22,7 +21,7 @@
 
 class HostsClient(service_client.ServiceClient):
 
-    def list_hosts(self, params=None):
+    def list_hosts(self, **params):
         """Lists all hosts."""
 
         url = 'os-hosts'
diff --git a/tempest/services/compute/json/hypervisor_client.py b/tempest/services/compute/json/hypervisor_client.py
index 61a4dc1..e894a5c 100644
--- a/tempest/services/compute/json/hypervisor_client.py
+++ b/tempest/services/compute/json/hypervisor_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.api_schema.response.compute.v2_1 import hypervisors as schema
 from tempest.common import service_client
diff --git a/tempest/services/compute/json/images_client.py b/tempest/services/compute/json/images_client.py
index 172bbe2..b0ce2dc 100644
--- a/tempest/services/compute/json/images_client.py
+++ b/tempest/services/compute/json/images_client.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 from tempest_lib import exceptions as lib_exc
 
diff --git a/tempest/services/compute/json/instance_usage_audit_log_client.py b/tempest/services/compute/json/instance_usage_audit_log_client.py
index c0841bf..f06a675 100644
--- a/tempest/services/compute/json/instance_usage_audit_log_client.py
+++ b/tempest/services/compute/json/instance_usage_audit_log_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.api_schema.response.compute.v2_1 import \
     instance_usage_audit_logs as schema
diff --git a/tempest/services/compute/json/interfaces_client.py b/tempest/services/compute/json/interfaces_client.py
index e5ab965..e8b2b64 100644
--- a/tempest/services/compute/json/interfaces_client.py
+++ b/tempest/services/compute/json/interfaces_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.api_schema.response.compute.v2_1 import interfaces as schema
 from tempest.api_schema.response.compute.v2_1 import servers as servers_schema
diff --git a/tempest/services/compute/json/keypairs_client.py b/tempest/services/compute/json/keypairs_client.py
index cf03a24..6f819ae 100644
--- a/tempest/services/compute/json/keypairs_client.py
+++ b/tempest/services/compute/json/keypairs_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.api_schema.response.compute.v2_1 import keypairs as schema
 from tempest.common import service_client
diff --git a/tempest/services/compute/json/limits_client.py b/tempest/services/compute/json/limits_client.py
index bfa340c..4287619 100644
--- a/tempest/services/compute/json/limits_client.py
+++ b/tempest/services/compute/json/limits_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.api_schema.response.compute.v2_1 import limits as schema
 from tempest.common import service_client
diff --git a/tempest/services/compute/json/migrations_client.py b/tempest/services/compute/json/migrations_client.py
index cd04e45..06c8f13 100644
--- a/tempest/services/compute/json/migrations_client.py
+++ b/tempest/services/compute/json/migrations_client.py
@@ -12,8 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
 from tempest.api_schema.response.compute.v2_1 import migrations as schema
@@ -22,7 +21,7 @@
 
 class MigrationsClient(service_client.ServiceClient):
 
-    def list_migrations(self, params=None):
+    def list_migrations(self, **params):
         """Lists all migrations."""
 
         url = 'os-migrations'
diff --git a/tempest/services/compute/json/networks_client.py b/tempest/services/compute/json/networks_client.py
index cf9f534..6373f01 100644
--- a/tempest/services/compute/json/networks_client.py
+++ b/tempest/services/compute/json/networks_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.common import service_client
 
diff --git a/tempest/services/compute/json/quota_classes_client.py b/tempest/services/compute/json/quota_classes_client.py
index a741be4..30d3501 100644
--- a/tempest/services/compute/json/quota_classes_client.py
+++ b/tempest/services/compute/json/quota_classes_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.api_schema.response.compute.v2_1\
     import quota_classes as classes_schema
diff --git a/tempest/services/compute/json/quotas_client.py b/tempest/services/compute/json/quotas_client.py
index b038508..4ea47ed 100644
--- a/tempest/services/compute/json/quotas_client.py
+++ b/tempest/services/compute/json/quotas_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.api_schema.response.compute.v2_1 import quotas as schema
 from tempest.common import service_client
diff --git a/tempest/services/compute/json/security_group_default_rules_client.py b/tempest/services/compute/json/security_group_default_rules_client.py
index 304e759..fcc715a 100644
--- a/tempest/services/compute/json/security_group_default_rules_client.py
+++ b/tempest/services/compute/json/security_group_default_rules_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.api_schema.response.compute.v2_1 import \
     security_group_default_rule as schema
diff --git a/tempest/services/compute/json/security_groups_client.py b/tempest/services/compute/json/security_groups_client.py
index ca4f9bc..5a3d771 100644
--- a/tempest/services/compute/json/security_groups_client.py
+++ b/tempest/services/compute/json/security_groups_client.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 from tempest_lib import exceptions as lib_exc
 
@@ -24,7 +23,7 @@
 
 class SecurityGroupsClient(service_client.ServiceClient):
 
-    def list_security_groups(self, params=None):
+    def list_security_groups(self, **params):
         """List all security groups for a user."""
 
         url = 'os-security-groups'
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 1a3864c..1159a58 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -14,15 +14,14 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
 import time
 
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 from tempest_lib import exceptions as lib_exc
 
 from tempest.api_schema.response.compute.v2_1 import servers as schema
 from tempest.common import service_client
-from tempest.common import waiters
 from tempest import exceptions
 
 
@@ -166,14 +165,6 @@
         self.validate_response(_schema, resp, body)
         return service_client.ResponseBody(resp, body)
 
-    def wait_for_server_status(self, server_id, status, extra_timeout=0,
-                               raise_on_error=True, ready_wait=True):
-        """Waits for a server to reach a given status."""
-        return waiters.wait_for_server_status(self, server_id, status,
-                                              extra_timeout=extra_timeout,
-                                              raise_on_error=raise_on_error,
-                                              ready_wait=ready_wait)
-
     def wait_for_server_termination(self, server_id, ignore_error=False):
         """Waits for server to reach termination."""
         start_time = int(time.time())
diff --git a/tempest/services/compute/json/services_client.py b/tempest/services/compute/json/services_client.py
index 01f96af..699d3e7 100644
--- a/tempest/services/compute/json/services_client.py
+++ b/tempest/services/compute/json/services_client.py
@@ -14,8 +14,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
 from tempest.api_schema.response.compute.v2_1 import services as schema
@@ -24,7 +23,7 @@
 
 class ServicesClient(service_client.ServiceClient):
 
-    def list_services(self, params=None):
+    def list_services(self, **params):
         url = 'os-services'
         if params:
             url += '?%s' % urllib.urlencode(params)
diff --git a/tempest/services/compute/json/tenant_networks_client.py b/tempest/services/compute/json/tenant_networks_client.py
index b15ebe3..d31c5f5 100644
--- a/tempest/services/compute/json/tenant_networks_client.py
+++ b/tempest/services/compute/json/tenant_networks_client.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.api_schema.response.compute.v2_1 import tenant_networks as schema
 from tempest.common import service_client
diff --git a/tempest/services/compute/json/tenant_usages_client.py b/tempest/services/compute/json/tenant_usages_client.py
index fac412e..72fcde2 100644
--- a/tempest/services/compute/json/tenant_usages_client.py
+++ b/tempest/services/compute/json/tenant_usages_client.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
 from tempest.api_schema.response.compute.v2_1 import tenant_usages as schema
@@ -23,7 +22,7 @@
 
 class TenantUsagesClient(service_client.ServiceClient):
 
-    def list_tenant_usages(self, params=None):
+    def list_tenant_usages(self, **params):
         url = 'os-simple-tenant-usage'
         if params:
             url += '?%s' % urllib.urlencode(params)
@@ -33,7 +32,7 @@
         self.validate_response(schema.list_tenant_usage, resp, body)
         return service_client.ResponseBodyList(resp, body['tenant_usages'][0])
 
-    def show_tenant_usage(self, tenant_id, params=None):
+    def show_tenant_usage(self, tenant_id, **params):
         url = 'os-simple-tenant-usage/%s' % tenant_id
         if params:
             url += '?%s' % urllib.urlencode(params)
diff --git a/tempest/services/compute/json/volumes_extensions_client.py b/tempest/services/compute/json/volumes_extensions_client.py
index 121f160..ac55049 100644
--- a/tempest/services/compute/json/volumes_extensions_client.py
+++ b/tempest/services/compute/json/volumes_extensions_client.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 from tempest_lib import exceptions as lib_exc
 
diff --git a/tempest/services/data_processing/v1_1/data_processing_client.py b/tempest/services/data_processing/v1_1/data_processing_client.py
index 04cf9a3..bbc0f2a 100644
--- a/tempest/services/data_processing/v1_1/data_processing_client.py
+++ b/tempest/services/data_processing/v1_1/data_processing_client.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.common import service_client
 
diff --git a/tempest/services/identity/v2/json/identity_client.py b/tempest/services/identity/v2/json/identity_client.py
index fd4160f..1076fca 100644
--- a/tempest/services/identity/v2/json/identity_client.py
+++ b/tempest/services/identity/v2/json/identity_client.py
@@ -10,7 +10,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 from tempest_lib import exceptions as lib_exc
 
 from tempest.common import service_client
diff --git a/tempest/services/identity/v3/json/credentials_client.py b/tempest/services/identity/v3/json/credentials_client.py
index 281f3cd..e27f960 100644
--- a/tempest/services/identity/v3/json/credentials_client.py
+++ b/tempest/services/identity/v3/json/credentials_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.common import service_client
 
diff --git a/tempest/services/identity/v3/json/endpoints_client.py b/tempest/services/identity/v3/json/endpoints_client.py
index 44acb47..f93fb74 100644
--- a/tempest/services/identity/v3/json/endpoints_client.py
+++ b/tempest/services/identity/v3/json/endpoints_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.common import service_client
 
diff --git a/tempest/services/identity/v3/json/identity_client.py b/tempest/services/identity/v3/json/identity_client.py
index d8afd2c..87d4b79 100644
--- a/tempest/services/identity/v3/json/identity_client.py
+++ b/tempest/services/identity/v3/json/identity_client.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
 from tempest.common import service_client
diff --git a/tempest/services/identity/v3/json/policy_client.py b/tempest/services/identity/v3/json/policy_client.py
index 424fb1d..f820598 100644
--- a/tempest/services/identity/v3/json/policy_client.py
+++ b/tempest/services/identity/v3/json/policy_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.common import service_client
 
diff --git a/tempest/services/identity/v3/json/region_client.py b/tempest/services/identity/v3/json/region_client.py
index 9d7d496..43226be 100644
--- a/tempest/services/identity/v3/json/region_client.py
+++ b/tempest/services/identity/v3/json/region_client.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
 from tempest.common import service_client
diff --git a/tempest/services/identity/v3/json/service_client.py b/tempest/services/identity/v3/json/service_client.py
index c678918..52ff479 100644
--- a/tempest/services/identity/v3/json/service_client.py
+++ b/tempest/services/identity/v3/json/service_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.common import service_client
 
diff --git a/tempest/services/image/v1/json/image_client.py b/tempest/services/image/v1/json/image_client.py
index eae5c64..a07612a 100644
--- a/tempest/services/image/v1/json/image_client.py
+++ b/tempest/services/image/v1/json/image_client.py
@@ -15,11 +15,11 @@
 
 import copy
 import errno
-import json
 import os
 import time
 
 from oslo_log import log as logging
+from oslo_serialization import jsonutils as json
 import six
 from six.moves.urllib import parse as urllib
 from tempest_lib.common.utils import misc as misc_utils
diff --git a/tempest/services/image/v2/json/image_client.py b/tempest/services/image/v2/json/image_client.py
index 95972ae..67f7708 100644
--- a/tempest/services/image/v2/json/image_client.py
+++ b/tempest/services/image/v2/json/image_client.py
@@ -13,9 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
 import jsonschema
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 from tempest_lib import exceptions as lib_exc
 
diff --git a/tempest/services/messaging/json/messaging_client.py b/tempest/services/messaging/json/messaging_client.py
index b99e0a7..2f233a9 100644
--- a/tempest/services/messaging/json/messaging_client.py
+++ b/tempest/services/messaging/json/messaging_client.py
@@ -13,9 +13,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import json
 import uuid
 
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
 from tempest.api_schema.response.messaging.v1 import queues as queues_schema
diff --git a/tempest/services/network/json/network_client.py b/tempest/services/network/json/network_client.py
index b3663fc..ce200d2 100644
--- a/tempest/services/network/json/network_client.py
+++ b/tempest/services/network/json/network_client.py
@@ -10,9 +10,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
 import time
 
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 from tempest_lib.common.utils import misc
 from tempest_lib import exceptions as lib_exc
@@ -39,46 +39,12 @@
     version = '2.0'
     uri_prefix = "v2.0"
 
-    def get_uri(self, plural_name):
-        # get service prefix from resource name
-
-        # the following map is used to construct proper URI
-        # for the given neutron resource
-        service_resource_prefix_map = {
-            'networks': '',
-            'subnets': '',
-            'ports': '',
-            'metering_labels': 'metering',
-            'metering_label_rules': 'metering',
-        }
-        service_prefix = service_resource_prefix_map.get(
-            plural_name)
-        plural_name = plural_name.replace("_", "-")
-        if service_prefix:
-            uri = '%s/%s/%s' % (self.uri_prefix, service_prefix,
-                                plural_name)
-        else:
-            uri = '%s/%s' % (self.uri_prefix, plural_name)
-        return uri
-
-    def pluralize(self, resource_name):
-        # get plural from map or just add 's'
-
-        # map from resource name to a plural name
-        # needed only for those which can't be constructed as name + 's'
-        resource_plural_map = {
-            'security_groups': 'security_groups',
-            'security_group_rules': 'security_group_rules',
-            'quotas': 'quotas',
-        }
-        return resource_plural_map.get(resource_name, resource_name + 's')
-
     def _list_resources(self, uri, **filters):
         req_uri = self.uri_prefix + uri
         if filters:
             req_uri += '?' + urllib.urlencode(filters, doseq=1)
         resp, body = self.get(req_uri)
-        body = self.deserialize_list(body)
+        body = json.loads(body)
         self.expected_success(200, resp.status)
         return service_client.ResponseBody(resp, body)
 
@@ -96,23 +62,23 @@
         if fields:
             req_uri += '?' + urllib.urlencode(fields, doseq=1)
         resp, body = self.get(req_uri)
-        body = self.deserialize_single(body)
+        body = json.loads(body)
         self.expected_success(200, resp.status)
         return service_client.ResponseBody(resp, body)
 
     def _create_resource(self, uri, post_data):
         req_uri = self.uri_prefix + uri
-        req_post_data = self.serialize(post_data)
+        req_post_data = json.dumps(post_data)
         resp, body = self.post(req_uri, req_post_data)
-        body = self.deserialize_single(body)
+        body = json.loads(body)
         self.expected_success(201, resp.status)
         return service_client.ResponseBody(resp, body)
 
     def _update_resource(self, uri, post_data):
         req_uri = self.uri_prefix + uri
-        req_post_data = self.serialize(post_data)
+        req_post_data = json.dumps(post_data)
         resp, body = self.put(req_uri, req_post_data)
-        body = self.deserialize_single(body)
+        body = json.loads(body)
         self.expected_success(200, resp.status)
         return service_client.ResponseBody(resp, body)
 
@@ -285,34 +251,21 @@
         uri = '/extensions'
         return self._list_resources(uri, **filters)
 
-    # Common methods that are hard to automate
     def create_bulk_network(self, names):
         network_list = [{'name': name} for name in names]
         post_data = {'networks': network_list}
-        body = self.serialize_list(post_data, "networks", "network")
-        uri = self.get_uri("networks")
-        resp, body = self.post(uri, body)
-        body = self.deserialize_list(body)
-        self.expected_success(201, resp.status)
-        return service_client.ResponseBody(resp, body)
+        uri = '/networks'
+        return self._create_resource(uri, post_data)
 
     def create_bulk_subnet(self, subnet_list):
         post_data = {'subnets': subnet_list}
-        body = self.serialize_list(post_data, 'subnets', 'subnet')
-        uri = self.get_uri('subnets')
-        resp, body = self.post(uri, body)
-        body = self.deserialize_list(body)
-        self.expected_success(201, resp.status)
-        return service_client.ResponseBody(resp, body)
+        uri = '/subnets'
+        return self._create_resource(uri, post_data)
 
     def create_bulk_port(self, port_list):
         post_data = {'ports': port_list}
-        body = self.serialize_list(post_data, 'ports', 'port')
-        uri = self.get_uri('ports')
-        resp, body = self.post(uri, body)
-        body = self.deserialize_list(body)
-        self.expected_success(201, resp.status)
-        return service_client.ResponseBody(resp, body)
+        uri = '/ports'
+        return self._create_resource(uri, post_data)
 
     def wait_for_resource_deletion(self, resource_type, id):
         """Waits for a resource to be deleted."""
@@ -371,32 +324,14 @@
             message = '(%s) %s' % (caller, message)
         raise exceptions.TimeoutException(message)
 
-    def deserialize_single(self, body):
-        return json.loads(body)
-
-    def deserialize_list(self, body):
-        return json.loads(body)
-
-    def serialize(self, data):
-        return json.dumps(data)
-
-    def serialize_list(self, data, root=None, item=None):
-        return self.serialize(data)
-
     def update_quotas(self, tenant_id, **kwargs):
         put_body = {'quota': kwargs}
-        body = json.dumps(put_body)
-        uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
-        resp, body = self.put(uri, body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body['quota'])
+        uri = '/quotas/%s' % tenant_id
+        return self._update_resource(uri, put_body)
 
     def reset_quotas(self, tenant_id):
-        uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
-        resp, body = self.delete(uri)
-        self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        uri = '/quotas/%s' % tenant_id
+        return self._delete_resource(uri)
 
     def show_quotas(self, tenant_id, **fields):
         uri = '/quotas/%s' % tenant_id
@@ -410,18 +345,12 @@
         post_body = {'router': kwargs}
         post_body['router']['name'] = name
         post_body['router']['admin_state_up'] = admin_state_up
-        body = json.dumps(post_body)
-        uri = '%s/routers' % (self.uri_prefix)
-        resp, body = self.post(uri, body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        uri = '/routers'
+        return self._create_resource(uri, post_body)
 
     def _update_router(self, router_id, set_enable_snat, **kwargs):
-        uri = '%s/routers/%s' % (self.uri_prefix, router_id)
-        resp, body = self.get(uri)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
+        uri = '/routers/%s' % router_id
+        body = self._show_resource(uri)
         update_body = {}
         update_body['name'] = kwargs.get('name', body['router']['name'])
         update_body['admin_state_up'] = kwargs.get(
@@ -440,11 +369,7 @@
         if 'distributed' in kwargs:
             update_body['distributed'] = kwargs['distributed']
         update_body = dict(router=update_body)
-        update_body = json.dumps(update_body)
-        resp, body = self.put(uri, update_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return self._update_resource(uri, update_body)
 
     def update_router(self, router_id, **kwargs):
         """Update a router leaving enable_snat to its default value."""
@@ -476,64 +401,37 @@
         return self._update_router(router_id, set_enable_snat=True, **kwargs)
 
     def add_router_interface_with_subnet_id(self, router_id, subnet_id):
-        uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
-                                                      router_id)
+        uri = '/routers/%s/add_router_interface' % router_id
         update_body = {"subnet_id": subnet_id}
-        update_body = json.dumps(update_body)
-        resp, body = self.put(uri, update_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return self._update_resource(uri, update_body)
 
     def add_router_interface_with_port_id(self, router_id, port_id):
-        uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
-                                                      router_id)
+        uri = '/routers/%s/add_router_interface' % router_id
         update_body = {"port_id": port_id}
-        update_body = json.dumps(update_body)
-        resp, body = self.put(uri, update_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return self._update_resource(uri, update_body)
 
     def remove_router_interface_with_subnet_id(self, router_id, subnet_id):
-        uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
-                                                         router_id)
+        uri = '/routers/%s/remove_router_interface' % router_id
         update_body = {"subnet_id": subnet_id}
-        update_body = json.dumps(update_body)
-        resp, body = self.put(uri, update_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return self._update_resource(uri, update_body)
 
     def remove_router_interface_with_port_id(self, router_id, port_id):
-        uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
-                                                         router_id)
+        uri = '/routers/%s/remove_router_interface' % router_id
         update_body = {"port_id": port_id}
-        update_body = json.dumps(update_body)
-        resp, body = self.put(uri, update_body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return self._update_resource(uri, update_body)
 
     def list_router_interfaces(self, uuid):
-        uri = '%s/ports?device_id=%s' % (self.uri_prefix, uuid)
-        resp, body = self.get(uri)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        uri = '/ports?device_id=%s' % uuid
+        return self._list_resources(uri)
 
     def update_agent(self, agent_id, agent_info):
         """
         :param agent_info: Agent update information.
         E.g {"admin_state_up": True}
         """
-        uri = '%s/agents/%s' % (self.uri_prefix, agent_id)
+        uri = '/agents/%s' % agent_id
         agent = {"agent": agent_info}
-        body = json.dumps(agent)
-        resp, body = self.put(uri, body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return self._update_resource(uri, agent)
 
     def show_agent(self, agent_id, **fields):
         uri = '/agents/%s' % agent_id
@@ -544,89 +442,54 @@
         return self._list_resources(uri, **filters)
 
     def list_routers_on_l3_agent(self, agent_id):
-        uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
-        resp, body = self.get(uri)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        uri = '/agents/%s/l3-routers' % agent_id
+        return self._list_resources(uri)
 
     def list_l3_agents_hosting_router(self, router_id):
-        uri = '%s/routers/%s/l3-agents' % (self.uri_prefix, router_id)
-        resp, body = self.get(uri)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        uri = '/routers/%s/l3-agents' % router_id
+        return self._list_resources(uri)
 
     def add_router_to_l3_agent(self, agent_id, router_id):
-        uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
+        uri = '/agents/%s/l3-routers' % agent_id
         post_body = {"router_id": router_id}
-        body = json.dumps(post_body)
-        resp, body = self.post(uri, body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return self._create_resource(uri, post_body)
 
     def remove_router_from_l3_agent(self, agent_id, router_id):
-        uri = '%s/agents/%s/l3-routers/%s' % (
-            self.uri_prefix, agent_id, router_id)
-        resp, body = self.delete(uri)
-        self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        uri = '/agents/%s/l3-routers/%s' % (agent_id, router_id)
+        return self._delete_resource(uri)
 
     def list_dhcp_agent_hosting_network(self, network_id):
-        uri = '%s/networks/%s/dhcp-agents' % (self.uri_prefix, network_id)
-        resp, body = self.get(uri)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        uri = '/networks/%s/dhcp-agents' % network_id
+        return self._list_resources(uri)
 
     def list_networks_hosted_by_one_dhcp_agent(self, agent_id):
-        uri = '%s/agents/%s/dhcp-networks' % (self.uri_prefix, agent_id)
-        resp, body = self.get(uri)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        uri = '/agents/%s/dhcp-networks' % agent_id
+        return self._list_resources(uri)
 
     def remove_network_from_dhcp_agent(self, agent_id, network_id):
-        uri = '%s/agents/%s/dhcp-networks/%s' % (self.uri_prefix, agent_id,
-                                                 network_id)
-        resp, body = self.delete(uri)
-        self.expected_success(204, resp.status)
-        return service_client.ResponseBody(resp, body)
+        uri = '/agents/%s/dhcp-networks/%s' % (agent_id,
+                                               network_id)
+        return self._delete_resource(uri)
 
-    def update_extra_routes(self, router_id, nexthop, destination):
-        uri = '%s/routers/%s' % (self.uri_prefix, router_id)
+    def update_extra_routes(self, router_id, routes):
+        uri = '/routers/%s' % router_id
         put_body = {
             'router': {
-                'routes': [{'nexthop': nexthop,
-                            "destination": destination}]
+                'routes': routes
             }
         }
-        body = json.dumps(put_body)
-        resp, body = self.put(uri, body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return self._update_resource(uri, put_body)
 
     def delete_extra_routes(self, router_id):
-        uri = '%s/routers/%s' % (self.uri_prefix, router_id)
-        null_routes = None
+        uri = '/routers/%s' % router_id
         put_body = {
             'router': {
-                'routes': null_routes
+                'routes': None
             }
         }
-        body = json.dumps(put_body)
-        resp, body = self.put(uri, body)
-        self.expected_success(200, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        return self._update_resource(uri, put_body)
 
     def add_dhcp_agent_to_network(self, agent_id, network_id):
         post_body = {'network_id': network_id}
-        body = json.dumps(post_body)
-        uri = '%s/agents/%s/dhcp-networks' % (self.uri_prefix, agent_id)
-        resp, body = self.post(uri, body)
-        self.expected_success(201, resp.status)
-        body = json.loads(body)
-        return service_client.ResponseBody(resp, body)
+        uri = '/agents/%s/dhcp-networks' % agent_id
+        return self._create_resource(uri, post_body)
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index dece763..d89aa5d 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -13,9 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
 from xml.etree import ElementTree as etree
 
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
 from tempest.common import service_client
diff --git a/tempest/services/object_storage/container_client.py b/tempest/services/object_storage/container_client.py
index 8e225b0..b31fe1b 100644
--- a/tempest/services/object_storage/container_client.py
+++ b/tempest/services/object_storage/container_client.py
@@ -13,9 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
 from xml.etree import ElementTree as etree
 
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
 from tempest.common import service_client
diff --git a/tempest/services/orchestration/json/orchestration_client.py b/tempest/services/orchestration/json/orchestration_client.py
index 4d443d3..84a9ed9 100644
--- a/tempest/services/orchestration/json/orchestration_client.py
+++ b/tempest/services/orchestration/json/orchestration_client.py
@@ -13,10 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
 import re
 import time
 
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 from tempest_lib import exceptions as lib_exc
 
diff --git a/tempest/services/volume/json/admin/volume_hosts_client.py b/tempest/services/volume/json/admin/volume_hosts_client.py
index 260679e..6801453 100644
--- a/tempest/services/volume/json/admin/volume_hosts_client.py
+++ b/tempest/services/volume/json/admin/volume_hosts_client.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
 from tempest.common import service_client
diff --git a/tempest/services/volume/json/admin/volume_services_client.py b/tempest/services/volume/json/admin/volume_services_client.py
index 2b2a696..c8607c1 100644
--- a/tempest/services/volume/json/admin/volume_services_client.py
+++ b/tempest/services/volume/json/admin/volume_services_client.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
 from tempest.common import service_client
diff --git a/tempest/services/volume/json/admin/volume_types_client.py b/tempest/services/volume/json/admin/volume_types_client.py
index 9a75f6c..84c7bc5 100644
--- a/tempest/services/volume/json/admin/volume_types_client.py
+++ b/tempest/services/volume/json/admin/volume_types_client.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 from tempest_lib import exceptions as lib_exc
 
diff --git a/tempest/services/volume/json/availability_zone_client.py b/tempest/services/volume/json/availability_zone_client.py
index e60d5c1..13d5d55 100644
--- a/tempest/services/volume/json/availability_zone_client.py
+++ b/tempest/services/volume/json/availability_zone_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.common import service_client
 
diff --git a/tempest/services/volume/json/backups_client.py b/tempest/services/volume/json/backups_client.py
index bf9af6e..8d34230 100644
--- a/tempest/services/volume/json/backups_client.py
+++ b/tempest/services/volume/json/backups_client.py
@@ -13,9 +13,12 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
 import time
 
+from oslo_serialization import jsonutils as json
+
+from tempest_lib import exceptions as lib_exc
+
 from tempest.common import service_client
 from tempest import exceptions
 
@@ -74,6 +77,24 @@
         self.expected_success(200, resp.status)
         return service_client.ResponseBodyList(resp, body['backups'])
 
+    def export_backup(self, backup_id):
+        """Export backup metadata record."""
+        url = "backups/%s/export_record" % backup_id
+        resp, body = self.get(url)
+        body = json.loads(body)
+        self.expected_success(200, resp.status)
+        return service_client.ResponseBody(resp, body['backup-record'])
+
+    def import_backup(self, backup_service, backup_url):
+        """Import backup metadata record."""
+        post_body = {'backup_service': backup_service,
+                     'backup_url': backup_url}
+        post_body = json.dumps({'backup-record': post_body})
+        resp, body = self.post("backups/import_record", post_body)
+        body = json.loads(body)
+        self.expected_success(201, resp.status)
+        return service_client.ResponseBody(resp, body['backup'])
+
     def wait_for_backup_status(self, backup_id, status):
         """Waits for a Backup to reach a given status."""
         body = self.show_backup(backup_id)
@@ -94,6 +115,18 @@
                             self.build_timeout))
                 raise exceptions.TimeoutException(message)
 
+    def wait_for_backup_deletion(self, backup_id):
+        """Waits for backup deletion"""
+        start_time = int(time.time())
+        while True:
+            try:
+                self.show_backup(backup_id)
+            except lib_exc.NotFound:
+                return
+            if int(time.time()) - start_time >= self.build_timeout:
+                raise exceptions.TimeoutException
+            time.sleep(self.build_interval)
+
 
 class BackupsClient(BaseBackupsClient):
     """Volume V1 Backups client"""
diff --git a/tempest/services/volume/json/extensions_client.py b/tempest/services/volume/json/extensions_client.py
index 3bf468a..1098e1e 100644
--- a/tempest/services/volume/json/extensions_client.py
+++ b/tempest/services/volume/json/extensions_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
+from oslo_serialization import jsonutils as json
 
 from tempest.common import service_client
 
diff --git a/tempest/services/volume/json/qos_client.py b/tempest/services/volume/json/qos_client.py
index 37ab9cd..e3d6a29 100644
--- a/tempest/services/volume/json/qos_client.py
+++ b/tempest/services/volume/json/qos_client.py
@@ -12,9 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
 import time
 
+from oslo_serialization import jsonutils as json
 from tempest_lib import exceptions as lib_exc
 
 from tempest.common import service_client
diff --git a/tempest/services/volume/json/snapshots_client.py b/tempest/services/volume/json/snapshots_client.py
index 7f7cceb..fa1f9dd 100644
--- a/tempest/services/volume/json/snapshots_client.py
+++ b/tempest/services/volume/json/snapshots_client.py
@@ -10,10 +10,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
 import time
 
 from oslo_log import log as logging
+from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 from tempest_lib import exceptions as lib_exc
 
diff --git a/tempest/services/volume/json/volumes_client.py b/tempest/services/volume/json/volumes_client.py
index b8e3464..26f186e 100644
--- a/tempest/services/volume/json/volumes_client.py
+++ b/tempest/services/volume/json/volumes_client.py
@@ -13,8 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
+from oslo_serialization import jsonutils as json
 import six
 from six.moves.urllib import parse as urllib
 from tempest_lib import exceptions as lib_exc
diff --git a/tempest/stress/actions/server_create_destroy.py b/tempest/stress/actions/server_create_destroy.py
index d0e4eea..9f41526 100644
--- a/tempest/stress/actions/server_create_destroy.py
+++ b/tempest/stress/actions/server_create_destroy.py
@@ -13,6 +13,7 @@
 #    limitations under the License.
 
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 import tempest.stress.stressaction as stressaction
 
@@ -31,8 +32,8 @@
         server = self.manager.servers_client.create_server(
             name, self.image, self.flavor)
         server_id = server['id']
-        self.manager.servers_client.wait_for_server_status(server_id,
-                                                           'ACTIVE')
+        waiters.wait_for_server_status(self.manager.servers_client, server_id,
+                                       'ACTIVE')
         self.logger.info("created %s" % server_id)
         self.logger.info("deleting %s" % name)
         self.manager.servers_client.delete_server(server_id)
diff --git a/tempest/stress/actions/ssh_floating.py b/tempest/stress/actions/ssh_floating.py
index 9e540fa..4a27466 100644
--- a/tempest/stress/actions/ssh_floating.py
+++ b/tempest/stress/actions/ssh_floating.py
@@ -14,6 +14,7 @@
 import subprocess
 
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 import tempest.stress.stressaction as stressaction
 import tempest.test
@@ -79,8 +80,8 @@
                                               **vm_args)
         self.server_id = server['id']
         if self.wait_after_vm_create:
-            self.manager.servers_client.wait_for_server_status(self.server_id,
-                                                               'ACTIVE')
+            waiters.wait_for_server_status(self.manager.servers_client,
+                                           self.server_id, 'ACTIVE')
 
     def _destroy_vm(self):
         self.logger.info("deleting %s" % self.server_id)
@@ -172,8 +173,8 @@
             self._create_vm()
         if self.reboot:
             self.manager.servers_client.reboot(self.server_id, 'HARD')
-            self.manager.servers_client.wait_for_server_status(self.server_id,
-                                                               'ACTIVE')
+            waiters.wait_for_server_status(self.manager.servers_client,
+                                           self.server_id, 'ACTIVE')
 
         self.run_core()
 
diff --git a/tempest/stress/actions/volume_attach_delete.py b/tempest/stress/actions/volume_attach_delete.py
index a5e393f..d6965c7 100644
--- a/tempest/stress/actions/volume_attach_delete.py
+++ b/tempest/stress/actions/volume_attach_delete.py
@@ -12,6 +12,7 @@
 #    limitations under the License.
 
 from tempest.common.utils import data_utils
+from tempest.common import waiters
 from tempest import config
 import tempest.stress.stressaction as stressaction
 
@@ -40,7 +41,8 @@
         server = self.manager.servers_client.create_server(
             vm_name, self.image, self.flavor)
         server_id = server['id']
-        self.manager.servers_client.wait_for_server_status(server_id, 'ACTIVE')
+        waiters.wait_for_server_status(self.manager.servers_client, server_id,
+                                       'ACTIVE')
         self.logger.info("created vm %s" % server_id)
 
         # Step 3: attach volume to vm
diff --git a/tempest/stress/actions/volume_attach_verify.py b/tempest/stress/actions/volume_attach_verify.py
index 1cd96ed..3ba2a91 100644
--- a/tempest/stress/actions/volume_attach_verify.py
+++ b/tempest/stress/actions/volume_attach_verify.py
@@ -14,6 +14,7 @@
 
 from tempest.common.utils import data_utils
 from tempest.common.utils.linux import remote_client
+from tempest.common import waiters
 from tempest import config
 import tempest.stress.stressaction as stressaction
 import tempest.test
@@ -41,8 +42,8 @@
                                               self.flavor,
                                               **vm_args)
         self.server_id = server['id']
-        self.manager.servers_client.wait_for_server_status(self.server_id,
-                                                           'ACTIVE')
+        waiters.wait_for_server_status(self.manager.servers_client,
+                                       self.server_id, 'ACTIVE')
 
     def _destroy_vm(self):
         self.logger.info("deleting server: %s" % self.server_id)
diff --git a/tempest/stress/cleanup.py b/tempest/stress/cleanup.py
index d9b430e..b785156 100644
--- a/tempest/stress/cleanup.py
+++ b/tempest/stress/cleanup.py
@@ -47,7 +47,7 @@
             pass
 
     secgrp_client = admin_manager.security_groups_client
-    secgrp = secgrp_client.list_security_groups({"all_tenants": True})
+    secgrp = secgrp_client.list_security_groups(all_tenants=True)
     secgrp_del = [grp for grp in secgrp if grp['name'] != 'default']
     LOG.info("Cleanup::remove %s Security Group" % len(secgrp_del))
     for g in secgrp_del:
diff --git a/tempest/test.py b/tempest/test.py
index 80e61c7..df6b30d 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -15,7 +15,6 @@
 
 import atexit
 import functools
-import json
 import os
 import re
 import sys
@@ -25,6 +24,7 @@
 
 import fixtures
 from oslo_log import log as logging
+from oslo_serialization import jsonutils as json
 from oslo_utils import importutils
 import six
 import testscenarios
@@ -44,7 +44,7 @@
 
 
 def attr(**kwargs):
-    """A decorator which applies the  testtools attr decorator
+    """A decorator which applies the testtools attr decorator
 
     This decorator applies the testtools.testcase.attr if it is in the list of
     attributes to testtools we want to apply.
diff --git a/tempest/test_discover/plugins.py b/tempest/test_discover/plugins.py
index 197bd0c..2701f02 100644
--- a/tempest/test_discover/plugins.py
+++ b/tempest/test_discover/plugins.py
@@ -13,12 +13,16 @@
 # under the License.
 
 import abc
+import logging
 
 import six
 import stevedore
 from tempest_lib.common.utils import misc
 
 
+LOG = logging.getLogger(__name__)
+
+
 @six.add_metaclass(abc.ABCMeta)
 class TempestPlugin(object):
     """A TempestPlugin class provides the basic hooks for an external
@@ -46,8 +50,14 @@
     """
     def __init__(self):
         self.ext_plugins = stevedore.ExtensionManager(
-            'tempest.test.plugins', invoke_on_load=True,
-            propagate_map_exceptions=True)
+            'tempest.test_plugins', invoke_on_load=True,
+            propagate_map_exceptions=True,
+            on_load_failure_callback=self.failure_hook)
+
+    @staticmethod
+    def failure_hook(_, ep, err):
+        LOG.error('Could not load %r: %s', ep.name, err)
+        raise err
 
     def get_plugin_load_tests_tuple(self):
         load_tests_dict = {}
diff --git a/tempest/test_discover/test_discover.py b/tempest/test_discover/test_discover.py
index 86aa855..dac7d91 100644
--- a/tempest/test_discover/test_discover.py
+++ b/tempest/test_discover/test_discover.py
@@ -48,8 +48,8 @@
     for plugin in plugin_load_tests:
         test_dir, top_path = plugin_load_tests[plugin]
         if not pattern:
-            suite.addTests(loader.discover(test_dir, top_level=top_path))
+            suite.addTests(loader.discover(test_dir, top_level_dir=top_path))
         else:
             suite.addTests(loader.discover(test_dir, pattern=pattern,
-                                           top_level=top_path))
+                                           top_level_dir=top_path))
     return suite
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 7286d76..6bc96f2 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -12,9 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
 
 import mock
+from oslo_serialization import jsonutils as json
 from oslotest import mockpatch
 
 from tempest.cmd import verify_tempest_config
diff --git a/tempest/tests/fake_identity.py b/tempest/tests/fake_identity.py
index ad78f85..d0de927 100644
--- a/tempest/tests/fake_identity.py
+++ b/tempest/tests/fake_identity.py
@@ -13,9 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
-
 import httplib2
+from oslo_serialization import jsonutils as json
 
 FAKE_AUTH_URL = 'http://fake_uri.com/auth'
 
diff --git a/tempest/tests/services/__init__.py b/tempest/tests/services/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/services/__init__.py
diff --git a/tempest/tests/services/compute/__init__.py b/tempest/tests/services/compute/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/services/compute/__init__.py
diff --git a/tempest/tests/services/compute/test_agents_client.py b/tempest/tests/services/compute/test_agents_client.py
new file mode 100644
index 0000000..d268a18
--- /dev/null
+++ b/tempest/tests/services/compute/test_agents_client.py
@@ -0,0 +1,49 @@
+# Copyright 2015 NEC Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import httplib2
+
+from oslotest import mockpatch
+
+from tempest.services.compute.json import agents_client
+from tempest.tests import base
+from tempest.tests import fake_auth_provider
+from tempest.tests import fake_config
+
+
+class TestAgentsClient(base.TestCase):
+
+    def setUp(self):
+        super(TestAgentsClient, self).setUp()
+        self.useFixture(fake_config.ConfigFixture())
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = agents_client.AgentsClient(fake_auth,
+                                                 'compute', 'regionOne')
+
+    def _test_list_agents(self, bytes_body=False):
+        body = '{"agents": []}'
+        if bytes_body:
+            body = bytes(body.encode('utf-8'))
+        expected = []
+        response = (httplib2.Response({'status': 200}), body)
+        self.useFixture(mockpatch.Patch(
+            'tempest.common.service_client.ServiceClient.get',
+            return_value=response))
+        self.assertEqual(expected, self.client.list_agents())
+
+    def test_list_agents_with_str_body(self):
+        self._test_list_agents()
+
+    def test_list_agents_with_bytes_body(self):
+        self._test_list_agents(bytes_body=True)
diff --git a/tempest/tests/test_glance_http.py b/tempest/tests/test_glance_http.py
index 71aa395..105caec 100644
--- a/tempest/tests/test_glance_http.py
+++ b/tempest/tests/test_glance_http.py
@@ -13,10 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
 import socket
 
 import mock
+from oslo_serialization import jsonutils as json
 from oslotest import mockpatch
 import six
 from six.moves import http_client as httplib
diff --git a/test-requirements.txt b/test-requirements.txt
index 7115168..8fcf071 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -10,4 +10,4 @@
 mock>=1.1;python_version!='2.6'
 mock==1.0.1;python_version=='2.6'
 coverage>=3.6
-oslotest>=1.5.1 # Apache-2.0
+oslotest>=1.7.0 # Apache-2.0