diff --git a/docs/release-notes/version-2.11.md b/docs/release-notes/version-2.11.md
index c80b74296..360fbff8a 100644
--- a/docs/release-notes/version-2.11.md
+++ b/docs/release-notes/version-2.11.md
@@ -95,6 +95,7 @@ A new cloud model has been introduced for representing the boundary of a network
* [#5873](https://github.com/netbox-community/netbox/issues/5873) - Use numeric IDs in all object URLs
* [#5990](https://github.com/netbox-community/netbox/issues/5990) - Deprecated `display_field` parameter for custom script ObjectVar and MultiObjectVar fields
* [#5995](https://github.com/netbox-community/netbox/issues/5995) - Dropped backward compatibility for `queryset` parameter on ObjectVar and MultiObjectVar (use `model` instead)
+* [#6014](https://github.com/netbox-community/netbox/issues/6014) - Moved virtual machine interfaces list to a separate view
### REST API Changes
diff --git a/netbox/templates/virtualization/virtualmachine.html b/netbox/templates/virtualization/virtualmachine.html
index 43f1fc5e7..71196b45a 100644
--- a/netbox/templates/virtualization/virtualmachine.html
+++ b/netbox/templates/virtualization/virtualmachine.html
@@ -1,44 +1,10 @@
-{% extends 'generic/object.html' %}
+{% extends 'virtualization/virtualmachine/base.html' %}
{% load buttons %}
{% load custom_links %}
{% load static %}
{% load helpers %}
{% load plugins %}
-{% block breadcrumbs %}
- {% if object.cluster %}
-
{{ object.cluster }}
- {% endif %}
- {{ object }}
-{% endblock %}
-
-{% block buttons %}
- {% if perms.virtualization.add_vminterface %}
-
- Add Interfaces
-
- {% endif %}
- {{ block.super }}
-{% endblock %}
-
-{% block tabs %}
-
-{% endblock %}
-
{% block content %}
@@ -236,54 +202,6 @@
{% plugin_full_width_page object %}
-
-
-
- {% table_config_form vminterface_table %}
-
-
{% include 'secrets/inc/private_key_modal.html' %}
{% endblock %}
diff --git a/netbox/templates/virtualization/virtualmachine/base.html b/netbox/templates/virtualization/virtualmachine/base.html
new file mode 100644
index 000000000..00212aadd
--- /dev/null
+++ b/netbox/templates/virtualization/virtualmachine/base.html
@@ -0,0 +1,45 @@
+{% extends 'generic/object.html' %}
+{% load buttons %}
+{% load helpers %}
+{% load custom_links %}
+{% load plugins %}
+
+{% block breadcrumbs %}
+ Virtual Machines
+ {{ object.cluster }}
+ {{ object }}
+{% endblock %}
+
+{% block buttons %}
+ {% if perms.virtualization.add_vminterface %}
+
+ Add Interfaces
+
+ {% endif %}
+ {{ block.super }}
+{% endblock %}
+
+{% block tabs %}
+
+{% endblock %}
diff --git a/netbox/templates/virtualization/virtualmachine/interfaces.html b/netbox/templates/virtualization/virtualmachine/interfaces.html
new file mode 100644
index 000000000..15d07310c
--- /dev/null
+++ b/netbox/templates/virtualization/virtualmachine/interfaces.html
@@ -0,0 +1,54 @@
+{% extends 'virtualization/virtualmachine/base.html' %}
+{% load render_table from django_tables2 %}
+{% load helpers %}
+{% load static %}
+
+{% block content %}
+
+ {% table_config_form interface_table %}
+{% endblock %}
+
+{% block javascript %}
+
+
+
+{% endblock %}
diff --git a/netbox/virtualization/tests/test_views.py b/netbox/virtualization/tests/test_views.py
index cb0055789..0e1dee9e4 100644
--- a/netbox/virtualization/tests/test_views.py
+++ b/netbox/virtualization/tests/test_views.py
@@ -1,3 +1,5 @@
+from django.test import override_settings
+from django.urls import reverse
from netaddr import EUI
from dcim.choices import InterfaceModeChoices
@@ -196,6 +198,19 @@ class VirtualMachineTestCase(ViewTestCases.PrimaryObjectViewTestCase):
'comments': 'New comments',
}
+ @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
+ def test_device_interfaces(self):
+ virtualmachine = VirtualMachine.objects.first()
+ vminterfaces = (
+ VMInterface(virtual_machine=virtualmachine, name='Interface 1'),
+ VMInterface(virtual_machine=virtualmachine, name='Interface 2'),
+ VMInterface(virtual_machine=virtualmachine, name='Interface 3'),
+ )
+ VMInterface.objects.bulk_create(vminterfaces)
+
+ url = reverse('virtualization:virtualmachine_interfaces', kwargs={'pk': virtualmachine.pk})
+ self.assertHttpStatus(self.client.get(url), 200)
+
class VMInterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
model = VMInterface
diff --git a/netbox/virtualization/urls.py b/netbox/virtualization/urls.py
index 17750e806..457b73024 100644
--- a/netbox/virtualization/urls.py
+++ b/netbox/virtualization/urls.py
@@ -51,6 +51,7 @@ urlpatterns = [
path('virtual-machines/edit/', views.VirtualMachineBulkEditView.as_view(), name='virtualmachine_bulk_edit'),
path('virtual-machines/delete/', views.VirtualMachineBulkDeleteView.as_view(), name='virtualmachine_bulk_delete'),
path('virtual-machines//', views.VirtualMachineView.as_view(), name='virtualmachine'),
+ path('virtual-machines//interfaces/', views.VirtualMachineInterfacesView.as_view(), name='virtualmachine_interfaces'),
path('virtual-machines//edit/', views.VirtualMachineEditView.as_view(), name='virtualmachine_edit'),
path('virtual-machines//delete/', views.VirtualMachineDeleteView.as_view(), name='virtualmachine_delete'),
path('virtual-machines//config-context/', views.VirtualMachineConfigContextView.as_view(), name='virtualmachine_configcontext'),
diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py
index a874d2a92..75f2368fb 100644
--- a/netbox/virtualization/views.py
+++ b/netbox/virtualization/views.py
@@ -322,6 +322,30 @@ class VirtualMachineView(generic.ObjectView):
}
+class VirtualMachineInterfacesView(generic.ObjectView):
+ queryset = VirtualMachine.objects.all()
+ template_name = 'virtualization/virtualmachine/interfaces.html'
+
+ def get_extra_context(self, request, instance):
+ interfaces = instance.interfaces.restrict(request.user, 'view').prefetch_related(
+ Prefetch('ip_addresses', queryset=IPAddress.objects.restrict(request.user)),
+ 'tags',
+ )
+ interface_table = tables.VirtualMachineVMInterfaceTable(
+ data=interfaces,
+ user=request.user,
+ orderable=False
+ )
+ if request.user.has_perm('virtualization.change_vminterface') or \
+ request.user.has_perm('virtualization.delete_vminterface'):
+ interface_table.columns.show('pk')
+
+ return {
+ 'interface_table': interface_table,
+ 'active_tab': 'interfaces',
+ }
+
+
class VirtualMachineConfigContextView(ObjectConfigContextView):
queryset = VirtualMachine.objects.annotate_config_context_data()
base_template = 'virtualization/virtualmachine.html'