Browse Source

Added dedicated cable trace view; removed modal

Jeremy Stretch 7 years ago
parent
commit
470aabe1d7

+ 2 - 1
netbox/circuits/urls.py

@@ -1,6 +1,6 @@
 from django.conf.urls import url
 from django.conf.urls import url
 
 
-from dcim.views import CableCreateView
+from dcim.views import CableCreateView, CableTraceView
 from extras.views import ObjectChangeLogView
 from extras.views import ObjectChangeLogView
 from . import views
 from . import views
 from .models import Circuit, CircuitTermination, CircuitType, Provider
 from .models import Circuit, CircuitTermination, CircuitType, Provider
@@ -44,5 +44,6 @@ urlpatterns = [
     url(r'^circuit-terminations/(?P<pk>\d+)/edit/$', views.CircuitTerminationEditView.as_view(), name='circuittermination_edit'),
     url(r'^circuit-terminations/(?P<pk>\d+)/edit/$', views.CircuitTerminationEditView.as_view(), name='circuittermination_edit'),
     url(r'^circuit-terminations/(?P<pk>\d+)/delete/$', views.CircuitTerminationDeleteView.as_view(), name='circuittermination_delete'),
     url(r'^circuit-terminations/(?P<pk>\d+)/delete/$', views.CircuitTerminationDeleteView.as_view(), name='circuittermination_delete'),
     url(r'^circuit-terminations/(?P<termination_a_id>\d+)/connect/$', CableCreateView.as_view(), name='circuittermination_connect', kwargs={'termination_a_type': CircuitTermination}),
     url(r'^circuit-terminations/(?P<termination_a_id>\d+)/connect/$', CableCreateView.as_view(), name='circuittermination_connect', kwargs={'termination_a_type': CircuitTermination}),
+    url(r'^circuit-terminations/(?P<pk>\d+)/trace/$', CableTraceView.as_view(), name='circuittermination_trace', kwargs={'model': CircuitTermination}),
 
 
 ]
 ]

+ 8 - 0
netbox/dcim/models.py

@@ -85,6 +85,7 @@ class CableTermination(models.Model):
             ]
             ]
         """
         """
         def get_peer_port(termination, position=1):
         def get_peer_port(termination, position=1):
+            from circuits.models import CircuitTermination
 
 
             # Map a front port to its corresponding rear port
             # Map a front port to its corresponding rear port
             if isinstance(termination, FrontPort):
             if isinstance(termination, FrontPort):
@@ -102,6 +103,13 @@ class CableTermination(models.Model):
                 )
                 )
                 return peer_port, 1
                 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
             # Termination is not a pass-through port
             else:
             else:
                 return None, None
                 return None, None

+ 9 - 2
netbox/dcim/urls.py

@@ -5,8 +5,8 @@ from ipam.views import ServiceCreateView
 from secrets.views import secret_add
 from secrets.views import secret_add
 from . import views
 from . import views
 from .models import (
 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'
 app_name = 'dcim'
@@ -166,6 +166,7 @@ urlpatterns = [
     url(r'^console-ports/(?P<pk>\d+)/disconnect/$', views.ConsolePortDisconnectView.as_view(), name='consoleport_disconnect'),
     url(r'^console-ports/(?P<pk>\d+)/disconnect/$', views.ConsolePortDisconnectView.as_view(), name='consoleport_disconnect'),
     url(r'^console-ports/(?P<pk>\d+)/edit/$', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
     url(r'^console-ports/(?P<pk>\d+)/edit/$', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
     url(r'^console-ports/(?P<pk>\d+)/delete/$', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'),
     url(r'^console-ports/(?P<pk>\d+)/delete/$', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'),
+    url(r'^console-ports/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='consoleport_trace', kwargs={'model': ConsolePort}),
 
 
     # Console server ports
     # Console server ports
     url(r'^devices/console-server-ports/add/$', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'),
     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<pk>\d+)/disconnect/$', views.ConsoleServerPortDisconnectView.as_view(), name='consoleserverport_disconnect'),
     url(r'^console-server-ports/(?P<pk>\d+)/disconnect/$', views.ConsoleServerPortDisconnectView.as_view(), name='consoleserverport_disconnect'),
     url(r'^console-server-ports/(?P<pk>\d+)/edit/$', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'),
     url(r'^console-server-ports/(?P<pk>\d+)/edit/$', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'),
     url(r'^console-server-ports/(?P<pk>\d+)/delete/$', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'),
     url(r'^console-server-ports/(?P<pk>\d+)/delete/$', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'),
+    url(r'^console-server-ports/(?P<pk>\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'),
     url(r'^console-server-ports/rename/$', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'),
 
 
     # Power ports
     # Power ports
@@ -188,6 +190,7 @@ urlpatterns = [
     url(r'^power-ports/(?P<pk>\d+)/disconnect/$', views.PowerPortDisconnectView.as_view(), name='powerport_disconnect'),
     url(r'^power-ports/(?P<pk>\d+)/disconnect/$', views.PowerPortDisconnectView.as_view(), name='powerport_disconnect'),
     url(r'^power-ports/(?P<pk>\d+)/edit/$', views.PowerPortEditView.as_view(), name='powerport_edit'),
     url(r'^power-ports/(?P<pk>\d+)/edit/$', views.PowerPortEditView.as_view(), name='powerport_edit'),
     url(r'^power-ports/(?P<pk>\d+)/delete/$', views.PowerPortDeleteView.as_view(), name='powerport_delete'),
     url(r'^power-ports/(?P<pk>\d+)/delete/$', views.PowerPortDeleteView.as_view(), name='powerport_delete'),
+    url(r'^power-ports/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='powerport_trace', kwargs={'model': PowerPort}),
 
 
     # Power outlets
     # Power outlets
     url(r'^devices/power-outlets/add/$', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'),
     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<pk>\d+)/disconnect/$', views.PowerOutletDisconnectView.as_view(), name='poweroutlet_disconnect'),
     url(r'^power-outlets/(?P<pk>\d+)/disconnect/$', views.PowerOutletDisconnectView.as_view(), name='poweroutlet_disconnect'),
     url(r'^power-outlets/(?P<pk>\d+)/edit/$', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'),
     url(r'^power-outlets/(?P<pk>\d+)/edit/$', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'),
     url(r'^power-outlets/(?P<pk>\d+)/delete/$', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'),
     url(r'^power-outlets/(?P<pk>\d+)/delete/$', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'),
+    url(r'^power-outlets/(?P<pk>\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'),
     url(r'^power-outlets/rename/$', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'),
 
 
     # Interfaces
     # Interfaces
@@ -213,6 +217,7 @@ urlpatterns = [
     url(r'^interfaces/(?P<pk>\d+)/assign-vlans/$', views.InterfaceAssignVLANsView.as_view(), name='interface_assign_vlans'),
     url(r'^interfaces/(?P<pk>\d+)/assign-vlans/$', views.InterfaceAssignVLANsView.as_view(), name='interface_assign_vlans'),
     url(r'^interfaces/(?P<pk>\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'),
     url(r'^interfaces/(?P<pk>\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'),
     url(r'^interfaces/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='interface_changelog', kwargs={'model': Interface}),
     url(r'^interfaces/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='interface_changelog', kwargs={'model': Interface}),
+    url(r'^interfaces/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='interface_trace', kwargs={'model': Interface}),
     url(r'^interfaces/rename/$', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'),
     url(r'^interfaces/rename/$', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'),
 
 
     # Front ports
     # Front ports
@@ -221,6 +226,7 @@ urlpatterns = [
     url(r'^devices/(?P<pk>\d+)/front-ports/delete/$', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'),
     url(r'^devices/(?P<pk>\d+)/front-ports/delete/$', views.FrontPortBulkDeleteView.as_view(), name='frontport_bulk_delete'),
     url(r'^front-ports/(?P<pk>\d+)/edit/$', views.FrontPortEditView.as_view(), name='frontport_edit'),
     url(r'^front-ports/(?P<pk>\d+)/edit/$', views.FrontPortEditView.as_view(), name='frontport_edit'),
     url(r'^front-ports/(?P<pk>\d+)/delete/$', views.FrontPortDeleteView.as_view(), name='frontport_delete'),
     url(r'^front-ports/(?P<pk>\d+)/delete/$', views.FrontPortDeleteView.as_view(), name='frontport_delete'),
+    url(r'^front-ports/(?P<pk>\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'),
     url(r'^front-ports/rename/$', views.FrontPortBulkRenameView.as_view(), name='frontport_bulk_rename'),
 
 
     # Rear ports
     # Rear ports
@@ -229,6 +235,7 @@ urlpatterns = [
     url(r'^devices/(?P<pk>\d+)/rear-ports/delete/$', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'),
     url(r'^devices/(?P<pk>\d+)/rear-ports/delete/$', views.RearPortBulkDeleteView.as_view(), name='rearport_bulk_delete'),
     url(r'^rear-ports/(?P<pk>\d+)/edit/$', views.RearPortEditView.as_view(), name='rearport_edit'),
     url(r'^rear-ports/(?P<pk>\d+)/edit/$', views.RearPortEditView.as_view(), name='rearport_edit'),
     url(r'^rear-ports/(?P<pk>\d+)/delete/$', views.RearPortDeleteView.as_view(), name='rearport_delete'),
     url(r'^rear-ports/(?P<pk>\d+)/delete/$', views.RearPortDeleteView.as_view(), name='rearport_delete'),
+    url(r'^rear-ports/(?P<pk>\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'),
     url(r'^rear-ports/rename/$', views.RearPortBulkRenameView.as_view(), name='rearport_bulk_rename'),
 
 
     # Device bays
     # Device bays

+ 15 - 0
netbox/dcim/views.py

@@ -2063,6 +2063,21 @@ class CableDeleteView(PermissionRequiredMixin, ObjectDeleteView):
     default_return_url = 'dcim:cable_list'
     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
 # Connections
 #
 #

+ 6 - 3
netbox/templates/circuits/inc/circuit_termination.html

@@ -39,9 +39,12 @@
             <tr>
             <tr>
                 <td>Termination</td>
                 <td>Termination</td>
                 <td>
                 <td>
-                    {% if termination.connected_endpoint %}
-                        <a href="{% url 'dcim:device' pk=termination.connected_endpoint.device.pk %}">{{ termination.connected_endpoint.device }}</a>
-                        <i class="fa fa-angle-right"></i> {{ termination.connected_endpoint }}
+                    {% if termination.cable %}
+                        <a href="{{ termination.cable.get_absolute_url }}">{{ termination.cable }}</a>
+                        {% if termination.connected_endpoint %}
+                            to <a href="{% url 'dcim:device' pk=termination.connected_endpoint.device.pk %}">{{ termination.connected_endpoint.device }}</a>
+                            <i class="fa fa-angle-right"></i> {{ termination.connected_endpoint }}
+                        {% endif %}
                     {% else %}
                     {% else %}
                         {% if perms.circuits.change_circuittermination %}
                         {% if perms.circuits.change_circuittermination %}
                             <div class="pull-right">
                             <div class="pull-right">

+ 42 - 0
netbox/templates/dcim/cable_trace.html

@@ -0,0 +1,42 @@
+{% extends '_base.html' %}
+{% load helpers %}
+
+{% block header %}
+    <h1>{% block title %}Cable Trace for {{ obj }}{% endblock %}</h1>
+{% endblock %}
+
+{% block content %}
+    <div class="row">
+        <div class="col-md-4 col-md-offset-1 text-center">
+            <h4>Near End</h4>
+        </div>
+        <div class="col-md-4 col-md-offset-3 text-center">
+            <h4>Far End</h4>
+        </div>
+    </div>
+    {% for near_end, cable, far_end in trace %}
+        <div class="row">
+            <div class="col-md-1 text-right">
+                <h3>{{ forloop.counter }}</h3>
+            </div>
+            <div class="col-md-4">
+                {% include 'dcim/inc/cable_trace_end.html' with end=near_end %}
+            </div>
+            <div class="col-md-3 text-center">
+                <h4>
+                    <a href="{% url 'dcim:cable' pk=cable.pk %}">
+                        {% if cable.label %}<code>{{ cable.label }}</code>{% else %}Cable #{{ cable.pk }}{% endif %}
+                    </a>
+                </h4>
+                {{ cable.get_status_display }}<br />
+                {{ cable.get_type_display|default:"" }}
+                {% if cable.length %}- {{ cable.length }}{{ cable.length_unit }}{% endif %}
+                <span class="label color-block center-block" style="background-color: #{{ cable.color }}">&nbsp;</span>
+            </div>
+            <div class="col-md-4">
+                {% include 'dcim/inc/cable_trace_end.html' with end=far_end %}
+            </div>
+        </div>
+        {% if not forloop.last %}<hr />{% endif %}
+    {% endfor %}
+{% endblock %}

+ 0 - 2
netbox/templates/dcim/device.html

@@ -777,7 +777,6 @@
         </div>
         </div>
     </div>
     </div>
 {% include 'inc/modal.html' with modal_name='graphs' %}
 {% include 'inc/modal.html' with modal_name='graphs' %}
-{% include 'inc/modal.html' with modal_name='cabletrace' %}
 {% include 'secrets/inc/private_key_modal.html' %}
 {% include 'secrets/inc/private_key_modal.html' %}
 {% endblock %}
 {% endblock %}
 
 
@@ -848,7 +847,6 @@ $('button.toggle-ips').click(function() {
     return false;
     return false;
 });
 });
 </script>
 </script>
-<script src="{% static 'js/cabletrace.js' %}?v{{ settings.VERSION }}"></script>
 <script src="{% static 'js/graphs.js' %}?v{{ settings.VERSION }}"></script>
 <script src="{% static 'js/graphs.js' %}?v{{ settings.VERSION }}"></script>
 <script src="{% static 'js/secrets.js' %}?v{{ settings.VERSION }}"></script>
 <script src="{% static 'js/secrets.js' %}?v{{ settings.VERSION }}"></script>
 {% endblock %}
 {% endblock %}

+ 27 - 0
netbox/templates/dcim/inc/cable_trace_end.html

@@ -0,0 +1,27 @@
+{% load helpers %}
+
+<div class="panel panel-default">
+    <div class="panel-heading text-center">
+        {% if end.device %}
+            <strong><a href="{{ end.device.get_absolute_url }}">{{ end.device }}</a></strong>
+        {% else %}
+            <strong><a href="{{ end.circuit.get_absolute_url }}">{{ end.circuit }}</a></strong>
+        {% endif %}
+    </div>
+    <div class="panel-body text-center">
+        {% if end.device %}
+            {# Device component #}
+            {% with model=end|model_name %}
+                <strong>{{ model|bettertitle }} {{ end }}</strong><br />
+                {% 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 #}
+            <strong>Side {{ end.term_side }}</strong>
+        {% endif %}
+    </div>
+</div>

+ 2 - 2
netbox/templates/dcim/inc/interface.html

@@ -32,9 +32,9 @@
     <td>
     <td>
         {% if iface.cable %}
         {% if iface.cable %}
             <a href="{{ iface.cable.get_absolute_url }}">{{ iface.cable }}</a>
             <a href="{{ iface.cable.get_absolute_url }}">{{ iface.cable }}</a>
-            <button type="button" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#cabletrace_modal" data-obj="{{ device.name }} - {{ iface.name }}" data-url="{% url 'dcim-api:interface-trace' pk=iface.pk %}" title="Trace cable">
+            <a href="{% url 'dcim:interface_trace' pk=iface.pk %}" class="btn btn-primary btn-xs" title="Trace">
                 <i class="fa fa-share-alt" aria-hidden="true"></i>
                 <i class="fa fa-share-alt" aria-hidden="true"></i>
-            </button>
+            </a>
         {% else %}
         {% else %}
             &mdash;
             &mdash;
         {% endif %}
         {% endif %}