Merge "Revert "Use stable constraint in tox to release new tag for Victoria""
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/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/roles/process-stackviz/README.rst b/roles/process-stackviz/README.rst
deleted file mode 100644
index a8447d2..0000000
--- a/roles/process-stackviz/README.rst
+++ /dev/null
@@ -1,22 +0,0 @@
-Generate stackviz report.
-
-Generate stackviz report using subunit and dstat data, using
-the stackviz archive embedded in test images.
-
-**Role Variables**
-
-.. zuul:rolevar:: devstack_base_dir
-   :default: /opt/stack
-
-   The devstack base directory.
-
-.. zuul:rolevar:: stage_dir
-   :default: "{{ ansible_user_dir }}"
-
-   The stage directory where the input data can be found and
-   the output will be produced.
-
-.. zuul:rolevar:: zuul_work_dir
-   :default: {{ devstack_base_dir }}/tempest
-
-   Directory to work in. It has to be a fully qualified path.
diff --git a/roles/process-stackviz/defaults/main.yaml b/roles/process-stackviz/defaults/main.yaml
deleted file mode 100644
index f3bc32b..0000000
--- a/roles/process-stackviz/defaults/main.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-devstack_base_dir: /opt/stack
-stage_dir: "{{ ansible_user_dir }}"
-zuul_work_dir: "{{ devstack_base_dir }}/tempest"
diff --git a/roles/process-stackviz/tasks/main.yaml b/roles/process-stackviz/tasks/main.yaml
deleted file mode 100644
index e3a0a0e..0000000
--- a/roles/process-stackviz/tasks/main.yaml
+++ /dev/null
@@ -1,70 +0,0 @@
-- name: Check if stackviz archive exists
-  stat:
-    path: "/opt/cache/files/stackviz-latest.tar.gz"
-  register: stackviz_archive
-
-- debug:
-    msg: "Stackviz archive could not be found in /opt/cache/files/stackviz-latest.tar.gz"
-  when: not stackviz_archive.stat.exists
-
-- name: Check if subunit data exists
-  stat:
-    path: "{{ zuul_work_dir }}/testrepository.subunit"
-  register: subunit_input
-
-- debug:
-    msg: "Subunit file could not be found at {{ zuul_work_dir }}/testrepository.subunit"
-  when: not subunit_input.stat.exists
-
-- name: Install stackviz
-  when:
-    - stackviz_archive.stat.exists
-    - subunit_input.stat.exists
-  block:
-    - include_role:
-        name: ensure-pip
-
-    - pip:
-        name: "file://{{ stackviz_archive.stat.path }}"
-        virtualenv: /tmp/stackviz
-        virtualenv_command: '{{ ensure_pip_virtualenv_command }}'
-        extra_args: -U
-
-- name: Deploy stackviz static html+js
-  command: cp -pR /tmp/stackviz/share/stackviz-html {{ stage_dir }}/stackviz
-  when:
-    - stackviz_archive.stat.exists
-    - subunit_input.stat.exists
-
-- name: Check if dstat data exists
-  stat:
-    path: "{{ devstack_base_dir }}/logs/dstat-csv.log"
-  register: dstat_input
-  when:
-    - stackviz_archive.stat.exists
-    - subunit_input.stat.exists
-
-- name: Run stackviz with dstat
-  shell: |
-    cat {{ subunit_input.stat.path }} | \
-      /tmp/stackviz/bin/stackviz-export \
-        --dstat "{{ devstack_base_dir }}/logs/dstat-csv.log" \
-        --env --stdin \
-        {{ stage_dir }}/stackviz/data
-  when:
-    - stackviz_archive.stat.exists
-    - subunit_input.stat.exists
-    - dstat_input.stat.exists
-  failed_when: False
-
-- name: Run stackviz without dstat
-  shell: |
-    cat {{ subunit_input.stat.path }} | \
-      /tmp/stackviz/bin/stackviz-export \
-        --env --stdin \
-        {{ stage_dir }}/stackviz/data
-  when:
-    - stackviz_archive.stat.exists
-    - subunit_input.stat.exists
-    - not dstat_input.stat.exists
-  failed_when: False
diff --git a/tempest/api/compute/admin/test_agents.py b/tempest/api/compute/admin/test_agents.py
index 0414b91..4cc5fdd 100644
--- a/tempest/api/compute/admin/test_agents.py
+++ b/tempest/api/compute/admin/test_agents.py
@@ -13,14 +13,24 @@
 #    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 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):
         super(AgentsAdminTestJSON, cls).setup_clients()
         cls.client = cls.os_admin.agents_client
diff --git a/tempest/api/compute/admin/test_instance_usage_audit_log.py b/tempest/api/compute/admin/test_instance_usage_audit_log.py
index 1b62249..4dcbb3b 100644
--- a/tempest/api/compute/admin/test_instance_usage_audit_log.py
+++ b/tempest/api/compute/admin/test_instance_usage_audit_log.py
@@ -22,6 +22,7 @@
 
 
 class InstanceUsageAuditLogTestJSON(base.BaseV2ComputeAdminTest):
+    """Test instance usage audit logs API"""
 
     @classmethod
     def setup_clients(cls):
@@ -30,12 +31,12 @@
 
     @decorators.idempotent_id('25319919-33d9-424f-9f99-2c203ee48b9d')
     def test_list_instance_usage_audit_logs(self):
-        # list instance usage audit logs
+        """Test listing instance usage audit logs"""
         self.adm_client.list_instance_usage_audit_logs()
 
     @decorators.idempotent_id('6e40459d-7c5f-400b-9e83-449fbc8e7feb')
     def test_get_instance_usage_audit_log(self):
-        # Get instance usage audit log before specified time
+        """Test getting instance usage audit log before specified time"""
         now = datetime.datetime.now()
         self.adm_client.show_instance_usage_audit_log(
             urllib.quote(now.strftime("%Y-%m-%d %H:%M:%S")))
diff --git a/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py b/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
index de8e221..84d18c4 100644
--- a/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
+++ b/tempest/api/compute/admin/test_instance_usage_audit_log_negative.py
@@ -23,6 +23,7 @@
 
 
 class InstanceUsageAuditLogNegativeTestJSON(base.BaseV2ComputeAdminTest):
+    """Negative tests of instance usage audit logs"""
 
     @classmethod
     def setup_clients(cls):
@@ -32,7 +33,10 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('a9d33178-d2c9-4131-ad3b-f4ca8d0308a2')
     def test_instance_usage_audit_logs_with_nonadmin_user(self):
-        # the instance_usage_audit_logs API just can be accessed by admin user
+        """Test list/show instance usage audit logs by non-admin should fail
+
+        The instance_usage_audit_logs API just can be accessed by admin user.
+        """
         self.assertRaises(lib_exc.Forbidden,
                           self.instance_usages_audit_log_client.
                           list_instance_usage_audit_logs)
@@ -45,6 +49,10 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9b952047-3641-41c7-ba91-a809fc5974c8')
     def test_get_instance_usage_audit_logs_with_invalid_time(self):
+        """Test showing instance usage audit logs with invalid time
+
+        Showing instance usage audit logs with invalid time should fail.
+        """
         self.assertRaises(lib_exc.BadRequest,
                           self.adm_client.show_instance_usage_audit_log,
                           "invalid_time")
diff --git a/tempest/api/compute/admin/test_security_groups.py b/tempest/api/compute/admin/test_security_groups.py
index dfa801b..f0a6a84 100644
--- a/tempest/api/compute/admin/test_security_groups.py
+++ b/tempest/api/compute/admin/test_security_groups.py
@@ -20,6 +20,12 @@
 
 
 class SecurityGroupsTestAdminJSON(base.BaseV2ComputeAdminTest):
+    """Test security groups API that requires admin privilege
+
+    Test security groups API that requires admin privilege with compute
+    microversion less than 2.36
+    """
+
     max_microversion = '2.35'
 
     @classmethod
@@ -37,7 +43,17 @@
     @decorators.idempotent_id('49667619-5af9-4c63-ab5d-2cfdd1c8f7f1')
     @utils.services('network')
     def test_list_security_groups_list_all_tenants_filter(self):
-        # Admin can list security groups of all tenants
+        """Test listing security groups with all_tenants filter
+
+        1. Create two security groups for non-admin user
+        2. Create two security groups for admin user
+        3. Fetch all security groups based on 'all_tenants' search filter by
+           admin, check that all four created security groups are present in
+           fetched list
+        4. Fetch all security groups based on 'all_tenants' search filter by
+           non-admin, check only two security groups created by the provided
+           non-admin user are present in fetched list
+        """
         # List of all security groups created
         security_group_list = []
         # Create two security groups for a non-admin tenant
diff --git a/tempest/api/compute/admin/test_server_diagnostics.py b/tempest/api/compute/admin/test_server_diagnostics.py
index 005efdd..d855a62 100644
--- a/tempest/api/compute/admin/test_server_diagnostics.py
+++ b/tempest/api/compute/admin/test_server_diagnostics.py
@@ -19,6 +19,8 @@
 
 
 class ServerDiagnosticsTest(base.BaseV2ComputeAdminTest):
+    """Test server diagnostics with compute microversion less than 2.48"""
+
     min_microversion = None
     max_microversion = '2.47'
 
@@ -29,6 +31,7 @@
 
     @decorators.idempotent_id('31ff3486-b8a0-4f56-a6c0-aab460531db3')
     def test_get_server_diagnostics(self):
+        """Test getting server diagnostics"""
         server_id = self.create_test_server(wait_until='ACTIVE')['id']
         diagnostics = self.client.show_server_diagnostics(server_id)
 
@@ -41,6 +44,8 @@
 
 
 class ServerDiagnosticsV248Test(base.BaseV2ComputeAdminTest):
+    """Test server diagnostics with compute microversion greater than 2.47"""
+
     min_microversion = '2.48'
     max_microversion = 'latest'
 
@@ -51,6 +56,7 @@
 
     @decorators.idempotent_id('64d0d48c-dff1-11e6-bf01-fe55135034f3')
     def test_get_server_diagnostics(self):
+        """Test getting server diagnostics"""
         server_id = self.create_test_server(wait_until='ACTIVE')['id']
         # Response status and filed types will be checked by json schema
         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 530c8fb..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"""
@@ -201,6 +204,10 @@
     @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'])
diff --git a/tempest/api/compute/admin/test_volume_swap.py b/tempest/api/compute/admin/test_volume_swap.py
index edcb1a7..c1236a7 100644
--- a/tempest/api/compute/admin/test_volume_swap.py
+++ b/tempest/api/compute/admin/test_volume_swap.py
@@ -68,21 +68,7 @@
 
 
 class TestVolumeSwap(TestVolumeSwapBase):
-    """The test suite for swapping of volume with admin user.
-
-    The following is the scenario outline:
-
-    1. Create a volume "volume1" with non-admin.
-    2. Create a volume "volume2" with non-admin.
-    3. Boot an instance "instance1" with non-admin.
-    4. Attach "volume1" to "instance1" with non-admin.
-    5. Swap volume from "volume1" to "volume2" as admin.
-    6. Check the swap volume is successful and "volume2"
-       is attached to "instance1" and "volume1" is in available state.
-    7. Swap volume from "volume2" to "volume1" as admin.
-    8. Check the swap volume is successful and "volume1"
-       is attached to "instance1" and "volume2" is in available state.
-    """
+    """The test suite for swapping of volume with admin user"""
 
     # NOTE(mriedem): This is an uncommon scenario to call the compute API
     # to swap volumes directly; swap volume is primarily only for volume
@@ -92,6 +78,21 @@
     @decorators.idempotent_id('1769f00d-a693-4d67-a631-6a3496773813')
     @utils.services('volume')
     def test_volume_swap(self):
+        """Test swapping of volume attached to server with admin user
+
+        The following is the scenario outline:
+
+        1. Create a volume "volume1" with non-admin.
+        2. Create a volume "volume2" with non-admin.
+        3. Boot an instance "instance1" with non-admin.
+        4. Attach "volume1" to "instance1" with non-admin.
+        5. Swap volume from "volume1" to "volume2" as admin.
+        6. Check the swap volume is successful and "volume2"
+           is attached to "instance1" and "volume1" is in available state.
+        7. Swap volume from "volume2" to "volume1" as admin.
+        8. Check the swap volume is successful and "volume1"
+           is attached to "instance1" and "volume2" is in available state.
+        """
         # Create two volumes.
         # NOTE(gmann): Volumes are created before server creation so that
         # volumes cleanup can happen successfully irrespective of which volume
@@ -134,6 +135,12 @@
 
 
 class TestMultiAttachVolumeSwap(TestVolumeSwapBase):
+    """Test swapping volume attached to multiple servers
+
+    Test swapping volume attached to multiple servers with microversion
+    greater than 2.59
+    """
+
     min_microversion = '2.60'
     max_microversion = 'latest'
 
@@ -164,6 +171,20 @@
                              condition=CONF.compute.min_compute_nodes > 1)
     @utils.services('volume')
     def test_volume_swap_with_multiattach(self):
+        """Test swapping volume attached to multiple servers
+
+        The following is the scenario outline:
+
+        1. Create a volume "volume1" with non-admin.
+        2. Create a volume "volume2" with non-admin.
+        3. Boot 2 instances "server1" and "server2" with non-admin.
+        4. Attach "volume1" to "server1" with non-admin.
+        5. Attach "volume1" to "server2" with non-admin.
+        6. Swap "volume1" to "volume2" on "server1"
+        7. Check "volume1" is attached to "server2" and not attached to
+           "server1"
+        8. Check "volume2" is attached to "server1".
+        """
         # Create two volumes.
         # NOTE(gmann): Volumes are created before server creation so that
         # volumes cleanup can happen successfully irrespective of which volume
diff --git a/tempest/api/compute/admin/test_volumes_negative.py b/tempest/api/compute/admin/test_volumes_negative.py
index 7b0f48b..10d522b 100644
--- a/tempest/api/compute/admin/test_volumes_negative.py
+++ b/tempest/api/compute/admin/test_volumes_negative.py
@@ -23,6 +23,8 @@
 
 
 class VolumesAdminNegativeTest(base.BaseV2ComputeAdminTest):
+    """Negative tests of volume swapping"""
+
     create_default_network = True
 
     @classmethod
@@ -40,6 +42,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('309b5ecd-0585-4a7e-a36f-d2b2bf55259d')
     def test_update_attached_volume_with_nonexistent_volume_in_uri(self):
+        """Test swapping non existent volume should fail"""
         volume = self.create_volume()
         nonexistent_volume = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound,
@@ -51,6 +54,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('7dcac15a-b107-46d3-a5f6-cb863f4e454a')
     def test_update_attached_volume_with_nonexistent_volume_in_body(self):
+        """Test swapping volume to a non existence volume should fail"""
         volume = self.create_volume()
         self.attach_volume(self.server, volume)
 
@@ -62,6 +66,12 @@
 
 
 class UpdateMultiattachVolumeNegativeTest(base.BaseV2ComputeAdminTest):
+    """Negative tests of swapping volume attached to multiple servers
+
+    Negative tests of swapping volume attached to multiple servers with
+    compute microversion greater than 2.59 and volume microversion greater
+    than 3.26
+    """
 
     min_microversion = '2.60'
     volume_min_microversion = '3.27'
@@ -76,7 +86,16 @@
     @decorators.idempotent_id('7576d497-b7c6-44bd-9cc5-c5b4e50fec71')
     @utils.services('volume')
     def test_multiattach_rw_volume_update_failure(self):
+        """Test swapping volume attached to multi-servers with read-write mode
 
+        1. Create two volumes "vol1" and "vol2"
+        2. Create two instances "server1" and "server2"
+        3. Attach "vol1" to both of these instances
+        4. By default both of these attachments should have an attach_mode of
+           read-write, so trying to swap "vol1" to "vol2" should fail
+        5. Check "vol1" is still attached to both servers
+        6. Check "vol2" is not attached to any server
+        """
         # Create two multiattach capable volumes.
         vol1 = self.create_volume(multiattach=True)
         vol2 = self.create_volume(multiattach=True)
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/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/images/test_images_oneserver.py b/tempest/api/compute/images/test_images_oneserver.py
index b811421..23f8326 100644
--- a/tempest/api/compute/images/test_images_oneserver.py
+++ b/tempest/api/compute/images/test_images_oneserver.py
@@ -22,6 +22,7 @@
 
 
 class ImagesOneServerTestJSON(base.BaseV2ComputeTest):
+    """Test server images API"""
 
     @classmethod
     def resource_setup(cls):
@@ -54,6 +55,7 @@
 
     @decorators.idempotent_id('3731d080-d4c5-4872-b41a-64d0d0021314')
     def test_create_delete_image(self):
+        """Test create/delete server image"""
         if self.is_requested_microversion_compatible('2.35'):
             MIN_DISK = 'minDisk'
             MIN_RAM = 'minRam'
@@ -93,6 +95,7 @@
 
     @decorators.idempotent_id('3b7c6fe4-dfe7-477c-9243-b06359db51e6')
     def test_create_image_specify_multibyte_character_image_name(self):
+        """Test creating server image with multibyte character image name"""
         # prefix character is:
         # http://unicode.org/cldr/utility/character.jsp?a=20A1
 
diff --git a/tempest/api/compute/images/test_images_oneserver_negative.py b/tempest/api/compute/images/test_images_oneserver_negative.py
index 37f9be3..0296220 100644
--- a/tempest/api/compute/images/test_images_oneserver_negative.py
+++ b/tempest/api/compute/images/test_images_oneserver_negative.py
@@ -30,6 +30,8 @@
 
 
 class ImagesOneServerNegativeTestJSON(base.BaseV2ComputeTest):
+    """Negative tests of server images"""
+
     create_default_network = True
 
     def tearDown(self):
@@ -87,7 +89,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('55d1d38c-dd66-4933-9c8e-7d92aeb60ddc')
     def test_create_image_specify_invalid_metadata(self):
-        # Return an error when creating image with invalid metadata
+        """Test creating server image with invalid metadata should fail"""
         meta = {'': ''}
         self.assertRaises(lib_exc.BadRequest, self.create_image_from_server,
                           self.server_id, metadata=meta)
@@ -95,7 +97,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('3d24d11f-5366-4536-bd28-cff32b748eca')
     def test_create_image_specify_metadata_over_limits(self):
-        # Return an error when creating image with meta data over 255 chars
+        """Test creating server image with metadata over 255 should fail"""
         meta = {'a' * 256: 'b' * 256}
         self.assertRaises(lib_exc.BadRequest, self.create_image_from_server,
                           self.server_id, metadata=meta)
@@ -103,8 +105,11 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0460efcf-ee88-4f94-acef-1bf658695456')
     def test_create_second_image_when_first_image_is_being_saved(self):
-        # Disallow creating another image when first image is being saved
+        """Test creating another server image when first image is being saved
 
+        Creating another server image when first image is being saved is
+        not allowed.
+        """
         # Create first snapshot
         image = self.create_image_from_server(self.server_id)
         self.addCleanup(self._reset_server)
@@ -123,8 +128,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('084f0cbc-500a-4963-8a4e-312905862581')
     def test_create_image_specify_name_over_character_limit(self):
-        # Return an error if snapshot name over 255 characters is passed
-
+        """Test creating server image with image name over 255 should fail"""
         snapshot_name = ('a' * 256)
         self.assertRaises(lib_exc.BadRequest,
                           self.compute_images_client.create_image,
@@ -133,8 +137,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0894954d-2db2-4195-a45b-ffec0bc0187e')
     def test_delete_image_that_is_not_yet_active(self):
-        # Return an error while trying to delete an image what is creating
-
+        """Test deleting a non-active server image should fail"""
         image = self.create_image_from_server(self.server_id)
         if api_version_utils.compare_version_header_to_response(
             "OpenStack-API-Version", "compute 2.45", image.response, "lt"):
diff --git a/tempest/api/compute/images/test_list_image_filters.py b/tempest/api/compute/images/test_list_image_filters.py
index 2ac7de3..7930c67 100644
--- a/tempest/api/compute/images/test_list_image_filters.py
+++ b/tempest/api/compute/images/test_list_image_filters.py
@@ -31,6 +31,8 @@
 
 
 class ListImageFiltersTestJSON(base.BaseV2ComputeTest):
+    """Test listing server images with compute microversion less than 2.36"""
+
     max_microversion = '2.35'
 
     @classmethod
@@ -129,8 +131,11 @@
 
     @decorators.idempotent_id('a3f5b513-aeb3-42a9-b18e-f091ef73254d')
     def test_list_images_filter_by_status(self):
-        # The list of images should contain only images with the
-        # provided status
+        """Test listing server images filtered by image status
+
+        The list of images should contain only images with the
+        provided image status.
+        """
         params = {'status': 'ACTIVE'}
         images = self.client.list_images(**params)['images']
 
@@ -140,8 +145,11 @@
 
     @decorators.idempotent_id('33163b73-79f5-4d07-a7ea-9213bcc468ff')
     def test_list_images_filter_by_name(self):
-        # List of all images should contain the expected images filtered
-        # by name
+        """Test listing server images filtered by image name
+
+        The list of images should contain only images with the
+        provided image name.
+        """
         params = {'name': self.image1['name']}
         images = self.client.list_images(**params)['images']
 
@@ -153,7 +161,11 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
                           'Snapshotting is not available.')
     def test_list_images_filter_by_server_id(self):
-        # The images should contain images filtered by server id
+        """Test listing images filtered by server id
+
+        The list of images should contain only images with the
+        provided server id.
+        """
         params = {'server': self.server1['id']}
         images = self.client.list_images(**params)['images']
 
@@ -169,7 +181,11 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
                           'Snapshotting is not available.')
     def test_list_images_filter_by_server_ref(self):
-        # The list of servers should be filtered by server ref
+        """Test listing images filtered by server link href
+
+        The list of images should contain only images with the
+        provided server link href.
+        """
         server_links = self.server2['links']
 
         # Try all server link types
@@ -188,7 +204,11 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
                           'Snapshotting is not available.')
     def test_list_images_filter_by_type(self):
-        # The list of servers should be filtered by image type
+        """Test listing images filtered by image type
+
+        The list of images should contain only images with the
+        provided image type.
+        """
         params = {'type': 'snapshot'}
         images = self.client.list_images(**params)['images']
 
@@ -202,13 +222,22 @@
 
     @decorators.idempotent_id('3a484ca9-67ba-451e-b494-7fcf28d32d62')
     def test_list_images_limit_results(self):
-        # Verify only the expected number of results are returned
+        """Test listing images with limited count
+
+        If we use limit=1 when listing images, then only 1 image should be
+        returned.
+        """
         params = {'limit': '1'}
         images = self.client.list_images(**params)['images']
         self.assertEqual(1, len([x for x in images if 'id' in x]))
 
     @decorators.idempotent_id('18bac3ae-da27-436c-92a9-b22474d13aab')
     def test_list_images_filter_by_changes_since(self):
+        """Test listing images filtered by changes-since
+
+        The list of images should contain only images updated since the
+        provided changes-since value.
+        """
         # Verify only updated images are returned in the detailed list
 
         # Becoming ACTIVE will modify the updated time
@@ -220,8 +249,11 @@
 
     @decorators.idempotent_id('9b0ea018-6185-4f71-948a-a123a107988e')
     def test_list_images_with_detail_filter_by_status(self):
-        # Detailed list of all images should only contain images
-        # with the provided status
+        """Test listing server images details filtered by image status
+
+        The list of images should contain only images with the
+        provided image status.
+        """
         params = {'status': 'ACTIVE'}
         images = self.client.list_images(detail=True, **params)['images']
 
@@ -231,8 +263,11 @@
 
     @decorators.idempotent_id('644ea267-9bd9-4f3b-af9f-dffa02396a17')
     def test_list_images_with_detail_filter_by_name(self):
-        # Detailed list of all images should contain the expected
-        # images filtered by name
+        """Test listing server images details filtered by image name
+
+        The list of images should contain only images with the
+        provided image name.
+        """
         params = {'name': self.image1['name']}
         images = self.client.list_images(detail=True, **params)['images']
 
@@ -242,8 +277,11 @@
 
     @decorators.idempotent_id('ba2fa9a9-b672-47cc-b354-3b4c0600e2cb')
     def test_list_images_with_detail_limit_results(self):
-        # Verify only the expected number of results (with full details)
-        # are returned
+        """Test listing images details with limited count
+
+        If we use limit=1 when listing images with full details, then only 1
+        image should be returned.
+        """
         params = {'limit': '1'}
         images = self.client.list_images(detail=True, **params)['images']
         self.assertEqual(1, len(images))
@@ -252,7 +290,11 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
                           'Snapshotting is not available.')
     def test_list_images_with_detail_filter_by_server_ref(self):
-        # Detailed list of servers should be filtered by server ref
+        """Test listing images details filtered by server link href
+
+        The list of images should contain only images with the
+        provided server link href.
+        """
         server_links = self.server2['links']
 
         # Try all server link types
@@ -271,7 +313,11 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.snapshot,
                           'Snapshotting is not available.')
     def test_list_images_with_detail_filter_by_type(self):
-        # The detailed list of servers should be filtered by image type
+        """Test listing images details filtered by image type
+
+        The list of images should contain only images with the
+        provided image type.
+        """
         params = {'type': 'snapshot'}
         images = self.client.list_images(detail=True, **params)['images']
         self.client.show_image(self.image_ref)
@@ -286,8 +332,11 @@
 
     @decorators.idempotent_id('7d439e18-ac2e-4827-b049-7e18004712c4')
     def test_list_images_with_detail_filter_by_changes_since(self):
-        # Verify an update image is returned
+        """Test listing images details filtered by changes-since
 
+        The list of images should contain only images updated since the
+        provided changes-since value.
+        """
         # Becoming ACTIVE will modify the updated time
         # Filter by the image's created time
         params = {'changes-since': self.image1['created']}
diff --git a/tempest/api/compute/images/test_list_image_filters_negative.py b/tempest/api/compute/images/test_list_image_filters_negative.py
index 81c59f9..f77da4b 100644
--- a/tempest/api/compute/images/test_list_image_filters_negative.py
+++ b/tempest/api/compute/images/test_list_image_filters_negative.py
@@ -22,6 +22,12 @@
 
 
 class ListImageFiltersNegativeTestJSON(base.BaseV2ComputeTest):
+    """Negative tests of listing images using compute images API
+
+    Negative tests of listing images using compute images API with
+    microversion less than 2.36.
+    """
+
     max_microversion = '2.35'
 
     @classmethod
@@ -39,7 +45,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('391b0440-432c-4d4b-b5da-c5096aa247eb')
     def test_get_nonexistent_image(self):
-        # Check raises a NotFound
+        """Test getting a non existent image should fail"""
         nonexistent_image = data_utils.rand_uuid()
         self.assertRaises(lib_exc.NotFound, self.client.show_image,
                           nonexistent_image)
diff --git a/tempest/api/compute/servers/test_create_server_multi_nic.py b/tempest/api/compute/servers/test_create_server_multi_nic.py
index d0f53fe..bd3f58d 100644
--- a/tempest/api/compute/servers/test_create_server_multi_nic.py
+++ b/tempest/api/compute/servers/test_create_server_multi_nic.py
@@ -24,6 +24,7 @@
 
 
 class ServersTestMultiNic(base.BaseV2ComputeTest):
+    """Test multiple networks in servers"""
 
     @classmethod
     def skip_checks(cls):
@@ -59,8 +60,11 @@
 
     @decorators.idempotent_id('0578d144-ed74-43f8-8e57-ab10dbf9b3c2')
     def test_verify_multiple_nics_order(self):
-        # Verify that the networks order given at the server creation is
-        # preserved within the server.
+        """Test verifying multiple networks order in server
+
+        The networks order given at the server creation is preserved within
+        the server.
+        """
         net1 = self._create_net_subnet_ret_net_from_cidr('19.80.0.0/24')
         net2 = self._create_net_subnet_ret_net_from_cidr('19.86.0.0/24')
 
@@ -95,6 +99,12 @@
 
     @decorators.idempotent_id('1678d144-ed74-43f8-8e57-ab10dbf9b3c2')
     def test_verify_duplicate_network_nics(self):
+        """Test multiple duplicate networks can be used to create server
+
+        Creating server with networks [net1, net2, net1], the server can
+        be created successfully and all three networks are in the server
+        addresses.
+        """
         # Verify that server creation does not fail when more than one nic
         # is created on the same network.
         net1 = self._create_net_subnet_ret_net_from_cidr('19.80.0.0/24')
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_actions.py b/tempest/api/compute/servers/test_server_actions.py
index d477be0..4db6987 100644
--- a/tempest/api/compute/servers/test_server_actions.py
+++ b/tempest/api/compute/servers/test_server_actions.py
@@ -34,6 +34,8 @@
 
 
 class ServerActionsTestJSON(base.BaseV2ComputeTest):
+    """Test server actions"""
+
     def setUp(self):
         # NOTE(afazekas): Normally we use the same server with all test cases,
         # but if it has an issue, we build a new one
@@ -84,6 +86,11 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.change_password,
                           'Change password not available.')
     def test_change_server_password(self):
+        """Test changing server's password
+
+        The server's password should be set to the provided password and
+        the user can authenticate with the new password.
+        """
         # Since this test messes with the password and makes the
         # server unreachable, it should create its own server
         validation_resources = self.get_test_validation_resources(
@@ -147,17 +154,24 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('2cb1baf6-ac8d-4429-bf0d-ba8a0ba53e32')
     def test_reboot_server_hard(self):
-        # The server should be power cycled
+        """Test hard rebooting server
+
+        The server should be power cycled.
+        """
         self._test_reboot_server('HARD')
 
     @decorators.skip_because(bug="1014647")
     @decorators.idempotent_id('4640e3ef-a5df-482e-95a1-ceeeb0faa84d')
     def test_reboot_server_soft(self):
-        # The server should be signaled to reboot gracefully
+        """Test soft rebooting server
+
+        The server should be signaled to reboot gracefully.
+        """
         self._test_reboot_server('SOFT')
 
     @decorators.idempotent_id('1d1c9104-1b0a-11e7-a3d4-fa163e65f5ce')
     def test_remove_server_all_security_groups(self):
+        """Test removing all security groups from server"""
         server = self.create_test_server(wait_until='ACTIVE')
 
         # Remove all Security group
@@ -232,12 +246,19 @@
 
     @decorators.idempotent_id('aaa6cdf3-55a7-461a-add9-1c8596b9a07c')
     def test_rebuild_server(self):
+        """Test rebuilding server
+
+        The server should be rebuilt using the provided image and data.
+        """
         self._test_rebuild_server()
 
     @decorators.idempotent_id('30449a88-5aff-4f9b-9866-6ee9b17f906d')
     def test_rebuild_server_in_stop_state(self):
-        # The server in stop state  should be rebuilt using the provided
-        # image and remain in SHUTOFF state
+        """Test rebuilding server in stop state
+
+        The server in stop state should be rebuilt using the provided
+        image and remain in SHUTOFF state.
+        """
         server = self.client.show_server(self.server_id)['server']
         old_image = server['image']['id']
         new_image = (self.image_ref_alt
@@ -274,6 +295,10 @@
     @decorators.idempotent_id('b68bd8d6-855d-4212-b59b-2e704044dace')
     @utils.services('volume')
     def test_rebuild_server_with_volume_attached(self):
+        """Test rebuilding server with volume attached
+
+        The volume should be attached to the instance after rebuild.
+        """
         # create a new volume and attach it to the server
         volume = self.create_volume()
 
@@ -333,6 +358,7 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize not available.')
     def test_resize_server_confirm(self):
+        """Test resizing server and then confirming"""
         self._test_resize_server_confirm(self.server_id, stop=False)
 
     @decorators.idempotent_id('e6c28180-7454-4b59-b188-0257af08a63b')
@@ -341,6 +367,7 @@
                           'Resize not available.')
     @utils.services('volume')
     def test_resize_volume_backed_server_confirm(self):
+        """Test resizing a volume backed server and then confirming"""
         # We have to create a new server that is volume-backed since the one
         # from setUp is not volume-backed.
         kwargs = {'volume_backed': True,
@@ -377,14 +404,18 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize not available.')
     def test_resize_server_confirm_from_stopped(self):
+        """Test resizing a stopped server and then confirming"""
         self._test_resize_server_confirm(self.server_id, stop=True)
 
     @decorators.idempotent_id('c03aab19-adb1-44f5-917d-c419577e9e68')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize not available.')
     def test_resize_server_revert(self):
-        # The server's RAM and disk space should return to its original
-        # values after a resize is reverted
+        """Test resizing server and then reverting
+
+        The server's RAM and disk space should return to its original
+        values after a resize is reverted.
+        """
 
         self.client.resize_server(self.server_id, self.flavor_ref_alt)
         # NOTE(zhufl): Explicitly delete the server to get a new one for later
@@ -405,10 +436,13 @@
                           'Resize not available.')
     @utils.services('volume')
     def test_resize_server_revert_with_volume_attached(self):
-        # Tests attaching a volume to a server instance and then resizing
-        # the instance. Once the instance is resized, revert the resize which
-        # should move the instance and volume attachment back to the original
-        # compute host.
+        """Test resizing a volume attached server and then reverting
+
+        Tests attaching a volume to a server instance and then resizing
+        the instance. Once the instance is resized, revert the resize which
+        should move the instance and volume attachment back to the original
+        compute host.
+        """
 
         # Create a blank volume and attach it to the server created in setUp.
         volume = self.create_volume()
@@ -437,7 +471,14 @@
                           'Snapshotting not available, backup not possible.')
     @utils.services('image')
     def test_create_backup(self):
-        # Positive test:create backup successfully and rotate backups correctly
+        """Test creating server backup
+
+        1. create server backup1 with rotation=2, there are 1 backup.
+        2. create server backup2 with rotation=2, there are 2 backups.
+        3. create server backup3, due to the rotation is 2, the first one
+           (backup1) will be deleted, so now there are still 2 backups.
+        """
+
         # create the first and the second backup
 
         # Check if glance v1 is available to determine which client to use. We
@@ -563,8 +604,11 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.console_output,
                           'Console output not supported.')
     def test_get_console_output(self):
-        # Positive test:Should be able to GET the console output
-        # for a given server_id and number of lines
+        """Test getting console output for a server
+
+        Should be able to GET the console output for a given server_id and
+        number of lines.
+        """
 
         # This reboot is necessary for outputting some console log after
         # creating an instance backup. If an instance backup, the console
@@ -579,6 +623,11 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.console_output,
                           'Console output not supported.')
     def test_get_console_output_with_unlimited_size(self):
+        """Test getting server's console output with unlimited size
+
+        The console output lines length should be bigger than the one
+        of test_get_console_output.
+        """
         server = self.create_test_server(wait_until='ACTIVE')
 
         def _check_full_length_console_log():
@@ -597,8 +646,11 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.console_output,
                           'Console output not supported.')
     def test_get_console_output_server_id_in_shutoff_status(self):
-        # Positive test:Should be able to GET the console output
-        # for a given server_id in SHUTOFF status
+        """Test getting console output for a server in SHUTOFF status
+
+        Should be able to GET the console output for a given server_id
+        in SHUTOFF status.
+        """
 
         # NOTE: SHUTOFF is irregular status. To avoid test instability,
         #       one server is created only for this test without using
@@ -614,6 +666,7 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.pause,
                           'Pause is not available.')
     def test_pause_unpause_server(self):
+        """Test pausing and unpausing server"""
         self.client.pause_server(self.server_id)
         waiters.wait_for_server_status(self.client, self.server_id, 'PAUSED')
         self.client.unpause_server(self.server_id)
@@ -623,6 +676,7 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.suspend,
                           'Suspend is not available.')
     def test_suspend_resume_server(self):
+        """Test suspending and resuming server"""
         self.client.suspend_server(self.server_id)
         waiters.wait_for_server_status(self.client, self.server_id,
                                        'SUSPENDED')
@@ -634,6 +688,7 @@
                           'Shelve is not available.')
     @utils.services('image')
     def test_shelve_unshelve_server(self):
+        """Test shelving and unshelving server"""
         if CONF.image_feature_enabled.api_v2:
             glance_client = self.os_primary.image_client_v2
         elif CONF.image_feature_enabled.api_v1:
@@ -673,6 +728,7 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.pause,
                           'Pause is not available.')
     def test_shelve_paused_server(self):
+        """Test shelving a paused server"""
         server = self.create_test_server(wait_until='ACTIVE')
         self.client.pause_server(server['id'])
         waiters.wait_for_server_status(self.client, server['id'], 'PAUSED')
@@ -682,6 +738,7 @@
 
     @decorators.idempotent_id('af8eafd4-38a7-4a4b-bdbc-75145a580560')
     def test_stop_start_server(self):
+        """Test stopping and starting server"""
         self.client.stop_server(self.server_id)
         waiters.wait_for_server_status(self.client, self.server_id, 'SHUTOFF')
         self.client.start_server(self.server_id)
@@ -689,6 +746,12 @@
 
     @decorators.idempotent_id('80a8094c-211e-440a-ab88-9e59d556c7ee')
     def test_lock_unlock_server(self):
+        """Test locking and unlocking server
+
+        Lock the server, and trying to stop it will fail because locked
+        server is not allowed to be stopped by non-admin user.
+        Then unlock the server, now the server can be stopped and started.
+        """
         # Lock the server,try server stop(exceptions throw),unlock it and retry
         self.client.lock_server(self.server_id)
         self.addCleanup(self.client.unlock_server, self.server_id)
@@ -714,6 +777,10 @@
     @testtools.skipUnless(CONF.compute_feature_enabled.vnc_console,
                           'VNC Console feature is disabled.')
     def test_get_vnc_console(self):
+        """Test getting vnc console from a server
+
+        The returned vnc console url should be in valid format.
+        """
         if self.is_requested_microversion_compatible('2.5'):
             body = self.client.get_vnc_console(
                 self.server_id, type='novnc')['console']
diff --git a/tempest/api/compute/servers/test_server_addresses.py b/tempest/api/compute/servers/test_server_addresses.py
index c936ce5..5a3f5d0 100644
--- a/tempest/api/compute/servers/test_server_addresses.py
+++ b/tempest/api/compute/servers/test_server_addresses.py
@@ -19,6 +19,7 @@
 
 
 class ServerAddressesTestJSON(base.BaseV2ComputeTest):
+    """Test server addresses"""
     create_default_network = True
 
     @classmethod
@@ -36,8 +37,10 @@
     @decorators.idempotent_id('6eb718c0-02d9-4d5e-acd1-4e0c269cef39')
     @utils.services('network')
     def test_list_server_addresses(self):
-        # All public and private addresses for
-        # a server should be returned
+        """Test listing server address
+
+        All public and private addresses for a server should be returned.
+        """
 
         addresses = self.client.list_addresses(self.server['id'])['addresses']
 
@@ -51,8 +54,11 @@
     @decorators.idempotent_id('87bbc374-5538-4f64-b673-2b0e4443cc30')
     @utils.services('network')
     def test_list_server_addresses_by_network(self):
-        # Providing a network type should filter
-        # the addresses return by that type
+        """Test listing server addresses filtered by network addresses
+
+        Providing a network address should filter the addresses same with
+        the specified one.
+        """
 
         addresses = self.client.list_addresses(self.server['id'])['addresses']
 
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_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_microversions.py b/tempest/api/compute/servers/test_servers_microversions.py
index 2434884..566d04a 100644
--- a/tempest/api/compute/servers/test_servers_microversions.py
+++ b/tempest/api/compute/servers/test_servers_microversions.py
@@ -32,11 +32,13 @@
 
 
 class ServerShowV254Test(base.BaseV2ComputeTest):
+    """Test servers API schema for compute microversion greater than 2.53"""
     min_microversion = '2.54'
     max_microversion = 'latest'
 
     @decorators.idempotent_id('09170a98-4940-4637-add7-1a35121f1a5a')
     def test_rebuild_server(self):
+        """Test rebuilding server with microversion greater than 2.53"""
         server = self.create_test_server(wait_until='ACTIVE')
         keypair_name = data_utils.rand_name(
             self.__class__.__name__ + '-keypair')
@@ -52,11 +54,13 @@
 
 
 class ServerShowV257Test(base.BaseV2ComputeTest):
+    """Test servers API schema for compute microversion greater than 2.56"""
     min_microversion = '2.57'
     max_microversion = 'latest'
 
     @decorators.idempotent_id('803df848-080a-4261-8f11-b020cd9b6f60')
     def test_rebuild_server(self):
+        """Test rebuilding server with microversion greater than 2.56"""
         server = self.create_test_server(wait_until='ACTIVE')
         user_data = "ZWNobyAiaGVsbG8gd29ybGQi"
         # Checking rebuild API response schema
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/volumes/test_volume_snapshots.py b/tempest/api/compute/volumes/test_volume_snapshots.py
index f3ccf8d..30bea60 100644
--- a/tempest/api/compute/volumes/test_volume_snapshots.py
+++ b/tempest/api/compute/volumes/test_volume_snapshots.py
@@ -24,6 +24,7 @@
 
 
 class VolumesSnapshotsTestJSON(base.BaseV2ComputeTest):
+    """Test volume snapshots with compute microversion less than 2.36"""
 
     # These tests will fail with a 404 starting from microversion 2.36. For
     # more information, see:
@@ -48,6 +49,7 @@
 
     @decorators.idempotent_id('cd4ec87d-7825-450d-8040-6e2068f2da8f')
     def test_volume_snapshot_create_get_list_delete(self):
+        """Test create/get/list/delete volume snapshot"""
         volume = self.create_volume()
         self.addCleanup(self.delete_volume, volume['id'])
 
diff --git a/tempest/api/compute/volumes/test_volumes_get.py b/tempest/api/compute/volumes/test_volumes_get.py
index 0d23c1f..554f418 100644
--- a/tempest/api/compute/volumes/test_volumes_get.py
+++ b/tempest/api/compute/volumes/test_volumes_get.py
@@ -25,6 +25,7 @@
 
 
 class VolumesGetTestJSON(base.BaseV2ComputeTest):
+    """Test compute volumes API with microversion less than 2.36"""
 
     # These tests will fail with a 404 starting from microversion 2.36. For
     # more information, see:
@@ -45,7 +46,7 @@
 
     @decorators.idempotent_id('f10f25eb-9775-4d9d-9cbe-1cf54dae9d5f')
     def test_volume_create_get_delete(self):
-        # CREATE, GET, DELETE Volume
+        """Test create/get/delete volume"""
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
         # Create volume
diff --git a/tempest/api/compute/volumes/test_volumes_list.py b/tempest/api/compute/volumes/test_volumes_list.py
index 28bc174..0b37264 100644
--- a/tempest/api/compute/volumes/test_volumes_list.py
+++ b/tempest/api/compute/volumes/test_volumes_list.py
@@ -21,6 +21,8 @@
 
 
 class VolumesTestJSON(base.BaseV2ComputeTest):
+    """Test listing volumes with compute microversion less than 2.36"""
+
     # NOTE: This test creates a number of 1G volumes. To run successfully,
     # ensure that the backing file for the volume group that Nova uses
     # has space for at least 3 1G volumes!
@@ -57,7 +59,7 @@
 
     @decorators.idempotent_id('bc2dd1a0-15af-48e5-9990-f2e75a48325d')
     def test_volume_list(self):
-        # Should return the list of Volumes
+        """Test listing volumes should return all volumes"""
         # Fetch all Volumes
         fetched_list = self.client.list_volumes()['volumes']
         # Now check if all the Volumes created in setup are in fetched list
@@ -72,7 +74,7 @@
 
     @decorators.idempotent_id('bad0567a-5a4f-420b-851e-780b55bb867c')
     def test_volume_list_with_details(self):
-        # Should return the list of Volumes with details
+        """Test listing volumes with detail should return all volumes"""
         # Fetch all Volumes
         fetched_list = self.client.list_volumes(detail=True)['volumes']
         # Now check if all the Volumes created in setup are in fetched list
@@ -87,7 +89,11 @@
 
     @decorators.idempotent_id('1048ed81-2baf-487a-b284-c0622b86e7b8')
     def test_volume_list_param_limit(self):
-        # Return the list of volumes based on limit set
+        """Test listing volumes based on limit set
+
+        If we list volumes with limit=2, then only 2 volumes should be
+        returned.
+        """
         params = {'limit': 2}
         fetched_vol_list = self.client.list_volumes(**params)['volumes']
 
@@ -96,7 +102,11 @@
 
     @decorators.idempotent_id('33985568-4965-49d5-9bcc-0aa007ca5b7a')
     def test_volume_list_with_detail_param_limit(self):
-        # Return the list of volumes with details based on limit set.
+        """Test listing volumes with detail based on limit set
+
+        If we list volumes with detail with limit=2, then only 2 volumes with
+        detail should be returned.
+        """
         params = {'limit': 2}
         fetched_vol_list = self.client.list_volumes(detail=True,
                                                     **params)['volumes']
@@ -106,7 +116,12 @@
 
     @decorators.idempotent_id('51c22651-a074-4ea7-af0b-094f9331303e')
     def test_volume_list_param_offset_and_limit(self):
-        # Return the list of volumes based on offset and limit set.
+        """Test listing volumes based on offset and limit set
+
+        If we list volumes with offset=1 and limit=1, then 1 volume located
+        in the position 1 in the all volumes list should be returned.
+        (The items in the all volumes list start from position 0.)
+        """
         # get all volumes list
         all_vol_list = self.client.list_volumes()['volumes']
         params = {'offset': 1, 'limit': 1}
@@ -123,7 +138,13 @@
 
     @decorators.idempotent_id('06b6abc4-3f10-48e9-a7a1-3facc98f03e5')
     def test_volume_list_with_detail_param_offset_and_limit(self):
-        # Return the list of volumes details based on offset and limit set.
+        """Test listing volumes with detail based on offset and limit set
+
+        If we list volumes with detail with offset=1 and limit=1, then 1
+        volume with detail located in the position 1 in the all volumes list
+        should be returned.
+        (The items in the all volumes list start from position 0.)
+        """
         # get all volumes list
         all_vol_list = self.client.list_volumes(detail=True)['volumes']
         params = {'offset': 1, 'limit': 1}
diff --git a/tempest/api/compute/volumes/test_volumes_negative.py b/tempest/api/compute/volumes/test_volumes_negative.py
index 444ce93..f553e32 100644
--- a/tempest/api/compute/volumes/test_volumes_negative.py
+++ b/tempest/api/compute/volumes/test_volumes_negative.py
@@ -23,6 +23,7 @@
 
 
 class VolumesNegativeTest(base.BaseV2ComputeTest):
+    """Negative tests of volumes with compute microversion less than 2.36"""
 
     # These tests will fail with a 404 starting from microversion 2.36. For
     # more information, see:
@@ -44,7 +45,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c03ea686-905b-41a2-8748-9635154b7c57')
     def test_volume_get_nonexistent_volume_id(self):
-        # Negative: Should not be able to get details of nonexistent volume
+        """Test getting details of a non existent volume should fail"""
         # Creating a nonexistent volume id
         # Trying to GET a non existent volume
         self.assertRaises(lib_exc.NotFound, self.client.show_volume,
@@ -53,7 +54,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('54a34226-d910-4b00-9ef8-8683e6c55846')
     def test_volume_delete_nonexistent_volume_id(self):
-        # Negative: Should not be able to delete nonexistent Volume
+        """Test deleting a nonexistent volume should fail"""
         # Creating nonexistent volume id
         # Trying to DELETE a non existent volume
         self.assertRaises(lib_exc.NotFound, self.client.delete_volume,
@@ -62,8 +63,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5125ae14-152b-40a7-b3c5-eae15e9022ef')
     def test_create_volume_with_invalid_size(self):
-        # Negative: Should not be able to create volume with invalid size
-        # in request
+        """Test creating volume with invalid size should fail"""
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
         self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
@@ -72,8 +72,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('131cb3a1-75cc-4d40-b4c3-1317f64719b0')
     def test_create_volume_without_passing_size(self):
-        # Negative: Should not be able to create volume without passing size
-        # in request
+        """Test creating volume without specifying size should fail"""
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
         self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
@@ -82,7 +81,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8cce995e-0a83-479a-b94d-e1e40b8a09d1')
     def test_create_volume_with_size_zero(self):
-        # Negative: Should not be able to create volume with size zero
+        """Test creating volume with size=0 should fail"""
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         metadata = {'Type': 'work'}
         self.assertRaises(lib_exc.BadRequest, self.client.create_volume,
@@ -91,14 +90,13 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('62bab09a-4c03-4617-8cca-8572bc94af9b')
     def test_get_volume_without_passing_volume_id(self):
-        # Negative: Should not be able to get volume when empty ID is passed
+        """Test getting volume details without volume id should fail"""
         self.assertRaises(lib_exc.NotFound, self.client.show_volume, '')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('62972737-124b-4513-b6cf-2f019f178494')
     def test_delete_invalid_volume_id(self):
-        # Negative: Should not be able to delete volume when invalid ID is
-        # passed
+        """Test deleting volume with an invalid volume id should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.client.delete_volume,
                           data_utils.rand_name('invalid'))
@@ -106,5 +104,5 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0d1417c5-4ae8-4c2c-adc5-5f0b864253e5')
     def test_delete_volume_without_passing_volume_id(self):
-        # Negative: Should not be able to delete volume when empty ID is passed
+        """Test deleting volume without volume id should fail"""
         self.assertRaises(lib_exc.NotFound, self.client.delete_volume, '')
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/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_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_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_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index bd4b3fa..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):
@@ -118,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/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/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 989b8ef..a632dee 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -598,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/volume/backups.py b/tempest/lib/api_schema/response/volume/backups.py
index 9e85f5f..cba7981 100644
--- a/tempest/lib/api_schema/response/volume/backups.py
+++ b/tempest/lib/api_schema/response/volume/backups.py
@@ -66,7 +66,7 @@
                 'properties': {
                     'id': {'type': 'string', 'format': 'uuid'},
                     'links': parameter_types.links,
-                    'name': {'type': 'string'},
+                    '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.
@@ -91,7 +91,7 @@
                 'properties': {
                     'id': {'type': 'string', 'format': 'uuid'},
                     'links': parameter_types.links,
-                    'name': {'type': 'string'},
+                    'name': {'type': ['string', 'null']},
                     'metadata': {'^.+$': {'type': 'string'}}
                 },
                 'additionalProperties': False,
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/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/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/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/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/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/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/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