Skip to content

Commit 4ab58f2

Browse files
authored
Fixes: #15016 - Catch AssertionError from cable trace and throw ValidationError (#16384)
1 parent d208ddd commit 4ab58f2

File tree

4 files changed

+34
-15
lines changed

4 files changed

+34
-15
lines changed

netbox/dcim/exceptions.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class UnsupportedCablePath(Exception):
2+
pass

netbox/dcim/models/cables.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from dcim.utils import decompile_path_node, object_to_path_node
1616
from netbox.models import ChangeLoggedModel, PrimaryModel
1717
from utilities.conversion import to_meters
18+
from utilities.exceptions import AbortRequest
1819
from utilities.fields import ColorField
1920
from utilities.querysets import RestrictedQuerySet
2021
from wireless.models import WirelessLink
@@ -26,6 +27,7 @@
2627
'CableTermination',
2728
)
2829

30+
from ..exceptions import UnsupportedCablePath
2931

3032
trace_paths = Signal()
3133

@@ -236,8 +238,10 @@ def save(self, *args, **kwargs):
236238
for termination in self.b_terminations:
237239
if not termination.pk or termination not in b_terminations:
238240
CableTermination(cable=self, cable_end='B', termination=termination).save()
239-
240-
trace_paths.send(Cable, instance=self, created=_created)
241+
try:
242+
trace_paths.send(Cable, instance=self, created=_created)
243+
except UnsupportedCablePath as e:
244+
raise AbortRequest(e)
241245

242246
def get_status_color(self):
243247
return LinkStatusChoices.colors.get(self.status)
@@ -531,8 +535,8 @@ def from_origin(cls, terminations):
531535
return None
532536

533537
# Ensure all originating terminations are attached to the same link
534-
if len(terminations) > 1:
535-
assert all(t.link == terminations[0].link for t in terminations[1:])
538+
if len(terminations) > 1 and not all(t.link == terminations[0].link for t in terminations[1:]):
539+
raise UnsupportedCablePath(_("All originating terminations must be attached to the same link"))
536540

537541
path = []
538542
position_stack = []
@@ -543,12 +547,13 @@ def from_origin(cls, terminations):
543547
while terminations:
544548

545549
# Terminations must all be of the same type
546-
assert all(isinstance(t, type(terminations[0])) for t in terminations[1:])
550+
if not all(isinstance(t, type(terminations[0])) for t in terminations[1:]):
551+
raise UnsupportedCablePath(_("All mid-span terminations must have the same termination type"))
547552

548553
# All mid-span terminations must all be attached to the same device
549-
if not isinstance(terminations[0], PathEndpoint):
550-
assert all(isinstance(t, type(terminations[0])) for t in terminations[1:])
551-
assert all(t.parent_object == terminations[0].parent_object for t in terminations[1:])
554+
if (not isinstance(terminations[0], PathEndpoint) and not
555+
all(t.parent_object == terminations[0].parent_object for t in terminations[1:])):
556+
raise UnsupportedCablePath(_("All mid-span terminations must have the same parent object"))
552557

553558
# Check for a split path (e.g. rear port fanning out to multiple front ports with
554559
# different cables attached)
@@ -571,8 +576,10 @@ def from_origin(cls, terminations):
571576
return None
572577
# Otherwise, halt the trace if no link exists
573578
break
574-
assert all(type(link) in (Cable, WirelessLink) for link in links)
575-
assert all(isinstance(link, type(links[0])) for link in links)
579+
if not all(type(link) in (Cable, WirelessLink) for link in links):
580+
raise UnsupportedCablePath(_("All links must be cable or wireless"))
581+
if not all(isinstance(link, type(links[0])) for link in links):
582+
raise UnsupportedCablePath(_("All links must match first link type"))
576583

577584
# Step 3: Record asymmetric paths as split
578585
not_connected_terminations = [termination.link for termination in terminations if termination.link is None]
@@ -653,14 +660,18 @@ def from_origin(cls, terminations):
653660
positions = position_stack.pop()
654661

655662
# Ensure we have a number of positions equal to the amount of remote terminations
656-
assert len(remote_terminations) == len(positions)
663+
if len(remote_terminations) != len(positions):
664+
raise UnsupportedCablePath(
665+
_("All positions counts within the path on opposite ends of links must match")
666+
)
657667

658668
# Get our front ports
659669
q_filter = Q()
660670
for rt in remote_terminations:
661671
position = positions.pop()
662672
q_filter |= Q(rear_port_id=rt.pk, rear_port_position=position)
663-
assert q_filter is not Q()
673+
if q_filter is Q():
674+
raise UnsupportedCablePath(_("Remote termination position filter is missing"))
664675
front_ports = FrontPort.objects.filter(q_filter)
665676
# Obtain the individual front ports based on the termination and position
666677
elif position_stack:

netbox/dcim/tests/test_cablepaths.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from dcim.models import *
66
from dcim.svg import CableTraceSVG
77
from dcim.utils import object_to_path_node
8+
from utilities.exceptions import AbortRequest
89

910

1011
class CablePathTestCase(TestCase):
@@ -2470,7 +2471,7 @@ def test_401_exclude_midspan_devices(self):
24702471
b_terminations=[frontport1, frontport3],
24712472
label='C1'
24722473
)
2473-
with self.assertRaises(AssertionError):
2474+
with self.assertRaises(AbortRequest):
24742475
cable1.save()
24752476

24762477
self.assertPathDoesNotExist(
@@ -2489,7 +2490,7 @@ def test_401_exclude_midspan_devices(self):
24892490
label='C3'
24902491
)
24912492

2492-
with self.assertRaises(AssertionError):
2493+
with self.assertRaises(AbortRequest):
24932494
cable3.save()
24942495

24952496
self.assertPathDoesNotExist(

netbox/wireless/signals.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
from django.db.models.signals import post_save, post_delete
44
from django.dispatch import receiver
55

6+
from dcim.exceptions import UnsupportedCablePath
67
from dcim.models import CablePath, Interface
78
from dcim.utils import create_cablepath
9+
from utilities.exceptions import AbortRequest
810
from .models import WirelessLink
911

1012

@@ -34,7 +36,10 @@ def update_connected_interfaces(instance, created, raw=False, **kwargs):
3436
# Create/update cable paths
3537
if created:
3638
for interface in (instance.interface_a, instance.interface_b):
37-
create_cablepath([interface])
39+
try:
40+
create_cablepath([interface])
41+
except UnsupportedCablePath as e:
42+
raise AbortRequest(e)
3843

3944

4045
@receiver(post_delete, sender=WirelessLink)

0 commit comments

Comments
 (0)