fix: pair LC patchpanel ports by number

This commit is contained in:
2026-08-31 11:22:53 +02:00
parent f3d658b52b
commit ff8dacc68c
7 changed files with 46 additions and 4 deletions
+5
View File
@@ -30,6 +30,11 @@ Ab Version `0.10.3` kann dort optional eine VLAN-Gruppe ausgewählt werden. Sie
dient als dynamischer Filter für die VLAN-Mehrfachauswahl und wird nicht als dient als dynamischer Filter für die VLAN-Mehrfachauswahl und wird nicht als
zusätzliche Eigenschaft der Verbindung gespeichert. zusätzliche Eigenschaft der Verbindung gespeichert.
Ab Version `0.10.4` verwendet die Patchpanel-Automatik die numerische
Portkennung unabhängig von zusätzlichen Bezeichnungen wie `LC`, `Front` oder
`Rear`. Damit werden auch kleinere LC/LC-Patchpanels, etwa mit sechs Ports,
fortlaufend eins-zu-eins zugeordnet.
## Kompatibilität ## Kompatibilität
- NetBox `>=4.6.5,<4.7` - NetBox `>=4.6.5,<4.7`
+1 -1
View File
@@ -1,6 +1,6 @@
from netbox.plugins import PluginConfig, get_plugin_config from netbox.plugins import PluginConfig, get_plugin_config
__version__ = "0.10.3" __version__ = "0.10.4"
class NetBoxUtilitiesConfig(PluginConfig): class NetBoxUtilitiesConfig(PluginConfig):
+3
View File
@@ -26,6 +26,9 @@ def port_identifier(port):
name = str(getattr(port, "name", "")).casefold() name = str(getattr(port, "name", "")).casefold()
name = re.sub(r"\b(?:front|rear)(?:[\s_-]*port)?(?=\b|\d)", " ", name) name = re.sub(r"\b(?:front|rear)(?:[\s_-]*port)?(?=\b|\d)", " ", name)
name = re.sub(r"^(?:fp|rp|f|r)(?=\s*[-_.:/]?\s*\d)", "", name) name = re.sub(r"^(?:fp|rp|f|r)(?=\s*[-_.:/]?\s*\d)", "", name)
numbers = tuple(int(token) for token in re.findall(r"\d+", name))
if numbers:
return numbers
tokens = re.findall(r"\d+|[^\W\d_]+", name) tokens = re.findall(r"\d+|[^\W\d_]+", name)
identifier = tuple(int(token) if token.isdigit() else token for token in tokens) identifier = tuple(int(token) if token.isdigit() else token for token in tokens)
return identifier or None return identifier or None
@@ -0,0 +1,34 @@
from types import SimpleNamespace
from django.test import SimpleTestCase
from netbox_utilities.patchpanel import pair_ports, port_identifier
def port(pk, name):
return SimpleNamespace(pk=pk, name=name)
class LCPatchpanelPairingTest(SimpleTestCase):
def test_uses_numeric_identifier_despite_connector_labels(self):
self.assertEqual(port_identifier(port(1, "LC Front 01")), (1,))
self.assertEqual(port_identifier(port(2, "Rear 1")), (1,))
def test_pairs_all_six_lc_ports_by_number(self):
front_ports = [port(number, f"LC Front {number}") for number in range(1, 7)]
rear_ports = [port(100 + number, f"Rear {number}") for number in range(1, 7)]
pairs = pair_ports(front_ports, rear_ports)
self.assertEqual(
[(front.pk, rear.pk) for front, rear in pairs],
[(number, 100 + number) for number in range(1, 7)],
)
def test_keeps_compound_numeric_identifiers_distinct(self):
front_ports = [port(12, "Cassette 1 LC 2"), port(13, "Cassette 1 LC 3")]
rear_ports = [port(112, "Rear 1/2"), port(113, "Rear 1/3")]
pairs = pair_ports(front_ports, rear_ports)
self.assertEqual([(front.pk, rear.pk) for front, rear in pairs], [(12, 112), (13, 113)])
+1 -1
View File
@@ -449,7 +449,7 @@ class ReorderRackFrontendTest(SimpleTestCase):
self.assertEqual(template_name, "netbox_utilities/reorder_rack.html") self.assertEqual(template_name, "netbox_utilities/reorder_rack.html")
self.assertEqual(context["reorder_devices"][0]["label"], "LEO-Fritzbox") self.assertEqual(context["reorder_devices"][0]["label"], "LEO-Fritzbox")
self.assertEqual(context["reorder_devices"][0]["grid_width"], 6) self.assertEqual(context["reorder_devices"][0]["grid_width"], 6)
self.assertEqual(context["asset_version"], "0.10.3") self.assertEqual(context["asset_version"], "0.10.4")
self.assertIs(context["reorder_rack_width_data"], get_width_data.return_value) self.assertIs(context["reorder_rack_width_data"], get_width_data.return_value)
get_width_data.assert_called_once() get_width_data.assert_called_once()
self.assertIs(get_width_data.call_args.kwargs["rack"], rack) self.assertIs(get_width_data.call_args.kwargs["rack"], rack)
@@ -246,7 +246,7 @@ class TopologyViewsRackWidthTest(SimpleTestCase):
self.assertIn('id="netbox-utilities-topology-rack-width-styles"', html) self.assertIn('id="netbox-utilities-topology-rack-width-styles"', html)
self.assertIn('.rack-device[href="/dcim/devices/334/"]', html) self.assertIn('.rack-device[href="/dcim/devices/334/"]', html)
self.assertIn("left: calc(50% + 3px) !important", html) self.assertIn("left: calc(50% + 3px) !important", html)
self.assertIn("netbox_utilities/topology-rack-width.js?v=0.10.3", html) self.assertIn("netbox_utilities/topology-rack-width.js?v=0.10.4", html)
self.assertIn("left:calc(0% + 3px)!important", html) self.assertIn("left:calc(0% + 3px)!important", html)
self.assertIn("left:calc(50% + 3px)!important", html) self.assertIn("left:calc(50% + 3px)!important", html)
self.assertIn("width:calc(50% - 6px)!important", html) self.assertIn("width:calc(50% - 6px)!important", html)
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "netbox-utilities" name = "netbox-utilities"
version = "0.10.3" version = "0.10.4"
description = "Navigation, tenant utilities, connection VLANs, partial-width racks, and bulk operations for NetBox 4.6" description = "Navigation, tenant utilities, connection VLANs, partial-width racks, and bulk operations for NetBox 4.6"
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"