Add RF channel fields to Interface

This commit is contained in:
jeremystretch 2021-10-12 10:46:41 -04:00
parent 3e7922e41e
commit 8e1535f7ec
14 changed files with 236 additions and 13 deletions

View File

@ -632,6 +632,8 @@ class InterfaceSerializer(PrimaryModelSerializer, CableTerminationSerializer, Co
parent = NestedInterfaceSerializer(required=False, allow_null=True)
lag = NestedInterfaceSerializer(required=False, allow_null=True)
mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False)
rf_channel = ChoiceField(choices=WirelessChannelChoices)
rf_channel_width = ChoiceField(choices=WirelessChannelWidthChoices)
untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
tagged_vlans = SerializedPKRelatedField(
queryset=VLAN.objects.all(),
@ -646,10 +648,10 @@ class InterfaceSerializer(PrimaryModelSerializer, CableTerminationSerializer, Co
model = Interface
fields = [
'id', 'url', 'display', 'device', 'name', 'label', 'type', 'enabled', 'parent', 'lag', 'mtu', 'mac_address',
'wwn', 'mgmt_only', 'description', 'mode', 'untagged_vlan', 'tagged_vlans', 'mark_connected', 'cable',
'cable_peer', 'cable_peer_type', 'connected_endpoint', 'connected_endpoint_type',
'connected_endpoint_reachable', 'tags', 'custom_fields', 'created', 'last_updated', 'count_ipaddresses',
'_occupied',
'wwn', 'mgmt_only', 'description', 'mode', 'rf_channel', 'rf_channel_width', 'untagged_vlan',
'tagged_vlans', 'mark_connected', 'cable', 'cable_peer', 'cable_peer_type', 'connected_endpoint',
'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created',
'last_updated', 'count_ipaddresses', '_occupied',
]
def validate(self, data):

View File

@ -1135,6 +1135,137 @@ class CableLengthUnitChoices(ChoiceSet):
)
#
# Wireless
#
class WirelessChannelChoices(ChoiceSet):
CHANNEL_AUTO = 'auto'
# 2.4 GHz
CHANNEL_24G_1 = '2.4g-1'
CHANNEL_24G_2 = '2.4g-2'
CHANNEL_24G_3 = '2.4g-3'
CHANNEL_24G_4 = '2.4g-4'
CHANNEL_24G_5 = '2.4g-5'
CHANNEL_24G_6 = '2.4g-6'
CHANNEL_24G_7 = '2.4g-7'
CHANNEL_24G_8 = '2.4g-8'
CHANNEL_24G_9 = '2.4g-9'
CHANNEL_24G_10 = '2.4g-10'
CHANNEL_24G_11 = '2.4g-11'
CHANNEL_24G_12 = '2.4g-12'
CHANNEL_24G_13 = '2.4g-13'
# 5 GHz
CHANNEL_5G_32 = '5g-32'
CHANNEL_5G_34 = '5g-34'
CHANNEL_5G_36 = '5g-36'
CHANNEL_5G_38 = '5g-38'
CHANNEL_5G_40 = '5g-40'
CHANNEL_5G_42 = '5g-42'
CHANNEL_5G_44 = '5g-44'
CHANNEL_5G_46 = '5g-46'
CHANNEL_5G_48 = '5g-48'
CHANNEL_5G_50 = '5g-50'
CHANNEL_5G_52 = '5g-52'
CHANNEL_5G_54 = '5g-54'
CHANNEL_5G_56 = '5g-56'
CHANNEL_5G_58 = '5g-58'
CHANNEL_5G_60 = '5g-60'
CHANNEL_5G_62 = '5g-62'
CHANNEL_5G_64 = '5g-64'
CHANNEL_5G_100 = '5g-100'
CHANNEL_5G_102 = '5g-102'
CHANNEL_5G_104 = '5g-104'
CHANNEL_5G_106 = '5g-106'
CHANNEL_5G_108 = '5g-108'
CHANNEL_5G_110 = '5g-110'
CHANNEL_5G_112 = '5g-112'
CHANNEL_5G_114 = '5g-114'
CHANNEL_5G_116 = '5g-116'
CHANNEL_5G_118 = '5g-118'
CHANNEL_5G_120 = '5g-120'
CHANNEL_5G_122 = '5g-122'
CHANNEL_5G_124 = '5g-124'
CHANNEL_5G_126 = '5g-126'
CHANNEL_5G_128 = '5g-128'
CHOICES = (
(CHANNEL_AUTO, 'Auto'),
(
'2.4 GHz (802.11b/g/n/ax)',
(
(CHANNEL_24G_1, '1 (2412 MHz)'),
(CHANNEL_24G_2, '2 (2417 MHz)'),
(CHANNEL_24G_3, '3 (2422 MHz)'),
(CHANNEL_24G_4, '4 (2427 MHz)'),
(CHANNEL_24G_5, '5 (2432 MHz)'),
(CHANNEL_24G_6, '6 (2437 MHz)'),
(CHANNEL_24G_7, '7 (2442 MHz)'),
(CHANNEL_24G_8, '8 (2447 MHz)'),
(CHANNEL_24G_9, '9 (2452 MHz)'),
(CHANNEL_24G_10, '10 (2457 MHz)'),
(CHANNEL_24G_11, '11 (2462 MHz)'),
(CHANNEL_24G_12, '12 (2467 MHz)'),
(CHANNEL_24G_13, '13 (2472 MHz)'),
)
),
(
'5 GHz (802.11a/n/ac/ax)',
(
(CHANNEL_5G_32, '32 (5160 MHz)'),
(CHANNEL_5G_34, '34 (5170 MHz)'),
(CHANNEL_5G_36, '36 (5180 MHz)'),
(CHANNEL_5G_38, '38 (5190 MHz)'),
(CHANNEL_5G_40, '40 (5200 MHz)'),
(CHANNEL_5G_42, '42 (5210 MHz)'),
(CHANNEL_5G_44, '44 (5220 MHz)'),
(CHANNEL_5G_46, '46 (5230 MHz)'),
(CHANNEL_5G_48, '48 (5240 MHz)'),
(CHANNEL_5G_50, '50 (5250 MHz)'),
(CHANNEL_5G_52, '52 (5260 MHz)'),
(CHANNEL_5G_54, '54 (5270 MHz)'),
(CHANNEL_5G_56, '56 (5280 MHz)'),
(CHANNEL_5G_58, '58 (5290 MHz)'),
(CHANNEL_5G_60, '60 (5300 MHz)'),
(CHANNEL_5G_62, '62 (5310 MHz)'),
(CHANNEL_5G_64, '64 (5320 MHz)'),
(CHANNEL_5G_100, '100 (5500 MHz)'),
(CHANNEL_5G_102, '102 (5510 MHz)'),
(CHANNEL_5G_104, '104 (5520 MHz)'),
(CHANNEL_5G_106, '106 (5530 MHz)'),
(CHANNEL_5G_108, '108 (5540 MHz)'),
(CHANNEL_5G_110, '110 (5550 MHz)'),
(CHANNEL_5G_112, '112 (5560 MHz)'),
(CHANNEL_5G_114, '114 (5570 MHz)'),
(CHANNEL_5G_116, '116 (5580 MHz)'),
(CHANNEL_5G_118, '118 (5590 MHz)'),
(CHANNEL_5G_120, '120 (5600 MHz)'),
(CHANNEL_5G_122, '122 (5610 MHz)'),
(CHANNEL_5G_124, '124 (5620 MHz)'),
(CHANNEL_5G_126, '126 (5630 MHz)'),
(CHANNEL_5G_128, '128 (5640 MHz)'),
)
),
)
class WirelessChannelWidthChoices(ChoiceSet):
CHANNEL_WIDTH_20 = 20
CHANNEL_WIDTH_40 = 40
CHANNEL_WIDTH_80 = 80
CHANNEL_WIDTH_160 = 160
CHOICES = (
(CHANNEL_WIDTH_20, '20 MHz'),
(CHANNEL_WIDTH_40, '40 MHz'),
(CHANNEL_WIDTH_80, '80 MHz'),
(CHANNEL_WIDTH_160, '160 MHz'),
)
#
# PowerFeeds
#

View File

@ -42,6 +42,7 @@ WIRELESS_IFACE_TYPES = [
InterfaceTypeChoices.TYPE_80211N,
InterfaceTypeChoices.TYPE_80211AC,
InterfaceTypeChoices.TYPE_80211AD,
InterfaceTypeChoices.TYPE_80211AX,
]
NONCONNECTABLE_IFACE_TYPES = VIRTUAL_IFACE_TYPES + WIRELESS_IFACE_TYPES

View File

@ -990,7 +990,10 @@ class InterfaceFilterSet(PrimaryModelFilterSet, DeviceComponentFilterSet, CableT
class Meta:
model = Interface
fields = ['id', 'name', 'label', 'type', 'enabled', 'mtu', 'mgmt_only', 'mode', 'description']
fields = [
'id', 'name', 'label', 'type', 'enabled', 'mtu', 'mgmt_only', 'mode', 'rf_channel', 'rf_channel_width',
'description',
]
def filter_device(self, queryset, name, value):
try:

View File

@ -926,7 +926,7 @@ class PowerOutletBulkEditForm(
class InterfaceBulkEditForm(
form_from_model(Interface, [
'label', 'type', 'parent', 'lag', 'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description',
'mode',
'mode', 'rf_channel', 'rf_channel_width',
]),
BootstrapMixin,
AddRemoveTagsForm,
@ -977,8 +977,8 @@ class InterfaceBulkEditForm(
class Meta:
nullable_fields = [
'label', 'parent', 'lag', 'mac_address', 'wwn', 'mtu', 'description', 'mode', 'untagged_vlan',
'tagged_vlans',
'label', 'parent', 'lag', 'mac_address', 'wwn', 'mtu', 'description', 'mode', 'rf_channel',
'rf_channel_width', 'untagged_vlan', 'tagged_vlans',
]
def __init__(self, *args, **kwargs):

View File

@ -584,7 +584,7 @@ class InterfaceCSVForm(CustomFieldModelCSVForm):
model = Interface
fields = (
'device', 'name', 'label', 'parent', 'lag', 'type', 'enabled', 'mark_connected', 'mac_address', 'wwn',
'mtu', 'mgmt_only', 'description', 'mode',
'mtu', 'mgmt_only', 'description', 'mode', 'rf_channel', 'rf_channel_width',
)
def __init__(self, *args, **kwargs):

View File

@ -963,6 +963,7 @@ class InterfaceFilterForm(DeviceComponentFilterForm):
field_groups = [
['q', 'tag'],
['name', 'label', 'type', 'enabled', 'mgmt_only', 'mac_address', 'wwn'],
['rf_channel', 'rf_channel_width'],
['region_id', 'site_group_id', 'site_id', 'location_id', 'device_id'],
]
type = forms.MultipleChoiceField(
@ -990,6 +991,16 @@ class InterfaceFilterForm(DeviceComponentFilterForm):
required=False,
label='WWN'
)
rf_channel = forms.MultipleChoiceField(
choices=WirelessChannelChoices,
required=False,
widget=StaticSelectMultiple()
)
rf_channel_width = forms.MultipleChoiceField(
choices=WirelessChannelWidthChoices,
required=False,
widget=StaticSelectMultiple()
)
tag = TagFilterField(model)

View File

@ -1098,12 +1098,15 @@ class InterfaceForm(BootstrapMixin, InterfaceCommonForm, CustomFieldModelForm):
model = Interface
fields = [
'device', 'name', 'label', 'type', 'enabled', 'parent', 'lag', 'mac_address', 'wwn', 'mtu', 'mgmt_only',
'mark_connected', 'description', 'mode', 'untagged_vlan', 'tagged_vlans', 'tags',
'mark_connected', 'description', 'mode', 'rf_channel', 'rf_channel_width', 'untagged_vlan', 'tagged_vlans',
'tags',
]
widgets = {
'device': forms.HiddenInput(),
'type': StaticSelect(),
'mode': StaticSelect(),
'rf_channel': StaticSelect(),
'rf_channel_width': StaticSelect(),
}
labels = {
'mode': '802.1Q Mode',

View File

@ -465,7 +465,19 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
mode = forms.ChoiceField(
choices=add_blank_choice(InterfaceModeChoices),
required=False,
widget=StaticSelect()
)
rf_channel = forms.ChoiceField(
choices=add_blank_choice(WirelessChannelChoices),
required=False,
widget=StaticSelect(),
label='Wireless channel'
)
rf_channel_width = forms.ChoiceField(
choices=add_blank_choice(WirelessChannelWidthChoices),
required=False,
widget=StaticSelect(),
label='Channel width'
)
untagged_vlan = DynamicModelChoiceField(
queryset=VLAN.objects.all(),
@ -477,7 +489,8 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
)
field_order = (
'device', 'name_pattern', 'label_pattern', 'type', 'enabled', 'parent', 'lag', 'mtu', 'mac_address',
'description', 'mgmt_only', 'mark_connected', 'mode', 'untagged_vlan', 'tagged_vlans', 'tags'
'description', 'mgmt_only', 'mark_connected', 'rf_channel', 'rf_channel_width', 'mode' 'untagged_vlan',
'tagged_vlans', 'tags'
)
def __init__(self, *args, **kwargs):

View File

@ -0,0 +1,21 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dcim', '0135_location_tenant'),
]
operations = [
migrations.AddField(
model_name='interface',
name='rf_channel',
field=models.CharField(blank=True, max_length=50),
),
migrations.AddField(
model_name='interface',
name='rf_channel_width',
field=models.PositiveSmallIntegerField(blank=True, null=True),
),
]

View File

@ -517,6 +517,18 @@ class Interface(ComponentModel, BaseInterface, CableTermination, PathEndpoint):
verbose_name='WWN',
help_text='64-bit World Wide Name'
)
rf_channel = models.CharField(
max_length=50,
choices=WirelessChannelChoices,
blank=True,
verbose_name='Wireless channel'
)
rf_channel_width = models.PositiveSmallIntegerField(
choices=WirelessChannelWidthChoices,
blank=True,
null=True,
verbose_name='Channel width'
)
untagged_vlan = models.ForeignKey(
to='ipam.VLAN',
on_delete=models.SET_NULL,
@ -603,6 +615,12 @@ class Interface(ComponentModel, BaseInterface, CableTermination, PathEndpoint):
if self.pk and self.lag_id == self.pk:
raise ValidationError({'lag': "A LAG interface cannot be its own parent."})
# RF channel attributes may be set only for wireless interfaces
if self.rf_channel and self.type not in WIRELESS_IFACE_TYPES:
raise ValidationError({'rf_channel': "Channel may be set only on wireless interfaces."})
if self.rf_channel_width and self.type not in WIRELESS_IFACE_TYPES:
raise ValidationError({'rf_channel_width': "Channel width may be set only on wireless interfaces."})
# Validate untagged VLAN
if self.untagged_vlan and self.untagged_vlan.site not in [self.device.site, None]:
raise ValidationError({

View File

@ -493,8 +493,8 @@ class InterfaceTable(DeviceComponentTable, BaseInterfaceTable, PathEndpointTable
model = Interface
fields = (
'pk', 'name', 'device', 'label', 'enabled', 'type', 'mgmt_only', 'mtu', 'mode', 'mac_address', 'wwn',
'description', 'mark_connected', 'cable', 'cable_color', 'cable_peer', 'connection', 'tags', 'ip_addresses',
'untagged_vlan', 'tagged_vlans',
'rf_channel', 'rf_channel_width', 'description', 'mark_connected', 'cable', 'cable_color', 'cable_peer',
'connection', 'tags', 'ip_addresses', 'untagged_vlan', 'tagged_vlans',
)
default_columns = ('pk', 'name', 'device', 'label', 'enabled', 'type', 'description')

View File

@ -39,6 +39,16 @@
<th scope="row">Type</th>
<td>{{ object.get_type_display }}</td>
</tr>
{% if object.is_wireless %}
<tr>
<th scope="row">Channel</th>
<td>{{ object.get_rf_channel_display|placeholder }}</td>
</tr>
<tr>
<th scope="row">Channel Width</th>
<td>{{ object.get_rf_channel_width_display|placeholder }}</td>
</tr>
{% endif %}
<tr>
<th scope="row">Enabled</th>
<td>

View File

@ -29,6 +29,16 @@
{% render_field form.mark_connected %}
</div>
{% if form.instance.is_wireless %}
<div class="field-group my-5">
<div class="row mb-2">
<h5 class="offset-sm-3">Wireless</h5>
</div>
{% render_field form.rf_channel %}
{% render_field form.rf_channel_width %}
</div>
{% endif %}
<div class="field-group my-5">
<div class="row mb-2">
<h5 class="offset-sm-3">802.1Q Switching</h5>