blob: f08fafef159ab2d7b4c30149b60e39ae203d0460 [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 Dmitriev65a80ee2017-06-30 17:30:37 +030083def render_dir(template_dir, output_dir, contexts):
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.
92 """
Dennis Dmitriev7de24762017-07-17 18:34:06 +030093 def toyaml(value, width=0, indentfirst=False):
94 string = yaml.dump(value, default_flow_style=False)
95 if string.splitlines():
96 return (
97 ' ' * width * indentfirst +
98 ('\n' + ' ' * width).join(string.splitlines()) + '\n')
99 else:
100 return ''
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300101
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300102 overwrite_if_exists = True
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300103
104 merged_context = {}
Dennis Dmitriev7de24762017-07-17 18:34:06 +0300105
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300106 for fcon in contexts:
107 if fcon.endswith('.yaml'):
108 context = helpers.yaml_read(fcon)
109 elif fcon.endswith('.json'):
110 context = helpers.json_read(fcon)
111 else:
Dennis Dmitriev86750962017-07-11 19:44:05 +0300112 sys.exit("Error: Please use YAML or JSON files for contexts")
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300113
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300114 merged_context = helpers.merge_nested_objects(merged_context, context)
115
Dennis Dmitriev7de24762017-07-17 18:34:06 +0300116 merged_context['toyaml'] = toyaml
117
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300118 try:
119 generate.generate_files(
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300120 repo_dir=template_dir,
121 context=merged_context,
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300122 overwrite_if_exists=overwrite_if_exists,
123 output_dir=output_dir
124 )
125
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300126 except UndefinedVariableInTemplate as undefined_err:
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300127 context_str = yaml.dump(
128 undefined_err.context,
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300129 default_flow_style=False
130 )
131 print('='*15 + ' Context: '+ '='*15 + '\n{}'.format(context_str) + '='*40)
Dennis Dmitriev86750962017-07-11 19:44:05 +0300132 print('>>> {}'.format(undefined_err.message))
133 sys.exit('>>> Error message: {}'.format(undefined_err.error.message))