diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 0046a2be4..1f2f3b9ed 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -699,35 +699,20 @@ class WritableInterfaceSerializer(ValidatedModelSerializer): 'id', 'device', 'name', 'form_factor', 'enabled', 'lag', 'mtu', 'mac_address', 'mgmt_only', 'description', 'mode', 'untagged_vlan', 'tagged_vlans', ] - ignore_validation_fields = [ - 'tagged_vlans' - ] def validate(self, data): - # Get the device for later use - if self.instance: - device = self.instance.device - else: - device = data.get('device') + # Validate that all untagged VLANs either belong to the same site as the Interface's parent Deivce or + # VirtualMachine, or are global. + parent = self.instance.parent if self.instance else data.get('device') or data.get('virtual_machine') + for vlan in data.get('tagged_vlans', []): + if vlan.site not in [parent, None]: + raise serializers.ValidationError( + "Tagged VLAN {} must belong to the same site as the interface's parent device/VM, or it must be " + "global".format(vlan) + ) - # Validate VLANs belong to the device's site or global - # We have to do this here decause of the ManyToMany relationship - native_vlan = data.get('native_vlan') - if native_vlan: - if native_vlan.site != device.site and native_vlan.site is not None: - raise serializers.ValidationError("Native VLAN is invalid for the interface's device.") - - tagged_vlan_members = data.get('tagged_vlan_members') - if tagged_vlan_members: - for vlan in tagged_vlan_members: - if vlan.site != device.site and vlan.site is not None: - raise serializers.ValidationError("Tagged VLAN {} is invalid for the interface's device.".format(vlan)) - - # Enforce model validation - super(WritableInterfaceSerializer, self).validate(data) - - return data + return super(WritableInterfaceSerializer, self).validate(data) # diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 1012924b7..1993e9143 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -1318,6 +1318,13 @@ class Interface(models.Model): ) }) + # Validate untagged VLAN + if self.untagged_vlan and self.untagged_vlan.site not in [self.parent.site, None]: + raise ValidationError({ + 'untagged_vlan': "The untagged VLAN ({}) must belong to the same site as the interface's parent " + "device/VM, or it must be global".format(self.untagged_vlan) + }) + @property def parent(self): return self.device or self.virtual_machine diff --git a/netbox/utilities/api.py b/netbox/utilities/api.py index 237ffd723..4f5ce4471 100644 --- a/netbox/utilities/api.py +++ b/netbox/utilities/api.py @@ -48,11 +48,6 @@ class ValidatedModelSerializer(ModelSerializer): attrs = data.copy() attrs.pop('custom_fields', None) - # remove any fields marked for no validation - ignore_validation_fields = getattr(self.Meta, 'ignore_validation_fields', []) - for field in ignore_validation_fields: - attrs.pop(field) - # Run clean() on an instance of the model if self.instance is None: instance = self.Meta.model(**attrs)