model_forms.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. from django import forms
  2. from django.contrib.contenttypes.models import ContentType
  3. from django.core.exceptions import ObjectDoesNotExist
  4. from django.utils.translation import gettext_lazy as _
  5. from circuits.choices import (
  6. CircuitCommitRateChoices, CircuitTerminationPortSpeedChoices, VirtualCircuitTerminationRoleChoices,
  7. )
  8. from circuits.constants import *
  9. from circuits.models import *
  10. from dcim.models import Interface, Site
  11. from ipam.models import ASN
  12. from netbox.forms import NetBoxModelForm, OrganizationalModelForm, PrimaryModelForm
  13. from tenancy.forms import TenancyForm
  14. from utilities.forms import get_field_value
  15. from utilities.forms.fields import (
  16. ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField,
  17. )
  18. from utilities.forms.mixins import DistanceValidationMixin
  19. from utilities.forms.rendering import FieldSet, InlineFields
  20. from utilities.forms.widgets import DatePicker, HTMXSelect, NumberWithOptions
  21. from utilities.templatetags.builtins.filters import bettertitle
  22. __all__ = (
  23. 'CircuitForm',
  24. 'CircuitGroupAssignmentForm',
  25. 'CircuitGroupForm',
  26. 'CircuitTerminationForm',
  27. 'CircuitTypeForm',
  28. 'ProviderForm',
  29. 'ProviderAccountForm',
  30. 'ProviderNetworkForm',
  31. 'VirtualCircuitForm',
  32. 'VirtualCircuitTerminationForm',
  33. 'VirtualCircuitTypeForm',
  34. )
  35. class ProviderForm(PrimaryModelForm):
  36. slug = SlugField()
  37. asns = DynamicModelMultipleChoiceField(
  38. queryset=ASN.objects.all(),
  39. label=_('ASNs'),
  40. required=False
  41. )
  42. fieldsets = (
  43. FieldSet('name', 'slug', 'asns', 'description', 'tags'),
  44. )
  45. class Meta:
  46. model = Provider
  47. fields = [
  48. 'name', 'slug', 'asns', 'description', 'owner', 'comments', 'tags',
  49. ]
  50. class ProviderAccountForm(PrimaryModelForm):
  51. provider = DynamicModelChoiceField(
  52. label=_('Provider'),
  53. queryset=Provider.objects.all(),
  54. selector=True,
  55. quick_add=True
  56. )
  57. class Meta:
  58. model = ProviderAccount
  59. fields = [
  60. 'provider', 'name', 'account', 'description', 'owner', 'comments', 'tags',
  61. ]
  62. class ProviderNetworkForm(PrimaryModelForm):
  63. provider = DynamicModelChoiceField(
  64. label=_('Provider'),
  65. queryset=Provider.objects.all(),
  66. selector=True,
  67. quick_add=True
  68. )
  69. fieldsets = (
  70. FieldSet('provider', 'name', 'service_id', 'description', 'tags'),
  71. )
  72. class Meta:
  73. model = ProviderNetwork
  74. fields = [
  75. 'provider', 'name', 'service_id', 'description', 'owner', 'comments', 'tags',
  76. ]
  77. class CircuitTypeForm(OrganizationalModelForm):
  78. fieldsets = (
  79. FieldSet('name', 'slug', 'color', 'description', 'owner', 'tags'),
  80. )
  81. class Meta:
  82. model = CircuitType
  83. fields = [
  84. 'name', 'slug', 'color', 'description', 'comments', 'tags',
  85. ]
  86. class CircuitForm(DistanceValidationMixin, TenancyForm, PrimaryModelForm):
  87. provider = DynamicModelChoiceField(
  88. label=_('Provider'),
  89. queryset=Provider.objects.all(),
  90. selector=True,
  91. quick_add=True
  92. )
  93. provider_account = DynamicModelChoiceField(
  94. label=_('Provider account'),
  95. queryset=ProviderAccount.objects.all(),
  96. required=False,
  97. query_params={
  98. 'provider_id': '$provider',
  99. }
  100. )
  101. type = DynamicModelChoiceField(
  102. queryset=CircuitType.objects.all(),
  103. quick_add=True
  104. )
  105. fieldsets = (
  106. FieldSet(
  107. 'provider',
  108. 'provider_account',
  109. 'cid',
  110. 'type',
  111. 'status',
  112. InlineFields('distance', 'distance_unit', label=_('Distance')),
  113. 'description',
  114. 'tags',
  115. name=_('Circuit')
  116. ),
  117. FieldSet('install_date', 'termination_date', 'commit_rate', name=_('Service Parameters')),
  118. FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
  119. )
  120. class Meta:
  121. model = Circuit
  122. fields = [
  123. 'cid', 'type', 'provider', 'provider_account', 'status', 'install_date', 'termination_date', 'commit_rate',
  124. 'distance', 'distance_unit', 'description', 'tenant_group', 'tenant', 'owner', 'comments', 'tags',
  125. ]
  126. widgets = {
  127. 'install_date': DatePicker(),
  128. 'termination_date': DatePicker(),
  129. 'commit_rate': NumberWithOptions(
  130. options=CircuitCommitRateChoices
  131. ),
  132. }
  133. class CircuitTerminationForm(NetBoxModelForm):
  134. circuit = DynamicModelChoiceField(
  135. label=_('Circuit'),
  136. queryset=Circuit.objects.all(),
  137. selector=True
  138. )
  139. termination_type = ContentTypeChoiceField(
  140. queryset=ContentType.objects.filter(model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES),
  141. widget=HTMXSelect(),
  142. required=False,
  143. label=_('Termination type')
  144. )
  145. termination = DynamicModelChoiceField(
  146. label=_('Termination'),
  147. queryset=Site.objects.none(), # Initial queryset
  148. required=False,
  149. disabled=True,
  150. selector=True
  151. )
  152. fieldsets = (
  153. FieldSet(
  154. 'circuit', 'term_side', 'description', 'tags',
  155. 'termination_type', 'termination',
  156. 'mark_connected', name=_('Circuit Termination')
  157. ),
  158. FieldSet('port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', name=_('Termination Details')),
  159. )
  160. class Meta:
  161. model = CircuitTermination
  162. fields = [
  163. 'circuit', 'term_side', 'termination_type', 'mark_connected', 'port_speed', 'upstream_speed',
  164. 'xconnect_id', 'pp_info', 'description', 'tags',
  165. ]
  166. widgets = {
  167. 'port_speed': NumberWithOptions(
  168. options=CircuitTerminationPortSpeedChoices
  169. ),
  170. 'upstream_speed': NumberWithOptions(
  171. options=CircuitTerminationPortSpeedChoices
  172. ),
  173. }
  174. def __init__(self, *args, **kwargs):
  175. instance = kwargs.get('instance')
  176. initial = kwargs.get('initial', {})
  177. if instance is not None and instance.termination:
  178. initial['termination'] = instance.termination
  179. kwargs['initial'] = initial
  180. super().__init__(*args, **kwargs)
  181. if termination_type_id := get_field_value(self, 'termination_type'):
  182. try:
  183. termination_type = ContentType.objects.get(pk=termination_type_id)
  184. model = termination_type.model_class()
  185. self.fields['termination'].queryset = model.objects.all()
  186. self.fields['termination'].widget.attrs['selector'] = model._meta.label_lower
  187. self.fields['termination'].disabled = False
  188. self.fields['termination'].label = _(bettertitle(model._meta.verbose_name))
  189. except ObjectDoesNotExist:
  190. pass
  191. if self.instance and termination_type_id != self.instance.termination_type_id:
  192. self.initial['termination'] = None
  193. def clean(self):
  194. super().clean()
  195. # Assign the selected termination (if any)
  196. self.instance.termination = self.cleaned_data.get('termination')
  197. class CircuitGroupForm(TenancyForm, OrganizationalModelForm):
  198. fieldsets = (
  199. FieldSet('name', 'slug', 'description', 'tags', name=_('Circuit Group')),
  200. FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
  201. )
  202. class Meta:
  203. model = CircuitGroup
  204. fields = [
  205. 'name', 'slug', 'description', 'tenant_group', 'tenant', 'owner', 'comments', 'tags',
  206. ]
  207. class CircuitGroupAssignmentForm(NetBoxModelForm):
  208. group = DynamicModelChoiceField(
  209. label=_('Group'),
  210. queryset=CircuitGroup.objects.all(),
  211. )
  212. member_type = ContentTypeChoiceField(
  213. queryset=ContentType.objects.filter(CIRCUIT_GROUP_ASSIGNMENT_MEMBER_MODELS),
  214. widget=HTMXSelect(),
  215. required=False,
  216. label=_('Circuit type')
  217. )
  218. member = DynamicModelChoiceField(
  219. label=_('Circuit'),
  220. queryset=Circuit.objects.none(), # Initial queryset
  221. required=False,
  222. disabled=True,
  223. selector=True
  224. )
  225. fieldsets = (
  226. FieldSet('group', 'member_type', 'member', 'priority', 'tags', name=_('Group Assignment')),
  227. )
  228. class Meta:
  229. model = CircuitGroupAssignment
  230. fields = [
  231. 'group', 'member_type', 'priority', 'tags',
  232. ]
  233. def __init__(self, *args, **kwargs):
  234. instance = kwargs.get('instance')
  235. initial = kwargs.get('initial', {})
  236. if instance is not None and instance.member:
  237. initial['member'] = instance.member
  238. kwargs['initial'] = initial
  239. super().__init__(*args, **kwargs)
  240. if member_type_id := get_field_value(self, 'member_type'):
  241. try:
  242. model = ContentType.objects.get(pk=member_type_id).model_class()
  243. self.fields['member'].queryset = model.objects.all()
  244. self.fields['member'].widget.attrs['selector'] = model._meta.label_lower
  245. self.fields['member'].disabled = False
  246. self.fields['member'].label = _(bettertitle(model._meta.verbose_name))
  247. except ObjectDoesNotExist:
  248. pass
  249. if self.instance.pk and member_type_id != self.instance.member_type_id:
  250. self.initial['member'] = None
  251. def clean(self):
  252. super().clean()
  253. # Assign the selected circuit (if any)
  254. self.instance.member = self.cleaned_data.get('member')
  255. class VirtualCircuitTypeForm(OrganizationalModelForm):
  256. fieldsets = (
  257. FieldSet('name', 'slug', 'color', 'description', 'tags'),
  258. )
  259. class Meta:
  260. model = VirtualCircuitType
  261. fields = [
  262. 'name', 'slug', 'color', 'description', 'owner', 'comments', 'tags',
  263. ]
  264. class VirtualCircuitForm(TenancyForm, PrimaryModelForm):
  265. provider_network = DynamicModelChoiceField(
  266. label=_('Provider network'),
  267. queryset=ProviderNetwork.objects.all(),
  268. selector=True
  269. )
  270. provider_account = DynamicModelChoiceField(
  271. label=_('Provider account'),
  272. queryset=ProviderAccount.objects.all(),
  273. required=False
  274. )
  275. type = DynamicModelChoiceField(
  276. queryset=VirtualCircuitType.objects.all(),
  277. quick_add=True
  278. )
  279. fieldsets = (
  280. FieldSet(
  281. 'provider_network', 'provider_account', 'cid', 'type', 'status', 'description', 'tags',
  282. name=_('Virtual circuit'),
  283. ),
  284. FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
  285. )
  286. class Meta:
  287. model = VirtualCircuit
  288. fields = [
  289. 'cid', 'provider_network', 'provider_account', 'type', 'status', 'description', 'tenant_group', 'tenant',
  290. 'owner', 'comments', 'tags',
  291. ]
  292. class VirtualCircuitTerminationForm(NetBoxModelForm):
  293. virtual_circuit = DynamicModelChoiceField(
  294. label=_('Virtual circuit'),
  295. queryset=VirtualCircuit.objects.all(),
  296. selector=True
  297. )
  298. role = forms.ChoiceField(
  299. choices=VirtualCircuitTerminationRoleChoices,
  300. widget=HTMXSelect(),
  301. label=_('Role')
  302. )
  303. interface = DynamicModelChoiceField(
  304. label=_('Interface'),
  305. queryset=Interface.objects.all(),
  306. selector=True,
  307. query_params={
  308. 'kind': 'virtual',
  309. 'virtual_circuit_termination_id': 'null',
  310. },
  311. context={
  312. 'parent': 'device',
  313. }
  314. )
  315. fieldsets = (
  316. FieldSet('virtual_circuit', 'role', 'interface', 'description', 'tags'),
  317. )
  318. class Meta:
  319. model = VirtualCircuitTermination
  320. fields = [
  321. 'virtual_circuit', 'role', 'interface', 'description', 'tags',
  322. ]