diff --git a/netbox/templates/virtualization/virtualmachine/virtual_disks.html b/netbox/templates/virtualization/virtualmachine/virtual_disks.html
index 6cf5c2146..a947f9824 100644
--- a/netbox/templates/virtualization/virtualmachine/virtual_disks.html
+++ b/netbox/templates/virtualization/virtualmachine/virtual_disks.html
@@ -1,3 +1,14 @@
{% extends 'generic/object_children.html' %}
{% load helpers %}
{% load i18n %}
+
+{% block bulk_edit_controls %}
+ {{ block.super }}
+ {% if 'bulk_rename' in actions %}
+
+ {% endif %}
+{% endblock bulk_edit_controls %}
diff --git a/netbox/virtualization/filtersets.py b/netbox/virtualization/filtersets.py
index ac72fa392..645ac6572 100644
--- a/netbox/virtualization/filtersets.py
+++ b/netbox/virtualization/filtersets.py
@@ -16,6 +16,7 @@ __all__ = (
'ClusterFilterSet',
'ClusterGroupFilterSet',
'ClusterTypeFilterSet',
+ 'VirtualDiskFilterSet',
'VirtualMachineFilterSet',
'VMInterfaceFilterSet',
)
diff --git a/netbox/virtualization/forms/bulk_edit.py b/netbox/virtualization/forms/bulk_edit.py
index f23f99115..9a352bd8d 100644
--- a/netbox/virtualization/forms/bulk_edit.py
+++ b/netbox/virtualization/forms/bulk_edit.py
@@ -19,6 +19,7 @@ __all__ = (
'ClusterGroupBulkEditForm',
'ClusterTypeBulkEditForm',
'VirtualDiskBulkEditForm',
+ 'VirtualDiskBulkRenameForm',
'VirtualMachineBulkEditForm',
'VMInterfaceBulkEditForm',
'VMInterfaceBulkRenameForm',
@@ -336,3 +337,10 @@ class VirtualDiskBulkEditForm(NetBoxModelBulkEditForm):
(None, ('size',)),
)
nullable_fields = ()
+
+
+class VirtualDiskBulkRenameForm(BulkRenameForm):
+ pk = forms.ModelMultipleChoiceField(
+ queryset=VirtualDisk.objects.all(),
+ widget=forms.MultipleHiddenInput()
+ )
diff --git a/netbox/virtualization/tests/test_filtersets.py b/netbox/virtualization/tests/test_filtersets.py
index d474af21a..617d2e97d 100644
--- a/netbox/virtualization/tests/test_filtersets.py
+++ b/netbox/virtualization/tests/test_filtersets.py
@@ -6,7 +6,7 @@ from tenancy.models import Tenant, TenantGroup
from utilities.testing import ChangeLoggedFilterSetTests, create_test_device
from virtualization.choices import *
from virtualization.filtersets import *
-from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
+from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualDisk, VirtualMachine, VMInterface
class ClusterTypeTestCase(TestCase, ChangeLoggedFilterSetTests):
@@ -516,3 +516,54 @@ class VMInterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
def test_description(self):
params = {'description': ['foobar1', 'foobar2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+
+class VirtualDiskTestCase(TestCase, ChangeLoggedFilterSetTests):
+ queryset = VirtualDisk.objects.all()
+ filterset = VirtualDiskFilterSet
+
+ @classmethod
+ def setUpTestData(cls):
+
+ cluster_types = (
+ ClusterType(name='Cluster Type 1', slug='cluster-type-1'),
+ ClusterType(name='Cluster Type 2', slug='cluster-type-2'),
+ ClusterType(name='Cluster Type 3', slug='cluster-type-3'),
+ )
+ ClusterType.objects.bulk_create(cluster_types)
+
+ clusters = (
+ Cluster(name='Cluster 1', type=cluster_types[0]),
+ Cluster(name='Cluster 2', type=cluster_types[1]),
+ Cluster(name='Cluster 3', type=cluster_types[2]),
+ )
+ Cluster.objects.bulk_create(clusters)
+
+ vms = (
+ VirtualMachine(name='Virtual Machine 1', cluster=clusters[0]),
+ VirtualMachine(name='Virtual Machine 2', cluster=clusters[1]),
+ VirtualMachine(name='Virtual Machine 3', cluster=clusters[2]),
+ )
+ VirtualMachine.objects.bulk_create(vms)
+
+ disks = (
+ VirtualDisk(virtual_machine=vms[0], name='Disk 1', size=123,),
+ VirtualDisk(virtual_machine=vms[1], name='Disk 2', size=456,),
+ VirtualDisk(virtual_machine=vms[2], name='Disk 3', size=789,),
+ )
+ VirtualDisk.objects.bulk_create(disks)
+
+ def test_name(self):
+ params = {'name': ['Disk 1', 'Disk 2']}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+ def test_size(self):
+ params = {'size': [1, 2]}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+ def test_virtual_machine(self):
+ vms = VirtualMachine.objects.all()[:2]
+ params = {'virtual_machine_id': [vms[0].pk, vms[1].pk]}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+ params = {'virtual_machine': [vms[0].name, vms[1].name]}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
diff --git a/netbox/virtualization/tests/test_views.py b/netbox/virtualization/tests/test_views.py
index a5d831d7e..0405a6c14 100644
--- a/netbox/virtualization/tests/test_views.py
+++ b/netbox/virtualization/tests/test_views.py
@@ -7,7 +7,7 @@ from dcim.models import DeviceRole, Platform, Site
from ipam.models import VLAN, VRF
from utilities.testing import ViewTestCases, create_tags, create_test_device
from virtualization.choices import *
-from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
+from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualDisk, VirtualMachine, VMInterface
class ClusterGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
@@ -374,3 +374,61 @@ class VMInterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
'untagged_vlan': vlans[0].pk,
'tagged_vlans': [v.pk for v in vlans[1:4]],
}
+
+
+class VirtualDiskTestCase(ViewTestCases.DeviceComponentViewTestCase):
+ model = VirtualDisk
+ validation_excluded_fields = ('name',)
+
+ @classmethod
+ def setUpTestData(cls):
+
+ site = Site.objects.create(name='Site 1', slug='site-1')
+ role = DeviceRole.objects.create(name='Device Role 1', slug='device-role-1')
+ clustertype = ClusterType.objects.create(name='Cluster Type 1', slug='cluster-type-1')
+ cluster = Cluster.objects.create(name='Cluster 1', type=clustertype, site=site)
+ virtualmachines = (
+ VirtualMachine(name='Virtual Machine 1', site=site, cluster=cluster, role=role),
+ VirtualMachine(name='Virtual Machine 2', site=site, cluster=cluster, role=role),
+ )
+ VirtualMachine.objects.bulk_create(virtualmachines)
+
+ disks = VirtualDisk.objects.bulk_create([
+ VirtualDisk(virtual_machine=virtualmachines[0], name='Disk 1'),
+ VirtualDisk(virtual_machine=virtualmachines[0], name='Disk 2'),
+ VirtualDisk(virtual_machine=virtualmachines[0], name='Disk 3'),
+ ])
+
+ tags = create_tags('Alpha', 'Bravo', 'Charlie')
+
+ cls.form_data = {
+ 'virtual_machine': virtualmachines[0].pk,
+ 'name': 'Interface X',
+ 'size': 123,
+ 'tags': [t.pk for t in tags],
+ }
+
+ cls.bulk_create_data = {
+ 'virtual_machine': virtualmachines[1].pk,
+ 'name': 'Interface [4-6]',
+ 'size': 456,
+ 'tags': [t.pk for t in tags],
+ }
+
+ cls.csv_data = (
+ f"virtual_machine,name,size",
+ f"Virtual Machine 2,Disk 4,111",
+ f"Virtual Machine 2,Disk 5,222",
+ f"Virtual Machine 2,Disk 6,333",
+ )
+
+ cls.csv_update_data = (
+ f"id,name,size",
+ f"{disks[0].pk},Disk 7,777",
+ f"{disks[1].pk},Disk 8,888",
+ f"{disks[2].pk},Disk 9,999",
+ )
+
+ cls.bulk_edit_data = {
+ 'size': 444,
+ }
diff --git a/netbox/virtualization/urls.py b/netbox/virtualization/urls.py
index 8a360ffdc..2edd21b7d 100644
--- a/netbox/virtualization/urls.py
+++ b/netbox/virtualization/urls.py
@@ -53,6 +53,7 @@ urlpatterns = [
path('disks/add/', views.VirtualDiskEditView.as_view(), name='virtualdisk_add'),
path('disks/import/', views.VirtualDiskBulkImportView.as_view(), name='virtualdisk_import'),
path('disks/edit/', views.VirtualDiskBulkEditView.as_view(), name='virtualdisk_bulk_edit'),
+ path('disks/rename/', views.VirtualDiskBulkRenameView.as_view(), name='virtualdisk_bulk_rename'),
path('disks/delete/', views.VirtualDiskBulkDeleteView.as_view(), name='virtualdisk_bulk_delete'),
path('disks//', include(get_model_urls('virtualization', 'virtualdisk'))),
path('virtual-machines/disks/add/', views.VirtualMachineBulkAddVirtualDiskView.as_view(), name='virtualmachine_bulk_add_virtualdisk'),
diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py
index 10e4daad0..e5132468f 100644
--- a/netbox/virtualization/views.py
+++ b/netbox/virtualization/views.py
@@ -625,6 +625,11 @@ class VirtualDiskBulkEditView(generic.BulkEditView):
form = forms.VirtualDiskBulkEditForm
+class VirtualDiskBulkRenameView(generic.BulkRenameView):
+ queryset = VirtualDisk.objects.all()
+ form = forms.VirtualDiskBulkRenameForm
+
+
class VirtualDiskBulkDeleteView(generic.BulkDeleteView):
queryset = VirtualDisk.objects.all()
filterset = filtersets.VirtualDiskFilterSet