From 302a4052d6849b2aeee03ff46ad4d86403de5442 Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Mon, 10 Feb 2025 15:15:06 +0000
Subject: [PATCH 1/9] =?UTF-8?q?=F0=9F=91=BD=EF=B8=8F=20=E2=9C=A8=20Add=20n?=
 =?UTF-8?q?ew=20POST=20endpoint=20to=20create=20a=20VM?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/api/serializers.py | 125 ++++++++++++++++++++++++++-
 netbox_sys_plugin/api/urls.py        |   1 +
 netbox_sys_plugin/api/views.py       |  35 +++++++-
 3 files changed, 159 insertions(+), 2 deletions(-)

diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py
index 9f059af..8691831 100644
--- a/netbox_sys_plugin/api/serializers.py
+++ b/netbox_sys_plugin/api/serializers.py
@@ -1,5 +1,6 @@
 from rest_framework import serializers
 from virtualization.models import VirtualMachine, Cluster, ClusterType
+from dcim.models import DeviceRole, Platform
 from netbox.api.fields import ChoiceField
 from dcim.api.nested_serializers import (
     NestedDeviceSerializer, NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedSiteSerializer,
@@ -14,6 +15,8 @@ from virtualization.choices import *
 from netbox.api.serializers import NetBoxModelSerializer
 from ..utils import validate_extra_config_values
 from ..validators import validate_extra_config_structure
+from ..forms import CreateVmForm
+import json
 
 #Netbox Data Serializer
 
@@ -396,4 +399,124 @@ class VirtualMachineSerializer(NetBoxModelSerializer):
         vm_assigned_extra_config = VmAssignedExtraConfig.objects.filter(assigned_object_type=ContentType.objects.get_for_model(VirtualMachine),
             assigned_object_id=obj.id)
         return NestedVmAssignedExtraConfigSerializer(vm_assigned_extra_config, many=True).data
-    
\ No newline at end of file
+
+class CreateVmSerializer(serializers.Serializer):
+    """Serializer to handle Create VM requests."""
+    cluster_type = serializers.CharField(required=True)
+    cluster = serializers.CharField(required=True)
+    name = serializers.CharField(required=True, max_length=50)
+    status = serializers.CharField(required=True, max_length=50)
+    role = serializers.CharField(required=True)
+    platform = serializers.CharField(required=True)
+    description = serializers.CharField(required=False, allow_blank=True)
+    virtual_machine_type = serializers.CharField(required=True)
+    interface_name = serializers.CharField(required=True, max_length=50)
+    ip_addresses = serializers.CharField(required=True, max_length=50)
+    ip_addresses_status = serializers.CharField(required=True, max_length=50)
+    gateway = serializers.CharField(required=True, max_length=50)
+    gateway_status = serializers.CharField(required=True, max_length=50)
+    domain_names = serializers.JSONField(required=False)
+    extra_config_structure = serializers.CharField(required=True)
+    extra_config_values = serializers.JSONField(required=False)
+    ntp_service_name = serializers.CharField(required=True, max_length=50)
+    ntp_service_protocol = serializers.CharField(required=True, max_length=50)
+    ntp_service_ports = serializers.CharField(required=True, max_length=50)
+    dns_service_name = serializers.CharField(required=True, max_length=50)
+    dns_service_protocol = serializers.CharField(required=True, max_length=50)
+    dns_service_ports = serializers.CharField(required=True, max_length=50)
+    syslog_service_name = serializers.CharField(required=True, max_length=50)
+    syslog_service_protocol = serializers.CharField(required=True, max_length=50)
+    syslog_service_ports = serializers.CharField(required=True, max_length=50)
+    #tags = serializers.ListField(child=serializers.IntegerField(), required=False)
+
+    class Meta:
+        fields = '__all__'
+
+    def validate(self, data):
+        request = self.context.get("request")
+        if not request or not request.user.has_perm("virtualization.add_virutal_machine"):
+            raise serializers.ValidationError("You dont have permission to create a VM")
+    
+        cluster_type = data.pop('cluster_type', None)
+        data['cl_list_new-cluster_type']=cluster_type
+        
+        cluster = data.pop('cluster', None)
+        data['cl_list_new-cluster']=cluster
+
+        name = data.pop('name', None)
+        data['vms_new-0-name']=name
+
+        status = data.pop('status', None)
+        data['vms_new-0-status']=status
+
+        role = data.pop('role', None)
+        data['vms_new-0-role']=role
+        
+        platform = data.pop('platform', None)
+        data['vms_new-0-platform']=platform
+
+        description = data.pop('description', None)
+        data['vms_new-0-description']=description
+
+        virtual_machine_type = data.pop('virtual_machine_type', None)
+        data['vmassignedvmtype_new-virtual_machine_type']=virtual_machine_type
+        
+        interface_name = data.pop('interface_name', None)
+        data['vmis_new-0-name']=interface_name
+        
+        ip_addresses = data.pop('ip_addresses', None)
+        data['ip_new-address']=ip_addresses
+
+        ip_addresses_status = data.pop('ip_addresses_status', None)
+        data['ip_new-status']=ip_addresses_status
+
+        gateway = data.pop('gateway', None)
+        data['gateway_new-address']=gateway
+
+        gateway_status = data.pop('gateway_status', None)
+        data['gateway_new-status']=gateway_status
+        
+        domain_names = data.pop('domain_names', None)
+        data['domainnames_new-domain_names']=json.dumps(domain_names)
+        
+        extra_config_structure = data.pop('extra_config_structure', None)
+        data['vmassignedextraconfig_new-provider_type_extra_config']=extra_config_structure
+
+        extra_config_values = data.pop('extra_config_values', None)
+        data['vmassignedextraconfig_new-extra_config_values']=json.dumps(extra_config_values)
+
+        ntp_service_name = data.pop('ntp_service_name', None)
+        data['service_ntp-0-name']=ntp_service_name
+
+        ntp_service_protocol = data.pop('ntp_service_protocol', None)
+        data['service_ntp-0-protocol']=ntp_service_protocol
+        
+        ntp_service_ports = data.pop('ntp_service_ports', None)
+        data['service_ntp-0-ports']=ntp_service_ports
+        
+        dns_service_name = data.pop('dns_service_name', None)
+        data['service_dns-0-name']=dns_service_name
+
+        dns_service_protocol = data.pop('dns_service_protocol', None)
+        data['service_dns-0-protocol']=dns_service_protocol
+
+        dns_service_ports = data.pop('dns_service_ports', None)
+        data['service_dns-0-ports']=dns_service_ports
+
+        syslog_service_name = data.pop('syslog_service_name', None)
+        data['service_syslog-0-name']=syslog_service_name
+        
+        syslog_service_protocol = data.pop('syslog_service_protocol', None)
+        data['service_syslog-0-protocol']=syslog_service_protocol
+        
+        syslog_service_ports = data.pop('syslog_service_ports', None)
+        data['service_syslog-0-ports']=syslog_service_ports
+        data = super().validate(data)
+        return data
+
+    def create(self, data):
+        """Process VM creation using CreateVmForm."""
+        form_data = data
+        form = CreateVmForm(data=form_data)
+        vm = form.process_creation(form_data)
+        return vm
\ No newline at end of file
diff --git a/netbox_sys_plugin/api/urls.py b/netbox_sys_plugin/api/urls.py
index b6d7df9..67c639c 100644
--- a/netbox_sys_plugin/api/urls.py
+++ b/netbox_sys_plugin/api/urls.py
@@ -12,6 +12,7 @@ router.register(r'ProviderTypeExtraConfig', ProviderTypeExtraConfigViewSet)
 router.register(r'VirtualMachine', VirtualMachineViewSet, basename='virtualmachine')
 router.register(r'ExtraConfig', ProviderTypeExtraConfigViewSet)
 router.register(r'AssignedExtraConfig', VmAssignedExtraConfigViewSet)
+router.register(r'CreateVM', CreateVmViewSet)
 
 app_name = 'netbox_sys_plugin'
 
diff --git a/netbox_sys_plugin/api/views.py b/netbox_sys_plugin/api/views.py
index c407a54..c209153 100644
--- a/netbox_sys_plugin/api/views.py
+++ b/netbox_sys_plugin/api/views.py
@@ -1,9 +1,10 @@
 from rest_framework import viewsets, status
-from rest_framework.decorators import action
+from rest_framework.permissions import IsAuthenticated
 from rest_framework.response import Response
 from django.db.models import Prefetch
 from netbox.api.viewsets import NetBoxModelViewSet
 from virtualization import filtersets
+from django.db import transaction
 from .. filtersets import VmTypeFilterSet, ProviderTypeExtraConfigFilterSet
 from .. models import VirtualMachineMaintenance, ProviderCredentials, VmAssignedVirtualMachineType, VirtualMachineType,DomainNames, ProviderTypeExtraConfig, WebhookPayload
 from . serializers import *
@@ -66,3 +67,35 @@ class VmAssignedExtraConfigViewSet(viewsets.ModelViewSet):
     serializer_class = VmAssignedExtraConfigSerializer
     http_method_names = ["get", "post", "patch", "delete", "options"]
 
+class CreateVmViewSet(NetBoxModelViewSet):
+    """API ViewSet to handle VM creation."""
+    queryset = WebhookPayload.objects.all()
+    serializer_class = CreateVmSerializer
+    permission_classes = [IsAuthenticated]
+    http_method_names = ["post"]
+
+    def create_vm(self, request):
+        serializer = self.get_serializer(data=request.data)
+        if serializer.is_valid():
+            try:
+                with transaction.atomic():
+                    vm = serializer.save()
+                    return Response({
+                        "message": "VM created successfully!",
+                        "virtual_machine": {
+                            "id": vm.id,
+                            "name": vm.name,
+                            "status": vm.status,
+                            "role": vm.role.name if vm.role else None,
+                            "platform": vm.platform.name if vm.platform else None,
+                            "description": vm.description,
+                            "domain_names": vm.domain_names,
+                            "extra_config_values": vm.extra_config_values,
+                            "virtual_machine_type": vm.virtual_machine_type.id if vm.virtual_machine_type else None,
+                            "services": vm.services,
+                            "ip_addresses": vm.ip_addresses,
+                        },
+                    }, status=status.HTTP_201_CREATED)
+            except ValueError as e:
+                return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
+        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
-- 
GitLab


From 1cb1deb9375da34afcb64721309397510c157376 Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Mon, 10 Feb 2025 15:39:37 +0000
Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=90=9B=20Fix=20naming=20and=20tests?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/api/serializers.py | 2 +-
 netbox_sys_plugin/api/urls.py        | 2 +-
 netbox_sys_plugin/api/views.py       | 7 +++----
 3 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py
index 8691831..a2b5592 100644
--- a/netbox_sys_plugin/api/serializers.py
+++ b/netbox_sys_plugin/api/serializers.py
@@ -400,7 +400,7 @@ class VirtualMachineSerializer(NetBoxModelSerializer):
             assigned_object_id=obj.id)
         return NestedVmAssignedExtraConfigSerializer(vm_assigned_extra_config, many=True).data
 
-class CreateVmSerializer(serializers.Serializer):
+class CreateVirtualMachineSerializer(serializers.Serializer):
     """Serializer to handle Create VM requests."""
     cluster_type = serializers.CharField(required=True)
     cluster = serializers.CharField(required=True)
diff --git a/netbox_sys_plugin/api/urls.py b/netbox_sys_plugin/api/urls.py
index 67c639c..67be6be 100644
--- a/netbox_sys_plugin/api/urls.py
+++ b/netbox_sys_plugin/api/urls.py
@@ -12,7 +12,7 @@ router.register(r'ProviderTypeExtraConfig', ProviderTypeExtraConfigViewSet)
 router.register(r'VirtualMachine', VirtualMachineViewSet, basename='virtualmachine')
 router.register(r'ExtraConfig', ProviderTypeExtraConfigViewSet)
 router.register(r'AssignedExtraConfig', VmAssignedExtraConfigViewSet)
-router.register(r'CreateVM', CreateVmViewSet)
+router.register(r'CreateVirtualMachine', CreateVirtualMachineViewSet,basename='CreteVirtualMachine')
 
 app_name = 'netbox_sys_plugin'
 
diff --git a/netbox_sys_plugin/api/views.py b/netbox_sys_plugin/api/views.py
index c209153..6984939 100644
--- a/netbox_sys_plugin/api/views.py
+++ b/netbox_sys_plugin/api/views.py
@@ -67,14 +67,13 @@ class VmAssignedExtraConfigViewSet(viewsets.ModelViewSet):
     serializer_class = VmAssignedExtraConfigSerializer
     http_method_names = ["get", "post", "patch", "delete", "options"]
 
-class CreateVmViewSet(NetBoxModelViewSet):
+class CreateVirtualMachineViewSet(NetBoxModelViewSet):
     """API ViewSet to handle VM creation."""
-    queryset = WebhookPayload.objects.all()
-    serializer_class = CreateVmSerializer
+    serializer_class = CreateVirtualMachineSerializer
     permission_classes = [IsAuthenticated]
     http_method_names = ["post"]
 
-    def create_vm(self, request):
+    def create_virtual_machine(self, request):
         serializer = self.get_serializer(data=request.data)
         if serializer.is_valid():
             try:
-- 
GitLab


From e15c66222e9c2ef45bef30e349a0d104d2206ad2 Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Mon, 10 Feb 2025 17:06:45 +0000
Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=90=9B=20Fix=20permission=20bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/api/serializers.py | 21 +++++++++++++--------
 netbox_sys_plugin/api/views.py       | 22 ++++++----------------
 2 files changed, 19 insertions(+), 24 deletions(-)

diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py
index a2b5592..63455c7 100644
--- a/netbox_sys_plugin/api/serializers.py
+++ b/netbox_sys_plugin/api/serializers.py
@@ -1,6 +1,6 @@
 from rest_framework import serializers
+from django.contrib.auth.models import User
 from virtualization.models import VirtualMachine, Cluster, ClusterType
-from dcim.models import DeviceRole, Platform
 from netbox.api.fields import ChoiceField
 from dcim.api.nested_serializers import (
     NestedDeviceSerializer, NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedSiteSerializer,
@@ -430,13 +430,19 @@ class CreateVirtualMachineSerializer(serializers.Serializer):
     #tags = serializers.ListField(child=serializers.IntegerField(), required=False)
 
     class Meta:
-        fields = '__all__'
+        fields = ['cluster_type','cluster','name','status','role','platform'
+                  ,'description','virtual_machine_type','interface_name','ip_addresses','ip_addresses_status'
+                  ,'gateway','gateway_status','domain_names','extra_config_structure','extra_config_values'
+                  ,'ntp_service_name','ntp_service_protocol','ntp_service_ports'
+                  ,'dns_service_name','dns_service_protocol','dns_service_ports'
+                  ,'syslog_service_name','syslog_service_protocol','syslog_service_ports'
+                  ]
 
     def validate(self, data):
         request = self.context.get("request")
-        if not request or not request.user.has_perm("virtualization.add_virutal_machine"):
+        if not request or not request.user.has_perm("virtualization.add_virtualmachine"):
             raise serializers.ValidationError("You dont have permission to create a VM")
-    
+
         cluster_type = data.pop('cluster_type', None)
         data['cl_list_new-cluster_type']=cluster_type
         
@@ -511,12 +517,11 @@ class CreateVirtualMachineSerializer(serializers.Serializer):
         
         syslog_service_ports = data.pop('syslog_service_ports', None)
         data['service_syslog-0-ports']=syslog_service_ports
+
         data = super().validate(data)
         return data
 
     def create(self, data):
         """Process VM creation using CreateVmForm."""
-        form_data = data
-        form = CreateVmForm(data=form_data)
-        vm = form.process_creation(form_data)
-        return vm
\ No newline at end of file
+
+        return data
\ No newline at end of file
diff --git a/netbox_sys_plugin/api/views.py b/netbox_sys_plugin/api/views.py
index 6984939..fd91921 100644
--- a/netbox_sys_plugin/api/views.py
+++ b/netbox_sys_plugin/api/views.py
@@ -1,5 +1,5 @@
 from rest_framework import viewsets, status
-from rest_framework.permissions import IsAuthenticated
+from rest_framework.permissions import IsAuthenticated, DjangoModelPermissions
 from rest_framework.response import Response
 from django.db.models import Prefetch
 from netbox.api.viewsets import NetBoxModelViewSet
@@ -69,31 +69,21 @@ class VmAssignedExtraConfigViewSet(viewsets.ModelViewSet):
 
 class CreateVirtualMachineViewSet(NetBoxModelViewSet):
     """API ViewSet to handle VM creation."""
+    queryset = Cluster.objects.all()
     serializer_class = CreateVirtualMachineSerializer
-    permission_classes = [IsAuthenticated]
+    permission_classes = [IsAuthenticated, DjangoModelPermissions]
     http_method_names = ["post"]
 
-    def create_virtual_machine(self, request):
+    def create(self, request):
         serializer = self.get_serializer(data=request.data)
         if serializer.is_valid():
             try:
                 with transaction.atomic():
                     vm = serializer.save()
+                    form = CreateVmForm(data=vm)
+                    vm = form.process_creation(vm)
                     return Response({
                         "message": "VM created successfully!",
-                        "virtual_machine": {
-                            "id": vm.id,
-                            "name": vm.name,
-                            "status": vm.status,
-                            "role": vm.role.name if vm.role else None,
-                            "platform": vm.platform.name if vm.platform else None,
-                            "description": vm.description,
-                            "domain_names": vm.domain_names,
-                            "extra_config_values": vm.extra_config_values,
-                            "virtual_machine_type": vm.virtual_machine_type.id if vm.virtual_machine_type else None,
-                            "services": vm.services,
-                            "ip_addresses": vm.ip_addresses,
-                        },
                     }, status=status.HTTP_201_CREATED)
             except ValueError as e:
                 return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
-- 
GitLab


From 09f25640b27c672f61ad865092afea7ecd1f287b Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Tue, 11 Feb 2025 09:36:55 +0000
Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=91=BD=EF=B8=8F=20Add=20tag=20to=20AP?=
 =?UTF-8?q?I=20endpoint?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/api/serializers.py | 35 +++++++++++++++-------------
 1 file changed, 19 insertions(+), 16 deletions(-)

diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py
index 63455c7..b9bcfb4 100644
--- a/netbox_sys_plugin/api/serializers.py
+++ b/netbox_sys_plugin/api/serializers.py
@@ -402,21 +402,21 @@ class VirtualMachineSerializer(NetBoxModelSerializer):
 
 class CreateVirtualMachineSerializer(serializers.Serializer):
     """Serializer to handle Create VM requests."""
-    cluster_type = serializers.CharField(required=True)
-    cluster = serializers.CharField(required=True)
+    cluster_type = serializers.IntegerField(required=True)
+    cluster = serializers.IntegerField(required=True)
     name = serializers.CharField(required=True, max_length=50)
     status = serializers.CharField(required=True, max_length=50)
-    role = serializers.CharField(required=True)
-    platform = serializers.CharField(required=True)
-    description = serializers.CharField(required=False, allow_blank=True)
-    virtual_machine_type = serializers.CharField(required=True)
+    role = serializers.IntegerField(required=True)
+    platform = serializers.IntegerField(required=True)
+    description = serializers.CharField(required=True)
+    virtual_machine_type = serializers.IntegerField(required=True)
     interface_name = serializers.CharField(required=True, max_length=50)
     ip_addresses = serializers.CharField(required=True, max_length=50)
     ip_addresses_status = serializers.CharField(required=True, max_length=50)
     gateway = serializers.CharField(required=True, max_length=50)
     gateway_status = serializers.CharField(required=True, max_length=50)
     domain_names = serializers.JSONField(required=False)
-    extra_config_structure = serializers.CharField(required=True)
+    extra_config_structure = serializers.IntegerField(required=True)
     extra_config_values = serializers.JSONField(required=False)
     ntp_service_name = serializers.CharField(required=True, max_length=50)
     ntp_service_protocol = serializers.CharField(required=True, max_length=50)
@@ -427,7 +427,7 @@ class CreateVirtualMachineSerializer(serializers.Serializer):
     syslog_service_name = serializers.CharField(required=True, max_length=50)
     syslog_service_protocol = serializers.CharField(required=True, max_length=50)
     syslog_service_ports = serializers.CharField(required=True, max_length=50)
-    #tags = serializers.ListField(child=serializers.IntegerField(), required=False)
+    tag = serializers.IntegerField(required=False)
 
     class Meta:
         fields = ['cluster_type','cluster','name','status','role','platform'
@@ -436,6 +436,7 @@ class CreateVirtualMachineSerializer(serializers.Serializer):
                   ,'ntp_service_name','ntp_service_protocol','ntp_service_ports'
                   ,'dns_service_name','dns_service_protocol','dns_service_ports'
                   ,'syslog_service_name','syslog_service_protocol','syslog_service_ports'
+                  ,'tag'
                   ]
 
     def validate(self, data):
@@ -444,10 +445,10 @@ class CreateVirtualMachineSerializer(serializers.Serializer):
             raise serializers.ValidationError("You dont have permission to create a VM")
 
         cluster_type = data.pop('cluster_type', None)
-        data['cl_list_new-cluster_type']=cluster_type
+        data['cl_list_new-cluster_type']= str(cluster_type)
         
         cluster = data.pop('cluster', None)
-        data['cl_list_new-cluster']=cluster
+        data['cl_list_new-cluster']= str(cluster)
 
         name = data.pop('name', None)
         data['vms_new-0-name']=name
@@ -456,16 +457,16 @@ class CreateVirtualMachineSerializer(serializers.Serializer):
         data['vms_new-0-status']=status
 
         role = data.pop('role', None)
-        data['vms_new-0-role']=role
+        data['vms_new-0-role']=str(role)
         
         platform = data.pop('platform', None)
-        data['vms_new-0-platform']=platform
+        data['vms_new-0-platform']=str(platform)
 
         description = data.pop('description', None)
         data['vms_new-0-description']=description
 
         virtual_machine_type = data.pop('virtual_machine_type', None)
-        data['vmassignedvmtype_new-virtual_machine_type']=virtual_machine_type
+        data['vmassignedvmtype_new-virtual_machine_type']=str(virtual_machine_type)
         
         interface_name = data.pop('interface_name', None)
         data['vmis_new-0-name']=interface_name
@@ -486,7 +487,7 @@ class CreateVirtualMachineSerializer(serializers.Serializer):
         data['domainnames_new-domain_names']=json.dumps(domain_names)
         
         extra_config_structure = data.pop('extra_config_structure', None)
-        data['vmassignedextraconfig_new-provider_type_extra_config']=extra_config_structure
+        data['vmassignedextraconfig_new-provider_type_extra_config']=str(extra_config_structure)
 
         extra_config_values = data.pop('extra_config_values', None)
         data['vmassignedextraconfig_new-extra_config_values']=json.dumps(extra_config_values)
@@ -507,7 +508,7 @@ class CreateVirtualMachineSerializer(serializers.Serializer):
         data['service_dns-0-protocol']=dns_service_protocol
 
         dns_service_ports = data.pop('dns_service_ports', None)
-        data['service_dns-0-ports']=dns_service_ports
+        data['service_dns-0-ports']= dns_service_ports
 
         syslog_service_name = data.pop('syslog_service_name', None)
         data['service_syslog-0-name']=syslog_service_name
@@ -518,10 +519,12 @@ class CreateVirtualMachineSerializer(serializers.Serializer):
         syslog_service_ports = data.pop('syslog_service_ports', None)
         data['service_syslog-0-ports']=syslog_service_ports
 
+        tag = data.pop('tag', None)
+        data['tag_new-tag']=str(tag)
+
         data = super().validate(data)
         return data
 
     def create(self, data):
         """Process VM creation using CreateVmForm."""
-
         return data
\ No newline at end of file
-- 
GitLab


From c84fa015a61a79af14d95104e5c4a6378a5b404b Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Tue, 11 Feb 2025 17:23:42 +0000
Subject: [PATCH 5/9] =?UTF-8?q?=E2=9C=85=20Add=20tests=20for=20the=20Creat?=
 =?UTF-8?q?eVM=20API=20endpoint?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/api/serializers.py          |  14 +--
 netbox_sys_plugin/api/views.py                |   1 +
 .../tests/createvm/test_createvm_api.py       | 109 ++++++++++++++++++
 netbox_sys_plugin/tests/test_init.py          |   2 +-
 4 files changed, 117 insertions(+), 9 deletions(-)
 create mode 100644 netbox_sys_plugin/tests/createvm/test_createvm_api.py

diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py
index b9bcfb4..ca8b1ee 100644
--- a/netbox_sys_plugin/api/serializers.py
+++ b/netbox_sys_plugin/api/serializers.py
@@ -1,5 +1,4 @@
 from rest_framework import serializers
-from django.contrib.auth.models import User
 from virtualization.models import VirtualMachine, Cluster, ClusterType
 from netbox.api.fields import ChoiceField
 from dcim.api.nested_serializers import (
@@ -15,7 +14,6 @@ from virtualization.choices import *
 from netbox.api.serializers import NetBoxModelSerializer
 from ..utils import validate_extra_config_values
 from ..validators import validate_extra_config_structure
-from ..forms import CreateVmForm
 import json
 
 #Netbox Data Serializer
@@ -402,11 +400,11 @@ class VirtualMachineSerializer(NetBoxModelSerializer):
 
 class CreateVirtualMachineSerializer(serializers.Serializer):
     """Serializer to handle Create VM requests."""
-    cluster_type = serializers.IntegerField(required=True)
-    cluster = serializers.IntegerField(required=True)
+    provider_type = serializers.IntegerField(required=True)
+    provider = serializers.IntegerField(required=True)
     name = serializers.CharField(required=True, max_length=50)
     status = serializers.CharField(required=True, max_length=50)
-    role = serializers.IntegerField(required=True)
+    owner = serializers.IntegerField(required=True)
     platform = serializers.IntegerField(required=True)
     description = serializers.CharField(required=True)
     virtual_machine_type = serializers.IntegerField(required=True)
@@ -444,10 +442,10 @@ class CreateVirtualMachineSerializer(serializers.Serializer):
         if not request or not request.user.has_perm("virtualization.add_virtualmachine"):
             raise serializers.ValidationError("You dont have permission to create a VM")
 
-        cluster_type = data.pop('cluster_type', None)
+        cluster_type = data.pop('provider_type', None)
         data['cl_list_new-cluster_type']= str(cluster_type)
         
-        cluster = data.pop('cluster', None)
+        cluster = data.pop('provider', None)
         data['cl_list_new-cluster']= str(cluster)
 
         name = data.pop('name', None)
@@ -456,7 +454,7 @@ class CreateVirtualMachineSerializer(serializers.Serializer):
         status = data.pop('status', None)
         data['vms_new-0-status']=status
 
-        role = data.pop('role', None)
+        role = data.pop('owner', None)
         data['vms_new-0-role']=str(role)
         
         platform = data.pop('platform', None)
diff --git a/netbox_sys_plugin/api/views.py b/netbox_sys_plugin/api/views.py
index fd91921..285ae72 100644
--- a/netbox_sys_plugin/api/views.py
+++ b/netbox_sys_plugin/api/views.py
@@ -8,6 +8,7 @@ from django.db import transaction
 from .. filtersets import VmTypeFilterSet, ProviderTypeExtraConfigFilterSet
 from .. models import VirtualMachineMaintenance, ProviderCredentials, VmAssignedVirtualMachineType, VirtualMachineType,DomainNames, ProviderTypeExtraConfig, WebhookPayload
 from . serializers import *
+from ..forms import CreateVmForm
 
 class VirtualMachineMaintenanceViewSet(viewsets.ModelViewSet):
     queryset = VirtualMachineMaintenance.objects.all()
diff --git a/netbox_sys_plugin/tests/createvm/test_createvm_api.py b/netbox_sys_plugin/tests/createvm/test_createvm_api.py
new file mode 100644
index 0000000..1bf4a52
--- /dev/null
+++ b/netbox_sys_plugin/tests/createvm/test_createvm_api.py
@@ -0,0 +1,109 @@
+"""SYS Plugin CreateVirtualMachine API Test Case Class"""
+
+from users.models import ObjectPermission
+from django.contrib.contenttypes.models import ContentType
+from rest_framework import status
+from virtualization.models import Cluster, ClusterType, VMInterface, VirtualMachine
+from dcim.models import DeviceRole, Platform
+from ipam.models import IPAddress, Service
+from extras.models import Tag
+from ... models import DomainNames, VmAssignedVirtualMachineType, VirtualMachineType, VmAssignedExtraConfig, ProviderTypeExtraConfig, WebhookPayload
+from ..base import BaseAPITestCase
+
+class CreateVmAPITestCase(BaseAPITestCase):
+    """Test suite for Create Virtual Machine API"""
+    brief_fields = [
+"cluster_type","cluster","name","status","role","platform","description","virtual_machine_type","interface_name","ip_addresses"
+  "ip_addresses_status","gateway","gateway_status","domain_names","extra_config_structure","extra_config_values","ntp_service_name",
+  "ntp_service_protocol","ntp_service_ports","dns_service_name","dns_service_protocol","dns_service_ports","syslog_service_name",
+  "syslog_service_protocol","syslog_service_ports","tag"
+    ]
+
+
+    @classmethod
+    def setUpTestData(cls):
+
+        """Set up test data for Create Virtual Machine API"""
+        #Create Tag
+        cls.tag = Tag.objects.create(name="test_tag",slug="testtag")
+        #Create Device Role
+        cls.devicerole = DeviceRole.objects.create(name="Test Device", slug="TestRole")
+        #Create Platform
+        cls.platform = Platform.objects.create(name="Test platform", slug="TestPlatform")
+        # Create a ClusterType
+        cls.cluster_type = ClusterType.objects.create(name="Test ClusterType1", slug="ClusterType1")
+        # Create Cluster
+        cls.cluster = Cluster.objects.create(name="Test Cluster", type=cls.cluster_type)
+        # Create VM Type
+        cls.vmtype = VirtualMachineType.objects.create(
+            virtual_machine_type_name='vm_type_name1',
+            virtual_machine_type_desc='vm_type_desc_1',
+            assigned_object=cls.cluster_type
+        )
+        cls.extra_config_structure = ProviderTypeExtraConfig.objects.create(
+            extra_config_name="TEST",
+            extra_config_structure={'test_extra': [{'id': {'required': 'true','type': 'String'}}]},
+            extra_config_description="Test Extra",
+            assigned_object=cls.cluster_type
+        )
+
+        # Data for valid creation
+        cls.valid_create_data = [
+            {
+            "provider_type": cls.cluster_type.id,
+            "provider": cls.cluster.id,
+            "name": "vm_test",
+            "status": "active",
+            "owner": cls.devicerole.id,
+            "platform": cls.platform.id,
+            "description": "test",
+            "virtual_machine_type": cls.vmtype.id,
+            "interface_name": "interface_test",
+            "ip_addresses": "192.168.1.10/24",
+            "ip_addresses_status": "active",
+            "gateway": "193.168.1.10/24",
+            "gateway_status": "active",
+            "domain_names": {"example_domain_name": "vm.example.com"},
+            "extra_config_structure": cls.extra_config_structure.id,
+            "extra_config_values": {"id": "id"},
+            "ntp_service_name": "ntp_test",
+            "ntp_service_protocol": "tcp",
+            "ntp_service_ports": "44",
+            "dns_service_name": "dns_test",
+            "dns_service_protocol": "tcp",
+            "dns_service_ports": "44",
+            "syslog_service_name": "syslog_test",
+            "syslog_service_protocol": "tcp",
+            "syslog_service_ports": "44",
+            "tag":str(cls.tag.id)
+            }
+        ]
+
+    def test_valid_createVM(self):
+        """Test creating a valid CreateVM"""
+
+        obj_perm = ObjectPermission(
+            name="Create CreateVM Permission",
+            actions=["add", "view"],
+        )
+        obj_perm.save()
+        obj_perm.users.add(self.user)
+        obj_perm.object_types.add(ContentType.objects.get_for_model(DeviceRole))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Platform))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(ClusterType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Cluster))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VMInterface))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VirtualMachine))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Service))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(IPAddress))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(DomainNames))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VmAssignedVirtualMachineType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VirtualMachineType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VmAssignedExtraConfig))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(ProviderTypeExtraConfig))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(WebhookPayload))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Tag))
+
+        api_data = self.valid_create_data[0]
+        response = self.client.post("/api/plugins/sys/CreateVirtualMachine/", api_data, format="json", **self.header)
+        self.assertHttpStatus(response, status.HTTP_201_CREATED)
diff --git a/netbox_sys_plugin/tests/test_init.py b/netbox_sys_plugin/tests/test_init.py
index 8a0c2e0..b0b0bb7 100644
--- a/netbox_sys_plugin/tests/test_init.py
+++ b/netbox_sys_plugin/tests/test_init.py
@@ -24,5 +24,5 @@ class InitTestCase(APITestCase):
         self.assertTrue("VirtualMachine" in content)
         self.assertTrue("ExtraConfig" in content)
         self.assertTrue("AssignedExtraConfig" in content)
-        self.assertTrue("VirtualMachine" in content)
+        self.assertTrue("CreateVirtualMachine" in content)
         self.assertEqual(response.status_code, 200)
-- 
GitLab


From 21112b5b44d40876e73f5a3653639963475f68a1 Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Tue, 11 Feb 2025 17:35:47 +0000
Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=90=9B=20Fix=20bug=20in=20tests?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/tests/createvm/test_createvm_view.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/netbox_sys_plugin/tests/createvm/test_createvm_view.py b/netbox_sys_plugin/tests/createvm/test_createvm_view.py
index 5c09480..79df3d9 100644
--- a/netbox_sys_plugin/tests/createvm/test_createvm_view.py
+++ b/netbox_sys_plugin/tests/createvm/test_createvm_view.py
@@ -136,7 +136,7 @@ class CreateVmFormTestCase(TestCase):
 
         #invalid role/owner
         self.invalid_ports_form_data = self.form_data_template.copy()
-        self.invalid_ports_form_data["vms_new-0-role"] = "2"  # Invalid format
+        self.invalid_ports_form_data["vms_new-0-role"] = "999"  # Invalid Number
         form_data = self.invalid_ports_form_data
         form = CreateVmForm(data=form_data)
         with self.assertRaises(ValueError) as context:
@@ -156,7 +156,7 @@ class CreateVmFormTestCase(TestCase):
 
         #invalid platform
         self.invalid_ports_form_data = self.form_data_template.copy()
-        self.invalid_ports_form_data["vms_new-0-platform"] = "2"  # Invalid id
+        self.invalid_ports_form_data["vms_new-0-platform"] = "999"  # Invalid id
         form_data = self.invalid_ports_form_data
         form = CreateVmForm(data=form_data)
         with self.assertRaises(ValueError) as context:
@@ -176,7 +176,7 @@ class CreateVmFormTestCase(TestCase):
 
         #invalid Extra Config
         self.invalid_ports_form_data = self.form_data_template.copy()
-        self.invalid_ports_form_data["vmassignedextraconfig_new-provider_type_extra_config"] = "2"  # Invalid id
+        self.invalid_ports_form_data["vmassignedextraconfig_new-provider_type_extra_config"] = "999"  # Invalid id
         form_data = self.invalid_ports_form_data
         form = CreateVmForm(data=form_data)
         with self.assertRaises(ValueError) as context:
-- 
GitLab


From f6230789e1b8896c4af828efe61e29559008951d Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Wed, 12 Feb 2025 11:06:19 +0000
Subject: [PATCH 7/9] =?UTF-8?q?=E2=9C=85=20Add=20test=20GET=20virtualmachi?=
 =?UTF-8?q?ne=20info=20and=20create=20invalid=20info?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/api/views.py                |   3 +-
 .../tests/createvm/test_createvm_api.py       | 287 +++++++++++++++++-
 2 files changed, 287 insertions(+), 3 deletions(-)

diff --git a/netbox_sys_plugin/api/views.py b/netbox_sys_plugin/api/views.py
index 285ae72..ea8e963 100644
--- a/netbox_sys_plugin/api/views.py
+++ b/netbox_sys_plugin/api/views.py
@@ -1,6 +1,7 @@
 from rest_framework import viewsets, status
 from rest_framework.permissions import IsAuthenticated, DjangoModelPermissions
 from rest_framework.response import Response
+from django.core.exceptions import ValidationError
 from django.db.models import Prefetch
 from netbox.api.viewsets import NetBoxModelViewSet
 from virtualization import filtersets
@@ -86,6 +87,6 @@ class CreateVirtualMachineViewSet(NetBoxModelViewSet):
                     return Response({
                         "message": "VM created successfully!",
                     }, status=status.HTTP_201_CREATED)
-            except ValueError as e:
+            except (ValueError, ValidationError) as e:
                 return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
         return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
diff --git a/netbox_sys_plugin/tests/createvm/test_createvm_api.py b/netbox_sys_plugin/tests/createvm/test_createvm_api.py
index 1bf4a52..9bd45c9 100644
--- a/netbox_sys_plugin/tests/createvm/test_createvm_api.py
+++ b/netbox_sys_plugin/tests/createvm/test_createvm_api.py
@@ -48,8 +48,7 @@ class CreateVmAPITestCase(BaseAPITestCase):
         )
 
         # Data for valid creation
-        cls.valid_create_data = [
-            {
+        cls.valid_create_data = [{
             "provider_type": cls.cluster_type.id,
             "provider": cls.cluster.id,
             "name": "vm_test",
@@ -76,8 +75,126 @@ class CreateVmAPITestCase(BaseAPITestCase):
             "syslog_service_protocol": "tcp",
             "syslog_service_ports": "44",
             "tag":str(cls.tag.id)
+            },
+            {
+            "provider_type": cls.cluster_type.id,
+            "provider": cls.cluster.id,
+            "name": "vm_test2",
+            "status": "active",
+            "owner": cls.devicerole.id,
+            "platform": cls.platform.id,
+            "description": "test",
+            "virtual_machine_type": cls.vmtype.id,
+            "interface_name": "interface_test",
+            "ip_addresses": "192.168.1.10/24",
+            "ip_addresses_status": "active",
+            "gateway": "193.168.1.10/24",
+            "gateway_status": "active",
+            "domain_names": {"example_domain_name": "vm.example.com"},
+            "extra_config_structure": cls.extra_config_structure.id,
+            "extra_config_values": {"id": "id"},
+            "ntp_service_name": "ntp_test",
+            "ntp_service_protocol": "tcp",
+            "ntp_service_ports": "44",
+            "dns_service_name": "dns_test",
+            "dns_service_protocol": "tcp",
+            "dns_service_ports": "44",
+            "syslog_service_name": "syslog_test",
+            "syslog_service_protocol": "tcp",
+            "syslog_service_ports": "44",
+            "tag":str(cls.tag.id)
+            }]
+        
+        # Data for invalid creation
+        cls.invalid_create_data = [
+            #Check unique
+            {
+            "provider_type": cls.cluster_type.id,
+            "provider": cls.cluster.id,
+            "name": "vm_test",
+            "status": "active",
+            "owner": cls.devicerole.id,
+            "platform": cls.platform.id,
+            "description": "test_unique",
+            "virtual_machine_type": cls.vmtype.id,
+            "interface_name": "interface_test",
+            "ip_addresses": "192.168.1.10/24",
+            "ip_addresses_status": "active",
+            "gateway": "193.168.1.10/24",
+            "gateway_status": "active",
+            "domain_names": {"example_domain_name": "vm.example.com"},
+            "extra_config_structure": cls.extra_config_structure.id,
+            "extra_config_values": {"id": "id"},
+            "ntp_service_name": "ntp_test",
+            "ntp_service_protocol": "tcp",
+            "ntp_service_ports": "44",
+            "dns_service_name": "dns_test",
+            "dns_service_protocol": "tcp",
+            "dns_service_ports": "44",
+            "syslog_service_name": "syslog_test",
+            "syslog_service_protocol": "tcp",
+            "syslog_service_ports": "44",
+            "tag":str(cls.tag.id)
+            },
+            #Invalid port
+            {
+            "provider_type": cls.cluster_type.id,
+            "provider": cls.cluster.id,
+            "name": "vm_test",
+            "status": "active",
+            "owner": cls.devicerole.id,
+            "platform": cls.platform.id,
+            "description": "test_invalid",
+            "virtual_machine_type": cls.vmtype.id,
+            "interface_name": "interface_test",
+            "ip_addresses": "192.168.1.10/24",
+            "ip_addresses_status": "active",
+            "gateway": "193.168.1.10/24",
+            "gateway_status": "active",
+            "domain_names": {"example_domain_name": "vm.example.com"},
+            "extra_config_structure": cls.extra_config_structure.id,
+            "extra_config_values": {"id": "id"},
+            "ntp_service_name": "ntp_test",
+            "ntp_service_protocol": "tcp",
+            "ntp_service_ports": "44",
+            "dns_service_name": "dns_test",
+            "dns_service_protocol": "tcp",
+            "dns_service_ports": "AA",
+            "syslog_service_name": "syslog_test",
+            "syslog_service_protocol": "tcp",
+            "syslog_service_ports": "44",
+            "tag":str(cls.tag.id)
+            },
+            #Missing field
+            {
+            "provider_type": cls.cluster_type.id,
+            "provider": cls.cluster.id,
+            "name": "vm_test",
+            "status": "active",
+            "owner": cls.devicerole.id,
+            "platform": cls.platform.id,
+            "virtual_machine_type": cls.vmtype.id,
+            "interface_name": "interface_test",
+            "ip_addresses": "192.168.1.10/24",
+            "ip_addresses_status": "active",
+            "gateway": "193.168.1.10/24",
+            "gateway_status": "active",
+            "domain_names": {"example_domain_name": "vm.example.com"},
+            "extra_config_structure": cls.extra_config_structure.id,
+            "extra_config_values": {"id": "id"},
+            "ntp_service_name": "ntp_test",
+            "ntp_service_protocol": "tcp",
+            "ntp_service_ports": "44",
+            "dns_service_name": "dns_test",
+            "dns_service_protocol": "tcp",
+            "dns_service_ports": "AA",
+            "syslog_service_name": "syslog_test",
+            "syslog_service_protocol": "tcp",
+            "syslog_service_ports": "44",
+            "tag":str(cls.tag.id)
             }
         ]
+        
 
     def test_valid_createVM(self):
         """Test creating a valid CreateVM"""
@@ -107,3 +224,169 @@ class CreateVmAPITestCase(BaseAPITestCase):
         api_data = self.valid_create_data[0]
         response = self.client.post("/api/plugins/sys/CreateVirtualMachine/", api_data, format="json", **self.header)
         self.assertHttpStatus(response, status.HTTP_201_CREATED)
+
+    def test_unique_key_createVM(self):
+        """Test unique vm key of CreateVM"""
+
+        obj_perm = ObjectPermission(
+            name="Create test unique CreateVM Permission",
+            actions=["add", "view"],
+        )
+        obj_perm.save()
+        obj_perm.users.add(self.user)
+        obj_perm.object_types.add(ContentType.objects.get_for_model(DeviceRole))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Platform))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(ClusterType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Cluster))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VMInterface))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VirtualMachine))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Service))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(IPAddress))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(DomainNames))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VmAssignedVirtualMachineType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VirtualMachineType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VmAssignedExtraConfig))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(ProviderTypeExtraConfig))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(WebhookPayload))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Tag))
+
+        #1st VM
+        api_data = self.invalid_create_data[0]
+        response = self.client.post("/api/plugins/sys/CreateVirtualMachine/", api_data, format="json", **self.header)
+        self.assertHttpStatus(response, status.HTTP_201_CREATED)
+        #2nd VM
+        api_data = self.invalid_create_data[0]
+        response = self.client.post("/api/plugins/sys/CreateVirtualMachine/", api_data, format="json", **self.header)
+        self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
+        self.assertIn('Virtual machine name must be unique per cluster.',str(response.data))
+        
+    def test_invalid_value_createVM(self):
+        """Test creating an invalid CreateVM"""
+
+        obj_perm = ObjectPermission(
+            name="Create invalid CreateVM Permission",
+            actions=["add", "view"],
+        )
+        obj_perm.save()
+        obj_perm.users.add(self.user)
+        obj_perm.object_types.add(ContentType.objects.get_for_model(DeviceRole))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Platform))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(ClusterType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Cluster))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VMInterface))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VirtualMachine))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Service))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(IPAddress))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(DomainNames))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VmAssignedVirtualMachineType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VirtualMachineType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VmAssignedExtraConfig))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(ProviderTypeExtraConfig))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(WebhookPayload))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Tag))
+
+        api_data = self.invalid_create_data[1]
+        response = self.client.post("/api/plugins/sys/CreateVirtualMachine/", api_data, format="json", **self.header)
+        self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
+        self.assertIn('Error during creation: Invalid ports value: AA',str(response.data))
+
+    def test_invalid_missing_value_createVM(self):
+        """Test creating an invalid CreateVM"""
+
+        obj_perm = ObjectPermission(
+            name="Create invalid CreateVM Permission",
+            actions=["add", "view"],
+        )
+        obj_perm.save()
+        obj_perm.users.add(self.user)
+        obj_perm.object_types.add(ContentType.objects.get_for_model(DeviceRole))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Platform))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(ClusterType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Cluster))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VMInterface))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VirtualMachine))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Service))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(IPAddress))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(DomainNames))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VmAssignedVirtualMachineType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VirtualMachineType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VmAssignedExtraConfig))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(ProviderTypeExtraConfig))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(WebhookPayload))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Tag))
+
+        api_data = self.invalid_create_data[2]
+        response = self.client.post("/api/plugins/sys/CreateVirtualMachine/", api_data, format="json", **self.header)
+        self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
+        self.assertIn('This field is required.',str(response.data))
+
+
+    def test_get_all_created_virtual_machines(self):
+        """Test fetching all VMs"""
+        obj_perm = ObjectPermission(
+            name="View all VMs Permission",
+            actions=["view","add"],
+        )
+        obj_perm.save()
+        obj_perm.users.add(self.user)
+        obj_perm.object_types.add(ContentType.objects.get_for_model(DeviceRole))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Platform))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(ClusterType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Cluster))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VMInterface))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VirtualMachine))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Service))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(IPAddress))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(DomainNames))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VmAssignedVirtualMachineType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VirtualMachineType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VmAssignedExtraConfig))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(ProviderTypeExtraConfig))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(WebhookPayload))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Tag))
+
+        #1st VM
+        api_data = self.valid_create_data[0]
+        response = self.client.post("/api/plugins/sys/CreateVirtualMachine/", api_data, format="json", **self.header)
+        self.assertHttpStatus(response, status.HTTP_201_CREATED)
+        #2nd VM
+        api_data = self.valid_create_data[1]
+        response = self.client.post("/api/plugins/sys/CreateVirtualMachine/", api_data, format="json", **self.header)
+        self.assertHttpStatus(response, status.HTTP_201_CREATED)
+
+        response = self.client.get("/api/plugins/sys/VirtualMachine/", **self.header)
+        self.assertHttpStatus(response, status.HTTP_200_OK)
+        self.assertGreaterEqual(len(response.data), 2)
+
+    def test_get_single_created_virtual_machine(self):
+        """Test fetching a single VM"""
+        obj_perm = ObjectPermission(
+            name="View single VM Permission",
+            actions=["view","add"],
+        )
+        obj_perm.save()
+        obj_perm.users.add(self.user)
+        obj_perm.object_types.add(ContentType.objects.get_for_model(DeviceRole))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Platform))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(ClusterType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Cluster))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VMInterface))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VirtualMachine))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Service))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(IPAddress))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(DomainNames))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VmAssignedVirtualMachineType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VirtualMachineType))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(VmAssignedExtraConfig))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(ProviderTypeExtraConfig))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(WebhookPayload))
+        obj_perm.object_types.add(ContentType.objects.get_for_model(Tag))
+
+        #1st VM
+        api_data = self.valid_create_data[0]
+        response = self.client.post("/api/plugins/sys/CreateVirtualMachine/", api_data, format="json", **self.header)
+        self.assertHttpStatus(response, status.HTTP_201_CREATED)
+
+        VM = VirtualMachine.objects.first()
+        response = self.client.get((f'/api/plugins/sys/VirtualMachine/{VM.id}/'), **self.header)
+        self.assertHttpStatus(response, status.HTTP_200_OK)
\ No newline at end of file
-- 
GitLab


From 08e181f267374806c52445b2bce069cf98df4700 Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Wed, 12 Feb 2025 11:11:49 +0000
Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=9A=A8=20Fix=20lint?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/tests/createvm/test_createvm_api.py | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/netbox_sys_plugin/tests/createvm/test_createvm_api.py b/netbox_sys_plugin/tests/createvm/test_createvm_api.py
index 9bd45c9..eaa1d55 100644
--- a/netbox_sys_plugin/tests/createvm/test_createvm_api.py
+++ b/netbox_sys_plugin/tests/createvm/test_createvm_api.py
@@ -104,7 +104,7 @@ class CreateVmAPITestCase(BaseAPITestCase):
             "syslog_service_ports": "44",
             "tag":str(cls.tag.id)
             }]
-        
+
         # Data for invalid creation
         cls.invalid_create_data = [
             #Check unique
@@ -194,7 +194,6 @@ class CreateVmAPITestCase(BaseAPITestCase):
             "tag":str(cls.tag.id)
             }
         ]
-        
 
     def test_valid_createVM(self):
         """Test creating a valid CreateVM"""
@@ -259,7 +258,7 @@ class CreateVmAPITestCase(BaseAPITestCase):
         response = self.client.post("/api/plugins/sys/CreateVirtualMachine/", api_data, format="json", **self.header)
         self.assertHttpStatus(response, status.HTTP_400_BAD_REQUEST)
         self.assertIn('Virtual machine name must be unique per cluster.',str(response.data))
-        
+
     def test_invalid_value_createVM(self):
         """Test creating an invalid CreateVM"""
 
@@ -389,4 +388,4 @@ class CreateVmAPITestCase(BaseAPITestCase):
 
         VM = VirtualMachine.objects.first()
         response = self.client.get((f'/api/plugins/sys/VirtualMachine/{VM.id}/'), **self.header)
-        self.assertHttpStatus(response, status.HTTP_200_OK)
\ No newline at end of file
+        self.assertHttpStatus(response, status.HTTP_200_OK)
-- 
GitLab


From f9fc488c6ce2d88d6f71cd7a06b93f81eb879a94 Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Wed, 12 Feb 2025 11:19:26 +0000
Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=9A=A8=20Fix=20lint=20missing=20new?=
 =?UTF-8?q?=20line?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/api/serializers.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py
index ca8b1ee..7dd60b3 100644
--- a/netbox_sys_plugin/api/serializers.py
+++ b/netbox_sys_plugin/api/serializers.py
@@ -525,4 +525,4 @@ class CreateVirtualMachineSerializer(serializers.Serializer):
 
     def create(self, data):
         """Process VM creation using CreateVmForm."""
-        return data
\ No newline at end of file
+        return data
-- 
GitLab