object_create.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. from django import forms
  2. from dcim.models import *
  3. from extras.forms import CustomFieldModelForm
  4. from extras.models import Tag
  5. from utilities.forms import (
  6. BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, ExpandableNameField,
  7. )
  8. __all__ = (
  9. 'ComponentCreateForm',
  10. 'FrontPortCreateForm',
  11. 'FrontPortTemplateCreateForm',
  12. 'VirtualChassisCreateForm',
  13. )
  14. class ComponentCreateForm(BootstrapMixin, forms.Form):
  15. """
  16. Subclass this form when facilitating the creation of one or more device component or component templates based on
  17. a name pattern.
  18. """
  19. name_pattern = ExpandableNameField(
  20. label='Name'
  21. )
  22. label_pattern = ExpandableNameField(
  23. label='Label',
  24. required=False,
  25. help_text='Alphanumeric ranges are supported. (Must match the number of names being created.)'
  26. )
  27. def clean(self):
  28. super().clean()
  29. # Validate that the number of components being created from both the name_pattern and label_pattern are equal
  30. if self.cleaned_data['label_pattern']:
  31. name_pattern_count = len(self.cleaned_data['name_pattern'])
  32. label_pattern_count = len(self.cleaned_data['label_pattern'])
  33. if name_pattern_count != label_pattern_count:
  34. raise forms.ValidationError({
  35. 'label_pattern': f'The provided name pattern will create {name_pattern_count} components, however '
  36. f'{label_pattern_count} labels will be generated. These counts must match.'
  37. }, code='label_pattern_mismatch')
  38. class FrontPortTemplateCreateForm(ComponentCreateForm):
  39. rear_port_set = forms.MultipleChoiceField(
  40. choices=[],
  41. label='Rear ports',
  42. help_text='Select one rear port assignment for each front port being created.',
  43. )
  44. field_order = (
  45. 'name_pattern', 'label_pattern', 'rear_port_set',
  46. )
  47. def __init__(self, *args, **kwargs):
  48. super().__init__(*args, **kwargs)
  49. device_type = DeviceType.objects.get(
  50. pk=self.initial.get('device_type') or self.data.get('device_type')
  51. )
  52. # Determine which rear port positions are occupied. These will be excluded from the list of available mappings.
  53. occupied_port_positions = [
  54. (front_port.rear_port_id, front_port.rear_port_position)
  55. for front_port in device_type.frontporttemplates.all()
  56. ]
  57. # Populate rear port choices
  58. choices = []
  59. rear_ports = RearPortTemplate.objects.filter(device_type=device_type)
  60. for rear_port in rear_ports:
  61. for i in range(1, rear_port.positions + 1):
  62. if (rear_port.pk, i) not in occupied_port_positions:
  63. choices.append(
  64. ('{}:{}'.format(rear_port.pk, i), '{}:{}'.format(rear_port.name, i))
  65. )
  66. self.fields['rear_port_set'].choices = choices
  67. def get_iterative_data(self, iteration):
  68. # Assign rear port and position from selected set
  69. rear_port, position = self.cleaned_data['rear_port_set'][iteration].split(':')
  70. return {
  71. 'rear_port': int(rear_port),
  72. 'rear_port_position': int(position),
  73. }
  74. class FrontPortCreateForm(ComponentCreateForm):
  75. rear_port_set = forms.MultipleChoiceField(
  76. choices=[],
  77. label='Rear ports',
  78. help_text='Select one rear port assignment for each front port being created.',
  79. )
  80. field_order = (
  81. 'name_pattern', 'label_pattern', 'rear_port_set',
  82. )
  83. def __init__(self, *args, **kwargs):
  84. super().__init__(*args, **kwargs)
  85. device = Device.objects.get(
  86. pk=self.initial.get('device') or self.data.get('device')
  87. )
  88. # Determine which rear port positions are occupied. These will be excluded from the list of available
  89. # mappings.
  90. occupied_port_positions = [
  91. (front_port.rear_port_id, front_port.rear_port_position)
  92. for front_port in device.frontports.all()
  93. ]
  94. # Populate rear port choices
  95. choices = []
  96. rear_ports = RearPort.objects.filter(device=device)
  97. for rear_port in rear_ports:
  98. for i in range(1, rear_port.positions + 1):
  99. if (rear_port.pk, i) not in occupied_port_positions:
  100. choices.append(
  101. ('{}:{}'.format(rear_port.pk, i), '{}:{}'.format(rear_port.name, i))
  102. )
  103. self.fields['rear_port_set'].choices = choices
  104. def get_iterative_data(self, iteration):
  105. # Assign rear port and position from selected set
  106. rear_port, position = self.cleaned_data['rear_port_set'][iteration].split(':')
  107. return {
  108. 'rear_port': int(rear_port),
  109. 'rear_port_position': int(position),
  110. }
  111. class VirtualChassisCreateForm(CustomFieldModelForm):
  112. region = DynamicModelChoiceField(
  113. queryset=Region.objects.all(),
  114. required=False,
  115. initial_params={
  116. 'sites': '$site'
  117. }
  118. )
  119. site_group = DynamicModelChoiceField(
  120. queryset=SiteGroup.objects.all(),
  121. required=False,
  122. initial_params={
  123. 'sites': '$site'
  124. }
  125. )
  126. site = DynamicModelChoiceField(
  127. queryset=Site.objects.all(),
  128. required=False,
  129. query_params={
  130. 'region_id': '$region',
  131. 'group_id': '$site_group',
  132. }
  133. )
  134. rack = DynamicModelChoiceField(
  135. queryset=Rack.objects.all(),
  136. required=False,
  137. null_option='None',
  138. query_params={
  139. 'site_id': '$site'
  140. }
  141. )
  142. members = DynamicModelMultipleChoiceField(
  143. queryset=Device.objects.all(),
  144. required=False,
  145. query_params={
  146. 'site_id': '$site',
  147. 'rack_id': '$rack',
  148. }
  149. )
  150. initial_position = forms.IntegerField(
  151. initial=1,
  152. required=False,
  153. help_text='Position of the first member device. Increases by one for each additional member.'
  154. )
  155. tags = DynamicModelMultipleChoiceField(
  156. queryset=Tag.objects.all(),
  157. required=False
  158. )
  159. class Meta:
  160. model = VirtualChassis
  161. fields = [
  162. 'name', 'domain', 'region', 'site_group', 'site', 'rack', 'members', 'initial_position', 'tags',
  163. ]
  164. def clean(self):
  165. if self.cleaned_data['members'] and self.cleaned_data['initial_position'] is None:
  166. raise forms.ValidationError({
  167. 'initial_position': "A position must be specified for the first VC member."
  168. })
  169. def save(self, *args, **kwargs):
  170. instance = super().save(*args, **kwargs)
  171. # Assign VC members
  172. if instance.pk and self.cleaned_data['members']:
  173. initial_position = self.cleaned_data.get('initial_position', 1)
  174. for i, member in enumerate(self.cleaned_data['members'], start=initial_position):
  175. member.virtual_chassis = instance
  176. member.vc_position = i
  177. member.save()
  178. return instance