From 4573078d0ba5cf69120727c95c28c09b6b2f9faf Mon Sep 17 00:00:00 2001 From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu> Date: Tue, 14 Jan 2025 11:40:01 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=9A=91=20Fix=20issues=20with=20struct?= =?UTF-8?q?ure=20and=20form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- netbox_sys_plugin/forms/createvm.py | 18 +++++++++++++ netbox_sys_plugin/migrations/0001_initial.py | 7 ++--- netbox_sys_plugin/models/provider.py | 2 ++ netbox_sys_plugin/validators.py | 28 ++++++++++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 netbox_sys_plugin/validators.py diff --git a/netbox_sys_plugin/forms/createvm.py b/netbox_sys_plugin/forms/createvm.py index 69d4d3a..aec6076 100644 --- a/netbox_sys_plugin/forms/createvm.py +++ b/netbox_sys_plugin/forms/createvm.py @@ -55,6 +55,14 @@ class ClusterFormList(NetBoxModelForm, forms.Form): class VirtualMachineForm(NetBoxModelForm): """Form for Virtual Machines.""" + role = DynamicModelChoiceField( + queryset=DeviceRole.objects.all(), required=True, label="Owner", + ) + + platform = DynamicModelChoiceField( + queryset=Platform.objects.all(), required=True, label="Platform", + ) + class Meta: model = VirtualMachine fields = ('name', 'cluster', 'status','role','platform', 'description','tags') @@ -341,12 +349,22 @@ class CreateVmForm(NetBoxModelForm): def create_virtual_machine(data, cluster): """Create and save a VirtualMachine object.""" role_id = data.get('vms_new-0-role', '') + + role_raw=role_id.strip() + if not role_raw: + raise ValueError("Owner cannot be empty") + try: role = DeviceRole.objects.get(pk=role_id) except DeviceRole.DoesNotExist as e: raise ValueError(f"Invalid Device Role ID: {role_id}") from e platform_id = data.get('vms_new-0-platform', '') + + platform_raw=platform_id.strip() + if not platform_raw: + raise ValueError("Platform cannot be empty") + try: platform = Platform.objects.get(pk=platform_id) except Platform.DoesNotExist as e: diff --git a/netbox_sys_plugin/migrations/0001_initial.py b/netbox_sys_plugin/migrations/0001_initial.py index 8d4fadf..d3b0feb 100644 --- a/netbox_sys_plugin/migrations/0001_initial.py +++ b/netbox_sys_plugin/migrations/0001_initial.py @@ -1,8 +1,9 @@ -# Generated by Django 4.2.16 on 2025-01-03 15:28 +# Generated by Django 4.2.16 on 2025-01-14 11:34 import django.core.validators from django.db import migrations, models import django.db.models.deletion +import netbox_sys_plugin.validators import taggit.managers import utilities.json @@ -12,8 +13,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), ('extras', '0098_webhook_custom_field_data_webhook_tags'), + ('contenttypes', '0002_remove_content_type_name'), ] operations = [ @@ -25,7 +26,7 @@ class Migration(migrations.Migration): ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=utilities.json.CustomFieldJSONEncoder)), ('extra_config_name', models.CharField(blank=True, default=None, max_length=50, null=True)), - ('extra_config_structure', models.JSONField(blank=True, null=True)), + ('extra_config_structure', models.JSONField(blank=True, null=True, validators=[netbox_sys_plugin.validators.validate_extra_config_structure])), ('extra_config_description', models.CharField(default=None, max_length=100)), ('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')), diff --git a/netbox_sys_plugin/models/provider.py b/netbox_sys_plugin/models/provider.py index a42f9d4..0e1441e 100644 --- a/netbox_sys_plugin/models/provider.py +++ b/netbox_sys_plugin/models/provider.py @@ -6,6 +6,7 @@ from django.contrib.contenttypes.models import ContentType from django.db import models from virtualization.models import Cluster, ClusterType from netbox.models import NetBoxModel +from ..validators import validate_extra_config_structure CLUSTER_ASSIGNMENT_MODELS = models.Q(models.Q(app_label="virtualization", model="Cluster")) CLUSTER_TYPE_ASSIGNMENT_MODELS = models.Q(models.Q(app_label="virtualization", model="ClusterType")) @@ -117,6 +118,7 @@ class ProviderTypeExtraConfig(NetBoxModel): extra_config_structure = models.JSONField( blank=True, null=True, + validators=[validate_extra_config_structure] ) extra_config_description = models.CharField( default=None, blank=False, null=False, diff --git a/netbox_sys_plugin/validators.py b/netbox_sys_plugin/validators.py new file mode 100644 index 0000000..91a6976 --- /dev/null +++ b/netbox_sys_plugin/validators.py @@ -0,0 +1,28 @@ +"""Validators definitions""" + +from django.core.exceptions import ValidationError + +def validate_extra_config_structure(value): + """ + Validates the `extra_config_structure` field to ensure it matches the required JSON structure. + """ + if not isinstance(value, dict): + raise ValidationError("The structure must be a dictionary.") + + if len(value) != 1: + raise ValidationError("The structure must contain exactly one key.") + + for struct_name, fields in value.items(): + if not isinstance(fields, list): + raise ValidationError("The value of the root key must be a list.") + + for field in fields: + if not isinstance(field, dict): + raise ValidationError("Each field in the list must be a dictionary.") + + for field_name, field_properties in field.items(): + if not isinstance(field_properties, dict): + raise ValidationError(f"The field `{field_name}` must be a dictionary.") + + if "type" not in field_properties or "required" not in field_properties: + raise ValidationError(f"The field `{field_name}` must contain `type` and `required` keys.") -- GitLab From 5a3e74b38ebdffcf8cfe8e86cbdbcae312a95119 Mon Sep 17 00:00:00 2001 From: Frederico Sequeira <frederico.sequeira@ext.ec.europa.eu> Date: Tue, 14 Jan 2025 11:45:54 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=9A=A8=20Fix=20Lint=20issues=20in=20v?= =?UTF-8?q?alidators?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- netbox_sys_plugin/validators.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/netbox_sys_plugin/validators.py b/netbox_sys_plugin/validators.py index 91a6976..3425f70 100644 --- a/netbox_sys_plugin/validators.py +++ b/netbox_sys_plugin/validators.py @@ -8,21 +8,21 @@ def validate_extra_config_structure(value): """ if not isinstance(value, dict): raise ValidationError("The structure must be a dictionary.") - + if len(value) != 1: raise ValidationError("The structure must contain exactly one key.") - for struct_name, fields in value.items(): + for fields in value.items(): if not isinstance(fields, list): raise ValidationError("The value of the root key must be a list.") - + for field in fields: if not isinstance(field, dict): raise ValidationError("Each field in the list must be a dictionary.") - + for field_name, field_properties in field.items(): if not isinstance(field_properties, dict): raise ValidationError(f"The field `{field_name}` must be a dictionary.") - + if "type" not in field_properties or "required" not in field_properties: raise ValidationError(f"The field `{field_name}` must contain `type` and `required` keys.") -- GitLab