Merge "Api support for action_list2"
This commit is contained in:
commit
62dc971fa6
|
@ -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}
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue