From 0e5553cb756a847d49514dba418c700e9ecb98ce Mon Sep 17 00:00:00 2001 From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu> Date: Tue, 4 Feb 2025 12:13:41 +0000 Subject: [PATCH 1/9] =?UTF-8?q?=F0=9F=90=9B=20=F0=9F=91=BD=EF=B8=8F=20Fix?= =?UTF-8?q?=20naming=20issues=20and=20simply=20API=20for=20maintenance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- netbox_sys_plugin/api/nested_serializers.py | 8 ++-- netbox_sys_plugin/api/serializers.py | 39 ++++++++++--------- .../vm_maintenance/test_vm_maintenance_api.py | 22 ++++------- 3 files changed, 32 insertions(+), 37 deletions(-) diff --git a/netbox_sys_plugin/api/nested_serializers.py b/netbox_sys_plugin/api/nested_serializers.py index de14531..db41577 100644 --- a/netbox_sys_plugin/api/nested_serializers.py +++ b/netbox_sys_plugin/api/nested_serializers.py @@ -9,20 +9,20 @@ from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMac from tenancy.models import * __all__ = [ - 'NestedVirtualMachineSerializer', + 'NestedSysVirtualMachineSerializer', 'NestedVirtualMachineMaintenanceSerializer', 'NestedProviderCredentialsSerializer', 'NestedVmAssignedVirtualMachineTypeSerializer', 'NestedVirtualMachineTypeSerializer', 'NestedTenantGroupSerializer', - 'NestedTenantSerializer', + 'NestedSysTenantSerializer', 'NestedDomainNamesSerializer', 'NestedVmAssignedExtraConfigSerializer', 'NestedProviderTypeExtraConfigSerializer', ] -class NestedVirtualMachineSerializer(serializers.ModelSerializer): +class NestedSysVirtualMachineSerializer(serializers.ModelSerializer): class Meta: model = VirtualMachine fields = ['id', 'name'] @@ -65,7 +65,7 @@ class NestedTenantGroupSerializer(WritableNestedSerializer): fields = ['id', 'url', 'display', 'name', 'slug', 'tenant_count', '_depth', 'created', 'last_updated'] -class NestedTenantSerializer(WritableNestedSerializer): +class NestedSysTenantSerializer(WritableNestedSerializer): url = serializers.HyperlinkedIdentityField(view_name='tenancy-api:tenant-detail') class Meta: diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py index 45d35f1..c513556 100644 --- a/netbox_sys_plugin/api/serializers.py +++ b/netbox_sys_plugin/api/serializers.py @@ -16,7 +16,7 @@ from ..utils import validate_extra_config_values #Netbox Data Serializer -class ClusterTypeSerializer(serializers.ModelSerializer): +class SysClusterTypeSerializer(serializers.ModelSerializer): provider_type_extra_config = serializers.SerializerMethodField() class Meta: model = ClusterType @@ -27,7 +27,7 @@ class ClusterTypeSerializer(serializers.ModelSerializer): assigned_object_id=obj.id) return NestedProviderTypeExtraConfigSerializer(provider_type_extra_config, many=True).data -class ClusterSerializer(serializers.ModelSerializer): +class SysClusterSerializer(serializers.ModelSerializer): cluster_type = serializers.SerializerMethodField() provider_credentials = serializers.SerializerMethodField() @@ -44,32 +44,33 @@ class ClusterSerializer(serializers.ModelSerializer): return NestedProviderCredentialsSerializer(provider_credentials, many=True).data def get_cluster_type(self, obj): - return ClusterTypeSerializer(obj.type).data + return SysClusterTypeSerializer(obj.type).data #Plugin Data Serializer class VirtualMachineMaintenanceSerializer(NetBoxModelSerializer): id = serializers.IntegerField(read_only=True) - virtual_machine = NestedVirtualMachineSerializer(source='assigned_object', read_only=True) - assigned_object_id = serializers.IntegerField(write_only=True) - assigned_object_type = serializers.PrimaryKeyRelatedField( - queryset=ContentType.objects.all(), - write_only=True, - ) + virtual_machine = NestedSysVirtualMachineSerializer(source='assigned_object', read_only=True) + virtual_machine_id = serializers.IntegerField(write_only=True) + display = serializers.CharField(source="__str__",read_only=True) class Meta: model = VirtualMachineMaintenance - fields = '__all__' + fields = ['id','virtual_machine','virtual_machine_id','maintenance_window','tags'] + + def validate(self,data): + data["assigned_object_type"] = ContentType.objects.get_for_model(VirtualMachine) + return data def create(self, validated_data): - assigned_object_type = validated_data.pop("assigned_object_type") - validated_data["assigned_object_type"] = assigned_object_type + assigned_object_id = validated_data.pop("virtual_machine_id") + validated_data["assigned_object_id"] = assigned_object_id return super().create(validated_data) class ProviderCredentialsSerializer(NetBoxModelSerializer): id = serializers.IntegerField(read_only=True) - cluster = ClusterSerializer(source='assigned_object', read_only=True) + cluster = SysClusterSerializer(source='assigned_object', read_only=True) assigned_object_id = serializers.IntegerField(write_only=True) assigned_object_type = serializers.PrimaryKeyRelatedField( queryset=ContentType.objects.all(), @@ -89,7 +90,7 @@ class ProviderCredentialsSerializer(NetBoxModelSerializer): class VirtualMachineTypeSerializer(NetBoxModelSerializer): id = serializers.IntegerField(read_only=True) - cluster_type = ClusterTypeSerializer(source='assigned_object', read_only=True) + cluster_type = SysClusterTypeSerializer(source='assigned_object', read_only=True) assigned_object_id = serializers.IntegerField(write_only=True) assigned_object_type = serializers.PrimaryKeyRelatedField( queryset=ContentType.objects.all(), @@ -140,7 +141,7 @@ class VmAssignedVirtualMachineTypeSerializer(NetBoxModelSerializer): def get_virtual_machine_type_name(self, obj): """Get the name of the related VirtualMachineType.""" return obj.virtual_machine_type.virtual_machine_type_name if obj.virtual_machine_type else None - + def get_assigned_object(self, obj): """Get the assigned object representation.""" assigned_object = obj.assigned_object @@ -182,7 +183,7 @@ class VmAssignedVirtualMachineTypeSerializer(NetBoxModelSerializer): class DomainNamesSerializer(NetBoxModelSerializer): id = serializers.IntegerField(read_only=True) - virtual_machine = NestedVirtualMachineSerializer(source='assigned_object', read_only=True) + virtual_machine = NestedSysVirtualMachineSerializer(source='assigned_object', read_only=True) assigned_object_id = serializers.IntegerField(write_only=True) assigned_object_type = serializers.PrimaryKeyRelatedField( queryset=ContentType.objects.all(), @@ -227,7 +228,7 @@ class ProviderTypeExtraConfigSerializer(NetBoxModelSerializer): class VmAssignedExtraConfigSerializer(NetBoxModelSerializer): id = serializers.IntegerField(read_only=True) - virtual_machine = NestedVirtualMachineSerializer(source='assigned_object', read_only=True) + virtual_machine = NestedSysVirtualMachineSerializer(source='assigned_object', read_only=True) assigned_object_id = serializers.IntegerField(write_only=True) assigned_object_type = serializers.PrimaryKeyRelatedField( queryset=ContentType.objects.all(), @@ -289,10 +290,10 @@ class VirtualMachineSerializer(NetBoxModelSerializer): url = serializers.HyperlinkedIdentityField(view_name='plugins-api:netbox_sys_plugin-api:virtualmachine-detail') status = ChoiceField(choices=VirtualMachineStatusChoices, required=False) site = NestedSiteSerializer(required=False, allow_null=True) - cluster = ClusterSerializer(required=False, allow_null=True) + cluster = SysClusterSerializer(required=False, allow_null=True) device = NestedDeviceSerializer(required=False, allow_null=True) role = NestedDeviceRoleSerializer(required=False, allow_null=True) - tenant = NestedTenantSerializer(required=False, allow_null=True) + tenant = NestedSysTenantSerializer(required=False, allow_null=True) platform = NestedPlatformSerializer(required=False, allow_null=True) primary_ip = NestedIPAddressSerializer(read_only=True) primary_ip4 = NestedIPAddressSerializer(required=False, allow_null=True) diff --git a/netbox_sys_plugin/tests/vm_maintenance/test_vm_maintenance_api.py b/netbox_sys_plugin/tests/vm_maintenance/test_vm_maintenance_api.py index 59dce22..24b88d0 100644 --- a/netbox_sys_plugin/tests/vm_maintenance/test_vm_maintenance_api.py +++ b/netbox_sys_plugin/tests/vm_maintenance/test_vm_maintenance_api.py @@ -10,7 +10,7 @@ from ..base import BaseAPITestCase class VirtualMachineMaintenanceApiTestCase(BaseAPITestCase): """Test suite for VirtualMachineMaintenance API""" model = VirtualMachineMaintenance - brief_fields = ["maintenance_window", "assigned_object_id", "assigned_object_type"] + brief_fields = ["maintenance_window", "virtual_machine_id"] @classmethod @@ -79,8 +79,7 @@ class VirtualMachineMaintenanceApiTestCase(BaseAPITestCase): cls.valid_create_data = [ { "maintenance_window": "2-12:00", - "assigned_object_type": cls.vm_ct.pk, - "assigned_object_id": cls.virtual_machine3.id, + "virtual_machine_id": cls.virtual_machine3.id, } ] @@ -88,19 +87,16 @@ class VirtualMachineMaintenanceApiTestCase(BaseAPITestCase): cls.invalid_create_data = [ { "maintenance_window": "invalid-format", - "assigned_object_type": cls.vm_ct.pk, - "assigned_object_id": cls.virtual_machine4.id, + "virtual_machine_id": cls.virtual_machine4.id, }, { "maintenance_window": "5-25:00", # Invalid time - "assigned_object_type": cls.vm_ct.pk, - "assigned_object_id": cls.virtual_machine5.id, + "virtual_machine_id": cls.virtual_machine5.id, }, { "maintenance_window": "2-12:00", - "assigned_object_type": cls.vm_ct.pk, - "assigned_object_id": None, # Missing VirtualMachine + "virtual_machine_id": None, # Missing VirtualMachine }, ] @@ -108,13 +104,11 @@ class VirtualMachineMaintenanceApiTestCase(BaseAPITestCase): cls.valid_check_unique_data = [ { "maintenance_window": "2-12:00", - "assigned_object_type": cls.vm_ct.pk, - "assigned_object_id": cls.virtual_machine6.id, # Same Virtual Machine + "virtual_machine_id": cls.virtual_machine6.id, # Same Virtual Machine }, { "maintenance_window": "2-11:00", - "assigned_object_type": cls.vm_ct.pk, - "assigned_object_id": cls.virtual_machine6.id, # Same Virtual Machine + "virtual_machine_id": cls.virtual_machine6.id, # Same Virtual Machine } ] @@ -145,7 +139,7 @@ class VirtualMachineMaintenanceApiTestCase(BaseAPITestCase): for form_data in self.valid_check_unique_data: response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) - self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + self.assertHttpStatus(response, status.HTTP_500_INTERNAL_SERVER_ERROR) def test_create_invalid_maintenance_window(self): """Test creating invalid maintenance windows""" -- GitLab From b79480311435135e5bd1741713206f14bd633c1b Mon Sep 17 00:00:00 2001 From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu> Date: Tue, 4 Feb 2025 13:17:51 +0000 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=91=BD=EF=B8=8F=20Update=20API=20and?= =?UTF-8?q?=20tests=20for=20Provider=20Credentials?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- netbox_sys_plugin/api/serializers.py | 16 ++++++++-------- .../test_provider_credentials_api.py | 16 ++++++---------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py index c513556..97fa108 100644 --- a/netbox_sys_plugin/api/serializers.py +++ b/netbox_sys_plugin/api/serializers.py @@ -71,21 +71,21 @@ class ProviderCredentialsSerializer(NetBoxModelSerializer): id = serializers.IntegerField(read_only=True) cluster = SysClusterSerializer(source='assigned_object', read_only=True) - assigned_object_id = serializers.IntegerField(write_only=True) - assigned_object_type = serializers.PrimaryKeyRelatedField( - queryset=ContentType.objects.all(), - write_only=True, - ) + cluster_id = serializers.IntegerField(write_only=True) display = serializers.CharField(source="__str__",read_only=True) class Meta: model = ProviderCredentials - fields = '__all__' + fields = ['id','cluster','cluster_id','provider_username','provider_password_vault_path','tags'] + + def validate(self,data): + data["assigned_object_type"] = ContentType.objects.get_for_model(VirtualMachine) + return data def create(self, validated_data): - assigned_object_type = validated_data.pop("assigned_object_type") - validated_data["assigned_object_type"] = assigned_object_type + assigned_object_id = validated_data.pop("cluster_id") + validated_data["assigned_object_id"] = assigned_object_id return super().create(validated_data) class VirtualMachineTypeSerializer(NetBoxModelSerializer): diff --git a/netbox_sys_plugin/tests/provider_credentials/test_provider_credentials_api.py b/netbox_sys_plugin/tests/provider_credentials/test_provider_credentials_api.py index 18b2a87..d4243b3 100644 --- a/netbox_sys_plugin/tests/provider_credentials/test_provider_credentials_api.py +++ b/netbox_sys_plugin/tests/provider_credentials/test_provider_credentials_api.py @@ -10,7 +10,7 @@ from ..base import BaseAPITestCase class ProviderCredentialsApiTestCase(BaseAPITestCase): """Test suite for Provider Credentials API""" model = ProviderCredentials - brief_fields = ["provider_username","provider_password_vault_path", "assigned_object_id", "assigned_object_type"] + brief_fields = ["provider_username","provider_password_vault_path", "cluster_id", "assigned_object_type"] @classmethod @@ -51,8 +51,7 @@ class ProviderCredentialsApiTestCase(BaseAPITestCase): { "provider_username": "test_user3", "provider_password_vault_path": "test/path3", - "assigned_object_type": cls.clt_ct.pk, - "assigned_object_id": cls.cluster3.id, + "cluster_id": cls.cluster3.pk, } ] @@ -61,8 +60,7 @@ class ProviderCredentialsApiTestCase(BaseAPITestCase): { "provider_username": "test_user4", "provider_password_vault_path": "test/path4", - "assigned_object_type": cls.clt_ct.pk, - "assigned_object_id": None, # Missing Cluster + "cluster_id": None, # Missing Cluster }, ] @@ -71,14 +69,12 @@ class ProviderCredentialsApiTestCase(BaseAPITestCase): { "provider_username": "test_user_unique", "provider_password_vault_path": "test/path4", - "assigned_object_type": cls.clt_ct.pk, - "assigned_object_id": cls.cluster4.id, # Same Virtual Machine + "cluster_id": cls.cluster4.pk, # Same Virtual Machine }, { "provider_username": "test_user_unique", "provider_password_vault_path": "test/path4", - "assigned_object_type": cls.clt_ct.pk, - "assigned_object_id": cls.cluster4.id, # Same Virtual Machine + "cluster_id": cls.cluster4.pk, # Same Virtual Machine } ] @@ -110,7 +106,7 @@ class ProviderCredentialsApiTestCase(BaseAPITestCase): for form_data in self.valid_check_unique_data: response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) - self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + self.assertHttpStatus(response, status.HTTP_500_INTERNAL_SERVER_ERROR) def test_create_credentials_no_assignment(self): """Test creating invalid credentials windows""" -- GitLab From bf141b2d87b2e085f7d99b2f39e370ea79db0912 Mon Sep 17 00:00:00 2001 From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu> Date: Tue, 4 Feb 2025 13:57:48 +0000 Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=91=BD=EF=B8=8F=20Update=20API=20for?= =?UTF-8?q?=20Virtual=20Machine=20Type=20and=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- netbox_sys_plugin/api/serializers.py | 19 +++++++++---------- .../test_virtual_machine_type_api.py | 16 ++++++---------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py index 97fa108..34b61a7 100644 --- a/netbox_sys_plugin/api/serializers.py +++ b/netbox_sys_plugin/api/serializers.py @@ -51,7 +51,6 @@ class VirtualMachineMaintenanceSerializer(NetBoxModelSerializer): id = serializers.IntegerField(read_only=True) virtual_machine = NestedSysVirtualMachineSerializer(source='assigned_object', read_only=True) virtual_machine_id = serializers.IntegerField(write_only=True) - display = serializers.CharField(source="__str__",read_only=True) class Meta: @@ -80,7 +79,7 @@ class ProviderCredentialsSerializer(NetBoxModelSerializer): fields = ['id','cluster','cluster_id','provider_username','provider_password_vault_path','tags'] def validate(self,data): - data["assigned_object_type"] = ContentType.objects.get_for_model(VirtualMachine) + data["assigned_object_type"] = ContentType.objects.get_for_model(Cluster) return data def create(self, validated_data): @@ -91,20 +90,20 @@ class ProviderCredentialsSerializer(NetBoxModelSerializer): class VirtualMachineTypeSerializer(NetBoxModelSerializer): id = serializers.IntegerField(read_only=True) cluster_type = SysClusterTypeSerializer(source='assigned_object', read_only=True) - assigned_object_id = serializers.IntegerField(write_only=True) - assigned_object_type = serializers.PrimaryKeyRelatedField( - queryset=ContentType.objects.all(), - write_only=True, - ) + cluster_type_id = serializers.IntegerField(write_only=True) display = serializers.CharField(source="__str__",read_only=True) class Meta: model = VirtualMachineType - fields = '__all__' + fields = ['id','cluster_type','cluster_type_id','virtual_machine_type_name','virtual_machine_type_desc','tags'] + + def validate(self,data): + data["assigned_object_type"] = ContentType.objects.get_for_model(ClusterType) + return data def create(self, validated_data): - assigned_object_type = validated_data.pop("assigned_object_type") - validated_data["assigned_object_type"] = assigned_object_type + assigned_object_id = validated_data.pop("cluster_type_id") + validated_data["assigned_object_id"] = assigned_object_id return super().create(validated_data) class VmAssignedVirtualMachineTypeSerializer(NetBoxModelSerializer): diff --git a/netbox_sys_plugin/tests/virtual_machine_type/test_virtual_machine_type_api.py b/netbox_sys_plugin/tests/virtual_machine_type/test_virtual_machine_type_api.py index 43662c6..d0d80ce 100644 --- a/netbox_sys_plugin/tests/virtual_machine_type/test_virtual_machine_type_api.py +++ b/netbox_sys_plugin/tests/virtual_machine_type/test_virtual_machine_type_api.py @@ -10,7 +10,7 @@ from ..base import BaseAPITestCase class VitualMachineTypeApiTestCase(BaseAPITestCase): """Test suite for VmMachineType API""" model = VirtualMachineType - brief_fields = ["virtual_machine_type_name","virtual_machine_type_desc","assigned_object_id", "assigned_object_type"] + brief_fields = ["virtual_machine_type_name","virtual_machine_type_desc","cluster_type_id",] @classmethod @@ -43,8 +43,7 @@ class VitualMachineTypeApiTestCase(BaseAPITestCase): { "virtual_machine_type_name": "vm_type_name3", "virtual_machine_type_desc":"vm_type_desc_3", - "assigned_object_type": cls.cluster_type_ct.pk, - "assigned_object_id": cls.cluster_type.id, + "cluster_type_id": cls.cluster_type.id, } ] @@ -53,8 +52,7 @@ class VitualMachineTypeApiTestCase(BaseAPITestCase): { "virtual_machine_type_name": "vm_type_name4", "virtual_machine_type_desc":"vm_type_desc_4", - "assigned_object_type": cls.cluster_type_ct.pk, - "assigned_object_id": None, # Missing ClusterType + "cluster_type_id": None, # Missing ClusterType }, ] @@ -63,14 +61,12 @@ class VitualMachineTypeApiTestCase(BaseAPITestCase): { "virtual_machine_type_name": "vm_type_name4", # Same name "virtual_machine_type_desc":"vm_type_desc_different", - "assigned_object_type": cls.cluster_type_ct.pk, - "assigned_object_id": cls.cluster_type2.id, # Same ClusterType + "cluster_type_id": cls.cluster_type2.id, # Same ClusterType }, { "virtual_machine_type_name": "vm_type_name4", # Same name "virtual_machine_type_desc":"vm_type_desc_4", - "assigned_object_type": cls.cluster_type_ct.pk, - "assigned_object_id": cls.cluster_type2.id, # Same ClusterType + "cluster_type_id": cls.cluster_type2.id, # Same ClusterType } ] @@ -103,7 +99,7 @@ class VitualMachineTypeApiTestCase(BaseAPITestCase): for form_data in self.valid_check_unique_data: response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) - self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + self.assertHttpStatus(response, status.HTTP_500_INTERNAL_SERVER_ERROR) def test_create_invalid_machine_type(self): """Test creating invalid machine type""" -- GitLab From 868bc58f9a2b33a73f9ef29943a85c44673ff989 Mon Sep 17 00:00:00 2001 From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu> Date: Tue, 4 Feb 2025 16:14:03 +0000 Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=91=BD=EF=B8=8F=20Update=20domain=20n?= =?UTF-8?q?ames=20API=20and=20API=20testing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- netbox_sys_plugin/api/serializers.py | 16 ++++++++-------- .../vm_domain_names/test_vm_domain_names_api.py | 17 ++++++----------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py index 34b61a7..2d0237d 100644 --- a/netbox_sys_plugin/api/serializers.py +++ b/netbox_sys_plugin/api/serializers.py @@ -183,20 +183,20 @@ class VmAssignedVirtualMachineTypeSerializer(NetBoxModelSerializer): class DomainNamesSerializer(NetBoxModelSerializer): id = serializers.IntegerField(read_only=True) virtual_machine = NestedSysVirtualMachineSerializer(source='assigned_object', read_only=True) - assigned_object_id = serializers.IntegerField(write_only=True) - assigned_object_type = serializers.PrimaryKeyRelatedField( - queryset=ContentType.objects.all(), - write_only=True, - ) + virtual_machine_id = serializers.IntegerField(write_only=True) display = serializers.CharField(source="__str__",read_only=True) class Meta: model = DomainNames - fields = '__all__' + fields = ['id','virtual_machine','virtual_machine_id','domain_names','tags'] + + def validate(self,data): + data["assigned_object_type"] = ContentType.objects.get_for_model(VirtualMachine) + return data def create(self, validated_data): - assigned_object_type = validated_data.pop("assigned_object_type") - validated_data["assigned_object_type"] = assigned_object_type + assigned_object_id = validated_data.pop("virtual_machine_id") + validated_data["assigned_object_id"] = assigned_object_id return super().create(validated_data) class WebhookPayloadSerializer(NetBoxModelSerializer): diff --git a/netbox_sys_plugin/tests/vm_domain_names/test_vm_domain_names_api.py b/netbox_sys_plugin/tests/vm_domain_names/test_vm_domain_names_api.py index 34338a7..b680ccf 100644 --- a/netbox_sys_plugin/tests/vm_domain_names/test_vm_domain_names_api.py +++ b/netbox_sys_plugin/tests/vm_domain_names/test_vm_domain_names_api.py @@ -79,8 +79,7 @@ class VirtualMachinedomain_namesApiTestCase(BaseAPITestCase): cls.valid_create_data = [ { "domain_names": {'domain1':'ec.test.test','domain2':'ec.test2.test2'}, - "assigned_object_type": cls.vm_ct.pk, - "assigned_object_id": cls.virtual_machine3.id, + "virtual_machine_id": cls.virtual_machine3.id, } ] @@ -88,13 +87,11 @@ class VirtualMachinedomain_namesApiTestCase(BaseAPITestCase): cls.invalid_create_data = [ { "domain_names": None, - "assigned_object_type": cls.vm_ct.pk, - "assigned_object_id": cls.virtual_machine4.id, + "virtual_machine_id": cls.virtual_machine4.id, }, { "domain_names": {'domain1':'ec.test.test','domain2':'ec.test2.test2'}, - "assigned_object_type": cls.vm_ct.pk, - "assigned_object_id": None, # Missing VirtualMachine + "virtual_machine_id": None, # Missing VirtualMachine }, ] @@ -102,13 +99,11 @@ class VirtualMachinedomain_namesApiTestCase(BaseAPITestCase): cls.valid_check_unique_data = [ { "domain_names": {'domain1':'ec.test.test','domain2':'ec.test2.test2'}, - "assigned_object_type": cls.vm_ct.pk, - "assigned_object_id": cls.virtual_machine6.id, # Same Virtual Machine + "virtual_machine_id": cls.virtual_machine6.id, # Same Virtual Machine }, { "domain_names": {'domain1':'ec.test.test','domain2':'ec.test2.test2'}, - "assigned_object_type": cls.vm_ct.pk, - "assigned_object_id": cls.virtual_machine6.id, # Same Virtual Machine + "virtual_machine_id": cls.virtual_machine6.id, # Same Virtual Machine } ] @@ -139,7 +134,7 @@ class VirtualMachinedomain_namesApiTestCase(BaseAPITestCase): for form_data in self.valid_check_unique_data: response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) - self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + self.assertHttpStatus(response, status.HTTP_500_INTERNAL_SERVER_ERROR) def test_create_invalid_domain_names(self): """Test creating invalid domain names""" -- GitLab From c384726af83c3d058fd65092dd8a9baaed9d185c Mon Sep 17 00:00:00 2001 From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu> Date: Tue, 4 Feb 2025 17:07:05 +0000 Subject: [PATCH 5/9] =?UTF-8?q?=F0=9F=91=BD=EF=B8=8F=20Simplify=20Extra=20?= =?UTF-8?q?Config=20API=20and=20update=20api=20testing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- netbox_sys_plugin/api/serializers.py | 20 ++++++++----- .../test_provider_type_extra_config_api.py | 29 ++++--------------- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py index 2d0237d..ecc9924 100644 --- a/netbox_sys_plugin/api/serializers.py +++ b/netbox_sys_plugin/api/serializers.py @@ -13,6 +13,7 @@ from .nested_serializers import * from virtualization.choices import * from netbox.api.serializers import NetBoxModelSerializer from ..utils import validate_extra_config_values +from ..validators import validate_extra_config_structure #Netbox Data Serializer @@ -210,19 +211,24 @@ class WebhookPayloadSerializer(NetBoxModelSerializer): class ProviderTypeExtraConfigSerializer(NetBoxModelSerializer): id = serializers.IntegerField(read_only=True) extra_config_structure = serializers.JSONField() - assigned_object_type = serializers.PrimaryKeyRelatedField( - queryset=ContentType.objects.all(), - write_only=True, - ) + cluster_type = SysClusterTypeSerializer(source='assigned_object', read_only=True) + cluster_type_id = serializers.IntegerField(write_only=True) display = serializers.CharField(source="__str__",read_only=True) class Meta: model = ProviderTypeExtraConfig - fields = '__all__' + fields = ['id','cluster_type','cluster_type_id','extra_config_structure','extra_config_name','extra_config_description','tags'] + + def validate(self,data): + data["assigned_object_type"] = ContentType.objects.get_for_model(ClusterType) + extra_config = data.get("extra_config_structure") + if 'extra_config_structure' in data: + validate_extra_config_structure(extra_config) + return data def create(self, validated_data): - assigned_object_type = validated_data.pop("assigned_object_type") - validated_data["assigned_object_type"] = assigned_object_type + assigned_object_id = validated_data.pop("cluster_type_id") + validated_data["assigned_object_id"] = assigned_object_id return super().create(validated_data) class VmAssignedExtraConfigSerializer(NetBoxModelSerializer): diff --git a/netbox_sys_plugin/tests/provider_type_extra_config/test_provider_type_extra_config_api.py b/netbox_sys_plugin/tests/provider_type_extra_config/test_provider_type_extra_config_api.py index fb3071f..2dcc325 100644 --- a/netbox_sys_plugin/tests/provider_type_extra_config/test_provider_type_extra_config_api.py +++ b/netbox_sys_plugin/tests/provider_type_extra_config/test_provider_type_extra_config_api.py @@ -63,8 +63,7 @@ class ProviderTypeExtraConfigApiTestCase(BaseAPITestCase): "extra_config_name": "Test Config", "extra_config_structure": {'test_valid': [{'id': {'required': 'true','type': 'String'}}]}, "extra_config_description": "Test structure", - "assigned_object_type": cls.ct_ct.id, - "assigned_object_id": cls.cluster_type.pk, + "cluster_type_id": cls.cluster_type.pk, } ] @@ -74,24 +73,15 @@ class ProviderTypeExtraConfigApiTestCase(BaseAPITestCase): "extra_config_name": "Config Invalid 1", "extra_config_structure": {'test_invalid': {'id': {'required': 'true'}}}, #No dict root key "extra_config_description": "Test structure", - "assigned_object_id": cls.cluster_type2.pk, - "assigned_object_type": cls.ct_ct.id, + "cluster_type_id": cls.cluster_type2.pk, }, { "extra_config_name": "Config Invalid 2", "extra_config_structure": {'test_valid': [{'id': {'type': 'String'}}]}, #No required field "extra_config_description": "Test structure", - "assigned_object_id": cls.cluster_type3.pk, - "assigned_object_type": cls.ct_ct.id, + "cluster_type_id": cls.cluster_type3.pk, }, - { - "extra_config_name": "Config Invalid 4", - "extra_config_structure": {'test_valid': [{'id': {'required': 'true','type': 'String'}}]}, - "extra_config_description": "Test structure", - "assigned_object_id": cls.cluster_type5.pk, - "assigned_object_type": None #No assignment field - }, ] # Data for checking unique key @@ -100,15 +90,13 @@ class ProviderTypeExtraConfigApiTestCase(BaseAPITestCase): "extra_config_name": "Test Config uniq", "extra_config_structure": {'test_uniq': [{'id': {'required': 'true','type': 'String'}}]}, "extra_config_description": "Test structure", - "assigned_object_type": cls.ct_ct.pk, - "assigned_object_id": cls.cluster_type6.id, # Same Virtual Machine + "cluster_type_id": cls.cluster_type6.pk, # Same Virtual Machine }, { "extra_config_name": "Test Config uniq2", "extra_config_structure": {'test_uniq2': [{'id': {'required': 'true','type': 'String'}}]}, "extra_config_description": "Test structure", - "assigned_object_type": cls.ct_ct.pk, - "assigned_object_id": cls.cluster_type6.id, # Same Virtual Machine + "cluster_type_id": cls.cluster_type6.pk, # Same Virtual Machine } ] @@ -142,8 +130,7 @@ class ProviderTypeExtraConfigApiTestCase(BaseAPITestCase): for form_data in self.valid_check_unique_data: response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) - self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) - self.assertIn('The fields assigned_object_id must make a unique set.',str(response.data)) + self.assertHttpStatus(response, status.HTTP_500_INTERNAL_SERVER_ERROR) def test_create_invalid_extra_config(self): """Test creating invalid extra config""" @@ -165,10 +152,6 @@ class ProviderTypeExtraConfigApiTestCase(BaseAPITestCase): self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) self.assertIn('must contain `type` and `required` keys',str(response.data)) - form_data = self.invalid_create_data[2] - 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_update_extra_config(self): """Test updating an existing extra config""" -- GitLab From e83ccfeac3961d3cf406734e9bb85178e0b5fa23 Mon Sep 17 00:00:00 2001 From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu> Date: Tue, 4 Feb 2025 18:40:13 +0000 Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=91=BD=EF=B8=8F=20Simplify=20Assignme?= =?UTF-8?q?nt=20of=20extra=20config=20and=20api=20testing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- netbox_sys_plugin/api/serializers.py | 20 +++++++------- .../test_vm_assigned_extra_config_api.py | 26 +++++++------------ 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py index ecc9924..6a3b5de 100644 --- a/netbox_sys_plugin/api/serializers.py +++ b/netbox_sys_plugin/api/serializers.py @@ -96,7 +96,7 @@ class VirtualMachineTypeSerializer(NetBoxModelSerializer): class Meta: model = VirtualMachineType - fields = ['id','cluster_type','cluster_type_id','virtual_machine_type_name','virtual_machine_type_desc','tags'] + fields = ['id','display','cluster_type','cluster_type_id','virtual_machine_type_name','virtual_machine_type_desc','tags'] def validate(self,data): data["assigned_object_type"] = ContentType.objects.get_for_model(ClusterType) @@ -217,7 +217,7 @@ class ProviderTypeExtraConfigSerializer(NetBoxModelSerializer): class Meta: model = ProviderTypeExtraConfig - fields = ['id','cluster_type','cluster_type_id','extra_config_structure','extra_config_name','extra_config_description','tags'] + fields = ['id','cluster_type','display','cluster_type_id','extra_config_structure','extra_config_name','extra_config_description','tags'] def validate(self,data): data["assigned_object_type"] = ContentType.objects.get_for_model(ClusterType) @@ -234,20 +234,16 @@ class ProviderTypeExtraConfigSerializer(NetBoxModelSerializer): class VmAssignedExtraConfigSerializer(NetBoxModelSerializer): id = serializers.IntegerField(read_only=True) virtual_machine = NestedSysVirtualMachineSerializer(source='assigned_object', read_only=True) - assigned_object_id = serializers.IntegerField(write_only=True) - assigned_object_type = serializers.PrimaryKeyRelatedField( - queryset=ContentType.objects.all(), - write_only=True, - ) + virtual_machine_id = serializers.IntegerField(write_only=True) display = serializers.CharField(source="__str__",read_only=True) class Meta: model = VmAssignedExtraConfig - fields = '__all__' + fields = ['id','display','virtual_machine','virtual_machine_id','provider_type_extra_config','extra_config_values','provider_type_extra_config_assignment_desc','tags'] def create(self, validated_data): - assigned_object_type = validated_data.pop("assigned_object_type") - validated_data["assigned_object_type"] = assigned_object_type + assigned_object_id = validated_data.pop("virtual_machine_id") + validated_data["assigned_object_id"] = assigned_object_id return super().create(validated_data) def validate_extra_config_values(self, extra_config_values): @@ -284,10 +280,12 @@ class VmAssignedExtraConfigSerializer(NetBoxModelSerializer): data['extra_config_values'] = self.validate_extra_config_values(data.get('extra_config_values', {})) # Ensure a valid virtual machine assignment - if not self.partial and not data.get("assigned_object_id"): + if not self.partial and not data.get("virtual_machine_id"): raise serializers.ValidationError( {"__all__": "Can't assign more than one Extra Config to the same Virtual Machine"} ) + + data["assigned_object_type"] = ContentType.objects.get_for_model(VirtualMachine) return data diff --git a/netbox_sys_plugin/tests/vm_assigned_extra_config/test_vm_assigned_extra_config_api.py b/netbox_sys_plugin/tests/vm_assigned_extra_config/test_vm_assigned_extra_config_api.py index 7d0a2b2..f9510eb 100644 --- a/netbox_sys_plugin/tests/vm_assigned_extra_config/test_vm_assigned_extra_config_api.py +++ b/netbox_sys_plugin/tests/vm_assigned_extra_config/test_vm_assigned_extra_config_api.py @@ -10,7 +10,7 @@ from ..base import BaseAPITestCase class VmAssignedExtraConfigApiTestCase(BaseAPITestCase): """Test suite for VmAssignedExtraConfig API""" model = VmAssignedExtraConfig - brief_fields = ["extra_config_name","extra_config_structure","extra_config_description", "assigned_object_id", "assigned_object_type"] + brief_fields = ["extra_config_name","extra_config_structure","extra_config_description", "virtual_machine_id"] @classmethod @@ -113,8 +113,7 @@ class VmAssignedExtraConfigApiTestCase(BaseAPITestCase): { "extra_config_values":{'id': 'id2'}, "provider_type_extra_config_assignment_desc":"Test Assign 2", - "assigned_object_id":cls.virtual_machine3.pk, - "assigned_object_type": cls.vm_ct.pk, + "virtual_machine_id":cls.virtual_machine3.pk, "provider_type_extra_config":cls.structure2.pk } ] @@ -124,29 +123,25 @@ class VmAssignedExtraConfigApiTestCase(BaseAPITestCase): { "extra_config_values":{'desc': 'id2'}, #Wrong field "provider_type_extra_config_assignment_desc":"Test Assign 2", - "assigned_object_id":cls.virtual_machine4.pk, - "assigned_object_type": cls.vm_ct.pk, + "virtual_machine_id":cls.virtual_machine4.pk, "provider_type_extra_config":cls.structure2.pk }, { "extra_config_values":{'id': True}, #Wrong type "provider_type_extra_config_assignment_desc":"Test Assign 2", - "assigned_object_id":cls.virtual_machine5.pk, - "assigned_object_type": cls.vm_ct.pk, + "virtual_machine_id":cls.virtual_machine5.pk, "provider_type_extra_config":cls.structure2.pk }, { "extra_config_values":{'id': 'id2'}, "provider_type_extra_config_assignment_desc":"Test Assign 2", - "assigned_object_id":cls.virtual_machine7.pk, - "assigned_object_type": cls.vm_ct.pk, + "virtual_machine_id":cls.virtual_machine7.pk, "provider_type_extra_config":None #No Structure }, { "extra_config_values":{'id': 'id2'}, "provider_type_extra_config_assignment_desc":"Test Assign 2", - "assigned_object_id":None, #No Assignment - "assigned_object_type": cls.vm_ct.pk, + "virtual_machine_id":None, #No Assignment "provider_type_extra_config":cls.structure2.pk }, @@ -157,16 +152,14 @@ class VmAssignedExtraConfigApiTestCase(BaseAPITestCase): { "extra_config_values":{'id': 'id2'}, "provider_type_extra_config_assignment_desc":"Test Assign 2", - "assigned_object_id":cls.virtual_machine5.pk, - "assigned_object_type": cls.vm_ct.pk, + "virtual_machine_id":cls.virtual_machine5.pk, "provider_type_extra_config":cls.structure2.pk }, { "extra_config_values":{'id': 'id2'}, "provider_type_extra_config_assignment_desc":"Test Assign 2", - "assigned_object_id":cls.virtual_machine5.pk, - "assigned_object_type": cls.vm_ct.pk, + "virtual_machine_id":cls.virtual_machine5.pk, "provider_type_extra_config":cls.structure2.pk }, @@ -201,8 +194,7 @@ class VmAssignedExtraConfigApiTestCase(BaseAPITestCase): for form_data in self.valid_check_unique_data: response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) - self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) - self.assertIn('The fields assigned_object_id must make a unique set.',str(response.data)) + self.assertHttpStatus(response, status.HTTP_500_INTERNAL_SERVER_ERROR) def test_create_invalid_assigned_extra_config(self): """Test creating invalid extra config""" -- GitLab From 14455ff9e886fc99e881cb1c6b3529b5af156a2e Mon Sep 17 00:00:00 2001 From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu> Date: Wed, 5 Feb 2025 13:04:02 +0000 Subject: [PATCH 7/9] =?UTF-8?q?=E2=9C=85=20=F0=9F=91=BD=EF=B8=8F=20simplif?= =?UTF-8?q?y=20assign=20vm=20type=20API,=20add=20unique=20key=20error=20ha?= =?UTF-8?q?ndling=20and=20changes=20api=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- netbox_sys_plugin/api/serializers.py | 125 ++++++++++++------ .../test_provider_credentials_api.py | 2 +- .../test_provider_type_extra_config_api.py | 2 +- .../test_virtual_machine_type_api.py | 2 +- .../test_vm_assigned_extra_config_api.py | 2 +- ...st_vm_assigned_virtual_machine_type_api.py | 58 ++++++-- .../test_vm_domain_names_api.py | 2 +- .../vm_maintenance/test_vm_maintenance_api.py | 2 +- 8 files changed, 139 insertions(+), 56 deletions(-) diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py index 6a3b5de..9f059af 100644 --- a/netbox_sys_plugin/api/serializers.py +++ b/netbox_sys_plugin/api/serializers.py @@ -59,7 +59,18 @@ class VirtualMachineMaintenanceSerializer(NetBoxModelSerializer): fields = ['id','virtual_machine','virtual_machine_id','maintenance_window','tags'] def validate(self,data): + + # Validate uniqueness + assigned_object_id = data.get("virtual_machine_id") data["assigned_object_type"] = ContentType.objects.get_for_model(VirtualMachine) + assigned_object_type = data.get("assigned_object_type") + if VirtualMachineMaintenance.objects.filter( + assigned_object_id=assigned_object_id, + assigned_object_type=assigned_object_type + ).exists(): + raise serializers.ValidationError( + "A virtual Machine can only have one Maintenance Window" + ) return data def create(self, validated_data): @@ -80,7 +91,17 @@ class ProviderCredentialsSerializer(NetBoxModelSerializer): fields = ['id','cluster','cluster_id','provider_username','provider_password_vault_path','tags'] def validate(self,data): + + assigned_object_id = data.get("cluster_id") data["assigned_object_type"] = ContentType.objects.get_for_model(Cluster) + assigned_object_type = data.get("assigned_object_type") + if ProviderCredentials.objects.filter( + assigned_object_id=assigned_object_id, + assigned_object_type=assigned_object_type + ).exists(): + raise serializers.ValidationError( + "A Provider can only have one set of Credentials" + ) return data def create(self, validated_data): @@ -99,7 +120,19 @@ class VirtualMachineTypeSerializer(NetBoxModelSerializer): fields = ['id','display','cluster_type','cluster_type_id','virtual_machine_type_name','virtual_machine_type_desc','tags'] def validate(self,data): + assigned_object_id = data.get("cluster_type_id") + type_name = data.get("virtual_machine_type_name") data["assigned_object_type"] = ContentType.objects.get_for_model(ClusterType) + assigned_object_type = data.get("assigned_object_type") + if VirtualMachineType.objects.filter( + assigned_object_id=assigned_object_id, + virtual_machine_type_name=type_name, + assigned_object_type=assigned_object_type + ).exists(): + raise serializers.ValidationError( + "The name of the Type must be unique per Proviver Type" + ) + return data def create(self, validated_data): @@ -112,50 +145,33 @@ class VmAssignedVirtualMachineTypeSerializer(NetBoxModelSerializer): Serializer for VmAssignedVirtualMachineType. Includes nested representations for related objects. """ + id = serializers.IntegerField(read_only=True) + virtual_machine = NestedSysVirtualMachineSerializer(source='assigned_object', read_only=True) + type = NestedVirtualMachineTypeSerializer(source='virtual_machine_type', read_only=True) + virtual_machine_id = serializers.IntegerField(write_only=True) virtual_machine_type = serializers.PrimaryKeyRelatedField( queryset=VirtualMachineType.objects.all() ) - assigned_object_type = serializers.PrimaryKeyRelatedField( - queryset=ContentType.objects.all() - ) - assigned_object_id = serializers.IntegerField() + # Optional: Add any extra fields for enhanced representation - virtual_machine_type_name = serializers.SerializerMethodField() - assigned_object = serializers.SerializerMethodField() class Meta: model = VmAssignedVirtualMachineType fields = [ 'id', + 'virtual_machine', + 'virtual_machine_id', 'virtual_machine_type', - 'virtual_machine_type_name', + 'type', 'virtual_machine_type_assignment_desc', - 'assigned_object_type', - 'assigned_object_id', - 'assigned_object', - 'created', - 'last_updated', + 'tags', ] - def get_virtual_machine_type_name(self, obj): - """Get the name of the related VirtualMachineType.""" - return obj.virtual_machine_type.virtual_machine_type_name if obj.virtual_machine_type else None - - def get_assigned_object(self, obj): - """Get the assigned object representation.""" - assigned_object = obj.assigned_object - if isinstance(assigned_object, VirtualMachine): - return { - "id": assigned_object.id, - "name": assigned_object.name, - "cluster": assigned_object.cluster.name if assigned_object.cluster else None, - } - return None - def validate(self, data): virtual_machine_type = data.get("virtual_machine_type") - assigned_object_id = data.get("assigned_object_id") + assigned_object_id = data.get("virtual_machine_id") + data["assigned_object_type"] = ContentType.objects.get_for_model(VirtualMachine) assigned_object_type = data.get("assigned_object_type") # Validate uniqueness @@ -165,21 +181,25 @@ class VmAssignedVirtualMachineTypeSerializer(NetBoxModelSerializer): assigned_object_type=assigned_object_type ).exists(): raise serializers.ValidationError( - "The combination of virtual_machine_type, assigned_object_id, and assigned_object_type must be unique." + "The combination of virtual_machine_type, virtual_machine_id must be unique." ) # Ensure VirtualMachineType.assigned_object matches the ClusterType of the Cluster associated with the VirtualMachine - if assigned_object_type.model == "virtualmachine": - virtual_machine = VirtualMachine.objects.get(id=assigned_object_id) - cluster_type_of_vm = virtual_machine.cluster.type # Get the ClusterType of the Cluster - virtual_machine_type_cluster_type = virtual_machine_type.assigned_object # ClusterType linked to the VirtualMachineType - - if cluster_type_of_vm != virtual_machine_type_cluster_type: - raise serializers.ValidationError( - "The ClusterType of the Cluster where the VirtualMachine is assigned " - "must match the ClusterType of the VirtualMachineType." - ) + + virtual_machine = VirtualMachine.objects.get(id=assigned_object_id) + cluster_type_of_vm = virtual_machine.cluster.type # Get the ClusterType of the Cluster + virtual_machine_type_cluster_type = virtual_machine_type.assigned_object # ClusterType linked to the VirtualMachineType + if cluster_type_of_vm != virtual_machine_type_cluster_type: + raise serializers.ValidationError( + "The ClusterType of the Cluster where the VirtualMachine is assigned " + "must match the ClusterType of the VirtualMachineType." + ) return data + + def create(self, validated_data): + assigned_object_id = validated_data.pop("virtual_machine_id") + validated_data["assigned_object_id"] = assigned_object_id + return super().create(validated_data) class DomainNamesSerializer(NetBoxModelSerializer): id = serializers.IntegerField(read_only=True) @@ -192,7 +212,16 @@ class DomainNamesSerializer(NetBoxModelSerializer): fields = ['id','virtual_machine','virtual_machine_id','domain_names','tags'] def validate(self,data): + assigned_object_id = data.get("virtual_machine_id") data["assigned_object_type"] = ContentType.objects.get_for_model(VirtualMachine) + assigned_object_type = data.get("assigned_object_type") + if DomainNames.objects.filter( + assigned_object_id=assigned_object_id, + assigned_object_type=assigned_object_type + ).exists(): + raise serializers.ValidationError( + "A virtual Machine can only have one set of domain names" + ) return data def create(self, validated_data): @@ -220,7 +249,16 @@ class ProviderTypeExtraConfigSerializer(NetBoxModelSerializer): fields = ['id','cluster_type','display','cluster_type_id','extra_config_structure','extra_config_name','extra_config_description','tags'] def validate(self,data): + assigned_object_id = data.get("cluster_type_id") data["assigned_object_type"] = ContentType.objects.get_for_model(ClusterType) + assigned_object_type = data.get("assigned_object_type") + if ProviderTypeExtraConfig.objects.filter( + assigned_object_id=assigned_object_id, + assigned_object_type=assigned_object_type + ).exists(): + raise serializers.ValidationError( + "A Proviver Type can only have one set of extra configurations" + ) extra_config = data.get("extra_config_structure") if 'extra_config_structure' in data: validate_extra_config_structure(extra_config) @@ -285,7 +323,16 @@ class VmAssignedExtraConfigSerializer(NetBoxModelSerializer): {"__all__": "Can't assign more than one Extra Config to the same Virtual Machine"} ) + assigned_object_id = data.get("virtual_machine_id") data["assigned_object_type"] = ContentType.objects.get_for_model(VirtualMachine) + assigned_object_type = data.get("assigned_object_type") + if VmAssignedExtraConfig.objects.filter( + assigned_object_id=assigned_object_id, + assigned_object_type=assigned_object_type + ).exists(): + raise serializers.ValidationError( + "A virtual Machine can only have one set Extra Configurations" + ) return data diff --git a/netbox_sys_plugin/tests/provider_credentials/test_provider_credentials_api.py b/netbox_sys_plugin/tests/provider_credentials/test_provider_credentials_api.py index d4243b3..beb1be7 100644 --- a/netbox_sys_plugin/tests/provider_credentials/test_provider_credentials_api.py +++ b/netbox_sys_plugin/tests/provider_credentials/test_provider_credentials_api.py @@ -106,7 +106,7 @@ class ProviderCredentialsApiTestCase(BaseAPITestCase): for form_data in self.valid_check_unique_data: response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) - self.assertHttpStatus(response, status.HTTP_500_INTERNAL_SERVER_ERROR) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) def test_create_credentials_no_assignment(self): """Test creating invalid credentials windows""" diff --git a/netbox_sys_plugin/tests/provider_type_extra_config/test_provider_type_extra_config_api.py b/netbox_sys_plugin/tests/provider_type_extra_config/test_provider_type_extra_config_api.py index 2dcc325..53053ed 100644 --- a/netbox_sys_plugin/tests/provider_type_extra_config/test_provider_type_extra_config_api.py +++ b/netbox_sys_plugin/tests/provider_type_extra_config/test_provider_type_extra_config_api.py @@ -130,7 +130,7 @@ class ProviderTypeExtraConfigApiTestCase(BaseAPITestCase): for form_data in self.valid_check_unique_data: response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) - self.assertHttpStatus(response, status.HTTP_500_INTERNAL_SERVER_ERROR) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) def test_create_invalid_extra_config(self): """Test creating invalid extra config""" diff --git a/netbox_sys_plugin/tests/virtual_machine_type/test_virtual_machine_type_api.py b/netbox_sys_plugin/tests/virtual_machine_type/test_virtual_machine_type_api.py index d0d80ce..2150f51 100644 --- a/netbox_sys_plugin/tests/virtual_machine_type/test_virtual_machine_type_api.py +++ b/netbox_sys_plugin/tests/virtual_machine_type/test_virtual_machine_type_api.py @@ -99,7 +99,7 @@ class VitualMachineTypeApiTestCase(BaseAPITestCase): for form_data in self.valid_check_unique_data: response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) - self.assertHttpStatus(response, status.HTTP_500_INTERNAL_SERVER_ERROR) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) def test_create_invalid_machine_type(self): """Test creating invalid machine type""" diff --git a/netbox_sys_plugin/tests/vm_assigned_extra_config/test_vm_assigned_extra_config_api.py b/netbox_sys_plugin/tests/vm_assigned_extra_config/test_vm_assigned_extra_config_api.py index f9510eb..de1b3a5 100644 --- a/netbox_sys_plugin/tests/vm_assigned_extra_config/test_vm_assigned_extra_config_api.py +++ b/netbox_sys_plugin/tests/vm_assigned_extra_config/test_vm_assigned_extra_config_api.py @@ -194,7 +194,7 @@ class VmAssignedExtraConfigApiTestCase(BaseAPITestCase): for form_data in self.valid_check_unique_data: response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) - self.assertHttpStatus(response, status.HTTP_500_INTERNAL_SERVER_ERROR) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) def test_create_invalid_assigned_extra_config(self): """Test creating invalid extra config""" diff --git a/netbox_sys_plugin/tests/vm_assigned_virtual_machine_type/test_vm_assigned_virtual_machine_type_api.py b/netbox_sys_plugin/tests/vm_assigned_virtual_machine_type/test_vm_assigned_virtual_machine_type_api.py index 3850ea7..8e07ced 100644 --- a/netbox_sys_plugin/tests/vm_assigned_virtual_machine_type/test_vm_assigned_virtual_machine_type_api.py +++ b/netbox_sys_plugin/tests/vm_assigned_virtual_machine_type/test_vm_assigned_virtual_machine_type_api.py @@ -10,7 +10,7 @@ from ..base import BaseAPITestCase class VmAssignedVirtualMachineTypeApiTestCase(BaseAPITestCase): """Test suite for VmAssignedVirtualMachineType API""" model = VmAssignedVirtualMachineType - brief_fields = ["virtual_machine_type", "assigned_object_id", "assigned_object_type"] + brief_fields = ["virtual_machine_type", "virtual_machine_id", "virtual_machine_type_assignment_desc"] @classmethod def setUpTestData(cls): @@ -48,6 +48,12 @@ class VmAssignedVirtualMachineTypeApiTestCase(BaseAPITestCase): cluster=cls.cluster_vmware ) + cls.virtual_machine5 = VirtualMachine.objects.create( + name="Test VM4", + status="active", + cluster=cls.cluster_aws + ) + # Create VirtualMachineTypes cls.virtual_machine_type_aws = VirtualMachineType.objects.create( virtual_machine_type_name='AWS', @@ -72,8 +78,22 @@ class VmAssignedVirtualMachineTypeApiTestCase(BaseAPITestCase): cls.valid_create_data = [ { "virtual_machine_type": cls.virtual_machine_type_aws.pk, - "assigned_object_id": cls.virtual_machine2.id, - "assigned_object_type": cls.vm_ct.pk + "virtual_machine_id": cls.virtual_machine2.id, + "virtual_machine_type_assignment_desc": "Test Assignement", + } + ] + + # Data for valid creation + cls.unique_create_data = [ + { + "virtual_machine_type": cls.virtual_machine_type_aws.pk, + "virtual_machine_id": cls.virtual_machine5.id, + "virtual_machine_type_assignment_desc": "Test unqiue 1", + }, + { + "virtual_machine_type": cls.virtual_machine_type_aws.pk, + "virtual_machine_id": cls.virtual_machine5.id, + "virtual_machine_type_assignment_desc": "Test unique 2", } ] @@ -81,13 +101,13 @@ class VmAssignedVirtualMachineTypeApiTestCase(BaseAPITestCase): cls.invalid_create_data = [ { "virtual_machine_type": None, - "assigned_object_type": cls.vm_ct.pk, - "assigned_object_id": cls.virtual_machine4.id, + "virtual_machine_id": cls.virtual_machine4.id, + "virtual_machine_type_assignment_desc": "Test Assignement", }, { "virtual_machine_type": cls.virtual_machine_type_vmware.id, # assign virtual_machine_type diffrent than ClusertType of VM - "assigned_object_type": cls.vm_ct.pk, - "assigned_object_id": cls.virtual_machine3.id, + "virtual_machine_id": cls.virtual_machine3.pk, + "virtual_machine_type_assignment_desc": "Test Assignement", }, ] @@ -104,9 +124,27 @@ class VmAssignedVirtualMachineTypeApiTestCase(BaseAPITestCase): 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["assigned_object_id"], form_data["assigned_object_id"]) self.assertEqual(response.data["virtual_machine_type"], form_data["virtual_machine_type"]) + def test_unique_key_on_virtual_machine_type_assignment_(self): + """Test unique key on VmAssignedVirtualMachineType""" + obj_perm = ObjectPermission( + name="Create VmAssignedVirtualMachineType Permission", + actions=["add", "view"], + ) + obj_perm.save() + obj_perm.users.add(self.user) + obj_perm.object_types.add(ContentType.objects.get_for_model(VmAssignedVirtualMachineType)) + + form_data = self.unique_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["virtual_machine_type"], form_data["virtual_machine_type"]) + + form_data = self.unique_create_data[1] + response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) + def test_update_virtual_machine_type_assignment(self): """Test updating an existing VmAssignedVirtualMachineType""" vm_assignment = VmAssignedVirtualMachineType.objects.first() @@ -120,14 +158,12 @@ class VmAssignedVirtualMachineTypeApiTestCase(BaseAPITestCase): update_data = { "virtual_machine_type": self.virtual_machine_type_aws.id, - "assigned_object_id": self.virtual_machine.id, - "assigned_object_type": self.vm_ct.pk + "virtual_machine_id": self.virtual_machine.pk, } response = self.client.patch(self._get_detail_url(vm_assignment), update_data, format="json", **self.header) self.assertHttpStatus(response, status.HTTP_200_OK) self.assertEqual(response.data["virtual_machine_type"], update_data["virtual_machine_type"]) - self.assertEqual(response.data["assigned_object_id"], update_data["assigned_object_id"]) def test_create_invalid_virtual_machine_type_assignment(self): """Test creating invalid VmAssignedVirtualMachineType""" diff --git a/netbox_sys_plugin/tests/vm_domain_names/test_vm_domain_names_api.py b/netbox_sys_plugin/tests/vm_domain_names/test_vm_domain_names_api.py index b680ccf..738a32f 100644 --- a/netbox_sys_plugin/tests/vm_domain_names/test_vm_domain_names_api.py +++ b/netbox_sys_plugin/tests/vm_domain_names/test_vm_domain_names_api.py @@ -134,7 +134,7 @@ class VirtualMachinedomain_namesApiTestCase(BaseAPITestCase): for form_data in self.valid_check_unique_data: response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) - self.assertHttpStatus(response, status.HTTP_500_INTERNAL_SERVER_ERROR) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) def test_create_invalid_domain_names(self): """Test creating invalid domain names""" diff --git a/netbox_sys_plugin/tests/vm_maintenance/test_vm_maintenance_api.py b/netbox_sys_plugin/tests/vm_maintenance/test_vm_maintenance_api.py index 24b88d0..65110f7 100644 --- a/netbox_sys_plugin/tests/vm_maintenance/test_vm_maintenance_api.py +++ b/netbox_sys_plugin/tests/vm_maintenance/test_vm_maintenance_api.py @@ -139,7 +139,7 @@ class VirtualMachineMaintenanceApiTestCase(BaseAPITestCase): for form_data in self.valid_check_unique_data: response = self.client.post(self._get_list_url(), form_data, format="json", **self.header) - self.assertHttpStatus(response, status.HTTP_500_INTERNAL_SERVER_ERROR) + self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST) def test_create_invalid_maintenance_window(self): """Test creating invalid maintenance windows""" -- GitLab From 837dea7b93c228b8d0b6dfb2a79d8c43fc79061a Mon Sep 17 00:00:00 2001 From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu> Date: Wed, 5 Feb 2025 17:32:38 +0000 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=93=9D=20Updated=20API=20documentatio?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/definition/models/domainNames.smithy | 24 +-- .../models/providerCredentials.smithy | 45 ++---- .../models/providerTypeExtraConfig.smithy | 56 +++---- docs/definition/models/sys.smithy | 2 +- .../models/virtualMachineMaintenance.smithy | 34 ++-- .../models/virtualMachineType.smithy | 23 +-- .../models/vmAssignedExtraConfig.smithy | 23 +-- .../vmAssignedVirtualMachineType.smithy | 35 ++--- docs/definition/models/webhookPayload.smithy | 120 ++++++++++++++ docs/definition/models/webhooksettings.smithy | 147 ------------------ 10 files changed, 203 insertions(+), 306 deletions(-) create mode 100644 docs/definition/models/webhookPayload.smithy delete mode 100644 docs/definition/models/webhooksettings.smithy diff --git a/docs/definition/models/domainNames.smithy b/docs/definition/models/domainNames.smithy index 57dce0e..64b1771 100644 --- a/docs/definition/models/domainNames.smithy +++ b/docs/definition/models/domainNames.smithy @@ -10,8 +10,7 @@ resource domainNames { } properties: { domain_names: Document - assigned_object_id: Ids - assigned_object_type: String + virtual_machine_id: Ids } read: GetDomainNames @@ -37,9 +36,7 @@ operation GetDomainNames { @required $id @required - $assigned_object_id - @required - $assigned_object_type + $virtual_machine_id $domain_names } @@ -53,9 +50,7 @@ operation CreateDomainNames { input := for domainNames { @required - $assigned_object_id - @required - $assigned_object_type + $virtual_machine_id @required $domain_names } @@ -64,9 +59,7 @@ operation CreateDomainNames { @required $id @required - $assigned_object_id - @required - $assigned_object_type + $virtual_machine_id @required $domain_names } @@ -84,11 +77,7 @@ operation UpdateDomainNames { @httpLabel $id - @required - $assigned_object_id - @required - $assigned_object_type - + $virtual_machine_id $domain_names } @@ -149,9 +138,8 @@ structure VmdomainNames for domainNames { $id @required - $assigned_object_id + $virtual_machine_id @required - $assigned_object_type $domain_names } diff --git a/docs/definition/models/providerCredentials.smithy b/docs/definition/models/providerCredentials.smithy index 2ee9b09..7b682a6 100644 --- a/docs/definition/models/providerCredentials.smithy +++ b/docs/definition/models/providerCredentials.smithy @@ -9,10 +9,9 @@ resource providerCredentials { id: Ids } properties: { - assigned_object_type_id: Ids - assigned_object_id: String + cluster_id: String provider_url: String - provider_name: String + provider_username: String provider_password_vault_url: String } @@ -37,13 +36,11 @@ operation GetProviderCredentials { output := for providerCredentials { @required - $assigned_object_type_id - @required - $assigned_object_id + $cluster_id @required $provider_url @required - $provider_name + $provider_username @required $provider_password_vault_url } @@ -55,15 +52,10 @@ operation GetProviderCredentials { @http(method: "POST", uri: "/api/plugins/sys/ProviderCredentials/") operation CreateProviderCredentials { input := for providerCredentials { - - @required - $assigned_object_type_id - @required - $assigned_object_id @required - $provider_url + $cluster_id @required - $provider_name + $provider_username @required $provider_password_vault_url } @@ -72,13 +64,9 @@ operation CreateProviderCredentials { @required $id @required - $assigned_object_type_id - @required - $assigned_object_id - @required - $provider_url + $cluster_id @required - $provider_name + $provider_username @required $provider_password_vault_url } @@ -96,13 +84,9 @@ operation UpdateProviderCredentials { @httpLabel $id @required - $assigned_object_type_id - @required - $assigned_object_id - @required - $provider_url + $cluster_id @required - $provider_name + $provider_username @required $provider_password_vault_url } @@ -161,15 +145,10 @@ list ProviderCredentialsList { structure ProviderCredentialsSummary for providerCredentials { @required $id - - @required - $assigned_object_type_id - @required - $assigned_object_id @required - $provider_url + $cluster_id @required - $provider_name + $provider_username @required $provider_password_vault_url } diff --git a/docs/definition/models/providerTypeExtraConfig.smithy b/docs/definition/models/providerTypeExtraConfig.smithy index 4f06599..4aa55e6 100644 --- a/docs/definition/models/providerTypeExtraConfig.smithy +++ b/docs/definition/models/providerTypeExtraConfig.smithy @@ -11,8 +11,8 @@ resource providerTypeExtraConfig { properties: { extra_config_description: String extra_config_structure: String - assigned_object_id: Ids - assigned_object_type: String + cluster_type_id: Ids + extra_config_name: String } read: GetproviderTypeExtraConfig @@ -26,7 +26,7 @@ resource providerTypeExtraConfig { @tags(["SYS"]) @documentation("Show Provider Type Extra Config based on ID") @readonly -@http(method: "GET", uri: "/api/plugins/sys/ExtraConfig/{id}/") +@http(method: "GET", uri: "/api/plugins/sys/ProviderTypeExtraConfig/{id}/") operation GetproviderTypeExtraConfig { input := for providerTypeExtraConfig { @required @@ -38,30 +38,30 @@ operation GetproviderTypeExtraConfig { @required $id @required - $assigned_object_id - @required - $assigned_object_type + $cluster_type_id @required $extra_config_structure - + @required + $extra_config_name + @required $extra_config_description } } @tags(["SYS"]) -@documentation("Show Provider Type Extra Config information") +@documentation("Create Provider Type Extra Config information") @idempotent -@http(method: "POST", uri: "/api/plugins/sys/ExtraConfig/") +@http(method: "POST", uri: "/api/plugins/sys/ProviderTypeExtraConfig/") operation CreateproviderTypeExtraConfig { input := for providerTypeExtraConfig { @required - $assigned_object_id - @required - $assigned_object_type + $cluster_type_id @required $extra_config_structure - + @required + $extra_config_name + @required $extra_config_description } @@ -69,12 +69,12 @@ operation CreateproviderTypeExtraConfig { @required $id @required - $assigned_object_id - @required - $assigned_object_type + $cluster_type_id @required $extra_config_structure - + @required + $extra_config_name + @required $extra_config_description } @@ -84,7 +84,7 @@ operation CreateproviderTypeExtraConfig { @tags(["SYS"]) @documentation("Update Provider Type Extra Config") -@http(method: "PATCH", uri: "/api/plugins/sys/ExtraConfig/{id}/") +@http(method: "PATCH", uri: "/api/plugins/sys/ProviderTypeExtraConfig/{id}/") operation UpdateproviderTypeExtraConfig { input := for providerTypeExtraConfig { @required @@ -92,12 +92,12 @@ operation UpdateproviderTypeExtraConfig { $id @required - $assigned_object_id - @required - $assigned_object_type + $cluster_type_id @required $extra_config_structure - + @required + $extra_config_name + @required $extra_config_description } @@ -108,7 +108,7 @@ operation UpdateproviderTypeExtraConfig { @tags(["SYS"]) @documentation("Delete Provider Type Extra Config") @idempotent -@http(method: "DELETE", uri: "/api/plugins/sys/ExtraConfig/{id}/") +@http(method: "DELETE", uri: "/api/plugins/sys/ProviderTypeExtraConfig/{id}/") operation DeleteproviderTypeExtraConfig { input := for providerTypeExtraConfig { @required @@ -120,7 +120,7 @@ operation DeleteproviderTypeExtraConfig { @tags(["SYS"]) @documentation("List Provider Type Extra Config") @readonly -@http(method: "GET",uri: "/api/plugins/sys/ExtraConfig/") +@http(method: "GET",uri: "/api/plugins/sys/ProviderTypeExtraConfig/") @paginated(inputToken: "next", outputToken: "next", pageSize: "limit", items: "results") operation ListproviderTypeExtraConfig { input := for providerTypeExtraConfig { @@ -157,12 +157,12 @@ structure ProviderTypeExtraConfigSummary for providerTypeExtraConfig { $id @required - $assigned_object_id - @required - $assigned_object_type + $cluster_type_id @required $extra_config_structure - + @required + $extra_config_name + @required $extra_config_description } diff --git a/docs/definition/models/sys.smithy b/docs/definition/models/sys.smithy index f680893..157d24e 100644 --- a/docs/definition/models/sys.smithy +++ b/docs/definition/models/sys.smithy @@ -9,7 +9,7 @@ use aws.protocols#restJson1 @restJson1 service sys { version: "0.0.1", - resources: [vmMaintenance,vmType,vmAssignmentType,providerCredentials,domainNames,providerTypeExtraConfig,vmAssignmentExtraConfig,webhookSettings] + resources: [vmMaintenance,vmType,vmAssignmentType,providerCredentials,domainNames,providerTypeExtraConfig,vmAssignmentExtraConfig,webhookPayload] } diff --git a/docs/definition/models/virtualMachineMaintenance.smithy b/docs/definition/models/virtualMachineMaintenance.smithy index 25fead0..cf9b3bb 100644 --- a/docs/definition/models/virtualMachineMaintenance.smithy +++ b/docs/definition/models/virtualMachineMaintenance.smithy @@ -10,8 +10,7 @@ resource vmMaintenance { } properties: { maintenance_window: String - assigned_object_id: Ids - assigned_object_type: String + virtual_machine_id: Ids } read: GetVmMaintenance @@ -25,7 +24,7 @@ resource vmMaintenance { @tags(["SYS"]) @documentation("Show Virtual Machine Maintenance information based on ID") @readonly -@http(method: "GET", uri: "/api/plugins/sys/VmMaintenance/{id}/") +@http(method: "GET", uri: "/api/plugins/sys/MaintenanceWindow/{id}/") operation GetVmMaintenance { input := for vmMaintenance { @required @@ -37,25 +36,21 @@ operation GetVmMaintenance { @required $id @required - $assigned_object_id - @required - $assigned_object_type + $virtual_machine_id $maintenance_window } } @tags(["SYS"]) -@documentation("Show Virtual Machine Maintenance information") +@documentation("Create Virtual Machine Maintenance information") @idempotent -@http(method: "POST", uri: "/api/plugins/sys/VmMaintenance/") +@http(method: "POST", uri: "/api/plugins/sys/MaintenanceWindow/") operation CreateVmMaintenance { input := for vmMaintenance { @required - $assigned_object_id - @required - $assigned_object_type + $virtual_machine_id $maintenance_window } @@ -64,9 +59,7 @@ operation CreateVmMaintenance { @required $id @required - $assigned_object_id - @required - $assigned_object_type + $virtual_machine_id $maintenance_window } @@ -77,7 +70,7 @@ operation CreateVmMaintenance { @tags(["SYS"]) @documentation("Update Virtual Machine Maintenance information") -@http(method: "PATCH", uri: "/api/plugins/sys/VmMaintenance/{id}/") +@http(method: "PATCH", uri: "/api/plugins/sys/MaintenanceWindow/{id}/") operation UpdateVmMaintenance { input := for vmMaintenance { @required @@ -85,9 +78,7 @@ operation UpdateVmMaintenance { $id @required - $assigned_object_id - @required - $assigned_object_type + $virtual_machine_id $maintenance_window } @@ -99,7 +90,7 @@ operation UpdateVmMaintenance { @tags(["SYS"]) @documentation("Delete Virtual Machine Maintenance information") @idempotent -@http(method: "DELETE", uri: "/api/plugins/sys/VmMaintenance/{id}/") +@http(method: "DELETE", uri: "/api/plugins/sys/MaintenanceWindow/{id}/") operation DeleteVmMaintenance { input := for vmMaintenance { @required @@ -112,7 +103,7 @@ operation DeleteVmMaintenance { @tags(["SYS"]) @documentation("List Virtual Machine Maintenance Info") @readonly -@http(method: "GET",uri: "/api/plugins/sys/VmMaintenance/") +@http(method: "GET",uri: "/api/plugins/sys/MaintenanceWindow/") @paginated(inputToken: "next", outputToken: "next", pageSize: "limit", items: "results") operation ListVmMaintenance { input := for vmMaintenance { @@ -149,9 +140,8 @@ structure Vmaintenance for vmMaintenance { $id @required - $assigned_object_id + $virtual_machine_id @required - $assigned_object_type $maintenance_window } diff --git a/docs/definition/models/virtualMachineType.smithy b/docs/definition/models/virtualMachineType.smithy index 90ce66a..e762d49 100644 --- a/docs/definition/models/virtualMachineType.smithy +++ b/docs/definition/models/virtualMachineType.smithy @@ -11,8 +11,7 @@ resource vmType { properties: { virtual_machine_type_name: String virtual_machine_type_desc: String - assigned_object_id: Ids - assigned_object_type: String + cluster_type_id: Ids } read: GetvmType @@ -38,9 +37,7 @@ operation GetvmType { @required $id @required - $assigned_object_id - @required - $assigned_object_type + $cluster_type_id @required $virtual_machine_type_name @@ -56,9 +53,7 @@ operation CreatevmType { input := for vmType { @required - $assigned_object_id - @required - $assigned_object_type + $cluster_type_id @required $virtual_machine_type_name @@ -69,9 +64,7 @@ operation CreatevmType { @required $id @required - $assigned_object_id - @required - $assigned_object_type + $cluster_type_id @required $virtual_machine_type_name @@ -92,9 +85,7 @@ operation UpdatevmType { $id @required - $assigned_object_id - @required - $assigned_object_type + $cluster_type_id @required $virtual_machine_type_name @@ -157,9 +148,7 @@ structure VmTypeSummary for vmType { $id @required - $assigned_object_id - @required - $assigned_object_type + $cluster_type_id @required $virtual_machine_type_name $virtual_machine_type_desc diff --git a/docs/definition/models/vmAssignedExtraConfig.smithy b/docs/definition/models/vmAssignedExtraConfig.smithy index da4e885..c4073f2 100644 --- a/docs/definition/models/vmAssignedExtraConfig.smithy +++ b/docs/definition/models/vmAssignedExtraConfig.smithy @@ -9,11 +9,10 @@ resource vmAssignmentExtraConfig { id: Ids } properties: { + virtual_machine_id: Ids provider_type_extra_config_id: Ids provider_type_extra_config_assignment_desc: String extra_config_values: String - assigned_object_id: Ids - assigned_object_type: String } read: GetvmAssignmentExtraConfig @@ -39,9 +38,7 @@ operation GetvmAssignmentExtraConfig { @required $id @required - $assigned_object_id - @required - $assigned_object_type + $virtual_machine_id @required $provider_type_extra_config_id @required @@ -59,9 +56,7 @@ operation CreatevmAssignmentExtraConfig { input := for vmAssignmentExtraConfig { @required - $assigned_object_id - @required - $assigned_object_type + $virtual_machine_id @required $provider_type_extra_config_id @required @@ -74,9 +69,7 @@ operation CreatevmAssignmentExtraConfig { @required $id @required - $assigned_object_id - @required - $assigned_object_type + $virtual_machine_id @required $provider_type_extra_config_id @required @@ -99,9 +92,7 @@ operation UpdatevmAssignmentExtraConfig { $id @required - $assigned_object_id - @required - $assigned_object_type + $virtual_machine_id @required $provider_type_extra_config_id @required @@ -166,9 +157,7 @@ structure vmAssignmentExtraConfigSummary for vmAssignmentExtraConfig { $id @required - $assigned_object_id - @required - $assigned_object_type + $virtual_machine_id @required $provider_type_extra_config_id @required diff --git a/docs/definition/models/vmAssignedVirtualMachineType.smithy b/docs/definition/models/vmAssignedVirtualMachineType.smithy index 11aa2d5..f9bf778 100644 --- a/docs/definition/models/vmAssignedVirtualMachineType.smithy +++ b/docs/definition/models/vmAssignedVirtualMachineType.smithy @@ -9,10 +9,9 @@ resource vmAssignmentType { id: Ids } properties: { - virtual_machine_type_assignment_id: Ids + virtual_machine_type_id: Ids virtual_machine_type_assignment_desc: String - assigned_object_id: Ids - assigned_object_type: String + virtual_machine_id: Ids } read: GetvmAssignmentType @@ -38,11 +37,9 @@ operation GetvmAssignmentType { @required $id @required - $assigned_object_id + $virtual_machine_id @required - $assigned_object_type - @required - $virtual_machine_type_assignment_id + $virtual_machine_type_id $virtual_machine_type_assignment_desc } @@ -56,11 +53,9 @@ operation CreatevmAssignmentType { input := for vmAssignmentType { @required - $assigned_object_id - @required - $assigned_object_type + $virtual_machine_id @required - $virtual_machine_type_assignment_id + $virtual_machine_type_id $virtual_machine_type_assignment_desc } @@ -69,11 +64,9 @@ operation CreatevmAssignmentType { @required $id @required - $assigned_object_id - @required - $assigned_object_type + $virtual_machine_id @required - $virtual_machine_type_assignment_id + $virtual_machine_type_id $virtual_machine_type_assignment_desc } @@ -92,11 +85,9 @@ operation UpdatevmAssignmentType { $id @required - $assigned_object_id + $virtual_machine_id @required - $assigned_object_type - @required - $virtual_machine_type_assignment_id + $virtual_machine_type_id $virtual_machine_type_assignment_desc } @@ -157,12 +148,10 @@ structure vmAssignmentTypeSummary for vmAssignmentType { $id @required - $assigned_object_id + $virtual_machine_id @required - $assigned_object_type + $virtual_machine_type_id @required - $virtual_machine_type_assignment_id - $virtual_machine_type_assignment_desc } diff --git a/docs/definition/models/webhookPayload.smithy b/docs/definition/models/webhookPayload.smithy new file mode 100644 index 0000000..b309262 --- /dev/null +++ b/docs/definition/models/webhookPayload.smithy @@ -0,0 +1,120 @@ +$version: "2.0" + +namespace eu.europa.ec.snet.sys + +@tags(["SYS"]) +@documentation("Resource - Webhook Payload") +resource webhookPayload { + identifiers: { + id: Ids + } + properties: { + payload_data: Url + + } + read: GetwebhookPayload + create: CreatewebhookPayload + delete: DeletewebhookPayload + list: ListwebhookPayload +} + + +@tags(["SYS"]) +@documentation("Show Webhook Payload based on ID") +@readonly +@http(method: "GET", uri: "/api/plugins/sys/webhookPayload/{id}/") +operation GetwebhookPayload { + input := for webhookPayload { + @required + @httpLabel + $id + } + + output := for webhookPayload { + @required + $id + @required + $payload_data + + + } +} + +@tags(["SYS"]) +@documentation("Show Webhook Payload information") +@idempotent +@http(method: "POST", uri: "/api/plugins/sys/webhookPayload/") +operation CreatewebhookPayload { + input := for webhookPayload { + + @required + $payload_data + + } + + output := for webhookPayload { + @required + $id + @required + $payload_data + + } + + errors: [NotUnique,WrongAssignedobject] +} + +@tags(["SYS"]) +@documentation("Delete Webhook Payload") +@idempotent +@http(method: "DELETE", uri: "/api/plugins/sys/webhookPayload/{id}/") +operation DeletewebhookPayload { + input := for webhookPayload { + @required + @httpLabel + $id + } +} + +@tags(["SYS"]) +@documentation("List Webhook Payload") +@readonly +@http(method: "GET",uri: "/api/plugins/sys/webhookPayload/") +@paginated(inputToken: "next", outputToken: "next", pageSize: "limit", items: "results") +operation ListwebhookPayload { + input := for webhookPayload { + @httpQuery("limit") + limit: Integer + @httpQuery("offset") + offset: String + @httpQuery("ordering") + next: String + } + + output := for webhookPayload { + count: Integer + next: String + previous: String + @required + results: webhookPayloadList + } +} + +@documentation("Unique constraint to prevent duplication of IDs") +@uniqueItems +list webhookPayloadList { + + member: webhookPayloadSummary +} +@references( +[ + {resource: webhookPayload} +] +) +structure webhookPayloadSummary for webhookPayload { + @required + $id + + @required + $payload_data +} + diff --git a/docs/definition/models/webhooksettings.smithy b/docs/definition/models/webhooksettings.smithy deleted file mode 100644 index 79b6c73..0000000 --- a/docs/definition/models/webhooksettings.smithy +++ /dev/null @@ -1,147 +0,0 @@ -$version: "2.0" - -namespace eu.europa.ec.snet.sys - -@tags(["SYS"]) -@documentation("Resource - Webhook Settings") -resource webhookSettings { - identifiers: { - id: Ids - } - properties: { - payload_url: Url - http_content_type: String - - } - read: GetwebhookSettings - create: CreatewebhookSettings - update: UpdatewebhookSettings - delete: DeletewebhookSettings - list: ListwebhookSettings -} - - -@tags(["SYS"]) -@documentation("Show Webhook Settings based on ID") -@readonly -@http(method: "GET", uri: "/api/plugins/sys/WebhookSettings/{id}/") -operation GetwebhookSettings { - input := for webhookSettings { - @required - @httpLabel - $id - } - - output := for webhookSettings { - @required - $id - @required - $payload_url - @required - $http_content_type - - } -} - -@tags(["SYS"]) -@documentation("Show Webhook Settings information") -@idempotent -@http(method: "POST", uri: "/api/plugins/sys/WebhookSettings/") -operation CreatewebhookSettings { - input := for webhookSettings { - - @required - $payload_url - @required - $http_content_type - } - - output := for webhookSettings { - @required - $id - @required - $payload_url - @required - $http_content_type - } - - errors: [NotUnique,WrongAssignedobject] -} - - -@tags(["SYS"]) -@documentation("Update Webhook Settings") -@http(method: "PATCH", uri: "/api/plugins/sys/WebhookSettings/{id}/") -operation UpdatewebhookSettings { - input := for webhookSettings { - @required - @httpLabel - $id - - @required - $payload_url - @required - $http_content_type - } - - errors: [NotUnique,WrongAssignedobject] -} - - -@tags(["SYS"]) -@documentation("Delete Webhook Settings") -@idempotent -@http(method: "DELETE", uri: "/api/plugins/sys/WebhookSettings/{id}/") -operation DeletewebhookSettings { - input := for webhookSettings { - @required - @httpLabel - $id - } -} - -@tags(["SYS"]) -@documentation("List Webhook Settings") -@readonly -@http(method: "GET",uri: "/api/plugins/sys/WebhookSettings/") -@paginated(inputToken: "next", outputToken: "next", pageSize: "limit", items: "results") -operation ListwebhookSettings { - input := for webhookSettings { - @httpQuery("limit") - limit: Integer - @httpQuery("offset") - offset: String - @httpQuery("ordering") - next: String - } - - output := for webhookSettings { - count: Integer - next: String - previous: String - @required - results: WebhookSettingsList - } -} - -@documentation("Unique constraint to prevent duplication of IDs") -@uniqueItems -list WebhookSettingsList { - - member: WebhookSettingsSummary -} -@references( -[ - {resource: webhookSettings} -] -) -structure WebhookSettingsSummary for webhookSettings { - @required - $id - - @required - $payload_url - @required - $http_content_type -} - -- GitLab From 2572190b9c2d2d41e6981c2805c31c489addb222 Mon Sep 17 00:00:00 2001 From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu> Date: Thu, 6 Feb 2025 12:16:51 +0000 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=93=9D=20Add=20prerequisite=20data=20?= =?UTF-8?q?documentation=20and=20update=20data=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/how_to_use.md | 76 +++++++++++++++++++ ...tings.md => netbox_sys_webhook_payload.md} | 3 +- 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 docs/how_to_use.md rename docs/model/{netbox_sys_webhook_settings.md => netbox_sys_webhook_payload.md} (63%) diff --git a/docs/how_to_use.md b/docs/how_to_use.md new file mode 100644 index 0000000..7925d2d --- /dev/null +++ b/docs/how_to_use.md @@ -0,0 +1,76 @@ +# How to use the SYS Plugin + +This Netbox plugin is used to store and manage the information necessary for the [Create VM](https://code.europa.eu/digit-c4/sys-service-catalogue/-/blob/main/docs/source/smithy/models/resources/VirtualMachine.smithy?ref_type=heads) service. + +## Mapping Information +|STRUCTURE NAME| FIELD | TYPE | DESCRIPTION |NETBOX FIELD MAPPING | +|--------------| ----------------- | ------------|-------------|----------------------| +|PROVIDER_CREDENTIALS| URL | STRING | Provider URL |Mapped using `Virtualization`>`Cluster`>`Name` | +|PROVIDER_CREDENTIALS| USER | STRING | Provider User |Mapped in SYS plugin **Provider Credentials** table. Table connected with `Cluster` Table | +|PROVIDER_CREDENTIALS| PASSWORD | STRING | Provider Password |Mapped in SYS plugin **Provider Credentials** table. Table connected with `Cluster` Table | +|PROVIDER_CREDENTIALS| REGION | STRING | Provider Region/ What cluster or cloud provider regions |Mapped using `Sites` and connected to `Virtualization`>`Cluster` | +|| PROVIDER_TYPE | STRING | Provider Type |Mapped using `Virtualization`>`Virtual Machines`>`Cluster Type` | +|| NAME | STRING | Virtual Machine Name |Mapped using `Virtualization`>`Virtual Machines`>`Name` | +|| TYPE | STRING | Virtual Machine type (Small, Medium, Big, etc) |Mapped in SYS plugin **Virtual Machine Type** table. This table is connected with the `Cluster Type` table and gets assigned to a `Virtual Machine` in the table **VM Assigned Virtual Machine Type** | +|NETWORK| IP_ADDRESS | STRING | IP Address |Mapped using `IPAM`>`IP Addresses`>`IP Address`. | +|NETWORK| DOMAIN_NAME | STRING | Domain Name |Mapped in SYS plugin **Domain Names** table. Table connected with `Virtual Machine` Table | +|NETWORK| GATEWAY | STRING | Gateway |Mapped using `IPAM`>`IP Addresses` with the description "Gateway" | +|NETWORK| NETMASK | STRING | Netmask |Mapped using `IPAM`>`IP Addresses`. Parsing the value in front of the "\\" from the IP address | +|NETWORK| INTERFACE_NAME | STRING | Interface Name |Mapped using `Virtualization`>`Virtual Machines`>`Interfaces`>`Name` | +|| OWNER | STRING | Owner / Which Squad (CMDB, SYS, RPS, etc) |Mapped using `Role` in Virtual Machine | +|| PLATFORM | STRING | Platform |Mapped using `Devices`>`Platforms`. Connection to Virtual Machine done in the `Management` Section during the creation of a Virtual Machine| +|| MAINTENANCE_WINDOW | STRING | Maintenance Window (weekdayTime) |Mapped in SYS plugin **VM Maintenance** table. Table connected with `Virtual Machine` Table| +|DEPENDENCIES| NTP_SERVER | STRING | Network Time Protocol Server |Mapped using a created record in the `IPAM`>`Services`| +|DEPENDENCIES| DNS_SERVER | JSON | Domain Name System Server |Mapped using a created record in the `IPAM`>`Services`| +|DEPENDENCIES| SYSLOG_SERVER | STRING | Syslog Server |Mapped using a created record in the `IPAM`>`Services`| +|PROVIDER TYPE SPECIFIC EXTRA INFO|VMWARE, AWSCREATE etc| JSON | Provider Type extra information |Mapped in SYS plugin **Provider Type Extra Config** table. This table is connected with the `Cluster Type` table and gets assigned to a `Virtual Machine` in the table **VM Assigned Extra Config** | + + +## Prerequisite data initialization +Since the plugin uses its own data model connected with the netbox core data model there is some data that needs to be inserted in both data models for it to be ready: + +|Data| Description | Data model | +|--------------| ----------------- | ------------| +| Cluster Types | Provider Types (aws, vmware, ...) |Netbox data model +| Virtual Machine Types | Virtual Machine Types assigned to provider types (Big, Small, Medium,...)|Plugin data model| +| Extra Configs(structures)| Extra set of fields assigned to Provider Types |Plugin data model| +| Clusters | Providers|Netbox data model| +| Provider Credentials | Credentials to access the Provider (Username and Vault Password Path) |Plugin data model| +| Sites |Save the region of the VM|Netbox data model| +| Roles |Save owner of the VM|Netbox data model| +| Tags |Tag to identify all the objects created with the plugin|Netbox data model| + + + +### Extra Configuration structure + +The Extra Configuration Structure follows a set of rules that are defined as validation in the UI and API that help the user set this value. + +The template shows how this structure needs to be set: + +```json +{ + "structure_name": [ + { + "field_name": { + "type": "String", + "required": "true" + }, + "field_name2": { + "type": "Integer", + "required": "false" + }, + "field_name3": { + "type": "Boolean", + "required": "true" + } + } + ] +} +``` +Rules: +- The structure must be a dictionary. +- The structure must contain exactly one key. +- The "structure_name" must be a list. +- Each field in the list must be a dictionary. +- Each field must contain `type` and `required` keys. diff --git a/docs/model/netbox_sys_webhook_settings.md b/docs/model/netbox_sys_webhook_payload.md similarity index 63% rename from docs/model/netbox_sys_webhook_settings.md rename to docs/model/netbox_sys_webhook_payload.md index 680b800..643cee5 100644 --- a/docs/model/netbox_sys_webhook_settings.md +++ b/docs/model/netbox_sys_webhook_payload.md @@ -5,7 +5,6 @@ | FIELD | TYPE | DESCRIPTION | |----------------------------------------------------|----------------------------------|---------------------------------------------------------------------------| | id | Big (8 byte) integer | Unique ID | -| payload_url | String (URL ) | URL will be called using the POST method when the webhook is called | -| http_content_type | String (100) | Http content type used in the webhook. Default value "application/json" | +| payload_data | Json | Information from all the objects created with the CreateVM Form | -- GitLab