feat(tools): Enhance guest tools installation and management
- Added new API endpoints for tool catalog and installation management. - Updated usage documentation to include new tool installation features. - Implemented asynchronous tool installation with job tracking. - Introduced integration tests for tool installation in disposable VMs. - Enhanced dashboard UI to reflect tool installation status and usage instructions. - Improved error handling and logging for tool installation processes. - Added support for multiple Python versions and Conda environments. - Refactored tool management logic to ensure better state handling and job persistence.
This commit is contained in:
+28
-9
@@ -77,13 +77,20 @@ class ToolService:
|
||||
self._owner = descriptor
|
||||
try:
|
||||
self.store.recover()
|
||||
self._workers_left = self.settings.tool_workers
|
||||
self._threads = [threading.Thread(target=self._worker, name=f"uvm-tools-{index}", daemon=True)
|
||||
for index in range(self.settings.tool_workers)]
|
||||
for thread in self._threads:
|
||||
thread.start()
|
||||
for index in range(self.settings.tool_workers):
|
||||
thread = threading.Thread(target=self._worker, name=f"uvm-tools-{index}", daemon=True)
|
||||
self._workers_left += 1
|
||||
try:
|
||||
thread.start()
|
||||
except BaseException:
|
||||
self._workers_left -= 1
|
||||
raise
|
||||
self._threads.append(thread)
|
||||
except BaseException:
|
||||
self._release_owner()
|
||||
self._stop.set()
|
||||
self.notify()
|
||||
if not self._workers_left:
|
||||
self._release_owner()
|
||||
raise
|
||||
|
||||
def _release_owner(self):
|
||||
@@ -181,8 +188,11 @@ class ToolService:
|
||||
data = self.store.read(vm_id)
|
||||
running = self.lifecycle.inspect(vm.id).observed_status == "running"
|
||||
if refresh and running:
|
||||
supported = data["supported"]
|
||||
try:
|
||||
result = self._execute(vm, self._payload(vm, "probe", all_selections()), 90)
|
||||
if type(result.get("supported")) is bool:
|
||||
supported = result["supported"]
|
||||
if not result["ok"]:
|
||||
raise UvmError(_text(result.get("error"), 2048))
|
||||
inventory = self._inventory(result, all_selections())
|
||||
@@ -192,7 +202,8 @@ class ToolService:
|
||||
data = self.store.read(vm_id)
|
||||
except UvmError as error:
|
||||
with self.store.transaction(vm_id) as current:
|
||||
current.update(error=_text(error, 2048), agent_ready=not isinstance(error, GuestAgentUnavailableError))
|
||||
current.update(error=_text(error, 2048), agent_ready=not isinstance(error, GuestAgentUnavailableError),
|
||||
supported=supported)
|
||||
data = self.store.read(vm_id)
|
||||
if not running:
|
||||
data.update(agent_ready=False, guest_busy=False)
|
||||
@@ -258,7 +269,7 @@ class ToolService:
|
||||
def ready(current, data):
|
||||
observed = {item["key"]: item for item in data["inventory"]}
|
||||
observed.update({item["key"]: item for item in initial})
|
||||
data.update(inventory=list(observed.values()), checked_at=time.time(), agent_ready=True,
|
||||
data.update(inventory=list(observed.values()), agent_ready=True,
|
||||
guest_busy=False, supported=True, error=None)
|
||||
self._update(job, ready)
|
||||
for index, selection in enumerate(selections):
|
||||
@@ -289,7 +300,7 @@ class ToolService:
|
||||
inventory[selection.key] = observed
|
||||
elif selection.key in inventory:
|
||||
inventory[selection.key].update(installed=False, error="Refresh to verify after the failed installation.")
|
||||
data.update(inventory=list(inventory.values()), checked_at=time.time())
|
||||
data.update(inventory=list(inventory.values()))
|
||||
self._update(job, finish_item)
|
||||
submitted = False
|
||||
|
||||
@@ -317,3 +328,11 @@ class ToolService:
|
||||
self._update(job, failed)
|
||||
except NotFoundError:
|
||||
pass
|
||||
except Exception:
|
||||
logging.getLogger(__name__).exception("Unexpected tool installation failure")
|
||||
try:
|
||||
self._update(job, lambda current, data: self.store.interrupt(
|
||||
current, "Unexpected installer failure; check the management server log and refresh before retrying.",
|
||||
time.time(), outcome_unknown=submitted))
|
||||
except NotFoundError:
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user