Merge "Add support of schema versioning for microversion"
diff --git a/tempest/exceptions.py b/tempest/exceptions.py
index 1d725af..931737d 100644
--- a/tempest/exceptions.py
+++ b/tempest/exceptions.py
@@ -181,6 +181,11 @@
                "be of format MajorNum.MinorNum or string 'latest'.")
 
 
+class JSONSchemaNotFound(TempestException):
+    message = ("JSON Schema for %(version)s is not found in \n"
+               " %(schema_versions_info)s")
+
+
 class CommandFailed(Exception):
     def __init__(self, returncode, cmd, output, stderr):
         super(CommandFailed, self).__init__()
diff --git a/tempest/services/compute/json/base.py b/tempest/services/compute/json/base.py
index ea1a696..40d3056 100644
--- a/tempest/services/compute/json/base.py
+++ b/tempest/services/compute/json/base.py
@@ -14,6 +14,9 @@
 
 from tempest_lib.common import rest_client
 
+from tempest.common import api_version_request
+from tempest import exceptions
+
 
 class BaseComputeClient(rest_client.RestClient):
     api_microversion = None
@@ -26,3 +29,36 @@
 
     def set_api_microversion(self, microversion):
         self.api_microversion = microversion
+
+    def get_schema(self, schema_versions_info):
+        """Get JSON schema
+
+        This method provides the matching schema for requested
+        microversion (self.api_microversion).
+        :param schema_versions_info: List of dict which provides schema
+        information with range of valid versions.
+        Example -
+        schema_versions_info = [
+            {'min': None, 'max': '2.1', 'schema': schemav21},
+            {'min': '2.2', 'max': '2.9', 'schema': schemav22},
+            {'min': '2.10', 'max': None, 'schema': schemav210}]
+        """
+        schema = None
+        version = api_version_request.APIVersionRequest(self.api_microversion)
+        for items in schema_versions_info:
+            min_version = api_version_request.APIVersionRequest(items['min'])
+            max_version = api_version_request.APIVersionRequest(items['max'])
+            # This is case where self.api_microversion is None, which means
+            # request without microversion So select base v2.1 schema.
+            if version.is_null() and items['min'] is None:
+                schema = items['schema']
+                break
+            # else select appropriate schema as per self.api_microversion
+            elif version.matches(min_version, max_version):
+                schema = items['schema']
+                break
+        if schema is None:
+            raise exceptions.JSONSchemaNotFound(
+                version=version.get_string(),
+                schema_versions_info=schema_versions_info)
+        return schema
diff --git a/tempest/tests/services/compute/test_base_compute_client.py b/tempest/tests/services/compute/test_base_compute_client.py
index 13461e4..134fe39 100644
--- a/tempest/tests/services/compute/test_base_compute_client.py
+++ b/tempest/tests/services/compute/test_base_compute_client.py
@@ -16,6 +16,7 @@
 import mock
 from tempest_lib.common import rest_client
 
+from tempest import exceptions
 from tempest.services.compute.json import base as base_compute_client
 from tempest.tests import fake_auth_provider
 from tempest.tests.services.compute import base
@@ -70,3 +71,82 @@
                                'raw_request') as mock_get:
             mock_get.side_effect = raw_request
             self.client.get('fake_url')
+
+
+class DummyServiceClient1(base_compute_client.BaseComputeClient):
+    schema_versions_info = [
+        {'min': None, 'max': '2.1', 'schema': 'schemav21'},
+        {'min': '2.2', 'max': '2.9', 'schema': 'schemav22'},
+        {'min': '2.10', 'max': None, 'schema': 'schemav210'}]
+
+    def return_selected_schema(self):
+        return self.get_schema(self.schema_versions_info)
+
+
+class TestSchemaVersionsNone(base.BaseComputeServiceTest):
+    api_microversion = None
+    expected_schema = 'schemav21'
+
+    def setUp(self):
+        super(TestSchemaVersionsNone, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = DummyServiceClient1(fake_auth, 'compute', 'regionOne')
+        self.client.api_microversion = self.api_microversion
+
+    def test_schema(self):
+        self.assertEqual(self.expected_schema,
+                         self.client.return_selected_schema())
+
+
+class TestSchemaVersionsV21(TestSchemaVersionsNone):
+    api_microversion = '2.1'
+    expected_schema = 'schemav21'
+
+
+class TestSchemaVersionsV22(TestSchemaVersionsNone):
+    api_microversion = '2.2'
+    expected_schema = 'schemav22'
+
+
+class TestSchemaVersionsV25(TestSchemaVersionsNone):
+    api_microversion = '2.5'
+    expected_schema = 'schemav22'
+
+
+class TestSchemaVersionsV29(TestSchemaVersionsNone):
+    api_microversion = '2.9'
+    expected_schema = 'schemav22'
+
+
+class TestSchemaVersionsV210(TestSchemaVersionsNone):
+    api_microversion = '2.10'
+    expected_schema = 'schemav210'
+
+
+class TestSchemaVersionsLatest(TestSchemaVersionsNone):
+    api_microversion = 'latest'
+    expected_schema = 'schemav210'
+
+
+class DummyServiceClient2(base_compute_client.BaseComputeClient):
+    schema_versions_info = [
+        {'min': None, 'max': '2.1', 'schema': 'schemav21'},
+        {'min': '2.2', 'max': '2.9', 'schema': 'schemav22'}]
+
+    def return_selected_schema(self):
+        return self.get_schema(self.schema_versions_info)
+
+
+class TestSchemaVersionsNotFound(base.BaseComputeServiceTest):
+    api_microversion = '2.10'
+    expected_schema = 'schemav210'
+
+    def setUp(self):
+        super(TestSchemaVersionsNotFound, self).setUp()
+        fake_auth = fake_auth_provider.FakeAuthProvider()
+        self.client = DummyServiceClient2(fake_auth, 'compute', 'regionOne')
+        self.client.api_microversion = self.api_microversion
+
+    def test_schema(self):
+        self.assertRaises(exceptions.JSONSchemaNotFound,
+                          self.client.return_selected_schema)