diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py
index 8da7c102b..2b426b285 100644
--- a/netbox/dcim/models.py
+++ b/netbox/dcim/models.py
@@ -2372,9 +2372,49 @@ class Cable(ChangeLoggedModel):
('termination_b_type', 'termination_b_id'),
)
- # TODO: This should follow all cables in a path
def get_path_endpoints(self):
"""
- Return the endpoints connected by this cable path.
+ Traverse both ends of a cable path and return its connected endpoints. Note that one or both endpoints may be
+ None.
"""
- return (self.termination_a, self.termination_b)
+ def trace_cable(termination, position=None):
+
+ # 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)
+
+ # 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(
+ 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)
+
+ # Termination is not a panel port, so we've reached the end of the path
+ return termination
+
+ return trace_cable(self.termination_a), trace_cable(self.termination_b)
diff --git a/netbox/dcim/signals.py b/netbox/dcim/signals.py
index 2320ba2d5..1d1094d66 100644
--- a/netbox/dcim/signals.py
+++ b/netbox/dcim/signals.py
@@ -27,10 +27,11 @@ def update_connected_endpoints(instance, **kwargs):
When a Cable is saved, update its connected endpoints.
"""
termination_a, termination_b = instance.get_path_endpoints()
- termination_a.connected_endpoint = termination_b
- termination_a.save()
- termination_b.connected_endpoint = termination_a
- termination_b.save()
+ if termination_a is not None and termination_b is not None:
+ termination_a.connected_endpoint = termination_b
+ termination_a.save()
+ termination_b.connected_endpoint = termination_a
+ termination_b.save()
@receiver(post_delete, sender=Cable)
diff --git a/netbox/templates/dcim/inc/interface.html b/netbox/templates/dcim/inc/interface.html
index 287410617..72a56f367 100644
--- a/netbox/templates/dcim/inc/interface.html
+++ b/netbox/templates/dcim/inc/interface.html
@@ -106,7 +106,7 @@
{% else %}
-
+
{% endif %}
diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py
index 0e36f3220..dcc0905f9 100644
--- a/netbox/utilities/views.py
+++ b/netbox/utilities/views.py
@@ -212,6 +212,13 @@ class ObjectEditView(GetReturnURLMixin, View):
obj_created = not form.instance.pk
obj = form.save()
+ print("Connecting {} {} to {} {}".format(
+ obj.termination_a.device,
+ obj.termination_a,
+ obj.termination_b.device,
+ obj.termination_b
+ ))
+
msg = '{} {}'.format(
'Created' if obj_created else 'Modified',
self.model._meta.verbose_name