Passed
Pull Request — master (#65)
by Jan
07:53
created

openscap_report.cli.CommandLineAPI.__init__()   A

Complexity

Conditions 1

Size

Total Lines 10
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

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