ソースを参照

Closes #14365: Introduce job_start and job_end signals (#14393)

* Introduce job_start and job_end signals, and receivers to process event rules

* Complete signals documentation
Jeremy Stretch 2 年 前
コミット
4fc0a999ea
4 ファイル変更61 行追加20 行削除
  1. 24 0
      docs/development/signals.md
  2. 5 20
      netbox/core/models/jobs.py
  3. 6 0
      netbox/core/signals.py
  4. 26 0
      netbox/extras/signals.py

+ 24 - 0
docs/development/signals.md

@@ -9,3 +9,27 @@ This signal is sent by models which inherit from `CustomValidationMixin` at the
 ### Receivers
 
 * `extras.signals.run_custom_validators()`
+
+## core.job_start
+
+This signal is sent whenever a [background job](../features/background-jobs.md) is started.
+
+### Receivers
+
+* `extras.signals.process_job_start_event_rules()`
+
+## core.job_end
+
+This signal is sent whenever a [background job](../features/background-jobs.md) is terminated.
+
+### Receivers
+
+* `extras.signals.process_job_end_event_rules()`
+
+## core.pre_sync
+
+This signal is sent when the [DataSource](../models/core/datasource.md) model's `sync()` method is called.
+
+## core.post_sync
+
+This signal is sent when a [DataSource](../models/core/datasource.md) finishes synchronizing.

+ 5 - 20
netbox/core/models/jobs.py

@@ -12,6 +12,7 @@ from django.utils.translation import gettext as _
 
 from core.choices import JobStatusChoices
 from core.models import ContentType
+from core.signals import job_end, job_start
 from extras.constants import EVENT_JOB_END, EVENT_JOB_START
 from netbox.config import get_config
 from netbox.constants import RQ_QUEUE_DEFAULT
@@ -168,8 +169,8 @@ class Job(models.Model):
         self.status = JobStatusChoices.STATUS_RUNNING
         self.save()
 
-        # Handle events
-        self.process_event(event=EVENT_JOB_START)
+        # Send signal
+        job_start.send(self)
 
     def terminate(self, status=JobStatusChoices.STATUS_COMPLETED, error=None):
         """
@@ -186,8 +187,8 @@ class Job(models.Model):
         self.completed = timezone.now()
         self.save()
 
-        # Handle events
-        self.process_event(event=EVENT_JOB_END)
+        # Send signal
+        job_end.send(self)
 
     @classmethod
     def enqueue(cls, func, instance, name='', user=None, schedule_at=None, interval=None, **kwargs):
@@ -223,19 +224,3 @@ class Job(models.Model):
             queue.enqueue(func, job_id=str(job.job_id), job=job, **kwargs)
 
         return job
-
-    def process_event(self, event):
-        """
-        Process any EventRules relevant to the passed job event (i.e. start or stop).
-        """
-        from extras.models import EventRule
-        from extras.events import process_event_rules
-
-        # Fetch any event rules matching this object type and action
-        event_rules = EventRule.objects.filter(
-            **{f'type_{event}': True},
-            content_types=self.object_type,
-            enabled=True
-        )
-
-        process_event_rules(event_rules, self.object_type.model, event, self.data, self.user.username)

+ 6 - 0
netbox/core/signals.py

@@ -4,10 +4,16 @@ from django.dispatch import Signal, receiver
 from .models import ConfigRevision
 
 __all__ = (
+    'job_end',
+    'job_start',
     'post_sync',
     'pre_sync',
 )
 
+# Job signals
+job_start = Signal()
+job_end = Signal()
+
 # DataSource signals
 pre_sync = Signal()
 post_sync = Signal()

+ 26 - 0
netbox/extras/signals.py

@@ -8,6 +8,10 @@ from django.dispatch import receiver, Signal
 from django.utils.translation import gettext_lazy as _
 from django_prometheus.models import model_deletes, model_inserts, model_updates
 
+from core.signals import job_end, job_start
+from extras.constants import EVENT_JOB_END, EVENT_JOB_START
+from extras.events import process_event_rules
+from extras.models import EventRule
 from extras.validators import CustomValidator
 from netbox.config import get_config
 from netbox.context import current_request, events_queue
@@ -235,3 +239,25 @@ def validate_assigned_tags(sender, instance, action, model, pk_set, **kwargs):
     for tag in model.objects.filter(pk__in=pk_set, object_types__isnull=False).prefetch_related('object_types'):
         if ct not in tag.object_types.all():
             raise AbortRequest(f"Tag {tag} cannot be assigned to {ct.model} objects.")
+
+
+#
+# Event rules
+#
+
+@receiver(job_start)
+def process_job_start_event_rules(sender, **kwargs):
+    """
+    Process event rules for jobs starting.
+    """
+    event_rules = EventRule.objects.filter(type_job_start=True, enabled=True, content_types=sender.object_type)
+    process_event_rules(event_rules, sender.object_type.model, EVENT_JOB_START, sender.data, sender.user.username)
+
+
+@receiver(job_end)
+def process_job_end_event_rules(sender, **kwargs):
+    """
+    Process event rules for jobs terminating.
+    """
+    event_rules = EventRule.objects.filter(type_job_end=True, enabled=True, content_types=sender.object_type)
+    process_event_rules(event_rules, sender.object_type.model, EVENT_JOB_END, sender.data, sender.user.username)