diff --git a/netbox/templates/virtualization/virtualmachine.html b/netbox/templates/virtualization/virtualmachine.html index cd66cb105..cb83142ea 100644 --- a/netbox/templates/virtualization/virtualmachine.html +++ b/netbox/templates/virtualization/virtualmachine.html @@ -58,24 +58,20 @@ - Cluster + Role - {% if vm.cluster.group %} - {{ vm.cluster.group }} - + {% if vm.role %} + {{ vm.role }} + {% else %} + None {% endif %} - {{ vm.cluster }} - - Cluster Type - {{ vm.cluster.type }} - Platform {% if vm.platform %} - {{ vm.platform }} + {{ vm.platform }} {% else %} None {% endif %} @@ -127,6 +123,27 @@ +
+
+ Cluster +
+ + + + + + + + + +
Cluster + {% if vm.cluster.group %} + {{ vm.cluster.group }} + + {% endif %} + {{ vm.cluster }} +
Cluster Type{{ vm.cluster.type }}
+
Resources diff --git a/netbox/templates/virtualization/virtualmachine_edit.html b/netbox/templates/virtualization/virtualmachine_edit.html index 085f3cadf..7c240857f 100644 --- a/netbox/templates/virtualization/virtualmachine_edit.html +++ b/netbox/templates/virtualization/virtualmachine_edit.html @@ -7,9 +7,15 @@
{% render_field form.name %} {% render_field form.status %} + {% render_field form.role %} + {% render_field form.platform %} +
+
+
+
Cluster
+
{% render_field form.cluster_group %} {% render_field form.cluster %} - {% render_field form.platform %}
diff --git a/netbox/virtualization/api/serializers.py b/netbox/virtualization/api/serializers.py index b85495d83..68f5118bf 100644 --- a/netbox/virtualization/api/serializers.py +++ b/netbox/virtualization/api/serializers.py @@ -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', ] diff --git a/netbox/virtualization/filters.py b/netbox/virtualization/filters.py index ea7686a23..31a002009 100644 --- a/netbox/virtualization/filters.py +++ b/netbox/virtualization/filters.py @@ -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)', diff --git a/netbox/virtualization/forms.py b/netbox/virtualization/forms.py index bcae2f842..ef27ea994 100644 --- a/netbox/virtualization/forms.py +++ b/netbox/virtualization/forms.py @@ -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') + ) # diff --git a/netbox/virtualization/migrations/0004_virtualmachine_add_role.py b/netbox/virtualization/migrations/0004_virtualmachine_add_role.py new file mode 100644 index 000000000..10dec60fa --- /dev/null +++ b/netbox/virtualization/migrations/0004_virtualmachine_add_role.py @@ -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'), + ), + ] diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py index edf0385d4..47b179670 100644 --- a/netbox/virtualization/models.py +++ b/netbox/virtualization/models.py @@ -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, diff --git a/netbox/virtualization/tables.py b/netbox/virtualization/tables.py index 21314b51c..a40af6091 100644 --- a/netbox/virtualization/tables.py +++ b/netbox/virtualization/tables.py @@ -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') #