mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #9177: Add tenant assignment for wireless LANs & links
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
# Wireless LANs
|
# Wireless LANs
|
||||||
|
|
||||||
A wireless LAN is a set of interfaces connected via a common wireless channel. Each instance must have an SSID, and may optionally be correlated to a VLAN. Wireless LANs can be arranged into hierarchical groups.
|
A wireless LAN is a set of interfaces connected via a common wireless channel. Each instance must have an SSID, and may optionally be correlated to a VLAN. Wireless LANs can be arranged into hierarchical groups, and each may be associated with a particular tenant.
|
||||||
|
|
||||||
An interface may be attached to multiple wireless LANs, provided they are all operating on the same channel. Only wireless interfaces may be attached to wireless LANs.
|
An interface may be attached to multiple wireless LANs, provided they are all operating on the same channel. Only wireless interfaces may be attached to wireless LANs.
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Wireless Links
|
# Wireless Links
|
||||||
|
|
||||||
A wireless link represents a connection between exactly two wireless interfaces. It may optionally be assigned an SSID and a description. It may also have a status assigned to it, similar to the cable model.
|
A wireless link represents a connection between exactly two wireless interfaces. It may optionally be assigned an SSID and a description. It may also have a status assigned to it, similar to the cable model. Each wireless link may also be assigned to a particular tenant.
|
||||||
|
|
||||||
Each wireless link may have authentication attributes associated with it, including:
|
Each wireless link may have authentication attributes associated with it, including:
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
* [#8495](https://github.com/netbox-community/netbox/issues/8495) - Enable custom field grouping
|
* [#8495](https://github.com/netbox-community/netbox/issues/8495) - Enable custom field grouping
|
||||||
* [#8995](https://github.com/netbox-community/netbox/issues/8995) - Enable arbitrary ordering of REST API results
|
* [#8995](https://github.com/netbox-community/netbox/issues/8995) - Enable arbitrary ordering of REST API results
|
||||||
* [#9166](https://github.com/netbox-community/netbox/issues/9166) - Add UI visibility toggle for custom fields
|
* [#9166](https://github.com/netbox-community/netbox/issues/9166) - Add UI visibility toggle for custom fields
|
||||||
|
* [#9177](https://github.com/netbox-community/netbox/issues/9177) - Add tenant assignment for wireless LANs & links
|
||||||
* [#9536](https://github.com/netbox-community/netbox/issues/9536) - Track API token usage times
|
* [#9536](https://github.com/netbox-community/netbox/issues/9536) - Track API token usage times
|
||||||
* [#9582](https://github.com/netbox-community/netbox/issues/9582) - Enable assigning config contexts based on device location
|
* [#9582](https://github.com/netbox-community/netbox/issues/9582) - Enable assigning config contexts based on device location
|
||||||
|
|
||||||
@ -70,3 +71,7 @@
|
|||||||
* Added `device` field
|
* Added `device` field
|
||||||
* The `site` field is now directly writable (rather than being inferred from the assigned cluster)
|
* The `site` field is now directly writable (rather than being inferred from the assigned cluster)
|
||||||
* The `cluster` field is now optional. A virtual machine must have a site and/or cluster assigned.
|
* The `cluster` field is now optional. A virtual machine must have a site and/or cluster assigned.
|
||||||
|
wireless.WirelessLAN
|
||||||
|
* Added `tenant` field
|
||||||
|
wireless.WirelessLink
|
||||||
|
* Added `tenant` field
|
||||||
|
@ -61,6 +61,10 @@
|
|||||||
<h2><a href="{% url 'dcim:device_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.device_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.device_count }}</a></h2>
|
<h2><a href="{% url 'dcim:device_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.device_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.device_count }}</a></h2>
|
||||||
<p>Devices</p>
|
<p>Devices</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col col-md-4 text-center">
|
||||||
|
<h2><a href="{% url 'dcim:cable_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.cable_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.cable_count }}</a></h2>
|
||||||
|
<p>Cables</p>
|
||||||
|
</div>
|
||||||
<div class="col col-md-4 text-center">
|
<div class="col col-md-4 text-center">
|
||||||
<h2><a href="{% url 'ipam:vrf_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.vrf_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.vrf_count }}</a></h2>
|
<h2><a href="{% url 'ipam:vrf_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.vrf_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.vrf_count }}</a></h2>
|
||||||
<p>VRFs</p>
|
<p>VRFs</p>
|
||||||
@ -102,8 +106,12 @@
|
|||||||
<p>Clusters</p>
|
<p>Clusters</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col col-md-4 text-center">
|
<div class="col col-md-4 text-center">
|
||||||
<h2><a href="{% url 'dcim:cable_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.cable_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.cable_count }}</a></h2>
|
<h2><a href="{% url 'wireless:wirelesslan_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.wirelesslan_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.wirelesslan_count }}</a></h2>
|
||||||
<p>Cables</p>
|
<p>Wireless LANs</p>
|
||||||
|
</div>
|
||||||
|
<div class="col col-md-4 text-center">
|
||||||
|
<h2><a href="{% url 'wireless:wirelesslink_list' %}?tenant_id={{ object.pk }}" class="stat-btn btn {% if stats.wirelesslink_count %}btn-primary{% else %}btn-outline-dark{% endif %} btn-lg">{{ stats.wirelesslink_count }}</a></h2>
|
||||||
|
<p>Wireless Links</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,36 +6,45 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col col-md-6">
|
<div class="col col-md-6">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h5 class="card-header">Wireless LAN</h5>
|
<h5 class="card-header">Wireless LAN</h5>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<table class="table table-hover attr-table">
|
<table class="table table-hover attr-table">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">SSID</th>
|
<th scope="row">SSID</th>
|
||||||
<td>{{ object.ssid }}</td>
|
<td>{{ object.ssid }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Group</td>
|
<td>Group</td>
|
||||||
<td>{{ object.group|linkify|placeholder }}</td>
|
<td>{{ object.group|linkify|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Description</th>
|
<th scope="row">Description</th>
|
||||||
<td>{{ object.description|placeholder }}</td>
|
<td>{{ object.description|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">VLAN</th>
|
<th scope="row">VLAN</th>
|
||||||
<td>{{ object.vlan|linkify|placeholder }}</td>
|
<td>{{ object.vlan|linkify|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
<tr>
|
||||||
</div>
|
<th scope="row">Tenant</th>
|
||||||
</div>
|
<td>
|
||||||
{% include 'inc/panels/tags.html' %}
|
{% if object.tenant.group %}
|
||||||
{% plugin_left_page object %}
|
{{ object.tenant.group|linkify }} /
|
||||||
|
{% endif %}
|
||||||
|
{{ object.tenant|linkify|placeholder }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col col-md-6">
|
{% include 'inc/panels/tags.html' %}
|
||||||
{% include 'wireless/inc/authentication_attrs.html' %}
|
{% plugin_left_page object %}
|
||||||
{% include 'inc/panels/custom_fields.html' %}
|
</div>
|
||||||
{% plugin_right_page object %}
|
<div class="col col-md-6">
|
||||||
|
{% include 'wireless/inc/authentication_attrs.html' %}
|
||||||
|
{% include 'inc/panels/custom_fields.html' %}
|
||||||
|
{% plugin_right_page object %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -23,6 +23,15 @@
|
|||||||
<th scope="row">SSID</th>
|
<th scope="row">SSID</th>
|
||||||
<td>{{ object.ssid|placeholder }}</td>
|
<td>{{ object.ssid|placeholder }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Tenant</th>
|
||||||
|
<td>
|
||||||
|
{% if object.tenant.group %}
|
||||||
|
{{ object.tenant.group|linkify }} /
|
||||||
|
{% endif %}
|
||||||
|
{{ object.tenant|linkify|placeholder }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Description</th>
|
<th scope="row">Description</th>
|
||||||
<td>{{ object.description|placeholder }}</td>
|
<td>{{ object.description|placeholder }}</td>
|
||||||
|
@ -7,6 +7,7 @@ from ipam.models import Aggregate, IPAddress, IPRange, Prefix, VLAN, VRF, ASN
|
|||||||
from netbox.views import generic
|
from netbox.views import generic
|
||||||
from utilities.utils import count_related
|
from utilities.utils import count_related
|
||||||
from virtualization.models import VirtualMachine, Cluster
|
from virtualization.models import VirtualMachine, Cluster
|
||||||
|
from wireless.models import WirelessLAN, WirelessLink
|
||||||
from . import filtersets, forms, tables
|
from . import filtersets, forms, tables
|
||||||
from .models import *
|
from .models import *
|
||||||
|
|
||||||
@ -114,6 +115,8 @@ class TenantView(generic.ObjectView):
|
|||||||
'cluster_count': Cluster.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
|
'cluster_count': Cluster.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
|
||||||
'cable_count': Cable.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
|
'cable_count': Cable.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
|
||||||
'asn_count': ASN.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
|
'asn_count': ASN.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
|
||||||
|
'wirelesslan_count': WirelessLAN.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
|
||||||
|
'wirelesslink_count': WirelessLink.objects.restrict(request.user, 'view').filter(tenant=instance).count(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -5,6 +5,7 @@ from dcim.api.serializers import NestedInterfaceSerializer
|
|||||||
from ipam.api.serializers import NestedVLANSerializer
|
from ipam.api.serializers import NestedVLANSerializer
|
||||||
from netbox.api import ChoiceField
|
from netbox.api import ChoiceField
|
||||||
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
|
from netbox.api.serializers import NestedGroupModelSerializer, NetBoxModelSerializer
|
||||||
|
from tenancy.api.nested_serializers import NestedTenantSerializer
|
||||||
from wireless.choices import *
|
from wireless.choices import *
|
||||||
from wireless.models import *
|
from wireless.models import *
|
||||||
from .nested_serializers import *
|
from .nested_serializers import *
|
||||||
@ -33,14 +34,15 @@ class WirelessLANSerializer(NetBoxModelSerializer):
|
|||||||
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslan-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslan-detail')
|
||||||
group = NestedWirelessLANGroupSerializer(required=False, allow_null=True)
|
group = NestedWirelessLANGroupSerializer(required=False, allow_null=True)
|
||||||
vlan = NestedVLANSerializer(required=False, allow_null=True)
|
vlan = NestedVLANSerializer(required=False, allow_null=True)
|
||||||
|
tenant = NestedTenantSerializer(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)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = WirelessLAN
|
model = WirelessLAN
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'ssid', 'description', 'group', 'vlan', 'auth_type', 'auth_cipher', 'auth_psk',
|
'id', 'url', 'display', 'ssid', 'description', 'group', 'vlan', 'tenant', 'auth_type', 'auth_cipher',
|
||||||
'description', 'tags', 'custom_fields', 'created', 'last_updated',
|
'auth_psk', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -49,12 +51,13 @@ class WirelessLinkSerializer(NetBoxModelSerializer):
|
|||||||
status = ChoiceField(choices=LinkStatusChoices, required=False)
|
status = ChoiceField(choices=LinkStatusChoices, required=False)
|
||||||
interface_a = NestedInterfaceSerializer()
|
interface_a = NestedInterfaceSerializer()
|
||||||
interface_b = NestedInterfaceSerializer()
|
interface_b = NestedInterfaceSerializer()
|
||||||
|
tenant = NestedTenantSerializer(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)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = WirelessLink
|
model = WirelessLink
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display', 'interface_a', 'interface_b', 'ssid', 'status', 'description', 'auth_type',
|
'id', 'url', 'display', 'interface_a', 'interface_b', 'ssid', 'status', 'tenant', 'auth_type',
|
||||||
'auth_cipher', 'auth_psk', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
|
'auth_cipher', 'auth_psk', 'description', 'tags', 'custom_fields', 'created', 'last_updated',
|
||||||
]
|
]
|
||||||
|
@ -27,12 +27,12 @@ class WirelessLANGroupViewSet(NetBoxModelViewSet):
|
|||||||
|
|
||||||
|
|
||||||
class WirelessLANViewSet(NetBoxModelViewSet):
|
class WirelessLANViewSet(NetBoxModelViewSet):
|
||||||
queryset = WirelessLAN.objects.prefetch_related('vlan', 'tags')
|
queryset = WirelessLAN.objects.prefetch_related('vlan', 'tenant', 'tags')
|
||||||
serializer_class = serializers.WirelessLANSerializer
|
serializer_class = serializers.WirelessLANSerializer
|
||||||
filterset_class = filtersets.WirelessLANFilterSet
|
filterset_class = filtersets.WirelessLANFilterSet
|
||||||
|
|
||||||
|
|
||||||
class WirelessLinkViewSet(NetBoxModelViewSet):
|
class WirelessLinkViewSet(NetBoxModelViewSet):
|
||||||
queryset = WirelessLink.objects.prefetch_related('interface_a', 'interface_b', 'tags')
|
queryset = WirelessLink.objects.prefetch_related('interface_a', 'interface_b', 'tenant', 'tags')
|
||||||
serializer_class = serializers.WirelessLinkSerializer
|
serializer_class = serializers.WirelessLinkSerializer
|
||||||
filterset_class = filtersets.WirelessLinkFilterSet
|
filterset_class = filtersets.WirelessLinkFilterSet
|
||||||
|
@ -4,6 +4,7 @@ from django.db.models import Q
|
|||||||
from dcim.choices import LinkStatusChoices
|
from dcim.choices import LinkStatusChoices
|
||||||
from ipam.models import VLAN
|
from ipam.models import VLAN
|
||||||
from netbox.filtersets import OrganizationalModelFilterSet, NetBoxModelFilterSet
|
from netbox.filtersets import OrganizationalModelFilterSet, NetBoxModelFilterSet
|
||||||
|
from tenancy.filtersets import TenancyFilterSet
|
||||||
from utilities.filters import MultiValueNumberFilter, TreeNodeMultipleChoiceFilter
|
from utilities.filters import MultiValueNumberFilter, TreeNodeMultipleChoiceFilter
|
||||||
from .choices import *
|
from .choices import *
|
||||||
from .models import *
|
from .models import *
|
||||||
@ -30,7 +31,7 @@ class WirelessLANGroupFilterSet(OrganizationalModelFilterSet):
|
|||||||
fields = ['id', 'name', 'slug', 'description']
|
fields = ['id', 'name', 'slug', 'description']
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANFilterSet(NetBoxModelFilterSet):
|
class WirelessLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
|
||||||
group_id = TreeNodeMultipleChoiceFilter(
|
group_id = TreeNodeMultipleChoiceFilter(
|
||||||
queryset=WirelessLANGroup.objects.all(),
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
field_name='group',
|
field_name='group',
|
||||||
@ -66,7 +67,7 @@ class WirelessLANFilterSet(NetBoxModelFilterSet):
|
|||||||
return queryset.filter(qs_filter)
|
return queryset.filter(qs_filter)
|
||||||
|
|
||||||
|
|
||||||
class WirelessLinkFilterSet(NetBoxModelFilterSet):
|
class WirelessLinkFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
|
||||||
interface_a_id = MultiValueNumberFilter()
|
interface_a_id = MultiValueNumberFilter()
|
||||||
interface_b_id = MultiValueNumberFilter()
|
interface_b_id = MultiValueNumberFilter()
|
||||||
status = django_filters.MultipleChoiceFilter(
|
status = django_filters.MultipleChoiceFilter(
|
||||||
|
@ -3,6 +3,7 @@ from django import forms
|
|||||||
from dcim.choices import LinkStatusChoices
|
from dcim.choices import LinkStatusChoices
|
||||||
from ipam.models import VLAN
|
from ipam.models import VLAN
|
||||||
from netbox.forms import NetBoxModelBulkEditForm
|
from netbox.forms import NetBoxModelBulkEditForm
|
||||||
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import add_blank_choice, DynamicModelChoiceField
|
from utilities.forms import add_blank_choice, DynamicModelChoiceField
|
||||||
from wireless.choices import *
|
from wireless.choices import *
|
||||||
from wireless.constants import SSID_MAX_LENGTH
|
from wireless.constants import SSID_MAX_LENGTH
|
||||||
@ -47,6 +48,10 @@ class WirelessLANBulkEditForm(NetBoxModelBulkEditForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label='SSID'
|
label='SSID'
|
||||||
)
|
)
|
||||||
|
tenant = DynamicModelChoiceField(
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
description = forms.CharField(
|
description = forms.CharField(
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
@ -65,11 +70,11 @@ class WirelessLANBulkEditForm(NetBoxModelBulkEditForm):
|
|||||||
|
|
||||||
model = WirelessLAN
|
model = WirelessLAN
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('group', 'vlan', 'ssid', 'description')),
|
(None, ('group', 'ssid', 'vlan', 'tenant', 'description')),
|
||||||
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
|
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
|
||||||
)
|
)
|
||||||
nullable_fields = (
|
nullable_fields = (
|
||||||
'ssid', 'group', 'vlan', 'description', 'auth_type', 'auth_cipher', 'auth_psk',
|
'ssid', 'group', 'vlan', 'tenant', 'description', 'auth_type', 'auth_cipher', 'auth_psk',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -83,6 +88,10 @@ class WirelessLinkBulkEditForm(NetBoxModelBulkEditForm):
|
|||||||
choices=add_blank_choice(LinkStatusChoices),
|
choices=add_blank_choice(LinkStatusChoices),
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
tenant = DynamicModelChoiceField(
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
description = forms.CharField(
|
description = forms.CharField(
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
@ -101,9 +110,9 @@ class WirelessLinkBulkEditForm(NetBoxModelBulkEditForm):
|
|||||||
|
|
||||||
model = WirelessLink
|
model = WirelessLink
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('ssid', 'status', 'description')),
|
(None, ('ssid', 'status', 'tenant', 'description')),
|
||||||
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk'))
|
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk'))
|
||||||
)
|
)
|
||||||
nullable_fields = (
|
nullable_fields = (
|
||||||
'ssid', 'description', 'auth_type', 'auth_cipher', 'auth_psk',
|
'ssid', 'tenant', 'description', 'auth_type', 'auth_cipher', 'auth_psk',
|
||||||
)
|
)
|
||||||
|
@ -2,6 +2,7 @@ from dcim.choices import LinkStatusChoices
|
|||||||
from dcim.models import Interface
|
from dcim.models import Interface
|
||||||
from ipam.models import VLAN
|
from ipam.models import VLAN
|
||||||
from netbox.forms import NetBoxModelCSVForm
|
from netbox.forms import NetBoxModelCSVForm
|
||||||
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import CSVChoiceField, CSVModelChoiceField, SlugField
|
from utilities.forms import CSVChoiceField, CSVModelChoiceField, SlugField
|
||||||
from wireless.choices import *
|
from wireless.choices import *
|
||||||
from wireless.models import *
|
from wireless.models import *
|
||||||
@ -40,6 +41,12 @@ class WirelessLANCSVForm(NetBoxModelCSVForm):
|
|||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
help_text='Bridged VLAN'
|
help_text='Bridged VLAN'
|
||||||
)
|
)
|
||||||
|
tenant = CSVModelChoiceField(
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
required=False,
|
||||||
|
to_field_name='name',
|
||||||
|
help_text='Assigned tenant'
|
||||||
|
)
|
||||||
auth_type = CSVChoiceField(
|
auth_type = CSVChoiceField(
|
||||||
choices=WirelessAuthTypeChoices,
|
choices=WirelessAuthTypeChoices,
|
||||||
required=False,
|
required=False,
|
||||||
@ -53,7 +60,7 @@ class WirelessLANCSVForm(NetBoxModelCSVForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = WirelessLAN
|
model = WirelessLAN
|
||||||
fields = ('ssid', 'group', 'description', 'vlan', 'auth_type', 'auth_cipher', 'auth_psk')
|
fields = ('ssid', 'group', 'vlan', 'tenant', 'description', 'auth_type', 'auth_cipher', 'auth_psk')
|
||||||
|
|
||||||
|
|
||||||
class WirelessLinkCSVForm(NetBoxModelCSVForm):
|
class WirelessLinkCSVForm(NetBoxModelCSVForm):
|
||||||
@ -67,6 +74,12 @@ class WirelessLinkCSVForm(NetBoxModelCSVForm):
|
|||||||
interface_b = CSVModelChoiceField(
|
interface_b = CSVModelChoiceField(
|
||||||
queryset=Interface.objects.all()
|
queryset=Interface.objects.all()
|
||||||
)
|
)
|
||||||
|
tenant = CSVModelChoiceField(
|
||||||
|
queryset=Tenant.objects.all(),
|
||||||
|
required=False,
|
||||||
|
to_field_name='name',
|
||||||
|
help_text='Assigned tenant'
|
||||||
|
)
|
||||||
auth_type = CSVChoiceField(
|
auth_type = CSVChoiceField(
|
||||||
choices=WirelessAuthTypeChoices,
|
choices=WirelessAuthTypeChoices,
|
||||||
required=False,
|
required=False,
|
||||||
@ -80,4 +93,6 @@ class WirelessLinkCSVForm(NetBoxModelCSVForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = WirelessLink
|
model = WirelessLink
|
||||||
fields = ('interface_a', 'interface_b', 'ssid', 'description', 'auth_type', 'auth_cipher', 'auth_psk')
|
fields = (
|
||||||
|
'interface_a', 'interface_b', 'ssid', 'tenant', 'description', 'auth_type', 'auth_cipher', 'auth_psk',
|
||||||
|
)
|
||||||
|
@ -3,6 +3,7 @@ from django.utils.translation import gettext as _
|
|||||||
|
|
||||||
from dcim.choices import LinkStatusChoices
|
from dcim.choices import LinkStatusChoices
|
||||||
from netbox.forms import NetBoxModelFilterSetForm
|
from netbox.forms import NetBoxModelFilterSetForm
|
||||||
|
from tenancy.forms import TenancyFilterForm
|
||||||
from utilities.forms import add_blank_choice, DynamicModelMultipleChoiceField, StaticSelect, TagFilterField
|
from utilities.forms import add_blank_choice, DynamicModelMultipleChoiceField, StaticSelect, TagFilterField
|
||||||
from wireless.choices import *
|
from wireless.choices import *
|
||||||
from wireless.models import *
|
from wireless.models import *
|
||||||
@ -24,11 +25,12 @@ class WirelessLANGroupFilterForm(NetBoxModelFilterSetForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANFilterForm(NetBoxModelFilterSetForm):
|
class WirelessLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = WirelessLAN
|
model = WirelessLAN
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, ('q', 'tag')),
|
(None, ('q', 'tag')),
|
||||||
('Attributes', ('ssid', 'group_id',)),
|
('Attributes', ('ssid', 'group_id',)),
|
||||||
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
|
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
|
||||||
)
|
)
|
||||||
ssid = forms.CharField(
|
ssid = forms.CharField(
|
||||||
@ -57,8 +59,14 @@ class WirelessLANFilterForm(NetBoxModelFilterSetForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class WirelessLinkFilterForm(NetBoxModelFilterSetForm):
|
class WirelessLinkFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = WirelessLink
|
model = WirelessLink
|
||||||
|
fieldsets = (
|
||||||
|
(None, ('q', 'tag')),
|
||||||
|
('Attributes', ('ssid', 'status',)),
|
||||||
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
|
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
|
||||||
|
)
|
||||||
ssid = forms.CharField(
|
ssid = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='SSID'
|
label='SSID'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from dcim.models import Device, Interface, Location, Region, Site, SiteGroup
|
from dcim.models import Device, Interface, Location, Region, Site, SiteGroup
|
||||||
from ipam.models import VLAN, VLANGroup
|
from ipam.models import VLAN, VLANGroup
|
||||||
from netbox.forms import NetBoxModelForm
|
from netbox.forms import NetBoxModelForm
|
||||||
|
from tenancy.forms import TenancyForm
|
||||||
from utilities.forms import DynamicModelChoiceField, SlugField, StaticSelect
|
from utilities.forms import DynamicModelChoiceField, SlugField, StaticSelect
|
||||||
from wireless.models import *
|
from wireless.models import *
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ class WirelessLANGroupForm(NetBoxModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANForm(NetBoxModelForm):
|
class WirelessLANForm(TenancyForm, NetBoxModelForm):
|
||||||
group = DynamicModelChoiceField(
|
group = DynamicModelChoiceField(
|
||||||
queryset=WirelessLANGroup.objects.all(),
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -79,14 +80,15 @@ class WirelessLANForm(NetBoxModelForm):
|
|||||||
fieldsets = (
|
fieldsets = (
|
||||||
('Wireless LAN', ('ssid', 'group', 'description', 'tags')),
|
('Wireless LAN', ('ssid', 'group', 'description', 'tags')),
|
||||||
('VLAN', ('region', 'site_group', 'site', 'vlan_group', 'vlan',)),
|
('VLAN', ('region', 'site_group', 'site', 'vlan_group', 'vlan',)),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
|
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = WirelessLAN
|
model = WirelessLAN
|
||||||
fields = [
|
fields = [
|
||||||
'ssid', 'group', 'description', 'region', 'site_group', 'site', 'vlan_group', 'vlan', 'auth_type',
|
'ssid', 'group', 'description', 'region', 'site_group', 'site', 'vlan_group', 'vlan', 'tenant_group',
|
||||||
'auth_cipher', 'auth_psk', 'tags',
|
'tenant', 'auth_type', 'auth_cipher', 'auth_psk', 'tags',
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'auth_type': StaticSelect,
|
'auth_type': StaticSelect,
|
||||||
@ -94,7 +96,7 @@ class WirelessLANForm(NetBoxModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class WirelessLinkForm(NetBoxModelForm):
|
class WirelessLinkForm(TenancyForm, NetBoxModelForm):
|
||||||
site_a = DynamicModelChoiceField(
|
site_a = DynamicModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -180,6 +182,7 @@ class WirelessLinkForm(NetBoxModelForm):
|
|||||||
('Side A', ('site_a', 'location_a', 'device_a', 'interface_a')),
|
('Side A', ('site_a', 'location_a', 'device_a', 'interface_a')),
|
||||||
('Side B', ('site_b', 'location_b', 'device_b', 'interface_b')),
|
('Side B', ('site_b', 'location_b', 'device_b', 'interface_b')),
|
||||||
('Link', ('status', 'ssid', 'description', 'tags')),
|
('Link', ('status', 'ssid', 'description', 'tags')),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
|
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -187,7 +190,7 @@ class WirelessLinkForm(NetBoxModelForm):
|
|||||||
model = WirelessLink
|
model = WirelessLink
|
||||||
fields = [
|
fields = [
|
||||||
'site_a', 'location_a', 'device_a', 'interface_a', 'site_b', 'location_b', 'device_b', 'interface_b',
|
'site_a', 'location_a', 'device_a', 'interface_a', 'site_b', 'location_b', 'device_b', 'interface_b',
|
||||||
'status', 'ssid', 'description', 'auth_type', 'auth_cipher', 'auth_psk', 'tags',
|
'status', 'ssid', 'tenant_group', 'tenant', 'description', 'auth_type', 'auth_cipher', 'auth_psk', 'tags',
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
'status': StaticSelect,
|
'status': StaticSelect,
|
||||||
|
25
netbox/wireless/migrations/0004_wireless_tenancy.py
Normal file
25
netbox/wireless/migrations/0004_wireless_tenancy.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Generated by Django 4.0.5 on 2022-06-27 13:44
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('tenancy', '0007_contact_link'),
|
||||||
|
('wireless', '0003_created_datetimefield'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='wirelesslan',
|
||||||
|
name='tenant',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='wireless_lans', to='tenancy.tenant'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='wirelesslink',
|
||||||
|
name='tenant',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='wireless_links', to='tenancy.tenant'),
|
||||||
|
),
|
||||||
|
]
|
@ -101,6 +101,13 @@ class WirelessLAN(WirelessAuthenticationBase, NetBoxModel):
|
|||||||
null=True,
|
null=True,
|
||||||
verbose_name='VLAN'
|
verbose_name='VLAN'
|
||||||
)
|
)
|
||||||
|
tenant = models.ForeignKey(
|
||||||
|
to='tenancy.Tenant',
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='wireless_lans',
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
blank=True
|
blank=True
|
||||||
@ -143,6 +150,13 @@ class WirelessLink(WirelessAuthenticationBase, NetBoxModel):
|
|||||||
choices=LinkStatusChoices,
|
choices=LinkStatusChoices,
|
||||||
default=LinkStatusChoices.STATUS_CONNECTED
|
default=LinkStatusChoices.STATUS_CONNECTED
|
||||||
)
|
)
|
||||||
|
tenant = models.ForeignKey(
|
||||||
|
to='tenancy.Tenant',
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name='wireless_links',
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
description = models.CharField(
|
description = models.CharField(
|
||||||
max_length=200,
|
max_length=200,
|
||||||
blank=True
|
blank=True
|
||||||
|
@ -2,6 +2,7 @@ import django_tables2 as tables
|
|||||||
|
|
||||||
from dcim.models import Interface
|
from dcim.models import Interface
|
||||||
from netbox.tables import NetBoxTable, columns
|
from netbox.tables import NetBoxTable, columns
|
||||||
|
from tenancy.tables import TenantColumn
|
||||||
from wireless.models import *
|
from wireless.models import *
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -39,6 +40,7 @@ class WirelessLANTable(NetBoxTable):
|
|||||||
group = tables.Column(
|
group = tables.Column(
|
||||||
linkify=True
|
linkify=True
|
||||||
)
|
)
|
||||||
|
tenant = TenantColumn()
|
||||||
interface_count = tables.Column(
|
interface_count = tables.Column(
|
||||||
verbose_name='Interfaces'
|
verbose_name='Interfaces'
|
||||||
)
|
)
|
||||||
@ -49,8 +51,8 @@ class WirelessLANTable(NetBoxTable):
|
|||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = WirelessLAN
|
model = WirelessLAN
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'ssid', 'group', 'description', 'vlan', 'interface_count', 'auth_type', 'auth_cipher', 'auth_psk',
|
'pk', 'ssid', 'group', 'tenant', 'description', 'vlan', 'interface_count', 'auth_type', 'auth_cipher',
|
||||||
'tags', 'created', 'last_updated',
|
'auth_psk', 'tags', 'created', 'last_updated',
|
||||||
)
|
)
|
||||||
default_columns = ('pk', 'ssid', 'group', 'description', 'vlan', 'auth_type', 'interface_count')
|
default_columns = ('pk', 'ssid', 'group', 'description', 'vlan', 'auth_type', 'interface_count')
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
|
|
||||||
from netbox.tables import NetBoxTable, columns
|
from netbox.tables import NetBoxTable, columns
|
||||||
|
from tenancy.tables import TenantColumn
|
||||||
from wireless.models import *
|
from wireless.models import *
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -28,6 +29,7 @@ class WirelessLinkTable(NetBoxTable):
|
|||||||
interface_b = tables.Column(
|
interface_b = tables.Column(
|
||||||
linkify=True
|
linkify=True
|
||||||
)
|
)
|
||||||
|
tenant = TenantColumn()
|
||||||
tags = columns.TagColumn(
|
tags = columns.TagColumn(
|
||||||
url_name='wireless:wirelesslink_list'
|
url_name='wireless:wirelesslink_list'
|
||||||
)
|
)
|
||||||
@ -35,7 +37,7 @@ class WirelessLinkTable(NetBoxTable):
|
|||||||
class Meta(NetBoxTable.Meta):
|
class Meta(NetBoxTable.Meta):
|
||||||
model = WirelessLink
|
model = WirelessLink
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'status', 'device_a', 'interface_a', 'device_b', 'interface_b', 'ssid', 'description',
|
'pk', 'id', 'status', 'device_a', 'interface_a', 'device_b', 'interface_b', 'ssid', 'tenant', 'description',
|
||||||
'auth_type', 'auth_cipher', 'auth_psk', 'tags', 'created', 'last_updated',
|
'auth_type', 'auth_cipher', 'auth_psk', 'tags', 'created', 'last_updated',
|
||||||
)
|
)
|
||||||
default_columns = (
|
default_columns = (
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from wireless.choices import *
|
|
||||||
from wireless.models import *
|
|
||||||
from dcim.choices import InterfaceTypeChoices
|
from dcim.choices import InterfaceTypeChoices
|
||||||
from dcim.models import Interface
|
from dcim.models import Interface
|
||||||
|
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.models import *
|
||||||
|
|
||||||
|
|
||||||
class AppTest(APITestCase):
|
class AppTest(APITestCase):
|
||||||
@ -52,6 +53,12 @@ class WirelessLANTest(APIViewTestCases.APIViewTestCase):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
|
tenants = (
|
||||||
|
Tenant(name='Tenant 1', slug='tenant-1'),
|
||||||
|
Tenant(name='Tenant 2', slug='tenant-2'),
|
||||||
|
)
|
||||||
|
Tenant.objects.bulk_create(tenants)
|
||||||
|
|
||||||
groups = (
|
groups = (
|
||||||
WirelessLANGroup(name='Group 1', slug='group-1'),
|
WirelessLANGroup(name='Group 1', slug='group-1'),
|
||||||
WirelessLANGroup(name='Group 2', slug='group-2'),
|
WirelessLANGroup(name='Group 2', slug='group-2'),
|
||||||
@ -71,21 +78,25 @@ class WirelessLANTest(APIViewTestCases.APIViewTestCase):
|
|||||||
{
|
{
|
||||||
'ssid': 'WLAN4',
|
'ssid': 'WLAN4',
|
||||||
'group': groups[0].pk,
|
'group': groups[0].pk,
|
||||||
|
'tenant': tenants[0].pk,
|
||||||
'auth_type': WirelessAuthTypeChoices.TYPE_OPEN,
|
'auth_type': WirelessAuthTypeChoices.TYPE_OPEN,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'ssid': 'WLAN5',
|
'ssid': 'WLAN5',
|
||||||
'group': groups[1].pk,
|
'group': groups[1].pk,
|
||||||
|
'tenant': tenants[0].pk,
|
||||||
'auth_type': WirelessAuthTypeChoices.TYPE_WPA_PERSONAL,
|
'auth_type': WirelessAuthTypeChoices.TYPE_WPA_PERSONAL,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'ssid': 'WLAN6',
|
'ssid': 'WLAN6',
|
||||||
|
'tenant': tenants[0].pk,
|
||||||
'auth_type': WirelessAuthTypeChoices.TYPE_WPA_ENTERPRISE,
|
'auth_type': WirelessAuthTypeChoices.TYPE_WPA_ENTERPRISE,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
cls.bulk_update_data = {
|
cls.bulk_update_data = {
|
||||||
'group': groups[2].pk,
|
'group': groups[2].pk,
|
||||||
|
'tenant': tenants[1].pk,
|
||||||
'description': 'New description',
|
'description': 'New description',
|
||||||
'auth_type': WirelessAuthTypeChoices.TYPE_WPA_PERSONAL,
|
'auth_type': WirelessAuthTypeChoices.TYPE_WPA_PERSONAL,
|
||||||
'auth_cipher': WirelessAuthCipherChoices.CIPHER_AES,
|
'auth_cipher': WirelessAuthCipherChoices.CIPHER_AES,
|
||||||
@ -115,10 +126,16 @@ class WirelessLinkTest(APIViewTestCases.APIViewTestCase):
|
|||||||
]
|
]
|
||||||
Interface.objects.bulk_create(interfaces)
|
Interface.objects.bulk_create(interfaces)
|
||||||
|
|
||||||
|
tenants = (
|
||||||
|
Tenant(name='Tenant 1', slug='tenant-1'),
|
||||||
|
Tenant(name='Tenant 2', slug='tenant-2'),
|
||||||
|
)
|
||||||
|
Tenant.objects.bulk_create(tenants)
|
||||||
|
|
||||||
wireless_links = (
|
wireless_links = (
|
||||||
WirelessLink(ssid='LINK1', interface_a=interfaces[0], interface_b=interfaces[1]),
|
WirelessLink(ssid='LINK1', interface_a=interfaces[0], interface_b=interfaces[1], tenant=tenants[0]),
|
||||||
WirelessLink(ssid='LINK2', interface_a=interfaces[2], interface_b=interfaces[3]),
|
WirelessLink(ssid='LINK2', interface_a=interfaces[2], interface_b=interfaces[3], tenant=tenants[0]),
|
||||||
WirelessLink(ssid='LINK3', interface_a=interfaces[4], interface_b=interfaces[5]),
|
WirelessLink(ssid='LINK3', interface_a=interfaces[4], interface_b=interfaces[5], tenant=tenants[0]),
|
||||||
)
|
)
|
||||||
WirelessLink.objects.bulk_create(wireless_links)
|
WirelessLink.objects.bulk_create(wireless_links)
|
||||||
|
|
||||||
@ -127,15 +144,18 @@ class WirelessLinkTest(APIViewTestCases.APIViewTestCase):
|
|||||||
'interface_a': interfaces[6].pk,
|
'interface_a': interfaces[6].pk,
|
||||||
'interface_b': interfaces[7].pk,
|
'interface_b': interfaces[7].pk,
|
||||||
'ssid': 'LINK4',
|
'ssid': 'LINK4',
|
||||||
|
'tenant': tenants[1].pk,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'interface_a': interfaces[8].pk,
|
'interface_a': interfaces[8].pk,
|
||||||
'interface_b': interfaces[9].pk,
|
'interface_b': interfaces[9].pk,
|
||||||
'ssid': 'LINK5',
|
'ssid': 'LINK5',
|
||||||
|
'tenant': tenants[1].pk,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'interface_a': interfaces[10].pk,
|
'interface_a': interfaces[10].pk,
|
||||||
'interface_b': interfaces[11].pk,
|
'interface_b': interfaces[11].pk,
|
||||||
'ssid': 'LINK6',
|
'ssid': 'LINK6',
|
||||||
|
'tenant': tenants[1].pk,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -3,6 +3,7 @@ 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
|
||||||
from ipam.models import VLAN
|
from ipam.models import VLAN
|
||||||
|
from tenancy.models import Tenant
|
||||||
from wireless.choices import *
|
from wireless.choices import *
|
||||||
from wireless.filtersets import *
|
from wireless.filtersets import *
|
||||||
from wireless.models import *
|
from wireless.models import *
|
||||||
@ -43,10 +44,6 @@ class WirelessLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
params = {'slug': ['wireless-lan-group-1', 'wireless-lan-group-2']}
|
params = {'slug': ['wireless-lan-group-1', 'wireless-lan-group-2']}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
def test_description(self):
|
|
||||||
params = {'description': ['A', 'B']}
|
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
|
||||||
|
|
||||||
def test_parent(self):
|
def test_parent(self):
|
||||||
parent_groups = WirelessLANGroup.objects.filter(parent__isnull=True)[:2]
|
parent_groups = WirelessLANGroup.objects.filter(parent__isnull=True)[:2]
|
||||||
params = {'parent_id': [parent_groups[0].pk, parent_groups[1].pk]}
|
params = {'parent_id': [parent_groups[0].pk, parent_groups[1].pk]}
|
||||||
@ -81,10 +78,17 @@ class WirelessLANTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
)
|
)
|
||||||
VLAN.objects.bulk_create(vlans)
|
VLAN.objects.bulk_create(vlans)
|
||||||
|
|
||||||
|
tenants = (
|
||||||
|
Tenant(name='Tenant 1', slug='tenant-1'),
|
||||||
|
Tenant(name='Tenant 2', slug='tenant-2'),
|
||||||
|
Tenant(name='Tenant 3', slug='tenant-3'),
|
||||||
|
)
|
||||||
|
Tenant.objects.bulk_create(tenants)
|
||||||
|
|
||||||
wireless_lans = (
|
wireless_lans = (
|
||||||
WirelessLAN(ssid='WLAN1', group=groups[0], vlan=vlans[0], auth_type=WirelessAuthTypeChoices.TYPE_OPEN, auth_cipher=WirelessAuthCipherChoices.CIPHER_AUTO, auth_psk='PSK1'),
|
WirelessLAN(ssid='WLAN1', group=groups[0], vlan=vlans[0], tenant=tenants[0], auth_type=WirelessAuthTypeChoices.TYPE_OPEN, auth_cipher=WirelessAuthCipherChoices.CIPHER_AUTO, auth_psk='PSK1'),
|
||||||
WirelessLAN(ssid='WLAN2', group=groups[1], vlan=vlans[1], auth_type=WirelessAuthTypeChoices.TYPE_WEP, auth_cipher=WirelessAuthCipherChoices.CIPHER_TKIP, auth_psk='PSK2'),
|
WirelessLAN(ssid='WLAN2', group=groups[1], vlan=vlans[1], tenant=tenants[1], auth_type=WirelessAuthTypeChoices.TYPE_WEP, auth_cipher=WirelessAuthCipherChoices.CIPHER_TKIP, auth_psk='PSK2'),
|
||||||
WirelessLAN(ssid='WLAN3', group=groups[2], vlan=vlans[2], auth_type=WirelessAuthTypeChoices.TYPE_WPA_PERSONAL, auth_cipher=WirelessAuthCipherChoices.CIPHER_AES, auth_psk='PSK3'),
|
WirelessLAN(ssid='WLAN3', group=groups[2], vlan=vlans[2], tenant=tenants[2], auth_type=WirelessAuthTypeChoices.TYPE_WPA_PERSONAL, auth_cipher=WirelessAuthCipherChoices.CIPHER_AES, auth_psk='PSK3'),
|
||||||
)
|
)
|
||||||
WirelessLAN.objects.bulk_create(wireless_lans)
|
WirelessLAN.objects.bulk_create(wireless_lans)
|
||||||
|
|
||||||
@ -116,6 +120,13 @@ class WirelessLANTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
params = {'auth_psk': ['PSK1', 'PSK2']}
|
params = {'auth_psk': ['PSK1', 'PSK2']}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_tenant(self):
|
||||||
|
tenants = Tenant.objects.all()[:2]
|
||||||
|
params = {'tenant_id': [tenants[0].pk, tenants[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'tenant': [tenants[0].slug, tenants[1].slug]}
|
||||||
|
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()
|
||||||
@ -124,6 +135,13 @@ class WirelessLinkTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
|
tenants = (
|
||||||
|
Tenant(name='Tenant 1', slug='tenant-1'),
|
||||||
|
Tenant(name='Tenant 2', slug='tenant-2'),
|
||||||
|
Tenant(name='Tenant 3', slug='tenant-3'),
|
||||||
|
)
|
||||||
|
Tenant.objects.bulk_create(tenants)
|
||||||
|
|
||||||
devices = (
|
devices = (
|
||||||
create_test_device('device1'),
|
create_test_device('device1'),
|
||||||
create_test_device('device2'),
|
create_test_device('device2'),
|
||||||
@ -152,6 +170,7 @@ class WirelessLinkTestCase(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',
|
||||||
|
tenant=tenants[0],
|
||||||
description='foobar1'
|
description='foobar1'
|
||||||
).save()
|
).save()
|
||||||
WirelessLink(
|
WirelessLink(
|
||||||
@ -162,6 +181,7 @@ class WirelessLinkTestCase(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',
|
||||||
|
tenant=tenants[1],
|
||||||
description='foobar2'
|
description='foobar2'
|
||||||
).save()
|
).save()
|
||||||
WirelessLink(
|
WirelessLink(
|
||||||
@ -171,7 +191,8 @@ class WirelessLinkTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
status=LinkStatusChoices.STATUS_DECOMMISSIONING,
|
status=LinkStatusChoices.STATUS_DECOMMISSIONING,
|
||||||
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',
|
||||||
|
tenant=tenants[2],
|
||||||
).save()
|
).save()
|
||||||
WirelessLink(
|
WirelessLink(
|
||||||
interface_a=interfaces[5],
|
interface_a=interfaces[5],
|
||||||
@ -202,3 +223,10 @@ class WirelessLinkTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
def test_description(self):
|
def test_description(self):
|
||||||
params = {'description': ['foobar1', 'foobar2']}
|
params = {'description': ['foobar1', 'foobar2']}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
|
||||||
|
def test_tenant(self):
|
||||||
|
tenants = Tenant.objects.all()[:2]
|
||||||
|
params = {'tenant_id': [tenants[0].pk, tenants[1].pk]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
params = {'tenant': [tenants[0].slug, tenants[1].slug]}
|
||||||
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
@ -2,6 +2,7 @@ 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
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
@ -47,6 +48,13 @@ class WirelessLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
|
tenants = (
|
||||||
|
Tenant(name='Tenant 1', slug='tenant-1'),
|
||||||
|
Tenant(name='Tenant 2', slug='tenant-2'),
|
||||||
|
Tenant(name='Tenant 3', slug='tenant-3'),
|
||||||
|
)
|
||||||
|
Tenant.objects.bulk_create(tenants)
|
||||||
|
|
||||||
groups = (
|
groups = (
|
||||||
WirelessLANGroup(name='Wireless LAN Group 1', slug='wireless-lan-group-1'),
|
WirelessLANGroup(name='Wireless LAN Group 1', slug='wireless-lan-group-1'),
|
||||||
WirelessLANGroup(name='Wireless LAN Group 2', slug='wireless-lan-group-2'),
|
WirelessLANGroup(name='Wireless LAN Group 2', slug='wireless-lan-group-2'),
|
||||||
@ -55,9 +63,9 @@ class WirelessLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
group.save()
|
group.save()
|
||||||
|
|
||||||
WirelessLAN.objects.bulk_create([
|
WirelessLAN.objects.bulk_create([
|
||||||
WirelessLAN(group=groups[0], ssid='WLAN1'),
|
WirelessLAN(group=groups[0], ssid='WLAN1', tenant=tenants[0]),
|
||||||
WirelessLAN(group=groups[0], ssid='WLAN2'),
|
WirelessLAN(group=groups[0], ssid='WLAN2', tenant=tenants[0]),
|
||||||
WirelessLAN(group=groups[0], ssid='WLAN3'),
|
WirelessLAN(group=groups[0], ssid='WLAN3', tenant=tenants[0]),
|
||||||
])
|
])
|
||||||
|
|
||||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||||
@ -65,14 +73,15 @@ class WirelessLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
cls.form_data = {
|
cls.form_data = {
|
||||||
'ssid': 'WLAN2',
|
'ssid': 'WLAN2',
|
||||||
'group': groups[1].pk,
|
'group': groups[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",
|
f"group,ssid,tenant",
|
||||||
"Wireless LAN Group 2,WLAN4",
|
f"Wireless LAN Group 2,WLAN4,{tenants[0].name}",
|
||||||
"Wireless LAN Group 2,WLAN5",
|
f"Wireless LAN Group 2,WLAN5,{tenants[1].name}",
|
||||||
"Wireless LAN Group 2,WLAN6",
|
f"Wireless LAN Group 2,WLAN6,{tenants[2].name}",
|
||||||
)
|
)
|
||||||
|
|
||||||
cls.bulk_edit_data = {
|
cls.bulk_edit_data = {
|
||||||
@ -85,6 +94,14 @@ class WirelessLinkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
|
|
||||||
|
tenants = (
|
||||||
|
Tenant(name='Tenant 1', slug='tenant-1'),
|
||||||
|
Tenant(name='Tenant 2', slug='tenant-2'),
|
||||||
|
Tenant(name='Tenant 3', slug='tenant-3'),
|
||||||
|
)
|
||||||
|
Tenant.objects.bulk_create(tenants)
|
||||||
|
|
||||||
device = create_test_device('test-device')
|
device = create_test_device('test-device')
|
||||||
interfaces = [
|
interfaces = [
|
||||||
Interface(
|
Interface(
|
||||||
@ -98,9 +115,9 @@ class WirelessLinkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
]
|
]
|
||||||
Interface.objects.bulk_create(interfaces)
|
Interface.objects.bulk_create(interfaces)
|
||||||
|
|
||||||
WirelessLink(interface_a=interfaces[0], interface_b=interfaces[1], ssid='LINK1').save()
|
WirelessLink(interface_a=interfaces[0], interface_b=interfaces[1], ssid='LINK1', tenant=tenants[0]).save()
|
||||||
WirelessLink(interface_a=interfaces[2], interface_b=interfaces[3], ssid='LINK2').save()
|
WirelessLink(interface_a=interfaces[2], interface_b=interfaces[3], ssid='LINK2', tenant=tenants[0]).save()
|
||||||
WirelessLink(interface_a=interfaces[4], interface_b=interfaces[5], ssid='LINK3').save()
|
WirelessLink(interface_a=interfaces[4], interface_b=interfaces[5], ssid='LINK3', tenant=tenants[0]).save()
|
||||||
|
|
||||||
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
tags = create_tags('Alpha', 'Bravo', 'Charlie')
|
||||||
|
|
||||||
@ -108,14 +125,15 @@ class WirelessLinkTestCase(ViewTestCases.PrimaryObjectViewTestCase):
|
|||||||
'interface_a': interfaces[6].pk,
|
'interface_a': interfaces[6].pk,
|
||||||
'interface_b': interfaces[7].pk,
|
'interface_b': interfaces[7].pk,
|
||||||
'status': LinkStatusChoices.STATUS_PLANNED,
|
'status': LinkStatusChoices.STATUS_PLANNED,
|
||||||
|
'tenant': tenants[1].pk,
|
||||||
'tags': [t.pk for t in tags],
|
'tags': [t.pk for t in tags],
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
"interface_a,interface_b,status",
|
f"interface_a,interface_b,status,tenant",
|
||||||
f"{interfaces[6].pk},{interfaces[7].pk},connected",
|
f"{interfaces[6].pk},{interfaces[7].pk},connected,{tenants[0].name}",
|
||||||
f"{interfaces[8].pk},{interfaces[9].pk},connected",
|
f"{interfaces[8].pk},{interfaces[9].pk},connected,{tenants[1].name}",
|
||||||
f"{interfaces[10].pk},{interfaces[11].pk},connected",
|
f"{interfaces[10].pk},{interfaces[11].pk},connected,{tenants[2].name}",
|
||||||
)
|
)
|
||||||
|
|
||||||
cls.bulk_edit_data = {
|
cls.bulk_edit_data = {
|
||||||
|
Reference in New Issue
Block a user