Change flat network name for nosdn fdio scenario
[apex-tripleo-heat-templates.git] / tools / yaml-nic-config-2-script.py
1 #!/usr/bin/env python
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
5 #
6 #         http://www.apache.org/licenses/LICENSE-2.0
7 #
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
12 #    under the License.
13
14 import collections
15 import copy
16 import os
17 import sys
18 import traceback
19 import yaml
20 import six
21 import re
22
23
24 #convert comments into 'comments<num>: ...' YAML
25 def to_commented_yaml(filename):
26     out_str = ''
27     last_non_comment_spaces = ''
28     with open(filename, 'r') as f:
29         comment_count = 0
30         for line in f:
31             char_count = 0
32             spaces = ''
33             for char in line:
34                 char_count += 1
35                 if char == ' ':
36                     spaces+=' '
37                     next;
38                 elif char == '#':
39                     comment_count += 1
40                     comment = line[char_count:-1]
41                     out_str += "%scomment%i_%i: '%s'\n" % (last_non_comment_spaces, comment_count, len(spaces), comment)
42                     break;
43                 else:
44                     last_non_comment_spaces = spaces
45                     out_str += line
46
47                     #inline comments check
48                     m = re.match(".*:.*#(.*)", line)
49                     if m:
50                         comment_count += 1
51                         out_str += "%s  inline_comment%i: '%s'\n" % (last_non_comment_spaces, comment_count, m.group(1))
52                     break;
53
54     with open(filename, 'w') as f:
55         f.write(out_str)
56
57     return out_str
58
59 #convert back to normal #commented YAML
60 def to_normal_yaml(filename):
61
62     with open(filename, 'r') as f:
63         data = f.read()
64
65     out_str = ''
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
70         if m:
71             if next_line_break:
72                 out_str += '\n'
73                 next_line_break = False
74             for x in range(0, int(m.group(1))):
75                 out_str += " "
76             out_str += "#%s\n" % m.group(2)
77         elif i:
78             out_str += " #%s\n" % i.group(1)
79             next_line_break = False
80         else:
81             if next_line_break:
82                 out_str += '\n'
83             out_str += line
84             next_line_break = True
85
86     if next_line_break:
87         out_str += '\n'
88
89     with open(filename, 'w') as f:
90         f.write(out_str)
91
92     return out_str
93
94
95 class description(six.text_type):
96     pass
97
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())
103
104     def description_presenter(self, data):
105         if '\n' in data:
106             style = '>'
107         else:
108             style = ''
109         return self.represent_scalar(
110             yaml.resolver.BaseResolver.DEFAULT_SCALAR_TAG, data, style=style)
111
112
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))
118
119
120 TemplateDumper.add_representer(description,
121                                TemplateDumper.description_presenter)
122
123 TemplateDumper.add_representer(collections.OrderedDict,
124                                TemplateDumper.represent_ordered_dict)
125
126
127 TemplateLoader.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
128                                TemplateLoader.construct_mapping)
129
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)
133
134 def exit_usage():
135     print('Usage %s <yaml file>' % sys.argv[0])
136     sys.exit(1)
137
138 def convert(filename):
139     print('Converting %s' % filename)
140     try:
141         tpl = yaml.load(open(filename).read(), Loader=TemplateLoader)
142     except Exception:
143         print(traceback.format_exc())
144         return 0
145
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']
150     script_path = None
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))
155             script_path = p
156     if script_path is None:
157         print("Error couldn't find run-os-net-config.sh relative to filename")
158         exit_usage()
159
160     for r in (tpl.get('resources', {})).items():
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
176         else:
177             print("No match %s" % r[0])
178             return 0
179
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']
189     #print('Result:')
190     #print('%s' % yaml.dump(od_result, Dumper=TemplateDumper, width=120, default_flow_style=False))
191     #print('---')
192     #replace = raw_input(
193         #"Replace file %s?  Answer y/n" % filename).lower() == 'y'
194     #if replace:
195     #print("Replace %s" % filename)
196     write_template(od_result, filename)
197     #else:
198     #    print("NOT replacing %s" % filename)
199     #    return 0
200     return 1
201
202 if len(sys.argv) < 2:
203     exit_usage()
204
205 path_args = sys.argv[1:]
206 exit_val = 0
207 num_converted = 0
208
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)
214     else:
215         print('Unexpected argument %s' % base_path)
216         exit_usage()
217 if num_converted == 0:
218   exit_val = 1
219 sys.exit(exit_val)