mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Refactor of webhook signaling system to use the same middleware mechanics of Changelogging
This commit is contained in:
committed by
Jeremy Stretch
parent
9876a2efcd
commit
722d0d5554
@ -9,8 +9,3 @@ class CircuitsConfig(AppConfig):
|
|||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
import circuits.signals
|
import circuits.signals
|
||||||
|
|
||||||
# register webhook signals
|
|
||||||
from extras.webhooks import register_signals
|
|
||||||
from .models import Circuit, Provider
|
|
||||||
register_signals([Circuit, Provider])
|
|
||||||
|
@ -60,7 +60,6 @@ class Provider(ChangeLoggedModel, CustomFieldModel):
|
|||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'circuits.api.serializers.ProviderSerializer'
|
|
||||||
csv_headers = ['name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments']
|
csv_headers = ['name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -99,7 +98,6 @@ class CircuitType(ChangeLoggedModel):
|
|||||||
unique=True
|
unique=True
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'circuits.api.serializers.CircuitTypeSerializer'
|
|
||||||
csv_headers = ['name', 'slug']
|
csv_headers = ['name', 'slug']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -174,7 +172,6 @@ class Circuit(ChangeLoggedModel, CustomFieldModel):
|
|||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'circuits.api.serializers.CircuitSerializer'
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
|
'cid', 'provider', 'type', 'status', 'tenant', 'install_date', 'commit_rate', 'description', 'comments',
|
||||||
]
|
]
|
||||||
|
@ -10,8 +10,3 @@ class DCIMConfig(AppConfig):
|
|||||||
def ready(self):
|
def ready(self):
|
||||||
|
|
||||||
import dcim.signals
|
import dcim.signals
|
||||||
|
|
||||||
# register webhook signals
|
|
||||||
from extras.webhooks import register_signals
|
|
||||||
from .models import Site, Rack, RackGroup, Device, Interface
|
|
||||||
register_signals([Site, Rack, Device, Interface, RackGroup])
|
|
||||||
|
@ -79,7 +79,6 @@ class Region(MPTTModel, ChangeLoggedModel):
|
|||||||
unique=True
|
unique=True
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'dcim.api.serializers.RegionSerializer'
|
|
||||||
csv_headers = ['name', 'slug', 'parent']
|
csv_headers = ['name', 'slug', 'parent']
|
||||||
|
|
||||||
class MPTTMeta:
|
class MPTTMeta:
|
||||||
@ -201,7 +200,6 @@ class Site(ChangeLoggedModel, CustomFieldModel):
|
|||||||
objects = SiteManager()
|
objects = SiteManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'dcim.api.serializers.SiteSerializer'
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description', 'physical_address',
|
'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description', 'physical_address',
|
||||||
'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone', 'contact_email', 'comments',
|
'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone', 'contact_email', 'comments',
|
||||||
@ -287,7 +285,6 @@ class RackGroup(ChangeLoggedModel):
|
|||||||
related_name='rack_groups'
|
related_name='rack_groups'
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'dcim.api.serializers.RackGroupSerializer'
|
|
||||||
csv_headers = ['site', 'name', 'slug']
|
csv_headers = ['site', 'name', 'slug']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -325,7 +322,6 @@ class RackRole(ChangeLoggedModel):
|
|||||||
)
|
)
|
||||||
color = ColorField()
|
color = ColorField()
|
||||||
|
|
||||||
serializer = 'dcim.api.serializers.RackRoleSerializer'
|
|
||||||
csv_headers = ['name', 'slug', 'color']
|
csv_headers = ['name', 'slug', 'color']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -432,7 +428,6 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
|
|||||||
objects = RackManager()
|
objects = RackManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'dcim.api.serializers.RackSerializer'
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'site', 'group_name', 'name', 'facility_id', 'tenant', 'role', 'type', 'serial', 'width', 'u_height',
|
'site', 'group_name', 'name', 'facility_id', 'tenant', 'role', 'type', 'serial', 'width', 'u_height',
|
||||||
'desc_units', 'comments',
|
'desc_units', 'comments',
|
||||||
@ -636,8 +631,6 @@ class RackReservation(ChangeLoggedModel):
|
|||||||
max_length=100
|
max_length=100
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'dcim.api.serializers.RackReservationSerializer'
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['created']
|
ordering = ['created']
|
||||||
|
|
||||||
@ -697,7 +690,6 @@ class Manufacturer(ChangeLoggedModel):
|
|||||||
unique=True
|
unique=True
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'dcim.api.serializers.ManufacturerSerializer'
|
|
||||||
csv_headers = ['name', 'slug']
|
csv_headers = ['name', 'slug']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -792,7 +784,6 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
|
|||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'dcim.api.serializers.DeviceTypeSerializer'
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'is_console_server',
|
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'is_console_server',
|
||||||
'is_pdu', 'is_network_device', 'subdevice_role', 'interface_ordering', 'comments',
|
'is_pdu', 'is_network_device', 'subdevice_role', 'interface_ordering', 'comments',
|
||||||
@ -1076,7 +1067,6 @@ class DeviceRole(ChangeLoggedModel):
|
|||||||
help_text='Virtual machines may be assigned to this role'
|
help_text='Virtual machines may be assigned to this role'
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'dcim.api.serializers.DeviceRoleSerializer'
|
|
||||||
csv_headers = ['name', 'slug', 'color', 'vm_role']
|
csv_headers = ['name', 'slug', 'color', 'vm_role']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -1135,7 +1125,6 @@ class Platform(ChangeLoggedModel):
|
|||||||
verbose_name='Legacy RPC client'
|
verbose_name='Legacy RPC client'
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'dcim.api.serializers.PlatformSerializer'
|
|
||||||
csv_headers = ['name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args']
|
csv_headers = ['name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -1302,7 +1291,6 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
objects = DeviceManager()
|
objects = DeviceManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'dcim.api.serializers.DeviceSerializer'
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', 'status',
|
'name', 'device_role', 'tenant', 'manufacturer', 'model_name', 'platform', 'serial', 'asset_tag', 'status',
|
||||||
'site', 'rack_group', 'rack_name', 'position', 'face', 'comments',
|
'site', 'rack_group', 'rack_name', 'position', 'face', 'comments',
|
||||||
@ -1858,8 +1846,6 @@ class Interface(ComponentModel):
|
|||||||
objects = InterfaceQuerySet.as_manager()
|
objects = InterfaceQuerySet.as_manager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'dcim.api.serializers.InterfaceSerializer'
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['device', 'name']
|
ordering = ['device', 'name']
|
||||||
unique_together = ['device', 'name']
|
unique_together = ['device', 'name']
|
||||||
@ -2263,7 +2249,6 @@ class VirtualChassis(ChangeLoggedModel):
|
|||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'dcim.api.serializers.VirtualChassisSerializer'
|
|
||||||
csv_headers = ['master', 'domain']
|
csv_headers = ['master', 'domain']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -9,7 +9,11 @@ from django.conf import settings
|
|||||||
from django.db.models.signals import post_delete, post_save
|
from django.db.models.signals import post_delete, post_save
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from .constants import OBJECTCHANGE_ACTION_CREATE, OBJECTCHANGE_ACTION_DELETE, OBJECTCHANGE_ACTION_UPDATE
|
from extras.webhooks import enqueue_webhooks
|
||||||
|
from .constants import (
|
||||||
|
OBJECTCHANGE_ACTION_CREATE, OBJECTCHANGE_ACTION_DELETE, OBJECTCHANGE_ACTION_UPDATE,
|
||||||
|
WEBHOOK_MODELS
|
||||||
|
)
|
||||||
from .models import ObjectChange
|
from .models import ObjectChange
|
||||||
|
|
||||||
|
|
||||||
@ -18,12 +22,10 @@ _thread_locals = threading.local()
|
|||||||
|
|
||||||
def mark_object_changed(instance, **kwargs):
|
def mark_object_changed(instance, **kwargs):
|
||||||
"""
|
"""
|
||||||
Mark an object as having been created, saved, or updated. At the end of the request, this change will be recorded.
|
Mark an object as having been created, saved, or updated. At the end of the request, this change will be recorded
|
||||||
We have to wait until the *end* of the request to the serialize the object, because related fields like tags and
|
and/or associated webhooks fired. We have to wait until the *end* of the request to the serialize the object,
|
||||||
custom fields have not yet been updated when the post_save signal is emitted.
|
because related fields like tags and custom fields have not yet been updated when the post_save signal is emitted.
|
||||||
"""
|
"""
|
||||||
if not hasattr(instance, 'log_change'):
|
|
||||||
return
|
|
||||||
|
|
||||||
# Determine what action is being performed. The post_save signal sends a `created` boolean, whereas post_delete
|
# Determine what action is being performed. The post_save signal sends a `created` boolean, whereas post_delete
|
||||||
# does not.
|
# does not.
|
||||||
@ -35,7 +37,12 @@ def mark_object_changed(instance, **kwargs):
|
|||||||
_thread_locals.changed_objects.append((instance, action))
|
_thread_locals.changed_objects.append((instance, action))
|
||||||
|
|
||||||
|
|
||||||
class ChangeLoggingMiddleware(object):
|
class ObjectChangeMiddleware(object):
|
||||||
|
"""
|
||||||
|
This middleware intercepts all requests to connects object signals to the Django runtime. The signals collect all
|
||||||
|
changed objects into a local thread by way of the `mark_object_changed()` receiver. At the end of the request,
|
||||||
|
the middleware iterates over the objects to process change events like Change Logging and Webhooks.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, get_response):
|
def __init__(self, get_response):
|
||||||
self.get_response = get_response
|
self.get_response = get_response
|
||||||
@ -56,11 +63,16 @@ class ChangeLoggingMiddleware(object):
|
|||||||
# Process the request
|
# Process the request
|
||||||
response = self.get_response(request)
|
response = self.get_response(request)
|
||||||
|
|
||||||
# Record object changes
|
# Perform change logging and fire Webhook signals
|
||||||
for obj, action in _thread_locals.changed_objects:
|
for obj, action in _thread_locals.changed_objects:
|
||||||
if obj.pk:
|
# Log object changes
|
||||||
|
if obj.pk and hasattr(obj, 'log_change'):
|
||||||
obj.log_change(request.user, request.id, action)
|
obj.log_change(request.user, request.id, action)
|
||||||
|
|
||||||
|
# Enqueue Webhooks if they are enabled
|
||||||
|
if settings.WEBHOOKS_ENABLED and obj.__class__.__name__.lower() in WEBHOOK_MODELS:
|
||||||
|
enqueue_webhooks(obj, action)
|
||||||
|
|
||||||
# Housekeeping: 1% chance of clearing out expired ObjectChanges
|
# Housekeeping: 1% chance of clearing out expired ObjectChanges
|
||||||
if _thread_locals.changed_objects and settings.CHANGELOG_RETENTION and random.randint(1, 100) == 1:
|
if _thread_locals.changed_objects and settings.CHANGELOG_RETENTION and random.randint(1, 100) == 1:
|
||||||
cutoff = timezone.now() - timedelta(days=settings.CHANGELOG_RETENTION)
|
cutoff = timezone.now() - timedelta(days=settings.CHANGELOG_RETENTION)
|
||||||
|
@ -813,7 +813,6 @@ class ObjectChange(models.Model):
|
|||||||
editable=False
|
editable=False
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'extras.api.serializers.ObjectChangeSerializer'
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'time', 'user', 'user_name', 'request_id', 'action', 'changed_object_type', 'changed_object_id',
|
'time', 'user', 'user_name', 'request_id', 'action', 'changed_object_type', 'changed_object_id',
|
||||||
'related_object_type', 'related_object_id', 'object_repr', 'object_data',
|
'related_object_type', 'related_object_id', 'object_repr', 'object_data',
|
||||||
|
@ -1,119 +1,54 @@
|
|||||||
import time
|
import datetime
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.db.models.signals import post_save, post_delete
|
|
||||||
from django.dispatch import Signal
|
|
||||||
|
|
||||||
from extras.models import Webhook
|
from extras.models import Webhook
|
||||||
from utilities.utils import dynamic_import
|
from extras.constants import OBJECTCHANGE_ACTION_CREATE, OBJECTCHANGE_ACTION_DELETE, OBJECTCHANGE_ACTION_UPDATE
|
||||||
|
from utilities.api import get_serializer_for_model
|
||||||
|
|
||||||
|
|
||||||
def enqueue_webhooks(webhooks, model_class, data, event, signal_received_timestamp):
|
def enqueue_webhooks(instance, action):
|
||||||
"""
|
"""
|
||||||
Serialize data and enqueue webhooks
|
Find Webhook(s) assigned to this instance + action and enqueue them
|
||||||
|
to be processed
|
||||||
"""
|
"""
|
||||||
serializer_context = {
|
type_create = action == OBJECTCHANGE_ACTION_CREATE
|
||||||
'request': None,
|
type_update = action == OBJECTCHANGE_ACTION_UPDATE
|
||||||
}
|
type_delete = action == OBJECTCHANGE_ACTION_DELETE
|
||||||
|
|
||||||
if isinstance(data, list):
|
# Find assigned webhooks
|
||||||
serializer_property = data[0].serializer
|
obj_type = ContentType.objects.get_for_model(instance.__class__)
|
||||||
serializer_cls = dynamic_import(serializer_property)
|
|
||||||
serialized_data = serializer_cls(data, context=serializer_context, many=True)
|
|
||||||
else:
|
|
||||||
serializer_property = data.serializer
|
|
||||||
serializer_cls = dynamic_import(serializer_property)
|
|
||||||
serialized_data = serializer_cls(data, context=serializer_context)
|
|
||||||
|
|
||||||
from django_rq import get_queue
|
|
||||||
webhook_queue = get_queue('default')
|
|
||||||
|
|
||||||
for webhook in webhooks:
|
|
||||||
webhook_queue.enqueue("extras.webhooks_worker.process_webhook",
|
|
||||||
webhook,
|
|
||||||
serialized_data.data,
|
|
||||||
model_class,
|
|
||||||
event,
|
|
||||||
signal_received_timestamp)
|
|
||||||
|
|
||||||
|
|
||||||
def post_save_receiver(sender, instance, created, **kwargs):
|
|
||||||
"""
|
|
||||||
Receives post_save signals from registered models. If the webhook
|
|
||||||
backend is enabled, queue any webhooks that apply to the event.
|
|
||||||
"""
|
|
||||||
if settings.WEBHOOKS_ENABLED:
|
|
||||||
signal_received_timestamp = time.time()
|
|
||||||
# look for any webhooks that match this event
|
|
||||||
updated = not created
|
|
||||||
obj_type = ContentType.objects.get_for_model(sender)
|
|
||||||
webhooks = Webhook.objects.filter(
|
webhooks = Webhook.objects.filter(
|
||||||
Q(enabled=True) &
|
Q(enabled=True) &
|
||||||
(
|
(
|
||||||
Q(type_create=created) |
|
Q(type_create=type_create) |
|
||||||
Q(type_update=updated)
|
Q(type_update=type_update) |
|
||||||
|
Q(type_delete=type_delete)
|
||||||
) &
|
) &
|
||||||
Q(obj_type=obj_type)
|
Q(obj_type=obj_type)
|
||||||
)
|
)
|
||||||
event = 'created' if created else 'updated'
|
|
||||||
if webhooks:
|
|
||||||
enqueue_webhooks(webhooks, sender, instance, event, signal_received_timestamp)
|
|
||||||
|
|
||||||
|
|
||||||
def post_delete_receiver(sender, instance, **kwargs):
|
|
||||||
"""
|
|
||||||
Receives post_delete signals from registered models. If the webhook
|
|
||||||
backend is enabled, queue any webhooks that apply to the event.
|
|
||||||
"""
|
|
||||||
if settings.WEBHOOKS_ENABLED:
|
|
||||||
signal_received_timestamp = time.time()
|
|
||||||
obj_type = ContentType.objects.get_for_model(sender)
|
|
||||||
# look for any webhooks that match this event
|
|
||||||
webhooks = Webhook.objects.filter(enabled=True, type_delete=True, obj_type=obj_type)
|
|
||||||
if webhooks:
|
|
||||||
enqueue_webhooks(webhooks, sender, instance, 'deleted', signal_received_timestamp)
|
|
||||||
|
|
||||||
|
|
||||||
def bulk_operation_receiver(sender, **kwargs):
|
|
||||||
"""
|
|
||||||
Receives bulk_operation_signal signals from registered models. If the webhook
|
|
||||||
backend is enabled, queue any webhooks that apply to the event.
|
|
||||||
"""
|
|
||||||
if settings.WEBHOOKS_ENABLED:
|
|
||||||
signal_received_timestamp = time.time()
|
|
||||||
event = kwargs['event']
|
|
||||||
obj_type = ContentType.objects.get_for_model(sender)
|
|
||||||
# look for any webhooks that match this event
|
|
||||||
if event == 'created':
|
|
||||||
webhooks = Webhook.objects.filter(enabled=True, type_create=True, obj_type=obj_type)
|
|
||||||
elif event == 'updated':
|
|
||||||
webhooks = Webhook.objects.filter(enabled=True, type_update=True, obj_type=obj_type)
|
|
||||||
elif event == 'deleted':
|
|
||||||
webhooks = Webhook.objects.filter(enabled=True, type_delete=True, obj_type=obj_type)
|
|
||||||
else:
|
|
||||||
webhooks = None
|
|
||||||
|
|
||||||
if webhooks:
|
if webhooks:
|
||||||
enqueue_webhooks(webhooks, sender, list(kwargs['instances']), event, signal_received_timestamp)
|
# Get the Model's API serializer class and serialize the object
|
||||||
|
serializer_class = get_serializer_for_model(instance.__class__)
|
||||||
|
serializer_context = {
|
||||||
|
'request': None,
|
||||||
|
}
|
||||||
|
serializer = serializer_class(instance, context=serializer_context)
|
||||||
|
|
||||||
|
# We must only import django_rq if the Webhooks feature is enabled.
|
||||||
|
# Only if we have gotten to ths point, is the feature enabled
|
||||||
|
from django_rq import get_queue
|
||||||
|
webhook_queue = get_queue('default')
|
||||||
|
|
||||||
# the bulk operation signal is used to overcome signals not being sent for bulk model changes
|
# enqueue the webhooks:
|
||||||
bulk_operation_signal = Signal(providing_args=["instances", "event"])
|
for webhook in webhooks:
|
||||||
bulk_operation_signal.connect(bulk_operation_receiver)
|
webhook_queue.enqueue(
|
||||||
|
"extras.webhooks_worker.process_webhook",
|
||||||
|
webhook,
|
||||||
def register_signals(senders):
|
serializer.data,
|
||||||
"""
|
instance.__class__,
|
||||||
Take a list of senders (Models) and register them to the post_save
|
action,
|
||||||
and post_delete signal receivers.
|
str(datetime.datetime.now())
|
||||||
"""
|
)
|
||||||
if settings.WEBHOOKS_ENABLED:
|
|
||||||
# only register signals if the backend is enabled
|
|
||||||
# this reduces load by not firing signals if the
|
|
||||||
# webhook backend feature is disabled
|
|
||||||
|
|
||||||
for sender in senders:
|
|
||||||
post_save.connect(post_save_receiver, sender=sender)
|
|
||||||
post_delete.connect(post_delete_receiver, sender=sender)
|
|
||||||
|
@ -4,7 +4,7 @@ import hmac
|
|||||||
import requests
|
import requests
|
||||||
from django_rq import job
|
from django_rq import job
|
||||||
|
|
||||||
from extras.constants import WEBHOOK_CT_JSON, WEBHOOK_CT_X_WWW_FORM_ENCODED
|
from extras.constants import WEBHOOK_CT_JSON, WEBHOOK_CT_X_WWW_FORM_ENCODED, OBJECTCHANGE_ACTION_CHOICES
|
||||||
|
|
||||||
|
|
||||||
@job('default')
|
@job('default')
|
||||||
@ -13,7 +13,7 @@ def process_webhook(webhook, data, model_class, event, timestamp):
|
|||||||
Make a POST request to the defined Webhook
|
Make a POST request to the defined Webhook
|
||||||
"""
|
"""
|
||||||
payload = {
|
payload = {
|
||||||
'event': event,
|
'event': dict(OBJECTCHANGE_ACTION_CHOICES)[event],
|
||||||
'timestamp': timestamp,
|
'timestamp': timestamp,
|
||||||
'model': model_class.__name__,
|
'model': model_class.__name__,
|
||||||
'data': data
|
'data': data
|
||||||
|
@ -6,10 +6,3 @@ from django.apps import AppConfig
|
|||||||
class IPAMConfig(AppConfig):
|
class IPAMConfig(AppConfig):
|
||||||
name = "ipam"
|
name = "ipam"
|
||||||
verbose_name = "IPAM"
|
verbose_name = "IPAM"
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
|
|
||||||
# register webhook signals
|
|
||||||
from extras.webhooks import register_signals
|
|
||||||
from .models import Aggregate, Prefix, IPAddress, VLAN, VRF, VLANGroup, Service
|
|
||||||
register_signals([Aggregate, Prefix, IPAddress, VLAN, VRF, VLANGroup, Service])
|
|
||||||
|
@ -59,7 +59,6 @@ class VRF(ChangeLoggedModel, CustomFieldModel):
|
|||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'ipam.api.serializers.VRFSerializer'
|
|
||||||
csv_headers = ['name', 'rd', 'tenant', 'enforce_unique', 'description']
|
csv_headers = ['name', 'rd', 'tenant', 'enforce_unique', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -108,7 +107,6 @@ class RIR(ChangeLoggedModel):
|
|||||||
help_text='IP space managed by this RIR is considered private'
|
help_text='IP space managed by this RIR is considered private'
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'ipam.api.serializers.RIRSerializer'
|
|
||||||
csv_headers = ['name', 'slug', 'is_private']
|
csv_headers = ['name', 'slug', 'is_private']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -162,7 +160,6 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel):
|
|||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'ipam.api.serializers.AggregateSerializer'
|
|
||||||
csv_headers = ['prefix', 'rir', 'date_added', 'description']
|
csv_headers = ['prefix', 'rir', 'date_added', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -243,7 +240,6 @@ class Role(ChangeLoggedModel):
|
|||||||
default=1000
|
default=1000
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'ipam.api.serializers.RoleSerializer'
|
|
||||||
csv_headers = ['name', 'slug', 'weight']
|
csv_headers = ['name', 'slug', 'weight']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -336,7 +332,6 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
|
|||||||
objects = PrefixQuerySet.as_manager()
|
objects = PrefixQuerySet.as_manager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'ipam.api.serializers.PrefixSerializer'
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'prefix', 'vrf', 'tenant', 'site', 'vlan_group', 'vlan_vid', 'status', 'role', 'is_pool', 'description',
|
'prefix', 'vrf', 'tenant', 'site', 'vlan_group', 'vlan_vid', 'status', 'role', 'is_pool', 'description',
|
||||||
]
|
]
|
||||||
@ -577,7 +572,6 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
|
|||||||
objects = IPAddressManager()
|
objects = IPAddressManager()
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'ipam.api.serializers.IPAddressSerializer'
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'address', 'vrf', 'tenant', 'status', 'role', 'device', 'virtual_machine', 'interface_name', 'is_primary',
|
'address', 'vrf', 'tenant', 'status', 'role', 'device', 'virtual_machine', 'interface_name', 'is_primary',
|
||||||
'description',
|
'description',
|
||||||
@ -677,7 +671,6 @@ class VLANGroup(ChangeLoggedModel):
|
|||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'ipam.api.serializers.VLANGroupSerializer'
|
|
||||||
csv_headers = ['name', 'slug', 'site']
|
csv_headers = ['name', 'slug', 'site']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -775,7 +768,6 @@ class VLAN(ChangeLoggedModel, CustomFieldModel):
|
|||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'ipam.api.serializers.VLANSerializer'
|
|
||||||
csv_headers = ['site', 'group_name', 'vid', 'name', 'tenant', 'status', 'role', 'description']
|
csv_headers = ['site', 'group_name', 'vid', 'name', 'tenant', 'status', 'role', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -879,7 +871,6 @@ class Service(ChangeLoggedModel, CustomFieldModel):
|
|||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'ipam.api.serializers.ServiceSerializer'
|
|
||||||
csv_headers = ['device', 'virtual_machine', 'name', 'protocol', 'description']
|
csv_headers = ['device', 'virtual_machine', 'name', 'protocol', 'description']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -175,7 +175,7 @@ MIDDLEWARE = (
|
|||||||
'utilities.middleware.ExceptionHandlingMiddleware',
|
'utilities.middleware.ExceptionHandlingMiddleware',
|
||||||
'utilities.middleware.LoginRequiredMiddleware',
|
'utilities.middleware.LoginRequiredMiddleware',
|
||||||
'utilities.middleware.APIVersionMiddleware',
|
'utilities.middleware.APIVersionMiddleware',
|
||||||
'extras.middleware.ChangeLoggingMiddleware',
|
'extras.middleware.ObjectChangeMiddleware',
|
||||||
)
|
)
|
||||||
|
|
||||||
ROOT_URLCONF = 'netbox.urls'
|
ROOT_URLCONF = 'netbox.urls'
|
||||||
|
@ -285,7 +285,6 @@ class SecretRole(ChangeLoggedModel):
|
|||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'ipam.api.secrets.SecretSerializer'
|
|
||||||
csv_headers = ['name', 'slug']
|
csv_headers = ['name', 'slug']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -354,7 +353,6 @@ class Secret(ChangeLoggedModel, CustomFieldModel):
|
|||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
plaintext = None
|
plaintext = None
|
||||||
serializer = 'ipam.api.secrets.SecretSerializer'
|
|
||||||
csv_headers = ['device', 'role', 'name', 'plaintext']
|
csv_headers = ['device', 'role', 'name', 'plaintext']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -5,10 +5,3 @@ from django.apps import AppConfig
|
|||||||
|
|
||||||
class TenancyConfig(AppConfig):
|
class TenancyConfig(AppConfig):
|
||||||
name = 'tenancy'
|
name = 'tenancy'
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
|
|
||||||
# register webhook signals
|
|
||||||
from extras.webhooks import register_signals
|
|
||||||
from .models import Tenant, TenantGroup
|
|
||||||
register_signals([Tenant, TenantGroup])
|
|
||||||
|
@ -23,7 +23,6 @@ class TenantGroup(ChangeLoggedModel):
|
|||||||
unique=True
|
unique=True
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'tenancy.api.serializers.TenantGroupSerializer'
|
|
||||||
csv_headers = ['name', 'slug']
|
csv_headers = ['name', 'slug']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -78,7 +77,6 @@ class Tenant(ChangeLoggedModel, CustomFieldModel):
|
|||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'tenancy.api.serializers.TenantSerializer'
|
|
||||||
csv_headers = ['name', 'slug', 'group', 'description', 'comments']
|
csv_headers = ['name', 'slug', 'group', 'description', 'comments']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -25,7 +25,6 @@ from django.views.generic import View
|
|||||||
from django_tables2 import RequestConfig
|
from django_tables2 import RequestConfig
|
||||||
|
|
||||||
from extras.models import CustomField, CustomFieldValue, ExportTemplate
|
from extras.models import CustomField, CustomFieldValue, ExportTemplate
|
||||||
from extras.webhooks import bulk_operation_signal
|
|
||||||
from utilities.utils import queryset_to_csv
|
from utilities.utils import queryset_to_csv
|
||||||
from utilities.forms import BootstrapMixin, CSVDataField
|
from utilities.forms import BootstrapMixin, CSVDataField
|
||||||
from .constants import M2M_FIELD_TYPES
|
from .constants import M2M_FIELD_TYPES
|
||||||
@ -757,9 +756,6 @@ class ComponentCreateView(View):
|
|||||||
field_links.append(field_link)
|
field_links.append(field_link)
|
||||||
getattr(self.model, field).through.objects.bulk_create(field_links)
|
getattr(self.model, field).through.objects.bulk_create(field_links)
|
||||||
|
|
||||||
# send the bulk operations signal for webhooks
|
|
||||||
bulk_operation_signal.send(sender=self.model, instances=new_components, event="created")
|
|
||||||
|
|
||||||
messages.success(request, "Added {} {} to {}.".format(
|
messages.success(request, "Added {} {} to {}.".format(
|
||||||
len(new_components), self.model._meta.verbose_name_plural, parent
|
len(new_components), self.model._meta.verbose_name_plural, parent
|
||||||
))
|
))
|
||||||
@ -829,9 +825,6 @@ class BulkComponentCreateView(GetReturnURLMixin, View):
|
|||||||
if not form.errors:
|
if not form.errors:
|
||||||
self.model.objects.bulk_create(new_components)
|
self.model.objects.bulk_create(new_components)
|
||||||
|
|
||||||
# send the bulk operations signal for webhooks
|
|
||||||
bulk_operation_signal.send(sender=self.model, instances=new_components, event="created")
|
|
||||||
|
|
||||||
messages.success(request, "Added {} {} to {} {}.".format(
|
messages.success(request, "Added {} {} to {} {}.".format(
|
||||||
len(new_components),
|
len(new_components),
|
||||||
self.model._meta.verbose_name_plural,
|
self.model._meta.verbose_name_plural,
|
||||||
|
@ -5,10 +5,3 @@ from django.apps import AppConfig
|
|||||||
|
|
||||||
class VirtualizationConfig(AppConfig):
|
class VirtualizationConfig(AppConfig):
|
||||||
name = 'virtualization'
|
name = 'virtualization'
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
|
|
||||||
# register webhook signals
|
|
||||||
from extras.webhooks import register_signals
|
|
||||||
from .models import Cluster, ClusterGroup, VirtualMachine
|
|
||||||
register_signals([Cluster, VirtualMachine, ClusterGroup])
|
|
||||||
|
@ -31,7 +31,6 @@ class ClusterType(ChangeLoggedModel):
|
|||||||
unique=True
|
unique=True
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'virtualization.api.serializers.ClusterTypeSerializer'
|
|
||||||
csv_headers = ['name', 'slug']
|
csv_headers = ['name', 'slug']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -67,7 +66,6 @@ class ClusterGroup(ChangeLoggedModel):
|
|||||||
unique=True
|
unique=True
|
||||||
)
|
)
|
||||||
|
|
||||||
serializer = 'virtualization.api.serializers.ClusterGroupSerializer'
|
|
||||||
csv_headers = ['name', 'slug']
|
csv_headers = ['name', 'slug']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -129,7 +127,6 @@ class Cluster(ChangeLoggedModel, CustomFieldModel):
|
|||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'virtualization.api.serializers.ClusterSerializer'
|
|
||||||
csv_headers = ['name', 'type', 'group', 'site', 'comments']
|
csv_headers = ['name', 'type', 'group', 'site', 'comments']
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -250,7 +247,6 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
|
|||||||
|
|
||||||
tags = TaggableManager()
|
tags = TaggableManager()
|
||||||
|
|
||||||
serializer = 'virtualization.api.serializers.VirtualMachineSerializer'
|
|
||||||
csv_headers = [
|
csv_headers = [
|
||||||
'name', 'status', 'role', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments',
|
'name', 'status', 'role', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments',
|
||||||
]
|
]
|
||||||
|
Reference in New Issue
Block a user