mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Fixes #5012: Return details of exceptions resulting from report/script execution
This commit is contained in:
@ -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
|
||||
|
||||
---
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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} <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()
|
||||
|
@ -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}")
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
Reference in New Issue
Block a user