From 138af27bf7c18d6833233ff84029eb8ef6a186bf Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 13 Oct 2021 13:28:14 -0400 Subject: [PATCH] Record wireless links as part of cable path --- netbox/dcim/models/cables.py | 10 ++++----- netbox/dcim/models/device_components.py | 11 ++++++++++ netbox/dcim/signals.py | 28 +------------------------ netbox/dcim/utils.py | 27 ++++++++++++++++++++++++ netbox/wireless/signals.py | 21 +++++++++++++------ 5 files changed, 59 insertions(+), 38 deletions(-) diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index c3f8cac3f..6c61c0712 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -379,7 +379,7 @@ class CablePath(BigIDModel): """ from circuits.models import CircuitTermination - if origin is None or origin.cable is None: + if origin is None or origin.link is None: return None destination = None @@ -389,12 +389,12 @@ class CablePath(BigIDModel): is_split = False node = origin - while node.cable is not None: - if node.cable.status != CableStatusChoices.STATUS_CONNECTED: + while node.link is not None: + if hasattr(node.link, 'status') and node.link.status != CableStatusChoices.STATUS_CONNECTED: is_active = False - # Follow the cable to its far-end termination - path.append(object_to_path_node(node.cable)) + # Follow the link to its far-end termination + path.append(object_to_path_node(node.link)) peer_termination = node.get_cable_peer() # Follow a FrontPort to its corresponding RearPort diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index dc8246990..38e902f22 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -157,6 +157,13 @@ class CableTermination(models.Model): def parent_object(self): raise NotImplementedError("CableTermination models must implement parent_object()") + @property + def link(self): + """ + Generic wrapper for a Cable, WirelessLink, or some other relation to a connected termination. + """ + return self.cable + class PathEndpoint(models.Model): """ @@ -657,6 +664,10 @@ class Interface(ComponentModel, BaseInterface, CableTermination, PathEndpoint): def is_lag(self): return self.type == InterfaceTypeChoices.TYPE_LAG + @property + def link(self): + return self.cable or self.wireless_link + # # Pass-through ports diff --git a/netbox/dcim/signals.py b/netbox/dcim/signals.py index 9fc68ee70..942bf04e4 100644 --- a/netbox/dcim/signals.py +++ b/netbox/dcim/signals.py @@ -2,37 +2,11 @@ import logging from django.contrib.contenttypes.models import ContentType from django.db.models.signals import post_save, post_delete, pre_delete -from django.db import transaction from django.dispatch import receiver from .choices import CableStatusChoices from .models import Cable, CablePath, Device, PathEndpoint, PowerPanel, Rack, Location, VirtualChassis - - -def create_cablepath(node): - """ - Create CablePaths for all paths originating from the specified node. - """ - cp = CablePath.from_origin(node) - if cp: - try: - cp.save() - except Exception as e: - print(node, node.pk) - raise e - - -def rebuild_paths(obj): - """ - Rebuild all CablePaths which traverse the specified node - """ - cable_paths = CablePath.objects.filter(path__contains=obj) - - with transaction.atomic(): - for cp in cable_paths: - cp.delete() - if cp.origin: - create_cablepath(cp.origin) +from .utils import create_cablepath, rebuild_paths # diff --git a/netbox/dcim/utils.py b/netbox/dcim/utils.py index 91c5c7c77..ec3a44603 100644 --- a/netbox/dcim/utils.py +++ b/netbox/dcim/utils.py @@ -1,4 +1,5 @@ from django.contrib.contenttypes.models import ContentType +from django.db import transaction def compile_path_node(ct_id, object_id): @@ -26,3 +27,29 @@ def path_node_to_object(repr): ct_id, object_id = decompile_path_node(repr) ct = ContentType.objects.get_for_id(ct_id) return ct.model_class().objects.get(pk=object_id) + + +def create_cablepath(node): + """ + Create CablePaths for all paths originating from the specified node. + """ + from dcim.models import CablePath + + cp = CablePath.from_origin(node) + if cp: + cp.save() + + +def rebuild_paths(obj): + """ + Rebuild all CablePaths which traverse the specified node + """ + from dcim.models import CablePath + + cable_paths = CablePath.objects.filter(path__contains=obj) + + with transaction.atomic(): + for cp in cable_paths: + cp.delete() + if cp.origin: + create_cablepath(cp.origin) diff --git a/netbox/wireless/signals.py b/netbox/wireless/signals.py index a42566a00..b8dc8a186 100644 --- a/netbox/wireless/signals.py +++ b/netbox/wireless/signals.py @@ -3,7 +3,8 @@ import logging from django.db.models.signals import post_save, post_delete from django.dispatch import receiver -from dcim.models import Interface +from dcim.models import CablePath, Interface +from dcim.utils import create_cablepath from .models import WirelessLink @@ -12,11 +13,10 @@ from .models import WirelessLink # @receiver(post_save, sender=WirelessLink) -def update_connected_interfaces(instance, raw=False, **kwargs): +def update_connected_interfaces(instance, created, raw=False, **kwargs): """ When a WirelessLink is saved, save a reference to it on each connected interface. """ - print('update_connected_interfaces') logger = logging.getLogger('netbox.wireless.wirelesslink') if raw: logger.debug(f"Skipping endpoint updates for imported wireless link {instance}") @@ -25,21 +25,25 @@ def update_connected_interfaces(instance, raw=False, **kwargs): if instance.interface_a.wireless_link != instance: logger.debug(f"Updating interface A for wireless link {instance}") instance.interface_a.wireless_link = instance - # instance.interface_a._cable_peer = instance.interface_b # TODO: Rename _cable_peer field + instance.interface_a._cable_peer = instance.interface_b # TODO: Rename _cable_peer field instance.interface_a.save() if instance.interface_b.cable != instance: logger.debug(f"Updating interface B for wireless link {instance}") instance.interface_b.wireless_link = instance - # instance.interface_b._cable_peer = instance.interface_a + instance.interface_b._cable_peer = instance.interface_a instance.interface_b.save() + # Create/update cable paths + if created: + for interface in (instance.interface_a, instance.interface_b): + create_cablepath(interface) + @receiver(post_delete, sender=WirelessLink) def nullify_connected_interfaces(instance, **kwargs): """ When a WirelessLink is deleted, update its two connected Interfaces """ - print('nullify_connected_interfaces') logger = logging.getLogger('netbox.wireless.wirelesslink') if instance.interface_a is not None: @@ -56,3 +60,8 @@ def nullify_connected_interfaces(instance, **kwargs): _cable_peer_type=None, _cable_peer_id=None ) + + # Delete and retrace any dependent cable paths + for cablepath in CablePath.objects.filter(path__contains=instance): + print(f'Deleting cable path {cablepath.pk}') + cablepath.delete()