diff options
author | 2016-04-22 08:43:26 +0000 | |
---|---|---|
committer | 2016-04-22 08:43:26 +0000 | |
commit | 2fa4785aa218cf655f3405d832d2200d64fd033e (patch) | |
tree | 0061edccecca9a99aa9a50c6c0e2783c07370112 /tosca2heat/heat-translator/translator | |
parent | 56c2a5d3ba6193c9a98870a264bc5e3aca19a086 (diff) | |
parent | c8201c119ec686e79797721156767685fe848aca (diff) |
Merge "Update tosca lib to version 0.5"
Diffstat (limited to 'tosca2heat/heat-translator/translator')
174 files changed, 11781 insertions, 0 deletions
diff --git a/tosca2heat/heat-translator/translator/__init__.py b/tosca2heat/heat-translator/translator/__init__.py new file mode 100644 index 0000000..3c2a807 --- /dev/null +++ b/tosca2heat/heat-translator/translator/__init__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import pbr.version + + +__version__ = pbr.version.VersionInfo( + 'heat-translator').version_string() diff --git a/tosca2heat/heat-translator/translator/common/__init__.py b/tosca2heat/heat-translator/translator/common/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tosca2heat/heat-translator/translator/common/__init__.py diff --git a/tosca2heat/heat-translator/translator/common/exception.py b/tosca2heat/heat-translator/translator/common/exception.py new file mode 100644 index 0000000..be86116 --- /dev/null +++ b/tosca2heat/heat-translator/translator/common/exception.py @@ -0,0 +1,48 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +''' +Exceptions for the TOSCA Translator package. +''' + +from toscaparser.common.exception import TOSCAException +from toscaparser.utils.gettextutils import _ + + +class ConfFileParseError(TOSCAException): + msg_fmt = _('%(message)s') + + +class ConfOptionNotDefined(TOSCAException): + msg_fmt = _('Option %(key)s in section %(section)s ' + 'is not defined in conf file') + + +class ConfSectionNotDefined(TOSCAException): + msg_fmt = _('Section %(section)s is not defined in conf file') + + +class ToscaModImportError(TOSCAException): + msg_fmt = _('Unable to import module %(mod_name)s. ' + 'Check to see that it exists and has no ' + 'language definition errors.') + + +class ToscaClassImportError(TOSCAException): + msg_fmt = _('Unable to import class %(name)s in ' + 'module %(mod_name)s. Check to see that it ' + 'exists and has no language definition errors.') + + +class ToscaClassAttributeError(TOSCAException): + msg_fmt = _('Class attribute referenced not found. ' + '%(message)s. Check to see that it is defined.') diff --git a/tosca2heat/heat-translator/translator/common/utils.py b/tosca2heat/heat-translator/translator/common/utils.py new file mode 100644 index 0000000..459b5ee --- /dev/null +++ b/tosca2heat/heat-translator/translator/common/utils.py @@ -0,0 +1,319 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import json +import logging +import math +import numbers +import os +import re +import requests +from six.moves.urllib.parse import urlparse +import yaml + +from toscaparser.utils.gettextutils import _ +import toscaparser.utils.yamlparser + +YAML_ORDER_PARSER = toscaparser.utils.yamlparser.simple_ordered_parse +log = logging.getLogger('heat-translator') + +# Required environment variables to create openstackclient object. +ENV_VARIABLES = ['OS_AUTH_URL', 'OS_PASSWORD', 'OS_USERNAME', 'OS_TENANT_NAME'] + + +class MemoryUnit(object): + + UNIT_SIZE_DEFAULT = 'B' + UNIT_SIZE_DICT = {'B': 1, 'kB': 1000, 'KiB': 1024, 'MB': 1000000, + 'MiB': 1048576, 'GB': 1000000000, + 'GiB': 1073741824, 'TB': 1000000000000, + 'TiB': 1099511627776} + + @staticmethod + def convert_unit_size_to_num(size, unit=None): + """Convert given size to a number representing given unit. + + If unit is None, convert to a number representing UNIT_SIZE_DEFAULT + :param size: unit size e.g. 1 TB + :param unit: unit to be converted to e.g GB + :return: converted number e.g. 1000 for 1 TB size and unit GB + """ + if unit: + unit = MemoryUnit.validate_unit(unit) + else: + unit = MemoryUnit.UNIT_SIZE_DEFAULT + log.info(_('A memory unit is not provided for size; using the ' + 'default unit %(default)s.') % {'default': 'B'}) + regex = re.compile('(\d*)\s*(\w*)') + result = regex.match(str(size)).groups() + if result[1]: + unit_size = MemoryUnit.validate_unit(result[1]) + converted = int(str_to_num(result[0]) + * MemoryUnit.UNIT_SIZE_DICT[unit_size] + * math.pow(MemoryUnit.UNIT_SIZE_DICT + [unit], -1)) + log.info(_('Given size %(size)s is converted to %(num)s ' + '%(unit)s.') % {'size': size, + 'num': converted, 'unit': unit}) + else: + converted = (str_to_num(result[0])) + return converted + + @staticmethod + def validate_unit(unit): + if unit in MemoryUnit.UNIT_SIZE_DICT.keys(): + return unit + else: + for key in MemoryUnit.UNIT_SIZE_DICT.keys(): + if key.upper() == unit.upper(): + return key + + msg = _('Provided unit "{0}" is not valid. The valid units are' + ' {1}').format(unit, MemoryUnit.UNIT_SIZE_DICT.keys()) + log.error(msg) + raise ValueError(msg) + + +class CompareUtils(object): + + MISMATCH_VALUE1_LABEL = "<Expected>" + MISMATCH_VALUE2_LABEL = "<Provided>" + ORDERLESS_LIST_KEYS = ['allowed_values', 'depends_on'] + + @staticmethod + def compare_dicts(dict1, dict2): + """Return False if not equal, True if both are equal.""" + + if dict1 is None and dict2 is None: + return True + if dict1 is None or dict2 is None: + return False + + both_equal = True + for dict1_item, dict2_item in zip(dict1.items(), dict2.items()): + if dict1_item != dict2_item: + msg = (_("%(label1)s: %(item1)s \n is not equal to \n:" + "%(label2)s: %(item2)s") + % {'label1': CompareUtils.MISMATCH_VALUE2_LABEL, + 'item1': dict1_item, + 'label2': CompareUtils.MISMATCH_VALUE1_LABEL, + 'item2': dict2_item}) + log.warning(msg) + both_equal = False + break + return both_equal + + @staticmethod + def compare_hot_yamls(generated_yaml, expected_yaml): + hot_translated_dict = YAML_ORDER_PARSER(generated_yaml) + hot_expected_dict = YAML_ORDER_PARSER(expected_yaml) + return CompareUtils.compare_dicts(hot_translated_dict, + hot_expected_dict) + + @staticmethod + def reorder(dic): + '''Canonicalize list items in the dictionary for ease of comparison. + + For properties whose value is a list in which the order does not + matter, some pre-processing is required to bring those lists into a + canonical format. We use sorting just to make sure such differences + in ordering would not cause to a mismatch. + ''' + + if type(dic) is not dict: + return None + + reordered = {} + for key in dic.keys(): + value = dic[key] + if type(value) is dict: + reordered[key] = CompareUtils.reorder(value) + elif type(value) is list \ + and key in CompareUtils.ORDERLESS_LIST_KEYS: + reordered[key] = sorted(value) + else: + reordered[key] = value + return reordered + + @staticmethod + def diff_dicts(dict1, dict2, reorder=True): + '''Compares two dictionaries and returns their differences. + + Returns a dictionary of mismatches between the two dictionaries. + An empty dictionary is returned if two dictionaries are equivalent. + The reorder parameter indicates whether reordering is required + before comparison or not. + ''' + + if reorder: + dict1 = CompareUtils.reorder(dict1) + dict2 = CompareUtils.reorder(dict2) + + if dict1 is None and dict2 is None: + return {} + if dict1 is None or dict2 is None: + return {CompareUtils.MISMATCH_VALUE1_LABEL: dict1, + CompareUtils.MISMATCH_VALUE2_LABEL: dict2} + + diff = {} + keys1 = set(dict1.keys()) + keys2 = set(dict2.keys()) + for key in keys1.union(keys2): + if key in keys1 and key not in keys2: + diff[key] = {CompareUtils.MISMATCH_VALUE1_LABEL: dict1[key], + CompareUtils.MISMATCH_VALUE2_LABEL: None} + elif key not in keys1 and key in keys2: + diff[key] = {CompareUtils.MISMATCH_VALUE1_LABEL: None, + CompareUtils.MISMATCH_VALUE2_LABEL: dict2[key]} + else: + val1 = dict1[key] + val2 = dict2[key] + if val1 != val2: + if type(val1) is dict and type(val2) is dict: + diff[key] = CompareUtils.diff_dicts(val1, val2, False) + else: + diff[key] = {CompareUtils.MISMATCH_VALUE1_LABEL: val1, + CompareUtils.MISMATCH_VALUE2_LABEL: val2} + return diff + + +class YamlUtils(object): + + @staticmethod + def get_dict(yaml_file): + '''Returns the dictionary representation of the given YAML spec.''' + try: + return yaml.load(open(yaml_file)) + except IOError: + return None + + @staticmethod + def compare_yamls(yaml1_file, yaml2_file): + '''Returns true if two dictionaries are equivalent, false otherwise.''' + dict1 = YamlUtils.get_dict(yaml1_file) + dict2 = YamlUtils.get_dict(yaml2_file) + return CompareUtils.compare_dicts(dict1, dict2) + + @staticmethod + def compare_yaml_dict(yaml_file, dic): + '''Returns true if yaml matches the dictionary, false otherwise.''' + return CompareUtils.compare_dicts(YamlUtils.get_dict(yaml_file), dic) + + +class TranslationUtils(object): + + @staticmethod + def compare_tosca_translation_with_hot(tosca_file, hot_file, params): + '''Verify tosca translation against the given hot specification. + + inputs: + tosca_file: relative local path or URL to the tosca input file + hot_file: relative path to expected hot output + params: dictionary of parameter name value pairs + + Returns as a dictionary the difference between the HOT translation + of the given tosca_file and the given hot_file. + ''' + + from toscaparser.tosca_template import ToscaTemplate + from translator.hot.tosca_translator import TOSCATranslator + + tosca_tpl = os.path.normpath(os.path.join( + os.path.dirname(os.path.abspath(__file__)), tosca_file)) + a_file = os.path.isfile(tosca_tpl) + if not a_file: + tosca_tpl = tosca_file + + expected_hot_tpl = os.path.join( + os.path.dirname(os.path.abspath(__file__)), hot_file) + + tosca = ToscaTemplate(tosca_tpl, params, a_file) + translate = TOSCATranslator(tosca, params) + + output = translate.translate() + output_dict = toscaparser.utils.yamlparser.simple_parse(output) + expected_output_dict = YamlUtils.get_dict(expected_hot_tpl) + return CompareUtils.diff_dicts(output_dict, expected_output_dict) + + +class UrlUtils(object): + + @staticmethod + def validate_url(path): + """Validates whether the given path is a URL or not. + + If the given path includes a scheme (http, https, ftp, ...) and a net + location (a domain name such as www.github.com) it is validated as a + URL. + """ + parsed = urlparse(path) + return bool(parsed.scheme) and bool(parsed.netloc) + + +def str_to_num(value): + """Convert a string representation of a number into a numeric type.""" + if isinstance(value, numbers.Number): + return value + try: + return int(value) + except ValueError: + return float(value) + + +def check_for_env_variables(): + return set(ENV_VARIABLES) < set(os.environ.keys()) + + +def get_ks_access_dict(): + tenant_name = os.getenv('OS_TENANT_NAME') + username = os.getenv('OS_USERNAME') + password = os.getenv('OS_PASSWORD') + auth_url = os.getenv('OS_AUTH_URL') + + auth_dict = { + "auth": { + "tenantName": tenant_name, + "passwordCredentials": { + "username": username, + "password": password + } + } + } + headers = {'Content-Type': 'application/json'} + try: + keystone_response = requests.post(auth_url + '/tokens', + data=json.dumps(auth_dict), + headers=headers) + if keystone_response.status_code != 200: + return None + return json.loads(keystone_response.content) + except Exception: + return None + + +def get_url_for(access_dict, service_type): + if access_dict is None: + return None + service_catalog = access_dict['access']['serviceCatalog'] + service_url = '' + for service in service_catalog: + if service['type'] == service_type: + service_url = service['endpoints'][0]['publicURL'] + break + return service_url + + +def get_token_id(access_dict): + if access_dict is None: + return None + return access_dict['access']['token']['id'] diff --git a/tosca2heat/heat-translator/translator/conf/__init__.py b/tosca2heat/heat-translator/translator/conf/__init__.py new file mode 100644 index 0000000..2c61252 --- /dev/null +++ b/tosca2heat/heat-translator/translator/conf/__init__.py @@ -0,0 +1,36 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +''' Initialize the global configuration for the translator ''' + +import os + +from translator.conf.config import ConfigProvider + +CONF_FILENAME = 'translator.conf' + + +def init_global_conf(): + '''Initialize the configuration provider. + + Allows the configuration to be shared throughout the translator code. + The file used is translator.conf, and is within the conf/ directory. It + is a standard ini format, and is prcessed using the ConfigParser module. + + ''' + conf_path = os.path.dirname(os.path.abspath(__file__)) + conf_file = os.path.join(conf_path, CONF_FILENAME) + ConfigProvider._load_config(conf_file) + + +init_global_conf() diff --git a/tosca2heat/heat-translator/translator/conf/config.py b/tosca2heat/heat-translator/translator/conf/config.py new file mode 100644 index 0000000..4e8fe87 --- /dev/null +++ b/tosca2heat/heat-translator/translator/conf/config.py @@ -0,0 +1,67 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +''' Provide a global configuration for the TOSCA translator''' + +from six.moves import configparser + +from toscaparser.utils.gettextutils import _ +import translator.common.exception as exception + + +class ConfigProvider(object): + '''Global config proxy that wraps a ConfigParser object. + + Allows for class based access to config values. Should only be initialized + once using the corresponding translator.conf file in the conf directory. + + ''' + + # List that captures all of the conf file sections. + # Append any new sections to this list. + _sections = ['DEFAULT'] + _translator_config = None + + @classmethod + def _load_config(cls, conf_file): + '''Private method only to be called once from the __init__ module''' + + cls._translator_config = configparser.ConfigParser() + try: + cls._translator_config.read(conf_file) + except configparser.ParsingError: + msg = _('Unable to parse translator.conf file.' + 'Check to see that it exists in the conf directory.') + raise exception.ConfFileParseError(message=msg) + + @classmethod + def get_value(cls, section, key): + try: + value = cls._translator_config.get(section, key) + except configparser.NoOptionError: + raise exception.ConfOptionNotDefined(key=key, section=section) + except configparser.NoSectionError: + raise exception.ConfSectionNotDefined(section=section) + + return value + + @classmethod + def get_all_values(cls): + values = [] + for section in cls._sections: + try: + values.extend(cls._translator_config.items(section=section)) + except configparser.NoOptionError: + raise exception.ConfSectionNotDefined(section=section) + + return values diff --git a/tosca2heat/heat-translator/translator/conf/translator.conf b/tosca2heat/heat-translator/translator/conf/translator.conf new file mode 100644 index 0000000..6ebf292 --- /dev/null +++ b/tosca2heat/heat-translator/translator/conf/translator.conf @@ -0,0 +1,4 @@ +[DEFAULT] + +# Relative path location for custom types +custom_types_location=translator/custom/hot
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/custom/__init__.py b/tosca2heat/heat-translator/translator/custom/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tosca2heat/heat-translator/translator/custom/__init__.py diff --git a/tosca2heat/heat-translator/translator/custom/hot/__init__.py b/tosca2heat/heat-translator/translator/custom/hot/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tosca2heat/heat-translator/translator/custom/hot/__init__.py diff --git a/tosca2heat/heat-translator/translator/hot/__init__.py b/tosca2heat/heat-translator/translator/hot/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/__init__.py diff --git a/tosca2heat/heat-translator/translator/hot/syntax/__init__.py b/tosca2heat/heat-translator/translator/hot/syntax/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/syntax/__init__.py diff --git a/tosca2heat/heat-translator/translator/hot/syntax/hot_output.py b/tosca2heat/heat-translator/translator/hot/syntax/hot_output.py new file mode 100644 index 0000000..ad77fb3 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/syntax/hot_output.py @@ -0,0 +1,25 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +class HotOutput(object): + '''Attributes for HOT output section.''' + + def __init__(self, name, value, description=None): + self.name = name + self.value = value + self.description = description + + def get_dict_output(self): + return {self.name: {'value': self.value, + 'description': self.description}} diff --git a/tosca2heat/heat-translator/translator/hot/syntax/hot_parameter.py b/tosca2heat/heat-translator/translator/hot/syntax/hot_parameter.py new file mode 100644 index 0000000..1ecb2ce --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/syntax/hot_parameter.py @@ -0,0 +1,52 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from collections import OrderedDict +import logging +from toscaparser.utils.gettextutils import _ + +KEYS = (TYPE, DESCRIPTION, DEFAULT, CONSTRAINTS, HIDDEN, LABEL) = \ + ('type', 'description', 'default', 'constraints', 'hidden', 'label') + +log = logging.getLogger('heat-translator') + + +class HotParameter(object): + '''Attributes for HOT parameter section.''' + + def __init__(self, name, type, label=None, description=None, default=None, + hidden=None, constraints=None): + self.name = name + self.type = type + self.label = label + self.description = description + self.default = default + self.hidden = hidden + self.constraints = constraints + log.info(_('Initialized the input parameters.')) + + def get_dict_output(self): + param_sections = OrderedDict() + param_sections[TYPE] = self.type + if self.label: + param_sections[LABEL] = self.label + if self.description: + param_sections[DESCRIPTION] = self.description + if self.default: + param_sections[DEFAULT] = self.default + if self.hidden: + param_sections[HIDDEN] = self.hidden + if self.constraints: + param_sections[CONSTRAINTS] = self.constraints + + return {self.name: param_sections} diff --git a/tosca2heat/heat-translator/translator/hot/syntax/hot_resource.py b/tosca2heat/heat-translator/translator/hot/syntax/hot_resource.py new file mode 100644 index 0000000..d7d0100 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/syntax/hot_resource.py @@ -0,0 +1,362 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from collections import OrderedDict +import logging +import six + +from toscaparser.elements.interfaces import InterfacesDef +from toscaparser.functions import GetInput +from toscaparser.nodetemplate import NodeTemplate +from toscaparser.utils.gettextutils import _ + + +SECTIONS = (TYPE, PROPERTIES, MEDADATA, DEPENDS_ON, UPDATE_POLICY, + DELETION_POLICY) = \ + ('type', 'properties', 'metadata', + 'depends_on', 'update_policy', 'deletion_policy') +log = logging.getLogger('heat-translator') + + +class HotResource(object): + '''Base class for TOSCA node type translation to Heat resource type.''' + + def __init__(self, nodetemplate, name=None, type=None, properties=None, + metadata=None, depends_on=None, + update_policy=None, deletion_policy=None): + log.debug(_('Translating TOSCA node type to HOT resource type.')) + self.nodetemplate = nodetemplate + if name: + self.name = name + else: + self.name = nodetemplate.name + self.type = type + self.properties = properties or {} + # special case for HOT softwareconfig + if type == 'OS::Heat::SoftwareConfig': + self.properties['group'] = 'script' + self.metadata = metadata + + # The difference between depends_on and depends_on_nodes is + # that depends_on defines dependency in the context of the + # HOT template and it is used during the template output. + # Depends_on_nodes defines the direct dependency between the + # tosca nodes and is not used during the output of the + # HOT template but for internal processing only. When a tosca + # node depends on another node it will be always added to + # depends_on_nodes but not always to depends_on. For example + # if the source of dependency is a server, the dependency will + # be added as properties.get_resource and not depends_on + if depends_on: + self.depends_on = depends_on + self.depends_on_nodes = depends_on + else: + self.depends_on = [] + self.depends_on_nodes = [] + self.update_policy = update_policy + self.deletion_policy = deletion_policy + self.group_dependencies = {} + # if hide_resource is set to true, then this resource will not be + # generated in the output yaml. + self.hide_resource = False + + def handle_properties(self): + # the property can hold a value or the intrinsic function get_input + # for value, copy it + # for get_input, convert to get_param + for prop in self.nodetemplate.get_properties_objects(): + pass + + def handle_life_cycle(self): + hot_resources = [] + deploy_lookup = {} + # TODO(anyone): sequence for life cycle needs to cover different + # scenarios and cannot be fixed or hard coded here + operations_deploy_sequence = ['create', 'configure', 'start'] + + operations = HotResource._get_all_operations(self.nodetemplate) + + # create HotResource for each operation used for deployment: + # create, start, configure + # ignore the other operations + # observe the order: create, start, configure + # use the current HotResource for the first operation in this order + + # hold the original name since it will be changed during + # the transformation + node_name = self.name + reserve_current = 'NONE' + + for operation in operations_deploy_sequence: + if operation in operations.keys(): + reserve_current = operation + break + + # create the set of SoftwareDeployment and SoftwareConfig for + # the interface operations + hosting_server = None + if self.nodetemplate.requirements is not None: + hosting_server = self._get_hosting_server() + for operation in operations.values(): + if operation.name in operations_deploy_sequence: + config_name = node_name + '_' + operation.name + '_config' + deploy_name = node_name + '_' + operation.name + '_deploy' + hot_resources.append( + HotResource(self.nodetemplate, + config_name, + 'OS::Heat::SoftwareConfig', + {'config': + {'get_file': operation.implementation}})) + + # hosting_server is None if requirements is None + hosting_on_server = (hosting_server.name if + hosting_server else None) + if operation.name == reserve_current: + 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 + else: + sd_config = {'config': {'get_resource': config_name}, + 'server': {'get_resource': + hosting_on_server}} + deploy_resource = \ + HotResource(self.nodetemplate, + deploy_name, + 'OS::Heat::SoftwareDeployment', + sd_config) + hot_resources.append(deploy_resource) + deploy_lookup[operation.name] = deploy_resource + lifecycle_inputs = self._get_lifecycle_inputs(operation) + if lifecycle_inputs: + deploy_resource.properties['input_values'] = \ + lifecycle_inputs + + # Add dependencies for the set of HOT resources in the sequence defined + # in operations_deploy_sequence + # TODO(anyone): find some better way to encode this implicit sequence + group = {} + for op, hot in deploy_lookup.items(): + # position to determine potential preceding nodes + op_index = operations_deploy_sequence.index(op) + for preceding_op in \ + reversed(operations_deploy_sequence[:op_index]): + preceding_hot = deploy_lookup.get(preceding_op) + if preceding_hot: + hot.depends_on.append(preceding_hot) + hot.depends_on_nodes.append(preceding_hot) + group[preceding_hot] = hot + break + + # 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 + + def handle_connectsto(self, tosca_source, tosca_target, hot_source, + hot_target, config_location, operation): + # The ConnectsTo relationship causes a configuration operation in + # the target. + # This hot resource is the software config portion in the HOT template + # This method adds the matching software deployment with the proper + # target server and dependency + if config_location == 'target': + hosting_server = hot_target._get_hosting_server() + hot_depends = hot_target + elif config_location == 'source': + hosting_server = self._get_hosting_server() + hot_depends = hot_source + deploy_name = tosca_source.name + '_' + tosca_target.name + \ + '_connect_deploy' + sd_config = {'config': {'get_resource': self.name}, + 'server': {'get_resource': hosting_server.name}} + deploy_resource = \ + HotResource(self.nodetemplate, + deploy_name, + 'OS::Heat::SoftwareDeployment', + sd_config, + depends_on=[hot_depends]) + connect_inputs = self._get_connect_inputs(config_location, operation) + if connect_inputs: + deploy_resource.properties['input_values'] = connect_inputs + + return deploy_resource + + def handle_expansion(self): + pass + + def handle_hosting(self): + # handle hosting server for the OS:HEAT::SoftwareDeployment + # from the TOSCA nodetemplate, traverse the relationship chain + # down to the server + if self.type == 'OS::Heat::SoftwareDeployment': + # skip if already have hosting + # If type is NodeTemplate, look up corresponding HotResrouce + host_server = self.properties.get('server') + if host_server is None or not host_server['get_resource']: + raise Exception(_("Internal Error: expecting host " + "in software deployment")) + elif isinstance(host_server['get_resource'], NodeTemplate): + self.properties['server']['get_resource'] = \ + host_server['get_resource'].name + + def top_of_chain(self): + dependent = self.group_dependencies.get(self) + if dependent is None: + return self + else: + return dependent.top_of_chain() + + def get_dict_output(self): + resource_sections = OrderedDict() + resource_sections[TYPE] = self.type + if self.properties: + resource_sections[PROPERTIES] = self.properties + if self.metadata: + resource_sections[MEDADATA] = self.metadata + if self.depends_on: + resource_sections[DEPENDS_ON] = [] + for depend in self.depends_on: + resource_sections[DEPENDS_ON].append(depend.name) + if self.update_policy: + resource_sections[UPDATE_POLICY] = self.update_policy + if self.deletion_policy: + resource_sections[DELETION_POLICY] = self.deletion_policy + + return {self.name: resource_sections} + + def _get_lifecycle_inputs(self, operation): + # check if this lifecycle operation has input values specified + # extract and convert to HOT format + if isinstance(operation.value, six.string_types): + # the operation has a static string + return {} + else: + # the operation is a dict {'implemenation': xxx, 'input': yyy} + inputs = operation.value.get('inputs') + deploy_inputs = {} + if inputs: + for name, value in six.iteritems(inputs): + deploy_inputs[name] = value + return deploy_inputs + + def _get_connect_inputs(self, config_location, operation): + if config_location == 'target': + inputs = operation.get('pre_configure_target').get('inputs') + elif config_location == 'source': + inputs = operation.get('pre_configure_source').get('inputs') + deploy_inputs = {} + if inputs: + for name, value in six.iteritems(inputs): + deploy_inputs[name] = value + return deploy_inputs + + def _get_hosting_server(self, node_template=None): + # find the server that hosts this software by checking the + # requirements and following the hosting chain + this_node_template = self.nodetemplate \ + if node_template is None else node_template + for requirement in this_node_template.requirements: + for requirement_name, assignment in six.iteritems(requirement): + for check_node in this_node_template.related_nodes: + # check if the capability is Container + if isinstance(assignment, dict): + node_name = assignment.get('node') + else: + node_name = assignment + if node_name and node_name == check_node.name: + if self._is_container_type(requirement_name, + check_node): + return check_node + elif check_node.related_nodes: + return self._get_hosting_server(check_node) + return None + + def _is_container_type(self, requirement_name, node): + # capability is a list of dict + # For now just check if it's type tosca.nodes.Compute + # TODO(anyone): match up requirement and capability + base_type = HotResource.get_base_type(node.type_definition) + if base_type.type == 'tosca.nodes.Compute': + return True + else: + return False + + def get_hot_attribute(self, attribute, args): + # this is a place holder and should be implemented by the subclass + # if translation is needed for the particular attribute + raise Exception(_("No translation in TOSCA type {0} for attribute " + "{1}").format(self.nodetemplate.type, attribute)) + + def get_tosca_props(self): + tosca_props = {} + for prop in self.nodetemplate.get_properties_objects(): + if isinstance(prop.value, GetInput): + tosca_props[prop.name] = {'get_param': prop.value.input_name} + else: + tosca_props[prop.name] = prop.value + return tosca_props + + @staticmethod + 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": + return operations + + while True: + type_operations = HotResource._get_interface_operations_from_type( + node_type, node, 'Standard') + type_operations.update(operations) + operations = type_operations + + if node_type.parent_type is not None: + node_type = node_type.parent_type + else: + return operations + + @staticmethod + def _get_interface_operations_from_type(node_type, node, lifecycle_name): + operations = {} + if isinstance(node_type, str) or \ + node_type.type == "tosca.policies.Placement": + return operations + if node_type.interfaces and lifecycle_name in node_type.interfaces: + for name, elems in node_type.interfaces[lifecycle_name].items(): + # ignore empty operations (only type) + # ignore global interface inputs, + # concrete inputs are on the operations themselves + if name != 'type' and name != 'inputs': + operations[name] = InterfacesDef(node_type, + lifecycle_name, + node, name, elems) + return operations + + @staticmethod + def get_base_type(node_type): + if node_type.parent_type is not None: + if node_type.parent_type.type.endswith('.Root'): + return node_type + else: + return HotResource.get_base_type(node_type.parent_type) + else: + return node_type diff --git a/tosca2heat/heat-translator/translator/hot/syntax/hot_template.py b/tosca2heat/heat-translator/translator/hot/syntax/hot_template.py new file mode 100644 index 0000000..4263c4d --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/syntax/hot_template.py @@ -0,0 +1,83 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from collections import OrderedDict +import logging +import textwrap +from toscaparser.utils.gettextutils import _ +import yaml + +log = logging.getLogger('heat-translator') + + +class HotTemplate(object): + '''Container for full Heat Orchestration template.''' + + SECTIONS = (VERSION, DESCRIPTION, PARAMETER_GROUPS, PARAMETERS, + RESOURCES, OUTPUTS, MAPPINGS) = \ + ('heat_template_version', 'description', 'parameter_groups', + 'parameters', 'resources', 'outputs', '__undefined__') + + VERSIONS = (LATEST,) = ('2013-05-23',) + + def __init__(self): + self.resources = [] + self.outputs = [] + self.parameters = [] + self.description = "" + + def represent_ordereddict(self, dumper, data): + nodes = [] + for key, value in data.items(): + node_key = dumper.represent_data(key) + node_value = dumper.represent_data(value) + nodes.append((node_key, node_value)) + return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', nodes) + + def output_to_yaml(self): + log.debug(_('Converting translated output to yaml format.')) + dict_output = OrderedDict() + # Version + version_string = self.VERSION + ": " + self.LATEST + "\n\n" + + # Description + desc_str = "" + if self.description: + # Wrap the text to a new line if the line exceeds 80 characters. + wrapped_txt = "\n ".join(textwrap.wrap(self.description, 80)) + desc_str = self.DESCRIPTION + ": >\n " + wrapped_txt + "\n\n" + + # Parameters + all_params = OrderedDict() + for parameter in self.parameters: + all_params.update(parameter.get_dict_output()) + dict_output.update({self.PARAMETERS: all_params}) + + # Resources + all_resources = OrderedDict() + for resource in self.resources: + if not resource.hide_resource: + all_resources.update(resource.get_dict_output()) + dict_output.update({self.RESOURCES: all_resources}) + + # Outputs + all_outputs = OrderedDict() + for output in self.outputs: + all_outputs.update(output.get_dict_output()) + dict_output.update({self.OUTPUTS: all_outputs}) + + yaml.add_representer(OrderedDict, 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('\'', '') + return version_string + desc_str + yaml_string diff --git a/tosca2heat/heat-translator/translator/hot/tests/__init__.py b/tosca2heat/heat-translator/translator/hot/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tests/__init__.py diff --git a/tosca2heat/heat-translator/translator/hot/tests/test_hot_parameter.py b/tosca2heat/heat-translator/translator/hot/tests/test_hot_parameter.py new file mode 100644 index 0000000..8d3f535 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tests/test_hot_parameter.py @@ -0,0 +1,44 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from collections import OrderedDict + +from toscaparser.tests.base import TestCase +from translator.hot.syntax.hot_parameter import CONSTRAINTS +from translator.hot.syntax.hot_parameter import DEFAULT +from translator.hot.syntax.hot_parameter import DESCRIPTION +from translator.hot.syntax.hot_parameter import HIDDEN +from translator.hot.syntax.hot_parameter import HotParameter +from translator.hot.syntax.hot_parameter import LABEL +from translator.hot.syntax.hot_parameter import TYPE + +TEST_CONSTRAINTS = {'equal': 'allowed_values', 'greater_than': 'range'} + + +class HotParameterTest(TestCase): + + # This test ensures the variables set during the creation of a HotParameter + # object are returned in an OrderedDict when calling get_dict_output(). + def test_dict_output(self): + name = 'HotParameterTest' + hot_parameter = HotParameter(name, 'Type', + label='Label', + description='Description', + default='Default', + hidden=True, + constraints=TEST_CONSTRAINTS) + expected_dict = OrderedDict([(TYPE, 'Type'), (LABEL, 'Label'), + (DESCRIPTION, 'Description'), + (DEFAULT, 'Default'), (HIDDEN, True), + (CONSTRAINTS, TEST_CONSTRAINTS)]) + + self.assertEqual(hot_parameter.get_dict_output()[name], expected_dict) diff --git a/tosca2heat/heat-translator/translator/hot/tests/test_translate_inputs.py b/tosca2heat/heat-translator/translator/hot/tests/test_translate_inputs.py new file mode 100644 index 0000000..2b302ab --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tests/test_translate_inputs.py @@ -0,0 +1,351 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +from collections import OrderedDict +from toscaparser.parameters import Input +from toscaparser.tests.base import TestCase +from toscaparser.utils.gettextutils import _ +import toscaparser.utils.yamlparser +from translator.common.utils import CompareUtils +from translator.hot.translate_inputs import TranslateInputs + + +class ToscaTemplateInputValidationTest(TestCase): + + def _translate_input_test(self, tpl_snippet, input_params, + expectedmessage=None, + expected_hot_params=None): + inputs_dict = (toscaparser.utils.yamlparser. + simple_parse(tpl_snippet)['inputs']) + inputs = [] + for name, attrs in inputs_dict.items(): + input = Input(name, attrs) + inputs.append(input) + + translateinput = TranslateInputs(inputs, input_params) + try: + resulted_hot_params = translateinput.translate() + if expected_hot_params: + self._compare_hot_params(resulted_hot_params, + expected_hot_params) + except Exception as err: + self.assertEqual(expectedmessage, err.__str__()) + + def _compare_hot_params(self, resulted_hot_params, + expected_hot_params): + for expected_param in expected_hot_params: + for resulted_param_obj in resulted_hot_params: + resulted_param = resulted_param_obj.get_dict_output() + result = CompareUtils.compare_dicts(expected_param, + resulted_param) + if not result: + raise Exception(_("hot input and resulted input " + "params are not equal.")) + + def test_invalid_input_type(self): + tpl_snippet = ''' + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + ''' + + input_params = {'cpus': '0.3'} + expectedmessage = _('"0.3" is not an integer.') + self._translate_input_test(tpl_snippet, input_params, + expectedmessage) + + def test_invalid_input_constraints_for_equal(self): + tpl_snippet = ''' + inputs: + num_cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - equal: 1 + ''' + + input_params = {'num_cpus': '0'} + expectedmessage = _('The value "0" of property "num_cpus" is not ' + 'equal to "1".') + self._translate_input_test(tpl_snippet, input_params, expectedmessage) + + def test_invalid_input_constraints_for_greater_or_equal(self): + tpl_snippet = ''' + inputs: + num_cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - greater_or_equal: 1 + ''' + + input_params = {'num_cpus': '0'} + expectedmessage = _('The value "0" of property "num_cpus" must be ' + 'greater than or equal to "1".') + self._translate_input_test(tpl_snippet, input_params, expectedmessage) + + def test_invalid_input_constraints_for_greater_than(self): + tpl_snippet = ''' + inputs: + num_cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - greater_than: 1 + ''' + + input_params = {'num_cpus': '0'} + expectedmessage = _('The value "0" of property "num_cpus" must be ' + 'greater than "1".') + self._translate_input_test(tpl_snippet, input_params, expectedmessage) + + def test_invalid_input_constraints_for_less_than(self): + tpl_snippet = ''' + inputs: + num_cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - less_than: 8 + ''' + + input_params = {'num_cpus': '8'} + expectedmessage = _('The value "8" of property "num_cpus" must be ' + 'less than "8".') + self._translate_input_test(tpl_snippet, input_params, expectedmessage) + + def test_invalid_input_constraints_for_less_or_equal(self): + tpl_snippet = ''' + inputs: + num_cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - less_or_equal: 8 + ''' + + input_params = {'num_cpus': '9'} + expectedmessage = _('The value "9" of property "num_cpus" must be ' + 'less than or equal to "8".') + self._translate_input_test(tpl_snippet, input_params, expectedmessage) + + def test_invalid_input_constraints_for_valid_values(self): + tpl_snippet = ''' + inputs: + num_cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + ''' + + input_params = {'num_cpus': '3'} + expectedmessage = _('The value "3" of property "num_cpus" is not ' + 'valid. Expected a value from "[1, 2, 4, 8]".') + self._translate_input_test(tpl_snippet, input_params, expectedmessage) + + def test_invalid_input_constraints_for_in_range(self): + tpl_snippet = ''' + inputs: + num_cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - in_range: [ 1, 8 ] + ''' + + input_params = {'num_cpus': '10'} + expectedmessage = _('The value "10" of property "num_cpus" is out of ' + 'range "(min:1, max:8)".') + self._translate_input_test(tpl_snippet, input_params, expectedmessage) + + def test_invalid_input_constraints_for_min_length(self): + tpl_snippet = ''' + inputs: + user_name: + type: string + description: Name of the user. + constraints: + - min_length: 8 + ''' + + input_params = {'user_name': 'abcd'} + expectedmessage = _('Length of value "abcd" of property "user_name" ' + 'must be at least "8".') + self._translate_input_test(tpl_snippet, input_params, expectedmessage) + + def test_invalid_input_constraints_for_max_length(self): + tpl_snippet = ''' + inputs: + user_name: + type: string + description: Name of the user. + constraints: + - max_length: 6 + ''' + + input_params = {'user_name': 'abcdefg'} + expectedmessage = _('Length of value "abcdefg" of property ' + '"user_name" must be no greater than "6".') + self._translate_input_test(tpl_snippet, input_params, expectedmessage) + + def test_invalid_input_constraints_for_pattern(self): + tpl_snippet = ''' + inputs: + user_name: + type: string + description: Name of the user. + constraints: + - pattern: '^\w+$' + ''' + + input_params = {'user_name': '1-abc'} + expectedmessage = _('The value "1-abc" of property "user_name" does ' + 'not match pattern "^\\w+$".') + self._translate_input_test(tpl_snippet, input_params, expectedmessage) + + def test_valid_input_storage_size(self): + tpl_snippet = ''' + inputs: + storage_size: + type: scalar-unit.size + description: size of the storage volume. + ''' + + expectedmessage = _('both equal.') + input_params = {'storage_size': '2 GB'} + expected_hot_params = [{'storage_size': + OrderedDict([('type', 'number'), + ('description', + 'size of the storage volume.'), + ('default', 2)])}] + self._translate_input_test(tpl_snippet, input_params, + expectedmessage, expected_hot_params) + + """ TOSCA 2000 MB => 2 GB HOT conversion""" + input_params = {'storage_size': '2000 MB'} + expected_hot_params = [{'storage_size': + OrderedDict([('type', 'number'), + ('description', + 'size of the storage volume.'), + ('default', 2)])}] + self._translate_input_test(tpl_snippet, input_params, + expectedmessage, expected_hot_params) + + """ TOSCA 2048 MB => 2 GB HOT conversion""" + input_params = {'storage_size': '2048 MB'} + expected_hot_params = [{'storage_size': + OrderedDict([('type', 'number'), + ('description', + 'size of the storage volume.'), + ('default', 2)])}] + self._translate_input_test(tpl_snippet, input_params, + expectedmessage, expected_hot_params) + + """ TOSCA 2 MB => 1 GB HOT conversion""" + input_params = {'storage_size': '2 MB'} + expected_hot_params = [{'storage_size': + OrderedDict([('type', 'number'), + ('description', + 'size of the storage volume.'), + ('default', 1)])}] + self._translate_input_test(tpl_snippet, input_params, + expectedmessage, expected_hot_params) + + """ TOSCA 1024 MB => 1 GB HOT conversion""" + input_params = {'storage_size': '1024 MB'} + expected_hot_params = [{'storage_size': + OrderedDict([('type', 'number'), + ('description', + 'size of the storage volume.'), + ('default', 1)])}] + self._translate_input_test(tpl_snippet, input_params, + expectedmessage, expected_hot_params) + + """ TOSCA 1024 MiB => 1 GB HOT conversion""" + input_params = {'storage_size': '1024 MiB'} + expected_hot_params = [{'storage_size': + OrderedDict([('type', 'number'), + ('description', + 'size of the storage volume.'), + ('default', 1)])}] + self._translate_input_test(tpl_snippet, input_params, + expectedmessage, expected_hot_params) + + def test_invalid_input_storage_size(self): + tpl_snippet = ''' + inputs: + storage_size: + type: scalar-unit.size + description: size of the storage volume. + ''' + + input_params = {'storage_size': '0 MB'} + expectedmsg = _("Unit value should be > 0.") + self._translate_input_test(tpl_snippet, input_params, expectedmsg) + + input_params = {'storage_size': '-2 MB'} + expectedmsg = _('"-2 MB" is not a valid scalar-unit.') + self._translate_input_test(tpl_snippet, input_params, expectedmsg) + + def test_invalid_input_type_version(self): + tpl_snippet = ''' + inputs: + version: + type: version + ''' + + input_params = {'version': '0.a'} + expectedmessage = _('Value of TOSCA version property ' + '"0.a" is invalid.') + self._translate_input_test(tpl_snippet, input_params, + expectedmessage) + + input_params = {'version': '0.0.0.abc'} + expectedmessage = _('Value of TOSCA version property ' + '"0.0.0.abc" is invalid.') + self._translate_input_test(tpl_snippet, input_params, + expectedmessage) + + def test_valid_input_type_version(self): + tpl_snippet = ''' + inputs: + version: + type: version + default: 12 + ''' + + expectedmessage = _('both equal.') + input_params = {'version': '18'} + expected_hot_params = [{'version': + OrderedDict([('type', 'string'), + ('default', '18.0')])}] + self._translate_input_test(tpl_snippet, input_params, expectedmessage, + expected_hot_params) + + input_params = {'version': '18.0'} + expected_hot_params = [{'version': + OrderedDict([('type', 'string'), + ('default', '18.0')])}] + self._translate_input_test(tpl_snippet, input_params, expectedmessage, + expected_hot_params) + + input_params = {'version': '18.0.1'} + expected_hot_params = [{'version': + OrderedDict([('type', 'string'), + ('default', '18.0.1')])}] + self._translate_input_test(tpl_snippet, input_params, expectedmessage, + expected_hot_params) diff --git a/tosca2heat/heat-translator/translator/hot/tests/test_translate_outputs.py b/tosca2heat/heat-translator/translator/hot/tests/test_translate_outputs.py new file mode 100644 index 0000000..955150e --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tests/test_translate_outputs.py @@ -0,0 +1,50 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +from toscaparser.tests.base import TestCase +from toscaparser.tosca_template import ToscaTemplate +import toscaparser.utils.yamlparser +from translator.hot.tosca_translator import TOSCATranslator + + +class ToscaTemplateOutputTest(TestCase): + + def test_translate_output(self): + tosca_tpl = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "../../tests/data/" + "tosca_nodejs_mongodb_two_instances.yaml") + tosca = ToscaTemplate(tosca_tpl) + translate = TOSCATranslator(tosca, []) + hot_translation = translate.translate() + + expected_output = {'nodejs_url': + {'description': 'URL for the nodejs ' + 'server, http://<IP>:3000', + 'value': + {'get_attr': + ['app_server', 'networks', 'private', 0]}}, + 'mongodb_url': + {'description': 'URL for the mongodb server.', + 'value': + {'get_attr': + ['mongo_server', 'networks', 'private', 0]}}} + + hot_translation_dict = \ + toscaparser.utils.yamlparser.simple_parse(hot_translation) + + outputs = hot_translation_dict.get('outputs') + for resource_name in outputs: + translated_value = outputs.get(resource_name) + expected_value = expected_output.get(resource_name) + self.assertEqual(translated_value, expected_value) diff --git a/tosca2heat/heat-translator/translator/hot/tosca/__init__.py b/tosca2heat/heat-translator/translator/hot/tosca/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/__init__.py diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tests/__init__.py b/tosca2heat/heat-translator/translator/hot/tosca/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tests/__init__.py diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_blockstorage.py b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_blockstorage.py new file mode 100644 index 0000000..d4fffe1 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_blockstorage.py @@ -0,0 +1,84 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from toscaparser.common.exception import InvalidPropertyValueError +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_block_storage import ToscaBlockStorage + + +class ToscaBlockStoreTest(TestCase): + + def _tosca_blockstore_test(self, tpl_snippet, expectedprops): + nodetemplates = (toscaparser.utils.yamlparser. + simple_parse(tpl_snippet)['node_templates']) + name = list(nodetemplates.keys())[0] + try: + nodetemplate = NodeTemplate(name, nodetemplates) + tosca_block_store = ToscaBlockStorage(nodetemplate) + tosca_block_store.handle_properties() + if not self._compare_properties(tosca_block_store.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 + raise + + def _compare_properties(self, hotprops, expectedprops): + return all(item in hotprops.items() for item in expectedprops.items()) + + def test_node_blockstorage_with_properties(self): + tpl_snippet = ''' + node_templates: + my_storage: + type: tosca.nodes.BlockStorage + properties: + size: 1024 MiB + snapshot_id: abc + ''' + expectedprops = {'snapshot_id': 'abc', + 'size': 1} + self._tosca_blockstore_test( + tpl_snippet, + expectedprops) + + tpl_snippet = ''' + node_templates: + my_storage: + type: tosca.nodes.BlockStorage + properties: + size: 124 MB + snapshot_id: abc + ''' + expectedprops = {'snapshot_id': 'abc', + 'size': 1} + self._tosca_blockstore_test( + tpl_snippet, + expectedprops) + + def test_node_blockstorage_with_invalid_size_property(self): + tpl_snippet = ''' + node_templates: + my_storage: + type: tosca.nodes.BlockStorage + properties: + size: 0 MB + snapshot_id: abc + ''' + expectedprops = {} + self.assertRaises(InvalidPropertyValueError, + lambda: self._tosca_blockstore_test(tpl_snippet, + expectedprops)) 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 new file mode 100644 index 0000000..e0cdbb6 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_compute.py @@ -0,0 +1,286 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json +import mock +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 + + +class ToscaComputeTest(TestCase): + + def _tosca_compute_test(self, tpl_snippet, expectedprops): + 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 + + def _compare_properties(self, hotprops, expectedprops): + return all(item in hotprops.items() for item in expectedprops.items()) + + def test_node_compute_with_host_and_os_capabilities(self): + tpl_snippet = ''' + node_templates: + server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: 4 + mem_size: 4 GB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Fedora + version: 18.0 + ''' + expectedprops = {'flavor': 'm1.large', + 'image': 'fedora-amd64-heat-config'} + self._tosca_compute_test( + tpl_snippet, + expectedprops) + + def test_node_compute_without_os_capabilities(self): + tpl_snippet = ''' + node_templates: + server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: 4 + mem_size: 4 GB + #left intentionally + ''' + expectedprops = {'flavor': 'm1.large', + 'image': None} + self._tosca_compute_test( + tpl_snippet, + expectedprops) + + def test_node_compute_without_host_capabilities(self): + tpl_snippet = ''' + node_templates: + server: + type: tosca.nodes.Compute + capabilities: + os: + properties: + architecture: x86_64 + type: Linux + distribution: Fedora + version: 18.0 + ''' + expectedprops = {'flavor': None, + 'image': 'fedora-amd64-heat-config'} + self._tosca_compute_test( + tpl_snippet, + expectedprops) + + def test_node_compute_without_properties_and_os_capabilities(self): + tpl_snippet = ''' + node_templates: + server: + type: tosca.nodes.Compute + properties: + #left intentionally + capabilities: + #left intentionally + ''' + expectedprops = {'flavor': None, + 'image': None} + self._tosca_compute_test( + tpl_snippet, + expectedprops) + + def test_node_compute_with_only_type(self): + tpl_snippet = ''' + node_templates: + server: + type: tosca.nodes.Compute + ''' + expectedprops = {'flavor': None, + 'image': None} + self._tosca_compute_test( + tpl_snippet, + expectedprops) + + def test_node_compute_host_capabilities_without_properties(self): + tpl_snippet = ''' + node_templates: + server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + #left intentionally + ''' + expectedprops = {'flavor': 'm1.nano'} + self._tosca_compute_test( + tpl_snippet, + expectedprops) + + def test_node_compute_host_capabilities_without_disk_size(self): + tpl_snippet = ''' + node_templates: + server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + num_cpus: 4 + mem_size: 4 GB + ''' + expectedprops = {'flavor': 'm1.large'} + self._tosca_compute_test( + tpl_snippet, + expectedprops) + + def test_node_compute_host_capabilities_without_mem_size(self): + tpl_snippet = ''' + node_templates: + server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + num_cpus: 4 + disk_size: 10 GB + ''' + expectedprops = {'flavor': 'm1.large'} + self._tosca_compute_test( + tpl_snippet, + expectedprops) + + def test_node_compute_host_capabilities_without_mem_size_disk_size(self): + tpl_snippet = ''' + node_templates: + server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + num_cpus: 4 + ''' + expectedprops = {'flavor': 'm1.large'} + self._tosca_compute_test( + tpl_snippet, + expectedprops) + + @patch('requests.post') + @patch('requests.get') + @patch('os.getenv') + def test_node_compute_with_nova_flavor(self, mock_os_getenv, + mock_get, mock_post): + tpl_snippet = ''' + node_templates: + server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + num_cpus: 1 + disk_size: 1 GB + mem_size: 1 GB + ''' + with patch('translator.common.utils.' + 'check_for_env_variables') as mock_check_env: + mock_check_env.return_value = True + mock_os_getenv.side_effect = ['demo', 'demo', + 'demo', 'http://abc.com/5000/', + 'demo', 'demo', + 'demo', 'http://abc.com/5000/'] + mock_ks_response = mock.MagicMock() + mock_ks_response.status_code = 200 + mock_ks_content = { + 'access': { + 'token': { + 'id': 'd1dfa603-3662-47e0-b0b6-3ae7914bdf76' + }, + 'serviceCatalog': [{ + 'type': 'compute', + 'endpoints': [{ + 'publicURL': 'http://abc.com' + }] + }] + } + } + mock_ks_response.content = json.dumps(mock_ks_content) + mock_nova_response = mock.MagicMock() + mock_nova_response.status_code = 200 + mock_flavor_content = { + 'flavors': [{ + 'name': 'm1.mock_flavor', + 'ram': 1024, + 'disk': 1, + 'vcpus': 1 + }] + } + mock_nova_response.content = \ + json.dumps(mock_flavor_content) + mock_post.return_value = mock_ks_response + mock_get.return_value = mock_nova_response + expectedprops = {'flavor': 'm1.mock_flavor'} + self._tosca_compute_test( + tpl_snippet, + expectedprops) + + @patch('requests.post') + @patch('requests.get') + @patch('os.getenv') + def test_node_compute_without_nova_flavor(self, mock_os_getenv, + mock_get, mock_post): + tpl_snippet = ''' + node_templates: + server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + num_cpus: 1 + disk_size: 1 GB + mem_size: 1 GB + ''' + with patch('translator.common.utils.' + 'check_for_env_variables') as mock_check_env: + mock_check_env.return_value = True + mock_os_getenv.side_effect = ['demo', 'demo', + 'demo', 'http://abc.com/5000/'] + mock_ks_response = mock.MagicMock() + mock_ks_content = {} + mock_ks_response.content = json.dumps(mock_ks_content) + expectedprops = {'flavor': 'm1.small', + 'user_data_format': 'SOFTWARE_CONFIG', + 'image': None} + self._tosca_compute_test( + tpl_snippet, + expectedprops) diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_objectstore.py b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_objectstore.py new file mode 100644 index 0000000..4c42794 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_objectstore.py @@ -0,0 +1,71 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +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_object_storage import ToscaObjectStorage + + +class ToscaObjectStoreTest(TestCase): + + def _tosca_objectstore_test(self, tpl_snippet, expectedprops): + nodetemplates = (toscaparser.utils.yamlparser. + simple_parse(tpl_snippet)['node_templates']) + name = list(nodetemplates.keys())[0] + try: + nodetemplate = NodeTemplate(name, nodetemplates) + tosca_object_store = ToscaObjectStorage(nodetemplate) + tosca_object_store.handle_properties() + if not self._compare_properties(tosca_object_store.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 + raise + + def _compare_properties(self, hotprops, expectedprops): + return all(item in hotprops.items() for item in expectedprops.items()) + + def test_node_objectstorage_with_properties(self): + tpl_snippet = ''' + node_templates: + server: + type: tosca.nodes.ObjectStorage + properties: + name: test + size: 1024 KB + maxsize: 1 MB + ''' + expectedprops = {'name': 'test', + 'X-Container-Meta': {'Quota-Bytes': 1000000}} + self._tosca_objectstore_test( + tpl_snippet, + expectedprops) + + def test_node_objectstorage_with_few_properties(self): + tpl_snippet = ''' + node_templates: + server: + type: tosca.nodes.ObjectStorage + properties: + name: test + size: 1024 B + ''' + expectedprops = {'name': 'test', + 'X-Container-Meta': {'Quota-Bytes': 1024}} + self._tosca_objectstore_test( + tpl_snippet, + expectedprops) diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_policies.py b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_policies.py new file mode 100644 index 0000000..24368ab --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tests/test_tosca_policies.py @@ -0,0 +1,80 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from toscaparser.nodetemplate import NodeTemplate +from toscaparser.policy import Policy +from toscaparser.tests.base import TestCase +import toscaparser.utils.yamlparser +from translator.hot.tosca.tosca_compute import ToscaCompute +from translator.hot.tosca.tosca_policies import ToscaPolicies + + +class ToscaPoicyTest(TestCase): + + def _tosca_policy_test(self, tpl_snippet, expectedprops): + nodetemplates = (toscaparser.utils.yamlparser. + simple_parse(tpl_snippet)['node_templates']) + policies = (toscaparser.utils.yamlparser. + simple_parse(tpl_snippet)['policies']) + name = list(nodetemplates.keys())[0] + policy_name = list(policies[0].keys())[0] + for policy in policies: + tpl = policy[policy_name] + targets = tpl["targets"] + try: + nodetemplate = NodeTemplate(name, nodetemplates) + toscacompute = ToscaCompute(nodetemplate) + toscacompute.handle_properties() + + policy = Policy(policy_name, tpl, targets, + "node_templates") + toscapolicy = ToscaPolicies(policy) + nodetemplate = [toscacompute] + toscapolicy.handle_properties(nodetemplate) + + self.assertEqual(toscacompute.properties, expectedprops) + except Exception: + raise + + def test_compute_with_policies(self): + tpl_snippet = ''' + node_templates: + server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: 4 + mem_size: 4 GB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Fedora + version: 18.0 + policies: + - my_compute_placement_policy: + type: tosca.policies.Placement + description: Apply my placement policy to my application servers + targets: [ server ] + ''' + expectedprops = {'flavor': 'm1.large', + 'image': 'fedora-amd64-heat-config', + 'scheduler_hints': { + 'group': { + 'get_resource': + 'my_compute_placement_policy'}}, + 'user_data_format': 'SOFTWARE_CONFIG'} + self._tosca_policy_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 new file mode 100644 index 0000000..d4b2f44 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage.py @@ -0,0 +1,71 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging +from toscaparser.common.exception import InvalidPropertyValueError +from toscaparser.elements.scalarunit import ScalarUnit_Size +from toscaparser.functions import GetInput +from toscaparser.utils.gettextutils import _ +from translator.hot.syntax.hot_resource import HotResource + +log = logging.getLogger('heat-translator') + +# Name used to dynamically load appropriate map class. +TARGET_CLASS_NAME = 'ToscaBlockStorage' + + +class ToscaBlockStorage(HotResource): + '''Translate TOSCA node type tosca.nodes.BlockStorage.''' + + toscatype = 'tosca.nodes.BlockStorage' + + def __init__(self, nodetemplate): + super(ToscaBlockStorage, self).__init__(nodetemplate, + type='OS::Cinder::Volume') + pass + + def handle_properties(self): + tosca_props = {} + for prop in self.nodetemplate.get_properties_objects(): + if isinstance(prop.value, GetInput): + tosca_props[prop.name] = {'get_param': prop.value.input_name} + else: + if prop.name == "size": + size_value = (ScalarUnit_Size(prop.value). + get_num_from_scalar_unit('GiB')) + if size_value == 0: + # OpenStack Heat expects size in GB + msg = _('Cinder Volume Size unit should be in GB.') + log.error(msg) + raise InvalidPropertyValueError( + what=msg) + elif int(size_value) < size_value: + size_value = int(size_value) + 1 + log.warning(_("Cinder unit value should be in " + "multiples of GBs. so corrected " + " %(prop_val)s to %(size_value)s GB.") + % {'prop_val': prop.value, + 'size_value': size_value}) + tosca_props[prop.name] = int(size_value) + else: + tosca_props[prop.name] = prop.value + self.properties = tosca_props + + def get_hot_attribute(self, attribute, args): + attr = {} + # Convert from a TOSCA attribute for a nodetemplate to a HOT + # 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] + 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 new file mode 100644 index 0000000..715d5b3 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_block_storage_attachment.py @@ -0,0 +1,48 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from toscaparser.functions import GetInput +from translator.hot.syntax.hot_resource import HotResource + +# Name used to dynamically load appropriate map class. +TARGET_CLASS_NAME = 'ToscaBlockStorageAttachment' + + +class ToscaBlockStorageAttachment(HotResource): + '''Translate TOSCA relationship AttachesTo for Compute and BlockStorage.''' + + toscatype = 'tosca.nodes.BlockStorageAttachment' + + def __init__(self, template, nodetemplates, instance_uuid, volume_id): + super(ToscaBlockStorageAttachment, + self).__init__(template, type='OS::Cinder::VolumeAttachment') + self.nodetemplates = nodetemplates + self.instance_uuid = {'get_resource': instance_uuid} + self.volume_id = {'get_resource': volume_id} + + def handle_properties(self): + tosca_props = {} + for prop in self.nodetemplate.get_properties_objects(): + if isinstance(prop.value, GetInput): + tosca_props[prop.name] = {'get_param': prop.value.input_name} + else: + tosca_props[prop.name] = prop.value + self.properties = tosca_props + # instance_uuid and volume_id for Cinder volume attachment + self.properties['instance_uuid'] = self.instance_uuid + self.properties['volume_id'] = self.volume_id + if 'location' in self.properties: + self.properties['mountpoint'] = self.properties.pop('location') + + def handle_life_cycle(self): + pass diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py new file mode 100644 index 0000000..e2ac130 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_compute.py @@ -0,0 +1,280 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json +import logging +import requests + +from toscaparser.utils.gettextutils import _ +import translator.common.utils +from translator.hot.syntax.hot_resource import HotResource + +log = logging.getLogger('heat-translator') + + +# Name used to dynamically load appropriate map class. +TARGET_CLASS_NAME = 'ToscaCompute' + +# A design issue to be resolved is how to translate the generic TOSCA server +# properties to OpenStack flavors and images. At the Atlanta design summit, +# there was discussion on using Glance to store metadata and Graffiti to +# describe artifacts. We will follow these projects to see if they can be +# leveraged for this TOSCA translation. +# For development purpose at this time, we temporarily hardcode a list of +# flavors and images here +FLAVORS = {'m1.xlarge': {'mem_size': 16384, 'disk_size': 160, 'num_cpus': 8}, + 'm1.large': {'mem_size': 8192, 'disk_size': 80, 'num_cpus': 4}, + 'm1.medium': {'mem_size': 4096, 'disk_size': 40, 'num_cpus': 2}, + 'm1.small': {'mem_size': 2048, 'disk_size': 20, 'num_cpus': 1}, + 'm1.tiny': {'mem_size': 512, 'disk_size': 1, 'num_cpus': 1}, + 'm1.micro': {'mem_size': 128, 'disk_size': 0, 'num_cpus': 1}, + 'm1.nano': {'mem_size': 64, 'disk_size': 0, 'num_cpus': 1}} + +IMAGES = {'ubuntu-software-config-os-init': {'architecture': 'x86_64', + 'type': 'Linux', + 'distribution': 'Ubuntu', + 'version': '14.04'}, + 'ubuntu-12.04-software-config-os-init': {'architecture': 'x86_64', + 'type': 'Linux', + 'distribution': 'Ubuntu', + 'version': '12.04'}, + 'fedora-amd64-heat-config': {'architecture': 'x86_64', + 'type': 'Linux', + 'distribution': 'Fedora', + 'version': '18.0'}, + 'F18-x86_64-cfntools': {'architecture': 'x86_64', + 'type': 'Linux', + 'distribution': 'Fedora', + 'version': '19'}, + 'Fedora-x86_64-20-20131211.1-sda': {'architecture': 'x86_64', + 'type': 'Linux', + 'distribution': 'Fedora', + 'version': '20'}, + 'cirros-0.3.1-x86_64-uec': {'architecture': 'x86_64', + 'type': 'Linux', + 'distribution': 'CirrOS', + 'version': '0.3.1'}, + 'cirros-0.3.2-x86_64-uec': {'architecture': 'x86_64', + 'type': 'Linux', + 'distribution': 'CirrOS', + 'version': '0.3.2'}, + 'rhel-6.5-test-image': {'architecture': 'x86_64', + 'type': 'Linux', + 'distribution': 'RHEL', + 'version': '6.5'}} + + +class ToscaCompute(HotResource): + '''Translate TOSCA node type tosca.nodes.Compute.''' + + COMPUTE_HOST_PROP = (DISK_SIZE, MEM_SIZE, NUM_CPUS) = \ + ('disk_size', 'mem_size', 'num_cpus') + + COMPUTE_OS_PROP = (ARCHITECTURE, DISTRIBUTION, TYPE, VERSION) = \ + ('architecture', 'distribution', 'type', 'version') + toscatype = 'tosca.nodes.Compute' + + def __init__(self, nodetemplate): + super(ToscaCompute, self).__init__(nodetemplate, + type='OS::Nova::Server') + # List with associated hot port resources with this server + self.assoc_port_resources = [] + pass + + def handle_properties(self): + self.properties = self.translate_compute_flavor_and_image( + self.nodetemplate.get_capability('host'), + self.nodetemplate.get_capability('os')) + self.properties['user_data_format'] = 'SOFTWARE_CONFIG' + tosca_props = self.get_tosca_props() + for key, value in tosca_props.items(): + self.properties[key] = value + + # To be reorganized later based on new development in Glance and Graffiti + def translate_compute_flavor_and_image(self, + host_capability, + os_capability): + hot_properties = {} + host_cap_props = {} + os_cap_props = {} + image = None + flavor = None + 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 os_capability: + for prop in os_capability.get_properties_objects(): + os_cap_props[prop.name] = prop.value + image = self._best_image(os_cap_props) + hot_properties['flavor'] = flavor + hot_properties['image'] = image + return hot_properties + + def _create_nova_flavor_dict(self): + '''Populates and returns the flavors dict using Nova ReST API''' + 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 + nova_url = translator.common.utils.get_url_for(access_dict, + 'compute') + if not nova_url: + return None + nova_response = requests.get(nova_url + '/flavors/detail', + headers={'X-Auth-Token': + access_token}) + if nova_response.status_code != 200: + return None + flavors = json.loads(nova_response.content)['flavors'] + flavor_dict = dict() + for flavor in flavors: + flavor_name = str(flavor['name']) + flavor_dict[flavor_name] = { + 'mem_size': flavor['ram'], + 'disk_size': flavor['disk'], + 'num_cpus': flavor['vcpus'], + } + except Exception as e: + # Handles any exception coming from openstack + log.warn(_('Choosing predefined flavors since received ' + 'Openstack Exception: %s') % str(e)) + return None + return flavor_dict + + def _best_flavor(self, properties): + log.info(_('Choosing the best flavor for given attributes.')) + # Check whether user exported all required environment variables. + flavors = FLAVORS + if translator.common.utils.check_for_env_variables(): + resp = self._create_nova_flavor_dict() + if resp: + flavors = resp + + # start with all flavors + match_all = flavors.keys() + + # TODO(anyone): Handle the case where the value contains something like + # get_input instead of a value. + # flavors that fit the CPU count + cpu = properties.get(self.NUM_CPUS) + if cpu is None: + self._log_compute_msg(self.NUM_CPUS, 'flavor') + match_cpu = self._match_flavors(match_all, flavors, self.NUM_CPUS, cpu) + + # flavors that fit the mem size + mem = properties.get(self.MEM_SIZE) + if mem: + mem = translator.common.utils.MemoryUnit.convert_unit_size_to_num( + mem, 'MB') + else: + self._log_compute_msg(self.MEM_SIZE, 'flavor') + match_cpu_mem = self._match_flavors(match_cpu, flavors, + self.MEM_SIZE, mem) + # flavors that fit the disk size + disk = properties.get(self.DISK_SIZE) + if disk: + disk = translator.common.utils.MemoryUnit.\ + convert_unit_size_to_num(disk, 'GB') + else: + self._log_compute_msg(self.DISK_SIZE, 'flavor') + match_cpu_mem_disk = self._match_flavors(match_cpu_mem, flavors, + self.DISK_SIZE, disk) + # if multiple match, pick the flavor with the least memory + # the selection can be based on other heuristic, e.g. pick one with the + # least total resource + if len(match_cpu_mem_disk) > 1: + return self._least_flavor(match_cpu_mem_disk, flavors, 'mem_size') + elif len(match_cpu_mem_disk) == 1: + return match_cpu_mem_disk[0] + else: + return None + + def _best_image(self, properties): + 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, + 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) + distribution = properties.get(self.DISTRIBUTION) + if distribution is None: + self._log_compute_msg(self.DISTRIBUTION, 'image') + 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, + self.VERSION, version) + + if len(match_version): + return list(match_version)[0] + + def _match_flavors(self, this_list, this_dict, attr, size): + '''Return from this list all flavors matching the attribute size.''' + if not size: + return list(this_list) + matching_flavors = [] + for flavor in this_list: + if isinstance(size, int): + if this_dict[flavor][attr] >= size: + matching_flavors.append(flavor) + log.debug(_('Returning list of flavors matching the attribute size.')) + return matching_flavors + + def _least_flavor(self, this_list, this_dict, attr): + '''Return from this list the flavor with the smallest attr.''' + least_flavor = this_list[0] + for flavor in this_list: + if this_dict[flavor][attr] < this_dict[least_flavor][attr]: + least_flavor = flavor + return least_flavor + + def _match_images(self, this_list, this_dict, attr, prop): + if not prop: + return this_list + matching_images = [] + for image in this_list: + if this_dict[image][attr].lower() == str(prop).lower(): + matching_images.append(image) + return matching_images + + def get_hot_attribute(self, attribute, args): + attr = {} + # Convert from a TOSCA attribute for a nodetemplate to a HOT + # attribute for the matching resource. Unless there is additional + # runtime support, this should be a one to one mapping. + + # Note: We treat private and public IP addresses equally, but + # this will change in the future when TOSCA starts to support + # multiple private/public IP addresses. + log.debug(_('Converting TOSCA attribute for a nodetemplate to a HOT \ + attriute.')) + if attribute == 'private_address' or \ + attribute == 'public_address': + attr['get_attr'] = [self.name, 'networks', 'private', 0] + + return attr + + def _log_compute_msg(self, prop, what): + msg = _('No value is provided for Compute capability ' + 'property "%(prop)s". This may set an undesired "%(what)s" ' + 'in the template.') % {'prop': prop, 'what': what} + log.warn(msg) diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_database.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_database.py new file mode 100644 index 0000000..26c9d4d --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_database.py @@ -0,0 +1,30 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from translator.hot.syntax.hot_resource import HotResource + +# Name used to dynamically load appropriate map class. +TARGET_CLASS_NAME = 'ToscaDatabase' + + +class ToscaDatabase(HotResource): + '''Translate TOSCA node type tosca.nodes.Database.''' + + toscatype = 'tosca.nodes.Database' + + def __init__(self, nodetemplate): + super(ToscaDatabase, self).__init__(nodetemplate) + pass + + def handle_properties(self): + pass diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_dbms.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_dbms.py new file mode 100644 index 0000000..38c31bd --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_dbms.py @@ -0,0 +1,30 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from translator.hot.syntax.hot_resource import HotResource + +# Name used to dynamically load appropriate map class. +TARGET_CLASS_NAME = 'ToscaDbms' + + +class ToscaDbms(HotResource): + '''Translate TOSCA node type tosca.nodes.DBMS.''' + + toscatype = 'tosca.nodes.DBMS' + + def __init__(self, nodetemplate): + super(ToscaDbms, self).__init__(nodetemplate) + pass + + def handle_properties(self): + pass diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_network_network.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_network_network.py new file mode 100644 index 0000000..2b80313 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_network_network.py @@ -0,0 +1,118 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from toscaparser.common.exception import InvalidPropertyValueError +from translator.hot.syntax.hot_resource import HotResource + +# Name used to dynamically load appropriate map class. +TARGET_CLASS_NAME = 'ToscaNetwork' + + +class ToscaNetwork(HotResource): + '''Translate TOSCA node type tosca.nodes.network.Network.''' + + toscatype = 'tosca.nodes.network.Network' + SUBNET_SUFFIX = '_subnet' + NETWORK_PROPS = ['network_name', 'network_id', 'segmentation_id'] + SUBNET_PROPS = ['ip_version', 'cidr', 'start_ip', 'end_ip', 'gateway_ip'] + + existing_resource_id = None + + def __init__(self, nodetemplate): + super(ToscaNetwork, self).__init__(nodetemplate, + type='OS::Neutron::Net') + pass + + def handle_properties(self): + tosca_props = self.get_tosca_props() + + net_props = {} + for key, value in tosca_props.items(): + if key in self.NETWORK_PROPS: + if key == 'network_name': + # If CIDR is specified network_name should + # be used as the name for the new network. + if 'cidr' in tosca_props.keys(): + net_props['name'] = value + # If CIDR is not specified network_name will be used + # to lookup existing network. If network_id is specified + # together with network_name then network_id should be + # used to lookup the network instead + elif 'network_id' not in tosca_props.keys(): + self.hide_resource = True + self.existing_resource_id = value + break + elif key == 'network_id': + self.hide_resource = True + self.existing_resource_id = value + break + elif key == 'segmentation_id': + net_props['segmentation_id'] = \ + tosca_props['segmentation_id'] + # Hardcode to vxlan for now until we add the network type + # and physical network to the spec. + net_props['value_specs'] = {'provider:segmentation_id': + value, 'provider:network_type': + 'vxlan'} + self.properties = net_props + + def handle_expansion(self): + # If the network resource should not be output (they are hidden), + # there is no need to generate subnet resource + if self.hide_resource: + return + + tosca_props = self.get_tosca_props() + + subnet_props = {} + + ip_pool_start = None + ip_pool_end = None + + for key, value in tosca_props.items(): + if key in self.SUBNET_PROPS: + if key == 'start_ip': + ip_pool_start = value + elif key == 'end_ip': + ip_pool_end = value + elif key == 'dhcp_enabled': + subnet_props['enable_dhcp'] = value + else: + subnet_props[key] = value + + if 'network_id' in tosca_props: + subnet_props['network'] = tosca_props['network_id'] + else: + subnet_props['network'] = '{ get_resource: %s }' % (self.name) + + # Handle allocation pools + # Do this only if both start_ip and end_ip are provided + # If one of them is missing throw an exception. + if ip_pool_start and ip_pool_end: + allocation_pool = {} + allocation_pool['start'] = ip_pool_start + allocation_pool['end'] = ip_pool_end + allocation_pools = [allocation_pool] + subnet_props['allocation_pools'] = allocation_pools + elif ip_pool_start: + raise InvalidPropertyValueError(what=_('start_ip')) + elif ip_pool_end: + raise InvalidPropertyValueError(what=_('end_ip')) + + subnet_resource_name = self.name + self.SUBNET_SUFFIX + + hot_resources = [HotResource(self.nodetemplate, + type='OS::Neutron::Subnet', + name=subnet_resource_name, + properties=subnet_props)] + return hot_resources diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_network_port.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_network_port.py new file mode 100644 index 0000000..4fd2d70 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_network_port.py @@ -0,0 +1,114 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from translator.hot.syntax.hot_resource import HotResource + +# Name used to dynamically load appropriate map class. +TARGET_CLASS_NAME = 'ToscaNetworkPort' +TOSCA_LINKS_TO = 'tosca.relationships.network.LinksTo' +TOSCA_BINDS_TO = 'tosca.relationships.network.BindsTo' + + +class ToscaNetworkPort(HotResource): + '''Translate TOSCA node type tosca.nodes.network.Port.''' + + toscatype = 'tosca.nodes.network.Port' + + def __init__(self, nodetemplate): + super(ToscaNetworkPort, self).__init__(nodetemplate, + type='OS::Neutron::Port') + # Default order + self.order = 0 + pass + + def _generate_networks_for_compute(self, port_resources): + '''Generate compute networks property list from the port resources.''' + networks = [] + for resource in port_resources: + networks.append({'port': '{ get_resource: %s }' % (resource.name)}) + return networks + + def _insert_sorted_resource(self, resources, resource): + '''Insert a resource in the list of resources and keep the order.''' + lo = 0 + hi = len(resources) + while lo < hi: + mid = (lo + hi) // 2 + if resource.order < resources[mid].order: + hi = mid + else: + lo = mid + 1 + resources.insert(lo, resource) + + def handle_properties(self): + tosca_props = self.get_tosca_props() + port_props = {} + for key, value in tosca_props.items(): + if key == 'ip_address': + fixed_ip = {} + fixed_ip['ip_address'] = value + port_props['fixed_ips'] = [fixed_ip] + elif key == 'order': + self.order = value + # TODO(sdmonov): Need to implement the properties below + elif key == 'is_default': + pass + elif key == 'ip_range_start': + pass + elif key == 'ip_range_end': + pass + else: + port_props[key] = value + + links_to = None + binds_to = None + for rel, node in self.nodetemplate.relationships.items(): + # Check for LinksTo relations. If found add a network property with + # the network name into the port + if not links_to and rel.is_derived_from(TOSCA_LINKS_TO): + links_to = node + + network_resource = None + for hot_resource in self.depends_on_nodes: + if links_to.name == hot_resource.name: + network_resource = hot_resource + self.depends_on.remove(hot_resource) + break + + if network_resource.existing_resource_id: + port_props['network'] =\ + str(network_resource.existing_resource_id) + else: + port_props['network'] = '{ get_resource: %s }'\ + % (links_to.name) + + # Check for BindsTo relationship. If found add network to the + # network property of the corresponding compute resource + elif not binds_to and rel.is_derived_from(TOSCA_BINDS_TO): + binds_to = node + compute_resource = None + for hot_resource in self.depends_on_nodes: + if binds_to.name == hot_resource.name: + compute_resource = hot_resource + self.depends_on.remove(hot_resource) + break + if compute_resource: + port_rsrcs = compute_resource.assoc_port_resources + self._insert_sorted_resource(port_rsrcs, self) + # TODO(sdmonov): Using generate networks every time we add + # a network is not the fastest way to do the things. We + # should do this only once at the end. + networks = self._generate_networks_for_compute(port_rsrcs) + compute_resource.properties['networks'] = networks + + self.properties = port_props diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_object_storage.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_object_storage.py new file mode 100644 index 0000000..177503f --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_object_storage.py @@ -0,0 +1,57 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from toscaparser.elements.scalarunit import ScalarUnit_Size +from translator.hot.syntax.hot_resource import HotResource + +# Name used to dynamically load appropriate map class. +TARGET_CLASS_NAME = 'ToscaObjectStorage' + + +class ToscaObjectStorage(HotResource): + '''Translate TOSCA node type tosca.nodes.ObjectStorage.''' + + toscatype = 'tosca.nodes.ObjectStorage' + + def __init__(self, nodetemplate): + super(ToscaObjectStorage, self).__init__(nodetemplate, + type='OS::Swift::Container') + pass + + def handle_properties(self): + tosca_props = self.get_tosca_props() + objectstore_props = {} + container_quota = {} + skip_check = False + + for key, value in tosca_props.items(): + if key == "name": + objectstore_props["name"] = value + elif key == "size" or key == "maxsize": + # currently heat is not supporting dynamically increase + # the container quota-size. + # if both defined in tosca template, consider store_maxsize. + if skip_check: + continue + quota_size = None + if "maxsize" in tosca_props.keys(): + quota_size = tosca_props["maxsize"] + else: + quota_size = tosca_props["size"] + container_quota["Quota-Bytes"] = \ + ScalarUnit_Size(quota_size).get_num_from_scalar_unit() + objectstore_props["X-Container-Meta"] = container_quota + skip_check = True + + objectstore_props["X-Container-Read"] = '".r:*"' + self.properties = objectstore_props diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies.py new file mode 100644 index 0000000..b32fc1d --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_policies.py @@ -0,0 +1,36 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from translator.hot.syntax.hot_resource import HotResource + +# Name used to dynamically load appropriate map class. +TARGET_CLASS_NAME = 'ToscaPolicies' + + +class ToscaPolicies(HotResource): + '''Translate TOSCA policy type tosca.poicies.Placement.''' + + toscatype = 'tosca.policies.Placement' + + def __init__(self, policy): + super(ToscaPolicies, self).__init__(policy, + type='OS::Nova::ServerGroup') + self.policy = policy + + def handle_properties(self, resources): + self.properties["name"] = self.name + self.properties["policies"] = ["affinity"] + for resource in resources: + if resource.name in self.policy.targets: + resource.properties["scheduler_hints"] = { + "group": {"get_resource": self.name}} diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_software_component.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_software_component.py new file mode 100644 index 0000000..044de43 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_software_component.py @@ -0,0 +1,30 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from translator.hot.syntax.hot_resource import HotResource + +# Name used to dynamically load appropriate map class. +TARGET_CLASS_NAME = 'ToscaSoftwareComponent' + + +class ToscaSoftwareComponent(HotResource): + '''Translate TOSCA node type tosca.nodes.SoftwareComponent.''' + + toscatype = 'tosca.nodes.SoftwareComponent' + + def __init__(self, nodetemplate): + super(ToscaSoftwareComponent, self).__init__(nodetemplate) + pass + + def handle_properties(self): + pass diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_web_application.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_web_application.py new file mode 100644 index 0000000..d0a9c5d --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_web_application.py @@ -0,0 +1,30 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from translator.hot.syntax.hot_resource import HotResource + +# Name used to dynamically load appropriate map class. +TARGET_CLASS_NAME = 'ToscaWebApplication' + + +class ToscaWebApplication(HotResource): + '''Translate TOSCA node type tosca.nodes.WebApplication.''' + + toscatype = 'tosca.nodes.WebApplication' + + def __init__(self, nodetemplate): + super(ToscaWebApplication, self).__init__(nodetemplate) + pass + + def handle_properties(self): + pass diff --git a/tosca2heat/heat-translator/translator/hot/tosca/tosca_webserver.py b/tosca2heat/heat-translator/translator/hot/tosca/tosca_webserver.py new file mode 100644 index 0000000..83bda80 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca/tosca_webserver.py @@ -0,0 +1,30 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from translator.hot.syntax.hot_resource import HotResource + +# Name used to dynamically load appropriate map class. +TARGET_CLASS_NAME = 'ToscaWebserver' + + +class ToscaWebserver(HotResource): + '''Translate TOSCA node type tosca.nodes.WebServer.''' + + toscatype = 'tosca.nodes.WebServer' + + def __init__(self, nodetemplate): + super(ToscaWebserver, self).__init__(nodetemplate) + pass + + def handle_properties(self): + pass diff --git a/tosca2heat/heat-translator/translator/hot/tosca_translator.py b/tosca2heat/heat-translator/translator/hot/tosca_translator.py new file mode 100644 index 0000000..14ef8a1 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/tosca_translator.py @@ -0,0 +1,68 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging +from toscaparser.utils.gettextutils import _ +from translator.hot.syntax.hot_template import HotTemplate +from translator.hot.translate_inputs import TranslateInputs +from translator.hot.translate_node_templates import TranslateNodeTemplates +from translator.hot.translate_outputs import TranslateOutputs + +log = logging.getLogger('heat-translator') + + +class TOSCATranslator(object): + '''Invokes translation methods.''' + + def __init__(self, tosca, parsed_params, deploy=None): + super(TOSCATranslator, self).__init__() + self.tosca = tosca + self.hot_template = HotTemplate() + self.parsed_params = parsed_params + self.deploy = deploy + self.node_translator = None + log.info(_('Initialized parmaters for translation.')) + + def translate(self): + self._resolve_input() + self.hot_template.description = self.tosca.description + self.hot_template.parameters = self._translate_inputs() + self.node_translator = TranslateNodeTemplates(self.tosca, + self.hot_template) + self.hot_template.resources = self.node_translator.translate() + self.hot_template.outputs = self._translate_outputs() + return self.hot_template.output_to_yaml() + + def _translate_inputs(self): + translator = TranslateInputs(self.tosca.inputs, self.parsed_params, + self.deploy) + return translator.translate() + + def _translate_outputs(self): + translator = TranslateOutputs(self.tosca.outputs, self.node_translator) + return translator.translate() + + # check all properties for all node and ensure they are resolved + # to actual value + def _resolve_input(self): + for n in self.tosca.nodetemplates: + for node_prop in n.get_properties_objects(): + if isinstance(node_prop.value, dict): + try: + self.parsed_params[node_prop.value['get_input']] + except Exception: + msg = (_('Must specify all input values in \ + TOSCA template, missing %s.') % + node_prop.value['get_input']) + log.error(msg) + raise ValueError(msg) diff --git a/tosca2heat/heat-translator/translator/hot/translate_inputs.py b/tosca2heat/heat-translator/translator/hot/translate_inputs.py new file mode 100644 index 0000000..6d677d1 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/translate_inputs.py @@ -0,0 +1,167 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging +from toscaparser.dataentity import DataEntity +from toscaparser.elements.scalarunit import ScalarUnit_Size +from toscaparser.parameters import Input +from toscaparser.utils.gettextutils import _ +from toscaparser.utils.validateutils import TOSCAVersionProperty +from translator.hot.syntax.hot_parameter import HotParameter + + +INPUT_CONSTRAINTS = (CONSTRAINTS, DESCRIPTION, LENGTH, RANGE, + MIN, MAX, ALLOWED_VALUES, ALLOWED_PATTERN) = \ + ('constraints', 'description', 'length', 'range', + 'min', 'max', 'allowed_values', 'allowed_pattern') + +TOSCA_CONSTRAINT_OPERATORS = (EQUAL, GREATER_THAN, GREATER_OR_EQUAL, LESS_THAN, + LESS_OR_EQUAL, IN_RANGE, VALID_VALUES, LENGTH, + MIN_LENGTH, MAX_LENGTH, PATTERN) = \ + ('equal', 'greater_than', 'greater_or_equal', + 'less_than', 'less_or_equal', 'in_range', + 'valid_values', 'length', 'min_length', + 'max_length', 'pattern') + +TOSCA_TO_HOT_CONSTRAINTS_ATTRS = {'equal': 'allowed_values', + 'greater_than': 'range', + 'greater_or_equal': 'range', + 'less_than': 'range', + 'less_or_equal': 'range', + 'in_range': 'range', + 'valid_values': 'allowed_values', + 'length': 'length', + 'min_length': 'length', + 'max_length': 'length', + 'pattern': 'allowed_pattern'} + +TOSCA_TO_HOT_INPUT_TYPES = {'string': 'string', + 'integer': 'number', + 'float': 'number', + 'boolean': 'boolean', + 'timestamp': 'string', + 'scalar-unit.size': 'number', + 'version': 'string', + 'null': 'string', + 'PortDef': 'number'} + +log = logging.getLogger('heat-translator') + + +class TranslateInputs(object): + + '''Translate TOSCA Inputs to Heat Parameters.''' + + def __init__(self, inputs, parsed_params, deploy=None): + self.inputs = inputs + self.parsed_params = parsed_params + self.deploy = deploy + + def translate(self): + return self._translate_inputs() + + def _translate_inputs(self): + hot_inputs = [] + if 'key_name' in self.parsed_params and 'key_name' not in self.inputs: + name = 'key_name' + type = 'string' + default = self.parsed_params[name] + schema_dict = {'type': type, 'default': default} + input = Input(name, schema_dict) + self.inputs.append(input) + + log.info(_('Translating TOSCA input type to HOT input type.')) + for input in self.inputs: + hot_default = None + hot_input_type = TOSCA_TO_HOT_INPUT_TYPES[input.type] + + if input.name in self.parsed_params: + hot_default = DataEntity.validate_datatype( + input.type, self.parsed_params[input.name]) + elif input.default is not None: + hot_default = DataEntity.validate_datatype(input.type, + input.default) + else: + if self.deploy: + msg = _("Need to specify a value " + "for input {0}.").format(input.name) + log.error(msg) + raise Exception(msg) + if input.type == "scalar-unit.size": + # Assumption here is to use this scalar-unit.size for size of + # cinder volume in heat templates and will be in GB. + # should add logic to support other types if needed. + input_value = hot_default + hot_default = (ScalarUnit_Size(hot_default). + get_num_from_scalar_unit('GiB')) + if hot_default == 0: + msg = _('Unit value should be > 0.') + log.error(msg) + raise Exception(msg) + elif int(hot_default) < hot_default: + hot_default = int(hot_default) + 1 + log.warning(_("Cinder unit value should be in multiples" + " of GBs. So corrected %(input_value)s " + "to %(hot_default)s GB.") + % {'input_value': input_value, + 'hot_default': hot_default}) + if input.type == 'version': + hot_default = TOSCAVersionProperty(hot_default).get_version() + + hot_constraints = [] + if input.constraints: + for constraint in input.constraints: + if hot_default: + constraint.validate(hot_default) + hc, hvalue = self._translate_constraints( + constraint.constraint_key, constraint.constraint_value) + hot_constraints.append({hc: hvalue}) + + hot_inputs.append(HotParameter(name=input.name, + type=hot_input_type, + description=input.description, + default=hot_default, + constraints=hot_constraints)) + return hot_inputs + + def _translate_constraints(self, name, value): + hot_constraint = TOSCA_TO_HOT_CONSTRAINTS_ATTRS[name] + + # Offset used to support less_than and greater_than. + # TODO(anyone): when parser supports float, verify this works + offset = 1 + + if name == EQUAL: + hot_value = [value] + elif name == GREATER_THAN: + hot_value = {"min": value + offset} + elif name == GREATER_OR_EQUAL: + hot_value = {"min": value} + elif name == LESS_THAN: + hot_value = {"max": value - offset} + elif name == LESS_OR_EQUAL: + hot_value = {"max": value} + elif name == IN_RANGE: + # value is list type here + min_value = min(value) + max_value = max(value) + hot_value = {"min": min_value, "max": max_value} + elif name == LENGTH: + hot_value = {"min": value, "max": value} + elif name == MIN_LENGTH: + hot_value = {"min": value} + elif name == MAX_LENGTH: + hot_value = {"max": value} + else: + hot_value = value + return hot_constraint, hot_value diff --git a/tosca2heat/heat-translator/translator/hot/translate_node_templates.py b/tosca2heat/heat-translator/translator/hot/translate_node_templates.py new file mode 100644 index 0000000..46cdd71 --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/translate_node_templates.py @@ -0,0 +1,483 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import importlib +import logging +import os +import six + +from toscaparser.functions import GetAttribute +from toscaparser.functions import GetInput +from toscaparser.functions import GetProperty +from toscaparser.properties import Property +from toscaparser.relationship_template import RelationshipTemplate +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.conf.config import ConfigProvider as translatorConfig +from translator.hot.syntax.hot_resource import HotResource +from translator.hot.tosca.tosca_block_storage_attachment import ( + ToscaBlockStorageAttachment + ) + +########################### +# Module utility Functions +# for dynamic class loading +########################### + + +def _generate_type_map(): + '''Generate TOSCA translation types map. + + Load user defined classes from location path specified in conf file. + Base classes are located within the tosca directory. + + ''' + + # Base types directory + BASE_PATH = 'translator/hot/tosca' + + # Custom types directory defined in conf file + custom_path = translatorConfig.get_value('DEFAULT', + 'custom_types_location') + + # First need to load the parent module, for example 'contrib.hot', + # for all of the dynamically loaded classes. + classes = [] + _load_classes((BASE_PATH, custom_path), classes) + try: + types_map = {clazz.toscatype: clazz for clazz in classes} + except AttributeError as e: + raise ToscaClassAttributeError(message=e.message) + + return types_map + + +def _load_classes(locations, classes): + '''Dynamically load all the classes from the given locations.''' + + for cls_path in locations: + # Use the absolute path of the class path + abs_path = os.path.dirname(os.path.abspath(__file__)) + abs_path = abs_path.replace('translator/hot', cls_path) + + # Grab all the tosca type module files in the given path + mod_files = [f for f in os.listdir(abs_path) if f.endswith('.py') + and not f.startswith('__init__') + and f.startswith('tosca_')] + + # For each module, pick out the target translation class + for f in mod_files: + # NOTE: For some reason the existing code does not use the map to + # instantiate ToscaBlockStorageAttachment. Don't add it to the map + # here until the dependent code is fixed to use the map. + if f == 'tosca_block_storage_attachment.py': + continue + + mod_name = cls_path + '/' + f.strip('.py') + mod_name = mod_name.replace('/', '.') + try: + mod = importlib.import_module(mod_name) + target_name = getattr(mod, 'TARGET_CLASS_NAME') + clazz = getattr(mod, target_name) + classes.append(clazz) + except ImportError: + raise ToscaModImportError(mod_name=mod_name) + except AttributeError: + if target_name: + raise ToscaClassImportError(name=target_name, + mod_name=mod_name) + else: + # TARGET_CLASS_NAME is not defined in module. + # Re-raise the exception + raise + +################## +# Module constants +################## + +SECTIONS = (TYPE, PROPERTIES, REQUIREMENTS, INTERFACES, LIFECYCLE, INPUT) = \ + ('type', 'properties', 'requirements', + 'interfaces', 'lifecycle', 'input') + +# TODO(anyone): the following requirement names should not be hard-coded +# in the translator. Since they are basically arbitrary names, we have to get +# them from TOSCA type definitions. +# To be fixed with the blueprint: +# https://blueprints.launchpad.net/heat-translator/+spec/tosca-custom-types +REQUIRES = (CONTAINER, DEPENDENCY, DATABASE_ENDPOINT, CONNECTION, HOST) = \ + ('container', 'dependency', 'database_endpoint', + 'connection', 'host') + +INTERFACES_STATE = (CREATE, START, CONFIGURE, START, DELETE) = \ + ('create', 'stop', 'configure', 'start', 'delete') + + +TOSCA_TO_HOT_REQUIRES = {'container': 'server', 'host': 'server', + 'dependency': 'depends_on', "connects": 'depends_on'} + +TOSCA_TO_HOT_PROPERTIES = {'properties': 'input'} +log = logging.getLogger('heat-translator') + +TOSCA_TO_HOT_TYPE = _generate_type_map() + + +class TranslateNodeTemplates(object): + '''Translate TOSCA NodeTemplates to Heat Resources.''' + + def __init__(self, tosca, hot_template): + self.tosca = tosca + self.nodetemplates = self.tosca.nodetemplates + self.hot_template = hot_template + # list of all HOT resources generated + self.hot_resources = [] + # mapping between TOSCA nodetemplate and HOT resource + log.debug(_('Mapping between TOSCA nodetemplate and HOT resource.')) + self.hot_lookup = {} + self.policies = self.tosca.topology_template.policies + + def translate(self): + return self._translate_nodetemplates() + + def _recursive_handle_properties(self, resource): + '''Recursively handle the properties of the depends_on_nodes nodes.''' + # Use of hashtable (dict) here should be faster? + if resource in self.processed_resources: + return + self.processed_resources.append(resource) + for depend_on in resource.depends_on_nodes: + self._recursive_handle_properties(depend_on) + + if resource.type == "OS::Nova::ServerGroup": + resource.handle_properties(self.hot_resources) + else: + resource.handle_properties() + + def _translate_nodetemplates(self): + + log.debug(_('Translating the node templates.')) + suffix = 0 + # Copy the TOSCA graph: nodetemplate + for node in self.nodetemplates: + base_type = HotResource.get_base_type(node.type_definition) + hot_node = TOSCA_TO_HOT_TYPE[base_type.type](node) + self.hot_resources.append(hot_node) + self.hot_lookup[node] = hot_node + + # BlockStorage Attachment is a special case, + # which doesn't match to Heat Resources 1 to 1. + if base_type.type == "tosca.nodes.Compute": + volume_name = None + requirements = node.requirements + if requirements: + # Find the name of associated BlockStorage node + for requires in requirements: + for value in requires.values(): + if isinstance(value, dict): + for node_name in value.values(): + for n in self.nodetemplates: + if n.name == node_name: + volume_name = node_name + break + else: # unreachable code ! + for n in self.nodetemplates: + if n.name == node_name: + volume_name = node_name + break + + suffix = suffix + 1 + attachment_node = self._get_attachment_node(node, + suffix, + volume_name) + if attachment_node: + self.hot_resources.append(attachment_node) + for i in self.tosca.inputs: + if (i.name == 'key_name' and + node.get_property_value('key_name') is None): + schema = {'type': i.type, 'default': i.default} + value = {"get_param": "key_name"} + prop = Property(i.name, value, schema) + node._properties.append(prop) + + for policy in self.policies: + policy_type = policy.type_definition + policy_node = TOSCA_TO_HOT_TYPE[policy_type.type](policy) + self.hot_resources.append(policy_node) + + # Handle life cycle operations: this may expand each node + # 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 + self.hot_resources += lifecycle_resources + + # Handle configuration from ConnectsTo relationship in the TOSCA node: + # this will generate multiple HOT resources, set of 2 for each + # configuration + connectsto_resources = [] + for node in self.nodetemplates: + for requirement in node.requirements: + for endpoint, details in six.iteritems(requirement): + relation = None + if isinstance(details, dict): + target = details.get('node') + relation = details.get('relationship') + else: + target = details + if (target and relation and + not isinstance(relation, six.string_types)): + interfaces = relation.get('interfaces') + connectsto_resources += \ + self._create_connect_configs(node, + target, + interfaces) + self.hot_resources += connectsto_resources + + # Copy the initial dependencies based on the relationship in + # the TOSCA template + for node in self.nodetemplates: + for node_depend in node.related_nodes: + # 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 \ + node.related[node_depend].type == \ + node.type_definition.HOSTEDON: + self.hot_lookup[node].properties['server'] = \ + {'get_resource': self.hot_lookup[node_depend].name} + # for all others, add dependency as depends_on + else: + self.hot_lookup[node].depends_on.append( + self.hot_lookup[node_depend].top_of_chain()) + + self.hot_lookup[node].depends_on_nodes.append( + self.hot_lookup[node_depend].top_of_chain()) + + # handle hosting relationship + for resource in self.hot_resources: + resource.handle_hosting() + + # handle built-in properties of HOT resources + # if a resource depends on other resources, + # their properties need to be handled first. + # Use recursion to handle the properties of the + # dependent nodes in correct order + self.processed_resources = [] + for resource in self.hot_resources: + self._recursive_handle_properties(resource) + + # handle resources that need to expand to more than one HOT resource + expansion_resources = [] + for resource in self.hot_resources: + expanded = resource.handle_expansion() + if expanded: + expansion_resources += expanded + self.hot_resources += expansion_resources + + # Resolve function calls: GetProperty, GetAttribute, GetInput + # at this point, all the HOT resources should have been created + # in the graph. + for resource in self.hot_resources: + # traverse the reference chain to get the actual value + inputs = resource.properties.get('input_values') + if inputs: + for name, value in six.iteritems(inputs): + inputs[name] = self._translate_input(value, resource) + + return self.hot_resources + + def _translate_input(self, input_value, resource): + get_property_args = None + if isinstance(input_value, GetProperty): + get_property_args = input_value.args + # to remove when the parser is fixed to return GetProperty + if isinstance(input_value, dict) and 'get_property' in input_value: + get_property_args = input_value['get_property'] + if 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): + # 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]} + else: + return {'get_param': input_value.args} + + return input_value + + @staticmethod + def get_all_artifacts(nodetemplate): + artifacts = nodetemplate.type_definition.get_value('artifacts', + parent=True) + if not artifacts: + artifacts = {} + tpl_artifacts = nodetemplate.entity_tpl.get('artifacts') + if tpl_artifacts: + artifacts.update(tpl_artifacts) + + return artifacts + + def _get_attachment_node(self, node, suffix, volume_name): + attach = False + ntpl = self.nodetemplates + for key, value in node.relationships.items(): + if key.is_derived_from('tosca.relationships.AttachesTo'): + if value.is_derived_from('tosca.nodes.BlockStorage'): + attach = True + if attach: + relationship_tpl = None + for req in node.requirements: + for key, val in req.items(): + attach = val + relship = val.get('relationship') + for rkey, rval in val.items(): + if relship and isinstance(relship, dict): + for rkey, rval in relship.items(): + if rkey == 'type': + relationship_tpl = val + attach = rval + elif rkey == 'template': + rel_tpl_list = \ + (self.tosca.topology_template. + _tpl_relationship_templates()) + relationship_tpl = rel_tpl_list[rval] + attach = rval + else: + continue + elif isinstance(relship, str): + attach = relship + relationship_tpl = val + relationship_templates = \ + self.tosca._tpl_relationship_templates() + if 'relationship' in relationship_tpl and \ + attach not in \ + self.tosca._tpl_relationship_types() and \ + attach in relationship_templates: + relationship_tpl['relationship'] = \ + relationship_templates[attach] + break + if relationship_tpl: + rval_new = attach + "_" + str(suffix) + att = RelationshipTemplate( + relationship_tpl, rval_new, + self.tosca._tpl_relationship_types()) + hot_node = ToscaBlockStorageAttachment(att, ntpl, + node.name, + volume_name + ) + return hot_node + + def find_hot_resource(self, name): + for resource in self.hot_resources: + 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): + if tosca_name == 'SELF': + return current_hot_resource + if tosca_name == 'HOST' and current_hot_resource is not None: + for req in current_hot_resource.nodetemplate.requirements: + if 'host' in req: + return self._find_hot_resource_for_tosca(req['host']) + + for node in self.nodetemplates: + if node.name == tosca_name: + return self.hot_lookup[node] + + return None + + def _create_connect_configs(self, source_node, target_name, + connect_interfaces): + connectsto_resources = [] + if connect_interfaces: + for iname, interface in six.iteritems(connect_interfaces): + connectsto_resources += \ + self._create_connect_config(source_node, target_name, + interface) + return connectsto_resources + + def _create_connect_config(self, source_node, target_name, + connect_interface): + connectsto_resources = [] + target_node = self._find_tosca_node(target_name) + # the configuration can occur on the source or the target + connect_config = connect_interface.get('pre_configure_target') + if connect_config is not None: + config_location = 'target' + else: + connect_config = connect_interface.get('pre_configure_source') + if connect_config is not None: + config_location = 'source' + else: + msg = _("Template error: " + "no configuration found for ConnectsTo " + "in {1}").format(self.nodetemplate.name) + log.error(msg) + raise Exception(msg) + config_name = source_node.name + '_' + target_name + '_connect_config' + implement = connect_config.get('implementation') + if config_location == 'target': + hot_config = HotResource(target_node, + config_name, + 'OS::Heat::SoftwareConfig', + {'config': {'get_file': implement}}) + elif config_location == 'source': + hot_config = HotResource(source_node, + config_name, + 'OS::Heat::SoftwareConfig', + {'config': {'get_file': implement}}) + connectsto_resources.append(hot_config) + hot_target = self._find_hot_resource_for_tosca(target_name) + hot_source = self._find_hot_resource_for_tosca(source_node.name) + connectsto_resources.append(hot_config. + handle_connectsto(source_node, + target_node, + hot_source, + hot_target, + config_location, + connect_interface)) + return connectsto_resources diff --git a/tosca2heat/heat-translator/translator/hot/translate_outputs.py b/tosca2heat/heat-translator/translator/hot/translate_outputs.py new file mode 100644 index 0000000..4197cdd --- /dev/null +++ b/tosca2heat/heat-translator/translator/hot/translate_outputs.py @@ -0,0 +1,48 @@ +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import logging + +from toscaparser.utils.gettextutils import _ +from translator.hot.syntax.hot_output import HotOutput + +log = logging.getLogger('heat-translator') + + +class TranslateOutputs(object): + '''Translate TOSCA Outputs to Heat Outputs.''' + + def __init__(self, outputs, node_translator): + log.debug(_('Translating TOSCA outputs to HOT outputs.')) + self.outputs = outputs + self.nodes = node_translator + + def translate(self): + return self._translate_outputs() + + def _translate_outputs(self): + hot_outputs = [] + for output in self.outputs: + if output.value.name == 'get_attribute': + get_parameters = output.value.args + hot_target = self.nodes.find_hot_resource(get_parameters[0]) + hot_value = hot_target.get_hot_attribute(get_parameters[1], + get_parameters) + hot_outputs.append(HotOutput(output.name, + hot_value, + output.description)) + else: + hot_outputs.append(HotOutput(output.name, + output.value, + output.description)) + return hot_outputs diff --git a/tosca2heat/heat-translator/translator/osc/__init__.py b/tosca2heat/heat-translator/translator/osc/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tosca2heat/heat-translator/translator/osc/__init__.py diff --git a/tosca2heat/heat-translator/translator/osc/osc_plugin.py b/tosca2heat/heat-translator/translator/osc/osc_plugin.py new file mode 100644 index 0000000..6d3d25a --- /dev/null +++ b/tosca2heat/heat-translator/translator/osc/osc_plugin.py @@ -0,0 +1,41 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from translator.osc import utils + +DEFAULT_TRANSLATOR_API_VERSION = '1' +API_VERSION_OPTION = 'os_translator_api_version' +API_NAME = 'translator' +API_VERSIONS = { + '1': 'translator.v1.client.Client', +} + + +def make_client(instance): + # NOTE(stevemar): We don't need a client because + # heat-translator itself is a command line tool + pass + + +def build_option_parser(parser): + """Hook to add global options.""" + + parser.add_argument( + '--os-translator-api-version', + metavar='<translator-api-version>', + default=utils.env( + 'OS_TRANSLATOR_API_VERSION', + default=DEFAULT_TRANSLATOR_API_VERSION), + help='Translator API version, default=' + + DEFAULT_TRANSLATOR_API_VERSION + + ' (Env: OS_TRANSLATOR_API_VERSION)') + return parser diff --git a/tosca2heat/heat-translator/translator/osc/utils.py b/tosca2heat/heat-translator/translator/osc/utils.py new file mode 100644 index 0000000..e8a6814 --- /dev/null +++ b/tosca2heat/heat-translator/translator/osc/utils.py @@ -0,0 +1,45 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +"""Common client utilities""" + +import argparse +import os + + +def env(*vars, **kwargs): + """Search for the first defined of possibly many env vars + + Returns the first environment variable defined in vars, or + returns the default defined in kwargs. + """ + for v in vars: + value = os.environ.get(v, None) + if value: + return value + return kwargs.get('default', '') + + +class KeyValueAction(argparse.Action): + """A custom action to parse arguments as key=value pairs. """ + + def __call__(self, parser, namespace, values, option_string=None): + # Make sure we have an empty dict rather than None + if getattr(namespace, self.dest, None) is None: + setattr(namespace, self.dest, {}) + + # Add value if an assignment else remove it + if '=' in values: + getattr(namespace, self.dest, {}).update([values.split('=', 1)]) + else: + getattr(namespace, self.dest, {}).pop(values, None) diff --git a/tosca2heat/heat-translator/translator/osc/v1/__init__.py b/tosca2heat/heat-translator/translator/osc/v1/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tosca2heat/heat-translator/translator/osc/v1/__init__.py diff --git a/tosca2heat/heat-translator/translator/osc/v1/tests/__init__.py b/tosca2heat/heat-translator/translator/osc/v1/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tosca2heat/heat-translator/translator/osc/v1/tests/__init__.py diff --git a/tosca2heat/heat-translator/translator/osc/v1/tests/fakes.py b/tosca2heat/heat-translator/translator/osc/v1/tests/fakes.py new file mode 100644 index 0000000..a08c3ac --- /dev/null +++ b/tosca2heat/heat-translator/translator/osc/v1/tests/fakes.py @@ -0,0 +1,32 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import sys + + +class FakeApp(object): + def __init__(self): + self.client_manager = None + self.stdin = sys.stdin + self.stdout = sys.stdout + self.stderr = sys.stderr + + +class FakeClientManager(object): + def __init__(self): + self.compute = None + self.identity = None + self.image = None + self.object_store = None + self.volume = None + self.network = None + self.session = None diff --git a/tosca2heat/heat-translator/translator/osc/v1/tests/test_translate.py b/tosca2heat/heat-translator/translator/osc/v1/tests/test_translate.py new file mode 100644 index 0000000..6a5f115 --- /dev/null +++ b/tosca2heat/heat-translator/translator/osc/v1/tests/test_translate.py @@ -0,0 +1,448 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +import testtools + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO +import toscaparser.utils.yamlparser + +from toscaparser.common.exception import ExceptionCollector +from toscaparser.common.exception import URLException +from toscaparser.common.exception import ValidationError +from toscaparser.utils.gettextutils import _ +from translator.common.utils import CompareUtils +from translator.common.utils import YamlUtils +from translator.osc.v1.tests import fakes +from translator.osc.v1.tests import utils +from translator.osc.v1 import translate + + +class TestTranslateTemplate(testtools.TestCase): + + def setUp(self): + super(TestTranslateTemplate, self).setUp() + self.app = fakes.FakeApp() + self.app.client_manager = fakes.FakeClientManager() + self.app.client_manager.translator = None + self.cmd = translate.TranslateTemplate(self.app, None) + + def check_parser(self, cmd, args, verify_args): + cmd_parser = cmd.get_parser('check_parser') + try: + parsed_args = cmd_parser.parse_args(args) + except SystemExit: + raise Exception("Argument parse failed") + for av in verify_args: + attr, value = av + if attr: + self.assertIn(attr, parsed_args) + self.assertEqual(getattr(parsed_args, attr), value) + return parsed_args + + def _check_error(self, tosca_file, hot_file, params, assert_error, + expected_msg, c_error): + arglist = ["--template-file", tosca_file, + "--template-type", "tosca"] + parsed_args = self.check_parser(self.cmd, arglist, []) + parsed_args.parameter = params + self.assertRaises(assert_error, self.cmd.take_action, + parsed_args) + ExceptionCollector.assertExceptionMessage(c_error, expected_msg) + + @mock.patch('sys.stdout', new_callable=StringIO) + def _check_success(self, tosca_file, hot_file, params, mock_stdout): + arglist = ["--template-file", tosca_file, + "--template-type", "tosca"] + parsed_args = self.check_parser(self.cmd, arglist, []) + parsed_args.parameter = params + self.cmd.take_action(parsed_args) + expected_output = YamlUtils.get_dict(hot_file) + mock_stdout_yaml = "\n".join(mock_stdout.getvalue().split("\n")) + actual_output = toscaparser.utils.yamlparser.simple_parse( + mock_stdout_yaml) + self.assertEqual({}, CompareUtils.diff_dicts( + actual_output, expected_output)) + + def test_osc_translate_single_server(self): + tosca_file = utils.get_template_path("tosca_single_server.yaml") + + hot_file = utils.get_template_path("hot_output/hot_single_server.yaml") + + params = {'cpus': 1} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_single_server_defaults_with_input(self): + tosca_file = utils.get_template_path( + "tosca_single_server_with_defaults.yaml") + + hot_file = utils.get_template_path( + "hot_output/hot_single_server_with_defaults_with_input.yaml") + + params = {'cpus': '1'} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_single_server_defaults_without_input(self): + tosca_file = utils.get_template_path( + "tosca_single_server_with_defaults.yaml") + + hot_file = utils.get_template_path( + "hot_output/hot_single_server_with_defaults_without_input.yaml") + + self._check_success(tosca_file, hot_file, {}) + + def test_osc_translate_wordpress_single_instance(self): + tosca_file = utils.get_template_path( + "tosca_single_instance_wordpress.yaml") + hot_file = utils.get_template_path( + "hot_output/hot_single_instance_wordpress.yaml") + params = {'db_name': 'wordpress', + 'db_user': 'wp_user', + 'db_pwd': 'wp_pass', + 'db_root_pwd': 'passw0rd', + 'db_port': 3366, + 'cpus': 8} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_helloworld(self): + tosca_file = utils.get_template_path( + "tosca_helloworld.yaml") + hot_file = utils.get_template_path( + "hot_output/hot_hello_world.yaml") + self._check_success(tosca_file, hot_file, {}) + + def test_osc_translate_host_assignment(self): + tosca_file = utils.get_template_path( + "test_host_assignment.yaml") + hot_file = utils.get_template_path( + "hot_output/hot_host_assignment.yaml") + self._check_success(tosca_file, hot_file, {}) + + def test_osc_translate_elk(self): + tosca_file = utils.get_template_path( + "tosca_elk.yaml") + hot_file = utils.get_template_path( + "hot_output/hot_elk.yaml") + params = {'github_url': + 'http://github.com/paypal/rest-api-sample-app-nodejs.git', + 'my_cpus': 4} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_nodejs_mongodb_two_instances(self): + tosca_file = utils.get_template_path( + "tosca_nodejs_mongodb_two_instances.yaml") + hot_file = utils.get_template_path( + "hot_output/hot_nodejs_mongodb_two_instances.yaml") + params = {'github_url': + 'http://github.com/paypal/rest-api-sample-app-nodejs.git', + 'my_cpus': 4} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_blockstorage_with_attachment(self): + tosca_file = utils.get_template_path( + "storage/tosca_blockstorage_with_attachment.yaml") + hot_file = utils.get_template_path( + "hot_output/storage/hot_blockstorage_with_attachment.yaml") + params = {'cpus': 1, + 'storage_location': '/dev/vdc', + 'storage_size': '2000 MB', + 'storage_snapshot_id': 'ssid'} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_blockstorage_with_custom_relationship_type(self): + tosca_file = utils.get_template_path( + "storage/tosca_blockstorage_with_custom_relationship_type.yaml") + hot_file = utils.get_template_path( + "hot_output/storage/" + "hot_blockstorage_with_custom_relationship_type.yaml") + params = {'cpus': 1, + 'storage_location': '/dev/vdc', + 'storage_size': '1 GB', + 'storage_snapshot_id': 'ssid'} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_blockstorage_with_relationship_template(self): + tosca_file = utils.get_template_path( + "storage/" + + "tosca_blockstorage_with_relationship_template.yaml") + hot_file = utils.get_template_path( + "hot_output/storage/" + + "hot_blockstorage_with_relationship_template.yaml") + params = {'cpus': 1, + 'storage_location': '/dev/vdc', + 'storage_size': '1 GB'} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_blockstorage_with_attachment_notation1(self): + tosca_file = utils.get_template_path( + "storage/" + + "tosca_blockstorage_with_attachment_notation1.yaml") + hot_file1 = utils.get_template_path( + "hot_output/storage/" + + "hot_blockstorage_with_attachment_notation1_alt1.yaml") + hot_file2 = utils.get_template_path( + "hot_output/storage/" + + "hot_blockstorage_with_attachment_notation1_alt2.yaml") + params = {'cpus': 1, + 'storage_location': 'some_folder', + 'storage_size': '1 GB', + 'storage_snapshot_id': 'ssid'} + try: + self._check_success(tosca_file, hot_file1, params) + except Exception: + self._check_success(tosca_file, hot_file2, params) + + def test_osc_translate_blockstorage_with_attachment_notation2(self): + tosca_file = utils.get_template_path( + "storage/" + + "tosca_blockstorage_with_attachment_notation2.yaml") + hot_file1 = utils.get_template_path( + "hot_output/storage/" + + "hot_blockstorage_with_attachment_notation2_alt1.yaml") + hot_file2 = utils.get_template_path( + "hot_output/storage/" + + "hot_blockstorage_with_attachment_notation2_alt2.yaml") + params = {'cpus': 1, + 'storage_location': '/dev/vdc', + 'storage_size': '1 GB', + 'storage_snapshot_id': 'ssid'} + try: + self._check_success(tosca_file, hot_file1, params) + except Exception: + self._check_success(tosca_file, hot_file2, params) + + def test_osc_translate_multiple_blockstorage_with_attachment(self): + tosca_file = utils.get_template_path( + "storage/" + + "tosca_multiple_blockstorage_with_attachment.yaml") + hot_file1 = utils.get_template_path( + "hot_output/storage/" + + "hot_multiple_blockstorage_with_attachment_alt1.yaml") + hot_file2 = utils.get_template_path( + "hot_output/storage/" + + "hot_multiple_blockstorage_with_attachment_alt2.yaml") + params = {'cpus': 1, + 'storage_location': '/dev/vdc', + 'storage_size': '1 GB', + 'storage_snapshot_id': 'ssid'} + try: + self._check_success(tosca_file, hot_file1, params) + except Exception: + self._check_success(tosca_file, hot_file2, params) + + def test_osc_translate_single_object_store(self): + tosca_file = utils.get_template_path( + "storage/tosca_single_object_store.yaml") + hot_file = utils.get_template_path( + "hot_output/hot_single_object_store.yaml") + params = {'objectstore_name': 'myobjstore'} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_one_server_one_network(self): + tosca_file = utils.get_template_path( + "network/tosca_one_server_one_network.yaml") + hot_file = utils.get_template_path( + "hot_output/network/" + + "hot_one_server_one_network.yaml") + params = {'network_name': 'private_net'} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_server_on_existing_network(self): + tosca_file = utils.get_template_path( + "network/" + + "tosca_server_on_existing_network.yaml") + hot_file = utils.get_template_path( + "hot_output/network/" + + "hot_server_on_existing_network.yaml") + params = {'network_name': 'private_net'} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_two_servers_one_network(self): + tosca_file = utils.get_template_path( + "network/tosca_two_servers_one_network.yaml") + hot_file = utils.get_template_path( + "hot_output/network/" + + "hot_two_servers_one_network.yaml") + params = {'network_name': 'my_private_net', + 'network_cidr': '10.0.0.0/24', + 'network_start_ip': '10.0.0.100', + 'network_end_ip': '10.0.0.150'} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_one_server_three_networks(self): + tosca_file = utils.get_template_path( + "network/" + + "tosca_one_server_three_networks.yaml") + hot_file = utils.get_template_path( + "hot_output/network/" + + "hot_one_server_three_networks.yaml") + self._check_success(tosca_file, hot_file, {}) + + def test_osc_translate_software_component(self): + tosca_file = utils.get_template_path("tosca_software_component.yaml") + hot_file = utils.get_template_path( + "hot_output/hot_software_component.yaml") + params = {'cpus': '1', + 'download_url': 'http://www.software.com/download'} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_web_application(self): + tosca_file = utils.get_template_path("tosca_web_application.yaml") + hot_file = utils.get_template_path( + "hot_output/hot_web_application.yaml") + params = {'cpus': '2', 'context_root': 'my_web_app'} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_template_with_url_import(self): + tosca_file = utils.get_template_path( + "tosca_single_instance_wordpress_with_url_import.yaml") + hot_file = utils.get_template_path( + "hot_output/hot_single_instance_wordpress.yaml") + params = {'db_name': 'wordpress', + 'db_user': 'wp_user', + 'db_pwd': 'wp_pass', + 'db_root_pwd': 'passw0rd', + 'db_port': 3366, + 'cpus': 8} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_template_by_url_with_local_import(self): + tosca_file = ("https://raw.githubusercontent.com/openstack/" + + "heat-translator/master/translator/tests/data/" + + "tosca_single_instance_wordpress.yaml") + hot_file = utils.get_template_path( + "hot_output/" + + "hot_single_instance_wordpress.yaml") + params = {'db_name': 'wordpress', + 'db_user': 'wp_user', + 'db_pwd': 'wp_pass', + 'db_root_pwd': 'passw0rd', + 'db_port': 3366, + 'cpus': 8} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_template_by_url_with_local_abspath_import(self): + tosca_file = ("https://raw.githubusercontent.com/openstack/" + + "heat-translator/master/translator/tests/data/" + + "tosca_single_instance_wordpress_with_local_abspath" + + "_import.yaml") + hot_file = utils.get_template_path( + "hot_output/" + + "hot_single_instance_wordpress.yaml") + params = {'db_name': 'wordpress', + 'db_user': 'wp_user', + 'db_pwd': 'wp_pass', + 'db_root_pwd': 'passw0rd', + 'db_port': 3366, + 'cpus': 8} + + 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".') + self._check_error(tosca_file, hot_file, params, ValidationError, + expected_msg, ImportError) + + def test_osc_translate_template_by_url_with_url_import(self): + tosca_url = ("https://raw.githubusercontent.com/openstack/" + + "heat-translator/master/translator/tests/data/" + + "tosca_single_instance_wordpress_with_url_import.yaml") + hot_file = utils.get_template_path( + "hot_output/" + + "hot_single_instance_wordpress.yaml") + params = {'db_name': 'wordpress', + 'db_user': 'wp_user', + 'db_pwd': 'wp_pass', + 'db_root_pwd': 'passw0rd', + 'db_port': 3366, + 'cpus': 8} + self._check_success(tosca_url, hot_file, params) + + def test_osc_translate_hello_world_csar(self): + tosca_file = utils.get_template_path("csar_hello_world.zip") + hot_file = utils.get_template_path( + "hot_output/hot_hello_world.yaml") + self._check_success(tosca_file, hot_file, {}) + + def test_osc_single_instance_wordpress_csar(self): + tosca_file = utils.get_template_path( + "csar_single_instance_wordpress.zip") + hot_file = utils.get_template_path( + "hot_output/" + + "hot_single_instance_wordpress_from_csar.yaml") + params = {'db_name': 'wordpress', + 'db_user': 'wp_user', + 'db_pwd': 'wp_pass', + 'db_root_pwd': 'passw0rd', + 'db_port': 3366, + 'cpus': 8} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_elk_csar_from_url(self): + tosca_file = ("https://github.com/openstack/heat-translator/raw/" + + "master/translator/tests/data/csar_elk.zip") + hot_file = utils.get_template_path( + "hot_output/hot_elk_from_csar.yaml") + params = {'github_url': + 'http://github.com/paypal/rest-api-sample-app-nodejs.git', + 'my_cpus': 4} + self._check_success(tosca_file, hot_file, params) + + def test_osc_translate_csar_not_zip(self): + tosca_file = utils.get_template_path("csar_not_zip.zip") + hot_file = '' + expected_msg = _('"%s" is not a valid zip file.') % tosca_file + self._check_error(tosca_file, hot_file, {}, ValidationError, + expected_msg, ValidationError) + + def test_osc_translate_csar_metadata_not_yaml(self): + tosca_file = utils.get_template_path("csar_metadata_not_yaml.zip") + hot_file = '' + expected_msg = _('The file "TOSCA-Metadata/TOSCA.meta" in the CSAR ' + '"%s" does not contain valid YAML' + ' content.') % tosca_file + self._check_error(tosca_file, hot_file, {}, ValidationError, + expected_msg, ValidationError) + + def test_osc_translate_csar_wrong_metadata_file(self): + tosca_file = utils.get_template_path("csar_wrong_metadata_file.zip") + hot_file = '' + + expected_msg = _('"%s" is not a valid CSAR as it does not contain the ' + 'required file "TOSCA.meta" in the folder ' + '"TOSCA-Metadata".') % tosca_file + self._check_error(tosca_file, hot_file, {}, ValidationError, + expected_msg, ValidationError) + + def test_osc_translate_csar_wordpress_invalid_import_path(self): + tosca_file = utils.get_template_path( + "csar_wordpress_invalid_import_path.zip") + hot_file = '' + expected_msg = _('Import ' + '"Invalid_import_path/wordpress.yaml" is not valid.') + self._check_error(tosca_file, hot_file, {}, ValidationError, + expected_msg, ImportError) + + def test_osc_translate_csar_wordpress_invalid_script_url(self): + tosca_file = utils.get_template_path( + "csar_wordpress_invalid_script_url.zip") + hot_file = '' + expected_msg = _('The resource at ' + '"https://raw.githubusercontent.com/openstack/' + 'heat-translator/master/translator/tests/data/' + 'custom_types/wordpress1.yaml" cannot be accessed.') + self._check_error(tosca_file, hot_file, {}, ValidationError, + expected_msg, URLException) diff --git a/tosca2heat/heat-translator/translator/osc/v1/tests/utils.py b/tosca2heat/heat-translator/translator/osc/v1/tests/utils.py new file mode 100644 index 0000000..edd45a7 --- /dev/null +++ b/tosca2heat/heat-translator/translator/osc/v1/tests/utils.py @@ -0,0 +1,19 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os + + +def get_template_path(path): + data_folder = "../../../tests/data/" + return os.path.join(os.path.dirname(os.path.abspath(__file__)), + data_folder + path) diff --git a/tosca2heat/heat-translator/translator/osc/v1/translate.py b/tosca2heat/heat-translator/translator/osc/v1/translate.py new file mode 100644 index 0000000..eeaaa18 --- /dev/null +++ b/tosca2heat/heat-translator/translator/osc/v1/translate.py @@ -0,0 +1,106 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""Translate action implementations""" + +import logging +import logging.config +import os +import sys + +from cliff import command + +from toscaparser.tosca_template import ToscaTemplate +from toscaparser.utils.gettextutils import _ +from translator.common.utils import UrlUtils +from translator.hot.tosca_translator import TOSCATranslator +from translator.osc import utils + + +logging.config.fileConfig('heat_translator_logging.conf') +log = logging.getLogger('heat-translator') + + +class TranslateTemplate(command.Command): + + """Translate a template""" + + auth_required = False + + def get_parser(self, prog_name): + parser = super(TranslateTemplate, self).get_parser(prog_name) + parser.add_argument( + '--template-file', + metavar='<template-file>', + required=True, + help='Path to the file that needs to be translated.') + parser.add_argument( + '--template-type', + metavar='<template-type>', + required=True, + choices=['tosca'], + help='Format of the template file.') + parser.add_argument( + '--output-file', + metavar='<output-file>', + help='Path to place the translated content.') + parser.add_argument( + '--parameter', + metavar='<key=value>', + action=utils.KeyValueAction, + help='Set a property for this template ' + '(repeat option to set multiple properties)', + ) + parser.add_argument( + '--validate-only', + metavar='<true or false>', + help='Set to true to only validate a template file.', + default='false') + return parser + + def take_action(self, parsed_args): + log.debug(_('Translating the template with input parameters' + '(%s).'), parsed_args) + output = None + + if parsed_args.parameter: + parsed_params = parsed_args.parameter + else: + parsed_params = {} + + if parsed_args.template_type == "tosca": + path = parsed_args.template_file + a_file = os.path.isfile(path) + a_url = UrlUtils.validate_url(path) if not a_file else False + if a_file or a_url: + validate = parsed_args.validate_only + if validate and validate.lower() == "true": + ToscaTemplate(path, parsed_params, a_file) + msg = (_('The input "%(path)s" successfully passed ' + 'validation.') % {'path': path}) + print(msg) + else: + tosca = ToscaTemplate(path, parsed_params, a_file) + translator = TOSCATranslator(tosca, parsed_params) + output = translator.translate() + else: + msg = _('Could not find template file.') + log.error(msg) + sys.stdout.write(msg) + raise SystemExit + + if output: + if parsed_args.output_file: + with open(parsed_args.output_file, 'w+') as f: + f.write(output) + else: + print(output) diff --git a/tosca2heat/heat-translator/translator/shell.py b/tosca2heat/heat-translator/translator/shell.py new file mode 100644 index 0000000..92d92d9 --- /dev/null +++ b/tosca2heat/heat-translator/translator/shell.py @@ -0,0 +1,238 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import ast +import json +import logging +import logging.config +import os +import prettytable +import requests +import sys +import uuid +import yaml + +from toscaparser.tosca_template import ToscaTemplate +from toscaparser.utils.gettextutils import _ +from toscaparser.utils.urlutils import UrlUtils +from translator.common import utils +from translator.hot.tosca_translator import TOSCATranslator + +""" +Test the heat-translator translation from command line as: +#heat-translator + --template-file=<path to the YAML template> + --template-type=<type of template e.g. tosca> + --parameters="purpose=test" +Takes three user arguments, +1. type of translation (e.g. tosca) (required) +2. Path to the file that needs to be translated (required) +3. Input parameters (optional) + +In order to use heat-translator to only validate template, +without actual translation, pass --validate-only=true along with +other required arguments. + +""" + +logging.config.fileConfig('heat_translator_logging.conf') +log = logging.getLogger("heat-translator") + + +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 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 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}) + 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) + if heat_tpl: + if utils.check_for_env_variables() and self.deploy: + try: + heatclient(heat_tpl, parsed_params) + except Exception: + log.error(_("Unable to launch the heat stack")) + + self._write_output(heat_tpl, output_file) + else: + msg = _("The path %(path)s is not a valid file or URL.") % { + 'path': path} + 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: + 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) + return parsed_inputs + + def _translate(self, sourcetype, path, parsed_params, a_file): + 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) + log.debug(_('Translating the tosca template.')) + output = translator.translate() + return output + + def _write_output(self, output, output_file=None): + if output: + if output_file: + with open(output_file, 'w+') as f: + f.write(output) + else: + print(output) + + +def heatclient(output, params): + try: + access_dict = utils.get_ks_access_dict() + endpoint = utils.get_url_for(access_dict, 'orchestration') + token = utils.get_token_id(access_dict) + except Exception as e: + log.error(e) + headers = { + 'Content-Type': 'application/json', + 'X-Auth-Token': token + } + heat_stack_name = "heat_" + str(uuid.uuid4()).split("-")[0] + output = yaml.load(output) + output['heat_template_version'] = str(output['heat_template_version']) + data = { + 'stack_name': heat_stack_name, + 'template': output, + 'parameters': params + } + response = requests.post(endpoint + '/stacks', + data=json.dumps(data), + headers=headers) + content = ast.literal_eval(response._content) + if response.status_code == 201: + stack_id = content["stack"]["id"] + get_url = endpoint + '/stacks/' + heat_stack_name + '/' + stack_id + get_stack_response = requests.get(get_url, + headers=headers) + stack_details = json.loads(get_stack_response.content)["stack"] + col_names = ["id", "stack_name", "stack_status", "creation_time", + "updated_time"] + pt = prettytable.PrettyTable(col_names) + stack_list = [] + for col in col_names: + stack_list.append(stack_details[col]) + pt.add_row(stack_list) + print(pt) + else: + err_msg = content["error"]["message"] + log(_("Unable to deploy to Heat\n%s\n") % err_msg) + + +def main(args=None): + if args is None: + args = sys.argv[1:] + TranslatorShell().main(args) + + +if __name__ == '__main__': + main() diff --git a/tosca2heat/heat-translator/translator/tests/__init__.py b/tosca2heat/heat-translator/translator/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/__init__.py diff --git a/tosca2heat/heat-translator/translator/tests/base.py b/tosca2heat/heat-translator/translator/tests/base.py new file mode 100644 index 0000000..6e93268 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/base.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- + +# Copyright 2010-2011 OpenStack Foundation +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. +# +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os + +import fixtures +import testtools + +_TRUE_VALUES = ('True', 'true', '1', 'yes') + + +class TestCase(testtools.TestCase): + + """Test case base class for all unit tests.""" + + def setUp(self): + """Run before each test method to initialize test environment.""" + + super(TestCase, self).setUp() + test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0) + try: + test_timeout = int(test_timeout) + except ValueError: + # If timeout value is invalid do not set a timeout. + test_timeout = 0 + if test_timeout > 0: + self.useFixture(fixtures.Timeout(test_timeout, gentle=True)) + + self.useFixture(fixtures.NestedTempfile()) + self.useFixture(fixtures.TempHomeDir()) + + if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES: + stdout = self.useFixture(fixtures.StringStream('stdout')).stream + self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) + if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES: + stderr = self.useFixture(fixtures.StringStream('stderr')).stream + self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) + + self.log_fixture = self.useFixture(fixtures.FakeLogger()) diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/collectd/config.py b/tosca2heat/heat-translator/translator/tests/data/artifacts/collectd/config.py new file mode 100644 index 0000000..686bbd1 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/collectd/config.py @@ -0,0 +1,25 @@ +#!/usr/bin/python + +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# This script configures collectd to send metric data to the +# logstash server port 25826 +# The environment variable logstash_ip is expected to be set up +import os +with open("/etc/collectd/collectd.conf.d/tosca_elk.conf", "w") as fh: + fh.write(""" + LoadPlugin network + <Plugin network> + Server "%s" "25826" + </Plugin> + """ % (os.environ['logstash_ip'])) diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/collectd/create.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/collectd/create.sh new file mode 100644 index 0000000..a483b88 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/collectd/create.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# This script install collectd for monitoring data + +apt-get update +apt-get install -y collectd diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/collectd/start.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/collectd/start.sh new file mode 100644 index 0000000..7e8e033 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/collectd/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# This script starts collectd as a service in init.d +service collectd stop +service collectd start diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/elasticsearch/create.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/elasticsearch/create.sh new file mode 100644 index 0000000..c34126c --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/elasticsearch/create.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# This script installs java and elasticsearch + +apt-get update +apt-get install -y openjdk-7-jre-headless + +wget -qO - https://packages.elasticsearch.org/GPG-KEY-elasticsearch | apt-key add - +echo "deb http://packages.elasticsearch.org/elasticsearch/1.5/debian stable main" | tee -a /etc/apt/sources.list + +apt-get update +apt-get install -y elasticsearch + +# set up to run as service +update-rc.d elasticsearch defaults 95 10 diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/elasticsearch/start.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/elasticsearch/start.sh new file mode 100644 index 0000000..bbc0347 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/elasticsearch/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# This script starts elasticsearch as a service in init.d +service elasticsearch stop +service elasticsearch start diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/kibana/config.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/kibana/config.sh new file mode 100644 index 0000000..f28215a --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/kibana/config.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# This script configures kibana to connect to the elasticsearch server +# to access data and to export the app url on port 5601: +# The environment variable elasticsearch_ip and kibana_ip are expected +# to be set up. +sed -i 's/localhost/'$elasticsearch_ip'/' /opt/kibana/config/kibana.yml +sed -i 's/0.0.0.0/'$kibana_ip'/' /opt/kibana/config/kibana.yml diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/kibana/create.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/kibana/create.sh new file mode 100644 index 0000000..41914b1 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/kibana/create.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# This script installs kibana and sets it up to run as a service in init.d +cd /opt +wget https://download.elastic.co/kibana/kibana/kibana-4.1.0-linux-x64.tar.gz +tar xzvf kibana-4.1.0-linux-x64.tar.gz +mv kibana-4.1.0-linux-x64 kibana + +# set up to run as service +cd /etc/init.d +wget https://gist.githubusercontent.com/thisismitch/8b15ac909aed214ad04a/raw/bce61d85643c2dcdfbc2728c55a41dab444dca20/kibana4 +chmod +x kibana4 +update-rc.d kibana4 defaults 96 9 diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/kibana/start.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/kibana/start.sh new file mode 100644 index 0000000..5149bb3 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/kibana/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# This script starts kibana as a service in init.d +service kibana4 stop +service kibana4 start diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/configure_collectd.py b/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/configure_collectd.py new file mode 100644 index 0000000..18fdacf --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/configure_collectd.py @@ -0,0 +1,28 @@ +#!/usr/bin/python + +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# This script configures the logstash input using the udp protocol on +# port 25826. This is intended to receive data from collectd from +# any source +with open("/etc/logstash/conf.d/collectd.conf", "w") as fh: + fh.write(""" + input { + udp { + port => 25826 # 25826 is the default for collectd + buffer_size => 1452 # 1452 is the default for collectd + codec => collectd { } + tags => ["metrics"] + type => "collectd" + } + }""") diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/configure_elasticsearch.py b/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/configure_elasticsearch.py new file mode 100644 index 0000000..2e5389c --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/configure_elasticsearch.py @@ -0,0 +1,26 @@ +#!/usr/bin/python + +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# This script configures the logstash output to forward to elasticsearch +# The environment variable elasticsearch_ip is expected to be set up +import os +with open("/etc/logstash/conf.d/elasticsearch.conf", 'w') as fh: + fh.write(""" + output { + elasticsearch { + action => index + host => "%s" + protocol => "http" + } + }""" % (os.environ['elasticsearch_ip'])) diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/configure_rsyslog.py b/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/configure_rsyslog.py new file mode 100644 index 0000000..fc610c2 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/configure_rsyslog.py @@ -0,0 +1,25 @@ +#!/usr/bin/python + +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# This script configures the logstash input using the RELP protocol on +# port 2514 This is intended to receive logs from rsyslog from +# any source +with open("/etc/logstash/conf.d/rsyslog.conf", "w") as fh: + fh.write(""" + input { + relp { + port => 2514 + tags => ["logs"] + } + }""") diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/create.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/create.sh new file mode 100644 index 0000000..77cc8fd --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/create.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# This script installs java, logstash and the contrib package for logstash +# install java as prereq + +apt-get update +apt-get install -y openjdk-7-jre-headless +mkdir /etc/logstash + +# install by apt-get from repo +wget -O - http://packages.elasticsearch.org/GPG-KEY-elasticsearch | apt-key add - +echo "deb http://packages.elasticsearch.org/logstash/1.4/debian stable main" | tee -a /etc/apt/sources.list + +apt-get update +apt-get install -y logstash + +# install contrib to get the relp plugin +/opt/logstash/bin/plugin install contrib + +# set up to run as service +update-rc.d logstash defaults 95 10 diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/start.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/start.sh new file mode 100644 index 0000000..a73cf61 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/logstash/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# Run logstash as service in init.d +service logstash stop +service logstash start diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/mongodb/config.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/mongodb/config.sh new file mode 100644 index 0000000..78f484e --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/mongodb/config.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# Edit the file /etc/mongod.conf, update with real IP of Mongo server +# This script configures the mongodb server to export its service on +# the server IP +# bind_ip = 127.0.0.1 -> bind_ip = <IP for Mongo server> +# The environment variable mongodb_ip is expected to be set up +sed -i "s/= 127.0.0.1/= $mongodb_ip,127.0.0.1/" /etc/mongod.conf diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/mongodb/create.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/mongodb/create.sh new file mode 100644 index 0000000..d84c275 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/mongodb/create.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# This script installs mongodb + +apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 +echo "deb http://repo.mongodb.org/apt/ubuntu "$(lsb_release -sc)"/mongodb-org/3.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-3.0.list + +apt-get update +apt-get install -y mongodb-org + +#Wait for mongodb initialization +while [[ ! -d "/var/lib/mongodb/_tmp" ]]; do + echo "Waiting for mongodb initialization ..." + sleep 5 +done diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/mongodb/create_database.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/mongodb/create_database.sh new file mode 100644 index 0000000..16f1358 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/mongodb/create_database.sh @@ -0,0 +1,5 @@ +#!/bin/bash +echo "conn = new Mongo();" > setup.js +echo "db = conn.getDB('paypal_pizza');" >> setup.js +echo "db.about.insert({'name': 'PayPal Pizza Store'});" >> setup.js +mongo setup.js diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/mongodb/start.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/mongodb/start.sh new file mode 100644 index 0000000..ac200a5 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/mongodb/start.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# This script starts mongodb +service mongod stop +rm /var/lib/mongodb/mongod.lock +service mongod start diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/mysql/mysql_database_configure.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/mysql/mysql_database_configure.sh new file mode 100644 index 0000000..092136a --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/mysql/mysql_database_configure.sh @@ -0,0 +1,8 @@ +#!/bin/sh +cat << EOF | mysql -u root --password=$db_root_password +CREATE DATABASE $db_name; +GRANT ALL PRIVILEGES ON $db_name.* TO "$db_user"@"localhost" +IDENTIFIED BY "$db_password"; +FLUSH PRIVILEGES; +EXIT +EOF
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/mysql/mysql_dbms_configure.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/mysql/mysql_dbms_configure.sh new file mode 100644 index 0000000..d4ef6b4 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/mysql/mysql_dbms_configure.sh @@ -0,0 +1,5 @@ +#!/bin/sh +sed --regexp-extended "s/(port\s*=\s*)[0-9]*/\1$db_port/g" </etc/mysql/my.cnf >/tmp/my.cnf +mv -f /tmp/my.cnf /etc/mysql/my.cnf +/etc/init.d/mysql stop +/etc/init.d/mysql start
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/mysql/mysql_dbms_install.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/mysql/mysql_dbms_install.sh new file mode 100644 index 0000000..38628b9 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/mysql/mysql_dbms_install.sh @@ -0,0 +1,9 @@ +#!/bin/bash +#This script installs mysql server + +apt-get update + +debconf-set-selections <<< "mysql-server mysql-server/root_password password $db_root_password" +debconf-set-selections <<< "mysql-server mysql-server/root_password_again password $db_root_password" + +apt-get -y install --fix-missing mysql-server
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/mysql/mysql_dbms_start.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/mysql/mysql_dbms_start.sh new file mode 100644 index 0000000..3378670 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/mysql/mysql_dbms_start.sh @@ -0,0 +1,2 @@ +#!/bin/sh +/etc/init.d/mysql start
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/nodejs/config.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/nodejs/config.sh new file mode 100644 index 0000000..1e149a2 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/nodejs/config.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# This script installs an app for nodejs: the app intended is the paypal app +# and it is configured to connect to the mongodb server +# The environment variables github_url and mongodb_ip are expected to be set up +export app_dir=/opt/app +git clone $github_url /opt/app +if [ -f /opt/app/package.json ]; then + cd /opt/app/ && npm install + sed -i "s/localhost/$mongodb_ip/" config.json +fi + +cat > /etc/init/nodeapp.conf <<EOS +description "node.js app" + +start on (net-device-up + and local-filesystems + and runlevel [2345]) +stop on runlevel [!2345] + +expect fork +respawn + +script + export HOME=/ + export NODE_PATH=/usr/lib/node + exec /usr/bin/node ${app_dir}/app.js >> /var/log/nodeapp.log 2>&1 & +end script +EOS diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/nodejs/create.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/nodejs/create.sh new file mode 100644 index 0000000..04fd6c6 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/nodejs/create.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# This script installs nodejs and the prereq + +add-apt-repository ppa:chris-lea/node.js + +apt-get update +apt-get install -y nodejs build-essential diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/nodejs/start.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/nodejs/start.sh new file mode 100644 index 0000000..6939cb7 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/nodejs/start.sh @@ -0,0 +1,3 @@ +#!/bin/bash +# This script starts the nodejs application +start nodeapp diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/rsyslog/config.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/rsyslog/config.sh new file mode 100644 index 0000000..630767d --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/rsyslog/config.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# This script configures the output for rsyslogd to send logs to the +# logstash server port 2514 using the RELP protocol +# The environment variable logstash_ip is expected to be set up +echo "module(load=\"omrelp\") +action(type=\"omrelp\" target=\"$logstash_ip\" port=\"2514\")" > /etc/rsyslog.d/tosca_elk.conf + +# Remove the /dev/xconsole configuration as xconsole +# is not available by default +l=`awk '/=warn.*\|.*\/dev\/xconsole/{print NR - 1}' /etc/rsyslog.d/50-default.conf` +if [ ! -z $l ]; then + l=`expr $l + 1` + line=`cat /etc/rsyslog.d/50-default.conf | head -n $l | tail -1` + if [[ ! $line == \#* ]]; then + l0=`expr $l - 3` + sed -i -r -e "${l0},${l}s/^.{0}/&#/" /etc/rsyslog.d/50-default.conf + fi +fi + +# Enable nodejs logs for rsyslog +if ! grep -q nodeapp "/etc/rsyslog.conf"; then + sed -i 's/\$PrivDropToGroup\ syslog/\$PrivDropToGroup adm/' /etc/rsyslog.conf + echo "\$ModLoad imfile.so +\$InputFileName /var/log/nodeapp.log +\$InputFileTag paypal_pizza: +\$InputFileStateFile stat-nodeapp +\$InputRunFileMonitor +\$InputFilePollInterval 1" >> /etc/rsyslog.conf +fi diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/rsyslog/create.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/rsyslog/create.sh new file mode 100644 index 0000000..affdd6e --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/rsyslog/create.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# This script installs rsyslog and the library for RELP + +apt-get update +apt-get install -y rsyslog rsyslog-relp diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/rsyslog/start.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/rsyslog/start.sh new file mode 100644 index 0000000..3de82d1 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/rsyslog/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# This script starts rsyslogd as a service in init.d +service rsyslog stop +service rsyslog start diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/webserver/webserver_install.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/webserver/webserver_install.sh new file mode 100644 index 0000000..4ca9b4e --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/webserver/webserver_install.sh @@ -0,0 +1,5 @@ +#!/bin/sh +#This script installs apache web server + +apt-get update +apt-get install -y apache2
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/webserver/webserver_start.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/webserver/webserver_start.sh new file mode 100644 index 0000000..e962ca5 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/webserver/webserver_start.sh @@ -0,0 +1,2 @@ +#!/bin/sh +service apache2 start
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/wordpress/wordpress_configure.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/wordpress/wordpress_configure.sh new file mode 100644 index 0000000..5598b4f --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/wordpress/wordpress_configure.sh @@ -0,0 +1,4 @@ +#!/bin/sh +ln -s /usr/share/wordpress /var/www/html/wordpress +gzip -d /usr/share/doc/wordpress/examples/setup-mysql.gz +echo $wp_db_password | bash /usr/share/doc/wordpress/examples/setup-mysql -e $wp_db_name -u $wp_db_user localhost
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/artifacts/wordpress/wordpress_install.sh b/tosca2heat/heat-translator/translator/tests/data/artifacts/wordpress/wordpress_install.sh new file mode 100644 index 0000000..1320443 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/artifacts/wordpress/wordpress_install.sh @@ -0,0 +1,5 @@ +#!/bin/sh +#This script installs wordpress + +apt-get update +apt-get install -y wordpress
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/csar_elk.zip b/tosca2heat/heat-translator/translator/tests/data/csar_elk.zip Binary files differnew file mode 100644 index 0000000..5fae801 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/csar_elk.zip diff --git a/tosca2heat/heat-translator/translator/tests/data/csar_hello_world.zip b/tosca2heat/heat-translator/translator/tests/data/csar_hello_world.zip Binary files differnew file mode 100644 index 0000000..43ffbbc --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/csar_hello_world.zip diff --git a/tosca2heat/heat-translator/translator/tests/data/csar_metadata_not_yaml.zip b/tosca2heat/heat-translator/translator/tests/data/csar_metadata_not_yaml.zip Binary files differnew file mode 100644 index 0000000..3e6120b --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/csar_metadata_not_yaml.zip diff --git a/tosca2heat/heat-translator/translator/tests/data/csar_not_zip.zip b/tosca2heat/heat-translator/translator/tests/data/csar_not_zip.zip new file mode 100644 index 0000000..43b7f5f --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/csar_not_zip.zip @@ -0,0 +1 @@ +This is an invalid CSAR file.
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/csar_single_instance_wordpress.zip b/tosca2heat/heat-translator/translator/tests/data/csar_single_instance_wordpress.zip Binary files differnew file mode 100644 index 0000000..17e655e --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/csar_single_instance_wordpress.zip diff --git a/tosca2heat/heat-translator/translator/tests/data/csar_wordpress_invalid_import_path.zip b/tosca2heat/heat-translator/translator/tests/data/csar_wordpress_invalid_import_path.zip Binary files differnew file mode 100644 index 0000000..9dc6c9a --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/csar_wordpress_invalid_import_path.zip diff --git a/tosca2heat/heat-translator/translator/tests/data/csar_wordpress_invalid_script_url.zip b/tosca2heat/heat-translator/translator/tests/data/csar_wordpress_invalid_script_url.zip Binary files differnew file mode 100644 index 0000000..6014f92 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/csar_wordpress_invalid_script_url.zip diff --git a/tosca2heat/heat-translator/translator/tests/data/csar_wrong_metadata_file.zip b/tosca2heat/heat-translator/translator/tests/data/csar_wrong_metadata_file.zip Binary files differnew file mode 100644 index 0000000..85d660a --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/csar_wrong_metadata_file.zip diff --git a/tosca2heat/heat-translator/translator/tests/data/custom_types/collectd.yaml b/tosca2heat/heat-translator/translator/tests/data/custom_types/collectd.yaml new file mode 100644 index 0000000..1ac0935 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/custom_types/collectd.yaml @@ -0,0 +1,13 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + collectd is a daemon which gathers statistics about the system it is running on. + +node_types: + tosca.nodes.SoftwareComponent.Collectd: + derived_from: tosca.nodes.SoftwareComponent + requirements: + - log_endpoint: + capability: tosca.capabilities.Endpoint + node: tosca.nodes.SoftwareComponent.Logstash + relationship: tosca.relationships.ConnectsTo
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/custom_types/elasticsearch.yaml b/tosca2heat/heat-translator/translator/tests/data/custom_types/elasticsearch.yaml new file mode 100644 index 0000000..b140a32 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/custom_types/elasticsearch.yaml @@ -0,0 +1,12 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + Elasticsearch is an open-source search engine built on top of Apache Lucene, + a full-text search-engine library. + +node_types: + tosca.nodes.SoftwareComponent.Elasticsearch: + derived_from: tosca.nodes.SoftwareComponent + capabilities: + search_endpoint: + type: tosca.capabilities.Endpoint diff --git a/tosca2heat/heat-translator/translator/tests/data/custom_types/kibana.yaml b/tosca2heat/heat-translator/translator/tests/data/custom_types/kibana.yaml new file mode 100644 index 0000000..5701e69 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/custom_types/kibana.yaml @@ -0,0 +1,14 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + Kibana is an open source analytics and visualization platform designed to work with Elasticsearch. + You use Kibana to search, view, and interact with data stored in Elasticsearch. + +node_types: + tosca.nodes.SoftwareComponent.Kibana: + derived_from: tosca.nodes.SoftwareComponent + requirements: + - search_endpoint: + capability: tosca.capabilities.Endpoint + node: tosca.nodes.SoftwareComponent.Elasticsearch + relationship: tosca.relationships.ConnectsTo diff --git a/tosca2heat/heat-translator/translator/tests/data/custom_types/logstash.yaml b/tosca2heat/heat-translator/translator/tests/data/custom_types/logstash.yaml new file mode 100644 index 0000000..cf60521 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/custom_types/logstash.yaml @@ -0,0 +1,25 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + Logstash is a tool for receiving, processing and outputting logs. All kinds + of logs. System logs, webserver logs, error logs, application logs, and just + about anything you can throw at it. + +node_types: + tosca.nodes.SoftwareComponent.Logstash: + derived_from: tosca.nodes.SoftwareComponent + requirements: + - search_endpoint: + capability: tosca.capabilities.Endpoint + node: tosca.nodes.SoftwareComponent.Elasticsearch + relationship: + type: tosca.relationships.ConnectsTo + interfaces: + Configure: + pre_configure_source: + inputs: + elasticsearch_ip: + type: string + capabilities: + log_endpoint: + type: tosca.capabilities.Endpoint diff --git a/tosca2heat/heat-translator/translator/tests/data/custom_types/paypalpizzastore_nodejs_app.yaml b/tosca2heat/heat-translator/translator/tests/data/custom_types/paypalpizzastore_nodejs_app.yaml new file mode 100644 index 0000000..d62c4c1 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/custom_types/paypalpizzastore_nodejs_app.yaml @@ -0,0 +1,29 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + Pizza store app that allows you to explore the features provided by PayPal's REST APIs. + More detail can be found at https://github.com/paypal/rest-api-sample-app-nodejs/ + +node_types: + tosca.nodes.WebApplication.PayPalPizzaStore: + derived_from: tosca.nodes.WebApplication + properties: + github_url: + required: no + type: string + description: location of the application on the github. + default: https://github.com/sample.git + requirements: + #WebApplication inherits Computer, so host implied. + - database_connection: + capability: tosca.capabilities.Endpoint.Database + node: tosca.nodes.Database + relationship: tosca.relationships.ConnectsTo + interfaces: + Standard: + configure: + inputs: + github_url: + type: string + mongodb_ip: + type: string diff --git a/tosca2heat/heat-translator/translator/tests/data/custom_types/rsyslog.yaml b/tosca2heat/heat-translator/translator/tests/data/custom_types/rsyslog.yaml new file mode 100644 index 0000000..4614ee7 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/custom_types/rsyslog.yaml @@ -0,0 +1,13 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + RSYSLOG is the Rocket-fast SYStem for LOG processing. + +node_types: + tosca.nodes.SoftwareComponent.Rsyslog: + derived_from: tosca.nodes.SoftwareComponent + requirements: + - log_endpoint: + capability: tosca.capabilities.Endpoint + node: tosca.nodes.SoftwareComponent.Logstash + relationship: tosca.relationships.ConnectsTo diff --git a/tosca2heat/heat-translator/translator/tests/data/custom_types/wordpress.yaml b/tosca2heat/heat-translator/translator/tests/data/custom_types/wordpress.yaml new file mode 100644 index 0000000..5899ed9 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/custom_types/wordpress.yaml @@ -0,0 +1,19 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +node_types: + tosca.nodes.WebApplication.WordPress: + derived_from: tosca.nodes.WebApplication + requirements: + - database_endpoint: + capability: tosca.capabilities.Endpoint.Database + node: tosca.nodes.Database + relationship: tosca.relationships.ConnectsTo + interfaces: + Standard: + inputs: + wp_db_name: + type: string + wp_db_user: + type: string + wp_db_password: + type: string diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_artifact.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_artifact.yaml new file mode 100644 index 0000000..7284116 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_artifact.yaml @@ -0,0 +1,30 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA template to test artifact usage + +parameters: {} +resources: + customwebserver_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: customwebserver_create_config + input_values: + content: + get_file: http://www.mycompany.org/content.tgz + 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 + customwebserver_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: install.sh + group: script +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_custom_type.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_custom_type.yaml new file mode 100644 index 0000000..9f722cc --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_custom_type.yaml @@ -0,0 +1,34 @@ + +heat_template_version: 2013-05-23 + +description: > + TOSCA template to test custom type with an interface defined on it + +parameters: + install_path: + type: string + default: /home/custom/other +resources: + customwebserver_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: customwebserver_create_config + input_values: + path: + get_param: install_path + 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 + customwebserver_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: install.sh + group: script +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_custom_type_with_override.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_custom_type_with_override.yaml new file mode 100644 index 0000000..24a2893 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_custom_type_with_override.yaml @@ -0,0 +1,34 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA template to test custom type with an interface defined on it, + and an interface overriding the type's one on the template itself + +parameters: + install_path: + type: string + default: /home/custom/other +resources: + customwebserver_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: customwebserver_create_config + input_values: + path: + get_param: install_path + 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 + customwebserver_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: install_override.sh + group: script +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_custom_type_with_param_override.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_custom_type_with_param_override.yaml new file mode 100644 index 0000000..b166d7c --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_custom_type_with_param_override.yaml @@ -0,0 +1,34 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA template to test custom type with an interface defined on it, + and an interface overriding the type's one on the template itself + +parameters: + install_path: + type: string + default: /home/custom/from/cli +resources: + customwebserver_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: customwebserver_create_config + input_values: + path: + get_param: install_path + 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 + customwebserver_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: install_override.sh + group: script +outputs: {}
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_elk.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_elk.yaml new file mode 100644 index 0000000..a298745 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_elk.yaml @@ -0,0 +1,551 @@ +heat_template_version: 2013-05-23 + +description: > + This TOSCA simple profile deploys nodejs, mongodb, elasticsearch, logstash and + kibana each on a separate server with monitoring enabled for nodejs server where + a sample nodejs application is running. The rsyslog and collectd are installed + on a nodejs server. + +parameters: + github_url: + type: string + description: The URL to download nodejs. + default: http://github.com/paypal/rest-api-sample-app-nodejs.git + + my_cpus: + type: number + description: Number of CPUs for the server. + default: 4 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + +resources: + + nodejs_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: nodejs_create_config + server: + get_resource: app_server + + nodejs_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: nodejs/create.sh + group: script + + paypal_pizzastore_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: paypal_pizzastore_configure_config + input_values: + github_url: + get_param: github_url + mongodb_ip: + get_attr: + - mongo_server + - networks + - private + - 0 + server: + get_resource: app_server + depends_on: + - nodejs_create_deploy + - mongo_db_create_deploy + + paypal_pizzastore_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: nodejs/config.sh + group: script + + paypal_pizzastore_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: paypal_pizzastore_start_config + server: + get_resource: app_server + depends_on: + - paypal_pizzastore_configure_deploy + + paypal_pizzastore_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: nodejs/start.sh + group: script + + + mongo_dbms_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mongo_dbms_create_config + server: + get_resource: mongo_server + + mongo_dbms_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: mongodb/create.sh + group: script + + mongo_dbms_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mongo_dbms_configure_config + input_values: + mongodb_ip: + get_attr: + - mongo_server + - networks + - private + - 0 + server: + get_resource: mongo_server + depends_on: + - mongo_dbms_create_deploy + + mongo_dbms_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: mongodb/config.sh + group: script + + mongo_dbms_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mongo_dbms_start_config + server: + get_resource: mongo_server + depends_on: + - mongo_dbms_configure_deploy + + mongo_dbms_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: mongodb/start.sh + group: script + + mongo_db_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mongo_db_create_config + server: + get_resource: mongo_server + depends_on: + - mongo_dbms_start_deploy + + mongo_db_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: mongodb/create_database.sh + group: script + + + app_collectd_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_collectd_create_config + server: + get_resource: app_server + depends_on: + - logstash_start_deploy + + app_collectd_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: collectd/create.sh + group: script + + app_collectd_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_collectd_configure_config + input_values: + logstash_ip: + get_attr: + - logstash_server + - networks + - private + - 0 + server: + get_resource: app_server + depends_on: + - app_collectd_create_deploy + + app_collectd_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: collectd/config.py + group: script + + app_collectd_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_collectd_start_config + server: + get_resource: app_server + depends_on: + - app_collectd_configure_deploy + + app_collectd_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: collectd/start.sh + group: script + + app_collectd_logstash_connect_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_collectd_logstash_connect_config + server: + get_resource: logstash_server + depends_on: + - logstash_create_deploy + + app_collectd_logstash_connect_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: logstash/configure_collectd.py + group: script + + + app_rsyslog_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_rsyslog_create_config + server: + get_resource: app_server + depends_on: + - logstash_start_deploy + + app_rsyslog_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: rsyslog/create.sh + group: script + + app_rsyslog_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_rsyslog_start_config + server: + get_resource: app_server + depends_on: + - app_rsyslog_configure_deploy + + app_rsyslog_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: rsyslog/start.sh + group: script + + app_rsyslog_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_rsyslog_configure_config + input_values: + logstash_ip: + get_attr: + - logstash_server + - networks + - private + - 0 + server: + get_resource: app_server + depends_on: + - app_rsyslog_create_deploy + + app_rsyslog_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: rsyslog/config.sh + group: script + + app_rsyslog_logstash_connect_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_rsyslog_logstash_connect_config + server: + get_resource: logstash_server + depends_on: + - logstash_create_deploy + + app_rsyslog_logstash_connect_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: logstash/configure_rsyslog.py + group: script + + + logstash_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: logstash_create_config + server: + get_resource: logstash_server + depends_on: + - elasticsearch_start_deploy + + logstash_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: logstash/create.sh + group: script + + logstash_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: logstash_start_config + server: + get_resource: logstash_server + depends_on: + - logstash_create_deploy + + logstash_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: logstash/start.sh + group: script + + logstash_elasticsearch_connect_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: logstash_elasticsearch_connect_config + input_values: + elasticsearch_ip: + get_attr: + - elasticsearch_server + - networks + - private + - 0 + server: + get_resource: logstash_server + depends_on: + - logstash_create_deploy + + logstash_elasticsearch_connect_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: logstash/configure_elasticsearch.py + group: script + + + elasticsearch_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: elasticsearch_create_config + server: + get_resource: elasticsearch_server + + elasticsearch_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: elasticsearch/create.sh + group: script + + elasticsearch_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: elasticsearch_start_config + server: + get_resource: elasticsearch_server + depends_on: + - elasticsearch_create_deploy + + elasticsearch_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: elasticsearch/start.sh + group: script + + + kibana_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: kibana_create_config + server: + get_resource: kibana_server + depends_on: + - elasticsearch_start_deploy + + kibana_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: kibana/create.sh + group: script + + kibana_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: kibana_configure_config + input_values: + elasticsearch_ip: + get_attr: + - elasticsearch_server + - networks + - private + - 0 + kibana_ip: + get_attr: + - kibana_server + - networks + - private + - 0 + server: + get_resource: kibana_server + depends_on: + - kibana_create_deploy + + kibana_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: kibana/config.sh + group: script + + kibana_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: kibana_start_config + server: + get_resource: kibana_server + depends_on: + - kibana_configure_deploy + + kibana_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: kibana/start.sh + group: script + + + app_server: + type: OS::Nova::Server + properties: + flavor: m1.large + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + mongo_server: + type: OS::Nova::Server + properties: + flavor: m1.large + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + logstash_server: + type: OS::Nova::Server + properties: + flavor: m1.large + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + elasticsearch_server: + type: OS::Nova::Server + properties: + flavor: m1.large + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + kibana_server: + type: OS::Nova::Server + properties: + flavor: m1.large + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + +outputs: + nodejs_url: + description: URL for the nodejs server, http://<IP>:3000 + value: + get_attr: + - app_server + - networks + - private + - 0 + + mongodb_url: + description: URL for the mongodb server. + value: + get_attr: + - mongo_server + - networks + - private + - 0 + + logstash_url: + description: URL for the logstash server. + value: + get_attr: + - logstash_server + - networks + - private + - 0 + + elasticsearch_url: + description: URL for the elasticsearch server. + value: + get_attr: + - elasticsearch_server + - networks + - private + - 0 + + kibana_url: + description: URL for the kibana server. + value: + get_attr: + - kibana_server + - networks + - private + - 0 + diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_elk_from_csar.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_elk_from_csar.yaml new file mode 100644 index 0000000..5eb1701 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_elk_from_csar.yaml @@ -0,0 +1,551 @@ +heat_template_version: 2013-05-23 + +description: > + This TOSCA simple profile deploys nodejs, mongodb, elasticsearch, logstash and + kibana each on a separate server with monitoring enabled for nodejs server where + a sample nodejs application is running. The rsyslog and collectd are installed + on a nodejs server. + +parameters: + github_url: + type: string + description: The URL to download nodejs. + default: http://github.com/paypal/rest-api-sample-app-nodejs.git + + my_cpus: + type: number + description: Number of CPUs for the server. + default: 4 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + +resources: + + nodejs_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: nodejs_create_config + server: + get_resource: app_server + + nodejs_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/nodejs/create.sh + group: script + + paypal_pizzastore_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: paypal_pizzastore_configure_config + input_values: + github_url: + get_param: github_url + mongodb_ip: + get_attr: + - mongo_server + - networks + - private + - 0 + server: + get_resource: app_server + depends_on: + - nodejs_create_deploy + - mongo_db_create_deploy + + paypal_pizzastore_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/nodejs/config.sh + group: script + + paypal_pizzastore_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: paypal_pizzastore_start_config + server: + get_resource: app_server + depends_on: + - paypal_pizzastore_configure_deploy + + paypal_pizzastore_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/nodejs/start.sh + group: script + + + mongo_dbms_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mongo_dbms_create_config + server: + get_resource: mongo_server + + mongo_dbms_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/mongodb/create.sh + group: script + + mongo_dbms_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mongo_dbms_configure_config + input_values: + mongodb_ip: + get_attr: + - mongo_server + - networks + - private + - 0 + server: + get_resource: mongo_server + depends_on: + - mongo_dbms_create_deploy + + mongo_dbms_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/mongodb/config.sh + group: script + + mongo_dbms_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mongo_dbms_start_config + server: + get_resource: mongo_server + depends_on: + - mongo_dbms_configure_deploy + + mongo_dbms_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/mongodb/start.sh + group: script + + mongo_db_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mongo_db_create_config + server: + get_resource: mongo_server + depends_on: + - mongo_dbms_start_deploy + + mongo_db_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/mongodb/create_database.sh + group: script + + + app_collectd_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_collectd_create_config + server: + get_resource: app_server + depends_on: + - logstash_start_deploy + + app_collectd_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/collectd/create.sh + group: script + + app_collectd_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_collectd_configure_config + input_values: + logstash_ip: + get_attr: + - logstash_server + - networks + - private + - 0 + server: + get_resource: app_server + depends_on: + - app_collectd_create_deploy + + app_collectd_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Python/collectd/config.py + group: script + + app_collectd_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_collectd_start_config + server: + get_resource: app_server + depends_on: + - app_collectd_configure_deploy + + app_collectd_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/collectd/start.sh + group: script + + app_collectd_logstash_connect_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_collectd_logstash_connect_config + server: + get_resource: logstash_server + depends_on: + - logstash_create_deploy + + app_collectd_logstash_connect_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Python/logstash/configure_collectd.py + group: script + + + app_rsyslog_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_rsyslog_create_config + server: + get_resource: app_server + depends_on: + - logstash_start_deploy + + app_rsyslog_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/rsyslog/create.sh + group: script + + app_rsyslog_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_rsyslog_start_config + server: + get_resource: app_server + depends_on: + - app_rsyslog_configure_deploy + + app_rsyslog_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/rsyslog/start.sh + group: script + + app_rsyslog_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_rsyslog_configure_config + input_values: + logstash_ip: + get_attr: + - logstash_server + - networks + - private + - 0 + server: + get_resource: app_server + depends_on: + - app_rsyslog_create_deploy + + app_rsyslog_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/rsyslog/config.sh + group: script + + app_rsyslog_logstash_connect_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_rsyslog_logstash_connect_config + server: + get_resource: logstash_server + depends_on: + - logstash_create_deploy + + app_rsyslog_logstash_connect_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Python/logstash/configure_rsyslog.py + group: script + + + logstash_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: logstash_create_config + server: + get_resource: logstash_server + depends_on: + - elasticsearch_start_deploy + + logstash_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/logstash/create.sh + group: script + + logstash_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: logstash_start_config + server: + get_resource: logstash_server + depends_on: + - logstash_create_deploy + + logstash_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/logstash/start.sh + group: script + + logstash_elasticsearch_connect_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: logstash_elasticsearch_connect_config + input_values: + elasticsearch_ip: + get_attr: + - elasticsearch_server + - networks + - private + - 0 + server: + get_resource: logstash_server + depends_on: + - logstash_create_deploy + + logstash_elasticsearch_connect_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Python/logstash/configure_elasticsearch.py + group: script + + + elasticsearch_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: elasticsearch_create_config + server: + get_resource: elasticsearch_server + + elasticsearch_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/elasticsearch/create.sh + group: script + + elasticsearch_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: elasticsearch_start_config + server: + get_resource: elasticsearch_server + depends_on: + - elasticsearch_create_deploy + + elasticsearch_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/elasticsearch/start.sh + group: script + + + kibana_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: kibana_create_config + server: + get_resource: kibana_server + depends_on: + - elasticsearch_start_deploy + + kibana_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/kibana/create.sh + group: script + + kibana_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: kibana_configure_config + input_values: + elasticsearch_ip: + get_attr: + - elasticsearch_server + - networks + - private + - 0 + kibana_ip: + get_attr: + - kibana_server + - networks + - private + - 0 + server: + get_resource: kibana_server + depends_on: + - kibana_create_deploy + + kibana_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/kibana/config.sh + group: script + + kibana_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: kibana_start_config + server: + get_resource: kibana_server + depends_on: + - kibana_configure_deploy + + kibana_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/kibana/start.sh + group: script + + + app_server: + type: OS::Nova::Server + properties: + flavor: m1.large + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + mongo_server: + type: OS::Nova::Server + properties: + flavor: m1.large + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + logstash_server: + type: OS::Nova::Server + properties: + flavor: m1.large + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + elasticsearch_server: + type: OS::Nova::Server + properties: + flavor: m1.large + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + kibana_server: + type: OS::Nova::Server + properties: + flavor: m1.large + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + +outputs: + nodejs_url: + description: URL for the nodejs server, http://<IP>:3000 + value: + get_attr: + - app_server + - networks + - private + - 0 + + mongodb_url: + description: URL for the mongodb server. + value: + get_attr: + - mongo_server + - networks + - private + - 0 + + logstash_url: + description: URL for the logstash server. + value: + get_attr: + - logstash_server + - networks + - private + - 0 + + elasticsearch_url: + description: URL for the elasticsearch server. + value: + get_attr: + - elasticsearch_server + - networks + - private + - 0 + + kibana_url: + description: URL for the kibana server. + value: + get_attr: + - kibana_server + - networks + - private + - 0 + diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_flavor_and_image.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_flavor_and_image.yaml new file mode 100644 index 0000000..da8285e --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_flavor_and_image.yaml @@ -0,0 +1,18 @@ +heat_template_version: 2013-05-23 + +description: > + Template for deploying a server with custom properties for image, flavor and key_name. + +parameters: + key_name: + type: string + default: inputkey +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: rhel-6.5-test-image + key_name: {get_param: key_name} + user_data_format: SOFTWARE_CONFIG +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_flavor_and_image_params.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_flavor_and_image_params.yaml new file mode 100644 index 0000000..679461c --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_flavor_and_image_params.yaml @@ -0,0 +1,18 @@ +heat_template_version: 2013-05-23 + +description: > + Template for deploying a server with custom properties for image, flavor and key_name. + +parameters: + key_name: + type: string + default: paramkey +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: rhel-6.5-test-image + key_name: {get_param: key_name} + user_data_format: SOFTWARE_CONFIG +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_hello_world.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_hello_world.yaml new file mode 100644 index 0000000..8cb4081 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_hello_world.yaml @@ -0,0 +1,14 @@ +heat_template_version: 2013-05-23 + +description: > + Template for deploying a single server with predefined properties. + +parameters: {} +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: rhel-6.5-test-image + user_data_format: SOFTWARE_CONFIG +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_hello_world_userkey.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_hello_world_userkey.yaml new file mode 100644 index 0000000..e5fadb0 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_hello_world_userkey.yaml @@ -0,0 +1,19 @@ +heat_template_version: 2013-05-23 + +description: > + Template for deploying a single server with predefined properties. + +parameters: + key_name: + type: string + default: userkey + +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: rhel-6.5-test-image + key_name: { get_param: key_name } + user_data_format: SOFTWARE_CONFIG +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_host_assignment.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_host_assignment.yaml new file mode 100644 index 0000000..33f3059 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_host_assignment.yaml @@ -0,0 +1,135 @@ +heat_template_version: 2013-05-23 + +description: > + A template to test host assignment for translated hot resources. + It makes sure if a resource depends on multiple hosts only the + one with the "HostedOn" relationship is picked as the host. In + this template, the translated resource 'app_collectd_create_deploy' + would depend on 'logstash_server' and 'app_server'. But it would + have "HostedOn" relationship with 'app_server', and that server + would be its host. + +parameters: {} +resources: + app_server: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + logstash_server: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + app_collectd_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: collectd/create.sh + group: script + + app_collectd_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_collectd_create_config + server: + get_resource: app_server + depends_on: + - logstash_start_deploy + + app_collectd_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: collectd/config.py + group: script + + app_collectd_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_collectd_configure_config + input_values: + logstash_ip: + get_attr: + - logstash_server + - networks + - private + - 0 + server: + get_resource: app_server + depends_on: + - app_collectd_create_deploy + + app_collectd_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: collectd/start.sh + group: script + + app_collectd_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_collectd_start_config + server: + get_resource: app_server + depends_on: + - app_collectd_configure_deploy + + logstash_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: logstash/create.sh + group: script + + logstash_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: logstash_create_config + server: + get_resource: logstash_server + + logstash_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: logstash/start.sh + group: script + + logstash_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: logstash_start_config + server: + get_resource: logstash_server + depends_on: + - logstash_create_deploy + + app_collectd_logstash_connect_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: logstash/configure_collectd.py + group: script + + app_collectd_logstash_connect_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: app_collectd_logstash_connect_config + server: + get_resource: logstash_server + depends_on: + - logstash_create_deploy + +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_nfv_sample.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_nfv_sample.yaml new file mode 100644 index 0000000..2103d43 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_nfv_sample.yaml @@ -0,0 +1,35 @@ +heat_template_version: 2013-05-23 + +description: > + Template for deploying a single server with predefined properties. + +parameters: {} +resources: + VDU1: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: rhel-6.5-test-image + networks: + - port: { get_resource: CP1 } + user_data_format: SOFTWARE_CONFIG + CP1: + type: OS::Neutron::Port + properties: + fixed_ips: + - ip_address: '192.168.0.55' + network: { get_resource: VL1 } + VL1: + type: OS::Neutron::Net + VL1_subnet: + type: OS::Neutron::Subnet + properties: + ip_version: 4 + allocation_pools: + - end: 192.168.0.200 + start: 192.168.0.50 + gateway_ip: 192.168.0.1 + cidr: 192.168.0.0/24 + network: { get_resource: VL1 } +outputs: {} + diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_nodejs_mongodb_two_instances.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_nodejs_mongodb_two_instances.yaml new file mode 100644 index 0000000..b95120b --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_nodejs_mongodb_two_instances.yaml @@ -0,0 +1,185 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with nodejs and mongodb. + +parameters: + github_url: + type: string + description: The URL to download nodejs. + default: http://github.com/paypal/rest-api-sample-app-nodejs.git + + my_cpus: + type: number + description: Number of CPUs for the server. + default: 4 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + +resources: + mongo_dbms_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mongo_dbms_create_config + server: + get_resource: mongo_server + + mongo_dbms_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: mongodb/create.sh + group: script + + mongo_dbms_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mongo_dbms_configure_config + input_values: + mongodb_ip: + get_attr: + - mongo_server + - networks + - private + - 0 + server: + get_resource: mongo_server + depends_on: + - mongo_dbms_create_deploy + + mongo_dbms_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: mongodb/config.sh + group: script + + mongo_dbms_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mongo_dbms_start_config + server: + get_resource: mongo_server + depends_on: + - mongo_dbms_configure_deploy + + mongo_dbms_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: mongodb/start.sh + group: script + + mongo_db_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mongo_db_create_config + server: + get_resource: mongo_server + depends_on: + - mongo_dbms_start_deploy + + mongo_db_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: mongodb/create_database.sh + group: script + + nodejs_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: nodejs_create_config + server: + get_resource: app_server + + nodejs_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: nodejs/create.sh + group: script + + paypal_pizzastore_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: paypal_pizzastore_configure_config + input_values: + github_url: http://github.com/paypal/rest-api-sample-app-nodejs.git + mongodb_ip: + get_attr: + - mongo_server + - networks + - private + - 0 + server: + get_resource: app_server + depends_on: + - mongo_db_create_deploy + - nodejs_create_deploy + + paypal_pizzastore_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: nodejs/config.sh + group: script + + paypal_pizzastore_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: paypal_pizzastore_start_config + server: + get_resource: app_server + depends_on: + - paypal_pizzastore_configure_deploy + + paypal_pizzastore_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: nodejs/start.sh + group: script + + mongo_server: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + app_server: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + +outputs: + mongodb_url: + description: URL for the mongodb server. + value: + get_attr: + - mongo_server + - networks + - private + - 0 + nodejs_url: + description: URL for the nodejs server, http://<IP>:3000 + value: + get_attr: + - app_server + - networks + - private + - 0 diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_policies.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_policies.yaml new file mode 100644 index 0000000..c7cfa44 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_policies.yaml @@ -0,0 +1,25 @@ +heat_template_version: 2013-05-23 + +description: > + Template for deploying the nodes based on given policies. + +parameters: {} + +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: rhel-6.5-test-image + scheduler_hints: + group: + get_resource: my_compute_placement_policy + user_data_format: SOFTWARE_CONFIG + my_compute_placement_policy: + type: OS::Nova::ServerGroup + properties: + name: my_compute_placement_policy + policies: + - affinity + +outputs: {}
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_instance_wordpress.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_instance_wordpress.yaml new file mode 100644 index 0000000..8c10a93 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_instance_wordpress.yaml @@ -0,0 +1,209 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with wordpress, web server and mysql on the same server. + +parameters: + db_name: + type: string + description: The name of the database. + default: wordpress + db_user: + type: string + description: The user name of the DB user. + default: wp_user + db_pwd: + type: string + description: The WordPress database admin account password. + default: wp_pass + cpus: + type: number + description: Number of CPUs for the server. + default: 8 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + db_root_pwd: + type: string + description: Root password for MySQL. + default: passw0rd + db_port: + type: number + description: Port for the MySQL database. + default: 3366 + +resources: + + mysql_dbms_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: mysql/mysql_dbms_install.sh + group: script + + mysql_dbms_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mysql_dbms_create_config + input_values: + db_root_password: + get_param: db_root_pwd + server: + get_resource: server + + mysql_dbms_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: mysql/mysql_dbms_start.sh + group: script + + mysql_dbms_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mysql_dbms_start_config + server: + get_resource: server + depends_on: + - mysql_dbms_configure_deploy + + mysql_dbms_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: mysql/mysql_dbms_configure.sh + group: script + + mysql_dbms_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mysql_dbms_configure_config + input_values: + db_port: 3366 + server: + get_resource: server + depends_on: + - mysql_dbms_create_deploy + + mysql_database_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: mysql/mysql_database_configure.sh + group: script + + mysql_database_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mysql_database_configure_config + input_values: + db_name: + get_param: db_name + db_password: + get_param: db_pwd + db_root_password: + get_param: db_root_pwd + db_user: + get_param: db_user + server: + get_resource: server + depends_on: + - mysql_dbms_start_deploy + + webserver_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: webserver/webserver_install.sh + group: script + + webserver_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: webserver_create_config + server: + get_resource: server + + webserver_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: webserver/webserver_start.sh + group: script + + webserver_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: webserver_start_config + server: + get_resource: server + depends_on: + - webserver_create_deploy + + wordpress_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: wordpress/wordpress_install.sh + group: script + + wordpress_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: wordpress_create_config + server: + get_resource: server + depends_on: + - webserver_start_deploy + - mysql_database_configure_deploy + + wordpress_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: wordpress/wordpress_configure.sh + group: script + + wordpress_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: wordpress_configure_config + input_values: + wp_db_name: + get_param: db_name + wp_db_password: + get_param: db_pwd + wp_db_user: + get_param: db_user + server: + get_resource: server + depends_on: + - wordpress_create_deploy + + server: + type: OS::Nova::Server + properties: + flavor: m1.xlarge + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + +outputs: + website_url: + description: URL for Wordpress wiki. + value: + get_attr: + - server + - networks + - private + - 0 diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_instance_wordpress_from_csar.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_instance_wordpress_from_csar.yaml new file mode 100644 index 0000000..3598540 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_instance_wordpress_from_csar.yaml @@ -0,0 +1,207 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with wordpress, web server and mysql on the same server. + +parameters: + db_name: + type: string + description: The name of the database. + default: wordpress + db_user: + type: string + description: The user name of the DB user. + default: wp_user + db_pwd: + type: string + description: The WordPress database admin account password. + default: wp_pass + cpus: + type: number + description: Number of CPUs for the server. + default: 8 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + db_root_pwd: + type: string + description: Root password for MySQL. + default: passw0rd + db_port: + type: number + description: Port for the MySQL database. + default: 3366 + +resources: + + mysql_dbms_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/MYSQLDBMS/install.sh + group: script + + mysql_dbms_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mysql_dbms_create_config + server: + get_resource: server + + mysql_dbms_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/MYSQLDBMS/start.sh + group: script + + mysql_dbms_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mysql_dbms_start_config + server: + get_resource: server + depends_on: + - mysql_dbms_configure_deploy + + mysql_dbms_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/MYSQLDBMS/configure.sh + group: script + + mysql_dbms_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mysql_dbms_configure_config + input_values: + db_root_password: + get_param: db_root_pwd + server: + get_resource: server + depends_on: + - mysql_dbms_create_deploy + + mysql_database_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/MYSQLDatabase/configure.sh + group: script + + mysql_database_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: mysql_database_configure_config + input_values: + db_name: + get_param: db_name + db_password: + get_param: db_pwd + db_root_password: + get_param: db_root_pwd + db_user: + get_param: db_user + server: + get_resource: server + depends_on: + - mysql_dbms_start_deploy + + webserver_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/WebServer/install.sh + group: script + + webserver_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: webserver_create_config + server: + get_resource: server + + webserver_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/WebServer/start.sh + group: script + + webserver_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: webserver_start_config + server: + get_resource: server + depends_on: + - webserver_create_deploy + + wordpress_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/WordPress/install.sh + group: script + + wordpress_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: wordpress_create_config + server: + get_resource: server + depends_on: + - webserver_start_deploy + - mysql_database_configure_deploy + + wordpress_configure_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: ../Scripts/WordPress/configure.sh + group: script + + wordpress_configure_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: wordpress_configure_config + input_values: + wp_db_name: + get_param: db_name + wp_db_password: + get_param: db_pwd + wp_db_user: + get_param: db_user + server: + get_resource: server + depends_on: + - wordpress_create_deploy + + server: + type: OS::Nova::Server + properties: + flavor: m1.xlarge + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + +outputs: + website_url: + description: IP address for Wordpress wiki. + value: + get_attr: + - server + - networks + - private + - 0 diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_object_store.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_object_store.yaml new file mode 100644 index 0000000..91491e3 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_object_store.yaml @@ -0,0 +1,21 @@ +heat_template_version: 2013-05-23 + +description: > + Tosca template for creating an object storage service. + +parameters: + objectstore_name: + type: string + default: myobjstore + +resources: + obj_store_server: + type: OS::Swift::Container + properties: + X-Container-Meta: + Quota-Bytes: 1000000000 + X-Container-Read: ".r:*" + name: + get_param: objectstore_name + +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_server.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_server.yaml new file mode 100644 index 0000000..5cffb43 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_server.yaml @@ -0,0 +1,36 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile that just defines a single compute instance and selects a + (guest) host Operating System from the Compute node's properties. Note, this + example does not include default values on inputs properties. + +parameters: + cpus: + type: number + description: Number of CPUs for the server. + default: 1 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.small + image: ubuntu-12.04-software-config-os-init + user_data_format: SOFTWARE_CONFIG + +outputs: + private_ip: + description: The private IP address of the deployed server instance. + value: + get_attr: + - my_server + - networks + - private + - 0 diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_server_with_defaults_with_input.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_server_with_defaults_with_input.yaml new file mode 100644 index 0000000..1eb88a7 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_server_with_defaults_with_input.yaml @@ -0,0 +1,36 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile that just defines a single compute instance and selects a + (guest) host Operating System from the Compute node's properties. Note, this + example includes default values on inputs properties. + +parameters: + cpus: + type: number + description: Number of CPUs for the server. + default: 1 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.small + image: ubuntu-12.04-software-config-os-init + user_data_format: SOFTWARE_CONFIG + +outputs: + private_ip: + description: The private IP address of the deployed server instance. + value: + get_attr: + - my_server + - networks + - private + - 0 diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_server_with_defaults_without_input.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_server_with_defaults_without_input.yaml new file mode 100644 index 0000000..4e7e6b5 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_server_with_defaults_without_input.yaml @@ -0,0 +1,36 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile that just defines a single compute instance and selects a + (guest) host Operating System from the Compute node's properties. Note, this + example includes default values on inputs properties. + +parameters: + cpus: + type: number + description: Number of CPUs for the server. + default: 4 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.large + image: ubuntu-12.04-software-config-os-init + user_data_format: SOFTWARE_CONFIG + +outputs: + private_ip: + description: The private IP address of the deployed server instance. + value: + get_attr: + - my_server + - networks + - private + - 0 diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_server_without_tosca_os_version.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_server_without_tosca_os_version.yaml new file mode 100644 index 0000000..d2828cf --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_single_server_without_tosca_os_version.yaml @@ -0,0 +1,17 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile that just defines a single compute instance and selects a + flavor and host Operating System for the Compute node. Note, this is just a test + template showing Compute without optional 'version' property of OS capability. + In general, you should have version to narrow down your image selection. + +parameters: {} +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: rhel-6.5-test-image + user_data_format: SOFTWARE_CONFIG +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_software_component.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_software_component.yaml new file mode 100644 index 0000000..b1bbe49 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_software_component.yaml @@ -0,0 +1,58 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with a software component. + +parameters: + cpus: + type: number + description: Number of CPUs for the server. + default: 1 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + +resources: + server: + type: OS::Nova::Server + properties: + flavor: m1.small + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + my_software_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: my_software_create_config + server: + get_resource: server + + my_software_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: software_install.sh + group: script + + my_software_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: my_software_start_config + server: + get_resource: server + depends_on: + - my_software_create_deploy + + my_software_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: software_start.sh + group: script + +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_web_application.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_web_application.yaml new file mode 100644 index 0000000..38f12e6 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/hot_web_application.yaml @@ -0,0 +1,100 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with a web application. + +parameters: + context_root: + type: string + description: Context root for installing the application. + default: my_web_app + + cpus: + type: number + description: Number of CPUs for the server. + default: 2 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + +resources: + server: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: ubuntu-software-config-os-init + user_data_format: SOFTWARE_CONFIG + + web_server_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: web_server_create_config + server: + get_resource: server + + web_server_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: web_server_install.sh + group: script + + web_server_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: web_server_start_config + server: + get_resource: server + depends_on: + - web_server_create_deploy + + web_server_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: web_server_start.sh + group: script + + web_app_create_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: web_app_create_config + input_values: + context_root: + get_param: context_root + server: + get_resource: server + depends_on: + - web_server_start_deploy + + web_app_create_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: web_app_install.sh + group: script + + web_app_start_deploy: + type: OS::Heat::SoftwareDeployment + properties: + config: + get_resource: web_app_start_config + server: + get_resource: server + depends_on: + - web_app_create_deploy + + web_app_start_config: + type: OS::Heat::SoftwareConfig + properties: + config: + get_file: web_app_start.sh + group: script + +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_custom_network_nodes.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_custom_network_nodes.yaml new file mode 100644 index 0000000..556dcf4 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_custom_network_nodes.yaml @@ -0,0 +1,33 @@ +heat_template_version: 2013-05-23 + +description: > + Template for deploying a single server with predefined properties. + +parameters: {} +resources: + VDU1: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: rhel-6.5-test-image + networks: + - port: { get_resource: CP1 } + user_data_format: SOFTWARE_CONFIG + CP1: + type: OS::Neutron::Port + properties: + network: { get_resource: VL1 } + VL1: + type: OS::Neutron::Net + VL1_subnet: + type: OS::Neutron::Subnet + properties: + ip_version: 4 + allocation_pools: + - end: 192.168.0.200 + start: 192.168.0.50 + gateway_ip: 192.168.0.1 + cidr: 192.168.0.0/24 + network: { get_resource: VL1 } +outputs: {} + diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_one_server_one_network.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_one_server_one_network.yaml new file mode 100644 index 0000000..cfcd290 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_one_server_one_network.yaml @@ -0,0 +1,44 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with 1 server bound to a new network + +parameters: + network_name: + type: string + description: Network name + default: private_net + +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.small + image: cirros-0.3.2-x86_64-uec + networks: + - port: { get_resource: my_port } + user_data_format: SOFTWARE_CONFIG + + my_network: + type: OS::Neutron::Net + properties: + name: + get_param: network_name + + my_network_subnet: + type: OS::Neutron::Subnet + properties: + allocation_pools: + - end: 192.168.0.200 + start: 192.168.0.50 + cidr: 192.168.0.0/24 + gateway_ip: 192.168.0.1 + ip_version: 4 + network: { get_resource: my_network } + + my_port: + type: OS::Neutron::Port + properties: + network: { get_resource: my_network } + +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_one_server_three_networks.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_one_server_three_networks.yaml new file mode 100644 index 0000000..f8674e1 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_one_server_three_networks.yaml @@ -0,0 +1,71 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with 1 server bound to 3 networks + +parameters: {} + +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.small + image: cirros-0.3.2-x86_64-uec + networks: + - port: { get_resource: my_port1 } + - port: { get_resource: my_port2 } + - port: { get_resource: my_port3 } + user_data_format: SOFTWARE_CONFIG + + my_network1: + type: OS::Neutron::Net + properties: + name: net1 + + my_network2: + type: OS::Neutron::Net + properties: + name: net2 + + my_network3: + type: OS::Neutron::Net + properties: + name: net3 + + my_network1_subnet: + type: OS::Neutron::Subnet + properties: + cidr: 192.168.1.0/24 + ip_version: 4 + network: { get_resource: my_network1 } + + my_network2_subnet: + type: OS::Neutron::Subnet + properties: + cidr: 192.168.2.0/24 + ip_version: 4 + network: { get_resource: my_network2 } + + my_network3_subnet: + type: OS::Neutron::Subnet + properties: + cidr: 192.168.3.0/24 + ip_version: 4 + network: { get_resource: my_network3 } + + my_port1: + type: OS::Neutron::Port + properties: + network: { get_resource: my_network1 } + + my_port2: + type: OS::Neutron::Port + properties: + network: { get_resource: my_network2 } + + my_port3: + type: OS::Neutron::Port + properties: + network: { get_resource: my_network3 } + +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_server_on_existing_network.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_server_on_existing_network.yaml new file mode 100644 index 0000000..b8282b5 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_server_on_existing_network.yaml @@ -0,0 +1,27 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with 1 server bound to an existing network + +parameters: + network_name: + type: string + description: Network name + default: private_net + +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.small + image: cirros-0.3.2-x86_64-uec + networks: + - port: { get_resource: my_port } + user_data_format: SOFTWARE_CONFIG + + my_port: + type: OS::Neutron::Port + properties: + network: {get_param: network_name} + +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_two_servers_one_network.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_two_servers_one_network.yaml new file mode 100644 index 0000000..21157bb --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/network/hot_two_servers_one_network.yaml @@ -0,0 +1,72 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with 2 servers bound to the 1 network + +parameters: + network_name: + type: string + description: Network name + default: my_private_net + network_cidr: + type: string + description: CIDR for the network + default: 10.0.0.0/24 + network_start_ip: + type: string + description: Start IP for the allocation pool + default: 10.0.0.100 + network_end_ip: + type: string + description: End IP for the allocation pool + default: 10.0.0.150 + +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.small + image: cirros-0.3.2-x86_64-uec + networks: + - port: { get_resource: my_port } + user_data_format: SOFTWARE_CONFIG + + my_server2: + type: OS::Nova::Server + properties: + flavor: m1.small + image: cirros-0.3.2-x86_64-uec + networks: + - port: { get_resource: my_port2 } + user_data_format: SOFTWARE_CONFIG + + my_network: + type: OS::Neutron::Net + properties: + name: + get_param: network_name + + my_network_subnet: + type: OS::Neutron::Subnet + properties: + allocation_pools: + - end: + get_param: network_end_ip + start: + get_param: network_start_ip + cidr: + get_param: network_cidr + ip_version: 4 + network: { get_resource: my_network } + + my_port: + type: OS::Neutron::Port + properties: + network: { get_resource: my_network } + + my_port2: + type: OS::Neutron::Port + properties: + network: { get_resource: my_network } + +outputs: {} diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment.yaml new file mode 100644 index 0000000..5ff5382 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment.yaml @@ -0,0 +1,71 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with server and attached block storage using the normative + AttachesTo Relationship Type. + +parameters: + cpus: + type: number + description: Number of CPUs for the server. + default: 1 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + storage_location: + type: string + description: Block storage mount point (filesystem path). + default: /dev/vdc + storage_size: + type: number + description: Size of the storage to be created. + default: 2 + storage_snapshot_id: + type: string + description: "Optional identifier for an existing snapshot to use when creating storage." + default: ssid + +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.small + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage + + my_storage: + type: OS::Cinder::Volume + properties: + size: + get_param: storage_size + snapshot_id: + get_param: storage_snapshot_id + + attachesto_1: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_server + mountpoint: + get_param: storage_location + volume_id: + get_resource: my_storage + +outputs: + private_ip: + description: The private IP address of the newly created compute instance. + value: + get_attr: + - my_server + - networks + - private + - 0 + volume_id: + description: The volume id of the block storage instance. + value: + get_resource: my_storage diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment_notation1_alt1.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment_notation1_alt1.yaml new file mode 100644 index 0000000..9ffaf23 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment_notation1_alt1.yaml @@ -0,0 +1,92 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with a Single Block Storage node shared by 2-Tier + Application with custom AttachesTo Type and implied relationships. + +parameters: + cpus: + type: number + description: Number of CPUs for the server. + default: 1 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + storage_size: + type: number + description: Size of the storage to be created. + default: 1 + storage_snapshot_id: + type: string + description: Optional identifier for an existing snapshot to use when creating storage. + default: ssid + +resources: + my_storage: + type: OS::Cinder::Volume + properties: + size: + get_param: storage_size + snapshot_id: + get_param: storage_snapshot_id + + my_web_app_tier_1: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage + + myattachesto_1: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_web_app_tier_1 + mountpoint: /default_location + volume_id: + get_resource: my_storage + + my_web_app_tier_2: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage + + myattachesto_2: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_web_app_tier_2 + mountpoint: /some_other_data_location + volume_id: + get_resource: my_storage + +outputs: + private_ip_1: + description: The private IP address of the applications first tier. + value: + get_attr: + - my_web_app_tier_1 + - networks + - private + - 0 + private_ip_2: + description: The private IP address of the applications second tier. + value: + get_attr: + - my_web_app_tier_2 + - networks + - private + - 0 + volume_id: + description: The volume id of the block storage instance. + value: + get_resource: my_storage diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment_notation1_alt2.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment_notation1_alt2.yaml new file mode 100644 index 0000000..9b5e71c --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment_notation1_alt2.yaml @@ -0,0 +1,92 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with a Single Block Storage node shared by 2-Tier + Application with custom AttachesTo Type and implied relationships. + +parameters: + cpus: + type: number + description: Number of CPUs for the server. + default: 1 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + storage_size: + type: number + description: Size of the storage to be created. + default: 1 + storage_snapshot_id: + type: string + description: Optional identifier for an existing snapshot to use when creating storage. + default: ssid + +resources: + my_storage: + type: OS::Cinder::Volume + properties: + size: + get_param: storage_size + snapshot_id: + get_param: storage_snapshot_id + + my_web_app_tier_1: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage + + myattachesto_2: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_web_app_tier_1 + mountpoint: /default_location + volume_id: + get_resource: my_storage + + my_web_app_tier_2: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage + + myattachesto_1: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_web_app_tier_2 + mountpoint: /some_other_data_location + volume_id: + get_resource: my_storage + +outputs: + private_ip_1: + description: The private IP address of the applications first tier. + value: + get_attr: + - my_web_app_tier_1 + - networks + - private + - 0 + private_ip_2: + description: The private IP address of the applications second tier. + value: + get_attr: + - my_web_app_tier_2 + - networks + - private + - 0 + volume_id: + description: The volume id of the block storage instance. + value: + get_resource: my_storage diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment_notation2_alt1.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment_notation2_alt1.yaml new file mode 100644 index 0000000..1b4eb73 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment_notation2_alt1.yaml @@ -0,0 +1,96 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with a single Block Storage node shared by 2-Tier + Application with custom AttachesTo Type and explicit Relationship Templates. + +parameters: + cpus: + type: number + description: Number of CPUs for the server. + default: 1 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + storage_location: + type: string + description: Block storage mount point (filesystem path). + default: /dev/vdc + storage_size: + type: number + description: Size of the storage to be created. + default: 1 + storage_snapshot_id: + type: string + description: Optional identifier for an existing snapshot to use when creating storage. + default: ssid + +resources: + my_storage: + type: OS::Cinder::Volume + properties: + size: + get_param: storage_size + snapshot_id: + get_param: storage_snapshot_id + + my_web_app_tier_1: + type: OS::Nova::Server + properties: + flavor: m1.small + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage + + storage_attachesto_1_2: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_web_app_tier_1 + mountpoint: /my_data_location + volume_id: + get_resource: my_storage + + my_web_app_tier_2: + type: OS::Nova::Server + properties: + flavor: m1.small + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage + + storage_attachesto_2_1: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_web_app_tier_2 + mountpoint: /some_other_data_location + volume_id: + get_resource: my_storage + +outputs: + private_ip_1: + description: The private IP address of the applications first tier. + value: + get_attr: + - my_web_app_tier_1 + - networks + - private + - 0 + private_ip_2: + description: The private IP address of the applications second tier. + value: + get_attr: + - my_web_app_tier_2 + - networks + - private + - 0 + volume_id: + description: The volume id of the block storage instance. + value: + get_resource: my_storage diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment_notation2_alt2.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment_notation2_alt2.yaml new file mode 100644 index 0000000..0311a55 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_attachment_notation2_alt2.yaml @@ -0,0 +1,96 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with a single Block Storage node shared by 2-Tier + Application with custom AttachesTo Type and explicit Relationship Templates. + +parameters: + cpus: + type: number + description: Number of CPUs for the server. + default: 1 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + storage_location: + type: string + description: Block storage mount point (filesystem path). + default: /dev/vdc + storage_size: + type: number + description: Size of the storage to be created. + default: 1 + storage_snapshot_id: + type: string + description: Optional identifier for an existing snapshot to use when creating storage. + default: ssid + +resources: + my_storage: + type: OS::Cinder::Volume + properties: + size: + get_param: storage_size + snapshot_id: + get_param: storage_snapshot_id + + my_web_app_tier_1: + type: OS::Nova::Server + properties: + flavor: m1.small + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage + + storage_attachesto_1_1: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_web_app_tier_1 + mountpoint: /my_data_location + volume_id: + get_resource: my_storage + + my_web_app_tier_2: + type: OS::Nova::Server + properties: + flavor: m1.small + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage + + storage_attachesto_2_2: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_web_app_tier_2 + mountpoint: /some_other_data_location + volume_id: + get_resource: my_storage + +outputs: + private_ip_1: + description: The private IP address of the applications first tier. + value: + get_attr: + - my_web_app_tier_1 + - networks + - private + - 0 + private_ip_2: + description: The private IP address of the applications second tier. + value: + get_attr: + - my_web_app_tier_2 + - networks + - private + - 0 + volume_id: + description: The volume id of the block storage instance. + value: + get_resource: my_storage diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_custom_relationship_type.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_custom_relationship_type.yaml new file mode 100644 index 0000000..bce4603 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_custom_relationship_type.yaml @@ -0,0 +1,72 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with server and attached block storage using a custom + AttachesTo Relationship Type. + +parameters: + cpus: + type: number + description: Number of CPUs for the server. + default: 1 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + storage_location: + type: string + description: Block storage mount point (filesystem path). + default: /dev/vdc + storage_size: + type: number + description: Size of the storage to be created. + default: 1 + storage_snapshot_id: + type: string + description: Optional identifier for an existing snapshot to use when creating storage. + default: ssid + +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.small + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage + + my_storage: + type: OS::Cinder::Volume + properties: + size: + get_param: storage_size + snapshot_id: + get_param: storage_snapshot_id + + mycustomattachesto_1: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_server + volume_id: + get_resource: my_storage + mountpoint: + get_param: storage_location + + +outputs: + private_ip: + description: The private IP address of the newly created compute instance. + value: + get_attr: + - my_server + - networks + - private + - 0 + volume_id: + description: The volume id of the block storage instance. + value: + get_resource: my_storage diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_relationship_template.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_relationship_template.yaml new file mode 100644 index 0000000..e17dff9 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_blockstorage_with_relationship_template.yaml @@ -0,0 +1,65 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with server and attached block storage using a named + Relationship Template for the storage attachment. + +parameters: + cpus: + type: number + description: Number of CPUs for the server. + default: 1 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + storage_location: + type: string + description: Block storage mount point (filesystem path). + default: /dev/vdc + storage_size: + type: number + description: Size of the storage to be created. + default: 1 + +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.small + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage + + my_storage: + type: OS::Cinder::Volume + properties: + size: + get_param: storage_size + + storage_attachment_1: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_server + mountpoint: + get_input: storage_location + volume_id: + get_resource: my_storage + +outputs: + private_ip: + description: The private IP address of the newly created compute instance. + value: + get_attr: + - my_server + - networks + - private + - 0 + volume_id: + description: The volume id of the block storage instance. + value: + get_resource: my_storage diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_multiple_blockstorage_with_attachment_alt1.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_multiple_blockstorage_with_attachment_alt1.yaml new file mode 100644 index 0000000..55ada08 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_multiple_blockstorage_with_attachment_alt1.yaml @@ -0,0 +1,109 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with 2 servers each with different attached block storage. + +parameters: + cpus: + type: number + description: Number of CPUs for the server. + default: 1 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + storage_location: + type: string + description: Block storage mount point (filesystem path). + default: /dev/vdc + storage_size: + type: number + description: Size of the storage to be created. + default: 1 + storage_snapshot_id: + type: string + description: Optional identifier for an existing snapshot to use when creating storage. + default: ssid + +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage + + my_storage: + type: OS::Cinder::Volume + properties: + size: + get_param: storage_size + snapshot_id: + get_param: storage_snapshot_id + + attachesto_1: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_server + mountpoint: + get_param: storage_location + volume_id: + get_resource: my_storage + + my_server2: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage2 + + my_storage2: + type: OS::Cinder::Volume + properties: + size: + get_param: storage_size + snapshot_id: + get_param: storage_snapshot_id + + attachesto_2: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_server2 + mountpoint: + get_param: storage_location + volume_id: + get_resource: my_storage2 + +outputs: + server_ip_1: + description: The private IP address of the applications first server. + value: + get_attr: + - my_server + - networks + - private + - 0 + server_ip_2: + description: The private IP address of the applications second server. + value: + get_attr: + - my_server2 + - networks + - private + - 0 + volume_id_1: + description: The volume id of the first block storage instance. + value: + get_resource: my_storage + volume_id_2: + description: The volume id of the second block storage instance. + value: + get_resource: my_storage2 diff --git a/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_multiple_blockstorage_with_attachment_alt2.yaml b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_multiple_blockstorage_with_attachment_alt2.yaml new file mode 100644 index 0000000..3386d79 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/hot_output/storage/hot_multiple_blockstorage_with_attachment_alt2.yaml @@ -0,0 +1,109 @@ +heat_template_version: 2013-05-23 + +description: > + TOSCA simple profile with 2 servers each with different attached block storage. + +parameters: + cpus: + type: number + description: Number of CPUs for the server. + default: 1 + constraints: + - allowed_values: + - 1 + - 2 + - 4 + - 8 + storage_location: + type: string + description: Block storage mount point (filesystem path). + default: /dev/vdc + storage_size: + type: number + description: Size of the storage to be created. + default: 1 + storage_snapshot_id: + type: string + description: Optional identifier for an existing snapshot to use when creating storage. + default: ssid + +resources: + my_server: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage + + my_storage: + type: OS::Cinder::Volume + properties: + size: + get_param: storage_size + snapshot_id: + get_param: storage_snapshot_id + + attachesto_2: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_server + mountpoint: + get_param: storage_location + volume_id: + get_resource: my_storage + + my_server2: + type: OS::Nova::Server + properties: + flavor: m1.medium + image: fedora-amd64-heat-config + user_data_format: SOFTWARE_CONFIG + depends_on: + - my_storage2 + + my_storage2: + type: OS::Cinder::Volume + properties: + size: + get_param: storage_size + snapshot_id: + get_param: storage_snapshot_id + + attachesto_1: + type: OS::Cinder::VolumeAttachment + properties: + instance_uuid: + get_resource: my_server2 + mountpoint: + get_param: storage_location + volume_id: + get_resource: my_storage2 + +outputs: + server_ip_1: + description: The private IP address of the applications first server. + value: + get_attr: + - my_server + - networks + - private + - 0 + server_ip_2: + description: The private IP address of the applications second server. + value: + get_attr: + - my_server2 + - networks + - private + - 0 + volume_id_1: + description: The volume id of the first block storage instance. + value: + get_resource: my_storage + volume_id_2: + description: The volume id of the second block storage instance. + value: + get_resource: my_storage2 diff --git a/tosca2heat/heat-translator/translator/tests/data/network/test_tosca_custom_network_nodes_defs.yaml b/tosca2heat/heat-translator/translator/tests/data/network/test_tosca_custom_network_nodes_defs.yaml new file mode 100644 index 0000000..552ae07 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/network/test_tosca_custom_network_nodes_defs.yaml @@ -0,0 +1,41 @@ +node_types: + tosca.nodes.vendor.VDU: + derived_from: tosca.nodes.Compute + capabilities: + virtualbinding: + type: tosca.capabilities.vendor.VendorBindable + + tosca.nodes.vendor.CP: + derived_from: tosca.nodes.network.Port + requirements: + - virtualLink: + capability: tosca.capabilities.VendorLinkable + relationship: tosca.relationships.vendor.VendorLinksTo + node: tosca.nodes.vendor.VL + - virtualBinding: + capability: tosca.capabilities.vendor.VendorBindable + node: tosca.nodes.vendor.VDU + relationship: tosca.relationships.vendor.VendorBindsTo + + tosca.nodes.vendor.VL: + derived_from: tosca.nodes.network.Network + capabilities: + virtual_linkable: + type: tosca.capabilities.vendor.VendorLinkable + +relationship_types: + tosca.relationships.vendor.VendorLinksTo: + derived_from: tosca.relationships.network.LinksTo + valid_target_types: [ tosca.capabilities.vendor.VendorLinkable ] + + tosca.relationships.vendor.VendorBindsTo: + derived_from: tosca.relationships.network.BindsTo + valid_target_types: [ tosca.capabilities.vendor.VendorBindable ] + +capability_types: + tosca.capabilities.vendor.VendorLinkable: + derived_from: tosca.capabilities.network.Linkable + + tosca.capabilities.vendor.VendorBindable: + derived_from: tosca.capabilities.network.Bindable + diff --git a/tosca2heat/heat-translator/translator/tests/data/network/test_tosca_custom_network_nodes_imports.yaml b/tosca2heat/heat-translator/translator/tests/data/network/test_tosca_custom_network_nodes_imports.yaml new file mode 100644 index 0000000..ea473b1 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/network/test_tosca_custom_network_nodes_imports.yaml @@ -0,0 +1,41 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: Template for deploying a single server with predefined properties. + +imports: + - test_tosca_custom_network_nodes_defs.yaml + +topology_template: + node_templates: + + VDU1: + type: tosca.nodes.vendor.VDU + capabilities: + host: + properties: + num_cpus: 2 + disk_size: 10 GB + mem_size: 512 MB + # Guest Operating System properties + os: + properties: + # host Operating System image properties + architecture: x86_64 + type: Linux + distribution: RHEL + version: 6.5 + CP1: + type: tosca.nodes.vendor.CP + requirements: + - virtualLink: + node: VL1 + - virtualBinding: + node: VDU1 + + VL1: + type: tosca.nodes.vendor.VL + properties: + cidr: '192.168.0.0/24' + start_ip: '192.168.0.50' + end_ip: '192.168.0.200' + gateway_ip: '192.168.0.1' diff --git a/tosca2heat/heat-translator/translator/tests/data/network/test_tosca_custom_network_nodes_inline.yaml b/tosca2heat/heat-translator/translator/tests/data/network/test_tosca_custom_network_nodes_inline.yaml new file mode 100644 index 0000000..509aab4 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/network/test_tosca_custom_network_nodes_inline.yaml @@ -0,0 +1,82 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: Template for deploying a single server with predefined properties. + + +node_types: + tosca.nodes.vendor.VDU: + derived_from: tosca.nodes.Compute + capabilities: + virtualbinding: + type: tosca.capabilities.vendor.VendorBindable + + tosca.nodes.vendor.CP: + derived_from: tosca.nodes.network.Port + requirements: + - virtualLink: + capability: tosca.capabilities.VendorLinkable + relationship: tosca.relationships.vendor.VendorLinksTo + node: tosca.nodes.vendor.VL + - virtualBinding: + capability: tosca.capabilities.vendor.VendorBindable + node: tosca.nodes.vendor.VDU + relationship: tosca.relationships.vendor.VendorBindsTo + + tosca.nodes.vendor.VL: + derived_from: tosca.nodes.network.Network + capabilities: + virtual_linkable: + type: tosca.capabilities.vendor.VendorLinkable + +relationship_types: + tosca.relationships.vendor.VendorLinksTo: + derived_from: tosca.relationships.network.LinksTo + valid_target_types: [ tosca.capabilities.vendor.VendorLinkable ] + + tosca.relationships.vendor.VendorBindsTo: + derived_from: tosca.relationships.network.BindsTo + valid_target_types: [ tosca.capabilities.vendor.VendorBindable ] + +capability_types: + tosca.capabilities.vendor.VendorLinkable: + derived_from: tosca.capabilities.network.Linkable + + tosca.capabilities.vendor.VendorBindable: + derived_from: tosca.capabilities.network.Bindable + +topology_template: + node_templates: + + VDU1: + type: tosca.nodes.vendor.VDU + capabilities: + host: + properties: + num_cpus: 2 + disk_size: 10 GB + mem_size: 512 MB + # Guest Operating System properties + os: + properties: + # host Operating System image properties + architecture: x86_64 + type: Linux + distribution: RHEL + version: 6.5 + CP1: + type: tosca.nodes.vendor.CP + requirements: + - virtualLink: + node: VL1 + relationship: tosca.relationships.vendor.VendorLinksTo + - virtualBinding: + node: VDU1 + relationship: tosca.relationships.vendor.VendorBindsTo + + VL1: + type: tosca.nodes.vendor.VL + properties: + cidr: '192.168.0.0/24' + start_ip: '192.168.0.50' + end_ip: '192.168.0.200' + gateway_ip: '192.168.0.1' diff --git a/tosca2heat/heat-translator/translator/tests/data/network/tosca_one_server_one_network.yaml b/tosca2heat/heat-translator/translator/tests/data/network/tosca_one_server_one_network.yaml new file mode 100644 index 0000000..8e58fa9 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/network/tosca_one_server_one_network.yaml @@ -0,0 +1,43 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with 1 server bound to a new network + +topology_template: + + inputs: + network_name: + type: string + description: Network name + + node_templates: + my_server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: 1 + mem_size: 512 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: CirrOS + version: 0.3.2 + + my_network: + type: tosca.nodes.network.Network + properties: + network_name: { get_input: network_name } + ip_version: 4 + cidr: '192.168.0.0/24' + start_ip: '192.168.0.50' + end_ip: '192.168.0.200' + gateway_ip: '192.168.0.1' + + my_port: + type: tosca.nodes.network.Port + requirements: + - binding: my_server + - link: my_network diff --git a/tosca2heat/heat-translator/translator/tests/data/network/tosca_one_server_three_networks.yaml b/tosca2heat/heat-translator/translator/tests/data/network/tosca_one_server_three_networks.yaml new file mode 100644 index 0000000..d791b17 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/network/tosca_one_server_three_networks.yaml @@ -0,0 +1,64 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with 1 server bound to 3 networks + +topology_template: + + node_templates: + my_server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: 1 + mem_size: 512 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: CirrOS + version: 0.3.2 + + my_network1: + type: tosca.nodes.network.Network + properties: + cidr: '192.168.1.0/24' + network_name: net1 + + my_network2: + type: tosca.nodes.network.Network + properties: + cidr: '192.168.2.0/24' + network_name: net2 + + my_network3: + type: tosca.nodes.network.Network + properties: + cidr: '192.168.3.0/24' + network_name: net3 + + my_port1: + type: tosca.nodes.network.Port + properties: + order: 0 + requirements: + - binding: my_server + - link: my_network1 + + my_port2: + type: tosca.nodes.network.Port + properties: + order: 1 + requirements: + - binding: my_server + - link: my_network2 + + my_port3: + type: tosca.nodes.network.Port + properties: + order: 2 + requirements: + - binding: my_server + - link: my_network3 diff --git a/tosca2heat/heat-translator/translator/tests/data/network/tosca_server_on_existing_network.yaml b/tosca2heat/heat-translator/translator/tests/data/network/tosca_server_on_existing_network.yaml new file mode 100644 index 0000000..7fedc13 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/network/tosca_server_on_existing_network.yaml @@ -0,0 +1,39 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with 1 server bound to an existing network + +topology_template: + inputs: + network_name: + type: string + description: Network name + + node_templates: + my_server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: 1 + mem_size: 512 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: CirrOS + version: 0.3.2 + + my_network: + type: tosca.nodes.network.Network + properties: + network_name: { get_input: network_name } + + my_port: + type: tosca.nodes.network.Port + requirements: + - binding: + node: my_server + - link: + node: my_network diff --git a/tosca2heat/heat-translator/translator/tests/data/network/tosca_two_servers_one_network.yaml b/tosca2heat/heat-translator/translator/tests/data/network/tosca_two_servers_one_network.yaml new file mode 100644 index 0000000..1473a8d --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/network/tosca_two_servers_one_network.yaml @@ -0,0 +1,79 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with 2 servers bound to the 1 network + +topology_template: + + inputs: + network_name: + type: string + description: Network name + network_cidr: + type: string + default: 10.0.0.0/24 + description: CIDR for the network + network_start_ip: + type: string + default: 10.0.0.100 + description: Start IP for the allocation pool + network_end_ip: + type: string + default: 10.0.0.150 + description: End IP for the allocation pool + + node_templates: + my_server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: 1 + mem_size: 512 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: CirrOS + version: 0.3.2 + + my_server2: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: 1 + mem_size: 512 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: CirrOS + version: 0.3.2 + + my_network: + type: tosca.nodes.network.Network + properties: + ip_version: 4 + cidr: { get_input: network_cidr } + network_name: { get_input: network_name } + start_ip: { get_input: network_start_ip } + end_ip: { get_input: network_end_ip } + + my_port: + type: tosca.nodes.network.Port + requirements: + - binding: + node: my_server + - link: + node: my_network + + my_port2: + type: tosca.nodes.network.Port + requirements: + - binding: + node: my_server2 + - link: + node: my_network diff --git a/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_attachment.yaml b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_attachment.yaml new file mode 100644 index 0000000..460fa4c --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_attachment.yaml @@ -0,0 +1,61 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with server and attached block storage using the normative AttachesTo Relationship Type. + +topology_template: + + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + storage_size: + type: scalar-unit.size + description: Size of the storage to be created. + default: 1 GB + storage_snapshot_id: + type: string + description: > + Optional identifier for an existing snapshot to use when creating storage. + storage_location: + type: string + description: Block storage mount point (filesystem path). + + node_templates: + my_server: + type: Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 4096 kB + os: + properties: + architecture: x86_64 + type: linux + distribution: fedora + version: 18.0 + requirements: + - local_storage: + node: my_storage + relationship: + type: AttachesTo + properties: + location: { get_input: storage_location } + + my_storage: + type: BlockStorage + properties: + size: { get_input: storage_size } + snapshot_id: { get_input: storage_snapshot_id } + + outputs: + private_ip: + description: The private IP address of the newly created compute instance. + value: { get_attribute: [my_server, private_address] } + volume_id: + description: The volume id of the block storage instance. + value: { get_attribute: [my_storage, volume_id] } diff --git a/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_attachment_notation1.yaml b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_attachment_notation1.yaml new file mode 100644 index 0000000..df22d72 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_attachment_notation1.yaml @@ -0,0 +1,87 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with a Single Block Storage node shared by 2-Tier Application with custom AttachesTo Type and implied relationships. + +relationship_types: + MyAttachesTo: + derived_from: tosca.relationships.AttachesTo + properties: + location: + type: string + default: /default_location + +topology_template: + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + storage_size: + type: scalar-unit.size + default: 1 GB + description: Size of the storage to be created. + storage_snapshot_id: + type: string + description: > + Optional identifier for an existing snapshot to use when creating storage. + + node_templates: + my_web_app_tier_1: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 4096 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Fedora + version: 18.0 + requirements: + - local_storage: + node: my_storage + relationship: MyAttachesTo + + my_web_app_tier_2: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 4096 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Fedora + version: 18.0 + requirements: + - local_storage: + node: my_storage + relationship: + type: MyAttachesTo + properties: + location: /some_other_data_location + + my_storage: + type: tosca.nodes.BlockStorage + properties: + size: { get_input: storage_size } + snapshot_id: { get_input: storage_snapshot_id } + + outputs: + private_ip_1: + description: The private IP address of the application's first tier. + value: { get_attribute: [my_web_app_tier_1, private_address] } + private_ip_2: + description: The private IP address of the application's second tier. + value: { get_attribute: [my_web_app_tier_2, private_address] } + volume_id: + description: The volume id of the block storage instance. + value: { get_attribute: [my_storage, volume_id] } diff --git a/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_attachment_notation2.yaml b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_attachment_notation2.yaml new file mode 100644 index 0000000..cb1c17a --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_attachment_notation2.yaml @@ -0,0 +1,99 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with a single Block Storage node shared by 2-Tier Application with custom AttachesTo Type and explicit Relationship Templates. + +relationship_types: + MyAttachesTo: + derived_from: tosca.relationships.AttachesTo + properties: + location: + type: string + default: /default_location + +topology_template: + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + storage_size: + type: scalar-unit.size + default: 1 GB + description: Size of the storage to be created. + storage_snapshot_id: + type: string + description: > + Optional identifier for an existing snapshot to use when creating storage. + storage_location: + type: string + description: > + Block storage mount point (filesystem path). + + node_templates: + + my_web_app_tier_1: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 4096 kB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Fedora + version: 18.0 + requirements: + - local_storage: + node: my_storage + relationship: storage_attachesto_1 + + my_web_app_tier_2: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 4096 kB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Fedora + version: 18.0 + requirements: + - local_storage: + node: my_storage + relationship: storage_attachesto_2 + + my_storage: + type: tosca.nodes.BlockStorage + properties: + size: { get_input: storage_size } + snapshot_id: { get_input: storage_snapshot_id } + + relationship_templates: + storage_attachesto_1: + type: MyAttachesTo + properties: + location: /my_data_location + + storage_attachesto_2: + type: MyAttachesTo + properties: + location: /some_other_data_location + outputs: + private_ip_1: + description: The private IP address of the application's first tier. + value: { get_attribute: [my_web_app_tier_1, private_address] } + private_ip_2: + description: The private IP address of the application's second tier. + value: { get_attribute: [my_web_app_tier_2, private_address] } + volume_id: + description: The volume id of the block storage instance. + value: { get_attribute: [my_storage, volume_id] } diff --git a/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_custom_relationship_type.yaml b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_custom_relationship_type.yaml new file mode 100644 index 0000000..932f89e --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_custom_relationship_type.yaml @@ -0,0 +1,64 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with server and attached block storage using a custom AttachesTo Relationship Type. + +relationship_types: + MyCustomAttachesTo: + derived_from: AttachesTo + +topology_template: + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + storage_size: + type: scalar-unit.size + description: Size of the storage to be created. + default: 1 GB + storage_snapshot_id: + type: string + description: > + Optional identifier for an existing snapshot to use when creating storage. + storage_location: + type: string + description: Block storage mount point (filesystem path). + + node_templates: + my_server: + type: Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 4 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Fedora + version: 18.0 + requirements: + - local_storage: + node: my_storage + # Declare custom AttachesTo type using the 'relationship' keyword + relationship: + type: MyCustomAttachesTo + properties: + location: { get_input: storage_location } + my_storage: + type: BlockStorage + properties: + size: { get_input: storage_size } + snapshot_id: { get_input: storage_snapshot_id } + + outputs: + private_ip: + description: The private IP address of the newly created compute instance. + value: { get_attribute: [my_server, private_address] } + volume_id: + description: The volume id of the block storage instance. + value: { get_attribute: [my_storage, volume_id] } diff --git a/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_relationship_template.yaml b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_relationship_template.yaml new file mode 100644 index 0000000..c31a4da --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_blockstorage_with_relationship_template.yaml @@ -0,0 +1,59 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with server and attached block storage using a named Relationship Template for the storage attachment. + +topology_template: + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + storage_size: + type: scalar-unit.size + description: Size of the storage to be created. + default: 1 GB + storage_location: + type: string + description: Block storage mount point (filesystem path). + + node_templates: + my_server: + type: Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 4 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Fedora + version: 18.0 + requirements: + - local_storage: + node: my_storage + # Declare template to use with 'relationship' keyword + relationship: storage_attachment + + my_storage: + type: BlockStorage + properties: + size: { get_input: storage_size } + + relationship_templates: + storage_attachment: + type: AttachesTo + properties: + location: { get_input: storage_location } + + outputs: + private_ip: + description: The private IP address of the newly created compute instance. + value: { get_attribute: [my_server, private_address] } + volume_id: + description: The volume id of the block storage instance. + value: { get_attribute: [my_storage, volume_id] } diff --git a/tosca2heat/heat-translator/translator/tests/data/storage/tosca_multiple_blockstorage_with_attachment.yaml b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_multiple_blockstorage_with_attachment.yaml new file mode 100644 index 0000000..aa4647e --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_multiple_blockstorage_with_attachment.yaml @@ -0,0 +1,93 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with 2 servers each with different attached block storage. + +topology_template: + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + storage_size: + type: scalar-unit.size + default: 1 GB + description: Size of the storage to be created. + storage_snapshot_id: + type: string + description: > + Optional identifier for an existing snapshot to use when creating storage. + storage_location: + type: string + description: > + Block storage mount point (filesystem path). + + node_templates: + my_server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 4096 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Fedora + version: 18.0 + requirements: + - local_storage: + node: my_storage + relationship: + type: AttachesTo + properties: + location: { get_input: storage_location } + my_storage: + type: tosca.nodes.BlockStorage + properties: + size: { get_input: storage_size } + snapshot_id: { get_input: storage_snapshot_id } + + my_server2: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 4096 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Fedora + version: 18.0 + requirements: + - local_storage: + node: my_storage2 + relationship: + type: AttachesTo + properties: + location: { get_input: storage_location } + my_storage2: + type: tosca.nodes.BlockStorage + properties: + size: { get_input: storage_size } + snapshot_id: { get_input: storage_snapshot_id } + + outputs: + server_ip_1: + description: The private IP address of the application's first server. + value: { get_attribute: [my_server, private_address] } + server_ip_2: + description: The private IP address of the application's second server. + value: { get_attribute: [my_server2, private_address] } + volume_id_1: + description: The volume id of the first block storage instance. + value: { get_attribute: [my_storage, volume_id] } + volume_id_2: + description: The volume id of the second block storage instance. + value: { get_attribute: [my_storage2, volume_id] } diff --git a/tosca2heat/heat-translator/translator/tests/data/storage/tosca_single_object_store.yaml b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_single_object_store.yaml new file mode 100644 index 0000000..869af48 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/storage/tosca_single_object_store.yaml @@ -0,0 +1,17 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + Tosca template for creating an object storage service. + +topology_template: + inputs: + objectstore_name: + type: string + + node_templates: + obj_store_server: + type: tosca.nodes.ObjectStorage + properties: + name: { get_input: objectstore_name } + size: 1024 kB + maxsize: 1 GB diff --git a/tosca2heat/heat-translator/translator/tests/data/test_host_assignment.yaml b/tosca2heat/heat-translator/translator/tests/data/test_host_assignment.yaml new file mode 100644 index 0000000..acffd24 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/test_host_assignment.yaml @@ -0,0 +1,80 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + A template to test host assignment for translated hot resources. + It makes sure if a resource depends on multiple hosts only the + one with the "HostedOn" relationship is picked as the host. In + this template, the translated resource 'app_collectd_create_deploy' + would depend on 'logstash_server' and 'app_server'. But it would + have "HostedOn" relationship with 'app_server', and that server + would be its host. + +imports: + - custom_types/logstash.yaml + - custom_types/collectd.yaml + - custom_types/rsyslog.yaml + - custom_types/elasticsearch.yaml + +dsl_definitions: + host_capabilities: &host_capabilities + # compute properties (flavor) + disk_size: 10 GB + num_cpus: 1 + mem_size: 4096 MB + os_capabilities: &os_capabilities + architecture: x86_64 + type: Linux + distribution: Ubuntu + version: 14.04 + +topology_template: + node_templates: + app_collectd: + type: tosca.nodes.SoftwareComponent.Collectd + requirements: + - host: + node: app_server + - log_endpoint: + node: logstash + capability: log_endpoint + relationship: + type: tosca.relationships.ConnectsTo + interfaces: + Configure: + pre_configure_target: + implementation: logstash/configure_collectd.py + interfaces: + Standard: + create: collectd/create.sh + configure: + implementation: collectd/config.py + inputs: + logstash_ip: { get_attribute: [logstash_server, private_address] } + start: collectd/start.sh + + logstash: + type: tosca.nodes.SoftwareComponent.Logstash + requirements: + - host: + node: logstash_server + interfaces: + Standard: + create: logstash/create.sh + start: logstash/start.sh + + app_server: + type: tosca.nodes.Compute + capabilities: + os: + properties: *os_capabilities + host: + properties: *host_capabilities + + logstash_server: + type: tosca.nodes.Compute + capabilities: + os: + properties: *os_capabilities + host: + properties: *host_capabilities + diff --git a/tosca2heat/heat-translator/translator/tests/data/test_single_server_without_optional_version_prop.yaml b/tosca2heat/heat-translator/translator/tests/data/test_single_server_without_optional_version_prop.yaml new file mode 100644 index 0000000..8cf5255 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/test_single_server_without_optional_version_prop.yaml @@ -0,0 +1,24 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile that just defines a single compute instance and selects a + flavor and host Operating System for the Compute node. Note, this is just a test + template showing Compute without optional 'version' property of OS capability. In + general, you should have version to narrow down your image selection. + +topology_template: + + node_templates: + my_server: + type: Compute + capabilities: + host: + properties: + disk_size: 40 GB + num_cpus: 2 + mem_size: 4 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: RHEL diff --git a/tosca2heat/heat-translator/translator/tests/data/test_tosca_artifact.yaml b/tosca2heat/heat-translator/translator/tests/data/test_tosca_artifact.yaml new file mode 100644 index 0000000..be2caca --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/test_tosca_artifact.yaml @@ -0,0 +1,40 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: TOSCA template to test artifact usage + +node_types: + tosca.nodes.CustomWebServer: + derived_from: tosca.nodes.WebServer + artifacts: + web_content: + file: http://www.mycompany.org/content.tgz + type: tosca.artifacts.File + interfaces: + Standard: + create: + inputs: + content: { get_artifact: [ SELF, web_content ] } + implementation: install.sh + +topology_template: + node_templates: + + customwebserver: + type: tosca.nodes.CustomWebServer + requirements: + - host: server + + 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/test_tosca_custom_type.yaml b/tosca2heat/heat-translator/translator/tests/data/test_tosca_custom_type.yaml new file mode 100644 index 0000000..c427ef0 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/test_tosca_custom_type.yaml @@ -0,0 +1,47 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: TOSCA template to test custom type with an interface defined on it + +node_types: + tosca.nodes.CustomWebServer: + derived_from: tosca.nodes.WebServer + properties: + custom_install_path: + type: string + default: /home/custom/custom + interfaces: + Standard: + create: + implementation: install.sh + inputs: + path: { get_property: [ SELF, custom_install_path ] } + +topology_template: + inputs: + install_path: + type: string + default: /home/custom/other + + node_templates: + + customwebserver: + type: tosca.nodes.CustomWebServer + requirements: + - host: server + properties: + custom_install_path : { get_input: install_path } + + 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/test_tosca_custom_type_with_override.yaml b/tosca2heat/heat-translator/translator/tests/data/test_tosca_custom_type_with_override.yaml new file mode 100644 index 0000000..d398c63 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/test_tosca_custom_type_with_override.yaml @@ -0,0 +1,45 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA template to test custom type with an interface defined on it, + and an interface overriding the type's one on the template itself + +node_types: + tosca.nodes.CustomWebServer: + derived_from: tosca.nodes.WebServer + interfaces: + Standard: + create: + implementation: install.sh + +topology_template: + inputs: + install_path: + type: string + default: /home/custom/other + + node_templates: + customwebserver: + type: tosca.nodes.CustomWebServer + requirements: + - host: server + interfaces: + Standard: + create: + implementation: install_override.sh + inputs: + path: { get_input: install_path } + + 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/test_tosca_flavor_and_image.yaml b/tosca2heat/heat-translator/translator/tests/data/test_tosca_flavor_and_image.yaml new file mode 100644 index 0000000..3247589 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/test_tosca_flavor_and_image.yaml @@ -0,0 +1,29 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: Template for deploying a server with custom properties for image, flavor and key_name. + +node_types: + tosca.nodes.nfv.VDU: + derived_from: tosca.nodes.Compute + properties: + key_name: + type: string + image: + type: string + flavor: + type: string + +topology_template: + inputs: + key_name: + type: string + default: inputkey + + node_templates: + my_server: + type: tosca.nodes.nfv.VDU + properties: + flavor: m1.medium + image: rhel-6.5-test-image + key_name: + get_input: key_name diff --git a/tosca2heat/heat-translator/translator/tests/data/test_tosca_nfv_sample.yaml b/tosca2heat/heat-translator/translator/tests/data/test_tosca_nfv_sample.yaml new file mode 100644 index 0000000..1112234 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/test_tosca_nfv_sample.yaml @@ -0,0 +1,43 @@ +tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0 + +description: Template for deploying a single server with predefined properties. + +topology_template: + node_templates: + + VDU1: + type: tosca.nodes.nfv.VDU + capabilities: + host: + properties: + num_cpus: 2 + disk_size: 10 GB + mem_size: 512 MB + # Guest Operating System properties + os: + properties: + # host Operating System image properties + architecture: x86_64 + type: Linux + distribution: RHEL + version: 6.5 + CP1: + type: tosca.nodes.nfv.CP + properties: + ip_address: 192.168.0.55 + requirements: + - virtualLink: + node: VL1 +# relationship: tosca.relationships.nfv.VirtualLinksTo + - virtualBinding: + node: VDU1 + relationship: tosca.relationships.nfv.VirtualBindsTo + + VL1: + type: tosca.nodes.nfv.VL + properties: + vendor: ACME + cidr: '192.168.0.0/24' + start_ip: '192.168.0.50' + end_ip: '192.168.0.200' + gateway_ip: '192.168.0.1' diff --git a/tosca2heat/heat-translator/translator/tests/data/tosca_elk.yaml b/tosca2heat/heat-translator/translator/tests/data/tosca_elk.yaml new file mode 100644 index 0000000..a074aa6 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/tosca_elk.yaml @@ -0,0 +1,219 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + This TOSCA simple profile deploys nodejs, mongodb, elasticsearch, logstash + and kibana each on a separate server with monitoring enabled for nodejs + server where a sample nodejs application is running. The rsyslog and collectd + are installed on a nodejs server. + +imports: + - custom_types/paypalpizzastore_nodejs_app.yaml + - custom_types/elasticsearch.yaml + - custom_types/logstash.yaml + - custom_types/kibana.yaml + - custom_types/collectd.yaml + - custom_types/rsyslog.yaml + +dsl_definitions: + host_capabilities: &host_capabilities + disk_size: 10 GB + num_cpus: { get_input: my_cpus } + mem_size: 4096 MB + os_capabilities: &os_capabilities + architecture: x86_64 + type: Linux + distribution: Ubuntu + version: 14.04 + +topology_template: + inputs: + my_cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + default: 1 + github_url: + type: string + description: The URL to download nodejs. + default: http://github.com/paypal/rest-api-sample-app-nodejs.git + + node_templates: + paypal_pizzastore: + type: tosca.nodes.WebApplication.PayPalPizzaStore + properties: + github_url: { get_input: github_url } + requirements: + - host: nodejs + - database_connection: mongo_db + interfaces: + Standard: + configure: + implementation: nodejs/config.sh + inputs: + github_url: { get_property: [ SELF, github_url ] } + mongodb_ip: { get_attribute: [mongo_server, private_address] } + start: nodejs/start.sh + nodejs: + type: tosca.nodes.WebServer + requirements: + - host: app_server + interfaces: + Standard: + create: nodejs/create.sh + mongo_db: + type: tosca.nodes.Database + requirements: + - host: mongo_dbms + interfaces: + Standard: + create: mongodb/create_database.sh + mongo_dbms: + type: tosca.nodes.DBMS + requirements: + - host: mongo_server + interfaces: + Standard: + create: mongodb/create.sh + configure: + implementation: mongodb/config.sh + inputs: + mongodb_ip: { get_attribute: [mongo_server, private_address] } + start: mongodb/start.sh + elasticsearch: + type: tosca.nodes.SoftwareComponent.Elasticsearch + requirements: + - host: elasticsearch_server + interfaces: + Standard: + create: elasticsearch/create.sh + start: elasticsearch/start.sh + logstash: + type: tosca.nodes.SoftwareComponent.Logstash + requirements: + - host: logstash_server + - search_endpoint: + node: elasticsearch + capability: search_endpoint + relationship: + type: tosca.relationships.ConnectsTo + interfaces: + Configure: + pre_configure_source: + implementation: logstash/configure_elasticsearch.py + inputs: + elasticsearch_ip: { get_attribute: [elasticsearch_server, private_address] } + interfaces: + Standard: + create: logstash/create.sh + start: logstash/start.sh + kibana: + type: tosca.nodes.SoftwareComponent.Kibana + requirements: + - host: kibana_server + - search_endpoint: + node: elasticsearch + capability: search_endpoint + interfaces: + Standard: + create: kibana/create.sh + configure: + implementation: kibana/config.sh + inputs: + elasticsearch_ip: { get_attribute: [elasticsearch_server, private_address] } + kibana_ip: { get_attribute: [kibana_server, private_address] } + start: kibana/start.sh + app_collectd: + type: tosca.nodes.SoftwareComponent.Collectd + requirements: + - host: app_server + - log_endpoint: + node: logstash + capability: log_endpoint + relationship: + type: tosca.relationships.ConnectsTo + interfaces: + Configure: + pre_configure_target: + implementation: logstash/configure_collectd.py + interfaces: + Standard: + create: collectd/create.sh + configure: + implementation: collectd/config.py + inputs: + logstash_ip: { get_attribute: [logstash_server, private_address] } + start: collectd/start.sh + app_rsyslog: + type: tosca.nodes.SoftwareComponent.Rsyslog + requirements: + - host: app_server + - log_endpoint: + node: logstash + capability: log_endpoint + relationship: + type: tosca.relationships.ConnectsTo + interfaces: + Configure: + pre_configure_target: + implementation: logstash/configure_rsyslog.py + interfaces: + Standard: + create: rsyslog/create.sh + configure: + implementation: rsyslog/config.sh + inputs: + logstash_ip: { get_attribute: [logstash_server, private_address] } + start: rsyslog/start.sh + app_server: + type: tosca.nodes.Compute + capabilities: + host: + properties: *host_capabilities + os: + properties: *os_capabilities + mongo_server: + type: tosca.nodes.Compute + capabilities: + host: + properties: *host_capabilities + os: + properties: *os_capabilities + elasticsearch_server: + type: tosca.nodes.Compute + capabilities: + host: + properties: *host_capabilities + os: + properties: *os_capabilities + logstash_server: + type: tosca.nodes.Compute + capabilities: + host: + properties: *host_capabilities + os: + properties: *os_capabilities + kibana_server: + type: tosca.nodes.Compute + capabilities: + host: + properties: *host_capabilities + os: + properties: *os_capabilities + + outputs: + nodejs_url: + description: URL for the nodejs server, http://<IP>:3000 + value: { get_attribute: [ app_server, private_address ] } + mongodb_url: + description: URL for the mongodb server. + value: { get_attribute: [ mongo_server, private_address ] } + elasticsearch_url: + description: URL for the elasticsearch server. + value: { get_attribute: [ elasticsearch_server, private_address ] } + logstash_url: + description: URL for the logstash server. + value: { get_attribute: [ logstash_server, private_address ] } + kibana_url: + description: URL for the kibana server. + value: { get_attribute: [ kibana_server, private_address ] } diff --git a/tosca2heat/heat-translator/translator/tests/data/tosca_helloworld.yaml b/tosca2heat/heat-translator/translator/tests/data/tosca_helloworld.yaml new file mode 100644 index 0000000..5b913ff --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/tosca_helloworld.yaml @@ -0,0 +1,23 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: Template for deploying a single server with predefined properties. + +topology_template: + node_templates: + my_server: + type: tosca.nodes.Compute + capabilities: + # Host container properties + host: + properties: + num_cpus: 2 + disk_size: 10 GB + mem_size: 512 MB + # Guest Operating System properties + os: + properties: + # host Operating System image properties + architecture: x86_64 + type: Linux + distribution: RHEL + version: 6.5 diff --git a/tosca2heat/heat-translator/translator/tests/data/tosca_helloworld_invalid.yaml b/tosca2heat/heat-translator/translator/tests/data/tosca_helloworld_invalid.yaml new file mode 100644 index 0000000..ea60733 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/tosca_helloworld_invalid.yaml @@ -0,0 +1,23 @@ +tosca_definitions: tosca_simple_yaml_1_0 + +description: Template with invalid version and topology_template section. + +topology_template: + node_temp: + my_server: + type: tosca.nodes.Compute + capabilities: + # Host container properties + host: + properties: + num_cpus: 2 + disk_size: 10 GB + mem_size: 512 MB + # Guest Operating System properties + os: + properties: + # host Operating System image properties + architecture: x86_64 + type: Linux + distribution: RHEL + version: 6.5 diff --git a/tosca2heat/heat-translator/translator/tests/data/tosca_nodejs_mongodb_two_instances.yaml b/tosca2heat/heat-translator/translator/tests/data/tosca_nodejs_mongodb_two_instances.yaml new file mode 100644 index 0000000..f611071 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/tosca_nodejs_mongodb_two_instances.yaml @@ -0,0 +1,96 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with nodejs and mongodb. + +imports: + - custom_types/paypalpizzastore_nodejs_app.yaml + +dsl_definitions: + host_capabilities: &host_capabilities + disk_size: 10 GB + num_cpus: 1 + mem_size: 4096 MB + os_capabilities: &os_capabilities + architecture: x86_64 + type: Linux + distribution: Ubuntu + version: 14.04 + +topology_template: + inputs: + my_cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + default: 1 + github_url: + type: string + description: The URL to download nodejs. + default: http://github.com/paypal/rest-api-sample-app-nodejs.git + + node_templates: + paypal_pizzastore: + type: tosca.nodes.WebApplication.PayPalPizzaStore + properties: + github_url: { get_input: github_url } + requirements: + - host: nodejs + - database_connection: mongo_db + interfaces: + Standard: + configure: + implementation: nodejs/config.sh + inputs: + github_url: http://github.com/paypal/rest-api-sample-app-nodejs.git + mongodb_ip: { get_attribute: [mongo_server, private_address] } + start: nodejs/start.sh + nodejs: + type: tosca.nodes.WebServer + requirements: + - host: app_server + interfaces: + Standard: + create: nodejs/create.sh + mongo_db: + type: tosca.nodes.Database + requirements: + - host: mongo_dbms + interfaces: + Standard: + create: mongodb/create_database.sh + mongo_dbms: + type: tosca.nodes.DBMS + requirements: + - host: mongo_server + interfaces: + Standard: + create: mongodb/create.sh + configure: + implementation: mongodb/config.sh + inputs: + mongodb_ip: { get_attribute: [mongo_server, private_address] } + start: mongodb/start.sh + mongo_server: + type: tosca.nodes.Compute + capabilities: + host: + properties: *host_capabilities + os: + properties: *os_capabilities + app_server: + type: tosca.nodes.Compute + capabilities: + host: + properties: *host_capabilities + os: + properties: *os_capabilities + + outputs: + nodejs_url: + description: URL for the nodejs server, http://<IP>:3000 + value: { get_attribute: [app_server, private_address] } + mongodb_url: + description: URL for the mongodb server. + value: { get_attribute: [mongo_server, private_address] } diff --git a/tosca2heat/heat-translator/translator/tests/data/tosca_policies.yaml b/tosca2heat/heat-translator/translator/tests/data/tosca_policies.yaml new file mode 100644 index 0000000..26417d3 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/tosca_policies.yaml @@ -0,0 +1,28 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: Template for deploying the nodes based on given policies. + +topology_template: + node_templates: + my_server: + type: tosca.nodes.Compute + capabilities: + # Host container properties + host: + properties: + num_cpus: 2 + disk_size: 10 GB + mem_size: 512 MB + # Guest Operating System properties + os: + properties: + # host Operating System image properties + architecture: x86_64 + type: Linux + distribution: RHEL + version: 6.5 + policies: + - my_compute_placement_policy: + type: tosca.policies.Placement + description: Apply my placement policy to my application’s servers + targets: [ my_server ] diff --git a/tosca2heat/heat-translator/translator/tests/data/tosca_single_instance_wordpress.yaml b/tosca2heat/heat-translator/translator/tests/data/tosca_single_instance_wordpress.yaml new file mode 100644 index 0000000..8c907db --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/tosca_single_instance_wordpress.yaml @@ -0,0 +1,120 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with wordpress, web server and mysql on the same server. + +imports: + - custom_types/wordpress.yaml + +topology_template: + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + default: 1 + db_name: + type: string + description: The name of the database. + default: wordpress + db_user: + type: string + description: The user name of the DB user. + default: wp_user + db_pwd: + type: string + description: The WordPress database admin account password. + default: wp_pass + db_root_pwd: + type: string + description: Root password for MySQL. + db_port: + type: PortDef + description: Port for the MySQL database. + default: 3306 + + node_templates: + wordpress: + type: tosca.nodes.WebApplication.WordPress + requirements: + - host: webserver + - database_endpoint: mysql_database + interfaces: + Standard: + create: wordpress/wordpress_install.sh + configure: + implementation: wordpress/wordpress_configure.sh + inputs: + wp_db_name: { get_property: [ mysql_database, name ] } + wp_db_user: { get_property: [ mysql_database, user ] } + wp_db_password: { get_property: [ mysql_database, password ] } + + mysql_database: + type: tosca.nodes.Database + properties: + name: { get_input: db_name } + user: { get_input: db_user } + password: { get_input: db_pwd } + capabilities: + database_endpoint: + properties: + port: { get_input: db_port } + requirements: + - host: + node: mysql_dbms + interfaces: + Standard: + configure: + implementation: mysql/mysql_database_configure.sh + inputs: + db_name: { get_property: [ SELF, name ] } + db_user: { get_property: [ SELF, user ] } + db_password: { get_property: [ SELF, password ] } + db_root_password: { get_property: [ mysql_dbms, root_password ] } + mysql_dbms: + type: tosca.nodes.DBMS + properties: + root_password: { get_input: db_root_pwd } + port: { get_input: db_port } + requirements: + - host: server + interfaces: + Standard: + create: + implementation: mysql/mysql_dbms_install.sh + inputs: + db_root_password: { get_property: [ SELF, root_password ] } + start: mysql/mysql_dbms_start.sh + configure: + implementation: mysql/mysql_dbms_configure.sh + inputs: + db_port: 3366 + + webserver: + type: tosca.nodes.WebServer + requirements: + - host: server + interfaces: + Standard: + create: webserver/webserver_install.sh + start: webserver/webserver_start.sh + server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 4096 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Ubuntu + version: 14.04 + + outputs: + website_url: + description: URL for Wordpress wiki. + value: { get_attribute: [server, private_address] } diff --git a/tosca2heat/heat-translator/translator/tests/data/tosca_single_instance_wordpress_with_local_abspath_import.yaml b/tosca2heat/heat-translator/translator/tests/data/tosca_single_instance_wordpress_with_local_abspath_import.yaml new file mode 100644 index 0000000..af2e7a3 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/tosca_single_instance_wordpress_with_local_abspath_import.yaml @@ -0,0 +1,125 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with wordpress, web server and mysql on the same server. + This template was added to test an 'invalid' scenario where the input template + to heat-translator is provided as a URL and that template is referencing an + import using an absolute path. The translation of this template would work + only if it is tried locally (not via a URL link but via a file system + reference) and the referenced import exists in the path below. + +imports: + - /tmp/wordpress.yaml + +topology_template: + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + default: 1 + db_name: + type: string + description: The name of the database. + default: wordpress + db_user: + type: string + description: The user name of the DB user. + default: wp_user + db_pwd: + type: string + description: The WordPress database admin account password. + default: wp_pass + db_root_pwd: + type: string + description: Root password for MySQL. + db_port: + type: PortDef + description: Port for the MySQL database. + default: 3306 + + node_templates: + wordpress: + type: tosca.nodes.WebApplication.WordPress + requirements: + - host: webserver + - database_endpoint: mysql_database + interfaces: + Standard: + create: wordpress/wordpress_install.sh + configure: + implementation: wordpress/wordpress_configure.sh + inputs: + wp_db_name: { get_input: db_name } + wp_db_user: { get_input: db_user } + wp_db_password: { get_input: db_pwd } + + mysql_database: + type: tosca.nodes.Database + properties: + name: { get_input: db_name } + user: { get_input: db_user } + password: { get_input: db_pwd } + capabilities: + database_endpoint: + properties: + port: { get_input: db_port } + requirements: + - host: + node: mysql_dbms + interfaces: + Standard: + configure: + implementation: mysql/mysql_database_configure.sh + inputs: + db_name: { get_input: db_name } + db_user: { get_input: db_user } + db_password: { get_input: db_pwd } + db_root_password: { get_input: db_root_pwd } + mysql_dbms: + type: tosca.nodes.DBMS + properties: + root_password: { get_input: db_root_pwd } + port: { get_input: db_port } + requirements: + - host: server + interfaces: + Standard: + create: + implementation: mysql/mysql_dbms_install.sh + inputs: + db_root_password: passw0rd + start: mysql/mysql_dbms_start.sh + configure: + implementation: mysql/mysql_dbms_configure.sh + inputs: + db_port: 3366 + + webserver: + type: tosca.nodes.WebServer + requirements: + - host: server + interfaces: + Standard: + create: webserver/webserver_install.sh + start: webserver/webserver_start.sh + server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 4096 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Ubuntu + version: 14.04 + + outputs: + website_url: + description: URL for Wordpress wiki. + value: { get_attribute: [server, private_address] } diff --git a/tosca2heat/heat-translator/translator/tests/data/tosca_single_instance_wordpress_with_url_import.yaml b/tosca2heat/heat-translator/translator/tests/data/tosca_single_instance_wordpress_with_url_import.yaml new file mode 100644 index 0000000..69dbfeb --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/tosca_single_instance_wordpress_with_url_import.yaml @@ -0,0 +1,120 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with wordpress, web server and mysql on the same server. + +imports: + - https://raw.githubusercontent.com/openstack/heat-translator/master/translator/tests/data/custom_types/wordpress.yaml + +topology_template: + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + default: 1 + db_name: + type: string + description: The name of the database. + default: wordpress + db_user: + type: string + description: The user name of the DB user. + default: wp_user + db_pwd: + type: string + description: The WordPress database admin account password. + default: wp_pass + db_root_pwd: + type: string + description: Root password for MySQL. + db_port: + type: PortDef + description: Port for the MySQL database. + default: 3306 + + node_templates: + wordpress: + type: tosca.nodes.WebApplication.WordPress + requirements: + - host: webserver + - database_endpoint: mysql_database + interfaces: + Standard: + create: wordpress/wordpress_install.sh + configure: + implementation: wordpress/wordpress_configure.sh + inputs: + wp_db_name: { get_property: [ mysql_database, name ] } + wp_db_user: { get_property: [ mysql_database, user ] } + wp_db_password: { get_property: [ mysql_database, password ] } + + mysql_database: + type: tosca.nodes.Database + properties: + name: { get_input: db_name } + user: { get_input: db_user } + password: { get_input: db_pwd } + capabilities: + database_endpoint: + properties: + port: { get_input: db_port } + requirements: + - host: + node: mysql_dbms + interfaces: + Standard: + configure: + implementation: mysql/mysql_database_configure.sh + inputs: + db_name: { get_property: [ SELF, name ] } + db_user: { get_property: [ SELF, user ] } + db_password: { get_property: [ SELF, password ] } + db_root_password: { get_property: [ mysql_dbms, root_password ] } + mysql_dbms: + type: tosca.nodes.DBMS + properties: + root_password: { get_input: db_root_pwd } + port: { get_input: db_port } + requirements: + - host: server + interfaces: + Standard: + create: + implementation: mysql/mysql_dbms_install.sh + inputs: + db_root_password: { get_property: [ SELF, root_password ] } + start: mysql/mysql_dbms_start.sh + configure: + implementation: mysql/mysql_dbms_configure.sh + inputs: + db_port: 3366 + + webserver: + type: tosca.nodes.WebServer + requirements: + - host: server + interfaces: + Standard: + create: webserver/webserver_install.sh + start: webserver/webserver_start.sh + server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 4096 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Ubuntu + version: 14.04 + + outputs: + website_url: + description: URL for Wordpress wiki. + value: { get_attribute: [server, private_address] } diff --git a/tosca2heat/heat-translator/translator/tests/data/tosca_single_server.yaml b/tosca2heat/heat-translator/translator/tests/data/tosca_single_server.yaml new file mode 100644 index 0000000..67a0161 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/tosca_single_server.yaml @@ -0,0 +1,32 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile that just defines a single compute instance and selects a (guest) host Operating System from the Compute node's properties. Note, this example does not include default values on inputs properties. + +topology_template: + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + + node_templates: + my_server: + type: Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 4 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: ubuntu + version: 12.04 + outputs: + private_ip: + description: The private IP address of the deployed server instance. + value: { get_attribute: [my_server, private_address] } diff --git a/tosca2heat/heat-translator/translator/tests/data/tosca_single_server_with_defaults.yaml b/tosca2heat/heat-translator/translator/tests/data/tosca_single_server_with_defaults.yaml new file mode 100644 index 0000000..68933e2 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/tosca_single_server_with_defaults.yaml @@ -0,0 +1,35 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile that just defines a single compute instance and + selects a (guest) host Operating System from the Compute node's properties. + Note, this example includes default values on inputs properties. + +topology_template: + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + default: 4 + + node_templates: + my_server: + type: Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 4 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: ubuntu + version: 12.04 + outputs: + private_ip: + description: The private IP address of the deployed server instance. + value: { get_attribute: [my_server, private_address] }
\ No newline at end of file diff --git a/tosca2heat/heat-translator/translator/tests/data/tosca_software_component.yaml b/tosca2heat/heat-translator/translator/tests/data/tosca_software_component.yaml new file mode 100644 index 0000000..88186a5 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/tosca_software_component.yaml @@ -0,0 +1,40 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with a software component. + +topology_template: + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + default: 1 + + node_templates: + my_software: + type: tosca.nodes.SoftwareComponent + properties: + component_version: 1.0 + requirements: + - host: server + interfaces: + Standard: + create: software_install.sh + start: software_start.sh + + server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 1024 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Ubuntu + version: 14.04 diff --git a/tosca2heat/heat-translator/translator/tests/data/tosca_web_application.yaml b/tosca2heat/heat-translator/translator/tests/data/tosca_web_application.yaml new file mode 100644 index 0000000..d5ab038 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/data/tosca_web_application.yaml @@ -0,0 +1,56 @@ +tosca_definitions_version: tosca_simple_yaml_1_0 + +description: > + TOSCA simple profile with a web application. + +topology_template: + inputs: + cpus: + type: integer + description: Number of CPUs for the server. + constraints: + - valid_values: [ 1, 2, 4, 8 ] + default: 1 + context_root: + type: string + description: Context root for installing the application. + default: app + + node_templates: + web_app: + type: tosca.nodes.WebApplication + properties: + context_root: { get_input: context_root } + requirements: + - host: web_server + interfaces: + Standard: + create: + implementation: web_app_install.sh + inputs: + context_root: { get_input: context_root } + start: web_app_start.sh + + web_server: + type: tosca.nodes.WebServer + requirements: + - host: server + interfaces: + Standard: + create: web_server_install.sh + start: web_server_start.sh + + server: + type: tosca.nodes.Compute + capabilities: + host: + properties: + disk_size: 10 GB + num_cpus: { get_input: cpus } + mem_size: 1024 MB + os: + properties: + architecture: x86_64 + type: Linux + distribution: Ubuntu + version: 14.04 diff --git a/tosca2heat/heat-translator/translator/tests/test_conf.py b/tosca2heat/heat-translator/translator/tests/test_conf.py new file mode 100644 index 0000000..6506c27 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/test_conf.py @@ -0,0 +1,57 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +import os + +from translator.conf.config import ConfigProvider as translatorConfig +from translator.tests.base import TestCase + + +def reload_config(func): + '''Decorator to reload config. + + Set to default values defined in translator.conf file + + ''' + + def reload(*args): + func(*args) + path = os.path.dirname(os.path.abspath(__file__)) + '/../conf/' + conf_file = os.path.join(path, 'translator.conf') + translatorConfig._load_config(conf_file) + + return reload + + +class ConfTest(TestCase): + + @reload_config + @mock.patch('six.moves.configparser.ConfigParser') + def test_load_config(self, mock_config_parser): + translatorConfig._translator_config.read = mock.MagicMock() + translatorConfig._load_config('fake_file.conf') + self.assertTrue(translatorConfig._translator_config.read.called) + + def test_get_value(self): + ret_value = mock.MagicMock(return_value='hot') + translatorConfig._translator_config.get = ret_value + value = translatorConfig.get_value('DEFAULT', 'language') + self.assertTrue(translatorConfig._translator_config.get.called) + self.assertEqual(value, 'hot') + + def test_get_all_values(self): + ret_value = mock.MagicMock(return_value=['hot']) + translatorConfig._translator_config.items = ret_value + values = translatorConfig.get_all_values() + self.assertTrue(translatorConfig._translator_config.items.called) + self.assertEqual(values[0], 'hot') diff --git a/tosca2heat/heat-translator/translator/tests/test_shell.py b/tosca2heat/heat-translator/translator/tests/test_shell.py new file mode 100644 index 0000000..b001c1a --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/test_shell.py @@ -0,0 +1,191 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import ast +import json +import os +import shutil +import tempfile + +from mock import patch +from toscaparser.common import exception +from toscaparser.utils.gettextutils import _ +import translator.shell as shell +from translator.tests.base import TestCase + + +class ShellTest(TestCase): + tosca_helloworld = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "data/tosca_helloworld.yaml") + template_file = '--template-file=' + tosca_helloworld + template_type = '--template-type=tosca' + template_validation = "--validate-only=true" + 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', + self.template_type)) + err_msg = _('The path template.txt is not a valid file or URL.') + 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)) + + 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)) + + def test_valid_template(self): + try: + shell.main([self.template_file, self.template_type]) + 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__)), + "data/tosca_single_instance_wordpress.yaml") + parameters = '--parameters="cpus=2;db_name=wpdb;db_user=test;'\ + 'db_port=2000;db_root_pwd=fun2test;db_pwd=fun2test"' + template = '--template-file=' + tosca_single_instance_wordpress + try: + shell.main([template, self.template_type, parameters]) + except Exception: + self.fail(self.failure_msg) + + def test_validate_only(self): + try: + shell.main([self.template_file, self.template_type, + self.template_validation]) + except Exception: + self.fail(self.failure_msg) + + template = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "data/tosca_helloworld_invalid.yaml") + invalid_template = '--template-file=' + template + self.assertRaises(exception.ValidationError, shell.main, + [invalid_template, self.template_type, + self.template_validation]) + + def test_output_file(self): + temp_dir = tempfile.mkdtemp() + temp_file = "/test_translation_output.txt" + output_file = "--output-file=" + temp_dir + temp_file + try: + shell.main([self.template_file, self.template_type, output_file]) + except Exception: + self.fail(self.failure_msg) + finally: + if temp_dir: + shutil.rmtree(temp_dir) + self.assertTrue(temp_dir is None or + not os.path.exists(temp_dir)) + + @patch('uuid.uuid4') + @patch('translator.common.utils.check_for_env_variables') + @patch('requests.post') + @patch('translator.common.utils.get_url_for') + @patch('translator.common.utils.get_token_id') + @patch('os.getenv') + @patch('translator.hot.tosca.tosca_compute.' + 'ToscaCompute._create_nova_flavor_dict') + def test_template_deploy_with_credentials(self, mock_flavor_dict, + mock_os_getenv, + mock_token, + mock_url, mock_post, + mock_env, + mock_uuid): + mock_uuid.return_value = 'abcXXX-abcXXX' + mock_env.return_value = True + mock_flavor_dict.return_value = { + 'm1.medium': {'mem_size': 4096, 'disk_size': 40, 'num_cpus': 2} + } + mock_url.return_value = 'http://abc.com' + mock_token.return_value = 'mock_token' + mock_os_getenv.side_effect = ['demo', 'demo', + 'demo', 'http://www.abc.com'] + try: + data = { + 'stack_name': 'heat_abcXXX', + 'parameters': {}, + 'template': { + 'outputs': {}, + 'heat_template_version': '2013-05-23', + 'description': 'Template for deploying a single server ' + 'with predefined properties.\n', + 'parameters': {}, + 'resources': { + 'my_server': { + 'type': 'OS::Nova::Server', + 'properties': { + 'flavor': 'm1.medium', + 'user_data_format': 'SOFTWARE_CONFIG', + 'image': 'rhel-6.5-test-image' + } + } + } + } + } + + mock_heat_res = { + "stack": { + "id": 1234 + } + } + headers = { + 'Content-Type': 'application/json', + 'X-Auth-Token': 'mock_token' + } + + class mock_response(object): + def __init__(self, status_code, _content): + self.status_code = status_code + self._content = _content + + mock_response_obj = mock_response(201, json.dumps(mock_heat_res)) + mock_post.return_value = mock_response_obj + shell.main([self.template_file, self.template_type, + "--deploy"]) + args, kwargs = mock_post.call_args + self.assertEqual(args[0], 'http://abc.com/stacks') + self.assertEqual(ast.literal_eval(kwargs['data']), data) + self.assertEqual(kwargs['headers'], headers) + except Exception: + self.fail(self.failure_msg) diff --git a/tosca2heat/heat-translator/translator/tests/test_template.py b/tosca2heat/heat-translator/translator/tests/test_template.py new file mode 100644 index 0000000..7cced36 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/test_template.py @@ -0,0 +1,63 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +from toscaparser.tosca_template import ToscaTemplate +from translator.tests.base import TestCase + + +class ToscaMongoNodejsTest(TestCase): + parsed_params = {'storage_snapshot_id': 'test_id', + 'storage_location': '/test', 'cpus': '1', + 'storage_size': '1'} + + '''TOSCA template with nodejs, app and mongodb on 2 servers.''' + tosca_tpl = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "../tests/data/tosca_nodejs_mongodb_two_instances.yaml") + tosca = ToscaTemplate(tosca_tpl, parsed_params) + + def test_relationship_def(self): + expected_relationship = 'tosca.relationships.HostedOn' + expected_capabilities_names = 'node' + for tpl in self.tosca.nodetemplates: + if tpl.name == 'nodejs': + def_keys = tpl.type_definition.relationship.keys() + self.assertIn( + expected_relationship, + sorted([x.type for x in def_keys])) + self.assertIn( + expected_capabilities_names, + sorted([x.capability_name for x in def_keys])) + + def test_relationships(self): + expected_relationship = ['tosca.relationships.HostedOn'] + expected_relatednodes = ['app_server'] + for tpl in self.tosca.nodetemplates: + rels = tpl.relationships + if rels: + if tpl.name == 'nodejs': + self.assertEqual( + expected_relationship, + sorted([x.type for x in tpl.relationships.keys()])) + self.assertEqual( + expected_relatednodes, + sorted([y.name for y in tpl.relationships.values()])) + + def test_related_nodes(self): + expected_nodejs = ['app_server'] + actual_nodejs = [] + for tpl in self.tosca.nodetemplates: + if tpl.name == 'nodejs': + for node in tpl.related_nodes: + actual_nodejs.append(node.name) + self.assertEqual(sorted(actual_nodejs), expected_nodejs) diff --git a/tosca2heat/heat-translator/translator/tests/test_tosca_hot_translation.py b/tosca2heat/heat-translator/translator/tests/test_tosca_hot_translation.py new file mode 100644 index 0000000..e58d842 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/test_tosca_hot_translation.py @@ -0,0 +1,633 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import json +import os + +from toscaparser.common.exception import ExceptionCollector +from toscaparser.common.exception import URLException +from toscaparser.common.exception import ValidationError +from toscaparser.utils.gettextutils import _ +from translator.common.utils import TranslationUtils +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} + diff = TranslationUtils.compare_tosca_translation_with_hot(tosca_file, + hot_file, + params) + self.assertEqual({}, diff, '<difference> : ' + + json.dumps(diff, indent=4, separators=(', ', ': '))) + + 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=(', ', ': '))) + + 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=(', ', ': '))) + + def test_hot_translate_wordpress_single_instance(self): + tosca_file = '../tests/data/tosca_single_instance_wordpress.yaml' + hot_file = '../tests/data/hot_output/' \ + 'hot_single_instance_wordpress.yaml' + params = {'db_name': 'wordpress', + 'db_user': 'wp_user', + 'db_pwd': 'wp_pass', + '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=(', ', ': '))) + + 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=(', ', ': '))) + + 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=(', ', ': '))) + + def test_hot_translate_elk(self): + tosca_file = '../tests/data/tosca_elk.yaml' + hot_file = '../tests/data/hot_output/hot_elk.yaml' + 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=(', ', ': '))) + + def test_hot_translate_nodejs_mongodb_two_instances(self): + tosca_file = '../tests/data/tosca_nodejs_mongodb_two_instances.yaml' + hot_file = '../tests/data/hot_output/' \ + 'hot_nodejs_mongodb_two_instances.yaml' + 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=(', ', ': '))) + + def test_hot_translate_blockstorage_with_attachment(self): + tosca_file = '../tests/data/storage/' \ + 'tosca_blockstorage_with_attachment.yaml' + hot_file = '../tests/data/hot_output/storage/' \ + 'hot_blockstorage_with_attachment.yaml' + params = {'cpus': 1, + '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=(', ', ': '))) + + def test_hot_translate_blockstorage_with_custom_relationship_type(self): + tosca_file = '../tests/data/storage/' \ + 'tosca_blockstorage_with_custom_relationship_type.yaml' + hot_file = '../tests/data/hot_output/storage/' \ + 'hot_blockstorage_with_custom_relationship_type.yaml' + params = {'cpus': 1, + '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=(', ', ': '))) + + def test_hot_translate_blockstorage_with_relationship_template(self): + tosca_file = '../tests/data/storage/' \ + 'tosca_blockstorage_with_relationship_template.yaml' + hot_file = '../tests/data/hot_output/storage/' \ + 'hot_blockstorage_with_relationship_template.yaml' + 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=(', ', ': '))) + + def test_hot_translate_blockstorage_with_attachment_notation1(self): + tosca_file = '../tests/data/storage/' \ + 'tosca_blockstorage_with_attachment_notation1.yaml' + hot_file1 = '../tests/data/hot_output/storage/' \ + 'hot_blockstorage_with_attachment_notation1_alt1.yaml' + hot_file2 = '../tests/data/hot_output/storage/' \ + 'hot_blockstorage_with_attachment_notation1_alt2.yaml' + params = {'cpus': 1, + '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=(', ', ': '))) + except Exception: + diff2 = TranslationUtils.compare_tosca_translation_with_hot( + tosca_file, hot_file2, params) + self.assertEqual({}, diff2, '<difference> : ' + + json.dumps(diff2, indent=4, + separators=(', ', ': '))) + + def test_hot_translate_blockstorage_with_attachment_notation2(self): + tosca_file = '../tests/data/storage/' \ + 'tosca_blockstorage_with_attachment_notation2.yaml' + hot_file1 = '../tests/data/hot_output/storage/' \ + 'hot_blockstorage_with_attachment_notation2_alt1.yaml' + hot_file2 = '../tests/data/hot_output/storage/' \ + 'hot_blockstorage_with_attachment_notation2_alt2.yaml' + params = {'cpus': 1, + '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=(', ', ': '))) + except Exception: + diff2 = TranslationUtils.compare_tosca_translation_with_hot( + tosca_file, hot_file2, params) + self.assertEqual({}, diff2, '<difference> : ' + + json.dumps(diff2, indent=4, + separators=(', ', ': '))) + + def test_hot_translate_multiple_blockstorage_with_attachment(self): + tosca_file = '../tests/data/storage/' \ + 'tosca_multiple_blockstorage_with_attachment.yaml' + hot_file1 = '../tests/data/hot_output/storage/' \ + 'hot_multiple_blockstorage_with_attachment_alt1.yaml' + hot_file2 = '../tests/data/hot_output/storage/' \ + 'hot_multiple_blockstorage_with_attachment_alt2.yaml' + params = {'cpus': 1, + '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=(', ', ': '))) + except Exception: + diff2 = TranslationUtils.compare_tosca_translation_with_hot( + tosca_file, hot_file2, params) + self.assertEqual({}, diff2, '<difference> : ' + + json.dumps(diff2, indent=4, + separators=(', ', ': '))) + + 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=(', ', ': '))) + + 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=(', ', ': '))) + + def test_hot_translate_server_on_existing_network(self): + tosca_file = '../tests/data/network/' \ + 'tosca_server_on_existing_network.yaml' + 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=(', ', ': '))) + + def test_hot_translate_two_servers_one_network(self): + tosca_file = '../tests/data/network/tosca_two_servers_one_network.yaml' + hot_file = '../tests/data/hot_output/network/' \ + 'hot_two_servers_one_network.yaml' + params = {'network_name': 'my_private_net', + '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=(', ', ': '))) + + def test_hot_translate_one_server_three_networks(self): + tosca_file = '../tests/data/network/' \ + 'tosca_one_server_three_networks.yaml' + 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=(', ', ': '))) + + 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=(', ', ': '))) + + 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=(', ', ': '))) + + def test_hot_translate_template_with_url_import(self): + tosca_file = '../tests/data/' \ + 'tosca_single_instance_wordpress_with_url_import.yaml' + hot_file = '../tests/data/hot_output/' \ + 'hot_single_instance_wordpress.yaml' + params = {'db_name': 'wordpress', + 'db_user': 'wp_user', + 'db_pwd': 'wp_pass', + '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=(', ', ': '))) + + def test_hot_translate_template_by_url_with_local_import(self): + tosca_file = 'https://raw.githubusercontent.com/openstack/' \ + 'heat-translator/master/translator/tests/data/' \ + 'tosca_single_instance_wordpress.yaml' + hot_file = '../tests/data/hot_output/' \ + 'hot_single_instance_wordpress.yaml' + params = {'db_name': 'wordpress', + 'db_user': 'wp_user', + 'db_pwd': 'wp_pass', + '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=(', ', ': '))) + + def test_hot_translate_template_by_url_with_local_abspath_import(self): + tosca_file = 'https://raw.githubusercontent.com/openstack/' \ + 'heat-translator/master/translator/tests/data/' \ + 'tosca_single_instance_wordpress_with_local_abspath' \ + '_import.yaml' + hot_file = '../tests/data/hot_output/' \ + 'hot_single_instance_wordpress.yaml' + params = {'db_name': 'wordpress', + 'db_user': 'wp_user', + 'db_pwd': 'wp_pass', + '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) + + def test_hot_translate_template_by_url_with_url_import(self): + tosca_url = 'https://raw.githubusercontent.com/openstack/' \ + 'heat-translator/master/translator/tests/data/' \ + 'tosca_single_instance_wordpress_with_url_import.yaml' + hot_file = '../tests/data/hot_output/' \ + 'hot_single_instance_wordpress.yaml' + params = {'db_name': 'wordpress', + 'db_user': 'wp_user', + 'db_pwd': 'wp_pass', + '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=(', ', ': '))) + + 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=(', ', ': '))) + + def test_translate_single_instance_wordpress_csar(self): + tosca_file = '../tests/data/csar_single_instance_wordpress.zip' + hot_file = '../tests/data/hot_output/' \ + 'hot_single_instance_wordpress_from_csar.yaml' + params = {'db_name': 'wordpress', + 'db_user': 'wp_user', + 'db_pwd': 'wp_pass', + '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=(', ', ': '))) + + def test_translate_elk_csar_from_url(self): + tosca_file = 'https://github.com/openstack/heat-translator/raw/' \ + 'master/translator/tests/data/csar_elk.zip' + hot_file = '../tests/data/hot_output/hot_elk_from_csar.yaml' + 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=(', ', ': '))) + + 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) + + 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) + + 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) + + 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) + + 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) + + 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=(', ', ': '))) + + 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=(', ', ': '))) + + 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=(', ', ': '))) + + 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=(', ', ': '))) + + 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=(', ', ': '))) + + 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=(', ', ': '))) + + def test_hot_translate_without_tosca_os_version(self): + tosca_file = '../tests/data/' \ + 'test_single_server_without_optional_version_prop.yaml' + 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=(', ', ': '))) + + 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=(', ', ': '))) + + def test_hot_translate_custom_networks_nodes_inline(self): + tosca_file = '../tests/data/network/' \ + 'test_tosca_custom_network_nodes_inline.yaml' + 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=(', ', ': '))) + + def test_hot_translate_custom_networks_nodes_imports(self): + tosca_file = '../tests/data/network/' \ + 'test_tosca_custom_network_nodes_imports.yaml' + 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=(', ', ': '))) + + 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=(', ', ': '))) + + 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=(', ', ': '))) diff --git a/tosca2heat/heat-translator/translator/tests/test_utils.py b/tosca2heat/heat-translator/translator/tests/test_utils.py new file mode 100644 index 0000000..b6d75d9 --- /dev/null +++ b/tosca2heat/heat-translator/translator/tests/test_utils.py @@ -0,0 +1,236 @@ +# 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 +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +from toscaparser.tests.base import TestCase +import translator.common.utils + + +class CommonUtilsTest(TestCase): + + MemoryUnit = translator.common.utils.MemoryUnit + cmpUtils = translator.common.utils.CompareUtils + yamlUtils = translator.common.utils.YamlUtils + UrlUtils = translator.common.utils.UrlUtils + + def test_convert_unit_size_to_num(self): + size = '1 TB' + num_to_convert = 'GB' + expected_output = 1000 + output = self.MemoryUnit.convert_unit_size_to_num(size, num_to_convert) + self.assertEqual(output, expected_output) + + size = '40 GB' + num_to_convert = 'MB' + expected_output = 40000 + output = self.MemoryUnit.convert_unit_size_to_num(size, num_to_convert) + self.assertEqual(output, expected_output) + + size = '20 B' + num_to_convert = None + expected_output = 20 + output = self.MemoryUnit.convert_unit_size_to_num(size, num_to_convert) + self.assertEqual(output, expected_output) + + def test_validate_unit(self): + unit = 'AB' + exp_msg = ('Provided unit "{0}" is not valid. The valid units are ' + '{1}').format(unit, self.MemoryUnit.UNIT_SIZE_DICT.keys()) + try: + self.MemoryUnit.validate_unit(unit) + except Exception as err: + self.assertTrue( + isinstance(err, ValueError)) + self.assertEqual(exp_msg, err.__str__()) + + def test_unit_size_conversion_to_GNU_standard(self): + unit = 'gB' + standard_unit = 'GB' + converted_unit = self.MemoryUnit.validate_unit(unit) + self.assertEqual(converted_unit, standard_unit) + + unit = 'KB' + standard_unit = 'kB' + converted_unit = self.MemoryUnit.validate_unit(unit) + self.assertEqual(converted_unit, standard_unit) + + unit = 'kb' + standard_unit = 'kB' + converted_unit = self.MemoryUnit.validate_unit(unit) + self.assertEqual(converted_unit, standard_unit) + + unit = 'kB' + standard_unit = 'kB' + converted_unit = self.MemoryUnit.validate_unit(unit) + self.assertEqual(converted_unit, standard_unit) + + unit = 'MIB' + standard_unit = 'MiB' + converted_unit = self.MemoryUnit.validate_unit(unit) + self.assertEqual(converted_unit, standard_unit) + + def test_str_to_num_value_error(self): + str_to_convert = '55063.000000' + expected_output = 55063.0 + output = translator.common.utils.str_to_num(str_to_convert) + self.assertEqual(output, expected_output) + + def test_compare_dicts_unequal(self): + dict1 = {'allowed_values': [1, 2, 4, 8], + 'server3': {'depends_on': ['server1', 'server2']}} + dict2 = {'allowed_values': [1, 2, 4, 8], + 'server3': {'depends_on': ['server2', 'server1']}} + self.assertFalse(self.cmpUtils.compare_dicts(dict1, dict2)) + + def test_dicts_equivalent_empty_dicts(self): + self.assertTrue(self.cmpUtils.compare_dicts(None, None)) + self.assertFalse(self.cmpUtils.compare_dicts(None, {})) + self.assertFalse(self.cmpUtils.compare_dicts(None, {'x': '2'})) + + def test_compareutils_reorder(self): + dic = {'output': {'website_url': {'value': {'get_attr': + ['server', 'networks', + 'private', 0]}}}, + 'allowed_values': [2, 8, 1, 4], + 'server3': {'depends_on': ['server2', 'server1']}} + reordered_dic = {'output': {'website_url': {'value': {'get_attr': + ['server', 'networks', + 'private', 0]}}}, + 'allowed_values': [1, 2, 4, 8], + 'server3': {'depends_on': ['server1', 'server2']}} + self.assertEqual(reordered_dic, self.cmpUtils.reorder(dic)) + + def test_compareutils_diff_dicts_both_null(self): + expected = None + provided = None + self.assertEqual({}, + self.cmpUtils.diff_dicts(expected, provided)) + + def test_compareutils_diff_dicts_one_null(self): + expected = {'keyname': 'userkey'} + provided = None + self.assertEqual( + {self.cmpUtils.MISMATCH_VALUE1_LABEL: {'keyname': 'userkey'}, + self.cmpUtils.MISMATCH_VALUE2_LABEL: None}, + self.cmpUtils.diff_dicts(expected, provided)) + + def test_compareutils_diff_dicts_missing_key(self): + expected = {'server3': {'depends_on': ['server1', 'server2'], + 'keyname': 'userkey'}} + provided = {'server3': {'depends_on': ['server2', 'server1']}} + self.assertEqual( + {'server3': {'keyname': + {self.cmpUtils.MISMATCH_VALUE1_LABEL: 'userkey', + self.cmpUtils.MISMATCH_VALUE2_LABEL: None}}}, + self.cmpUtils.diff_dicts(expected, provided)) + + def test_compareutils_diff_dicts_missing_key_other_dict(self): + expected = {'server3': {'depends_on': ['server1', 'server2']}} + provided = {'server3': {'depends_on': ['server2', 'server1'], + 'keyname': 'userkey'}} + self.assertEqual( + {'server3': {'keyname': + {self.cmpUtils.MISMATCH_VALUE1_LABEL: None, + self.cmpUtils.MISMATCH_VALUE2_LABEL: 'userkey'}}}, + self.cmpUtils.diff_dicts(expected, provided)) + + def test_compareutils_diff_dicts_value_diff(self): + expected = \ + {'output': + {'website_url': + {'value': + {'get_attr': ['server', 'networks', 'private', 0]}}}, + 'server3': {'depends_on': ['server2', 'server1']}} + provided = \ + {'output': + {'website_url': + {'value': + {'get_attr': ['server', 'networks', 'public', 0]}}}, + 'server3': {'depends_on': ['server2', 'server1']}} + self.assertEqual( + {'output': + {'website_url': + {'value': + {'get_attr': + {self.cmpUtils.MISMATCH_VALUE1_LABEL: + ['server', 'networks', 'private', 0], + self.cmpUtils.MISMATCH_VALUE2_LABEL: + ['server', 'networks', 'public', 0]}}}}}, + self.cmpUtils.diff_dicts(expected, provided)) + + def test_yamlutils_get_dict_missing_file(self): + self.assertIsNone(self.yamlUtils.get_dict('./no_file.yaml')) + + def test_yamlutils_get_dict(self): + yaml_file = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + '../tests/data/custom_types/rsyslog.yaml') + dict = \ + {'tosca_definitions_version': 'tosca_simple_yaml_1_0', + 'description': + 'RSYSLOG is the Rocket-fast SYStem for LOG processing.\n', + 'node_types': + {'tosca.nodes.SoftwareComponent.Rsyslog': + {'derived_from': 'tosca.nodes.SoftwareComponent', + 'requirements': + [{'log_endpoint': + {'capability': 'tosca.capabilities.Endpoint', + 'node': 'tosca.nodes.SoftwareComponent.Logstash', + 'relationship': 'tosca.relationships.ConnectsTo'}}]}}} + self.assertEqual(dict, self.yamlUtils.get_dict(yaml_file)) + + def test_yamlutils_compare_yamls(self): + yaml_file1 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + '../tests/data/custom_types/kibana.yaml') + yaml_file2 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + '../tests/data/custom_types/collectd.yaml') + self.assertTrue(self.yamlUtils.compare_yamls(yaml_file1, yaml_file1)) + self.assertFalse(self.yamlUtils.compare_yamls(yaml_file1, yaml_file2)) + + def test_yamlutils_compare_yaml_dict(self): + yaml_file1 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + '../tests/data/custom_types/rsyslog.yaml') + yaml_file2 = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + '../tests/data/custom_types/collectd.yaml') + dict = \ + {'tosca_definitions_version': 'tosca_simple_yaml_1_0', + 'description': + 'RSYSLOG is the Rocket-fast SYStem for LOG processing.\n', + 'node_types': + {'tosca.nodes.SoftwareComponent.Rsyslog': + {'derived_from': 'tosca.nodes.SoftwareComponent', + 'requirements': + [{'log_endpoint': + {'capability': 'tosca.capabilities.Endpoint', + 'node': 'tosca.nodes.SoftwareComponent.Logstash', + 'relationship': 'tosca.relationships.ConnectsTo'}}]}}} + self.assertEqual({}, self.cmpUtils.diff_dicts( + self.yamlUtils.get_dict(yaml_file1), dict)) + self.assertFalse(self.yamlUtils.compare_yaml_dict(yaml_file2, dict)) + + def test_assert_value_is_num(self): + value = 1 + output = translator.common.utils.str_to_num(value) + self.assertEqual(value, output) + + def test_urlutils_validate_url(self): + self.assertTrue(self.UrlUtils.validate_url("http://www.github.com/")) + self.assertTrue( + self.UrlUtils.validate_url("https://github.com:81/a/2/a.b")) + self.assertTrue(self.UrlUtils.validate_url("ftp://github.com")) + self.assertFalse(self.UrlUtils.validate_url("github.com")) + self.assertFalse(self.UrlUtils.validate_url("123")) + self.assertFalse(self.UrlUtils.validate_url("a/b/c")) |