From c0ea63da5fdd195e865aa0e2d543bdf9dcf8c28c Mon Sep 17 00:00:00 2001 From: Brian Tiemann Date: Wed, 28 Jan 2026 20:08:47 -0500 Subject: [PATCH] Simplify Multiple MAC addresses with additional selectable column for tables in list view and detail view --- netbox/dcim/models/device_components.py | 19 ---------- netbox/dcim/tables/devices.py | 38 +++++++++++-------- netbox/dcim/tables/template_code.py | 10 +++++ netbox/templates/dcim/interface.html | 8 ++-- .../templates/virtualization/vminterface.html | 8 ++-- 5 files changed, 39 insertions(+), 44 deletions(-) diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 7f21b95f2..a16daa1e5 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -732,25 +732,6 @@ class BaseInterface(models.Model): if self.primary_mac_address: return self.primary_mac_address.mac_address - @property - def mac_address_display(self): - """ - Rich representation of MAC addresses for use in table columns (e.g. InterfaceTable). - Handles various configurations of MAC addresses for an interface: - 11:22:33:44:55:66 <-- Single MAC address on interface, assigned as primary - 11:22:33:44:55:66 (2) <-- Multiple MAC addresses on interface, one assigned as primary - 2 available <-- 1 or more MAC addresses on interface, none assigned as primary - - <-- No MAC addresses on interface - """ - available_mac_count = self.mac_addresses.count() - if self.primary_mac_address: - if available_mac_count > 1: - return f"{self.primary_mac_address} ({available_mac_count})" - return self.primary_mac_address - if available_mac_count: - return f"{available_mac_count} available" - return None - class Interface( InterfaceValidationMixin, diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index 81b3ace20..fe34896ce 100644 --- a/netbox/dcim/tables/devices.py +++ b/netbox/dcim/tables/devices.py @@ -583,6 +583,15 @@ class BaseInterfaceTable(NetBoxTable): orderable=False, verbose_name=_('IP Addresses') ) + primary_mac_address = tables.Column( + verbose_name=_('MAC Address'), + linkify=True + ) + mac_addresses = tables.TemplateColumn( + template_code=INTERFACE_MACADDRESSES, + orderable=False, + verbose_name=_('MAC Addresses') + ) fhrp_groups = tables.TemplateColumn( accessor=Accessor('fhrp_group_assignments'), template_code=INTERFACE_FHRPGROUPS, @@ -614,11 +623,6 @@ class BaseInterfaceTable(NetBoxTable): verbose_name=_('Q-in-Q SVLAN'), linkify=True ) - primary_mac_address = tables.Column( - verbose_name=_('MAC Address'), - accessor=Accessor('mac_address_display'), - linkify=True - ) def value_ip_addresses(self, value): return ",".join([str(obj.address) for obj in value.all()]) @@ -681,11 +685,12 @@ class InterfaceTable(BaseInterfaceTable, ModularDeviceComponentTable, PathEndpoi model = models.Interface fields = ( 'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'enabled', 'type', 'mgmt_only', 'mtu', - 'speed', 'speed_formatted', 'duplex', 'mode', 'primary_mac_address', 'wwn', 'poe_mode', 'poe_type', - 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'description', - 'mark_connected', 'cable', 'cable_color', 'wireless_link', 'wireless_lans', 'link_peer', 'connection', - 'tags', 'vdcs', 'vrf', 'l2vpn', 'tunnel', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', - 'qinq_svlan', 'inventory_items', 'created', 'last_updated', 'vlan_translation_policy' + 'speed', 'speed_formatted', 'duplex', 'mode', 'mac_addresses', 'primary_mac_address', 'wwn', + 'poe_mode', 'poe_type', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', + 'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link', 'wireless_lans', 'link_peer', + 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn', 'tunnel', 'ip_addresses', 'fhrp_groups', + 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'inventory_items', 'created', 'last_updated', + 'vlan_translation_policy', ) default_columns = ('pk', 'name', 'device', 'label', 'enabled', 'type', 'description') @@ -719,10 +724,11 @@ class DeviceInterfaceTable(InterfaceTable): model = models.Interface fields = ( 'pk', 'id', 'name', 'module_bay', 'module', 'label', 'enabled', 'type', 'parent', 'bridge', 'lag', - 'mgmt_only', 'mtu', 'mode', 'primary_mac_address', 'wwn', 'rf_role', 'rf_channel', 'rf_channel_frequency', - 'rf_channel_width', 'tx_power', 'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link', - 'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', 'l2vpn', 'tunnel', 'ip_addresses', - 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'actions', + 'mgmt_only', 'mtu', 'mode', 'mac_addresses', 'primary_mac_address', 'wwn', 'rf_role', 'rf_channel', + 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'description', 'mark_connected', 'cable', + 'cable_color', 'wireless_link', 'wireless_lans', 'link_peer', 'connection', 'tags', 'vdcs', 'vrf', + 'l2vpn', 'tunnel', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', + 'actions', ) default_columns = ( 'pk', 'name', 'label', 'enabled', 'type', 'parent', 'lag', 'mtu', 'mode', 'description', 'ip_addresses', @@ -1172,4 +1178,6 @@ class MACAddressTable(PrimaryModelTable): 'pk', 'id', 'mac_address', 'assigned_object_parent', 'assigned_object', 'description', 'is_primary', 'comments', 'tags', 'created', 'last_updated', ) - default_columns = ('pk', 'mac_address', 'assigned_object_parent', 'assigned_object', 'description') + default_columns = ( + 'pk', 'mac_address', 'is_primary', 'assigned_object_parent', 'assigned_object', 'description', + ) diff --git a/netbox/dcim/tables/template_code.py b/netbox/dcim/tables/template_code.py index 356f76750..cfd0f63c5 100644 --- a/netbox/dcim/tables/template_code.py +++ b/netbox/dcim/tables/template_code.py @@ -62,6 +62,16 @@ INTERFACE_IPADDRESSES = """ {% endif %} """ +INTERFACE_MACADDRESSES = """ + {% if value.count > 3 %} + {{ value.count }} + {% else %} + {% for mac in value.all %} + {{ mac }} + {% endfor %} + {% endif %} +""" + INTERFACE_FHRPGROUPS = """ {% for assignment in value.all %} {{ assignment.group }} diff --git a/netbox/templates/dcim/interface.html b/netbox/templates/dcim/interface.html index aa5b6e603..b53d239b4 100644 --- a/netbox/templates/dcim/interface.html +++ b/netbox/templates/dcim/interface.html @@ -143,11 +143,9 @@ {% trans "MAC Address" %} - {% if object.mac_address_display %} - {{ object.mac_address_display|linkify }} - {% if object.primary_mac_address %} - {% trans "Primary" %} - {% endif %} + {% if object.primary_mac_address %} + {{ object.primary_mac_address|linkify }} + {% trans "Primary" %} {% else %} {{ ''|placeholder }} {% endif %} diff --git a/netbox/templates/virtualization/vminterface.html b/netbox/templates/virtualization/vminterface.html index fa7a90567..b8ae28c5d 100644 --- a/netbox/templates/virtualization/vminterface.html +++ b/netbox/templates/virtualization/vminterface.html @@ -78,11 +78,9 @@ {% trans "MAC Address" %} - {% if object.mac_address_display %} - {{ object.mac_address_display|linkify }} - {% if object.primary_mac_address %} - {% trans "Primary" %} - {% endif %} + {% if object.primary_mac_address %} + {{ object.primary_mac_address|linkify }} + {% trans "Primary" %} {% else %} {{ ''|placeholder }} {% endif %}