mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 12:06:53 -06:00
Add WirelessLANGroup model
This commit is contained in:
parent
43f2d4a331
commit
01f791a44e
@ -168,6 +168,7 @@ CONNECTIONS_MENU = Menu(
|
|||||||
label='Connections',
|
label='Connections',
|
||||||
items=(
|
items=(
|
||||||
get_model_item('dcim', 'cable', 'Cables', actions=['import']),
|
get_model_item('dcim', 'cable', 'Cables', actions=['import']),
|
||||||
|
get_model_item('wireless', 'wirelesslink', 'Wirelesss Links', actions=['import']),
|
||||||
MenuItem(
|
MenuItem(
|
||||||
link='dcim:interface_connections_list',
|
link='dcim:interface_connections_list',
|
||||||
link_text='Interface Connections',
|
link_text='Interface Connections',
|
||||||
@ -196,7 +197,7 @@ WIRELESS_MENU = Menu(
|
|||||||
label='Wireless',
|
label='Wireless',
|
||||||
items=(
|
items=(
|
||||||
get_model_item('wireless', 'wirelesslan', 'Wireless LANs'),
|
get_model_item('wireless', 'wirelesslan', 'Wireless LANs'),
|
||||||
get_model_item('wireless', 'wirelesslink', 'Wirelesss Links', actions=['import']),
|
get_model_item('wireless', 'wirelesslangroup', 'Wireless LAN Groups'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -13,6 +13,16 @@
|
|||||||
<th scope="row">SSID</th>
|
<th scope="row">SSID</th>
|
||||||
<td>{{ object.ssid }}</td>
|
<td>{{ object.ssid }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Group</td>
|
||||||
|
<td>
|
||||||
|
{% if object.group %}
|
||||||
|
<a href="{{ object.group.get_absolute_url }}">{{ object.group }}</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">None</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Description</th>
|
<th scope="row">Description</th>
|
||||||
<td>{{ object.description|placeholder }}</td>
|
<td>{{ object.description|placeholder }}</td>
|
||||||
|
72
netbox/templates/wireless/wirelesslangroup.html
Normal file
72
netbox/templates/wireless/wirelesslangroup.html
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
{% extends 'generic/object.html' %}
|
||||||
|
{% load helpers %}
|
||||||
|
{% load plugins %}
|
||||||
|
|
||||||
|
{% block breadcrumbs %}
|
||||||
|
{{ block.super }}
|
||||||
|
{% for group in object.get_ancestors %}
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'wireless:wirelesslangroup_list' %}?parent_id={{ group.pk }}">{{ group }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<h5 class="card-header">Wireless LAN Group</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<table class="table table-hover attr-table">
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Name</th>
|
||||||
|
<td>{{ object.name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Description</th>
|
||||||
|
<td>{{ object.description|placeholder }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Parent</th>
|
||||||
|
<td>
|
||||||
|
{% if object.parent %}
|
||||||
|
<a href="{{ object.parent.get_absolute_url }}">{{ object.parent }}</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">—</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Wireless LANs</th>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'wireless:wirelesslan_list' %}?group_id={{ object.pk }}">{{ wirelesslans_table.rows|length }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% plugin_left_page object %}
|
||||||
|
</div>
|
||||||
|
<div class="col col-md-6">
|
||||||
|
{% include 'inc/custom_fields_panel.html' %}
|
||||||
|
{% plugin_right_page object %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col col-md-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">Wireless LANs</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{% include 'inc/table.html' with table=wirelesslans_table %}
|
||||||
|
</div>
|
||||||
|
{% if perms.wireless.add_wirelesslan %}
|
||||||
|
<div class="card-footer text-end noprint">
|
||||||
|
<a href="{% url 'wireless:wirelesslan_add' %}?group={{ object.pk }}" class="btn btn-sm btn-primary">
|
||||||
|
<span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Wireless LAN
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% include 'inc/paginator.html' with paginator=wirelesslans_table.paginator page=wirelesslans_table.page %}
|
||||||
|
{% plugin_full_width_page object %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
@ -5,10 +5,21 @@ from wireless.models import *
|
|||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'NestedWirelessLANSerializer',
|
'NestedWirelessLANSerializer',
|
||||||
|
'NestedWirelessLANGroupSerializer',
|
||||||
'NestedWirelessLinkSerializer',
|
'NestedWirelessLinkSerializer',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NestedWirelessLANGroupSerializer(WritableNestedSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslangroup-detail')
|
||||||
|
wirelesslan_count = serializers.IntegerField(read_only=True)
|
||||||
|
_depth = serializers.IntegerField(source='level', read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = WirelessLANGroup
|
||||||
|
fields = ['id', 'url', 'display', 'name', 'slug', 'wirelesslan_count', '_depth']
|
||||||
|
|
||||||
|
|
||||||
class NestedWirelessLANSerializer(WritableNestedSerializer):
|
class NestedWirelessLANSerializer(WritableNestedSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslan-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslan-detail')
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ from dcim.choices import LinkStatusChoices
|
|||||||
from dcim.api.serializers import NestedInterfaceSerializer
|
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 PrimaryModelSerializer
|
from netbox.api.serializers import NestedGroupModelSerializer, PrimaryModelSerializer
|
||||||
from wireless.models import *
|
from wireless.models import *
|
||||||
from .nested_serializers import *
|
from .nested_serializers import *
|
||||||
|
|
||||||
@ -14,6 +14,19 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupSerializer(NestedGroupModelSerializer):
|
||||||
|
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslangroup-detail')
|
||||||
|
parent = NestedWirelessLANGroupSerializer(required=False, allow_null=True)
|
||||||
|
wirelesslan_count = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = WirelessLANGroup
|
||||||
|
fields = [
|
||||||
|
'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'custom_fields', 'created', 'last_updated',
|
||||||
|
'wirelesslan_count', '_depth',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANSerializer(PrimaryModelSerializer):
|
class WirelessLANSerializer(PrimaryModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslan-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslan-detail')
|
||||||
vlan = NestedVLANSerializer(required=False, allow_null=True)
|
vlan = NestedVLANSerializer(required=False, allow_null=True)
|
||||||
|
@ -5,6 +5,7 @@ from . import views
|
|||||||
router = OrderedDefaultRouter()
|
router = OrderedDefaultRouter()
|
||||||
router.APIRootView = views.WirelessRootView
|
router.APIRootView = views.WirelessRootView
|
||||||
|
|
||||||
|
router.register('wireless-lan-groupss', views.WirelessLANGroupViewSet)
|
||||||
router.register('wireless-lans', views.WirelessLANViewSet)
|
router.register('wireless-lans', views.WirelessLANViewSet)
|
||||||
router.register('wireless-links', views.WirelessLinkViewSet)
|
router.register('wireless-links', views.WirelessLinkViewSet)
|
||||||
|
|
||||||
|
@ -14,6 +14,18 @@ class WirelessRootView(APIRootView):
|
|||||||
return 'Wireless'
|
return 'Wireless'
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupViewSet(CustomFieldModelViewSet):
|
||||||
|
queryset = WirelessLANGroup.objects.add_related_count(
|
||||||
|
WirelessLANGroup.objects.all(),
|
||||||
|
WirelessLAN,
|
||||||
|
'group',
|
||||||
|
'wirelesslan_count',
|
||||||
|
cumulative=True
|
||||||
|
)
|
||||||
|
serializer_class = serializers.WirelessLANGroupSerializer
|
||||||
|
filterset_class = filtersets.WirelessLANGroupFilterSet
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANViewSet(CustomFieldModelViewSet):
|
class WirelessLANViewSet(CustomFieldModelViewSet):
|
||||||
queryset = WirelessLAN.objects.prefetch_related('vlan', 'tags')
|
queryset = WirelessLAN.objects.prefetch_related('vlan', 'tags')
|
||||||
serializer_class = serializers.WirelessLANSerializer
|
serializer_class = serializers.WirelessLANSerializer
|
||||||
|
@ -3,15 +3,31 @@ from django.db.models import Q
|
|||||||
|
|
||||||
from dcim.choices import LinkStatusChoices
|
from dcim.choices import LinkStatusChoices
|
||||||
from extras.filters import TagFilter
|
from extras.filters import TagFilter
|
||||||
from netbox.filtersets import PrimaryModelFilterSet
|
from netbox.filtersets import OrganizationalModelFilterSet, PrimaryModelFilterSet
|
||||||
from .models import *
|
from .models import *
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'WirelessLANFilterSet',
|
'WirelessLANFilterSet',
|
||||||
|
'WirelessLANGroupFilterSet',
|
||||||
'WirelessLinkFilterSet',
|
'WirelessLinkFilterSet',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupFilterSet(OrganizationalModelFilterSet):
|
||||||
|
parent_id = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
queryset=WirelessLANGroup.objects.all()
|
||||||
|
)
|
||||||
|
parent = django_filters.ModelMultipleChoiceFilter(
|
||||||
|
field_name='parent__slug',
|
||||||
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
|
to_field_name='slug'
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = WirelessLANGroup
|
||||||
|
fields = ['id', 'name', 'slug', 'description']
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANFilterSet(PrimaryModelFilterSet):
|
class WirelessLANFilterSet(PrimaryModelFilterSet):
|
||||||
q = django_filters.CharFilter(
|
q = django_filters.CharFilter(
|
||||||
method='search',
|
method='search',
|
||||||
|
@ -9,15 +9,38 @@ from wireless.models import *
|
|||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'WirelessLANBulkEditForm',
|
'WirelessLANBulkEditForm',
|
||||||
|
'WirelessLANGroupBulkEditForm',
|
||||||
'WirelessLinkBulkEditForm',
|
'WirelessLinkBulkEditForm',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupBulkEditForm(BootstrapMixin, CustomFieldModelBulkEditForm):
|
||||||
|
pk = forms.ModelMultipleChoiceField(
|
||||||
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
|
widget=forms.MultipleHiddenInput
|
||||||
|
)
|
||||||
|
parent = DynamicModelChoiceField(
|
||||||
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
description = forms.CharField(
|
||||||
|
max_length=200,
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
nullable_fields = ['parent', 'description']
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class WirelessLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=WirelessLAN.objects.all(),
|
queryset=WirelessLAN.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
)
|
)
|
||||||
|
group = DynamicModelChoiceField(
|
||||||
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
vlan = DynamicModelChoiceField(
|
vlan = DynamicModelChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -31,7 +54,7 @@ class WirelessLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldMode
|
|||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
nullable_fields = ['vlan', 'ssid', 'description']
|
nullable_fields = ['ssid', 'group', 'vlan', 'description']
|
||||||
|
|
||||||
|
|
||||||
class WirelessLinkBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class WirelessLinkBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
||||||
|
@ -2,16 +2,37 @@ from dcim.choices import LinkStatusChoices
|
|||||||
from dcim.models import Interface
|
from dcim.models import Interface
|
||||||
from extras.forms import CustomFieldModelCSVForm
|
from extras.forms import CustomFieldModelCSVForm
|
||||||
from ipam.models import VLAN
|
from ipam.models import VLAN
|
||||||
from utilities.forms import CSVChoiceField, CSVModelChoiceField
|
from utilities.forms import CSVChoiceField, CSVModelChoiceField, SlugField
|
||||||
from wireless.models import *
|
from wireless.models import *
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'WirelessLANCSVForm',
|
'WirelessLANCSVForm',
|
||||||
|
'WirelessLANGroupCSVForm',
|
||||||
'WirelessLinkCSVForm',
|
'WirelessLinkCSVForm',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupCSVForm(CustomFieldModelCSVForm):
|
||||||
|
parent = CSVModelChoiceField(
|
||||||
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
to_field_name='name',
|
||||||
|
help_text='Parent group'
|
||||||
|
)
|
||||||
|
slug = SlugField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = WirelessLANGroup
|
||||||
|
fields = ('name', 'slug', 'parent', 'description')
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANCSVForm(CustomFieldModelCSVForm):
|
class WirelessLANCSVForm(CustomFieldModelCSVForm):
|
||||||
|
group = CSVModelChoiceField(
|
||||||
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
to_field_name='name',
|
||||||
|
help_text='Assigned group'
|
||||||
|
)
|
||||||
vlan = CSVModelChoiceField(
|
vlan = CSVModelChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
@ -20,7 +41,7 @@ class WirelessLANCSVForm(CustomFieldModelCSVForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = WirelessLAN
|
model = WirelessLAN
|
||||||
fields = ('ssid', 'description', 'vlan')
|
fields = ('ssid', 'group', 'description', 'vlan')
|
||||||
|
|
||||||
|
|
||||||
class WirelessLinkCSVForm(CustomFieldModelCSVForm):
|
class WirelessLinkCSVForm(CustomFieldModelCSVForm):
|
||||||
|
@ -3,19 +3,38 @@ from django.utils.translation import gettext as _
|
|||||||
|
|
||||||
from dcim.choices import LinkStatusChoices
|
from dcim.choices import LinkStatusChoices
|
||||||
from extras.forms import CustomFieldModelFilterForm
|
from extras.forms import CustomFieldModelFilterForm
|
||||||
from utilities.forms import add_blank_choice, BootstrapMixin, StaticSelect, TagFilterField
|
from utilities.forms import (
|
||||||
|
add_blank_choice, BootstrapMixin, DynamicModelMultipleChoiceField, StaticSelect, TagFilterField,
|
||||||
|
)
|
||||||
from wireless.models import *
|
from wireless.models import *
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'WirelessLANFilterForm',
|
'WirelessLANFilterForm',
|
||||||
|
'WirelessLANGroupFilterForm',
|
||||||
'WirelessLinkFilterForm',
|
'WirelessLinkFilterForm',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
|
||||||
|
model = WirelessLANGroup
|
||||||
|
q = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),
|
||||||
|
label=_('Search')
|
||||||
|
)
|
||||||
|
parent_id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
label=_('Parent group'),
|
||||||
|
fetch_trigger='open'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
|
class WirelessLANFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
|
||||||
model = WirelessLAN
|
model = WirelessLAN
|
||||||
field_groups = [
|
field_groups = [
|
||||||
['q', 'tag'],
|
('q', 'tag'),
|
||||||
|
('group_id',),
|
||||||
]
|
]
|
||||||
q = forms.CharField(
|
q = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
@ -26,6 +45,13 @@ class WirelessLANFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label='SSID'
|
label='SSID'
|
||||||
)
|
)
|
||||||
|
group_id = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
|
required=False,
|
||||||
|
null_option='None',
|
||||||
|
label=_('Group'),
|
||||||
|
fetch_trigger='open'
|
||||||
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,16 +2,37 @@ from dcim.models import Interface
|
|||||||
from extras.forms import CustomFieldModelForm
|
from extras.forms import CustomFieldModelForm
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
from ipam.models import VLAN
|
from ipam.models import VLAN
|
||||||
from utilities.forms import BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, StaticSelect
|
from utilities.forms import (
|
||||||
|
BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField, StaticSelect,
|
||||||
|
)
|
||||||
from wireless.models import *
|
from wireless.models import *
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'WirelessLANForm',
|
'WirelessLANForm',
|
||||||
|
'WirelessLANGroupForm',
|
||||||
'WirelessLinkForm',
|
'WirelessLinkForm',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupForm(BootstrapMixin, CustomFieldModelForm):
|
||||||
|
parent = DynamicModelChoiceField(
|
||||||
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
slug = SlugField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = WirelessLANGroup
|
||||||
|
fields = [
|
||||||
|
'parent', 'name', 'slug', 'description',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANForm(BootstrapMixin, CustomFieldModelForm):
|
class WirelessLANForm(BootstrapMixin, CustomFieldModelForm):
|
||||||
|
group = DynamicModelChoiceField(
|
||||||
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
vlan = DynamicModelChoiceField(
|
vlan = DynamicModelChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -24,10 +45,10 @@ class WirelessLANForm(BootstrapMixin, CustomFieldModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = WirelessLAN
|
model = WirelessLAN
|
||||||
fields = [
|
fields = [
|
||||||
'ssid', 'description', 'vlan', 'tags',
|
'ssid', 'group', 'description', 'vlan', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
('Wireless LAN', ('ssid', 'description', 'tags')),
|
('Wireless LAN', ('ssid', 'group', 'description', 'tags')),
|
||||||
('VLAN', ('vlan',)),
|
('VLAN', ('vlan',)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -7,3 +7,9 @@ from .types import *
|
|||||||
class WirelessQuery(graphene.ObjectType):
|
class WirelessQuery(graphene.ObjectType):
|
||||||
wirelesslan = ObjectField(WirelessLANType)
|
wirelesslan = ObjectField(WirelessLANType)
|
||||||
wirelesslan_list = ObjectListField(WirelessLANType)
|
wirelesslan_list = ObjectListField(WirelessLANType)
|
||||||
|
|
||||||
|
wirelesslangroup = ObjectField(WirelessLANGroupType)
|
||||||
|
wirelesslangroup_list = ObjectListField(WirelessLANGroupType)
|
||||||
|
|
||||||
|
wirelesslink = ObjectField(WirelessLinkType)
|
||||||
|
wirelesslink_list = ObjectListField(WirelessLinkType)
|
||||||
|
@ -3,10 +3,19 @@ from netbox.graphql.types import ObjectType
|
|||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'WirelessLANType',
|
'WirelessLANType',
|
||||||
|
'WirelessLANGroupType',
|
||||||
'WirelessLinkType',
|
'WirelessLinkType',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupType(ObjectType):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.WirelessLANGroup
|
||||||
|
fields = '__all__'
|
||||||
|
filterset_class = filtersets.WirelessLANGroupFilterSet
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANType(ObjectType):
|
class WirelessLANType(ObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
# Generated by Django 3.2.8 on 2021-10-13 13:44
|
|
||||||
|
|
||||||
import django.core.serializers.json
|
import django.core.serializers.json
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
import mptt.fields
|
||||||
import taggit.managers
|
import taggit.managers
|
||||||
|
|
||||||
|
|
||||||
@ -17,6 +16,27 @@ class Migration(migrations.Migration):
|
|||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='WirelessLANGroup',
|
||||||
|
fields=[
|
||||||
|
('created', models.DateField(auto_now_add=True, null=True)),
|
||||||
|
('last_updated', models.DateTimeField(auto_now=True, null=True)),
|
||||||
|
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
|
||||||
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=100, unique=True)),
|
||||||
|
('slug', models.SlugField(max_length=100, unique=True)),
|
||||||
|
('description', models.CharField(blank=True, max_length=200)),
|
||||||
|
('lft', models.PositiveIntegerField(editable=False)),
|
||||||
|
('rght', models.PositiveIntegerField(editable=False)),
|
||||||
|
('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
|
||||||
|
('level', models.PositiveIntegerField(editable=False)),
|
||||||
|
('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='wireless.wirelesslangroup')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ('name', 'pk'),
|
||||||
|
'unique_together': {('parent', 'name')},
|
||||||
|
},
|
||||||
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='WirelessLAN',
|
name='WirelessLAN',
|
||||||
fields=[
|
fields=[
|
||||||
@ -25,6 +45,7 @@ class Migration(migrations.Migration):
|
|||||||
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
|
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
|
||||||
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
('ssid', models.CharField(max_length=32)),
|
('ssid', models.CharField(max_length=32)),
|
||||||
|
('group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wireless_lans', to='wireless.wirelesslangroup')),
|
||||||
('description', models.CharField(blank=True, max_length=200)),
|
('description', models.CharField(blank=True, max_length=200)),
|
||||||
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
|
||||||
('vlan', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='ipam.vlan')),
|
('vlan', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='ipam.vlan')),
|
||||||
|
@ -1,20 +1,58 @@
|
|||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from mptt.models import MPTTModel, TreeForeignKey
|
||||||
|
|
||||||
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 extras.utils import extras_features
|
from extras.utils import extras_features
|
||||||
from netbox.models import BigIDModel, PrimaryModel
|
from netbox.models import BigIDModel, NestedGroupModel, PrimaryModel
|
||||||
from utilities.querysets import RestrictedQuerySet
|
from utilities.querysets import RestrictedQuerySet
|
||||||
from .constants import SSID_MAX_LENGTH
|
from .constants import SSID_MAX_LENGTH
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'WirelessLAN',
|
'WirelessLAN',
|
||||||
|
'WirelessLANGroup',
|
||||||
'WirelessLink',
|
'WirelessLink',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
|
||||||
|
class WirelessLANGroup(NestedGroupModel):
|
||||||
|
"""
|
||||||
|
A nested grouping of WirelessLANs
|
||||||
|
"""
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=100,
|
||||||
|
unique=True
|
||||||
|
)
|
||||||
|
slug = models.SlugField(
|
||||||
|
max_length=100,
|
||||||
|
unique=True
|
||||||
|
)
|
||||||
|
parent = TreeForeignKey(
|
||||||
|
to='self',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='children',
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
db_index=True
|
||||||
|
)
|
||||||
|
description = models.CharField(
|
||||||
|
max_length=200,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('name', 'pk')
|
||||||
|
unique_together = (
|
||||||
|
('parent', 'name')
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse('wireless:wirelesslangroup', args=[self.pk])
|
||||||
|
|
||||||
|
|
||||||
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
|
||||||
class WirelessLAN(PrimaryModel):
|
class WirelessLAN(PrimaryModel):
|
||||||
"""
|
"""
|
||||||
@ -24,6 +62,13 @@ class WirelessLAN(PrimaryModel):
|
|||||||
max_length=SSID_MAX_LENGTH,
|
max_length=SSID_MAX_LENGTH,
|
||||||
verbose_name='SSID'
|
verbose_name='SSID'
|
||||||
)
|
)
|
||||||
|
group = models.ForeignKey(
|
||||||
|
to='wireless.WirelessLANGroup',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name='wireless_lans',
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
vlan = models.ForeignKey(
|
vlan = models.ForeignKey(
|
||||||
to='ipam.VLAN',
|
to='ipam.VLAN',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
@ -100,7 +145,7 @@ class WirelessLink(PrimaryModel):
|
|||||||
|
|
||||||
objects = RestrictedQuerySet.as_manager()
|
objects = RestrictedQuerySet.as_manager()
|
||||||
|
|
||||||
clone_fields = ('ssid', 'status')
|
clone_fields = ('ssid', 'group', 'status')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['pk']
|
ordering = ['pk']
|
||||||
|
@ -1,14 +1,35 @@
|
|||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
|
|
||||||
|
from utilities.tables import (
|
||||||
|
BaseTable, ButtonsColumn, ChoiceFieldColumn, LinkedCountColumn, MPTTColumn, TagColumn, ToggleColumn,
|
||||||
|
)
|
||||||
from .models import *
|
from .models import *
|
||||||
from utilities.tables import BaseTable, ChoiceFieldColumn, TagColumn, ToggleColumn
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'WirelessLANTable',
|
'WirelessLANTable',
|
||||||
|
'WirelessLANGroupTable',
|
||||||
'WirelessLinkTable',
|
'WirelessLinkTable',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupTable(BaseTable):
|
||||||
|
pk = ToggleColumn()
|
||||||
|
name = MPTTColumn(
|
||||||
|
linkify=True
|
||||||
|
)
|
||||||
|
wirelesslan_count = LinkedCountColumn(
|
||||||
|
viewname='wireless:wirelesslan_list',
|
||||||
|
url_params={'group_id': 'pk'},
|
||||||
|
verbose_name='Wireless LANs'
|
||||||
|
)
|
||||||
|
actions = ButtonsColumn(WirelessLANGroup)
|
||||||
|
|
||||||
|
class Meta(BaseTable.Meta):
|
||||||
|
model = WirelessLANGroup
|
||||||
|
fields = ('pk', 'name', 'wirelesslan_count', 'description', 'slug', 'actions')
|
||||||
|
default_columns = ('pk', 'name', 'wirelesslan_count', 'description', 'actions')
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANTable(BaseTable):
|
class WirelessLANTable(BaseTable):
|
||||||
pk = ToggleColumn()
|
pk = ToggleColumn()
|
||||||
ssid = tables.Column(
|
ssid = tables.Column(
|
||||||
|
@ -7,6 +7,17 @@ from .models import *
|
|||||||
app_name = 'wireless'
|
app_name = 'wireless'
|
||||||
urlpatterns = (
|
urlpatterns = (
|
||||||
|
|
||||||
|
# Wireless LAN groups
|
||||||
|
path('wireless-lan-groups/', views.WirelessLANGroupListView.as_view(), name='wirelesslangroup_list'),
|
||||||
|
path('wireless-lan-groups/add/', views.WirelessLANGroupEditView.as_view(), name='wirelesslangroup_add'),
|
||||||
|
path('wireless-lan-groups/import/', views.WirelessLANGroupBulkImportView.as_view(), name='wirelesslangroup_import'),
|
||||||
|
path('wireless-lan-groups/edit/', views.WirelessLANGroupBulkEditView.as_view(), name='wirelesslangroup_bulk_edit'),
|
||||||
|
path('wireless-lan-groups/delete/', views.WirelessLANGroupBulkDeleteView.as_view(), name='wirelesslangroup_bulk_delete'),
|
||||||
|
path('wireless-lan-groups/<int:pk>/', views.WirelessLANGroupView.as_view(), name='wirelesslangroup'),
|
||||||
|
path('wireless-lan-groups/<int:pk>/edit/', views.WirelessLANGroupEditView.as_view(), name='wirelesslangroup_edit'),
|
||||||
|
path('wireless-lan-groups/<int:pk>/delete/', views.WirelessLANGroupDeleteView.as_view(), name='wirelesslangroup_delete'),
|
||||||
|
path('wireless-lan-groups/<int:pk>/changelog/', ObjectChangeLogView.as_view(), name='wirelesslangroup_changelog', kwargs={'model': WirelessLANGroup}),
|
||||||
|
|
||||||
# Wireless LANs
|
# Wireless LANs
|
||||||
path('wireless-lans/', views.WirelessLANListView.as_view(), name='wirelesslan_list'),
|
path('wireless-lans/', views.WirelessLANListView.as_view(), name='wirelesslan_list'),
|
||||||
path('wireless-lans/add/', views.WirelessLANEditView.as_view(), name='wirelesslan_add'),
|
path('wireless-lans/add/', views.WirelessLANEditView.as_view(), name='wirelesslan_add'),
|
||||||
|
@ -1,8 +1,81 @@
|
|||||||
from netbox.views import generic
|
from netbox.views import generic
|
||||||
|
from utilities.tables import paginate_table
|
||||||
from . import filtersets, forms, tables
|
from . import filtersets, forms, tables
|
||||||
from .models import *
|
from .models import *
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Wireless LAN groups
|
||||||
|
#
|
||||||
|
|
||||||
|
class WirelessLANGroupListView(generic.ObjectListView):
|
||||||
|
queryset = WirelessLANGroup.objects.add_related_count(
|
||||||
|
WirelessLANGroup.objects.all(),
|
||||||
|
WirelessLAN,
|
||||||
|
'group',
|
||||||
|
'wirelesslan_count',
|
||||||
|
cumulative=True
|
||||||
|
)
|
||||||
|
filterset = filtersets.WirelessLANGroupFilterSet
|
||||||
|
filterset_form = forms.WirelessLANGroupFilterForm
|
||||||
|
table = tables.WirelessLANGroupTable
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupView(generic.ObjectView):
|
||||||
|
queryset = WirelessLANGroup.objects.all()
|
||||||
|
|
||||||
|
def get_extra_context(self, request, instance):
|
||||||
|
wirelesslans = WirelessLAN.objects.restrict(request.user, 'view').filter(
|
||||||
|
group=instance
|
||||||
|
)
|
||||||
|
wirelesslans_table = tables.WirelessLANTable(wirelesslans, exclude=('group',))
|
||||||
|
paginate_table(wirelesslans_table, request)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'wirelesslans_table': wirelesslans_table,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupEditView(generic.ObjectEditView):
|
||||||
|
queryset = WirelessLANGroup.objects.all()
|
||||||
|
model_form = forms.WirelessLANGroupForm
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupDeleteView(generic.ObjectDeleteView):
|
||||||
|
queryset = WirelessLANGroup.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupBulkImportView(generic.BulkImportView):
|
||||||
|
queryset = WirelessLANGroup.objects.all()
|
||||||
|
model_form = forms.WirelessLANGroupCSVForm
|
||||||
|
table = tables.WirelessLANGroupTable
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupBulkEditView(generic.BulkEditView):
|
||||||
|
queryset = WirelessLANGroup.objects.add_related_count(
|
||||||
|
WirelessLANGroup.objects.all(),
|
||||||
|
WirelessLAN,
|
||||||
|
'group',
|
||||||
|
'wirelesslan_count',
|
||||||
|
cumulative=True
|
||||||
|
)
|
||||||
|
filterset = filtersets.WirelessLANGroupFilterSet
|
||||||
|
table = tables.WirelessLANGroupTable
|
||||||
|
form = forms.WirelessLANGroupBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class WirelessLANGroupBulkDeleteView(generic.BulkDeleteView):
|
||||||
|
queryset = WirelessLANGroup.objects.add_related_count(
|
||||||
|
WirelessLANGroup.objects.all(),
|
||||||
|
WirelessLAN,
|
||||||
|
'group',
|
||||||
|
'wirelesslan_count',
|
||||||
|
cumulative=True
|
||||||
|
)
|
||||||
|
filterset = filtersets.WirelessLANGroupFilterSet
|
||||||
|
table = tables.WirelessLANGroupTable
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Wireless LANs
|
# Wireless LANs
|
||||||
#
|
#
|
||||||
|
Loading…
Reference in New Issue
Block a user