mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 09:51:22 -06:00
18296 Add Tenancy to VLAN Groups (#18690)
* 18296 add tenant to vlan groups * 18296 add tenant to vlan groups * 18296 add tenant to vlan groups * 18296 add tenant to vlan groups * 18296 review changes
This commit is contained in:
parent
ef89fc1264
commit
08b2fc424a
@ -37,6 +37,7 @@ class VLANGroupSerializer(NetBoxModelSerializer):
|
||||
scope = serializers.SerializerMethodField(read_only=True)
|
||||
vid_ranges = IntegerRangeSerializer(many=True, required=False)
|
||||
utilization = serializers.CharField(read_only=True)
|
||||
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
|
||||
|
||||
# Related object counts
|
||||
vlan_count = RelatedObjectCountField('vlans')
|
||||
@ -45,7 +46,7 @@ class VLANGroupSerializer(NetBoxModelSerializer):
|
||||
model = VLANGroup
|
||||
fields = [
|
||||
'id', 'url', 'display_url', 'display', 'name', 'slug', 'scope_type', 'scope_id', 'scope', 'vid_ranges',
|
||||
'description', 'tags', 'custom_fields', 'created', 'last_updated', 'vlan_count', 'utilization'
|
||||
'tenant', 'description', 'tags', 'custom_fields', 'created', 'last_updated', 'vlan_count', 'utilization'
|
||||
]
|
||||
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'vlan_count')
|
||||
validators = []
|
||||
|
@ -857,7 +857,7 @@ class FHRPGroupAssignmentFilterSet(ChangeLoggedModelFilterSet):
|
||||
)
|
||||
|
||||
|
||||
class VLANGroupFilterSet(OrganizationalModelFilterSet):
|
||||
class VLANGroupFilterSet(OrganizationalModelFilterSet, TenancyFilterSet):
|
||||
scope_type = ContentTypeFilter()
|
||||
region = django_filters.NumberFilter(
|
||||
method='filter_scope'
|
||||
|
@ -430,11 +430,17 @@ class VLANGroupBulkEditForm(NetBoxModelBulkEditForm):
|
||||
label=_('VLAN ID ranges'),
|
||||
required=False
|
||||
)
|
||||
tenant = DynamicModelChoiceField(
|
||||
label=_('Tenant'),
|
||||
queryset=Tenant.objects.all(),
|
||||
required=False
|
||||
)
|
||||
|
||||
model = VLANGroup
|
||||
fieldsets = (
|
||||
FieldSet('site', 'vid_ranges', 'description'),
|
||||
FieldSet('scope_type', 'scope', name=_('Scope')),
|
||||
FieldSet('tenant', name=_('Tenancy')),
|
||||
)
|
||||
nullable_fields = ('description', 'scope')
|
||||
|
||||
|
@ -438,10 +438,17 @@ class VLANGroupImportForm(NetBoxModelImportForm):
|
||||
vid_ranges = NumericRangeArrayField(
|
||||
required=False
|
||||
)
|
||||
tenant = CSVModelChoiceField(
|
||||
label=_('Tenant'),
|
||||
queryset=Tenant.objects.all(),
|
||||
required=False,
|
||||
to_field_name='name',
|
||||
help_text=_('Assigned tenant')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = VLANGroup
|
||||
fields = ('name', 'slug', 'scope_type', 'scope_id', 'vid_ranges', 'description', 'tags')
|
||||
fields = ('name', 'slug', 'scope_type', 'scope_id', 'vid_ranges', 'tenant', 'description', 'tags')
|
||||
labels = {
|
||||
'scope_id': 'Scope ID',
|
||||
}
|
||||
|
@ -411,12 +411,13 @@ class FHRPGroupFilterForm(NetBoxModelFilterSetForm):
|
||||
tag = TagFilterField(model)
|
||||
|
||||
|
||||
class VLANGroupFilterForm(NetBoxModelFilterSetForm):
|
||||
class VLANGroupFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||
fieldsets = (
|
||||
FieldSet('q', 'filter_id', 'tag'),
|
||||
FieldSet('region', 'sitegroup', 'site', 'location', 'rack', name=_('Location')),
|
||||
FieldSet('cluster_group', 'cluster', name=_('Cluster')),
|
||||
FieldSet('contains_vid', name=_('VLANs')),
|
||||
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
|
||||
)
|
||||
model = VLANGroup
|
||||
region = DynamicModelMultipleChoiceField(
|
||||
|
@ -598,7 +598,7 @@ class FHRPGroupAssignmentForm(forms.ModelForm):
|
||||
return group
|
||||
|
||||
|
||||
class VLANGroupForm(NetBoxModelForm):
|
||||
class VLANGroupForm(TenancyForm, NetBoxModelForm):
|
||||
slug = SlugField()
|
||||
vid_ranges = NumericRangeArrayField(
|
||||
label=_('VLAN IDs')
|
||||
@ -621,12 +621,13 @@ class VLANGroupForm(NetBoxModelForm):
|
||||
FieldSet('name', 'slug', 'description', 'tags', name=_('VLAN Group')),
|
||||
FieldSet('vid_ranges', name=_('Child VLANs')),
|
||||
FieldSet('scope_type', 'scope', name=_('Scope')),
|
||||
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = VLANGroup
|
||||
fields = [
|
||||
'name', 'slug', 'description', 'vid_ranges', 'scope_type', 'tags',
|
||||
'name', 'slug', 'description', 'vid_ranges', 'scope_type', 'tenant_group', 'tenant', 'tags',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -266,6 +266,7 @@ class VLANGroupType(OrganizationalObjectType):
|
||||
|
||||
vlans: List[VLANType]
|
||||
vid_ranges: List[str]
|
||||
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
|
||||
|
||||
@strawberry_django.field
|
||||
def scope(self) -> Annotated[Union[
|
||||
|
26
netbox/ipam/migrations/0077_vlangroup_tenant.py
Normal file
26
netbox/ipam/migrations/0077_vlangroup_tenant.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Generated by Django 5.1.3 on 2025-02-20 17:49
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ipam', '0076_natural_ordering'),
|
||||
('tenancy', '0017_natural_ordering'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='vlangroup',
|
||||
name='tenant',
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name='vlan_groups',
|
||||
to='tenancy.tenant',
|
||||
),
|
||||
),
|
||||
]
|
@ -62,6 +62,13 @@ class VLANGroup(OrganizationalModel):
|
||||
verbose_name=_('VLAN ID ranges'),
|
||||
default=default_vid_ranges
|
||||
)
|
||||
tenant = models.ForeignKey(
|
||||
to='tenancy.Tenant',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='vlan_groups',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
_total_vlan_ids = models.PositiveBigIntegerField(
|
||||
default=VLAN_VID_MAX - VLAN_VID_MIN + 1
|
||||
)
|
||||
|
@ -28,7 +28,7 @@ AVAILABLE_LABEL = mark_safe('<span class="badge text-bg-success">Available</span
|
||||
# VLAN groups
|
||||
#
|
||||
|
||||
class VLANGroupTable(NetBoxTable):
|
||||
class VLANGroupTable(TenancyColumnsMixin, NetBoxTable):
|
||||
name = tables.Column(
|
||||
verbose_name=_('Name'),
|
||||
linkify=True
|
||||
@ -65,9 +65,11 @@ class VLANGroupTable(NetBoxTable):
|
||||
model = VLANGroup
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'scope_type', 'scope', 'vid_ranges_list', 'vlan_count', 'slug', 'description',
|
||||
'tags', 'created', 'last_updated', 'actions', 'utilization',
|
||||
'tenant', 'tenant_group', 'tags', 'created', 'last_updated', 'actions', 'utilization',
|
||||
)
|
||||
default_columns = (
|
||||
'pk', 'name', 'scope_type', 'scope', 'vlan_count', 'utilization', 'tenant', 'description'
|
||||
)
|
||||
default_columns = ('pk', 'name', 'scope_type', 'scope', 'vlan_count', 'utilization', 'description')
|
||||
|
||||
|
||||
#
|
||||
|
@ -1568,27 +1568,45 @@ class VLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
cluster = Cluster(name='Cluster 1', type=clustertype)
|
||||
cluster.save()
|
||||
|
||||
tenant_groups = (
|
||||
TenantGroup(name='Tenant group 1', slug='tenant-group-1'),
|
||||
TenantGroup(name='Tenant group 2', slug='tenant-group-2'),
|
||||
TenantGroup(name='Tenant group 3', slug='tenant-group-3'),
|
||||
)
|
||||
for tenantgroup in tenant_groups:
|
||||
tenantgroup.save()
|
||||
|
||||
tenants = (
|
||||
Tenant(name='Tenant 1', slug='tenant-1', group=tenant_groups[0]),
|
||||
Tenant(name='Tenant 2', slug='tenant-2', group=tenant_groups[1]),
|
||||
Tenant(name='Tenant 3', slug='tenant-3', group=tenant_groups[2]),
|
||||
)
|
||||
Tenant.objects.bulk_create(tenants)
|
||||
|
||||
vlan_groups = (
|
||||
VLANGroup(
|
||||
name='VLAN Group 1',
|
||||
slug='vlan-group-1',
|
||||
vid_ranges=[NumericRange(1, 11), NumericRange(100, 200)],
|
||||
scope=region,
|
||||
description='foobar1'
|
||||
description='foobar1',
|
||||
tenant=tenants[0]
|
||||
),
|
||||
VLANGroup(
|
||||
name='VLAN Group 2',
|
||||
slug='vlan-group-2',
|
||||
vid_ranges=[NumericRange(1, 11), NumericRange(200, 300)],
|
||||
scope=sitegroup,
|
||||
description='foobar2'
|
||||
description='foobar2',
|
||||
tenant=tenants[1]
|
||||
),
|
||||
VLANGroup(
|
||||
name='VLAN Group 3',
|
||||
slug='vlan-group-3',
|
||||
vid_ranges=[NumericRange(1, 11), NumericRange(300, 400)],
|
||||
scope=site,
|
||||
description='foobar3'
|
||||
description='foobar3',
|
||||
tenant=tenants[1]
|
||||
),
|
||||
VLANGroup(
|
||||
name='VLAN Group 4',
|
||||
@ -1671,6 +1689,20 @@ class VLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
params = {'cluster': Cluster.objects.first().pk}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
||||
|
||||
def test_tenant(self):
|
||||
tenants = Tenant.objects.all()[:2]
|
||||
params = {'tenant_id': [tenants[0].pk, tenants[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
params = {'tenant': [tenants[0].slug, tenants[1].slug]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
def test_tenant_group(self):
|
||||
tenant_groups = TenantGroup.objects.all()[:2]
|
||||
params = {'tenant_group_id': [tenant_groups[0].pk, tenant_groups[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
params = {'tenant_group': [tenant_groups[0].slug, tenant_groups[1].slug]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
|
||||
class VLANTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
queryset = VLAN.objects.all()
|
||||
|
@ -46,6 +46,15 @@
|
||||
<th scope="row">Utilization</th>
|
||||
<td>{% utilization_graph object.utilization %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Tenant" %}</th>
|
||||
<td>
|
||||
{% if object.tenant.group %}
|
||||
{{ object.tenant.group|linkify }} /
|
||||
{% endif %}
|
||||
{{ object.tenant|linkify|placeholder }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
|
Loading…
Reference in New Issue
Block a user