Merge "Adding description for testcases - compute part8"
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 2a3edf4..59a2f64 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -24,6 +24,7 @@
 
 import os
 import subprocess
+import sys
 
 # Build the plugin registry
 def build_plugin_registry(app):
@@ -31,16 +32,20 @@
         os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
     subprocess.call(['tools/generate-tempest-plugins-list.sh'], cwd=root_dir)
 
+def autodoc_skip_member_handler(app, what, name, obj, skip, options):
+    return skip or (what == "class" and not name.startswith("test"))
+
 def setup(app):
+    app.connect('autodoc-skip-member', autodoc_skip_member_handler)
     if os.getenv('GENERATE_TEMPEST_PLUGIN_LIST', 'true').lower() == 'true':
         app.connect('builder-inited', build_plugin_registry)
 
-
-
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+# sys.path.insert(0, os.path.abspath('.'))
+sys.path.insert(0, os.path.abspath('../../tempest'))
+sys.path.insert(0, os.path.abspath('../../tempest/api'))
 
 # -- General configuration -----------------------------------------------------
 
@@ -204,3 +209,10 @@
     ('index', 'doc-tempest.tex', u'Tempest Testing Project',
      u'OpenStack Foundation', 'manual'),
 ]
+
+latex_use_xindy = False
+
+latex_elements = {
+    'maxlistdepth': 20,
+    'printindex': '\\footnotesize\\raggedright\\printindex'
+}
diff --git a/doc/source/index.rst b/doc/source/index.rst
index d4dc166..66e68ea 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -56,6 +56,13 @@
 
    supported_version
 
+Description of Tests
+--------------------
+.. toctree::
+   :maxdepth: 2
+
+   tests/modules
+
 For Contributors
 ================
 
diff --git a/doc/source/tests/modules.rst b/doc/source/tests/modules.rst
new file mode 100644
index 0000000..026a7a5
--- /dev/null
+++ b/doc/source/tests/modules.rst
@@ -0,0 +1,21 @@
+Description of Tests
+====================
+
+OpenStack Services Integration Tests
+------------------------------------
+.. toctree::
+   :maxdepth: 2
+
+   scenario/modules
+
+OpenStack Services API Tests
+----------------------------
+.. toctree::
+   :maxdepth: 2
+
+   compute/modules
+   identity/modules
+   image/modules
+   network/modules
+   object_storage/modules
+   volume/modules
diff --git a/releasenotes/notes/25/subunt-describe-call-verbose-arg-fix.yaml b/releasenotes/notes/25/subunt-describe-call-verbose-arg-fix.yaml
new file mode 100644
index 0000000..d2a644e
--- /dev/null
+++ b/releasenotes/notes/25/subunt-describe-call-verbose-arg-fix.yaml
@@ -0,0 +1,10 @@
+---
+fixes:
+  - |
+    Fixed bug #1890060. tempest subunit_describe_calls --verbose not working with Cliff CLI.
+    The subunit_describe_calls --verbose argument was a boolean and worked in the non Cliff CLI
+    which is now deprecated, but does not work with cliff since --verbase is a standard cliff
+    argument which is an int.  Since the tool is in lib directory we cannot change the interface,
+    so we add a new argument -a --all-stdout that will allow cliff CLI to support the
+    feature in subunnit_describe_calls to print request and response headers and bodies
+    to stdout.
\ No newline at end of file
diff --git a/releasenotes/notes/add-can-migrate-between-any-hosts-config-option-x8ah4f9737a28e9b.yaml b/releasenotes/notes/add-can-migrate-between-any-hosts-config-option-x8ah4f9737a28e9b.yaml
new file mode 100644
index 0000000..26fe01a
--- /dev/null
+++ b/releasenotes/notes/add-can-migrate-between-any-hosts-config-option-x8ah4f9737a28e9b.yaml
@@ -0,0 +1,9 @@
+---
+features:
+  - Add a new config option can_migrate_between_any_hosts in the
+    compute-feature-enabled section, which can be set to False for environment
+    with non homogeneous compute nodes, so that it can select a destination
+    host for migrating automatically, otherwise the testcase may fail
+    unexpectedly. e.g., if source host is with CPU "E5-2699 v4" and the
+    selected target host is with CPU "E5-2670 v3", the live-migration will
+    fail because of the downgrade issue.
diff --git a/releasenotes/notes/xenapi_apis-conf-fcca549283e53ed6.yaml b/releasenotes/notes/xenapi_apis-conf-fcca549283e53ed6.yaml
new file mode 100644
index 0000000..4d26210
--- /dev/null
+++ b/releasenotes/notes/xenapi_apis-conf-fcca549283e53ed6.yaml
@@ -0,0 +1,7 @@
+---
+upgrade:
+  - |
+    A number of Compute APIs that only worked with the XenAPI virt driver have
+    been removed in the Compute service. As a result, their corresponding tests
+    are now disabled by default. They can be re-enabled using the new
+    ``[compute_feature_enabled] xenapi_apis`` config option.
diff --git a/tempest/api/compute/admin/test_agents.py b/tempest/api/compute/admin/test_agents.py
index 0901374..4cc5fdd 100644
--- a/tempest/api/compute/admin/test_agents.py
+++ b/tempest/api/compute/admin/test_agents.py
@@ -13,12 +13,22 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
+CONF = config.CONF
 
+
+# TODO(stephenfin): Remove these tests once the nova Ussuri branch goes EOL
 class AgentsAdminTestJSON(base.BaseV2ComputeAdminTest):
-    """Tests Agents API"""
+    """Tests Compute Agents API"""
+
+    @classmethod
+    def skip_checks(cls):
+        super(AgentsAdminTestJSON, cls).skip_checks()
+        if not CONF.compute_feature_enabled.xenapi_apis:
+            raise cls.skipException('The os-agents API is not supported.')
 
     @classmethod
     def setup_clients(cls):
@@ -46,7 +56,7 @@
 
     @decorators.idempotent_id('1fc6bdc8-0b6d-4cc7-9f30-9b04fabe5b90')
     def test_create_agent(self):
-        # Create an agent.
+        """Test creating a compute agent"""
         params = self._param_helper(
             hypervisor='kvm', os='win', architecture='x86',
             version='7.0', url='xxx://xxxx/xxx/xxx',
@@ -58,6 +68,7 @@
 
     @decorators.idempotent_id('dc9ffd51-1c50-4f0e-a820-ae6d2a568a9e')
     def test_update_agent(self):
+        """Test updating a compute agent"""
         # Create and update an agent.
         body = self.client.create_agent(**self.params_agent)['agent']
         self.addCleanup(self.client.delete_agent, body['agent_id'])
@@ -71,7 +82,7 @@
 
     @decorators.idempotent_id('470e0b89-386f-407b-91fd-819737d0b335')
     def test_delete_agent(self):
-        # Create an agent and delete it.
+        """Test deleting a compute agent"""
         body = self.client.create_agent(**self.params_agent)['agent']
         self.client.delete_agent(body['agent_id'])
 
@@ -82,7 +93,7 @@
 
     @decorators.idempotent_id('6a326c69-654b-438a-80a3-34bcc454e138')
     def test_list_agents(self):
-        # Create an agent and  list all agents.
+        """Test listing compute agents"""
         body = self.client.create_agent(**self.params_agent)['agent']
         self.addCleanup(self.client.delete_agent, body['agent_id'])
         agents = self.client.list_agents()['agents']
@@ -91,7 +102,7 @@
 
     @decorators.idempotent_id('eabadde4-3cd7-4ec4-a4b5-5a936d2d4408')
     def test_list_agents_with_filter(self):
-        # Create agents and list the agent builds by the filter.
+        """Test listing compute agents by the filter"""
         body = self.client.create_agent(**self.params_agent)['agent']
         self.addCleanup(self.client.delete_agent, body['agent_id'])
         params = self._param_helper(
diff --git a/tempest/api/compute/admin/test_aggregates.py b/tempest/api/compute/admin/test_aggregates.py
index 7a3bfdf..2716259 100644
--- a/tempest/api/compute/admin/test_aggregates.py
+++ b/tempest/api/compute/admin/test_aggregates.py
@@ -71,10 +71,11 @@
 
 
 class AggregatesAdminTestJSON(AggregatesAdminTestBase):
+    """Tests Aggregates API that require admin privileges"""
 
     @decorators.idempotent_id('0d148aa3-d54c-4317-aa8d-42040a475e20')
     def test_aggregate_create_delete(self):
-        # Create and delete an aggregate.
+        """Test create/delete aggregate"""
         aggregate = self._create_test_aggregate()
         self.assertIsNone(aggregate['availability_zone'])
 
@@ -83,7 +84,7 @@
 
     @decorators.idempotent_id('5873a6f8-671a-43ff-8838-7ce430bb6d0b')
     def test_aggregate_create_delete_with_az(self):
-        # Create and delete an aggregate.
+        """Test create/delete aggregate with availability_zone"""
         az_name = data_utils.rand_name(self.az_name_prefix)
         aggregate = self._create_test_aggregate(availability_zone=az_name)
         self.assertEqual(az_name, aggregate['availability_zone'])
@@ -93,7 +94,7 @@
 
     @decorators.idempotent_id('68089c38-04b1-4758-bdf0-cf0daec4defd')
     def test_aggregate_create_verify_entry_in_list(self):
-        # Create an aggregate and ensure it is listed.
+        """Test listing aggregate should contain the created aggregate"""
         aggregate = self._create_test_aggregate()
         aggregates = self.client.list_aggregates()['aggregates']
         self.assertIn((aggregate['id'], aggregate['availability_zone']),
@@ -102,7 +103,7 @@
 
     @decorators.idempotent_id('36ec92ca-7a73-43bc-b920-7531809e8540')
     def test_aggregate_create_update_metadata_get_details(self):
-        # Create an aggregate and ensure its details are returned.
+        """Test set/get aggregate metadata"""
         aggregate = self._create_test_aggregate()
         body = self.client.show_aggregate(aggregate['id'])['aggregate']
         self.assertEqual(aggregate['name'], body['name'])
@@ -121,7 +122,7 @@
 
     @decorators.idempotent_id('4d2b2004-40fa-40a1-aab2-66f4dab81beb')
     def test_aggregate_create_update_with_az(self):
-        # Update an aggregate and ensure properties are updated correctly
+        """Test create/update aggregate with availability_zone"""
         aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
         az_name = data_utils.rand_name(self.az_name_prefix)
         aggregate = self._create_test_aggregate(
@@ -148,7 +149,7 @@
 
     @decorators.idempotent_id('c8e85064-e79b-4906-9931-c11c24294d02')
     def test_aggregate_add_remove_host(self):
-        # Add a host to the given aggregate and remove.
+        """Test adding host to and removing host from aggregate"""
         self.useFixture(fixtures.LockFixture('availability_zone'))
         aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
         aggregate = self._create_test_aggregate(name=aggregate_name)
@@ -169,7 +170,10 @@
 
     @decorators.idempotent_id('7f6a1cc5-2446-4cdb-9baa-b6ae0a919b72')
     def test_aggregate_add_host_list(self):
-        # Add a host to the given aggregate and list.
+        """Test listing aggregate contains the host added to the aggregate
+
+        Add a host to the given aggregate and list.
+        """
         self.useFixture(fixtures.LockFixture('availability_zone'))
         aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
         aggregate = self._create_test_aggregate(name=aggregate_name)
@@ -188,7 +192,10 @@
 
     @decorators.idempotent_id('eeef473c-7c52-494d-9f09-2ed7fc8fc036')
     def test_aggregate_add_host_get_details(self):
-        # Add a host to the given aggregate and get details.
+        """Test showing aggregate contains the host added to the aggregate
+
+        Add a host to the given aggregate and get details.
+        """
         self.useFixture(fixtures.LockFixture('availability_zone'))
         aggregate_name = data_utils.rand_name(self.aggregate_name_prefix)
         aggregate = self._create_test_aggregate(name=aggregate_name)
@@ -204,7 +211,7 @@
 
     @decorators.idempotent_id('96be03c7-570d-409c-90f8-e4db3c646996')
     def test_aggregate_add_host_create_server_with_az(self):
-        # Add a host to the given aggregate and create a server.
+        """Test adding a host to the given aggregate and creating a server"""
         self.useFixture(fixtures.LockFixture('availability_zone'))
         az_name = data_utils.rand_name(self.az_name_prefix)
         aggregate = self._create_test_aggregate(availability_zone=az_name)
@@ -233,6 +240,11 @@
 
 
 class AggregatesAdminTestV241(AggregatesAdminTestBase):
+    """Tests Aggregates API that require admin privileges
+
+    Tests Aggregates API that require admin privileges with compute
+    microversion greater than 2.40.
+    """
     min_microversion = '2.41'
 
     # NOTE(gmann): This test tests the Aggregate APIs response schema
@@ -241,6 +253,11 @@
 
     @decorators.idempotent_id('fdf24d9e-8afa-4700-b6aa-9c498351504f')
     def test_create_update_show_aggregate_add_remove_host(self):
+        """Test response schema of aggregates API
+
+        Test response schema of aggregates API(create/update/show/add host/
+        remove host) with compute microversion greater than 2.40.
+        """
         # Update and add a host to the given aggregate and get details.
         self.useFixture(fixtures.LockFixture('availability_zone'))
         # Checking create aggregate API response schema
diff --git a/tempest/api/compute/admin/test_availability_zone.py b/tempest/api/compute/admin/test_availability_zone.py
index bbd39b6..3eb0d9a 100644
--- a/tempest/api/compute/admin/test_availability_zone.py
+++ b/tempest/api/compute/admin/test_availability_zone.py
@@ -27,12 +27,12 @@
 
     @decorators.idempotent_id('d3431479-8a09-4f76-aa2d-26dc580cb27c')
     def test_get_availability_zone_list(self):
-        # List of availability zone
+        """Test listing availability zones"""
         availability_zone = self.client.list_availability_zones()
         self.assertNotEmpty(availability_zone['availabilityZoneInfo'])
 
     @decorators.idempotent_id('ef726c58-530f-44c2-968c-c7bed22d5b8c')
     def test_get_availability_zone_list_detail(self):
-        # List of availability zones and available services
+        """Test listing availability zones with detail"""
         availability_zone = self.client.list_availability_zones(detail=True)
         self.assertNotEmpty(availability_zone['availabilityZoneInfo'])
diff --git a/tempest/api/compute/admin/test_availability_zone_negative.py b/tempest/api/compute/admin/test_availability_zone_negative.py
index a58c22c..6e576e8 100644
--- a/tempest/api/compute/admin/test_availability_zone_negative.py
+++ b/tempest/api/compute/admin/test_availability_zone_negative.py
@@ -18,7 +18,7 @@
 
 
 class AZAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
-    """Tests Availability Zone API List"""
+    """Negative Tests of Availability Zone API List"""
 
     @classmethod
     def setup_clients(cls):
@@ -28,8 +28,12 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('bf34dca2-fdc3-4073-9c02-7648d9eae0d7')
     def test_get_availability_zone_list_detail_with_non_admin_user(self):
-        # List of availability zones and available services with
-        # non-administrator user
+        """Test listing availability zone with detail by non-admin user
+
+        List of availability zones and available services with
+        non-administrator user is not allowed.
+        """
+
         self.assertRaises(
             lib_exc.Forbidden,
             self.non_adm_client.list_availability_zones, detail=True)
diff --git a/tempest/api/compute/admin/test_fixed_ips.py b/tempest/api/compute/admin/test_fixed_ips.py
index 66c2c2d..9de3da9 100644
--- a/tempest/api/compute/admin/test_fixed_ips.py
+++ b/tempest/api/compute/admin/test_fixed_ips.py
@@ -22,6 +22,7 @@
 
 
 class FixedIPsTestJson(base.BaseV2ComputeAdminTest):
+    """Test fixed ips API"""
 
     @classmethod
     def skip_checks(cls):
@@ -56,13 +57,16 @@
 
     @decorators.idempotent_id('16b7d848-2f7c-4709-85a3-2dfb4576cc52')
     def test_list_fixed_ip_details(self):
+        """Test getting fixed ip details"""
         fixed_ip = self.client.show_fixed_ip(self.ip)
         self.assertEqual(fixed_ip['fixed_ip']['address'], self.ip)
 
     @decorators.idempotent_id('5485077b-7e46-4cec-b402-91dc3173433b')
     def test_set_reserve(self):
+        """Test reserving fixed ip"""
         self.client.reserve_fixed_ip(self.ip, reserve="None")
 
     @decorators.idempotent_id('7476e322-b9ff-4710-bf82-49d51bac6e2e')
     def test_set_unreserve(self):
+        """Test unreserving fixed ip"""
         self.client.reserve_fixed_ip(self.ip, unreserve="None")
diff --git a/tempest/api/compute/admin/test_fixed_ips_negative.py b/tempest/api/compute/admin/test_fixed_ips_negative.py
index 7d41f46..1629faa 100644
--- a/tempest/api/compute/admin/test_fixed_ips_negative.py
+++ b/tempest/api/compute/admin/test_fixed_ips_negative.py
@@ -22,6 +22,7 @@
 
 
 class FixedIPsNegativeTestJson(base.BaseV2ComputeAdminTest):
+    """Negative tests of fixed ips API"""
 
     @classmethod
     def skip_checks(cls):
@@ -58,12 +59,14 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9f17f47d-daad-4adc-986e-12370c93e407')
     def test_list_fixed_ip_details_with_non_admin_user(self):
+        """Test listing fixed ip with detail by non-admin user is forbidden"""
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.show_fixed_ip, self.ip)
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ce60042c-fa60-4836-8d43-1c8e3359dc47')
     def test_set_reserve_with_non_admin_user(self):
+        """Test reserving fixed ip by non-admin user is forbidden"""
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.reserve_fixed_ip,
                           self.ip, reserve="None")
@@ -71,6 +74,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f1f7a35b-0390-48c5-9803-5f27461439db')
     def test_set_unreserve_with_non_admin_user(self):
+        """Test unreserving fixed ip by non-admin user is forbidden"""
         self.assertRaises(lib_exc.Forbidden,
                           self.non_admin_client.reserve_fixed_ip,
                           self.ip, unreserve="None")
@@ -78,6 +82,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f51cf464-7fc5-4352-bc3e-e75cfa2cb717')
     def test_set_reserve_with_invalid_ip(self):
+        """Test reserving invalid fixed ip should fail"""
         # NOTE(maurosr): since this exercises the same code snippet, we do it
         # only for reserve action
         # NOTE(eliqiao): in Juno, the exception is NotFound, but in master, we
@@ -90,6 +95,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('fd26ef50-f135-4232-9d32-281aab3f9176')
     def test_fixed_ip_with_invalid_action(self):
+        """Test operating fixed ip with invalid action should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.client.reserve_fixed_ip,
                           self.ip, invalid_action="None")
diff --git a/tempest/api/compute/admin/test_flavors_access.py b/tempest/api/compute/admin/test_flavors_access.py
index b8e2b42..87ab7c7 100644
--- a/tempest/api/compute/admin/test_flavors_access.py
+++ b/tempest/api/compute/admin/test_flavors_access.py
@@ -43,8 +43,12 @@
 
     @decorators.idempotent_id('ea2c2211-29fa-4db9-97c3-906d36fad3e0')
     def test_flavor_access_list_with_private_flavor(self):
-        # Test to make sure that list flavor access on a newly created
-        # private flavor will return an empty access list
+        """Test listing flavor access for a private flavor
+
+        Listing flavor access on a newly created private flavor will return
+        an empty access list.
+        """
+        # Test to make sure that
         flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
                                     disk=self.disk, is_public='False')
 
@@ -54,7 +58,7 @@
 
     @decorators.idempotent_id('59e622f6-bdf6-45e3-8ba8-fedad905a6b4')
     def test_flavor_access_add_remove(self):
-        # Test to add and remove flavor access to a given tenant.
+        """Test add/remove flavor access to a given project"""
         flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
                                     disk=self.disk, is_public='False')
 
diff --git a/tempest/api/compute/admin/test_flavors_access_negative.py b/tempest/api/compute/admin/test_flavors_access_negative.py
index 45ca10a..ac09cb0 100644
--- a/tempest/api/compute/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/admin/test_flavors_access_negative.py
@@ -46,7 +46,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0621c53e-d45d-40e7-951d-43e5e257b272')
     def test_flavor_access_list_with_public_flavor(self):
-        # Test to list flavor access with exceptions by querying public flavor
+        """Test listing flavor access of a public flavor should fail"""
         flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
                                     disk=self.disk, is_public='True')
         self.assertRaises(lib_exc.NotFound,
@@ -56,7 +56,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('41eaaade-6d37-4f28-9c74-f21b46ca67bd')
     def test_flavor_non_admin_add(self):
-        # Test to add flavor access as a user without admin privileges.
+        """Test adding flavor access by a non-admin user is forbidden"""
         flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
                                     disk=self.disk, is_public='False')
         self.assertRaises(lib_exc.Forbidden,
@@ -67,7 +67,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('073e79a6-c311-4525-82dc-6083d919cb3a')
     def test_flavor_non_admin_remove(self):
-        # Test to remove flavor access as a user without admin privileges.
+        """Test removing flavor access by a non-admin user should fail"""
         flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
                                     disk=self.disk, is_public='False')
 
@@ -84,6 +84,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f3592cc0-0306-483c-b210-9a7b5346eddc')
     def test_add_flavor_access_duplicate(self):
+        """Test adding duplicate flavor access to same flavor should fail"""
         # Create a new flavor.
         flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
                                     disk=self.disk, is_public='False')
@@ -104,6 +105,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1f710927-3bc7-4381-9f82-0ca6e42644b7')
     def test_remove_flavor_access_not_found(self):
+        """Test removing non existent flavor access should fail"""
         # Create a new flavor.
         flavor = self.create_flavor(ram=self.ram, vcpus=self.vcpus,
                                     disk=self.disk, is_public='False')
diff --git a/tempest/api/compute/admin/test_flavors_microversions.py b/tempest/api/compute/admin/test_flavors_microversions.py
index 31b9217..d904cbd 100644
--- a/tempest/api/compute/admin/test_flavors_microversions.py
+++ b/tempest/api/compute/admin/test_flavors_microversions.py
@@ -18,6 +18,8 @@
 
 
 class FlavorsV255TestJSON(base.BaseV2ComputeAdminTest):
+    """Test flavors API with compute microversion greater than 2.54"""
+
     min_microversion = '2.55'
     max_microversion = 'latest'
 
@@ -26,6 +28,11 @@
 
     @decorators.idempotent_id('61976b25-488d-41dc-9dcb-cb9693a7b075')
     def test_crud_flavor(self):
+        """Test create/show/update/list flavor
+
+        Check the response schema of flavors API with microversion greater
+        than 2.54.
+        """
         flavor_id = data_utils.rand_int_id(start=1000)
         # Checking create API response schema
         new_flavor_id = self.create_flavor(ram=512,
@@ -44,6 +51,7 @@
 
 
 class FlavorsV261TestJSON(FlavorsV255TestJSON):
+    """Test flavors API with compute microversion greater than 2.60"""
     min_microversion = '2.61'
     max_microversion = 'latest'
 
diff --git a/tempest/api/compute/admin/test_live_migration.py b/tempest/api/compute/admin/test_live_migration.py
index a845c72..941315e 100644
--- a/tempest/api/compute/admin/test_live_migration.py
+++ b/tempest/api/compute/admin/test_live_migration.py
@@ -71,6 +71,10 @@
 
     def _live_migrate(self, server_id, target_host, state,
                       volume_backed=False):
+        # If target_host is None, check whether source host is different with
+        # the new host after migration.
+        if target_host is None:
+            source_host = self.get_host_for_server(server_id)
         self._migrate_server_to(server_id, target_host, volume_backed)
         waiters.wait_for_server_status(self.servers_client, server_id, state)
         migration_list = (self.admin_migration_client.list_migrations()
@@ -82,8 +86,12 @@
             if (live_migration['instance_uuid'] == server_id):
                 msg += "\n%s" % live_migration
         msg += "]"
-        self.assertEqual(target_host, self.get_host_for_server(server_id),
-                         msg)
+        if target_host is None:
+            self.assertNotEqual(source_host,
+                                self.get_host_for_server(server_id), msg)
+        else:
+            self.assertEqual(target_host, self.get_host_for_server(server_id),
+                             msg)
 
 
 class LiveMigrationTest(LiveMigrationTestBase):
@@ -105,7 +113,11 @@
         server_id = self.create_test_server(wait_until="ACTIVE",
                                             volume_backed=volume_backed)['id']
         source_host = self.get_host_for_server(server_id)
-        destination_host = self.get_host_other_than(server_id)
+        if not CONF.compute_feature_enabled.can_migrate_between_any_hosts:
+            # not to specify a host so that the scheduler will pick one
+            destination_host = None
+        else:
+            destination_host = self.get_host_other_than(server_id)
 
         if state == 'PAUSED':
             self.admin_servers_client.pause_server(server_id)
@@ -123,11 +135,17 @@
             self._live_migrate(server_id, source_host, state, volume_backed)
 
     @decorators.idempotent_id('1dce86b8-eb04-4c03-a9d8-9c1dc3ee0c7b')
+    @testtools.skipUnless(CONF.compute_feature_enabled.
+                          block_migration_for_live_migration,
+                          'Block Live migration not available')
     def test_live_block_migration(self):
         """Test live migrating an active server"""
         self._test_live_migration()
 
     @decorators.idempotent_id('1e107f21-61b2-4988-8f22-b196e938ab88')
+    @testtools.skipUnless(CONF.compute_feature_enabled.
+                          block_migration_for_live_migration,
+                          'Block Live migration not available')
     @testtools.skipUnless(CONF.compute_feature_enabled.pause,
                           'Pause is not available.')
     def test_live_block_migration_paused(self):
@@ -155,7 +173,11 @@
         """Test live migrating a server with volume attached"""
         server = self.create_test_server(wait_until="ACTIVE")
         server_id = server['id']
-        target_host = self.get_host_other_than(server_id)
+        if not CONF.compute_feature_enabled.can_migrate_between_any_hosts:
+            # not to specify a host so that the scheduler will pick one
+            target_host = None
+        else:
+            target_host = self.get_host_other_than(server_id)
 
         volume = self.create_volume()
 
diff --git a/tempest/api/compute/admin/test_live_migration_negative.py b/tempest/api/compute/admin/test_live_migration_negative.py
index 8327a3b..80c0525 100644
--- a/tempest/api/compute/admin/test_live_migration_negative.py
+++ b/tempest/api/compute/admin/test_live_migration_negative.py
@@ -24,6 +24,8 @@
 
 
 class LiveMigrationNegativeTest(base.BaseV2ComputeAdminTest):
+    """Negative tests of live migration"""
+
     @classmethod
     def skip_checks(cls):
         super(LiveMigrationNegativeTest, cls).skip_checks()
@@ -40,7 +42,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7fb7856e-ae92-44c9-861a-af62d7830bcb')
     def test_invalid_host_for_migration(self):
-        # Migrating to an invalid host should not change the status
+        """Test migrating to an invalid host should not change the status"""
         target_host = data_utils.rand_name('host')
         server = self.create_test_server(wait_until="ACTIVE")
 
@@ -52,6 +54,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6e2f94f5-2ee8-4830-bef5-5bc95bb0795b')
     def test_live_block_migration_suspended(self):
+        """Test migrating a suspended server should not change the status"""
         server = self.create_test_server(wait_until="ACTIVE")
 
         self.admin_servers_client.suspend_server(server['id'])
diff --git a/tempest/api/compute/admin/test_quotas.py b/tempest/api/compute/admin/test_quotas.py
index 0060ffe..9d5e0c9 100644
--- a/tempest/api/compute/admin/test_quotas.py
+++ b/tempest/api/compute/admin/test_quotas.py
@@ -97,9 +97,11 @@
 
 
 class QuotasAdminTestJSON(QuotasAdminTestBase):
+    """Test compute quotas by admin user"""
+
     @decorators.idempotent_id('3b0a7c8f-cf58-46b8-a60c-715a32a8ba7d')
     def test_get_default_quotas(self):
-        # Admin can get the default resource quota set for a tenant
+        """Test admin can get the default compute quota set for a project"""
         expected_quota_set = self.default_quota_set | set(['id'])
         quota_set = self.adm_client.show_default_quota_set(
             self.demo_tenant_id)['quota_set']
@@ -109,7 +111,7 @@
 
     @decorators.idempotent_id('55fbe2bf-21a9-435b-bbd2-4162b0ed799a')
     def test_update_all_quota_resources_for_tenant(self):
-        # Admin can update all the resource quota limits for a tenant
+        """Test admin can update all the compute quota limits for a project"""
         default_quota_set = self.adm_client.show_default_quota_set(
             self.demo_tenant_id)['quota_set']
         new_quota_set = {'metadata_items': 256, 'ram': 10240,
@@ -140,11 +142,12 @@
     # TODO(afazekas): merge these test cases
     @decorators.idempotent_id('ce9e0815-8091-4abd-8345-7fe5b85faa1d')
     def test_get_updated_quotas(self):
+        """Test that GET shows the updated quota set of project"""
         self._get_updated_quotas()
 
     @decorators.idempotent_id('389d04f0-3a41-405f-9317-e5f86e3c44f0')
     def test_delete_quota(self):
-        # Admin can delete the resource quota set for a project
+        """Test admin can delete the compute quota set for a project"""
         project_name = data_utils.rand_name('ram_quota_project')
         project_desc = project_name + '-desc'
         project = identity.identity_utils(self.os_admin).create_project(
@@ -165,26 +168,40 @@
 
 
 class QuotasAdminTestV236(QuotasAdminTestBase):
-    min_microversion = '2.36'
+    """Test compute quotas with microversion greater than 2.35
+
     # NOTE(gmann): This test tests the Quota APIs response schema
     # for 2.36 microversion. No specific assert or behaviour verification
     # is needed.
+    """
+
+    min_microversion = '2.36'
 
     @decorators.idempotent_id('4268b5c9-92e5-4adc-acf1-3a2798f3d803')
     def test_get_updated_quotas(self):
-        # Checking Quota update, get, get details APIs response schema
+        """Test compute quotas API with microversion greater than 2.35
+
+        Checking compute quota update, get, get details APIs response schema.
+        """
         self._get_updated_quotas()
 
 
 class QuotasAdminTestV257(QuotasAdminTestBase):
-    min_microversion = '2.57'
+    """Test compute quotas with microversion greater than 2.56
+
     # NOTE(gmann): This test tests the Quota APIs response schema
     # for 2.57 microversion. No specific assert or behaviour verification
     # is needed.
+    """
+
+    min_microversion = '2.57'
 
     @decorators.idempotent_id('e641e6c6-e86c-41a4-9e5c-9493c0ae47ad')
     def test_get_updated_quotas(self):
-        # Checking Quota update, get, get details APIs response schema
+        """Test compute quotas API with microversion greater than 2.56
+
+        Checking compute quota update, get, get details APIs response schema.
+        """
         self._get_updated_quotas()
 
 
@@ -212,6 +229,7 @@
     # 'danger' flag.
     @decorators.idempotent_id('7932ab0f-5136-4075-b201-c0e2338df51a')
     def test_update_default_quotas(self):
+        """Test updating default compute quota class set"""
         # get the current 'default' quota class values
         body = (self.adm_client.show_quota_class_set('default')
                 ['quota_class_set'])
diff --git a/tempest/api/compute/admin/test_server_diagnostics_negative.py b/tempest/api/compute/admin/test_server_diagnostics_negative.py
index 6215c37..8f14cbc 100644
--- a/tempest/api/compute/admin/test_server_diagnostics_negative.py
+++ b/tempest/api/compute/admin/test_server_diagnostics_negative.py
@@ -18,6 +18,7 @@
 
 
 class ServerDiagnosticsNegativeTest(base.BaseV2ComputeAdminTest):
+    """Negative tests of server diagnostics"""
 
     @classmethod
     def setup_clients(cls):
@@ -27,7 +28,10 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e84e2234-60d2-42fa-8b30-e2d3049724ac')
     def test_get_server_diagnostics_by_non_admin(self):
-        # Non-admin user cannot view server diagnostics according to policy
+        """Test getting server diagnostics by non-admin user is forbidden
+
+        Non-admin user cannot view server diagnostics according to policy.
+        """
         server_id = self.create_test_server(wait_until='ACTIVE')['id']
         self.assertRaises(lib_exc.Forbidden,
                           self.client.show_server_diagnostics, server_id)
diff --git a/tempest/api/compute/admin/test_servers.py b/tempest/api/compute/admin/test_servers.py
index 170b2cc..ab1b49a 100644
--- a/tempest/api/compute/admin/test_servers.py
+++ b/tempest/api/compute/admin/test_servers.py
@@ -14,10 +14,13 @@
 
 from tempest.api.compute import base
 from tempest.common import waiters
+from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
+CONF = config.CONF
+
 
 class ServersAdminTestJSON(base.BaseV2ComputeAdminTest):
     """Tests Servers API using admin privileges"""
@@ -45,7 +48,7 @@
 
     @decorators.idempotent_id('06f960bb-15bb-48dc-873d-f96e89be7870')
     def test_list_servers_filter_by_error_status(self):
-        # Filter the list of servers by server error status
+        """Test filtering the list of servers by server error status"""
         params = {'status': 'error'}
         self.client.reset_state(self.s1_id, state='error')
         body = self.non_admin_client.list_servers(**params)
@@ -61,6 +64,7 @@
 
     @decorators.idempotent_id('d56e9540-73ed-45e0-9b88-98fc419087eb')
     def test_list_servers_detailed_filter_by_invalid_status(self):
+        """Test filtering the list of servers by invalid server status"""
         params = {'status': 'invalid_status'}
         if self.is_requested_microversion_compatible('2.37'):
             body = self.client.list_servers(detail=True, **params)
@@ -72,8 +76,11 @@
 
     @decorators.idempotent_id('51717b38-bdc1-458b-b636-1cf82d99f62f')
     def test_list_servers_by_admin(self):
-        # Listing servers by admin user returns a list which doesn't
-        # contain the other tenants' server by default
+        """Test listing servers by admin without other projects
+
+        Listing servers by admin user returns a list which doesn't
+        contain the other projects' server by default.
+        """
         body = self.client.list_servers(detail=True)
         servers = body['servers']
 
@@ -85,8 +92,11 @@
 
     @decorators.idempotent_id('9f5579ae-19b4-4985-a091-2a5d56106580')
     def test_list_servers_by_admin_with_all_tenants(self):
-        # Listing servers by admin user with all tenants parameter
-        # Here should be listed all servers
+        """Test listing servers by admin with all tenants
+
+        Listing servers by admin user with all tenants parameter,
+        all servers should be listed.
+        """
         params = {'all_tenants': ''}
         body = self.client.list_servers(detail=True, **params)
         servers = body['servers']
@@ -98,8 +108,10 @@
     @decorators.related_bug('1659811')
     @decorators.idempotent_id('7e5d6b8f-454a-4ba1-8ae2-da857af8338b')
     def test_list_servers_by_admin_with_specified_tenant(self):
-        # In nova v2, tenant_id is ignored unless all_tenants is specified
+        """Test listing servers by admin with specified project
 
+        In nova v2, tenant_id is ignored unless all_tenants is specified.
+        """
         # List the primary tenant but get nothing due to odd specified behavior
         tenant_id = self.non_admin_client.tenant_id
         params = {'tenant_id': tenant_id}
@@ -128,7 +140,7 @@
 
     @decorators.idempotent_id('86c7a8f7-50cf-43a9-9bac-5b985317134f')
     def test_list_servers_filter_by_exist_host(self):
-        # Filter the list of servers by existent host
+        """Test filtering the list of servers by existent host"""
         server = self.client.show_server(self.s1_id)['server']
         hostname = server['OS-EXT-SRV-ATTR:host']
         params = {'host': hostname, 'all_tenants': '1'}
@@ -144,6 +156,7 @@
 
     @decorators.idempotent_id('ee8ae470-db70-474d-b752-690b7892cab1')
     def test_reset_state_server(self):
+        """Test resetting server state to error/active"""
         # Reset server's state to 'error'
         self.client.reset_state(self.s1_id, state='error')
 
@@ -160,9 +173,11 @@
 
     @decorators.idempotent_id('682cb127-e5bb-4f53-87ce-cb9003604442')
     def test_rebuild_server_in_error_state(self):
-        # The server in error state should be rebuilt using the provided
-        # image and changed to ACTIVE state
+        """Test rebuilding server in error state
 
+        The server in error state should be rebuilt using the provided
+        image and changed to ACTIVE state.
+        """
         # resetting vm state require admin privilege
         self.client.reset_state(self.s1_id, state='error')
         rebuilt_server = self.non_admin_client.rebuild_server(
@@ -188,6 +203,11 @@
 
     @decorators.idempotent_id('7a1323b4-a6a2-497a-96cb-76c07b945c71')
     def test_reset_network_inject_network_info(self):
+        """Test resetting and injecting network info of a server"""
+        if not CONF.compute_feature_enabled.xenapi_apis:
+            raise self.skipException(
+                'The resetNetwork server action is not supported.')
+
         # Reset Network of a Server
         server = self.create_test_server(wait_until='ACTIVE')
         self.client.reset_network(server['id'])
@@ -196,6 +216,7 @@
 
     @decorators.idempotent_id('fdcd9b33-0903-4e00-a1f7-b5f6543068d6')
     def test_create_server_with_scheduling_hint(self):
+        """Test creating server with scheduling hint"""
         # Create a server with scheduler hints.
         hints = {
             'same_host': self.s1_id
diff --git a/tempest/api/compute/admin/test_servers_negative.py b/tempest/api/compute/admin/test_servers_negative.py
index f720b84..f52d4c0 100644
--- a/tempest/api/compute/admin/test_servers_negative.py
+++ b/tempest/api/compute/admin/test_servers_negative.py
@@ -26,7 +26,7 @@
 
 
 class ServersAdminNegativeTestJSON(base.BaseV2ComputeAdminTest):
-    """Tests Servers API using admin privileges"""
+    """Negative Tests of Servers API using admin privileges"""
 
     @classmethod
     def setup_clients(cls):
@@ -47,6 +47,7 @@
                           'Resize not available.')
     @decorators.attr(type=['negative'])
     def test_resize_server_using_overlimit_ram(self):
+        """Test resizing server using over limit ram should fail"""
         # NOTE(mriedem): Avoid conflicts with os-quota-class-sets tests.
         self.useFixture(fixtures.LockFixture('compute_quotas'))
         quota_set = self.quotas_client.show_quota_set(
@@ -69,6 +70,7 @@
                           'Resize not available.')
     @decorators.attr(type=['negative'])
     def test_resize_server_using_overlimit_vcpus(self):
+        """Test resizing server using over limit vcpus should fail"""
         # NOTE(mriedem): Avoid conflicts with os-quota-class-sets tests.
         self.useFixture(fixtures.LockFixture('compute_quotas'))
         quota_set = self.quotas_client.show_quota_set(
@@ -89,6 +91,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b0b4d8af-1256-41ef-9ee7-25f1c19dde80')
     def test_reset_state_server_invalid_state(self):
+        """Test resetting server state to invalid state value should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.client.reset_state, self.s1_id,
                           state='invalid')
@@ -96,6 +99,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('4cdcc984-fab0-4577-9a9d-6d558527ee9d')
     def test_reset_state_server_invalid_type(self):
+        """Test resetting server state to invalid state type should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.client.reset_state, self.s1_id,
                           state=1)
@@ -103,13 +107,14 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e741298b-8df2-46f0-81cb-8f814ff2504c')
     def test_reset_state_server_nonexistent_server(self):
+        """Test resetting a non existent server's state should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.client.reset_state, '999', state='error')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('46a4e1ca-87ae-4d28-987a-1b6b136a0221')
     def test_migrate_non_existent_server(self):
-        # migrate a non existent server
+        """Test migrating a non existent server should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.client.migrate_server,
                           data_utils.rand_uuid())
@@ -121,6 +126,7 @@
                           'Suspend is not available.')
     @decorators.attr(type=['negative'])
     def test_migrate_server_invalid_state(self):
+        """Test migrating a server with invalid state should fail"""
         # create server.
         server = self.create_test_server(wait_until='ACTIVE')
         server_id = server['id']
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage.py b/tempest/api/compute/admin/test_simple_tenant_usage.py
index d4c60b3..c24f420 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage.py
@@ -26,6 +26,7 @@
 
 
 class TenantUsagesTestJSON(base.BaseV2ComputeAdminTest):
+    """Test tenant usages"""
 
     @classmethod
     def setup_clients(cls):
@@ -67,7 +68,7 @@
 
     @decorators.idempotent_id('062c8ae9-9912-4249-8b51-e38d664e926e')
     def test_list_usage_all_tenants(self):
-        # Get usage for all tenants
+        """Test getting usage for all tenants"""
         tenant_usage = self.call_until_valid(
             self.adm_client.list_tenant_usages, VALID_WAIT,
             start=self.start, end=self.end, detailed="1")['tenant_usages'][0]
@@ -75,7 +76,7 @@
 
     @decorators.idempotent_id('94135049-a4c5-4934-ad39-08fa7da4f22e')
     def test_get_usage_tenant(self):
-        # Get usage for a specific tenant
+        """Test getting usage for a specific tenant"""
         tenant_usage = self.call_until_valid(
             self.adm_client.show_tenant_usage, VALID_WAIT,
             self.tenant_id, start=self.start, end=self.end)['tenant_usage']
@@ -84,7 +85,7 @@
 
     @decorators.idempotent_id('9d00a412-b40e-4fd9-8eba-97b496316116')
     def test_get_usage_tenant_with_non_admin_user(self):
-        # Get usage for a specific tenant with non admin user
+        """Test getting usage for a specific tenant with non admin user"""
         tenant_usage = self.call_until_valid(
             self.client.show_tenant_usage, VALID_WAIT,
             self.tenant_id, start=self.start, end=self.end)['tenant_usage']
diff --git a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
index cb60b8d..4b5a5d5 100644
--- a/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
+++ b/tempest/api/compute/admin/test_simple_tenant_usage_negative.py
@@ -21,6 +21,7 @@
 
 
 class TenantUsagesNegativeTestJSON(base.BaseV2ComputeAdminTest):
+    """Negative tests of compute tenant usages API"""
 
     @classmethod
     def setup_clients(cls):
@@ -43,7 +44,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8b21e135-d94b-4991-b6e9-87059609c8ed')
     def test_get_usage_tenant_with_empty_tenant_id(self):
-        # Get usage for a specific tenant empty
+        """Test getting tenant usage with empty tenant id should fail"""
         params = {'start': self.start,
                   'end': self.end}
         self.assertRaises(lib_exc.NotFound,
@@ -53,7 +54,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('4079dd2a-9e8d-479f-869d-6fa985ce45b6')
     def test_get_usage_tenant_with_invalid_date(self):
-        # Get usage for tenant with invalid date
+        """Test getting tenant usage with invalid time range should fail"""
         params = {'start': self.end,
                   'end': self.start}
         self.assertRaises(lib_exc.BadRequest,
@@ -63,7 +64,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('bbe6fe2c-15d8-404c-a0a2-44fad0ad5cc7')
     def test_list_usage_all_tenants_with_non_admin_user(self):
-        # Get usage for all tenants with non admin user
+        """Test listing usage of all tenants by non-admin user is forbidden"""
         params = {'start': self.start,
                   'end': self.end,
                   'detailed': "1"}
diff --git a/tempest/api/compute/base.py b/tempest/api/compute/base.py
index 74570ce..8b847fc 100644
--- a/tempest/api/compute/base.py
+++ b/tempest/api/compute/base.py
@@ -566,17 +566,19 @@
         # the state of the volume to change to available. This is so we don't
         # error out when trying to delete the volume during teardown.
         if volume['multiattach']:
+            att = waiters.wait_for_volume_attachment_create(
+                self.volumes_client, volume['id'], server['id'])
             self.addCleanup(waiters.wait_for_volume_attachment_remove,
                             self.volumes_client, volume['id'],
-                            attachment['id'])
+                            att['attachment_id'])
         else:
             self.addCleanup(waiters.wait_for_volume_resource_status,
                             self.volumes_client, volume['id'], 'available')
+            waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                    volume['id'], 'in-use')
         # Ignore 404s on detach in case the server is deleted or the volume
         # is already detached.
         self.addCleanup(self._detach_volume, server, volume)
-        waiters.wait_for_volume_resource_status(self.volumes_client,
-                                                volume['id'], 'in-use')
         return attachment
 
     def create_volume_snapshot(self, volume_id, name=None, description=None,
diff --git a/tempest/api/compute/certificates/test_certificates.py b/tempest/api/compute/certificates/test_certificates.py
index 0e6c016..5917931 100644
--- a/tempest/api/compute/certificates/test_certificates.py
+++ b/tempest/api/compute/certificates/test_certificates.py
@@ -21,6 +21,7 @@
 
 
 class CertificatesV2TestJSON(base.BaseV2ComputeTest):
+    """Test Certificates API"""
 
     @classmethod
     def skip_checks(cls):
@@ -30,10 +31,10 @@
 
     @decorators.idempotent_id('c070a441-b08e-447e-a733-905909535b1b')
     def test_create_root_certificate(self):
-        # create certificates
+        """Test creating root certificate"""
         self.certificates_client.create_certificate()
 
     @decorators.idempotent_id('3ac273d0-92d2-4632-bdfc-afbc21d4606c')
     def test_get_root_certificate(self):
-        # get the root certificate
+        """Test getting root certificate details"""
         self.certificates_client.show_certificate('root')
diff --git a/tempest/api/compute/flavors/test_flavors.py b/tempest/api/compute/flavors/test_flavors.py
index 58861a1..9ab75c5 100644
--- a/tempest/api/compute/flavors/test_flavors.py
+++ b/tempest/api/compute/flavors/test_flavors.py
@@ -28,6 +28,9 @@
         flavor = self.flavors_client.show_flavor(self.flavor_ref)['flavor']
         flavor_min_detail = {'id': flavor['id'], 'links': flavor['links'],
                              'name': flavor['name']}
+        # description field is added to the response of list_flavors in 2.55
+        if not self.is_requested_microversion_compatible('2.54'):
+            flavor_min_detail.update({'description': flavor['description']})
         self.assertIn(flavor_min_detail, flavors)
 
     @decorators.idempotent_id('6e85fde4-b3cd-4137-ab72-ed5f418e8c24')
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions.py b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
index 2adc482..6097bbc 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions.py
@@ -25,13 +25,13 @@
 
 
 class FloatingIPsTestJSON(base.BaseFloatingIPsTest):
+    """Test floating ips API with compute microversion less than 2.36"""
 
     max_microversion = '2.35'
 
     @decorators.idempotent_id('f7bfb946-297e-41b8-9e8c-aba8e9bb5194')
     def test_allocate_floating_ip(self):
-        # Positive test:Allocation of a new floating IP to a project
-        # should be successful
+        """Test allocating a floating ip to a project"""
         body = self.client.create_floating_ip(
             pool=CONF.network.floating_network_name)['floating_ip']
         floating_ip_id_allocated = body['id']
@@ -45,8 +45,7 @@
 
     @decorators.idempotent_id('de45e989-b5ca-4a9b-916b-04a52e7bbb8b')
     def test_delete_floating_ip(self):
-        # Positive test:Deletion of valid floating IP from project
-        # should be successful
+        """Test deleting a valid floating ip from project"""
         # Creating the floating IP that is to be deleted in this method
         floating_ip_body = self.client.create_floating_ip(
             pool=CONF.network.floating_network_name)['floating_ip']
@@ -59,6 +58,7 @@
 
 
 class FloatingIPsAssociationTestJSON(base.BaseFloatingIPsTest):
+    """Test floating ips association with microversion less than 2.44"""
 
     max_microversion = '2.43'
 
@@ -80,9 +80,7 @@
     @testtools.skipUnless(CONF.network.public_network_id,
                           'The public_network_id option must be specified.')
     def test_associate_disassociate_floating_ip(self):
-        # Positive test:Associate and disassociate the provided floating IP
-        # to a specific server should be successful
-
+        """Test associate/disassociate floating ip to a server"""
         # Association of floating IP to fixed IP address
         self.client.associate_floating_ip_to_server(
             self.floating_ip,
@@ -102,6 +100,12 @@
     @testtools.skipUnless(CONF.network.public_network_id,
                           'The public_network_id option must be specified.')
     def test_associate_already_associated_floating_ip(self):
+        """Test associating an already associated floating ip
+
+        First associate a floating ip to server1, then associate the floating
+        ip to server2, the floating ip will be associated to server2 and no
+        longer associated to server1.
+        """
         # positive test:Association of an already associated floating IP
         # to specific server should change the association of the Floating IP
         # Create server so as to use for Multiple association
diff --git a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
index 9257458..e99e218 100644
--- a/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
+++ b/tempest/api/compute/floating_ips/test_floating_ips_actions_negative.py
@@ -25,6 +25,7 @@
 
 
 class FloatingIPsNegativeTestJSON(base.BaseFloatingIPsTest):
+    """Test floating ips API with compute microversion less than 2.36"""
 
     max_microversion = '2.35'
 
@@ -46,8 +47,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6e0f059b-e4dd-48fb-8207-06e3bba5b074')
     def test_allocate_floating_ip_from_nonexistent_pool(self):
-        # Negative test:Allocation of a new floating IP from a nonexistent_pool
-        # to a project should fail
+        """Test allocating floating ip from non existent pool should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.client.create_floating_ip,
                           pool="non_exist_pool")
@@ -55,15 +55,14 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ae1c55a8-552b-44d4-bfb6-2a115a15d0ba')
     def test_delete_nonexistent_floating_ip(self):
-        # Negative test:Deletion of a nonexistent floating IP
-        # from project should fail
-
+        """Test deleting non existent floating ip should fail"""
         # Deleting the non existent floating IP
         self.assertRaises(lib_exc.NotFound, self.client.delete_floating_ip,
                           self.non_exist_id)
 
 
 class FloatingIPsAssociationNegativeTestJSON(base.BaseFloatingIPsTest):
+    """Test floating ips API with compute microversion less than 2.44"""
 
     max_microversion = '2.43'
 
@@ -76,8 +75,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('595fa616-1a71-4670-9614-46564ac49a4c')
     def test_associate_nonexistent_floating_ip(self):
-        # Negative test:Association of a non existent floating IP
-        # to specific server should fail
+        """Test associating non existent floating ip to server should fail"""
         # Associating non existent floating IP
         self.assertRaises(lib_exc.NotFound,
                           self.client.associate_floating_ip_to_server,
@@ -86,7 +84,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0a081a66-e568-4e6b-aa62-9587a876dca8')
     def test_dissociate_nonexistent_floating_ip(self):
-        # Negative test:Dissociation of a non existent floating IP should fail
+        """Test dissociating non existent floating ip should fail"""
         # Dissociating non existent floating IP
         self.assertRaises(lib_exc.NotFound,
                           self.client.disassociate_floating_ip_from_server,
@@ -95,7 +93,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('804b4fcb-bbf5-412f-925d-896672b61eb3')
     def test_associate_ip_to_server_without_passing_floating_ip(self):
-        # Negative test:Association of empty floating IP to specific server
+        """Test associating empty floating ip to server should fail"""
         # should raise NotFound or BadRequest(In case of Nova V2.1) exception.
         self.assertRaises((lib_exc.NotFound, lib_exc.BadRequest),
                           self.client.associate_floating_ip_to_server,
@@ -106,10 +104,13 @@
     @testtools.skipUnless(CONF.network.public_network_id,
                           'The public_network_id option must be specified.')
     def test_associate_ip_to_server_with_floating_ip(self):
-        # The VM have one port
-        # Associate floating IP A to the VM
-        # Associate floating IP B which is from same pool with floating IP A
-        # to the VM, should raise BadRequest exception
+        """Test associating floating ip to server already with floating ip
+
+        1. The VM have one port
+        2. Associate floating IP A to the VM
+        3. Associate floating IP B which is from same pool with floating IP A
+           to the VM, should raise BadRequest exception
+        """
         body = self.client.create_floating_ip(
             pool=CONF.network.public_network_id)['floating_ip']
         self.addCleanup(self.client.delete_floating_ip, body['id'])
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips.py b/tempest/api/compute/floating_ips/test_list_floating_ips.py
index 944f798..6bfee95 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips.py
@@ -21,6 +21,7 @@
 
 
 class FloatingIPDetailsTestJSON(base.BaseFloatingIPsTest):
+    """Test floating ip details with compute microversion less than 2.36"""
 
     max_microversion = '2.35'
 
@@ -37,7 +38,7 @@
 
     @decorators.idempotent_id('16db31c3-fb85-40c9-bbe2-8cf7b67ff99f')
     def test_list_floating_ips(self):
-        # Positive test:Should return the list of floating IPs
+        """Test listing floating ips"""
         body = self.client.list_floating_ips()['floating_ips']
         floating_ips = body
         self.assertNotEmpty(floating_ips,
@@ -47,7 +48,7 @@
 
     @decorators.idempotent_id('eef497e0-8ff7-43c8-85ef-558440574f84')
     def test_get_floating_ip_details(self):
-        # Positive test:Should be able to GET the details of floatingIP
+        """Test getting floating ip details"""
         # Creating a floating IP for which details are to be checked
         body = self.client.create_floating_ip(
             pool=CONF.network.floating_network_name)['floating_ip']
@@ -68,7 +69,7 @@
 
     @decorators.idempotent_id('df389fc8-56f5-43cc-b290-20eda39854d3')
     def test_list_floating_ip_pools(self):
-        # Positive test:Should return the list of floating IP Pools
+        """Test listing floating ip pools"""
         floating_ip_pools = self.pools_client.list_floating_ip_pools()
         self.assertNotEmpty(floating_ip_pools['floating_ip_pools'],
                             "Expected floating IP Pools. Got zero.")
diff --git a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
index d69248c..aa0320d 100644
--- a/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
+++ b/tempest/api/compute/floating_ips/test_list_floating_ips_negative.py
@@ -23,14 +23,18 @@
 
 
 class FloatingIPDetailsNegativeTestJSON(base.BaseFloatingIPsTest):
+    """Negative tests of floating ip detail
+
+    Negative tests of floating ip detail with compute microversion less
+    than 2.36.
+    """
 
     max_microversion = '2.35'
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7ab18834-4a4b-4f28-a2c5-440579866695')
     def test_get_nonexistent_floating_ip_details(self):
-        # Negative test:Should not be able to GET the details
-        # of non-existent floating IP
+        """Test getting non existent floating ip should fail"""
         # Creating a non-existent floatingIP id
         if CONF.service_available.neutron:
             non_exist_id = data_utils.rand_uuid()
diff --git a/tempest/api/compute/images/test_image_metadata.py b/tempest/api/compute/images/test_image_metadata.py
index 1f3af5f..561265f 100644
--- a/tempest/api/compute/images/test_image_metadata.py
+++ b/tempest/api/compute/images/test_image_metadata.py
@@ -28,6 +28,8 @@
 
 
 class ImagesMetadataTestJSON(base.BaseV2ComputeTest):
+    """Test image metadata with compute microversion less than 2.39"""
+
     max_microversion = '2.38'
 
     @classmethod
@@ -89,7 +91,10 @@
 
     @decorators.idempotent_id('37ec6edd-cf30-4c53-bd45-ae74db6b0531')
     def test_list_image_metadata(self):
-        # All metadata key/value pairs for an image should be returned
+        """Test listing image metadata
+
+        All metadata key/value pairs for an image should be returned.
+        """
         resp_metadata = self.client.list_image_metadata(self.image_id)
         expected = {'metadata': {
             'os_version': 'value1', 'os_distro': 'value2'}}
@@ -97,7 +102,10 @@
 
     @decorators.idempotent_id('ece7befc-d3ce-42a4-b4be-c3067a418c29')
     def test_set_image_metadata(self):
-        # The metadata for the image should match the new values
+        """Test setting image metadata
+
+        The metadata for the image should match the new values.
+        """
         req_metadata = {'os_version': 'value2', 'architecture': 'value3'}
         self.client.set_image_metadata(self.image_id,
                                        req_metadata)
@@ -108,7 +116,10 @@
 
     @decorators.idempotent_id('7b491c11-a9d5-40fe-a696-7f7e03d3fea2')
     def test_update_image_metadata(self):
-        # The metadata for the image should match the updated values
+        """Test updating image medata
+
+        The metadata for the image should match the updated values.
+        """
         req_metadata = {'os_version': 'alt1', 'architecture': 'value3'}
         self.client.update_image_metadata(self.image_id,
                                           req_metadata)
@@ -122,15 +133,21 @@
 
     @decorators.idempotent_id('4f5db52f-6685-4c75-b848-f4bb363f9aa6')
     def test_get_image_metadata_item(self):
-        # The value for a specific metadata key should be returned
+        """Test getting image metadata item
+
+        The value for a specific metadata key should be returned.
+        """
         meta = self.client.show_image_metadata_item(self.image_id,
                                                     'os_distro')['meta']
         self.assertEqual('value2', meta['os_distro'])
 
     @decorators.idempotent_id('f2de776a-4778-4d90-a5da-aae63aee64ae')
     def test_set_image_metadata_item(self):
-        # The value provided for the given meta item should be set for
-        # the image
+        """Test setting image metadata item
+
+        The value provided for the given meta item should be set for
+        the image.
+        """
         meta = {'os_version': 'alt'}
         self.client.set_image_metadata_item(self.image_id,
                                             'os_version', meta)
@@ -140,7 +157,10 @@
 
     @decorators.idempotent_id('a013796c-ba37-4bb5-8602-d944511def14')
     def test_delete_image_metadata_item(self):
-        # The metadata value/key pair should be deleted from the image
+        """Test deleting image metadata item
+
+        The metadata value/key pair should be deleted from the image.
+        """
         self.client.delete_image_metadata_item(self.image_id,
                                                'os_version')
         resp_metadata = self.client.list_image_metadata(self.image_id)
diff --git a/tempest/api/compute/images/test_image_metadata_negative.py b/tempest/api/compute/images/test_image_metadata_negative.py
index 407fb08..b9806c7 100644
--- a/tempest/api/compute/images/test_image_metadata_negative.py
+++ b/tempest/api/compute/images/test_image_metadata_negative.py
@@ -20,6 +20,11 @@
 
 
 class ImagesMetadataNegativeTestJSON(base.BaseV2ComputeTest):
+    """Negative tests of image metadata
+
+    Negative tests of image metadata with compute microversion less than 2.39.
+    """
+
     max_microversion = '2.38'
 
     @classmethod
@@ -30,15 +35,14 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('94069db2-792f-4fa8-8bd3-2271a6e0c095')
     def test_list_nonexistent_image_metadata(self):
-        # Negative test: List on nonexistent image
-        # metadata should not happen
+        """Test listing metadata of a non existence image should fail"""
         self.assertRaises(lib_exc.NotFound, self.client.list_image_metadata,
                           data_utils.rand_uuid())
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a403ef9e-9f95-427c-b70a-3ce3388796f1')
     def test_update_nonexistent_image_metadata(self):
-        # Negative test:An update should not happen for a non-existent image
+        """Test updating metadata of a non existence image should fail"""
         meta = {'os_distro': 'alt1', 'os_version': 'alt2'}
         self.assertRaises(lib_exc.NotFound,
                           self.client.update_image_metadata,
@@ -47,7 +51,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('41ae052c-6ee6-405c-985e-5712393a620d')
     def test_get_nonexistent_image_metadata_item(self):
-        # Negative test: Get on non-existent image should not happen
+        """Test getting metadata of a non existence image should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.client.show_image_metadata_item,
                           data_utils.rand_uuid(), 'os_version')
@@ -55,7 +59,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('dc64f2ce-77e8-45b0-88c8-e15041d08eaf')
     def test_set_nonexistent_image_metadata(self):
-        # Negative test: Metadata should not be set to a non-existent image
+        """Test setting metadata of a non existence image should fail"""
         meta = {'os_distro': 'alt1', 'os_version': 'alt2'}
         self.assertRaises(lib_exc.NotFound, self.client.set_image_metadata,
                           data_utils.rand_uuid(), meta)
@@ -63,8 +67,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('2154fd03-ab54-457c-8874-e6e3eb56e9cf')
     def test_set_nonexistent_image_metadata_item(self):
-        # Negative test: Metadata item should not be set to a
-        # nonexistent image
+        """Test setting metadata item of a non existence image should fail"""
         meta = {'os_distro': 'alt'}
         self.assertRaises(lib_exc.NotFound,
                           self.client.set_image_metadata_item,
@@ -74,8 +77,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('848e157f-6bcf-4b2e-a5dd-5124025a8518')
     def test_delete_nonexistent_image_metadata_item(self):
-        # Negative test: Shouldn't be able to delete metadata
-        # item from non-existent image
+        """Test deleting metadata item of a non existence image should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.client.delete_image_metadata_item,
                           data_utils.rand_uuid(), 'os_distro')
diff --git a/tempest/api/compute/keypairs/test_keypairs.py b/tempest/api/compute/keypairs/test_keypairs.py
index 66abb21..8df2e84 100644
--- a/tempest/api/compute/keypairs/test_keypairs.py
+++ b/tempest/api/compute/keypairs/test_keypairs.py
@@ -19,11 +19,16 @@
 
 
 class KeyPairsV2TestJSON(base.BaseKeypairTest):
+    """Test keypairs API with compute microversion less than 2.2"""
+
     max_microversion = '2.1'
 
     @decorators.idempotent_id('1d1dbedb-d7a0-432a-9d09-83f543c3c19b')
     def test_keypairs_create_list_delete(self):
-        # Keypairs created should be available in the response list
+        """Test create/list/delete keypairs
+
+        Keypairs created should be available in the response list
+        """
         # Create 3 keypairs
         key_list = list()
         for _ in range(3):
@@ -48,7 +53,7 @@
 
     @decorators.idempotent_id('6c1d3123-4519-4742-9194-622cb1714b7d')
     def test_keypair_create_delete(self):
-        # Keypair should be created, verified and deleted
+        """Test create/delete keypair"""
         k_name = data_utils.rand_name('keypair')
         keypair = self.create_keypair(k_name)
         key_name = keypair['name']
@@ -58,7 +63,7 @@
 
     @decorators.idempotent_id('a4233d5d-52d8-47cc-9a25-e1864527e3df')
     def test_get_keypair_detail(self):
-        # Keypair should be created, Got details by name and deleted
+        """Test getting keypair detail by keypair name"""
         k_name = data_utils.rand_name('keypair')
         self.create_keypair(k_name)
         keypair_detail = self.keypairs_client.show_keypair(k_name)['keypair']
@@ -68,7 +73,7 @@
 
     @decorators.idempotent_id('39c90c6a-304a-49dd-95ec-2366129def05')
     def test_keypair_create_with_pub_key(self):
-        # Keypair should be created with a given public key
+        """Test creating keypair with a given public key"""
         k_name = data_utils.rand_name('keypair')
         pub_key = ("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCs"
                    "Ne3/1ILNCqFyfYWDeTKLD6jEXC2OQHLmietMWW+/vd"
diff --git a/tempest/api/compute/keypairs/test_keypairs_negative.py b/tempest/api/compute/keypairs/test_keypairs_negative.py
index 81635ca..40bea3f 100644
--- a/tempest/api/compute/keypairs/test_keypairs_negative.py
+++ b/tempest/api/compute/keypairs/test_keypairs_negative.py
@@ -21,10 +21,12 @@
 
 
 class KeyPairsNegativeTestJSON(base.BaseKeypairTest):
+    """Negative tests of keypairs API"""
+
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('29cca892-46ae-4d48-bc32-8fe7e731eb81')
     def test_keypair_create_with_invalid_pub_key(self):
-        # Keypair should not be created with a non RSA public key
+        """Test keypair should not be created with a non RSA public key"""
         pub_key = "ssh-rsa JUNK nova@ubuntu"
         self.assertRaises(lib_exc.BadRequest,
                           self.create_keypair, pub_key=pub_key)
@@ -32,7 +34,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7cc32e47-4c42-489d-9623-c5e2cb5a2fa5')
     def test_keypair_delete_nonexistent_key(self):
-        # Non-existent key deletion should throw a proper error
+        """Test non-existent key deletion should throw a proper error"""
         k_name = data_utils.rand_name("keypair-non-existent")
         self.assertRaises(lib_exc.NotFound,
                           self.keypairs_client.delete_keypair,
@@ -41,7 +43,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('dade320e-69ca-42a9-ba4a-345300f127e0')
     def test_create_keypair_with_empty_public_key(self):
-        # Keypair should not be created with an empty public key
+        """Test keypair should not be created with an empty public key"""
         pub_key = ' '
         self.assertRaises(lib_exc.BadRequest, self.create_keypair,
                           pub_key=pub_key)
@@ -49,7 +51,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('fc100c19-2926-4b9c-8fdc-d0589ee2f9ff')
     def test_create_keypair_when_public_key_bits_exceeds_maximum(self):
-        # Keypair should not be created when public key bits are too long
+        """Test keypair should not be created when public key are too long"""
         pub_key = 'ssh-rsa ' + 'A' * 2048 + ' openstack@ubuntu'
         self.assertRaises(lib_exc.BadRequest, self.create_keypair,
                           pub_key=pub_key)
@@ -57,7 +59,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0359a7f1-f002-4682-8073-0c91e4011b7c')
     def test_create_keypair_with_duplicate_name(self):
-        # Keypairs with duplicate names should not be created
+        """Test keypairs with duplicate names should not be created"""
         k_name = data_utils.rand_name('keypair')
         self.keypairs_client.create_keypair(name=k_name)
         # Now try the same keyname to create another key
@@ -68,14 +70,14 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1398abe1-4a84-45fb-9294-89f514daff00')
     def test_create_keypair_with_empty_name_string(self):
-        # Keypairs with name being an empty string should not be created
+        """Test keypairs with empty name should not be created"""
         self.assertRaises(lib_exc.BadRequest, self.create_keypair,
                           '')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('3faa916f-779f-4103-aca7-dc3538eee1b7')
     def test_create_keypair_with_long_keynames(self):
-        # Keypairs with name longer than 255 chars should not be created
+        """Test keypairs with name longer than 255 should not be created"""
         k_name = 'keypair-'.ljust(260, '0')
         self.assertRaises(lib_exc.BadRequest, self.create_keypair,
                           k_name)
@@ -83,7 +85,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('45fbe5e0-acb5-49aa-837a-ff8d0719db91')
     def test_create_keypair_invalid_name(self):
-        # Keypairs with name being an invalid name should not be created
+        """Test keypairs with an invalid name should not be created"""
         k_name = r'key_/.\@:'
         self.assertRaises(lib_exc.BadRequest, self.create_keypair,
                           k_name)
diff --git a/tempest/api/compute/keypairs/test_keypairs_v22.py b/tempest/api/compute/keypairs/test_keypairs_v22.py
index 1aff262..e229c37 100644
--- a/tempest/api/compute/keypairs/test_keypairs_v22.py
+++ b/tempest/api/compute/keypairs/test_keypairs_v22.py
@@ -18,6 +18,8 @@
 
 
 class KeyPairsV22TestJSON(test_keypairs.KeyPairsV2TestJSON):
+    """Test keypairs API with compute microversion greater than 2.1"""
+
     min_microversion = '2.2'
     max_microversion = 'latest'
 
@@ -43,9 +45,11 @@
 
     @decorators.idempotent_id('8726fa85-7f98-4b20-af9e-f710a4f3391c')
     def test_keypairsv22_create_list_show(self):
+        """Test create/list/show keypair"""
         self._test_keypairs_create_list_show()
 
     @decorators.idempotent_id('89d59d43-f735-441a-abcf-0601727f47b6')
     def test_keypairsv22_create_list_show_with_type(self):
+        """Test create/list/show keypair with keypair type"""
         keypair_type = 'x509'
         self._test_keypairs_create_list_show(keypair_type=keypair_type)
diff --git a/tempest/api/compute/security_groups/test_security_group_rules.py b/tempest/api/compute/security_groups/test_security_group_rules.py
index 4c99ea6..59848f6 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules.py
@@ -18,6 +18,10 @@
 
 
 class SecurityGroupRulesTestJSON(base.BaseSecurityGroupsTest):
+    """Test security group rules API
+
+    Test security group rules API with compute microversion less than 2.36.
+    """
 
     @classmethod
     def setup_clients(cls):
@@ -55,8 +59,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('850795d7-d4d3-4e55-b527-a774c0123d3a')
     def test_security_group_rules_create(self):
-        # Positive test: Creation of Security Group rule
-        # should be successful
+        """Test creating security group rules"""
         # Creating a Security Group to add rules to it
         security_group = self.create_security_group()
         securitygroup_id = security_group['id']
@@ -72,10 +75,7 @@
 
     @decorators.idempotent_id('7a01873e-3c38-4f30-80be-31a043cfe2fd')
     def test_security_group_rules_create_with_optional_cidr(self):
-        # Positive test: Creation of Security Group rule
-        # with optional argument cidr
-        # should be successful
-
+        """Test creating security group rules with optional field cidr"""
         # Creating a Security Group to add rules to it
         security_group = self.create_security_group()
         parent_group_id = security_group['id']
@@ -94,10 +94,7 @@
 
     @decorators.idempotent_id('7f5d2899-7705-4d4b-8458-4505188ffab6')
     def test_security_group_rules_create_with_optional_group_id(self):
-        # Positive test: Creation of Security Group rule
-        # with optional argument group_id
-        # should be successful
-
+        """Test creating security group rules with optional field group id"""
         # Creating a Security Group to add rules to it
         security_group = self.create_security_group()
         parent_group_id = security_group['id']
@@ -122,8 +119,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('a6154130-5a55-4850-8be4-5e9e796dbf17')
     def test_security_group_rules_list(self):
-        # Positive test: Created Security Group rules should be
-        # in the list of all rules
+        """Test listing security group rules"""
         # Creating a Security Group to add rules to it
         security_group = self.create_security_group()
         securitygroup_id = security_group['id']
@@ -159,7 +155,7 @@
 
     @decorators.idempotent_id('fc5c5acf-2091-43a6-a6ae-e42760e9ffaf')
     def test_security_group_rules_delete_when_peer_group_deleted(self):
-        # Positive test:rule will delete when peer group deleting
+        """Test security group rule gets deleted when peer group is deleted"""
         # Creating a Security Group to add rules to it
         security_group = self.create_security_group()
         sg1_id = security_group['id']
diff --git a/tempest/api/compute/security_groups/test_security_group_rules_negative.py b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
index 8283aae..3d000ca 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules_negative.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules_negative.py
@@ -20,6 +20,11 @@
 
 
 class SecurityGroupRulesNegativeTestJSON(base.BaseSecurityGroupsTest):
+    """Negative tests of security group rules API
+
+    Negative tests of security group rules API with compute microversion
+    less than 2.36.
+    """
 
     @classmethod
     def setup_clients(cls):
@@ -29,8 +34,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1d507e98-7951-469b-82c3-23f1e6b8c254')
     def test_create_security_group_rule_with_non_existent_id(self):
-        # Negative test: Creation of Security Group rule should FAIL
-        # with non existent Parent group id
+        """Test creating security group rule with non existent parent group
+
+        Negative test: Creation of security group rule should fail
+        with non existent parent group id.
+        """
         # Adding rules to the non existent Security Group id
         parent_group_id = self.generate_random_security_group_id()
         ip_protocol = 'tcp'
@@ -45,8 +53,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('2244d7e4-adb7-4ecb-9930-2d77e123ce4f')
     def test_create_security_group_rule_with_invalid_id(self):
-        # Negative test: Creation of Security Group rule should FAIL
-        # with Parent group id which is not integer
+        """Test creating security group rule with invalid parent group id
+
+        Negative test: Creation of security group rule should fail
+        with parent group id which is not integer.
+        """
         # Adding rules to the non int Security Group id
         parent_group_id = data_utils.rand_name('non_int_id')
         ip_protocol = 'tcp'
@@ -61,7 +72,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8bd56d02-3ffa-4d67-9933-b6b9a01d6089')
     def test_create_security_group_rule_duplicate(self):
-        # Negative test: Create Security Group rule duplicate should fail
+        """Test creating duplicate security group rule should fail"""
         # Creating a Security Group to add rule to it
         sg = self.create_security_group()
         # Adding rules to the created Security Group
@@ -85,8 +96,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('84c81249-9f6e-439c-9bbf-cbb0d2cddbdf')
     def test_create_security_group_rule_with_invalid_ip_protocol(self):
-        # Negative test: Creation of Security Group rule should FAIL
-        # with invalid ip_protocol
+        """Test creating security group rule with invalid ip protocol
+
+        Negative test: Creation of security group rule should fail
+        with invalid ip_protocol.
+        """
         # Creating a Security Group to add rule to it
         sg = self.create_security_group()
         # Adding rules to the created Security Group
@@ -104,8 +118,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('12bbc875-1045-4f7a-be46-751277baedb9')
     def test_create_security_group_rule_with_invalid_from_port(self):
-        # Negative test: Creation of Security Group rule should FAIL
-        # with invalid from_port
+        """Test creating security group rule with invalid from_port
+
+        Negative test: Creation of security group rule should fail
+        with invalid from_port.
+        """
         # Creating a Security Group to add rule to it
         sg = self.create_security_group()
         # Adding rules to the created Security Group
@@ -122,8 +139,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ff88804d-144f-45d1-bf59-dd155838a43a')
     def test_create_security_group_rule_with_invalid_to_port(self):
-        # Negative test: Creation of Security Group rule should FAIL
-        # with invalid to_port
+        """Test creating security group rule with invalid to_port
+
+        Negative test: Creation of security group rule should fail
+        with invalid to_port.
+        """
         # Creating a Security Group to add rule to it
         sg = self.create_security_group()
         # Adding rules to the created Security Group
@@ -140,8 +160,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('00296fa9-0576-496a-ae15-fbab843189e0')
     def test_create_security_group_rule_with_invalid_port_range(self):
-        # Negative test: Creation of Security Group rule should FAIL
-        # with invalid port range.
+        """Test creating security group rule with invalid port range
+
+        Negative test: Creation of security group rule should fail
+        with invalid port range.
+        """
         # Creating a Security Group to add rule to it.
         sg = self.create_security_group()
         # Adding a rule to the created Security Group
@@ -158,8 +181,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('56fddcca-dbb8-4494-a0db-96e9f869527c')
     def test_delete_security_group_rule_with_non_existent_id(self):
-        # Negative test: Deletion of Security Group rule should be FAIL
-        # with non existent id
+        """Test deleting non existent security group rule should fail"""
         non_existent_rule_id = self.generate_random_security_group_id()
         self.assertRaises(lib_exc.NotFound,
                           self.rules_client.delete_security_group_rule,
diff --git a/tempest/api/compute/security_groups/test_security_groups.py b/tempest/api/compute/security_groups/test_security_groups.py
index 62d5bea..671a779 100644
--- a/tempest/api/compute/security_groups/test_security_groups.py
+++ b/tempest/api/compute/security_groups/test_security_groups.py
@@ -21,6 +21,7 @@
 
 
 class SecurityGroupsTestJSON(base.BaseSecurityGroupsTest):
+    """Test security groups API with compute microversion less than 2.36"""
 
     @classmethod
     def setup_clients(cls):
@@ -30,7 +31,10 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('eb2b087d-633d-4d0d-a7bd-9e6ba35b32de')
     def test_security_groups_create_list_delete(self):
-        # Positive test:Should return the list of Security Groups
+        """Test create/list/delete security groups
+
+        Positive test: Should return the list of security groups.
+        """
         # Create 3 Security Groups
         security_group_list = []
         for _ in range(3):
@@ -60,9 +64,11 @@
 
     @decorators.idempotent_id('ecc0da4a-2117-48af-91af-993cca39a615')
     def test_security_group_create_get_delete(self):
-        # Security Group should be created, fetched and deleted
-        # with char space between name along with
-        # leading and trailing spaces
+        """Test create/get/delete security group
+
+        Security group should be created, fetched and deleted
+        with char space between name along with leading and trailing spaces.
+        """
         s_name = ' %s ' % data_utils.rand_name('securitygroup ')
         securitygroup = self.create_security_group(name=s_name)
         securitygroup_name = securitygroup['name']
@@ -80,8 +86,11 @@
 
     @decorators.idempotent_id('fe4abc0d-83f5-4c50-ad11-57a1127297a2')
     def test_server_security_groups(self):
-        # Checks that security groups may be added and linked to a server
-        # and not deleted if the server is active.
+        """Test adding security groups to a server
+
+        Checks that security groups may be added and linked to a server
+        and not deleted if the server is active.
+        """
         # Create a couple security groups that we will use
         # for the server resource this test creates
         sg = self.create_security_group()
@@ -121,7 +130,7 @@
 
     @decorators.idempotent_id('7d4e1d3c-3209-4d6d-b020-986304ebad1f')
     def test_update_security_groups(self):
-        # Update security group name and description
+        """Test updating security group name and description"""
         # Create a security group
         securitygroup = self.create_security_group()
         securitygroup_id = securitygroup['id']
@@ -139,6 +148,11 @@
 
     @decorators.idempotent_id('79517d60-535a-438f-af3d-e6feab1cbea7')
     def test_list_security_groups_by_server(self):
+        """Test listing security groups by server
+
+        Create security groups and add them to a server, then list security
+        groups by server, the added security groups should be in the list.
+        """
         # Create a couple security groups that we will use
         # for the server resource this test creates
         sg = self.create_security_group()
diff --git a/tempest/api/compute/security_groups/test_security_groups_negative.py b/tempest/api/compute/security_groups/test_security_groups_negative.py
index 9c44bb2..4607112 100644
--- a/tempest/api/compute/security_groups/test_security_groups_negative.py
+++ b/tempest/api/compute/security_groups/test_security_groups_negative.py
@@ -25,6 +25,11 @@
 
 
 class SecurityGroupsNegativeTestJSON(base.BaseSecurityGroupsTest):
+    """Negative tests of security groups API
+
+    Negative tests of security groups API with compute microversion
+    less than 2.36.
+    """
 
     @classmethod
     def setup_clients(cls):
@@ -34,8 +39,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('673eaec1-9b3e-48ed-bdf1-2786c1b9661c')
     def test_security_group_get_nonexistent_group(self):
-        # Negative test:Should not be able to GET the details
-        # of non-existent Security Group
+        """Test getting non existent security group details should fail"""
         non_exist_id = self.generate_random_security_group_id()
         self.assertRaises(lib_exc.NotFound, self.client.show_security_group,
                           non_exist_id)
@@ -45,8 +49,12 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1759c3cb-b0fc-44b7-86ce-c99236be911d')
     def test_security_group_create_with_invalid_group_name(self):
-        # Negative test: Security Group should not be created with group name
-        # as an empty string/with white spaces/chars more than 255
+        """Test creating security group with invalid group name should fail
+
+        Negative test: Security group should not be created with group name
+        as an empty string, or group name with white spaces, or group name
+        with chars more than 255.
+        """
         s_description = data_utils.rand_name('description')
         # Create Security Group with empty string as group name
         self.assertRaises(lib_exc.BadRequest,
@@ -67,9 +75,12 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('777b6f14-aca9-4758-9e84-38783cfa58bc')
     def test_security_group_create_with_invalid_group_description(self):
-        # Negative test: Security Group should not be created with description
-        # longer than 255 chars. Empty description is allowed by the API
-        # reference, however.
+        """Test creating security group with invalid group description
+
+        Negative test: Security group should not be created with description
+        longer than 255 chars. Empty description is allowed by the API
+        reference, however.
+        """
         s_name = data_utils.rand_name('securitygroup')
         # Create Security Group with group description longer than 255 chars
         s_description = 'description-'.ljust(260, '0')
@@ -82,8 +93,7 @@
                       "Neutron allows duplicate names for security groups")
     @decorators.attr(type=['negative'])
     def test_security_group_create_with_duplicate_name(self):
-        # Negative test:Security Group with duplicate name should not
-        # be created
+        """Test creating security group with duplicate name should fail"""
         s_name = data_utils.rand_name('securitygroup')
         s_description = data_utils.rand_name('description')
         self.create_security_group(name=s_name, description=s_description)
@@ -95,7 +105,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('36a1629f-c6da-4a26-b8b8-55e7e5d5cd58')
     def test_delete_the_default_security_group(self):
-        # Negative test:Deletion of the "default" Security Group should Fail
+        """Test deleting "default" security group should fail"""
         default_security_group_id = None
         body = self.client.list_security_groups()['security_groups']
         for i in range(len(body)):
@@ -110,7 +120,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6727c00b-214c-4f9e-9a52-017ac3e98411')
     def test_delete_nonexistent_security_group(self):
-        # Negative test:Deletion of a non-existent Security Group should fail
+        """Test deleting non existent security group should fail"""
         non_exist_id = self.generate_random_security_group_id()
         self.assertRaises(lib_exc.NotFound,
                           self.client.delete_security_group, non_exist_id)
@@ -118,8 +128,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1438f330-8fa4-4aeb-8a94-37c250106d7f')
     def test_delete_security_group_without_passing_id(self):
-        # Negative test:Deletion of a Security Group with out passing ID
-        # should Fail
+        """Test deleting security group passing empty group id should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.client.delete_security_group, '')
 
@@ -128,7 +137,7 @@
                       "Neutron does not check the security group ID")
     @decorators.attr(type=['negative'])
     def test_update_security_group_with_invalid_sg_id(self):
-        # Update security_group with invalid sg_id should fail
+        """Test updating security group with invalid group id should fail"""
         s_name = data_utils.rand_name('sg')
         s_description = data_utils.rand_name('description')
         # Create a non int sg_id
@@ -142,7 +151,7 @@
                       "Neutron does not check the security group name")
     @decorators.attr(type=['negative'])
     def test_update_security_group_with_invalid_sg_name(self):
-        # Update security_group with invalid sg_name should fail
+        """Test updating security group to invalid group name should fail"""
         securitygroup = self.create_security_group()
         securitygroup_id = securitygroup['id']
         # Update Security Group with group name longer than 255 chars
@@ -156,7 +165,7 @@
                       "Neutron does not check the security group description")
     @decorators.attr(type=['negative'])
     def test_update_security_group_with_invalid_sg_des(self):
-        # Update security_group with invalid sg_des should fail
+        """Test updating security group to invalid description should fail"""
         securitygroup = self.create_security_group()
         securitygroup_id = securitygroup['id']
         # Update Security Group with group description longer than 255 chars
@@ -168,7 +177,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('27edee9c-873d-4da6-a68a-3c256efebe8f')
     def test_update_non_existent_security_group(self):
-        # Update a non-existent Security Group should Fail
+        """Test updating a non existent security group should fail"""
         non_exist_id = self.generate_random_security_group_id()
         s_name = data_utils.rand_name('sg')
         s_description = data_utils.rand_name('description')
diff --git a/tempest/api/compute/servers/test_attach_interfaces.py b/tempest/api/compute/servers/test_attach_interfaces.py
index c1af6c7..0601bbe 100644
--- a/tempest/api/compute/servers/test_attach_interfaces.py
+++ b/tempest/api/compute/servers/test_attach_interfaces.py
@@ -99,6 +99,7 @@
 
 
 class AttachInterfacesTestJSON(AttachInterfacesTestBase):
+    """Test attaching interfaces"""
 
     def wait_for_port_detach(self, port_id):
         """Waits for the port's device_id to be unset.
@@ -230,6 +231,7 @@
     @decorators.idempotent_id('73fe8f02-590d-4bf1-b184-e9ca81065051')
     @utils.services('network')
     def test_create_list_show_delete_interfaces_by_network_port(self):
+        """Test create/list/show/delete interfaces by network port"""
         server, ifs, _ = self._create_server_get_interfaces()
         interface_count = len(ifs)
         self.assertGreater(interface_count, 0)
@@ -262,6 +264,7 @@
     @decorators.idempotent_id('d290c06c-f5b3-11e7-8ec8-002293781009')
     @utils.services('network')
     def test_create_list_show_delete_interfaces_by_fixed_ip(self):
+        """Test create/list/show/delete interfaces by fixed ip"""
         # NOTE(zhufl) By default only project that is admin or network owner
         # or project with role advsvc is authorised to create interfaces with
         # fixed-ip, so if we don't create network for each project, do not
@@ -290,7 +293,7 @@
 
     @decorators.idempotent_id('2f3a0127-95c7-4977-92d2-bc5aec602fb4')
     def test_reassign_port_between_servers(self):
-        """Tests the following:
+        """Tests reassigning port between servers
 
         1. Create a port in Neutron.
         2. Create two servers in Nova.
@@ -343,12 +346,15 @@
 
 
 class AttachInterfacesUnderV243Test(AttachInterfacesTestBase):
+    """Test attaching interfaces with compute microversion less than 2.44"""
+
     max_microversion = '2.43'
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('c7e0e60b-ee45-43d0-abeb-8596fd42a2f9')
     @utils.services('network')
     def test_add_remove_fixed_ip(self):
+        """Test adding and removing fixed ip from server"""
         # NOTE(zhufl) By default only project that is admin or network owner
         # or project with role advsvc is authorised to add interfaces with
         # fixed-ip, so if we don't create network for each project, do not
diff --git a/tempest/api/compute/servers/test_device_tagging.py b/tempest/api/compute/servers/test_device_tagging.py
index 8879369..a7e2187 100644
--- a/tempest/api/compute/servers/test_device_tagging.py
+++ b/tempest/api/compute/servers/test_device_tagging.py
@@ -103,6 +103,7 @@
 
 
 class TaggedBootDevicesTest(DeviceTaggingBase):
+    """Test tagged boot device with compute microversion equals 2.32"""
 
     min_microversion = '2.32'
     # NOTE(mriedem): max_version looks odd but it's actually correct. Due to a
@@ -149,6 +150,16 @@
     @decorators.idempotent_id('a2e65a6c-66f1-4442-aaa8-498c31778d96')
     @utils.services('network', 'volume', 'image')
     def test_tagged_boot_devices(self):
+        """Test tagged boot devices
+
+        1. Create volumes
+        2. Create networks
+        3. Create subnets
+        4. Create ports
+        5. Create server, specifying tags for items in networks and
+           block_device_mapping_v2,
+        6. Verify tagged devices are in server via metadata service
+        """
         # Create volumes
         # The create_volume methods waits for the volumes to be available and
         # the base class will clean them up on tearDown.
@@ -300,11 +311,14 @@
 
 
 class TaggedBootDevicesTest_v242(TaggedBootDevicesTest):
+    """Test tagged boot devices with compute microversion greater than 2.41"""
+
     min_microversion = '2.42'
     max_microversion = 'latest'
 
 
 class TaggedAttachmentsTest(DeviceTaggingBase):
+    """Test tagged attachments with compute microversion greater than 2.48"""
 
     min_microversion = '2.49'
     max_microversion = 'latest'
@@ -342,6 +356,16 @@
     @decorators.idempotent_id('3e41c782-2a89-4922-a9d2-9a188c4e7c7c')
     @utils.services('network', 'volume', 'image')
     def test_tagged_attachment(self):
+        """Test tagged attachment
+
+        1. Create network
+        2. Create subnet
+        3. Create volume
+        4. Create server
+        5. Attach tagged networks and volume
+        6. Verify tagged devices are in server via metadata service
+        7. Detach tagged networks and volume
+        """
         # Create network
         net = self.networks_client.create_network(
             name=data_utils.rand_name(
diff --git a/tempest/api/compute/servers/test_instance_actions.py b/tempest/api/compute/servers/test_instance_actions.py
index 00837eb..5ab592a 100644
--- a/tempest/api/compute/servers/test_instance_actions.py
+++ b/tempest/api/compute/servers/test_instance_actions.py
@@ -19,6 +19,8 @@
 
 
 class InstanceActionsTestJSON(base.BaseV2ComputeTest):
+    """Test instance actions API"""
+
     create_default_network = True
 
     @classmethod
@@ -34,7 +36,7 @@
 
     @decorators.idempotent_id('77ca5cc5-9990-45e0-ab98-1de8fead201a')
     def test_list_instance_actions(self):
-        # List actions of the provided server
+        """Test listing actions of the provided server"""
         self.client.reboot_server(self.server['id'], type='HARD')
         waiters.wait_for_server_status(self.client,
                                        self.server['id'], 'ACTIVE')
@@ -47,7 +49,7 @@
 
     @decorators.idempotent_id('aacc71ca-1d70-4aa5-bbf6-0ff71470e43c')
     def test_get_instance_action(self):
-        # Get the action details of the provided server
+        """Test getting the action details of the provided server"""
         body = self.client.show_instance_action(
             self.server['id'], self.request_id)['instanceAction']
         self.assertEqual(self.server['id'], body['instance_uuid'])
@@ -55,6 +57,8 @@
 
 
 class InstanceActionsV221TestJSON(base.BaseV2ComputeTest):
+    """Test instance actions with compute microversion greater than 2.20"""
+
     create_default_network = True
 
     min_microversion = '2.21'
@@ -67,8 +71,11 @@
 
     @decorators.idempotent_id('0a0f85d4-10fa-41f6-bf80-a54fb4aa2ae1')
     def test_get_list_deleted_instance_actions(self):
+        """Test listing actions for deleted instance
 
-        # List actions of the deleted server
+        Listing actions for deleted instance should succeed and the returned
+        actions should contain 'create' and 'delete'.
+        """
         server = self.create_test_server(wait_until='ACTIVE')
         self.client.delete_server(server['id'])
         waiters.wait_for_server_termination(self.client, server['id'])
diff --git a/tempest/api/compute/servers/test_instance_actions_negative.py b/tempest/api/compute/servers/test_instance_actions_negative.py
index 4b5a2c3..dd2bf06 100644
--- a/tempest/api/compute/servers/test_instance_actions_negative.py
+++ b/tempest/api/compute/servers/test_instance_actions_negative.py
@@ -20,6 +20,8 @@
 
 
 class InstanceActionsNegativeTestJSON(base.BaseV2ComputeTest):
+    """Negative tests of instance actions"""
+
     create_default_network = True
 
     @classmethod
@@ -35,7 +37,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('67e1fce6-7ec2-45c6-92d4-0a8f1a632910')
     def test_list_instance_actions_non_existent_server(self):
-        # List actions of the non-existent server id
+        """Test listing actions for non existent instance should fail"""
         non_existent_server_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound,
                           self.client.list_instance_actions,
@@ -44,6 +46,6 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0269f40a-6f18-456c-b336-c03623c897f1')
     def test_get_instance_action_invalid_request(self):
-        # Get the action details of the provided server with invalid request
+        """Test getting instance action with invalid request_id should fail"""
         self.assertRaises(lib_exc.NotFound, self.client.show_instance_action,
                           self.server['id'], '999')
diff --git a/tempest/api/compute/servers/test_list_server_filters.py b/tempest/api/compute/servers/test_list_server_filters.py
index 7f62c64..990dd52 100644
--- a/tempest/api/compute/servers/test_list_server_filters.py
+++ b/tempest/api/compute/servers/test_list_server_filters.py
@@ -296,21 +296,32 @@
         for ip in ip_list:
             self.assertNotIn(ip_list[ip], map(lambda x: x['id'], servers))
 
-    @decorators.skip_because(bug="1540645")
     @decorators.idempotent_id('a905e287-c35e-42f2-b132-d02b09f3654a')
     def test_list_servers_filtered_by_ip_regex(self):
         """Filter the list of servers by part of server ip address"""
-        # Here should be listed all servers
         if not self.fixed_network_name:
             msg = 'fixed_network_name needs to be configured to run this test'
             raise self.skipException(msg)
-        self.s1 = self.client.show_server(self.s1['id'])['server']
-        addr_spec = self.s1['addresses'][self.fixed_network_name][0]
-        ip = addr_spec['addr'][0:-3]
+        # query addresses of the 3 servers
+        addrs = []
+        for s in [self.s1, self.s2, self.s3]:
+            s_show = self.client.show_server(s['id'])['server']
+            addr_spec = s_show['addresses'][self.fixed_network_name][0]
+            addrs.append(addr_spec['addr'])
+        # find common part of the 3 ip addresses
+        prefix = ''
+        addrs_len = [len(a) for a in addrs]
+        addrs_len.sort()
+        # iterate over the smallest length of an ip
+        for i in range(addrs_len[0]):
+            if not addrs[0][i] == addrs[1][i] == addrs[2][i]:
+                break
+            prefix += addrs[0][i]
+
         if addr_spec['version'] == 4:
-            params = {'ip': ip}
+            params = {'ip': prefix}
         else:
-            params = {'ip6': ip}
+            params = {'ip6': prefix}
         # capture all servers in case something goes wrong
         all_servers = self.client.list_servers(detail=True)
         body = self.client.list_servers(**params)
diff --git a/tempest/api/compute/servers/test_list_servers_negative.py b/tempest/api/compute/servers/test_list_servers_negative.py
index b95db5c..3d55696 100644
--- a/tempest/api/compute/servers/test_list_servers_negative.py
+++ b/tempest/api/compute/servers/test_list_servers_negative.py
@@ -20,6 +20,8 @@
 
 
 class ListServersNegativeTestJSON(base.BaseV2ComputeTest):
+    """Negative tests of listing servers"""
+
     create_default_network = True
 
     @classmethod
@@ -45,7 +47,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('24a26f1a-1ddc-4eea-b0d7-a90cc874ad8f')
     def test_list_servers_with_a_deleted_server(self):
-        # Verify deleted servers do not show by default in list servers
+        """Test that deleted servers do not show by default in list servers"""
         # List servers and verify server not returned
         body = self.client.list_servers()
         servers = body['servers']
@@ -56,7 +58,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ff01387d-c7ad-47b4-ae9e-64fa214638fe')
     def test_list_servers_by_non_existing_image(self):
-        # Listing servers for a non existing image returns empty list
+        """Test listing servers for a non existing image returns empty list"""
         body = self.client.list_servers(image='non_existing_image')
         servers = body['servers']
         self.assertEmpty(servers)
@@ -64,7 +66,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5913660b-223b-44d4-a651-a0fbfd44ca75')
     def test_list_servers_by_non_existing_flavor(self):
-        # Listing servers by non existing flavor returns empty list
+        """Test listing servers by non existing flavor returns empty list"""
         body = self.client.list_servers(flavor='non_existing_flavor')
         servers = body['servers']
         self.assertEmpty(servers)
@@ -72,7 +74,12 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e2c77c4a-000a-4af3-a0bd-629a328bde7c')
     def test_list_servers_by_non_existing_server_name(self):
-        # Listing servers for a non existent server name returns empty list
+        """Test listing servers for a non existent server name
+
+        Listing servers for a non existent server name should return empty
+        list.
+        """
+
         body = self.client.list_servers(name='non_existing_server_name')
         servers = body['servers']
         self.assertEmpty(servers)
@@ -80,9 +87,13 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('fcdf192d-0f74-4d89-911f-1ec002b822c4')
     def test_list_servers_status_non_existing(self):
-        # When invalid status is specified, up to microversion 2.37,
-        # an empty list is returned, and starting from microversion 2.38,
-        # a 400 error is returned in that case.
+        """Test listing servers with non existing status
+
+        When invalid status is specified, up to microversion 2.37,
+        an empty list is returned, and starting from microversion 2.38,
+        a 400 error is returned in that case.
+        """
+
         if self.is_requested_microversion_compatible('2.37'):
             body = self.client.list_servers(status='non_existing_status')
             servers = body['servers']
@@ -94,6 +105,12 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d47c17fb-eebd-4287-8e95-f20a7e627b18')
     def test_list_servers_by_limits_greater_than_actual_count(self):
+        """Test listing servers by limit greater than actual count
+
+        Listing servers by limit greater than actual count should return
+        all servers.
+        """
+
         # Gather the complete list of servers in the project for reference
         full_list = self.client.list_servers()['servers']
         # List servers by specifying a greater value for limit
@@ -104,21 +121,21 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('679bc053-5e70-4514-9800-3dfab1a380a6')
     def test_list_servers_by_limits_pass_string(self):
-        # Return an error if a string value is passed for limit
+        """Test listing servers by non-integer limit should fail"""
         self.assertRaises(lib_exc.BadRequest, self.client.list_servers,
                           limit='testing')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('62610dd9-4713-4ee0-8beb-fd2c1aa7f950')
     def test_list_servers_by_limits_pass_negative_value(self):
-        # Return an error if a negative value for limit is passed
+        """Test listing servers by negative limit should fail"""
         self.assertRaises(lib_exc.BadRequest, self.client.list_servers,
                           limit=-1)
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('87d12517-e20a-4c9c-97b6-dd1628d6d6c9')
     def test_list_servers_by_changes_since_invalid_date(self):
-        # Return an error when invalid date format is passed
+        """Test listing servers by invalid changes-since format should fail"""
         params = {'changes-since': '2011/01/01'}
         self.assertRaises(lib_exc.BadRequest, self.client.list_servers,
                           **params)
@@ -126,7 +143,12 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('74745ad8-b346-45b5-b9b8-509d7447fc1f')
     def test_list_servers_by_changes_since_future_date(self):
-        # Return an empty list when a date in the future is passed.
+        """Test listing servers by a future changes-since date
+
+        Return an empty list when a date in the future is passed as
+        changes-since value.
+        """
+
         # updated_at field may haven't been set at the point in the boot
         # process where build_request still exists, so add
         # {'status': 'ACTIVE'} along with changes-since as filter.
@@ -138,7 +160,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('93055106-2d34-46fe-af68-d9ddbf7ee570')
     def test_list_servers_detail_server_is_deleted(self):
-        # Server details are not listed for a deleted server
+        """Test listing servers detail should not contain deleted server"""
         body = self.client.list_servers(detail=True)
         servers = body['servers']
         actual = [srv for srv in servers
diff --git a/tempest/api/compute/servers/test_multiple_create.py b/tempest/api/compute/servers/test_multiple_create.py
index dcadace..10c76bb 100644
--- a/tempest/api/compute/servers/test_multiple_create.py
+++ b/tempest/api/compute/servers/test_multiple_create.py
@@ -19,11 +19,15 @@
 
 
 class MultipleCreateTestJSON(base.BaseV2ComputeTest):
+    """Test creating multiple servers in one request"""
     create_default_network = True
 
     @decorators.idempotent_id('61e03386-89c3-449c-9bb1-a06f423fd9d1')
     def test_multiple_create(self):
-        # Creating server with min_count=2, 2 servers will be created.
+        """Test creating multiple servers in one request
+
+        Creating server with min_count=2, 2 servers will be created.
+        """
         tenant_network = self.get_tenant_network()
         body, servers = compute.create_test_server(
             self.os_primary,
@@ -40,8 +44,12 @@
 
     @decorators.idempotent_id('864777fb-2f1e-44e3-b5b9-3eb6fa84f2f7')
     def test_multiple_create_with_reservation_return(self):
-        # Creating multiple servers with return_reservation_id=True,
-        # reservation_id will be returned.
+        """Test creating multiple servers with return_reservation_id=True
+
+        Creating multiple servers with return_reservation_id=True,
+        reservation_id will be returned.
+        """
+
         body = self.create_test_server(wait_until='ACTIVE',
                                        min_count=1,
                                        max_count=2,
diff --git a/tempest/api/compute/servers/test_multiple_create_negative.py b/tempest/api/compute/servers/test_multiple_create_negative.py
index 6bdf83b..3a970dd 100644
--- a/tempest/api/compute/servers/test_multiple_create_negative.py
+++ b/tempest/api/compute/servers/test_multiple_create_negative.py
@@ -19,11 +19,12 @@
 
 
 class MultipleCreateNegativeTestJSON(base.BaseV2ComputeTest):
+    """Negative tests of creating multiple servers in one request"""
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('daf29d8d-e928-4a01-9a8c-b129603f3fc0')
     def test_min_count_less_than_one(self):
-        # Creating server with min_count=0 should fail.
+        """Test creating server with min_count=0 should fail"""
         invalid_min_count = 0
         self.assertRaises(lib_exc.BadRequest, self.create_test_server,
                           min_count=invalid_min_count)
@@ -31,7 +32,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('999aa722-d624-4423-b813-0d1ac9884d7a')
     def test_min_count_non_integer(self):
-        # Creating server with non-integer min_count should fail.
+        """Test creating server with non-integer min_count should fail"""
         invalid_min_count = 2.5
         self.assertRaises(lib_exc.BadRequest, self.create_test_server,
                           min_count=invalid_min_count)
@@ -39,7 +40,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a6f9c2ab-e060-4b82-b23c-4532cb9390ff')
     def test_max_count_less_than_one(self):
-        # Creating server with max_count < 1 shoudld fail.
+        """Test creating server with max_count < 1 shoudld fail"""
         invalid_max_count = 0
         self.assertRaises(lib_exc.BadRequest, self.create_test_server,
                           max_count=invalid_max_count)
@@ -47,7 +48,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9c5698d1-d7af-4c80-b971-9d403135eea2')
     def test_max_count_non_integer(self):
-        # Creating server with non-integer max_count should fail.
+        """Test creating server with non-integer max_count should fail"""
         invalid_max_count = 2.5
         self.assertRaises(lib_exc.BadRequest, self.create_test_server,
                           max_count=invalid_max_count)
@@ -55,7 +56,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('476da616-f1ef-4271-a9b1-b9fc87727cdf')
     def test_max_count_less_than_min_count(self):
-        # Creating server with max_count less than min_count should fail.
+        """Test creating server with max_count < min_count should fail"""
         min_count = 3
         max_count = 2
         self.assertRaises(lib_exc.BadRequest, self.create_test_server,
diff --git a/tempest/api/compute/servers/test_server_metadata.py b/tempest/api/compute/servers/test_server_metadata.py
index 9d87e1c..9f93e76 100644
--- a/tempest/api/compute/servers/test_server_metadata.py
+++ b/tempest/api/compute/servers/test_server_metadata.py
@@ -14,13 +14,26 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest import config
 from tempest.lib import decorators
 
+CONF = config.CONF
 
+
+# TODO(stephenfin): Remove these tests once the nova Ussuri branch goes EOL
 class ServerMetadataTestJSON(base.BaseV2ComputeTest):
+    """Test server metadata"""
+
     create_default_network = True
 
     @classmethod
+    def skip_checks(cls):
+        super(ServerMetadataTestJSON, cls).skip_checks()
+        if not CONF.compute_feature_enabled.xenapi_apis:
+            raise cls.skipException(
+                'Metadata is read-only on non-Xen-based deployments.')
+
+    @classmethod
     def setup_clients(cls):
         super(ServerMetadataTestJSON, cls).setup_clients()
         cls.client = cls.servers_client
@@ -37,7 +50,10 @@
 
     @decorators.idempotent_id('479da087-92b3-4dcf-aeb3-fd293b2d14ce')
     def test_list_server_metadata(self):
-        # All metadata key/value pairs for a server should be returned
+        """Test listing server metadata
+
+        All metadata key/value pairs for a server should be returned.
+        """
         resp_metadata = (self.client.list_server_metadata(self.server['id'])
                          ['metadata'])
 
@@ -47,7 +63,10 @@
 
     @decorators.idempotent_id('211021f6-21de-4657-a68f-908878cfe251')
     def test_set_server_metadata(self):
-        # The server's metadata should be replaced with the provided values
+        """Test setting server metadata
+
+        The server's metadata should be replaced with the provided values
+        """
         # Create a new set of metadata for the server
         req_metadata = {'meta2': 'data2', 'meta3': 'data3'}
         self.client.set_server_metadata(self.server['id'], req_metadata)
@@ -60,8 +79,10 @@
 
     @decorators.idempotent_id('344d981e-0c33-4997-8a5d-6c1d803e4134')
     def test_update_server_metadata(self):
-        # The server's metadata values should be updated to the
-        # provided values
+        """Test updating server metadata
+
+        The server's metadata values should be updated to the provided values.
+        """
         meta = {'key1': 'alt1', 'key3': 'value3'}
         self.client.update_server_metadata(self.server['id'], meta)
 
@@ -73,8 +94,11 @@
 
     @decorators.idempotent_id('0f58d402-e34a-481d-8af8-b392b17426d9')
     def test_update_metadata_empty_body(self):
-        # The original metadata should not be lost if empty metadata body is
-        # passed
+        """Test updating server metadata to empty values
+
+        The original server metadata should not be lost if empty metadata
+        body is passed.
+        """
         meta = {}
         self.client.update_server_metadata(self.server['id'], meta)
         resp_metadata = (self.client.list_server_metadata(self.server['id'])
@@ -84,15 +108,19 @@
 
     @decorators.idempotent_id('3043c57d-7e0e-49a6-9a96-ad569c265e6a')
     def test_get_server_metadata_item(self):
-        # The value for a specific metadata key should be returned
+        """Test getting specific server metadata item"""
         meta = self.client.show_server_metadata_item(self.server['id'],
                                                      'key2')['meta']
         self.assertEqual('value2', meta['key2'])
 
     @decorators.idempotent_id('58c02d4f-5c67-40be-8744-d3fa5982eb1c')
     def test_set_server_metadata_item(self):
-        # The item's value should be updated to the provided value
-        # Update the metadata value
+        """Test updating specific server metadata item
+
+        The metadata item's value should be updated to the provided value.
+        """
+
+        # Update the metadata value.
         meta = {'nova': 'alt'}
         self.client.set_server_metadata_item(self.server['id'], 'nova', meta)
 
@@ -104,7 +132,10 @@
 
     @decorators.idempotent_id('127642d6-4c7b-4486-b7cd-07265a378658')
     def test_delete_server_metadata_item(self):
-        # The metadata value/key pair should be deleted from the server
+        """Test deleting server metadata item
+
+        The metadata value/key pair should be deleted from the server.
+        """
         self.client.delete_server_metadata_item(self.server['id'], 'key1')
 
         # Verify the metadata item has been removed
diff --git a/tempest/api/compute/servers/test_server_metadata_negative.py b/tempest/api/compute/servers/test_server_metadata_negative.py
index 5688af1..a697b95 100644
--- a/tempest/api/compute/servers/test_server_metadata_negative.py
+++ b/tempest/api/compute/servers/test_server_metadata_negative.py
@@ -14,12 +14,17 @@
 #    under the License.
 
 from tempest.api.compute import base
+from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
+CONF = config.CONF
+
 
 class ServerMetadataNegativeTestJSON(base.BaseV2ComputeTest):
+    """Negative tests of server metadata"""
+
     create_default_network = True
 
     @classmethod
@@ -36,6 +41,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('fe114a8f-3a57-4eff-9ee2-4e14628df049')
     def test_server_create_metadata_key_too_long(self):
+        """Test creating server with too long metadata key should fail"""
         # Attempt to start a server with a meta-data key that is > 255
         # characters
 
@@ -52,7 +58,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('92431555-4d8b-467c-b95b-b17daa5e57ff')
     def test_create_server_metadata_blank_key(self):
-        # Blank key should trigger an error.
+        """Test creating server with blank metadata key should fail"""
         meta = {'': 'data1'}
         self.assertRaises(lib_exc.BadRequest,
                           self.create_test_server,
@@ -61,6 +67,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('4d9cd7a3-2010-4b41-b8fe-3bbf0b169466')
     def test_server_metadata_non_existent_server(self):
+        """Test getting metadata item for a non existent server should fail"""
         # GET on a non-existent server should not succeed
         non_existent_server_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound,
@@ -71,7 +78,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f408e78e-3066-4097-9299-3b0182da812e')
     def test_list_server_metadata_non_existent_server(self):
-        # List metadata on a non-existent server should not succeed
+        """Test listing metadata for a non existent server should fail"""
         non_existent_server_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound,
                           self.client.list_server_metadata,
@@ -80,8 +87,10 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0025fbd6-a4ba-4cde-b8c2-96805dcfdabc')
     def test_wrong_key_passed_in_body(self):
-        # Raise BadRequest if key in uri does not match
-        # the key passed in body.
+        """Test setting server metadata item with wrong key in body
+
+        Raise BadRequest if key in uri does not match the key passed in body.
+        """
         meta = {'testkey': 'testvalue'}
         self.assertRaises(lib_exc.BadRequest,
                           self.client.set_server_metadata_item,
@@ -90,7 +99,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0df38c2a-3d4e-4db5-98d8-d4d9fa843a12')
     def test_set_metadata_non_existent_server(self):
-        # Set metadata on a non-existent server should not succeed
+        """Test setting metadata for a non existent server should fail"""
+        if not CONF.compute_feature_enabled.xenapi_apis:
+            raise self.skipException(
+                'Metadata is read-only on non-Xen-based deployments.')
+
         non_existent_server_id = data_utils.rand_uuid()
         meta = {'meta1': 'data1'}
         self.assertRaises(lib_exc.NotFound,
@@ -101,7 +114,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('904b13dc-0ef2-4e4c-91cd-3b4a0f2f49d8')
     def test_update_metadata_non_existent_server(self):
-        # An update should not happen for a non-existent server
+        """Test updating metadata for a non existent server should fail"""
+        if not CONF.compute_feature_enabled.xenapi_apis:
+            raise self.skipException(
+                'Metadata is read-only on non-Xen-based deployments.')
+
         non_existent_server_id = data_utils.rand_uuid()
         meta = {'key1': 'value1', 'key2': 'value2'}
         self.assertRaises(lib_exc.NotFound,
@@ -112,7 +129,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a452f38c-05c2-4b47-bd44-a4f0bf5a5e48')
     def test_update_metadata_with_blank_key(self):
-        # Blank key should trigger an error
+        """Test updating server metadata to blank key should fail"""
+        if not CONF.compute_feature_enabled.xenapi_apis:
+            raise self.skipException(
+                'Metadata is read-only on non-Xen-based deployments.')
+
         meta = {'': 'data1'}
         self.assertRaises(lib_exc.BadRequest,
                           self.client.update_server_metadata,
@@ -121,7 +142,14 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('6bbd88e1-f8b3-424d-ba10-ae21c45ada8d')
     def test_delete_metadata_non_existent_server(self):
-        # Should not be able to delete metadata item from a non-existent server
+        """Test deleting metadata item from a non existent server
+
+        Should not be able to delete metadata item from a non-existent server.
+        """
+        if not CONF.compute_feature_enabled.xenapi_apis:
+            raise self.skipException(
+                'Metadata is read-only on non-Xen-based deployments.')
+
         non_existent_server_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound,
                           self.client.delete_server_metadata_item,
@@ -131,9 +159,15 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('d8c0a210-a5c3-4664-be04-69d96746b547')
     def test_metadata_items_limit(self):
-        # A 403 Forbidden or 413 Overlimit (old behaviour) exception
-        # will be raised while exceeding metadata items limit for
-        # tenant.
+        """Test set/update server metadata over limit should fail
+
+        A 403 Forbidden or 413 Overlimit (old behaviour) exception
+        will be raised while exceeding metadata items limit for project.
+        """
+        if not CONF.compute_feature_enabled.xenapi_apis:
+            raise self.skipException(
+                'Metadata is read-only on non-Xen-based deployments.')
+
         quota_set = self.quotas_client.show_quota_set(
             self.tenant_id)['quota_set']
         quota_metadata = quota_set['metadata_items']
@@ -157,8 +191,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('96100343-7fa9-40d8-80fa-d29ef588ce1c')
     def test_set_server_metadata_blank_key(self):
-        # Raise a bad request error for blank key.
-        # set_server_metadata will replace all metadata with new value
+        """Test setting server metadata with blank key should fail"""
+        if not CONF.compute_feature_enabled.xenapi_apis:
+            raise self.skipException(
+                'Metadata is read-only on non-Xen-based deployments.')
+
         meta = {'': 'data1'}
         self.assertRaises(lib_exc.BadRequest,
                           self.client.set_server_metadata,
@@ -167,8 +204,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('64a91aee-9723-4863-be44-4c9d9f1e7d0e')
     def test_set_server_metadata_missing_metadata(self):
-        # Raise a bad request error for a missing metadata field
-        # set_server_metadata will replace all metadata with new value
+        """Test setting server metadata without metadata field should fail"""
+        if not CONF.compute_feature_enabled.xenapi_apis:
+            raise self.skipException(
+                'Metadata is read-only on non-Xen-based deployments.')
+
         meta = {'meta1': 'data1'}
         self.assertRaises(lib_exc.BadRequest,
                           self.client.set_server_metadata,
diff --git a/tempest/api/compute/servers/test_server_password.py b/tempest/api/compute/servers/test_server_password.py
index 7b31ede..f61d4fd 100644
--- a/tempest/api/compute/servers/test_server_password.py
+++ b/tempest/api/compute/servers/test_server_password.py
@@ -19,6 +19,8 @@
 
 
 class ServerPasswordTestJSON(base.BaseV2ComputeTest):
+    """Test server password"""
+
     create_default_network = True
 
     @classmethod
@@ -28,8 +30,10 @@
 
     @decorators.idempotent_id('f83b582f-62a8-4f22-85b0-0dee50ff783a')
     def test_get_server_password(self):
+        """Test getting password of a server"""
         self.servers_client.show_password(self.server['id'])
 
     @decorators.idempotent_id('f8229e8b-b625-4493-800a-bde86ac611ea')
     def test_delete_server_password(self):
+        """Test deleting password from a server"""
         self.servers_client.delete_password(self.server['id'])
diff --git a/tempest/api/compute/servers/test_server_rescue.py b/tempest/api/compute/servers/test_server_rescue.py
index 1247494..5445113 100644
--- a/tempest/api/compute/servers/test_server_rescue.py
+++ b/tempest/api/compute/servers/test_server_rescue.py
@@ -53,9 +53,11 @@
 
 
 class ServerRescueTestJSON(ServerRescueTestBase):
+    """Test server rescue"""
 
     @decorators.idempotent_id('fd032140-714c-42e4-a8fd-adcd8df06be6')
     def test_rescue_unrescue_instance(self):
+        """Test rescue/unrescue server"""
         password = data_utils.rand_password()
         server = self.create_test_server(adminPass=password,
                                          wait_until='ACTIVE')
@@ -68,6 +70,7 @@
 
 
 class ServerRescueTestJSONUnderV235(ServerRescueTestBase):
+    """Test server rescue with compute microversion less than 2.36"""
 
     max_microversion = '2.35'
 
@@ -81,7 +84,7 @@
     @testtools.skipUnless(CONF.network_feature_enabled.floating_ips,
                           "Floating ips are not available")
     def test_rescued_vm_associate_dissociate_floating_ip(self):
-        # Association of floating IP to a rescued vm
+        """Test associate/dissociate floating ip for rescued server"""
         floating_ip_body = self.floating_ips_client.create_floating_ip(
             pool=CONF.network.floating_network_name)['floating_ip']
         self.addCleanup(self.floating_ips_client.delete_floating_ip,
@@ -96,6 +99,7 @@
 
     @decorators.idempotent_id('affca41f-7195-492d-8065-e09eee245404')
     def test_rescued_vm_add_remove_security_group(self):
+        """Test add/remove security group to for rescued server"""
         # Add Security group
         sg = self.create_security_group()
         self.servers_client.add_security_group(self.rescued_server_id,
@@ -154,33 +158,43 @@
 
 
 class ServerStableDeviceRescueTest(BaseServerStableDeviceRescueTest):
+    """Test rescuing server specifying type of device for the rescue disk"""
 
     @decorators.idempotent_id('947004c3-e8ef-47d9-9f00-97b74f9eaf96')
     def test_stable_device_rescue_cdrom_ide(self):
+        """Test rescuing server with cdrom and ide as the rescue disk"""
         server_id, rescue_image_id = self._create_server_and_rescue_image(
             hw_rescue_device='cdrom', hw_rescue_bus='ide')
         self._test_stable_device_rescue(server_id, rescue_image_id)
 
     @decorators.idempotent_id('16865750-1417-4854-bcf7-496e6753c01e')
     def test_stable_device_rescue_disk_virtio(self):
+        """Test rescuing server with disk and virtio as the rescue disk"""
         server_id, rescue_image_id = self._create_server_and_rescue_image(
             hw_rescue_device='disk', hw_rescue_bus='virtio')
         self._test_stable_device_rescue(server_id, rescue_image_id)
 
     @decorators.idempotent_id('12340157-6306-4745-bdda-cfa019908b48')
     def test_stable_device_rescue_disk_scsi(self):
+        """Test rescuing server with disk and scsi as the rescue disk"""
         server_id, rescue_image_id = self._create_server_and_rescue_image(
             hw_rescue_device='disk', hw_rescue_bus='scsi')
         self._test_stable_device_rescue(server_id, rescue_image_id)
 
     @decorators.idempotent_id('647d04cf-ad35-4956-89ab-b05c5c16f30c')
     def test_stable_device_rescue_disk_usb(self):
+        """Test rescuing server with disk and usb as the rescue disk"""
         server_id, rescue_image_id = self._create_server_and_rescue_image(
             hw_rescue_device='disk', hw_rescue_bus='usb')
         self._test_stable_device_rescue(server_id, rescue_image_id)
 
     @decorators.idempotent_id('a3772b42-00bf-4310-a90b-1cc6fd3e7eab')
     def test_stable_device_rescue_disk_virtio_with_volume_attached(self):
+        """Test rescuing server with volume attached
+
+        Attach a volume to the server and then rescue the server with disk
+        and virtio as the rescue disk.
+        """
         server_id, rescue_image_id = self._create_server_and_rescue_image(
             hw_rescue_device='disk', hw_rescue_bus='virtio')
         server = self.servers_client.show_server(server_id)['server']
@@ -192,12 +206,22 @@
 
 
 class ServerBootFromVolumeStableRescueTest(BaseServerStableDeviceRescueTest):
+    """Test rescuing server specifying type of device for the rescue disk
+
+    Test rescuing server specifying type of device for the rescue disk with
+    compute microversion greater than 2.86.
+    """
 
     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):
+        """Test rescuing server with blank volume as block_device_mapping_v2
+
+        Create a server with block_device_mapping_v2 with blank volume,
+        then rescue the server with disk and virtio as the rescue disk.
+        """
         block_device_mapping_v2 = [{
             "boot_index": "0",
             "source_type": "blank",
@@ -211,6 +235,11 @@
     @decorators.attr(type='slow')
     @decorators.idempotent_id('e4636333-c928-40fc-98b7-70a23eef4224')
     def test_stable_device_rescue_bfv_image_volume(self):
+        """Test rescuing server with blank volume as block_device_mapping_v2
+
+        Create a server with block_device_mapping_v2 with image volume,
+        then rescue the server with disk and virtio as the rescue disk.
+        """
         block_device_mapping_v2 = [{
             "boot_index": "0",
             "source_type": "image",
diff --git a/tempest/api/compute/servers/test_servers.py b/tempest/api/compute/servers/test_servers.py
index 3a4bd6d..cc013e3 100644
--- a/tempest/api/compute/servers/test_servers.py
+++ b/tempest/api/compute/servers/test_servers.py
@@ -25,6 +25,7 @@
 
 
 class ServersTestJSON(base.BaseV2ComputeTest):
+    """Test servers API"""
     create_default_network = True
 
     @classmethod
@@ -37,8 +38,11 @@
                           enable_instance_password,
                           'Instance password not available.')
     def test_create_server_with_admin_password(self):
-        # If an admin password is provided on server creation, the server's
-        # root password should be set to that password.
+        """Test creating server with admin password
+
+        If an admin password is provided on server creation, the server's
+        root password should be set to that password.
+        """
         server = self.create_test_server(adminPass='testpassword')
         self.addCleanup(self.delete_server, server['id'])
 
@@ -47,8 +51,7 @@
 
     @decorators.idempotent_id('8fea6be7-065e-47cf-89b8-496e6f96c699')
     def test_create_with_existing_server_name(self):
-        # Creating a server with a name that already exists is allowed
-
+        """Test creating a server with already existing name is allowed"""
         # TODO(sdague): clear out try, we do cleanup one layer up
         server_name = data_utils.rand_name(
             self.__class__.__name__ + '-server')
@@ -69,8 +72,7 @@
 
     @decorators.idempotent_id('f9e15296-d7f9-4e62-b53f-a04e89160833')
     def test_create_specify_keypair(self):
-        # Specify a keypair while creating a server
-
+        """Test creating server with keypair"""
         key_name = data_utils.rand_name('key')
         self.keypairs_client.create_keypair(name=key_name)
         self.addCleanup(self.keypairs_client.delete_keypair, key_name)
@@ -97,7 +99,7 @@
 
     @decorators.idempotent_id('5e6ccff8-349d-4852-a8b3-055df7988dd2')
     def test_update_server_name(self):
-        # The server name should be changed to the provided value
+        """Test updating server name to the provided value"""
         server = self.create_test_server(wait_until='ACTIVE')
         self.addCleanup(self.delete_server, server['id'])
         # Update instance name with non-ASCII characters
@@ -115,7 +117,7 @@
 
     @decorators.idempotent_id('89b90870-bc13-4b73-96af-f9d4f2b70077')
     def test_update_access_server_address(self):
-        # The server's access addresses should reflect the provided values
+        """Test updating server's access addresses to the provided value"""
         server = self.create_test_server(wait_until='ACTIVE')
         self.addCleanup(self.delete_server, server['id'])
 
@@ -132,7 +134,7 @@
 
     @decorators.idempotent_id('38fb1d02-c3c5-41de-91d3-9bc2025a75eb')
     def test_create_server_with_ipv6_addr_only(self):
-        # Create a server without an IPv4 address(only IPv6 address).
+        """Test creating server with ipv6 address only(no ipv4 address)"""
         server = self.create_test_server(accessIPv6='2001:2001::3',
                                          wait_until='ACTIVE')
         self.addCleanup(self.delete_server, server['id'])
@@ -142,17 +144,22 @@
     @decorators.related_bug('1730756')
     @decorators.idempotent_id('defbaca5-d611-49f5-ae21-56ee25d2db49')
     def test_create_server_specify_multibyte_character_name(self):
-        # prefix character is:
-        # http://unicode.org/cldr/utility/character.jsp?a=20A1
+        """Test creating server with multi character name
 
-        # We use a string with 3 byte utf-8 character due to nova
-        # will return 400(Bad Request) if we attempt to send a name which has
-        # 4 byte utf-8 character.
+        prefix character is:
+        http://unicode.org/cldr/utility/character.jsp?a=20A1
+
+        We use a string with 3 byte utf-8 character due to nova
+        will return 400(Bad Request) if we attempt to send a name which has
+        4 byte utf-8 character.
+        """
         utf8_name = data_utils.rand_name(b'\xe2\x82\xa1'.decode('utf-8'))
         self.create_test_server(name=utf8_name, wait_until='ACTIVE')
 
 
 class ServerShowV247Test(base.BaseV2ComputeTest):
+    """Test servers API with compute microversion greater than 2.46"""
+
     min_microversion = '2.47'
     max_microversion = 'latest'
 
@@ -164,12 +171,14 @@
 
     @decorators.idempotent_id('88b0bdb2-494c-11e7-a919-92ebcb67fe33')
     def test_show_server(self):
+        """Test getting server detail"""
         server = self.create_test_server()
         # All fields will be checked by API schema
         self.servers_client.show_server(server['id'])
 
     @decorators.idempotent_id('8de397c2-57d0-4b90-aa30-e5d668f21a8b')
     def test_update_rebuild_list_server(self):
+        """Test update/rebuild/list server"""
         server = self.create_test_server()
         # Checking update API response schema
         self.servers_client.update_server(server['id'])
@@ -184,6 +193,8 @@
 
 
 class ServerShowV263Test(base.BaseV2ComputeTest):
+    """Test servers API with compute microversion greater than 2.62"""
+
     min_microversion = '2.63'
     max_microversion = 'latest'
 
@@ -195,6 +206,7 @@
                           'required to test image certificate validation.')
     @decorators.idempotent_id('71b8e3d5-11d2-494f-b917-b094a4afed3c')
     def test_show_update_rebuild_list_server(self):
+        """Test show/update/rebuild/list server"""
         trusted_certs = CONF.compute.certified_image_trusted_certs
         server = self.create_test_server(
             image_id=CONF.compute.certified_image_ref,
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 6676358..4f85048 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -60,7 +60,8 @@
         server = cls.create_test_server(wait_until='ACTIVE')
         cls.server_id = server['id']
 
-        server = cls.create_test_server()
+        # Wait until the instance is active to avoid the delete racing
+        server = cls.create_test_server(wait_until='ACTIVE')
         cls.client.delete_server(server['id'])
         waiters.wait_for_server_termination(cls.client, server['id'])
         cls.deleted_server_id = server['id']
diff --git a/tempest/api/compute/servers/test_virtual_interfaces.py b/tempest/api/compute/servers/test_virtual_interfaces.py
index dfd6ca4..b2e02c5 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces.py
@@ -28,6 +28,8 @@
 # TODO(mriedem): Remove this test class once the nova queens branch goes into
 # extended maintenance mode.
 class VirtualInterfacesTestJSON(base.BaseV2ComputeTest):
+    """Test virtual interfaces API with compute microversion less than 2.44"""
+
     max_microversion = '2.43'
 
     depends_on_nova_network = True
@@ -47,9 +49,7 @@
     @decorators.idempotent_id('96c4e2ef-5e4d-4d7f-87f5-fed6dca18016')
     @utils.services('network')
     def test_list_virtual_interfaces(self):
-        # Positive test:Should be able to GET the virtual interfaces list
-        # for a given server_id
-
+        """Test listing virtual interfaces of a server"""
         if CONF.service_available.neutron:
             with testtools.ExpectedException(exceptions.BadRequest):
                 self.client.list_virtual_interfaces(self.server['id'])
diff --git a/tempest/api/compute/servers/test_virtual_interfaces_negative.py b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
index f6e8bc9..5667281 100644
--- a/tempest/api/compute/servers/test_virtual_interfaces_negative.py
+++ b/tempest/api/compute/servers/test_virtual_interfaces_negative.py
@@ -23,6 +23,12 @@
 # TODO(mriedem): Remove this test class once the nova queens branch goes into
 # extended maintenance mode.
 class VirtualInterfacesNegativeTestJSON(base.BaseV2ComputeTest):
+    """Negative tests of virtual interfaces API
+
+    Negative tests of virtual interfaces API for compute microversion less
+    than 2.44.
+    """
+
     max_microversion = '2.43'
 
     depends_on_nova_network = True
@@ -37,8 +43,7 @@
     @decorators.idempotent_id('64ebd03c-1089-4306-93fa-60f5eb5c803c')
     @utils.services('network')
     def test_list_virtual_interfaces_invalid_server_id(self):
-        # Negative test: Should not be able to GET virtual interfaces
-        # for an invalid server_id
+        """Test listing virtual interfaces of an invalid server should fail"""
         invalid_server_id = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound,
                           self.servers_client.list_virtual_interfaces,
diff --git a/tempest/api/compute/test_extensions.py b/tempest/api/compute/test_extensions.py
index 12e7fea..3318876 100644
--- a/tempest/api/compute/test_extensions.py
+++ b/tempest/api/compute/test_extensions.py
@@ -27,10 +27,11 @@
 
 
 class ExtensionsTest(base.BaseV2ComputeTest):
+    """Tests Compute Extensions API"""
 
     @decorators.idempotent_id('3bb27738-b759-4e0d-a5fa-37d7a6df07d1')
     def test_list_extensions(self):
-        # List of all extensions
+        """Test listing compute extensions"""
         if not CONF.compute_feature_enabled.api_extensions:
             raise self.skipException('There are not any extensions configured')
         extensions = self.extensions_client.list_extensions()['extensions']
@@ -50,6 +51,6 @@
     @decorators.idempotent_id('05762f39-bdfa-4cdb-9b46-b78f8e78e2fd')
     @utils.requires_ext(extension='os-consoles', service='compute')
     def test_get_extension(self):
-        # get the specified extensions
+        """Test getting specified compute extension details"""
         extension = self.extensions_client.show_extension('os-consoles')
         self.assertEqual('os-consoles', extension['extension']['alias'])
diff --git a/tempest/api/compute/test_networks.py b/tempest/api/compute/test_networks.py
index 76131e2..97c26e4 100644
--- a/tempest/api/compute/test_networks.py
+++ b/tempest/api/compute/test_networks.py
@@ -20,6 +20,7 @@
 
 
 class ComputeNetworksTest(base.BaseV2ComputeTest):
+    """Test compute networks API with compute microversion less than 2.36"""
     max_microversion = '2.35'
 
     @classmethod
@@ -35,5 +36,6 @@
 
     @decorators.idempotent_id('3fe07175-312e-49a5-a623-5f52eeada4c2')
     def test_list_networks(self):
+        """Test listing networks using compute networks API"""
         networks = self.client.list_networks()['networks']
         self.assertNotEmpty(networks, "No networks found.")
diff --git a/tempest/api/compute/test_quotas.py b/tempest/api/compute/test_quotas.py
index a62492d..5fe0e3b 100644
--- a/tempest/api/compute/test_quotas.py
+++ b/tempest/api/compute/test_quotas.py
@@ -20,6 +20,7 @@
 
 
 class QuotasTestJSON(base.BaseV2ComputeTest):
+    """Test compute quotas"""
 
     @classmethod
     def skip_checks(cls):
@@ -59,7 +60,7 @@
 
     @decorators.idempotent_id('f1ef0a97-dbbb-4cca-adc5-c9fbc4f76107')
     def test_get_quotas(self):
-        # User can get the quota set for it's tenant
+        """Test user can get the compute quota set for it's project"""
         expected_quota_set = self.default_quota_set | set(['id'])
         quota_set = self.client.show_quota_set(self.tenant_id)['quota_set']
         self.assertEqual(quota_set['id'], self.tenant_id)
@@ -75,7 +76,7 @@
 
     @decorators.idempotent_id('9bfecac7-b966-4f47-913f-1a9e2c12134a')
     def test_get_default_quotas(self):
-        # User can get the default quota set for it's tenant
+        """Test user can get the default compute quota set for it's project"""
         expected_quota_set = self.default_quota_set | set(['id'])
         quota_set = (self.client.show_default_quota_set(self.tenant_id)
                      ['quota_set'])
@@ -85,7 +86,7 @@
 
     @decorators.idempotent_id('cd65d997-f7e4-4966-a7e9-d5001b674fdc')
     def test_compare_tenant_quotas_with_default_quotas(self):
-        # Tenants are created with the default quota values
+        """Test tenants are created with the default compute quota values"""
         default_quota_set = \
             self.client.show_default_quota_set(self.tenant_id)['quota_set']
         tenant_quota_set = (self.client.show_quota_set(self.tenant_id)
diff --git a/tempest/api/compute/test_tenant_networks.py b/tempest/api/compute/test_tenant_networks.py
index f4eada0..17f4b80 100644
--- a/tempest/api/compute/test_tenant_networks.py
+++ b/tempest/api/compute/test_tenant_networks.py
@@ -18,6 +18,8 @@
 
 
 class ComputeTenantNetworksTest(base.BaseV2ComputeTest):
+    """Test compute tenant networks API with microversion less than 2.36"""
+
     max_microversion = '2.35'
 
     @classmethod
@@ -34,8 +36,11 @@
     @decorators.idempotent_id('edfea98e-bbe3-4c7a-9739-87b986baff26')
     @utils.services('network')
     def test_list_show_tenant_networks(self):
-        # Fetch all networks that are visible to the tenant: this may include
-        # shared and external networks
+        """Test list/show tenant networks
+
+        Fetch all networks that are visible to the tenant: this may include
+        shared and external networks.
+        """
         tenant_networks = [
             n['id'] for n in self.client.list_tenant_networks()['networks']
         ]
diff --git a/tempest/api/compute/volumes/test_attach_volume_negative.py b/tempest/api/compute/volumes/test_attach_volume_negative.py
index 9a506af..516f599 100644
--- a/tempest/api/compute/volumes/test_attach_volume_negative.py
+++ b/tempest/api/compute/volumes/test_attach_volume_negative.py
@@ -21,6 +21,8 @@
 
 
 class AttachVolumeNegativeTest(base.BaseV2ComputeTest):
+    """Negative tests of volume attaching"""
+
     create_default_network = True
 
     @classmethod
@@ -34,6 +36,7 @@
     @decorators.related_bug('1630783', status_code=500)
     @decorators.idempotent_id('a313b5cd-fbd0-49cc-94de-870e99f763c7')
     def test_delete_attached_volume(self):
+        """Test deleting attachemd volume should fail"""
         server = self.create_test_server(wait_until='ACTIVE')
         volume = self.create_volume()
         self.attach_volume(server, volume)
@@ -44,10 +47,13 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('aab919e2-d992-4cbb-a4ed-745c2475398c')
     def test_attach_attached_volume_to_same_server(self):
-        # Test attaching the same volume to the same instance once
-        # it's already attached. The nova/cinder validation for this differs
-        # depending on whether or not cinder v3.27 is being used to attach
-        # the volume to the instance.
+        """Test attaching attached volume to same server should fail
+
+        Test attaching the same volume to the same instance once
+        it's already attached. The nova/cinder validation for this differs
+        depending on whether or not cinder v3.27 is being used to attach
+        the volume to the instance.
+        """
         server = self.create_test_server(wait_until='ACTIVE')
         volume = self.create_volume()
 
@@ -59,6 +65,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ee37a796-2afb-11e7-bc0f-fa163e65f5ce')
     def test_attach_attached_volume_to_different_server(self):
+        """Test attaching attached volume to different server should fail"""
         server1 = self.create_test_server(wait_until='ACTIVE')
         volume = self.create_volume()
 
diff --git a/tempest/api/identity/v2/test_api_discovery.py b/tempest/api/identity/v2/test_api_discovery.py
index 5b9d38c..afda104 100644
--- a/tempest/api/identity/v2/test_api_discovery.py
+++ b/tempest/api/identity/v2/test_api_discovery.py
@@ -18,11 +18,12 @@
 
 
 class TestApiDiscovery(base.BaseIdentityV2Test):
-    """Tests for API discovery features."""
+    """Tests for identity v2 API discovery features."""
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('ea889a68-a15f-4166-bfb1-c12456eae853')
     def test_api_version_resources(self):
+        """Test showing identity v2 api version resources"""
         descr = self.non_admin_client.show_api_description()['version']
         expected_resources = ('id', 'links', 'media-types', 'status',
                               'updated')
@@ -34,6 +35,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('007a0be0-78fe-4fdb-bbee-e9216cc17bb2')
     def test_api_media_types(self):
+        """Test showing identity v2 api version media type"""
         descr = self.non_admin_client.show_api_description()['version']
         # Get MIME type bases and descriptions
         media_types = [(media_type['base'], media_type['type']) for
@@ -49,6 +51,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('77fd6be0-8801-48e6-b9bf-38cdd2f253ec')
     def test_api_version_statuses(self):
+        """Test showing identity v2 api version status"""
         descr = self.non_admin_client.show_api_description()['version']
         status = descr['status'].lower()
         supported_statuses = ['current', 'stable', 'experimental',
diff --git a/tempest/api/identity/v2/test_extension.py b/tempest/api/identity/v2/test_extension.py
index c538c14..13555bd 100644
--- a/tempest/api/identity/v2/test_extension.py
+++ b/tempest/api/identity/v2/test_extension.py
@@ -18,10 +18,11 @@
 
 
 class ExtensionTestJSON(base.BaseIdentityV2Test):
+    """Test extensions in identity v2 API"""
 
     @decorators.idempotent_id('85f3f661-f54c-4d48-b563-72ae952b9383')
     def test_list_extensions(self):
-        # List all the extensions
+        """List all the identity extensions via v2 API"""
         body = self.non_admin_client.list_extensions()['extensions']['values']
         self.assertNotEmpty(body)
         keys = ['name', 'updated', 'alias', 'links',
diff --git a/tempest/api/identity/v2/test_tenants.py b/tempest/api/identity/v2/test_tenants.py
index b2a6d13..1752b65 100644
--- a/tempest/api/identity/v2/test_tenants.py
+++ b/tempest/api/identity/v2/test_tenants.py
@@ -19,11 +19,13 @@
 
 
 class IdentityTenantsTest(base.BaseIdentityV2Test):
+    """Test listing tenants in identity v2 API"""
 
     credentials = ['primary', 'alt']
 
     @decorators.idempotent_id('ecae2459-243d-4ba1-ad02-65f15dc82b78')
     def test_list_tenants_returns_only_authorized_tenants(self):
+        """Test listing tenants only returns authorized tenants via v2 API"""
         alt_tenant_name = self.os_alt.credentials.tenant_name
         resp = self.non_admin_tenants_client.list_tenants()
 
diff --git a/tempest/api/identity/v2/test_tokens.py b/tempest/api/identity/v2/test_tokens.py
index 64b81c2..a928ad9 100644
--- a/tempest/api/identity/v2/test_tokens.py
+++ b/tempest/api/identity/v2/test_tokens.py
@@ -20,10 +20,11 @@
 
 
 class TokensTest(base.BaseIdentityV2Test):
+    """Test tokens in identity v2 API"""
 
     @decorators.idempotent_id('65ae3b78-91ff-467b-a705-f6678863b8ec')
     def test_create_token(self):
-
+        """Test creating token for user via v2 API"""
         token_client = self.non_admin_token_client
 
         # get a token for the user
diff --git a/tempest/api/identity/v2/test_users.py b/tempest/api/identity/v2/test_users.py
index 2eea860..a63b45c 100644
--- a/tempest/api/identity/v2/test_users.py
+++ b/tempest/api/identity/v2/test_users.py
@@ -28,6 +28,7 @@
 
 
 class IdentityUsersTest(base.BaseIdentityV2Test):
+    """Test user password in identity v2 API"""
 
     @classmethod
     def resource_setup(cls):
@@ -85,6 +86,7 @@
                       'immutable user source and solely '
                       'provides read-only access to users.')
     def test_user_update_own_password(self):
+        """test updating user's own password via v2 API"""
         old_pass = self.creds.password
         old_token = self.non_admin_users_client.token
         new_pass = data_utils.rand_password()
diff --git a/tempest/api/identity/v3/test_api_discovery.py b/tempest/api/identity/v3/test_api_discovery.py
index e87d1cd..ebb96fd 100644
--- a/tempest/api/identity/v3/test_api_discovery.py
+++ b/tempest/api/identity/v3/test_api_discovery.py
@@ -22,10 +22,11 @@
 
 
 class TestApiDiscovery(base.BaseIdentityV3Test):
-    """Tests for API discovery features."""
+    """Tests for identity API discovery features."""
 
     @decorators.idempotent_id('79aec9ae-710f-4c54-a4fc-3aa25b4feac3')
     def test_identity_v3_existence(self):
+        """Test that identity v3 version should exist"""
         versions = self.non_admin_versions_client.list_versions()
         found = any(
             "v3" in version.get('id')
@@ -35,9 +36,12 @@
     @decorators.idempotent_id('721f480f-35b6-46c7-846e-047e6acea0dc')
     @decorators.attr(type='smoke')
     def test_list_api_versions(self):
-        # NOTE: Actually this API doesn't depend on v3 API at all, because
-        # the API operation is "GET /" without v3's endpoint. The reason of
-        # this test path is just v3 API is CURRENT on Keystone side.
+        """Test listing identity api versions
+
+        NOTE: Actually this API doesn't depend on v3 API at all, because
+        the API operation is "GET /" without v3's endpoint. The reason of
+        this test path is just v3 API is CURRENT on Keystone side.
+        """
         versions = self.non_admin_versions_client.list_versions()
         expected_resources = ('id', 'links', 'media-types', 'status',
                               'updated')
@@ -49,6 +53,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('b9232f5e-d9e5-4d97-b96c-28d3db4de1bd')
     def test_api_version_resources(self):
+        """Test showing identity v3 api version resources"""
         descr = self.non_admin_client.show_api_description()['version']
         expected_resources = ('id', 'links', 'media-types', 'status',
                               'updated')
@@ -60,6 +65,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('657c1970-4722-4189-8831-7325f3bc4265')
     def test_api_media_types(self):
+        """Test showing identity v3 api version media type"""
         descr = self.non_admin_client.show_api_description()['version']
         # Get MIME type bases and descriptions
         media_types = [(media_type['base'], media_type['type']) for
@@ -75,6 +81,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('8879a470-abfb-47bb-bb8d-5a7fd279ad1e')
     def test_api_version_statuses(self):
+        """Test showing identity v3 api version status"""
         descr = self.non_admin_client.show_api_description()['version']
         status = descr['status'].lower()
         supported_statuses = ['current', 'stable', 'experimental',
diff --git a/tempest/api/identity/v3/test_domains.py b/tempest/api/identity/v3/test_domains.py
index 9f132dd..bb62ea6 100644
--- a/tempest/api/identity/v3/test_domains.py
+++ b/tempest/api/identity/v3/test_domains.py
@@ -21,6 +21,7 @@
 
 
 class DefaultDomainTestJSON(base.BaseIdentityV3Test):
+    """Test identity default domains"""
 
     @classmethod
     def setup_clients(cls):
@@ -35,5 +36,6 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('17a5de24-e6a0-4e4a-a9ee-d85b6e5612b5')
     def test_default_domain_exists(self):
+        """Test showing default domain"""
         domain = self.domains_client.show_domain(self.domain_id)['domain']
         self.assertTrue(domain['enabled'])
diff --git a/tempest/api/identity/v3/test_projects.py b/tempest/api/identity/v3/test_projects.py
index bbb4013..338b57b 100644
--- a/tempest/api/identity/v3/test_projects.py
+++ b/tempest/api/identity/v3/test_projects.py
@@ -19,11 +19,13 @@
 
 
 class IdentityV3ProjectsTest(base.BaseIdentityV3Test):
+    """Test identity projects"""
 
     credentials = ['primary', 'alt']
 
     @decorators.idempotent_id('86128d46-e170-4644-866a-cc487f699e1d')
     def test_list_projects_returns_only_authorized_projects(self):
+        """Test listing projects only returns authorized projects"""
         alt_project_name = self.os_alt.credentials.project_name
         resp = self.non_admin_users_client.list_user_projects(
             self.os_primary.credentials.user_id)
diff --git a/tempest/api/identity/v3/test_tokens.py b/tempest/api/identity/v3/test_tokens.py
index cb05f39..b201285 100644
--- a/tempest/api/identity/v3/test_tokens.py
+++ b/tempest/api/identity/v3/test_tokens.py
@@ -24,9 +24,11 @@
 
 
 class TokensV3Test(base.BaseIdentityV3Test):
+    """Test identity tokens"""
 
     @decorators.idempotent_id('a9512ac3-3909-48a4-b395-11f438e16260')
     def test_validate_token(self):
+        """Test validating token for user"""
         creds = self.os_primary.credentials
         user_id = creds.user_id
         username = creds.username
@@ -69,7 +71,7 @@
 
     @decorators.idempotent_id('6f8e4436-fc96-4282-8122-e41df57197a9')
     def test_create_token(self):
-
+        """Test creating token for user"""
         creds = self.os_primary.credentials
         user_id = creds.user_id
         username = creds.username
@@ -120,9 +122,12 @@
 
     @decorators.idempotent_id('0f9f5a5f-d5cd-4a86-8a5b-c5ded151f212')
     def test_token_auth_creation_existence_deletion(self):
-        # Tests basic token auth functionality in a way that is compatible with
-        # pre-provisioned credentials. The default user is used for token
-        # authentication.
+        """Test auth/check existence/delete token for user
+
+        Tests basic token auth functionality in a way that is compatible with
+        pre-provisioned credentials. The default user is used for token
+        authentication.
+        """
 
         # Valid user's token is authenticated
         user = self.os_primary.credentials
diff --git a/tempest/api/identity/v3/test_users.py b/tempest/api/identity/v3/test_users.py
index d4e7612..6425ea9 100644
--- a/tempest/api/identity/v3/test_users.py
+++ b/tempest/api/identity/v3/test_users.py
@@ -28,6 +28,7 @@
 
 
 class IdentityV3UsersTest(base.BaseIdentityV3Test):
+    """Test identity user password"""
 
     @classmethod
     def resource_setup(cls):
@@ -82,6 +83,7 @@
                       'immutable user source and solely '
                       'provides read-only access to users.')
     def test_user_update_own_password(self):
+        """Test updating user's own password"""
         old_pass = self.creds.password
         old_token = self.non_admin_client.token
         new_pass = data_utils.rand_password()
@@ -111,6 +113,7 @@
                       'immutable user source and solely '
                       'provides read-only access to users.')
     def test_password_history_check_self_service_api(self):
+        """Test checking password changing history"""
         old_pass = self.creds.password
         new_pass1 = data_utils.rand_password()
         new_pass2 = data_utils.rand_password()
@@ -141,6 +144,7 @@
                           'Security compliance not available.')
     @decorators.idempotent_id('a7ad8bbf-2cff-4520-8c1d-96332e151658')
     def test_user_account_lockout(self):
+        """Test locking out user account after failure attempts"""
         if (CONF.identity.user_lockout_failure_attempts <= 0 or
                 CONF.identity.user_lockout_duration <= 0):
             raise self.skipException(
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 ad7dfb3..a8dae7c 100644
--- a/tempest/api/network/admin/test_floating_ips_admin_actions.py
+++ b/tempest/api/network/admin/test_floating_ips_admin_actions.py
@@ -62,7 +62,7 @@
         This test performs below operations:
         1. Create couple floating ips for admin and non-admin users.
         2. Verify if admin can access all floating ips including other user
-           and non-admin user can only access its own floating ips.
+        and non-admin user can only access its own floating ips.
         """
         # Create floating ip from admin user
         floating_ip_admin = self.admin_floating_ips_client.create_floatingip(
diff --git a/tempest/api/network/admin/test_ports.py b/tempest/api/network/admin/test_ports.py
index 51f2857..5f9f29f 100644
--- a/tempest/api/network/admin/test_ports.py
+++ b/tempest/api/network/admin/test_ports.py
@@ -48,6 +48,8 @@
                      "name": data_utils.rand_name(self.__class__.__name__)}
         body = self.admin_ports_client.create_port(**post_body)
         port = body['port']
+        self.addCleanup(self.admin_ports_client.wait_for_resource_deletion,
+                        port['id'])
         self.addCleanup(
             test_utils.call_and_ignore_notfound_exc,
             self.admin_ports_client.delete_port, port['id'])
@@ -63,6 +65,8 @@
                      "name": data_utils.rand_name(self.__class__.__name__)}
         body = self.admin_ports_client.create_port(**post_body)
         port = body['port']
+        self.addCleanup(self.admin_ports_client.wait_for_resource_deletion,
+                        port['id'])
         self.addCleanup(
             test_utils.call_and_ignore_notfound_exc,
             self.admin_ports_client.delete_port, port['id'])
@@ -82,6 +86,8 @@
                      "name": data_utils.rand_name(self.__class__.__name__)}
         body = self.admin_ports_client.create_port(**post_body)
         port = body['port']
+        self.addCleanup(self.admin_ports_client.wait_for_resource_deletion,
+                        port['id'])
         self.addCleanup(
             test_utils.call_and_ignore_notfound_exc,
             self.admin_ports_client.delete_port, port['id'])
@@ -110,6 +116,8 @@
             name=data_utils.rand_name(self.__class__.__name__),
             network_id=self.network['id'])
         port = body['port']
+        self.addCleanup(self.admin_ports_client.wait_for_resource_deletion,
+                        port['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.admin_ports_client.delete_port, port['id'])
         body = self.admin_ports_client.show_port(port['id'])
diff --git a/tempest/api/network/test_ports.py b/tempest/api/network/test_ports.py
index c6d049a..479578d 100644
--- a/tempest/api/network/test_ports.py
+++ b/tempest/api/network/test_ports.py
@@ -77,6 +77,8 @@
             name=data_utils.rand_name(self.__class__.__name__))
         port = body['port']
         # Schedule port deletion with verification upon test completion
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        port['id'])
         self.addCleanup(self._delete_port, port['id'])
         self.assertTrue(port['admin_state_up'])
         # Verify port update
@@ -99,6 +101,10 @@
         created_ports = body['ports']
         port1 = created_ports[0]
         port2 = created_ports[1]
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        port1['id'])
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        port2['id'])
         self.addCleanup(self._delete_port, port1['id'])
         self.addCleanup(self._delete_port, port2['id'])
         self.assertEqual(port1['network_id'], network1['id'])
@@ -126,6 +132,8 @@
         body = self.ports_client.create_port(
             network_id=net_id,
             name=data_utils.rand_name(self.__class__.__name__))
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        body['port']['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.ports_client.delete_port, body['port']['id'])
         port = body['port']
@@ -183,11 +191,15 @@
         port_1 = self.ports_client.create_port(
             network_id=network['id'],
             name=data_utils.rand_name(self.__class__.__name__))
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        port_1['port']['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.ports_client.delete_port, port_1['port']['id'])
         port_2 = self.ports_client.create_port(
             network_id=network['id'],
             name=data_utils.rand_name(self.__class__.__name__))
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        port_2['port']['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.ports_client.delete_port, port_2['port']['id'])
         # List ports filtered by fixed_ips
@@ -241,6 +253,8 @@
             network_id=network['id'],
             name=data_utils.rand_name(self.__class__.__name__),
             fixed_ips=fixed_ips)
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        port_1['port']['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.ports_client.delete_port, port_1['port']['id'])
         fixed_ips = [{'subnet_id': subnet['id'], 'ip_address': ip_address_2}]
@@ -248,6 +262,8 @@
             network_id=network['id'],
             name=data_utils.rand_name(self.__class__.__name__),
             fixed_ips=fixed_ips)
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        port_2['port']['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.ports_client.delete_port, port_2['port']['id'])
 
@@ -307,6 +323,8 @@
         # Add router interface to port created above
         self.routers_client.add_router_interface(router['id'],
                                                  port_id=port['port']['id'])
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        port['port']['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.routers_client.remove_router_interface,
                         router['id'], port_id=port['port']['id'])
@@ -343,6 +361,8 @@
         # Create a port with multiple IP addresses
         port = self.create_port(network,
                                 fixed_ips=fixed_ips)
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        port['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.ports_client.delete_port, port['id'])
         self.assertEqual(2, len(port['fixed_ips']))
@@ -386,6 +406,8 @@
             "admin_state_up": True,
             "fixed_ips": fixed_ip_1}
         body = self.ports_client.create_port(**post_body)
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        body['port']['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.ports_client.delete_port, body['port']['id'])
         port = body['port']
@@ -456,6 +478,8 @@
             network_id=self.network['id'],
             mac_address=free_mac_address,
             name=data_utils.rand_name(self.__class__.__name__))
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        body['port']['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.ports_client.delete_port, body['port']['id'])
         port = body['port']
@@ -474,6 +498,8 @@
         network = self._create_network()
         self._create_subnet(network)
         port = self.create_port(network, security_groups=[])
+        self.addCleanup(self.ports_client.wait_for_resource_deletion,
+                        port['id'])
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.ports_client.delete_port, port['id'])
         self.assertIsNotNone(port['security_groups'])
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
index 80f790f..687fe57 100644
--- a/tempest/api/object_storage/test_account_bulk.py
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -21,6 +21,7 @@
 
 
 class BulkTest(base.BaseObjectTest):
+    """Test bulk operation of archived file"""
 
     def setUp(self):
         super(BulkTest, self).setUp()
@@ -70,7 +71,7 @@
     @decorators.idempotent_id('a407de51-1983-47cc-9f14-47c2b059413c')
     @utils.requires_ext(extension='bulk_upload', service='object')
     def test_extract_archive(self):
-        # Test bulk operation of file upload with an archived file
+        """Test bulk operation of file upload with an archived file"""
         filepath, container_name, object_name = self._create_archive()
         resp = self._upload_archive(filepath)
         self.containers.append(container_name)
@@ -95,7 +96,7 @@
     @decorators.idempotent_id('c075e682-0d2a-43b2-808d-4116200d736d')
     @utils.requires_ext(extension='bulk_delete', service='object')
     def test_bulk_delete(self):
-        # Test bulk operation of deleting multiple files
+        """Test bulk operation of deleting multiple files"""
         filepath, container_name, object_name = self._create_archive()
         self._upload_archive(filepath)
 
@@ -110,7 +111,7 @@
     @decorators.idempotent_id('dbea2bcb-efbb-4674-ac8a-a5a0e33d1d79')
     @utils.requires_ext(extension='bulk_delete', service='object')
     def test_bulk_delete_by_POST(self):
-        # Test bulk operation of deleting multiple files
+        """Test bulk operation of deleting multiple files by HTTP POST"""
         filepath, container_name, object_name = self._create_archive()
         self._upload_archive(filepath)
 
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index 48f42ec..6854bbe 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -22,6 +22,7 @@
 
 
 class AccountQuotasTest(base.BaseObjectTest):
+    """Test account quotas"""
 
     credentials = [['operator', CONF.object_storage.operator_role],
                    ['reseller', CONF.object_storage.reseller_admin_role]]
@@ -79,6 +80,7 @@
     @decorators.idempotent_id('a22ef352-a342-4587-8f47-3bbdb5b039c4')
     @utils.requires_ext(extension='account_quotas', service='object')
     def test_upload_valid_object(self):
+        """Test uploading valid object"""
         object_name = data_utils.rand_name(name="TestObject")
         data = data_utils.arbitrary_string()
         resp, _ = self.object_client.create_object(self.container_name,
diff --git a/tempest/api/object_storage/test_account_services_negative.py b/tempest/api/object_storage/test_account_services_negative.py
index 3e664d7..8d2a501 100644
--- a/tempest/api/object_storage/test_account_services_negative.py
+++ b/tempest/api/object_storage/test_account_services_negative.py
@@ -21,6 +21,7 @@
 
 
 class AccountNegativeTest(base.BaseObjectTest):
+    """Negative tests of account"""
 
     credentials = [['operator', CONF.object_storage.operator_role],
                    ['operator_alt', CONF.object_storage.operator_role]]
@@ -33,7 +34,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('070e6aca-6152-4867-868d-1118d68fb38c')
     def test_list_containers_with_non_authorized_user(self):
-        # list containers using non-authorized user
+        """Test listing containers using non-authorized user"""
 
         test_auth_provider = self.os_operator.auth_provider
         # Get auth for the test user
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
index e9ca0b1..c8731fe 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -22,6 +22,7 @@
 
 
 class ObjectTestACLs(base.BaseObjectTest):
+    """Test object ACLs"""
 
     credentials = [['operator', CONF.object_storage.operator_role],
                    ['operator_alt', CONF.object_storage.operator_role]]
@@ -36,7 +37,7 @@
 
     @decorators.idempotent_id('a3270f3f-7640-4944-8448-c7ea783ea5b6')
     def test_read_object_with_rights(self):
-        # attempt to read object using authorized user
+        """Test reading object using authorized user"""
         # update X-Container-Read metadata ACL
         tenant_id = self.os_roles_operator_alt.credentials.tenant_id
         user_id = self.os_roles_operator_alt.credentials.user_id
@@ -64,7 +65,7 @@
 
     @decorators.idempotent_id('aa58bfa5-40d9-4bc3-82b4-d07f4a9e392a')
     def test_write_object_with_rights(self):
-        # attempt to write object using authorized user
+        """Test writing object using authorized user"""
         # update X-Container-Write metadata ACL
         tenant_id = self.os_roles_operator_alt.credentials.tenant_id
         user_id = self.os_roles_operator_alt.credentials.user_id
diff --git a/tempest/api/object_storage/test_container_acl_negative.py b/tempest/api/object_storage/test_container_acl_negative.py
index 90b24b4..73d7f27 100644
--- a/tempest/api/object_storage/test_container_acl_negative.py
+++ b/tempest/api/object_storage/test_container_acl_negative.py
@@ -22,6 +22,7 @@
 
 
 class ObjectACLsNegativeTest(base.BaseObjectTest):
+    """Negative tests of object ACLs"""
 
     credentials = [['operator', CONF.object_storage.operator_role],
                    ['operator_alt', CONF.object_storage.operator_role]]
@@ -48,6 +49,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('af587587-0c24-4e15-9822-8352ce711013')
     def test_write_object_without_using_creds(self):
+        """Test writing object without using credentials"""
         # trying to create object with empty headers
         # X-Auth-Token is not provided
         object_name = data_utils.rand_name(name='Object')
@@ -62,6 +64,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('af85af0b-a025-4e72-a90e-121babf55720')
     def test_delete_object_without_using_creds(self):
+        """Test deleting object without using credentials"""
         # create object
         object_name = data_utils.rand_name(name='Object')
         self.object_client.create_object(self.container_name, object_name,
@@ -79,7 +82,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('63d84e37-55a6-42e2-9e5f-276e60e26a00')
     def test_write_object_with_non_authorized_user(self):
-        # attempt to upload another file using non-authorized user
+        """Test writing object with non-authorized user"""
         # User provided token is forbidden. ACL are not set
         object_name = data_utils.rand_name(name='Object')
         # trying to create object with non-authorized user
@@ -94,7 +97,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('abf63359-be52-4feb-87dd-447689fc77fd')
     def test_read_object_with_non_authorized_user(self):
-        # attempt to read object using non-authorized user
+        """Test reading object with non-authorized user"""
         # User provided token is forbidden. ACL are not set
         object_name = data_utils.rand_name(name='Object')
         resp, _ = self.object_client.create_object(
@@ -112,7 +115,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7343ac3d-cfed-4198-9bb0-00149741a492')
     def test_delete_object_with_non_authorized_user(self):
-        # attempt to delete object using non-authorized user
+        """Test deleting object with non-authorized user"""
         # User provided token is forbidden. ACL are not set
         object_name = data_utils.rand_name(name='Object')
         resp, _ = self.object_client.create_object(
@@ -130,7 +133,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9ed01334-01e9-41ea-87ea-e6f465582823')
     def test_read_object_without_rights(self):
-        # attempt to read object using non-authorized user
+        """Test reading object without rights"""
         # update X-Container-Read metadata ACL
         cont_headers = {'X-Container-Read': 'badtenant:baduser'}
         resp_meta, _ = (
@@ -155,7 +158,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a3a585a7-d8cf-4b65-a1a0-edc2b1204f85')
     def test_write_object_without_rights(self):
-        # attempt to write object using non-authorized user
+        """Test writing object without rights"""
         # update X-Container-Write metadata ACL
         cont_headers = {'X-Container-Write': 'badtenant:baduser'}
         resp_meta, _ = (
@@ -177,7 +180,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8ba512ad-aa6e-444e-b882-2906a0ea2052')
     def test_write_object_without_write_rights(self):
-        # attempt to write object using non-authorized user
+        """Test writing object without write rights"""
         # update X-Container-Read and X-Container-Write metadata ACL
         tenant_name = self.os_operator.credentials.tenant_name
         username = self.os_operator.credentials.username
@@ -203,7 +206,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b4e366f8-f185-47ab-b789-df4416f9ecdb')
     def test_delete_object_without_write_rights(self):
-        # attempt to delete object using non-authorized user
+        """Test deleting object without write rights"""
         # update X-Container-Read and X-Container-Write metadata ACL
         tenant_name = self.os_operator.credentials.tenant_name
         username = self.os_operator.credentials.username
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index cdc420e..7ad6f6f 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -19,6 +19,8 @@
 
 
 class ContainerTest(base.BaseObjectTest):
+    """Test containers"""
+
     def tearDown(self):
         self.delete_containers()
         super(ContainerTest, self).tearDown()
@@ -26,6 +28,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('92139d73-7819-4db1-85f8-3f2f22a8d91f')
     def test_create_container(self):
+        """Test creating container"""
         container_name = data_utils.rand_name(name='TestContainer')
         resp, _ = self.container_client.update_container(container_name)
         self.containers.append(container_name)
@@ -33,7 +36,7 @@
 
     @decorators.idempotent_id('49f866ed-d6af-4395-93e7-4187eb56d322')
     def test_create_container_overwrite(self):
-        # overwrite container with the same name
+        """Test overwriting container with the same name"""
         container_name = data_utils.rand_name(name='TestContainer')
         self.container_client.update_container(container_name)
         self.containers.append(container_name)
@@ -43,7 +46,7 @@
 
     @decorators.idempotent_id('c2ac4d59-d0f5-40d5-ba19-0635056d48cd')
     def test_create_container_with_metadata_key(self):
-        # create container with the blank value of metadata
+        """Test creating container with the blank value of metadata"""
         container_name = data_utils.rand_name(name='TestContainer')
         headers = {'X-Container-Meta-test-container-meta': ''}
         resp, _ = self.container_client.update_container(
@@ -60,7 +63,7 @@
 
     @decorators.idempotent_id('e1e8df32-7b22-44e1-aa08-ccfd8d446b58')
     def test_create_container_with_metadata_value(self):
-        # create container with metadata value
+        """Test creating container with metadata value"""
         container_name = data_utils.rand_name(name='TestContainer')
 
         # metadata name using underscores should be converted to hyphens
@@ -79,7 +82,7 @@
 
     @decorators.idempotent_id('24d16451-1c0c-4e4f-b59c-9840a3aba40e')
     def test_create_container_with_remove_metadata_key(self):
-        # create container with the blank value of remove metadata
+        """Test creating container with the blank value of remove metadata"""
         container_name = data_utils.rand_name(name='TestContainer')
         headers = {'X-Container-Meta-test-container-meta': 'Meta1'}
         self.container_client.update_container(container_name, **headers)
@@ -97,7 +100,7 @@
 
     @decorators.idempotent_id('8a21ebad-a5c7-4e29-b428-384edc8cd156')
     def test_create_container_with_remove_metadata_value(self):
-        # create container with remove metadata
+        """Test creating container with remove metadata"""
         container_name = data_utils.rand_name(name='TestContainer')
         headers = {'X-Container-Meta-test-container-meta': 'Meta1'}
         self.container_client.update_container(container_name, **headers)
@@ -114,6 +117,7 @@
 
     @decorators.idempotent_id('95d3a249-b702-4082-a2c4-14bb860cf06a')
     def test_delete_container(self):
+        """Test deleting container"""
         # create a container
         container_name = self.create_container()
         # delete container, success asserted within
@@ -123,7 +127,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('312ff6bd-5290-497f-bda1-7c5fec6697ab')
     def test_list_container_contents(self):
-        # get container contents list
+        """Test getting container contents list"""
         container_name = self.create_container()
         object_name, _ = self.create_object(container_name)
 
@@ -134,7 +138,7 @@
 
     @decorators.idempotent_id('4646ac2d-9bfb-4c7d-a3c5-0f527402b3df')
     def test_list_container_contents_with_no_object(self):
-        # get empty container contents list
+        """Test getting empty container contents list"""
         container_name = self.create_container()
 
         resp, object_list = self.container_client.list_container_objects(
@@ -144,7 +148,7 @@
 
     @decorators.idempotent_id('fe323a32-57b9-4704-a996-2e68f83b09bc')
     def test_list_container_contents_with_delimiter(self):
-        # get container contents list using delimiter param
+        """Test getting container contents list using delimiter param"""
         container_name = self.create_container()
         object_name = data_utils.rand_name(name='TestObject/')
         self.create_object(container_name, object_name)
@@ -158,7 +162,7 @@
 
     @decorators.idempotent_id('55b4fa5c-e12e-4ca9-8fcf-a79afe118522')
     def test_list_container_contents_with_end_marker(self):
-        # get container contents list using end_marker param
+        """Test getting container contents list using end_marker param"""
         container_name = self.create_container()
         object_name, _ = self.create_object(container_name)
 
@@ -171,7 +175,7 @@
 
     @decorators.idempotent_id('196f5034-6ab0-4032-9da9-a937bbb9fba9')
     def test_list_container_contents_with_format_json(self):
-        # get container contents list using format_json param
+        """Test getting container contents list using format_json param"""
         container_name = self.create_container()
         self.create_object(container_name)
 
@@ -190,7 +194,7 @@
 
     @decorators.idempotent_id('655a53ca-4d15-408c-a377-f4c6dbd0a1fa')
     def test_list_container_contents_with_format_xml(self):
-        # get container contents list using format_xml param
+        """Test getting container contents list using format_xml param"""
         container_name = self.create_container()
         self.create_object(container_name)
 
@@ -214,7 +218,7 @@
 
     @decorators.idempotent_id('297ec38b-2b61-4ff4-bcd1-7fa055e97b61')
     def test_list_container_contents_with_limit(self):
-        # get container contents list using limit param
+        """Test getting container contents list using limit param"""
         container_name = self.create_container()
         object_name, _ = self.create_object(container_name)
 
@@ -227,7 +231,7 @@
 
     @decorators.idempotent_id('c31ddc63-2a58-4f6b-b25c-94d2937e6867')
     def test_list_container_contents_with_marker(self):
-        # get container contents list using marker param
+        """Test getting container contents list using marker param"""
         container_name = self.create_container()
         object_name, _ = self.create_object(container_name)
 
@@ -240,7 +244,7 @@
 
     @decorators.idempotent_id('58ca6cc9-6af0-408d-aaec-2a6a7b2f0df9')
     def test_list_container_contents_with_path(self):
-        # get container contents list using path param
+        """Test getting container contents list using path param"""
         container_name = self.create_container()
         object_name = data_utils.rand_name(name='TestObject')
         object_name = 'Swift/' + object_name
@@ -255,7 +259,7 @@
 
     @decorators.idempotent_id('77e742c7-caf2-4ec9-8aa4-f7d509a3344c')
     def test_list_container_contents_with_prefix(self):
-        # get container contents list using prefix param
+        """Test getting container contents list using prefix param"""
         container_name = self.create_container()
         object_name, _ = self.create_object(container_name)
 
@@ -270,7 +274,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('96e68f0e-19ec-4aa2-86f3-adc6a45e14dd')
     def test_list_container_metadata(self):
-        # List container metadata
+        """Test listing container metadata"""
         container_name = self.create_container()
 
         metadata = {'name': 'Pictures'}
@@ -286,7 +290,7 @@
 
     @decorators.idempotent_id('a2faf936-6b13-4f8d-92a2-c2278355821e')
     def test_list_no_container_metadata(self):
-        # HEAD container without metadata
+        """Test listing container without metadata"""
         container_name = self.create_container()
 
         resp, _ = self.container_client.list_container_metadata(
@@ -296,7 +300,10 @@
 
     @decorators.idempotent_id('cf19bc0b-7e16-4a5a-aaed-cb0c2fe8deef')
     def test_update_container_metadata_with_create_and_delete_metadata(self):
-        # Send one request of adding and deleting metadata
+        """Test updating container with adding and deleting metadata
+
+        Send one request of adding and deleting metadata.
+        """
         container_name = data_utils.rand_name(name='TestContainer')
         metadata_1 = {'X-Container-Meta-test-container-meta1': 'Meta1'}
         self.container_client.update_container(container_name, **metadata_1)
@@ -319,7 +326,7 @@
 
     @decorators.idempotent_id('2ae5f295-4bf1-4e04-bfad-21e54b62cec5')
     def test_update_container_metadata_with_create_metadata(self):
-        # update container metadata using add metadata
+        """Test updating container metadata using add metadata"""
         container_name = self.create_container()
 
         metadata = {'test-container-meta1': 'Meta1'}
@@ -337,7 +344,7 @@
 
     @decorators.idempotent_id('3a5ce7d4-6e4b-47d0-9d87-7cd42c325094')
     def test_update_container_metadata_with_delete_metadata(self):
-        # update container metadata using delete metadata
+        """Test updating container metadata using delete metadata"""
         container_name = data_utils.rand_name(name='TestContainer')
         metadata = {'X-Container-Meta-test-container-meta1': 'Meta1'}
         self.container_client.update_container(container_name, **metadata)
@@ -355,7 +362,7 @@
 
     @decorators.idempotent_id('31f40a5f-6a52-4314-8794-cd89baed3040')
     def test_update_container_metadata_with_create_metadata_key(self):
-        # update container metadata with a blank value of metadata
+        """Test updating container metadata with a blank value of metadata"""
         container_name = self.create_container()
 
         metadata = {'test-container-meta1': ''}
@@ -371,7 +378,7 @@
 
     @decorators.idempotent_id('a2e36378-6f1f-43f4-840a-ffd9cfd61914')
     def test_update_container_metadata_with_delete_metadata_key(self):
-        # update container metadata with a blank value of metadata
+        """Test updating container metadata with a blank value of metadata"""
         container_name = data_utils.rand_name(name='TestContainer')
         headers = {'X-Container-Meta-test-container-meta1': 'Meta1'}
         self.container_client.update_container(container_name, **headers)
diff --git a/tempest/api/object_storage/test_container_services_negative.py b/tempest/api/object_storage/test_container_services_negative.py
index b8c83b7..31c33db 100644
--- a/tempest/api/object_storage/test_container_services_negative.py
+++ b/tempest/api/object_storage/test_container_services_negative.py
@@ -25,6 +25,7 @@
 
 
 class ContainerNegativeTest(base.BaseObjectTest):
+    """Negative tests of containers"""
 
     @classmethod
     def resource_setup(cls):
@@ -41,7 +42,7 @@
         CONF.object_storage_feature_enabled.discoverability,
         'Discoverability function is disabled')
     def test_create_container_name_exceeds_max_length(self):
-        # Attempts to create a container name that is longer than max
+        """Test creating container with name longer than max"""
         max_length = self.constraints['max_container_name_length']
         # create a container with long name
         container_name = data_utils.arbitrary_string(size=max_length + 1)
@@ -58,8 +59,7 @@
         CONF.object_storage_feature_enabled.discoverability,
         'Discoverability function is disabled')
     def test_create_container_metadata_name_exceeds_max_length(self):
-        # Attempts to create container with metadata name
-        # that is longer than max.
+        """Test creating container with metadata name longer than max"""
         max_length = self.constraints['max_meta_name_length']
         container_name = data_utils.rand_name(name='TestContainer')
         metadata_name = 'X-Container-Meta-' + data_utils.arbitrary_string(
@@ -77,8 +77,7 @@
         CONF.object_storage_feature_enabled.discoverability,
         'Discoverability function is disabled')
     def test_create_container_metadata_value_exceeds_max_length(self):
-        # Attempts to create container with metadata value
-        # that is longer than max.
+        """Test creating container with metadata value longer than max"""
         max_length = self.constraints['max_meta_value_length']
         container_name = data_utils.rand_name(name='TestContainer')
         metadata_value = data_utils.arbitrary_string(size=max_length + 1)
@@ -95,8 +94,7 @@
         CONF.object_storage_feature_enabled.discoverability,
         'Discoverability function is disabled')
     def test_create_container_metadata_exceeds_overall_metadata_count(self):
-        # Attempts to create container with metadata that exceeds the
-        # default count
+        """Test creating container with metadata exceeding default count"""
         max_count = self.constraints['max_meta_count']
         container_name = data_utils.rand_name(name='TestContainer')
         metadata = {}
@@ -113,8 +111,7 @@
     @decorators.attr(type=["negative"])
     @decorators.idempotent_id('1a95ab2e-b712-4a98-8a4d-8ce21b7557d6')
     def test_get_metadata_headers_with_invalid_container_name(self):
-        # Attempts to retrieve metadata headers with an invalid
-        # container name.
+        """Test getting metadata headers with invalid container name"""
         self.assertRaises(exceptions.NotFound,
                           self.container_client.list_container_metadata,
                           'invalid_container_name')
@@ -122,7 +119,7 @@
     @decorators.attr(type=["negative"])
     @decorators.idempotent_id('125a24fa-90a7-4cfc-b604-44e49d788390')
     def test_update_metadata_with_nonexistent_container_name(self):
-        # Attempts to update metadata using a nonexistent container name.
+        """Test updating metadata using a nonexistent container name"""
         metadata = {'animal': 'penguin'}
 
         self.assertRaises(
@@ -133,7 +130,7 @@
     @decorators.attr(type=["negative"])
     @decorators.idempotent_id('65387dbf-a0e2-4aac-9ddc-16eb3f1f69ba')
     def test_delete_with_nonexistent_container_name(self):
-        # Attempts to delete metadata using a nonexistent container name.
+        """Test deleting metadata using a non existent container name"""
         metadata = {'animal': 'penguin'}
 
         self.assertRaises(
@@ -144,8 +141,7 @@
     @decorators.attr(type=["negative"])
     @decorators.idempotent_id('14331d21-1e81-420a-beea-19cb5e5207f5')
     def test_list_all_container_objects_with_nonexistent_container(self):
-        # Attempts to get a listing of all objects on a container
-        # that doesn't exist.
+        """Test getting a list of all objects on a non existent container"""
         params = {'limit': 9999, 'format': 'json'}
         self.assertRaises(exceptions.NotFound,
                           self.container_client.list_container_objects,
@@ -154,8 +150,7 @@
     @decorators.attr(type=["negative"])
     @decorators.idempotent_id('86b2ab08-92d5-493d-acd2-85f0c848819e')
     def test_list_all_container_objects_on_deleted_container(self):
-        # Attempts to get a listing of all objects on a container
-        # that was deleted.
+        """Test getting a list of all objects on a deleted container"""
         container_name = self.create_container()
         # delete container
         resp, _ = self.container_client.delete_container(container_name)
@@ -168,6 +163,7 @@
     @decorators.attr(type=["negative"])
     @decorators.idempotent_id('42da116e-1e8c-4c96-9e06-2f13884ed2b1')
     def test_delete_non_empty_container(self):
+        """Test deleting a container with object in it"""
         # create a container and an object within it
         # attempt to delete a container that isn't empty.
         container_name = self.create_container()
diff --git a/tempest/api/object_storage/test_container_staticweb.py b/tempest/api/object_storage/test_container_staticweb.py
index 1243b83..ef98ed8 100644
--- a/tempest/api/object_storage/test_container_staticweb.py
+++ b/tempest/api/object_storage/test_container_staticweb.py
@@ -21,6 +21,7 @@
 
 
 class StaticWebTest(base.BaseObjectTest):
+    """Test static web"""
 
     @classmethod
     def resource_setup(cls):
@@ -47,6 +48,7 @@
     @decorators.idempotent_id('c1f055ab-621d-4a6a-831f-846fcb578b8b')
     @utils.requires_ext(extension='staticweb', service='object')
     def test_web_index(self):
+        """Test web index"""
         headers = {'web-index': self.object_name}
 
         self.container_client.create_update_or_delete_container_metadata(
@@ -79,6 +81,7 @@
     @decorators.idempotent_id('941814cf-db9e-4b21-8112-2b6d0af10ee5')
     @utils.requires_ext(extension='staticweb', service='object')
     def test_web_listing(self):
+        """Test web listing"""
         headers = {'web-listings': 'true'}
 
         self.container_client.create_update_or_delete_container_metadata(
@@ -111,6 +114,7 @@
     @decorators.idempotent_id('bc37ec94-43c8-4990-842e-0e5e02fc8926')
     @utils.requires_ext(extension='staticweb', service='object')
     def test_web_listing_css(self):
+        """Test web listing css"""
         headers = {'web-listings': 'true',
                    'web-listings-css': 'listings.css'}
 
@@ -134,6 +138,7 @@
     @decorators.idempotent_id('f18b4bef-212e-45e7-b3ca-59af3a465f82')
     @utils.requires_ext(extension='staticweb', service='object')
     def test_web_error(self):
+        """Test web error"""
         headers = {'web-listings': 'true',
                    'web-error': self.object_name}
 
diff --git a/tempest/api/object_storage/test_healthcheck.py b/tempest/api/object_storage/test_healthcheck.py
index 8e9e406..f5e2443 100644
--- a/tempest/api/object_storage/test_healthcheck.py
+++ b/tempest/api/object_storage/test_healthcheck.py
@@ -19,12 +19,14 @@
 
 
 class HealthcheckTest(base.BaseObjectTest):
+    """Test healthcheck"""
 
     def setUp(self):
         super(HealthcheckTest, self).setUp()
 
     @decorators.idempotent_id('db5723b1-f25c-49a9-bfeb-7b5640caf337')
     def test_get_healthcheck(self):
+        """Test getting healthcheck"""
         url = self.account_client._get_base_version_url() + "healthcheck"
         resp, body = self.account_client.raw_request(url, "GET")
         self.account_client._error_checker(resp, body)
diff --git a/tempest/api/object_storage/test_object_formpost.py b/tempest/api/object_storage/test_object_formpost.py
index cd834bf..d857d3b 100644
--- a/tempest/api/object_storage/test_object_formpost.py
+++ b/tempest/api/object_storage/test_object_formpost.py
@@ -25,6 +25,7 @@
 
 
 class ObjectFormPostTest(base.BaseObjectTest):
+    """Test object post with form"""
 
     metadata = {}
     containers = []
@@ -110,6 +111,7 @@
     @decorators.idempotent_id('80fac02b-6e54-4f7b-be0d-a965b5cbef76')
     @utils.requires_ext(extension='formpost', service='object')
     def test_post_object_using_form(self):
+        """Test posting object using form"""
         body, content_type = self.get_multipart_form()
 
         headers = {'Content-Type': content_type,
diff --git a/tempest/api/object_storage/test_object_formpost_negative.py b/tempest/api/object_storage/test_object_formpost_negative.py
index df6a0fd..0499eef 100644
--- a/tempest/api/object_storage/test_object_formpost_negative.py
+++ b/tempest/api/object_storage/test_object_formpost_negative.py
@@ -26,6 +26,7 @@
 
 
 class ObjectFormPostNegativeTest(base.BaseObjectTest):
+    """Negative tests of object post with form"""
 
     metadata = {}
     containers = []
@@ -112,6 +113,7 @@
     @utils.requires_ext(extension='formpost', service='object')
     @decorators.attr(type=['negative'])
     def test_post_object_using_form_expired(self):
+        """Test posting object using expired form"""
         body, content_type = self.get_multipart_form(expires=1)
         time.sleep(2)
 
@@ -129,6 +131,7 @@
     @utils.requires_ext(extension='formpost', service='object')
     @decorators.attr(type=['negative'])
     def test_post_object_using_form_invalid_signature(self):
+        """Test posting object using form with invalid signature"""
         self.key = "Wrong"
         body, content_type = self.get_multipart_form()
 
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index b99f93a..29354b6 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -25,6 +25,7 @@
 
 
 class ObjectTempUrlTest(base.BaseObjectTest):
+    """Test object temp url"""
 
     @classmethod
     def resource_setup(cls):
@@ -90,6 +91,7 @@
     @decorators.idempotent_id('f91c96d4-1230-4bba-8eb9-84476d18d991')
     @utils.requires_ext(extension='tempurl', service='object')
     def test_get_object_using_temp_url(self):
+        """Test getting object using temp url"""
         expires = self._get_expiry_date()
 
         # get a temp URL for the created object
@@ -109,6 +111,7 @@
     @decorators.idempotent_id('671f9583-86bd-4128-a034-be282a68c5d8')
     @utils.requires_ext(extension='tempurl', service='object')
     def test_get_object_using_temp_url_key_2(self):
+        """Test getting object using metadata 'Temp-URL-Key-2'"""
         key2 = 'Meta2-'
         metadata = {'Temp-URL-Key-2': key2}
         self.account_client.create_update_or_delete_account_metadata(
@@ -134,6 +137,7 @@
     @decorators.idempotent_id('9b08dade-3571-4152-8a4f-a4f2a873a735')
     @utils.requires_ext(extension='tempurl', service='object')
     def test_put_object_using_temp_url(self):
+        """Test putting object using temp url"""
         new_data = data_utils.random_bytes(size=len(self.object_name))
 
         expires = self._get_expiry_date()
@@ -160,6 +164,7 @@
     @decorators.idempotent_id('249a0111-5ad3-4534-86a7-1993d55f9185')
     @utils.requires_ext(extension='tempurl', service='object')
     def test_head_object_using_temp_url(self):
+        """Test HEAD operation of object using temp url"""
         expires = self._get_expiry_date()
 
         # get a temp URL for the created object
@@ -174,6 +179,7 @@
     @decorators.idempotent_id('9d9cfd90-708b-465d-802c-e4a8090b823d')
     @utils.requires_ext(extension='tempurl', service='object')
     def test_get_object_using_temp_url_with_inline_query_parameter(self):
+        """Test getting object using temp url with inline query parameter"""
         expires = self._get_expiry_date()
 
         # get a temp URL for the created object
diff --git a/tempest/api/object_storage/test_object_temp_url_negative.py b/tempest/api/object_storage/test_object_temp_url_negative.py
index 17ae6c1..bbb4827 100644
--- a/tempest/api/object_storage/test_object_temp_url_negative.py
+++ b/tempest/api/object_storage/test_object_temp_url_negative.py
@@ -26,6 +26,7 @@
 
 
 class ObjectTempUrlNegativeTest(base.BaseObjectTest):
+    """Negative tests of object temp url"""
 
     metadata = {}
     containers = []
@@ -96,7 +97,7 @@
     @decorators.idempotent_id('5a583aca-c804-41ba-9d9a-e7be132bdf0b')
     @utils.requires_ext(extension='tempurl', service='object')
     def test_get_object_after_expiration_time(self):
-
+        """Test getting object after expiration time"""
         expires = self._get_expiry_date(1)
         # get a temp URL for the created object
         url = self._get_temp_url(self.container_name,
diff --git a/tempest/api/object_storage/test_object_version.py b/tempest/api/object_storage/test_object_version.py
index 75111b6..b64b172 100644
--- a/tempest/api/object_storage/test_object_version.py
+++ b/tempest/api/object_storage/test_object_version.py
@@ -24,6 +24,8 @@
 
 
 class ContainerTest(base.BaseObjectTest):
+    """Test versioned container"""
+
     def assertContainer(self, container, count, byte, versioned):
         resp, _ = self.container_client.list_container_metadata(container)
         self.assertHeaders(resp, 'Container', 'HEAD')
@@ -39,6 +41,15 @@
         not CONF.object_storage_feature_enabled.object_versioning,
         'Object-versioning is disabled')
     def test_versioned_container(self):
+        """Test versioned container
+
+        1. create container1
+        2. create container2, with container1 as 'X-versions-Location' header
+        3. create object1 in container1
+        4. create 2nd version of object1
+        5. delete object version 2
+        6. delete object version 1
+        """
         # create container
         vers_container_name = data_utils.rand_name(name='TestVersionContainer')
         resp, _ = self.container_client.update_container(vers_container_name)
diff --git a/tempest/api/volume/admin/test_backends_capabilities.py b/tempest/api/volume/admin/test_backends_capabilities.py
index 1351704..3c76eca 100644
--- a/tempest/api/volume/admin/test_backends_capabilities.py
+++ b/tempest/api/volume/admin/test_backends_capabilities.py
@@ -20,6 +20,7 @@
 
 
 class BackendsCapabilitiesAdminTestsJSON(base.BaseVolumeAdminTest):
+    """Test backends capabilities"""
 
     @classmethod
     def resource_setup(cls):
@@ -32,14 +33,16 @@
 
     @decorators.idempotent_id('3750af44-5ea2-4cd4-bc3e-56e7e6caf854')
     def test_get_capabilities_backend(self):
-        # Test backend properties
+        """Test getting backend capabilities"""
         # Check response schema
         self.admin_capabilities_client.show_backend_capabilities(self.hosts[0])
 
     @decorators.idempotent_id('a9035743-d46a-47c5-9cb7-3c80ea16dea0')
     def test_compare_volume_stats_values(self):
-        # Test values comparison between show_backend_capabilities
-        # to show_pools
+        """Test comparing volume stats values
+
+        Compare volume stats between show_backend_capabilities and show_pools.
+        """
         VOLUME_STATS = ('vendor_name',
                         'volume_backend_name',
                         'storage_protocol')
diff --git a/tempest/api/volume/admin/test_group_snapshots.py b/tempest/api/volume/admin/test_group_snapshots.py
index c57766e..0a8b56d 100644
--- a/tempest/api/volume/admin/test_group_snapshots.py
+++ b/tempest/api/volume/admin/test_group_snapshots.py
@@ -62,12 +62,26 @@
 
 
 class GroupSnapshotsTest(BaseGroupSnapshotsTest):
+    """Test group snapshot"""
+
     _api_version = 3
     min_microversion = '3.14'
     max_microversion = 'latest'
 
     @decorators.idempotent_id('1298e537-f1f0-47a3-a1dd-8adec8168897')
     def test_group_snapshot_create_show_list_delete(self):
+        """Test create/show/list/delete group snapshot
+
+        1. Create volume type "volume_type1"
+        2. Create group type "group_type1"
+        3. Create group "group1" with "group_type1" and "volume_type1"
+        4. Create volume "volume1" with "volume_type1" and "group1"
+        5. Create group snapshot "group_snapshot1" with "group1"
+        6. Check snapshot created from "volume1" reaches available status
+        7. Check the created group snapshot "group_snapshot1" is in the list
+           of all group snapshots
+        8. Delete group snapshot "group_snapshot1"
+        """
         # Create volume type
         volume_type = self.create_volume_type()
 
@@ -118,6 +132,18 @@
 
     @decorators.idempotent_id('eff52c70-efc7-45ed-b47a-4ad675d09b81')
     def test_create_group_from_group_snapshot(self):
+        """Test creating group from group snapshot
+
+        1. Create volume type "volume_type1"
+        2. Create group type "group_type1"
+        3. Create group "group1" with "group_type1" and "volume_type1"
+        4. Create volume "volume1" with "volume_type1" and "group1"
+        5. Create group snapshot "group_snapshot1" with "group1"
+        6. Check snapshot created from "volume1" reaches available status
+        7. Create group "group2" from "group_snapshot1"
+        8. Check the volumes belonging to "group2" reach available status
+        9. Check "group2" reaches available status
+        """
         # Create volume type
         volume_type = self.create_volume_type()
 
@@ -161,6 +187,20 @@
     @decorators.idempotent_id('7d7fc000-0b4c-4376-a372-544116d2e127')
     @decorators.related_bug('1739031')
     def test_delete_group_snapshots_following_updated_volumes(self):
+        """Test deleting group snapshot following updated volumes
+
+        1. Create volume type "volume_type1"
+        2. Create group type "group_type1"
+        3. Create group "group1" with "group_type1" and "volume_type1"
+        4. Create 2 volumes "volume1" and "volume2"
+           with "volume_type1" and "group1"
+        5. For each created volume, removing and then adding back to "group1"
+        6. Create group snapshot "group_snapshot1" with "group1"
+        7. Check snapshots created from "volume1" and "volume2" reach
+           available status
+        8. Delete "group_snapshot1"
+        9. Check snapshots created from "volume1" and "volume2" are deleted
+        """
         volume_type = self.create_volume_type()
 
         group_type = self.create_group_type()
@@ -211,6 +251,8 @@
 
 
 class GroupSnapshotsV319Test(BaseGroupSnapshotsTest):
+    """Test group snapshot with volume microversion greater than 3.18"""
+
     _api_version = 3
     min_microversion = '3.19'
     max_microversion = 'latest'
@@ -218,6 +260,7 @@
     @decorators.idempotent_id('3b42c9b9-c984-4444-816e-ca2e1ed30b40')
     @decorators.skip_because(bug='1770179')
     def test_reset_group_snapshot_status(self):
+        """Test resetting group snapshot status to creating/available/error"""
         # Create volume type
         volume_type = self.create_volume_type()
 
diff --git a/tempest/api/volume/admin/test_group_type_specs.py b/tempest/api/volume/admin/test_group_type_specs.py
index c5e6d1a..159c6fb 100644
--- a/tempest/api/volume/admin/test_group_type_specs.py
+++ b/tempest/api/volume/admin/test_group_type_specs.py
@@ -19,12 +19,15 @@
 
 
 class GroupTypeSpecsTest(base.BaseVolumeAdminTest):
+    """Test group type specs"""
+
     _api_version = 3
     min_microversion = '3.11'
     max_microversion = 'latest'
 
     @decorators.idempotent_id('bb4e30d0-de6e-4f4d-866c-dcc48d023b4e')
     def test_group_type_specs_create_show_update_list_delete(self):
+        """Test create/show/update/list/delete group type specs"""
         # Create new group type
         group_type = self.create_group_type()
 
diff --git a/tempest/api/volume/admin/test_group_types.py b/tempest/api/volume/admin/test_group_types.py
index 6723207..3993020 100644
--- a/tempest/api/volume/admin/test_group_types.py
+++ b/tempest/api/volume/admin/test_group_types.py
@@ -19,13 +19,15 @@
 
 
 class GroupTypesTest(base.BaseVolumeAdminTest):
+    """Test group types"""
+
     _api_version = 3
     min_microversion = '3.11'
     max_microversion = 'latest'
 
     @decorators.idempotent_id('dd71e5f9-393e-4d4f-90e9-fa1b8d278864')
     def test_group_type_create_list_update_show(self):
-        # Create/list/show group type.
+        """Test create/list/update/show group type"""
         name = data_utils.rand_name(self.__class__.__name__ + '-group-type')
         description = data_utils.rand_name("group-type-description")
         group_specs = {"consistent_group_snapshot_enabled": "<is> False"}
diff --git a/tempest/api/volume/admin/test_multi_backend.py b/tempest/api/volume/admin/test_multi_backend.py
index c5c70d2..a5de987 100644
--- a/tempest/api/volume/admin/test_multi_backend.py
+++ b/tempest/api/volume/admin/test_multi_backend.py
@@ -21,6 +21,7 @@
 
 
 class VolumeMultiBackendTest(base.BaseVolumeAdminTest):
+    """Test volume multi backends"""
 
     @classmethod
     def skip_checks(cls):
@@ -78,24 +79,49 @@
 
     @decorators.idempotent_id('c1a41f3f-9dad-493e-9f09-3ff197d477cc')
     def test_backend_name_reporting(self):
-        # get volume id which created by type without prefix
+        """Test backend name reporting for volume when type is without prefix
+
+        1. Create volume type, with 'volume_backend_name' as extra spec key
+        2. Create volume using the created volume type
+        3. Check 'os-vol-host-attr:host' of the volume info, the value should
+           contain '@' character, like 'cinder@CloveStorage#tecs_backend'
+        """
         for volume_id in self.volume_id_list_without_prefix:
             self._test_backend_name_reporting_by_volume_id(volume_id)
 
     @decorators.idempotent_id('f38e647f-ab42-4a31-a2e7-ca86a6485215')
     def test_backend_name_reporting_with_prefix(self):
-        # get volume id which created by type with prefix
+        """Test backend name reporting for volume when type is with prefix
+
+        1. Create volume type, with 'capabilities:volume_backend_name' as
+           extra spec key
+        2. Create volume using the created volume type
+        3. Check 'os-vol-host-attr:host' of the volume info, the value should
+           contain '@' character, like 'cinder@CloveStorage#tecs_backend'
+        """
         for volume_id in self.volume_id_list_with_prefix:
             self._test_backend_name_reporting_by_volume_id(volume_id)
 
     @decorators.idempotent_id('46435ab1-a0af-4401-8373-f14e66b0dd58')
     def test_backend_name_distinction(self):
-        # get volume ids which created by type without prefix
+        """Test volume backend distinction when type is without prefix
+
+        1. For each backend, create volume type with 'volume_backend_name'
+           as extra spec key
+        2. Create volumes using the created volume types
+        3. Check 'os-vol-host-attr:host' of each created volume is different.
+        """
         self._test_backend_name_distinction(self.volume_id_list_without_prefix)
 
     @decorators.idempotent_id('4236305b-b65a-4bfc-a9d2-69cb5b2bf2ed')
     def test_backend_name_distinction_with_prefix(self):
-        # get volume ids which created by type without prefix
+        """Test volume backend distinction when type is with prefix
+
+        1. For each backend, create volume type with
+           'capabilities:volume_backend_name' as extra spec key
+        2. Create volumes using the created volume types
+        3. Check 'os-vol-host-attr:host' of each created volume is different.
+        """
         self._test_backend_name_distinction(self.volume_id_list_with_prefix)
 
     def _get_volume_host(self, volume_id):
diff --git a/tempest/api/volume/admin/test_snapshots_actions.py b/tempest/api/volume/admin/test_snapshots_actions.py
index 41849bc..4fca240 100644
--- a/tempest/api/volume/admin/test_snapshots_actions.py
+++ b/tempest/api/volume/admin/test_snapshots_actions.py
@@ -22,6 +22,8 @@
 
 
 class SnapshotsActionsTest(base.BaseVolumeAdminTest):
+    """Test volume snapshot actions"""
+
     @classmethod
     def skip_checks(cls):
         super(SnapshotsActionsTest, cls).skip_checks()
@@ -65,7 +67,7 @@
 
     @decorators.idempotent_id('3e13ca2f-48ea-49f3-ae1a-488e9180d535')
     def test_reset_snapshot_status(self):
-        # Reset snapshot status to creating
+        """Test resetting snapshot status to creating"""
         status = 'creating'
         self.admin_snapshots_client.reset_snapshot_status(
             self.snapshot['id'], status)
@@ -74,6 +76,10 @@
 
     @decorators.idempotent_id('41288afd-d463-485e-8f6e-4eea159413eb')
     def test_update_snapshot_status(self):
+        """Test updating snapshot
+
+        Update snapshot status to 'error' and progress to '80%'.
+        """
         # Reset snapshot status to creating
         status = 'creating'
         self.admin_snapshots_client.reset_snapshot_status(
@@ -95,20 +101,20 @@
 
     @decorators.idempotent_id('05f711b6-e629-4895-8103-7ca069f2073a')
     def test_snapshot_force_delete_when_snapshot_is_creating(self):
-        # test force delete when status of snapshot is creating
+        """Test force delete when status of snapshot is creating"""
         self._create_reset_and_force_delete_temp_snapshot('creating')
 
     @decorators.idempotent_id('92ce8597-b992-43a1-8868-6316b22a969e')
     def test_snapshot_force_delete_when_snapshot_is_deleting(self):
-        # test force delete when status of snapshot is deleting
+        """Test force delete when status of snapshot is deleting"""
         self._create_reset_and_force_delete_temp_snapshot('deleting')
 
     @decorators.idempotent_id('645a4a67-a1eb-4e8e-a547-600abac1525d')
     def test_snapshot_force_delete_when_snapshot_is_error(self):
-        # test force delete when status of snapshot is error
+        """Test force delete when status of snapshot is error"""
         self._create_reset_and_force_delete_temp_snapshot('error')
 
     @decorators.idempotent_id('bf89080f-8129-465e-9327-b2f922666ba5')
     def test_snapshot_force_delete_when_snapshot_is_error_deleting(self):
-        # test force delete when status of snapshot is error_deleting
+        """Test force delete when status of snapshot is error_deleting"""
         self._create_reset_and_force_delete_temp_snapshot('error_deleting')
diff --git a/tempest/api/volume/admin/test_volume_quota_classes.py b/tempest/api/volume/admin/test_volume_quota_classes.py
index ee52354..f482788 100644
--- a/tempest/api/volume/admin/test_volume_quota_classes.py
+++ b/tempest/api/volume/admin/test_volume_quota_classes.py
@@ -30,6 +30,7 @@
 
 
 class VolumeQuotaClassesTest(base.BaseVolumeAdminTest):
+    """Test volume quota classes"""
 
     def setUp(self):
         # Note(jeremy.zhang): All test cases in this class need to externally
@@ -44,6 +45,7 @@
 
     @decorators.idempotent_id('abb9198e-67d0-4b09-859f-4f4a1418f176')
     def test_show_default_quota(self):
+        """Test showing default volume quota class set"""
         # response body is validated by schema
         default_quotas = self.admin_quota_classes_client.show_quota_class_set(
             'default')['quota_class_set']
@@ -51,6 +53,11 @@
 
     @decorators.idempotent_id('a7644c63-2669-467a-b00e-452dd5c5397b')
     def test_update_default_quota(self):
+        """Test updating default volume quota class set
+
+        Check current project and new project's default quota are updated
+        to the provided one.
+        """
         LOG.debug("Get the current default quota class values")
         body = self.admin_quota_classes_client.show_quota_class_set(
             'default')['quota_class_set']
diff --git a/tempest/api/volume/admin/test_volume_quotas.py b/tempest/api/volume/admin/test_volume_quotas.py
index b073604..5ab8e87 100644
--- a/tempest/api/volume/admin/test_volume_quotas.py
+++ b/tempest/api/volume/admin/test_volume_quotas.py
@@ -22,6 +22,8 @@
 
 
 class VolumeQuotasAdminTestJSON(base.BaseVolumeAdminTest):
+    """Test volume quotas with admin privilege"""
+
     credentials = ['primary', 'alt', 'admin']
 
     def setUp(self):
@@ -54,17 +56,19 @@
 
     @decorators.idempotent_id('59eada70-403c-4cef-a2a3-a8ce2f1b07a0')
     def test_list_quotas(self):
+        """Test showing volume quota set"""
         # Check response schema
         self.admin_quotas_client.show_quota_set(self.demo_tenant_id)
 
     @decorators.idempotent_id('2be020a2-5fdd-423d-8d35-a7ffbc36e9f7')
     def test_list_default_quotas(self):
+        """Test showing volume default quota set"""
         # Check response schema
         self.admin_quotas_client.show_default_quota_set(self.demo_tenant_id)
 
     @decorators.idempotent_id('3d45c99e-cc42-4424-a56e-5cbd212b63a6')
     def test_update_all_quota_resources_for_tenant(self):
-        # Admin can update all the resource quota limits for a tenant
+        """Test admin can update all the volume quota limits for a project"""
         new_quota_set = {'gigabytes': 1009,
                          'volumes': 11,
                          'snapshots': 11,
@@ -87,14 +91,14 @@
 
     @decorators.idempotent_id('18c51ae9-cb03-48fc-b234-14a19374dbed')
     def test_show_quota_usage(self):
+        """Test showing volume quota usage"""
         # Check response schema
         self.admin_quotas_client.show_quota_set(
             self.os_admin.credentials.tenant_id, params={'usage': True})
 
     @decorators.idempotent_id('874b35a9-51f1-4258-bec5-cd561b6690d3')
     def test_delete_quota(self):
-        # Admin can delete the resource quota set for a project
-
+        """Test admin can delete the volume quota set for a project"""
         self.addCleanup(self.admin_quotas_client.update_quota_set,
                         self.demo_tenant_id, **self.cleanup_quota_set)
 
@@ -112,6 +116,7 @@
 
     @decorators.idempotent_id('ae8b6091-48ad-4bfa-a188-bbf5cc02115f')
     def test_quota_usage(self):
+        """Test volume quota usage is updated after creating volume"""
         quota_usage = self.admin_quotas_client.show_quota_set(
             self.demo_tenant_id, params={'usage': True})['quota_set']
 
@@ -131,6 +136,7 @@
 
     @decorators.idempotent_id('8911036f-9d54-4720-80cc-a1c9796a8805')
     def test_quota_usage_after_volume_transfer(self):
+        """Test volume quota usage is updated after transferring volume"""
         # Create a volume for transfer
         volume = self.create_volume()
         self.addCleanup(self.delete_volume,
diff --git a/tempest/api/volume/admin/test_volume_retype.py b/tempest/api/volume/admin/test_volume_retype.py
index 18e0b9b..5c14d52 100644
--- a/tempest/api/volume/admin/test_volume_retype.py
+++ b/tempest/api/volume/admin/test_volume_retype.py
@@ -88,6 +88,7 @@
 
 
 class VolumeRetypeWithMigrationTest(VolumeRetypeTest):
+    """Test volume retype with migration"""
 
     @classmethod
     def skip_checks(cls):
@@ -134,11 +135,25 @@
 
     @decorators.idempotent_id('a1a41f3f-9dad-493e-9f09-3ff197d477cd')
     def test_available_volume_retype_with_migration(self):
+        """Test volume retype with migration
+
+        1. Create volume1 with volume_type1
+        2. Retype volume1 to volume_type2 with migration_policy='on-demand'
+        3. Check volume1's volume_type is changed to volume_type2, and
+           'os-vol-host-attr:host' in the volume info is changed.
+        """
         src_vol = self.create_volume(volume_type=self.src_vol_type['name'])
         self._retype_volume(src_vol, migration_policy='on-demand')
 
     @decorators.idempotent_id('d0d9554f-e7a5-4104-8973-f35b27ccb60d')
     def test_volume_from_snapshot_retype_with_migration(self):
+        """Test volume created from snapshot retype with migration
+
+        1. Create volume1 from snapshot with volume_type1
+        2. Retype volume1 to volume_type2 with migration_policy='on-demand'
+        3. Check volume1's volume_type is changed to volume_type2, and
+           'os-vol-host-attr:host' in the volume info is changed.
+        """
         src_vol = self._create_volume_from_snapshot()
 
         # Migrate the volume from snapshot to the second backend
@@ -146,6 +161,7 @@
 
 
 class VolumeRetypeWithoutMigrationTest(VolumeRetypeTest):
+    """Test volume retype without migration"""
 
     @classmethod
     def resource_setup(cls):
@@ -174,6 +190,13 @@
 
     @decorators.idempotent_id('b90412ee-465d-46e9-b249-ec84a47d5f25')
     def test_available_volume_retype(self):
+        """Test volume retype without migration
+
+        1. Create volume1 with volume_type1
+        2. Retype volume1 to volume_type2 with migration_policy='never'
+        3. Check volume1's volume_type is changed to volume_type2, and
+           'os-vol-host-attr:host' in the volume info is not changed.
+        """
         src_vol = self.create_volume(volume_type=self.src_vol_type['name'])
 
         # Retype the volume from snapshot
diff --git a/tempest/api/volume/admin/test_volume_services.py b/tempest/api/volume/admin/test_volume_services.py
index 293af81..1d12a73 100644
--- a/tempest/api/volume/admin/test_volume_services.py
+++ b/tempest/api/volume/admin/test_volume_services.py
@@ -39,12 +39,14 @@
 
     @decorators.idempotent_id('e0218299-0a59-4f43-8b2b-f1c035b3d26d')
     def test_list_services(self):
+        """Test listing volume services"""
         services = (self.admin_volume_services_client.list_services()
                     ['services'])
         self.assertNotEmpty(services)
 
     @decorators.idempotent_id('63a3e1ca-37ee-4983-826d-83276a370d25')
     def test_get_service_by_service_binary_name(self):
+        """Test getting volume service by binary name"""
         services = (self.admin_volume_services_client.list_services(
             binary=self.binary_name)['services'])
         self.assertNotEmpty(services)
@@ -53,6 +55,7 @@
 
     @decorators.idempotent_id('178710e4-7596-4e08-9333-745cb8bc4f8d')
     def test_get_service_by_host_name(self):
+        """Test getting volume service by service host name"""
         services_on_host = [service for service in self.services if
                             _get_host(service['host']) == self.host_name]
 
@@ -69,6 +72,7 @@
 
     @decorators.idempotent_id('67ec6902-f91d-4dec-91fa-338523208bbc')
     def test_get_service_by_volume_host_name(self):
+        """Test getting volume service by volume host name"""
         volume_id = self.create_volume()['id']
         volume = self.admin_volume_client.show_volume(volume_id)['volume']
         hostname = _get_host(volume['os-vol-host-attr:host'])
@@ -83,7 +87,7 @@
 
     @decorators.idempotent_id('ffa6167c-4497-4944-a464-226bbdb53908')
     def test_get_service_by_service_and_host_name(self):
-
+        """Test getting volume service by binary name and host name"""
         services = (self.admin_volume_services_client.list_services(
             host=self.host_name, binary=self.binary_name))['services']
 
diff --git a/tempest/api/volume/admin/test_volume_services_negative.py b/tempest/api/volume/admin/test_volume_services_negative.py
index 3a863a1..bf39be5 100644
--- a/tempest/api/volume/admin/test_volume_services_negative.py
+++ b/tempest/api/volume/admin/test_volume_services_negative.py
@@ -19,6 +19,7 @@
 
 
 class VolumeServicesNegativeTest(base.BaseVolumeAdminTest):
+    """Negative tests of volume services"""
 
     @classmethod
     def resource_setup(cls):
@@ -30,6 +31,7 @@
     @decorators.attr(type='negative')
     @decorators.idempotent_id('3246ce65-ba70-4159-aa3b-082c28e4b484')
     def test_enable_service_with_invalid_host(self):
+        """Test enabling volume service with invalid host should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.admin_volume_services_client.enable_service,
                           host='invalid_host', binary=self.binary)
@@ -37,6 +39,7 @@
     @decorators.attr(type='negative')
     @decorators.idempotent_id('c571f179-c6e6-4c50-a0ab-368b628a8ac1')
     def test_disable_service_with_invalid_binary(self):
+        """Test disabling volume service with invalid binary should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.admin_volume_services_client.disable_service,
                           host=self.host, binary='invalid_binary')
@@ -44,6 +47,7 @@
     @decorators.attr(type='negative')
     @decorators.idempotent_id('77767b36-5e8f-4c68-a0b5-2308cc21ec64')
     def test_disable_log_reason_with_no_reason(self):
+        """Test disabling volume service with none reason should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.admin_volume_services_client.disable_log_reason,
                           host=self.host, binary=self.binary,
@@ -52,6 +56,7 @@
     @decorators.attr(type='negative')
     @decorators.idempotent_id('712bfab8-1f44-4eb5-a632-fa70bf78f05e')
     def test_freeze_host_with_invalid_host(self):
+        """Test freezing volume service with invalid host should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.admin_volume_services_client.freeze_host,
                           host='invalid_host')
@@ -59,6 +64,7 @@
     @decorators.attr(type='negative')
     @decorators.idempotent_id('7c6287c9-d655-47e1-9a11-76f6657a6dce')
     def test_thaw_host_with_invalid_host(self):
+        """Test thawing volume service with invalid host should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.admin_volume_services_client.thaw_host,
                           host='invalid_host')
diff --git a/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
index ff5e7e2..10fd485 100644
--- a/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
+++ b/tempest/api/volume/admin/test_volume_snapshot_quotas_negative.py
@@ -24,6 +24,7 @@
 
 
 class VolumeSnapshotQuotasNegativeTestJSON(base.BaseVolumeAdminTest):
+    """Negative tests of volume snapshot quotas"""
 
     @classmethod
     def skip_checks(cls):
@@ -67,6 +68,7 @@
     @decorators.attr(type='negative')
     @decorators.idempotent_id('02bbf63f-6c05-4357-9d98-2926a94064ff')
     def test_quota_volume_snapshots(self):
+        """Test creating snapshot exceeding snapshots quota should fail"""
         self.assertRaises(lib_exc.OverLimit,
                           self.snapshots_client.create_snapshot,
                           volume_id=self.volume['id'])
@@ -74,6 +76,7 @@
     @decorators.attr(type='negative')
     @decorators.idempotent_id('c99a1ca9-6cdf-498d-9fdf-25832babef27')
     def test_quota_volume_gigabytes_snapshots(self):
+        """Test creating snapshot exceeding gigabytes quota should fail"""
         self.addCleanup(self.admin_quotas_client.update_quota_set,
                         self.demo_tenant_id,
                         **self.shared_quota_set)
diff --git a/tempest/api/volume/admin/test_volume_type_access.py b/tempest/api/volume/admin/test_volume_type_access.py
index b64face..55ec428 100644
--- a/tempest/api/volume/admin/test_volume_type_access.py
+++ b/tempest/api/volume/admin/test_volume_type_access.py
@@ -24,11 +24,13 @@
 
 
 class VolumeTypesAccessTest(base.BaseVolumeAdminTest):
+    """Test volume type access"""
 
     credentials = ['primary', 'alt', 'admin']
 
     @decorators.idempotent_id('d4dd0027-835f-4554-a6e5-50903fb79184')
     def test_volume_type_access_add(self):
+        """Test adding volume type access for non-admin project"""
         # Creating a NON public volume type
         params = {'os-volume-type-access:is_public': False}
         volume_type = self.create_volume_type(**params)
@@ -52,6 +54,7 @@
 
     @decorators.idempotent_id('5220eb28-a435-43ce-baaf-ed46f0e95159')
     def test_volume_type_access_list(self):
+        """Test listing volume type access"""
         # Creating a NON public volume type
         params = {'os-volume-type-access:is_public': False}
         volume_type = self.create_volume_type(**params)
diff --git a/tempest/api/volume/admin/test_volume_types.py b/tempest/api/volume/admin/test_volume_types.py
index ecc850e..ebcd3b7 100644
--- a/tempest/api/volume/admin/test_volume_types.py
+++ b/tempest/api/volume/admin/test_volume_types.py
@@ -23,17 +23,18 @@
 
 
 class VolumeTypesTest(base.BaseVolumeAdminTest):
+    """Test volume types"""
 
     @decorators.idempotent_id('9d9b28e3-1b2e-4483-a2cc-24aa0ea1de54')
     def test_volume_type_list(self):
-        # List volume types.
+        """Test listing volume types"""
         body = \
             self.admin_volume_types_client.list_volume_types()['volume_types']
         self.assertIsInstance(body, list)
 
     @decorators.idempotent_id('c03cc62c-f4e9-4623-91ec-64ce2f9c1260')
     def test_volume_crud_with_volume_type_and_extra_specs(self):
-        # Create/update/get/delete volume with volume_type and extra spec.
+        """Test create/update/get/delete volume with volume_type"""
         volume_types = list()
         vol_name = data_utils.rand_name(self.__class__.__name__ + '-volume')
         proto = CONF.volume.storage_protocol
@@ -80,7 +81,7 @@
 
     @decorators.idempotent_id('4e955c3b-49db-4515-9590-0c99f8e471ad')
     def test_volume_type_create_get_delete(self):
-        # Create/get volume type.
+        """Test create/get/delete volume type"""
         name = data_utils.rand_name(self.__class__.__name__ + '-volume-type')
         description = data_utils.rand_name("volume-type-description")
         proto = CONF.volume.storage_protocol
@@ -118,7 +119,7 @@
 
     @decorators.idempotent_id('7830abd0-ff99-4793-a265-405684a54d46')
     def test_volume_type_encryption_create_get_update_delete(self):
-        # Create/get/update/delete encryption type.
+        """Test create/get/update/delete volume encryption type"""
         create_kwargs = {'provider': 'LuksEncryptor',
                          'control_location': 'front-end'}
         volume_type_id = self.create_volume_type()['id']
@@ -175,6 +176,7 @@
 
     @decorators.idempotent_id('cf9f07c6-db9e-4462-a243-5933ad65e9c8')
     def test_volume_type_update(self):
+        """Test updating volume type details"""
         # Create volume type
         volume_type = self.create_volume_type()
 
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs.py b/tempest/api/volume/admin/test_volume_types_extra_specs.py
index 730acdf..852aa93 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs.py
@@ -19,6 +19,7 @@
 
 
 class VolumeTypesExtraSpecsTest(base.BaseVolumeAdminTest):
+    """Test volume type extra specs"""
 
     @classmethod
     def resource_setup(cls):
@@ -27,7 +28,7 @@
 
     @decorators.idempotent_id('b42923e9-0452-4945-be5b-d362ae533e60')
     def test_volume_type_extra_specs_list(self):
-        # List Volume types extra specs.
+        """Test listing volume type extra specs"""
         extra_specs = {"spec1": "val1"}
         body = self.admin_volume_types_client.create_volume_type_extra_specs(
             self.volume_type['id'], extra_specs)['extra_specs']
@@ -40,7 +41,7 @@
 
     @decorators.idempotent_id('0806db36-b4a0-47a1-b6f3-c2e7f194d017')
     def test_volume_type_extra_specs_update(self):
-        # Update volume type extra specs
+        """Test updating volume type extra specs"""
         extra_specs = {"spec2": "val1"}
         body = self.admin_volume_types_client.create_volume_type_extra_specs(
             self.volume_type['id'], extra_specs)['extra_specs']
@@ -74,7 +75,7 @@
 
     @decorators.idempotent_id('d4772798-601f-408a-b2a5-29e8a59d1220')
     def test_volume_type_extra_spec_create_get_delete(self):
-        # Create/Get/Delete volume type extra spec.
+        """Test Create/Get/Delete volume type extra specs"""
         spec_key = "spec3"
         extra_specs = {spec_key: "val1"}
         body = self.admin_volume_types_client.create_volume_type_extra_specs(
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
index fe249d6..6b2a278 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
@@ -20,6 +20,7 @@
 
 
 class ExtraSpecsNegativeTest(base.BaseVolumeAdminTest):
+    """Negative tests of volume type extra specs"""
 
     @classmethod
     def resource_setup(cls):
@@ -30,7 +31,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('08961d20-5cbb-4910-ac0f-89ad6dbb2da1')
     def test_update_no_body(self):
-        # Should not update volume type extra specs with no body
+        """Test updating volume type extra specs with no body should fail"""
         self.assertRaises(
             lib_exc.BadRequest,
             self.admin_volume_types_client.update_volume_type_extra_specs,
@@ -39,7 +40,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('25e5a0ee-89b3-4c53-8310-236f76c75365')
     def test_update_nonexistent_extra_spec_id(self):
-        # Should not update volume type extra specs with nonexistent id.
+        """Test updating volume type extra specs with non existent name
+
+        Updating volume type extra specs with non existent extra spec name
+        should fail.
+        """
         extra_spec = {"spec1": "val2"}
         self.assertRaises(
             lib_exc.BadRequest,
@@ -50,7 +55,10 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9bf7a657-b011-4aec-866d-81c496fbe5c8')
     def test_update_none_extra_spec_id(self):
-        # Should not update volume type extra specs with none id.
+        """Test updating volume type extra specs without name
+
+        Updating volume type extra specs without extra spec name should fail.
+        """
         extra_spec = {"spec1": "val2"}
         self.assertRaises(
             lib_exc.BadRequest,
@@ -60,8 +68,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a77dfda2-9100-448e-9076-ed1711f4bdfc')
     def test_update_multiple_extra_spec(self):
-        # Should not update volume type extra specs with multiple specs as
-        # body.
+        """Test updating multiple volume type extra specs should fail"""
         extra_spec = {"spec1": "val2", "spec2": "val1"}
         self.assertRaises(
             lib_exc.BadRequest,
@@ -72,8 +79,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('49d5472c-a53d-4eab-a4d3-450c4db1c545')
     def test_create_nonexistent_type_id(self):
-        # Should not create volume type extra spec for nonexistent volume
-        # type id.
+        """Test creating volume type extra specs for non existent volume type
+
+        Creating volume type extra specs for non existent volume type should
+        fail.
+        """
         extra_specs = {"spec2": "val1"}
         self.assertRaises(
             lib_exc.NotFound,
@@ -83,7 +93,10 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c821bdc8-43a4-4bf4-86c8-82f3858d5f7d')
     def test_create_none_body(self):
-        # Should not create volume type extra spec for none POST body.
+        """Test creating volume type extra spec with none POST body
+
+        Creating volume type extra spec with none POST body should fail.
+        """
         self.assertRaises(
             lib_exc.BadRequest,
             self.admin_volume_types_client.create_volume_type_extra_specs,
@@ -92,7 +105,10 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('bc772c71-1ed4-4716-b945-8b5ed0f15e87')
     def test_create_invalid_body(self):
-        # Should not create volume type extra spec for invalid POST body.
+        """Test creating volume type extra spec with invalid POST body
+
+        Creating volume type extra spec with invalid POST body should fail.
+        """
         self.assertRaises(
             lib_exc.BadRequest,
             self.admin_volume_types_client.create_volume_type_extra_specs,
@@ -101,8 +117,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('031cda8b-7d23-4246-8bf6-bbe73fd67074')
     def test_delete_nonexistent_volume_type_id(self):
-        # Should not delete volume type extra spec for nonexistent
-        # type id.
+        """Test deleting volume type extra spec for non existent volume type
+
+        Deleting volume type extra spec for non existent volume type should
+        fail.
+        """
         self.assertRaises(
             lib_exc.NotFound,
             self.admin_volume_types_client.delete_volume_type_extra_specs,
@@ -111,7 +130,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('dee5cf0c-cdd6-4353-b70c-e847050d71fb')
     def test_list_nonexistent_volume_type_id(self):
-        # Should not list volume type extra spec for nonexistent type id.
+        """Test listing volume type extra spec for non existent volume type
+
+        Listing volume type extra spec for non existent volume type should
+        fail.
+        """
         self.assertRaises(
             lib_exc.NotFound,
             self.admin_volume_types_client.list_volume_types_extra_specs,
@@ -120,7 +143,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9f402cbd-1838-4eb4-9554-126a6b1908c9')
     def test_get_nonexistent_volume_type_id(self):
-        # Should not get volume type extra spec for nonexistent type id.
+        """Test getting volume type extra spec for non existent volume type
+
+        Getting volume type extra spec for non existent volume type should
+        fail.
+        """
         self.assertRaises(
             lib_exc.NotFound,
             self.admin_volume_types_client.show_volume_type_extra_specs,
@@ -129,8 +156,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c881797d-12ff-4f1a-b09d-9f6212159753')
     def test_get_nonexistent_extra_spec_name(self):
-        # Should not get volume type extra spec for nonexistent extra spec
-        # name.
+        """Test getting volume type extra spec for non existent spec name
+
+        Getting volume type extra spec for non existent extra spec name should
+        fail.
+        """
         self.assertRaises(
             lib_exc.NotFound,
             self.admin_volume_types_client.show_volume_type_extra_specs,
diff --git a/tempest/api/volume/admin/test_volumes_actions.py b/tempest/api/volume/admin/test_volumes_actions.py
index 33e503f..ecddfba 100644
--- a/tempest/api/volume/admin/test_volumes_actions.py
+++ b/tempest/api/volume/admin/test_volumes_actions.py
@@ -108,5 +108,4 @@
         waiters.wait_for_volume_resource_status(self.volumes_client,
                                                 volume_id, 'available')
         vol_info = self.volumes_client.show_volume(volume_id)['volume']
-        self.assertIn('attachments', vol_info)
         self.assertEmpty(vol_info['attachments'])
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 45060d0..835cc1d 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -26,6 +26,7 @@
 
 
 class VolumesBackupsAdminTest(base.BaseVolumeAdminTest):
+    """Test volume backups"""
 
     @classmethod
     def skip_checks(cls):
@@ -67,11 +68,8 @@
         # Export Backup
         export_backup = (self.admin_backups_client.export_backup(backup['id'])
                          ['backup-record'])
-        self.assertIn('backup_service', export_backup)
-        self.assertIn('backup_url', export_backup)
         self.assertTrue(export_backup['backup_service'].startswith(
                         'cinder.backup.drivers'))
-        self.assertIsNotNone(export_backup['backup_url'])
 
         # NOTE(geguileo): Backups are imported with the same backup id
         # (important for incremental backups among other things), so we cannot
@@ -92,7 +90,6 @@
         # deletions will delete data from the backup back-end because they
         # were both pointing to the same backend data.
         self.addCleanup(self._delete_backup, new_id)
-        self.assertIn("id", import_backup)
         self.assertEqual(new_id, import_backup['id'])
         waiters.wait_for_volume_resource_status(self.admin_backups_client,
                                                 import_backup['id'],
@@ -122,6 +119,7 @@
 
     @decorators.idempotent_id('47a35425-a891-4e13-961c-c45deea21e94')
     def test_volume_backup_reset_status(self):
+        """Test resetting volume backup status to error"""
         # Create a volume
         volume = self.create_volume()
         # Create a backup
diff --git a/tempest/api/volume/admin/test_volumes_list.py b/tempest/api/volume/admin/test_volumes_list.py
index 6ce4a85..c3229f0 100644
--- a/tempest/api/volume/admin/test_volumes_list.py
+++ b/tempest/api/volume/admin/test_volumes_list.py
@@ -24,6 +24,7 @@
 
 
 class VolumesListAdminTestJSON(base.BaseVolumeAdminTest):
+    """Test listing volumes with admin privilege"""
 
     @classmethod
     def resource_setup(cls):
@@ -41,7 +42,7 @@
 
     @decorators.idempotent_id('5866286f-3290-4cfd-a414-088aa6cdc469')
     def test_volume_list_param_tenant(self):
-        # Test to list volumes from single tenant
+        """Test admin can list volumes belonging to specified project"""
         # Create a volume in admin tenant
         adm_vol = self.admin_volume_client.create_volume(
             size=CONF.volume.volume_size)['volume']
diff --git a/tempest/api/volume/test_availability_zone.py b/tempest/api/volume/test_availability_zone.py
index 0b6ee38..39369be 100644
--- a/tempest/api/volume/test_availability_zone.py
+++ b/tempest/api/volume/test_availability_zone.py
@@ -22,7 +22,7 @@
 
     @decorators.idempotent_id('01f1ae88-eba9-4c6b-a011-6f7ace06b725')
     def test_get_availability_zone_list(self):
-        # List of availability zone
+        """Test listing volume available zones"""
         availability_zone = (
             self.availability_zone_client.list_availability_zones()
             ['availabilityZoneInfo'])
diff --git a/tempest/api/volume/test_extensions.py b/tempest/api/volume/test_extensions.py
index 39ce00c..acd9ca2 100644
--- a/tempest/api/volume/test_extensions.py
+++ b/tempest/api/volume/test_extensions.py
@@ -26,10 +26,11 @@
 
 
 class ExtensionsTestJSON(base.BaseVolumeTest):
+    """Test volume extensions"""
 
     @decorators.idempotent_id('94607eb0-43a5-47ca-82aa-736b41bd2e2c')
     def test_list_extensions(self):
-        # List of all extensions
+        """Test listing volume extensions"""
         extensions = (self.volumes_extension_client.list_extensions()
                       ['extensions'])
         if not CONF.volume_feature_enabled.api_extensions:
diff --git a/tempest/api/volume/test_image_metadata.py b/tempest/api/volume/test_image_metadata.py
index 53b3acc..8f9bbd2 100644
--- a/tempest/api/volume/test_image_metadata.py
+++ b/tempest/api/volume/test_image_metadata.py
@@ -24,6 +24,7 @@
 
 
 class VolumesImageMetadata(base.BaseVolumeTest):
+    """Test volume image metadata"""
 
     @classmethod
     def skip_checks(cls):
@@ -41,6 +42,7 @@
     @decorators.idempotent_id('03efff0b-5c75-4822-8f10-8789ac15b13e')
     @utils.services('image')
     def test_update_show_delete_image_metadata(self):
+        """Test update/show/delete volume's image metadata"""
         # Update image metadata
         image_metadata = {'image_id': '5137a025-3c5f-43c1-bc64-5f41270040a5',
                           'image_name': 'image',
diff --git a/tempest/api/volume/test_snapshot_metadata.py b/tempest/api/volume/test_snapshot_metadata.py
index e6fe25d..ee1b5e5 100644
--- a/tempest/api/volume/test_snapshot_metadata.py
+++ b/tempest/api/volume/test_snapshot_metadata.py
@@ -23,6 +23,8 @@
 
 
 class SnapshotMetadataTestJSON(base.BaseVolumeTest):
+    """Test snapshot metadata"""
+
     @classmethod
     def skip_checks(cls):
         super(SnapshotMetadataTestJSON, cls).skip_checks()
@@ -45,6 +47,7 @@
 
     @decorators.idempotent_id('a2f20f99-e363-4584-be97-bc33afb1a56c')
     def test_crud_snapshot_metadata(self):
+        """Test create/get/update/delete snapshot metadata"""
         # Create metadata for the snapshot
         metadata = {"key1": "value1",
                     "key2": "value2",
@@ -82,7 +85,7 @@
 
     @decorators.idempotent_id('e8ff85c5-8f97-477f-806a-3ac364a949ed')
     def test_update_show_snapshot_metadata_item(self):
-        # Update metadata item for the snapshot
+        """Test update/show snapshot metadata item"""
         metadata = {"key1": "value1",
                     "key2": "value2",
                     "key3": "value3"}
diff --git a/tempest/api/volume/test_versions.py b/tempest/api/volume/test_versions.py
index 1e5c9de..e065bdf 100644
--- a/tempest/api/volume/test_versions.py
+++ b/tempest/api/volume/test_versions.py
@@ -17,14 +17,14 @@
 
 
 class VersionsTest(base.BaseVolumeTest):
-    """Test cinder versions"""
+    """Test volume versions"""
 
     _api_version = 3
 
     @decorators.idempotent_id('77838fc4-b49b-4c64-9533-166762517369')
     @decorators.attr(type='smoke')
     def test_list_versions(self):
-        """Test listing cinder versions"""
+        """Test listing volume versions"""
         # NOTE: The version data is checked on service client side
         #       with JSON-Schema validation. It is enough to just call
         #       the API here.
@@ -32,7 +32,7 @@
 
     @decorators.idempotent_id('7f755ae2-caa9-4049-988c-331d8f7a579f')
     def test_show_version(self):
-        "Test getting cinder version details"
+        """Test getting volume version details"""
         # NOTE: The version data is checked on service client side
         # with JSON-Schema validation. So we will loop through each
         # version and call show version.
diff --git a/tempest/api/volume/test_volume_absolute_limits.py b/tempest/api/volume/test_volume_absolute_limits.py
index 4d64a95..ccf0804 100644
--- a/tempest/api/volume/test_volume_absolute_limits.py
+++ b/tempest/api/volume/test_volume_absolute_limits.py
@@ -24,6 +24,7 @@
 # it requires force_tenant_isolation=True, which need admin
 # credentials to create non-admin users for the tests.
 class AbsoluteLimitsTests(base.BaseVolumeAdminTest):  # noqa: T115
+    """Test volume absolute limits"""
 
     # avoid existing volumes of pre-defined tenant
     force_tenant_isolation = True
@@ -43,7 +44,7 @@
 
     @decorators.idempotent_id('8e943f53-e9d6-4272-b2e9-adcf2f7c29ad')
     def test_get_volume_absolute_limits(self):
-        # get volume limit for a tenant
+        """Test getting volume absolute limits"""
         absolute_limits = \
             self.volume_limits_client.show_limits(
             )['limits']['absolute']
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index 9edffc6..5b50bfa 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -25,6 +25,8 @@
 
 
 class VolumesActionsTest(base.BaseVolumeTest):
+    """Test volume actions"""
+
     create_default_network = True
 
     @classmethod
@@ -38,6 +40,7 @@
     @decorators.attr(type='smoke')
     @utils.services('compute')
     def test_attach_detach_volume_to_instance(self):
+        """Test attaching and detaching volume to instance"""
         # Create a server
         server = self.create_server()
         # Volume is attached and detached successfully from an instance
@@ -53,7 +56,7 @@
 
     @decorators.idempotent_id('63e21b4c-0a0c-41f6-bfc3-7c2816815599')
     def test_volume_bootable(self):
-        # Verify that a volume bootable flag is retrieved
+        """Test setting and retrieving bootable flag of a volume"""
         for bool_bootable in [True, False]:
             self.volumes_client.set_bootable_volume(self.volume['id'],
                                                     bootable=bool_bootable)
@@ -69,6 +72,11 @@
     @decorators.idempotent_id('9516a2c8-9135-488c-8dd6-5677a7e5f371')
     @utils.services('compute')
     def test_get_volume_attachment(self):
+        """Test getting volume attachments
+
+        Attach a volume to a server, and then retrieve volume's attachments
+        info.
+        """
         # Create a server
         server = self.create_server()
         # Verify that a volume's attachment information is retrieved
@@ -84,7 +92,6 @@
                         self.volume['id'], 'available')
         self.addCleanup(self.volumes_client.detach_volume, self.volume['id'])
         volume = self.volumes_client.show_volume(self.volume['id'])['volume']
-        self.assertIn('attachments', volume)
         attachment = volume['attachments'][0]
 
         self.assertEqual('/dev/%s' %
@@ -97,6 +104,7 @@
     @decorators.idempotent_id('d8f1ca95-3d5b-44a3-b8ca-909691c9532d')
     @utils.services('image')
     def test_volume_upload(self):
+        """Test uploading volume to create an image"""
         # NOTE(gfidente): the volume uploaded in Glance comes from setUpClass,
         # it is shared with the other tests. After it is uploaded in Glance,
         # there is no way to delete it from Cinder, so we delete it from Glance
@@ -119,6 +127,7 @@
 
     @decorators.idempotent_id('92c4ef64-51b2-40c0-9f7e-4749fbaaba33')
     def test_reserve_unreserve_volume(self):
+        """Test reserving and unreserving volume"""
         # Mark volume as reserved.
         self.volumes_client.reserve_volume(self.volume['id'])
         # To get the volume info
@@ -132,6 +141,7 @@
 
     @decorators.idempotent_id('fff74e1e-5bd3-4b33-9ea9-24c103bc3f59')
     def test_volume_readonly_update(self):
+        """Test updating and retrieve volume's readonly flag"""
         for readonly in [True, False]:
             # Update volume readonly
             self.volumes_client.update_volume_readonly(self.volume['id'],
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index c178272..2e78114 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -27,6 +27,7 @@
 
 
 class VolumesBackupsTest(base.BaseVolumeTest):
+    """Test volumes backup"""
 
     @classmethod
     def skip_checks(cls):
@@ -54,6 +55,16 @@
                       'ceph does not support arbitrary container names')
     @decorators.idempotent_id('a66eb488-8ee1-47d4-8e9f-575a095728c6')
     def test_volume_backup_create_get_detailed_list_restore_delete(self):
+        """Test create/get/list/restore/delete volume backup
+
+        1. Create volume1 with metadata
+        2. Create backup1 from volume1
+        3. Show backup1
+        4. List backups with detail
+        5. Restore backup1
+        6. Verify backup1 has been restored successfully with the metadata
+           of volume1
+        """
         # Create a volume with metadata
         metadata = {"vol-meta1": "value1",
                     "vol-meta2": "value2",
@@ -80,11 +91,7 @@
         self.assertEqual('container', backup['container'])
 
         # Get all backups with detail
-        backups = self.backups_client.list_backups(
-            detail=True)['backups']
-        for backup_info in backups:
-            self.assertIn('created_at', backup_info)
-            self.assertIn('links', backup_info)
+        backups = self.backups_client.list_backups(detail=True)['backups']
         self.assertIn((backup['name'], backup['id']),
                       [(m['name'], m['id']) for m in backups])
 
@@ -93,7 +100,7 @@
         restored_volume_metadata = self.volumes_client.show_volume(
             restored_volume['volume_id'])['volume']['metadata']
 
-        # Verify the backups has been restored successfully
+        # Verify the backup has been restored successfully
         # with the metadata of the source volume.
         self.assertThat(restored_volume_metadata.items(),
                         matchers.ContainsAll(metadata.items()))
@@ -124,6 +131,13 @@
     @decorators.idempotent_id('2a8ba340-dff2-4511-9db7-646f07156b15')
     @utils.services('image')
     def test_bootable_volume_backup_and_restore(self):
+        """Test backuping and restoring a bootable volume
+
+        1. Create volume1 from image
+        2. Create backup1 from volume1
+        3. Restore backup1
+        4. Verify the restored backup volume is bootable
+        """
         # Create volume from image
         img_uuid = CONF.compute.image_ref
         volume = self.create_volume(imageRef=img_uuid)
@@ -148,6 +162,7 @@
 
 
 class VolumesBackupsV39Test(base.BaseVolumeTest):
+    """Test volumes backup with volume microversion greater than 3.8"""
 
     _api_version = 3
     min_microversion = '3.9'
@@ -161,6 +176,7 @@
 
     @decorators.idempotent_id('9b374cbc-be5f-4d37-8848-7efb8a873dcc')
     def test_update_backup(self):
+        """Test updating backup's name and description"""
         # Create volume and backup
         volume = self.create_volume()
         backup = self.create_backup(volume_id=volume['id'])
@@ -176,7 +192,6 @@
             backup['id'], **update_kwargs)['backup']
         self.assertEqual(backup['id'], update_backup['id'])
         self.assertEqual(update_kwargs['name'], update_backup['name'])
-        self.assertIn('links', update_backup)
 
         # Assert response body for show_backup method
         retrieved_backup = self.backups_client.show_backup(
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index ade2deb..91728ab 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -37,11 +37,9 @@
         kwargs['name'] = v_name
         kwargs['metadata'] = metadata
         volume = self.volumes_client.create_volume(**kwargs)['volume']
-        self.assertIn('id', volume)
         self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
         waiters.wait_for_volume_resource_status(self.volumes_client,
                                                 volume['id'], 'available')
-        self.assertIn('name', volume)
         self.assertEqual(volume['name'], v_name,
                          "The created volume name is not equal "
                          "to the requested name")
@@ -101,7 +99,6 @@
                   'availability_zone': volume['availability_zone'],
                   'size': CONF.volume.volume_size}
         new_volume = self.volumes_client.create_volume(**params)['volume']
-        self.assertIn('id', new_volume)
         self.addCleanup(self.delete_volume, self.volumes_client,
                         new_volume['id'])
         waiters.wait_for_volume_resource_status(self.volumes_client,
@@ -153,7 +150,5 @@
     @decorators.idempotent_id('c4f2431e-4920-4736-9e00-4040386b6feb')
     def test_show_volume_summary(self):
         """Test showing volume summary"""
-        volume_summary = \
-            self.volumes_client.show_volume_summary()['volume-summary']
-        for key in ['total_size', 'total_count']:
-            self.assertIn(key, volume_summary)
+        # check response schema
+        self.volumes_client.show_volume_summary()
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 866bd87..76c22f0 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -28,6 +28,7 @@
 
 
 class VolumesNegativeTest(base.BaseVolumeTest):
+    """Negative tests of volumes"""
 
     @classmethod
     def resource_setup(cls):
@@ -58,50 +59,49 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f131c586-9448-44a4-a8b0-54ca838aa43e')
     def test_volume_get_nonexistent_volume_id(self):
-        # Should not be able to get a non-existent volume
+        """Test getting non existent volume should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.show_volume,
                           data_utils.rand_uuid())
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('555efa6e-efcd-44ef-8a3b-4a7ca4837a29')
     def test_volume_delete_nonexistent_volume_id(self):
-        # Should not be able to delete a non-existent Volume
+        """Test deleting non existent volume should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.delete_volume,
                           data_utils.rand_uuid())
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1ed83a8a-682d-4dfb-a30e-ee63ffd6c049')
     def test_create_volume_with_invalid_size(self):
-        # Should not be able to create volume with invalid size in request
+        """Test creating volume with invalid size should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.create_volume, size='#$%')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9387686f-334f-4d31-a439-33494b9e2683')
     def test_create_volume_without_passing_size(self):
-        # Should not be able to create volume without passing size
-        # in request
+        """Test creating volume with empty size should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.create_volume, size='')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('41331caa-eaf4-4001-869d-bc18c1869360')
     def test_create_volume_with_size_zero(self):
-        # Should not be able to create volume with size zero
+        """Test creating volume with zero size should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.create_volume, size='0')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8b472729-9eba-446e-a83b-916bdb34bef7')
     def test_create_volume_with_size_negative(self):
-        # Should not be able to create volume with size negative
+        """Test creating volume with negative size should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.create_volume, size='-1')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('10254ed8-3849-454e-862e-3ab8e6aa01d2')
     def test_create_volume_with_nonexistent_volume_type(self):
-        # Should not be able to create volume with non-existent volume type
+        """Test creating volume with non existent volume type should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
                           size=CONF.volume.volume_size,
                           volume_type=data_utils.rand_uuid())
@@ -109,7 +109,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0c36f6ae-4604-4017-b0a9-34fdc63096f9')
     def test_create_volume_with_nonexistent_snapshot_id(self):
-        # Should not be able to create volume with non-existent snapshot
+        """Test creating volume with non existent snapshot should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
                           size=CONF.volume.volume_size,
                           snapshot_id=data_utils.rand_uuid())
@@ -117,7 +117,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('47c73e08-4be8-45bb-bfdf-0c4e79b88344')
     def test_create_volume_with_nonexistent_source_volid(self):
-        # Should not be able to create volume with non-existent source volume
+        """Test creating volume with non existent source volume should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
                           size=CONF.volume.volume_size,
                           source_volid=data_utils.rand_uuid())
@@ -125,46 +125,49 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0186422c-999a-480e-a026-6a665744c30c')
     def test_update_volume_with_nonexistent_volume_id(self):
+        """Test updating non existent volume should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
                           volume_id=data_utils.rand_uuid())
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e66e40d6-65e6-4e75-bdc7-636792fa152d')
     def test_update_volume_with_invalid_volume_id(self):
+        """Test updating volume with invalid volume id should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
                           volume_id=data_utils.rand_name('invalid'))
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('72aeca85-57a5-4c1f-9057-f320f9ea575b')
     def test_update_volume_with_empty_volume_id(self):
+        """Test updating volume with empty volume id should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
                           volume_id='')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('30799cfd-7ee4-446c-b66c-45b383ed211b')
     def test_get_invalid_volume_id(self):
-        # Should not be able to get volume with invalid id
+        """Test getting volume with invalid volume id should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.show_volume,
                           data_utils.rand_name('invalid'))
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c6c3db06-29ad-4e91-beb0-2ab195fe49e3')
     def test_get_volume_without_passing_volume_id(self):
-        # Should not be able to get volume when empty ID is passed
+        """Test getting volume with empty volume id should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.show_volume, '')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1f035827-7c32-4019-9240-b4ec2dbd9dfd')
     def test_delete_invalid_volume_id(self):
-        # Should not be able to delete volume when invalid ID is passed
+        """Test deleting volume with invalid volume id should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.delete_volume,
                           data_utils.rand_name('invalid'))
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('441a1550-5d44-4b30-af0f-a6d402f52026')
     def test_delete_volume_without_passing_volume_id(self):
-        # Should not be able to delete volume when empty ID is passed
+        """Test deleting volume with empty volume id should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.delete_volume, '')
 
@@ -172,6 +175,7 @@
     @decorators.idempotent_id('f5e56b0a-5d02-43c1-a2a7-c9b792c2e3f6')
     @utils.services('compute')
     def test_attach_volumes_with_nonexistent_volume_id(self):
+        """Test attaching non existent volume to server should fail"""
         server = self.create_server()
 
         self.assertRaises(lib_exc.NotFound,
@@ -183,6 +187,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9f9c24e4-011d-46b5-b992-952140ce237a')
     def test_detach_volumes_with_invalid_volume_id(self):
+        """Test detaching volume with invalid volume id should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.detach_volume,
                           'xxx')
@@ -190,7 +195,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e0c75c74-ee34-41a9-9288-2a2051452854')
     def test_volume_extend_with_size_smaller_than_original_size(self):
-        # Extend volume with smaller size than original size.
+        """Test extending volume with decreasing size should fail"""
         extend_size = 0
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.extend_volume,
@@ -199,7 +204,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5d0b480d-e833-439f-8a5a-96ad2ed6f22f')
     def test_volume_extend_with_non_number_size(self):
-        # Extend volume when size is non number.
+        """Test extending volume with non-integer size should fail"""
         extend_size = 'abc'
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.extend_volume,
@@ -208,7 +213,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('355218f1-8991-400a-a6bb-971239287d92')
     def test_volume_extend_with_None_size(self):
-        # Extend volume with None size.
+        """Test extending volume with none size should fail"""
         extend_size = None
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.extend_volume,
@@ -217,7 +222,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8f05a943-013c-4063-ac71-7baf561e82eb')
     def test_volume_extend_with_nonexistent_volume_id(self):
-        # Extend volume size when volume is nonexistent.
+        """Test extending non existent volume should fail"""
         extend_size = self.volume['size'] + 1
         self.assertRaises(lib_exc.NotFound, self.volumes_client.extend_volume,
                           data_utils.rand_uuid(), new_size=extend_size)
@@ -225,7 +230,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('aff8ba64-6d6f-4f2e-bc33-41a08ee9f115')
     def test_volume_extend_without_passing_volume_id(self):
-        # Extend volume size when passing volume id is None.
+        """Test extending volume without passing volume id should fail"""
         extend_size = self.volume['size'] + 1
         self.assertRaises(lib_exc.NotFound, self.volumes_client.extend_volume,
                           None, new_size=extend_size)
@@ -233,6 +238,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ac6084c0-0546-45f9-b284-38a367e0e0e2')
     def test_reserve_volume_with_nonexistent_volume_id(self):
+        """Test reserving non existent volume should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.reserve_volume,
                           data_utils.rand_uuid())
@@ -240,6 +246,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('eb467654-3dc1-4a72-9b46-47c29d22654c')
     def test_unreserve_volume_with_nonexistent_volume_id(self):
+        """Test unreserving non existent volume should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.unreserve_volume,
                           data_utils.rand_uuid())
@@ -247,6 +254,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('449c4ed2-ecdd-47bb-98dc-072aeccf158c')
     def test_reserve_volume_with_negative_volume_status(self):
+        """Test reserving already reserved volume should fail"""
         # Mark volume as reserved.
         self.volumes_client.reserve_volume(self.volume['id'])
         # Mark volume which is marked as reserved before
@@ -259,6 +267,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0f4aa809-8c7b-418f-8fb3-84c7a5dfc52f')
     def test_list_volumes_with_nonexistent_name(self):
+        """Test listing volumes with non existent name should get nothing"""
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         params = {'name': v_name}
         fetched_volume = self.volumes_client.list_volumes(
@@ -268,6 +277,10 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9ca17820-a0e7-4cbd-a7fa-f4468735e359')
     def test_list_volumes_detail_with_nonexistent_name(self):
+        """Test listing volume details with non existent name
+
+        Listing volume details with non existent name should get nothing.
+        """
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         params = {'name': v_name}
         fetched_volume = \
@@ -278,6 +291,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('143b279b-7522-466b-81be-34a87d564a7c')
     def test_list_volumes_with_invalid_status(self):
+        """Test listing volumes with invalid status should get nothing"""
         params = {'status': 'null'}
         fetched_volume = self.volumes_client.list_volumes(
             params=params)['volumes']
@@ -286,6 +300,10 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ba94b27b-be3f-496c-a00e-0283b373fa75')
     def test_list_volumes_detail_with_invalid_status(self):
+        """Test listing volume details with invalid status
+
+        Listing volume details with invalid status should get nothing
+        """
         params = {'status': 'null'}
         fetched_volume = \
             self.volumes_client.list_volumes(detail=True,
@@ -296,6 +314,7 @@
     @decorators.idempotent_id('5b810c91-0ad1-47ce-aee8-615f789be78f')
     @utils.services('image')
     def test_create_volume_from_image_with_decreasing_size(self):
+        """Test creating volume from image with decreasing size should fail"""
         # Create image
         image = self.create_image()
 
@@ -311,6 +330,7 @@
     @decorators.idempotent_id('d15e7f35-2cfc-48c8-9418-c8223a89bcbb')
     @utils.services('image')
     def test_create_volume_from_deactivated_image(self):
+        """Test creating volume from deactivated image should fail"""
         # Create image
         image = self.create_image()
 
diff --git a/tempest/api/volume/test_volumes_snapshots.py b/tempest/api/volume/test_volumes_snapshots.py
index bf221e8..fd2e7c4 100644
--- a/tempest/api/volume/test_volumes_snapshots.py
+++ b/tempest/api/volume/test_volumes_snapshots.py
@@ -25,6 +25,8 @@
 
 
 class VolumesSnapshotTestJSON(base.BaseVolumeTest):
+    """Test volume snapshots"""
+
     create_default_network = True
 
     @classmethod
@@ -41,6 +43,7 @@
     @decorators.idempotent_id('8567b54c-4455-446d-a1cf-651ddeaa3ff2')
     @utils.services('compute')
     def test_snapshot_create_delete_with_volume_in_use(self):
+        """Test create/delete snapshot from volume attached to server"""
         # Create a test instance
         server = self.create_server()
         # NOTE(zhufl) Here we create volume from self.image_ref for adding
@@ -66,7 +69,13 @@
     @decorators.idempotent_id('5210a1de-85a0-11e6-bb21-641c676a5d61')
     @utils.services('compute')
     def test_snapshot_create_offline_delete_online(self):
+        """Test creating snapshots when volume is detached and attached
 
+        1. Create snapshot1 from volume1(not attached to any server)
+        2. Attach volume1 to server1
+        3. Create snapshot2 and snapshot3 from volume1
+        4. Delete snapshot3, snapshot1, snapshot2
+        """
         # Create a snapshot while it is not attached
         snapshot1 = self.create_snapshot(self.volume_origin['id'])
 
@@ -74,7 +83,7 @@
         server = self.create_server()
         self.attach_volume(server['id'], self.volume_origin['id'])
 
-        # Now that the volume is attached, create another snapshots
+        # Now that the volume is attached, create other snapshots
         snapshot2 = self.create_snapshot(self.volume_origin['id'], force=True)
         snapshot3 = self.create_snapshot(self.volume_origin['id'], force=True)
 
@@ -86,6 +95,7 @@
 
     @decorators.idempotent_id('2a8abbe4-d871-46db-b049-c41f5af8216e')
     def test_snapshot_create_get_list_update_delete(self):
+        """Test create/get/list/update/delete snapshot"""
         # Create a snapshot with metadata
         metadata = {"snap-meta1": "value1",
                     "snap-meta2": "value2",
@@ -156,19 +166,25 @@
 
     @decorators.idempotent_id('677863d1-3142-456d-b6ac-9924f667a7f4')
     def test_volume_from_snapshot(self):
-        # Creates a volume from a snapshot passing a size
-        # different from the source
+        """Test creating volume from snapshot with extending size"""
         self._create_volume_from_snapshot(extra_size=1)
 
     @decorators.idempotent_id('053d8870-8282-4fff-9dbb-99cb58bb5e0a')
     def test_volume_from_snapshot_no_size(self):
-        # Creates a volume from a snapshot defaulting to original size
+        """Test creating volume from snapshot with original size"""
         self._create_volume_from_snapshot()
 
     @decorators.idempotent_id('bbcfa285-af7f-479e-8c1a-8c34fc16543c')
     @testtools.skipUnless(CONF.volume_feature_enabled.backup,
                           "Cinder backup is disabled")
     def test_snapshot_backup(self):
+        """Test creating backup from snapshot and volume
+
+        1. Create snapshot1 from volume1
+        2. Create backup from volume1 and snapshot1
+        3. Check the created backup's volume is volume1 and snapshot
+           is snapshot1
+        """
         # Create a snapshot
         snapshot = self.create_snapshot(volume_id=self.volume_origin['id'])
 
diff --git a/tempest/api/volume/test_volumes_snapshots_list.py b/tempest/api/volume/test_volumes_snapshots_list.py
index f4f039c..77627bc 100644
--- a/tempest/api/volume/test_volumes_snapshots_list.py
+++ b/tempest/api/volume/test_volumes_snapshots_list.py
@@ -18,6 +18,7 @@
 
 
 class VolumesSnapshotListTestJSON(base.BaseVolumeTest):
+    """Test listing volume snapshots"""
 
     @classmethod
     def skip_checks(cls):
@@ -50,6 +51,7 @@
 
     def _list_snapshots_by_param_limit(self, limit, expected_elements):
         """list snapshots by limit param"""
+
         # Get snapshots list using limit parameter
         fetched_snap_list = self.snapshots_client.list_snapshots(
             limit=limit)['snapshots']
@@ -58,7 +60,8 @@
 
     @decorators.idempotent_id('59f41f43-aebf-48a9-ab5d-d76340fab32b')
     def test_snapshots_list_with_params(self):
-        """list snapshots with params."""
+        """Test listing snapshots with params"""
+
         # Verify list snapshots by display_name filter
         params = {'name': self.snapshot['name']}
         self._list_by_param_values_and_assert(**params)
@@ -74,7 +77,8 @@
 
     @decorators.idempotent_id('220a1022-1fcd-4a74-a7bd-6b859156cda2')
     def test_snapshots_list_details_with_params(self):
-        """list snapshot details with params."""
+        """Test listing snapshot details with params"""
+
         # Verify list snapshot details by display_name filter
         params = {'name': self.snapshot['name']}
         self._list_by_param_values_and_assert(with_detail=True, **params)
@@ -88,24 +92,29 @@
 
     @decorators.idempotent_id('db4d8e0a-7a2e-41cc-a712-961f6844e896')
     def test_snapshot_list_param_limit(self):
-        # List returns limited elements
+        """Test listing snapshot with limit returns the limited elements
+
+        If listing snapshots with limit=1, then 1 snapshot is returned.
+        """
         self._list_snapshots_by_param_limit(limit=1, expected_elements=1)
 
     @decorators.idempotent_id('a1427f61-420e-48a5-b6e3-0b394fa95400')
     def test_snapshot_list_param_limit_equals_infinite(self):
-        # List returns all elements when request limit exceeded
-        # snapshots number
+        """Test listing snapshot with infinite limit
+
+        If listing snapshots with limit greater than the count of all
+        snapshots, then all snapshots are returned.
+        """
         snap_list = self.snapshots_client.list_snapshots()['snapshots']
         self._list_snapshots_by_param_limit(limit=100000,
                                             expected_elements=len(snap_list))
 
     @decorators.idempotent_id('e3b44b7f-ae87-45b5-8a8c-66110eb24d0a')
     def test_snapshot_list_param_limit_equals_zero(self):
-        # List returns zero elements
+        """Test listing snapshot with zero limit should return empty list"""
         self._list_snapshots_by_param_limit(limit=0, expected_elements=0)
 
     def _list_snapshots_param_sort(self, sort_key, sort_dir):
-        """list snapshots by sort param"""
         snap_list = self.snapshots_client.list_snapshots(
             sort_key=sort_key, sort_dir=sort_dir)['snapshots']
         self.assertNotEmpty(snap_list)
@@ -122,33 +131,42 @@
 
     @decorators.idempotent_id('c5513ada-64c1-4d28-83b9-af3307ec1388')
     def test_snapshot_list_param_sort_id_asc(self):
+        """Test listing snapshots sort by id ascendingly"""
         self._list_snapshots_param_sort(sort_key='id', sort_dir='asc')
 
     @decorators.idempotent_id('8a7fe058-0b41-402a-8afd-2dbc5a4a718b')
     def test_snapshot_list_param_sort_id_desc(self):
+        """Test listing snapshots sort by id descendingly"""
         self._list_snapshots_param_sort(sort_key='id', sort_dir='desc')
 
     @decorators.idempotent_id('4052c3a0-2415-440a-a8cc-305a875331b0')
     def test_snapshot_list_param_sort_created_at_asc(self):
+        """Test listing snapshots sort by created_at ascendingly"""
         self._list_snapshots_param_sort(sort_key='created_at', sort_dir='asc')
 
     @decorators.idempotent_id('dcbbe24a-f3c0-4ec8-9274-55d48db8d1cf')
     def test_snapshot_list_param_sort_created_at_desc(self):
+        """Test listing snapshots sort by created_at descendingly"""
         self._list_snapshots_param_sort(sort_key='created_at', sort_dir='desc')
 
     @decorators.idempotent_id('d58b5fed-0c37-42d3-8c5d-39014ac13c00')
     def test_snapshot_list_param_sort_name_asc(self):
+        """Test listing snapshots sort by display_name ascendingly"""
         self._list_snapshots_param_sort(sort_key='display_name',
                                         sort_dir='asc')
 
     @decorators.idempotent_id('96ba6f4d-1f18-47e1-b4bc-76edc6c21250')
     def test_snapshot_list_param_sort_name_desc(self):
+        """Test listing snapshots sort by display_name descendingly"""
         self._list_snapshots_param_sort(sort_key='display_name',
                                         sort_dir='desc')
 
     @decorators.idempotent_id('05489dde-44bc-4961-a1f5-3ce7ee7824f7')
     def test_snapshot_list_param_marker(self):
-        # The list of snapshots should end before the provided marker
+        """Test listing snapshots with marker
+
+        The list of snapshots should end before the provided marker
+        """
         snap_list = self.snapshots_client.list_snapshots()['snapshots']
         # list_snapshots will take the reverse order as they are created.
         snapshot_id_list = [snap['id'] for snap in snap_list][::-1]
@@ -163,6 +181,13 @@
 
     @decorators.idempotent_id('ca96d551-17c6-4e11-b0e8-52d3bb8a63c7')
     def test_snapshot_list_param_offset(self):
+        """Test listing snapshots with offset and limit
+
+        If listing snapshots with offset=2 and limit=3, then at most 3(limit)
+        snapshots located in the position 2(offset) in the all snapshots list
+        should be returned.
+        (The items in the all snapshots list start from position 0.)
+        """
         params = {'offset': 2, 'limit': 3}
         snap_list = self.snapshots_client.list_snapshots(**params)['snapshots']
         # Verify the list of snapshots skip offset=2 from the first element
diff --git a/tempest/api/volume/test_volumes_snapshots_negative.py b/tempest/api/volume/test_volumes_snapshots_negative.py
index 0453c0a..9c36dc6 100644
--- a/tempest/api/volume/test_volumes_snapshots_negative.py
+++ b/tempest/api/volume/test_volumes_snapshots_negative.py
@@ -20,6 +20,7 @@
 
 
 class VolumesSnapshotNegativeTestJSON(base.BaseVolumeTest):
+    """Negative tests of volume snapshot"""
 
     @classmethod
     def skip_checks(cls):
@@ -30,7 +31,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e3e466af-70ab-4f4b-a967-ab04e3532ea7')
     def test_create_snapshot_with_nonexistent_volume_id(self):
-        # Create a snapshot with nonexistent volume id
+        """Test creating snapshot from non existent volume should fail"""
         s_name = data_utils.rand_name(self.__class__.__name__ + '-snap')
         self.assertRaises(lib_exc.NotFound,
                           self.snapshots_client.create_snapshot,
@@ -40,6 +41,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('bb9da53e-d335-4309-9c15-7e76fd5e4d6d')
     def test_create_snapshot_without_passing_volume_id(self):
+        """Test creating snapshot without passing volume_id should fail"""
         # Create a snapshot without passing volume id
         s_name = data_utils.rand_name(self.__class__.__name__ + '-snap')
         self.assertRaises(lib_exc.NotFound,
@@ -49,6 +51,10 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('677863d1-34f9-456d-b6ac-9924f667a7f4')
     def test_volume_from_snapshot_decreasing_size(self):
+        """Test creating volume from snapshot with decreasing size
+
+        creating volume from snapshot with decreasing size should fail.
+        """
         # Creates a volume a snapshot passing a size different from the source
         src_size = CONF.volume.volume_size * 2
 
@@ -64,6 +70,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8fd92339-e22f-4591-86b4-1e2215372a40')
     def test_list_snapshot_invalid_param_limit(self):
+        """Test listing snapshots with invalid limit param should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.snapshots_client.list_snapshots,
                           limit='invalid')
@@ -71,6 +78,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('27b5f37f-bf69-4e8c-986e-c44f3d6819b8')
     def test_list_snapshots_invalid_param_sort(self):
+        """Test listing snapshots with invalid sort key should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.snapshots_client.list_snapshots,
                           sort_key='invalid')
@@ -78,6 +86,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('b68deeda-ca79-4a32-81af-5c51179e553a')
     def test_list_snapshots_invalid_param_marker(self):
+        """Test listing snapshots with invalid marker should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.snapshots_client.list_snapshots,
                           marker=data_utils.rand_uuid())
diff --git a/tempest/cmd/init.py b/tempest/cmd/init.py
index d84f3a3..6e93d69 100644
--- a/tempest/cmd/init.py
+++ b/tempest/cmd/init.py
@@ -44,11 +44,14 @@
 
     :return: default config dir
     """
+    # NOTE: The default directory should be on a Linux box.
     global_conf_dir = '/etc/tempest'
     xdg_config = os.environ.get('XDG_CONFIG_HOME',
-                                os.path.expanduser('~/.config'))
+                                os.path.expanduser(os.path.join('~',
+                                                                '.config')))
     user_xdg_global_path = os.path.join(xdg_config, 'tempest')
-    user_global_path = os.path.join(os.path.expanduser('~'), '.tempest/etc')
+    user_global_path = os.path.join(os.path.expanduser('~'),
+                                    '.tempest', 'etc')
     if os.path.isdir(global_conf_dir):
         return global_conf_dir
     elif os.path.isdir(user_xdg_global_path):
@@ -121,7 +124,7 @@
     def generate_sample_config(self, local_dir):
         conf_generator = os.path.join(os.path.dirname(__file__),
                                       'config-generator.tempest.conf')
-        output_file = os.path.join(local_dir, 'etc/tempest.conf.sample')
+        output_file = os.path.join(local_dir, 'etc', 'tempest.conf.sample')
         if os.path.isfile(conf_generator):
             generator.main(['--config-file', conf_generator, '--output-file',
                             output_file])
diff --git a/tempest/cmd/subunit_describe_calls.py b/tempest/cmd/subunit_describe_calls.py
index e029538..172fbaa 100644
--- a/tempest/cmd/subunit_describe_calls.py
+++ b/tempest/cmd/subunit_describe_calls.py
@@ -30,6 +30,8 @@
 * ``--ports, -p``: (Optional) The path to a JSON file describing the ports
   being used by different services
 * ``--verbose, -v``: (Optional) Print Request and Response Headers and Body
+  data to stdout in the non cliff deprecated CLI
+* ``--all-stdout, -a``: (Optional) Print Request and Response Headers and Body
   data to stdout
 
 
@@ -278,7 +280,7 @@
     return url_parser
 
 
-def output(url_parser, output_file, verbose):
+def output(url_parser, output_file, all_stdout):
     if output_file is not None:
         with open(output_file, "w") as outfile:
             outfile.write(json.dumps(url_parser.test_logs))
@@ -294,7 +296,7 @@
             sys.stdout.write('\t- {0} {1} request for {2} to {3}\n'.format(
                 item.get('status_code'), item.get('verb'),
                 item.get('service'), item.get('url')))
-            if verbose:
+            if all_stdout:
                 sys.stdout.write('\t\t- request headers: {0}\n'.format(
                     item.get('request_headers')))
                 sys.stdout.write('\t\t- request body: {0}\n'.format(
@@ -313,7 +315,7 @@
               "please use: 'tempest subunit-describe-calls'")
         cl_args = ArgumentParser().parse_args()
     parser = parse(cl_args.subunit, cl_args.non_subunit_name, cl_args.ports)
-    output(parser, cl_args.output_file, cl_args.verbose)
+    output(parser, cl_args.output_file, cl_args.all_stdout)
 
 
 def _parser_add_args(parser):
@@ -339,9 +341,23 @@
         help="A JSON file describing the ports for each service."
     )
 
-    parser.add_argument(
-        "-v", "--verbose", action='store_true', default=False,
-        help="Add Request and Response header and body data to stdout."
+    group = parser.add_mutually_exclusive_group()
+    # the -v and --verbose command are for the old subunit-describe-calls
+    # main() CLI interface.  It does not work with the new
+    # tempest subunit-describe-callss CLI. So when the main CLI approach is
+    # deleted this argument is not needed.
+    group.add_argument(
+        "-v", "--verbose", action='store_true', dest='all_stdout',
+        help='Add Request and Response header and body data to stdout print.'
+             ' NOTE: This argument deprecated and does not work with'
+             ' tempest subunit-describe-calls CLI.'
+             ' Use new option: "-a", "--all-stdout"'
+    )
+    group.add_argument(
+        "-a", "--all-stdout", action='store_true',
+        help="Add Request and Response header and body data to stdout print."
+             " Note: this argument work with the subunit-describe-calls and"
+             " tempest subunit-describe-calls CLI commands."
     )
 
 
diff --git a/tempest/common/waiters.py b/tempest/common/waiters.py
index 14790d6..fc25914 100644
--- a/tempest/common/waiters.py
+++ b/tempest/common/waiters.py
@@ -223,6 +223,25 @@
              resource_name, resource_id, status, time.time() - start)
 
 
+def wait_for_volume_attachment_create(client, volume_id, server_id):
+    """Waits for a volume attachment to be created at a given volume."""
+    start = int(time.time())
+    while True:
+        attachments = client.show_volume(volume_id)['volume']['attachments']
+        found = [a for a in attachments if a['server_id'] == server_id]
+        if found:
+            LOG.info('Attachment %s created for volume %s to server %s after '
+                     'waiting for %f seconds', found[0]['attachment_id'],
+                     volume_id, server_id, time.time() - start)
+            return found[0]
+        time.sleep(client.build_interval)
+        if int(time.time()) - start >= client.build_timeout:
+            message = ('Failed to attach volume %s to server %s '
+                       'within the required time (%s s).' %
+                       (volume_id, server_id, client.build_timeout))
+            raise lib_exc.TimeoutException(message)
+
+
 def wait_for_volume_attachment_remove(client, volume_id, attachment_id):
     """Waits for a volume attachment to be removed from a given volume."""
     start = int(time.time())
diff --git a/tempest/config.py b/tempest/config.py
index eca2023..a632dee 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -481,6 +481,12 @@
                                   'MIN_LIBVIRT_VERSION is >= 1.2.17 on all '
                                   'branches from stable/rocky and will be '
                                   'removed in a future release.'),
+    cfg.BoolOpt('can_migrate_between_any_hosts',
+                default=True,
+                help="Does the test environment support migrating between "
+                     "any hosts? In environments with non-homogeneous compute "
+                     "nodes you can set this to False so that it will select "
+                     "destination host for migrating automatically"),
     cfg.BoolOpt('vnc_console',
                 default=False,
                 help='Enable VNC console. This configuration value should '
@@ -592,6 +598,18 @@
                 help='Does the test environment support attaching a volume to '
                      'more than one instance? This depends on hypervisor and '
                      'volume backend/type and compute API version 2.60.'),
+    cfg.BoolOpt('xenapi_apis',
+                default=False,
+                help='Does the test environment support the XenAPI-specific '
+                     'APIs: os-agents, writeable server metadata and the '
+                     'resetNetwork server action? '
+                     'These were removed in Victoria alongside the XenAPI '
+                     'virt driver.',
+                deprecated_for_removal=True,
+                deprecated_reason="On Nova side, XenAPI virt driver and the "
+                                  "APIs that only worked with that driver "
+                                  "have been removed and there's nothing to "
+                                  "test after Ussuri."),
 ]
 
 
diff --git a/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py b/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py
index 28ed816..8aed37d 100644
--- a/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py
+++ b/tempest/lib/api_schema/response/compute/v2_1/parameter_types.py
@@ -120,3 +120,10 @@
     # 7: SUSPENDED
     'enum': [0, 1, 3, 4, 6, 7]
 }
+
+uuid_or_null = {
+    'anyOf': [
+        {'type': 'string', 'format': 'uuid'},
+        {'type': 'null'}
+    ]
+}
diff --git a/tempest/lib/api_schema/response/volume/backups.py b/tempest/lib/api_schema/response/volume/backups.py
new file mode 100644
index 0000000..cba7981
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/backups.py
@@ -0,0 +1,229 @@
+# Copyright 2015 NEC Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+common_show_backup = {
+    'type': 'object',
+    'properties': {
+        'status': {'type': 'string'},
+        'object_count': {'type': 'integer'},
+        'container': {'type': ['string', 'null']},
+        'description': {'type': ['string', 'null']},
+        'links': parameter_types.links,
+        'availability_zone': {'type': ['string', 'null']},
+        'created_at': parameter_types.date_time,
+        'updated_at': parameter_types.date_time_or_null,
+        'name': {'type': ['string', 'null']},
+        'has_dependent_backups': {'type': 'boolean'},
+        'volume_id': {'type': 'string', 'format': 'uuid'},
+        'fail_reason': {'type': ['string', 'null']},
+        'size': {'type': 'integer'},
+        'id': {'type': 'string', 'format': 'uuid'},
+        'is_incremental': {'type': 'boolean'},
+        'data_timestamp': parameter_types.date_time_or_null,
+        'snapshot_id': {'type': ['string', 'null']},
+        # TODO(zhufl): os-backup-project-attr:project_id is added
+        # in 3.18, we should move it to the 3.18 schema file when
+        # microversion is supported in volume interfaces.
+        'os-backup-project-attr:project_id': {
+            'type': 'string', 'format': 'uuid'},
+        # TODO(zhufl): metadata is added in 3.43, we should move it
+        # to the 3.43 schema file when microversion is supported
+        # in volume interfaces.
+        'metadata': {'^.+$': {'type': 'string'}},
+        # TODO(zhufl): user_id is added in 3.56, we should move it
+        # to the 3.56 schema file when microversion is supported
+        # in volume interfaces.
+        'user_id': {'type': 'string'},
+    },
+    'additionalProperties': False,
+    'required': ['status', 'object_count', 'fail_reason', 'links',
+                 'created_at', 'updated_at', 'name', 'volume_id', 'size', 'id',
+                 'data_timestamp']
+}
+
+create_backup = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'backup': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'links': parameter_types.links,
+                    'name': {'type': ['string', 'null']},
+                    # TODO(zhufl): metadata is added in 3.43, we should move it
+                    # to the 3.43 schema file when microversion is supported
+                    # in volume interfaces.
+                    'metadata': {'^.+$': {'type': 'string'}},
+                },
+                'additionalProperties': False,
+                'required': ['id', 'links', 'name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['backup']
+    }
+}
+
+update_backup = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'backup': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'links': parameter_types.links,
+                    'name': {'type': ['string', 'null']},
+                    'metadata': {'^.+$': {'type': 'string'}}
+                },
+                'additionalProperties': False,
+                'required': ['id', 'links', 'name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['backup']
+    }
+}
+
+restore_backup = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'restore': {
+                'type': 'object',
+                'properties': {
+                    'backup_id': {'type': 'string', 'format': 'uuid'},
+                    'volume_id': {'type': 'string', 'format': 'uuid'},
+                    'volume_name': {'type': 'string'},
+                },
+                'additionalProperties': False,
+                'required': ['backup_id', 'volume_id', 'volume_name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['restore']
+    }
+}
+
+delete_backup = {'status_code': [202]}
+
+show_backup = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'backup': common_show_backup
+        },
+        'additionalProperties': False,
+        'required': ['backup']
+    }
+}
+
+list_backups_no_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'backups': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'links': parameter_types.links,
+                        'id': {'type': 'string', 'format': 'uuid'},
+                        'name': {'type': ['string', 'null']},
+                        # 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': ['links', 'id', 'name']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['backups'],
+    }
+}
+
+list_backups_detail = copy.deepcopy(common_show_backup)
+# 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
+list_backups_detail['properties'].update({'count': {'type': 'integer'}})
+list_backups_with_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'backups': {
+                'type': 'array',
+                'items': list_backups_detail
+            }
+        },
+        'additionalProperties': False,
+        'required': ['backups'],
+    }
+}
+
+export_backup = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'backup-record': {
+                'type': 'object',
+                'properties': {
+                    'backup_service': {'type': 'string'},
+                    'backup_url': {'type': 'string'}
+                },
+                'additionalProperties': False,
+                'required': ['backup_service', 'backup_url']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['backup-record']
+    }
+}
+
+import_backup = {
+    'status_code': [201],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'backup': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'links': parameter_types.links,
+                    'name': {'type': ['string', 'null']},
+                },
+                'additionalProperties': False,
+                'required': ['id', 'links', 'name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['backup']
+    }
+}
+
+reset_backup_status = {'status_code': [202]}
diff --git a/tempest/lib/api_schema/response/volume/groups.py b/tempest/lib/api_schema/response/volume/groups.py
new file mode 100644
index 0000000..cb31269
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/groups.py
@@ -0,0 +1,164 @@
+# Copyright 2015 NEC Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+create_group = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'group': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'name': {'type': 'string'},
+                },
+                'additionalProperties': False,
+                'required': ['id', 'name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['group']
+    }
+}
+
+delete_group = {'status_code': [202]}
+
+show_group = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'group': {
+                'type': 'object',
+                'properties': {
+                    'status': {'type': 'string'},
+                    'description': {'type': ['string', 'null']},
+                    'availability_zone': {'type': 'string'},
+                    'created_at': parameter_types.date_time,
+                    'group_type': {'type': 'string', 'format': 'uuid'},
+                    'group_snapshot_id': {'type': ['string', 'null']},
+                    'source_group_id': {'type': ['string', 'null']},
+                    'volume_types': {
+                        'type': 'array',
+                        'items': {'type': 'string', 'format': 'uuid'}
+                    },
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'name': {'type': 'string'},
+                    # TODO(zhufl): volumes is added in 3.25, we should move it
+                    # to the 3.25 schema file when microversion is supported
+                    # in volume interfaces
+                    'volumes': {
+                        'type': 'array',
+                        'items': {'type': 'string', 'format': 'uuid'}
+                    },
+                    'replication_status': {'type': 'string'}
+                },
+                'additionalProperties': False,
+                'required': ['status', 'description', 'created_at',
+                             'group_type', 'volume_types', 'id', 'name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['group']
+    }
+}
+
+list_groups_no_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'groups': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'id': {'type': 'string', 'format': 'uuid'},
+                        'name': {'type': 'string'}
+                    },
+                    'additionalProperties': False,
+                    'required': ['id', 'name'],
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['groups'],
+    }
+}
+
+list_groups_with_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'groups': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'status': {'type': 'string'},
+                        'description': {'type': ['string', 'null']},
+                        'availability_zone': {'type': 'string'},
+                        'created_at': parameter_types.date_time,
+                        'group_type': {'type': 'string', 'format': 'uuid'},
+                        'group_snapshot_id': {'type': ['string', 'null']},
+                        'source_group_id': {'type': ['string', 'null']},
+                        'volume_types': {
+                            'type': 'array',
+                            'items': {'type': 'string', 'format': 'uuid'}
+                        },
+                        'id': {'type': 'string', 'format': 'uuid'},
+                        'name': {'type': 'string'},
+                        # TODO(zhufl): volumes is added in 3.25, we should
+                        # move it to the 3.25 schema file when microversion
+                        # is supported in volume interfaces
+                        'volumes': {
+                            'type': 'array',
+                            'items': {'type': 'string', 'format': 'uuid'}
+                        },
+                    },
+                    'additionalProperties': False,
+                    'required': ['status', 'description', 'created_at',
+                                 'group_type', 'volume_types', 'id', 'name']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['groups'],
+    }
+}
+
+create_group_from_source = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'group': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'name': {'type': 'string'},
+                },
+                'additionalProperties': False,
+                'required': ['id', 'name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['group']
+    }
+}
+update_group = {'status_code': [202]}
+reset_group_status = {'status_code': [202]}
diff --git a/tempest/lib/api_schema/response/volume/manage_volume.py b/tempest/lib/api_schema/response/volume/manage_volume.py
new file mode 100644
index 0000000..d3acfd9
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/manage_volume.py
@@ -0,0 +1,27 @@
+# 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.volume import volumes
+
+
+manage_volume = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'volume': volumes.common_show_volume},
+        'additionalProperties': False,
+        'required': ['volume']
+    }
+}
diff --git a/tempest/lib/api_schema/response/volume/volumes.py b/tempest/lib/api_schema/response/volume/volumes.py
new file mode 100644
index 0000000..ffcf488
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/volumes.py
@@ -0,0 +1,368 @@
+# 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
+
+attachments = {
+    'type': 'array',
+    'items': {
+        'type': 'object',
+        'properties': {
+            'server_id': {'type': 'string', 'format': 'uuid'},
+            'attachment_id': {'type': 'string', 'format': 'uuid'},
+            'attached_at': parameter_types.date_time_or_null,
+            'host_name': {'type': ['string', 'null']},
+            'volume_id': {'type': 'string', 'format': 'uuid'},
+            'device': {'type': ['string', 'null']},
+            'id': {'type': 'string', 'format': 'uuid'}
+        },
+        'additionalProperties': False,
+        'required': ['server_id', 'attachment_id', 'host_name',
+                     'volume_id', 'device', 'id']
+    }
+}
+
+common_show_volume = {
+    'type': 'object',
+    'properties': {
+        'migration_status': {'type': ['string', 'null']},
+        'attachments': attachments,
+        'links': parameter_types.links,
+        'availability_zone': {'type': ['string', 'null']},
+        'os-vol-host-attr:host': {
+            'type': ['string', 'null'], 'pattern': '.+@.+#.+'},
+        'encrypted': {'type': 'boolean'},
+        'updated_at': parameter_types.date_time_or_null,
+        'replication_status': {'type': ['string', 'null']},
+        'snapshot_id': parameter_types.uuid_or_null,
+        'id': {'type': 'string', 'format': 'uuid'},
+        'size': {'type': 'integer'},
+        'user_id': {'type': 'string', 'format': 'uuid'},
+        'os-vol-tenant-attr:tenant_id': {'type': 'string',
+                                         'format': 'uuid'},
+        'os-vol-mig-status-attr:migstat': {'type': ['string', 'null']},
+        'metadata': {'type': 'object'},
+        'status': {'type': 'string'},
+        'volume_image_metadata': {'type': ['object', 'null']},
+        'description': {'type': ['string', 'null']},
+        'multiattach': {'type': 'boolean'},
+        'source_volid': parameter_types.uuid_or_null,
+        'consistencygroup_id': parameter_types.uuid_or_null,
+        'os-vol-mig-status-attr:name_id': parameter_types.uuid_or_null,
+        'name': {'type': ['string', 'null']},
+        'bootable': {'type': 'string'},
+        'created_at': parameter_types.date_time,
+        'volume_type': {'type': ['string', 'null']},
+        # TODO(zhufl): group_id is added in 3.13, we should move it to the
+        # 3.13 schema file when microversion is supported in volume interfaces
+        'group_id': parameter_types.uuid_or_null,
+        # TODO(zhufl): provider_id is added in 3.21, we should move it to the
+        # 3.21 schema file when microversion is supported in volume interfaces
+        'provider_id': parameter_types.uuid_or_null,
+        # TODO(zhufl): service_uuid and shared_targets are added in 3.48,
+        # we should move them to the 3.48 schema file when microversion
+        # is supported in volume interfaces.
+        'service_uuid': parameter_types.uuid_or_null,
+        'shared_targets': {'type': 'boolean'}
+    },
+    'additionalProperties': False,
+    'required': ['attachments', 'links', 'encrypted',
+                 'updated_at', 'replication_status', 'id',
+                 'size', 'user_id', 'availability_zone',
+                 'metadata', 'status', 'description',
+                 'multiattach', 'consistencygroup_id',
+                 'name', 'bootable', 'created_at',
+                 'volume_type', 'snapshot_id', 'source_volid']
+}
+
+list_volumes_no_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'volumes': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'links': parameter_types.links,
+                        'id': {'type': 'string', 'format': 'uuid'},
+                        'name': {'type': ['string', 'null']},
+                        # 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': ['links', 'id', 'name']
+                }
+            },
+            'volumes_links': parameter_types.links
+        },
+        'additionalProperties': False,
+        'required': ['volumes']
+    }
+}
+
+show_volume = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'volume': common_show_volume
+        },
+        'additionalProperties': False,
+        'required': ['volume']
+    }
+}
+
+list_volumes_detail = copy.deepcopy(common_show_volume)
+# 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
+# list_volumes_detail['properties'].update({'count': {'type': 'integer'}})
+list_volumes_with_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'volumes': {
+                'type': 'array',
+                'items': list_volumes_detail
+            },
+            'volumes_links': parameter_types.links
+        },
+        'additionalProperties': False,
+        'required': ['volumes']
+    }
+}
+
+create_volume = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'volume': {
+                'type': 'object',
+                'properties': {
+                    'migration_status': {'type': ['string', 'null']},
+                    'attachments': attachments,
+                    'links': parameter_types.links,
+                    'availability_zone': {'type': ['string', 'null']},
+                    'encrypted': {'type': 'boolean'},
+                    'updated_at': parameter_types.date_time_or_null,
+                    'replication_status': {'type': ['string', 'null']},
+                    'snapshot_id': parameter_types.uuid_or_null,
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'size': {'type': 'integer'},
+                    'user_id': {'type': 'string', 'format': 'uuid'},
+                    'metadata': {'type': 'object'},
+                    'status': {'type': 'string'},
+                    'description': {'type': ['string', 'null']},
+                    'multiattach': {'type': 'boolean'},
+                    'source_volid': parameter_types.uuid_or_null,
+                    'consistencygroup_id': parameter_types.uuid_or_null,
+                    'name': {'type': ['string', 'null']},
+                    'bootable': {'type': 'string'},
+                    'created_at': parameter_types.date_time,
+                    'volume_type': {'type': ['string', 'null']},
+                    # TODO(zhufl): group_id is added in 3.13, we should move
+                    # it to the 3.13 schema file when microversion is
+                    # supported in volume interfaces.
+                    'group_id': parameter_types.uuid_or_null,
+                    # TODO(zhufl): provider_id is added in 3.21, we should
+                    # move it to the 3.21 schema file when microversion is
+                    # supported in volume interfaces
+                    'provider_id': parameter_types.uuid_or_null,
+                    # TODO(zhufl): service_uuid and shared_targets are added
+                    # in 3.48, we should move them to the 3.48 schema file
+                    # when microversion is supported in volume interfaces.
+                    'service_uuid': parameter_types.uuid_or_null,
+                    'shared_targets': {'type': 'boolean'}
+                },
+                'additionalProperties': False,
+                'required': ['attachments', 'links', 'encrypted',
+                             'updated_at', 'replication_status', 'id',
+                             'size', 'user_id', 'availability_zone',
+                             'metadata', 'status', 'description',
+                             'multiattach', 'consistencygroup_id',
+                             'name', 'bootable', 'created_at',
+                             'volume_type', 'snapshot_id', 'source_volid']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['volume']
+    }
+}
+
+update_volume = copy.deepcopy(create_volume)
+update_volume.update({'status_code': [200]})
+
+delete_volume = {'status_code': [202]}
+
+show_volume_summary = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'volume-summary': {
+                'type': 'object',
+                'properties': {
+                    'total_size': {'type': 'integer'},
+                    'total_count': {'type': 'integer'},
+                    # TODO(zhufl): metadata is added in 3.36, we should move
+                    # it to the 3.36 schema file when microversion is
+                    # supported in volume interfaces
+                    'metadata': {'type': 'object'},
+                },
+                'additionalProperties': False,
+                'required': ['total_size', 'total_count']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['volume-summary']
+    }
+}
+
+# TODO(zhufl): This is under discussion, so will be merged in a seperate patch.
+# https://bugs.launchpad.net/cinder/+bug/1880566
+# upload_volume = {
+#     'status_code': [202],
+#     'response_body': {
+#         'type': 'object',
+#         'properties': {
+#             'os-volume_upload_image': {
+#                 'type': 'object',
+#                 'properties': {
+#                     'status': {'type': 'string'},
+#                     'image_name': {'type': 'string'},
+#                     'disk_format': {'type': 'string'},
+#                     'container_format': {'type': 'string'},
+#                     'is_public': {'type': 'boolean'},
+#                     'visibility': {'type': 'string'},
+#                     'protected': {'type': 'boolean'},
+#                     'updated_at': parameter_types.date_time_or_null,
+#                     'image_id': {'type': 'string', 'format': 'uuid'},
+#                     'display_description': {'type': ['string', 'null']},
+#                     'id': {'type': 'string', 'format': 'uuid'},
+#                     'size': {'type': 'integer'},
+#                     'volume_type': {
+#                         'type': ['object', 'null'],
+#                         'properties': {
+#                             'created_at': parameter_types.date_time,
+#                             'deleted': {'type': 'boolean'},
+#                             'deleted_at': parameter_types.date_time_or_null,
+#                             'description': {'type': ['string', 'null']},
+#                             'extra_specs': {
+#                                 'type': 'object',
+#                                 'patternProperties': {
+#                                     '^.+$': {'type': 'string'}
+#                                 }
+#                             },
+#                             'id': {'type': 'string', 'format': 'uuid'},
+#                             'is_public': {'type': 'boolean'},
+#                             'name': {'type': ['string', 'null']},
+#                             'qos_specs_id': parameter_types.uuid_or_null,
+#                             'updated_at': parameter_types.date_time_or_null
+#                         },
+#                     }
+#                 },
+#                 'additionalProperties': False,
+#                 'required': ['status', 'image_name', 'updated_at',
+#                              'image_id',
+#                              'display_description', 'id', 'size',
+#                              'volume_type', 'disk_format',
+#                              'container_format']
+#             }
+#         },
+#         'additionalProperties': False,
+#         'required': ['os-volume_upload_image']
+#     }
+# }
+
+attach_volume = {'status_code': [202]}
+set_bootable_volume = {'status_code': [200]}
+detach_volume = {'status_code': [202]}
+reserve_volume = {'status_code': [202]}
+unreserve_volume = {'status_code': [202]}
+extend_volume = {'status_code': [202]}
+reset_volume_status = {'status_code': [202]}
+update_volume_readonly = {'status_code': [202]}
+force_delete_volume = {'status_code': [202]}
+retype_volume = {'status_code': [202]}
+force_detach_volume = {'status_code': [202]}
+
+create_volume_metadata = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'metadata': {'type': 'object'},
+        },
+        'additionalProperties': False,
+        'required': ['metadata']
+    }
+}
+
+show_volume_metadata = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'metadata': {'type': 'object'},
+        },
+        'additionalProperties': False,
+        'required': ['metadata']
+    }
+}
+update_volume_metadata = copy.deepcopy(show_volume_metadata)
+
+show_volume_metadata_item = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'meta': {'type': 'object'},
+        },
+        'additionalProperties': False,
+        'required': ['meta']
+    }
+}
+update_volume_metadata_item = copy.deepcopy(show_volume_metadata_item)
+delete_volume_metadata_item = {'status_code': [200]}
+
+update_volume_image_metadata = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {'metadata': {'type': 'object'}},
+        'additionalProperties': False,
+        'required': ['metadata']
+    }
+}
+delete_volume_image_metadata = {'status_code': [200]}
+show_volume_image_metadata = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'metadata': {'type': 'object'},
+        },
+        'additionalProperties': False,
+        'required': ['metadata']
+    }
+}
+
+unmanage_volume = {'status_code': [202]}
diff --git a/tempest/lib/cmd/skip_tracker.py b/tempest/lib/cmd/skip_tracker.py
index 87806b7..95376e3 100755
--- a/tempest/lib/cmd/skip_tracker.py
+++ b/tempest/lib/cmd/skip_tracker.py
@@ -31,10 +31,11 @@
 except ImportError:
     launchpad = None
 
-LPCACHEDIR = os.path.expanduser('~/.launchpadlib/cache')
+LPCACHEDIR = os.path.expanduser(os.path.join('~', '.launchpadlib', 'cache'))
 LOG = logging.getLogger(__name__)
 
-BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..'))
+BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                       '..', '..', '..'))
 TESTDIR = os.path.join(BASEDIR, 'tempest')
 
 
diff --git a/tempest/lib/common/preprov_creds.py b/tempest/lib/common/preprov_creds.py
index 1011504..641d727 100644
--- a/tempest/lib/common/preprov_creds.py
+++ b/tempest/lib/common/preprov_creds.py
@@ -172,7 +172,7 @@
         return self.is_multi_user()
 
     def _create_hash_file(self, hash_string):
-        path = os.path.join(os.path.join(self.accounts_dir, hash_string))
+        path = os.path.join(self.accounts_dir, hash_string)
         if not os.path.isfile(path):
             with open(path, 'w') as fd:
                 fd.write(self.name)
@@ -194,8 +194,7 @@
             if res:
                 return _hash
             else:
-                path = os.path.join(os.path.join(self.accounts_dir,
-                                                 _hash))
+                path = os.path.join(self.accounts_dir, _hash)
                 with open(path, 'r') as fd:
                     names.append(fd.read())
         msg = ('Insufficient number of users provided. %s have allocated all '
diff --git a/tempest/lib/common/utils/linux/remote_client.py b/tempest/lib/common/utils/linux/remote_client.py
index 8ac1d38..71fed02 100644
--- a/tempest/lib/common/utils/linux/remote_client.py
+++ b/tempest/lib/common/utils/linux/remote_client.py
@@ -11,7 +11,6 @@
 #    under the License.
 
 import functools
-import re
 import sys
 
 import netaddr
@@ -134,9 +133,8 @@
         This method will not unmount the config drive, so unmount_config_drive
         must be used for cleanup.
         """
-        cmd_blkid = 'blkid | grep -i config-2'
-        result = self.exec_command(cmd_blkid)
-        dev_name = re.match('([^:]+)', result).group()
+        cmd_blkid = 'blkid -L config-2 -o device'
+        dev_name = self.exec_command(cmd_blkid).strip()
 
         try:
             self.exec_command('sudo mount %s /mnt' % dev_name)
diff --git a/tempest/lib/decorators.py b/tempest/lib/decorators.py
index 808e0fb..ebe2d61 100644
--- a/tempest/lib/decorators.py
+++ b/tempest/lib/decorators.py
@@ -124,7 +124,7 @@
     def decorator(f):
         f = testtools.testcase.attr('id-%s' % id)(f)
         if f.__doc__:
-            f.__doc__ = 'Test idempotent id: %s\n%s' % (id, f.__doc__)
+            f.__doc__ = 'Test idempotent id: %s\n\n%s' % (id, f.__doc__)
         else:
             f.__doc__ = 'Test idempotent id: %s' % id
         return f
diff --git a/tempest/lib/services/volume/v3/backups_client.py b/tempest/lib/services/volume/v3/backups_client.py
index 970471e..1df45fa 100644
--- a/tempest/lib/services/volume/v3/backups_client.py
+++ b/tempest/lib/services/volume/v3/backups_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 backups as schema
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.volume import base_client
@@ -34,7 +35,7 @@
         post_body = json.dumps({'backup': kwargs})
         resp, body = self.post('backups', post_body)
         body = json.loads(body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.create_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def update_backup(self, backup_id, **kwargs):
@@ -47,7 +48,7 @@
         put_body = json.dumps({'backup': kwargs})
         resp, body = self.put('backups/%s' % backup_id, put_body)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.update_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def restore_backup(self, backup_id, **kwargs):
@@ -60,13 +61,13 @@
         post_body = json.dumps({'restore': kwargs})
         resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
         body = json.loads(body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.restore_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_backup(self, backup_id):
         """Delete a backup of volume."""
         resp, body = self.delete('backups/%s' % backup_id)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.delete_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_backup(self, backup_id):
@@ -74,7 +75,7 @@
         url = "backups/%s" % backup_id
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def list_backups(self, detail=False, **params):
@@ -86,13 +87,15 @@
         https://docs.openstack.org/api-ref/block-storage/v3/index.html#list-backups-with-detail
         """
         url = "backups"
+        list_backups_schema = schema.list_backups_no_detail
         if detail:
             url += "/detail"
+            list_backups_schema = schema.list_backups_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_backups_schema, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def export_backup(self, backup_id):
@@ -100,7 +103,7 @@
         url = "backups/%s/export_record" % backup_id
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.export_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def import_backup(self, **kwargs):
@@ -113,14 +116,14 @@
         post_body = json.dumps({'backup-record': kwargs})
         resp, body = self.post("backups/import_record", post_body)
         body = json.loads(body)
-        self.expected_success(201, resp.status)
+        self.validate_response(schema.import_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def reset_backup_status(self, backup_id, status):
         """Reset the specified backup's status."""
         post_body = json.dumps({'os-reset_status': {"status": status}})
         resp, body = self.post('backups/%s/action' % backup_id, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.reset_backup_status, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def is_resource_deleted(self, id):
diff --git a/tempest/lib/services/volume/v3/groups_client.py b/tempest/lib/services/volume/v3/groups_client.py
index ffae232..3d8523d 100644
--- a/tempest/lib/services/volume/v3/groups_client.py
+++ b/tempest/lib/services/volume/v3/groups_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 groups as schema
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.volume import base_client
@@ -23,6 +24,7 @@
 
 class GroupsClient(base_client.BaseClient):
     """Client class to send CRUD Volume Group API requests"""
+    api_version = 'v3'
 
     def create_group(self, **kwargs):
         """Creates a group.
@@ -35,7 +37,7 @@
         post_body = json.dumps({'group': kwargs})
         resp, body = self.post('groups', post_body)
         body = json.loads(body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.create_group, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_group(self, group_id, delete_volumes=True):
@@ -49,7 +51,7 @@
         post_body = json.dumps({'delete': post_body})
         resp, body = self.post('groups/%s/action' % group_id,
                                post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.delete_group, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_group(self, group_id):
@@ -62,7 +64,7 @@
         url = "groups/%s" % str(group_id)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_group, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def list_groups(self, detail=False, **params):
@@ -74,13 +76,15 @@
         https://docs.openstack.org/api-ref/block-storage/v3/#list-groups-with-details
         """
         url = "groups"
+        schema_list_groups = schema.list_groups_no_detail
         if detail:
             url += "/detail"
+            schema_list_groups = schema.list_groups_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(schema_list_groups, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def create_group_from_source(self, **kwargs):
@@ -93,7 +97,7 @@
         post_body = json.dumps({'create-from-src': kwargs})
         resp, body = self.post('groups/action', post_body)
         body = json.loads(body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.create_group_from_source, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def update_group(self, group_id, **kwargs):
@@ -105,7 +109,7 @@
         """
         put_body = json.dumps({'group': kwargs})
         resp, body = self.put('groups/%s' % group_id, put_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.update_group, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def reset_group_status(self, group_id, status_to_set):
@@ -116,7 +120,7 @@
         """
         post_body = json.dumps({'reset_status': {'status': status_to_set}})
         resp, body = self.post('groups/%s/action' % group_id, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.reset_group_status, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def is_resource_deleted(self, id):
diff --git a/tempest/lib/services/volume/v3/volume_manage_client.py b/tempest/lib/services/volume/v3/volume_manage_client.py
index 85b1b82..f6642c5 100644
--- a/tempest/lib/services/volume/v3/volume_manage_client.py
+++ b/tempest/lib/services/volume/v3/volume_manage_client.py
@@ -15,6 +15,7 @@
 
 from oslo_serialization import jsonutils as json
 
+from tempest.lib.api_schema.response.volume import manage_volume as schema
 from tempest.lib.common import rest_client
 
 
@@ -30,6 +31,6 @@
         """
         post_body = json.dumps({'volume': kwargs})
         resp, body = self.post('os-volume-manage', post_body)
-        self.expected_success(202, resp.status)
         body = json.loads(body)
+        self.validate_response(schema.manage_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/volumes_client.py b/tempest/lib/services/volume/v3/volumes_client.py
index 9c6729b..b8535d8 100644
--- a/tempest/lib/services/volume/v3/volumes_client.py
+++ b/tempest/lib/services/volume/v3/volumes_client.py
@@ -17,6 +17,7 @@
 import six
 from six.moves.urllib import parse as urllib
 
+from tempest.lib.api_schema.response.volume import volumes as schema
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.volume import base_client
@@ -55,14 +56,16 @@
         https://docs.openstack.org/api-ref/block-storage/v3/index.html#list-accessible-volumes
         """
         url = 'volumes'
+        list_schema = schema.list_volumes_no_detail
         if detail:
+            list_schema = schema.list_volumes_with_detail
             url += '/detail'
         if params:
             url += '?%s' % self._prepare_params(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 migrate_volume(self, volume_id, **kwargs):
@@ -83,7 +86,7 @@
         url = "volumes/%s" % volume_id
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def create_volume(self, **kwargs):
@@ -96,7 +99,7 @@
         post_body = json.dumps({'volume': kwargs})
         resp, body = self.post('volumes', post_body)
         body = json.loads(body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.create_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def update_volume(self, volume_id, **kwargs):
@@ -109,7 +112,7 @@
         put_body = json.dumps({'volume': kwargs})
         resp, body = self.put('volumes/%s' % volume_id, put_body)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.update_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_volume(self, volume_id, **params):
@@ -123,7 +126,7 @@
         if params:
             url += '?%s' % urllib.urlencode(params)
         resp, body = self.delete(url)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.delete_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_volume_summary(self, **params):
@@ -138,7 +141,7 @@
             url += '?%s' % urllib.urlencode(params)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_volume_summary, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def upload_volume(self, volume_id, **kwargs):
@@ -152,6 +155,10 @@
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
         body = json.loads(body)
+        # TODO(zhufl): This is under discussion, so will be merged
+        # in a seperate patch.
+        # https://bugs.launchpad.net/cinder/+bug/1880566
+        # self.validate_response(schema.upload_volume, resp, body)
         self.expected_success(202, resp.status)
         return rest_client.ResponseBody(resp, body)
 
@@ -165,7 +172,7 @@
         post_body = json.dumps({'os-attach': kwargs})
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.attach_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def set_bootable_volume(self, volume_id, **kwargs):
@@ -178,7 +185,7 @@
         post_body = json.dumps({'os-set_bootable': kwargs})
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.set_bootable_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def detach_volume(self, volume_id):
@@ -186,7 +193,7 @@
         post_body = json.dumps({'os-detach': {}})
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.detach_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def reserve_volume(self, volume_id):
@@ -194,7 +201,7 @@
         post_body = json.dumps({'os-reserve': {}})
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.reserve_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def unreserve_volume(self, volume_id):
@@ -202,7 +209,7 @@
         post_body = json.dumps({'os-unreserve': {}})
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.unreserve_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def is_resource_deleted(self, id):
@@ -237,7 +244,7 @@
         post_body = json.dumps({'os-extend': kwargs})
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.extend_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def reset_volume_status(self, volume_id, **kwargs):
@@ -249,7 +256,7 @@
         """
         post_body = json.dumps({'os-reset_status': kwargs})
         resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.reset_volume_status, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def update_volume_readonly(self, volume_id, **kwargs):
@@ -262,14 +269,14 @@
         post_body = json.dumps({'os-update_readonly_flag': kwargs})
         url = 'volumes/%s/action' % (volume_id)
         resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.update_volume_readonly, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def force_delete_volume(self, volume_id):
         """Force Delete Volume."""
         post_body = json.dumps({'os-force_delete': {}})
         resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.force_delete_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def create_volume_metadata(self, volume_id, metadata):
@@ -283,7 +290,7 @@
         url = "volumes/%s/metadata" % volume_id
         resp, body = self.post(url, put_body)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.create_volume_metadata, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_volume_metadata(self, volume_id):
@@ -291,7 +298,7 @@
         url = "volumes/%s/metadata" % volume_id
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_volume_metadata, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def update_volume_metadata(self, volume_id, metadata):
@@ -305,7 +312,7 @@
         url = "volumes/%s/metadata" % volume_id
         resp, body = self.put(url, put_body)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.update_volume_metadata, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_volume_metadata_item(self, volume_id, id):
@@ -313,7 +320,7 @@
         url = "volumes/%s/metadata/%s" % (volume_id, id)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_volume_metadata_item, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def update_volume_metadata_item(self, volume_id, id, meta_item):
@@ -322,14 +329,14 @@
         url = "volumes/%s/metadata/%s" % (volume_id, id)
         resp, body = self.put(url, put_body)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.update_volume_metadata_item, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_volume_metadata_item(self, volume_id, id):
         """Delete metadata item for the volume."""
         url = "volumes/%s/metadata/%s" % (volume_id, id)
         resp, body = self.delete(url)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.delete_volume_metadata_item, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def retype_volume(self, volume_id, **kwargs):
@@ -341,7 +348,7 @@
         """
         post_body = json.dumps({'os-retype': kwargs})
         resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.retype_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def force_detach_volume(self, volume_id, **kwargs):
@@ -354,7 +361,7 @@
         post_body = json.dumps({'os-force_detach': kwargs})
         url = 'volumes/%s/action' % volume_id
         resp, body = self.post(url, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.force_detach_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def update_volume_image_metadata(self, volume_id, **kwargs):
@@ -368,7 +375,7 @@
         url = "volumes/%s/action" % (volume_id)
         resp, body = self.post(url, post_body)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.update_volume_image_metadata, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_volume_image_metadata(self, volume_id, key_name):
@@ -376,7 +383,7 @@
         post_body = json.dumps({'os-unset_image_metadata': {'key': key_name}})
         url = "volumes/%s/action" % (volume_id)
         resp, body = self.post(url, post_body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.delete_volume_image_metadata, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_volume_image_metadata(self, volume_id):
@@ -385,7 +392,7 @@
         url = "volumes/%s/action" % volume_id
         resp, body = self.post(url, post_body)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_volume_image_metadata, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def unmanage_volume(self, volume_id):
@@ -397,5 +404,5 @@
         """
         post_body = json.dumps({'os-unmanage': {}})
         resp, body = self.post('volumes/%s/action' % volume_id, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.unmanage_volume, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/manager.py b/tempest/manager.py
index e3174d4..b485ef2 100644
--- a/tempest/manager.py
+++ b/tempest/manager.py
@@ -33,7 +33,7 @@
 
     def __init__(self, credentials, scope='project'):
         msg = ("tempest.manager.Manager is not a stable interface and as such "
-               "it should not imported directly. It will be removed as "
+               "it should not be imported directly. It will be removed as "
                "soon as the client manager becomes available in tempest.lib.")
         LOG.warning(msg)
         dscv = CONF.identity.disable_ssl_certificate_validation
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index a80b77d..ff860d5 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -91,10 +91,32 @@
             volume_microversion=self.volume_request_microversion,
             placement_microversion=self.placement_request_microversion))
 
+    def setup_compute_client(cls):
+        """Compute and Compute security groups client"""
+        cls.compute_images_client = cls.os_primary.compute_images_client
+        cls.keypairs_client = cls.os_primary.keypairs_client
+        cls.compute_security_groups_client = (
+            cls.os_primary.compute_security_groups_client)
+        cls.compute_security_group_rules_client = (
+            cls.os_primary.compute_security_group_rules_client)
+        cls.servers_client = cls.os_primary.servers_client
+        cls.interface_client = cls.os_primary.interfaces_client
+
+    def setup_network_client(cls):
+        """Neutron network client"""
+        cls.networks_client = cls.os_primary.networks_client
+        cls.ports_client = cls.os_primary.ports_client
+        cls.routers_client = cls.os_primary.routers_client
+        cls.subnets_client = cls.os_primary.subnets_client
+        cls.floating_ips_client = cls.os_primary.floating_ips_client
+        cls.security_groups_client = cls.os_primary.security_groups_client
+        cls.security_group_rules_client = (
+            cls.os_primary.security_group_rules_client)
+
     @classmethod
     def setup_clients(cls):
+        """This setup the service clients for the tests"""
         super(ScenarioTest, cls).setup_clients()
-        # Clients (in alphabetical order)
         cls.flavors_client = cls.os_primary.flavors_client
         cls.compute_floating_ips_client = (
             cls.os_primary.compute_floating_ips_client)
@@ -108,37 +130,20 @@
                 raise lib_exc.InvalidConfiguration(
                     'Either api_v1 or api_v2 must be True in '
                     '[image-feature-enabled].')
-        # Compute image client
-        cls.compute_images_client = cls.os_primary.compute_images_client
-        cls.keypairs_client = cls.os_primary.keypairs_client
-        # Nova security groups client
-        cls.compute_security_groups_client = (
-            cls.os_primary.compute_security_groups_client)
-        cls.compute_security_group_rules_client = (
-            cls.os_primary.compute_security_group_rules_client)
-        cls.servers_client = cls.os_primary.servers_client
-        cls.interface_client = cls.os_primary.interfaces_client
-        # Neutron network client
-        cls.networks_client = cls.os_primary.networks_client
-        cls.ports_client = cls.os_primary.ports_client
-        cls.routers_client = cls.os_primary.routers_client
-        cls.subnets_client = cls.os_primary.subnets_client
-        cls.floating_ips_client = cls.os_primary.floating_ips_client
-        cls.security_groups_client = cls.os_primary.security_groups_client
-        cls.security_group_rules_client = (
-            cls.os_primary.security_group_rules_client)
-        # Use the latest available volume clients
+
+        cls.setup_compute_client(cls)
+        cls.setup_network_client(cls)
         if CONF.service_available.cinder:
             cls.volumes_client = cls.os_primary.volumes_client_latest
             cls.snapshots_client = cls.os_primary.snapshots_client_latest
             cls.backups_client = cls.os_primary.backups_client_latest
 
     # ## Test functions library
-    #
     # The create_[resource] functions only return body and discard the
     # resp part which is not used in scenario tests
 
     def create_port(self, network_id, client=None, **kwargs):
+        """Creates port"""
         if not client:
             client = self.ports_client
         name = data_utils.rand_name(self.__class__.__name__)
@@ -156,6 +161,13 @@
         return port
 
     def create_keypair(self, client=None):
+        """Creates keypair
+
+        Keypair is a public key of OpenSSH key pair used for accessing
+        and create servers
+        Keypair can also be created by a private key for the same purpose
+        Here, the keys are randomly generated[public/private]
+        """
         if not client:
             client = self.keypairs_client
         name = data_utils.rand_name(self.__class__.__name__)
@@ -295,6 +307,13 @@
 
     def create_volume(self, size=None, name=None, snapshot_id=None,
                       imageRef=None, volume_type=None):
+        """Creates volume
+
+        This wrapper utility creates volume and waits for volume to be
+        in 'available' state.
+        This method returns the volume's full representation by GET request.
+        """
+
         if size is None:
             size = CONF.volume.volume_size
         if imageRef:
@@ -334,6 +353,11 @@
     def create_backup(self, volume_id, name=None, description=None,
                       force=False, snapshot_id=None, incremental=False,
                       container=None):
+        """Creates backup
+
+        This wrapper utility creates backup and waits for backup to be
+        in 'available' state.
+        """
 
         name = name or data_utils.rand_name(
             self.__class__.__name__ + "-backup")
@@ -351,6 +375,12 @@
         return backup
 
     def restore_backup(self, backup_id):
+        """Restore backup
+
+        This wrapper utility restores backup and waits for backup to be
+        in 'available' state.
+        """
+
         restore = self.backups_client.restore_backup(backup_id)['restore']
         self.addCleanup(self.volumes_client.delete_volume,
                         restore['volume_id'])
@@ -362,8 +392,31 @@
         self.assertEqual(backup_id, restore['backup_id'])
         return restore
 
+    def rebuild_server(self, server_id, image=None,
+                       preserve_ephemeral=False, wait=True,
+                       rebuild_kwargs=None):
+        if image is None:
+            image = CONF.compute.image_ref
+        rebuild_kwargs = rebuild_kwargs or {}
+        LOG.debug("Rebuilding server (id: %s, image: %s, preserve eph: %s)",
+                  server_id, image, preserve_ephemeral)
+        self.servers_client.rebuild_server(
+            server_id=server_id,
+            image_ref=image,
+            preserve_ephemeral=preserve_ephemeral,
+            **rebuild_kwargs)
+        if wait:
+            waiters.wait_for_server_status(self.servers_client,
+                                           server_id, 'ACTIVE')
+
     def create_volume_snapshot(self, volume_id, name=None, description=None,
                                metadata=None, force=False):
+        """Creates volume
+
+        This wrapper utility creates volume snapshot and waits for backup
+        to be in 'available' state.
+        """
+
         name = name or data_utils.rand_name(
             self.__class__.__name__ + '-snapshot')
         snapshot = self.snapshots_client.create_snapshot(
@@ -372,6 +425,7 @@
             display_name=name,
             description=description,
             metadata=metadata)['snapshot']
+
         self.addCleanup(self.snapshots_client.wait_for_resource_deletion,
                         snapshot['id'])
         self.addCleanup(self.snapshots_client.delete_snapshot, snapshot['id'])
@@ -401,6 +455,23 @@
         admin_volume_type_client.delete_volume_type(volume_type['id'])
 
     def create_volume_type(self, client=None, name=None, backend_name=None):
+        """Creates volume type
+
+        In a multiple-storage back-end configuration,
+        each back end has a name (volume_backend_name).
+        The name of the back end is declared as an extra-specification
+        of a volume type (such as, volume_backend_name=LVM).
+        When a volume is created, the scheduler chooses an
+        appropriate back end to handle the request, according
+        to the volume type specified by the user.
+        The scheduler uses volume types to explicitly create volumes on
+        specific back ends.
+
+        Before using volume type, a volume type has to be declared
+        to Block Storage. In addition to that, an extra-specification
+        has to be created to link the volume type to a back end name.
+        """
+
         if not client:
             client = self.os_admin.volume_types_client_latest
         if not name:
@@ -416,6 +487,7 @@
 
         volume_type = client.create_volume_type(
             name=randomized_name, extra_specs=extra_specs)['volume_type']
+        self.assertIn('id', volume_type)
         self.addCleanup(self._cleanup_volume_type, volume_type)
         return volume_type
 
@@ -456,7 +528,7 @@
         return rules
 
     def _create_security_group(self):
-        # Create security group
+        """Create security group and add rules to security group"""
         sg_name = data_utils.rand_name(self.__class__.__name__)
         sg_desc = sg_name + " description"
         secgroup = self.compute_security_groups_client.create_security_group(
@@ -470,7 +542,6 @@
 
         # Add rules to the security group
         self._create_loginable_secgroup_rule(secgroup['id'])
-
         return secgroup
 
     def get_remote_client(self, ip_address, username=None, private_key=None,
@@ -502,36 +573,7 @@
         linux_client.validate_authentication()
         return linux_client
 
-    def _image_create(self, name, fmt, path,
-                      disk_format=None, properties=None):
-        if properties is None:
-            properties = {}
-        name = data_utils.rand_name('%s-' % name)
-        params = {
-            'name': name,
-            'container_format': fmt,
-            'disk_format': disk_format or fmt,
-        }
-        if CONF.image_feature_enabled.api_v1:
-            params['is_public'] = 'False'
-            params['properties'] = properties
-            params = {'headers': common_image.image_meta_to_headers(**params)}
-        else:
-            params['visibility'] = 'private'
-            # Additional properties are flattened out in the v2 API.
-            params.update(properties)
-        body = self.image_client.create_image(**params)
-        image = body['image'] if 'image' in body else body
-        self.addCleanup(self.image_client.delete_image, image['id'])
-        self.assertEqual("queued", image['status'])
-        with open(path, 'rb') as image_file:
-            if CONF.image_feature_enabled.api_v1:
-                self.image_client.update_image(image['id'], data=image_file)
-            else:
-                self.image_client.store_image_file(image['id'], image_file)
-        return image['id']
-
-    def glance_image_create(self):
+    def image_create(self, name='scenario-img'):
         img_path = CONF.scenario.img_file
         if not os.path.exists(img_path):
             # TODO(kopecmartin): replace LOG.warning for rasing
@@ -553,16 +595,38 @@
                   "properties: %s",
                   img_path, img_container_format, img_disk_format,
                   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
+        if img_properties is None:
+            img_properties = {}
+        name = data_utils.rand_name('%s-' % name)
+        params = {
+            'name': name,
+            'container_format': img_container_format,
+            'disk_format': img_disk_format or img_container_format,
+        }
+        if CONF.image_feature_enabled.api_v1:
+            params['is_public'] = 'False'
+            if img_properties:
+                params['properties'] = img_properties
+            params = {'headers': common_image.image_meta_to_headers(**params)}
+        else:
+            params['visibility'] = 'private'
+            # Additional properties are flattened out in the v2 API.
+            if img_properties:
+                params.update(img_properties)
+        body = self.image_client.create_image(**params)
+        image = body['image'] if 'image' in body else body
+        self.addCleanup(self.image_client.delete_image, image['id'])
+        self.assertEqual("queued", image['status'])
+        with open(img_path, 'rb') as image_file:
+            if CONF.image_feature_enabled.api_v1:
+                self.image_client.update_image(image['id'], data=image_file)
+            else:
+                self.image_client.store_image_file(image['id'], image_file)
+        LOG.debug("image:%s", image['id'])
+        return image['id']
 
     def _log_console_output(self, servers=None, client=None):
+        """Console log output"""
         if not CONF.compute_feature_enabled.console_output:
             LOG.debug('Console output not supported, cannot log')
             return
@@ -581,11 +645,12 @@
                           "for the console log", server['id'])
 
     def _log_net_info(self, exc):
-        # network debug is called as part of ssh init
+        """network debug is called as part of ssh init"""
         if not isinstance(exc, lib_exc.SSHTimeout):
             LOG.debug('Network information on a devstack host')
 
     def create_server_snapshot(self, server, name=None):
+        """Creates server snapshot"""
         # Glance client
         _image_client = self.image_client
         # Compute client
@@ -603,7 +668,7 @@
                         _image_client.delete_image, image_id)
 
         if CONF.image_feature_enabled.api_v1:
-            # In glance v1 the additional properties are stored in the headers.
+            # In glance v1 the additional properties are stored in the headers
             resp = _image_client.check_image(image_id)
             snapshot_image = common_image.get_image_meta_from_headers(resp)
             image_props = snapshot_image.get('properties', {})
@@ -633,22 +698,34 @@
         return snapshot_image
 
     def nova_volume_attach(self, server, volume_to_attach):
+        """Compute volume attach
+
+        This utility attaches volume from compute and waits for the
+        volume status to be 'in-use' state.
+        """
         volume = self.servers_client.attach_volume(
             server['id'], volumeId=volume_to_attach['id'])['volumeAttachment']
         self.assertEqual(volume_to_attach['id'], volume['id'])
         waiters.wait_for_volume_resource_status(self.volumes_client,
                                                 volume['id'], 'in-use')
-
         # Return the updated volume after the attachment
         return self.volumes_client.show_volume(volume['id'])['volume']
 
     def nova_volume_detach(self, server, volume):
+        """Compute volume detach
+
+        This utility detaches volume from compute and check whether the
+        volume status is 'available' state, and if not, an exception
+        will be thrown.
+        """
         self.servers_client.detach_volume(server['id'], volume['id'])
         waiters.wait_for_volume_resource_status(self.volumes_client,
                                                 volume['id'], 'available')
+        volume = self.volumes_client.show_volume(volume['id'])['volume']
 
     def ping_ip_address(self, ip_address, should_succeed=True,
                         ping_timeout=None, mtu=None, server=None):
+        """ping ip address"""
         timeout = ping_timeout or CONF.validation.ping_timeout
         cmd = ['ping', '-c1', '-w1']
 
@@ -709,6 +786,7 @@
         :raises: AssertError if the result of the connectivity check does
             not match the value of the should_connect param
         """
+
         LOG.debug('checking network connections to IP %s with user: %s',
                   ip_address, username)
         if should_connect:
@@ -732,7 +810,7 @@
                 LOG.exception(extra_msg)
                 raise
 
-    def create_floating_ip(self, thing, pool_name=None):
+    def create_floating_ip(self, server, pool_name=None):
         """Create a floating IP and associates to a server on Nova"""
 
         if not pool_name:
@@ -743,11 +821,17 @@
                         self.compute_floating_ips_client.delete_floating_ip,
                         floating_ip['id'])
         self.compute_floating_ips_client.associate_floating_ip_to_server(
-            floating_ip['ip'], thing['id'])
+            floating_ip['ip'], server['id'])
         return floating_ip
 
     def create_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
                          private_key=None, server=None):
+        """Creates timestamp
+
+        This wrapper utility does ssh, creates timestamp and returns the
+        created timestamp.
+        """
+
         ssh_client = self.get_remote_client(ip_address,
                                             private_key=private_key,
                                             server=server)
@@ -765,6 +849,11 @@
 
     def get_timestamp(self, ip_address, dev_name=None, mount_path='/mnt',
                       private_key=None, server=None):
+        """Returns timestamp
+
+        This wrapper utility does ssh and returns the timestamp.
+        """
+
         ssh_client = self.get_remote_client(ip_address,
                                             private_key=private_key,
                                             server=server)
@@ -782,6 +871,7 @@
         Based on the configuration we're in, return a correct ip
         address for validating that a guest is up.
         """
+
         if CONF.validation.connect_method == 'floating':
             # The tests calling this method don't have a floating IP
             # and can't make use of the validation resources. So the
@@ -807,6 +897,8 @@
 
     @classmethod
     def get_host_for_server(cls, server_id):
+        """Gets host of server"""
+
         server_details = cls.os_admin.servers_client.show_server(server_id)
         return server_details['server']['OS-EXT-SRV-ATTR:host']
 
@@ -825,6 +917,12 @@
                                     security_group=None,
                                     delete_on_termination=False,
                                     name=None):
+        """Boot instance from resource
+
+        This wrapper utility boots instance from resource with block device
+        mapping with source info passed in arguments
+        """
+
         create_kwargs = dict()
         if keypair:
             create_kwargs['key_name'] = keypair['name']
@@ -841,6 +939,7 @@
         return self.create_server(image_id='', **create_kwargs)
 
     def create_volume_from_image(self):
+        """Create volume from image"""
         img_uuid = CONF.compute.image_ref
         vol_name = data_utils.rand_name(
             self.__class__.__name__ + '-volume-origin')
@@ -896,8 +995,17 @@
                       namestart='subnet-smoke', **kwargs):
         """Create a subnet for the given network
 
+        This utility creates subnet for the given network
         within the cidr block configured for tenant networks.
+
+        :param **kwargs:
+            See extra parameters below
+
+        :Keyword Arguments:
+
+            * *ip_version = ip version of the given network,
         """
+
         if not subnets_client:
             subnets_client = self.subnets_client
 
@@ -1000,22 +1108,23 @@
                             "Unable to get network by name: %s" % network_name)
         return net[0]
 
-    def create_floating_ip(self, thing, external_network_id=None,
+    def create_floating_ip(self, server, external_network_id=None,
                            port_id=None, client=None):
         """Create a floating IP and associates to a resource/port on Neutron"""
+
         if not external_network_id:
             external_network_id = CONF.network.public_network_id
         if not client:
             client = self.floating_ips_client
         if not port_id:
-            port_id, ip4 = self._get_server_port_id_and_ip4(thing)
+            port_id, ip4 = self._get_server_port_id_and_ip4(server)
         else:
             ip4 = None
 
         kwargs = {
             'floating_network_id': external_network_id,
             'port_id': port_id,
-            'tenant_id': thing.get('project_id') or thing['tenant_id'],
+            'tenant_id': server.get('project_id') or server['tenant_id'],
             'fixed_ip_address': ip4,
         }
         if CONF.network.subnet_id:
@@ -1035,6 +1144,7 @@
         :param status: target status
         :raises: AssertionError if status doesn't match
         """
+
         floatingip_id = floating_ip['id']
 
         def refresh():
@@ -1061,6 +1171,7 @@
                                           private_key,
                                           should_connect=True,
                                           servers_for_debug=None):
+        """Checks tenant network connectivity"""
         if not CONF.network.project_networks_reachable:
             msg = 'Tenant networks not configured to be reachable.'
             LOG.info(msg)
@@ -1093,6 +1204,7 @@
         :returns: True, if the connection succeeded and it was expected to
             succeed. False otherwise.
         """
+
         method_name = '%s_check' % protocol
         connectivity_checker = getattr(source, method_name)
 
@@ -1156,6 +1268,7 @@
         :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 project_id:
@@ -1197,6 +1310,7 @@
                     port_range_max: 22
                     }
         """
+
         if sec_group_rules_client is None:
             sec_group_rules_client = self.security_group_rules_client
         if security_groups_client is None:
@@ -1287,6 +1401,7 @@
         network has, a tenant router will be created and returned that
         routes traffic to the public network.
         """
+
         if not client:
             client = self.routers_client
         if not project_id:
@@ -1327,6 +1442,7 @@
                                    'provider:segmentation_id': '42'}
         :returns: network, subnet, router
         """
+
         if CONF.network.shared_physical_network:
             # NOTE(Shrews): This exception is for environments where tenant
             # credential isolation is available, but network separation is
@@ -1383,6 +1499,7 @@
     def create_encryption_type(self, client=None, type_id=None, provider=None,
                                key_size=None, cipher=None,
                                control_location=None):
+        """Creates an encryption type for volume"""
         if not client:
             client = self.admin_encryption_types_client
         if not type_id:
@@ -1396,6 +1513,7 @@
     def create_encrypted_volume(self, encryption_provider, volume_type,
                                 key_size=256, cipher='aes-xts-plain64',
                                 control_location='front-end'):
+        """Creates an encrypted volume"""
         volume_type = self.create_volume_type(name=volume_type)
         self.create_encryption_type(type_id=volume_type['id'],
                                     provider=encryption_provider,
@@ -1436,11 +1554,12 @@
         cls.object_client = cls.os_operator.object_client
 
     def get_swift_stat(self):
-        """get swift status for our user account."""
+        """Get swift status for our user account."""
         self.account_client.list_account_containers()
         LOG.debug('Swift status information obtained successfully')
 
     def create_container(self, container_name=None):
+        """Creates container"""
         name = container_name or data_utils.rand_name(
             'swift-scenario-container')
         self.container_client.update_container(name)
@@ -1453,10 +1572,12 @@
         return name
 
     def delete_container(self, container_name):
+        """Deletes container"""
         self.container_client.delete_container(container_name)
         LOG.debug('Container %s deleted', container_name)
 
     def upload_object_to_container(self, container_name, obj_name=None):
+        """Uploads object to container"""
         obj_name = obj_name or data_utils.rand_name('swift-scenario-object')
         obj_data = data_utils.random_bytes()
         self.object_client.create_object(container_name, obj_name, obj_data)
@@ -1467,6 +1588,7 @@
         return obj_name, obj_data
 
     def delete_object(self, container_name, filename):
+        """Deletes object"""
         self.object_client.delete_object(container_name, filename)
         self.list_and_check_container_objects(container_name,
                                               not_present_obj=[filename])
@@ -1474,8 +1596,13 @@
     def list_and_check_container_objects(self, container_name,
                                          present_obj=None,
                                          not_present_obj=None):
-        # List objects for a given container and assert which are present and
-        # which are not.
+        """List and verify objects for a given container
+
+        This utility lists objects for a given container
+        and asserts which are present and
+        which are not
+        """
+
         if present_obj is None:
             present_obj = []
         if not_present_obj is None:
@@ -1490,5 +1617,6 @@
                 self.assertNotIn(obj, object_list)
 
     def download_and_verify(self, container_name, obj_name, expected_data):
+        """Asserts the object and expected data to verify if they are same"""
         _, obj = self.object_client.get_object(container_name, obj_name)
         self.assertEqual(obj, expected_data)
diff --git a/tempest/scenario/test_encrypted_cinder_volumes.py b/tempest/scenario/test_encrypted_cinder_volumes.py
index 008d1ae..fc93a5e 100644
--- a/tempest/scenario/test_encrypted_cinder_volumes.py
+++ b/tempest/scenario/test_encrypted_cinder_volumes.py
@@ -44,7 +44,7 @@
             raise cls.skipException('Encrypted volume attach is not supported')
 
     def launch_instance(self):
-        image = self.glance_image_create()
+        image = self.image_create()
         keypair = self.create_keypair()
 
         return self.create_server(image_id=image, key_name=keypair['name'])
diff --git a/tempest/scenario/test_minbw_allocation_placement.py b/tempest/scenario/test_minbw_allocation_placement.py
index e7085f6..5eab1da 100644
--- a/tempest/scenario/test_minbw_allocation_placement.py
+++ b/tempest/scenario/test_minbw_allocation_placement.py
@@ -124,8 +124,11 @@
             resources1='%s:%s' % (self.INGRESS_RESOURCE_CLASS,
                                   self.SMALLEST_POSSIBLE_BW))
         if len(alloc_candidates['provider_summaries']) == 0:
-            self.fail('No allocation candidates are available for %s:%s' %
-                      (self.INGRESS_RESOURCE_CLASS, self.SMALLEST_POSSIBLE_BW))
+            # Skip if the backend does not support QoS minimum bandwidth
+            # allocation in Placement API
+            raise self.skipException(
+                'No allocation candidates are available for %s:%s' %
+                (self.INGRESS_RESOURCE_CLASS, self.SMALLEST_POSSIBLE_BW))
 
         # Just to be sure check with impossible high (placement max_int),
         # allocation
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 4cd860d..fe42583 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -106,7 +106,7 @@
     @decorators.idempotent_id('bdbb5441-9204-419d-a225-b4fdbfb1a1a8')
     @utils.services('compute', 'volume', 'image', 'network')
     def test_minimum_basic_scenario(self):
-        image = self.glance_image_create()
+        image = self.image_create()
         keypair = self.create_keypair()
 
         server = self.create_server(image_id=image, key_name=keypair['name'])
diff --git a/tempest/scenario/test_network_v6.py b/tempest/scenario/test_network_v6.py
index 8de6614..14f24c7 100644
--- a/tempest/scenario/test_network_v6.py
+++ b/tempest/scenario/test_network_v6.py
@@ -130,7 +130,7 @@
             key_name=self.keypair['name'],
             security_groups=[{'name': self.sec_grp['name']}],
             networks=[{'uuid': n['id']} for n in networks])
-        fip = self.create_floating_ip(thing=srv)
+        fip = self.create_floating_ip(server=srv)
         ips = self.define_server_ips(srv=srv)
         ssh = self.get_remote_client(
             ip_address=fip['floating_ip_address'],
diff --git a/tempest/test_discover/test_discover.py b/tempest/test_discover/test_discover.py
index 143c6e1..5816ab1 100644
--- a/tempest/test_discover/test_discover.py
+++ b/tempest/test_discover/test_discover.py
@@ -30,8 +30,8 @@
     base_path = os.path.split(os.path.dirname(os.path.abspath(__file__)))[0]
     base_path = os.path.split(base_path)[0]
     # Load local tempest tests
-    for test_dir in ['tempest/api', 'tempest/scenario']:
-        full_test_dir = os.path.join(base_path, test_dir)
+    for test_dir in ['api', 'scenario']:
+        full_test_dir = os.path.join(base_path, 'tempest', test_dir)
         if not pattern:
             suite.addTests(loader.discover(full_test_dir,
                                            top_level_dir=base_path))
diff --git a/tempest/tests/cmd/sample_streams/calls.subunit b/tempest/tests/cmd/subunit_describe_calls_data/calls.subunit
similarity index 100%
rename from tempest/tests/cmd/sample_streams/calls.subunit
rename to tempest/tests/cmd/subunit_describe_calls_data/calls.subunit
Binary files differ
diff --git a/tempest/tests/cmd/subunit_describe_calls_data/calls_subunit_expected.json b/tempest/tests/cmd/subunit_describe_calls_data/calls_subunit_expected.json
new file mode 100644
index 0000000..53976ee
--- /dev/null
+++ b/tempest/tests/cmd/subunit_describe_calls_data/calls_subunit_expected.json
@@ -0,0 +1,87 @@
+{"bar":[
+      {
+         "name":"AgentsAdminTestJSON:setUp",
+         "request_body":"{\"agent\": {\"url\": \"xxx://xxxx/xxx/xxx\", \"hypervisor\": \"common\", \"md5hash\": \"add6bb58e139be103324d04d82d8f545\", \"version\": \"7.0\", \"architecture\": \"tempest-x86_64-424013832\", \"os\": \"linux\"}}",
+         "request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+         "response_body":"{\"agent\": {\"url\": \"xxx://xxxx/xxx/xxx\", \"hypervisor\": \"common\", \"md5hash\": \"add6bb58e139be103324d04d82d8f545\", \"version\": \"7.0\", \"architecture\": \"tempest-x86_64-424013832\", \"os\": \"linux\", \"agent_id\": 1}}",
+         "response_headers":"{'status': '200', 'content-length': '203', 'x-compute-request-id': 'req-25ddaae2-0ef1-40d1-8228-59bd64a7e75b', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': 'application/json'}",
+         "service":"Nova",
+         "status_code":"200",
+         "url":"v2.1/<id>/os-agents",
+         "verb":"POST"
+},
+      {
+         "name":"AgentsAdminTestJSON:test_create_agent",
+         "request_body":"{\"agent\": {\"url\": \"xxx://xxxx/xxx/xxx\", \"hypervisor\": \"kvm\", \"md5hash\": \"add6bb58e139be103324d04d82d8f545\", \"version\": \"7.0\", \"architecture\": \"tempest-x86-252246646\", \"os\": \"win\"}}",
+         "request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+         "response_body":"{\"agent\": {\"url\": \"xxx://xxxx/xxx/xxx\", \"hypervisor\": \"kvm\", \"md5hash\": \"add6bb58e139be103324d04d82d8f545\", \"version\": \"7.0\", \"architecture\": \"tempest-x86-252246646\", \"os\": \"win\", \"agent_id\": 2}}",
+         "response_headers":"{'status': '200', 'content-length': '195', 'x-compute-request-id': 'req-b4136f06-c015-4e7e-995f-c43831e3ecce', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': 'application/json'}",
+         "service":"Nova",
+         "status_code":"200",
+         "url":"v2.1/<id>/os-agents",
+         "verb":"POST"
+},
+      {
+         "name":"AgentsAdminTestJSON:tearDown",
+         "request_body":"None",
+         "request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+         "response_body":"",
+         "response_headers":"{'status': '200', 'content-length': '0', 'x-compute-request-id': 'req-ee905fd6-a5b5-4da4-8c37-5363cb25bd9d', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': 'application/json'}",
+         "service":"Nova",
+         "status_code":"200",
+         "url":"v2.1/<id>/os-agents/1",
+         "verb":"DELETE"
+},
+      {
+         "name":"AgentsAdminTestJSON:_run_cleanups",
+         "request_body":"None",
+         "request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+         "response_headers":"{'status': '200', 'content-length': '0', 'x-compute-request-id': 'req-e912cac0-63e0-4679-a68a-b6d18ddca074', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': 'application/json'}",
+         "service":"Nova",
+         "status_code":"200",
+         "url":"v2.1/<id>/os-agents/2",
+         "verb":"DELETE"
+}], "foo":[
+      {
+         "name":"AgentsAdminTestJSON:setUp",
+         "request_body":"{\"agent\": {\"url\": \"xxx://xxxx/xxx/xxx\", \"hypervisor\": \"common\", \"md5hash\": \"add6bb58e139be103324d04d82d8f545\", \"version\": \"7.0\", \"architecture\": \"tempest-x86_64-948635295\", \"os\": \"linux\"}}",
+         "request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+         "response_body":"{\"agent\": {\"url\": \"xxx://xxxx/xxx/xxx\", \"hypervisor\": \"common\", \"md5hash\": \"add6bb58e139be103324d04d82d8f545\", \"version\": \"7.0\", \"architecture\": \"tempest-x86_64-948635295\", \"os\": \"linux\", \"agent_id\": 3}}",
+         "response_headers":"{'status': '200', 'content-length': '203', 'x-compute-request-id': 'req-ccd2116d-04b1-4ffe-ae32-fb623f68bf1c', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': 'application/json'}",
+         "service":"Nova",
+         "status_code":"200",
+         "url":"v2.1/<id>/os-agents",
+         "verb":"POST"
+},
+      {
+         "name":"AgentsAdminTestJSON:test_delete_agent",
+         "request_body":"None",
+         "request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+         "response_body":"",
+         "response_headers":"{'status': '200', 'content-length': '0', 'x-compute-request-id': 'req-6e7fa28f-ae61-4388-9a78-947c58bc0588', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': 'application/json'}",
+         "service":"Nova",
+         "status_code":"200",
+         "url":"v2.1/<id>/os-agents/3",
+         "verb":"DELETE"
+},
+      {
+         "name":"AgentsAdminTestJSON:test_delete_agent",
+         "request_body":"None",
+         "request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+         "response_body":"{\"agents\": []}",
+         "response_headers":"{'status': '200', 'content-length': '14', 'content-location': 'http://23.253.76.97:8774/v2.1/cf6b1933fe5b476fbbabb876f6d1b924/os-agents', 'x-compute-request-id': 'req-e41aa9b4-41a6-4138-ae04-220b768eb644', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': 'application/json'}",
+         "service":"Nova",
+         "status_code":"200",
+         "url":"v2.1/<id>/os-agents",
+         "verb":"GET"
+},
+      {
+         "name":"AgentsAdminTestJSON:tearDown",
+         "request_body":"None",
+         "request_headers":"{'Content-Type': 'application/json', 'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
+         "response_headers":"{'status': '404', 'content-length': '82', 'x-compute-request-id': 'req-e297aeea-91cf-4f26-b49c-8f46b1b7a926', 'vary': 'X-OpenStack-Nova-API-Version', 'connection': 'close', 'x-openstack-nova-api-version': '2.1', 'date': 'Tue, 02 Feb 2016 03:27:02 GMT', 'content-type': 'application/json; charset=UTF-8'}",
+         "service":"Nova",
+         "status_code":"404",
+         "url":"v2.1/<id>/os-agents/3",
+         "verb":"DELETE"
+}]}
\ No newline at end of file
diff --git a/tempest/tests/cmd/test_cleanup_services.py b/tempest/tests/cmd/test_cleanup_services.py
index 7bf7315..fc44793 100644
--- a/tempest/tests/cmd/test_cleanup_services.py
+++ b/tempest/tests/cmd/test_cleanup_services.py
@@ -511,7 +511,8 @@
             },
             {
                 "id": "aa77asdf-1234",
-                "name": "saved-volume"
+                "name": "saved-volume",
+                "links": [],
             }
         ]
     }
diff --git a/tempest/tests/cmd/test_subunit_describe_calls.py b/tempest/tests/cmd/test_subunit_describe_calls.py
index cb34ba6..4fed84a 100644
--- a/tempest/tests/cmd/test_subunit_describe_calls.py
+++ b/tempest/tests/cmd/test_subunit_describe_calls.py
@@ -14,220 +14,422 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
+import argparse
+from io import StringIO
 import os
+import shutil
 import subprocess
+import sys
 import tempfile
+from unittest import mock
+from unittest.mock import patch
 
+
+from oslo_serialization import jsonutils as json
 from tempest.cmd import subunit_describe_calls
 from tempest.tests import base
 
 
-class TestSubunitDescribeCalls(base.TestCase):
-    def test_return_code(self):
-        subunit_file = os.path.join(
-            os.path.dirname(os.path.abspath(__file__)),
-            'sample_streams/calls.subunit')
-        p = subprocess.Popen([
-            'subunit-describe-calls', '-s', subunit_file,
-            '-o', tempfile.mkstemp()[1]], stdin=subprocess.PIPE)
-        p.communicate()
-        self.assertEqual(0, p.returncode)
+class TestArgumentParser(base.TestCase):
+    def test_init(self):
+        test_object = subunit_describe_calls.ArgumentParser()
+        self.assertEqual("subunit-describe-calls", test_object.prog)
+        self.assertEqual(subunit_describe_calls.DESCRIPTION,
+                         test_object.description)
 
-    def test_verbose(self):
-        subunit_file = os.path.join(
-            os.path.dirname(os.path.abspath(__file__)),
-            'sample_streams/calls.subunit')
-        p = subprocess.Popen([
-            'subunit-describe-calls', '-s', subunit_file,
-            '-v'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
-        stdout = p.communicate()
-        self.assertEqual(0, p.returncode)
-        self.assertIn(b'- request headers:', stdout[0])
-        self.assertIn(b'- request body:', stdout[0])
-        self.assertIn(b'- response headers:', stdout[0])
-        self.assertIn(b'- response body:', stdout[0])
 
-    def test_return_code_no_output(self):
-        subunit_file = os.path.join(
+class TestUrlParser(base.TestCase):
+    services_custom_ports = {
+        "18776": "Block Storage",
+        "18774": "Nova",
+        "18773": "Nova-API",
+        "18386": "Sahara",
+        "35358": "Keystone",
+        "19292": "Glance",
+        "19696": "Neutron",
+        "16000": "Swift",
+        "18004": "Heat",
+        "18777": "Ceilometer",
+        "10080": "Horizon",
+        "18080": "Swift",
+        "1873": "rsync",
+        "13260": "iSCSI",
+        "13306": "MySQL",
+        "15672": "AMQP",
+        "18082": "murano"}
+
+    def setUp(self):
+        super(TestUrlParser, self).setUp()
+        self.test_object = subunit_describe_calls.UrlParser()
+
+    def test_get_service_default_ports(self):
+        base_url = "http://site.something.com:"
+        for port in self.test_object.services:
+            url = base_url + port + "/v2/action"
+            service = self.test_object.services[port]
+            self.assertEqual(service, self.test_object.get_service(url))
+
+    def test_get_service_custom_ports(self):
+        self.test_object = subunit_describe_calls.\
+            UrlParser(services=self.services_custom_ports)
+        base_url = "http://site.something.com:"
+        for port in self.services_custom_ports:
+            url = base_url + port + "/v2/action"
+            service = self.services_custom_ports[port]
+            self.assertEqual(service, self.test_object.get_service(url))
+
+    def test_get_service_port_not_found(self):
+        url = "https://site.somewhere.com:1234/v2/action"
+        self.assertEqual("Unknown", self.test_object.get_service(url))
+        self.assertEqual("Unknown", self.test_object.get_service(""))
+
+    def test_parse_details_none(self):
+        self.assertIsNone(self.test_object.parse_details(None))
+
+    def test_url_path_ports(self):
+        uuid_sample1 = "3715e0bb-b1b3-4291-aa13-2c86c3b9ec93"
+        uuid_sample2 = "2715e0bb-b1b4-4291-aa13-2c86c3b9ec88"
+
+        # test http url
+        host = "http://host.company.com"
+        url = host + ":8776/v3/" + uuid_sample1 + "/types/" + \
+            uuid_sample2 + "/extra_specs"
+        self.assertEqual("v3/<uuid>/types/<uuid>/extra_specs",
+                         self.test_object.url_path(url))
+        url = host + ":8774/v2.1/servers/" + uuid_sample1
+        self.assertEqual("v2.1/servers/<uuid>",
+                         self.test_object.url_path(url))
+        # test https url
+        host = "https://host.company.com"
+        url = host + ":8776/v3/" + uuid_sample1 + "/types/" + \
+            uuid_sample2 + "/extra_specs"
+        self.assertEqual("v3/<uuid>/types/<uuid>/extra_specs",
+                         self.test_object.url_path(url))
+        url = host + ":8774/v2.1/servers/" + uuid_sample1
+        self.assertEqual("v2.1/servers/<uuid>",
+                         self.test_object.url_path(url))
+
+    def test_url_path_no_match(self):
+        host_port = 'https://host.company.com:1234/'
+        url = 'v2/action/no/special/data'
+        self.assertEqual(url, self.test_object.url_path(host_port + url))
+        url = 'data'
+        self.assertEqual(url, self.test_object.url_path(url))
+
+
+class TestCliBase(base.TestCase):
+    """Base class for share code on all CLI sub-process testing"""
+
+    def setUp(self):
+        super(TestCliBase, self).setUp()
+        self._subunit_file = os.path.join(
             os.path.dirname(os.path.abspath(__file__)),
-            'sample_streams/calls.subunit')
+            'subunit_describe_calls_data', 'calls.subunit')
+
+    def _bytes_to_string(self, data):
+        if isinstance(data, (bytes, bytearray)):
+            data = str(data, 'utf-8')
+        return data
+
+    def _assert_cli_message(self, data):
+        data = self._bytes_to_string(data)
+        self.assertIn("Running subunit_describe_calls ...", data)
+
+    def _assert_deprecated_warning(self, stdout):
+        self.assertIn(
+            b"Use of: 'subunit-describe-calls' is deprecated, "
+            b"please use: 'tempest subunit-describe-calls'", stdout)
+
+    def _assert_expect_json(self, json_data):
+        expected_file_name = os.path.join(
+            os.path.dirname(os.path.abspath(__file__)),
+            'subunit_describe_calls_data', 'calls_subunit_expected.json')
+        with open(expected_file_name, "rb") as read_file:
+            expected_result = json.load(read_file)
+        self.assertDictEqual(expected_result, json_data)
+
+    def _assert_headers_and_bodies(self, data):
+        data = self._bytes_to_string(data)
+        self.assertIn('- request headers:', data)
+        self.assertIn('- request body:', data)
+        self.assertIn('- response headers:', data)
+        self.assertIn('- response body:', data)
+
+    def _assert_methods_details(self, data):
+        data = self._bytes_to_string(data)
+        self.assertIn('foo', data)
+        self.assertIn('- 200 POST request for Nova to v2.1/<id>/',
+                      data)
+        self.assertIn('- 200 DELETE request for Nova to v2.1/<id>/',
+                      data)
+        self.assertIn('- 200 GET request for Nova to v2.1/<id>/',
+                      data)
+        self.assertIn('- 404 DELETE request for Nova to v2.1/<id>/',
+                      data)
+
+    def _assert_mutual_exclusive_message(self, stderr):
+        self.assertIn(b"usage: subunit-describe-calls "
+                      b"[-h] [-s [<subunit file>]]", stderr)
+        self.assertIn(b"[-n <non subunit name>] [-o <output file>]",
+                      stderr)
+        self.assertIn(b"[-p <ports file>] [-v | -a]", stderr)
+        self.assertIn(
+            b"subunit-describe-calls: error: argument -v/--verbose: "
+            b"not allowed with argument -a/--all-stdout", stderr)
+
+    def _assert_no_headers_and_bodies(self, data):
+        data = self._bytes_to_string(data)
+        self.assertNotIn('- request headers:', data)
+        self.assertNotIn('- request body:', data)
+        self.assertNotIn('- response headers:', data)
+        self.assertNotIn('- response body:', data)
+
+
+class TestMainCli(TestCliBase):
+    """Test cases that use subunit_describe_calls module main interface
+
+    via subprocess calls to make sure the total user experience
+    is well defined and tested. This interface is deprecated.
+    Note: these test do not affect code coverage percentages.
+    """
+
+    def test_main_output_file(self):
+        temp_file = tempfile.mkstemp()[1]
         p = subprocess.Popen([
-            'subunit-describe-calls', '-s', subunit_file],
-            stdin=subprocess.PIPE, stdout=subprocess.PIPE)
-        stdout = p.communicate()
+            'subunit-describe-calls', '-s', self._subunit_file,
+            '-o', temp_file], stdin=subprocess.PIPE,
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        stdout, stderr = p.communicate()
         self.assertEqual(0, p.returncode)
-        self.assertIn(b'foo', stdout[0])
-        self.assertIn(b'- 200 POST request for Nova to v2.1/<id>/',
-                      stdout[0])
-        self.assertIn(b'- 200 DELETE request for Nova to v2.1/<id>/',
-                      stdout[0])
-        self.assertIn(b'- 200 GET request for Nova to v2.1/<id>/',
-                      stdout[0])
-        self.assertIn(b'- 404 DELETE request for Nova to v2.1/<id>/',
-                      stdout[0])
-        self.assertNotIn(b'- request headers:', stdout[0])
-        self.assertNotIn(b'- request body:', stdout[0])
-        self.assertNotIn(b'- response headers:', stdout[0])
-        self.assertNotIn(b'- response body:', stdout[0])
+        self._assert_cli_message(stdout)
+        self._assert_deprecated_warning(stdout)
+        with open(temp_file, 'r') as file:
+            data = json.loads(file.read())
+        self._assert_expect_json(data)
+
+    def test_main_verbose(self):
+        p = subprocess.Popen([
+            'subunit-describe-calls', '-s', self._subunit_file,
+            '-v'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE)
+        stdout, stderr = p.communicate()
+        self.assertEqual(0, p.returncode)
+        self._assert_cli_message(stdout)
+        self._assert_deprecated_warning(stdout)
+        self._assert_methods_details(stdout)
+        self._assert_headers_and_bodies(stdout)
+
+    def test_main_all_stdout(self):
+        p = subprocess.Popen([
+            'subunit-describe-calls', '-s', self._subunit_file,
+            '--all-stdout'], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE)
+        stdout, stderr = p.communicate()
+        self.assertEqual(0, p.returncode)
+        self._assert_cli_message(stdout)
+        self._assert_deprecated_warning(stdout)
+        self._assert_methods_details(stdout)
+        self._assert_headers_and_bodies(stdout)
+
+    def test_main(self):
+        p = subprocess.Popen([
+            'subunit-describe-calls', '-s', self._subunit_file],
+            stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE)
+        stdout, stderr = p.communicate()
+        self.assertEqual(0, p.returncode)
+        self._assert_cli_message(stdout)
+        self._assert_deprecated_warning(stdout)
+        self._assert_methods_details(stdout)
+        self._assert_no_headers_and_bodies(stdout)
+
+    def test_main_verbose_and_all_stdout(self):
+        p = subprocess.Popen([
+            'subunit-describe-calls', '-s', self._subunit_file,
+            '-a', '-v'],
+            stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE)
+        stdout, stderr = p.communicate()
+        self.assertEqual(2, p.returncode)
+        self._assert_cli_message(stdout)
+        self._assert_deprecated_warning(stdout)
+        self._assert_mutual_exclusive_message(stderr)
+
+
+class TestCli(TestCliBase):
+    """Test cases that use tempest subunit_describe_calls cliff interface
+
+    via subprocess calls to make sure the total user experience
+    is well defined and tested.
+    Note: these test do not affect code coverage percentages.
+    """
+
+    def _assert_cliff_verbose(self, stdout):
+        self.assertIn(b'tempest initialize_app', stdout)
+        self.assertIn(b'prepare_to_run_command TempestSubunitDescribeCalls',
+                      stdout)
+        self.assertIn(b'tempest clean_up TempestSubunitDescribeCalls',
+                      stdout)
+
+    def test_run_all_stdout(self):
+        p = subprocess.Popen(['tempest', 'subunit-describe-calls',
+                              '-s', self._subunit_file, '-a'],
+                             stdin=subprocess.PIPE,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.PIPE)
+        stdout, stderr = p.communicate()
+        self.assertEqual(0, p.returncode)
+        self._assert_cli_message(stdout)
+        self._assert_methods_details(stdout)
+        self._assert_headers_and_bodies(stdout)
+
+    def test_run_verbose(self):
+        p = subprocess.Popen(['tempest', 'subunit-describe-calls',
+                              '-s', self._subunit_file, '-v'],
+                             stdin=subprocess.PIPE,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.PIPE)
+        stdout, stderr = p.communicate()
+        self.assertEqual(0, p.returncode)
+        self._assert_cli_message(stdout)
+        self._assert_methods_details(stdout)
+        self._assert_no_headers_and_bodies(stdout)
+        self._assert_cliff_verbose(stderr)
+
+    def test_run_min(self):
+        p = subprocess.Popen(['tempest', 'subunit-describe-calls',
+                              '-s', self._subunit_file],
+                             stdin=subprocess.PIPE,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.PIPE)
+        stdout, stderr = p.communicate()
+        self.assertEqual(0, p.returncode)
+        self._assert_cli_message(stdout)
+        self._assert_methods_details(stdout)
+        self._assert_no_headers_and_bodies(stdout)
+
+    def test_run_verbose_all_stdout(self):
+        """Test Cliff -v argument
+
+        Since Cliff framework has a argument at the
+        abstract command level the -v or --verbose for
+        this command is not processed as a boolean.
+        So the use of verbose only exists for the
+        deprecated main CLI interface.  When the
+        main is deleted this test would not be needed.
+        """
+        p = subprocess.Popen(['tempest', 'subunit-describe-calls',
+                              '-s', self._subunit_file, '-a', '-v'],
+                             stdin=subprocess.PIPE,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.PIPE)
+        stdout, stderr = p.communicate()
+        self.assertEqual(0, p.returncode)
+        self._assert_cli_message(stdout)
+        self._assert_cliff_verbose(stderr)
+        self._assert_methods_details(stdout)
+
+
+class TestSubunitDescribeCalls(TestCliBase):
+    """Test cases use the subunit_describe_calls module interface
+
+    and effect code coverage reporting
+    """
+
+    def setUp(self):
+        super(TestSubunitDescribeCalls, self).setUp()
+        self.test_object = subunit_describe_calls.TempestSubunitDescribeCalls(
+            app=mock.Mock(),
+            app_args=mock.Mock(spec=argparse.Namespace))
 
     def test_parse(self):
-        subunit_file = os.path.join(
-            os.path.dirname(os.path.abspath(__file__)),
-            'sample_streams/calls.subunit')
-        parser = subunit_describe_calls.parse(
-            open(subunit_file), "pythonlogging", None)
-        expected_result = {
-            'bar': [{
-                'name': 'AgentsAdminTestJSON:setUp',
-                'request_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
-                '"hypervisor": "common", "md5hash": '
-                '"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
-                '"architecture": "tempest-x86_64-424013832", "os": "linux"}}',
-                'request_headers': "{'Content-Type': 'application/json', "
-                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
-                'response_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
-                '"hypervisor": "common", "md5hash": '
-                '"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
-                '"architecture": "tempest-x86_64-424013832", "os": "linux", '
-                '"agent_id": 1}}',
-                'response_headers': "{'status': '200', 'content-length': "
-                "'203', 'x-compute-request-id': "
-                "'req-25ddaae2-0ef1-40d1-8228-59bd64a7e75b', 'vary': "
-                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
-                "'x-openstack-nova-api-version': '2.1', 'date': "
-                "'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': "
-                "'application/json'}",
-                'service': 'Nova',
-                'status_code': '200',
-                'url': 'v2.1/<id>/os-agents',
-                'verb': 'POST'}, {
-                'name': 'AgentsAdminTestJSON:test_create_agent',
-                'request_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
-                '"hypervisor": "kvm", "md5hash": '
-                '"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
-                '"architecture": "tempest-x86-252246646", "os": "win"}}',
-                'request_headers': "{'Content-Type': 'application/json', "
-                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
-                'response_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
-                '"hypervisor": "kvm", "md5hash": '
-                '"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
-                '"architecture": "tempest-x86-252246646", "os": "win", '
-                '"agent_id": 2}}',
-                'response_headers': "{'status': '200', 'content-length': "
-                "'195', 'x-compute-request-id': "
-                "'req-b4136f06-c015-4e7e-995f-c43831e3ecce', 'vary': "
-                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
-                "'x-openstack-nova-api-version': '2.1', 'date': "
-                "'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': "
-                "'application/json'}",
-                'service': 'Nova',
-                'status_code': '200',
-                'url': 'v2.1/<id>/os-agents',
-                'verb': 'POST'}, {
-                'name': 'AgentsAdminTestJSON:tearDown',
-                'request_body': 'None',
-                'request_headers': "{'Content-Type': 'application/json', "
-                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
-                'response_body': '',
-                'response_headers': "{'status': '200', 'content-length': "
-                "'0', 'x-compute-request-id': "
-                "'req-ee905fd6-a5b5-4da4-8c37-5363cb25bd9d', 'vary': "
-                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
-                "'x-openstack-nova-api-version': '2.1', 'date': "
-                "'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': "
-                "'application/json'}",
-                'service': 'Nova',
-                'status_code': '200',
-                'url': 'v2.1/<id>/os-agents/1',
-                'verb': 'DELETE'}, {
-                'name': 'AgentsAdminTestJSON:_run_cleanups',
-                'request_body': 'None',
-                'request_headers': "{'Content-Type': 'application/json', "
-                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
-                'response_headers': "{'status': '200', 'content-length': "
-                "'0', 'x-compute-request-id': "
-                "'req-e912cac0-63e0-4679-a68a-b6d18ddca074', 'vary': "
-                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
-                "'x-openstack-nova-api-version': '2.1', 'date': "
-                "'Tue, 02 Feb 2016 03:27:00 GMT', 'content-type': "
-                "'application/json'}",
-                'service': 'Nova',
-                'status_code': '200',
-                'url': 'v2.1/<id>/os-agents/2',
-                'verb': 'DELETE'}],
-            'foo': [{
-                'name': 'AgentsAdminTestJSON:setUp',
-                'request_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
-                '"hypervisor": "common", "md5hash": '
-                '"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
-                '"architecture": "tempest-x86_64-948635295", "os": "linux"}}',
-                'request_headers': "{'Content-Type': 'application/json', "
-                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
-                'response_body': '{"agent": {"url": "xxx://xxxx/xxx/xxx", '
-                '"hypervisor": "common", "md5hash": '
-                '"add6bb58e139be103324d04d82d8f545", "version": "7.0", '
-                '"architecture": "tempest-x86_64-948635295", "os": "linux", '
-                '"agent_id": 3}}',
-                'response_headers': "{'status': '200', 'content-length': "
-                "'203', 'x-compute-request-id': "
-                "'req-ccd2116d-04b1-4ffe-ae32-fb623f68bf1c', 'vary': "
-                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
-                "'x-openstack-nova-api-version': '2.1', 'date': "
-                "'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': "
-                "'application/json'}",
-                'service': 'Nova',
-                'status_code': '200',
-                'url': 'v2.1/<id>/os-agents',
-                'verb': 'POST'}, {
-                'name': 'AgentsAdminTestJSON:test_delete_agent',
-                'request_body': 'None',
-                'request_headers': "{'Content-Type': 'application/json', "
-                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
-                'response_body': '',
-                'response_headers': "{'status': '200', 'content-length': "
-                "'0', 'x-compute-request-id': "
-                "'req-6e7fa28f-ae61-4388-9a78-947c58bc0588', 'vary': "
-                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
-                "'x-openstack-nova-api-version': '2.1', 'date': "
-                "'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': "
-                "'application/json'}",
-                'service': 'Nova',
-                'status_code': '200',
-                'url': 'v2.1/<id>/os-agents/3',
-                'verb': 'DELETE'}, {
-                'name': 'AgentsAdminTestJSON:test_delete_agent',
-                'request_body': 'None',
-                'request_headers': "{'Content-Type': 'application/json', "
-                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
-                'response_body': '{"agents": []}',
-                'response_headers': "{'status': '200', 'content-length': "
-                "'14', 'content-location': "
-                "'http://23.253.76.97:8774/v2.1/"
-                "cf6b1933fe5b476fbbabb876f6d1b924/os-agents', "
-                "'x-compute-request-id': "
-                "'req-e41aa9b4-41a6-4138-ae04-220b768eb644', 'vary': "
-                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
-                "'x-openstack-nova-api-version': '2.1', 'date': "
-                "'Tue, 02 Feb 2016 03:27:01 GMT', 'content-type': "
-                "'application/json'}",
-                'service': 'Nova',
-                'status_code': '200',
-                'url': 'v2.1/<id>/os-agents',
-                'verb': 'GET'}, {
-                'name': 'AgentsAdminTestJSON:tearDown',
-                'request_body': 'None',
-                'request_headers': "{'Content-Type': 'application/json', "
-                "'Accept': 'application/json', 'X-Auth-Token': '<omitted>'}",
-                'response_headers': "{'status': '404', 'content-length': "
-                "'82', 'x-compute-request-id': "
-                "'req-e297aeea-91cf-4f26-b49c-8f46b1b7a926', 'vary': "
-                "'X-OpenStack-Nova-API-Version', 'connection': 'close', "
-                "'x-openstack-nova-api-version': '2.1', 'date': "
-                "'Tue, 02 Feb 2016 03:27:02 GMT', 'content-type': "
-                "'application/json; charset=UTF-8'}",
-                'service': 'Nova',
-                'status_code': '404',
-                'url': 'v2.1/<id>/os-agents/3',
-                'verb': 'DELETE'}]}
+        with open(self._subunit_file, 'r') as read_file:
+            parser = subunit_describe_calls.parse(
+                read_file, "pythonlogging", None)
+        self._assert_expect_json(parser.test_logs)
 
-        self.assertEqual(expected_result, parser.test_logs)
+    def test_get_description(self):
+        self.assertEqual(subunit_describe_calls.DESCRIPTION,
+                         self.test_object.get_description())
+
+    def test_get_parser_default_min(self):
+        parser = self.test_object.get_parser('NAME')
+        parsed_args = parser.parse_args([])
+        self.assertIsNone(parsed_args.output_file)
+        self.assertIsNone(parsed_args.ports)
+        self.assertFalse(parsed_args.all_stdout)
+        self.assertEqual(parsed_args.subunit, sys.stdin)
+
+    def test_get_parser_default_max(self):
+        temp_dir = tempfile.mkdtemp(prefix="parser")
+        self.addCleanup(shutil.rmtree, temp_dir, ignore_errors=True)
+        outfile_name = os.path.join(temp_dir, 'output.json')
+        open(outfile_name, 'a').close()
+        portfile_name = os.path.join(temp_dir, 'ports.json')
+        open(portfile_name, 'a').close()
+
+        parser = self.test_object.get_parser('NAME')
+        parsed_args = parser.parse_args(["-a", "-o " + outfile_name,
+                                         "-p " + portfile_name])
+
+        self.assertIsNotNone(parsed_args.output_file)
+        self.assertIsNotNone(parsed_args.ports)
+        self.assertTrue(parsed_args.all_stdout)
+        self.assertEqual(parsed_args.subunit, sys.stdin)
+
+    def test_take_action_min(self):
+        parser = self.test_object.get_parser('NAME')
+        parsed_args = parser.parse_args(["-s" + self._subunit_file],)
+        with patch('sys.stdout', new=StringIO()) as mock_stdout:
+            self.test_object.take_action(parsed_args)
+
+        stdout_data = mock_stdout.getvalue()
+        self._assert_methods_details(stdout_data)
+        self._assert_no_headers_and_bodies(stdout_data)
+
+    def test_take_action_all_stdout(self):
+        parser = self.test_object.get_parser('NAME')
+        parsed_args = parser.parse_args(["-as" + self._subunit_file],)
+        with patch('sys.stdout', new=StringIO()) as mock_stdout:
+            self.test_object.take_action(parsed_args)
+
+        stdout_data = mock_stdout.getvalue()
+        self._assert_methods_details(stdout_data)
+        self._assert_headers_and_bodies(stdout_data)
+
+    def test_take_action_outfile_files(self):
+        temp_file = tempfile.mkstemp()[1]
+        parser = self.test_object.get_parser('NAME')
+        parsed_args = parser.parse_args(
+            ["-as" + self._subunit_file, '-o', temp_file], )
+        with patch('sys.stdout', new=StringIO()) as mock_stdout:
+            self.test_object.take_action(parsed_args)
+        stdout_data = mock_stdout.getvalue()
+        self._assert_cli_message(stdout_data)
+        with open(temp_file, 'r') as file:
+            data = json.loads(file.read())
+        self._assert_expect_json(data)
+
+    def test_take_action_no_items(self):
+        temp_file = tempfile.mkstemp()[1]
+        parser = self.test_object.get_parser('NAME')
+        parsed_args = parser.parse_args(
+            ["-as" + temp_file], )
+        with patch('sys.stdout', new=StringIO()) as mock_stdout:
+            self.test_object.take_action(parsed_args)
+        stdout_data = mock_stdout.getvalue()
+        self._assert_cli_message(stdout_data)
+
+    def test_take_action_exception(self):
+        parser = self.test_object.get_parser('NAME')
+        parsed_args = parser.parse_args(["-s" + self._subunit_file],)
+        with patch('sys.stderr', new=StringIO()) as mock_stderr:
+            with patch('tempest.cmd.subunit_describe_calls.entry_point') \
+                    as mock_method:
+                mock_method.side_effect = OSError()
+                self.assertRaises(OSError, self.test_object.take_action,
+                                  parsed_args)
+                stderr_data = mock_stderr.getvalue()
+
+        self.assertIn("Traceback (most recent call last):", stderr_data)
+        self.assertIn("entry_point(parsed_args)", stderr_data)
diff --git a/tempest/tests/cmd/test_tempest_init.py b/tempest/tests/cmd/test_tempest_init.py
index 9042b12..fce0882 100644
--- a/tempest/tests/cmd/test_tempest_init.py
+++ b/tempest/tests/cmd/test_tempest_init.py
@@ -40,7 +40,7 @@
 
     def test_generate_sample_config(self):
         local_dir = self.useFixture(fixtures.TempDir())
-        etc_dir_path = os.path.join(local_dir.path, 'etc/')
+        etc_dir_path = os.path.join(local_dir.path, 'etc')
         os.mkdir(etc_dir_path)
         init_cmd = init.TempestInit(None, None)
         local_sample_conf_file = os.path.join(etc_dir_path,
@@ -56,7 +56,7 @@
 
     def test_update_local_conf(self):
         local_dir = self.useFixture(fixtures.TempDir())
-        etc_dir_path = os.path.join(local_dir.path, 'etc/')
+        etc_dir_path = os.path.join(local_dir.path, 'etc')
         os.mkdir(etc_dir_path)
         lock_dir = os.path.join(local_dir.path, 'tempest_lock')
         config_path = os.path.join(etc_dir_path, 'tempest.conf')
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 721fd76..277e049 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -579,7 +579,7 @@
                           os, 'fakeservice')
 
     def test_get_config_file(self):
-        conf_dir = os.path.join(os.getcwd(), 'etc/')
+        conf_dir = os.path.join(os.getcwd(), 'etc')
         conf_file = "tempest.conf.sample"
         local_sample_conf_file = os.path.join(conf_dir, conf_file)
 
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index 5f8b990..73924bd 100755
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -234,6 +234,29 @@
                                     mock.call(volume_id)])
         mock_sleep.assert_called_once_with(1)
 
+    def test_wait_for_volume_attachment_create(self):
+        vol_detached = {'volume': {'attachments': []}}
+        vol_attached = {'volume': {'attachments': [
+                       {'id': uuids.volume_id,
+                        'attachment_id': uuids.attachment_id,
+                        'server_id': uuids.server_id,
+                        'volume_id': uuids.volume_id}]}}
+        show_volume = mock.MagicMock(side_effect=[
+            vol_detached, vol_detached, vol_attached])
+        client = mock.Mock(spec=volumes_client.VolumesClient,
+                           build_interval=1,
+                           build_timeout=5,
+                           show_volume=show_volume)
+        self.patch('time.time')
+        self.patch('time.sleep')
+        att = waiters.wait_for_volume_attachment_create(
+            client, uuids.volume_id, uuids.server_id)
+        assert att == vol_attached['volume']['attachments'][0]
+        # Assert that show volume is called until the attachment is removed.
+        show_volume.assert_has_calls([mock.call(uuids.volume_id),
+                                      mock.call(uuids.volume_id),
+                                      mock.call(uuids.volume_id)])
+
     def test_wait_for_volume_attachment(self):
         vol_detached = {'volume': {'attachments': []}}
         vol_attached = {'volume': {'attachments': [
@@ -249,9 +272,9 @@
         waiters.wait_for_volume_attachment_remove(client, uuids.volume_id,
                                                   uuids.attachment_id)
         # Assert that show volume is called until the attachment is removed.
-        show_volume.assert_has_calls = [mock.call(uuids.volume_id),
-                                        mock.call(uuids.volume_id),
-                                        mock.call(uuids.volume_id)]
+        show_volume.assert_has_calls([mock.call(uuids.volume_id),
+                                      mock.call(uuids.volume_id),
+                                      mock.call(uuids.volume_id)])
 
     def test_wait_for_volume_attachment_timeout(self):
         show_volume = mock.MagicMock(return_value={
diff --git a/tempest/tests/lib/cmd/test_check_uuid.py b/tempest/tests/lib/cmd/test_check_uuid.py
index 130f90a..28ebca1 100644
--- a/tempest/tests/lib/cmd/test_check_uuid.py
+++ b/tempest/tests/lib/cmd/test_check_uuid.py
@@ -11,13 +11,56 @@
 # under the License.
 
 import importlib
+import os
+import sys
 import tempfile
 from unittest import mock
 
+import fixtures
+
 from tempest.lib.cmd import check_uuid
 from tempest.tests import base
 
 
+class TestCLInterface(base.TestCase):
+    CODE = "import unittest\n" \
+           "class TestClass(unittest.TestCase):\n" \
+           "    def test_tests(self):\n" \
+           "        pass"
+
+    def create_tests_file(self, directory):
+        with open(directory + "/__init__.py", "w"):
+            pass
+
+        tests_file = directory + "/tests.py"
+        with open(tests_file, "w") as fake_file:
+            fake_file.write(TestCLInterface.CODE)
+
+        return tests_file
+
+    def test_fix_argument_no(self):
+        temp_dir = self.useFixture(fixtures.TempDir(rootdir="."))
+        tests_file = self.create_tests_file(temp_dir.path)
+
+        sys.argv = [sys.argv[0]] + ["--package",
+                                    os.path.relpath(temp_dir.path)]
+
+        self.assertRaises(SystemExit, check_uuid.run)
+        with open(tests_file, "r") as f:
+            self.assertTrue(TestCLInterface.CODE == f.read())
+
+    def test_fix_argument_yes(self):
+        temp_dir = self.useFixture(fixtures.TempDir(rootdir="."))
+        tests_file = self.create_tests_file(temp_dir.path)
+
+        sys.argv = [sys.argv[0]] + ["--fix", "--package",
+                                    os.path.relpath(temp_dir.path)]
+
+        check_uuid.run()
+        with open(tests_file, "r") as f:
+            self.assertTrue(TestCLInterface.CODE != f.read())
+
+
 class TestSourcePatcher(base.TestCase):
     def test_add_patch(self):
         patcher = check_uuid.SourcePatcher()
diff --git a/tempest/tests/lib/services/volume/v3/test_backups_client.py b/tempest/tests/lib/services/volume/v3/test_backups_client.py
index 97e1132..ca7918a 100644
--- a/tempest/tests/lib/services/volume/v3/test_backups_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_backups_client.py
@@ -45,6 +45,8 @@
                 "availability_zone": "az1",
                 "container": "volumebackups",
                 "created_at": "2013-04-02T10:35:27.000000",
+                "updated_at": "2013-04-02T10:39:27.000000",
+                "data_timestamp": "2013-04-02T10:35:27.000000",
                 "description": None,
                 "fail_reason": None,
                 "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
@@ -64,7 +66,6 @@
                 "user_id": "515ba0dd59f84f25a6a084a45d8d93b2",
                 "size": 1,
                 "status": "available",
-                "updated_at": "2013-04-02T10:35:27.000000",
                 "volume_id": "e5185058-943a-4cb4-96d9-72c184c337d6",
                 "is_incremental": True,
                 "has_dependent_backups": False
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 d4313a2..3d47caf 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
@@ -54,7 +54,6 @@
                 }
             ],
             "availability_zone": "nova",
-            "os-vol-host-attr:host": "controller1@rbd#rbd",
             "encrypted": False,
             "updated_at": None,
             "replication_status": None,
@@ -62,15 +61,12 @@
             "id": "c07cd4a4-b52b-4511-a176-fbaa2011a227",
             "size": 0,
             "user_id": "142d8663efce464c89811c63e45bd82e",
-            "os-vol-tenant-attr:tenant_id": "f21a9c86d7114bf99c711f4874d80474",
-            "os-vol-mig-status-attr:migstat": None,
             "metadata": {},
             "status": "creating",
             "description": "volume-manage-description",
             "multiattach": False,
             "source_volid": None,
             "consistencygroup_id": None,
-            "os-vol-mig-status-attr:name_id": None,
             "name": "volume-managed",
             "bootable": "false",
             "created_at": "2017-07-11T09:14:01.000000",
diff --git a/tempest/tests/lib/services/volume/v3/test_volumes_client.py b/tempest/tests/lib/services/volume/v3/test_volumes_client.py
index 56c1a35..6bd75d9 100644
--- a/tempest/tests/lib/services/volume/v3/test_volumes_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_volumes_client.py
@@ -26,10 +26,6 @@
         "volume-summary": {
             "total_size": 4,
             "total_count": 4,
-            "metadata": {
-                "key1": ["value1", "value2"],
-                "key2": ["value2"]
-            }
         }
     }
 
diff --git a/tools/generate-tempest-plugins-list.py b/tools/generate-tempest-plugins-list.py
index 530ce5e..618c388 100644
--- a/tools/generate-tempest-plugins-list.py
+++ b/tools/generate-tempest-plugins-list.py
@@ -41,7 +41,7 @@
     'x/intel-nfv-ci-tests',  # https://review.opendev.org/#/c/634640/
     'openstack/networking-generic-switch',
     # https://review.opendev.org/#/c/634846/
-    'openstack/networking-l2gw-tempest-plugin',
+    'x/networking-l2gw-tempest-plugin',
     # https://review.opendev.org/#/c/635093/
     'openstack/networking-midonet',  # https://review.opendev.org/#/c/635096/
     'x/networking-plumgrid',  # https://review.opendev.org/#/c/635096/
diff --git a/tox.ini b/tox.ini
index 0477d6f..031a400 100644
--- a/tox.ini
+++ b/tox.ini
@@ -282,15 +282,31 @@
   -r{toxinidir}/requirements.txt
   -r{toxinidir}/doc/requirements.txt
 commands =
+  sphinx-apidoc -f -o doc/source/tests/compute tempest/api/compute
+  sphinx-apidoc -f -o doc/source/tests/identity tempest/api/identity
+  sphinx-apidoc -f -o doc/source/tests/image tempest/api/image
+  sphinx-apidoc -f -o doc/source/tests/network tempest/api/network
+  sphinx-apidoc -f -o doc/source/tests/object_storage tempest/api/object_storage
+  sphinx-apidoc -f -o doc/source/tests/scenario tempest/scenario
+  sphinx-apidoc -f -o doc/source/tests/volume tempest/api/volume
   rm -rf doc/build
   sphinx-build -W -b html doc/source doc/build/html
-whitelist_externals = rm
+whitelist_externals =
+    rm
 
 [testenv:pdf-docs]
 deps = {[testenv:docs]deps}
 whitelist_externals =
+   rm
    make
 commands =
+   sphinx-apidoc -f -o doc/source/tests/compute tempest/api/compute
+   sphinx-apidoc -f -o doc/source/tests/identity tempest/api/identity
+   sphinx-apidoc -f -o doc/source/tests/image tempest/api/image
+   sphinx-apidoc -f -o doc/source/tests/network tempest/api/network
+   sphinx-apidoc -f -o doc/source/tests/object_storage tempest/api/object_storage
+   sphinx-apidoc -f -o doc/source/tests/scenario tempest/scenario
+   sphinx-apidoc -f -o doc/source/tests/volume tempest/api/volume
    sphinx-build -W -b latex doc/source doc/build/pdf
    make -C doc/build/pdf