blob: a8e048bb0d01e3ef0915133f104bb36552b1257f [file] [log] [blame]
Dennis Dmitriev30dfb892017-06-29 20:58:11 +03001import yaml
2import json
Dennis Dmitriev86750962017-07-11 19:44:05 +03003import sys
Dennis Dmitriev30dfb892017-06-29 20:58:11 +03004
5from cookiecutter import generate
6from cookiecutter.exceptions import UndefinedVariableInTemplate
7
8from reclass_tools import helpers
9from reclass_tools import reclass_models
10from reclass_tools import walk_models
11
12
13def create_inventory_context(domain=None, keys=None):
14 """Dumps the current inventory per domain
15
16 Example of context:
17
18 <global_settings>: # only if required
19 ...
20 current_clusters:
21 <cluster_names>:
22 # here are cluster settings if required
23 nodes:
24 <node_names>:
25 name: ctl01
26 reclass_storage_name: openstack_control_node01
27 roles:
28 - vcp # 'vcp' or None
29 parameters: # specified keys to dump, for example
30 # parameters.linux.network.interface below:
31 linux:
32 network:
33 interfaces:
34 ..
35 """
36 inventory = reclass_models.inventory_list(domain=domain)
37 vcp_list = reclass_models.vcp_list(domain=domain, inventory=inventory)
38 reclass_storage = reclass_models.reclass_storage(domain=domain, inventory=inventory)
39
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +030040 if domain is None:
Dennis Dmitriev86750962017-07-11 19:44:05 +030041 sys.exit("Error: please specify a domain name from: \n{}".format('\n'.join(reclass_storage.keys())))
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +030042
43 for storage_domain, storage_nodes in reclass_storage.items():
44 if storage_domain != domain:
45 continue
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030046
47 current_cluster_nodes = {}
48 for storage_node_name, storage_node in storage_nodes.items():
49 inventory_node_name = "{0}.{1}".format(storage_node['name'], storage_node['domain'])
50 current_cluster_nodes[inventory_node_name] = {
51 'name': storage_node['name'],
52 'reclass_storage_name': storage_node_name,
53 'roles': list(),
54 'parameters': dict(),
55 }
56
57 if (storage_node['name'], storage_node['domain']) in vcp_list:
58 # Add role 'vcp' to mark the VM nodes.
59 current_cluster_nodes[inventory_node_name]['roles'].append('vcp')
60
61 if keys:
62 # Dump specified parameters for the node
63 # Will fail with KeyError if 'inventory_node_name' doesn't
64 # exists in reclass inventory
65 # (wasn't generated with reclass.storage yet, for example)
66 node = inventory[inventory_node_name]
67 for key in keys:
68 key_path = key.split('.')
69 reclass_key = helpers.get_nested_key(node, path=key_path)
70 if reclass_key:
71 helpers.create_nested_key(current_cluster_nodes[inventory_node_name], path=key_path, value=reclass_key)
72
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +030073 current_underlay_context = {
74 'cookiecutter': {
75 'cluster_name': storage_domain,
76 'nodes': current_cluster_nodes,
77 }
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030078 }
79
80 return current_underlay_context
81
82
Dennis Dmitriev55989022017-07-17 19:23:16 +030083def render_dir(template_dir, output_dir, contexts, env_name=None):
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030084 """Coockiecutter echancement to use several source JSON files
85
86 :param template_dir: directory with templates to render
87 :param output_dir: directory that should be created from templates
88 :param context_files: list of strings, paths to YAML or JSON files
89 that provide the context variables for rendering.
90 Merge of the files usind update() into a single
91 dict is in the same order as files in the list.
Dennis Dmitriev0cea5702017-07-17 19:03:23 +030092 :param env_name: name for new environment that will be created
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030093 """
Dennis Dmitriev7de24762017-07-17 18:34:06 +030094 def toyaml(value, width=0, indentfirst=False):
95 string = yaml.dump(value, default_flow_style=False)
96 if string.splitlines():
97 return (
98 ' ' * width * indentfirst +
99 ('\n' + ' ' * width).join(string.splitlines()) + '\n')
100 else:
101 return ''
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300102
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300103 overwrite_if_exists = True
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300104
105 merged_context = {}
Dennis Dmitriev7de24762017-07-17 18:34:06 +0300106
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300107 for fcon in contexts:
108 if fcon.endswith('.yaml'):
109 context = helpers.yaml_read(fcon)
110 elif fcon.endswith('.json'):
111 context = helpers.json_read(fcon)
112 else:
Dennis Dmitriev86750962017-07-11 19:44:05 +0300113 sys.exit("Error: Please use YAML or JSON files for contexts")
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300114
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300115 merged_context = helpers.merge_nested_objects(merged_context, context)
116
Dennis Dmitriev7de24762017-07-17 18:34:06 +0300117 merged_context['toyaml'] = toyaml
Dennis Dmitriev55989022017-07-17 19:23:16 +0300118 if env_name:
119 merged_context['cookiecutter']['_environment_name'] = env_name
Dennis Dmitriev7de24762017-07-17 18:34:06 +0300120
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300121 try:
122 generate.generate_files(
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300123 repo_dir=template_dir,
124 context=merged_context,
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300125 overwrite_if_exists=overwrite_if_exists,
126 output_dir=output_dir
127 )
128
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300129 except UndefinedVariableInTemplate as undefined_err:
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300130 context_str = yaml.dump(
131 undefined_err.context,
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300132 default_flow_style=False
133 )
134 print('='*15 + ' Context: '+ '='*15 + '\n{}'.format(context_str) + '='*40)
Dennis Dmitriev86750962017-07-11 19:44:05 +0300135 print('>>> {}'.format(undefined_err.message))
136 sys.exit('>>> Error message: {}'.format(undefined_err.error.message))