diff --git a/heat/engine/resource.py b/heat/engine/resource.py index 2f4ff6775..35cb8ab78 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -249,7 +249,7 @@ class Resource(object): def metadata_get(self, refresh=False): if refresh: self._rsrc_metadata = None - if self.id is None: + if self.id is None or self.action == self.INIT: return self.t.metadata() if self._rsrc_metadata is not None: return self._rsrc_metadata @@ -259,7 +259,7 @@ class Resource(object): return rs.rsrc_metadata def metadata_set(self, metadata): - if self.id is None: + if self.id is None or self.action == self.INIT: raise exception.ResourceNotAvailable(resource_name=self.name) rs = resource_objects.Resource.get_obj(self.stack.context, self.id) rs.update_and_save({'rsrc_metadata': metadata}) @@ -790,7 +790,7 @@ class Resource(object): yield self.action_handler_task('delete_snapshot', args=[data]) def physical_resource_name(self): - if self.id is None: + if self.id is None or self.action == self.INIT: return None name = '%s-%s-%s' % (self.stack.name, @@ -904,9 +904,8 @@ class Resource(object): except Exception as ex: LOG.warn(_LW('db error %s'), ex) - def _store(self): + def _store(self, metadata=None): '''Create the resource in the database.''' - metadata = self.metadata_get() try: rs = {'action': self.action, 'status': self.status, @@ -940,35 +939,43 @@ class Resource(object): ev.store() def _store_or_update(self, action, status, reason): + prev_action = self.action self.action = action self.status = status self.status_reason = reason + data = { + 'action': self.action, + 'status': self.status, + 'status_reason': reason, + 'stack_id': self.stack.id, + 'updated_at': self.updated_time, + 'properties_data': self._stored_properties_data, + 'needed_by': self.needed_by, + 'requires': self.requires, + 'replaces': self.replaces, + 'replaced_by': self.replaced_by, + 'current_template_id': self.current_template_id, + 'nova_instance': self.resource_id + } + if prev_action == self.INIT: + metadata = self.t.metadata() + data['rsrc_metadata'] = metadata + else: + metadata = self._rsrc_metadata + if self.id is not None: try: rs = resource_objects.Resource.get_obj(self.context, self.id) - rs.update_and_save({ - 'action': self.action, - 'status': self.status, - 'status_reason': reason, - 'stack_id': self.stack.id, - 'updated_at': self.updated_time, - 'properties_data': self._stored_properties_data, - 'needed_by': self.needed_by, - 'requires': self.requires, - 'replaces': self.replaces, - 'replaced_by': self.replaced_by, - 'current_template_id': self.current_template_id, - 'nova_instance': self.resource_id}) + rs.update_and_save(data) except Exception as ex: LOG.error(_LE('DB error %s'), ex) - - # store resource in DB on transition to CREATE_IN_PROGRESS - # all other transitions (other than to DELETE_COMPLETE) - # should be handled by the update_and_save above.. - elif (action, status) in [(self.CREATE, self.IN_PROGRESS), - (self.ADOPT, self.IN_PROGRESS)]: - self._store() + else: + self._rsrc_metadata = metadata + else: + # This should only happen in unit tests + LOG.warning(_LW('Resource "%s" not pre-stored in DB'), self) + self._store(metadata) def _resolve_attribute(self, name): """ diff --git a/heat/engine/service.py b/heat/engine/service.py index 8740431bf..41a07d3b2 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -1115,7 +1115,8 @@ class EngineService(service.Service): # when signalling a WaitConditionHandle resource, and other # resources may refer to WaitCondition Fn::GetAtt Data for r in stack.dependencies: - if r.name != rsrc.name and r.id is not None: + if (r.name != rsrc.name and r.id is not None and + r.action != r.INIT): r.metadata_update() s = self._get_stack(cnxt, stack_identity) diff --git a/heat/engine/stack.py b/heat/engine/stack.py index dbb02b6ff..e2bcfebd2 100755 --- a/heat/engine/stack.py +++ b/heat/engine/stack.py @@ -430,6 +430,8 @@ class Stack(collections.Mapping): self.t.add_resource(definition) if self.t.id is not None: self.t.store(self.context) + if resource.action == resource.INIT: + resource._store() def remove_resource(self, resource_name): '''Remove the resource with the specified name.''' @@ -618,6 +620,11 @@ class Stack(collections.Mapping): return [resource.preview() for resource in self.resources.itervalues()] + def _store_resources(self): + for r in reversed(self.dependencies): + if r.action == r.INIT: + r._store() + @profiler.trace('Stack.create', hide_args=False) def create(self): ''' @@ -628,6 +635,8 @@ class Stack(collections.Mapping): self.FAILED): self.delete(action=self.ROLLBACK) + self._store_resources() + creator = scheduler.TaskRunner( self.stack_task, action=self.CREATE, reverse=False, post_func=rollback, diff --git a/heat/tests/test_nested_stack.py b/heat/tests/test_nested_stack.py index d87f5b42a..b20a7e904 100644 --- a/heat/tests/test_nested_stack.py +++ b/heat/tests/test_nested_stack.py @@ -353,6 +353,7 @@ Outputs: def test_handle_delete(self): self.res.rpc_client = mock.MagicMock() + self.res.action = self.res.CREATE stack_identity = identifier.HeatIdentifier( self.ctx.tenant_id, self.res.physical_resource_name(), diff --git a/heat/tests/test_provider_template.py b/heat/tests/test_provider_template.py index e368ed277..f6a97c342 100644 --- a/heat/tests/test_provider_template.py +++ b/heat/tests/test_provider_template.py @@ -878,6 +878,7 @@ class TemplateResourceCrudTest(common.HeatTestCase): self.res.id = 55 self.res.uuid = six.text_type(uuid.uuid4()) self.res.resource_id = six.text_type(uuid.uuid4()) + self.res.action = self.res.CREATE ident = identifier.HeatIdentifier(self.ctx.tenant_id, self.res.physical_resource_name(), self.res.resource_id) diff --git a/heat/tests/test_software_deployment.py b/heat/tests/test_software_deployment.py index 124f6e453..dc624bf19 100644 --- a/heat/tests/test_software_deployment.py +++ b/heat/tests/test_software_deployment.py @@ -869,6 +869,7 @@ class SoftwareDeploymentTest(common.HeatTestCase): self.deployment.id = 23 self.deployment.uuid = str(uuid.uuid4()) + self.deployment.action = self.deployment.CREATE container = self.deployment.physical_resource_name() temp_url = self.deployment._get_temp_url()