Added a status field for virtual machines

This commit is contained in:
Jeremy Stretch 2017-09-14 14:35:34 -04:00
parent d6637410a2
commit 0d8947bf36
9 changed files with 93 additions and 8 deletions

View File

@ -51,6 +51,12 @@
<td>Name</td> <td>Name</td>
<td>{{ vm.name }}</td> <td>{{ vm.name }}</td>
</tr> </tr>
<tr>
<td>Status</td>
<td>
<span class="label label-{{ vm.get_status_class }}">{{ vm.get_status_display }}</span>
</td>
</tr>
<tr> <tr>
<td>Cluster</td> <td>Cluster</td>
<td> <td>

View File

@ -6,6 +6,7 @@
<div class="panel-heading"><strong>Virtual Machine</strong></div> <div class="panel-heading"><strong>Virtual Machine</strong></div>
<div class="panel-body"> <div class="panel-body">
{% render_field form.name %} {% render_field form.name %}
{% render_field form.status %}
{% render_field form.cluster_group %} {% render_field form.cluster_group %}
{% render_field form.cluster %} {% render_field form.cluster %}
{% render_field form.platform %} {% render_field form.platform %}

View File

@ -8,6 +8,7 @@ from dcim.models import Interface
from extras.api.customfields import CustomFieldModelSerializer from extras.api.customfields import CustomFieldModelSerializer
from tenancy.api.serializers import NestedTenantSerializer from tenancy.api.serializers import NestedTenantSerializer
from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer
from virtualization.constants import STATUS_CHOICES
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
@ -82,6 +83,7 @@ class WritableClusterSerializer(CustomFieldModelSerializer):
# #
class VirtualMachineSerializer(CustomFieldModelSerializer): class VirtualMachineSerializer(CustomFieldModelSerializer):
status = ChoiceFieldSerializer(choices=STATUS_CHOICES)
cluster = NestedClusterSerializer() cluster = NestedClusterSerializer()
tenant = NestedTenantSerializer() tenant = NestedTenantSerializer()
platform = NestedPlatformSerializer() platform = NestedPlatformSerializer()
@ -89,7 +91,8 @@ class VirtualMachineSerializer(CustomFieldModelSerializer):
class Meta: class Meta:
model = VirtualMachine model = VirtualMachine
fields = [ fields = [
'id', 'name', 'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'comments', 'custom_fields', 'id', 'name', 'status', 'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'comments',
'custom_fields',
] ]
@ -106,7 +109,8 @@ class WritableVirtualMachineSerializer(CustomFieldModelSerializer):
class Meta: class Meta:
model = VirtualMachine model = VirtualMachine
fields = [ fields = [
'id', 'name', 'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'comments', 'custom_fields', 'id', 'name', 'status', 'cluster', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'comments',
'custom_fields',
] ]

View File

@ -0,0 +1,19 @@
from __future__ import unicode_literals
# VirtualMachine statuses (replicated from Device statuses)
STATUS_OFFLINE = 0
STATUS_ACTIVE = 1
STATUS_STAGED = 3
STATUS_CHOICES = [
[STATUS_ACTIVE, 'Active'],
[STATUS_OFFLINE, 'Offline'],
[STATUS_STAGED, 'Staged'],
]
# Bootstrap CSS classes for VirtualMachine statuses
VM_STATUS_CLASSES = {
0: 'warning',
1: 'success',
3: 'primary',
}

View File

@ -7,6 +7,7 @@ from dcim.models import Platform
from extras.filters import CustomFieldFilterSet from extras.filters import CustomFieldFilterSet
from tenancy.models import Tenant from tenancy.models import Tenant
from utilities.filters import NullableModelMultipleChoiceFilter, NumericInFilter from utilities.filters import NullableModelMultipleChoiceFilter, NumericInFilter
from .constants import STATUS_CHOICES
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
@ -55,6 +56,9 @@ class VirtualMachineFilter(CustomFieldFilterSet):
method='search', method='search',
label='Search', label='Search',
) )
status = django_filters.MultipleChoiceFilter(
choices=STATUS_CHOICES
)
cluster_group_id = NullableModelMultipleChoiceFilter( cluster_group_id = NullableModelMultipleChoiceFilter(
name='cluster__group', name='cluster__group',
queryset=ClusterGroup.objects.all(), queryset=ClusterGroup.objects.all(),

View File

@ -12,10 +12,11 @@ from extras.forms import CustomFieldBulkEditForm, CustomFieldForm, CustomFieldFi
from tenancy.forms import TenancyForm from tenancy.forms import TenancyForm
from tenancy.models import Tenant from tenancy.models import Tenant
from utilities.forms import ( from utilities.forms import (
APISelect, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ChainedFieldsMixin, add_blank_choice, APISelect, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect,
ChainedModelChoiceField, ChainedModelMultipleChoiceField, CommentField, ComponentForm, ConfirmationForm, ChainedFieldsMixin, ChainedModelChoiceField, ChainedModelMultipleChoiceField, CommentField, ComponentForm,
ExpandableNameField, FilterChoiceField, SlugField, SmallTextarea, ConfirmationForm, CSVChoiceField, ExpandableNameField, FilterChoiceField, SlugField, SmallTextarea,
) )
from .constants import STATUS_CHOICES
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
@ -185,7 +186,9 @@ class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldForm):
class Meta: class Meta:
model = VirtualMachine model = VirtualMachine
fields = ['name', 'cluster_group', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments'] fields = [
'name', 'status', 'cluster_group', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments',
]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -200,6 +203,11 @@ class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldForm):
class VirtualMachineCSVForm(forms.ModelForm): class VirtualMachineCSVForm(forms.ModelForm):
status = CSVChoiceField(
choices=STATUS_CHOICES,
required=False,
help_text='Operational status of device'
)
cluster = forms.ModelChoiceField( cluster = forms.ModelChoiceField(
queryset=Cluster.objects.all(), queryset=Cluster.objects.all(),
to_field_name='name', to_field_name='name',
@ -229,11 +237,12 @@ class VirtualMachineCSVForm(forms.ModelForm):
class Meta: class Meta:
model = VirtualMachine model = VirtualMachine
fields = ['name', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments'] fields = ['name', 'status', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments']
class VirtualMachineBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm): class VirtualMachineBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
pk = forms.ModelMultipleChoiceField(queryset=VirtualMachine.objects.all(), widget=forms.MultipleHiddenInput) 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) cluster = forms.ModelChoiceField(queryset=Cluster.objects.all(), required=False)
tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False) tenant = forms.ModelChoiceField(queryset=Tenant.objects.all(), required=False)
platform = forms.ModelChoiceField(queryset=Platform.objects.all(), required=False) platform = forms.ModelChoiceField(queryset=Platform.objects.all(), required=False)
@ -246,6 +255,13 @@ class VirtualMachineBulkEditForm(BootstrapMixin, CustomFieldBulkEditForm):
nullable_fields = ['tenant', 'platform', 'vcpus', 'memory', 'disk'] nullable_fields = ['tenant', 'platform', 'vcpus', 'memory', 'disk']
def vm_status_choices():
status_counts = {}
for status in VirtualMachine.objects.values('status').annotate(count=Count('status')).order_by('status'):
status_counts[status['status']] = status['count']
return [(s[0], '{} ({})'.format(s[1], status_counts.get(s[0], 0))) for s in STATUS_CHOICES]
class VirtualMachineFilterForm(BootstrapMixin, CustomFieldFilterForm): class VirtualMachineFilterForm(BootstrapMixin, CustomFieldFilterForm):
model = VirtualMachine model = VirtualMachine
q = forms.CharField(required=False, label='Search') q = forms.CharField(required=False, label='Search')
@ -258,6 +274,7 @@ 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'
) )
status = forms.MultipleChoiceField(choices=vm_status_choices, required=False)
# #

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2017-09-14 17:49
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('virtualization', '0001_virtualization'),
]
operations = [
migrations.AddField(
model_name='virtualmachine',
name='status',
field=models.PositiveSmallIntegerField(choices=[[1, 'Active'], [0, 'Offline'], [3, 'Staged']], default=1, verbose_name='Status'),
),
]

View File

@ -7,6 +7,7 @@ from django.utils.encoding import python_2_unicode_compatible
from extras.models import CustomFieldModel, CustomFieldValue from extras.models import CustomFieldModel, CustomFieldValue
from utilities.models import CreatedUpdatedModel from utilities.models import CreatedUpdatedModel
from .constants import STATUS_ACTIVE, STATUS_CHOICES, VM_STATUS_CLASSES
# #
@ -139,6 +140,11 @@ class VirtualMachine(CreatedUpdatedModel, CustomFieldModel):
max_length=64, max_length=64,
unique=True unique=True
) )
status = models.PositiveSmallIntegerField(
choices=STATUS_CHOICES,
default=STATUS_ACTIVE,
verbose_name='Status'
)
primary_ip4 = models.OneToOneField( primary_ip4 = models.OneToOneField(
to='ipam.IPAddress', to='ipam.IPAddress',
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
@ -187,3 +193,6 @@ class VirtualMachine(CreatedUpdatedModel, CustomFieldModel):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('virtualization:virtualmachine', args=[self.pk]) return reverse('virtualization:virtualmachine', args=[self.pk])
def get_status_class(self):
return VM_STATUS_CLASSES[self.status]

View File

@ -20,6 +20,10 @@ CLUSTERGROUP_ACTIONS = """
{% endif %} {% endif %}
""" """
VIRTUALMACHINE_STATUS = """
<span class="label label-{{ record.get_status_class }}">{{ record.get_status_display }}</span>
"""
# #
# Cluster types # Cluster types
@ -79,12 +83,13 @@ class ClusterTable(BaseTable):
class VirtualMachineTable(BaseTable): class VirtualMachineTable(BaseTable):
pk = ToggleColumn() pk = ToggleColumn()
name = tables.LinkColumn() name = tables.LinkColumn()
status = tables.TemplateColumn(template_code=VIRTUALMACHINE_STATUS)
cluster = tables.LinkColumn('virtualization:cluster', args=[Accessor('cluster.pk')]) cluster = tables.LinkColumn('virtualization:cluster', args=[Accessor('cluster.pk')])
tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')]) tenant = tables.LinkColumn('tenancy:tenant', args=[Accessor('tenant.slug')])
class Meta(BaseTable.Meta): class Meta(BaseTable.Meta):
model = VirtualMachine model = VirtualMachine
fields = ('pk', 'name', 'cluster', 'tenant', 'vcpus', 'memory', 'disk') fields = ('pk', 'name', 'status', 'cluster', 'tenant', 'vcpus', 'memory', 'disk')
# #