From 44abeeff5a94e6f87671e4b58ed01ce323904cf6 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 5 Mar 2026 16:35:31 -0500 Subject: [PATCH] Fixes #21012: Ensure all tagged VLANs assigned to an interface are listed under the interface detail UI view --- netbox/dcim/views.py | 18 +---------- netbox/ipam/tables/vlans.py | 45 +--------------------------- netbox/templates/dcim/interface.html | 5 +++- netbox/virtualization/views.py | 24 ++++----------- 4 files changed, 12 insertions(+), 80 deletions(-) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 6d7e0e99e..c90f63753 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -16,7 +16,7 @@ from circuits.models import Circuit, CircuitTermination from extras.ui.panels import CustomFieldsPanel, ImageAttachmentsPanel, TagsPanel from extras.views import ObjectConfigContextView, ObjectRenderConfigView from ipam.models import ASN, VLAN, IPAddress, Prefix, VLANGroup -from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable +from ipam.tables import VLANTranslationRuleTable from netbox.object_actions import * from netbox.ui import actions, layout from netbox.ui.panels import ( @@ -3230,21 +3230,6 @@ class InterfaceView(generic.ObjectView): ) lag_interfaces_table.configure(request) - # Get assigned VLANs and annotate whether each is tagged or untagged - vlans = [] - if instance.untagged_vlan is not None: - vlans.append(instance.untagged_vlan) - vlans[0].tagged = False - for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'): - vlan.tagged = True - vlans.append(vlan) - vlan_table = InterfaceVLANTable( - interface=instance, - data=vlans, - orderable=False - ) - vlan_table.configure(request) - # Get VLAN translation rules vlan_translation_table = None if instance.vlan_translation_policy: @@ -3260,7 +3245,6 @@ class InterfaceView(generic.ObjectView): 'bridge_interfaces_table': bridge_interfaces_table, 'child_interfaces_table': child_interfaces_table, 'lag_interfaces_table': lag_interfaces_table, - 'vlan_table': vlan_table, 'vlan_translation_table': vlan_translation_table, } diff --git a/netbox/ipam/tables/vlans.py b/netbox/ipam/tables/vlans.py index c7c75f91e..2082ea616 100644 --- a/netbox/ipam/tables/vlans.py +++ b/netbox/ipam/tables/vlans.py @@ -1,19 +1,17 @@ import django_tables2 as tables from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy as _ -from django_tables2.utils import Accessor from dcim.models import Interface from dcim.tables.template_code import INTERFACE_LINKTERMINATION, LINKTERMINATION from ipam.models import * from netbox.tables import NetBoxTable, OrganizationalModelTable, PrimaryModelTable, columns -from tenancy.tables import TenancyColumnsMixin, TenantColumn +from tenancy.tables import TenancyColumnsMixin from virtualization.models import VMInterface from .template_code import * __all__ = ( - 'InterfaceVLANTable', 'VLANDevicesTable', 'VLANGroupTable', 'VLANMembersTable', @@ -198,47 +196,6 @@ class VLANVirtualMachinesTable(VLANMembersTable): exclude = ('id', ) -class InterfaceVLANTable(NetBoxTable): - """ - List VLANs assigned to a specific Interface. - """ - vid = tables.Column( - linkify=True, - verbose_name=_('VID') - ) - tagged = columns.BooleanColumn( - verbose_name=_('Tagged'), - false_mark=None - ) - site = tables.Column( - verbose_name=_('Site'), - linkify=True - ) - group = tables.Column( - accessor=Accessor('group__name'), - verbose_name=_('Group') - ) - tenant = TenantColumn( - verbose_name=_('Tenant'), - ) - status = columns.ChoiceFieldColumn( - verbose_name=_('Status'), - ) - role = tables.Column( - verbose_name=_('Role'), - linkify=True - ) - - class Meta(NetBoxTable.Meta): - model = VLAN - fields = ('vid', 'tagged', 'site', 'group', 'name', 'tenant', 'status', 'role', 'description') - exclude = ('id', ) - - def __init__(self, interface, *args, **kwargs): - self.interface = interface - super().__init__(*args, **kwargs) - - # # VLAN Translation # diff --git a/netbox/templates/dcim/interface.html b/netbox/templates/dcim/interface.html index dc873a89f..50bc35f59 100644 --- a/netbox/templates/dcim/interface.html +++ b/netbox/templates/dcim/interface.html @@ -411,7 +411,10 @@
- {% include 'inc/panel_table.html' with table=vlan_table heading="VLANs" %} +
+

{% trans "VLANs" %}

+ {% htmx_table 'ipam:vlan_list' interface_id=object.pk %} +
{% if object.is_lag %} diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index 1fca15c5e..c17f727ad 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -13,7 +13,7 @@ from dcim.tables import DeviceTable from extras.ui.panels import CustomFieldsPanel, ImageAttachmentsPanel, TagsPanel from extras.views import ObjectConfigContextView, ObjectRenderConfigView from ipam.models import IPAddress, VLANGroup -from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable +from ipam.tables import VLANTranslationRuleTable from ipam.ui.panels import FHRPGroupAssignmentsPanel from netbox.object_actions import ( AddObject, @@ -594,7 +594,11 @@ class VMInterfaceView(generic.ObjectView): ), ], ), - ContextTablePanel('vlan_table', title=_('Assigned VLANs')), + ObjectsTablePanel( + model='ipam.VLAN', + title=_('Assigned VLANs'), + filters={'vminterface_id': lambda ctx: ctx['object'].pk}, + ), ContextTablePanel('vlan_translation_table', title=_('VLAN Translation')), ContextTablePanel('child_interfaces_table', title=_('Child Interfaces')), ], @@ -620,24 +624,8 @@ class VMInterfaceView(generic.ObjectView): ) vlan_translation_table.configure(request) - # Get assigned VLANs and annotate whether each is tagged or untagged - vlans = [] - if instance.untagged_vlan is not None: - vlans.append(instance.untagged_vlan) - vlans[0].tagged = False - for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'): - vlan.tagged = True - vlans.append(vlan) - vlan_table = InterfaceVLANTable( - interface=instance, - data=vlans, - orderable=False - ) - vlan_table.configure(request) - return { 'child_interfaces_table': child_interfaces_tables, - 'vlan_table': vlan_table, 'vlan_translation_table': vlan_translation_table, }