Passed
Pull Request — master (#105)
by Jan
07:54 queued 01:36
created

openscap_report.cli.CommandLineAPI.__init__()   A

Complexity

Conditions 1

Size

Total Lines 11
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 11
ccs 11
cts 11
cp 1
rs 9.85
c 0
b 0
f 0
cc 1
nop 1
crap 1
1
# Copyright 2022, Red Hat, Inc.
2
# SPDX-License-Identifier: GPL-2.0-or-later
3
4 1
import argparse
5 1
import logging
6 1
from sys import exit as sys_exit
7 1
from sys import stdin, stdout
8
9 1
from lxml.etree import XMLSyntaxError
10
11 1
from . import __version__
12 1
from .debug_settings import DebugSetting
13 1
from .report_generators import (HTMLReportGenerator, JSONMiniReportGenerator,
14
                                JSONReportGenerator,
15
                                OldStyleHTMLReportGenerator)
16 1
from .scap_results_parser import SCAPResultsParser
17
18 1
DESCRIPTION = ("Generates an HTML report from an ARF (or XCCDF Result) file with results of "
19
               "a SCAP-compatible utility scan. Unless the --output option is specified "
20
               "the report will be written to the standard output.")
21 1
LOG_LEVES_DESCRIPTION = (
22
    "LOG LEVELS:\n"
23
    "\tDEBUG - Detailed information, typically of interest only for diagnosing problems.\n"
24
    "\tINFO - A confirmation that things are working as expected.\n"
25
    "\tWARING -  An indication that something unexpected happened, or a signal of"
26
    " a possible problem in the future. The software is still working as expected.\n"
27
    "\tERROR - Due to a more serious problems, the software has not been able to perform "
28
    "its function to the full extent.\n"
29
    "\tCRITICAL - A serious error, indicating that the program itself may be unable "
30
    "to continue operating.\n"
31
)
32 1
DEBUG_FLAGS_DESCRIPTION = (
33
    "DEBUG FLAGS:\n"
34
    "\tNO-MINIFY - The HTML report will not be minified.\n"
35
    "\tBUTTON-SHOW-ALL-RULES - Adds a button to the HTML report for expanding all rules.\n"
36
    "\tONLINE-CSS - Use the latest online version of Patternfly CSS/JS in the HTML report\n"
37
    "\tBUTTON-SHOW-ALL-RULES-AND-OVAL-TEST-DETAILS - "
38
    "Adds a button to the HTML report for expanding all rules and all OVAL test details."
39
)
40
41 1
MASSAGE_FORMAT = '%(levelname)s: %(message)s'
42 1
EXPECTED_ERRORS = (XMLSyntaxError, )
43 1
EXIT_FAILURE_CODE = 1
44 1
EXIT_SUCCESS_CODE = 0
45
46
47 1
def prepare_parser():
48 1
    parser = argparse.ArgumentParser(
49
        prog="oscap-report",
50
        formatter_class=argparse.RawTextHelpFormatter,
51
        description=DESCRIPTION,
52
        add_help=False,
53
    )
54 1
    parser.add_argument(
55
        "--version",
56
        action="version",
57
        version="%(prog)s " + __version__,
58
        help="Show program's version number and exit.")
59 1
    parser.add_argument(
60
        '-h',
61
        '--help',
62
        action='help',
63
        default=argparse.SUPPRESS,
64
        help='Show this help message and exit.')
65 1
    parser.add_argument(
66
        'FILE',
67
        type=argparse.FileType("r"),
68
        nargs='?',
69
        default=stdin,
70
        help="ARF (XCCDF) file or stdin if not provided.")
71 1
    parser.add_argument(
72
        "-o",
73
        "--output",
74
        action="store",
75
        type=argparse.FileType("wb+", 0),
76
        default=stdout,
77
        help="write the report to a file instead of the standard output.")
78 1
    parser.add_argument(
79
        "--log-file",
80
        action="store",
81
        default=None,
82
        help="write the log to a file instead of stderr.")
83 1
    parser.add_argument(
84
        "--log-level",
85
        action="store",
86
        default="WARNING",
87
        choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
88
        help=(
89
            "write debug information to the log up to the LOG_LEVEL."
90
            f"\n{LOG_LEVES_DESCRIPTION}")
91
    )
92 1
    parser.add_argument(
93
        "-f",
94
        "--format",
95
        action="store",
96
        default="HTML",
97
        choices=["HTML", "OLD-STYLE-HTML", "JSON", "JSON-MINI"],
98
        help="FORMAT: %(choices)s (default: %(default)s)."
99
    )
100 1
    parser.add_argument(
101
        "-d",
102
        "--debug",
103
        action="store",
104
        nargs='+',
105
        default=[""],
106
        choices=[
107
            "NO-MINIFY",
108
            "BUTTON-SHOW-ALL-RULES",
109
            "ONLINE-CSS",
110
            "BUTTON-SHOW-ALL-RULES-AND-OVAL-TEST-DETAILS"
111
        ],
112
        help=(
113
            "extra HTML generation options for debugging"
114
            f"{DEBUG_FLAGS_DESCRIPTION}")
115
    )
116 1
    return parser
117
118
119 1
class CommandLineAPI():  # pylint: disable=R0902
120 1
    def __init__(self):
121 1
        self.arguments = prepare_parser().parse_args()
122 1
        self.log_file = self.arguments.log_file
123 1
        self.log_level = self.arguments.log_level
124 1
        self.debug_flags = self.arguments.debug
125 1
        self._setup_logging()
126 1
        logging.debug("Args: %s", self.arguments)
127 1
        self.report_file = self.arguments.FILE
128 1
        self.output_file = self.arguments.output
129 1
        self.output_format = self.arguments.format.upper()
130 1
        self.debug_setting = DebugSetting()
131
132 1
    def _setup_logging(self):
133 1
        logging.basicConfig(
134
            format=MASSAGE_FORMAT,
135
            filename=self.log_file,
136
            filemode='w',
137
            level=self.log_level.upper()
138
        )
139
140 1
    def get_report_generator(self, report_parser):
141 1
        dict_of_report_generators = {
142
            "HTML": HTMLReportGenerator,
143
            "OLD-STYLE-HTML": OldStyleHTMLReportGenerator,
144
            "JSON": JSONReportGenerator,
145
            "JSON-MINI": JSONMiniReportGenerator,
146
        }
147 1
        return dict_of_report_generators[self.output_format](report_parser)
148
149 1
    def generate_report(self, report_parser):
150 1
        logging.info("Generate report")
151 1
        report_generator = self.get_report_generator(report_parser)
152
153 1
        self.debug_setting.update_settings_with_debug_flags(self.debug_flags)
154
155 1
        return report_generator.generate_report(self.debug_setting)
156
157 1
    def load_file(self):
158 1
        logging.info("Loading file: %s", self.report_file)
159 1
        return self.report_file.read().encode()
160
161 1
    def store_file(self, data):
162 1
        logging.info("Store report")
163 1
        if self.output_file.name == "<stdout>":
164 1
            logging.info("Output is stdout, converting bytes output to str")
165 1
            data = data.read().decode("utf-8")
166 1
        self.output_file.writelines(data)
167
168 1
    def close_files(self):
169 1
        logging.info("Close files")
170 1
        self.report_file.close()
171 1
        self.output_file.close()
172
173
174 1
def main():
175 1
    exit_code = EXIT_SUCCESS_CODE
176 1
    api = CommandLineAPI()
177 1
    arf_report = api.load_file()
178
179 1
    logging.info("Parse file")
180 1
    try:
181 1
        parser = SCAPResultsParser(arf_report)
182
183 1
        report = api.generate_report(parser)
184
185 1
        api.store_file(report)
186
    except EXPECTED_ERRORS as error:
187
        logging.fatal("%s", error)
188
        exit_code = EXIT_FAILURE_CODE
189 1
    api.close_files()
190 1
    sys_exit(exit_code)
191
192
193 1
if __name__ == '__main__':
194
    main()
195