Browse Source

Fixes #2573: Fix bulk console/power/interface disconnections

Jeremy Stretch 7 năm trước cách đây
mục cha
commit
788847edaa

+ 1 - 0
CHANGELOG.md

@@ -45,6 +45,7 @@ NetBox now supports modeling physical cables for console, power, and interface c
 * [#2569](https://github.com/digitalocean/netbox/issues/2569) - Added LSH fiber type; removed SC duplex/simplex designations
 * [#2569](https://github.com/digitalocean/netbox/issues/2569) - Added LSH fiber type; removed SC duplex/simplex designations
 * [#2571](https://github.com/digitalocean/netbox/issues/2571) - Enforce deletion of attached cable when deleting a termination point
 * [#2571](https://github.com/digitalocean/netbox/issues/2571) - Enforce deletion of attached cable when deleting a termination point
 * [#2572](https://github.com/digitalocean/netbox/issues/2572) - Add button to disconnect cable from circuit termination
 * [#2572](https://github.com/digitalocean/netbox/issues/2572) - Add button to disconnect cable from circuit termination
+* [#2573](https://github.com/digitalocean/netbox/issues/2573) - Fix bulk console/power/interface disconnections
 
 
 ## API Changes
 ## API Changes
 
 

+ 3 - 3
netbox/dcim/urls.py

@@ -169,13 +169,13 @@ urlpatterns = [
     # 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/console-server-ports/add/$', views.DeviceBulkAddConsoleServerPortView.as_view(), name='device_bulk_add_consoleserverport'),
     url(r'^devices/(?P<pk>\d+)/console-server-ports/add/$', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'),
     url(r'^devices/(?P<pk>\d+)/console-server-ports/add/$', views.ConsoleServerPortCreateView.as_view(), name='consoleserverport_add'),
-    url(r'^devices/(?P<pk>\d+)/console-server-ports/disconnect/$', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'),
     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<termination_a_id>\d+)/connect/$', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}),
     url(r'^console-server-ports/(?P<termination_a_id>\d+)/connect/$', views.CableCreateView.as_view(), name='consoleserverport_connect', kwargs={'termination_a_type': ConsoleServerPort}),
     url(r'^console-server-ports/(?P<pk>\d+)/edit/$', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'),
     url(r'^console-server-ports/(?P<pk>\d+)/edit/$', views.ConsoleServerPortEditView.as_view(), name='consoleserverport_edit'),
     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'),
     url(r'^console-server-ports/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='consoleserverport_trace', kwargs={'model': ConsoleServerPort}),
     url(r'^console-server-ports/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='consoleserverport_trace', kwargs={'model': ConsoleServerPort}),
     url(r'^console-server-ports/rename/$', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'),
     url(r'^console-server-ports/rename/$', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'),
+    url(r'^console-server-ports/disconnect/$', views.ConsoleServerPortBulkDisconnectView.as_view(), name='consoleserverport_bulk_disconnect'),
 
 
     # Power ports
     # Power ports
     url(r'^devices/power-ports/add/$', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'),
     url(r'^devices/power-ports/add/$', views.DeviceBulkAddPowerPortView.as_view(), name='device_bulk_add_powerport'),
@@ -189,19 +189,18 @@ urlpatterns = [
     # Power outlets
     # Power outlets
     url(r'^devices/power-outlets/add/$', views.DeviceBulkAddPowerOutletView.as_view(), name='device_bulk_add_poweroutlet'),
     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.PowerOutletCreateView.as_view(), name='poweroutlet_add'),
     url(r'^devices/(?P<pk>\d+)/power-outlets/add/$', views.PowerOutletCreateView.as_view(), name='poweroutlet_add'),
-    url(r'^devices/(?P<pk>\d+)/power-outlets/disconnect/$', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'),
     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<termination_a_id>\d+)/connect/$', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}),
     url(r'^power-outlets/(?P<termination_a_id>\d+)/connect/$', views.CableCreateView.as_view(), name='poweroutlet_connect', kwargs={'termination_a_type': PowerOutlet}),
     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'),
     url(r'^power-outlets/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='poweroutlet_trace', kwargs={'model': PowerOutlet}),
     url(r'^power-outlets/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='poweroutlet_trace', kwargs={'model': PowerOutlet}),
     url(r'^power-outlets/rename/$', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'),
     url(r'^power-outlets/rename/$', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'),
+    url(r'^power-outlets/disconnect/$', views.PowerOutletBulkDisconnectView.as_view(), name='poweroutlet_bulk_disconnect'),
 
 
     # Interfaces
     # Interfaces
     url(r'^devices/interfaces/add/$', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'),
     url(r'^devices/interfaces/add/$', views.DeviceBulkAddInterfaceView.as_view(), name='device_bulk_add_interface'),
     url(r'^devices/(?P<pk>\d+)/interfaces/add/$', views.InterfaceCreateView.as_view(), name='interface_add'),
     url(r'^devices/(?P<pk>\d+)/interfaces/add/$', views.InterfaceCreateView.as_view(), 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/edit/$', views.InterfaceBulkEditView.as_view(), name='interface_bulk_edit'),
-    url(r'^devices/(?P<pk>\d+)/interfaces/disconnect/$', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'),
     url(r'^devices/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
     url(r'^devices/(?P<pk>\d+)/interfaces/delete/$', views.InterfaceBulkDeleteView.as_view(), name='interface_bulk_delete'),
     url(r'^interfaces/(?P<termination_a_id>\d+)/connect/$', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}),
     url(r'^interfaces/(?P<termination_a_id>\d+)/connect/$', views.CableCreateView.as_view(), name='interface_connect', kwargs={'termination_a_type': Interface}),
     url(r'^interfaces/(?P<pk>\d+)/$', views.InterfaceView.as_view(), name='interface'),
     url(r'^interfaces/(?P<pk>\d+)/$', views.InterfaceView.as_view(), name='interface'),
@@ -211,6 +210,7 @@ urlpatterns = [
     url(r'^interfaces/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='interface_changelog', kwargs={'model': Interface}),
     url(r'^interfaces/(?P<pk>\d+)/changelog/$', ObjectChangeLogView.as_view(), name='interface_changelog', kwargs={'model': Interface}),
     url(r'^interfaces/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='interface_trace', kwargs={'model': Interface}),
     url(r'^interfaces/(?P<pk>\d+)/trace/$', views.CableTraceView.as_view(), name='interface_trace', kwargs={'model': Interface}),
     url(r'^interfaces/rename/$', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'),
     url(r'^interfaces/rename/$', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'),
+    url(r'^interfaces/disconnect/$', views.InterfaceBulkDisconnectView.as_view(), name='interface_bulk_disconnect'),
 
 
     # Front ports
     # Front ports
     # url(r'^devices/front-ports/add/$', views.DeviceBulkAddFrontPortView.as_view(), name='device_bulk_add_frontport'),
     # url(r'^devices/front-ports/add/$', views.DeviceBulkAddFrontPortView.as_view(), name='device_bulk_add_frontport'),

+ 19 - 30
netbox/dcim/views.py

@@ -74,7 +74,7 @@ class BulkRenameView(GetReturnURLMixin, View):
         })
         })
 
 
 
 
-class BulkDisconnectView(View):
+class BulkDisconnectView(GetReturnURLMixin, View):
     """
     """
     An extendable view for disconnection console/power/interface components in bulk.
     An extendable view for disconnection console/power/interface components in bulk.
     """
     """
@@ -82,22 +82,30 @@ class BulkDisconnectView(View):
     form = None
     form = None
     template_name = 'dcim/bulk_disconnect.html'
     template_name = 'dcim/bulk_disconnect.html'
 
 
-    def disconnect_objects(self, objects):
-        raise NotImplementedError()
-
-    def post(self, request, pk):
+    def post(self, request):
 
 
-        device = get_object_or_404(Device, pk=pk)
         selected_objects = []
         selected_objects = []
+        return_url = self.get_return_url(request)
 
 
         if '_confirm' in request.POST:
         if '_confirm' in request.POST:
             form = self.form(request.POST)
             form = self.form(request.POST)
+
             if form.is_valid():
             if form.is_valid():
-                count = self.disconnect_objects(form.cleaned_data['pk'])
-                messages.success(request, "Disconnected {} {} on {}".format(
-                    count, self.model._meta.verbose_name_plural, device
+
+                with transaction.atomic():
+
+                    count = 0
+                    for obj in self.model.objects.filter(pk__in=form.cleaned_data['pk']):
+                        if obj.cable is None:
+                            continue
+                        obj.cable.delete()
+                        count += 1
+
+                messages.success(request, "Disconnected {} {}".format(
+                    count, self.model._meta.verbose_name_plural
                 ))
                 ))
-                return redirect(device.get_absolute_url())
+
+                return redirect(return_url)
 
 
         else:
         else:
             form = self.form(initial={'pk': request.POST.getlist('pk')})
             form = self.form(initial={'pk': request.POST.getlist('pk')})
@@ -105,10 +113,9 @@ class BulkDisconnectView(View):
 
 
         return render(request, self.template_name, {
         return render(request, self.template_name, {
             'form': form,
             'form': form,
-            'device': device,
             'obj_type_plural': self.model._meta.verbose_name_plural,
             'obj_type_plural': self.model._meta.verbose_name_plural,
             'selected_objects': selected_objects,
             'selected_objects': selected_objects,
-            'return_url': device.get_absolute_url(),
+            'return_url': return_url,
         })
         })
 
 
 
 
@@ -1139,14 +1146,6 @@ class ConsoleServerPortBulkDisconnectView(PermissionRequiredMixin, BulkDisconnec
     model = ConsoleServerPort
     model = ConsoleServerPort
     form = forms.ConsoleServerPortBulkDisconnectForm
     form = forms.ConsoleServerPortBulkDisconnectForm
 
 
-    def disconnect_objects(self, consoleserverports):
-        return ConsolePort.objects.filter(
-            connected_endpoint__in=consoleserverports
-        ).update(
-            connected_endpoint=None,
-            connection_status=None
-        )
-
 
 
 class ConsoleServerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
 class ConsoleServerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_consoleserverport'
     permission_required = 'dcim.delete_consoleserverport'
@@ -1223,11 +1222,6 @@ class PowerOutletBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView)
     model = PowerOutlet
     model = PowerOutlet
     form = forms.PowerOutletBulkDisconnectForm
     form = forms.PowerOutletBulkDisconnectForm
 
 
-    def disconnect_objects(self, poweroutlets):
-        return PowerPort.objects.filter(connected_endpoint__in=poweroutlets).update(
-            connected_endpoint=None, connection_status=None
-        )
-
 
 
 class PowerOutletBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
 class PowerOutletBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
     permission_required = 'dcim.delete_poweroutlet'
     permission_required = 'dcim.delete_poweroutlet'
@@ -1308,11 +1302,6 @@ class InterfaceBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView):
     model = Interface
     model = Interface
     form = forms.InterfaceBulkDisconnectForm
     form = forms.InterfaceBulkDisconnectForm
 
 
-    def disconnect_objects(self, interfaces):
-        return Interface.objects.filter(_connected_interface__in=interfaces).update(
-            _connected_interface=None, connection_status=None
-        )
-
 
 
 class InterfaceBulkEditView(PermissionRequiredMixin, BulkEditView):
 class InterfaceBulkEditView(PermissionRequiredMixin, BulkEditView):
     permission_required = 'dcim.change_interface'
     permission_required = 'dcim.change_interface'

+ 1 - 1
netbox/templates/dcim/bulk_disconnect.html

@@ -4,7 +4,7 @@
 {% block title %}Disconnect {{ obj_type_plural|bettertitle }}{% endblock %}
 {% block title %}Disconnect {{ obj_type_plural|bettertitle }}{% endblock %}
 
 
 {% block message %}
 {% block message %}
-    <p>Are you sure you want to disconnect all {{ selected_objects|length }} of these {{ obj_type_plural }} on <strong>{{ device }}</strong>?</p>
+    <p>Are you sure you want to disconnect these {{ selected_objects|length }} {{ obj_type_plural }}?</p>
     <ul>
     <ul>
         {% for obj in selected_objects %}
         {% for obj in selected_objects %}
             <li>{{ obj }}</li>
             <li>{{ obj }}</li>

+ 3 - 3
netbox/templates/dcim/device.html

@@ -530,7 +530,7 @@
                             </button>
                             </button>
                         {% endif %}
                         {% endif %}
                         {% if interfaces and perms.dcim.change_interface %}
                         {% if interfaces and perms.dcim.change_interface %}
-                            <button type="submit" name="_disconnect" formaction="{% url 'dcim:interface_bulk_disconnect' pk=device.pk %}" class="btn btn-danger btn-xs">
+                            <button type="submit" name="_disconnect" formaction="{% url 'dcim:interface_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
                                 <span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
                             </button>
                             </button>
                         {% endif %}
                         {% endif %}
@@ -585,7 +585,7 @@
                             <button type="submit" name="_rename" formaction="{% url 'dcim:consoleserverport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
                             <button type="submit" name="_rename" formaction="{% url 'dcim:consoleserverport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
                                 <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
                                 <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
                             </button>
                             </button>
-                            <button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleserverport_bulk_disconnect' pk=device.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
+                            <button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleserverport_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
                                 <span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
                             </button>
                             </button>
                         {% endif %}
                         {% endif %}
@@ -640,7 +640,7 @@
                             <button type="submit" name="_rename" formaction="{% url 'dcim:poweroutlet_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
                             <button type="submit" name="_rename" formaction="{% url 'dcim:poweroutlet_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
                                 <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
                                 <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
                             </button>
                             </button>
-                            <button type="submit" name="_disconnect" formaction="{% url 'dcim:poweroutlet_bulk_disconnect' pk=device.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
+                            <button type="submit" name="_disconnect" formaction="{% url 'dcim:poweroutlet_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
                                 <span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
                             </button>
                             </button>
                         {% endif %}
                         {% endif %}