Przeglądaj źródła

Add trigger_webhooks() to JobResult

jeremystretch 3 lat temu
rodzic
commit
4de64d783e

+ 9 - 1
netbox/extras/constants.py

@@ -1,8 +1,16 @@
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType
 
 
-# Webhook content types
+# Webhooks
 HTTP_CONTENT_TYPE_JSON = 'application/json'
 HTTP_CONTENT_TYPE_JSON = 'application/json'
 
 
+WEBHOOK_EVENT_TYPES = {
+    'create': 'created',
+    'update': 'updated',
+    'delete': 'deleted',
+    'job_start': 'job_started',
+    'job_end': 'job_ended',
+}
+
 # Dashboard
 # Dashboard
 DEFAULT_DASHBOARD = [
 DEFAULT_DASHBOARD = [
     {
     {

+ 40 - 7
netbox/extras/models/models.py

@@ -26,7 +26,7 @@ from netbox.constants import RQ_QUEUE_DEFAULT
 from netbox.models import ChangeLoggedModel
 from netbox.models import ChangeLoggedModel
 from netbox.models.features import (
 from netbox.models.features import (
     CloningMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, JobResultsMixin, SyncedDataMixin,
     CloningMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, JobResultsMixin, SyncedDataMixin,
-    TagsMixin,
+    TagsMixin, WebhooksMixin,
 )
 )
 from utilities.querysets import RestrictedQuerySet
 from utilities.querysets import RestrictedQuerySet
 from utilities.utils import render_jinja2
 from utilities.utils import render_jinja2
@@ -689,10 +689,16 @@ class JobResult(models.Model):
         """
         """
         Record the job's start time and update its status to "running."
         Record the job's start time and update its status to "running."
         """
         """
-        if self.started is None:
-            self.started = timezone.now()
-            self.status = JobResultStatusChoices.STATUS_RUNNING
-            JobResult.objects.filter(pk=self.pk).update(started=self.started, status=self.status)
+        if self.started is not None:
+            return
+
+        # Start the job
+        self.started = timezone.now()
+        self.status = JobResultStatusChoices.STATUS_RUNNING
+        JobResult.objects.filter(pk=self.pk).update(started=self.started, status=self.status)
+
+        # Handle webhooks
+        self.trigger_webhooks(event='job_start')
 
 
     def terminate(self, status=JobResultStatusChoices.STATUS_COMPLETED):
     def terminate(self, status=JobResultStatusChoices.STATUS_COMPLETED):
         """
         """
@@ -701,10 +707,15 @@ class JobResult(models.Model):
         valid_statuses = JobResultStatusChoices.TERMINAL_STATE_CHOICES
         valid_statuses = JobResultStatusChoices.TERMINAL_STATE_CHOICES
         if status not in valid_statuses:
         if status not in valid_statuses:
             raise ValueError(f"Invalid status for job termination. Choices are: {', '.join(valid_statuses)}")
             raise ValueError(f"Invalid status for job termination. Choices are: {', '.join(valid_statuses)}")
+
+        # Mark the job as completed
         self.status = status
         self.status = status
         self.completed = timezone.now()
         self.completed = timezone.now()
         JobResult.objects.filter(pk=self.pk).update(status=self.status, completed=self.completed)
         JobResult.objects.filter(pk=self.pk).update(status=self.status, completed=self.completed)
 
 
+        # Handle webhooks
+        self.trigger_webhooks(event='job_end')
+
     @classmethod
     @classmethod
     def enqueue_job(cls, func, name, obj_type, user, schedule_at=None, interval=None, *args, **kwargs):
     def enqueue_job(cls, func, name, obj_type, user, schedule_at=None, interval=None, *args, **kwargs):
         """
         """
@@ -738,6 +749,28 @@ class JobResult(models.Model):
 
 
         return job_result
         return job_result
 
 
+    def trigger_webhooks(self, event):
+        rq_queue_name = get_config().QUEUE_MAPPINGS.get('webhook', RQ_QUEUE_DEFAULT)
+        rq_queue = django_rq.get_queue(rq_queue_name, is_async=False)
+
+        # Fetch any webhooks matching this object type and action
+        webhooks = Webhook.objects.filter(
+            **{f'type_{event}': True},
+            content_types=self.obj_type,
+            enabled=True
+        )
+
+        for webhook in webhooks:
+            rq_queue.enqueue(
+                "extras.webhooks_worker.process_webhook",
+                webhook=webhook,
+                model_name=self.obj_type.model,
+                event=event,
+                data=self.data,
+                timestamp=str(timezone.now()),
+                username=self.user.username
+            )
+
 
 
 class ConfigRevision(models.Model):
 class ConfigRevision(models.Model):
     """
     """
@@ -780,7 +813,7 @@ class ConfigRevision(models.Model):
 # Custom scripts & reports
 # Custom scripts & reports
 #
 #
 
 
-class Script(JobResultsMixin, models.Model):
+class Script(JobResultsMixin, WebhooksMixin, models.Model):
     """
     """
     Dummy model used to generate permissions for custom scripts. Does not exist in the database.
     Dummy model used to generate permissions for custom scripts. Does not exist in the database.
     """
     """
@@ -792,7 +825,7 @@ class Script(JobResultsMixin, models.Model):
 # Reports
 # Reports
 #
 #
 
 
-class Report(JobResultsMixin, models.Model):
+class Report(JobResultsMixin, WebhooksMixin, models.Model):
     """
     """
     Dummy model used to generate permissions for reports. Does not exist in the database.
     Dummy model used to generate permissions for reports. Does not exist in the database.
     """
     """

+ 7 - 4
netbox/extras/webhooks_worker.py

@@ -5,8 +5,8 @@ from django.conf import settings
 from django_rq import job
 from django_rq import job
 from jinja2.exceptions import TemplateError
 from jinja2.exceptions import TemplateError
 
 
-from .choices import ObjectChangeActionChoices
 from .conditions import ConditionSet
 from .conditions import ConditionSet
+from .constants import WEBHOOK_EVENT_TYPES
 from .webhooks import generate_signature
 from .webhooks import generate_signature
 
 
 logger = logging.getLogger('netbox.webhooks_worker')
 logger = logging.getLogger('netbox.webhooks_worker')
@@ -28,7 +28,7 @@ def eval_conditions(webhook, data):
 
 
 
 
 @job('default')
 @job('default')
-def process_webhook(webhook, model_name, event, data, snapshots, timestamp, username, request_id):
+def process_webhook(webhook, model_name, event, data, timestamp, username, request_id=None, snapshots=None):
     """
     """
     Make a POST request to the defined Webhook
     Make a POST request to the defined Webhook
     """
     """
@@ -38,14 +38,17 @@ def process_webhook(webhook, model_name, event, data, snapshots, timestamp, user
 
 
     # Prepare context data for headers & body templates
     # Prepare context data for headers & body templates
     context = {
     context = {
-        'event': dict(ObjectChangeActionChoices)[event].lower(),
+        'event': WEBHOOK_EVENT_TYPES[event],
         'timestamp': timestamp,
         'timestamp': timestamp,
         'model': model_name,
         'model': model_name,
         'username': username,
         'username': username,
         'request_id': request_id,
         'request_id': request_id,
         'data': data,
         'data': data,
-        'snapshots': snapshots,
     }
     }
+    if snapshots:
+        context.update({
+            'snapshots': snapshots
+        })
 
 
     # Build the headers for the HTTP request
     # Build the headers for the HTTP request
     headers = {
     headers = {