diff --git a/netbox/dcim/forms/bulk_edit.py b/netbox/dcim/forms/bulk_edit.py
index a2fa28eaf..8577d93b3 100644
--- a/netbox/dcim/forms/bulk_edit.py
+++ b/netbox/dcim/forms/bulk_edit.py
@@ -1393,9 +1393,9 @@ class PowerOutletBulkEditForm(
class InterfaceBulkEditForm(
ComponentBulkEditForm,
form_from_model(Interface, [
- 'label', 'type', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'wwn', 'mtu', 'mgmt_only',
- 'mark_connected', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width',
- 'tx_power', 'wireless_lans'
+ 'label', 'type', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'wwn', 'mtu', 'mgmt_only', 'mark_connected',
+ 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
+ 'wireless_lans'
])
):
enabled = forms.NullBooleanField(
@@ -1518,9 +1518,9 @@ class InterfaceBulkEditForm(
),
)
nullable_fields = (
- 'module', 'label', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'mac_address', 'wwn', 'vdcs', 'mtu',
- 'description', 'poe_mode', 'poe_type', 'mode', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width',
- 'tx_power', 'untagged_vlan', 'tagged_vlans', 'vrf', 'wireless_lans'
+ 'module', 'label', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'wwn', 'vdcs', 'mtu', 'description',
+ 'poe_mode', 'poe_type', 'mode', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
+ 'untagged_vlan', 'tagged_vlans', 'vrf', 'wireless_lans'
)
def __init__(self, *args, **kwargs):
diff --git a/netbox/dcim/forms/common.py b/netbox/dcim/forms/common.py
index bae7fd222..100898f90 100644
--- a/netbox/dcim/forms/common.py
+++ b/netbox/dcim/forms/common.py
@@ -12,11 +12,6 @@ __all__ = (
class InterfaceCommonForm(forms.Form):
- mac_address = forms.CharField(
- empty_value=None,
- required=False,
- label=_('MAC address')
- )
mtu = forms.IntegerField(
required=False,
min_value=INTERFACE_MTU_MIN,
diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py
index f62a02921..d49b141a5 100644
--- a/netbox/dcim/forms/model_forms.py
+++ b/netbox/dcim/forms/model_forms.py
@@ -1405,7 +1405,7 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm):
FieldSet(
'device', 'module', 'name', 'label', 'type', 'speed', 'duplex', 'description', 'tags', name=_('Interface')
),
- FieldSet('vrf', 'mac_address', 'wwn', name=_('Addressing')),
+ FieldSet('vrf', 'wwn', name=_('Addressing')),
FieldSet('vdcs', 'mtu', 'tx_power', 'enabled', 'mgmt_only', 'mark_connected', name=_('Operation')),
FieldSet('parent', 'bridge', 'lag', name=_('Related Interfaces')),
FieldSet('poe_mode', 'poe_type', name=_('PoE')),
@@ -1422,8 +1422,8 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm):
class Meta:
model = Interface
fields = [
- 'device', 'module', 'vdcs', 'name', 'label', 'type', 'speed', 'duplex', 'enabled', 'parent', 'bridge', 'lag',
- 'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'poe_mode', 'poe_type', 'mode',
+ 'device', 'module', 'vdcs', 'name', 'label', 'type', 'speed', 'duplex', 'enabled', 'parent', 'bridge',
+ 'lag', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'poe_mode', 'poe_type', 'mode',
'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'wireless_lans',
'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vlan_translation_policy', 'vrf', 'tags',
]
@@ -1437,21 +1437,6 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm):
'mode': '802.1Q Mode',
}
- def clean_mac_address(self):
- if self.cleaned_data['mac_address'] and (
- not self.instance.pk or
- not MACAddress.objects.filter(mac_address=self.cleaned_data['mac_address'], interface=self.instance).exists()
- ):
- mac_address = MACAddress.objects.create(mac_address=self.cleaned_data['mac_address'])
- return mac_address
- return None
-
- def save(self, commit=True):
- result = super().save(commit=commit)
- if self.cleaned_data['mac_address']:
- self.instance.mac_addresses.add(self.cleaned_data['mac_address'])
- return result
-
class FrontPortForm(ModularDeviceComponentForm):
rear_port = DynamicModelChoiceField(
diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py
index 2a2186d27..ff374644f 100644
--- a/netbox/dcim/models/devices.py
+++ b/netbox/dcim/models/devices.py
@@ -1500,7 +1500,7 @@ class MACAddress(PrimaryModel):
fk_field='assigned_object_id'
)
is_primary = models.BooleanField(
- verbose_name=_('is primary for interface'),
+ verbose_name=_('is primary'),
default=True
)
diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py
index 530d6c25b..70bd8242e 100644
--- a/netbox/dcim/tables/devices.py
+++ b/netbox/dcim/tables/devices.py
@@ -1128,7 +1128,7 @@ class MACAddressTable(NetBoxTable):
verbose_name=_('Parent')
)
is_primary = columns.BooleanColumn(
- verbose_name=_('Primary for Interface'),
+ verbose_name=_('Primary'),
false_mark=None
)
tags = columns.TagColumn(
@@ -1141,6 +1141,7 @@ class MACAddressTable(NetBoxTable):
class Meta(DeviceComponentTable.Meta):
model = models.MACAddress
fields = (
- 'pk', 'id', 'mac_address', 'assigned_object', 'created', 'last_updated', 'is_primary',
+ 'pk', 'id', 'mac_address', 'assigned_object_parent', 'assigned_object', 'is_primary', 'created',
+ 'last_updated',
)
- default_columns = ('pk', 'mac_address', 'assigned_object', 'is_primary')
+ default_columns = ('pk', 'mac_address', 'assigned_object_parent', 'assigned_object', 'is_primary')
diff --git a/netbox/dcim/tables/template_code.py b/netbox/dcim/tables/template_code.py
index 96ab803e6..dc9724e3c 100644
--- a/netbox/dcim/tables/template_code.py
+++ b/netbox/dcim/tables/template_code.py
@@ -314,6 +314,9 @@ INTERFACE_BUTTONS = """
{% if perms.ipam.add_ipaddress %}
IP Address
{% endif %}
+ {% if perms.dcim.add_macaddress %}
+ MAC Address
+ {% endif %}
{% if perms.dcim.add_inventoryitem %}
Inventory Item
{% endif %}
diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py
index 7b25d6d78..9850081d1 100644
--- a/netbox/dcim/tests/test_views.py
+++ b/netbox/dcim/tests/test_views.py
@@ -2508,7 +2508,6 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
'enabled': False,
'bridge': interfaces[4].pk,
'lag': interfaces[3].pk,
- 'mac_address': EUI('01:02:03:04:05:06'),
'wwn': EUI('01:02:03:04:05:06:07:08', version=64),
'mtu': 65000,
'speed': 1000000,
@@ -2533,7 +2532,6 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
'enabled': False,
'bridge': interfaces[4].pk,
'lag': interfaces[3].pk,
- 'mac_address': EUI('01:02:03:04:05:06'),
'wwn': EUI('01:02:03:04:05:06:07:08', version=64),
'mtu': 2000,
'speed': 100000,
diff --git a/netbox/templates/dcim/interface.html b/netbox/templates/dcim/interface.html
index 476ee7ee7..f364b12bc 100644
--- a/netbox/templates/dcim/interface.html
+++ b/netbox/templates/dcim/interface.html
@@ -123,11 +123,24 @@
{% trans "MAC Address" %} |
- {{ object.mac_address|placeholder }} |
+
+ {% if object.mac_address %}
+ {{ object.mac_address }}
+ {% trans "Primary" %}
+ {% else %}
+ {{ ''|placeholder }}
+ {% endif %}
+ |
{% trans "WWN" %} |
- {{ object.wwn|placeholder }} |
+
+ {% if object.wwn %}
+ {{ object.wwn }}
+ {% else %}
+ {{ ''|placeholder }}
+ {% endif %}
+ |
{% trans "VRF" %} |
diff --git a/netbox/templates/dcim/macaddress.html b/netbox/templates/dcim/macaddress.html
index 12b332121..6d7532e6d 100644
--- a/netbox/templates/dcim/macaddress.html
+++ b/netbox/templates/dcim/macaddress.html
@@ -6,7 +6,7 @@
{% block content %}
-
+
@@ -33,7 +33,7 @@
- {% trans "Primary MAC address for Interface" %} |
+ {% trans "Primary for interface" %} |
{% checkmark object.is_primary %} |
@@ -42,7 +42,7 @@
{% include 'inc/panels/custom_fields.html' %}
{% plugin_left_page object %}
-
+
{% include 'inc/panels/comments.html' %}
{% plugin_right_page object %}
diff --git a/netbox/templates/virtualization/vminterface.html b/netbox/templates/virtualization/vminterface.html
index 54a2cb982..1709bcb5f 100644
--- a/netbox/templates/virtualization/vminterface.html
+++ b/netbox/templates/virtualization/vminterface.html
@@ -56,8 +56,15 @@
{{ object.mtu|placeholder }} |
- {% trans "MAC Address" %} |
- {{ object.mac_address|placeholder }} |
+ {% trans "MAC Address" %} |
+
+ {% if object.mac_address %}
+ {{ object.mac_address }}
+ {% trans "Primary" %}
+ {% else %}
+ {{ ''|placeholder }}
+ {% endif %}
+ |
{% trans "802.1Q Mode" %} |
diff --git a/netbox/virtualization/forms/model_forms.py b/netbox/virtualization/forms/model_forms.py
index 458ae6a99..eb1630d63 100644
--- a/netbox/virtualization/forms/model_forms.py
+++ b/netbox/virtualization/forms/model_forms.py
@@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _
from dcim.forms.common import InterfaceCommonForm
from dcim.forms.mixins import ScopedForm
-from dcim.models import Device, DeviceRole, MACAddress, Platform, Rack, Region, Site, SiteGroup
+from dcim.models import Device, DeviceRole, Platform, Rack, Region, Site, SiteGroup
from extras.models import ConfigTemplate
from ipam.choices import VLANQinQRoleChoices
from ipam.models import IPAddress, VLAN, VLANGroup, VLANTranslationPolicy, VRF
@@ -358,7 +358,7 @@ class VMInterfaceForm(InterfaceCommonForm, VMComponentForm):
fieldsets = (
FieldSet('virtual_machine', 'name', 'description', 'tags', name=_('Interface')),
- FieldSet('vrf', 'mac_address', name=_('Addressing')),
+ FieldSet('vrf', name=_('Addressing')),
FieldSet('mtu', 'enabled', name=_('Operation')),
FieldSet('parent', 'bridge', name=_('Related Interfaces')),
FieldSet(
@@ -370,8 +370,8 @@ class VMInterfaceForm(InterfaceCommonForm, VMComponentForm):
class Meta:
model = VMInterface
fields = [
- 'virtual_machine', 'name', 'parent', 'bridge', 'enabled', 'mac_address', 'mtu', 'description', 'mode',
- 'vlan_group', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vlan_translation_policy', 'vrf', 'tags',
+ 'virtual_machine', 'name', 'parent', 'bridge', 'enabled', 'mtu', 'description', 'mode', 'vlan_group',
+ 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vlan_translation_policy', 'vrf', 'tags',
]
labels = {
'mode': _('802.1Q Mode'),
@@ -380,14 +380,6 @@ class VMInterfaceForm(InterfaceCommonForm, VMComponentForm):
'mode': HTMXSelect(),
}
- def clean_mac_address(self):
- return MACAddress.objects.create(mac_address=self.cleaned_data['mac_address'])
-
- def save(self, commit=True):
- result = super().save(commit=commit)
- self.instance.mac_addresses.add(self.cleaned_data['mac_address'])
- return result
-
class VirtualDiskForm(VMComponentForm):
diff --git a/netbox/virtualization/tables/virtualmachines.py b/netbox/virtualization/tables/virtualmachines.py
index 26d32f8cf..ad41d5c13 100644
--- a/netbox/virtualization/tables/virtualmachines.py
+++ b/netbox/virtualization/tables/virtualmachines.py
@@ -25,6 +25,9 @@ VMINTERFACE_BUTTONS = """
{% if perms.ipam.add_ipaddress %}
IP Address
{% endif %}
+ {% if perms.dcim.add_macaddress %}
+ MAC Address
+ {% endif %}
{% if perms.vpn.add_l2vpntermination %}
L2VPN Termination
{% endif %}
diff --git a/netbox/virtualization/tests/test_views.py b/netbox/virtualization/tests/test_views.py
index b9cb7b437..dfd7e041c 100644
--- a/netbox/virtualization/tests/test_views.py
+++ b/netbox/virtualization/tests/test_views.py
@@ -1,7 +1,6 @@
from django.contrib.contenttypes.models import ContentType
from django.test import override_settings
from django.urls import reverse
-from netaddr import EUI
from dcim.choices import InterfaceModeChoices
from dcim.models import DeviceRole, Platform, Site
@@ -331,7 +330,6 @@ class VMInterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
'name': 'Interface X',
'enabled': False,
'bridge': interfaces[1].pk,
- 'mac_address': EUI('01-02-03-04-05-06'),
'mtu': 65000,
'description': 'New description',
'mode': InterfaceModeChoices.MODE_TAGGED,
@@ -346,7 +344,6 @@ class VMInterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
'name': 'Interface [4-6]',
'enabled': False,
'bridge': interfaces[3].pk,
- 'mac_address': EUI('01-02-03-04-05-06'),
'mtu': 2000,
'description': 'New description',
'mode': InterfaceModeChoices.MODE_TAGGED,