diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py index 35af0e1441e210663a3fc7e5651d4688a30b124b..7f746ddd679694db4ae33db6066263c61e3ad005 100644 --- a/netbox_sys_plugin/api/serializers.py +++ b/netbox_sys_plugin/api/serializers.py @@ -7,7 +7,7 @@ from dcim.api.nested_serializers import ( from ipam.api.nested_serializers import ( NestedIPAddressSerializer, NestedServiceSerializer ) -from .. models import VirtualMachineMaintenance, ProviderCredentials,VmAssignedVirtualMachineType, VirtualMachineType,DomainNames, WebhookSettings,ProviderTypeExtraConfig,VmAssignedExtraConfig +from .. models import VirtualMachineMaintenance, ProviderCredentials,VmAssignedVirtualMachineType, VirtualMachineType,DomainNames, WebhookSettings,ProviderTypeExtraConfig,VmAssignedExtraConfig, WebhookPayload from django.contrib.contenttypes.models import ContentType from .nested_serializers import * from virtualization.choices import * @@ -212,6 +212,14 @@ class WebhookSettingsSerializer(NetBoxModelSerializer): raise serializers.ValidationError("You can only have one webbook setting.") return data +class WebhookPayloadSerializer(NetBoxModelSerializer): + id = serializers.IntegerField(read_only=True) + payload_data = serializers.JSONField() + + class Meta: + model = WebhookPayload + fields = '__all__' + class ProviderTypeExtraConfigSerializer(NetBoxModelSerializer): id = serializers.IntegerField(read_only=True) extra_config_structure = serializers.JSONField() diff --git a/netbox_sys_plugin/api/urls.py b/netbox_sys_plugin/api/urls.py index 47e62cc215e6724ac4712d6c8898699b05a88fd9..2766642dfb3c58eaa9a169a0abe01f200d5aed53 100644 --- a/netbox_sys_plugin/api/urls.py +++ b/netbox_sys_plugin/api/urls.py @@ -8,6 +8,7 @@ router.register(r'AssignedVmType', VmAssignedVirtualMachineTypeViewSet) router.register(r'VmType', VirtualMachineTypeViewSet) router.register(r'DomainNames', DomainNamesViewSet) router.register(r'WebhookSettings', WebhookSettingsViewSet) +router.register(r'WebhookPayload', WebhookPayloadViewSet) router.register(r'ProviderTypeExtraConfig', ProviderTypeExtraConfigViewSet) router.register(r'VirtualMachine', VirtualMachineViewSet, basename='virtualmachine') router.register(r'ExtraConfig', ProviderTypeExtraConfigViewSet) diff --git a/netbox_sys_plugin/api/views.py b/netbox_sys_plugin/api/views.py index 0e0c29403be8e9ad704df38c10067c54fe6d89ac..3d9ed9f85c58df079701e9acca31992ecde95705 100644 --- a/netbox_sys_plugin/api/views.py +++ b/netbox_sys_plugin/api/views.py @@ -5,7 +5,7 @@ from django.db.models import Prefetch from netbox.api.viewsets import NetBoxModelViewSet from virtualization import filtersets from .. filtersets import VmTypeFilterSet, ProviderTypeExtraConfigFilterSet -from .. models import VirtualMachineMaintenance, ProviderCredentials, VmAssignedVirtualMachineType, VirtualMachineType,DomainNames, WebhookSettings, ProviderTypeExtraConfig +from .. models import VirtualMachineMaintenance, ProviderCredentials, VmAssignedVirtualMachineType, VirtualMachineType,DomainNames, WebhookSettings, ProviderTypeExtraConfig, WebhookPayload from . serializers import * class VirtualMachineMaintenanceViewSet(viewsets.ModelViewSet): @@ -55,6 +55,11 @@ class WebhookSettingsViewSet(viewsets.ModelViewSet): serializer_class = WebhookSettingsSerializer http_method_names = ["get", "post", "patch", "delete", "options"] +class WebhookPayloadViewSet(viewsets.ModelViewSet): + queryset = WebhookPayload.objects.all() + serializer_class = WebhookPayloadSerializer + http_method_names = ["get", "post", "delete", "options"] + class ProviderTypeExtraConfigViewSet(viewsets.ModelViewSet): queryset = ProviderTypeExtraConfig.objects.all() serializer_class = ProviderTypeExtraConfigSerializer diff --git a/netbox_sys_plugin/forms/createvm.py b/netbox_sys_plugin/forms/createvm.py index 4b35f0576d33bc64b829f612c0c17e3dcf075d0f..826743dd3847f59817bd706dafd3062137db9224 100644 --- a/netbox_sys_plugin/forms/createvm.py +++ b/netbox_sys_plugin/forms/createvm.py @@ -13,8 +13,8 @@ from virtualization.models import ClusterType, Cluster, VirtualMachine, VMInterf from ipam.models import IPAddress, Service from dcim.models import DeviceRole, Platform from extras.models import Tag, TaggedItem -from .. models import DomainNames, VmAssignedVirtualMachineType, VirtualMachineType, VmAssignedExtraConfig, ProviderTypeExtraConfig -from . . utils import send_webhook, validate_extra_config_values +from .. models import DomainNames, VmAssignedVirtualMachineType, VirtualMachineType, VmAssignedExtraConfig, ProviderTypeExtraConfig, WebhookPayload +from . . utils import validate_extra_config_values @@ -643,8 +643,14 @@ class CreateVmForm(NetBoxModelForm): ], } + wb_payload = WebhookPayload( + payload_data=payload + ) + wb_payload.full_clean() + wb_payload.save() + CreateVmForm.assign_tags(wb_payload,data) # Send webhook - send_webhook(payload) + #send_webhook(payload) return vm except ValueError as e: diff --git a/netbox_sys_plugin/migrations/0006_webhookpayload.py b/netbox_sys_plugin/migrations/0006_webhookpayload.py new file mode 100644 index 0000000000000000000000000000000000000000..28b2fc792cf7b7a21e622328bde6212671703b81 --- /dev/null +++ b/netbox_sys_plugin/migrations/0006_webhookpayload.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.16 on 2025-01-31 17:35 + +from django.db import migrations, models +import taggit.managers +import utilities.json + + +class Migration(migrations.Migration): + + dependencies = [ + ('extras', '0098_webhook_custom_field_data_webhook_tags'), + ('netbox_sys_plugin', '0005_alter_webhooksettings_payload_url'), + ] + + operations = [ + migrations.CreateModel( + name='WebhookPayload', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), + ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)), + ('payload_data', models.JSONField(blank=True)), + ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')), + ], + options={ + 'verbose_name': 'Webhook Payload', + 'verbose_name_plural': 'Webhook Payloads', + 'ordering': ['payload_data'], + 'unique_together': {('payload_data',)}, + }, + ), + ] diff --git a/netbox_sys_plugin/models/operation.py b/netbox_sys_plugin/models/operation.py index 7d3eb4d425411a2e63019c5f2c091dc134c5645e..06126b78018d7e84d9a3b6ce3fb8f8a6656956f9 100644 --- a/netbox_sys_plugin/models/operation.py +++ b/netbox_sys_plugin/models/operation.py @@ -35,3 +35,24 @@ class WebhookSettings(NetBoxModel): def get_absolute_url(self): """override""" return reverse("plugins:netbox_sys_plugin:webhooksettings", args=[self.pk]) + +class WebhookPayload(NetBoxModel): + """Webhook Payload definition class""" + + payload_data = models.JSONField( + blank=True, + null=False, + help_text=_( + "JSON Payload to be sent" + ) + ) + class Meta: + """Meta class""" + unique_together = ["payload_data"] + ordering = ["payload_data"] + verbose_name = "Webhook Payload" + verbose_name_plural = "Webhook Payloads" + + def get_absolute_url(self): + """override""" + return reverse("plugins:netbox_sys_plugin:webhookpayloads", args=[self.pk]) diff --git a/netbox_sys_plugin/navigation.py b/netbox_sys_plugin/navigation.py index 1eea0d03b9de4de1d0c29793b02e4f95f0700966..92c70c1e8fda21e625de13ab705f2289b9bd388a 100644 --- a/netbox_sys_plugin/navigation.py +++ b/netbox_sys_plugin/navigation.py @@ -135,9 +135,14 @@ vmMachineItem = [ operItem =[ PluginMenuItem( link="plugins:netbox_sys_plugin:webhooksettings_list", - link_text="Webhook", + link_text="Webhook Settings", permissions=["netbox_sys_plugin.list_webhooksettings"], ), + PluginMenuItem( + link="plugins:netbox_sys_plugin:webhookpayloads_list", + link_text="Webhook Payload", + permissions=["netbox_sys_plugin.list_webhookpayloads"], + ), ] #Menu menu = PluginMenu( diff --git a/netbox_sys_plugin/tables.py b/netbox_sys_plugin/tables.py index 70ae2e58960304a80da80550d86a63002c886ef2..5d21779265fbc72d556c94735b9d2c38c165160c 100644 --- a/netbox_sys_plugin/tables.py +++ b/netbox_sys_plugin/tables.py @@ -8,6 +8,7 @@ from .models import ( VmAssignedVirtualMachineType, VirtualMachineType, WebhookSettings, + WebhookPayload, DomainNames, ProviderTypeExtraConfig, VmAssignedExtraConfig) @@ -216,6 +217,32 @@ class WebhookSettingsTable(NetBoxTable): default_columns = ('id','payload_url','http_content_type') +#WebhookPayload +class WebhookPayloadTable(NetBoxTable): + """webhook Settings Table definition class""" + + pk = columns.ToggleColumn() + id = tables.Column( + linkify=False, + ) + + payload_data = tables.Column( + linkify=False, + verbose_name="Payload Data", + ) + + class Meta(NetBoxTable.Meta): + """Meta class""" + model = WebhookPayload + fields = ( + "pk", + "id", + "payload_data", + ) + + default_columns = ('id','payload_data') + + #ExtraConfig class ProviderTypeExtraConfigTable(NetBoxTable): """Provider Type Extra Configs Table definition class""" diff --git a/netbox_sys_plugin/tests/webhook_payload/__init__.py b/netbox_sys_plugin/tests/webhook_payload/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/netbox_sys_plugin/tests/webhook_payload/test_webhook_payload_api.py b/netbox_sys_plugin/tests/webhook_payload/test_webhook_payload_api.py new file mode 100644 index 0000000000000000000000000000000000000000..1907262d5c379dddce187d3507227ce6fdc81c3d --- /dev/null +++ b/netbox_sys_plugin/tests/webhook_payload/test_webhook_payload_api.py @@ -0,0 +1,127 @@ +"""SYS Plugin Webhook payload API Test Case Class""" + +from users.models import ObjectPermission +from django.contrib.contenttypes.models import ContentType +from rest_framework import status +from netbox_sys_plugin.models import WebhookPayload +from ..base import BaseAPITestCase + +class WebhookPayloadApiTestCase(BaseAPITestCase): + """Test suite for WebhookPayload API""" + model = WebhookPayload + brief_fields = ["payload_data"] + + + @classmethod + def setUpTestData(cls): + + """Set up test data for WebhookPayload API""" + + # Data for valid creation + cls.valid_create_data = [ + { + "payload_data":{'provider': {'id': 3, 'name': 'http://testtestesteste.com', 'provider_type': {'id': 2, 'name': 'AWS'}}}, + } + ] + + # Data for invalid creation + cls.invalid_create_data = [ + { + "payload_data": None, # Missing + + }, + ] + + def test_create_valid_webhook_payload(self): + """Test creating a valid webhook payload""" + + obj_perm = ObjectPermission( + name="Create Webhook payload Permission", + actions=["add", "view"], + ) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ContentType.objects.get_for_model(WebhookPayload)) + + form_data = self.valid_create_data[0] + response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) + + self.assertHttpStatus(response, status.HTTP_201_CREATED) + self.assertEqual(response.data["payload_data"], form_data["payload_data"]) + + def test_create_invalid_webhook_payload(self): + """Test creating invalid webhook payload""" + obj_perm = ObjectPermission( + name="Invalid Webhook Payload Permission", + actions=["add", "view"], + ) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ContentType.objects.get_for_model(WebhookPayload)) + + form_data = self.invalid_create_data[0] + response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + self.assertIn('This field may not be null.',str(response.data)) + + def test_get_webhook_payload_list(self): + """Test fetching webhook payload list""" + + # Create Webhook payload + WebhookPayload.objects.create( + payload_data={'provider': {'id': 3, 'name': 'http://testtestesteste.com', 'provider_type': {'id': 2, 'name': 'AWS'}}}, + ) + + obj_perm = ObjectPermission( + name="Create Webhook Payload Permission", + actions=["view"], + ) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ContentType.objects.get_for_model(WebhookPayload)) + + response = self.client.get(self._get_list_url(), **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertGreaterEqual(len(response.data), 2) + + def test_get_single_webhook_payload(self): + """Test fetching a single webhook Payload""" + + # Create Webhook payload + WebhookPayload.objects.create( + payload_data={'provider': {'id': 3, 'name': 'http://testtestesteste.com', 'provider_type': {'id': 2, 'name': 'AWS'}}}, + ) + + obj_perm = ObjectPermission( + name="View Webhook payload Permission", + actions=["view"], + ) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ContentType.objects.get_for_model(WebhookPayload)) + + webhook_payload = WebhookPayload.objects.first() + response = self.client.get(self._get_detail_url(webhook_payload), **self.header) + self.assertHttpStatus(response, status.HTTP_200_OK) + self.assertEqual(response.data["payload_data"], webhook_payload.payload_data) + + def test_delete_webhook_payload(self): + """Test deleting a webhook payload""" + + # Create Webhook payload + WebhookPayload.objects.create( + payload_data={'provider': {'id': 3, 'name': 'http://testtestesteste.com', 'provider_type': {'id': 2, 'name': 'AWS'}}}, + ) + + webhook_payload = WebhookPayload.objects.first() + obj_perm = ObjectPermission( + name="Delete Webhook payload Permission", + actions=["delete"], + ) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ContentType.objects.get_for_model(WebhookPayload)) + + response = self.client.delete(self._get_detail_url(webhook_payload), **self.header) + self.assertHttpStatus(response, status.HTTP_204_NO_CONTENT) + self.assertFalse(WebhookPayload.objects.filter(id=webhook_payload.id).exists()) diff --git a/netbox_sys_plugin/urls.py b/netbox_sys_plugin/urls.py index fb24174dff736445bcd9be41168c7b0d515b3184..7c8ea9e1468ba00c49a3732d700f1295ca8e8602 100644 --- a/netbox_sys_plugin/urls.py +++ b/netbox_sys_plugin/urls.py @@ -68,6 +68,15 @@ urlpatterns = ( 'model': models.WebhookSettings}), path('webhook-settings/<int:pk>/journal/', ObjectJournalView.as_view(), name='webhooksettings_journal', kwargs={ 'model': models.WebhookSettings}), + #WebhookPayload + path('webhook-payload/<int:pk>/', views.WebhookPayloadView.as_view(), name='webhookpayload'), + path('webhook-payload/', views.WebhookPayloadListView.as_view(), name='webhookpayloads_list'), + path('webhook-payload/<int:pk>/delete/', views.WebhookPayloadDeleteView.as_view(), name='webhookpayload_delete'), + path('webhook-payload/<int:pk>/edit/', views.WebhookPayloadEditView.as_view(), name='webhookpayload_edit'), + path('webhook-payload/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='webhookpayload_changelog', kwargs={ + 'model': models.WebhookPayload}), + path('webhook-payload/<int:pk>/journal/', ObjectJournalView.as_view(), name='webhookpayload_journal', kwargs={ + 'model': models.WebhookPayload}), #Extra Config path('extra-config/<int:pk>/', views.ProviderTypeExtraConfigView.as_view(), name='providertypeextraconfig'), path('extra-config/', views.ProviderTypeExtraConfigListView.as_view(), name='providertypeextraconfig_list'), diff --git a/netbox_sys_plugin/views.py b/netbox_sys_plugin/views.py index 977300ce5656a39ff98ba4b645a00b43b4d8b310..bbd8d28ced3b209f7905dd6e4a939769aaca6731 100644 --- a/netbox_sys_plugin/views.py +++ b/netbox_sys_plugin/views.py @@ -205,6 +205,31 @@ class WebhookSettingsDeleteView(generic.ObjectDeleteView): """Webhook Settings Delete delete view definition""" queryset = models.WebhookSettings.objects.all() +#Webhook Payload +class WebhookPayloadView(generic.ObjectView): + + """ Webhook Payload view definition""" + + queryset = ( + models.WebhookPayload.objects.all() + ) + +class WebhookPayloadListView(generic.ObjectListView): + """Webhook Payload list view definition""" + + queryset = models.WebhookPayload.objects.all() + table = tables.WebhookPayloadTable + +class WebhookPayloadDeleteView(generic.ObjectDeleteView): + """Webhook Payload Delete delete view definition""" + queryset = models.WebhookPayload.objects.all() + +class WebhookPayloadEditView(generic.ObjectEditView): + """ + Defines the edit view for the Webhook Payload django model. + """ + queryset = models.WebhookPayload.objects.all() + #ExtraConfig class ProviderTypeExtraConfigView(generic.ObjectView): """ Virtual Machine Type view definition"""