Merge "Add functions for outputs to heat service"

This commit is contained in:
Jenkins 2015-11-26 15:30:11 +00:00 committed by Gerrit Code Review
commit 50395d41e9
5 changed files with 145 additions and 16 deletions

View File

@ -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

View File

@ -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(

View File

@ -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,

View File

@ -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):

View File

@ -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)