This commit is contained in:
parent
e79e33e9dd
commit
98c94f5fc7
|
|
@ -1229,10 +1229,13 @@ class VirtualChassis(PrimaryModel):
|
|||
lag__device=F('device')
|
||||
)
|
||||
if interfaces:
|
||||
raise ProtectedError(_(
|
||||
"Unable to delete virtual chassis {self}. There are member interfaces which form a cross-chassis LAG "
|
||||
"interfaces."
|
||||
).format(self=self, interfaces=InterfaceSpeedChoices))
|
||||
raise ProtectedError(
|
||||
_(
|
||||
"Unable to delete virtual chassis {virtual_chassis}. One or more member interfaces form a "
|
||||
"cross-chassis LAG."
|
||||
).format(virtual_chassis=self),
|
||||
set(interfaces),
|
||||
)
|
||||
|
||||
# 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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from decimal import Decimal
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models import ProtectedError
|
||||
from django.db.models.signals import post_save
|
||||
from django.test import TestCase, tag
|
||||
|
||||
|
|
@ -2429,6 +2430,59 @@ class VirtualChassisTestCase(TestCase):
|
|||
self.assertIsNone(device2.vc_position)
|
||||
self.assertIsNone(device2.vc_priority)
|
||||
|
||||
@tag('regression') # Ref: #22720
|
||||
def test_virtualchassis_deletion_blocked_by_cross_chassis_lag(self):
|
||||
"""
|
||||
Deleting a VirtualChassis whose members form a cross-chassis LAG must
|
||||
raise ProtectedError exposing the blocking interfaces, leaving the VC
|
||||
and its member assignments unchanged.
|
||||
"""
|
||||
device1 = Device.objects.get(name='TestDevice1')
|
||||
device2 = Device.objects.get(name='TestDevice2')
|
||||
|
||||
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()
|
||||
|
||||
lag = Interface.objects.create(device=device1, name='lag0', type=InterfaceTypeChoices.TYPE_LAG)
|
||||
member_interface = Interface(
|
||||
device=device2,
|
||||
name='eth0',
|
||||
type=InterfaceTypeChoices.TYPE_1GE_FIXED,
|
||||
lag=lag,
|
||||
)
|
||||
# A cross-chassis LAG member is valid while both devices share the VC
|
||||
member_interface.full_clean()
|
||||
member_interface.save()
|
||||
|
||||
with self.assertRaises(ProtectedError) as cm:
|
||||
vc.delete()
|
||||
|
||||
self.assertEqual(
|
||||
cm.exception.args[0],
|
||||
'Unable to delete virtual chassis Test VC. One or more member interfaces form a cross-chassis LAG.'
|
||||
)
|
||||
self.assertEqual(set(cm.exception.protected_objects), {member_interface})
|
||||
|
||||
# The failed deletion must not clear the VC or its member assignments
|
||||
self.assertTrue(VirtualChassis.objects.filter(pk=vc.pk).exists())
|
||||
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)
|
||||
|
||||
def test_virtualchassis_duplicate_vc_position(self):
|
||||
"""
|
||||
Test that two devices cannot be assigned to the same vc_position
|
||||
|
|
|
|||
Loading…
Reference in New Issue