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 os
|
||||||
import re
|
import re
|
||||||
import requests
|
import requests
|
||||||
|
import six
|
||||||
from six.moves.urllib.parse import urlparse
|
from six.moves.urllib.parse import urlparse
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
@ -262,12 +263,17 @@ class UrlUtils(object):
|
|||||||
|
|
||||||
def str_to_num(value):
|
def str_to_num(value):
|
||||||
"""Convert a string representation of a number into a numeric type."""
|
"""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
|
return value
|
||||||
try:
|
try:
|
||||||
return int(value)
|
return int(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return float(value)
|
try:
|
||||||
|
return float(value)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def check_for_env_variables():
|
def check_for_env_variables():
|
||||||
|
@ -104,7 +104,7 @@ class HotResource(object):
|
|||||||
# scenarios and cannot be fixed or hard coded here
|
# scenarios and cannot be fixed or hard coded here
|
||||||
operations_deploy_sequence = ['create', 'configure', 'start']
|
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 HotResource for each operation used for deployment:
|
||||||
# create, start, configure
|
# create, start, configure
|
||||||
@ -351,7 +351,7 @@ class HotResource(object):
|
|||||||
return tosca_props
|
return tosca_props
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_all_operations(node):
|
def get_all_operations(node):
|
||||||
operations = {}
|
operations = {}
|
||||||
for operation in node.interfaces:
|
for operation in node.interfaces:
|
||||||
operations[operation.name] = operation
|
operations[operation.name] = operation
|
||||||
|
@ -67,5 +67,5 @@ class ToscaBlockStorage(HotResource):
|
|||||||
# attribute for the matching resource. Unless there is additional
|
# attribute for the matching resource. Unless there is additional
|
||||||
# runtime support, this should be a one to one mapping.
|
# runtime support, this should be a one to one mapping.
|
||||||
if attribute == 'volume_id':
|
if attribute == 'volume_id':
|
||||||
attr['get_resource'] = args[0]
|
attr['get_resource'] = self.name
|
||||||
return attr
|
return attr
|
||||||
|
@ -25,6 +25,7 @@ from toscaparser.utils.gettextutils import _
|
|||||||
from translator.common.exception import ToscaClassAttributeError
|
from translator.common.exception import ToscaClassAttributeError
|
||||||
from translator.common.exception import ToscaClassImportError
|
from translator.common.exception import ToscaClassImportError
|
||||||
from translator.common.exception import ToscaModImportError
|
from translator.common.exception import ToscaModImportError
|
||||||
|
from translator.common import utils
|
||||||
from translator.conf.config import ConfigProvider as translatorConfig
|
from translator.conf.config import ConfigProvider as translatorConfig
|
||||||
from translator.hot.syntax.hot_resource import HotResource
|
from translator.hot.syntax.hot_resource import HotResource
|
||||||
from translator.hot.tosca.tosca_block_storage_attachment import (
|
from translator.hot.tosca.tosca_block_storage_attachment import (
|
||||||
@ -312,7 +313,7 @@ class TranslateNodeTemplates(object):
|
|||||||
inputs = resource.properties.get('input_values')
|
inputs = resource.properties.get('input_values')
|
||||||
if inputs:
|
if inputs:
|
||||||
for name, value in six.iteritems(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
|
# remove resources without type defined
|
||||||
# for example a SoftwareComponent without interfaces
|
# for example a SoftwareComponent without interfaces
|
||||||
@ -327,49 +328,118 @@ class TranslateNodeTemplates(object):
|
|||||||
|
|
||||||
return self.hot_resources
|
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
|
get_property_args = None
|
||||||
if isinstance(input_value, GetProperty):
|
if isinstance(param_value, GetProperty):
|
||||||
get_property_args = input_value.args
|
get_property_args = param_value.args
|
||||||
# to remove when the parser is fixed to return GetProperty
|
# to remove when the parser is fixed to return GetProperty
|
||||||
if isinstance(input_value, dict) and 'get_property' in input_value:
|
if isinstance(param_value, dict) and 'get_property' in param_value:
|
||||||
get_property_args = input_value['get_property']
|
get_property_args = param_value['get_property']
|
||||||
if get_property_args is not None:
|
if get_property_args is not None:
|
||||||
hot_target = self._find_hot_resource_for_tosca(
|
tosca_target, prop_name, prop_arg = \
|
||||||
get_property_args[0], resource)
|
self.decipher_get_operation(get_property_args,
|
||||||
if hot_target:
|
tosca_template)
|
||||||
props = hot_target.get_tosca_props()
|
if tosca_target:
|
||||||
prop_name = get_property_args[1]
|
prop_value = tosca_target.get_property_value(prop_name)
|
||||||
if prop_name in props:
|
if prop_value:
|
||||||
return props[prop_name]
|
prop_value = self.translate_param_value(
|
||||||
elif isinstance(input_value, GetAttribute):
|
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
|
# for the attribute
|
||||||
# get the proper target type to perform the translation
|
# get the proper target type to perform the translation
|
||||||
args = input_value.result().args
|
tosca_target, attr_name, attr_arg = \
|
||||||
hot_target = self._find_hot_resource_for_tosca(args[0], resource)
|
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)
|
if tosca_target:
|
||||||
# most of artifacts logic should move to the parser
|
artifacts = self.get_all_artifacts(tosca_target)
|
||||||
elif isinstance(input_value, dict) and 'get_artifact' in input_value:
|
if artifact_name in artifacts:
|
||||||
get_artifact_args = input_value['get_artifact']
|
artifact = artifacts[artifact_name]
|
||||||
|
if artifact.get('type', None) == 'tosca.artifacts.File':
|
||||||
hot_target = self._find_hot_resource_for_tosca(
|
return {'get_file': artifact.get('file')}
|
||||||
get_artifact_args[0], resource)
|
get_input_args = None
|
||||||
artifacts = TranslateNodeTemplates.get_all_artifacts(
|
if isinstance(param_value, GetInput):
|
||||||
hot_target.nodetemplate)
|
get_input_args = param_value.args
|
||||||
|
if isinstance(param_value, dict) and 'get_input' in param_value:
|
||||||
if get_artifact_args[1] in artifacts:
|
get_input_args = param_value['get_input']
|
||||||
artifact = artifacts[get_artifact_args[1]]
|
if get_input_args is not None:
|
||||||
if artifact.get('type', None) == 'tosca.artifacts.File':
|
if isinstance(get_input_args, list) \
|
||||||
return {'get_file': artifact.get('file')}
|
and len(get_input_args) == 1:
|
||||||
elif isinstance(input_value, GetInput):
|
return {'get_param': self.translate_param_value(
|
||||||
if isinstance(input_value.args, list) \
|
get_input_args[0], resource)}
|
||||||
and len(input_value.args) == 1:
|
|
||||||
return {'get_param': input_value.args[0]}
|
|
||||||
else:
|
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
|
@staticmethod
|
||||||
def get_all_artifacts(nodetemplate):
|
def get_all_artifacts(nodetemplate):
|
||||||
@ -438,23 +508,29 @@ class TranslateNodeTemplates(object):
|
|||||||
if resource.name == name:
|
if resource.name == name:
|
||||||
return resource
|
return resource
|
||||||
|
|
||||||
def _find_tosca_node(self, tosca_name):
|
def _find_tosca_node(self, tosca_name, current_tosca_template=None):
|
||||||
for node in self.nodetemplates:
|
tosca_node = None
|
||||||
if node.name == tosca_name:
|
if tosca_name == 'SELF':
|
||||||
return node
|
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,
|
def _find_hot_resource_for_tosca(self, tosca_name,
|
||||||
current_hot_resource=None):
|
current_hot_resource=None):
|
||||||
if tosca_name == 'SELF':
|
current_tosca_resource = current_hot_resource.nodetemplate \
|
||||||
return current_hot_resource
|
if current_hot_resource else None
|
||||||
if tosca_name == 'HOST' and current_hot_resource is not None:
|
tosca_node = self._find_tosca_node(tosca_name, current_tosca_resource)
|
||||||
for req in current_hot_resource.nodetemplate.requirements:
|
if tosca_node:
|
||||||
if 'host' in req:
|
return self.hot_lookup[tosca_node]
|
||||||
return self._find_hot_resource_for_tosca(req['host'])
|
|
||||||
|
|
||||||
for node in self.nodetemplates:
|
|
||||||
if node.name == tosca_name:
|
|
||||||
return self.hot_lookup[node]
|
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -33,16 +33,7 @@ class TranslateOutputs(object):
|
|||||||
def _translate_outputs(self):
|
def _translate_outputs(self):
|
||||||
hot_outputs = []
|
hot_outputs = []
|
||||||
for output in self.outputs:
|
for output in self.outputs:
|
||||||
if output.value.name == 'get_attribute':
|
hot_value = self.nodes.translate_param_value(output.value, None)
|
||||||
get_parameters = output.value.args
|
hot_outputs.append(HotOutput(output.name, hot_value,
|
||||||
hot_target = self.nodes.find_hot_resource(get_parameters[0])
|
output.description))
|
||||||
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))
|
|
||||||
return hot_outputs
|
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'
|
hot_file = '../tests/data/hot_output/hot_interface_on_compute.yaml'
|
||||||
params = {}
|
params = {}
|
||||||
self._test_successful_translation(tosca_file, hot_file, 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