|
@@ -1,5 +1,5 @@
|
|
|
from circuits.models import *
|
|
from circuits.models import *
|
|
|
-from dcim.choices import LinkStatusChoices
|
|
|
|
|
|
|
+from dcim.choices import CableEndChoices, LinkStatusChoices
|
|
|
from dcim.models import *
|
|
from dcim.models import *
|
|
|
from dcim.svg import CableTraceSVG
|
|
from dcim.svg import CableTraceSVG
|
|
|
from dcim.tests.utils import BaseCablePathTestCase
|
|
from dcim.tests.utils import BaseCablePathTestCase
|
|
@@ -3033,3 +3033,60 @@ class LegacyCablePathTestCase(BaseCablePathTestCase):
|
|
|
self.assertEqual(CablePath.objects.count(), 1)
|
|
self.assertEqual(CablePath.objects.count(), 1)
|
|
|
interface.refresh_from_db()
|
|
interface.refresh_from_db()
|
|
|
self.assertPathIsSet(interface, path)
|
|
self.assertPathIsSet(interface, path)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class CableDependentObjectsTestCase(BaseCablePathTestCase):
|
|
|
|
|
+ """
|
|
|
|
|
+ Test Cable.update_dependent_objects(), which retraces the paths of a Cable written to the database
|
|
|
|
|
+ by a process that bypasses save() (e.g. a tool replaying serialized changes).
|
|
|
|
|
+ """
|
|
|
|
|
+ def _create_cable_raw(self, termination_a, termination_b):
|
|
|
|
|
+ """
|
|
|
|
|
+ Write a Cable and its terminations directly to the database, bypassing Cable.save().
|
|
|
|
|
+ """
|
|
|
|
|
+ cable = Cable(status=LinkStatusChoices.STATUS_CONNECTED)
|
|
|
|
|
+ cable.save_base(raw=True)
|
|
|
|
|
+
|
|
|
|
|
+ for termination, cable_end in (
|
|
|
|
|
+ (termination_a, CableEndChoices.SIDE_A),
|
|
|
|
|
+ (termination_b, CableEndChoices.SIDE_B),
|
|
|
|
|
+ ):
|
|
|
|
|
+ ct = CableTermination(cable=cable, cable_end=cable_end, termination=termination)
|
|
|
|
|
+ ct.cache_related_objects()
|
|
|
|
|
+ ct.save_base(raw=True)
|
|
|
|
|
+ termination.cable = cable
|
|
|
|
|
+ termination.cable_end = cable_end
|
|
|
|
|
+ termination.save()
|
|
|
|
|
+
|
|
|
|
|
+ return cable
|
|
|
|
|
+
|
|
|
|
|
+ def test_retrace_after_raw_create(self):
|
|
|
|
|
+ interface1 = Interface.objects.create(device=self.device, name='Interface 1')
|
|
|
|
|
+ interface2 = Interface.objects.create(device=self.device, name='Interface 2')
|
|
|
|
|
+
|
|
|
|
|
+ cable = self._create_cable_raw(interface1, interface2)
|
|
|
|
|
+ self.assertEqual(CablePath.objects.count(), 0)
|
|
|
|
|
+
|
|
|
|
|
+ cable.update_dependent_objects()
|
|
|
|
|
+
|
|
|
|
|
+ self.assertPathExists((interface1, cable, interface2), is_complete=True, is_active=True)
|
|
|
|
|
+ self.assertPathExists((interface2, cable, interface1), is_complete=True, is_active=True)
|
|
|
|
|
+ self.assertEqual(CablePath.objects.count(), 2)
|
|
|
|
|
+
|
|
|
|
|
+ def test_retrace_is_idempotent(self):
|
|
|
|
|
+ interface1 = Interface.objects.create(device=self.device, name='Interface 1')
|
|
|
|
|
+ interface2 = Interface.objects.create(device=self.device, name='Interface 2')
|
|
|
|
|
+
|
|
|
|
|
+ cable = Cable(a_terminations=[interface1], b_terminations=[interface2])
|
|
|
|
|
+ cable.save()
|
|
|
|
|
+ self.assertEqual(CablePath.objects.count(), 2)
|
|
|
|
|
+
|
|
|
|
|
+ cable.update_dependent_objects()
|
|
|
|
|
+
|
|
|
|
|
+ path1 = self.assertPathExists((interface1, cable, interface2), is_complete=True, is_active=True)
|
|
|
|
|
+ path2 = self.assertPathExists((interface2, cable, interface1), is_complete=True, is_active=True)
|
|
|
|
|
+ self.assertEqual(CablePath.objects.count(), 2)
|
|
|
|
|
+ interface1.refresh_from_db()
|
|
|
|
|
+ interface2.refresh_from_db()
|
|
|
|
|
+ self.assertPathIsSet(interface1, path1)
|
|
|
|
|
+ self.assertPathIsSet(interface2, path2)
|