mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-23 04:22:01 -06:00
Closes #6014: Move virtual machine interfaces list to a separate view
This commit is contained in:
parent
1544823d73
commit
0364d8cd43
@ -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,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
netbox/templates/virtualization/virtualmachine/base.html
Normal file
45
netbox/templates/virtualization/virtualmachine/base.html
Normal file
@ -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 %}
|
@ -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 %}
|
@ -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
|
||||
|
@ -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'),
|
||||
|
@ -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'
|
||||
|
Loading…
Reference in New Issue
Block a user