import argparse
+def apply_maps(template):
+ """Apply Merge::Map within template.
+
+ Any dict {'Merge::Map': {'Foo': 'Bar', 'Baz': 'Quux'}}
+ will resolve to ['Bar', 'Quux'] - that is a dict with key
+ 'Merge::Map' is replaced entirely by that dict['Merge::Map'].values().
+ """
+ if isinstance(template, dict):
+ if 'Merge::Map' in template:
+ return sorted(
+ apply_maps(value) for value in template['Merge::Map'].values()
+ )
+ else:
+ return dict((key, apply_maps(value))
+ for key, value in template.items())
+ elif isinstance(template, list):
+ return [apply_maps(item) for item in template]
+ else:
+ return template
+
+
def apply_scaling(template, scaling, in_copies=None):
"""Apply a set of scaling operations to template.
Values are handled via scale_value.
- Keys in dicts are always copied per the scaling rule.
+ Keys in dicts are copied per the scaling rule.
Values are either replaced or copied depending on whether the given
scaling rule is in in_copies.
+
+ in_copies is reset to None when a dict {'Merge::Map': someobject} is
+ encountered.
"""
in_copies = dict(in_copies or {})
# Shouldn't be needed but to avoid unexpected side effects/bugs we short
if not scaling:
return template
if isinstance(template, dict):
+ if 'Merge::Map' in template:
+ in_copies = None
new_template = {}
for key, value in template.items():
for prefix, copy_num, new_key in scale_value(
"be copied to Prefix1Foo in the output, and value Prefix0Bar to be"
"renamed to Prefix1Bar inside that copy, or copied to Prefix1Bar "
"outside of any copy.")
+ parser.add_argument(
+ '--change-image-params', action='store_true', default=False,
+ help="Change parameters in templates to match resource names. This was "
+ " the default at one time but it causes issues when parameter "
+ " names need to remain stable.")
args = parser.parse_args(argv)
templates = args.templates
scaling = parse_scaling(args.scale)
merged_template = merge(templates, args.master_role, args.slave_roles,
- args.included_template_dir, scaling=scaling)
+ args.included_template_dir, scaling=scaling,
+ change_image_params=args.change_image_params)
if args.output == '-':
out_file = sys.stdout
else:
def merge(templates, master_role=None, slave_roles=None,
included_template_dir=INCLUDED_TEMPLATE_DIR,
- scaling=None):
+ scaling=None, change_image_params=None):
scaling = scaling or {}
errors = []
end_template={'HeatTemplateFormatVersion': '2012-12-12',
new_resources = template.get('Resources', {})
for r, rbody in sorted(new_resources.items()):
if rbody['Type'] in MERGABLE_TYPES:
- if 'image' in MERGABLE_TYPES[rbody['Type']]:
- image_key = MERGABLE_TYPES[rbody['Type']]['image']
- # XXX Assuming ImageId is always a Ref
- ikey_val = end_template['Parameters'][rbody['Properties'][image_key]['Ref']]
- del end_template['Parameters'][rbody['Properties'][image_key]['Ref']]
+ if change_image_params:
+ if 'image' in MERGABLE_TYPES[rbody['Type']]:
+ image_key = MERGABLE_TYPES[rbody['Type']]['image']
+ # XXX Assuming ImageId is always a Ref
+ ikey_val = end_template['Parameters'][rbody['Properties'][image_key]['Ref']]
+ del end_template['Parameters'][rbody['Properties'][image_key]['Ref']]
role = rbody.get('Metadata', {}).get('OpenStack::Role', r)
role = translate_role(role, master_role, slave_roles)
if role != r:
if 'Resources' not in end_template:
end_template['Resources'] = {}
end_template['Resources'][role] = rbody
- if 'image' in MERGABLE_TYPES[rbody['Type']]:
- ikey = '%sImage' % (role)
- end_template['Resources'][role]['Properties'][image_key] = {'Ref': ikey}
- end_template['Parameters'][ikey] = ikey_val
+ if change_image_params:
+ if 'image' in MERGABLE_TYPES[rbody['Type']]:
+ ikey = '%sImage' % (role)
+ end_template['Resources'][role]['Properties'][image_key] = {'Ref': ikey}
+ end_template['Parameters'][ikey] = ikey_val
elif rbody['Type'] == 'FileInclude':
# we trust os.path.join to DTRT: if FileInclude path isn't
# absolute, join to included_template_dir (./)
end_template['Resources'][r] = rbody
end_template = apply_scaling(end_template, scaling)
+ end_template = apply_maps(end_template)
def fix_ref(item, old, new):
if isinstance(item, dict):