Merge "Test nonexistent volume type extra_spec name instead of id"
diff --git a/bindep.txt b/bindep.txt
index 8914ade..efd3a10 100644
--- a/bindep.txt
+++ b/bindep.txt
@@ -1,5 +1,5 @@
 # This file contains runtime (non-python) dependencies
-# More info at: http://docs.openstack.org/infra/bindep/readme.html
+# More info at: https://docs.openstack.org/infra/bindep/readme.html
 
 libffi-dev [platform:dpkg]
 libffi-devel [platform:rpm]
diff --git a/doc/source/microversion_testing.rst b/doc/source/microversion_testing.rst
index d80081d..573b7d4 100644
--- a/doc/source/microversion_testing.rst
+++ b/doc/source/microversion_testing.rst
@@ -300,35 +300,35 @@
 
  * `2.2`_
 
- .. _2.2: http://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id2
+ .. _2.2: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id2
 
  * `2.10`_
 
- .. _2.10: http://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id9
+ .. _2.10: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id9
 
  * `2.20`_
 
- .. _2.20: http://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id18
+ .. _2.20: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id18
 
  * `2.25`_
 
- .. _2.25: http://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-mitaka
+ .. _2.25: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-mitaka
 
  * `2.32`_
 
- .. _2.32: http://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id29
+ .. _2.32: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id29
 
  * `2.37`_
 
- .. _2.37: http://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id34
+ .. _2.37: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id34
 
  * `2.42`_
 
- .. _2.42: http://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-ocata
+ .. _2.42: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#maximum-in-ocata
 
  * `2.47`_
 
- .. _2.47: http://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id42
+ .. _2.47: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#id42
 
  * `2.48`_
 
diff --git a/releasenotes/notes/add-is-resource-deleted-sg-client-f4a7a7a54ff024d7.yaml b/releasenotes/notes/add-is-resource-deleted-sg-client-f4a7a7a54ff024d7.yaml
new file mode 100644
index 0000000..e046326
--- /dev/null
+++ b/releasenotes/notes/add-is-resource-deleted-sg-client-f4a7a7a54ff024d7.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - |
+    Implement the `rest_client` method `is_resource_deleted` in the network
+    security group client.
diff --git a/releasenotes/notes/raise-exception-when-error-deleting-on-volume-18d0d0c5886212dd.yaml b/releasenotes/notes/raise-exception-when-error-deleting-on-volume-18d0d0c5886212dd.yaml
new file mode 100644
index 0000000..194dbc1
--- /dev/null
+++ b/releasenotes/notes/raise-exception-when-error-deleting-on-volume-18d0d0c5886212dd.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+  - |
+    Tempest checks a volume delete by waiting for NotFound(404) on
+    show_volume(). Sometime a volume delete fails and the volume status
+    becomes error_deleting which means the delete is failed.
+    So Tempest doesn't need to wait anymore. A new release of Tempest
+    raises an exception DeleteErrorException instead of waiting.
diff --git a/requirements.txt b/requirements.txt
index a74f5c2..aaecaaa 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,7 +5,7 @@
 cliff>=2.8.0 # Apache-2.0
 jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
 testtools>=1.4.0 # MIT
-paramiko>=2.0 # LGPLv2.1+
+paramiko>=2.0.0 # LGPLv2.1+
 netaddr!=0.7.16,>=0.7.13 # BSD
 testrepository>=0.0.18 # Apache-2.0/BSD
 oslo.concurrency>=3.8.0 # Apache-2.0
@@ -15,7 +15,7 @@
 oslo.utils>=3.20.0 # Apache-2.0
 six>=1.9.0 # MIT
 fixtures>=3.0.0 # Apache-2.0/BSD
-PyYAML>=3.10.0 # MIT
+PyYAML>=3.10 # MIT
 python-subunit>=0.0.18 # Apache-2.0/BSD
 stevedore>=1.20.0 # Apache-2.0
 PrettyTable<0.8,>=0.7.1 # BSD
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 1aa9227..5894e80 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -157,8 +157,7 @@
 
     def _restore_default_quotas(self, original_defaults):
         LOG.debug("restoring quota class defaults")
-        self.adm_client.update_quota_class_set(
-            'default', **original_defaults)['quota_class_set']
+        self.adm_client.update_quota_class_set('default', **original_defaults)
 
     # NOTE(sdague): this test is problematic as it changes
     # global state, and possibly needs to be part of a set of
diff --git a/tempest/api/compute/security_groups/test_security_group_rules.py b/tempest/api/compute/security_groups/test_security_group_rules.py
index 124db0e..2b5ae48 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules.py
@@ -178,7 +178,7 @@
             ip_protocol=self.ip_protocol,
             from_port=self.from_port,
             to_port=self.to_port,
-            group_id=sg2_id)['security_group_rule']
+            group_id=sg2_id)
 
         # Delete group2
         self.security_groups_client.delete_security_group(sg2_id)
diff --git a/tempest/api/compute/servers/test_create_server.py b/tempest/api/compute/servers/test_create_server.py
index b727ddd..1f4071f 100644
--- a/tempest/api/compute/servers/test_create_server.py
+++ b/tempest/api/compute/servers/test_create_server.py
@@ -17,7 +17,6 @@
 import testtools
 
 from tempest.api.compute import base
-from tempest.common import compute
 from tempest.common.utils.linux import remote_client
 from tempest import config
 from tempest.lib.common.utils import data_utils
@@ -133,22 +132,6 @@
                'hostname "%s" but got "%s".' % (self.name, hostname))
         self.assertEqual(self.name.lower(), hostname, msg)
 
-    @decorators.idempotent_id('ed20d3fb-9d1f-4329-b160-543fbd5d9811')
-    @testtools.skipUnless(
-        compute.is_scheduler_filter_enabled("ServerGroupAffinityFilter"),
-        'ServerGroupAffinityFilter is not available.')
-    def test_create_server_with_scheduler_hint_group(self):
-        # Create a server with the scheduler hint "group".
-        group_id = self.create_test_server_group()['id']
-        hints = {'group': group_id}
-        server = self.create_test_server(scheduler_hints=hints,
-                                         wait_until='ACTIVE')
-
-        # Check a server is in the group
-        server_group = (self.server_groups_client.show_server_group(group_id)
-                        ['server_group'])
-        self.assertIn(server['id'], server_group['members'])
-
 
 class ServersTestManualDisk(ServersTestJSON):
     disk_config = 'MANUAL'
diff --git a/tempest/api/compute/servers/test_server_group.py b/tempest/api/compute/servers/test_server_group.py
index 69d7897..f402540 100644
--- a/tempest/api/compute/servers/test_server_group.py
+++ b/tempest/api/compute/servers/test_server_group.py
@@ -13,7 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.api.compute import base
+from tempest.common import compute
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest import test
@@ -106,3 +109,19 @@
         # List the server-group
         body = self.client.list_server_groups()['server_groups']
         self.assertIn(self.created_server_group, body)
+
+    @decorators.idempotent_id('ed20d3fb-9d1f-4329-b160-543fbd5d9811')
+    @testtools.skipUnless(
+        compute.is_scheduler_filter_enabled("ServerGroupAffinityFilter"),
+        'ServerGroupAffinityFilter is not available.')
+    def test_create_server_with_scheduler_hint_group(self):
+        # Create a server with the scheduler hint "group".
+        hints = {'group': self.created_server_group['id']}
+        server = self.create_test_server(scheduler_hints=hints,
+                                         wait_until='ACTIVE')
+        self.addCleanup(self.delete_server, server['id'])
+
+        # Check a server is in the group
+        server_group = (self.server_groups_client.show_server_group(
+            self.created_server_group['id'])['server_group'])
+        self.assertIn(server['id'], server_group['members'])
diff --git a/tempest/api/compute/servers/test_server_metadata.py b/tempest/api/compute/servers/test_server_metadata.py
index f77e7d3..fe95018 100644
--- a/tempest/api/compute/servers/test_server_metadata.py
+++ b/tempest/api/compute/servers/test_server_metadata.py
@@ -32,7 +32,7 @@
     def setUp(self):
         super(ServerMetadataTestJSON, self).setUp()
         meta = {'key1': 'value1', 'key2': 'value2'}
-        self.client.set_server_metadata(self.server['id'], meta)['metadata']
+        self.client.set_server_metadata(self.server['id'], meta)
 
     @decorators.idempotent_id('479da087-92b3-4dcf-aeb3-fd293b2d14ce')
     def test_list_server_metadata(self):
@@ -49,8 +49,7 @@
         # The server's metadata should be replaced with the provided values
         # Create a new set of metadata for the server
         req_metadata = {'meta2': 'data2', 'meta3': 'data3'}
-        self.client.set_server_metadata(self.server['id'],
-                                        req_metadata)['metadata']
+        self.client.set_server_metadata(self.server['id'], req_metadata)
 
         # Verify the expected values are correct, and that the
         # previous values have been removed
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index 2e68efd..c846f88 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -119,7 +119,7 @@
 
         # Update Image
         new_image_name = data_utils.rand_name('new-image')
-        body = self.client.update_image(image['id'], [
+        self.client.update_image(image['id'], [
             dict(replace='/name', value=new_image_name)])
 
         # Verifying updating
diff --git a/tempest/api/network/test_extra_dhcp_options.py b/tempest/api/network/test_extra_dhcp_options.py
index dc9042e..ff66e9a 100644
--- a/tempest/api/network/test_extra_dhcp_options.py
+++ b/tempest/api/network/test_extra_dhcp_options.py
@@ -75,7 +75,7 @@
     def test_update_show_port_with_extra_dhcp_options(self):
         # Update port with extra dhcp options
         name = data_utils.rand_name('new-port-name')
-        body = self.ports_client.update_port(
+        self.ports_client.update_port(
             self.port['id'],
             name=name,
             extra_dhcp_opts=self.extra_dhcp_opts)
diff --git a/tempest/api/object_storage/base.py b/tempest/api/object_storage/base.py
index 11273e4..4c49b2a 100644
--- a/tempest/api/object_storage/base.py
+++ b/tempest/api/object_storage/base.py
@@ -71,9 +71,6 @@
     def setup_credentials(cls):
         cls.set_network_resources()
         super(BaseObjectTest, cls).setup_credentials()
-        # credentials may be overwritten by children classes
-        if hasattr(cls, 'os_roles_operator'):
-            cls.os = cls.os_roles_operator
 
     @classmethod
     def setup_clients(cls):
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index 092d369..f46c256 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -29,7 +29,6 @@
     @classmethod
     def setup_credentials(cls):
         super(AccountQuotasTest, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
         cls.os_reselleradmin = cls.os_roles_reseller
 
     @classmethod
diff --git a/tempest/api/object_storage/test_account_quotas_negative.py b/tempest/api/object_storage/test_account_quotas_negative.py
index 60233b4..431bbe9 100644
--- a/tempest/api/object_storage/test_account_quotas_negative.py
+++ b/tempest/api/object_storage/test_account_quotas_negative.py
@@ -29,7 +29,6 @@
     @classmethod
     def setup_credentials(cls):
         super(AccountQuotasNegativeTest, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
         cls.os_reselleradmin = cls.os_roles_reseller
 
     @classmethod
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 2fb676f..0f86540 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -36,14 +36,13 @@
     @classmethod
     def setup_credentials(cls):
         super(AccountTest, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
         cls.os_operator = cls.os_roles_operator_alt
 
     @classmethod
     def resource_setup(cls):
         super(AccountTest, cls).resource_setup()
         for i in range(ord('a'), ord('f') + 1):
-            name = data_utils.rand_name(name='%s-' % chr(i))
+            name = data_utils.rand_name(name='%s-' % six.int2byte(i))
             cls.container_client.create_container(name)
             cls.containers.append(name)
         cls.containers_count = len(cls.containers)
diff --git a/tempest/api/object_storage/test_account_services_negative.py b/tempest/api/object_storage/test_account_services_negative.py
index e98a4f5..3e664d7 100644
--- a/tempest/api/object_storage/test_account_services_negative.py
+++ b/tempest/api/object_storage/test_account_services_negative.py
@@ -28,7 +28,6 @@
     @classmethod
     def setup_credentials(cls):
         super(AccountNegativeTest, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
         cls.os_operator = cls.os_roles_operator_alt
 
     @decorators.attr(type=['negative'])
diff --git a/tempest/api/object_storage/test_container_acl_negative.py b/tempest/api/object_storage/test_container_acl_negative.py
index 655626c..e064753 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -29,7 +29,6 @@
     @classmethod
     def setup_credentials(cls):
         super(ObjectACLsNegativeTest, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
         cls.os_operator = cls.os_roles_operator_alt
 
     @classmethod
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 4cb1914..7665b48 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -41,7 +41,6 @@
     @classmethod
     def setup_credentials(cls):
         super(ContainerSyncTest, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
         cls.os_alt = cls.os_roles_operator_alt
 
     @classmethod
diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py
index 556ca2f..d3cdb72 100644
--- a/tempest/api/object_storage/test_object_services.py
+++ b/tempest/api/object_storage/test_object_services.py
@@ -967,7 +967,6 @@
     @classmethod
     def setup_credentials(cls):
         super(PublicObjectTest, cls).setup_credentials()
-        cls.os = cls.os_roles_operator
         cls.os_alt = cls.os_roles_operator_alt
 
     @classmethod
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index 91bc677..217dead 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -103,7 +103,7 @@
         self.assertEqual(body, self.content)
 
         # Testing a HEAD on this Temp URL
-        resp, body = self.object_client.head(url)
+        resp, _ = self.object_client.head(url)
         self.assertHeaders(resp, 'Object', 'HEAD')
 
     @decorators.idempotent_id('671f9583-86bd-4128-a034-be282a68c5d8')
@@ -142,11 +142,11 @@
                                  expires, self.key)
 
         # trying to put random data in the object using temp url
-        resp, body = self.object_client.put(url, new_data, None)
+        resp, _ = self.object_client.put(url, new_data, None)
         self.assertHeaders(resp, 'Object', 'PUT')
 
         # Testing a HEAD on this Temp URL
-        resp, body = self.object_client.head(url)
+        resp, _ = self.object_client.head(url)
         self.assertHeaders(resp, 'Object', 'HEAD')
 
         # Validate that the content of the object has been modified
diff --git a/tempest/api/object_storage/test_object_version.py b/tempest/api/object_storage/test_object_version.py
index dc0d179..4799053 100644
--- a/tempest/api/object_storage/test_object_version.py
+++ b/tempest/api/object_storage/test_object_version.py
@@ -51,7 +51,7 @@
     def test_versioned_container(self):
         # create container
         vers_container_name = data_utils.rand_name(name='TestVersionContainer')
-        resp, body = self.container_client.create_container(
+        resp, _ = self.container_client.create_container(
             vers_container_name)
         self.containers.append(vers_container_name)
         self.assertHeaders(resp, 'Container', 'PUT')
@@ -59,7 +59,7 @@
 
         base_container_name = data_utils.rand_name(name='TestBaseContainer')
         headers = {'X-versions-Location': vers_container_name}
-        resp, body = self.container_client.create_container(
+        resp, _ = self.container_client.create_container(
             base_container_name,
             metadata=headers,
             metadata_prefix='')
@@ -76,20 +76,20 @@
         data_2 = data_utils.random_bytes()
         resp, _ = self.object_client.create_object(base_container_name,
                                                    object_name, data_2)
-        resp, body = self.object_client.get_object(base_container_name,
-                                                   object_name)
+        _, body = self.object_client.get_object(base_container_name,
+                                                object_name)
         self.assertEqual(body, data_2)
         # delete object version 2
         resp, _ = self.object_client.delete_object(base_container_name,
                                                    object_name)
         self.assertContainer(base_container_name, '1', '1024',
                              vers_container_name)
-        resp, body = self.object_client.get_object(base_container_name,
-                                                   object_name)
+        _, body = self.object_client.get_object(base_container_name,
+                                                object_name)
         self.assertEqual(body, data_1)
         # delete object version 1
-        resp, _ = self.object_client.delete_object(base_container_name,
-                                                   object_name)
+        self.object_client.delete_object(base_container_name,
+                                         object_name)
         # containers should be empty
         self.assertContainer(base_container_name, '0', '0',
                              vers_container_name)
diff --git a/tempest/api/volume/admin/test_volume_retype_with_migration.py b/tempest/api/volume/admin/test_volume_retype_with_migration.py
index 94d5299..f0b3a4f 100644
--- a/tempest/api/volume/admin/test_volume_retype_with_migration.py
+++ b/tempest/api/volume/admin/test_volume_retype_with_migration.py
@@ -85,9 +85,7 @@
         volume_source = self.admin_volume_client.show_volume(
             self.src_vol['id'])['volume']
 
-        # TODO(erlon): change this to volumes_client client after Bug
-        # #1657806 is fixed
-        self.admin_volume_client.retype_volume(
+        self.volumes_client.retype_volume(
             self.src_vol['id'],
             new_type=self.dst_vol_type['name'],
             migration_policy='on-demand')
diff --git a/tempest/api/volume/test_versions.py b/tempest/api/volume/test_versions.py
index 0083a3b..b4d48db 100644
--- a/tempest/api/volume/test_versions.py
+++ b/tempest/api/volume/test_versions.py
@@ -26,4 +26,4 @@
         # NOTE: The version data is checked on service client side
         #       with JSON-Schema validation. It is enough to just call
         #       the API here.
-        self.versions_client.list_versions()['versions']
+        self.versions_client.list_versions()
diff --git a/tempest/cmd/cleanup.py b/tempest/cmd/cleanup.py
index ac73cbf..a128b3f 100644
--- a/tempest/cmd/cleanup.py
+++ b/tempest/cmd/cleanup.py
@@ -104,7 +104,8 @@
     def init(self, parsed_args):
         cleanup_service.init_conf()
         self.options = parsed_args
-        self.admin_mgr = credentials.AdminManager()
+        self.admin_mgr = clients.Manager(
+            credentials.get_configured_admin_credentials())
         self.dry_run_data = {}
         self.json_data = {}
 
@@ -263,9 +264,10 @@
 
     def _remove_admin_role(self, tenant_id):
         LOG.debug("Remove admin user role for tenant: %s", tenant_id)
-        # Must initialize AdminManager for each user role
+        # Must initialize Admin Manager for each user role
         # Otherwise authentication exception is thrown, weird
-        id_cl = credentials.AdminManager().identity_client
+        id_cl = clients.Manager(
+            credentials.get_configured_admin_credentials()).identity_client
         if (self._tenant_exists(tenant_id)):
             try:
                 id_cl.delete_role_from_user_on_project(tenant_id,
diff --git a/tempest/cmd/cleanup_service.py b/tempest/cmd/cleanup_service.py
index 11cd4bb..c322213 100644
--- a/tempest/cmd/cleanup_service.py
+++ b/tempest/cmd/cleanup_service.py
@@ -16,6 +16,7 @@
 
 from oslo_log import log as logging
 
+from tempest import clients
 from tempest.common import credentials_factory as credentials
 from tempest.common import identity
 from tempest.common.utils import net_info
@@ -78,7 +79,8 @@
 
 
 def _get_network_id(net_name, project_name):
-    am = credentials.AdminManager()
+    am = clients.Manager(
+        credentials.get_configured_admin_credentials())
     net_cl = am.networks_client
     tn_cl = am.tenants_client
 
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
index 350dd0b..e71032a 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -101,6 +101,7 @@
 import six
 from testrepository.commands import run_argv
 
+from tempest import clients
 from tempest.cmd import cleanup_service
 from tempest.cmd import init
 from tempest.cmd import workspace
@@ -216,7 +217,8 @@
         print("Initializing saved state.")
         data = {}
         self.global_services = cleanup_service.get_global_cleanup_services()
-        self.admin_mgr = credentials.AdminManager()
+        self.admin_mgr = clients.Manager(
+            credentials.get_configured_admin_credentials())
         admin_mgr = self.admin_mgr
         kwargs = {'data': data,
                   'is_dry_run': False,
diff --git a/tempest/common/validation_resources.py b/tempest/common/validation_resources.py
index 84f1c9d..ae9d584 100644
--- a/tempest/common/validation_resources.py
+++ b/tempest/common/validation_resources.py
@@ -19,35 +19,72 @@
 LOG = logging.getLogger(__name__)
 
 
-def _create_neutron_sec_group_rules(os, sec_group, ethertype='IPv4'):
-    sec_group_rules_client = os.security_group_rules_client
-
-    sec_group_rules_client.create_security_group_rule(
-        security_group_id=sec_group['id'],
-        protocol='tcp',
-        ethertype=ethertype,
-        port_range_min=22,
-        port_range_max=22,
-        direction='ingress')
-    sec_group_rules_client.create_security_group_rule(
-        security_group_id=sec_group['id'],
-        protocol='icmp',
-        ethertype=ethertype,
-        direction='ingress')
+def _network_service(clients, use_neutron):
+    # Internal helper to select the right network clients
+    if use_neutron:
+        return clients.network
+    else:
+        return clients.compute
 
 
-def create_ssh_security_group(os, add_rule=False, ethertype='IPv4',
+def create_ssh_security_group(clients, add_rule=False, ethertype='IPv4',
                               use_neutron=True):
-    security_groups_client = os.compute_security_groups_client
-    security_group_rules_client = os.compute_security_group_rules_client
+    """Create a security group for ping/ssh testing
+
+    Create a security group to be attached to a VM using the nova or neutron
+    clients. If rules are added, the group can be attached to a VM to enable
+    connectivity validation over ICMP and further testing over SSH.
+
+    :param clients: Instance of `tempest.lib.services.clients.ServiceClients`
+        or of a subclass of it. Resources are provisioned using clients from
+        `clients`.
+    :param add_rule: Whether security group rules are provisioned or not.
+        Defaults to `False`.
+    :param ethertype: 'IPv4' or 'IPv6'. Honoured only in case neutron is used.
+    :param use_neutron: When True resources are provisioned via neutron, when
+        False resources are provisioned via nova.
+    :returns: A dictionary with the security group as returned by the API.
+
+    Examples::
+
+        from tempest.common import validation_resources as vr
+        from tempest.lib import auth
+        from tempest.lib.services import clients
+
+        creds = auth.get_credentials('http://mycloud/identity/v3',
+                                     username='me', project_name='me',
+                                     password='secret', domain_name='Default')
+        osclients = clients.ServiceClients(creds, 'http://mycloud/identity/v3')
+        # Security group for IPv4 tests
+        sg4 = vr.create_ssh_security_group(osclients, add_rule=True)
+        # Security group for IPv6 tests
+        sg6 = vr.create_ssh_security_group(osclients, ethertype='IPv6',
+                                           add_rule=True)
+    """
+    network_service = _network_service(clients, use_neutron)
+    security_groups_client = network_service.SecurityGroupsClient()
+    security_group_rules_client = network_service.SecurityGroupRulesClient()
+    # Security Group clients for nova and neutron behave the same
     sg_name = data_utils.rand_name('securitygroup-')
     sg_description = data_utils.rand_name('description-')
     security_group = security_groups_client.create_security_group(
         name=sg_name, description=sg_description)['security_group']
+    # Security Group Rules clients require different parameters depending on
+    # the network service in use
     if add_rule:
         if use_neutron:
-            _create_neutron_sec_group_rules(os, security_group,
-                                            ethertype=ethertype)
+            security_group_rules_client.create_security_group_rule(
+                security_group_id=security_group['id'],
+                protocol='tcp',
+                ethertype=ethertype,
+                port_range_min=22,
+                port_range_max=22,
+                direction='ingress')
+            security_group_rules_client.create_security_group_rule(
+                security_group_id=security_group['id'],
+                protocol='icmp',
+                ethertype=ethertype,
+                direction='ingress')
         else:
             security_group_rules_client.create_security_group_rule(
                 parent_group_id=security_group['id'], ip_protocol='tcp',
@@ -60,95 +97,196 @@
     return security_group
 
 
-def create_validation_resources(os, validation_resources=None,
+def create_validation_resources(clients, keypair=False, floating_ip=False,
+                                security_group=False,
+                                security_group_rules=False,
                                 ethertype='IPv4', use_neutron=True,
                                 floating_network_id=None,
                                 floating_network_name=None):
+    """Provision resources for VM ping/ssh testing
+
+    Create resources required to be able to ping / ssh a virtual machine:
+    keypair, security group, security group rules and a floating IP.
+    Which of those resources are required may depend on the cloud setup and on
+    the specific test and it can be controlled via the corresponding
+    arguments.
+
+    Provisioned resources are returned in a dictionary.
+
+    :param clients: Instance of `tempest.lib.services.clients.ServiceClients`
+        or of a subclass of it. Resources are provisioned using clients from
+        `clients`.
+    :param keypair: Whether to provision a keypair. Defaults to False.
+    :param floating_ip: Whether to provision a floating IP. Defaults to False.
+    :param security_group: Whether to provision a security group. Defaults to
+        False.
+    :param security_group_rules: Whether to provision security group rules.
+        Defaults to False.
+    :param ethertype: 'IPv4' or 'IPv6'. Honoured only in case neutron is used.
+    :param use_neutron: When True resources are provisioned via neutron, when
+        False resources are provisioned via nova.
+    :param floating_network_id: The id of the network used to provision a
+        floating IP. Only used if a floating IP is requested and with neutron.
+    :param floating_network_name: The name of the floating IP pool used to
+        provision the floating IP. Only used if a floating IP is requested and
+        with nova-net.
+    :returns: A dictionary with the same keys as the input
+        `validation_resources` and the resources for values in the format
+         they are returned by the API.
+
+    Examples::
+
+        from tempest.common import validation_resources as vr
+        from tempest.lib import auth
+        from tempest.lib.services import clients
+
+        creds = auth.get_credentials('http://mycloud/identity/v3',
+                                     username='me', project_name='me',
+                                     password='secret', domain_name='Default')
+        osclients = clients.ServiceClients(creds, 'http://mycloud/identity/v3')
+        # Request keypair and floating IP
+        resources = dict(keypair=True, security_group=False,
+                         security_group_rules=False, floating_ip=True)
+        resources = vr.create_validation_resources(
+            osclients, use_neutron=True,
+            floating_network_id='4240E68E-23DA-4C82-AC34-9FEFAA24521C',
+            **resources)
+
+        # The floating IP to be attached to the VM
+        floating_ip = resources['floating_ip']['ip']
+    """
     # Create and Return the validation resources required to validate a VM
     validation_data = {}
-    if validation_resources:
-        if validation_resources['keypair']:
-            keypair_name = data_utils.rand_name('keypair')
-            validation_data.update(os.keypairs_client.create_keypair(
+    if keypair:
+        keypair_name = data_utils.rand_name('keypair')
+        validation_data.update(
+            clients.compute.KeyPairsClient().create_keypair(
                 name=keypair_name))
-            LOG.debug("Validation resource key %s created", keypair_name)
-        add_rule = False
-        if validation_resources['security_group']:
-            if validation_resources['security_group_rules']:
-                add_rule = True
-            validation_data['security_group'] = \
-                create_ssh_security_group(
-                    os, add_rule, use_neutron=use_neutron, ethertype=ethertype)
-        if validation_resources['floating_ip']:
-            if use_neutron:
-                floatingip = os.floating_ips_client.create_floatingip(
-                    floating_network_id=floating_network_id)
-                # validation_resources['floating_ip'] has historically looked
-                # like a compute API POST /os-floating-ips response, so we need
-                # to mangle it a bit for a Neutron response with different
-                # fields.
-                validation_data['floating_ip'] = floatingip['floatingip']
-                validation_data['floating_ip']['ip'] = (
-                    floatingip['floatingip']['floating_ip_address'])
-            else:
-                # NOTE(mriedem): The os-floating-ips compute API was deprecated
-                # in the 2.36 microversion. Any tests for CRUD operations on
-                # floating IPs using the compute API should be capped at 2.35.
-                validation_data.update(
-                    os.compute_floating_ips_client.create_floating_ip(
-                        pool=floating_network_name))
+        LOG.debug("Validation resource key %s created", keypair_name)
+    if security_group:
+        validation_data['security_group'] = create_ssh_security_group(
+            clients, add_rule=security_group_rules,
+            use_neutron=use_neutron, ethertype=ethertype)
+    if floating_ip:
+        floating_ip_client = _network_service(
+            clients, use_neutron).FloatingIPsClient()
+        if use_neutron:
+            floatingip = floating_ip_client.create_floatingip(
+                floating_network_id=floating_network_id)
+            # validation_resources['floating_ip'] has historically looked
+            # like a compute API POST /os-floating-ips response, so we need
+            # to mangle it a bit for a Neutron response with different
+            # fields.
+            validation_data['floating_ip'] = floatingip['floatingip']
+            validation_data['floating_ip']['ip'] = (
+                floatingip['floatingip']['floating_ip_address'])
+        else:
+            # NOTE(mriedem): The os-floating-ips compute API was deprecated
+            # in the 2.36 microversion. Any tests for CRUD operations on
+            # floating IPs using the compute API should be capped at 2.35.
+            validation_data.update(floating_ip_client.create_floating_ip(
+                pool=floating_network_name))
     return validation_data
 
 
-def clear_validation_resources(os, validation_data=None):
-    # Cleanup the vm validation resources
+def clear_validation_resources(clients, keypair=None, floating_ip=None,
+                               security_group=None, use_neutron=True):
+    """Cleanup resources for VM ping/ssh testing
+
+    Cleanup a set of resources provisioned via `create_validation_resources`.
+    In case of errors during cleanup, the exception is logged and the cleanup
+    process is continued. The first exception that was raised is re-raised
+    after the cleanup is complete.
+
+    :param clients: Instance of `tempest.lib.services.clients.ServiceClients`
+        or of a subclass of it. Resources are provisioned using clients from
+        `clients`.
+    :param keypair: A dictionary with the keypair to be deleted. Defaults to
+        None.
+    :param floating_ip: A dictionary with the floating_ip to be deleted.
+        Defaults to None.
+    :param security_group: A dictionary with the security_group to be deleted.
+        Defaults to None.
+    :param use_neutron: When True resources are provisioned via neutron, when
+        False resources are provisioned via nova.
+    :returns: A dictionary with the same keys as the input
+        `validation_resources` and the resources for values in the format
+         they are returned by the API.
+
+    Examples::
+
+        from tempest.common import validation_resources as vr
+        from tempest.lib import auth
+        from tempest.lib.services import clients
+
+        creds = auth.get_credentials('http://mycloud/identity/v3',
+                                     username='me', project_name='me',
+                                     password='secret', domain_name='Default')
+        osclients = clients.ServiceClients(creds, 'http://mycloud/identity/v3')
+        # Request keypair and floating IP
+        resources = dict(keypair=True, security_group=False,
+                         security_group_rules=False, floating_ip=True)
+        resources = vr.create_validation_resources(
+            osclients, validation_resources=resources, use_neutron=True,
+            floating_network_id='4240E68E-23DA-4C82-AC34-9FEFAA24521C')
+
+        # Now cleanup the resources
+        try:
+            vr.clear_validation_resources(osclients, use_neutron=True,
+                                          **resources)
+        except Exception as e:
+            LOG.exception('Something went wrong during cleanup, ignoring')
+    """
     has_exception = None
-    if validation_data:
-        if 'keypair' in validation_data:
-            keypair_client = os.keypairs_client
-            keypair_name = validation_data['keypair']['name']
-            try:
-                keypair_client.delete_keypair(keypair_name)
-            except lib_exc.NotFound:
-                LOG.warning(
-                    "Keypair %s is not found when attempting to delete",
-                    keypair_name
-                )
-            except Exception as exc:
-                LOG.exception('Exception raised while deleting key %s',
-                              keypair_name)
-                if not has_exception:
-                    has_exception = exc
-        if 'security_group' in validation_data:
-            security_group_client = os.compute_security_groups_client
-            sec_id = validation_data['security_group']['id']
-            try:
-                security_group_client.delete_security_group(sec_id)
-                security_group_client.wait_for_resource_deletion(sec_id)
-            except lib_exc.NotFound:
-                LOG.warning("Security group %s is not found when attempting "
-                            "to delete", sec_id)
-            except lib_exc.Conflict as exc:
-                LOG.exception('Conflict while deleting security '
-                              'group %s VM might not be deleted', sec_id)
-                if not has_exception:
-                    has_exception = exc
-            except Exception as exc:
-                LOG.exception('Exception raised while deleting security '
-                              'group %s', sec_id)
-                if not has_exception:
-                    has_exception = exc
-        if 'floating_ip' in validation_data:
-            floating_client = os.compute_floating_ips_client
-            fip_id = validation_data['floating_ip']['id']
-            try:
-                floating_client.delete_floating_ip(fip_id)
-            except lib_exc.NotFound:
-                LOG.warning('Floating ip %s not found while attempting to '
-                            'delete', fip_id)
-            except Exception as exc:
-                LOG.exception('Exception raised while deleting ip %s', fip_id)
-                if not has_exception:
-                    has_exception = exc
+    if keypair:
+        keypair_client = clients.compute.KeyPairsClient()
+        keypair_name = keypair['name']
+        try:
+            keypair_client.delete_keypair(keypair_name)
+        except lib_exc.NotFound:
+            LOG.warning(
+                "Keypair %s is not found when attempting to delete",
+                keypair_name
+            )
+        except Exception as exc:
+            LOG.exception('Exception raised while deleting key %s',
+                          keypair_name)
+            if not has_exception:
+                has_exception = exc
+    network_service = _network_service(clients, use_neutron)
+    if security_group:
+        security_group_client = network_service.SecurityGroupsClient()
+        sec_id = security_group['id']
+        try:
+            security_group_client.delete_security_group(sec_id)
+            security_group_client.wait_for_resource_deletion(sec_id)
+        except lib_exc.NotFound:
+            LOG.warning("Security group %s is not found when attempting "
+                        "to delete", sec_id)
+        except lib_exc.Conflict as exc:
+            LOG.exception('Conflict while deleting security '
+                          'group %s VM might not be deleted', sec_id)
+            if not has_exception:
+                has_exception = exc
+        except Exception as exc:
+            LOG.exception('Exception raised while deleting security '
+                          'group %s', sec_id)
+            if not has_exception:
+                has_exception = exc
+    if floating_ip:
+        floating_ip_client = network_service.FloatingIPsClient()
+        fip_id = floating_ip['id']
+        try:
+            if use_neutron:
+                floating_ip_client.delete_floatingip(fip_id)
+            else:
+                floating_ip_client.delete_floating_ip(fip_id)
+        except lib_exc.NotFound:
+            LOG.warning('Floating ip %s not found while attempting to '
+                        'delete', fip_id)
+        except Exception as exc:
+            LOG.exception('Exception raised while deleting ip %s', fip_id)
+            if not has_exception:
+                has_exception = exc
     if has_exception:
         raise has_exception
diff --git a/tempest/lib/common/http.py b/tempest/lib/common/http.py
index 8a47d44..b4b1fc9 100644
--- a/tempest/lib/common/http.py
+++ b/tempest/lib/common/http.py
@@ -25,8 +25,7 @@
         if disable_ssl_certificate_validation:
             urllib3.disable_warnings()
             kwargs['cert_reqs'] = 'CERT_NONE'
-
-        if ca_certs:
+        elif ca_certs:
             kwargs['cert_reqs'] = 'CERT_REQUIRED'
             kwargs['ca_certs'] = ca_certs
 
diff --git a/tempest/lib/services/network/security_groups_client.py b/tempest/lib/services/network/security_groups_client.py
index 1f30216..d3ebf20 100644
--- a/tempest/lib/services/network/security_groups_client.py
+++ b/tempest/lib/services/network/security_groups_client.py
@@ -10,6 +10,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.network import base
 
 
@@ -66,3 +67,10 @@
         """
         uri = '/security-groups'
         return self.list_resources(uri, **filters)
+
+    def is_resource_deleted(self, id):
+        try:
+            self.show_security_group(id)
+        except lib_exc.NotFound:
+            return True
+        return False
diff --git a/tempest/lib/services/volume/v2/volumes_client.py b/tempest/lib/services/volume/v2/volumes_client.py
index e932adc..d13e449 100644
--- a/tempest/lib/services/volume/v2/volumes_client.py
+++ b/tempest/lib/services/volume/v2/volumes_client.py
@@ -197,10 +197,18 @@
         return rest_client.ResponseBody(resp, body)
 
     def is_resource_deleted(self, id):
+        """Check the specified resource is deleted or not.
+
+        :param id: A checked resource id
+        :raises lib_exc.DeleteErrorException: If the specified resource is on
+        the status the delete was failed.
+        """
         try:
-            self.show_volume(id)
+            volume = self.show_volume(id)
         except lib_exc.NotFound:
             return True
+        if volume["volume"]["status"] == "error_deleting":
+            raise lib_exc.DeleteErrorException(resource_id=id)
         return False
 
     @property
diff --git a/tempest/test.py b/tempest/test.py
index 47cbb5e..78db8e1 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -379,12 +379,14 @@
             raise lib_exc.InvalidConfiguration(
                 msg % CONF.validation.ip_version_for_ssh)
         if hasattr(cls, "os_primary"):
+            vr = cls.validation_resources
             cls.validation_resources = vresources.create_validation_resources(
-                cls.os_primary, cls.validation_resources,
+                cls.os_primary,
                 use_neutron=CONF.service_available.neutron,
                 ethertype='IPv' + str(CONF.validation.ip_version_for_ssh),
                 floating_network_id=CONF.network.public_network_id,
-                floating_network_name=CONF.network.floating_network_name)
+                floating_network_name=CONF.network.floating_network_name,
+                **vr)
         else:
             LOG.warning("Client manager not found, validation resources not"
                         " created")
@@ -398,8 +400,10 @@
         """
         if cls.validation_resources:
             if hasattr(cls, "os_primary"):
-                vresources.clear_validation_resources(cls.os_primary,
-                                                      cls.validation_resources)
+                vr = cls.validation_resources
+                vresources.clear_validation_resources(
+                    cls.os_primary,
+                    use_neutron=CONF.service_available.neutron, **vr)
                 cls.validation_resources = {}
             else:
                 LOG.warning("Client manager not found, validation resources "
diff --git a/tempest/tests/lib/common/test_http.py b/tempest/tests/lib/common/test_http.py
new file mode 100644
index 0000000..a292209
--- /dev/null
+++ b/tempest/tests/lib/common/test_http.py
@@ -0,0 +1,68 @@
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.common import http
+from tempest.tests import base
+
+
+class TestClosingHttp(base.TestCase):
+    def setUp(self):
+        super(TestClosingHttp, self).setUp()
+        self.cert_none = "CERT_NONE"
+        self.cert_location = "/etc/ssl/certs/ca-certificates.crt"
+
+    def test_constructor_invalid_ca_certs_and_timeout(self):
+        connection = http.ClosingHttp(
+            disable_ssl_certificate_validation=False,
+            ca_certs=None,
+            timeout=None)
+        for attr in ('cert_reqs', 'ca_certs', 'timeout'):
+            self.assertNotIn(attr, connection.connection_pool_kw)
+
+    def test_constructor_valid_ca_certs(self):
+        cert_required = 'CERT_REQUIRED'
+        connection = http.ClosingHttp(
+            disable_ssl_certificate_validation=False,
+            ca_certs=self.cert_location,
+            timeout=None)
+        self.assertEqual(cert_required,
+                         connection.connection_pool_kw['cert_reqs'])
+        self.assertEqual(self.cert_location,
+                         connection.connection_pool_kw['ca_certs'])
+        self.assertNotIn('timeout',
+                         connection.connection_pool_kw)
+
+    def test_constructor_ssl_cert_validation_disabled(self):
+        connection = http.ClosingHttp(
+            disable_ssl_certificate_validation=True,
+            ca_certs=None,
+            timeout=30)
+        self.assertEqual(self.cert_none,
+                         connection.connection_pool_kw['cert_reqs'])
+        self.assertEqual(30,
+                         connection.connection_pool_kw['timeout'])
+        self.assertNotIn('ca_certs',
+                         connection.connection_pool_kw)
+
+    def test_constructor_ssl_cert_validation_disabled_and_ca_certs(self):
+        connection = http.ClosingHttp(
+            disable_ssl_certificate_validation=True,
+            ca_certs=self.cert_location,
+            timeout=None)
+        self.assertNotIn('timeout',
+                         connection.connection_pool_kw)
+        self.assertEqual(self.cert_none,
+                         connection.connection_pool_kw['cert_reqs'])
+        self.assertNotIn('ca_certs',
+                         connection.connection_pool_kw)
diff --git a/tempest/tests/lib/test_ssh.py b/tempest/tests/lib/test_ssh.py
index a16da1c..37fe646 100644
--- a/tempest/tests/lib/test_ssh.py
+++ b/tempest/tests/lib/test_ssh.py
@@ -12,11 +12,11 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from io import StringIO
 import socket
 
 import mock
 import six
+from six import StringIO
 import testtools
 
 from tempest.lib.common import ssh
diff --git a/tempest/tests/test_base_test.py b/tempest/tests/test_base_test.py
index 6c6f612..3ece11d 100644
--- a/tempest/tests/test_base_test.py
+++ b/tempest/tests/test_base_test.py
@@ -13,10 +13,10 @@
 #    under the License.
 
 import mock
+from oslo_config import cfg
 
 from tempest import clients
 from tempest.common import credentials_factory as credentials
-from tempest import config
 from tempest.lib.common import fixed_network
 from tempest import test
 from tempest.tests import base
@@ -28,8 +28,9 @@
         super(TestBaseTestCase, self).setUp()
         self.useFixture(fake_config.ConfigFixture())
         self.fixed_network_name = 'fixed-net'
-        config.CONF.compute.fixed_network_name = self.fixed_network_name
-        config.CONF.service_available.neutron = True
+        cfg.CONF.set_default('fixed_network_name', self.fixed_network_name,
+                             'compute')
+        cfg.CONF.set_default('neutron', True, 'service_available')
 
     @mock.patch.object(test.BaseTestCase, 'get_client_manager')
     @mock.patch.object(test.BaseTestCase, '_get_credentials_provider')
@@ -56,7 +57,7 @@
     def test_get_tenant_network_with_nova_net(self, mock_man, mock_iaa,
                                               mock_giv, mock_gtn, mock_gcp,
                                               mock_gcm):
-        config.CONF.service_available.neutron = False
+        cfg.CONF.set_default('neutron', False, 'service_available')
         mock_prov = mock.Mock()
         mock_admin_man = mock.Mock()
         mock_iaa.return_value = True
diff --git a/tempest/tests/test_decorators.py b/tempest/tests/test_decorators.py
index 2fc84dc..e3e21f9 100644
--- a/tempest/tests/test_decorators.py
+++ b/tempest/tests/test_decorators.py
@@ -31,6 +31,10 @@
                          fake_config.FakePrivate)
 
 
+# NOTE: The test module is for tempest.test.idempotent_id.
+# After all projects switch to use decorators.idempotent_id,
+# we can remove tempest.test.idempotent_id as well as this
+# test module
 class TestIdempotentIdDecorator(BaseDecoratorsTest):
 
     def _test_helper(self, _id, **decorator_args):
diff --git a/test-requirements.txt b/test-requirements.txt
index 09c7685..29f0865 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -5,8 +5,8 @@
 # needed for doc build
 sphinx>=1.6.2 # BSD
 openstackdocstheme>=1.16.0 # Apache-2.0
-reno!=2.3.1,>=1.8.0 # Apache-2.0
-mock>=2.0 # BSD
+reno>=2.5.0 # Apache-2.0
+mock>=2.0.0 # BSD
 coverage!=4.4,>=4.0 # Apache-2.0
 oslotest>=1.10.0 # Apache-2.0
 flake8-import-order==0.11 # LGPLv3