API layer support to cluster-operation API

This adds the API layer support to cluster-operation call.

Change-Id: I76d6ad74a6d3feb321f14d415bce65d00d54c03c
This commit is contained in:
tengqm 2016-12-26 07:53:54 -05:00
parent 624b0dbaec
commit fc382cc987
5 changed files with 114 additions and 0 deletions

View File

@ -15,6 +15,7 @@
"clusters:action": "",
"clusters:update": "",
"clusters:collect": "",
"clusters:operation": "",
"profiles:index": "",
"profiles:create": "",
"profiles:get": "",

View File

@ -38,3 +38,4 @@ it can be used by both users and developers.
---
Added ``profile_type_ops`` API.
Added ``cluster_operation`` API.

View File

@ -287,6 +287,32 @@ class ClusterController(wsgi.Controller):
obj = util.parse_request('ClusterCollectRequest', req, params)
return self.rpc_client.call2(req.context, 'cluster_collect', obj)
@wsgi.Controller.api_version('1.4')
@util.policy_enforce
def operation(self, req, cluster_id, body=None):
"""Perform specified operation on the specified cluster."""
body = body or {}
if len(body) < 1:
raise exc.HTTPBadRequest(_('No operation specified'))
if len(body) > 1:
raise exc.HTTPBadRequest(_('Multiple operations specified'))
operation = list(body.keys())[0]
params = {
'identity': cluster_id,
'operation': operation,
'params': body[operation].get('params', {}),
'filters': body[operation].get('filters', {}),
}
obj = util.parse_request('ClusterOperationRequest', req, params)
res = self.rpc_client.call2(req.context, 'cluster_op', obj)
location = {'location': '/actions/%s' % res['action']}
res.update(location)
return res
@util.policy_enforce
def delete(self, req, cluster_id):
params = {'identity': cluster_id}

View File

@ -171,6 +171,11 @@ class API(wsgi.Router):
action="delete",
conditions={'method': 'DELETE'},
success=202)
sub_mapper.connect("cluster_operation",
"/clusters/{cluster_id}/ops",
action="operation",
conditions={'method': 'POST'},
success=202)
# Nodes
res = wsgi.Resource(nodes.NodeController(conf))

View File

@ -1349,6 +1349,87 @@ class ClusterControllerTest(shared.ControllerTest, base.SenlinTestCase):
self.assertIn('403 Forbidden', six.text_type(resp))
self.assertEqual(0, mock_call.call_count)
@mock.patch.object(util, 'parse_request')
@mock.patch.object(rpc_client.EngineClient, 'call2')
def test_operation(self, mock_call, mock_parse, mock_enforce):
self._mock_enforce_setup(mock_enforce, 'operation', True)
cid = 'aaaa-bbbb-cccc'
body = {
'dance': {
'params': {
'style': 'tango'
},
'filters': {
'role': 'slave'
}
}
}
req = self._post('/clusters/aaaa-bbbb-cccc/ops',
jsonutils.dumps(body), version='1.4')
eng_resp = {'action': 'ACTION_ID'}
mock_call.return_value = eng_resp
obj = mock.Mock()
mock_parse.return_value = obj
resp = self.controller.operation(req, cluster_id=cid, body=body)
self.assertEqual(eng_resp, resp)
mock_call.assert_called_once_with(req.context, 'cluster_op', obj)
@mock.patch.object(rpc_client.EngineClient, 'call2')
def test_operation_version_mismatch(self, mock_call, mock_enforce):
cid = 'aaaa-bbbb-cccc'
body = {'dance': {}}
req = self._post('/clusters/aaaa-bbbb/ops', jsonutils.dumps(body),
version='1.1')
ex = self.assertRaises(senlin_exc.MethodVersionNotFound,
self.controller.operation,
req, cluster_id=cid, body=body)
self.assertEqual(0, mock_call.call_count)
self.assertEqual('API version 1.1 is not supported on this method.',
six.text_type(ex))
def test_operation_no_operations(self, mock_enforce):
self._mock_enforce_setup(mock_enforce, 'operation', True)
cid = 'aaaa-bbbb-cccc'
body = {}
req = self._post('/clusters/aaaa-bbbb-cccc/ops',
jsonutils.dumps(body), version='1.4')
ex = self.assertRaises(exc.HTTPBadRequest,
self.controller.operation,
req, cluster_id=cid, body=body)
self.assertEqual("No operation specified", six.text_type(ex))
def test_operation_multi_operations(self, mock_enforce):
self._mock_enforce_setup(mock_enforce, 'operation', True)
cid = 'aaaa-bbbb-cccc'
body = {'dance': {}, 'sing': {}}
req = self._post('/clusters/aaaa-bbbb-cccc/ops',
jsonutils.dumps(body), version='1.4')
ex = self.assertRaises(exc.HTTPBadRequest,
self.controller.operation,
req, cluster_id=cid, body=body)
self.assertEqual("Multiple operations specified", six.text_type(ex))
def test_cluster_operation_err_denied_policy(self, mock_enforce):
self._mock_enforce_setup(mock_enforce, 'operation', False)
body = {'someoperation': {}}
req = self._post('/clusters/abc/ops', jsonutils.dumps(body),
version='1.4')
resp = shared.request_with_middleware(fault.FaultWrapper,
self.controller.operation,
req, cluster_id='abc', body=body)
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, 'call2')
def test_delete(self, mock_call, mock_parse, mock_enforce):