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
4#
5#         http://www.apache.org/licenses/LICENSE-2.0
6#
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
11#    under the License.
12
13import json
14import yaml
15
16from heatclient._i18n import _
17
18if hasattr(yaml, 'CSafeLoader'):
19    yaml_loader_base = yaml.CSafeLoader
20else:
21    yaml_loader_base = yaml.SafeLoader
22
23if hasattr(yaml, 'CSafeDumper'):
24    yaml_dumper_base = yaml.CSafeDumper
25else:
26    yaml_dumper_base = yaml.SafeDumper
27
28
29# We create custom class to not overriden the default yaml behavior
30class yaml_loader(yaml_loader_base):
31    pass
32
33
34class yaml_dumper(yaml_dumper_base):
35    pass
36
37
38def _construct_yaml_str(self, node):
39    # Override the default string handling function
40    # to always return unicode objects
41    return self.construct_scalar(node)
42
43
44yaml_loader.add_constructor(u'tag:yaml.org,2002:str', _construct_yaml_str)
45# Unquoted dates like 2013-05-23 in yaml files get loaded as objects of type
46# datetime.data which causes problems in API layer when being processed by
47# openstack.common.jsonutils. Therefore, make unicode string out of timestamps
48# until jsonutils can handle dates.
49yaml_loader.add_constructor(u'tag:yaml.org,2002:timestamp',
50                            _construct_yaml_str)
51
52
53def parse(tmpl_str):
54    """Takes a string and returns a dict containing the parsed structure.
55
56    This includes determination of whether the string is using the
57    JSON or YAML format.
58    """
59    # strip any whitespace before the check
60    tmpl_str = tmpl_str.strip()
61    if tmpl_str.startswith('{'):
62        tpl = json.loads(tmpl_str)
63    else:
64        try:
65            tpl = yaml.load(tmpl_str, Loader=yaml_loader)
66        except yaml.YAMLError:
67            # NOTE(prazumovsky): we need to return more informative error for
68            # user, so use SafeLoader, which return error message with template
69            # snippet where error has been occurred.
70            try:
71                tpl = yaml.load(tmpl_str, Loader=yaml.SafeLoader)
72            except yaml.YAMLError as yea:
73                raise ValueError(yea)
74        else:
75            if tpl is None:
76                tpl = {}
77    # Looking for supported version keys in the loaded template
78    if not ('HeatTemplateFormatVersion' in tpl
79            or 'heat_template_version' in tpl
80            or 'AWSTemplateFormatVersion' in tpl):
81        raise ValueError(_("Template format version not found."))
82    return tpl
83