Convergence: Handling resouce clean-up

1. There is no back-up stack which can take care of deleting the old
resources after the stack update is done. The DB objects, in convergence
model, needs to be deleted up front after they have been deleted from
real world.

2. When the stack fails, do not delete the previous template. There
might be resource from previous template in DB that are not yet deleted.

3. Resource unlock should use the boolean return value instead of
integer.

Change-Id: I49cc319638c81833c6c544c53e8c321e358e9744
Co-Authored-By: Sirushti Murugesan <sirushti.murugesan@hp.com>
Co-Authored-By: Anant Patil <anant.patil@hp.com>
This commit is contained in:
Angus Salkeld 2015-06-25 12:43:52 +10:00 committed by Anant Patil
parent d481619ed5
commit 5f9d1ebbdb
5 changed files with 35 additions and 37 deletions

View File

@ -1008,21 +1008,13 @@ class Resource(object):
def delete_convergence(self, template_id, resource_data, engine_id):
'''
Deletes the resource by invoking the scheduler TaskRunner
and it persists the resource's current_template_id to template_id and
resource's requires to list of the required resource id from the
given resource_data and existing resource's requires.
Destroys the resource. The destroy task is run in a scheduler
TaskRunner after acquiring the lock on resource.
'''
with self.lock(engine_id):
runner = scheduler.TaskRunner(self.delete)
runner = scheduler.TaskRunner(self.destroy)
runner()
# update the resource db record
self.current_template_id = template_id
current_requires = {graph_key[0]
for graph_key, data in resource_data.items()}
self.requires = (list(set(self.requires) - current_requires))
@scheduler.wrappertask
def delete(self):
'''
@ -1194,14 +1186,16 @@ class Resource(object):
def unlock(self, rsrc, engine_id, atomic_key):
if atomic_key is None:
atomic_key = 0
res = rsrc.select_and_update(
updated_ok = rsrc.select_and_update(
{'engine_id': None,
'current_template_id': self.current_template_id,
'updated_at': self.updated_time,
'requires': self.requires},
expected_engine_id=engine_id,
atomic_key=atomic_key + 1)
if res != 1:
if not updated_ok:
LOG.warn(_LW('Failed to unlock resource %s'), rsrc.name)
def _resolve_attribute(self, name):

View File

@ -1609,9 +1609,10 @@ class Stack(collections.Mapping):
1. Delete previous raw template if stack completes successfully.
2. Deletes all sync points. They are no longer needed after stack
has completed/failed.
3. Delete the stack is the action is DELETE.
3. Delete the stack if the action is DELETE.
'''
if self.prev_raw_template_id is not None:
if (self.prev_raw_template_id is not None and
self.status != self.FAILED):
prev_tmpl_id = self.prev_raw_template_id
self.prev_raw_template_id = None
self.store()

View File

@ -203,7 +203,7 @@ class WorkerService(service.Service):
cnxt, stack.id, current_traversal, reason)
return
graph_key = (rsrc.id, is_update)
graph_key = (resource_id, is_update)
if graph_key not in graph and rsrc.replaces is not None:
# If we are a replacement, impersonate the replaced resource for
# the purposes of calculating whether subsequent resources are
@ -221,7 +221,7 @@ class WorkerService(service.Service):
input_data if fwd else None, fwd)
check_stack_complete(cnxt, rsrc.stack, current_traversal,
rsrc.id, deps, is_update)
resource_id, deps, is_update)
except sync_point.SyncPointNotFound:
# Reload the stack to determine the current traversal, and check
# the SyncPoint for the current node to determine if it is ready.

View File

@ -40,6 +40,7 @@ from heat.engine import stack_lock
from heat.engine import template as templatem
from heat.engine import watchrule
from heat.objects import event as event_object
from heat.objects import raw_template as raw_template_object
from heat.objects import resource as resource_objects
from heat.objects import stack as stack_object
from heat.objects import sync_point as sync_point_object
@ -502,18 +503,25 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
stack.mark_complete(stack.current_traversal)
self.assertTrue(stack.purge_db.called)
def test_purge_db_deletes_previous_template(self, mock_cr):
@mock.patch.object(raw_template_object.RawTemplate, 'delete')
def test_purge_db_deletes_previous_template(self, mock_tmpl_delete,
mock_cr):
stack = tools.get_stack('test_stack', utils.dummy_context(),
template=tools.string_template_five,
convergence=True)
prev_tmpl = templatem.Template.create_empty_template()
prev_tmpl.store()
stack.prev_raw_template_id = prev_tmpl.id
stack.store()
stack.prev_raw_template_id = 10
stack.purge_db()
self.assertRaises(exception.NotFound,
templatem.Template.load,
stack.context, prev_tmpl.id)
self.assertTrue(mock_tmpl_delete.called)
@mock.patch.object(raw_template_object.RawTemplate, 'delete')
def test_purge_db_does_not_delete_previous_template_when_stack_fails(
self, mock_tmpl_delete, mock_cr):
stack = tools.get_stack('test_stack', utils.dummy_context(),
template=tools.string_template_five,
convergence=True)
stack.status = stack.FAILED
stack.purge_db()
self.assertFalse(mock_tmpl_delete.called)
def test_purge_db_deletes_sync_points(self, mock_cr):
stack = tools.get_stack('test_stack', utils.dummy_context(),
@ -525,17 +533,16 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
stack.context, stack.id, stack.current_traversal)
self.assertEqual(0, rows)
def test_purge_db_deletes_stack_for_deleted_stack(self, mock_cr):
@mock.patch.object(stack_object.Stack, 'delete')
def test_purge_db_deletes_stack_for_deleted_stack(self, mock_stack_delete,
mock_cr):
stack = tools.get_stack('test_stack', utils.dummy_context(),
template=tools.string_template_five,
convergence=True)
stack.store()
stack.state_set(stack.DELETE, stack.COMPLETE, 'test reason')
stack.purge_db()
self.assertRaises(exception.NotFound,
parser.Stack.load,
stack.context, stack_id=stack.id,
show_deleted=False)
self.assertTrue(mock_stack_delete.called)
def test_get_best_existing_db_resource(self, mock_cr):
stack = tools.get_stack('test_stack', utils.dummy_context(),

View File

@ -1478,21 +1478,17 @@ class ResourceTest(common.HeatTestCase):
res.name)
self.assertEqual(msg, six.text_type(ex))
@mock.patch.object(resource.Resource, 'delete')
def test_delete_convergence(self, mock_delete):
def test_delete_convergence(self):
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
res.requires = [1, 2]
res._store()
res.destroy = mock.Mock()
self._assert_resource_lock(res.id, None, None)
res.delete_convergence('template_key', {(1, True): {},
(1, True): {}},
'engine-007')
mock_delete.assert_called_once_with()
self.assertEqual('template_key', res.current_template_id)
self.assertEqual([2], res.requires)
self._assert_resource_lock(res.id, None, 2)
self.assertTrue(res.destroy.called)
def test_delete_in_progress_convergence(self):
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')