diff --git a/netbox/templates/virtualization/vminterface.html b/netbox/templates/virtualization/vminterface.html
index e3779ef21..e574e926e 100644
--- a/netbox/templates/virtualization/vminterface.html
+++ b/netbox/templates/virtualization/vminterface.html
@@ -57,9 +57,10 @@
- {% plugin_left_page object %}
+ {% plugin_left_page object %}
+ {% include 'inc/custom_fields_panel.html' %}
{% include 'extras/inc/tags_panel.html' with tags=object.tags.all %}
{% plugin_right_page object %}
diff --git a/netbox/templates/virtualization/vminterface_edit.html b/netbox/templates/virtualization/vminterface_edit.html
index d4869aaf9..c0ad6e98c 100644
--- a/netbox/templates/virtualization/vminterface_edit.html
+++ b/netbox/templates/virtualization/vminterface_edit.html
@@ -31,6 +31,14 @@
{% render_field form.tagged_vlans %}
+ {% if form.custom_fields %}
+
+
Custom Fields
+
+ {% render_custom_fields form %}
+
+
+ {% endif %}
{% endblock %}
{% block buttons %}
diff --git a/netbox/virtualization/api/serializers.py b/netbox/virtualization/api/serializers.py
index e3b82e40a..f2640811b 100644
--- a/netbox/virtualization/api/serializers.py
+++ b/netbox/virtualization/api/serializers.py
@@ -103,7 +103,7 @@ class VirtualMachineWithConfigContextSerializer(VirtualMachineSerializer):
# VM interfaces
#
-class VMInterfaceSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
+class VMInterfaceSerializer(TaggedObjectSerializer, CustomFieldModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='virtualization-api:vminterface-detail')
virtual_machine = NestedVirtualMachineSerializer()
mode = ChoiceField(choices=InterfaceModeChoices, allow_blank=True, required=False)
@@ -119,7 +119,7 @@ class VMInterfaceSerializer(TaggedObjectSerializer, ValidatedModelSerializer):
model = VMInterface
fields = [
'id', 'url', 'virtual_machine', 'name', 'enabled', 'mtu', 'mac_address', 'description', 'mode',
- 'untagged_vlan', 'tagged_vlans', 'tags',
+ 'untagged_vlan', 'tagged_vlans', 'tags', 'custom_fields', 'created', 'last_updated',
]
def validate(self, data):
diff --git a/netbox/virtualization/filters.py b/netbox/virtualization/filters.py
index ea668c737..be9b70749 100644
--- a/netbox/virtualization/filters.py
+++ b/netbox/virtualization/filters.py
@@ -237,7 +237,7 @@ class VirtualMachineFilterSet(
return queryset.exclude(params)
-class VMInterfaceFilterSet(BaseFilterSet):
+class VMInterfaceFilterSet(BaseFilterSet, CustomFieldModelFilterSet, CreatedUpdatedFilterSet):
q = django_filters.CharFilter(
method='search',
label='Search',
diff --git a/netbox/virtualization/forms.py b/netbox/virtualization/forms.py
index c2d0aee38..2aed5230f 100644
--- a/netbox/virtualization/forms.py
+++ b/netbox/virtualization/forms.py
@@ -576,7 +576,7 @@ class VirtualMachineFilterForm(BootstrapMixin, TenancyFilterForm, CustomFieldFil
# VM interfaces
#
-class VMInterfaceForm(BootstrapMixin, InterfaceCommonForm, forms.ModelForm):
+class VMInterfaceForm(BootstrapMixin, InterfaceCommonForm, CustomFieldModelForm):
untagged_vlan = DynamicModelChoiceField(
queryset=VLAN.objects.all(),
required=False,
diff --git a/netbox/virtualization/migrations/0020_standardize_models.py b/netbox/virtualization/migrations/0020_standardize_models.py
index 42c44e83a..42dedbc72 100644
--- a/netbox/virtualization/migrations/0020_standardize_models.py
+++ b/netbox/virtualization/migrations/0020_standardize_models.py
@@ -54,4 +54,9 @@ class Migration(migrations.Migration):
name='last_updated',
field=models.DateTimeField(auto_now=True, null=True),
),
+ migrations.AddField(
+ model_name='vminterface',
+ name='custom_field_data',
+ field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder),
+ ),
]
diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py
index 9744df3a5..4896e26bd 100644
--- a/netbox/virtualization/models.py
+++ b/netbox/virtualization/models.py
@@ -9,7 +9,7 @@ from dcim.models import BaseInterface, Device
from extras.models import ConfigContextModel, TaggedItem
from extras.querysets import ConfigContextModelQuerySet
from extras.utils import extras_features
-from netbox.models import ChangeLoggedModel, OrganizationalModel, PrimaryModel
+from netbox.models import OrganizationalModel, PrimaryModel
from utilities.fields import NaturalOrderingField
from utilities.ordering import naturalize_interface
from utilities.query_functions import CollateAsChar
@@ -372,9 +372,8 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
# Interfaces
#
-# TODO: Inherit from PrimaryModel
-@extras_features('export_templates', 'webhooks')
-class VMInterface(ChangeLoggedModel, BaseInterface):
+@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
+class VMInterface(PrimaryModel, BaseInterface):
virtual_machine = models.ForeignKey(
to='virtualization.VirtualMachine',
on_delete=models.CASCADE,