From 9985f2cb82af6b37e70f5d06a04953201a33f3ab Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 24 Oct 2018 14:24:02 -0400 Subject: [PATCH] Added CableListView --- netbox/dcim/tables.py | 38 ++++++++++++++++- netbox/dcim/urls.py | 16 ++++---- netbox/dcim/views.py | 59 +++++++++++++++++---------- netbox/templates/dcim/cable_list.html | 17 ++++++++ netbox/templates/inc/nav_menu.html | 5 ++- 5 files changed, 105 insertions(+), 30 deletions(-) create mode 100644 netbox/templates/dcim/cable_list.html diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index d72497ecd..df7b6d89b 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -4,7 +4,7 @@ from django_tables2.utils import Accessor from tenancy.tables import COL_TENANT from utilities.tables import BaseTable, BooleanColumn, ToggleColumn from .models import ( - ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay, + Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay, DeviceBayTemplate, DeviceRole, DeviceType, FrontPanelPort, FrontPanelPortTemplate, Interface, InterfaceTemplate, InventoryItem, Manufacturer, Platform, PowerOutlet, PowerOutletTemplate, PowerPort, PowerPortTemplate, Rack, RackGroup, RackReservation, RearPanelPort, RearPanelPortTemplate, Region, Site, VirtualChassis, @@ -616,6 +616,42 @@ class DeviceBayTable(BaseTable): fields = ('name',) +# +# Cables +# + +class CableTable(BaseTable): + device_a = tables.LinkColumn( + viewname='dcim:device', + accessor=Accessor('endpoint_a.device'), + args=[Accessor('endpoint_a.device.pk')], + verbose_name='Device A' + ) + termination_a = tables.Column( + accessor=Accessor('endpoint_a.name'), + verbose_name='Component' + ) + device_b = tables.LinkColumn( + viewname='dcim:device', + accessor=Accessor('endpoint_b.device'), + args=[Accessor('endpoint_b.device.pk')], + verbose_name='Device B' + ) + termination_b = tables.Column( + accessor=Accessor('endpoint_b.name'), + verbose_name='Component' + ) + # django-tables2 adds CSS `class="label"` which causes rendering issues + _label = tables.Column( + accessor=Accessor('label'), + verbose_name='Label' + ) + + class Meta(BaseTable.Meta): + model = Cable + fields = ('device_a', 'termination_a', 'device_b', 'termination_b', 'status', '_label', 'color') + + # # Device connections # diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index a7f52b80c..0a71c698f 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -162,7 +162,7 @@ urlpatterns = [ url(r'^devices/(?P\d+)/console-ports/add/$', views.ConsolePortCreateView.as_view(), name='consoleport_add'), url(r'^devices/(?P\d+)/console-ports/delete/$', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'), # url(r'^console-ports/(?P\d+)/connect/$', views.ConsolePortConnectView.as_view(), name='consoleport_connect'), - url(r'^console-ports/(?P\d+)/connect/$', views.CableConnectView.as_view(), name='consoleport_connect', kwargs={'endpoint_a_type': ConsolePort}), + url(r'^console-ports/(?P\d+)/connect/$', views.CableCreateView.as_view(), name='consoleport_connect', kwargs={'endpoint_a_type': ConsolePort}), 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'), @@ -173,7 +173,7 @@ urlpatterns = [ url(r'^devices/(?P\d+)/console-server-ports/disconnect/$', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'), url(r'^devices/(?P\d+)/console-server-ports/delete/$', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'), # url(r'^console-server-ports/(?P\d+)/connect/$', views.ConsoleServerPortConnectView.as_view(), name='consoleserverport_connect'), - url(r'^console-server-ports/(?P\d+)/connect/$', views.CableConnectView.as_view(), name='consoleserverport_connect', kwargs={'endpoint_a_type': ConsoleServerPort}), + url(r'^console-server-ports/(?P\d+)/connect/$', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'endpoint_a_type': ConsoleServerPort}), 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'), @@ -184,7 +184,7 @@ urlpatterns = [ url(r'^devices/(?P\d+)/power-ports/add/$', views.PowerPortCreateView.as_view(), name='powerport_add'), url(r'^devices/(?P\d+)/power-ports/delete/$', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'), # url(r'^power-ports/(?P\d+)/connect/$', views.PowerPortConnectView.as_view(), name='powerport_connect'), - url(r'^power-ports/(?P\d+)/connect/$', views.CableConnectView.as_view(), name='powerport_connect', kwargs={'endpoint_a_type': PowerPort}), + url(r'^power-ports/(?P\d+)/connect/$', views.CableCreateView.as_view(), name='powerport_connect', kwargs={'endpoint_a_type': PowerPort}), 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'), @@ -195,7 +195,7 @@ urlpatterns = [ url(r'^devices/(?P\d+)/power-outlets/disconnect/$', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'), url(r'^devices/(?P\d+)/power-outlets/delete/$', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'), # url(r'^power-outlets/(?P\d+)/connect/$', views.PowerOutletConnectView.as_view(), name='poweroutlet_connect'), - url(r'^power-outlets/(?P\d+)/connect/$', views.CableConnectView.as_view(), name='poweroutlet_connect', kwargs={'endpoint_a_type': PowerOutlet}), + url(r'^power-outlets/(?P\d+)/connect/$', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'endpoint_a_type': PowerOutlet}), 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'), @@ -207,9 +207,7 @@ urlpatterns = [ url(r'^devices/(?P\d+)/interfaces/edit/$', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'), url(r'^devices/(?P\d+)/interfaces/disconnect/$', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'), url(r'^devices/(?P\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'), - # url(r'^devices/(?P\d+)/interface-connections/add/$', views.InterfaceConnectionAddView.as_view(), name='interfaceconnection_add'), - url(r'^interfaces/(?P\d+)/connect/$', views.CableConnectView.as_view(), name='interface_connect', kwargs={'endpoint_a_type': Interface}), - # url(r'^interface-connections/(?P\d+)/delete/$', views.InterfaceConnectionDeleteView.as_view(), name='interfaceconnection_delete'), + url(r'^interfaces/(?P\d+)/connect/$', views.CableCreateView.as_view(), name='interface_connect', kwargs={'endpoint_a_type': Interface}), url(r'^interfaces/(?P\d+)/$', views.InterfaceView.as_view(), name='interface'), url(r'^interfaces/(?P\d+)/edit/$', views.InterfaceEditView.as_view(), name='interface_edit'), url(r'^interfaces/(?P\d+)/assign-vlans/$', views.InterfaceAssignVLANsView.as_view(), name='interface_assign_vlans'), @@ -252,6 +250,10 @@ urlpatterns = [ url(r'^inventory-items/(?P\d+)/delete/$', views.InventoryItemDeleteView.as_view(), name='inventoryitem_delete'), url(r'^devices/(?P\d+)/inventory-items/add/$', views.InventoryItemEditView.as_view(), name='inventoryitem_add'), + # Cables + url(r'^cables/$', views.CableListView.as_view(), name='cable_list'), + url(r'^cables/(?P\d+)/delete/$', views.CableDeleteView.as_view(), name='cable_delete'), + # Console/power/interface connections url(r'^console-connections/$', views.ConsoleConnectionsListView.as_view(), name='console_connections_list'), # url(r'^console-connections/import/$', views.ConsoleConnectionsBulkImportView.as_view(), name='console_connections_import'), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 991ec2ac0..3246ca281 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1,18 +1,14 @@ from operator import attrgetter -from django.apps import apps from django.contrib import messages from django.contrib.auth.mixins import PermissionRequiredMixin -from django.core.exceptions import ObjectDoesNotExist from django.core.paginator import EmptyPage, PageNotAnInteger from django.db import transaction -from django.db.models import Count, F, Q +from django.db.models import Count, F from django.forms import modelformset_factory -from django.http import Http404, HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils.html import escape -from django.utils.http import urlencode from django.utils.safestring import mark_safe from django.views.generic import View from natsort import natsorted @@ -2013,6 +2009,43 @@ class DeviceBulkAddDeviceBayView(PermissionRequiredMixin, BulkComponentCreateVie default_return_url = 'dcim:device_list' +# +# Cables +# + +class CableListView(ObjectListView): + queryset = Cable.objects.prefetch_related( + 'endpoint_a__device', 'endpoint_b__device' + ) + # filter = filters.CableFilter + # filter_form = forms.CableFilterForm + table = tables.CableTable + template_name = 'dcim/cable_list.html' + + +class CableCreateView(PermissionRequiredMixin, ObjectEditView): + permission_required = 'dcim.add_cable' + model = Cable + model_form = forms.CableForm + template_name = 'dcim/cable_connect.html' + + def alter_obj(self, obj, request, url_args, url_kwargs): + + # Retrieve endpoint A based on the given type and PK + endpoint_a_type = url_kwargs.get('endpoint_a_type') + endpoint_a_id = url_kwargs.get('endpoint_a_id') + obj.endpoint_a = endpoint_a_type.objects.get(pk=endpoint_a_id) + + return obj + + +class CableDeleteView(PermissionRequiredMixin, ObjectDeleteView): + permission_required = 'dcim.delete_cable' + model = Cable + default_return_url = 'dcim:cable_list' + + + # # Connections # @@ -2058,22 +2091,6 @@ class InterfaceConnectionsListView(ObjectListView): template_name = 'dcim/interface_connections_list.html' -class CableConnectView(PermissionRequiredMixin, ObjectEditView): - permission_required = 'dcim.add_cable' - model = Cable - model_form = forms.CableForm - template_name = 'dcim/cable_connect.html' - - def alter_obj(self, obj, request, url_args, url_kwargs): - - # Retrieve endpoint A based on the given type and PK - endpoint_a_type = url_kwargs.get('endpoint_a_type') - endpoint_a_id = url_kwargs.get('endpoint_a_id') - obj.endpoint_a = endpoint_a_type.objects.get(pk=endpoint_a_id) - - return obj - - # # Inventory items # diff --git a/netbox/templates/dcim/cable_list.html b/netbox/templates/dcim/cable_list.html new file mode 100644 index 000000000..b942986e3 --- /dev/null +++ b/netbox/templates/dcim/cable_list.html @@ -0,0 +1,17 @@ +{% extends '_base.html' %} +{% load buttons %} + +{% block content %} +
+ {% export_button content_type %} +
+

{% block title %}Cables{% endblock %}

+
+
+ {% include 'responsive_table.html' %} +
+
+ {% include 'inc/search_panel.html' %} +
+
+{% endblock %} diff --git a/netbox/templates/inc/nav_menu.html b/netbox/templates/inc/nav_menu.html index 08ecb926d..ef6329564 100644 --- a/netbox/templates/inc/nav_menu.html +++ b/netbox/templates/inc/nav_menu.html @@ -113,7 +113,7 @@ -