2 # Licensed under the Apache License, Version 2.0 (the "License"); you may
3 # not use this file except in compliance with the License. You may obtain
4 # a copy of the License at
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11 # License for the specific language governing permissions and limitations
24 #convert comments into 'comments<num>: ...' YAML
25 def to_commented_yaml(filename):
27 last_non_comment_spaces = ''
28 with open(filename, 'r') as f:
40 comment = line[char_count:-1]
41 out_str += "%scomment%i_%i: '%s'\n" % (last_non_comment_spaces, comment_count, len(spaces), comment)
44 last_non_comment_spaces = spaces
47 #inline comments check
48 m = re.match(".*:.*#(.*)", line)
51 out_str += "%s inline_comment%i: '%s'\n" % (last_non_comment_spaces, comment_count, m.group(1))
54 with open(filename, 'w') as f:
59 #convert back to normal #commented YAML
60 def to_normal_yaml(filename):
62 with open(filename, 'r') as f:
66 next_line_break = False
67 for line in data.split('\n'):
68 m = re.match(" +comment[0-9]+_([0-9]+): '(.*)'.*", line) #normal comments
69 i = re.match(" +inline_comment[0-9]+: '(.*)'.*", line) #inline comments
73 next_line_break = False
74 for x in range(0, int(m.group(1))):
76 out_str += "#%s\n" % m.group(2)
78 out_str += " #%s\n" % i.group(1)
79 next_line_break = False
84 next_line_break = True
89 with open(filename, 'w') as f:
95 class description(six.text_type):
98 # FIXME: Some of this duplicates code from build_endpoint_map.py, we should
99 # refactor to share the common code
100 class TemplateDumper(yaml.SafeDumper):
101 def represent_ordered_dict(self, data):
102 return self.represent_dict(data.items())
104 def description_presenter(self, data):
109 return self.represent_scalar(
110 yaml.resolver.BaseResolver.DEFAULT_SCALAR_TAG, data, style=style)
113 # We load mappings into OrderedDict to preserve their order
114 class TemplateLoader(yaml.SafeLoader):
115 def construct_mapping(self, node):
116 self.flatten_mapping(node)
117 return collections.OrderedDict(self.construct_pairs(node))
120 TemplateDumper.add_representer(description,
121 TemplateDumper.description_presenter)
123 TemplateDumper.add_representer(collections.OrderedDict,
124 TemplateDumper.represent_ordered_dict)
127 TemplateLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
128 TemplateLoader.construct_mapping)
130 def write_template(template, filename=None):
131 with open(filename, 'w') as f:
132 yaml.dump(template, f, TemplateDumper, width=120, default_flow_style=False)
135 print('Usage %s <yaml file>' % sys.argv[0])
138 def convert(filename):
139 print('Converting %s' % filename)
141 tpl = yaml.load(open(filename).read(), Loader=TemplateLoader)
143 print(traceback.format_exc())
146 # Check which path we need for run-os-net-config.sh because we have
147 # nic config templates in the top-level and network/config
148 script_paths = ['network/scripts/run-os-net-config.sh',
149 '../../scripts/run-os-net-config.sh']
151 for p in script_paths:
152 check_path = os.path.join(os.path.dirname(filename), p)
153 if os.path.isfile(check_path):
154 print("Found %s, using %s" % (check_path, p))
156 if script_path is None:
157 print("Error couldn't find run-os-net-config.sh relative to filename")
160 for r in six.iteritems(tpl.get('resources', {})):
161 if (r[1].get('type') == 'OS::Heat::StructuredConfig' and
162 r[1].get('properties', {}).get('group') == 'os-apply-config' and
163 r[1].get('properties', {}).get('config', {}).get('os_net_config')):
164 #print("match %s" % r[0])
165 new_r = collections.OrderedDict()
166 new_r['type'] = 'OS::Heat::SoftwareConfig'
167 new_r['properties'] = collections.OrderedDict()
168 new_r['properties']['group'] = 'script'
169 old_net_config = r[1].get(
170 'properties', {}).get('config', {}).get('os_net_config')
171 new_config = {'str_replace': collections.OrderedDict()}
172 new_config['str_replace']['template'] = {'get_file': script_path}
173 new_config['str_replace']['params'] = {'$network_config': old_net_config}
174 new_r['properties']['config'] = new_config
175 tpl['resources'][r[0]] = new_r
177 print("No match %s" % r[0])
180 # Preserve typical HOT template key ordering
181 od_result = collections.OrderedDict()
182 # Need to bump the HOT version so str_replace supports serializing to json
183 od_result['heat_template_version'] = "2016-10-14"
184 if tpl.get('description'):
185 od_result['description'] = description(tpl['description'])
186 od_result['parameters'] = tpl['parameters']
187 od_result['resources'] = tpl['resources']
188 od_result['outputs'] = tpl['outputs']
190 #print('%s' % yaml.dump(od_result, Dumper=TemplateDumper, width=120, default_flow_style=False))
192 #replace = raw_input(
193 #"Replace file %s? Answer y/n" % filename).lower() == 'y'
195 #print("Replace %s" % filename)
196 write_template(od_result, filename)
198 # print("NOT replacing %s" % filename)
202 if len(sys.argv) < 2:
205 path_args = sys.argv[1:]
209 for base_path in path_args:
210 if os.path.isfile(base_path) and base_path.endswith('.yaml'):
211 to_commented_yaml(base_path)
212 num_converted += convert(base_path)
213 to_normal_yaml(base_path)
215 print('Unexpected argument %s' % base_path)
217 if num_converted == 0: