diff --git a/netbox/ipam/forms/filtersets.py b/netbox/ipam/forms/filtersets.py index a79707cf7..36aae3774 100644 --- a/netbox/ipam/forms/filtersets.py +++ b/netbox/ipam/forms/filtersets.py @@ -441,18 +441,6 @@ class VLANGroupFilterForm(NetBoxModelFilterSetForm): required=False, label=_('Rack') ) - # min_vid = forms.IntegerField( - # required=False, - # min_value=VLAN_VID_MIN, - # max_value=VLAN_VID_MAX, - # label=_('Minimum VID') - # ) - # max_vid = forms.IntegerField( - # required=False, - # min_value=VLAN_VID_MIN, - # max_value=VLAN_VID_MAX, - # label=_('Maximum VID') - # ) cluster = DynamicModelMultipleChoiceField( queryset=Cluster.objects.all(), required=False, diff --git a/netbox/ipam/models/vlans.py b/netbox/ipam/models/vlans.py index fe52a6647..fb88d68e6 100644 --- a/netbox/ipam/models/vlans.py +++ b/netbox/ipam/models/vlans.py @@ -89,16 +89,10 @@ class VLANGroup(OrganizationalModel): if self.scope_id and not self.scope_type: raise ValidationError(_("Cannot set scope_id without scope_type.")) - # Validate min/max child VID limits - if self.max_vid < self.min_vid: - raise ValidationError({ - 'max_vid': _("Maximum child VID must be greater than or equal to minimum child VID") - }) - def save(self, *args, **kwargs): self._total_vlan_ids = 0 - for range in vland_id_ranges: - self._total_vlan_ids += range.upper - range.lower + 1 + for vlan_range in vland_id_ranges: + self._total_vlan_ids += vlan_range.upper - vlan_range.lower + 1 super().save(*args, **kwargs) @@ -106,7 +100,9 @@ class VLANGroup(OrganizationalModel): """ Return all available VLANs within this group. """ - available_vlans = {vid for vid in range(self.min_vid, self.max_vid + 1)} + available_vlans = {} + for vlan_range in self.vlan_id_ranges: + available_vlans = {vid for vid in range(vlan_range.lower, vlan_range.upper + 1)} available_vlans -= set(VLAN.objects.filter(group=self).values_list('vid', flat=True)) return sorted(available_vlans) @@ -115,8 +111,7 @@ class VLANGroup(OrganizationalModel): """ Return the first available VLAN ID (1-4094) in the group. """ - available_vids = [] - # available_vids = self.get_available_vids() + available_vids = self.get_available_vids() if available_vids: return available_vids[0] return None @@ -127,6 +122,10 @@ class VLANGroup(OrganizationalModel): """ return VLAN.objects.filter(group=self).order_by('vid') + @property + def vlan_ranges(self): + return ','.join([f"{self.vlan_id_ranges.lower}-{self.vlan_id_ranges.upper}" for val in value]) + class VLAN(PrimaryModel): """ diff --git a/netbox/ipam/querysets.py b/netbox/ipam/querysets.py index bb3aba433..c84231c4b 100644 --- a/netbox/ipam/querysets.py +++ b/netbox/ipam/querysets.py @@ -63,7 +63,7 @@ class VLANGroupQuerySet(RestrictedQuerySet): return self.annotate( vlan_count=count_related(VLAN, 'group'), - # utilization=Round(F('vlan_count') / (F('max_vid') - F('min_vid') + 1.0) * 100, 2) + utilization=Round(F('vlan_count') / F('_total_vlan_ids') * 100, 2) ) diff --git a/netbox/ipam/tables/vlans.py b/netbox/ipam/tables/vlans.py index a1250fed3..b5a98a27e 100644 --- a/netbox/ipam/tables/vlans.py +++ b/netbox/ipam/tables/vlans.py @@ -72,6 +72,10 @@ class VLANGroupTable(NetBoxTable): linkify=True, orderable=False ) + vlan_ranges = tables.Column( + verbose_name=_('VLAN Ranges'), + orderable=False + ) vlan_count = columns.LinkedCountColumn( viewname='ipam:vlan_list', url_params={'group_id': 'pk'}, @@ -91,11 +95,10 @@ class VLANGroupTable(NetBoxTable): class Meta(NetBoxTable.Meta): model = VLANGroup fields = ( - 'pk', 'id', 'name', 'scope_type', 'scope', 'vlan_id_ranges', 'vlan_count', 'slug', 'description', + 'pk', 'id', 'name', 'scope_type', 'scope', 'vlan_ranges', 'vlan_count', 'slug', 'description', 'tags', 'created', 'last_updated', 'actions', 'utilization', ) - # default_columns = ('pk', 'name', 'scope_type', 'scope', 'vlan_count', 'utilization', 'description') - default_columns = ('pk', 'name', 'scope_type', 'scope', 'vlan_count', 'description') + default_columns = ('pk', 'name', 'scope_type', 'scope', 'vlan_count', 'utilization', 'description') # diff --git a/netbox/templates/ipam/vlangroup.html b/netbox/templates/ipam/vlangroup.html index 813032b2d..8c45a3994 100644 --- a/netbox/templates/ipam/vlangroup.html +++ b/netbox/templates/ipam/vlangroup.html @@ -40,14 +40,12 @@ {% trans "Permitted VIDs" %} - {{ object.min_vid }} - {{ object.max_vid }} + {{ object.vlan_ranges }} - {% comment %} Utilization {% utilization_graph object.utilization %} - {% endcomment %} {% include 'inc/panels/tags.html' %} diff --git a/netbox/utilities/forms/fields/array.py b/netbox/utilities/forms/fields/array.py index 1bd002004..197588006 100644 --- a/netbox/utilities/forms/fields/array.py +++ b/netbox/utilities/forms/fields/array.py @@ -43,8 +43,7 @@ class NumericRangeArrayField(forms.CharField): ) def prepare_value(self, value): - range_str = ','.join([f"{val.lower}-{val.upper}" for val in value]) - return range_str + return ','.join([f"{val.lower}-{val.upper}" for val in value]) def to_python(self, value): if not value: