Skip to content

Commit

Permalink
Add an ERROR report group
Browse files Browse the repository at this point in the history
Currently there is no straightforward way to identify whether a report
is an ordinary report or an error report (report generated from an
error).

An `ERROR` group is introduced in `leapp.reporting.Groups.ERROR` which
is used to group error reports. This is utilized in the leapp output to
show the number of error reports in the summary. Also error reports in
`leapp-report.txt` are now followed by a "(error)" string similar to
inhibitors.

Jira: OAMG-9532
  • Loading branch information
matejmatuska committed Aug 9, 2023
1 parent 154e1c5 commit c475b7f
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 28 deletions.
22 changes: 20 additions & 2 deletions leapp/reporting/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ class Groups(BaseListPrimitive):
name = 'groups'

INHIBITOR = 'inhibitor'
# TODO doc
ERROR = 'error'
FAILURE = 'failure'
ACCESSIBILITY = 'accessibility'
AUTHENTICATION = 'authentication'
Expand Down Expand Up @@ -186,7 +188,7 @@ def __init__(self, *args, **kwargs):

# To allow backwards-compatibility with previous report-schema
# Groups that match _DEPRECATION_FLAGS will be shown as flags, the rest as tags
_DEPRECATION_FLAGS = [Groups.INHIBITOR, Groups.FAILURE]
_DEPRECATION_FLAGS = [Groups.INHIBITOR, Groups.FAILURE, Groups.ERROR]


class Key(BasePrimitive):
Expand Down Expand Up @@ -378,12 +380,22 @@ def _create_report_object(entries):
_sanitize_entries(entries)
for entry in entries:
entry.apply(report)

if Groups.INHIBITOR in report.get('groups', []):
if Groups.ERROR in report.get('groups', []):
# *error != inhibitor*
msg = ('Only one of Groups.ERROR and Groups.INHIBITOR can be set at once.'
' Maybe you were looking for Groups.FAILURE?')
raise ValueError(msg)

# Before removing Flags, the original inhibitor detection worked
# by checking the `flags` field; keep the `flags` field until we drop Flags completely
# Currently we know that 'flags' does not exist otherwise so it's
# safe to just set it
report['flags'] = [Groups.INHIBITOR]
if Groups.ERROR in report.get('groups', []):
# see above for explanation
report['flags'] = [Groups.ERROR]

return Report(report=report)

Expand Down Expand Up @@ -431,6 +443,12 @@ def create_report_from_error(error_dict):
Convert error json to report json
"""
error = ErrorModel.create(error_dict)
entries = [Title(error.message), Summary(error.details or ""), Severity('high'), Audience('sysadmin')]
entries = [
Title(error.message),
Summary(error.details or ""),
Severity('high'),
Audience('sysadmin'),
Groups([Groups.ERROR])
]
report = _create_report_object(entries)
return json.loads(report.dump()['report'])
51 changes: 34 additions & 17 deletions leapp/utils/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ def _print_report_titles(reports):

def report_inhibitors(context_id):
# The following imports are required to be here to avoid import loop problems
from leapp.utils.report import fetch_upgrade_report_messages, is_inhibitor # noqa; pylint: disable=import-outside-toplevel
from leapp.utils.report import fetch_upgrade_report_messages, Groups, has_flag_group # noqa; pylint: disable=import-outside-toplevel
reports = fetch_upgrade_report_messages(context_id)
inhibitors = [report for report in reports if is_inhibitor(report)]
inhibitors = [report for report in reports if has_flag_group(report, Groups.INHIBITOR)]
if inhibitors:
text = 'UPGRADE INHIBITED'
with pretty_block(text=text, end_text=text, color=Color.red, target=sys.stdout):
Expand Down Expand Up @@ -110,25 +110,41 @@ def report_errors(errors):
print_error(error)


def _filter_reports(reports, severity=None, is_inhibitor=False):
def _filter_reports(reports, severity=None, has_flag=None):
# The following imports are required to be here to avoid import loop problems
from leapp.utils.report import is_inhibitor as isinhibitor # noqa; pylint: disable=import-outside-toplevel
if not severity:
return [report for report in reports if isinhibitor(report) == is_inhibitor]
return [report for report in reports if report['severity'] == severity and isinhibitor(report) == is_inhibitor]
from leapp.reporting import Groups # noqa; pylint: disable=import-outside-toplevel
from leapp.utils.report import has_flag_group # noqa; pylint: disable=import-outside-toplevel

def is_ordinary_report(report):
return not has_flag_group(report, Groups.ERROR) and not has_flag_group(report, Groups.INHIBITOR)

result = reports
if has_flag is False:
result = [rep for rep in result if is_ordinary_report(rep)]
elif has_flag is not None:
result = [rep for rep in result if has_flag_group(rep, has_flag)]
if severity:
result = [rep for rep in result if rep['severity'] == severity]

return result


def _print_reports_summary(reports):
# The following imports are required to be here to avoid import loop problems
from leapp.reporting import Severity # noqa; pylint: disable=import-outside-toplevel
from leapp.reporting import Groups, Severity # noqa; pylint: disable=import-outside-toplevel

errors = _filter_reports(reports, has_flag=Groups.ERROR)
inhibitors = _filter_reports(reports, has_flag=Groups.INHIBITOR)

ordinary = _filter_reports(reports, has_flag=False)

inhibitors = _filter_reports(reports, is_inhibitor=True)
high = _filter_reports(reports, Severity.HIGH)
medium = _filter_reports(reports, Severity.MEDIUM)
low = _filter_reports(reports, Severity.LOW)
info = _filter_reports(reports, Severity.INFO)
high = _filter_reports(ordinary, Severity.HIGH)
medium = _filter_reports(ordinary, Severity.MEDIUM)
low = _filter_reports(ordinary, Severity.LOW)
info = _filter_reports(ordinary, Severity.INFO)

print('Reports summary:')
print(' Errors: {:5}'.format(len(errors)))
print(' Inhibitors: {:5}'.format(len(inhibitors)))
print(' HIGH severity reports: {:5}'.format(len(high)))
print(' MEDIUM severity reports: {:5}'.format(len(medium)))
Expand All @@ -151,21 +167,22 @@ def report_info(context_id, report_paths, log_paths, answerfile=None, fail=False
from leapp.utils.report import fetch_upgrade_report_messages # noqa; pylint: disable=import-outside-toplevel
reports = fetch_upgrade_report_messages(context_id)

high = _filter_reports(reports, Severity.HIGH)
medium = _filter_reports(reports, Severity.MEDIUM)
ordinary = _filter_reports(reports, has_flag=False)
high = _filter_reports(ordinary, Severity.HIGH)
medium = _filter_reports(ordinary, Severity.MEDIUM)

color = Color.green
if medium or high:
color = Color.yellow
if fail:
color = Color.bold
color = Color.red

with pretty_block("REPORT", target=sys.stdout, color=color):
if high or medium:
print('HIGH and MEDIUM severity reports:')
_print_report_titles(high + medium)
sys.stdout.write('\n')

sys.stdout.write('\n')
_print_reports_summary(reports)

print(
Expand Down
28 changes: 19 additions & 9 deletions leapp/utils/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def fetch_upgrade_report_messages(context_id):
"""
:param context_id: ID to identify the needed messages
:type context_id: str
:return: All upgrade messages of type "Report" withing the given context
:return: All upgrade messages of type "Report" within the given context
"""
report_msgs = get_messages(names=['Report', 'ErrorModel'], context=context_id) or []

Expand Down Expand Up @@ -94,17 +94,22 @@ def fetch_upgrade_report_messages(context_id):


def is_inhibitor(message):
return has_flag_group(message, Groups.INHIBITOR)


def has_flag_group(message, group):
"""
A helper function to check if a message is inhibiting upgrade.
It takes care of possible differences in report schema, like tags/flags -> groups merge in 1.2.0
A helper to check if a message has Group which would have been a Flag before the Tags/Flags merge in 1.2.0
"""
# NOTE(ivasilev) As groups and flags are mutual exclusive, let's not bother with report_schema version
# NOTE(borrowed from ivasilev) As groups and flags are mutual exclusive, let's not bother with report_schema version
# and just check both fields for inhibitor presence
return Groups.INHIBITOR in message.get('groups', message.get('flags', []))
return group in message.get('groups', message.get('flags', []))


def importance(message):
if is_inhibitor(message):
if has_flag_group(message, Groups.ERROR):
return -1
if has_flag_group(message, Groups.INHIBITOR):
return 0
return SEVERITY_LEVELS.get(message['severity'], 99)

Expand All @@ -115,8 +120,13 @@ def generate_report_file(messages_to_report, context, path, report_schema='1.1.0
if path.endswith(".txt"):
with io.open(path, 'w', encoding='utf-8') as f:
for message in sorted(messages_to_report, key=importance):
f.write(u'Risk Factor: {}{}\n'.format(message['severity'],
' (inhibitor)' if is_inhibitor(message) else ''))
flag = ''
if has_flag_group(message, Groups.ERROR):
flag = '(error)'
elif has_flag_group(message, Groups.INHIBITOR):
flag = '(inhibitor)'

f.write(u'Risk Factor: {} {}\n'.format(message['severity'], flag))
f.write(u'Title: {}\n'.format(message['title']))
f.write(u'Summary: {}\n'.format(message['summary']))
remediation = Remediation.from_dict(message.get('detail', {}))
Expand All @@ -136,7 +146,7 @@ def generate_report_file(messages_to_report, context, path, report_schema='1.1.0
f.write(u'-' * 40 + '\n')
elif path.endswith(".json"):
with io.open(path, 'w', encoding='utf-8') as f:
# Here all possible convertions will take place
# Here all possible conversions will take place
if report_schema_tuple < (1, 1, 0):
# report-schema 1.0.0 doesn't have a stable report key
# copy list of messages here not to mess the initial structure for possible further invocations
Expand Down

0 comments on commit c475b7f

Please sign in to comment.