From 6f43f2b003986899d4d5de4f40c6760379136c5e Mon Sep 17 00:00:00 2001
From: licanwei
Date: Fri, 29 Nov 2019 11:52:42 +0800
Subject: [PATCH] Add a new microversion for data model API
microversion 1.3 for list data model API
Change-Id: Ibf8774a48c3d13ca9762bd5319f5e1ce2ed82b2f
Closes-Bug: #1854121
---
.../controllers/rest_api_version_history.rst | 4 ++++
watcher/api/controllers/v1/__init__.py | 16 +++++++------
watcher/api/controllers/v1/data_model.py | 3 +++
watcher/api/controllers/v1/utils.py | 9 ++++++++
watcher/api/controllers/v1/versions.py | 3 ++-
watcher/tests/api/test_root.py | 23 +++++++++++++++++--
watcher/tests/api/v1/test_data_model.py | 12 +++++++++-
7 files changed, 59 insertions(+), 11 deletions(-)
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)