Browse Source

Added a status field for virtual machines

Jeremy Stretch 7 years ago
parent
commit
6872ab0e14

+ 6 - 0
netbox/templates/virtualization/virtualmachine.html

@@ -52,6 +52,12 @@
                     <td>{{ vm.name }}</td>
                 </tr>
                 <tr>
+                    <td>Status</td>
+                    <td>
+                        <span class="label label-{{ vm.get_status_class }}">{{ vm.get_status_display }}</span>
+                    </td>
+                </tr>
+                <tr>
                     <td>Cluster</td>
                     <td>
                         {% if vm.cluster.group %}

+ 1 - 0
netbox/templates/virtualization/virtualmachine_edit.html

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

+ 6 - 2
netbox/virtualization/api/serializers.py

@@ -8,6 +8,7 @@ from dcim.models import Interface
 from extras.api.customfields import CustomFieldModelSerializer
 from tenancy.api.serializers import NestedTenantSerializer
 from utilities.api import ChoiceFieldSerializer, ValidatedModelSerializer
+from virtualization.constants import STATUS_CHOICES
 from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 
 
@@ -82,6 +83,7 @@ class WritableClusterSerializer(CustomFieldModelSerializer):
 #
 
 class VirtualMachineSerializer(CustomFieldModelSerializer):
+    status = ChoiceFieldSerializer(choices=STATUS_CHOICES)
     cluster = NestedClusterSerializer()
     tenant = NestedTenantSerializer()
     platform = NestedPlatformSerializer()
@@ -89,7 +91,8 @@ class VirtualMachineSerializer(CustomFieldModelSerializer):
     class Meta:
         model = VirtualMachine
         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:
         model = VirtualMachine
         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',
         ]
 
 

+ 19 - 0
netbox/virtualization/constants.py

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

+ 4 - 0
netbox/virtualization/filters.py

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

+ 22 - 5
netbox/virtualization/forms.py

@@ -12,10 +12,11 @@ from extras.forms import CustomFieldBulkEditForm, CustomFieldForm, CustomFieldFi
 from tenancy.forms import TenancyForm
 from tenancy.models import Tenant
 from utilities.forms import (
-    APISelect, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect, ChainedFieldsMixin,
-    ChainedModelChoiceField, ChainedModelMultipleChoiceField, CommentField, ComponentForm, ConfirmationForm,
-    ExpandableNameField, FilterChoiceField, SlugField, SmallTextarea,
+    add_blank_choice, APISelect, APISelectMultiple, BootstrapMixin, BulkEditForm, BulkEditNullBooleanSelect,
+    ChainedFieldsMixin, ChainedModelChoiceField, ChainedModelMultipleChoiceField, CommentField, ComponentForm,
+    ConfirmationForm, CSVChoiceField, ExpandableNameField, FilterChoiceField, SlugField, SmallTextarea,
 )
+from .constants import STATUS_CHOICES
 from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
 
 
@@ -185,7 +186,9 @@ class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldForm):
 
     class Meta:
         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):
 
@@ -200,6 +203,11 @@ class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldForm):
 
 
 class VirtualMachineCSVForm(forms.ModelForm):
+    status = CSVChoiceField(
+        choices=STATUS_CHOICES,
+        required=False,
+        help_text='Operational status of device'
+    )
     cluster = forms.ModelChoiceField(
         queryset=Cluster.objects.all(),
         to_field_name='name',
@@ -229,11 +237,12 @@ class VirtualMachineCSVForm(forms.ModelForm):
 
     class Meta:
         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):
     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)
     tenant = forms.ModelChoiceField(queryset=Tenant.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']
 
 
+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):
     model = VirtualMachine
     q = forms.CharField(required=False, label='Search')
@@ -258,6 +274,7 @@ class VirtualMachineFilterForm(BootstrapMixin, CustomFieldFilterForm):
         queryset=Cluster.objects.annotate(filter_count=Count('virtual_machines')),
         label='Cluster'
     )
+    status = forms.MultipleChoiceField(choices=vm_status_choices, required=False)
 
 
 #

+ 20 - 0
netbox/virtualization/migrations/0002_virtualmachine_add_status.py

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

+ 9 - 0
netbox/virtualization/models.py

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

+ 6 - 1
netbox/virtualization/tables.py

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