diff --git a/docs/release-notes/version-2.9.md b/docs/release-notes/version-2.9.md index 596956f9f..cf759e3bf 100644 --- a/docs/release-notes/version-2.9.md +++ b/docs/release-notes/version-2.9.md @@ -6,6 +6,7 @@ * [#4990](https://github.com/netbox-community/netbox/issues/4990) - Restore change logging during custom script execution * [#5004](https://github.com/netbox-community/netbox/issues/5004) - Permit assignment of an interface to a LAG on any peer virtual chassis member +* [#5012](https://github.com/netbox-community/netbox/issues/5012) - Return details of exceptions resulting from report/script execution * [#5020](https://github.com/netbox-community/netbox/issues/5020) - Correct handling of dependent objects during bulk deletion --- diff --git a/netbox/extras/models/models.py b/netbox/extras/models/models.py index f3810e7c5..e57caf091 100644 --- a/netbox/extras/models/models.py +++ b/netbox/extras/models/models.py @@ -652,15 +652,13 @@ class JobResult(models.Model): def set_status(self, status): """ - Helper method to change the status of the job result and save. If the target status is terminal, the - completion time is also set. + Helper method to change the status of the job result. If the target status is terminal, the completion + time is also set. """ self.status = status if status in JobResultStatusChoices.TERMINAL_STATE_CHOICES: self.completed = timezone.now() - self.save() - @classmethod def enqueue_job(cls, func, name, obj_type, user, *args, **kwargs): """ diff --git a/netbox/extras/reports.py b/netbox/extras/reports.py index 439868dfd..64fbffb46 100644 --- a/netbox/extras/reports.py +++ b/netbox/extras/reports.py @@ -2,10 +2,10 @@ import importlib import inspect import logging import pkgutil +import traceback from collections import OrderedDict from django.conf import settings -from django.db.models import Q from django.utils import timezone from django_rq import job @@ -79,6 +79,7 @@ def run_report(job_result, *args, **kwargs): except Exception as e: print(e) job_result.set_status(JobResultStatusChoices.STATUS_ERRORED) + job_result.save() logging.error(f"Error during execution of report {job_result.name}") # Delete any previous terminal state results @@ -170,7 +171,7 @@ class Report(object): timezone.now().isoformat(), level, str(obj) if obj else None, - obj.get_absolute_url() if getattr(obj, 'get_absolute_url', None) else None, + obj.get_absolute_url() if hasattr(obj, 'get_absolute_url') else None, message, )) @@ -223,17 +224,25 @@ class Report(object): job_result.status = JobResultStatusChoices.STATUS_RUNNING job_result.save() - for method_name in self.test_methods: - self.active_test = method_name - test_method = getattr(self, method_name) - test_method() + try: - if self.failed: - self.logger.warning("Report failed") - job_result.status = JobResultStatusChoices.STATUS_FAILED - else: - self.logger.info("Report completed successfully") - job_result.status = JobResultStatusChoices.STATUS_COMPLETED + for method_name in self.test_methods: + self.active_test = method_name + test_method = getattr(self, method_name) + test_method() + + if self.failed: + self.logger.warning("Report failed") + job_result.status = JobResultStatusChoices.STATUS_FAILED + else: + self.logger.info("Report completed successfully") + job_result.status = JobResultStatusChoices.STATUS_COMPLETED + + except Exception as e: + stacktrace = traceback.format_exc() + self.log_failure(None, f"An exception occurred: {type(e).__name__}: {e}
{stacktrace}
") + logger.error(f"Exception raised during report execution: {e}") + job_result.set_status(JobResultStatusChoices.STATUS_ERRORED) job_result.data = self._results job_result.completed = timezone.now() diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index 1d7229089..9d5380655 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -446,32 +446,26 @@ def run_script(data, request, commit=True, *args, **kwargs): try: with transaction.atomic(): script.output = script.run(**kwargs) + job_result.set_status(JobResultStatusChoices.STATUS_COMPLETED) if not commit: raise AbortTransaction() except AbortTransaction: - pass + script.log_info("Database changes have been reverted automatically.") except Exception as e: stacktrace = traceback.format_exc() script.log_failure( - "An exception occurred: `{}: {}`\n```\n{}\n```".format(type(e).__name__, e, stacktrace) + f"An exception occurred: `{type(e).__name__}: {e}`\n```\n{stacktrace}\n```" ) + script.log_info("Database changes have been reverted due to error.") logger.error(f"Exception raised during script execution: {e}") - commit = False job_result.set_status(JobResultStatusChoices.STATUS_ERRORED) finally: - if job_result.status != JobResultStatusChoices.STATUS_ERRORED: - job_result.data = ScriptOutputSerializer(script).data - job_result.set_status(JobResultStatusChoices.STATUS_COMPLETED) - - if not commit: - # Delete all pending changelog entries - script.log_info( - "Database changes have been reverted automatically." - ) + job_result.data = ScriptOutputSerializer(script).data + job_result.save() logger.info(f"Script completed in {job_result.duration}") diff --git a/netbox/templates/extras/report_result.html b/netbox/templates/extras/report_result.html index e1c46fe90..80715f2aa 100644 --- a/netbox/templates/extras/report_result.html +++ b/netbox/templates/extras/report_result.html @@ -16,7 +16,7 @@ {% endif %} {% include 'extras/inc/job_label.html' with result=result %}

- {% if result.completed and result.status != 'errored' %} + {% if result.completed %}
Report Methods @@ -75,10 +75,8 @@
- {% elif result.status == 'errored' %} -
Error during report execution
{% else %} -
Pending results
+
Pending results
{% endif %}
diff --git a/netbox/templates/extras/script_result.html b/netbox/templates/extras/script_result.html index 88013e05e..c9fc348ee 100644 --- a/netbox/templates/extras/script_result.html +++ b/netbox/templates/extras/script_result.html @@ -41,7 +41,7 @@ {% include 'extras/inc/job_label.html' with result=result %}

- {% if result.completed and result.status != 'errored' %} + {% if result.completed %}
@@ -76,12 +76,6 @@
- {% elif result.stats == 'errored' %} -
-
-
Error during script execution
-
-
{% else %}