blob: 34a3102a8b16d9dbc00f12cb61b6c47bcdbf41b1 [file] [log] [blame]
import argparse
import os
import reclass
import sys
import traceback
from logging import INFO, DEBUG
import reporter
from cfg_checker.common.exception import ConfigException
from cfg_checker.common import utils, const
from cfg_checker.common import config, logger, logger_cli, pkg_dir
from cfg_checker.clients import salt
import cfg_checker.reclass_cmp as reclass_cmp
from cfg_checker.pkg_check import CloudPackageChecker
from cfg_checker.network_checks import NetworkChecker
pkg_dir = os.path.dirname(__file__)
pkg_dir = os.path.normpath(pkg_dir)
commands = {
'packages': ['report'],
'network': ['check', 'report'],
'reclass': ['list', 'diff']
}
class MyParser(argparse.ArgumentParser):
def error(self, message):
sys.stderr.write('Error: {0}\n\n'.format(message))
self.print_help()
def help_message():
print"""
Please, use following examples to generate info reports:\n
cfg_checker packages report\n
cfg_checker network check\n
cfg_checker network report\n
"""
return
def get_file_arg(args):
if args.file:
return args.file
else:
raise ConfigException("No report filename supplied")
def get_path_arg(path):
if os.path.exists(path):
return path
else:
raise ConfigException("'{}' not exists".format(path))
def validate_model(path):
logger_cli.debug("\t...validating '{}' as a model".format(path))
_checks = []
_is_folder = os.path.isdir(path)
logger_cli.debug("\t- folder? -> {}".format(_is_folder))
_checks.append(_is_folder)
_has_classes = os.path.isdir(os.path.join(path, "classes"))
logger_cli.debug("\t- has classes? -> {}".format(_has_classes))
_checks.append(_has_classes)
_has_cluster = os.path.isdir(os.path.join(path, "classes", "cluster"))
logger_cli.debug("\t- has classes/cluster? -> {}".format(_has_cluster))
_checks.append(_has_cluster)
_has_system = os.path.isdir(os.path.join(path, "classes", "system"))
logger_cli.debug("\t- has classes/system? -> {}".format(_has_system))
_checks.append(_has_system)
_has_nodes = os.path.isdir(os.path.join(path, "nodes"))
logger_cli.debug("\t- has nodes? -> {}".format(_has_nodes))
_checks.append(_has_nodes)
logger_cli.debug("\t-> {}".format(
all(_checks)
))
return all(_checks)
def packages_report(args):
"""Create package versions report
:args: - parser arguments
:return: - no return value
"""
_filename = get_file_arg(args)
# init connection to salt and collect minion data
pChecker = CloudPackageChecker()
# collect data on installed packages
pChecker.collect_installed_packages()
# diff installed and candidates
# pChecker.collect_packages()
# report it
pChecker.create_html_report(_filename)
def network_check(args):
logger_cli.info("# Network check (CLI output)")
netChecker = NetworkChecker()
netChecker.collect_network_info()
netChecker.print_network_report()
return
def network_report(args):
logger_cli.info("# Network check (HTML report: '{}')".format(args.file))
_filename = get_file_arg(args)
netChecker = NetworkChecker()
netChecker.collect_network_info()
netChecker.create_html_report(_filename)
return
def reclass_list(args):
logger_cli.info("# Reclass list")
_path = get_path_arg(args.models_path)
logger_cli.info("# ...models path is '{}'".format(args.models_path))
models = {}
for _folder in os.listdir(args.models_path):
# validate item as a model
_model_path = os.path.join(
args.models_path,
_folder
)
_validated = validate_model(_model_path)
if not _validated:
logger_cli.info("-> '{}' not a valid model".format(_folder))
continue
else:
models[_folder] = _model_path
logger_cli.info("-> '{}' at '{}'".format(_folder, _model_path))
# TODO: collect info about the model
return
def reclass_diff(args):
logger_cli.info("Reclass comparer (HTML report: '{}'".format(args.file))
_filename = get_file_arg(args)
# checking folder params
_model1 = get_path_arg(args.model1)
_model2 = get_path_arg(args.model2)
# Do actual compare using hardcoded model names
mComparer = reclass_cmp.ModelComparer()
mComparer.model_name_1 = os.path.split(_model1)[1]
mComparer.model_path_1 = _model1
mComparer.model_name_2 = os.path.split(_model2)[1]
mComparer.model_path_2 = _model2
mComparer.load_model_tree(
mComparer.model_name_1,
mComparer.model_path_1
)
mComparer.load_model_tree(
mComparer.model_name_2,
mComparer.model_path_2
)
diffs = mComparer.generate_model_report_tree()
report = reporter.ReportToFile(
reporter.HTMLModelCompare(),
_filename
)
logger_cli.info("# Generating report to {}".format(_filename))
report({
"nodes": {},
"diffs": diffs
})
def config_check_entrypoint():
"""
Main entry point. Uses nested parsers structure
with a default function to execute the comand
:return: - no return value
"""
# Main entrypointр
parser = MyParser(prog="Cloud configuration checker")
# Parsers (each parser can have own arguments)
# - subparsers (command)
# |- pkg_parser
# | - pkg_subparsers (type)
# | - pkg_report_parser (default func - pkg_check)
# |- net_parser
# | - net_subparsers (type)
# | - net_check_parser (default func - net_check)
# | - net_report_parser (default func - net_report)
# - reclass_parser
# - reclass_list (default func - reclass_list)
# - reclass_compare (default func - reclass_diff)
parser.add_argument(
"-d",
"--debug",
action="store_true", default=False,
help="Set CLI logging level to DEBUG"
)
parser.add_argument(
'-f',
'--file',
help="HTML filename to save report"
)
subparsers = parser.add_subparsers(dest='command')
# packages
pkg_parser = subparsers.add_parser(
'packages',
help="Package versions check (Candidate vs Installed)"
)
pkg_subparsers = pkg_parser.add_subparsers(dest='type')
pkg_report_parser = pkg_subparsers.add_parser(
'report',
help="Report package versions to HTML file"
)
# networking
net_parser = subparsers.add_parser(
'network',
help="Network infrastructure checks and reports"
)
net_subparsers = net_parser.add_subparsers(dest='type')
net_check_parser = net_subparsers.add_parser(
'check',
help="Do network check and print the result"
)
net_report_parser = net_subparsers.add_parser(
'report',
help="Generate network check report"
)
# reclass
reclass_parser = subparsers.add_parser(
'reclass',
help="Reclass related checks and reports"
)
reclass_subparsers = reclass_parser.add_subparsers(dest='type')
reclass_list_parser = reclass_subparsers.add_parser(
'list',
help="List models available to compare"
)
reclass_list_parser.add_argument(
"-p",
"--models-path",
default="/srv/salt/",
help="Global path to search models in"
)
reclass_diff_parser = reclass_subparsers.add_parser(
'diff',
help="List models available to compare"
)
reclass_diff_parser.add_argument(
"--model1",
required=True,
help="Model A <path>. Model name is the folder name"
)
reclass_diff_parser.add_argument(
"--model2",
required=True,
help="Model B <path>. Model name is the folder name"
)
#parse arguments
try:
args = parser.parse_args()
except TypeError as e:
logger_cli.info("\nPlease, check arguments")
return
# Handle options
if args.debug:
logger_cli.setLevel(DEBUG)
else:
logger_cli.setLevel(INFO)
# Validate the commands
# check command
if args.command not in commands:
logger_cli.info("\nPlease, type a command listed above")
return
elif args.type not in commands[args.command]:
# check type
logger_cli.info(
"\nPlease, select '{}' command type listed above".format(
args.command
)
)
return
else:
# form function name to call
_method_name = args.command + "_" + args.type
_this_module = sys.modules[__name__]
_method = getattr(_this_module, _method_name)
# Execute the command
result = _method(args)
logger.debug(result)
if __name__ == '__main__':
try:
config_check_entrypoint()
except ConfigException as e:
logger_cli.error("\nERROR: {}".format(
e.message
))
exc_type, exc_value, exc_traceback = sys.exc_info()
logger_cli.debug("\n{}".format(
"".join(traceback.format_exception(
exc_type,
exc_value,
exc_traceback
))
))