blob: 34a3102a8b16d9dbc00f12cb61b6c47bcdbf41b1 [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 Savatieiev9c642112019-02-26 13:55:43 -060011from cfg_checker.common.exception import 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 Savatieiev5118de02019-02-20 15:50:42 -0600223 subparsers = parser.add_subparsers(dest='command')
224 # packages
225 pkg_parser = subparsers.add_parser(
226 'packages',
227 help="Package versions check (Candidate vs Installed)"
228 )
229 pkg_subparsers = pkg_parser.add_subparsers(dest='type')
230
231 pkg_report_parser = pkg_subparsers.add_parser(
232 'report',
233 help="Report package versions to HTML file"
234 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600235
236 # networking
237 net_parser = subparsers.add_parser(
238 'network',
Alex Savatieiev9c642112019-02-26 13:55:43 -0600239 help="Network infrastructure checks and reports"
Alex Savatieiev5118de02019-02-20 15:50:42 -0600240 )
241 net_subparsers = net_parser.add_subparsers(dest='type')
242
243 net_check_parser = net_subparsers.add_parser(
244 'check',
245 help="Do network check and print the result"
246 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600247
248 net_report_parser = net_subparsers.add_parser(
249 'report',
250 help="Generate network check report"
251 )
Alex Savatieiev5118de02019-02-20 15:50:42 -0600252
Alex Savatieiev9c642112019-02-26 13:55:43 -0600253 # reclass
254 reclass_parser = subparsers.add_parser(
255 'reclass',
256 help="Reclass related checks and reports"
257 )
258 reclass_subparsers = reclass_parser.add_subparsers(dest='type')
259 reclass_list_parser = reclass_subparsers.add_parser(
260 'list',
261 help="List models available to compare"
262 )
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600263 reclass_list_parser.add_argument(
264 "-p",
265 "--models-path",
266 default="/srv/salt/",
267 help="Global path to search models in"
268 )
Alex Savatieiev9c642112019-02-26 13:55:43 -0600269
270 reclass_diff_parser = reclass_subparsers.add_parser(
271 'diff',
272 help="List models available to compare"
273 )
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600274 reclass_diff_parser.add_argument(
275 "--model1",
276 required=True,
277 help="Model A <path>. Model name is the folder name"
278 )
279 reclass_diff_parser.add_argument(
280 "--model2",
281 required=True,
282 help="Model B <path>. Model name is the folder name"
283 )
284
Alex Savatieiev9c642112019-02-26 13:55:43 -0600285
Alex Savatieiev5118de02019-02-20 15:50:42 -0600286 #parse arguments
Alex Savatieiev9c642112019-02-26 13:55:43 -0600287 try:
288 args = parser.parse_args()
289 except TypeError as e:
290 logger_cli.info("\nPlease, check arguments")
291 return
Alex Savatieiev5118de02019-02-20 15:50:42 -0600292
Alex Savatieiev799bee32019-02-20 17:19:26 -0600293 # Handle options
294 if args.debug:
295 logger_cli.setLevel(DEBUG)
296 else:
297 logger_cli.setLevel(INFO)
298
Alex Savatieiev9c642112019-02-26 13:55:43 -0600299 # Validate the commands
300 # check command
301 if args.command not in commands:
302 logger_cli.info("\nPlease, type a command listed above")
303 return
304 elif args.type not in commands[args.command]:
305 # check type
306 logger_cli.info(
307 "\nPlease, select '{}' command type listed above".format(
308 args.command
309 )
310 )
311 return
312 else:
313 # form function name to call
314 _method_name = args.command + "_" + args.type
315 _this_module = sys.modules[__name__]
316 _method = getattr(_this_module, _method_name)
317
Alex Savatieiev5118de02019-02-20 15:50:42 -0600318 # Execute the command
Alex Savatieiev9c642112019-02-26 13:55:43 -0600319 result = _method(args)
Alex Savatieiev5118de02019-02-20 15:50:42 -0600320
321 logger.debug(result)
Alex Savatieiev5118de02019-02-20 15:50:42 -0600322
323if __name__ == '__main__':
Alex Savatieieve47f7f42019-02-20 16:41:23 -0600324 try:
325 config_check_entrypoint()
Alex Savatieiev9c642112019-02-26 13:55:43 -0600326 except ConfigException as e:
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600327 logger_cli.error("\nERROR: {}".format(
328 e.message
329 ))
330
Alex Savatieieve9613992019-02-21 18:20:35 -0600331 exc_type, exc_value, exc_traceback = sys.exc_info()
Alex Savatieiev06ab17d2019-02-26 18:40:48 -0600332 logger_cli.debug("\n{}".format(
Alex Savatieieve9613992019-02-21 18:20:35 -0600333 "".join(traceback.format_exception(
334 exc_type,
335 exc_value,
336 exc_traceback
337 ))
338 ))