From 3fa4ceadb0d79ebc88aacf69091d4fbdb0101016 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 21 Nov 2019 22:39:15 -0500 Subject: [PATCH] Interface.mode to slug (#3569) --- netbox/dcim/api/serializers.py | 2 +- netbox/dcim/choices.py | 33 ++++++++++++++----- netbox/dcim/constants.py | 8 ----- netbox/dcim/forms.py | 8 ++--- .../migrations/0082_3569_interface_fields.py | 26 +++++++++++++++ netbox/dcim/models.py | 8 ++--- netbox/dcim/tests/test_api.py | 10 +++--- netbox/virtualization/api/serializers.py | 5 ++- netbox/virtualization/forms.py | 11 +++---- netbox/virtualization/tests/test_api.py | 11 +++---- 10 files changed, 76 insertions(+), 46 deletions(-) diff --git a/netbox/dcim/api/serializers.py b/netbox/dcim/api/serializers.py index 7018a8fc9..bd61f2a9b 100644 --- a/netbox/dcim/api/serializers.py +++ b/netbox/dcim/api/serializers.py @@ -469,7 +469,7 @@ class InterfaceSerializer(TaggitSerializer, ConnectedEndpointSerializer): device = NestedDeviceSerializer() type = ChoiceField(choices=InterfaceTypeChoices, required=False) lag = NestedInterfaceSerializer(required=False, allow_null=True) - mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False, allow_null=True) + mode = ChoiceField(choices=InterfaceModeChoices, required=False, allow_null=True) untagged_vlan = NestedVLANSerializer(required=False, allow_null=True) tagged_vlans = SerializedPKRelatedField( queryset=VLAN.objects.all(), diff --git a/netbox/dcim/choices.py b/netbox/dcim/choices.py index 12df4f701..c5c0c7707 100644 --- a/netbox/dcim/choices.py +++ b/netbox/dcim/choices.py @@ -111,9 +111,7 @@ class DeviceFaceChoices(ChoiceSet): # class ConsolePortTypeChoices(ChoiceSet): - """ - ConsolePort/ConsoleServerPort.type slugs - """ + TYPE_DE9 = 'de-9' TYPE_DB25 = 'db-25' TYPE_RJ45 = 'rj-45' @@ -152,6 +150,7 @@ class ConsolePortTypeChoices(ChoiceSet): # class PowerPortTypeChoices(ChoiceSet): + # TODO: Add more power port types # IEC 60320 TYPE_IEC_C6 = 'iec-60320-c6' @@ -239,6 +238,7 @@ class PowerPortTypeChoices(ChoiceSet): # class PowerOutletTypeChoices(ChoiceSet): + # TODO: Add more power outlet types # IEC 60320 TYPE_IEC_C5 = 'iec-60320-c5' @@ -326,9 +326,7 @@ class PowerOutletTypeChoices(ChoiceSet): # class InterfaceTypeChoices(ChoiceSet): - """ - Interface.type slugs - """ + # Virtual TYPE_VIRTUAL = 'virtual' TYPE_LAG = 'lag' @@ -623,14 +621,31 @@ class InterfaceTypeChoices(ChoiceSet): } +class InterfaceModeChoices(ChoiceSet): + + MODE_ACCESS = 'access' + MODE_TAGGED = 'tagged' + MODE_TAGGED_ALL = 'tagged-all' + + CHOICES = ( + (MODE_ACCESS, 'Access'), + (MODE_TAGGED, 'Tagged'), + (MODE_TAGGED_ALL, 'Tagged (All)'), + ) + + LEGACY_MAP = { + MODE_ACCESS: 100, + MODE_TAGGED: 200, + MODE_TAGGED_ALL: 300, + } + + # # FrontPorts/RearPorts # class PortTypeChoices(ChoiceSet): - """ - FrontPort/RearPort.type slugs - """ + TYPE_8P8C = '8p8c' TYPE_110_PUNCH = '110-punch' TYPE_BNC = 'bnc' diff --git a/netbox/dcim/constants.py b/netbox/dcim/constants.py index 4b199cf15..72fec872d 100644 --- a/netbox/dcim/constants.py +++ b/netbox/dcim/constants.py @@ -20,14 +20,6 @@ WIRELESS_IFACE_TYPES = [ NONCONNECTABLE_IFACE_TYPES = VIRTUAL_IFACE_TYPES + WIRELESS_IFACE_TYPES -IFACE_MODE_ACCESS = 100 -IFACE_MODE_TAGGED = 200 -IFACE_MODE_TAGGED_ALL = 300 -IFACE_MODE_CHOICES = [ - [IFACE_MODE_ACCESS, 'Access'], - [IFACE_MODE_TAGGED, 'Tagged'], - [IFACE_MODE_TAGGED_ALL, 'Tagged All'], -] # Pass-through port types PORT_TYPE_8P8C = 1000 diff --git a/netbox/dcim/forms.py b/netbox/dcim/forms.py index 0a4444907..1ea8b2571 100644 --- a/netbox/dcim/forms.py +++ b/netbox/dcim/forms.py @@ -66,13 +66,13 @@ class InterfaceCommonForm: tagged_vlans = self.cleaned_data['tagged_vlans'] # Untagged interfaces cannot be assigned tagged VLANs - if self.cleaned_data['mode'] == IFACE_MODE_ACCESS and tagged_vlans: + if self.cleaned_data['mode'] == InterfaceModeChoices.MODE_ACCESS and tagged_vlans: raise forms.ValidationError({ 'mode': "An access interface cannot have tagged VLANs assigned." }) # Remove all tagged VLAN assignments from "tagged all" interfaces - elif self.cleaned_data['mode'] == IFACE_MODE_TAGGED_ALL: + elif self.cleaned_data['mode'] == InterfaceModeChoices.MODE_TAGGED_ALL: self.cleaned_data['tagged_vlans'] = [] @@ -2450,7 +2450,7 @@ class InterfaceCreateForm(InterfaceCommonForm, ComponentForm, forms.Form): required=False ) mode = forms.ChoiceField( - choices=add_blank_choice(IFACE_MODE_CHOICES), + choices=add_blank_choice(InterfaceModeChoices), required=False, widget=StaticSelect2(), ) @@ -2564,7 +2564,7 @@ class InterfaceBulkEditForm(InterfaceCommonForm, BootstrapMixin, AddRemoveTagsFo required=False ) mode = forms.ChoiceField( - choices=add_blank_choice(IFACE_MODE_CHOICES), + choices=add_blank_choice(InterfaceModeChoices), required=False, widget=StaticSelect2() ) diff --git a/netbox/dcim/migrations/0082_3569_interface_fields.py b/netbox/dcim/migrations/0082_3569_interface_fields.py index d325844fe..b3606d32a 100644 --- a/netbox/dcim/migrations/0082_3569_interface_fields.py +++ b/netbox/dcim/migrations/0082_3569_interface_fields.py @@ -75,6 +75,13 @@ INTERFACE_TYPE_CHOICES = ( ) +INTERFACE_MODE_CHOICES = ( + (100, 'access'), + (200, 'tagged'), + (300, 'tagged-all'), +) + + def interfacetemplate_type_to_slug(apps, schema_editor): InterfaceTemplate = apps.get_model('dcim', 'InterfaceTemplate') for id, slug in INTERFACE_TYPE_CHOICES: @@ -87,6 +94,12 @@ def interface_type_to_slug(apps, schema_editor): Interface.objects.filter(type=id).update(type=slug) +def interface_mode_to_slug(apps, schema_editor): + Interface = apps.get_model('dcim', 'Interface') + for id, slug in INTERFACE_MODE_CHOICES: + Interface.objects.filter(mode=id).update(mode=slug) + + class Migration(migrations.Migration): atomic = False @@ -111,4 +124,17 @@ class Migration(migrations.Migration): migrations.RunPython( code=interface_type_to_slug ), + migrations.AlterField( + model_name='interface', + name='mode', + field=models.CharField(blank=True, default='', max_length=50), + ), + migrations.RunPython( + code=interface_mode_to_slug + ), + migrations.AlterField( + model_name='interface', + name='mode', + field=models.CharField(blank=True, max_length=50), + ), ] diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 4e67f61cc..6ca145d72 100644 --- a/netbox/dcim/models.py +++ b/netbox/dcim/models.py @@ -2261,10 +2261,10 @@ class Interface(CableTermination, ComponentModel): verbose_name='OOB Management', help_text='This interface is used only for out-of-band management' ) - mode = models.PositiveSmallIntegerField( - choices=IFACE_MODE_CHOICES, + mode = models.CharField( + max_length=50, + choices=InterfaceModeChoices, blank=True, - null=True ) untagged_vlan = models.ForeignKey( to='ipam.VLAN', @@ -2373,7 +2373,7 @@ class Interface(CableTermination, ComponentModel): self.untagged_vlan = None # Only "tagged" interfaces may have tagged VLANs assigned. ("tagged all" implies all VLANs are assigned.) - if self.pk and self.mode is not IFACE_MODE_TAGGED: + if self.pk and self.mode is not InterfaceModeChoices.MODE_TAGGED: self.tagged_vlans.clear() return super().save(*args, **kwargs) diff --git a/netbox/dcim/tests/test_api.py b/netbox/dcim/tests/test_api.py index 730639704..17cbe9e6d 100644 --- a/netbox/dcim/tests/test_api.py +++ b/netbox/dcim/tests/test_api.py @@ -3,7 +3,7 @@ from netaddr import IPNetwork from rest_framework import status from circuits.models import Circuit, CircuitTermination, CircuitType, Provider -from dcim.choices import SubdeviceRoleChoices +from dcim.choices import InterfaceModeChoices, SubdeviceRoleChoices from dcim.constants import * from dcim.models import ( Cable, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, DeviceBay, @@ -2474,7 +2474,7 @@ class InterfaceTest(APITestCase): data = { 'device': self.device.pk, 'name': 'Test Interface 4', - 'mode': IFACE_MODE_TAGGED, + 'mode': InterfaceModeChoices.MODE_TAGGED, 'untagged_vlan': self.vlan3.id, 'tagged_vlans': [self.vlan1.id, self.vlan2.id], } @@ -2521,21 +2521,21 @@ class InterfaceTest(APITestCase): { 'device': self.device.pk, 'name': 'Test Interface 4', - 'mode': IFACE_MODE_TAGGED, + 'mode': InterfaceModeChoices.MODE_TAGGED, 'untagged_vlan': self.vlan2.id, 'tagged_vlans': [self.vlan1.id], }, { 'device': self.device.pk, 'name': 'Test Interface 5', - 'mode': IFACE_MODE_TAGGED, + 'mode': InterfaceModeChoices.MODE_TAGGED, 'untagged_vlan': self.vlan2.id, 'tagged_vlans': [self.vlan1.id], }, { 'device': self.device.pk, 'name': 'Test Interface 6', - 'mode': IFACE_MODE_TAGGED, + 'mode': InterfaceModeChoices.MODE_TAGGED, 'untagged_vlan': self.vlan2.id, 'tagged_vlans': [self.vlan1.id], }, diff --git a/netbox/virtualization/api/serializers.py b/netbox/virtualization/api/serializers.py index 4b519e5e2..98cc63226 100644 --- a/netbox/virtualization/api/serializers.py +++ b/netbox/virtualization/api/serializers.py @@ -3,8 +3,7 @@ from rest_framework import serializers from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField from dcim.api.nested_serializers import NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedSiteSerializer -from dcim.choices import InterfaceTypeChoices -from dcim.constants import IFACE_MODE_CHOICES +from dcim.choices import InterfaceModeChoices, InterfaceTypeChoices from dcim.models import Interface from extras.api.customfields import CustomFieldModelSerializer from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer @@ -100,7 +99,7 @@ class VirtualMachineWithConfigContextSerializer(VirtualMachineSerializer): class InterfaceSerializer(TaggitSerializer, ValidatedModelSerializer): virtual_machine = NestedVirtualMachineSerializer() type = ChoiceField(choices=InterfaceTypeChoices, default=InterfaceTypeChoices.TYPE_VIRTUAL, required=False) - mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False, allow_null=True) + mode = ChoiceField(choices=InterfaceModeChoices, required=False, allow_null=True) untagged_vlan = NestedVLANSerializer(required=False, allow_null=True) tagged_vlans = SerializedPKRelatedField( queryset=VLAN.objects.all(), diff --git a/netbox/virtualization/forms.py b/netbox/virtualization/forms.py index 09848e0c7..5bfca6654 100644 --- a/netbox/virtualization/forms.py +++ b/netbox/virtualization/forms.py @@ -2,8 +2,7 @@ from django import forms from django.core.exceptions import ValidationError from taggit.forms import TagField -from dcim.choices import InterfaceTypeChoices -from dcim.constants import IFACE_MODE_ACCESS, IFACE_MODE_TAGGED_ALL, IFACE_MODE_CHOICES +from dcim.choices import InterfaceModeChoices, InterfaceTypeChoices from dcim.forms import INTERFACE_MODE_HELP_TEXT from dcim.models import Device, DeviceRole, Interface, Platform, Rack, Region, Site from extras.forms import AddRemoveTagsForm, CustomFieldBulkEditForm, CustomFieldForm, CustomFieldFilterForm @@ -718,13 +717,13 @@ class InterfaceForm(BootstrapMixin, forms.ModelForm): tagged_vlans = self.cleaned_data['tagged_vlans'] # Untagged interfaces cannot be assigned tagged VLANs - if self.cleaned_data['mode'] == IFACE_MODE_ACCESS and tagged_vlans: + if self.cleaned_data['mode'] == InterfaceModeChoices.MODE_ACCESS and tagged_vlans: raise forms.ValidationError({ 'mode': "An access interface cannot have tagged VLANs assigned." }) # Remove all tagged VLAN assignments from "tagged all" interfaces - elif self.cleaned_data['mode'] == IFACE_MODE_TAGGED_ALL: + elif self.cleaned_data['mode'] == InterfaceModeChoices.MODE_TAGGED_ALL: self.cleaned_data['tagged_vlans'] = [] @@ -755,7 +754,7 @@ class InterfaceCreateForm(ComponentForm): required=False ) mode = forms.ChoiceField( - choices=add_blank_choice(IFACE_MODE_CHOICES), + choices=add_blank_choice(InterfaceModeChoices), required=False, widget=StaticSelect2(), ) @@ -840,7 +839,7 @@ class InterfaceBulkEditForm(BootstrapMixin, BulkEditForm): required=False ) mode = forms.ChoiceField( - choices=add_blank_choice(IFACE_MODE_CHOICES), + choices=add_blank_choice(InterfaceModeChoices), required=False, widget=StaticSelect2() ) diff --git a/netbox/virtualization/tests/test_api.py b/netbox/virtualization/tests/test_api.py index 64d9cf8b9..683a65a2b 100644 --- a/netbox/virtualization/tests/test_api.py +++ b/netbox/virtualization/tests/test_api.py @@ -2,8 +2,7 @@ from django.urls import reverse from netaddr import IPNetwork from rest_framework import status -from dcim.choices import InterfaceTypeChoices -from dcim.constants import IFACE_MODE_TAGGED +from dcim.choices import InterfaceModeChoices, InterfaceTypeChoices from dcim.models import Interface from ipam.models import IPAddress, VLAN from utilities.testing import APITestCase @@ -552,7 +551,7 @@ class InterfaceTest(APITestCase): data = { 'virtual_machine': self.virtualmachine.pk, 'name': 'Test Interface 4', - 'mode': IFACE_MODE_TAGGED, + 'mode': InterfaceModeChoices.MODE_TAGGED, 'untagged_vlan': self.vlan3.id, 'tagged_vlans': [self.vlan1.id, self.vlan2.id], } @@ -599,21 +598,21 @@ class InterfaceTest(APITestCase): { 'virtual_machine': self.virtualmachine.pk, 'name': 'Test Interface 4', - 'mode': IFACE_MODE_TAGGED, + 'mode': InterfaceModeChoices.MODE_TAGGED, 'untagged_vlan': self.vlan2.id, 'tagged_vlans': [self.vlan1.id], }, { 'virtual_machine': self.virtualmachine.pk, 'name': 'Test Interface 5', - 'mode': IFACE_MODE_TAGGED, + 'mode': InterfaceModeChoices.MODE_TAGGED, 'untagged_vlan': self.vlan2.id, 'tagged_vlans': [self.vlan1.id], }, { 'virtual_machine': self.virtualmachine.pk, 'name': 'Test Interface 6', - 'mode': IFACE_MODE_TAGGED, + 'mode': InterfaceModeChoices.MODE_TAGGED, 'untagged_vlan': self.vlan2.id, 'tagged_vlans': [self.vlan1.id], },