Passed
Pull Request — master (#6)
by Jan
03:39
created

oscap_report.cli.main()   A

Complexity

Conditions 2

Size

Total Lines 17
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 14
nop 0
dl 0
loc 17
rs 9.7
c 0
b 0
f 0
1
import argparse
2
import logging
3
from sys import exit as sys_exit
4
from sys import stdin, stdout
5
6
from lxml.etree import XMLSyntaxError
7
8
from . import __version__
9
from .old_html_report_style.report_generator import ReportGenerator
10
from .scap_results_parser.scap_results_parser import SCAPResultsParser
11
12
DESCRIPTION = ("Generate a HTML (JSON, PDF?, Printable HTML, etc) document (HTML report)"
13
               " from an ARF (or XCCDF file) containing results of oscap scan. Unless"
14
               " the --output option is specified it will be written to standard output.")
15
LOG_LEVES_DESCRIPTION = (
16
    "LOG LEVELS:\n"
17
    "\tDEBUG - Detailed information, typically of interest only when diagnosing problems.\n"
18
    "\tINFO - Confirmation that things are working as expected.\n"
19
    "\tWARING -  An indication that something unexpected happened, or indicative of"
20
    " some problem in the near future. The software is still working as expected.\n"
21
    "\tERROR - Due to a more serious problem, the software has not been able to perform "
22
    "some function.\n"
23
    "\tCRITICAL - A serious error, indicating that the program itself may be unable "
24
    "to continue running.\n"
25
)
26
MASSAGE_FORMAT = '%(levelname)s: %(message)s'
27
EXPECTED_ERRORS = (XMLSyntaxError, )
28
EXIT_FAILURE_CODE = 1
29
EXIT_SUCCESS_CODE = 0
30
31
32
class CommandLineAPI():
33
    def __init__(self):
34
        self.arguments = self._parse_arguments()
35
        self.log_file = self.arguments.log_file
36
        self.log_level = self.arguments.log_level
37
        self._setup_logging()
38
        logging.debug("Args: %s", self.arguments)
39
        self.report_file = self.arguments.FILE
40
        self.output_file = self.arguments.output
41
        self.output_format = self.arguments.format.upper()
42
43
    def _parse_arguments(self):
44
        parser = argparse.ArgumentParser(
45
            prog="oscap-report",
46
            formatter_class=argparse.RawTextHelpFormatter,
47
            description=DESCRIPTION,
48
            add_help=False,
49
        )
50
        self._prepare_arguments(parser)
51
        return parser.parse_args()
52
53
    @staticmethod
54
    def _prepare_arguments(parser):
55
        parser.add_argument(
56
            "--version",
57
            action="version",
58
            version="%(prog)s " + __version__,
59
            help="Show program's version number and exit.")
60
        parser.add_argument(
61
            '-h',
62
            '--help',
63
            action='help',
64
            default=argparse.SUPPRESS,
65
            help='Show this help message and exit.')
66
        parser.add_argument(
67
            'FILE',
68
            type=argparse.FileType("r"),
69
            nargs='?',
70
            default=stdin,
71
            help="ARF file, stdin if not provided.")
72
        parser.add_argument(
73
            "-o",
74
            "--output",
75
            action="store",
76
            type=argparse.FileType("wb+", 0),
77
            default=stdout,
78
            help="write the report to this file instead of standard output.")
79
        parser.add_argument(
80
            "--log-file",
81
            action="store",
82
            default=None,
83
            help="if not provided - stderr.")
84
        parser.add_argument(
85
            "--log-level",
86
            action="store",
87
            default="WARNING",
88
            choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
89
            help=(
90
                "creates LOG_FILE file with log information depending on LOG_LEVEL."
91
                "\n{}").format(LOG_LEVES_DESCRIPTION)
92
        )
93
        parser.add_argument(
94
            "-f",
95
            "--format",
96
            action="store",
97
            default="HTML",
98
            choices=["HTML"],
99
            help="FORMAT: %(choices)s (default: %(default)s)."
100
        )
101
102
    def _setup_logging(self):
103
        logging.basicConfig(
104
            format=MASSAGE_FORMAT,
105
            filename=self.log_file,
106
            filemode='w',
107
            level=self.log_level.upper()
108
        )
109
110
    @staticmethod
111
    def generate_report(report_parser):
112
        logging.info("Generate report")
113
        report_generator = ReportGenerator(report_parser)
114
        return report_generator.generate_html_report()
115
116
    def load_file(self):
117
        logging.info("Loading file: %s", self.report_file)
118
        return self.report_file.read().encode()
119
120
    def store_file(self, data):
121
        logging.info("Store report")
122
        if self.output_file.name == "<stdout>":
123
            logging.info("Output is stdout, converting bites output to str")
124
            data = data.read().decode("utf-8")
125
        self.output_file.writelines(data)
126
127
    def close_files(self):
128
        logging.info("Close files")
129
        self.report_file.close()
130
        self.output_file.close()
131
132
133
def main():
134
    exit_code = EXIT_SUCCESS_CODE
135
    api = CommandLineAPI()
136
    arf_report = api.load_file()
137
138
    logging.info("Parse file")
139
    try:
140
        parser = SCAPResultsParser(arf_report)
141
142
        report = api.generate_report(parser)
143
144
        api.store_file(report)
145
    except EXPECTED_ERRORS as error:
146
        logging.fatal("%s", error)
147
        exit_code = EXIT_FAILURE_CODE
148
    api.close_files()
149
    sys_exit(exit_code)
150
151
152
if __name__ == '__main__':
153
    main()
154