blob: 8bb5840cda28f80a2aa29c15c4f6d77d20b305e3 [file] [log] [blame]
Dennis Dmitriev566db4b2017-07-18 18:13:07 +03001# Copyright 2013 - 2017 Mirantis, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030014
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030015import sys
16import yaml
17
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030018from cookiecutter.exceptions import UndefinedVariableInTemplate
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030019from cookiecutter import generate
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030020
21from reclass_tools import helpers
22from reclass_tools import reclass_models
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030023
24
25def create_inventory_context(domain=None, keys=None):
26 """Dumps the current inventory per domain
27
28 Example of context:
29
30 <global_settings>: # only if required
31 ...
32 current_clusters:
33 <cluster_names>:
34 # here are cluster settings if required
35 nodes:
36 <node_names>:
37 name: ctl01
38 reclass_storage_name: openstack_control_node01
39 roles:
40 - vcp # 'vcp' or None
41 parameters: # specified keys to dump, for example
42 # parameters.linux.network.interface below:
43 linux:
44 network:
45 interfaces:
46 ..
47 """
48 inventory = reclass_models.inventory_list(domain=domain)
49 vcp_list = reclass_models.vcp_list(domain=domain, inventory=inventory)
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030050 reclass_storage = reclass_models.reclass_storage(domain=domain,
51 inventory=inventory)
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030052
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +030053 if domain is None:
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030054 sys.exit("Error: please specify a domain name from: \n{}"
55 .format('\n'.join(reclass_storage.keys())))
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +030056
57 for storage_domain, storage_nodes in reclass_storage.items():
58 if storage_domain != domain:
59 continue
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030060
61 current_cluster_nodes = {}
62 for storage_node_name, storage_node in storage_nodes.items():
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030063 inventory_node_name = "{0}.{1}".format(storage_node['name'],
64 storage_node['domain'])
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030065 current_cluster_nodes[inventory_node_name] = {
66 'name': storage_node['name'],
67 'reclass_storage_name': storage_node_name,
68 'roles': list(),
69 'parameters': dict(),
70 }
71
72 if (storage_node['name'], storage_node['domain']) in vcp_list:
73 # Add role 'vcp' to mark the VM nodes.
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030074 current_cluster_nodes[
75 inventory_node_name]['roles'].append('vcp')
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030076
77 if keys:
78 # Dump specified parameters for the node
79 # Will fail with KeyError if 'inventory_node_name' doesn't
80 # exists in reclass inventory
81 # (wasn't generated with reclass.storage yet, for example)
82 node = inventory[inventory_node_name]
83 for key in keys:
84 key_path = key.split('.')
85 reclass_key = helpers.get_nested_key(node, path=key_path)
86 if reclass_key:
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030087 helpers.create_nested_key(
88 current_cluster_nodes[inventory_node_name],
89 path=key_path,
90 value=reclass_key)
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030091
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +030092 current_underlay_context = {
93 'cookiecutter': {
94 'cluster_name': storage_domain,
95 'nodes': current_cluster_nodes,
96 }
Dennis Dmitriev30dfb892017-06-29 20:58:11 +030097 }
98
99 return current_underlay_context
100
101
Dennis Dmitriev55989022017-07-17 19:23:16 +0300102def render_dir(template_dir, output_dir, contexts, env_name=None):
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300103 """Coockiecutter echancement to use several source JSON files
104
105 :param template_dir: directory with templates to render
106 :param output_dir: directory that should be created from templates
107 :param context_files: list of strings, paths to YAML or JSON files
108 that provide the context variables for rendering.
109 Merge of the files usind update() into a single
110 dict is in the same order as files in the list.
Dennis Dmitriev0cea5702017-07-17 19:03:23 +0300111 :param env_name: name for new environment that will be created
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300112 """
Dennis Dmitriev7de24762017-07-17 18:34:06 +0300113 def toyaml(value, width=0, indentfirst=False):
114 string = yaml.dump(value, default_flow_style=False)
115 if string.splitlines():
116 return (
117 ' ' * width * indentfirst +
118 ('\n' + ' ' * width).join(string.splitlines()) + '\n')
119 else:
120 return ''
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300121
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300122 overwrite_if_exists = True
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300123
124 merged_context = {}
Dennis Dmitriev7de24762017-07-17 18:34:06 +0300125
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300126 for fcon in contexts:
127 if fcon.endswith('.yaml'):
128 context = helpers.yaml_read(fcon)
129 elif fcon.endswith('.json'):
130 context = helpers.json_read(fcon)
131 else:
Dennis Dmitriev86750962017-07-11 19:44:05 +0300132 sys.exit("Error: Please use YAML or JSON files for contexts")
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300133
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300134 merged_context = helpers.merge_nested_objects(merged_context, context)
135
Dennis Dmitriev7de24762017-07-17 18:34:06 +0300136 merged_context['toyaml'] = toyaml
Dennis Dmitriev55989022017-07-17 19:23:16 +0300137 if env_name:
138 merged_context['cookiecutter']['_environment_name'] = env_name
Dennis Dmitriev7de24762017-07-17 18:34:06 +0300139
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300140 try:
141 generate.generate_files(
Dennis Dmitriev65a80ee2017-06-30 17:30:37 +0300142 repo_dir=template_dir,
143 context=merged_context,
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300144 overwrite_if_exists=overwrite_if_exists,
145 output_dir=output_dir
146 )
147
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300148 except UndefinedVariableInTemplate as undefined_err:
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300149 context_str = yaml.dump(
150 undefined_err.context,
Dennis Dmitriev30dfb892017-06-29 20:58:11 +0300151 default_flow_style=False
152 )
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300153 print('=' * 15 + ' Context: ' + '=' * 15 +
154 '\n{}'.format(context_str) + '='*40)
Dennis Dmitriev86750962017-07-11 19:44:05 +0300155 print('>>> {}'.format(undefined_err.message))
156 sys.exit('>>> Error message: {}'.format(undefined_err.error.message))