Просмотр исходного кода

Add RF channel fields to Interface

jeremystretch 4 лет назад
Родитель
Сommit
8e1535f7ec

+ 6 - 4
netbox/dcim/api/serializers.py

@@ -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):

+ 131 - 0
netbox/dcim/choices.py

@@ -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
 #

+ 1 - 0
netbox/dcim/constants.py

@@ -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

+ 4 - 1
netbox/dcim/filtersets.py

@@ -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:

+ 3 - 3
netbox/dcim/forms/bulk_edit.py

@@ -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):

+ 1 - 1
netbox/dcim/forms/bulk_import.py

@@ -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):

+ 11 - 0
netbox/dcim/forms/filtersets.py

@@ -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)
 
 

+ 4 - 1
netbox/dcim/forms/models.py

@@ -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',

+ 14 - 1
netbox/dcim/forms/object_create.py

@@ -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):

+ 21 - 0
netbox/dcim/migrations/0136_wireless.py

@@ -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),
+        ),
+    ]

+ 18 - 0
netbox/dcim/models/device_components.py

@@ -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({

+ 2 - 2
netbox/dcim/tables/devices.py

@@ -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')
 

+ 10 - 0
netbox/templates/dcim/interface.html

@@ -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>

+ 10 - 0
netbox/templates/dcim/interface_edit.html

@@ -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>