From d15954775875f19ac8c9f26ba6248b24341e16ac Mon Sep 17 00:00:00 2001 From: Daniel Sheppard Date: Mon, 26 Aug 2024 23:57:55 -0500 Subject: [PATCH] Make changes to validation to account for M2M not being available under model in addition to not being able to check incoming vlans under same model. --- .../api/serializers_/device_components.py | 21 +++++++++++++++++++ netbox/dcim/forms/model_forms.py | 20 ++++++++++++++++++ netbox/dcim/models/device_components.py | 8 ++----- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/netbox/dcim/api/serializers_/device_components.py b/netbox/dcim/api/serializers_/device_components.py index d342a8c49..f26e81aa1 100644 --- a/netbox/dcim/api/serializers_/device_components.py +++ b/netbox/dcim/api/serializers_/device_components.py @@ -238,6 +238,27 @@ class InterfaceSerializer(NetBoxModelSerializer, CabledObjectSerializer, Connect def validate(self, data): if not self.nested: + + # Validate 802.1q mode and vlan(s) + mode = None + tagged_vlans = [] + + if self.instance.pk and 'mode' in data.keys(): + mode = data.get('mode') if 'mode' in self.data.keys() else self.instance.get('mode') + elif 'mode' in data.keys(): + mode = data.get('mode') + + if self.instance.pk and 'tagged_vlans' in data.keys(): + tagged_vlans = data.get('tagged_vlans') if 'tagged_vlans' in data.keys() else \ + self.instance.tagged_vlans.all() + elif 'tagged_vlans' in data.keys(): + tagged_vlans = data.get('tagged_vlans') + + if mode != InterfaceModeChoices.MODE_TAGGED and tagged_vlans: + raise serializers.ValidationError({ + 'tagged_vlans': "Interface mode does not support including tagged vlans" + }) + # Validate many-to-many VLAN assignments device = self.instance.device if self.instance else data.get('device') for vlan in data.get('tagged_vlans', []): diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py index c12ddccde..a03175725 100644 --- a/netbox/dcim/forms/model_forms.py +++ b/netbox/dcim/forms/model_forms.py @@ -1363,6 +1363,26 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm): 'mode': '802.1Q Mode', } + def clean(self): + mode = None + tagged_vlans = [] + + if self.instance.pk and 'mode' in self.cleaned_data.keys(): + mode = self.cleaned_data.get('mode') if 'mode' in self.cleaned_data.keys() else self.instance.get('mode') + elif 'mode' in self.cleaned_data.keys(): + mode = self.cleaned_data.get('mode') + + if self.instance.pk and 'tagged_vlans' in self.cleaned_data.keys(): + tagged_vlans = self.cleaned_data.get('tagged_vlans') if 'tagged_vlans' in self.cleaned_data.keys() else\ + self.instance.tagged_vlans.all() + elif 'tagged_vlans' in self.cleaned_data.keys(): + tagged_vlans = self.cleaned_data.get('tagged_vlans') + + if mode != InterfaceModeChoices.MODE_TAGGED and tagged_vlans: + raise forms.ValidationError({ + 'tagged_vlans': "Interface mode does not support including tagged vlans" + }) + class FrontPortForm(ModularDeviceComponentForm): rear_port = DynamicModelChoiceField( diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index b4382b455..101a1a459 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -894,6 +894,8 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd raise ValidationError({'rf_channel_width': _("Cannot specify custom width with channel selected.")}) # VLAN validation + if self.mode is None and self.untagged_vlan: + raise ValidationError({'untagged_vlan': _("Interface mode does not support including an untagged vlan.")}) # Validate untagged VLAN if self.untagged_vlan and self.untagged_vlan.site not in [self.device.site, None]: @@ -904,12 +906,6 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd ).format(untagged_vlan=self.untagged_vlan) }) - # Validate that tagged-all payload does not include tagged_vlans - if self.mode != InterfaceModeChoices.MODE_TAGGED and self.tagged_vlans: - raise ValidationError({ - 'tagged_vlans': "Interface mode does not support including tagged vlans" - }) - def save(self, *args, **kwargs): # Set absolute channel attributes from selected options