diff --git a/watcher/api/controllers/rest_api_version_history.rst b/watcher/api/controllers/rest_api_version_history.rst index 4dd360f12..2fe970865 100644 --- a/watcher/api/controllers/rest_api_version_history.rst +++ b/watcher/api/controllers/rest_api_version_history.rst @@ -30,3 +30,7 @@ audits. --- Added ``force`` into create audit request. If ``force`` is true, audit will be executed despite of ongoing actionplan. + +1.3 +--- +Added list data model API. diff --git a/watcher/api/controllers/v1/__init__.py b/watcher/api/controllers/v1/__init__.py index c59febb4a..c37a9f19f 100644 --- a/watcher/api/controllers/v1/__init__.py +++ b/watcher/api/controllers/v1/__init__.py @@ -40,6 +40,7 @@ from watcher.api.controllers.v1 import goal from watcher.api.controllers.v1 import scoring_engine from watcher.api.controllers.v1 import service from watcher.api.controllers.v1 import strategy +from watcher.api.controllers.v1 import utils from watcher.api.controllers.v1 import versions @@ -163,13 +164,14 @@ class V1(APIBase): 'audits', '', bookmark=True) ] - v1.data_model = [link.Link.make_link('self', base_url, - 'data_model', ''), - link.Link.make_link('bookmark', - base_url, - 'data_model', '', - bookmark=True) - ] + if utils.allow_list_datamodel(): + v1.data_model = [link.Link.make_link('self', base_url, + 'data_model', ''), + link.Link.make_link('bookmark', + base_url, + 'data_model', '', + bookmark=True) + ] v1.actions = [link.Link.make_link('self', base_url, 'actions', ''), link.Link.make_link('bookmark', diff --git a/watcher/api/controllers/v1/data_model.py b/watcher/api/controllers/v1/data_model.py index f496a38ba..7ab98200a 100644 --- a/watcher/api/controllers/v1/data_model.py +++ b/watcher/api/controllers/v1/data_model.py @@ -24,6 +24,7 @@ from wsme import types as wtypes import wsmeext.pecan as wsme_pecan from watcher.api.controllers.v1 import types +from watcher.api.controllers.v1 import utils from watcher.common import exception from watcher.common import policy from watcher.decision_engine import rpcapi @@ -49,6 +50,8 @@ class DataModelController(rest.RestController): :param audit_uuid: The UUID of the audit, used to filter data model by the scope in audit. """ + if not utils.allow_list_datamodel(): + raise exception.NotAcceptable if self.from_data_model: raise exception.OperationNotPermitted allowed_data_model_type = [ diff --git a/watcher/api/controllers/v1/utils.py b/watcher/api/controllers/v1/utils.py index 285823ab2..dee4d2bb4 100644 --- a/watcher/api/controllers/v1/utils.py +++ b/watcher/api/controllers/v1/utils.py @@ -176,3 +176,12 @@ def allow_force(): """ return pecan.request.version.minor >= ( versions.VERSIONS.MINOR_2_FORCE.value) + + +def allow_list_datamodel(): + """Check if we should support list data model API. + + Version 1.3 of the API added support to list data model. + """ + return pecan.request.version.minor >= ( + versions.VERSIONS.MINOR_3_DATAMODEL.value) diff --git a/watcher/api/controllers/v1/versions.py b/watcher/api/controllers/v1/versions.py index eee1eb436..5ee04307a 100644 --- a/watcher/api/controllers/v1/versions.py +++ b/watcher/api/controllers/v1/versions.py @@ -21,7 +21,8 @@ class VERSIONS(enum.Enum): MINOR_0_ROCKY = 0 # v1.0: corresponds to Rocky API MINOR_1_START_END_TIMING = 1 # v1.1: Add start/end timei for audit MINOR_2_FORCE = 2 # v1.2: Add force field to audit - MINOR_MAX_VERSION = 2 + MINOR_3_DATAMODEL = 3 # v1.3: Add list datamodel API + MINOR_MAX_VERSION = 3 # This is the version 1 API BASE_VERSION = 1 diff --git a/watcher/tests/api/test_root.py b/watcher/tests/api/test_root.py index 0e9676be6..4a246310d 100644 --- a/watcher/tests/api/test_root.py +++ b/watcher/tests/api/test_root.py @@ -27,8 +27,9 @@ class TestRoot(base.FunctionalTest): class TestV1Root(base.FunctionalTest): - def test_get_v1_root(self): - data = self.get_json('/') + def test_get_v1_root_all(self): + data = self.get_json( + '/', headers={'OpenStack-API-Version': 'infra-optim 1.3'}) self.assertEqual('v1', data['id']) # Check fields are not empty for f in data.keys(): @@ -43,3 +44,21 @@ class TestV1Root(base.FunctionalTest): self.assertIn({'type': 'application/vnd.openstack.watcher.v1+json', 'base': 'application/json'}, data['media_types']) + + def test_get_v1_root_without_datamodel(self): + data = self.get_json( + '/', headers={'OpenStack-API-Version': 'infra-optim 1.2'}) + self.assertEqual('v1', data['id']) + # Check fields are not empty + for f in data.keys(): + self.assertNotIn(f, ['', []]) + # Check if all known resources are present and there are no extra ones. + not_resources = ('id', 'links', 'media_types') + actual_resources = tuple(set(data.keys()) - set(not_resources)) + expected_resources = ('audit_templates', 'audits', 'actions', + 'action_plans', 'scoring_engines', + 'services') + self.assertEqual(sorted(expected_resources), sorted(actual_resources)) + + self.assertIn({'type': 'application/vnd.openstack.watcher.v1+json', + 'base': 'application/json'}, data['media_types']) diff --git a/watcher/tests/api/v1/test_data_model.py b/watcher/tests/api/v1/test_data_model.py index 1ab999b37..ceca97578 100644 --- a/watcher/tests/api/v1/test_data_model.py +++ b/watcher/tests/api/v1/test_data_model.py @@ -32,9 +32,18 @@ class TestListDataModel(api_base.FunctionalTest): self.addCleanup(p_dcapi.stop) def test_get_all(self): - response = self.get_json('/data_model/?data_model_type=compute') + response = self.get_json( + '/data_model/?data_model_type=compute', + headers={'OpenStack-API-Version': 'infra-optim 1.3'}) self.assertEqual('fake_response_value', response) + def test_get_all_not_acceptable(self): + response = self.get_json( + '/data_model/?data_model_type=compute', + headers={'OpenStack-API-Version': 'infra-optim 1.2'}, + expect_errors=True) + self.assertEqual(406, response.status_int) + class TestDataModelPolicyEnforcement(api_base.FunctionalTest): @@ -60,6 +69,7 @@ class TestDataModelPolicyEnforcement(api_base.FunctionalTest): self._common_policy_check( "data_model:get_all", self.get_json, "/data_model/?data_model_type=compute", + headers={'OpenStack-API-Version': 'infra-optim 1.3'}, expect_errors=True)