mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
Closes #1493: Added functional roles for virtual machines
This commit is contained in:
parent
f49d7ce1da
commit
6243fbfd0d
@ -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')
|
||||
|
||||
|
||||
#
|
||||
|
Loading…
Reference in New Issue
Block a user