From 7754f7e75b1f450f964d8ef1fd22f52a23a7e50e Mon Sep 17 00:00:00 2001 From: jonnary Date: Thu, 10 Nov 2016 21:19:20 +0800 Subject: [PATCH] Api support for action_list2 This patch adds API support for action_list2. Change-Id: Id65c6ffc58932d2860b422bbb0183d5d0941154e --- senlin/api/openstack/v1/actions.py | 39 +++-- .../unit/api/openstack/v1/test_actions.py | 134 +++++++++--------- 2 files changed, 87 insertions(+), 86 deletions(-) diff --git a/senlin/api/openstack/v1/actions.py b/senlin/api/openstack/v1/actions.py index a295fee4b..ad317d383 100644 --- a/senlin/api/openstack/v1/actions.py +++ b/senlin/api/openstack/v1/actions.py @@ -11,6 +11,8 @@ # License for the specific language governing permissions and limitations # under the License. +import jsonschema +import six from webob import exc from senlin.api.common import util @@ -18,6 +20,8 @@ from senlin.api.common import wsgi from senlin.common import consts from senlin.common.i18n import _ from senlin.common import utils +from senlin.objects import base as obj_base +from senlin.objects.requests import actions as vora class ActionData(object): @@ -56,40 +60,35 @@ class ActionController(wsgi.Controller): @util.policy_enforce def index(self, req): - filter_whitelist = { + whitelist = { consts.ACTION_NAME: 'mixed', consts.ACTION_TARGET: 'mixed', consts.ACTION_ACTION: 'mixed', consts.ACTION_STATUS: 'mixed', - } - param_whitelist = { consts.PARAM_LIMIT: 'single', consts.PARAM_MARKER: 'single', consts.PARAM_SORT: 'single', consts.PARAM_GLOBAL_PROJECT: 'single', } for key in req.params.keys(): - if (key not in param_whitelist.keys() and key not in - filter_whitelist.keys()): + if key not in whitelist.keys(): raise exc.HTTPBadRequest(_('Invalid parameter %s') % key) - params = util.get_allowed_params(req.params, param_whitelist) - filters = util.get_allowed_params(req.params, filter_whitelist) + params = util.get_allowed_params(req.params, whitelist) - key = consts.PARAM_LIMIT - if key in params: - params[key] = utils.parse_int_param(key, params[key]) + project_safe = not utils.parse_bool_param( + consts.PARAM_GLOBAL_PROJECT, + params.pop(consts.PARAM_GLOBAL_PROJECT, False)) + params['project_safe'] = project_safe - key = consts.PARAM_GLOBAL_PROJECT - if key in params: - global_project = utils.parse_bool_param(key, params[key]) - params.pop(key) - params['project_safe'] = not global_project + try: + norm_req = obj_base.SenlinObject.normalize_req( + 'ActionListRequest', params) + obj = vora.ActionListRequest.obj_from_primitive(norm_req) + jsonschema.validate(norm_req, obj.to_json_schema()) + except (ValueError) as ex: + raise exc.HTTPBadRequest(six.text_type(ex)) - if not filters: - filters = None - - actions = self.rpc_client.action_list(req.context, filters=filters, - **params) + actions = self.rpc_client.call2(req.context, "action_list2", obj) return {'actions': actions} diff --git a/senlin/tests/unit/api/openstack/v1/test_actions.py b/senlin/tests/unit/api/openstack/v1/test_actions.py index af28002b3..7d9cea8bb 100644 --- a/senlin/tests/unit/api/openstack/v1/test_actions.py +++ b/senlin/tests/unit/api/openstack/v1/test_actions.py @@ -18,6 +18,7 @@ from senlin.api.middleware import fault from senlin.api.openstack.v1 import actions from senlin.common import exception as senlin_exc from senlin.common import policy +from senlin.objects.requests import actions as vora from senlin.rpc import client as rpc_client from senlin.tests.unit.api import shared from senlin.tests.unit.common import base @@ -62,44 +63,50 @@ class ActionControllerTest(shared.ControllerTest, base.SenlinTestCase): } ] - mock_call = self.patchobject(rpc_client.EngineClient, 'call', + mock_call = self.patchobject(rpc_client.EngineClient, 'call2', return_value=engine_resp) result = self.controller.index(req) - - default_args = {'limit': None, 'marker': None, 'sort': None, - 'filters': None, 'project_safe': True} - - mock_call.assert_called_with(req.context, - ('action_list', default_args)) - expected = {'actions': engine_resp} self.assertEqual(expected, result) + mock_call.assert_called_with(req.context, + 'action_list2', mock.ANY) - @mock.patch.object(rpc_client.EngineClient, 'call') + request = mock_call.call_args[0][2] + self.assertIsInstance(request, vora.ActionListRequest) + self.assertTrue(request.project_safe) + + @mock.patch.object(rpc_client.EngineClient, 'call2') def test_action_index_whitelists_params(self, mock_call, mock_enforce): self._mock_enforce_setup(mock_enforce, 'index', True) + marker_uuid = '8216a86c-1bdc-442e-b493-329385d37cbc' params = { + 'name': 'NODE_CREATE', + 'status': 'SUCCEEDED', 'limit': 10, - 'marker': 'fake marker', - 'sort': 'fake sorting option', + 'marker': marker_uuid, + 'sort': 'status', 'global_project': True, } req = self._get('/actions', params=params) + mock_call.return_value = [] - self.controller.index(req) + result = self.controller.index(req) + expected = {'actions': []} + self.assertEqual(expected, result) + mock_call.assert_called_with(req.context, + 'action_list2', mock.ANY) + request = mock_call.call_args[0][2] + self.assertIsInstance(request, vora.ActionListRequest) + self.assertEqual(['NODE_CREATE'], request.name) + self.assertEqual(['SUCCEEDED'], request.status) + self.assertEqual(10, request.limit) + self.assertEqual(marker_uuid, request.marker) + self.assertEqual('status', request.sort) + self.assertFalse(request.project_safe) - rpc_call_args, _ = mock_call.call_args - engine_args = rpc_call_args[1][1] - - self.assertEqual(5, len(engine_args)) - self.assertIn('limit', engine_args) - self.assertIn('marker', engine_args) - self.assertIn('sort', engine_args) - self.assertIn('project_safe', engine_args) - - @mock.patch.object(rpc_client.EngineClient, 'call') + @mock.patch.object(rpc_client.EngineClient, 'call2') def test_action_index_whitelists_invalid_params(self, mock_call, mock_enforce): self._mock_enforce_setup(mock_enforce, 'index', True) @@ -114,58 +121,53 @@ class ActionControllerTest(shared.ControllerTest, base.SenlinTestCase): str(ex)) self.assertFalse(mock_call.called) - @mock.patch.object(rpc_client.EngineClient, 'call') + @mock.patch.object(rpc_client.EngineClient, 'call2') def test_action_index_limit_not_int(self, mock_call, mock_enforce): self._mock_enforce_setup(mock_enforce, 'index', True) params = {'limit': 'not-int'} req = self._get('/actions', params=params) - ex = self.assertRaises(senlin_exc.InvalidParameter, - self.controller.index, req) - - self.assertEqual("Invalid value 'not-int' specified for 'limit'", - six.text_type(ex)) - self.assertFalse(mock_call.called) - - @mock.patch.object(rpc_client.EngineClient, 'call') - def test_action_index_whitelist_filter_params(self, mock_call, - mock_enforce): - self._mock_enforce_setup(mock_enforce, 'index', True) - params = { - 'name': 'fake name', - 'target': '1111-2222-3333', - 'action': 'CLUSTER_CREATE', - 'status': 'SUCCEEDED' - } - req = self._get('/actions', params=params) - mock_call.return_value = [] - - self.controller.index(req) - - rpc_call_args, _ = mock_call.call_args - engine_args = rpc_call_args[1][1] - self.assertIn('filters', engine_args) - - filters = engine_args['filters'] - self.assertEqual(4, len(filters)) - self.assertIn('name', filters) - self.assertIn('action', filters) - self.assertIn('target', filters) - self.assertIn('status', filters) - - @mock.patch.object(rpc_client.EngineClient, 'call') - def test_action_index_whitelist_filter_invalid_params(self, mock_call, - mock_enforce): - self._mock_enforce_setup(mock_enforce, 'index', True) - params = { - 'balrog': 'you shall not pass!' - } - req = self._get('/actions', params=params) ex = self.assertRaises(exc.HTTPBadRequest, self.controller.index, req) - self.assertEqual("Invalid parameter balrog", - str(ex)) + self.assertEqual("invalid literal for int() with base 10: 'not-int'", + six.text_type(ex)) + self.assertFalse(mock_call.called) + + @mock.patch.object(rpc_client.EngineClient, 'call2') + def test_action_index_global_project_true(self, mock_call, mock_enforce): + self._mock_enforce_setup(mock_enforce, 'index', True) + params = {'global_project': 'True'} + req = self._get('/actions', params=params) + + self.controller.index(req) + + request = mock_call.call_args[0][2] + self.assertFalse(request.project_safe) + + @mock.patch.object(rpc_client.EngineClient, 'call2') + def test_action_index_global_project_false(self, mock_call, mock_enforce): + self._mock_enforce_setup(mock_enforce, 'index', True) + params = {'global_project': 'False'} + req = self._get('/actions', params=params) + + self.controller.index(req) + + request = mock_call.call_args[0][2] + self.assertTrue(request.project_safe) + + @mock.patch.object(rpc_client.EngineClient, 'call2') + def test_action_index_global_project_not_bool(self, mock_call, + mock_enforce): + self._mock_enforce_setup(mock_enforce, 'index', True) + params = {'global_project': 'No'} + req = self._get('/actions', params=params) + + ex = self.assertRaises(senlin_exc.InvalidParameter, + self.controller.index, req) + + self.assertEqual("Invalid value 'No' specified for 'global_project'", + six.text_type(ex)) self.assertFalse(mock_call.called) def test_action_index_denied_policy(self, mock_enforce):