mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-24 17:38:37 -06:00
Add bulk renaming function for VM interfaces
This commit is contained in:
parent
9a0bc16c86
commit
052555c3f7
@ -23,12 +23,12 @@ 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,
|
||||||
ColorSelect, CommentField, ConfirmationForm, CSVChoiceField, CSVModelChoiceField, CSVModelForm,
|
BulkRenameForm, 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,
|
||||||
)
|
)
|
||||||
from virtualization.models import Cluster, ClusterGroup, VirtualMachine
|
from virtualization.models import Cluster, ClusterGroup
|
||||||
from .choices import *
|
from .choices import *
|
||||||
from .constants import *
|
from .constants import *
|
||||||
from .models import (
|
from .models import (
|
||||||
@ -150,30 +150,6 @@ class LabeledComponentForm(BootstrapMixin, forms.Form):
|
|||||||
}, code='label_pattern_mismatch')
|
}, code='label_pattern_mismatch')
|
||||||
|
|
||||||
|
|
||||||
class BulkRenameForm(forms.Form):
|
|
||||||
"""
|
|
||||||
An extendable form to be used for renaming device components in bulk.
|
|
||||||
"""
|
|
||||||
find = forms.CharField()
|
|
||||||
replace = forms.CharField()
|
|
||||||
use_regex = forms.BooleanField(
|
|
||||||
required=False,
|
|
||||||
initial=True,
|
|
||||||
label='Use regular expressions'
|
|
||||||
)
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
|
|
||||||
# Validate regular expression in "find" field
|
|
||||||
if self.cleaned_data['use_regex']:
|
|
||||||
try:
|
|
||||||
re.compile(self.cleaned_data['find'])
|
|
||||||
except re.error:
|
|
||||||
raise forms.ValidationError({
|
|
||||||
'find': "Invalid regular expression"
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Fields
|
# Fields
|
||||||
#
|
#
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import re
|
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
@ -25,8 +24,9 @@ from utilities.paginator import EnhancedPaginator
|
|||||||
from utilities.permissions import get_permission_for_model
|
from utilities.permissions import get_permission_for_model
|
||||||
from utilities.utils import csv_format
|
from utilities.utils import csv_format
|
||||||
from utilities.views import (
|
from utilities.views import (
|
||||||
BulkComponentCreateView, BulkDeleteView, BulkEditView, BulkImportView, ComponentCreateView, GetReturnURLMixin,
|
BulkComponentCreateView, BulkDeleteView, BulkEditView, BulkImportView, BulkRenameView, ComponentCreateView,
|
||||||
ObjectView, ObjectImportView, ObjectDeleteView, ObjectEditView, ObjectListView, ObjectPermissionRequiredMixin,
|
GetReturnURLMixin, ObjectView, ObjectImportView, ObjectDeleteView, ObjectEditView, ObjectListView,
|
||||||
|
ObjectPermissionRequiredMixin,
|
||||||
)
|
)
|
||||||
from virtualization.models import VirtualMachine
|
from virtualization.models import VirtualMachine
|
||||||
from . import filters, forms, tables
|
from . import filters, forms, tables
|
||||||
@ -41,58 +41,6 @@ from .models import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BulkRenameView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|
||||||
"""
|
|
||||||
An extendable view for renaming device components in bulk.
|
|
||||||
"""
|
|
||||||
queryset = None
|
|
||||||
form = None
|
|
||||||
template_name = 'dcim/bulk_rename.html'
|
|
||||||
|
|
||||||
def get_required_permission(self):
|
|
||||||
return get_permission_for_model(self.queryset.model, 'change')
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
|
|
||||||
if '_preview' in request.POST or '_apply' in request.POST:
|
|
||||||
form = self.form(request.POST, initial={'pk': request.POST.getlist('pk')})
|
|
||||||
selected_objects = self.queryset.filter(pk__in=form.initial['pk'])
|
|
||||||
|
|
||||||
if form.is_valid():
|
|
||||||
for obj in selected_objects:
|
|
||||||
find = form.cleaned_data['find']
|
|
||||||
replace = form.cleaned_data['replace']
|
|
||||||
if form.cleaned_data['use_regex']:
|
|
||||||
try:
|
|
||||||
obj.new_name = re.sub(find, replace, obj.name)
|
|
||||||
# Catch regex group reference errors
|
|
||||||
except re.error:
|
|
||||||
obj.new_name = obj.name
|
|
||||||
else:
|
|
||||||
obj.new_name = obj.name.replace(find, 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.queryset.model._meta.verbose_name_plural
|
|
||||||
))
|
|
||||||
return redirect(self.get_return_url(request))
|
|
||||||
|
|
||||||
else:
|
|
||||||
form = self.form(initial={'pk': request.POST.getlist('pk')})
|
|
||||||
selected_objects = self.queryset.filter(pk__in=form.initial['pk'])
|
|
||||||
|
|
||||||
return render(request, self.template_name, {
|
|
||||||
'form': form,
|
|
||||||
'obj_type_plural': self.queryset.model._meta.verbose_name_plural,
|
|
||||||
'selected_objects': selected_objects,
|
|
||||||
'return_url': self.get_return_url(request),
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
class BulkDisconnectView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
class BulkDisconnectView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
||||||
"""
|
"""
|
||||||
An extendable view for disconnection console/power/interface components in bulk.
|
An extendable view for disconnection console/power/interface components in bulk.
|
||||||
|
@ -292,6 +292,9 @@
|
|||||||
{% if perms.virtualization.add_vminterface or perms.virtualization.delete_vminterface %}
|
{% if perms.virtualization.add_vminterface or perms.virtualization.delete_vminterface %}
|
||||||
<div class="panel-footer noprint">
|
<div class="panel-footer noprint">
|
||||||
{% if interfaces and perms.virtualization.change_vminterface %}
|
{% if interfaces and perms.virtualization.change_vminterface %}
|
||||||
|
<button type="submit" name="_rename" formaction="{% url 'virtualization:vminterface_bulk_rename' %}?return_url={{ virtualmachine.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:vminterface_bulk_edit' %}?return_url={{ virtualmachine.get_absolute_url }}" class="btn btn-warning btn-xs">
|
<button type="submit" name="_edit" formaction="{% url 'virtualization:vminterface_bulk_edit' %}?return_url={{ virtualmachine.get_absolute_url }}" 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>
|
||||||
|
@ -733,6 +733,30 @@ class BulkEditForm(forms.Form):
|
|||||||
self.nullable_fields = self.Meta.nullable_fields
|
self.nullable_fields = self.Meta.nullable_fields
|
||||||
|
|
||||||
|
|
||||||
|
class BulkRenameForm(forms.Form):
|
||||||
|
"""
|
||||||
|
An extendable form to be used for renaming objects in bulk.
|
||||||
|
"""
|
||||||
|
find = forms.CharField()
|
||||||
|
replace = forms.CharField()
|
||||||
|
use_regex = forms.BooleanField(
|
||||||
|
required=False,
|
||||||
|
initial=True,
|
||||||
|
label='Use regular expressions'
|
||||||
|
)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
|
||||||
|
# Validate regular expression in "find" field
|
||||||
|
if self.cleaned_data['use_regex']:
|
||||||
|
try:
|
||||||
|
re.compile(self.cleaned_data['find'])
|
||||||
|
except re.error:
|
||||||
|
raise forms.ValidationError({
|
||||||
|
'find': "Invalid regular expression"
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class CSVModelForm(forms.ModelForm):
|
class CSVModelForm(forms.ModelForm):
|
||||||
"""
|
"""
|
||||||
ModelForm used for the import of objects in CSV format.
|
ModelForm used for the import of objects in CSV format.
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
@ -963,6 +964,58 @@ class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class BulkRenameView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
||||||
|
"""
|
||||||
|
An extendable view for renaming objects in bulk.
|
||||||
|
"""
|
||||||
|
queryset = None
|
||||||
|
form = None
|
||||||
|
template_name = 'utilities/obj_bulk_rename.html'
|
||||||
|
|
||||||
|
def get_required_permission(self):
|
||||||
|
return get_permission_for_model(self.queryset.model, 'change')
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
|
||||||
|
if '_preview' in request.POST or '_apply' in request.POST:
|
||||||
|
form = self.form(request.POST, initial={'pk': request.POST.getlist('pk')})
|
||||||
|
selected_objects = self.queryset.filter(pk__in=form.initial['pk'])
|
||||||
|
|
||||||
|
if form.is_valid():
|
||||||
|
for obj in selected_objects:
|
||||||
|
find = form.cleaned_data['find']
|
||||||
|
replace = form.cleaned_data['replace']
|
||||||
|
if form.cleaned_data['use_regex']:
|
||||||
|
try:
|
||||||
|
obj.new_name = re.sub(find, replace, obj.name)
|
||||||
|
# Catch regex group reference errors
|
||||||
|
except re.error:
|
||||||
|
obj.new_name = obj.name
|
||||||
|
else:
|
||||||
|
obj.new_name = obj.name.replace(find, 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.queryset.model._meta.verbose_name_plural
|
||||||
|
))
|
||||||
|
return redirect(self.get_return_url(request))
|
||||||
|
|
||||||
|
else:
|
||||||
|
form = self.form(initial={'pk': request.POST.getlist('pk')})
|
||||||
|
selected_objects = self.queryset.filter(pk__in=form.initial['pk'])
|
||||||
|
|
||||||
|
return render(request, self.template_name, {
|
||||||
|
'form': form,
|
||||||
|
'obj_type_plural': self.queryset.model._meta.verbose_name_plural,
|
||||||
|
'selected_objects': selected_objects,
|
||||||
|
'return_url': self.get_return_url(request),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
class BulkDeleteView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
|
||||||
"""
|
"""
|
||||||
Delete objects in bulk.
|
Delete objects in bulk.
|
||||||
|
@ -14,9 +14,9 @@ from tenancy.forms import TenancyFilterForm, TenancyForm
|
|||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
add_blank_choice, APISelect, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect,
|
add_blank_choice, APISelect, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect,
|
||||||
CommentField, ConfirmationForm, CSVChoiceField, CSVModelChoiceField, CSVModelForm, DynamicModelChoiceField,
|
BulkRenameForm, CommentField, ConfirmationForm, CSVChoiceField, CSVModelChoiceField, CSVModelForm,
|
||||||
DynamicModelMultipleChoiceField, ExpandableNameField, form_from_model, JSONField, SlugField, SmallTextarea,
|
DynamicModelChoiceField, DynamicModelMultipleChoiceField, ExpandableNameField, form_from_model, JSONField,
|
||||||
StaticSelect2, StaticSelect2Multiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES,
|
SlugField, SmallTextarea, StaticSelect2, StaticSelect2Multiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES,
|
||||||
)
|
)
|
||||||
from .choices import *
|
from .choices import *
|
||||||
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
|
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
|
||||||
@ -810,6 +810,13 @@ class VMInterfaceBulkEditForm(BootstrapMixin, BulkEditForm):
|
|||||||
self.fields['tagged_vlans'].widget.add_additional_query_param('site_id', site.pk)
|
self.fields['tagged_vlans'].widget.add_additional_query_param('site_id', site.pk)
|
||||||
|
|
||||||
|
|
||||||
|
class VMInterfaceBulkRenameForm(BulkRenameForm):
|
||||||
|
pk = forms.ModelMultipleChoiceField(
|
||||||
|
queryset=VMInterface.objects.all(),
|
||||||
|
widget=forms.MultipleHiddenInput()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class VMInterfaceFilterForm(forms.Form):
|
class VMInterfaceFilterForm(forms.Form):
|
||||||
model = VMInterface
|
model = VMInterface
|
||||||
enabled = forms.NullBooleanField(
|
enabled = forms.NullBooleanField(
|
||||||
|
@ -55,6 +55,7 @@ urlpatterns = [
|
|||||||
path('interfaces/add/', views.InterfaceCreateView.as_view(), name='vminterface_add'),
|
path('interfaces/add/', views.InterfaceCreateView.as_view(), name='vminterface_add'),
|
||||||
path('interfaces/import/', views.InterfaceBulkImportView.as_view(), name='vminterface_import'),
|
path('interfaces/import/', views.InterfaceBulkImportView.as_view(), name='vminterface_import'),
|
||||||
path('interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='vminterface_bulk_edit'),
|
path('interfaces/edit/', views.InterfaceBulkEditView.as_view(), name='vminterface_bulk_edit'),
|
||||||
|
path('interfaces/rename/', views.InterfaceBulkRenameView.as_view(), name='vminterface_bulk_rename'),
|
||||||
path('interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='vminterface_bulk_delete'),
|
path('interfaces/delete/', views.InterfaceBulkDeleteView.as_view(), name='vminterface_bulk_delete'),
|
||||||
path('interfaces/<int:pk>/', views.InterfaceView.as_view(), name='vminterface'),
|
path('interfaces/<int:pk>/', views.InterfaceView.as_view(), name='vminterface'),
|
||||||
path('interfaces/<int:pk>/edit/', views.InterfaceEditView.as_view(), name='vminterface_edit'),
|
path('interfaces/<int:pk>/edit/', views.InterfaceEditView.as_view(), name='vminterface_edit'),
|
||||||
|
@ -10,8 +10,8 @@ from extras.views import ObjectConfigContextView
|
|||||||
from ipam.models import Service
|
from ipam.models import Service
|
||||||
from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable
|
from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable
|
||||||
from utilities.views import (
|
from utilities.views import (
|
||||||
BulkComponentCreateView, BulkDeleteView, BulkEditView, BulkImportView, ComponentCreateView, ObjectView,
|
BulkComponentCreateView, BulkDeleteView, BulkEditView, BulkImportView, BulkRenameView, ComponentCreateView,
|
||||||
ObjectDeleteView, ObjectEditView, ObjectListView,
|
ObjectView, ObjectDeleteView, ObjectEditView, ObjectListView,
|
||||||
)
|
)
|
||||||
from . import filters, forms, tables
|
from . import filters, forms, tables
|
||||||
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
|
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
|
||||||
@ -362,6 +362,11 @@ class InterfaceBulkEditView(BulkEditView):
|
|||||||
form = forms.VMInterfaceBulkEditForm
|
form = forms.VMInterfaceBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceBulkRenameView(BulkRenameView):
|
||||||
|
queryset = VMInterface.objects.all()
|
||||||
|
form = forms.VMInterfaceBulkRenameForm
|
||||||
|
|
||||||
|
|
||||||
class InterfaceBulkDeleteView(BulkDeleteView):
|
class InterfaceBulkDeleteView(BulkDeleteView):
|
||||||
queryset = VMInterface.objects.all()
|
queryset = VMInterface.objects.all()
|
||||||
table = tables.VMInterfaceTable
|
table = tables.VMInterfaceTable
|
||||||
|
Loading…
Reference in New Issue
Block a user