1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Handle traces which split at a RearPort

This commit is contained in:
Jeremy Stretch
2020-10-08 13:45:47 -04:00
parent 29eebf9fbe
commit 0c5efa243d
5 changed files with 82 additions and 29 deletions

@ -3,12 +3,3 @@ class LoopDetected(Exception):
A loop has been detected while tracing a cable path.
"""
pass
class CableTraceSplit(Exception):
"""
A cable trace cannot be completed because a RearPort maps to multiple FrontPorts and
we don't know which one to follow.
"""
def __init__(self, termination, *args, **kwargs):
self.termination = termination

@ -172,9 +172,11 @@ class PathEndpoint(models.Model):
return []
# Construct the complete path
path = [self, *[path_node_to_object(obj) for obj in self._path.path], self._path.destination]
assert not len(path) % 3,\
f"Invalid path length for CablePath #{self.pk}: {len(self._path.path)} elements in path"
path = [self, *[path_node_to_object(obj) for obj in self._path.path]]
while (len(path) + 1) % 3:
# Pad to ensure we have complete three-tuples (e.g. for paths that end at a RearPort)
path.append(None)
path.append(self._path.destination)
# Return the path as a list of three-tuples (A termination, cable, B termination)
return list(zip(*[iter(path)] * 3))

@ -721,6 +721,63 @@ class CablePathTestCase(TestCase):
self.assertEqual(CablePath.objects.filter(destination_id__isnull=True).count(), 4)
self.assertEqual(CablePath.objects.filter(destination_id__isnull=False).count(), 0)
def test_206_unidirectional_split_paths(self):
"""
[IF1] --C1-- [RP1] [FP1:1] --C2-- [IF2]
[FP1:2] --C3-- [IF3]
"""
self.interface1.refresh_from_db()
self.interface2.refresh_from_db()
self.interface3.refresh_from_db()
# Create cables 1
cable1 = Cable(termination_a=self.interface1, termination_b=self.rear_port1)
cable1.save()
self.assertPathExists(
origin=self.interface1,
destination=None,
path=(cable1, self.rear_port1),
is_active=False
)
self.assertEqual(CablePath.objects.count(), 1)
# Create cables 2-3
cable2 = Cable(termination_a=self.interface2, termination_b=self.front_port1_1)
cable2.save()
cable3 = Cable(termination_a=self.interface3, termination_b=self.front_port1_2)
cable3.save()
self.assertPathExists(
origin=self.interface2,
destination=self.interface1,
path=(cable2, self.front_port1_1, self.rear_port1, cable1),
is_active=True
)
self.assertPathExists(
origin=self.interface3,
destination=self.interface1,
path=(cable3, self.front_port1_2, self.rear_port1, cable1),
is_active=True
)
self.assertEqual(CablePath.objects.count(), 3)
# Delete cable 1
cable1.delete()
# Check that the partial path was deleted and the two complete paths are now partial
self.assertPathExists(
origin=self.interface2,
destination=None,
path=(cable2, self.front_port1_1, self.rear_port1),
is_active=False
)
self.assertPathExists(
origin=self.interface3,
destination=None,
path=(cable3, self.front_port1_2, self.rear_port1),
is_active=False
)
self.assertEqual(CablePath.objects.count(), 2)
def test_301_create_path_via_existing_cable(self):
"""
[IF1] --C1-- [FP5] [RP5] --C2-- [RP6] [FP6] --C3-- [IF2]

@ -1,7 +1,6 @@
from django.contrib.contenttypes.models import ContentType
from .choices import CableStatusChoices
from .exceptions import CableTraceSplit
def compile_path_node(ct_id, object_id):
@ -69,8 +68,8 @@ def trace_path(node):
node = FrontPort.objects.get(rear_port=peer_termination, rear_port_position=position)
path.append(object_to_path_node(node))
else:
# No position indicated: path has split (probably invalid?)
raise CableTraceSplit(peer_termination)
# No position indicated: path has split, so we stop at the RearPort
break
# Anything else marks the end of the path
else:

@ -19,16 +19,17 @@
{% elif near_end.circuit %}
{% include 'dcim/trace/circuit.html' with circuit=near_end.circuit %}
{% include 'dcim/trace/termination.html' with termination=near_end %}
{% else %}
<h3 class="text-danger text-center">Split Paths!</h3>
{# TODO: Present the user with successive paths to choose from #}
{% endif %}
{# Cable #}
<div class="row">
{% if cable %}
{% if cable %}
<div class="row">
{% include 'dcim/trace/cable.html' %}
{% else %}
<h4>No cable</h4>
{% endif %}
</div>
</div>
{% endif %}
{# Far end #}
{% if far_end.device %}
@ -45,15 +46,18 @@
{% endif %}
{% endif %}
{% if forloop.last and far_end %}
<div class="row">
<div class="text-center">
<h3 class="text-success text-center">Trace completed!</h3>
{% if total_length %}
<h5>Total length: {{ total_length|floatformat:"-2" }} Meters<h5>
{% endif %}
</div>
</div>
{% endif %}
{% endfor %}
<div class="row">
<div class="text-center">
<h3 class="text-success text-center">Trace completed!</h3>
{% if total_length %}
<h5>Total length: {{ total_length|floatformat:"-2" }} Meters<h5>
{% endif %}
</div>
</div>
</div>
<div class="col-md-7 col-sm-12">