blob: b46ca604bd2a294908ef0cb56a2100b012cb8424 [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
14from cfg_checker.clients import salt
15
Alex Savatieiev06ab17d2019-02-26 18:40:48 -060016import cfg_checker.reclass_cmp as reclass_cmp
Alex Savatieiev5118de02019-02-20 15:50:42 -060017from cfg_checker.pkg_check import CloudPackageChecker
Alex Savatieiev9b2f6512019-02-20 18:05:00 -060018from cfg_checker.network_checks import NetworkChecker
Alex Savatieiev5118de02019-02-20 15:50:42 -060019
20pkg_dir = os.path.dirname(__file__)
21pkg_dir = os.path.normpath(pkg_dir)
22
Alex Savatieiev9c642112019-02-26 13:55:43 -060023commands = {
24 'packages': ['report'],
25 'network': ['check', 'report'],
26 'reclass': ['list', 'diff']
27}
Alex Savatieiev5118de02019-02-20 15:50:42 -060028
29class MyParser(argparse.ArgumentParser):
30 def error(self, message):
31 sys.stderr.write('Error: {0}\n\n'.format(message))
32 self.print_help()
33
34
Alex Savatieiev5118de02019-02-20 15:50:42 -060035def help_message():
36 print"""
37 Please, use following examples to generate info reports:\n
38 cfg_checker packages report\n
39 cfg_checker network check\n
40 cfg_checker network report\n
41 """
42 return
43
Alex Savatieiev9c642112019-02-26 13:55:43 -060044
45def get_file_arg(args):
Alex Savatieieve47f7f42019-02-20 16:41:23 -060046 if args.file:
Alex Savatieiev9c642112019-02-26 13:55:43 -060047 return args.file
Alex Savatieieve47f7f42019-02-20 16:41:23 -060048 else:
Alex Savatieiev9c642112019-02-26 13:55:43 -060049 raise ConfigException("No report filename supplied")
50
51
Alex Savatieiev06ab17d2019-02-26 18:40:48 -060052def get_path_arg(path):
53 if os.path.exists(path):
54 return path
55 else:
56 raise ConfigException("'{}' not exists".format(path))
57
58
59def validate_model(path):
60 logger_cli.debug("\t...validating '{}' as a model".format(path))
61 _checks = []
62 _is_folder = os.path.isdir(path)
63 logger_cli.debug("\t- folder? -> {}".format(_is_folder))
64 _checks.append(_is_folder)
65 _has_classes = os.path.isdir(os.path.join(path, "classes"))
66 logger_cli.debug("\t- has classes? -> {}".format(_has_classes))
67 _checks.append(_has_classes)
68 _has_cluster = os.path.isdir(os.path.join(path, "classes", "cluster"))
69 logger_cli.debug("\t- has classes/cluster? -> {}".format(_has_cluster))
70 _checks.append(_has_cluster)
71 _has_system = os.path.isdir(os.path.join(path, "classes", "system"))
72 logger_cli.debug("\t- has classes/system? -> {}".format(_has_system))
73 _checks.append(_has_system)
74 _has_nodes = os.path.isdir(os.path.join(path, "nodes"))
75 logger_cli.debug("\t- has nodes? -> {}".format(_has_nodes))
76 _checks.append(_has_nodes)
77
78 logger_cli.debug("\t-> {}".format(
79 all(_checks)
80 ))
81
82 return all(_checks)
83
84
Alex Savatieiev9c642112019-02-26 13:55:43 -060085def packages_report(args):
86 """Create package versions report
87
88 :args: - parser arguments
89 :return: - no return value
90 """
91 _filename = get_file_arg(args)
92
Alex Savatieiev5118de02019-02-20 15:50:42 -060093 # init connection to salt and collect minion data
94 pChecker = CloudPackageChecker()
95 # collect data on installed packages
96 pChecker.collect_installed_packages()
97 # diff installed and candidates
98 # pChecker.collect_packages()
99 # report it
100 pChecker.create_html_report(_filename)
101
102
Alex Savatieiev9c642112019-02-26 13:55:43 -0600103def network_check(args):
104 logger_cli.info("# Network check (CLI output)")
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600105 netChecker = NetworkChecker()
106 netChecker.collect_network_info()
107 netChecker.print_network_report()
Alex Savatieiev5118de02019-02-20 15:50:42 -0600108
109 return
110
111
Alex Savatieiev9c642112019-02-26 13:55:43 -0600112def network_report(args):
113 logger_cli.info("# Network check (HTML report: '{}')".format(args.file))
114 _filename = get_file_arg(args)
115
Alex Savatieiev9b2f6512019-02-20 18:05:00 -0600116 netChecker = NetworkChecker()
117 netChecker.collect_network_info()
Alex Savatieiev9c642112019-02-26 13:55:43 -0600118 netChecker.create_html_report(_filename)
119
120 return
121
122
123def reclass_list(args):
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600124 logger_cli.info("# Reclass list")
125 _path = get_path_arg(args.models_path)
126
127 logger_cli.info("# ...models path is '{}'".format(args.models_path))
128
129 models = {}
130 for _folder in os.listdir(args.models_path):
131 # validate item as a model
132 _model_path = os.path.join(
133 args.models_path,
134 _folder
135 )
136 _validated = validate_model(_model_path)
137
138 if not _validated:
139 logger_cli.info("-> '{}' not a valid model".format(_folder))
140 continue
141 else:
142 models[_folder] = _model_path
143
144 logger_cli.info("-> '{}' at '{}'".format(_folder, _model_path))
145
146 # TODO: collect info about the model
Alex Savatieiev9c642112019-02-26 13:55:43 -0600147
148 return
149
150
151def reclass_diff(args):
152 logger_cli.info("Reclass comparer (HTML report: '{}'".format(args.file))
153 _filename = get_file_arg(args)
Alex Savatieiev5118de02019-02-20 15:50:42 -0600154
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600155 # checking folder params
156 _model1 = get_path_arg(args.model1)
157 _model2 = get_path_arg(args.model2)
158
159 # Do actual compare using hardcoded model names
160 mComparer = reclass_cmp.ModelComparer()
161
162 mComparer.model_name_1 = os.path.split(_model1)[1]
163 mComparer.model_path_1 = _model1
164 mComparer.model_name_2 = os.path.split(_model2)[1]
165 mComparer.model_path_2 = _model2
166
167 mComparer.load_model_tree(
168 mComparer.model_name_1,
169 mComparer.model_path_1
170 )
171 mComparer.load_model_tree(
172 mComparer.model_name_2,
173 mComparer.model_path_2
174 )
175
176 diffs = mComparer.generate_model_report_tree()
177
178 report = reporter.ReportToFile(
179 reporter.HTMLModelCompare(),
180 _filename
181 )
182 logger_cli.info("# Generating report to {}".format(_filename))
183 report({
184 "nodes": {},
185 "diffs": diffs
186 })
Alex Savatieiev5118de02019-02-20 15:50:42 -0600187
188
189def config_check_entrypoint():
Alex Savatieiev9c642112019-02-26 13:55:43 -0600190 """
191 Main entry point. Uses nested parsers structure
192 with a default function to execute the comand
193
194 :return: - no return value
195 """
Alex Savatieiev5118de02019-02-20 15:50:42 -0600196 # Main entrypointр
197 parser = MyParser(prog="Cloud configuration checker")
Alex Savatieiev9c642112019-02-26 13:55:43 -0600198
199 # Parsers (each parser can have own arguments)
200 # - subparsers (command)
201 # |- pkg_parser
202 # | - pkg_subparsers (type)
203 # | - pkg_report_parser (default func - pkg_check)
204 # |- net_parser
205 # | - net_subparsers (type)
206 # | - net_check_parser (default func - net_check)
207 # | - net_report_parser (default func - net_report)
208 # - reclass_parser
209 # - reclass_list (default func - reclass_list)
210 # - reclass_compare (default func - reclass_diff)
211
Alex Savatieiev799bee32019-02-20 17:19:26 -0600212 parser.add_argument(
213 "-d",
214 "--debug",
215 action="store_true", default=False,
216 help="Set CLI logging level to DEBUG"
217 )
Alex Savatieiev9c642112019-02-26 13:55:43 -0600218 parser.add_argument(
219 '-f',
220 '--file',
221 help="HTML filename to save report"
222 )
Alex Savatieiev63576832019-02-27 15:46:26 -0600223 parser.add_argument(
224 '-s',
225 '--sudo',
226 action='store_true', default=True,
227 help="Use sudo for getting salt creds"
228 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600229 subparsers = parser.add_subparsers(dest='command')
230 # packages
231 pkg_parser = subparsers.add_parser(
232 'packages',
233 help="Package versions check (Candidate vs Installed)"
234 )
235 pkg_subparsers = pkg_parser.add_subparsers(dest='type')
236
237 pkg_report_parser = pkg_subparsers.add_parser(
238 'report',
239 help="Report package versions to HTML file"
240 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600241
242 # networking
243 net_parser = subparsers.add_parser(
244 'network',
Alex Savatieiev9c642112019-02-26 13:55:43 -0600245 help="Network infrastructure checks and reports"
Alex Savatieiev5118de02019-02-20 15:50:42 -0600246 )
247 net_subparsers = net_parser.add_subparsers(dest='type')
248
249 net_check_parser = net_subparsers.add_parser(
250 'check',
251 help="Do network check and print the result"
252 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600253
254 net_report_parser = net_subparsers.add_parser(
255 'report',
256 help="Generate network check report"
257 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600258
Alex Savatieiev9c642112019-02-26 13:55:43 -0600259 # reclass
260 reclass_parser = subparsers.add_parser(
261 'reclass',
262 help="Reclass related checks and reports"
263 )
264 reclass_subparsers = reclass_parser.add_subparsers(dest='type')
265 reclass_list_parser = reclass_subparsers.add_parser(
266 'list',
267 help="List models available to compare"
268 )
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600269 reclass_list_parser.add_argument(
270 "-p",
271 "--models-path",
272 default="/srv/salt/",
273 help="Global path to search models in"
274 )
Alex Savatieiev9c642112019-02-26 13:55:43 -0600275
276 reclass_diff_parser = reclass_subparsers.add_parser(
277 'diff',
278 help="List models available to compare"
279 )
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600280 reclass_diff_parser.add_argument(
281 "--model1",
282 required=True,
283 help="Model A <path>. Model name is the folder name"
284 )
285 reclass_diff_parser.add_argument(
286 "--model2",
287 required=True,
288 help="Model B <path>. Model name is the folder name"
289 )
290
Alex Savatieiev9c642112019-02-26 13:55:43 -0600291
Alex Savatieiev5118de02019-02-20 15:50:42 -0600292 #parse arguments
Alex Savatieiev9c642112019-02-26 13:55:43 -0600293 try:
294 args = parser.parse_args()
295 except TypeError as e:
296 logger_cli.info("\nPlease, check arguments")
297 return
Alex Savatieiev5118de02019-02-20 15:50:42 -0600298
Alex Savatieiev63576832019-02-27 15:46:26 -0600299 # Pass externally configured values
300 config.ssh_uses_sudo = args.sudo
301
Alex Savatieiev799bee32019-02-20 17:19:26 -0600302 # Handle options
303 if args.debug:
304 logger_cli.setLevel(DEBUG)
305 else:
306 logger_cli.setLevel(INFO)
307
Alex Savatieiev9c642112019-02-26 13:55:43 -0600308 # Validate the commands
309 # check command
310 if args.command not in commands:
311 logger_cli.info("\nPlease, type a command listed above")
312 return
313 elif args.type not in commands[args.command]:
314 # check type
315 logger_cli.info(
316 "\nPlease, select '{}' command type listed above".format(
317 args.command
318 )
319 )
320 return
321 else:
322 # form function name to call
323 _method_name = args.command + "_" + args.type
324 _this_module = sys.modules[__name__]
325 _method = getattr(_this_module, _method_name)
326
Alex Savatieiev5118de02019-02-20 15:50:42 -0600327 # Execute the command
Alex Savatieiev9c642112019-02-26 13:55:43 -0600328 result = _method(args)
Alex Savatieiev5118de02019-02-20 15:50:42 -0600329
330 logger.debug(result)
Alex Savatieiev5118de02019-02-20 15:50:42 -0600331
332if __name__ == '__main__':
Alex Savatieieve47f7f42019-02-20 16:41:23 -0600333 try:
334 config_check_entrypoint()
Alex Savatieiev63576832019-02-27 15:46:26 -0600335 except CheckerException as e:
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600336 logger_cli.error("\nERROR: {}".format(
337 e.message
338 ))
339
Alex Savatieieve9613992019-02-21 18:20:35 -0600340 exc_type, exc_value, exc_traceback = sys.exc_info()
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600341 logger_cli.debug("\n{}".format(
Alex Savatieieve9613992019-02-21 18:20:35 -0600342 "".join(traceback.format_exception(
343 exc_type,
344 exc_value,
345 exc_traceback
346 ))
347 ))