From ef49378dafde1252589210ae7604b67493535d25 Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Mon, 4 May 2026 15:46:33 -0400 Subject: [PATCH 1/3] vm: Use cloud-init status --wait instead of sleep Replace fixed sleeps with 'sudo cloud-init status --wait' to wait for cloud-init to finish, rather than hoping a hardcoded timeout is long enough. --- kt/commands/content_release/impl.py | 4 +--- kt/commands/vm/impl.py | 8 +++----- kt/ktlib/util.py | 1 - 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/kt/commands/content_release/impl.py b/kt/commands/content_release/impl.py index 699efac..0312618 100644 --- a/kt/commands/content_release/impl.py +++ b/kt/commands/content_release/impl.py @@ -1,7 +1,6 @@ import logging import os import re -import time from git import GitCommandError, Repo @@ -10,7 +9,6 @@ from kt.ktlib.local import LocalCommand from kt.ktlib.mock import Mock from kt.ktlib.ssh import SshCommand -from kt.ktlib.util import Constants from kt.ktlib.vm import Vm # Source download configuration for kernels that don't work with getsrc.sh @@ -309,7 +307,7 @@ def test(cls, kernel_workspace: str): # Wait for dependencies to be installed if VM was just created logging.info("Waiting for VM dependencies to be installed...") - time.sleep(Constants.VM_DEPS_INSTALL_WAIT_SECONDS) + SshCommand.run(domain=vm_instance.domain, command=["sudo cloud-init status --wait || true"]) # Install the built RPMs build_files_dir = kernel_workspace_obj.folder / "build_files" diff --git a/kt/commands/vm/impl.py b/kt/commands/vm/impl.py index 6a62e9b..734493c 100644 --- a/kt/commands/vm/impl.py +++ b/kt/commands/vm/impl.py @@ -1,8 +1,7 @@ import logging -import time from kt.ktlib.config import Config -from kt.ktlib.util import Constants +from kt.ktlib.ssh import SshCommand from kt.ktlib.virt import VmCommand from kt.ktlib.vm import Vm @@ -25,9 +24,8 @@ def main(name: str, console: bool, destroy: bool, override: bool, list_all: bool config = Config.load() if test: - # Wait for the dependencies to be installed - logging.info("Waiting for the deps to be installed") - time.sleep(Constants.VM_DEPS_INSTALL_WAIT_SECONDS) + logging.info("Waiting for cloud-init to finish...") + SshCommand.run(domain=vm_instance.domain, command=["sudo cloud-init status --wait || true"]) vm_instance.test(config=config) if console: diff --git a/kt/ktlib/util.py b/kt/ktlib/util.py index 3a15254..784271e 100644 --- a/kt/ktlib/util.py +++ b/kt/ktlib/util.py @@ -13,6 +13,5 @@ class Constants: CLOUD_INIT = "cloud_init.yaml" - VM_DEPS_INSTALL_WAIT_SECONDS = 300 VM_STARTUP_WAIT_SECONDS = 60 VM_REBOOT_WAIT_SECONDS = 120 From 8317f83c4c190fe23dda6e984af02ed4cff4ff95 Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Tue, 5 May 2026 09:26:14 -0400 Subject: [PATCH 2/3] vm: Add --vcpus option to set the number of virtual CPUs Drop the old cpuset/static placement that pinned VMs to specific cores. Defaults to 12 if not specified. --- kt/commands/vm/command.py | 13 +++++++++++-- kt/commands/vm/impl.py | 4 ++-- kt/ktlib/virt.py | 3 ++- kt/ktlib/vm.py | 16 +++++++++------- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/kt/commands/vm/command.py b/kt/commands/vm/command.py index c3b16f8..6a5e0e6 100644 --- a/kt/commands/vm/command.py +++ b/kt/commands/vm/command.py @@ -57,9 +57,18 @@ help="Lists existings vms", ) @click.option("--test", is_flag=True, help="Build the kernel and run kselftests") +@click.option("--vcpus", type=int, default=12, help="Number of virtual CPUs (default: 12)") @click.argument("kernel_workspace", required=False, shell_complete=ShellCompletion.show_kernel_workspaces) -def vm(kernel_workspace, console, destroy, override, list_all, test): +def vm(kernel_workspace, console, destroy, override, list_all, test, vcpus): if not list_all and not kernel_workspace: raise click.UsageError("kernel_workspace is required unless --list-all is specified") - main(name=kernel_workspace, console=console, destroy=destroy, override=override, list_all=list_all, test=test) + main( + name=kernel_workspace, + console=console, + destroy=destroy, + override=override, + list_all=list_all, + test=test, + vcpus=vcpus, + ) diff --git a/kt/commands/vm/impl.py b/kt/commands/vm/impl.py index 734493c..0f4849e 100644 --- a/kt/commands/vm/impl.py +++ b/kt/commands/vm/impl.py @@ -6,7 +6,7 @@ from kt.ktlib.vm import Vm -def main(name: str, console: bool, destroy: bool, override: bool, list_all: bool, test: bool = False): +def main(name: str, console: bool, destroy: bool, override: bool, list_all: bool, test: bool = False, vcpus: int = 12): if list_all: VmCommand.list_all() return @@ -20,7 +20,7 @@ def main(name: str, console: bool, destroy: bool, override: bool, list_all: bool vm.destroy() return - vm_instance = Vm.setup_and_spinup(kernel_workspace_name=name, override=override) + vm_instance = Vm.setup_and_spinup(kernel_workspace_name=name, override=override, vcpus=vcpus) config = Config.load() if test: diff --git a/kt/ktlib/virt.py b/kt/ktlib/virt.py index f2b091c..f20cdd6 100644 --- a/kt/ktlib/virt.py +++ b/kt/ktlib/virt.py @@ -41,6 +41,7 @@ def install( vm_major_version: str, cloud_init_path: Path, common_dir: Path, + vcpus: int = 12, ): command = [ "--name", @@ -51,7 +52,7 @@ def install( "--virt-type", "kvm", "--vcpus", - "12,vcpu.cpuset=0-11,vcpu.placement=static", + str(vcpus), "--memory", str(32768), "--vnc", diff --git a/kt/ktlib/vm.py b/kt/ktlib/vm.py index 9984fe4..4031722 100644 --- a/kt/ktlib/vm.py +++ b/kt/ktlib/vm.py @@ -96,13 +96,14 @@ def load_from_workspace(cls, kernel_workspace_name: str): return cls.load(config=config, kernel_workspace=kernel_workspace) @classmethod - def setup_and_spinup(cls, kernel_workspace_name: str, override: bool = False): + def setup_and_spinup(cls, kernel_workspace_name: str, override: bool = False, vcpus: int = 12): """ Setup and spin up a VM from a kernel workspace name. Args: kernel_workspace_name: The name of the kernel workspace override: If True, destroy and recreate the VM + vcpus: Number of virtual CPUs Returns: VmInstance: The running VM instance @@ -114,7 +115,7 @@ def setup_and_spinup(cls, kernel_workspace_name: str, override: bool = False): vm.destroy() vm.setup(config=config) - vm_instance = vm.spin_up(config=config) + vm_instance = vm.spin_up(config=config, vcpus=vcpus) return vm_instance @@ -168,7 +169,7 @@ def _setup_cloud_init(self, config: Config): f.write("#cloud-config\n") yaml.dump(data, f) - def _create_image(self, config: Config): + def _create_image(self, config: Config, vcpus: int = 12): # Make sure the dir exists self.qcow2_path.parent.mkdir(parents=True, exist_ok=True) @@ -179,16 +180,17 @@ def _create_image(self, config: Config): # Resize the disk to 30GB self._resize_disk() - self._virt_install(config=config) + self._virt_install(config=config, vcpus=vcpus) time.sleep(Constants.VM_STARTUP_WAIT_SECONDS) - def _virt_install(self, config: Config): + def _virt_install(self, config: Config, vcpus: int = 12): return VmCommand.install( name=self.name, qcow2_path=self.qcow2_path, vm_major_version=self.vm_major_version, cloud_init_path=self.cloud_init_path, common_dir=config.base_path, + vcpus=vcpus, ) def _resize_disk(self): @@ -202,11 +204,11 @@ def _resize_disk(self): def setup(self, config: Config): self._download_source_image() - def spin_up(self, config: Config) -> VmInstance: + def spin_up(self, config: Config, vcpus: int = 12) -> VmInstance: if not VirtHelper.exists(vm_name=self.name): logging.info(f"VM {self.name} does not exist, creating from scratch...") - self._create_image(config=config) + self._create_image(config=config, vcpus=vcpus) return VmInstance(name=self.name, kernel_workspace=self.kernel_workspace) logging.info(f"Vm {self.name} already exists") From 95afb972b2512c9872872e1962df301b3b891f12 Mon Sep 17 00:00:00 2001 From: Brett Mastbergen Date: Tue, 5 May 2026 10:06:04 -0400 Subject: [PATCH 3/3] vm: Add --memory option to set VM memory size Defaults to 32768 MiB if not specified. --- kt/commands/vm/command.py | 4 +++- kt/commands/vm/impl.py | 13 +++++++++++-- kt/ktlib/virt.py | 3 ++- kt/ktlib/vm.py | 16 +++++++++------- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/kt/commands/vm/command.py b/kt/commands/vm/command.py index 6a5e0e6..8ba2eff 100644 --- a/kt/commands/vm/command.py +++ b/kt/commands/vm/command.py @@ -58,8 +58,9 @@ ) @click.option("--test", is_flag=True, help="Build the kernel and run kselftests") @click.option("--vcpus", type=int, default=12, help="Number of virtual CPUs (default: 12)") +@click.option("--memory", type=int, default=32768, help="Memory in MiB (default: 32768)") @click.argument("kernel_workspace", required=False, shell_complete=ShellCompletion.show_kernel_workspaces) -def vm(kernel_workspace, console, destroy, override, list_all, test, vcpus): +def vm(kernel_workspace, console, destroy, override, list_all, test, vcpus, memory): if not list_all and not kernel_workspace: raise click.UsageError("kernel_workspace is required unless --list-all is specified") @@ -71,4 +72,5 @@ def vm(kernel_workspace, console, destroy, override, list_all, test, vcpus): list_all=list_all, test=test, vcpus=vcpus, + memory=memory, ) diff --git a/kt/commands/vm/impl.py b/kt/commands/vm/impl.py index 0f4849e..3eec174 100644 --- a/kt/commands/vm/impl.py +++ b/kt/commands/vm/impl.py @@ -6,7 +6,16 @@ from kt.ktlib.vm import Vm -def main(name: str, console: bool, destroy: bool, override: bool, list_all: bool, test: bool = False, vcpus: int = 12): +def main( + name: str, + console: bool, + destroy: bool, + override: bool, + list_all: bool, + test: bool = False, + vcpus: int = 12, + memory: int = 32768, +): if list_all: VmCommand.list_all() return @@ -20,7 +29,7 @@ def main(name: str, console: bool, destroy: bool, override: bool, list_all: bool vm.destroy() return - vm_instance = Vm.setup_and_spinup(kernel_workspace_name=name, override=override, vcpus=vcpus) + vm_instance = Vm.setup_and_spinup(kernel_workspace_name=name, override=override, vcpus=vcpus, memory=memory) config = Config.load() if test: diff --git a/kt/ktlib/virt.py b/kt/ktlib/virt.py index f20cdd6..50aa9f9 100644 --- a/kt/ktlib/virt.py +++ b/kt/ktlib/virt.py @@ -42,6 +42,7 @@ def install( cloud_init_path: Path, common_dir: Path, vcpus: int = 12, + memory: int = 32768, ): command = [ "--name", @@ -54,7 +55,7 @@ def install( "--vcpus", str(vcpus), "--memory", - str(32768), + str(memory), "--vnc", "--cloud-init", f"user-data={cloud_init_path}", diff --git a/kt/ktlib/vm.py b/kt/ktlib/vm.py index 4031722..b6d7c84 100644 --- a/kt/ktlib/vm.py +++ b/kt/ktlib/vm.py @@ -96,7 +96,7 @@ def load_from_workspace(cls, kernel_workspace_name: str): return cls.load(config=config, kernel_workspace=kernel_workspace) @classmethod - def setup_and_spinup(cls, kernel_workspace_name: str, override: bool = False, vcpus: int = 12): + def setup_and_spinup(cls, kernel_workspace_name: str, override: bool = False, vcpus: int = 12, memory: int = 32768): """ Setup and spin up a VM from a kernel workspace name. @@ -104,6 +104,7 @@ def setup_and_spinup(cls, kernel_workspace_name: str, override: bool = False, vc kernel_workspace_name: The name of the kernel workspace override: If True, destroy and recreate the VM vcpus: Number of virtual CPUs + memory: Memory in MiB Returns: VmInstance: The running VM instance @@ -115,7 +116,7 @@ def setup_and_spinup(cls, kernel_workspace_name: str, override: bool = False, vc vm.destroy() vm.setup(config=config) - vm_instance = vm.spin_up(config=config, vcpus=vcpus) + vm_instance = vm.spin_up(config=config, vcpus=vcpus, memory=memory) return vm_instance @@ -169,7 +170,7 @@ def _setup_cloud_init(self, config: Config): f.write("#cloud-config\n") yaml.dump(data, f) - def _create_image(self, config: Config, vcpus: int = 12): + def _create_image(self, config: Config, vcpus: int = 12, memory: int = 32768): # Make sure the dir exists self.qcow2_path.parent.mkdir(parents=True, exist_ok=True) @@ -180,10 +181,10 @@ def _create_image(self, config: Config, vcpus: int = 12): # Resize the disk to 30GB self._resize_disk() - self._virt_install(config=config, vcpus=vcpus) + self._virt_install(config=config, vcpus=vcpus, memory=memory) time.sleep(Constants.VM_STARTUP_WAIT_SECONDS) - def _virt_install(self, config: Config, vcpus: int = 12): + def _virt_install(self, config: Config, vcpus: int = 12, memory: int = 32768): return VmCommand.install( name=self.name, qcow2_path=self.qcow2_path, @@ -191,6 +192,7 @@ def _virt_install(self, config: Config, vcpus: int = 12): cloud_init_path=self.cloud_init_path, common_dir=config.base_path, vcpus=vcpus, + memory=memory, ) def _resize_disk(self): @@ -204,11 +206,11 @@ def _resize_disk(self): def setup(self, config: Config): self._download_source_image() - def spin_up(self, config: Config, vcpus: int = 12) -> VmInstance: + def spin_up(self, config: Config, vcpus: int = 12, memory: int = 32768) -> VmInstance: if not VirtHelper.exists(vm_name=self.name): logging.info(f"VM {self.name} does not exist, creating from scratch...") - self._create_image(config=config, vcpus=vcpus) + self._create_image(config=config, vcpus=vcpus, memory=memory) return VmInstance(name=self.name, kernel_workspace=self.kernel_workspace) logging.info(f"Vm {self.name} already exists")