views.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. from django.contrib import messages
  2. from django.db import transaction
  3. from django.db.models import Prefetch
  4. from django.shortcuts import get_object_or_404, redirect, render
  5. from django.urls import reverse
  6. from dcim.filtersets import DeviceFilterSet
  7. from dcim.models import Device
  8. from dcim.tables import DeviceTable
  9. from extras.views import ObjectConfigContextView
  10. from ipam.models import IPAddress, Service
  11. from ipam.tables import AssignedIPAddressesTable, InterfaceVLANTable
  12. from netbox.views import generic
  13. from utilities.utils import count_related
  14. from . import filtersets, forms, tables
  15. from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
  16. #
  17. # Cluster types
  18. #
  19. class ClusterTypeListView(generic.ObjectListView):
  20. queryset = ClusterType.objects.annotate(
  21. cluster_count=count_related(Cluster, 'type')
  22. )
  23. filterset = filtersets.ClusterTypeFilterSet
  24. filterset_form = forms.ClusterTypeFilterForm
  25. table = tables.ClusterTypeTable
  26. class ClusterTypeView(generic.ObjectView):
  27. queryset = ClusterType.objects.all()
  28. def get_extra_context(self, request, instance):
  29. clusters = Cluster.objects.restrict(request.user, 'view').filter(
  30. type=instance
  31. ).annotate(
  32. device_count=count_related(Device, 'cluster'),
  33. vm_count=count_related(VirtualMachine, 'cluster')
  34. )
  35. clusters_table = tables.ClusterTable(clusters, user=request.user, exclude=('type',))
  36. clusters_table.configure(request)
  37. return {
  38. 'clusters_table': clusters_table,
  39. }
  40. class ClusterTypeEditView(generic.ObjectEditView):
  41. queryset = ClusterType.objects.all()
  42. form = forms.ClusterTypeForm
  43. class ClusterTypeDeleteView(generic.ObjectDeleteView):
  44. queryset = ClusterType.objects.all()
  45. class ClusterTypeBulkImportView(generic.BulkImportView):
  46. queryset = ClusterType.objects.all()
  47. model_form = forms.ClusterTypeCSVForm
  48. table = tables.ClusterTypeTable
  49. class ClusterTypeBulkEditView(generic.BulkEditView):
  50. queryset = ClusterType.objects.annotate(
  51. cluster_count=count_related(Cluster, 'type')
  52. )
  53. filterset = filtersets.ClusterTypeFilterSet
  54. table = tables.ClusterTypeTable
  55. form = forms.ClusterTypeBulkEditForm
  56. class ClusterTypeBulkDeleteView(generic.BulkDeleteView):
  57. queryset = ClusterType.objects.annotate(
  58. cluster_count=count_related(Cluster, 'type')
  59. )
  60. table = tables.ClusterTypeTable
  61. #
  62. # Cluster groups
  63. #
  64. class ClusterGroupListView(generic.ObjectListView):
  65. queryset = ClusterGroup.objects.annotate(
  66. cluster_count=count_related(Cluster, 'group')
  67. )
  68. filterset = filtersets.ClusterGroupFilterSet
  69. filterset_form = forms.ClusterGroupFilterForm
  70. table = tables.ClusterGroupTable
  71. class ClusterGroupView(generic.ObjectView):
  72. queryset = ClusterGroup.objects.all()
  73. def get_extra_context(self, request, instance):
  74. clusters = Cluster.objects.restrict(request.user, 'view').filter(
  75. group=instance
  76. ).annotate(
  77. device_count=count_related(Device, 'cluster'),
  78. vm_count=count_related(VirtualMachine, 'cluster')
  79. )
  80. clusters_table = tables.ClusterTable(clusters, user=request.user, exclude=('group',))
  81. clusters_table.configure(request)
  82. return {
  83. 'clusters_table': clusters_table,
  84. }
  85. class ClusterGroupEditView(generic.ObjectEditView):
  86. queryset = ClusterGroup.objects.all()
  87. form = forms.ClusterGroupForm
  88. class ClusterGroupDeleteView(generic.ObjectDeleteView):
  89. queryset = ClusterGroup.objects.all()
  90. class ClusterGroupBulkImportView(generic.BulkImportView):
  91. queryset = ClusterGroup.objects.annotate(
  92. cluster_count=count_related(Cluster, 'group')
  93. )
  94. model_form = forms.ClusterGroupCSVForm
  95. table = tables.ClusterGroupTable
  96. class ClusterGroupBulkEditView(generic.BulkEditView):
  97. queryset = ClusterGroup.objects.annotate(
  98. cluster_count=count_related(Cluster, 'group')
  99. )
  100. filterset = filtersets.ClusterGroupFilterSet
  101. table = tables.ClusterGroupTable
  102. form = forms.ClusterGroupBulkEditForm
  103. class ClusterGroupBulkDeleteView(generic.BulkDeleteView):
  104. queryset = ClusterGroup.objects.annotate(
  105. cluster_count=count_related(Cluster, 'group')
  106. )
  107. table = tables.ClusterGroupTable
  108. #
  109. # Clusters
  110. #
  111. class ClusterListView(generic.ObjectListView):
  112. permission_required = 'virtualization.view_cluster'
  113. queryset = Cluster.objects.annotate(
  114. device_count=count_related(Device, 'cluster'),
  115. vm_count=count_related(VirtualMachine, 'cluster')
  116. )
  117. table = tables.ClusterTable
  118. filterset = filtersets.ClusterFilterSet
  119. filterset_form = forms.ClusterFilterForm
  120. class ClusterView(generic.ObjectView):
  121. queryset = Cluster.objects.all()
  122. class ClusterVirtualMachinesView(generic.ObjectChildrenView):
  123. queryset = Cluster.objects.all()
  124. child_model = VirtualMachine
  125. table = tables.VirtualMachineTable
  126. filterset = filtersets.VirtualMachineFilterSet
  127. template_name = 'virtualization/cluster/virtual_machines.html'
  128. def get_children(self, request, parent):
  129. return VirtualMachine.objects.restrict(request.user, 'view').filter(cluster=parent)
  130. def get_extra_context(self, request, instance):
  131. return {
  132. 'active_tab': 'virtual-machines',
  133. }
  134. class ClusterDevicesView(generic.ObjectChildrenView):
  135. queryset = Cluster.objects.all()
  136. child_model = Device
  137. table = DeviceTable
  138. filterset = DeviceFilterSet
  139. template_name = 'virtualization/cluster/devices.html'
  140. def get_children(self, request, parent):
  141. return Device.objects.restrict(request.user, 'view').filter(cluster=parent)
  142. def get_extra_context(self, request, instance):
  143. return {
  144. 'active_tab': 'devices',
  145. }
  146. class ClusterEditView(generic.ObjectEditView):
  147. queryset = Cluster.objects.all()
  148. form = forms.ClusterForm
  149. class ClusterDeleteView(generic.ObjectDeleteView):
  150. queryset = Cluster.objects.all()
  151. class ClusterBulkImportView(generic.BulkImportView):
  152. queryset = Cluster.objects.all()
  153. model_form = forms.ClusterCSVForm
  154. table = tables.ClusterTable
  155. class ClusterBulkEditView(generic.BulkEditView):
  156. queryset = Cluster.objects.prefetch_related('type', 'group', 'site')
  157. filterset = filtersets.ClusterFilterSet
  158. table = tables.ClusterTable
  159. form = forms.ClusterBulkEditForm
  160. class ClusterBulkDeleteView(generic.BulkDeleteView):
  161. queryset = Cluster.objects.prefetch_related('type', 'group', 'site')
  162. filterset = filtersets.ClusterFilterSet
  163. table = tables.ClusterTable
  164. class ClusterAddDevicesView(generic.ObjectEditView):
  165. queryset = Cluster.objects.all()
  166. form = forms.ClusterAddDevicesForm
  167. template_name = 'virtualization/cluster_add_devices.html'
  168. def get(self, request, pk):
  169. cluster = get_object_or_404(self.queryset, pk=pk)
  170. form = self.form(cluster, initial=request.GET)
  171. return render(request, self.template_name, {
  172. 'cluster': cluster,
  173. 'form': form,
  174. 'return_url': reverse('virtualization:cluster', kwargs={'pk': pk}),
  175. })
  176. def post(self, request, pk):
  177. cluster = get_object_or_404(self.queryset, pk=pk)
  178. form = self.form(cluster, request.POST)
  179. if form.is_valid():
  180. device_pks = form.cleaned_data['devices']
  181. with transaction.atomic():
  182. # Assign the selected Devices to the Cluster
  183. for device in Device.objects.filter(pk__in=device_pks):
  184. device.cluster = cluster
  185. device.save()
  186. messages.success(request, "Added {} devices to cluster {}".format(
  187. len(device_pks), cluster
  188. ))
  189. return redirect(cluster.get_absolute_url())
  190. return render(request, self.template_name, {
  191. 'cluster': cluster,
  192. 'form': form,
  193. 'return_url': cluster.get_absolute_url(),
  194. })
  195. class ClusterRemoveDevicesView(generic.ObjectEditView):
  196. queryset = Cluster.objects.all()
  197. form = forms.ClusterRemoveDevicesForm
  198. template_name = 'generic/bulk_remove.html'
  199. def post(self, request, pk):
  200. cluster = get_object_or_404(self.queryset, pk=pk)
  201. if '_confirm' in request.POST:
  202. form = self.form(request.POST)
  203. if form.is_valid():
  204. device_pks = form.cleaned_data['pk']
  205. with transaction.atomic():
  206. # Remove the selected Devices from the Cluster
  207. for device in Device.objects.filter(pk__in=device_pks):
  208. device.cluster = None
  209. device.save()
  210. messages.success(request, "Removed {} devices from cluster {}".format(
  211. len(device_pks), cluster
  212. ))
  213. return redirect(cluster.get_absolute_url())
  214. else:
  215. form = self.form(initial={'pk': request.POST.getlist('pk')})
  216. selected_objects = Device.objects.filter(pk__in=form.initial['pk'])
  217. device_table = DeviceTable(list(selected_objects), orderable=False)
  218. return render(request, self.template_name, {
  219. 'form': form,
  220. 'parent_obj': cluster,
  221. 'table': device_table,
  222. 'obj_type_plural': 'devices',
  223. 'return_url': cluster.get_absolute_url(),
  224. })
  225. #
  226. # Virtual machines
  227. #
  228. class VirtualMachineListView(generic.ObjectListView):
  229. queryset = VirtualMachine.objects.all()
  230. filterset = filtersets.VirtualMachineFilterSet
  231. filterset_form = forms.VirtualMachineFilterForm
  232. table = tables.VirtualMachineTable
  233. template_name = 'virtualization/virtualmachine_list.html'
  234. class VirtualMachineView(generic.ObjectView):
  235. queryset = VirtualMachine.objects.prefetch_related('tenant__group')
  236. def get_extra_context(self, request, instance):
  237. # Interfaces
  238. vminterfaces = VMInterface.objects.restrict(request.user, 'view').filter(
  239. virtual_machine=instance
  240. ).prefetch_related(
  241. Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user))
  242. )
  243. vminterface_table = tables.VirtualMachineVMInterfaceTable(vminterfaces, user=request.user, orderable=False)
  244. if request.user.has_perm('virtualization.change_vminterface') or \
  245. request.user.has_perm('virtualization.delete_vminterface'):
  246. vminterface_table.columns.show('pk')
  247. # Services
  248. services = Service.objects.restrict(request.user, 'view').filter(
  249. virtual_machine=instance
  250. ).prefetch_related(
  251. Prefetch('ipaddresses', queryset=IPAddress.objects.restrict(request.user))
  252. )
  253. return {
  254. 'vminterface_table': vminterface_table,
  255. 'services': services,
  256. }
  257. class VirtualMachineInterfacesView(generic.ObjectChildrenView):
  258. queryset = VirtualMachine.objects.all()
  259. child_model = VMInterface
  260. table = tables.VirtualMachineVMInterfaceTable
  261. filterset = filtersets.VMInterfaceFilterSet
  262. template_name = 'virtualization/virtualmachine/interfaces.html'
  263. def get_children(self, request, parent):
  264. return parent.interfaces.restrict(request.user, 'view').prefetch_related(
  265. Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
  266. 'tags',
  267. )
  268. def get_extra_context(self, request, instance):
  269. return {
  270. 'active_tab': 'interfaces',
  271. }
  272. class VirtualMachineConfigContextView(ObjectConfigContextView):
  273. queryset = VirtualMachine.objects.annotate_config_context_data()
  274. base_template = 'virtualization/virtualmachine.html'
  275. class VirtualMachineEditView(generic.ObjectEditView):
  276. queryset = VirtualMachine.objects.all()
  277. form = forms.VirtualMachineForm
  278. class VirtualMachineDeleteView(generic.ObjectDeleteView):
  279. queryset = VirtualMachine.objects.all()
  280. class VirtualMachineBulkImportView(generic.BulkImportView):
  281. queryset = VirtualMachine.objects.all()
  282. model_form = forms.VirtualMachineCSVForm
  283. table = tables.VirtualMachineTable
  284. class VirtualMachineBulkEditView(generic.BulkEditView):
  285. queryset = VirtualMachine.objects.prefetch_related('cluster', 'tenant', 'role')
  286. filterset = filtersets.VirtualMachineFilterSet
  287. table = tables.VirtualMachineTable
  288. form = forms.VirtualMachineBulkEditForm
  289. class VirtualMachineBulkDeleteView(generic.BulkDeleteView):
  290. queryset = VirtualMachine.objects.prefetch_related('cluster', 'tenant', 'role')
  291. filterset = filtersets.VirtualMachineFilterSet
  292. table = tables.VirtualMachineTable
  293. #
  294. # VM interfaces
  295. #
  296. class VMInterfaceListView(generic.ObjectListView):
  297. queryset = VMInterface.objects.all()
  298. filterset = filtersets.VMInterfaceFilterSet
  299. filterset_form = forms.VMInterfaceFilterForm
  300. table = tables.VMInterfaceTable
  301. actions = ('import', 'export', 'bulk_edit', 'bulk_delete')
  302. class VMInterfaceView(generic.ObjectView):
  303. queryset = VMInterface.objects.all()
  304. def get_extra_context(self, request, instance):
  305. # Get assigned IP addresses
  306. ipaddress_table = AssignedIPAddressesTable(
  307. data=instance.ip_addresses.restrict(request.user, 'view').prefetch_related('vrf', 'tenant'),
  308. orderable=False
  309. )
  310. # Get child interfaces
  311. child_interfaces = VMInterface.objects.restrict(request.user, 'view').filter(parent=instance)
  312. child_interfaces_tables = tables.VMInterfaceTable(
  313. child_interfaces,
  314. exclude=('virtual_machine',),
  315. orderable=False
  316. )
  317. # Get assigned VLANs and annotate whether each is tagged or untagged
  318. vlans = []
  319. if instance.untagged_vlan is not None:
  320. vlans.append(instance.untagged_vlan)
  321. vlans[0].tagged = False
  322. for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
  323. vlan.tagged = True
  324. vlans.append(vlan)
  325. vlan_table = InterfaceVLANTable(
  326. interface=instance,
  327. data=vlans,
  328. orderable=False
  329. )
  330. return {
  331. 'ipaddress_table': ipaddress_table,
  332. 'child_interfaces_table': child_interfaces_tables,
  333. 'vlan_table': vlan_table,
  334. }
  335. class VMInterfaceCreateView(generic.ComponentCreateView):
  336. queryset = VMInterface.objects.all()
  337. form = forms.VMInterfaceCreateForm
  338. model_form = forms.VMInterfaceForm
  339. patterned_fields = ('name',)
  340. class VMInterfaceEditView(generic.ObjectEditView):
  341. queryset = VMInterface.objects.all()
  342. form = forms.VMInterfaceForm
  343. template_name = 'virtualization/vminterface_edit.html'
  344. class VMInterfaceDeleteView(generic.ObjectDeleteView):
  345. queryset = VMInterface.objects.all()
  346. class VMInterfaceBulkImportView(generic.BulkImportView):
  347. queryset = VMInterface.objects.all()
  348. model_form = forms.VMInterfaceCSVForm
  349. table = tables.VMInterfaceTable
  350. class VMInterfaceBulkEditView(generic.BulkEditView):
  351. queryset = VMInterface.objects.all()
  352. table = tables.VMInterfaceTable
  353. form = forms.VMInterfaceBulkEditForm
  354. class VMInterfaceBulkRenameView(generic.BulkRenameView):
  355. queryset = VMInterface.objects.all()
  356. form = forms.VMInterfaceBulkRenameForm
  357. class VMInterfaceBulkDeleteView(generic.BulkDeleteView):
  358. queryset = VMInterface.objects.all()
  359. table = tables.VMInterfaceTable
  360. #
  361. # Bulk Device component creation
  362. #
  363. class VirtualMachineBulkAddInterfaceView(generic.BulkComponentCreateView):
  364. parent_model = VirtualMachine
  365. parent_field = 'virtual_machine'
  366. form = forms.VMInterfaceBulkCreateForm
  367. queryset = VMInterface.objects.all()
  368. model_form = forms.VMInterfaceForm
  369. filterset = filtersets.VirtualMachineFilterSet
  370. table = tables.VirtualMachineTable
  371. default_return_url = 'virtualization:virtualmachine_list'
  372. def get_required_permission(self):
  373. return f'virtualization.add_vminterface'