Merge "Fix race conditions between containers"
[apex-tripleo-heat-templates.git] / tripleo_heat_templates / environment_generator.py
1 #!/usr/bin/env python
2
3 # Copyright 2015 Red Hat Inc.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License"); you may
6 # not use this file except in compliance with the License. You may obtain
7 # a copy of the License at
8 #
9 #      http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 # License for the specific language governing permissions and limitations
15 # under the License.
16
17 import errno
18 import os
19 import sys
20 import yaml
21
22
23 _PARAM_FORMAT = u"""  # %(description)s
24   %(mandatory)s# Type: %(type)s
25   %(name)s: %(default)s
26 """
27 _STATIC_MESSAGE_START = (
28     '  # ******************************************************\n'
29     '  # Static parameters - these are values that must be\n'
30     '  # included in the environment but should not be changed.\n'
31     '  # ******************************************************\n'
32     )
33 _STATIC_MESSAGE_END = ('  # *********************\n'
34                        '  # End static parameters\n'
35                        '  # *********************\n'
36                        )
37 _FILE_HEADER = (
38     '# *******************************************************************\n'
39     '# This file was created automatically by the sample environment\n'
40     '# generator. Developers should use `tox -e genconfig` to update it.\n'
41     '# Users are recommended to make changes to a copy of the file instead\n'
42     '# of the original, if any customizations are needed.\n'
43     '# *******************************************************************\n'
44     )
45 # Certain parameter names can't be changed, but shouldn't be shown because
46 # they are never intended for direct user input.
47 _PRIVATE_OVERRIDES = ['server', 'servers', 'NodeIndex']
48
49
50 def _create_output_dir(target_file):
51     try:
52         os.makedirs(os.path.dirname(target_file))
53     except OSError as e:
54         if e.errno == errno.EEXIST:
55             pass
56         else:
57             raise
58
59
60 def _generate_environment(input_env, parent_env=None):
61     if parent_env is None:
62         parent_env = {}
63     env = dict(parent_env)
64     env.update(input_env)
65     parameter_defaults = {}
66     param_names = []
67     for template_file, template_data in env['files'].items():
68         with open(template_file) as f:
69             f_data = yaml.safe_load(f)
70             f_params = f_data['parameters']
71             parameter_defaults.update(f_params)
72             if template_data['parameters'] == 'all':
73                 new_names = [k for k, v in f_params.items()]
74             else:
75                 new_names = template_data['parameters']
76             missing_params = [name for name in new_names
77                               if name not in f_params]
78             if missing_params:
79                 raise RuntimeError('Did not find specified parameter names %s '
80                                    'in file %s for environment %s' %
81                                    (missing_params, template_file,
82                                     env['name']))
83             param_names += new_names
84
85     static_names = env.get('static', [])
86     static_defaults = {k: v for k, v in parameter_defaults.items()
87                        if k in param_names and
88                        k in static_names
89                        }
90     parameter_defaults = {k: v for k, v in parameter_defaults.items()
91                           if k in param_names and
92                           k not in _PRIVATE_OVERRIDES and
93                           not k.startswith('_') and
94                           k not in static_names
95                           }
96     for k, v in env.get('sample_values', {}).items():
97         if k in parameter_defaults:
98             parameter_defaults[k]['sample'] = v
99         if k in static_defaults:
100             static_defaults[k]['sample'] = v
101
102     def write_sample_entry(f, name, value):
103         default = value.get('default')
104         mandatory = ''
105         if default is None:
106             mandatory = ('# Mandatory. This parameter must be set by the '
107                          'user.\n  ')
108             default = '<None>'
109         if value.get('sample') is not None:
110             default = value['sample']
111         if default == '':
112             default = "''"
113         try:
114             # If the default value is something like %index%, yaml won't
115             # parse the output correctly unless we wrap it in quotes.
116             # However, not all default values can be wrapped so we need to
117             # do it conditionally.
118             if default.startswith('%'):
119                 default = "'%s'" % default
120         except AttributeError:
121             pass
122
123         values = {'name': name,
124                   'type': value['type'],
125                   'description':
126                       value.get('description', '').rstrip().replace('\n',
127                                                                     '\n  # '),
128                   'default': default,
129                   'mandatory': mandatory,
130                   }
131         f.write(_PARAM_FORMAT % values + '\n')
132
133     target_file = os.path.join('environments', env['name'] + '.yaml')
134     _create_output_dir(target_file)
135     with open(target_file, 'w') as env_file:
136         env_file.write(_FILE_HEADER)
137         # TODO(bnemec): Once Heat allows the title and description to live in
138         # the environment itself, uncomment these entries and make them
139         # top-level keys in the YAML.
140         env_title = env.get('title', '')
141         env_file.write(u'# title: %s\n' % env_title)
142         env_desc = env.get('description', '')
143         env_file.write(u'# description: |\n')
144         for line in env_desc.splitlines():
145             env_file.write(u'#   %s\n' % line)
146
147         if parameter_defaults:
148             env_file.write(u'parameter_defaults:\n')
149         for name, value in sorted(parameter_defaults.items()):
150             write_sample_entry(env_file, name, value)
151         if static_defaults:
152             env_file.write(_STATIC_MESSAGE_START)
153         for name, value in sorted(static_defaults.items()):
154             write_sample_entry(env_file, name, value)
155         if static_defaults:
156             env_file.write(_STATIC_MESSAGE_END)
157
158         if env.get('resource_registry'):
159             env_file.write(u'resource_registry:\n')
160         for res, value in sorted(env.get('resource_registry', {}).items()):
161             env_file.write(u'  %s: %s\n' % (res, value))
162         print('Wrote sample environment "%s"' % target_file)
163
164     for e in env.get('children', []):
165         _generate_environment(e, env)
166
167
168 def generate_environments(config_file):
169     with open(config_file) as f:
170         config = yaml.safe_load(f)
171     for env in config['environments']:
172         _generate_environment(env)
173
174
175 def usage(exit_code=1):
176     print('Usage: %s <filename.yaml>' % sys.argv[0])
177     sys.exit(exit_code)
178
179
180 def main():
181     try:
182         config_file = sys.argv[1]
183     except IndexError:
184         usage()
185     generate_environments(config_file)
186
187
188 if __name__ == '__main__':
189     main()