Skip to content

Commit

Permalink
Closes #18153: Introduce virtual circuit types
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremystretch committed Jan 3, 2025
1 parent c3b0de3 commit b78d442
Show file tree
Hide file tree
Showing 28 changed files with 639 additions and 48 deletions.
4 changes: 4 additions & 0 deletions docs/models/circuits/virtualcircuit.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ The [provider account](./provideraccount.md) with which the virtual circuit is a

The unique identifier assigned to the virtual circuit by its [provider](./provider.md).

### Type

The assigned [virtual circuit type](./virtualcircuittype.md).

### Status

The operational status of the virtual circuit. By default, the following statuses are available:
Expand Down
13 changes: 13 additions & 0 deletions docs/models/circuits/virtualcircuittype.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Virtual Circuit Types

Like physical [circuits](./circuit.md), [virtual circuits](./virtualcircuit.md) are classified by functional type. These types are completely customizable, and can help categorize circuits by function or technology.

## Fields

### Name

A unique human-friendly name.

### Slug

A unique URL-friendly identifier. (This value can be used for filtering.)
22 changes: 19 additions & 3 deletions netbox/circuits/api/serializers_/circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from circuits.constants import CIRCUIT_GROUP_ASSIGNMENT_MEMBER_MODELS, CIRCUIT_TERMINATION_TERMINATION_TYPES
from circuits.models import (
Circuit, CircuitGroup, CircuitGroupAssignment, CircuitTermination, CircuitType, VirtualCircuit,
VirtualCircuitTermination,
VirtualCircuitTermination, VirtualCircuitType,
)
from dcim.api.serializers_.device_components import InterfaceSerializer
from dcim.api.serializers_.cables import CabledObjectSerializer
Expand All @@ -25,6 +25,7 @@
'CircuitTypeSerializer',
'VirtualCircuitSerializer',
'VirtualCircuitTerminationSerializer',
'VirtualCircuitTypeSerializer',
)


Expand Down Expand Up @@ -175,17 +176,32 @@ def get_member(self, obj):
return serializer(obj.member, nested=True, context=context).data


class VirtualCircuitTypeSerializer(NetBoxModelSerializer):

# Related object counts
virtual_circuit_count = RelatedObjectCountField('virtual_circuits')

class Meta:
model = VirtualCircuitType
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'color', 'description', 'tags', 'custom_fields',
'created', 'last_updated', 'virtual_circuit_count',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'virtual_circuit_count')


class VirtualCircuitSerializer(NetBoxModelSerializer):
provider_network = ProviderNetworkSerializer(nested=True)
provider_account = ProviderAccountSerializer(nested=True, required=False, allow_null=True, default=None)
type = VirtualCircuitTypeSerializer(nested=True)
status = ChoiceField(choices=CircuitStatusChoices, required=False)
tenant = TenantSerializer(nested=True, required=False, allow_null=True)

class Meta:
model = VirtualCircuit
fields = [
'id', 'url', 'display_url', 'display', 'cid', 'provider_network', 'provider_account', 'status', 'tenant',
'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
'id', 'url', 'display_url', 'display', 'cid', 'provider_network', 'provider_account', 'type', 'status',
'tenant', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
]
brief_fields = ('id', 'url', 'display', 'provider_network', 'cid', 'description')

Expand Down
1 change: 1 addition & 0 deletions netbox/circuits/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

# Virtual circuits
router.register('virtual-circuits', views.VirtualCircuitViewSet)
router.register('virtual-circuit-types', views.VirtualCircuitTypeViewSet)
router.register('virtual-circuit-terminations', views.VirtualCircuitTerminationViewSet)

app_name = 'circuits-api'
Expand Down
10 changes: 10 additions & 0 deletions netbox/circuits/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@ class ProviderNetworkViewSet(NetBoxModelViewSet):
filterset_class = filtersets.ProviderNetworkFilterSet


#
# Virtual circuit types
#

class VirtualCircuitTypeViewSet(NetBoxModelViewSet):
queryset = VirtualCircuitType.objects.all()
serializer_class = serializers.VirtualCircuitTypeSerializer
filterset_class = filtersets.VirtualCircuitTypeFilterSet


#
# Virtual circuits
#
Expand Down
18 changes: 18 additions & 0 deletions netbox/circuits/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
'ProviderFilterSet',
'VirtualCircuitFilterSet',
'VirtualCircuitTerminationFilterSet',
'VirtualCircuitTypeFilterSet',
)


Expand Down Expand Up @@ -462,6 +463,13 @@ def filter_provider(self, queryset, name, value):
)


class VirtualCircuitTypeFilterSet(OrganizationalModelFilterSet):

class Meta:
model = VirtualCircuitType
fields = ('id', 'name', 'slug', 'color', 'description')


class VirtualCircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
provider_id = django_filters.ModelMultipleChoiceFilter(
field_name='provider_network__provider',
Expand Down Expand Up @@ -489,6 +497,16 @@ class VirtualCircuitFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
queryset=ProviderNetwork.objects.all(),
label=_('Provider network (ID)'),
)
type_id = django_filters.ModelMultipleChoiceFilter(
queryset=VirtualCircuitType.objects.all(),
label=_('Virtual circuit type (ID)'),
)
type = django_filters.ModelMultipleChoiceFilter(
field_name='type__slug',
queryset=VirtualCircuitType.objects.all(),
to_field_name='slug',
label=_('Virtual circuit type (slug)'),
)
status = django_filters.MultipleChoiceFilter(
choices=CircuitStatusChoices,
null_value=None
Expand Down
24 changes: 24 additions & 0 deletions netbox/circuits/forms/bulk_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
'ProviderNetworkBulkEditForm',
'VirtualCircuitBulkEditForm',
'VirtualCircuitTerminationBulkEditForm',
'VirtualCircuitTypeBulkEditForm',
)


Expand Down Expand Up @@ -297,6 +298,24 @@ class CircuitGroupAssignmentBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('priority',)


class VirtualCircuitTypeBulkEditForm(NetBoxModelBulkEditForm):
color = ColorField(
label=_('Color'),
required=False
)
description = forms.CharField(
label=_('Description'),
max_length=200,
required=False
)

model = VirtualCircuitType
fieldsets = (
FieldSet('color', 'description'),
)
nullable_fields = ('color', 'description')


class VirtualCircuitBulkEditForm(NetBoxModelBulkEditForm):
provider_network = DynamicModelChoiceField(
label=_('Provider network'),
Expand All @@ -308,6 +327,11 @@ class VirtualCircuitBulkEditForm(NetBoxModelBulkEditForm):
queryset=ProviderAccount.objects.all(),
required=False
)
type = DynamicModelChoiceField(
label=_('Type'),
queryset=VirtualCircuitType.objects.all(),
required=False
)
status = forms.ChoiceField(
label=_('Status'),
choices=add_blank_choice(CircuitStatusChoices),
Expand Down
18 changes: 17 additions & 1 deletion netbox/circuits/forms/bulk_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
'VirtualCircuitImportForm',
'VirtualCircuitTerminationImportForm',
'VirtualCircuitTerminationImportRelatedForm',
'VirtualCircuitTypeImportForm',
)


Expand Down Expand Up @@ -194,6 +195,14 @@ class Meta:
fields = ('member_type', 'member_id', 'group', 'priority')


class VirtualCircuitTypeImportForm(NetBoxModelImportForm):
slug = SlugField()

class Meta:
model = VirtualCircuitType
fields = ('name', 'slug', 'color', 'description', 'tags')


class VirtualCircuitImportForm(NetBoxModelImportForm):
provider_network = CSVModelChoiceField(
label=_('Provider network'),
Expand All @@ -208,6 +217,12 @@ class VirtualCircuitImportForm(NetBoxModelImportForm):
help_text=_('Assigned provider account (if any)'),
required=False
)
type = CSVModelChoiceField(
label=_('Type'),
queryset=VirtualCircuitType.objects.all(),
to_field_name='name',
help_text=_('Type of virtual circuit')
)
status = CSVChoiceField(
label=_('Status'),
choices=CircuitStatusChoices,
Expand All @@ -224,7 +239,8 @@ class VirtualCircuitImportForm(NetBoxModelImportForm):
class Meta:
model = VirtualCircuit
fields = [
'cid', 'provider_network', 'provider_account', 'status', 'tenant', 'description', 'comments', 'tags',
'cid', 'provider_network', 'provider_account', 'type', 'status', 'tenant', 'description', 'comments',
'tags',
]


Expand Down
22 changes: 21 additions & 1 deletion netbox/circuits/forms/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
'ProviderNetworkFilterForm',
'VirtualCircuitFilterForm',
'VirtualCircuitTerminationFilterForm',
'VirtualCircuitTypeFilterForm',
)


Expand Down Expand Up @@ -302,12 +303,26 @@ class CircuitGroupAssignmentFilterForm(NetBoxModelFilterSetForm):
tag = TagFilterField(model)


class VirtualCircuitTypeFilterForm(NetBoxModelFilterSetForm):
model = VirtualCircuitType
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
FieldSet('color', name=_('Attributes')),
)
tag = TagFilterField(model)

color = ColorField(
label=_('Color'),
required=False
)


class VirtualCircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBoxModelFilterSetForm):
model = VirtualCircuit
fieldsets = (
FieldSet('q', 'filter_id', 'tag'),
FieldSet('provider_id', 'provider_account_id', 'provider_network_id', name=_('Provider')),
FieldSet('status', name=_('Attributes')),
FieldSet('type', 'status', name=_('Attributes')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
)
selector_fields = ('filter_id', 'q', 'provider_id', 'provider_network_id')
Expand All @@ -332,6 +347,11 @@ class VirtualCircuitFilterForm(TenancyFilterForm, ContactModelFilterForm, NetBox
},
label=_('Provider network')
)
type_id = DynamicModelMultipleChoiceField(
queryset=VirtualCircuitType.objects.all(),
required=False,
label=_('Type')
)
status = forms.MultipleChoiceField(
label=_('Status'),
choices=CircuitStatusChoices,
Expand Down
24 changes: 22 additions & 2 deletions netbox/circuits/forms/model_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
'ProviderNetworkForm',
'VirtualCircuitForm',
'VirtualCircuitTerminationForm',
'VirtualCircuitTypeForm',
)


Expand Down Expand Up @@ -305,6 +306,20 @@ def clean(self):
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'),
Expand All @@ -316,19 +331,24 @@ class VirtualCircuitForm(TenancyForm, NetBoxModelForm):
queryset=ProviderAccount.objects.all(),
required=False
)
type = DynamicModelChoiceField(
queryset=VirtualCircuitType.objects.all(),
quick_add=True
)
comments = CommentField()

fieldsets = (
FieldSet(
'provider_network', 'provider_account', 'cid', 'status', 'description', 'tags', name=_('Virtual circuit'),
'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', 'status', 'description', 'tenant_group', 'tenant',
'cid', 'provider_network', 'provider_account', 'type', 'status', 'description', 'tenant_group', 'tenant',
'comments', 'tags',
]

Expand Down
7 changes: 7 additions & 0 deletions netbox/circuits/graphql/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
'ProviderNetworkFilter',
'VirtualCircuitFilter',
'VirtualCircuitTerminationFilter',
'VirtualCircuitTypeFilter',
)


Expand Down Expand Up @@ -65,6 +66,12 @@ class ProviderNetworkFilter(BaseFilterMixin):
pass


@strawberry_django.filter(models.VirtualCircuitType, lookups=True)
@autotype_decorator(filtersets.VirtualCircuitTypeFilterSet)
class VirtualCircuitTypeFilter(BaseFilterMixin):
pass


@strawberry_django.filter(models.VirtualCircuit, lookups=True)
@autotype_decorator(filtersets.VirtualCircuitFilterSet)
class VirtualCircuitFilter(BaseFilterMixin):
Expand Down
3 changes: 3 additions & 0 deletions netbox/circuits/graphql/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ class CircuitsQuery:

virtual_circuit_termination: VirtualCircuitTerminationType = strawberry_django.field()
virtual_circuit_termination_list: List[VirtualCircuitTerminationType] = strawberry_django.field()

virtual_circuit_type: VirtualCircuitTypeType = strawberry_django.field()
virtual_circuit_type_list: List[VirtualCircuitTypeType] = strawberry_django.field()
13 changes: 13 additions & 0 deletions netbox/circuits/graphql/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
'ProviderNetworkType',
'VirtualCircuitTerminationType',
'VirtualCircuitType',
'VirtualCircuitTypeType',
)


Expand Down Expand Up @@ -130,6 +131,17 @@ def member(self) -> Annotated[Union[
return self.member


@strawberry_django.type(
models.VirtualCircuitType,
fields='__all__',
filters=VirtualCircuitTypeFilter
)
class VirtualCircuitTypeType(OrganizationalObjectType):
color: str

virtual_circuits: List[Annotated["VirtualCircuitType", strawberry.lazy('circuits.graphql.types')]]


@strawberry_django.type(
models.VirtualCircuitTermination,
fields='__all__',
Expand All @@ -154,6 +166,7 @@ class VirtualCircuitTerminationType(CustomFieldsMixin, TagsMixin, ObjectType):
class VirtualCircuitType(NetBoxObjectType):
provider_network: ProviderNetworkType = strawberry_django.field(select_related=["provider_network"])
provider_account: ProviderAccountType | None
type: VirtualCircuitTypeType
tenant: TenantType | None

terminations: List[VirtualCircuitTerminationType]
Loading

0 comments on commit b78d442

Please sign in to comment.