diff options
Diffstat (limited to 'tosca2heat/heat-translator/translator')
28 files changed, 1185 insertions, 510 deletions
diff --git a/tosca2heat/heat-translator/translator/common/utils.py b/tosca2heat/heat-translator/translator/common/utils.py index 459b5ee..8e4b690 100644 --- a/tosca2heat/heat-translator/translator/common/utils.py +++ b/tosca2heat/heat-translator/translator/common/utils.py @@ -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(): diff --git a/tosca2heat/heat-translator/translator/conf/config.py b/tosca2heat/heat-translator/translator/conf/config.py index 4e8fe87..52ac458 100644 --- a/tosca2heat/heat-translator/translator/conf/config.py +++ b/tosca2heat/heat-translator/translator/conf/config.py @@ -12,6 +12,7 @@ # under the License. ''' Provide a global configuration for the TOSCA translator''' +import os from six.moves import configparser @@ -65,3 +66,11 @@ class ConfigProvider(object): raise exception.ConfSectionNotDefined(section=section) return values + + @classmethod + def get_translator_logging_file(cls): + conf_file = '' + CONF_FILENAME = 'heat_translator_logging.conf' + conf_path = os.path.dirname(os.path.abspath(__file__)) + conf_file = os.path.join(conf_path, CONF_FILENAME) + return conf_file diff --git a/tosca2heat/heat-translator/translator/conf/heat_translator_logging.conf b/tosca2heat/heat-translator/translator/conf/heat_translator_logging.conf new file mode 100644 index 0000000..e01a889 --- /dev/null +++ b/tosca2heat/heat-translator/translator/conf/heat_translator_logging.conf @@ -0,0 +1,43 @@ + +[loggers] +keys=root,heat-translator + +[handlers] +keys=RotatingFileHandler,SysLogHandler,NullHandler + +[formatters] +keys=form01 + +[logger_root] +level=DEBUG +handlers=NullHandler + +[logger_heat-translator] +level=INFO +#one can be removed based on requirements +handlers=SysLogHandler, RotatingFileHandler +qualname=heat-translator +propagate=1 + +[handler_RotatingFileHandler] +class=handlers.RotatingFileHandler +level=INFO +formatter=form01 +#rotation happens after 100MB +args=('/tmp/heat-translator.log', 'a', 100000000, 5, 'utf8') + +[handler_SysLogHandler] +class=handlers.SysLogHandler +formatter=form01 +level=INFO +args=('/dev/log', handlers.SysLogHandler.LOG_SYSLOG) + +[handler_NullHandler] +class=NullHandler +formatter=form01 +level=DEBUG +args=() + +[formatter_form01] +format = %(asctime)s - %(name)s - %(levelname)s - %(filename)s : %(message)s +datefmt = 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 diff --git a/tosca2heat/heat-translator/translator/osc/v1/translate.py b/tosca2heat/heat-translator/translator/osc/v1/translate.py index eeaaa18..ef005e2 100644 --- a/tosca2heat/heat-translator/translator/osc/v1/translate.py +++ b/tosca2heat/heat-translator/translator/osc/v1/translate.py @@ -22,11 +22,12 @@ from cliff import command from toscaparser.tosca_template import ToscaTemplate from toscaparser.utils.gettextutils import _ from translator.common.utils import UrlUtils +from translator.conf.config import ConfigProvider from translator.hot.tosca_translator import TOSCATranslator from translator.osc import utils - -logging.config.fileConfig('heat_translator_logging.conf') +conf_file = ConfigProvider.get_translator_logging_file() +logging.config.fileConfig(conf_file) log = logging.getLogger('heat-translator') diff --git a/tosca2heat/heat-translator/translator/shell.py b/tosca2heat/heat-translator/translator/shell.py index 92d92d9..d5333bc 100644 --- a/tosca2heat/heat-translator/translator/shell.py +++ b/tosca2heat/heat-translator/translator/shell.py @@ -11,6 +11,7 @@ # under the License. +import argparse import ast import json import logging @@ -26,6 +27,7 @@ from toscaparser.tosca_template import ToscaTemplate from toscaparser.utils.gettextutils import _ from toscaparser.utils.urlutils import UrlUtils from translator.common import utils +from translator.conf.config import ConfigProvider from translator.hot.tosca_translator import TOSCATranslator """ @@ -44,8 +46,8 @@ without actual translation, pass --validate-only=true along with other required arguments. """ - -logging.config.fileConfig('heat_translator_logging.conf') +conf_file = ConfigProvider.get_translator_logging_file() +logging.config.fileConfig(conf_file) log = logging.getLogger("heat-translator") @@ -53,78 +55,72 @@ class TranslatorShell(object): SUPPORTED_TYPES = ['tosca'] - def _validate(self, args): - if len(args) < 2: - msg = _("The program requires minimum two arguments. " - "Please refer to the usage documentation.") - log.error(msg) - raise ValueError(msg) - if "--template-file=" not in args[0]: - msg = _("The program expects --template-file as first argument. " - "Please refer to the usage documentation.") - log.error(msg) - raise ValueError(msg) - if "--template-type=" not in args[1]: - msg = _("The program expects --template-type as second argument. " - "Please refer to the usage documentation.") - log.error(msg) - raise ValueError(msg) + def get_parser(self): + parser = argparse.ArgumentParser(prog="heat-translator") + + parser.add_argument('--template-file', + metavar='<filename>', + required=True, + help=_('Template file to load.')) + + parser.add_argument('--output-file', + metavar='<filename>', + help=_('Where to store the output file. If not ' + 'passed, it will be printed to stdin.')) + + parser.add_argument('--template-type', + metavar='<input-template-type>', + choices=self.SUPPORTED_TYPES, + default='tosca', + help=(_('Template type to parse. Choose between ' + '%s.') % self.SUPPORTED_TYPES)) + + parser.add_argument('--parameters', + metavar='<param1=val1;param2=val2;...>', + help=_('Optional input parameters.')) + + parser.add_argument('--validate-only', + action='store_true', + default=False, + help=_('Only validate input template, do not ' + 'perform translation.')) + + parser.add_argument('--deploy', + action='store_true', + default=False, + help=_('Whether to deploy the generated template ' + 'or not.')) + + return parser + + def main(self, argv): + + parser = self.get_parser() + (args, args_list) = parser.parse_known_args(argv) + + template_file = args.template_file + template_type = args.template_type + output_file = args.output_file + validate_only = args.validate_only + deploy = args.deploy - def main(self, args): - # TODO(spzala): set self.deploy based on passed args once support for - # --deploy argument is enabled. - self.deploy = False - self._validate(args) - path = args[0].split('--template-file=')[1] - # e.g. --template_file=translator/tests/data/tosca_helloworld.yaml - template_type = args[1].split('--template-type=')[1] - # e.g. --template_type=tosca - if not template_type: - msg = _("Template type is needed. For example, 'tosca'") - log.error(msg) - raise ValueError(msg) - elif template_type not in self.SUPPORTED_TYPES: - msg = _("%(value)s is not a valid template type.") % { - 'value': template_type} - log.error(msg) - raise ValueError(msg) parsed_params = {} - validate_only = None - output_file = None - if len(args) > 2: - parameters = None - for arg in args: - if "--validate-only=" in arg: - validate_only = arg - if "--parameters=" in arg: - parameters = arg - if "--output-file=" in arg: - output = arg - output_file = output.split('--output-file=')[1] - if "--deploy" in arg: - self.deploy = True - if parameters: - parsed_params = self._parse_parameters(parameters) - a_file = os.path.isfile(path) - a_url = UrlUtils.validate_url(path) if not a_file else False + if args.parameters: + parsed_params = self._parse_parameters(args.parameters) + + a_file = os.path.isfile(template_file) + a_url = UrlUtils.validate_url(template_file) if not a_file else False if a_file or a_url: - run_only_validation = False if validate_only: - value = validate_only.split('-validate-only=')[1].lower() - if template_type == 'tosca' and value == 'true': - run_only_validation = True - if run_only_validation: - ToscaTemplate(path, parsed_params, a_file) - msg = (_('The input "%(path)s" successfully passed ' - 'validation.') % {'path': path}) + ToscaTemplate(template_file, parsed_params, a_file) + msg = (_('The input "%(template_file)s" successfully passed ' + 'validation.') % {'template_file': template_file}) print(msg) else: - log.info( - _('Checked whether template path is a file or url path.')) - heat_tpl = self._translate(template_type, path, parsed_params, - a_file) + heat_tpl = self._translate(template_type, template_file, + parsed_params, a_file, deploy) if heat_tpl: - if utils.check_for_env_variables() and self.deploy: + if utils.check_for_env_variables() and deploy: try: heatclient(heat_tpl, parsed_params) except Exception: @@ -132,47 +128,42 @@ class TranslatorShell(object): self._write_output(heat_tpl, output_file) else: - msg = _("The path %(path)s is not a valid file or URL.") % { - 'path': path} + msg = (_('The path %(template_file)s is not a valid ' + 'file or URL.') % {'template_file': template_file}) + log.error(msg) raise ValueError(msg) def _parse_parameters(self, parameter_list): parsed_inputs = {} - if parameter_list.startswith('--parameters'): - # Parameters are semi-colon separated - inputs = parameter_list.split('--parameters=')[1].\ - replace('"', '').split(';') - # Each parameter should be an assignment - for param in inputs: - keyvalue = param.split('=') - # Validate the parameter has both a name and value - msg = _("'%(param)s' is not a well-formed parameter.") % { - 'param': param} - if keyvalue.__len__() is 2: - # Assure parameter name is not zero-length or whitespace - stripped_name = keyvalue[0].strip() - if not stripped_name: - log.error(msg) - raise ValueError(msg) - # Add the valid parameter to the dictionary - parsed_inputs[keyvalue[0]] = keyvalue[1] - else: + + # Parameters are semi-colon separated + inputs = parameter_list.replace('"', '').split(';') + # Each parameter should be an assignment + for param in inputs: + keyvalue = param.split('=') + # Validate the parameter has both a name and value + msg = _("'%(param)s' is not a well-formed parameter.") % { + 'param': param} + if keyvalue.__len__() is 2: + # Assure parameter name is not zero-length or whitespace + stripped_name = keyvalue[0].strip() + if not stripped_name: log.error(msg) raise ValueError(msg) - else: - msg = _("'%(list)s' is not a valid parameter list.") % { - 'list': parameter_list} - log.error(msg) - raise ValueError(msg) + # Add the valid parameter to the dictionary + parsed_inputs[keyvalue[0]] = keyvalue[1] + else: + log.error(msg) + raise ValueError(msg) return parsed_inputs - def _translate(self, sourcetype, path, parsed_params, a_file): + def _translate(self, sourcetype, path, parsed_params, a_file, deploy): output = None if sourcetype == "tosca": log.debug(_('Loading the tosca template.')) tosca = ToscaTemplate(path, parsed_params, a_file) - translator = TOSCATranslator(tosca, parsed_params, self.deploy) + translator = TOSCATranslator(tosca, parsed_params, deploy) log.debug(_('Translating the tosca template.')) output = translator.translate() return output diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/ssh/ssh_generate_keys.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/ssh/ssh_generate_keys.sh new file mode 100644 index 0000000..9dc8e16 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/ssh/ssh_generate_keys.sh @@ -0,0 +1,4 @@ +#!/bin/bash +test -d /root/.ssh || mkdir /root/.ssh +test -f /root/.ssh/id_rsa.pub || ssh-keygen -q -t rsa -N "" -f /root/.ssh/id_rsa +cat /root/.ssh/id_rsa.pub > ${heat_outputs_path}.public_key diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/ssh/ssh_import_public_key.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/ssh/ssh_import_public_key.sh new file mode 100644 index 0000000..1ef94e0 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/ssh/ssh_import_public_key.sh @@ -0,0 +1,3 @@ +#!/bin/bash +test -d /root/.ssh || mkdir /root/.ssh +echo "$public_key" >> /root/.ssh/authorized_keys diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_exchange_public_ssh_key.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_exchange_public_ssh_key.yaml new file mode 100644 index 0000000..1dfa125 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_exchange_public_ssh_key.yaml @@ -0,0 +1,55 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA template to test get_operation_output by exchanging ssh public key + +parameters: {} +resources: + generate_ssh_key_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: generate_ssh_key_create_config + server: + get_resource: server1 + import_public_key_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: import_public_key_create_config + input_values: + public_key: + get_attr: + - generate_ssh_key_create_deploy + - public_key + server: + get_resource: server2 + depends_on: + - generate_ssh_key_create_deploy + server1: + type: OS::Nova::Server + properties: + flavor: m1.small + image: ubuntu-12.04-software-config-os-init + user_data_format: SOFTWARE_CONFIG + server2: + type: OS::Nova::Server + properties: + flavor: m1.small + image: ubuntu-12.04-software-config-os-init + user_data_format: SOFTWARE_CONFIG + generate_ssh_key_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: artifacts/ssh/ssh_generate_keys.sh + group: script + outputs: + - name: public_key + import_public_key_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: artifacts/ssh/ssh_import_public_key.sh + group: script +outputs: {}
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_get_functions_semantic.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_get_functions_semantic.yaml new file mode 100644 index 0000000..318a739 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_get_functions_semantic.yaml @@ -0,0 +1,52 @@ +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: + input_values: + list_val: list_val_0 + config: + get_resource: myapp_configure_config + 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: + group: script + config: + get_file: myapp_configure.sh +outputs: + map_val: + description: map_val + value: + get_input: map_val + concat_map_val: + value: + str_replace: + params: + $s2: :8080 + $s0: http:// + $s1: + get_input: map_val + template: $s0$s1$s2 + static_map_val: + value: static_value + test_list_of_functions: + value: + - get_input: map_val + - static_value diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_interface_on_compute.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_interface_on_compute.yaml new file mode 100644 index 0000000..0399c06 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_interface_on_compute.yaml @@ -0,0 +1,44 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA template to test Compute node with interface + +parameters: {} +resources: + softwarecomponent_depending_on_customcompute_install_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: softwarecomponent_depending_on_customcompute_install_create_config + server: + get_resource: server + depends_on: + - server_create_deploy + server: + type: OS::Nova::Server + properties: + flavor: m1.small + image: ubuntu-12.04-software-config-os-init + user_data_format: SOFTWARE_CONFIG + softwarecomponent_depending_on_customcompute_install_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: post_install.sh + group: script + server_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: install.sh + group: script + server_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: server_create_config + input_values: + install_path: /opt + server: + get_resource: server +outputs: {}
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_script_types.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_script_types.yaml new file mode 100644 index 0000000..5f0585d --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_script_types.yaml @@ -0,0 +1,101 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA template to test usage of different script types like Ansible and Puppet + one. + +parameters: {} +resources: + customwebserver2_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: customwebserver2_create_config + server: + get_resource: server + customwebserver_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: customwebserver_create_config + server: + get_resource: server + server: + type: OS::Nova::Server + properties: + flavor: m1.small + image: ubuntu-12.04-software-config-os-init + user_data_format: SOFTWARE_CONFIG + customwebserver2_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: start.sh + group: script + customwebserver2_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: customwebserver2_start_config + server: + get_resource: server + depends_on: + - customwebserver2_configure_deploy + customwebserver2_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: install.sh + group: script + customwebserver2_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: configure.py + group: script + customwebserver2_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: customwebserver2_configure_config + server: + get_resource: server + depends_on: + - customwebserver2_create_deploy + customwebserver_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: start.pp + group: puppet + customwebserver_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: customwebserver_start_config + server: + get_resource: server + depends_on: + - customwebserver_configure_deploy + customwebserver_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: install.yaml + group: ansible + customwebserver_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: configure.yml + group: ansible + customwebserver_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: customwebserver_configure_config + server: + get_resource: server + depends_on: + - customwebserver_create_deploy +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/test_tosca_get_functions_semantic.yaml b/tosca2heat/heat-translator/translator/tests/data/test_tosca_get_functions_semantic.yaml new file mode 100644 index 0000000..2a76978 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/test_tosca_get_functions_semantic.yaml @@ -0,0 +1,84 @@ +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: + list_val: { get_property: [ SELF, myfeature, my_list, 0 ] } + + outputs: + map_val: + description: map_val + value: { get_property: [ myapp, myfeature, my_map, test_key ] } + + static_map_val: + value: { get_property: [ myapp, myfeature, my_map, test_key_static ] } + + concat_map_val: + value: { concat: [ 'http://', { get_property: [ myapp, myfeature, my_map, test_key ] }, ':8080' ] } + + test_list_of_functions: + value: [ { get_property: [ myapp, myfeature, my_map, test_key ] }, { get_property: [ myapp, myfeature, my_map, test_key_static ] } ] diff --git a/tosca2heat/heat-translator/translator/tests/data/test_tosca_interface_on_compute.yaml b/tosca2heat/heat-translator/translator/tests/data/test_tosca_interface_on_compute.yaml new file mode 100644 index 0000000..e033c3c --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/test_tosca_interface_on_compute.yaml @@ -0,0 +1,48 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: TOSCA template to test Compute node with interface + +node_types: + tosca.nodes.CustomCompute: + derived_from: tosca.nodes.Compute + properties: + install_path: + type: string + default: /opt + interfaces: + Standard: + create: + implementation: install.sh + inputs: + install_path: { get_property: [ SELF, install_path ] } + +topology_template: + node_templates: + + softwarecomponent_without_behavior: + type: tosca.nodes.SoftwareComponent + requirements: + - host: server + + softwarecomponent_depending_on_customcompute_install: + type: tosca.nodes.SoftwareComponent + interfaces: + Standard: + create: + implementation: post_install.sh + requirements: + - host: server + + server: + type: tosca.nodes.CustomCompute + capabilities: + host: + properties: + num_cpus: 1 + mem_size: 1 GB + os: + properties: + type: Linux + distribution: Ubuntu + version: 12.04 + architecture: x86_64 diff --git a/tosca2heat/heat-translator/translator/tests/data/test_tosca_script_types.yaml b/tosca2heat/heat-translator/translator/tests/data/test_tosca_script_types.yaml new file mode 100644 index 0000000..b54cbcb --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/test_tosca_script_types.yaml @@ -0,0 +1,48 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA template to test usage of different script types like + Ansible and Puppet one. + +topology_template: + + node_templates: + customwebserver: + type: tosca.nodes.WebServer + requirements: + - host: server + interfaces: + Standard: + create: + implementation: install.yaml + configure: + implementation: configure.yml + start: + implementation: start.pp + + customwebserver2: + type: tosca.nodes.WebServer + requirements: + - host: server + interfaces: + Standard: + create: + implementation: install.sh + configure: + implementation: configure.py + start: + implementation: start.sh + + 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 diff --git a/tosca2heat/heat-translator/translator/tests/data/tosca_exchange_public_ssh_key.yaml b/tosca2heat/heat-translator/translator/tests/data/tosca_exchange_public_ssh_key.yaml new file mode 100644 index 0000000..7decb33 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/tosca_exchange_public_ssh_key.yaml @@ -0,0 +1,54 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: TOSCA template to test get_operation_output by exchanging ssh public key + +topology_template: + + node_templates: + server1: + 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 + + server2: + 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 + + generate_ssh_key: + type: tosca.nodes.SoftwareComponent + interfaces: + Standard: + create: artifacts/ssh/ssh_generate_keys.sh + requirements: + - host: server1 + + import_public_key: + type: tosca.nodes.SoftwareComponent + interfaces: + Standard: + create: + implementation: artifacts/ssh/ssh_import_public_key.sh + inputs: + public_key: { get_operation_output: [generate_ssh_key, Standard, create, public_key] } + requirements: + - host: server2 + - dependency: generate_ssh_key diff --git a/tosca2heat/heat-translator/translator/tests/test_shell.py b/tosca2heat/heat-translator/translator/tests/test_shell.py index b001c1a..62f3510 100644 --- a/tosca2heat/heat-translator/translator/tests/test_shell.py +++ b/tosca2heat/heat-translator/translator/tests/test_shell.py @@ -29,28 +29,9 @@ class ShellTest(TestCase): "data/tosca_helloworld.yaml") template_file = '--template-file=' + tosca_helloworld template_type = '--template-type=tosca' - template_validation = "--validate-only=true" + template_validation = "--validate-only" failure_msg = _('The program raised an exception unexpectedly.') - def test_missing_arg(self): - error = self.assertRaises(ValueError, shell.main, '') - err_msg = _('The program requires minimum two arguments. ' - 'Please refer to the usage documentation.') - self.assertEqual(err_msg, str(error)) - - def test_invalid_file_arg(self): - error = self.assertRaises(ValueError, shell.main, 'translate me') - err_msg = _('The program expects --template-file as first ' - 'argument. Please refer to the usage documentation.') - self.assertEqual(err_msg, str(error)) - - def test_invalid_type_arg(self): - error = self.assertRaises(ValueError, - shell.main, ('--template-file=', 'xyz')) - err_msg = _('The program expects --template-type as second argument. ' - 'Please refer to the usage documentation.') - self.assertEqual(err_msg, str(error)) - def test_invalid_file_value(self): error = self.assertRaises(ValueError, shell.main, ('--template-file=template.txt', @@ -59,17 +40,13 @@ class ShellTest(TestCase): self.assertEqual(err_msg, str(error)) def test_invalid_type_value(self): - error = self.assertRaises(ValueError, shell.main, - (self.template_file, '--template-type=xyz')) - err_msg = _('xyz is not a valid template type.') - self.assertEqual(err_msg, str(error)) + self.assertRaises(SystemExit, shell.main, + (self.template_file, '--template-type=xyz')) def test_invalid_parameters(self): - error = self.assertRaises(ValueError, shell.main, - (self.template_file, self.template_type, - '--parameters=key')) - err_msg = _("'key' is not a well-formed parameter.") - self.assertEqual(err_msg, str(error)) + self.assertRaises(ValueError, shell.main, + (self.template_file, self.template_type, + '--parameters=key')) def test_valid_template(self): try: @@ -77,6 +54,12 @@ class ShellTest(TestCase): except Exception: self.fail(self.failure_msg) + def test_valid_template_without_type(self): + try: + shell.main([self.template_file]) + except Exception: + self.fail(self.failure_msg) + def test_valid_template_with_parameters(self): tosca_single_instance_wordpress = os.path.join( os.path.dirname(os.path.abspath(__file__)), @@ -126,7 +109,10 @@ class ShellTest(TestCase): @patch('os.getenv') @patch('translator.hot.tosca.tosca_compute.' 'ToscaCompute._create_nova_flavor_dict') - def test_template_deploy_with_credentials(self, mock_flavor_dict, + @patch('translator.hot.tosca.tosca_compute.' + 'ToscaCompute._populate_image_dict') + def test_template_deploy_with_credentials(self, mock_populate_image_dict, + mock_flavor_dict, mock_os_getenv, mock_token, mock_url, mock_post, @@ -137,6 +123,14 @@ class ShellTest(TestCase): mock_flavor_dict.return_value = { 'm1.medium': {'mem_size': 4096, 'disk_size': 40, 'num_cpus': 2} } + mock_populate_image_dict.return_value = { + "rhel-6.5-test-image": { + "version": "6.5", + "architecture": "x86_64", + "distribution": "RHEL", + "type": "Linux" + } + } mock_url.return_value = 'http://abc.com' mock_token.return_value = 'mock_token' mock_os_getenv.side_effect = ['demo', 'demo', diff --git a/tosca2heat/heat-translator/translator/tests/test_tosca_hot_translation.py b/tosca2heat/heat-translator/translator/tests/test_tosca_hot_translation.py index e58d842..7a89827 100644 --- a/tosca2heat/heat-translator/translator/tests/test_tosca_hot_translation.py +++ b/tosca2heat/heat-translator/translator/tests/test_tosca_hot_translation.py @@ -23,35 +23,48 @@ from translator.tests.base import TestCase class ToscaHotTranslationTest(TestCase): - def test_hot_translate_single_server(self): - tosca_file = '../tests/data/tosca_single_server.yaml' - hot_file = '../tests/data/hot_output/hot_single_server.yaml' - params = {'cpus': 1} + def _test_successful_translation(self, tosca_file, hot_file, params=None): + if not params: + params = {} diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, hot_file, params) self.assertEqual({}, diff, '<difference> : ' + json.dumps(diff, indent=4, separators=(', ', ': '))) + def _test_failed_translation(self, tosca_file, hot_file, params, msg, + msg_path, error_raise, error_collect): + if msg_path: + path = os.path.normpath(os.path.join( + os.path.dirname(os.path.realpath(__file__)), tosca_file)) + msg = msg % path + self.assertRaises( + error_raise, + TranslationUtils.compare_tosca_translation_with_hot, + tosca_file, hot_file, params) + ExceptionCollector.assertExceptionMessage(error_collect, msg) + + def test_hot_translate_single_server(self): + tosca_file = '../tests/data/tosca_single_server.yaml' + hot_file = '../tests/data/hot_output/hot_single_server.yaml' + params = {'cpus': 1} + self._test_successful_translation(tosca_file, hot_file, params) + def test_hot_translate_single_server_with_defaults(self): tosca_file = \ '../tests/data/tosca_single_server_with_defaults.yaml' + hot_file_with_input = '../tests/data/hot_output/' \ 'hot_single_server_with_defaults_with_input.yaml' - hot_file_without_input = '../tests/data/hot_output/' \ - 'hot_single_server_with_defaults_without_input.yaml' - params1 = {'cpus': '1'} - diff1 = TranslationUtils.compare_tosca_translation_with_hot( - tosca_file, hot_file_with_input, params1) - self.assertEqual({}, diff1, '<difference> : ' + - json.dumps(diff1, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file_with_input, + params1) + hot_file_without_input = '../tests/data/hot_output/' \ + 'hot_single_server_with_defaults_without_input.yaml' params2 = {} - diff2 = TranslationUtils.compare_tosca_translation_with_hot( - tosca_file, hot_file_without_input, params2) - self.assertEqual({}, diff2, '<difference> : ' + - json.dumps(diff2, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file_without_input, + params2) def test_hot_translate_wordpress_single_instance(self): tosca_file = '../tests/data/tosca_single_instance_wordpress.yaml' @@ -63,29 +76,17 @@ class ToscaHotTranslationTest(TestCase): 'db_root_pwd': 'passw0rd', 'db_port': 3366, 'cpus': 8} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_helloworld(self): tosca_file = '../tests/data/tosca_helloworld.yaml' hot_file = '../tests/data/hot_output/hot_hello_world.yaml' - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - {}) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file) def test_hot_translate_host_assignment(self): tosca_file = '../tests/data/test_host_assignment.yaml' hot_file = '../tests/data/hot_output/hot_host_assignment.yaml' - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - {}) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file) def test_hot_translate_elk(self): tosca_file = '../tests/data/tosca_elk.yaml' @@ -93,11 +94,7 @@ class ToscaHotTranslationTest(TestCase): params = {'github_url': 'http://github.com/paypal/rest-api-sample-app-nodejs.git', 'my_cpus': 4} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_nodejs_mongodb_two_instances(self): tosca_file = '../tests/data/tosca_nodejs_mongodb_two_instances.yaml' @@ -106,11 +103,7 @@ class ToscaHotTranslationTest(TestCase): params = {'github_url': 'http://github.com/paypal/rest-api-sample-app-nodejs.git', 'my_cpus': 4} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_blockstorage_with_attachment(self): tosca_file = '../tests/data/storage/' \ @@ -121,11 +114,7 @@ class ToscaHotTranslationTest(TestCase): 'storage_location': '/dev/vdc', 'storage_size': '2000 MB', 'storage_snapshot_id': 'ssid'} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_blockstorage_with_custom_relationship_type(self): tosca_file = '../tests/data/storage/' \ @@ -136,11 +125,7 @@ class ToscaHotTranslationTest(TestCase): 'storage_location': '/dev/vdc', 'storage_size': '1 GB', 'storage_snapshot_id': 'ssid'} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_blockstorage_with_relationship_template(self): tosca_file = '../tests/data/storage/' \ @@ -150,11 +135,7 @@ class ToscaHotTranslationTest(TestCase): params = {'cpus': 1, 'storage_location': '/dev/vdc', 'storage_size': '1 GB'} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_blockstorage_with_attachment_notation1(self): tosca_file = '../tests/data/storage/' \ @@ -167,19 +148,11 @@ class ToscaHotTranslationTest(TestCase): 'storage_location': 'some_folder', 'storage_size': '1 GB', 'storage_snapshot_id': 'ssid'} - diff1 = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file1, - params) + try: - self.assertEqual({}, diff1, '<difference> : ' + - json.dumps(diff1, indent=4, - separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file1, params) except Exception: - diff2 = TranslationUtils.compare_tosca_translation_with_hot( - tosca_file, hot_file2, params) - self.assertEqual({}, diff2, '<difference> : ' + - json.dumps(diff2, indent=4, - separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file2, params) def test_hot_translate_blockstorage_with_attachment_notation2(self): tosca_file = '../tests/data/storage/' \ @@ -192,19 +165,10 @@ class ToscaHotTranslationTest(TestCase): 'storage_location': '/dev/vdc', 'storage_size': '1 GB', 'storage_snapshot_id': 'ssid'} - diff1 = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file1, - params) try: - self.assertEqual({}, diff1, '<difference> : ' + - json.dumps(diff1, indent=4, - separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file1, params) except Exception: - diff2 = TranslationUtils.compare_tosca_translation_with_hot( - tosca_file, hot_file2, params) - self.assertEqual({}, diff2, '<difference> : ' + - json.dumps(diff2, indent=4, - separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file2, params) def test_hot_translate_multiple_blockstorage_with_attachment(self): tosca_file = '../tests/data/storage/' \ @@ -217,40 +181,23 @@ class ToscaHotTranslationTest(TestCase): 'storage_location': '/dev/vdc', 'storage_size': '1 GB', 'storage_snapshot_id': 'ssid'} - diff1 = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file1, - params) try: - self.assertEqual({}, diff1, '<difference> : ' + - json.dumps(diff1, indent=4, - separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file1, params) except Exception: - diff2 = TranslationUtils.compare_tosca_translation_with_hot( - tosca_file, hot_file2, params) - self.assertEqual({}, diff2, '<difference> : ' + - json.dumps(diff2, indent=4, - separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file2, params) def test_hot_translate_single_object_store(self): tosca_file = '../tests/data/storage/tosca_single_object_store.yaml' hot_file = '../tests/data/hot_output/hot_single_object_store.yaml' params = {'objectstore_name': 'myobjstore'} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_one_server_one_network(self): tosca_file = '../tests/data/network/tosca_one_server_one_network.yaml' hot_file = '../tests/data/hot_output/network/' \ 'hot_one_server_one_network.yaml' params = {'network_name': 'private_net'} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_server_on_existing_network(self): tosca_file = '../tests/data/network/' \ @@ -258,11 +205,7 @@ class ToscaHotTranslationTest(TestCase): hot_file = '../tests/data/hot_output/network/' \ 'hot_server_on_existing_network.yaml' params = {'network_name': 'private_net'} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_two_servers_one_network(self): tosca_file = '../tests/data/network/tosca_two_servers_one_network.yaml' @@ -272,11 +215,7 @@ class ToscaHotTranslationTest(TestCase): 'network_cidr': '10.0.0.0/24', 'network_start_ip': '10.0.0.100', 'network_end_ip': '10.0.0.150'} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_one_server_three_networks(self): tosca_file = '../tests/data/network/' \ @@ -284,32 +223,20 @@ class ToscaHotTranslationTest(TestCase): hot_file = '../tests/data/hot_output/network/' \ 'hot_one_server_three_networks.yaml' params = {} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_software_component(self): tosca_file = '../tests/data/tosca_software_component.yaml' hot_file = '../tests/data/hot_output/hot_software_component.yaml' params = {'cpus': '1', 'download_url': 'http://www.software.com/download'} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_web_application(self): tosca_file = '../tests/data/tosca_web_application.yaml' hot_file = '../tests/data/hot_output/hot_web_application.yaml' params = {'cpus': '2', 'context_root': 'my_web_app'} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_template_with_url_import(self): tosca_file = '../tests/data/' \ @@ -322,11 +249,7 @@ class ToscaHotTranslationTest(TestCase): 'db_root_pwd': 'passw0rd', 'db_port': 3366, 'cpus': 8} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_template_by_url_with_local_import(self): tosca_file = 'https://raw.githubusercontent.com/openstack/' \ @@ -340,11 +263,7 @@ class ToscaHotTranslationTest(TestCase): 'db_root_pwd': 'passw0rd', 'db_port': 3366, 'cpus': 8} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_template_by_url_with_local_abspath_import(self): tosca_file = 'https://raw.githubusercontent.com/openstack/' \ @@ -359,17 +278,15 @@ class ToscaHotTranslationTest(TestCase): 'db_root_pwd': 'passw0rd', 'db_port': 3366, 'cpus': 8} - - self.assertRaises( - ValidationError, - TranslationUtils.compare_tosca_translation_with_hot, - tosca_file, hot_file, params) expected_msg = _('Absolute file name "/tmp/wordpress.yaml" cannot be ' 'used in a URL-based input template "https://raw.' 'githubusercontent.com/openstack/heat-translator/' 'master/translator/tests/data/tosca_single_instance_' 'wordpress_with_local_abspath_import.yaml".') - ExceptionCollector.assertExceptionMessage(ImportError, expected_msg) + msg_path = False + self._test_failed_translation(tosca_file, hot_file, params, + expected_msg, msg_path, ValidationError, + ImportError) def test_hot_translate_template_by_url_with_url_import(self): tosca_url = 'https://raw.githubusercontent.com/openstack/' \ @@ -383,20 +300,12 @@ class ToscaHotTranslationTest(TestCase): 'db_root_pwd': 'passw0rd', 'db_port': 3366, 'cpus': 8} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_url, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_url, hot_file, params) def test_translate_hello_world_csar(self): tosca_file = '../tests/data/csar_hello_world.zip' hot_file = '../tests/data/hot_output/hot_hello_world.yaml' - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - {}) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file) def test_translate_single_instance_wordpress_csar(self): tosca_file = '../tests/data/csar_single_instance_wordpress.zip' @@ -408,11 +317,7 @@ class ToscaHotTranslationTest(TestCase): 'db_root_pwd': 'passw0rd', 'db_port': 3366, 'cpus': 8} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_translate_elk_csar_from_url(self): tosca_file = 'https://github.com/openstack/heat-translator/raw/' \ @@ -421,150 +326,103 @@ class ToscaHotTranslationTest(TestCase): params = {'github_url': 'http://github.com/paypal/rest-api-sample-app-nodejs.git', 'my_cpus': 4} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_translate_csar_not_zip(self): tosca_file = '../tests/data/csar_not_zip.zip' hot_file = '' params = {} - - self.assertRaises( - ValidationError, - TranslationUtils.compare_tosca_translation_with_hot, - tosca_file, hot_file, params) - path = os.path.normpath(os.path.join( - os.path.dirname(os.path.realpath(__file__)), tosca_file)) - expected_msg = _('"%s" is not a valid zip file.') % path - ExceptionCollector.assertExceptionMessage(ValidationError, - expected_msg) + expected_msg = _('"%s" is not a valid zip file.') + msg_path = True + self._test_failed_translation(tosca_file, hot_file, params, + expected_msg, msg_path, ValidationError, + ValidationError) def test_translate_csar_metadata_not_yaml(self): tosca_file = '../tests/data/csar_metadata_not_yaml.zip' hot_file = '' params = {} - - self.assertRaises( - ValidationError, - TranslationUtils.compare_tosca_translation_with_hot, - tosca_file, hot_file, params) - path = os.path.normpath(os.path.join( - os.path.dirname(os.path.realpath(__file__)), tosca_file)) expected_msg = _('The file "TOSCA-Metadata/TOSCA.meta" in the CSAR ' - '"%s" does not contain valid YAML content.') % path - ExceptionCollector.assertExceptionMessage(ValidationError, - expected_msg) + '"%s" does not contain valid YAML content.') + msg_path = True + self._test_failed_translation(tosca_file, hot_file, params, + expected_msg, msg_path, ValidationError, + ValidationError) def test_translate_csar_wrong_metadata_file(self): tosca_file = '../tests/data/csar_wrong_metadata_file.zip' hot_file = '' params = {} - - self.assertRaises( - ValidationError, - TranslationUtils.compare_tosca_translation_with_hot, - tosca_file, hot_file, params) - path = os.path.normpath(os.path.join( - os.path.dirname(os.path.realpath(__file__)), tosca_file)) expected_msg = _('"%s" is not a valid CSAR as it does not contain the ' 'required file "TOSCA.meta" in the folder ' - '"TOSCA-Metadata".') % path - ExceptionCollector.assertExceptionMessage(ValidationError, - expected_msg) + '"TOSCA-Metadata".') + msg_path = True + self._test_failed_translation(tosca_file, hot_file, params, + expected_msg, msg_path, ValidationError, + ValidationError) def test_translate_csar_wordpress_invalid_import_path(self): tosca_file = '../tests/data/csar_wordpress_invalid_import_path.zip' hot_file = '' params = {} - - self.assertRaises( - ValidationError, - TranslationUtils.compare_tosca_translation_with_hot, - tosca_file, hot_file, params) expected_msg = _('Import ' '"Invalid_import_path/wordpress.yaml" is not valid.') - ExceptionCollector.assertExceptionMessage(ImportError, expected_msg) + msg_path = False + self._test_failed_translation(tosca_file, hot_file, params, + expected_msg, msg_path, ValidationError, + ImportError) def test_translate_csar_wordpress_invalid_script_url(self): tosca_file = '../tests/data/csar_wordpress_invalid_script_url.zip' hot_file = '' params = {} - - self.assertRaises( - ValidationError, - TranslationUtils.compare_tosca_translation_with_hot, - tosca_file, hot_file, params) expected_msg = _('The resource at ' '"https://raw.githubusercontent.com/openstack/' 'heat-translator/master/translator/tests/data/' 'custom_types/wordpress1.yaml" cannot be accessed.') - ExceptionCollector.assertExceptionMessage(URLException, expected_msg) + msg_path = False + self._test_failed_translation(tosca_file, hot_file, params, + expected_msg, msg_path, ValidationError, + URLException) def test_hot_translate_flavor_image(self): tosca_file = '../tests/data/test_tosca_flavor_and_image.yaml' hot_file = '../tests/data/hot_output/hot_flavor_and_image.yaml' - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - {}) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file) def test_hot_translate_flavor_image_params(self): tosca_file = '../tests/data/test_tosca_flavor_and_image.yaml' hot_file = '../tests/data/hot_output/hot_flavor_and_image_params.yaml' params = {'key_name': 'paramkey'} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_custom_type(self): tosca_file = '../tests/data/test_tosca_custom_type.yaml' hot_file = '../tests/data/hot_output/' \ 'hot_custom_type.yaml' params = {} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_custom_type_with_override(self): tosca_file = '../tests/data/test_tosca_custom_type_with_override.yaml' hot_file = '../tests/data/hot_output/' \ 'hot_custom_type_with_override.yaml' params = {} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_custom_type_with_param_override(self): tosca_file = '../tests/data/test_tosca_custom_type_with_override.yaml' hot_file = '../tests/data/hot_output/' \ 'hot_custom_type_with_param_override.yaml' params = {'install_path': '/home/custom/from/cli'} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_artifact(self): tosca_file = '../tests/data/test_tosca_artifact.yaml' hot_file = '../tests/data/hot_output/' \ 'hot_artifact.yaml' params = {} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_without_tosca_os_version(self): tosca_file = '../tests/data/' \ @@ -572,21 +430,13 @@ class ToscaHotTranslationTest(TestCase): hot_file = '../tests/data/hot_output/' \ 'hot_single_server_without_tosca_os_version.yaml' params = {} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_helloworld_with_userkey(self): tosca_file = '../tests/data/tosca_helloworld.yaml' hot_file = '../tests/data/hot_output/hot_hello_world_userkey.yaml' params = {'key_name': 'userkey'} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_custom_networks_nodes_inline(self): tosca_file = '../tests/data/network/' \ @@ -594,11 +444,7 @@ class ToscaHotTranslationTest(TestCase): hot_file = '../tests/data/hot_output/network/' \ 'hot_custom_network_nodes.yaml' params = {} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_custom_networks_nodes_imports(self): tosca_file = '../tests/data/network/' \ @@ -606,28 +452,40 @@ class ToscaHotTranslationTest(TestCase): hot_file = '../tests/data/hot_output/network/' \ 'hot_custom_network_nodes.yaml' params = {} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_nfv_sample(self): tosca_file = '../tests/data/test_tosca_nfv_sample.yaml' hot_file = '../tests/data/hot_output/hot_nfv_sample.yaml' params = {} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) def test_hot_translate_policy(self): tosca_file = '../tests/data/tosca_policies.yaml' hot_file = '../tests/data/hot_output/hot_policies.yaml' params = {} - diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, - hot_file, - params) - self.assertEqual({}, diff, '<difference> : ' + - json.dumps(diff, indent=4, separators=(', ', ': '))) + self._test_successful_translation(tosca_file, hot_file, params) + + def test_hot_script_types(self): + tosca_file = '../tests/data/test_tosca_script_types.yaml' + hot_file = '../tests/data/hot_output/hot_script_types.yaml' + params = {} + self._test_successful_translation(tosca_file, hot_file, params) + + def test_hot_interface_on_compute(self): + tosca_file = '../tests/data/test_tosca_interface_on_compute.yaml' + 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) + + def test_hot_exchange_public_ssh_key(self): + tosca_file = '../tests/data/tosca_exchange_public_ssh_key.yaml' + hot_file = '../tests/data/hot_output/hot_exchange_public_ssh_key.yaml' + params = {} + self._test_successful_translation(tosca_file, hot_file, params) |