From ce2282b428eaecbc7a72d93803ff8d95f2f3270e Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Mon, 18 Nov 2024 17:00:35 +0000
Subject: [PATCH 01/11] =?UTF-8?q?=E2=9C=A8=20Add=20machine=20type?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../migrations/0003_machinetype.py            | 38 +++++++++++
 netbox_sys_plugin/models/__init__.py          |  3 +-
 netbox_sys_plugin/models/machine.py           | 66 +++++++++++++++++++
 netbox_sys_plugin/navigation.py               | 21 ++++++
 4 files changed, 127 insertions(+), 1 deletion(-)
 create mode 100644 netbox_sys_plugin/migrations/0003_machinetype.py
 create mode 100644 netbox_sys_plugin/models/machine.py

diff --git a/netbox_sys_plugin/migrations/0003_machinetype.py b/netbox_sys_plugin/migrations/0003_machinetype.py
new file mode 100644
index 0000000..9bca12a
--- /dev/null
+++ b/netbox_sys_plugin/migrations/0003_machinetype.py
@@ -0,0 +1,38 @@
+# Generated by Django 4.2.16 on 2024-11-18 16:43
+
+from django.db import migrations, models
+import django.db.models.deletion
+import taggit.managers
+import utilities.json
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('extras', '0098_webhook_custom_field_data_webhook_tags'),
+        ('contenttypes', '0002_remove_content_type_name'),
+        ('netbox_sys_plugin', '0002_alter_virtualmachinemaintenance_maintenance_window_and_more'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='MachineType',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
+                ('created', models.DateTimeField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
+                ('machine_type_name', models.CharField(blank=True, default=None, max_length=50, null=True)),
+                ('machine_type_desc', models.CharField(blank=True, default=None, max_length=100, null=True)),
+                ('assigned_object_id', models.PositiveBigIntegerField(blank=True, null=True)),
+                ('assigned_object_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(('app_label', 'virtualization'), ('model', 'ClusterType'))), null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')),
+                ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
+            ],
+            options={
+                'verbose_name': 'Machine Type',
+                'verbose_name_plural': 'Machine Types',
+                'ordering': ['assigned_object_id'],
+                'unique_together': {('assigned_object_id',)},
+            },
+        ),
+    ]
diff --git a/netbox_sys_plugin/models/__init__.py b/netbox_sys_plugin/models/__init__.py
index cef638b..44d7d4f 100644
--- a/netbox_sys_plugin/models/__init__.py
+++ b/netbox_sys_plugin/models/__init__.py
@@ -1,4 +1,5 @@
 """Models definitions"""
 
 from .maintenance import VirtualMachineMaintenance
-from .provider import ProviderCredentials
\ No newline at end of file
+from .provider import ProviderCredentials
+from .machine import MachineType
\ No newline at end of file
diff --git a/netbox_sys_plugin/models/machine.py b/netbox_sys_plugin/models/machine.py
new file mode 100644
index 0000000..95525da
--- /dev/null
+++ b/netbox_sys_plugin/models/machine.py
@@ -0,0 +1,66 @@
+"""Models definitions"""
+
+from django.core.validators import (
+    RegexValidator,
+)
+from django.urls import reverse
+from django.core.validators import (
+    URLValidator,    
+)  # pylint: disable=import-error
+from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
+from django.contrib.contenttypes.models import ContentType
+from django.db import models
+from virtualization.models import ClusterType
+from netbox.models import NetBoxModel
+
+CLUSTERTYPE_ASSIGNMENT_MODELS = models.Q(models.Q(app_label="virtualization", model="ClusterType"))
+
+
+
+class MachineType(NetBoxModel):
+    """Cluster ProviderInfo definition class"""
+
+    machine_type_name = models.CharField(
+        default=None, blank=True, null=True,
+        max_length=50,
+        )
+    
+    machine_type_desc = models.CharField(
+        default=None, blank=True, null=True,
+        max_length=100)
+
+    assigned_object_type = models.ForeignKey(
+        to=ContentType,
+        limit_choices_to=CLUSTERTYPE_ASSIGNMENT_MODELS,
+        on_delete=models.PROTECT,
+        null=True,
+        blank=True,
+    )
+    assigned_object_id = models.PositiveBigIntegerField(null=True, blank=True)
+    assigned_object = GenericForeignKey(
+        ct_field="assigned_object_type",
+        fk_field="assigned_object_id",
+    )
+
+    class Meta:
+        """Meta class"""
+        unique_together = ["assigned_object_id"]
+        ordering = ["assigned_object_id"]
+        verbose_name = "Machine Type"
+        verbose_name_plural = "Machine Types"
+
+
+    def __str__(self):
+        return f"for Cluster Type ID {self.assigned_object_id}"
+
+    def get_absolute_url(self):
+        """override"""
+        return reverse("plugins:netbox_sys_plugin:machinetype", args=[self.pk])
+
+GenericRelation(
+    to=MachineType,
+    content_type_field="assigned_object_type",
+    object_id_field="assigned_object_id",
+    related_query_name="ClusterType",
+).contribute_to_class(ClusterType, "MachineType")
+
diff --git a/netbox_sys_plugin/navigation.py b/netbox_sys_plugin/navigation.py
index 2a39a83..d4188d3 100644
--- a/netbox_sys_plugin/navigation.py
+++ b/netbox_sys_plugin/navigation.py
@@ -3,6 +3,7 @@
 from extras.plugins import PluginMenuButton, PluginMenuItem, PluginMenu
 from utilities.choices import ButtonColorChoices
 
+#Buttons
 vm_maintenance_buttons = [
     PluginMenuButton(
         link="plugins:netbox_sys_plugin:virtualmachinemaintenance_add",
@@ -21,6 +22,16 @@ cluster_provider_credentials_buttons = [
     ),
 ]
 
+clutertype_machine_type_buttons = [
+    PluginMenuButton(
+        link="plugins:netbox_sys_plugin:providercredentials_add",
+        title="Add",
+        icon_class="mdi mdi-plus-thick",
+        color=ButtonColorChoices.GREEN,
+    ),
+]
+
+#Items
 vmMaintenanceItem = [
     PluginMenuItem(
         link="plugins:netbox_sys_plugin:virtualmachinemaintenance_list",
@@ -37,11 +48,21 @@ clusterProviderCredentialsItem = [
     ),
 ]
 
+clusterTypeMachineTypeItem = [
+    PluginMenuItem(
+        link="plugins:netbox_sys_plugin:providercredentials_list",
+        link_text="Machine Type",
+        buttons=cluster_provider_credentials_buttons,
+    ),
+]
+
+#Menu
 menu = PluginMenu(
     label="Sys",
     groups=(
         ("Maintenance", vmMaintenanceItem),
         ("Provider", clusterProviderCredentialsItem),
+        ("Machine", clusterTypeMachineTypeItem),
             ),
     icon_class="mdi mdi-all-inclusive-box-outline",
 )
-- 
GitLab


From b94201cd450fbf7a83db0dffc95894a208669020 Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Tue, 19 Nov 2024 14:10:28 +0000
Subject: [PATCH 02/11] =?UTF-8?q?=F0=9F=9A=A7=20Add=20working=20forms=20fo?=
 =?UTF-8?q?r=20CRUDS=20operations?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/api/serializers.py          | 23 ++++-
 netbox_sys_plugin/api/urls.py                 |  3 +-
 netbox_sys_plugin/api/views.py                | 10 +-
 netbox_sys_plugin/forms/__init__.py           |  3 +-
 netbox_sys_plugin/forms/machine.py            | 96 +++++++++++++++++++
 ...hinetype.py => 0003_virtualmachinetype.py} |  8 +-
 netbox_sys_plugin/models/__init__.py          |  2 +-
 netbox_sys_plugin/models/machine.py           | 12 +--
 netbox_sys_plugin/navigation.py               |  6 +-
 netbox_sys_plugin/tables.py                   | 42 +++++++-
 .../netbox_sys_plugin/virtualmachinetype.html | 44 +++++++++
 netbox_sys_plugin/urls.py                     | 31 +++---
 netbox_sys_plugin/views.py                    | 33 ++++++-
 13 files changed, 273 insertions(+), 40 deletions(-)
 create mode 100644 netbox_sys_plugin/forms/machine.py
 rename netbox_sys_plugin/migrations/{0003_machinetype.py => 0003_virtualmachinetype.py} (83%)
 create mode 100644 netbox_sys_plugin/templates/netbox_sys_plugin/virtualmachinetype.html

diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py
index 4e7d712..a5502fd 100644
--- a/netbox_sys_plugin/api/serializers.py
+++ b/netbox_sys_plugin/api/serializers.py
@@ -1,8 +1,9 @@
 from rest_framework import serializers
-from virtualization.models import VirtualMachine, Cluster
-from .. models import VirtualMachineMaintenance, ProviderCredentials
+from virtualization.models import VirtualMachine, Cluster, ClusterType
+from .. models import VirtualMachineMaintenance, ProviderCredentials,VirtualMachineType
 from django.contrib.contenttypes.models import ContentType
 
+#Netbox Data Serializer
 class VirtualMachineSerializer(serializers.ModelSerializer):
     class Meta:
         model = VirtualMachine
@@ -13,6 +14,13 @@ class ClusterSerializer(serializers.ModelSerializer):
         model = Cluster
         fields = ['id', 'name']
 
+class ClusterTypeSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = ClusterType
+        fields = ['id', 'name']
+
+
+#Plugin Data Serializer
 class VirtualMachineMaintenanceSerializer(serializers.ModelSerializer):
     id = serializers.IntegerField(read_only=True)
     virtual_machine = VirtualMachineSerializer(source='assigned_object', read_only=True)
@@ -33,4 +41,15 @@ class ProviderCredentialsSerializer(serializers.ModelSerializer):
     class Meta:
         model = ProviderCredentials
         #fields = ['id','maintenance_window','assigned_object_type','assigned_object_id','assigned_object_type','virtual_machine']
+        fields = '__all__'
+
+class VirtualMachineTypeSerializer(serializers.ModelSerializer):
+    id = serializers.IntegerField(read_only=True)
+    cluster_type = ClusterTypeSerializer(source='assigned_object', read_only=True)
+    assigned_object_id = serializers.IntegerField(write_only=True)
+    assigned_object_type = serializers.CharField(write_only=True)
+
+    class Meta:
+        model = VirtualMachineType
+        #fields = ['id','maintenance_window','assigned_object_type','assigned_object_id','assigned_object_type','virtual_machine']
         fields = '__all__'
\ No newline at end of file
diff --git a/netbox_sys_plugin/api/urls.py b/netbox_sys_plugin/api/urls.py
index 6f3fb34..8454336 100644
--- a/netbox_sys_plugin/api/urls.py
+++ b/netbox_sys_plugin/api/urls.py
@@ -1,9 +1,10 @@
 from rest_framework.routers import DefaultRouter
-from .views import VirtualMachineMaintenanceViewSet, ProviderCredentialsViewSet
+from .views import VirtualMachineMaintenanceViewSet, ProviderCredentialsViewSet, VirtualMachineTypeViewSet
 
 router = DefaultRouter()
 router.register(r'MaintenanceWindow', VirtualMachineMaintenanceViewSet)
 router.register(r'ProviderCredentials', ProviderCredentialsViewSet)
+router.register(r'VmType', VirtualMachineTypeViewSet)
 
 app_name = "netbox_sys_plugin"
 
diff --git a/netbox_sys_plugin/api/views.py b/netbox_sys_plugin/api/views.py
index b375333..40331ea 100644
--- a/netbox_sys_plugin/api/views.py
+++ b/netbox_sys_plugin/api/views.py
@@ -1,8 +1,8 @@
 from rest_framework import viewsets, status
 from rest_framework.decorators import action
 from rest_framework.response import Response
-from .. models import VirtualMachineMaintenance, ProviderCredentials
-from . serializers import VirtualMachineMaintenanceSerializer, ProviderCredentialsSerializer
+from .. models import VirtualMachineMaintenance, ProviderCredentials, VirtualMachineType
+from . serializers import VirtualMachineMaintenanceSerializer, ProviderCredentialsSerializer, VirtualMachineTypeSerializer
 
 class VirtualMachineMaintenanceViewSet(viewsets.ModelViewSet):
     queryset = VirtualMachineMaintenance.objects.all()
@@ -13,4 +13,10 @@ class VirtualMachineMaintenanceViewSet(viewsets.ModelViewSet):
 class ProviderCredentialsViewSet(viewsets.ModelViewSet):
     queryset = ProviderCredentials.objects.all()
     serializer_class = ProviderCredentialsSerializer
+    http_method_names = ["get", "post", "patch", "delete", "options"]
+
+
+class VirtualMachineTypeViewSet(viewsets.ModelViewSet):
+    queryset = VirtualMachineType.objects.all()
+    serializer_class = VirtualMachineTypeSerializer
     http_method_names = ["get", "post", "patch", "delete", "options"]
\ No newline at end of file
diff --git a/netbox_sys_plugin/forms/__init__.py b/netbox_sys_plugin/forms/__init__.py
index 0f3ba6e..70c47d7 100644
--- a/netbox_sys_plugin/forms/__init__.py
+++ b/netbox_sys_plugin/forms/__init__.py
@@ -1,4 +1,5 @@
 """Forms definitions"""
 
 from .maintenance import *
-from .provider import *
\ No newline at end of file
+from .provider import *
+from .machine import *
\ No newline at end of file
diff --git a/netbox_sys_plugin/forms/machine.py b/netbox_sys_plugin/forms/machine.py
new file mode 100644
index 0000000..914243f
--- /dev/null
+++ b/netbox_sys_plugin/forms/machine.py
@@ -0,0 +1,96 @@
+"""Forms definitions"""
+
+from virtualization.models import ClusterType
+from django.contrib.contenttypes.models import ContentType
+from django.utils.translation import gettext_lazy as _
+from django.core.exceptions import ValidationError
+from django import forms
+from netbox.forms import (
+    NetBoxModelForm,
+    NetBoxModelFilterSetForm,
+)
+from utilities.forms.fields import DynamicModelChoiceField
+from ..models import VirtualMachineType
+
+class VmTypeForm(NetBoxModelForm):
+    """
+    GUI form to add or edit a Virtual Machine Type.
+    """
+
+    cluster_type = DynamicModelChoiceField(
+        queryset=ClusterType.objects.all(), required=True, label="Cluster Type"
+    )
+    virtual_machine_type_name = forms.CharField(
+        max_length=200, min_length=1, required=True, label="Name"
+    )
+    virtual_machine_type_desc = forms.CharField(
+        max_length=50, min_length=1, required=False, label="Description"
+    )
+
+
+    def __init__(self, *args, **kwargs):
+        # Initialize helper selectors
+        instance = kwargs.get("instance")
+        initial = kwargs.get("initial", {}).copy()
+        if instance:
+            if isinstance(instance.assigned_object, ClusterType):
+                initial["cluster_type"] = instance.assigned_object
+        kwargs["initial"] = initial
+        super().__init__(*args, **kwargs)
+
+        if instance:
+            current_cluster_type_id = instance.assigned_object.id if instance.assigned_object else None
+        else:
+            current_cluster_type_id = None
+
+        assigned_cluster_types = VirtualMachineType.objects.filter(
+            assigned_object_type=ContentType.objects.get_for_model(ClusterType)
+        ).exclude(assigned_object_id=current_cluster_type_id).values_list('assigned_object_id', flat=True)
+        self.fields['cluster_type'].queryset = ClusterType.objects.exclude(id__in=assigned_cluster_types)
+
+    class Meta:
+        """Meta class"""
+        model = VirtualMachineType
+        fields = ('cluster_type','virtual_machine_type_name', 'virtual_machine_type_desc','tags')
+
+    def clean_cluster(self):
+
+        cluster_type = self.cleaned_data.get("cluster_type")
+
+        cluster_type_ct = ContentType.objects.get_for_model(ClusterType)
+
+        if VirtualMachineType.objects.filter(
+            assigned_object_type=cluster_type_ct,
+            assigned_object_id=cluster_type.id
+        ).exclude(pk=self.instance.pk).exists():
+            raise ValidationError()
+        return cluster_type
+
+    def clean(self):
+        """
+        Validates form inputs before submitting:
+        """
+        super().clean()
+
+        if self.errors.get(f"virtual_machine_type_name"):
+            return
+        
+        cluster_type = self.cleaned_data.get("cluster_type")
+
+        #Check if cluster is assigned corretly
+        if (not cluster_type):
+            raise ValidationError(
+                {"__all__": "A Virtual Machine Type needs to be associated with the Cluster Type"},
+            )
+
+    def save(self, *args, **kwargs):
+        # Set assigned object
+        self.instance.assigned_object = (
+            self.cleaned_data.get("cluster_type")
+        )
+        return super().save(*args, **kwargs)
+
+class VmTypeFilterForm(NetBoxModelFilterSetForm):
+    """Virtual Machine Type filter form definition class"""
+
+    model = VirtualMachineType
\ No newline at end of file
diff --git a/netbox_sys_plugin/migrations/0003_machinetype.py b/netbox_sys_plugin/migrations/0003_virtualmachinetype.py
similarity index 83%
rename from netbox_sys_plugin/migrations/0003_machinetype.py
rename to netbox_sys_plugin/migrations/0003_virtualmachinetype.py
index 9bca12a..d7d0a5c 100644
--- a/netbox_sys_plugin/migrations/0003_machinetype.py
+++ b/netbox_sys_plugin/migrations/0003_virtualmachinetype.py
@@ -1,4 +1,4 @@
-# Generated by Django 4.2.16 on 2024-11-18 16:43
+# Generated by Django 4.2.16 on 2024-11-18 17:19
 
 from django.db import migrations, models
 import django.db.models.deletion
@@ -16,14 +16,14 @@ class Migration(migrations.Migration):
 
     operations = [
         migrations.CreateModel(
-            name='MachineType',
+            name='VirtualMachineType',
             fields=[
                 ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
                 ('created', models.DateTimeField(auto_now_add=True, null=True)),
                 ('last_updated', models.DateTimeField(auto_now=True, null=True)),
                 ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('machine_type_name', models.CharField(blank=True, default=None, max_length=50, null=True)),
-                ('machine_type_desc', models.CharField(blank=True, default=None, max_length=100, null=True)),
+                ('virtual_machine_type_name', models.CharField(blank=True, default=None, max_length=50, null=True)),
+                ('virtual_machine_type_desc', models.CharField(blank=True, default=None, max_length=100, null=True)),
                 ('assigned_object_id', models.PositiveBigIntegerField(blank=True, null=True)),
                 ('assigned_object_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(('app_label', 'virtualization'), ('model', 'ClusterType'))), null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')),
                 ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
diff --git a/netbox_sys_plugin/models/__init__.py b/netbox_sys_plugin/models/__init__.py
index 44d7d4f..da5e1b1 100644
--- a/netbox_sys_plugin/models/__init__.py
+++ b/netbox_sys_plugin/models/__init__.py
@@ -2,4 +2,4 @@
 
 from .maintenance import VirtualMachineMaintenance
 from .provider import ProviderCredentials
-from .machine import MachineType
\ No newline at end of file
+from .machine import VirtualMachineType
\ No newline at end of file
diff --git a/netbox_sys_plugin/models/machine.py b/netbox_sys_plugin/models/machine.py
index 95525da..c3e510c 100644
--- a/netbox_sys_plugin/models/machine.py
+++ b/netbox_sys_plugin/models/machine.py
@@ -17,15 +17,15 @@ CLUSTERTYPE_ASSIGNMENT_MODELS = models.Q(models.Q(app_label="virtualization", mo
 
 
 
-class MachineType(NetBoxModel):
+class VirtualMachineType(NetBoxModel):
     """Cluster ProviderInfo definition class"""
 
-    machine_type_name = models.CharField(
+    virtual_machine_type_name = models.CharField(
         default=None, blank=True, null=True,
         max_length=50,
         )
     
-    machine_type_desc = models.CharField(
+    virtual_machine_type_desc = models.CharField(
         default=None, blank=True, null=True,
         max_length=100)
 
@@ -55,12 +55,12 @@ class MachineType(NetBoxModel):
 
     def get_absolute_url(self):
         """override"""
-        return reverse("plugins:netbox_sys_plugin:machinetype", args=[self.pk])
+        return reverse("plugins:netbox_sys_plugin:virtualmachinetype", args=[self.pk])
 
 GenericRelation(
-    to=MachineType,
+    to=VirtualMachineType,
     content_type_field="assigned_object_type",
     object_id_field="assigned_object_id",
     related_query_name="ClusterType",
-).contribute_to_class(ClusterType, "MachineType")
+).contribute_to_class(ClusterType, "VirtualMachineType")
 
diff --git a/netbox_sys_plugin/navigation.py b/netbox_sys_plugin/navigation.py
index d4188d3..c105859 100644
--- a/netbox_sys_plugin/navigation.py
+++ b/netbox_sys_plugin/navigation.py
@@ -24,7 +24,7 @@ cluster_provider_credentials_buttons = [
 
 clutertype_machine_type_buttons = [
     PluginMenuButton(
-        link="plugins:netbox_sys_plugin:providercredentials_add",
+        link="plugins:netbox_sys_plugin:virtualmachinetype_add",
         title="Add",
         icon_class="mdi mdi-plus-thick",
         color=ButtonColorChoices.GREEN,
@@ -50,9 +50,9 @@ clusterProviderCredentialsItem = [
 
 clusterTypeMachineTypeItem = [
     PluginMenuItem(
-        link="plugins:netbox_sys_plugin:providercredentials_list",
+        link="plugins:netbox_sys_plugin:virtualmachinetype_list",
         link_text="Machine Type",
-        buttons=cluster_provider_credentials_buttons,
+        buttons=clutertype_machine_type_buttons,
     ),
 ]
 
diff --git a/netbox_sys_plugin/tables.py b/netbox_sys_plugin/tables.py
index bea34c6..8cb536b 100644
--- a/netbox_sys_plugin/tables.py
+++ b/netbox_sys_plugin/tables.py
@@ -2,9 +2,9 @@
 
 import django_tables2 as tables
 from netbox.tables import NetBoxTable, columns
-from .models import VirtualMachineMaintenance, ProviderCredentials
-
+from .models import VirtualMachineMaintenance, ProviderCredentials, VirtualMachineType
 
+#VmMaintenance
 class VmMaintenanceTable(NetBoxTable):
     """VM Maintenance Table definition class"""
 
@@ -35,7 +35,7 @@ class VmMaintenanceTable(NetBoxTable):
 
         default_columns = ('id','maintenance_window','assigned_object')
 
-
+#ProviderCredentials
 class ProviderCredentialsTable(NetBoxTable):
     """VM Maintenance Table definition class"""
 
@@ -79,3 +79,39 @@ class ProviderCredentialsTable(NetBoxTable):
         )
 
         default_columns = ('id','provider_url','provider_username','provider_password_vault_url','assigned_object')
+
+#ProviderCredentials
+class VmTypeTable(NetBoxTable):
+    """VM Maintenance Table definition class"""
+
+    pk = columns.ToggleColumn()
+    id = tables.Column(
+        linkify=False,
+    )
+    
+    assigned_object = tables.Column(
+        linkify=True,
+        orderable=False,
+        verbose_name="Cluster Type",
+    )
+
+    virtual_machine_type_name = tables.Column(
+        linkify=True,
+        verbose_name="Machine Type Name",
+    )
+    virtual_machine_type_desc = tables.Column(
+        linkify=True,
+        verbose_name="Machine Type Descritpion",
+    )
+
+    class Meta(NetBoxTable.Meta):
+        """Meta class"""  
+        model = VirtualMachineType
+        fields = (
+            "pk",
+            "id",
+            "virtual_machine_type_name",
+            "virtual_machine_type_desc", 
+        )
+
+        default_columns = ('id','virtual_machine_type_name','virtual_machine_type_desc','assigned_object')
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/virtualmachinetype.html b/netbox_sys_plugin/templates/netbox_sys_plugin/virtualmachinetype.html
new file mode 100644
index 0000000..ee2c51d
--- /dev/null
+++ b/netbox_sys_plugin/templates/netbox_sys_plugin/virtualmachinetype.html
@@ -0,0 +1,44 @@
+{% extends 'generic/object.html' %}
+
+{% block content %}
+<div class="row mb-3">
+  <div class="col col-md-6">
+    <div class="card">
+      <h5 class="card-header">Virtual Machine Types</h5>
+      <div class="card-body">
+        <table class="table table-hover attr-table">
+          <tr>
+            <th scope="row">Name</th>
+            <td>{{ object.virtual_machine_type_name }}</td>
+          </tr>
+          <tr>
+            <th scope="row">Description</th>
+            <td>{{ object.virtual_machine_type_desc }}</td>
+          </tr>
+        </table>
+      </div>
+    </div>
+    {% include 'inc/panels/custom_fields.html' %}
+    {% include 'inc/panels/tags.html' %}
+  </div>
+  <div class="col col-md-6">
+    <div class="card">
+      <h5 class="card-header">Assigned Object</h5>
+      <div class="card-body">
+        <table class="table table-hover attr-table">
+          <tr>
+            <th scope="row">{{ object.assigned_object_type|cut:"virtualization | " }}</th>
+            <td>
+              {% if object.assigned_object %}
+                <a href="{{ object.assigned_object.get_absolute_url }}">{{ object.assigned_object }}</a>
+              {% else %}
+                {{ ''|placeholder }}
+              {% endif %}
+            </td>
+          </tr>
+        </table>
+      </div>
+    </div>
+  </div>
+</div>
+{% endblock content %}
diff --git a/netbox_sys_plugin/urls.py b/netbox_sys_plugin/urls.py
index 4d3e076..ac9e61f 100644
--- a/netbox_sys_plugin/urls.py
+++ b/netbox_sys_plugin/urls.py
@@ -5,29 +5,30 @@ from netbox.views.generic import ObjectChangeLogView, ObjectJournalView
 from netbox_sys_plugin import models, views
 
 urlpatterns = (
-
+    #maintenance
+    path('vm-maintenance/<int:pk>/', views.VmMaintenanceView.as_view(), name='virtualmachinemaintenance'),
     path('vm-maintenance/', views.VmMaintenanceListView.as_view(), name='virtualmachinemaintenance_list'),
     path('vm-maintenance/add/', views.VmMaintenanceEditView.as_view(), name='virtualmachinemaintenance_add'),
-    path('vm-maintenance/<int:pk>/', views.VmMaintenanceView.as_view(), name='virtualmachinemaintenance'),
     path('vm-maintenance/<int:pk>/edit/', views.VmMaintenanceEditView.as_view(), name='virtualmachinemaintenance_edit'),
     path('vm-maintenance/<int:pk>/delete/', views.VmMaintenanceDeleteView.as_view(), name='virtualmachinemaintenance_delete'),
-    path('vm-maintenance/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='virtualmachinemaintenance_changelog', kwargs={
-        'model': models.VirtualMachineMaintenance
-    }),
-    path('vm-maintenance/<int:pk>/journal/', ObjectJournalView.as_view(), name='virtualmachinemaintenance_journal', kwargs={
-        'model': models.VirtualMachineMaintenance
-    }),
+    path('vm-maintenance/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='virtualmachinemaintenance_changelog', kwargs={'model': models.VirtualMachineMaintenance }),
+    path('vm-maintenance/<int:pk>/journal/', ObjectJournalView.as_view(), name='virtualmachinemaintenance_journal', kwargs={'model': models.VirtualMachineMaintenance}),
+    #providerCredentials
+    path('provider-credentials/<int:pk>/', views.ProviderCredentialsView.as_view(), name='providercredentials'),
     path('provider-credentials/', views.ProviderCredentialsListView.as_view(), name='providercredentials_list'),
     path('provider-credentials/add/', views.ProviderCredentialsEditView.as_view(), name='providercredentials_add'),
-    path('provider-credentials/<int:pk>/', views.ProviderCredentialsView.as_view(), name='providercredentials'),
     path('provider-credentials/<int:pk>/edit/', views.ProviderCredentialsEditView.as_view(), name='providercredentials_edit'),
     path('provider-credentials/<int:pk>/delete/', views.ProviderCredentialsDeleteView.as_view(), name='providercredentials_delete'),
-    path('provider-credentials/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='providercredentials_changelog', kwargs={
-        'model': models.VirtualMachineMaintenance
-    }),
-    path('provider-credentials/<int:pk>/journal/', ObjectJournalView.as_view(), name='providercredentials_journal', kwargs={
-        'model': models.VirtualMachineMaintenance
-    }),
+    path('provider-credentials/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='providercredentials_changelog', kwargs={'model': models.VirtualMachineMaintenance}),
+    path('provider-credentials/<int:pk>/journal/', ObjectJournalView.as_view(), name='providercredentials_journal', kwargs={'model': models.VirtualMachineMaintenance}),
+    #virtualMachineType
+    path('vm-type/<int:pk>/', views.VmTypeView.as_view(), name='virtualmachinetype'),
+    path('vm-type/', views.VmTypeListView.as_view(), name='virtualmachinetype_list'),
+    path('vm-type/add/', views.VmTypeEditView.as_view(), name='virtualmachinetype_add'),
+    path('vm-type/<int:pk>/edit/', views.VmTypeEditView.as_view(), name='virtualmachinetype_edit'),
+    path('vm-type/<int:pk>/delete/', views.VmTypeDeleteView.as_view(), name='virtualmachinetype_delete'),
+    path('vm-type/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='virtualmachinetype_changelog', kwargs={'model': models.VirtualMachineType}),
+    path('vm-type/<int:pk>/journal/', ObjectJournalView.as_view(), name='virtualmachinetype_journal', kwargs={'model': models.VirtualMachineType}),
 
 
 )
diff --git a/netbox_sys_plugin/views.py b/netbox_sys_plugin/views.py
index f80387d..e346a1b 100644
--- a/netbox_sys_plugin/views.py
+++ b/netbox_sys_plugin/views.py
@@ -3,6 +3,7 @@
 from netbox.views import generic
 from . import forms, models, tables, filtersets
 
+#VmMaintenance
 class  VmMaintenanceView(generic.ObjectView):
     """ Vm maintenance view definition"""
 
@@ -30,7 +31,7 @@ class VmMaintenanceDeleteView(generic.ObjectDeleteView):
 
     queryset = models.VirtualMachineMaintenance.objects.all()
 
-
+#ProviderCredentials
 class  ProviderCredentialsView(generic.ObjectView):
     """ Provider Credentials view definition"""
 
@@ -56,4 +57,32 @@ class ProviderCredentialsEditView(generic.ObjectEditView):
     Defines the edit view for the Vm maintenance django model.
     """
     queryset = models.ProviderCredentials.objects.all()
-    form = forms.ProviderCredentialsForm
\ No newline at end of file
+    form = forms.ProviderCredentialsForm
+
+#VmType
+class  VmTypeView(generic.ObjectView):
+    """ Virtual Machine Type view definition"""
+
+    queryset = (
+        models.VirtualMachineType.objects.all()
+    )
+
+class VmTypeListView(generic.ObjectListView):
+    """Cluster Type Virtual Machine Type list view definition"""
+
+    queryset = models.VirtualMachineType.objects.all()
+    table = tables.VmTypeTable
+    #filterset = filtersets.ProviderCredentialsFilterSet
+    #filterset_form = forms.VmTypeFilterForm
+
+class VmTypeDeleteView(generic.ObjectDeleteView):
+    """Vm maintenance delete view definition"""
+
+    queryset = models.VirtualMachineType.objects.all()
+
+class VmTypeEditView(generic.ObjectEditView):
+    """
+    Defines the edit view for the Vm maintenance django model.
+    """
+    queryset = models.VirtualMachineType.objects.all()
+    form = forms.VmTypeForm
\ No newline at end of file
-- 
GitLab


From a507e6c93e39ef2195e6be029c5324cbe4e0ecbf Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Tue, 19 Nov 2024 15:40:01 +0000
Subject: [PATCH 03/11] =?UTF-8?q?=F0=9F=9A=A7=20New=20structure,=20validat?=
 =?UTF-8?q?ion=20and=20templates?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/api/serializers.py          |   8 +-
 netbox_sys_plugin/filtersets.py               |  24 +++-
 netbox_sys_plugin/forms/__init__.py           |   1 -
 netbox_sys_plugin/forms/machine.py            | 136 ++++++++++++++----
 netbox_sys_plugin/forms/maintenance.py        |  99 -------------
 .../migrations/0003_virtualmachinetype.py     |   4 +-
 netbox_sys_plugin/models/__init__.py          |   3 +-
 netbox_sys_plugin/models/machine.py           |  63 +++++++-
 netbox_sys_plugin/models/maintenance.py       |  66 ---------
 netbox_sys_plugin/navigation.py               |  33 +++--
 netbox_sys_plugin/tables.py                   |  12 +-
 netbox_sys_plugin/template_content.py         |   9 +-
 .../netbox_sys_plugin/virtualmachinetype.html |   4 +
 .../vm_virtualmachinetype.html                |  41 ++++++
 ...nance_table.html => vm_vmmaintenance.html} |   0
 netbox_sys_plugin/views.py                    |   4 +-
 16 files changed, 272 insertions(+), 235 deletions(-)
 delete mode 100644 netbox_sys_plugin/forms/maintenance.py
 delete mode 100644 netbox_sys_plugin/models/maintenance.py
 create mode 100644 netbox_sys_plugin/templates/netbox_sys_plugin/vm_virtualmachinetype.html
 rename netbox_sys_plugin/templates/netbox_sys_plugin/{vm_vmmaintenance_table.html => vm_vmmaintenance.html} (100%)

diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py
index a5502fd..e4c45da 100644
--- a/netbox_sys_plugin/api/serializers.py
+++ b/netbox_sys_plugin/api/serializers.py
@@ -14,12 +14,6 @@ class ClusterSerializer(serializers.ModelSerializer):
         model = Cluster
         fields = ['id', 'name']
 
-class ClusterTypeSerializer(serializers.ModelSerializer):
-    class Meta:
-        model = ClusterType
-        fields = ['id', 'name']
-
-
 #Plugin Data Serializer
 class VirtualMachineMaintenanceSerializer(serializers.ModelSerializer):
     id = serializers.IntegerField(read_only=True)
@@ -45,7 +39,7 @@ class ProviderCredentialsSerializer(serializers.ModelSerializer):
 
 class VirtualMachineTypeSerializer(serializers.ModelSerializer):
     id = serializers.IntegerField(read_only=True)
-    cluster_type = ClusterTypeSerializer(source='assigned_object', read_only=True)
+    virtual_machine = VirtualMachineSerializer(source='assigned_object', read_only=True)
     assigned_object_id = serializers.IntegerField(write_only=True)
     assigned_object_type = serializers.CharField(write_only=True)
 
diff --git a/netbox_sys_plugin/filtersets.py b/netbox_sys_plugin/filtersets.py
index 75a77e0..6dae0a4 100644
--- a/netbox_sys_plugin/filtersets.py
+++ b/netbox_sys_plugin/filtersets.py
@@ -4,7 +4,7 @@ import django_filters
 from netbox.filtersets import NetBoxModelFilterSet
 from virtualization.models import VirtualMachine, Cluster
 from django.db.models import Q
-from .models import VirtualMachineMaintenance, ProviderCredentials
+from .models import VirtualMachineMaintenance, ProviderCredentials, VirtualMachineType
 
 
 class VmMaintenanceFilterSet(NetBoxModelFilterSet):
@@ -28,6 +28,28 @@ class VmMaintenanceFilterSet(NetBoxModelFilterSet):
         if not value.strip():
             return queryset
         return queryset.filter(Q(maintenance_window__icontains=value))
+    
+class VmTypeFilterSet(NetBoxModelFilterSet):
+    """VirtualMachineType filterset definition class"""
+
+    virtualmachine = django_filters.ModelMultipleChoiceFilter(
+        field_name="VirtualMachine__name",
+        queryset=VirtualMachine.objects.all(),
+        to_field_name="name",
+        label="Virtual Machine (name)",
+    )
+
+    class Meta:
+        """Meta class"""     
+        model = VirtualMachineType
+        fields = ('virtual_machine_type_name','virtual_machine_type_desc', 'virtualmachine')
+
+    # pylint: disable=W0613
+    def search(self, queryset, name, value):
+        """override"""
+        if not value.strip():
+            return queryset
+        return queryset.filter(Q(virtual_machine_type_name__icontains=value)|Q(virtual_machine_type_desc__icontains=value))
 
 class ProviderCredentialsFilterSet(NetBoxModelFilterSet):
     """ProviderCredentials filterset definition class"""
diff --git a/netbox_sys_plugin/forms/__init__.py b/netbox_sys_plugin/forms/__init__.py
index 70c47d7..c3fb247 100644
--- a/netbox_sys_plugin/forms/__init__.py
+++ b/netbox_sys_plugin/forms/__init__.py
@@ -1,5 +1,4 @@
 """Forms definitions"""
 
-from .maintenance import *
 from .provider import *
 from .machine import *
\ No newline at end of file
diff --git a/netbox_sys_plugin/forms/machine.py b/netbox_sys_plugin/forms/machine.py
index 914243f..e678197 100644
--- a/netbox_sys_plugin/forms/machine.py
+++ b/netbox_sys_plugin/forms/machine.py
@@ -1,28 +1,31 @@
 """Forms definitions"""
 
-from virtualization.models import ClusterType
+from virtualization.models import VirtualMachine
 from django.contrib.contenttypes.models import ContentType
 from django.utils.translation import gettext_lazy as _
 from django.core.exceptions import ValidationError
 from django import forms
+from django.utils.safestring import mark_safe
 from netbox.forms import (
     NetBoxModelForm,
     NetBoxModelFilterSetForm,
 )
 from utilities.forms.fields import DynamicModelChoiceField
-from ..models import VirtualMachineType
+from ..models import VirtualMachineType, VirtualMachineMaintenance
 
 class VmTypeForm(NetBoxModelForm):
     """
     GUI form to add or edit a Virtual Machine Type.
     """
 
-    cluster_type = DynamicModelChoiceField(
-        queryset=ClusterType.objects.all(), required=True, label="Cluster Type"
+    virtual_machine = DynamicModelChoiceField(
+        queryset=VirtualMachine.objects.all(), required=True, label="Virtual Machine"
     )
+
     virtual_machine_type_name = forms.CharField(
         max_length=200, min_length=1, required=True, label="Name"
     )
+    
     virtual_machine_type_desc = forms.CharField(
         max_length=50, min_length=1, required=False, label="Description"
     )
@@ -33,38 +36,38 @@ class VmTypeForm(NetBoxModelForm):
         instance = kwargs.get("instance")
         initial = kwargs.get("initial", {}).copy()
         if instance:
-            if isinstance(instance.assigned_object, ClusterType):
-                initial["cluster_type"] = instance.assigned_object
+            if isinstance(instance.assigned_object, VirtualMachine):
+                initial["virtual_machine"] = instance.assigned_object
         kwargs["initial"] = initial
         super().__init__(*args, **kwargs)
 
         if instance:
-            current_cluster_type_id = instance.assigned_object.id if instance.assigned_object else None
+            current_virtual_machine_id = instance.assigned_object.id if instance.assigned_object else None
         else:
-            current_cluster_type_id = None
+            current_virtual_machine_id = None
 
-        assigned_cluster_types = VirtualMachineType.objects.filter(
-            assigned_object_type=ContentType.objects.get_for_model(ClusterType)
-        ).exclude(assigned_object_id=current_cluster_type_id).values_list('assigned_object_id', flat=True)
-        self.fields['cluster_type'].queryset = ClusterType.objects.exclude(id__in=assigned_cluster_types)
+        assigned_virtual_machines = VirtualMachineType.objects.filter(
+            assigned_object_type=ContentType.objects.get_for_model(VirtualMachine)
+        ).exclude(assigned_object_id=current_virtual_machine_id).values_list('assigned_object_id', flat=True)
+        self.fields['virtual_machine'].queryset = VirtualMachine.objects.exclude(id__in=assigned_virtual_machines)
 
     class Meta:
         """Meta class"""
         model = VirtualMachineType
-        fields = ('cluster_type','virtual_machine_type_name', 'virtual_machine_type_desc','tags')
+        fields = ('virtual_machine','virtual_machine_type_name', 'virtual_machine_type_desc','tags')
 
     def clean_cluster(self):
 
-        cluster_type = self.cleaned_data.get("cluster_type")
+        virtual_machine = self.cleaned_data.get("virtual_machine")
 
-        cluster_type_ct = ContentType.objects.get_for_model(ClusterType)
+        virtual_machine_ct = ContentType.objects.get_for_model(VirtualMachine)
 
         if VirtualMachineType.objects.filter(
-            assigned_object_type=cluster_type_ct,
-            assigned_object_id=cluster_type.id
+            assigned_object_type=virtual_machine_ct,
+            assigned_object_id=virtual_machine.id
         ).exclude(pk=self.instance.pk).exists():
             raise ValidationError()
-        return cluster_type
+        return virtual_machine
 
     def clean(self):
         """
@@ -75,22 +78,107 @@ class VmTypeForm(NetBoxModelForm):
         if self.errors.get(f"virtual_machine_type_name"):
             return
         
-        cluster_type = self.cleaned_data.get("cluster_type")
+        virtual_machine = self.cleaned_data.get("virtual_machine")
 
-        #Check if cluster is assigned corretly
-        if (not cluster_type):
+        #Check if VM is assigned corretly
+        if (not virtual_machine):
             raise ValidationError(
-                {"__all__": "A Virtual Machine Type needs to be associated with the Cluster Type"},
+                {"__all__": "A Virtual Machine Type needs to be associated with the Virtual Machine"},
             )
 
     def save(self, *args, **kwargs):
         # Set assigned object
         self.instance.assigned_object = (
-            self.cleaned_data.get("cluster_type")
+            self.cleaned_data.get("virtual_machine")
         )
         return super().save(*args, **kwargs)
 
 class VmTypeFilterForm(NetBoxModelFilterSetForm):
     """Virtual Machine Type filter form definition class"""
 
-    model = VirtualMachineType
\ No newline at end of file
+    model = VirtualMachineType
+
+class VmMaitenanceForm(NetBoxModelForm):
+    """
+    GUI form to add or edit a VM Maitenance.
+    """
+    virtual_machine = DynamicModelChoiceField(
+        queryset=VirtualMachine.objects.none(), required=True, label="Virtual Machine"
+    )
+    maintenance_window = forms.CharField(
+        max_length=7, min_length=1, required=True, label="Maintenance Window",
+        help_text=mark_safe(
+            "<b>*Note:</b> Day of the week and a time of the day, 0 = Sunday , 1 = monday. Format 0-00:00",
+        ),
+    )
+
+    def __init__(self, *args, **kwargs):
+        # Initialize helper selectors
+        instance = kwargs.get("instance")
+        initial = kwargs.get("initial", {}).copy()
+        if instance:
+            if isinstance(instance.assigned_object, VirtualMachine):
+                initial["virtual_machine"] = instance.assigned_object
+        kwargs["initial"] = initial
+        super().__init__(*args, **kwargs)
+
+        if instance:
+            current_vm_id = instance.assigned_object.id if instance.assigned_object else None
+        else:
+            current_vm_id = None
+
+        assigned_vms = VirtualMachineMaintenance.objects.filter(
+            assigned_object_type=ContentType.objects.get_for_model(VirtualMachine)
+        ).exclude(assigned_object_id=current_vm_id).values_list('assigned_object_id', flat=True)
+        self.fields['virtual_machine'].queryset = VirtualMachine.objects.exclude(id__in=assigned_vms)
+
+    class Meta:
+        """Meta class"""
+        model = VirtualMachineMaintenance
+        fields = ( 'virtual_machine','maintenance_window','tags')
+
+    def clean_virtual_machine(self):
+
+        virtual_machine = self.cleaned_data.get("virtual_machine")
+
+        virtual_machine_ct = ContentType.objects.get_for_model(VirtualMachine)
+
+        if VirtualMachineMaintenance.objects.filter(
+            assigned_object_type=virtual_machine_ct,
+            assigned_object_id=virtual_machine.id
+        ).exclude(pk=self.instance.pk).exists():
+            raise ValidationError()
+        return virtual_machine
+    
+
+
+    def clean(self):
+        """
+        Validates form inputs before submitting:
+        """
+        super().clean()
+
+        if self.errors.get("maintenance_window"):
+            return
+
+        virtual_machine = self.cleaned_data.get("virtual_machine")
+
+        #Check if VM is assigned corretly
+        if (not virtual_machine):
+            raise ValidationError(
+                {"__all__": "A Virtual Machine needs to be associated with the maintenance window"},
+            )
+
+
+
+    def save(self, *args, **kwargs):
+        # Set assigned object
+        self.instance.assigned_object = (
+            self.cleaned_data.get("virtual_machine") 
+        )
+        return super().save(*args, **kwargs)
+
+class VmMaintenanceFilterForm(NetBoxModelFilterSetForm):
+    """MacAddress filter form definition class"""
+
+    model = VirtualMachineMaintenance
\ No newline at end of file
diff --git a/netbox_sys_plugin/forms/maintenance.py b/netbox_sys_plugin/forms/maintenance.py
deleted file mode 100644
index 46618c6..0000000
--- a/netbox_sys_plugin/forms/maintenance.py
+++ /dev/null
@@ -1,99 +0,0 @@
-"""Forms definitions"""
-
-from virtualization.models import VirtualMachine
-from django.contrib.contenttypes.models import ContentType
-from django.utils.translation import gettext_lazy as _
-from django.core.exceptions import ValidationError
-from django import forms
-from django.utils.safestring import mark_safe
-from netbox.forms import (
-    NetBoxModelForm,
-    NetBoxModelFilterSetForm,
-)
-from utilities.forms.fields import DynamicModelChoiceField
-from ..models import  VirtualMachineMaintenance
-
-class VmMaitenanceForm(NetBoxModelForm):
-    """
-    GUI form to add or edit a VM Maitenance.
-    """
-    virtual_machine = DynamicModelChoiceField(
-        queryset=VirtualMachine.objects.none(), required=True, label="Virtual Machine"
-    )
-    maintenance_window = forms.CharField(
-        max_length=7, min_length=1, required=True, label="Maintenance Window",
-        help_text=mark_safe(
-            "<b>*Note:</b> Day of the week and a time of the day, 0 = Sunday , 1 = monday. Format 0-00:00",
-        ),
-    )
-
-    def __init__(self, *args, **kwargs):
-        # Initialize helper selectors
-        instance = kwargs.get("instance")
-        initial = kwargs.get("initial", {}).copy()
-        if instance:
-            if isinstance(instance.assigned_object, VirtualMachine):
-                initial["virtual_machine"] = instance.assigned_object
-        kwargs["initial"] = initial
-        super().__init__(*args, **kwargs)
-
-        if instance:
-            current_vm_id = instance.assigned_object.id if instance.assigned_object else None
-        else:
-            current_vm_id = None
-
-        assigned_vms = VirtualMachineMaintenance.objects.filter(
-            assigned_object_type=ContentType.objects.get_for_model(VirtualMachine)
-        ).exclude(assigned_object_id=current_vm_id).values_list('assigned_object_id', flat=True)
-        self.fields['virtual_machine'].queryset = VirtualMachine.objects.exclude(id__in=assigned_vms)
-
-    class Meta:
-        """Meta class"""
-        model = VirtualMachineMaintenance
-        fields = ( 'virtual_machine','maintenance_window','tags')
-
-    def clean_virtual_machine(self):
-
-        virtual_machine = self.cleaned_data.get("virtual_machine")
-
-        virtual_machine_ct = ContentType.objects.get_for_model(VirtualMachine)
-
-        if VirtualMachineMaintenance.objects.filter(
-            assigned_object_type=virtual_machine_ct,
-            assigned_object_id=virtual_machine.id
-        ).exclude(pk=self.instance.pk).exists():
-            raise ValidationError()
-        return virtual_machine
-    
-
-
-    def clean(self):
-        """
-        Validates form inputs before submitting:
-        """
-        super().clean()
-
-        if self.errors.get("maintenance_window"):
-            return
-
-        virtual_machine = self.cleaned_data.get("virtual_machine")
-
-        #Check if VM is assigned corretly
-        if (not virtual_machine):
-            raise ValidationError(
-                {"__all__": "A Virtual Machine needs to be associated with the maintenance window"},
-            )
-
-
-
-    def save(self, *args, **kwargs):
-        # Set assigned object
-        self.instance.assigned_object = (
-            self.cleaned_data.get("virtual_machine") 
-        )
-        return super().save(*args, **kwargs)
-
-class VmMaintenanceFilterForm(NetBoxModelFilterSetForm):
-    """MacAddress filter form definition class"""
-
-    model = VirtualMachineMaintenance
\ No newline at end of file
diff --git a/netbox_sys_plugin/migrations/0003_virtualmachinetype.py b/netbox_sys_plugin/migrations/0003_virtualmachinetype.py
index d7d0a5c..c7e67e7 100644
--- a/netbox_sys_plugin/migrations/0003_virtualmachinetype.py
+++ b/netbox_sys_plugin/migrations/0003_virtualmachinetype.py
@@ -1,4 +1,4 @@
-# Generated by Django 4.2.16 on 2024-11-18 17:19
+# Generated by Django 4.2.16 on 2024-11-19 15:30
 
 from django.db import migrations, models
 import django.db.models.deletion
@@ -25,7 +25,7 @@ class Migration(migrations.Migration):
                 ('virtual_machine_type_name', models.CharField(blank=True, default=None, max_length=50, null=True)),
                 ('virtual_machine_type_desc', models.CharField(blank=True, default=None, max_length=100, null=True)),
                 ('assigned_object_id', models.PositiveBigIntegerField(blank=True, null=True)),
-                ('assigned_object_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(('app_label', 'virtualization'), ('model', 'ClusterType'))), null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')),
+                ('assigned_object_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(('app_label', 'virtualization'), ('model', 'VirtualMachine'))), null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')),
                 ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
             ],
             options={
diff --git a/netbox_sys_plugin/models/__init__.py b/netbox_sys_plugin/models/__init__.py
index da5e1b1..d300513 100644
--- a/netbox_sys_plugin/models/__init__.py
+++ b/netbox_sys_plugin/models/__init__.py
@@ -1,5 +1,4 @@
 """Models definitions"""
 
-from .maintenance import VirtualMachineMaintenance
 from .provider import ProviderCredentials
-from .machine import VirtualMachineType
\ No newline at end of file
+from .machine import VirtualMachineType, VirtualMachineMaintenance
\ No newline at end of file
diff --git a/netbox_sys_plugin/models/machine.py b/netbox_sys_plugin/models/machine.py
index c3e510c..d2dd85c 100644
--- a/netbox_sys_plugin/models/machine.py
+++ b/netbox_sys_plugin/models/machine.py
@@ -10,15 +10,15 @@ from django.core.validators import (
 from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
 from django.contrib.contenttypes.models import ContentType
 from django.db import models
-from virtualization.models import ClusterType
+from virtualization.models import VirtualMachine
 from netbox.models import NetBoxModel
 
-CLUSTERTYPE_ASSIGNMENT_MODELS = models.Q(models.Q(app_label="virtualization", model="ClusterType"))
+VM_ASSIGNMENT_MODELS = models.Q(models.Q(app_label="virtualization", model="VirtualMachine"))
 
 
 
 class VirtualMachineType(NetBoxModel):
-    """Cluster ProviderInfo definition class"""
+    """Virtual Machine Type Info definition class"""
 
     virtual_machine_type_name = models.CharField(
         default=None, blank=True, null=True,
@@ -31,7 +31,7 @@ class VirtualMachineType(NetBoxModel):
 
     assigned_object_type = models.ForeignKey(
         to=ContentType,
-        limit_choices_to=CLUSTERTYPE_ASSIGNMENT_MODELS,
+        limit_choices_to=VM_ASSIGNMENT_MODELS,
         on_delete=models.PROTECT,
         null=True,
         blank=True,
@@ -51,7 +51,7 @@ class VirtualMachineType(NetBoxModel):
 
 
     def __str__(self):
-        return f"for Cluster Type ID {self.assigned_object_id}"
+        return f"For Virtual machine ID {self.assigned_object_id}"
 
     def get_absolute_url(self):
         """override"""
@@ -61,6 +61,55 @@ GenericRelation(
     to=VirtualMachineType,
     content_type_field="assigned_object_type",
     object_id_field="assigned_object_id",
-    related_query_name="ClusterType",
-).contribute_to_class(ClusterType, "VirtualMachineType")
+    related_query_name="VirtualMachine",
+).contribute_to_class(VirtualMachine, "VirtualMachineType")
+
+class VirtualMachineMaintenance(NetBoxModel):
+    """virtual machine maintenance definition class"""
+
+    maintenance_window = models.CharField(
+        default=None, blank=True, null=True,
+        max_length=200,
+        validators=[
+            RegexValidator(
+                regex='(^([0-6])-[0-1]?[0-9]|2[0-3]):[0-5][0-9]$',
+                message='Incorrect maintenance window format',
+            ),
+        ],
+    )
+
+    assigned_object_type = models.ForeignKey(
+        to=ContentType,
+        limit_choices_to=VM_ASSIGNMENT_MODELS,
+        on_delete=models.PROTECT,
+        null=True,
+        blank=True,
+    )
+    assigned_object_id = models.PositiveBigIntegerField(null=True, blank=True)
+    assigned_object = GenericForeignKey(
+        ct_field="assigned_object_type",
+        fk_field="assigned_object_id",
+    )
+
+    class Meta:
+        """Meta class"""
+        unique_together = ["assigned_object_id"]
+        ordering = ["assigned_object_id"]
+        verbose_name = "Maintenance Information"
+        verbose_name_plural = "Maintenance Informations"
+
+
+    def __str__(self):
+        return f"For Virtual machine ID {self.assigned_object_id}"
+
+    def get_absolute_url(self):
+        """override"""
+        return reverse("plugins:netbox_sys_plugin:virtualmachinemaintenance", args=[self.pk])
+
+GenericRelation(
+    to=VirtualMachineMaintenance,
+    content_type_field="assigned_object_type",
+    object_id_field="assigned_object_id",
+    related_query_name="VirtualMachine",
+).contribute_to_class(VirtualMachine, "VirtualMachineMaintenance")
 
diff --git a/netbox_sys_plugin/models/maintenance.py b/netbox_sys_plugin/models/maintenance.py
deleted file mode 100644
index 53e1a81..0000000
--- a/netbox_sys_plugin/models/maintenance.py
+++ /dev/null
@@ -1,66 +0,0 @@
-"""Models definitions"""
-
-from django.core.validators import (
-    RegexValidator,
-)
-from django.urls import reverse
-from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
-from django.contrib.contenttypes.models import ContentType
-from django.db import models
-from django.db.models import Q
-from virtualization.models import VirtualMachine
-from netbox.models import NetBoxModel
-
-MAINT_ASSIGNMENT_MODELS = models.Q(models.Q(app_label="virtualization", model="VirtualMachine"))
-
-
-
-class VirtualMachineMaintenance(NetBoxModel):
-    """virtual machine maintenance definition class"""
-
-    maintenance_window = models.CharField(
-        default=None, blank=True, null=True,
-        max_length=200,
-        validators=[
-            RegexValidator(
-                regex='(^([0-6])-[0-1]?[0-9]|2[0-3]):[0-5][0-9]$',
-                message='Incorrect maintenance window format',
-            ),
-        ],
-    )
-
-    assigned_object_type = models.ForeignKey(
-        to=ContentType,
-        limit_choices_to=MAINT_ASSIGNMENT_MODELS,
-        on_delete=models.PROTECT,
-        null=True,
-        blank=True,
-    )
-    assigned_object_id = models.PositiveBigIntegerField(null=True, blank=True)
-    assigned_object = GenericForeignKey(
-        ct_field="assigned_object_type",
-        fk_field="assigned_object_id",
-    )
-
-    class Meta:
-        """Meta class"""
-        unique_together = ["assigned_object_id"]
-        ordering = ["assigned_object_id"]
-        verbose_name = "Maintenance Information"
-        verbose_name_plural = "Maintenance Informations"
-
-
-    def __str__(self):
-        return f"For Virtual machine ID {self.assigned_object_id}"
-
-    def get_absolute_url(self):
-        """override"""
-        return reverse("plugins:netbox_sys_plugin:virtualmachinemaintenance", args=[self.pk])
-
-GenericRelation(
-    to=VirtualMachineMaintenance,
-    content_type_field="assigned_object_type",
-    object_id_field="assigned_object_id",
-    related_query_name="VirtualMachine",
-).contribute_to_class(VirtualMachine, "VirtualMachineMaintenance")
-
diff --git a/netbox_sys_plugin/navigation.py b/netbox_sys_plugin/navigation.py
index c105859..a2db5e9 100644
--- a/netbox_sys_plugin/navigation.py
+++ b/netbox_sys_plugin/navigation.py
@@ -1,7 +1,10 @@
 """Navigation Menu definitions"""
 
+from django.utils.translation import gettext as _
 from extras.plugins import PluginMenuButton, PluginMenuItem, PluginMenu
+from netbox.registry import registry
 from utilities.choices import ButtonColorChoices
+from . import *
 
 #Buttons
 vm_maintenance_buttons = [
@@ -10,6 +13,7 @@ vm_maintenance_buttons = [
         title="Add",
         icon_class="mdi mdi-plus-thick",
         color=ButtonColorChoices.GREEN,
+        permissions=["netbox_sys_plugin.add_maintenance"],
     ),
 ]
 
@@ -19,40 +23,40 @@ cluster_provider_credentials_buttons = [
         title="Add",
         icon_class="mdi mdi-plus-thick",
         color=ButtonColorChoices.GREEN,
+        permissions=["netbox_sys_plugin.add_providercredentials"],
     ),
 ]
 
-clutertype_machine_type_buttons = [
+vm_machine_type_buttons = [
     PluginMenuButton(
         link="plugins:netbox_sys_plugin:virtualmachinetype_add",
         title="Add",
         icon_class="mdi mdi-plus-thick",
         color=ButtonColorChoices.GREEN,
+        permissions=["netbox_sys_plugin.add_machinetype"],
     ),
 ]
 
 #Items
-vmMaintenanceItem = [
-    PluginMenuItem(
-        link="plugins:netbox_sys_plugin:virtualmachinemaintenance_list",
-        link_text="Virtual Machine Maintenance",
-        buttons=vm_maintenance_buttons,
-    ),
-]
 
 clusterProviderCredentialsItem = [
     PluginMenuItem(
         link="plugins:netbox_sys_plugin:providercredentials_list",
-        link_text="Provider Credentials",
+        link_text="Credentials",
         buttons=cluster_provider_credentials_buttons,
     ),
 ]
 
-clusterTypeMachineTypeItem = [
+vmMachineItem = [
     PluginMenuItem(
         link="plugins:netbox_sys_plugin:virtualmachinetype_list",
-        link_text="Machine Type",
-        buttons=clutertype_machine_type_buttons,
+        link_text="Type",
+        buttons=vm_machine_type_buttons,
+    ),
+    PluginMenuItem(
+        link="plugins:netbox_sys_plugin:virtualmachinemaintenance_list",
+        link_text="Maintenance",
+        buttons=vm_maintenance_buttons,
     ),
 ]
 
@@ -60,9 +64,8 @@ clusterTypeMachineTypeItem = [
 menu = PluginMenu(
     label="Sys",
     groups=(
-        ("Maintenance", vmMaintenanceItem),
+        ("Virtual Machine", (vmMachineItem)),
         ("Provider", clusterProviderCredentialsItem),
-        ("Machine", clusterTypeMachineTypeItem),
             ),
     icon_class="mdi mdi-all-inclusive-box-outline",
-)
+)
\ No newline at end of file
diff --git a/netbox_sys_plugin/tables.py b/netbox_sys_plugin/tables.py
index 8cb536b..2385d86 100644
--- a/netbox_sys_plugin/tables.py
+++ b/netbox_sys_plugin/tables.py
@@ -47,7 +47,7 @@ class ProviderCredentialsTable(NetBoxTable):
     assigned_object = tables.Column(
         linkify=True,
         orderable=False,
-        verbose_name="Cluster",
+        verbose_name="Virtual Machine",
     )
 
     provider_url = tables.Column(
@@ -80,7 +80,7 @@ class ProviderCredentialsTable(NetBoxTable):
 
         default_columns = ('id','provider_url','provider_username','provider_password_vault_url','assigned_object')
 
-#ProviderCredentials
+#VmType
 class VmTypeTable(NetBoxTable):
     """VM Maintenance Table definition class"""
 
@@ -92,16 +92,16 @@ class VmTypeTable(NetBoxTable):
     assigned_object = tables.Column(
         linkify=True,
         orderable=False,
-        verbose_name="Cluster Type",
+        verbose_name="Virtual Machine",
     )
 
     virtual_machine_type_name = tables.Column(
         linkify=True,
-        verbose_name="Machine Type Name",
+        verbose_name="Type Name",
     )
     virtual_machine_type_desc = tables.Column(
-        linkify=True,
-        verbose_name="Machine Type Descritpion",
+        linkify=False,
+        verbose_name="Type Description",
     )
 
     class Meta(NetBoxTable.Meta):
diff --git a/netbox_sys_plugin/template_content.py b/netbox_sys_plugin/template_content.py
index d23d99b..4260247 100644
--- a/netbox_sys_plugin/template_content.py
+++ b/netbox_sys_plugin/template_content.py
@@ -4,13 +4,16 @@
 from extras.plugins import PluginTemplateExtension
 from netbox_sys_plugin import models
 
-class VmMaintenanceTable(PluginTemplateExtension):
+class VmTable(PluginTemplateExtension):
     """VM Maintenance object template"""
 
     model = 'virtualization.virtualmachine'
 
     def left_page(self):
-        return self.render('netbox_sys_plugin/vm_vmmaintenance_table.html', extra_context={'vmmaintenanceinfo': models.VirtualMachineMaintenance.objects.filter(VirtualMachine=self.context['object'])})
+        return self.render('netbox_sys_plugin/vm_vmmaintenance.html', extra_context={'vmmaintenanceinfo': models.VirtualMachineMaintenance.objects.filter(VirtualMachine=self.context['object'])})
+    
+    def right_page(self):
+        return self.render('netbox_sys_plugin/vm_virtualmachinetype.html', extra_context={'vmtypeinfo': models.VirtualMachineType.objects.filter(VirtualMachine=self.context['object'])})
     
 class ProviderCredentialsTable(PluginTemplateExtension):
     """Provider Credentials object template"""
@@ -21,4 +24,4 @@ class ProviderCredentialsTable(PluginTemplateExtension):
         return self.render('netbox_sys_plugin/cluster_providercredentials.html', extra_context={'providercredentialsinfo': models.ProviderCredentials.objects.filter(Cluster=self.context['object'])})
 
 
-template_extensions = [VmMaintenanceTable,ProviderCredentialsTable]
+template_extensions = [VmTable,ProviderCredentialsTable]
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/virtualmachinetype.html b/netbox_sys_plugin/templates/netbox_sys_plugin/virtualmachinetype.html
index ee2c51d..e7a8e89 100644
--- a/netbox_sys_plugin/templates/netbox_sys_plugin/virtualmachinetype.html
+++ b/netbox_sys_plugin/templates/netbox_sys_plugin/virtualmachinetype.html
@@ -1,5 +1,9 @@
 {% extends 'generic/object.html' %}
 
+{% block title%}
+Virtual Machine Type for {{ object.assigned_object }} 
+{% endblock title%}
+
 {% block content %}
 <div class="row mb-3">
   <div class="col col-md-6">
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/vm_virtualmachinetype.html b/netbox_sys_plugin/templates/netbox_sys_plugin/vm_virtualmachinetype.html
new file mode 100644
index 0000000..dbf759f
--- /dev/null
+++ b/netbox_sys_plugin/templates/netbox_sys_plugin/vm_virtualmachinetype.html
@@ -0,0 +1,41 @@
+{% block content %}
+  <div class="row mb-3">
+    <div class="col col-md-12">
+      <div class="card">
+        <h5 class="card-header">Virtual Machine Type</h5>
+        <div class="card-body">
+          <table class="table table-responsive">
+              <th scope="row">ID</th>
+              <th scope="row">Type</th>
+              <th scope="row">Description</th>
+              {% for vmtype in vmtypeinfo %}
+        <tr>
+            <td>{{ vmtype.id }}</td>
+            <td>
+              {% if vmtype.virtual_machine_type_name %}
+                <a href="{{ vmtype.get_absolute_url }}">{{ vmtype.virtual_machine_type_name }}</a>
+              {% else %}
+                {{ ''|placeholder }}
+              {% endif %}
+            </td>
+            <td>
+              {% if vmtype.virtual_machine_type_desc %}
+                {{ vmtype.virtual_machine_type_desc }}
+              {% else %}
+                {{ ''|placeholder }}
+              {% endif %}
+            </td>
+        </tr>
+        {% empty %}
+        <tr>
+            <td colspan="3">No maintenance window found.</td>
+        </tr>
+        {% endfor %}
+            </tr>
+          </table>
+        </div>
+      </div>
+      {% include 'inc/panels/custom_fields.html' %}
+    </div>
+  </div>
+{% endblock content %}
\ No newline at end of file
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/vm_vmmaintenance_table.html b/netbox_sys_plugin/templates/netbox_sys_plugin/vm_vmmaintenance.html
similarity index 100%
rename from netbox_sys_plugin/templates/netbox_sys_plugin/vm_vmmaintenance_table.html
rename to netbox_sys_plugin/templates/netbox_sys_plugin/vm_vmmaintenance.html
diff --git a/netbox_sys_plugin/views.py b/netbox_sys_plugin/views.py
index e346a1b..fe34158 100644
--- a/netbox_sys_plugin/views.py
+++ b/netbox_sys_plugin/views.py
@@ -72,8 +72,8 @@ class VmTypeListView(generic.ObjectListView):
 
     queryset = models.VirtualMachineType.objects.all()
     table = tables.VmTypeTable
-    #filterset = filtersets.ProviderCredentialsFilterSet
-    #filterset_form = forms.VmTypeFilterForm
+    filterset = filtersets.VmTypeFilterSet
+    filterset_form = forms.VmTypeFilterForm
 
 class VmTypeDeleteView(generic.ObjectDeleteView):
     """Vm maintenance delete view definition"""
-- 
GitLab


From a7acde89a364486882bdbe96665c03a8c1d228e6 Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Tue, 19 Nov 2024 15:46:31 +0000
Subject: [PATCH 04/11] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20Typo=20in=20ta?=
 =?UTF-8?q?ble?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/netbox_sys_plugin/tables.py b/netbox_sys_plugin/tables.py
index 2385d86..07a313d 100644
--- a/netbox_sys_plugin/tables.py
+++ b/netbox_sys_plugin/tables.py
@@ -47,7 +47,7 @@ class ProviderCredentialsTable(NetBoxTable):
     assigned_object = tables.Column(
         linkify=True,
         orderable=False,
-        verbose_name="Virtual Machine",
+        verbose_name="Cluster",
     )
 
     provider_url = tables.Column(
-- 
GitLab


From 7058f5232ed4b84656ef85806228fe2003936d91 Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Tue, 19 Nov 2024 16:09:05 +0000
Subject: [PATCH 05/11] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20typo=20in=20cl?=
 =?UTF-8?q?ean?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/netbox_sys_plugin/forms/machine.py b/netbox_sys_plugin/forms/machine.py
index e678197..645391a 100644
--- a/netbox_sys_plugin/forms/machine.py
+++ b/netbox_sys_plugin/forms/machine.py
@@ -56,7 +56,7 @@ class VmTypeForm(NetBoxModelForm):
         model = VirtualMachineType
         fields = ('virtual_machine','virtual_machine_type_name', 'virtual_machine_type_desc','tags')
 
-    def clean_cluster(self):
+    def clean_virtual_machine(self):
 
         virtual_machine = self.cleaned_data.get("virtual_machine")
 
-- 
GitLab


From c4914bb8a9c2b0c246319f034bdc642700a2a70a Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Wed, 20 Nov 2024 14:59:45 +0000
Subject: [PATCH 06/11] =?UTF-8?q?=F0=9F=8F=97=EF=B8=8F=20Naming=20of=20obj?=
 =?UTF-8?q?ects=20changed=20and=20included=20Type=20Table?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/api/serializers.py          | 23 ++++-
 netbox_sys_plugin/api/urls.py                 |  3 +-
 netbox_sys_plugin/api/views.py                | 11 ++-
 netbox_sys_plugin/filtersets.py               |  6 +-
 netbox_sys_plugin/forms/machine.py            | 94 +++++++++++++++++--
 ...dvirtualmachinetype_virtualmachinetype.py} | 24 ++++-
 ...lter_virtualmachinetype_unique_together.py | 17 ++++
 netbox_sys_plugin/models/__init__.py          |  4 +-
 netbox_sys_plugin/models/machine.py           | 63 +++++++++++--
 netbox_sys_plugin/navigation.py               | 27 +++++-
 netbox_sys_plugin/tables.py                   | 42 ++++++++-
 netbox_sys_plugin/template_content.py         |  2 +-
 ...l => vm_vmassignedvirtualmachinetype.html} |  0
 .../vmassignedvirtualmachinetype.html         | 48 ++++++++++
 netbox_sys_plugin/urls.py                     | 13 ++-
 netbox_sys_plugin/views.py                    | 35 ++++++-
 16 files changed, 372 insertions(+), 40 deletions(-)
 rename netbox_sys_plugin/migrations/{0003_virtualmachinetype.py => 0003_vmassignedvirtualmachinetype_virtualmachinetype.py} (55%)
 create mode 100644 netbox_sys_plugin/migrations/0004_alter_virtualmachinetype_unique_together.py
 rename netbox_sys_plugin/templates/netbox_sys_plugin/{vm_virtualmachinetype.html => vm_vmassignedvirtualmachinetype.html} (100%)
 create mode 100644 netbox_sys_plugin/templates/netbox_sys_plugin/vmassignedvirtualmachinetype.html

diff --git a/netbox_sys_plugin/api/serializers.py b/netbox_sys_plugin/api/serializers.py
index e4c45da..d180901 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 virtualization.models import VirtualMachine, Cluster, ClusterType
-from .. models import VirtualMachineMaintenance, ProviderCredentials,VirtualMachineType
+from .. models import VirtualMachineMaintenance, ProviderCredentials,VmAssignedVirtualMachineType, VirtualMachineType
 from django.contrib.contenttypes.models import ContentType
 
 #Netbox Data Serializer
@@ -14,6 +14,12 @@ class ClusterSerializer(serializers.ModelSerializer):
         model = Cluster
         fields = ['id', 'name']
 
+class ClusterTypeSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = ClusterType
+        fields = ['id', 'name']
+
+
 #Plugin Data Serializer
 class VirtualMachineMaintenanceSerializer(serializers.ModelSerializer):
     id = serializers.IntegerField(read_only=True)
@@ -37,13 +43,24 @@ class ProviderCredentialsSerializer(serializers.ModelSerializer):
         #fields = ['id','maintenance_window','assigned_object_type','assigned_object_id','assigned_object_type','virtual_machine']
         fields = '__all__'
 
-class VirtualMachineTypeSerializer(serializers.ModelSerializer):
+class VmAssignedVirtualMachineTypeSerializer(serializers.ModelSerializer):
     id = serializers.IntegerField(read_only=True)
     virtual_machine = VirtualMachineSerializer(source='assigned_object', read_only=True)
     assigned_object_id = serializers.IntegerField(write_only=True)
     assigned_object_type = serializers.CharField(write_only=True)
 
+    class Meta:
+        model = VmAssignedVirtualMachineType
+        #fields = ['id','maintenance_window','assigned_object_type','assigned_object_id','assigned_object_type','virtual_machine']
+        fields = '__all__'
+
+class VirtualMachineTypeSerializer(serializers.ModelSerializer):
+    id = serializers.IntegerField(read_only=True)
+    cluster_type = ClusterTypeSerializer(source='assigned_object', read_only=True)
+    assigned_object_id = serializers.IntegerField(write_only=True)
+    assigned_object_type = serializers.CharField(write_only=True)
+
     class Meta:
         model = VirtualMachineType
         #fields = ['id','maintenance_window','assigned_object_type','assigned_object_id','assigned_object_type','virtual_machine']
-        fields = '__all__'
\ No newline at end of file
+        fields = '__all__'
diff --git a/netbox_sys_plugin/api/urls.py b/netbox_sys_plugin/api/urls.py
index 8454336..95a045a 100644
--- a/netbox_sys_plugin/api/urls.py
+++ b/netbox_sys_plugin/api/urls.py
@@ -1,9 +1,10 @@
 from rest_framework.routers import DefaultRouter
-from .views import VirtualMachineMaintenanceViewSet, ProviderCredentialsViewSet, VirtualMachineTypeViewSet
+from .views import VirtualMachineMaintenanceViewSet, ProviderCredentialsViewSet, VmAssignedVirtualMachineTypeViewSet, VirtualMachineTypeViewSet
 
 router = DefaultRouter()
 router.register(r'MaintenanceWindow', VirtualMachineMaintenanceViewSet)
 router.register(r'ProviderCredentials', ProviderCredentialsViewSet)
+router.register(r'AssignedVmType', VmAssignedVirtualMachineTypeViewSet)
 router.register(r'VmType', VirtualMachineTypeViewSet)
 
 app_name = "netbox_sys_plugin"
diff --git a/netbox_sys_plugin/api/views.py b/netbox_sys_plugin/api/views.py
index 40331ea..9666730 100644
--- a/netbox_sys_plugin/api/views.py
+++ b/netbox_sys_plugin/api/views.py
@@ -1,8 +1,8 @@
 from rest_framework import viewsets, status
 from rest_framework.decorators import action
 from rest_framework.response import Response
-from .. models import VirtualMachineMaintenance, ProviderCredentials, VirtualMachineType
-from . serializers import VirtualMachineMaintenanceSerializer, ProviderCredentialsSerializer, VirtualMachineTypeSerializer
+from .. models import VirtualMachineMaintenance, ProviderCredentials, VmAssignedVirtualMachineType, VirtualMachineType
+from . serializers import VirtualMachineMaintenanceSerializer, ProviderCredentialsSerializer, VmAssignedVirtualMachineTypeSerializer, VirtualMachineTypeSerializer
 
 class VirtualMachineMaintenanceViewSet(viewsets.ModelViewSet):
     queryset = VirtualMachineMaintenance.objects.all()
@@ -16,7 +16,12 @@ class ProviderCredentialsViewSet(viewsets.ModelViewSet):
     http_method_names = ["get", "post", "patch", "delete", "options"]
 
 
+class VmAssignedVirtualMachineTypeViewSet(viewsets.ModelViewSet):
+    queryset = VmAssignedVirtualMachineType.objects.all()
+    serializer_class = VmAssignedVirtualMachineTypeSerializer
+    http_method_names = ["get", "post", "patch", "delete", "options"]
+
 class VirtualMachineTypeViewSet(viewsets.ModelViewSet):
     queryset = VirtualMachineType.objects.all()
     serializer_class = VirtualMachineTypeSerializer
-    http_method_names = ["get", "post", "patch", "delete", "options"]
\ No newline at end of file
+    http_method_names = ["get", "post", "patch", "delete", "options"]
diff --git a/netbox_sys_plugin/filtersets.py b/netbox_sys_plugin/filtersets.py
index 6dae0a4..fae3a5d 100644
--- a/netbox_sys_plugin/filtersets.py
+++ b/netbox_sys_plugin/filtersets.py
@@ -4,7 +4,7 @@ import django_filters
 from netbox.filtersets import NetBoxModelFilterSet
 from virtualization.models import VirtualMachine, Cluster
 from django.db.models import Q
-from .models import VirtualMachineMaintenance, ProviderCredentials, VirtualMachineType
+from .models import VirtualMachineMaintenance, ProviderCredentials, VmAssignedVirtualMachineType
 
 
 class VmMaintenanceFilterSet(NetBoxModelFilterSet):
@@ -29,7 +29,7 @@ class VmMaintenanceFilterSet(NetBoxModelFilterSet):
             return queryset
         return queryset.filter(Q(maintenance_window__icontains=value))
     
-class VmTypeFilterSet(NetBoxModelFilterSet):
+class VmAssignedVmTypeFilterSet(NetBoxModelFilterSet):
     """VirtualMachineType filterset definition class"""
 
     virtualmachine = django_filters.ModelMultipleChoiceFilter(
@@ -41,7 +41,7 @@ class VmTypeFilterSet(NetBoxModelFilterSet):
 
     class Meta:
         """Meta class"""     
-        model = VirtualMachineType
+        model = VmAssignedVirtualMachineType
         fields = ('virtual_machine_type_name','virtual_machine_type_desc', 'virtualmachine')
 
     # pylint: disable=W0613
diff --git a/netbox_sys_plugin/forms/machine.py b/netbox_sys_plugin/forms/machine.py
index 645391a..71e36b2 100644
--- a/netbox_sys_plugin/forms/machine.py
+++ b/netbox_sys_plugin/forms/machine.py
@@ -1,6 +1,6 @@
 """Forms definitions"""
 
-from virtualization.models import VirtualMachine
+from virtualization.models import VirtualMachine, ClusterType
 from django.contrib.contenttypes.models import ContentType
 from django.utils.translation import gettext_lazy as _
 from django.core.exceptions import ValidationError
@@ -11,9 +11,9 @@ from netbox.forms import (
     NetBoxModelFilterSetForm,
 )
 from utilities.forms.fields import DynamicModelChoiceField
-from ..models import VirtualMachineType, VirtualMachineMaintenance
+from ..models import VmAssignedVirtualMachineType, VirtualMachineMaintenance, VirtualMachineType
 
-class VmTypeForm(NetBoxModelForm):
+class VmAssignedVmTypeForm(NetBoxModelForm):
     """
     GUI form to add or edit a Virtual Machine Type.
     """
@@ -46,14 +46,14 @@ class VmTypeForm(NetBoxModelForm):
         else:
             current_virtual_machine_id = None
 
-        assigned_virtual_machines = VirtualMachineType.objects.filter(
+        assigned_virtual_machines = VmAssignedVirtualMachineType.objects.filter(
             assigned_object_type=ContentType.objects.get_for_model(VirtualMachine)
         ).exclude(assigned_object_id=current_virtual_machine_id).values_list('assigned_object_id', flat=True)
         self.fields['virtual_machine'].queryset = VirtualMachine.objects.exclude(id__in=assigned_virtual_machines)
 
     class Meta:
         """Meta class"""
-        model = VirtualMachineType
+        model = VmAssignedVirtualMachineType
         fields = ('virtual_machine','virtual_machine_type_name', 'virtual_machine_type_desc','tags')
 
     def clean_virtual_machine(self):
@@ -62,7 +62,7 @@ class VmTypeForm(NetBoxModelForm):
 
         virtual_machine_ct = ContentType.objects.get_for_model(VirtualMachine)
 
-        if VirtualMachineType.objects.filter(
+        if VmAssignedVirtualMachineType.objects.filter(
             assigned_object_type=virtual_machine_ct,
             assigned_object_id=virtual_machine.id
         ).exclude(pk=self.instance.pk).exists():
@@ -93,6 +93,88 @@ class VmTypeForm(NetBoxModelForm):
         )
         return super().save(*args, **kwargs)
 
+class VmAssignedVmTypeFilterForm(NetBoxModelFilterSetForm):
+    """Virtual Machine Type filter form definition class"""
+
+    model = VmAssignedVirtualMachineType
+
+class VmTypeForm(NetBoxModelForm):
+    """
+    GUI form to add or edit a Virtual Machine Type.
+    """
+
+    cluster_type = DynamicModelChoiceField(
+        queryset=ClusterType.objects.all(), required=True, label="Cluster Type"
+    )
+    virtual_machine_type_name = forms.CharField(
+        max_length=200, min_length=1, required=True, label="Name"
+    )
+    virtual_machine_type_desc = forms.CharField(
+        max_length=50, min_length=1, required=False, label="Description"
+    )
+
+
+    def __init__(self, *args, **kwargs):
+        # Initialize helper selectors
+        instance = kwargs.get("instance")
+        initial = kwargs.get("initial", {}).copy()
+        if instance:
+            if isinstance(instance.assigned_object, ClusterType):
+                initial["cluster_type"] = instance.assigned_object
+        kwargs["initial"] = initial
+        super().__init__(*args, **kwargs)
+
+        if instance:
+            current_cluster_type_id = instance.assigned_object.id if instance.assigned_object else None
+        else:
+            current_cluster_type_id = None
+
+        assigned_cluster_types = VirtualMachineType.objects.filter(
+            assigned_object_type=ContentType.objects.get_for_model(ClusterType)
+        ).exclude(assigned_object_id=current_cluster_type_id).values_list('assigned_object_id', flat=True)
+        self.fields['cluster_type'].queryset = ClusterType.objects.exclude(id__in=assigned_cluster_types)
+
+    class Meta:
+        """Meta class"""
+        model = VirtualMachineType
+        fields = ('cluster_type','virtual_machine_type_name', 'virtual_machine_type_desc','tags')
+
+    def clean_cluster(self):
+
+        cluster_type = self.cleaned_data.get("cluster_type")
+
+        cluster_type_ct = ContentType.objects.get_for_model(ClusterType)
+
+        if VirtualMachineType.objects.filter(
+            assigned_object_type=cluster_type_ct,assigned_object_id=cluster_type.id
+                ).exclude(pk=self.instance.pk).exists():
+            raise ValidationError()
+        return cluster_type
+
+    def clean(self):
+        """
+        Validates form inputs before submitting:
+        """
+        super().clean()
+
+        if self.errors.get(f"virtual_machine_type_name"):
+            return
+        
+        cluster_type = self.cleaned_data.get("cluster_type")
+
+        #Check if cluster is assigned corretly
+        if (not cluster_type):
+            raise ValidationError(
+                {"__all__": "A Virtual Machine Type needs to be associated with the Cluster Type"},
+            )
+
+    def save(self, *args, **kwargs):
+        # Set assigned object
+        self.instance.assigned_object = (
+            self.cleaned_data.get("cluster_type")
+        )
+        return super().save(*args, **kwargs)
+
 class VmTypeFilterForm(NetBoxModelFilterSetForm):
     """Virtual Machine Type filter form definition class"""
 
diff --git a/netbox_sys_plugin/migrations/0003_virtualmachinetype.py b/netbox_sys_plugin/migrations/0003_vmassignedvirtualmachinetype_virtualmachinetype.py
similarity index 55%
rename from netbox_sys_plugin/migrations/0003_virtualmachinetype.py
rename to netbox_sys_plugin/migrations/0003_vmassignedvirtualmachinetype_virtualmachinetype.py
index c7e67e7..c35fd43 100644
--- a/netbox_sys_plugin/migrations/0003_virtualmachinetype.py
+++ b/netbox_sys_plugin/migrations/0003_vmassignedvirtualmachinetype_virtualmachinetype.py
@@ -1,4 +1,4 @@
-# Generated by Django 4.2.16 on 2024-11-19 15:30
+# Generated by Django 4.2.16 on 2024-11-20 14:43
 
 from django.db import migrations, models
 import django.db.models.deletion
@@ -16,7 +16,7 @@ class Migration(migrations.Migration):
 
     operations = [
         migrations.CreateModel(
-            name='VirtualMachineType',
+            name='VmAssignedVirtualMachineType',
             fields=[
                 ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
                 ('created', models.DateTimeField(auto_now_add=True, null=True)),
@@ -28,6 +28,26 @@ class Migration(migrations.Migration):
                 ('assigned_object_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(('app_label', 'virtualization'), ('model', 'VirtualMachine'))), null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')),
                 ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
             ],
+            options={
+                'verbose_name': 'Assigned Machine Type',
+                'verbose_name_plural': 'Assigned Machine Types',
+                'ordering': ['assigned_object_id'],
+                'unique_together': {('assigned_object_id',)},
+            },
+        ),
+        migrations.CreateModel(
+            name='VirtualMachineType',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
+                ('created', models.DateTimeField(auto_now_add=True, null=True)),
+                ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+                ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
+                ('virtual_machine_type_name', models.CharField(blank=True, default=None, max_length=50, null=True)),
+                ('virtual_machine_type_desc', models.CharField(blank=True, default=None, max_length=100, null=True)),
+                ('assigned_object_id', models.PositiveBigIntegerField(blank=True, null=True)),
+                ('assigned_object_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(('app_label', 'virtualization'), ('model', 'ClusterType'))), null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')),
+                ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
+            ],
             options={
                 'verbose_name': 'Machine Type',
                 'verbose_name_plural': 'Machine Types',
diff --git a/netbox_sys_plugin/migrations/0004_alter_virtualmachinetype_unique_together.py b/netbox_sys_plugin/migrations/0004_alter_virtualmachinetype_unique_together.py
new file mode 100644
index 0000000..1d662ad
--- /dev/null
+++ b/netbox_sys_plugin/migrations/0004_alter_virtualmachinetype_unique_together.py
@@ -0,0 +1,17 @@
+# Generated by Django 4.2.16 on 2024-11-20 14:54
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('netbox_sys_plugin', '0003_vmassignedvirtualmachinetype_virtualmachinetype'),
+    ]
+
+    operations = [
+        migrations.AlterUniqueTogether(
+            name='virtualmachinetype',
+            unique_together={('assigned_object_id', 'virtual_machine_type_name')},
+        ),
+    ]
diff --git a/netbox_sys_plugin/models/__init__.py b/netbox_sys_plugin/models/__init__.py
index d300513..bf52380 100644
--- a/netbox_sys_plugin/models/__init__.py
+++ b/netbox_sys_plugin/models/__init__.py
@@ -1,4 +1,4 @@
 """Models definitions"""
 
-from .provider import ProviderCredentials
-from .machine import VirtualMachineType, VirtualMachineMaintenance
\ No newline at end of file
+from .provider import *
+from .machine import *
\ No newline at end of file
diff --git a/netbox_sys_plugin/models/machine.py b/netbox_sys_plugin/models/machine.py
index d2dd85c..310d3c2 100644
--- a/netbox_sys_plugin/models/machine.py
+++ b/netbox_sys_plugin/models/machine.py
@@ -10,14 +10,62 @@ from django.core.validators import (
 from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
 from django.contrib.contenttypes.models import ContentType
 from django.db import models
-from virtualization.models import VirtualMachine
+from virtualization.models import VirtualMachine, ClusterType
 from netbox.models import NetBoxModel
 
 VM_ASSIGNMENT_MODELS = models.Q(models.Q(app_label="virtualization", model="VirtualMachine"))
+CLUSTERTYPE_ASSIGNMENT_MODELS = models.Q(models.Q(app_label="virtualization", model="ClusterType"))
 
+#VM Type
+class VirtualMachineType(NetBoxModel):
+    """Cluster ProviderInfo definition class"""
 
+    virtual_machine_type_name = models.CharField(
+        default=None, blank=True, null=True,
+        max_length=50,
+        )
+    
+    virtual_machine_type_desc = models.CharField(
+        default=None, blank=True, null=True,
+        max_length=100)
 
-class VirtualMachineType(NetBoxModel):
+    assigned_object_type = models.ForeignKey(
+        to=ContentType,
+        limit_choices_to=CLUSTERTYPE_ASSIGNMENT_MODELS,
+        on_delete=models.PROTECT,
+        null=True,
+        blank=True,
+    )
+    assigned_object_id = models.PositiveBigIntegerField(null=True, blank=True)
+    assigned_object = GenericForeignKey(
+        ct_field="assigned_object_type",
+        fk_field="assigned_object_id",
+    )
+
+    class Meta:
+        """Meta class"""
+        unique_together = ["assigned_object_id","virtual_machine_type_name"]
+        ordering = ["assigned_object_id"]
+        verbose_name = "Machine Type"
+        verbose_name_plural = "Machine Types"
+
+
+    def __str__(self):
+        return f"for Cluster Type ID {self.assigned_object_id}"
+
+    def get_absolute_url(self):
+        """override"""
+        return reverse("plugins:netbox_sys_plugin:virtualmachinetype", args=[self.pk])
+
+GenericRelation(
+    to=VirtualMachineType,
+    content_type_field="assigned_object_type",
+    object_id_field="assigned_object_id",
+    related_query_name="ClusterType",
+).contribute_to_class(ClusterType, "VirtualMachineType")
+
+#VM Assigned Type
+class VmAssignedVirtualMachineType(NetBoxModel):
     """Virtual Machine Type Info definition class"""
 
     virtual_machine_type_name = models.CharField(
@@ -46,8 +94,8 @@ class VirtualMachineType(NetBoxModel):
         """Meta class"""
         unique_together = ["assigned_object_id"]
         ordering = ["assigned_object_id"]
-        verbose_name = "Machine Type"
-        verbose_name_plural = "Machine Types"
+        verbose_name = "Assigned Machine Type"
+        verbose_name_plural = "Assigned Machine Types"
 
 
     def __str__(self):
@@ -55,15 +103,16 @@ class VirtualMachineType(NetBoxModel):
 
     def get_absolute_url(self):
         """override"""
-        return reverse("plugins:netbox_sys_plugin:virtualmachinetype", args=[self.pk])
+        return reverse("plugins:netbox_sys_plugin:vmassignedvirtualmachinetype", args=[self.pk])
 
 GenericRelation(
-    to=VirtualMachineType,
+    to=VmAssignedVirtualMachineType,
     content_type_field="assigned_object_type",
     object_id_field="assigned_object_id",
     related_query_name="VirtualMachine",
-).contribute_to_class(VirtualMachine, "VirtualMachineType")
+).contribute_to_class(VirtualMachine, "VmAssignedVirtualMachineType")
 
+#VM Maintenance
 class VirtualMachineMaintenance(NetBoxModel):
     """virtual machine maintenance definition class"""
 
diff --git a/netbox_sys_plugin/navigation.py b/netbox_sys_plugin/navigation.py
index a2db5e9..c9e7582 100644
--- a/netbox_sys_plugin/navigation.py
+++ b/netbox_sys_plugin/navigation.py
@@ -27,13 +27,23 @@ cluster_provider_credentials_buttons = [
     ),
 ]
 
+vm_assigned_machine_type_buttons = [
+    PluginMenuButton(
+        link="plugins:netbox_sys_plugin:vmassignedvirtualmachinetype_add",
+        title="Add",
+        icon_class="mdi mdi-plus-thick",
+        color=ButtonColorChoices.GREEN,
+        permissions=["netbox_sys_plugin.add_vmassignedmachinetype"],
+    ),
+]
+
 vm_machine_type_buttons = [
     PluginMenuButton(
         link="plugins:netbox_sys_plugin:virtualmachinetype_add",
         title="Add",
         icon_class="mdi mdi-plus-thick",
         color=ButtonColorChoices.GREEN,
-        permissions=["netbox_sys_plugin.add_machinetype"],
+        permissions=["netbox_sys_plugin.add_vmmachinetype"],
     ),
 ]
 
@@ -49,9 +59,9 @@ clusterProviderCredentialsItem = [
 
 vmMachineItem = [
     PluginMenuItem(
-        link="plugins:netbox_sys_plugin:virtualmachinetype_list",
-        link_text="Type",
-        buttons=vm_machine_type_buttons,
+        link="plugins:netbox_sys_plugin:vmassignedvirtualmachinetype_list",
+        link_text="Type Assignment",
+        buttons=vm_assigned_machine_type_buttons,
     ),
     PluginMenuItem(
         link="plugins:netbox_sys_plugin:virtualmachinemaintenance_list",
@@ -60,12 +70,21 @@ vmMachineItem = [
     ),
 ]
 
+operItem =[
+    PluginMenuItem(
+        link="plugins:netbox_sys_plugin:virtualmachinetype_list",
+        link_text="Virtual Machine Types",
+        buttons=vm_machine_type_buttons,
+    ),
+
+]
 #Menu
 menu = PluginMenu(
     label="Sys",
     groups=(
         ("Virtual Machine", (vmMachineItem)),
         ("Provider", clusterProviderCredentialsItem),
+        ("Operation", (operItem)),
             ),
     icon_class="mdi mdi-all-inclusive-box-outline",
 )
\ No newline at end of file
diff --git a/netbox_sys_plugin/tables.py b/netbox_sys_plugin/tables.py
index 07a313d..4d4b80a 100644
--- a/netbox_sys_plugin/tables.py
+++ b/netbox_sys_plugin/tables.py
@@ -2,7 +2,7 @@
 
 import django_tables2 as tables
 from netbox.tables import NetBoxTable, columns
-from .models import VirtualMachineMaintenance, ProviderCredentials, VirtualMachineType
+from .models import VirtualMachineMaintenance, ProviderCredentials, VmAssignedVirtualMachineType, VirtualMachineType
 
 #VmMaintenance
 class VmMaintenanceTable(NetBoxTable):
@@ -80,8 +80,8 @@ class ProviderCredentialsTable(NetBoxTable):
 
         default_columns = ('id','provider_url','provider_username','provider_password_vault_url','assigned_object')
 
-#VmType
-class VmTypeTable(NetBoxTable):
+#VmAssignedType
+class VmAssignedVmTypeTable(NetBoxTable):
     """VM Maintenance Table definition class"""
 
     pk = columns.ToggleColumn()
@@ -104,6 +104,42 @@ class VmTypeTable(NetBoxTable):
         verbose_name="Type Description",
     )
 
+    class Meta(NetBoxTable.Meta):
+        """Meta class"""  
+        model = VmAssignedVirtualMachineType
+        fields = (
+            "pk",
+            "id",
+            "virtual_machine_type_name",
+            "virtual_machine_type_desc", 
+        )
+
+        default_columns = ('id','virtual_machine_type_name','virtual_machine_type_desc','assigned_object')
+
+#VmType
+class VmTypeTable(NetBoxTable):
+    """VM Maintenance Table definition class"""
+
+    pk = columns.ToggleColumn()
+    id = tables.Column(
+        linkify=False,
+    )
+    
+    assigned_object = tables.Column(
+        linkify=True,
+        orderable=False,
+        verbose_name="Cluster Type",
+    )
+
+    virtual_machine_type_name = tables.Column(
+        linkify=True,
+        verbose_name="Machine Type Name",
+    )
+    virtual_machine_type_desc = tables.Column(
+        linkify=True,
+        verbose_name="Machine Type Descritpion",
+    )
+
     class Meta(NetBoxTable.Meta):
         """Meta class"""  
         model = VirtualMachineType
diff --git a/netbox_sys_plugin/template_content.py b/netbox_sys_plugin/template_content.py
index 4260247..f390d53 100644
--- a/netbox_sys_plugin/template_content.py
+++ b/netbox_sys_plugin/template_content.py
@@ -13,7 +13,7 @@ class VmTable(PluginTemplateExtension):
         return self.render('netbox_sys_plugin/vm_vmmaintenance.html', extra_context={'vmmaintenanceinfo': models.VirtualMachineMaintenance.objects.filter(VirtualMachine=self.context['object'])})
     
     def right_page(self):
-        return self.render('netbox_sys_plugin/vm_virtualmachinetype.html', extra_context={'vmtypeinfo': models.VirtualMachineType.objects.filter(VirtualMachine=self.context['object'])})
+        return self.render('netbox_sys_plugin/vm_vmassignedvirtualmachinetype.html', extra_context={'vmtypeinfo': models.VmAssignedVirtualMachineType.objects.filter(VirtualMachine=self.context['object'])})
     
 class ProviderCredentialsTable(PluginTemplateExtension):
     """Provider Credentials object template"""
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/vm_virtualmachinetype.html b/netbox_sys_plugin/templates/netbox_sys_plugin/vm_vmassignedvirtualmachinetype.html
similarity index 100%
rename from netbox_sys_plugin/templates/netbox_sys_plugin/vm_virtualmachinetype.html
rename to netbox_sys_plugin/templates/netbox_sys_plugin/vm_vmassignedvirtualmachinetype.html
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/vmassignedvirtualmachinetype.html b/netbox_sys_plugin/templates/netbox_sys_plugin/vmassignedvirtualmachinetype.html
new file mode 100644
index 0000000..e7a8e89
--- /dev/null
+++ b/netbox_sys_plugin/templates/netbox_sys_plugin/vmassignedvirtualmachinetype.html
@@ -0,0 +1,48 @@
+{% extends 'generic/object.html' %}
+
+{% block title%}
+Virtual Machine Type for {{ object.assigned_object }} 
+{% endblock title%}
+
+{% block content %}
+<div class="row mb-3">
+  <div class="col col-md-6">
+    <div class="card">
+      <h5 class="card-header">Virtual Machine Types</h5>
+      <div class="card-body">
+        <table class="table table-hover attr-table">
+          <tr>
+            <th scope="row">Name</th>
+            <td>{{ object.virtual_machine_type_name }}</td>
+          </tr>
+          <tr>
+            <th scope="row">Description</th>
+            <td>{{ object.virtual_machine_type_desc }}</td>
+          </tr>
+        </table>
+      </div>
+    </div>
+    {% include 'inc/panels/custom_fields.html' %}
+    {% include 'inc/panels/tags.html' %}
+  </div>
+  <div class="col col-md-6">
+    <div class="card">
+      <h5 class="card-header">Assigned Object</h5>
+      <div class="card-body">
+        <table class="table table-hover attr-table">
+          <tr>
+            <th scope="row">{{ object.assigned_object_type|cut:"virtualization | " }}</th>
+            <td>
+              {% if object.assigned_object %}
+                <a href="{{ object.assigned_object.get_absolute_url }}">{{ object.assigned_object }}</a>
+              {% else %}
+                {{ ''|placeholder }}
+              {% endif %}
+            </td>
+          </tr>
+        </table>
+      </div>
+    </div>
+  </div>
+</div>
+{% endblock content %}
diff --git a/netbox_sys_plugin/urls.py b/netbox_sys_plugin/urls.py
index ac9e61f..94b7d55 100644
--- a/netbox_sys_plugin/urls.py
+++ b/netbox_sys_plugin/urls.py
@@ -19,8 +19,16 @@ urlpatterns = (
     path('provider-credentials/add/', views.ProviderCredentialsEditView.as_view(), name='providercredentials_add'),
     path('provider-credentials/<int:pk>/edit/', views.ProviderCredentialsEditView.as_view(), name='providercredentials_edit'),
     path('provider-credentials/<int:pk>/delete/', views.ProviderCredentialsDeleteView.as_view(), name='providercredentials_delete'),
-    path('provider-credentials/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='providercredentials_changelog', kwargs={'model': models.VirtualMachineMaintenance}),
-    path('provider-credentials/<int:pk>/journal/', ObjectJournalView.as_view(), name='providercredentials_journal', kwargs={'model': models.VirtualMachineMaintenance}),
+    path('provider-credentials/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='providercredentials_changelog', kwargs={'model': models.ProviderCredentials}),
+    path('provider-credentials/<int:pk>/journal/', ObjectJournalView.as_view(), name='providercredentials_journal', kwargs={'model': models.ProviderCredentials}),
+    #virtualMachineAssignedType
+    path('vm-assigned-type/<int:pk>/', views.VmAssignedVmTypeView.as_view(), name='vmassignedvirtualmachinetype'),
+    path('vm-assigned-type/', views.VmAssignedVmTypeListView.as_view(), name='vmassignedvirtualmachinetype_list'),
+    path('vm-assigned-type/add/', views.VmAssignedVmTypeEditView.as_view(), name='vmassignedvirtualmachinetype_add'),
+    path('vm-assigned-type/<int:pk>/edit/', views.VmAssignedVmTypeEditView.as_view(), name='vmassignedvirtualmachinetype_edit'),
+    path('vm-assigned-type/<int:pk>/delete/', views.VmAssignedVmTypeDeleteView.as_view(), name='vmassignedvirtualmachinetype_delete'),
+    path('vm-assigned-type/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='vmassignedvirtualmachinetype_changelog', kwargs={'model': models.VmAssignedVirtualMachineType}),
+    path('vm-assigned-type/<int:pk>/journal/', ObjectJournalView.as_view(), name='vmassignedvirtualmachinetype_journal', kwargs={'model': models.VmAssignedVirtualMachineType}),
     #virtualMachineType
     path('vm-type/<int:pk>/', views.VmTypeView.as_view(), name='virtualmachinetype'),
     path('vm-type/', views.VmTypeListView.as_view(), name='virtualmachinetype_list'),
@@ -31,4 +39,5 @@ urlpatterns = (
     path('vm-type/<int:pk>/journal/', ObjectJournalView.as_view(), name='virtualmachinetype_journal', kwargs={'model': models.VirtualMachineType}),
 
 
+
 )
diff --git a/netbox_sys_plugin/views.py b/netbox_sys_plugin/views.py
index fe34158..123e31e 100644
--- a/netbox_sys_plugin/views.py
+++ b/netbox_sys_plugin/views.py
@@ -59,6 +59,35 @@ class ProviderCredentialsEditView(generic.ObjectEditView):
     queryset = models.ProviderCredentials.objects.all()
     form = forms.ProviderCredentialsForm
 
+#VmAssignedType
+class  VmAssignedVmTypeView(generic.ObjectView):
+    """ Virtual Machine Type view definition"""
+
+    queryset = (
+        models.VmAssignedVirtualMachineType.objects.all()
+    )
+
+class VmAssignedVmTypeListView(generic.ObjectListView):
+    """Cluster Type Virtual Machine Type list view definition"""
+
+    queryset = models.VmAssignedVirtualMachineType.objects.all()
+    table = tables.VmAssignedVmTypeTable
+    filterset = filtersets.VmAssignedVmTypeFilterSet
+    filterset_form = forms.VmAssignedVmTypeFilterForm
+
+class VmAssignedVmTypeDeleteView(generic.ObjectDeleteView):
+    """Vm maintenance delete view definition"""
+
+    queryset = models.VmAssignedVirtualMachineType.objects.all()
+
+class VmAssignedVmTypeEditView(generic.ObjectEditView):
+    """
+    Defines the edit view for the Vm maintenance django model.
+    """
+    queryset = models.VmAssignedVirtualMachineType.objects.all()
+    form = forms.VmAssignedVmTypeForm
+
+
 #VmType
 class  VmTypeView(generic.ObjectView):
     """ Virtual Machine Type view definition"""
@@ -72,8 +101,8 @@ class VmTypeListView(generic.ObjectListView):
 
     queryset = models.VirtualMachineType.objects.all()
     table = tables.VmTypeTable
-    filterset = filtersets.VmTypeFilterSet
-    filterset_form = forms.VmTypeFilterForm
+    #filterset = filtersets.VmTypeFilterSet
+    #filterset_form = forms.VmTypeFilterForm
 
 class VmTypeDeleteView(generic.ObjectDeleteView):
     """Vm maintenance delete view definition"""
@@ -85,4 +114,4 @@ class VmTypeEditView(generic.ObjectEditView):
     Defines the edit view for the Vm maintenance django model.
     """
     queryset = models.VirtualMachineType.objects.all()
-    form = forms.VmTypeForm
\ No newline at end of file
+    form = forms.VmTypeForm
-- 
GitLab


From 3e2f5e8b09e495d21364003785c995a2a72e2f92 Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Wed, 20 Nov 2024 16:54:28 +0000
Subject: [PATCH 07/11] =?UTF-8?q?=F0=9F=9A=A7=20Fix=20Type=20table=20conne?=
 =?UTF-8?q?ction=20and=20fix=20UI=20elements?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 ...ox_sys_vm_assigned_virtual_machine_type.md | 11 ++++++
 netbox_sys_plugin/filtersets.py               |  2 +-
 netbox_sys_plugin/forms/machine.py            | 34 ++++---------------
 ...005_vmassignedvirtualmachinetype_vmtype.py | 20 +++++++++++
 ...ignedvirtualmachinetype_vmtype_and_more.py | 24 +++++++++++++
 ...machinetype_virtualmachinetype_and_more.py | 23 +++++++++++++
 ..._virtual_machine_type_assignment_desc_a.py | 28 +++++++++++++++
 netbox_sys_plugin/models/machine.py           | 17 ++++++----
 netbox_sys_plugin/tables.py                   | 21 ++++++------
 .../cluster_providercredentials.html          |  2 +-
 .../netbox_sys_plugin/virtualmachinetype.html |  2 +-
 .../vm_vmassignedvirtualmachinetype.html      |  6 ++--
 .../vmassignedvirtualmachinetype.html         | 13 +++++--
 13 files changed, 149 insertions(+), 54 deletions(-)
 create mode 100644 docs/model/netbox_sys_vm_assigned_virtual_machine_type.md
 create mode 100644 netbox_sys_plugin/migrations/0005_vmassignedvirtualmachinetype_vmtype.py
 create mode 100644 netbox_sys_plugin/migrations/0006_remove_vmassignedvirtualmachinetype_vmtype_and_more.py
 create mode 100644 netbox_sys_plugin/migrations/0007_remove_vmassignedvirtualmachinetype_virtualmachinetype_and_more.py
 create mode 100644 netbox_sys_plugin/migrations/0008_rename_virtual_machine_type_desc_vmassignedvirtualmachinetype_virtual_machine_type_assignment_desc_a.py

diff --git a/docs/model/netbox_sys_vm_assigned_virtual_machine_type.md b/docs/model/netbox_sys_vm_assigned_virtual_machine_type.md
new file mode 100644
index 0000000..791b6da
--- /dev/null
+++ b/docs/model/netbox_sys_vm_assigned_virtual_machine_type.md
@@ -0,0 +1,11 @@
+## netbox_sys_virtual_machine_type
+
+**Mapping Redirect definition class**
+
+| 	FIELD	                             | 	TYPE	                        | 	DESCRIPTION	                                                            |
+|-------------------------------         |-----------------------------------|---------------------------------------------------------------------------|
+| 	id	                                 | 	Big (8 byte) integer	        | 	Unique ID	                                                            |
+| 	assigned_object_type_id              | 	Big (8 byte) integer	        | 	cluster type type ID	                                                |
+| 	assigned_object_id	                 | 	Big (8 byte) integer	        | 	cluster type unique ID	                                                |
+| 	virtual_machine_type_assignment_id   | 	String              	        | 	Virtual Machine type ID (refers to the table virtual_machine_tye)       |
+| 	virtual_machine_type_assignment_desc | 	String              	        | 	Virtual Machine type assignment description                             |
\ No newline at end of file
diff --git a/netbox_sys_plugin/filtersets.py b/netbox_sys_plugin/filtersets.py
index fae3a5d..6574a70 100644
--- a/netbox_sys_plugin/filtersets.py
+++ b/netbox_sys_plugin/filtersets.py
@@ -42,7 +42,7 @@ class VmAssignedVmTypeFilterSet(NetBoxModelFilterSet):
     class Meta:
         """Meta class"""     
         model = VmAssignedVirtualMachineType
-        fields = ('virtual_machine_type_name','virtual_machine_type_desc', 'virtualmachine')
+        fields = ('virtual_machine_type_name','virtual_machine_type_assignment_desc', 'virtualmachine')
 
     # pylint: disable=W0613
     def search(self, queryset, name, value):
diff --git a/netbox_sys_plugin/forms/machine.py b/netbox_sys_plugin/forms/machine.py
index 71e36b2..a936746 100644
--- a/netbox_sys_plugin/forms/machine.py
+++ b/netbox_sys_plugin/forms/machine.py
@@ -10,7 +10,7 @@ from netbox.forms import (
     NetBoxModelForm,
     NetBoxModelFilterSetForm,
 )
-from utilities.forms.fields import DynamicModelChoiceField
+from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField
 from ..models import VmAssignedVirtualMachineType, VirtualMachineMaintenance, VirtualMachineType
 
 class VmAssignedVmTypeForm(NetBoxModelForm):
@@ -22,11 +22,12 @@ class VmAssignedVmTypeForm(NetBoxModelForm):
         queryset=VirtualMachine.objects.all(), required=True, label="Virtual Machine"
     )
 
-    virtual_machine_type_name = forms.CharField(
-        max_length=200, min_length=1, required=True, label="Name"
+    virtual_machine_type_name = DynamicModelChoiceField(
+        queryset=VirtualMachineType.objects.all(),
+        required=True, label="Virtual Machine Type"
     )
     
-    virtual_machine_type_desc = forms.CharField(
+    virtual_machine_type_assignment_desc = forms.CharField(
         max_length=50, min_length=1, required=False, label="Description"
     )
 
@@ -54,7 +55,7 @@ class VmAssignedVmTypeForm(NetBoxModelForm):
     class Meta:
         """Meta class"""
         model = VmAssignedVirtualMachineType
-        fields = ('virtual_machine','virtual_machine_type_name', 'virtual_machine_type_desc','tags')
+        fields = ('virtual_machine','virtual_machine_type_name', 'virtual_machine_type_assignment_desc','tags')
 
     def clean_virtual_machine(self):
 
@@ -83,7 +84,7 @@ class VmAssignedVmTypeForm(NetBoxModelForm):
         #Check if VM is assigned corretly
         if (not virtual_machine):
             raise ValidationError(
-                {"__all__": "A Virtual Machine Type needs to be associated with the Virtual Machine"},
+                {"__all__": "The virtual machine you picked already has a Type assigned"},
             )
 
     def save(self, *args, **kwargs):
@@ -124,33 +125,12 @@ class VmTypeForm(NetBoxModelForm):
         kwargs["initial"] = initial
         super().__init__(*args, **kwargs)
 
-        if instance:
-            current_cluster_type_id = instance.assigned_object.id if instance.assigned_object else None
-        else:
-            current_cluster_type_id = None
-
-        assigned_cluster_types = VirtualMachineType.objects.filter(
-            assigned_object_type=ContentType.objects.get_for_model(ClusterType)
-        ).exclude(assigned_object_id=current_cluster_type_id).values_list('assigned_object_id', flat=True)
-        self.fields['cluster_type'].queryset = ClusterType.objects.exclude(id__in=assigned_cluster_types)
 
     class Meta:
         """Meta class"""
         model = VirtualMachineType
         fields = ('cluster_type','virtual_machine_type_name', 'virtual_machine_type_desc','tags')
 
-    def clean_cluster(self):
-
-        cluster_type = self.cleaned_data.get("cluster_type")
-
-        cluster_type_ct = ContentType.objects.get_for_model(ClusterType)
-
-        if VirtualMachineType.objects.filter(
-            assigned_object_type=cluster_type_ct,assigned_object_id=cluster_type.id
-                ).exclude(pk=self.instance.pk).exists():
-            raise ValidationError()
-        return cluster_type
-
     def clean(self):
         """
         Validates form inputs before submitting:
diff --git a/netbox_sys_plugin/migrations/0005_vmassignedvirtualmachinetype_vmtype.py b/netbox_sys_plugin/migrations/0005_vmassignedvirtualmachinetype_vmtype.py
new file mode 100644
index 0000000..de15b2e
--- /dev/null
+++ b/netbox_sys_plugin/migrations/0005_vmassignedvirtualmachinetype_vmtype.py
@@ -0,0 +1,20 @@
+# Generated by Django 4.2.16 on 2024-11-20 15:07
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('netbox_sys_plugin', '0004_alter_virtualmachinetype_unique_together'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='vmassignedvirtualmachinetype',
+            name='vmtype',
+            field=models.OneToOneField(db_column='vm_type_id', default=0, on_delete=django.db.models.deletion.CASCADE, related_name='vmtype', to='netbox_sys_plugin.virtualmachinetype'),
+            preserve_default=False,
+        ),
+    ]
diff --git a/netbox_sys_plugin/migrations/0006_remove_vmassignedvirtualmachinetype_vmtype_and_more.py b/netbox_sys_plugin/migrations/0006_remove_vmassignedvirtualmachinetype_vmtype_and_more.py
new file mode 100644
index 0000000..c038afe
--- /dev/null
+++ b/netbox_sys_plugin/migrations/0006_remove_vmassignedvirtualmachinetype_vmtype_and_more.py
@@ -0,0 +1,24 @@
+# Generated by Django 4.2.16 on 2024-11-20 15:19
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('netbox_sys_plugin', '0005_vmassignedvirtualmachinetype_vmtype'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='vmassignedvirtualmachinetype',
+            name='vmtype',
+        ),
+        migrations.AddField(
+            model_name='vmassignedvirtualmachinetype',
+            name='VirtualMachineType',
+            field=models.OneToOneField(db_column='virtual_machine_type_id', default=0, on_delete=django.db.models.deletion.CASCADE, related_name='VirtualMachineType', to='netbox_sys_plugin.virtualmachinetype'),
+            preserve_default=False,
+        ),
+    ]
diff --git a/netbox_sys_plugin/migrations/0007_remove_vmassignedvirtualmachinetype_virtualmachinetype_and_more.py b/netbox_sys_plugin/migrations/0007_remove_vmassignedvirtualmachinetype_virtualmachinetype_and_more.py
new file mode 100644
index 0000000..cc3e70f
--- /dev/null
+++ b/netbox_sys_plugin/migrations/0007_remove_vmassignedvirtualmachinetype_virtualmachinetype_and_more.py
@@ -0,0 +1,23 @@
+# Generated by Django 4.2.16 on 2024-11-20 15:30
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('netbox_sys_plugin', '0006_remove_vmassignedvirtualmachinetype_vmtype_and_more'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='vmassignedvirtualmachinetype',
+            name='VirtualMachineType',
+        ),
+        migrations.AddField(
+            model_name='vmassignedvirtualmachinetype',
+            name='virtual_machine_type_id',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_vm_types', to='netbox_sys_plugin.virtualmachinetype'),
+        ),
+    ]
diff --git a/netbox_sys_plugin/migrations/0008_rename_virtual_machine_type_desc_vmassignedvirtualmachinetype_virtual_machine_type_assignment_desc_a.py b/netbox_sys_plugin/migrations/0008_rename_virtual_machine_type_desc_vmassignedvirtualmachinetype_virtual_machine_type_assignment_desc_a.py
new file mode 100644
index 0000000..bd812dd
--- /dev/null
+++ b/netbox_sys_plugin/migrations/0008_rename_virtual_machine_type_desc_vmassignedvirtualmachinetype_virtual_machine_type_assignment_desc_a.py
@@ -0,0 +1,28 @@
+# Generated by Django 4.2.16 on 2024-11-20 16:16
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('netbox_sys_plugin', '0007_remove_vmassignedvirtualmachinetype_virtualmachinetype_and_more'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='vmassignedvirtualmachinetype',
+            old_name='virtual_machine_type_desc',
+            new_name='virtual_machine_type_assignment_desc',
+        ),
+        migrations.RemoveField(
+            model_name='vmassignedvirtualmachinetype',
+            name='virtual_machine_type_id',
+        ),
+        migrations.AlterField(
+            model_name='vmassignedvirtualmachinetype',
+            name='virtual_machine_type_name',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_vm_type', to='netbox_sys_plugin.virtualmachinetype'),
+        ),
+    ]
diff --git a/netbox_sys_plugin/models/machine.py b/netbox_sys_plugin/models/machine.py
index 310d3c2..66aef0c 100644
--- a/netbox_sys_plugin/models/machine.py
+++ b/netbox_sys_plugin/models/machine.py
@@ -51,7 +51,7 @@ class VirtualMachineType(NetBoxModel):
 
 
     def __str__(self):
-        return f"for Cluster Type ID {self.assigned_object_id}"
+        return self.virtual_machine_type_name or f"for Cluster Type ID {self.assigned_object_id}"
 
     def get_absolute_url(self):
         """override"""
@@ -67,13 +67,16 @@ GenericRelation(
 #VM Assigned Type
 class VmAssignedVirtualMachineType(NetBoxModel):
     """Virtual Machine Type Info definition class"""
-
-    virtual_machine_type_name = models.CharField(
-        default=None, blank=True, null=True,
-        max_length=50,
-        )
+  
+    virtual_machine_type_name = models.ForeignKey(
+        to="VirtualMachineType",
+        on_delete=models.SET_NULL,
+        null=True,
+        blank=True,
+        related_name="assigned_vm_type"
+    )
     
-    virtual_machine_type_desc = models.CharField(
+    virtual_machine_type_assignment_desc = models.CharField(
         default=None, blank=True, null=True,
         max_length=100)
 
diff --git a/netbox_sys_plugin/tables.py b/netbox_sys_plugin/tables.py
index 4d4b80a..bb9049b 100644
--- a/netbox_sys_plugin/tables.py
+++ b/netbox_sys_plugin/tables.py
@@ -86,7 +86,7 @@ class VmAssignedVmTypeTable(NetBoxTable):
 
     pk = columns.ToggleColumn()
     id = tables.Column(
-        linkify=False,
+        linkify=True,
     )
     
     assigned_object = tables.Column(
@@ -94,14 +94,13 @@ class VmAssignedVmTypeTable(NetBoxTable):
         orderable=False,
         verbose_name="Virtual Machine",
     )
-
     virtual_machine_type_name = tables.Column(
         linkify=True,
-        verbose_name="Type Name",
+        verbose_name="Virtual Machine Type",
     )
-    virtual_machine_type_desc = tables.Column(
+    virtual_machine_type_assignment_desc = tables.Column(
         linkify=False,
-        verbose_name="Type Description",
+        verbose_name="Description",
     )
 
     class Meta(NetBoxTable.Meta):
@@ -111,10 +110,10 @@ class VmAssignedVmTypeTable(NetBoxTable):
             "pk",
             "id",
             "virtual_machine_type_name",
-            "virtual_machine_type_desc", 
+            "virtual_machine_type_assignment_desc", 
         )
 
-        default_columns = ('id','virtual_machine_type_name','virtual_machine_type_desc','assigned_object')
+        default_columns = ('id','assigned_object','virtual_machine_type_name','virtual_machine_type_assignment_desc')
 
 #VmType
 class VmTypeTable(NetBoxTable):
@@ -122,7 +121,7 @@ class VmTypeTable(NetBoxTable):
 
     pk = columns.ToggleColumn()
     id = tables.Column(
-        linkify=False,
+        linkify=True,
     )
     
     assigned_object = tables.Column(
@@ -133,11 +132,11 @@ class VmTypeTable(NetBoxTable):
 
     virtual_machine_type_name = tables.Column(
         linkify=True,
-        verbose_name="Machine Type Name",
+        verbose_name="Virtual Machine Type",
     )
     virtual_machine_type_desc = tables.Column(
         linkify=True,
-        verbose_name="Machine Type Descritpion",
+        verbose_name="Descritpion",
     )
 
     class Meta(NetBoxTable.Meta):
@@ -150,4 +149,4 @@ class VmTypeTable(NetBoxTable):
             "virtual_machine_type_desc", 
         )
 
-        default_columns = ('id','virtual_machine_type_name','virtual_machine_type_desc','assigned_object')
+        default_columns = ('id','virtual_machine_type_name','assigned_object','virtual_machine_type_desc')
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/cluster_providercredentials.html b/netbox_sys_plugin/templates/netbox_sys_plugin/cluster_providercredentials.html
index 82082e0..71616af 100644
--- a/netbox_sys_plugin/templates/netbox_sys_plugin/cluster_providercredentials.html
+++ b/netbox_sys_plugin/templates/netbox_sys_plugin/cluster_providercredentials.html
@@ -34,7 +34,7 @@
         </tr>
         {% empty %}
         <tr>
-            <td colspan="3">No maintenance window found.</td>
+            <td colspan="3">No Provider Credentials found.</td>
         </tr>
         {% endfor %}
             </tr>
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/virtualmachinetype.html b/netbox_sys_plugin/templates/netbox_sys_plugin/virtualmachinetype.html
index e7a8e89..c33372a 100644
--- a/netbox_sys_plugin/templates/netbox_sys_plugin/virtualmachinetype.html
+++ b/netbox_sys_plugin/templates/netbox_sys_plugin/virtualmachinetype.html
@@ -8,7 +8,7 @@ Virtual Machine Type for {{ object.assigned_object }}
 <div class="row mb-3">
   <div class="col col-md-6">
     <div class="card">
-      <h5 class="card-header">Virtual Machine Types</h5>
+      <h5 class="card-header">Virtual Machine Type</h5>
       <div class="card-body">
         <table class="table table-hover attr-table">
           <tr>
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/vm_vmassignedvirtualmachinetype.html b/netbox_sys_plugin/templates/netbox_sys_plugin/vm_vmassignedvirtualmachinetype.html
index dbf759f..4154da8 100644
--- a/netbox_sys_plugin/templates/netbox_sys_plugin/vm_vmassignedvirtualmachinetype.html
+++ b/netbox_sys_plugin/templates/netbox_sys_plugin/vm_vmassignedvirtualmachinetype.html
@@ -19,8 +19,8 @@
               {% endif %}
             </td>
             <td>
-              {% if vmtype.virtual_machine_type_desc %}
-                {{ vmtype.virtual_machine_type_desc }}
+              {% if vmtype.virtual_machine_type_assignment_desc %}
+                {{ vmtype.virtual_machine_type_assignment_desc }}
               {% else %}
                 {{ ''|placeholder }}
               {% endif %}
@@ -28,7 +28,7 @@
         </tr>
         {% empty %}
         <tr>
-            <td colspan="3">No maintenance window found.</td>
+            <td colspan="3">Virtual Machine Types Found</td>
         </tr>
         {% endfor %}
             </tr>
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/vmassignedvirtualmachinetype.html b/netbox_sys_plugin/templates/netbox_sys_plugin/vmassignedvirtualmachinetype.html
index e7a8e89..aadce1c 100644
--- a/netbox_sys_plugin/templates/netbox_sys_plugin/vmassignedvirtualmachinetype.html
+++ b/netbox_sys_plugin/templates/netbox_sys_plugin/vmassignedvirtualmachinetype.html
@@ -8,16 +8,23 @@ Virtual Machine Type for {{ object.assigned_object }}
 <div class="row mb-3">
   <div class="col col-md-6">
     <div class="card">
-      <h5 class="card-header">Virtual Machine Types</h5>
+      <h5 class="card-header">Virtual Machine Type Assignedment </h5>
       <div class="card-body">
         <table class="table table-hover attr-table">
           <tr>
             <th scope="row">Name</th>
-            <td>{{ object.virtual_machine_type_name }}</td>
+            <td>
+              {% if object.virtual_machine_type_name %}
+                <a href="{{ object.virtual_machine_type_name.get_absolute_url }}">{{ object.virtual_machine_type_name }}</a>
+              {% else %}
+                {{ ''|placeholder }}
+              {% endif %}
+            </td>
           </tr>
+
           <tr>
             <th scope="row">Description</th>
-            <td>{{ object.virtual_machine_type_desc }}</td>
+            <td>{{ object.virtual_machine_type_assignment_desc }}</td>
           </tr>
         </table>
       </div>
-- 
GitLab


From 5c74e0935a10f135d3d1a4213cde9b2b1a8c3144 Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Thu, 21 Nov 2024 15:54:27 +0000
Subject: [PATCH 08/11] =?UTF-8?q?=F0=9F=8E=A8=20Improve=20structure=20and?=
 =?UTF-8?q?=20add=20validations?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/forms/machine.py            | 99 +++++++------------
 netbox_sys_plugin/forms/provider.py           | 20 +---
 ...type_virtual_machine_type_name_and_more.py | 25 +++++
 ...almachinetype_virtual_machine_type_name.py | 19 ++++
 netbox_sys_plugin/models/machine.py           | 16 +--
 5 files changed, 90 insertions(+), 89 deletions(-)
 create mode 100644 netbox_sys_plugin/migrations/0009_alter_virtualmachinetype_virtual_machine_type_name_and_more.py
 create mode 100644 netbox_sys_plugin/migrations/0010_alter_vmassignedvirtualmachinetype_virtual_machine_type_name.py

diff --git a/netbox_sys_plugin/forms/machine.py b/netbox_sys_plugin/forms/machine.py
index a936746..d90b2f7 100644
--- a/netbox_sys_plugin/forms/machine.py
+++ b/netbox_sys_plugin/forms/machine.py
@@ -22,15 +22,6 @@ class VmAssignedVmTypeForm(NetBoxModelForm):
         queryset=VirtualMachine.objects.all(), required=True, label="Virtual Machine"
     )
 
-    virtual_machine_type_name = DynamicModelChoiceField(
-        queryset=VirtualMachineType.objects.all(),
-        required=True, label="Virtual Machine Type"
-    )
-    
-    virtual_machine_type_assignment_desc = forms.CharField(
-        max_length=50, min_length=1, required=False, label="Description"
-    )
-
 
     def __init__(self, *args, **kwargs):
         # Initialize helper selectors
@@ -43,54 +34,42 @@ class VmAssignedVmTypeForm(NetBoxModelForm):
         super().__init__(*args, **kwargs)
 
         if instance:
-            current_virtual_machine_id = instance.assigned_object.id if instance.assigned_object else None
+            current_vm_id = instance.assigned_object.id if instance.assigned_object else None
         else:
-            current_virtual_machine_id = None
+            current_vm_id = None
 
-        assigned_virtual_machines = VmAssignedVirtualMachineType.objects.filter(
+        assigned_vms = VmAssignedVirtualMachineType.objects.filter(
             assigned_object_type=ContentType.objects.get_for_model(VirtualMachine)
-        ).exclude(assigned_object_id=current_virtual_machine_id).values_list('assigned_object_id', flat=True)
-        self.fields['virtual_machine'].queryset = VirtualMachine.objects.exclude(id__in=assigned_virtual_machines)
+        ).exclude(assigned_object_id=current_vm_id).values_list('assigned_object_id', flat=True)
+        self.fields['virtual_machine'].queryset = VirtualMachine.objects.exclude(id__in=assigned_vms)
 
     class Meta:
         """Meta class"""
         model = VmAssignedVirtualMachineType
         fields = ('virtual_machine','virtual_machine_type_name', 'virtual_machine_type_assignment_desc','tags')
 
-    def clean_virtual_machine(self):
-
-        virtual_machine = self.cleaned_data.get("virtual_machine")
-
-        virtual_machine_ct = ContentType.objects.get_for_model(VirtualMachine)
-
-        if VmAssignedVirtualMachineType.objects.filter(
-            assigned_object_type=virtual_machine_ct,
-            assigned_object_id=virtual_machine.id
-        ).exclude(pk=self.instance.pk).exists():
-            raise ValidationError()
-        return virtual_machine
-
     def clean(self):
         """
         Validates form inputs before submitting:
         """
         super().clean()
-
-        if self.errors.get(f"virtual_machine_type_name"):
-            return
         
-        virtual_machine = self.cleaned_data.get("virtual_machine")
+        
+        vm = self.cleaned_data.get("virtual_machine")
 
-        #Check if VM is assigned corretly
-        if (not virtual_machine):
+        #Check if Virtual Machine is assigned corretly
+        if (not vm):
             raise ValidationError(
-                {"__all__": "The virtual machine you picked already has a Type assigned"},
+                {"__all__": "Can't assign more than one Type to the same Virtual Machine"},
             )
+        
+        if self.errors.get("virtual_machine"):
+            return
 
     def save(self, *args, **kwargs):
         # Set assigned object
         self.instance.assigned_object = (
-            self.cleaned_data.get("virtual_machine")
+            self.cleaned_data.get("virtual_machine") 
         )
         return super().save(*args, **kwargs)
 
@@ -137,22 +116,23 @@ class VmTypeForm(NetBoxModelForm):
         """
         super().clean()
 
-        if self.errors.get(f"virtual_machine_type_name"):
-            return
-        
+        virtual_machine_type_name = self.cleaned_data.get("virtual_machine_type_name")
         cluster_type = self.cleaned_data.get("cluster_type")
+        cluster_type_ct = ContentType.objects.get_for_model(ClusterType)
 
-        #Check if cluster is assigned corretly
-        if (not cluster_type):
+        if VirtualMachineType.objects.filter(
+            virtual_machine_type_name =virtual_machine_type_name,
+            assigned_object_type=cluster_type_ct,
+            assigned_object_id=cluster_type.id
+        ).exclude(pk=self.instance.pk).exists():
             raise ValidationError(
-                {"__all__": "A Virtual Machine Type needs to be associated with the Cluster Type"},
+                {"__all__":"A Type with this name and cluster type already exists"}
             )
 
+
     def save(self, *args, **kwargs):
         # Set assigned object
-        self.instance.assigned_object = (
-            self.cleaned_data.get("cluster_type")
-        )
+        self.instance.assigned_object = self.cleaned_data.get("cluster_type")
         return super().save(*args, **kwargs)
 
 class VmTypeFilterForm(NetBoxModelFilterSetForm):
@@ -199,37 +179,24 @@ class VmMaitenanceForm(NetBoxModelForm):
         model = VirtualMachineMaintenance
         fields = ( 'virtual_machine','maintenance_window','tags')
 
-    def clean_virtual_machine(self):
-
-        virtual_machine = self.cleaned_data.get("virtual_machine")
-
-        virtual_machine_ct = ContentType.objects.get_for_model(VirtualMachine)
-
-        if VirtualMachineMaintenance.objects.filter(
-            assigned_object_type=virtual_machine_ct,
-            assigned_object_id=virtual_machine.id
-        ).exclude(pk=self.instance.pk).exists():
-            raise ValidationError()
-        return virtual_machine
-    
-
 
     def clean(self):
         """
         Validates form inputs before submitting:
         """
         super().clean()
+        
+        
+        vm = self.cleaned_data.get("virtual_machine")
 
-        if self.errors.get("maintenance_window"):
-            return
-
-        virtual_machine = self.cleaned_data.get("virtual_machine")
-
-        #Check if VM is assigned corretly
-        if (not virtual_machine):
+        #Check if Virtual Machine is assigned corretly
+        if (not vm):
             raise ValidationError(
-                {"__all__": "A Virtual Machine needs to be associated with the maintenance window"},
+                {"__all__": "Can't assign more than one Maintenance Window to the same Virtual Machine"},
             )
+        
+        if self.errors.get("virtual_machine"):
+            return
 
 
 
diff --git a/netbox_sys_plugin/forms/provider.py b/netbox_sys_plugin/forms/provider.py
index 1909d38..ffd747c 100644
--- a/netbox_sys_plugin/forms/provider.py
+++ b/netbox_sys_plugin/forms/provider.py
@@ -55,18 +55,6 @@ class ProviderCredentialsForm(NetBoxModelForm):
         model = ProviderCredentials
         fields = ('cluster','provider_url', 'provider_username', 'provider_password_vault_url','tags')
 
-    def clean_cluster(self):
-
-        cluster = self.cleaned_data.get("cluster")
-
-        cluster_ct = ContentType.objects.get_for_model(Cluster)
-
-        if ProviderCredentials.objects.filter(
-            assigned_object_type=cluster_ct,
-            assigned_object_id=cluster.id
-        ).exclude(pk=self.instance.pk).exists():
-            raise ValidationError()
-        return cluster
 
     def clean(self):
         """
@@ -74,16 +62,18 @@ class ProviderCredentialsForm(NetBoxModelForm):
         """
         super().clean()
 
-        if self.errors.get(f"provider_url"):
-            return
+
         
         cluster = self.cleaned_data.get("cluster")
 
         #Check if cluster is assigned corretly
         if (not cluster):
             raise ValidationError(
-                {"__all__": "A Virtual Machine needs to be associated with the maintenance window"},
+                {"__all__": "Can't assign more than one Credential to the same Cluster"},
             )
+        
+        if self.errors.get(f"provider_url"):
+            return
 
     def save(self, *args, **kwargs):
         # Set assigned object
diff --git a/netbox_sys_plugin/migrations/0009_alter_virtualmachinetype_virtual_machine_type_name_and_more.py b/netbox_sys_plugin/migrations/0009_alter_virtualmachinetype_virtual_machine_type_name_and_more.py
new file mode 100644
index 0000000..c70b7a7
--- /dev/null
+++ b/netbox_sys_plugin/migrations/0009_alter_virtualmachinetype_virtual_machine_type_name_and_more.py
@@ -0,0 +1,25 @@
+# Generated by Django 4.2.16 on 2024-11-21 12:24
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('netbox_sys_plugin', '0008_rename_virtual_machine_type_desc_vmassignedvirtualmachinetype_virtual_machine_type_assignment_desc_a'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='virtualmachinetype',
+            name='virtual_machine_type_name',
+            field=models.CharField(default=None, max_length=50),
+        ),
+        migrations.AlterField(
+            model_name='vmassignedvirtualmachinetype',
+            name='virtual_machine_type_name',
+            field=models.ForeignKey(blank=True, default=1, on_delete=django.db.models.deletion.CASCADE, related_name='assigned_vm_type', to='netbox_sys_plugin.virtualmachinetype'),
+            preserve_default=False,
+        ),
+    ]
diff --git a/netbox_sys_plugin/migrations/0010_alter_vmassignedvirtualmachinetype_virtual_machine_type_name.py b/netbox_sys_plugin/migrations/0010_alter_vmassignedvirtualmachinetype_virtual_machine_type_name.py
new file mode 100644
index 0000000..6dbc855
--- /dev/null
+++ b/netbox_sys_plugin/migrations/0010_alter_vmassignedvirtualmachinetype_virtual_machine_type_name.py
@@ -0,0 +1,19 @@
+# Generated by Django 4.2.16 on 2024-11-21 12:38
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('netbox_sys_plugin', '0009_alter_virtualmachinetype_virtual_machine_type_name_and_more'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='vmassignedvirtualmachinetype',
+            name='virtual_machine_type_name',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='assigned_vm_type', to='netbox_sys_plugin.virtualmachinetype'),
+        ),
+    ]
diff --git a/netbox_sys_plugin/models/machine.py b/netbox_sys_plugin/models/machine.py
index 66aef0c..fb137ac 100644
--- a/netbox_sys_plugin/models/machine.py
+++ b/netbox_sys_plugin/models/machine.py
@@ -21,7 +21,7 @@ class VirtualMachineType(NetBoxModel):
     """Cluster ProviderInfo definition class"""
 
     virtual_machine_type_name = models.CharField(
-        default=None, blank=True, null=True,
+        default=None, blank=False, null=False,
         max_length=50,
         )
     
@@ -46,12 +46,12 @@ class VirtualMachineType(NetBoxModel):
         """Meta class"""
         unique_together = ["assigned_object_id","virtual_machine_type_name"]
         ordering = ["assigned_object_id"]
-        verbose_name = "Machine Type"
-        verbose_name_plural = "Machine Types"
+        verbose_name = "Virtual Machine Type"
+        verbose_name_plural = "Virtual Machine Types"
 
 
     def __str__(self):
-        return self.virtual_machine_type_name or f"for Cluster Type ID {self.assigned_object_id}"
+        return self.virtual_machine_type_name
 
     def get_absolute_url(self):
         """override"""
@@ -70,9 +70,9 @@ class VmAssignedVirtualMachineType(NetBoxModel):
   
     virtual_machine_type_name = models.ForeignKey(
         to="VirtualMachineType",
-        on_delete=models.SET_NULL,
-        null=True,
-        blank=True,
+        on_delete=models.PROTECT,
+        null=False,
+        blank=False,
         related_name="assigned_vm_type"
     )
     
@@ -102,7 +102,7 @@ class VmAssignedVirtualMachineType(NetBoxModel):
 
 
     def __str__(self):
-        return f"For Virtual machine ID {self.assigned_object_id}"
+        return f"Virtual machine ID {self.assigned_object_id}"
 
     def get_absolute_url(self):
         """override"""
-- 
GitLab


From 92b3502da18ffcae2cfdb4ab9fc859dc2d0bb3bc Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Thu, 21 Nov 2024 16:10:52 +0000
Subject: [PATCH 09/11] =?UTF-8?q?=F0=9F=92=84=20Add=20filters=20to=20VM=20?=
 =?UTF-8?q?Types?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/filtersets.py | 29 ++++++++++++++++++++++++++---
 netbox_sys_plugin/views.py      |  4 ++--
 2 files changed, 28 insertions(+), 5 deletions(-)

diff --git a/netbox_sys_plugin/filtersets.py b/netbox_sys_plugin/filtersets.py
index 6574a70..07edfc5 100644
--- a/netbox_sys_plugin/filtersets.py
+++ b/netbox_sys_plugin/filtersets.py
@@ -2,9 +2,9 @@
 
 import django_filters
 from netbox.filtersets import NetBoxModelFilterSet
-from virtualization.models import VirtualMachine, Cluster
+from virtualization.models import VirtualMachine, Cluster, ClusterType
 from django.db.models import Q
-from .models import VirtualMachineMaintenance, ProviderCredentials, VmAssignedVirtualMachineType
+from .models import VirtualMachineMaintenance, ProviderCredentials, VmAssignedVirtualMachineType, VirtualMachineType
 
 
 class VmMaintenanceFilterSet(NetBoxModelFilterSet):
@@ -30,7 +30,7 @@ class VmMaintenanceFilterSet(NetBoxModelFilterSet):
         return queryset.filter(Q(maintenance_window__icontains=value))
     
 class VmAssignedVmTypeFilterSet(NetBoxModelFilterSet):
-    """VirtualMachineType filterset definition class"""
+    """VirtualMachineAssignType filterset definition class"""
 
     virtualmachine = django_filters.ModelMultipleChoiceFilter(
         field_name="VirtualMachine__name",
@@ -51,6 +51,29 @@ class VmAssignedVmTypeFilterSet(NetBoxModelFilterSet):
             return queryset
         return queryset.filter(Q(virtual_machine_type_name__icontains=value)|Q(virtual_machine_type_desc__icontains=value))
 
+class VmTypeFilterSet(NetBoxModelFilterSet):
+    """VirtualMachineType filterset definition class"""
+
+    cluster_type = django_filters.ModelMultipleChoiceFilter(
+        field_name="ClusterType__name",
+        queryset=ClusterType.objects.all(),
+        to_field_name="name",
+        label="Cluster Type (name)",
+    )
+
+    class Meta:
+        """Meta class"""     
+        model = VirtualMachineType
+        fields = ('virtual_machine_type_name','virtual_machine_type_desc', 'cluster_type')
+
+    # pylint: disable=W0613
+    def search(self, queryset, name, value):
+        """override"""
+        if not value.strip():
+            return queryset
+        return queryset.filter(Q(virtual_machine_type_name__icontains=value)|Q(virtual_machine_type_desc__icontains=value))
+
+
 class ProviderCredentialsFilterSet(NetBoxModelFilterSet):
     """ProviderCredentials filterset definition class"""
 
diff --git a/netbox_sys_plugin/views.py b/netbox_sys_plugin/views.py
index 123e31e..f93e69d 100644
--- a/netbox_sys_plugin/views.py
+++ b/netbox_sys_plugin/views.py
@@ -101,8 +101,8 @@ class VmTypeListView(generic.ObjectListView):
 
     queryset = models.VirtualMachineType.objects.all()
     table = tables.VmTypeTable
-    #filterset = filtersets.VmTypeFilterSet
-    #filterset_form = forms.VmTypeFilterForm
+    filterset = filtersets.VmTypeFilterSet
+    filterset_form = forms.VmTypeFilterForm
 
 class VmTypeDeleteView(generic.ObjectDeleteView):
     """Vm maintenance delete view definition"""
-- 
GitLab


From e2267072c56023cd14f93ba7ca62313acdc00d26 Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Thu, 21 Nov 2024 17:04:13 +0000
Subject: [PATCH 10/11] =?UTF-8?q?=F0=9F=92=84=20Add=20filtersets=20and=20U?=
 =?UTF-8?q?I=20objects?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/filtersets.py               |  4 +-
 netbox_sys_plugin/forms/machine.py            |  2 +-
 ...virtualmachinetype_virtual_machine_type.py | 18 +++++++++
 netbox_sys_plugin/models/machine.py           |  2 +-
 netbox_sys_plugin/tables.py                   |  6 +--
 netbox_sys_plugin/template_content.py         | 17 ++++++--
 .../clustertype_virtualmachinetype.html       | 39 +++++++++++++++++++
 .../vm_vmassignedvirtualmachinetype.html      | 12 +++---
 .../vmassignedvirtualmachinetype.html         |  6 +--
 9 files changed, 86 insertions(+), 20 deletions(-)
 create mode 100644 netbox_sys_plugin/migrations/0011_rename_virtual_machine_type_name_vmassignedvirtualmachinetype_virtual_machine_type.py
 create mode 100644 netbox_sys_plugin/templates/netbox_sys_plugin/clustertype_virtualmachinetype.html

diff --git a/netbox_sys_plugin/filtersets.py b/netbox_sys_plugin/filtersets.py
index 07edfc5..3c031b6 100644
--- a/netbox_sys_plugin/filtersets.py
+++ b/netbox_sys_plugin/filtersets.py
@@ -42,14 +42,14 @@ class VmAssignedVmTypeFilterSet(NetBoxModelFilterSet):
     class Meta:
         """Meta class"""     
         model = VmAssignedVirtualMachineType
-        fields = ('virtual_machine_type_name','virtual_machine_type_assignment_desc', 'virtualmachine')
+        fields = ('virtual_machine_type','virtual_machine_type_assignment_desc', 'virtualmachine')
 
     # pylint: disable=W0613
     def search(self, queryset, name, value):
         """override"""
         if not value.strip():
             return queryset
-        return queryset.filter(Q(virtual_machine_type_name__icontains=value)|Q(virtual_machine_type_desc__icontains=value))
+        return queryset.filter(Q(virtual_machine_type__icontains=value)|Q(virtual_machine_type_desc__icontains=value))
 
 class VmTypeFilterSet(NetBoxModelFilterSet):
     """VirtualMachineType filterset definition class"""
diff --git a/netbox_sys_plugin/forms/machine.py b/netbox_sys_plugin/forms/machine.py
index d90b2f7..7dbb437 100644
--- a/netbox_sys_plugin/forms/machine.py
+++ b/netbox_sys_plugin/forms/machine.py
@@ -46,7 +46,7 @@ class VmAssignedVmTypeForm(NetBoxModelForm):
     class Meta:
         """Meta class"""
         model = VmAssignedVirtualMachineType
-        fields = ('virtual_machine','virtual_machine_type_name', 'virtual_machine_type_assignment_desc','tags')
+        fields = ('virtual_machine','virtual_machine_type', 'virtual_machine_type_assignment_desc','tags')
 
     def clean(self):
         """
diff --git a/netbox_sys_plugin/migrations/0011_rename_virtual_machine_type_name_vmassignedvirtualmachinetype_virtual_machine_type.py b/netbox_sys_plugin/migrations/0011_rename_virtual_machine_type_name_vmassignedvirtualmachinetype_virtual_machine_type.py
new file mode 100644
index 0000000..be74bb5
--- /dev/null
+++ b/netbox_sys_plugin/migrations/0011_rename_virtual_machine_type_name_vmassignedvirtualmachinetype_virtual_machine_type.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.16 on 2024-11-21 16:38
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('netbox_sys_plugin', '0010_alter_vmassignedvirtualmachinetype_virtual_machine_type_name'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='vmassignedvirtualmachinetype',
+            old_name='virtual_machine_type_name',
+            new_name='virtual_machine_type',
+        ),
+    ]
diff --git a/netbox_sys_plugin/models/machine.py b/netbox_sys_plugin/models/machine.py
index fb137ac..690faa7 100644
--- a/netbox_sys_plugin/models/machine.py
+++ b/netbox_sys_plugin/models/machine.py
@@ -68,7 +68,7 @@ GenericRelation(
 class VmAssignedVirtualMachineType(NetBoxModel):
     """Virtual Machine Type Info definition class"""
   
-    virtual_machine_type_name = models.ForeignKey(
+    virtual_machine_type = models.ForeignKey(
         to="VirtualMachineType",
         on_delete=models.PROTECT,
         null=False,
diff --git a/netbox_sys_plugin/tables.py b/netbox_sys_plugin/tables.py
index bb9049b..001f223 100644
--- a/netbox_sys_plugin/tables.py
+++ b/netbox_sys_plugin/tables.py
@@ -94,7 +94,7 @@ class VmAssignedVmTypeTable(NetBoxTable):
         orderable=False,
         verbose_name="Virtual Machine",
     )
-    virtual_machine_type_name = tables.Column(
+    virtual_machine_type = tables.Column(
         linkify=True,
         verbose_name="Virtual Machine Type",
     )
@@ -109,11 +109,11 @@ class VmAssignedVmTypeTable(NetBoxTable):
         fields = (
             "pk",
             "id",
-            "virtual_machine_type_name",
+            "virtual_machine_type",
             "virtual_machine_type_assignment_desc", 
         )
 
-        default_columns = ('id','assigned_object','virtual_machine_type_name','virtual_machine_type_assignment_desc')
+        default_columns = ('id','assigned_object','virtual_machine_type','virtual_machine_type_assignment_desc')
 
 #VmType
 class VmTypeTable(NetBoxTable):
diff --git a/netbox_sys_plugin/template_content.py b/netbox_sys_plugin/template_content.py
index f390d53..4400d88 100644
--- a/netbox_sys_plugin/template_content.py
+++ b/netbox_sys_plugin/template_content.py
@@ -5,7 +5,7 @@ from extras.plugins import PluginTemplateExtension
 from netbox_sys_plugin import models
 
 class VmTable(PluginTemplateExtension):
-    """VM Maintenance object template"""
+    """VM object template"""
 
     model = 'virtualization.virtualmachine'
 
@@ -13,8 +13,17 @@ class VmTable(PluginTemplateExtension):
         return self.render('netbox_sys_plugin/vm_vmmaintenance.html', extra_context={'vmmaintenanceinfo': models.VirtualMachineMaintenance.objects.filter(VirtualMachine=self.context['object'])})
     
     def right_page(self):
-        return self.render('netbox_sys_plugin/vm_vmassignedvirtualmachinetype.html', extra_context={'vmtypeinfo': models.VmAssignedVirtualMachineType.objects.filter(VirtualMachine=self.context['object'])})
-    
+        return self.render('netbox_sys_plugin/vm_vmassignedvirtualmachinetype.html', extra_context={'vmassigntypeinfo': models.VmAssignedVirtualMachineType.objects.filter(VirtualMachine=self.context['object'])})
+
+class VmTypeTable(PluginTemplateExtension):
+    """Cluster object template"""
+
+    model = 'virtualization.clustertype'
+
+    def right_page(self):
+        return self.render('netbox_sys_plugin/clustertype_virtualmachinetype.html', extra_context={'vmtypesinfo': models.VirtualMachineType.objects.filter(ClusterType=self.context['object'])})
+
+
 class ProviderCredentialsTable(PluginTemplateExtension):
     """Provider Credentials object template"""
 
@@ -24,4 +33,4 @@ class ProviderCredentialsTable(PluginTemplateExtension):
         return self.render('netbox_sys_plugin/cluster_providercredentials.html', extra_context={'providercredentialsinfo': models.ProviderCredentials.objects.filter(Cluster=self.context['object'])})
 
 
-template_extensions = [VmTable,ProviderCredentialsTable]
+template_extensions = [VmTable,ProviderCredentialsTable,VmTypeTable]
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/clustertype_virtualmachinetype.html b/netbox_sys_plugin/templates/netbox_sys_plugin/clustertype_virtualmachinetype.html
new file mode 100644
index 0000000..cd3a25c
--- /dev/null
+++ b/netbox_sys_plugin/templates/netbox_sys_plugin/clustertype_virtualmachinetype.html
@@ -0,0 +1,39 @@
+{% block content %}
+  <div class="row mb-3">
+    <div class="col col-md-12">
+      <div class="card">
+        <h5 class="card-header">Provider Credentials</h5>
+        <div class="card-body">
+          <table class="table table-responsive">
+              <th scope="row">Virtual Machine Type</th>
+              <th scope="row">Description</th>
+              {% for vmtypes in vmtypesinfo %}
+        <tr>
+            <td>
+              {% if vmtypes.virtual_machine_type_name %}
+                <a href="{{ vmtypes.get_absolute_url }}">{{ vmtypes.virtual_machine_type_name }}</a>
+              {% else %}
+                {{ ''|placeholder }}
+              {% endif %}
+            </td>
+            <td>
+              {% if vmtypes.virtual_machine_type_desc %}
+                {{ vmtypes.virtual_machine_type_desc }}
+              {% else %}
+                {{ ''|placeholder }}
+              {% endif %}
+            </td>
+        </tr>
+        {% empty %}
+        <tr>
+            <td colspan="3">No Virtual Machine Types found.</td>
+        </tr>
+        {% endfor %}
+            </tr>
+          </table>
+        </div>
+      </div>
+      {% include 'inc/panels/custom_fields.html' %}
+    </div>
+  </div>
+{% endblock content %}
\ No newline at end of file
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/vm_vmassignedvirtualmachinetype.html b/netbox_sys_plugin/templates/netbox_sys_plugin/vm_vmassignedvirtualmachinetype.html
index 4154da8..bdebafb 100644
--- a/netbox_sys_plugin/templates/netbox_sys_plugin/vm_vmassignedvirtualmachinetype.html
+++ b/netbox_sys_plugin/templates/netbox_sys_plugin/vm_vmassignedvirtualmachinetype.html
@@ -8,19 +8,19 @@
               <th scope="row">ID</th>
               <th scope="row">Type</th>
               <th scope="row">Description</th>
-              {% for vmtype in vmtypeinfo %}
+              {% for vmassigntype in vmassigntypeinfo %}
         <tr>
-            <td>{{ vmtype.id }}</td>
+            <td>{{ vmassigntype.id }}</td>
             <td>
-              {% if vmtype.virtual_machine_type_name %}
-                <a href="{{ vmtype.get_absolute_url }}">{{ vmtype.virtual_machine_type_name }}</a>
+              {% if vmassigntype.virtual_machine_type %}
+                <a href="{{ vmassigntype.get_absolute_url }}">{{ vmassigntype.virtual_machine_type }}</a>
               {% else %}
                 {{ ''|placeholder }}
               {% endif %}
             </td>
             <td>
-              {% if vmtype.virtual_machine_type_assignment_desc %}
-                {{ vmtype.virtual_machine_type_assignment_desc }}
+              {% if vmassigntype.virtual_machine_type_assignment_desc %}
+                {{ vmassigntype.virtual_machine_type_assignment_desc }}
               {% else %}
                 {{ ''|placeholder }}
               {% endif %}
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/vmassignedvirtualmachinetype.html b/netbox_sys_plugin/templates/netbox_sys_plugin/vmassignedvirtualmachinetype.html
index aadce1c..5fa41ae 100644
--- a/netbox_sys_plugin/templates/netbox_sys_plugin/vmassignedvirtualmachinetype.html
+++ b/netbox_sys_plugin/templates/netbox_sys_plugin/vmassignedvirtualmachinetype.html
@@ -8,14 +8,14 @@ Virtual Machine Type for {{ object.assigned_object }}
 <div class="row mb-3">
   <div class="col col-md-6">
     <div class="card">
-      <h5 class="card-header">Virtual Machine Type Assignedment </h5>
+      <h5 class="card-header">Virtual Machine Type Assignement </h5>
       <div class="card-body">
         <table class="table table-hover attr-table">
           <tr>
             <th scope="row">Name</th>
             <td>
-              {% if object.virtual_machine_type_name %}
-                <a href="{{ object.virtual_machine_type_name.get_absolute_url }}">{{ object.virtual_machine_type_name }}</a>
+              {% if object.virtual_machine_type %}
+                <a href="{{ object.virtual_machine_type.get_absolute_url }}">{{ object.virtual_machine_type }}</a>
               {% else %}
                 {{ ''|placeholder }}
               {% endif %}
-- 
GitLab


From ad5d87f508a67586d73c7d34d11512c05430d4ac Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Thu, 21 Nov 2024 17:11:29 +0000
Subject: [PATCH 11/11] =?UTF-8?q?=F0=9F=94=A5=20squash=20migrations?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 ...chinetype_vmassignedvirtualmachinetype.py} | 28 +++++++++----------
 ...lter_virtualmachinetype_unique_together.py | 17 -----------
 ...005_vmassignedvirtualmachinetype_vmtype.py | 20 -------------
 ...ignedvirtualmachinetype_vmtype_and_more.py | 24 ----------------
 ...machinetype_virtualmachinetype_and_more.py | 23 ---------------
 ..._virtual_machine_type_assignment_desc_a.py | 28 -------------------
 ...type_virtual_machine_type_name_and_more.py | 25 -----------------
 ...almachinetype_virtual_machine_type_name.py | 19 -------------
 ...virtualmachinetype_virtual_machine_type.py | 18 ------------
 9 files changed, 14 insertions(+), 188 deletions(-)
 rename netbox_sys_plugin/migrations/{0003_vmassignedvirtualmachinetype_virtualmachinetype.py => 0003_virtualmachinetype_vmassignedvirtualmachinetype.py} (81%)
 delete mode 100644 netbox_sys_plugin/migrations/0004_alter_virtualmachinetype_unique_together.py
 delete mode 100644 netbox_sys_plugin/migrations/0005_vmassignedvirtualmachinetype_vmtype.py
 delete mode 100644 netbox_sys_plugin/migrations/0006_remove_vmassignedvirtualmachinetype_vmtype_and_more.py
 delete mode 100644 netbox_sys_plugin/migrations/0007_remove_vmassignedvirtualmachinetype_virtualmachinetype_and_more.py
 delete mode 100644 netbox_sys_plugin/migrations/0008_rename_virtual_machine_type_desc_vmassignedvirtualmachinetype_virtual_machine_type_assignment_desc_a.py
 delete mode 100644 netbox_sys_plugin/migrations/0009_alter_virtualmachinetype_virtual_machine_type_name_and_more.py
 delete mode 100644 netbox_sys_plugin/migrations/0010_alter_vmassignedvirtualmachinetype_virtual_machine_type_name.py
 delete mode 100644 netbox_sys_plugin/migrations/0011_rename_virtual_machine_type_name_vmassignedvirtualmachinetype_virtual_machine_type.py

diff --git a/netbox_sys_plugin/migrations/0003_vmassignedvirtualmachinetype_virtualmachinetype.py b/netbox_sys_plugin/migrations/0003_virtualmachinetype_vmassignedvirtualmachinetype.py
similarity index 81%
rename from netbox_sys_plugin/migrations/0003_vmassignedvirtualmachinetype_virtualmachinetype.py
rename to netbox_sys_plugin/migrations/0003_virtualmachinetype_vmassignedvirtualmachinetype.py
index c35fd43..cad573f 100644
--- a/netbox_sys_plugin/migrations/0003_vmassignedvirtualmachinetype_virtualmachinetype.py
+++ b/netbox_sys_plugin/migrations/0003_virtualmachinetype_vmassignedvirtualmachinetype.py
@@ -1,4 +1,4 @@
-# Generated by Django 4.2.16 on 2024-11-20 14:43
+# Generated by Django 4.2.16 on 2024-11-21 17:06
 
 from django.db import migrations, models
 import django.db.models.deletion
@@ -9,48 +9,48 @@ import utilities.json
 class Migration(migrations.Migration):
 
     dependencies = [
-        ('extras', '0098_webhook_custom_field_data_webhook_tags'),
         ('contenttypes', '0002_remove_content_type_name'),
+        ('extras', '0098_webhook_custom_field_data_webhook_tags'),
         ('netbox_sys_plugin', '0002_alter_virtualmachinemaintenance_maintenance_window_and_more'),
     ]
 
     operations = [
         migrations.CreateModel(
-            name='VmAssignedVirtualMachineType',
+            name='VirtualMachineType',
             fields=[
                 ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
                 ('created', models.DateTimeField(auto_now_add=True, null=True)),
                 ('last_updated', models.DateTimeField(auto_now=True, null=True)),
                 ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('virtual_machine_type_name', models.CharField(blank=True, default=None, max_length=50, null=True)),
+                ('virtual_machine_type_name', models.CharField(default=None, max_length=50)),
                 ('virtual_machine_type_desc', models.CharField(blank=True, default=None, max_length=100, null=True)),
                 ('assigned_object_id', models.PositiveBigIntegerField(blank=True, null=True)),
-                ('assigned_object_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(('app_label', 'virtualization'), ('model', 'VirtualMachine'))), null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')),
+                ('assigned_object_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(('app_label', 'virtualization'), ('model', 'ClusterType'))), null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')),
                 ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
             ],
             options={
-                'verbose_name': 'Assigned Machine Type',
-                'verbose_name_plural': 'Assigned Machine Types',
+                'verbose_name': 'Virtual Machine Type',
+                'verbose_name_plural': 'Virtual Machine Types',
                 'ordering': ['assigned_object_id'],
-                'unique_together': {('assigned_object_id',)},
+                'unique_together': {('assigned_object_id', 'virtual_machine_type_name')},
             },
         ),
         migrations.CreateModel(
-            name='VirtualMachineType',
+            name='VmAssignedVirtualMachineType',
             fields=[
                 ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
                 ('created', models.DateTimeField(auto_now_add=True, null=True)),
                 ('last_updated', models.DateTimeField(auto_now=True, null=True)),
                 ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)),
-                ('virtual_machine_type_name', models.CharField(blank=True, default=None, max_length=50, null=True)),
-                ('virtual_machine_type_desc', models.CharField(blank=True, default=None, max_length=100, null=True)),
+                ('virtual_machine_type_assignment_desc', models.CharField(blank=True, default=None, max_length=100, null=True)),
                 ('assigned_object_id', models.PositiveBigIntegerField(blank=True, null=True)),
-                ('assigned_object_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(('app_label', 'virtualization'), ('model', 'ClusterType'))), null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')),
+                ('assigned_object_type', models.ForeignKey(blank=True, limit_choices_to=models.Q(models.Q(('app_label', 'virtualization'), ('model', 'VirtualMachine'))), null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')),
                 ('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
+                ('virtual_machine_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='assigned_vm_type', to='netbox_sys_plugin.virtualmachinetype')),
             ],
             options={
-                'verbose_name': 'Machine Type',
-                'verbose_name_plural': 'Machine Types',
+                'verbose_name': 'Assigned Machine Type',
+                'verbose_name_plural': 'Assigned Machine Types',
                 'ordering': ['assigned_object_id'],
                 'unique_together': {('assigned_object_id',)},
             },
diff --git a/netbox_sys_plugin/migrations/0004_alter_virtualmachinetype_unique_together.py b/netbox_sys_plugin/migrations/0004_alter_virtualmachinetype_unique_together.py
deleted file mode 100644
index 1d662ad..0000000
--- a/netbox_sys_plugin/migrations/0004_alter_virtualmachinetype_unique_together.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 4.2.16 on 2024-11-20 14:54
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('netbox_sys_plugin', '0003_vmassignedvirtualmachinetype_virtualmachinetype'),
-    ]
-
-    operations = [
-        migrations.AlterUniqueTogether(
-            name='virtualmachinetype',
-            unique_together={('assigned_object_id', 'virtual_machine_type_name')},
-        ),
-    ]
diff --git a/netbox_sys_plugin/migrations/0005_vmassignedvirtualmachinetype_vmtype.py b/netbox_sys_plugin/migrations/0005_vmassignedvirtualmachinetype_vmtype.py
deleted file mode 100644
index de15b2e..0000000
--- a/netbox_sys_plugin/migrations/0005_vmassignedvirtualmachinetype_vmtype.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated by Django 4.2.16 on 2024-11-20 15:07
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('netbox_sys_plugin', '0004_alter_virtualmachinetype_unique_together'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='vmassignedvirtualmachinetype',
-            name='vmtype',
-            field=models.OneToOneField(db_column='vm_type_id', default=0, on_delete=django.db.models.deletion.CASCADE, related_name='vmtype', to='netbox_sys_plugin.virtualmachinetype'),
-            preserve_default=False,
-        ),
-    ]
diff --git a/netbox_sys_plugin/migrations/0006_remove_vmassignedvirtualmachinetype_vmtype_and_more.py b/netbox_sys_plugin/migrations/0006_remove_vmassignedvirtualmachinetype_vmtype_and_more.py
deleted file mode 100644
index c038afe..0000000
--- a/netbox_sys_plugin/migrations/0006_remove_vmassignedvirtualmachinetype_vmtype_and_more.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 4.2.16 on 2024-11-20 15:19
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('netbox_sys_plugin', '0005_vmassignedvirtualmachinetype_vmtype'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='vmassignedvirtualmachinetype',
-            name='vmtype',
-        ),
-        migrations.AddField(
-            model_name='vmassignedvirtualmachinetype',
-            name='VirtualMachineType',
-            field=models.OneToOneField(db_column='virtual_machine_type_id', default=0, on_delete=django.db.models.deletion.CASCADE, related_name='VirtualMachineType', to='netbox_sys_plugin.virtualmachinetype'),
-            preserve_default=False,
-        ),
-    ]
diff --git a/netbox_sys_plugin/migrations/0007_remove_vmassignedvirtualmachinetype_virtualmachinetype_and_more.py b/netbox_sys_plugin/migrations/0007_remove_vmassignedvirtualmachinetype_virtualmachinetype_and_more.py
deleted file mode 100644
index cc3e70f..0000000
--- a/netbox_sys_plugin/migrations/0007_remove_vmassignedvirtualmachinetype_virtualmachinetype_and_more.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 4.2.16 on 2024-11-20 15:30
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('netbox_sys_plugin', '0006_remove_vmassignedvirtualmachinetype_vmtype_and_more'),
-    ]
-
-    operations = [
-        migrations.RemoveField(
-            model_name='vmassignedvirtualmachinetype',
-            name='VirtualMachineType',
-        ),
-        migrations.AddField(
-            model_name='vmassignedvirtualmachinetype',
-            name='virtual_machine_type_id',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_vm_types', to='netbox_sys_plugin.virtualmachinetype'),
-        ),
-    ]
diff --git a/netbox_sys_plugin/migrations/0008_rename_virtual_machine_type_desc_vmassignedvirtualmachinetype_virtual_machine_type_assignment_desc_a.py b/netbox_sys_plugin/migrations/0008_rename_virtual_machine_type_desc_vmassignedvirtualmachinetype_virtual_machine_type_assignment_desc_a.py
deleted file mode 100644
index bd812dd..0000000
--- a/netbox_sys_plugin/migrations/0008_rename_virtual_machine_type_desc_vmassignedvirtualmachinetype_virtual_machine_type_assignment_desc_a.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Generated by Django 4.2.16 on 2024-11-20 16:16
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('netbox_sys_plugin', '0007_remove_vmassignedvirtualmachinetype_virtualmachinetype_and_more'),
-    ]
-
-    operations = [
-        migrations.RenameField(
-            model_name='vmassignedvirtualmachinetype',
-            old_name='virtual_machine_type_desc',
-            new_name='virtual_machine_type_assignment_desc',
-        ),
-        migrations.RemoveField(
-            model_name='vmassignedvirtualmachinetype',
-            name='virtual_machine_type_id',
-        ),
-        migrations.AlterField(
-            model_name='vmassignedvirtualmachinetype',
-            name='virtual_machine_type_name',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_vm_type', to='netbox_sys_plugin.virtualmachinetype'),
-        ),
-    ]
diff --git a/netbox_sys_plugin/migrations/0009_alter_virtualmachinetype_virtual_machine_type_name_and_more.py b/netbox_sys_plugin/migrations/0009_alter_virtualmachinetype_virtual_machine_type_name_and_more.py
deleted file mode 100644
index c70b7a7..0000000
--- a/netbox_sys_plugin/migrations/0009_alter_virtualmachinetype_virtual_machine_type_name_and_more.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 4.2.16 on 2024-11-21 12:24
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('netbox_sys_plugin', '0008_rename_virtual_machine_type_desc_vmassignedvirtualmachinetype_virtual_machine_type_assignment_desc_a'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='virtualmachinetype',
-            name='virtual_machine_type_name',
-            field=models.CharField(default=None, max_length=50),
-        ),
-        migrations.AlterField(
-            model_name='vmassignedvirtualmachinetype',
-            name='virtual_machine_type_name',
-            field=models.ForeignKey(blank=True, default=1, on_delete=django.db.models.deletion.CASCADE, related_name='assigned_vm_type', to='netbox_sys_plugin.virtualmachinetype'),
-            preserve_default=False,
-        ),
-    ]
diff --git a/netbox_sys_plugin/migrations/0010_alter_vmassignedvirtualmachinetype_virtual_machine_type_name.py b/netbox_sys_plugin/migrations/0010_alter_vmassignedvirtualmachinetype_virtual_machine_type_name.py
deleted file mode 100644
index 6dbc855..0000000
--- a/netbox_sys_plugin/migrations/0010_alter_vmassignedvirtualmachinetype_virtual_machine_type_name.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 4.2.16 on 2024-11-21 12:38
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('netbox_sys_plugin', '0009_alter_virtualmachinetype_virtual_machine_type_name_and_more'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='vmassignedvirtualmachinetype',
-            name='virtual_machine_type_name',
-            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='assigned_vm_type', to='netbox_sys_plugin.virtualmachinetype'),
-        ),
-    ]
diff --git a/netbox_sys_plugin/migrations/0011_rename_virtual_machine_type_name_vmassignedvirtualmachinetype_virtual_machine_type.py b/netbox_sys_plugin/migrations/0011_rename_virtual_machine_type_name_vmassignedvirtualmachinetype_virtual_machine_type.py
deleted file mode 100644
index be74bb5..0000000
--- a/netbox_sys_plugin/migrations/0011_rename_virtual_machine_type_name_vmassignedvirtualmachinetype_virtual_machine_type.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 4.2.16 on 2024-11-21 16:38
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('netbox_sys_plugin', '0010_alter_vmassignedvirtualmachinetype_virtual_machine_type_name'),
-    ]
-
-    operations = [
-        migrations.RenameField(
-            model_name='vmassignedvirtualmachinetype',
-            old_name='virtual_machine_type_name',
-            new_name='virtual_machine_type',
-        ),
-    ]
-- 
GitLab