#!/usr/bin/python3 # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import argparse import os import sys import yaml def filter_count(pair): key, _ = pair if key.endswith('Count'): return True else: return False # We need to filter out roles with simillar name that our starts with picked up. # But we also want to filter out baremetal node provision provided params, ContainerImage,Prepare and so on. # Example to filter out: # resource_registry: # OS::TripleO::ComputeHCI::Net::SoftwareConfig: OS::Heat::None # OS::TripleO::ComputeHCI::Ports::InternalApiPort: /usr/share/openstack-tripleo-heat-templates/network/ports/deployed_internal_api.yaml # OS::TripleO::ComputeHCI::Ports::StorageMgmtPort: /usr/share/openstack-tripleo-heat-templates/network/ports/deployed_storage_mgmt.yaml # OS::TripleO::ComputeHCI::Ports::StoragePort: /usr/share/openstack-tripleo-heat-templates/network/ports/deployed_storage.yaml # OS::TripleO::ComputeHCI::Ports::TenantPort: /usr/share/openstack-tripleo-heat-templates/network/ports/deployed_tenant.yaml # This one specifically as it's OSP17.1 EL8.4 compatibility: # OS::TripleO::ComputeHCI::Services::NovaLibvirt: /usr/share/openstack-tripleo-heat-templates/deployment/deprecated/nova/nova-libvirt-container-puppet.yaml def filter_out_reg(pair): key, _ = pair if ("::Ports::" in key and key.endswith("Port")): return False elif key.endswith("Services::NovaLibvirt"): return False elif key.endswith("Net::SoftwareConfig"): return False else: return True # parameter_defaults: # ComputeHCICount: 1 # ComputeHCIHostnameFormat: computehci-%index% # ComputeHCINetworkConfigTemplate: /home/stack/overcloud-deploy/qe-Cloud-0/nic-configs/osdcompute.j2 # ComputeHCIContainerImagePrepare: def filter_out_f(pair, rolename=''): key, _ = pair if key.startswith(rolename): return False elif ("::Ports::" in key and key.endswith("Port:")): return False else: return True def filter_out_f_2(pair, removeme=''): key, _ = pair return False if key.endswith(removeme) else True def special_merge(dict_1, dict_2): dict_result = {**dict_1, **dict_2} for key, value in dict_result.items(): if key in dict_1 and key in dict_2: if (type(dict_1[key]) is list): dict_result[key] = dict_1[key] + [value] elif (type(dict_1[key]) is dict): dict_result[key] = special_merge(dict_1[key], value) else: dict_result[key] = [value, dict_1[key]] return dict_result def filter_role_parameters(env_files, rolename, new_role_env_file): n_role_params_def = {} n_role_res_registry = {} pd = {} rr = {} for file in env_files: if os.path.exists(file): with open(file, 'r') as env_file: data = yaml.safe_load(env_file) par = data.get('parameter_defaults', {}) res = data.get('resource_registry', {}) rr = special_merge(rr, res) pd = special_merge(pd, par) if pd: for key, value in pd.items(): if key.startswith(rolename): n_role_params_def[key] = value if rr: for key, value in rr.items(): if key.startswith("OS::TripleO::{}:".format(rolename)): n_role_res_registry[key] = value # We can have ComputeCount Compute1Count which # means we have picked on multiple roles and as we # cannot parse here for all role_specific tagged params # we simply pick on Count: to figure out if we don't happen # to have colision role_count = [] filter_out = [] role_count = dict(filter(filter_count, n_role_params_def.items())) if len(role_count) > 1: for key in role_count: if key != "{}Count".format(rolename): filter_out.append(key[:-5]) for role in filter_out: n_role_params_def = dict(filter( lambda seq: filter_out_f(seq, rolename=role), n_role_params_def.items())) for word in ['Count', 'HostnameFormat', 'NetworkConfigTemplate', 'ContainerImagePrepare', 'UpgradeInitCommand']: n_role_params_def = dict(filter( lambda seq: filter_out_f_2(seq, removeme=word), n_role_params_def.items())) n_role_res_registry = dict(filter( lambda seq: filter_out_reg(seq), n_role_res_registry.items())) return (n_role_res_registry, n_role_params_def) def rename_parameters(parameters, role_src, role_dst): rr, pd = parameters new_rr = {} new_pd = {} for registry in rr.keys(): new_rr[registry.replace(role_src, role_dst)] = rr[registry] for parameter in pd.keys(): new_pd[parameter.replace(role_src, role_dst)] = pd[parameter] return (new_rr, new_pd) def write_parameters(parameters, new_role_env_file): rr, pd = parameters with open(new_role_env_file, 'w') as new_file: # I don't think we can blindly dump here, if one is empty we skip dumping dump_var = {} if (pd != {}): dump_var['parameter_defaults'] = pd if (rr != {}): dump_var['resource_registry'] = rr if (dump_var != {}): yaml.dump(dump_var, new_file, default_flow_style=False) else: dump_var['parameter_defaults'] = {} yaml.dump(dump_var, new_file, default_flow_style=False) def parse_opts(argv): parser = argparse.ArgumentParser() parser.add_argument('--environment', '-e', dest='e', help='envs', action = 'append') parser.add_argument('--output-file', '-o', dest='output_file', required=True, help='Output file where the outcome is written') parser.add_argument('--rolename-src', dest='rolename_src', required=True, help='The name of the role to copy') parser.add_argument('--rolename-dst', dest='rolename_dst', required=True, help='The name of the role with paramaters copied') opts = parser.parse_known_args(argv[1:]) return opts opts = parse_opts(sys.argv) parameters = filter_role_parameters(opts[0].e, opts[0].rolename_src, opts[0].output_file) parameters = rename_parameters(parameters, opts[0].rolename_src, opts[0].rolename_dst) write_parameters(parameters, opts[0].output_file)