bulk_edit.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. from django import forms
  2. from django.utils.translation import gettext_lazy as _
  3. from dcim.choices import InterfaceModeChoices
  4. from dcim.constants import INTERFACE_MTU_MAX, INTERFACE_MTU_MIN
  5. from dcim.forms.mixins import ScopedBulkEditForm
  6. from dcim.models import Device, DeviceRole, Platform, Site
  7. from extras.models import ConfigTemplate
  8. from ipam.models import VLAN, VRF, VLANGroup, VLANTranslationPolicy
  9. from netbox.forms import NetBoxModelBulkEditForm, OrganizationalModelBulkEditForm, PrimaryModelBulkEditForm
  10. from netbox.forms.mixins import OwnerMixin
  11. from tenancy.models import Tenant
  12. from utilities.forms import BulkRenameForm, add_blank_choice
  13. from utilities.forms.fields import DynamicModelChoiceField, DynamicModelMultipleChoiceField
  14. from utilities.forms.rendering import FieldSet
  15. from utilities.forms.widgets import BulkEditNullBooleanSelect
  16. from ..choices import *
  17. from ..models import *
  18. __all__ = (
  19. 'ClusterBulkEditForm',
  20. 'ClusterGroupBulkEditForm',
  21. 'ClusterTypeBulkEditForm',
  22. 'VMInterfaceBulkEditForm',
  23. 'VMInterfaceBulkRenameForm',
  24. 'VirtualDiskBulkEditForm',
  25. 'VirtualDiskBulkRenameForm',
  26. 'VirtualMachineBulkEditForm',
  27. 'VirtualMachineTypeBulkEditForm',
  28. )
  29. class ClusterTypeBulkEditForm(OrganizationalModelBulkEditForm):
  30. model = ClusterType
  31. fieldsets = (
  32. FieldSet('description'),
  33. )
  34. nullable_fields = ('description', 'comments')
  35. class ClusterGroupBulkEditForm(OrganizationalModelBulkEditForm):
  36. model = ClusterGroup
  37. fieldsets = (
  38. FieldSet('description'),
  39. )
  40. nullable_fields = ('description', 'comments')
  41. class ClusterBulkEditForm(ScopedBulkEditForm, PrimaryModelBulkEditForm):
  42. type = DynamicModelChoiceField(
  43. label=_('Type'),
  44. queryset=ClusterType.objects.all(),
  45. required=False
  46. )
  47. group = DynamicModelChoiceField(
  48. label=_('Group'),
  49. queryset=ClusterGroup.objects.all(),
  50. required=False
  51. )
  52. status = forms.ChoiceField(
  53. label=_('Status'),
  54. choices=add_blank_choice(ClusterStatusChoices),
  55. required=False,
  56. initial=''
  57. )
  58. tenant = DynamicModelChoiceField(
  59. label=_('Tenant'),
  60. queryset=Tenant.objects.all(),
  61. required=False
  62. )
  63. model = Cluster
  64. fieldsets = (
  65. FieldSet('type', 'group', 'status', 'tenant', 'description'),
  66. FieldSet('scope_type', 'scope', name=_('Scope')),
  67. )
  68. nullable_fields = (
  69. 'group', 'scope', 'tenant', 'description', 'comments',
  70. )
  71. class VirtualMachineTypeBulkEditForm(PrimaryModelBulkEditForm):
  72. default_platform = DynamicModelChoiceField(
  73. label=_('Default platform'),
  74. queryset=Platform.objects.all(),
  75. required=False
  76. )
  77. default_vcpus = forms.IntegerField(
  78. label=_('Default vCPUs'),
  79. required=False,
  80. )
  81. default_memory = forms.IntegerField(
  82. label=_('Default Memory (MB)'),
  83. required=False,
  84. )
  85. model = VirtualMachineType
  86. fieldsets = (
  87. FieldSet('description', name=_('Virtual Machine Type')),
  88. FieldSet('default_platform', 'default_vcpus', 'default_memory', name=_('Defaults')),
  89. )
  90. nullable_fields = (
  91. 'default_platform', 'default_vcpus', 'default_memory', 'description', 'comments',
  92. )
  93. class VirtualMachineBulkEditForm(PrimaryModelBulkEditForm):
  94. virtual_machine_type = DynamicModelChoiceField(
  95. label=_('Virtual machine type'),
  96. queryset=VirtualMachineType.objects.all(),
  97. required=False
  98. )
  99. status = forms.ChoiceField(
  100. label=_('Status'),
  101. choices=add_blank_choice(VirtualMachineStatusChoices),
  102. required=False,
  103. initial='',
  104. )
  105. start_on_boot = forms.ChoiceField(
  106. label=_('Start on boot'),
  107. choices=add_blank_choice(VirtualMachineStartOnBootChoices),
  108. required=False,
  109. initial='',
  110. )
  111. site = DynamicModelChoiceField(
  112. label=_('Site'),
  113. queryset=Site.objects.all(),
  114. required=False
  115. )
  116. cluster = DynamicModelChoiceField(
  117. label=_('Cluster'),
  118. queryset=Cluster.objects.all(),
  119. required=False,
  120. query_params={
  121. 'site_id': '$site'
  122. }
  123. )
  124. device = DynamicModelChoiceField(
  125. label=_('Device'),
  126. queryset=Device.objects.all(),
  127. required=False,
  128. query_params={
  129. 'cluster_id': '$cluster',
  130. 'site_id': '$site'
  131. }
  132. )
  133. role = DynamicModelChoiceField(
  134. label=_('Role'),
  135. queryset=DeviceRole.objects.filter(
  136. vm_role=True
  137. ),
  138. required=False,
  139. query_params={
  140. "vm_role": "True"
  141. }
  142. )
  143. tenant = DynamicModelChoiceField(
  144. label=_('Tenant'),
  145. queryset=Tenant.objects.all(),
  146. required=False
  147. )
  148. platform = DynamicModelChoiceField(
  149. label=_('Platform'),
  150. queryset=Platform.objects.all(),
  151. required=False
  152. )
  153. vcpus = forms.IntegerField(
  154. required=False,
  155. label=_('vCPUs')
  156. )
  157. memory = forms.IntegerField(
  158. required=False,
  159. label=_('Memory (MB)')
  160. )
  161. disk = forms.IntegerField(
  162. required=False,
  163. label=_('Disk (MB)')
  164. )
  165. config_template = DynamicModelChoiceField(
  166. queryset=ConfigTemplate.objects.all(),
  167. required=False
  168. )
  169. model = VirtualMachine
  170. fieldsets = (
  171. FieldSet('virtual_machine_type', 'status', 'start_on_boot', 'role', 'tenant', 'platform', 'description'),
  172. FieldSet('site', 'cluster', 'device', name=_('Placement')),
  173. FieldSet('vcpus', 'memory', 'disk', name=_('Resources')),
  174. FieldSet('config_template', name=_('Configuration')),
  175. )
  176. nullable_fields = (
  177. 'virtual_machine_type', 'role', 'site', 'cluster', 'device', 'platform', 'vcpus', 'memory', 'disk', 'tenant',
  178. 'description', 'comments',
  179. )
  180. class VMInterfaceBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
  181. virtual_machine = forms.ModelChoiceField(
  182. label=_('Virtual machine'),
  183. queryset=VirtualMachine.objects.all(),
  184. required=False,
  185. disabled=True,
  186. widget=forms.HiddenInput()
  187. )
  188. parent = DynamicModelChoiceField(
  189. label=_('Parent'),
  190. queryset=VMInterface.objects.all(),
  191. required=False
  192. )
  193. bridge = DynamicModelChoiceField(
  194. label=_('Bridge'),
  195. queryset=VMInterface.objects.all(),
  196. required=False
  197. )
  198. enabled = forms.NullBooleanField(
  199. label=_('Enabled'),
  200. required=False,
  201. widget=BulkEditNullBooleanSelect()
  202. )
  203. mtu = forms.IntegerField(
  204. required=False,
  205. min_value=INTERFACE_MTU_MIN,
  206. max_value=INTERFACE_MTU_MAX,
  207. label=_('MTU')
  208. )
  209. description = forms.CharField(
  210. label=_('Description'),
  211. max_length=100,
  212. required=False
  213. )
  214. mode = forms.ChoiceField(
  215. label=_('Mode'),
  216. choices=add_blank_choice(InterfaceModeChoices),
  217. required=False
  218. )
  219. vlan_group = DynamicModelChoiceField(
  220. queryset=VLANGroup.objects.all(),
  221. required=False,
  222. label=_('VLAN group')
  223. )
  224. untagged_vlan = DynamicModelChoiceField(
  225. queryset=VLAN.objects.all(),
  226. required=False,
  227. query_params={
  228. 'group_id': '$vlan_group',
  229. },
  230. label=_('Untagged VLAN')
  231. )
  232. tagged_vlans = DynamicModelMultipleChoiceField(
  233. queryset=VLAN.objects.all(),
  234. required=False,
  235. query_params={
  236. 'group_id': '$vlan_group',
  237. },
  238. label=_('Tagged VLANs')
  239. )
  240. vrf = DynamicModelChoiceField(
  241. queryset=VRF.objects.all(),
  242. required=False,
  243. label=_('VRF')
  244. )
  245. vlan_translation_policy = DynamicModelChoiceField(
  246. queryset=VLANTranslationPolicy.objects.all(),
  247. required=False,
  248. label=_('VLAN Translation Policy')
  249. )
  250. model = VMInterface
  251. fieldsets = (
  252. FieldSet('mtu', 'enabled', 'vrf', 'description'),
  253. FieldSet('parent', 'bridge', name=_('Related Interfaces')),
  254. FieldSet(
  255. 'mode', 'vlan_group', 'untagged_vlan', 'tagged_vlans', 'vlan_translation_policy',
  256. name=_('802.1Q Switching')
  257. ),
  258. )
  259. nullable_fields = (
  260. 'parent', 'bridge', 'mtu', 'vrf', 'description', 'vlan_translation_policy',
  261. )
  262. def __init__(self, *args, **kwargs):
  263. super().__init__(*args, **kwargs)
  264. if 'virtual_machine' in self.initial:
  265. vm_id = self.initial.get('virtual_machine')
  266. # Restrict parent/bridge interface assignment by VM
  267. self.fields['parent'].widget.add_query_param('virtual_machine_id', vm_id)
  268. self.fields['bridge'].widget.add_query_param('virtual_machine_id', vm_id)
  269. # Limit VLAN choices by virtual machine
  270. self.fields['untagged_vlan'].widget.add_query_param('available_on_virtualmachine', vm_id)
  271. self.fields['tagged_vlans'].widget.add_query_param('available_on_virtualmachine', vm_id)
  272. else:
  273. # See 5643
  274. if 'pk' in self.initial:
  275. site = None
  276. interfaces = VMInterface.objects.filter(
  277. pk__in=self.initial['pk']
  278. ).prefetch_related(
  279. 'virtual_machine__site',
  280. 'virtual_machine__cluster',
  281. 'virtual_machine__device',
  282. )
  283. # Determine the effective site for each interface's VM (from its site,
  284. # cluster, or device). If all selected interfaces share the same site,
  285. # use it to filter VLAN choices; otherwise leave unfiltered.
  286. for interface in interfaces:
  287. vm = interface.virtual_machine
  288. vm_site = (
  289. vm.site
  290. or (vm.cluster and vm.cluster._site)
  291. or (vm.device and vm.device.site)
  292. )
  293. if site is None:
  294. site = vm_site
  295. elif vm_site is not site:
  296. site = None
  297. break
  298. if site is not None:
  299. self.fields['untagged_vlan'].widget.add_query_param('site_id', site.pk)
  300. self.fields['tagged_vlans'].widget.add_query_param('site_id', site.pk)
  301. self.fields['parent'].choices = ()
  302. self.fields['parent'].widget.attrs['disabled'] = True
  303. self.fields['bridge'].choices = ()
  304. self.fields['bridge'].widget.attrs['disabled'] = True
  305. class VMInterfaceBulkRenameForm(BulkRenameForm):
  306. pk = forms.ModelMultipleChoiceField(
  307. queryset=VMInterface.objects.all(),
  308. widget=forms.MultipleHiddenInput()
  309. )
  310. class VirtualDiskBulkEditForm(OwnerMixin, NetBoxModelBulkEditForm):
  311. virtual_machine = forms.ModelChoiceField(
  312. label=_('Virtual machine'),
  313. queryset=VirtualMachine.objects.all(),
  314. required=False,
  315. disabled=True,
  316. widget=forms.HiddenInput()
  317. )
  318. size = forms.IntegerField(
  319. required=False,
  320. label=_('Size (MB)')
  321. )
  322. description = forms.CharField(
  323. label=_('Description'),
  324. max_length=100,
  325. required=False
  326. )
  327. model = VirtualDisk
  328. fieldsets = (
  329. FieldSet('size', 'description'),
  330. )
  331. nullable_fields = ('description',)
  332. class VirtualDiskBulkRenameForm(BulkRenameForm):
  333. pk = forms.ModelMultipleChoiceField(
  334. queryset=VirtualDisk.objects.all(),
  335. widget=forms.MultipleHiddenInput()
  336. )