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
22 from six.moves.urllib.parse import urlparse
27 from toscaparser.utils.gettextutils import _
28 import toscaparser.utils.yamlparser
30 YAML_ORDER_PARSER = toscaparser.utils.yamlparser.simple_ordered_parse
31 log = logging.getLogger('heat-translator')
33 # Required environment variables to create openstackclient object.
34 ENV_VARIABLES = ['OS_AUTH_URL', 'OS_PASSWORD', 'OS_USERNAME', 'OS_TENANT_NAME']
37 class MemoryUnit(object):
39 UNIT_SIZE_DEFAULT = 'B'
40 UNIT_SIZE_DICT = {'B': 1, 'kB': 1000, 'KiB': 1024, 'MB': 1000000,
41 'MiB': 1048576, 'GB': 1000000000,
42 'GiB': 1073741824, 'TB': 1000000000000,
46 def convert_unit_size_to_num(size, unit=None):
47 """Convert given size to a number representing given unit.
49 If unit is None, convert to a number representing UNIT_SIZE_DEFAULT
50 :param size: unit size e.g. 1 TB
51 :param unit: unit to be converted to e.g GB
52 :return: converted number e.g. 1000 for 1 TB size and unit GB
55 unit = MemoryUnit.validate_unit(unit)
57 unit = MemoryUnit.UNIT_SIZE_DEFAULT
58 log.info(_('A memory unit is not provided for size; using the '
59 'default unit %(default)s.') % {'default': 'B'})
60 regex = re.compile('(\d*)\s*(\w*)')
61 result = regex.match(str(size)).groups()
63 unit_size = MemoryUnit.validate_unit(result[1])
64 converted = int(str_to_num(result[0])
65 * MemoryUnit.UNIT_SIZE_DICT[unit_size]
66 * math.pow(MemoryUnit.UNIT_SIZE_DICT
68 log.info(_('Given size %(size)s is converted to %(num)s '
69 '%(unit)s.') % {'size': size,
70 'num': converted, 'unit': unit})
72 converted = (str_to_num(result[0]))
76 def validate_unit(unit):
77 if unit in MemoryUnit.UNIT_SIZE_DICT.keys():
80 for key in MemoryUnit.UNIT_SIZE_DICT.keys():
81 if key.upper() == unit.upper():
84 msg = _('Provided unit "{0}" is not valid. The valid units are'
85 ' {1}').format(unit, MemoryUnit.UNIT_SIZE_DICT.keys())
90 class CompareUtils(object):
92 MISMATCH_VALUE1_LABEL = "<Expected>"
93 MISMATCH_VALUE2_LABEL = "<Provided>"
94 ORDERLESS_LIST_KEYS = ['allowed_values', 'depends_on']
97 def compare_dicts(dict1, dict2):
98 """Return False if not equal, True if both are equal."""
100 if dict1 is None and dict2 is None:
102 if dict1 is None or dict2 is None:
106 for dict1_item, dict2_item in zip(dict1.items(), dict2.items()):
107 if dict1_item != dict2_item:
108 msg = (_("%(label1)s: %(item1)s \n is not equal to \n:"
109 "%(label2)s: %(item2)s")
110 % {'label1': CompareUtils.MISMATCH_VALUE2_LABEL,
112 'label2': CompareUtils.MISMATCH_VALUE1_LABEL,
113 'item2': dict2_item})
120 def compare_hot_yamls(generated_yaml, expected_yaml):
121 hot_translated_dict = YAML_ORDER_PARSER(generated_yaml)
122 hot_expected_dict = YAML_ORDER_PARSER(expected_yaml)
123 return CompareUtils.compare_dicts(hot_translated_dict,
128 '''Canonicalize list items in the dictionary for ease of comparison.
130 For properties whose value is a list in which the order does not
131 matter, some pre-processing is required to bring those lists into a
132 canonical format. We use sorting just to make sure such differences
133 in ordering would not cause to a mismatch.
136 if type(dic) is not dict:
140 for key in dic.keys():
142 if type(value) is dict:
143 reordered[key] = CompareUtils.reorder(value)
144 elif type(value) is list \
145 and key in CompareUtils.ORDERLESS_LIST_KEYS:
146 reordered[key] = sorted(value)
148 reordered[key] = value
152 def diff_dicts(dict1, dict2, reorder=True):
153 '''Compares two dictionaries and returns their differences.
155 Returns a dictionary of mismatches between the two dictionaries.
156 An empty dictionary is returned if two dictionaries are equivalent.
157 The reorder parameter indicates whether reordering is required
158 before comparison or not.
162 dict1 = CompareUtils.reorder(dict1)
163 dict2 = CompareUtils.reorder(dict2)
165 if dict1 is None and dict2 is None:
167 if dict1 is None or dict2 is None:
168 return {CompareUtils.MISMATCH_VALUE1_LABEL: dict1,
169 CompareUtils.MISMATCH_VALUE2_LABEL: dict2}
172 keys1 = set(dict1.keys())
173 keys2 = set(dict2.keys())
174 for key in keys1.union(keys2):
175 if key in keys1 and key not in keys2:
176 diff[key] = {CompareUtils.MISMATCH_VALUE1_LABEL: dict1[key],
177 CompareUtils.MISMATCH_VALUE2_LABEL: None}
178 elif key not in keys1 and key in keys2:
179 diff[key] = {CompareUtils.MISMATCH_VALUE1_LABEL: None,
180 CompareUtils.MISMATCH_VALUE2_LABEL: dict2[key]}
185 if type(val1) is dict and type(val2) is dict:
186 diff[key] = CompareUtils.diff_dicts(val1, val2, False)
188 diff[key] = {CompareUtils.MISMATCH_VALUE1_LABEL: val1,
189 CompareUtils.MISMATCH_VALUE2_LABEL: val2}
193 class YamlUtils(object):
196 def get_dict(yaml_file):
197 '''Returns the dictionary representation of the given YAML spec.'''
199 return yaml.safe_load(open(yaml_file))
204 def compare_yamls(yaml1_file, yaml2_file):
205 '''Returns true if two dictionaries are equivalent, false otherwise.'''
206 dict1 = YamlUtils.get_dict(yaml1_file)
207 dict2 = YamlUtils.get_dict(yaml2_file)
208 return CompareUtils.compare_dicts(dict1, dict2)
211 def compare_yaml_dict(yaml_file, dic):
212 '''Returns true if yaml matches the dictionary, false otherwise.'''
213 return CompareUtils.compare_dicts(YamlUtils.get_dict(yaml_file), dic)
216 class TranslationUtils(object):
219 def compare_tosca_translation_with_hot(tosca_file, hot_files, params,
220 nested_resources=False):
221 '''Verify tosca translation against the given hot specification.
224 tosca_file: relative local path or URL to the tosca input file
225 hot_file: relative path to expected hot output
226 params: dictionary of parameter name value pairs
228 Returns as a dictionary the difference between the HOT translation
229 of the given tosca_file and the given hot_file.
232 from toscaparser.tosca_template import ToscaTemplate
233 from translator.hot.tosca_translator import TOSCATranslator
235 tosca_tpl = os.path.normpath(os.path.join(
236 os.path.dirname(os.path.abspath(__file__)), tosca_file))
237 a_file = os.path.isfile(tosca_tpl)
239 tosca_tpl = tosca_file
241 expected_hot_templates = []
242 for hot_file in hot_files:
243 expected_hot_templates.append(os.path.join(
244 os.path.dirname(os.path.abspath(__file__)), hot_file))
246 tosca = ToscaTemplate(tosca_tpl, params, a_file)
247 translate = TOSCATranslator(tosca, params)
249 basename = os.path.basename(hot_files[0])
250 output_hot_templates = translate.translate_to_yaml_files_dict(basename)
253 basename = os.path.basename(hot_files[0])
254 output_hot_templates =\
255 translate.translate_to_yaml_files_dict(basename, True)
258 for output_hot_template_name in output_hot_templates:
259 output_dict[output_hot_template_name] = \
260 toscaparser.utils.yamlparser.simple_parse(
261 output_hot_templates[output_hot_template_name])
263 expected_output_dict = {}
264 for expected_hot_template in expected_hot_templates:
265 expected_output_dict[os.path.basename(expected_hot_template)] = \
266 YamlUtils.get_dict(expected_hot_template)
268 return CompareUtils.diff_dicts(expected_output_dict, output_dict)
271 class UrlUtils(object):
274 def validate_url(path):
275 """Validates whether the given path is a URL or not.
277 If the given path includes a scheme (http, https, ftp, ...) and a net
278 location (a domain name such as www.github.com) it is validated as a
281 parsed = urlparse(path)
282 return bool(parsed.scheme) and bool(parsed.netloc)
285 def str_to_num(value):
286 """Convert a string representation of a number into a numeric type."""
287 if isinstance(value, numbers.Number) \
288 or isinstance(value, six.integer_types) \
289 or isinstance(value, float):
300 def check_for_env_variables():
301 return set(ENV_VARIABLES) < set(os.environ.keys())
304 def get_ks_access_dict():
305 tenant_name = os.getenv('OS_TENANT_NAME')
306 username = os.getenv('OS_USERNAME')
307 password = os.getenv('OS_PASSWORD')
308 auth_url = os.getenv('OS_AUTH_URL')
312 "tenantName": tenant_name,
313 "passwordCredentials": {
314 "username": username,
319 headers = {'Content-Type': 'application/json'}
321 keystone_response = requests.post(auth_url + '/tokens',
322 data=json.dumps(auth_dict),
324 if keystone_response.status_code != 200:
326 return json.loads(keystone_response.content)
331 def get_url_for(access_dict, service_type):
332 if access_dict is None:
334 service_catalog = access_dict['access']['serviceCatalog']
336 for service in service_catalog:
337 if service['type'] == service_type:
338 service_url = service['endpoints'][0]['publicURL']
343 def get_token_id(access_dict):
344 if access_dict is None:
346 return access_dict['access']['token']['id']
349 def decompress(zip_file, dir=None):
350 """Decompress Zip file
352 Decompress any zip file. For example, TOSCA CSAR
355 zip_file: file in zip format
356 dir: directory to decompress zip. If not provided an unique temporary
357 directory will be generated and used.
359 dir: absolute path to the decopressed directory
362 dir = tempfile.NamedTemporaryFile().name
363 with zipfile.ZipFile(zip_file, "r") as zf:
368 def get_dict_value(dict_item, key, get_files):
370 return get_files.append(dict_item[key])
371 for k, v in dict_item.items():
372 if isinstance(v, dict):
373 get_dict_value(v, key, get_files)