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:
parent
10088def6c
commit
40036afff2
|
@ -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):
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue