diff --git a/docs/model/netbox_sys_provider_type_extra_config.md b/docs/model/netbox_sys_provider_type_extra_config.md index 7b7d3ce3b988612e9ac40cb467d9dccbe7892e3e..df4bb78bf1be71a161863c3d5e8ec7e142fe948a 100644 --- a/docs/model/netbox_sys_provider_type_extra_config.md +++ b/docs/model/netbox_sys_provider_type_extra_config.md @@ -7,4 +7,5 @@ | 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 | -| extra_config | JSON | Provider type extra configurations | \ No newline at end of file +| extra_config_structure | JSON | JSON with data structure for extra configurations | +| extra_config_description | Char Field | Provider type extra configurations description | diff --git a/docs/model/netbox_sys_vm_assigned_extra_config.md b/docs/model/netbox_sys_vm_assigned_extra_config.md index a0d232a046769bf7f8e8fb448e4cfca2690fec90..803f49395928de724e9a235c8c16f341136f9c24 100644 --- a/docs/model/netbox_sys_vm_assigned_extra_config.md +++ b/docs/model/netbox_sys_vm_assigned_extra_config.md @@ -2,9 +2,11 @@ **VM Assigned Provider Type Extra Configuration definition class** -| FIELD | TYPE | DESCRIPTION | -|------------------------------- |----------------------------------|---------------------------------------------------------------------------| -| id | Big (8 byte) integer | Unique ID | -| assigned_object_type_id | Big (8 byte) integer | Virtual Machine type type ID | -| assigned_object_id | Big (8 byte) integer | Virtual Machine unique ID | -| extra_config | JSON | Json with extra configuration with the structure from provider type | \ No newline at end of file +| FIELD | TYPE | DESCRIPTION | +|------------------------------- |----------------------------------|---------------------------------------------------------------------------| +| id | Big (8 byte) integer | Unique ID | +| provider_type_extra_config_id | Big (8 byte) integer | Extra Config Structure ID | +| provider_type_extra_config_assignment_desc | Char Field | Provider Type Extra config assigment description | +| assigned_object_type_id | Big (8 byte) integer | Virtual Machine type type ID | +| assigned_object_id | Big (8 byte) integer | Virtual Machine unique ID | +| extra_config_values | JSON | Json with extra configuration with the structure from provider type | \ No newline at end of file diff --git a/netbox_sys_plugin/api/views.py b/netbox_sys_plugin/api/views.py index b3a103029a75a16915e7a7b47e99dfb9cdaaca4b..9424ca1627bd80b106b16e85af82c618ee564b19 100644 --- a/netbox_sys_plugin/api/views.py +++ b/netbox_sys_plugin/api/views.py @@ -58,6 +58,7 @@ class WebhookSettingsViewSet(viewsets.ModelViewSet): class ProviderTypeExtraConfigViewSet(viewsets.ModelViewSet): queryset = ProviderTypeExtraConfig.objects.all() serializer_class = ProviderTypeExtraConfigSerializer + filterset_class = ProviderTypeExtraConfigFilterSet http_method_names = ["get", "post", "patch", "delete", "options"] class VmAssignedExtraConfigViewSet(viewsets.ModelViewSet): diff --git a/netbox_sys_plugin/forms/createvm.py b/netbox_sys_plugin/forms/createvm.py index bcd51f92eedd83fdcdb31768c0a17a5b63d6c8e9..954ba44ad286d70246c925634ba92f8b1bc5bb58 100644 --- a/netbox_sys_plugin/forms/createvm.py +++ b/netbox_sys_plugin/forms/createvm.py @@ -2,6 +2,7 @@ from django import forms from django.forms import inlineformset_factory from django.contrib.contenttypes.models import ContentType from django.utils.translation import gettext_lazy as _ +from django.core.exceptions import ValidationError from django.db import transaction from utilities.forms.fields import DynamicModelChoiceField, JSONField from netbox.forms import NetBoxModelForm @@ -9,8 +10,8 @@ from virtualization.models import ClusterType, Cluster, VirtualMachine, VMInterf from ipam.models import IPAddress, Service from dcim.models import DeviceRole, Platform from extras.models import Tag, TaggedItem -from .. models import DomainNames, VmAssignedVirtualMachineType, VirtualMachineType -from . . utils import send_webhook +from .. models import DomainNames, VmAssignedVirtualMachineType, VirtualMachineType, VmAssignedExtraConfig, ProviderTypeExtraConfig +from . . utils import send_webhook, validate_extra_config_values import netaddr @@ -32,11 +33,11 @@ class ClusterForm(NetBoxModelForm): class ClusterFormList(NetBoxModelForm, forms.Form): """Form for Cluster List.""" cluster_type = DynamicModelChoiceField( - queryset=ClusterType.objects.all(), required=False, label="Provider Type" + queryset=ClusterType.objects.all(), required=True, label="Provider Type" ) cluster = DynamicModelChoiceField( - queryset=Cluster.objects.all(), required=False, label="Provider", + queryset=Cluster.objects.all(), required=True, label="Provider", query_params={'type_id': '$cl_list_new-cluster_type',}, ) @@ -128,6 +129,32 @@ class VmAssignedVirtualMachineTypeForm(NetBoxModelForm, forms.Form): super().__init__(*args, **kwargs) self.fields.pop('tags',None) +class VmAssignedExtraConfigurationsForm(NetBoxModelForm, forms.Form): + """Form for Extra Config Assignment.""" + + provider_type_extra_config = DynamicModelChoiceField( + queryset=ProviderTypeExtraConfig.objects.all(), + required=True, + label="Provider Type Extra Config", + query_params={ + 'cluster_type_id': '$cl_list_new-cluster_type', # Example: Filter configs with non-null structures + } + ) + + extra_config_values = JSONField( + label=_('Extra Config Values'), + required=True, + initial=({'fieldname1': 'fieldvalue1','fieldname2': 'fieldvalue2'}) + ) + + class Meta: + model = VmAssignedExtraConfig + fields = ('provider_type_extra_config','extra_config_values','tags') + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields.pop('tags',None) + class TagsForm(NetBoxModelForm): """Form for IP Addresses.""" tag = DynamicModelChoiceField( @@ -183,6 +210,10 @@ VmAssignedVirtualMachineTypeFormSet = forms.modelformset_factory( VirtualMachineType, form=VmAssignedVirtualMachineTypeForm, formset=VmAssignedVirtualMachineTypeForm, extra=1, can_delete=False ) +VmAssignedExtraConfigFormSet = forms.modelformset_factory( + VmAssignedExtraConfig, form=VmAssignedExtraConfigurationsForm, formset=VmAssignedExtraConfigurationsForm, extra=1, can_delete=False +) + TagFormSet = forms.modelformset_factory( Tag, form=TagsForm, formset=TagsForm, extra=1, can_delete=False ) @@ -245,6 +276,10 @@ class CreateVmForm(NetBoxModelForm): self.vmassignedvmtype_formsets = [] empty_vmassignedvmtype_formset = VmAssignedVirtualMachineTypeFormSet(data=data, prefix='vmassignedvmtype_new') self.vmassignedvmtype_formsets.append(('new', empty_vmassignedvmtype_formset)) + # VM Assigned Extra Config + self.vmassignedextraconfig_formsets = [] + empty_vmassignedextraconfig_formset = VmAssignedExtraConfigFormSet(data=data, prefix='vmassignedextraconfig_new') + self.vmassignedextraconfig_formsets.append(('new', empty_vmassignedextraconfig_formset)) # Tags self.tagformsets = [] empty_tagformset = TagFormSet(data=data, prefix='tag_new') @@ -296,7 +331,6 @@ class CreateVmForm(NetBoxModelForm): cleaned_data = {k.strip(): v.strip() for k, v in data.items() if isinstance(v,str)} return cleaned_data - @staticmethod def create_virtual_machine(data, cluster): """Create and save a VirtualMachine object.""" @@ -387,7 +421,6 @@ class CreateVmForm(NetBoxModelForm): CreateVmForm.assign_tags(ip_address,data) return ip_address - def create_all_ip_adresses(self, data, vm_interface): self.create_ip_address(data,vm_interface,'ip_new','Network IP Address') self.create_ip_address(data,vm_interface,'gateway_new','Network Gateway') @@ -431,6 +464,49 @@ class CreateVmForm(NetBoxModelForm): CreateVmForm.assign_tags(domain_names_object, data) return domain_names_object + @staticmethod + def create_extraconfig_assignment(data, vm): + """Assign Extra COnfig to the virtual machine.""" + extra_config_estructure_id =data.get('vmassignedextraconfig_new-provider_type_extra_config', '') + + extra_config_values =data.get('vmassignedextraconfig_new-extra_config_values', '') + + + extra_config_estructure_id =data.get('vmassignedextraconfig_new-provider_type_extra_config', '') + try: + extra_config_estructure = ProviderTypeExtraConfig.objects.get(pk=extra_config_estructure_id) + except ProviderTypeExtraConfig.DoesNotExist: + raise ValueError(f"Invalid Extra Config Structure: {extra_config_estructure_id}") + + cleaned_extra_config_data = CreateVmForm.clean_json(extra_config_values) + + #Validation of structure + provider_config = extra_config_estructure + extra_config_values= cleaned_extra_config_data + + if provider_config and provider_config.extra_config_structure: + structure_dict = provider_config.extra_config_structure + first_structure_name = next(iter(structure_dict), None) + + if first_structure_name: + structure = structure_dict[first_structure_name][0] + + if isinstance(structure, dict): + is_valid, errors = validate_extra_config_values(structure, extra_config_values) + if not is_valid: + raise ValueError(errors) + + vm_extraconfig = VmAssignedExtraConfig( + provider_type_extra_config=extra_config_estructure, + assigned_object=vm, + extra_config_values=cleaned_extra_config_data, + provider_type_extra_config_assignment_desc=f"Config {extra_config_estructure.extra_config_name} assigned to {vm.name} VM" + ) + vm_extraconfig.full_clean() + vm_extraconfig.save() + CreateVmForm.assign_tags(vm_extraconfig,data) + return vm_extraconfig + def process_creation(self, data): """Object creation""" try: @@ -438,7 +514,8 @@ class CreateVmForm(NetBoxModelForm): cluster = Cluster.objects.get(pk=data.get('cl_list_new-cluster', '')) vm = self.create_virtual_machine(data, cluster) vmi = self.create_vm_interface(data, vm) - self.create_vmtype_assignment(data,vm) + self.create_vmtype_assignment(data, vm) + self.create_extraconfig_assignment(data, vm) self.create_domain_names(data,vm) self.create_all_services(data, vm) self.create_all_ip_adresses(data, vmi) @@ -457,6 +534,11 @@ class CreateVmForm(NetBoxModelForm): assigned_object_id=vm.id, assigned_object_type=vm_content_type ).select_related('virtual_machine_type') + + extras = VmAssignedExtraConfig.objects.filter( + assigned_object_id=vm.id, + assigned_object_type=vm_content_type + ) domain_names = DomainNames.objects.filter( assigned_object_id=vm.id, @@ -472,15 +554,15 @@ class CreateVmForm(NetBoxModelForm): "platform": vm.platform.name if vm.platform else None, "description": vm.description, }, - "cluster": { + "provider": { "id": cluster.id if cluster else None, "name": cluster.name if cluster else None, - "type": { + "provider_type": { "id": cluster.type.id if cluster.type else None, "name": cluster.type.name if cluster.type else None, }, }, - "Virtual Machine Type": [ + "virtual_machine_type": [ { "id": type.id, "name": type.virtual_machine_type.virtual_machine_type_name, @@ -489,7 +571,7 @@ class CreateVmForm(NetBoxModelForm): } for type in types ], - "interface": [ + "network_interface": [ { "id": interface.id, "name": interface.name} @@ -500,21 +582,29 @@ class CreateVmForm(NetBoxModelForm): "id": ip.id, "address": str(ip.address), "status": ip.status, + "description": ip.description, } for ip in ip_addresses ], + "extra_config": [ + { + "id": extra.id, + "extra_config": extra.extra_config_values, + } + for extra in extras + ], "services": [ { + "description": service.description, "id": service.id, "name": service.name, - "description": service.description, } for service in services ], "domain_names": [ { "id": domain.id, - "names": domain.domain_names, + "domain_names": domain.domain_names, } for domain in domain_names ], diff --git a/netbox_sys_plugin/forms/machine.py b/netbox_sys_plugin/forms/machine.py index 7f00170f4b88abe6c53eab5ed468f8c93dd69333..8f424dcf0d5456f19b64a4e035eccba353c34236 100644 --- a/netbox_sys_plugin/forms/machine.py +++ b/netbox_sys_plugin/forms/machine.py @@ -244,7 +244,7 @@ class VmAssignedExtraConfigForm(NetBoxModelForm): extra_config_values = JSONField( label=_('Extra Config Values'), - required=False + required=True ) def __init__(self, *args, **kwargs): 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 fd8f6eccb6b4424b2270f65e2a3f36acf1198df7..6a5cda2c36e5fcbc4541200675e86f752dbc943a 100644 --- a/netbox_sys_plugin/templates/netbox_sys_plugin/create_vm.html +++ b/netbox_sys_plugin/templates/netbox_sys_plugin/create_vm.html @@ -99,6 +99,16 @@ SYS - Virtual Machine </div> {% endfor %} {% endfor %} + {% for vmassignedextraconfig_formset in form.vmassignedextraconfig_formsets %} + {% for vmassignedextraconfig_form in vmassignedextraconfig_formset %} + <div class="form-group" class="field-group mb-5"> + {{ vmassignedextraconfig_form.as_p }} + {% if vmassignedextraconfig_form.non_field_errors %} + <h1> {{ vmassignedextraconfig_form.non_field_errors }}</h1> + {% endif %} + </div> + {% endfor %} + {% endfor %} </div> <div class="field-group my-5"> <h5>Dependencies</h5>