Merge "Handle get_attribute in Tosca outputs"

This commit is contained in:
Jenkins 2014-11-17 19:33:04 +00:00 committed by Gerrit Code Review
commit 222490b757
10 changed files with 220 additions and 25 deletions

View File

@ -12,6 +12,8 @@
# under the License. # under the License.
from translator.hot.syntax.hot_output import HotOutput from translator.hot.syntax.hot_output import HotOutput
from translator.toscalib import functions
from translator.toscalib.utils.gettextutils import _
TOSCA_TO_HOT_GET_ATTRS = {'ip_address': 'first_address'} TOSCA_TO_HOT_GET_ATTRS = {'ip_address': 'first_address'}
@ -29,17 +31,28 @@ class TranslateOutputs():
hot_outputs = [] hot_outputs = []
for output in self.outputs: for output in self.outputs:
hot_value = {} hot_value = {}
if 'get_property' in output.value: if isinstance(output.value, functions.GetAttribute):
get_parameters = output.value['get_property'] func = output.value
if get_parameters[1] in TOSCA_TO_HOT_GET_ATTRS: get_parameters = [
get_parameters[1] = \ func.node_template_name,
TOSCA_TO_HOT_GET_ATTRS[get_parameters[1]] self._translate_attribute_name(func.attribute_name)]
hot_value['get_attr'] = get_parameters
elif isinstance(output.value, functions.GetProperty):
func = output.value
if func.req_or_cap:
raise NotImplementedError(_(
'get_property with requirement/capability in outputs '
'translation is not supported'))
get_parameters = [
func.node_template_name,
self._translate_attribute_name(func.property_name)]
hot_value['get_attr'] = get_parameters hot_value['get_attr'] = get_parameters
hot_outputs.append(HotOutput(output.name,
hot_value,
output.description))
else: else:
hot_outputs.append(HotOutput(output.name, hot_value['get_attr'] = output.value
output.value, hot_outputs.append(HotOutput(output.name,
output.description)) hot_value,
output.description))
return hot_outputs return hot_outputs
def _translate_attribute_name(self, attribute_name):
return TOSCA_TO_HOT_GET_ATTRS.get(attribute_name, attribute_name)

View File

@ -47,4 +47,7 @@ node_templates:
outputs: outputs:
public_ip: public_ip:
description: Public IP address of the newly created compute instance. description: Public IP address of the newly created compute instance.
value: { get_attribute: [server, ip_address] } value: { get_attribute: [my_server, ip_address] }
volume_id:
description: The volume id of the block storage instance.
value: { get_attribute: [my_storage, volume_id] }

View File

@ -46,6 +46,19 @@ class ToscaBlockStorageTest(TestCase):
expected_value = expected_resouce.get('attachto_1') expected_value = expected_resouce.get('attachto_1')
self.assertEqual(translated_value, expected_value) self.assertEqual(translated_value, expected_value)
outputs = output_dict['outputs']
self.assertIn('public_ip', outputs)
self.assertEqual(
'Public IP address of the newly created compute instance.',
outputs['public_ip']['description'])
self.assertEqual({'get_attr': ['my_server', 'first_address']},
outputs['public_ip']['value'])
self.assertIn('volume_id', outputs)
self.assertEqual('The volume id of the block storage instance.',
outputs['volume_id']['description'])
self.assertEqual({'get_attr': ['my_storage', 'volume_id']},
outputs['volume_id']['value'])
def test_translate_multi_storage(self): def test_translate_multi_storage(self):
'''TOSCA template with multiple BlockStorage and Attachment.''' '''TOSCA template with multiple BlockStorage and Attachment.'''
tosca_tpl = os.path.join( tosca_tpl = os.path.join(
@ -69,7 +82,6 @@ class ToscaBlockStorageTest(TestCase):
'volume_id': 'my_storage2'}} 'volume_id': 'my_storage2'}}
output_dict = translator.toscalib.utils.yamlparser.simple_parse(output) output_dict = translator.toscalib.utils.yamlparser.simple_parse(output)
resources = output_dict.get('resources') resources = output_dict.get('resources')
translated_volume_attachment.append(resources.get('attachto_1')) translated_volume_attachment.append(resources.get('attachto_1'))
translated_volume_attachment.append(resources.get('attachto_2')) translated_volume_attachment.append(resources.get('attachto_2'))

View File

@ -179,7 +179,7 @@ tosca.nodes.BlockStorage:
type: string type: string
required: false required: false
attributes: attributes:
volumeId: volume_id:
type: string type: string
capabilities: capabilities:
attachment: attachment:

View File

@ -86,13 +86,58 @@ class GetInput(Function):
class GetAttribute(Function): class GetAttribute(Function):
"""Get an attribute value of an entity defined in the service template
Node template attributes values are set in runtime and therefore its the
responsibility of the Tosca engine to implement the evaluation of
get_attribute functions.
Arguments:
* Node template name.
* Attribute name.
Examples:
* { get_attribute: [ server, ip_address ] }
"""
def validate(self): def validate(self):
pass if len(self.args) != 2:
raise ValueError(_(
'Illegal arguments for {0} function. Expected arguments: '
'node-template-name, attribute-name').format(GET_ATTRIBUTE))
self._find_attribute(self.args[1])
def result(self): def result(self):
pass pass
def _find_attribute(self, attribute_name):
node_tpl = self._find_node_template(self.args[0])
found = [
attr for attr in node_tpl.type_definition.attributes_def
if attr.name == attribute_name]
if len(found) == 0:
raise KeyError(_(
"Attribute: '{0}' not found in node template: {1}.").format(
attribute_name, node_tpl.name))
return found[0]
def _find_node_template(self, node_template_name):
for node_template in self.tosca_tpl.nodetemplates:
if node_template.name == node_template_name:
return node_template
raise KeyError(_(
'No such node template: {0}.').format(node_template_name))
@property
def node_template_name(self):
return self.args[0]
@property
def attribute_name(self):
return self.args[1]
class GetProperty(Function): class GetProperty(Function):
"""Get a property value of an entity defined in the same service template. """Get a property value of an entity defined in the same service template.
@ -209,6 +254,22 @@ class GetProperty(Function):
self.context, self.context,
property_value) property_value)
@property
def node_template_name(self):
return self.args[0]
@property
def property_name(self):
if len(self.args) > 2:
return self.args[2]
return self.args[1]
@property
def req_or_cap(self):
if len(self.args) > 2:
return self.args[1]
return None
function_mappings = { function_mappings = {
GET_PROPERTY: GetProperty, GET_PROPERTY: GetProperty,

View File

@ -0,0 +1,25 @@
tosca_definitions_version: tosca_simple_1.0
description: >
Tosca template for testing unknown attribute name in get_attribute
function.
inputs:
image_id:
type: string
node_templates:
server:
type: tosca.nodes.Compute
properties:
os_type: Linux
interfaces:
tosca.interfaces.node.Lifecycle:
configure:
implementation: start_server.sh
input:
image_id: { get_input: image_id }
outputs:
ip_address:
value: { get_attribute: [ server, unknown_attribute ] }

View File

@ -0,0 +1,25 @@
tosca_definitions_version: tosca_simple_1.0
description: >
Tosca template for testing unknown node template name in get_attribute
function.
inputs:
image_id:
type: string
node_templates:
server:
type: tosca.nodes.Compute
properties:
os_type: Linux
interfaces:
tosca.interfaces.node.Lifecycle:
configure:
implementation: start_server.sh
input:
image_id: { get_input: image_id }
outputs:
ip_address:
value: { get_attribute: [ unknown_node_template, ip_address ] }

View File

@ -27,4 +27,4 @@ node_templates:
outputs: outputs:
server_address: server_address:
description: IP address of server instance. description: IP address of server instance.
value: { get_property: [server, ip_address] } value: { get_attribute: [server, ip_address] }

View File

@ -96,3 +96,50 @@ class IntrinsicFunctionsTest(TestCase):
self.assertRaises(exception.UnknownInputError, self.assertRaises(exception.UnknownInputError,
self._load_template, self._load_template,
'functions/test_unknown_input_in_interface.yaml') 'functions/test_unknown_input_in_interface.yaml')
class GetAttributeTest(TestCase):
def _load_template(self, filename):
return ToscaTemplate(os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'data',
filename))
def test_get_attribute_in_outputs(self):
tpl = self._load_template('tosca_single_instance_wordpress.yaml')
website_url_output = [
x for x in tpl.outputs if x.name == 'website_url'][0]
self.assertIsInstance(website_url_output.value, functions.GetAttribute)
self.assertEqual('server', website_url_output.value.node_template_name)
self.assertEqual('ip_address', website_url_output.value.attribute_name)
def test_get_attribute_invalid_args(self):
expected_msg = 'Expected arguments: node-template-name, attribute-name'
err = self.assertRaises(ValueError,
functions.get_function, None, None,
{'get_attribute': []})
self.assertIn(expected_msg, six.text_type(err))
err = self.assertRaises(ValueError,
functions.get_function, None, None,
{'get_attribute': ['x']})
self.assertIn(expected_msg, six.text_type(err))
err = self.assertRaises(ValueError,
functions.get_function, None, None,
{'get_attribute': ['x', 'y', 'z']})
self.assertIn(expected_msg, six.text_type(err))
def test_get_attribute_unknown_node_template_name(self):
err = self.assertRaises(
KeyError,
self._load_template,
'functions/test_get_attribute_unknown_node_template_name.yaml')
self.assertIn('unknown_node_template', six.text_type(err))
def test_get_attribute_unknown_attribute(self):
err = self.assertRaises(
KeyError,
self._load_template,
'functions/test_get_attribute_unknown_attribute_name.yaml')
self.assertIn('unknown_attribute', six.text_type(err))
self.assertIn('server', six.text_type(err))

View File

@ -16,7 +16,7 @@ import os
from translator.toscalib.common.exception import MissingRequiredFieldError from translator.toscalib.common.exception import MissingRequiredFieldError
from translator.toscalib.common.exception import UnknownFieldError from translator.toscalib.common.exception import UnknownFieldError
from translator.toscalib.functions import get_function from translator.toscalib import functions
from translator.toscalib.nodetemplate import NodeTemplate from translator.toscalib.nodetemplate import NodeTemplate
from translator.toscalib.parameters import Input, Output from translator.toscalib.parameters import Input, Output
from translator.toscalib.tpl_relationship_graph import ToscaGraph from translator.toscalib.tpl_relationship_graph import ToscaGraph
@ -128,21 +128,30 @@ class ToscaTemplate(object):
"""Process intrinsic functions """Process intrinsic functions
Current implementation processes functions within node template Current implementation processes functions within node template
properties, requirements and interfaces inputs. properties, requirements, interfaces inputs and template outputs.
""" """
for node_template in self.nodetemplates: for node_template in self.nodetemplates:
for prop in node_template.properties: for prop in node_template.properties:
prop.value = get_function(self, node_template, prop.value) prop.value = functions.get_function(self,
node_template,
prop.value)
for interface in node_template.interfaces: for interface in node_template.interfaces:
if interface.input: if interface.input:
for name, value in interface.input.items(): for name, value in interface.input.items():
interface.input[name] = get_function(self, interface.input[name] = functions.get_function(
node_template, self,
value) node_template,
value)
if node_template.requirements: if node_template.requirements:
for req in node_template.requirements: for req in node_template.requirements:
if 'properties' in req: if 'properties' in req:
for key, value in req['properties'].items(): for key, value in req['properties'].items():
req['properties'][key] = get_function(self, req['properties'][key] = functions.get_function(
req, self,
value) req,
value)
for output in self.outputs:
func = functions.get_function(self, self.outputs, output.value)
if isinstance(func, functions.GetAttribute):
output.attrs[output.VALUE] = func