| import yaml |
| import json |
| import sys |
| |
| from cookiecutter import generate |
| from cookiecutter.exceptions import UndefinedVariableInTemplate |
| |
| from reclass_tools import helpers |
| from reclass_tools import reclass_models |
| from reclass_tools import walk_models |
| |
| |
| def create_inventory_context(domain=None, keys=None): |
| """Dumps the current inventory per domain |
| |
| Example of context: |
| |
| <global_settings>: # only if required |
| ... |
| current_clusters: |
| <cluster_names>: |
| # here are cluster settings if required |
| nodes: |
| <node_names>: |
| name: ctl01 |
| reclass_storage_name: openstack_control_node01 |
| roles: |
| - vcp # 'vcp' or None |
| parameters: # specified keys to dump, for example |
| # parameters.linux.network.interface below: |
| linux: |
| network: |
| interfaces: |
| .. |
| """ |
| inventory = reclass_models.inventory_list(domain=domain) |
| vcp_list = reclass_models.vcp_list(domain=domain, inventory=inventory) |
| reclass_storage = reclass_models.reclass_storage(domain=domain, inventory=inventory) |
| |
| if domain is None: |
| sys.exit("Error: please specify a domain name from: \n{}".format('\n'.join(reclass_storage.keys()))) |
| |
| for storage_domain, storage_nodes in reclass_storage.items(): |
| if storage_domain != domain: |
| continue |
| |
| current_cluster_nodes = {} |
| for storage_node_name, storage_node in storage_nodes.items(): |
| inventory_node_name = "{0}.{1}".format(storage_node['name'], storage_node['domain']) |
| current_cluster_nodes[inventory_node_name] = { |
| 'name': storage_node['name'], |
| 'reclass_storage_name': storage_node_name, |
| 'roles': list(), |
| 'parameters': dict(), |
| } |
| |
| if (storage_node['name'], storage_node['domain']) in vcp_list: |
| # Add role 'vcp' to mark the VM nodes. |
| current_cluster_nodes[inventory_node_name]['roles'].append('vcp') |
| |
| if keys: |
| # Dump specified parameters for the node |
| # Will fail with KeyError if 'inventory_node_name' doesn't |
| # exists in reclass inventory |
| # (wasn't generated with reclass.storage yet, for example) |
| node = inventory[inventory_node_name] |
| for key in keys: |
| key_path = key.split('.') |
| reclass_key = helpers.get_nested_key(node, path=key_path) |
| if reclass_key: |
| helpers.create_nested_key(current_cluster_nodes[inventory_node_name], path=key_path, value=reclass_key) |
| |
| current_underlay_context = { |
| 'cookiecutter': { |
| 'cluster_name': storage_domain, |
| 'nodes': current_cluster_nodes, |
| } |
| } |
| |
| return current_underlay_context |
| |
| |
| def render_dir(template_dir, output_dir, contexts): |
| """Coockiecutter echancement to use several source JSON files |
| |
| :param template_dir: directory with templates to render |
| :param output_dir: directory that should be created from templates |
| :param context_files: list of strings, paths to YAML or JSON files |
| that provide the context variables for rendering. |
| Merge of the files usind update() into a single |
| dict is in the same order as files in the list. |
| """ |
| def toyaml(value, width=0, indentfirst=False): |
| string = yaml.dump(value, default_flow_style=False) |
| if string.splitlines(): |
| return ( |
| ' ' * width * indentfirst + |
| ('\n' + ' ' * width).join(string.splitlines()) + '\n') |
| else: |
| return '' |
| |
| overwrite_if_exists = True |
| |
| merged_context = {} |
| |
| for fcon in contexts: |
| if fcon.endswith('.yaml'): |
| context = helpers.yaml_read(fcon) |
| elif fcon.endswith('.json'): |
| context = helpers.json_read(fcon) |
| else: |
| sys.exit("Error: Please use YAML or JSON files for contexts") |
| |
| merged_context = helpers.merge_nested_objects(merged_context, context) |
| |
| merged_context['toyaml'] = toyaml |
| |
| try: |
| generate.generate_files( |
| repo_dir=template_dir, |
| context=merged_context, |
| overwrite_if_exists=overwrite_if_exists, |
| output_dir=output_dir |
| ) |
| |
| except UndefinedVariableInTemplate as undefined_err: |
| context_str = yaml.dump( |
| undefined_err.context, |
| default_flow_style=False |
| ) |
| print('='*15 + ' Context: '+ '='*15 + '\n{}'.format(context_str) + '='*40) |
| print('>>> {}'.format(undefined_err.message)) |
| sys.exit('>>> Error message: {}'.format(undefined_err.error.message)) |