Explorar o código

Fixes #3868: Fix creation of interfaces for virtual machines

Jeremy Stretch %!s(int64=6) %!d(string=hai) anos
pai
achega
fe490d144a

+ 4 - 0
docs/release-notes/version-2.7.md

@@ -235,6 +235,10 @@ PATCH) to maintain backward compatibility. This behavior will be discontinued be
 * [#3706](https://github.com/digitalocean/netbox/issues/3706) - Increase `available_power` maximum value on PowerFeed
 * [#3731](https://github.com/digitalocean/netbox/issues/3731) - Change Graph.type to a ContentType foreign key field
 
+## Bug Fixes (From Beta)
+
+* [#3868](https://github.com/digitalocean/netbox/issues/3868) - Fix creation of interfaces for virtual machines
+
 ## API Changes
 
 * Choice fields now use human-friendly strings for their values instead of integers (see

+ 3 - 2
netbox/dcim/models.py

@@ -22,6 +22,7 @@ from utilities.fields import ColorField
 from utilities.managers import NaturalOrderingManager
 from utilities.models import ChangeLoggedModel
 from utilities.utils import foreground_color, serialize_object, to_meters
+from virtualization.choices import VMInterfaceTypeChoices
 
 from .choices import *
 from .constants import *
@@ -2510,9 +2511,9 @@ 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 InterfaceTypeChoices.TYPE_VIRTUAL:
+        if self.virtual_machine and self.type not in VMInterfaceTypeChoices.values():
             raise ValidationError({
-                'type': "Virtual machines can only have virtual interfaces."
+                'type': "Invalid interface type for a virtual machine: {}".format(self.type)
             })
 
         # Virtual interfaces cannot be connected

+ 4 - 0
netbox/utilities/choices.py

@@ -16,6 +16,10 @@ class ChoiceSet(metaclass=ChoiceSetMeta):
     CHOICES = list()
     LEGACY_MAP = dict()
 
+    @classmethod
+    def values(cls):
+        return [c[0] for c in cls.CHOICES]
+
     @classmethod
     def slug_to_id(cls, slug):
         """

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

@@ -99,7 +99,7 @@ class VirtualMachineWithConfigContextSerializer(VirtualMachineSerializer):
 
 class InterfaceSerializer(TaggitSerializer, ValidatedModelSerializer):
     virtual_machine = NestedVirtualMachineSerializer()
-    type = ChoiceField(choices=InterfaceTypeChoices, default=InterfaceTypeChoices.TYPE_VIRTUAL, required=False)
+    type = ChoiceField(choices=VMInterfaceTypeChoices, default=VMInterfaceTypeChoices.TYPE_VIRTUAL, required=False)
     mode = ChoiceField(choices=InterfaceModeChoices, required=False, allow_null=True)
     untagged_vlan = NestedVLANSerializer(required=False, allow_null=True)
     tagged_vlans = SerializedPKRelatedField(

+ 14 - 0
netbox/virtualization/choices.py

@@ -1,3 +1,4 @@
+from dcim.choices import InterfaceTypeChoices
 from utilities.choices import ChoiceSet
 
 
@@ -22,3 +23,16 @@ class VirtualMachineStatusChoices(ChoiceSet):
         STATUS_ACTIVE: 1,
         STATUS_STAGED: 3,
     }
+
+
+#
+# Interface types (for VirtualMachines)
+#
+
+class VMInterfaceTypeChoices(ChoiceSet):
+
+    TYPE_VIRTUAL = InterfaceTypeChoices.TYPE_VIRTUAL
+
+    CHOICES = (
+        (TYPE_VIRTUAL, 'Virtual'),
+    )

+ 5 - 9
netbox/virtualization/forms.py

@@ -2,7 +2,7 @@ from django import forms
 from django.core.exceptions import ValidationError
 from taggit.forms import TagField
 
-from dcim.choices import InterfaceModeChoices, InterfaceTypeChoices
+from dcim.choices import InterfaceModeChoices
 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
@@ -18,10 +18,6 @@ from utilities.forms import (
 from .choices import *
 from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 
-VIFACE_TYPE_CHOICES = (
-    (InterfaceTypeChoices.TYPE_VIRTUAL, 'Virtual'),
-)
-
 
 #
 # Cluster types
@@ -740,8 +736,8 @@ class InterfaceCreateForm(ComponentForm):
         label='Name'
     )
     type = forms.ChoiceField(
-        choices=VIFACE_TYPE_CHOICES,
-        initial=InterfaceTypeChoices.TYPE_VIRTUAL,
+        choices=VMInterfaceTypeChoices,
+        initial=VMInterfaceTypeChoices.TYPE_VIRTUAL,
         widget=forms.HiddenInput()
     )
     enabled = forms.BooleanField(
@@ -925,8 +921,8 @@ class VirtualMachineBulkAddComponentForm(BootstrapMixin, forms.Form):
 
 class VirtualMachineBulkAddInterfaceForm(VirtualMachineBulkAddComponentForm):
     type = forms.ChoiceField(
-        choices=VIFACE_TYPE_CHOICES,
-        initial=InterfaceTypeChoices.TYPE_VIRTUAL,
+        choices=VMInterfaceTypeChoices,
+        initial=VMInterfaceTypeChoices.TYPE_VIRTUAL,
         widget=forms.HiddenInput()
     )
     enabled = forms.BooleanField(