diff --git a/netbox/core/api/serializers.py b/netbox/core/api/serializers.py index 0d743d952..4ae426df5 100644 --- a/netbox/core/api/serializers.py +++ b/netbox/core/api/serializers.py @@ -69,5 +69,5 @@ class JobSerializer(BaseModelSerializer): model = Job fields = [ 'id', 'url', 'display', 'object_type', 'object_id', 'name', 'status', 'created', 'scheduled', 'interval', - 'started', 'completed', 'user', 'data', 'job_id', + 'started', 'completed', 'user', 'data', 'error', 'job_id', ] diff --git a/netbox/core/jobs.py b/netbox/core/jobs.py index d25981920..32b546b20 100644 --- a/netbox/core/jobs.py +++ b/netbox/core/jobs.py @@ -25,7 +25,7 @@ def sync_datasource(job, *args, **kwargs): job.terminate() except Exception as e: - job.terminate(status=JobStatusChoices.STATUS_ERRORED) + job.terminate(status=JobStatusChoices.STATUS_ERRORED, error=str(e)) DataSource.objects.filter(pk=datasource.pk).update(status=DataSourceStatusChoices.FAILED) if type(e) in (SyncError, JobTimeoutException): logging.error(e) diff --git a/netbox/core/migrations/0006_job_add_error_field.py b/netbox/core/migrations/0006_job_add_error_field.py new file mode 100644 index 000000000..2927db4c4 --- /dev/null +++ b/netbox/core/migrations/0006_job_add_error_field.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.6 on 2023-10-23 20:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0005_job_created_auto_now'), + ] + + operations = [ + migrations.AddField( + model_name='job', + name='error', + field=models.TextField(blank=True, editable=False), + ), + ] diff --git a/netbox/core/models/jobs.py b/netbox/core/models/jobs.py index 61b0e64fa..4e9a93bfb 100644 --- a/netbox/core/models/jobs.py +++ b/netbox/core/models/jobs.py @@ -92,6 +92,11 @@ class Job(models.Model): null=True, blank=True ) + error = models.TextField( + verbose_name=_('error'), + editable=False, + blank=True + ) job_id = models.UUIDField( verbose_name=_('job ID'), unique=True @@ -158,7 +163,7 @@ class Job(models.Model): # Handle webhooks self.trigger_webhooks(event=EVENT_JOB_START) - def terminate(self, status=JobStatusChoices.STATUS_COMPLETED): + def terminate(self, status=JobStatusChoices.STATUS_COMPLETED, error=None): """ Mark the job as completed, optionally specifying a particular termination status. """ @@ -168,6 +173,8 @@ class Job(models.Model): # Mark the job as completed self.status = status + if error: + self.error = error self.completed = timezone.now() self.save() diff --git a/netbox/core/tables/jobs.py b/netbox/core/tables/jobs.py index 32ca67f7f..3388aee19 100644 --- a/netbox/core/tables/jobs.py +++ b/netbox/core/tables/jobs.py @@ -47,7 +47,7 @@ class JobTable(NetBoxTable): model = Job fields = ( 'pk', 'id', 'object_type', 'object', 'name', 'status', 'created', 'scheduled', 'interval', 'started', - 'completed', 'user', 'job_id', + 'completed', 'user', 'error', 'job_id', ) default_columns = ( 'pk', 'id', 'object_type', 'object', 'name', 'status', 'created', 'started', 'completed', 'user', diff --git a/netbox/extras/management/commands/runscript.py b/netbox/extras/management/commands/runscript.py index d9a9f41ae..3cf70281c 100644 --- a/netbox/extras/management/commands/runscript.py +++ b/netbox/extras/management/commands/runscript.py @@ -59,7 +59,7 @@ class Command(BaseCommand): logger.error(f"Exception raised during script execution: {e}") clear_webhooks.send(request) job.data = ScriptOutputSerializer(script).data - job.terminate(status=JobStatusChoices.STATUS_ERRORED) + job.terminate(status=JobStatusChoices.STATUS_ERRORED, error=str(e)) logger.info(f"Script completed in {job.duration}") diff --git a/netbox/extras/reports.py b/netbox/extras/reports.py index cc279a49a..c8a13fe15 100644 --- a/netbox/extras/reports.py +++ b/netbox/extras/reports.py @@ -40,8 +40,8 @@ def run_report(job, *args, **kwargs): try: report.run(job) - except Exception: - job.terminate(status=JobStatusChoices.STATUS_ERRORED) + except Exception as e: + job.terminate(status=JobStatusChoices.STATUS_ERRORED, error=str(e)) logging.error(f"Error during execution of report {job.name}") finally: # Schedule the next job if an interval has been set @@ -230,7 +230,7 @@ class Report(object): 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.terminate(status=JobStatusChoices.STATUS_ERRORED) + job.terminate(status=JobStatusChoices.STATUS_ERRORED, error=str(e)) # Perform any post-run tasks self.post_run() diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py index e93326ddc..df75200e6 100644 --- a/netbox/extras/scripts.py +++ b/netbox/extras/scripts.py @@ -519,7 +519,7 @@ def run_script(data, request, job, commit=True, **kwargs): logger.error(f"Exception raised during script execution: {e}") script.log_info("Database changes have been reverted due to error.") job.data = ScriptOutputSerializer(script).data - job.terminate(status=JobStatusChoices.STATUS_ERRORED) + job.terminate(status=JobStatusChoices.STATUS_ERRORED, error=str(e)) clear_webhooks.send(request) logger.info(f"Script completed in {job.duration}") diff --git a/netbox/templates/core/job.html b/netbox/templates/core/job.html index 1fe3862cd..deb651739 100644 --- a/netbox/templates/core/job.html +++ b/netbox/templates/core/job.html @@ -35,6 +35,12 @@