1 # Licensed under the Apache License, Version 2.0 (the "License"); you may
2 # not use this file except in compliance with the License. You may obtain
3 # a copy of the License at
5 # http://www.apache.org/licenses/LICENSE-2.0
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10 # License for the specific language governing permissions and limitations
19 from six.moves.urllib.parse import urlparse
22 from toscaparser.utils.gettextutils import _
23 import toscaparser.utils.yamlparser
25 YAML_ORDER_PARSER = toscaparser.utils.yamlparser.simple_ordered_parse
26 log = logging.getLogger('tosca')
27 log = logging.getLogger('heat-translator')
30 class MemoryUnit(object):
32 UNIT_SIZE_DEFAULT = 'B'
33 UNIT_SIZE_DICT = {'B': 1, 'kB': 1000, 'KiB': 1024, 'MB': 1000000,
34 'MiB': 1048576, 'GB': 1000000000,
35 'GiB': 1073741824, 'TB': 1000000000000,
39 def convert_unit_size_to_num(size, unit=None):
40 """Convert given size to a number representing given unit.
42 If unit is None, convert to a number representing UNIT_SIZE_DEFAULT
43 :param size: unit size e.g. 1 TB
44 :param unit: unit to be converted to e.g GB
45 :return: converted number e.g. 1000 for 1 TB size and unit GB
48 unit = MemoryUnit.validate_unit(unit)
50 unit = MemoryUnit.UNIT_SIZE_DEFAULT
51 log.info(_('A memory unit is not provided for size; using the '
52 'default unit %(default)s') % {'default': 'B'})
53 regex = re.compile('(\d*)\s*(\w*)')
54 result = regex.match(str(size)).groups()
56 unit_size = MemoryUnit.validate_unit(result[1])
57 converted = int(str_to_num(result[0])
58 * MemoryUnit.UNIT_SIZE_DICT[unit_size]
59 * math.pow(MemoryUnit.UNIT_SIZE_DICT
61 log.info(_('Given size %(size)s is converted to %(num)s '
62 '%(unit)s') % {'size': size,
63 'num': converted, 'unit': unit})
65 converted = (str_to_num(result[0]))
69 def validate_unit(unit):
70 if unit in MemoryUnit.UNIT_SIZE_DICT.keys():
73 for key in MemoryUnit.UNIT_SIZE_DICT.keys():
74 if key.upper() == unit.upper():
77 msg = _('Provided unit "{0}" is not valid. The valid units are'
78 ' {1}').format(unit, MemoryUnit.UNIT_SIZE_DICT.keys())
83 class CompareUtils(object):
85 MISMATCH_VALUE1_LABEL = "<Expected>"
86 MISMATCH_VALUE2_LABEL = "<Provided>"
87 ORDERLESS_LIST_KEYS = ['allowed_values', 'depends_on']
90 def compare_dicts(dict1, dict2):
91 """Return False if not equal, True if both are equal."""
93 if dict1 is None and dict2 is None:
95 if dict1 is None or dict2 is None:
99 for dict1_item, dict2_item in zip(dict1.items(), dict2.items()):
100 if dict1_item != dict2_item:
101 msg = (_("%(label1)s: %(item1)s \n is not equal to \n:"
102 "%(label2)s: %(item2)s")
103 % {'label1': CompareUtils.MISMATCH_VALUE2_LABEL,
105 'label2': CompareUtils.MISMATCH_VALUE1_LABEL,
106 'item2': dict2_item})
113 def compare_hot_yamls(generated_yaml, expected_yaml):
114 hot_translated_dict = YAML_ORDER_PARSER(generated_yaml)
115 hot_expected_dict = YAML_ORDER_PARSER(expected_yaml)
116 return CompareUtils.compare_dicts(hot_translated_dict,
121 '''Canonicalize list items in the dictionary for ease of comparison.
123 For properties whose value is a list in which the order does not
124 matter, some pre-processing is required to bring those lists into a
125 canonical format. We use sorting just to make sure such differences
126 in ordering would not cause to a mismatch.
129 if type(dic) is not dict:
133 for key in dic.keys():
135 if type(value) is dict:
136 reordered[key] = CompareUtils.reorder(value)
137 elif type(value) is list \
138 and key in CompareUtils.ORDERLESS_LIST_KEYS:
139 reordered[key] = sorted(value)
141 reordered[key] = value
145 def diff_dicts(dict1, dict2, reorder=True):
146 '''Compares two dictionaries and returns their differences.
148 Returns a dictionary of mismatches between the two dictionaries.
149 An empty dictionary is returned if two dictionaries are equivalent.
150 The reorder parameter indicates whether reordering is required
151 before comparison or not.
155 dict1 = CompareUtils.reorder(dict1)
156 dict2 = CompareUtils.reorder(dict2)
158 if dict1 is None and dict2 is None:
160 if dict1 is None or dict2 is None:
161 return {CompareUtils.MISMATCH_VALUE1_LABEL: dict1,
162 CompareUtils.MISMATCH_VALUE2_LABEL: dict2}
165 keys1 = set(dict1.keys())
166 keys2 = set(dict2.keys())
167 for key in keys1.union(keys2):
168 if key in keys1 and key not in keys2:
169 diff[key] = {CompareUtils.MISMATCH_VALUE1_LABEL: dict1[key],
170 CompareUtils.MISMATCH_VALUE2_LABEL: None}
171 elif key not in keys1 and key in keys2:
172 diff[key] = {CompareUtils.MISMATCH_VALUE1_LABEL: None,
173 CompareUtils.MISMATCH_VALUE2_LABEL: dict2[key]}
178 if type(val1) is dict and type(val2) is dict:
179 diff[key] = CompareUtils.diff_dicts(val1, val2, False)
181 diff[key] = {CompareUtils.MISMATCH_VALUE1_LABEL: val1,
182 CompareUtils.MISMATCH_VALUE2_LABEL: val2}
186 class YamlUtils(object):
189 def get_dict(yaml_file):
190 '''Returns the dictionary representation of the given YAML spec.'''
192 return yaml.load(open(yaml_file))
197 def compare_yamls(yaml1_file, yaml2_file):
198 '''Returns true if two dictionaries are equivalent, false otherwise.'''
199 dict1 = YamlUtils.get_dict(yaml1_file)
200 dict2 = YamlUtils.get_dict(yaml2_file)
201 return CompareUtils.compare_dicts(dict1, dict2)
204 def compare_yaml_dict(yaml_file, dic):
205 '''Returns true if yaml matches the dictionary, false otherwise.'''
206 return CompareUtils.compare_dicts(YamlUtils.get_dict(yaml_file), dic)
209 class TranslationUtils(object):
212 def compare_tosca_translation_with_hot(tosca_file, hot_file, params):
213 '''Verify tosca translation against the given hot specification.
216 tosca_file: relative local path or URL to the tosca input file
217 hot_file: relative path to expected hot output
218 params: dictionary of parameter name value pairs
220 Returns as a dictionary the difference between the HOT translation
221 of the given tosca_file and the given hot_file.
224 from toscaparser.tosca_template import ToscaTemplate
225 from translator.hot.tosca_translator import TOSCATranslator
227 tosca_tpl = os.path.normpath(os.path.join(
228 os.path.dirname(os.path.abspath(__file__)), tosca_file))
229 a_file = os.path.isfile(tosca_tpl)
231 tosca_tpl = tosca_file
233 expected_hot_tpl = os.path.join(
234 os.path.dirname(os.path.abspath(__file__)), hot_file)
236 tosca = ToscaTemplate(tosca_tpl, params, a_file)
237 translate = TOSCATranslator(tosca, params)
239 output = translate.translate()
240 output_dict = toscaparser.utils.yamlparser.simple_parse(output)
241 expected_output_dict = YamlUtils.get_dict(expected_hot_tpl)
242 return CompareUtils.diff_dicts(output_dict, expected_output_dict)
245 class UrlUtils(object):
248 def validate_url(path):
249 """Validates whether the given path is a URL or not.
251 If the given path includes a scheme (http, https, ftp, ...) and a net
252 location (a domain name such as www.github.com) it is validated as a
255 parsed = urlparse(path)
256 return bool(parsed.scheme) and bool(parsed.netloc)
259 def str_to_num(value):
260 """Convert a string representation of a number into a numeric type."""
261 if isinstance(value, numbers.Number):