model_forms.py 12 KB

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