Merge "Need to have stable implementation of nova_volume_detach() and add missing docstrings"
diff --git a/tempest/api/object_storage/test_account_bulk.py b/tempest/api/object_storage/test_account_bulk.py
index 80f790f..687fe57 100644
--- a/tempest/api/object_storage/test_account_bulk.py
+++ b/tempest/api/object_storage/test_account_bulk.py
@@ -21,6 +21,7 @@
 
 
 class BulkTest(base.BaseObjectTest):
+    """Test bulk operation of archived file"""
 
     def setUp(self):
         super(BulkTest, self).setUp()
@@ -70,7 +71,7 @@
     @decorators.idempotent_id('a407de51-1983-47cc-9f14-47c2b059413c')
     @utils.requires_ext(extension='bulk_upload', service='object')
     def test_extract_archive(self):
-        # Test bulk operation of file upload with an archived file
+        """Test bulk operation of file upload with an archived file"""
         filepath, container_name, object_name = self._create_archive()
         resp = self._upload_archive(filepath)
         self.containers.append(container_name)
@@ -95,7 +96,7 @@
     @decorators.idempotent_id('c075e682-0d2a-43b2-808d-4116200d736d')
     @utils.requires_ext(extension='bulk_delete', service='object')
     def test_bulk_delete(self):
-        # Test bulk operation of deleting multiple files
+        """Test bulk operation of deleting multiple files"""
         filepath, container_name, object_name = self._create_archive()
         self._upload_archive(filepath)
 
@@ -110,7 +111,7 @@
     @decorators.idempotent_id('dbea2bcb-efbb-4674-ac8a-a5a0e33d1d79')
     @utils.requires_ext(extension='bulk_delete', service='object')
     def test_bulk_delete_by_POST(self):
-        # Test bulk operation of deleting multiple files
+        """Test bulk operation of deleting multiple files by HTTP POST"""
         filepath, container_name, object_name = self._create_archive()
         self._upload_archive(filepath)
 
diff --git a/tempest/api/object_storage/test_account_quotas.py b/tempest/api/object_storage/test_account_quotas.py
index 48f42ec..6854bbe 100644
--- a/tempest/api/object_storage/test_account_quotas.py
+++ b/tempest/api/object_storage/test_account_quotas.py
@@ -22,6 +22,7 @@
 
 
 class AccountQuotasTest(base.BaseObjectTest):
+    """Test account quotas"""
 
     credentials = [['operator', CONF.object_storage.operator_role],
                    ['reseller', CONF.object_storage.reseller_admin_role]]
@@ -79,6 +80,7 @@
     @decorators.idempotent_id('a22ef352-a342-4587-8f47-3bbdb5b039c4')
     @utils.requires_ext(extension='account_quotas', service='object')
     def test_upload_valid_object(self):
+        """Test uploading valid object"""
         object_name = data_utils.rand_name(name="TestObject")
         data = data_utils.arbitrary_string()
         resp, _ = self.object_client.create_object(self.container_name,
diff --git a/tempest/api/object_storage/test_account_services_negative.py b/tempest/api/object_storage/test_account_services_negative.py
index 3e664d7..8d2a501 100644
--- a/tempest/api/object_storage/test_account_services_negative.py
+++ b/tempest/api/object_storage/test_account_services_negative.py
@@ -21,6 +21,7 @@
 
 
 class AccountNegativeTest(base.BaseObjectTest):
+    """Negative tests of account"""
 
     credentials = [['operator', CONF.object_storage.operator_role],
                    ['operator_alt', CONF.object_storage.operator_role]]
@@ -33,7 +34,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('070e6aca-6152-4867-868d-1118d68fb38c')
     def test_list_containers_with_non_authorized_user(self):
-        # list containers using non-authorized user
+        """Test listing containers using non-authorized user"""
 
         test_auth_provider = self.os_operator.auth_provider
         # Get auth for the test user
diff --git a/tempest/api/object_storage/test_container_acl.py b/tempest/api/object_storage/test_container_acl.py
index e9ca0b1..c8731fe 100644
--- a/tempest/api/object_storage/test_container_acl.py
+++ b/tempest/api/object_storage/test_container_acl.py
@@ -22,6 +22,7 @@
 
 
 class ObjectTestACLs(base.BaseObjectTest):
+    """Test object ACLs"""
 
     credentials = [['operator', CONF.object_storage.operator_role],
                    ['operator_alt', CONF.object_storage.operator_role]]
@@ -36,7 +37,7 @@
 
     @decorators.idempotent_id('a3270f3f-7640-4944-8448-c7ea783ea5b6')
     def test_read_object_with_rights(self):
-        # attempt to read object using authorized user
+        """Test reading object using authorized user"""
         # update X-Container-Read metadata ACL
         tenant_id = self.os_roles_operator_alt.credentials.tenant_id
         user_id = self.os_roles_operator_alt.credentials.user_id
@@ -64,7 +65,7 @@
 
     @decorators.idempotent_id('aa58bfa5-40d9-4bc3-82b4-d07f4a9e392a')
     def test_write_object_with_rights(self):
-        # attempt to write object using authorized user
+        """Test writing object using authorized user"""
         # update X-Container-Write metadata ACL
         tenant_id = self.os_roles_operator_alt.credentials.tenant_id
         user_id = self.os_roles_operator_alt.credentials.user_id
diff --git a/tempest/api/object_storage/test_container_services.py b/tempest/api/object_storage/test_container_services.py
index cdc420e..7ad6f6f 100644
--- a/tempest/api/object_storage/test_container_services.py
+++ b/tempest/api/object_storage/test_container_services.py
@@ -19,6 +19,8 @@
 
 
 class ContainerTest(base.BaseObjectTest):
+    """Test containers"""
+
     def tearDown(self):
         self.delete_containers()
         super(ContainerTest, self).tearDown()
@@ -26,6 +28,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('92139d73-7819-4db1-85f8-3f2f22a8d91f')
     def test_create_container(self):
+        """Test creating container"""
         container_name = data_utils.rand_name(name='TestContainer')
         resp, _ = self.container_client.update_container(container_name)
         self.containers.append(container_name)
@@ -33,7 +36,7 @@
 
     @decorators.idempotent_id('49f866ed-d6af-4395-93e7-4187eb56d322')
     def test_create_container_overwrite(self):
-        # overwrite container with the same name
+        """Test overwriting container with the same name"""
         container_name = data_utils.rand_name(name='TestContainer')
         self.container_client.update_container(container_name)
         self.containers.append(container_name)
@@ -43,7 +46,7 @@
 
     @decorators.idempotent_id('c2ac4d59-d0f5-40d5-ba19-0635056d48cd')
     def test_create_container_with_metadata_key(self):
-        # create container with the blank value of metadata
+        """Test creating container with the blank value of metadata"""
         container_name = data_utils.rand_name(name='TestContainer')
         headers = {'X-Container-Meta-test-container-meta': ''}
         resp, _ = self.container_client.update_container(
@@ -60,7 +63,7 @@
 
     @decorators.idempotent_id('e1e8df32-7b22-44e1-aa08-ccfd8d446b58')
     def test_create_container_with_metadata_value(self):
-        # create container with metadata value
+        """Test creating container with metadata value"""
         container_name = data_utils.rand_name(name='TestContainer')
 
         # metadata name using underscores should be converted to hyphens
@@ -79,7 +82,7 @@
 
     @decorators.idempotent_id('24d16451-1c0c-4e4f-b59c-9840a3aba40e')
     def test_create_container_with_remove_metadata_key(self):
-        # create container with the blank value of remove metadata
+        """Test creating container with the blank value of remove metadata"""
         container_name = data_utils.rand_name(name='TestContainer')
         headers = {'X-Container-Meta-test-container-meta': 'Meta1'}
         self.container_client.update_container(container_name, **headers)
@@ -97,7 +100,7 @@
 
     @decorators.idempotent_id('8a21ebad-a5c7-4e29-b428-384edc8cd156')
     def test_create_container_with_remove_metadata_value(self):
-        # create container with remove metadata
+        """Test creating container with remove metadata"""
         container_name = data_utils.rand_name(name='TestContainer')
         headers = {'X-Container-Meta-test-container-meta': 'Meta1'}
         self.container_client.update_container(container_name, **headers)
@@ -114,6 +117,7 @@
 
     @decorators.idempotent_id('95d3a249-b702-4082-a2c4-14bb860cf06a')
     def test_delete_container(self):
+        """Test deleting container"""
         # create a container
         container_name = self.create_container()
         # delete container, success asserted within
@@ -123,7 +127,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('312ff6bd-5290-497f-bda1-7c5fec6697ab')
     def test_list_container_contents(self):
-        # get container contents list
+        """Test getting container contents list"""
         container_name = self.create_container()
         object_name, _ = self.create_object(container_name)
 
@@ -134,7 +138,7 @@
 
     @decorators.idempotent_id('4646ac2d-9bfb-4c7d-a3c5-0f527402b3df')
     def test_list_container_contents_with_no_object(self):
-        # get empty container contents list
+        """Test getting empty container contents list"""
         container_name = self.create_container()
 
         resp, object_list = self.container_client.list_container_objects(
@@ -144,7 +148,7 @@
 
     @decorators.idempotent_id('fe323a32-57b9-4704-a996-2e68f83b09bc')
     def test_list_container_contents_with_delimiter(self):
-        # get container contents list using delimiter param
+        """Test getting container contents list using delimiter param"""
         container_name = self.create_container()
         object_name = data_utils.rand_name(name='TestObject/')
         self.create_object(container_name, object_name)
@@ -158,7 +162,7 @@
 
     @decorators.idempotent_id('55b4fa5c-e12e-4ca9-8fcf-a79afe118522')
     def test_list_container_contents_with_end_marker(self):
-        # get container contents list using end_marker param
+        """Test getting container contents list using end_marker param"""
         container_name = self.create_container()
         object_name, _ = self.create_object(container_name)
 
@@ -171,7 +175,7 @@
 
     @decorators.idempotent_id('196f5034-6ab0-4032-9da9-a937bbb9fba9')
     def test_list_container_contents_with_format_json(self):
-        # get container contents list using format_json param
+        """Test getting container contents list using format_json param"""
         container_name = self.create_container()
         self.create_object(container_name)
 
@@ -190,7 +194,7 @@
 
     @decorators.idempotent_id('655a53ca-4d15-408c-a377-f4c6dbd0a1fa')
     def test_list_container_contents_with_format_xml(self):
-        # get container contents list using format_xml param
+        """Test getting container contents list using format_xml param"""
         container_name = self.create_container()
         self.create_object(container_name)
 
@@ -214,7 +218,7 @@
 
     @decorators.idempotent_id('297ec38b-2b61-4ff4-bcd1-7fa055e97b61')
     def test_list_container_contents_with_limit(self):
-        # get container contents list using limit param
+        """Test getting container contents list using limit param"""
         container_name = self.create_container()
         object_name, _ = self.create_object(container_name)
 
@@ -227,7 +231,7 @@
 
     @decorators.idempotent_id('c31ddc63-2a58-4f6b-b25c-94d2937e6867')
     def test_list_container_contents_with_marker(self):
-        # get container contents list using marker param
+        """Test getting container contents list using marker param"""
         container_name = self.create_container()
         object_name, _ = self.create_object(container_name)
 
@@ -240,7 +244,7 @@
 
     @decorators.idempotent_id('58ca6cc9-6af0-408d-aaec-2a6a7b2f0df9')
     def test_list_container_contents_with_path(self):
-        # get container contents list using path param
+        """Test getting container contents list using path param"""
         container_name = self.create_container()
         object_name = data_utils.rand_name(name='TestObject')
         object_name = 'Swift/' + object_name
@@ -255,7 +259,7 @@
 
     @decorators.idempotent_id('77e742c7-caf2-4ec9-8aa4-f7d509a3344c')
     def test_list_container_contents_with_prefix(self):
-        # get container contents list using prefix param
+        """Test getting container contents list using prefix param"""
         container_name = self.create_container()
         object_name, _ = self.create_object(container_name)
 
@@ -270,7 +274,7 @@
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('96e68f0e-19ec-4aa2-86f3-adc6a45e14dd')
     def test_list_container_metadata(self):
-        # List container metadata
+        """Test listing container metadata"""
         container_name = self.create_container()
 
         metadata = {'name': 'Pictures'}
@@ -286,7 +290,7 @@
 
     @decorators.idempotent_id('a2faf936-6b13-4f8d-92a2-c2278355821e')
     def test_list_no_container_metadata(self):
-        # HEAD container without metadata
+        """Test listing container without metadata"""
         container_name = self.create_container()
 
         resp, _ = self.container_client.list_container_metadata(
@@ -296,7 +300,10 @@
 
     @decorators.idempotent_id('cf19bc0b-7e16-4a5a-aaed-cb0c2fe8deef')
     def test_update_container_metadata_with_create_and_delete_metadata(self):
-        # Send one request of adding and deleting metadata
+        """Test updating container with adding and deleting metadata
+
+        Send one request of adding and deleting metadata.
+        """
         container_name = data_utils.rand_name(name='TestContainer')
         metadata_1 = {'X-Container-Meta-test-container-meta1': 'Meta1'}
         self.container_client.update_container(container_name, **metadata_1)
@@ -319,7 +326,7 @@
 
     @decorators.idempotent_id('2ae5f295-4bf1-4e04-bfad-21e54b62cec5')
     def test_update_container_metadata_with_create_metadata(self):
-        # update container metadata using add metadata
+        """Test updating container metadata using add metadata"""
         container_name = self.create_container()
 
         metadata = {'test-container-meta1': 'Meta1'}
@@ -337,7 +344,7 @@
 
     @decorators.idempotent_id('3a5ce7d4-6e4b-47d0-9d87-7cd42c325094')
     def test_update_container_metadata_with_delete_metadata(self):
-        # update container metadata using delete metadata
+        """Test updating container metadata using delete metadata"""
         container_name = data_utils.rand_name(name='TestContainer')
         metadata = {'X-Container-Meta-test-container-meta1': 'Meta1'}
         self.container_client.update_container(container_name, **metadata)
@@ -355,7 +362,7 @@
 
     @decorators.idempotent_id('31f40a5f-6a52-4314-8794-cd89baed3040')
     def test_update_container_metadata_with_create_metadata_key(self):
-        # update container metadata with a blank value of metadata
+        """Test updating container metadata with a blank value of metadata"""
         container_name = self.create_container()
 
         metadata = {'test-container-meta1': ''}
@@ -371,7 +378,7 @@
 
     @decorators.idempotent_id('a2e36378-6f1f-43f4-840a-ffd9cfd61914')
     def test_update_container_metadata_with_delete_metadata_key(self):
-        # update container metadata with a blank value of metadata
+        """Test updating container metadata with a blank value of metadata"""
         container_name = data_utils.rand_name(name='TestContainer')
         headers = {'X-Container-Meta-test-container-meta1': 'Meta1'}
         self.container_client.update_container(container_name, **headers)
diff --git a/tempest/api/object_storage/test_healthcheck.py b/tempest/api/object_storage/test_healthcheck.py
index 8e9e406..f5e2443 100644
--- a/tempest/api/object_storage/test_healthcheck.py
+++ b/tempest/api/object_storage/test_healthcheck.py
@@ -19,12 +19,14 @@
 
 
 class HealthcheckTest(base.BaseObjectTest):
+    """Test healthcheck"""
 
     def setUp(self):
         super(HealthcheckTest, self).setUp()
 
     @decorators.idempotent_id('db5723b1-f25c-49a9-bfeb-7b5640caf337')
     def test_get_healthcheck(self):
+        """Test getting healthcheck"""
         url = self.account_client._get_base_version_url() + "healthcheck"
         resp, body = self.account_client.raw_request(url, "GET")
         self.account_client._error_checker(resp, body)
diff --git a/tempest/api/object_storage/test_object_version.py b/tempest/api/object_storage/test_object_version.py
index 75111b6..b64b172 100644
--- a/tempest/api/object_storage/test_object_version.py
+++ b/tempest/api/object_storage/test_object_version.py
@@ -24,6 +24,8 @@
 
 
 class ContainerTest(base.BaseObjectTest):
+    """Test versioned container"""
+
     def assertContainer(self, container, count, byte, versioned):
         resp, _ = self.container_client.list_container_metadata(container)
         self.assertHeaders(resp, 'Container', 'HEAD')
@@ -39,6 +41,15 @@
         not CONF.object_storage_feature_enabled.object_versioning,
         'Object-versioning is disabled')
     def test_versioned_container(self):
+        """Test versioned container
+
+        1. create container1
+        2. create container2, with container1 as 'X-versions-Location' header
+        3. create object1 in container1
+        4. create 2nd version of object1
+        5. delete object version 2
+        6. delete object version 1
+        """
         # create container
         vers_container_name = data_utils.rand_name(name='TestVersionContainer')
         resp, _ = self.container_client.update_container(vers_container_name)
diff --git a/tempest/api/volume/admin/test_volume_services.py b/tempest/api/volume/admin/test_volume_services.py
index 293af81..1d12a73 100644
--- a/tempest/api/volume/admin/test_volume_services.py
+++ b/tempest/api/volume/admin/test_volume_services.py
@@ -39,12 +39,14 @@
 
     @decorators.idempotent_id('e0218299-0a59-4f43-8b2b-f1c035b3d26d')
     def test_list_services(self):
+        """Test listing volume services"""
         services = (self.admin_volume_services_client.list_services()
                     ['services'])
         self.assertNotEmpty(services)
 
     @decorators.idempotent_id('63a3e1ca-37ee-4983-826d-83276a370d25')
     def test_get_service_by_service_binary_name(self):
+        """Test getting volume service by binary name"""
         services = (self.admin_volume_services_client.list_services(
             binary=self.binary_name)['services'])
         self.assertNotEmpty(services)
@@ -53,6 +55,7 @@
 
     @decorators.idempotent_id('178710e4-7596-4e08-9333-745cb8bc4f8d')
     def test_get_service_by_host_name(self):
+        """Test getting volume service by service host name"""
         services_on_host = [service for service in self.services if
                             _get_host(service['host']) == self.host_name]
 
@@ -69,6 +72,7 @@
 
     @decorators.idempotent_id('67ec6902-f91d-4dec-91fa-338523208bbc')
     def test_get_service_by_volume_host_name(self):
+        """Test getting volume service by volume host name"""
         volume_id = self.create_volume()['id']
         volume = self.admin_volume_client.show_volume(volume_id)['volume']
         hostname = _get_host(volume['os-vol-host-attr:host'])
@@ -83,7 +87,7 @@
 
     @decorators.idempotent_id('ffa6167c-4497-4944-a464-226bbdb53908')
     def test_get_service_by_service_and_host_name(self):
-
+        """Test getting volume service by binary name and host name"""
         services = (self.admin_volume_services_client.list_services(
             host=self.host_name, binary=self.binary_name))['services']
 
diff --git a/tempest/api/volume/admin/test_volume_type_access.py b/tempest/api/volume/admin/test_volume_type_access.py
index b64face..55ec428 100644
--- a/tempest/api/volume/admin/test_volume_type_access.py
+++ b/tempest/api/volume/admin/test_volume_type_access.py
@@ -24,11 +24,13 @@
 
 
 class VolumeTypesAccessTest(base.BaseVolumeAdminTest):
+    """Test volume type access"""
 
     credentials = ['primary', 'alt', 'admin']
 
     @decorators.idempotent_id('d4dd0027-835f-4554-a6e5-50903fb79184')
     def test_volume_type_access_add(self):
+        """Test adding volume type access for non-admin project"""
         # Creating a NON public volume type
         params = {'os-volume-type-access:is_public': False}
         volume_type = self.create_volume_type(**params)
@@ -52,6 +54,7 @@
 
     @decorators.idempotent_id('5220eb28-a435-43ce-baaf-ed46f0e95159')
     def test_volume_type_access_list(self):
+        """Test listing volume type access"""
         # Creating a NON public volume type
         params = {'os-volume-type-access:is_public': False}
         volume_type = self.create_volume_type(**params)
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs.py b/tempest/api/volume/admin/test_volume_types_extra_specs.py
index 730acdf..852aa93 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs.py
@@ -19,6 +19,7 @@
 
 
 class VolumeTypesExtraSpecsTest(base.BaseVolumeAdminTest):
+    """Test volume type extra specs"""
 
     @classmethod
     def resource_setup(cls):
@@ -27,7 +28,7 @@
 
     @decorators.idempotent_id('b42923e9-0452-4945-be5b-d362ae533e60')
     def test_volume_type_extra_specs_list(self):
-        # List Volume types extra specs.
+        """Test listing volume type extra specs"""
         extra_specs = {"spec1": "val1"}
         body = self.admin_volume_types_client.create_volume_type_extra_specs(
             self.volume_type['id'], extra_specs)['extra_specs']
@@ -40,7 +41,7 @@
 
     @decorators.idempotent_id('0806db36-b4a0-47a1-b6f3-c2e7f194d017')
     def test_volume_type_extra_specs_update(self):
-        # Update volume type extra specs
+        """Test updating volume type extra specs"""
         extra_specs = {"spec2": "val1"}
         body = self.admin_volume_types_client.create_volume_type_extra_specs(
             self.volume_type['id'], extra_specs)['extra_specs']
@@ -74,7 +75,7 @@
 
     @decorators.idempotent_id('d4772798-601f-408a-b2a5-29e8a59d1220')
     def test_volume_type_extra_spec_create_get_delete(self):
-        # Create/Get/Delete volume type extra spec.
+        """Test Create/Get/Delete volume type extra specs"""
         spec_key = "spec3"
         extra_specs = {spec_key: "val1"}
         body = self.admin_volume_types_client.create_volume_type_extra_specs(
diff --git a/tempest/api/volume/admin/test_volumes_backup.py b/tempest/api/volume/admin/test_volumes_backup.py
index 45060d0..bd4b3fa 100644
--- a/tempest/api/volume/admin/test_volumes_backup.py
+++ b/tempest/api/volume/admin/test_volumes_backup.py
@@ -67,11 +67,8 @@
         # Export Backup
         export_backup = (self.admin_backups_client.export_backup(backup['id'])
                          ['backup-record'])
-        self.assertIn('backup_service', export_backup)
-        self.assertIn('backup_url', export_backup)
         self.assertTrue(export_backup['backup_service'].startswith(
                         'cinder.backup.drivers'))
-        self.assertIsNotNone(export_backup['backup_url'])
 
         # NOTE(geguileo): Backups are imported with the same backup id
         # (important for incremental backups among other things), so we cannot
@@ -92,7 +89,6 @@
         # deletions will delete data from the backup back-end because they
         # were both pointing to the same backend data.
         self.addCleanup(self._delete_backup, new_id)
-        self.assertIn("id", import_backup)
         self.assertEqual(new_id, import_backup['id'])
         waiters.wait_for_volume_resource_status(self.admin_backups_client,
                                                 import_backup['id'],
diff --git a/tempest/api/volume/admin/test_volumes_list.py b/tempest/api/volume/admin/test_volumes_list.py
index 6ce4a85..c3229f0 100644
--- a/tempest/api/volume/admin/test_volumes_list.py
+++ b/tempest/api/volume/admin/test_volumes_list.py
@@ -24,6 +24,7 @@
 
 
 class VolumesListAdminTestJSON(base.BaseVolumeAdminTest):
+    """Test listing volumes with admin privilege"""
 
     @classmethod
     def resource_setup(cls):
@@ -41,7 +42,7 @@
 
     @decorators.idempotent_id('5866286f-3290-4cfd-a414-088aa6cdc469')
     def test_volume_list_param_tenant(self):
-        # Test to list volumes from single tenant
+        """Test admin can list volumes belonging to specified project"""
         # Create a volume in admin tenant
         adm_vol = self.admin_volume_client.create_volume(
             size=CONF.volume.volume_size)['volume']
diff --git a/tempest/api/volume/test_availability_zone.py b/tempest/api/volume/test_availability_zone.py
index 0b6ee38..39369be 100644
--- a/tempest/api/volume/test_availability_zone.py
+++ b/tempest/api/volume/test_availability_zone.py
@@ -22,7 +22,7 @@
 
     @decorators.idempotent_id('01f1ae88-eba9-4c6b-a011-6f7ace06b725')
     def test_get_availability_zone_list(self):
-        # List of availability zone
+        """Test listing volume available zones"""
         availability_zone = (
             self.availability_zone_client.list_availability_zones()
             ['availabilityZoneInfo'])
diff --git a/tempest/api/volume/test_extensions.py b/tempest/api/volume/test_extensions.py
index 39ce00c..acd9ca2 100644
--- a/tempest/api/volume/test_extensions.py
+++ b/tempest/api/volume/test_extensions.py
@@ -26,10 +26,11 @@
 
 
 class ExtensionsTestJSON(base.BaseVolumeTest):
+    """Test volume extensions"""
 
     @decorators.idempotent_id('94607eb0-43a5-47ca-82aa-736b41bd2e2c')
     def test_list_extensions(self):
-        # List of all extensions
+        """Test listing volume extensions"""
         extensions = (self.volumes_extension_client.list_extensions()
                       ['extensions'])
         if not CONF.volume_feature_enabled.api_extensions:
diff --git a/tempest/api/volume/test_image_metadata.py b/tempest/api/volume/test_image_metadata.py
index 53b3acc..8f9bbd2 100644
--- a/tempest/api/volume/test_image_metadata.py
+++ b/tempest/api/volume/test_image_metadata.py
@@ -24,6 +24,7 @@
 
 
 class VolumesImageMetadata(base.BaseVolumeTest):
+    """Test volume image metadata"""
 
     @classmethod
     def skip_checks(cls):
@@ -41,6 +42,7 @@
     @decorators.idempotent_id('03efff0b-5c75-4822-8f10-8789ac15b13e')
     @utils.services('image')
     def test_update_show_delete_image_metadata(self):
+        """Test update/show/delete volume's image metadata"""
         # Update image metadata
         image_metadata = {'image_id': '5137a025-3c5f-43c1-bc64-5f41270040a5',
                           'image_name': 'image',
diff --git a/tempest/api/volume/test_snapshot_metadata.py b/tempest/api/volume/test_snapshot_metadata.py
index e6fe25d..ee1b5e5 100644
--- a/tempest/api/volume/test_snapshot_metadata.py
+++ b/tempest/api/volume/test_snapshot_metadata.py
@@ -23,6 +23,8 @@
 
 
 class SnapshotMetadataTestJSON(base.BaseVolumeTest):
+    """Test snapshot metadata"""
+
     @classmethod
     def skip_checks(cls):
         super(SnapshotMetadataTestJSON, cls).skip_checks()
@@ -45,6 +47,7 @@
 
     @decorators.idempotent_id('a2f20f99-e363-4584-be97-bc33afb1a56c')
     def test_crud_snapshot_metadata(self):
+        """Test create/get/update/delete snapshot metadata"""
         # Create metadata for the snapshot
         metadata = {"key1": "value1",
                     "key2": "value2",
@@ -82,7 +85,7 @@
 
     @decorators.idempotent_id('e8ff85c5-8f97-477f-806a-3ac364a949ed')
     def test_update_show_snapshot_metadata_item(self):
-        # Update metadata item for the snapshot
+        """Test update/show snapshot metadata item"""
         metadata = {"key1": "value1",
                     "key2": "value2",
                     "key3": "value3"}
diff --git a/tempest/api/volume/test_volumes_actions.py b/tempest/api/volume/test_volumes_actions.py
index e42ea40..5b50bfa 100644
--- a/tempest/api/volume/test_volumes_actions.py
+++ b/tempest/api/volume/test_volumes_actions.py
@@ -25,6 +25,8 @@
 
 
 class VolumesActionsTest(base.BaseVolumeTest):
+    """Test volume actions"""
+
     create_default_network = True
 
     @classmethod
@@ -38,6 +40,7 @@
     @decorators.attr(type='smoke')
     @utils.services('compute')
     def test_attach_detach_volume_to_instance(self):
+        """Test attaching and detaching volume to instance"""
         # Create a server
         server = self.create_server()
         # Volume is attached and detached successfully from an instance
@@ -53,7 +56,7 @@
 
     @decorators.idempotent_id('63e21b4c-0a0c-41f6-bfc3-7c2816815599')
     def test_volume_bootable(self):
-        # Verify that a volume bootable flag is retrieved
+        """Test setting and retrieving bootable flag of a volume"""
         for bool_bootable in [True, False]:
             self.volumes_client.set_bootable_volume(self.volume['id'],
                                                     bootable=bool_bootable)
@@ -69,6 +72,11 @@
     @decorators.idempotent_id('9516a2c8-9135-488c-8dd6-5677a7e5f371')
     @utils.services('compute')
     def test_get_volume_attachment(self):
+        """Test getting volume attachments
+
+        Attach a volume to a server, and then retrieve volume's attachments
+        info.
+        """
         # Create a server
         server = self.create_server()
         # Verify that a volume's attachment information is retrieved
@@ -96,6 +104,7 @@
     @decorators.idempotent_id('d8f1ca95-3d5b-44a3-b8ca-909691c9532d')
     @utils.services('image')
     def test_volume_upload(self):
+        """Test uploading volume to create an image"""
         # NOTE(gfidente): the volume uploaded in Glance comes from setUpClass,
         # it is shared with the other tests. After it is uploaded in Glance,
         # there is no way to delete it from Cinder, so we delete it from Glance
@@ -118,6 +127,7 @@
 
     @decorators.idempotent_id('92c4ef64-51b2-40c0-9f7e-4749fbaaba33')
     def test_reserve_unreserve_volume(self):
+        """Test reserving and unreserving volume"""
         # Mark volume as reserved.
         self.volumes_client.reserve_volume(self.volume['id'])
         # To get the volume info
@@ -131,6 +141,7 @@
 
     @decorators.idempotent_id('fff74e1e-5bd3-4b33-9ea9-24c103bc3f59')
     def test_volume_readonly_update(self):
+        """Test updating and retrieve volume's readonly flag"""
         for readonly in [True, False]:
             # Update volume readonly
             self.volumes_client.update_volume_readonly(self.volume['id'],
diff --git a/tempest/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index c178272..e3b8800 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -80,11 +80,7 @@
         self.assertEqual('container', backup['container'])
 
         # Get all backups with detail
-        backups = self.backups_client.list_backups(
-            detail=True)['backups']
-        for backup_info in backups:
-            self.assertIn('created_at', backup_info)
-            self.assertIn('links', backup_info)
+        backups = self.backups_client.list_backups(detail=True)['backups']
         self.assertIn((backup['name'], backup['id']),
                       [(m['name'], m['id']) for m in backups])
 
@@ -176,7 +172,6 @@
             backup['id'], **update_kwargs)['backup']
         self.assertEqual(backup['id'], update_backup['id'])
         self.assertEqual(update_kwargs['name'], update_backup['name'])
-        self.assertIn('links', update_backup)
 
         # Assert response body for show_backup method
         retrieved_backup = self.backups_client.show_backup(
diff --git a/tempest/api/volume/test_volumes_negative.py b/tempest/api/volume/test_volumes_negative.py
index 866bd87..76c22f0 100644
--- a/tempest/api/volume/test_volumes_negative.py
+++ b/tempest/api/volume/test_volumes_negative.py
@@ -28,6 +28,7 @@
 
 
 class VolumesNegativeTest(base.BaseVolumeTest):
+    """Negative tests of volumes"""
 
     @classmethod
     def resource_setup(cls):
@@ -58,50 +59,49 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('f131c586-9448-44a4-a8b0-54ca838aa43e')
     def test_volume_get_nonexistent_volume_id(self):
-        # Should not be able to get a non-existent volume
+        """Test getting non existent volume should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.show_volume,
                           data_utils.rand_uuid())
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('555efa6e-efcd-44ef-8a3b-4a7ca4837a29')
     def test_volume_delete_nonexistent_volume_id(self):
-        # Should not be able to delete a non-existent Volume
+        """Test deleting non existent volume should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.delete_volume,
                           data_utils.rand_uuid())
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1ed83a8a-682d-4dfb-a30e-ee63ffd6c049')
     def test_create_volume_with_invalid_size(self):
-        # Should not be able to create volume with invalid size in request
+        """Test creating volume with invalid size should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.create_volume, size='#$%')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9387686f-334f-4d31-a439-33494b9e2683')
     def test_create_volume_without_passing_size(self):
-        # Should not be able to create volume without passing size
-        # in request
+        """Test creating volume with empty size should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.create_volume, size='')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('41331caa-eaf4-4001-869d-bc18c1869360')
     def test_create_volume_with_size_zero(self):
-        # Should not be able to create volume with size zero
+        """Test creating volume with zero size should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.create_volume, size='0')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8b472729-9eba-446e-a83b-916bdb34bef7')
     def test_create_volume_with_size_negative(self):
-        # Should not be able to create volume with size negative
+        """Test creating volume with negative size should fail"""
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.create_volume, size='-1')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('10254ed8-3849-454e-862e-3ab8e6aa01d2')
     def test_create_volume_with_nonexistent_volume_type(self):
-        # Should not be able to create volume with non-existent volume type
+        """Test creating volume with non existent volume type should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
                           size=CONF.volume.volume_size,
                           volume_type=data_utils.rand_uuid())
@@ -109,7 +109,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0c36f6ae-4604-4017-b0a9-34fdc63096f9')
     def test_create_volume_with_nonexistent_snapshot_id(self):
-        # Should not be able to create volume with non-existent snapshot
+        """Test creating volume with non existent snapshot should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
                           size=CONF.volume.volume_size,
                           snapshot_id=data_utils.rand_uuid())
@@ -117,7 +117,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('47c73e08-4be8-45bb-bfdf-0c4e79b88344')
     def test_create_volume_with_nonexistent_source_volid(self):
-        # Should not be able to create volume with non-existent source volume
+        """Test creating volume with non existent source volume should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.create_volume,
                           size=CONF.volume.volume_size,
                           source_volid=data_utils.rand_uuid())
@@ -125,46 +125,49 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0186422c-999a-480e-a026-6a665744c30c')
     def test_update_volume_with_nonexistent_volume_id(self):
+        """Test updating non existent volume should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
                           volume_id=data_utils.rand_uuid())
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e66e40d6-65e6-4e75-bdc7-636792fa152d')
     def test_update_volume_with_invalid_volume_id(self):
+        """Test updating volume with invalid volume id should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
                           volume_id=data_utils.rand_name('invalid'))
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('72aeca85-57a5-4c1f-9057-f320f9ea575b')
     def test_update_volume_with_empty_volume_id(self):
+        """Test updating volume with empty volume id should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.update_volume,
                           volume_id='')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('30799cfd-7ee4-446c-b66c-45b383ed211b')
     def test_get_invalid_volume_id(self):
-        # Should not be able to get volume with invalid id
+        """Test getting volume with invalid volume id should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.show_volume,
                           data_utils.rand_name('invalid'))
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('c6c3db06-29ad-4e91-beb0-2ab195fe49e3')
     def test_get_volume_without_passing_volume_id(self):
-        # Should not be able to get volume when empty ID is passed
+        """Test getting volume with empty volume id should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.show_volume, '')
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('1f035827-7c32-4019-9240-b4ec2dbd9dfd')
     def test_delete_invalid_volume_id(self):
-        # Should not be able to delete volume when invalid ID is passed
+        """Test deleting volume with invalid volume id should fail"""
         self.assertRaises(lib_exc.NotFound, self.volumes_client.delete_volume,
                           data_utils.rand_name('invalid'))
 
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('441a1550-5d44-4b30-af0f-a6d402f52026')
     def test_delete_volume_without_passing_volume_id(self):
-        # Should not be able to delete volume when empty ID is passed
+        """Test deleting volume with empty volume id should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.delete_volume, '')
 
@@ -172,6 +175,7 @@
     @decorators.idempotent_id('f5e56b0a-5d02-43c1-a2a7-c9b792c2e3f6')
     @utils.services('compute')
     def test_attach_volumes_with_nonexistent_volume_id(self):
+        """Test attaching non existent volume to server should fail"""
         server = self.create_server()
 
         self.assertRaises(lib_exc.NotFound,
@@ -183,6 +187,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9f9c24e4-011d-46b5-b992-952140ce237a')
     def test_detach_volumes_with_invalid_volume_id(self):
+        """Test detaching volume with invalid volume id should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.detach_volume,
                           'xxx')
@@ -190,7 +195,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('e0c75c74-ee34-41a9-9288-2a2051452854')
     def test_volume_extend_with_size_smaller_than_original_size(self):
-        # Extend volume with smaller size than original size.
+        """Test extending volume with decreasing size should fail"""
         extend_size = 0
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.extend_volume,
@@ -199,7 +204,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('5d0b480d-e833-439f-8a5a-96ad2ed6f22f')
     def test_volume_extend_with_non_number_size(self):
-        # Extend volume when size is non number.
+        """Test extending volume with non-integer size should fail"""
         extend_size = 'abc'
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.extend_volume,
@@ -208,7 +213,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('355218f1-8991-400a-a6bb-971239287d92')
     def test_volume_extend_with_None_size(self):
-        # Extend volume with None size.
+        """Test extending volume with none size should fail"""
         extend_size = None
         self.assertRaises(lib_exc.BadRequest,
                           self.volumes_client.extend_volume,
@@ -217,7 +222,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('8f05a943-013c-4063-ac71-7baf561e82eb')
     def test_volume_extend_with_nonexistent_volume_id(self):
-        # Extend volume size when volume is nonexistent.
+        """Test extending non existent volume should fail"""
         extend_size = self.volume['size'] + 1
         self.assertRaises(lib_exc.NotFound, self.volumes_client.extend_volume,
                           data_utils.rand_uuid(), new_size=extend_size)
@@ -225,7 +230,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('aff8ba64-6d6f-4f2e-bc33-41a08ee9f115')
     def test_volume_extend_without_passing_volume_id(self):
-        # Extend volume size when passing volume id is None.
+        """Test extending volume without passing volume id should fail"""
         extend_size = self.volume['size'] + 1
         self.assertRaises(lib_exc.NotFound, self.volumes_client.extend_volume,
                           None, new_size=extend_size)
@@ -233,6 +238,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ac6084c0-0546-45f9-b284-38a367e0e0e2')
     def test_reserve_volume_with_nonexistent_volume_id(self):
+        """Test reserving non existent volume should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.reserve_volume,
                           data_utils.rand_uuid())
@@ -240,6 +246,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('eb467654-3dc1-4a72-9b46-47c29d22654c')
     def test_unreserve_volume_with_nonexistent_volume_id(self):
+        """Test unreserving non existent volume should fail"""
         self.assertRaises(lib_exc.NotFound,
                           self.volumes_client.unreserve_volume,
                           data_utils.rand_uuid())
@@ -247,6 +254,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('449c4ed2-ecdd-47bb-98dc-072aeccf158c')
     def test_reserve_volume_with_negative_volume_status(self):
+        """Test reserving already reserved volume should fail"""
         # Mark volume as reserved.
         self.volumes_client.reserve_volume(self.volume['id'])
         # Mark volume which is marked as reserved before
@@ -259,6 +267,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('0f4aa809-8c7b-418f-8fb3-84c7a5dfc52f')
     def test_list_volumes_with_nonexistent_name(self):
+        """Test listing volumes with non existent name should get nothing"""
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         params = {'name': v_name}
         fetched_volume = self.volumes_client.list_volumes(
@@ -268,6 +277,10 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('9ca17820-a0e7-4cbd-a7fa-f4468735e359')
     def test_list_volumes_detail_with_nonexistent_name(self):
+        """Test listing volume details with non existent name
+
+        Listing volume details with non existent name should get nothing.
+        """
         v_name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
         params = {'name': v_name}
         fetched_volume = \
@@ -278,6 +291,7 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('143b279b-7522-466b-81be-34a87d564a7c')
     def test_list_volumes_with_invalid_status(self):
+        """Test listing volumes with invalid status should get nothing"""
         params = {'status': 'null'}
         fetched_volume = self.volumes_client.list_volumes(
             params=params)['volumes']
@@ -286,6 +300,10 @@
     @decorators.attr(type=['negative'])
     @decorators.idempotent_id('ba94b27b-be3f-496c-a00e-0283b373fa75')
     def test_list_volumes_detail_with_invalid_status(self):
+        """Test listing volume details with invalid status
+
+        Listing volume details with invalid status should get nothing
+        """
         params = {'status': 'null'}
         fetched_volume = \
             self.volumes_client.list_volumes(detail=True,
@@ -296,6 +314,7 @@
     @decorators.idempotent_id('5b810c91-0ad1-47ce-aee8-615f789be78f')
     @utils.services('image')
     def test_create_volume_from_image_with_decreasing_size(self):
+        """Test creating volume from image with decreasing size should fail"""
         # Create image
         image = self.create_image()
 
@@ -311,6 +330,7 @@
     @decorators.idempotent_id('d15e7f35-2cfc-48c8-9418-c8223a89bcbb')
     @utils.services('image')
     def test_create_volume_from_deactivated_image(self):
+        """Test creating volume from deactivated image should fail"""
         # Create image
         image = self.create_image()
 
diff --git a/tempest/lib/api_schema/response/volume/backups.py b/tempest/lib/api_schema/response/volume/backups.py
new file mode 100644
index 0000000..9e85f5f
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/backups.py
@@ -0,0 +1,229 @@
+# Copyright 2015 NEC Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+common_show_backup = {
+    'type': 'object',
+    'properties': {
+        'status': {'type': 'string'},
+        'object_count': {'type': 'integer'},
+        'container': {'type': ['string', 'null']},
+        'description': {'type': ['string', 'null']},
+        'links': parameter_types.links,
+        'availability_zone': {'type': ['string', 'null']},
+        'created_at': parameter_types.date_time,
+        'updated_at': parameter_types.date_time_or_null,
+        'name': {'type': ['string', 'null']},
+        'has_dependent_backups': {'type': 'boolean'},
+        'volume_id': {'type': 'string', 'format': 'uuid'},
+        'fail_reason': {'type': ['string', 'null']},
+        'size': {'type': 'integer'},
+        'id': {'type': 'string', 'format': 'uuid'},
+        'is_incremental': {'type': 'boolean'},
+        'data_timestamp': parameter_types.date_time_or_null,
+        'snapshot_id': {'type': ['string', 'null']},
+        # TODO(zhufl): os-backup-project-attr:project_id is added
+        # in 3.18, we should move it to the 3.18 schema file when
+        # microversion is supported in volume interfaces.
+        'os-backup-project-attr:project_id': {
+            'type': 'string', 'format': 'uuid'},
+        # TODO(zhufl): metadata is added in 3.43, we should move it
+        # to the 3.43 schema file when microversion is supported
+        # in volume interfaces.
+        'metadata': {'^.+$': {'type': 'string'}},
+        # TODO(zhufl): user_id is added in 3.56, we should move it
+        # to the 3.56 schema file when microversion is supported
+        # in volume interfaces.
+        'user_id': {'type': 'string'},
+    },
+    'additionalProperties': False,
+    'required': ['status', 'object_count', 'fail_reason', 'links',
+                 'created_at', 'updated_at', 'name', 'volume_id', 'size', 'id',
+                 'data_timestamp']
+}
+
+create_backup = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'backup': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'links': parameter_types.links,
+                    'name': {'type': 'string'},
+                    # TODO(zhufl): metadata is added in 3.43, we should move it
+                    # to the 3.43 schema file when microversion is supported
+                    # in volume interfaces.
+                    'metadata': {'^.+$': {'type': 'string'}},
+                },
+                'additionalProperties': False,
+                'required': ['id', 'links', 'name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['backup']
+    }
+}
+
+update_backup = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'backup': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'links': parameter_types.links,
+                    'name': {'type': 'string'},
+                    'metadata': {'^.+$': {'type': 'string'}}
+                },
+                'additionalProperties': False,
+                'required': ['id', 'links', 'name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['backup']
+    }
+}
+
+restore_backup = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'restore': {
+                'type': 'object',
+                'properties': {
+                    'backup_id': {'type': 'string', 'format': 'uuid'},
+                    'volume_id': {'type': 'string', 'format': 'uuid'},
+                    'volume_name': {'type': 'string'},
+                },
+                'additionalProperties': False,
+                'required': ['backup_id', 'volume_id', 'volume_name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['restore']
+    }
+}
+
+delete_backup = {'status_code': [202]}
+
+show_backup = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'backup': common_show_backup
+        },
+        'additionalProperties': False,
+        'required': ['backup']
+    }
+}
+
+list_backups_no_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'backups': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'links': parameter_types.links,
+                        'id': {'type': 'string', 'format': 'uuid'},
+                        'name': {'type': ['string', 'null']},
+                        # TODO(zhufl): count is added in 3.45, we should move
+                        # it to the 3.45 schema file when microversion is
+                        # supported in volume interfaces
+                        'count': {'type': 'integer'}
+                    },
+                    'additionalProperties': False,
+                    'required': ['links', 'id', 'name']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['backups'],
+    }
+}
+
+list_backups_detail = copy.deepcopy(common_show_backup)
+# TODO(zhufl): count is added in 3.45, we should move it to the 3.45 schema
+# file when microversion is supported in volume interfaces
+list_backups_detail['properties'].update({'count': {'type': 'integer'}})
+list_backups_with_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'backups': {
+                'type': 'array',
+                'items': list_backups_detail
+            }
+        },
+        'additionalProperties': False,
+        'required': ['backups'],
+    }
+}
+
+export_backup = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'backup-record': {
+                'type': 'object',
+                'properties': {
+                    'backup_service': {'type': 'string'},
+                    'backup_url': {'type': 'string'}
+                },
+                'additionalProperties': False,
+                'required': ['backup_service', 'backup_url']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['backup-record']
+    }
+}
+
+import_backup = {
+    'status_code': [201],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'backup': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'links': parameter_types.links,
+                    'name': {'type': ['string', 'null']},
+                },
+                'additionalProperties': False,
+                'required': ['id', 'links', 'name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['backup']
+    }
+}
+
+reset_backup_status = {'status_code': [202]}
diff --git a/tempest/lib/api_schema/response/volume/groups.py b/tempest/lib/api_schema/response/volume/groups.py
new file mode 100644
index 0000000..cb31269
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/groups.py
@@ -0,0 +1,164 @@
+# Copyright 2015 NEC Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+create_group = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'group': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'name': {'type': 'string'},
+                },
+                'additionalProperties': False,
+                'required': ['id', 'name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['group']
+    }
+}
+
+delete_group = {'status_code': [202]}
+
+show_group = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'group': {
+                'type': 'object',
+                'properties': {
+                    'status': {'type': 'string'},
+                    'description': {'type': ['string', 'null']},
+                    'availability_zone': {'type': 'string'},
+                    'created_at': parameter_types.date_time,
+                    'group_type': {'type': 'string', 'format': 'uuid'},
+                    'group_snapshot_id': {'type': ['string', 'null']},
+                    'source_group_id': {'type': ['string', 'null']},
+                    'volume_types': {
+                        'type': 'array',
+                        'items': {'type': 'string', 'format': 'uuid'}
+                    },
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'name': {'type': 'string'},
+                    # TODO(zhufl): volumes is added in 3.25, we should move it
+                    # to the 3.25 schema file when microversion is supported
+                    # in volume interfaces
+                    'volumes': {
+                        'type': 'array',
+                        'items': {'type': 'string', 'format': 'uuid'}
+                    },
+                    'replication_status': {'type': 'string'}
+                },
+                'additionalProperties': False,
+                'required': ['status', 'description', 'created_at',
+                             'group_type', 'volume_types', 'id', 'name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['group']
+    }
+}
+
+list_groups_no_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'groups': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'id': {'type': 'string', 'format': 'uuid'},
+                        'name': {'type': 'string'}
+                    },
+                    'additionalProperties': False,
+                    'required': ['id', 'name'],
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['groups'],
+    }
+}
+
+list_groups_with_detail = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'groups': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'status': {'type': 'string'},
+                        'description': {'type': ['string', 'null']},
+                        'availability_zone': {'type': 'string'},
+                        'created_at': parameter_types.date_time,
+                        'group_type': {'type': 'string', 'format': 'uuid'},
+                        'group_snapshot_id': {'type': ['string', 'null']},
+                        'source_group_id': {'type': ['string', 'null']},
+                        'volume_types': {
+                            'type': 'array',
+                            'items': {'type': 'string', 'format': 'uuid'}
+                        },
+                        'id': {'type': 'string', 'format': 'uuid'},
+                        'name': {'type': 'string'},
+                        # TODO(zhufl): volumes is added in 3.25, we should
+                        # move it to the 3.25 schema file when microversion
+                        # is supported in volume interfaces
+                        'volumes': {
+                            'type': 'array',
+                            'items': {'type': 'string', 'format': 'uuid'}
+                        },
+                    },
+                    'additionalProperties': False,
+                    'required': ['status', 'description', 'created_at',
+                                 'group_type', 'volume_types', 'id', 'name']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['groups'],
+    }
+}
+
+create_group_from_source = {
+    'status_code': [202],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'group': {
+                'type': 'object',
+                'properties': {
+                    'id': {'type': 'string', 'format': 'uuid'},
+                    'name': {'type': 'string'},
+                },
+                'additionalProperties': False,
+                'required': ['id', 'name']
+            }
+        },
+        'additionalProperties': False,
+        'required': ['group']
+    }
+}
+update_group = {'status_code': [202]}
+reset_group_status = {'status_code': [202]}
diff --git a/tempest/lib/services/volume/v3/backups_client.py b/tempest/lib/services/volume/v3/backups_client.py
index 970471e..1df45fa 100644
--- a/tempest/lib/services/volume/v3/backups_client.py
+++ b/tempest/lib/services/volume/v3/backups_client.py
@@ -16,6 +16,7 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
+from tempest.lib.api_schema.response.volume import backups as schema
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.volume import base_client
@@ -34,7 +35,7 @@
         post_body = json.dumps({'backup': kwargs})
         resp, body = self.post('backups', post_body)
         body = json.loads(body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.create_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def update_backup(self, backup_id, **kwargs):
@@ -47,7 +48,7 @@
         put_body = json.dumps({'backup': kwargs})
         resp, body = self.put('backups/%s' % backup_id, put_body)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.update_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def restore_backup(self, backup_id, **kwargs):
@@ -60,13 +61,13 @@
         post_body = json.dumps({'restore': kwargs})
         resp, body = self.post('backups/%s/restore' % (backup_id), post_body)
         body = json.loads(body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.restore_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_backup(self, backup_id):
         """Delete a backup of volume."""
         resp, body = self.delete('backups/%s' % backup_id)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.delete_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_backup(self, backup_id):
@@ -74,7 +75,7 @@
         url = "backups/%s" % backup_id
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def list_backups(self, detail=False, **params):
@@ -86,13 +87,15 @@
         https://docs.openstack.org/api-ref/block-storage/v3/index.html#list-backups-with-detail
         """
         url = "backups"
+        list_backups_schema = schema.list_backups_no_detail
         if detail:
             url += "/detail"
+            list_backups_schema = schema.list_backups_with_detail
         if params:
             url += '?%s' % urllib.urlencode(params)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(list_backups_schema, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def export_backup(self, backup_id):
@@ -100,7 +103,7 @@
         url = "backups/%s/export_record" % backup_id
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.export_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def import_backup(self, **kwargs):
@@ -113,14 +116,14 @@
         post_body = json.dumps({'backup-record': kwargs})
         resp, body = self.post("backups/import_record", post_body)
         body = json.loads(body)
-        self.expected_success(201, resp.status)
+        self.validate_response(schema.import_backup, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def reset_backup_status(self, backup_id, status):
         """Reset the specified backup's status."""
         post_body = json.dumps({'os-reset_status': {"status": status}})
         resp, body = self.post('backups/%s/action' % backup_id, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.reset_backup_status, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def is_resource_deleted(self, id):
diff --git a/tempest/lib/services/volume/v3/groups_client.py b/tempest/lib/services/volume/v3/groups_client.py
index ffae232..3d8523d 100644
--- a/tempest/lib/services/volume/v3/groups_client.py
+++ b/tempest/lib/services/volume/v3/groups_client.py
@@ -16,6 +16,7 @@
 from oslo_serialization import jsonutils as json
 from six.moves.urllib import parse as urllib
 
+from tempest.lib.api_schema.response.volume import groups as schema
 from tempest.lib.common import rest_client
 from tempest.lib import exceptions as lib_exc
 from tempest.lib.services.volume import base_client
@@ -23,6 +24,7 @@
 
 class GroupsClient(base_client.BaseClient):
     """Client class to send CRUD Volume Group API requests"""
+    api_version = 'v3'
 
     def create_group(self, **kwargs):
         """Creates a group.
@@ -35,7 +37,7 @@
         post_body = json.dumps({'group': kwargs})
         resp, body = self.post('groups', post_body)
         body = json.loads(body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.create_group, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def delete_group(self, group_id, delete_volumes=True):
@@ -49,7 +51,7 @@
         post_body = json.dumps({'delete': post_body})
         resp, body = self.post('groups/%s/action' % group_id,
                                post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.delete_group, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_group(self, group_id):
@@ -62,7 +64,7 @@
         url = "groups/%s" % str(group_id)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_group, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def list_groups(self, detail=False, **params):
@@ -74,13 +76,15 @@
         https://docs.openstack.org/api-ref/block-storage/v3/#list-groups-with-details
         """
         url = "groups"
+        schema_list_groups = schema.list_groups_no_detail
         if detail:
             url += "/detail"
+            schema_list_groups = schema.list_groups_with_detail
         if params:
             url += '?%s' % urllib.urlencode(params)
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema_list_groups, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def create_group_from_source(self, **kwargs):
@@ -93,7 +97,7 @@
         post_body = json.dumps({'create-from-src': kwargs})
         resp, body = self.post('groups/action', post_body)
         body = json.loads(body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.create_group_from_source, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def update_group(self, group_id, **kwargs):
@@ -105,7 +109,7 @@
         """
         put_body = json.dumps({'group': kwargs})
         resp, body = self.put('groups/%s' % group_id, put_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.update_group, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def reset_group_status(self, group_id, status_to_set):
@@ -116,7 +120,7 @@
         """
         post_body = json.dumps({'reset_status': {'status': status_to_set}})
         resp, body = self.post('groups/%s/action' % group_id, post_body)
-        self.expected_success(202, resp.status)
+        self.validate_response(schema.reset_group_status, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def is_resource_deleted(self, id):
diff --git a/tempest/tests/lib/services/volume/v3/test_backups_client.py b/tempest/tests/lib/services/volume/v3/test_backups_client.py
index 97e1132..ca7918a 100644
--- a/tempest/tests/lib/services/volume/v3/test_backups_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_backups_client.py
@@ -45,6 +45,8 @@
                 "availability_zone": "az1",
                 "container": "volumebackups",
                 "created_at": "2013-04-02T10:35:27.000000",
+                "updated_at": "2013-04-02T10:39:27.000000",
+                "data_timestamp": "2013-04-02T10:35:27.000000",
                 "description": None,
                 "fail_reason": None,
                 "id": "2ef47aee-8844-490c-804d-2a8efe561c65",
@@ -64,7 +66,6 @@
                 "user_id": "515ba0dd59f84f25a6a084a45d8d93b2",
                 "size": 1,
                 "status": "available",
-                "updated_at": "2013-04-02T10:35:27.000000",
                 "volume_id": "e5185058-943a-4cb4-96d9-72c184c337d6",
                 "is_incremental": True,
                 "has_dependent_backups": False