views.py 12 KB

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