From 5ce238fb17e38702ceef85a3706e9d2236c43e0e Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Thu, 13 Jul 2017 13:59:27 -0400 Subject: [PATCH] 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. Change-Id: Id438336e5c88dc7c2d168ba01ee703faa17e8b8e Closes-Bug: #1703703 Related-Bug: #1523748 --- heat/engine/resource.py | 15 +++++--- heat/engine/stack.py | 35 +++++++++++-------- .../functional/test_resources_list.py | 7 ++++ 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/heat/engine/resource.py b/heat/engine/resource.py index e8f158e9a2..0202725221 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -670,11 +670,18 @@ class Resource(status.ResourceStatus): 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 diff --git a/heat/engine/stack.py b/heat/engine/stack.py index 8a6081e07f..d6e1fc80a4 100644 --- a/heat/engine/stack.py +++ b/heat/engine/stack.py @@ -322,9 +322,9 @@ class Stack(collections.Mapping): else: resources = self._db_resources_get() for rsc in six.itervalues(resources): - defn = self._rsrc_def_for_db_resource(rsc, rsrc_def_cache) - if defn: - yield resource.Resource(rsc.name, defn, self) + loaded_res = self._resource_from_db_resource(rsc, rsrc_def_cache) + if loaded_res is not None: + yield loaded_res def iter_resources(self, nested_depth=0, filters=None): """Iterates over all the resources in a stack. @@ -367,23 +367,32 @@ class Stack(collections.Mapping): self._db_resources = _db_resources return self._db_resources - def _rsrc_def_for_db_resource(self, db_res, rsrc_def_cache=None): + def _resource_from_db_resource(self, db_res, rsrc_def_cache=None): tid = db_res.current_template_id if tid is None: tid = self.t.id + if tid == self.t.id: + cur_res = self.resources[db_res.name] + if 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 - return rsrc_def.get(db_res.name) + defn = rsrc_def.get(db_res.name) + + if defn is None: + return None + + return resource.Resource(db_res.name, defn, self) def resource_get(self, name): """Return a stack resource, even if not in the current template.""" @@ -394,9 +403,7 @@ class Stack(collections.Mapping): # fall back to getting the resource from the database db_res = self.db_resource_get(name) if db_res: - defn = self._rsrc_def_for_db_resource(db_res) - if defn: - return resource.Resource(db_res.name, defn, self) + return self._resource_from_db_resource(db_res) return None diff --git a/heat_integrationtests/functional/test_resources_list.py b/heat_integrationtests/functional/test_resources_list.py index 257afc5651..f57cf67365 100644 --- a/heat_integrationtests/functional/test_resources_list.py +++ b/heat_integrationtests/functional/test_resources_list.py @@ -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)