mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
Merge pull request #8420 from netbox-community/7853-speed_duplex
Fixes #7853 - Add speed and duplex
This commit is contained in:
commit
3d6c2c5fef
@ -721,6 +721,7 @@ class InterfaceSerializer(PrimaryModelSerializer, LinkTerminationSerializer, Con
|
||||
bridge = NestedInterfaceSerializer(required=False, allow_null=True)
|
||||
lag = NestedInterfaceSerializer(required=False, allow_null=True)
|
||||
mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False)
|
||||
duplex = ChoiceField(choices=InterfaceDuplexChoices, allow_blank=True, required=False)
|
||||
rf_role = ChoiceField(choices=WirelessRoleChoices, required=False, allow_null=True)
|
||||
rf_channel = ChoiceField(choices=WirelessChannelChoices, required=False)
|
||||
untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
|
||||
@ -746,7 +747,7 @@ class InterfaceSerializer(PrimaryModelSerializer, LinkTerminationSerializer, Con
|
||||
model = Interface
|
||||
fields = [
|
||||
'id', 'url', 'display', 'device', 'module', 'name', 'label', 'type', 'enabled', 'parent', 'bridge', 'lag',
|
||||
'mtu', 'mac_address', 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role', 'rf_channel',
|
||||
'mtu', 'mac_address', 'speed', 'duplex', 'wwn', 'mgmt_only', 'description', 'mode', 'rf_role', 'rf_channel',
|
||||
'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', 'mark_connected',
|
||||
'cable', 'wireless_link', 'link_peer', 'link_peer_type', 'wireless_lans', 'vrf', 'connected_endpoint',
|
||||
'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created',
|
||||
|
@ -951,6 +951,19 @@ class InterfaceTypeChoices(ChoiceSet):
|
||||
)
|
||||
|
||||
|
||||
class InterfaceDuplexChoices(ChoiceSet):
|
||||
|
||||
DUPLEX_HALF = 'half'
|
||||
DUPLEX_FULL = 'full'
|
||||
DUPLEX_AUTO = 'auto'
|
||||
|
||||
CHOICES = (
|
||||
(DUPLEX_HALF, 'Half'),
|
||||
(DUPLEX_FULL, 'Full'),
|
||||
(DUPLEX_AUTO, 'Auto'),
|
||||
)
|
||||
|
||||
|
||||
class InterfaceModeChoices(ChoiceSet):
|
||||
|
||||
MODE_ACCESS = 'access'
|
||||
|
@ -1196,6 +1196,10 @@ class InterfaceFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableT
|
||||
queryset=Interface.objects.all(),
|
||||
label='LAG interface (ID)',
|
||||
)
|
||||
speed = MultiValueNumberFilter()
|
||||
duplex = django_filters.MultipleChoiceFilter(
|
||||
choices=InterfaceDuplexChoices
|
||||
)
|
||||
mac_address = MultiValueMACAddressFilter()
|
||||
wwn = MultiValueWWNFilter()
|
||||
tag = TagFilter()
|
||||
|
@ -72,12 +72,12 @@ class PowerOutletBulkCreateForm(
|
||||
|
||||
|
||||
class InterfaceBulkCreateForm(
|
||||
form_from_model(Interface, ['type', 'enabled', 'mtu', 'mgmt_only', 'mark_connected']),
|
||||
form_from_model(Interface, ['type', 'enabled', 'speed', 'duplex', 'mtu', 'mgmt_only', 'mark_connected']),
|
||||
DeviceBulkAddComponentForm
|
||||
):
|
||||
model = Interface
|
||||
field_order = (
|
||||
'name_pattern', 'label_pattern', 'type', 'enabled', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'tags',
|
||||
'name_pattern', 'label_pattern', 'type', 'enabled', 'speed', 'duplex', 'mtu', 'mgmt_only', 'mark_connected', 'description', 'tags',
|
||||
)
|
||||
|
||||
|
||||
|
@ -11,7 +11,7 @@ from ipam.models import ASN, VLAN, VRF
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms import (
|
||||
add_blank_choice, BulkEditForm, BulkEditNullBooleanSelect, ColorField, CommentField, DynamicModelChoiceField,
|
||||
DynamicModelMultipleChoiceField, form_from_model, SmallTextarea, StaticSelect,
|
||||
DynamicModelMultipleChoiceField, form_from_model, SmallTextarea, StaticSelect, SelectSpeedWidget,
|
||||
)
|
||||
|
||||
__all__ = (
|
||||
@ -1028,7 +1028,7 @@ class PowerOutletBulkEditForm(
|
||||
|
||||
class InterfaceBulkEditForm(
|
||||
form_from_model(Interface, [
|
||||
'label', 'type', 'parent', 'bridge', 'lag', 'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected',
|
||||
'label', 'type', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected',
|
||||
'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
|
||||
]),
|
||||
AddRemoveTagsForm,
|
||||
@ -1064,6 +1064,11 @@ class InterfaceBulkEditForm(
|
||||
},
|
||||
label='LAG'
|
||||
)
|
||||
speed = forms.IntegerField(
|
||||
required=False,
|
||||
widget=SelectSpeedWidget(attrs={'readonly': None}),
|
||||
label='Speed'
|
||||
)
|
||||
mgmt_only = forms.NullBooleanField(
|
||||
required=False,
|
||||
widget=BulkEditNullBooleanSelect,
|
||||
@ -1089,7 +1094,7 @@ class InterfaceBulkEditForm(
|
||||
|
||||
class Meta:
|
||||
nullable_fields = [
|
||||
'label', 'parent', 'bridge', 'lag', 'mac_address', 'wwn', 'mtu', 'description', 'mode', 'rf_channel',
|
||||
'label', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'mac_address', 'wwn', 'mtu', 'description', 'mode', 'rf_channel',
|
||||
'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', 'vrf',
|
||||
]
|
||||
|
||||
|
@ -618,6 +618,11 @@ class InterfaceCSVForm(CustomFieldModelCSVForm):
|
||||
choices=InterfaceTypeChoices,
|
||||
help_text='Physical medium'
|
||||
)
|
||||
duplex = CSVChoiceField(
|
||||
choices=InterfaceDuplexChoices,
|
||||
required=False,
|
||||
help_text='Duplex'
|
||||
)
|
||||
mode = CSVChoiceField(
|
||||
choices=InterfaceModeChoices,
|
||||
required=False,
|
||||
@ -638,7 +643,7 @@ class InterfaceCSVForm(CustomFieldModelCSVForm):
|
||||
class Meta:
|
||||
model = Interface
|
||||
fields = (
|
||||
'device', 'name', 'label', 'parent', 'bridge', 'lag', 'type', 'enabled', 'mark_connected', 'mac_address',
|
||||
'device', 'name', 'label', 'parent', 'bridge', 'lag', 'type', 'speed', 'duplex', 'enabled', 'mark_connected', 'mac_address',
|
||||
'wwn', 'mtu', 'mgmt_only', 'description', 'mode', 'vrf', 'rf_role', 'rf_channel', 'rf_channel_frequency',
|
||||
'rf_channel_width', 'tx_power',
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ from ipam.models import ASN, VRF
|
||||
from tenancy.forms import TenancyFilterForm
|
||||
from utilities.forms import (
|
||||
APISelectMultiple, add_blank_choice, ColorField, DynamicModelMultipleChoiceField, FilterForm, StaticSelect,
|
||||
StaticSelectMultiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES,
|
||||
StaticSelectMultiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES, SelectSpeedWidget,
|
||||
)
|
||||
from wireless.choices import *
|
||||
|
||||
@ -927,7 +927,7 @@ class InterfaceFilterForm(DeviceComponentFilterForm):
|
||||
model = Interface
|
||||
field_groups = [
|
||||
['q', 'tag'],
|
||||
['name', 'label', 'kind', 'type', 'enabled', 'mgmt_only'],
|
||||
['name', 'label', 'kind', 'type', 'speed', 'duplex', 'enabled', 'mgmt_only'],
|
||||
['vrf_id', 'mac_address', 'wwn'],
|
||||
['rf_role', 'rf_channel', 'rf_channel_width', 'tx_power'],
|
||||
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
||||
@ -942,6 +942,17 @@ class InterfaceFilterForm(DeviceComponentFilterForm):
|
||||
required=False,
|
||||
widget=StaticSelectMultiple()
|
||||
)
|
||||
speed = forms.IntegerField(
|
||||
required=False,
|
||||
label='Select Speed',
|
||||
widget=SelectSpeedWidget(attrs={'readonly': None})
|
||||
)
|
||||
duplex = forms.MultipleChoiceField(
|
||||
choices=InterfaceDuplexChoices,
|
||||
required=False,
|
||||
label='Select Duplex',
|
||||
widget=StaticSelectMultiple()
|
||||
)
|
||||
enabled = forms.NullBooleanField(
|
||||
required=False,
|
||||
widget=StaticSelect(
|
||||
|
@ -14,7 +14,7 @@ from tenancy.forms import TenancyForm
|
||||
from utilities.forms import (
|
||||
APISelect, add_blank_choice, BootstrapMixin, ClearableFileInput, CommentField, ContentTypeChoiceField,
|
||||
DynamicModelChoiceField, DynamicModelMultipleChoiceField, JSONField, NumericArrayField, SelectWithPK, SmallTextarea,
|
||||
SlugField, StaticSelect,
|
||||
SlugField, StaticSelect, SelectSpeedWidget,
|
||||
)
|
||||
from virtualization.models import Cluster, ClusterGroup
|
||||
from wireless.models import WirelessLAN, WirelessLANGroup
|
||||
@ -1274,12 +1274,12 @@ class InterfaceForm(InterfaceCommonForm, CustomFieldModelForm):
|
||||
class Meta:
|
||||
model = Interface
|
||||
fields = [
|
||||
'device', 'name', 'label', 'type', 'enabled', 'parent', 'bridge', 'lag', 'mac_address', 'wwn', 'mtu',
|
||||
'device', 'name', 'label', 'type', 'speed', 'duplex', 'enabled', 'parent', 'bridge', 'lag', 'mac_address', 'wwn', 'mtu',
|
||||
'mgmt_only', 'mark_connected', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency',
|
||||
'rf_channel_width', 'tx_power', 'wireless_lans', 'untagged_vlan', 'tagged_vlans', 'vrf', 'tags',
|
||||
]
|
||||
fieldsets = (
|
||||
('Interface', ('device', 'name', 'type', 'label', 'description', 'tags')),
|
||||
('Interface', ('device', 'name', 'type', 'speed', 'duplex', 'label', 'description', 'tags')),
|
||||
('Addressing', ('vrf', 'mac_address', 'wwn')),
|
||||
('Operation', ('mtu', 'tx_power', 'enabled', 'mgmt_only', 'mark_connected')),
|
||||
('Related Interfaces', ('parent', 'bridge', 'lag')),
|
||||
@ -1292,6 +1292,8 @@ class InterfaceForm(InterfaceCommonForm, CustomFieldModelForm):
|
||||
widgets = {
|
||||
'device': forms.HiddenInput(),
|
||||
'type': StaticSelect(),
|
||||
'speed': SelectSpeedWidget(),
|
||||
'duplex': StaticSelect(),
|
||||
'mode': StaticSelect(),
|
||||
'rf_role': StaticSelect(),
|
||||
'rf_channel': StaticSelect(),
|
||||
|
23
netbox/dcim/migrations/0150_interface_speed_duplex.py
Normal file
23
netbox/dcim/migrations/0150_interface_speed_duplex.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Generated by Django 3.2.10 on 2022-01-08 18:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dcim', '0149_interface_vrf'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='duplex',
|
||||
field=models.CharField(blank=True, max_length=50, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='interface',
|
||||
name='speed',
|
||||
field=models.PositiveIntegerField(blank=True, null=True),
|
||||
),
|
||||
]
|
@ -545,6 +545,18 @@ class Interface(ModularComponentModel, BaseInterface, LinkTermination, PathEndpo
|
||||
verbose_name='Management only',
|
||||
help_text='This interface is used only for out-of-band management'
|
||||
)
|
||||
speed = models.PositiveIntegerField(
|
||||
verbose_name='Speed',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
duplex = models.CharField(
|
||||
verbose_name='Duplex',
|
||||
max_length=50,
|
||||
blank=True,
|
||||
null=True,
|
||||
choices=InterfaceDuplexChoices
|
||||
)
|
||||
wwn = WWNField(
|
||||
null=True,
|
||||
blank=True,
|
||||
|
@ -524,10 +524,10 @@ class InterfaceTable(ModularDeviceComponentTable, BaseInterfaceTable, PathEndpoi
|
||||
model = Interface
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'device', 'module_bay', 'module', 'label', 'enabled', 'type', 'mgmt_only', 'mtu',
|
||||
'mode', 'mac_address', 'wwn', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width',
|
||||
'tx_power', 'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link', 'wireless_lans',
|
||||
'link_peer', 'connection', 'tags', 'vrf', 'ip_addresses', 'fhrp_groups', 'untagged_vlan', 'tagged_vlans',
|
||||
'created', 'last_updated',
|
||||
'speed', 'duplex', 'mode', 'mac_address', 'wwn', 'rf_role', 'rf_channel', 'rf_channel_frequency',
|
||||
'rf_channel_width', 'tx_power', 'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link',
|
||||
'wireless_lans', 'link_peer', 'connection', 'tags', 'vrf', 'ip_addresses', 'fhrp_groups', 'untagged_vlan',
|
||||
'tagged_vlans', 'created', 'last_updated',
|
||||
)
|
||||
default_columns = ('pk', 'name', 'device', 'label', 'enabled', 'type', 'description')
|
||||
|
||||
|
@ -1442,6 +1442,8 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
|
||||
'tagged_vlans': [vlans[0].pk, vlans[1].pk],
|
||||
'untagged_vlan': vlans[2].pk,
|
||||
'wireless_lans': [wireless_lans[0].pk, wireless_lans[1].pk],
|
||||
'speed': 1000000,
|
||||
'duplex': 'full'
|
||||
},
|
||||
{
|
||||
'device': device.pk,
|
||||
@ -1454,6 +1456,8 @@ class InterfaceTest(Mixins.ComponentTraceMixin, APIViewTestCases.APIViewTestCase
|
||||
'tagged_vlans': [vlans[0].pk, vlans[1].pk],
|
||||
'untagged_vlan': vlans[2].pk,
|
||||
'wireless_lans': [wireless_lans[0].pk, wireless_lans[1].pk],
|
||||
'speed': 100000,
|
||||
'duplex': 'half'
|
||||
},
|
||||
{
|
||||
'device': device.pk,
|
||||
|
@ -2383,10 +2383,10 @@ class InterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
Device.objects.filter(pk=devices[1].pk).update(virtual_chassis=virtual_chassis, vc_position=2, vc_priority=2)
|
||||
|
||||
interfaces = (
|
||||
Interface(device=devices[0], name='Interface 1', label='A', type=InterfaceTypeChoices.TYPE_1GE_SFP, enabled=True, mgmt_only=True, mtu=100, mode=InterfaceModeChoices.MODE_ACCESS, mac_address='00-00-00-00-00-01', description='First', vrf=vrfs[0]),
|
||||
Interface(device=devices[1], name='Interface 2', label='B', type=InterfaceTypeChoices.TYPE_1GE_GBIC, enabled=True, mgmt_only=True, mtu=200, mode=InterfaceModeChoices.MODE_TAGGED, mac_address='00-00-00-00-00-02', description='Second', vrf=vrfs[1]),
|
||||
Interface(device=devices[2], name='Interface 3', label='C', type=InterfaceTypeChoices.TYPE_1GE_FIXED, enabled=False, mgmt_only=False, mtu=300, mode=InterfaceModeChoices.MODE_TAGGED_ALL, mac_address='00-00-00-00-00-03', description='Third', vrf=vrfs[2]),
|
||||
Interface(device=devices[3], name='Interface 4', label='D', type=InterfaceTypeChoices.TYPE_OTHER, enabled=True, mgmt_only=True, tx_power=40),
|
||||
Interface(device=devices[0], name='Interface 1', label='A', type=InterfaceTypeChoices.TYPE_1GE_SFP, enabled=True, mgmt_only=True, mtu=100, mode=InterfaceModeChoices.MODE_ACCESS, mac_address='00-00-00-00-00-01', description='First', vrf=vrfs[0], speed=1000000, duplex='half'),
|
||||
Interface(device=devices[1], name='Interface 2', label='B', type=InterfaceTypeChoices.TYPE_1GE_GBIC, enabled=True, mgmt_only=True, mtu=200, mode=InterfaceModeChoices.MODE_TAGGED, mac_address='00-00-00-00-00-02', description='Second', vrf=vrfs[1], speed=1000000, duplex='full'),
|
||||
Interface(device=devices[2], name='Interface 3', label='C', type=InterfaceTypeChoices.TYPE_1GE_FIXED, enabled=False, mgmt_only=False, mtu=300, mode=InterfaceModeChoices.MODE_TAGGED_ALL, mac_address='00-00-00-00-00-03', description='Third', vrf=vrfs[2], speed=100000, duplex='half'),
|
||||
Interface(device=devices[3], name='Interface 4', label='D', type=InterfaceTypeChoices.TYPE_OTHER, enabled=True, mgmt_only=True, tx_power=40, speed=100000, duplex='full'),
|
||||
Interface(device=devices[3], name='Interface 5', label='E', type=InterfaceTypeChoices.TYPE_OTHER, enabled=True, mgmt_only=True, tx_power=40),
|
||||
Interface(device=devices[3], name='Interface 6', label='F', type=InterfaceTypeChoices.TYPE_OTHER, enabled=False, mgmt_only=False, tx_power=40),
|
||||
Interface(device=devices[3], name='Interface 7', type=InterfaceTypeChoices.TYPE_80211AC, rf_role=WirelessRoleChoices.ROLE_AP, rf_channel=WirelessChannelChoices.CHANNEL_24G_1, rf_channel_frequency=2412, rf_channel_width=22),
|
||||
@ -2423,6 +2423,14 @@ class InterfaceTestCase(TestCase, ChangeLoggedFilterSetTests):
|
||||
params = {'mtu': [100, 200]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
def test_speed(self):
|
||||
params = {'speed': [1000000, 100000]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
||||
def test_duplex(self):
|
||||
params = {'duplex': ['half', 'full']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
||||
def test_mgmt_only(self):
|
||||
params = {'mgmt_only': 'true'}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
@ -2124,6 +2124,8 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
'mac_address': EUI('01:02:03:04:05:06'),
|
||||
'wwn': EUI('01:02:03:04:05:06:07:08', version=64),
|
||||
'mtu': 65000,
|
||||
'speed': 1000000,
|
||||
'duplex': 'full',
|
||||
'mgmt_only': True,
|
||||
'description': 'A front port',
|
||||
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||
@ -2145,6 +2147,8 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
'mac_address': EUI('01:02:03:04:05:06'),
|
||||
'wwn': EUI('01:02:03:04:05:06:07:08', version=64),
|
||||
'mtu': 2000,
|
||||
'speed': 100000,
|
||||
'duplex': 'half',
|
||||
'mgmt_only': True,
|
||||
'description': 'A front port',
|
||||
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||
@ -2162,6 +2166,8 @@ class InterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
|
||||
'mac_address': EUI('01:02:03:04:05:06'),
|
||||
'wwn': EUI('01:02:03:04:05:06:07:08', version=64),
|
||||
'mtu': 2000,
|
||||
'speed': 1000000,
|
||||
'duplex': 'full',
|
||||
'mgmt_only': True,
|
||||
'description': 'New description',
|
||||
'mode': InterfaceModeChoices.MODE_TAGGED,
|
||||
|
@ -46,6 +46,14 @@
|
||||
<th scope="row">Type</th>
|
||||
<td>{{ object.get_type_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Speed</th>
|
||||
<td>{{ object.speed|humanize_speed|placeholder }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Duplex</th>
|
||||
<td>{{ object.get_duplex_display }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Enabled</th>
|
||||
<td>{% checkmark object.enabled %}</td>
|
||||
|
@ -16,6 +16,8 @@
|
||||
{% endif %}
|
||||
{% render_field form.name %}
|
||||
{% render_field form.type %}
|
||||
{% render_field form.speed %}
|
||||
{% render_field form.duplex %}
|
||||
{% render_field form.label %}
|
||||
{% render_field form.description %}
|
||||
{% render_field form.tags %}
|
||||
|
Loading…
Reference in New Issue
Block a user