diff --git a/heat/engine/check_resource.py b/heat/engine/check_resource.py index 5f3c0cc9bd..999a78b5cd 100644 --- a/heat/engine/check_resource.py +++ b/heat/engine/check_resource.py @@ -287,9 +287,10 @@ class CheckResource(object): rsrc, stack) -def load_resource(cnxt, resource_id, resource_data, is_update): +def load_resource(cnxt, resource_id, resource_data, + current_traversal, is_update): try: - return resource.Resource.load(cnxt, resource_id, + return resource.Resource.load(cnxt, resource_id, current_traversal, is_update, resource_data) except (exception.ResourceNotFound, exception.NotFound): # can be ignored diff --git a/heat/engine/resource.py b/heat/engine/resource.py index 2e40cea6ff..4c7ece4d70 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -322,14 +322,17 @@ class Resource(status.ResourceStatus): self._stackref = weakref.ref(stack) @classmethod - def load(cls, context, resource_id, is_update, data): + def load(cls, context, resource_id, current_traversal, is_update, data): from heat.engine import stack as stack_mod db_res = resource_objects.Resource.get_obj(context, resource_id) curr_stack = stack_mod.Stack.load(context, stack_id=db_res.stack_id, cache_data=data) resource_owning_stack = curr_stack - if db_res.current_template_id != curr_stack.t.id: + if (db_res.current_template_id != curr_stack.t.id and + (db_res.action != cls.INIT or + not is_update or + current_traversal != curr_stack.current_traversal)): # load stack with template owning the resource db_stack = stack_objects.Stack.get_by_id(context, db_res.stack_id) db_stack.raw_template = None diff --git a/heat/engine/resources/openstack/neutron/port.py b/heat/engine/resources/openstack/neutron/port.py index da00879923..9bbe963147 100644 --- a/heat/engine/resources/openstack/neutron/port.py +++ b/heat/engine/resources/openstack/neutron/port.py @@ -558,23 +558,25 @@ class Port(neutron.NeutronResource): def restore_prev_rsrc(self, convergence=False): # In case of convergence, during rollback, the previous rsrc is # already selected and is being acted upon. - backup_stack = self.stack._backup_stack() - backup_res = backup_stack.resources.get(self.name) - prev_port = self if convergence else backup_res - fixed_ips = prev_port.data().get('port_fip', []) - - props = {'fixed_ips': []} if convergence: + prev_port = self existing_port, rsrc_owning_stack, stack = resource.Resource.load( - prev_port.context, prev_port.replaced_by, True, + prev_port.context, prev_port.replaced_by, + prev_port.stack.current_traversal, True, prev_port.stack.cache_data ) existing_port_id = existing_port.resource_id else: + backup_stack = self.stack._backup_stack() + prev_port = backup_stack.resources.get(self.name) existing_port_id = self.resource_id + if existing_port_id: # reset fixed_ips to [] for new resource + props = {'fixed_ips': []} self.client().update_port(existing_port_id, {'port': props}) + + fixed_ips = prev_port.data().get('port_fip', []) if fixed_ips and prev_port.resource_id: # restore ip for old port prev_port_props = {'fixed_ips': jsonutils.loads(fixed_ips)} diff --git a/heat/engine/resources/openstack/nova/server_network_mixin.py b/heat/engine/resources/openstack/nova/server_network_mixin.py index d699eb15fd..f8a34514ed 100644 --- a/heat/engine/resources/openstack/nova/server_network_mixin.py +++ b/heat/engine/resources/openstack/nova/server_network_mixin.py @@ -522,17 +522,17 @@ class ServerNetworkMixin(object): def restore_ports_after_rollback(self, convergence): # In case of convergence, during rollback, the previous rsrc is # already selected and is being acted upon. - backup_stack = self.stack._backup_stack() - backup_res = backup_stack.resources.get(self.name) - prev_server = self if convergence else backup_res - if convergence: + prev_server = self rsrc, rsrc_owning_stack, stack = resource.Resource.load( - prev_server.context, prev_server.replaced_by, True, + prev_server.context, prev_server.replaced_by, + prev_server.stack.current_traversal, True, prev_server.stack.cache_data ) existing_server = rsrc else: + backup_stack = self.stack._backup_stack() + prev_server = backup_stack.resources.get(self.name) existing_server = self # Wait until server will move to active state. We can't diff --git a/heat/engine/worker.py b/heat/engine/worker.py index 46e5f92d95..0fff9bc12c 100644 --- a/heat/engine/worker.py +++ b/heat/engine/worker.py @@ -158,7 +158,7 @@ class WorkerService(object): resource_data = node_data.load_resources_data(in_data if is_update else {}) rsrc, rsrc_owning_stack, stack = check_resource.load_resource( - cnxt, resource_id, resource_data, is_update) + cnxt, resource_id, resource_data, current_traversal, is_update) if rsrc is None: return diff --git a/heat/tests/convergence/scenarios/update_interrupt_create.py b/heat/tests/convergence/scenarios/update_interrupt_create.py new file mode 100644 index 0000000000..8b4c8292b7 --- /dev/null +++ b/heat/tests/convergence/scenarios/update_interrupt_create.py @@ -0,0 +1,22 @@ +def check_resource_count(expected_count): + test.assertEqual(expected_count, len(reality.all_resources())) + +example_template = Template({ + 'A': RsrcDef({}, []), + 'B': RsrcDef({'a': '4alpha'}, ['A']), + 'C': RsrcDef({'a': 'foo'}, ['B']), + 'D': RsrcDef({'a': 'bar'}, ['C']), +}) +engine.create_stack('foo', example_template) +engine.noop(1) + +example_template2 = Template({ + 'A': RsrcDef({}, []), + 'B': RsrcDef({'a': '4alpha'}, ['A']), + 'C': RsrcDef({'a': 'blarg'}, ['B']), + 'D': RsrcDef({'a': 'wibble'}, ['C']), +}) +engine.update_stack('foo', example_template2) +engine.call(check_resource_count, 2) +engine.noop(11) +engine.call(verify, example_template2) diff --git a/heat/tests/engine/test_check_resource.py b/heat/tests/engine/test_check_resource.py index 09d59bf600..efdd44cf0f 100644 --- a/heat/tests/engine/test_check_resource.py +++ b/heat/tests/engine/test_check_resource.py @@ -345,18 +345,20 @@ class CheckWorkflowUpdateTest(common.HeatTestCase): resC = self.stack['C'] # lets say C is update-replaced is_update = True + trav_id = self.stack.current_traversal replacementC_id = resC.make_replacement(self.stack.t.id) replacementC, stack, _ = resource.Resource.load(self.ctx, replacementC_id, + trav_id, is_update, {}) self.cr._initiate_propagate_resource(self.ctx, replacementC_id, - self.stack.current_traversal, + trav_id, is_update, replacementC, self.stack) # check_stack_complete should be called with resC.id not # replacementC.id mock_csc.assert_called_once_with(self.ctx, self.stack, - self.stack.current_traversal, + trav_id, resC.id, mock.ANY, is_update) diff --git a/heat/tests/test_resource.py b/heat/tests/test_resource.py index 2e6047e5d6..2a2e59298a 100644 --- a/heat/tests/test_resource.py +++ b/heat/tests/test_resource.py @@ -109,7 +109,8 @@ class ResourceTest(common.HeatTestCase): res.state_set('CREATE', 'IN_PROGRESS') self.stack.add_resource(res) loaded_res, res_owning_stack, stack = resource.Resource.load( - self.stack.context, res.id, True, {}) + self.stack.context, res.id, + self.stack.current_traversal, True, {}) self.assertEqual(loaded_res.id, res.id) self.assertEqual(self.stack.t, stack.t) @@ -133,7 +134,8 @@ class ResourceTest(common.HeatTestCase): res.state_set('CREATE', 'IN_PROGRESS') self.old_stack.add_resource(res) loaded_res, res_owning_stack, stack = resource.Resource.load( - self.old_stack.context, res.id, False, {}) + self.old_stack.context, res.id, + self.stack.current_traversal, False, {}) self.assertEqual(loaded_res.id, res.id) self.assertEqual(self.old_stack.t, stack.t) self.assertNotEqual(self.new_stack.t, stack.t) @@ -158,7 +160,8 @@ class ResourceTest(common.HeatTestCase): self.stack._resources = None loaded_res, res_owning_stack, stack = resource.Resource.load( - self.stack.context, res.id, False, {}) + self.stack.context, res.id, + self.stack.current_traversal, False, {}) self.assertEqual(origin_resources, stack.resources) self.assertEqual(loaded_res.id, res.id) self.assertEqual(self.stack.t, stack.t) @@ -2663,7 +2666,8 @@ class ResourceTest(common.HeatTestCase): res.store() data = {'bar': {'atrr1': 'baz', 'attr2': 'baz2'}} mock_stack_load.return_value = stack - resource.Resource.load(stack.context, res.id, True, data) + resource.Resource.load(stack.context, res.id, + stack.current_traversal, True, data) self.assertTrue(mock_stack_load.called) mock_stack_load.assert_called_with(stack.context, stack_id=stack.id,