views.py 12 KB

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