Jeremy Stretch 6 лет назад
Родитель
Сommit
5f5081f719

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

@@ -257,7 +257,7 @@ class PowerOutletTemplateSerializer(ValidatedModelSerializer):
 
 class InterfaceTemplateSerializer(ValidatedModelSerializer):
     device_type = NestedDeviceTypeSerializer()
-    type = ChoiceField(choices=IFACE_TYPE_CHOICES, required=False)
+    type = ChoiceField(choices=InterfaceTypeChoices, required=False)
 
     class Meta:
         model = InterfaceTemplate
@@ -467,7 +467,7 @@ class PowerPortSerializer(TaggitSerializer, ConnectedEndpointSerializer):
 
 class InterfaceSerializer(TaggitSerializer, ConnectedEndpointSerializer):
     device = NestedDeviceSerializer()
-    type = ChoiceField(choices=IFACE_TYPE_CHOICES, required=False)
+    type = ChoiceField(choices=InterfaceTypeChoices, required=False)
     lag = NestedInterfaceSerializer(required=False, allow_null=True)
     mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False, allow_null=True)
     untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)

+ 5 - 5
netbox/dcim/choices.py

@@ -107,7 +107,7 @@ class DeviceFaceChoices(ChoiceSet):
 
 
 #
-# Console port type values
+# ConsolePorts
 #
 
 class ConsolePortTypeChoices(ChoiceSet):
@@ -148,7 +148,7 @@ class ConsolePortTypeChoices(ChoiceSet):
 
 
 #
-# Power port types
+# PowerPorts
 #
 
 class PowerPortTypeChoices(ChoiceSet):
@@ -235,7 +235,7 @@ class PowerPortTypeChoices(ChoiceSet):
 
 
 #
-# Power outlet types
+# PowerOutlets
 #
 
 class PowerOutletTypeChoices(ChoiceSet):
@@ -322,7 +322,7 @@ class PowerOutletTypeChoices(ChoiceSet):
 
 
 #
-# Interface type values
+# Interfaces
 #
 
 class InterfaceTypeChoices(ChoiceSet):
@@ -624,7 +624,7 @@ class InterfaceTypeChoices(ChoiceSet):
 
 
 #
-# Port type values
+# FrontPorts/RearPorts
 #
 
 class PortTypeChoices(ChoiceSet):

+ 11 - 220
netbox/dcim/constants.py

@@ -1,230 +1,21 @@
-#
-# Numeric interface types
-#
+from .choices import InterfaceTypeChoices
 
-# Virtual
-IFACE_TYPE_VIRTUAL = 0
-IFACE_TYPE_LAG = 200
-# Ethernet
-IFACE_TYPE_100ME_FIXED = 800
-IFACE_TYPE_1GE_FIXED = 1000
-IFACE_TYPE_1GE_GBIC = 1050
-IFACE_TYPE_1GE_SFP = 1100
-IFACE_TYPE_2GE_FIXED = 1120
-IFACE_TYPE_5GE_FIXED = 1130
-IFACE_TYPE_10GE_FIXED = 1150
-IFACE_TYPE_10GE_CX4 = 1170
-IFACE_TYPE_10GE_SFP_PLUS = 1200
-IFACE_TYPE_10GE_XFP = 1300
-IFACE_TYPE_10GE_XENPAK = 1310
-IFACE_TYPE_10GE_X2 = 1320
-IFACE_TYPE_25GE_SFP28 = 1350
-IFACE_TYPE_40GE_QSFP_PLUS = 1400
-IFACE_TYPE_50GE_QSFP28 = 1420
-IFACE_TYPE_100GE_CFP = 1500
-IFACE_TYPE_100GE_CFP2 = 1510
-IFACE_TYPE_100GE_CFP4 = 1520
-IFACE_TYPE_100GE_CPAK = 1550
-IFACE_TYPE_100GE_QSFP28 = 1600
-IFACE_TYPE_200GE_CFP2 = 1650
-IFACE_TYPE_200GE_QSFP56 = 1700
-IFACE_TYPE_400GE_QSFP_DD = 1750
-IFACE_TYPE_400GE_OSFP = 1800
-# Wireless
-IFACE_TYPE_80211A = 2600
-IFACE_TYPE_80211G = 2610
-IFACE_TYPE_80211N = 2620
-IFACE_TYPE_80211AC = 2630
-IFACE_TYPE_80211AD = 2640
-# Cellular
-IFACE_TYPE_GSM = 2810
-IFACE_TYPE_CDMA = 2820
-IFACE_TYPE_LTE = 2830
-# SONET
-IFACE_TYPE_SONET_OC3 = 6100
-IFACE_TYPE_SONET_OC12 = 6200
-IFACE_TYPE_SONET_OC48 = 6300
-IFACE_TYPE_SONET_OC192 = 6400
-IFACE_TYPE_SONET_OC768 = 6500
-IFACE_TYPE_SONET_OC1920 = 6600
-IFACE_TYPE_SONET_OC3840 = 6700
-# Fibrechannel
-IFACE_TYPE_1GFC_SFP = 3010
-IFACE_TYPE_2GFC_SFP = 3020
-IFACE_TYPE_4GFC_SFP = 3040
-IFACE_TYPE_8GFC_SFP_PLUS = 3080
-IFACE_TYPE_16GFC_SFP_PLUS = 3160
-IFACE_TYPE_32GFC_SFP28 = 3320
-IFACE_TYPE_128GFC_QSFP28 = 3400
-# InfiniBand
-IFACE_TYPE_INFINIBAND_SDR = 7010
-IFACE_TYPE_INFINIBAND_DDR = 7020
-IFACE_TYPE_INFINIBAND_QDR = 7030
-IFACE_TYPE_INFINIBAND_FDR10 = 7040
-IFACE_TYPE_INFINIBAND_FDR = 7050
-IFACE_TYPE_INFINIBAND_EDR = 7060
-IFACE_TYPE_INFINIBAND_HDR = 7070
-IFACE_TYPE_INFINIBAND_NDR = 7080
-IFACE_TYPE_INFINIBAND_XDR = 7090
-# Serial
-IFACE_TYPE_T1 = 4000
-IFACE_TYPE_E1 = 4010
-IFACE_TYPE_T3 = 4040
-IFACE_TYPE_E3 = 4050
-# Stacking
-IFACE_TYPE_STACKWISE = 5000
-IFACE_TYPE_STACKWISE_PLUS = 5050
-IFACE_TYPE_FLEXSTACK = 5100
-IFACE_TYPE_FLEXSTACK_PLUS = 5150
-IFACE_TYPE_JUNIPER_VCP = 5200
-IFACE_TYPE_SUMMITSTACK = 5300
-IFACE_TYPE_SUMMITSTACK128 = 5310
-IFACE_TYPE_SUMMITSTACK256 = 5320
-IFACE_TYPE_SUMMITSTACK512 = 5330
 
-# Other
-IFACE_TYPE_OTHER = 32767
-
-IFACE_TYPE_CHOICES = [
-    [
-        'Virtual interfaces',
-        [
-            [IFACE_TYPE_VIRTUAL, 'Virtual'],
-            [IFACE_TYPE_LAG, 'Link Aggregation Group (LAG)'],
-        ],
-    ],
-    [
-        'Ethernet (fixed)',
-        [
-            [IFACE_TYPE_100ME_FIXED, '100BASE-TX (10/100ME)'],
-            [IFACE_TYPE_1GE_FIXED, '1000BASE-T (1GE)'],
-            [IFACE_TYPE_2GE_FIXED, '2.5GBASE-T (2.5GE)'],
-            [IFACE_TYPE_5GE_FIXED, '5GBASE-T (5GE)'],
-            [IFACE_TYPE_10GE_FIXED, '10GBASE-T (10GE)'],
-            [IFACE_TYPE_10GE_CX4, '10GBASE-CX4 (10GE)'],
-        ]
-    ],
-    [
-        'Ethernet (modular)',
-        [
-            [IFACE_TYPE_1GE_GBIC, 'GBIC (1GE)'],
-            [IFACE_TYPE_1GE_SFP, 'SFP (1GE)'],
-            [IFACE_TYPE_10GE_SFP_PLUS, 'SFP+ (10GE)'],
-            [IFACE_TYPE_10GE_XFP, 'XFP (10GE)'],
-            [IFACE_TYPE_10GE_XENPAK, 'XENPAK (10GE)'],
-            [IFACE_TYPE_10GE_X2, 'X2 (10GE)'],
-            [IFACE_TYPE_25GE_SFP28, 'SFP28 (25GE)'],
-            [IFACE_TYPE_40GE_QSFP_PLUS, 'QSFP+ (40GE)'],
-            [IFACE_TYPE_50GE_QSFP28, 'QSFP28 (50GE)'],
-            [IFACE_TYPE_100GE_CFP, 'CFP (100GE)'],
-            [IFACE_TYPE_100GE_CFP2, 'CFP2 (100GE)'],
-            [IFACE_TYPE_200GE_CFP2, 'CFP2 (200GE)'],
-            [IFACE_TYPE_100GE_CFP4, 'CFP4 (100GE)'],
-            [IFACE_TYPE_100GE_CPAK, 'Cisco CPAK (100GE)'],
-            [IFACE_TYPE_100GE_QSFP28, 'QSFP28 (100GE)'],
-            [IFACE_TYPE_200GE_QSFP56, 'QSFP56 (200GE)'],
-            [IFACE_TYPE_400GE_QSFP_DD, 'QSFP-DD (400GE)'],
-            [IFACE_TYPE_400GE_OSFP, 'OSFP (400GE)'],
-        ]
-    ],
-    [
-        'Wireless',
-        [
-            [IFACE_TYPE_80211A, 'IEEE 802.11a'],
-            [IFACE_TYPE_80211G, 'IEEE 802.11b/g'],
-            [IFACE_TYPE_80211N, 'IEEE 802.11n'],
-            [IFACE_TYPE_80211AC, 'IEEE 802.11ac'],
-            [IFACE_TYPE_80211AD, 'IEEE 802.11ad'],
-        ]
-    ],
-    [
-        'Cellular',
-        [
-            [IFACE_TYPE_GSM, 'GSM'],
-            [IFACE_TYPE_CDMA, 'CDMA'],
-            [IFACE_TYPE_LTE, 'LTE'],
-        ]
-    ],
-    [
-        'SONET',
-        [
-            [IFACE_TYPE_SONET_OC3, 'OC-3/STM-1'],
-            [IFACE_TYPE_SONET_OC12, 'OC-12/STM-4'],
-            [IFACE_TYPE_SONET_OC48, 'OC-48/STM-16'],
-            [IFACE_TYPE_SONET_OC192, 'OC-192/STM-64'],
-            [IFACE_TYPE_SONET_OC768, 'OC-768/STM-256'],
-            [IFACE_TYPE_SONET_OC1920, 'OC-1920/STM-640'],
-            [IFACE_TYPE_SONET_OC3840, 'OC-3840/STM-1234'],
-        ]
-    ],
-    [
-        'FibreChannel',
-        [
-            [IFACE_TYPE_1GFC_SFP, 'SFP (1GFC)'],
-            [IFACE_TYPE_2GFC_SFP, 'SFP (2GFC)'],
-            [IFACE_TYPE_4GFC_SFP, 'SFP (4GFC)'],
-            [IFACE_TYPE_8GFC_SFP_PLUS, 'SFP+ (8GFC)'],
-            [IFACE_TYPE_16GFC_SFP_PLUS, 'SFP+ (16GFC)'],
-            [IFACE_TYPE_32GFC_SFP28, 'SFP28 (32GFC)'],
-            [IFACE_TYPE_128GFC_QSFP28, 'QSFP28 (128GFC)'],
-        ]
-    ],
-    [
-        'InfiniBand',
-        [
-            [IFACE_TYPE_INFINIBAND_SDR, 'SDR (2 Gbps)'],
-            [IFACE_TYPE_INFINIBAND_DDR, 'DDR (4 Gbps)'],
-            [IFACE_TYPE_INFINIBAND_QDR, 'QDR (8 Gbps)'],
-            [IFACE_TYPE_INFINIBAND_FDR10, 'FDR10 (10 Gbps)'],
-            [IFACE_TYPE_INFINIBAND_FDR, 'FDR (13.5 Gbps)'],
-            [IFACE_TYPE_INFINIBAND_EDR, 'EDR (25 Gbps)'],
-            [IFACE_TYPE_INFINIBAND_HDR, 'HDR (50 Gbps)'],
-            [IFACE_TYPE_INFINIBAND_NDR, 'NDR (100 Gbps)'],
-            [IFACE_TYPE_INFINIBAND_XDR, 'XDR (250 Gbps)'],
-        ]
-    ],
-    [
-        'Serial',
-        [
-            [IFACE_TYPE_T1, 'T1 (1.544 Mbps)'],
-            [IFACE_TYPE_E1, 'E1 (2.048 Mbps)'],
-            [IFACE_TYPE_T3, 'T3 (45 Mbps)'],
-            [IFACE_TYPE_E3, 'E3 (34 Mbps)'],
-        ]
-    ],
-    [
-        'Stacking',
-        [
-            [IFACE_TYPE_STACKWISE, 'Cisco StackWise'],
-            [IFACE_TYPE_STACKWISE_PLUS, 'Cisco StackWise Plus'],
-            [IFACE_TYPE_FLEXSTACK, 'Cisco FlexStack'],
-            [IFACE_TYPE_FLEXSTACK_PLUS, 'Cisco FlexStack Plus'],
-            [IFACE_TYPE_JUNIPER_VCP, 'Juniper VCP'],
-            [IFACE_TYPE_SUMMITSTACK, 'Extreme SummitStack'],
-            [IFACE_TYPE_SUMMITSTACK128, 'Extreme SummitStack-128'],
-            [IFACE_TYPE_SUMMITSTACK256, 'Extreme SummitStack-256'],
-            [IFACE_TYPE_SUMMITSTACK512, 'Extreme SummitStack-512'],
-        ]
-    ],
-    [
-        'Other',
-        [
-            [IFACE_TYPE_OTHER, 'Other'],
-        ]
-    ],
-]
+#
+# Interface type groups
+#
 
 VIRTUAL_IFACE_TYPES = [
-    IFACE_TYPE_VIRTUAL,
-    IFACE_TYPE_LAG,
+    InterfaceTypeChoices.TYPE_VIRTUAL,
+    InterfaceTypeChoices.TYPE_LAG,
 ]
 
 WIRELESS_IFACE_TYPES = [
-    IFACE_TYPE_80211A,
-    IFACE_TYPE_80211G,
-    IFACE_TYPE_80211N,
-    IFACE_TYPE_80211AC,
-    IFACE_TYPE_80211AD,
+    InterfaceTypeChoices.TYPE_80211A,
+    InterfaceTypeChoices.TYPE_80211G,
+    InterfaceTypeChoices.TYPE_80211N,
+    InterfaceTypeChoices.TYPE_80211AC,
+    InterfaceTypeChoices.TYPE_80211AD,
 ]
 
 NONCONNECTABLE_IFACE_TYPES = VIRTUAL_IFACE_TYPES + WIRELESS_IFACE_TYPES

+ 1 - 1
netbox/dcim/filters.py

@@ -748,7 +748,7 @@ class InterfaceFilter(django_filters.FilterSet):
         label='Assigned VID'
     )
     type = django_filters.MultipleChoiceFilter(
-        choices=IFACE_TYPE_CHOICES,
+        choices=InterfaceTypeChoices,
         null_value=None
     )
 

+ 12 - 14
netbox/dcim/forms.py

@@ -1076,7 +1076,7 @@ class InterfaceTemplateCreateForm(ComponentForm):
         label='Name'
     )
     type = forms.ChoiceField(
-        choices=IFACE_TYPE_CHOICES,
+        choices=InterfaceTypeChoices,
         widget=StaticSelect2()
     )
     mgmt_only = forms.BooleanField(
@@ -1091,7 +1091,7 @@ class InterfaceTemplateBulkEditForm(BootstrapMixin, BulkEditForm):
         widget=forms.MultipleHiddenInput()
     )
     type = forms.ChoiceField(
-        choices=add_blank_choice(IFACE_TYPE_CHOICES),
+        choices=add_blank_choice(InterfaceTypeChoices),
         required=False,
         widget=StaticSelect2()
     )
@@ -1311,11 +1311,6 @@ class InterfaceTemplateImportForm(ComponentTemplateImportForm):
             'device_type', 'name', 'type', 'mgmt_only',
         ]
 
-    def clean_type(self):
-        # Convert slug value to field integer value
-        slug = self.cleaned_data['type']
-        return InterfaceTypeChoices.slug_to_id(slug)
-
 
 class FrontPortTemplateImportForm(ComponentTemplateImportForm):
     type = forms.ChoiceField(
@@ -2031,7 +2026,7 @@ class DeviceBulkAddComponentForm(BootstrapMixin, forms.Form):
 
 class DeviceBulkAddInterfaceForm(DeviceBulkAddComponentForm):
     type = forms.ChoiceField(
-        choices=IFACE_TYPE_CHOICES,
+        choices=InterfaceTypeChoices,
         widget=StaticSelect2()
     )
     enabled = forms.BooleanField(
@@ -2377,12 +2372,14 @@ class InterfaceForm(InterfaceCommonForm, BootstrapMixin, forms.ModelForm):
         if self.is_bound:
             device = Device.objects.get(pk=self.data['device'])
             self.fields['lag'].queryset = Interface.objects.filter(
-                device__in=[device, device.get_vc_master()], type=IFACE_TYPE_LAG
+                device__in=[device, device.get_vc_master()],
+                type=InterfaceTypeChoices.TYPE_LAG
             )
         else:
             device = self.instance.device
             self.fields['lag'].queryset = Interface.objects.filter(
-                device__in=[self.instance.device, self.instance.device.get_vc_master()], type=IFACE_TYPE_LAG
+                device__in=[self.instance.device, self.instance.device.get_vc_master()],
+                type=InterfaceTypeChoices.TYPE_LAG
             )
 
         # Limit VLan choices to those in: global vlans, global groups, the current site's group, the current site
@@ -2421,7 +2418,7 @@ class InterfaceCreateForm(InterfaceCommonForm, ComponentForm, forms.Form):
         label='Name'
     )
     type = forms.ChoiceField(
-        choices=IFACE_TYPE_CHOICES,
+        choices=InterfaceTypeChoices,
         widget=StaticSelect2(),
     )
     enabled = forms.BooleanField(
@@ -2490,7 +2487,8 @@ class InterfaceCreateForm(InterfaceCommonForm, ComponentForm, forms.Form):
         # Limit LAG choices to interfaces belonging to this device (or its VC master)
         if self.parent is not None:
             self.fields['lag'].queryset = Interface.objects.filter(
-                device__in=[self.parent, self.parent.get_vc_master()], type=IFACE_TYPE_LAG
+                device__in=[self.parent, self.parent.get_vc_master()],
+                type=InterfaceTypeChoices.TYPE_LAG
             )
         else:
             self.fields['lag'].queryset = Interface.objects.none()
@@ -2532,7 +2530,7 @@ class InterfaceBulkEditForm(InterfaceCommonForm, BootstrapMixin, AddRemoveTagsFo
         widget=forms.MultipleHiddenInput()
     )
     type = forms.ChoiceField(
-        choices=add_blank_choice(IFACE_TYPE_CHOICES),
+        choices=add_blank_choice(InterfaceTypeChoices),
         required=False,
         widget=StaticSelect2()
     )
@@ -2602,7 +2600,7 @@ class InterfaceBulkEditForm(InterfaceCommonForm, BootstrapMixin, AddRemoveTagsFo
         if device is not None:
             self.fields['lag'].queryset = Interface.objects.filter(
                 device__in=[device, device.get_vc_master()],
-                type=IFACE_TYPE_LAG
+                type=InterfaceTypeChoices.TYPE_LAG
             )
         else:
             self.fields['lag'].choices = []

+ 114 - 0
netbox/dcim/migrations/0082_interface_type_to_slug.py

@@ -0,0 +1,114 @@
+from django.db import migrations, models
+
+
+INTERFACE_TYPE_CHOICES = (
+    (0, 'virtual'),
+    (200, 'lag'),
+    (800, '100base-tx'),
+    (1000, '1000base-t'),
+    (1050, '1000base-x-gbic'),
+    (1100, '1000base-x-sfp'),
+    (1120, '2.5gbase-t'),
+    (1130, '5gbase-t'),
+    (1150, '10gbase-t'),
+    (1170, '10gbase-cx4'),
+    (1200, '10gbase-x-sfpp'),
+    (1300, '10gbase-x-xfp'),
+    (1310, '10gbase-x-xenpak'),
+    (1320, '10gbase-x-x2'),
+    (1350, '25gbase-x-sfp28'),
+    (1400, '40gbase-x-qsfpp'),
+    (1420, '50gbase-x-sfp28'),
+    (1500, '100gbase-x-cfp'),
+    (1510, '100gbase-x-cfp2'),
+    (1520, '100gbase-x-cfp4'),
+    (1550, '100gbase-x-cpak'),
+    (1600, '100gbase-x-qsfp28'),
+    (1650, '200gbase-x-cfp2'),
+    (1700, '200gbase-x-qsfp56'),
+    (1750, '400gbase-x-qsfpdd'),
+    (1800, '400gbase-x-osfp'),
+    (2600, 'ieee802.11a'),
+    (2610, 'ieee802.11g'),
+    (2620, 'ieee802.11n'),
+    (2630, 'ieee802.11ac'),
+    (2640, 'ieee802.11ad'),
+    (2810, 'gsm'),
+    (2820, 'cdma'),
+    (2830, 'lte'),
+    (6100, 'sonet-oc3'),
+    (6200, 'sonet-oc12'),
+    (6300, 'sonet-oc48'),
+    (6400, 'sonet-oc192'),
+    (6500, 'sonet-oc768'),
+    (6600, 'sonet-oc1920'),
+    (6700, 'sonet-oc3840'),
+    (3010, '1gfc-sfp'),
+    (3020, '2gfc-sfp'),
+    (3040, '4gfc-sfp'),
+    (3080, '8gfc-sfpp'),
+    (3160, '16gfc-sfpp'),
+    (3320, '32gfc-sfp28'),
+    (3400, '128gfc-sfp28'),
+    (7010, 'inifiband-sdr'),
+    (7020, 'inifiband-ddr'),
+    (7030, 'inifiband-qdr'),
+    (7040, 'inifiband-fdr10'),
+    (7050, 'inifiband-fdr'),
+    (7060, 'inifiband-edr'),
+    (7070, 'inifiband-hdr'),
+    (7080, 'inifiband-ndr'),
+    (7090, 'inifiband-xdr'),
+    (4000, 't1'),
+    (4010, 'e1'),
+    (4040, 't3'),
+    (4050, 'e3'),
+    (5000, 'cisco-stackwise'),
+    (5050, 'cisco-stackwise-plus'),
+    (5100, 'cisco-flexstack'),
+    (5150, 'cisco-flexstack-plus'),
+    (5200, 'juniper-vcp'),
+    (5300, 'extreme-summitstack'),
+    (5310, 'extreme-summitstack-128'),
+    (5320, 'extreme-summitstack-256'),
+    (5330, 'extreme-summitstack-512'),
+)
+
+
+def interfacetemplate_type_to_slug(apps, schema_editor):
+    InterfaceTemplate = apps.get_model('dcim', 'InterfaceTemplate')
+    for id, slug in INTERFACE_TYPE_CHOICES:
+        InterfaceTemplate.objects.filter(type=id).update(type=slug)
+
+
+def interface_type_to_slug(apps, schema_editor):
+    Interface = apps.get_model('dcim', 'Interface')
+    for id, slug in INTERFACE_TYPE_CHOICES:
+        Interface.objects.filter(type=id).update(type=slug)
+
+
+class Migration(migrations.Migration):
+    atomic = False
+
+    dependencies = [
+        ('dcim', '0081_devicetype_subdevicerole_to_slug'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='interfacetemplate',
+            name='type',
+            field=models.CharField(max_length=50),
+        ),
+        migrations.RunPython(
+            code=interfacetemplate_type_to_slug
+        ),
+        migrations.AlterField(
+            model_name='interface',
+            name='type',
+            field=models.CharField(max_length=50),
+        ),
+        migrations.RunPython(
+            code=interface_type_to_slug
+        ),
+    ]

+ 9 - 9
netbox/dcim/models.py

@@ -1206,9 +1206,9 @@ class InterfaceTemplate(ComponentTemplateModel):
     name = models.CharField(
         max_length=64
     )
-    type = models.PositiveSmallIntegerField(
-        choices=IFACE_TYPE_CHOICES,
-        default=IFACE_TYPE_10GE_SFP_PLUS
+    type = models.CharField(
+        max_length=50,
+        choices=InterfaceTypeChoices
     )
     mgmt_only = models.BooleanField(
         default=False,
@@ -2238,9 +2238,9 @@ class Interface(CableTermination, ComponentModel):
         blank=True,
         verbose_name='Parent LAG'
     )
-    type = models.PositiveSmallIntegerField(
-        choices=IFACE_TYPE_CHOICES,
-        default=IFACE_TYPE_10GE_SFP_PLUS
+    type = models.CharField(
+        max_length=50,
+        choices=InterfaceTypeChoices
     )
     enabled = models.BooleanField(
         default=True
@@ -2323,7 +2323,7 @@ class Interface(CableTermination, ComponentModel):
             raise ValidationError("An interface must belong to either a device or a virtual machine.")
 
         # VM interfaces must be virtual
-        if self.virtual_machine and self.type is not IFACE_TYPE_VIRTUAL:
+        if self.virtual_machine and self.type is not InterfaceTypeChoices.TYPE_VIRTUAL:
             raise ValidationError({
                 'type': "Virtual machines can only have virtual interfaces."
             })
@@ -2352,7 +2352,7 @@ class Interface(CableTermination, ComponentModel):
             })
 
         # Only a LAG can have LAG members
-        if self.type != IFACE_TYPE_LAG and self.member_interfaces.exists():
+        if self.type != InterfaceTypeChoices.TYPE_LAG and self.member_interfaces.exists():
             raise ValidationError({
                 'type': "Cannot change interface type; it has LAG members ({}).".format(
                     ", ".join([iface.name for iface in self.member_interfaces.all()])
@@ -2435,7 +2435,7 @@ class Interface(CableTermination, ComponentModel):
 
     @property
     def is_lag(self):
-        return self.type == IFACE_TYPE_LAG
+        return self.type == InterfaceTypeChoices.TYPE_LAG
 
     @property
     def count_ipaddresses(self):

+ 11 - 11
netbox/dcim/tests/test_api.py

@@ -2554,7 +2554,7 @@ class InterfaceTest(APITestCase):
     def test_update_interface(self):
 
         lag_interface = Interface.objects.create(
-            device=self.device, name='Test LAG Interface', type=IFACE_TYPE_LAG
+            device=self.device, name='Test LAG Interface', type=InterfaceTypeChoices.TYPE_LAG
         )
 
         data = {
@@ -2842,7 +2842,7 @@ class CableTest(APITestCase):
         )
         for device in [self.device1, self.device2]:
             for i in range(0, 10):
-                Interface(device=device, type=IFACE_TYPE_1GE_FIXED, name='eth{}'.format(i)).save()
+                Interface(device=device, type=InterfaceTypeChoices.TYPE_1GE_FIXED, name='eth{}'.format(i)).save()
 
         self.cable1 = Cable(
             termination_a=self.device1.interfaces.get(name='eth0'),
@@ -3411,23 +3411,23 @@ class VirtualChassisTest(APITestCase):
             device_type=device_type, device_role=device_role, name='StackSwitch9', site=site
         )
         for i in range(0, 13):
-            Interface.objects.create(device=self.device1, name='1/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
+            Interface.objects.create(device=self.device1, name='1/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
         for i in range(0, 13):
-            Interface.objects.create(device=self.device2, name='2/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
+            Interface.objects.create(device=self.device2, name='2/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
         for i in range(0, 13):
-            Interface.objects.create(device=self.device3, name='3/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
+            Interface.objects.create(device=self.device3, name='3/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
         for i in range(0, 13):
-            Interface.objects.create(device=self.device4, name='1/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
+            Interface.objects.create(device=self.device4, name='1/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
         for i in range(0, 13):
-            Interface.objects.create(device=self.device5, name='2/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
+            Interface.objects.create(device=self.device5, name='2/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
         for i in range(0, 13):
-            Interface.objects.create(device=self.device6, name='3/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
+            Interface.objects.create(device=self.device6, name='3/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
         for i in range(0, 13):
-            Interface.objects.create(device=self.device7, name='1/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
+            Interface.objects.create(device=self.device7, name='1/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
         for i in range(0, 13):
-            Interface.objects.create(device=self.device8, name='2/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
+            Interface.objects.create(device=self.device8, name='2/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
         for i in range(0, 13):
-            Interface.objects.create(device=self.device9, name='3/{}'.format(i), type=IFACE_TYPE_1GE_FIXED)
+            Interface.objects.create(device=self.device9, name='3/{}'.format(i), type=InterfaceTypeChoices.TYPE_1GE_FIXED)
 
         # Create two VirtualChassis with three members each
         self.vc1 = VirtualChassis.objects.create(master=self.device1, domain='test-domain-1')

+ 4 - 4
netbox/dcim/tests/test_models.py

@@ -193,7 +193,7 @@ class DeviceTestCase(TestCase):
         InterfaceTemplate(
             device_type=self.device_type,
             name='Interface 1',
-            type=IFACE_TYPE_1GE_FIXED,
+            type=InterfaceTypeChoices.TYPE_1GE_FIXED,
             mgmt_only=True
         ).save()
 
@@ -257,7 +257,7 @@ class DeviceTestCase(TestCase):
         Interface.objects.get(
             device=d,
             name='Interface 1',
-            type=IFACE_TYPE_1GE_FIXED,
+            type=InterfaceTypeChoices.TYPE_1GE_FIXED,
             mgmt_only=True
         )
 
@@ -379,7 +379,7 @@ class CableTestCase(TestCase):
         """
         A cable cannot terminate to a virtual interface
         """
-        virtual_interface = Interface(device=self.device1, name="V1", type=IFACE_TYPE_VIRTUAL)
+        virtual_interface = Interface(device=self.device1, name="V1", type=InterfaceTypeChoices.TYPE_VIRTUAL)
         cable = Cable(termination_a=self.interface2, termination_b=virtual_interface)
         with self.assertRaises(ValidationError):
             cable.clean()
@@ -388,7 +388,7 @@ class CableTestCase(TestCase):
         """
         A cable cannot terminate to a wireless interface
         """
-        wireless_interface = Interface(device=self.device1, name="W1", type=IFACE_TYPE_80211A)
+        wireless_interface = Interface(device=self.device1, name="W1", type=InterfaceTypeChoices.TYPE_80211A)
         cable = Cable(termination_a=self.interface2, termination_b=wireless_interface)
         with self.assertRaises(ValidationError):
             cable.clean()

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

@@ -348,7 +348,7 @@ device-bays:
         self.assertEqual(dt.interface_templates.count(), 3)
         iface1 = InterfaceTemplate.objects.first()
         self.assertEqual(iface1.name, 'Interface 1')
-        self.assertEqual(iface1.type, IFACE_TYPE_1GE_FIXED)
+        self.assertEqual(iface1.type, InterfaceTypeChoices.TYPE_1GE_FIXED)
         self.assertTrue(iface1.mgmt_only)
 
         self.assertEqual(dt.rearport_templates.count(), 3)
@@ -514,17 +514,17 @@ class CableTestCase(TestCase):
         device2 = Device(name='Device 2', site=site, device_type=devicetype, device_role=devicerole)
         device2.save()
 
-        iface1 = Interface(device=device1, name='Interface 1', type=IFACE_TYPE_1GE_FIXED)
+        iface1 = Interface(device=device1, name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED)
         iface1.save()
-        iface2 = Interface(device=device1, name='Interface 2', type=IFACE_TYPE_1GE_FIXED)
+        iface2 = Interface(device=device1, name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED)
         iface2.save()
-        iface3 = Interface(device=device1, name='Interface 3', type=IFACE_TYPE_1GE_FIXED)
+        iface3 = Interface(device=device1, name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED)
         iface3.save()
-        iface4 = Interface(device=device2, name='Interface 1', type=IFACE_TYPE_1GE_FIXED)
+        iface4 = Interface(device=device2, name='Interface 1', type=InterfaceTypeChoices.TYPE_1GE_FIXED)
         iface4.save()
-        iface5 = Interface(device=device2, name='Interface 2', type=IFACE_TYPE_1GE_FIXED)
+        iface5 = Interface(device=device2, name='Interface 2', type=InterfaceTypeChoices.TYPE_1GE_FIXED)
         iface5.save()
-        iface6 = Interface(device=device2, name='Interface 3', type=IFACE_TYPE_1GE_FIXED)
+        iface6 = Interface(device=device2, name='Interface 3', type=InterfaceTypeChoices.TYPE_1GE_FIXED)
         iface6.save()
 
         Cable(termination_a=iface1, termination_b=iface4, type=CABLE_TYPE_CAT6).save()

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

@@ -3,7 +3,8 @@ from rest_framework import serializers
 from taggit_serializer.serializers import TaggitSerializer, TagListSerializerField
 
 from dcim.api.nested_serializers import NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedSiteSerializer
-from dcim.constants import IFACE_TYPE_CHOICES, IFACE_TYPE_VIRTUAL, IFACE_MODE_CHOICES
+from dcim.choices import InterfaceTypeChoices
+from dcim.constants import IFACE_MODE_CHOICES
 from dcim.models import Interface
 from extras.api.customfields import CustomFieldModelSerializer
 from ipam.api.nested_serializers import NestedIPAddressSerializer, NestedVLANSerializer
@@ -98,7 +99,7 @@ class VirtualMachineWithConfigContextSerializer(VirtualMachineSerializer):
 
 class InterfaceSerializer(TaggitSerializer, ValidatedModelSerializer):
     virtual_machine = NestedVirtualMachineSerializer()
-    type = ChoiceField(choices=IFACE_TYPE_CHOICES, default=IFACE_TYPE_VIRTUAL, required=False)
+    type = ChoiceField(choices=InterfaceTypeChoices, default=InterfaceTypeChoices.TYPE_VIRTUAL, required=False)
     mode = ChoiceField(choices=IFACE_MODE_CHOICES, required=False, allow_null=True)
     untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
     tagged_vlans = SerializedPKRelatedField(

+ 5 - 4
netbox/virtualization/forms.py

@@ -2,7 +2,8 @@ from django import forms
 from django.core.exceptions import ValidationError
 from taggit.forms import TagField
 
-from dcim.constants import IFACE_TYPE_VIRTUAL, IFACE_MODE_ACCESS, IFACE_MODE_TAGGED_ALL, IFACE_MODE_CHOICES
+from dcim.choices import InterfaceTypeChoices
+from dcim.constants import IFACE_MODE_ACCESS, IFACE_MODE_TAGGED_ALL, IFACE_MODE_CHOICES
 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
@@ -19,7 +20,7 @@ from .constants import *
 from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 
 VIFACE_TYPE_CHOICES = (
-    (IFACE_TYPE_VIRTUAL, 'Virtual'),
+    (InterfaceTypeChoices.TYPE_VIRTUAL, 'Virtual'),
 )
 
 
@@ -733,7 +734,7 @@ class InterfaceCreateForm(ComponentForm):
     )
     type = forms.ChoiceField(
         choices=VIFACE_TYPE_CHOICES,
-        initial=IFACE_TYPE_VIRTUAL,
+        initial=InterfaceTypeChoices.TYPE_VIRTUAL,
         widget=forms.HiddenInput()
     )
     enabled = forms.BooleanField(
@@ -918,7 +919,7 @@ class VirtualMachineBulkAddComponentForm(BootstrapMixin, forms.Form):
 class VirtualMachineBulkAddInterfaceForm(VirtualMachineBulkAddComponentForm):
     type = forms.ChoiceField(
         choices=VIFACE_TYPE_CHOICES,
-        initial=IFACE_TYPE_VIRTUAL,
+        initial=InterfaceTypeChoices.TYPE_VIRTUAL,
         widget=forms.HiddenInput()
     )
     enabled = forms.BooleanField(

+ 5 - 4
netbox/virtualization/tests/test_api.py

@@ -2,7 +2,8 @@ from django.urls import reverse
 from netaddr import IPNetwork
 from rest_framework import status
 
-from dcim.constants import IFACE_TYPE_VIRTUAL, IFACE_MODE_TAGGED
+from dcim.choices import InterfaceTypeChoices
+from dcim.constants import IFACE_MODE_TAGGED
 from dcim.models import Interface
 from ipam.models import IPAddress, VLAN
 from utilities.testing import APITestCase
@@ -489,17 +490,17 @@ class InterfaceTest(APITestCase):
         self.interface1 = Interface.objects.create(
             virtual_machine=self.virtualmachine,
             name='Test Interface 1',
-            type=IFACE_TYPE_VIRTUAL
+            type=InterfaceTypeChoices.TYPE_VIRTUAL
         )
         self.interface2 = Interface.objects.create(
             virtual_machine=self.virtualmachine,
             name='Test Interface 2',
-            type=IFACE_TYPE_VIRTUAL
+            type=InterfaceTypeChoices.TYPE_VIRTUAL
         )
         self.interface3 = Interface.objects.create(
             virtual_machine=self.virtualmachine,
             name='Test Interface 3',
-            type=IFACE_TYPE_VIRTUAL
+            type=InterfaceTypeChoices.TYPE_VIRTUAL
         )
 
         self.vlan1 = VLAN.objects.create(name="Test VLAN 1", vid=1)