Add a Resource plugin API for cancelling actions

Allow a resource type plugin to provide a handle_*_cancel() method to
handle cancelling an in-progress action.

Change-Id: I20d243328daf1ffa1a40d55c5fcf889c70a8a59c
Related-Bug: #1585815
Related-Bug: #1591337
This commit is contained in:
Zane Bitter 2016-07-13 17:03:56 -04:00
parent 10088def6c
commit 40036afff2
3 changed files with 118 additions and 9 deletions

View File

@ -775,16 +775,34 @@ class Resource(object):
handler_data = handler(*args)
yield
if callable(check):
while True:
try:
done = check(handler_data)
except PollDelay as delay:
yield delay.period
else:
if done:
break
try:
while True:
try:
done = check(handler_data)
except PollDelay as delay:
yield delay.period
else:
yield
if done:
break
else:
yield
except Exception:
raise
except: # noqa
with excutils.save_and_reraise_exception():
canceller = getattr(
self,
'handle_%s_cancel' % handler_action,
None
)
if callable(canceller):
try:
canceller(handler_data)
except Exception:
LOG.exception(
_LE('Error cancelling resource %s'),
action
)
@scheduler.wrappertask
def _do_action(self, action, pre_func=None, resource_data=None):

View File

@ -62,6 +62,22 @@ class GenericResource(resource.Resource):
self.type())
class CancellableResource(GenericResource):
def check_create_complete(self, cookie):
return True
def handle_create_cancel(self, cookie):
LOG.warning(_LW('Cancelling create generic resource (Type "%s")'),
self.type())
def check_update_complete(self, cookie):
return True
def handle_update_cancel(self, cookie):
LOG.warning(_LW('Cancelling update generic resource (Type "%s")'),
self.type())
class ResWithShowAttr(GenericResource):
def _show_resource(self):
return {'foo': self.name,

View File

@ -914,6 +914,52 @@ class ResourceTest(common.HeatTestCase):
self.assertEqual((res.CREATE, res.COMPLETE), res.state)
self.m.VerifyAll()
def test_create_cancel(self):
tmpl = rsrc_defn.ResourceDefinition('test_resource', 'Foo')
res = generic_rsrc.CancellableResource('test_resource', tmpl,
self.stack)
self.m.StubOutWithMock(res, 'handle_create')
self.m.StubOutWithMock(res, 'check_create_complete')
self.m.StubOutWithMock(res, 'handle_create_cancel')
cookie = object()
res.handle_create().AndReturn(cookie)
res.check_create_complete(cookie).AndReturn(False)
res.handle_create_cancel(cookie).AndReturn(None)
self.m.ReplayAll()
runner = scheduler.TaskRunner(res.create)
runner.start()
runner.step()
runner.cancel()
self.m.VerifyAll()
def test_create_cancel_exception(self):
tmpl = rsrc_defn.ResourceDefinition('test_resource', 'Foo')
res = generic_rsrc.CancellableResource('test_resource', tmpl,
self.stack)
self.m.StubOutWithMock(res, 'handle_create')
self.m.StubOutWithMock(res, 'check_create_complete')
self.m.StubOutWithMock(res, 'handle_create_cancel')
cookie = object()
res.handle_create().AndReturn(cookie)
res.check_create_complete(cookie).AndReturn(False)
res.handle_create_cancel(cookie).AndRaise(Exception)
self.m.ReplayAll()
runner = scheduler.TaskRunner(res.create)
runner.start()
runner.step()
runner.cancel()
self.m.VerifyAll()
def test_preview(self):
tmpl = rsrc_defn.ResourceDefinition('test_resource',
'GenericResourceType')
@ -1089,6 +1135,35 @@ class ResourceTest(common.HeatTestCase):
self.assertEqual((res.UPDATE, res.FAILED), res.state)
self.m.VerifyAll()
def test_update_cancel(self):
tmpl = rsrc_defn.ResourceDefinition('test_resource', 'Foo')
res = generic_rsrc.CancellableResource('test_resource', tmpl,
self.stack)
self.m.StubOutWithMock(res, '_needs_update')
self.m.StubOutWithMock(res, 'handle_update')
self.m.StubOutWithMock(res, 'check_update_complete')
self.m.StubOutWithMock(res, 'handle_update_cancel')
res._needs_update(mock.ANY, mock.ANY,
mock.ANY, mock.ANY,
None).AndReturn(True)
cookie = object()
res.handle_update(mock.ANY, mock.ANY, mock.ANY).AndReturn(cookie)
res.check_update_complete(cookie).AndReturn(False)
res.handle_update_cancel(cookie).AndReturn(None)
self.m.ReplayAll()
scheduler.TaskRunner(res.create)()
runner = scheduler.TaskRunner(res.update, tmpl)
runner.start()
runner.step()
runner.cancel()
self.m.VerifyAll()
def test_check_supported(self):
tmpl = rsrc_defn.ResourceDefinition('test_res', 'GenericResourceType')
res = generic_rsrc.ResourceWithProps('test_res', tmpl, self.stack)