mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-23 04:22:01 -06:00
Added views for VM interfaces
This commit is contained in:
parent
a7c56eab86
commit
e81e33af38
65
netbox/templates/virtualization/inc/vminterface.html
Normal file
65
netbox/templates/virtualization/inc/vminterface.html
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<tr class="interface{% if not iface.enabled %} danger{% endif %}">
|
||||||
|
{% if selectable and perms.virtualization.change_vminterface or perms.virtualization.delete_vminterface %}
|
||||||
|
<td class="pk">
|
||||||
|
<input name="pk" type="checkbox" value="{{ iface.pk }}" />
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
<td>
|
||||||
|
<i class="fa fa-fw fa-square"></i> <span>{{ iface.name }}</span>
|
||||||
|
{% if iface.description %}
|
||||||
|
<i class="fa fa-fw fa-comment-o" title="{{ iface.description }}"></i>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>{{ iface.mtu|default:"" }}</td>
|
||||||
|
<td>{{ iface.mac_address|default:"" }}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{% if perms.virtualization.change_vminterface %}
|
||||||
|
<a href="{% url 'virtualization:vminterface_edit' pk=iface.pk %}" class="btn btn-info btn-xs" title="Edit interface">
|
||||||
|
<i class="glyphicon glyphicon-pencil" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.virtualization.delete_vminterface %}
|
||||||
|
<a href="{% url 'virtualization:vminterface_delete' pk=iface.pk %}" class="btn btn-danger btn-xs" title="Delete interface">
|
||||||
|
<i class="glyphicon glyphicon-trash" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% for ip in iface.ip_addresses.all %}
|
||||||
|
<tr class="ipaddress">
|
||||||
|
{% if selectable and perms.dcim.change_interface or perms.dcim.delete_interface %}
|
||||||
|
<td></td>
|
||||||
|
{% endif %}
|
||||||
|
<td colspan="3">
|
||||||
|
<a href="{% url 'ipam:ipaddress' pk=ip.pk %}">{{ ip }}</a>
|
||||||
|
{% if ip.description %}
|
||||||
|
<i class="fa fa-fw fa-comment-o" title="{{ ip.description }}"></i>
|
||||||
|
{% endif %}
|
||||||
|
{% if device.primary_ip4 == ip or device.primary_ip6 == ip %}
|
||||||
|
<span class="label label-success">Primary</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{% if ip.vrf %}
|
||||||
|
<a href="{% url 'ipam:vrf' pk=ip.vrf.pk %}">{{ ip.vrf }}</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">Global</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="label label-{{ ip.get_status_class }}">{{ ip.get_status_display }}</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{% if perms.ipam.change_ipaddress %}
|
||||||
|
<a href="{% url 'ipam:ipaddress_edit' pk=ip.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-info btn-xs">
|
||||||
|
<i class="glyphicon glyphicon-pencil" aria-hidden="true" title="Edit IP address"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.ipam.delete_ipaddress %}
|
||||||
|
<a href="{% url 'ipam:ipaddress_delete' pk=ip.pk %}?return_url={{ device.get_absolute_url }}" class="btn btn-danger btn-xs">
|
||||||
|
<i class="glyphicon glyphicon-trash" aria-hidden="true" title="Delete IP address"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
@ -41,7 +41,7 @@
|
|||||||
<h1>{% block title %}{{ vm }}{% endblock %}</h1>
|
<h1>{% block title %}{{ vm }}{% endblock %}</h1>
|
||||||
{% include 'inc/created_updated.html' with obj=vm %}
|
{% include 'inc/created_updated.html' with obj=vm %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-7">
|
<div class="col-md-5">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<strong>Virtual Machine</strong>
|
<strong>Virtual Machine</strong>
|
||||||
@ -132,7 +132,66 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-5">
|
<div class="col-md-7">
|
||||||
|
{% if perms.virtualization.change_vminterface or perms.virtualization.delete_vminterface %}
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="virtual_machine" value="{{ vm.pk }}" />
|
||||||
|
{% endif %}
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>Interfaces</strong>
|
||||||
|
<div class="pull-right">
|
||||||
|
<button class="btn btn-default btn-xs toggle-ips" selected="selected">
|
||||||
|
<span class="glyphicon glyphicon-check" aria-hidden="true"></span> Show IPs
|
||||||
|
</button>
|
||||||
|
{% if perms.virtualization.change_vminterface and interfaces|length > 1 %}
|
||||||
|
<button class="btn btn-default btn-xs toggle">
|
||||||
|
<span class="glyphicon glyphicon-unchecked" aria-hidden="true"></span> Select all
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.virtualization.add_vminterface and interfaces|length > 10 %}
|
||||||
|
<a href="{% url 'virtualization:vminterface_add' pk=vm.pk %}" class="btn btn-primary btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add interfaces
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table id="interfaces_table" class="table table-hover panel-body component-list">
|
||||||
|
{% for iface in interfaces %}
|
||||||
|
{% include 'virtualization/inc/vminterface.html' with selectable=True %}
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">No interfaces defined</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% if perms.virtualization.add_vminterface or perms.virtualization.delete_vminterface %}
|
||||||
|
<div class="panel-footer">
|
||||||
|
{% if interfaces and perms.virtualization.change_vminterface %}
|
||||||
|
<button type="submit" name="_edit" formaction="{% url 'virtualization:vminterface_bulk_edit' pk=vm.pk %}" class="btn btn-warning btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-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' pk=vm.pk %}" class="btn btn-danger btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.virtualization.add_vminterface %}
|
||||||
|
<div class="pull-right">
|
||||||
|
<a href="{% url 'virtualization:vminterface_add' pk=vm.pk %}" class="btn btn-primary btn-xs">
|
||||||
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> Add interfaces
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="clearfix"></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if perms.virtualization.delete_vminterface %}
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
{% extends '_base.html' %}
|
||||||
|
{% load helpers %}
|
||||||
|
{% load form_helpers %}
|
||||||
|
|
||||||
|
{% block title %}Create {{ component_type }} ({{ parent }}){% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form action="." method="post" class="form form-horizontal">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 col-md-offset-3">
|
||||||
|
{% if form.non_field_errors %}
|
||||||
|
<div class="panel panel-danger">
|
||||||
|
<div class="panel-heading"><strong>Errors</strong></div>
|
||||||
|
<div class="panel-body">
|
||||||
|
{{ form.non_field_errors }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<strong>{{ component_type|bettertitle }}</strong>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-3 control-label required">Virtual Machine</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<p class="form-control-static">{{ parent }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% render_form form %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-md-9 col-md-offset-3">
|
||||||
|
<button type="submit" name="_create" class="btn btn-primary">Create</button>
|
||||||
|
<button type="submit" name="_addanother" class="btn btn-primary">Create and Add More</button>
|
||||||
|
<a href="{{ return_url }}" class="btn btn-default">Cancel</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
@ -3,11 +3,15 @@ from __future__ import unicode_literals
|
|||||||
from django import forms
|
from django import forms
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
|
|
||||||
|
from dcim.formfields import MACAddressFormField
|
||||||
from extras.forms import CustomFieldBulkEditForm, CustomFieldForm, CustomFieldFilterForm
|
from extras.forms import CustomFieldBulkEditForm, CustomFieldForm, CustomFieldFilterForm
|
||||||
from tenancy.forms import TenancyForm
|
from tenancy.forms import TenancyForm
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import APISelect, BootstrapMixin, ChainedModelChoiceField, FilterChoiceField, SlugField
|
from utilities.forms import (
|
||||||
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
|
APISelect, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ChainedModelChoiceField, ComponentForm,
|
||||||
|
ExpandableNameField, FilterChoiceField, SlugField,
|
||||||
|
)
|
||||||
|
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine, VMInterface
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -157,3 +161,44 @@ class VirtualMachineFilterForm(BootstrapMixin, CustomFieldFilterForm):
|
|||||||
queryset=Cluster.objects.annotate(filter_count=Count('virtual_machines')),
|
queryset=Cluster.objects.annotate(filter_count=Count('virtual_machines')),
|
||||||
label='Cluster'
|
label='Cluster'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# VM interfaces
|
||||||
|
#
|
||||||
|
|
||||||
|
class VMInterfaceForm(BootstrapMixin, forms.ModelForm):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = VMInterface
|
||||||
|
fields = ['virtual_machine', 'name', 'enabled', 'mac_address', 'mtu', 'description']
|
||||||
|
widgets = {
|
||||||
|
'virtual_machine': forms.HiddenInput(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class VMInterfaceCreateForm(ComponentForm):
|
||||||
|
name_pattern = ExpandableNameField(label='Name')
|
||||||
|
enabled = forms.BooleanField(required=False)
|
||||||
|
mtu = forms.IntegerField(required=False, min_value=1, max_value=32767, label='MTU')
|
||||||
|
mac_address = MACAddressFormField(required=False, label='MAC Address')
|
||||||
|
description = forms.CharField(max_length=100, required=False)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
||||||
|
# Set interfaces enabled by default
|
||||||
|
kwargs['initial'] = kwargs.get('initial', {}).copy()
|
||||||
|
kwargs['initial'].update({'enabled': True})
|
||||||
|
|
||||||
|
super(VMInterfaceCreateForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class VMInterfaceBulkEditForm(BootstrapMixin, BulkEditForm):
|
||||||
|
pk = forms.ModelMultipleChoiceField(queryset=VMInterface.objects.all(), widget=forms.MultipleHiddenInput)
|
||||||
|
virtual_machine = forms.ModelChoiceField(queryset=VirtualMachine.objects.all(), widget=forms.HiddenInput)
|
||||||
|
enabled = forms.NullBooleanField(required=False, widget=BulkEditNullBooleanSelect)
|
||||||
|
mtu = forms.IntegerField(required=False, min_value=1, max_value=32767, label='MTU')
|
||||||
|
description = forms.CharField(max_length=100, required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
nullable_fields = ['mtu', 'description']
|
||||||
|
@ -225,6 +225,7 @@ class VMInterface(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['virtual_machine', 'name']
|
ordering = ['virtual_machine', 'name']
|
||||||
unique_together = ['virtual_machine', 'name']
|
unique_together = ['virtual_machine', 'name']
|
||||||
|
verbose_name = 'VM interface'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -83,3 +83,14 @@ class VirtualMachineTable(BaseTable):
|
|||||||
class Meta(BaseTable.Meta):
|
class Meta(BaseTable.Meta):
|
||||||
model = VirtualMachine
|
model = VirtualMachine
|
||||||
fields = ('pk', 'name', 'cluster', 'tenant', 'vcpus', 'memory', 'disk')
|
fields = ('pk', 'name', 'cluster', 'tenant', 'vcpus', 'memory', 'disk')
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# VM components
|
||||||
|
#
|
||||||
|
|
||||||
|
class VMInterfaceTable(BaseTable):
|
||||||
|
|
||||||
|
class Meta(BaseTable.Meta):
|
||||||
|
model = VMInterface
|
||||||
|
fields = ('name', 'enabled', 'description')
|
||||||
|
@ -38,4 +38,12 @@ urlpatterns = [
|
|||||||
url(r'^virtual-machines/(?P<pk>\d+)/edit/$', views.VirtualMachineEditView.as_view(), name='virtualmachine_edit'),
|
url(r'^virtual-machines/(?P<pk>\d+)/edit/$', views.VirtualMachineEditView.as_view(), name='virtualmachine_edit'),
|
||||||
url(r'^virtual-machines/(?P<pk>\d+)/delete/$', views.VirtualMachineDeleteView.as_view(), name='virtualmachine_delete'),
|
url(r'^virtual-machines/(?P<pk>\d+)/delete/$', views.VirtualMachineDeleteView.as_view(), name='virtualmachine_delete'),
|
||||||
|
|
||||||
|
# VM interfaces
|
||||||
|
# url(r'^virtual-machines/interfaces/add/$', views.VMBulkAddVMInterfaceView.as_view(), name='vm_bulk_add_vminterface'),
|
||||||
|
url(r'^virtual-machines/(?P<pk>\d+)/interfaces/add/$', views.VMInterfaceCreateView.as_view(), name='vminterface_add'),
|
||||||
|
url(r'^virtual-machines/(?P<pk>\d+)/interfaces/edit/$', views.VMInterfaceBulkEditView.as_view(), name='vminterface_bulk_edit'),
|
||||||
|
url(r'^virtual-machines/(?P<pk>\d+)/interfaces/delete/$', views.VMInterfaceBulkDeleteView.as_view(), name='vminterface_bulk_delete'),
|
||||||
|
url(r'^vm-interfaces/(?P<pk>\d+)/edit/$', views.VMInterfaceEditView.as_view(), name='vminterface_edit'),
|
||||||
|
url(r'^vm-interfaces/(?P<pk>\d+)/delete/$', views.VMInterfaceDeleteView.as_view(), name='vminterface_delete'),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@ -155,9 +155,11 @@ class VirtualMachineView(View):
|
|||||||
def get(self, request, pk):
|
def get(self, request, pk):
|
||||||
|
|
||||||
vm = get_object_or_404(VirtualMachine.objects.select_related('tenant__group'), pk=pk)
|
vm = get_object_or_404(VirtualMachine.objects.select_related('tenant__group'), pk=pk)
|
||||||
|
interfaces = VMInterface.objects.filter(virtual_machine=vm)
|
||||||
|
|
||||||
return render(request, 'virtualization/virtualmachine.html', {
|
return render(request, 'virtualization/virtualmachine.html', {
|
||||||
'vm': vm,
|
'vm': vm,
|
||||||
|
'interfaces': interfaces,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -200,36 +202,43 @@ class VirtualMachineBulkEditView(PermissionRequiredMixin, BulkEditView):
|
|||||||
# VM interfaces
|
# VM interfaces
|
||||||
#
|
#
|
||||||
|
|
||||||
# class VMInterfaceCreateView(PermissionRequiredMixin, ComponentCreateView):
|
class VMInterfaceCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||||
# permission_required = 'virtualization.add_vminterface'
|
permission_required = 'virtualization.add_vminterface'
|
||||||
# parent_model = VirtualMachine
|
parent_model = VirtualMachine
|
||||||
# parent_field = 'vm'
|
parent_field = 'virtual_machine'
|
||||||
# model = VMInterface
|
model = VMInterface
|
||||||
# form = forms.VMInterfaceCreateForm
|
form = forms.VMInterfaceCreateForm
|
||||||
# model_form = forms.VMInterfaceForm
|
model_form = forms.VMInterfaceForm
|
||||||
#
|
template_name = 'virtualization/virtualmachine_component_add.html'
|
||||||
#
|
|
||||||
# class VMInterfaceEditView(PermissionRequiredMixin, ComponentEditView):
|
|
||||||
# permission_required = 'virtualization.change_vminterface'
|
class VMInterfaceEditView(PermissionRequiredMixin, ObjectEditView):
|
||||||
# model = VMInterface
|
permission_required = 'virtualization.change_vminterface'
|
||||||
# form_class = forms.VMInterfaceForm
|
model = VMInterface
|
||||||
#
|
form_class = forms.VMInterfaceForm
|
||||||
#
|
|
||||||
# class VMInterfaceDeleteView(PermissionRequiredMixin, ComponentDeleteView):
|
def get_return_url(self, request, obj):
|
||||||
# permission_required = 'virtualization.delete_vminterface'
|
return obj.virtual_machine.get_absolute_url()
|
||||||
# model = VMInterface
|
|
||||||
#
|
|
||||||
#
|
class VMInterfaceDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||||
# class VMInterfaceBulkEditView(PermissionRequiredMixin, BulkEditView):
|
permission_required = 'virtualization.delete_vminterface'
|
||||||
# permission_required = 'virtualization.change_vminterface'
|
model = VMInterface
|
||||||
# cls = VMInterface
|
|
||||||
# parent_cls = VirtualMachine
|
def get_return_url(self, request, obj):
|
||||||
# table = tables.VMInterfaceTable
|
return obj.virtual_machine.get_absolute_url()
|
||||||
# form = forms.VMInterfaceBulkEditForm
|
|
||||||
#
|
|
||||||
#
|
class VMInterfaceBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||||
# class VMInterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
permission_required = 'virtualization.change_vminterface'
|
||||||
# permission_required = 'virtualization.delete_vminterface'
|
cls = VMInterface
|
||||||
# cls = VMInterface
|
parent_cls = VirtualMachine
|
||||||
# parent_cls = VirtualMachine
|
table = tables.VMInterfaceTable
|
||||||
# table = tables.VMInterfaceTable
|
form = forms.VMInterfaceBulkEditForm
|
||||||
|
|
||||||
|
|
||||||
|
class VMInterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||||
|
permission_required = 'virtualization.delete_vminterface'
|
||||||
|
cls = VMInterface
|
||||||
|
parent_cls = VirtualMachine
|
||||||
|
table = tables.VMInterfaceTable
|
||||||
|
Loading…
Reference in New Issue
Block a user