mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 09:51:22 -06:00
Fixes #2571: Enforce deletion of attached cable when deleting a termination point
This commit is contained in:
parent
bb5432de7d
commit
3e92aa9fe7
@ -43,6 +43,7 @@ NetBox now supports modeling physical cables for console, power, and interface c
|
|||||||
* [#2566](https://github.com/digitalocean/netbox/issues/2566) - Prevent both ends of a cable from connecting to the same termination point
|
* [#2566](https://github.com/digitalocean/netbox/issues/2566) - Prevent both ends of a cable from connecting to the same termination point
|
||||||
* [#2567](https://github.com/digitalocean/netbox/issues/2567) - Introduced proxy models to represent console/power/interface connections
|
* [#2567](https://github.com/digitalocean/netbox/issues/2567) - Introduced proxy models to represent console/power/interface connections
|
||||||
* [#2569](https://github.com/digitalocean/netbox/issues/2569) - Added LSH fiber type; removed SC duplex/simplex designations
|
* [#2569](https://github.com/digitalocean/netbox/issues/2569) - Added LSH fiber type; removed SC duplex/simplex designations
|
||||||
|
* [#2571](https://github.com/digitalocean/netbox/issues/2571) - Enforce deletion of attached cable when deleting a termination point
|
||||||
|
|
||||||
## API Changes
|
## API Changes
|
||||||
|
|
||||||
|
@ -73,6 +73,18 @@ class CableTermination(models.Model):
|
|||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Generic relations to Cable. These ensure that an attached Cable is deleted if the terminated object is deleted.
|
||||||
|
_cabled_as_a = GenericRelation(
|
||||||
|
to='dcim.Cable',
|
||||||
|
content_type_field='termination_a_type',
|
||||||
|
object_id_field='termination_a_id'
|
||||||
|
)
|
||||||
|
_cabled_as_b = GenericRelation(
|
||||||
|
to='dcim.Cable',
|
||||||
|
content_type_field='termination_b_type',
|
||||||
|
object_id_field='termination_b_id'
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
@ -45,8 +45,10 @@ def update_connected_endpoints(instance, **kwargs):
|
|||||||
def nullify_connected_endpoints(instance, **kwargs):
|
def nullify_connected_endpoints(instance, **kwargs):
|
||||||
|
|
||||||
# Disassociate the Cable from its termination points
|
# Disassociate the Cable from its termination points
|
||||||
|
if instance.termination_a is not None:
|
||||||
instance.termination_a.cable = None
|
instance.termination_a.cable = None
|
||||||
instance.termination_a.save()
|
instance.termination_a.save()
|
||||||
|
if instance.termination_b is not None:
|
||||||
instance.termination_b.cable = None
|
instance.termination_b.cable = None
|
||||||
instance.termination_b.save()
|
instance.termination_b.save()
|
||||||
|
|
||||||
|
@ -149,3 +149,54 @@ class RackTestCase(TestCase):
|
|||||||
face=None,
|
face=None,
|
||||||
)
|
)
|
||||||
self.assertTrue(pdu)
|
self.assertTrue(pdu)
|
||||||
|
|
||||||
|
|
||||||
|
class CableTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
|
||||||
|
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'
|
||||||
|
)
|
||||||
|
devicerole = DeviceRole.objects.create(
|
||||||
|
name='Test Device Role 1', slug='test-device-role-1', color='ff0000'
|
||||||
|
)
|
||||||
|
self.device1 = Device.objects.create(
|
||||||
|
device_type=devicetype, device_role=devicerole, name='TestDevice1', site=site
|
||||||
|
)
|
||||||
|
self.device2 = Device.objects.create(
|
||||||
|
device_type=devicetype, device_role=devicerole, name='TestDevice2', site=site
|
||||||
|
)
|
||||||
|
self.interface1 = Interface.objects.create(device=self.device1, name='eth0')
|
||||||
|
self.interface2 = Interface.objects.create(device=self.device2, name='eth0')
|
||||||
|
self.cable = Cable(termination_a=self.interface1, termination_b=self.interface2)
|
||||||
|
self.cable.save()
|
||||||
|
|
||||||
|
def test_cable_creation(self):
|
||||||
|
"""
|
||||||
|
When a new Cable is created, it must be cached on either termination point.
|
||||||
|
"""
|
||||||
|
interface1 = Interface.objects.get(pk=self.interface1.pk)
|
||||||
|
self.assertEqual(self.cable.termination_a, interface1)
|
||||||
|
interface2 = Interface.objects.get(pk=self.interface2.pk)
|
||||||
|
self.assertEqual(self.cable.termination_b, interface2)
|
||||||
|
|
||||||
|
def test_cable_deletion(self):
|
||||||
|
"""
|
||||||
|
When a Cable is deleted, the `cable` field on its termination points must be nullified.
|
||||||
|
"""
|
||||||
|
self.cable.delete()
|
||||||
|
interface1 = Interface.objects.get(pk=self.interface1.pk)
|
||||||
|
self.assertIsNone(interface1.cable)
|
||||||
|
interface2 = Interface.objects.get(pk=self.interface2.pk)
|
||||||
|
self.assertIsNone(interface2.cable)
|
||||||
|
|
||||||
|
def test_cabletermination_deletion(self):
|
||||||
|
"""
|
||||||
|
When a CableTermination object is deleted, its attached Cable (if any) must also be deleted.
|
||||||
|
"""
|
||||||
|
self.interface1.delete()
|
||||||
|
cable = Cable.objects.filter(pk=self.cable.pk).first()
|
||||||
|
self.assertIsNone(cable)
|
||||||
|
Loading…
Reference in New Issue
Block a user