Merge "Handle get_attribute in Tosca outputs"
This commit is contained in:
commit
222490b757
@ -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)
|
||||||
|
@ -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] }
|
||||||
|
@ -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'))
|
||||||
|
@ -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:
|
||||||
|
@ -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,
|
||||||
|
@ -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 ] }
|
@ -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 ] }
|
@ -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] }
|
||||||
|
@ -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))
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user