views.py 12 KB

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