Change flat network name for nosdn fdio scenario
[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', 'DefaultPasswords']
48 # Hidden params are not included by default when the 'all' option is used,
49 # but can be explicitly included by referencing them in sample_defaults or
50 # static.  This allows us to generate sample environments using them when
51 # necessary, but they won't be improperly included by accident.
52 _HIDDEN_PARAMS = ['EndpointMap', 'RoleName', 'RoleParameters',
53                   'ServiceNetMap', 'ServiceData',
54                   ]
55
56
57 def _create_output_dir(target_file):
58     try:
59         os.makedirs(os.path.dirname(target_file))
60     except OSError as e:
61         if e.errno == errno.EEXIST:
62             pass
63         else:
64             raise
65
66
67 def _generate_environment(input_env, parent_env=None):
68     if parent_env is None:
69         parent_env = {}
70     env = dict(parent_env)
71     env.pop('children', None)
72     env.update(input_env)
73     parameter_defaults = {}
74     param_names = []
75     sample_values = env.get('sample_values', {})
76     static_names = env.get('static', [])
77     for template_file, template_data in env['files'].items():
78         with open(template_file) as f:
79             f_data = yaml.safe_load(f)
80             f_params = f_data['parameters']
81             parameter_defaults.update(f_params)
82             if template_data['parameters'] == 'all':
83                 new_names = [k for k, v in f_params.items()]
84                 for hidden in _HIDDEN_PARAMS:
85                     if (hidden not in (static_names + sample_values.keys()) and
86                             hidden in new_names):
87                         new_names.remove(hidden)
88             else:
89                 new_names = template_data['parameters']
90             missing_params = [name for name in new_names
91                               if name not in f_params]
92             if missing_params:
93                 raise RuntimeError('Did not find specified parameter names %s '
94                                    'in file %s for environment %s' %
95                                    (missing_params, template_file,
96                                     env['name']))
97             param_names += new_names
98
99     static_defaults = {k: v for k, v in parameter_defaults.items()
100                        if k in param_names and
101                        k in static_names
102                        }
103     parameter_defaults = {k: v for k, v in parameter_defaults.items()
104                           if k in param_names and
105                           k not in _PRIVATE_OVERRIDES and
106                           not k.startswith('_') and
107                           k not in static_names
108                           }
109
110     for k, v in sample_values.items():
111         if k in parameter_defaults:
112             parameter_defaults[k]['sample'] = v
113         if k in static_defaults:
114             static_defaults[k]['sample'] = v
115
116     def write_sample_entry(f, name, value):
117         default = value.get('default')
118         mandatory = ''
119         if default is None:
120             mandatory = ('# Mandatory. This parameter must be set by the '
121                          'user.\n  ')
122             default = '<None>'
123         if value.get('sample') is not None:
124             default = value['sample']
125         # We ultimately cast this to str for output anyway
126         default = str(default)
127         if default == '':
128             default = "''"
129         # If the default value is something like %index%, yaml won't
130         # parse the output correctly unless we wrap it in quotes.
131         # However, not all default values can be wrapped so we need to
132         # do it conditionally.
133         if default.startswith('%'):
134             default = "'%s'" % default
135         if not default.startswith('\n'):
136             default = ' ' + default
137
138         values = {'name': name,
139                   'type': value['type'],
140                   'description':
141                       value.get('description', '').rstrip().replace('\n',
142                                                                     '\n  # '),
143                   'default': default,
144                   'mandatory': mandatory,
145                   }
146         f.write(_PARAM_FORMAT % values + '\n')
147
148     target_file = os.path.join('environments', env['name'] + '.yaml')
149     _create_output_dir(target_file)
150     with open(target_file, 'w') as env_file:
151         env_file.write(_FILE_HEADER)
152         # TODO(bnemec): Once Heat allows the title and description to live in
153         # the environment itself, uncomment these entries and make them
154         # top-level keys in the YAML.
155         env_title = env.get('title', '')
156         env_file.write(u'# title: %s\n' % env_title)
157         env_desc = env.get('description', '')
158         env_file.write(u'# description: |\n')
159         for line in env_desc.splitlines():
160             env_file.write(u'#   %s\n' % line)
161
162         if parameter_defaults or static_defaults:
163             env_file.write(u'parameter_defaults:\n')
164         for name, value in sorted(parameter_defaults.items()):
165             write_sample_entry(env_file, name, value)
166         if static_defaults:
167             env_file.write(_STATIC_MESSAGE_START)
168         for name, value in sorted(static_defaults.items()):
169             write_sample_entry(env_file, name, value)
170         if static_defaults:
171             env_file.write(_STATIC_MESSAGE_END)
172
173         if env.get('resource_registry'):
174             env_file.write(u'resource_registry:\n')
175         for res, value in sorted(env.get('resource_registry', {}).items()):
176             env_file.write(u'  %s: %s\n' % (res, value))
177         print('Wrote sample environment "%s"' % target_file)
178
179     for e in env.get('children', []):
180         _generate_environment(e, env)
181
182
183 def generate_environments(config_path):
184     if os.path.isdir(config_path):
185         config_files = os.listdir(config_path)
186         config_files = [os.path.join(config_path, i) for i in config_files
187                         if os.path.splitext(i)[1] == '.yaml']
188     else:
189         config_files = [config_path]
190     for config_file in config_files:
191         print('Reading environment definitions from %s' % config_file)
192         with open(config_file) as f:
193             config = yaml.safe_load(f)
194         for env in config['environments']:
195             _generate_environment(env)
196
197
198 def usage(exit_code=1):
199     print('Usage: %s [<filename.yaml> | <directory>]' % sys.argv[0])
200     sys.exit(exit_code)
201
202
203 def main():
204     try:
205         config_path = sys.argv[1]
206     except IndexError:
207         usage()
208     generate_environments(config_path)
209
210
211 if __name__ == '__main__':
212     main()