1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 11:46:49 +00:00
webvirtcloud/instances/test_template_quota.py
catborise 1fb74d6378 fix(instances): turning a template back into a VM is checked against the quota
Templates do not count towards a user's quota and cannot be started.
Staff owners may mark and unmark their VMs as templates, so a staff
user could mark a VM as a template, use the freed quota elsewhere, and
unmark it again: the VM came back without any check, over the quota.

For users with a quota (everyone but superusers), turning a template
back into a VM now passes check_user_quota with the VM's size (one VM,
its vCPUs, memory and disks). If the quota is reached or cannot be
verified, it stays a template and the user is told why. Marking a VM as
a template frees quota and is not checked. The view takes the
per-user quota lock like the other quota-checked changes.

The existing template-flag test gives its mocked VM a size. The new
tests fail on the previous code.
2026-10-05 22:19:34 +03:00

68 lines
3.3 KiB
Python

"""Templates do not count towards the quota and cannot be started. Turning a
template back into a VM adds it to its owner's usage, so for users with a
quota it is checked like any other increase."""
from unittest.mock import patch
from accounts.models import UserInstance
from computes.models import Compute
from django.contrib.auth import get_user_model
from django.contrib.messages import get_messages
from django.test import TestCase
from django.urls import reverse
from instances.models import Instance
from instances.utils import QUOTA_UNVERIFIED
class TemplateFlagQuotaTests(TestCase):
def setUp(self):
User = get_user_model()
self.staff = User.objects.create_user("tq-staff", password="pw", is_staff=True)
compute = Compute.objects.create(name="tq", hostname="10.0.0.5", login="u", password="p", type=1)
self.vm = Instance.objects.create(compute=compute, name="tq-vm", uuid="12345678-0000-0000-0000-00000000aaaa")
UserInstance.objects.create(user=self.staff, instance=self.vm, is_change=True)
def post(self, user, is_template, quota=""):
self.client.force_login(user)
data = {"title": "", "description": ""}
if is_template:
data["is_template"] = "on"
with patch("instances.models.wvmInstance") as wvm, patch(
"instances.views.utils.check_user_quota", return_value=quota
) as check:
wvm.return_value.get_vcpu.return_value = 2
wvm.return_value.get_memory.return_value = 2048
wvm.return_value.get_disk_devices.return_value = [{"size": 10 << 30}]
response = self.client.post(reverse("instances:change_options", args=[self.vm.id]), data)
self.vm.refresh_from_db()
return check, [str(m) for m in get_messages(response.wsgi_request)]
def test_making_a_template_needs_no_quota(self):
check, _ = self.post(self.staff, is_template=True)
self.assertTrue(self.vm.is_template)
check.assert_not_called()
def test_turning_a_template_into_a_vm_is_checked(self):
Instance.objects.filter(pk=self.vm.pk).update(is_template=True)
check, _ = self.post(self.staff, is_template=False)
self.assertFalse(self.vm.is_template)
check.assert_called_once_with(self.staff, 1, 2, 2048, 10)
def test_over_quota_the_template_stays_a_template(self):
Instance.objects.filter(pk=self.vm.pk).update(is_template=True)
_, messages = self.post(self.staff, is_template=False, quota="cpu")
self.assertTrue(self.vm.is_template)
self.assertTrue(any("quota" in m for m in messages), messages)
def test_an_unverifiable_quota_keeps_it_a_template(self):
Instance.objects.filter(pk=self.vm.pk).update(is_template=True)
_, messages = self.post(self.staff, is_template=False, quota=QUOTA_UNVERIFIED)
self.assertTrue(self.vm.is_template)
self.assertTrue(any("cannot be reached" in m for m in messages), messages)
def test_superusers_have_no_quota(self):
Instance.objects.filter(pk=self.vm.pk).update(is_template=True)
admin = get_user_model().objects.create_superuser("tq-admin", "tq@example.com", "pw")
check, _ = self.post(admin, is_template=False, quota="cpu")
self.assertFalse(self.vm.is_template)
check.assert_not_called()