Remove mac_address field from interface forms

This commit is contained in:
Jeremy Stretch 2024-11-15 14:11:11 -05:00
parent e4530bc91f
commit a0db3b0719
13 changed files with 51 additions and 57 deletions

View File

@ -1393,9 +1393,9 @@ class PowerOutletBulkEditForm(
class InterfaceBulkEditForm( class InterfaceBulkEditForm(
ComponentBulkEditForm, ComponentBulkEditForm,
form_from_model(Interface, [ form_from_model(Interface, [
'label', 'type', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'wwn', 'mtu', 'mgmt_only', 'label', 'type', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'wwn', 'mtu', 'mgmt_only', 'mark_connected',
'mark_connected', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
'tx_power', 'wireless_lans' 'wireless_lans'
]) ])
): ):
enabled = forms.NullBooleanField( enabled = forms.NullBooleanField(
@ -1518,9 +1518,9 @@ class InterfaceBulkEditForm(
), ),
) )
nullable_fields = ( nullable_fields = (
'module', 'label', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'mac_address', 'wwn', 'vdcs', 'mtu', 'module', 'label', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'wwn', 'vdcs', 'mtu', 'description',
'description', 'poe_mode', 'poe_type', 'mode', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'poe_mode', 'poe_type', 'mode', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
'tx_power', 'untagged_vlan', 'tagged_vlans', 'vrf', 'wireless_lans' 'untagged_vlan', 'tagged_vlans', 'vrf', 'wireless_lans'
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):

View File

@ -12,11 +12,6 @@ __all__ = (
class InterfaceCommonForm(forms.Form): class InterfaceCommonForm(forms.Form):
mac_address = forms.CharField(
empty_value=None,
required=False,
label=_('MAC address')
)
mtu = forms.IntegerField( mtu = forms.IntegerField(
required=False, required=False,
min_value=INTERFACE_MTU_MIN, min_value=INTERFACE_MTU_MIN,

View File

@ -1405,7 +1405,7 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm):
FieldSet( FieldSet(
'device', 'module', 'name', 'label', 'type', 'speed', 'duplex', 'description', 'tags', name=_('Interface') '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('vdcs', 'mtu', 'tx_power', 'enabled', 'mgmt_only', 'mark_connected', name=_('Operation')),
FieldSet('parent', 'bridge', 'lag', name=_('Related Interfaces')), FieldSet('parent', 'bridge', 'lag', name=_('Related Interfaces')),
FieldSet('poe_mode', 'poe_type', name=_('PoE')), FieldSet('poe_mode', 'poe_type', name=_('PoE')),
@ -1422,8 +1422,8 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm):
class Meta: class Meta:
model = Interface model = Interface
fields = [ fields = [
'device', 'module', 'vdcs', 'name', 'label', 'type', 'speed', 'duplex', 'enabled', 'parent', 'bridge', 'lag', 'device', 'module', 'vdcs', 'name', 'label', 'type', 'speed', 'duplex', 'enabled', 'parent', 'bridge',
'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'poe_mode', 'poe_type', 'mode', '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', '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', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vlan_translation_policy', 'vrf', 'tags',
] ]
@ -1437,21 +1437,6 @@ class InterfaceForm(InterfaceCommonForm, ModularDeviceComponentForm):
'mode': '802.1Q Mode', '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): class FrontPortForm(ModularDeviceComponentForm):
rear_port = DynamicModelChoiceField( rear_port = DynamicModelChoiceField(

View File

@ -1500,7 +1500,7 @@ class MACAddress(PrimaryModel):
fk_field='assigned_object_id' fk_field='assigned_object_id'
) )
is_primary = models.BooleanField( is_primary = models.BooleanField(
verbose_name=_('is primary for interface'), verbose_name=_('is primary'),
default=True default=True
) )

View File

@ -1128,7 +1128,7 @@ class MACAddressTable(NetBoxTable):
verbose_name=_('Parent') verbose_name=_('Parent')
) )
is_primary = columns.BooleanColumn( is_primary = columns.BooleanColumn(
verbose_name=_('Primary for Interface'), verbose_name=_('Primary'),
false_mark=None false_mark=None
) )
tags = columns.TagColumn( tags = columns.TagColumn(
@ -1141,6 +1141,7 @@ class MACAddressTable(NetBoxTable):
class Meta(DeviceComponentTable.Meta): class Meta(DeviceComponentTable.Meta):
model = models.MACAddress model = models.MACAddress
fields = ( 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')

View File

@ -314,6 +314,9 @@ INTERFACE_BUTTONS = """
{% if perms.ipam.add_ipaddress %} {% if perms.ipam.add_ipaddress %}
<li><a class="dropdown-item" href="{% url 'ipam:ipaddress_add' %}?interface={{ record.pk }}&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">IP Address</a></li> <li><a class="dropdown-item" href="{% url 'ipam:ipaddress_add' %}?interface={{ record.pk }}&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">IP Address</a></li>
{% endif %} {% endif %}
{% if perms.dcim.add_macaddress %}
<li><a class="dropdown-item" href="{% url 'dcim:macaddress_add' %}?interface={{ record.pk }}&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">MAC Address</a></li>
{% endif %}
{% if perms.dcim.add_inventoryitem %} {% if perms.dcim.add_inventoryitem %}
<li><a class="dropdown-item" href="{% url 'dcim:inventoryitem_add' %}?device={{ record.device_id }}&component_type={{ record|content_type_id }}&component_id={{ record.pk }}&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">Inventory Item</a></li> <li><a class="dropdown-item" href="{% url 'dcim:inventoryitem_add' %}?device={{ record.device_id }}&component_type={{ record|content_type_id }}&component_id={{ record.pk }}&return_url={% url 'dcim:device_interfaces' pk=object.pk %}">Inventory Item</a></li>
{% endif %} {% endif %}

View File

@ -2508,7 +2508,6 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
'enabled': False, 'enabled': False,
'bridge': interfaces[4].pk, 'bridge': interfaces[4].pk,
'lag': interfaces[3].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), 'wwn': EUI('01:02:03:04:05:06:07:08', version=64),
'mtu': 65000, 'mtu': 65000,
'speed': 1000000, 'speed': 1000000,
@ -2533,7 +2532,6 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
'enabled': False, 'enabled': False,
'bridge': interfaces[4].pk, 'bridge': interfaces[4].pk,
'lag': interfaces[3].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), 'wwn': EUI('01:02:03:04:05:06:07:08', version=64),
'mtu': 2000, 'mtu': 2000,
'speed': 100000, 'speed': 100000,

View File

@ -123,11 +123,24 @@
<table class="table table-hover attr-table"> <table class="table table-hover attr-table">
<tr> <tr>
<th scope="row">{% trans "MAC Address" %}</th> <th scope="row">{% trans "MAC Address" %}</th>
<td><span class="font-monospace">{{ object.mac_address|placeholder }}</span></td> <td>
{% if object.mac_address %}
<span class="font-monospace">{{ object.mac_address }}</span>
<span class="badge text-bg-primary">{% trans "Primary" %}</span>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr> </tr>
<tr> <tr>
<th scope="row">{% trans "WWN" %}</th> <th scope="row">{% trans "WWN" %}</th>
<td><span class="font-monospace">{{ object.wwn|placeholder }}</span></td> <td>
{% if object.wwn %}
<span class="font-monospace">{{ object.wwn }}</span>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr> </tr>
<tr> <tr>
<th scope="row">{% trans "VRF" %}</th> <th scope="row">{% trans "VRF" %}</th>

View File

@ -6,7 +6,7 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col col-md-4"> <div class="col col-md-6">
<div class="card"> <div class="card">
<h2 class="card-header">{% trans "MAC Address" %}</h2> <h2 class="card-header">{% trans "MAC Address" %}</h2>
<table class="table table-hover attr-table"> <table class="table table-hover attr-table">
@ -33,7 +33,7 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<th scope="row">{% trans "Primary MAC address for Interface" %}</th> <th scope="row">{% trans "Primary for interface" %}</th>
<td>{% checkmark object.is_primary %}</td> <td>{% checkmark object.is_primary %}</td>
</tr> </tr>
</table> </table>
@ -42,7 +42,7 @@
{% include 'inc/panels/custom_fields.html' %} {% include 'inc/panels/custom_fields.html' %}
{% plugin_left_page object %} {% plugin_left_page object %}
</div> </div>
<div class="col col-md-8"> <div class="col col-md-6">
{% include 'inc/panels/comments.html' %} {% include 'inc/panels/comments.html' %}
{% plugin_right_page object %} {% plugin_right_page object %}
</div> </div>

View File

@ -56,8 +56,15 @@
<td>{{ object.mtu|placeholder }}</td> <td>{{ object.mtu|placeholder }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">{% trans "MAC Address" %}</th> <th scope="row">{% trans "MAC Address" %}</th>
<td><span class="font-monospace">{{ object.mac_address|placeholder }}</span></td> <td>
{% if object.mac_address %}
<span class="font-monospace">{{ object.mac_address }}</span>
<span class="badge text-bg-primary">{% trans "Primary" %}</span>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr> </tr>
<tr> <tr>
<th scope="row">{% trans "802.1Q Mode" %}</th> <th scope="row">{% trans "802.1Q Mode" %}</th>

View File

@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _
from dcim.forms.common import InterfaceCommonForm from dcim.forms.common import InterfaceCommonForm
from dcim.forms.mixins import ScopedForm 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 extras.models import ConfigTemplate
from ipam.choices import VLANQinQRoleChoices from ipam.choices import VLANQinQRoleChoices
from ipam.models import IPAddress, VLAN, VLANGroup, VLANTranslationPolicy, VRF from ipam.models import IPAddress, VLAN, VLANGroup, VLANTranslationPolicy, VRF
@ -358,7 +358,7 @@ class VMInterfaceForm(InterfaceCommonForm, VMComponentForm):
fieldsets = ( fieldsets = (
FieldSet('virtual_machine', 'name', 'description', 'tags', name=_('Interface')), FieldSet('virtual_machine', 'name', 'description', 'tags', name=_('Interface')),
FieldSet('vrf', 'mac_address', name=_('Addressing')), FieldSet('vrf', name=_('Addressing')),
FieldSet('mtu', 'enabled', name=_('Operation')), FieldSet('mtu', 'enabled', name=_('Operation')),
FieldSet('parent', 'bridge', name=_('Related Interfaces')), FieldSet('parent', 'bridge', name=_('Related Interfaces')),
FieldSet( FieldSet(
@ -370,8 +370,8 @@ class VMInterfaceForm(InterfaceCommonForm, VMComponentForm):
class Meta: class Meta:
model = VMInterface model = VMInterface
fields = [ fields = [
'virtual_machine', 'name', 'parent', 'bridge', 'enabled', 'mac_address', 'mtu', 'description', 'mode', 'virtual_machine', 'name', 'parent', 'bridge', 'enabled', 'mtu', 'description', 'mode', 'vlan_group',
'vlan_group', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vlan_translation_policy', 'vrf', 'tags', 'untagged_vlan', 'tagged_vlans', 'qinq_svlan', 'vlan_translation_policy', 'vrf', 'tags',
] ]
labels = { labels = {
'mode': _('802.1Q Mode'), 'mode': _('802.1Q Mode'),
@ -380,14 +380,6 @@ class VMInterfaceForm(InterfaceCommonForm, VMComponentForm):
'mode': HTMXSelect(), '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): class VirtualDiskForm(VMComponentForm):

View File

@ -25,6 +25,9 @@ VMINTERFACE_BUTTONS = """
{% if perms.ipam.add_ipaddress %} {% if perms.ipam.add_ipaddress %}
<li><a class="dropdown-item" href="{% url 'ipam:ipaddress_add' %}?vminterface={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}">IP Address</a></li> <li><a class="dropdown-item" href="{% url 'ipam:ipaddress_add' %}?vminterface={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}">IP Address</a></li>
{% endif %} {% endif %}
{% if perms.dcim.add_macaddress %}
<li><a class="dropdown-item" href="{% url 'dcim:macaddress_add' %}?vminterface={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}">MAC Address</a></li>
{% endif %}
{% if perms.vpn.add_l2vpntermination %} {% if perms.vpn.add_l2vpntermination %}
<li><a class="dropdown-item" href="{% url 'vpn:l2vpntermination_add' %}?virtual_machine={{ object.pk }}&vminterface={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}">L2VPN Termination</a></li> <li><a class="dropdown-item" href="{% url 'vpn:l2vpntermination_add' %}?virtual_machine={{ object.pk }}&vminterface={{ record.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}">L2VPN Termination</a></li>
{% endif %} {% endif %}

View File

@ -1,7 +1,6 @@
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.test import override_settings from django.test import override_settings
from django.urls import reverse from django.urls import reverse
from netaddr import EUI
from dcim.choices import InterfaceModeChoices from dcim.choices import InterfaceModeChoices
from dcim.models import DeviceRole, Platform, Site from dcim.models import DeviceRole, Platform, Site
@ -331,7 +330,6 @@ class VMInterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
'name': 'Interface X', 'name': 'Interface X',
'enabled': False, 'enabled': False,
'bridge': interfaces[1].pk, 'bridge': interfaces[1].pk,
'mac_address': EUI('01-02-03-04-05-06'),
'mtu': 65000, 'mtu': 65000,
'description': 'New description', 'description': 'New description',
'mode': InterfaceModeChoices.MODE_TAGGED, 'mode': InterfaceModeChoices.MODE_TAGGED,
@ -346,7 +344,6 @@ class VMInterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
'name': 'Interface [4-6]', 'name': 'Interface [4-6]',
'enabled': False, 'enabled': False,
'bridge': interfaces[3].pk, 'bridge': interfaces[3].pk,
'mac_address': EUI('01-02-03-04-05-06'),
'mtu': 2000, 'mtu': 2000,
'description': 'New description', 'description': 'New description',
'mode': InterfaceModeChoices.MODE_TAGGED, 'mode': InterfaceModeChoices.MODE_TAGGED,