Parcourir la source

Added views for VM interfaces

Jeremy Stretch il y a 7 ans
Parent
commit
e81e33af38

+ 65 - 0
netbox/templates/virtualization/inc/vminterface.html

@@ -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 %}

+ 61 - 2
netbox/templates/virtualization/virtualmachine.html

@@ -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 %}

+ 44 - 0
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 %}
+<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 %}

+ 47 - 2
netbox/virtualization/forms.py

@@ -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 .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')),
         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']

+ 1 - 0
netbox/virtualization/models.py

@@ -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

+ 11 - 0
netbox/virtualization/tables.py

@@ -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')

+ 8 - 0
netbox/virtualization/urls.py

@@ -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'),
+
 ]
 ]

+ 42 - 33
netbox/virtualization/views.py

@@ -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):
-#     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