Closes #1493: Added functional roles for virtual machines

This commit is contained in:
Jeremy Stretch 2017-09-29 11:13:41 -04:00
parent f49d7ce1da
commit 6243fbfd0d
8 changed files with 115 additions and 25 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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