Coverage for virtualization/views.py: 91%
415 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
1from django.contrib import messages
2from django.contrib.contenttypes.models import ContentType
3from django.db import router, transaction
4from django.db.models import Prefetch, Sum
5from django.shortcuts import get_object_or_404, redirect, render
6from django.urls import reverse
7from django.utils.translation import gettext_lazy as _
9from dcim.filtersets import DeviceFilterSet
10from dcim.forms import DeviceFilterForm
11from dcim.models import Device
12from dcim.tables import DeviceTable
13from extras.ui.panels import CustomFieldsPanel, ImageAttachmentsPanel, TagsPanel
14from extras.views import ObjectConfigContextView, ObjectRenderConfigView
15from ipam.models import IPAddress, VLANGroup
16from ipam.tables import VLANTranslationRuleTable
17from ipam.ui.panels import FHRPGroupAssignmentsPanel
18from netbox.object_actions import (
19 AddObject,
20 BulkDelete,
21 BulkEdit,
22 BulkExport,
23 BulkImport,
24 BulkRename,
25 DeleteObject,
26 EditObject,
27)
28from netbox.ui import actions, layout
29from netbox.ui.breadcrumbs import Breadcrumb, filtered_list_url, object_view_url
30from netbox.ui.panels import (
31 CommentsPanel,
32 ContextTablePanel,
33 ObjectsTablePanel,
34 OrganizationalObjectPanel,
35 RelatedObjectsPanel,
36 TemplatePanel,
37)
38from netbox.views import generic
39from utilities.query import count_related
40from utilities.query_functions import CollateAsChar
41from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view
43from . import filtersets, forms, tables
44from .models import *
45from .object_actions import BulkAddComponents
46from .ui import panels
48#
49# Cluster types
50#
53@register_model_view(ClusterType, 'list', path='', detail=False)
54class ClusterTypeListView(generic.ObjectListView):
55 queryset = ClusterType.objects.annotate(
56 cluster_count=count_related(Cluster, 'type')
57 )
58 filterset = filtersets.ClusterTypeFilterSet
59 filterset_form = forms.ClusterTypeFilterForm
60 table = tables.ClusterTypeTable
63@register_model_view(ClusterType)
64class ClusterTypeView(GetRelatedModelsMixin, generic.ObjectView):
65 queryset = ClusterType.objects.all()
66 layout = layout.SimpleLayout(
67 left_panels=[
68 OrganizationalObjectPanel(),
69 TagsPanel(),
70 ],
71 right_panels=[
72 RelatedObjectsPanel(),
73 CustomFieldsPanel(),
74 CommentsPanel(),
75 ],
76 )
78 def get_extra_context(self, request, instance):
79 return {
80 'related_models': self.get_related_models(request, instance),
81 }
84@register_model_view(ClusterType, 'add', detail=False)
85@register_model_view(ClusterType, 'edit')
86class ClusterTypeEditView(generic.ObjectEditView):
87 queryset = ClusterType.objects.all()
88 form = forms.ClusterTypeForm
91@register_model_view(ClusterType, 'delete')
92class ClusterTypeDeleteView(generic.ObjectDeleteView):
93 queryset = ClusterType.objects.all()
96@register_model_view(ClusterType, 'bulk_import', path='import', detail=False)
97class ClusterTypeBulkImportView(generic.BulkImportView):
98 queryset = ClusterType.objects.all()
99 model_form = forms.ClusterTypeImportForm
102@register_model_view(ClusterType, 'bulk_edit', path='edit', detail=False)
103class ClusterTypeBulkEditView(generic.BulkEditView):
104 queryset = ClusterType.objects.annotate(
105 cluster_count=count_related(Cluster, 'type')
106 )
107 filterset = filtersets.ClusterTypeFilterSet
108 table = tables.ClusterTypeTable
109 form = forms.ClusterTypeBulkEditForm
112@register_model_view(ClusterType, 'bulk_rename', path='rename', detail=False)
113class ClusterTypeBulkRenameView(generic.BulkRenameView):
114 queryset = ClusterType.objects.all()
115 filterset = filtersets.ClusterTypeFilterSet
118@register_model_view(ClusterType, 'bulk_delete', path='delete', detail=False)
119class ClusterTypeBulkDeleteView(generic.BulkDeleteView):
120 queryset = ClusterType.objects.annotate(
121 cluster_count=count_related(Cluster, 'type')
122 )
123 filterset = filtersets.ClusterTypeFilterSet
124 table = tables.ClusterTypeTable
127#
128# Cluster groups
129#
131@register_model_view(ClusterGroup, 'list', path='', detail=False)
132class ClusterGroupListView(generic.ObjectListView):
133 queryset = ClusterGroup.objects.annotate(
134 cluster_count=count_related(Cluster, 'group')
135 )
136 filterset = filtersets.ClusterGroupFilterSet
137 filterset_form = forms.ClusterGroupFilterForm
138 table = tables.ClusterGroupTable
141@register_model_view(ClusterGroup)
142class ClusterGroupView(GetRelatedModelsMixin, generic.ObjectView):
143 queryset = ClusterGroup.objects.all()
144 layout = layout.SimpleLayout(
145 left_panels=[
146 OrganizationalObjectPanel(),
147 TagsPanel(),
148 ],
149 right_panels=[
150 RelatedObjectsPanel(),
151 CustomFieldsPanel(),
152 CommentsPanel(),
153 ],
154 )
156 def get_extra_context(self, request, instance):
157 return {
158 'related_models': self.get_related_models(
159 request,
160 instance,
161 extra=(
162 (
163 VLANGroup.objects.restrict(request.user, 'view').filter(
164 scope_type=ContentType.objects.get_for_model(ClusterGroup),
165 scope_id=instance.pk
166 ), 'cluster_group'),
167 ),
168 ),
169 }
172@register_model_view(ClusterGroup, 'add', detail=False)
173@register_model_view(ClusterGroup, 'edit')
174class ClusterGroupEditView(generic.ObjectEditView):
175 queryset = ClusterGroup.objects.all()
176 form = forms.ClusterGroupForm
179@register_model_view(ClusterGroup, 'delete')
180class ClusterGroupDeleteView(generic.ObjectDeleteView):
181 queryset = ClusterGroup.objects.all()
184@register_model_view(ClusterGroup, 'bulk_import', path='import', detail=False)
185class ClusterGroupBulkImportView(generic.BulkImportView):
186 queryset = ClusterGroup.objects.annotate(
187 cluster_count=count_related(Cluster, 'group')
188 )
189 model_form = forms.ClusterGroupImportForm
192@register_model_view(ClusterGroup, 'bulk_edit', path='edit', detail=False)
193class ClusterGroupBulkEditView(generic.BulkEditView):
194 queryset = ClusterGroup.objects.annotate(
195 cluster_count=count_related(Cluster, 'group')
196 )
197 filterset = filtersets.ClusterGroupFilterSet
198 table = tables.ClusterGroupTable
199 form = forms.ClusterGroupBulkEditForm
202@register_model_view(ClusterGroup, 'bulk_rename', path='rename', detail=False)
203class ClusterGroupBulkRenameView(generic.BulkRenameView):
204 queryset = ClusterGroup.objects.all()
205 filterset = filtersets.ClusterGroupFilterSet
208@register_model_view(ClusterGroup, 'bulk_delete', path='delete', detail=False)
209class ClusterGroupBulkDeleteView(generic.BulkDeleteView):
210 queryset = ClusterGroup.objects.annotate(
211 cluster_count=count_related(Cluster, 'group')
212 )
213 filterset = filtersets.ClusterGroupFilterSet
214 table = tables.ClusterGroupTable
217#
218# Clusters
219#
221@register_model_view(Cluster, 'list', path='', detail=False)
222class ClusterListView(generic.ObjectListView):
223 permission_required = 'virtualization.view_cluster'
224 queryset = Cluster.objects.annotate(
225 device_count=count_related(Device, 'cluster'),
226 vm_count=count_related(VirtualMachine, 'cluster')
227 )
228 table = tables.ClusterTable
229 filterset = filtersets.ClusterFilterSet
230 filterset_form = forms.ClusterFilterForm
233@register_model_view(Cluster)
234class ClusterView(GetRelatedModelsMixin, generic.ObjectView):
235 queryset = Cluster.objects.all()
236 layout = layout.SimpleLayout(
237 breadcrumbs=[
238 Breadcrumb('type', url=filtered_list_url('virtualization:cluster_list', 'type_id')),
239 Breadcrumb('group', url=filtered_list_url('virtualization:cluster_list', 'group_id')),
240 ],
241 left_panels=[
242 panels.ClusterPanel(),
243 CommentsPanel(),
244 ],
245 right_panels=[
246 TemplatePanel('virtualization/panels/cluster_resources.html'),
247 RelatedObjectsPanel(),
248 CustomFieldsPanel(),
249 TagsPanel(),
250 ],
251 )
253 def get_extra_context(self, request, instance):
254 return {
255 **instance.virtual_machines.aggregate(
256 vcpus_sum=Sum('vcpus'),
257 memory_sum=Sum('memory'),
258 disk_sum=Sum('disk')
259 ),
260 'related_models': self.get_related_models(
261 request,
262 instance,
263 omit=(),
264 extra=(
265 (VLANGroup.objects.restrict(request.user, 'view').filter(
266 scope_type=ContentType.objects.get_for_model(Cluster),
267 scope_id=instance.pk
268 ), 'cluster'),
269 )
270 ),
271 }
274@register_model_view(Cluster, 'virtualmachines', path='virtual-machines')
275class ClusterVirtualMachinesView(generic.ObjectChildrenView):
276 queryset = Cluster.objects.all()
277 child_model = VirtualMachine
278 table = tables.VirtualMachineTable
279 filterset = filtersets.VirtualMachineFilterSet
280 filterset_form = forms.VirtualMachineFilterForm
281 actions = (EditObject, DeleteObject, BulkEdit)
282 tab = ViewTab(
283 label=_('Virtual Machines'),
284 badge=lambda obj: obj.virtual_machines.count(),
285 permission='virtualization.view_virtualmachine',
286 weight=500
287 )
289 def get_children(self, request, parent):
290 return VirtualMachine.objects.restrict(request.user, 'view').filter(cluster=parent)
293@register_model_view(Cluster, 'devices')
294class ClusterDevicesView(generic.ObjectChildrenView):
295 queryset = Cluster.objects.all()
296 child_model = Device
297 table = DeviceTable
298 filterset = DeviceFilterSet
299 filterset_form = DeviceFilterForm
300 actions = (EditObject, DeleteObject, BulkEdit)
301 tab = ViewTab(
302 label=_('Devices'),
303 badge=lambda obj: obj.devices.count(),
304 permission='virtualization.view_virtualmachine',
305 weight=600
306 )
308 def get_children(self, request, parent):
309 return Device.objects.restrict(request.user, 'view').filter(cluster=parent)
312@register_model_view(Cluster, 'add', detail=False)
313@register_model_view(Cluster, 'edit')
314class ClusterEditView(generic.ObjectEditView):
315 queryset = Cluster.objects.all()
316 form = forms.ClusterForm
319@register_model_view(Cluster, 'delete')
320class ClusterDeleteView(generic.ObjectDeleteView):
321 queryset = Cluster.objects.all()
324@register_model_view(Cluster, 'bulk_import', path='import', detail=False)
325class ClusterBulkImportView(generic.BulkImportView):
326 queryset = Cluster.objects.all()
327 model_form = forms.ClusterImportForm
330@register_model_view(Cluster, 'bulk_edit', path='edit', detail=False)
331class ClusterBulkEditView(generic.BulkEditView):
332 queryset = Cluster.objects.all()
333 filterset = filtersets.ClusterFilterSet
334 table = tables.ClusterTable
335 form = forms.ClusterBulkEditForm
338@register_model_view(Cluster, 'bulk_rename', path='rename', detail=False)
339class ClusterBulkRenameView(generic.BulkRenameView):
340 queryset = Cluster.objects.all()
341 filterset = filtersets.ClusterFilterSet
344@register_model_view(Cluster, 'bulk_delete', path='delete', detail=False)
345class ClusterBulkDeleteView(generic.BulkDeleteView):
346 queryset = Cluster.objects.all()
347 filterset = filtersets.ClusterFilterSet
348 table = tables.ClusterTable
351@register_model_view(Cluster, 'add_devices', path='devices/add')
352class ClusterAddDevicesView(generic.ObjectEditView):
353 queryset = Cluster.objects.all()
354 form = forms.ClusterAddDevicesForm
355 template_name = 'virtualization/cluster_add_devices.html'
357 def get(self, request, pk):
358 cluster = get_object_or_404(self.queryset, pk=pk)
359 form = self.form(cluster, initial=request.GET)
361 return render(request, self.template_name, {
362 'cluster': cluster,
363 'form': form,
364 'return_url': reverse('virtualization:cluster', kwargs={'pk': pk}),
365 })
367 def post(self, request, pk):
368 cluster = get_object_or_404(self.queryset, pk=pk)
369 form = self.form(cluster, request.POST)
371 if form.is_valid():
373 device_pks = form.cleaned_data['devices']
374 with transaction.atomic(using=router.db_for_write(Device)):
376 # Assign the selected Devices to the Cluster
377 for device in Device.objects.filter(pk__in=device_pks):
378 device.snapshot()
379 device.cluster = cluster
380 device.save()
382 messages.success(request, _("Added {count} devices to cluster {cluster}").format(
383 count=len(device_pks),
384 cluster=cluster
385 ))
386 return redirect(cluster.get_absolute_url())
388 return render(request, self.template_name, {
389 'cluster': cluster,
390 'form': form,
391 'return_url': cluster.get_absolute_url(),
392 })
395#
396# Virtual machine types
397#
400@register_model_view(VirtualMachineType, 'list', path='', detail=False)
401class VirtualMachineTypeListView(generic.ObjectListView):
402 queryset = VirtualMachineType.objects.all()
403 filterset = filtersets.VirtualMachineTypeFilterSet
404 filterset_form = forms.VirtualMachineTypeFilterForm
405 table = tables.VirtualMachineTypeTable
408@register_model_view(VirtualMachineType)
409class VirtualMachineTypeView(GetRelatedModelsMixin, generic.ObjectView):
410 queryset = VirtualMachineType.objects.all()
411 layout = layout.SimpleLayout(
412 left_panels=[
413 panels.VirtualMachineTypePanel(),
414 TagsPanel(),
415 CommentsPanel(),
416 ],
417 right_panels=[
418 RelatedObjectsPanel(),
419 CustomFieldsPanel(),
420 ImageAttachmentsPanel(),
421 ],
422 )
424 def get_extra_context(self, request, instance):
425 return {
426 'related_models': self.get_related_models(request, instance),
427 }
430@register_model_view(VirtualMachineType, 'add', detail=False)
431@register_model_view(VirtualMachineType, 'edit')
432class VirtualMachineTypeEditView(generic.ObjectEditView):
433 queryset = VirtualMachineType.objects.all()
434 form = forms.VirtualMachineTypeForm
437@register_model_view(VirtualMachineType, 'delete')
438class VirtualMachineTypeDeleteView(generic.ObjectDeleteView):
439 queryset = VirtualMachineType.objects.all()
442@register_model_view(VirtualMachineType, 'bulk_import', path='import', detail=False)
443class VirtualMachineTypeBulkImportView(generic.BulkImportView):
444 queryset = VirtualMachineType.objects.all()
445 model_form = forms.VirtualMachineTypeImportForm
448@register_model_view(VirtualMachineType, 'bulk_edit', path='edit', detail=False)
449class VirtualMachineTypeBulkEditView(generic.BulkEditView):
450 queryset = VirtualMachineType.objects.all()
451 filterset = filtersets.VirtualMachineTypeFilterSet
452 table = tables.VirtualMachineTypeTable
453 form = forms.VirtualMachineTypeBulkEditForm
456@register_model_view(VirtualMachineType, 'bulk_rename', path='rename', detail=False)
457class VirtualMachineTypeBulkRenameView(generic.BulkRenameView):
458 queryset = VirtualMachineType.objects.all()
459 filterset = filtersets.VirtualMachineTypeFilterSet
462@register_model_view(VirtualMachineType, 'bulk_delete', path='delete', detail=False)
463class VirtualMachineTypeBulkDeleteView(generic.BulkDeleteView):
464 queryset = VirtualMachineType.objects.all()
465 filterset = filtersets.VirtualMachineTypeFilterSet
466 table = tables.VirtualMachineTypeTable
469#
470# Virtual machines
471#
474@register_model_view(VirtualMachine, 'list', path='', detail=False)
475class VirtualMachineListView(generic.ObjectListView):
476 queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
477 filterset = filtersets.VirtualMachineFilterSet
478 filterset_form = forms.VirtualMachineFilterForm
479 table = tables.VirtualMachineTable
480 actions = (AddObject, BulkImport, BulkExport, BulkAddComponents, BulkEdit, BulkRename, BulkDelete)
483@register_model_view(VirtualMachine)
484class VirtualMachineView(generic.ObjectView):
485 queryset = VirtualMachine.objects.all()
486 layout = layout.SimpleLayout(
487 left_panels=[
488 panels.VirtualMachinePanel(),
489 CustomFieldsPanel(),
490 TagsPanel(),
491 CommentsPanel(),
492 ],
493 right_panels=[
494 panels.VirtualMachinePlacementPanel(),
495 TemplatePanel('virtualization/panels/virtual_machine_resources.html'),
496 ObjectsTablePanel(
497 model='ipam.Service',
498 title=_('Application Services'),
499 filters={'virtual_machine_id': lambda ctx: ctx['object'].pk},
500 exclude_columns=['parent'],
501 actions=[
502 actions.AddObject(
503 'ipam.Service',
504 url_params={
505 'parent_content_type': lambda ctx: ContentType.objects.get_for_model(ctx['object']).pk,
506 'parent_object_id': lambda ctx: ctx['object'].pk,
507 },
508 ),
509 ],
510 ),
511 ImageAttachmentsPanel(),
512 ],
513 bottom_panels=[
514 ObjectsTablePanel(
515 model='virtualization.VirtualDisk',
516 filters={'virtual_machine_id': lambda ctx: ctx['object'].pk},
517 exclude_columns=['virtual_machine'],
518 actions=[
519 actions.AddObject(
520 'virtualization.VirtualDisk', url_params={'virtual_machine': lambda ctx: ctx['object'].pk}
521 ),
522 ],
523 ),
524 ],
525 )
528@register_model_view(VirtualMachine, 'interfaces')
529class VirtualMachineInterfacesView(generic.ObjectChildrenView):
530 queryset = VirtualMachine.objects.all()
531 child_model = VMInterface
532 table = tables.VirtualMachineVMInterfaceTable
533 filterset = filtersets.VMInterfaceFilterSet
534 filterset_form = forms.VMInterfaceFilterForm
535 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
536 tab = ViewTab(
537 label=_('Interfaces'),
538 badge=lambda obj: obj.interface_count,
539 permission='virtualization.view_vminterface',
540 weight=500
541 )
543 def get_children(self, request, parent):
544 return parent.interfaces.restrict(request.user, 'view').prefetch_related(
545 Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
546 'tags',
547 )
550@register_model_view(VirtualMachine, 'disks')
551class VirtualMachineVirtualDisksView(generic.ObjectChildrenView):
552 queryset = VirtualMachine.objects.all()
553 child_model = VirtualDisk
554 table = tables.VirtualMachineVirtualDiskTable
555 filterset = filtersets.VirtualDiskFilterSet
556 filterset_form = forms.VirtualDiskFilterForm
557 actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
558 tab = ViewTab(
559 label=_('Virtual Disks'),
560 badge=lambda obj: obj.virtual_disk_count,
561 permission='virtualization.view_virtualdisk',
562 weight=500
563 )
565 def get_children(self, request, parent):
566 return parent.virtualdisks.restrict(request.user, 'view').prefetch_related('tags')
569@register_model_view(VirtualMachine, 'configcontext', path='config-context')
570class VirtualMachineConfigContextView(ObjectConfigContextView):
571 queryset = VirtualMachine.objects.all()
572 base_template = 'virtualization/virtualmachine.html'
573 tab = ViewTab(
574 label=_('Config Context'),
575 weight=2000
576 )
579@register_model_view(VirtualMachine, 'render-config')
580class VirtualMachineRenderConfigView(ObjectRenderConfigView):
581 queryset = VirtualMachine.objects.all()
582 base_template = 'virtualization/virtualmachine/base.html'
583 additional_permissions = ['virtualization.render_config_virtualmachine']
584 tab = ViewTab(
585 label=_('Render Config'),
586 weight=2100,
587 )
590@register_model_view(VirtualMachine, 'add', detail=False)
591@register_model_view(VirtualMachine, 'edit')
592class VirtualMachineEditView(generic.ObjectEditView):
593 queryset = VirtualMachine.objects.all()
594 form = forms.VirtualMachineForm
597@register_model_view(VirtualMachine, 'delete')
598class VirtualMachineDeleteView(generic.ObjectDeleteView):
599 queryset = VirtualMachine.objects.all()
602@register_model_view(VirtualMachine, 'bulk_import', path='import', detail=False)
603class VirtualMachineBulkImportView(generic.BulkImportView):
604 queryset = VirtualMachine.objects.all()
605 model_form = forms.VirtualMachineImportForm
608@register_model_view(VirtualMachine, 'bulk_edit', path='edit', detail=False)
609class VirtualMachineBulkEditView(generic.BulkEditView):
610 queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
611 filterset = filtersets.VirtualMachineFilterSet
612 table = tables.VirtualMachineTable
613 form = forms.VirtualMachineBulkEditForm
616@register_model_view(VirtualMachine, 'bulk_rename', path='rename', detail=False)
617class VirtualMachineBulkRenameView(generic.BulkRenameView):
618 queryset = VirtualMachine.objects.all()
619 filterset = filtersets.VirtualMachineFilterSet
622@register_model_view(VirtualMachine, 'bulk_delete', path='delete', detail=False)
623class VirtualMachineBulkDeleteView(generic.BulkDeleteView):
624 queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
625 filterset = filtersets.VirtualMachineFilterSet
626 table = tables.VirtualMachineTable
629#
630# VM interfaces
631#
634@register_model_view(VMInterface, 'list', path='', detail=False)
635class VMInterfaceListView(generic.ObjectListView):
636 queryset = VMInterface.objects.all()
637 filterset = filtersets.VMInterfaceFilterSet
638 filterset_form = forms.VMInterfaceFilterForm
639 table = tables.VMInterfaceTable
642@register_model_view(VMInterface)
643class VMInterfaceView(generic.ObjectView):
644 template_name = 'generic/object.html'
645 queryset = VMInterface.objects.all()
646 layout = layout.SimpleLayout(
647 breadcrumbs=[
648 Breadcrumb(
649 'virtual_machine',
650 url=object_view_url('virtualization:virtualmachine_interfaces'),
651 ),
652 ],
653 left_panels=[
654 panels.VMInterfacePanel(),
655 TagsPanel(),
656 ],
657 right_panels=[
658 CustomFieldsPanel(),
659 panels.VMInterfaceAddressingPanel(),
660 FHRPGroupAssignmentsPanel(),
661 ],
662 bottom_panels=[
663 ObjectsTablePanel(
664 model='ipam.IPaddress',
665 filters={'vminterface_id': lambda ctx: ctx['object'].pk},
666 exclude_columns=['assigned', 'assigned_object', 'assigned_object_parent'],
667 actions=[
668 actions.AddObject(
669 'ipam.IPaddress',
670 url_params={
671 'virtual_machine': lambda ctx: ctx['object'].virtual_machine.pk,
672 'vminterface': lambda ctx: ctx['object'].pk,
673 },
674 ),
675 ],
676 ),
677 ObjectsTablePanel(
678 model='dcim.MACAddress',
679 filters={'vminterface_id': lambda ctx: ctx['object'].pk},
680 exclude_columns=['assigned_object', 'assigned_object_parent'],
681 actions=[
682 actions.AddObject(
683 'dcim.MACAddress', url_params={'vminterface': lambda ctx: ctx['object'].pk}
684 ),
685 ],
686 ),
687 ObjectsTablePanel(
688 model='ipam.VLAN',
689 title=_('Assigned VLANs'),
690 filters={'vminterface_id': lambda ctx: ctx['object'].pk},
691 ),
692 ContextTablePanel('vlan_translation_table', title=_('VLAN Translation')),
693 ContextTablePanel('child_interfaces_table', title=_('Child Interfaces')),
694 ],
695 )
697 def get_extra_context(self, request, instance):
699 # Get child interfaces
700 child_interfaces = VMInterface.objects.restrict(request.user, 'view').filter(parent=instance)
701 child_interfaces_tables = tables.VMInterfaceTable(
702 child_interfaces,
703 exclude=('virtual_machine',),
704 orderable=False
705 )
706 child_interfaces_tables.configure(request)
708 # Get VLAN translation rules
709 vlan_translation_table = None
710 if instance.vlan_translation_policy:
711 vlan_translation_table = VLANTranslationRuleTable(
712 data=instance.vlan_translation_policy.rules.all(),
713 orderable=False
714 )
715 vlan_translation_table.configure(request)
717 return {
718 'child_interfaces_table': child_interfaces_tables,
719 'vlan_translation_table': vlan_translation_table,
720 }
723@register_model_view(VMInterface, 'add', detail=False)
724class VMInterfaceCreateView(generic.ComponentCreateView):
725 queryset = VMInterface.objects.all()
726 form = forms.VMInterfaceCreateForm
727 model_form = forms.VMInterfaceForm
730@register_model_view(VMInterface, 'edit')
731class VMInterfaceEditView(generic.ObjectEditView):
732 queryset = VMInterface.objects.all()
733 form = forms.VMInterfaceForm
736@register_model_view(VMInterface, 'delete')
737class VMInterfaceDeleteView(generic.ObjectDeleteView):
738 queryset = VMInterface.objects.all()
741@register_model_view(VMInterface, 'bulk_import', path='import', detail=False)
742class VMInterfaceBulkImportView(generic.BulkImportView):
743 queryset = VMInterface.objects.all()
744 model_form = forms.VMInterfaceImportForm
747@register_model_view(VMInterface, 'bulk_edit', path='edit', detail=False)
748class VMInterfaceBulkEditView(generic.BulkEditView):
749 queryset = VMInterface.objects.all()
750 filterset = filtersets.VMInterfaceFilterSet
751 table = tables.VMInterfaceTable
752 form = forms.VMInterfaceBulkEditForm
755@register_model_view(VMInterface, 'bulk_rename', path='rename', detail=False)
756class VMInterfaceBulkRenameView(generic.BulkRenameView):
757 queryset = VMInterface.objects.all()
758 filterset = filtersets.VMInterfaceFilterSet
761@register_model_view(VMInterface, 'bulk_delete', path='delete', detail=False)
762class VMInterfaceBulkDeleteView(generic.BulkDeleteView):
763 # Ensure child interfaces are deleted prior to their parents
764 queryset = VMInterface.objects.order_by('virtual_machine', 'parent', CollateAsChar('_name'))
765 filterset = filtersets.VMInterfaceFilterSet
766 table = tables.VMInterfaceTable
769#
770# Virtual disks
771#
773@register_model_view(VirtualDisk, 'list', path='', detail=False)
774class VirtualDiskListView(generic.ObjectListView):
775 queryset = VirtualDisk.objects.all()
776 filterset = filtersets.VirtualDiskFilterSet
777 filterset_form = forms.VirtualDiskFilterForm
778 table = tables.VirtualDiskTable
781@register_model_view(VirtualDisk)
782class VirtualDiskView(generic.ObjectView):
783 template_name = 'generic/object.html'
784 queryset = VirtualDisk.objects.all()
785 layout = layout.SimpleLayout(
786 breadcrumbs=[
787 Breadcrumb(
788 'virtual_machine',
789 url=object_view_url('virtualization:virtualmachine_disks'),
790 ),
791 ],
792 left_panels=[
793 panels.VirtualDiskPanel(),
794 TagsPanel(),
795 ],
796 right_panels=[
797 CustomFieldsPanel(),
798 ],
799 )
802@register_model_view(VirtualDisk, 'add', detail=False)
803class VirtualDiskCreateView(generic.ComponentCreateView):
804 queryset = VirtualDisk.objects.all()
805 form = forms.VirtualDiskCreateForm
806 model_form = forms.VirtualDiskForm
809@register_model_view(VirtualDisk, 'edit')
810class VirtualDiskEditView(generic.ObjectEditView):
811 queryset = VirtualDisk.objects.all()
812 form = forms.VirtualDiskForm
815@register_model_view(VirtualDisk, 'delete')
816class VirtualDiskDeleteView(generic.ObjectDeleteView):
817 queryset = VirtualDisk.objects.all()
820@register_model_view(VirtualDisk, 'bulk_import', path='import', detail=False)
821class VirtualDiskBulkImportView(generic.BulkImportView):
822 queryset = VirtualDisk.objects.all()
823 model_form = forms.VirtualDiskImportForm
826@register_model_view(VirtualDisk, 'bulk_edit', path='edit', detail=False)
827class VirtualDiskBulkEditView(generic.BulkEditView):
828 queryset = VirtualDisk.objects.all()
829 filterset = filtersets.VirtualDiskFilterSet
830 table = tables.VirtualDiskTable
831 form = forms.VirtualDiskBulkEditForm
834@register_model_view(VirtualDisk, 'bulk_rename', path='rename', detail=False)
835class VirtualDiskBulkRenameView(generic.BulkRenameView):
836 queryset = VirtualDisk.objects.all()
837 filterset = filtersets.VirtualDiskFilterSet
840@register_model_view(VirtualDisk, 'bulk_delete', path='delete', detail=False)
841class VirtualDiskBulkDeleteView(generic.BulkDeleteView):
842 queryset = VirtualDisk.objects.all()
843 filterset = filtersets.VirtualDiskFilterSet
844 table = tables.VirtualDiskTable
847#
848# Bulk Device component creation
849#
851class VirtualMachineBulkAddInterfaceView(generic.BulkComponentCreateView):
852 parent_model = VirtualMachine
853 parent_field = 'virtual_machine'
854 form = forms.VMInterfaceBulkCreateForm
855 queryset = VMInterface.objects.all()
856 model_form = forms.VMInterfaceForm
857 filterset = filtersets.VirtualMachineFilterSet
858 table = tables.VirtualMachineTable
859 default_return_url = 'virtualization:virtualmachine_list'
861 def get_required_permission(self):
862 return 'virtualization.add_vminterface'
865class VirtualMachineBulkAddVirtualDiskView(generic.BulkComponentCreateView):
866 parent_model = VirtualMachine
867 parent_field = 'virtual_machine'
868 form = forms.VirtualDiskBulkCreateForm
869 queryset = VirtualDisk.objects.all()
870 model_form = forms.VirtualDiskForm
871 filterset = filtersets.VirtualMachineFilterSet
872 table = tables.VirtualMachineTable
873 default_return_url = 'virtualization:virtualmachine_list'
875 def get_required_permission(self):
876 return 'virtualization.add_virtualdisk'