From 267a78d58d31e6ec19ab03adf385044c0e829550 Mon Sep 17 00:00:00 2001 From: Louis Date: Thu, 6 Aug 2026 16:07:47 +0200 Subject: [PATCH] fix: skip patchpanel automation on edits --- README.md | 12 +++---- netbox_utilities/__init__.py | 2 +- netbox_utilities/patchpanel.py | 8 +++-- netbox_utilities/tests/test_patchpanel.py | 44 ++++++++++++++++++++++- pyproject.toml | 2 +- 5 files changed, 57 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 267149f..1fd9a8d 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Release-Tag oder ein bestimmter Commit verwendet werden: ```bash /opt/netbox/venv/bin/pip install --upgrade --force-reinstall \ - "git+https://git.mrblake.cc/MrBlake/Netbox-Utilities.git@v0.7.3" + "git+https://git.mrblake.cc/MrBlake/Netbox-Utilities.git@v0.7.4" ``` Alternativ kann hinter dem `@` die vollständige Commit-ID stehen. @@ -142,15 +142,15 @@ weiter. Auch Kurzformen wie `F01` und `R1` werden als dieselbe Kennung erkannt. Fehlt die passende Gegenseite, bleibt der Port unverknüpft; die nachfolgenden Nummern rutschen nicht auf. -Die Zuordnung wird nach Änderungen am Gerät, nach dem Einbau eines Moduls und -nach dem Anlegen oder Umbenennen einzelner Front- beziehungsweise Rearports -erneut hergestellt. Dabei ersetzt die Patchpanel-Automatik abweichende -vorhandene Port-Mappings auf diesen Geräten durch die feste 1:1-Zuordnung. +Die Zuordnung wird ausschließlich beim erstmaligen Anlegen eines Patchpanels, +beim Einbau eines neuen Moduls sowie beim Anlegen eines neuen Front- oder +Rearports hergestellt. Das Bearbeiten eines vorhandenen Patchpanels, Moduls +oder Ports löst die Automatik nicht aus. Bei Installation oder Update findet kein automatischer Bestandslauf statt. Vorhandene Patchpanel-Geräte und deren Kabelpfade bleiben unverändert. Die Automatik greift erst, wenn ein betreffendes Gerät, Modul oder ein Front-/Rearport -anschließend neu angelegt beziehungsweise gespeichert wird. +neu angelegt wird. ### Mehrere Geräte mit NetBox Reorder Rack verschieben diff --git a/netbox_utilities/__init__.py b/netbox_utilities/__init__.py index 248273a..f384362 100644 --- a/netbox_utilities/__init__.py +++ b/netbox_utilities/__init__.py @@ -1,6 +1,6 @@ from netbox.plugins import PluginConfig, get_plugin_config -__version__ = "0.7.3" +__version__ = "0.7.4" class NetBoxUtilitiesConfig(PluginConfig): diff --git a/netbox_utilities/patchpanel.py b/netbox_utilities/patchpanel.py index 2ed1893..9d5a6f5 100644 --- a/netbox_utilities/patchpanel.py +++ b/netbox_utilities/patchpanel.py @@ -117,8 +117,8 @@ def synchronize_patchpanel(device_id): return len(pairs) -def _component_saved(sender, instance, raw=False, **kwargs): - if not raw and not _component_instantiation_active.get(): +def _component_saved(sender, instance, created=False, raw=False, **kwargs): + if created and not raw and not _component_instantiation_active.get(): synchronize_patchpanel(getattr(instance, "device_id", None)) @@ -131,6 +131,10 @@ def _wrap_component_container_save(model, device_id_getter): @wraps(original_save) def patchpanel_aware_save(instance, *args, **kwargs): + created = instance._state.adding + if not created: + return original_save(instance, *args, **kwargs) + token = _component_instantiation_active.set(True) try: result = original_save(instance, *args, **kwargs) diff --git a/netbox_utilities/tests/test_patchpanel.py b/netbox_utilities/tests/test_patchpanel.py index 6f498cc..3487c49 100644 --- a/netbox_utilities/tests/test_patchpanel.py +++ b/netbox_utilities/tests/test_patchpanel.py @@ -4,7 +4,14 @@ from unittest.mock import MagicMock, patch from django.test import SimpleTestCase -from netbox_utilities.patchpanel import is_patchpanel, pair_ports, port_identifier, retrace_patchpanel_paths +from netbox_utilities.patchpanel import ( + _component_saved, + _wrap_component_container_save, + is_patchpanel, + pair_ports, + port_identifier, + retrace_patchpanel_paths, +) def port(pk, name): @@ -12,6 +19,41 @@ def port(pk, name): class PatchpanelPairingTest(SimpleTestCase): + @patch("netbox_utilities.patchpanel.synchronize_patchpanel") + def test_component_edits_do_not_trigger_automation(self, synchronize): + component = SimpleNamespace(device_id=42) + + _component_saved(None, component, created=False) + + synchronize.assert_not_called() + + @patch("netbox_utilities.patchpanel.synchronize_patchpanel") + def test_new_components_trigger_automation(self, synchronize): + component = SimpleNamespace(device_id=42) + + _component_saved(None, component, created=True) + + synchronize.assert_called_once_with(42) + + @patch("netbox_utilities.patchpanel.synchronize_patchpanel") + def test_existing_device_save_does_not_trigger_automation(self, synchronize): + class Container: + def __init__(self, adding): + self._state = SimpleNamespace(adding=adding) + self.pk = 42 + + def save(self): + self._state.adding = False + return "saved" + + _wrap_component_container_save(Container, lambda instance: instance.pk) + + self.assertEqual(Container(adding=False).save(), "saved") + synchronize.assert_not_called() + + self.assertEqual(Container(adding=True).save(), "saved") + synchronize.assert_called_once_with(42) + def test_existing_device_migrations_are_noops(self): migration_names = ( "0004_patchpanel_port_mappings", diff --git a/pyproject.toml b/pyproject.toml index de56800..4e7f6b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "netbox-utilities" -version = "0.7.3" +version = "0.7.4" description = "Navigation, tenant utilities, patchpanel mapping, bulk uploads, and atomic rack reordering for NetBox 4.6" readme = "README.md" requires-python = ">=3.12"