Merge "Add functions for outputs to heat service"
This commit is contained in:
commit
50395d41e9
@ -170,24 +170,30 @@ def translate_filters(params):
|
||||
return params
|
||||
|
||||
|
||||
def format_stack_outputs(stack, outputs):
|
||||
def format_stack_outputs(stack, outputs, resolve_value=False):
|
||||
"""Return a representation of the given output template.
|
||||
|
||||
Return a representation of the given output template for the given stack
|
||||
that matches the API output expectations.
|
||||
"""
|
||||
def format_stack_output(k):
|
||||
output = {
|
||||
rpc_api.OUTPUT_DESCRIPTION: outputs[k].get('Description',
|
||||
'No description given'),
|
||||
rpc_api.OUTPUT_KEY: k,
|
||||
rpc_api.OUTPUT_VALUE: stack.output(k)
|
||||
}
|
||||
if outputs[k].get('error_msg'):
|
||||
output.update({rpc_api.OUTPUT_ERROR: outputs[k].get('error_msg')})
|
||||
return output
|
||||
return [format_stack_output(stack, outputs,
|
||||
key, resolve_value=resolve_value)
|
||||
for key in outputs]
|
||||
|
||||
return [format_stack_output(key) for key in outputs]
|
||||
|
||||
def format_stack_output(stack, outputs, k, resolve_value=True):
|
||||
result = {
|
||||
rpc_api.OUTPUT_KEY: k,
|
||||
rpc_api.OUTPUT_DESCRIPTION: outputs[k].get('Description',
|
||||
'No description given'),
|
||||
}
|
||||
|
||||
if resolve_value:
|
||||
result.update({rpc_api.OUTPUT_VALUE: stack.output(k)})
|
||||
|
||||
if outputs[k].get('error_msg'):
|
||||
result.update({rpc_api.OUTPUT_ERROR: outputs[k].get('error_msg')})
|
||||
return result
|
||||
|
||||
|
||||
def format_stack(stack, preview=False):
|
||||
@ -227,7 +233,8 @@ def format_stack(stack, preview=False):
|
||||
# allow users to view the outputs of stacks
|
||||
if stack.action != stack.DELETE and stack.status != stack.IN_PROGRESS:
|
||||
info[rpc_api.STACK_OUTPUTS] = format_stack_outputs(stack,
|
||||
stack.outputs)
|
||||
stack.outputs,
|
||||
resolve_value=True)
|
||||
|
||||
return info
|
||||
|
||||
|
@ -1069,6 +1069,39 @@ class EngineService(service.Service):
|
||||
return s.raw_template.template
|
||||
return None
|
||||
|
||||
@context.request_context
|
||||
def list_outputs(self, cntx, stack_identity):
|
||||
"""Get a list of stack outputs.
|
||||
|
||||
:param cntx: RPC context.
|
||||
:param stack_identity: Name of the stack you want to see.
|
||||
: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)
|
||||
|
||||
return api.format_stack_outputs(stack, stack.t[stack.t.OUTPUTS])
|
||||
|
||||
@context.request_context
|
||||
def show_output(self, cntx, stack_identity, output_key):
|
||||
"""Returns dict with specified output key, value and description.
|
||||
|
||||
:param cntx: RPC context.
|
||||
:param stack_identity: Name of the stack you want to see.
|
||||
:param output_key: key of desired stack output.
|
||||
: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)
|
||||
|
||||
outputs = stack.t[stack.t.OUTPUTS]
|
||||
|
||||
if output_key not in outputs:
|
||||
raise exception.NotFound(_('Specified output key %s not '
|
||||
'found.') % output_key)
|
||||
output = stack.resolve_static_data(outputs[output_key])
|
||||
return api.format_stack_output(stack, {output_key: output}, output_key)
|
||||
|
||||
def _remote_call(self, cnxt, lock_engine_id, call, **kwargs):
|
||||
timeout = cfg.CONF.engine_life_check_timeout
|
||||
self.cctxt = self._client.prepare(
|
||||
|
@ -382,7 +382,8 @@ 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):
|
||||
use_stored_context=False, force_reload=False, cache_data=None,
|
||||
resolve_data=True):
|
||||
"""Retrieve a Stack from the database."""
|
||||
if stack is None:
|
||||
stack = stack_object.Stack.get_by_id(
|
||||
@ -399,7 +400,7 @@ class Stack(collections.Mapping):
|
||||
|
||||
return cls._from_db(context, stack,
|
||||
use_stored_context=use_stored_context,
|
||||
cache_data=cache_data)
|
||||
cache_data=cache_data, resolve_data=resolve_data)
|
||||
|
||||
@classmethod
|
||||
def load_all(cls, context, limit=None, marker=None, sort_keys=None,
|
||||
|
@ -412,7 +412,8 @@ class FormatTest(common.HeatTestCase):
|
||||
stack.status = 'COMPLETE'
|
||||
stack['generic'].action = 'CREATE'
|
||||
stack['generic'].status = 'COMPLETE'
|
||||
info = api.format_stack_outputs(stack, stack.outputs)
|
||||
info = api.format_stack_outputs(stack, stack.outputs,
|
||||
resolve_value=True)
|
||||
expected = [{'description': 'No description given',
|
||||
'output_error': 'The Referenced Attribute (generic Bar) '
|
||||
'is incorrect.',
|
||||
@ -425,6 +426,37 @@ class FormatTest(common.HeatTestCase):
|
||||
self.assertEqual(expected, sorted(info, key=lambda k: k['output_key'],
|
||||
reverse=True))
|
||||
|
||||
def test_format_stack_outputs_unresolved(self):
|
||||
tmpl = template.Template({
|
||||
'HeatTemplateFormatVersion': '2012-12-12',
|
||||
'Resources': {
|
||||
'generic': {'Type': 'GenericResourceType'}
|
||||
},
|
||||
'Outputs': {
|
||||
'correct_output': {
|
||||
'Description': 'Good output',
|
||||
'Value': {'Fn::GetAtt': ['generic', 'Foo']}
|
||||
},
|
||||
'incorrect_output': {
|
||||
'Value': {'Fn::GetAtt': ['generic', 'Bar']}
|
||||
}
|
||||
}
|
||||
})
|
||||
stack = parser.Stack(utils.dummy_context(), 'test_stack',
|
||||
tmpl, stack_id=str(uuid.uuid4()))
|
||||
stack.action = 'CREATE'
|
||||
stack.status = 'COMPLETE'
|
||||
stack['generic'].action = 'CREATE'
|
||||
stack['generic'].status = 'COMPLETE'
|
||||
info = api.format_stack_outputs(stack, stack.outputs)
|
||||
expected = [{'description': 'No description given',
|
||||
'output_key': 'incorrect_output'},
|
||||
{'description': 'Good output',
|
||||
'output_key': 'correct_output'}]
|
||||
|
||||
self.assertEqual(expected, sorted(info, key=lambda k: k['output_key'],
|
||||
reverse=True))
|
||||
|
||||
|
||||
class FormatValidateParameterTest(common.HeatTestCase):
|
||||
|
||||
|
@ -1019,6 +1019,62 @@ class StackServiceTest(common.HeatTestCase):
|
||||
msg = "Template with version %s not found" % version
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
|
||||
def test_stack_list_outputs(self):
|
||||
t = template_format.parse(tools.wp_template)
|
||||
t['outputs'] = {
|
||||
'test': {'value': '{ get_attr: fir }',
|
||||
'description': 'sec'},
|
||||
'test2': {'value': 'sec'}}
|
||||
tmpl = templatem.Template(t)
|
||||
stack = parser.Stack(self.ctx, 'service_list_outputs_stack', tmpl)
|
||||
|
||||
self.patchobject(self.eng, '_get_stack')
|
||||
self.patchobject(parser.Stack, 'load', return_value=stack)
|
||||
|
||||
outputs = self.eng.list_outputs(self.ctx, mock.ANY)
|
||||
|
||||
self.assertIn({'output_key': 'test',
|
||||
'description': 'sec'}, outputs)
|
||||
self.assertIn({'output_key': 'test2',
|
||||
'description': 'No description given'},
|
||||
outputs)
|
||||
|
||||
def test_stack_empty_list_outputs(self):
|
||||
# Ensure that stack with no output returns empty list
|
||||
t = template_format.parse(tools.wp_template)
|
||||
t['outputs'] = {}
|
||||
tmpl = templatem.Template(t)
|
||||
stack = parser.Stack(self.ctx, 'service_list_outputs_stack', tmpl)
|
||||
|
||||
self.patchobject(self.eng, '_get_stack')
|
||||
self.patchobject(parser.Stack, 'load', return_value=stack)
|
||||
|
||||
outputs = self.eng.list_outputs(self.ctx, mock.ANY)
|
||||
self.assertEqual([], outputs)
|
||||
|
||||
def test_stack_show_output(self):
|
||||
t = template_format.parse(tools.wp_template)
|
||||
t['outputs'] = {'test': {'value': 'first', 'description': 'sec'},
|
||||
'test2': {'value': 'sec'}}
|
||||
tmpl = templatem.Template(t)
|
||||
stack = parser.Stack(self.ctx, 'service_list_outputs_stack', tmpl)
|
||||
|
||||
self.patchobject(self.eng, '_get_stack')
|
||||
self.patchobject(parser.Stack, 'load', return_value=stack)
|
||||
|
||||
output = self.eng.show_output(self.ctx, mock.ANY, 'test')
|
||||
self.assertEqual({'output_key': 'test', 'output_value': 'first',
|
||||
'description': 'sec'},
|
||||
output)
|
||||
|
||||
# Ensure that stack raised NotFound error with incorrect key.
|
||||
ex = self.assertRaises(dispatcher.ExpectedException,
|
||||
self.eng.show_output,
|
||||
self.ctx, mock.ANY, 'bunny')
|
||||
self.assertEqual(exception.NotFound, ex.exc_info[0])
|
||||
self.assertEqual('Specified output key bunny not found.',
|
||||
six.text_type(ex.exc_info[1]))
|
||||
|
||||
def test_stack_list_all_empty(self):
|
||||
sl = self.eng.list_stacks(self.ctx)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user