Преглед изворни кода

Closes #4795: Add bulk disconnect capability for console and power ports

Jeremy Stretch пре 5 година
родитељ
комит
319799b5ce

+ 1 - 0
docs/release-notes/version-2.9.md

@@ -16,6 +16,7 @@ NetBox v2.9 replaces Django's built-in permissions framework with one that suppo
 * [#4742](https://github.com/netbox-community/netbox/issues/4742) - Add tagging for cables, power panels, and rack reservations
 * [#4788](https://github.com/netbox-community/netbox/issues/4788) - Add dedicated views for all device components
 * [#4792](https://github.com/netbox-community/netbox/issues/4792) - Add bulk rename capability for console and power ports
+* [#4795](https://github.com/netbox-community/netbox/issues/4795) - Add bulk disconnect capability for console and power ports
 
 ### Configuration Changes
 

+ 0 - 35
netbox/dcim/forms.py

@@ -2375,13 +2375,6 @@ class ConsoleServerPortBulkEditForm(
         ]
 
 
-class ConsoleServerPortBulkDisconnectForm(ConfirmationForm):
-    pk = forms.ModelMultipleChoiceField(
-        queryset=ConsoleServerPort.objects.all(),
-        widget=forms.MultipleHiddenInput()
-    )
-
-
 class ConsoleServerPortCSVForm(CSVModelForm):
     device = CSVModelChoiceField(
         queryset=Device.objects.all(),
@@ -2603,13 +2596,6 @@ class PowerOutletBulkEditForm(
             self.fields['power_port'].widget.attrs['disabled'] = True
 
 
-class PowerOutletBulkDisconnectForm(ConfirmationForm):
-    pk = forms.ModelMultipleChoiceField(
-        queryset=PowerOutlet.objects.all(),
-        widget=forms.MultipleHiddenInput
-    )
-
-
 class PowerOutletCSVForm(CSVModelForm):
     device = CSVModelChoiceField(
         queryset=Device.objects.all(),
@@ -2908,13 +2894,6 @@ class InterfaceBulkEditForm(
             self.cleaned_data['tagged_vlans'] = []
 
 
-class InterfaceBulkDisconnectForm(ConfirmationForm):
-    pk = forms.ModelMultipleChoiceField(
-        queryset=Interface.objects.all(),
-        widget=forms.MultipleHiddenInput()
-    )
-
-
 class InterfaceCSVForm(CSVModelForm):
     device = CSVModelChoiceField(
         queryset=Device.objects.all(),
@@ -3094,13 +3073,6 @@ class FrontPortBulkEditForm(
         ]
 
 
-class FrontPortBulkDisconnectForm(ConfirmationForm):
-    pk = forms.ModelMultipleChoiceField(
-        queryset=FrontPort.objects.all(),
-        widget=forms.MultipleHiddenInput
-    )
-
-
 class FrontPortCSVForm(CSVModelForm):
     device = CSVModelChoiceField(
         queryset=Device.objects.all(),
@@ -3217,13 +3189,6 @@ class RearPortBulkEditForm(
         ]
 
 
-class RearPortBulkDisconnectForm(ConfirmationForm):
-    pk = forms.ModelMultipleChoiceField(
-        queryset=RearPort.objects.all(),
-        widget=forms.MultipleHiddenInput
-    )
-
-
 class RearPortCSVForm(CSVModelForm):
     device = CSVModelChoiceField(
         queryset=Device.objects.all(),

+ 2 - 2
netbox/dcim/urls.py

@@ -188,7 +188,7 @@ urlpatterns = [
     path('console-ports/import/', views.ConsolePortBulkImportView.as_view(), name='consoleport_import'),
     path('console-ports/edit/', views.ConsolePortBulkEditView.as_view(), name='consoleport_bulk_edit'),
     path('console-ports/rename/', views.ConsolePortBulkRenameView.as_view(), name='consoleport_bulk_rename'),
-    # TODO: Bulk disconnect view for ConsolePorts
+    path('console-ports/disconnect/', views.ConsolePortBulkDisconnectView.as_view(), name='consoleport_bulk_disconnect'),
     path('console-ports/delete/', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'),
     path('console-ports/<int:pk>/', views.ConsolePortView.as_view(), name='consoleport'),
     path('console-ports/<int:pk>/edit/', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
@@ -220,7 +220,7 @@ urlpatterns = [
     path('power-ports/import/', views.PowerPortBulkImportView.as_view(), name='powerport_import'),
     path('power-ports/edit/', views.PowerPortBulkEditView.as_view(), name='powerport_bulk_edit'),
     path('power-ports/rename/', views.PowerPortBulkRenameView.as_view(), name='powerport_bulk_rename'),
-    # TODO: Bulk disconnect view for PowerPorts
+    path('power-ports/disconnect/', views.PowerPortBulkDisconnectView.as_view(), name='powerport_bulk_disconnect'),
     path('power-ports/delete/', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'),
     path('power-ports/<int:pk>/', views.PowerPortView.as_view(), name='powerport'),
     path('power-ports/<int:pk>/edit/', views.PowerPortEditView.as_view(), name='powerport_edit'),

+ 21 - 7
netbox/dcim/views.py

@@ -6,7 +6,7 @@ from django.contrib.contenttypes.models import ContentType
 from django.core.paginator import EmptyPage, PageNotAnInteger
 from django.db import transaction
 from django.db.models import Count, F
-from django.forms import modelformset_factory
+from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory
 from django.shortcuts import get_object_or_404, redirect, render
 from django.urls import reverse
 from django.utils.html import escape
@@ -46,9 +46,20 @@ class BulkDisconnectView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View)
     An extendable view for disconnection console/power/interface components in bulk.
     """
     queryset = None
-    form = None
     template_name = 'dcim/bulk_disconnect.html'
 
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        # Create a new Form class from ConfirmationForm
+        class _Form(ConfirmationForm):
+            pk = ModelMultipleChoiceField(
+                queryset=self.queryset,
+                widget=MultipleHiddenInput()
+            )
+
+        self.form = _Form
+
     def get_required_permission(self):
         return get_permission_for_model(self.queryset.model, 'change')
 
@@ -1203,6 +1214,10 @@ class ConsolePortBulkRenameView(BulkRenameView):
     queryset = ConsolePort.objects.all()
 
 
+class ConsolePortBulkDisconnectView(BulkDisconnectView):
+    queryset = ConsolePort.objects.all()
+
+
 class ConsolePortBulkDeleteView(BulkDeleteView):
     queryset = ConsolePort.objects.all()
     filterset = filters.ConsolePortFilterSet
@@ -1262,7 +1277,6 @@ class ConsoleServerPortBulkRenameView(BulkRenameView):
 
 class ConsoleServerPortBulkDisconnectView(BulkDisconnectView):
     queryset = ConsoleServerPort.objects.all()
-    form = forms.ConsoleServerPortBulkDisconnectForm
 
 
 class ConsoleServerPortBulkDeleteView(BulkDeleteView):
@@ -1322,6 +1336,10 @@ class PowerPortBulkRenameView(BulkRenameView):
     queryset = PowerPort.objects.all()
 
 
+class PowerPortBulkDisconnectView(BulkDisconnectView):
+    queryset = PowerPort.objects.all()
+
+
 class PowerPortBulkDeleteView(BulkDeleteView):
     queryset = PowerPort.objects.all()
     filterset = filters.PowerPortFilterSet
@@ -1381,7 +1399,6 @@ class PowerOutletBulkRenameView(BulkRenameView):
 
 class PowerOutletBulkDisconnectView(BulkDisconnectView):
     queryset = PowerOutlet.objects.all()
-    form = forms.PowerOutletBulkDisconnectForm
 
 
 class PowerOutletBulkDeleteView(BulkDeleteView):
@@ -1476,7 +1493,6 @@ class InterfaceBulkRenameView(BulkRenameView):
 
 class InterfaceBulkDisconnectView(BulkDisconnectView):
     queryset = Interface.objects.all()
-    form = forms.InterfaceBulkDisconnectForm
 
 
 class InterfaceBulkDeleteView(BulkDeleteView):
@@ -1538,7 +1554,6 @@ class FrontPortBulkRenameView(BulkRenameView):
 
 class FrontPortBulkDisconnectView(BulkDisconnectView):
     queryset = FrontPort.objects.all()
-    form = forms.FrontPortBulkDisconnectForm
 
 
 class FrontPortBulkDeleteView(BulkDeleteView):
@@ -1600,7 +1615,6 @@ class RearPortBulkRenameView(BulkRenameView):
 
 class RearPortBulkDisconnectView(BulkDisconnectView):
     queryset = RearPort.objects.all()
-    form = forms.RearPortBulkDisconnectForm
 
 
 class RearPortBulkDeleteView(BulkDeleteView):

+ 6 - 0
netbox/templates/dcim/device.html

@@ -346,6 +346,9 @@
                                 <button type="submit" name="_edit" formaction="{% url 'dcim:consoleport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
                                     <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
                                 </button>
+                                <button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
+                                    <span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
+                                </button>
                             {% endif %}
                             {% if console_ports and perms.dcim.delete_consoleport %}
                                 <button type="submit" name="_delete" formaction="{% url 'dcim:consoleport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
@@ -383,6 +386,9 @@
                                 <button type="submit" name="_edit" formaction="{% url 'dcim:powerport_bulk_edit' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
                                     <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
                                 </button>
+                                <button type="submit" name="_disconnect" formaction="{% url 'dcim:powerport_bulk_disconnect' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
+                                    <span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
+                                </button>
                             {% endif %}
                             {% if power_ports and perms.dcim.delete_powerport %}
                                 <button type="submit" name="_delete" formaction="{% url 'dcim:powerport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">