Merge "Handle get_attribute in Tosca outputs"
This commit is contained in:
commit
222490b757
@ -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)
|
||||
|
@ -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] }
|
||||
|
@ -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'))
|
||||
|
@ -179,7 +179,7 @@ tosca.nodes.BlockStorage:
|
||||
type: string
|
||||
required: false
|
||||
attributes:
|
||||
volumeId:
|
||||
volume_id:
|
||||
type: string
|
||||
capabilities:
|
||||
attachment:
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
server_address:
|
||||
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._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))
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user