Store Resources before they are created

Store all Resources in an initial INIT_COMPLETE state prior to creating a
stack, rather than waiting until each Resource is create()d. When updating
a stack, store each new Resource just prior to create()ing it.

Change-Id: Ifeee8213c1962cbf45fd0dd59f1832b0eeff1a47
Closes-Bug: #1319813
This commit is contained in:
Zane Bitter 2015-03-18 21:30:31 -04:00
parent 740dc88dcc
commit 073a9d3404
6 changed files with 46 additions and 26 deletions

View File

@ -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):
"""

View File

@ -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)

View File

@ -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,

View File

@ -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(),

View File

@ -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)

View File

@ -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()