diff --git a/docs/release-notes/version-2.10.md b/docs/release-notes/version-2.10.md index e071d4941..4ff839b6e 100644 --- a/docs/release-notes/version-2.10.md +++ b/docs/release-notes/version-2.10.md @@ -13,6 +13,7 @@ * [#5358](https://github.com/netbox-community/netbox/issues/5358) - Fix user table configuration for VM interfaces * [#5374](https://github.com/netbox-community/netbox/issues/5374) - Fix exception thrown when tracing mid-point * [#5376](https://github.com/netbox-community/netbox/issues/5376) - Correct invalid custom field filter logic values +* [#5395](https://github.com/netbox-community/netbox/issues/5395) - Fix cable tracing for rear ports with no corresponding front port --- diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index d1e4e2df8..7a9f2dc2c 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -411,21 +411,27 @@ class CablePath(models.Model): position_stack.append(peer_termination.rear_port_position) path.append(object_to_path_node(node)) - # Follow a RearPort to its corresponding FrontPort + # Follow a RearPort to its corresponding FrontPort (if any) elif isinstance(peer_termination, RearPort): path.append(object_to_path_node(peer_termination)) + + # Determine the peer FrontPort's position if peer_termination.positions == 1: - node = FrontPort.objects.get(rear_port=peer_termination, rear_port_position=1) - path.append(object_to_path_node(node)) + position = 1 elif position_stack: position = position_stack.pop() - node = FrontPort.objects.get(rear_port=peer_termination, rear_port_position=position) - path.append(object_to_path_node(node)) else: # No position indicated: path has split, so we stop at the RearPort is_split = True break + try: + node = FrontPort.objects.get(rear_port=peer_termination, rear_port_position=position) + path.append(object_to_path_node(node)) + except ObjectDoesNotExist: + # No corresponding FrontPort found for the RearPort + break + # Anything else marks the end of the path else: destination = peer_termination diff --git a/netbox/dcim/tests/test_cablepaths.py b/netbox/dcim/tests/test_cablepaths.py index 9e0f0e77f..37d7014f1 100644 --- a/netbox/dcim/tests/test_cablepaths.py +++ b/netbox/dcim/tests/test_cablepaths.py @@ -796,6 +796,30 @@ class CablePathTestCase(TestCase): ) self.assertEqual(CablePath.objects.count(), 2) + def test_207_rearport_without_frontport(self): + """ + [IF1] --C1-- [FP1] [RP1] --C2-- [RP2] + """ + interface1 = Interface.objects.create(device=self.device, name='Interface 1') + rearport1 = RearPort.objects.create(device=self.device, name='Rear Port 1', positions=1) + rearport2 = RearPort.objects.create(device=self.device, name='Rear Port 2', positions=1) + frontport1 = FrontPort.objects.create( + device=self.device, name='Front Port 1', rear_port=rearport1, rear_port_position=1 + ) + + # Create cables + cable1 = Cable(termination_a=interface1, termination_b=frontport1) + cable1.save() + cable2 = Cable(termination_a=rearport1, termination_b=rearport2) + cable2.save() + self.assertPathExists( + origin=interface1, + destination=None, + path=(cable1, frontport1, rearport1, cable2, rearport2), + is_active=False + ) + self.assertEqual(CablePath.objects.count(), 1) + def test_301_create_path_via_existing_cable(self): """ [IF1] --C1-- [FP1] [RP2] --C2-- [RP2] [FP2] --C3-- [IF2]