mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-14 01:41:22 -06:00
Add status field to WirelessLink
This commit is contained in:
parent
ec0560a2c5
commit
95ed07a95e
@ -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:
|
||||
|
@ -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'
|
||||
|
@ -1205,7 +1205,7 @@ class CableFilterSet(PrimaryModelFilterSet):
|
||||
choices=CableTypeChoices
|
||||
)
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
choices=CableStatusChoices
|
||||
choices=LinkStatusChoices
|
||||
)
|
||||
color = django_filters.MultipleChoiceFilter(
|
||||
choices=ColorChoices
|
||||
|
@ -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=''
|
||||
|
@ -807,7 +807,7 @@ class CableCSVForm(CustomFieldModelCSVForm):
|
||||
|
||||
# Cable attributes
|
||||
status = CSVChoiceField(
|
||||
choices=CableStatusChoices,
|
||||
choices=LinkStatusChoices,
|
||||
required=False,
|
||||
help_text='Connection status'
|
||||
)
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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):
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
|
@ -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',
|
||||
]
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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()
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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')),
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user