Implemented DeviceType component template creation and deletion

This commit is contained in:
Jeremy Stretch 2016-03-04 23:09:32 -05:00
parent 009ef41e92
commit 61cbee15ca
6 changed files with 237 additions and 86 deletions

View File

@ -1,7 +1,8 @@
import django_tables2 as tables import django_tables2 as tables
from django_tables2.utils import Accessor 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 = """ PREFIXES_PER_VLAN = """
@ -98,6 +99,112 @@ class DeviceTypeBulkEditTable(DeviceTypeTable):
fields = ('pk', 'model', 'manufacturer', 'u_height') 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 # Devices
# #

View File

@ -3,7 +3,8 @@ from django.conf.urls import url
from secrets.views import secret_add from secrets.views import secret_add
from . import views from . import views
from .forms import ConsolePortTemplateForm from .models import ConsolePortTemplate, ConsoleServerPortTemplate, PowerPortTemplate, PowerOutletTemplate, \
InterfaceTemplate
urlpatterns = [ urlpatterns = [
@ -34,16 +35,28 @@ urlpatterns = [
url(r'^device-types/(?P<pk>\d+)/$', views.devicetype, name='devicetype'), 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+)/edit/$', views.devicetype_edit, name='devicetype_edit'),
url(r'^device-types/(?P<pk>\d+)/delete/$', views.devicetype_delete, name='devicetype_delete'), 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(), url(r'^device-types/(?P<pk>\d+)/console-ports/add/$', views.ConsolePortTemplateAddView.as_view(),
name='devicetype_add_consoleport'), 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'), 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(), url(r'^device-types/(?P<pk>\d+)/power-ports/add/$', views.PowerPortTemplateAddView.as_view(),
name='devicetype_add_powerport'), 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(), url(r'^device-types/(?P<pk>\d+)/power-outlets/add/$', views.PowerOutletTemplateAddView.as_view(),
name='devicetype_add_poweroutlet'), 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(), url(r'^device-types/(?P<pk>\d+)/interfaces/add/$', views.InterfaceTemplateAddView.as_view(),
name='devicetype_add_interface'), name='devicetype_add_interface'),
url(r'^device-types/(?P<pk>\d+)/interfaces/delete/$', views.component_template_delete,
{'model': InterfaceTemplate}, name='devicetype_delete_interface'),
# Devices # Devices
url(r'^devices/$', views.DeviceListView.as_view(), name='device_list'), url(r'^devices/$', views.DeviceListView.as_view(), name='device_list'),

View File

@ -6,6 +6,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db.models import Count, ProtectedError from django.db.models import Count, ProtectedError
from django.forms import ModelMultipleChoiceField, MultipleHiddenInput
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.utils.http import urlencode 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, \ PowerOutletTemplate, InterfaceTemplate, Device, ConsolePort, ConsoleServerPort, PowerPort, PowerOutlet, Interface, \
InterfaceConnection, Module, CONNECTION_STATUS_CONNECTED InterfaceConnection, Module, CONNECTION_STATUS_CONNECTED
from .tables import SiteTable, RackTable, RackBulkEditTable, DeviceTypeTable, DeviceTypeBulkEditTable, DeviceTable, \ 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+)\]' EXPANSION_PATTERN = '\[(\d+-\d+)\]'
@ -331,8 +335,27 @@ def devicetype(request, pk):
devicetype = get_object_or_404(DeviceType, pk=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', { return render(request, 'dcim/devicetype.html', {
'devicetype': devicetype, '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' redirect_url = 'dcim:devicetype_list'
#
# Device type components
#
class ComponentTemplateCreateView(View): class ComponentTemplateCreateView(View):
model = None model = None
form = None form = None
@ -506,6 +533,45 @@ class InterfaceTemplateAddView(ComponentTemplateCreateView):
form = InterfaceTemplateForm 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 # Devices
# #

View File

@ -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 %}

View File

@ -73,90 +73,13 @@
</tr> </tr>
</table> </table>
</div> </div>
<div class="panel panel-default"> {% 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' %}
<div class="panel-heading"> {% 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 %}
<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>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="panel panel-default"> {% 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' %}
<div class="panel-heading"> {% 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' %}
{% if perms.dcim.change_devicetype %} {% 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' %}
<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>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -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 %}