Add rf_role to Interface

This commit is contained in:
jeremystretch 2021-10-13 20:16:36 -04:00
parent 01f791a44e
commit 438b4b4758
14 changed files with 86 additions and 30 deletions

View File

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

View File

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

View File

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

View File

@ -926,7 +926,7 @@ class PowerOutletBulkEditForm(
class InterfaceBulkEditForm( class InterfaceBulkEditForm(
form_from_model(Interface, [ form_from_model(Interface, [
'label', 'type', 'parent', 'lag', 'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected', 'description', '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, BootstrapMixin,
AddRemoveTagsForm, AddRemoveTagsForm,

View File

@ -579,12 +579,17 @@ class InterfaceCSVForm(CustomFieldModelCSVForm):
required=False, required=False,
help_text='IEEE 802.1Q operational mode (for L2 interfaces)' 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: class Meta:
model = Interface model = Interface
fields = ( fields = (
'device', 'name', 'label', 'parent', 'lag', 'type', 'enabled', 'mark_connected', 'mac_address', 'wwn', '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): def __init__(self, *args, **kwargs):

View File

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

View File

@ -1104,13 +1104,14 @@ class InterfaceForm(BootstrapMixin, InterfaceCommonForm, CustomFieldModelForm):
model = Interface model = Interface
fields = [ fields = [
'device', 'name', 'label', 'type', 'enabled', 'parent', 'lag', 'mac_address', 'wwn', 'mtu', 'mgmt_only', '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', 'mark_connected', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_width', 'wireless_lans',
'tagged_vlans', 'tags', 'untagged_vlan', 'tagged_vlans', 'tags',
] ]
widgets = { widgets = {
'device': forms.HiddenInput(), 'device': forms.HiddenInput(),
'type': StaticSelect(), 'type': StaticSelect(),
'mode': StaticSelect(), 'mode': StaticSelect(),
'rf_role': StaticSelect(),
'rf_channel': StaticSelect(), 'rf_channel': StaticSelect(),
'rf_channel_width': StaticSelect(), 'rf_channel_width': StaticSelect(),
} }

View File

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

View File

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

View File

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

View File

@ -524,6 +524,12 @@ class Interface(ComponentModel, BaseInterface, LinkTermination, PathEndpoint):
verbose_name='WWN', verbose_name='WWN',
help_text='64-bit World Wide Name' 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( rf_channel = models.CharField(
max_length=50, max_length=50,
choices=WirelessChannelChoices, choices=WirelessChannelChoices,
@ -636,9 +642,11 @@ class Interface(ComponentModel, BaseInterface, LinkTermination, PathEndpoint):
raise ValidationError({'lag': "A LAG interface cannot be its own parent."}) raise ValidationError({'lag': "A LAG interface cannot be its own parent."})
# RF channel attributes may be set only for wireless interfaces # 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."}) 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."}) raise ValidationError({'rf_channel_width': "Channel width may be set only on wireless interfaces."})
# Validate untagged VLAN # Validate untagged VLAN

View File

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

View File

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

View File

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