blob: b3b0f5afcaa2195cf5abd150eacc94c0c35a2e38 [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
Vladimir Khlyunev132da8d2024-12-05 02:53:28 +040046 if self.params.str:
47 add_val = str(self.params.add_value)
48 else:
Hanna Arhipova7e4abf32020-11-04 14:16:24 +020049 try:
Vladimir Khlyunev132da8d2024-12-05 02:53:28 +040050 add_val = int(self.params.add_value)
51 except ValueError:
52 # Try convert to float
53 try:
54 add_val = float(self.params.add_value)
55 except ValueError:
56 try:
57 # Try convert to list
58 if type(eval(self.params.add_value)) == list:
59 add_val = eval(self.params.add_value)
60 except (SyntaxError, NameError):
61 add_val = self.params.add_value
Sergeyb50d1f02017-12-25 18:07:15 +040062
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030063 walk_models.add_reclass_parameter(
64 self.params.path,
65 self.params.key_name,
Sergeyb50d1f02017-12-25 18:07:15 +040066 add_val,
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030067 verbose=self.params.verbose,
68 merge=self.params.merge)
69
Oleksii Grudev9d63bc12019-02-13 16:12:51 +020070 def do_add_bool_key(self):
71 add_val = self.str2bool(self.params.add_value)
72
73 walk_models.add_reclass_parameter(
74 self.params.path,
75 self.params.key_name,
76 add_val,
77 verbose=self.params.verbose,
78 merge=self.params.merge)
79
Dennis Dmitriev86750962017-07-11 19:44:05 +030080 def do_del_key(self):
Dennis Dmitriev566db4b2017-07-18 18:13:07 +030081 walk_models.remove_reclass_parameter(
Dennis Dmitriev86750962017-07-11 19:44:05 +030082 self.params.path,
83 self.params.key_name,
84 verbose=self.params.verbose,
85 pretend=False)
86
87 def do_list_params(self):
88 results = walk_models.get_all_reclass_params(
89 self.params.path,
90 verbose=self.params.verbose)
Dennis Dmitrieva7de8b52018-08-08 23:14:03 +030091 print(yaml.dump(results, width=255))
Dennis Dmitriev86750962017-07-11 19:44:05 +030092
93 def do_list_domains(self):
94 try:
95 from reclass_tools import reclass_models
96 except ImportError:
97 sys.exit("Please run this tool on the salt-master node "
98 "with installed 'reclass'")
99 inventory = reclass_models.inventory_list()
100 reclass_storage = reclass_models.reclass_storage(inventory=inventory)
101 print('\n'.join(sorted(reclass_storage.keys())))
Dennis Dmitrieve56c8b92017-06-16 01:53:16 +0300102
Dennis Dmitriev86750962017-07-11 19:44:05 +0300103 def do_list_nodes(self):
104 try:
105 from reclass_tools import reclass_models
106 except ImportError:
107 sys.exit("Please run this tool on the salt-master node "
108 "with installed 'reclass'")
109
110 inventory = reclass_models.inventory_list(domain=self.params.domain)
111 vcp_nodes = reclass_models.vcp_list(domain=self.params.domain,
112 inventory=inventory)
113 vcp_node_names = ['{0}.{1}'.format(name, domain)
114 for name, domain in vcp_nodes]
115
116 if self.params.vcp_only:
117 print('\n'.join(sorted(vcp_node_names)))
118 elif self.params.non_vcp_only:
119 print('\n'.join(sorted((node_name for node_name in inventory.keys()
120 if node_name not in vcp_node_names))))
121 else:
122 print('\n'.join(sorted(inventory.keys())))
123
Dennis Dmitriev806706d2017-07-29 22:31:23 +0300124 def do_trace_key(self):
125 try:
126 from reclass_tools import reclass_models
127 except ImportError:
128 sys.exit("Please run this tool on the salt-master node "
129 "with installed 'reclass'")
130 reclass_models.trace_key(key=self.params.key_name,
131 domain=self.params.domain,
132 node=self.params.node)
133
Dennis Dmitriev86750962017-07-11 19:44:05 +0300134 def do_show_context(self):
135 try:
136 from reclass_tools import create_inventory
137 except ImportError:
138 sys.exit("Please run this tool on the salt-master node "
139 "with installed 'reclass'")
140
141 current_underlay_context = create_inventory.create_inventory_context(
142 domain=self.params.domain, keys=self.params.keys)
143
Dennis Dmitrieva7de8b52018-08-08 23:14:03 +0300144 print(yaml.dump(current_underlay_context, default_flow_style=False,
145 width=255))
Dennis Dmitriev86750962017-07-11 19:44:05 +0300146
147 def do_render(self):
Dennis Dmitrievf260d152017-08-15 00:59:24 +0300148 from reclass_tools import render
Dennis Dmitriev86750962017-07-11 19:44:05 +0300149
150 if not self.params.template_dir or not self.params.output_dir \
151 or not self.params.contexts:
152 sys.exit("Missing parameters, see: reclass-tools render -h")
153
Dennis Dmitrievf260d152017-08-15 00:59:24 +0300154 render.render_dir(template_dir=self.params.template_dir,
155 output_dir=self.params.output_dir,
156 contexts=self.params.contexts,
157 env_name=self.params.env_name)
Dennis Dmitriev86750962017-07-11 19:44:05 +0300158
Hanna Arhipova577ecc72021-02-15 19:28:47 +0200159 def do_merge_context(self):
160 content = yaml.load(open(self.params.yaml).read(), yaml.SafeLoader)
161 walk_models.merge_dict_to_reclass(
162 self.params.path,
163 content)
164
Dennis Dmitriev86750962017-07-11 19:44:05 +0300165 def get_params(self):
166
167 verbose_parser = argparse.ArgumentParser(add_help=False)
168 verbose_parser.add_argument('--verbose', dest='verbose',
169 action='store_const', const=True,
170 help='Show verbosed output', default=False)
171
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300172 merge_parser = argparse.ArgumentParser(add_help=False)
Dennis Dmitriev6792f7e2017-07-18 19:11:22 +0300173 merge_parser.add_argument('--merge', dest='merge',
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300174 action='store_const', const=True,
Dennis Dmitriev6792f7e2017-07-18 19:11:22 +0300175 help='Merge value to list or dict',
176 default=False)
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300177
Vladimir Khlyunev132da8d2024-12-05 02:53:28 +0400178 as_str_parser = argparse.ArgumentParser(add_help=False)
179 as_str_parser.add_argument('--str', dest='str',
180 action='store_const', const=True,
181 help='Use value as plain string',
182 default=False)
183
Dennis Dmitriev86750962017-07-11 19:44:05 +0300184 key_parser = argparse.ArgumentParser(add_help=False)
185 key_parser_help = (
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300186 'Key name to find in reclass model files, for example:'
187 ' parameters.linux.network.interface')
Dennis Dmitriev86750962017-07-11 19:44:05 +0300188 key_parser.add_argument('key_name', help=key_parser_help, default=None)
189
190 keys_parser = argparse.ArgumentParser(add_help=False)
191 keys_parser.add_argument(
192 'keys',
193 help='Key names to find in reclass model files', nargs='*')
194
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300195 add_value_parser = argparse.ArgumentParser(add_help=False)
196 add_value_parser.add_argument(
197 'add_value',
198 help=('Value to add to the reclass model files, can be in the '
199 'inline YAML format'))
200
Hanna Arhipova577ecc72021-02-15 19:28:47 +0200201 yaml_parser = argparse.ArgumentParser(add_help=False)
202 yaml_parser.add_argument(
203 'yaml',
204 help='Path to YAML file with extra context')
205
Dennis Dmitriev86750962017-07-11 19:44:05 +0300206 path_parser = argparse.ArgumentParser(add_help=False)
207 path_parser.add_argument(
208 'path',
209 help='Path to search for *.yml files.', nargs='+')
210
211 domain_parser = argparse.ArgumentParser(add_help=False)
212 domain_parser.add_argument(
213 '--domain', '-d', dest='domain',
214 help=('Show only the nodes which names are ended with the '
215 'specified domain, for example: example.local'))
216
Dennis Dmitriev806706d2017-07-29 22:31:23 +0300217 node_parser = argparse.ArgumentParser(add_help=False)
218 node_parser.add_argument(
219 '--node', '-n', dest='node',
220 help=('Show only the specified node, for example: '
221 'ctl01.example.local'))
222
Dennis Dmitriev0cea5702017-07-17 19:03:23 +0300223 env_name_parser = argparse.ArgumentParser(add_help=False)
224 env_name_parser.add_argument(
225 '--env-name', '-e', dest='env_name',
Dennis Dmitriev55989022017-07-17 19:23:16 +0300226 help=("Name of the 'environment' to create or use"),
227 default=None)
Dennis Dmitriev0cea5702017-07-17 19:03:23 +0300228
Dennis Dmitriev86750962017-07-11 19:44:05 +0300229 vcp_only_parser = argparse.ArgumentParser(add_help=False)
230 vcp_only_parser.add_argument(
231 '--vcp-only', dest='vcp_only',
232 action='store_const', const=True,
233 help=('Show only VCP nodes (present in '
234 'parameters.salt.control.cluster.internal.node)'),
235 default=False)
236
237 non_vcp_only_parser = argparse.ArgumentParser(add_help=False)
238 non_vcp_only_parser.add_argument(
239 '--non-vcp-only', dest='non_vcp_only',
240 action='store_const', const=True, default=False,
241 help=('Show only non-VCP nodes (absent in '
242 'parameters.salt.control.cluster.internal.node)'))
243
244 render_parser = argparse.ArgumentParser(add_help=False)
245 render_parser.add_argument(
246 '--template-dir', '-t', dest='template_dir',
247 help=('Coockiecutter-based template directory'))
248 render_parser.add_argument(
249 '--output-dir', '-o', dest='output_dir',
250 help=('Path to the directory where the rendered '
251 'template will be placed'))
252 render_parser.add_argument(
Dennis Dmitrievc2534372017-10-09 15:36:21 +0300253 '--context', '-c', dest='contexts', action='append',
Dennis Dmitriev86750962017-07-11 19:44:05 +0300254 help=('YAML/JSON files with context data to render '
255 'the template'))
256
Dennis Dmitriev86750962017-07-11 19:44:05 +0300257 parser = argparse.ArgumentParser(
258 description="Manage virtual environments. "
259 "For additional help, use with -h/--help option")
260 subparsers = parser.add_subparsers(title="Operation commands",
261 help='available commands',
262 dest='command')
263
Dennis Dmitriev86750962017-07-11 19:44:05 +0300264 subparsers.add_parser('get-key',
265 parents=[key_parser, path_parser,
266 verbose_parser],
267 help="Find a key in YAMLs found in <path>",
268 description=("Get a key collected from "
269 "different YAMLs"))
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300270 subparsers.add_parser('add-key',
271 parents=[key_parser, add_value_parser,
272 path_parser, verbose_parser,
Vladimir Khlyunev132da8d2024-12-05 02:53:28 +0400273 merge_parser, as_str_parser],
Oleksii Grudev9d63bc12019-02-13 16:12:51 +0200274 help="Add a key in YAMLs found in <path>",
275 description=("Add a key to "
Dennis Dmitriev566db4b2017-07-18 18:13:07 +0300276 "different YAMLs"))
Oleksii Grudev9d63bc12019-02-13 16:12:51 +0200277
278 subparsers.add_parser('add-bool-key',
279 parents=[key_parser, add_value_parser,
280 path_parser, verbose_parser,
281 merge_parser],
282 help="Add a bool key in YAMLs found in <path>. "
283 "True/False string values are converted to bool",
284 description=("Add a bool key collected to "
285 "different YAMLs"))
286
Dennis Dmitriev86750962017-07-11 19:44:05 +0300287 subparsers.add_parser('del-key',
288 parents=[key_parser, path_parser,
289 verbose_parser],
290 help="Delete a key from YAMLs found in <path>",
291 description="Delete a key from different YAMLs")
292 subparsers.add_parser('list-params',
293 parents=[path_parser, verbose_parser],
294 help=("Collect all options for "
295 "'parameters._params' keys from YAMLs "
296 "found in <path>"))
297 subparsers.add_parser('list-nodes',
298 parents=[domain_parser, vcp_only_parser,
299 non_vcp_only_parser],
300 help=("List nodes that are available for "
301 "reclass. Use on salt-master node only!"))
302 subparsers.add_parser('list-domains',
303 help=("List domains that are available from "
304 "reclass models. Use on salt-master "
305 "node only!"))
306 subparsers.add_parser('show-context',
307 parents=[domain_parser, keys_parser],
308 help=("Show domain nodes with rendered content "
309 "for specified keys. Use on salt-master "
310 "node for already generated inventory "
311 "only!"))
Dennis Dmitriev806706d2017-07-29 22:31:23 +0300312 subparsers.add_parser('trace-key',
313 parents=[domain_parser, node_parser, key_parser],
314 help=("Use 'reclass' to merge the model and "
315 "show all the classes where the "
316 "specified key is overwritten, and updated"
317 " values during merging and after "
318 "interpolation. "
319 "Use on salt-master node only!"))
Dennis Dmitriev86750962017-07-11 19:44:05 +0300320 subparsers.add_parser('render',
Dennis Dmitriev0cea5702017-07-17 19:03:23 +0300321 parents=[render_parser, env_name_parser],
Dennis Dmitriev86750962017-07-11 19:44:05 +0300322 help=("Render cookiecutter template using "
323 "multiple metadata sources"))
Hanna Arhipova577ecc72021-02-15 19:28:47 +0200324 subparsers.add_parser('merge-context',
325 parents=[yaml_parser, path_parser],
326 help=("Merge nested content from YAML file "
327 "to specified reclass models"))
Dennis Dmitriev86750962017-07-11 19:44:05 +0300328
329 if len(self.args) == 0:
330 self.args = ['-h']
331 return parser.parse_args(self.args)
332
333
334def main(args=None):
Dennis Dmitrieve56c8b92017-06-16 01:53:16 +0300335 if args is None:
336 args = sys.argv[1:]
337
Dennis Dmitriev86750962017-07-11 19:44:05 +0300338 shell = Shell(args)
339 shell.execute()