views.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. from django.contrib import messages
  2. from django.db import transaction
  3. from django.db.models import Count, Prefetch
  4. from django.shortcuts import get_object_or_404, redirect, render
  5. from django.urls import reverse
  6. from dcim.models import Device
  7. from dcim.tables import DeviceTable
  8. from extras.views import ObjectConfigContextView
  9. from ipam.models import IPAddress, Service
  10. from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable
  11. from netbox.views import generic
  12. from secrets.models import Secret
  13. from utilities.utils import get_subquery
  14. from . import filters, 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(cluster_count=Count('clusters')).order_by(*ClusterType._meta.ordering)
  21. table = tables.ClusterTypeTable
  22. class ClusterTypeEditView(generic.ObjectEditView):
  23. queryset = ClusterType.objects.all()
  24. model_form = forms.ClusterTypeForm
  25. class ClusterTypeDeleteView(generic.ObjectDeleteView):
  26. queryset = ClusterType.objects.all()
  27. class ClusterTypeBulkImportView(generic.BulkImportView):
  28. queryset = ClusterType.objects.all()
  29. model_form = forms.ClusterTypeCSVForm
  30. table = tables.ClusterTypeTable
  31. class ClusterTypeBulkDeleteView(generic.BulkDeleteView):
  32. queryset = ClusterType.objects.annotate(cluster_count=Count('clusters')).order_by(*ClusterType._meta.ordering)
  33. table = tables.ClusterTypeTable
  34. #
  35. # Cluster groups
  36. #
  37. class ClusterGroupListView(generic.ObjectListView):
  38. queryset = ClusterGroup.objects.annotate(cluster_count=Count('clusters')).order_by(*ClusterGroup._meta.ordering)
  39. table = tables.ClusterGroupTable
  40. class ClusterGroupEditView(generic.ObjectEditView):
  41. queryset = ClusterGroup.objects.all()
  42. model_form = forms.ClusterGroupForm
  43. class ClusterGroupDeleteView(generic.ObjectDeleteView):
  44. queryset = ClusterGroup.objects.all()
  45. class ClusterGroupBulkImportView(generic.BulkImportView):
  46. queryset = ClusterGroup.objects.all()
  47. model_form = forms.ClusterGroupCSVForm
  48. table = tables.ClusterGroupTable
  49. class ClusterGroupBulkDeleteView(generic.BulkDeleteView):
  50. queryset = ClusterGroup.objects.annotate(cluster_count=Count('clusters')).order_by(*ClusterGroup._meta.ordering)
  51. table = tables.ClusterGroupTable
  52. #
  53. # Clusters
  54. #
  55. class ClusterListView(generic.ObjectListView):
  56. permission_required = 'virtualization.view_cluster'
  57. queryset = Cluster.objects.annotate(
  58. device_count=get_subquery(Device, 'cluster'),
  59. vm_count=get_subquery(VirtualMachine, 'cluster')
  60. )
  61. table = tables.ClusterTable
  62. filterset = filters.ClusterFilterSet
  63. filterset_form = forms.ClusterFilterForm
  64. class ClusterView(generic.ObjectView):
  65. queryset = Cluster.objects.all()
  66. def get_extra_context(self, request, instance):
  67. devices = Device.objects.restrict(request.user, 'view').filter(cluster=instance).prefetch_related(
  68. 'site', 'rack', 'tenant', 'device_type__manufacturer'
  69. )
  70. device_table = DeviceTable(list(devices), orderable=False)
  71. if request.user.has_perm('virtualization.change_cluster'):
  72. device_table.columns.show('pk')
  73. return {
  74. 'device_table': device_table,
  75. }
  76. class ClusterEditView(generic.ObjectEditView):
  77. template_name = 'virtualization/cluster_edit.html'
  78. queryset = Cluster.objects.all()
  79. model_form = forms.ClusterForm
  80. class ClusterDeleteView(generic.ObjectDeleteView):
  81. queryset = Cluster.objects.all()
  82. class ClusterBulkImportView(generic.BulkImportView):
  83. queryset = Cluster.objects.all()
  84. model_form = forms.ClusterCSVForm
  85. table = tables.ClusterTable
  86. class ClusterBulkEditView(generic.BulkEditView):
  87. queryset = Cluster.objects.prefetch_related('type', 'group', 'site')
  88. filterset = filters.ClusterFilterSet
  89. table = tables.ClusterTable
  90. form = forms.ClusterBulkEditForm
  91. class ClusterBulkDeleteView(generic.BulkDeleteView):
  92. queryset = Cluster.objects.prefetch_related('type', 'group', 'site')
  93. filterset = filters.ClusterFilterSet
  94. table = tables.ClusterTable
  95. class ClusterAddDevicesView(generic.ObjectEditView):
  96. queryset = Cluster.objects.all()
  97. form = forms.ClusterAddDevicesForm
  98. template_name = 'virtualization/cluster_add_devices.html'
  99. def get(self, request, pk):
  100. cluster = get_object_or_404(self.queryset, pk=pk)
  101. form = self.form(cluster, initial=request.GET)
  102. return render(request, self.template_name, {
  103. 'cluster': cluster,
  104. 'form': form,
  105. 'return_url': reverse('virtualization:cluster', kwargs={'pk': pk}),
  106. })
  107. def post(self, request, pk):
  108. cluster = get_object_or_404(self.queryset, pk=pk)
  109. form = self.form(cluster, request.POST)
  110. if form.is_valid():
  111. device_pks = form.cleaned_data['devices']
  112. with transaction.atomic():
  113. # Assign the selected Devices to the Cluster
  114. for device in Device.objects.filter(pk__in=device_pks):
  115. device.cluster = cluster
  116. device.save()
  117. messages.success(request, "Added {} devices to cluster {}".format(
  118. len(device_pks), cluster
  119. ))
  120. return redirect(cluster.get_absolute_url())
  121. return render(request, self.template_name, {
  122. 'cluster': cluster,
  123. 'form': form,
  124. 'return_url': cluster.get_absolute_url(),
  125. })
  126. class ClusterRemoveDevicesView(generic.ObjectEditView):
  127. queryset = Cluster.objects.all()
  128. form = forms.ClusterRemoveDevicesForm
  129. template_name = 'generic/object_bulk_remove.html'
  130. def post(self, request, pk):
  131. cluster = get_object_or_404(self.queryset, pk=pk)
  132. if '_confirm' in request.POST:
  133. form = self.form(request.POST)
  134. if form.is_valid():
  135. device_pks = form.cleaned_data['pk']
  136. with transaction.atomic():
  137. # Remove the selected Devices from the Cluster
  138. for device in Device.objects.filter(pk__in=device_pks):
  139. device.cluster = None
  140. device.save()
  141. messages.success(request, "Removed {} devices from cluster {}".format(
  142. len(device_pks), cluster
  143. ))
  144. return redirect(cluster.get_absolute_url())
  145. else:
  146. form = self.form(initial={'pk': request.POST.getlist('pk')})
  147. selected_objects = Device.objects.filter(pk__in=form.initial['pk'])
  148. device_table = DeviceTable(list(selected_objects), orderable=False)
  149. return render(request, self.template_name, {
  150. 'form': form,
  151. 'parent_obj': cluster,
  152. 'table': device_table,
  153. 'obj_type_plural': 'devices',
  154. 'return_url': cluster.get_absolute_url(),
  155. })
  156. #
  157. # Virtual machines
  158. #
  159. class VirtualMachineListView(generic.ObjectListView):
  160. queryset = VirtualMachine.objects.all()
  161. filterset = filters.VirtualMachineFilterSet
  162. filterset_form = forms.VirtualMachineFilterForm
  163. table = tables.VirtualMachineDetailTable
  164. template_name = 'virtualization/virtualmachine_list.html'
  165. class VirtualMachineView(generic.ObjectView):
  166. queryset = VirtualMachine.objects.prefetch_related('tenant__group')
  167. def get_extra_context(self, request, instance):
  168. # Interfaces
  169. vminterfaces = VMInterface.objects.restrict(request.user, 'view').filter(
  170. virtual_machine=instance
  171. ).prefetch_related(
  172. Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user))
  173. )
  174. vminterface_table = tables.VirtualMachineVMInterfaceTable(vminterfaces, user=request.user, orderable=False)
  175. if request.user.has_perm('virtualization.change_vminterface') or \
  176. request.user.has_perm('virtualization.delete_vminterface'):
  177. vminterface_table.columns.show('pk')
  178. # Services
  179. services = Service.objects.restrict(request.user, 'view').filter(
  180. virtual_machine=instance
  181. ).prefetch_related(
  182. Prefetch('ipaddresses', queryset=IPAddress.objects.restrict(request.user))
  183. )
  184. # Secrets
  185. secrets = Secret.objects.restrict(request.user, 'view').filter(virtual_machine=instance)
  186. return {
  187. 'vminterface_table': vminterface_table,
  188. 'services': services,
  189. 'secrets': secrets,
  190. }
  191. class VirtualMachineConfigContextView(ObjectConfigContextView):
  192. queryset = VirtualMachine.objects.annotate_config_context_data()
  193. base_template = 'virtualization/virtualmachine.html'
  194. class VirtualMachineEditView(generic.ObjectEditView):
  195. queryset = VirtualMachine.objects.all()
  196. model_form = forms.VirtualMachineForm
  197. template_name = 'virtualization/virtualmachine_edit.html'
  198. class VirtualMachineDeleteView(generic.ObjectDeleteView):
  199. queryset = VirtualMachine.objects.all()
  200. class VirtualMachineBulkImportView(generic.BulkImportView):
  201. queryset = VirtualMachine.objects.all()
  202. model_form = forms.VirtualMachineCSVForm
  203. table = tables.VirtualMachineTable
  204. class VirtualMachineBulkEditView(generic.BulkEditView):
  205. queryset = VirtualMachine.objects.prefetch_related('cluster', 'tenant', 'role')
  206. filterset = filters.VirtualMachineFilterSet
  207. table = tables.VirtualMachineTable
  208. form = forms.VirtualMachineBulkEditForm
  209. class VirtualMachineBulkDeleteView(generic.BulkDeleteView):
  210. queryset = VirtualMachine.objects.prefetch_related('cluster', 'tenant', 'role')
  211. filterset = filters.VirtualMachineFilterSet
  212. table = tables.VirtualMachineTable
  213. #
  214. # VM interfaces
  215. #
  216. class VMInterfaceListView(generic.ObjectListView):
  217. queryset = VMInterface.objects.all()
  218. filterset = filters.VMInterfaceFilterSet
  219. filterset_form = forms.VMInterfaceFilterForm
  220. table = tables.VMInterfaceTable
  221. action_buttons = ('export',)
  222. class VMInterfaceView(generic.ObjectView):
  223. queryset = VMInterface.objects.all()
  224. def get_extra_context(self, request, instance):
  225. # Get assigned IP addresses
  226. ipaddress_table = InterfaceIPAddressTable(
  227. data=instance.ip_addresses.restrict(request.user, 'view').prefetch_related('vrf', 'tenant'),
  228. orderable=False
  229. )
  230. # Get assigned VLANs and annotate whether each is tagged or untagged
  231. vlans = []
  232. if instance.untagged_vlan is not None:
  233. vlans.append(instance.untagged_vlan)
  234. vlans[0].tagged = False
  235. for vlan in instance.tagged_vlans.restrict(request.user).prefetch_related('site', 'group', 'tenant', 'role'):
  236. vlan.tagged = True
  237. vlans.append(vlan)
  238. vlan_table = InterfaceVLANTable(
  239. interface=instance,
  240. data=vlans,
  241. orderable=False
  242. )
  243. return {
  244. 'ipaddress_table': ipaddress_table,
  245. 'vlan_table': vlan_table,
  246. }
  247. # TODO: This should not use ComponentCreateView
  248. class VMInterfaceCreateView(generic.ComponentCreateView):
  249. queryset = VMInterface.objects.all()
  250. form = forms.VMInterfaceCreateForm
  251. model_form = forms.VMInterfaceForm
  252. template_name = 'virtualization/virtualmachine_component_add.html'
  253. class VMInterfaceEditView(generic.ObjectEditView):
  254. queryset = VMInterface.objects.all()
  255. model_form = forms.VMInterfaceForm
  256. template_name = 'virtualization/vminterface_edit.html'
  257. class VMInterfaceDeleteView(generic.ObjectDeleteView):
  258. queryset = VMInterface.objects.all()
  259. class VMInterfaceBulkImportView(generic.BulkImportView):
  260. queryset = VMInterface.objects.all()
  261. model_form = forms.VMInterfaceCSVForm
  262. table = tables.VMInterfaceTable
  263. class VMInterfaceBulkEditView(generic.BulkEditView):
  264. queryset = VMInterface.objects.all()
  265. table = tables.VMInterfaceTable
  266. form = forms.VMInterfaceBulkEditForm
  267. class VMInterfaceBulkRenameView(generic.BulkRenameView):
  268. queryset = VMInterface.objects.all()
  269. form = forms.VMInterfaceBulkRenameForm
  270. class VMInterfaceBulkDeleteView(generic.BulkDeleteView):
  271. queryset = VMInterface.objects.all()
  272. table = tables.VMInterfaceTable
  273. #
  274. # Bulk Device component creation
  275. #
  276. class VirtualMachineBulkAddInterfaceView(generic.BulkComponentCreateView):
  277. parent_model = VirtualMachine
  278. parent_field = 'virtual_machine'
  279. form = forms.VMInterfaceBulkCreateForm
  280. queryset = VMInterface.objects.all()
  281. model_form = forms.VMInterfaceForm
  282. filterset = filters.VirtualMachineFilterSet
  283. table = tables.VirtualMachineTable