From 009ef41e928e0c61559abdfcbff74880922c51c8 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 4 Mar 2016 16:41:24 -0500 Subject: [PATCH] Added views to create device component templates --- netbox/dcim/forms.py | 47 ++++++++++- netbox/dcim/urls.py | 12 +++ netbox/dcim/views.py | 82 ++++++++++++++++++- .../dcim/component_template_add.html | 42 ++++++++++ netbox/templates/dcim/devicetype.html | 15 ++++ 5 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 netbox/templates/dcim/component_template_add.html diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 2acdeb4f6..50af2b016 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -8,7 +8,8 @@ from utilities.forms import BootstrapMixin, SmallTextarea, SelectWithDisabled, C Livesearch, CSVDataField, CommentField, BulkImportForm, FlexibleModelChoiceField, ExpandableNameField from .models import Site, Rack, RackGroup, Device, Manufacturer, DeviceType, DeviceRole, Platform, ConsolePort, \ - ConsoleServerPort, PowerPort, PowerOutlet, Interface, InterfaceConnection, CONNECTION_STATUS_CHOICES, \ + ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, PowerPort, PowerPortTemplate, PowerOutlet, \ + PowerOutletTemplate, Interface, InterfaceTemplate, InterfaceConnection, CONNECTION_STATUS_CHOICES, \ CONNECTION_STATUS_PLANNED, CONNECTION_STATUS_CONNECTED, IFACE_FF_VIRTUAL, STATUS_CHOICES @@ -191,6 +192,50 @@ class DeviceTypeFilterForm(forms.Form, BootstrapMixin): widget=forms.SelectMultiple(attrs={'size': 8})) +# +# Device component templates +# + +class ConsolePortTemplateForm(forms.ModelForm, BootstrapMixin): + name_pattern = ExpandableNameField(label='Name') + + class Meta: + model = ConsolePortTemplate + fields = ['name_pattern'] + + +class ConsoleServerPortTemplateForm(forms.ModelForm, BootstrapMixin): + name_pattern = ExpandableNameField(label='Name') + + class Meta: + model = ConsoleServerPortTemplate + fields = ['name_pattern'] + + +class PowerPortTemplateForm(forms.ModelForm, BootstrapMixin): + name_pattern = ExpandableNameField(label='Name') + + class Meta: + model = PowerPortTemplate + fields = ['name_pattern'] + + +class PowerOutletTemplateForm(forms.ModelForm, BootstrapMixin): + name_pattern = ExpandableNameField(label='Name') + + class Meta: + model = PowerOutletTemplate + fields = ['name_pattern'] + + +class InterfaceTemplateForm(forms.ModelForm, BootstrapMixin): + name_pattern = ExpandableNameField(label='Name') + + class Meta: + model = InterfaceTemplate + fields = ['name_pattern', 'form_factor', 'mgmt_only'] + + # # Devices # diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index eded376bc..6f65b7e8d 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -3,6 +3,8 @@ from django.conf.urls import url from secrets.views import secret_add from . import views +from .forms import ConsolePortTemplateForm + urlpatterns = [ @@ -32,6 +34,16 @@ 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'), + 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(), + name='devicetype_add_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-outlets/add/$', views.PowerOutletTemplateAddView.as_view(), + name='devicetype_add_poweroutlet'), + url(r'^device-types/(?P\d+)/interfaces/add/$', views.InterfaceTemplateAddView.as_view(), + name='devicetype_add_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 095b5d06a..71eafb298 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -3,11 +3,13 @@ import re from django.contrib import messages from django.contrib.auth.decorators import permission_required 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.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect, render from django.utils.http import urlencode +from django.views.generic import View from ipam.models import Prefix, IPAddress, VLAN from circuits.models import Circuit @@ -25,8 +27,10 @@ from .forms import SiteForm, SiteImportForm, RackForm, RackImportForm, RackBulkE PowerPortCreateForm, PowerPortConnectionForm, PowerConnectionImportForm, PowerOutletForm, PowerOutletCreateForm, \ PowerOutletConnectionForm, InterfaceForm, InterfaceCreateForm, InterfaceBulkCreateForm, InterfaceConnectionForm, \ InterfaceConnectionDeletionForm, InterfaceConnectionImportForm, ConsoleConnectionFilterForm, \ - PowerConnectionFilterForm, InterfaceConnectionFilterForm, IPAddressForm -from .models import Site, Rack, DeviceType, Device, ConsolePort, ConsoleServerPort, PowerPort, PowerOutlet, Interface, \ + PowerConnectionFilterForm, InterfaceConnectionFilterForm, IPAddressForm, ConsolePortTemplateForm, \ + ConsoleServerPortTemplateForm, PowerPortTemplateForm, PowerOutletTemplateForm, InterfaceTemplateForm +from .models import Site, Rack, DeviceType, ConsolePortTemplate, ConsoleServerPortTemplate, PowerPortTemplate, \ + 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 @@ -428,6 +432,80 @@ class DeviceTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): redirect_url = 'dcim:devicetype_list' +class ComponentTemplateCreateView(View): + model = None + form = None + + def get(self, request, pk, *args, **kwargs): + + devicetype = get_object_or_404(DeviceType, pk=pk) + + return render(request, 'dcim/component_template_add.html', { + 'devicetype': devicetype, + 'component_type': self.model._meta.verbose_name, + 'form': self.form(), + 'cancel_url': reverse('dcim:devicetype', kwargs={'pk': devicetype.pk}), + }) + + def post(self, request, pk, *args, **kwargs): + + devicetype = get_object_or_404(DeviceType, pk=pk) + + form = self.form(request.POST) + if form.is_valid(): + + component_templates = [] + for name in form.cleaned_data['name_pattern']: + component_template = self.form(request.POST).save(commit=False) + component_template.device_type = devicetype + component_template.name = name + try: + component_template.full_clean() + component_templates.append(component_template) + except ValidationError: + form.add_error('name_pattern', "Duplicate name found: {}".format(name)) + + if not form.errors: + self.model.objects.bulk_create(component_templates) + messages.success(request, "Added {} compontent(s) to {}".format(len(component_templates), devicetype)) + if '_addanother' in request.POST: + return redirect(request.path) + else: + return redirect('dcim:devicetype', pk=devicetype.pk) + + return render(request, 'dcim/component_template_add.html', { + 'devicetype': devicetype, + 'component_type': self.model._meta.verbose_name, + 'form': form, + 'cancel_url': reverse('dcim:devicetype', kwargs={'pk': devicetype.pk}), + }) + + +class ConsolePortTemplateAddView(ComponentTemplateCreateView): + model = ConsolePortTemplate + form = ConsolePortTemplateForm + + +class ConsoleServerPortTemplateAddView(ComponentTemplateCreateView): + model = ConsoleServerPortTemplate + form = ConsoleServerPortTemplateForm + + +class PowerPortTemplateAddView(ComponentTemplateCreateView): + model = PowerPortTemplate + form = PowerPortTemplateForm + + +class PowerOutletTemplateAddView(ComponentTemplateCreateView): + model = PowerOutletTemplate + form = PowerOutletTemplateForm + + +class InterfaceTemplateAddView(ComponentTemplateCreateView): + model = InterfaceTemplate + form = InterfaceTemplateForm + + # # Devices # diff --git a/netbox/templates/dcim/component_template_add.html b/netbox/templates/dcim/component_template_add.html new file mode 100644 index 000000000..d305debf5 --- /dev/null +++ b/netbox/templates/dcim/component_template_add.html @@ -0,0 +1,42 @@ +{% extends '_base.html' %} +{% load form_helpers %} + +{% block title %}Add {{ component_type }} to {{ devicetype }}{% endblock %} + +{% block content %} +
+ {% csrf_token %} +
+
+ {% if form.non_field_errors %} +
+
Errors
+
+ {{ form.non_field_errors }} +
+
+ {% endif %} +
+
+ New {{ component_type }} +
+
+
+ +
+

{{ devicetype }}

+
+
+ {% render_form form %} +
+
+
+
+ + Cancel +
+
+
+
+
+{% endblock %} diff --git a/netbox/templates/dcim/devicetype.html b/netbox/templates/dcim/devicetype.html index c3538fd82..0427eab00 100644 --- a/netbox/templates/dcim/devicetype.html +++ b/netbox/templates/dcim/devicetype.html @@ -75,6 +75,9 @@
+ {% if perms.dcim.change_devicetype %} + Add Console Ports + {% endif %} Console Ports
@@ -88,6 +91,9 @@
+ {% if perms.dcim.change_devicetype %} + Add Power Ports + {% endif %} Power Ports
@@ -103,6 +109,9 @@
+ {% if perms.dcim.change_devicetype %} + Add Interfaces + {% endif %} Interfaces
@@ -118,6 +127,9 @@
+ {% if perms.dcim.change_devicetype %} + Add Console Server Ports + {% endif %} Console Server Ports
@@ -131,6 +143,9 @@
+ {% if perms.dcim.change_devicetype %} + Add Power Outlet + {% endif %} Power Outlets