Deleting device with cable connected leaves cable with only one side terminated #6686

Closed
opened 2025-12-29 19:43:56 +01:00 by adam · 7 comments
Owner

Originally created by @themmini on GitHub (Jul 19, 2022).

Originally assigned to: @jeremystretch on GitHub.

NetBox version

v3.3-beta1

Python version

3.10

Steps to Reproduce

  1. Create device A with NIC A
  2. Create device B with NIC B
  3. Connect NICs with Cable
    image
  4. Delete device A or B
    EDIT*
  5. Interact with cable e.g delete cable/remaining device - class error.

Expected Behavior

Delete action, deletes Device A AND the cable attached.
Delete remaining interface

Observed Behavior

Delete action deletes Device B, leaves cable in the database with only one termination point:
image

Unable to delete cable/or device cable is left
<class 'dcim.models.device_components.Interface.DoesNotExist'>

Originally created by @themmini on GitHub (Jul 19, 2022). Originally assigned to: @jeremystretch on GitHub. ### NetBox version v3.3-beta1 ### Python version 3.10 ### Steps to Reproduce 1. Create device A with NIC A 2. Create device B with NIC B 3. Connect NICs with Cable ![image](https://user-images.githubusercontent.com/11536761/179667404-23de6e47-91f1-40d0-bc74-9fc51ee57651.png) 4. Delete device A or B EDIT* 5. Interact with cable e.g delete cable/remaining device - class error. ### Expected Behavior ~~Delete action, deletes Device A **AND** the cable attached.~~ Delete remaining interface ### Observed Behavior ~~Delete action deletes Device B, leaves cable in the database with only one termination point:~~ ![image](https://user-images.githubusercontent.com/11536761/179667593-857c41fe-f66c-4979-8c0c-1b2956441806.png) Unable to delete cable/or device cable is left <class 'dcim.models.device_components.Interface.DoesNotExist'>
adam added the type: bugstatus: acceptedbeta labels 2025-12-29 19:43:56 +01:00
adam closed this issue 2025-12-29 19:43:57 +01:00
Author
Owner

@jeremystretch commented on GitHub (Jul 19, 2022):

While this is a change in behavior from v3.2 and earlier, I'm not sure I agree with classifying it as a bug. We've had numerous requests for the ability to leave cabling in place with only one end connected, which was not feasible with the old model but is now.

@jeremystretch commented on GitHub (Jul 19, 2022): While this is a change in behavior from v3.2 and earlier, I'm not sure I agree with classifying it as a bug. We've had numerous requests for the ability to leave cabling in place with only one end connected, which was not feasible with the old model but is now.
Author
Owner

@themmini commented on GitHub (Jul 19, 2022):

While that's fine - if you try to delete the other device you run into issues:
Same happens if you try to delete the cable.
image

@themmini commented on GitHub (Jul 19, 2022): While that's fine - if you try to delete the other device you run into issues: Same happens if you try to delete the cable. ![image](https://user-images.githubusercontent.com/11536761/179749970-7dd9059b-a7ee-4886-a7d2-27efd4829ec5.png)
Author
Owner

@Miccia94 commented on GitHub (Jul 21, 2022):

Having the same error as @mini-man

@Miccia94 commented on GitHub (Jul 21, 2022): Having the same error as @mini-man
Author
Owner

@jeremystretch commented on GitHub (Jul 25, 2022):

Ok, I see the issue. Previously, the cable was being deleted automatically, which was forcing the path to be recalculated. But that doesn't happen now, so we just need to add a receiver to handle that whenever a cable termination is deleted.

@jeremystretch commented on GitHub (Jul 25, 2022): Ok, I see the issue. Previously, the cable was being deleted automatically, which was forcing the path to be recalculated. But that doesn't happen now, so we just need to add a receiver to handle that whenever a cable termination is deleted.
Author
Owner

@jeremystretch commented on GitHub (Jul 25, 2022):

Or we disable the deletion of the object until the cable has been disconnected. I recall this being proposed in the past, but I'm not sure it went anywhere.

Edit: This was proposed a while back in #5418.

@jeremystretch commented on GitHub (Jul 25, 2022): _Or_ we disable the deletion of the object until the cable has been disconnected. I recall this being proposed in the past, but I'm not sure it went anywhere. Edit: This was proposed a while back in #5418.
Author
Owner

@jeremystretch commented on GitHub (Jul 25, 2022):

I've opened #9837 to discuss the above. Marking this as blocked until we reach a consensus on the desired behavior.

@jeremystretch commented on GitHub (Jul 25, 2022): I've opened #9837 to discuss the above. Marking this as blocked until we reach a consensus on the desired behavior.
Author
Owner

@jaylik commented on GitHub (Jul 29, 2022):

Guess this will have better solution later but temporary workaround below,

diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py
index 3f287317b..058b7b2e4 100644
--- a/netbox/dcim/models/cables.py
+++ b/netbox/dcim/models/cables.py
@@ -604,7 +604,13 @@ class CablePath(models.Model):
         """
         Retrace the path from the currently-defined originating termination(s)
         """
-        _new = self.from_origin(self.origins)
+        try:
+            _new = self.from_origin(self.origins)
+        except:
+            try:
+                _new = self.from_origin(self.destinations)
+            except:
+                _new = False
         if _new:
             self.path = _new.path
             self.is_complete = _new.is_complete
@@ -641,7 +647,8 @@ class CablePath(models.Model):
             nodes = []
             for node in step:
                 ct_id, object_id = decompile_path_node(node)
-                nodes.append(prefetched[ct_id][object_id])
+                if ct_id in prefetched and object_id in prefetched[ct_id]:
+                    nodes.append(prefetched[ct_id][object_id])
             path.append(nodes)

         return path
diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py
index 8f62b0626..a2c465a61 100644
--- a/netbox/dcim/models/device_components.py
+++ b/netbox/dcim/models/device_components.py
@@ -236,7 +236,10 @@ class PathEndpoint(models.Model):
         """
         Caching accessor for the attached CablePath's destination (if any)
         """
-        return self._path.destinations if self._path else []
+        try:
+            return self._path.destinations if self._path else []
+        except:
+            return []


 #
diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py
index 12e070e70..3589ed4b8 100644
--- a/netbox/dcim/views.py
+++ b/netbox/dcim/views.py
@@ -2850,7 +2850,7 @@ class CableEditView(generic.ObjectEditView):
             termination_a = obj.terminations.filter(cable_end='A').first()
             a_type = termination_a.termination._meta.model if termination_a else None
             termination_b = obj.terminations.filter(cable_end='B').first()
-            b_type = termination_b.termination._meta.model if termination_b else None
+            b_type = termination_b.termination._meta.model if termination_b else a_type
             self.form = forms.get_cable_form(a_type, b_type)

         return obj
@jaylik commented on GitHub (Jul 29, 2022): Guess this will have better solution later but temporary workaround below, ``` diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index 3f287317b..058b7b2e4 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -604,7 +604,13 @@ class CablePath(models.Model): """ Retrace the path from the currently-defined originating termination(s) """ - _new = self.from_origin(self.origins) + try: + _new = self.from_origin(self.origins) + except: + try: + _new = self.from_origin(self.destinations) + except: + _new = False if _new: self.path = _new.path self.is_complete = _new.is_complete @@ -641,7 +647,8 @@ class CablePath(models.Model): nodes = [] for node in step: ct_id, object_id = decompile_path_node(node) - nodes.append(prefetched[ct_id][object_id]) + if ct_id in prefetched and object_id in prefetched[ct_id]: + nodes.append(prefetched[ct_id][object_id]) path.append(nodes) return path diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index 8f62b0626..a2c465a61 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -236,7 +236,10 @@ class PathEndpoint(models.Model): """ Caching accessor for the attached CablePath's destination (if any) """ - return self._path.destinations if self._path else [] + try: + return self._path.destinations if self._path else [] + except: + return [] # diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 12e070e70..3589ed4b8 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -2850,7 +2850,7 @@ class CableEditView(generic.ObjectEditView): termination_a = obj.terminations.filter(cable_end='A').first() a_type = termination_a.termination._meta.model if termination_a else None termination_b = obj.terminations.filter(cable_end='B').first() - b_type = termination_b.termination._meta.model if termination_b else None + b_type = termination_b.termination._meta.model if termination_b else a_type self.form = forms.get_cable_form(a_type, b_type) return obj ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#6686