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