1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Record wireless links as part of cable path

This commit is contained in:
jeremystretch
2021-10-13 13:28:14 -04:00
parent 445e16f668
commit 138af27bf7
5 changed files with 59 additions and 38 deletions

View File

@ -379,7 +379,7 @@ class CablePath(BigIDModel):
""" """
from circuits.models import CircuitTermination 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 return None
destination = None destination = None
@ -389,12 +389,12 @@ class CablePath(BigIDModel):
is_split = False is_split = False
node = origin node = origin
while node.cable is not None: while node.link is not None:
if node.cable.status != CableStatusChoices.STATUS_CONNECTED: if hasattr(node.link, 'status') and node.link.status != CableStatusChoices.STATUS_CONNECTED:
is_active = False is_active = False
# Follow the cable to its far-end termination # Follow the link to its far-end termination
path.append(object_to_path_node(node.cable)) path.append(object_to_path_node(node.link))
peer_termination = node.get_cable_peer() peer_termination = node.get_cable_peer()
# Follow a FrontPort to its corresponding RearPort # Follow a FrontPort to its corresponding RearPort

View File

@ -157,6 +157,13 @@ class CableTermination(models.Model):
def parent_object(self): def parent_object(self):
raise NotImplementedError("CableTermination models must implement parent_object()") 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): class PathEndpoint(models.Model):
""" """
@ -657,6 +664,10 @@ class Interface(ComponentModel, BaseInterface, CableTermination, PathEndpoint):
def is_lag(self): def is_lag(self):
return self.type == InterfaceTypeChoices.TYPE_LAG return self.type == InterfaceTypeChoices.TYPE_LAG
@property
def link(self):
return self.cable or self.wireless_link
# #
# Pass-through ports # Pass-through ports

View File

@ -2,37 +2,11 @@ import logging
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models.signals import post_save, post_delete, pre_delete from django.db.models.signals import post_save, post_delete, pre_delete
from django.db import transaction
from django.dispatch import receiver from django.dispatch import receiver
from .choices import CableStatusChoices from .choices import CableStatusChoices
from .models import Cable, CablePath, Device, PathEndpoint, PowerPanel, Rack, Location, VirtualChassis from .models import Cable, CablePath, Device, PathEndpoint, PowerPanel, Rack, Location, VirtualChassis
from .utils import create_cablepath, rebuild_paths
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)
# #

View File

@ -1,4 +1,5 @@
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db import transaction
def compile_path_node(ct_id, object_id): 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_id, object_id = decompile_path_node(repr)
ct = ContentType.objects.get_for_id(ct_id) ct = ContentType.objects.get_for_id(ct_id)
return ct.model_class().objects.get(pk=object_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)

View File

@ -3,7 +3,8 @@ import logging
from django.db.models.signals import post_save, post_delete from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver 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 from .models import WirelessLink
@ -12,11 +13,10 @@ from .models import WirelessLink
# #
@receiver(post_save, sender=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. When a WirelessLink is saved, save a reference to it on each connected interface.
""" """
print('update_connected_interfaces')
logger = logging.getLogger('netbox.wireless.wirelesslink') logger = logging.getLogger('netbox.wireless.wirelesslink')
if raw: if raw:
logger.debug(f"Skipping endpoint updates for imported wireless link {instance}") 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: if instance.interface_a.wireless_link != instance:
logger.debug(f"Updating interface A for wireless link {instance}") logger.debug(f"Updating interface A for wireless link {instance}")
instance.interface_a.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() instance.interface_a.save()
if instance.interface_b.cable != instance: if instance.interface_b.cable != instance:
logger.debug(f"Updating interface B for wireless link {instance}") logger.debug(f"Updating interface B for wireless link {instance}")
instance.interface_b.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() 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) @receiver(post_delete, sender=WirelessLink)
def nullify_connected_interfaces(instance, **kwargs): def nullify_connected_interfaces(instance, **kwargs):
""" """
When a WirelessLink is deleted, update its two connected Interfaces When a WirelessLink is deleted, update its two connected Interfaces
""" """
print('nullify_connected_interfaces')
logger = logging.getLogger('netbox.wireless.wirelesslink') logger = logging.getLogger('netbox.wireless.wirelesslink')
if instance.interface_a is not None: if instance.interface_a is not None:
@ -56,3 +60,8 @@ def nullify_connected_interfaces(instance, **kwargs):
_cable_peer_type=None, _cable_peer_type=None,
_cable_peer_id=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()