Don't load new copies of current resources

The Stack._find_filtered_resources() method returns Resource objects for
all resources associated with the Stack, regardless of whether they are
current (present in the template; latest version in the case of
convergence). To do this, it previously created a new Resource object
for every resource found in the database.

However, for those resources which *are* current this is unnecessary. We
can access the Resource object simply through self.resources. It turns
out this is necessary for obtaining the required_by list for legacy
stacks, because only the Resources obtained from self.resources also
appear in the Dependencies graph obtained from self.dependencies. The
required_by list is read when listing or showing resources, which would
either return an empty list or fail for legacy stacks.

This patch also makes the Resource.required_by() method more robust in
its error handling.

 Conflicts:
	heat/engine/stack.py

The backport is simpler than the patch on master because
3967a93f5d has not been backported to
stable/newton.

Change-Id: Id438336e5c88dc7c2d168ba01ee703faa17e8b8e
Closes-Bug: #1703703
Related-Bug: #1523748
(cherry picked from commit 5ce238fb17
                       and 65630d4e8b)
This commit is contained in:
Zane Bitter 2017-07-13 13:59:27 -04:00
parent 04ecb75fae
commit 4e09419701
3 changed files with 28 additions and 10 deletions

View File

@ -662,11 +662,18 @@ class Resource(object):
directly.
"""
try:
return [r.name for r in self.stack.dependencies.required_by(self)]
reqd_by = self.stack.dependencies.required_by(self)
except KeyError:
# for convergence, fall back to building from needed_by
return [r.name for r in self.stack.resources.values()
if r.id in self.needed_by]
if self.stack.convergence:
# for convergence, fall back to building from needed_by
needed_by_ids = self.needed_by or set()
reqd_by = [r for r in self.stack.resources.values()
if r.id in needed_by_ids]
else:
LOG.error('Getting required_by list for Resource not in '
'dependency graph.')
return []
return [r.name for r in reqd_by]
def client(self, name=None, version=None):
client_name = name or self.default_client_name

View File

@ -364,15 +364,19 @@ class Stack(collections.Mapping):
if tid is None:
tid = self.t.id
if tid == self.t.id:
cur_res = self.resources.get(db_res.name)
if cur_res is not None and (cur_res.id == db_res.id):
return cur_res
if rsrc_def_cache and tid in rsrc_def_cache:
rsrc_def = rsrc_def_cache[tid]
elif tid == self.t.id:
rsrc_def = self.t.resource_definitions(self)
if rsrc_def_cache:
rsrc_def_cache[tid] = rsrc_def
else:
t = tmpl.Template.load(self.context, tid)
rsrc_def = t.resource_definitions(self)
if tid == self.t.id:
rsrc_def = self.t.resource_definitions(self)
else:
t = tmpl.Template.load(self.context, tid)
rsrc_def = t.resource_definitions(self)
if rsrc_def_cache:
rsrc_def_cache[tid] = rsrc_def

View File

@ -41,3 +41,10 @@ class ResourcesList(functional_base.FunctionalTestsBase):
filters={'name': 'test2'})
self.assertEqual('CREATE_COMPLETE', test2.resource_status)
def test_required_by(self):
stack_identifier = self.stack_create(template=test_template_depend)
[test1] = self.client.resources.list(stack_identifier,
filters={'name': 'test1'})
self.assertEqual(['test2'], test1.required_by)