From fe490d144a357774bc01a5957196af37e2e0d0cf Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 9 Jan 2020 14:53:18 -0500 Subject: [PATCH] Fixes #3868: Fix creation of interfaces for virtual machines --- docs/release-notes/version-2.7.md | 4 ++++ netbox/dcim/models.py | 5 +++-- netbox/utilities/choices.py | 4 ++++ netbox/virtualization/api/serializers.py | 2 +- netbox/virtualization/choices.py | 14 ++++++++++++++ netbox/virtualization/forms.py | 14 +++++--------- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/docs/release-notes/version-2.7.md b/docs/release-notes/version-2.7.md index 2dff5110e..eaf337e3c 100644 --- a/docs/release-notes/version-2.7.md +++ b/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 diff --git a/netbox/dcim/models.py b/netbox/dcim/models.py index 724150977..8796ea1ea 100644 --- a/netbox/dcim/models.py +++ b/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 diff --git a/netbox/utilities/choices.py b/netbox/utilities/choices.py index 7738aa7c0..e49b3a365 100644 --- a/netbox/utilities/choices.py +++ b/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): """ diff --git a/netbox/virtualization/api/serializers.py b/netbox/virtualization/api/serializers.py index 10180d279..8725cbee1 100644 --- a/netbox/virtualization/api/serializers.py +++ b/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( diff --git a/netbox/virtualization/choices.py b/netbox/virtualization/choices.py index d96842c35..3c4a17c7b 100644 --- a/netbox/virtualization/choices.py +++ b/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'), + ) diff --git a/netbox/virtualization/forms.py b/netbox/virtualization/forms.py index a62a427e2..910e9a39f 100644 --- a/netbox/virtualization/forms.py +++ b/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(