1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 11:16:47 +00:00

fix(instances): a resize keeps a VM's maximum vCPUs above the host's CPUs

The Resize tab offered vCPU counts only up to the host's logical CPUs.
A VM whose maximum was higher (4 on a 2-CPU host, say) found no option
for it, so the Maximum list showed 1, and a resize of the shut-off VM
lowered its maximum to that without a word.

get_max_cpus() now takes the VM's own maximum as a floor, and the
Instance's vcpu_range passes it. The host's CPU count shown on the page
and used by the clone form is read from the host alone, not from that
range.
This commit is contained in:
catborise 2026-11-03 22:37:51 +03:00
parent 4997950e75
commit 103ce111ef
4 changed files with 21 additions and 6 deletions

View file

@ -117,7 +117,8 @@ class Instance(models.Model):
@cached_property
def vcpu_range(self):
return self.proxy.get_max_cpus()
"""The vCPU counts the Resize tab offers, the VM's own maximum among them."""
return self.proxy.get_max_cpus(at_least=self.config_vcpu)
@cached_property
def cur_vcpu(self):

View file

@ -8,7 +8,7 @@ from appsettings.settings import get_settings
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.test import SimpleTestCase, TestCase
from django.urls import reverse
from instances.models import Instance
from libvirt import VIR_DOMAIN_RUNNING
@ -94,3 +94,16 @@ class ResizeInputTests(TestCase):
from django.template.loader import render_to_string
html = render_to_string("instances/resize_tab.html", {"instance": self.vm, "can_change": True})
self.assertRegex(html, r'<input [^>]*name="memory"[^>]*disabled>')
class VcpuRangeTests(SimpleTestCase):
"""The Resize tab offers the VM's own maximum even above the host's CPUs,
so a resize does not lower it to the first choice."""
def test_the_counts_reach_the_vm_s_maximum(self):
from vrtManager.instance import wvmInstance
vm = wvmInstance.__new__(wvmInstance)
vm.__dict__["host_info"] = ["x86_64", 4096, 2, 0, 1, 1, 2, 1] # 2 logical CPUs
self.assertEqual(list(vm.get_max_cpus()), [1, 2])
self.assertEqual(list(vm.get_max_cpus(at_least=4)), [1, 2, 3, 4])

View file

@ -330,7 +330,7 @@ def _instance_page(request, pk, instance, compute):
# userinstances = UserInstance.objects.filter(instance=instance).order_by('user__username')
userinstances = instance.userinstance_set.order_by("user__username")
# Host resources
vcpu_host = len(instance.vcpu_range)
vcpu_host = len(instance.proxy.get_max_cpus()) # the host's logical CPUs, not the VM's maximum
memory_host = instance.proxy.get_max_memory()
bus_host = instance.proxy.get_disk_bus_types(instance.arch, instance.machine)
cdrom_buses = cdrom_bus_choices(instance)

View file

@ -779,11 +779,12 @@ class wvmInstance(wvmConnect):
def get_max_memory(self):
return self.host_info[1] * 1048576
def get_max_cpus(self):
"""Get number of physical CPUs."""
def get_max_cpus(self, at_least=0):
"""The vCPU counts to offer: up to the host's logical CPUs, or up to
at_least when that is more (a VM's own maximum, kept by a resize)."""
hostinfo = self.host_info
pcpus = hostinfo[4] * hostinfo[5] * hostinfo[6] * hostinfo[7]
return range(1, int(pcpus + 1))
return range(1, max(int(pcpus), at_least) + 1)
def get_interface_addresses(self, iface_mac):
"""The running VM's addresses of the NIC with iface_mac: (IPv4, IPv6