mirror of
				https://github.com/netbox-community/netbox.git
				synced 2024-05-10 07:54:54 +00:00 
			
		
		
		
	Closes #1493: Added functional roles for virtual machines
This commit is contained in:
		@@ -58,24 +58,20 @@
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>Cluster</td>
 | 
			
		||||
                    <td>Role</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        {% if vm.cluster.group %}
 | 
			
		||||
                            <a href="{{ vm.cluster.group.get_absolute_url }}">{{ vm.cluster.group }}</a>
 | 
			
		||||
                            <i class="fa fa-angle-right"></i>
 | 
			
		||||
                        {% if vm.role %}
 | 
			
		||||
                            <a href="{% url 'virtualization:virtualmachine_list' %}?role={{ vm.role.slug }}">{{ vm.role }}</a>
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                            <span class="text-muted">None</span>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                        <a href="{{ vm.cluster.get_absolute_url }}">{{ vm.cluster }}</a>
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>Cluster Type</td>
 | 
			
		||||
                    <td>{{ vm.cluster.type }}</td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>Platform</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        {% if vm.platform %}
 | 
			
		||||
                            <span>{{ vm.platform }}</span>
 | 
			
		||||
                            <a href="{% url 'virtualization:virtualmachine_list' %}?platform={{ vm.platform.slug }}">{{ vm.platform }}</a>
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                            <span class="text-muted">None</span>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
@@ -127,6 +123,27 @@
 | 
			
		||||
                </tr>
 | 
			
		||||
            </table>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="panel panel-default">
 | 
			
		||||
            <div class="panel-heading">
 | 
			
		||||
                <strong>Cluster</strong>
 | 
			
		||||
            </div>
 | 
			
		||||
            <table class="table table-hover panel-body attr-table">
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>Cluster</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        {% if vm.cluster.group %}
 | 
			
		||||
                            <a href="{{ vm.cluster.group.get_absolute_url }}">{{ vm.cluster.group }}</a>
 | 
			
		||||
                            <i class="fa fa-angle-right"></i>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                        <a href="{{ vm.cluster.get_absolute_url }}">{{ vm.cluster }}</a>
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>Cluster Type</td>
 | 
			
		||||
                    <td>{{ vm.cluster.type }}</td>
 | 
			
		||||
                </tr>
 | 
			
		||||
            </table>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="panel panel-default">
 | 
			
		||||
            <div class="panel-heading">
 | 
			
		||||
                <strong>Resources</strong>
 | 
			
		||||
 
 | 
			
		||||
@@ -7,9 +7,15 @@
 | 
			
		||||
        <div class="panel-body">
 | 
			
		||||
            {% render_field form.name %}
 | 
			
		||||
            {% render_field form.status %}
 | 
			
		||||
            {% render_field form.role %}
 | 
			
		||||
            {% render_field form.platform %}
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="panel panel-default">
 | 
			
		||||
        <div class="panel-heading"><strong>Cluster</strong></div>
 | 
			
		||||
        <div class="panel-body">
 | 
			
		||||
            {% render_field form.cluster_group %}
 | 
			
		||||
            {% render_field form.cluster %}
 | 
			
		||||
            {% render_field form.platform %}
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="panel panel-default">
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
from rest_framework import serializers
 | 
			
		||||
 | 
			
		||||
from dcim.api.serializers import NestedPlatformSerializer, NestedSiteSerializer
 | 
			
		||||
from dcim.api.serializers import NestedDeviceRoleSerializer, NestedPlatformSerializer, NestedSiteSerializer
 | 
			
		||||
from dcim.constants import VIFACE_FF_CHOICES
 | 
			
		||||
from dcim.models import Interface
 | 
			
		||||
from extras.api.customfields import CustomFieldModelSerializer
 | 
			
		||||
@@ -86,14 +86,15 @@ class WritableClusterSerializer(CustomFieldModelSerializer):
 | 
			
		||||
class VirtualMachineSerializer(CustomFieldModelSerializer):
 | 
			
		||||
    status = ChoiceFieldSerializer(choices=STATUS_CHOICES)
 | 
			
		||||
    cluster = NestedClusterSerializer()
 | 
			
		||||
    role = NestedDeviceRoleSerializer()
 | 
			
		||||
    tenant = NestedTenantSerializer()
 | 
			
		||||
    platform = NestedPlatformSerializer()
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = VirtualMachine
 | 
			
		||||
        fields = [
 | 
			
		||||
            'id', 'name', 'status', 'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory',
 | 
			
		||||
            'disk', 'comments', 'custom_fields',
 | 
			
		||||
            'id', 'name', 'status', 'cluster', 'role', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'vcpus',
 | 
			
		||||
            'memory', 'disk', 'comments', 'custom_fields',
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -110,8 +111,8 @@ class WritableVirtualMachineSerializer(CustomFieldModelSerializer):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = VirtualMachine
 | 
			
		||||
        fields = [
 | 
			
		||||
            'id', 'name', 'status', 'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory',
 | 
			
		||||
            'disk', 'comments', 'custom_fields',
 | 
			
		||||
            'id', 'name', 'status', 'cluster', 'role', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'vcpus',
 | 
			
		||||
            'memory', 'disk', 'comments', 'custom_fields',
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
 | 
			
		||||
import django_filters
 | 
			
		||||
from django.db.models import Q
 | 
			
		||||
 | 
			
		||||
from dcim.models import Platform, Site
 | 
			
		||||
from dcim.models import DeviceRole, Platform, Site
 | 
			
		||||
from extras.filters import CustomFieldFilterSet
 | 
			
		||||
from tenancy.models import Tenant
 | 
			
		||||
from utilities.filters import NullableModelMultipleChoiceFilter, NumericInFilter
 | 
			
		||||
@@ -80,10 +80,21 @@ class VirtualMachineFilter(CustomFieldFilterSet):
 | 
			
		||||
        to_field_name='slug',
 | 
			
		||||
        label='Cluster group (slug)',
 | 
			
		||||
    )
 | 
			
		||||
    cluster_id = NullableModelMultipleChoiceFilter(
 | 
			
		||||
    cluster_id = django_filters.ModelMultipleChoiceFilter(
 | 
			
		||||
        queryset=Cluster.objects.all(),
 | 
			
		||||
        label='Cluster (ID)',
 | 
			
		||||
    )
 | 
			
		||||
    role_id = NullableModelMultipleChoiceFilter(
 | 
			
		||||
        name='role_id',
 | 
			
		||||
        queryset=DeviceRole.objects.all(),
 | 
			
		||||
        label='Role (ID)',
 | 
			
		||||
    )
 | 
			
		||||
    role = NullableModelMultipleChoiceFilter(
 | 
			
		||||
        name='role__slug',
 | 
			
		||||
        queryset=DeviceRole.objects.all(),
 | 
			
		||||
        to_field_name='slug',
 | 
			
		||||
        label='Role (slug)',
 | 
			
		||||
    )
 | 
			
		||||
    tenant_id = NullableModelMultipleChoiceFilter(
 | 
			
		||||
        queryset=Tenant.objects.all(),
 | 
			
		||||
        label='Tenant (ID)',
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ from django.db.models import Count
 | 
			
		||||
 | 
			
		||||
from dcim.constants import IFACE_FF_VIRTUAL, VIFACE_FF_CHOICES
 | 
			
		||||
from dcim.formfields import MACAddressFormField
 | 
			
		||||
from dcim.models import Device, Interface, Platform, Rack, Region, Site
 | 
			
		||||
from dcim.models import Device, DeviceRole, Interface, Platform, Rack, Region, Site
 | 
			
		||||
from extras.forms import CustomFieldBulkEditForm, CustomFieldForm, CustomFieldFilterForm
 | 
			
		||||
from tenancy.forms import TenancyForm
 | 
			
		||||
from tenancy.models import Tenant
 | 
			
		||||
@@ -222,7 +222,8 @@ class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = VirtualMachine
 | 
			
		||||
        fields = [
 | 
			
		||||
            'name', 'status', 'cluster_group', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments',
 | 
			
		||||
            'name', 'status', 'cluster_group', 'cluster', 'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk',
 | 
			
		||||
            'comments',
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
@@ -251,6 +252,15 @@ class VirtualMachineCSVForm(forms.ModelForm):
 | 
			
		||||
            'invalid_choice': 'Invalid cluster name.',
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    role = forms.ModelChoiceField(
 | 
			
		||||
        queryset=DeviceRole.objects.all(),
 | 
			
		||||
        required=False,
 | 
			
		||||
        to_field_name='name',
 | 
			
		||||
        help_text='Name of functional role',
 | 
			
		||||
        error_messages={
 | 
			
		||||
            'invalid_choice': 'Invalid role name.'
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    tenant = forms.ModelChoiceField(
 | 
			
		||||
        queryset=Tenant.objects.all(),
 | 
			
		||||
        required=False,
 | 
			
		||||
@@ -272,13 +282,14 @@ class VirtualMachineCSVForm(forms.ModelForm):
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = VirtualMachine
 | 
			
		||||
        fields = ['name', 'status', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments']
 | 
			
		||||
        fields = ['name', 'status', 'cluster', 'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VirtualMachineBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
 | 
			
		||||
    pk = forms.ModelMultipleChoiceField(queryset=VirtualMachine.objects.all(), widget=forms.MultipleHiddenInput)
 | 
			
		||||
    status = forms.ChoiceField(choices=add_blank_choice(STATUS_CHOICES), required=False, initial='')
 | 
			
		||||
    cluster = forms.ModelChoiceField(queryset=Cluster.objects.all(), required=False)
 | 
			
		||||
    role = forms.ModelChoiceField(queryset=DeviceRole.objects.all(), required=False)
 | 
			
		||||
    tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
 | 
			
		||||
    platform = forms.ModelChoiceField(queryset=Platform.objects.all(), required=False)
 | 
			
		||||
    vcpus = forms.IntegerField(required=False, label='vCPUs')
 | 
			
		||||
@@ -287,7 +298,7 @@ class VirtualMachineBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
 | 
			
		||||
    comments = CommentField(widget=SmallTextarea)
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        nullable_fields = ['tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments']
 | 
			
		||||
        nullable_fields = ['role', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def vm_status_choices():
 | 
			
		||||
@@ -303,13 +314,28 @@ class VirtualMachineFilterForm(BootstrapMixin, CustomFieldFilterForm):
 | 
			
		||||
    cluster_group = FilterChoiceField(
 | 
			
		||||
        queryset=ClusterGroup.objects.all(),
 | 
			
		||||
        to_field_name='slug',
 | 
			
		||||
        null_option=(0, 'None'),
 | 
			
		||||
        null_option=(0, 'None')
 | 
			
		||||
    )
 | 
			
		||||
    cluster_id = FilterChoiceField(
 | 
			
		||||
        queryset=Cluster.objects.annotate(filter_count=Count('virtual_machines')),
 | 
			
		||||
        label='Cluster'
 | 
			
		||||
    )
 | 
			
		||||
    role = FilterChoiceField(
 | 
			
		||||
        queryset=DeviceRole.objects.annotate(filter_count=Count('virtual_machines')),
 | 
			
		||||
        to_field_name='slug',
 | 
			
		||||
        null_option=(0, 'None')
 | 
			
		||||
    )
 | 
			
		||||
    status = forms.MultipleChoiceField(choices=vm_status_choices, required=False)
 | 
			
		||||
    tenant = FilterChoiceField(
 | 
			
		||||
        queryset=Tenant.objects.annotate(filter_count=Count('virtual_machines')),
 | 
			
		||||
        to_field_name='slug',
 | 
			
		||||
        null_option=(0, 'None')
 | 
			
		||||
    )
 | 
			
		||||
    platform = FilterChoiceField(
 | 
			
		||||
        queryset=Platform.objects.annotate(filter_count=Count('virtual_machines')),
 | 
			
		||||
        to_field_name='slug',
 | 
			
		||||
        null_option=(0, 'None')
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# Generated by Django 1.11.4 on 2017-09-29 14:32
 | 
			
		||||
from __future__ import unicode_literals
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import django.db.models.deletion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ('dcim', '0044_virtualization'),
 | 
			
		||||
        ('virtualization', '0003_cluster_add_site'),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='virtualmachine',
 | 
			
		||||
            name='role',
 | 
			
		||||
            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='virtual_machines', to='dcim.DeviceRole'),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -179,6 +179,13 @@ class VirtualMachine(CreatedUpdatedModel, CustomFieldModel):
 | 
			
		||||
        default=STATUS_ACTIVE,
 | 
			
		||||
        verbose_name='Status'
 | 
			
		||||
    )
 | 
			
		||||
    role = models.ForeignKey(
 | 
			
		||||
        to='dcim.DeviceRole',
 | 
			
		||||
        on_delete=models.PROTECT,
 | 
			
		||||
        related_name='virtual_machines',
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True
 | 
			
		||||
    )
 | 
			
		||||
    primary_ip4 = models.OneToOneField(
 | 
			
		||||
        to='ipam.IPAddress',
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
 
 | 
			
		||||
@@ -95,7 +95,7 @@ class VirtualMachineTable(BaseTable):
 | 
			
		||||
 | 
			
		||||
    class Meta(BaseTable.Meta):
 | 
			
		||||
        model = VirtualMachine
 | 
			
		||||
        fields = ('pk', 'name', 'status', 'cluster', 'tenant', 'vcpus', 'memory', 'disk')
 | 
			
		||||
        fields = ('pk', 'name', 'status', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VirtualMachineDetailTable(VirtualMachineTable):
 | 
			
		||||
@@ -105,7 +105,7 @@ class VirtualMachineDetailTable(VirtualMachineTable):
 | 
			
		||||
 | 
			
		||||
    class Meta(BaseTable.Meta):
 | 
			
		||||
        model = VirtualMachine
 | 
			
		||||
        fields = ('pk', 'name', 'status', 'cluster', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip')
 | 
			
		||||
        fields = ('pk', 'name', 'status', 'cluster', 'role', 'tenant', 'vcpus', 'memory', 'disk', 'primary_ip')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user