mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 01:41:22 -06:00

* Fixes #19415: Increased Circuit/WirelessLink absolute distance upper limit Also adds form validation that provides a useful message to the user rather than a 500 error with potentially little information. * Include forgotten migration files * Remove unnecessary comments * Remove more unnecessary comments * Addresses PR feedback * Gah, remove django migration header comment * Clean up new has_field_errors mechanism, fix issue with ObjectAttribute * Address PR feedback, revert changes to render_fieldset template tag
390 lines
12 KiB
Python
390 lines
12 KiB
Python
from django import forms
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.core.exceptions import ObjectDoesNotExist
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from circuits.choices import (
|
|
CircuitCommitRateChoices, CircuitTerminationPortSpeedChoices, VirtualCircuitTerminationRoleChoices,
|
|
)
|
|
from circuits.constants import *
|
|
from circuits.models import *
|
|
from dcim.models import Interface, Site
|
|
from ipam.models import ASN
|
|
from netbox.forms import NetBoxModelForm
|
|
from tenancy.forms import TenancyForm
|
|
from utilities.forms import get_field_value
|
|
from utilities.forms.fields import (
|
|
CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField,
|
|
)
|
|
from utilities.forms.mixins import DistanceValidationMixin
|
|
from utilities.forms.rendering import FieldSet, InlineFields
|
|
from utilities.forms.widgets import DatePicker, HTMXSelect, NumberWithOptions
|
|
from utilities.templatetags.builtins.filters import bettertitle
|
|
|
|
__all__ = (
|
|
'CircuitForm',
|
|
'CircuitGroupAssignmentForm',
|
|
'CircuitGroupForm',
|
|
'CircuitTerminationForm',
|
|
'CircuitTypeForm',
|
|
'ProviderForm',
|
|
'ProviderAccountForm',
|
|
'ProviderNetworkForm',
|
|
'VirtualCircuitForm',
|
|
'VirtualCircuitTerminationForm',
|
|
'VirtualCircuitTypeForm',
|
|
)
|
|
|
|
|
|
class ProviderForm(NetBoxModelForm):
|
|
slug = SlugField()
|
|
asns = DynamicModelMultipleChoiceField(
|
|
queryset=ASN.objects.all(),
|
|
label=_('ASNs'),
|
|
required=False
|
|
)
|
|
comments = CommentField()
|
|
|
|
fieldsets = (
|
|
FieldSet('name', 'slug', 'asns', 'description', 'tags'),
|
|
)
|
|
|
|
class Meta:
|
|
model = Provider
|
|
fields = [
|
|
'name', 'slug', 'asns', 'description', 'comments', 'tags',
|
|
]
|
|
|
|
|
|
class ProviderAccountForm(NetBoxModelForm):
|
|
provider = DynamicModelChoiceField(
|
|
label=_('Provider'),
|
|
queryset=Provider.objects.all(),
|
|
selector=True,
|
|
quick_add=True
|
|
)
|
|
comments = CommentField()
|
|
|
|
class Meta:
|
|
model = ProviderAccount
|
|
fields = [
|
|
'provider', 'name', 'account', 'description', 'comments', 'tags',
|
|
]
|
|
|
|
|
|
class ProviderNetworkForm(NetBoxModelForm):
|
|
provider = DynamicModelChoiceField(
|
|
label=_('Provider'),
|
|
queryset=Provider.objects.all(),
|
|
selector=True,
|
|
quick_add=True
|
|
)
|
|
comments = CommentField()
|
|
|
|
fieldsets = (
|
|
FieldSet('provider', 'name', 'service_id', 'description', 'tags'),
|
|
)
|
|
|
|
class Meta:
|
|
model = ProviderNetwork
|
|
fields = [
|
|
'provider', 'name', 'service_id', 'description', 'comments', 'tags',
|
|
]
|
|
|
|
|
|
class CircuitTypeForm(NetBoxModelForm):
|
|
slug = SlugField()
|
|
|
|
fieldsets = (
|
|
FieldSet('name', 'slug', 'color', 'description', 'tags'),
|
|
)
|
|
|
|
class Meta:
|
|
model = CircuitType
|
|
fields = [
|
|
'name', 'slug', 'color', 'description', 'tags',
|
|
]
|
|
|
|
|
|
class CircuitForm(DistanceValidationMixin, TenancyForm, NetBoxModelForm):
|
|
provider = DynamicModelChoiceField(
|
|
label=_('Provider'),
|
|
queryset=Provider.objects.all(),
|
|
selector=True,
|
|
quick_add=True
|
|
)
|
|
provider_account = DynamicModelChoiceField(
|
|
label=_('Provider account'),
|
|
queryset=ProviderAccount.objects.all(),
|
|
required=False,
|
|
query_params={
|
|
'provider_id': '$provider',
|
|
}
|
|
)
|
|
type = DynamicModelChoiceField(
|
|
queryset=CircuitType.objects.all(),
|
|
quick_add=True
|
|
)
|
|
comments = CommentField()
|
|
|
|
fieldsets = (
|
|
FieldSet(
|
|
'provider',
|
|
'provider_account',
|
|
'cid',
|
|
'type',
|
|
'status',
|
|
InlineFields('distance', 'distance_unit', label=_('Distance')),
|
|
'description',
|
|
'tags',
|
|
name=_('Circuit')
|
|
),
|
|
FieldSet('install_date', 'termination_date', 'commit_rate', name=_('Service Parameters')),
|
|
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
|
|
)
|
|
|
|
class Meta:
|
|
model = Circuit
|
|
fields = [
|
|
'cid', 'type', 'provider', 'provider_account', 'status', 'install_date', 'termination_date', 'commit_rate',
|
|
'distance', 'distance_unit', 'description', 'tenant_group', 'tenant', 'comments', 'tags',
|
|
]
|
|
widgets = {
|
|
'install_date': DatePicker(),
|
|
'termination_date': DatePicker(),
|
|
'commit_rate': NumberWithOptions(
|
|
options=CircuitCommitRateChoices
|
|
),
|
|
}
|
|
|
|
|
|
class CircuitTerminationForm(NetBoxModelForm):
|
|
circuit = DynamicModelChoiceField(
|
|
label=_('Circuit'),
|
|
queryset=Circuit.objects.all(),
|
|
selector=True
|
|
)
|
|
termination_type = ContentTypeChoiceField(
|
|
queryset=ContentType.objects.filter(model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES),
|
|
widget=HTMXSelect(),
|
|
required=False,
|
|
label=_('Termination type')
|
|
)
|
|
termination = DynamicModelChoiceField(
|
|
label=_('Termination'),
|
|
queryset=Site.objects.none(), # Initial queryset
|
|
required=False,
|
|
disabled=True,
|
|
selector=True
|
|
)
|
|
|
|
fieldsets = (
|
|
FieldSet(
|
|
'circuit', 'term_side', 'description', 'tags',
|
|
'termination_type', 'termination',
|
|
'mark_connected', name=_('Circuit Termination')
|
|
),
|
|
FieldSet('port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', name=_('Termination Details')),
|
|
)
|
|
|
|
class Meta:
|
|
model = CircuitTermination
|
|
fields = [
|
|
'circuit', 'term_side', 'termination_type', 'mark_connected', 'port_speed', 'upstream_speed',
|
|
'xconnect_id', 'pp_info', 'description', 'tags',
|
|
]
|
|
widgets = {
|
|
'port_speed': NumberWithOptions(
|
|
options=CircuitTerminationPortSpeedChoices
|
|
),
|
|
'upstream_speed': NumberWithOptions(
|
|
options=CircuitTerminationPortSpeedChoices
|
|
),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
instance = kwargs.get('instance')
|
|
initial = kwargs.get('initial', {})
|
|
|
|
if instance is not None and instance.termination:
|
|
initial['termination'] = instance.termination
|
|
kwargs['initial'] = initial
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
if termination_type_id := get_field_value(self, 'termination_type'):
|
|
try:
|
|
termination_type = ContentType.objects.get(pk=termination_type_id)
|
|
model = termination_type.model_class()
|
|
self.fields['termination'].queryset = model.objects.all()
|
|
self.fields['termination'].widget.attrs['selector'] = model._meta.label_lower
|
|
self.fields['termination'].disabled = False
|
|
self.fields['termination'].label = _(bettertitle(model._meta.verbose_name))
|
|
except ObjectDoesNotExist:
|
|
pass
|
|
|
|
if self.instance and termination_type_id != self.instance.termination_type_id:
|
|
self.initial['termination'] = None
|
|
|
|
def clean(self):
|
|
super().clean()
|
|
|
|
# Assign the selected termination (if any)
|
|
self.instance.termination = self.cleaned_data.get('termination')
|
|
|
|
|
|
class CircuitGroupForm(TenancyForm, NetBoxModelForm):
|
|
slug = SlugField()
|
|
|
|
fieldsets = (
|
|
FieldSet('name', 'slug', 'description', 'tags', name=_('Circuit Group')),
|
|
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
|
|
)
|
|
|
|
class Meta:
|
|
model = CircuitGroup
|
|
fields = [
|
|
'name', 'slug', 'description', 'tenant_group', 'tenant', 'tags',
|
|
]
|
|
|
|
|
|
class CircuitGroupAssignmentForm(NetBoxModelForm):
|
|
group = DynamicModelChoiceField(
|
|
label=_('Group'),
|
|
queryset=CircuitGroup.objects.all(),
|
|
)
|
|
member_type = ContentTypeChoiceField(
|
|
queryset=ContentType.objects.filter(CIRCUIT_GROUP_ASSIGNMENT_MEMBER_MODELS),
|
|
widget=HTMXSelect(),
|
|
required=False,
|
|
label=_('Circuit type')
|
|
)
|
|
member = DynamicModelChoiceField(
|
|
label=_('Circuit'),
|
|
queryset=Circuit.objects.none(), # Initial queryset
|
|
required=False,
|
|
disabled=True,
|
|
selector=True
|
|
)
|
|
|
|
fieldsets = (
|
|
FieldSet('group', 'member_type', 'member', 'priority', 'tags', name=_('Group Assignment')),
|
|
)
|
|
|
|
class Meta:
|
|
model = CircuitGroupAssignment
|
|
fields = [
|
|
'group', 'member_type', 'priority', 'tags',
|
|
]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
instance = kwargs.get('instance')
|
|
initial = kwargs.get('initial', {})
|
|
|
|
if instance is not None and instance.member:
|
|
initial['member'] = instance.member
|
|
kwargs['initial'] = initial
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
if member_type_id := get_field_value(self, 'member_type'):
|
|
try:
|
|
model = ContentType.objects.get(pk=member_type_id).model_class()
|
|
self.fields['member'].queryset = model.objects.all()
|
|
self.fields['member'].widget.attrs['selector'] = model._meta.label_lower
|
|
self.fields['member'].disabled = False
|
|
self.fields['member'].label = _(bettertitle(model._meta.verbose_name))
|
|
except ObjectDoesNotExist:
|
|
pass
|
|
|
|
if self.instance.pk and member_type_id != self.instance.member_type_id:
|
|
self.initial['member'] = None
|
|
|
|
def clean(self):
|
|
super().clean()
|
|
|
|
# Assign the selected circuit (if any)
|
|
self.instance.member = self.cleaned_data.get('member')
|
|
|
|
|
|
class VirtualCircuitTypeForm(NetBoxModelForm):
|
|
slug = SlugField()
|
|
|
|
fieldsets = (
|
|
FieldSet('name', 'slug', 'color', 'description', 'tags'),
|
|
)
|
|
|
|
class Meta:
|
|
model = VirtualCircuitType
|
|
fields = [
|
|
'name', 'slug', 'color', 'description', 'tags',
|
|
]
|
|
|
|
|
|
class VirtualCircuitForm(TenancyForm, NetBoxModelForm):
|
|
provider_network = DynamicModelChoiceField(
|
|
label=_('Provider network'),
|
|
queryset=ProviderNetwork.objects.all(),
|
|
selector=True
|
|
)
|
|
provider_account = DynamicModelChoiceField(
|
|
label=_('Provider account'),
|
|
queryset=ProviderAccount.objects.all(),
|
|
required=False
|
|
)
|
|
type = DynamicModelChoiceField(
|
|
queryset=VirtualCircuitType.objects.all(),
|
|
quick_add=True
|
|
)
|
|
comments = CommentField()
|
|
|
|
fieldsets = (
|
|
FieldSet(
|
|
'provider_network', 'provider_account', 'cid', 'type', 'status', 'description', 'tags',
|
|
name=_('Virtual circuit'),
|
|
),
|
|
FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
|
|
)
|
|
|
|
class Meta:
|
|
model = VirtualCircuit
|
|
fields = [
|
|
'cid', 'provider_network', 'provider_account', 'type', 'status', 'description', 'tenant_group', 'tenant',
|
|
'comments', 'tags',
|
|
]
|
|
|
|
|
|
class VirtualCircuitTerminationForm(NetBoxModelForm):
|
|
virtual_circuit = DynamicModelChoiceField(
|
|
label=_('Virtual circuit'),
|
|
queryset=VirtualCircuit.objects.all(),
|
|
selector=True
|
|
)
|
|
role = forms.ChoiceField(
|
|
choices=VirtualCircuitTerminationRoleChoices,
|
|
widget=HTMXSelect(),
|
|
label=_('Role')
|
|
)
|
|
interface = DynamicModelChoiceField(
|
|
label=_('Interface'),
|
|
queryset=Interface.objects.all(),
|
|
selector=True,
|
|
query_params={
|
|
'kind': 'virtual',
|
|
'virtual_circuit_termination_id': 'null',
|
|
},
|
|
context={
|
|
'parent': 'device',
|
|
}
|
|
)
|
|
|
|
fieldsets = (
|
|
FieldSet('virtual_circuit', 'role', 'interface', 'description', 'tags'),
|
|
)
|
|
|
|
class Meta:
|
|
model = VirtualCircuitTermination
|
|
fields = [
|
|
'virtual_circuit', 'role', 'interface', 'description', 'tags',
|
|
]
|