mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-09 01:49:35 -06:00
Merge pull request #20739 from netbox-community/20738-vc-delete
Some checks are pending
CI / build (20.x, 3.10) (push) Waiting to run
CI / build (20.x, 3.11) (push) Waiting to run
CI / build (20.x, 3.12) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, actions) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, python) (push) Waiting to run
Some checks are pending
CI / build (20.x, 3.10) (push) Waiting to run
CI / build (20.x, 3.11) (push) Waiting to run
CI / build (20.x, 3.12) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, actions) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, python) (push) Waiting to run
20738 update vc_position in delete not signal handler
This commit is contained in:
commit
5b3ff3c0e9
@ -1154,7 +1154,6 @@ class VirtualChassis(PrimaryModel):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
|
|
||||||
# Check for LAG interfaces split across member chassis
|
# Check for LAG interfaces split across member chassis
|
||||||
interfaces = Interface.objects.filter(
|
interfaces = Interface.objects.filter(
|
||||||
device__in=self.members.all(),
|
device__in=self.members.all(),
|
||||||
@ -1168,6 +1167,13 @@ class VirtualChassis(PrimaryModel):
|
|||||||
"interfaces."
|
"interfaces."
|
||||||
).format(self=self, interfaces=InterfaceSpeedChoices))
|
).format(self=self, interfaces=InterfaceSpeedChoices))
|
||||||
|
|
||||||
|
# Clear vc_position and vc_priority on member devices BEFORE calling super().delete()
|
||||||
|
# This must be done here because on_delete=SET_NULL executes before pre_delete signal
|
||||||
|
for device in self.members.all():
|
||||||
|
device.vc_position = None
|
||||||
|
device.vc_priority = None
|
||||||
|
device.save()
|
||||||
|
|
||||||
return super().delete(*args, **kwargs)
|
return super().delete(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.db.models.signals import post_save, post_delete, pre_delete
|
from django.db.models.signals import post_save, post_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
from dcim.choices import CableEndChoices, LinkStatusChoices
|
from dcim.choices import CableEndChoices, LinkStatusChoices
|
||||||
@ -85,18 +85,6 @@ def assign_virtualchassis_master(instance, created, **kwargs):
|
|||||||
master.save()
|
master.save()
|
||||||
|
|
||||||
|
|
||||||
@receiver(pre_delete, sender=VirtualChassis)
|
|
||||||
def clear_virtualchassis_members(instance, **kwargs):
|
|
||||||
"""
|
|
||||||
When a VirtualChassis is deleted, nullify the vc_position and vc_priority fields of its prior members.
|
|
||||||
"""
|
|
||||||
devices = Device.objects.filter(virtual_chassis=instance.pk)
|
|
||||||
for device in devices:
|
|
||||||
device.vc_position = None
|
|
||||||
device.vc_priority = None
|
|
||||||
device.save()
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Cables
|
# Cables
|
||||||
#
|
#
|
||||||
|
|||||||
@ -1031,3 +1031,92 @@ class VirtualDeviceContextTestCase(TestCase):
|
|||||||
vdc2 = VirtualDeviceContext(device=device, name="VDC 2", identifier=1, status='active')
|
vdc2 = VirtualDeviceContext(device=device, name="VDC 2", identifier=1, status='active')
|
||||||
with self.assertRaises(ValidationError):
|
with self.assertRaises(ValidationError):
|
||||||
vdc2.full_clean()
|
vdc2.full_clean()
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualChassisTestCase(TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
site = Site.objects.create(name='Test Site 1', slug='test-site-1')
|
||||||
|
manufacturer = Manufacturer.objects.create(name='Test Manufacturer 1', slug='test-manufacturer-1')
|
||||||
|
devicetype = DeviceType.objects.create(
|
||||||
|
manufacturer=manufacturer, model='Test Device Type 1', slug='test-device-type-1'
|
||||||
|
)
|
||||||
|
role = DeviceRole.objects.create(
|
||||||
|
name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
|
||||||
|
)
|
||||||
|
Device.objects.create(
|
||||||
|
device_type=devicetype, role=role, name='TestDevice1', site=site
|
||||||
|
)
|
||||||
|
Device.objects.create(
|
||||||
|
device_type=devicetype, role=role, name='TestDevice2', site=site
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_virtualchassis_deletion_clears_vc_position(self):
|
||||||
|
"""
|
||||||
|
Test that when a VirtualChassis is deleted, member devices have their
|
||||||
|
vc_position and vc_priority fields set to None.
|
||||||
|
"""
|
||||||
|
devices = Device.objects.all()
|
||||||
|
device1 = devices[0]
|
||||||
|
device2 = devices[1]
|
||||||
|
|
||||||
|
# Create a VirtualChassis with two member devices
|
||||||
|
vc = VirtualChassis.objects.create(name='Test VC', master=device1)
|
||||||
|
|
||||||
|
device1.virtual_chassis = vc
|
||||||
|
device1.vc_position = 1
|
||||||
|
device1.vc_priority = 10
|
||||||
|
device1.save()
|
||||||
|
|
||||||
|
device2.virtual_chassis = vc
|
||||||
|
device2.vc_position = 2
|
||||||
|
device2.vc_priority = 20
|
||||||
|
device2.save()
|
||||||
|
|
||||||
|
# Verify devices are members of the VC with positions set
|
||||||
|
device1.refresh_from_db()
|
||||||
|
device2.refresh_from_db()
|
||||||
|
self.assertEqual(device1.virtual_chassis, vc)
|
||||||
|
self.assertEqual(device1.vc_position, 1)
|
||||||
|
self.assertEqual(device1.vc_priority, 10)
|
||||||
|
self.assertEqual(device2.virtual_chassis, vc)
|
||||||
|
self.assertEqual(device2.vc_position, 2)
|
||||||
|
self.assertEqual(device2.vc_priority, 20)
|
||||||
|
|
||||||
|
# Delete the VirtualChassis
|
||||||
|
vc.delete()
|
||||||
|
|
||||||
|
# Verify devices have vc_position and vc_priority set to None
|
||||||
|
device1.refresh_from_db()
|
||||||
|
device2.refresh_from_db()
|
||||||
|
self.assertIsNone(device1.virtual_chassis)
|
||||||
|
self.assertIsNone(device1.vc_position)
|
||||||
|
self.assertIsNone(device1.vc_priority)
|
||||||
|
self.assertIsNone(device2.virtual_chassis)
|
||||||
|
self.assertIsNone(device2.vc_position)
|
||||||
|
self.assertIsNone(device2.vc_priority)
|
||||||
|
|
||||||
|
def test_virtualchassis_duplicate_vc_position(self):
|
||||||
|
"""
|
||||||
|
Test that two devices cannot be assigned to the same vc_position
|
||||||
|
within the same VirtualChassis.
|
||||||
|
"""
|
||||||
|
devices = Device.objects.all()
|
||||||
|
device1 = devices[0]
|
||||||
|
device2 = devices[1]
|
||||||
|
|
||||||
|
# Create a VirtualChassis
|
||||||
|
vc = VirtualChassis.objects.create(name='Test VC')
|
||||||
|
|
||||||
|
# Assign first device to vc_position 1
|
||||||
|
device1.virtual_chassis = vc
|
||||||
|
device1.vc_position = 1
|
||||||
|
device1.full_clean()
|
||||||
|
device1.save()
|
||||||
|
|
||||||
|
# Try to assign second device to the same vc_position
|
||||||
|
device2.virtual_chassis = vc
|
||||||
|
device2.vc_position = 1
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
device2.full_clean()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user