Coverage for circuits/forms/model_forms.py: 88%
108 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
1from django.contrib.contenttypes.models import ContentType
2from django.utils.translation import gettext_lazy as _
4from circuits.choices import (
5 CircuitCommitRateChoices,
6 CircuitPriorityChoices,
7 CircuitStatusChoices,
8 CircuitTerminationPortSpeedChoices,
9 CircuitTerminationSideChoices,
10 VirtualCircuitTerminationRoleChoices,
11)
12from circuits.constants import *
13from circuits.models import *
14from dcim.models import Interface
15from ipam.models import ASN
16from netbox.choices import DistanceUnitChoices
17from netbox.forms import NetBoxModelForm, OrganizationalModelForm, PrimaryModelForm
18from tenancy.forms import TenancyForm
19from utilities.forms import GenericObjectFormMixin, add_blank_choice
20from utilities.forms.fields import (
21 ChoiceField,
22 DynamicModelChoiceField,
23 DynamicModelMultipleChoiceField,
24 GenericObjectChoiceField,
25 SlugField,
26 TypedChoiceField,
27)
28from utilities.forms.mixins import DistanceValidationMixin
29from utilities.forms.rendering import FieldSet, InlineFields, M2MAddRemoveFields
30from utilities.forms.widgets import DatePicker, NumberWithOptions
32__all__ = (
33 'CircuitForm',
34 'CircuitGroupAssignmentForm',
35 'CircuitGroupForm',
36 'CircuitTerminationForm',
37 'CircuitTypeForm',
38 'ProviderAccountForm',
39 'ProviderForm',
40 'ProviderNetworkForm',
41 'VirtualCircuitForm',
42 'VirtualCircuitTerminationForm',
43 'VirtualCircuitTypeForm',
44)
47class ProviderForm(PrimaryModelForm):
48 slug = SlugField()
49 asns = DynamicModelMultipleChoiceField(
50 queryset=ASN.objects.all(),
51 label=_('ASNs'),
52 required=False
53 )
54 add_asns = DynamicModelMultipleChoiceField(
55 queryset=ASN.objects.all(),
56 label=_('Add ASNs'),
57 required=False
58 )
59 remove_asns = DynamicModelMultipleChoiceField(
60 queryset=ASN.objects.all(),
61 label=_('Remove ASNs'),
62 required=False
63 )
65 fieldsets = (
66 FieldSet('name', 'slug', M2MAddRemoveFields('asns'), 'description', 'tags'),
67 )
69 class Meta:
70 model = Provider
71 fields = [
72 'name', 'slug', 'description', 'owner', 'comments', 'tags',
73 ]
75 def __init__(self, *args, **kwargs):
76 super().__init__(*args, **kwargs)
77 if self.instance.pk and (count := self.instance.asns.count()) >= M2MAddRemoveFields.THRESHOLD:
78 # Add/remove mode for large M2M sets
79 self.fields.pop('asns')
80 self.fields['add_asns'].widget.add_query_param('provider_id__n', self.instance.pk)
81 self.fields['remove_asns'].widget.add_query_param('provider_id', self.instance.pk)
82 self.fields['remove_asns'].help_text = _("{count} ASNs currently assigned").format(count=count)
83 else:
84 # Simple mode for new objects or small M2M sets
85 self.fields.pop('add_asns')
86 self.fields.pop('remove_asns')
87 if self.instance.pk:
88 self.initial['asns'] = list(self.instance.asns.values_list('pk', flat=True))
91class ProviderAccountForm(PrimaryModelForm):
92 provider = DynamicModelChoiceField(
93 label=_('Provider'),
94 queryset=Provider.objects.all(),
95 selector=True,
96 quick_add=True
97 )
99 fieldsets = (
100 FieldSet('provider', 'account', 'name', 'description', 'tags'),
101 )
103 class Meta:
104 model = ProviderAccount
105 fields = [
106 'provider', 'account', 'name', 'description', 'owner', 'comments', 'tags',
107 ]
110class ProviderNetworkForm(PrimaryModelForm):
111 provider = DynamicModelChoiceField(
112 label=_('Provider'),
113 queryset=Provider.objects.all(),
114 selector=True,
115 quick_add=True
116 )
118 fieldsets = (
119 FieldSet('provider', 'name', 'service_id', 'description', 'tags'),
120 )
122 class Meta:
123 model = ProviderNetwork
124 fields = [
125 'provider', 'name', 'service_id', 'description', 'owner', 'comments', 'tags',
126 ]
129class CircuitTypeForm(OrganizationalModelForm):
130 fieldsets = (
131 FieldSet('name', 'slug', 'color', 'description', 'tags'),
132 )
134 class Meta:
135 model = CircuitType
136 fields = [
137 'name', 'slug', 'color', 'description', 'owner', 'comments', 'tags',
138 ]
141class CircuitForm(DistanceValidationMixin, TenancyForm, PrimaryModelForm):
142 status = ChoiceField(
143 label=_('Status'),
144 choices=CircuitStatusChoices,
145 initial=CircuitStatusChoices.STATUS_ACTIVE,
146 )
147 distance_unit = TypedChoiceField(
148 label=_('Distance unit'),
149 choices=add_blank_choice(DistanceUnitChoices),
150 required=False,
151 )
152 provider = DynamicModelChoiceField(
153 label=_('Provider'),
154 queryset=Provider.objects.all(),
155 selector=True,
156 quick_add=True
157 )
158 provider_account = DynamicModelChoiceField(
159 label=_('Provider account'),
160 queryset=ProviderAccount.objects.all(),
161 required=False,
162 query_params={
163 'provider_id': '$provider',
164 }
165 )
166 type = DynamicModelChoiceField(
167 queryset=CircuitType.objects.all(),
168 quick_add=True
169 )
171 fieldsets = (
172 FieldSet(
173 'provider',
174 'provider_account',
175 'cid',
176 'type',
177 'status',
178 InlineFields('distance', 'distance_unit', label=_('Distance')),
179 'description',
180 'tags',
181 name=_('Circuit')
182 ),
183 FieldSet('install_date', 'termination_date', 'commit_rate', name=_('Service Parameters')),
184 FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
185 )
187 class Meta:
188 model = Circuit
189 fields = [
190 'cid', 'type', 'provider', 'provider_account', 'status', 'install_date', 'termination_date', 'commit_rate',
191 'distance', 'distance_unit', 'description', 'tenant_group', 'tenant', 'owner', 'comments', 'tags',
192 ]
193 widgets = {
194 'install_date': DatePicker(),
195 'termination_date': DatePicker(),
196 'commit_rate': NumberWithOptions(
197 options=CircuitCommitRateChoices
198 ),
199 }
202class CircuitTerminationForm(GenericObjectFormMixin, NetBoxModelForm):
203 term_side = ChoiceField(
204 label=_('Termination side'),
205 choices=CircuitTerminationSideChoices,
206 )
207 circuit = DynamicModelChoiceField(
208 label=_('Circuit'),
209 queryset=Circuit.objects.all(),
210 selector=True
211 )
212 termination = GenericObjectChoiceField(
213 label=_('Termination'),
214 content_type_queryset=ContentType.objects.filter(model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES),
215 required=True,
216 selector=True,
217 hx_target_id='circuit-termination',
218 )
220 fieldsets = (
221 FieldSet(
222 'circuit', 'term_side', 'description', 'tags', 'termination', 'mark_connected',
223 name=_('Circuit Termination'), html_id='circuit-termination',
224 ),
225 FieldSet('port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', name=_('Termination Details')),
226 )
228 class Meta:
229 model = CircuitTermination
230 fields = [
231 'circuit', 'term_side', 'mark_connected', 'port_speed', 'upstream_speed',
232 'xconnect_id', 'pp_info', 'description', 'tags',
233 ]
234 widgets = {
235 'port_speed': NumberWithOptions(
236 options=CircuitTerminationPortSpeedChoices
237 ),
238 'upstream_speed': NumberWithOptions(
239 options=CircuitTerminationPortSpeedChoices
240 ),
241 }
244class CircuitGroupForm(TenancyForm, OrganizationalModelForm):
245 fieldsets = (
246 FieldSet('name', 'slug', 'description', 'tags', name=_('Circuit Group')),
247 FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
248 )
250 class Meta:
251 model = CircuitGroup
252 fields = [
253 'name', 'slug', 'description', 'tenant_group', 'tenant', 'owner', 'comments', 'tags',
254 ]
257class CircuitGroupAssignmentForm(GenericObjectFormMixin, NetBoxModelForm):
258 priority = TypedChoiceField(
259 label=_('Priority'),
260 choices=add_blank_choice(CircuitPriorityChoices),
261 required=False,
262 )
263 group = DynamicModelChoiceField(
264 label=_('Group'),
265 queryset=CircuitGroup.objects.all(),
266 )
267 member = GenericObjectChoiceField(
268 label=_('Member'),
269 content_type_queryset=ContentType.objects.filter(CIRCUIT_GROUP_ASSIGNMENT_MEMBER_MODELS),
270 required=False,
271 selector=True,
272 hx_target_id='circuit-group-assignment',
273 )
275 fieldsets = (
276 FieldSet(
277 'group', 'member', 'priority', 'tags',
278 name=_('Group Assignment'), html_id='circuit-group-assignment',
279 ),
280 )
282 class Meta:
283 model = CircuitGroupAssignment
284 fields = [
285 'group', 'priority', 'tags',
286 ]
289class VirtualCircuitTypeForm(OrganizationalModelForm):
290 fieldsets = (
291 FieldSet('name', 'slug', 'color', 'description', 'tags'),
292 )
294 class Meta:
295 model = VirtualCircuitType
296 fields = [
297 'name', 'slug', 'color', 'description', 'owner', 'comments', 'tags',
298 ]
301class VirtualCircuitForm(TenancyForm, PrimaryModelForm):
302 status = ChoiceField(
303 label=_('Status'),
304 choices=CircuitStatusChoices,
305 initial=CircuitStatusChoices.STATUS_ACTIVE,
306 )
307 provider_network = DynamicModelChoiceField(
308 label=_('Provider network'),
309 queryset=ProviderNetwork.objects.all(),
310 selector=True
311 )
312 provider_account = DynamicModelChoiceField(
313 label=_('Provider account'),
314 queryset=ProviderAccount.objects.all(),
315 required=False
316 )
317 type = DynamicModelChoiceField(
318 queryset=VirtualCircuitType.objects.all(),
319 quick_add=True
320 )
322 fieldsets = (
323 FieldSet(
324 'provider_network', 'provider_account', 'cid', 'type', 'status', 'description', 'tags',
325 name=_('Virtual circuit'),
326 ),
327 FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
328 )
330 class Meta:
331 model = VirtualCircuit
332 fields = [
333 'cid', 'provider_network', 'provider_account', 'type', 'status', 'description', 'tenant_group', 'tenant',
334 'owner', 'comments', 'tags',
335 ]
338class VirtualCircuitTerminationForm(NetBoxModelForm):
339 virtual_circuit = DynamicModelChoiceField(
340 label=_('Virtual circuit'),
341 queryset=VirtualCircuit.objects.all(),
342 selector=True
343 )
344 role = ChoiceField(
345 choices=VirtualCircuitTerminationRoleChoices,
346 label=_('Role')
347 )
348 interface = DynamicModelChoiceField(
349 label=_('Interface'),
350 queryset=Interface.objects.all(),
351 selector=True,
352 query_params={
353 'kind': 'virtual',
354 'virtual_circuit_termination_id': 'null',
355 },
356 context={
357 'parent': 'device',
358 }
359 )
361 fieldsets = (
362 FieldSet(
363 'virtual_circuit', 'role', 'interface', 'description', 'tags',
364 ),
365 )
367 class Meta:
368 model = VirtualCircuitTermination
369 fields = [
370 'virtual_circuit', 'role', 'interface', 'description', 'tags',
371 ]