mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-08 08:38:16 -06:00
Serialize VID ranges as integer lists in REST API
This commit is contained in:
parent
45183e4066
commit
46a40c5745
@ -6,11 +6,10 @@ from dcim.api.serializers_.sites import SiteSerializer
|
||||
from ipam.choices import *
|
||||
from ipam.constants import VLANGROUP_SCOPE_TYPES
|
||||
from ipam.models import VLAN, VLANGroup
|
||||
from netbox.api.fields import ChoiceField, ContentTypeField, RelatedObjectCountField
|
||||
from netbox.api.fields import ChoiceField, ContentTypeField, IntegerRangeSerializer, RelatedObjectCountField
|
||||
from netbox.api.serializers import NetBoxModelSerializer
|
||||
from tenancy.api.serializers_.tenants import TenantSerializer
|
||||
from utilities.api import get_serializer_for_model
|
||||
from utilities.data import ranges_to_string, string_to_range_array
|
||||
from vpn.api.serializers_.l2vpn import L2VPNTerminationSerializer
|
||||
from .roles import RoleSerializer
|
||||
|
||||
@ -22,14 +21,6 @@ __all__ = (
|
||||
)
|
||||
|
||||
|
||||
class NumericRangeArraySerializer(serializers.CharField):
|
||||
def to_internal_value(self, data):
|
||||
return string_to_range_array(data)
|
||||
|
||||
def to_representation(self, instance):
|
||||
return ranges_to_string(instance)
|
||||
|
||||
|
||||
class VLANGroupSerializer(NetBoxModelSerializer):
|
||||
scope_type = ContentTypeField(
|
||||
queryset=ContentType.objects.filter(
|
||||
@ -41,11 +32,11 @@ class VLANGroupSerializer(NetBoxModelSerializer):
|
||||
)
|
||||
scope_id = serializers.IntegerField(allow_null=True, required=False, default=None)
|
||||
scope = serializers.SerializerMethodField(read_only=True)
|
||||
vlan_id_ranges = IntegerRangeSerializer(many=True, required=False)
|
||||
utilization = serializers.CharField(read_only=True)
|
||||
|
||||
# Related object counts
|
||||
vlan_count = RelatedObjectCountField('vlans')
|
||||
vlan_id_ranges = NumericRangeArraySerializer(required=False)
|
||||
|
||||
class Meta:
|
||||
model = VLANGroup
|
||||
|
@ -96,19 +96,24 @@ class VLANGroup(OrganizationalModel):
|
||||
raise ValidationError(_("Cannot set scope_id without scope_type."))
|
||||
|
||||
# Validate vlan ranges
|
||||
if check_ranges_overlap(self.vlan_id_ranges):
|
||||
if self.vlan_id_ranges and check_ranges_overlap(self.vlan_id_ranges):
|
||||
raise ValidationError({'vlan_id_ranges': _("Ranges cannot overlap.")})
|
||||
|
||||
for ranges in self.vlan_id_ranges:
|
||||
if ranges.lower >= ranges.upper:
|
||||
raise ValidationError({
|
||||
'vlan_id_ranges': _("Maximum child VID must be greater than or equal to minimum child VID Invalid range ({value})").format(value=ranges)
|
||||
'vlan_id_ranges': _(
|
||||
"Maximum child VID must be greater than or equal to minimum child VID Invalid range ({value})"
|
||||
).format(value=ranges)
|
||||
})
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self._total_vlan_ids = 0
|
||||
for vlan_range in self.vlan_id_ranges:
|
||||
self._total_vlan_ids += vlan_range.upper - vlan_range.lower + 1
|
||||
if self.vlan_id_ranges:
|
||||
self._total_vlan_ids = 0
|
||||
for vlan_range in self.vlan_id_ranges:
|
||||
self._total_vlan_ids += vlan_range.upper - vlan_range.lower + 1
|
||||
else:
|
||||
self._total_vlan_ids = VLAN_VID_MAX - VLAN_VID_MIN + 1
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db.backends.postgresql.psycopg_any import NumericRange
|
||||
from django.utils.translation import gettext as _
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
@ -11,6 +12,7 @@ __all__ = (
|
||||
'ChoiceField',
|
||||
'ContentTypeField',
|
||||
'IPNetworkSerializer',
|
||||
'IntegerRangeSerializer',
|
||||
'RelatedObjectCountField',
|
||||
'SerializedPKRelatedField',
|
||||
)
|
||||
@ -154,3 +156,19 @@ class RelatedObjectCountField(serializers.ReadOnlyField):
|
||||
self.relation = relation
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
|
||||
class IntegerRangeSerializer(serializers.Serializer):
|
||||
"""
|
||||
Represents a range of integers.
|
||||
"""
|
||||
def to_internal_value(self, data):
|
||||
if not isinstance(data, (list, tuple)) or len(data) != 2:
|
||||
raise ValidationError(_("Ranges must be specified in the form (lower, upper)."))
|
||||
if type(data[0]) is not int or type(data[1]) is not int:
|
||||
raise ValidationError(_("Range boundaries must be defined as integers."))
|
||||
|
||||
return NumericRange(data[0], data[1])
|
||||
|
||||
def to_representation(self, instance):
|
||||
return instance.lower, instance.upper
|
||||
|
@ -140,6 +140,8 @@ def ranges_to_string(ranges):
|
||||
For example:
|
||||
[Range(1, 100), Range(200, 300)] => "1-100, 200-300"
|
||||
"""
|
||||
if not ranges:
|
||||
return ''
|
||||
return ', '.join([f"{r.lower}-{r.upper}" for r in ranges])
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user