From b902d93ade7f22e15d431161d91634f168dcd26c Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Mon, 10 Jul 2017 13:48:01 -0400 Subject: [PATCH] Handle errors calculating dep_attrs for nested get_attr Ensure that in convergence we can calculate the dep_attrs of a resource, including the attributes referenced in outputs, even if: - One of the resource's attributes is referenced in an ouput; and - The attribute path itself also contains a get_attr function; and - The attribute referenced by the inner get_attr function in the output is not referenced by any resources. Since the attributes referenced only in outputs (and not in resources) are not passed to dependent resources in the NodeData, the data required to resolve the inner get_attr function is not available. Therefore getting the dep_attrs for the relevant resource from the output definition would fail. The next patch does so during the calculation of the NodeData, and any resulting exception is not caught (bug 1703043), so the stack would end up stuck IN_PROGRESS. To avoid this, ignore any errors resolving attribute path components in the course of calculating the dep_attrs of a given resource. This may mean that an attribute value required for the outputs is not stored in the database, but it can still be resolved live later. Change-Id: I2ed1e3dacd371e36656559ba92dfada05df0ceba Related-Bug: #1660831 --- heat/engine/hot/functions.py | 45 ++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/heat/engine/hot/functions.py b/heat/engine/hot/functions.py index 79f27a42c..eef946f16 100644 --- a/heat/engine/hot/functions.py +++ b/heat/engine/hot/functions.py @@ -16,6 +16,7 @@ import hashlib import itertools from oslo_config import cfg +from oslo_log import log as logging from oslo_serialization import jsonutils import six from six.moves.urllib import parse as urlparse @@ -27,6 +28,9 @@ from heat.common.i18n import _ from heat.engine import attributes from heat.engine import function + +LOG = logging.getLogger(__name__) + opts = [ cfg.IntOpt('limit_iterators', default=200, @@ -182,9 +186,17 @@ class GetAttThenSelect(function.Function): raise exception.InvalidTemplateReference(resource=resource_name, key=path) + def _attr_path(self): + return function.resolve(self._attribute) + def dep_attrs(self, resource_name): if self._res_name() == resource_name: - attrs = [function.resolve(self._attribute)] + try: + attrs = [self._attr_path()] + except Exception as exc: + LOG.debug("Ignoring exception calculating required attributes" + ": %s %s", type(exc).__name__, six.text_type(exc)) + attrs = [] else: attrs = [] return itertools.chain(super(GetAttThenSelect, @@ -266,18 +278,13 @@ class GetAtt(GetAttThenSelect): else: return None - def dep_attrs(self, resource_name): - if self._res_name() == resource_name: - path = function.resolve(self._path_components) - attr = [function.resolve(self._attribute)] - if path: - attrs = [tuple(attr + path)] - else: - attrs = attr + def _attr_path(self): + path = function.resolve(self._path_components) + attr = function.resolve(self._attribute) + if path: + return tuple([attr] + path) else: - attrs = [] - return itertools.chain(function.dep_attrs(self.args, resource_name), - attrs) + return attr class GetAttAllAttributes(GetAtt): @@ -311,16 +318,10 @@ class GetAttAllAttributes(GetAtt): raise TypeError(_('Argument to "%s" must be a list') % self.fn_name) - def dep_attrs(self, resource_name): - """Check if there is no attribute_name defined, return empty chain.""" - if self._attribute is not None: - return super(GetAttAllAttributes, self).dep_attrs(resource_name) - elif self._res_name() == resource_name: - attrs = [attributes.ALL_ATTRIBUTES] - else: - attrs = [] - return itertools.chain(function.dep_attrs(self.args, - resource_name), attrs) + def _attr_path(self): + if self._attribute is None: + return attributes.ALL_ATTRIBUTES + return super(GetAttAllAttributes, self)._attr_path() def result(self): if self._attribute is None: