From 619728a0489b5418415451befe0fbad96852187e Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 1 Dec 2025 13:35:14 -0500 Subject: [PATCH] Ensure cable paths are retraced when port mappings are changes via form --- netbox/dcim/forms/mixins.py | 12 ++++++++++++ netbox/dcim/models/cables.py | 2 +- netbox/dcim/signals.py | 26 +++++++++++++------------- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/netbox/dcim/forms/mixins.py b/netbox/dcim/forms/mixins.py index 91a0501dd..b2fc46bc3 100644 --- a/netbox/dcim/forms/mixins.py +++ b/netbox/dcim/forms/mixins.py @@ -1,6 +1,8 @@ from django import forms from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ObjectDoesNotExist, ValidationError +from django.db import connection +from django.db.models.signals import post_save from django.utils.translation import gettext_lazy as _ from dcim.constants import LOCATION_SCOPE_TYPES @@ -191,3 +193,13 @@ class FrontPortFormMixin(forms.Form): }) ) self.port_mapping_model.objects.bulk_create(mappings) + # Send post_save signals + for mapping in mappings: + post_save.send( + sender=PortMapping, + instance=mapping, + created=True, + raw=False, + using=connection, + update_fields=None + ) diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index 9d8c2c290..e75b4c110 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -31,7 +31,7 @@ __all__ = ( 'CableTermination', ) -logger = logging.getLogger(__name__) +logger = logging.getLogger(f'netbox.{__name__}') trace_paths = Signal() diff --git a/netbox/dcim/signals.py b/netbox/dcim/signals.py index 81cb0ae6c..5ec1f68d7 100644 --- a/netbox/dcim/signals.py +++ b/netbox/dcim/signals.py @@ -1,5 +1,6 @@ import logging +from django.db.models import Q from django.db.models.signals import post_save, post_delete from django.dispatch import receiver @@ -7,7 +8,7 @@ from dcim.choices import CableEndChoices, LinkStatusChoices from virtualization.models import VMInterface from .models import ( Cable, CablePath, CableTermination, ConsolePort, ConsoleServerPort, Device, DeviceBay, FrontPort, Interface, - InventoryItem, ModuleBay, PathEndpoint, PowerOutlet, PowerPanel, PowerPort, Rack, RearPort, Location, + InventoryItem, ModuleBay, PathEndpoint, PortMapping, PowerOutlet, PowerPanel, PowerPort, Rack, RearPort, Location, VirtualChassis, ) from .models.cables import trace_paths @@ -135,6 +136,17 @@ def retrace_cable_paths(instance, **kwargs): cablepath.retrace() +@receiver((post_delete, post_save), sender=PortMapping) +def update_passthrough_port_paths(instance, **kwargs): + """ + When a PortMapping is created or deleted, retrace any CablePaths which traverse its front and/or rear ports. + """ + for cablepath in CablePath.objects.filter( + Q(_nodes__contains=instance.front_port) | Q(_nodes__contains=instance.rear_port) + ): + cablepath.retrace() + + @receiver(post_delete, sender=CableTermination) def nullify_connected_endpoints(instance, **kwargs): """ @@ -150,18 +162,6 @@ def nullify_connected_endpoints(instance, **kwargs): cablepath.retrace() -# TODO: Adapt signal handler to act on changes to port mappings -@receiver(post_save, sender=FrontPort) -def extend_rearport_cable_paths(instance, created, raw, **kwargs): - """ - When a new FrontPort is created, add it to any CablePaths which end at its corresponding RearPort. - """ - if created and not raw: - for mapping in instance.mappings.prefetch_related('rear_port'): - for cablepath in CablePath.objects.filter(_nodes__contains=mapping.rear_port): - cablepath.retrace() - - @receiver(post_save, sender=Interface) @receiver(post_save, sender=VMInterface) def update_mac_address_interface(instance, created, raw, **kwargs):