From f60ac48ec8279334b4b460cf47cb0ad02a5454bd Mon Sep 17 00:00:00 2001 From: William McBroom Date: Wed, 3 Apr 2024 15:46:04 -0500 Subject: [PATCH] Add CallbackMethod to allow setting custom methods per job --- chroniker/admin.py | 16 +++++++- .../migrations/0004_auto_20240403_1154.py | 36 +++++++++++++++++ chroniker/models.py | 40 +++++++++++++++++-- 3 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 chroniker/migrations/0004_auto_20240403_1154.py diff --git a/chroniker/admin.py b/chroniker/admin.py index 7d7a8b7..75f94d3 100644 --- a/chroniker/admin.py +++ b/chroniker/admin.py @@ -16,7 +16,7 @@ from django.utils.text import capfirst from django.utils.translation import gettext_lazy as _ -from chroniker.models import Job, Log, JobDependency, Monitor +from chroniker.models import Job, Log, JobDependency, Monitor, CallbackMethod from chroniker import utils from chroniker.widgets import ImproveRawIdFieldsFormTabularInline @@ -25,6 +25,10 @@ except ImportError: ApproxCountQuerySet = None +class CallbackMethodAdmin(admin.ModelAdmin): + list_display = ('name', 'reference') + +admin.site.register(CallbackMethod, CallbackMethodAdmin) class JobDependencyInline(ImproveRawIdFieldsFormTabularInline): model = JobDependency @@ -106,7 +110,7 @@ def job_type(self, obj=''): 'hostname', 'is_monitor', ) - filter_horizontal = ('subscribers',) + filter_horizontal = ('subscribers','callbacks') fieldsets = ( ('Job Details', { 'classes': ('wide',), @@ -173,6 +177,14 @@ def job_type(self, obj=''): 'email_success_to_subscribers', ) }), + ('Callbacks', { + 'classes': ('wide',), + 'fields': ( + 'callbacks', + 'callback_errors_to_subscribers', + 'callback_success_to_subscribers', + ) + }), ('Frequency options', { 'classes': ('wide',), 'fields': ( diff --git a/chroniker/migrations/0004_auto_20240403_1154.py b/chroniker/migrations/0004_auto_20240403_1154.py new file mode 100644 index 0000000..eedea06 --- /dev/null +++ b/chroniker/migrations/0004_auto_20240403_1154.py @@ -0,0 +1,36 @@ +# Generated by Django 3.2.23 on 2024-04-03 16:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('chroniker', '0003_auto_20200822_2026'), + ] + + operations = [ + migrations.CreateModel( + name='CallbackMethod', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200, verbose_name='name')), + ('reference', models.CharField(max_length=200, verbose_name='reference')), + ], + ), + migrations.AddField( + model_name='job', + name='callback_errors_to_subscribers', + field=models.BooleanField(default=True, help_text='If checked, the stdout and stderr of a job will be sent to the callback if an error occur.'), + ), + migrations.AddField( + model_name='job', + name='callback_success_to_subscribers', + field=models.BooleanField(default=False, help_text='If checked, the stdout of a job will be sent to the callback if not errors occur.'), + ), + migrations.AddField( + model_name='job', + name='callbacks', + field=models.ManyToManyField(blank=True, related_name='callbacked_jobs', to='chroniker.CallbackMethod'), + ), + ] diff --git a/chroniker/models.py b/chroniker/models.py index 214cca5..fbf0ebe 100644 --- a/chroniker/models.py +++ b/chroniker/models.py @@ -407,6 +407,15 @@ def create_log(job): create_log(job) #transaction.commit() +class CallbackMethod(models.Model): + + def __str__(self): + return self.name + + objects = JobManager() + + name = models.CharField(_("name"), max_length=200) + reference = models.CharField(_("reference"), max_length=200) class Job(models.Model): """ @@ -464,6 +473,18 @@ class Job(models.Model): last_run_successful = models.BooleanField(_('success'), blank=True, null=True, editable=False) + callbacks = models.ManyToManyField(CallbackMethod, related_name='callbacked_jobs', blank=True) + + callback_errors_to_subscribers = models.BooleanField( + default=True, + help_text=_('If checked, the stdout and stderr of a job will ' + \ + 'be sent to the callback if an error occur.')) + + callback_success_to_subscribers = models.BooleanField( + default=False, + help_text=_('If checked, the stdout of a job will ' + \ + 'be sent to the callback if not errors occur.')) + subscribers = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='subscribed_jobs', blank=True, limit_choices_to={'is_staff': True}) email_errors_to_subscribers = models.BooleanField( @@ -1119,11 +1140,24 @@ def handle_run(self, update_heartbeat=True, stdout_queue=None, stderr_queue=None print('Error emailing subscribers: %s' % e, file=sys.stderr) traceback.print_exc() + # Call successful callback. + try: + if self.callback_success_to_subscribers: + if last_run_successful and job.callbacks.all(): + for callback in job.callbacks.all(): + cb = import_string(callback.reference) + cb(self, stdout=stdout_str, stderr=stderr_str) + except Exception as e: + print('Error executing callback: %s' % e, file=sys.stderr) + traceback.print_exc() + # Call error callback. try: - if not last_run_successful and _settings.CHRONIKER_JOB_ERROR_CALLBACK: - cb = import_string(_settings.CHRONIKER_JOB_ERROR_CALLBACK) - cb(self, stdout=stdout_str, stderr=stderr_str) + if self.callback_errors_to_subscribers: + if not last_run_successful and job.callbacks.all(): + for callback in job.callbacks.all(): + cb = import_string(callback.reference) + cb(self, stdout=stdout_str, stderr=stderr_str) except Exception as e: print('Error executing callback: %s' % e, file=sys.stderr) traceback.print_exc()