From c94d111401f2b2facf16d056e12e62b43a6417b9 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 16 Dec 2016 16:29:32 -0500 Subject: [PATCH] Closes #743: Enabled bulk creation of all device components --- netbox/dcim/forms.py | 28 ++++++----- netbox/dcim/urls.py | 25 +++++---- netbox/dcim/views.py | 56 +++++++++++++++------ netbox/project-static/js/forms.js | 8 +++ netbox/templates/dcim/inc/device_table.html | 16 ++++-- 5 files changed, 93 insertions(+), 40 deletions(-) diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index c3c83f777bd..c72ee74d095 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -588,18 +588,6 @@ class Meta: nullable_fields = ['tenant', 'platform'] -class DeviceBulkAddComponentForm(forms.Form, BootstrapMixin): - pk = forms.ModelMultipleChoiceField(queryset=Device.objects.all(), widget=forms.MultipleHiddenInput) - name_pattern = ExpandableNameField(label='Name') - - -class DeviceBulkAddInterfaceForm(forms.ModelForm, DeviceBulkAddComponentForm): - - class Meta: - model = Interface - fields = ['name_pattern', 'form_factor', 'mgmt_only', 'description'] - - class DeviceFilterForm(BootstrapMixin, CustomFieldFilterForm): model = Device site = FilterChoiceField(queryset=Site.objects.annotate(filter_count=Count('racks__devices')), to_field_name='slug') @@ -616,6 +604,22 @@ class DeviceFilterForm(BootstrapMixin, CustomFieldFilterForm): mac_address = forms.CharField(required=False, label='MAC address') +# +# Bulk device component creation +# + +class DeviceBulkAddComponentForm(forms.Form, BootstrapMixin): + pk = forms.ModelMultipleChoiceField(queryset=Device.objects.all(), widget=forms.MultipleHiddenInput) + name_pattern = ExpandableNameField(label='Name') + + +class DeviceBulkAddInterfaceForm(forms.ModelForm, DeviceBulkAddComponentForm): + + class Meta: + model = Interface + fields = ['pk', 'name_pattern', 'form_factor', 'mgmt_only', 'description'] + + # # Console ports # diff --git a/netbox/dcim/urls.py b/netbox/dcim/urls.py index a7c29a8af83..5fa4776cc82 100644 --- a/netbox/dcim/urls.py +++ b/netbox/dcim/urls.py @@ -108,6 +108,7 @@ url(r'^devices/(?P\d+)/services/assign/$', ServiceEditView.as_view(), name='service_assign'), # Console ports + url(r'^devices/console-ports/add/$', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'), url(r'^devices/(?P\d+)/console-ports/add/$', views.consoleport_add, 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.consoleport_connect, name='consoleport_connect'), @@ -116,6 +117,7 @@ url(r'^console-ports/(?P\d+)/delete/$', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'), # Console server ports + url(r'^devices/console-server-ports/add/$', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'), url(r'^devices/(?P\d+)/console-server-ports/add/$', views.consoleserverport_add, name='consoleserverport_add'), 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.consoleserverport_connect, name='consoleserverport_connect'), @@ -124,6 +126,7 @@ url(r'^console-server-ports/(?P\d+)/delete/$', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'), # Power ports + url(r'^devices/power-ports/add/$', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'), url(r'^devices/(?P\d+)/power-ports/add/$', views.powerport_add, 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.powerport_connect, name='powerport_connect'), @@ -132,6 +135,7 @@ url(r'^power-ports/(?P\d+)/delete/$', views.PowerPortDeleteView.as_view(), name='powerport_delete'), # Power outlets + url(r'^devices/power-outlets/add/$', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'), url(r'^devices/(?P\d+)/power-outlets/add/$', views.poweroutlet_add, name='poweroutlet_add'), url(r'^devices/(?P\d+)/power-outlets/delete/$', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'), url(r'^power-outlets/(?P\d+)/connect/$', views.poweroutlet_connect, name='poweroutlet_connect'), @@ -139,7 +143,18 @@ 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'), + # Interfaces + url(r'^devices/interfaces/add/$', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'), + url(r'^devices/(?P\d+)/interfaces/add/$', views.interface_add, name='interface_add'), + url(r'^devices/(?P\d+)/interfaces/edit/$', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'), + url(r'^devices/(?P\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'), + url(r'^devices/(?P\d+)/interface-connections/add/$', views.interfaceconnection_add, name='interfaceconnection_add'), + url(r'^interface-connections/(?P\d+)/delete/$', views.interfaceconnection_delete, name='interfaceconnection_delete'), + url(r'^interfaces/(?P\d+)/edit/$', views.InterfaceEditView.as_view(), name='interface_edit'), + url(r'^interfaces/(?P\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'), + # Device bays + url(r'^devices/device-bays/add/$', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'), url(r'^devices/(?P\d+)/bays/add/$', views.devicebay_add, name='devicebay_add'), url(r'^devices/(?P\d+)/bays/delete/$', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'), url(r'^device-bays/(?P\d+)/edit/$', views.DeviceBayEditView.as_view(), name='devicebay_edit'), @@ -155,16 +170,6 @@ url(r'^interface-connections/$', views.InterfaceConnectionsListView.as_view(), name='interface_connections_list'), url(r'^interface-connections/import/$', views.InterfaceConnectionsBulkImportView.as_view(), name='interface_connections_import'), - # Interfaces - url(r'^devices/interfaces/add/$', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'), - url(r'^devices/(?P\d+)/interfaces/add/$', views.interface_add, name='interface_add'), - url(r'^devices/(?P\d+)/interfaces/edit/$', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'), - url(r'^devices/(?P\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'), - url(r'^devices/(?P\d+)/interface-connections/add/$', views.interfaceconnection_add, name='interfaceconnection_add'), - url(r'^interface-connections/(?P\d+)/delete/$', views.interfaceconnection_delete, name='interfaceconnection_delete'), - url(r'^interfaces/(?P\d+)/edit/$', views.InterfaceEditView.as_view(), name='interface_edit'), - url(r'^interfaces/(?P\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'), - # Modules url(r'^devices/(?P\d+)/modules/add/$', views.ModuleEditView.as_view(), name='module_add'), url(r'^modules/(?P\d+)/edit/$', views.ModuleEditView.as_view(), name='module_edit'), diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index e7337ad3b53..0eee658ce87 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -688,13 +688,17 @@ def device_lldp_neighbors(request, pk): }) +# +# Bulk device component creation +# + class DeviceBulkAddComponentView(View): """ Add one or more components (e.g. interfaces) to a selected set of Devices. """ - form = None - component_cls = None - component_form = None + form = forms.DeviceBulkAddComponentForm + model = None + model_form = None def get(self): return redirect('dcim:device_list') @@ -722,18 +726,18 @@ def post(self, request): 'name': name, } component_data.update(data) - component_form = self.component_form(component_data) + component_form = self.model_form(component_data) if component_form.is_valid(): new_components.append(component_form.save(commit=False)) else: - form.add_error('name_pattern', "Duplicate {} name for {}: {}".format( - self.component_cls._meta.verbose_name, device, name - )) + for field, errors in component_form.errors.as_data().items(): + for e in errors: + form.add_error(field, u'{} {}: {}'.format(device, name, ', '.join(e))) if not form.errors: - self.component_cls.objects.bulk_create(new_components) + self.model.objects.bulk_create(new_components) messages.success(request, u"Added {} {} to {} devices.".format( - len(new_components), self.component_cls._meta.verbose_name_plural, len(form.cleaned_data['pk']) + len(new_components), self.model._meta.verbose_name_plural, len(form.cleaned_data['pk']) )) return redirect('dcim:device_list') @@ -747,19 +751,41 @@ def post(self, request): return render(request, 'dcim/device_bulk_add_component.html', { 'form': form, - 'component_name': self.component_cls._meta.verbose_name_plural, + 'component_name': self.model._meta.verbose_name_plural, 'selected_devices': selected_devices, 'cancel_url': reverse('dcim:device_list'), }) +class DeviceBulkAddConsolePortView(DeviceBulkAddComponentView): + model = ConsolePort + model_form = forms.ConsolePortForm + + +class DeviceBulkAddConsoleServerPortView(DeviceBulkAddComponentView): + model = ConsoleServerPort + model_form = forms.ConsoleServerPortForm + + +class DeviceBulkAddPowerPortView(DeviceBulkAddComponentView): + model = PowerPort + model_form = forms.PowerPortForm + + +class DeviceBulkAddPowerOutletView(DeviceBulkAddComponentView): + model = PowerOutlet + model_form = forms.PowerOutletForm + + class DeviceBulkAddInterfaceView(DeviceBulkAddComponentView): - """ - Add one or more components (e.g. interfaces) to a selected set of Devices. - """ form = forms.DeviceBulkAddInterfaceForm - component_cls = Interface - component_form = forms.InterfaceForm + model = Interface + model_form = forms.InterfaceForm + + +class DeviceBulkAddDeviceBayView(DeviceBulkAddComponentView): + model = DeviceBay + model_form = forms.DeviceBayForm # diff --git a/netbox/project-static/js/forms.js b/netbox/project-static/js/forms.js index 439cf8701ce..3adc9c13f9b 100644 --- a/netbox/project-static/js/forms.js +++ b/netbox/project-static/js/forms.js @@ -51,6 +51,14 @@ $(document).ready(function() { $('#id_' + this.value).toggle('disabled'); }); + // Set formaction and submit using a link + $('a.formaction').click(function (event) { + event.preventDefault(); + var form = $(this).closest('form'); + form.attr('action', $(this).attr('href')); + form.submit(); + }); + // API select widget $('select[filter-for]').change(function () { diff --git a/netbox/templates/dcim/inc/device_table.html b/netbox/templates/dcim/inc/device_table.html index 08344706e1a..b416d35b451 100644 --- a/netbox/templates/dcim/inc/device_table.html +++ b/netbox/templates/dcim/inc/device_table.html @@ -2,8 +2,18 @@ {% block extra_actions %} {% if perms.dcim.add_interface %} - + {% endif %} {% endblock %}