From 9dbce602be17e5884f4b04d5695a14fbce1ac066 Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Mon, 2 Dec 2024 15:09:33 +0000
Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20Add=20tags=20to=20form?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/forms/createvm.py           | 57 +++++++++++++++++--
 .../netbox_sys_plugin/create_vm.html          | 10 +++-
 2 files changed, 60 insertions(+), 7 deletions(-)

diff --git a/netbox_sys_plugin/forms/createvm.py b/netbox_sys_plugin/forms/createvm.py
index e8c94bf..740f5ce 100644
--- a/netbox_sys_plugin/forms/createvm.py
+++ b/netbox_sys_plugin/forms/createvm.py
@@ -5,6 +5,7 @@ from netbox.forms import NetBoxModelForm
 from virtualization.models import ClusterType, Cluster, VirtualMachine, VMInterface
 from ipam.models import IPAddress, Service
 from dcim.models import DeviceRole,Site, Platform
+from extras.models import Tag
 from utilities.forms.fields import DynamicModelChoiceField
 from django.db import transaction
 
@@ -38,7 +39,7 @@ class ClusterFormList(NetBoxModelForm):
     
     class Meta:
         model = Cluster
-        fields = ('cluster_type','cluster')
+        fields = ('cluster_type','cluster','tags')
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
@@ -90,6 +91,21 @@ class IPAddressForm(NetBoxModelForm):
         super().__init__(*args, **kwargs)
         self.fields.pop('tags',None)
 
+class TagsForm(NetBoxModelForm):
+    """Form for IP Addresses."""
+    tag = DynamicModelChoiceField(
+        queryset=Tag.objects.all(), required=False, label="Tag"
+    )
+    class Meta:
+        model = Tag
+        fields = ('tag',)
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.fields.pop('tags',None)
+    
+
+
 
 # Inline formsets for managing relationships
 ClusterFormSet = inlineformset_factory(
@@ -122,6 +138,10 @@ ClusterListFormSet = forms.modelformset_factory(
     Cluster, form=ClusterFormList, formset=ClusterFormList, extra=1, can_delete=False
 )
 
+TagFormSet = forms.modelformset_factory(
+    Tag, form=TagsForm, formset=TagsForm, extra=1, can_delete=False
+)
+
 
 # Combined form
 class CreateVmForm(NetBoxModelForm):
@@ -172,6 +192,10 @@ class CreateVmForm(NetBoxModelForm):
         self.cl_list_formsets = []
         empty_cl_list_formset = ClusterListFormSet(data=data, prefix='cl_list_new')
         self.cl_list_formsets.append(('new', empty_cl_list_formset))
+        # Tags
+        self.tagformsets = []
+        empty_tagformset = TagFormSet(data=data, prefix='tag_new')
+        self.tagformsets.append(('new', empty_tagformset))
 
     @staticmethod
     def check_cluster_exist(data):
@@ -194,14 +218,28 @@ class CreateVmForm(NetBoxModelForm):
             return ports
         except ValueError:
             raise ValueError(f"Invalid ports value: {ports_field}")
+   
+    @staticmethod
+    def get_parse_tags(data):
+        tag_id = data.get('tag_new-tag', '')
+        tags_field = Tag.objects.get(pk=tag_id)
+        #try:
+        #    tags = [int(tag.strip()) for tag in tags_field.split(',') if tag.strip().isdigit()]
+        #    return tags 
+        #except ValueError:
+        #    raise ValueError(f"Invalid ports value: {tags_field}")
+        
+        return tags_field 
 
     @staticmethod
     def create_cluster_type(data):
         """Create and save a ClusterType object."""
+        tags = CreateVmForm.get_parse_tags(data)
         cluster_type = ClusterType(
             name=data.get('name', ''),
             slug=data.get('slug', ''),
-            description=data.get('description', '')
+            description=data.get('description', ''),
+            tags=tags
         )
         cluster_type.full_clean()
         cluster_type.save()
@@ -215,13 +253,15 @@ class CreateVmForm(NetBoxModelForm):
             site = Site.objects.get(pk=site_id)
         except Site.DoesNotExist:
             raise ValueError(f"Invalid Site ID: {site_id}")
+        tags = CreateVmForm.get_parse_tags(data)
 
         cluster = Cluster(
             name=data.get('clusters-0-name', ''),
             type=cluster_type,
             status=data.get('clusters-0-status', ''),
             site=site,
-            description=data.get('clusters-0-description', '')
+            description=data.get('clusters-0-description', ''),
+            tags=tags
         )
         cluster.full_clean()
         cluster.save()
@@ -241,6 +281,7 @@ class CreateVmForm(NetBoxModelForm):
             platform = Platform.objects.get(pk=platform_id)
         except Platform.DoesNotExist:
             raise ValueError(f"Invalid Platform ID: {platform_id}")
+        tags = CreateVmForm.get_parse_tags(data)
 
         vm = VirtualMachine(
             name=data.get('vms_new-0-name', ''),
@@ -249,7 +290,8 @@ class CreateVmForm(NetBoxModelForm):
             site=cluster.site,
             platform=platform,
             description=data.get('vms_new-0-description', ''),
-            cluster=cluster
+            cluster=cluster,
+            tags=tags
         )
         vm.full_clean()
         vm.save()
@@ -271,13 +313,15 @@ class CreateVmForm(NetBoxModelForm):
         """Create and save a Service object."""
         ports_field = data.get(f'{prefix}-0-ports', '')
         ports = CreateVmForm.parse_ports(ports_field)
+        tags = CreateVmForm.get_parse_tags(data)
 
         service = Service(
             name=data.get(f'{prefix}-0-name', ''),
             protocol=data.get(f'{prefix}-0-protocol', ''),
             ports=ports,
             description=description,
-            virtual_machine=vm
+            virtual_machine=vm,
+            tags=tags
         )
         service.full_clean()
         service.save()
@@ -296,7 +340,8 @@ class CreateVmForm(NetBoxModelForm):
             address=data.get('ip_new-address', ''),
             status=data.get('ip_new-status', ''),
             role=data.get('ip_new-role', ''),
-            assigned_object=vm_interface
+            assigned_object=vm_interface,
+            tags=data.get('tag_new-tag', '')
         )
         ip_address.full_clean()
         ip_address.save()
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/create_vm.html b/netbox_sys_plugin/templates/netbox_sys_plugin/create_vm.html
index d267c46..ad1def9 100644
--- a/netbox_sys_plugin/templates/netbox_sys_plugin/create_vm.html
+++ b/netbox_sys_plugin/templates/netbox_sys_plugin/create_vm.html
@@ -122,7 +122,15 @@ SYS - Virtual Machine
           {% endfor %}
           {% endfor %}
         </div>
-        
+        <div class="field-group my-5">
+          <h5>Tag</h5>
+          {% for tagformset in form.tagformsets %}
+          {% for tag_form in tagformset %}
+          <div class="form-group" class="field-group mb-5">
+            {{ tag_form.as_p }}
+          </div>
+        {% endfor %}
+        {% endfor %}
         <div class="text-end my-3">
           {% block buttons %}
           <button type="submit" class="btn btn-primary">Save</button>
-- 
GitLab


From 922197b939724ee459ef1c30cdb2c1293f0c132e Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Mon, 2 Dec 2024 15:58:49 +0000
Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=90=9B=20Fix=20not=20assigned=20tags?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/forms/createvm.py | 52 ++++++++++++++++++-----------
 1 file changed, 32 insertions(+), 20 deletions(-)

diff --git a/netbox_sys_plugin/forms/createvm.py b/netbox_sys_plugin/forms/createvm.py
index 740f5ce..f0483a8 100644
--- a/netbox_sys_plugin/forms/createvm.py
+++ b/netbox_sys_plugin/forms/createvm.py
@@ -1,13 +1,15 @@
 from django import forms
 from django.forms import inlineformset_factory
+from django.contrib.contenttypes.models import ContentType
+from django.db import transaction
+from utilities.forms.fields import DynamicModelChoiceField
 from netbox.forms import NetBoxModelForm
-
 from virtualization.models import ClusterType, Cluster, VirtualMachine, VMInterface
 from ipam.models import IPAddress, Service
 from dcim.models import DeviceRole,Site, Platform
-from extras.models import Tag
-from utilities.forms.fields import DynamicModelChoiceField
-from django.db import transaction
+from extras.models import Tag, TaggedItem
+
+
 
 
 
@@ -222,27 +224,39 @@ class CreateVmForm(NetBoxModelForm):
     @staticmethod
     def get_parse_tags(data):
         tag_id = data.get('tag_new-tag', '')
-        tags_field = Tag.objects.get(pk=tag_id)
-        #try:
-        #    tags = [int(tag.strip()) for tag in tags_field.split(',') if tag.strip().isdigit()]
-        #    return tags 
-        #except ValueError:
-        #    raise ValueError(f"Invalid ports value: {tags_field}")
         
-        return tags_field 
+        if not tag_id:
+            return []
+        try:
+            tags_field = Tag.objects.get(pk=tag_id)
+            return [tags_field]
+        except Tag.DoesNotExist:
+            raise ValueError (f"Invalid tag: {tags_field}")
+    
+    @staticmethod
+    def assign_tags(obj,data):
+        """Assign Tags to the created objects"""
+
+        tags = CreateVmForm.get_parse_tags(data)
+        content_type = ContentType.objects.get_for_model(obj)
+        for tag in tags:
+            TaggedItem.objects.create(
+                tag=tag,
+                content_type=content_type,
+                object_id=obj.pk
+            )
 
     @staticmethod
     def create_cluster_type(data):
         """Create and save a ClusterType object."""
-        tags = CreateVmForm.get_parse_tags(data)
         cluster_type = ClusterType(
             name=data.get('name', ''),
             slug=data.get('slug', ''),
             description=data.get('description', ''),
-            tags=tags
         )
         cluster_type.full_clean()
         cluster_type.save()
+        CreateVmForm.assign_tags(cluster_type,data)
         return cluster_type
 
     @staticmethod
@@ -253,7 +267,6 @@ class CreateVmForm(NetBoxModelForm):
             site = Site.objects.get(pk=site_id)
         except Site.DoesNotExist:
             raise ValueError(f"Invalid Site ID: {site_id}")
-        tags = CreateVmForm.get_parse_tags(data)
 
         cluster = Cluster(
             name=data.get('clusters-0-name', ''),
@@ -261,10 +274,10 @@ class CreateVmForm(NetBoxModelForm):
             status=data.get('clusters-0-status', ''),
             site=site,
             description=data.get('clusters-0-description', ''),
-            tags=tags
         )
         cluster.full_clean()
         cluster.save()
+        CreateVmForm.assign_tags(cluster,data)
         return cluster
 
     @staticmethod
@@ -281,7 +294,6 @@ class CreateVmForm(NetBoxModelForm):
             platform = Platform.objects.get(pk=platform_id)
         except Platform.DoesNotExist:
             raise ValueError(f"Invalid Platform ID: {platform_id}")
-        tags = CreateVmForm.get_parse_tags(data)
 
         vm = VirtualMachine(
             name=data.get('vms_new-0-name', ''),
@@ -291,10 +303,10 @@ class CreateVmForm(NetBoxModelForm):
             platform=platform,
             description=data.get('vms_new-0-description', ''),
             cluster=cluster,
-            tags=tags
         )
         vm.full_clean()
         vm.save()
+        CreateVmForm.assign_tags(vm,data)
         return vm
 
     @staticmethod
@@ -306,6 +318,7 @@ class CreateVmForm(NetBoxModelForm):
         )
         vmi.full_clean()
         vmi.save()
+        CreateVmForm.assign_tags(vmi,data)
         return vmi
     
     @staticmethod
@@ -313,7 +326,6 @@ class CreateVmForm(NetBoxModelForm):
         """Create and save a Service object."""
         ports_field = data.get(f'{prefix}-0-ports', '')
         ports = CreateVmForm.parse_ports(ports_field)
-        tags = CreateVmForm.get_parse_tags(data)
 
         service = Service(
             name=data.get(f'{prefix}-0-name', ''),
@@ -321,10 +333,10 @@ class CreateVmForm(NetBoxModelForm):
             ports=ports,
             description=description,
             virtual_machine=vm,
-            tags=tags
         )
         service.full_clean()
         service.save()
+        CreateVmForm.assign_tags(service,data)
         return service
 
     def create_all_services(self, data, vm):
@@ -341,10 +353,10 @@ class CreateVmForm(NetBoxModelForm):
             status=data.get('ip_new-status', ''),
             role=data.get('ip_new-role', ''),
             assigned_object=vm_interface,
-            tags=data.get('tag_new-tag', '')
         )
         ip_address.full_clean()
         ip_address.save()
+        CreateVmForm.assign_tags(ip_address,data)
         return ip_address
 
     def process_creation(self, data):
-- 
GitLab


From bd367838bf3881d6a15f393f185fabbcb22c73ce Mon Sep 17 00:00:00 2001
From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu>
Date: Tue, 3 Dec 2024 11:31:47 +0000
Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=90=9B=20Fix=20several=20issues?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 netbox_sys_plugin/forms/createvm.py           |  6 ++---
 netbox_sys_plugin/navigation.py               | 27 ++++++++++---------
 netbox_sys_plugin/tables.py                   |  2 +-
 .../providercredentials.html                  |  2 +-
 4 files changed, 19 insertions(+), 18 deletions(-)

diff --git a/netbox_sys_plugin/forms/createvm.py b/netbox_sys_plugin/forms/createvm.py
index f0483a8..9bbfbb4 100644
--- a/netbox_sys_plugin/forms/createvm.py
+++ b/netbox_sys_plugin/forms/createvm.py
@@ -28,7 +28,7 @@ class ClusterForm(NetBoxModelForm):
         super().__init__(*args, **kwargs)
         self.fields.pop('tags',None)
 
-class ClusterFormList(NetBoxModelForm):
+class ClusterFormList(NetBoxModelForm, forms.Form):
     """Form for Cluster List."""
     cluster_type = DynamicModelChoiceField(
         queryset=ClusterType.objects.all(), required=False, label="Provider Type"
@@ -147,7 +147,7 @@ TagFormSet = forms.modelformset_factory(
 
 # Combined form
 class CreateVmForm(NetBoxModelForm):
-    """Combined form for managing ClusterType, Cluster, and VirtualMachine."""
+    """Combined form for managing ClusterType, Cluster, and VirtualMachine, etc"""
 
     name = forms.CharField(max_length=50, min_length=1, required=False, label="Name")
     slug = forms.CharField(max_length=100, min_length=1, required=False, label="Slug")
@@ -360,7 +360,7 @@ class CreateVmForm(NetBoxModelForm):
         return ip_address
 
     def process_creation(self, data):
-        """Process creation of ClusterType, Cluster, VM, and related objects."""
+        """Object creation"""
         try:
             with transaction.atomic():
                 cluster_exists = self.check_cluster_exist(data)
diff --git a/netbox_sys_plugin/navigation.py b/netbox_sys_plugin/navigation.py
index 2391977..8a4fa9b 100644
--- a/netbox_sys_plugin/navigation.py
+++ b/netbox_sys_plugin/navigation.py
@@ -68,37 +68,38 @@ clusterProviderCredentialsItem = [
 ]
 
 vmMachineItem = [
+    PluginMenuItem(
+        link="plugins:netbox_sys_plugin:virtualmachinetype_list",
+        link_text="VM Type",
+        buttons=vm_machine_type_buttons
+    ),
     PluginMenuItem(
         link="plugins:netbox_sys_plugin:vmassignedvirtualmachinetype_list",
-        link_text="Type Assignment",
+        link_text="VM Type Assignment",
         buttons=vm_assigned_machine_type_buttons,
     ),
     PluginMenuItem(
         link="plugins:netbox_sys_plugin:virtualmachinemaintenance_list",
-        link_text="Maintenance",
+        link_text="VM Maintenance",
         buttons=vm_maintenance_buttons,
     ),
-]
-
-operItem =[
-    PluginMenuItem(
-        link="plugins:netbox_sys_plugin:virtualmachinetype_list",
-        link_text="Virtual Machine Types",
-        buttons=vm_machine_type_buttons
-    ),
     PluginMenuItem(
         link="plugins:netbox_sys_plugin:creatvm_add",
-        link_text="Create Virtual Machine ",
+        link_text="Create VM",
         buttons=create_vm_buttons
     ),
 ]
+
+operItem =[
+
+]
 #Menu
 menu = PluginMenu(
     label="Sys",
     groups=(
-        ("Virtual Machine", (vmMachineItem)),
+        
         ("Provider", clusterProviderCredentialsItem),
-        ("Operation", (operItem)),
+        ("Virtual Machine", (vmMachineItem)),
             ),
     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 b7de3e4..374b9b2 100644
--- a/netbox_sys_plugin/tables.py
+++ b/netbox_sys_plugin/tables.py
@@ -57,7 +57,7 @@ class ProviderCredentialsTable(NetBoxTable):
     )
     provider_password_vault_path = tables.Column(
         linkify=False,
-        verbose_name="Password Vault URL",
+        verbose_name="Password Vault Path",
     )
 
     class Meta(NetBoxTable.Meta):
diff --git a/netbox_sys_plugin/templates/netbox_sys_plugin/providercredentials.html b/netbox_sys_plugin/templates/netbox_sys_plugin/providercredentials.html
index cb91198..f6c63b9 100644
--- a/netbox_sys_plugin/templates/netbox_sys_plugin/providercredentials.html
+++ b/netbox_sys_plugin/templates/netbox_sys_plugin/providercredentials.html
@@ -16,7 +16,7 @@ Provider Credentials for {{ object.assigned_object }}
             <td>{{ object.provider_username }}</td>
           </tr>
           <tr>
-            <th scope="row">Password vault URL</th>
+            <th scope="row">Password vault Path</th>
             <td>{{ object.provider_password_vault_path }}</td>
           </tr>
         </table>
-- 
GitLab