diff --git a/netbox/circuits/urls.py b/netbox/circuits/urls.py index b0920d9cd..be1106308 100644 --- a/netbox/circuits/urls.py +++ b/netbox/circuits/urls.py @@ -1,6 +1,6 @@ from django.conf.urls import url -from dcim.views import CableCreateView +from dcim.views import CableCreateView, CableTraceView from extras.views import ObjectChangeLogView from . import views from .models import Circuit, CircuitTermination, CircuitType, Provider @@ -44,5 +44,6 @@ urlpatterns = [ url(r'^circuit-terminations/(?P\d+)/edit/$', views.CircuitTerminationEditView.as_view(), name='circuittermination_edit'), url(r'^circuit-terminations/(?P\d+)/delete/$', views.CircuitTerminationDeleteView.as_view(), name='circuittermination_delete'), url(r'^circuit-terminations/(?P\d+)/connect/$', CableCreateView.as_view(), name='circuittermination_connect', kwargs={'termination_a_type': CircuitTermination}), + url(r'^circuit-terminations/(?P\d+)/trace/$', CableTraceView.as_view(), name='circuittermination_trace', kwargs={'model': CircuitTermination}), ] diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 5b5f6cd10..4014d00e5 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -85,6 +85,7 @@ class CableTermination(models.Model): ] """ def get_peer_port(termination, position=1): + from circuits.models import CircuitTermination # Map a front port to its corresponding rear port if isinstance(termination, FrontPort): @@ -102,6 +103,13 @@ class CableTermination(models.Model): ) return peer_port, 1 + # Follow a circuit to its other termination + elif isinstance(termination, CircuitTermination): + peer_termination = termination.get_peer_termination() + if peer_termination is None: + return None, None + return peer_termination, position + # Termination is not a pass-through port else: return None, None diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index 9a669d4f1..13ffec1ec 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -5,8 +5,8 @@ from ipam.views import ServiceCreateView from secrets.views import secret_add from . import views from .models import ( - ConsolePort, ConsoleServerPort, Device, DeviceRole, DeviceType, Interface, Manufacturer, Platform, PowerPort, - PowerOutlet, Rack, RackGroup, RackReservation, RackRole, Region, Site, VirtualChassis, + ConsolePort, ConsoleServerPort, Device, DeviceRole, DeviceType, FrontPort, Interface, Manufacturer, Platform, + PowerPort, PowerOutlet, Rack, RackGroup, RackReservation, RackRole, RearPort, Region, Site, VirtualChassis, ) app_name = 'dcim' @@ -166,6 +166,7 @@ urlpatterns = [ url(r'^console-ports/(?P\d+)/disconnect/$', views.ConsolePortDisconnectView.as_view(), name='consoleport_disconnect'), url(r'^console-ports/(?P\d+)/edit/$', views.ConsolePortEditView.as_view(), name='consoleport_edit'), url(r'^console-ports/(?P\d+)/delete/$', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'), + url(r'^console-ports/(?P\d+)/trace/$', views.CableTraceView.as_view(), name='consoleport_trace', kwargs={'model': ConsolePort}), # Console server ports url(r'^devices/console-server-ports/add/$', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'), @@ -177,6 +178,7 @@ urlpatterns = [ url(r'^console-server-ports/(?P\d+)/disconnect/$', views.ConsoleServerPortDisconnectView.as_view(), name='consoleserverport_disconnect'), url(r'^console-server-ports/(?P\d+)/edit/$', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'), url(r'^console-server-ports/(?P\d+)/delete/$', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'), + url(r'^console-server-ports/(?P\d+)/trace/$', views.CableTraceView.as_view(), name='consoleserverport_trace', kwargs={'model': ConsoleServerPort}), url(r'^console-server-ports/rename/$', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'), # Power ports @@ -188,6 +190,7 @@ urlpatterns = [ url(r'^power-ports/(?P\d+)/disconnect/$', views.PowerPortDisconnectView.as_view(), name='powerport_disconnect'), url(r'^power-ports/(?P\d+)/edit/$', views.PowerPortEditView.as_view(), name='powerport_edit'), url(r'^power-ports/(?P\d+)/delete/$', views.PowerPortDeleteView.as_view(), name='powerport_delete'), + url(r'^power-ports/(?P\d+)/trace/$', views.CableTraceView.as_view(), name='powerport_trace', kwargs={'model': PowerPort}), # Power outlets url(r'^devices/power-outlets/add/$', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'), @@ -199,6 +202,7 @@ urlpatterns = [ url(r'^power-outlets/(?P\d+)/disconnect/$', views.PowerOutletDisconnectView.as_view(), name='poweroutlet_disconnect'), url(r'^power-outlets/(?P\d+)/edit/$', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'), url(r'^power-outlets/(?P\d+)/delete/$', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'), + url(r'^power-outlets/(?P\d+)/trace/$', views.CableTraceView.as_view(), name='poweroutlet_trace', kwargs={'model': PowerOutlet}), url(r'^power-outlets/rename/$', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'), # Interfaces @@ -213,6 +217,7 @@ urlpatterns = [ url(r'^interfaces/(?P\d+)/assign-vlans/$', views.InterfaceAssignVLANsView.as_view(), name='interface_assign_vlans'), url(r'^interfaces/(?P\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'), url(r'^interfaces/(?P\d+)/changelog/$', ObjectChangeLogView.as_view(), name='interface_changelog', kwargs={'model': Interface}), + url(r'^interfaces/(?P\d+)/trace/$', views.CableTraceView.as_view(), name='interface_trace', kwargs={'model': Interface}), url(r'^interfaces/rename/$', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'), # Front ports @@ -221,6 +226,7 @@ urlpatterns = [ url(r'^devices/(?P\d+)/front-ports/delete/$', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'), url(r'^front-ports/(?P\d+)/edit/$', views.FrontPortEditView.as_view(), name='frontport_edit'), url(r'^front-ports/(?P\d+)/delete/$', views.FrontPortDeleteView.as_view(), name='frontport_delete'), + url(r'^front-ports/(?P\d+)/trace/$', views.CableTraceView.as_view(), name='frontport_trace', kwargs={'model': FrontPort}), url(r'^front-ports/rename/$', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'), # Rear ports @@ -229,6 +235,7 @@ urlpatterns = [ url(r'^devices/(?P\d+)/rear-ports/delete/$', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'), url(r'^rear-ports/(?P\d+)/edit/$', views.RearPortEditView.as_view(), name='rearport_edit'), url(r'^rear-ports/(?P\d+)/delete/$', views.RearPortDeleteView.as_view(), name='rearport_delete'), + url(r'^rear-ports/(?P\d+)/trace/$', views.CableTraceView.as_view(), name='rearport_trace', kwargs={'model': RearPort}), url(r'^rear-ports/rename/$', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'), # Device bays diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 81b3d28fe..60b998a6f 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -2063,6 +2063,21 @@ class CableDeleteView(PermissionRequiredMixin, ObjectDeleteView): default_return_url = 'dcim:cable_list' +class CableTraceView(View): + """ + Trace a cable path beginning from the given termination. + """ + + def get(self, request, model, pk): + + obj = get_object_or_404(model, pk=pk) + + return render(request, 'dcim/cable_trace.html', { + 'obj': obj, + 'trace': obj.trace(), + }) + + # # Connections # diff --git a/netbox/templates/circuits/inc/circuit_termination.html b/netbox/templates/circuits/inc/circuit_termination.html index bdfd6faae..560322357 100644 --- a/netbox/templates/circuits/inc/circuit_termination.html +++ b/netbox/templates/circuits/inc/circuit_termination.html @@ -39,9 +39,12 @@ Termination - {% if termination.connected_endpoint %} - {{ termination.connected_endpoint.device }} - {{ termination.connected_endpoint }} + {% if termination.cable %} + {{ termination.cable }} + {% if termination.connected_endpoint %} + to {{ termination.connected_endpoint.device }} + {{ termination.connected_endpoint }} + {% endif %} {% else %} {% if perms.circuits.change_circuittermination %}
diff --git a/netbox/templates/dcim/cable_trace.html b/netbox/templates/dcim/cable_trace.html new file mode 100644 index 000000000..3211664ad --- /dev/null +++ b/netbox/templates/dcim/cable_trace.html @@ -0,0 +1,42 @@ +{% extends '_base.html' %} +{% load helpers %} + +{% block header %} +

{% block title %}Cable Trace for {{ obj }}{% endblock %}

+{% endblock %} + +{% block content %} +
+
+

Near End

+
+
+

Far End

+
+
+ {% for near_end, cable, far_end in trace %} +
+
+

{{ forloop.counter }}

+
+
+ {% include 'dcim/inc/cable_trace_end.html' with end=near_end %} +
+
+

+ + {% if cable.label %}{{ cable.label }}{% else %}Cable #{{ cable.pk }}{% endif %} + +

+ {{ cable.get_status_display }}
+ {{ cable.get_type_display|default:"" }} + {% if cable.length %}- {{ cable.length }}{{ cable.length_unit }}{% endif %} +   +
+
+ {% include 'dcim/inc/cable_trace_end.html' with end=far_end %} +
+
+ {% if not forloop.last %}
{% endif %} + {% endfor %} +{% endblock %} diff --git a/netbox/templates/dcim/device.html b/netbox/templates/dcim/device.html index 1c2bbfccd..7c1042d2c 100644 --- a/netbox/templates/dcim/device.html +++ b/netbox/templates/dcim/device.html @@ -777,7 +777,6 @@
{% include 'inc/modal.html' with modal_name='graphs' %} -{% include 'inc/modal.html' with modal_name='cabletrace' %} {% include 'secrets/inc/private_key_modal.html' %} {% endblock %} @@ -848,7 +847,6 @@ $('button.toggle-ips').click(function() { return false; }); - {% endblock %} diff --git a/netbox/templates/dcim/inc/cable_trace_end.html b/netbox/templates/dcim/inc/cable_trace_end.html new file mode 100644 index 000000000..1ed12c877 --- /dev/null +++ b/netbox/templates/dcim/inc/cable_trace_end.html @@ -0,0 +1,27 @@ +{% load helpers %} + +
+
+ {% if end.device %} + {{ end.device }} + {% else %} + {{ end.circuit }} + {% endif %} +
+
+ {% if end.device %} + {# Device component #} + {% with model=end|model_name %} + {{ model|bettertitle }} {{ end }}
+ {% if model == 'interface' %} + {{ end.get_form_factor_display }} + {% elif model == 'front port' or model == 'rear port' %} + {{ end.get_type_display }} + {% endif %} + {% endwith %} + {% else %} + {# Circuit termination #} + Side {{ end.term_side }} + {% endif %} +
+
diff --git a/netbox/templates/dcim/inc/interface.html b/netbox/templates/dcim/inc/interface.html index a3bc3400a..cfc41ef9b 100644 --- a/netbox/templates/dcim/inc/interface.html +++ b/netbox/templates/dcim/inc/interface.html @@ -32,9 +32,9 @@ {% if iface.cable %} {{ iface.cable }} - + {% else %} — {% endif %}