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.
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'}
@ -29,17 +31,28 @@ class TranslateOutputs():
hot_outputs = []
for output in self.outputs:
hot_value = {}
if 'get_property' in output.value:
get_parameters = output.value['get_property']
if get_parameters[1] in TOSCA_TO_HOT_GET_ATTRS:
get_parameters[1] = \
TOSCA_TO_HOT_GET_ATTRS[get_parameters[1]]
if isinstance(output.value, functions.GetAttribute):
func = output.value
get_parameters = [
func.node_template_name,
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_outputs.append(HotOutput(output.name,
hot_value,
output.description))
else:
hot_outputs.append(HotOutput(output.name,
output.value,
output.description))
hot_value['get_attr'] = output.value
hot_outputs.append(HotOutput(output.name,
hot_value,
output.description))
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:
public_ip:
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')
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):
'''TOSCA template with multiple BlockStorage and Attachment.'''
tosca_tpl = os.path.join(
@ -69,7 +82,6 @@ class ToscaBlockStorageTest(TestCase):
'volume_id': 'my_storage2'}}
output_dict = translator.toscalib.utils.yamlparser.simple_parse(output)
resources = output_dict.get('resources')
translated_volume_attachment.append(resources.get('attachto_1'))
translated_volume_attachment.append(resources.get('attachto_2'))

View File

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

View File

@ -86,13 +86,58 @@ class GetInput(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):
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):
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):
"""Get a property value of an entity defined in the same service template.
@ -209,6 +254,22 @@ class GetProperty(Function):
self.context,
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 = {
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:
server_address:
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._load_template,
'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 UnknownFieldError
from translator.toscalib.functions import get_function
from translator.toscalib import functions
from translator.toscalib.nodetemplate import NodeTemplate
from translator.toscalib.parameters import Input, Output
from translator.toscalib.tpl_relationship_graph import ToscaGraph
@ -128,21 +128,30 @@ class ToscaTemplate(object):
"""Process intrinsic functions
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 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:
if interface.input:
for name, value in interface.input.items():
interface.input[name] = get_function(self,
node_template,
value)
interface.input[name] = functions.get_function(
self,
node_template,
value)
if node_template.requirements:
for req in node_template.requirements:
if 'properties' in req:
for key, value in req['properties'].items():
req['properties'][key] = get_function(self,
req,
value)
req['properties'][key] = functions.get_function(
self,
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