Bläddra i källkod

Fixes #4548: Fix tracing cables through a single RearPort

Jeremy Stretch 5 år sedan
förälder
incheckning
bcb7899b04

+ 1 - 0
docs/release-notes/version-2.8.md

@@ -12,6 +12,7 @@
 
 * [#4527](https://github.com/netbox-community/netbox/issues/4527) - Fix assignment of certain tags to config contexts
 * [#4545](https://github.com/netbox-community/netbox/issues/4545) - Removed all squashed schema migrations to allow direct upgrades from very old releases
+* [#4548](https://github.com/netbox-community/netbox/issues/4548) - Fix tracing cables through a single RearPort
 * [#4549](https://github.com/netbox-community/netbox/issues/4549) - Fix encoding unicode webhook body data
 * [#4556](https://github.com/netbox-community/netbox/issues/4556) - Update form for adding devices to clusters
 

+ 4 - 3
netbox/dcim/models/device_components.py

@@ -123,11 +123,12 @@ class CableTermination(models.Model):
             # Map a rear port/position to its corresponding front port
             elif isinstance(termination, RearPort):
 
-                # Can't map to a FrontPort without a position
-                if not position_stack:
+                # Can't map to a FrontPort without a position if there are multiple options
+                if termination.positions > 1 and not position_stack:
                     raise CableTraceSplit(termination)
 
-                position = position_stack.pop()
+                # We can assume position 1 if the RearPort has only one position
+                position = position_stack.pop() if position_stack else 1
 
                 # Validate the position
                 if position not in range(1, termination.positions + 1):

+ 44 - 1
netbox/dcim/tests/test_models.py

@@ -514,10 +514,10 @@ class CablePathTestCase(TestCase):
 
     def test_direct_connection(self):
         """
+        Test a direct connection between two interfaces.
 
         [Device 1] ----- [Device 2]
              Iface1     Iface1
-
         """
         # Create cable
         cable = Cable(
@@ -549,6 +549,49 @@ class CablePathTestCase(TestCase):
         self.assertIsNone(endpoint_a.connection_status)
         self.assertIsNone(endpoint_b.connection_status)
 
+    def test_connection_via_single_rear_port(self):
+        """
+        Test a connection which passes through a single front/rear port pair.
+
+                     1               2
+        [Device 1] ----- [Panel 1] ----- [Device 2]
+             Iface1     FP1     RP1     Iface1
+        """
+        # Create cables
+        cable1 = Cable(
+            termination_a=Interface.objects.get(device__name='Device 1', name='Interface 1'),
+            termination_b=FrontPort.objects.get(device__name='Panel 1', name='Front Port 1')
+        )
+        cable1.save()
+        cable2 = Cable(
+            termination_b=RearPort.objects.get(device__name='Panel 1', name='Rear Port 1'),
+            termination_a=Interface.objects.get(device__name='Device 2', name='Interface 1')
+        )
+        cable2.save()
+
+        # Retrieve endpoints
+        endpoint_a = Interface.objects.get(device__name='Device 1', name='Interface 1')
+        endpoint_b = Interface.objects.get(device__name='Device 2', name='Interface 1')
+
+        # Validate connections
+        self.assertEqual(endpoint_a.connected_endpoint, endpoint_b)
+        self.assertEqual(endpoint_b.connected_endpoint, endpoint_a)
+        self.assertTrue(endpoint_a.connection_status)
+        self.assertTrue(endpoint_b.connection_status)
+
+        # Delete cable 1
+        cable1.delete()
+
+        # Refresh endpoints
+        endpoint_a.refresh_from_db()
+        endpoint_b.refresh_from_db()
+
+        # Check that connections have been nullified
+        self.assertIsNone(endpoint_a.connected_endpoint)
+        self.assertIsNone(endpoint_b.connected_endpoint)
+        self.assertIsNone(endpoint_a.connection_status)
+        self.assertIsNone(endpoint_b.connection_status)
+
     def test_connections_via_patch(self):
         """
         Test two connections via patched rear ports: