1
0
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:
Jeremy Stretch
2020-08-20 12:47:26 -04:00
parent bf4fee1592
commit bc0e6cc8dd
6 changed files with 33 additions and 39 deletions

View File

@ -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
---

View File

@ -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):
"""

View File

@ -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()

View File

@ -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}")

View File

@ -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>

View File

@ -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">