From 3786262aed62d81ad35de3d4f703c38223907ffd Mon Sep 17 00:00:00 2001 From: kairat_kushaev Date: Wed, 27 May 2015 11:56:31 +0300 Subject: [PATCH] Backup new resource as soon as possible The root cause came discussion in https://review.openstack.org/#/c/175868/. It is better to copy a new resources that appears during stack-update to backup stack as soon as possible because it reduces time when backup stack is not synchronized with the existing stack. Change-Id: If104037225905dc9c504972864270d6e68e08d73 --- heat/engine/update.py | 28 +++++++++---- heat/tests/test_stack_update.py | 70 +++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 8 deletions(-) diff --git a/heat/engine/update.py b/heat/engine/update.py index 874779bd5..db2c78d56 100644 --- a/heat/engine/update.py +++ b/heat/engine/update.py @@ -119,6 +119,19 @@ class StackUpdate(object): existing_res.state_set(existing_res.UPDATE, existing_res.COMPLETE) self.existing_stack.add_resource(new_res) + + # Save new resource definition to backup stack if it is not + # present in backup stack template already + # it allows to resolve all dependencies that existing resource + # can have if it was copied to backup stack + if (res_name not in + self.previous_stack.t[self.previous_stack.t.RESOURCES]): + LOG.debug("Backing up new Resource %s" % res_name) + definition = new_res.t.reparse(self.previous_stack, + new_res.stack.t) + self.previous_stack.t.add_resource(definition) + self.previous_stack.t.store(self.previous_stack.context) + yield new_res.create() @scheduler.wrappertask @@ -135,14 +148,13 @@ class StackUpdate(object): except resource.UpdateReplace: pass else: - # Save resource definition to backup stack if it is not - # present in backup stack template already - if res_name not in self.previous_stack.t[ - self.previous_stack.t.RESOURCES]: - definition = existing_res.t.reparse(self.previous_stack, - existing_res.stack.t) - self.previous_stack.t.add_resource(definition) - self.previous_stack.t.store(self.previous_stack.context) + # Save updated resource definition to backup stack + # cause it allows the backup stack resources to be synchronized + LOG.debug("Backing up updated Resource %s" % res_name) + definition = existing_res.t.reparse(self.previous_stack, + existing_res.stack.t) + self.previous_stack.t.add_resource(definition) + self.previous_stack.t.store(self.previous_stack.context) LOG.info(_LI("Resource %(res_name)s for stack %(stack_name)s " "updated"), diff --git a/heat/tests/test_stack_update.py b/heat/tests/test_stack_update.py index 3e0eade67..4838dfb4e 100644 --- a/heat/tests/test_stack_update.py +++ b/heat/tests/test_stack_update.py @@ -1603,3 +1603,73 @@ class StackUpdateTest(common.HeatTestCase): self.stack.delete() self.assertEqual((stack.Stack.DELETE, stack.Stack.COMPLETE), self.stack.state) + + def test_backup_stack_synchronized_after_update(self): + """Test when backup stack updated correctly during stack update. + + Test checks the following scenario: + 1. Create stack + 2. Update stack (failed - so the backup should not be deleted) + 3. Update stack (failed - so the backup from step 2 should be updated) + The test checks that backup stack is synchronized with the main stack. + """ + # create a stack + tmpl_create = { + 'heat_template_version': '2013-05-23', + 'resources': { + 'Ares': {'type': 'GenericResourceType'} + } + } + self.stack = stack.Stack(self.ctx, 'test_update_stack_backup', + template.Template(tmpl_create), + disable_rollback=True) + self.stack.store() + self.stack.create() + self.assertEqual((stack.Stack.CREATE, stack.Stack.COMPLETE), + self.stack.state) + + # try to update a stack with a new resource that should be backed up + tmpl_update = { + 'heat_template_version': '2013-05-23', + 'resources': { + 'Ares': {'type': 'GenericResourceType'}, + 'Bres': { + 'type': 'ResWithComplexPropsAndAttrs', + 'properties': { + 'an_int': 0, + } + }, + 'Cres': { + 'type': 'ResourceWithPropsType', + 'properties': { + 'Foo': {'get_resource': 'Bres'}, + } + } + } + } + + self.patchobject(generic_rsrc.ResourceWithProps, + 'handle_create', + side_effect=[Exception, Exception]) + + stack_with_new_resource = stack.Stack( + self.ctx, + 'test_update_stack_backup', + template.Template(tmpl_update)) + self.stack.update(stack_with_new_resource) + self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED), + self.stack.state) + # assert that backup stack has been updated correctly + self.assertIn('Bres', self.stack._backup_stack()) + + # update the stack with resource that updated in-place + tmpl_update['resources']['Bres']['properties']['an_int'] = 1 + updated_stack_second = stack.Stack(self.ctx, + 'test_update_stack_backup', + template.Template(tmpl_update)) + self.stack.update(updated_stack_second) + self.assertEqual((stack.Stack.UPDATE, stack.Stack.FAILED), + self.stack.state) + # assert that resource in backup stack also has been updated + backup = self.stack._backup_stack() + self.assertEqual(1, backup['Bres'].properties['an_int'])