Browse Source

Implemented DeviceType component template creation and deletion

Jeremy Stretch 9 years ago
parent
commit
61cbee15ca

+ 108 - 1
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 = """
@@ -99,6 +100,112 @@ class DeviceTypeBulkEditTable(DeviceTypeTable):
 
 
 #
+# 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
 #
 

+ 15 - 2
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<pk>\d+)/$', views.devicetype, name='devicetype'),
     url(r'^device-types/(?P<pk>\d+)/edit/$', views.devicetype_edit, name='devicetype_edit'),
     url(r'^device-types/(?P<pk>\d+)/delete/$', views.devicetype_delete, name='devicetype_delete'),
+
+    # Component templates
     url(r'^device-types/(?P<pk>\d+)/console-ports/add/$', views.ConsolePortTemplateAddView.as_view(),
         name='devicetype_add_consoleport'),
-    url(r'^device-types/(?P<pk>\d+)/console-server-ports/add/$', views.ConsolePortTemplateAddView.as_view(),
+    url(r'^device-types/(?P<pk>\d+)/console-ports/delete/$', views.component_template_delete,
+        {'model': ConsolePortTemplate}, name='devicetype_delete_consoleport'),
+    url(r'^device-types/(?P<pk>\d+)/console-server-ports/add/$', views.ConsoleServerPortTemplateAddView.as_view(),
         name='devicetype_add_consoleserverport'),
+    url(r'^device-types/(?P<pk>\d+)/console-server-ports/delete/$', views.component_template_delete,
+        {'model': ConsoleServerPortTemplate}, name='devicetype_delete_consoleserverport'),
     url(r'^device-types/(?P<pk>\d+)/power-ports/add/$', views.PowerPortTemplateAddView.as_view(),
         name='devicetype_add_powerport'),
+    url(r'^device-types/(?P<pk>\d+)/power-ports/delete/$', views.component_template_delete,
+        {'model': PowerPortTemplate}, name='devicetype_delete_powerport'),
     url(r'^device-types/(?P<pk>\d+)/power-outlets/add/$', views.PowerOutletTemplateAddView.as_view(),
         name='devicetype_add_poweroutlet'),
+    url(r'^device-types/(?P<pk>\d+)/power-outlets/delete/$', views.component_template_delete,
+        {'model': PowerOutletTemplate}, name='devicetype_delete_poweroutlet'),
     url(r'^device-types/(?P<pk>\d+)/interfaces/add/$', views.InterfaceTemplateAddView.as_view(),
         name='devicetype_add_interface'),
+    url(r'^device-types/(?P<pk>\d+)/interfaces/delete/$', views.component_template_delete,
+        {'model': InterfaceTemplate}, name='devicetype_delete_interface'),
 
     # Devices
     url(r'^devices/$', views.DeviceListView.as_view(), name='device_list'),

+ 67 - 1
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
 #

+ 13 - 0
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 %}
+    <p>Are you sure you want to delete these components from <strong>{{ devicetype }}</strong>?</p>
+    <ul>
+        {% for o in selected_objects %}
+            <li>{{ o }}</li>
+        {% endfor %}
+    </ul>
+{% endblock %}

+ 5 - 82
netbox/templates/dcim/devicetype.html

@@ -73,90 +73,13 @@
                 </tr>
             </table>
         </div>
-        <div class="panel panel-default">
-            <div class="panel-heading">
-                {% if perms.dcim.change_devicetype %}
-                    <a href="{% url 'dcim:devicetype_add_consoleport' pk=devicetype.pk %}" class="btn btn-primary btn-xs pull-right"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Console Ports</a>
-                {% endif %}
-                <strong>Console Ports</strong>
-            </div>
-            <table class="table table-hover panel-body">
-                {% for cp in devicetype.console_port_templates.all %}
-                    <tr>
-                        <td>{{ cp.name }}</td>
-                        <td></td>
-                    </tr>
-                {% endfor %}
-            </table>
-        </div>
-        <div class="panel panel-default">
-            <div class="panel-heading">
-                {% if perms.dcim.change_devicetype %}
-                    <a href="{% url 'dcim:devicetype_add_powerport' pk=devicetype.pk %}" class="btn btn-primary btn-xs pull-right"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Power Ports</a>
-                {% endif %}
-                <strong>Power Ports</strong>
-            </div>
-            <table class="table table-hover panel-body">
-                {% for pp in devicetype.power_port_templates.all %}
-                    <tr>
-                        <td>{{ pp.name }}</td>
-                        <td></td>
-                    </tr>
-                {% endfor %}
-            </table>
-        </div>
+        {% 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' %}
     </div>
 	<div class="col-md-6">
-        <div class="panel panel-default">
-            <div class="panel-heading">
-                {% if perms.dcim.change_devicetype %}
-                    <a href="{% url 'dcim:devicetype_add_interface' pk=devicetype.pk %}" class="btn btn-primary btn-xs pull-right"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Interfaces</a>
-                {% endif %}
-                <strong>Interfaces</strong>
-            </div>
-            <table class="table table-hover panel-body">
-                {% for iface in devicetype.interface_templates.all %}
-                    <tr>
-                        <td>{{ iface.name }}</td>
-                        <td>{{ iface.get_form_factor_display }}</td>
-                        <td>{{ iface.mgmt_only|yesno|capfirst }}</td>
-                        <td></td>
-                    </tr>
-                {% endfor %}
-            </table>
-        </div>
-        <div class="panel panel-default">
-            <div class="panel-heading">
-                {% if perms.dcim.change_devicetype %}
-                    <a href="{% url 'dcim:devicetype_add_consoleserverport' pk=devicetype.pk %}" class="btn btn-primary btn-xs pull-right"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Console Server Ports</a>
-                {% endif %}
-                <strong>Console Server Ports</strong>
-            </div>
-            <table class="table table-hover panel-body">
-                {% for csp in devicetype.cs_port_templates.all %}
-                    <tr>
-                        <td>{{ csp.name }}</td>
-                        <td></td>
-                    </tr>
-                {% endfor %}
-            </table>
-        </div>
-        <div class="panel panel-default">
-            <div class="panel-heading">
-                {% if perms.dcim.change_devicetype %}
-                    <a href="{% url 'dcim:devicetype_add_poweroutlet' pk=devicetype.pk %}" class="btn btn-primary btn-xs pull-right"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Power Outlet</a>
-                {% endif %}
-                <strong>Power Outlets</strong>
-            </div>
-            <table class="table table-hover panel-body">
-                {% for po in devicetype.power_outlet_templates.all %}
-                    <tr>
-                        <td>{{ po.name }}</td>
-                        <td></td>
-                    </tr>
-                {% endfor %}
-            </table>
-        </div>
+        {% 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' %}
     </div>
 </div>
 {% endblock %}

+ 29 - 0
netbox/templates/dcim/inc/devicetype_component_table.html

@@ -0,0 +1,29 @@
+{% load render_table from django_tables2 %}
+{% if perms.dcim.change_devicetype %}
+    <form method="post" action="{% url delete_url pk=devicetype.pk %}">
+        {% csrf_token %}
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <a href="{% url add_url pk=devicetype.pk %}" class="btn btn-primary btn-xs pull-right"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add {{ title }}</a>
+                <strong>{{ title }}</strong>
+            </div>
+            {% render_table table 'table.html' %}
+            {% if table.rows %}
+                <div class="panel-footer">
+                    <button type="submit" class="btn btn-xs btn-danger">
+                        <span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete Selected
+                    </button>
+                </div>
+            {% endif %}
+        </div>
+    </form>
+{% else %}
+    <div class="panel panel-default">
+        <div class="panel-heading">
+            <strong>{{ title }}</strong>
+        </div>
+        <table class="table table-hover panel-body">
+            {% render_table table table_template|default:'table.html' %}
+        </table>
+    </div>
+{% endif %}