Closes #4792: Add bulk rename capability for console and power ports

This commit is contained in:
Jeremy Stretch 2020-06-25 14:18:29 -04:00
parent 6d23d9ebb7
commit 1dbae5b64c
8 changed files with 328 additions and 316 deletions

View File

@ -15,6 +15,7 @@ NetBox v2.9 replaces Django's built-in permissions framework with one that suppo
* [#4615](https://github.com/netbox-community/netbox/issues/4615) - Add `label` field for all device components * [#4615](https://github.com/netbox-community/netbox/issues/4615) - Add `label` field for all device components
* [#4742](https://github.com/netbox-community/netbox/issues/4742) - Add tagging for cables, power panels, and rack reservations * [#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 * [#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
### Configuration Changes ### Configuration Changes

View File

@ -23,7 +23,7 @@ from tenancy.forms import TenancyFilterForm, TenancyForm
from tenancy.models import Tenant, TenantGroup from tenancy.models import Tenant, TenantGroup
from utilities.forms import ( from utilities.forms import (
APISelect, APISelectMultiple, add_blank_choice, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, APISelect, APISelectMultiple, add_blank_choice, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect,
BulkRenameForm, ColorSelect, CommentField, ConfirmationForm, CSVChoiceField, CSVModelChoiceField, CSVModelForm, ColorSelect, CommentField, ConfirmationForm, CSVChoiceField, CSVModelChoiceField, CSVModelForm,
DynamicModelChoiceField, DynamicModelMultipleChoiceField, ExpandableNameField, form_from_model, JSONField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, ExpandableNameField, form_from_model, JSONField,
NumericArrayField, SelectWithPK, SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple, TagFilterField, NumericArrayField, SelectWithPK, SmallTextarea, SlugField, StaticSelect2, StaticSelect2Multiple, TagFilterField,
BOOLEAN_WITH_BLANK_CHOICES, BOOLEAN_WITH_BLANK_CHOICES,
@ -2375,13 +2375,6 @@ class ConsoleServerPortBulkEditForm(
] ]
class ConsoleServerPortBulkRenameForm(BulkRenameForm):
pk = forms.ModelMultipleChoiceField(
queryset=ConsoleServerPort.objects.all(),
widget=forms.MultipleHiddenInput()
)
class ConsoleServerPortBulkDisconnectForm(ConfirmationForm): class ConsoleServerPortBulkDisconnectForm(ConfirmationForm):
pk = forms.ModelMultipleChoiceField( pk = forms.ModelMultipleChoiceField(
queryset=ConsoleServerPort.objects.all(), queryset=ConsoleServerPort.objects.all(),
@ -2610,13 +2603,6 @@ class PowerOutletBulkEditForm(
self.fields['power_port'].widget.attrs['disabled'] = True self.fields['power_port'].widget.attrs['disabled'] = True
class PowerOutletBulkRenameForm(BulkRenameForm):
pk = forms.ModelMultipleChoiceField(
queryset=PowerOutlet.objects.all(),
widget=forms.MultipleHiddenInput
)
class PowerOutletBulkDisconnectForm(ConfirmationForm): class PowerOutletBulkDisconnectForm(ConfirmationForm):
pk = forms.ModelMultipleChoiceField( pk = forms.ModelMultipleChoiceField(
queryset=PowerOutlet.objects.all(), queryset=PowerOutlet.objects.all(),
@ -2922,13 +2908,6 @@ class InterfaceBulkEditForm(
self.cleaned_data['tagged_vlans'] = [] self.cleaned_data['tagged_vlans'] = []
class InterfaceBulkRenameForm(BulkRenameForm):
pk = forms.ModelMultipleChoiceField(
queryset=Interface.objects.all(),
widget=forms.MultipleHiddenInput()
)
class InterfaceBulkDisconnectForm(ConfirmationForm): class InterfaceBulkDisconnectForm(ConfirmationForm):
pk = forms.ModelMultipleChoiceField( pk = forms.ModelMultipleChoiceField(
queryset=Interface.objects.all(), queryset=Interface.objects.all(),
@ -3115,13 +3094,6 @@ class FrontPortBulkEditForm(
] ]
class FrontPortBulkRenameForm(BulkRenameForm):
pk = forms.ModelMultipleChoiceField(
queryset=FrontPort.objects.all(),
widget=forms.MultipleHiddenInput
)
class FrontPortBulkDisconnectForm(ConfirmationForm): class FrontPortBulkDisconnectForm(ConfirmationForm):
pk = forms.ModelMultipleChoiceField( pk = forms.ModelMultipleChoiceField(
queryset=FrontPort.objects.all(), queryset=FrontPort.objects.all(),
@ -3245,13 +3217,6 @@ class RearPortBulkEditForm(
] ]
class RearPortBulkRenameForm(BulkRenameForm):
pk = forms.ModelMultipleChoiceField(
queryset=RearPort.objects.all(),
widget=forms.MultipleHiddenInput
)
class RearPortBulkDisconnectForm(ConfirmationForm): class RearPortBulkDisconnectForm(ConfirmationForm):
pk = forms.ModelMultipleChoiceField( pk = forms.ModelMultipleChoiceField(
queryset=RearPort.objects.all(), queryset=RearPort.objects.all(),
@ -3354,13 +3319,6 @@ class DeviceBayBulkEditForm(
) )
class DeviceBayBulkRenameForm(BulkRenameForm):
pk = forms.ModelMultipleChoiceField(
queryset=DeviceBay.objects.all(),
widget=forms.MultipleHiddenInput()
)
class DeviceBayCSVForm(CSVModelForm): class DeviceBayCSVForm(CSVModelForm):
device = CSVModelChoiceField( device = CSVModelChoiceField(
queryset=Device.objects.all(), queryset=Device.objects.all(),

View File

@ -187,7 +187,8 @@ urlpatterns = [
path('console-ports/add/', views.ConsolePortCreateView.as_view(), name='consoleport_add'), path('console-ports/add/', views.ConsolePortCreateView.as_view(), name='consoleport_add'),
path('console-ports/import/', views.ConsolePortBulkImportView.as_view(), name='consoleport_import'), 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/edit/', views.ConsolePortBulkEditView.as_view(), name='consoleport_bulk_edit'),
# TODO: Bulk rename, disconnect views for ConsolePorts path('console-ports/rename/', views.ConsolePortBulkRenameView.as_view(), name='consoleport_bulk_rename'),
# TODO: Bulk disconnect view for ConsolePorts
path('console-ports/delete/', views.ConsolePortBulkDeleteView.as_view(), name='consoleport_bulk_delete'), 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>/', views.ConsolePortView.as_view(), name='consoleport'),
path('console-ports/<int:pk>/edit/', views.ConsolePortEditView.as_view(), name='consoleport_edit'), path('console-ports/<int:pk>/edit/', views.ConsolePortEditView.as_view(), name='consoleport_edit'),
@ -218,7 +219,8 @@ urlpatterns = [
path('power-ports/add/', views.PowerPortCreateView.as_view(), name='powerport_add'), path('power-ports/add/', views.PowerPortCreateView.as_view(), name='powerport_add'),
path('power-ports/import/', views.PowerPortBulkImportView.as_view(), name='powerport_import'), 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/edit/', views.PowerPortBulkEditView.as_view(), name='powerport_bulk_edit'),
# TODO: Bulk rename, disconnect views for PowerPorts path('power-ports/rename/', views.PowerPortBulkRenameView.as_view(), name='powerport_bulk_rename'),
# TODO: Bulk disconnect view for PowerPorts
path('power-ports/delete/', views.PowerPortBulkDeleteView.as_view(), name='powerport_bulk_delete'), 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>/', views.PowerPortView.as_view(), name='powerport'),
path('power-ports/<int:pk>/edit/', views.PowerPortEditView.as_view(), name='powerport_edit'), path('power-ports/<int:pk>/edit/', views.PowerPortEditView.as_view(), name='powerport_edit'),

View File

@ -1199,6 +1199,10 @@ class ConsolePortBulkEditView(BulkEditView):
form = forms.ConsolePortBulkEditForm form = forms.ConsolePortBulkEditForm
class ConsolePortBulkRenameView(BulkRenameView):
queryset = ConsolePort.objects.all()
class ConsolePortBulkDeleteView(BulkDeleteView): class ConsolePortBulkDeleteView(BulkDeleteView):
queryset = ConsolePort.objects.all() queryset = ConsolePort.objects.all()
filterset = filters.ConsolePortFilterSet filterset = filters.ConsolePortFilterSet
@ -1254,7 +1258,6 @@ class ConsoleServerPortBulkEditView(BulkEditView):
class ConsoleServerPortBulkRenameView(BulkRenameView): class ConsoleServerPortBulkRenameView(BulkRenameView):
queryset = ConsoleServerPort.objects.all() queryset = ConsoleServerPort.objects.all()
form = forms.ConsoleServerPortBulkRenameForm
class ConsoleServerPortBulkDisconnectView(BulkDisconnectView): class ConsoleServerPortBulkDisconnectView(BulkDisconnectView):
@ -1315,6 +1318,10 @@ class PowerPortBulkEditView(BulkEditView):
form = forms.PowerPortBulkEditForm form = forms.PowerPortBulkEditForm
class PowerPortBulkRenameView(BulkRenameView):
queryset = PowerPort.objects.all()
class PowerPortBulkDeleteView(BulkDeleteView): class PowerPortBulkDeleteView(BulkDeleteView):
queryset = PowerPort.objects.all() queryset = PowerPort.objects.all()
filterset = filters.PowerPortFilterSet filterset = filters.PowerPortFilterSet
@ -1370,7 +1377,6 @@ class PowerOutletBulkEditView(BulkEditView):
class PowerOutletBulkRenameView(BulkRenameView): class PowerOutletBulkRenameView(BulkRenameView):
queryset = PowerOutlet.objects.all() queryset = PowerOutlet.objects.all()
form = forms.PowerOutletBulkRenameForm
class PowerOutletBulkDisconnectView(BulkDisconnectView): class PowerOutletBulkDisconnectView(BulkDisconnectView):
@ -1466,7 +1472,6 @@ class InterfaceBulkEditView(BulkEditView):
class InterfaceBulkRenameView(BulkRenameView): class InterfaceBulkRenameView(BulkRenameView):
queryset = Interface.objects.all() queryset = Interface.objects.all()
form = forms.InterfaceBulkRenameForm
class InterfaceBulkDisconnectView(BulkDisconnectView): class InterfaceBulkDisconnectView(BulkDisconnectView):
@ -1529,7 +1534,6 @@ class FrontPortBulkEditView(BulkEditView):
class FrontPortBulkRenameView(BulkRenameView): class FrontPortBulkRenameView(BulkRenameView):
queryset = FrontPort.objects.all() queryset = FrontPort.objects.all()
form = forms.FrontPortBulkRenameForm
class FrontPortBulkDisconnectView(BulkDisconnectView): class FrontPortBulkDisconnectView(BulkDisconnectView):
@ -1592,7 +1596,6 @@ class RearPortBulkEditView(BulkEditView):
class RearPortBulkRenameView(BulkRenameView): class RearPortBulkRenameView(BulkRenameView):
queryset = RearPort.objects.all() queryset = RearPort.objects.all()
form = forms.RearPortBulkRenameForm
class RearPortBulkDisconnectView(BulkDisconnectView): class RearPortBulkDisconnectView(BulkDisconnectView):
@ -1722,7 +1725,6 @@ class DeviceBayBulkEditView(BulkEditView):
class DeviceBayBulkRenameView(BulkRenameView): class DeviceBayBulkRenameView(BulkRenameView):
queryset = DeviceBay.objects.all() queryset = DeviceBay.objects.all()
form = forms.DeviceBayBulkRenameForm
class DeviceBayBulkDeleteView(BulkDeleteView): class DeviceBayBulkDeleteView(BulkDeleteView):

View File

@ -326,34 +326,79 @@
{% plugin_left_page device %} {% plugin_left_page device %}
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
{% if console_ports or power_ports %} {% if console_ports %}
<div class="panel panel-default"> <form method="post">
<div class="panel-heading"> {% csrf_token %}
<strong>Console / Power</strong> <div class="panel panel-default">
</div> <div class="panel-heading">
<table class="table table-hover panel-body component-list"> <strong>Console Ports</strong>
{% for cp in console_ports %} </div>
{% include 'dcim/inc/consoleport.html' %} <table class="table table-hover panel-body component-list">
{% endfor %} {% for cp in console_ports %}
{% for pp in power_ports %} {% include 'dcim/inc/consoleport.html' %}
{% include 'dcim/inc/powerport.html' %} {% endfor %}
{% endfor %} </table>
</table> <div class="panel-footer noprint">
{% if perms.dcim.add_interface or perms.dcim.add_consoleport or perms.dcim.add_powerport %} {% if console_ports and perms.dcim.change_consoleport %}
<div class="panel-footer text-right noprint"> <button type="submit" name="_rename" formaction="{% url 'dcim:consoleport_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
{% if perms.dcim.add_consoleport %} <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
<a href="{% url 'dcim:consoleport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-primary"> </button>
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console port <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">
</a> <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
</button>
{% endif %} {% endif %}
{% if perms.dcim.add_powerport %} {% if console_ports and perms.dcim.delete_consoleport %}
<a href="{% url 'dcim:powerport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-primary"> <button type="submit" name="_delete" formaction="{% url 'dcim:consoleport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power port <span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
</a> </button>
{% endif %}
{% if console_ports and perms.dcim.change_consoleport %}
<div class="pull-right">
<a href="{% url 'dcim:consoleport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-primary">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console port
</a>
</div>
{% endif %} {% endif %}
</div> </div>
{% endif %} </div>
</div> </form>
{% endif %}
{% if power_ports %}
<form method="post">
{% csrf_token %}
<div class="panel panel-default">
<div class="panel-heading">
<strong>Power Ports</strong>
</div>
<table class="table table-hover panel-body component-list">
{% for pp in power_ports %}
{% include 'dcim/inc/powerport.html' %}
{% endfor %}
</table>
<div class="panel-footer noprint">
{% if power_ports and perms.dcim.change_powerport %}
<button type="submit" name="_rename" formaction="{% url 'dcim:powerport_bulk_rename' %}?return_url={{ device.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 '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>
{% 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">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
</button>
{% endif %}
{% if power_ports and perms.dcim.change_powerport %}
<div class="pull-right">
<a href="{% url 'dcim:powerport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-xs btn-primary">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power port
</a>
</div>
{% endif %}
</div>
</div>
</form>
{% endif %} {% endif %}
{% if power_ports and poweroutlets %} {% if power_ports and poweroutlets %}
<div class="panel panel-default"> <div class="panel panel-default">
@ -501,262 +546,242 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
{% if device_bays or device.device_type.is_parent_device %} {% if device_bays or device.device_type.is_parent_device %}
{% if perms.dcim.delete_devicebay %} <form method="post">
<form method="post">
{% csrf_token %} {% csrf_token %}
{% endif %} <div class="panel panel-default">
<div class="panel panel-default"> <div class="panel-heading">
<div class="panel-heading"> <strong>Device Bays</strong>
<strong>Device Bays</strong> </div>
</div> <table class="table table-hover table-headings panel-body component-list">
<table class="table table-hover table-headings panel-body component-list"> <thead>
<thead>
<tr>
{% if perms.dcim.change_devicebay or perms.dcim.delete_devicebay %}
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
{% endif %}
<th>Name</th>
<th>Status</th>
<th>Description</th>
<th colspan="2">Installed Device</th>
<th></th>
</tr>
</thead>
<tbody>
{% for devicebay in device_bays %}
{% include 'dcim/inc/devicebay.html' %}
{% empty %}
<tr> <tr>
<td colspan="5" class="text-center text-muted">&mdash; No device bays defined &mdash;</td> {% if perms.dcim.change_devicebay or perms.dcim.delete_devicebay %}
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
{% endif %}
<th>Name</th>
<th>Status</th>
<th>Description</th>
<th colspan="2">Installed Device</th>
<th></th>
</tr> </tr>
{% endfor %} </thead>
</tbody> <tbody>
</table> {% for devicebay in device_bays %}
<div class="panel-footer noprint"> {% include 'dcim/inc/devicebay.html' %}
{% if device_bays and perms.dcim.change_devicebay %} {% empty %}
<button type="submit" name="_rename" formaction="{% url 'dcim:devicebay_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs"> <tr>
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename <td colspan="5" class="text-center text-muted">&mdash; No device bays defined &mdash;</td>
</button> </tr>
{% endif %} {% endfor %}
{% if device_bays and perms.dcim.delete_devicebay %} </tbody>
<button type="submit" formaction="{% url 'dcim:devicebay_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs"> </table>
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete selected <div class="panel-footer noprint">
</button> {% if device_bays and perms.dcim.change_devicebay %}
{% endif %} <button type="submit" name="_rename" formaction="{% url 'dcim:devicebay_bulk_rename' %}?return_url={{ device.get_absolute_url }}" class="btn btn-warning btn-xs">
{% if perms.dcim.add_devicebay %} <span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
<div class="pull-right"> </button>
<a href="{% url 'dcim:devicebay_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs"> {% endif %}
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add device bays {% if device_bays and perms.dcim.delete_devicebay %}
</a> <button type="submit" formaction="{% url 'dcim:devicebay_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
</div> <span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete selected
<div class="clearfix"></div> </button>
{% endif %} {% endif %}
</div> {% if perms.dcim.add_devicebay %}
</div> <div class="pull-right">
{% if perms.dcim.delete_devicebay %} <a href="{% url 'dcim:devicebay_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
</form> <span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add device bays
{% endif %} </a>
</div>
<div class="clearfix"></div>
{% endif %}
</div>
</div>
</form>
{% endif %} {% endif %}
{% if interfaces %} {% if interfaces %}
{% if perms.dcim.change_interface or perms.dcim.delete_interface %} <form method="post">
<form method="post">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="device" value="{{ device.pk }}" /> <div class="panel panel-default">
{% endif %} <div class="panel-heading">
<div class="panel panel-default"> <strong>Interfaces</strong>
<div class="panel-heading"> <div class="pull-right noprint">
<strong>Interfaces</strong> <button class="btn btn-default btn-xs toggle-ips" selected="selected">
<div class="pull-right noprint"> <span class="glyphicon glyphicon-check" aria-hidden="true"></span> Show IPs
<button class="btn btn-default btn-xs toggle-ips" selected="selected"> </button>
<span class="glyphicon glyphicon-check" aria-hidden="true"></span> Show IPs
</button>
</div>
<div class="col-md-2 pull-right noprint">
<input class="form-control interface-filter" type="text" placeholder="Filter" title="RegEx-enabled" style="height: 23px" />
</div>
</div>
<table id="interfaces_table" class="table table-hover table-headings panel-body component-list">
<thead>
<tr>
{% if perms.dcim.change_interface or perms.dcim.delete_interface %}
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
{% endif %}
<th>Name</th>
<th>LAG</th>
<th>Description</th>
<th>MTU</th>
<th>Mode</th>
<th>Cable</th>
<th colspan="2">Connection</th>
<th></th>
</tr>
</thead>
<tbody>
{% for iface in interfaces %}
{% include 'dcim/inc/interface.html' %}
{% endfor %}
</tbody>
</table>
<div class="panel-footer noprint">
{% 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">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
</button>
<button type="submit" name="_edit" formaction="{% url 'dcim:interface_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>
{% endif %}
{% if interfaces and perms.dcim.change_interface %}
<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
</button>
{% endif %}
{% if interfaces and perms.dcim.delete_interface %}
<button type="submit" name="_delete" formaction="{% url 'dcim:interface_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
</button>
{% endif %}
{% if perms.dcim.add_interface %}
<div class="pull-right">
<a href="{% url 'dcim:interface_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add interfaces
</a>
</div> </div>
<div class="clearfix"></div> <div class="col-md-2 pull-right noprint">
{% endif %} <input class="form-control interface-filter" type="text" placeholder="Filter" title="Filter text (regular expressions supported)" style="height: 23px" />
</div> </div>
</div> </div>
{% if perms.dcim.delete_interface %} <table id="interfaces_table" class="table table-hover table-headings panel-body component-list">
</form> <thead>
{% endif %} <tr>
{% if perms.dcim.change_interface or perms.dcim.delete_interface %}
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
{% endif %}
<th>Name</th>
<th>LAG</th>
<th>Description</th>
<th>MTU</th>
<th>Mode</th>
<th>Cable</th>
<th colspan="2">Connection</th>
<th></th>
</tr>
</thead>
<tbody>
{% for iface in interfaces %}
{% include 'dcim/inc/interface.html' %}
{% endfor %}
</tbody>
</table>
<div class="panel-footer noprint">
{% 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">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
</button>
<button type="submit" name="_edit" formaction="{% url 'dcim:interface_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>
{% endif %}
{% if interfaces and perms.dcim.change_interface %}
<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
</button>
{% endif %}
{% if interfaces and perms.dcim.delete_interface %}
<button type="submit" name="_delete" formaction="{% url 'dcim:interface_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
</button>
{% endif %}
{% if perms.dcim.add_interface %}
<div class="pull-right">
<a href="{% url 'dcim:interface_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add interfaces
</a>
</div>
<div class="clearfix"></div>
{% endif %}
</div>
</div>
</form>
{% endif %} {% endif %}
{% if consoleserverports %} {% if consoleserverports %}
{% if perms.dcim.delete_consoleserverport %} <form method="post">
<form method="post">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="device" value="{{ device.pk }}" /> <div class="panel panel-default">
{% endif %} <div class="panel-heading">
<div class="panel panel-default"> <strong>Console Server Ports</strong>
<div class="panel-heading"> </div>
<strong>Console Server Ports</strong> <table class="table table-hover table-headings panel-body component-list">
<thead>
<tr>
{% if perms.dcim.change_consoleserverport or perms.dcim.delete_consoleserverport %}
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
{% endif %}
<th>Name</th>
<th>Type</th>
<th>Description</th>
<th>Cable</th>
<th colspan="2">Connection</th>
<th></th>
</tr>
</thead>
<tbody>
{% for csp in consoleserverports %}
{% include 'dcim/inc/consoleserverport.html' %}
{% endfor %}
</tbody>
</table>
<div class="panel-footer noprint">
{% if consoleserverports 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">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
</button>
<button type="submit" name="_edit" formaction="{% url 'dcim:consoleserverport_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: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
</button>
{% endif %}
{% if consoleserverports and perms.dcim.delete_consoleserverport %}
<button type="submit" formaction="{% url 'dcim:consoleserverport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
</button>
{% endif %}
{% if perms.dcim.add_consoleserverport %}
<div class="pull-right">
<a href="{% url 'dcim:consoleserverport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console server ports
</a>
</div>
<div class="clearfix"></div>
{% endif %}
</div>
</div> </div>
<table class="table table-hover table-headings panel-body component-list"> </form>
<thead>
<tr>
{% if perms.dcim.change_consoleserverport or perms.dcim.delete_consoleserverport %}
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
{% endif %}
<th>Name</th>
<th>Type</th>
<th>Description</th>
<th>Cable</th>
<th colspan="2">Connection</th>
<th></th>
</tr>
</thead>
<tbody>
{% for csp in consoleserverports %}
{% include 'dcim/inc/consoleserverport.html' %}
{% endfor %}
</tbody>
</table>
<div class="panel-footer noprint">
{% if consoleserverports 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">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
</button>
<button type="submit" name="_edit" formaction="{% url 'dcim:consoleserverport_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: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
</button>
{% endif %}
{% if consoleserverports and perms.dcim.delete_consoleserverport %}
<button type="submit" formaction="{% url 'dcim:consoleserverport_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
</button>
{% endif %}
{% if perms.dcim.add_consoleserverport %}
<div class="pull-right">
<a href="{% url 'dcim:consoleserverport_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add console server ports
</a>
</div>
<div class="clearfix"></div>
{% endif %}
</div>
</div>
{% if perms.dcim.delete_consoleserverport %}
</form>
{% endif %}
{% endif %} {% endif %}
{% if poweroutlets %} {% if poweroutlets %}
{% if perms.dcim.delete_poweroutlet %} <form method="post">
<form method="post">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="device" value="{{ device.pk }}" /> <div class="panel panel-default">
{% endif %} <div class="panel-heading">
<div class="panel panel-default"> <strong>Power Outlets</strong>
<div class="panel-heading"> </div>
<strong>Power Outlets</strong> <table class="table table-hover table-headings panel-body component-list">
<thead>
<tr>
{% if perms.dcim.change_poweroutlet or perms.dcim.delete_poweroutlet %}
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
{% endif %}
<th>Name</th>
<th>Type</th>
<th>Input/Leg</th>
<th>Description</th>
<th>Cable</th>
<th colspan="3">Connection</th>
<th></th>
</tr>
</thead>
<tbody>
{% for po in poweroutlets %}
{% include 'dcim/inc/poweroutlet.html' %}
{% endfor %}
</tbody>
</table>
<div class="panel-footer noprint">
{% if poweroutlets 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">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
</button>
<button type="submit" name="_edit" formaction="{% url 'dcim:poweroutlet_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: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
</button>
{% endif %}
{% if poweroutlets and perms.dcim.delete_poweroutlet %}
<button type="submit" formaction="{% url 'dcim:poweroutlet_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
</button>
{% endif %}
{% if perms.dcim.add_poweroutlet %}
<div class="pull-right">
<a href="{% url 'dcim:poweroutlet_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power outlets
</a>
</div>
<div class="clearfix"></div>
{% endif %}
</div>
</div> </div>
<table class="table table-hover table-headings panel-body component-list"> </form>
<thead>
<tr>
{% if perms.dcim.change_poweroutlet or perms.dcim.delete_poweroutlet %}
<th class="pk"><input type="checkbox" class="toggle" title="Toggle all" /></th>
{% endif %}
<th>Name</th>
<th>Type</th>
<th>Input/Leg</th>
<th>Description</th>
<th>Cable</th>
<th colspan="3">Connection</th>
<th></th>
</tr>
</thead>
<tbody>
{% for po in poweroutlets %}
{% include 'dcim/inc/poweroutlet.html' %}
{% endfor %}
</tbody>
</table>
<div class="panel-footer noprint">
{% if poweroutlets 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">
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Rename
</button>
<button type="submit" name="_edit" formaction="{% url 'dcim:poweroutlet_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: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
</button>
{% endif %}
{% if poweroutlets and perms.dcim.delete_poweroutlet %}
<button type="submit" formaction="{% url 'dcim:poweroutlet_bulk_delete' %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
</button>
{% endif %}
{% if perms.dcim.add_poweroutlet %}
<div class="pull-right">
<a href="{% url 'dcim:poweroutlet_add' %}?device={{ device.pk }}&return_url={{ device.get_absolute_url }}" class="btn btn-primary btn-xs">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add power outlets
</a>
</div>
<div class="clearfix"></div>
{% endif %}
</div>
</div>
{% if perms.dcim.delete_poweroutlet %}
</form>
{% endif %}
{% endif %} {% endif %}
{% if front_ports %} {% if front_ports %}
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="device" value="{{ device.pk }}" />
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<strong>Front Ports</strong> <strong>Front Ports</strong>
@ -815,7 +840,6 @@
{% if rear_ports %} {% if rear_ports %}
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="device" value="{{ device.pk }}" />
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<strong>Rear Ports</strong> <strong>Rear Ports</strong>

View File

@ -1,5 +1,12 @@
<tr class="consoleport{% if cp.cable %} {{ cp.cable.get_status_class }}{% endif %}"> <tr class="consoleport{% if cp.cable %} {{ cp.cable.get_status_class }}{% endif %}">
{# Checkbox #}
{% if perms.dcim.change_consoleport or perms.dcim.delete_consoleport %}
<td class="pk">
<input name="pk" type="checkbox" value="{{ cp.pk }}" />
</td>
{% endif %}
{# Name #} {# Name #}
<td> <td>
<i class="fa fa-fw fa-keyboard-o"></i> <i class="fa fa-fw fa-keyboard-o"></i>

View File

@ -1,5 +1,12 @@
<tr class="powerport{% if pp.cable %} {{ pp.cable.get_status_class }}{% endif %}"> <tr class="powerport{% if pp.cable %} {{ pp.cable.get_status_class }}{% endif %}">
{# Checkbox #}
{% if perms.dcim.change_powerport or perms.dcim.delete_powerport %}
<td class="pk">
<input name="pk" type="checkbox" value="{{ pp.pk }}" />
</td>
{% endif %}
{# Name #} {# Name #}
<td> <td>
<i class="fa fa-fw fa-bolt"></i> <i class="fa fa-fw fa-bolt"></i>

View File

@ -28,7 +28,7 @@ from django_tables2 import RequestConfig
from extras.models import CustomField, CustomFieldValue, ExportTemplate from extras.models import CustomField, CustomFieldValue, ExportTemplate
from extras.querysets import CustomFieldQueryset from extras.querysets import CustomFieldQueryset
from utilities.exceptions import AbortTransaction from utilities.exceptions import AbortTransaction
from utilities.forms import BootstrapMixin, CSVDataField, TableConfigForm from utilities.forms import BootstrapMixin, BulkRenameForm, CSVDataField, TableConfigForm
from utilities.permissions import get_permission_for_model, resolve_permission from utilities.permissions import get_permission_for_model, resolve_permission
from utilities.utils import csv_format, prepare_cloned_fields from utilities.utils import csv_format, prepare_cloned_fields
from .error_handlers import handle_protectederror from .error_handlers import handle_protectederror
@ -988,9 +988,20 @@ class BulkRenameView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
An extendable view for renaming objects in bulk. An extendable view for renaming objects in bulk.
""" """
queryset = None queryset = None
form = None
template_name = 'utilities/obj_bulk_rename.html' template_name = 'utilities/obj_bulk_rename.html'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Create a new Form class from BulkRenameForm
class _Form(BulkRenameForm):
pk = ModelMultipleChoiceField(
queryset=self.queryset,
widget=MultipleHiddenInput()
)
self.form = _Form
def get_required_permission(self): def get_required_permission(self):
return get_permission_for_model(self.queryset.model, 'change') return get_permission_for_model(self.queryset.model, 'change')