Fixes #21542: Increase supported interface speed values above 2.1 Tbps (#21834)

This commit is contained in:
Martin Hauser
2026-04-03 23:55:11 +02:00
committed by GitHub
parent 7d71503ea2
commit f242f17ce5
12 changed files with 76 additions and 16 deletions

View File

@@ -26,6 +26,7 @@ from tenancy.models import *
from users.filterset_mixins import OwnerFilterMixin
from users.models import User
from utilities.filters import (
MultiValueBigNumberFilter,
MultiValueCharFilter,
MultiValueContentTypeFilter,
MultiValueMACAddressFilter,
@@ -2175,7 +2176,7 @@ class InterfaceFilterSet(
distinct=False,
label=_('LAG interface (ID)'),
)
speed = MultiValueNumberFilter()
speed = MultiValueBigNumberFilter(min_value=0)
duplex = django_filters.MultipleChoiceFilter(
choices=InterfaceDuplexChoices,
distinct=False,

View File

@@ -20,7 +20,13 @@ from netbox.forms.mixins import ChangelogMessageMixin, OwnerMixin
from tenancy.models import Tenant
from users.models import User
from utilities.forms import BulkEditForm, add_blank_choice, form_from_model
from utilities.forms.fields import ColorField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField
from utilities.forms.fields import (
ColorField,
DynamicModelChoiceField,
DynamicModelMultipleChoiceField,
JSONField,
PositiveBigIntegerField,
)
from utilities.forms.rendering import FieldSet, InlineFields, TabbedGroups
from utilities.forms.widgets import BulkEditNullBooleanSelect, NumberWithOptions
from virtualization.models import Cluster
@@ -1420,7 +1426,7 @@ class InterfaceBulkEditForm(
'device_id': '$device',
}
)
speed = forms.IntegerField(
speed = PositiveBigIntegerField(
label=_('Speed'),
required=False,
widget=NumberWithOptions(

View File

@@ -19,7 +19,7 @@ from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
from tenancy.models import Tenant
from users.models import User
from utilities.forms import BOOLEAN_WITH_BLANK_CHOICES, FilterForm, add_blank_choice
from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, TagFilterField
from utilities.forms.fields import ColorField, DynamicModelMultipleChoiceField, PositiveBigIntegerField, TagFilterField
from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import NumberWithOptions
from virtualization.models import Cluster, ClusterGroup, VirtualMachine
@@ -1603,7 +1603,7 @@ class InterfaceFilterForm(PathEndpointFilterForm, DeviceComponentFilterForm):
choices=InterfaceTypeChoices,
required=False
)
speed = forms.IntegerField(
speed = PositiveBigIntegerField(
label=_('Speed'),
required=False,
widget=NumberWithOptions(

View File

@@ -47,7 +47,13 @@ if TYPE_CHECKING:
VRFFilter,
)
from netbox.graphql.enums import ColorEnum
from netbox.graphql.filter_lookups import FloatLookup, IntegerArrayLookup, IntegerLookup, TreeNodeFilter
from netbox.graphql.filter_lookups import (
BigIntegerLookup,
FloatLookup,
IntegerArrayLookup,
IntegerLookup,
TreeNodeFilter,
)
from users.graphql.filters import UserFilter
from virtualization.graphql.filters import ClusterFilter
from vpn.graphql.filters import L2VPNFilter, TunnelTerminationFilter
@@ -519,7 +525,7 @@ class InterfaceFilter(
strawberry_django.filter_field()
)
mgmt_only: FilterLookup[bool] | None = strawberry_django.filter_field()
speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
speed: Annotated['BigIntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
strawberry_django.filter_field()
)
duplex: BaseFilterLookup[Annotated['InterfaceDuplexEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (

View File

@@ -433,6 +433,7 @@ class MACAddressType(PrimaryObjectType):
)
class InterfaceType(IPAddressesMixin, ModularComponentType, CabledObjectMixin, PathEndpointMixin):
_name: str
speed: BigInt | None
wwn: str | None
parent: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')] | None
bridge: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')] | None

View File

@@ -0,0 +1,15 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dcim', '0226_modulebay_rebuild_tree'),
]
operations = [
migrations.AlterField(
model_name='interface',
name='speed',
field=models.PositiveBigIntegerField(blank=True, null=True),
),
]

View File

@@ -806,7 +806,7 @@ class Interface(
verbose_name=_('management only'),
help_text=_('This interface is used only for out-of-band management')
)
speed = models.PositiveIntegerField(
speed = models.PositiveBigIntegerField(
blank=True,
null=True,
verbose_name=_('speed (Kbps)')

View File

@@ -1930,9 +1930,9 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
{
'device': device.pk,
'name': 'Interface 4',
'type': '1000base-t',
'type': 'other',
'mode': InterfaceModeChoices.MODE_TAGGED,
'speed': 1000000,
'speed': 16_000_000_000,
'duplex': 'full',
'vrf': vrfs[0].pk,
'poe_mode': InterfacePoEModeChoices.MODE_PD,

View File

@@ -4655,7 +4655,7 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
enabled=True,
mgmt_only=True,
tx_power=40,
speed=100000,
speed=16_000_000_000,
duplex='full',
poe_mode=InterfacePoEModeChoices.MODE_PD,
poe_type=InterfacePoETypeChoices.TYPE_2_8023AT,
@@ -4757,7 +4757,7 @@ class InterfaceTestCase(TestCase, DeviceComponentFilterSetTests, ChangeLoggedFil
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_speed(self):
params = {'speed': [1000000, 100000]}
params = {'speed': [16_000_000_000, 1_000_000, 100_000]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
def test_duplex(self):

View File

@@ -2961,13 +2961,13 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
cls.form_data = {
'device': device.pk,
'name': 'Interface X',
'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
'type': InterfaceTypeChoices.TYPE_OTHER,
'enabled': False,
'bridge': interfaces[4].pk,
'lag': interfaces[3].pk,
'wwn': EUI('01:02:03:04:05:06:07:08', version=64),
'mtu': 65000,
'speed': 1000000,
'speed': 16_000_000_000,
'duplex': 'full',
'mgmt_only': True,
'description': 'A front port',
@@ -2985,13 +2985,13 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
cls.bulk_create_data = {
'device': device.pk,
'name': 'Interface [4-6]',
'type': InterfaceTypeChoices.TYPE_1GE_GBIC,
'type': InterfaceTypeChoices.TYPE_OTHER,
'enabled': False,
'bridge': interfaces[4].pk,
'lag': interfaces[3].pk,
'wwn': EUI('01:02:03:04:05:06:07:08', version=64),
'mtu': 2000,
'speed': 100000,
'speed': 16_000_000_000,
'duplex': 'half',
'mgmt_only': True,
'description': 'A front port',

View File

@@ -7,9 +7,12 @@ from django_filters.constants import EMPTY_VALUES
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from .forms.fields import BigIntegerField
__all__ = (
'ContentTypeFilter',
'MultiValueArrayFilter',
'MultiValueBigNumberFilter',
'MultiValueCharFilter',
'MultiValueContentTypeFilter',
'MultiValueDateFilter',
@@ -77,6 +80,11 @@ class MultiValueNumberFilter(django_filters.MultipleChoiceFilter):
field_class = multivalue_field_factory(forms.IntegerField)
@extend_schema_field(OpenApiTypes.INT64)
class MultiValueBigNumberFilter(MultiValueNumberFilter):
field_class = multivalue_field_factory(BigIntegerField)
@extend_schema_field(OpenApiTypes.DECIMAL)
class MultiValueDecimalFilter(django_filters.MultipleChoiceFilter):
field_class = multivalue_field_factory(forms.DecimalField)

View File

@@ -2,6 +2,7 @@ import json
from django import forms
from django.conf import settings
from django.db.models import BigIntegerField as BigIntegerModelField
from django.db.models import Count
from django.forms.fields import InvalidJSONInput
from django.forms.fields import JSONField as _JSONField
@@ -13,17 +14,39 @@ from utilities.forms import widgets
from utilities.validators import EnhancedURLValidator
__all__ = (
'BigIntegerField',
'ColorField',
'CommentField',
'JSONField',
'LaxURLField',
'MACAddressField',
'PositiveBigIntegerField',
'QueryField',
'SlugField',
'TagFilterField',
)
class BigIntegerField(forms.IntegerField):
"""
An IntegerField constrained to the range of a signed 64-bit integer.
"""
def __init__(self, *args, **kwargs):
kwargs.setdefault('min_value', -BigIntegerModelField.MAX_BIGINT - 1)
kwargs.setdefault('max_value', BigIntegerModelField.MAX_BIGINT)
super().__init__(*args, **kwargs)
class PositiveBigIntegerField(BigIntegerField):
"""
An IntegerField constrained to the range supported by Django's
PositiveBigIntegerField model field.
"""
def __init__(self, *args, **kwargs):
kwargs.setdefault('min_value', 0)
super().__init__(*args, **kwargs)
class QueryField(forms.CharField):
"""
A CharField subclass used for global search/query fields in filter forms.