Просмотр исходного кода

Add status field to WirelessLink

jeremystretch 4 лет назад
Родитель
Сommit
95ed07a95e

+ 1 - 1
netbox/dcim/api/serializers.py

@@ -757,7 +757,7 @@ class CableSerializer(PrimaryModelSerializer):
     )
     )
     termination_a = serializers.SerializerMethodField(read_only=True)
     termination_a = serializers.SerializerMethodField(read_only=True)
     termination_b = serializers.SerializerMethodField(read_only=True)
     termination_b = serializers.SerializerMethodField(read_only=True)
-    status = ChoiceField(choices=CableStatusChoices, required=False)
+    status = ChoiceField(choices=LinkStatusChoices, required=False)
     length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False)
     length_unit = ChoiceField(choices=CableLengthUnitChoices, allow_blank=True, required=False)
 
 
     class Meta:
     class Meta:

+ 2 - 2
netbox/dcim/choices.py

@@ -1030,7 +1030,7 @@ class PortTypeChoices(ChoiceSet):
 
 
 
 
 #
 #
-# Cables
+# Cables/links
 #
 #
 
 
 class CableTypeChoices(ChoiceSet):
 class CableTypeChoices(ChoiceSet):
@@ -1094,7 +1094,7 @@ class CableTypeChoices(ChoiceSet):
     )
     )
 
 
 
 
-class CableStatusChoices(ChoiceSet):
+class LinkStatusChoices(ChoiceSet):
 
 
     STATUS_CONNECTED = 'connected'
     STATUS_CONNECTED = 'connected'
     STATUS_PLANNED = 'planned'
     STATUS_PLANNED = 'planned'

+ 1 - 1
netbox/dcim/filtersets.py

@@ -1205,7 +1205,7 @@ class CableFilterSet(PrimaryModelFilterSet):
         choices=CableTypeChoices
         choices=CableTypeChoices
     )
     )
     status = django_filters.MultipleChoiceFilter(
     status = django_filters.MultipleChoiceFilter(
-        choices=CableStatusChoices
+        choices=LinkStatusChoices
     )
     )
     color = django_filters.MultipleChoiceFilter(
     color = django_filters.MultipleChoiceFilter(
         choices=ColorChoices
         choices=ColorChoices

+ 1 - 1
netbox/dcim/forms/bulk_edit.py

@@ -453,7 +453,7 @@ class CableBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkE
         widget=StaticSelect()
         widget=StaticSelect()
     )
     )
     status = forms.ChoiceField(
     status = forms.ChoiceField(
-        choices=add_blank_choice(CableStatusChoices),
+        choices=add_blank_choice(LinkStatusChoices),
         required=False,
         required=False,
         widget=StaticSelect(),
         widget=StaticSelect(),
         initial=''
         initial=''

+ 1 - 1
netbox/dcim/forms/bulk_import.py

@@ -807,7 +807,7 @@ class CableCSVForm(CustomFieldModelCSVForm):
 
 
     # Cable attributes
     # Cable attributes
     status = CSVChoiceField(
     status = CSVChoiceField(
-        choices=CableStatusChoices,
+        choices=LinkStatusChoices,
         required=False,
         required=False,
         help_text='Connection status'
         help_text='Connection status'
     )
     )

+ 1 - 1
netbox/dcim/forms/filtersets.py

@@ -732,7 +732,7 @@ class CableFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
     )
     )
     status = forms.ChoiceField(
     status = forms.ChoiceField(
         required=False,
         required=False,
-        choices=add_blank_choice(CableStatusChoices),
+        choices=add_blank_choice(LinkStatusChoices),
         widget=StaticSelect()
         widget=StaticSelect()
     )
     )
     color = ColorField(
     color = ColorField(

+ 4 - 4
netbox/dcim/models/cables.py

@@ -64,8 +64,8 @@ class Cable(PrimaryModel):
     )
     )
     status = models.CharField(
     status = models.CharField(
         max_length=50,
         max_length=50,
-        choices=CableStatusChoices,
-        default=CableStatusChoices.STATUS_CONNECTED
+        choices=LinkStatusChoices,
+        default=LinkStatusChoices.STATUS_CONNECTED
     )
     )
     label = models.CharField(
     label = models.CharField(
         max_length=100,
         max_length=100,
@@ -285,7 +285,7 @@ class Cable(PrimaryModel):
         self._pk = self.pk
         self._pk = self.pk
 
 
     def get_status_class(self):
     def get_status_class(self):
-        return CableStatusChoices.CSS_CLASSES.get(self.status)
+        return LinkStatusChoices.CSS_CLASSES.get(self.status)
 
 
     def get_compatible_types(self):
     def get_compatible_types(self):
         """
         """
@@ -390,7 +390,7 @@ class CablePath(BigIDModel):
 
 
         node = origin
         node = origin
         while node.link is not None:
         while node.link is not None:
-            if hasattr(node.link, 'status') and node.link.status != CableStatusChoices.STATUS_CONNECTED:
+            if hasattr(node.link, 'status') and node.link.status != LinkStatusChoices.STATUS_CONNECTED:
                 is_active = False
                 is_active = False
 
 
             # Follow the link to its far-end termination
             # Follow the link to its far-end termination

+ 2 - 2
netbox/dcim/signals.py

@@ -4,7 +4,7 @@ from django.contrib.contenttypes.models import ContentType
 from django.db.models.signals import post_save, post_delete, pre_delete
 from django.db.models.signals import post_save, post_delete, pre_delete
 from django.dispatch import receiver
 from django.dispatch import receiver
 
 
-from .choices import CableStatusChoices
+from .choices import LinkStatusChoices
 from .models import Cable, CablePath, Device, PathEndpoint, PowerPanel, Rack, Location, VirtualChassis
 from .models import Cable, CablePath, Device, PathEndpoint, PowerPanel, Rack, Location, VirtualChassis
 from .utils import create_cablepath, rebuild_paths
 from .utils import create_cablepath, rebuild_paths
 
 
@@ -102,7 +102,7 @@ def update_connected_endpoints(instance, created, raw=False, **kwargs):
         # We currently don't support modifying either termination of an existing Cable. (This
         # We currently don't support modifying either termination of an existing Cable. (This
         # may change in the future.) However, we do need to capture status changes and update
         # may change in the future.) However, we do need to capture status changes and update
         # any CablePaths accordingly.
         # any CablePaths accordingly.
-        if instance.status != CableStatusChoices.STATUS_CONNECTED:
+        if instance.status != LinkStatusChoices.STATUS_CONNECTED:
             CablePath.objects.filter(path__contains=instance).update(is_active=False)
             CablePath.objects.filter(path__contains=instance).update(is_active=False)
         else:
         else:
             rebuild_paths(instance)
             rebuild_paths(instance)

+ 3 - 3
netbox/dcim/tests/test_cablepaths.py

@@ -2,7 +2,7 @@ from django.contrib.contenttypes.models import ContentType
 from django.test import TestCase
 from django.test import TestCase
 
 
 from circuits.models import *
 from circuits.models import *
-from dcim.choices import CableStatusChoices
+from dcim.choices import LinkStatusChoices
 from dcim.models import *
 from dcim.models import *
 from dcim.utils import object_to_path_node
 from dcim.utils import object_to_path_node
 
 
@@ -1142,7 +1142,7 @@ class CablePathTestCase(TestCase):
         self.assertEqual(CablePath.objects.count(), 2)
         self.assertEqual(CablePath.objects.count(), 2)
 
 
         # Change cable 2's status to "planned"
         # Change cable 2's status to "planned"
-        cable2.status = CableStatusChoices.STATUS_PLANNED
+        cable2.status = LinkStatusChoices.STATUS_PLANNED
         cable2.save()
         cable2.save()
         self.assertPathExists(
         self.assertPathExists(
             origin=interface1,
             origin=interface1,
@@ -1160,7 +1160,7 @@ class CablePathTestCase(TestCase):
 
 
         # Change cable 2's status to "connected"
         # Change cable 2's status to "connected"
         cable2 = Cable.objects.get(pk=cable2.pk)
         cable2 = Cable.objects.get(pk=cable2.pk)
-        cable2.status = CableStatusChoices.STATUS_CONNECTED
+        cable2.status = LinkStatusChoices.STATUS_CONNECTED
         cable2.save()
         cable2.save()
         self.assertPathExists(
         self.assertPathExists(
             origin=interface1,
             origin=interface1,

+ 8 - 8
netbox/dcim/tests/test_filtersets.py

@@ -2855,12 +2855,12 @@ class CableTestCase(TestCase, ChangeLoggedFilterSetTests):
         console_server_port = ConsoleServerPort.objects.create(device=devices[0], name='Console Server Port 1')
         console_server_port = ConsoleServerPort.objects.create(device=devices[0], name='Console Server Port 1')
 
 
         # Cables
         # Cables
-        Cable(termination_a=interfaces[1], termination_b=interfaces[2], label='Cable 1', type=CableTypeChoices.TYPE_CAT3, status=CableStatusChoices.STATUS_CONNECTED, color='aa1409', length=10, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
-        Cable(termination_a=interfaces[3], termination_b=interfaces[4], label='Cable 2', type=CableTypeChoices.TYPE_CAT3, status=CableStatusChoices.STATUS_CONNECTED, color='aa1409', length=20, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
-        Cable(termination_a=interfaces[5], termination_b=interfaces[6], label='Cable 3', type=CableTypeChoices.TYPE_CAT5E, status=CableStatusChoices.STATUS_CONNECTED, color='f44336', length=30, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
-        Cable(termination_a=interfaces[7], termination_b=interfaces[8], label='Cable 4', type=CableTypeChoices.TYPE_CAT5E, status=CableStatusChoices.STATUS_PLANNED, color='f44336', length=40, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
-        Cable(termination_a=interfaces[9], termination_b=interfaces[10], label='Cable 5', type=CableTypeChoices.TYPE_CAT6, status=CableStatusChoices.STATUS_PLANNED, color='e91e63', length=10, length_unit=CableLengthUnitChoices.UNIT_METER).save()
-        Cable(termination_a=interfaces[11], termination_b=interfaces[0], label='Cable 6', type=CableTypeChoices.TYPE_CAT6, status=CableStatusChoices.STATUS_PLANNED, color='e91e63', length=20, length_unit=CableLengthUnitChoices.UNIT_METER).save()
+        Cable(termination_a=interfaces[1], termination_b=interfaces[2], label='Cable 1', type=CableTypeChoices.TYPE_CAT3, status=LinkStatusChoices.STATUS_CONNECTED, color='aa1409', length=10, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
+        Cable(termination_a=interfaces[3], termination_b=interfaces[4], label='Cable 2', type=CableTypeChoices.TYPE_CAT3, status=LinkStatusChoices.STATUS_CONNECTED, color='aa1409', length=20, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
+        Cable(termination_a=interfaces[5], termination_b=interfaces[6], label='Cable 3', type=CableTypeChoices.TYPE_CAT5E, status=LinkStatusChoices.STATUS_CONNECTED, color='f44336', length=30, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
+        Cable(termination_a=interfaces[7], termination_b=interfaces[8], label='Cable 4', type=CableTypeChoices.TYPE_CAT5E, status=LinkStatusChoices.STATUS_PLANNED, color='f44336', length=40, length_unit=CableLengthUnitChoices.UNIT_FOOT).save()
+        Cable(termination_a=interfaces[9], termination_b=interfaces[10], label='Cable 5', type=CableTypeChoices.TYPE_CAT6, status=LinkStatusChoices.STATUS_PLANNED, color='e91e63', length=10, length_unit=CableLengthUnitChoices.UNIT_METER).save()
+        Cable(termination_a=interfaces[11], termination_b=interfaces[0], label='Cable 6', type=CableTypeChoices.TYPE_CAT6, status=LinkStatusChoices.STATUS_PLANNED, color='e91e63', length=20, length_unit=CableLengthUnitChoices.UNIT_METER).save()
         Cable(termination_a=console_port, termination_b=console_server_port, label='Cable 7').save()
         Cable(termination_a=console_port, termination_b=console_server_port, label='Cable 7').save()
 
 
     def test_label(self):
     def test_label(self):
@@ -2880,9 +2880,9 @@ class CableTestCase(TestCase, ChangeLoggedFilterSetTests):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
 
 
     def test_status(self):
     def test_status(self):
-        params = {'status': [CableStatusChoices.STATUS_CONNECTED]}
+        params = {'status': [LinkStatusChoices.STATUS_CONNECTED]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
-        params = {'status': [CableStatusChoices.STATUS_PLANNED]}
+        params = {'status': [LinkStatusChoices.STATUS_PLANNED]}
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
 
 
     def test_color(self):
     def test_color(self):

+ 2 - 2
netbox/dcim/tests/test_views.py

@@ -1813,7 +1813,7 @@ class CableTestCase(
             'termination_b_type': interface_ct.pk,
             'termination_b_type': interface_ct.pk,
             'termination_b_id': interfaces[3].pk,
             'termination_b_id': interfaces[3].pk,
             'type': CableTypeChoices.TYPE_CAT6,
             'type': CableTypeChoices.TYPE_CAT6,
-            'status': CableStatusChoices.STATUS_PLANNED,
+            'status': LinkStatusChoices.STATUS_PLANNED,
             'label': 'Label',
             'label': 'Label',
             'color': 'c0c0c0',
             'color': 'c0c0c0',
             'length': 100,
             'length': 100,
@@ -1830,7 +1830,7 @@ class CableTestCase(
 
 
         cls.bulk_edit_data = {
         cls.bulk_edit_data = {
             'type': CableTypeChoices.TYPE_CAT5E,
             'type': CableTypeChoices.TYPE_CAT5E,
-            'status': CableStatusChoices.STATUS_CONNECTED,
+            'status': LinkStatusChoices.STATUS_CONNECTED,
             'label': 'New label',
             'label': 'New label',
             'color': '00ff00',
             'color': '00ff00',
             'length': 50,
             'length': 50,

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

@@ -15,6 +15,10 @@
         <h5 class="card-header">Link Properties</h5>
         <h5 class="card-header">Link Properties</h5>
         <div class="card-body">
         <div class="card-body">
           <table class="table table-hover attr-table">
           <table class="table table-hover attr-table">
+            <tr>
+              <th scope="row">Status</th>
+              <td>{{ object.get_status_display }}</td>
+            </tr>
             <tr>
             <tr>
               <th scope="row">SSID</th>
               <th scope="row">SSID</th>
               <td>{{ object.ssid|placeholder }}</td>
               <td>{{ object.ssid|placeholder }}</td>

+ 4 - 1
netbox/wireless/api/serializers.py

@@ -1,7 +1,9 @@
 from rest_framework import serializers
 from rest_framework import serializers
 
 
+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.serializers import PrimaryModelSerializer
 from netbox.api.serializers import PrimaryModelSerializer
 from wireless.models import *
 from wireless.models import *
 from .nested_serializers import *
 from .nested_serializers import *
@@ -25,11 +27,12 @@ class WirelessLANSerializer(PrimaryModelSerializer):
 
 
 class WirelessLinkSerializer(PrimaryModelSerializer):
 class WirelessLinkSerializer(PrimaryModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslink-detail')
     url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslink-detail')
+    status = ChoiceField(choices=LinkStatusChoices, required=False)
     interface_a = NestedInterfaceSerializer()
     interface_a = NestedInterfaceSerializer()
     interface_b = NestedInterfaceSerializer()
     interface_b = NestedInterfaceSerializer()
 
 
     class Meta:
     class Meta:
         model = WirelessLink
         model = WirelessLink
         fields = [
         fields = [
-            'id', 'url', 'display', 'interface_a', 'interface_b', 'ssid', 'description',
+            'id', 'url', 'display', 'interface_a', 'interface_b', 'ssid', 'status', 'description',
         ]
         ]

+ 4 - 0
netbox/wireless/filtersets.py

@@ -1,6 +1,7 @@
 import django_filters
 import django_filters
 from django.db.models import Q
 from django.db.models import Q
 
 
+from dcim.choices import LinkStatusChoices
 from extras.filters import TagFilter
 from extras.filters import TagFilter
 from netbox.filtersets import PrimaryModelFilterSet
 from netbox.filtersets import PrimaryModelFilterSet
 from .models import *
 from .models import *
@@ -37,6 +38,9 @@ class WirelessLinkFilterSet(PrimaryModelFilterSet):
         method='search',
         method='search',
         label='Search',
         label='Search',
     )
     )
+    status = django_filters.MultipleChoiceFilter(
+        choices=LinkStatusChoices
+    )
     tag = TagFilter()
     tag = TagFilter()
 
 
     class Meta:
     class Meta:

+ 8 - 3
netbox/wireless/forms/bulk_edit.py

@@ -1,10 +1,11 @@
 from django import forms
 from django import forms
 
 
-from dcim.models import *
+from dcim.choices import LinkStatusChoices
 from extras.forms import AddRemoveTagsForm, CustomFieldModelBulkEditForm
 from extras.forms import AddRemoveTagsForm, CustomFieldModelBulkEditForm
 from ipam.models import VLAN
 from ipam.models import VLAN
 from utilities.forms import BootstrapMixin, DynamicModelChoiceField
 from utilities.forms import BootstrapMixin, DynamicModelChoiceField
 from wireless.constants import SSID_MAX_LENGTH
 from wireless.constants import SSID_MAX_LENGTH
+from wireless.models import *
 
 
 __all__ = (
 __all__ = (
     'WirelessLANBulkEditForm',
     'WirelessLANBulkEditForm',
@@ -14,7 +15,7 @@ __all__ = (
 
 
 class WirelessLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkEditForm):
 class WirelessLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkEditForm):
     pk = forms.ModelMultipleChoiceField(
     pk = forms.ModelMultipleChoiceField(
-        queryset=PowerFeed.objects.all(),
+        queryset=WirelessLAN.objects.all(),
         widget=forms.MultipleHiddenInput
         widget=forms.MultipleHiddenInput
     )
     )
     vlan = DynamicModelChoiceField(
     vlan = DynamicModelChoiceField(
@@ -35,13 +36,17 @@ class WirelessLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldMode
 
 
 class WirelessLinkBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkEditForm):
 class WirelessLinkBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkEditForm):
     pk = forms.ModelMultipleChoiceField(
     pk = forms.ModelMultipleChoiceField(
-        queryset=PowerFeed.objects.all(),
+        queryset=WirelessLink.objects.all(),
         widget=forms.MultipleHiddenInput
         widget=forms.MultipleHiddenInput
     )
     )
     ssid = forms.CharField(
     ssid = forms.CharField(
         max_length=SSID_MAX_LENGTH,
         max_length=SSID_MAX_LENGTH,
         required=False
         required=False
     )
     )
+    status = forms.ChoiceField(
+        choices=LinkStatusChoices,
+        required=False
+    )
     description = forms.CharField(
     description = forms.CharField(
         required=False
         required=False
     )
     )

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

@@ -1,7 +1,8 @@
+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 CSVModelChoiceField
+from utilities.forms import CSVChoiceField, CSVModelChoiceField
 from wireless.models import *
 from wireless.models import *
 
 
 __all__ = (
 __all__ = (
@@ -23,6 +24,10 @@ class WirelessLANCSVForm(CustomFieldModelCSVForm):
 
 
 
 
 class WirelessLinkCSVForm(CustomFieldModelCSVForm):
 class WirelessLinkCSVForm(CustomFieldModelCSVForm):
+    status = CSVChoiceField(
+        choices=LinkStatusChoices,
+        help_text='Connection status'
+    )
     interface_a = CSVModelChoiceField(
     interface_a = CSVModelChoiceField(
         queryset=Interface.objects.all()
         queryset=Interface.objects.all()
     )
     )

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

@@ -1,8 +1,9 @@
 from django import forms
 from django import forms
 from django.utils.translation import gettext as _
 from django.utils.translation import gettext as _
 
 
+from dcim.choices import LinkStatusChoices
 from extras.forms import CustomFieldModelFilterForm
 from extras.forms import CustomFieldModelFilterForm
-from utilities.forms import BootstrapMixin, TagFilterField
+from utilities.forms import add_blank_choice, BootstrapMixin, StaticSelect, TagFilterField
 from wireless.models import *
 from wireless.models import *
 
 
 __all__ = (
 __all__ = (
@@ -42,4 +43,9 @@ class WirelessLinkFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
         required=False,
         required=False,
         label='SSID'
         label='SSID'
     )
     )
+    status = forms.ChoiceField(
+        required=False,
+        choices=add_blank_choice(LinkStatusChoices),
+        widget=StaticSelect()
+    )
     tag = TagFilterField(model)
     tag = TagFilterField(model)

+ 5 - 2
netbox/wireless/forms/models.py

@@ -2,7 +2,7 @@ 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
+from utilities.forms import BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, StaticSelect
 from wireless.models import *
 from wireless.models import *
 
 
 __all__ = (
 __all__ = (
@@ -55,5 +55,8 @@ class WirelessLinkForm(BootstrapMixin, CustomFieldModelForm):
     class Meta:
     class Meta:
         model = WirelessLink
         model = WirelessLink
         fields = [
         fields = [
-            'interface_a', 'interface_b', 'ssid', 'description', 'tags',
+            'interface_a', 'interface_b', 'status', 'ssid', 'description', 'tags',
         ]
         ]
+        widgets = {
+            'status': StaticSelect,
+        }

+ 1 - 0
netbox/wireless/migrations/0001_wireless.py

@@ -42,6 +42,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(blank=True, max_length=32)),
                 ('ssid', models.CharField(blank=True, max_length=32)),
+                ('status', models.CharField(default='connected', max_length=50)),
                 ('description', models.CharField(blank=True, max_length=200)),
                 ('description', models.CharField(blank=True, max_length=200)),
                 ('_interface_a_device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.device')),
                 ('_interface_a_device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.device')),
                 ('_interface_b_device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.device')),
                 ('_interface_b_device', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='dcim.device')),

+ 11 - 0
netbox/wireless/models.py

@@ -2,6 +2,7 @@ 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 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, PrimaryModel
@@ -70,6 +71,11 @@ class WirelessLink(PrimaryModel):
         blank=True,
         blank=True,
         verbose_name='SSID'
         verbose_name='SSID'
     )
     )
+    status = models.CharField(
+        max_length=50,
+        choices=LinkStatusChoices,
+        default=LinkStatusChoices.STATUS_CONNECTED
+    )
     description = models.CharField(
     description = models.CharField(
         max_length=200,
         max_length=200,
         blank=True
         blank=True
@@ -94,6 +100,8 @@ class WirelessLink(PrimaryModel):
 
 
     objects = RestrictedQuerySet.as_manager()
     objects = RestrictedQuerySet.as_manager()
 
 
+    clone_fields = ('ssid', 'status')
+
     class Meta:
     class Meta:
         ordering = ['pk']
         ordering = ['pk']
         unique_together = ('interface_a', 'interface_b')
         unique_together = ('interface_a', 'interface_b')
@@ -104,6 +112,9 @@ class WirelessLink(PrimaryModel):
     def get_absolute_url(self):
     def get_absolute_url(self):
         return reverse('wireless:wirelesslink', args=[self.pk])
         return reverse('wireless:wirelesslink', args=[self.pk])
 
 
+    def get_status_class(self):
+        return LinkStatusChoices.CSS_CLASSES.get(self.status)
+
     def clean(self):
     def clean(self):
 
 
         # Validate interface types
         # Validate interface types

+ 4 - 3
netbox/wireless/tables.py

@@ -1,7 +1,7 @@
 import django_tables2 as tables
 import django_tables2 as tables
 
 
 from .models import *
 from .models import *
-from utilities.tables import BaseTable, TagColumn, ToggleColumn
+from utilities.tables import BaseTable, ChoiceFieldColumn, TagColumn, ToggleColumn
 
 
 __all__ = (
 __all__ = (
     'WirelessLANTable',
     'WirelessLANTable',
@@ -30,6 +30,7 @@ class WirelessLinkTable(BaseTable):
         linkify=True,
         linkify=True,
         verbose_name='ID'
         verbose_name='ID'
     )
     )
+    status = ChoiceFieldColumn()
     interface_a = tables.Column(
     interface_a = tables.Column(
         linkify=True
         linkify=True
     )
     )
@@ -42,5 +43,5 @@ class WirelessLinkTable(BaseTable):
 
 
     class Meta(BaseTable.Meta):
     class Meta(BaseTable.Meta):
         model = WirelessLink
         model = WirelessLink
-        fields = ('pk', 'id', 'interface_a', 'interface_b', 'ssid', 'description')
-        default_columns = ('pk', 'id', 'interface_a', 'interface_b', 'ssid', 'description')
+        fields = ('pk', 'id', 'status', 'interface_a', 'interface_b', 'ssid', 'description')
+        default_columns = ('pk', 'id', 'status', 'interface_a', 'interface_b', 'ssid', 'description')