10711 Add Scope to WirelessLAN (#17877)

* 7699 Add Scope to Cluster

* 7699 Serializer

* 7699 filterset

* 7699 bulk_edit

* 7699 bulk_import

* 7699 model_form

* 7699 graphql, tables

* 7699 fixes

* 7699 fixes

* 7699 fixes

* 7699 fixes

* 7699 fix tests

* 7699 fix graphql tests for clusters reference

* 7699 fix dcim tests

* 7699 fix ipam tests

* 7699 fix tests

* 7699 use mixin for model

* 7699 change mixin name

* 7699 scope form

* 7699 scope form

* 7699 scoped form, fitlerset

* 7699 review changes

* 7699 move ScopedFilterset

* 7699 move CachedScopeMixin

* 7699 review changes

* 10711 Add Scope to WirelessLAN

* 10711 Add Scope to WirelessLAN

* 10711 Add Scope to WirelessLAN

* 10711 Add Scope to WirelessLAN

* 10711 Add Scope to WirelessLAN

* 7699 review changes

* 7699 refactor mixins

* 7699 _sitegroup -> _site_group

* 7699 update docstring

* fix model

* remove old constants, update filtersets

* 10711 fix GraphQL

* 10711 fix API

* 10711 add tests

* 10711 review changes

* 10711 add tests

* 10711 add scope to detail template

* 10711 add api test

* Extend CSV test data

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
This commit is contained in:
Arthur Hanson 2024-11-07 07:28:02 -08:00 committed by GitHub
parent 4bba92617d
commit 812ce8471a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 277 additions and 30 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

@ -22,6 +22,14 @@
<th scope="row">{% trans "Status" %}</th> <th scope="row">{% trans "Status" %}</th>
<td>{% badge object.get_status_display bg_color=object.get_status_color %}</td> <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
</tr> </tr>
<tr>
<th scope="row">{% trans "Scope" %}</th>
{% if object.scope %}
<td>{{ object.scope|linkify }} ({% trans object.scope_type.name %})</td>
{% else %}
<td>{{ ''|placeholder }}</td>
{% endif %}
</tr>
<tr> <tr>
<th scope="row">{% trans "Description" %}</th> <th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td> <td>{{ object.description|placeholder }}</td>

View File

@ -1,9 +1,13 @@
from rest_framework import serializers from rest_framework import serializers
from dcim.constants import LOCATION_SCOPE_TYPES
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.utils import extend_schema_field
from ipam.api.serializers_.vlans import VLANSerializer from ipam.api.serializers_.vlans import VLANSerializer
from netbox.api.fields import ChoiceField from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
from tenancy.api.serializers_.tenants import TenantSerializer from tenancy.api.serializers_.tenants import TenantSerializer
from utilities.api import get_serializer_for_model
from wireless.choices import * from wireless.choices import *
from wireless.models import WirelessLAN, WirelessLANGroup from wireless.models import WirelessLAN, WirelessLANGroup
from .nested import NestedWirelessLANGroupSerializer from .nested import NestedWirelessLANGroupSerializer
@ -34,12 +38,30 @@ 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_type = ContentTypeField(
queryset=ContentType.objects.filter(
model__in=LOCATION_SCOPE_TYPES
),
allow_null=True,
required=False,
default=None
)
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', 'scope',
'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')
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_scope(self, obj):
if obj.scope_id is None:
return None
serializer = get_serializer_for_model(obj.scope)
context = {'request': self.context['request']}
return serializer(obj.scope, nested=True, context=context).data

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

@ -2,6 +2,7 @@ from django import forms
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.forms.mixins import ScopedBulkEditForm
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
@ -39,7 +40,7 @@ class WirelessLANGroupBulkEditForm(NetBoxModelBulkEditForm):
nullable_fields = ('parent', 'description') nullable_fields = ('parent', 'description')
class WirelessLANBulkEditForm(NetBoxModelBulkEditForm): class WirelessLANBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
status = forms.ChoiceField( status = forms.ChoiceField(
label=_('Status'), label=_('Status'),
choices=add_blank_choice(WirelessLANStatusChoices), choices=add_blank_choice(WirelessLANStatusChoices),
@ -89,10 +90,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,12 +1,13 @@
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.forms.mixins import ScopedImportForm
from dcim.models import Interface from dcim.models import Interface
from ipam.models import VLAN 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, CSVModelChoiceField, SlugField
from wireless.choices import * from wireless.choices import *
from wireless.models import * from wireless.models import *
@ -32,7 +33,7 @@ class WirelessLANGroupImportForm(NetBoxModelImportForm):
fields = ('name', 'slug', 'parent', 'description', 'tags') fields = ('name', 'slug', 'parent', 'description', 'tags')
class WirelessLANImportForm(NetBoxModelImportForm): class WirelessLANImportForm(ScopedImportForm, NetBoxModelImportForm):
group = CSVModelChoiceField( group = CSVModelChoiceField(
label=_('Group'), label=_('Group'),
queryset=WirelessLANGroup.objects.all(), queryset=WirelessLANGroup.objects.all(),
@ -75,9 +76,12 @@ class WirelessLANImportForm(NetBoxModelImportForm):
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

@ -2,6 +2,7 @@ from django import forms
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 Location, Region, Site, SiteGroup
from netbox.choices import * from netbox.choices import *
from netbox.forms import NetBoxModelFilterSetForm from netbox.forms import NetBoxModelFilterSetForm
from tenancy.forms import TenancyFilterForm from tenancy.forms import TenancyFilterForm
@ -33,6 +34,7 @@ class WirelessLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
fieldsets = ( fieldsets = (
FieldSet('q', 'filter_id', 'tag'), FieldSet('q', 'filter_id', 'tag'),
FieldSet('ssid', 'group_id', 'status', name=_('Attributes')), FieldSet('ssid', 'group_id', 'status', name=_('Attributes')),
FieldSet('region_id', 'site_group_id', 'site_id', 'location_id', name=_('Scope')),
FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')), FieldSet('tenant_group_id', 'tenant_id', name=_('Tenant')),
FieldSet('auth_type', 'auth_cipher', 'auth_psk', name=_('Authentication')), FieldSet('auth_type', 'auth_cipher', 'auth_psk', name=_('Authentication')),
) )
@ -65,6 +67,31 @@ class WirelessLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
label=_('Pre-shared key'), label=_('Pre-shared key'),
required=False required=False
) )
region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(),
required=False,
label=_('Region')
)
site_group_id = DynamicModelMultipleChoiceField(
queryset=SiteGroup.objects.all(),
required=False,
label=_('Site group')
)
site_id = DynamicModelMultipleChoiceField(
queryset=Site.objects.all(),
required=False,
null_option='None',
query_params={
'region_id': '$region_id',
'site_group_id': '$site_group_id',
},
label=_('Site')
)
location_id = DynamicModelMultipleChoiceField(
queryset=Location.objects.all(),
required=False,
label=_('Location')
)
tag = TagFilterField(model) tag = TagFilterField(model)

View File

@ -2,6 +2,7 @@ from django.forms import PasswordInput
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from dcim.models import Device, Interface, Location, Site from dcim.models import Device, Interface, Location, Site
from dcim.forms.mixins import ScopedForm
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
@ -35,7 +36,7 @@ class WirelessLANGroupForm(NetBoxModelForm):
] ]
class WirelessLANForm(TenancyForm, NetBoxModelForm): class WirelessLANForm(ScopedForm, TenancyForm, NetBoxModelForm):
group = DynamicModelChoiceField( group = DynamicModelChoiceField(
label=_('Group'), label=_('Group'),
queryset=WirelessLANGroup.objects.all(), queryset=WirelessLANGroup.objects.all(),
@ -51,6 +52,7 @@ class WirelessLANForm(TenancyForm, NetBoxModelForm):
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 +61,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
@ -28,7 +28,7 @@ class WirelessLANGroupType(OrganizationalObjectType):
@strawberry_django.type( @strawberry_django.type(
models.WirelessLAN, models.WirelessLAN,
fields='__all__', exclude=('scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'),
filters=WirelessLANFilter filters=WirelessLANFilter
) )
class WirelessLANType(NetBoxObjectType): class WirelessLANType(NetBoxObjectType):
@ -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

@ -0,0 +1,77 @@
# Generated by Django 5.0.9 on 2024-11-04 16:00
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('dcim', '0196_qinq_svlan'),
('wireless', '0010_charfield_null_choices'),
]
operations = [
migrations.AddField(
model_name='wirelesslan',
name='_location',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name='_%(class)ss',
to='dcim.location',
),
),
migrations.AddField(
model_name='wirelesslan',
name='_region',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name='_%(class)ss',
to='dcim.region',
),
),
migrations.AddField(
model_name='wirelesslan',
name='_site',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name='_%(class)ss',
to='dcim.site',
),
),
migrations.AddField(
model_name='wirelesslan',
name='_site_group',
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name='_%(class)ss',
to='dcim.sitegroup',
),
),
migrations.AddField(
model_name='wirelesslan',
name='scope_id',
field=models.PositiveBigIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='wirelesslan',
name='scope_type',
field=models.ForeignKey(
blank=True,
limit_choices_to=models.Q(('model__in', ('region', 'sitegroup', 'site', 'location'))),
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name='+',
to='contenttypes.contenttype',
),
),
]

View File

@ -4,6 +4,7 @@ from django.utils.translation import gettext_lazy as _
from dcim.choices import LinkStatusChoices from dcim.choices import LinkStatusChoices
from dcim.constants import WIRELESS_IFACE_TYPES from dcim.constants import WIRELESS_IFACE_TYPES
from dcim.models.mixins import CachedScopeMixin
from netbox.models import NestedGroupModel, PrimaryModel from netbox.models import NestedGroupModel, PrimaryModel
from netbox.models.mixins import DistanceMixin from netbox.models.mixins import DistanceMixin
from .choices import * from .choices import *
@ -71,7 +72,7 @@ class WirelessLANGroup(NestedGroupModel):
verbose_name_plural = _('wireless LAN groups') verbose_name_plural = _('wireless LAN groups')
class WirelessLAN(WirelessAuthenticationBase, PrimaryModel): class WirelessLAN(WirelessAuthenticationBase, CachedScopeMixin, PrimaryModel):
""" """
A wireless network formed among an arbitrary number of access point and clients. A wireless network formed among an arbitrary number of access point and clients.
""" """
@ -107,7 +108,7 @@ class WirelessLAN(WirelessAuthenticationBase, PrimaryModel):
null=True null=True
) )
clone_fields = ('ssid', 'group', 'tenant', 'description') clone_fields = ('ssid', 'group', 'scope_type', 'scope_id', 'tenant', 'description')
class Meta: class Meta:
ordering = ('ssid', 'pk') ordering = ('ssid', 'pk')

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

View File

@ -1,7 +1,7 @@
from django.urls import reverse from django.urls import reverse
from dcim.choices import InterfaceTypeChoices from dcim.choices import InterfaceTypeChoices
from dcim.models import Interface from dcim.models import Interface, Site
from tenancy.models import Tenant from tenancy.models import Tenant
from utilities.testing import APITestCase, APIViewTestCases, create_test_device from utilities.testing import APITestCase, APIViewTestCases, create_test_device
from wireless.choices import * from wireless.choices import *
@ -53,6 +53,12 @@ class WirelessLANTest(APIViewTestCases.APIViewTestCase):
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
sites = (
Site(name='Site 1', slug='site-1'),
Site(name='Site 2', slug='site-2'),
)
Site.objects.bulk_create(sites)
tenants = ( tenants = (
Tenant(name='Tenant 1', slug='tenant-1'), Tenant(name='Tenant 1', slug='tenant-1'),
Tenant(name='Tenant 2', slug='tenant-2'), Tenant(name='Tenant 2', slug='tenant-2'),
@ -94,6 +100,8 @@ class WirelessLANTest(APIViewTestCases.APIViewTestCase):
'status': WirelessLANStatusChoices.STATUS_DISABLED, 'status': WirelessLANStatusChoices.STATUS_DISABLED,
'tenant': tenants[0].pk, 'tenant': tenants[0].pk,
'auth_type': WirelessAuthTypeChoices.TYPE_WPA_ENTERPRISE, 'auth_type': WirelessAuthTypeChoices.TYPE_WPA_ENTERPRISE,
'scope_type': 'dcim.site',
'scope_id': sites[1].pk,
}, },
] ]

View File

@ -1,7 +1,7 @@
from django.test import TestCase from django.test import TestCase
from dcim.choices import InterfaceTypeChoices, LinkStatusChoices from dcim.choices import InterfaceTypeChoices, LinkStatusChoices
from dcim.models import Interface from dcim.models import Interface, Location, Region, Site, SiteGroup
from ipam.models import VLAN from ipam.models import VLAN
from netbox.choices import DistanceUnitChoices from netbox.choices import DistanceUnitChoices
from tenancy.models import Tenant from tenancy.models import Tenant
@ -110,6 +110,36 @@ class WirelessLANTestCase(TestCase, ChangeLoggedFilterSetTests):
) )
VLAN.objects.bulk_create(vlans) VLAN.objects.bulk_create(vlans)
regions = (
Region(name='Test Region 1', slug='test-region-1'),
Region(name='Test Region 2', slug='test-region-2'),
Region(name='Test Region 3', slug='test-region-3'),
)
for r in regions:
r.save()
site_groups = (
SiteGroup(name='Site Group 1', slug='site-group-1'),
SiteGroup(name='Site Group 2', slug='site-group-2'),
SiteGroup(name='Site Group 3', slug='site-group-3'),
)
for site_group in site_groups:
site_group.save()
sites = (
Site(name='Test Site 1', slug='test-site-1', region=regions[0], group=site_groups[0]),
Site(name='Test Site 2', slug='test-site-2', region=regions[1], group=site_groups[1]),
Site(name='Test Site 3', slug='test-site-3', region=regions[2], group=site_groups[2]),
)
Site.objects.bulk_create(sites)
locations = (
Location(name='Location 1', slug='location-1', site=sites[0]),
Location(name='Location 2', slug='location-2', site=sites[2]),
)
for location in locations:
location.save()
tenants = ( tenants = (
Tenant(name='Tenant 1', slug='tenant-1'), Tenant(name='Tenant 1', slug='tenant-1'),
Tenant(name='Tenant 2', slug='tenant-2'), Tenant(name='Tenant 2', slug='tenant-2'),
@ -127,7 +157,8 @@ class WirelessLANTestCase(TestCase, ChangeLoggedFilterSetTests):
auth_type=WirelessAuthTypeChoices.TYPE_OPEN, auth_type=WirelessAuthTypeChoices.TYPE_OPEN,
auth_cipher=WirelessAuthCipherChoices.CIPHER_AUTO, auth_cipher=WirelessAuthCipherChoices.CIPHER_AUTO,
auth_psk='PSK1', auth_psk='PSK1',
description='foobar1' description='foobar1',
scope=sites[0]
), ),
WirelessLAN( WirelessLAN(
ssid='WLAN2', ssid='WLAN2',
@ -138,7 +169,8 @@ class WirelessLANTestCase(TestCase, ChangeLoggedFilterSetTests):
auth_type=WirelessAuthTypeChoices.TYPE_WEP, auth_type=WirelessAuthTypeChoices.TYPE_WEP,
auth_cipher=WirelessAuthCipherChoices.CIPHER_TKIP, auth_cipher=WirelessAuthCipherChoices.CIPHER_TKIP,
auth_psk='PSK2', auth_psk='PSK2',
description='foobar2' description='foobar2',
scope=locations[0]
), ),
WirelessLAN( WirelessLAN(
ssid='WLAN3', ssid='WLAN3',
@ -149,12 +181,14 @@ class WirelessLANTestCase(TestCase, ChangeLoggedFilterSetTests):
auth_type=WirelessAuthTypeChoices.TYPE_WPA_PERSONAL, auth_type=WirelessAuthTypeChoices.TYPE_WPA_PERSONAL,
auth_cipher=WirelessAuthCipherChoices.CIPHER_AES, auth_cipher=WirelessAuthCipherChoices.CIPHER_AES,
auth_psk='PSK3', auth_psk='PSK3',
description='foobar3' description='foobar3',
scope=locations[1]
), ),
) )
WirelessLAN.objects.bulk_create(wireless_lans) for wireless_lan in wireless_lans:
wireless_lan.save()
device = create_test_device('Device 1') device = create_test_device('Device 1', site=sites[0])
interfaces = ( interfaces = (
Interface(device=device, name='Interface 1', type=InterfaceTypeChoices.TYPE_80211N), Interface(device=device, name='Interface 1', type=InterfaceTypeChoices.TYPE_80211N),
Interface(device=device, name='Interface 2', type=InterfaceTypeChoices.TYPE_80211N), Interface(device=device, name='Interface 2', type=InterfaceTypeChoices.TYPE_80211N),
@ -217,6 +251,38 @@ class WirelessLANTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'interface_id': [interfaces[0].pk, interfaces[1].pk]} params = {'interface_id': [interfaces[0].pk, interfaces[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2) self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_region(self):
regions = Region.objects.all()[:2]
params = {'region_id': [regions[0].pk, regions[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'region': [regions[0].slug, regions[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_site_group(self):
site_groups = SiteGroup.objects.all()[:2]
params = {'site_group_id': [site_groups[0].pk, site_groups[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'site_group': [site_groups[0].slug, site_groups[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_site(self):
sites = Site.objects.all()[:2]
params = {'site_id': [sites[0].pk, sites[1].pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
params = {'site': [sites[0].slug, sites[1].slug]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_location(self):
locations = Location.objects.all()[:1]
params = {'location_id': [locations[0].pk,]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
params = {'location': [locations[0].slug,]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
def test_scope_type(self):
params = {'scope_type': 'dcim.location'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class WirelessLinkTestCase(TestCase, ChangeLoggedFilterSetTests): class WirelessLinkTestCase(TestCase, ChangeLoggedFilterSetTests):
queryset = WirelessLink.objects.all() queryset = WirelessLink.objects.all()

View File

@ -1,7 +1,8 @@
from django.contrib.contenttypes.models import ContentType
from wireless.choices import * from wireless.choices import *
from wireless.models import * from wireless.models import *
from dcim.choices import InterfaceTypeChoices, LinkStatusChoices from dcim.choices import InterfaceTypeChoices, LinkStatusChoices
from dcim.models import Interface from dcim.models import Interface, Site
from netbox.choices import DistanceUnitChoices from netbox.choices import DistanceUnitChoices
from tenancy.models import Tenant from tenancy.models import Tenant
from utilities.testing import ViewTestCases, create_tags, create_test_device from utilities.testing import ViewTestCases, create_tags, create_test_device
@ -56,6 +57,12 @@ class WirelessLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
sites = (
Site(name='Site 1', slug='site-1'),
Site(name='Site 2', slug='site-2'),
)
Site.objects.bulk_create(sites)
tenants = ( tenants = (
Tenant(name='Tenant 1', slug='tenant-1'), Tenant(name='Tenant 1', slug='tenant-1'),
Tenant(name='Tenant 2', slug='tenant-2'), Tenant(name='Tenant 2', slug='tenant-2'),
@ -98,15 +105,17 @@ class WirelessLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
'ssid': 'WLAN2', 'ssid': 'WLAN2',
'group': groups[1].pk, 'group': groups[1].pk,
'status': WirelessLANStatusChoices.STATUS_DISABLED, 'status': WirelessLANStatusChoices.STATUS_DISABLED,
'scope_type': ContentType.objects.get_for_model(Site).pk,
'scope': sites[1].pk,
'tenant': tenants[1].pk, 'tenant': tenants[1].pk,
'tags': [t.pk for t in tags], 'tags': [t.pk for t in tags],
} }
cls.csv_data = ( cls.csv_data = (
"group,ssid,status,tenant", "group,ssid,status,tenant,scope_type,scope_id",
f"Wireless LAN Group 2,WLAN4,{WirelessLANStatusChoices.STATUS_ACTIVE},{tenants[0].name}", f"Wireless LAN Group 2,WLAN4,{WirelessLANStatusChoices.STATUS_ACTIVE},{tenants[0].name},,",
f"Wireless LAN Group 2,WLAN5,{WirelessLANStatusChoices.STATUS_DISABLED},{tenants[1].name}", f"Wireless LAN Group 2,WLAN5,{WirelessLANStatusChoices.STATUS_DISABLED},{tenants[1].name},dcim.site,{sites[0].pk}",
f"Wireless LAN Group 2,WLAN6,{WirelessLANStatusChoices.STATUS_RESERVED},{tenants[2].name}", f"Wireless LAN Group 2,WLAN6,{WirelessLANStatusChoices.STATUS_RESERVED},{tenants[2].name},dcim.site,{sites[1].pk}",
) )
cls.csv_update_data = ( cls.csv_update_data = (