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:
parent
3c15621dd4
commit
67647dade6
|
@ -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:
|
||||
|
|
|
@ -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 "
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue