Merge "Use create_image_from_server to create image for server"
diff --git a/tempest/api/identity/admin/v3/test_list_projects.py b/tempest/api/identity/admin/v3/test_list_projects.py
index 9022b2d..cb8ea11 100644
--- a/tempest/api/identity/admin/v3/test_list_projects.py
+++ b/tempest/api/identity/admin/v3/test_list_projects.py
@@ -44,29 +44,24 @@
     @classmethod
     def resource_setup(cls):
         super(ListProjectsTestJSON, cls).resource_setup()
-        cls.project_ids = list()
-        cls.domain_id = cls.os_admin.credentials.domain_id
+        domain_id = cls.os_admin.credentials.domain_id
         # Create project with domain
-        cls.p1_name = data_utils.rand_name('project')
+        p1_name = data_utils.rand_name(cls.__name__)
         cls.p1 = cls.projects_client.create_project(
-            cls.p1_name, enabled=False,
-            domain_id=cls.domain_id)['project']
+            p1_name, enabled=False, domain_id=domain_id)['project']
         cls.addClassResourceCleanup(cls.projects_client.delete_project,
                                     cls.p1['id'])
-        cls.project_ids.append(cls.p1['id'])
         # Create default project
-        p2_name = data_utils.rand_name('project')
+        p2_name = data_utils.rand_name(cls.__name__)
         cls.p2 = cls.projects_client.create_project(p2_name)['project']
         cls.addClassResourceCleanup(cls.projects_client.delete_project,
                                     cls.p2['id'])
-        cls.project_ids.append(cls.p2['id'])
         # Create a new project (p3) using p2 as parent project
-        p3_name = data_utils.rand_name('project')
+        p3_name = data_utils.rand_name(cls.__name__)
         cls.p3 = cls.projects_client.create_project(
             p3_name, parent_id=cls.p2['id'])['project']
         cls.addClassResourceCleanup(cls.projects_client.delete_project,
                                     cls.p3['id'])
-        cls.project_ids.append(cls.p3['id'])
 
     @decorators.idempotent_id('0fe7a334-675a-4509-b00e-1c4b95d5dae8')
     def test_list_projects_with_enabled(self):
@@ -98,7 +93,7 @@
         cls.p1 = cls.projects_client.show_project(
             cls.os_primary.credentials.project_id)['project']
         # Create a test project
-        p2_name = data_utils.rand_name('project')
+        p2_name = data_utils.rand_name(cls.__name__)
         p2_domain_id = CONF.identity.default_domain_id
         cls.p2 = cls.projects_client.create_project(
             p2_name, domain_id=p2_domain_id)['project']
diff --git a/tempest/api/identity/v3/test_api_discovery.py b/tempest/api/identity/v3/test_api_discovery.py
index c04c21b..e87d1cd 100644
--- a/tempest/api/identity/v3/test_api_discovery.py
+++ b/tempest/api/identity/v3/test_api_discovery.py
@@ -14,12 +14,24 @@
 #    under the License.
 
 from tempest.api.identity import base
+from tempest import config
 from tempest.lib import decorators
 
 
+CONF = config.CONF
+
+
 class TestApiDiscovery(base.BaseIdentityV3Test):
     """Tests for API discovery features."""
 
+    @decorators.idempotent_id('79aec9ae-710f-4c54-a4fc-3aa25b4feac3')
+    def test_identity_v3_existence(self):
+        versions = self.non_admin_versions_client.list_versions()
+        found = any(
+            "v3" in version.get('id')
+            for version in versions['versions']['values'])
+        self.assertEqual(CONF.identity_feature_enabled.api_v3, found)
+
     @decorators.idempotent_id('721f480f-35b6-46c7-846e-047e6acea0dc')
     @decorators.attr(type='smoke')
     def test_list_api_versions(self):
diff --git a/tempest/api/volume/admin/test_backends_capabilities.py b/tempest/api/volume/admin/test_backends_capabilities.py
index affed6b..1351704 100644
--- a/tempest/api/volume/admin/test_backends_capabilities.py
+++ b/tempest/api/volume/admin/test_backends_capabilities.py
@@ -21,17 +21,6 @@
 
 class BackendsCapabilitiesAdminTestsJSON(base.BaseVolumeAdminTest):
 
-    CAPABILITIES = ('namespace',
-                    'vendor_name',
-                    'volume_backend_name',
-                    'pool_name',
-                    'driver_version',
-                    'storage_protocol',
-                    'display_name',
-                    'description',
-                    'visibility',
-                    'properties')
-
     @classmethod
     def resource_setup(cls):
         super(BackendsCapabilitiesAdminTestsJSON, cls).resource_setup()
@@ -44,12 +33,8 @@
     @decorators.idempotent_id('3750af44-5ea2-4cd4-bc3e-56e7e6caf854')
     def test_get_capabilities_backend(self):
         # Test backend properties
-        backend = self.admin_capabilities_client.show_backend_capabilities(
-            self.hosts[0])
-
-        # Verify getting capabilities parameters from a backend
-        for key in self.CAPABILITIES:
-            self.assertIn(key, backend)
+        # 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):
diff --git a/tempest/api/volume/admin/test_volume_hosts.py b/tempest/api/volume/admin/test_volume_hosts.py
index 7e53ce8..83c27e1 100644
--- a/tempest/api/volume/admin/test_volume_hosts.py
+++ b/tempest/api/volume/admin/test_volume_hosts.py
@@ -26,13 +26,6 @@
                                 "The count of volume hosts is < 2, "
                                 "response of list hosts is: %s" % hosts)
 
-        # Check elements in volume hosts list
-        host_list_keys = ['service', 'host_name', 'last-update',
-                          'zone', 'service-status', 'service-state']
-        for host in hosts:
-            for key in host_list_keys:
-                self.assertIn(key, host)
-
     @decorators.idempotent_id('21168d57-b373-4b71-a3ac-f2c88f0c5d31')
     def test_show_host(self):
         hosts = self.admin_hosts_client.list_hosts()['hosts']
@@ -53,12 +46,6 @@
                             "all hosts that found are: %s" % hosts)
 
         # Check each cinder-volume host.
-        host_detail_keys = ['project', 'volume_count', 'snapshot_count',
-                            'host', 'total_volume_gb', 'total_snapshot_gb']
         for host in c_vol_hosts:
             host_details = self.admin_hosts_client.show_host(host)['host']
             self.assertNotEmpty(host_details)
-            for detail in host_details:
-                self.assertIn('resource', detail)
-                for key in host_detail_keys:
-                    self.assertIn(key, detail['resource'])
diff --git a/tempest/lib/api_schema/response/volume/capabilities.py b/tempest/lib/api_schema/response/volume/capabilities.py
new file mode 100644
index 0000000..ec60fc3
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/capabilities.py
@@ -0,0 +1,55 @@
+# 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.
+
+property_info = {
+    'type': 'object',
+    'properties': {
+        'type': {'type': 'string'},
+        'description': {'type': 'string'},
+        'title': {'type': 'string'}
+    },
+    'additionalProperties': False,
+    'required': ['type', 'description', 'title']
+}
+
+show_backend_capabilities = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'pool_name': {'type': ['string', 'null']},
+            'description': {'type': ['string', 'null']},
+            'volume_backend_name': {'type': 'string'},
+            'namespace': {'type': 'string',
+                          'pattern': '^OS::Storage::Capabilities::.+$'},
+            'visibility': {'type': ['string', 'null']},
+            'driver_version': {'type': 'string'},
+            'vendor_name': {'type': 'string'},
+            'properties': {
+                'type': 'object',
+                'properties': {
+                    '^.+$': property_info
+                },
+            },
+            'storage_protocol': {'type': 'string'},
+            'replication_targets': {'type': 'array'},
+            'display_name': {'type': ['string', 'null']}
+        },
+        'additionalProperties': False,
+        'required': ['pool_name', 'volume_backend_name', 'namespace',
+                     'visibility', 'driver_version', 'vendor_name',
+                     'properties', 'storage_protocol', 'replication_targets',
+                     'display_name', 'description']
+    }
+}
diff --git a/tempest/lib/api_schema/response/volume/hosts.py b/tempest/lib/api_schema/response/volume/hosts.py
new file mode 100644
index 0000000..d4848d5
--- /dev/null
+++ b/tempest/lib/api_schema/response/volume/hosts.py
@@ -0,0 +1,81 @@
+# Copyright 2018 ZTE Corporation.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from tempest.lib.api_schema.response.compute.v2_1 import parameter_types
+
+show_host = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'host': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'resource': {
+                            'type': 'object',
+                            'properties': {
+                                'volume_count': {'type': 'string'},
+                                'total_volume_gb': {'type': 'string'},
+                                'total_snapshot_gb': {'type': 'string'},
+                                'project': {'type': 'string'},
+                                'host': {'type': 'string', 'pattern': '.+@.+'},
+                                'snapshot_count': {'type': 'string'},
+                            },
+                            'additionalProperties': False,
+                            'required': ['volume_count', 'total_volume_gb',
+                                         'total_snapshot_gb', 'project',
+                                         'host', 'snapshot_count'],
+                        }
+                    },
+                    'additionalProperties': False,
+                    'required': ['resource']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['host']
+    }
+}
+
+list_hosts = {
+    'status_code': [200],
+    'response_body': {
+        'type': 'object',
+        'properties': {
+            'hosts': {
+                'type': 'array',
+                'items': {
+                    'type': 'object',
+                    'properties': {
+                        'service-status': {
+                            'enum': ['available', 'unavailable']},
+                        'service': {'type': 'string'},
+                        'zone': {'type': 'string'},
+                        'service-state': {
+                            'enum': ['enabled', 'disabled']},
+                        'host_name': {'type': 'string'},
+                        'last-update': parameter_types.date_time_or_null
+                    },
+                    'additionalProperties': False,
+                    'required': ['service-status', 'service', 'zone',
+                                 'service-state', 'host_name', 'last-update']
+                }
+            }
+        },
+        'additionalProperties': False,
+        'required': ['hosts']
+    }
+}
diff --git a/tempest/lib/services/identity/v3/roles_client.py b/tempest/lib/services/identity/v3/roles_client.py
index 43a9020..f9356be 100644
--- a/tempest/lib/services/identity/v3/roles_client.py
+++ b/tempest/lib/services/identity/v3/roles_client.py
@@ -42,8 +42,12 @@
         return rest_client.ResponseBody(resp, body)
 
     def list_roles(self, **params):
-        """Get the list of Roles."""
+        """Get the list of Roles.
 
+        For a full list of available parameters, please refer to the official
+        API reference:
+        https://docs.openstack.org/api-ref/identity/v3/index.html#list-roles
+        """
         url = 'roles'
         if params:
             url += '?%s' % urllib.urlencode(params)
diff --git a/tempest/lib/services/volume/v3/capabilities_client.py b/tempest/lib/services/volume/v3/capabilities_client.py
index ac2cd02..dc850a8 100644
--- a/tempest/lib/services/volume/v3/capabilities_client.py
+++ b/tempest/lib/services/volume/v3/capabilities_client.py
@@ -15,6 +15,7 @@
 
 from oslo_serialization import jsonutils as json
 
+from tempest.lib.api_schema.response.volume import capabilities as schema
 from tempest.lib.common import rest_client
 
 
@@ -30,5 +31,5 @@
         url = 'capabilities/%s' % host
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.show_backend_capabilities, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v3/hosts_client.py b/tempest/lib/services/volume/v3/hosts_client.py
index c95d2d2..019a852 100644
--- a/tempest/lib/services/volume/v3/hosts_client.py
+++ b/tempest/lib/services/volume/v3/hosts_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 hosts as schema
 from tempest.lib.common import rest_client
 
 
@@ -35,13 +36,13 @@
 
         resp, body = self.get(url)
         body = json.loads(body)
-        self.expected_success(200, resp.status)
+        self.validate_response(schema.list_hosts, resp, body)
         return rest_client.ResponseBody(resp, body)
 
     def show_host(self, host_name):
         """Show host details."""
         url = 'os-hosts/%s' % host_name
         resp, body = self.get(url)
-        self.expected_success(200, resp.status)
         body = json.loads(body)
+        self.validate_response(schema.show_host, resp, body)
         return rest_client.ResponseBody(resp, body)
diff --git a/tempest/tests/lib/services/volume/v3/test_hosts_client.py b/tempest/tests/lib/services/volume/v3/test_hosts_client.py
index 09bc0b1..8033e38 100644
--- a/tempest/tests/lib/services/volume/v3/test_hosts_client.py
+++ b/tempest/tests/lib/services/volume/v3/test_hosts_client.py
@@ -48,7 +48,7 @@
                     "total_volume_gb": "2",
                     "total_snapshot_gb": "0",
                     "project": "(total)",
-                    "host": "fake-host",
+                    "host": "fake-host@rbd",
                     "snapshot_count": "0"
                 }
             },
@@ -58,7 +58,7 @@
                     "total_volume_gb": "2",
                     "total_snapshot_gb": "0",
                     "project": "f21a9c86d7114bf99c711f4874d80474",
-                    "host": "fake-host",
+                    "host": "fake-host@lvm",
                     "snapshot_count": "0"
                 }
             }