Przeglądaj źródła

Closes #10710: Add status field to WirelessLAN

jeremystretch 3 lat temu
rodzic
commit
ad40d42dc4

+ 7 - 0
docs/models/wireless/wirelesslan.md

@@ -12,6 +12,13 @@ The service set identifier (SSID) for the wireless network.
 
 The [wireless LAN group](./wirelesslangroup.md) to which this wireless LAN is assigned (if any).
 
+### Status
+
+The operational status of the wireless network.
+
+!!! tip
+    Additional statuses may be defined by setting `WirelessLAN.status` under the [`FIELD_CHOICES`](../../configuration/data-validation.md#field_choices) configuration parameter.
+
 ### VLAN
 
 Each wireless LAN can optionally be mapped to a [VLAN](../ipam/vlan.md), to model a bridge between wired and wireless segments.

+ 4 - 0
netbox/templates/wireless/wirelesslan.html

@@ -18,6 +18,10 @@
             <td>Group</td>
             <td>{{ object.group|linkify|placeholder }}</td>
           </tr>
+          <tr>
+            <td>Status</td>
+            <td>{% badge object.get_status_display bg_color=object.get_status_color %}</td>
+          </tr>
           <tr>
             <th scope="row">Description</th>
             <td>{{ object.description|placeholder }}</td>

+ 3 - 2
netbox/wireless/api/serializers.py

@@ -33,6 +33,7 @@ class WirelessLANGroupSerializer(NestedGroupModelSerializer):
 class WirelessLANSerializer(NetBoxModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslan-detail')
     group = NestedWirelessLANGroupSerializer(required=False, allow_null=True)
+    status = ChoiceField(choices=WirelessLANStatusChoices, required=False, allow_blank=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)
@@ -41,8 +42,8 @@ class WirelessLANSerializer(NetBoxModelSerializer):
     class Meta:
         model = WirelessLAN
         fields = [
-            'id', 'url', 'display', 'ssid', 'description', 'group', 'vlan', 'tenant', 'auth_type', 'auth_cipher',
-            'auth_psk', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
+            'id', 'url', 'display', 'ssid', 'description', 'group', 'status', 'vlan', 'tenant', 'auth_type',
+            'auth_cipher', 'auth_psk', 'description', 'comments', 'tags', 'custom_fields', 'created', 'last_updated',
         ]
 
 

+ 18 - 0
netbox/wireless/choices.py

@@ -1,3 +1,5 @@
+from django.utils.translation import gettext as _
+
 from utilities.choices import ChoiceSet
 
 
@@ -11,6 +13,22 @@ class WirelessRoleChoices(ChoiceSet):
     )
 
 
+class WirelessLANStatusChoices(ChoiceSet):
+    key = 'WirelessLANS.status'
+
+    STATUS_ACTIVE = 'active'
+    STATUS_RESERVED = 'reserved'
+    STATUS_DISABLED = 'disabled'
+    STATUS_DEPRECATED = 'deprecated'
+
+    CHOICES = [
+        (STATUS_ACTIVE, _('Active'), 'green'),
+        (STATUS_RESERVED, _('Reserved'), 'cyan'),
+        (STATUS_DISABLED, _('Disabled'), 'orange'),
+        (STATUS_DEPRECATED, _('Deprecated'), 'red'),
+    ]
+
+
 class WirelessChannelChoices(ChoiceSet):
 
     # 2.4 GHz

+ 3 - 0
netbox/wireless/filtersets.py

@@ -43,6 +43,9 @@ class WirelessLANFilterSet(NetBoxModelFilterSet, TenancyFilterSet):
         lookup_expr='in',
         to_field_name='slug'
     )
+    status = django_filters.MultipleChoiceFilter(
+        choices=WirelessLANStatusChoices
+    )
     vlan_id = django_filters.ModelMultipleChoiceFilter(
         queryset=VLAN.objects.all()
     )

+ 5 - 1
netbox/wireless/forms/bulk_edit.py

@@ -34,6 +34,10 @@ class WirelessLANGroupBulkEditForm(NetBoxModelBulkEditForm):
 
 
 class WirelessLANBulkEditForm(NetBoxModelBulkEditForm):
+    status = forms.ChoiceField(
+        choices=add_blank_choice(WirelessLANStatusChoices),
+        required=False
+    )
     group = DynamicModelChoiceField(
         queryset=WirelessLANGroup.objects.all(),
         required=False
@@ -75,7 +79,7 @@ class WirelessLANBulkEditForm(NetBoxModelBulkEditForm):
 
     model = WirelessLAN
     fieldsets = (
-        (None, ('group', 'ssid', 'vlan', 'tenant', 'description')),
+        (None, ('group', 'ssid', 'status', 'vlan', 'tenant', 'description')),
         ('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
     )
     nullable_fields = (

+ 6 - 2
netbox/wireless/forms/bulk_import.py

@@ -35,6 +35,10 @@ class WirelessLANCSVForm(NetBoxModelCSVForm):
         to_field_name='name',
         help_text='Assigned group'
     )
+    status = CSVChoiceField(
+        choices=WirelessLANStatusChoices,
+        help_text='Operational status'
+    )
     vlan = CSVModelChoiceField(
         queryset=VLAN.objects.all(),
         required=False,
@@ -61,8 +65,8 @@ class WirelessLANCSVForm(NetBoxModelCSVForm):
     class Meta:
         model = WirelessLAN
         fields = (
-            'ssid', 'group', 'vlan', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk', 'description', 'comments',
-            'tags',
+            'ssid', 'group', 'status', 'vlan', 'tenant', 'auth_type', 'auth_cipher', 'auth_psk', 'description',
+            'comments', 'tags',
         )
 
 

+ 6 - 1
netbox/wireless/forms/filtersets.py

@@ -29,7 +29,7 @@ class WirelessLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
     model = WirelessLAN
     fieldsets = (
         (None, ('q', 'filter', 'tag')),
-        ('Attributes', ('ssid', 'group_id',)),
+        ('Attributes', ('ssid', 'group_id', 'status')),
         ('Tenant', ('tenant_group_id', 'tenant_id')),
         ('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
     )
@@ -43,6 +43,11 @@ class WirelessLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
         null_option='None',
         label=_('Group')
     )
+    status = forms.ChoiceField(
+        required=False,
+        choices=add_blank_choice(WirelessLANStatusChoices),
+        widget=StaticSelect()
+    )
     auth_type = forms.ChoiceField(
         required=False,
         choices=add_blank_choice(WirelessAuthTypeChoices),

+ 3 - 3
netbox/wireless/forms/model_forms.py

@@ -37,7 +37,6 @@ class WirelessLANForm(TenancyForm, NetBoxModelForm):
         queryset=WirelessLANGroup.objects.all(),
         required=False
     )
-
     region = DynamicModelChoiceField(
         queryset=Region.objects.all(),
         required=False,
@@ -85,7 +84,7 @@ class WirelessLANForm(TenancyForm, NetBoxModelForm):
     comments = CommentField()
 
     fieldsets = (
-        ('Wireless LAN', ('ssid', 'group', 'description', 'tags')),
+        ('Wireless LAN', ('ssid', 'group', 'status', 'description', 'tags')),
         ('VLAN', ('region', 'site_group', 'site', 'vlan_group', 'vlan',)),
         ('Tenancy', ('tenant_group', 'tenant')),
         ('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
@@ -94,10 +93,11 @@ class WirelessLANForm(TenancyForm, NetBoxModelForm):
     class Meta:
         model = WirelessLAN
         fields = [
-            'ssid', 'group', 'region', 'site_group', 'site', 'vlan_group', 'vlan', 'tenant_group', 'tenant',
+            'ssid', 'group', 'region', 'site_group', 'site', 'status', 'vlan_group', 'vlan', 'tenant_group', 'tenant',
             'auth_type', 'auth_cipher', 'auth_psk', 'description', 'comments', 'tags',
         ]
         widgets = {
+            'status': StaticSelect,
             'auth_type': StaticSelect,
             'auth_cipher': StaticSelect,
         }

+ 18 - 0
netbox/wireless/migrations/0008_wirelesslan_status.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.1.2 on 2022-11-04 17:07
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('wireless', '0007_standardize_description_comments'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='wirelesslan',
+            name='status',
+            field=models.CharField(default='active', max_length=50),
+        ),
+    ]

+ 8 - 0
netbox/wireless/models.py

@@ -84,6 +84,11 @@ class WirelessLAN(WirelessAuthenticationBase, PrimaryModel):
         blank=True,
         null=True
     )
+    status = models.CharField(
+        max_length=50,
+        choices=WirelessLANStatusChoices,
+        default=WirelessLANStatusChoices.STATUS_ACTIVE
+    )
     vlan = models.ForeignKey(
         to='ipam.VLAN',
         on_delete=models.PROTECT,
@@ -111,6 +116,9 @@ class WirelessLAN(WirelessAuthenticationBase, PrimaryModel):
     def get_absolute_url(self):
         return reverse('wireless:wirelesslan', args=[self.pk])
 
+    def get_status_color(self):
+        return WirelessLANStatusChoices.colors.get(self.status)
+
 
 def get_wireless_interface_types():
     # Wrap choices in a callable to avoid generating dummy migrations

+ 4 - 3
netbox/wireless/tables/wirelesslan.py

@@ -42,6 +42,7 @@ class WirelessLANTable(TenancyColumnsMixin, NetBoxTable):
     group = tables.Column(
         linkify=True
     )
+    status = columns.ChoiceFieldColumn()
     interface_count = tables.Column(
         verbose_name='Interfaces'
     )
@@ -53,10 +54,10 @@ class WirelessLANTable(TenancyColumnsMixin, NetBoxTable):
     class Meta(NetBoxTable.Meta):
         model = WirelessLAN
         fields = (
-            'pk', 'ssid', 'group', 'tenant', 'tenant_group', 'vlan', 'interface_count', 'auth_type', 'auth_cipher',
-            'auth_psk', 'description', 'comments', 'tags', 'created', 'last_updated',
+            'pk', 'ssid', 'group', 'status', 'tenant', 'tenant_group', 'vlan', 'interface_count', 'auth_type',
+            'auth_cipher', 'auth_psk', 'description', 'comments', 'tags', 'created', 'last_updated',
         )
-        default_columns = ('pk', 'ssid', 'group', 'description', 'vlan', 'auth_type', 'interface_count')
+        default_columns = ('pk', 'ssid', 'group', 'status', 'description', 'vlan', 'auth_type', 'interface_count')
 
 
 class WirelessLANInterfacesTable(NetBoxTable):

+ 7 - 3
netbox/wireless/tests/test_api.py

@@ -68,9 +68,9 @@ class WirelessLANTest(APIViewTestCases.APIViewTestCase):
             group.save()
 
         wireless_lans = (
-            WirelessLAN(ssid='WLAN1'),
-            WirelessLAN(ssid='WLAN2'),
-            WirelessLAN(ssid='WLAN3'),
+            WirelessLAN(ssid='WLAN1', status=WirelessLANStatusChoices.STATUS_ACTIVE),
+            WirelessLAN(ssid='WLAN2', status=WirelessLANStatusChoices.STATUS_ACTIVE),
+            WirelessLAN(ssid='WLAN3', status=WirelessLANStatusChoices.STATUS_ACTIVE),
         )
         WirelessLAN.objects.bulk_create(wireless_lans)
 
@@ -78,23 +78,27 @@ class WirelessLANTest(APIViewTestCases.APIViewTestCase):
             {
                 'ssid': 'WLAN4',
                 'group': groups[0].pk,
+                'status': WirelessLANStatusChoices.STATUS_DISABLED,
                 'tenant': tenants[0].pk,
                 'auth_type': WirelessAuthTypeChoices.TYPE_OPEN,
             },
             {
                 'ssid': 'WLAN5',
                 'group': groups[1].pk,
+                'status': WirelessLANStatusChoices.STATUS_DISABLED,
                 'tenant': tenants[0].pk,
                 'auth_type': WirelessAuthTypeChoices.TYPE_WPA_PERSONAL,
             },
             {
                 'ssid': 'WLAN6',
+                'status': WirelessLANStatusChoices.STATUS_DISABLED,
                 'tenant': tenants[0].pk,
                 'auth_type': WirelessAuthTypeChoices.TYPE_WPA_ENTERPRISE,
             },
         ]
 
         cls.bulk_update_data = {
+            'status': WirelessLANStatusChoices.STATUS_DEPRECATED,
             'group': groups[2].pk,
             'tenant': tenants[1].pk,
             'description': 'New description',

+ 46 - 6
netbox/wireless/tests/test_filtersets.py

@@ -64,9 +64,18 @@ class WirelessLANTestCase(TestCase, ChangeLoggedFilterSetTests):
     def setUpTestData(cls):
 
         groups = (
-            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 3', slug='wireless-lan-group-3'),
+            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 3',
+                slug='wireless-lan-group-3'
+            ),
         )
         for group in groups:
             group.save()
@@ -86,9 +95,36 @@ class WirelessLANTestCase(TestCase, ChangeLoggedFilterSetTests):
         Tenant.objects.bulk_create(tenants)
 
         wireless_lans = (
-            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], 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], tenant=tenants[2], auth_type=WirelessAuthTypeChoices.TYPE_WPA_PERSONAL, auth_cipher=WirelessAuthCipherChoices.CIPHER_AES, auth_psk='PSK3'),
+            WirelessLAN(
+                ssid='WLAN1',
+                group=groups[0],
+                status=WirelessLANStatusChoices.STATUS_ACTIVE,
+                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],
+                status=WirelessLANStatusChoices.STATUS_DISABLED,
+                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],
+                status=WirelessLANStatusChoices.STATUS_RESERVED,
+                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)
 
@@ -103,6 +139,10 @@ class WirelessLANTestCase(TestCase, ChangeLoggedFilterSetTests):
         params = {'group': [groups[0].slug, groups[1].slug]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
 
+    def test_status(self):
+        params = {'status': [WirelessLANStatusChoices.STATUS_ACTIVE, WirelessLANStatusChoices.STATUS_DISABLED]}
+        self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
     def test_vlan(self):
         vlans = VLAN.objects.all()[:2]
         params = {'vlan_id': [vlans[0].pk, vlans[1].pk]}

+ 24 - 7
netbox/wireless/tests/test_views.py

@@ -70,9 +70,24 @@ class WirelessLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
             group.save()
 
         wireless_lans = (
-            WirelessLAN(group=groups[0], ssid='WLAN1', tenant=tenants[0]),
-            WirelessLAN(group=groups[0], ssid='WLAN2', tenant=tenants[0]),
-            WirelessLAN(group=groups[0], ssid='WLAN3', tenant=tenants[0]),
+            WirelessLAN(
+                group=groups[0],
+                ssid='WLAN1',
+                status=WirelessLANStatusChoices.STATUS_ACTIVE,
+                tenant=tenants[0]
+            ),
+            WirelessLAN(
+                group=groups[0],
+                ssid='WLAN2',
+                status=WirelessLANStatusChoices.STATUS_ACTIVE,
+                tenant=tenants[0]
+            ),
+            WirelessLAN(
+                group=groups[0],
+                ssid='WLAN3',
+                status=WirelessLANStatusChoices.STATUS_ACTIVE,
+                tenant=tenants[0]
+            ),
         )
         WirelessLAN.objects.bulk_create(wireless_lans)
 
@@ -81,15 +96,16 @@ class WirelessLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
         cls.form_data = {
             'ssid': 'WLAN2',
             'group': groups[1].pk,
+            'status': WirelessLANStatusChoices.STATUS_DISABLED,
             'tenant': tenants[1].pk,
             'tags': [t.pk for t in tags],
         }
 
         cls.csv_data = (
-            f"group,ssid,tenant",
-            f"Wireless LAN Group 2,WLAN4,{tenants[0].name}",
-            f"Wireless LAN Group 2,WLAN5,{tenants[1].name}",
-            f"Wireless LAN Group 2,WLAN6,{tenants[2].name}",
+            f"group,ssid,status,tenant",
+            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,WLAN6,{WirelessLANStatusChoices.STATUS_RESERVED},{tenants[2].name}",
         )
 
         cls.csv_update_data = (
@@ -100,6 +116,7 @@ class WirelessLANTestCase(ViewTestCases.PrimaryObjectViewTestCase):
         )
 
         cls.bulk_edit_data = {
+            'status': WirelessLANStatusChoices.STATUS_DISABLED,
             'description': 'New description',
         }