blob: b46f5426db63dfedf87660357847b822019125f4 [file] [log] [blame]
Dennis Dmitriev86750962017-07-11 19:44:05 +03001# Copyright 2013 - 2017 Mirantis, Inc.
Dennis Dmitrieve56c8b92017-06-16 01:53:16 +03002#
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.
14
15from __future__ import print_function
16
17import argparse
Dennis Dmitrieve56c8b92017-06-16 01:53:16 +030018import sys
19import yaml
20
21from reclass_tools import walk_models
22
23
Dennis Dmitriev86750962017-07-11 19:44:05 +030024class Shell(object):
25 def __init__(self, args):
26 self.args = args
27 self.params = self.get_params()
Dennis Dmitrieve56c8b92017-06-16 01:53:16 +030028
Oleksii Grudev9d63bc12019-02-13 16:12:51 +020029 def str2bool(self, v):
30 return v.lower() in ("true")
31
Dennis Dmitriev86750962017-07-11 19:44:05 +030032 def execute(self):
33 command_name = 'do_{}'.format(self.params.command.replace('-', '_'))
34 command_method = getattr(self, command_name)
35 command_method()
Dennis Dmitrieve56c8b92017-06-16 01:53:16 +030036
Dennis Dmitriev86750962017-07-11 19:44:05 +030037 def do_get_key(self):
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030038 walk_models.remove_reclass_parameter(
Dennis Dmitriev86750962017-07-11 19:44:05 +030039 self.params.path,
40 self.params.key_name,
41 verbose=self.params.verbose,
42 pretend=True)
43
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030044 def do_add_key(self):
Sergeyb50d1f02017-12-25 18:07:15 +040045 # Try convert to digits
46 try:
47 add_val = int(self.params.add_value)
48 except ValueError:
49 try:
50 add_val = float(self.params.add_value)
51 except ValueError:
52 add_val = self.params.add_value
53
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030054 walk_models.add_reclass_parameter(
55 self.params.path,
56 self.params.key_name,
Sergeyb50d1f02017-12-25 18:07:15 +040057 add_val,
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030058 verbose=self.params.verbose,
59 merge=self.params.merge)
60
Oleksii Grudev9d63bc12019-02-13 16:12:51 +020061 def do_add_bool_key(self):
62 add_val = self.str2bool(self.params.add_value)
63
64 walk_models.add_reclass_parameter(
65 self.params.path,
66 self.params.key_name,
67 add_val,
68 verbose=self.params.verbose,
69 merge=self.params.merge)
70
Dennis Dmitriev86750962017-07-11 19:44:05 +030071 def do_del_key(self):
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030072 walk_models.remove_reclass_parameter(
Dennis Dmitriev86750962017-07-11 19:44:05 +030073 self.params.path,
74 self.params.key_name,
75 verbose=self.params.verbose,
76 pretend=False)
77
78 def do_list_params(self):
79 results = walk_models.get_all_reclass_params(
80 self.params.path,
81 verbose=self.params.verbose)
Dennis Dmitrieva7de8b52018-08-08 23:14:03 +030082 print(yaml.dump(results, width=255))
Dennis Dmitriev86750962017-07-11 19:44:05 +030083
84 def do_list_domains(self):
85 try:
86 from reclass_tools import reclass_models
87 except ImportError:
88 sys.exit("Please run this tool on the salt-master node "
89 "with installed 'reclass'")
90 inventory = reclass_models.inventory_list()
91 reclass_storage = reclass_models.reclass_storage(inventory=inventory)
92 print('\n'.join(sorted(reclass_storage.keys())))
Dennis Dmitrieve56c8b92017-06-16 01:53:16 +030093
Dennis Dmitriev86750962017-07-11 19:44:05 +030094 def do_list_nodes(self):
95 try:
96 from reclass_tools import reclass_models
97 except ImportError:
98 sys.exit("Please run this tool on the salt-master node "
99 "with installed 'reclass'")
100
101 inventory = reclass_models.inventory_list(domain=self.params.domain)
102 vcp_nodes = reclass_models.vcp_list(domain=self.params.domain,
103 inventory=inventory)
104 vcp_node_names = ['{0}.{1}'.format(name, domain)
105 for name, domain in vcp_nodes]
106
107 if self.params.vcp_only:
108 print('\n'.join(sorted(vcp_node_names)))
109 elif self.params.non_vcp_only:
110 print('\n'.join(sorted((node_name for node_name in inventory.keys()
111 if node_name not in vcp_node_names))))
112 else:
113 print('\n'.join(sorted(inventory.keys())))
114
Dennis Dmitriev806706d2017-07-29 22:31:23 +0300115 def do_trace_key(self):
116 try:
117 from reclass_tools import reclass_models
118 except ImportError:
119 sys.exit("Please run this tool on the salt-master node "
120 "with installed 'reclass'")
121 reclass_models.trace_key(key=self.params.key_name,
122 domain=self.params.domain,
123 node=self.params.node)
124
Dennis Dmitriev86750962017-07-11 19:44:05 +0300125 def do_show_context(self):
126 try:
127 from reclass_tools import create_inventory
128 except ImportError:
129 sys.exit("Please run this tool on the salt-master node "
130 "with installed 'reclass'")
131
132 current_underlay_context = create_inventory.create_inventory_context(
133 domain=self.params.domain, keys=self.params.keys)
134
Dennis Dmitrieva7de8b52018-08-08 23:14:03 +0300135 print(yaml.dump(current_underlay_context, default_flow_style=False,
136 width=255))
Dennis Dmitriev86750962017-07-11 19:44:05 +0300137
138 def do_render(self):
Dennis Dmitrievf260d152017-08-15 00:59:24 +0300139 from reclass_tools import render
Dennis Dmitriev86750962017-07-11 19:44:05 +0300140
141 if not self.params.template_dir or not self.params.output_dir \
142 or not self.params.contexts:
143 sys.exit("Missing parameters, see: reclass-tools render -h")
144
Dennis Dmitrievf260d152017-08-15 00:59:24 +0300145 render.render_dir(template_dir=self.params.template_dir,
146 output_dir=self.params.output_dir,
147 contexts=self.params.contexts,
148 env_name=self.params.env_name)
Dennis Dmitriev86750962017-07-11 19:44:05 +0300149
150 def get_params(self):
151
152 verbose_parser = argparse.ArgumentParser(add_help=False)
153 verbose_parser.add_argument('--verbose', dest='verbose',
154 action='store_const', const=True,
155 help='Show verbosed output', default=False)
156
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300157 merge_parser = argparse.ArgumentParser(add_help=False)
Dennis Dmitriev6792f7e2017-07-18 19:11:22 +0300158 merge_parser.add_argument('--merge', dest='merge',
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300159 action='store_const', const=True,
Dennis Dmitriev6792f7e2017-07-18 19:11:22 +0300160 help='Merge value to list or dict',
161 default=False)
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300162
Dennis Dmitriev86750962017-07-11 19:44:05 +0300163 key_parser = argparse.ArgumentParser(add_help=False)
164 key_parser_help = (
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300165 'Key name to find in reclass model files, for example:'
166 ' parameters.linux.network.interface')
Dennis Dmitriev86750962017-07-11 19:44:05 +0300167 key_parser.add_argument('key_name', help=key_parser_help, default=None)
168
169 keys_parser = argparse.ArgumentParser(add_help=False)
170 keys_parser.add_argument(
171 'keys',
172 help='Key names to find in reclass model files', nargs='*')
173
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300174 add_value_parser = argparse.ArgumentParser(add_help=False)
175 add_value_parser.add_argument(
176 'add_value',
177 help=('Value to add to the reclass model files, can be in the '
178 'inline YAML format'))
179
Dennis Dmitriev86750962017-07-11 19:44:05 +0300180 path_parser = argparse.ArgumentParser(add_help=False)
181 path_parser.add_argument(
182 'path',
183 help='Path to search for *.yml files.', nargs='+')
184
185 domain_parser = argparse.ArgumentParser(add_help=False)
186 domain_parser.add_argument(
187 '--domain', '-d', dest='domain',
188 help=('Show only the nodes which names are ended with the '
189 'specified domain, for example: example.local'))
190
Dennis Dmitriev806706d2017-07-29 22:31:23 +0300191 node_parser = argparse.ArgumentParser(add_help=False)
192 node_parser.add_argument(
193 '--node', '-n', dest='node',
194 help=('Show only the specified node, for example: '
195 'ctl01.example.local'))
196
Dennis Dmitriev0cea5702017-07-17 19:03:23 +0300197 env_name_parser = argparse.ArgumentParser(add_help=False)
198 env_name_parser.add_argument(
199 '--env-name', '-e', dest='env_name',
Dennis Dmitriev55989022017-07-17 19:23:16 +0300200 help=("Name of the 'environment' to create or use"),
201 default=None)
Dennis Dmitriev0cea5702017-07-17 19:03:23 +0300202
Dennis Dmitriev86750962017-07-11 19:44:05 +0300203 vcp_only_parser = argparse.ArgumentParser(add_help=False)
204 vcp_only_parser.add_argument(
205 '--vcp-only', dest='vcp_only',
206 action='store_const', const=True,
207 help=('Show only VCP nodes (present in '
208 'parameters.salt.control.cluster.internal.node)'),
209 default=False)
210
211 non_vcp_only_parser = argparse.ArgumentParser(add_help=False)
212 non_vcp_only_parser.add_argument(
213 '--non-vcp-only', dest='non_vcp_only',
214 action='store_const', const=True, default=False,
215 help=('Show only non-VCP nodes (absent in '
216 'parameters.salt.control.cluster.internal.node)'))
217
218 render_parser = argparse.ArgumentParser(add_help=False)
219 render_parser.add_argument(
220 '--template-dir', '-t', dest='template_dir',
221 help=('Coockiecutter-based template directory'))
222 render_parser.add_argument(
223 '--output-dir', '-o', dest='output_dir',
224 help=('Path to the directory where the rendered '
225 'template will be placed'))
226 render_parser.add_argument(
Dennis Dmitrievc2534372017-10-09 15:36:21 +0300227 '--context', '-c', dest='contexts', action='append',
Dennis Dmitriev86750962017-07-11 19:44:05 +0300228 help=('YAML/JSON files with context data to render '
229 'the template'))
230
Dennis Dmitriev86750962017-07-11 19:44:05 +0300231 parser = argparse.ArgumentParser(
232 description="Manage virtual environments. "
233 "For additional help, use with -h/--help option")
234 subparsers = parser.add_subparsers(title="Operation commands",
235 help='available commands',
236 dest='command')
237
Dennis Dmitriev86750962017-07-11 19:44:05 +0300238 subparsers.add_parser('get-key',
239 parents=[key_parser, path_parser,
240 verbose_parser],
241 help="Find a key in YAMLs found in <path>",
242 description=("Get a key collected from "
243 "different YAMLs"))
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300244 subparsers.add_parser('add-key',
245 parents=[key_parser, add_value_parser,
246 path_parser, verbose_parser,
247 merge_parser],
Oleksii Grudev9d63bc12019-02-13 16:12:51 +0200248 help="Add a key in YAMLs found in <path>",
249 description=("Add a key to "
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300250 "different YAMLs"))
Oleksii Grudev9d63bc12019-02-13 16:12:51 +0200251
252 subparsers.add_parser('add-bool-key',
253 parents=[key_parser, add_value_parser,
254 path_parser, verbose_parser,
255 merge_parser],
256 help="Add a bool key in YAMLs found in <path>. "
257 "True/False string values are converted to bool",
258 description=("Add a bool key collected to "
259 "different YAMLs"))
260
Dennis Dmitriev86750962017-07-11 19:44:05 +0300261 subparsers.add_parser('del-key',
262 parents=[key_parser, path_parser,
263 verbose_parser],
264 help="Delete a key from YAMLs found in <path>",
265 description="Delete a key from different YAMLs")
266 subparsers.add_parser('list-params',
267 parents=[path_parser, verbose_parser],
268 help=("Collect all options for "
269 "'parameters._params' keys from YAMLs "
270 "found in <path>"))
271 subparsers.add_parser('list-nodes',
272 parents=[domain_parser, vcp_only_parser,
273 non_vcp_only_parser],
274 help=("List nodes that are available for "
275 "reclass. Use on salt-master node only!"))
276 subparsers.add_parser('list-domains',
277 help=("List domains that are available from "
278 "reclass models. Use on salt-master "
279 "node only!"))
280 subparsers.add_parser('show-context',
281 parents=[domain_parser, keys_parser],
282 help=("Show domain nodes with rendered content "
283 "for specified keys. Use on salt-master "
284 "node for already generated inventory "
285 "only!"))
Dennis Dmitriev806706d2017-07-29 22:31:23 +0300286 subparsers.add_parser('trace-key',
287 parents=[domain_parser, node_parser, key_parser],
288 help=("Use 'reclass' to merge the model and "
289 "show all the classes where the "
290 "specified key is overwritten, and updated"
291 " values during merging and after "
292 "interpolation. "
293 "Use on salt-master node only!"))
Dennis Dmitriev86750962017-07-11 19:44:05 +0300294 subparsers.add_parser('render',
Dennis Dmitriev0cea5702017-07-17 19:03:23 +0300295 parents=[render_parser, env_name_parser],
Dennis Dmitriev86750962017-07-11 19:44:05 +0300296 help=("Render cookiecutter template using "
297 "multiple metadata sources"))
298
299 if len(self.args) == 0:
300 self.args = ['-h']
301 return parser.parse_args(self.args)
302
303
304def main(args=None):
Dennis Dmitrieve56c8b92017-06-16 01:53:16 +0300305 if args is None:
306 args = sys.argv[1:]
307
Dennis Dmitriev86750962017-07-11 19:44:05 +0300308 shell = Shell(args)
309 shell.execute()