Quellcode durchsuchen

Closes #6014: Move virtual machine interfaces list to a separate view

Jeremy Stretch vor 4 Jahren
Ursprung
Commit
0364d8cd43

+ 1 - 0
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
 

+ 1 - 83
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 %}
-    <li><a href="{{ object.cluster.get_absolute_url }}">{{ object.cluster }}</a></li>
-  {% endif %}
-  <li>{{ object }}</li>
-{% endblock %}
-
-{% block buttons %}
-  {% if perms.virtualization.add_vminterface %}
-    <a href="{% url 'virtualization:vminterface_add' %}?virtual_machine={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
-      <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Interfaces
-    </a>
-  {% endif %}
-  {{ block.super }}
-{% endblock %}
-
-{% block tabs %}
-  <ul class="nav nav-tabs">
-    <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
-      <a href="{{ object.get_absolute_url }}">Virtual Machine</a>
-    </li>
-    {% if perms.extras.view_configcontext %}
-      <li role="presentation"{% if active_tab == 'config-context' %} class="active"{% endif %}>
-        <a href="{% url 'virtualization:virtualmachine_configcontext' pk=object.pk %}">Config Context</a>
-      </li>
-    {% endif %}
-    {% if perms.extras.view_objectchange %}
-      <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
-        <a href="{% url 'virtualization:virtualmachine_changelog' pk=object.pk %}">Change Log</a>
-      </li>
-    {% endif %}
-  </ul>
-{% endblock %}
-
 {% block content %}
 <div class="row">
 	<div class="col-md-6">
@@ -236,54 +202,6 @@
         {% plugin_full_width_page object %}
     </div>
 </div>
-<div class="row">
-    <div class="col-md-12">
-        <form method="post">
-            {% csrf_token %}
-            <input type="hidden" name="virtual_machine" value="{{ object.pk }}" />
-            <div class="panel panel-default">
-                <div class="panel-heading">
-                    <strong>Interfaces</strong>
-                    <div class="pull-right noprint">
-                        {% if request.user.is_authenticated %}
-                            <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#VirtualMachineVMInterfaceTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
-                        {% endif %}
-                    </div>
-                    <div class="pull-right col-md-2 noprint">
-                        <input class="form-control interface-filter" type="text" placeholder="Filter" title="Filter text (regular expressions supported)" style="height: 23px" />
-                    </div>
-                </div>
-                {% include 'responsive_table.html' with table=vminterface_table %}
-                {% if perms.virtualization.add_vminterface or perms.virtualization.delete_vminterface %}
-                    <div class="panel-footer noprint">
-                        {% if interfaces and perms.virtualization.change_vminterface %}
-                            <button type="submit" name="_rename" formaction="{% url 'virtualization:vminterface_bulk_rename' %}?return_url={{ object.get_absolute_url }}" class="btn btn-warning btn-xs">
-                                <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
-                            </button>
-                            <button type="submit" name="_edit" formaction="{% url 'virtualization:vminterface_bulk_edit' %}?return_url={{ object.get_absolute_url }}" class="btn btn-warning btn-xs">
-                                <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
-                            </button>
-                        {% endif %}
-                        {% if interfaces and perms.virtualization.delete_vminterface %}
-                            <button type="submit" name="_delete" formaction="{% url 'virtualization:vminterface_bulk_delete' %}?return_url={{ object.get_absolute_url }}" class="btn btn-danger btn-xs">
-                                <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
-                            </button>
-                        {% endif %}
-                        {% if perms.virtualization.add_vminterface %}
-                            <div class="pull-right">
-                                <a href="{% url 'virtualization:vminterface_add' %}?virtual_machine={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary btn-xs">
-                                    <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add interfaces
-                                </a>
-                            </div>
-                            <div class="clearfix"></div>
-                        {% endif %}
-                     </div>
-                {% endif %}
-            </div>
-        </form>
-        {% table_config_form vminterface_table %}
-	</div>
-</div>
 {% include 'secrets/inc/private_key_modal.html' %}
 {% endblock %}
 

+ 45 - 0
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 %}
+  <li><a href="{% url 'virtualization:virtualmachine_list' %}">Virtual Machines</a></li>
+  <li><a href="{% url 'virtualization:virtualmachine_list' %}?cluster_id={{ object.cluster.pk }}">{{ object.cluster }}</a></li>
+  <li>{{ object }}</li>
+{% endblock %}
+
+{% block buttons %}
+  {% if perms.virtualization.add_vminterface %}
+    <a href="{% url 'virtualization:vminterface_add' %}?virtual_machine={{ object.pk }}&return_url={{ object.get_absolute_url }}" class="btn btn-primary">
+      <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add Interfaces
+    </a>
+  {% endif %}
+  {{ block.super }}
+{% endblock %}
+
+{% block tabs %}
+  <ul class="nav nav-tabs">
+    <li role="presentation"{% if not active_tab %} class="active"{% endif %}>
+      <a href="{{ object.get_absolute_url }}">Virtual Machine</a>
+    </li>
+    {% with interface_count=object.interfaces.count %}
+        {% if interface_count %}
+            <li role="presentation" {% if active_tab == 'interfaces' %} class="active"{% endif %}>
+                <a href="{% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}">Interfaces {% badge interface_count %}</a>
+            </li>
+        {% endif %}
+    {% endwith %}
+    {% if perms.extras.view_configcontext %}
+      <li role="presentation"{% if active_tab == 'config-context' %} class="active"{% endif %}>
+        <a href="{% url 'virtualization:virtualmachine_configcontext' pk=object.pk %}">Config Context</a>
+      </li>
+    {% endif %}
+    {% if perms.extras.view_objectchange %}
+      <li role="presentation"{% if active_tab == 'changelog' %} class="active"{% endif %}>
+        <a href="{% url 'virtualization:virtualmachine_changelog' pk=object.pk %}">Change Log</a>
+      </li>
+    {% endif %}
+  </ul>
+{% endblock %}

+ 54 - 0
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 %}
+    <form method="post">
+        {% csrf_token %}
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <strong>Interfaces</strong>
+                <div class="pull-right noprint">
+                    {% if request.user.is_authenticated %}
+                        <button type="button" class="btn btn-default btn-xs" data-toggle="modal" data-target="#VirtualMachineVMInterfaceTable_config" title="Configure table"><i class="mdi mdi-cog"></i> Configure</button>
+                    {% endif %}
+                </div>
+                <div class="pull-right col-md-2 noprint">
+                    <input class="form-control interface-filter" type="text" placeholder="Filter" title="Filter text (regular expressions supported)" style="height: 23px" />
+                </div>
+            </div>
+            {% render_table interface_table 'inc/table.html' %}
+            <div class="panel-footer noprint">
+                {% if perms.virtualization.change_vminterface %}
+                    <button type="submit" name="_rename" formaction="{% url 'virtualization:vminterface_bulk_rename' %}?return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Rename
+                    </button>
+                    <button type="submit" name="_edit" formaction="{% url 'virtualization:vminterface_bulk_edit' %}?virtualmachine={{ object.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}" class="btn btn-warning btn-xs">
+                        <span class="mdi mdi-pencil" aria-hidden="true"></span> Edit
+                    </button>
+                {% endif %}
+                {% if perms.virtualization.delete_vminterface %}
+                    <button type="submit" name="_delete" formaction="{% url 'virtualization:vminterface_bulk_delete' %}?return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}" class="btn btn-danger btn-xs">
+                        <span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Delete
+                    </button>
+                {% endif %}
+                {% if perms.virtualization.add_vminterface %}
+                    <div class="pull-right">
+                        <a href="{% url 'virtualization:vminterface_add' %}?virtualmachine={{ object.pk }}&return_url={% url 'virtualization:virtualmachine_interfaces' pk=object.pk %}" class="btn btn-primary btn-xs">
+                            <span class="mdi mdi-plus-thick" aria-hidden="true"></span> Add interfaces
+                        </a>
+                    </div>
+                {% endif %}
+                <div class="clearfix"></div>
+             </div>
+        </div>
+    </form>
+    {% table_config_form interface_table %}
+{% endblock %}
+
+{% block javascript %}
+  <script src="{% static 'js/connection_toggles.js' %}?v{{ settings.VERSION }}"></script>
+  <script src="{% static 'js/interface_filtering.js' %}?v{{ settings.VERSION }}"></script>
+  <script src="{% static 'js/tableconfig.js' %}?v{{ settings.VERSION }}"></script>
+{% endblock %}

+ 15 - 0
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

+ 1 - 0
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/<int:pk>/', views.VirtualMachineView.as_view(), name='virtualmachine'),
+    path('virtual-machines/<int:pk>/interfaces/', views.VirtualMachineInterfacesView.as_view(), name='virtualmachine_interfaces'),
     path('virtual-machines/<int:pk>/edit/', views.VirtualMachineEditView.as_view(), name='virtualmachine_edit'),
     path('virtual-machines/<int:pk>/delete/', views.VirtualMachineDeleteView.as_view(), name='virtualmachine_delete'),
     path('virtual-machines/<int:pk>/config-context/', views.VirtualMachineConfigContextView.as_view(), name='virtualmachine_configcontext'),

+ 24 - 0
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'