bulk_edit.py 10 KB

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