forms.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. from __future__ import unicode_literals
  2. from mptt.forms import TreeNodeChoiceField
  3. from django import forms
  4. from django.db.models import Count
  5. from dcim.constants import VIFACE_FF_CHOICES
  6. from dcim.formfields import MACAddressFormField
  7. from dcim.models import Device, Interface, Platform, Rack, Region, Site
  8. from extras.forms import CustomFieldBulkEditForm, CustomFieldForm, CustomFieldFilterForm
  9. from tenancy.forms import TenancyForm
  10. from tenancy.models import Tenant
  11. from utilities.forms import (
  12. APISelect, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ChainedFieldsMixin,
  13. ChainedModelChoiceField, ChainedModelMultipleChoiceField, CommentField, ComponentForm, ConfirmationForm,
  14. ExpandableNameField, FilterChoiceField, SlugField, SmallTextarea,
  15. )
  16. from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
  17. #
  18. # Cluster types
  19. #
  20. class ClusterTypeForm(BootstrapMixin, forms.ModelForm):
  21. slug = SlugField()
  22. class Meta:
  23. model = ClusterType
  24. fields = ['name', 'slug']
  25. #
  26. # Cluster groups
  27. #
  28. class ClusterGroupForm(BootstrapMixin, forms.ModelForm):
  29. slug = SlugField()
  30. class Meta:
  31. model = ClusterGroup
  32. fields = ['name', 'slug']
  33. #
  34. # Clusters
  35. #
  36. class ClusterForm(BootstrapMixin, CustomFieldForm):
  37. class Meta:
  38. model = Cluster
  39. fields = ['name', 'type', 'group']
  40. class ClusterCSVForm(forms.ModelForm):
  41. type = forms.ModelChoiceField(
  42. queryset=ClusterType.objects.all(),
  43. to_field_name='name',
  44. help_text='Name of cluster type',
  45. error_messages={
  46. 'invalid_choice': 'Invalid cluster type name.',
  47. }
  48. )
  49. group = forms.ModelChoiceField(
  50. queryset=ClusterGroup.objects.all(),
  51. to_field_name='name',
  52. required=False,
  53. help_text='Name of cluster group',
  54. error_messages={
  55. 'invalid_choice': 'Invalid cluster group name.',
  56. }
  57. )
  58. class Meta:
  59. model = Cluster
  60. fields = ['name', 'type', 'group']
  61. class ClusterBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
  62. pk = forms.ModelMultipleChoiceField(queryset=Cluster.objects.all(), widget=forms.MultipleHiddenInput)
  63. type = forms.ModelChoiceField(queryset=ClusterType.objects.all(), required=False)
  64. group = forms.ModelChoiceField(queryset=ClusterGroup.objects.all(), required=False)
  65. class Meta:
  66. nullable_fields = ['group']
  67. class ClusterFilterForm(BootstrapMixin, CustomFieldFilterForm):
  68. model = Cluster
  69. q = forms.CharField(required=False, label='Search')
  70. group = FilterChoiceField(
  71. queryset=ClusterGroup.objects.annotate(filter_count=Count('clusters')),
  72. to_field_name='slug',
  73. null_option=(0, 'None'),
  74. required=False,
  75. )
  76. type = FilterChoiceField(
  77. queryset=ClusterType.objects.annotate(filter_count=Count('clusters')),
  78. to_field_name='slug',
  79. required=False,
  80. )
  81. class ClusterAddDevicesForm(BootstrapMixin, ChainedFieldsMixin, forms.Form):
  82. region = TreeNodeChoiceField(
  83. queryset=Region.objects.all(),
  84. required=False,
  85. widget=forms.Select(
  86. attrs={'filter-for': 'site', 'nullable': 'true'}
  87. )
  88. )
  89. site = ChainedModelChoiceField(
  90. queryset=Site.objects.all(),
  91. chains=(
  92. ('region', 'region'),
  93. ),
  94. required=False,
  95. widget=APISelect(
  96. api_url='/api/dcim/sites/?region_id={{region}}',
  97. attrs={'filter-for': 'rack'}
  98. )
  99. )
  100. rack = ChainedModelChoiceField(
  101. queryset=Rack.objects.all(),
  102. chains=(
  103. ('site', 'site'),
  104. ),
  105. required=False,
  106. widget=APISelect(
  107. api_url='/api/dcim/racks/?site_id={{site}}',
  108. attrs={'filter-for': 'devices', 'nullable': 'true'}
  109. )
  110. )
  111. devices = ChainedModelMultipleChoiceField(
  112. queryset=Device.objects.filter(cluster__isnull=True),
  113. chains=(
  114. ('site', 'site'),
  115. ('rack', 'rack'),
  116. ),
  117. label='Device',
  118. required=False,
  119. widget=APISelectMultiple(
  120. api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}',
  121. display_field='display_name',
  122. disabled_indicator='cluster'
  123. )
  124. )
  125. class Meta:
  126. fields = ['region', 'site', 'rack', 'devices']
  127. def __init__(self, *args, **kwargs):
  128. super(ClusterAddDevicesForm, self).__init__(*args, **kwargs)
  129. self.fields['devices'].choices = []
  130. class ClusterRemoveDevicesForm(ConfirmationForm):
  131. pk = forms.ModelMultipleChoiceField(queryset=Device.objects.all(), widget=forms.MultipleHiddenInput)
  132. #
  133. # Virtual Machines
  134. #
  135. class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldForm):
  136. cluster_group = forms.ModelChoiceField(
  137. queryset=ClusterGroup.objects.all(),
  138. required=False,
  139. widget=forms.Select(
  140. attrs={'filter-for': 'cluster', 'nullable': 'true'}
  141. )
  142. )
  143. cluster = ChainedModelChoiceField(
  144. queryset=Cluster.objects.all(),
  145. chains=(
  146. ('group', 'cluster_group'),
  147. ),
  148. widget=APISelect(
  149. api_url='/api/virtualization/clusters/?group_id={{cluster_group}}'
  150. )
  151. )
  152. class Meta:
  153. model = VirtualMachine
  154. fields = ['name', 'cluster_group', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments']
  155. def __init__(self, *args, **kwargs):
  156. # Initialize helper selector
  157. instance = kwargs.get('instance')
  158. if instance.pk and instance.cluster is not None:
  159. initial = kwargs.get('initial', {}).copy()
  160. initial['cluster_group'] = instance.cluster.group
  161. kwargs['initial'] = initial
  162. super(VirtualMachineForm, self).__init__(*args, **kwargs)
  163. class VirtualMachineCSVForm(forms.ModelForm):
  164. cluster = forms.ModelChoiceField(
  165. queryset=Cluster.objects.all(),
  166. to_field_name='name',
  167. help_text='Name of parent cluster',
  168. error_messages={
  169. 'invalid_choice': 'Invalid cluster name.',
  170. }
  171. )
  172. tenant = forms.ModelChoiceField(
  173. queryset=Tenant.objects.all(),
  174. required=False,
  175. to_field_name='name',
  176. help_text='Name of assigned tenant',
  177. error_messages={
  178. 'invalid_choice': 'Tenant not found.'
  179. }
  180. )
  181. platform = forms.ModelChoiceField(
  182. queryset=Platform.objects.all(),
  183. required=False,
  184. to_field_name='name',
  185. help_text='Name of assigned platform',
  186. error_messages={
  187. 'invalid_choice': 'Invalid platform.',
  188. }
  189. )
  190. class Meta:
  191. model = VirtualMachine
  192. fields = ['name', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments']
  193. class VirtualMachineBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
  194. pk = forms.ModelMultipleChoiceField(queryset=VirtualMachine.objects.all(), widget=forms.MultipleHiddenInput)
  195. cluster = forms.ModelChoiceField(queryset=Cluster.objects.all(), required=False)
  196. tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
  197. platform = forms.ModelChoiceField(queryset=Platform.objects.all(), required=False)
  198. vcpus = forms.IntegerField(required=False, label='vCPUs')
  199. memory = forms.IntegerField(required=False, label='Memory (MB)')
  200. disk = forms.IntegerField(required=False, label='Disk (GB)')
  201. comments = CommentField(widget=SmallTextarea)
  202. class Meta:
  203. nullable_fields = ['tenant', 'platform', 'vcpus', 'memory', 'disk']
  204. class VirtualMachineFilterForm(BootstrapMixin, CustomFieldFilterForm):
  205. model = VirtualMachine
  206. q = forms.CharField(required=False, label='Search')
  207. cluster_group = FilterChoiceField(
  208. queryset=ClusterGroup.objects.all(),
  209. to_field_name='slug',
  210. null_option=(0, 'None'),
  211. )
  212. cluster_id = FilterChoiceField(
  213. queryset=Cluster.objects.annotate(filter_count=Count('virtual_machines')),
  214. label='Cluster'
  215. )
  216. #
  217. # VM interfaces
  218. #
  219. class InterfaceForm(BootstrapMixin, forms.ModelForm):
  220. class Meta:
  221. model = Interface
  222. fields = ['virtual_machine', 'name', 'form_factor', 'enabled', 'mac_address', 'mtu', 'description']
  223. widgets = {
  224. 'virtual_machine': forms.HiddenInput(),
  225. }
  226. class InterfaceCreateForm(ComponentForm):
  227. name_pattern = ExpandableNameField(label='Name')
  228. form_factor = forms.ChoiceField(choices=VIFACE_FF_CHOICES)
  229. enabled = forms.BooleanField(required=False)
  230. mtu = forms.IntegerField(required=False, min_value=1, max_value=32767, label='MTU')
  231. mac_address = MACAddressFormField(required=False, label='MAC Address')
  232. description = forms.CharField(max_length=100, required=False)
  233. def __init__(self, *args, **kwargs):
  234. # Set interfaces enabled by default
  235. kwargs['initial'] = kwargs.get('initial', {}).copy()
  236. kwargs['initial'].update({'enabled': True})
  237. super(InterfaceCreateForm, self).__init__(*args, **kwargs)
  238. class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm):
  239. pk = forms.ModelMultipleChoiceField(queryset=Interface.objects.all(), widget=forms.MultipleHiddenInput)
  240. virtual_machine = forms.ModelChoiceField(queryset=VirtualMachine.objects.all(), widget=forms.HiddenInput)
  241. enabled = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect)
  242. mtu = forms.IntegerField(required=False, min_value=1, max_value=32767, label='MTU')
  243. description = forms.CharField(max_length=100, required=False)
  244. class Meta:
  245. nullable_fields = ['mtu', 'description']
  246. #
  247. # Bulk VirtualMachine component creation
  248. #
  249. class VirtualMachineBulkAddComponentForm(BootstrapMixin, forms.Form):
  250. pk = forms.ModelMultipleChoiceField(queryset=VirtualMachine.objects.all(), widget=forms.MultipleHiddenInput)
  251. name_pattern = ExpandableNameField(label='Name')
  252. class VirtualMachineBulkAddInterfaceForm(VirtualMachineBulkAddComponentForm):
  253. form_factor = forms.ChoiceField(choices=VIFACE_FF_CHOICES)
  254. enabled = forms.BooleanField(required=False, initial=True)
  255. mtu = forms.IntegerField(required=False, min_value=1, max_value=32767, label='MTU')
  256. description = forms.CharField(max_length=100, required=False)