summaryrefslogtreecommitdiffstats
path: root/tosca2heat/heat-translator/translator/hot
diff options
context:
space:
mode:
authorshangxdy <shang.xiaodong@zte.com.cn>2016-07-08 15:15:00 +0800
committershangxdy <shang.xiaodong@zte.com.cn>2016-07-10 00:38:59 +0800
commit0997552722dc4845a854e0e6f8d7f18058e26380 (patch)
treeb90d1e808bb326612211ba56b3b941516493398d /tosca2heat/heat-translator/translator/hot
parent7fe3011a67a239f7dc04153c54eaff78ef967eaf (diff)
Synchronise the openstack bugs
When run unittests through tox, some test cases are always error, the errors are already done in openstack community, so it's necessary to synchronise the fixes. Change-Id: Ib29078e6cc138a474e89c6a2cc90ad7a1db1bb46 JIRA: PARSER-63 Signed-off-by: shangxdy <shang.xiaodong@zte.com.cn>
Diffstat (limited to 'tosca2heat/heat-translator/translator/hot')
-rw-r--r--tosca2heat/heat-translator/translator/hot/syntax/hot_output.py7
-rw-r--r--tosca2heat/heat-translator/translator/hot/syntax/hot_resource.py61
-rw-r--r--tosca2heat/heat-translator/translator/hot/syntax/hot_template.py1
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_compute.py60
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage.py2
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage_attachment.py7
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py79
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_antilocate.py1
-rw-r--r--tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_colocate.py1
-rw-r--r--tosca2heat/heat-translator/translator/hot/translate_node_templates.py287
-rw-r--r--tosca2heat/heat-translator/translator/hot/translate_outputs.py14
11 files changed, 400 insertions, 120 deletions
diff --git a/tosca2heat/heat-translator/translator/hot/syntax/hot_output.py b/tosca2heat/heat-translator/translator/hot/syntax/hot_output.py
index ad77fb3..a41208a 100644
--- a/tosca2heat/heat-translator/translator/hot/syntax/hot_output.py
+++ b/tosca2heat/heat-translator/translator/hot/syntax/hot_output.py
@@ -21,5 +21,8 @@ class HotOutput(object):
self.description = description
def get_dict_output(self):
- return {self.name: {'value': self.value,
- 'description': self.description}}
+ if self.description:
+ return {self.name: {'value': self.value,
+ 'description': self.description}}
+ else:
+ return {self.name: {'value': self.value}}
diff --git a/tosca2heat/heat-translator/translator/hot/syntax/hot_resource.py b/tosca2heat/heat-translator/translator/hot/syntax/hot_resource.py
index bbbeb46..261d8ee 100644
--- a/tosca2heat/heat-translator/translator/hot/syntax/hot_resource.py
+++ b/tosca2heat/heat-translator/translator/hot/syntax/hot_resource.py
@@ -1,4 +1,3 @@
-#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@@ -13,6 +12,7 @@
from collections import OrderedDict
import logging
+import os
import six
from toscaparser.elements.interfaces import InterfacesDef
@@ -44,7 +44,26 @@ class HotResource(object):
self.properties = properties or {}
# special case for HOT softwareconfig
if type == 'OS::Heat::SoftwareConfig':
- self.properties['group'] = 'script'
+ config = self.properties.get('config')
+ if config:
+ implementation_artifact = config.get('get_file')
+ if implementation_artifact:
+ filename, file_extension = os.path.splitext(
+ implementation_artifact)
+ file_extension = file_extension.lower()
+ # artifact_types should be read to find the exact script
+ # type, unfortunately artifact_types doesn't seem to be
+ # supported by the parser
+ if file_extension == '.ansible' \
+ or file_extension == '.yaml' \
+ or file_extension == '.yml':
+ self.properties['group'] = 'ansible'
+ if file_extension == '.pp':
+ self.properties['group'] = 'puppet'
+
+ if self.properties.get('group') is None:
+ self.properties['group'] = 'script'
+
self.metadata = metadata
# The difference between depends_on and depends_on_nodes is
@@ -84,7 +103,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
@@ -121,14 +140,22 @@ class HotResource(object):
# hosting_server is None if requirements is None
hosting_on_server = (hosting_server.name if
hosting_server else None)
- if operation.name == reserve_current:
+ base_type = HotResource.get_base_type(
+ self.nodetemplate.type_definition).type
+ # handle interfaces directly defined on a compute
+ if hosting_on_server is None \
+ and base_type == 'tosca.nodes.Compute':
+ hosting_on_server = self.name
+
+ if operation.name == reserve_current and \
+ base_type != 'tosca.nodes.Compute':
deploy_resource = self
self.name = deploy_name
self.type = 'OS::Heat::SoftwareDeployment'
self.properties = {'config': {'get_resource': config_name},
'server': {'get_resource':
hosting_on_server}}
- deploy_lookup[operation.name] = self
+ deploy_lookup[operation] = self
else:
sd_config = {'config': {'get_resource': config_name},
'server': {'get_resource':
@@ -139,7 +166,7 @@ class HotResource(object):
'OS::Heat::SoftwareDeployment',
sd_config)
hot_resources.append(deploy_resource)
- deploy_lookup[operation.name] = deploy_resource
+ deploy_lookup[operation] = deploy_resource
lifecycle_inputs = self._get_lifecycle_inputs(operation)
if lifecycle_inputs:
deploy_resource.properties['input_values'] = \
@@ -149,24 +176,34 @@ class HotResource(object):
# in operations_deploy_sequence
# TODO(anyone): find some better way to encode this implicit sequence
group = {}
+ op_index_max = -1
for op, hot in deploy_lookup.items():
# position to determine potential preceding nodes
- op_index = operations_deploy_sequence.index(op)
- for preceding_op in \
+ op_index = operations_deploy_sequence.index(op.name)
+ if op_index > op_index_max:
+ op_index_max = op_index
+ for preceding_op_name in \
reversed(operations_deploy_sequence[:op_index]):
- preceding_hot = deploy_lookup.get(preceding_op)
+ preceding_hot = deploy_lookup.get(
+ operations.get(preceding_op_name))
if preceding_hot:
hot.depends_on.append(preceding_hot)
hot.depends_on_nodes.append(preceding_hot)
group[preceding_hot] = hot
break
+ if op_index_max >= 0:
+ last_deploy = deploy_lookup.get(operations.get(
+ operations_deploy_sequence[op_index_max]))
+ else:
+ last_deploy = None
+
# save this dependency chain in the set of HOT resources
self.group_dependencies.update(group)
for hot in hot_resources:
hot.group_dependencies.update(group)
- return hot_resources
+ return hot_resources, deploy_lookup, last_deploy
def handle_connectsto(self, tosca_source, tosca_target, hot_source,
hot_target, config_location, operation):
@@ -313,14 +350,14 @@ 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
node_type = node.type_definition
if isinstance(node_type, str) or \
- node_type.type == "tosca.policies.Placement"or \
+ node_type.type == "tosca.policies.Placement" or \
node_type.type == "tosca.policies.Colocate" or \
node_type.type == "tosca.policies.Antilocate":
return operations
diff --git a/tosca2heat/heat-translator/translator/hot/syntax/hot_template.py b/tosca2heat/heat-translator/translator/hot/syntax/hot_template.py
index 4263c4d..0403562 100644
--- a/tosca2heat/heat-translator/translator/hot/syntax/hot_template.py
+++ b/tosca2heat/heat-translator/translator/hot/syntax/hot_template.py
@@ -77,6 +77,7 @@ class HotTemplate(object):
dict_output.update({self.OUTPUTS: all_outputs})
yaml.add_representer(OrderedDict, self.represent_ordereddict)
+ yaml.add_representer(dict, self.represent_ordereddict)
yaml_string = yaml.dump(dict_output, default_flow_style=False)
# get rid of the '' from yaml.dump around numbers
yaml_string = yaml_string.replace('\'', '')
diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_compute.py b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_compute.py
index e0cdbb6..d42cdc8 100644
--- a/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_compute.py
+++ b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_compute.py
@@ -16,7 +16,6 @@ from mock import patch
from toscaparser.nodetemplate import NodeTemplate
from toscaparser.tests.base import TestCase
-from toscaparser.utils.gettextutils import _
import toscaparser.utils.yamlparser
from translator.hot.tosca.tosca_compute import ToscaCompute
@@ -27,22 +26,12 @@ class ToscaComputeTest(TestCase):
nodetemplates = (toscaparser.utils.yamlparser.
simple_parse(tpl_snippet)['node_templates'])
name = list(nodetemplates.keys())[0]
- try:
- nodetemplate = NodeTemplate(name, nodetemplates)
- nodetemplate.validate()
- toscacompute = ToscaCompute(nodetemplate)
- toscacompute.handle_properties()
- if not self._compare_properties(toscacompute.properties,
- expectedprops):
- raise Exception(_("Hot Properties are not"
- " same as expected properties"))
- except Exception:
- # for time being rethrowing. Will be handled future based
- # on new development in Glance and Graffiti
- raise
+ nodetemplate = NodeTemplate(name, nodetemplates)
+ nodetemplate.validate()
+ toscacompute = ToscaCompute(nodetemplate)
+ toscacompute.handle_properties()
- def _compare_properties(self, hotprops, expectedprops):
- return all(item in hotprops.items() for item in expectedprops.items())
+ self.assertDictEqual(expectedprops, toscacompute.properties)
def test_node_compute_with_host_and_os_capabilities(self):
tpl_snippet = '''
@@ -63,7 +52,8 @@ class ToscaComputeTest(TestCase):
version: 18.0
'''
expectedprops = {'flavor': 'm1.large',
- 'image': 'fedora-amd64-heat-config'}
+ 'image': 'fedora-amd64-heat-config',
+ 'user_data_format': 'SOFTWARE_CONFIG'}
self._tosca_compute_test(
tpl_snippet,
expectedprops)
@@ -82,7 +72,8 @@ class ToscaComputeTest(TestCase):
#left intentionally
'''
expectedprops = {'flavor': 'm1.large',
- 'image': None}
+ 'image': None,
+ 'user_data_format': 'SOFTWARE_CONFIG'}
self._tosca_compute_test(
tpl_snippet,
expectedprops)
@@ -101,7 +92,8 @@ class ToscaComputeTest(TestCase):
version: 18.0
'''
expectedprops = {'flavor': None,
- 'image': 'fedora-amd64-heat-config'}
+ 'image': 'fedora-amd64-heat-config',
+ 'user_data_format': 'SOFTWARE_CONFIG'}
self._tosca_compute_test(
tpl_snippet,
expectedprops)
@@ -117,7 +109,8 @@ class ToscaComputeTest(TestCase):
#left intentionally
'''
expectedprops = {'flavor': None,
- 'image': None}
+ 'image': None,
+ 'user_data_format': 'SOFTWARE_CONFIG'}
self._tosca_compute_test(
tpl_snippet,
expectedprops)
@@ -129,7 +122,8 @@ class ToscaComputeTest(TestCase):
type: tosca.nodes.Compute
'''
expectedprops = {'flavor': None,
- 'image': None}
+ 'image': None,
+ 'user_data_format': 'SOFTWARE_CONFIG'}
self._tosca_compute_test(
tpl_snippet,
expectedprops)
@@ -144,7 +138,9 @@ class ToscaComputeTest(TestCase):
properties:
#left intentionally
'''
- expectedprops = {'flavor': 'm1.nano'}
+ expectedprops = {'flavor': None,
+ 'image': None,
+ 'user_data_format': 'SOFTWARE_CONFIG'}
self._tosca_compute_test(
tpl_snippet,
expectedprops)
@@ -160,7 +156,9 @@ class ToscaComputeTest(TestCase):
num_cpus: 4
mem_size: 4 GB
'''
- expectedprops = {'flavor': 'm1.large'}
+ expectedprops = {'flavor': 'm1.large',
+ 'image': None,
+ 'user_data_format': 'SOFTWARE_CONFIG'}
self._tosca_compute_test(
tpl_snippet,
expectedprops)
@@ -176,7 +174,9 @@ class ToscaComputeTest(TestCase):
num_cpus: 4
disk_size: 10 GB
'''
- expectedprops = {'flavor': 'm1.large'}
+ expectedprops = {'flavor': 'm1.large',
+ 'image': None,
+ 'user_data_format': 'SOFTWARE_CONFIG'}
self._tosca_compute_test(
tpl_snippet,
expectedprops)
@@ -191,7 +191,9 @@ class ToscaComputeTest(TestCase):
properties:
num_cpus: 4
'''
- expectedprops = {'flavor': 'm1.large'}
+ expectedprops = {'flavor': 'm1.large',
+ 'image': None,
+ 'user_data_format': 'SOFTWARE_CONFIG'}
self._tosca_compute_test(
tpl_snippet,
expectedprops)
@@ -249,7 +251,9 @@ class ToscaComputeTest(TestCase):
json.dumps(mock_flavor_content)
mock_post.return_value = mock_ks_response
mock_get.return_value = mock_nova_response
- expectedprops = {'flavor': 'm1.mock_flavor'}
+ expectedprops = {'flavor': 'm1.mock_flavor',
+ 'image': None,
+ 'user_data_format': 'SOFTWARE_CONFIG'}
self._tosca_compute_test(
tpl_snippet,
expectedprops)
@@ -279,8 +283,8 @@ class ToscaComputeTest(TestCase):
mock_ks_content = {}
mock_ks_response.content = json.dumps(mock_ks_content)
expectedprops = {'flavor': 'm1.small',
- 'user_data_format': 'SOFTWARE_CONFIG',
- 'image': None}
+ 'image': None,
+ 'user_data_format': 'SOFTWARE_CONFIG'}
self._tosca_compute_test(
tpl_snippet,
expectedprops)
diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage.py
index d4b2f44..924ff9d 100644
--- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage.py
+++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage.py
@@ -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
diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage_attachment.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage_attachment.py
index 715d5b3..2242c2d 100644
--- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage_attachment.py
+++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage_attachment.py
@@ -44,5 +44,10 @@ class ToscaBlockStorageAttachment(HotResource):
if 'location' in self.properties:
self.properties['mountpoint'] = self.properties.pop('location')
+ # TOSCA type can have a device name specified,
+ # this is unsupported by Heat
+ if 'device' in self.properties:
+ self.properties.pop('device')
+
def handle_life_cycle(self):
- pass
+ return None, None, None
diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py
index e2ac130..b8ad83c 100644
--- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py
+++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py
@@ -84,6 +84,14 @@ class ToscaCompute(HotResource):
('architecture', 'distribution', 'type', 'version')
toscatype = 'tosca.nodes.Compute'
+ ALLOWED_NOVA_SERVER_PROPS = \
+ ('admin_pass', 'availability_zone', 'block_device_mapping',
+ 'block_device_mapping_v2', 'config_drive', 'diskConfig', 'flavor',
+ 'flavor_update_policy', 'image', 'image_update_policy', 'key_name',
+ 'metadata', 'name', 'networks', 'personality', 'reservation_id',
+ 'scheduler_hints', 'security_groups', 'software_config_transport',
+ 'user_data', 'user_data_format', 'user_data_update_policy')
+
def __init__(self, nodetemplate):
super(ToscaCompute, self).__init__(nodetemplate,
type='OS::Nova::Server')
@@ -98,7 +106,8 @@ class ToscaCompute(HotResource):
self.properties['user_data_format'] = 'SOFTWARE_CONFIG'
tosca_props = self.get_tosca_props()
for key, value in tosca_props.items():
- self.properties[key] = value
+ if key in self.ALLOWED_NOVA_SERVER_PROPS:
+ self.properties[key] = value
# To be reorganized later based on new development in Glance and Graffiti
def translate_compute_flavor_and_image(self,
@@ -112,11 +121,17 @@ class ToscaCompute(HotResource):
if host_capability:
for prop in host_capability.get_properties_objects():
host_cap_props[prop.name] = prop.value
- flavor = self._best_flavor(host_cap_props)
+ # if HOST properties are not specified, we should not attempt to
+ # find best match of flavor
+ if host_cap_props:
+ flavor = self._best_flavor(host_cap_props)
if os_capability:
for prop in os_capability.get_properties_objects():
os_cap_props[prop.name] = prop.value
- image = self._best_image(os_cap_props)
+ # if OS properties are not specified, we should not attempt to
+ # find best match of image
+ if os_cap_props:
+ image = self._best_image(os_cap_props)
hot_properties['flavor'] = flavor
hot_properties['image'] = image
return hot_properties
@@ -153,6 +168,48 @@ class ToscaCompute(HotResource):
return None
return flavor_dict
+ def _populate_image_dict(self):
+ '''Populates and returns the images dict using Glance ReST API'''
+ images_dict = {}
+ try:
+ access_dict = translator.common.utils.get_ks_access_dict()
+ access_token = translator.common.utils.get_token_id(access_dict)
+ if access_token is None:
+ return None
+ glance_url = translator.common.utils.get_url_for(access_dict,
+ 'image')
+ if not glance_url:
+ return None
+ glance_response = requests.get(glance_url + '/v2/images',
+ headers={'X-Auth-Token':
+ access_token})
+ if glance_response.status_code != 200:
+ return None
+ images = json.loads(glance_response.content)["images"]
+ for image in images:
+ image_resp = requests.get(glance_url + '/v2/images/' +
+ image["id"],
+ headers={'X-Auth-Token':
+ access_token})
+ if image_resp.status_code != 200:
+ continue
+ metadata = ["architecture", "type", "distribution", "version"]
+ image_data = json.loads(image_resp.content)
+ if any(key in image_data.keys() for key in metadata):
+ images_dict[image_data["name"]] = dict()
+ for key in metadata:
+ if key in image_data.keys():
+ images_dict[image_data["name"]][key] = \
+ image_data[key]
+ else:
+ continue
+
+ except Exception as e:
+ # Handles any exception coming from openstack
+ log.warn(_('Choosing predefined flavors since received '
+ 'Openstack Exception: %s') % str(e))
+ return images_dict
+
def _best_flavor(self, properties):
log.info(_('Choosing the best flavor for given attributes.'))
# Check whether user exported all required environment variables.
@@ -202,26 +259,32 @@ class ToscaCompute(HotResource):
return None
def _best_image(self, properties):
- match_all = IMAGES.keys()
+ # Check whether user exported all required environment variables.
+ images = IMAGES
+ if translator.common.utils.check_for_env_variables():
+ resp = self._populate_image_dict()
+ if len(resp.keys()) > 0:
+ images = resp
+ match_all = images.keys()
architecture = properties.get(self.ARCHITECTURE)
if architecture is None:
self._log_compute_msg(self.ARCHITECTURE, 'image')
- match_arch = self._match_images(match_all, IMAGES,
+ match_arch = self._match_images(match_all, images,
self.ARCHITECTURE, architecture)
type = properties.get(self.TYPE)
if type is None:
self._log_compute_msg(self.TYPE, 'image')
- match_type = self._match_images(match_arch, IMAGES, self.TYPE, type)
+ match_type = self._match_images(match_arch, images, self.TYPE, type)
distribution = properties.get(self.DISTRIBUTION)
if distribution is None:
self._log_compute_msg(self.DISTRIBUTION, 'image')
- match_distribution = self._match_images(match_type, IMAGES,
+ match_distribution = self._match_images(match_type, images,
self.DISTRIBUTION,
distribution)
version = properties.get(self.VERSION)
if version is None:
self._log_compute_msg(self.VERSION, 'image')
- match_version = self._match_images(match_distribution, IMAGES,
+ match_version = self._match_images(match_distribution, images,
self.VERSION, version)
if len(match_version):
diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_antilocate.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_antilocate.py
index b7aca4a..37bfed6 100644
--- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_antilocate.py
+++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_antilocate.py
@@ -1,4 +1,3 @@
-#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_colocate.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_colocate.py
index 1a108bf..778f97e 100644
--- a/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_colocate.py
+++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies_colocate.py
@@ -1,4 +1,3 @@
-#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
diff --git a/tosca2heat/heat-translator/translator/hot/translate_node_templates.py b/tosca2heat/heat-translator/translator/hot/translate_node_templates.py
index 46cdd71..d8e7e48 100644
--- a/tosca2heat/heat-translator/translator/hot/translate_node_templates.py
+++ b/tosca2heat/heat-translator/translator/hot/translate_node_templates.py
@@ -16,6 +16,8 @@ import logging
import os
import six
+from collections import OrderedDict
+from toscaparser.functions import Concat
from toscaparser.functions import GetAttribute
from toscaparser.functions import GetInput
from toscaparser.functions import GetProperty
@@ -25,6 +27,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 (
@@ -132,6 +135,8 @@ log = logging.getLogger('heat-translator')
TOSCA_TO_HOT_TYPE = _generate_type_map()
+BASE_TYPES = six.string_types + six.integer_types + (dict, OrderedDict)
+
class TranslateNodeTemplates(object):
'''Translate TOSCA NodeTemplates to Heat Resources.'''
@@ -146,6 +151,9 @@ class TranslateNodeTemplates(object):
log.debug(_('Mapping between TOSCA nodetemplate and HOT resource.'))
self.hot_lookup = {}
self.policies = self.tosca.topology_template.policies
+ # stores the last deploy of generated behavior for a resource
+ # useful to satisfy underlying dependencies between interfaces
+ self.last_deploy_map = {}
def translate(self):
return self._translate_nodetemplates()
@@ -219,9 +227,14 @@ class TranslateNodeTemplates(object):
# into multiple HOT resources and may change their name
lifecycle_resources = []
for resource in self.hot_resources:
- expanded = resource.handle_life_cycle()
- if expanded:
- lifecycle_resources += expanded
+ expanded_resources, deploy_lookup, last_deploy = resource.\
+ handle_life_cycle()
+ if expanded_resources:
+ lifecycle_resources += expanded_resources
+ if deploy_lookup:
+ self.hot_lookup.update(deploy_lookup)
+ if last_deploy:
+ self.last_deploy_map[resource] = last_deploy
self.hot_resources += lifecycle_resources
# Handle configuration from ConnectsTo relationship in the TOSCA node:
@@ -253,7 +266,9 @@ class TranslateNodeTemplates(object):
# if the source of dependency is a server and the
# relationship type is 'tosca.relationships.HostedOn',
# add dependency as properties.server
- if node_depend.type == 'tosca.nodes.Compute' and \
+ base_type = HotResource.get_base_type(
+ node_depend.type_definition)
+ if base_type.type == 'tosca.nodes.Compute' and \
node.related[node_depend].type == \
node.type_definition.HOSTEDON:
self.hot_lookup[node].properties['server'] = \
@@ -266,6 +281,13 @@ class TranslateNodeTemplates(object):
self.hot_lookup[node].depends_on_nodes.append(
self.hot_lookup[node_depend].top_of_chain())
+ last_deploy = self.last_deploy_map.get(
+ self.hot_lookup[node_depend])
+ if last_deploy and \
+ last_deploy not in self.hot_lookup[node].depends_on:
+ self.hot_lookup[node].depends_on.append(last_deploy)
+ self.hot_lookup[node].depends_on_nodes.append(last_deploy)
+
# handle hosting relationship
for resource in self.hot_resources:
resource.handle_hosting()
@@ -295,53 +317,202 @@ 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
+ # would fall in this case
+ to_remove = []
+ for resource in self.hot_resources:
+ if resource.type is None:
+ to_remove.append(resource)
+
+ for resource in to_remove:
+ self.hot_resources.remove(resource)
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']
+ elif 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
+ elif 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()
- hot_target = self._find_hot_resource_for_tosca(args[0], resource)
-
- 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]}
+ 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)
+
+ 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
+ elif 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': self.translate_param_value(
+ get_input_args, resource)}
+ elif isinstance(param_value, dict) \
+ and 'get_operation_output' in param_value:
+ res = self._translate_get_operation_output_function(
+ param_value['get_operation_output'], tosca_template)
+ if res:
+ return res
+ concat_list = None
+ if isinstance(param_value, Concat):
+ concat_list = param_value.args
+ elif isinstance(param_value, dict) and 'concat' in param_value:
+ concat_list = param_value['concat']
+ if concat_list is not None:
+ res = self._translate_concat_function(concat_list, resource)
+ if res:
+ return res
+
+ if isinstance(param_value, list):
+ translated_list = []
+ for elem in param_value:
+ translated_elem = self.translate_param_value(elem, resource)
+ if translated_elem:
+ translated_list.append(translated_elem)
+ return translated_list
+
+ if isinstance(param_value, BASE_TYPES):
+ return param_value
+
+ return None
+
+ def _translate_concat_function(self, concat_list, resource):
+ str_replace_template = ''
+ str_replace_params = {}
+ index = 0
+ for elem in concat_list:
+ str_replace_template += '$s' + str(index)
+ str_replace_params['$s' + str(index)] = \
+ self.translate_param_value(elem, resource)
+ index += 1
+
+ return {'str_replace': {
+ 'template': str_replace_template,
+ 'params': str_replace_params
+ }}
+
+ def _translate_get_operation_output_function(self, args, tosca_template):
+ tosca_target = self._find_tosca_node(args[0],
+ tosca_template)
+ if tosca_target and len(args) >= 4:
+ operations = HotResource.get_all_operations(tosca_target)
+ # ignore Standard interface name,
+ # it is the only one supported in the translator anyway
+ op_name = args[2]
+ output_name = args[3]
+ if op_name in operations:
+ operation = operations[op_name]
+ if operation in self.hot_lookup:
+ matching_deploy = self.hot_lookup[operation]
+ matching_config_name = matching_deploy.\
+ properties['config']['get_resource']
+ matching_config = self.find_hot_resource(
+ matching_config_name)
+ if matching_config:
+ outputs = matching_config.properties.get('outputs')
+ if outputs is None:
+ outputs = []
+ outputs.append({'name': output_name})
+ matching_config.properties['outputs'] = outputs
+ return {'get_attr': [
+ matching_deploy.name,
+ output_name
+ ]}
+
+ @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:
- return {'get_param': input_value.args}
+ 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 input_value
+ return tosca_target, prop_name, prop_arg
@staticmethod
def get_all_artifacts(nodetemplate):
@@ -410,23 +581,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_hot_resource_for_tosca(self, tosca_name,
- current_hot_resource=None):
+ def _find_tosca_node(self, tosca_name, current_tosca_template=None):
+ tosca_node = 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:
+ tosca_node = current_tosca_template
+ if tosca_name == 'HOST' and current_tosca_template:
+ for req in current_tosca_template.requirements:
if 'host' in req:
- return self._find_hot_resource_for_tosca(req['host'])
+ tosca_node = self._find_tosca_node(req['host'])
- for node in self.nodetemplates:
- if node.name == tosca_name:
- return self.hot_lookup[node]
+ 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):
+ 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
diff --git a/tosca2heat/heat-translator/translator/hot/translate_outputs.py b/tosca2heat/heat-translator/translator/hot/translate_outputs.py
index 4197cdd..87ec02a 100644
--- a/tosca2heat/heat-translator/translator/hot/translate_outputs.py
+++ b/tosca2heat/heat-translator/translator/hot/translate_outputs.py
@@ -33,16 +33,8 @@ 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,
+ hot_value = self.nodes.translate_param_value(output.value, None)
+ if hot_value is not None:
+ hot_outputs.append(HotOutput(output.name, hot_value,
output.description))
return hot_outputs