From 438b4b47581f2b47a19243f3e848aabe52830b38 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 13 Oct 2021 20:16:36 -0400 Subject: [PATCH] Add rf_role to Interface --- netbox/dcim/api/serializers.py | 3 ++- netbox/dcim/choices.py | 10 ++++++++ netbox/dcim/filtersets.py | 4 ++-- netbox/dcim/forms/bulk_edit.py | 2 +- netbox/dcim/forms/bulk_import.py | 7 +++++- netbox/dcim/forms/filtersets.py | 14 ++++++++--- netbox/dcim/forms/models.py | 5 ++-- netbox/dcim/forms/object_create.py | 10 ++++++-- netbox/dcim/graphql/types.py | 3 +++ netbox/dcim/migrations/0136_wireless.py | 7 ++++-- netbox/dcim/models/device_components.py | 12 ++++++++-- netbox/dcim/tables/devices.py | 9 +++---- netbox/templates/dcim/interface.html | 29 +++++++++++++++-------- netbox/templates/dcim/interface_edit.html | 1 + 14 files changed, 86 insertions(+), 30 deletions(-) diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 03d14a160..af1b6eeb0 100644 --- a/netbox/dcim/api/serializers.py +++ b/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', diff --git a/netbox/dcim/choices.py b/netbox/dcim/choices.py index d58d5466d..b0ebf7cf4 100644 --- a/netbox/dcim/choices.py +++ b/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' diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py index f778fd083..ab4336dbf 100644 --- a/netbox/dcim/filtersets.py +++ b/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): diff --git a/netbox/dcim/forms/bulk_edit.py b/netbox/dcim/forms/bulk_edit.py index f710e7d8c..382a0570e 100644 --- a/netbox/dcim/forms/bulk_edit.py +++ b/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, diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py index 95b4dd136..675e850ed 100644 --- a/netbox/dcim/forms/bulk_import.py +++ b/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): diff --git a/netbox/dcim/forms/filtersets.py b/netbox/dcim/forms/filtersets.py index 169b93028..a6f25ae00 100644 --- a/netbox/dcim/forms/filtersets.py +++ b/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) diff --git a/netbox/dcim/forms/models.py b/netbox/dcim/forms/models.py index cd697e9f3..4cfa40fe2 100644 --- a/netbox/dcim/forms/models.py +++ b/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(), } diff --git a/netbox/dcim/forms/object_create.py b/netbox/dcim/forms/object_create.py index db28412e6..3998dcbc1 100644 --- a/netbox/dcim/forms/object_create.py +++ b/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): diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py index 55f1ba150..3d489973c 100644 --- a/netbox/dcim/graphql/types.py +++ b/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 diff --git a/netbox/dcim/migrations/0136_wireless.py b/netbox/dcim/migrations/0136_wireless.py index 3b33f7d3f..7a3ee9673 100644 --- a/netbox/dcim/migrations/0136_wireless.py +++ b/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', diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index f8649b419..74724baf8 100644 --- a/netbox/dcim/models/device_components.py +++ b/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 diff --git a/netbox/dcim/tables/devices.py b/netbox/dcim/tables/devices.py index a375a77cc..7f2ff1c71 100644 --- a/netbox/dcim/tables/devices.py +++ b/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 = ( diff --git a/netbox/templates/dcim/interface.html b/netbox/templates/dcim/interface.html index bf24a89ef..90c9497ef 100644 --- a/netbox/templates/dcim/interface.html +++ b/netbox/templates/dcim/interface.html @@ -39,16 +39,6 @@ Type {{ object.get_type_display }} - {% if object.is_wireless %} - - Channel - {{ object.get_rf_channel_display|placeholder }} - - - Channel Width - {{ object.get_rf_channel_width_display|placeholder }} - - {% endif %} Enabled @@ -274,6 +264,25 @@ {% endif %} {% if object.is_wireless %} +
+
Wireless
+
+ + + + + + + + + + + + + +
Role{{ object.get_rf_role_display|placeholder }}
Channel{{ object.get_rf_channel_display|placeholder }}
Channel Width{{ object.get_rf_channel_width_display|placeholder }}
+
+
Wireless LANs
diff --git a/netbox/templates/dcim/interface_edit.html b/netbox/templates/dcim/interface_edit.html index 51834f4e2..cb8d51828 100644 --- a/netbox/templates/dcim/interface_edit.html +++ b/netbox/templates/dcim/interface_edit.html @@ -34,6 +34,7 @@
Wireless
+ {% render_field form.rf_role %} {% render_field form.rf_channel %} {% render_field form.rf_channel_width %} {% render_field form.wireless_lans %}