common.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. from django import forms
  2. from django.utils.translation import gettext_lazy as _
  3. from dcim.choices import *
  4. from dcim.constants import *
  5. from utilities.forms import get_field_value
  6. __all__ = (
  7. 'InterfaceCommonForm',
  8. 'ModuleCommonForm'
  9. )
  10. class InterfaceCommonForm(forms.Form):
  11. mac_address = forms.CharField(
  12. empty_value=None,
  13. required=False,
  14. label=_('MAC address')
  15. )
  16. mtu = forms.IntegerField(
  17. required=False,
  18. min_value=INTERFACE_MTU_MIN,
  19. max_value=INTERFACE_MTU_MAX,
  20. label=_('MTU')
  21. )
  22. def __init__(self, *args, **kwargs):
  23. super().__init__(*args, **kwargs)
  24. # Determine the selected 802.1Q mode
  25. interface_mode = get_field_value(self, 'mode')
  26. # Delete VLAN tagging fields which are not relevant for the selected mode
  27. if interface_mode in (InterfaceModeChoices.MODE_ACCESS, InterfaceModeChoices.MODE_TAGGED_ALL):
  28. del self.fields['tagged_vlans']
  29. elif not interface_mode:
  30. del self.fields['vlan_group']
  31. del self.fields['untagged_vlan']
  32. del self.fields['tagged_vlans']
  33. def clean(self):
  34. super().clean()
  35. parent_field = 'device' if 'device' in self.cleaned_data else 'virtual_machine'
  36. tagged_vlans = self.cleaned_data.get('tagged_vlans')
  37. # Untagged interfaces cannot be assigned tagged VLANs
  38. if self.cleaned_data['mode'] == InterfaceModeChoices.MODE_ACCESS and tagged_vlans:
  39. raise forms.ValidationError({
  40. 'mode': _("An access interface cannot have tagged VLANs assigned.")
  41. })
  42. # Remove all tagged VLAN assignments from "tagged all" interfaces
  43. elif self.cleaned_data['mode'] == InterfaceModeChoices.MODE_TAGGED_ALL:
  44. self.cleaned_data['tagged_vlans'] = []
  45. # Validate tagged VLANs; must be a global VLAN or in the same site
  46. elif self.cleaned_data['mode'] == InterfaceModeChoices.MODE_TAGGED and tagged_vlans:
  47. valid_sites = [None, self.cleaned_data[parent_field].site]
  48. invalid_vlans = [str(v) for v in tagged_vlans if v.site not in valid_sites]
  49. if invalid_vlans:
  50. raise forms.ValidationError({
  51. 'tagged_vlans': _(
  52. "The tagged VLANs ({vlans}) must belong to the same site as the interface's parent device/VM, "
  53. "or they must be global"
  54. ).format(vlans=', '.join(invalid_vlans))
  55. })
  56. class ModuleCommonForm(forms.Form):
  57. def clean(self):
  58. super().clean()
  59. replicate_components = self.cleaned_data.get('replicate_components')
  60. adopt_components = self.cleaned_data.get('adopt_components')
  61. device = self.cleaned_data.get('device')
  62. module_type = self.cleaned_data.get('module_type')
  63. module_bay = self.cleaned_data.get('module_bay')
  64. if adopt_components:
  65. self.instance._adopt_components = True
  66. # Bail out if we are not installing a new module or if we are not replicating components (or if
  67. # validation has already failed)
  68. if self.errors or self.instance.pk or not replicate_components:
  69. self.instance._disable_replication = True
  70. return
  71. for templates, component_attribute in [
  72. ("consoleporttemplates", "consoleports"),
  73. ("consoleserverporttemplates", "consoleserverports"),
  74. ("interfacetemplates", "interfaces"),
  75. ("powerporttemplates", "powerports"),
  76. ("poweroutlettemplates", "poweroutlets"),
  77. ("rearporttemplates", "rearports"),
  78. ("frontporttemplates", "frontports")
  79. ]:
  80. # Prefetch installed components
  81. installed_components = {
  82. component.name: component for component in getattr(device, component_attribute).all()
  83. }
  84. # Get the templates for the module type.
  85. for template in getattr(module_type, templates).all():
  86. # Installing modules with placeholders require that the bay has a position value
  87. if MODULE_TOKEN in template.name and not module_bay.position:
  88. raise forms.ValidationError(
  89. _("Cannot install module with placeholder values in a module bay with no position defined.")
  90. )
  91. resolved_name = template.name.replace(MODULE_TOKEN, module_bay.position)
  92. existing_item = installed_components.get(resolved_name)
  93. # It is not possible to adopt components already belonging to a module
  94. if adopt_components and existing_item and existing_item.module:
  95. raise forms.ValidationError(
  96. _("Cannot adopt {name} '{resolved_name}' as it already belongs to a module").format(
  97. name=template.component_model.__name__,
  98. resolved_name=resolved_name
  99. )
  100. )
  101. # If we are not adopting components we error if the component exists
  102. if not adopt_components and resolved_name in installed_components:
  103. raise forms.ValidationError(
  104. _("{name} - {resolved_name} already exists").format(
  105. name=template.component_model.__name__,
  106. resolved_name=resolved_name
  107. )
  108. )