From 8bc6d8cb231ad45cd8b97ffb26cc3d989c60c277 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Tue, 10 May 2022 11:58:48 -0400 Subject: [PATCH] Introduce CablePath.retrace() to handle deleted cables --- netbox/dcim/models/cables.py | 23 +++++++++++++++++++-- netbox/dcim/signals.py | 40 +++++++++++------------------------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index 8b2db479e..6b3d35137 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -356,8 +356,11 @@ class CablePath(models.Model): # Step 2: Determine the attached link (Cable or WirelessLink), if any link = terminations[0].link assert all(t.link is link for t in terminations[1:]) - if link is None: - # No attached link; abort + if link is None and len(path) == 1: + # If this is the start of the path and no link exists, return None + return None + elif link is None: + # Otherwise, halt the trace if no link exists break assert type(link) in (Cable, WirelessLink) @@ -463,6 +466,22 @@ class CablePath(models.Model): is_split=is_split ) + def retrace(self): + """ + Retrace the path from the currently-defined originating termination(s) + """ + _new = self.from_origin([ + path_node_to_object(node) for node in self.path[0] + ]) + if _new: + self.path = _new.path + self.is_complete = _new.is_complete + self.is_active = _new.is_active + self.is_split = _new.is_split + self.save() + else: + self.delete() + def get_path(self): """ Return the path as a list of prefetched objects. diff --git a/netbox/dcim/signals.py b/netbox/dcim/signals.py index 10d2ee9c5..128cedf66 100644 --- a/netbox/dcim/signals.py +++ b/netbox/dcim/signals.py @@ -126,34 +126,18 @@ def update_connected_endpoints(instance, created, raw=False, **kwargs): @receiver(post_delete, sender=Cable) +def retrace_cable_paths(instance, **kwargs): + """ + When a Cable is deleted, check for and update its connected endpoints + """ + for cablepath in CablePath.objects.filter(_nodes__contains=instance): + cablepath.retrace() + + +@receiver(post_delete, sender=CableTermination) def nullify_connected_endpoints(instance, **kwargs): """ - When a Cable is deleted, check for and update its two connected endpoints + Disassociate the Cable from the termination object. """ - logger = logging.getLogger('netbox.dcim.cable') - - # # Disassociate the Cable from its termination points - # if instance.termination_a: - # logger.debug(f"Nullifying termination A for cable {instance}") - # model = instance.termination_a_type.model_class() - # model.objects.filter(pk__in=instance.termination_a_ids).update(_link_peer_type=None, _link_peer_id=None) - # if instance.termination_b: - # logger.debug(f"Nullifying termination B for cable {instance}") - # model = instance.termination_b_type.model_class() - # model.objects.filter(pk__in=instance.termination_b_ids).update(_link_peer_type=None, _link_peer_id=None) - - # Delete and retrace any dependent cable paths - for cablepath in CablePath.objects.filter(_nodes__contains=instance): - cablepath.delete() - # TODO: Create new traces - # cp = CablePath.from_origin(cablepath.origin) - # if cp: - # CablePath.objects.filter(pk=cablepath.pk).update( - # _nodes=cp._nodes, - # destination_type=ContentType.objects.get_for_model(cp.destination) if cp.destination else None, - # destination_id=cp.destination.pk if cp.destination else None, - # is_active=cp.is_active, - # is_split=cp.is_split - # ) - # else: - # cablepath.delete() + model = instance.termination_type.model_class() + model.objects.filter(pk=instance.termination_id).update(_link_peer_type=None, _link_peer_id=None)