mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-23 12:08:43 -06:00
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:
@@ -9,8 +9,8 @@ from ipam.models import ASN, IPAddress, RIR, VLAN, VLANTranslationPolicy, VRF
|
||||
from netbox.choices import ColorChoices, WeightUnitChoices
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
from users.models import User
|
||||
from utilities.testing import ChangeLoggedFilterSetTests, create_test_device
|
||||
from virtualization.models import Cluster, ClusterType, ClusterGroup
|
||||
from utilities.testing import ChangeLoggedFilterSetTests, create_test_device, create_test_virtualmachine
|
||||
from virtualization.models import Cluster, ClusterType, ClusterGroup, VMInterface, VirtualMachine
|
||||
from wireless.choices import WirelessChannelChoices, WirelessRoleChoices
|
||||
|
||||
|
||||
@@ -2323,10 +2323,17 @@ class DeviceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
PowerOutlet(device=devices[1], name='Power Outlet 2'),
|
||||
))
|
||||
interfaces = (
|
||||
Interface(device=devices[0], name='Interface 1', mac_address='00-00-00-00-00-01'),
|
||||
Interface(device=devices[1], name='Interface 2', mac_address='00-00-00-00-00-02'),
|
||||
Interface(device=devices[0], name='Interface 1'),
|
||||
Interface(device=devices[1], name='Interface 2'),
|
||||
)
|
||||
Interface.objects.bulk_create(interfaces)
|
||||
mac_addresses = (
|
||||
MACAddress(mac_address='00-00-00-00-00-01'),
|
||||
MACAddress(mac_address='00-00-00-00-00-02'),
|
||||
)
|
||||
MACAddress.objects.bulk_create(mac_addresses)
|
||||
interfaces[0].mac_addresses.set([mac_addresses[0]])
|
||||
interfaces[1].mac_addresses.set([mac_addresses[1]])
|
||||
rear_ports = (
|
||||
RearPort(device=devices[0], name='Rear Port 1', type=PortTypeChoices.TYPE_8P8C),
|
||||
RearPort(device=devices[1], name='Rear Port 2', type=PortTypeChoices.TYPE_8P8C),
|
||||
@@ -3670,6 +3677,13 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
|
||||
)
|
||||
VirtualDeviceContext.objects.bulk_create(vdcs)
|
||||
|
||||
mac_addresses = (
|
||||
MACAddress(mac_address='00-00-00-00-00-01'),
|
||||
MACAddress(mac_address='00-00-00-00-00-02'),
|
||||
MACAddress(mac_address='00-00-00-00-00-03'),
|
||||
)
|
||||
MACAddress.objects.bulk_create(mac_addresses)
|
||||
|
||||
vlans = (
|
||||
VLAN(name='SVLAN 1', vid=1001, qinq_role=VLANQinQRoleChoices.ROLE_SERVICE),
|
||||
VLAN(name='SVLAN 2', vid=1002, qinq_role=VLANQinQRoleChoices.ROLE_SERVICE),
|
||||
@@ -3695,7 +3709,6 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
|
||||
mgmt_only=True,
|
||||
mtu=100,
|
||||
mode=InterfaceModeChoices.MODE_ACCESS,
|
||||
mac_address='00-00-00-00-00-01',
|
||||
description='First',
|
||||
vrf=vrfs[0],
|
||||
speed=1000000,
|
||||
@@ -3721,7 +3734,6 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
|
||||
mgmt_only=True,
|
||||
mtu=200,
|
||||
mode=InterfaceModeChoices.MODE_TAGGED,
|
||||
mac_address='00-00-00-00-00-02',
|
||||
description='Second',
|
||||
vrf=vrfs[1],
|
||||
speed=1000000,
|
||||
@@ -3740,7 +3752,6 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
|
||||
mgmt_only=False,
|
||||
mtu=300,
|
||||
mode=InterfaceModeChoices.MODE_TAGGED_ALL,
|
||||
mac_address='00-00-00-00-00-03',
|
||||
description='Third',
|
||||
vrf=vrfs[2],
|
||||
speed=100000,
|
||||
@@ -3814,6 +3825,10 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
|
||||
interfaces[6].vdcs.set([vdcs[0]])
|
||||
interfaces[7].vdcs.set([vdcs[1]])
|
||||
|
||||
interfaces[0].mac_addresses.set([mac_addresses[0]])
|
||||
interfaces[2].mac_addresses.set([mac_addresses[1]])
|
||||
interfaces[3].mac_addresses.set([mac_addresses[2]])
|
||||
|
||||
# Cables
|
||||
Cable(a_terminations=[interfaces[0]], b_terminations=[interfaces[5]]).save()
|
||||
Cable(a_terminations=[interfaces[1]], b_terminations=[interfaces[6]]).save()
|
||||
@@ -5842,3 +5857,80 @@ class VirtualDeviceContextTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'primary_ip6_id': [addresses[2].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0)
|
||||
|
||||
|
||||
class MACAddressTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
queryset = MACAddress.objects.all()
|
||||
filterset = MACAddressFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
devices = (
|
||||
create_test_device('Device 1'),
|
||||
create_test_device('Device 2'),
|
||||
create_test_device('Device 3'),
|
||||
)
|
||||
interfaces = (
|
||||
Interface(device=devices[0], name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[1], name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
Interface(device=devices[2], name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED),
|
||||
)
|
||||
Interface.objects.bulk_create(interfaces)
|
||||
|
||||
virtual_machines = (
|
||||
create_test_virtualmachine('Virtual Machine 1'),
|
||||
create_test_virtualmachine('Virtual Machine 2'),
|
||||
create_test_virtualmachine('Virtual Machine 3'),
|
||||
)
|
||||
vm_interfaces = (
|
||||
VMInterface(virtual_machine=virtual_machines[0], name='Interface 1'),
|
||||
VMInterface(virtual_machine=virtual_machines[1], name='Interface 2'),
|
||||
VMInterface(virtual_machine=virtual_machines[2], name='Interface 3'),
|
||||
)
|
||||
VMInterface.objects.bulk_create(vm_interfaces)
|
||||
|
||||
mac_addresses = (
|
||||
# Device MACs
|
||||
MACAddress(mac_address='00-00-00-01-01-01', assigned_object=interfaces[0]),
|
||||
MACAddress(mac_address='00-00-00-02-01-01', assigned_object=interfaces[1]),
|
||||
MACAddress(mac_address='00-00-00-03-01-01', assigned_object=interfaces[2]),
|
||||
MACAddress(mac_address='00-00-00-03-01-02', assigned_object=interfaces[2]),
|
||||
# VM MACs
|
||||
MACAddress(mac_address='00-00-00-04-01-01', assigned_object=vm_interfaces[0]),
|
||||
MACAddress(mac_address='00-00-00-05-01-01', assigned_object=vm_interfaces[1]),
|
||||
MACAddress(mac_address='00-00-00-06-01-01', assigned_object=vm_interfaces[2]),
|
||||
MACAddress(mac_address='00-00-00-06-01-02', assigned_object=vm_interfaces[2]),
|
||||
)
|
||||
MACAddress.objects.bulk_create(mac_addresses)
|
||||
|
||||
def test_mac_address(self):
|
||||
params = {'mac_address': ['00-00-00-01-01-01', '00-00-00-02-01-01']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_device(self):
|
||||
devices = Device.objects.all()[:2]
|
||||
params = {'device_id': [devices[0].pk, devices[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'device': [devices[0].name, devices[1].name]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_virtual_machine(self):
|
||||
virtual_machines = VirtualMachine.objects.all()[:2]
|
||||
params = {'virtual_machine_id': [virtual_machines[0].pk, virtual_machines[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'virtual_machine': [virtual_machines[0].name, virtual_machines[1].name]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_interface(self):
|
||||
interfaces = Interface.objects.all()[:2]
|
||||
params = {'interface_id': [interfaces[0].pk, interfaces[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'interface': [interfaces[0].name, interfaces[1].name]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_vminterface(self):
|
||||
vm_interfaces = VMInterface.objects.all()[:2]
|
||||
params = {'vminterface_id': [vm_interfaces[0].pk, vm_interfaces[1].pk]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
params = {'vminterface': [vm_interfaces[0].name, vm_interfaces[1].name]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
@@ -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,
|
||||
@@ -2554,7 +2552,6 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
'type': InterfaceTypeChoices.TYPE_1GE_FIXED,
|
||||
'enabled': True,
|
||||
'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': 1000000,
|
||||
|
||||
Reference in New Issue
Block a user