Fully implements the semantic of get_* functions :
- handle optional capability/requirement parameter - handle optional nested property / index Handle functions in outputs. Change-Id: I3f070f0a2531b31c897e96c3581280d8afb09d89
This commit is contained in:
parent
1267ad19c0
commit
f17a5c24af
@ -18,6 +18,7 @@ import numbers
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
import six
|
||||
from six.moves.urllib.parse import urlparse
|
||||
import yaml
|
||||
|
||||
@ -262,12 +263,17 @@ class UrlUtils(object):
|
||||
|
||||
def str_to_num(value):
|
||||
"""Convert a string representation of a number into a numeric type."""
|
||||
if isinstance(value, numbers.Number):
|
||||
if isinstance(value, numbers.Number) \
|
||||
or isinstance(value, six.integer_types) \
|
||||
or isinstance(value, float):
|
||||
return value
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
return float(value)
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def check_for_env_variables():
|
||||
|
@ -104,7 +104,7 @@ class HotResource(object):
|
||||
# scenarios and cannot be fixed or hard coded here
|
||||
operations_deploy_sequence = ['create', 'configure', 'start']
|
||||
|
||||
operations = HotResource._get_all_operations(self.nodetemplate)
|
||||
operations = HotResource.get_all_operations(self.nodetemplate)
|
||||
|
||||
# create HotResource for each operation used for deployment:
|
||||
# create, start, configure
|
||||
@ -351,7 +351,7 @@ class HotResource(object):
|
||||
return tosca_props
|
||||
|
||||
@staticmethod
|
||||
def _get_all_operations(node):
|
||||
def get_all_operations(node):
|
||||
operations = {}
|
||||
for operation in node.interfaces:
|
||||
operations[operation.name] = operation
|
||||
|
@ -67,5 +67,5 @@ class ToscaBlockStorage(HotResource):
|
||||
# attribute for the matching resource. Unless there is additional
|
||||
# runtime support, this should be a one to one mapping.
|
||||
if attribute == 'volume_id':
|
||||
attr['get_resource'] = args[0]
|
||||
attr['get_resource'] = self.name
|
||||
return attr
|
||||
|
@ -25,6 +25,7 @@ from toscaparser.utils.gettextutils import _
|
||||
from translator.common.exception import ToscaClassAttributeError
|
||||
from translator.common.exception import ToscaClassImportError
|
||||
from translator.common.exception import ToscaModImportError
|
||||
from translator.common import utils
|
||||
from translator.conf.config import ConfigProvider as translatorConfig
|
||||
from translator.hot.syntax.hot_resource import HotResource
|
||||
from translator.hot.tosca.tosca_block_storage_attachment import (
|
||||
@ -312,7 +313,7 @@ class TranslateNodeTemplates(object):
|
||||
inputs = resource.properties.get('input_values')
|
||||
if inputs:
|
||||
for name, value in six.iteritems(inputs):
|
||||
inputs[name] = self._translate_input(value, resource)
|
||||
inputs[name] = self.translate_param_value(value, resource)
|
||||
|
||||
# remove resources without type defined
|
||||
# for example a SoftwareComponent without interfaces
|
||||
@ -327,49 +328,118 @@ class TranslateNodeTemplates(object):
|
||||
|
||||
return self.hot_resources
|
||||
|
||||
def _translate_input(self, input_value, resource):
|
||||
def translate_param_value(self, param_value, resource):
|
||||
tosca_template = None
|
||||
if resource:
|
||||
tosca_template = resource.nodetemplate
|
||||
|
||||
get_property_args = None
|
||||
if isinstance(input_value, GetProperty):
|
||||
get_property_args = input_value.args
|
||||
if isinstance(param_value, GetProperty):
|
||||
get_property_args = param_value.args
|
||||
# to remove when the parser is fixed to return GetProperty
|
||||
if isinstance(input_value, dict) and 'get_property' in input_value:
|
||||
get_property_args = input_value['get_property']
|
||||
if isinstance(param_value, dict) and 'get_property' in param_value:
|
||||
get_property_args = param_value['get_property']
|
||||
if get_property_args is not None:
|
||||
hot_target = self._find_hot_resource_for_tosca(
|
||||
get_property_args[0], resource)
|
||||
if hot_target:
|
||||
props = hot_target.get_tosca_props()
|
||||
prop_name = get_property_args[1]
|
||||
if prop_name in props:
|
||||
return props[prop_name]
|
||||
elif isinstance(input_value, GetAttribute):
|
||||
tosca_target, prop_name, prop_arg = \
|
||||
self.decipher_get_operation(get_property_args,
|
||||
tosca_template)
|
||||
if tosca_target:
|
||||
prop_value = tosca_target.get_property_value(prop_name)
|
||||
if prop_value:
|
||||
prop_value = self.translate_param_value(
|
||||
prop_value, resource)
|
||||
return self._unfold_value(prop_value, prop_arg)
|
||||
get_attr_args = None
|
||||
if isinstance(param_value, GetAttribute):
|
||||
get_attr_args = param_value.result().args
|
||||
# to remove when the parser is fixed to return GetAttribute
|
||||
if isinstance(param_value, dict) and 'get_attribute' in param_value:
|
||||
get_attr_args = param_value['get_attribute']
|
||||
if get_attr_args is not None:
|
||||
# for the attribute
|
||||
# get the proper target type to perform the translation
|
||||
args = input_value.result().args
|
||||
hot_target = self._find_hot_resource_for_tosca(args[0], resource)
|
||||
tosca_target, attr_name, attr_arg = \
|
||||
self.decipher_get_operation(get_attr_args, tosca_template)
|
||||
attr_args = []
|
||||
if attr_arg:
|
||||
attr_args += attr_arg
|
||||
if tosca_target:
|
||||
if tosca_target in self.hot_lookup:
|
||||
attr_value = self.hot_lookup[tosca_target].\
|
||||
get_hot_attribute(attr_name, attr_args)
|
||||
attr_value = self.translate_param_value(
|
||||
attr_value, resource)
|
||||
return self._unfold_value(attr_value, attr_arg)
|
||||
elif isinstance(param_value, dict) and 'get_artifact' in param_value:
|
||||
get_artifact_args = param_value['get_artifact']
|
||||
tosca_target, artifact_name, _ = \
|
||||
self.decipher_get_operation(get_artifact_args,
|
||||
tosca_template)
|
||||
|
||||
return hot_target.get_hot_attribute(args[1], args)
|
||||
# most of artifacts logic should move to the parser
|
||||
elif isinstance(input_value, dict) and 'get_artifact' in input_value:
|
||||
get_artifact_args = input_value['get_artifact']
|
||||
|
||||
hot_target = self._find_hot_resource_for_tosca(
|
||||
get_artifact_args[0], resource)
|
||||
artifacts = TranslateNodeTemplates.get_all_artifacts(
|
||||
hot_target.nodetemplate)
|
||||
|
||||
if get_artifact_args[1] in artifacts:
|
||||
artifact = artifacts[get_artifact_args[1]]
|
||||
if artifact.get('type', None) == 'tosca.artifacts.File':
|
||||
return {'get_file': artifact.get('file')}
|
||||
elif isinstance(input_value, GetInput):
|
||||
if isinstance(input_value.args, list) \
|
||||
and len(input_value.args) == 1:
|
||||
return {'get_param': input_value.args[0]}
|
||||
if tosca_target:
|
||||
artifacts = self.get_all_artifacts(tosca_target)
|
||||
if artifact_name in artifacts:
|
||||
artifact = artifacts[artifact_name]
|
||||
if artifact.get('type', None) == 'tosca.artifacts.File':
|
||||
return {'get_file': artifact.get('file')}
|
||||
get_input_args = None
|
||||
if isinstance(param_value, GetInput):
|
||||
get_input_args = param_value.args
|
||||
if isinstance(param_value, dict) and 'get_input' in param_value:
|
||||
get_input_args = param_value['get_input']
|
||||
if get_input_args is not None:
|
||||
if isinstance(get_input_args, list) \
|
||||
and len(get_input_args) == 1:
|
||||
return {'get_param': self.translate_param_value(
|
||||
get_input_args[0], resource)}
|
||||
else:
|
||||
return {'get_param': input_value.args}
|
||||
return {'get_param': self.translate_param_value(
|
||||
get_input_args, resource)}
|
||||
|
||||
return input_value
|
||||
return param_value
|
||||
|
||||
@staticmethod
|
||||
def _unfold_value(value, value_arg):
|
||||
if value_arg is not None:
|
||||
if isinstance(value, dict):
|
||||
val = value.get(value_arg)
|
||||
if val is not None:
|
||||
return val
|
||||
|
||||
index = utils.str_to_num(value_arg)
|
||||
if isinstance(value, list) and index is not None:
|
||||
return value[index]
|
||||
return value
|
||||
|
||||
def decipher_get_operation(self, args, current_tosca_node):
|
||||
tosca_target = self._find_tosca_node(args[0],
|
||||
current_tosca_node)
|
||||
new_target = None
|
||||
if tosca_target and len(args) > 2:
|
||||
cap_or_req_name = args[1]
|
||||
cap = tosca_target.get_capability(cap_or_req_name)
|
||||
if cap:
|
||||
new_target = cap
|
||||
else:
|
||||
for req in tosca_target.requirements:
|
||||
if cap_or_req_name in req:
|
||||
new_target = self._find_tosca_node(
|
||||
req[cap_or_req_name])
|
||||
cap = new_target.get_capability(cap_or_req_name)
|
||||
if cap:
|
||||
new_target = cap
|
||||
break
|
||||
|
||||
if new_target:
|
||||
tosca_target = new_target
|
||||
|
||||
prop_name = args[2]
|
||||
prop_arg = args[3] if len(args) >= 4 else None
|
||||
else:
|
||||
prop_name = args[1]
|
||||
prop_arg = args[2] if len(args) >= 3 else None
|
||||
|
||||
return tosca_target, prop_name, prop_arg
|
||||
|
||||
@staticmethod
|
||||
def get_all_artifacts(nodetemplate):
|
||||
@ -438,23 +508,29 @@ class TranslateNodeTemplates(object):
|
||||
if resource.name == name:
|
||||
return resource
|
||||
|
||||
def _find_tosca_node(self, tosca_name):
|
||||
for node in self.nodetemplates:
|
||||
if node.name == tosca_name:
|
||||
return node
|
||||
def _find_tosca_node(self, tosca_name, current_tosca_template=None):
|
||||
tosca_node = None
|
||||
if tosca_name == 'SELF':
|
||||
tosca_node = current_tosca_template
|
||||
if tosca_name == 'HOST' and current_tosca_template:
|
||||
for req in current_tosca_template.requirements:
|
||||
if 'host' in req:
|
||||
tosca_node = self._find_tosca_node(req['host'])
|
||||
|
||||
if tosca_node is None:
|
||||
for node in self.nodetemplates:
|
||||
if node.name == tosca_name:
|
||||
tosca_node = node
|
||||
break
|
||||
return tosca_node
|
||||
|
||||
def _find_hot_resource_for_tosca(self, tosca_name,
|
||||
current_hot_resource=None):
|
||||
if tosca_name == 'SELF':
|
||||
return current_hot_resource
|
||||
if tosca_name == 'HOST' and current_hot_resource is not None:
|
||||
for req in current_hot_resource.nodetemplate.requirements:
|
||||
if 'host' in req:
|
||||
return self._find_hot_resource_for_tosca(req['host'])
|
||||
|
||||
for node in self.nodetemplates:
|
||||
if node.name == tosca_name:
|
||||
return self.hot_lookup[node]
|
||||
current_tosca_resource = current_hot_resource.nodetemplate \
|
||||
if current_hot_resource else None
|
||||
tosca_node = self._find_tosca_node(tosca_name, current_tosca_resource)
|
||||
if tosca_node:
|
||||
return self.hot_lookup[tosca_node]
|
||||
|
||||
return None
|
||||
|
||||
|
@ -33,16 +33,7 @@ class TranslateOutputs(object):
|
||||
def _translate_outputs(self):
|
||||
hot_outputs = []
|
||||
for output in self.outputs:
|
||||
if output.value.name == 'get_attribute':
|
||||
get_parameters = output.value.args
|
||||
hot_target = self.nodes.find_hot_resource(get_parameters[0])
|
||||
hot_value = hot_target.get_hot_attribute(get_parameters[1],
|
||||
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 = self.nodes.translate_param_value(output.value, None)
|
||||
hot_outputs.append(HotOutput(output.name, hot_value,
|
||||
output.description))
|
||||
return hot_outputs
|
||||
|
@ -0,0 +1,40 @@
|
||||
heat_template_version: 2013-05-23
|
||||
|
||||
description: >
|
||||
TOSCA template to test get_* functions semantic
|
||||
|
||||
parameters:
|
||||
map_val:
|
||||
type: string
|
||||
resources:
|
||||
myapp_configure_deploy:
|
||||
type: OS::Heat::SoftwareDeployment
|
||||
properties:
|
||||
config:
|
||||
get_resource: myapp_configure_config
|
||||
input_values:
|
||||
the_list_val: list_val_0
|
||||
server:
|
||||
get_resource: server
|
||||
depends_on:
|
||||
- mysql_database
|
||||
server:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: m1.small
|
||||
image: ubuntu-12.04-software-config-os-init
|
||||
user_data_format: SOFTWARE_CONFIG
|
||||
myapp_configure_config:
|
||||
type: OS::Heat::SoftwareConfig
|
||||
properties:
|
||||
config:
|
||||
get_file: myapp_configure.sh
|
||||
group: script
|
||||
outputs:
|
||||
map_val:
|
||||
description: null
|
||||
value:
|
||||
get_input: map_val
|
||||
static_map_val:
|
||||
description: null
|
||||
value: static_value
|
77
translator/tests/data/test_tosca_get_functions_semantic.yaml
Normal file
77
translator/tests/data/test_tosca_get_functions_semantic.yaml
Normal file
@ -0,0 +1,77 @@
|
||||
tosca_definitions_version: tosca_simple_yaml_1_0
|
||||
|
||||
description: TOSCA template to test get_* functions semantic
|
||||
|
||||
node_types:
|
||||
tosca.capabilities.MyFeature:
|
||||
derived_from: tosca.capabilities.Root
|
||||
properties:
|
||||
my_list:
|
||||
type: list
|
||||
my_map:
|
||||
type: map
|
||||
|
||||
tosca.nodes.WebApplication.MyApp:
|
||||
derived_from: tosca.nodes.WebApplication
|
||||
requirements:
|
||||
- myfeature:
|
||||
capability: tosca.capabilities.MyFeature
|
||||
node: tosca.nodes.MyDatabase
|
||||
relationship: tosca.relationships.ConnectsTo
|
||||
|
||||
tosca.nodes.MyDatabase:
|
||||
derived_from: tosca.nodes.Database
|
||||
capabilities:
|
||||
myfeature:
|
||||
type: tosca.capabilities.MyFeature
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
map_val:
|
||||
type: string
|
||||
|
||||
node_templates:
|
||||
server:
|
||||
type: tosca.nodes.Compute
|
||||
capabilities:
|
||||
host:
|
||||
properties:
|
||||
num_cpus: 1
|
||||
mem_size: 1 GB
|
||||
os:
|
||||
properties:
|
||||
type: Linux
|
||||
distribution: Ubuntu
|
||||
version: 12.04
|
||||
architecture: x86_64
|
||||
|
||||
mysql_database:
|
||||
type: tosca.nodes.MyDatabase
|
||||
requirements:
|
||||
- host: server
|
||||
capabilities:
|
||||
myfeature:
|
||||
properties:
|
||||
my_list: [list_val_0]
|
||||
my_map:
|
||||
test_key: {get_input: map_val}
|
||||
test_key_static: static_value
|
||||
|
||||
myapp:
|
||||
type: tosca.nodes.WebApplication.MyApp
|
||||
requirements:
|
||||
- myfeature: mysql_database
|
||||
- host: server
|
||||
interfaces:
|
||||
Standard:
|
||||
configure:
|
||||
implementation: myapp_configure.sh
|
||||
inputs:
|
||||
the_list_val: { get_property: [ SELF, myfeature, my_list, 0 ] }
|
||||
|
||||
outputs:
|
||||
map_val:
|
||||
value: { get_property: [ myapp, myfeature, my_map, test_key ] }
|
||||
|
||||
static_map_val:
|
||||
value: { get_property: [ myapp, myfeature, my_map, test_key_static ] }
|
@ -477,3 +477,9 @@ class ToscaHotTranslationTest(TestCase):
|
||||
hot_file = '../tests/data/hot_output/hot_interface_on_compute.yaml'
|
||||
params = {}
|
||||
self._test_successful_translation(tosca_file, hot_file, params)
|
||||
|
||||
def test_hot_get_functions_semantic(self):
|
||||
tosca_file = '../tests/data/test_tosca_get_functions_semantic.yaml'
|
||||
hot_file = '../tests/data/hot_output/hot_get_functions_semantic.yaml'
|
||||
params = {}
|
||||
self._test_successful_translation(tosca_file, hot_file, params)
|
||||
|
Loading…
Reference in New Issue
Block a user