views.py 16 KB

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