mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -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>
|
||||
{% include 'inc/created_updated.html' with obj=vm %}
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<div class="col-md-5">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Virtual Machine</strong>
|
||||
@ -132,7 +132,66 @@
|
||||
</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>
|
||||
{% 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.db.models import Count
|
||||
|
||||
from dcim.formfields import MACAddressFormField
|
||||
from extras.forms import CustomFieldBulkEditForm, CustomFieldForm, CustomFieldFilterForm
|
||||
from tenancy.forms import TenancyForm
|
||||
from tenancy.models import Tenant
|
||||
from utilities.forms import APISelect, BootstrapMixin, ChainedModelChoiceField, FilterChoiceField, SlugField
|
||||
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
|
||||
from utilities.forms import (
|
||||
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')),
|
||||
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:
|
||||
ordering = ['virtual_machine', 'name']
|
||||
unique_together = ['virtual_machine', 'name']
|
||||
verbose_name = 'VM interface'
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -83,3 +83,14 @@ class VirtualMachineTable(BaseTable):
|
||||
class Meta(BaseTable.Meta):
|
||||
model = VirtualMachine
|
||||
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+)/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):
|
||||
|
||||
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', {
|
||||
'vm': vm,
|
||||
'interfaces': interfaces,
|
||||
})
|
||||
|
||||
|
||||
@ -200,36 +202,43 @@ class VirtualMachineBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
# VM interfaces
|
||||
#
|
||||
|
||||
# class VMInterfaceCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||
# permission_required = 'virtualization.add_vminterface'
|
||||
# parent_model = VirtualMachine
|
||||
# parent_field = 'vm'
|
||||
# model = VMInterface
|
||||
# form = forms.VMInterfaceCreateForm
|
||||
# model_form = forms.VMInterfaceForm
|
||||
#
|
||||
#
|
||||
# class VMInterfaceEditView(PermissionRequiredMixin, ComponentEditView):
|
||||
# permission_required = 'virtualization.change_vminterface'
|
||||
# model = VMInterface
|
||||
# form_class = forms.VMInterfaceForm
|
||||
#
|
||||
#
|
||||
# class VMInterfaceDeleteView(PermissionRequiredMixin, ComponentDeleteView):
|
||||
# permission_required = 'virtualization.delete_vminterface'
|
||||
# model = VMInterface
|
||||
#
|
||||
#
|
||||
# class VMInterfaceBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
# permission_required = 'virtualization.change_vminterface'
|
||||
# cls = VMInterface
|
||||
# parent_cls = VirtualMachine
|
||||
# table = tables.VMInterfaceTable
|
||||
# form = forms.VMInterfaceBulkEditForm
|
||||
#
|
||||
#
|
||||
# class VMInterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
|
||||
# permission_required = 'virtualization.delete_vminterface'
|
||||
# cls = VMInterface
|
||||
# parent_cls = VirtualMachine
|
||||
# table = tables.VMInterfaceTable
|
||||
class VMInterfaceCreateView(PermissionRequiredMixin, ComponentCreateView):
|
||||
permission_required = 'virtualization.add_vminterface'
|
||||
parent_model = VirtualMachine
|
||||
parent_field = 'virtual_machine'
|
||||
model = VMInterface
|
||||
form = forms.VMInterfaceCreateForm
|
||||
model_form = forms.VMInterfaceForm
|
||||
template_name = 'virtualization/virtualmachine_component_add.html'
|
||||
|
||||
|
||||
class VMInterfaceEditView(PermissionRequiredMixin, ObjectEditView):
|
||||
permission_required = 'virtualization.change_vminterface'
|
||||
model = VMInterface
|
||||
form_class = forms.VMInterfaceForm
|
||||
|
||||
def get_return_url(self, request, obj):
|
||||
return obj.virtual_machine.get_absolute_url()
|
||||
|
||||
|
||||
class VMInterfaceDeleteView(PermissionRequiredMixin, ObjectDeleteView):
|
||||
permission_required = 'virtualization.delete_vminterface'
|
||||
model = VMInterface
|
||||
|
||||
def get_return_url(self, request, obj):
|
||||
return obj.virtual_machine.get_absolute_url()
|
||||
|
||||
|
||||
class VMInterfaceBulkEditView(PermissionRequiredMixin, BulkEditView):
|
||||
permission_required = 'virtualization.change_vminterface'
|
||||
cls = VMInterface
|
||||
parent_cls = VirtualMachine
|
||||
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