blob: b8243fff8fc140b0500c248977f34c65475dfde0 [file] [log] [blame]
Alex Savatieiev5118de02019-02-20 15:50:42 -06001import argparse
2import os
3import sys
Alex Savatieieve9613992019-02-21 18:20:35 -06004import traceback
Alex Savatieiev06ab17d2019-02-26 18:40:48 -06005
Alex Savatieiev799bee32019-02-20 17:19:26 -06006from logging import INFO, DEBUG
Alex Savatieiev5118de02019-02-20 15:50:42 -06007
8import reporter
Alex Savatieiev06ab17d2019-02-26 18:40:48 -06009
Alex Savatieiev63576832019-02-27 15:46:26 -060010from cfg_checker.common.exception import CheckerException, ConfigException
Alex Savatieiev5118de02019-02-20 15:50:42 -060011from cfg_checker.common import utils, const
12from cfg_checker.common import config, logger, logger_cli, pkg_dir
Alex Savatieiev5118de02019-02-20 15:50:42 -060013
Alex Savatieiev06ab17d2019-02-26 18:40:48 -060014import cfg_checker.reclass_cmp as reclass_cmp
Alex Savatieiev5118de02019-02-20 15:50:42 -060015from cfg_checker.pkg_check import CloudPackageChecker
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060016from cfg_checker.network_checks import NetworkChecker
Alex Savatieiev5118de02019-02-20 15:50:42 -060017
18pkg_dir = os.path.dirname(__file__)
19pkg_dir = os.path.normpath(pkg_dir)
20
Alex Savatieiev9c642112019-02-26 13:55:43 -060021commands = {
22 'packages': ['report'],
23 'network': ['check', 'report'],
24 'reclass': ['list', 'diff']
25}
Alex Savatieiev5118de02019-02-20 15:50:42 -060026
27class MyParser(argparse.ArgumentParser):
28 def error(self, message):
29 sys.stderr.write('Error: {0}\n\n'.format(message))
30 self.print_help()
31
32
Alex Savatieiev5118de02019-02-20 15:50:42 -060033def help_message():
34 print"""
35 Please, use following examples to generate info reports:\n
36 cfg_checker packages report\n
37 cfg_checker network check\n
38 cfg_checker network report\n
39 """
40 return
41
Alex Savatieiev9c642112019-02-26 13:55:43 -060042
43def get_file_arg(args):
Alex Savatieieve47f7f42019-02-20 16:41:23 -060044 if args.file:
Alex Savatieiev9c642112019-02-26 13:55:43 -060045 return args.file
Alex Savatieieve47f7f42019-02-20 16:41:23 -060046 else:
Alex Savatieiev9c642112019-02-26 13:55:43 -060047 raise ConfigException("No report filename supplied")
48
49
Alex Savatieiev06ab17d2019-02-26 18:40:48 -060050def get_path_arg(path):
51 if os.path.exists(path):
52 return path
53 else:
54 raise ConfigException("'{}' not exists".format(path))
55
56
57def validate_model(path):
58 logger_cli.debug("\t...validating '{}' as a model".format(path))
59 _checks = []
60 _is_folder = os.path.isdir(path)
61 logger_cli.debug("\t- folder? -> {}".format(_is_folder))
62 _checks.append(_is_folder)
63 _has_classes = os.path.isdir(os.path.join(path, "classes"))
64 logger_cli.debug("\t- has classes? -> {}".format(_has_classes))
65 _checks.append(_has_classes)
66 _has_cluster = os.path.isdir(os.path.join(path, "classes", "cluster"))
67 logger_cli.debug("\t- has classes/cluster? -> {}".format(_has_cluster))
68 _checks.append(_has_cluster)
69 _has_system = os.path.isdir(os.path.join(path, "classes", "system"))
70 logger_cli.debug("\t- has classes/system? -> {}".format(_has_system))
71 _checks.append(_has_system)
72 _has_nodes = os.path.isdir(os.path.join(path, "nodes"))
73 logger_cli.debug("\t- has nodes? -> {}".format(_has_nodes))
74 _checks.append(_has_nodes)
75
76 logger_cli.debug("\t-> {}".format(
77 all(_checks)
78 ))
79
80 return all(_checks)
81
82
Alex Savatieiev9c642112019-02-26 13:55:43 -060083def packages_report(args):
84 """Create package versions report
85
86 :args: - parser arguments
87 :return: - no return value
88 """
89 _filename = get_file_arg(args)
90
Alex Savatieiev5118de02019-02-20 15:50:42 -060091 # init connection to salt and collect minion data
92 pChecker = CloudPackageChecker()
93 # collect data on installed packages
94 pChecker.collect_installed_packages()
95 # diff installed and candidates
96 # pChecker.collect_packages()
97 # report it
98 pChecker.create_html_report(_filename)
99
100
Alex Savatieiev9c642112019-02-26 13:55:43 -0600101def network_check(args):
102 logger_cli.info("# Network check (CLI output)")
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600103 netChecker = NetworkChecker()
104 netChecker.collect_network_info()
105 netChecker.print_network_report()
Alex Savatieiev5118de02019-02-20 15:50:42 -0600106
107 return
108
109
Alex Savatieiev9c642112019-02-26 13:55:43 -0600110def network_report(args):
111 logger_cli.info("# Network check (HTML report: '{}')".format(args.file))
112 _filename = get_file_arg(args)
113
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600114 netChecker = NetworkChecker()
115 netChecker.collect_network_info()
Alex Savatieiev9c642112019-02-26 13:55:43 -0600116 netChecker.create_html_report(_filename)
117
118 return
119
120
121def reclass_list(args):
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600122 logger_cli.info("# Reclass list")
123 _path = get_path_arg(args.models_path)
124
125 logger_cli.info("# ...models path is '{}'".format(args.models_path))
126
127 models = {}
128 for _folder in os.listdir(args.models_path):
129 # validate item as a model
130 _model_path = os.path.join(
131 args.models_path,
132 _folder
133 )
134 _validated = validate_model(_model_path)
135
136 if not _validated:
137 logger_cli.info("-> '{}' not a valid model".format(_folder))
138 continue
139 else:
140 models[_folder] = _model_path
141
142 logger_cli.info("-> '{}' at '{}'".format(_folder, _model_path))
143
144 # TODO: collect info about the model
Alex Savatieiev9c642112019-02-26 13:55:43 -0600145
146 return
147
148
149def reclass_diff(args):
150 logger_cli.info("Reclass comparer (HTML report: '{}'".format(args.file))
151 _filename = get_file_arg(args)
Alex Savatieiev5118de02019-02-20 15:50:42 -0600152
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600153 # checking folder params
154 _model1 = get_path_arg(args.model1)
155 _model2 = get_path_arg(args.model2)
156
157 # Do actual compare using hardcoded model names
158 mComparer = reclass_cmp.ModelComparer()
159
160 mComparer.model_name_1 = os.path.split(_model1)[1]
161 mComparer.model_path_1 = _model1
162 mComparer.model_name_2 = os.path.split(_model2)[1]
163 mComparer.model_path_2 = _model2
164
165 mComparer.load_model_tree(
166 mComparer.model_name_1,
167 mComparer.model_path_1
168 )
169 mComparer.load_model_tree(
170 mComparer.model_name_2,
171 mComparer.model_path_2
172 )
173
174 diffs = mComparer.generate_model_report_tree()
175
176 report = reporter.ReportToFile(
177 reporter.HTMLModelCompare(),
178 _filename
179 )
180 logger_cli.info("# Generating report to {}".format(_filename))
181 report({
182 "nodes": {},
183 "diffs": diffs
184 })
Alex Savatieiev5118de02019-02-20 15:50:42 -0600185
186
187def config_check_entrypoint():
Alex Savatieiev9c642112019-02-26 13:55:43 -0600188 """
189 Main entry point. Uses nested parsers structure
190 with a default function to execute the comand
191
192 :return: - no return value
193 """
Alex Savatieiev4c406322019-02-28 17:37:09 -0600194 # Main entrypoint
195 parser = MyParser(prog="# Mirantis Cloud configuration checker")
Alex Savatieiev9c642112019-02-26 13:55:43 -0600196
197 # Parsers (each parser can have own arguments)
198 # - subparsers (command)
199 # |- pkg_parser
200 # | - pkg_subparsers (type)
201 # | - pkg_report_parser (default func - pkg_check)
202 # |- net_parser
203 # | - net_subparsers (type)
204 # | - net_check_parser (default func - net_check)
205 # | - net_report_parser (default func - net_report)
206 # - reclass_parser
207 # - reclass_list (default func - reclass_list)
208 # - reclass_compare (default func - reclass_diff)
209
Alex Savatieiev799bee32019-02-20 17:19:26 -0600210 parser.add_argument(
211 "-d",
212 "--debug",
213 action="store_true", default=False,
214 help="Set CLI logging level to DEBUG"
215 )
Alex Savatieiev9c642112019-02-26 13:55:43 -0600216 parser.add_argument(
217 '-f',
218 '--file',
219 help="HTML filename to save report"
220 )
Alex Savatieiev63576832019-02-27 15:46:26 -0600221 parser.add_argument(
222 '-s',
223 '--sudo',
224 action='store_true', default=True,
225 help="Use sudo for getting salt creds"
226 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600227 subparsers = parser.add_subparsers(dest='command')
228 # packages
229 pkg_parser = subparsers.add_parser(
230 'packages',
231 help="Package versions check (Candidate vs Installed)"
232 )
233 pkg_subparsers = pkg_parser.add_subparsers(dest='type')
234
235 pkg_report_parser = pkg_subparsers.add_parser(
236 'report',
237 help="Report package versions to HTML file"
238 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600239
240 # networking
241 net_parser = subparsers.add_parser(
242 'network',
Alex Savatieiev9c642112019-02-26 13:55:43 -0600243 help="Network infrastructure checks and reports"
Alex Savatieiev5118de02019-02-20 15:50:42 -0600244 )
245 net_subparsers = net_parser.add_subparsers(dest='type')
246
247 net_check_parser = net_subparsers.add_parser(
248 'check',
249 help="Do network check and print the result"
250 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600251
252 net_report_parser = net_subparsers.add_parser(
253 'report',
254 help="Generate network check report"
255 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600256
Alex Savatieiev9c642112019-02-26 13:55:43 -0600257 # reclass
258 reclass_parser = subparsers.add_parser(
259 'reclass',
260 help="Reclass related checks and reports"
261 )
262 reclass_subparsers = reclass_parser.add_subparsers(dest='type')
263 reclass_list_parser = reclass_subparsers.add_parser(
264 'list',
265 help="List models available to compare"
266 )
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600267 reclass_list_parser.add_argument(
268 "-p",
269 "--models-path",
270 default="/srv/salt/",
271 help="Global path to search models in"
272 )
Alex Savatieiev9c642112019-02-26 13:55:43 -0600273
274 reclass_diff_parser = reclass_subparsers.add_parser(
275 'diff',
276 help="List models available to compare"
277 )
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600278 reclass_diff_parser.add_argument(
279 "--model1",
280 required=True,
281 help="Model A <path>. Model name is the folder name"
282 )
283 reclass_diff_parser.add_argument(
284 "--model2",
285 required=True,
286 help="Model B <path>. Model name is the folder name"
287 )
288
Alex Savatieiev9c642112019-02-26 13:55:43 -0600289
Alex Savatieiev5118de02019-02-20 15:50:42 -0600290 #parse arguments
Alex Savatieiev9c642112019-02-26 13:55:43 -0600291 try:
292 args = parser.parse_args()
293 except TypeError as e:
294 logger_cli.info("\nPlease, check arguments")
295 return
Alex Savatieiev5118de02019-02-20 15:50:42 -0600296
Alex Savatieiev63576832019-02-27 15:46:26 -0600297 # Pass externally configured values
298 config.ssh_uses_sudo = args.sudo
299
Alex Savatieiev799bee32019-02-20 17:19:26 -0600300 # Handle options
301 if args.debug:
302 logger_cli.setLevel(DEBUG)
303 else:
304 logger_cli.setLevel(INFO)
305
Alex Savatieiev9c642112019-02-26 13:55:43 -0600306 # Validate the commands
307 # check command
308 if args.command not in commands:
309 logger_cli.info("\nPlease, type a command listed above")
310 return
311 elif args.type not in commands[args.command]:
312 # check type
313 logger_cli.info(
314 "\nPlease, select '{}' command type listed above".format(
315 args.command
316 )
317 )
318 return
319 else:
320 # form function name to call
321 _method_name = args.command + "_" + args.type
322 _this_module = sys.modules[__name__]
323 _method = getattr(_this_module, _method_name)
324
Alex Savatieiev5118de02019-02-20 15:50:42 -0600325 # Execute the command
Alex Savatieiev9c642112019-02-26 13:55:43 -0600326 result = _method(args)
Alex Savatieiev5118de02019-02-20 15:50:42 -0600327
328 logger.debug(result)
Alex Savatieiev5118de02019-02-20 15:50:42 -0600329
Alex Savatieiev4c406322019-02-28 17:37:09 -0600330def cli_main():
Alex Savatieieve47f7f42019-02-20 16:41:23 -0600331 try:
332 config_check_entrypoint()
Alex Savatieiev63576832019-02-27 15:46:26 -0600333 except CheckerException as e:
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600334 logger_cli.error("\nERROR: {}".format(
335 e.message
336 ))
337
Alex Savatieieve9613992019-02-21 18:20:35 -0600338 exc_type, exc_value, exc_traceback = sys.exc_info()
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600339 logger_cli.debug("\n{}".format(
Alex Savatieieve9613992019-02-21 18:20:35 -0600340 "".join(traceback.format_exception(
341 exc_type,
342 exc_value,
343 exc_traceback
344 ))
345 ))
Alex Savatieiev4c406322019-02-28 17:37:09 -0600346
347if __name__ == '__main__':
348 cli_main()