Skip to content

Commit aa7eeda

Browse files
committed
Remove many-to-one profiles
1 parent a75dee7 commit aa7eeda

File tree

5 files changed

+140
-192
lines changed

5 files changed

+140
-192
lines changed

netbox/dcim/cable_profiles.py

Lines changed: 12 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ class BaseCableProfile:
1212
# Number of A & B terminations must match
1313
symmetrical = True
1414

15-
# Whether terminations on either side of the cable have a numeric position
16-
a_side_numbered = True
17-
b_side_numbered = True
18-
1915
def clean(self, cable):
2016
if self.a_max_connections and len(cable.a_terminations) > self.a_max_connections:
2117
raise ValidationError({
@@ -54,24 +50,21 @@ def get_mapped_position(self, side, position):
5450

5551
def get_peer_terminations(self, terminations, position_stack):
5652
local_end = terminations[0].cable_end
57-
position = None
58-
59-
# Pop the position stack if necessary
60-
if (local_end == 'A' and self.b_side_numbered) or (local_end == 'B' and self.a_side_numbered):
61-
try:
62-
position = position_stack.pop()[0]
63-
except IndexError:
64-
# TODO: Should this raise an error?
65-
# Bottomed out of stack
66-
pass
67-
6853
qs = CableTermination.objects.filter(
6954
cable=terminations[0].cable,
7055
cable_end=terminations[0].opposite_cable_end
7156
)
72-
if position is not None:
73-
qs = qs.filter(position=self.get_mapped_position(local_end, position))
74-
return qs
57+
58+
# TODO: Optimize this to use a single query under any condition
59+
if position_stack:
60+
# Attempt to find a peer termination at the same position currently in the stack. Pop the stack only if
61+
# we find one. Otherwise, return any peer terminations with a null position.
62+
position = self.get_mapped_position(local_end, position_stack[-1][0])
63+
if peers := qs.filter(position=position):
64+
position_stack.pop()
65+
return peers
66+
67+
return qs.filter(position=None)
7568

7669

7770
class StraightSingleCableProfile(BaseCableProfile):
@@ -84,20 +77,6 @@ class StraightMultiCableProfile(BaseCableProfile):
8477
b_max_connections = None
8578

8679

87-
class AToManyCableProfile(BaseCableProfile):
88-
a_max_connections = 1
89-
b_max_connections = None
90-
symmetrical = False
91-
a_side_numbered = False
92-
93-
94-
class BToManyCableProfile(BaseCableProfile):
95-
a_max_connections = None
96-
b_max_connections = 1
97-
symmetrical = False
98-
b_side_numbered = False
99-
100-
10180
class Shuffle2x2MPO8CableProfile(BaseCableProfile):
10281
a_max_connections = 8
10382
b_max_connections = 8
@@ -129,7 +108,7 @@ class Shuffle4x4MPO8CableProfile(BaseCableProfile):
129108
7: 6,
130109
8: 8,
131110
}
132-
# B side to A side position mapping
111+
# B side to A side position mapping (reverse of _a_mapping)
133112
_b_mapping = {v: k for k, v in _a_mapping.items()}
134113

135114
def get_mapped_position(self, side, position):

netbox/dcim/choices.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,17 +1720,12 @@ class PortTypeChoices(ChoiceSet):
17201720
class CableProfileChoices(ChoiceSet):
17211721
STRAIGHT_SINGLE = 'straight-single'
17221722
STRAIGHT_MULTI = 'straight-multi'
1723-
A_TO_MANY = 'a-to-many'
1724-
B_TO_MANY = 'b-to-many'
17251723
SHUFFLE_2X2_MPO8 = 'shuffle-2x2-mpo8'
17261724
SHUFFLE_4X4_MPO8 = 'shuffle-4x4-mpo8'
17271725

17281726
CHOICES = (
17291727
(STRAIGHT_SINGLE, _('Straight (single position)')),
17301728
(STRAIGHT_MULTI, _('Straight (multi-position)')),
1731-
# TODO: Better names for many-to-one profiles?
1732-
(A_TO_MANY, _('A to many')),
1733-
(B_TO_MANY, _('B to many')),
17341729
(SHUFFLE_2X2_MPO8, _('Shuffle (2x2 MPO8)')),
17351730
(SHUFFLE_4X4_MPO8, _('Shuffle (4x4 MPO8)')),
17361731
)

netbox/dcim/models/cables.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from utilities.querysets import RestrictedQuerySet
2222
from utilities.serialization import deserialize_object, serialize_object
2323
from wireless.models import WirelessLink
24-
from .device_components import FrontPort, RearPort, PathEndpoint
24+
from .device_components import FrontPort, PathEndpoint, RearPort
2525

2626
__all__ = (
2727
'Cable',
@@ -136,8 +136,6 @@ def profile_class(self):
136136
return {
137137
CableProfileChoices.STRAIGHT_SINGLE: cable_profiles.StraightSingleCableProfile,
138138
CableProfileChoices.STRAIGHT_MULTI: cable_profiles.StraightMultiCableProfile,
139-
CableProfileChoices.A_TO_MANY: cable_profiles.AToManyCableProfile,
140-
CableProfileChoices.B_TO_MANY: cable_profiles.BToManyCableProfile,
141139
CableProfileChoices.SHUFFLE_2X2_MPO8: cable_profiles.Shuffle2x2MPO8CableProfile,
142140
CableProfileChoices.SHUFFLE_4X4_MPO8: cable_profiles.Shuffle4x4MPO8CableProfile,
143141
}.get(self.profile)
@@ -328,7 +326,6 @@ def update_terminations(self):
328326
Create/delete CableTerminations for this Cable to reflect its current state.
329327
"""
330328
a_terminations, b_terminations = self.get_terminations()
331-
profile = self.profile_class if self.profile else None
332329

333330
# Delete any stale CableTerminations
334331
for termination, ct in a_terminations.items():
@@ -341,11 +338,11 @@ def update_terminations(self):
341338
# Save any new CableTerminations
342339
for i, termination in enumerate(self.a_terminations, start=1):
343340
if not termination.pk or termination not in a_terminations:
344-
position = i if profile and profile.a_side_numbered else None
341+
position = i if self.profile and isinstance(termination, PathEndpoint) else None
345342
CableTermination(cable=self, cable_end='A', position=position, termination=termination).save()
346343
for i, termination in enumerate(self.b_terminations, start=1):
347344
if not termination.pk or termination not in b_terminations:
348-
position = i if profile and profile.b_side_numbered else None
345+
position = i if self.profile and isinstance(termination, PathEndpoint) else None
349346
CableTermination(cable=self, cable_end='B', position=position, termination=termination).save()
350347

351348

netbox/dcim/tests/test_cablepaths.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2191,6 +2191,55 @@ def test_222_single_path_via_multiple_singleposition_rear_ports(self):
21912191
CableTraceSVG(interface1).render()
21922192
CableTraceSVG(interface2).render()
21932193

2194+
def test_223_single_path_via_multiple_pass_throughs_with_breakouts(self):
2195+
"""
2196+
[IF1] --C1-- [FP1] [RP1] --C2-- [IF3]
2197+
[IF2] [FP2] [RP2] [IF4]
2198+
"""
2199+
interface1 = Interface.objects.create(device=self.device, name='Interface 1')
2200+
interface2 = Interface.objects.create(device=self.device, name='Interface 2')
2201+
interface3 = Interface.objects.create(device=self.device, name='Interface 3')
2202+
interface4 = Interface.objects.create(device=self.device, name='Interface 4')
2203+
rearport1 = RearPort.objects.create(device=self.device, name='Rear Port 1', positions=1)
2204+
rearport2 = RearPort.objects.create(device=self.device, name='Rear Port 2', positions=1)
2205+
frontport1 = FrontPort.objects.create(
2206+
device=self.device, name='Front Port 1', rear_port=rearport1, rear_port_position=1
2207+
)
2208+
frontport2 = FrontPort.objects.create(
2209+
device=self.device, name='Front Port 2', rear_port=rearport2, rear_port_position=1
2210+
)
2211+
2212+
# Create cables
2213+
cable1 = Cable(
2214+
a_terminations=[interface1, interface2],
2215+
b_terminations=[frontport1, frontport2]
2216+
)
2217+
cable1.save()
2218+
cable2 = Cable(
2219+
a_terminations=[rearport1, rearport2],
2220+
b_terminations=[interface3, interface4]
2221+
)
2222+
cable2.save()
2223+
2224+
# Validate paths
2225+
self.assertPathExists(
2226+
(
2227+
[interface1, interface2], cable1, [frontport1, frontport2],
2228+
[rearport1, rearport2], cable2, [interface3, interface4],
2229+
),
2230+
is_complete=True,
2231+
is_active=True
2232+
)
2233+
self.assertPathExists(
2234+
(
2235+
[interface3, interface4], cable2, [rearport1, rearport2],
2236+
[frontport1, frontport2], cable1, [interface1, interface2],
2237+
),
2238+
is_complete=True,
2239+
is_active=True
2240+
)
2241+
self.assertEqual(CablePath.objects.count(), 2)
2242+
21942243
def test_301_create_path_via_existing_cable(self):
21952244
"""
21962245
[IF1] --C1-- [FP1] [RP1] --C2-- [RP2] [FP2] --C3-- [IF2]

0 commit comments

Comments
 (0)