Arthur 21 timmar sedan
förälder
incheckning
62076f77e2
2 ändrade filer med 56 tillägg och 5 borttagningar
  1. 2 5
      netbox/dcim/models/cables.py
  2. 54 0
      netbox/dcim/tests/test_cablepaths.py

+ 2 - 5
netbox/dcim/models/cables.py

@@ -1005,10 +1005,6 @@ class CablePath(models.Model):
             logger.debug(f'[Path segment #{segment}] Position stack: {position_stack}')
             logger.debug(f'[Path segment #{segment}] Local terminations: {terminations}')
 
-            # Fanned-out branches that reconverge on a single port are tracked by the position stack
-            if len(terminations) == 1:
-                fanout = False
-
             # Terminations must all be of the same type
             if not all(isinstance(t, type(terminations[0])) for t in terminations[1:]):
                 raise UnsupportedCablePath(_("All mid-span terminations must have the same termination type"))
@@ -1266,7 +1262,8 @@ class CablePath(models.Model):
 
             else:
                 # Check for non-symmetric path
-                if fanout and len(remote_terminations) > 1:
+                # Fanned-out branches arriving over separate links reach different endpoints
+                if fanout and len(links) > 1:
                     diverged = True
                 elif all(isinstance(t, type(remote_terminations[0])) for t in remote_terminations[1:]):
                     is_complete = True

+ 54 - 0
netbox/dcim/tests/test_cablepaths.py

@@ -3214,6 +3214,60 @@ class LegacyCablePathTestCase(BaseCablePathTestCase):
         self.assertFalse(interface1.path.is_complete)
         self.assertTrue(interface1.path.is_split)
 
+    def test_234_multiposition_frontport_reconverged_then_diverging(self):
+        """
+        [IF1] --C1-- [FP1:1] [RP1:1] --C2-- [RP2:1] [FP2] --C3-- [IF2]
+                     [FP1:2] [RP1:2]        [RP2:2] [FP3] --C4-- [IF3]
+        """
+        interface1 = Interface.objects.create(device=self.device, name='Interface 1')
+        interface2 = Interface.objects.create(device=self.device, name='Interface 2')
+        interface3 = Interface.objects.create(device=self.device, name='Interface 3')
+        rearport1 = RearPort.objects.create(device=self.device, name='Rear Port 1', positions=2)
+        rearport2 = RearPort.objects.create(device=self.device, name='Rear Port 2', positions=2)
+        frontport1 = FrontPort.objects.create(device=self.device, name='Front Port 1', positions=2)
+        frontport2 = FrontPort.objects.create(device=self.device, name='Front Port 2')
+        frontport3 = FrontPort.objects.create(device=self.device, name='Front Port 3')
+        PortMapping.objects.bulk_create([
+            PortMapping(
+                device=self.device, front_port=frontport1, front_port_position=1,
+                rear_port=rearport1, rear_port_position=1,
+            ),
+            PortMapping(
+                device=self.device, front_port=frontport1, front_port_position=2,
+                rear_port=rearport1, rear_port_position=2,
+            ),
+            PortMapping(
+                device=self.device, front_port=frontport2, front_port_position=1,
+                rear_port=rearport2, rear_port_position=1,
+            ),
+            PortMapping(
+                device=self.device, front_port=frontport3, front_port_position=1,
+                rear_port=rearport2, rear_port_position=2,
+            ),
+        ])
+
+        cable1 = Cable(a_terminations=[interface1], b_terminations=[frontport1])
+        cable1.save()
+        cable2 = Cable(a_terminations=[rearport1], b_terminations=[rearport2])
+        cable2.save()
+        cable3 = Cable(a_terminations=[frontport2], b_terminations=[interface2])
+        cable3.save()
+        cable4 = Cable(a_terminations=[frontport3], b_terminations=[interface3])
+        cable4.save()
+
+        # Reconverging on RP1 does not hide the branches diverging again at RP2
+        path1 = self.assertPathExists(
+            (
+                interface1, cable1, frontport1, rearport1, cable2, rearport2, (frontport2, frontport3),
+                (cable3, cable4), (interface2, interface3)
+            ),
+            is_complete=False,
+            is_split=True
+        )
+        self.assertEqual(path1.get_split_nodes(), [interface2, interface3])
+        interface1.refresh_from_db()
+        self.assertEqual(interface1.connected_endpoints, [])
+
     def test_301_create_path_via_existing_cable(self):
         """
         [IF1] --C1-- [FP1] [RP1] --C2-- [RP2] [FP2] --C3-- [IF2]