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:
parent
624b0dbaec
commit
fc382cc987
|
@ -15,6 +15,7 @@
|
|||
"clusters:action": "",
|
||||
"clusters:update": "",
|
||||
"clusters:collect": "",
|
||||
"clusters:operation": "",
|
||||
"profiles:index": "",
|
||||
"profiles:create": "",
|
||||
"profiles:get": "",
|
||||
|
|
|
@ -38,3 +38,4 @@ it can be used by both users and developers.
|
|||
---
|
||||
|
||||
Added ``profile_type_ops`` API.
|
||||
Added ``cluster_operation`` API.
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue