Return support status in profile type show

This patch enables senlin engine/api to return support status in profile
type show. This feature needs to be enabled by API micro-version
1.5.

Change-Id: I7a4bcdaaa857d776d926bbbfde3c517f05d8935b
This commit is contained in:
miaohb 2017-01-11 13:25:01 +08:00
parent cabc9c61e2
commit c5ed38633a
9 changed files with 168 additions and 2 deletions

View File

@ -1043,7 +1043,9 @@ profile_type:
in: body
required: True
description: |
A structured description of a profile type.
A structured description of a profile type. Since API micro-version 1.5,
a "support_status" property is returned which contains a list
of support status changes.
profile_type_name:
type: string

View File

@ -97,9 +97,18 @@ Response Parameters
Response Example
----------------
For API microversion lower than 1.5, the response only contains the name and
schema of the specified profile type:
.. literalinclude:: samples/profile-type-show-response.json
:language: javascript
Since API microversion 1.5, the response contains the support status of the
specified profile type:
.. literalinclude:: samples/profile-type-show-response-v1.5.json
:language: javascript
List profile type operations
============================

View File

@ -0,0 +1,69 @@
{
"profile_type": {
"name": "os.heat.stack-1.0",
"schema": {
"context": {
"default": {},
"description": "A dictionary for specifying the customized context for stack operations",
"readonly": false,
"required": false,
"type": "Map",
"updatable": false
},
"disable_rollback": {
"default": true,
"description": "A boolean specifying whether a stack operation can be rolled back.",
"readonly": false,
"required": false,
"type": "Boolean",
"updatable": true
},
"environment": {
"default": {},
"description": "A map that specifies the environment used for stack operations.",
"readonly": false,
"required": false,
"type": "Map",
"updatable": true
},
"files": {
"default": {},
"description": "Contents of files referenced by the template, if any.",
"readonly": false,
"required": false,
"type": "Map",
"updatable": true
},
"parameters": {
"default": {},
"description": "Parameters to be passed to Heat for stack operations.",
"readonly": false,
"required": false,
"type": "Map",
"updatable": true
},
"template": {
"description": "Heat stack template.",
"readonly": false,
"required": true,
"type": "Map",
"updatable": true
},
"timeout": {
"description": "A integer that specifies the number of minutes that a stack operation times out.",
"readonly": false,
"required": false,
"type": "Integer",
"updatable": true
}
}
"support_status": {
"1.0": [
{
"status": "SUPPORTED",
"since": "2016.04"
}
]
}
}
}

View File

@ -65,3 +65,5 @@ it can be used by both users and developers.
- Added ``support_status`` to profile type list.
- Added ``support_status`` to policy type list.
- Added ``support_status`` to profile type show.

View File

@ -44,6 +44,11 @@ class ProfileTypeController(wsgi.Controller):
obj = util.parse_request(
'ProfileTypeGetRequest', req, {'type_name': type_name})
content = self.rpc_client.call(req.context, 'profile_type_get', obj)
key = 'support_status'
if req.version_request <= vr.APIVersionRequest("1.4"):
# We return support_status from 1.5
if key in content:
content.pop(key)
return {'profile_type': content}
@wsgi.Controller.api_version('1.4')

View File

@ -285,7 +285,8 @@ class EngineService(service.Service):
return {
'name': req.type_name,
'schema': data
'schema': data,
'support_status': profile.VERSIONS
}
@request_context

View File

@ -13,10 +13,12 @@
from tempest.lib import decorators
from senlin.tests.tempest.api import base
from senlin.tests.tempest.common import utils
class TestProfileTypeShow(base.BaseSenlinAPITest):
@utils.api_microversion('1.4')
@decorators.idempotent_id('198165b3-1c1f-4801-8918-90c1adbf57c8')
def test_profile_type_show(self):
res = self.client.get_obj('profile-types', 'os.nova.server-1.0')
@ -28,3 +30,17 @@ class TestProfileTypeShow(base.BaseSenlinAPITest):
for key in ['name', 'schema']:
self.assertIn(key, profile_type)
self.assertEqual('os.nova.server-1.0', profile_type['name'])
@utils.api_microversion('1.5')
@decorators.idempotent_id('778d41df-0ce0-421f-98e5-2efdcec6d995')
def test_profile_type_show_v1_5(self):
res = self.client.get_obj('profile-types', 'os.nova.server-1.0')
# Verify resp of profile type show API
self.assertEqual(200, res['status'])
self.assertIsNotNone(res['body'])
profile_type = res['body']
for key in ['name', 'schema', 'support_status']:
self.assertIn(key, profile_type)
self.assertEqual('os.nova.server-1.0', profile_type['name'])
self.assertIsNotNone(profile_type['support_status'])

View File

@ -116,6 +116,64 @@ class ProfileTypeControllerTest(shared.ControllerTest, base.SenlinTestCase):
self.assertEqual(403, resp.status_int)
self.assertIn('403 Forbidden', six.text_type(resp))
@mock.patch.object(util, 'parse_request')
@mock.patch.object(rpc_client.EngineClient, 'call')
def test_get_old_version(self, mock_call, mock_parse, mock_enforce):
self._mock_enforce_setup(mock_enforce, 'get', True)
type_name = 'SimpleProfile'
req = self._get('/profile_types/%(type)s' % {'type': type_name},
version='1.3')
engine_response = {
'name': type_name,
'schema': {
'Foo': {'type': 'String', 'required': False},
'Bar': {'type': 'Integer', 'required': False},
}
}
mock_call.return_value = engine_response
obj = mock.Mock()
mock_parse.return_value = obj
response = self.controller.get(req, type_name=type_name)
self.assertEqual(engine_response, response['profile_type'])
mock_parse.assert_called_once_with(
'ProfileTypeGetRequest', req, {'type_name': type_name})
mock_call.assert_called_once_with(
req.context, 'profile_type_get', mock.ANY)
@mock.patch.object(util, 'parse_request')
@mock.patch.object(rpc_client.EngineClient, 'call')
def test_get_new_version(self, mock_call, mock_parse, mock_enforce):
self._mock_enforce_setup(mock_enforce, 'get', True)
type_name = 'SimpleProfile'
req = self._get('/profile_types/%(type)s' % {'type': type_name},
version='1.5')
engine_response = {
'name': type_name,
'schema': {
'Foo': {'type': 'String', 'required': False},
'Bar': {'type': 'Integer', 'required': False},
},
'support_status': {"1.0": [{"since": "2016.04",
"status": "supported"}]}
}
mock_call.return_value = engine_response
obj = mock.Mock()
mock_parse.return_value = obj
response = self.controller.get(req, type_name=type_name)
self.assertEqual(engine_response, response['profile_type'])
mock_parse.assert_called_once_with('ProfileTypeGetRequest', req,
{'type_name': type_name})
mock_call.assert_called_once_with(
req.context, 'profile_type_get', mock.ANY)
@mock.patch.object(util, 'parse_request')
@mock.patch.object(rpc_client.EngineClient, 'call')
def test_profile_type_get(self, mock_call, mock_parse, mock_enforce):

View File

@ -47,6 +47,8 @@ class ProfileTypeTest(base.SenlinTestCase):
x_env = mock.Mock()
x_profile_type = mock.Mock()
x_profile_type.get_schema.return_value = {'foo': 'bar'}
x_profile_type.VERSIONS = {'1.0': [{'status': 'supported',
'since': '2016.04'}]}
x_env.get_profile.return_value = x_profile_type
mock_env.return_value = x_env
@ -57,6 +59,8 @@ class ProfileTypeTest(base.SenlinTestCase):
{
'name': 'FAKE_TYPE',
'schema': {'foo': 'bar'},
'support_status': {'1.0': [{'status': 'supported',
'since': '2016.04'}]}
},
result)
mock_env.assert_called_once_with()