feat(dcim): Add 1C8P:8C1P breakout cable profile
Introduce Breakout1C8Px8C1PCableProfile to map a single 8-position connector to eight single-position connectors. Add profile choice, registration, and bidirectional link peer tests for the new breakout. Fixes #22279
This commit is contained in:
parent
1077ff7169
commit
4da65854e1
|
|
@ -409,6 +409,39 @@ class Breakout1C6Px6C1PCableProfile(BaseCableProfile):
|
|||
}
|
||||
|
||||
|
||||
class Breakout1C8Px8C1PCableProfile(BaseCableProfile):
|
||||
a_connectors = {
|
||||
1: 8,
|
||||
}
|
||||
b_connectors = {
|
||||
1: 1,
|
||||
2: 1,
|
||||
3: 1,
|
||||
4: 1,
|
||||
5: 1,
|
||||
6: 1,
|
||||
7: 1,
|
||||
8: 1,
|
||||
}
|
||||
_mapping = {
|
||||
(1, 1): (1, 1),
|
||||
(1, 2): (2, 1),
|
||||
(1, 3): (3, 1),
|
||||
(1, 4): (4, 1),
|
||||
(1, 5): (5, 1),
|
||||
(1, 6): (6, 1),
|
||||
(1, 7): (7, 1),
|
||||
(1, 8): (8, 1),
|
||||
(2, 1): (1, 2),
|
||||
(3, 1): (1, 3),
|
||||
(4, 1): (1, 4),
|
||||
(5, 1): (1, 5),
|
||||
(6, 1): (1, 6),
|
||||
(7, 1): (1, 7),
|
||||
(8, 1): (1, 8),
|
||||
}
|
||||
|
||||
|
||||
class Trunk2C4PShuffleCableProfile(BaseCableProfile):
|
||||
a_connectors = {
|
||||
1: 4,
|
||||
|
|
|
|||
|
|
@ -1787,6 +1787,7 @@ class CableProfileChoices(ChoiceSet):
|
|||
BREAKOUT_1C2P_2C1P = 'breakout-1c2p-2c1p'
|
||||
BREAKOUT_1C4P_4C1P = 'breakout-1c4p-4c1p'
|
||||
BREAKOUT_1C6P_6C1P = 'breakout-1c6p-6c1p'
|
||||
BREAKOUT_1C8P_8C1P = 'breakout-1c8p-8c1p'
|
||||
BREAKOUT_2C4P_8C1P_SHUFFLE = 'breakout-2c4p-8c1p-shuffle'
|
||||
|
||||
CHOICES = (
|
||||
|
|
@ -1827,6 +1828,7 @@ class CableProfileChoices(ChoiceSet):
|
|||
(BREAKOUT_1C2P_2C1P, _('1C2P:2C1P breakout')),
|
||||
(BREAKOUT_1C4P_4C1P, _('1C4P:4C1P breakout')),
|
||||
(BREAKOUT_1C6P_6C1P, _('1C6P:6C1P breakout')),
|
||||
(BREAKOUT_1C8P_8C1P, _('1C8P:8C1P breakout')),
|
||||
(BREAKOUT_2C4P_8C1P_SHUFFLE, _('2C4P:8C1P breakout (shuffle)')),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -207,6 +207,7 @@ class Cable(PrimaryModel):
|
|||
CableProfileChoices.BREAKOUT_1C2P_2C1P: cable_profiles.Breakout1C2Px2C1PCableProfile,
|
||||
CableProfileChoices.BREAKOUT_1C4P_4C1P: cable_profiles.Breakout1C4Px4C1PCableProfile,
|
||||
CableProfileChoices.BREAKOUT_1C6P_6C1P: cable_profiles.Breakout1C6Px6C1PCableProfile,
|
||||
CableProfileChoices.BREAKOUT_1C8P_8C1P: cable_profiles.Breakout1C8Px8C1PCableProfile,
|
||||
CableProfileChoices.BREAKOUT_2C4P_8C1P_SHUFFLE: cable_profiles.Breakout2C4Px8C1PShuffleCableProfile,
|
||||
}.get(self.profile)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from django.test import tag
|
|||
|
||||
from dcim.cable_profiles import (
|
||||
Breakout1C4Px4C1PCableProfile,
|
||||
Breakout1C8Px8C1PCableProfile,
|
||||
Single1C1PCableProfile,
|
||||
Single1C4PCableProfile,
|
||||
Trunk2C2PCableProfile,
|
||||
|
|
@ -74,6 +75,32 @@ class CableProfileLinkPeerTestCase(BaseCablePathTestCase):
|
|||
for interface in interfaces[2:4] + interfaces[6:8]:
|
||||
self.assertEqual(interface.link_peers, [rear_ports[1]])
|
||||
|
||||
def test_breakout_1c8p_8c1p_link_peers(self):
|
||||
"""
|
||||
Link peers for a 1C8P:8C1P breakout map the single A-side connector's
|
||||
eight positions to the eight B-side connectors, and each back to it.
|
||||
"""
|
||||
a_interface = Interface.objects.create(device=self.device, name='Interface A')
|
||||
b_interfaces = [
|
||||
Interface.objects.create(device=self.device, name=f'Interface B{i}') for i in range(1, 9)
|
||||
]
|
||||
|
||||
cable = Cable(
|
||||
profile=CableProfileChoices.BREAKOUT_1C8P_8C1P,
|
||||
a_terminations=[a_interface],
|
||||
b_terminations=b_interfaces,
|
||||
)
|
||||
cable.clean()
|
||||
cable.save()
|
||||
|
||||
a_interface.refresh_from_db()
|
||||
for iface in b_interfaces:
|
||||
iface.refresh_from_db()
|
||||
|
||||
self.assertEqual(a_interface.link_peers, b_interfaces)
|
||||
for iface in b_interfaces:
|
||||
self.assertEqual(iface.link_peers, [a_interface])
|
||||
|
||||
|
||||
class CableProfilePeerTerminationTestCase(BaseCablePathTestCase):
|
||||
"""
|
||||
|
|
@ -228,6 +255,32 @@ class CableProfilePeerTerminationTestCase(BaseCablePathTestCase):
|
|||
b_pairs = [(iface, 1) for iface in self.interfaces[9:13]]
|
||||
self._assert_batch_matches_singular(profile, b_pairs)
|
||||
|
||||
def test_breakout_1c8p_profile(self):
|
||||
"""
|
||||
Batch resolution on a 1C8P:8C1P breakout maps one A-side connector's
|
||||
eight positions to eight B-side connectors.
|
||||
"""
|
||||
cable = Cable(
|
||||
profile=CableProfileChoices.BREAKOUT_1C8P_8C1P,
|
||||
a_terminations=[self.interfaces[0]],
|
||||
b_terminations=self.interfaces[1:9],
|
||||
)
|
||||
cable.clean()
|
||||
cable.save()
|
||||
|
||||
self.interfaces[0].refresh_from_db()
|
||||
for iface in self.interfaces[1:9]:
|
||||
iface.refresh_from_db()
|
||||
profile = Breakout1C8Px8C1PCableProfile()
|
||||
|
||||
# A→B direction (one connector, 8 positions → 8 connectors)
|
||||
a_pairs = [(self.interfaces[0], pos) for pos in self.interfaces[0].cable_positions]
|
||||
self._assert_batch_matches_singular(profile, a_pairs)
|
||||
|
||||
# B→A direction (8 connectors, 1 position each → one connector)
|
||||
b_pairs = [(iface, 1) for iface in self.interfaces[1:9]]
|
||||
self._assert_batch_matches_singular(profile, b_pairs)
|
||||
|
||||
def test_multi_position_single_termination(self):
|
||||
"""
|
||||
When a single-connector multi-position profile has only one termination
|
||||
|
|
|
|||
Loading…
Reference in New Issue