Use existing disk field to store aggregate virtual disk size

This commit is contained in:
Jeremy Stretch 2023-11-14 15:14:19 -05:00
parent f4aa2e9f62
commit 7eb6b2fc47
6 changed files with 60 additions and 11 deletions

View File

@ -145,12 +145,6 @@
<td>
{% if object.disk %}
{{ object.disk }} {% trans "GB" context "Abbreviation for gigabyte" %}
<span class="text-warning">
<i class="mdi mdi-alert" title="{% trans "This field has been deprecated and will be removed in a future release." %}"></i>
</span>
{% elif object.disk_size %}
{{ object.disk_size }} {% trans "GB" context "Abbreviation for gigabyte" %}
({{ object.virtualdisks.count }} {% trans "disks" %})
{% else %}
{{ ''|placeholder }}
{% endif %}

View File

@ -5,7 +5,7 @@ class VirtualizationConfig(AppConfig):
name = 'virtualization'
def ready(self):
from . import search
from . import search, signals
from .models import VirtualMachine
from utilities.counters import connect_counters

View File

@ -240,6 +240,11 @@ class VirtualMachineForm(TenancyForm, NetBoxModelForm):
if self.instance.pk:
# Disable the disk field if one or more VirtualDisks have been created
if self.instance.virtualdisks.exists():
self.fields['disk'].widget.attrs['disabled'] = True
self.fields['disk'].help_text = _("Disk size is managed via the attachment of virtual disks.")
# Compile list of choices for primary IPv4 and IPv6 addresses
for family in [4, 6]:
ip_choices = [(None, '---------')]

View File

@ -2,7 +2,7 @@ from django.contrib.contenttypes.fields import GenericRelation
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
from django.db import models
from django.db.models import Q
from django.db.models import Q, Sum
from django.db.models.functions import Lower
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
@ -120,12 +120,10 @@ class VirtualMachine(ContactsMixin, RenderConfigMixin, ConfigContextModel, Prima
null=True,
verbose_name=_('memory (MB)')
)
# TODO: Remove in v4.0
disk = models.PositiveIntegerField(
blank=True,
null=True,
verbose_name=_('disk (GB)'),
help_text=_('deprecated - use Virtual Disks')
verbose_name=_('disk (GB)')
)
# Counter fields
@ -199,6 +197,17 @@ class VirtualMachine(ContactsMixin, RenderConfigMixin, ConfigContextModel, Prima
).format(device=self.device, cluster=self.cluster)
})
# Validate aggregate disk size
if self.pk:
total_disk = self.virtualdisks.aggregate(Sum('size', default=0))['size__sum']
if total_disk and self.disk != total_disk:
raise ValidationError({
'disk': _(
"The specified disk size ({size}) must match the aggregate size of assigned virtual disks "
"({total_size})."
).format(size=self.disk, total_size=total_disk)
})
# Validate primary IP addresses
interfaces = self.interfaces.all() if self.pk else None
for family in (4, 6):

View File

@ -0,0 +1,16 @@
from django.db.models import Sum
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from .models import VirtualDisk, VirtualMachine
@receiver((post_delete, post_save), sender=VirtualDisk)
def update_circuit(instance, **kwargs):
"""
When a VirtualDisk has been modified, update the aggregate disk_size value of its VM.
"""
vm = instance.virtual_machine
VirtualMachine.objects.filter(pk=vm.pk).update(
disk=vm.virtualdisks.aggregate(Sum('size'))['size__sum']
)

View File

@ -90,3 +90,28 @@ class VirtualMachineTestCase(TestCase):
# Uniqueness validation for name should ignore case
with self.assertRaises(ValidationError):
vm2.full_clean()
def test_disk_size(self):
vm = VirtualMachine(
cluster=Cluster.objects.first(),
name='Virtual Machine 1'
)
vm.save()
vm.refresh_from_db()
self.assertEqual(vm.disk, None)
# Create two VirtualDisks
VirtualDisk.objects.create(virtual_machine=vm, name='Virtual Disk 1', size=10)
VirtualDisk.objects.create(virtual_machine=vm, name='Virtual Disk 2', size=10)
vm.refresh_from_db()
self.assertEqual(vm.disk, 20)
# Delete one VirtualDisk
VirtualDisk.objects.first().delete()
vm.refresh_from_db()
self.assertEqual(vm.disk, 10)
# Attempt to manually overwrite the aggregate disk size
vm.disk = 30
with self.assertRaises(ValidationError):
vm.full_clean()