forms.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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, 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, ComponentForm, ConfirmationForm, ExpandableNameField,
  14. FilterChoiceField, SlugField,
  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. fields = ['name', 'type', 'group']
  60. class ClusterFilterForm(BootstrapMixin, CustomFieldFilterForm):
  61. model = Cluster
  62. q = forms.CharField(required=False, label='Search')
  63. group = FilterChoiceField(
  64. queryset=ClusterGroup.objects.annotate(filter_count=Count('clusters')),
  65. to_field_name='slug',
  66. null_option=(0, 'None'),
  67. required=False,
  68. )
  69. type = FilterChoiceField(
  70. queryset=ClusterType.objects.annotate(filter_count=Count('clusters')),
  71. to_field_name='slug',
  72. required=False,
  73. )
  74. class ClusterAddDevicesForm(BootstrapMixin, ChainedFieldsMixin, forms.Form):
  75. region = TreeNodeChoiceField(
  76. queryset=Region.objects.all(),
  77. required=False,
  78. widget=forms.Select(
  79. attrs={'filter-for': 'site', 'nullable': 'true'}
  80. )
  81. )
  82. site = ChainedModelChoiceField(
  83. queryset=Site.objects.all(),
  84. chains=(
  85. ('region', 'region'),
  86. ),
  87. required=False,
  88. widget=APISelect(
  89. api_url='/api/dcim/sites/?region_id={{region}}',
  90. attrs={'filter-for': 'rack'}
  91. )
  92. )
  93. rack = ChainedModelChoiceField(
  94. queryset=Rack.objects.all(),
  95. chains=(
  96. ('site', 'site'),
  97. ),
  98. required=False,
  99. widget=APISelect(
  100. api_url='/api/dcim/racks/?site_id={{site}}',
  101. attrs={'filter-for': 'devices', 'nullable': 'true'}
  102. )
  103. )
  104. devices = ChainedModelMultipleChoiceField(
  105. queryset=Device.objects.filter(cluster__isnull=True),
  106. chains=(
  107. ('site', 'site'),
  108. ('rack', 'rack'),
  109. ),
  110. label='Device',
  111. required=False,
  112. widget=APISelectMultiple(
  113. api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}',
  114. display_field='display_name',
  115. disabled_indicator='cluster'
  116. )
  117. )
  118. class Meta:
  119. fields = ['region', 'site', 'rack', 'devices']
  120. def __init__(self, *args, **kwargs):
  121. super(ClusterAddDevicesForm, self).__init__(*args, **kwargs)
  122. self.fields['devices'].choices = []
  123. class ClusterRemoveDevicesForm(ConfirmationForm):
  124. pk = forms.ModelMultipleChoiceField(queryset=Device.objects.all(), widget=forms.MultipleHiddenInput)
  125. #
  126. # Virtual Machines
  127. #
  128. class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldForm):
  129. cluster_group = forms.ModelChoiceField(
  130. queryset=ClusterGroup.objects.all(),
  131. required=False,
  132. widget=forms.Select(
  133. attrs={'filter-for': 'cluster', 'nullable': 'true'}
  134. )
  135. )
  136. cluster = ChainedModelChoiceField(
  137. queryset=Cluster.objects.all(),
  138. chains=(
  139. ('group', 'cluster_group'),
  140. ),
  141. widget=APISelect(
  142. api_url='/api/virtualization/clusters/?group_id={{cluster_group}}'
  143. )
  144. )
  145. class Meta:
  146. model = VirtualMachine
  147. fields = ['name', 'cluster_group', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments']
  148. def __init__(self, *args, **kwargs):
  149. # Initialize helper selector
  150. instance = kwargs.get('instance')
  151. if instance.pk and instance.cluster is not None:
  152. initial = kwargs.get('initial', {}).copy()
  153. initial['cluster_group'] = instance.cluster.group
  154. kwargs['initial'] = initial
  155. super(VirtualMachineForm, self).__init__(*args, **kwargs)
  156. class VirtualMachineCSVForm(forms.ModelForm):
  157. cluster = forms.ModelChoiceField(
  158. queryset=Cluster.objects.all(),
  159. to_field_name='name',
  160. help_text='Name of parent cluster',
  161. error_messages={
  162. 'invalid_choice': 'Invalid cluster name.',
  163. }
  164. )
  165. class Meta:
  166. fields = ['cluster', 'name', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments']
  167. class VirtualMachineBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
  168. pk = forms.ModelMultipleChoiceField(queryset=VirtualMachine.objects.all(), widget=forms.MultipleHiddenInput)
  169. cluster = forms.ModelChoiceField(queryset=Cluster.objects.all(), required=False, label='Cluster')
  170. tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
  171. class Meta:
  172. nullable_fields = ['tenant']
  173. class VirtualMachineFilterForm(BootstrapMixin, CustomFieldFilterForm):
  174. model = VirtualMachine
  175. q = forms.CharField(required=False, label='Search')
  176. cluster_group = FilterChoiceField(
  177. queryset=ClusterGroup.objects.all(),
  178. to_field_name='slug',
  179. null_option=(0, 'None'),
  180. )
  181. cluster_id = FilterChoiceField(
  182. queryset=Cluster.objects.annotate(filter_count=Count('virtual_machines')),
  183. label='Cluster'
  184. )
  185. #
  186. # VM interfaces
  187. #
  188. class InterfaceForm(BootstrapMixin, forms.ModelForm):
  189. class Meta:
  190. model = Interface
  191. fields = ['virtual_machine', 'name', 'form_factor', 'enabled', 'mac_address', 'mtu', 'description']
  192. widgets = {
  193. 'virtual_machine': forms.HiddenInput(),
  194. }
  195. class InterfaceCreateForm(ComponentForm):
  196. name_pattern = ExpandableNameField(label='Name')
  197. form_factor = forms.ChoiceField(choices=VIFACE_FF_CHOICES)
  198. enabled = forms.BooleanField(required=False)
  199. mtu = forms.IntegerField(required=False, min_value=1, max_value=32767, label='MTU')
  200. mac_address = MACAddressFormField(required=False, label='MAC Address')
  201. description = forms.CharField(max_length=100, required=False)
  202. def __init__(self, *args, **kwargs):
  203. # Set interfaces enabled by default
  204. kwargs['initial'] = kwargs.get('initial', {}).copy()
  205. kwargs['initial'].update({'enabled': True})
  206. super(InterfaceCreateForm, self).__init__(*args, **kwargs)
  207. class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm):
  208. pk = forms.ModelMultipleChoiceField(queryset=Interface.objects.all(), widget=forms.MultipleHiddenInput)
  209. virtual_machine = forms.ModelChoiceField(queryset=VirtualMachine.objects.all(), widget=forms.HiddenInput)
  210. enabled = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect)
  211. mtu = forms.IntegerField(required=False, min_value=1, max_value=32767, label='MTU')
  212. description = forms.CharField(max_length=100, required=False)
  213. class Meta:
  214. nullable_fields = ['mtu', 'description']