1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Add trigger_webhooks() to JobResult

This commit is contained in:
jeremystretch
2023-02-28 16:21:01 -05:00
committed by Jeremy Stretch
parent a8c331f88a
commit 4de64d783e
3 changed files with 56 additions and 12 deletions

View File

@ -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 = [
{ {

View File

@ -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: if self.started is not None:
self.started = timezone.now() return
self.status = JobResultStatusChoices.STATUS_RUNNING
JobResult.objects.filter(pk=self.pk).update(started=self.started, status=self.status) # 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.
""" """

View File

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