views.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. from django.contrib import messages
  2. from django.contrib.contenttypes.models import ContentType
  3. from django.db import router, transaction
  4. from django.db.models import Prefetch, Sum
  5. from django.shortcuts import get_object_or_404, redirect, render
  6. from django.urls import reverse
  7. from django.utils.translation import gettext_lazy as _
  8. from dcim.filtersets import DeviceFilterSet
  9. from dcim.forms import DeviceFilterForm
  10. from dcim.models import Device
  11. from dcim.tables import DeviceTable
  12. from extras.ui.panels import CustomFieldsPanel, ImageAttachmentsPanel, TagsPanel
  13. from extras.views import ObjectConfigContextView, ObjectRenderConfigView
  14. from ipam.models import IPAddress, VLANGroup
  15. from ipam.tables import VLANTranslationRuleTable
  16. from ipam.ui.panels import FHRPGroupAssignmentsPanel
  17. from netbox.object_actions import (
  18. AddObject,
  19. BulkDelete,
  20. BulkEdit,
  21. BulkExport,
  22. BulkImport,
  23. BulkRename,
  24. DeleteObject,
  25. EditObject,
  26. )
  27. from netbox.ui import actions, layout
  28. from netbox.ui.breadcrumbs import Breadcrumb, filtered_list_url, object_view_url
  29. from netbox.ui.panels import (
  30. CommentsPanel,
  31. ContextTablePanel,
  32. ObjectsTablePanel,
  33. OrganizationalObjectPanel,
  34. RelatedObjectsPanel,
  35. TemplatePanel,
  36. )
  37. from netbox.views import generic
  38. from utilities.query import count_related
  39. from utilities.query_functions import CollateAsChar
  40. from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view
  41. from . import filtersets, forms, tables
  42. from .models import *
  43. from .object_actions import BulkAddComponents
  44. from .ui import panels
  45. #
  46. # Cluster types
  47. #
  48. @register_model_view(ClusterType, 'list', path='', detail=False)
  49. class ClusterTypeListView(generic.ObjectListView):
  50. queryset = ClusterType.objects.annotate(
  51. cluster_count=count_related(Cluster, 'type')
  52. )
  53. filterset = filtersets.ClusterTypeFilterSet
  54. filterset_form = forms.ClusterTypeFilterForm
  55. table = tables.ClusterTypeTable
  56. @register_model_view(ClusterType)
  57. class ClusterTypeView(GetRelatedModelsMixin, generic.ObjectView):
  58. queryset = ClusterType.objects.all()
  59. layout = layout.SimpleLayout(
  60. left_panels=[
  61. OrganizationalObjectPanel(),
  62. TagsPanel(),
  63. ],
  64. right_panels=[
  65. RelatedObjectsPanel(),
  66. CustomFieldsPanel(),
  67. CommentsPanel(),
  68. ],
  69. )
  70. def get_extra_context(self, request, instance):
  71. return {
  72. 'related_models': self.get_related_models(request, instance),
  73. }
  74. @register_model_view(ClusterType, 'add', detail=False)
  75. @register_model_view(ClusterType, 'edit')
  76. class ClusterTypeEditView(generic.ObjectEditView):
  77. queryset = ClusterType.objects.all()
  78. form = forms.ClusterTypeForm
  79. @register_model_view(ClusterType, 'delete')
  80. class ClusterTypeDeleteView(generic.ObjectDeleteView):
  81. queryset = ClusterType.objects.all()
  82. @register_model_view(ClusterType, 'bulk_import', path='import', detail=False)
  83. class ClusterTypeBulkImportView(generic.BulkImportView):
  84. queryset = ClusterType.objects.all()
  85. model_form = forms.ClusterTypeImportForm
  86. @register_model_view(ClusterType, 'bulk_edit', path='edit', detail=False)
  87. class ClusterTypeBulkEditView(generic.BulkEditView):
  88. queryset = ClusterType.objects.annotate(
  89. cluster_count=count_related(Cluster, 'type')
  90. )
  91. filterset = filtersets.ClusterTypeFilterSet
  92. table = tables.ClusterTypeTable
  93. form = forms.ClusterTypeBulkEditForm
  94. @register_model_view(ClusterType, 'bulk_rename', path='rename', detail=False)
  95. class ClusterTypeBulkRenameView(generic.BulkRenameView):
  96. queryset = ClusterType.objects.all()
  97. filterset = filtersets.ClusterTypeFilterSet
  98. @register_model_view(ClusterType, 'bulk_delete', path='delete', detail=False)
  99. class ClusterTypeBulkDeleteView(generic.BulkDeleteView):
  100. queryset = ClusterType.objects.annotate(
  101. cluster_count=count_related(Cluster, 'type')
  102. )
  103. filterset = filtersets.ClusterTypeFilterSet
  104. table = tables.ClusterTypeTable
  105. #
  106. # Cluster groups
  107. #
  108. @register_model_view(ClusterGroup, 'list', path='', detail=False)
  109. class ClusterGroupListView(generic.ObjectListView):
  110. queryset = ClusterGroup.objects.annotate(
  111. cluster_count=count_related(Cluster, 'group')
  112. )
  113. filterset = filtersets.ClusterGroupFilterSet
  114. filterset_form = forms.ClusterGroupFilterForm
  115. table = tables.ClusterGroupTable
  116. @register_model_view(ClusterGroup)
  117. class ClusterGroupView(GetRelatedModelsMixin, generic.ObjectView):
  118. queryset = ClusterGroup.objects.all()
  119. layout = layout.SimpleLayout(
  120. left_panels=[
  121. OrganizationalObjectPanel(),
  122. TagsPanel(),
  123. ],
  124. right_panels=[
  125. RelatedObjectsPanel(),
  126. CustomFieldsPanel(),
  127. CommentsPanel(),
  128. ],
  129. )
  130. def get_extra_context(self, request, instance):
  131. return {
  132. 'related_models': self.get_related_models(
  133. request,
  134. instance,
  135. extra=(
  136. (
  137. VLANGroup.objects.restrict(request.user, 'view').filter(
  138. scope_type=ContentType.objects.get_for_model(ClusterGroup),
  139. scope_id=instance.pk
  140. ), 'cluster_group'),
  141. ),
  142. ),
  143. }
  144. @register_model_view(ClusterGroup, 'add', detail=False)
  145. @register_model_view(ClusterGroup, 'edit')
  146. class ClusterGroupEditView(generic.ObjectEditView):
  147. queryset = ClusterGroup.objects.all()
  148. form = forms.ClusterGroupForm
  149. @register_model_view(ClusterGroup, 'delete')
  150. class ClusterGroupDeleteView(generic.ObjectDeleteView):
  151. queryset = ClusterGroup.objects.all()
  152. @register_model_view(ClusterGroup, 'bulk_import', path='import', detail=False)
  153. class ClusterGroupBulkImportView(generic.BulkImportView):
  154. queryset = ClusterGroup.objects.annotate(
  155. cluster_count=count_related(Cluster, 'group')
  156. )
  157. model_form = forms.ClusterGroupImportForm
  158. @register_model_view(ClusterGroup, 'bulk_edit', path='edit', detail=False)
  159. class ClusterGroupBulkEditView(generic.BulkEditView):
  160. queryset = ClusterGroup.objects.annotate(
  161. cluster_count=count_related(Cluster, 'group')
  162. )
  163. filterset = filtersets.ClusterGroupFilterSet
  164. table = tables.ClusterGroupTable
  165. form = forms.ClusterGroupBulkEditForm
  166. @register_model_view(ClusterGroup, 'bulk_rename', path='rename', detail=False)
  167. class ClusterGroupBulkRenameView(generic.BulkRenameView):
  168. queryset = ClusterGroup.objects.all()
  169. filterset = filtersets.ClusterGroupFilterSet
  170. @register_model_view(ClusterGroup, 'bulk_delete', path='delete', detail=False)
  171. class ClusterGroupBulkDeleteView(generic.BulkDeleteView):
  172. queryset = ClusterGroup.objects.annotate(
  173. cluster_count=count_related(Cluster, 'group')
  174. )
  175. filterset = filtersets.ClusterGroupFilterSet
  176. table = tables.ClusterGroupTable
  177. #
  178. # Clusters
  179. #
  180. @register_model_view(Cluster, 'list', path='', detail=False)
  181. class ClusterListView(generic.ObjectListView):
  182. permission_required = 'virtualization.view_cluster'
  183. queryset = Cluster.objects.annotate(
  184. device_count=count_related(Device, 'cluster'),
  185. vm_count=count_related(VirtualMachine, 'cluster')
  186. )
  187. table = tables.ClusterTable
  188. filterset = filtersets.ClusterFilterSet
  189. filterset_form = forms.ClusterFilterForm
  190. @register_model_view(Cluster)
  191. class ClusterView(GetRelatedModelsMixin, generic.ObjectView):
  192. queryset = Cluster.objects.all()
  193. layout = layout.SimpleLayout(
  194. breadcrumbs=[
  195. Breadcrumb('type', url=filtered_list_url('virtualization:cluster_list', 'type_id')),
  196. Breadcrumb('group', url=filtered_list_url('virtualization:cluster_list', 'group_id')),
  197. ],
  198. left_panels=[
  199. panels.ClusterPanel(),
  200. CommentsPanel(),
  201. ],
  202. right_panels=[
  203. TemplatePanel('virtualization/panels/cluster_resources.html'),
  204. RelatedObjectsPanel(),
  205. CustomFieldsPanel(),
  206. TagsPanel(),
  207. ],
  208. )
  209. def get_extra_context(self, request, instance):
  210. return {
  211. **instance.virtual_machines.aggregate(
  212. vcpus_sum=Sum('vcpus'),
  213. memory_sum=Sum('memory'),
  214. disk_sum=Sum('disk')
  215. ),
  216. 'related_models': self.get_related_models(
  217. request,
  218. instance,
  219. omit=(),
  220. extra=(
  221. (VLANGroup.objects.restrict(request.user, 'view').filter(
  222. scope_type=ContentType.objects.get_for_model(Cluster),
  223. scope_id=instance.pk
  224. ), 'cluster'),
  225. )
  226. ),
  227. }
  228. @register_model_view(Cluster, 'virtualmachines', path='virtual-machines')
  229. class ClusterVirtualMachinesView(generic.ObjectChildrenView):
  230. queryset = Cluster.objects.all()
  231. child_model = VirtualMachine
  232. table = tables.VirtualMachineTable
  233. filterset = filtersets.VirtualMachineFilterSet
  234. filterset_form = forms.VirtualMachineFilterForm
  235. actions = (EditObject, DeleteObject, BulkEdit)
  236. tab = ViewTab(
  237. label=_('Virtual Machines'),
  238. badge=lambda obj: obj.virtual_machines.count(),
  239. permission='virtualization.view_virtualmachine',
  240. weight=500
  241. )
  242. def get_children(self, request, parent):
  243. return VirtualMachine.objects.restrict(request.user, 'view').filter(cluster=parent)
  244. @register_model_view(Cluster, 'devices')
  245. class ClusterDevicesView(generic.ObjectChildrenView):
  246. queryset = Cluster.objects.all()
  247. child_model = Device
  248. table = DeviceTable
  249. filterset = DeviceFilterSet
  250. filterset_form = DeviceFilterForm
  251. actions = (EditObject, DeleteObject, BulkEdit)
  252. tab = ViewTab(
  253. label=_('Devices'),
  254. badge=lambda obj: obj.devices.count(),
  255. permission='virtualization.view_virtualmachine',
  256. weight=600
  257. )
  258. def get_children(self, request, parent):
  259. return Device.objects.restrict(request.user, 'view').filter(cluster=parent)
  260. @register_model_view(Cluster, 'add', detail=False)
  261. @register_model_view(Cluster, 'edit')
  262. class ClusterEditView(generic.ObjectEditView):
  263. queryset = Cluster.objects.all()
  264. form = forms.ClusterForm
  265. @register_model_view(Cluster, 'delete')
  266. class ClusterDeleteView(generic.ObjectDeleteView):
  267. queryset = Cluster.objects.all()
  268. @register_model_view(Cluster, 'bulk_import', path='import', detail=False)
  269. class ClusterBulkImportView(generic.BulkImportView):
  270. queryset = Cluster.objects.all()
  271. model_form = forms.ClusterImportForm
  272. @register_model_view(Cluster, 'bulk_edit', path='edit', detail=False)
  273. class ClusterBulkEditView(generic.BulkEditView):
  274. queryset = Cluster.objects.all()
  275. filterset = filtersets.ClusterFilterSet
  276. table = tables.ClusterTable
  277. form = forms.ClusterBulkEditForm
  278. @register_model_view(Cluster, 'bulk_rename', path='rename', detail=False)
  279. class ClusterBulkRenameView(generic.BulkRenameView):
  280. queryset = Cluster.objects.all()
  281. filterset = filtersets.ClusterFilterSet
  282. @register_model_view(Cluster, 'bulk_delete', path='delete', detail=False)
  283. class ClusterBulkDeleteView(generic.BulkDeleteView):
  284. queryset = Cluster.objects.all()
  285. filterset = filtersets.ClusterFilterSet
  286. table = tables.ClusterTable
  287. @register_model_view(Cluster, 'add_devices', path='devices/add')
  288. class ClusterAddDevicesView(generic.ObjectEditView):
  289. queryset = Cluster.objects.all()
  290. form = forms.ClusterAddDevicesForm
  291. template_name = 'virtualization/cluster_add_devices.html'
  292. def get(self, request, pk):
  293. cluster = get_object_or_404(self.queryset, pk=pk)
  294. form = self.form(cluster, initial=request.GET)
  295. return render(request, self.template_name, {
  296. 'cluster': cluster,
  297. 'form': form,
  298. 'return_url': reverse('virtualization:cluster', kwargs={'pk': pk}),
  299. })
  300. def post(self, request, pk):
  301. cluster = get_object_or_404(self.queryset, pk=pk)
  302. form = self.form(cluster, request.POST)
  303. if form.is_valid():
  304. device_pks = form.cleaned_data['devices']
  305. with transaction.atomic(using=router.db_for_write(Device)):
  306. # Assign the selected Devices to the Cluster
  307. for device in Device.objects.filter(pk__in=device_pks):
  308. device.snapshot()
  309. device.cluster = cluster
  310. device.save()
  311. messages.success(request, _("Added {count} devices to cluster {cluster}").format(
  312. count=len(device_pks),
  313. cluster=cluster
  314. ))
  315. return redirect(cluster.get_absolute_url())
  316. return render(request, self.template_name, {
  317. 'cluster': cluster,
  318. 'form': form,
  319. 'return_url': cluster.get_absolute_url(),
  320. })
  321. #
  322. # Virtual machine types
  323. #
  324. @register_model_view(VirtualMachineType, 'list', path='', detail=False)
  325. class VirtualMachineTypeListView(generic.ObjectListView):
  326. queryset = VirtualMachineType.objects.all()
  327. filterset = filtersets.VirtualMachineTypeFilterSet
  328. filterset_form = forms.VirtualMachineTypeFilterForm
  329. table = tables.VirtualMachineTypeTable
  330. @register_model_view(VirtualMachineType)
  331. class VirtualMachineTypeView(GetRelatedModelsMixin, generic.ObjectView):
  332. queryset = VirtualMachineType.objects.all()
  333. layout = layout.SimpleLayout(
  334. left_panels=[
  335. panels.VirtualMachineTypePanel(),
  336. TagsPanel(),
  337. CommentsPanel(),
  338. ],
  339. right_panels=[
  340. RelatedObjectsPanel(),
  341. CustomFieldsPanel(),
  342. ImageAttachmentsPanel(),
  343. ],
  344. )
  345. def get_extra_context(self, request, instance):
  346. return {
  347. 'related_models': self.get_related_models(request, instance),
  348. }
  349. @register_model_view(VirtualMachineType, 'add', detail=False)
  350. @register_model_view(VirtualMachineType, 'edit')
  351. class VirtualMachineTypeEditView(generic.ObjectEditView):
  352. queryset = VirtualMachineType.objects.all()
  353. form = forms.VirtualMachineTypeForm
  354. @register_model_view(VirtualMachineType, 'delete')
  355. class VirtualMachineTypeDeleteView(generic.ObjectDeleteView):
  356. queryset = VirtualMachineType.objects.all()
  357. @register_model_view(VirtualMachineType, 'bulk_import', path='import', detail=False)
  358. class VirtualMachineTypeBulkImportView(generic.BulkImportView):
  359. queryset = VirtualMachineType.objects.all()
  360. model_form = forms.VirtualMachineTypeImportForm
  361. @register_model_view(VirtualMachineType, 'bulk_edit', path='edit', detail=False)
  362. class VirtualMachineTypeBulkEditView(generic.BulkEditView):
  363. queryset = VirtualMachineType.objects.all()
  364. filterset = filtersets.VirtualMachineTypeFilterSet
  365. table = tables.VirtualMachineTypeTable
  366. form = forms.VirtualMachineTypeBulkEditForm
  367. @register_model_view(VirtualMachineType, 'bulk_rename', path='rename', detail=False)
  368. class VirtualMachineTypeBulkRenameView(generic.BulkRenameView):
  369. queryset = VirtualMachineType.objects.all()
  370. filterset = filtersets.VirtualMachineTypeFilterSet
  371. @register_model_view(VirtualMachineType, 'bulk_delete', path='delete', detail=False)
  372. class VirtualMachineTypeBulkDeleteView(generic.BulkDeleteView):
  373. queryset = VirtualMachineType.objects.all()
  374. filterset = filtersets.VirtualMachineTypeFilterSet
  375. table = tables.VirtualMachineTypeTable
  376. #
  377. # Virtual machines
  378. #
  379. @register_model_view(VirtualMachine, 'list', path='', detail=False)
  380. class VirtualMachineListView(generic.ObjectListView):
  381. queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
  382. filterset = filtersets.VirtualMachineFilterSet
  383. filterset_form = forms.VirtualMachineFilterForm
  384. table = tables.VirtualMachineTable
  385. actions = (AddObject, BulkImport, BulkExport, BulkAddComponents, BulkEdit, BulkRename, BulkDelete)
  386. @register_model_view(VirtualMachine)
  387. class VirtualMachineView(generic.ObjectView):
  388. queryset = VirtualMachine.objects.all()
  389. layout = layout.SimpleLayout(
  390. left_panels=[
  391. panels.VirtualMachinePanel(),
  392. CustomFieldsPanel(),
  393. TagsPanel(),
  394. CommentsPanel(),
  395. ],
  396. right_panels=[
  397. panels.VirtualMachinePlacementPanel(),
  398. TemplatePanel('virtualization/panels/virtual_machine_resources.html'),
  399. ObjectsTablePanel(
  400. model='ipam.Service',
  401. title=_('Application Services'),
  402. filters={'virtual_machine_id': lambda ctx: ctx['object'].pk},
  403. exclude_columns=['parent'],
  404. actions=[
  405. actions.AddObject(
  406. 'ipam.Service',
  407. url_params={
  408. 'parent_content_type': lambda ctx: ContentType.objects.get_for_model(ctx['object']).pk,
  409. 'parent_object_id': lambda ctx: ctx['object'].pk,
  410. },
  411. ),
  412. ],
  413. ),
  414. ImageAttachmentsPanel(),
  415. ],
  416. bottom_panels=[
  417. ObjectsTablePanel(
  418. model='virtualization.VirtualDisk',
  419. filters={'virtual_machine_id': lambda ctx: ctx['object'].pk},
  420. exclude_columns=['virtual_machine'],
  421. actions=[
  422. actions.AddObject(
  423. 'virtualization.VirtualDisk', url_params={'virtual_machine': lambda ctx: ctx['object'].pk}
  424. ),
  425. ],
  426. ),
  427. ],
  428. )
  429. @register_model_view(VirtualMachine, 'interfaces')
  430. class VirtualMachineInterfacesView(generic.ObjectChildrenView):
  431. queryset = VirtualMachine.objects.all()
  432. child_model = VMInterface
  433. table = tables.VirtualMachineVMInterfaceTable
  434. filterset = filtersets.VMInterfaceFilterSet
  435. filterset_form = forms.VMInterfaceFilterForm
  436. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
  437. tab = ViewTab(
  438. label=_('Interfaces'),
  439. badge=lambda obj: obj.interface_count,
  440. permission='virtualization.view_vminterface',
  441. weight=500
  442. )
  443. def get_children(self, request, parent):
  444. return parent.interfaces.restrict(request.user, 'view').prefetch_related(
  445. Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
  446. 'tags',
  447. )
  448. @register_model_view(VirtualMachine, 'disks')
  449. class VirtualMachineVirtualDisksView(generic.ObjectChildrenView):
  450. queryset = VirtualMachine.objects.all()
  451. child_model = VirtualDisk
  452. table = tables.VirtualMachineVirtualDiskTable
  453. filterset = filtersets.VirtualDiskFilterSet
  454. filterset_form = forms.VirtualDiskFilterForm
  455. actions = (EditObject, DeleteObject, BulkEdit, BulkRename, BulkDelete)
  456. tab = ViewTab(
  457. label=_('Virtual Disks'),
  458. badge=lambda obj: obj.virtual_disk_count,
  459. permission='virtualization.view_virtualdisk',
  460. weight=500
  461. )
  462. def get_children(self, request, parent):
  463. return parent.virtualdisks.restrict(request.user, 'view').prefetch_related('tags')
  464. @register_model_view(VirtualMachine, 'configcontext', path='config-context')
  465. class VirtualMachineConfigContextView(ObjectConfigContextView):
  466. queryset = VirtualMachine.objects.all()
  467. base_template = 'virtualization/virtualmachine.html'
  468. tab = ViewTab(
  469. label=_('Config Context'),
  470. weight=2000
  471. )
  472. @register_model_view(VirtualMachine, 'render-config')
  473. class VirtualMachineRenderConfigView(ObjectRenderConfigView):
  474. queryset = VirtualMachine.objects.all()
  475. base_template = 'virtualization/virtualmachine/base.html'
  476. additional_permissions = ['virtualization.render_config_virtualmachine']
  477. tab = ViewTab(
  478. label=_('Render Config'),
  479. weight=2100,
  480. )
  481. @register_model_view(VirtualMachine, 'add', detail=False)
  482. @register_model_view(VirtualMachine, 'edit')
  483. class VirtualMachineEditView(generic.ObjectEditView):
  484. queryset = VirtualMachine.objects.all()
  485. form = forms.VirtualMachineForm
  486. @register_model_view(VirtualMachine, 'delete')
  487. class VirtualMachineDeleteView(generic.ObjectDeleteView):
  488. queryset = VirtualMachine.objects.all()
  489. @register_model_view(VirtualMachine, 'bulk_import', path='import', detail=False)
  490. class VirtualMachineBulkImportView(generic.BulkImportView):
  491. queryset = VirtualMachine.objects.all()
  492. model_form = forms.VirtualMachineImportForm
  493. @register_model_view(VirtualMachine, 'bulk_edit', path='edit', detail=False)
  494. class VirtualMachineBulkEditView(generic.BulkEditView):
  495. queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
  496. filterset = filtersets.VirtualMachineFilterSet
  497. table = tables.VirtualMachineTable
  498. form = forms.VirtualMachineBulkEditForm
  499. @register_model_view(VirtualMachine, 'bulk_rename', path='rename', detail=False)
  500. class VirtualMachineBulkRenameView(generic.BulkRenameView):
  501. queryset = VirtualMachine.objects.all()
  502. filterset = filtersets.VirtualMachineFilterSet
  503. @register_model_view(VirtualMachine, 'bulk_delete', path='delete', detail=False)
  504. class VirtualMachineBulkDeleteView(generic.BulkDeleteView):
  505. queryset = VirtualMachine.objects.prefetch_related('primary_ip4', 'primary_ip6')
  506. filterset = filtersets.VirtualMachineFilterSet
  507. table = tables.VirtualMachineTable
  508. #
  509. # VM interfaces
  510. #
  511. @register_model_view(VMInterface, 'list', path='', detail=False)
  512. class VMInterfaceListView(generic.ObjectListView):
  513. queryset = VMInterface.objects.all()
  514. filterset = filtersets.VMInterfaceFilterSet
  515. filterset_form = forms.VMInterfaceFilterForm
  516. table = tables.VMInterfaceTable
  517. @register_model_view(VMInterface)
  518. class VMInterfaceView(generic.ObjectView):
  519. template_name = 'generic/object.html'
  520. queryset = VMInterface.objects.all()
  521. layout = layout.SimpleLayout(
  522. breadcrumbs=[
  523. Breadcrumb(
  524. 'virtual_machine',
  525. url=object_view_url('virtualization:virtualmachine_interfaces'),
  526. ),
  527. ],
  528. left_panels=[
  529. panels.VMInterfacePanel(),
  530. TagsPanel(),
  531. ],
  532. right_panels=[
  533. CustomFieldsPanel(),
  534. panels.VMInterfaceAddressingPanel(),
  535. FHRPGroupAssignmentsPanel(),
  536. ],
  537. bottom_panels=[
  538. ObjectsTablePanel(
  539. model='ipam.IPaddress',
  540. filters={'vminterface_id': lambda ctx: ctx['object'].pk},
  541. exclude_columns=['assigned', 'assigned_object', 'assigned_object_parent'],
  542. actions=[
  543. actions.AddObject(
  544. 'ipam.IPaddress',
  545. url_params={
  546. 'virtual_machine': lambda ctx: ctx['object'].virtual_machine.pk,
  547. 'vminterface': lambda ctx: ctx['object'].pk,
  548. },
  549. ),
  550. ],
  551. ),
  552. ObjectsTablePanel(
  553. model='dcim.MACAddress',
  554. filters={'vminterface_id': lambda ctx: ctx['object'].pk},
  555. exclude_columns=['assigned_object', 'assigned_object_parent'],
  556. actions=[
  557. actions.AddObject(
  558. 'dcim.MACAddress', url_params={'vminterface': lambda ctx: ctx['object'].pk}
  559. ),
  560. ],
  561. ),
  562. ObjectsTablePanel(
  563. model='ipam.VLAN',
  564. title=_('Assigned VLANs'),
  565. filters={'vminterface_id': lambda ctx: ctx['object'].pk},
  566. ),
  567. ContextTablePanel('vlan_translation_table', title=_('VLAN Translation')),
  568. ContextTablePanel('child_interfaces_table', title=_('Child Interfaces')),
  569. ],
  570. )
  571. def get_extra_context(self, request, instance):
  572. # Get child interfaces
  573. child_interfaces = VMInterface.objects.restrict(request.user, 'view').filter(parent=instance)
  574. child_interfaces_tables = tables.VMInterfaceTable(
  575. child_interfaces,
  576. exclude=('virtual_machine',),
  577. orderable=False
  578. )
  579. child_interfaces_tables.configure(request)
  580. # Get VLAN translation rules
  581. vlan_translation_table = None
  582. if instance.vlan_translation_policy:
  583. vlan_translation_table = VLANTranslationRuleTable(
  584. data=instance.vlan_translation_policy.rules.all(),
  585. orderable=False
  586. )
  587. vlan_translation_table.configure(request)
  588. return {
  589. 'child_interfaces_table': child_interfaces_tables,
  590. 'vlan_translation_table': vlan_translation_table,
  591. }
  592. @register_model_view(VMInterface, 'add', detail=False)
  593. class VMInterfaceCreateView(generic.ComponentCreateView):
  594. queryset = VMInterface.objects.all()
  595. form = forms.VMInterfaceCreateForm
  596. model_form = forms.VMInterfaceForm
  597. @register_model_view(VMInterface, 'edit')
  598. class VMInterfaceEditView(generic.ObjectEditView):
  599. queryset = VMInterface.objects.all()
  600. form = forms.VMInterfaceForm
  601. @register_model_view(VMInterface, 'delete')
  602. class VMInterfaceDeleteView(generic.ObjectDeleteView):
  603. queryset = VMInterface.objects.all()
  604. @register_model_view(VMInterface, 'bulk_import', path='import', detail=False)
  605. class VMInterfaceBulkImportView(generic.BulkImportView):
  606. queryset = VMInterface.objects.all()
  607. model_form = forms.VMInterfaceImportForm
  608. @register_model_view(VMInterface, 'bulk_edit', path='edit', detail=False)
  609. class VMInterfaceBulkEditView(generic.BulkEditView):
  610. queryset = VMInterface.objects.all()
  611. filterset = filtersets.VMInterfaceFilterSet
  612. table = tables.VMInterfaceTable
  613. form = forms.VMInterfaceBulkEditForm
  614. @register_model_view(VMInterface, 'bulk_rename', path='rename', detail=False)
  615. class VMInterfaceBulkRenameView(generic.BulkRenameView):
  616. queryset = VMInterface.objects.all()
  617. filterset = filtersets.VMInterfaceFilterSet
  618. @register_model_view(VMInterface, 'bulk_delete', path='delete', detail=False)
  619. class VMInterfaceBulkDeleteView(generic.BulkDeleteView):
  620. # Ensure child interfaces are deleted prior to their parents
  621. queryset = VMInterface.objects.order_by('virtual_machine', 'parent', CollateAsChar('_name'))
  622. filterset = filtersets.VMInterfaceFilterSet
  623. table = tables.VMInterfaceTable
  624. #
  625. # Virtual disks
  626. #
  627. @register_model_view(VirtualDisk, 'list', path='', detail=False)
  628. class VirtualDiskListView(generic.ObjectListView):
  629. queryset = VirtualDisk.objects.all()
  630. filterset = filtersets.VirtualDiskFilterSet
  631. filterset_form = forms.VirtualDiskFilterForm
  632. table = tables.VirtualDiskTable
  633. @register_model_view(VirtualDisk)
  634. class VirtualDiskView(generic.ObjectView):
  635. template_name = 'generic/object.html'
  636. queryset = VirtualDisk.objects.all()
  637. layout = layout.SimpleLayout(
  638. breadcrumbs=[
  639. Breadcrumb(
  640. 'virtual_machine',
  641. url=object_view_url('virtualization:virtualmachine_disks'),
  642. ),
  643. ],
  644. left_panels=[
  645. panels.VirtualDiskPanel(),
  646. TagsPanel(),
  647. ],
  648. right_panels=[
  649. CustomFieldsPanel(),
  650. ],
  651. )
  652. @register_model_view(VirtualDisk, 'add', detail=False)
  653. class VirtualDiskCreateView(generic.ComponentCreateView):
  654. queryset = VirtualDisk.objects.all()
  655. form = forms.VirtualDiskCreateForm
  656. model_form = forms.VirtualDiskForm
  657. @register_model_view(VirtualDisk, 'edit')
  658. class VirtualDiskEditView(generic.ObjectEditView):
  659. queryset = VirtualDisk.objects.all()
  660. form = forms.VirtualDiskForm
  661. @register_model_view(VirtualDisk, 'delete')
  662. class VirtualDiskDeleteView(generic.ObjectDeleteView):
  663. queryset = VirtualDisk.objects.all()
  664. @register_model_view(VirtualDisk, 'bulk_import', path='import', detail=False)
  665. class VirtualDiskBulkImportView(generic.BulkImportView):
  666. queryset = VirtualDisk.objects.all()
  667. model_form = forms.VirtualDiskImportForm
  668. @register_model_view(VirtualDisk, 'bulk_edit', path='edit', detail=False)
  669. class VirtualDiskBulkEditView(generic.BulkEditView):
  670. queryset = VirtualDisk.objects.all()
  671. filterset = filtersets.VirtualDiskFilterSet
  672. table = tables.VirtualDiskTable
  673. form = forms.VirtualDiskBulkEditForm
  674. @register_model_view(VirtualDisk, 'bulk_rename', path='rename', detail=False)
  675. class VirtualDiskBulkRenameView(generic.BulkRenameView):
  676. queryset = VirtualDisk.objects.all()
  677. filterset = filtersets.VirtualDiskFilterSet
  678. @register_model_view(VirtualDisk, 'bulk_delete', path='delete', detail=False)
  679. class VirtualDiskBulkDeleteView(generic.BulkDeleteView):
  680. queryset = VirtualDisk.objects.all()
  681. filterset = filtersets.VirtualDiskFilterSet
  682. table = tables.VirtualDiskTable
  683. #
  684. # Bulk Device component creation
  685. #
  686. class VirtualMachineBulkAddInterfaceView(generic.BulkComponentCreateView):
  687. parent_model = VirtualMachine
  688. parent_field = 'virtual_machine'
  689. form = forms.VMInterfaceBulkCreateForm
  690. queryset = VMInterface.objects.all()
  691. model_form = forms.VMInterfaceForm
  692. filterset = filtersets.VirtualMachineFilterSet
  693. table = tables.VirtualMachineTable
  694. default_return_url = 'virtualization:virtualmachine_list'
  695. def get_required_permission(self):
  696. return 'virtualization.add_vminterface'
  697. class VirtualMachineBulkAddVirtualDiskView(generic.BulkComponentCreateView):
  698. parent_model = VirtualMachine
  699. parent_field = 'virtual_machine'
  700. form = forms.VirtualDiskBulkCreateForm
  701. queryset = VirtualDisk.objects.all()
  702. model_form = forms.VirtualDiskForm
  703. filterset = filtersets.VirtualMachineFilterSet
  704. table = tables.VirtualMachineTable
  705. default_return_url = 'virtualization:virtualmachine_list'
  706. def get_required_permission(self):
  707. return 'virtualization.add_virtualdisk'