From a45b6b170dc471fd73a532b4ca023a71cc7eb4d2 Mon Sep 17 00:00:00 2001 From: Martin Hauser Date: Thu, 22 Jan 2026 20:41:40 +0100 Subject: [PATCH] feat(dcim): Show peer connections for LAG members Add `InterfaceLAGMemberTable` for the LAG Members panel on LAG interface detail views. The table includes the parent device, member interface/type, and a peer column which renders connected endpoints (including the peer LAG when present). Fixes #19869 --- netbox/dcim/tables/devices.py | 28 +++++++++++++++++++++++ netbox/dcim/tables/template_code.py | 18 +++++++++++++++ netbox/dcim/views.py | 9 ++++++++ netbox/templates/dcim/interface.html | 34 ++++++---------------------- 4 files changed, 62 insertions(+), 27 deletions(-) diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index f01a3ed2f..859c5d573 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -27,6 +27,7 @@ __all__ = ( 'DeviceTable', 'FrontPortTable', 'InterfaceTable', + 'InterfaceLAGMemberTable', 'InventoryItemRoleTable', 'InventoryItemTable', 'MACAddressTable', @@ -689,6 +690,33 @@ class InterfaceTable(BaseInterfaceTable, ModularDeviceComponentTable, PathEndpoi default_columns = ('pk', 'name', 'device', 'label', 'enabled', 'type', 'description') +class InterfaceLAGMemberTable(PathEndpointTable, NetBoxTable): + parent = tables.Column( + verbose_name=_('Parent'), + accessor=Accessor('device'), + linkify=True, + ) + name = tables.Column( + verbose_name=_('Name'), + linkify=True, + order_by=('_name',), + ) + connection = columns.TemplateColumn( + accessor='connected_endpoints', + template_code=INTERFACE_LAG_MEMBERS_LINKTERMINATION, + verbose_name=_('Peer'), + orderable=False, + ) + tags = columns.TagColumn( + url_name='dcim:interface_list' + ) + + class Meta(NetBoxTable.Meta): + model = models.Interface + fields = ('pk', 'parent', 'name', 'type', 'connection') + default_columns = ('pk', 'parent', 'name', 'type', 'connection') + + class DeviceInterfaceTable(InterfaceTable): name = tables.TemplateColumn( verbose_name=_('Name'), diff --git a/netbox/dcim/tables/template_code.py b/netbox/dcim/tables/template_code.py index 356f76750..3675a18cc 100644 --- a/netbox/dcim/tables/template_code.py +++ b/netbox/dcim/tables/template_code.py @@ -24,6 +24,24 @@ INTERFACE_LINKTERMINATION = """ {% else %}""" + LINKTERMINATION + """{% endif %} """ +INTERFACE_LAG_MEMBERS_LINKTERMINATION = """ +{% for termination in value %} + {% if termination.parent_object %} + {{ termination.parent_object }} + + {% endif %} + {{ termination }} + {% if termination.lag %} + + {{ termination.lag }} + (LAG) + {% endif %} + {% if not forloop.last %}
{% endif %} +{% empty %} + {{ ''|placeholder }} +{% endfor %} +""" + CABLE_LENGTH = """ {% load helpers %} {% if record.length %}{{ record.length|floatformat:"-2" }} {{ record.length_unit }}{% endif %} diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index d12bd9f1c..35f3b90c1 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -3135,6 +3135,14 @@ class InterfaceView(generic.ObjectView): ) child_interfaces_table.configure(request) + # Get LAG interfaces + lag_interfaces = Interface.objects.restrict(request.user, 'view').filter(lag=instance) + lag_interfaces_table = tables.InterfaceLAGMemberTable( + lag_interfaces, + orderable=False + ) + lag_interfaces_table.configure(request) + # Get assigned VLANs and annotate whether each is tagged or untagged vlans = [] if instance.untagged_vlan is not None: @@ -3164,6 +3172,7 @@ class InterfaceView(generic.ObjectView): 'bridge_interfaces': bridge_interfaces, '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/templates/dcim/interface.html b/netbox/templates/dcim/interface.html index b53d239b4..dc873a89f 100644 --- a/netbox/templates/dcim/interface.html +++ b/netbox/templates/dcim/interface.html @@ -370,33 +370,6 @@ {% endif %} - {% if object.is_lag %} -
-

{% trans "LAG Members" %}

- - - - - - - - - - {% for member in object.member_interfaces.all %} - - - - - - {% empty %} - - - - {% endfor %} - -
{% trans "Parent" %}{% trans "Interface" %}{% trans "Type" %}
{{ member.device|linkify }}{{ member|linkify }}{{ member.get_type_display }}
{% trans "No member interfaces" %}
-
- {% endif %} {% include 'ipam/inc/panels/fhrp_groups.html' %} {% include 'dcim/inc/panels/inventory_items.html' %} {% plugin_right_page object %} @@ -441,6 +414,13 @@ {% include 'inc/panel_table.html' with table=vlan_table heading="VLANs" %} + {% if object.is_lag %} +
+
+ {% include 'inc/panel_table.html' with table=lag_interfaces_table heading="LAG Members" %} +
+
+ {% endif %} {% if object.vlan_translation_policy %}