diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 2b426b285..de7426d41 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -2377,44 +2377,44 @@ class Cable(ChangeLoggedModel): Traverse both ends of a cable path and return its connected endpoints. Note that one or both endpoints may be None. """ - def trace_cable(termination, position=None): + def trace_cable(termination, position=1): # Given a front port, follow the cable connected to the corresponding rear port/position if isinstance(termination, FrontPanelPort): - rear_port = termination.rear_port - port_type = ContentType.objects.get_for_model(rear_port) - next_cable = Cable.objects.filter( - Q(termination_a_type=port_type, termination_a_id=rear_port.pk) | - Q(termination_b_type=port_type, termination_b_id=rear_port.pk) - ).first() - if next_cable is None: - return None - if next_cable.termination_a == termination.rear_port: - return trace_cable(next_cable.termination_b, termination.rear_port_position) - else: - return trace_cable(next_cable.termination_a, termination.rear_port_position) + peer_port = termination.rear_port + position = termination.rear_port_position # Given a rear port/position, follow the cable connected to the corresponding front port - if isinstance(termination, RearPanelPort): - if position is None: - raise Exception("Must specify a position when tracing a path from a rear panel port") - front_port = FrontPanelPort.objects.get( + elif isinstance(termination, RearPanelPort): + if position not in range(1, termination.positions + 1): + raise Exception("Invalid position for {} ({} positions): {})".format( + termination, termination.positions, position + )) + peer_port = FrontPanelPort.objects.get( rear_port=termination, rear_port_position=position, ) - port_type = ContentType.objects.get_for_model(front_port) - next_cable = Cable.objects.filter( - Q(termination_a_type=port_type, termination_a_id=front_port.pk) | - Q(termination_b_type=port_type, termination_b_id=front_port.pk) - ).first() - if next_cable is None: - return None - if next_cable.termination_a == front_port: - return trace_cable(next_cable.termination_b) - else: - return trace_cable(next_cable.termination_a) + position=1 # Termination is not a panel port, so we've reached the end of the path - return termination + else: + return termination + + # Find the cable (if any) attached to the peer port + port_type = ContentType.objects.get_for_model(peer_port) + next_cable = Cable.objects.filter( + Q(termination_a_type=port_type, termination_a_id=peer_port.pk) | + Q(termination_b_type=port_type, termination_b_id=peer_port.pk) + ).first() + + # If no cable exists, return None + if next_cable is None: + return None + + # Return the far side termination of the cable + if next_cable.termination_a == peer_port: + return trace_cable(next_cable.termination_b, position) + else: + return trace_cable(next_cable.termination_a, position) return trace_cable(self.termination_a), trace_cable(self.termination_b) diff --git a/netbox/dcim/signals.py b/netbox/dcim/signals.py index 1d1094d66..e47865ac5 100644 --- a/netbox/dcim/signals.py +++ b/netbox/dcim/signals.py @@ -29,8 +29,10 @@ def update_connected_endpoints(instance, **kwargs): termination_a, termination_b = instance.get_path_endpoints() if termination_a is not None and termination_b is not None: termination_a.connected_endpoint = termination_b + termination_a.connection_status = True termination_a.save() termination_b.connected_endpoint = termination_a + termination_a.connection_status = True termination_b.save() @@ -40,7 +42,10 @@ def nullify_connected_endpoints(instance, **kwargs): When a Cable is deleted, nullify its connected endpoints. """ termination_a, termination_b = instance.get_path_endpoints() - termination_a.connected_endpoint = None - termination_a.save() - termination_b.connected_endpoint = None - termination_b.save() + if termination_a is not None and termination_b is not None: + termination_a.connected_endpoint = None + termination_a.connection_status = None + termination_a.save() + termination_b.connected_endpoint = None + termination_b.connection_status = None + termination_b.save()