From f7b240c6893a48d71da29974e54cb560b6575eb3 Mon Sep 17 00:00:00 2001 From: shangxdy Date: Sun, 26 Feb 2017 16:22:30 +0800 Subject: Sync heat-translator code Sync heat-translator code from upstream JIRA:PARSER-119 Change-Id: I2287b78ad38bc54f7740fd1ee5f08989d5e680bf Signed-off-by: shangxdy --- .../heat-translator/translator/common/exception.py | 5 ++ .../heat-translator/translator/common/flavors.py | 64 +++++++++++++++ .../heat-translator/translator/common/images.py | 91 ++++++++++++++++++++++ .../heat-translator/translator/common/utils.py | 67 +++++++++++++--- 4 files changed, 217 insertions(+), 10 deletions(-) create mode 100644 tosca2heat/heat-translator/translator/common/flavors.py create mode 100644 tosca2heat/heat-translator/translator/common/images.py (limited to 'tosca2heat/heat-translator/translator/common') diff --git a/tosca2heat/heat-translator/translator/common/exception.py b/tosca2heat/heat-translator/translator/common/exception.py index be86116..f16d3d7 100644 --- a/tosca2heat/heat-translator/translator/common/exception.py +++ b/tosca2heat/heat-translator/translator/common/exception.py @@ -43,6 +43,11 @@ class ToscaClassImportError(TOSCAException): 'exists and has no language definition errors.') +class UnsupportedTypeError(TOSCAException): + msg_fmt = _('Type "%(type)s" is valid TOSCA type but translation ' + 'support is not yet available.') + + 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/flavors.py b/tosca2heat/heat-translator/translator/common/flavors.py new file mode 100644 index 0000000..c44f883 --- /dev/null +++ b/tosca2heat/heat-translator/translator/common/flavors.py @@ -0,0 +1,64 @@ +# 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 + +try: + import novaclient.client + client_available = True +except ImportError: + client_available = False + pass + +log = logging.getLogger('heat-translator') + + +PREDEF_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} +} + +SESSION = None + +FLAVORS = {} + + +def get_flavors(): + global FLAVORS + + if FLAVORS: + return FLAVORS + + if SESSION is not None and client_available: + try: + client = novaclient.client.Client("2", session=SESSION) + except Exception as e: + # Handles any exception coming from openstack + log.warn(_('Choosing predefined flavors since received ' + 'Openstack Exception: %s') % str(e)) + else: + for flv in client.flavors.list(detailed=True): + FLAVORS[str(flv.name)] = { + "mem_size": flv.ram, + "disk_size": flv.disk, + "num_cpus": flv.vcpus + } + + if not FLAVORS: + FLAVORS = PREDEF_FLAVORS + + return FLAVORS diff --git a/tosca2heat/heat-translator/translator/common/images.py b/tosca2heat/heat-translator/translator/common/images.py new file mode 100644 index 0000000..f9fa4f1 --- /dev/null +++ b/tosca2heat/heat-translator/translator/common/images.py @@ -0,0 +1,91 @@ +# 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 + +try: + import glanceclient.client + client_available = True +except ImportError: + client_available = False + pass + +log = logging.getLogger('heat-translator') + + +PREDEF_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'} +} + +SESSION = None + +IMAGES = {} + + +def get_images(): + global IMAGES + + if IMAGES: + return IMAGES + + if SESSION is not None and client_available: + try: + client = glanceclient.client.Client("2", session=SESSION) + except Exception as e: + # Handles any exception coming from openstack + log.warn(_('Choosing predefined images since received ' + 'Openstack Exception: %s') % str(e)) + else: + for image in client.images.list(): + image_name = image.name.encode('ascii', 'ignore') + metadata = ["architecture", "type", "distribution", "version"] + if any(key in image.keys() for key in metadata): + IMAGES[image_name] = {} + for key in metadata: + if key in image.keys(): + IMAGES[image_name][key] = image[key] + + if not IMAGES: + IMAGES = PREDEF_IMAGES + + return IMAGES diff --git a/tosca2heat/heat-translator/translator/common/utils.py b/tosca2heat/heat-translator/translator/common/utils.py index 459b5ee..874c8ec 100644 --- a/tosca2heat/heat-translator/translator/common/utils.py +++ b/tosca2heat/heat-translator/translator/common/utils.py @@ -18,8 +18,11 @@ import numbers import os import re import requests +import six from six.moves.urllib.parse import urlparse +import tempfile import yaml +import zipfile from toscaparser.utils.gettextutils import _ import toscaparser.utils.yamlparser @@ -193,7 +196,7 @@ class YamlUtils(object): def get_dict(yaml_file): '''Returns the dictionary representation of the given YAML spec.''' try: - return yaml.load(open(yaml_file)) + return yaml.safe_load(open(yaml_file)) except IOError: return None @@ -213,7 +216,7 @@ class YamlUtils(object): class TranslationUtils(object): @staticmethod - def compare_tosca_translation_with_hot(tosca_file, hot_file, params): + def compare_tosca_translation_with_hot(tosca_file, hot_files, params): '''Verify tosca translation against the given hot specification. inputs: @@ -234,16 +237,28 @@ class TranslationUtils(object): if not a_file: tosca_tpl = tosca_file - expected_hot_tpl = os.path.join( - os.path.dirname(os.path.abspath(__file__)), hot_file) + expected_hot_templates = [] + for hot_file in hot_files: + expected_hot_templates.append(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) + basename = os.path.basename(hot_files[0]) + output_hot_templates = translate.translate_to_yaml_files_dict(basename) + output_dict = {} + for output_hot_template_name in output_hot_templates: + output_dict[output_hot_template_name] = \ + toscaparser.utils.yamlparser.simple_parse( + output_hot_templates[output_hot_template_name]) + + expected_output_dict = {} + for expected_hot_template in expected_hot_templates: + expected_output_dict[os.path.basename(expected_hot_template)] = \ + YamlUtils.get_dict(expected_hot_template) + + return CompareUtils.diff_dicts(expected_output_dict, output_dict) class UrlUtils(object): @@ -262,12 +277,17 @@ class UrlUtils(object): def str_to_num(value): """Convert a string representation of a number into a numeric type.""" - if isinstance(value, numbers.Number): + if isinstance(value, numbers.Number) \ + or isinstance(value, six.integer_types) \ + or isinstance(value, float): return value try: return int(value) except ValueError: - return float(value) + try: + return float(value) + except ValueError: + return None def check_for_env_variables(): @@ -317,3 +337,30 @@ def get_token_id(access_dict): if access_dict is None: return None return access_dict['access']['token']['id'] + + +def decompress(zip_file, dir=None): + """Decompress Zip file + + Decompress any zip file. For example, TOSCA CSAR + + inputs: + zip_file: file in zip format + dir: directory to decompress zip. If not provided an unique temporary + directory will be generated and used. + return: + dir: absolute path to the decopressed directory + """ + if not dir: + dir = tempfile.NamedTemporaryFile().name + with zipfile.ZipFile(zip_file, "r") as zf: + zf.extractall(dir) + return dir + + +def get_dict_value(dict_item, key, get_files): + if key in dict_item: + return get_files.append(dict_item[key]) + for k, v in dict_item.items(): + if isinstance(v, dict): + get_dict_value(v, key, get_files) -- cgit 1.2.3-korg