Merge "Adding description for testcases - network part2"
diff --git a/.zuul.yaml b/.zuul.yaml
index 87e277c..9c53ba9 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -180,6 +180,7 @@
         USE_PYTHON3: true
         FORCE_CONFIG_DRIVE: true
         ENABLE_VOLUME_MULTIATTACH: true
+        GLANCE_USE_IMPORT_WORKFLOW: True
       devstack_services:
         s-account: false
         s-container: false
@@ -270,6 +271,7 @@
         USE_PYTHON3: true
         FORCE_CONFIG_DRIVE: true
         ENABLE_VOLUME_MULTIATTACH: true
+        GLANCE_USE_IMPORT_WORKFLOW: True
 
 - job:
     name: tempest-integrated-object-storage
@@ -676,7 +678,6 @@
             voting: false
             irrelevant-files: *tempest-irrelevant-files
         - devstack-plugin-ceph-tempest-py3:
-            voting: false
             irrelevant-files: *tempest-irrelevant-files
         - neutron-grenade-multinode:
             irrelevant-files: *tempest-irrelevant-files
@@ -718,19 +719,19 @@
             irrelevant-files: *tempest-irrelevant-files
         - tempest-ipv6-only:
             irrelevant-files: *tempest-irrelevant-files-2
+        - devstack-plugin-ceph-tempest-py3:
+            irrelevant-files: *tempest-irrelevant-files
     experimental:
       jobs:
         - tempest-cinder-v2-api:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-all:
             irrelevant-files: *tempest-irrelevant-files
-        - legacy-tempest-dsvm-neutron-dvr-multinode-full:
-            irrelevant-files: *tempest-irrelevant-files
         - neutron-tempest-dvr-ha-multinode-full:
             irrelevant-files: *tempest-irrelevant-files
         - nova-tempest-v2-api:
             irrelevant-files: *tempest-irrelevant-files
-        - legacy-tempest-dsvm-lvm-multibackend:
+        - cinder-tempest-lvm-multibackend:
             irrelevant-files: *tempest-irrelevant-files
         - tempest-pg-full:
             irrelevant-files: *tempest-irrelevant-files
diff --git a/doc/source/conf.py b/doc/source/conf.py
index dd7c544..ded713d 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -112,7 +112,7 @@
 show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = 'native'
 
 # A list of ignored prefixes for module index sorting.
 modindex_common_prefix = ['tempest.']
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 36828e0..c43e420 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -193,10 +193,6 @@
 There are also options in the ``scenario`` section for images:
 
 #. ``img_file``
-#. ``img_dir``
-#. ``aki_img_file``
-#. ``ari_img_file``
-#. ``ami_img_file``
 #. ``img_container_format``
 #. ``img_disk_format``
 
@@ -205,13 +201,9 @@
 Tempest where an image file is located and describe its metadata for when it is
 uploaded.
 
-The behavior of these options is a bit convoluted (which will likely be fixed in
-future versions). You first need to specify ``img_dir``, which is the directory
-in which Tempest will look for the image files. First, it will check if the
-filename set for ``img_file`` could be found in ``img_dir``. If it is found then
-the ``img_container_format`` and ``img_disk_format`` options are used to upload
-that image to glance. However, if it is not found, Tempest will look for the
-three uec image file name options as a fallback. If neither is found, the tests
+You first need to specify full path of the image using ``img_file`` option.
+If it is found then the ``img_container_format`` and ``img_disk_format``
+options are used to upload that image to glance. If it's not found, the tests
 requiring an image to upload will fail.
 
 It is worth pointing out that using `cirros`_ is a very good choice for running
diff --git a/doc/source/index.rst b/doc/source/index.rst
index f878888..d4dc166 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -80,6 +80,7 @@
    microversion_testing
    test_removal
    write_tests
+   requirement_upper_constraint_for_tempest
 
 Plugins
 -------
diff --git a/doc/source/plugins/plugin.rst b/doc/source/plugins/plugin.rst
index a9e2059..ab1b0b1 100644
--- a/doc/source/plugins/plugin.rst
+++ b/doc/source/plugins/plugin.rst
@@ -43,7 +43,7 @@
 In order to create the basic structure with base classes and test directories
 you can use the tempest-plugin-cookiecutter project::
 
-  > pip install -U cookiecutter && cookiecutter https://opendev.org/openstack/tempest-plugin-cookiecutter
+  > pip install -U cookiecutter && cookiecutter https://opendev.org/openstack/tempest-plugin-cookiecutter.git
 
   Cloning into 'tempest-plugin-cookiecutter'...
   remote: Counting objects: 17, done.
diff --git a/doc/source/requirement_upper_constraint_for_tempest.rst b/doc/source/requirement_upper_constraint_for_tempest.rst
new file mode 100644
index 0000000..2eebdda
--- /dev/null
+++ b/doc/source/requirement_upper_constraint_for_tempest.rst
@@ -0,0 +1,56 @@
+Requirements Upper Constraint for Tempest
+=========================================
+
+Tempest is branchless and supported stable branches use Tempest
+master and all EM stable branches use old compatible Tempest version
+for their testing. This means the OpenStack installed upper-constraints
+might not be compatible with Tempest used for stable branch testing.
+For example, if Tempest master is used for testing the stable/stein
+then stable/stein constraint might not be compatible with Tempest master so
+we need to use master upper-constraints there. That is why we use virtual
+env for Tempest installation and running tests so that we can control Tempest
+required constraint from system wide installed constraints.
+
+Devstack takes care of using the master upper-constraints when Tempest master
+is used. But when old Tempest is used then devstack alone cannot handle the
+compatible constraints because Tempest in-tree tox.ini also set the
+upper-constraints which are master constraints so if devstack set the different
+constraints than what we have in tox.ini we end up re-creation of venv which
+flush all previously installed tempest plugins in that venv. More details are
+on `this ML thread <http://lists.openstack.org/pipermail/openstack-discuss/2020-April/014388.html>`_
+
+To solve that problem we have two ways:
+
+#. Set UPPER_CONSTRAINTS_FILE to compatible constraint path
+   This option is not easy as it requires to set this env var everywhere
+   Tempest tox env is used like in devstack, grenade, projects side, zuulv3 roles etc.
+
+#. Pin upper-constraints in tox.ini
+   If we can pin the upper-constraints in tox.ini on every release with the branch
+   constraint at the time of release then we can solve it in an easy way because tox
+   can use the compatible constraint at the time of venv creation itself. But this can
+   again mismatch with the devstack set constraint so we need to follow the below process
+   to make it work.
+
+How to pin upper-constraints in tox.ini
+---------------------------------------
+
+This has to be done exactly before we cut the Tempest new major version bump
+release for the cycle.
+
+Step1: Add the pin constraint proposal in `QA office hour <https://wiki.openstack.org/wiki/Meetings/QATeamMeeting#Agenda_for_next_Office_hours>`_.
+       Pin constraint proposal includes:
+
+       - pin constraint patch. `Example patch 720578 <https://review.opendev.org/#/c/720578/>`_
+       - revert of pin constraint patch. `Example patch 721724 <https://review.opendev.org/#/c/721724/>`_
+
+Step2: Approve pin constraint and its revert patch together.
+       During office hour we need to check that there are no open patches for
+       Tempest release and accordingly we fast approve the 'pin constraint' and its
+       revert patch during office hour itself. Remember 'pin constraint patch' has to be
+       the last commit to include in Tempest release.
+
+Step3: Use 'pin constraint patch' hash for the Tempest new release.
+       By using the 'pin constraint patch' hash we make sure tox.ini in Tempest
+       released tag has the compatible stable constraint not the master one.
+       For Example `Tempest 24.0.0 <https://opendev.org/openstack/tempest/src/tag/24.0.0/tox.ini#L14>`_
diff --git a/doc/source/sampleconf.rst b/doc/source/sampleconf.rst
index c290140..45164a3 100644
--- a/doc/source/sampleconf.rst
+++ b/doc/source/sampleconf.rst
@@ -10,4 +10,6 @@
 
 The sample configuration can also be viewed in `file form <_static/tempest.conf.sample>`_.
 
-.. literalinclude:: _static/tempest.conf.sample
+.. only:: html
+
+   .. literalinclude:: _static/tempest.conf.sample
diff --git a/releasenotes/notes/Remove-deprecated-image-scenario-options-b573c60e873ab451.yaml b/releasenotes/notes/Remove-deprecated-image-scenario-options-b573c60e873ab451.yaml
new file mode 100644
index 0000000..018d01d
--- /dev/null
+++ b/releasenotes/notes/Remove-deprecated-image-scenario-options-b573c60e873ab451.yaml
@@ -0,0 +1,15 @@
+---
+upgrade:
+  - |
+    The following deprecated image scenario options are removed after a ~4
+    year deprecation period.
+
+    * ``ami_img_file``
+    * ``ari_img_file``
+    * ``aki_img_file``
+
+    Starting Tempest 25.0.0 release, CONF.scenario.img_file need a full path
+    for the image. CONF.scenario.img_dir was deprecated and will be removed
+    in the next release. Till Tempest 25.0.0, old behavior is maintained and
+    keep working but starting Tempest 26.0.0, you need to specify the full path
+    in CONF.scenario.img_file config option.
diff --git a/releasenotes/notes/Set-default-of-operator_role-to-member-f9c3abd2ebde23b7.yaml b/releasenotes/notes/Set-default-of-operator_role-to-member-f9c3abd2ebde23b7.yaml
new file mode 100644
index 0000000..980f4ca
--- /dev/null
+++ b/releasenotes/notes/Set-default-of-operator_role-to-member-f9c3abd2ebde23b7.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - |
+    ``Member`` role has been deprecated and replaced by ``member``. Therefore
+    the default value of config option ``[object-storage].operator_role`` is
+    changed to ``member``. (Fixes bug #1330132)
diff --git a/releasenotes/notes/account-generator-accepts-positive-numbers-only-33a366a297494ef7.yaml b/releasenotes/notes/account-generator-accepts-positive-numbers-only-33a366a297494ef7.yaml
new file mode 100644
index 0000000..dfee1db
--- /dev/null
+++ b/releasenotes/notes/account-generator-accepts-positive-numbers-only-33a366a297494ef7.yaml
@@ -0,0 +1,7 @@
+---
+fixes:
+  - |
+   Concurrency parameter for account-generator command was accepting
+   negative values and zero. The concurrency parameter now accepts only
+   positive numbers. When a negative value or zero is passed to the
+   program then the program ends and help is displayed.
diff --git a/releasenotes/notes/image_import_testing_support-22ba4bcb9f2fb848.yaml b/releasenotes/notes/image_import_testing_support-22ba4bcb9f2fb848.yaml
new file mode 100644
index 0000000..b0180cc
--- /dev/null
+++ b/releasenotes/notes/image_import_testing_support-22ba4bcb9f2fb848.yaml
@@ -0,0 +1,17 @@
+---
+features:
+  - |
+    Add glance image import APIs function to v2
+    images_client library.
+
+    * stage_image_file
+    * info_import
+    * info_stores
+    * image_import
+other:
+  - |
+    New configuration options
+    ``CONF.glance.image_feature_enabled.image_import`` has been introduced
+    to enable the image import tests. If your glance deployement support
+    image import functionality then you can enable the image import tests
+    via this flag. Default value of this new config option is false.
diff --git a/releasenotes/notes/network-swap-to-project_id-a1d7fdf6c5e1cf44.yaml b/releasenotes/notes/network-swap-to-project_id-a1d7fdf6c5e1cf44.yaml
new file mode 100644
index 0000000..4cf0c76
--- /dev/null
+++ b/releasenotes/notes/network-swap-to-project_id-a1d7fdf6c5e1cf44.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    [`blueprint blueprint adopt-oslo-versioned-objects-for-db <https://blueprints.launchpad.net/neutron/+spec/adopt-oslo-versioned-objects-for-db>`_]
+    Any reference to "tenant_id" in Network objects is replaced with
+    "project_id".
diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py
index 71df749..9a9de43 100644
--- a/releasenotes/source/conf.py
+++ b/releasenotes/source/conf.py
@@ -98,7 +98,7 @@
 # show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = 'native'
 
 # A list of ignored prefixes for module index sorting.
 # modindex_common_prefix = []
diff --git a/requirements.txt b/requirements.txt
index bf38fae..d8738f0 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,7 +3,7 @@
 # process, which may cause wedges in the gate later.
 pbr!=2.1.0,>=2.0.0 # Apache-2.0
 cliff!=2.9.0,>=2.8.0 # Apache-2.0
-jsonschema>=2.6.0 # MIT
+jsonschema>=3.2.0 # MIT
 testtools>=2.2.0 # MIT
 paramiko>=2.0.0 # LGPLv2.1+
 netaddr>=0.7.18 # BSD
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index f42f53a..d6b6b7e 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -107,7 +107,10 @@
         """
         def verify_flavor_response_extension(flavor):
             # check some extensions for the flavor create/show/detail response
-            self.assertEqual(flavor['swap'], '')
+            if self.is_requested_microversion_compatible('2.74'):
+                self.assertEqual(flavor['swap'], '')
+            else:
+                self.assertEqual(flavor['swap'], 0)
             self.assertEqual(int(flavor['rxtx_factor']), 1)
             self.assertEqual(flavor['OS-FLV-EXT-DATA:ephemeral'], 0)
             self.assertEqual(flavor['os-flavor-access:is_public'], True)
diff --git a/tempest/api/compute/admin/test_services_negative.py b/tempest/api/compute/admin/test_services_negative.py
index 033caa8..a4d7d3f 100644
--- a/tempest/api/compute/admin/test_services_negative.py
+++ b/tempest/api/compute/admin/test_services_negative.py
@@ -43,6 +43,9 @@
         Expect all services to be returned when the request contains invalid
         parameters.
         """
+        if not self.is_requested_microversion_compatible('2.74'):
+            raise self.skipException(
+                "From microversion 2.75 invalid parameters are not allowed.")
         services = self.client.list_services()['services']
         services_xxx = (self.client.list_services(xxx='nova-compute')
                         ['services'])
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 17cd443..1247494 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -110,27 +110,29 @@
     create_default_network = True
 
     @classmethod
-    def skip_checks(self):
-        super(BaseServerStableDeviceRescueTest, self).skip_checks()
+    def skip_checks(cls):
+        super(BaseServerStableDeviceRescueTest, cls).skip_checks()
         if not CONF.compute_feature_enabled.rescue:
             msg = "Server rescue not available."
-            raise self.skipException(msg)
+            raise cls.skipException(msg)
         if not CONF.compute_feature_enabled.stable_rescue:
             msg = "Stable rescue not available."
-            raise self.skipException(msg)
+            raise cls.skipException(msg)
 
     def _create_server_and_rescue_image(self, hw_rescue_device=None,
                                         hw_rescue_bus=None,
                                         block_device_mapping_v2=None):
+
+        server_id = self.create_test_server(
+            wait_until='ACTIVE')['id']
+        image_id = self.create_image_from_server(
+            server_id, wait_until='ACTIVE')['id']
+
         if block_device_mapping_v2:
             server_id = self.create_test_server(
                 wait_until='ACTIVE',
                 block_device_mapping_v2=block_device_mapping_v2)['id']
-        else:
-            server_id = self.create_test_server(wait_until='ACTIVE')['id']
 
-        image_id = self.create_image_from_server(server_id,
-                                                 wait_until='ACTIVE')['id']
         if hw_rescue_bus:
             self.images_client.update_image(
                 image_id, [dict(add='/hw_rescue_bus',
@@ -193,6 +195,7 @@
 
     min_microversion = '2.87'
 
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('48f123cb-922a-4065-8db6-b9a9074a556b')
     def test_stable_device_rescue_bfv_blank_volume(self):
         block_device_mapping_v2 = [{
@@ -205,6 +208,7 @@
             block_device_mapping_v2=block_device_mapping_v2)
         self._test_stable_device_rescue(server_id, rescue_image_id)
 
+    @decorators.attr(type='slow')
     @decorators.idempotent_id('e4636333-c928-40fc-98b7-70a23eef4224')
     def test_stable_device_rescue_bfv_image_volume(self):
         block_device_mapping_v2 = [{
@@ -217,19 +221,3 @@
             hw_rescue_device='disk', hw_rescue_bus='virtio',
             block_device_mapping_v2=block_device_mapping_v2)
         self._test_stable_device_rescue(server_id, rescue_image_id)
-
-    @decorators.idempotent_id('7fcc5d2c-130e-4750-95f5-7343f9d0a2f3')
-    def test_stable_device_rescue_bfv_snapshot_volume(self):
-        volume_id = self.create_volume()['id']
-        self.volumes_client.set_bootable_volume(volume_id, bootable=True)
-        snapshot_id = self.create_volume_snapshot(volume_id)['id']
-        block_device_mapping_v2 = [{
-            "boot_index": "0",
-            "source_type": "snapshot",
-            "volume_size": CONF.volume.volume_size,
-            "uuid": snapshot_id,
-            "destination_type": "volume"}]
-        server_id, rescue_image_id = self._create_server_and_rescue_image(
-            hw_rescue_device='disk', hw_rescue_bus='virtio',
-            block_device_mapping_v2=block_device_mapping_v2)
-        self._test_stable_device_rescue(server_id, rescue_image_id)
diff --git a/tempest/api/identity/admin/v3/test_application_credentials.py b/tempest/api/identity/admin/v3/test_application_credentials.py
index 7e802c6..c9cafd8 100644
--- a/tempest/api/identity/admin/v3/test_application_credentials.py
+++ b/tempest/api/identity/admin/v3/test_application_credentials.py
@@ -20,9 +20,11 @@
 
 class ApplicationCredentialsV3AdminTest(base.BaseApplicationCredentialsV3Test,
                                         base.BaseIdentityV3AdminTest):
+    """Test keystone application credentials"""
 
     @decorators.idempotent_id('3b3dd48f-3388-406a-a9e6-4d078a552d0e')
     def test_create_application_credential_with_roles(self):
+        """Test creating keystone application credential with roles"""
         role = self.setup_test_role()
         self.os_admin.roles_v3_client.create_user_role_on_project(
             self.project_id,
diff --git a/tempest/api/identity/admin/v3/test_credentials.py b/tempest/api/identity/admin/v3/test_credentials.py
index 23fe788..441f10f 100644
--- a/tempest/api/identity/admin/v3/test_credentials.py
+++ b/tempest/api/identity/admin/v3/test_credentials.py
@@ -20,6 +20,8 @@
 
 
 class CredentialsTestJSON(base.BaseIdentityV3AdminTest):
+    """Test keystone credentials"""
+
     # NOTE: force_tenant_isolation is true in the base class by default but
     # overridden to false here to allow test execution for clouds using the
     # pre-provisioned credentials provider.
@@ -47,6 +49,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('7cd59bf9-bda4-4c72-9467-d21cab278355')
     def test_credentials_create_get_update_delete(self):
+        """Test creating, getting, updating, deleting of credentials"""
         blob = '{"access": "%s", "secret": "%s"}' % (
             data_utils.rand_name('Access'), data_utils.rand_name('Secret'))
         cred = self.creds_client.create_credential(
@@ -82,6 +85,7 @@
 
     @decorators.idempotent_id('13202c00-0021-42a1-88d4-81b44d448aab')
     def test_credentials_list_delete(self):
+        """Test listing credentials"""
         created_cred_ids = list()
         fetched_cred_ids = list()
 
diff --git a/tempest/api/identity/admin/v3/test_domain_configuration.py b/tempest/api/identity/admin/v3/test_domain_configuration.py
index c0b18ca..a246a36 100644
--- a/tempest/api/identity/admin/v3/test_domain_configuration.py
+++ b/tempest/api/identity/admin/v3/test_domain_configuration.py
@@ -21,6 +21,8 @@
 
 
 class DomainConfigurationTestJSON(base.BaseIdentityV3AdminTest):
+    """Test domain configuration"""
+
     # NOTE: force_tenant_isolation is true in the base class by default but
     # overridden to false here to allow test execution for clouds using the
     # pre-provisioned credentials provider.
@@ -51,10 +53,12 @@
 
     @decorators.idempotent_id('11a02bf0-6f94-4380-b3b0-c8dc18fc0d22')
     def test_show_default_group_config_and_options(self):
-        # The API supports only the identity and ldap groups. For the ldap
-        # group, a valid value is url or user_tree_dn. For the identity group,
-        # a valid value is driver.
+        """Test showing default keystone group config and options
 
+        The API supports only the identity and ldap groups. For the ldap
+        group, a valid value is url or user_tree_dn. For the identity group,
+        a valid value is driver.
+        """
         # Check that the default config has the identity and ldap groups.
         config = self.client.show_default_config_settings()['config']
         self.assertIsInstance(config, dict)
@@ -93,6 +97,7 @@
 
     @decorators.idempotent_id('9e3ff13c-f597-4f01-9377-d6c06c2a1477')
     def test_create_domain_config_and_show_config_groups_and_options(self):
+        """Test creating and showing keystone config groups and options"""
         domain, created_config = self._create_domain_and_config(
             self.custom_config)
 
@@ -117,6 +122,7 @@
 
     @decorators.idempotent_id('7161023e-5dd0-4612-9da0-1bac6ac30b63')
     def test_create_update_and_delete_domain_config(self):
+        """Test creating, updating and deleting keystone domain config"""
         domain, created_config = self._create_domain_and_config(
             self.custom_config)
 
@@ -140,6 +146,7 @@
 
     @decorators.idempotent_id('c7510fa2-6661-4170-9c6b-4783a80651e9')
     def test_create_update_and_delete_domain_config_groups_and_opts(self):
+        """Test create/update/delete keystone domain config groups and opts"""
         domain, _ = self._create_domain_and_config(self.custom_config)
 
         # Check that updating configuration groups work.
diff --git a/tempest/api/identity/admin/v3/test_endpoint_groups.py b/tempest/api/identity/admin/v3/test_endpoint_groups.py
index 7d85dc9..2fa92e3 100644
--- a/tempest/api/identity/admin/v3/test_endpoint_groups.py
+++ b/tempest/api/identity/admin/v3/test_endpoint_groups.py
@@ -20,6 +20,8 @@
 
 
 class EndPointGroupsTest(base.BaseIdentityV3AdminTest):
+    """Test endpoint groups"""
+
     # NOTE: force_tenant_isolation is true in the base class by default but
     # overridden to false here to allow test execution for clouds using the
     # pre-provisioned credentials provider.
@@ -68,6 +70,7 @@
 
     @decorators.idempotent_id('7c69e7a1-f865-402d-a2ea-44493017315a')
     def test_create_list_show_check_delete_endpoint_group(self):
+        """Test create/list/show/check/delete of endpoint group"""
         service_id = self._create_service()
         self.addCleanup(self.services_client.delete_service, service_id)
         name = data_utils.rand_name('service_group')
@@ -127,6 +130,7 @@
 
     @decorators.idempotent_id('51c8fc38-fa84-4e76-b5b6-6fc37770fb26')
     def test_update_endpoint_group(self):
+        """Test updating endpoint group"""
         # Creating an endpoint group so as to check update endpoint group
         # with new values
         service1_id = self._create_service()
diff --git a/tempest/api/identity/admin/v3/test_endpoints_negative.py b/tempest/api/identity/admin/v3/test_endpoints_negative.py
index 164b577..9689d87 100644
--- a/tempest/api/identity/admin/v3/test_endpoints_negative.py
+++ b/tempest/api/identity/admin/v3/test_endpoints_negative.py
@@ -20,6 +20,8 @@
 
 
 class EndpointsNegativeTestJSON(base.BaseIdentityV3AdminTest):
+    """Negative tests of endpoint"""
+
     # NOTE: force_tenant_isolation is true in the base class by default but
     # overridden to false here to allow test execution for clouds using the
     # pre-provisioned credentials provider.
@@ -48,7 +50,10 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ac6c137e-4d3d-448f-8c83-4f13d0942651')
     def test_create_with_enabled_False(self):
-        # Enabled should be a boolean, not a string like 'False'
+        """Test creating endpoint with invalid enabled value 'False'
+
+        Enabled should be a boolean, not a string like 'False'
+        """
         interface = 'public'
         url = data_utils.rand_url()
         region = data_utils.rand_name('region')
@@ -59,7 +64,10 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9c43181e-0627-484a-8c79-923e8a59598b')
     def test_create_with_enabled_True(self):
-        # Enabled should be a boolean, not a string like 'True'
+        """Test creating endpoint with invalid enabled value 'True'
+
+        Enabled should be a boolean, not a string like 'True'
+        """
         interface = 'public'
         url = data_utils.rand_url()
         region = data_utils.rand_name('region')
@@ -88,11 +96,17 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('65e41f32-5eb7-498f-a92a-a6ccacf7439a')
     def test_update_with_enabled_False(self):
-        # Enabled should be a boolean, not a string like 'False'
+        """Test updating endpoint with invalid enabled value 'False'
+
+        Enabled should be a boolean, not a string like 'False'
+        """
         self._assert_update_raises_bad_request('False')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('faba3587-f066-4757-a48e-b4a3f01803bb')
     def test_update_with_enabled_True(self):
-        # Enabled should be a boolean, not a string like 'True'
+        """Test updating endpoint with invalid enabled value 'True'
+
+        Enabled should be a boolean, not a string like 'True'
+        """
         self._assert_update_raises_bad_request('True')
diff --git a/tempest/api/identity/admin/v3/test_groups.py b/tempest/api/identity/admin/v3/test_groups.py
index 2dd1fe2..b2e3775 100644
--- a/tempest/api/identity/admin/v3/test_groups.py
+++ b/tempest/api/identity/admin/v3/test_groups.py
@@ -23,6 +23,8 @@
 
 
 class GroupsV3TestJSON(base.BaseIdentityV3AdminTest):
+    """Test keystone groups"""
+
     # NOTE: force_tenant_isolation is true in the base class by default but
     # overridden to false here to allow test execution for clouds using the
     # pre-provisioned credentials provider.
@@ -35,6 +37,7 @@
 
     @decorators.idempotent_id('2e80343b-6c81-4ac3-88c7-452f3e9d5129')
     def test_group_create_update_get(self):
+        """Test creating, updating and getting keystone group"""
         # Verify group creation works.
         name = data_utils.rand_name('Group')
         description = data_utils.rand_name('Description')
@@ -78,6 +81,7 @@
                       'immutable user source and solely '
                       'provides read-only access to users.')
     def test_group_users_add_list_delete(self):
+        """Test adding/listing/deleting group users"""
         group = self.setup_test_group(domain_id=self.domain['id'])
         # add user into group
         users = []
@@ -104,6 +108,7 @@
                       'immutable user source and solely '
                       'provides read-only access to users.')
     def test_list_user_groups(self):
+        """Test listing user groups when the user is in two groups"""
         # create a user
         user = self.create_test_user()
         # create two groups, and add user into them
@@ -127,7 +132,7 @@
 
     @decorators.idempotent_id('cc9a57a5-a9ed-4f2d-a29f-4f979a06ec71')
     def test_list_groups(self):
-        # Test to list groups
+        """Test listing groups"""
         group_ids = list()
         fetched_ids = list()
         for _ in range(3):
diff --git a/tempest/api/identity/admin/v3/test_inherits.py b/tempest/api/identity/admin/v3/test_inherits.py
index 2672f71..cababc6 100644
--- a/tempest/api/identity/admin/v3/test_inherits.py
+++ b/tempest/api/identity/admin/v3/test_inherits.py
@@ -21,6 +21,8 @@
 
 
 class InheritsV3TestJSON(base.BaseIdentityV3AdminTest):
+    """Test keystone inherits"""
+
     # NOTE: force_tenant_isolation is true in the base class by default but
     # overridden to false here to allow test execution for clouds using the
     # pre-provisioned credentials provider.
@@ -72,6 +74,7 @@
                       'Skipped because environment has an immutable user '
                       'source and solely provides read-only access to users.')
     def test_inherit_assign_list_check_revoke_roles_on_domains_user(self):
+        """Test assign/list/check/revoke inherited role on domain user"""
         # Create role
         src_role = self.setup_test_role()
         # Assign role on domains user
@@ -96,6 +99,7 @@
 
     @decorators.idempotent_id('c7a8dda2-be50-4fb4-9a9c-e830771078b1')
     def test_inherit_assign_list_check_revoke_roles_on_domains_group(self):
+        """Test assign/list/check/revoke inherited role on domain group"""
         # Create role
         src_role = self.setup_test_role()
         # Assign role on domains group
@@ -123,6 +127,7 @@
                       'Skipped because environment has an immutable user '
                       'source and solely provides read-only access to users.')
     def test_inherit_assign_check_revoke_roles_on_projects_user(self):
+        """Test assign/list/check/revoke inherited role on project user"""
         # Create role
         src_role = self.setup_test_role()
         # Assign role on projects user
@@ -138,6 +143,7 @@
 
     @decorators.idempotent_id('26021436-d5a4-4256-943c-ded01e0d4b45')
     def test_inherit_assign_check_revoke_roles_on_projects_group(self):
+        """Test assign/list/check/revoke inherited role on project group"""
         # Create role
         src_role = self.setup_test_role()
         # Assign role on projects group
@@ -157,6 +163,7 @@
                       'Skipped because environment has an immutable user '
                       'source and solely provides read-only access to users.')
     def test_inherit_assign_list_revoke_user_roles_on_domain(self):
+        """Test assign/list/check/revoke inherited role on domain"""
         # Create role
         src_role = self.setup_test_role()
 
@@ -204,6 +211,7 @@
                       'Skipped because environment has an immutable user '
                       'source and solely provides read-only access to users.')
     def test_inherit_assign_list_revoke_user_roles_on_project_tree(self):
+        """Test assign/list/check/revoke inherited role on project tree"""
         # Create role
         src_role = self.setup_test_role()
 
diff --git a/tempest/api/identity/admin/v3/test_trusts.py b/tempest/api/identity/admin/v3/test_trusts.py
index 78e3cce..580e304 100644
--- a/tempest/api/identity/admin/v3/test_trusts.py
+++ b/tempest/api/identity/admin/v3/test_trusts.py
@@ -27,6 +27,7 @@
 
 
 class TrustsV3TestJSON(base.BaseIdentityV3AdminTest):
+    """Test keystone trusts"""
 
     @classmethod
     def skip_checks(cls):
@@ -195,8 +196,11 @@
 
     @decorators.idempotent_id('5a0a91a4-baef-4a14-baba-59bf4d7fcace')
     def test_trust_impersonate(self):
-        # Test case to check we can create, get and delete a trust
-        # updates are not supported for trusts
+        """Test keystone trust with impersonation enabled
+
+        To check we can create, get and delete a trust.
+        Updates are not supported for trusts
+        """
         trust = self.create_trust()
         self.validate_trust(trust)
 
@@ -207,8 +211,11 @@
 
     @decorators.idempotent_id('ed2a8779-a7ac-49dc-afd7-30f32f936ed2')
     def test_trust_noimpersonate(self):
-        # Test case to check we can create, get and delete a trust
-        # with impersonation=False
+        """Test keystone trust with impersonation disabled
+
+        To check we can create, get and delete a trust
+        with impersonation=False
+        """
         trust = self.create_trust(impersonate=False)
         self.validate_trust(trust, impersonate=False)
 
@@ -219,8 +226,11 @@
 
     @decorators.idempotent_id('0ed14b66-cefd-4b5c-a964-65759453e292')
     def test_trust_expire(self):
-        # Test case to check we can create, get and delete a trust
-        # with an expiry specified
+        """Test expire attribute of keystone trust
+
+        To check we can create, get and delete a trust
+        with an expiry specified
+        """
         expires_at = timeutils.utcnow() + datetime.timedelta(hours=1)
         # NOTE(ylobankov) In some cases the expiry time may be rounded up
         # because of microseconds. In fact, it depends on database and its
@@ -246,8 +256,10 @@
 
     @decorators.idempotent_id('3e48f95d-e660-4fa9-85e0-5a3d85594384')
     def test_trust_expire_invalid(self):
-        # Test case to check we can check an invalid expiry time
-        # is rejected with the correct error
+        """Test invalid expire attribute of a keystone trust
+
+        To check an invalid expiry time is rejected with the correct error
+        """
         # with an expiry specified
         expires_str = 'bad.123Z'
         self.assertRaises(lib_exc.BadRequest,
@@ -256,6 +268,7 @@
 
     @decorators.idempotent_id('6268b345-87ca-47c0-9ce3-37792b43403a')
     def test_get_trusts_query(self):
+        """Test getting keystone trusts"""
         self.create_trust()
         trusts_get = self.trustor_client.list_trusts(
             trustor_user_id=self.trustor_user_id)['trusts']
@@ -265,7 +278,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('4773ebd5-ecbf-4255-b8d8-b63e6f72b65d')
     def test_get_trusts_all(self):
-
+        """Test getting all keystone trusts"""
         # Simple function that can be used for cleanup
         def set_scope(auth_provider, scope):
             auth_provider.scope = scope
diff --git a/tempest/api/identity/admin/v3/test_users_negative.py b/tempest/api/identity/admin/v3/test_users_negative.py
index 11dcdb0..1cba945 100644
--- a/tempest/api/identity/admin/v3/test_users_negative.py
+++ b/tempest/api/identity/admin/v3/test_users_negative.py
@@ -23,11 +23,12 @@
 
 
 class UsersNegativeTest(base.BaseIdentityV3AdminTest):
+    """Negative tests of keystone users"""
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e75f006c-89cc-477b-874d-588e4eab4b17')
     def test_create_user_for_non_existent_domain(self):
-        # Attempt to create a user in a non-existent domain should fail
+        """Attempt to create a user in a non-existent domain should fail"""
         u_name = data_utils.rand_name('user')
         u_email = u_name + '@testmail.tm'
         u_password = data_utils.rand_password()
@@ -39,7 +40,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b3c9fccc-4134-46f5-b600-1da6fb0a3b1f')
     def test_authentication_for_disabled_user(self):
-        # Attempt to authenticate for disabled user should fail
+        """Attempt to authenticate for disabled user should fail"""
         password = data_utils.rand_password()
         user = self.setup_test_user(password)
         self.disable_user(user['name'], user['domain_id'])
diff --git a/tempest/api/identity/v3/test_tokens.py b/tempest/api/identity/v3/test_tokens.py
index fa1c47f..cb05f39 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import operator
+
 from oslo_utils import timeutils
 import six
 
@@ -40,6 +42,15 @@
         authenticated_token = self.non_admin_client.show_token(
             subject_token)['token']
         # sanity checking to make sure they are indeed the same token
+        # If there are roles in the token, sort the roles
+        authenticated_token_roles = authenticated_token.get("roles")
+        if authenticated_token_roles:
+            authenticated_token["roles"] = authenticated_token_roles.sort(
+                key=operator.itemgetter('id'))
+        token_body_roles = token_body.get("roles")
+        if token_body_roles:
+            token_body["roles"] = token_body_roles.sort(
+                key=operator.itemgetter('id'))
         self.assertEqual(authenticated_token, token_body)
         # test to see if token has been properly authenticated
         self.assertEqual(authenticated_token['user']['id'], user_id)
diff --git a/tempest/api/image/v2/test_images.py b/tempest/api/image/v2/test_images.py
index c4a3e0e..3e72b34 100644
--- a/tempest/api/image/v2/test_images.py
+++ b/tempest/api/image/v2/test_images.py
@@ -29,6 +29,64 @@
 LOG = logging.getLogger(__name__)
 
 
+class ImportImagesTest(base.BaseV2ImageTest):
+    """Here we test the import operations for image"""
+
+    @classmethod
+    def skip_checks(cls):
+        super(ImportImagesTest, cls).skip_checks()
+        if not CONF.image_feature_enabled.import_image:
+            skip_msg = (
+                "%s skipped as image import is not available" % cls.__name__)
+            raise cls.skipException(skip_msg)
+
+    @decorators.idempotent_id('32ca0c20-e16f-44ac-8590-07869c9b4cc2')
+    def test_image_import(self):
+        """Here we test these functionalities
+
+        Create image, stage image data, import image and verify
+        that import succeeded.
+        """
+
+        body = self.client.info_import()
+        if 'glance-direct' not in body['import-methods']['value']:
+            raise self.skipException('Server does not support '
+                                     'glance-direct import method')
+
+        # Create image
+        uuid = '00000000-1111-2222-3333-444455556666'
+        image_name = data_utils.rand_name('image')
+        container_format = CONF.image.container_formats[0]
+        disk_format = CONF.image.disk_formats[0]
+        image = self.create_image(name=image_name,
+                                  container_format=container_format,
+                                  disk_format=disk_format,
+                                  visibility='private',
+                                  ramdisk_id=uuid)
+        self.assertIn('name', image)
+        self.assertEqual(image_name, image['name'])
+        self.assertIn('visibility', image)
+        self.assertEqual('private', image['visibility'])
+        self.assertIn('status', image)
+        self.assertEqual('queued', image['status'])
+
+        # Stage image data
+        file_content = data_utils.random_bytes()
+        image_file = six.BytesIO(file_content)
+        self.client.stage_image_file(image['id'], image_file)
+
+        # Now try to get image details
+        body = self.client.show_image(image['id'])
+        self.assertEqual(image['id'], body['id'])
+        self.assertEqual(image_name, body['name'])
+        self.assertEqual(uuid, body['ramdisk_id'])
+        self.assertEqual('uploading', body['status'])
+
+        # import image from staging to backend
+        self.client.image_import(image['id'])
+        self.client.wait_for_resource_activation(image['id'])
+
+
 class BasicOperationsImagesTest(base.BaseV2ImageTest):
     """Here we test the basic operations of images"""
 
diff --git a/tempest/api/network/admin/test_floating_ips_admin_actions.py b/tempest/api/network/admin/test_floating_ips_admin_actions.py
index adc4dda..7d41019 100644
--- a/tempest/api/network/admin/test_floating_ips_admin_actions.py
+++ b/tempest/api/network/admin/test_floating_ips_admin_actions.py
@@ -93,7 +93,7 @@
         # Creates a floating IP
         body = self.admin_floating_ips_client.create_floatingip(
             floating_network_id=self.ext_net_id,
-            tenant_id=self.network['tenant_id'],
+            project_id=self.network['project_id'],
             port_id=self.port['id'])
         created_floating_ip = body['floatingip']
         self.addCleanup(
@@ -101,7 +101,7 @@
             self.floating_ips_client.delete_floatingip,
             created_floating_ip['id'])
         self.assertIsNotNone(created_floating_ip['id'])
-        self.assertIsNotNone(created_floating_ip['tenant_id'])
+        self.assertIsNotNone(created_floating_ip['project_id'])
         self.assertIsNotNone(created_floating_ip['floating_ip_address'])
         self.assertEqual(created_floating_ip['port_id'], self.port['id'])
         self.assertEqual(created_floating_ip['floating_network_id'],
@@ -116,8 +116,8 @@
         self.assertEqual(shown_floating_ip['id'], created_floating_ip['id'])
         self.assertEqual(shown_floating_ip['floating_network_id'],
                          self.ext_net_id)
-        self.assertEqual(shown_floating_ip['tenant_id'],
-                         self.network['tenant_id'])
+        self.assertEqual(shown_floating_ip['project_id'],
+                         self.network['project_id'])
         self.assertEqual(shown_floating_ip['floating_ip_address'],
                          created_floating_ip['floating_ip_address'])
         self.assertEqual(shown_floating_ip['port_id'], self.port['id'])
diff --git a/tempest/api/network/admin/test_metering_extensions.py b/tempest/api/network/admin/test_metering_extensions.py
index 9825af4..a60cd48 100644
--- a/tempest/api/network/admin/test_metering_extensions.py
+++ b/tempest/api/network/admin/test_metering_extensions.py
@@ -122,7 +122,7 @@
         metering_label = body['metering_label']
         self.assertEqual(self.metering_label['id'], metering_label['id'])
         self.assertEqual(self.metering_label['tenant_id'],
-                         metering_label['tenant_id'])
+                         metering_label['project_id'])
         self.assertEqual(self.metering_label['name'], metering_label['name'])
         self.assertEqual(self.metering_label['description'],
                          metering_label['description'])
diff --git a/tempest/api/network/admin/test_negative_quotas.py b/tempest/api/network/admin/test_negative_quotas.py
index 0db038d..3562d6f 100644
--- a/tempest/api/network/admin/test_negative_quotas.py
+++ b/tempest/api/network/admin/test_negative_quotas.py
@@ -58,12 +58,12 @@
 
         # Create two networks
         n1 = self.admin_networks_client.create_network(
-            tenant_id=self.project['id'])
+            project_id=self.project['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.admin_networks_client.delete_network,
                         n1['network']['id'])
         n2 = self.admin_networks_client.create_network(
-            tenant_id=self.project['id'])
+            project_id=self.project['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.admin_networks_client.delete_network,
                         n2['network']['id'])
@@ -73,7 +73,7 @@
                 lib_exc.Conflict,
                 r"Quota exceeded for resources: \['network'\].*"):
             n3 = self.admin_networks_client.create_network(
-                tenant_id=self.project['id'])
+                project_id=self.project['id'])
             self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                             self.admin_networks_client.delete_network,
                             n3['network']['id'])
diff --git a/tempest/api/network/admin/test_quotas.py b/tempest/api/network/admin/test_quotas.py
index ef5ebb6..8b7bfb1 100644
--- a/tempest/api/network/admin/test_quotas.py
+++ b/tempest/api/network/admin/test_quotas.py
@@ -67,7 +67,7 @@
         non_default_quotas = self.admin_quotas_client.list_quotas()
         found = False
         for qs in non_default_quotas['quotas']:
-            if qs['tenant_id'] == project_id:
+            if qs['project_id'] == project_id:
                 found = True
         self.assertTrue(found)
 
@@ -81,7 +81,7 @@
         self.admin_quotas_client.reset_quotas(project_id)
         non_default_quotas = self.admin_quotas_client.list_quotas()
         for q in non_default_quotas['quotas']:
-            self.assertNotEqual(project_id, q['tenant_id'])
+            self.assertNotEqual(project_id, q['project_id'])
         quota_set = self.admin_quotas_client.show_quotas(project_id)['quota']
         default_quotas = self.admin_quotas_client.show_default_quotas(
             project_id)['quota']
diff --git a/tempest/api/network/admin/test_routers.py b/tempest/api/network/admin/test_routers.py
index c11aad5..90e0917 100644
--- a/tempest/api/network/admin/test_routers.py
+++ b/tempest/api/network/admin/test_routers.py
@@ -65,11 +65,11 @@
 
         name = data_utils.rand_name('router-')
         create_body = self.admin_routers_client.create_router(
-            name=name, tenant_id=project_id)
+            name=name, project_id=project_id)
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.admin_routers_client.delete_router,
                         create_body['router']['id'])
-        self.assertEqual(project_id, create_body['router']['tenant_id'])
+        self.assertEqual(project_id, create_body['router']['project_id'])
 
     @decorators.idempotent_id('847257cc-6afd-4154-b8fb-af49f5670ce8')
     @utils.requires_ext(extension='ext-gw-mode', service='network')
diff --git a/tempest/api/network/admin/test_routers_dvr.py b/tempest/api/network/admin/test_routers_dvr.py
index 270f802..291581c 100644
--- a/tempest/api/network/admin/test_routers_dvr.py
+++ b/tempest/api/network/admin/test_routers_dvr.py
@@ -106,14 +106,14 @@
         attribute will be set to True
         """
         name = data_utils.rand_name('router')
-        tenant_id = self.routers_client.tenant_id
+        project_id = self.routers_client.project_id
         # router needs to be in admin state down in order to be upgraded to DVR
         # l3ha routers are not upgradable to dvr, make it explicitly non ha
         router = self.admin_routers_client.create_router(name=name,
                                                          distributed=False,
                                                          admin_state_up=False,
                                                          ha=False,
-                                                         tenant_id=tenant_id)
+                                                         project_id=project_id)
         router_id = router['router']['id']
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.admin_routers_client.delete_router, router_id)
diff --git a/tempest/api/network/test_floating_ips.py b/tempest/api/network/test_floating_ips.py
index aaa5497..abcbbf7 100644
--- a/tempest/api/network/test_floating_ips.py
+++ b/tempest/api/network/test_floating_ips.py
@@ -83,7 +83,7 @@
             self.floating_ips_client.delete_floatingip,
             created_floating_ip['id'])
         self.assertIsNotNone(created_floating_ip['id'])
-        self.assertIsNotNone(created_floating_ip['tenant_id'])
+        self.assertIsNotNone(created_floating_ip['project_id'])
         self.assertIsNotNone(created_floating_ip['floating_ip_address'])
         self.assertEqual(created_floating_ip['port_id'], self.ports[0]['id'])
         self.assertEqual(created_floating_ip['floating_network_id'],
@@ -97,8 +97,8 @@
         self.assertEqual(shown_floating_ip['id'], created_floating_ip['id'])
         self.assertEqual(shown_floating_ip['floating_network_id'],
                          self.ext_net_id)
-        self.assertEqual(shown_floating_ip['tenant_id'],
-                         created_floating_ip['tenant_id'])
+        self.assertEqual(shown_floating_ip['project_id'],
+                         created_floating_ip['project_id'])
         self.assertEqual(shown_floating_ip['floating_ip_address'],
                          created_floating_ip['floating_ip_address'])
         self.assertEqual(shown_floating_ip['port_id'], self.ports[0]['id'])
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index 10121de..c4ad720 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -192,9 +192,9 @@
         port_list = self.ports_client.list_ports(fixed_ips=fixed_ips)
         # Check that we got the desired port
         ports = port_list['ports']
-        tenant_ids = set([port['tenant_id'] for port in ports])
-        self.assertEqual(len(tenant_ids), 1,
-                         'Ports from multiple tenants are in the list resp')
+        project_ids = set([port['project_id'] for port in ports])
+        self.assertEqual(len(project_ids), 1,
+                         'Ports from multiple projects are in the list resp')
         port_ids = [port['id'] for port in ports]
         fixed_ips = [port['fixed_ips'] for port in ports]
         port_net_ids = [port['network_id'] for port in ports]
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
index 6599e43..80f790f 100644
--- a/tempest/api/object_storage/test_account_bulk.py
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -16,7 +16,6 @@
 import tempfile
 
 from tempest.api.object_storage import base
-from tempest.common import custom_matchers
 from tempest.common import utils
 from tempest.lib import decorators
 
@@ -76,17 +75,7 @@
         resp = self._upload_archive(filepath)
         self.containers.append(container_name)
 
-        # When uploading an archived file with the bulk operation, the response
-        # does not contain 'content-length' header. This is the special case,
-        # therefore the existence of response headers is checked without
-        # custom matcher.
-        self.assertIn('transfer-encoding', resp.response)
-        self.assertIn('content-type', resp.response)
-        self.assertIn('x-trans-id', resp.response)
-        self.assertIn('date', resp.response)
-
-        # Check only the format of common headers with custom matcher
-        self.assertThat(resp.response, custom_matchers.AreAllWellFormatted())
+        self.assertHeaders(resp.response, 'Account', 'PUT')
 
         param = {'format': 'json'}
         resp, body = self.account_client.list_account_containers(param)
@@ -113,17 +102,7 @@
         data = '%s/%s\n%s' % (container_name, object_name, container_name)
         resp = self.bulk_client.delete_bulk_data(data=data)
 
-        # When deleting multiple files using the bulk operation, the response
-        # does not contain 'content-length' header. This is the special case,
-        # therefore the existence of response headers is checked without
-        # custom matcher.
-        self.assertIn('transfer-encoding', resp.response)
-        self.assertIn('content-type', resp.response)
-        self.assertIn('x-trans-id', resp.response)
-        self.assertIn('date', resp.response)
-
-        # Check only the format of common headers with custom matcher
-        self.assertThat(resp.response, custom_matchers.AreAllWellFormatted())
+        self.assertHeaders(resp.response, 'Account', 'DELETE')
 
         # Check if uploaded contents are completely deleted
         self._check_contents_deleted(container_name)
@@ -139,17 +118,7 @@
 
         resp = self.bulk_client.delete_bulk_data_with_post(data=data)
 
-        # When deleting multiple files using the bulk operation, the response
-        # does not contain 'content-length' header. This is the special case,
-        # therefore the existence of response headers is checked without
-        # custom matcher.
-        self.assertIn('transfer-encoding', resp.response)
-        self.assertIn('content-type', resp.response)
-        self.assertIn('x-trans-id', resp.response)
-        self.assertIn('date', resp.response)
-
-        # Check only the format of common headers with custom matcher
-        self.assertThat(resp.response, custom_matchers.AreAllWellFormatted())
+        self.assertHeaders(resp.response, 'Account', 'POST')
 
         # Check if uploaded contents are completely deleted
         self._check_contents_deleted(container_name)
diff --git a/tempest/api/object_storage/test_container_sync.py b/tempest/api/object_storage/test_container_sync.py
index 322579c..bdcb5ae 100644
--- a/tempest/api/object_storage/test_container_sync.py
+++ b/tempest/api/object_storage/test_container_sync.py
@@ -123,7 +123,7 @@
                 self.assertEqual(object_content, obj_name[::-1].encode())
 
     @decorators.attr(type='slow')
-    @decorators.skip_because(bug='1317133')
+    @decorators.unstable_test(bug='1317133')
     @decorators.idempotent_id('be008325-1bba-4925-b7dd-93b58f22ce9b')
     @testtools.skipIf(
         not CONF.object_storage_feature_enabled.container_sync,
diff --git a/tempest/api/object_storage/test_object_slo.py b/tempest/api/object_storage/test_object_slo.py
index c66776e..8bb2e6e 100644
--- a/tempest/api/object_storage/test_object_slo.py
+++ b/tempest/api/object_storage/test_object_slo.py
@@ -17,7 +17,6 @@
 from oslo_serialization import jsonutils as json
 
 from tempest.api.object_storage import base
-from tempest.common import custom_matchers
 from tempest.common import utils
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
@@ -160,17 +159,7 @@
             object_name,
             params=params_del)
 
-        # When deleting SLO using multipart manifest, the response contains
-        # not 'content-length' but 'transfer-encoding' header. This is the
-        # special case, therefore the existence of response headers is checked
-        # outside of custom matcher.
-        self.assertIn('transfer-encoding', resp)
-        self.assertIn('content-type', resp)
-        self.assertIn('x-trans-id', resp)
-        self.assertIn('date', resp)
-
-        # Check only the format of common headers with custom matcher
-        self.assertThat(resp, custom_matchers.AreAllWellFormatted())
+        self.assertHeaders(resp, 'Object', 'DELETE')
 
         resp, body = self.container_client.list_container_objects(
             self.container_name)
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index bcbcf43..7af5927 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -233,12 +233,15 @@
         return group
 
     def delete_group(self, group_id, delete_volumes=True):
-        self.groups_client.delete_group(group_id, delete_volumes)
+        group_vols = []
         if delete_volumes:
             vols = self.volumes_client.list_volumes(detail=True)['volumes']
             for vol in vols:
                 if vol['group_id'] == group_id:
-                    self.volumes_client.wait_for_resource_deletion(vol['id'])
+                    group_vols.append(vol['id'])
+        self.groups_client.delete_group(group_id, delete_volumes)
+        for vol in group_vols:
+            self.volumes_client.wait_for_resource_deletion(vol)
         self.groups_client.wait_for_resource_deletion(group_id)
 
 
diff --git a/tempest/cmd/account_generator.py b/tempest/cmd/account_generator.py
index b230615..ff552a1 100755
--- a/tempest/cmd/account_generator.py
+++ b/tempest/cmd/account_generator.py
@@ -97,6 +97,7 @@
 [OPTIONS] <accounts_file.yaml> -h``.
 """
 
+import argparse
 import os
 import traceback
 
@@ -199,6 +200,14 @@
     LOG.info('%s generated successfully!', account_file)
 
 
+def positive_int(number):
+    number = int(number)
+    if number <= 0:
+        raise argparse.ArgumentTypeError("Concurrency value should be a "
+                                         "positive number")
+    return number
+
+
 def _parser_add_args(parser):
     parser.add_argument('-c', '--config-file',
                         metavar='/etc/tempest.conf',
@@ -228,7 +237,7 @@
                         help='Resources tag')
     parser.add_argument('-r', '--concurrency',
                         default=1,
-                        type=int,
+                        type=positive_int,
                         required=False,
                         dest='concurrency',
                         help='Concurrency count')
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 8d5bdbd..235d8e3 100644
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -60,16 +60,16 @@
 """
 
 import argparse
+import configparser
 import os
 import re
 import sys
 import traceback
+from urllib import parse as urlparse
 
 from cliff import command
 from oslo_log import log as logging
 from oslo_serialization import jsonutils as json
-from six import moves
-from six.moves.urllib import parse as urlparse
 
 from tempest import clients
 from tempest.common import credentials_factory as credentials
@@ -439,9 +439,9 @@
 
     if update:
         conf_file = _get_config_file()
-        CONF_PARSER = moves.configparser.ConfigParser()
+        CONF_PARSER = configparser.ConfigParser()
         CONF_PARSER.optionxform = str
-        CONF_PARSER.readfp(conf_file)
+        CONF_PARSER.read_file(conf_file)
 
     # Indicate not to create network resources as part of getting credentials
     net_resources = {
diff --git a/tempest/common/custom_matchers.py b/tempest/common/custom_matchers.py
index c702d88..b0bf5b2 100644
--- a/tempest/common/custom_matchers.py
+++ b/tempest/common/custom_matchers.py
@@ -62,8 +62,9 @@
         # [1] https://bugs.launchpad.net/swift/+bug/1537811
         # [2] http://tracker.ceph.com/issues/13582
         if ('content-length' not in actual and
+                'transfer-encoding' not in actual and
                 self._content_length_required(actual)):
-            return NonExistentHeader('content-length')
+            return NonExistentHeaders(['content-length', 'transfer-encoding'])
         if 'content-type' not in actual:
             return NonExistentHeader('content-type')
         if 'x-trans-id' not in actual:
@@ -75,8 +76,6 @@
         if self.method == 'GET' or self.method == 'HEAD':
             if 'x-timestamp' not in actual:
                 return NonExistentHeader('x-timestamp')
-            if 'accept-ranges' not in actual:
-                return NonExistentHeader('accept-ranges')
             if self.target == 'Account':
                 if 'x-account-bytes-used' not in actual:
                     return NonExistentHeader('x-account-bytes-used')
@@ -192,6 +191,19 @@
         return {}
 
 
+class NonExistentHeaders(object):
+    """Informs an error message in the case of missing certain headers"""
+
+    def __init__(self, headers):
+        self.headers = headers
+
+    def describe(self):
+        return "none of these headers exist: %s" % self.headers
+
+    def get_details(self):
+        return {}
+
+
 class InvalidHeaderValue(object):
     """Informs an error message when a header contains a bad value"""
 
diff --git a/tempest/config.py b/tempest/config.py
index 204d977..eca2023 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -13,8 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from __future__ import print_function
-
 import os
 import tempfile
 
@@ -658,6 +656,12 @@
                                   'are current one. In future, Tempest will '
                                   'test v2 APIs only so this config option '
                                   'will be removed.'),
+    # Image import feature is setup in devstack victoria onwards.
+    # Once all stable branches setup the same via glance standalone
+    # mode or with uwsgi, we can remove this config option.
+    cfg.BoolOpt('import_image',
+                default=False,
+                help="Is image import feature enabled"),
 ]
 
 network_group = cfg.OptGroup(name='network',
@@ -1021,7 +1025,7 @@
                help="Number of seconds to wait while looping to check the "
                     "status of a container to container synchronization"),
     cfg.StrOpt('operator_role',
-               default='Member',
+               default='member',
                help="Role to add to users created for swift tests to "
                     "enable creating containers"),
     cfg.StrOpt('reseller_admin_role',
@@ -1068,11 +1072,13 @@
     cfg.StrOpt('img_dir',
                default='/opt/stack/new/devstack/files/images/'
                'cirros-0.3.1-x86_64-uec',
-               help='Directory containing image files',
+               help='Directory containing image files, this has been '
+                    'deprecated - img_file option contains a full path now.',
                deprecated_for_removal=True),
     cfg.StrOpt('img_file', deprecated_name='qcow2_img_file',
-               default='cirros-0.3.1-x86_64-disk.img',
-               help='Image file name'),
+               default='/opt/stack/new/devstack/files/images'
+               '/cirros-0.3.1-x86_64-disk.img',
+               help='Image full path.'),
     cfg.StrOpt('img_disk_format',
                default='qcow2',
                help='Image disk format'),
@@ -1081,18 +1087,6 @@
                help='Image container format'),
     cfg.DictOpt('img_properties', help='Glance image properties. '
                 'Use for custom images which require them'),
-    cfg.StrOpt('ami_img_file',
-               default='cirros-0.3.1-x86_64-blank.img',
-               help='AMI image file name',
-               deprecated_for_removal=True),
-    cfg.StrOpt('ari_img_file',
-               default='cirros-0.3.1-x86_64-initrd',
-               help='ARI image file name',
-               deprecated_for_removal=True),
-    cfg.StrOpt('aki_img_file',
-               default='cirros-0.3.1-x86_64-vmlinuz',
-               help='AKI image file name',
-               deprecated_for_removal=True),
     # TODO(yfried): add support for dhcpcd
     cfg.StrOpt('dhcp_client',
                default='udhcpc',
diff --git a/tempest/lib/api_schema/response/volume/group_types.py b/tempest/lib/api_schema/response/volume/group_types.py
index bcfa32e..4fc9ae8 100644
--- a/tempest/lib/api_schema/response/volume/group_types.py
+++ b/tempest/lib/api_schema/response/volume/group_types.py
@@ -73,6 +73,18 @@
     }
 }
 
+show_default_group_type = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'group_type': common_show_group_type
+        },
+        'additionalProperties': False,
+        'required': ['group_type']
+    }
+}
+
 update_group_type = {
     'status_code': [200],
     'response_body': {
diff --git a/tempest/lib/api_schema/response/volume/scheduler_stats.py b/tempest/lib/api_schema/response/volume/scheduler_stats.py
new file mode 100644
index 0000000..b5d7d2c
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/scheduler_stats.py
@@ -0,0 +1,79 @@
+# Copyright 2018 ZTE 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.
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+get_pools_no_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'pools': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'name': {'type': 'string'},
+                    },
+                    'additionalProperties': False,
+                    'required': ['name']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['pools'],
+    }
+}
+
+get_pools_with_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'pools': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'name': {'type': 'string'},
+                        'capabilities': {
+                            'type': ['object', 'null'],
+                            'properties': {
+                                'updated': parameter_types.date_time_or_null,
+                                'QoS_support': {'type': 'boolean'},
+                                'total_capacity_gb': {
+                                    'type': ['number', 'string']
+                                },
+                                'volume_backend_name': {'type': 'string'},
+                                'free_capacity_gb': {
+                                    'type': ['number', 'string']
+                                },
+                                'driver_version': {'type': 'string'},
+                                'reserved_percentage': {'type': 'integer'},
+                                'storage_protocol': {'type': 'string'},
+                                'vendor_name': {'type': 'string'},
+                                'timestamp': parameter_types.date_time_or_null
+                            },
+                            # Because some legacy volumes or backends may not
+                            # support pools, so no required fields here.
+                        },
+                    },
+                    'additionalProperties': False,
+                    'required': ['name', 'capabilities']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['pools'],
+    }
+}
diff --git a/tempest/lib/api_schema/response/volume/snapshots.py b/tempest/lib/api_schema/response/volume/snapshots.py
new file mode 100644
index 0000000..9d52801
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/snapshots.py
@@ -0,0 +1,198 @@
+# Copyright 2018 ZTE 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 copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+metadata = {
+    'type': 'object',
+    'patternProperties': {
+        '^.+$': {'type': 'string'}
+    }
+}
+
+common_snapshot_schema = {
+    'type': 'object',
+    'properties': {
+        'status': {'type': 'string'},
+        'description': {'type': ['string', 'null']},
+        'created_at': parameter_types.date_time,
+        'name': {'type': ['string', 'null']},
+        'volume_id': {'type': 'string', 'format': 'uuid'},
+        'metadata': metadata,
+        'id': {'type': 'string', 'format': 'uuid'},
+        'size': {'type': 'integer'},
+        'updated_at': parameter_types.date_time_or_null,
+        # TODO(zhufl): user_id is added in 3.41, we should move it
+        # to the 3.41 schema file when microversion is supported
+        # in volume interfaces
+        # 'user_id': {'type': 'string', 'format': 'uuid'}
+    },
+    'additionalProperties': False,
+    'required': ['status', 'description', 'created_at', 'metadata',
+                 'name', 'volume_id', 'id', 'size', 'updated_at']
+}
+
+common_snapshot_detail_schema = copy.deepcopy(common_snapshot_schema)
+common_snapshot_detail_schema['properties'].update(
+    {'os-extended-snapshot-attributes:progress': {'type': 'string'},
+     'os-extended-snapshot-attributes:project_id': {
+         'type': 'string', 'format': 'uuid'}})
+common_snapshot_detail_schema['required'].extend(
+    ['os-extended-snapshot-attributes:progress',
+     'os-extended-snapshot-attributes:project_id'])
+
+list_snapshots_no_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'snapshots': {
+                'type': 'array',
+                'items': common_snapshot_schema
+            },
+            'snapshots_links': parameter_types.links,
+            # TODO(zhufl): count is added in 3.45, we should move
+            # it to the 3.45 schema file when microversion is
+            # supported in volume interfaces
+            # 'count': {'type': 'integer'}
+        },
+        'additionalProperties': False,
+        'required': ['snapshots'],
+    }
+}
+
+list_snapshots_with_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'snapshots': {
+                'type': 'array',
+                'items': common_snapshot_detail_schema
+            },
+            'snapshots_links': parameter_types.links,
+            # TODO(zhufl): count is added in 3.45, we should move
+            # it to the 3.45 schema file when microversion is
+            # supported in volume interfaces
+            # 'count': {'type': 'integer'},
+        },
+        'additionalProperties': False,
+        'required': ['snapshots'],
+    }
+}
+
+show_snapshot = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'snapshot': common_snapshot_detail_schema
+        },
+        'additionalProperties': False,
+        'required': ['snapshot'],
+    }
+}
+
+create_snapshot = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'snapshot': common_snapshot_schema
+        },
+        'additionalProperties': False,
+        'required': ['snapshot'],
+    }
+}
+
+update_snapshot = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'snapshot': common_snapshot_schema
+        },
+        'additionalProperties': False,
+        'required': ['snapshot'],
+    }
+}
+
+delete_snapshot = {'status_code': [202]}
+reset_snapshot_status = {'status_code': [202]}
+update_snapshot_status = {'status_code': [202]}
+
+create_snapshot_metadata = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'metadata': metadata
+        },
+        'additionalProperties': False,
+        'required': ['metadata'],
+    }
+}
+
+show_snapshot_metadata = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'metadata': metadata
+        },
+        'additionalProperties': False,
+        'required': ['metadata'],
+    }
+}
+
+update_snapshot_metadata = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'metadata': metadata
+        },
+        'additionalProperties': False,
+        'required': ['metadata'],
+    }
+}
+
+show_snapshot_metadata_item = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'meta': metadata
+        },
+        'additionalProperties': False,
+        'required': ['meta'],
+    }
+}
+
+update_snapshot_metadata_item = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'meta': metadata
+        },
+        'additionalProperties': False,
+        'required': ['meta'],
+    }
+}
+
+delete_snapshot_metadata_item = {'status_code': [200]}
+force_delete_snapshot = {'status_code': [202]}
+unmanage_snapshot = {'status_code': [202]}
diff --git a/tempest/lib/common/dynamic_creds.py b/tempest/lib/common/dynamic_creds.py
index f27e926..8b82391 100644
--- a/tempest/lib/common/dynamic_creds.py
+++ b/tempest/lib/common/dynamic_creds.py
@@ -207,10 +207,10 @@
         # our newly created user has a role on the newly created project.
         if self.identity_version == 'v3' and not role_assigned:
             try:
-                self.creds_client.create_user_role('Member')
+                self.creds_client.create_user_role('member')
             except lib_exc.Conflict:
-                LOG.warning('Member role already exists, ignoring conflict.')
-            self.creds_client.assign_user_role(user, project, 'Member')
+                LOG.warning('member role already exists, ignoring conflict.')
+            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/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 431a0a0..0513e90 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -21,6 +21,7 @@
 
 import jsonschema
 from oslo_log import log as logging
+from oslo_log import versionutils
 from oslo_serialization import jsonutils as json
 import six
 from six.moves import urllib
@@ -177,13 +178,27 @@
         return self.auth_provider.credentials.tenant_name
 
     @property
+    def project_id(self):
+        """The project id being used for requests
+
+        :rtype: string
+        :return: The project id being used for requests
+        """
+        return self.auth_provider.credentials.tenant_id
+
+    @property
     def tenant_id(self):
         """The tenant/project id being used for requests
 
         :rtype: string
         :return: The tenant/project id being used for requests
         """
-        return self.auth_provider.credentials.tenant_id
+        # NOTE(ralonsoh): this property should be deprecated, reference
+        # blueprint adopt-oslo-versioned-objects-for-db.
+        versionutils.report_deprecated_feature(
+            self.LOG, '"tenant_id" property is deprecated for removal, use '
+                      '"project_id" instead')
+        return self.project_id
 
     @property
     def password(self):
@@ -899,12 +914,44 @@
                 raise exceptions.TimeoutException(message)
             time.sleep(self.build_interval)
 
+    def wait_for_resource_activation(self, id):
+        """Waits for a resource to become active
+
+        This method will loop over is_resource_active until either
+        is_resource_active returns True or the build timeout is reached. This
+        depends on is_resource_active being implemented
+
+        :param str id: The id of the resource to check
+        :raises TimeoutException: If the build_timeout has elapsed and the
+                                  resource still hasn't been active
+        """
+        start_time = int(time.time())
+        while True:
+            if self.is_resource_active(id):
+                return
+            if int(time.time()) - start_time >= self.build_timeout:
+                message = ('Failed to reach active state %(resource_type)s '
+                           '%(id)s within the required time (%(timeout)s s).' %
+                           {'resource_type': self.resource_type, 'id': id,
+                            'timeout': self.build_timeout})
+                caller = test_utils.find_test_caller()
+                if caller:
+                    message = '(%s) %s' % (caller, message)
+                raise exceptions.TimeoutException(message)
+            time.sleep(self.build_interval)
+
     def is_resource_deleted(self, id):
         """Subclasses override with specific deletion detection."""
         message = ('"%s" does not implement is_resource_deleted'
                    % self.__class__.__name__)
         raise NotImplementedError(message)
 
+    def is_resource_active(self, id):
+        """Subclasses override with specific active detection."""
+        message = ('"%s" does not implement is_resource_active'
+                   % self.__class__.__name__)
+        raise NotImplementedError(message)
+
     @property
     def resource_type(self):
         """Returns the primary type of resource this client works with."""
diff --git a/tempest/lib/common/thread.py b/tempest/lib/common/thread.py
index 510fc36..b47d40d 100644
--- a/tempest/lib/common/thread.py
+++ b/tempest/lib/common/thread.py
@@ -13,10 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-# This make disable relative module import
-from __future__ import absolute_import
-
-
 import six
 
 if six.PY2:
diff --git a/tempest/lib/services/compute/servers_client.py b/tempest/lib/services/compute/servers_client.py
index cbf7a8c..6723516 100644
--- a/tempest/lib/services/compute/servers_client.py
+++ b/tempest/lib/services/compute/servers_client.py
@@ -206,11 +206,17 @@
     def action(self, server_id, action_name,
                schema=schema.server_actions_common_schema,
                **kwargs):
-        post_body = json.dumps({action_name: kwargs})
+        if 'body' in kwargs:
+            post_body = json.dumps(kwargs['body'])
+        else:
+            post_body = json.dumps({action_name: kwargs})
         resp, body = self.post('servers/%s/action' % server_id,
                                post_body)
         if body:
             body = json.loads(body)
+        else:
+            if isinstance(body, bytes):
+                body = body.decode('utf-8')
         self.validate_response(schema, resp, body)
         return rest_client.ResponseBody(resp, body)
 
@@ -605,6 +611,15 @@
         API reference:
         https://docs.openstack.org/api-ref/compute/#unshelve-restore-shelved-server-unshelve-action
         """
+        # NOTE(gmann): pass None as request body if nothing is requested.
+        # Nova started the request body check since 2.77 microversion and only
+        # accept AZ or None as valid req body and reject the empty dict {}.
+        # Before 2.77 microverison anything is valid body as Nova does not
+        # check the request body but as per api-ref None is valid request
+        # body to pass so we do not need to check the requested microversion
+        # here and always default req body to None.
+        if not kwargs:
+            kwargs['body'] = {'unshelve': None}
         return self.action(server_id, 'unshelve', **kwargs)
 
     def shelve_offload_server(self, server_id, **kwargs):
diff --git a/tempest/lib/services/image/v2/images_client.py b/tempest/lib/services/image/v2/images_client.py
index 90778da..b9c5776 100644
--- a/tempest/lib/services/image/v2/images_client.py
+++ b/tempest/lib/services/image/v2/images_client.py
@@ -128,6 +128,15 @@
             return True
         return False
 
+    def is_resource_active(self, id):
+        try:
+            image = self.show_image(id)
+            if image['status'] != 'active':
+                return False
+        except lib_exc.NotFound:
+            return False
+        return True
+
     @property
     def resource_type(self):
         """Returns the primary type of resource this client works with."""
@@ -152,6 +161,80 @@
         self.expected_success(204, resp.status)
         return rest_client.ResponseBody(resp, body)
 
+    def stage_image_file(self, image_id, data):
+        """Upload binary image data to staging area.
+
+        For a full list of available parameters, please refer to the official
+        API reference (stage API:
+        https://docs.openstack.org/api-ref/image/v2/#interoperable-image-import
+        """
+        url = 'images/%s/stage' % image_id
+
+        # We are going to do chunked transfer, so split the input data
+        # info fixed-sized chunks.
+        headers = {'Content-Type': 'application/octet-stream'}
+        data = iter(functools.partial(data.read, CHUNKSIZE), b'')
+
+        resp, body = self.request('PUT', url, headers=headers,
+                                  body=data, chunked=True)
+        self.expected_success(204, resp.status)
+        return rest_client.ResponseBody(resp, body)
+
+    def info_import(self):
+        """Return information about server-supported import methods."""
+        url = 'info/import'
+        resp, body = self.get(url)
+
+        self.expected_success(200, resp.status)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def info_stores(self):
+        """Return information about server-supported stores."""
+        url = 'info/stores'
+        resp, body = self.get(url)
+        body = json.loads(body)
+        return rest_client.ResponseBody(resp, body)
+
+    def image_import(self, image_id, method='glance-direct',
+                     all_stores_must_succeed=None, all_stores=True,
+                     stores=None):
+        """Import data from staging area to glance store.
+
+        For a full list of available parameters, please refer to the official
+        API reference (stage API:
+        https://docs.openstack.org/api-ref/image/v2/#interoperable-image-import
+
+        :param method: The import method (i.e. glance-direct) to use
+        :param all_stores_must_succeed: Boolean indicating if all store imports
+                                        must succeed for the import to be
+                                        considered successful. Must be None if
+                                        server does not support multistore.
+        :param all_stores: Boolean indicating if image should be imported to
+                           all available stores (incompatible with stores)
+        :param stores: A list of destination store names for the import. Must
+                       be None if server does not support multistore.
+        """
+        url = 'images/%s/import' % image_id
+        data = {
+            "method": {
+                "name": method
+            },
+        }
+        if stores is not None:
+            data["stores"] = stores
+        else:
+            data["all_stores"] = all_stores
+
+        if all_stores_must_succeed is not None:
+            data['all_stores_must_succeed'] = all_stores_must_succeed
+        data = json.dumps(data)
+        headers = {'Content-Type': 'application/json'}
+        resp, _ = self.post(url, data, headers=headers)
+
+        self.expected_success(202, resp.status)
+        return rest_client.ResponseBody(resp)
+
     def show_image_file(self, image_id):
         """Download binary image data.
 
diff --git a/tempest/lib/services/network/quotas_client.py b/tempest/lib/services/network/quotas_client.py
index 997d201..96cc65d 100644
--- a/tempest/lib/services/network/quotas_client.py
+++ b/tempest/lib/services/network/quotas_client.py
@@ -12,11 +12,33 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import functools
+
 from tempest.lib.services.network import base
 
 
+def _warning_deprecate_tenant_id(func):
+    @functools.wraps(func)
+    def inner(*args, **kwargs):
+        _self = args[0]
+        # check length of arg to know whether 'tenant_id' is passed as
+        # positional arg or kwargs.
+        if len(args) < 2:
+            if 'tenant_id' in kwargs:
+                _self.LOG.warning(
+                    'positional arg name "tenant_id" is deprecated, for '
+                    'removal, please start using "project_id" instead')
+            elif 'project_id' in kwargs:
+                # fallback to deprecated name till deprecation phase.
+                kwargs['tenant_id'] = kwargs.pop('project_id')
+
+        return func(*args, **kwargs)
+    return inner
+
+
 class QuotasClient(base.BaseNetworkClient):
 
+    @_warning_deprecate_tenant_id
     def update_quotas(self, tenant_id, **kwargs):
         """Update quota for a project.
 
@@ -28,12 +50,14 @@
         uri = '/quotas/%s' % tenant_id
         return self.update_resource(uri, put_body)
 
+    @_warning_deprecate_tenant_id
     def reset_quotas(self, tenant_id):  # noqa
         # NOTE: This noqa is for passing T111 check and we cannot rename
         #       to keep backwards compatibility.
         uri = '/quotas/%s' % tenant_id
         return self.delete_resource(uri)
 
+    @_warning_deprecate_tenant_id
     def show_quotas(self, tenant_id, **fields):
         """Show quota for a project.
 
@@ -54,11 +78,13 @@
         uri = '/quotas'
         return self.list_resources(uri, **filters)
 
+    @_warning_deprecate_tenant_id
     def show_default_quotas(self, tenant_id):
         """List default quotas for a project."""
         uri = '/quotas/%s/default' % tenant_id
         return self.show_resource(uri)
 
+    @_warning_deprecate_tenant_id
     def show_quota_details(self, tenant_id):
         """Show quota details for a project."""
         uri = '/quotas/%s/details.json' % tenant_id
diff --git a/tempest/lib/services/volume/v3/group_types_client.py b/tempest/lib/services/volume/v3/group_types_client.py
index e0bf5e2..1dcd508 100644
--- a/tempest/lib/services/volume/v3/group_types_client.py
+++ b/tempest/lib/services/volume/v3/group_types_client.py
@@ -73,7 +73,7 @@
         url = 'group_types/default'
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_default_group_type, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_group_type(self, group_type_id):
diff --git a/tempest/lib/services/volume/v3/scheduler_stats_client.py b/tempest/lib/services/volume/v3/scheduler_stats_client.py
index 2ae8600..e18980d 100644
--- a/tempest/lib/services/volume/v3/scheduler_stats_client.py
+++ b/tempest/lib/services/volume/v3/scheduler_stats_client.py
@@ -15,6 +15,7 @@
 
 from oslo_serialization import jsonutils as json
 
+from tempest.lib.api_schema.response.volume import scheduler_stats as schema
 from tempest.lib.common import rest_client
 
 
@@ -28,9 +29,11 @@
         https://docs.openstack.org/api-ref/block-storage/v3/index.html#list-all-back-end-storage-pools
         """
         url = 'scheduler-stats/get_pools'
+        schema_get_pools = schema.get_pools_no_detail
         if detail:
             url += '?detail=True'
+            schema_get_pools = schema.get_pools_with_detail
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema_get_pools, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/snapshots_client.py b/tempest/lib/services/volume/v3/snapshots_client.py
index 264381d..8ca2044 100644
--- a/tempest/lib/services/volume/v3/snapshots_client.py
+++ b/tempest/lib/services/volume/v3/snapshots_client.py
@@ -16,6 +16,7 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
+from tempest.lib.api_schema.response.volume import snapshots as schema
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 
@@ -32,14 +33,16 @@
         https://docs.openstack.org/api-ref/block-storage/v3/index.html#list-snapshots-and-details
         """
         url = 'snapshots'
+        list_schema = schema.list_snapshots_no_detail
         if detail:
             url += '/detail'
+            list_schema = schema.list_snapshots_with_detail
         if params:
             url += '?%s' % urllib.urlencode(params)
 
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(list_schema, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_snapshot(self, snapshot_id):
@@ -52,7 +55,7 @@
         url = "snapshots/%s" % snapshot_id
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_snapshot, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def create_snapshot(self, **kwargs):
@@ -65,7 +68,7 @@
         post_body = json.dumps({'snapshot': kwargs})
         resp, body = self.post('snapshots', post_body)
         body = json.loads(body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.create_snapshot, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def update_snapshot(self, snapshot_id, **kwargs):
@@ -78,7 +81,7 @@
         put_body = json.dumps({'snapshot': kwargs})
         resp, body = self.put('snapshots/%s' % snapshot_id, put_body)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.update_snapshot, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_snapshot(self, snapshot_id):
@@ -89,7 +92,7 @@
         https://docs.openstack.org/api-ref/block-storage/v3/index.html#delete-a-snapshot
         """
         resp, body = self.delete("snapshots/%s" % snapshot_id)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.delete_snapshot, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def is_resource_deleted(self, id):
@@ -108,7 +111,7 @@
         """Reset the specified snapshot's status."""
         post_body = json.dumps({'os-reset_status': {"status": status}})
         resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.reset_snapshot_status, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def update_snapshot_status(self, snapshot_id, **kwargs):
@@ -121,7 +124,7 @@
         post_body = json.dumps({'os-update_snapshot_status': kwargs})
         url = 'snapshots/%s/action' % snapshot_id
         resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.update_snapshot_status, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def create_snapshot_metadata(self, snapshot_id, metadata):
@@ -135,7 +138,7 @@
         url = "snapshots/%s/metadata" % snapshot_id
         resp, body = self.post(url, put_body)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.create_snapshot_metadata, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_snapshot_metadata(self, snapshot_id):
@@ -148,7 +151,7 @@
         url = "snapshots/%s/metadata" % snapshot_id
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_snapshot_metadata, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def update_snapshot_metadata(self, snapshot_id, **kwargs):
@@ -162,7 +165,7 @@
         url = "snapshots/%s/metadata" % snapshot_id
         resp, body = self.put(url, put_body)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.update_snapshot_metadata, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_snapshot_metadata_item(self, snapshot_id, id):
@@ -170,7 +173,7 @@
         url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_snapshot_metadata_item, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def update_snapshot_metadata_item(self, snapshot_id, id, **kwargs):
@@ -184,21 +187,23 @@
         url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
         resp, body = self.put(url, put_body)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(
+            schema.update_snapshot_metadata_item, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_snapshot_metadata_item(self, snapshot_id, id):
         """Delete metadata item for the snapshot."""
         url = "snapshots/%s/metadata/%s" % (snapshot_id, id)
         resp, body = self.delete(url)
-        self.expected_success(200, resp.status)
+        self.validate_response(
+            schema.delete_snapshot_metadata_item, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def force_delete_snapshot(self, snapshot_id):
         """Force Delete Snapshot."""
         post_body = json.dumps({'os-force_delete': {}})
         resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.force_delete_snapshot, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def unmanage_snapshot(self, snapshot_id):
@@ -206,5 +211,5 @@
         post_body = json.dumps({'os-unmanage': {}})
         url = 'snapshots/%s/action' % (snapshot_id)
         resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.unmanage_snapshot, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index efdfe8e..a80b77d 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -14,6 +14,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import os
 import subprocess
 
 import netaddr
@@ -531,33 +532,32 @@
         return image['id']
 
     def glance_image_create(self):
-        img_path = CONF.scenario.img_dir + "/" + CONF.scenario.img_file
-        aki_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.aki_img_file
-        ari_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ari_img_file
-        ami_img_path = CONF.scenario.img_dir + "/" + CONF.scenario.ami_img_file
+        img_path = CONF.scenario.img_file
+        if not os.path.exists(img_path):
+            # TODO(kopecmartin): replace LOG.warning for rasing
+            # InvalidConfiguration exception after tempest 25.0.0 is
+            # released - there will be one release which accepts both
+            # behaviors in order to avoid many failures across CIs and etc.
+            LOG.warning(
+                'Starting Tempest 25.0.0 release, CONF.scenario.img_file need '
+                'a full path for the image. CONF.scenario.img_dir was '
+                'deprecated and will be removed in the next release. Till '
+                'Tempest 25.0.0, old behavior is maintained and keep working '
+                'but starting Tempest 26.0.0, you need to specify the full '
+                'path in CONF.scenario.img_file config option.')
+            img_path = os.path.join(CONF.scenario.img_dir, img_path)
         img_container_format = CONF.scenario.img_container_format
         img_disk_format = CONF.scenario.img_disk_format
         img_properties = CONF.scenario.img_properties
         LOG.debug("paths: img: %s, container_format: %s, disk_format: %s, "
-                  "properties: %s, ami: %s, ari: %s, aki: %s",
+                  "properties: %s",
                   img_path, img_container_format, img_disk_format,
-                  img_properties, ami_img_path, ari_img_path, aki_img_path)
-        try:
-            image = self._image_create('scenario-img',
-                                       img_container_format,
-                                       img_path,
-                                       disk_format=img_disk_format,
-                                       properties=img_properties)
-        except IOError:
-            LOG.warning(
-                "A(n) %s image was not found. Retrying with uec image.",
-                img_disk_format)
-            kernel = self._image_create('scenario-aki', 'aki', aki_img_path)
-            ramdisk = self._image_create('scenario-ari', 'ari', ari_img_path)
-            properties = {'kernel_id': kernel, 'ramdisk_id': ramdisk}
-            image = self._image_create('scenario-ami', 'ami',
-                                       path=ami_img_path,
-                                       properties=properties)
+                  img_properties)
+        image = self._image_create('scenario-img',
+                                   img_container_format,
+                                   img_path,
+                                   disk_format=img_disk_format,
+                                   properties=img_properties)
         LOG.debug("image:%s", image)
 
         return image
@@ -868,15 +868,15 @@
             raise cls.skipException('Neutron not available')
 
     def _create_network(self, networks_client=None,
-                        tenant_id=None,
+                        project_id=None,
                         namestart='network-smoke-',
                         port_security_enabled=True, **net_dict):
         if not networks_client:
             networks_client = self.networks_client
-        if not tenant_id:
-            tenant_id = networks_client.tenant_id
+        if not project_id:
+            project_id = networks_client.project_id
         name = data_utils.rand_name(namestart)
-        network_kwargs = dict(name=name, tenant_id=tenant_id)
+        network_kwargs = dict(name=name, project_id=project_id)
         if net_dict:
             network_kwargs.update(net_dict)
         # Neutron disables port security by default so we have to check the
@@ -901,14 +901,14 @@
         if not subnets_client:
             subnets_client = self.subnets_client
 
-        def cidr_in_use(cidr, tenant_id):
+        def cidr_in_use(cidr, project_id):
             """Check cidr existence
 
             :returns: True if subnet with cidr already exist in tenant
                   False else
             """
             cidr_in_use = self.os_admin.subnets_client.list_subnets(
-                tenant_id=tenant_id, cidr=cidr)['subnets']
+                project_id=project_id, cidr=cidr)['subnets']
             return len(cidr_in_use) != 0
 
         ip_version = kwargs.pop('ip_version', 4)
@@ -927,13 +927,13 @@
         # blocks until an unallocated block is found.
         for subnet_cidr in tenant_cidr.subnet(num_bits):
             str_cidr = str(subnet_cidr)
-            if cidr_in_use(str_cidr, tenant_id=network['tenant_id']):
+            if cidr_in_use(str_cidr, project_id=network['project_id']):
                 continue
 
             subnet = dict(
                 name=data_utils.rand_name(namestart),
                 network_id=network['id'],
-                tenant_id=network['tenant_id'],
+                project_id=network['project_id'],
                 cidr=str_cidr,
                 ip_version=ip_version,
                 **kwargs
@@ -1015,7 +1015,7 @@
         kwargs = {
             'floating_network_id': external_network_id,
             'port_id': port_id,
-            'tenant_id': thing['tenant_id'],
+            'tenant_id': thing.get('project_id') or thing['tenant_id'],
             'fixed_ip_address': ip4,
         }
         if CONF.network.subnet_id:
@@ -1038,9 +1038,12 @@
         floatingip_id = floating_ip['id']
 
         def refresh():
-            result = (self.floating_ips_client.
-                      show_floatingip(floatingip_id)['floatingip'])
-            return status == result['status']
+            floating_ip = (self.floating_ips_client.
+                           show_floatingip(floatingip_id)['floatingip'])
+            if status == floating_ip['status']:
+                LOG.info("FloatingIP: {fp} is at status: {st}"
+                         .format(fp=floating_ip, st=status))
+            return status == floating_ip['status']
 
         if not test_utils.call_until_true(refresh,
                                           CONF.network.build_timeout,
@@ -1052,8 +1055,6 @@
                                      "failed  to reach status: {st}"
                              .format(fp=floating_ip, cst=floating_ip['status'],
                                      st=status))
-        LOG.info("FloatingIP: {fp} is at status: {st}"
-                 .format(fp=floating_ip, st=status))
 
     def check_tenant_network_connectivity(self, server,
                                           username,
@@ -1121,18 +1122,18 @@
         self.fail(msg)
 
     def _create_security_group(self, security_group_rules_client=None,
-                               tenant_id=None,
+                               project_id=None,
                                namestart='secgroup-smoke',
                                security_groups_client=None):
         if security_group_rules_client is None:
             security_group_rules_client = self.security_group_rules_client
         if security_groups_client is None:
             security_groups_client = self.security_groups_client
-        if tenant_id is None:
-            tenant_id = security_groups_client.tenant_id
+        if project_id is None:
+            project_id = security_groups_client.project_id
         secgroup = self._create_empty_security_group(
             namestart=namestart, client=security_groups_client,
-            tenant_id=tenant_id)
+            project_id=project_id)
 
         # Add rules to the security group
         rules = self._create_loginable_secgroup_rule(
@@ -1140,11 +1141,11 @@
             secgroup=secgroup,
             security_groups_client=security_groups_client)
         for rule in rules:
-            self.assertEqual(tenant_id, rule['tenant_id'])
+            self.assertEqual(project_id, rule['project_id'])
             self.assertEqual(secgroup['id'], rule['security_group_id'])
         return secgroup
 
-    def _create_empty_security_group(self, client=None, tenant_id=None,
+    def _create_empty_security_group(self, client=None, project_id=None,
                                      namestart='secgroup-smoke'):
         """Create a security group without rules.
 
@@ -1152,23 +1153,23 @@
          - IPv4 egress to any
          - IPv6 egress to any
 
-        :param tenant_id: secgroup will be created in this tenant
+        :param project_id: secgroup will be created in this project
         :returns: the created security group
         """
         if client is None:
             client = self.security_groups_client
-        if not tenant_id:
-            tenant_id = client.tenant_id
+        if not project_id:
+            project_id = client.project_id
         sg_name = data_utils.rand_name(namestart)
         sg_desc = sg_name + " description"
         sg_dict = dict(name=sg_name,
                        description=sg_desc)
-        sg_dict['tenant_id'] = tenant_id
+        sg_dict['project_id'] = project_id
         result = client.create_security_group(**sg_dict)
 
         secgroup = result['security_group']
         self.assertEqual(secgroup['name'], sg_name)
-        self.assertEqual(tenant_id, secgroup['tenant_id'])
+        self.assertEqual(project_id, secgroup['project_id'])
         self.assertEqual(secgroup['description'], sg_desc)
 
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
@@ -1177,15 +1178,15 @@
 
     def _create_security_group_rule(self, secgroup=None,
                                     sec_group_rules_client=None,
-                                    tenant_id=None,
+                                    project_id=None,
                                     security_groups_client=None, **kwargs):
         """Create a rule from a dictionary of rule parameters.
 
         Create a rule in a secgroup. if secgroup not defined will search for
-        default secgroup in tenant_id.
+        default secgroup in project_id.
 
         :param secgroup: the security group.
-        :param tenant_id: if secgroup not passed -- the tenant in which to
+        :param project_id: if secgroup not passed -- the tenant in which to
             search for default secgroup
         :param kwargs: a dictionary containing rule parameters:
             for example, to allow incoming ssh:
@@ -1200,18 +1201,18 @@
             sec_group_rules_client = self.security_group_rules_client
         if security_groups_client is None:
             security_groups_client = self.security_groups_client
-        if not tenant_id:
-            tenant_id = security_groups_client.tenant_id
+        if not project_id:
+            project_id = security_groups_client.project_id
         if secgroup is None:
-            # Get default secgroup for tenant_id
+            # Get default secgroup for project_id
             default_secgroups = security_groups_client.list_security_groups(
-                name='default', tenant_id=tenant_id)['security_groups']
-            msg = "No default security group for tenant %s." % (tenant_id)
+                name='default', project_id=project_id)['security_groups']
+            msg = "No default security group for project %s." % (project_id)
             self.assertNotEmpty(default_secgroups, msg)
             secgroup = default_secgroups[0]
 
         ruleset = dict(security_group_id=secgroup['id'],
-                       tenant_id=secgroup['tenant_id'])
+                       project_id=secgroup['project_id'])
         ruleset.update(kwargs)
 
         sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset)
@@ -1277,7 +1278,7 @@
 
         return rules
 
-    def _get_router(self, client=None, tenant_id=None):
+    def _get_router(self, client=None, project_id=None):
         """Retrieve a router for the given tenant id.
 
         If a public router has been configured, it will be returned.
@@ -1288,8 +1289,8 @@
         """
         if not client:
             client = self.routers_client
-        if not tenant_id:
-            tenant_id = client.tenant_id
+        if not project_id:
+            project_id = client.project_id
         router_id = CONF.network.public_router_id
         network_id = CONF.network.public_network_id
         if router_id:
@@ -1299,7 +1300,7 @@
             router = client.create_router(
                 name=data_utils.rand_name(self.__class__.__name__ + '-router'),
                 admin_state_up=True,
-                tenant_id=tenant_id,
+                project_id=project_id,
                 external_gateway_info=dict(network_id=network_id))['router']
             self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                             client.delete_router, router['id'])
@@ -1310,14 +1311,14 @@
 
     def create_networks(self, networks_client=None,
                         routers_client=None, subnets_client=None,
-                        tenant_id=None, dns_nameservers=None,
+                        project_id=None, dns_nameservers=None,
                         port_security_enabled=True, **net_dict):
         """Create a network with a subnet connected to a router.
 
         The baremetal driver is a special case since all nodes are
         on the same shared network.
 
-        :param tenant_id: id of tenant to create resources in.
+        :param project_id: id of project to create resources in.
         :param dns_nameservers: list of dns servers to send to subnet.
         :param port_security_enabled: whether or not port_security is enabled
         :param net_dict: a dict containing experimental network information in
@@ -1342,11 +1343,11 @@
         else:
             network = self._create_network(
                 networks_client=networks_client,
-                tenant_id=tenant_id,
+                project_id=project_id,
                 port_security_enabled=port_security_enabled,
                 **net_dict)
             router = self._get_router(client=routers_client,
-                                      tenant_id=tenant_id)
+                                      project_id=project_id)
             subnet_kwargs = dict(network=network,
                                  subnets_client=subnets_client)
             # use explicit check because empty list is a valid option
diff --git a/tempest/scenario/test_network_advanced_server_ops.py b/tempest/scenario/test_network_advanced_server_ops.py
index b1919d4..e26dc9d 100644
--- a/tempest/scenario/test_network_advanced_server_ops.py
+++ b/tempest/scenario/test_network_advanced_server_ops.py
@@ -262,7 +262,7 @@
         self._wait_server_status_and_check_network_connectivity(
             server, keypair, floating_ip)
 
-    @decorators.skip_because(bug='1836595')
+    @decorators.unstable_test(bug='1836595')
     @decorators.idempotent_id('25b188d7-0183-4b1e-a11d-15840c8e2fd6')
     @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
                           'Cold migration is not available.')
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index d8584ec..6c1b3fa 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -297,9 +297,19 @@
         ip_mask = CONF.network.project_network_mask_bits
         # check if the address is not already in use, if not, set it
         if ' ' + ip_address + '/' + str(ip_mask) not in ip_output:
-            ssh_client.exec_command("sudo ip addr add %s/%s dev %s" % (
-                                    ip_address, ip_mask, new_nic))
-            ssh_client.exec_command("sudo ip link set %s up" % new_nic)
+            try:
+                ssh_client.exec_command("sudo ip addr add %s/%s dev %s" % (
+                                        ip_address, ip_mask, new_nic))
+                ssh_client.exec_command("sudo ip link set %s up" % new_nic)
+            except exceptions.SSHExecCommandFailed as exc:
+                if 'RTNETLINK answers: File exists' in str(exc):
+                    LOG.debug(
+                        'IP address %(ip_address)s is already set in device '
+                        '%(device)s\nPrevious "ip a" output: %(ip_output)s',
+                        {'ip_address': ip_address, 'device': new_nic,
+                         'ip_output': ip_output})
+                else:
+                    raise exc
 
     def _get_server_nics(self, ssh_client):
         reg = re.compile(r'(?P<num>\d+): (?P<nic_name>\w+)[@]?.*:')
@@ -321,7 +331,7 @@
         internal_ips = (
             p['fixed_ips'][0]['ip_address'] for p in
             self.os_admin.ports_client.list_ports(
-                tenant_id=server['tenant_id'],
+                project_id=server['tenant_id'],
                 network_id=network['id'])['ports']
             if p['device_owner'].startswith('network') or
             p['device_owner'].startswith('compute')
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 9cbd831..3fc93e4 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -176,7 +176,7 @@
         cls.primary_tenant = cls.TenantProperties(cls.os_primary)
         cls.alt_tenant = cls.TenantProperties(cls.os_alt)
         for tenant in [cls.primary_tenant, cls.alt_tenant]:
-            cls.tenants[tenant.creds.tenant_id] = tenant
+            cls.tenants[tenant.creds.project_id] = tenant
 
         cls.floating_ip_access = not CONF.network.public_router_id
 
@@ -199,14 +199,14 @@
     def _create_tenant_security_groups(self, tenant):
         access_sg = self._create_empty_security_group(
             namestart='secgroup_access-',
-            tenant_id=tenant.creds.tenant_id,
+            project_id=tenant.creds.project_id,
             client=tenant.manager.security_groups_client
         )
 
         # don't use default secgroup since it allows in-project traffic
         def_sg = self._create_empty_security_group(
             namestart='secgroup_general-',
-            tenant_id=tenant.creds.tenant_id,
+            project_id=tenant.creds.project_id,
             client=tenant.manager.security_groups_client
         )
         tenant.security_groups.update(access=access_sg, default=def_sg)
@@ -536,7 +536,7 @@
         # Create empty security group and add icmp rule in it
         new_sg = self._create_empty_security_group(
             namestart='secgroup_new-',
-            tenant_id=new_tenant.creds.tenant_id,
+            project_id=new_tenant.creds.project_id,
             client=new_tenant.manager.security_groups_client)
         icmp_rule = dict(
             protocol='icmp',
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index a33d4d4..a062d40 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -29,8 +29,10 @@
     The following is the scenario outline:
      * boot an instance and create a timestamp file in it
      * snapshot the instance
+     * add version metadata to the snapshot image
      * boot a second instance from the snapshot
      * check the existence of the timestamp file in the second instance
+     * snapshot the instance again
 
     """
 
@@ -63,6 +65,11 @@
         # snapshot the instance
         snapshot_image = self.create_server_snapshot(server=server)
 
+        # add version metadata to the snapshot image
+        self.image_client.update_image(
+            snapshot_image['id'], [dict(add='/version',
+                                        value='8.0')])
+
         # boot a second instance from the snapshot
         server_from_snapshot = self.create_server(
             image_id=snapshot_image['id'],
@@ -75,3 +82,6 @@
                                         private_key=keypair['private_key'],
                                         server=server_from_snapshot)
         self.assertEqual(timestamp, timestamp2)
+
+        # snapshot the instance again
+        self.create_server_snapshot(server=server_from_snapshot)
diff --git a/tempest/tests/api/compute/test_base.py b/tempest/tests/api/compute/test_base.py
index 1593464..74d2625 100644
--- a/tempest/tests/api/compute/test_base.py
+++ b/tempest/tests/api/compute/test_base.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import mock
+from unittest import mock
 
 from oslo_utils import uuidutils
 import six
diff --git a/tempest/tests/base.py b/tempest/tests/base.py
index 0b53b45..e8b2c98 100644
--- a/tempest/tests/base.py
+++ b/tempest/tests/base.py
@@ -12,7 +12,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import mock
+from unittest import mock
+
 from oslotest import base
 
 
diff --git a/tempest/tests/cmd/test_account_generator.py b/tempest/tests/cmd/test_account_generator.py
index a962e37..7d764be 100644
--- a/tempest/tests/cmd/test_account_generator.py
+++ b/tempest/tests/cmd/test_account_generator.py
@@ -12,8 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from unittest import mock
+
 import fixtures
-import mock
 from oslo_config import cfg
 
 from tempest.cmd import account_generator
@@ -336,3 +337,24 @@
     def setUp(self):
         self.mock_domains()
         super(TestDumpAccountsV3, self).setUp()
+
+
+class TestAccountGeneratorCliCheck(base.TestCase):
+
+    def setUp(self):
+        super(TestAccountGeneratorCliCheck, self).setUp()
+        self.account_generator = account_generator.TempestAccountGenerator(
+            app=mock.Mock(), app_args=mock.Mock())
+        self.parser = self.account_generator.get_parser("generator")
+
+    def test_account_generator_zero_concurrency(self):
+        error = self.assertRaises(
+            SystemExit, lambda: self.parser.parse_args(
+                ['-r', '0', 'accounts_file.yaml']))
+        self.assertTrue(error.code != 0)
+
+    def test_account_generator_negative_concurrency(self):
+        error = self.assertRaises(
+            SystemExit, lambda: self.parser.parse_args(
+                ['-r', '-1', 'accounts_file.yaml']))
+        self.assertTrue(error.code != 0)
diff --git a/tempest/tests/cmd/test_cleanup.py b/tempest/tests/cmd/test_cleanup.py
index 1618df9..69e735b 100644
--- a/tempest/tests/cmd/test_cleanup.py
+++ b/tempest/tests/cmd/test_cleanup.py
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import mock
+from unittest import mock
 
 from tempest.cmd import cleanup
 from tempest.tests import base
diff --git a/tempest/tests/cmd/test_cleanup_services.py b/tempest/tests/cmd/test_cleanup_services.py
index 8366290..7bf7315 100644
--- a/tempest/tests/cmd/test_cleanup_services.py
+++ b/tempest/tests/cmd/test_cleanup_services.py
@@ -274,19 +274,22 @@
                     "name": "test"
                 },
                 "name": "test-volume-snapshot",
-                "user_id": "40c2102f4a554b848d96b14f3eec39ed",
                 "volume_id": "173f7b48-c4c1-4e70-9acc-086b39073506",
                 "created_at": "2015-11-29T02:25:51.000000",
                 "size": 1,
                 "updated_at": "2015-11-20T05:36:40.000000",
-                "os-extended-snapshot-attributes:progress": "100%",
                 "id": "b1323cda-8e4b-41c1-afc5-2fc791809c8c",
                 "description": "volume snapshot"
             },
             {
                 "status": "available",
                 "name": "saved-snapshot",
+                "metadata": {},
                 "id": "1ad4c789-7e8w-4dwg-afc5",
+                "size": 1,
+                "volume_id": "af7c41be-1ff6-4233-a690-7ed61c34347f",
+                "created_at": "2015-11-20T05:39:40.000000",
+                "updated_at": "2015-11-20T05:39:40.000000",
                 "description": "snapshot in saved state"
             }
         ]
diff --git a/tempest/tests/cmd/test_run.py b/tempest/tests/cmd/test_run.py
index e9bbcc2..5d9ddfa 100644
--- a/tempest/tests/cmd/test_run.py
+++ b/tempest/tests/cmd/test_run.py
@@ -18,9 +18,9 @@
 import shutil
 import subprocess
 import tempfile
+from unittest import mock
 
 import fixtures
-import mock
 import six
 
 from tempest.cmd import run
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 8dbba38..721fd76 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -13,9 +13,9 @@
 #    under the License.
 
 import os
+from unittest import mock
 
 import fixtures
-import mock
 from oslo_serialization import jsonutils as json
 
 from tempest import clients
@@ -629,3 +629,23 @@
     def test_contains_version_negative_data(self):
         self.assertFalse(
             verify_tempest_config.contains_version('v5.', ['v1.0', 'v2.0']))
+
+    def test_check_service_availability(self):
+        class FakeAuthProvider:
+            def get_auth(self):
+                return ('token',
+                        {'serviceCatalog': [{'type': 'compute'},
+                                            {'type': 'image'},
+                                            {'type': 'volumev3'},
+                                            {'type': 'network'},
+                                            {'type': 'object-store'}]})
+
+        class Fake_os:
+            auth_provider = FakeAuthProvider()
+            auth_version = 'v2'
+        verify_tempest_config.CONF._config = fake_config.FakePrivate()
+        services = verify_tempest_config.check_service_availability(
+            Fake_os(), True)
+        self.assertEqual(
+            sorted(['nova', 'glance', 'neutron', 'swift', 'cinder']),
+            sorted(services))
diff --git a/tempest/tests/cmd/test_workspace.py b/tempest/tests/cmd/test_workspace.py
index 7a6b576..eae6202 100644
--- a/tempest/tests/cmd/test_workspace.py
+++ b/tempest/tests/cmd/test_workspace.py
@@ -16,12 +16,12 @@
 import shutil
 import subprocess
 import tempfile
-
-from mock import patch
+from unittest.mock import patch
 try:
     from StringIO import StringIO
 except ImportError:
     from io import StringIO
+
 from tempest.cmd import workspace
 from tempest.lib.common.utils import data_utils
 from tempest.tests import base
diff --git a/tempest/tests/common/test_compute.py b/tempest/tests/common/test_compute.py
index c108be9..45a439c 100644
--- a/tempest/tests/common/test_compute.py
+++ b/tempest/tests/common/test_compute.py
@@ -13,9 +13,10 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from unittest import mock
+
 from six.moves.urllib import parse as urlparse
 
-import mock
 
 from tempest.common import compute
 from tempest.tests import base
diff --git a/tempest/tests/common/test_credentials_factory.py b/tempest/tests/common/test_credentials_factory.py
index 7cf87f8..0ef3742 100644
--- a/tempest/tests/common/test_credentials_factory.py
+++ b/tempest/tests/common/test_credentials_factory.py
@@ -13,7 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import mock
+from unittest import mock
+
 from oslo_config import cfg
 import testtools
 
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index e3bb836..5f8b990 100755
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -13,8 +13,8 @@
 #    under the License.
 
 import time
+from unittest import mock
 
-import mock
 from oslo_utils.fixture import uuidsentinel as uuids
 
 from tempest.common import waiters
diff --git a/tempest/tests/common/utils/test_net_utils.py b/tempest/tests/common/utils/test_net_utils.py
index 83c6bcc..51d86d1 100644
--- a/tempest/tests/common/utils/test_net_utils.py
+++ b/tempest/tests/common/utils/test_net_utils.py
@@ -10,7 +10,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import mock
+from unittest import mock
 
 from tempest.common.utils import net_utils
 from tempest.lib import exceptions as lib_exc
diff --git a/tempest/tests/lib/cli/test_execute.py b/tempest/tests/lib/cli/test_execute.py
index c069af5..a10e3bb 100644
--- a/tempest/tests/lib/cli/test_execute.py
+++ b/tempest/tests/lib/cli/test_execute.py
@@ -12,8 +12,8 @@
 #    under the License.
 
 import subprocess
+from unittest import mock
 
-import mock
 
 from tempest.lib.cli import base as cli_base
 from tempest.lib import exceptions
diff --git a/tempest/tests/lib/cmd/__init__.py b/tempest/tests/lib/cmd/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/lib/cmd/__init__.py
diff --git a/tempest/tests/lib/cmd/test_check_uuid.py b/tempest/tests/lib/cmd/test_check_uuid.py
new file mode 100644
index 0000000..130f90a
--- /dev/null
+++ b/tempest/tests/lib/cmd/test_check_uuid.py
@@ -0,0 +1,138 @@
+# 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 importlib
+import tempfile
+from unittest import mock
+
+from tempest.lib.cmd import check_uuid
+from tempest.tests import base
+
+
+class TestSourcePatcher(base.TestCase):
+    def test_add_patch(self):
+        patcher = check_uuid.SourcePatcher()
+        fake_file = tempfile.NamedTemporaryFile("w+t", delete=False)
+        file_contents = 'first_line\nsecond_line'
+        fake_file.write(file_contents)
+        fake_file.close()
+        patcher.add_patch(fake_file.name, 'patch', 2)
+
+        source_file = patcher.source_files[fake_file.name]
+        self.assertEqual(1, len(patcher.patches))
+        (patch_id, patch), = patcher.patches.items()
+        self.assertEqual(patcher._quote('patch\n'), patch)
+        self.assertEqual('first_line\n{%s:s}second_line' % patch_id,
+                         patcher._unquote(source_file))
+
+    def test_apply_patches(self):
+        fake_file = tempfile.NamedTemporaryFile("w+t")
+        patcher = check_uuid.SourcePatcher()
+        patcher.patches = {'fake-uuid': patcher._quote('patch\n')}
+        patcher.source_files = {
+            fake_file.name: patcher._quote('first_line\n') +
+            '{fake-uuid:s}second_line'}
+        with mock.patch('sys.stdout'):
+            patcher.apply_patches()
+
+        lines = fake_file.read().split('\n')
+        fake_file.close()
+        self.assertEqual(['first_line', 'patch', 'second_line'], lines)
+        self.assertFalse(patcher.patches)
+        self.assertFalse(patcher.source_files)
+
+
+class TestTestChecker(base.TestCase):
+    def _test_add_uuid_to_test(self, source_file):
+        class Fake_test_node():
+            lineno = 1
+            col_offset = 4
+        patcher = check_uuid.SourcePatcher()
+        checker = check_uuid.TestChecker(importlib.import_module('tempest'))
+        fake_file = tempfile.NamedTemporaryFile("w+t", delete=False)
+        fake_file.write(source_file)
+        fake_file.close()
+        checker._add_uuid_to_test(patcher, Fake_test_node(), fake_file.name)
+
+        self.assertEqual(1, len(patcher.patches))
+        self.assertEqual(1, len(patcher.source_files))
+        (patch_id, patch), = patcher.patches.items()
+        changed_source_file, = patcher.source_files.values()
+        self.assertEqual('{%s:s}%s' % (patch_id, patcher._quote(source_file)),
+                         changed_source_file)
+        expected_patch_start = patcher._quote(
+            '    ' + check_uuid.DECORATOR_TEMPLATE.split('(')[0])
+        self.assertTrue(patch.startswith(expected_patch_start))
+
+    def test_add_uuid_to_test_def(self):
+        source_file = ("    def test_test():\n"
+                       "        pass")
+        self._test_add_uuid_to_test(source_file)
+
+    def test_add_uuid_to_test_decorator(self):
+        source_file = ("    @decorators.idempotent_id\n"
+                       "    def test_test():\n"
+                       "        pass")
+        self._test_add_uuid_to_test(source_file)
+
+    def test_add_import_for_test_uuid_no_tempest(self):
+        patcher = check_uuid.SourcePatcher()
+        checker = check_uuid.TestChecker(importlib.import_module('tempest'))
+        fake_file = tempfile.NamedTemporaryFile("w+t")
+
+        class Fake_src_parsed():
+            body = ['test_node']
+        checker._import_name = mock.Mock(return_value='fake_module')
+
+        checker._add_import_for_test_uuid(patcher, Fake_src_parsed(),
+                                          fake_file.name)
+        (patch_id, patch), = patcher.patches.items()
+        self.assertEqual(patcher._quote('\n' + check_uuid.IMPORT_LINE + '\n'),
+                         patch)
+        self.assertEqual('{%s:s}' % patch_id,
+                         patcher.source_files[fake_file.name])
+
+    def test_add_import_for_test_uuid_tempest(self):
+        patcher = check_uuid.SourcePatcher()
+        checker = check_uuid.TestChecker(importlib.import_module('tempest'))
+        fake_file = tempfile.NamedTemporaryFile("w+t", delete=False)
+        test1 = ("    def test_test():\n"
+                 "        pass\n")
+        test2 = ("    def test_another_test():\n"
+                 "        pass\n")
+        source_code = test1 + test2
+        fake_file.write(source_code)
+        fake_file.close()
+
+        def fake_import_name(node):
+            return node.name
+        checker._import_name = fake_import_name
+
+        class Fake_node():
+            def __init__(self, lineno, col_offset, name):
+                self.lineno = lineno
+                self.col_offset = col_offset
+                self.name = name
+
+        class Fake_src_parsed():
+            body = [Fake_node(1, 4, 'tempest.a_fake_module'),
+                    Fake_node(3, 4, 'another_fake_module')]
+
+        checker._add_import_for_test_uuid(patcher, Fake_src_parsed(),
+                                          fake_file.name)
+        (patch_id, patch), = patcher.patches.items()
+        self.assertEqual(patcher._quote(check_uuid.IMPORT_LINE + '\n'),
+                         patch)
+        expected_source = patcher._quote(test1) + '{' + patch_id + ':s}' +\
+            patcher._quote(test2)
+        self.assertEqual(expected_source,
+                         patcher.source_files[fake_file.name])
diff --git a/tempest/tests/lib/common/test_cred_client.py b/tempest/tests/lib/common/test_cred_client.py
index 3dff16f..860a465 100644
--- a/tempest/tests/lib/common/test_cred_client.py
+++ b/tempest/tests/lib/common/test_cred_client.py
@@ -11,7 +11,7 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-import mock
+from unittest import mock
 
 from tempest.lib.common import cred_client
 from tempest.tests import base
diff --git a/tempest/tests/lib/common/test_dynamic_creds.py b/tempest/tests/lib/common/test_dynamic_creds.py
index 4723458..e9073cc 100644
--- a/tempest/tests/lib/common/test_dynamic_creds.py
+++ b/tempest/tests/lib/common/test_dynamic_creds.py
@@ -12,8 +12,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from unittest import mock
+
 import fixtures
-import mock
 from oslo_config import cfg
 
 from tempest.common import credentials_factory as credentials
@@ -110,7 +111,7 @@
                           (200,
                            {'roles': [{'id': id, 'name': name},
                                       {'id': '1', 'name': 'FakeRole'},
-                                      {'id': '2', 'name': 'Member'}]}))))
+                                      {'id': '2', 'name': 'member'}]}))))
         return roles_fix
 
     def _mock_list_2_roles(self):
@@ -139,7 +140,7 @@
             return_value=(rest_client.ResponseBody
                           (200, {'roles': [
                               {'id': '1', 'name': 'FakeRole'},
-                              {'id': '2', 'name': 'Member'}]}))))
+                              {'id': '2', 'name': 'member'}]}))))
         return roles_fix
 
     def _mock_list_ec2_credentials(self, user_id, tenant_id):
@@ -664,6 +665,6 @@
         with mock.patch('tempest.lib.common.dynamic_creds.LOG') as log_mock:
             creds._create_creds()
             log_mock.warning.assert_called_once_with(
-                "Member role already exists, ignoring conflict.")
+                "member role already exists, ignoring conflict.")
         creds.creds_client.assign_user_role.assert_called_once_with(
-            mock.ANY, mock.ANY, 'Member')
+            mock.ANY, mock.ANY, 'member')
diff --git a/tempest/tests/lib/common/test_preprov_creds.py b/tempest/tests/lib/common/test_preprov_creds.py
index 25df2a7..579363e 100644
--- a/tempest/tests/lib/common/test_preprov_creds.py
+++ b/tempest/tests/lib/common/test_preprov_creds.py
@@ -15,8 +15,8 @@
 import hashlib
 import os
 import shutil
+from unittest import mock
 
-import mock
 import six
 import testtools
 
diff --git a/tempest/tests/lib/common/test_profiler.py b/tempest/tests/lib/common/test_profiler.py
index 59fa0364..166d831 100644
--- a/tempest/tests/lib/common/test_profiler.py
+++ b/tempest/tests/lib/common/test_profiler.py
@@ -10,7 +10,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import mock
+from unittest import mock
+
 import testtools
 
 from tempest.lib.common import profiler
diff --git a/tempest/tests/lib/common/test_validation_resources.py b/tempest/tests/lib/common/test_validation_resources.py
index d5139f4..d50fd89 100644
--- a/tempest/tests/lib/common/test_validation_resources.py
+++ b/tempest/tests/lib/common/test_validation_resources.py
@@ -11,8 +11,9 @@
 #    See the License for the specific language governing permissions and
 #    limitations under the License.
 
+from unittest import mock
+
 import fixtures
-import mock
 import testtools
 
 from tempest.lib.common import validation_resources as vr
diff --git a/tempest/tests/lib/common/utils/linux/test_remote_client.py b/tempest/tests/lib/common/utils/linux/test_remote_client.py
index 7a21a5f..df23e63 100644
--- a/tempest/tests/lib/common/utils/linux/test_remote_client.py
+++ b/tempest/tests/lib/common/utils/linux/test_remote_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import mock
+from unittest import mock
 
 from tempest.lib.common import ssh
 from tempest.lib.common.utils.linux import remote_client
diff --git a/tempest/tests/lib/common/utils/test_test_utils.py b/tempest/tests/lib/common/utils/test_test_utils.py
index 865767b..bdc0ea4 100644
--- a/tempest/tests/lib/common/utils/test_test_utils.py
+++ b/tempest/tests/lib/common/utils/test_test_utils.py
@@ -14,8 +14,8 @@
 #    under the License.
 
 import time
+from unittest import mock
 
-import mock
 
 from tempest.lib.common import thread
 from tempest.lib.common.utils import test_utils
diff --git a/tempest/tests/lib/services/compute/test_base_compute_client.py b/tempest/tests/lib/services/compute/test_base_compute_client.py
index 69e8542..5841ae4 100644
--- a/tempest/tests/lib/services/compute/test_base_compute_client.py
+++ b/tempest/tests/lib/services/compute/test_base_compute_client.py
@@ -12,7 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import mock
+from unittest import mock
 
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions
diff --git a/tempest/tests/lib/services/compute/test_servers_client.py b/tempest/tests/lib/services/compute/test_servers_client.py
index 86f6ad5..a82b255 100644
--- a/tempest/tests/lib/services/compute/test_servers_client.py
+++ b/tempest/tests/lib/services/compute/test_servers_client.py
@@ -14,8 +14,8 @@
 #    under the License.
 
 import copy
+from unittest import mock
 
-import mock
 
 from tempest.lib.services.compute import base_compute_client
 from tempest.lib.services.compute import servers_client
diff --git a/tempest/tests/lib/services/compute/test_services_client.py b/tempest/tests/lib/services/compute/test_services_client.py
index ba432e3..0c513cc 100644
--- a/tempest/tests/lib/services/compute/test_services_client.py
+++ b/tempest/tests/lib/services/compute/test_services_client.py
@@ -13,8 +13,8 @@
 #    under the License.
 
 import copy
+from unittest import mock
 
-import mock
 
 from tempest.lib.services.compute import base_compute_client
 from tempest.lib.services.compute import services_client
diff --git a/tempest/tests/lib/services/identity/v2/test_token_client.py b/tempest/tests/lib/services/identity/v2/test_token_client.py
index 5b4e210..dc14a50 100644
--- a/tempest/tests/lib/services/identity/v2/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v2/test_token_client.py
@@ -12,7 +12,8 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-import mock
+from unittest import mock
+
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.common import rest_client
diff --git a/tempest/tests/lib/services/identity/v3/test_token_client.py b/tempest/tests/lib/services/identity/v3/test_token_client.py
index 656e10a..1c2295d 100644
--- a/tempest/tests/lib/services/identity/v3/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_token_client.py
@@ -12,7 +12,8 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-import mock
+from unittest import mock
+
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.common import rest_client
diff --git a/tempest/tests/lib/services/network/test_base_network_client.py b/tempest/tests/lib/services/network/test_base_network_client.py
index e121cec..a426397 100644
--- a/tempest/tests/lib/services/network/test_base_network_client.py
+++ b/tempest/tests/lib/services/network/test_base_network_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import mock
+from unittest import mock
 
 from tempest.lib.services.network import base as base_network_client
 from tempest.tests.lib import fake_auth_provider
diff --git a/tempest/tests/lib/services/network/test_quotas_client.py b/tempest/tests/lib/services/network/test_quotas_client.py
index aa6c1a1..7dce4e1 100644
--- a/tempest/tests/lib/services/network/test_quotas_client.py
+++ b/tempest/tests/lib/services/network/test_quotas_client.py
@@ -52,7 +52,7 @@
         }
     }
 
-    FAKE_QUOTA_TENANT_ID = "bab7d5c60cd041a0a36f7c4b6e1dd978"
+    FAKE_QUOTA_PROJECT_ID = "bab7d5c60cd041a0a36f7c4b6e1dd978"
 
     FAKE_QUOTA_DETAILS = {
         "quota": {
@@ -115,7 +115,7 @@
             self.FAKE_PROJECT_QUOTAS,
             bytes_body,
             200,
-            tenant_id=self.FAKE_QUOTA_TENANT_ID)
+            project_id=self.FAKE_QUOTA_PROJECT_ID)
 
     def _test_show_default_quotas(self, bytes_body=False):
         self.check_service_client_function(
@@ -124,7 +124,7 @@
             self.FAKE_PROJECT_QUOTAS,
             bytes_body,
             200,
-            tenant_id=self.FAKE_QUOTA_TENANT_ID)
+            project_id=self.FAKE_QUOTA_PROJECT_ID)
 
     def _test_update_quotas(self, bytes_body=False):
         self.check_service_client_function(
@@ -133,7 +133,7 @@
             self.FAKE_PROJECT_QUOTAS,
             bytes_body,
             200,
-            tenant_id=self.FAKE_QUOTA_TENANT_ID)
+            project_id=self.FAKE_QUOTA_PROJECT_ID)
 
     def _test_show_quota_details(self, bytes_body=False):
         self.check_service_client_function(
@@ -142,7 +142,7 @@
             self.FAKE_QUOTA_DETAILS,
             bytes_body,
             200,
-            tenant_id=self.FAKE_QUOTA_TENANT_ID)
+            project_id=self.FAKE_QUOTA_PROJECT_ID)
 
     def test_reset_quotas(self):
         self.check_service_client_function(
@@ -150,7 +150,7 @@
             "tempest.lib.common.rest_client.RestClient.delete",
             {},
             status=204,
-            tenant_id=self.FAKE_QUOTA_TENANT_ID)
+            project_id=self.FAKE_QUOTA_PROJECT_ID)
 
     def test_list_quotas_with_str_body(self):
         self._test_list_quotas()
diff --git a/tempest/tests/lib/services/network/test_security_group_rules_client.py b/tempest/tests/lib/services/network/test_security_group_rules_client.py
index b9c17a1..2ecc996 100644
--- a/tempest/tests/lib/services/network/test_security_group_rules_client.py
+++ b/tempest/tests/lib/services/network/test_security_group_rules_client.py
@@ -14,8 +14,8 @@
 #    under the License.
 
 import copy
+from unittest import mock
 
-import mock
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.services.network import base as network_base
diff --git a/tempest/tests/lib/services/network/test_security_groups_client.py b/tempest/tests/lib/services/network/test_security_groups_client.py
index f96805f..501883b 100644
--- a/tempest/tests/lib/services/network/test_security_groups_client.py
+++ b/tempest/tests/lib/services/network/test_security_groups_client.py
@@ -14,8 +14,8 @@
 #    under the License.
 
 import copy
+from unittest import mock
 
-import mock
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.services.network import base as network_base
diff --git a/tempest/tests/lib/services/object_storage/test_object_client.py b/tempest/tests/lib/services/object_storage/test_object_client.py
index 1749b03..c646d61 100644
--- a/tempest/tests/lib/services/object_storage/test_object_client.py
+++ b/tempest/tests/lib/services/object_storage/test_object_client.py
@@ -14,7 +14,7 @@
 #    under the License.
 
 
-import mock
+from unittest import mock
 
 from tempest.lib import exceptions
 from tempest.lib.services.object_storage import object_client
diff --git a/tempest/tests/lib/services/test_clients.py b/tempest/tests/lib/services/test_clients.py
index 43fd88f..f83064a 100644
--- a/tempest/tests/lib/services/test_clients.py
+++ b/tempest/tests/lib/services/test_clients.py
@@ -13,9 +13,9 @@
 # the License.
 
 import types
+from unittest import mock
 
 import fixtures
-import mock
 import six
 import testtools
 
diff --git a/tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py b/tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
index 84c7589..7606a52 100644
--- a/tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_scheduler_stats_client.py
@@ -25,12 +25,14 @@
                 "name": "pool1",
                 "capabilities": {
                     "updated": "2014-10-28T00:00:00-00:00",
-                    "total_capacity": 1024,
-                    "free_capacity": 100,
+                    "total_capacity_gb": 1024,
+                    "free_capacity_gb": 100,
                     "volume_backend_name": "pool1",
                     "reserved_percentage": 0,
                     "driver_version": "1.0.0",
+                    "timestamp": "2014-10-28T00:00:00-00:00",
                     "storage_protocol": "iSCSI",
+                    "vendor_name": "vendor",
                     "QoS_support": False
                 }
             },
@@ -38,12 +40,14 @@
                 "name": "pool2",
                 "capabilities": {
                     "updated": "2014-10-28T00:00:00-00:00",
-                    "total_capacity": 512,
-                    "free_capacity": 200,
+                    "total_capacity_gb": 512,
+                    "free_capacity_gb": 200,
                     "volume_backend_name": "pool2",
                     "reserved_percentage": 0,
                     "driver_version": "1.0.2",
+                    "timestamp": "2014-10-28T00:00:00-00:00",
                     "storage_protocol": "iSER",
+                    "vendor_name": "vendor",
                     "QoS_support": True
                 }
             }
diff --git a/tempest/tests/lib/services/volume/v3/test_services_client.py b/tempest/tests/lib/services/volume/v3/test_services_client.py
index f65228f..c807bc2 100644
--- a/tempest/tests/lib/services/volume/v3/test_services_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_services_client.py
@@ -14,8 +14,8 @@
 #    under the License.
 
 import copy
+from unittest import mock
 
-import mock
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.services.volume.v3 import services_client
diff --git a/tempest/tests/lib/services/volume/v3/test_snapshot_manage_client.py b/tempest/tests/lib/services/volume/v3/test_snapshot_manage_client.py
index 1b88020..8309f7a 100644
--- a/tempest/tests/lib/services/volume/v3/test_snapshot_manage_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_snapshot_manage_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import mock
+from unittest import mock
 
 from oslo_serialization import jsonutils as json
 
diff --git a/tempest/tests/lib/services/volume/v3/test_transfers_client.py b/tempest/tests/lib/services/volume/v3/test_transfers_client.py
index d631fe7..1dfe2df 100644
--- a/tempest/tests/lib/services/volume/v3/test_transfers_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_transfers_client.py
@@ -14,8 +14,8 @@
 #    under the License.
 
 import copy
+from unittest import mock
 
-import mock
 from oslo_serialization import jsonutils as json
 
 from tempest.lib.services.volume.v3 import transfers_client
diff --git a/tempest/tests/lib/services/volume/v3/test_volume_manage_client.py b/tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
index 902f027..d4313a2 100644
--- a/tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_volume_manage_client.py
@@ -13,7 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import mock
+from unittest import mock
 
 from oslo_serialization import jsonutils as json
 
diff --git a/tempest/tests/lib/test_decorators.py b/tempest/tests/lib/test_decorators.py
index 9c6cac7..e3c17e8 100644
--- a/tempest/tests/lib/test_decorators.py
+++ b/tempest/tests/lib/test_decorators.py
@@ -14,8 +14,8 @@
 #    under the License.
 
 import abc
+from unittest import mock
 
-import mock
 import six
 import testtools
 
diff --git a/tempest/tests/lib/test_ssh.py b/tempest/tests/lib/test_ssh.py
index c849231..85048fb 100644
--- a/tempest/tests/lib/test_ssh.py
+++ b/tempest/tests/lib/test_ssh.py
@@ -13,8 +13,8 @@
 #    under the License.
 
 import socket
+from unittest import mock
 
-import mock
 import six
 from six import StringIO
 import testtools
diff --git a/tempest/tests/test_base_test.py b/tempest/tests/test_base_test.py
index 2b5a947..b154cd5 100644
--- a/tempest/tests/test_base_test.py
+++ b/tempest/tests/test_base_test.py
@@ -12,7 +12,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import mock
+from unittest import mock
+
 from oslo_config import cfg
 
 from tempest import clients
diff --git a/tempest/tests/test_imports.py b/tempest/tests/test_imports.py
index 6f1cfca..ad7bebb 100644
--- a/tempest/tests/test_imports.py
+++ b/tempest/tests/test_imports.py
@@ -12,7 +12,7 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-import mock
+from unittest import mock
 
 from tempest.tests import base
 
diff --git a/tempest/tests/test_test.py b/tempest/tests/test_test.py
index 49fd010..72e8b6d 100644
--- a/tempest/tests/test_test.py
+++ b/tempest/tests/test_test.py
@@ -15,8 +15,8 @@
 
 import os
 import sys
+from unittest import mock
 
-import mock
 from oslo_config import cfg
 import testtools
 
diff --git a/test-requirements.txt b/test-requirements.txt
index 17a7d2a..17fa9f1 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -2,7 +2,6 @@
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
 hacking>=3.0.1,<3.1.0;python_version>='3.5' # Apache-2.0
-mock>=2.0.0 # BSD
 coverage!=4.4,>=4.0 # Apache-2.0
 oslotest>=3.2.0 # Apache-2.0
 pycodestyle>=2.0.0,<2.6.0 # MIT
diff --git a/tools/generate-tempest-plugins-list.py b/tools/generate-tempest-plugins-list.py
index 5ffef3e..530ce5e 100644
--- a/tools/generate-tempest-plugins-list.py
+++ b/tools/generate-tempest-plugins-list.py
@@ -52,6 +52,8 @@
     'x/tap-as-a-service',  # To avoid sanity-job failure
     'x/valet',  # https://review.opendev.org/#/c/638339/
     'x/kingbird',  # https://bugs.launchpad.net/kingbird/+bug/1869722
+    # vmware-nsx is blacklisted since https://review.opendev.org/#/c/736952
+    'x/vmware-nsx-tempest-plugin',
 ]
 
 url = 'https://review.opendev.org/projects/'
diff --git a/tools/tempest-integrated-gate-compute-blacklist.txt b/tools/tempest-integrated-gate-compute-blacklist.txt
index 8805262..2290751 100644
--- a/tools/tempest-integrated-gate-compute-blacklist.txt
+++ b/tools/tempest-integrated-gate-compute-blacklist.txt
@@ -11,3 +11,9 @@
 tempest.scenario.test_object_storage_basic_ops.TestObjectStorageBasicOps.test_swift_basic_ops
 tempest.scenario.test_object_storage_basic_ops.TestObjectStorageBasicOps.test_swift_acl_anonymous_download
 tempest.scenario.test_volume_backup_restore.TestVolumeBackupRestore.test_volume_backup_restore
+
+# Skip test scenario when creating second image from instance
+# https://bugs.launchpad.net/tripleo/+bug/1881592
+# The test is most likely wrong and may fail if the fists image is create quickly.
+# FIXME: Either fix the test so it won't race or consider if we should cover the scenario at all.
+tempest.api.compute.images.test_images_oneserver_negative.ImagesOneServerNegativeTestJSON.test_create_second_image_when_first_image_is_being_saved
diff --git a/tools/tempest-plugin-sanity.sh b/tools/tempest-plugin-sanity.sh
index 2ff4aea..c983da9 100644
--- a/tools/tempest-plugin-sanity.sh
+++ b/tools/tempest-plugin-sanity.sh
@@ -66,7 +66,7 @@
 # function to create virtualenv to perform sanity operation
 function prepare_workspace {
     SANITY_DIR=$(pwd)
-    virtualenv -p python3 --clear "$SANITY_DIR"/.venv
+    python3 -m venv "$SANITY_DIR"/.venv
     export TVENV="$SANITY_DIR/tools/with_venv.sh"
     cd "$SANITY_DIR"