blob: 93898175b25708b8b8a67c904a54182f7885439b [file] [log] [blame]
Alex Savatieiev5118de02019-02-20 15:50:42 -06001import argparse
2import os
Alex Savatieiev06ab17d2019-02-26 18:40:48 -06003import reclass
Alex Savatieiev5118de02019-02-20 15:50:42 -06004import sys
Alex Savatieieve9613992019-02-21 18:20:35 -06005import traceback
Alex Savatieiev06ab17d2019-02-26 18:40:48 -06006
Alex Savatieiev799bee32019-02-20 17:19:26 -06007from logging import INFO, DEBUG
Alex Savatieiev5118de02019-02-20 15:50:42 -06008
9import reporter
Alex Savatieiev06ab17d2019-02-26 18:40:48 -060010
Alex Savatieiev63576832019-02-27 15:46:26 -060011from cfg_checker.common.exception import CheckerException, ConfigException
Alex Savatieiev5118de02019-02-20 15:50:42 -060012from cfg_checker.common import utils, const
13from cfg_checker.common import config, logger, logger_cli, pkg_dir
Alex Savatieiev5118de02019-02-20 15:50:42 -060014
Alex Savatieiev06ab17d2019-02-26 18:40:48 -060015import cfg_checker.reclass_cmp as reclass_cmp
Alex Savatieiev5118de02019-02-20 15:50:42 -060016from cfg_checker.pkg_check import CloudPackageChecker
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060017from cfg_checker.network_checks import NetworkChecker
Alex Savatieiev5118de02019-02-20 15:50:42 -060018
19pkg_dir = os.path.dirname(__file__)
20pkg_dir = os.path.normpath(pkg_dir)
21
Alex Savatieiev9c642112019-02-26 13:55:43 -060022commands = {
23 'packages': ['report'],
24 'network': ['check', 'report'],
25 'reclass': ['list', 'diff']
26}
Alex Savatieiev5118de02019-02-20 15:50:42 -060027
28class MyParser(argparse.ArgumentParser):
29 def error(self, message):
30 sys.stderr.write('Error: {0}\n\n'.format(message))
31 self.print_help()
32
33
Alex Savatieiev5118de02019-02-20 15:50:42 -060034def help_message():
35 print"""
36 Please, use following examples to generate info reports:\n
37 cfg_checker packages report\n
38 cfg_checker network check\n
39 cfg_checker network report\n
40 """
41 return
42
Alex Savatieiev9c642112019-02-26 13:55:43 -060043
44def get_file_arg(args):
Alex Savatieieve47f7f42019-02-20 16:41:23 -060045 if args.file:
Alex Savatieiev9c642112019-02-26 13:55:43 -060046 return args.file
Alex Savatieieve47f7f42019-02-20 16:41:23 -060047 else:
Alex Savatieiev9c642112019-02-26 13:55:43 -060048 raise ConfigException("No report filename supplied")
49
50
Alex Savatieiev06ab17d2019-02-26 18:40:48 -060051def get_path_arg(path):
52 if os.path.exists(path):
53 return path
54 else:
55 raise ConfigException("'{}' not exists".format(path))
56
57
58def validate_model(path):
59 logger_cli.debug("\t...validating '{}' as a model".format(path))
60 _checks = []
61 _is_folder = os.path.isdir(path)
62 logger_cli.debug("\t- folder? -> {}".format(_is_folder))
63 _checks.append(_is_folder)
64 _has_classes = os.path.isdir(os.path.join(path, "classes"))
65 logger_cli.debug("\t- has classes? -> {}".format(_has_classes))
66 _checks.append(_has_classes)
67 _has_cluster = os.path.isdir(os.path.join(path, "classes", "cluster"))
68 logger_cli.debug("\t- has classes/cluster? -> {}".format(_has_cluster))
69 _checks.append(_has_cluster)
70 _has_system = os.path.isdir(os.path.join(path, "classes", "system"))
71 logger_cli.debug("\t- has classes/system? -> {}".format(_has_system))
72 _checks.append(_has_system)
73 _has_nodes = os.path.isdir(os.path.join(path, "nodes"))
74 logger_cli.debug("\t- has nodes? -> {}".format(_has_nodes))
75 _checks.append(_has_nodes)
76
77 logger_cli.debug("\t-> {}".format(
78 all(_checks)
79 ))
80
81 return all(_checks)
82
83
Alex Savatieiev9c642112019-02-26 13:55:43 -060084def packages_report(args):
85 """Create package versions report
86
87 :args: - parser arguments
88 :return: - no return value
89 """
90 _filename = get_file_arg(args)
91
Alex Savatieiev5118de02019-02-20 15:50:42 -060092 # init connection to salt and collect minion data
93 pChecker = CloudPackageChecker()
94 # collect data on installed packages
95 pChecker.collect_installed_packages()
96 # diff installed and candidates
97 # pChecker.collect_packages()
98 # report it
99 pChecker.create_html_report(_filename)
100
101
Alex Savatieiev9c642112019-02-26 13:55:43 -0600102def network_check(args):
103 logger_cli.info("# Network check (CLI output)")
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600104 netChecker = NetworkChecker()
105 netChecker.collect_network_info()
106 netChecker.print_network_report()
Alex Savatieiev5118de02019-02-20 15:50:42 -0600107
108 return
109
110
Alex Savatieiev9c642112019-02-26 13:55:43 -0600111def network_report(args):
112 logger_cli.info("# Network check (HTML report: '{}')".format(args.file))
113 _filename = get_file_arg(args)
114
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600115 netChecker = NetworkChecker()
116 netChecker.collect_network_info()
Alex Savatieiev9c642112019-02-26 13:55:43 -0600117 netChecker.create_html_report(_filename)
118
119 return
120
121
122def reclass_list(args):
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600123 logger_cli.info("# Reclass list")
124 _path = get_path_arg(args.models_path)
125
126 logger_cli.info("# ...models path is '{}'".format(args.models_path))
127
128 models = {}
129 for _folder in os.listdir(args.models_path):
130 # validate item as a model
131 _model_path = os.path.join(
132 args.models_path,
133 _folder
134 )
135 _validated = validate_model(_model_path)
136
137 if not _validated:
138 logger_cli.info("-> '{}' not a valid model".format(_folder))
139 continue
140 else:
141 models[_folder] = _model_path
142
143 logger_cli.info("-> '{}' at '{}'".format(_folder, _model_path))
144
145 # TODO: collect info about the model
Alex Savatieiev9c642112019-02-26 13:55:43 -0600146
147 return
148
149
150def reclass_diff(args):
151 logger_cli.info("Reclass comparer (HTML report: '{}'".format(args.file))
152 _filename = get_file_arg(args)
Alex Savatieiev5118de02019-02-20 15:50:42 -0600153
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600154 # checking folder params
155 _model1 = get_path_arg(args.model1)
156 _model2 = get_path_arg(args.model2)
157
158 # Do actual compare using hardcoded model names
159 mComparer = reclass_cmp.ModelComparer()
160
161 mComparer.model_name_1 = os.path.split(_model1)[1]
162 mComparer.model_path_1 = _model1
163 mComparer.model_name_2 = os.path.split(_model2)[1]
164 mComparer.model_path_2 = _model2
165
166 mComparer.load_model_tree(
167 mComparer.model_name_1,
168 mComparer.model_path_1
169 )
170 mComparer.load_model_tree(
171 mComparer.model_name_2,
172 mComparer.model_path_2
173 )
174
175 diffs = mComparer.generate_model_report_tree()
176
177 report = reporter.ReportToFile(
178 reporter.HTMLModelCompare(),
179 _filename
180 )
181 logger_cli.info("# Generating report to {}".format(_filename))
182 report({
183 "nodes": {},
184 "diffs": diffs
185 })
Alex Savatieiev5118de02019-02-20 15:50:42 -0600186
187
188def config_check_entrypoint():
Alex Savatieiev9c642112019-02-26 13:55:43 -0600189 """
190 Main entry point. Uses nested parsers structure
191 with a default function to execute the comand
192
193 :return: - no return value
194 """
Alex Savatieiev4c406322019-02-28 17:37:09 -0600195 # Main entrypoint
196 parser = MyParser(prog="# Mirantis Cloud configuration checker")
Alex Savatieiev9c642112019-02-26 13:55:43 -0600197
198 # Parsers (each parser can have own arguments)
199 # - subparsers (command)
200 # |- pkg_parser
201 # | - pkg_subparsers (type)
202 # | - pkg_report_parser (default func - pkg_check)
203 # |- net_parser
204 # | - net_subparsers (type)
205 # | - net_check_parser (default func - net_check)
206 # | - net_report_parser (default func - net_report)
207 # - reclass_parser
208 # - reclass_list (default func - reclass_list)
209 # - reclass_compare (default func - reclass_diff)
210
Alex Savatieiev799bee32019-02-20 17:19:26 -0600211 parser.add_argument(
212 "-d",
213 "--debug",
214 action="store_true", default=False,
215 help="Set CLI logging level to DEBUG"
216 )
Alex Savatieiev9c642112019-02-26 13:55:43 -0600217 parser.add_argument(
218 '-f',
219 '--file',
220 help="HTML filename to save report"
221 )
Alex Savatieiev63576832019-02-27 15:46:26 -0600222 parser.add_argument(
223 '-s',
224 '--sudo',
225 action='store_true', default=True,
226 help="Use sudo for getting salt creds"
227 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600228 subparsers = parser.add_subparsers(dest='command')
229 # packages
230 pkg_parser = subparsers.add_parser(
231 'packages',
232 help="Package versions check (Candidate vs Installed)"
233 )
234 pkg_subparsers = pkg_parser.add_subparsers(dest='type')
235
236 pkg_report_parser = pkg_subparsers.add_parser(
237 'report',
238 help="Report package versions to HTML file"
239 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600240
241 # networking
242 net_parser = subparsers.add_parser(
243 'network',
Alex Savatieiev9c642112019-02-26 13:55:43 -0600244 help="Network infrastructure checks and reports"
Alex Savatieiev5118de02019-02-20 15:50:42 -0600245 )
246 net_subparsers = net_parser.add_subparsers(dest='type')
247
248 net_check_parser = net_subparsers.add_parser(
249 'check',
250 help="Do network check and print the result"
251 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600252
253 net_report_parser = net_subparsers.add_parser(
254 'report',
255 help="Generate network check report"
256 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600257
Alex Savatieiev9c642112019-02-26 13:55:43 -0600258 # reclass
259 reclass_parser = subparsers.add_parser(
260 'reclass',
261 help="Reclass related checks and reports"
262 )
263 reclass_subparsers = reclass_parser.add_subparsers(dest='type')
264 reclass_list_parser = reclass_subparsers.add_parser(
265 'list',
266 help="List models available to compare"
267 )
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600268 reclass_list_parser.add_argument(
269 "-p",
270 "--models-path",
271 default="/srv/salt/",
272 help="Global path to search models in"
273 )
Alex Savatieiev9c642112019-02-26 13:55:43 -0600274
275 reclass_diff_parser = reclass_subparsers.add_parser(
276 'diff',
277 help="List models available to compare"
278 )
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600279 reclass_diff_parser.add_argument(
280 "--model1",
281 required=True,
282 help="Model A <path>. Model name is the folder name"
283 )
284 reclass_diff_parser.add_argument(
285 "--model2",
286 required=True,
287 help="Model B <path>. Model name is the folder name"
288 )
289
Alex Savatieiev9c642112019-02-26 13:55:43 -0600290
Alex Savatieiev5118de02019-02-20 15:50:42 -0600291 #parse arguments
Alex Savatieiev9c642112019-02-26 13:55:43 -0600292 try:
293 args = parser.parse_args()
294 except TypeError as e:
295 logger_cli.info("\nPlease, check arguments")
296 return
Alex Savatieiev5118de02019-02-20 15:50:42 -0600297
Alex Savatieiev63576832019-02-27 15:46:26 -0600298 # Pass externally configured values
299 config.ssh_uses_sudo = args.sudo
300
Alex Savatieiev799bee32019-02-20 17:19:26 -0600301 # Handle options
302 if args.debug:
303 logger_cli.setLevel(DEBUG)
304 else:
305 logger_cli.setLevel(INFO)
306
Alex Savatieiev9c642112019-02-26 13:55:43 -0600307 # Validate the commands
308 # check command
309 if args.command not in commands:
310 logger_cli.info("\nPlease, type a command listed above")
311 return
312 elif args.type not in commands[args.command]:
313 # check type
314 logger_cli.info(
315 "\nPlease, select '{}' command type listed above".format(
316 args.command
317 )
318 )
319 return
320 else:
321 # form function name to call
322 _method_name = args.command + "_" + args.type
323 _this_module = sys.modules[__name__]
324 _method = getattr(_this_module, _method_name)
325
Alex Savatieiev5118de02019-02-20 15:50:42 -0600326 # Execute the command
Alex Savatieiev9c642112019-02-26 13:55:43 -0600327 result = _method(args)
Alex Savatieiev5118de02019-02-20 15:50:42 -0600328
329 logger.debug(result)
Alex Savatieiev5118de02019-02-20 15:50:42 -0600330
Alex Savatieiev4c406322019-02-28 17:37:09 -0600331def cli_main():
Alex Savatieieve47f7f42019-02-20 16:41:23 -0600332 try:
333 config_check_entrypoint()
Alex Savatieiev63576832019-02-27 15:46:26 -0600334 except CheckerException as e:
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600335 logger_cli.error("\nERROR: {}".format(
336 e.message
337 ))
338
Alex Savatieieve9613992019-02-21 18:20:35 -0600339 exc_type, exc_value, exc_traceback = sys.exc_info()
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600340 logger_cli.debug("\n{}".format(
Alex Savatieieve9613992019-02-21 18:20:35 -0600341 "".join(traceback.format_exception(
342 exc_type,
343 exc_value,
344 exc_traceback
345 ))
346 ))
Alex Savatieiev4c406322019-02-28 17:37:09 -0600347
348if __name__ == '__main__':
349 cli_main()