Skip to content

Instantly share code, notes, and snippets.

@r0x0d
Last active January 15, 2024 14:44
Show Gist options
  • Save r0x0d/0b0b1e741006daaab479502f6580e0b5 to your computer and use it in GitHub Desktop.
Save r0x0d/0b0b1e741006daaab479502f6580e0b5 to your computer and use it in GitHub Desktop.
extending the action framework
diff --git a/convert2rhel/actions/__init__.py b/convert2rhel/actions/__init__.py
index ce8c06f..1ae7375 100644
--- a/convert2rhel/actions/__init__.py
+++ b/convert2rhel/actions/__init__.py
@@ -691,7 +691,7 @@ def resolve_action_order(potential_actions, previously_resolved_actions=None):
)
-def run_actions():
+def run_pre_actions():
"""
Run all of the pre-ponr Actions.
@@ -719,23 +719,68 @@ def run_actions():
# Run the Actions in system_checks and all subsequent Stages.
results = system_checks.run()
- # Format results as a dictionary:
- # {
- # "$Action_id": {
- # "messages": [
- # {
- # "level": int,
- # "id": "$id",
- # "message": "" or "$message"
- # },
- # ]
- # "result": {
- # "level": int,
- # "id": "$id",
- # "message": "" or "$message"
- # },
- # },
- # }
+ return parse_action_results(results)
+
+
+def run_post_actions():
+ """
+ Run all of the post-ponr Actions.
+
+ This function runs the Actions that occur before the Point of no Return.
+ """
+ # Stages are created in the opposite order that they are run in so that
+ # each Stage can know about the Stage that comes after it (via the
+ # next_stage parameter).
+ #
+ # When we call check_dependencies() or run() on the first Stage
+ # (system_checks), it will operate on the first Stage and then recursively
+ # call check_dependencies() or run() on the next_stage.
+ post_ponr_final_changes = Stage("post_ponr_final_changes", "Final modifications to the system")
+ post_ponr_conversion = Stage("post_ponr_conversion", "Starting Conversion", next_stage=post_ponr_final_changes)
+
+ try:
+ # Check dependencies are satisfied for system_checks and all subsequent
+ # Stages.
+ post_ponr_conversion.check_dependencies()
+ except DependencyError as e:
+ # We want to fail early if dependencies are not properly set. This
+ # way we should fail in testing before release.
+ logger.critical("Some dependencies were set on Actions but not present in convert2rhel: %s" % e)
+
+ # Run the Actions in system_checks and all subsequent Stages.
+ results = post_ponr_conversion.run()
+
+ return parse_action_results(results)
+
+
+def parse_action_results(results):
+ """
+ Parse and format action results
+
+ .. note::
+ The formatted dictionary result is-as following:
+ {
+ "$Action_id": {
+ "messages": [
+ {
+ "level": int,
+ "id": "$id",
+ "message": "" or "$message"
+ },
+ ]
+ "result": {
+ "level": int,
+ "id": "$id",
+ "message": "" or "$message"
+ },
+ },
+ }
+
+ :param results: Unformatted results given by the actions
+ :type results: dict
+ :return: Formatted dictionary with the results
+ :rtype: dict[str, dict[str, list | dict]]
+ """
formatted_results = {}
for action in itertools.chain(*results):
msgs = [msg.to_dict() for msg in action.messages]
diff --git a/convert2rhel/actions/report.py b/convert2rhel/actions/report.py
index 29f4c14..9b94aec 100644
--- a/convert2rhel/actions/report.py
+++ b/convert2rhel/actions/report.py
@@ -149,7 +149,7 @@ def get_combined_results_and_message(results):
return combined_results_and_message
-def summary(results, include_all_reports=False, disable_colors=False):
+def summary(results, post_conversion, include_all_reports=False, disable_colors=False):
"""Output a summary regarding the actions execution.
This summary is intended to be used to inform the user about the results
@@ -213,7 +213,11 @@ def summary(results, include_all_reports=False, disable_colors=False):
highest ones.
:type include_all_reports: bool
"""
- logger.task("Pre-conversion analysis report")
+ if post_conversion:
+ logger.task("Post-conversion report")
+ else:
+ logger.task("Pre-conversion analysis report")
+
report = []
combined_results_and_message = get_combined_results_and_message(results)
@@ -247,7 +251,10 @@ def summary(results, include_all_reports=False, disable_colors=False):
# If there is no other message sent to the user, then we just give a
# happy message to them.
if not combined_results_and_message:
- report.append("No problems detected during the analysis!")
+ if post_conversion:
+ report.append("No problems detected during the conversion!")
+ else:
+ report.append("No problems detected during the analysis!")
logger.info("%s\n" % "\n".join(report))
diff --git a/convert2rhel/main.py b/convert2rhel/main.py
index b4358ce..8262be7 100644
--- a/convert2rhel/main.py
+++ b/convert2rhel/main.py
@@ -97,6 +97,7 @@ def main_locked():
"""Perform all steps for the entire conversion process."""
pre_conversion_results = None
+ post_conversion_results = None
process_phase = ConversionPhase.POST_CLI
try:
perform_boilerplate()
@@ -109,25 +110,13 @@ def main_locked():
# actions.run_actions() (either from a bug or from the user hitting
# Ctrl-C)
process_phase = ConversionPhase.PRE_PONR_CHANGES
- pre_conversion_results = actions.run_actions()
+ pre_conversion_results = actions.run_pre_actions()
if toolopts.tool_opts.activity == "analysis":
process_phase = ConversionPhase.ANALYZE_EXIT
raise _AnalyzeExit()
- pre_conversion_failures = actions.find_actions_of_severity(
- pre_conversion_results, "SKIP", level_for_raw_action_data
- )
- if pre_conversion_failures:
- # The report will be handled in the error handler, after rollback.
- loggerinst.critical("Conversion failed.")
-
- # Print the assessment just before we ask the user whether to continue past the PONR
- report.summary(
- pre_conversion_results,
- include_all_reports=False,
- disable_colors=logger_module.should_disable_color_output(),
- )
+ _analyze_actions_results(pre_conversion_results)
loggerinst.warning("********************************************************")
loggerinst.warning("The tool allows rollback of any action until this point.")
@@ -139,20 +128,28 @@ def main_locked():
loggerinst.warning("********************************************************")
utils.ask_to_continue()
- process_phase = ConversionPhase.POST_PONR_CHANGES
- post_ponr_changes()
+ # TODO(r0x0d): Remove this once we have all functions in
+ # `py:post_ponr_changes` migrated to actions.
+ if "CONVERT2RHEL_EXPERIMENTAL_POST_PONR_ACTIONS" in os.environ:
+ process_phase = ConversionPhase.POST_PONR_CHANGES
+ post_conversion_results = actions.run_post_actions()
+
+ _analyze_actions_results(post_conversion_results)
+ else:
+ post_ponr_changes()
+
loggerinst.info("\nConversion successful!\n")
# restart system if required
utils.restart_system()
-
except _AnalyzeExit:
breadcrumbs.breadcrumbs.finish_collection(success=True)
rollback_changes()
report.summary(
- pre_conversion_results,
+ results=pre_conversion_results,
+ post_conversion=process_phase == ConversionPhase.POST_PONR_CHANGES,
include_all_reports=True,
disable_colors=logger_module.should_disable_color_output(),
)
@@ -160,21 +157,57 @@ def main_locked():
except exceptions.CriticalError as err:
loggerinst.critical_no_exit(err.diagnosis)
- return _handle_main_exceptions(process_phase, pre_conversion_results)
+ results = _pick_conversion_results(process_phase, pre_conversion_results, post_conversion_results)
+ return _handle_main_exceptions(process_phase, results)
except (Exception, SystemExit, KeyboardInterrupt) as err:
- return _handle_main_exceptions(process_phase, pre_conversion_results)
+ results = _pick_conversion_results(process_phase, pre_conversion_results, post_conversion_results)
+ return _handle_main_exceptions(process_phase, results)
finally:
+ # TODO(r0x0d): Maybe we should merge both pre_conversion_results and post_conversion_results?
+
# Write the assessment to a file as json data so that other tools can
# parse and act upon it.
- if pre_conversion_results:
- actions.report.summary_as_json(pre_conversion_results)
- actions.report.summary_as_txt(pre_conversion_results)
+ results = _pick_conversion_results(process_phase, pre_conversion_results, post_conversion_results)
+ if results:
+ actions.report.summary_as_json(results)
+ actions.report.summary_as_txt(results)
return 0
+# TODO(r0x0d): Better function name
+def _analyze_actions_results(results):
+ """Analyze the action results for failures
+
+ :param results: The action results from the framework
+ :type results: dict
+ """
+ failures = actions.find_actions_of_severity(results, "SKIP", level_for_raw_action_data)
+ if failures:
+ # The report will be handled in the error handler, after rollback.
+ loggerinst.critical("Conversion failed.")
+
+ # Print the assessment just before we ask the user whether to continue past the PONR
+ report.summary(
+ results=results,
+ include_all_reports=False,
+ disable_colors=logger_module.should_disable_color_output(),
+ )
+
+# TODO(r0x0d): Better function name
+def _pick_conversion_results(process_phase, pre_conversion, post_conversion):
+ """Utilitary function to define which action results to use
+
+ Maybe not be necessary (or even correct), but it is the best approximation
+ idea for now.
+ """
+ if process_phase == ConversionPhase.PRE_PONR_CHANGES:
+ return pre_conversion
+
+ return post_conversion
+
def _handle_main_exceptions(process_phase, pre_conversion_results=None):
"""Common steps to handle graceful exit due to several different Exception types."""
breadcrumbs.breadcrumbs.finish_collection()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment