diff --git a/instances/models.py b/instances/models.py index 8d677d6d..981306e3 100644 --- a/instances/models.py +++ b/instances/models.py @@ -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): diff --git a/instances/tests/test_resize_input.py b/instances/tests/test_resize_input.py index 5f29118b..f87fae33 100644 --- a/instances/tests/test_resize_input.py +++ b/instances/tests/test_resize_input.py @@ -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']*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]) diff --git a/instances/views.py b/instances/views.py index f3d2c2a0..88717fb3 100755 --- a/instances/views.py +++ b/instances/views.py @@ -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) diff --git a/vrtManager/instance.py b/vrtManager/instance.py index 8d4fb39b..543c711d 100644 --- a/vrtManager/instance.py +++ b/vrtManager/instance.py @@ -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