Implement guest tool installation and management system

- Added `guest_tools.py` for standalone tool installer functionality.
- Introduced `ToolJobStore` in `tool_store.py` to manage per-VM tool job records.
- Created `ToolService` in `tools.py` for background installation workers using the guest execution channel.
- Enhanced `LifecycleService` in `lifecycle.py` to support tool installation queuing and preparation.
- Updated `guest_exec.py` to handle connection shutdown gracefully.
- Added validation and normalization functions for tool specifications.
- Implemented logging and error handling for tool installation processes.
- Ensured compatibility with Ubuntu 22.04 and ARM64/x86_64 architectures.
This commit is contained in:
2026-09-25 23:02:47 +05:30
parent 645bf6321a
commit e1b934e35e
11 changed files with 1345 additions and 0 deletions
+11
View File
@@ -3,6 +3,7 @@
from __future__ import annotations
import os
import math
from dataclasses import dataclass, field
from ipaddress import IPv4Address, IPv4Network
from pathlib import Path
@@ -46,6 +47,9 @@ class Settings:
api_timeout_s: float = 2.0
api_socket_timeout_s: float = 5.0
terminate_timeout_s: float = 5.0
tool_workers: int = 2
tool_ready_timeout_s: float = 120.0
tool_install_timeout_s: float = 1200.0
def __post_init__(self) -> None:
architecture = self.architecture.lower()
@@ -54,6 +58,13 @@ class Settings:
raise ConfigurationError(f"unsupported host architecture: {self.architecture}")
object.__setattr__(self, "architecture", architecture)
if type(self.tool_workers) is not int or not 1 <= self.tool_workers <= 8:
raise ConfigurationError("tool_workers must be an integer between 1 and 8")
for name in ("tool_ready_timeout_s", "tool_install_timeout_s"):
value = getattr(self, name)
if type(value) not in (int, float) or not math.isfinite(value) or not 0 < value <= 3600:
raise ConfigurationError(f"{name} must be finite, greater than zero, and at most 3600 seconds")
# Resolve defaults at construction time, while preserving individual URL overrides.
asset_base = f"{DEFAULT_GUEST_ASSETS_URL}/{architecture}"
if not self.kernel_url: