diff --git a/netbox/dcim/tables.py b/netbox/dcim/tables.py index 4502c0aec..be2d43631 100644 --- a/netbox/dcim/tables.py +++ b/netbox/dcim/tables.py @@ -1,7 +1,8 @@ import django_tables2 as tables from django_tables2.utils import Accessor -from .models import Site, Rack, DeviceType, Device, ConsolePort, PowerPort +from .models import Site, Rack, DeviceType, ConsolePortTemplate, ConsoleServerPortTemplate, PowerPortTemplate, \ + PowerOutletTemplate, InterfaceTemplate, Device, ConsolePort, PowerPort PREFIXES_PER_VLAN = """ @@ -98,6 +99,112 @@ class DeviceTypeBulkEditTable(DeviceTypeTable): fields = ('pk', 'model', 'manufacturer', 'u_height') +# +# Device type components +# + +class ConsolePortTemplateTable(tables.Table): + + class Meta: + model = ConsolePortTemplate + fields = ('name',) + empty_text = "None" + show_header = False + attrs = { + 'class': 'table table-hover panel-body', + } + + +class ConsolePortTemplateBulkDeleteTable(ConsolePortTemplateTable): + pk = tables.CheckBoxColumn() + + class Meta(ConsolePortTemplateTable.Meta): + model = None # django_tables2 bugfix + fields = ('pk', 'name') + + +class ConsoleServerPortTemplateTable(tables.Table): + + class Meta: + model = ConsoleServerPortTemplate + fields = ('name',) + empty_text = "None" + show_header = False + attrs = { + 'class': 'table table-hover panel-body', + } + + +class ConsoleServerPortTemplateBulkDeleteTable(ConsoleServerPortTemplateTable): + pk = tables.CheckBoxColumn() + + class Meta(ConsoleServerPortTemplateTable.Meta): + model = None # django_tables2 bugfix + fields = ('pk', 'name') + + +class PowerPortTemplateTable(tables.Table): + + class Meta: + model = PowerPortTemplate + fields = ('name',) + empty_text = "None" + show_header = False + attrs = { + 'class': 'table table-hover panel-body', + } + + +class PowerPortTemplateBulkDeleteTable(PowerPortTemplateTable): + pk = tables.CheckBoxColumn() + + class Meta(PowerPortTemplateTable.Meta): + model = None # django_tables2 bugfix + fields = ('pk', 'name') + + +class PowerOutletTemplateTable(tables.Table): + + class Meta: + model = PowerOutletTemplate + fields = ('name',) + empty_text = "None" + show_header = False + attrs = { + 'class': 'table table-hover panel-body', + } + + +class PowerOutletTemplateBulkDeleteTable(PowerOutletTemplateTable): + pk = tables.CheckBoxColumn() + + class Meta(PowerOutletTemplateTable.Meta): + model = None # django_tables2 bugfix + fields = ('pk', 'name') + + +class InterfaceTemplateTable(tables.Table): + + class Meta: + model = InterfaceTemplate + fields = ('name',) + empty_text = "None" + show_header = False + attrs = { + 'class': 'table table-hover panel-body', + } + + +class InterfaceTemplateBulkDeleteTable(InterfaceTemplateTable): + pk = tables.CheckBoxColumn() + + class Meta(InterfaceTemplateTable.Meta): + model = None # django_tables2 bugfix + fields = ('pk', 'name') + + + + # # Devices # diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index 6f65b7e8d..f328237b0 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -3,7 +3,8 @@ from django.conf.urls import url from secrets.views import secret_add from . import views -from .forms import ConsolePortTemplateForm +from .models import ConsolePortTemplate, ConsoleServerPortTemplate, PowerPortTemplate, PowerOutletTemplate, \ + InterfaceTemplate urlpatterns = [ @@ -34,16 +35,28 @@ urlpatterns = [ url(r'^device-types/(?P\d+)/$', views.devicetype, name='devicetype'), url(r'^device-types/(?P\d+)/edit/$', views.devicetype_edit, name='devicetype_edit'), url(r'^device-types/(?P\d+)/delete/$', views.devicetype_delete, name='devicetype_delete'), + + # Component templates url(r'^device-types/(?P\d+)/console-ports/add/$', views.ConsolePortTemplateAddView.as_view(), name='devicetype_add_consoleport'), - url(r'^device-types/(?P\d+)/console-server-ports/add/$', views.ConsolePortTemplateAddView.as_view(), + url(r'^device-types/(?P\d+)/console-ports/delete/$', views.component_template_delete, + {'model': ConsolePortTemplate}, name='devicetype_delete_consoleport'), + url(r'^device-types/(?P\d+)/console-server-ports/add/$', views.ConsoleServerPortTemplateAddView.as_view(), name='devicetype_add_consoleserverport'), + url(r'^device-types/(?P\d+)/console-server-ports/delete/$', views.component_template_delete, + {'model': ConsoleServerPortTemplate}, name='devicetype_delete_consoleserverport'), url(r'^device-types/(?P\d+)/power-ports/add/$', views.PowerPortTemplateAddView.as_view(), name='devicetype_add_powerport'), + url(r'^device-types/(?P\d+)/power-ports/delete/$', views.component_template_delete, + {'model': PowerPortTemplate}, name='devicetype_delete_powerport'), url(r'^device-types/(?P\d+)/power-outlets/add/$', views.PowerOutletTemplateAddView.as_view(), name='devicetype_add_poweroutlet'), + url(r'^device-types/(?P\d+)/power-outlets/delete/$', views.component_template_delete, + {'model': PowerOutletTemplate}, name='devicetype_delete_poweroutlet'), url(r'^device-types/(?P\d+)/interfaces/add/$', views.InterfaceTemplateAddView.as_view(), name='devicetype_add_interface'), + url(r'^device-types/(?P\d+)/interfaces/delete/$', views.component_template_delete, + {'model': InterfaceTemplate}, name='devicetype_delete_interface'), # Devices url(r'^devices/$', views.DeviceListView.as_view(), name='device_list'), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 71eafb298..10a82a15c 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -6,6 +6,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse from django.db.models import Count, ProtectedError +from django.forms import ModelMultipleChoiceField, MultipleHiddenInput from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect, render from django.utils.http import urlencode @@ -33,7 +34,10 @@ from .models import Site, Rack, DeviceType, ConsolePortTemplate, ConsoleServerPo PowerOutletTemplate, InterfaceTemplate, Device, ConsolePort, ConsoleServerPort, PowerPort, PowerOutlet, Interface, \ InterfaceConnection, Module, CONNECTION_STATUS_CONNECTED from .tables import SiteTable, RackTable, RackBulkEditTable, DeviceTypeTable, DeviceTypeBulkEditTable, DeviceTable, \ - DeviceBulkEditTable, DeviceImportTable, ConsoleConnectionTable, PowerConnectionTable, InterfaceConnectionTable + DeviceBulkEditTable, DeviceImportTable, ConsoleConnectionTable, PowerConnectionTable, InterfaceConnectionTable, \ + ConsolePortTemplateTable, ConsoleServerPortTemplateTable, PowerPortTemplateTable, PowerOutletTemplateTable, \ + InterfaceTemplateTable, ConsolePortTemplateBulkDeleteTable, ConsoleServerPortTemplateBulkDeleteTable, \ + PowerPortTemplateBulkDeleteTable, PowerOutletTemplateBulkDeleteTable, InterfaceTemplateBulkDeleteTable EXPANSION_PATTERN = '\[(\d+-\d+)\]' @@ -331,8 +335,27 @@ def devicetype(request, pk): devicetype = get_object_or_404(DeviceType, pk=pk) + # Component tables + if request.user.has_perm('dcim.change_devicetype'): + consoleport_table = ConsolePortTemplateBulkDeleteTable(ConsolePortTemplate.objects.filter(device_type=devicetype)) + consoleserverport_table = ConsoleServerPortTemplateBulkDeleteTable(ConsoleServerPortTemplate.objects.filter(device_type=devicetype)) + powerport_table = PowerPortTemplateBulkDeleteTable(PowerPortTemplate.objects.filter(device_type=devicetype)) + poweroutlet_table = PowerOutletTemplateBulkDeleteTable(PowerOutletTemplate.objects.filter(device_type=devicetype)) + interface_table = InterfaceTemplateBulkDeleteTable(InterfaceTemplate.objects.filter(device_type=devicetype)) + else: + consoleport_table = ConsolePortTemplateTable(ConsolePortTemplate.objects.filter(device_type=devicetype)) + consoleserverport_table = ConsoleServerPortTemplateTable(ConsoleServerPortTemplate.objects.filter(device_type=devicetype)) + powerport_table = PowerPortTemplateTable(PowerPortTemplate.objects.filter(device_type=devicetype)) + poweroutlet_table = PowerOutletTemplateTable(PowerOutletTemplate.objects.filter(device_type=devicetype)) + interface_table = InterfaceTemplateTable(InterfaceTemplate.objects.filter(device_type=devicetype)) + return render(request, 'dcim/devicetype.html', { 'devicetype': devicetype, + 'consoleport_table': consoleport_table, + 'consoleserverport_table': consoleserverport_table, + 'powerport_table': powerport_table, + 'poweroutlet_table': poweroutlet_table, + 'interface_table': interface_table, }) @@ -432,6 +455,10 @@ class DeviceTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): redirect_url = 'dcim:devicetype_list' +# +# Device type components +# + class ComponentTemplateCreateView(View): model = None form = None @@ -506,6 +533,45 @@ class InterfaceTemplateAddView(ComponentTemplateCreateView): form = InterfaceTemplateForm +def component_template_delete(request, pk, model): + + devicetype = get_object_or_404(DeviceType, pk=pk) + + class ComponentTemplateBulkDeleteForm(ConfirmationForm): + pk = ModelMultipleChoiceField(queryset=model.objects.all(), widget=MultipleHiddenInput) + + if '_confirm' in request.POST: + form = ComponentTemplateBulkDeleteForm(request.POST) + if form.is_valid(): + + # Delete component templates + objects_to_delete = model.objects.filter(pk__in=[v.id for v in form.cleaned_data['pk']]) + try: + deleted_count = objects_to_delete.count() + objects_to_delete.delete() + except ProtectedError, e: + handle_protectederror(list(objects_to_delete), request, e) + return redirect('dcim:devicetype', {'pk': devicetype.pk}) + + messages.success(request, "Deleted {} {}".format(deleted_count, model._meta.verbose_name_plural)) + return redirect('dcim:devicetype', pk=devicetype.pk) + + else: + form = ComponentTemplateBulkDeleteForm(initial={'pk': request.POST.getlist('pk')}) + + selected_objects = model.objects.filter(pk__in=form.initial.get('pk')) + if not selected_objects: + messages.warning(request, "No {} were selected for deletion.".format(model._meta.verbose_name_plural)) + return redirect('dcim:devicetype', pk=devicetype.pk) + + return render(request, 'dcim/component_template_delete.html', { + 'devicetype': devicetype, + 'form': form, + 'selected_objects': selected_objects, + 'cancel_url': reverse('dcim:devicetype', kwargs={'pk': devicetype.pk}), + }) + + # # Devices # diff --git a/netbox/templates/dcim/component_template_delete.html b/netbox/templates/dcim/component_template_delete.html new file mode 100644 index 000000000..caeab2962 --- /dev/null +++ b/netbox/templates/dcim/component_template_delete.html @@ -0,0 +1,13 @@ +{% extends 'utilities/confirmation_form.html' %} +{% load form_helpers %} + +{% block title %}Delete devie type components?{% endblock %} + +{% block message %} +

Are you sure you want to delete these components from {{ devicetype }}?

+
    + {% for o in selected_objects %} +
  • {{ o }}
  • + {% endfor %} +
+{% endblock %} diff --git a/netbox/templates/dcim/devicetype.html b/netbox/templates/dcim/devicetype.html index 0427eab00..4510e6e43 100644 --- a/netbox/templates/dcim/devicetype.html +++ b/netbox/templates/dcim/devicetype.html @@ -73,90 +73,13 @@ -
-
- {% if perms.dcim.change_devicetype %} - Add Console Ports - {% endif %} - Console Ports -
- - {% for cp in devicetype.console_port_templates.all %} - - - - - {% endfor %} -
{{ cp.name }}
-
-
-
- {% if perms.dcim.change_devicetype %} - Add Power Ports - {% endif %} - Power Ports -
- - {% for pp in devicetype.power_port_templates.all %} - - - - - {% endfor %} -
{{ pp.name }}
-
+ {% include 'dcim/inc/devicetype_component_table.html' with table=consoleport_table title='Console Ports' add_url='dcim:devicetype_add_consoleport' delete_url='dcim:devicetype_delete_consoleport' %} + {% include 'dcim/inc/devicetype_component_table.html' with table=powerport_table title='Power Ports' add_url='dcim:devicetype_add_powerport' delete_url='dcim:devicetype_delete_powerport' %}
-
-
- {% if perms.dcim.change_devicetype %} - Add Interfaces - {% endif %} - Interfaces -
- - {% for iface in devicetype.interface_templates.all %} - - - - - - - {% endfor %} -
{{ iface.name }}{{ iface.get_form_factor_display }}{{ iface.mgmt_only|yesno|capfirst }}
-
-
-
- {% if perms.dcim.change_devicetype %} - Add Console Server Ports - {% endif %} - Console Server Ports -
- - {% for csp in devicetype.cs_port_templates.all %} - - - - - {% endfor %} -
{{ csp.name }}
-
-
-
- {% if perms.dcim.change_devicetype %} - Add Power Outlet - {% endif %} - Power Outlets -
- - {% for po in devicetype.power_outlet_templates.all %} - - - - - {% endfor %} -
{{ po.name }}
-
+ {% include 'dcim/inc/devicetype_component_table.html' with table=interface_table title='Interfaces' add_url='dcim:devicetype_add_interface' delete_url='dcim:devicetype_delete_interface' %} + {% include 'dcim/inc/devicetype_component_table.html' with table=consoleserverport_table title='Console Server Ports' add_url='dcim:devicetype_add_consoleserverport' delete_url='dcim:devicetype_delete_consoleserverport' %} + {% include 'dcim/inc/devicetype_component_table.html' with table=poweroutlet_table title='Power Outlets' add_url='dcim:devicetype_add_poweroutlet' delete_url='dcim:devicetype_delete_poweroutlet' %}
{% endblock %} diff --git a/netbox/templates/dcim/inc/devicetype_component_table.html b/netbox/templates/dcim/inc/devicetype_component_table.html new file mode 100644 index 000000000..3c8913dc6 --- /dev/null +++ b/netbox/templates/dcim/inc/devicetype_component_table.html @@ -0,0 +1,29 @@ +{% load render_table from django_tables2 %} +{% if perms.dcim.change_devicetype %} +
+ {% csrf_token %} +
+
+ Add {{ title }} + {{ title }} +
+ {% render_table table 'table.html' %} + {% if table.rows %} + + {% endif %} +
+
+{% else %} +
+
+ {{ title }} +
+ + {% render_table table table_template|default:'table.html' %} +
+
+{% endif %}