Browse Source

Add status field to WirelessLink

jeremystretch 4 years ago
parent
commit
95ed07a95e

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

@@ -757,7 +757,7 @@ class CableSerializer(PrimaryModelSerializer):
     )
     termination_a = 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)
 
     class Meta:

+ 2 - 2
netbox/dcim/choices.py

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

+ 1 - 1
netbox/dcim/filtersets.py

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

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

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

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

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

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

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

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

@@ -64,8 +64,8 @@ class Cable(PrimaryModel):
     )
     status = models.CharField(
         max_length=50,
-        choices=CableStatusChoices,
-        default=CableStatusChoices.STATUS_CONNECTED
+        choices=LinkStatusChoices,
+        default=LinkStatusChoices.STATUS_CONNECTED
     )
     label = models.CharField(
         max_length=100,
@@ -285,7 +285,7 @@ class Cable(PrimaryModel):
         self._pk = self.pk
 
     def get_status_class(self):
-        return CableStatusChoices.CSS_CLASSES.get(self.status)
+        return LinkStatusChoices.CSS_CLASSES.get(self.status)
 
     def get_compatible_types(self):
         """
@@ -390,7 +390,7 @@ class CablePath(BigIDModel):
 
         node = origin
         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
 
             # 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.dispatch import receiver
 
-from .choices import CableStatusChoices
+from .choices import LinkStatusChoices
 from .models import Cable, CablePath, Device, PathEndpoint, PowerPanel, Rack, Location, VirtualChassis
 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
         # may change in the future.) However, we do need to capture status changes and update
         # 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)
         else:
             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 circuits.models import *
-from dcim.choices import CableStatusChoices
+from dcim.choices import LinkStatusChoices
 from dcim.models import *
 from dcim.utils import object_to_path_node
 
@@ -1142,7 +1142,7 @@ class CablePathTestCase(TestCase):
         self.assertEqual(CablePath.objects.count(), 2)
 
         # Change cable 2's status to "planned"
-        cable2.status = CableStatusChoices.STATUS_PLANNED
+        cable2.status = LinkStatusChoices.STATUS_PLANNED
         cable2.save()
         self.assertPathExists(
             origin=interface1,
@@ -1160,7 +1160,7 @@ class CablePathTestCase(TestCase):
 
         # Change cable 2's status to "connected"
         cable2 = Cable.objects.get(pk=cable2.pk)
-        cable2.status = CableStatusChoices.STATUS_CONNECTED
+        cable2.status = LinkStatusChoices.STATUS_CONNECTED
         cable2.save()
         self.assertPathExists(
             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')
 
         # 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()
 
     def test_label(self):
@@ -2880,9 +2880,9 @@ class CableTestCase(TestCase, ChangeLoggedFilterSetTests):
         self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
 
     def test_status(self):
-        params = {'status': [CableStatusChoices.STATUS_CONNECTED]}
+        params = {'status': [LinkStatusChoices.STATUS_CONNECTED]}
         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)
 
     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_id': interfaces[3].pk,
             'type': CableTypeChoices.TYPE_CAT6,
-            'status': CableStatusChoices.STATUS_PLANNED,
+            'status': LinkStatusChoices.STATUS_PLANNED,
             'label': 'Label',
             'color': 'c0c0c0',
             'length': 100,
@@ -1830,7 +1830,7 @@ class CableTestCase(
 
         cls.bulk_edit_data = {
             'type': CableTypeChoices.TYPE_CAT5E,
-            'status': CableStatusChoices.STATUS_CONNECTED,
+            'status': LinkStatusChoices.STATUS_CONNECTED,
             'label': 'New label',
             'color': '00ff00',
             'length': 50,

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

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

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

@@ -1,7 +1,9 @@
 from rest_framework import serializers
 
+from dcim.choices import LinkStatusChoices
 from dcim.api.serializers import NestedInterfaceSerializer
 from ipam.api.serializers import NestedVLANSerializer
+from netbox.api import ChoiceField
 from netbox.api.serializers import PrimaryModelSerializer
 from wireless.models import *
 from .nested_serializers import *
@@ -25,11 +27,12 @@ class WirelessLANSerializer(PrimaryModelSerializer):
 
 class WirelessLinkSerializer(PrimaryModelSerializer):
     url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslink-detail')
+    status = ChoiceField(choices=LinkStatusChoices, required=False)
     interface_a = NestedInterfaceSerializer()
     interface_b = NestedInterfaceSerializer()
 
     class Meta:
         model = WirelessLink
         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
 from django.db.models import Q
 
+from dcim.choices import LinkStatusChoices
 from extras.filters import TagFilter
 from netbox.filtersets import PrimaryModelFilterSet
 from .models import *
@@ -37,6 +38,9 @@ class WirelessLinkFilterSet(PrimaryModelFilterSet):
         method='search',
         label='Search',
     )
+    status = django_filters.MultipleChoiceFilter(
+        choices=LinkStatusChoices
+    )
     tag = TagFilter()
 
     class Meta:

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

@@ -1,10 +1,11 @@
 from django import forms
 
-from dcim.models import *
+from dcim.choices import LinkStatusChoices
 from extras.forms import AddRemoveTagsForm, CustomFieldModelBulkEditForm
 from ipam.models import VLAN
 from utilities.forms import BootstrapMixin, DynamicModelChoiceField
 from wireless.constants import SSID_MAX_LENGTH
+from wireless.models import *
 
 __all__ = (
     'WirelessLANBulkEditForm',
@@ -14,7 +15,7 @@ __all__ = (
 
 class WirelessLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkEditForm):
     pk = forms.ModelMultipleChoiceField(
-        queryset=PowerFeed.objects.all(),
+        queryset=WirelessLAN.objects.all(),
         widget=forms.MultipleHiddenInput
     )
     vlan = DynamicModelChoiceField(
@@ -35,13 +36,17 @@ class WirelessLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldMode
 
 class WirelessLinkBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkEditForm):
     pk = forms.ModelMultipleChoiceField(
-        queryset=PowerFeed.objects.all(),
+        queryset=WirelessLink.objects.all(),
         widget=forms.MultipleHiddenInput
     )
     ssid = forms.CharField(
         max_length=SSID_MAX_LENGTH,
         required=False
     )
+    status = forms.ChoiceField(
+        choices=LinkStatusChoices,
+        required=False
+    )
     description = forms.CharField(
         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 extras.forms import CustomFieldModelCSVForm
 from ipam.models import VLAN
-from utilities.forms import CSVModelChoiceField
+from utilities.forms import CSVChoiceField, CSVModelChoiceField
 from wireless.models import *
 
 __all__ = (
@@ -23,6 +24,10 @@ class WirelessLANCSVForm(CustomFieldModelCSVForm):
 
 
 class WirelessLinkCSVForm(CustomFieldModelCSVForm):
+    status = CSVChoiceField(
+        choices=LinkStatusChoices,
+        help_text='Connection status'
+    )
     interface_a = CSVModelChoiceField(
         queryset=Interface.objects.all()
     )

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

@@ -1,8 +1,9 @@
 from django import forms
 from django.utils.translation import gettext as _
 
+from dcim.choices import LinkStatusChoices
 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 *
 
 __all__ = (
@@ -42,4 +43,9 @@ class WirelessLinkFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
         required=False,
         label='SSID'
     )
+    status = forms.ChoiceField(
+        required=False,
+        choices=add_blank_choice(LinkStatusChoices),
+        widget=StaticSelect()
+    )
     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.models import Tag
 from ipam.models import VLAN
-from utilities.forms import BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField
+from utilities.forms import BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, StaticSelect
 from wireless.models import *
 
 __all__ = (
@@ -55,5 +55,8 @@ class WirelessLinkForm(BootstrapMixin, CustomFieldModelForm):
     class Meta:
         model = WirelessLink
         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)),
                 ('id', models.BigAutoField(primary_key=True, serialize=False)),
                 ('ssid', models.CharField(blank=True, max_length=32)),
+                ('status', models.CharField(default='connected', max_length=50)),
                 ('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_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.urls import reverse
 
+from dcim.choices import LinkStatusChoices
 from dcim.constants import WIRELESS_IFACE_TYPES
 from extras.utils import extras_features
 from netbox.models import BigIDModel, PrimaryModel
@@ -70,6 +71,11 @@ class WirelessLink(PrimaryModel):
         blank=True,
         verbose_name='SSID'
     )
+    status = models.CharField(
+        max_length=50,
+        choices=LinkStatusChoices,
+        default=LinkStatusChoices.STATUS_CONNECTED
+    )
     description = models.CharField(
         max_length=200,
         blank=True
@@ -94,6 +100,8 @@ class WirelessLink(PrimaryModel):
 
     objects = RestrictedQuerySet.as_manager()
 
+    clone_fields = ('ssid', 'status')
+
     class Meta:
         ordering = ['pk']
         unique_together = ('interface_a', 'interface_b')
@@ -104,6 +112,9 @@ class WirelessLink(PrimaryModel):
     def get_absolute_url(self):
         return reverse('wireless:wirelesslink', args=[self.pk])
 
+    def get_status_class(self):
+        return LinkStatusChoices.CSS_CLASSES.get(self.status)
+
     def clean(self):
 
         # Validate interface types

+ 4 - 3
netbox/wireless/tables.py

@@ -1,7 +1,7 @@
 import django_tables2 as tables
 
 from .models import *
-from utilities.tables import BaseTable, TagColumn, ToggleColumn
+from utilities.tables import BaseTable, ChoiceFieldColumn, TagColumn, ToggleColumn
 
 __all__ = (
     'WirelessLANTable',
@@ -30,6 +30,7 @@ class WirelessLinkTable(BaseTable):
         linkify=True,
         verbose_name='ID'
     )
+    status = ChoiceFieldColumn()
     interface_a = tables.Column(
         linkify=True
     )
@@ -42,5 +43,5 @@ class WirelessLinkTable(BaseTable):
 
     class Meta(BaseTable.Meta):
         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')