Browse Source

Fixes #5012: Return details of exceptions resulting from report/script execution

Jeremy Stretch 5 years ago
parent
commit
bc0e6cc8dd

+ 1 - 0
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
 
 ---

+ 2 - 4
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):
         """

+ 22 - 13
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()
-
-        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
+        try:
+
+            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} <pre>{stacktrace}</pre>")
+            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()

+ 6 - 12
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}")
 

+ 2 - 4
netbox/templates/extras/report_result.html

@@ -16,7 +16,7 @@
                 {% endif %}
                 <span id="pending-result-label">{% include 'extras/inc/job_label.html' with result=result %}</span>
             </p>
-            {% if result.completed and result.status != 'errored' %}
+            {% if result.completed %}
                 <div class="panel panel-default">
                     <div class="panel-heading">
                         <strong>Report Methods</strong>
@@ -75,10 +75,8 @@
                         </tbody>
                     </table>
                 </div>
-            {% elif result.status == 'errored' %}
-                <div class="well">Error during report execution</div>
             {% else %}
-            <div class="well">Pending results</div>
+                <div class="well">Pending results</div>
             {% endif %}
         </div>
     </div>

+ 1 - 7
netbox/templates/extras/script_result.html

@@ -41,7 +41,7 @@
             <span id="pending-result-label">{% include 'extras/inc/job_label.html' with result=result %}</span>
         </p>
         <div role="tabpanel" class="tab-pane active" id="log">
-            {% if result.completed and result.status != 'errored' %}
+            {% if result.completed %}
                 <div class="row">
                     <div class="col-md-12">
                         <div class="panel panel-default">
@@ -76,12 +76,6 @@
                         </div>
                     </div>
                 </div>
-            {% elif result.stats == 'errored' %}
-                <div class="row">
-                    <div class="col-md-12">
-                        <div class="well">Error during script execution</div>
-                    </div>
-                </div>
             {% else %}
                 <div class="row">
                     <div class="col-md-12">