Compare commits

..

12 Commits

Author SHA1 Message Date
Jeremy Stretch
0e69e6457b Merge 16a9e5b924 into 3140060f21 2025-12-14 15:47:31 -05:00
Jeremy Stretch
16a9e5b924 Update migration
Some checks are pending
CI / build (20.x, 3.12) (push) Waiting to run
CI / build (20.x, 3.13) (push) Waiting to run
CI / build (20.x, 3.14) (push) Waiting to run
2025-12-14 15:41:21 -05:00
Jeremy Stretch
97b9805b94 Simplify profile mappings; misc cleanup 2025-12-14 15:26:57 -05:00
Jeremy Stretch
fe52e4cd74 Rename breakout profile 2025-12-14 15:26:57 -05:00
Jeremy Stretch
2966288fb5 Fix test 2025-12-14 15:26:57 -05:00
Jeremy Stretch
f5ec885c3a Standardize naming of shuffle cables 2025-12-14 15:26:57 -05:00
Jeremy Stretch
6cc182c7ce Flesh out cable profiles & tests 2025-12-14 15:26:57 -05:00
Jeremy Stretch
c1c912d84b Clean up validation logic 2025-12-14 15:26:57 -05:00
Jeremy Stretch
7d945b24cb Rename migrations 2025-12-14 15:26:57 -05:00
Jeremy Stretch
697fda1be3 Fix shuffle breakout mapping 2025-12-14 15:26:57 -05:00
Jeremy Stretch
144f23444b Cleanup; updated tests 2025-12-14 15:26:57 -05:00
Jeremy Stretch
15c32d44e7 #20788: Map positions by connector (WIP) 2025-12-14 15:26:57 -05:00
6 changed files with 146 additions and 111 deletions

View File

@@ -6,7 +6,7 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('circuits', '0054_cable_position'), ('circuits', '0054_cable_connector_positions'),
] ]
operations = [ operations = [

View File

@@ -1,17 +1,24 @@
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from dcim.choices import CableEndChoices
from dcim.models import CableTermination from dcim.models import CableTermination
class BaseCableProfile: class BaseCableProfile:
"""Base class for representing a cable profile.""" """Base class for representing a cable profile."""
# Mappings of connectors to their available positions at either end of the cable. For example, a 12-strand MPO # Mappings of connectors to the number of positions presented by each, at either end of the cable. For example, a
# fiber cable would have one connector at either end with six positions (six bidirectional fiber pairs). # 12-strand MPO fiber cable would have one connector at either end with six positions (six bidirectional fiber
# pairs).
a_connectors = {} a_connectors = {}
b_connectors = {} b_connectors = {}
# Defined a mapping of A/B connector & position pairings. If not defined, all positions are presumed to be
# symmetrical (i.e. 1:1 on side A maps to 1:1 on side B). If defined, it must be constructed as a dictionary of
# two-item tuples, e.g. {(1, 1): (1, 1)}.
_mapping = None
def clean(self, cable): def clean(self, cable):
# Enforce maximum terminations limits # Enforce maximum terminations limits
a_terminations_count = len(cable.a_terminations) a_terminations_count = len(cable.a_terminations)
@@ -44,6 +51,8 @@ class BaseCableProfile:
Return the mapped far-end connector & position for a given cable end the local connector & position. Return the mapped far-end connector & position for a given cable end the local connector & position.
""" """
# By default, assume all positions are symmetrical. # By default, assume all positions are symmetrical.
if self._mapping:
return self._mapping.get((connector, position))
return connector, position return connector, position
def get_peer_termination(self, termination, position): def get_peer_termination(self, termination, position):
@@ -67,177 +76,182 @@ class BaseCableProfile:
return None, None return None, None
# Profile naming:
# - Single: One connector per side, with one or more positions
# - Trunk: Two or more connectors per side, with one or more positions per connector
# - Breakout: One or more connectors on the A side which map to a greater number of B side connectors
# - Shuffle: A cable with nonlinear position mappings between sides
class Single1C1PCableProfile(BaseCableProfile): class Single1C1PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1], 1: 1,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Single1C2PCableProfile(BaseCableProfile): class Single1C2PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2], 1: 2,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Single1C4PCableProfile(BaseCableProfile): class Single1C4PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4], 1: 4,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Single1C6PCableProfile(BaseCableProfile): class Single1C6PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4, 5, 6], 1: 6,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Single1C8PCableProfile(BaseCableProfile): class Single1C8PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4, 5, 6, 7, 8], 1: 8,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Single1C12PCableProfile(BaseCableProfile): class Single1C12PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 1: 12,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Single1C16PCableProfile(BaseCableProfile): class Single1C16PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16], 1: 16,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Trunk2C1PCableProfile(BaseCableProfile): class Trunk2C1PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1], 1: 1,
2: [1], 2: 1,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Trunk2C2PCableProfile(BaseCableProfile): class Trunk2C2PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2], 1: 2,
2: [1, 2], 2: 2,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Trunk2C4PCableProfile(BaseCableProfile): class Trunk2C4PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4], 1: 4,
2: [1, 2, 3, 4], 2: 4,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Trunk2C6PCableProfile(BaseCableProfile): class Trunk2C6PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4, 5, 6], 1: 6,
2: [1, 2, 3, 4, 5, 6], 2: 6,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Trunk2C8PCableProfile(BaseCableProfile): class Trunk2C8PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4, 5, 6, 7, 8], 1: 8,
2: [1, 2, 3, 4, 5, 6, 7, 8], 2: 8,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Trunk2C12PCableProfile(BaseCableProfile): class Trunk2C12PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 1: 12,
2: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 2: 12,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Trunk4C1PCableProfile(BaseCableProfile): class Trunk4C1PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1], 1: 1,
2: [1], 2: 1,
3: [1], 3: 1,
4: [1], 4: 1,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Trunk4C2PCableProfile(BaseCableProfile): class Trunk4C2PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2], 1: 2,
2: [1, 2], 2: 2,
3: [1, 2], 3: 2,
4: [1, 2], 4: 2,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Trunk4C4PCableProfile(BaseCableProfile): class Trunk4C4PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4], 1: 4,
2: [1, 2, 3, 4], 2: 4,
3: [1, 2, 3, 4], 3: 4,
4: [1, 2, 3, 4], 4: 4,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Trunk4C6PCableProfile(BaseCableProfile): class Trunk4C6PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4, 5, 6], 1: 6,
2: [1, 2, 3, 4, 5, 6], 2: 6,
3: [1, 2, 3, 4, 5, 6], 3: 6,
4: [1, 2, 3, 4, 5, 6], 4: 6,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Trunk4C8PCableProfile(BaseCableProfile): class Trunk4C8PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4, 5, 6, 7, 8], 1: 8,
2: [1, 2, 3, 4, 5, 6, 7, 8], 2: 8,
3: [1, 2, 3, 4, 5, 6, 7, 8], 3: 8,
4: [1, 2, 3, 4, 5, 6, 7, 8], 4: 8,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Trunk8C4PCableProfile(BaseCableProfile): class Trunk8C4PCableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4], 1: 4,
2: [1, 2, 3, 4], 2: 4,
3: [1, 2, 3, 4], 3: 4,
4: [1, 2, 3, 4], 4: 4,
5: [1, 2, 3, 4], 5: 4,
6: [1, 2, 3, 4], 6: 4,
7: [1, 2, 3, 4], 7: 4,
8: [1, 2, 3, 4], 8: 4,
} }
b_connectors = a_connectors b_connectors = a_connectors
class Breakout1x4CableProfile(BaseCableProfile): class Breakout1C4Px4C1PCableProfile(BaseCableProfile):
"""Breakout 1:4 to 4:1"""
a_connectors = { a_connectors = {
1: [1, 2, 3, 4], 1: 4,
} }
b_connectors = { b_connectors = {
1: [1], 1: 1,
2: [1], 2: 1,
3: [1], 3: 1,
4: [1], 4: 1,
} }
_mapping = { _mapping = {
(1, 1): (1, 1), (1, 1): (1, 1),
@@ -249,14 +263,38 @@ class Breakout1x4CableProfile(BaseCableProfile):
(4, 1): (1, 4), (4, 1): (1, 4),
} }
def get_mapped_position(self, side, connector, position):
return self._mapping.get((connector, position))
class Breakout1C6Px6C1PCableProfile(BaseCableProfile):
class Shuffle2x2MPO8CableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4], 1: 6,
2: [1, 2, 3, 4], }
b_connectors = {
1: 1,
2: 1,
3: 1,
4: 1,
5: 1,
6: 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),
(2, 1): (1, 2),
(3, 1): (1, 3),
(4, 1): (1, 4),
(5, 1): (1, 5),
(6, 1): (1, 6),
}
class Shuffle2C4PCableProfile(BaseCableProfile):
a_connectors = {
1: 4,
2: 4,
} }
b_connectors = a_connectors b_connectors = a_connectors
_mapping = { _mapping = {
@@ -270,16 +308,13 @@ class Shuffle2x2MPO8CableProfile(BaseCableProfile):
(2, 4): (2, 4), (2, 4): (2, 4),
} }
def get_mapped_position(self, side, connector, position):
return self._mapping.get((connector, position))
class Shuffle4C4PCableProfile(BaseCableProfile):
class Shuffle4x4MPO8CableProfile(BaseCableProfile):
a_connectors = { a_connectors = {
1: [1, 2, 3, 4], 1: 4,
2: [1, 2, 3, 4], 2: 4,
3: [1, 2, 3, 4], 3: 4,
4: [1, 2, 3, 4], 4: 4,
} }
b_connectors = a_connectors b_connectors = a_connectors
_mapping = { _mapping = {
@@ -301,27 +336,24 @@ class Shuffle4x4MPO8CableProfile(BaseCableProfile):
(4, 4): (4, 4), (4, 4): (4, 4),
} }
def get_mapped_position(self, side, connector, position):
return self._mapping.get((connector, position))
class ShuffleBreakout2x8CableProfile(BaseCableProfile): class ShuffleBreakout2x8CableProfile(BaseCableProfile):
""" """
Temporary solution for mapping 2 front/rear ports to 8 discrete interfaces Temporary solution for mapping 2 front/rear ports to 8 discrete interfaces
""" """
a_connectors = { a_connectors = {
1: [1, 2, 3, 4], 1: 4,
2: [1, 2, 3, 4], 2: 4,
} }
b_connectors = { b_connectors = {
1: [1], 1: 1,
2: [1], 2: 1,
3: [1], 3: 1,
4: [1], 4: 1,
5: [1], 5: 1,
6: [1], 6: 1,
7: [1], 7: 1,
8: [1], 8: 1,
} }
_a_mapping = { _a_mapping = {
(1, 1): (1, 1), (1, 1): (1, 1),
@@ -345,6 +377,6 @@ class ShuffleBreakout2x8CableProfile(BaseCableProfile):
} }
def get_mapped_position(self, side, connector, position): def get_mapped_position(self, side, connector, position):
if side.lower() == 'a': if side.lower() == CableEndChoices.SIDE_A:
return self._a_mapping.get((connector, position)) return self._a_mapping.get((connector, position))
return self._b_mapping.get((connector, position)) return self._b_mapping.get((connector, position))

View File

@@ -1744,11 +1744,12 @@ class CableProfileChoices(ChoiceSet):
TRUNK_4C8P = 'trunk-4c8p' TRUNK_4C8P = 'trunk-4c8p'
TRUNK_8C4P = 'trunk-8c4p' TRUNK_8C4P = 'trunk-8c4p'
# Breakouts # Breakouts
BREAKOUT_1X4 = 'breakout-1x4' BREAKOUT_1C4P_4C1P = 'breakout-1c4p-4c1p'
BREAKOUT_1C6P_6C1P = 'breakout-1c6p-6c1p'
SHUFFLE_BREAKOUT_2X8 = 'shuffle-breakout-2x8' SHUFFLE_BREAKOUT_2X8 = 'shuffle-breakout-2x8'
# Shuffles # Shuffles
SHUFFLE_2X2_MPO8 = 'shuffle-2x2-mpo8' SHUFFLE_2C4P = 'shuffle-2c4p'
SHUFFLE_4X4_MPO8 = 'shuffle-4x4-mpo8' SHUFFLE_4C4P = 'shuffle-4c4p'
CHOICES = ( CHOICES = (
( (
@@ -1781,16 +1782,17 @@ class CableProfileChoices(ChoiceSet):
), ),
), ),
( (
_('Breakouts'), _('Breakout'),
( (
(BREAKOUT_1X4, _('Breakout (1:4)')), (BREAKOUT_1C4P_4C1P, _('Breakout (1C4P/4C1P)')),
(BREAKOUT_1C6P_6C1P, _('Breakout (1C6P/6C1P)')),
), ),
), ),
( (
_('Shuffles'), _('Shuffle'),
( (
(SHUFFLE_2X2_MPO8, _('Shuffle (2x2 MPO8)')), (SHUFFLE_2C4P, _('Shuffle (2C4P)')),
(SHUFFLE_4X4_MPO8, _('Shuffle (4x4 MPO8)')), (SHUFFLE_4C4P, _('Shuffle (4C4P)')),
(SHUFFLE_BREAKOUT_2X8, _('Shuffle breakout (2x8)')), (SHUFFLE_BREAKOUT_2X8, _('Shuffle breakout (2x8)')),
), ),
), ),

View File

@@ -156,9 +156,10 @@ class Cable(PrimaryModel):
CableProfileChoices.TRUNK_4C6P: cable_profiles.Trunk4C6PCableProfile, CableProfileChoices.TRUNK_4C6P: cable_profiles.Trunk4C6PCableProfile,
CableProfileChoices.TRUNK_4C8P: cable_profiles.Trunk4C8PCableProfile, CableProfileChoices.TRUNK_4C8P: cable_profiles.Trunk4C8PCableProfile,
CableProfileChoices.TRUNK_8C4P: cable_profiles.Trunk8C4PCableProfile, CableProfileChoices.TRUNK_8C4P: cable_profiles.Trunk8C4PCableProfile,
CableProfileChoices.BREAKOUT_1X4: cable_profiles.Breakout1x4CableProfile, CableProfileChoices.BREAKOUT_1C4P_4C1P: cable_profiles.Breakout1C4Px4C1PCableProfile,
CableProfileChoices.SHUFFLE_2X2_MPO8: cable_profiles.Shuffle2x2MPO8CableProfile, CableProfileChoices.BREAKOUT_1C6P_6C1P: cable_profiles.Breakout1C6Px6C1PCableProfile,
CableProfileChoices.SHUFFLE_4X4_MPO8: cable_profiles.Shuffle4x4MPO8CableProfile, CableProfileChoices.SHUFFLE_2C4P: cable_profiles.Shuffle2C4PCableProfile,
CableProfileChoices.SHUFFLE_4C4P: cable_profiles.Shuffle4C4PCableProfile,
CableProfileChoices.SHUFFLE_BREAKOUT_2X8: cable_profiles.ShuffleBreakout2x8CableProfile, CableProfileChoices.SHUFFLE_BREAKOUT_2X8: cable_profiles.ShuffleBreakout2x8CableProfile,
}.get(self.profile) }.get(self.profile)
@@ -363,7 +364,7 @@ class Cable(PrimaryModel):
connector = positions = None connector = positions = None
if self.profile: if self.profile:
connector = i connector = i
positions = self.profile_class().a_connectors[i] positions = list(range(1, self.profile_class().a_connectors[i] + 1))
CableTermination( CableTermination(
cable=self, cable=self,
cable_end='A', cable_end='A',
@@ -376,7 +377,7 @@ class Cable(PrimaryModel):
connector = positions = None connector = positions = None
if self.profile: if self.profile:
connector = i connector = i
positions = self.profile_class().b_connectors[i] positions = list(range(1, self.profile_class().b_connectors[i] + 1))
CableTermination( CableTermination(
cable=self, cable=self,
cable_end='B', cable_end='B',

View File

@@ -2586,7 +2586,7 @@ class CableTest(APIViewTestCases.APIViewTestCase):
'object_id': interfaces[14].pk, 'object_id': interfaces[14].pk,
}], }],
'label': 'Cable 4', 'label': 'Cable 4',
'profile': CableProfileChoices.STRAIGHT_1C1P, 'profile': CableProfileChoices.SINGLE_1C1P,
}, },
{ {
'a_terminations': [{ 'a_terminations': [{
@@ -2598,7 +2598,7 @@ class CableTest(APIViewTestCases.APIViewTestCase):
'object_id': interfaces[15].pk, 'object_id': interfaces[15].pk,
}], }],
'label': 'Cable 5', 'label': 'Cable 5',
'profile': CableProfileChoices.STRAIGHT_1C1P, 'profile': CableProfileChoices.SINGLE_1C1P,
}, },
{ {
'a_terminations': [{ 'a_terminations': [{

View File

@@ -559,7 +559,7 @@ class CablePathTests(CablePathTestCase):
cable4.clean() cable4.clean()
cable4.save() cable4.save()
cable5 = Cable( cable5 = Cable(
profile=CableProfileChoices.BREAKOUT_1X4, profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
a_terminations=[rear_ports[0]], a_terminations=[rear_ports[0]],
b_terminations=interfaces[4:8], b_terminations=interfaces[4:8],
) )
@@ -632,7 +632,7 @@ class CablePathTests(CablePathTestCase):
# Test SVG generation # Test SVG generation
CableTraceSVG(interfaces[0]).render() CableTraceSVG(interfaces[0]).render()
def test_106_cable_profile_shuffle_2x2_mpo8(self): def test_106_cable_profile_shuffle(self):
""" """
[IF1] --C1-- [FP1][RP1] --C17-- [RP3][FP9] --C9-- [IF9] [IF1] --C1-- [FP1][RP1] --C17-- [RP3][FP9] --C9-- [IF9]
[IF2] --C2-- [FP2] [FP10] --C10-- [IF10] [IF2] --C2-- [FP2] [FP10] --C10-- [IF10]
@@ -710,7 +710,7 @@ class CablePathTests(CablePathTestCase):
cable.save() cable.save()
cables.append(cable) cables.append(cable)
shuffle_cable = Cable( shuffle_cable = Cable(
profile=CableProfileChoices.SHUFFLE_2X2_MPO8, profile=CableProfileChoices.SHUFFLE_2C4P,
a_terminations=rear_ports[0:2], a_terminations=rear_ports[0:2],
b_terminations=rear_ports[2:4], b_terminations=rear_ports[2:4],
) )
@@ -843,14 +843,14 @@ class CablePathTests(CablePathTestCase):
# Create cables # Create cables
cable1 = Cable( cable1 = Cable(
profile=CableProfileChoices.BREAKOUT_1X4, profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
a_terminations=[frontport1], a_terminations=[frontport1],
b_terminations=[interfaces[0], interfaces[1]], b_terminations=[interfaces[0], interfaces[1]],
) )
cable1.clean() cable1.clean()
cable1.save() cable1.save()
cable2 = Cable( cable2 = Cable(
profile=CableProfileChoices.BREAKOUT_1X4, profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
a_terminations=[rearport1], a_terminations=[rearport1],
b_terminations=[interfaces[2], interfaces[3]] b_terminations=[interfaces[2], interfaces[3]]
) )
@@ -974,14 +974,14 @@ class CablePathTests(CablePathTestCase):
# Create cables # Create cables
cable1 = Cable( cable1 = Cable(
profile=CableProfileChoices.BREAKOUT_1X4, profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
a_terminations=[frontport1], a_terminations=[frontport1],
b_terminations=[interfaces[0], interfaces[1]], b_terminations=[interfaces[0], interfaces[1]],
) )
cable1.clean() cable1.clean()
cable1.save() cable1.save()
cable2 = Cable( cable2 = Cable(
profile=CableProfileChoices.BREAKOUT_1X4, profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
a_terminations=[frontport2], a_terminations=[frontport2],
b_terminations=[interfaces[2], interfaces[3]], b_terminations=[interfaces[2], interfaces[3]],
) )
@@ -995,14 +995,14 @@ class CablePathTests(CablePathTestCase):
cable3.clean() cable3.clean()
cable3.save() cable3.save()
cable4 = Cable( cable4 = Cable(
profile=CableProfileChoices.BREAKOUT_1X4, profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
a_terminations=[frontport3], a_terminations=[frontport3],
b_terminations=[interfaces[4], interfaces[5]], b_terminations=[interfaces[4], interfaces[5]],
) )
cable4.clean() cable4.clean()
cable4.save() cable4.save()
cable5 = Cable( cable5 = Cable(
profile=CableProfileChoices.BREAKOUT_1X4, profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
a_terminations=[frontport4], a_terminations=[frontport4],
b_terminations=[interfaces[6], interfaces[7]], b_terminations=[interfaces[6], interfaces[7]],
) )
@@ -1115,14 +1115,14 @@ class CablePathTests(CablePathTestCase):
# Create cables # Create cables
cable1 = Cable( cable1 = Cable(
profile=CableProfileChoices.BREAKOUT_1X4, profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
a_terminations=[circuittermination1], a_terminations=[circuittermination1],
b_terminations=[interfaces[0], interfaces[1]], b_terminations=[interfaces[0], interfaces[1]],
) )
cable1.clean() cable1.clean()
cable1.save() cable1.save()
cable2 = Cable( cable2 = Cable(
profile=CableProfileChoices.BREAKOUT_1X4, profile=CableProfileChoices.BREAKOUT_1C4P_4C1P,
a_terminations=[circuittermination2], a_terminations=[circuittermination2],
b_terminations=[interfaces[2], interfaces[3]] b_terminations=[interfaces[2], interfaces[3]]
) )