10711 Add Scope to WirelessLAN

This commit is contained in:
Arthur Hanson 2024-10-28 11:43:30 -07:00
parent 4d01069f25
commit 06c8a92423
8 changed files with 81 additions and 14 deletions

View File

@ -43,3 +43,7 @@ The security cipher used to apply wireless authentication. Options include:
### Pre-Shared Key ### Pre-Shared Key
The security key configured on each client to grant access to the secured wireless LAN. This applies only to certain authentication types. The security key configured on each client to grant access to the secured wireless LAN. This applies only to certain authentication types.
### Scope
The [region](../dcim/region.md), [site](../dcim/site.md), [site group](../dcim/sitegroup.md) or [location](../dcim/location.md) with which this wireless LAN is associated.

View File

@ -34,12 +34,14 @@ class WirelessLANSerializer(NetBoxModelSerializer):
tenant = TenantSerializer(nested=True, required=False, allow_null=True) tenant = TenantSerializer(nested=True, required=False, allow_null=True)
auth_type = ChoiceField(choices=WirelessAuthTypeChoices, required=False, allow_blank=True) auth_type = ChoiceField(choices=WirelessAuthTypeChoices, required=False, allow_blank=True)
auth_cipher = ChoiceField(choices=WirelessAuthCipherChoices, required=False, allow_blank=True) auth_cipher = ChoiceField(choices=WirelessAuthCipherChoices, required=False, allow_blank=True)
scope_id = serializers.IntegerField(allow_null=True, required=False, default=None)
scope = serializers.SerializerMethodField(read_only=True)
class Meta: class Meta:
model = WirelessLAN model = WirelessLAN
fields = [ fields = [
'id', 'url', 'display_url', 'display', 'ssid', 'description', 'group', 'status', 'vlan', 'tenant', 'id', 'url', 'display_url', 'display', 'ssid', 'description', 'group', 'status', 'vlan', 'scope_type', 'scope_id',
'auth_type', 'auth_cipher', 'auth_psk', 'description', 'comments', 'tags', 'custom_fields', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk', 'description', 'comments', 'tags', 'custom_fields',
'created', 'last_updated', 'created', 'last_updated',
] ]
brief_fields = ('id', 'url', 'display', 'ssid', 'description') brief_fields = ('id', 'url', 'display', 'ssid', 'description')

View File

@ -2,6 +2,7 @@ import django_filters
from django.db.models import Q from django.db.models import Q
from dcim.choices import LinkStatusChoices from dcim.choices import LinkStatusChoices
from dcim.filtersets import ScopedFilterSet
from dcim.models import Interface from dcim.models import Interface
from ipam.models import VLAN from ipam.models import VLAN
from netbox.filtersets import OrganizationalModelFilterSet, NetBoxModelFilterSet from netbox.filtersets import OrganizationalModelFilterSet, NetBoxModelFilterSet
@ -43,7 +44,7 @@ class WirelessLANGroupFilterSet(OrganizationalModelFilterSet):
fields = ('id', 'name', 'slug', 'description') fields = ('id', 'name', 'slug', 'description')
class WirelessLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet): class WirelessLANFilterSet(NetBoxModelFilterSet, ScopedFilterSet, TenancyFilterSet):
group_id = TreeNodeMultipleChoiceFilter( group_id = TreeNodeMultipleChoiceFilter(
queryset=WirelessLANGroup.objects.all(), queryset=WirelessLANGroup.objects.all(),
field_name='group', field_name='group',
@ -74,7 +75,7 @@ class WirelessLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
class Meta: class Meta:
model = WirelessLAN model = WirelessLAN
fields = ('id', 'ssid', 'auth_psk', 'description') fields = ('id', 'ssid', 'auth_psk', 'scope_id', 'description')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():

View File

@ -1,16 +1,19 @@
from django import forms from django import forms
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from dcim.choices import LinkStatusChoices from dcim.choices import LinkStatusChoices
from dcim.models import Site
from ipam.models import VLAN from ipam.models import VLAN
from netbox.choices import * from netbox.choices import *
from netbox.forms import NetBoxModelBulkEditForm from netbox.forms import NetBoxModelBulkEditForm
from tenancy.models import Tenant from tenancy.models import Tenant
from utilities.forms import add_blank_choice from utilities.forms import add_blank_choice
from utilities.forms.fields import CommentField, DynamicModelChoiceField from utilities.forms.fields import CommentField, ContentTypeChoiceField, DynamicModelChoiceField
from utilities.forms.rendering import FieldSet from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import HTMXSelect
from wireless.choices import * from wireless.choices import *
from wireless.constants import SSID_MAX_LENGTH from wireless.constants import WIRELESSLAN_SCOPE_TYPES, SSID_MAX_LENGTH
from wireless.models import * from wireless.models import *
__all__ = ( __all__ = (
@ -79,6 +82,19 @@ class WirelessLANBulkEditForm(NetBoxModelBulkEditForm):
required=False, required=False,
label=_('Pre-shared key') label=_('Pre-shared key')
) )
scope_type = ContentTypeChoiceField(
queryset=ContentType.objects.filter(model__in=WIRELESSLAN_SCOPE_TYPES),
widget=HTMXSelect(method='post', attrs={'hx-select': '#form_fields'}),
required=False,
label=_('Scope type')
)
scope = DynamicModelChoiceField(
label=_('Scope'),
queryset=Site.objects.none(), # Initial queryset
required=False,
disabled=True,
selector=True
)
description = forms.CharField( description = forms.CharField(
label=_('Description'), label=_('Description'),
max_length=200, max_length=200,
@ -89,10 +105,11 @@ class WirelessLANBulkEditForm(NetBoxModelBulkEditForm):
model = WirelessLAN model = WirelessLAN
fieldsets = ( fieldsets = (
FieldSet('group', 'ssid', 'status', 'vlan', 'tenant', 'description'), FieldSet('group', 'ssid', 'status', 'vlan', 'tenant', 'description'),
FieldSet('scope_type', 'scope', name=_('Scope')),
FieldSet('auth_type', 'auth_cipher', 'auth_psk', name=_('Authentication')), FieldSet('auth_type', 'auth_cipher', 'auth_psk', name=_('Authentication')),
) )
nullable_fields = ( nullable_fields = (
'ssid', 'group', 'vlan', 'tenant', 'description', 'auth_type', 'auth_cipher', 'auth_psk', 'comments', 'ssid', 'group', 'vlan', 'tenant', 'description', 'auth_type', 'auth_cipher', 'auth_psk', 'scope', 'comments',
) )

View File

@ -1,3 +1,4 @@
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from dcim.choices import LinkStatusChoices from dcim.choices import LinkStatusChoices
@ -6,8 +7,9 @@ from ipam.models import VLAN
from netbox.choices import * from netbox.choices import *
from netbox.forms import NetBoxModelImportForm from netbox.forms import NetBoxModelImportForm
from tenancy.models import Tenant from tenancy.models import Tenant
from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, SlugField from utilities.forms.fields import CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, SlugField
from wireless.choices import * from wireless.choices import *
from wireless.constants import WIRELESSLAN_SCOPE_TYPES
from wireless.models import * from wireless.models import *
__all__ = ( __all__ = (
@ -71,13 +73,21 @@ class WirelessLANImportForm(NetBoxModelImportForm):
required=False, required=False,
help_text=_('Authentication cipher') help_text=_('Authentication cipher')
) )
scope_type = CSVContentTypeField(
queryset=ContentType.objects.filter(model__in=WIRELESSLAN_SCOPE_TYPES),
required=False,
label=_('Scope type (app & model)')
)
class Meta: class Meta:
model = WirelessLAN model = WirelessLAN
fields = ( fields = (
'ssid', 'group', 'status', 'vlan', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk', 'description', 'ssid', 'group', 'status', 'vlan', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk', 'scope_type', 'scope_id',
'comments', 'tags', 'description', 'comments', 'tags',
) )
labels = {
'scope_id': 'Scope ID',
}
class WirelessLinkImportForm(NetBoxModelImportForm): class WirelessLinkImportForm(NetBoxModelImportForm):

View File

@ -1,3 +1,4 @@
from django.contrib.contenttypes.models import ContentType
from django.forms import PasswordInput from django.forms import PasswordInput
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -5,8 +6,10 @@ from dcim.models import Device, Interface, Location, Site
from ipam.models import VLAN from ipam.models import VLAN
from netbox.forms import NetBoxModelForm from netbox.forms import NetBoxModelForm
from tenancy.forms import TenancyForm from tenancy.forms import TenancyForm
from utilities.forms.fields import CommentField, DynamicModelChoiceField, SlugField from utilities.forms.fields import CommentField, ContentTypeChoiceField, DynamicModelChoiceField, SlugField
from utilities.forms.rendering import FieldSet, InlineFields from utilities.forms.rendering import FieldSet, InlineFields
from utilities.forms.widgets import HTMXSelect
from wireless.constants import WIRELESSLAN_SCOPE_TYPES
from wireless.models import * from wireless.models import *
__all__ = ( __all__ = (
@ -47,10 +50,24 @@ class WirelessLANForm(TenancyForm, NetBoxModelForm):
selector=True, selector=True,
label=_('VLAN') label=_('VLAN')
) )
scope_type = ContentTypeChoiceField(
queryset=ContentType.objects.filter(model__in=WIRELESSLAN_SCOPE_TYPES),
widget=HTMXSelect(),
required=False,
label=_('Scope type')
)
scope = DynamicModelChoiceField(
label=_('Scope'),
queryset=Site.objects.none(), # Initial queryset
required=False,
disabled=True,
selector=True
)
comments = CommentField() comments = CommentField()
fieldsets = ( fieldsets = (
FieldSet('ssid', 'group', 'vlan', 'status', 'description', 'tags', name=_('Wireless LAN')), FieldSet('ssid', 'group', 'vlan', 'status', 'description', 'tags', name=_('Wireless LAN')),
FieldSet('scope_type', 'scope', name=_('Scope')),
FieldSet('tenant_group', 'tenant', name=_('Tenancy')), FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
FieldSet('auth_type', 'auth_cipher', 'auth_psk', name=_('Authentication')), FieldSet('auth_type', 'auth_cipher', 'auth_psk', name=_('Authentication')),
) )
@ -59,7 +76,7 @@ class WirelessLANForm(TenancyForm, NetBoxModelForm):
model = WirelessLAN model = WirelessLAN
fields = [ fields = [
'ssid', 'group', 'status', 'vlan', 'tenant_group', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk', 'ssid', 'group', 'status', 'vlan', 'tenant_group', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk',
'description', 'comments', 'tags', 'scope_type', 'description', 'comments', 'tags',
] ]
widgets = { widgets = {
'auth_psk': PasswordInput( 'auth_psk': PasswordInput(

View File

@ -1,4 +1,4 @@
from typing import Annotated, List from typing import Annotated, List, Union
import strawberry import strawberry
import strawberry_django import strawberry_django
@ -38,6 +38,15 @@ class WirelessLANType(NetBoxObjectType):
interfaces: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]] interfaces: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]
@strawberry_django.field
def scope(self) -> Annotated[Union[
Annotated["LocationType", strawberry.lazy('dcim.graphql.types')],
Annotated["RegionType", strawberry.lazy('dcim.graphql.types')],
Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')],
Annotated["SiteType", strawberry.lazy('dcim.graphql.types')],
], strawberry.union("WirelessLANScopeType")] | None:
return self.scope
@strawberry_django.type( @strawberry_django.type(
models.WirelessLink, models.WirelessLink,

View File

@ -51,6 +51,13 @@ class WirelessLANTable(TenancyColumnsMixin, NetBoxTable):
status = columns.ChoiceFieldColumn( status = columns.ChoiceFieldColumn(
verbose_name=_('Status'), verbose_name=_('Status'),
) )
scope_type = columns.ContentTypeColumn(
verbose_name=_('Scope Type'),
)
scope = tables.Column(
verbose_name=_('Scope'),
linkify=True
)
interface_count = tables.Column( interface_count = tables.Column(
verbose_name=_('Interfaces') verbose_name=_('Interfaces')
) )
@ -65,7 +72,7 @@ class WirelessLANTable(TenancyColumnsMixin, NetBoxTable):
model = WirelessLAN model = WirelessLAN
fields = ( fields = (
'pk', 'ssid', 'group', 'status', 'tenant', 'tenant_group', 'vlan', 'interface_count', 'auth_type', 'pk', 'ssid', 'group', 'status', 'tenant', 'tenant_group', 'vlan', 'interface_count', 'auth_type',
'auth_cipher', 'auth_psk', 'description', 'comments', 'tags', 'created', 'last_updated', 'auth_cipher', 'auth_psk', 'scope', 'scope_type', 'description', 'comments', 'tags', 'created', 'last_updated',
) )
default_columns = ('pk', 'ssid', 'group', 'status', 'description', 'vlan', 'auth_type', 'interface_count') default_columns = ('pk', 'ssid', 'group', 'status', 'description', 'vlan', 'auth_type', 'interface_count')