diff options
Diffstat (limited to 'tosca2heat/heat-translator')
36 files changed, 1220 insertions, 519 deletions
diff --git a/tosca2heat/heat-translator/.gitignore b/tosca2heat/heat-translator/.gitignore new file mode 100644 index 0000000..7dfa8ae --- /dev/null +++ b/tosca2heat/heat-translator/.gitignore @@ -0,0 +1,53 @@ +*.py[cod] + +# C extensions +*.so + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg +lib +lib64 + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox +nosetests.xml +.testrepository + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Complexity +output/*.html +output/*/index.html + +# Sphinx +doc/build + +# pbr generates these +AUTHORS +ChangeLog + +# Editors +*~ +.*.swp +.idea +*.iml
\ No newline at end of file diff --git a/tosca2heat/heat-translator/.gitreview b/tosca2heat/heat-translator/.gitreview new file mode 100644 index 0000000..59046fa --- /dev/null +++ b/tosca2heat/heat-translator/.gitreview @@ -0,0 +1,4 @@ +[gerrit] +host=review.openstack.org +port=29418 +project=openstack/heat-translator.git diff --git a/tosca2heat/heat-translator/.mailmap b/tosca2heat/heat-translator/.mailmap new file mode 100644 index 0000000..cc92f17 --- /dev/null +++ b/tosca2heat/heat-translator/.mailmap @@ -0,0 +1,3 @@ +# Format is: +# <preferred e-mail> <other e-mail 1> +# <preferred e-mail> <other e-mail 2>
\ No newline at end of file diff --git a/tosca2heat/heat-translator/doc/source/usage.rst b/tosca2heat/heat-translator/doc/source/usage.rst index ebad0e8..e8f132a 100644 --- a/tosca2heat/heat-translator/doc/source/usage.rst +++ b/tosca2heat/heat-translator/doc/source/usage.rst @@ -65,10 +65,11 @@ In this case, you can simply run translation via CLI entry point:: Things To Consider ------------------ -* When deploying the translated template with Heat, please ensure that you have image registered in the Glance. The Heat-Translator - project sets flavor and image from a pre-defined set of values (as listed in /home/openstack/heat-translator/translator/hot/tosca/tosca_compute.py) - with the best possible match to the constraints defined in the TOSCA template. If there is no possible match found, a null value is set currently. - Per the future plan, an image and flavor will be provided from an online repository. +* When use Heat-Translator in an OpenStack environment, please ensure that you have one or more preferred flavors and images available in your OpenStack + environment. To find an appropriate flavor and image, that meets constraints defined in the TOSCA template for the HOST and OS capabilities of TOSCA Compute node, + the Heat-Translator project first runs a query against Nova flavors and Glance images. During the query call, it uses the metadata of flavors and images. + If call to Nova or Glance can not be made or no flavor or image is found, the Heat-Translator project will set flavor and image from a pre-defined set of values (as listed in /home/openstack/heat-translator/translator/hot/tosca/tosca_compute.py) + with the best possible match to the constraints defined in the TOSCA template. * The ``key_name`` property of Nova server is irrelevant to the TOSCA specification and can not be used in TOSCA template. In order to use it in the translated templates, the user must provide it via parameters, and the heat-translator will set it to all resources of ``OS::Nova::Server`` type. * Since properties of TOSCA Compute OS and HOST capabilities are optional, the user should make sure that either they set these properties correctly diff --git a/tosca2heat/heat-translator/requirements.txt b/tosca2heat/heat-translator/requirements.txt index e54d0ab..b9792da 100644 --- a/tosca2heat/heat-translator/requirements.txt +++ b/tosca2heat/heat-translator/requirements.txt @@ -2,9 +2,9 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr>=1.6 # Apache-2.0 -Babel>=1.3 # BSD +Babel>=2.3.4 # BSD cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0 PyYAML>=3.1.0 # MIT python-dateutil>=2.4.2 # BSD six>=1.9.0 # MIT -tosca-parser>=0.4.0 # Apache-2.0 +tosca-parser>=0.5.0 # Apache-2.0 diff --git a/tosca2heat/heat-translator/run_py27-tosca-parser-master.sh b/tosca2heat/heat-translator/run_py27-tosca-parser-master.sh new file mode 100644 index 0000000..19fe15a --- /dev/null +++ b/tosca2heat/heat-translator/run_py27-tosca-parser-master.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +pip install -q -U -e "git+https://git.openstack.org/openstack/tosca-parser.git#egg=tosca_parser" diff --git a/tosca2heat/heat-translator/test-requirements.txt b/tosca2heat/heat-translator/test-requirements.txt index 2b9cae7..70a261b 100644 --- a/tosca2heat/heat-translator/test-requirements.txt +++ b/tosca2heat/heat-translator/test-requirements.txt @@ -4,11 +4,11 @@ hacking<0.11,>=0.10.0 coverage>=3.6 # Apache-2.0 discover # BSD -fixtures>=1.3.1 # Apache-2.0/BSD +fixtures>=3.0.0 # Apache-2.0/BSD oslotest>=1.10.0 # Apache-2.0 oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD -sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD +sphinx!=1.3b1,<1.3,>=1.2.1 # BSD testrepository>=0.0.18 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD testtools>=1.4.0 # MIT diff --git a/tosca2heat/heat-translator/tox.ini b/tosca2heat/heat-translator/tox.ini index 868e95a..11c9c97 100644 --- a/tosca2heat/heat-translator/tox.ini +++ b/tosca2heat/heat-translator/tox.ini @@ -1,6 +1,6 @@ [tox] minversion = 1.6 -envlist = py34,py27,pypy,pep8 +envlist = py34,py27,pep8 skipsdist = True [testenv] @@ -27,6 +27,11 @@ commands = python setup.py build_sphinx [testenv:debug] commands = oslo_debug_helper -t translator/tests {posargs} +[testenv:py27-tosca-parser-master] +commands = + ./run_py27-tosca-parser-master.sh + python setup.py test --slowest --testr-args='{posargs}' + [flake8] # H803 skipped on purpose per list discussion. # E123, E125 skipped as they are invalid PEP-8. 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/heat_translator_logging.conf b/tosca2heat/heat-translator/translator/conf/heat_translator_logging.conf index e01a889..e01a889 100644 --- a/tosca2heat/heat-translator/heat_translator_logging.conf +++ b/tosca2heat/heat-translator/translator/conf/heat_translator_logging.conf 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) |