Closes #1781: Enable bulk renaming of device components

This commit is contained in:
Jeremy Stretch 2018-01-10 15:48:07 -05:00
parent 9c27d18d6c
commit fc7a43f23e
6 changed files with 250 additions and 89 deletions

View File

@ -50,6 +50,14 @@ def get_device_by_name_or_pk(name):
return device return device
class BulkRenameForm(forms.Form):
"""
An extendable form to be used for renaming device components in bulk.
"""
find = forms.CharField()
replace = forms.CharField()
# #
# Regions # Regions
# #
@ -1346,6 +1354,10 @@ class ConsoleServerPortConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.
} }
class ConsoleServerPortBulkRenameForm(BulkRenameForm):
pk = forms.ModelMultipleChoiceField(queryset=ConsoleServerPort.objects.all(), widget=forms.MultipleHiddenInput)
class ConsoleServerPortBulkDisconnectForm(ConfirmationForm): class ConsoleServerPortBulkDisconnectForm(ConfirmationForm):
pk = forms.ModelMultipleChoiceField(queryset=ConsoleServerPort.objects.all(), widget=forms.MultipleHiddenInput) pk = forms.ModelMultipleChoiceField(queryset=ConsoleServerPort.objects.all(), widget=forms.MultipleHiddenInput)
@ -1607,6 +1619,10 @@ class PowerOutletConnectionForm(BootstrapMixin, ChainedFieldsMixin, forms.Form):
} }
class PowerOutletBulkRenameForm(BulkRenameForm):
pk = forms.ModelMultipleChoiceField(queryset=PowerOutlet.objects.all(), widget=forms.MultipleHiddenInput)
class PowerOutletBulkDisconnectForm(ConfirmationForm): class PowerOutletBulkDisconnectForm(ConfirmationForm):
pk = forms.ModelMultipleChoiceField(queryset=PowerOutlet.objects.all(), widget=forms.MultipleHiddenInput) pk = forms.ModelMultipleChoiceField(queryset=PowerOutlet.objects.all(), widget=forms.MultipleHiddenInput)
@ -1936,6 +1952,10 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm, ChainedFieldsMixin):
self.fields['tagged_vlans'].queryset = VLAN.objects.filter(**filter_dict) self.fields['tagged_vlans'].queryset = VLAN.objects.filter(**filter_dict)
class InterfaceBulkRenameForm(BulkRenameForm):
pk = forms.ModelMultipleChoiceField(queryset=Interface.objects.all(), widget=forms.MultipleHiddenInput)
class InterfaceBulkDisconnectForm(ConfirmationForm): class InterfaceBulkDisconnectForm(ConfirmationForm):
pk = forms.ModelMultipleChoiceField(queryset=Interface.objects.all(), widget=forms.MultipleHiddenInput) pk = forms.ModelMultipleChoiceField(queryset=Interface.objects.all(), widget=forms.MultipleHiddenInput)
@ -2143,6 +2163,10 @@ class PopulateDeviceBayForm(BootstrapMixin, forms.Form):
).exclude(pk=device_bay.device.pk) ).exclude(pk=device_bay.device.pk)
class DeviceBayBulkRenameForm(BulkRenameForm):
pk = forms.ModelMultipleChoiceField(queryset=DeviceBay.objects.all(), widget=forms.MultipleHiddenInput)
# #
# Connections # Connections
# #

View File

@ -154,6 +154,7 @@ urlpatterns = [
url(r'^console-server-ports/(?P<pk>\d+)/disconnect/$', views.ConsoleServerPortDisconnectView.as_view(), name='consoleserverport_disconnect'), url(r'^console-server-ports/(?P<pk>\d+)/disconnect/$', views.ConsoleServerPortDisconnectView.as_view(), name='consoleserverport_disconnect'),
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/rename/$', views.ConsoleServerPortBulkRenameView.as_view(), name='consoleserverport_bulk_rename'),
# 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'),
@ -173,6 +174,7 @@ urlpatterns = [
url(r'^power-outlets/(?P<pk>\d+)/disconnect/$', views.PowerOutletDisconnectView.as_view(), name='poweroutlet_disconnect'), url(r'^power-outlets/(?P<pk>\d+)/disconnect/$', views.PowerOutletDisconnectView.as_view(), name='poweroutlet_disconnect'),
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/rename/$', views.PowerOutletBulkRenameView.as_view(), name='poweroutlet_bulk_rename'),
# 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'),
@ -184,6 +186,7 @@ urlpatterns = [
url(r'^interface-connections/(?P<pk>\d+)/delete/$', views.InterfaceConnectionDeleteView.as_view(), name='interfaceconnection_delete'), url(r'^interface-connections/(?P<pk>\d+)/delete/$', views.InterfaceConnectionDeleteView.as_view(), name='interfaceconnection_delete'),
url(r'^interfaces/(?P<pk>\d+)/edit/$', views.InterfaceEditView.as_view(), name='interface_edit'), 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'), url(r'^interfaces/(?P<pk>\d+)/delete/$', views.InterfaceDeleteView.as_view(), name='interface_delete'),
url(r'^interfaces/rename/$', views.InterfaceBulkRenameView.as_view(), name='interface_bulk_rename'),
# Device bays # Device bays
url(r'^devices/device-bays/add/$', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'), url(r'^devices/device-bays/add/$', views.DeviceBulkAddDeviceBayView.as_view(), name='device_bulk_add_devicebay'),
@ -193,6 +196,7 @@ urlpatterns = [
url(r'^device-bays/(?P<pk>\d+)/delete/$', views.DeviceBayDeleteView.as_view(), name='devicebay_delete'), url(r'^device-bays/(?P<pk>\d+)/delete/$', views.DeviceBayDeleteView.as_view(), name='devicebay_delete'),
url(r'^device-bays/(?P<pk>\d+)/populate/$', views.DeviceBayPopulateView.as_view(), name='devicebay_populate'), url(r'^device-bays/(?P<pk>\d+)/populate/$', views.DeviceBayPopulateView.as_view(), name='devicebay_populate'),
url(r'^device-bays/(?P<pk>\d+)/depopulate/$', views.DeviceBayDepopulateView.as_view(), name='devicebay_depopulate'), url(r'^device-bays/(?P<pk>\d+)/depopulate/$', views.DeviceBayDepopulateView.as_view(), name='devicebay_depopulate'),
url(r'^device-bays/rename/$', views.DeviceBayBulkRenameView.as_view(), name='devicebay_bulk_rename'),
# Inventory items # Inventory items
url(r'^devices/(?P<device>\d+)/inventory-items/add/$', views.InventoryItemEditView.as_view(), name='inventoryitem_add'), url(r'^devices/(?P<device>\d+)/inventory-items/add/$', views.InventoryItemEditView.as_view(), name='inventoryitem_add'),

View File

@ -12,7 +12,7 @@ from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse from django.urls import reverse
from django.utils.html import escape from django.utils.html import escape
from django.utils.http import urlencode from django.utils.http import is_safe_url, urlencode
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.views.generic import View from django.views.generic import View
from natsort import natsorted from natsort import natsorted
@ -37,6 +37,50 @@ from .models import (
) )
class BulkRenameView(View):
"""
An extendable view for renaming device components in bulk.
"""
model = None
form = None
template_name = 'dcim/bulk_rename.html'
def post(self, request):
return_url = request.GET.get('return_url')
if not return_url or not is_safe_url(url=return_url, host=request.get_host()):
return_url = 'home'
if '_preview' in request.POST or '_apply' in request.POST:
form = self.form(request.POST, initial={'pk': request.POST.getlist('pk')})
selected_objects = self.model.objects.filter(pk__in=form.initial['pk'])
if form.is_valid():
for obj in selected_objects:
obj.new_name = obj.name.replace(form.cleaned_data['find'], form.cleaned_data['replace'])
if '_apply' in request.POST:
for obj in selected_objects:
obj.name = obj.new_name
obj.save()
messages.success(request, "Renamed {} {}".format(
len(selected_objects),
self.model._meta.verbose_name_plural
))
return redirect(return_url)
else:
form = self.form(initial={'pk': request.POST.getlist('pk')})
selected_objects = self.model.objects.filter(pk__in=form.initial['pk'])
return render(request, self.template_name, {
'form': form,
'obj_type_plural': self.model._meta.verbose_name_plural,
'selected_objects': selected_objects,
'return_url': return_url,
})
class BulkDisconnectView(View): class BulkDisconnectView(View):
""" """
An extendable view for disconnection console/power/interface components in bulk. An extendable view for disconnection console/power/interface components in bulk.
@ -1270,6 +1314,12 @@ class ConsoleServerPortDeleteView(PermissionRequiredMixin, ObjectDeleteView):
model = ConsoleServerPort model = ConsoleServerPort
class ConsoleServerPortBulkRenameView(PermissionRequiredMixin, BulkRenameView):
permission_required = 'dcim.change_consoleserverport'
model = ConsoleServerPort
form = forms.ConsoleServerPortBulkRenameForm
class ConsoleServerPortBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView): class ConsoleServerPortBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView):
permission_required = 'dcim.change_consoleserverport' permission_required = 'dcim.change_consoleserverport'
model = ConsoleServerPort model = ConsoleServerPort
@ -1548,6 +1598,12 @@ class PowerOutletDeleteView(PermissionRequiredMixin, ObjectDeleteView):
model = PowerOutlet model = PowerOutlet
class PowerOutletBulkRenameView(PermissionRequiredMixin, BulkRenameView):
permission_required = 'dcim.change_poweroutlet'
model = PowerOutlet
form = forms.PowerOutletBulkRenameForm
class PowerOutletBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView): class PowerOutletBulkDisconnectView(PermissionRequiredMixin, BulkDisconnectView):
permission_required = 'dcim.change_poweroutlet' permission_required = 'dcim.change_poweroutlet'
model = PowerOutlet model = PowerOutlet
@ -1612,6 +1668,12 @@ class InterfaceBulkEditView(PermissionRequiredMixin, BulkEditView):
form = forms.InterfaceBulkEditForm form = forms.InterfaceBulkEditForm
class InterfaceBulkRenameView(PermissionRequiredMixin, BulkRenameView):
permission_required = 'dcim.change_interface'
model = Interface
form = forms.InterfaceBulkRenameForm
class InterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class InterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_interface' permission_required = 'dcim.delete_interface'
cls = Interface cls = Interface
@ -1713,6 +1775,12 @@ class DeviceBayDepopulateView(PermissionRequiredMixin, View):
}) })
class DeviceBayBulkRenameView(PermissionRequiredMixin, BulkRenameView):
permission_required = 'dcim.change_devicebay'
model = DeviceBay
form = forms.DeviceBayBulkRenameForm
class DeviceBayBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): class DeviceBayBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
permission_required = 'dcim.delete_devicebay' permission_required = 'dcim.delete_devicebay'
cls = DeviceBay cls = DeviceBay

View File

@ -0,0 +1,56 @@
{% extends '_base.html' %}
{% load helpers %}
{% load form_helpers %}
{% block content %}
<h1>{% block title %}Renaming {{ selected_objects|length }} {{ obj_type_plural|bettertitle }}{% endblock %}</h1>
<div class="row">
<div class="col-md-7">
<table class="table">
<thead>
<tr>
<th>Current Name</th>
<th>New Name</th>
</tr>
</thead>
<tbody>
{% for obj in selected_objects %}
<tr>
<td>{{ obj.name }}</td>
<td>{{ obj.new_name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="col-md-5">
<form action="" method="post" class="form form-horizontal">
{% csrf_token %}
{% if form.non_field_errors %}
<div class="panel panel-danger">
<div class="panel-heading"><strong>Errors</strong></div>
<div class="panel-body">
{{ form.non_field_errors }}
</div>
</div>
{% endif %}
<div class="panel panel-default">
<div class="panel-heading"><strong>Rename</strong></div>
<div class="panel-body">
{% render_form form %}
</div>
</div>
<div class="form-group text-right">
<div class="col-md-12">
{% if '_preview' in request.POST and not form.errors %}
<button type="submit" name="_apply" class="btn btn-primary">Apply</button>
{% else %}
<button type="submit" name="_preview" class="btn btn-primary">Preview</button>
{% endif %}
<a href="{{ return_url }}" class="btn btn-default">Cancel</a>
</div>
</div>
</form>
</div>
</div>
{% endblock %}

View File

@ -398,23 +398,26 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% if perms.dcim.add_devicebay or perms.dcim.delete_devicebay %} <div class="panel-footer">
<div class="panel-footer"> {% if device_bays and perms.dcim.change_devicebay %}
{% if device_bays and perms.dcim.delete_devicebay %} <button type="submit" name="_rename" formaction="{% url 'dcim:devicebay_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
<button type="submit" class="btn btn-danger btn-xs"> <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete selected </button>
</button> {% endif %}
{% endif %} {% if device_bays and perms.dcim.delete_devicebay %}
{% if perms.dcim.add_devicebay %} <button type="submit" class="btn btn-danger btn-xs">
<div class="pull-right"> <span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete selected
<a href="{% url 'dcim:devicebay_add' pk=device.pk %}" class="btn btn-primary btn-xs"> </button>
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add device bays {% endif %}
</a> {% if perms.dcim.add_devicebay %}
</div> <div class="pull-right">
<div class="clearfix"></div> <a href="{% url 'dcim:devicebay_add' pk=device.pk %}" class="btn btn-primary btn-xs">
{% endif %} <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add device bays
</div> </a>
{% endif %} </div>
<div class="clearfix"></div>
{% endif %}
</div>
</div> </div>
{% if perms.dcim.delete_devicebay %} {% if perms.dcim.delete_devicebay %}
</form> </form>
@ -460,33 +463,34 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% if perms.dcim.add_interface or perms.dcim.delete_interface %} <div class="panel-footer">
<div class="panel-footer"> {% if interfaces and perms.dcim.change_interface %}
{% if interfaces and perms.dcim.change_interface %} <button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
<button type="submit" name="_edit" formaction="{% url 'dcim:interface_bulk_edit' pk=device.pk %}" 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> Edit </button>
</button> <button type="submit" name="_edit" formaction="{% url 'dcim:interface_bulk_edit' pk=device.pk %}" class="btn btn-warning btn-xs">
{% endif %} <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
{% if interfaces and perms.dcim.delete_interfaceconnection %} </button>
<button type="submit" name="_disconnect" formaction="{% url 'dcim:interface_bulk_disconnect' pk=device.pk %}" class="btn btn-danger btn-xs"> {% endif %}
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect {% if interfaces and perms.dcim.delete_interfaceconnection %}
</button> <button type="submit" name="_disconnect" formaction="{% url 'dcim:interface_bulk_disconnect' pk=device.pk %}" class="btn btn-danger btn-xs">
{% endif %} <span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
{% if interfaces and perms.dcim.delete_interface %} </button>
<button type="submit" name="_delete" formaction="{% url 'dcim:interface_bulk_delete' pk=device.pk %}" class="btn btn-danger btn-xs"> {% endif %}
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete {% if interfaces and perms.dcim.delete_interface %}
</button> <button type="submit" name="_delete" formaction="{% url 'dcim:interface_bulk_delete' pk=device.pk %}" class="btn btn-danger btn-xs">
{% endif %} <span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
{% if perms.dcim.add_interface %} </button>
<div class="pull-right"> {% endif %}
<a href="{% url 'dcim:interface_add' pk=device.pk %}" class="btn btn-primary btn-xs"> {% if perms.dcim.add_interface %}
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add interfaces <div class="pull-right">
</a> <a href="{% url 'dcim:interface_add' pk=device.pk %}" class="btn btn-primary btn-xs">
</div> <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add interfaces
<div class="clearfix"></div> </a>
{% endif %} </div>
</div> <div class="clearfix"></div>
{% endif %} {% endif %}
</div>
</div> </div>
{% if perms.dcim.delete_interface %} {% if perms.dcim.delete_interface %}
</form> </form>
@ -522,28 +526,29 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% if perms.dcim.add_consoleserverport or perms.dcim.delete_consoleserverport %} <div class="panel-footer">
<div class="panel-footer"> {% if cs_ports and perms.dcim.change_consoleport %}
{% if cs_ports and perms.dcim.change_consoleport %} <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="_disconnect" formaction="{% url 'dcim:consoleserverport_bulk_disconnect' pk=device.pk %}" class="btn btn-danger btn-xs"> <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect </button>
</button> <button type="submit" name="_disconnect" formaction="{% url 'dcim:consoleserverport_bulk_disconnect' pk=device.pk %}" class="btn btn-danger btn-xs">
{% endif %} <span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
{% if cs_ports and perms.dcim.delete_consoleserverport %} </button>
<button type="submit" class="btn btn-danger btn-xs"> {% endif %}
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete {% if cs_ports and perms.dcim.delete_consoleserverport %}
</button> <button type="submit" class="btn btn-danger btn-xs">
{% endif %} <span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
{% if perms.dcim.add_consoleserverport %} </button>
<div class="pull-right"> {% endif %}
<a href="{% url 'dcim:consoleserverport_add' pk=device.pk %}" class="btn btn-primary btn-xs"> {% if perms.dcim.add_consoleserverport %}
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console server ports <div class="pull-right">
</a> <a href="{% url 'dcim:consoleserverport_add' pk=device.pk %}" class="btn btn-primary btn-xs">
</div> <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console server ports
<div class="clearfix"></div> </a>
{% endif %} </div>
</div> <div class="clearfix"></div>
{% endif %} {% endif %}
</div>
</div> </div>
{% if perms.dcim.delete_consoleserverport %} {% if perms.dcim.delete_consoleserverport %}
</form> </form>
@ -579,28 +584,29 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% if perms.dcim.add_poweroutlet or perms.dcim.delete_poweroutlet %} <div class="panel-footer">
<div class="panel-footer"> {% if power_outlets and perms.dcim.change_powerport %}
{% if power_outlets and perms.dcim.change_powerport %} <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="_disconnect" formaction="{% url 'dcim:poweroutlet_bulk_disconnect' pk=device.pk %}" class="btn btn-danger btn-xs"> <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
<span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect </button>
</button> <button type="submit" name="_disconnect" formaction="{% url 'dcim:poweroutlet_bulk_disconnect' pk=device.pk %}" class="btn btn-danger btn-xs">
{% endif %} <span class="glyphicon glyphicon-resize-full" aria-hidden="true"></span> Disconnect
{% if power_outlets and perms.dcim.delete_poweroutlet %} </button>
<button type="submit" class="btn btn-danger btn-xs"> {% endif %}
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete {% if power_outlets and perms.dcim.delete_poweroutlet %}
</button> <button type="submit" class="btn btn-danger btn-xs">
{% endif %} <span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
{% if perms.dcim.add_poweroutlet %} </button>
<div class="pull-right"> {% endif %}
<a href="{% url 'dcim:poweroutlet_add' pk=device.pk %}" class="btn btn-primary btn-xs"> {% if perms.dcim.add_poweroutlet %}
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power outlets <div class="pull-right">
</a> <a href="{% url 'dcim:poweroutlet_add' pk=device.pk %}" class="btn btn-primary btn-xs">
</div> <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power outlets
<div class="clearfix"></div> </a>
{% endif %} </div>
</div> <div class="clearfix"></div>
{% endif %} {% endif %}
</div>
</div> </div>
{% if perms.dcim.delete_poweroutlet %} {% if perms.dcim.delete_poweroutlet %}
</form> </form>

View File

@ -265,6 +265,9 @@
{% if perms.dcim.add_interface or perms.dcim.delete_interface %} {% if perms.dcim.add_interface or perms.dcim.delete_interface %}
<div class="panel-footer"> <div class="panel-footer">
{% if interfaces and perms.dcim.change_interface %} {% if interfaces and perms.dcim.change_interface %}
<button type="submit" name="_rename" formaction="{% url 'dcim:interface_bulk_rename' %}?return_url={{ vm.get_absolute_url }}" class="btn btn-warning btn-xs">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
</button>
<button type="submit" name="_edit" formaction="{% url 'virtualization:interface_bulk_edit' pk=vm.pk %}" class="btn btn-warning btn-xs"> <button type="submit" name="_edit" formaction="{% url 'virtualization:interface_bulk_edit' pk=vm.pk %}" class="btn btn-warning btn-xs">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
</button> </button>