feat: implement resource resizing for VMs with storage management

- Added KnownHostsManager to handle cleanup of SSH known_hosts entries upon VM deletion.
- Introduced disk_size_bytes method in ImageStore to read logical disk capacity.
- Enhanced LifecycleService to manage VM resource resizing, including CPU, RAM, and storage.
- Updated application configuration to support known_hosts_files for SSH management.
- Modified dashboard to include resource resizing options in the UI.
- Improved error handling and validation for resource inputs in the dashboard.
- Removed obsolete .pyc files from the repository.
This commit is contained in:
2026-09-21 20:37:52 +05:30
parent fb80ce7c8a
commit 4a46685a4c
40 changed files with 1048 additions and 63 deletions
+10 -2
View File
@@ -19,6 +19,7 @@ from .firecracker.config import build_config, write_config
from .firecracker.process import FirecrackerProcessManager, ProcessInfo
from .images import ImageStore
from .integrity import load_manifest, verify_file
from .known_hosts import KnownHostsManager
from .network import NetworkManager
from .state import StateStore
from .system import check_kvm, ensure_data_directories, require_root
@@ -55,6 +56,7 @@ class LifecycleService:
def __init__(
self, settings: Settings, state_store: StateStore, images: ImageStore,
network: NetworkManager, process: FirecrackerProcessManager, *,
known_hosts: KnownHostsManager,
client_factory: Callable[[Path, float], FirecrackerClient] = FirecrackerClient,
clock: Callable[[], float] = time.time,
) -> None:
@@ -63,6 +65,7 @@ class LifecycleService:
self._images = images
self._network = network
self._process = process
self._known_hosts = known_hosts
self._client_factory = client_factory
self._clock = clock
@@ -251,7 +254,7 @@ class LifecycleService:
previous = vm.pending_resize
capacity = previous.storage_mib if previous is not None else vm.storage_mib
if capacity is None:
capacity = max(MIN_STORAGE_MIB, (Path(vm.disk).stat().st_size + 1024 * 1024 - 1) // (1024 * 1024))
capacity = max(MIN_STORAGE_MIB, (self._images.disk_size_bytes(Path(vm.disk)) + 1024 * 1024 - 1) // (1024 * 1024))
vm.pending_resize = VmResources(
cpu=cpu if cpu is not None else previous.cpu if previous is not None else vm.cpu,
ram_mib=ram_mib if ram_mib is not None else previous.ram_mib if previous is not None else vm.ram_mib,
@@ -284,7 +287,11 @@ class LifecycleService:
return
if self._process.observe(vm) != "gone":
raise ConflictError("cannot resize a disk without confirmed process exit")
if target.storage_mib != vm.storage_mib:
# A prior attempt may have published a resized image without saving
# its allocation. Check the disk too, including when a retry changes
# the target back to the previously recorded capacity.
if (target.storage_mib != vm.storage_mib
or self._images.disk_size_bytes(Path(vm.disk)) != target.storage_mib * 1024 * 1024):
self._images.resize_vm_disk(Path(vm.disk), target.storage_mib)
vm.cpu, vm.ram_mib, vm.storage_mib = target.cpu, target.ram_mib, target.storage_mib
vm.pending_resize = None
@@ -301,6 +308,7 @@ class LifecycleService:
try:
self._cleanup_runtime(vm, timeout=timeout)
self._remove_runtime(vm)
self._known_hosts.remove(vm)
self._remove_record(vm.id)
return vm
except BaseException as error: