Revise cluster/node check action records design

This patch revises cluster/node check action records design, the purpose
is to avoid creating too many action records when we do cluster/node
checking periodically.
And we will not delete check action records which are  from manual operation.

Implements: blueprint improve-action-event

Change-Id: Ia356a6a7e5f3afca5a10b6ca5961c9119012834c
This commit is contained in:
jonnary 2017-02-20 17:49:02 +08:00 committed by XueFeng Liu
parent 3c15621dd4
commit 67647dade6
5 changed files with 143 additions and 7 deletions

View File

@ -620,11 +620,20 @@ class ClusterAction(base.Action):
reason = _('Cluster checking completed.')
for node in self.entity.nodes:
node_id = node.id
need_delete = self.inputs.get('delete_check_action', False)
# delete some records of NODE_CHECK
if need_delete:
ao.Action.delete_by_target(
self.context, node_id, action=[consts.NODE_CHECK],
status=[consts.ACTION_SUCCEEDED, consts.ACTION_FAILED])
name = 'node_check_%s' % node_id[:8]
action_id = base.Action.create(
self.context, node_id, consts.NODE_CHECK,
name='node_check_%s' % node_id[:8],
self.context, node_id, consts.NODE_CHECK, name=name,
cause=consts.CAUSE_DERIVED,
inputs=self.inputs
)
child.append(action_id)
if child:

View File

@ -247,8 +247,10 @@ class HealthManager(service.Service):
ctx = context.get_service_context(user=cluster.user,
project=cluster.project)
params = {'delete_check_action': True}
try:
req = objects.ClusterCheckRequest(identity=cluster_id)
req = objects.ClusterCheckRequest(identity=cluster_id,
params=params)
action = self.rpc_client.call(ctx, 'cluster_check', req)
except Exception as ex:
LOG.warning("Failed in triggering 'cluster_check' RPC for "

View File

@ -1395,14 +1395,22 @@ class EngineService(service.Service):
ctx.user = db_cluster.user
ctx.project = db_cluster.project
params = {
kwargs = {
'name': 'cluster_check_%s' % db_cluster.id[:8],
'cause': consts.CAUSE_RPC,
'status': action_mod.Action.READY,
'inputs': req.params if req.obj_attr_is_set('params') else {}
}
need_delete = kwargs['inputs'].get('delete_check_action', False)
# delete some records of CLUSTER_CHECK
if need_delete:
action_obj.Action.delete_by_target(
ctx, db_cluster.id, action=[consts.CLUSTER_CHECK],
status=[consts.ACTION_SUCCEEDED, consts.ACTION_FAILED])
action_id = action_mod.Action.create(ctx, db_cluster.id,
consts.CLUSTER_CHECK, **params)
consts.CLUSTER_CHECK,
**kwargs)
dispatcher.start_action()
LOG.info("Cluster check action queued: %s.", action_id)

View File

@ -63,10 +63,72 @@ class ClusterCheckTest(base.SenlinTestCase):
mock_action.assert_has_calls([
mock.call(action.context, 'NODE_1', 'NODE_CHECK',
name='node_check_NODE_1',
cause=consts.CAUSE_DERIVED),
cause=consts.CAUSE_DERIVED,
inputs={}),
mock.call(action.context, 'NODE_2', 'NODE_CHECK',
name='node_check_NODE_2',
cause=consts.CAUSE_DERIVED)
cause=consts.CAUSE_DERIVED,
inputs={})
])
mock_dep.assert_called_once_with(action.context,
['NODE_ACTION_1', 'NODE_ACTION_2'],
'CLUSTER_ACTION_ID')
mock_update.assert_has_calls([
mock.call(action.context, 'NODE_ACTION_1', {'status': 'READY'}),
mock.call(action.context, 'NODE_ACTION_2', {'status': 'READY'}),
])
mock_start.assert_called_once_with()
mock_wait.assert_called_once_with()
cluster.eval_status.assert_called_once_with(
action.context, consts.CLUSTER_CHECK)
@mock.patch.object(ao.Action, 'update')
@mock.patch.object(ab.Action, 'create')
@mock.patch.object(ao.Action, 'delete_by_target')
@mock.patch.object(dobj.Dependency, 'create')
@mock.patch.object(dispatcher, 'start_action')
@mock.patch.object(ca.ClusterAction, '_wait_for_dependents')
def test_do_check_need_delete(self, mock_wait, mock_start, mock_dep,
mock_delete, mock_action, mock_update,
mock_load):
node1 = mock.Mock(id='NODE_1')
node2 = mock.Mock(id='NODE_2')
cluster = mock.Mock(id='FAKE_ID', status='old status',
status_reason='old reason')
cluster.nodes = [node1, node2]
cluster.do_check.return_value = True
mock_load.return_value = cluster
mock_action.side_effect = ['NODE_ACTION_1', 'NODE_ACTION_2']
action = ca.ClusterAction('FAKE_CLUSTER', 'CLUSTER_CHECK', self.ctx,
inputs={'delete_check_action': True})
action.id = 'CLUSTER_ACTION_ID'
mock_wait.return_value = (action.RES_OK, 'Everything is Okay')
# do it
res_code, res_msg = action.do_check()
# assertions
self.assertEqual(action.RES_OK, res_code)
self.assertEqual('Cluster checking completed.', res_msg)
mock_load.assert_called_once_with(action.context, 'FAKE_CLUSTER')
cluster.do_check.assert_called_once_with(action.context)
mock_delete.assert_has_calls([
mock.call(action.context, 'NODE_1', action=['NODE_CHECK'],
status=['SUCCEEDED', 'FAILED']),
mock.call(action.context, 'NODE_2', action=['NODE_CHECK'],
status=['SUCCEEDED', 'FAILED'])
])
mock_action.assert_has_calls([
mock.call(action.context, 'NODE_1', 'NODE_CHECK',
name='node_check_NODE_1',
cause=consts.CAUSE_DERIVED,
inputs={'delete_check_action': True}),
mock.call(action.context, 'NODE_2', 'NODE_CHECK',
name='node_check_NODE_2',
cause=consts.CAUSE_DERIVED,
inputs={'delete_check_action': True})
])
mock_dep.assert_called_once_with(action.context,
['NODE_ACTION_1', 'NODE_ACTION_2'],
@ -127,6 +189,7 @@ class ClusterCheckTest(base.SenlinTestCase):
mock_action.assert_called_once_with(
action.context, 'NODE_1', 'NODE_CHECK',
name='node_check_NODE_1',
inputs={},
cause=consts.CAUSE_DERIVED,
)
mock_dep.assert_called_once_with(action.context, ['NODE_ACTION_ID'],

View File

@ -25,6 +25,7 @@ from senlin.engine.actions import base as am
from senlin.engine import dispatcher
from senlin.engine import node as nm
from senlin.engine import service
from senlin.objects import action as ao
from senlin.objects import base as obj_base
from senlin.objects import cluster as co
from senlin.objects import cluster_policy as cpo
@ -1396,6 +1397,35 @@ class ClusterTest(base.SenlinTestCase):
)
notify.assert_called_once_with()
@mock.patch.object(ao.Action, 'delete_by_target')
@mock.patch.object(am.Action, 'create')
@mock.patch.object(co.Cluster, 'find')
@mock.patch.object(dispatcher, 'start_action')
def test_cluster_check_with_delete(self, notify, mock_find, mock_action,
mock_delete):
x_cluster = mock.Mock(id='CID', user='USER', project='PROJECT')
mock_find.return_value = x_cluster
mock_action.return_value = 'ACTION_ID'
req = orco.ClusterCheckRequest(identity='C1',
params={'delete_check_action': True})
res = self.eng.cluster_check(self.ctx, req.obj_to_primitive())
self.assertEqual({'action': 'ACTION_ID'}, res)
mock_find.assert_called_once_with(self.ctx, 'C1')
mock_delete.assert_called_once_with(
self.ctx, 'CID', action=['CLUSTER_CHECK'],
status=['SUCCEEDED', 'FAILED']
)
mock_action.assert_called_once_with(
self.ctx, 'CID', consts.CLUSTER_CHECK,
name='cluster_check_CID',
cause=consts.CAUSE_RPC,
status=am.Action.READY,
inputs={'delete_check_action': True},
)
notify.assert_called_once_with()
@mock.patch.object(am.Action, 'create')
@mock.patch.object(co.Cluster, 'find')
@mock.patch.object(dispatcher, 'start_action')
@ -1419,6 +1449,30 @@ class ClusterTest(base.SenlinTestCase):
)
notify.assert_called_once_with()
@mock.patch.object(am.Action, 'create')
@mock.patch.object(co.Cluster, 'find')
@mock.patch.object(dispatcher, 'start_action')
def test_cluster_check_project_is_none(self, notify, mock_find,
mock_action):
x_cluster = mock.Mock(id='CID', user='USER')
mock_find.return_value = x_cluster
mock_action.return_value = 'ACTION_ID'
req = orco.ClusterCheckRequest(identity='C1')
result = self.eng.cluster_check(self.ctx, req.obj_to_primitive())
self.assertIsNotNone(x_cluster.user)
self.assertEqual({'action': 'ACTION_ID'}, result)
mock_find.assert_called_once_with(self.ctx, 'C1')
mock_action.assert_called_once_with(
self.ctx, 'CID', consts.CLUSTER_CHECK,
name='cluster_check_CID',
cause=consts.CAUSE_RPC,
status=am.Action.READY,
inputs={},
)
notify.assert_called_once_with()
@mock.patch.object(co.Cluster, 'find')
def test_cluster_check_cluster_not_found(self, mock_find):
mock_find.side_effect = exc.ResourceNotFound(type='cluster',