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
22 __tht_root_dir = os.path.dirname(os.path.dirname(__file__))
26 parser = argparse.ArgumentParser(
27 description='Configure host network interfaces using a JSON'
28 ' config file format.')
29 parser.add_argument('-p', '--base_path', metavar='BASE_PATH',
30 help="""base path of templates to process.""",
32 parser.add_argument('-r', '--roles-data', metavar='ROLES_DATA',
33 help="""relative path to the roles_data.yaml file.""",
34 default='roles_data.yaml')
35 parser.add_argument('-n', '--network-data', metavar='NETWORK_DATA',
36 help="""relative path to the network_data.yaml file.""",
37 default='network_data.yaml')
38 parser.add_argument('--safe',
40 help="""Enable safe mode (do not overwrite files).""",
42 parser.add_argument('-o', '--output-dir', metavar='OUTPUT_DIR',
43 help="""Output dir for all the templates""",
45 opts = parser.parse_args(argv[1:])
50 def _j2_render_to_file(j2_template, j2_data, outfile_name=None,
52 yaml_f = outfile_name or j2_template.replace('.j2.yaml', '.yaml')
53 print('rendering j2 template to file: %s' % outfile_name)
55 if not overwrite and os.path.exists(outfile_name):
56 print('ERROR: path already exists for file: %s' % outfile_name)
59 # Search for templates relative to the current template path first
60 template_base = os.path.dirname(yaml_f)
61 j2_loader = jinja2.loaders.FileSystemLoader([template_base, __tht_root_dir])
64 # Render the j2 template
65 template = jinja2.Environment(loader=j2_loader).from_string(
67 r_template = template.render(**j2_data)
68 except jinja2.exceptions.TemplateError as ex:
69 error_msg = ("Error rendering template %s : %s"
70 % (yaml_f, six.text_type(ex)))
72 raise Exception(error_msg)
73 with open(outfile_name, 'w') as out_f:
74 out_f.write(r_template)
77 def process_templates(template_path, role_data_path, output_dir,
78 network_data_path, overwrite):
80 with open(role_data_path) as role_data_file:
81 role_data = yaml.safe_load(role_data_file)
83 with open(network_data_path) as network_data_file:
84 network_data = yaml.safe_load(network_data_file)
86 j2_excludes_path = os.path.join(template_path, 'j2_excludes.yaml')
87 with open(j2_excludes_path) as role_data_file:
88 j2_excludes = yaml.safe_load(role_data_file)
90 if output_dir and not os.path.isdir(output_dir):
91 if os.path.exists(output_dir):
92 raise RuntimeError('Output dir %s is not a directory' % output_dir)
95 role_names = [r.get('name') for r in role_data]
98 r_map[r.get('name')] = r
101 for n in network_data:
102 if (n.get('enabled') is not False):
103 n_map[n.get('name')] = n
104 if not n.get('name_lower'):
105 n_map[n.get('name')]['name_lower'] = n.get('name').lower()
107 print("skipping %s network: network is disabled" % n.get('name'))
109 excl_templates = ['%s/%s' % (template_path, e)
110 for e in j2_excludes.get('name')]
112 if os.path.isdir(template_path):
113 for subdir, dirs, files in os.walk(template_path):
115 # NOTE(flaper87): Ignore hidden dirs as we don't
116 # generate templates for those.
117 # Note the slice assigment for `dirs` is necessary
118 # because we need to modify the *elements* in the
119 # dirs list rather than the reference to the list.
120 # This way we'll make sure os.walk will iterate over
121 # the shrunk list. os.walk doesn't have an API for
122 # filtering dirs at this point.
123 dirs[:] = [d for d in dirs if not d[0] == '.']
124 files = [f for f in files if not f[0] == '.']
126 # NOTE(flaper87): We could have used shutil.copytree
127 # but it requires the dst dir to not be present. This
128 # approach is safer as it doesn't require us to delete
129 # the output_dir in advance and it allows for running
130 # the command multiple times with the same output_dir.
133 out_dir = os.path.join(output_dir, subdir)
134 if not os.path.exists(out_dir):
138 file_path = os.path.join(subdir, f)
139 # We do three templating passes here:
140 # 1. *.role.j2.yaml - we template just the role name
141 # and create multiple files (one per role)
142 # 2 *.network.j2.yaml - we template the network name and
143 # data and create multiple files for networks and
144 # network ports (one per network)
145 # 3. *.j2.yaml - we template with all roles_data,
146 # and create one file common to all roles
147 if f.endswith('.role.j2.yaml'):
148 print("jinja2 rendering role template %s" % f)
149 with open(file_path) as j2_template:
150 template_data = j2_template.read()
151 print("jinja2 rendering roles %s" % ","
153 for role in role_names:
154 j2_data = {'role': r_map[role]}
157 os.path.basename(f).replace('.role.j2.yaml',
159 out_f_path = os.path.join(out_dir, out_f)
160 if not (out_f_path in excl_templates):
161 if '{{role.name}}' in template_data:
162 j2_data = {'role': r_map[role],
163 'networks': network_data}
164 _j2_render_to_file(template_data, j2_data,
165 out_f_path, overwrite)
167 # Backwards compatibility with templates
168 # that specify {{role}} vs {{role.name}}
169 j2_data = {'role': role,
170 'networks': network_data}
171 # (dprince) For the undercloud installer we
172 # don'twant to have heat check nova/glance
174 if r_map[role].get('disable_constraints',
176 j2_data['disable_constraints'] = True
178 template_data,j2_data,
179 out_f_path, overwrite)
182 print('skipping rendering of %s' % out_f_path)
184 elif f.endswith('.network.j2.yaml'):
185 print("jinja2 rendering network template %s" % f)
186 with open(file_path) as j2_template:
187 template_data = j2_template.read()
188 print("jinja2 rendering networks %s" % ",".join(n_map))
189 for network in n_map:
190 j2_data = {'network': n_map[network]}
191 # Output file names in "<name>.yaml" format
192 out_f = os.path.basename(f).replace('.network.j2.yaml',
194 if os.path.dirname(file_path).endswith('ports'):
195 out_f = out_f.replace('port',
196 n_map[network]['name_lower'])
198 out_f = out_f.replace('network',
199 n_map[network]['name_lower'])
200 out_f_path = os.path.join(out_dir, out_f)
201 if not (out_f_path in excl_templates):
202 _j2_render_to_file(template_data, j2_data,
205 print('skipping rendering of %s' % out_f_path)
207 elif f.endswith('.j2.yaml'):
208 print("jinja2 rendering normal template %s" % f)
209 with open(file_path) as j2_template:
210 template_data = j2_template.read()
211 j2_data = {'roles': role_data,
212 'networks': network_data}
213 out_f = os.path.basename(f).replace('.j2.yaml', '.yaml')
214 out_f_path = os.path.join(out_dir, out_f)
215 _j2_render_to_file(template_data, j2_data, out_f_path,
218 shutil.copy(os.path.join(subdir, f), out_dir)
221 print('Unexpected argument %s' % template_path)
223 opts = parse_opts(sys.argv)
225 role_data_path = os.path.join(opts.base_path, opts.roles_data)
226 network_data_path = os.path.join(opts.base_path, opts.network_data)
228 process_templates(opts.base_path, role_data_path, opts.output_dir,
229 network_data_path, (not opts.safe))