Browse Source

Merge "Release locks when action is cancelled"

changes/96/768296/1
Zuul 6 months ago
committed by Gerrit Code Review
parent
commit
0c1525c64a
4 changed files with 32 additions and 18 deletions
  1. +7
    -5
      senlin/engine/actions/base.py
  2. +6
    -0
      senlin/engine/actions/cluster_action.py
  3. +12
    -0
      senlin/engine/actions/node_action.py
  4. +7
    -13
      senlin/tests/unit/engine/actions/test_action_base.py

+ 7
- 5
senlin/engine/actions/base.py View File

@ -379,14 +379,11 @@ class Action(object):
:raises: `ActionImmutable` if the action is in an unchangeable state
"""
expected = (self.INIT, self.WAITING, self.READY, self.RUNNING,
self.WAITING_LIFECYCLE_COMPLETION)
if self.status not in expected:
raise exception.ActionImmutable(id=self.id[:8], expected=expected,
actual=self.status)
LOG.debug('Forcing action %s to cancel.', self.id)
self.set_status(self.RES_CANCEL, 'Action execution force cancelled')
self.release_lock()
depended = dobj.Dependency.get_depended(self.context, self.id)
if not depended:
return
@ -400,6 +397,7 @@ class Action(object):
LOG.debug('Forcing action %s to cancel.', action.id)
action.set_status(action.RES_CANCEL,
'Action execution force cancelled')
action.release_lock()
def execute(self, **kwargs):
"""Execute the action.
@ -411,6 +409,10 @@ class Action(object):
"""
raise NotImplementedError
def release_lock(self):
"""Release the lock associated with the action."""
raise NotImplementedError
def set_status(self, result, reason=None):
"""Set action status based on return value from execute."""


+ 6
- 0
senlin/engine/actions/cluster_action.py View File

@ -1224,6 +1224,12 @@ class ClusterAction(base.Action):
"""Handler to cancel the execution of action."""
return self.RES_OK
def release_lock(self):
"""Handler to release the lock."""
senlin_lock.cluster_lock_release(self.target, self.id,
senlin_lock.CLUSTER_SCOPE)
return self.RES_OK
def CompleteLifecycleProc(context, action_id):
"""Complete lifecycle process."""


+ 12
- 0
senlin/engine/actions/node_action.py View File

@ -283,3 +283,15 @@ class NodeAction(base.Action):
def cancel(self):
"""Handler for cancelling the action."""
return self.RES_OK
def release_lock(self):
"""Handler to release the lock."""
senlin_lock.node_lock_release(self.entity.id, self.id)
# only release cluster lock if it was locked as part of this
# action (i.e. it's a user intiated action aka CAUSE_RPC from
# senlin API and a not a CAUSED_DERIVED)
if self.cause == consts.CAUSE_RPC:
senlin_lock.cluster_lock_release(self.entity.cluster_id, self.id,
senlin_lock.NODE_SCOPE)
return self.RES_OK

+ 7
- 13
senlin/tests/unit/engine/actions/test_action_base.py View File

@ -635,6 +635,7 @@ class ActionBaseTest(base.SenlinTestCase):
action = ab.Action(OBJID, 'OBJECT_ACTION', self.ctx, id=ACTION_ID)
action.load = mock.Mock()
action.set_status = mock.Mock()
action.release_lock = mock.Mock()
mock_dobj.return_value = None
action.status = action.RUNNING
@ -643,19 +644,23 @@ class ActionBaseTest(base.SenlinTestCase):
action.load.assert_not_called()
action.set_status.assert_called_once_with(
action.RES_CANCEL, 'Action execution force cancelled')
self.assertEqual(1, action.release_lock.call_count)
@mock.patch.object(dobj.Dependency, 'get_depended')
def test_force_cancel_children(self, mock_dobj):
action = ab.Action(OBJID, 'OBJECT_ACTION', self.ctx, id=ACTION_ID)
child_status_mock = mock.Mock()
child_release_mock = mock.Mock()
children = []
for child_id in CHILD_IDS:
child = ab.Action(OBJID, 'OBJECT_ACTION', self.ctx, id=child_id)
child.status = child.WAITING_LIFECYCLE_COMPLETION
child.set_status = child_status_mock
child.release_lock = child_release_mock
children.append(child)
mock_dobj.return_value = CHILD_IDS
action.set_status = mock.Mock()
action.release_lock = mock.Mock()
action.load = mock.Mock()
action.load.side_effect = children
@ -664,20 +669,9 @@ class ActionBaseTest(base.SenlinTestCase):
mock_dobj.assert_called_once_with(action.context, action.id)
self.assertEqual(2, child_status_mock.call_count)
self.assertEqual(2, child_release_mock.call_count)
self.assertEqual(2, action.load.call_count)
@mock.patch.object(dobj.Dependency, 'get_depended')
def test_force_cancel_immutable(self, mock_dobj):
action = ab.Action(OBJID, 'OBJECT_ACTION', self.ctx, id=ACTION_ID)
action.load = mock.Mock()
action.set_status = mock.Mock()
mock_dobj.return_value = None
action.status = action.FAILED
self.assertRaises(exception.ActionImmutable, action.force_cancel)
action.load.assert_not_called()
action.set_status.assert_not_called()
self.assertEqual(1, action.release_lock.call_count)
def test_execute_default(self):
action = ab.Action.__new__(DummyAction, OBJID, 'BOOM', self.ctx)


Loading…
Cancel
Save