blob: ce0d7a63352586d444080138d318d55cdfdcb2cd [file] [log] [blame]
Dennis Dmitriev30dfb892017-06-29 20:58:11 +03001import yaml
2import json
3
4from cookiecutter import __version__
5#from cookiecutter.log import configure_logger
6#from cookiecutter.main import cookiecutter
7
8from cookiecutter import generate
9from cookiecutter.exceptions import UndefinedVariableInTemplate
10
11from reclass_tools import helpers
12from reclass_tools import reclass_models
13from reclass_tools import walk_models
14
15
16def create_inventory_context(domain=None, keys=None):
17 """Dumps the current inventory per domain
18
19 Example of context:
20
21 <global_settings>: # only if required
22 ...
23 current_clusters:
24 <cluster_names>:
25 # here are cluster settings if required
26 nodes:
27 <node_names>:
28 name: ctl01
29 reclass_storage_name: openstack_control_node01
30 roles:
31 - vcp # 'vcp' or None
32 parameters: # specified keys to dump, for example
33 # parameters.linux.network.interface below:
34 linux:
35 network:
36 interfaces:
37 ..
38 """
39 inventory = reclass_models.inventory_list(domain=domain)
40 vcp_list = reclass_models.vcp_list(domain=domain, inventory=inventory)
41 reclass_storage = reclass_models.reclass_storage(domain=domain, inventory=inventory)
42
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +030043 if domain is None:
44 raise Exception("Please specify a domain name from: \n{}".format('\n'.join(reclass_storage.keys())))
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030045
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +030046 #current_underlay_context = {
47 # 'current_clusters': {
48 # }
49 #}
50
51 for storage_domain, storage_nodes in reclass_storage.items():
52 if storage_domain != domain:
53 continue
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030054
55 current_cluster_nodes = {}
56 for storage_node_name, storage_node in storage_nodes.items():
57 inventory_node_name = "{0}.{1}".format(storage_node['name'], storage_node['domain'])
58 current_cluster_nodes[inventory_node_name] = {
59 'name': storage_node['name'],
60 'reclass_storage_name': storage_node_name,
61 'roles': list(),
62 'parameters': dict(),
63 }
64
65 if (storage_node['name'], storage_node['domain']) in vcp_list:
66 # Add role 'vcp' to mark the VM nodes.
67 current_cluster_nodes[inventory_node_name]['roles'].append('vcp')
68
69 if keys:
70 # Dump specified parameters for the node
71 # Will fail with KeyError if 'inventory_node_name' doesn't
72 # exists in reclass inventory
73 # (wasn't generated with reclass.storage yet, for example)
74 node = inventory[inventory_node_name]
75 for key in keys:
76 key_path = key.split('.')
77 reclass_key = helpers.get_nested_key(node, path=key_path)
78 if reclass_key:
79 helpers.create_nested_key(current_cluster_nodes[inventory_node_name], path=key_path, value=reclass_key)
80
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +030081 #current_underlay_context['current_clusters'][domain] = {
82 # 'nodes': current_cluster_nodes
83 #}
84 current_underlay_context = {
85 'cookiecutter': {
86 'cluster_name': storage_domain,
87 'nodes': current_cluster_nodes,
88 }
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030089 }
90
91 return current_underlay_context
92
93
94 #1. Generate jinga interfaces / hw details based on node information provided to jinja
95
96 #2. Generate appropriate includes to reclass.storate model in config node
97 #configure_logger(
98 # stream_level='DEBUG' if verbose else 'INFO',
99 # debug_file=debug_file,
100 #)
101
102#current_clusters:
103# <cluster_names>:
104# nodes:
105# <node_names>:
106# name: ctl01
107# reclass_storage_name: openstack_control_node01
108# # if classes - then classes
109# roles:
110# - vcp # to select wich interface type to use
111# #- openstack_controller # Don't forget to map the roles to corresponded classes if needed
112# parameters: # there is just a DUMP of the existing model,
113# # which could be re-used complete or particulary for rendering new model
114# linux:
115# network:
116# interfaces:
117# ..
118
119
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300120def render_dir(template_dir, output_dir, contexts):
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300121 """Coockiecutter echancement to use several source JSON files
122
123 :param template_dir: directory with templates to render
124 :param output_dir: directory that should be created from templates
125 :param context_files: list of strings, paths to YAML or JSON files
126 that provide the context variables for rendering.
127 Merge of the files usind update() into a single
128 dict is in the same order as files in the list.
129 """
130
131#ipdb> repo_dir
132#u'/root/cookiecutter-templates/cluster_product/openstack'
133#ipdb> context
134#{u'cookiecutter': {u'openstack_telemetry_node02_hostname': u'mdb02', ... }}
135#ipdb> overwrite_if_exists
136#False
137#ipdb> output_dir
138#'/root/my_new_deployment/'
139
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300140 print(template_dir)
141 print(output_dir)
142 print(contexts)
143 #return
144 #repo_dir = '/root/cookiecutter-templates/cluster_product/openstack'
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300145 overwrite_if_exists = True
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300146 #output_dir = '/root/my_new_deployment/'
147 #context = {'cookiecutter': {'openstack_telemetry_node02_hostname': 'mdb02' }}
148
149 merged_context = {}
150 for fcon in contexts:
151 if fcon.endswith('.yaml'):
152 context = helpers.yaml_read(fcon)
153 elif fcon.endswith('.json'):
154 context = helpers.json_read(fcon)
155 else:
156 print("Error: Please use YAML or JSON files for contexts")
157 return # should be exit 1
158
159
160 #merged_context.update(context)
161 #merged_context = dict(chain(merged_context.items(), context.items()))
162 merged_context = helpers.merge_nested_objects(merged_context, context)
163
164 #print(yaml.dump(merged_context, default_flow_style=False))
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300165
166 try:
167 generate.generate_files(
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300168 repo_dir=template_dir,
169 context=merged_context,
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300170 overwrite_if_exists=overwrite_if_exists,
171 output_dir=output_dir
172 )
173
174
175 except UndefinedVariableInTemplate as undefined_err:
176 print('>>> {}'.format(undefined_err.message))
177 print('>>> Error message: {}'.format(undefined_err.error.message))
178
179 context_str = yaml.dump(
180 undefined_err.context,
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300181 default_flow_style=False
182 )
183 print('='*15 + ' Context: '+ '='*15 + '\n{}'.format(context_str) + '='*40)
184 return