forms.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. from __future__ import unicode_literals
  2. from django import forms
  3. from django.db.models import Count
  4. from dcim.models import Site, Device, Interface, Rack, VIRTUAL_IFACE_TYPES
  5. from extras.forms import CustomFieldForm, CustomFieldBulkEditForm, CustomFieldFilterForm
  6. from tenancy.forms import TenancyForm
  7. from tenancy.models import Tenant
  8. from utilities.forms import (
  9. APISelect, BootstrapMixin, ChainedFieldsMixin, ChainedModelChoiceField, CommentField, FilterChoiceField,
  10. SmallTextarea, SlugField,
  11. )
  12. from .models import Circuit, CircuitTermination, CircuitType, Provider
  13. #
  14. # Providers
  15. #
  16. class ProviderForm(BootstrapMixin, CustomFieldForm):
  17. slug = SlugField()
  18. comments = CommentField()
  19. class Meta:
  20. model = Provider
  21. fields = ['name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments']
  22. widgets = {
  23. 'noc_contact': SmallTextarea(attrs={'rows': 5}),
  24. 'admin_contact': SmallTextarea(attrs={'rows': 5}),
  25. }
  26. help_texts = {
  27. 'name': "Full name of the provider",
  28. 'asn': "BGP autonomous system number (if applicable)",
  29. 'portal_url': "URL of the provider's customer support portal",
  30. 'noc_contact': "NOC email address and phone number",
  31. 'admin_contact': "Administrative contact email address and phone number",
  32. }
  33. class ProviderCSVForm(forms.ModelForm):
  34. slug = SlugField()
  35. class Meta:
  36. model = Provider
  37. fields = ['name', 'slug', 'asn', 'account', 'portal_url', 'comments']
  38. help_texts = {
  39. 'name': 'Provider name',
  40. 'asn': '32-bit autonomous system number',
  41. 'portal_url': 'Portal URL',
  42. 'comments': 'Free-form comments',
  43. }
  44. class ProviderBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
  45. pk = forms.ModelMultipleChoiceField(queryset=Provider.objects.all(), widget=forms.MultipleHiddenInput)
  46. asn = forms.IntegerField(required=False, label='ASN')
  47. account = forms.CharField(max_length=30, required=False, label='Account number')
  48. portal_url = forms.URLField(required=False, label='Portal')
  49. noc_contact = forms.CharField(required=False, widget=SmallTextarea, label='NOC contact')
  50. admin_contact = forms.CharField(required=False, widget=SmallTextarea, label='Admin contact')
  51. comments = CommentField(widget=SmallTextarea)
  52. class Meta:
  53. nullable_fields = ['asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments']
  54. class ProviderFilterForm(BootstrapMixin, CustomFieldFilterForm):
  55. model = Provider
  56. q = forms.CharField(required=False, label='Search')
  57. site = FilterChoiceField(queryset=Site.objects.all(), to_field_name='slug')
  58. asn = forms.IntegerField(required=False, label='ASN')
  59. #
  60. # Circuit types
  61. #
  62. class CircuitTypeForm(BootstrapMixin, forms.ModelForm):
  63. slug = SlugField()
  64. class Meta:
  65. model = CircuitType
  66. fields = ['name', 'slug']
  67. #
  68. # Circuits
  69. #
  70. class CircuitForm(BootstrapMixin, TenancyForm, CustomFieldForm):
  71. comments = CommentField()
  72. class Meta:
  73. model = Circuit
  74. fields = [
  75. 'cid', 'type', 'provider', 'install_date', 'commit_rate', 'description', 'tenant_group', 'tenant',
  76. 'comments',
  77. ]
  78. help_texts = {
  79. 'cid': "Unique circuit ID",
  80. 'install_date': "Format: YYYY-MM-DD",
  81. 'commit_rate': "Committed rate",
  82. }
  83. class CircuitCSVForm(forms.ModelForm):
  84. provider = forms.ModelChoiceField(
  85. queryset=Provider.objects.all(),
  86. to_field_name='name',
  87. help_text='Name of parent provider',
  88. error_messages={
  89. 'invalid_choice': 'Provider not found.'
  90. }
  91. )
  92. type = forms.ModelChoiceField(
  93. queryset=CircuitType.objects.all(),
  94. to_field_name='name',
  95. help_text='Type of circuit',
  96. error_messages={
  97. 'invalid_choice': 'Invalid circuit type.'
  98. }
  99. )
  100. tenant = forms.ModelChoiceField(
  101. queryset=Tenant.objects.all(),
  102. required=False,
  103. to_field_name='name',
  104. help_text='Name of assigned tenant',
  105. error_messages={
  106. 'invalid_choice': 'Tenant not found.'
  107. }
  108. )
  109. class Meta:
  110. model = Circuit
  111. fields = ['cid', 'provider', 'type', 'tenant', 'install_date', 'commit_rate', 'description', 'comments']
  112. class CircuitBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
  113. pk = forms.ModelMultipleChoiceField(queryset=Circuit.objects.all(), widget=forms.MultipleHiddenInput)
  114. type = forms.ModelChoiceField(queryset=CircuitType.objects.all(), required=False)
  115. provider = forms.ModelChoiceField(queryset=Provider.objects.all(), required=False)
  116. tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
  117. commit_rate = forms.IntegerField(required=False, label='Commit rate (Kbps)')
  118. description = forms.CharField(max_length=100, required=False)
  119. comments = CommentField(widget=SmallTextarea)
  120. class Meta:
  121. nullable_fields = ['tenant', 'commit_rate', 'description', 'comments']
  122. class CircuitFilterForm(BootstrapMixin, CustomFieldFilterForm):
  123. model = Circuit
  124. q = forms.CharField(required=False, label='Search')
  125. type = FilterChoiceField(
  126. queryset=CircuitType.objects.annotate(filter_count=Count('circuits')),
  127. to_field_name='slug'
  128. )
  129. provider = FilterChoiceField(
  130. queryset=Provider.objects.annotate(filter_count=Count('circuits')),
  131. to_field_name='slug'
  132. )
  133. tenant = FilterChoiceField(
  134. queryset=Tenant.objects.annotate(filter_count=Count('circuits')),
  135. to_field_name='slug',
  136. null_option=(0, 'None')
  137. )
  138. site = FilterChoiceField(
  139. queryset=Site.objects.annotate(filter_count=Count('circuit_terminations')),
  140. to_field_name='slug'
  141. )
  142. #
  143. # Circuit terminations
  144. #
  145. class CircuitTerminationForm(BootstrapMixin, ChainedFieldsMixin, forms.ModelForm):
  146. site = forms.ModelChoiceField(
  147. queryset=Site.objects.all(),
  148. widget=forms.Select(
  149. attrs={'filter-for': 'rack'}
  150. )
  151. )
  152. rack = ChainedModelChoiceField(
  153. queryset=Rack.objects.all(),
  154. chains=(
  155. ('site', 'site'),
  156. ),
  157. required=False,
  158. label='Rack',
  159. widget=APISelect(
  160. api_url='/api/dcim/racks/?site_id={{site}}',
  161. attrs={'filter-for': 'device', 'nullable': 'true'}
  162. )
  163. )
  164. device = ChainedModelChoiceField(
  165. queryset=Device.objects.all(),
  166. chains=(
  167. ('site', 'site'),
  168. ('rack', 'rack'),
  169. ),
  170. required=False,
  171. label='Device',
  172. widget=APISelect(
  173. api_url='/api/dcim/devices/?site_id={{site}}&rack_id={{rack}}',
  174. display_field='display_name',
  175. attrs={'filter-for': 'interface'}
  176. )
  177. )
  178. interface = ChainedModelChoiceField(
  179. queryset=Interface.objects.exclude(form_factor__in=VIRTUAL_IFACE_TYPES).select_related(
  180. 'circuit_termination', 'connected_as_a', 'connected_as_b'
  181. ),
  182. chains=(
  183. ('device', 'device'),
  184. ),
  185. required=False,
  186. label='Interface',
  187. widget=APISelect(
  188. api_url='/api/dcim/interfaces/?device_id={{device}}&type=physical',
  189. disabled_indicator='is_connected'
  190. )
  191. )
  192. class Meta:
  193. model = CircuitTermination
  194. fields = [
  195. 'term_side', 'site', 'rack', 'device', 'interface', 'port_speed', 'upstream_speed', 'xconnect_id',
  196. 'pp_info',
  197. ]
  198. help_texts = {
  199. 'port_speed': "Physical circuit speed",
  200. 'xconnect_id': "ID of the local cross-connect",
  201. 'pp_info': "Patch panel ID and port number(s)"
  202. }
  203. widgets = {
  204. 'term_side': forms.HiddenInput(),
  205. }
  206. def __init__(self, *args, **kwargs):
  207. # Initialize helper selectors
  208. instance = kwargs.get('instance')
  209. if instance and instance.interface is not None:
  210. initial = kwargs.get('initial', {})
  211. initial['rack'] = instance.interface.device.rack
  212. initial['device'] = instance.interface.device
  213. kwargs['initial'] = initial
  214. super(CircuitTerminationForm, self).__init__(*args, **kwargs)
  215. # Mark connected interfaces as disabled
  216. self.fields['interface'].choices = [
  217. (iface.id, {'label': iface.name, 'disabled': iface.is_connected}) for iface in self.fields['interface'].queryset
  218. ]