4867 multiple mac addresses (#17902)

* Create MACAddress model and migrations to convert existing .mac_address fields to standalone objects

* Add migrations

* All views/filtering working and documentation done; no unit tests yet

* Redo migrations following VLAN Translation

* Remove mac_address filter fields and add table columns for device/vm

* Remove unnecessary "bulk rename"

* Fix filterset tests for Device

* Fix filterset tests for Interface

* Fix tests on single-object forms

* Fix serializer tests

* Fix filterset tests for VMInterface

* Fix filterset tests for Device and VirtualMachine

* Move new field check into lookup_map iteration

* Fix general MACAddress filter tests

* Add GraphQL types/filters/schema

* Fix bulk edit/create tests (bulk editing Interfaces will be unsupported because of inheritance from ComponentBulkEditForm)

* Make mac_address read_only on InterfaceSerializer/VMInterfaceSerializer

* Undo unrelated work

* Cleanup unused IPAddress derived stuff

* API endpoints

* Add serializer objects to interface serializers

* Clean up unnecessary bulk create forms/views/routes

* Add SearchIndex and adjust indexable fields for Interface and VMInterface

* Reorganize MACAddress classes out of association with DeviceComponents

* Move MACAddressSerializer

* Enforce saving only a single is_primary MACAddress per interface/vminterface

* Perform is_primary validation on MACAddress model and just check if one already exists for the interface

* Remove form-level validation

* Fix check for current is_primary setting when reassigning

* Model cleanup

* Documentation notes and cleanup

* Simplify serializer and add ip_addresses

* Add to VMInterfaceSerializer too

* Style cleanup

* Standardize "MAC Address" instead of "MAC"

* Remove unused views

* Add is_primary field for bulk edit

* HTML cleanup and add copy-to-clipboard button

* Remove mac_address from Interface and VMInterface bulk-edit forms

* Add device and VM filtering

* Use combined assigned_object_parent in table to match structure of IPAddressTable

* Add GFK fields to MACAddressSerializer

* Reorganize "Addressing" sections to remove from proximity to "Device Components" and related groupings

* Clean up migrations

* Misc cleanup

* Add filterset test

* Remove mac_address field from interface forms

* Designate primary MAC address via a ForeignKey on the interface models

* Add serializer fields for primary_mac_address

* Update docs

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
This commit is contained in:
bctiemann
2024-11-18 15:11:24 -05:00
committed by GitHub
parent b4f15092db
commit 353214098b
46 changed files with 1156 additions and 173 deletions

View File

@@ -10,7 +10,7 @@ from mptt.models import MPTTModel, TreeForeignKey
from dcim.choices import *
from dcim.constants import *
from dcim.fields import MACAddressField, WWNField
from dcim.fields import WWNField
from netbox.choices import ColorChoices
from netbox.models import OrganizationalModel, NetBoxModel
from utilities.fields import ColorField, NaturalOrderingField
@@ -505,11 +505,6 @@ class BaseInterface(models.Model):
verbose_name=_('enabled'),
default=True
)
mac_address = MACAddressField(
null=True,
blank=True,
verbose_name=_('MAC address')
)
mtu = models.PositiveIntegerField(
blank=True,
null=True,
@@ -572,6 +567,14 @@ class BaseInterface(models.Model):
blank=True,
verbose_name=_('VLAN Translation Policy')
)
primary_mac_address = models.OneToOneField(
to='dcim.MACAddress',
on_delete=models.SET_NULL,
related_name='+',
blank=True,
null=True,
verbose_name=_('primary MAC address')
)
class Meta:
abstract = True
@@ -585,6 +588,14 @@ class BaseInterface(models.Model):
'qinq_svlan': _("Only Q-in-Q interfaces may specify a service VLAN.")
})
# Check that the primary MAC address (if any) is assigned to this interface
if self.primary_mac_address and self.primary_mac_address.assigned_object != self:
raise ValidationError({
'primary_mac_address': _("MAC address {mac_address} is not assigned to this interface.").format(
mac_address=self.primary_mac_address
)
})
def save(self, *args, **kwargs):
# Remove untagged VLAN assignment for non-802.1Q interfaces
@@ -609,6 +620,11 @@ class BaseInterface(models.Model):
def count_fhrp_groups(self):
return self.fhrp_group_assignments.count()
@cached_property
def mac_address(self):
if self.primary_mac_address:
return self.primary_mac_address.mac_address
class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEndpoint, TrackingModelMixin):
"""
@@ -738,6 +754,12 @@ class Interface(ModularComponentModel, BaseInterface, CabledObjectModel, PathEnd
object_id_field='assigned_object_id',
related_query_name='interface'
)
mac_addresses = GenericRelation(
to='dcim.MACAddress',
content_type_field='assigned_object_type',
object_id_field='assigned_object_id',
related_query_name='interface'
)
fhrp_group_assignments = GenericRelation(
to='ipam.FHRPGroupAssignment',
content_type_field='interface_type',