views.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. import traceback
  2. from django.contrib import messages
  3. from django.db import transaction
  4. from django.db.models import Prefetch, Sum
  5. from django.http import HttpResponse
  6. from django.shortcuts import get_object_or_404, redirect, render
  7. from django.urls import reverse
  8. from django.utils.translation import gettext_lazy as _
  9. from django.views.generic.base import RedirectView
  10. from jinja2.exceptions import TemplateError
  11. from dcim.filtersets import DeviceFilterSet
  12. from dcim.forms import DeviceFilterForm
  13. from dcim.models import Device
  14. from dcim.tables import DeviceTable
  15. from extras.views import ObjectConfigContextView
  16. from ipam.models import IPAddress
  17. from ipam.tables import InterfaceVLANTable, VLANTranslationRuleTable
  18. from netbox.constants import DEFAULT_ACTION_PERMISSIONS
  19. from netbox.views import generic
  20. from tenancy.views import ObjectContactsView
  21. from utilities.query import count_related
  22. from utilities.query_functions import CollateAsChar
  23. from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view
  24. from . import filtersets, forms, tables
  25. from .models import *
  26. #
  27. # Cluster types
  28. #
  29. @register_model_view(ClusterType, 'list', path='', detail=False)
  30. class ClusterTypeListView(generic.ObjectListView):
  31. queryset = ClusterType.objects.annotate(
  32. cluster_count=count_related(Cluster, 'type')
  33. )
  34. filterset = filtersets.ClusterTypeFilterSet
  35. filterset_form = forms.ClusterTypeFilterForm
  36. table = tables.ClusterTypeTable
  37. @register_model_view(ClusterType)
  38. class ClusterTypeView(GetRelatedModelsMixin, generic.ObjectView):
  39. queryset = ClusterType.objects.all()
  40. def get_extra_context(self, request, instance):
  41. return {
  42. 'related_models': self.get_related_models(request, instance),
  43. }
  44. @register_model_view(ClusterType, 'add', detail=False)
  45. @register_model_view(ClusterType, 'edit')
  46. class ClusterTypeEditView(generic.ObjectEditView):
  47. queryset = ClusterType.objects.all()
  48. form = forms.ClusterTypeForm
  49. @register_model_view(ClusterType, 'delete')
  50. class ClusterTypeDeleteView(generic.ObjectDeleteView):
  51. queryset = ClusterType.objects.all()
  52. @register_model_view(ClusterType, 'import', detail=False)
  53. class ClusterTypeBulkImportView(generic.BulkImportView):
  54. queryset = ClusterType.objects.all()
  55. model_form = forms.ClusterTypeImportForm
  56. @register_model_view(ClusterType, 'bulk_edit', path='edit', detail=False)
  57. class ClusterTypeBulkEditView(generic.BulkEditView):
  58. queryset = ClusterType.objects.annotate(
  59. cluster_count=count_related(Cluster, 'type')
  60. )
  61. filterset = filtersets.ClusterTypeFilterSet
  62. table = tables.ClusterTypeTable
  63. form = forms.ClusterTypeBulkEditForm
  64. @register_model_view(ClusterType, 'bulk_delete', path='delete', detail=False)
  65. class ClusterTypeBulkDeleteView(generic.BulkDeleteView):
  66. queryset = ClusterType.objects.annotate(
  67. cluster_count=count_related(Cluster, 'type')
  68. )
  69. filterset = filtersets.ClusterTypeFilterSet
  70. table = tables.ClusterTypeTable
  71. #
  72. # Cluster groups
  73. #
  74. @register_model_view(ClusterGroup, 'list', path='', detail=False)
  75. class ClusterGroupListView(generic.ObjectListView):
  76. queryset = ClusterGroup.objects.annotate(
  77. cluster_count=count_related(Cluster, 'group')
  78. )
  79. filterset = filtersets.ClusterGroupFilterSet
  80. filterset_form = forms.ClusterGroupFilterForm
  81. table = tables.ClusterGroupTable
  82. @register_model_view(ClusterGroup)
  83. class ClusterGroupView(GetRelatedModelsMixin, generic.ObjectView):
  84. queryset = ClusterGroup.objects.all()
  85. def get_extra_context(self, request, instance):
  86. return {
  87. 'related_models': self.get_related_models(request, instance),
  88. }
  89. @register_model_view(ClusterGroup, 'add', detail=False)
  90. @register_model_view(ClusterGroup, 'edit')
  91. class ClusterGroupEditView(generic.ObjectEditView):
  92. queryset = ClusterGroup.objects.all()
  93. form = forms.ClusterGroupForm
  94. @register_model_view(ClusterGroup, 'delete')
  95. class ClusterGroupDeleteView(generic.ObjectDeleteView):
  96. queryset = ClusterGroup.objects.all()
  97. @register_model_view(ClusterGroup, 'import', detail=False)
  98. class ClusterGroupBulkImportView(generic.BulkImportView):
  99. queryset = ClusterGroup.objects.annotate(
  100. cluster_count=count_related(Cluster, 'group')
  101. )
  102. model_form = forms.ClusterGroupImportForm
  103. @register_model_view(ClusterGroup, 'bulk_edit', path='edit', detail=False)
  104. class ClusterGroupBulkEditView(generic.BulkEditView):
  105. queryset = ClusterGroup.objects.annotate(
  106. cluster_count=count_related(Cluster, 'group')
  107. )
  108. filterset = filtersets.ClusterGroupFilterSet
  109. table = tables.ClusterGroupTable
  110. form = forms.ClusterGroupBulkEditForm
  111. @register_model_view(ClusterGroup, 'bulk_delete', path='delete', detail=False)
  112. class ClusterGroupBulkDeleteView(generic.BulkDeleteView):
  113. queryset = ClusterGroup.objects.annotate(
  114. cluster_count=count_related(Cluster, 'group')
  115. )
  116. filterset = filtersets.ClusterGroupFilterSet
  117. table = tables.ClusterGroupTable
  118. @register_model_view(ClusterGroup, 'contacts')
  119. class ClusterGroupContactsView(ObjectContactsView):
  120. queryset = ClusterGroup.objects.all()
  121. #
  122. # Clusters
  123. #
  124. @register_model_view(Cluster, 'list', path='', detail=False)
  125. class ClusterListView(generic.ObjectListView):
  126. permission_required = 'virtualization.view_cluster'
  127. queryset = Cluster.objects.annotate(
  128. device_count=count_related(Device, 'cluster'),
  129. vm_count=count_related(VirtualMachine, 'cluster')
  130. )
  131. table = tables.ClusterTable
  132. filterset = filtersets.ClusterFilterSet
  133. filterset_form = forms.ClusterFilterForm
  134. @register_model_view(Cluster)
  135. class ClusterView(generic.ObjectView):
  136. queryset = Cluster.objects.all()
  137. def get_extra_context(self, request, instance):
  138. return instance.virtual_machines.aggregate(vcpus_sum=Sum('vcpus'), memory_sum=Sum('memory'), disk_sum=Sum('disk'))
  139. @register_model_view(Cluster, 'virtualmachines', path='virtual-machines')
  140. class ClusterVirtualMachinesView(generic.ObjectChildrenView):
  141. queryset = Cluster.objects.all()
  142. child_model = VirtualMachine
  143. table = tables.VirtualMachineTable
  144. filterset = filtersets.VirtualMachineFilterSet
  145. filterset_form = forms.VirtualMachineFilterForm
  146. tab = ViewTab(
  147. label=_('Virtual Machines'),
  148. badge=lambda obj: obj.virtual_machines.count(),
  149. permission='virtualization.view_virtualmachine',
  150. weight=500
  151. )
  152. def get_children(self, request, parent):
  153. return VirtualMachine.objects.restrict(request.user, 'view').filter(cluster=parent)
  154. @register_model_view(Cluster, 'devices')
  155. class ClusterDevicesView(generic.ObjectChildrenView):
  156. queryset = Cluster.objects.all()
  157. child_model = Device
  158. table = DeviceTable
  159. filterset = DeviceFilterSet
  160. filterset_form = DeviceFilterForm
  161. template_name = 'virtualization/cluster/devices.html'
  162. actions = {
  163. 'add': {'add'},
  164. 'import': {'add'},
  165. 'export': {'view'},
  166. 'bulk_edit': {'change'},
  167. 'bulk_remove_devices': {'change'},
  168. }
  169. tab = ViewTab(
  170. label=_('Devices'),
  171. badge=lambda obj: obj.devices.count(),
  172. permission='virtualization.view_virtualmachine',
  173. weight=600
  174. )
  175. def get_children(self, request, parent):
  176. return Device.objects.restrict(request.user, 'view').filter(cluster=parent)
  177. @register_model_view(Cluster, 'add', detail=False)
  178. @register_model_view(Cluster, 'edit')
  179. class ClusterEditView(generic.ObjectEditView):
  180. queryset = Cluster.objects.all()
  181. form = forms.ClusterForm
  182. @register_model_view(Cluster, 'delete')
  183. class ClusterDeleteView(generic.ObjectDeleteView):
  184. queryset = Cluster.objects.all()
  185. @register_model_view(Cluster, 'import', detail=False)
  186. class ClusterBulkImportView(generic.BulkImportView):
  187. queryset = Cluster.objects.all()
  188. model_form = forms.ClusterImportForm
  189. @register_model_view(Cluster, 'bulk_edit', path='edit', detail=False)
  190. class ClusterBulkEditView(generic.BulkEditView):
  191. queryset = Cluster.objects.all()
  192. filterset = filtersets.ClusterFilterSet
  193. table = tables.ClusterTable
  194. form = forms.ClusterBulkEditForm
  195. @register_model_view(Cluster, 'bulk_delete', path='delete', detail=False)
  196. class ClusterBulkDeleteView(generic.BulkDeleteView):
  197. queryset = Cluster.objects.all()
  198. filterset = filtersets.ClusterFilterSet
  199. table = tables.ClusterTable
  200. @register_model_view(Cluster, 'add_devices', path='devices/add')
  201. class ClusterAddDevicesView(generic.ObjectEditView):
  202. queryset = Cluster.objects.all()
  203. form = forms.ClusterAddDevicesForm
  204. template_name = 'virtualization/cluster_add_devices.html'
  205. def get(self, request, pk):
  206. cluster = get_object_or_404(self.queryset, pk=pk)
  207. form = self.form(cluster, initial=request.GET)
  208. return render(request, self.template_name, {
  209. 'cluster': cluster,
  210. 'form': form,
  211. 'return_url': reverse('virtualization:cluster', kwargs={'pk': pk}),
  212. })
  213. def post(self, request, pk):
  214. cluster = get_object_or_404(self.queryset, pk=pk)
  215. form = self.form(cluster, request.POST)
  216. if form.is_valid():
  217. device_pks = form.cleaned_data['devices']
  218. with transaction.atomic():
  219. # Assign the selected Devices to the Cluster
  220. for device in Device.objects.filter(pk__in=device_pks):
  221. device.cluster = cluster
  222. device.save()
  223. messages.success(request, _("Added {count} devices to cluster {cluster}").format(
  224. count=len(device_pks),
  225. cluster=cluster
  226. ))
  227. return redirect(cluster.get_absolute_url())
  228. return render(request, self.template_name, {
  229. 'cluster': cluster,
  230. 'form': form,
  231. 'return_url': cluster.get_absolute_url(),
  232. })
  233. @register_model_view(Cluster, 'remove_devices', path='devices/remove')
  234. class ClusterRemoveDevicesView(generic.ObjectEditView):
  235. queryset = Cluster.objects.all()
  236. form = forms.ClusterRemoveDevicesForm
  237. template_name = 'generic/bulk_remove.html'
  238. def post(self, request, pk):
  239. cluster = get_object_or_404(self.queryset, pk=pk)
  240. if '_confirm' in request.POST:
  241. form = self.form(request.POST)
  242. if form.is_valid():
  243. device_pks = form.cleaned_data['pk']
  244. with transaction.atomic():
  245. # Remove the selected Devices from the Cluster
  246. for device in Device.objects.filter(pk__in=device_pks):
  247. device.cluster = None
  248. device.save()
  249. messages.success(request, _("Removed {count} devices from cluster {cluster}").format(
  250. count=len(device_pks),
  251. cluster=cluster
  252. ))
  253. return redirect(cluster.get_absolute_url())
  254. else:
  255. form = self.form(initial={'pk': request.POST.getlist('pk')})
  256. selected_objects = Device.objects.filter(pk__in=form.initial['pk'])
  257. device_table = DeviceTable(list(selected_objects), orderable=False)
  258. return render(request, self.template_name, {
  259. 'form': form,
  260. 'parent_obj': cluster,
  261. 'table': device_table,
  262. 'obj_type_plural': 'devices',
  263. 'return_url': cluster.get_absolute_url(),
  264. })
  265. @register_model_view(Cluster, 'contacts')
  266. class ClusterContactsView(ObjectContactsView):
  267. queryset = Cluster.objects.all()
  268. #
  269. # Virtual machines
  270. #
  271. @register_model_view(VirtualMachine, 'list', path='', detail=False)
  272. class VirtualMachineListView(generic.ObjectListView):
  273. queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
  274. filterset = filtersets.VirtualMachineFilterSet
  275. filterset_form = forms.VirtualMachineFilterForm
  276. table = tables.VirtualMachineTable
  277. template_name = 'virtualization/virtualmachine_list.html'
  278. @register_model_view(VirtualMachine)
  279. class VirtualMachineView(generic.ObjectView):
  280. queryset = VirtualMachine.objects.all()
  281. @register_model_view(VirtualMachine, 'interfaces')
  282. class VirtualMachineInterfacesView(generic.ObjectChildrenView):
  283. queryset = VirtualMachine.objects.all()
  284. child_model = VMInterface
  285. table = tables.VirtualMachineVMInterfaceTable
  286. filterset = filtersets.VMInterfaceFilterSet
  287. filterset_form = forms.VMInterfaceFilterForm
  288. template_name = 'virtualization/virtualmachine/interfaces.html'
  289. actions = {
  290. **DEFAULT_ACTION_PERMISSIONS,
  291. 'bulk_rename': {'change'},
  292. }
  293. tab = ViewTab(
  294. label=_('Interfaces'),
  295. badge=lambda obj: obj.interface_count,
  296. permission='virtualization.view_vminterface',
  297. weight=500
  298. )
  299. def get_children(self, request, parent):
  300. return parent.interfaces.restrict(request.user, 'view').prefetch_related(
  301. Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
  302. 'tags',
  303. )
  304. @register_model_view(VirtualMachine, 'disks')
  305. class VirtualMachineVirtualDisksView(generic.ObjectChildrenView):
  306. queryset = VirtualMachine.objects.all()
  307. child_model = VirtualDisk
  308. table = tables.VirtualMachineVirtualDiskTable
  309. filterset = filtersets.VirtualDiskFilterSet
  310. filterset_form = forms.VirtualDiskFilterForm
  311. template_name = 'virtualization/virtualmachine/virtual_disks.html'
  312. tab = ViewTab(
  313. label=_('Virtual Disks'),
  314. badge=lambda obj: obj.virtual_disk_count,
  315. permission='virtualization.view_virtualdisk',
  316. weight=500
  317. )
  318. actions = {
  319. **DEFAULT_ACTION_PERMISSIONS,
  320. 'bulk_rename': {'change'},
  321. }
  322. def get_children(self, request, parent):
  323. return parent.virtualdisks.restrict(request.user, 'view').prefetch_related('tags')
  324. @register_model_view(VirtualMachine, 'configcontext', path='config-context')
  325. class VirtualMachineConfigContextView(ObjectConfigContextView):
  326. queryset = VirtualMachine.objects.annotate_config_context_data()
  327. base_template = 'virtualization/virtualmachine.html'
  328. tab = ViewTab(
  329. label=_('Config Context'),
  330. weight=2000
  331. )
  332. @register_model_view(VirtualMachine, 'render-config')
  333. class VirtualMachineRenderConfigView(generic.ObjectView):
  334. queryset = VirtualMachine.objects.all()
  335. template_name = 'virtualization/virtualmachine/render_config.html'
  336. tab = ViewTab(
  337. label=_('Render Config'),
  338. weight=2100
  339. )
  340. def get(self, request, **kwargs):
  341. instance = self.get_object(**kwargs)
  342. context = self.get_extra_context(request, instance)
  343. # If a direct export has been requested, return the rendered template content as a
  344. # downloadable file.
  345. if request.GET.get('export'):
  346. response = HttpResponse(context['rendered_config'], content_type='text')
  347. filename = f"{instance.name or 'config'}.txt"
  348. response['Content-Disposition'] = f'attachment; filename="{filename}"'
  349. return response
  350. return render(request, self.get_template_name(), {
  351. 'object': instance,
  352. 'tab': self.tab,
  353. **context,
  354. })
  355. def get_extra_context(self, request, instance):
  356. # Compile context data
  357. context_data = instance.get_config_context()
  358. context_data.update({'virtualmachine': instance})
  359. # Render the config template
  360. rendered_config = None
  361. if config_template := instance.get_config_template():
  362. try:
  363. rendered_config = config_template.render(context=context_data)
  364. except TemplateError as e:
  365. messages.error(request, _("An error occurred while rendering the template: {error}").format(error=e))
  366. rendered_config = traceback.format_exc()
  367. return {
  368. 'config_template': config_template,
  369. 'context_data': context_data,
  370. 'rendered_config': rendered_config,
  371. }
  372. @register_model_view(VirtualMachine, 'add', detail=False)
  373. @register_model_view(VirtualMachine, 'edit')
  374. class VirtualMachineEditView(generic.ObjectEditView):
  375. queryset = VirtualMachine.objects.all()
  376. form = forms.VirtualMachineForm
  377. @register_model_view(VirtualMachine, 'delete')
  378. class VirtualMachineDeleteView(generic.ObjectDeleteView):
  379. queryset = VirtualMachine.objects.all()
  380. @register_model_view(VirtualMachine, 'import', detail=False)
  381. class VirtualMachineBulkImportView(generic.BulkImportView):
  382. queryset = VirtualMachine.objects.all()
  383. model_form = forms.VirtualMachineImportForm
  384. @register_model_view(VirtualMachine, 'bulk_edit', path='edit', detail=False)
  385. class VirtualMachineBulkEditView(generic.BulkEditView):
  386. queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
  387. filterset = filtersets.VirtualMachineFilterSet
  388. table = tables.VirtualMachineTable
  389. form = forms.VirtualMachineBulkEditForm
  390. @register_model_view(VirtualMachine, 'bulk_delete', path='delete', detail=False)
  391. class VirtualMachineBulkDeleteView(generic.BulkDeleteView):
  392. queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
  393. filterset = filtersets.VirtualMachineFilterSet
  394. table = tables.VirtualMachineTable
  395. @register_model_view(VirtualMachine, 'contacts')
  396. class VirtualMachineContactsView(ObjectContactsView):
  397. queryset = VirtualMachine.objects.all()
  398. #
  399. # VM interfaces
  400. #
  401. @register_model_view(VMInterface, 'list', path='', detail=False)
  402. class VMInterfaceListView(generic.ObjectListView):
  403. queryset = VMInterface.objects.all()
  404. filterset = filtersets.VMInterfaceFilterSet
  405. filterset_form = forms.VMInterfaceFilterForm
  406. table = tables.VMInterfaceTable
  407. @register_model_view(VMInterface)
  408. class VMInterfaceView(generic.ObjectView):
  409. queryset = VMInterface.objects.all()
  410. def get_extra_context(self, request, instance):
  411. # Get child interfaces
  412. child_interfaces = VMInterface.objects.restrict(request.user, 'view').filter(parent=instance)
  413. child_interfaces_tables = tables.VMInterfaceTable(
  414. child_interfaces,
  415. exclude=('virtual_machine',),
  416. orderable=False
  417. )
  418. # Get VLAN translation rules
  419. vlan_translation_table = None
  420. if instance.vlan_translation_policy:
  421. vlan_translation_table = VLANTranslationRuleTable(
  422. data=instance.vlan_translation_policy.rules.all(),
  423. orderable=False
  424. )
  425. # Get assigned VLANs and annotate whether each is tagged or untagged
  426. vlans = []
  427. if instance.untagged_vlan is not None:
  428. vlans.append(instance.untagged_vlan)
  429. vlans[0].tagged = False
  430. for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
  431. vlan.tagged = True
  432. vlans.append(vlan)
  433. vlan_table = InterfaceVLANTable(
  434. interface=instance,
  435. data=vlans,
  436. orderable=False
  437. )
  438. return {
  439. 'child_interfaces_table': child_interfaces_tables,
  440. 'vlan_table': vlan_table,
  441. 'vlan_translation_table': vlan_translation_table,
  442. }
  443. @register_model_view(VMInterface, 'add', detail=False)
  444. class VMInterfaceCreateView(generic.ComponentCreateView):
  445. queryset = VMInterface.objects.all()
  446. form = forms.VMInterfaceCreateForm
  447. model_form = forms.VMInterfaceForm
  448. @register_model_view(VMInterface, 'edit')
  449. class VMInterfaceEditView(generic.ObjectEditView):
  450. queryset = VMInterface.objects.all()
  451. form = forms.VMInterfaceForm
  452. @register_model_view(VMInterface, 'delete')
  453. class VMInterfaceDeleteView(generic.ObjectDeleteView):
  454. queryset = VMInterface.objects.all()
  455. @register_model_view(VMInterface, 'import', detail=False)
  456. class VMInterfaceBulkImportView(generic.BulkImportView):
  457. queryset = VMInterface.objects.all()
  458. model_form = forms.VMInterfaceImportForm
  459. @register_model_view(VMInterface, 'bulk_edit', path='edit', detail=False)
  460. class VMInterfaceBulkEditView(generic.BulkEditView):
  461. queryset = VMInterface.objects.all()
  462. filterset = filtersets.VMInterfaceFilterSet
  463. table = tables.VMInterfaceTable
  464. form = forms.VMInterfaceBulkEditForm
  465. @register_model_view(VMInterface, 'bulk_rename', path='rename', detail=False)
  466. class VMInterfaceBulkRenameView(generic.BulkRenameView):
  467. queryset = VMInterface.objects.all()
  468. form = forms.VMInterfaceBulkRenameForm
  469. @register_model_view(VMInterface, 'bulk_delete', path='delete', detail=False)
  470. class VMInterfaceBulkDeleteView(generic.BulkDeleteView):
  471. # Ensure child interfaces are deleted prior to their parents
  472. queryset = VMInterface.objects.order_by('virtual_machine', 'parent', CollateAsChar('_name'))
  473. filterset = filtersets.VMInterfaceFilterSet
  474. table = tables.VMInterfaceTable
  475. #
  476. # Virtual disks
  477. #
  478. @register_model_view(VirtualDisk, 'list', path='', detail=False)
  479. class VirtualDiskListView(generic.ObjectListView):
  480. queryset = VirtualDisk.objects.all()
  481. filterset = filtersets.VirtualDiskFilterSet
  482. filterset_form = forms.VirtualDiskFilterForm
  483. table = tables.VirtualDiskTable
  484. @register_model_view(VirtualDisk)
  485. class VirtualDiskView(generic.ObjectView):
  486. queryset = VirtualDisk.objects.all()
  487. @register_model_view(VirtualDisk, 'add', detail=False)
  488. class VirtualDiskCreateView(generic.ComponentCreateView):
  489. queryset = VirtualDisk.objects.all()
  490. form = forms.VirtualDiskCreateForm
  491. model_form = forms.VirtualDiskForm
  492. @register_model_view(VirtualDisk, 'edit')
  493. class VirtualDiskEditView(generic.ObjectEditView):
  494. queryset = VirtualDisk.objects.all()
  495. form = forms.VirtualDiskForm
  496. @register_model_view(VirtualDisk, 'delete')
  497. class VirtualDiskDeleteView(generic.ObjectDeleteView):
  498. queryset = VirtualDisk.objects.all()
  499. @register_model_view(VirtualDisk, 'import', detail=False)
  500. class VirtualDiskBulkImportView(generic.BulkImportView):
  501. queryset = VirtualDisk.objects.all()
  502. model_form = forms.VirtualDiskImportForm
  503. @register_model_view(VirtualDisk, 'bulk_edit', path='edit', detail=False)
  504. class VirtualDiskBulkEditView(generic.BulkEditView):
  505. queryset = VirtualDisk.objects.all()
  506. filterset = filtersets.VirtualDiskFilterSet
  507. table = tables.VirtualDiskTable
  508. form = forms.VirtualDiskBulkEditForm
  509. @register_model_view(VirtualDisk, 'bulk_rename', path='rename', detail=False)
  510. class VirtualDiskBulkRenameView(generic.BulkRenameView):
  511. queryset = VirtualDisk.objects.all()
  512. form = forms.VirtualDiskBulkRenameForm
  513. @register_model_view(VirtualDisk, 'bulk_delete', path='delete', detail=False)
  514. class VirtualDiskBulkDeleteView(generic.BulkDeleteView):
  515. queryset = VirtualDisk.objects.all()
  516. filterset = filtersets.VirtualDiskFilterSet
  517. table = tables.VirtualDiskTable
  518. # TODO: Remove in v4.2
  519. class VirtualDiskRedirectView(RedirectView):
  520. """
  521. Redirect old (pre-v4.1) URLs for VirtualDisk views.
  522. """
  523. def get_redirect_url(self, path):
  524. return f"{reverse('virtualization:virtualdisk_list')}{path}"
  525. #
  526. # Bulk Device component creation
  527. #
  528. class VirtualMachineBulkAddInterfaceView(generic.BulkComponentCreateView):
  529. parent_model = VirtualMachine
  530. parent_field = 'virtual_machine'
  531. form = forms.VMInterfaceBulkCreateForm
  532. queryset = VMInterface.objects.all()
  533. model_form = forms.VMInterfaceForm
  534. filterset = filtersets.VirtualMachineFilterSet
  535. table = tables.VirtualMachineTable
  536. default_return_url = 'virtualization:virtualmachine_list'
  537. def get_required_permission(self):
  538. return 'virtualization.add_vminterface'
  539. class VirtualMachineBulkAddVirtualDiskView(generic.BulkComponentCreateView):
  540. parent_model = VirtualMachine
  541. parent_field = 'virtual_machine'
  542. form = forms.VirtualDiskBulkCreateForm
  543. queryset = VirtualDisk.objects.all()
  544. model_form = forms.VirtualDiskForm
  545. filterset = filtersets.VirtualMachineFilterSet
  546. table = tables.VirtualMachineTable
  547. default_return_url = 'virtualization:virtualmachine_list'
  548. def get_required_permission(self):
  549. return 'virtualization.add_virtualdisk'