diff --git a/heat/engine/service.py b/heat/engine/service.py index 263d019de7..2d739a5cc5 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -510,10 +510,9 @@ class EngineService(service.Service): """ if stack_identity is not None: db_stack = self._get_stack(cnxt, stack_identity, show_deleted=True) - stacks = [parser.Stack.load(cnxt, stack=db_stack, - resolve_data=resolve_outputs)] + stacks = [parser.Stack.load(cnxt, stack=db_stack)] else: - stacks = parser.Stack.load_all(cnxt, resolve_data=resolve_outputs) + stacks = parser.Stack.load_all(cnxt) return [api.format_stack( stack, resolve_outputs=resolve_outputs) for stack in stacks] @@ -1307,7 +1306,7 @@ class EngineService(service.Service): :return: list of stack outputs in defined format. """ s = self._get_stack(cntx, stack_identity) - stack = parser.Stack.load(cntx, stack=s, resolve_data=False) + stack = parser.Stack.load(cntx, stack=s) return api.format_stack_outputs(stack, stack.t[stack.t.OUTPUTS]) @@ -1321,7 +1320,7 @@ class EngineService(service.Service): :return: dict with output key, value and description in defined format. """ s = self._get_stack(cntx, stack_identity) - stack = parser.Stack.load(cntx, stack=s, resolve_data=False) + stack = parser.Stack.load(cntx, stack=s) outputs = stack.t[stack.t.OUTPUTS] @@ -1330,9 +1329,6 @@ class EngineService(service.Service): 'found.') % output_key) output = stack.resolve_outputs_data({output_key: outputs[output_key]}) - if not stack.outputs: - stack.outputs.update(output) - return api.format_stack_output(stack, output, output_key) def _remote_call(self, cnxt, lock_engine_id, call, **kwargs): @@ -2332,8 +2328,7 @@ class EngineService(service.Service): stk = parser.Stack.load(cnxt, stack=s, service_check_defer=True, - resource_validate=False, - resolve_data=False) + resource_validate=False) LOG.info(_LI('Engine %(engine)s went down when stack ' '%(stack_id)s was in action %(action)s'), {'engine': engine_id, 'action': stk.action, diff --git a/heat/engine/stack.py b/heat/engine/stack.py index c21e63f6ef..218f001130 100644 --- a/heat/engine/stack.py +++ b/heat/engine/stack.py @@ -118,7 +118,7 @@ class Stack(collections.Mapping): def __init__(self, context, stack_name, tmpl, stack_id=None, action=None, status=None, - status_reason='', timeout_mins=None, resolve_data=True, + status_reason='', timeout_mins=None, disable_rollback=True, parent_resource=None, owner_id=None, adopt_stack_data=None, stack_user_project_id=None, created_time=None, updated_time=None, @@ -168,6 +168,7 @@ class Stack(collections.Mapping): self.disable_rollback = disable_rollback self.parent_resource_name = parent_resource self._parent_stack = None + self._outputs = None self._resources = None self._dependencies = None self._access_allowed_handlers = {} @@ -227,12 +228,6 @@ class Stack(collections.Mapping): param_defaults=self.env.param_defaults) self._set_param_stackid() - if resolve_data: - self.outputs = self.resolve_outputs_data( - self.t[self.t.OUTPUTS], path=self.t.OUTPUTS) - else: - self.outputs = {} - @property def tags(self): if self._tags is None: @@ -299,6 +294,13 @@ class Stack(collections.Mapping): msg = _("Attempt to use stored_context with no user_creds") raise exception.Error(msg) + @property + def outputs(self): + if self._outputs is None: + self._outputs = self.resolve_outputs_data(self.t[self.t.OUTPUTS], + path=self.t.OUTPUTS) + return self._outputs + @property def resources(self): if self._resources is None: @@ -484,7 +486,7 @@ class Stack(collections.Mapping): @classmethod def load(cls, context, stack_id=None, stack=None, show_deleted=True, use_stored_context=False, force_reload=False, cache_data=None, - resolve_data=True, service_check_defer=False, + service_check_defer=False, resource_validate=True): """Retrieve a Stack from the database.""" if stack is None: @@ -501,14 +503,14 @@ class Stack(collections.Mapping): return cls._from_db(context, stack, use_stored_context=use_stored_context, - cache_data=cache_data, resolve_data=resolve_data, + cache_data=cache_data, service_check_defer=service_check_defer, resource_validate=resource_validate) @classmethod def load_all(cls, context, limit=None, marker=None, sort_keys=None, sort_dir=None, filters=None, - show_deleted=False, resolve_data=True, + show_deleted=False, show_nested=False, show_hidden=False, tags=None, tags_any=None, not_tags=None, not_tags_any=None): stacks = stack_object.Stack.get_all( @@ -527,14 +529,14 @@ class Stack(collections.Mapping): not_tags_any=not_tags_any) for stack in stacks: try: - yield cls._from_db(context, stack, resolve_data=resolve_data) + yield cls._from_db(context, stack) except exception.NotFound: # We're in a different transaction than the get_all, so a stack # returned above can be deleted by the time we try to load it. pass @classmethod - def _from_db(cls, context, stack, resolve_data=True, + def _from_db(cls, context, stack, use_stored_context=False, cache_data=None, service_check_defer=False, resource_validate=True): template = tmpl.Template.load( @@ -544,7 +546,6 @@ class Stack(collections.Mapping): action=stack.action, status=stack.status, status_reason=stack.status_reason, timeout_mins=stack.timeout, - resolve_data=resolve_data, disable_rollback=stack.disable_rollback, parent_resource=stack.parent_resource_name, owner_id=stack.owner_id, @@ -1517,9 +1518,7 @@ class Stack(collections.Mapping): # flip the template to the newstack values previous_template_id = self.t.id self.t = newstack.t - template_outputs = self.t[self.t.OUTPUTS] - self.outputs = self.resolve_outputs_data( - template_outputs, path=self.t.OUTPUTS) + self._outputs = None finally: if should_rollback: # Already handled in rollback task diff --git a/heat/tests/test_engine_service.py b/heat/tests/test_engine_service.py index e8b1b9938a..7b3ab6ae68 100644 --- a/heat/tests/test_engine_service.py +++ b/heat/tests/test_engine_service.py @@ -1027,8 +1027,7 @@ class StackServiceTest(common.HeatTestCase): t['outputs'] = {'test': {'value': 'first', 'description': 'sec'}, 'test2': {'value': 'sec'}} tmpl = templatem.Template(t) - stack = parser.Stack(self.ctx, 'service_list_outputs_stack', tmpl, - resolve_data=False) + stack = parser.Stack(self.ctx, 'service_list_outputs_stack', tmpl) self.patchobject(self.eng, '_get_stack') self.patchobject(parser.Stack, 'load', return_value=stack) @@ -1050,8 +1049,7 @@ class StackServiceTest(common.HeatTestCase): t = template_format.parse(tools.wp_template) t['outputs'] = {'test': {'value': 'first', 'description': 'sec'}} tmpl = templatem.Template(t) - stack = parser.Stack(self.ctx, 'service_list_outputs_stack', tmpl, - resolve_data=False) + stack = parser.Stack(self.ctx, 'service_list_outputs_stack', tmpl) self.patchobject(self.eng, '_get_stack') self.patchobject(parser.Stack, 'load', return_value=stack) @@ -1311,8 +1309,7 @@ class StackServiceTest(common.HeatTestCase): mock_stack_load.assert_called_once_with(self.ctx, stack=db_stack, service_check_defer=True, - resource_validate=False, - resolve_data=False) + resource_validate=False) self.assertTrue(lock2.release.called) mock_thread.start_with_acquired_lock.assert_called_once_with( fake_stack, lock1, diff --git a/heat/tests/test_stack.py b/heat/tests/test_stack.py index 54084d34d7..da83120146 100644 --- a/heat/tests/test_stack.py +++ b/heat/tests/test_stack.py @@ -425,7 +425,7 @@ class StackTest(common.HeatTestCase): stack.Stack.__init__(self.ctx, stk.name, t, stack_id=stk.id, action=stk.action, status=stk.status, status_reason=stk.status_reason, - timeout_mins=stk.timeout, resolve_data=True, + timeout_mins=stk.timeout, disable_rollback=stk.disable_rollback, parent_resource='parent', owner_id=None, stack_user_project_id=None, @@ -2717,7 +2717,7 @@ class StackTest(common.HeatTestCase): mock_dependency.validate.assert_called_once_with() stc = stack.Stack(self.ctx, utils.random_name(), self.tmpl) - stc.outputs = {'foo': {'Value': 'bar'}} + stc._outputs = {'foo': {'Value': 'bar'}} func_val.side_effect = AssertionError(expected_msg) expected_exception = self.assertRaises(AssertionError, stc.validate) self.assertEqual(expected_msg, six.text_type(expected_exception)) @@ -2727,8 +2727,7 @@ class StackTest(common.HeatTestCase): expected_message = 'Expected Assertion Error' tmpl.parse.side_effect = AssertionError(expected_message) - stc = stack.Stack(self.ctx, utils.random_name(), - tmpl, resolve_data=False) + stc = stack.Stack(self.ctx, utils.random_name(), tmpl) expected_exception = self.assertRaises(AssertionError, stc.resolve_outputs_data, None) diff --git a/heat/tests/test_template.py b/heat/tests/test_template.py index 511705e1d3..ea6d2acf11 100644 --- a/heat/tests/test_template.py +++ b/heat/tests/test_template.py @@ -320,8 +320,7 @@ class TestTemplateConditionParser(common.HeatTestCase): {'get_attr': [None, 'att']}]}}} # test with get_attr in equals tmpl = template.Template(t) - stk = stack.Stack(self.ctx, 'test_condition_with_get_attr_func', tmpl, - resolve_data=False) + stk = stack.Stack(self.ctx, 'test_condition_with_get_attr_func', tmpl) ex = self.assertRaises(exception.InvalidConditionFunction, tmpl._resolve_conditions, stk) self.assertIn('The function is not supported in condition: get_attr', @@ -329,8 +328,7 @@ class TestTemplateConditionParser(common.HeatTestCase): # test with get_resource in top level of a condition tmpl.t['conditions']['prod_env'] = {'get_resource': 'R1'} - stk = stack.Stack(self.ctx, 'test_condition_with_get_attr_func', tmpl, - resolve_data=False) + stk = stack.Stack(self.ctx, 'test_condition_with_get_attr_func', tmpl) ex = self.assertRaises(exception.InvalidConditionFunction, tmpl._resolve_conditions, stk) self.assertIn('The function is not supported in condition: ' @@ -338,8 +336,7 @@ class TestTemplateConditionParser(common.HeatTestCase): # test with get_attr in top level of a condition tmpl.t['conditions']['prod_env'] = {'get_attr': [None, 'att']} - stk = stack.Stack(self.ctx, 'test_condition_with_get_attr_func', tmpl, - resolve_data=False) + stk = stack.Stack(self.ctx, 'test_condition_with_get_attr_func', tmpl) ex = self.assertRaises(exception.InvalidConditionFunction, tmpl._resolve_conditions, stk) self.assertIn('The function is not supported in condition: get_attr', diff --git a/heat/tests/test_validate.py b/heat/tests/test_validate.py index 5c68d877f8..fafb0326ee 100644 --- a/heat/tests/test_validate.py +++ b/heat/tests/test_validate.py @@ -1622,8 +1622,9 @@ class ValidateTest(common.HeatTestCase): def test_validate_invalid_outputs(self): t = template_format.parse(test_template_invalid_outputs) template = tmpl.Template(t) + stack = parser.Stack(self.ctx, 'test_stack', template) err = self.assertRaises(exception.StackValidationFailed, - parser.Stack, self.ctx, 'test_stack', template) + stack.validate) error_message = ('outputs.string.value.get_attr: Arguments to ' '"get_attr" must be of the form ' '[resource_name, attribute, (path), ...]')