From 69e67d025831a98303dea8e1913f17ce0f346779 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 25 Mar 2025 17:11:36 -0400 Subject: [PATCH] Initial work on JSON schema validation --- netbox/dcim/forms/model_forms.py | 3 +++ netbox/dcim/models/modules.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index d5b84649e..b1c5931e2 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -406,6 +406,9 @@ class DeviceTypeForm(NetBoxModelForm): class ModuleTypeProfileForm(NetBoxModelForm): + schema = JSONField( + label=_('Schema') + ) comments = CommentField() fieldsets = ( diff --git a/netbox/dcim/models/modules.py b/netbox/dcim/models/modules.py index a41c216be..40c4beb4a 100644 --- a/netbox/dcim/models/modules.py +++ b/netbox/dcim/models/modules.py @@ -1,8 +1,11 @@ +import jsonschema import yaml from django.core.exceptions import ValidationError from django.db import models from django.db.models.signals import post_save from django.utils.translation import gettext_lazy as _ +from jsonschema.exceptions import SchemaError, ValidationError as JSONValidationError +from jsonschema.validators import Draft202012Validator as JSONSchemaValidator from dcim.choices import * from dcim.utils import update_interface_bridges @@ -42,6 +45,17 @@ class ModuleTypeProfile(PrimaryModel): def __str__(self): return self.name + def clean(self): + super().clean() + + # Validate the schema definition + try: + JSONSchemaValidator.check_schema(self.schema) + except SchemaError as e: + raise ValidationError({ + 'schema': _("Invalid schema: {error}").format(error=e) + }) + class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin): """ @@ -108,6 +122,20 @@ class ModuleType(ImageAttachmentsMixin, PrimaryModel, WeightMixin): def full_name(self): return f"{self.manufacturer} {self.model}" + def clean(self): + super().clean() + + # Validate any attributes against the assigned profile's schema + if self.profile: + try: + jsonschema.validate(self.attributes, schema=self.profile.schema) + except JSONValidationError as e: + raise ValidationError({ + 'attributes': _("Invalid schema: {error}").format(error=e) + }) + else: + self.attributes = None + def to_yaml(self): data = { 'profile': self.profile.name if self.profile else None,