Adapt tracing view to account for split ends (WIP)

This commit is contained in:
Jeremy Stretch 2020-04-15 17:09:04 -04:00
parent 5205c4963f
commit 29707cd496
5 changed files with 46 additions and 19 deletions

View File

@ -48,7 +48,7 @@ class CableTraceMixin(object):
# Initialize the path array
path = []
for near_end, cable, far_end in obj.trace():
for near_end, cable, far_end in obj.trace()[0]:
# Serialize each object
serializer_a = get_serializer_for_model(near_end, prefix='Nested')

View File

@ -92,7 +92,13 @@ class CableTermination(models.Model):
def trace(self):
"""
Return a list representing a complete cable path, with each individual segment represented as a three-tuple:
Return two items: the traceable portion of a cable path, and the termination points where it splits (if any).
This occurs when the trace is initiated from a midpoint along a path which traverses a RearPort. In cases where
the originating endpoint is unknown, it is not possible to know which corresponding FrontPort to follow.
The path is a list representing a complete cable path, with each individual segment represented as a
three-tuple:
[
(termination A, cable, termination B),
(termination C, cable, termination D),
@ -157,12 +163,12 @@ class CableTermination(models.Model):
if not endpoint.cable:
path.append((endpoint, None, None))
logger.debug("No cable connected")
return path
return path, None
# Check for loops
if endpoint.cable in [segment[1] for segment in path]:
logger.debug("Loop detected!")
return path
return path, None
# Record the current segment in the path
far_end = endpoint.get_cable_peer()
@ -172,9 +178,13 @@ class CableTermination(models.Model):
))
# Get the peer port of the far end termination
try:
endpoint = get_peer_port(far_end)
except CableTraceSplit as e:
return path, e.termination.frontports.all()
if endpoint is None:
return path
return path, None
def get_cable_peer(self):
if self.cable is None:
@ -191,16 +201,14 @@ class CableTermination(models.Model):
endpoints = []
# Get the far end of the last path segment
try:
endpoint = self.trace()[-1][2]
if endpoint is not None:
path, split_ends = self.trace()
endpoint = path[-1][2]
if split_ends is not None:
for termination in split_ends:
endpoints.extend(termination.get_path_endpoints())
elif endpoint is not None:
endpoints.append(endpoint)
# We've hit a RearPort mapped to multiple FrontPorts. Recurse to trace each of them individually.
except CableTraceSplit as e:
for frontport in e.termination.frontports.all():
endpoints.extend(frontport.get_path_endpoints())
return endpoints

View File

@ -52,7 +52,7 @@ def update_connected_endpoints(instance, **kwargs):
# Update any endpoints for this Cable.
endpoints = instance.termination_a.get_path_endpoints() + instance.termination_b.get_path_endpoints()
for endpoint in endpoints:
path = endpoint.trace()
path, split_ends = endpoint.trace()
# Determine overall path status (connected or planned)
path_status = True
for segment in path:

View File

@ -32,6 +32,7 @@ from virtualization.models import VirtualMachine
from . import filters, forms, tables
from .choices import DeviceFaceChoices
from .constants import NONCONNECTABLE_IFACE_TYPES
from .exceptions import CableTraceSplit
from .models import (
Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay,
DeviceBayTemplate, DeviceRole, DeviceType, FrontPort, FrontPortTemplate, Interface, InterfaceTemplate,
@ -2033,12 +2034,15 @@ class CableTraceView(PermissionRequiredMixin, View):
def get(self, request, model, pk):
obj = get_object_or_404(model, pk=pk)
trace = obj.trace()
total_length = sum([entry[1]._abs_length for entry in trace if entry[1] and entry[1]._abs_length])
path, split_ends = obj.trace()
total_length = sum(
[entry[1]._abs_length for entry in path if entry[1] and entry[1]._abs_length]
)
return render(request, 'dcim/cable_trace.html', {
'obj': obj,
'trace': trace,
'trace': path,
'split_ends': split_ends,
'total_length': total_length,
})

View File

@ -50,4 +50,19 @@
</div>
{% if not forloop.last %}<hr />{% endif %}
{% endfor %}
<div class="row">
<div class="col-md-11 col-md-offset-1">
{% if split_ends %}
<h3 class="text-danger text-center"><i class="fa fa-warning"></i> Trace Split</h3>
<p>Select a termination to continue:</p>
<ul>
{% for termination in split_ends %}
<li><a href="{% url 'dcim:frontport_trace' pk=termination.pk %}">{{ termination.parent }} / {{ termination }}</a></li>
{% endfor %}
</ul>
{% else %}
<h3 class="text-success text-center">Trace completed!</h3>
{% endif %}
</div>
</div>
{% endblock %}