mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #743: Enabled bulk creation of all device components
This commit is contained in:
@ -588,18 +588,6 @@ class DeviceBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
|
|||||||
nullable_fields = ['tenant', 'platform']
|
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):
|
class DeviceFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
||||||
model = Device
|
model = Device
|
||||||
site = FilterChoiceField(queryset=Site.objects.annotate(filter_count=Count('racks__devices')), to_field_name='slug')
|
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')
|
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
|
# Console ports
|
||||||
#
|
#
|
||||||
|
@ -108,6 +108,7 @@ urlpatterns = [
|
|||||||
url(r'^devices/(?P<device>\d+)/services/assign/$', ServiceEditView.as_view(), name='service_assign'),
|
url(r'^devices/(?P<device>\d+)/services/assign/$', ServiceEditView.as_view(), name='service_assign'),
|
||||||
|
|
||||||
# Console ports
|
# Console ports
|
||||||
|
url(r'^devices/console-ports/add/$', views.DeviceBulkAddConsolePortView.as_view(), name='device_bulk_add_consoleport'),
|
||||||
url(r'^devices/(?P<pk>\d+)/console-ports/add/$', views.consoleport_add, name='consoleport_add'),
|
url(r'^devices/(?P<pk>\d+)/console-ports/add/$', views.consoleport_add, name='consoleport_add'),
|
||||||
url(r'^devices/(?P<pk>\d+)/console-ports/delete/$', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'),
|
url(r'^devices/(?P<pk>\d+)/console-ports/delete/$', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'),
|
||||||
url(r'^console-ports/(?P<pk>\d+)/connect/$', views.consoleport_connect, name='consoleport_connect'),
|
url(r'^console-ports/(?P<pk>\d+)/connect/$', views.consoleport_connect, name='consoleport_connect'),
|
||||||
@ -116,6 +117,7 @@ urlpatterns = [
|
|||||||
url(r'^console-ports/(?P<pk>\d+)/delete/$', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'),
|
url(r'^console-ports/(?P<pk>\d+)/delete/$', views.ConsolePortDeleteView.as_view(), name='consoleport_delete'),
|
||||||
|
|
||||||
# Console server ports
|
# Console server ports
|
||||||
|
url(r'^devices/console-server-ports/add/$', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'),
|
||||||
url(r'^devices/(?P<pk>\d+)/console-server-ports/add/$', views.consoleserverport_add, name='consoleserverport_add'),
|
url(r'^devices/(?P<pk>\d+)/console-server-ports/add/$', views.consoleserverport_add, name='consoleserverport_add'),
|
||||||
url(r'^devices/(?P<pk>\d+)/console-server-ports/delete/$', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'),
|
url(r'^devices/(?P<pk>\d+)/console-server-ports/delete/$', views.ConsoleServerPortBulkDeleteView.as_view(), name='consoleserverport_bulk_delete'),
|
||||||
url(r'^console-server-ports/(?P<pk>\d+)/connect/$', views.consoleserverport_connect, name='consoleserverport_connect'),
|
url(r'^console-server-ports/(?P<pk>\d+)/connect/$', views.consoleserverport_connect, name='consoleserverport_connect'),
|
||||||
@ -124,6 +126,7 @@ urlpatterns = [
|
|||||||
url(r'^console-server-ports/(?P<pk>\d+)/delete/$', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'),
|
url(r'^console-server-ports/(?P<pk>\d+)/delete/$', views.ConsoleServerPortDeleteView.as_view(), name='consoleserverport_delete'),
|
||||||
|
|
||||||
# Power ports
|
# Power ports
|
||||||
|
url(r'^devices/power-ports/add/$', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'),
|
||||||
url(r'^devices/(?P<pk>\d+)/power-ports/add/$', views.powerport_add, name='powerport_add'),
|
url(r'^devices/(?P<pk>\d+)/power-ports/add/$', views.powerport_add, name='powerport_add'),
|
||||||
url(r'^devices/(?P<pk>\d+)/power-ports/delete/$', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'),
|
url(r'^devices/(?P<pk>\d+)/power-ports/delete/$', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'),
|
||||||
url(r'^power-ports/(?P<pk>\d+)/connect/$', views.powerport_connect, name='powerport_connect'),
|
url(r'^power-ports/(?P<pk>\d+)/connect/$', views.powerport_connect, name='powerport_connect'),
|
||||||
@ -132,6 +135,7 @@ urlpatterns = [
|
|||||||
url(r'^power-ports/(?P<pk>\d+)/delete/$', views.PowerPortDeleteView.as_view(), name='powerport_delete'),
|
url(r'^power-ports/(?P<pk>\d+)/delete/$', views.PowerPortDeleteView.as_view(), name='powerport_delete'),
|
||||||
|
|
||||||
# Power outlets
|
# Power outlets
|
||||||
|
url(r'^devices/power-outlets/add/$', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'),
|
||||||
url(r'^devices/(?P<pk>\d+)/power-outlets/add/$', views.poweroutlet_add, name='poweroutlet_add'),
|
url(r'^devices/(?P<pk>\d+)/power-outlets/add/$', views.poweroutlet_add, name='poweroutlet_add'),
|
||||||
url(r'^devices/(?P<pk>\d+)/power-outlets/delete/$', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'),
|
url(r'^devices/(?P<pk>\d+)/power-outlets/delete/$', views.PowerOutletBulkDeleteView.as_view(), name='poweroutlet_bulk_delete'),
|
||||||
url(r'^power-outlets/(?P<pk>\d+)/connect/$', views.poweroutlet_connect, name='poweroutlet_connect'),
|
url(r'^power-outlets/(?P<pk>\d+)/connect/$', views.poweroutlet_connect, name='poweroutlet_connect'),
|
||||||
@ -139,7 +143,18 @@ urlpatterns = [
|
|||||||
url(r'^power-outlets/(?P<pk>\d+)/edit/$', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'),
|
url(r'^power-outlets/(?P<pk>\d+)/edit/$', views.PowerOutletEditView.as_view(), name='poweroutlet_edit'),
|
||||||
url(r'^power-outlets/(?P<pk>\d+)/delete/$', views.PowerOutletDeleteView.as_view(), name='poweroutlet_delete'),
|
url(r'^power-outlets/(?P<pk>\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<pk>\d+)/interfaces/add/$', views.interface_add, name='interface_add'),
|
||||||
|
url(r'^devices/(?P<pk>\d+)/interfaces/edit/$', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'),
|
||||||
|
url(r'^devices/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
|
||||||
|
url(r'^devices/(?P<pk>\d+)/interface-connections/add/$', views.interfaceconnection_add, name='interfaceconnection_add'),
|
||||||
|
url(r'^interface-connections/(?P<pk>\d+)/delete/$', views.interfaceconnection_delete, name='interfaceconnection_delete'),
|
||||||
|
url(r'^interfaces/(?P<pk>\d+)/edit/$', views.InterfaceEditView.as_view(), name='interface_edit'),
|
||||||
|
url(r'^interfaces/(?P<pk>\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'),
|
||||||
|
|
||||||
# Device bays
|
# Device bays
|
||||||
|
url(r'^devices/device-bays/add/$', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'),
|
||||||
url(r'^devices/(?P<pk>\d+)/bays/add/$', views.devicebay_add, name='devicebay_add'),
|
url(r'^devices/(?P<pk>\d+)/bays/add/$', views.devicebay_add, name='devicebay_add'),
|
||||||
url(r'^devices/(?P<pk>\d+)/bays/delete/$', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'),
|
url(r'^devices/(?P<pk>\d+)/bays/delete/$', views.DeviceBayBulkDeleteView.as_view(), name='devicebay_bulk_delete'),
|
||||||
url(r'^device-bays/(?P<pk>\d+)/edit/$', views.DeviceBayEditView.as_view(), name='devicebay_edit'),
|
url(r'^device-bays/(?P<pk>\d+)/edit/$', views.DeviceBayEditView.as_view(), name='devicebay_edit'),
|
||||||
@ -155,16 +170,6 @@ urlpatterns = [
|
|||||||
url(r'^interface-connections/$', views.InterfaceConnectionsListView.as_view(), name='interface_connections_list'),
|
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'),
|
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<pk>\d+)/interfaces/add/$', views.interface_add, name='interface_add'),
|
|
||||||
url(r'^devices/(?P<pk>\d+)/interfaces/edit/$', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'),
|
|
||||||
url(r'^devices/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
|
|
||||||
url(r'^devices/(?P<pk>\d+)/interface-connections/add/$', views.interfaceconnection_add, name='interfaceconnection_add'),
|
|
||||||
url(r'^interface-connections/(?P<pk>\d+)/delete/$', views.interfaceconnection_delete, name='interfaceconnection_delete'),
|
|
||||||
url(r'^interfaces/(?P<pk>\d+)/edit/$', views.InterfaceEditView.as_view(), name='interface_edit'),
|
|
||||||
url(r'^interfaces/(?P<pk>\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'),
|
|
||||||
|
|
||||||
# Modules
|
# Modules
|
||||||
url(r'^devices/(?P<device>\d+)/modules/add/$', views.ModuleEditView.as_view(), name='module_add'),
|
url(r'^devices/(?P<device>\d+)/modules/add/$', views.ModuleEditView.as_view(), name='module_add'),
|
||||||
url(r'^modules/(?P<pk>\d+)/edit/$', views.ModuleEditView.as_view(), name='module_edit'),
|
url(r'^modules/(?P<pk>\d+)/edit/$', views.ModuleEditView.as_view(), name='module_edit'),
|
||||||
|
@ -688,13 +688,17 @@ def device_lldp_neighbors(request, pk):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Bulk device component creation
|
||||||
|
#
|
||||||
|
|
||||||
class DeviceBulkAddComponentView(View):
|
class DeviceBulkAddComponentView(View):
|
||||||
"""
|
"""
|
||||||
Add one or more components (e.g. interfaces) to a selected set of Devices.
|
Add one or more components (e.g. interfaces) to a selected set of Devices.
|
||||||
"""
|
"""
|
||||||
form = None
|
form = forms.DeviceBulkAddComponentForm
|
||||||
component_cls = None
|
model = None
|
||||||
component_form = None
|
model_form = None
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
return redirect('dcim:device_list')
|
return redirect('dcim:device_list')
|
||||||
@ -722,18 +726,18 @@ class DeviceBulkAddComponentView(View):
|
|||||||
'name': name,
|
'name': name,
|
||||||
}
|
}
|
||||||
component_data.update(data)
|
component_data.update(data)
|
||||||
component_form = self.component_form(component_data)
|
component_form = self.model_form(component_data)
|
||||||
if component_form.is_valid():
|
if component_form.is_valid():
|
||||||
new_components.append(component_form.save(commit=False))
|
new_components.append(component_form.save(commit=False))
|
||||||
else:
|
else:
|
||||||
form.add_error('name_pattern', "Duplicate {} name for {}: {}".format(
|
for field, errors in component_form.errors.as_data().items():
|
||||||
self.component_cls._meta.verbose_name, device, name
|
for e in errors:
|
||||||
))
|
form.add_error(field, u'{} {}: {}'.format(device, name, ', '.join(e)))
|
||||||
|
|
||||||
if not form.errors:
|
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(
|
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')
|
return redirect('dcim:device_list')
|
||||||
|
|
||||||
@ -747,19 +751,41 @@ class DeviceBulkAddComponentView(View):
|
|||||||
|
|
||||||
return render(request, 'dcim/device_bulk_add_component.html', {
|
return render(request, 'dcim/device_bulk_add_component.html', {
|
||||||
'form': form,
|
'form': form,
|
||||||
'component_name': self.component_cls._meta.verbose_name_plural,
|
'component_name': self.model._meta.verbose_name_plural,
|
||||||
'selected_devices': selected_devices,
|
'selected_devices': selected_devices,
|
||||||
'cancel_url': reverse('dcim:device_list'),
|
'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):
|
class DeviceBulkAddInterfaceView(DeviceBulkAddComponentView):
|
||||||
"""
|
|
||||||
Add one or more components (e.g. interfaces) to a selected set of Devices.
|
|
||||||
"""
|
|
||||||
form = forms.DeviceBulkAddInterfaceForm
|
form = forms.DeviceBulkAddInterfaceForm
|
||||||
component_cls = Interface
|
model = Interface
|
||||||
component_form = forms.InterfaceForm
|
model_form = forms.InterfaceForm
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceBulkAddDeviceBayView(DeviceBulkAddComponentView):
|
||||||
|
model = DeviceBay
|
||||||
|
model_form = forms.DeviceBayForm
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -51,6 +51,14 @@ $(document).ready(function() {
|
|||||||
$('#id_' + this.value).toggle('disabled');
|
$('#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
|
// API select widget
|
||||||
$('select[filter-for]').change(function () {
|
$('select[filter-for]').change(function () {
|
||||||
|
|
||||||
|
@ -2,8 +2,18 @@
|
|||||||
|
|
||||||
{% block extra_actions %}
|
{% block extra_actions %}
|
||||||
{% if perms.dcim.add_interface %}
|
{% if perms.dcim.add_interface %}
|
||||||
<button type="submit" name="_edit" formaction="{% url 'dcim:device_bulk_add_interface' %}" class="btn btn-primary btn-sm">
|
<div class="btn-group">
|
||||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Interfaces
|
<button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add Components <span class="caret"></span>
|
||||||
</button>
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="{% url 'dcim:device_bulk_add_consoleport' %}" class="formaction">Console Ports</a></li>
|
||||||
|
<li><a href="{% url 'dcim:device_bulk_add_consoleserverport' %}" class="formaction">Console Server Ports</a></li>
|
||||||
|
<li><a href="{% url 'dcim:device_bulk_add_powerport' %}" class="formaction">Power Ports</a></li>
|
||||||
|
<li><a href="{% url 'dcim:device_bulk_add_poweroutlet' %}" class="formaction">Power Outlets</a></li>
|
||||||
|
<li><a href="{% url 'dcim:device_bulk_add_interface' %}" class="formaction">Interfaces</a></li>
|
||||||
|
<li><a href="{% url 'dcim:device_bulk_add_devicebay' %}" class="formaction">Device Bays</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
Reference in New Issue
Block a user