jeremystretch 4 лет назад
Родитель
Сommit
438b4b4758

+ 2 - 1
netbox/dcim/api/serializers.py

@@ -632,6 +632,7 @@ class InterfaceSerializer(PrimaryModelSerializer, LinkTerminationSerializer, Con
     parent = NestedInterfaceSerializer(required=False, allow_null=True)
     lag = NestedInterfaceSerializer(required=False, allow_null=True)
     mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False)
+    rf_role = ChoiceField(choices=WirelessRoleChoices, required=False, allow_null=True)
     rf_channel = ChoiceField(choices=WirelessChannelChoices, required=False)
     rf_channel_width = ChoiceField(choices=WirelessChannelWidthChoices, required=False, allow_null=True)
     untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
@@ -648,7 +649,7 @@ class InterfaceSerializer(PrimaryModelSerializer, LinkTerminationSerializer, Con
         model = Interface
         fields = [
             'id', 'url', 'display', 'device', 'name', 'label', 'type', 'enabled', 'parent', 'lag', 'mtu', 'mac_address',
-            'wwn', 'mgmt_only', 'description', 'mode', 'rf_channel', 'rf_channel_width', 'untagged_vlan',
+            'wwn', 'mgmt_only', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_width', 'untagged_vlan',
             'tagged_vlans', 'mark_connected', 'cable', 'link_peer', 'link_peer_type', 'connected_endpoint',
             'connected_endpoint_type', 'connected_endpoint_reachable', 'tags', 'custom_fields', 'created',
             'last_updated', 'count_ipaddresses', '_occupied',

+ 10 - 0
netbox/dcim/choices.py

@@ -1139,6 +1139,16 @@ class CableLengthUnitChoices(ChoiceSet):
 # Wireless
 #
 
+class WirelessRoleChoices(ChoiceSet):
+    ROLE_AP = 'ap'
+    ROLE_STATION = 'station'
+
+    CHOICES = (
+        (ROLE_AP, 'Access point'),
+        (ROLE_STATION, 'Station'),
+    )
+
+
 class WirelessChannelChoices(ChoiceSet):
     CHANNEL_AUTO = 'auto'
 

+ 2 - 2
netbox/dcim/filtersets.py

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

+ 1 - 1
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', 'rf_channel', 'rf_channel_width',
+        'mode', 'rf_role', 'rf_channel', 'rf_channel_width',
     ]),
     BootstrapMixin,
     AddRemoveTagsForm,

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

@@ -579,12 +579,17 @@ class InterfaceCSVForm(CustomFieldModelCSVForm):
         required=False,
         help_text='IEEE 802.1Q operational mode (for L2 interfaces)'
     )
+    rf_role = CSVChoiceField(
+        choices=WirelessRoleChoices,
+        required=False,
+        help_text='Wireless role (AP/station)'
+    )
 
     class Meta:
         model = Interface
         fields = (
             'device', 'name', 'label', 'parent', 'lag', 'type', 'enabled', 'mark_connected', 'mac_address', 'wwn',
-            'mtu', 'mgmt_only', 'description', 'mode', 'rf_channel', 'rf_channel_width',
+            'mtu', 'mgmt_only', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_width',
         )
 
     def __init__(self, *args, **kwargs):

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

@@ -963,7 +963,7 @@ class InterfaceFilterForm(DeviceComponentFilterForm):
     field_groups = [
         ['q', 'tag'],
         ['name', 'label', 'type', 'enabled', 'mgmt_only', 'mac_address', 'wwn'],
-        ['rf_channel', 'rf_channel_width'],
+        ['rf_role', 'rf_channel', 'rf_channel_width'],
         ['region_id', 'site_group_id', 'site_id', 'location_id', 'device_id'],
     ]
     type = forms.MultipleChoiceField(
@@ -991,15 +991,23 @@ class InterfaceFilterForm(DeviceComponentFilterForm):
         required=False,
         label='WWN'
     )
+    rf_role = forms.MultipleChoiceField(
+        choices=WirelessRoleChoices,
+        required=False,
+        widget=StaticSelectMultiple(),
+        label='Wireless role'
+    )
     rf_channel = forms.MultipleChoiceField(
         choices=WirelessChannelChoices,
         required=False,
-        widget=StaticSelectMultiple()
+        widget=StaticSelectMultiple(),
+        label='Wireless channel'
     )
     rf_channel_width = forms.MultipleChoiceField(
         choices=WirelessChannelWidthChoices,
         required=False,
-        widget=StaticSelectMultiple()
+        widget=StaticSelectMultiple(),
+        label='Channel width'
     )
     tag = TagFilterField(model)
 

+ 3 - 2
netbox/dcim/forms/models.py

@@ -1104,13 +1104,14 @@ 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', 'rf_channel', 'rf_channel_width', 'wireless_lans', 'untagged_vlan',
-            'tagged_vlans', 'tags',
+            'mark_connected', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_width', 'wireless_lans',
+            'untagged_vlan', 'tagged_vlans', 'tags',
         ]
         widgets = {
             'device': forms.HiddenInput(),
             'type': StaticSelect(),
             'mode': StaticSelect(),
+            'rf_role': StaticSelect(),
             'rf_channel': StaticSelect(),
             'rf_channel_width': StaticSelect(),
         }

+ 8 - 2
netbox/dcim/forms/object_create.py

@@ -467,6 +467,12 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
         required=False,
         widget=StaticSelect()
     )
+    rf_role = forms.ChoiceField(
+        choices=add_blank_choice(WirelessRoleChoices),
+        required=False,
+        widget=StaticSelect(),
+        label='Wireless role'
+    )
     rf_channel = forms.ChoiceField(
         choices=add_blank_choice(WirelessChannelChoices),
         required=False,
@@ -489,8 +495,8 @@ class InterfaceCreateForm(ComponentCreateForm, InterfaceCommonForm):
     )
     field_order = (
         'device', 'name_pattern', 'label_pattern', 'type', 'enabled', 'parent', 'lag', 'mtu', 'mac_address',
-        'description', 'mgmt_only', 'mark_connected', 'rf_channel', 'rf_channel_width', 'mode' 'untagged_vlan',
-        'tagged_vlans', 'tags'
+        'description', 'mgmt_only', 'mark_connected', 'rf_role', 'rf_channel', 'rf_channel_width', 'mode',
+        'untagged_vlan', 'tagged_vlans', 'tags'
     )
 
     def __init__(self, *args, **kwargs):

+ 3 - 0
netbox/dcim/graphql/types.py

@@ -206,6 +206,9 @@ class InterfaceType(IPAddressesMixin, ComponentObjectType):
     def resolve_mode(self, info):
         return self.mode or None
 
+    def resolve_rf_role(self, info):
+        return self.rf_role or None
+
     def resolve_rf_channel(self, info):
         return self.rf_channel or None
 

+ 5 - 2
netbox/dcim/migrations/0136_wireless.py

@@ -1,5 +1,3 @@
-# Generated by Django 3.2.8 on 2021-10-13 13:44
-
 from django.db import migrations, models
 
 
@@ -10,6 +8,11 @@ class Migration(migrations.Migration):
     ]
 
     operations = [
+        migrations.AddField(
+            model_name='interface',
+            name='rf_role',
+            field=models.CharField(blank=True, max_length=30),
+        ),
         migrations.AddField(
             model_name='interface',
             name='rf_channel',

+ 10 - 2
netbox/dcim/models/device_components.py

@@ -524,6 +524,12 @@ class Interface(ComponentModel, BaseInterface, LinkTermination, PathEndpoint):
         verbose_name='WWN',
         help_text='64-bit World Wide Name'
     )
+    rf_role = models.CharField(
+        max_length=30,
+        choices=WirelessRoleChoices,
+        blank=True,
+        verbose_name='Wireless role'
+    )
     rf_channel = models.CharField(
         max_length=50,
         choices=WirelessChannelChoices,
@@ -636,9 +642,11 @@ class Interface(ComponentModel, BaseInterface, LinkTermination, PathEndpoint):
             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:
+        if self.rf_role and not self.is_wireless:
+            raise ValidationError({'rf_role': "Wireless role may be set only on wireless interfaces."})
+        if self.rf_channel and not self.is_wireless:
             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:
+        if self.rf_channel_width and not self.is_wireless:
             raise ValidationError({'rf_channel_width': "Channel width may be set only on wireless interfaces."})
 
         # Validate untagged VLAN

+ 5 - 4
netbox/dcim/tables/devices.py

@@ -496,8 +496,8 @@ class InterfaceTable(DeviceComponentTable, BaseInterfaceTable, PathEndpointTable
         model = Interface
         fields = (
             'pk', 'name', 'device', 'label', 'enabled', 'type', 'mgmt_only', 'mtu', 'mode', 'mac_address', 'wwn',
-            'rf_channel', 'rf_channel_width', 'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link',
-            'link_peer', 'connection', 'tags', 'ip_addresses', 'untagged_vlan', 'tagged_vlans',
+            'rf_role', 'rf_channel', 'rf_channel_width', 'description', 'mark_connected', 'cable', 'cable_color',
+            'wireless_link', 'link_peer', 'connection', 'tags', 'ip_addresses', 'untagged_vlan', 'tagged_vlans',
         )
         default_columns = ('pk', 'name', 'device', 'label', 'enabled', 'type', 'description')
 
@@ -528,8 +528,9 @@ class DeviceInterfaceTable(InterfaceTable):
         model = Interface
         fields = (
             'pk', 'name', 'label', 'enabled', 'type', 'parent', 'lag', 'mgmt_only', 'mtu', 'mode', 'mac_address', 'wwn',
-            'description', 'mark_connected', 'cable', 'cable_color', 'wireless_link', 'link_peer', 'connection', 'tags',
-            'ip_addresses', 'untagged_vlan', 'tagged_vlans', 'actions',
+            'rf_role', 'rf_channel', 'rf_channel_width', 'description', 'mark_connected', 'cable', 'cable_color',
+            'wireless_link', 'link_peer', 'connection', 'tags', 'ip_addresses', 'untagged_vlan', 'tagged_vlans',
+            'actions',
         )
         order_by = ('name',)
         default_columns = (

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

@@ -39,16 +39,6 @@
                             <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>
@@ -274,6 +264,25 @@
                 </div>
             {% endif %}
             {% if object.is_wireless %}
+                <div class="card">
+                    <h5 class="card-header">Wireless</h5>
+                    <div class="card-body">
+                        <table class="table table-hover">
+                            <tr>
+                                <th scope="row">Role</th>
+                                <td>{{ object.get_rf_role_display|placeholder }}</td>
+                            </tr>
+                            <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>
+                        </table>
+                    </div>
+                </div>
                 <div class="card">
                     <h5 class="card-header">Wireless LANs</h5>
                     <div class="card-body">

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

@@ -34,6 +34,7 @@
             <div class="row mb-2">
               <h5 class="offset-sm-3">Wireless</h5>
             </div>
+            {% render_field form.rf_role %}
             {% render_field form.rf_channel %}
             {% render_field form.rf_channel_width %}
             {% render_field form.wireless_lans %}