mirror of
https://github.com/netbox-community/netbox.git
synced 2026-03-24 10:22:07 +01:00
9604 Add Termination to CircuitTermination (#17821)
* 9604 add scope type to CircuitTermination * 9604 add scope type to CircuitTermination * 9604 add scope type to CircuitTermination * 9604 model_forms * 9604 form filtersets * 9604 form bulk_import * 9604 form bulk_edit * 9604 serializers * 9604 graphql * 9604 tests and detail template * 9604 fix migration merge * 9604 fix tests * 9604 fix tests * 9604 fix table * 9604 updates * fix tests * fix tests * fix tests * fix tests * fix tests * fix tests * fix tests * 9604 remove provider_network * 9604 fix tests * 9604 fix tests * 9604 fix forms * 9604 review changes * 9604 scope->termination * 9604 fix _circuit_terminations * 9604 fix _circuit_terminations * 9604 sitegroup -> site_group * 9604 update docs * 9604 fix form termination side reference * Misc cleanup * Fix terminations in circuits table * Fix missing imports * Clean up termination attrs display * Add termination & type to CircuitTerminationTable * Update cable tracing logic --------- Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
This commit is contained in:
@@ -1,17 +1,23 @@
|
||||
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, CircuitPriorityChoices, CircuitStatusChoices
|
||||
from circuits.constants import CIRCUIT_TERMINATION_TERMINATION_TYPES
|
||||
from circuits.models import *
|
||||
from dcim.models import Site
|
||||
from ipam.models import ASN
|
||||
from netbox.choices import DistanceUnitChoices
|
||||
from netbox.forms import NetBoxModelBulkEditForm
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms import add_blank_choice
|
||||
from utilities.forms.fields import ColorField, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField
|
||||
from utilities.forms.rendering import FieldSet, TabbedGroups
|
||||
from utilities.forms.widgets import BulkEditNullBooleanSelect, DatePicker, NumberWithOptions
|
||||
from utilities.forms import add_blank_choice, get_field_value
|
||||
from utilities.forms.fields import (
|
||||
ColorField, CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
|
||||
)
|
||||
from utilities.forms.rendering import FieldSet
|
||||
from utilities.forms.widgets import BulkEditNullBooleanSelect, DatePicker, HTMXSelect, NumberWithOptions
|
||||
from utilities.templatetags.builtins.filters import bettertitle
|
||||
|
||||
__all__ = (
|
||||
'CircuitBulkEditForm',
|
||||
@@ -197,15 +203,18 @@ class CircuitTerminationBulkEditForm(NetBoxModelBulkEditForm):
|
||||
max_length=200,
|
||||
required=False
|
||||
)
|
||||
site = DynamicModelChoiceField(
|
||||
label=_('Site'),
|
||||
queryset=Site.objects.all(),
|
||||
required=False
|
||||
termination_type = ContentTypeChoiceField(
|
||||
queryset=ContentType.objects.filter(model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES),
|
||||
widget=HTMXSelect(method='post', attrs={'hx-select': '#form_fields'}),
|
||||
required=False,
|
||||
label=_('Termination type')
|
||||
)
|
||||
provider_network = DynamicModelChoiceField(
|
||||
label=_('Provider Network'),
|
||||
queryset=ProviderNetwork.objects.all(),
|
||||
required=False
|
||||
termination = DynamicModelChoiceField(
|
||||
label=_('Termination'),
|
||||
queryset=Site.objects.none(), # Initial queryset
|
||||
required=False,
|
||||
disabled=True,
|
||||
selector=True
|
||||
)
|
||||
port_speed = forms.IntegerField(
|
||||
required=False,
|
||||
@@ -225,15 +234,26 @@ class CircuitTerminationBulkEditForm(NetBoxModelBulkEditForm):
|
||||
fieldsets = (
|
||||
FieldSet(
|
||||
'description',
|
||||
TabbedGroups(
|
||||
FieldSet('site', name=_('Site')),
|
||||
FieldSet('provider_network', name=_('Provider Network')),
|
||||
),
|
||||
'termination_type', 'termination',
|
||||
'mark_connected', name=_('Circuit Termination')
|
||||
),
|
||||
FieldSet('port_speed', 'upstream_speed', name=_('Termination Details')),
|
||||
)
|
||||
nullable_fields = ('description')
|
||||
nullable_fields = ('description', 'termination')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
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
|
||||
|
||||
|
||||
class CircuitGroupBulkEditForm(NetBoxModelBulkEditForm):
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
from django import forms
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from circuits.choices import *
|
||||
from circuits.constants import *
|
||||
from circuits.models import *
|
||||
from dcim.models import Site
|
||||
from netbox.choices import DistanceUnitChoices
|
||||
from netbox.forms import NetBoxModelImportForm
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, SlugField
|
||||
from utilities.forms.fields import CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, SlugField
|
||||
|
||||
__all__ = (
|
||||
'CircuitImportForm',
|
||||
@@ -127,17 +128,10 @@ class BaseCircuitTerminationImportForm(forms.ModelForm):
|
||||
label=_('Termination'),
|
||||
choices=CircuitTerminationSideChoices,
|
||||
)
|
||||
site = CSVModelChoiceField(
|
||||
label=_('Site'),
|
||||
queryset=Site.objects.all(),
|
||||
to_field_name='name',
|
||||
required=False
|
||||
)
|
||||
provider_network = CSVModelChoiceField(
|
||||
label=_('Provider network'),
|
||||
queryset=ProviderNetwork.objects.all(),
|
||||
to_field_name='name',
|
||||
required=False
|
||||
termination_type = CSVContentTypeField(
|
||||
queryset=ContentType.objects.filter(model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES),
|
||||
required=False,
|
||||
label=_('Termination type (app & model)')
|
||||
)
|
||||
|
||||
|
||||
@@ -145,9 +139,12 @@ class CircuitTerminationImportRelatedForm(BaseCircuitTerminationImportForm):
|
||||
class Meta:
|
||||
model = CircuitTermination
|
||||
fields = [
|
||||
'circuit', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id',
|
||||
'circuit', 'term_side', 'termination_type', 'termination_id', 'port_speed', 'upstream_speed', 'xconnect_id',
|
||||
'pp_info', 'description'
|
||||
]
|
||||
labels = {
|
||||
'termination_id': _('Termination ID'),
|
||||
}
|
||||
|
||||
|
||||
class CircuitTerminationImportForm(NetBoxModelImportForm, BaseCircuitTerminationImportForm):
|
||||
@@ -155,9 +152,12 @@ class CircuitTerminationImportForm(NetBoxModelImportForm, BaseCircuitTermination
|
||||
class Meta:
|
||||
model = CircuitTermination
|
||||
fields = [
|
||||
'circuit', 'term_side', 'site', 'provider_network', 'port_speed', 'upstream_speed', 'xconnect_id',
|
||||
'circuit', 'term_side', 'termination_type', 'termination_id', 'port_speed', 'upstream_speed', 'xconnect_id',
|
||||
'pp_info', 'description', 'tags'
|
||||
]
|
||||
labels = {
|
||||
'termination_id': _('Termination ID'),
|
||||
}
|
||||
|
||||
|
||||
class CircuitGroupImportForm(NetBoxModelImportForm):
|
||||
|
||||
@@ -3,7 +3,7 @@ from django.utils.translation import gettext as _
|
||||
|
||||
from circuits.choices import CircuitCommitRateChoices, CircuitPriorityChoices, CircuitStatusChoices, CircuitTerminationSideChoices
|
||||
from circuits.models import *
|
||||
from dcim.models import Region, Site, SiteGroup
|
||||
from dcim.models import Location, Region, Site, SiteGroup
|
||||
from ipam.models import ASN
|
||||
from netbox.choices import DistanceUnitChoices
|
||||
from netbox.forms import NetBoxModelFilterSetForm
|
||||
@@ -207,18 +207,29 @@ class CircuitTerminationFilterForm(NetBoxModelFilterSetForm):
|
||||
fieldsets = (
|
||||
FieldSet('q', 'filter_id', 'tag'),
|
||||
FieldSet('circuit_id', 'term_side', name=_('Circuit')),
|
||||
FieldSet('provider_id', 'provider_network_id', name=_('Provider')),
|
||||
FieldSet('region_id', 'site_group_id', 'site_id', name=_('Location')),
|
||||
FieldSet('provider_id', name=_('Provider')),
|
||||
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Termination')),
|
||||
)
|
||||
region_id = DynamicModelMultipleChoiceField(
|
||||
queryset=Region.objects.all(),
|
||||
required=False,
|
||||
label=_('Region')
|
||||
)
|
||||
site_group_id = DynamicModelMultipleChoiceField(
|
||||
queryset=SiteGroup.objects.all(),
|
||||
required=False,
|
||||
label=_('Site group')
|
||||
)
|
||||
site_id = DynamicModelMultipleChoiceField(
|
||||
queryset=Site.objects.all(),
|
||||
required=False,
|
||||
query_params={
|
||||
'region_id': '$region_id',
|
||||
'site_group_id': '$site_group_id',
|
||||
},
|
||||
label=_('Site')
|
||||
)
|
||||
location_id = DynamicModelMultipleChoiceField(
|
||||
queryset=Location.objects.all(),
|
||||
required=False,
|
||||
label=_('Location')
|
||||
)
|
||||
circuit_id = DynamicModelMultipleChoiceField(
|
||||
queryset=Circuit.objects.all(),
|
||||
required=False,
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
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
|
||||
from circuits.constants import *
|
||||
from circuits.models import *
|
||||
from dcim.models import Site
|
||||
from ipam.models import ASN
|
||||
from netbox.forms import NetBoxModelForm
|
||||
from tenancy.forms import TenancyForm
|
||||
from utilities.forms.fields import CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField
|
||||
from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups
|
||||
from utilities.forms.widgets import DatePicker, NumberWithOptions
|
||||
from utilities.forms import get_field_value
|
||||
from utilities.forms.fields import CommentField, ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField
|
||||
from utilities.forms.rendering import FieldSet, InlineFields
|
||||
from utilities.forms.widgets import DatePicker, HTMXSelect, NumberWithOptions
|
||||
from utilities.templatetags.builtins.filters import bettertitle
|
||||
|
||||
__all__ = (
|
||||
'CircuitForm',
|
||||
@@ -144,26 +149,24 @@ class CircuitTerminationForm(NetBoxModelForm):
|
||||
queryset=Circuit.objects.all(),
|
||||
selector=True
|
||||
)
|
||||
site = DynamicModelChoiceField(
|
||||
label=_('Site'),
|
||||
queryset=Site.objects.all(),
|
||||
termination_type = ContentTypeChoiceField(
|
||||
queryset=ContentType.objects.filter(model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES),
|
||||
widget=HTMXSelect(),
|
||||
required=False,
|
||||
selector=True
|
||||
label=_('Termination type')
|
||||
)
|
||||
provider_network = DynamicModelChoiceField(
|
||||
label=_('Provider network'),
|
||||
queryset=ProviderNetwork.objects.all(),
|
||||
termination = DynamicModelChoiceField(
|
||||
label=_('Termination'),
|
||||
queryset=Site.objects.none(), # Initial queryset
|
||||
required=False,
|
||||
disabled=True,
|
||||
selector=True
|
||||
)
|
||||
|
||||
fieldsets = (
|
||||
FieldSet(
|
||||
'circuit', 'term_side', 'description', 'tags',
|
||||
TabbedGroups(
|
||||
FieldSet('site', name=_('Site')),
|
||||
FieldSet('provider_network', name=_('Provider Network')),
|
||||
),
|
||||
'termination_type', 'termination',
|
||||
'mark_connected', name=_('Circuit Termination')
|
||||
),
|
||||
FieldSet('port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', name=_('Termination Details')),
|
||||
@@ -172,7 +175,7 @@ class CircuitTerminationForm(NetBoxModelForm):
|
||||
class Meta:
|
||||
model = CircuitTermination
|
||||
fields = [
|
||||
'circuit', 'term_side', 'site', 'provider_network', 'mark_connected', 'port_speed', 'upstream_speed',
|
||||
'circuit', 'term_side', 'termination_type', 'mark_connected', 'port_speed', 'upstream_speed',
|
||||
'xconnect_id', 'pp_info', 'description', 'tags',
|
||||
]
|
||||
widgets = {
|
||||
@@ -184,6 +187,36 @@ class CircuitTerminationForm(NetBoxModelForm):
|
||||
),
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user