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:
committed by
Jeremy Stretch
parent
a8c331f88a
commit
4de64d783e
@ -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 = [
|
||||||
{
|
{
|
||||||
|
@ -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.
|
||||||
"""
|
"""
|
||||||
|
@ -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 = {
|
||||||
|
Reference in New Issue
Block a user