+
Virtual Machine
@@ -132,7 +132,66 @@
-
+
+ {% if perms.virtualization.change_vminterface or perms.virtualization.delete_vminterface %}
+
+ {% endif %}
{% endblock %}
diff --git a/netbox/templates/virtualization/virtualmachine_component_add.html b/netbox/templates/virtualization/virtualmachine_component_add.html
new file mode 100644
index 000000000..7cac56705
--- /dev/null
+++ b/netbox/templates/virtualization/virtualmachine_component_add.html
@@ -0,0 +1,44 @@
+{% extends '_base.html' %}
+{% load helpers %}
+{% load form_helpers %}
+
+{% block title %}Create {{ component_type }} ({{ parent }}){% endblock %}
+
+{% block content %}
+
+{% endblock %}
diff --git a/netbox/virtualization/forms.py b/netbox/virtualization/forms.py
index 7dbf7dcb3..cb2288416 100644
--- a/netbox/virtualization/forms.py
+++ b/netbox/virtualization/forms.py
@@ -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']
diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py
index 7cdd46158..ce5f266d8 100644
--- a/netbox/virtualization/models.py
+++ b/netbox/virtualization/models.py
@@ -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
diff --git a/netbox/virtualization/tables.py b/netbox/virtualization/tables.py
index 3794a59b5..97261f3ae 100644
--- a/netbox/virtualization/tables.py
+++ b/netbox/virtualization/tables.py
@@ -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')
diff --git a/netbox/virtualization/urls.py b/netbox/virtualization/urls.py
index 81038e3d4..8542919a4 100644
--- a/netbox/virtualization/urls.py
+++ b/netbox/virtualization/urls.py
@@ -38,4 +38,12 @@ urlpatterns = [
url(r'^virtual-machines/(?P
\d+)/edit/$', views.VirtualMachineEditView.as_view(), name='virtualmachine_edit'),
url(r'^virtual-machines/(?P\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\d+)/interfaces/add/$', views.VMInterfaceCreateView.as_view(), name='vminterface_add'),
+ url(r'^virtual-machines/(?P\d+)/interfaces/edit/$', views.VMInterfaceBulkEditView.as_view(), name='vminterface_bulk_edit'),
+ url(r'^virtual-machines/(?P\d+)/interfaces/delete/$', views.VMInterfaceBulkDeleteView.as_view(), name='vminterface_bulk_delete'),
+ url(r'^vm-interfaces/(?P\d+)/edit/$', views.VMInterfaceEditView.as_view(), name='vminterface_edit'),
+ url(r'^vm-interfaces/(?P\d+)/delete/$', views.VMInterfaceDeleteView.as_view(), name='vminterface_delete'),
+
]
diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py
index 8d6388445..a46a55cae 100644
--- a/netbox/virtualization/views.py
+++ b/netbox/virtualization/views.py
@@ -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