From bc0e6cc8dd12ded382c5e9931fb0b63030a47e90 Mon Sep 17 00:00:00 2001
From: Jeremy Stretch
Date: Thu, 20 Aug 2020 12:47:26 -0400
Subject: [PATCH] Fixes #5012: Return details of exceptions resulting from
report/script execution
---
docs/release-notes/version-2.9.md | 1 +
netbox/extras/models/models.py | 6 ++--
netbox/extras/reports.py | 33 ++++++++++++++--------
netbox/extras/scripts.py | 18 ++++--------
netbox/templates/extras/report_result.html | 6 ++--
netbox/templates/extras/script_result.html | 8 +-----
6 files changed, 33 insertions(+), 39 deletions(-)
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 %}
- {% elif result.stats == 'errored' %}
-
-
-
Error during script execution
-
-
{% else %}