clan-vm-manager: Fix ui state desync on build error. Add build progress bar
All checks were successful
checks / check-links (pull_request) Successful in 21s
checks / checks-impure (pull_request) Successful in 1m56s
checks / checks (pull_request) Successful in 2m32s

This commit is contained in:
Luis Hebendanz 2024-02-26 01:59:45 +07:00
parent 183c1f4235
commit 0a8b8713d9
3 changed files with 32 additions and 13 deletions

View File

@ -99,7 +99,7 @@ def _init_proc(
gpid = os.getpgid(pid=pid)
print(f"Killing process group pid={pid} gpid={gpid}", file=sys.stderr)
os.killpg(gpid, signal.SIGTERM)
sys.exit(1)
# Don't use a finally block here, because we want the exitcode to be set to
# 0 if the function returns normally

View File

@ -136,6 +136,11 @@ class VM(GObject.Object):
self._logs_id: int = 0
self._log_file: IO[str] | None = None
# To be able to set the switch state programmatically
# we need to store the handler id returned by the connect method
# and block the signal while we change the state. This is cursed.
self.switch_handler_id: int = 0
# Make sure the VM is killed when the reference to this object is dropped
self._finalizer = weakref.finalize(self, self.kill_ref_drop)
@ -171,15 +176,25 @@ class VM(GObject.Object):
with self.create_machine() as machine:
# Start building VM
log.info(f"Building VM {self.get_id()}")
log_dir = Path(str(self.log_dir.name))
self.build_process = spawn(
on_except=None,
out_file=Path(str(self.log_dir.name)) / "build.log",
out_file=log_dir / "build.log",
func=vms.run.build_vm,
machine=machine,
tmpdir=log_dir,
vm=self.data.flake.vm,
)
GLib.idle_add(self.emit, "vm_status_changed", self)
# Start the logs watcher
self._logs_id = GLib.timeout_add(
50, self._get_logs_task, self.build_process
)
if self._logs_id == 0:
log.error("Failed to start VM log watcher")
log.debug(f"Starting logs watcher on file: {self.build_process.out_file}")
# Start the progress bar and show it
self.progress_bar.show()
self.prog_bar_id = GLib.timeout_add(100, self._pulse_progress_bar)
@ -208,7 +223,7 @@ class VM(GObject.Object):
GLib.idle_add(self.emit, "vm_status_changed", self)
# Start the logs watcher
self._logs_id = GLib.timeout_add(50, self._get_logs_task)
self._logs_id = GLib.timeout_add(50, self._get_logs_task, self.vm_process)
if self._logs_id == 0:
log.error("Failed to start VM log watcher")
log.debug(f"Starting logs watcher on file: {self.vm_process.out_file}")
@ -223,16 +238,17 @@ class VM(GObject.Object):
log.warn("VM is already running. Ignoring start request")
self.emit("vm_status_changed", self)
return
log.debug(f"VM state dir {self.log_dir.name}")
self._start_thread = threading.Thread(target=self.__start)
self._start_thread.start()
def _get_logs_task(self) -> bool:
if not self.vm_process.out_file.exists():
def _get_logs_task(self, proc: MPProcess) -> bool:
if not proc.out_file.exists():
return GLib.SOURCE_CONTINUE
if not self._log_file:
try:
self._log_file = open(self.vm_process.out_file)
self._log_file = open(proc.out_file)
except Exception as ex:
log.exception(ex)
self._log_file = None
@ -242,7 +258,7 @@ class VM(GObject.Object):
if len(line) != 0:
print(line.decode("utf-8"), end="", flush=True)
if not self.is_running():
if not proc.proc.is_alive():
log.debug("Removing logs watcher")
self._log_file = None
return GLib.SOURCE_REMOVE

View File

@ -193,11 +193,9 @@ class ClanList(Gtk.Box):
box.append(switch_box)
box.append(pref_button)
switch.connect("notify::active", partial(self.on_row_toggle, vm))
# def on_switch_state_set(switch: Any, state: bool) -> bool:
# return True
# switch.connect("state-set", on_switch_state_set)
vm.switch_handler_id = switch.connect(
"notify::active", partial(self.on_row_toggle, vm)
)
vm.connect("vm_status_changed", partial(self.vm_status_changed, switch))
# suffix.append(box)
@ -304,6 +302,11 @@ class ClanList(Gtk.Box):
if switch.get_sensitive() is False and not vm.is_building():
switch.set_sensitive(True)
exitc = vm.vm_process.proc.exitcode
exit_vm = vm.vm_process.proc.exitcode
exit_build = vm.build_process.proc.exitcode
exitc = exit_vm or exit_build
if not vm.is_running() and exitc != 0:
switch.handler_block(vm.switch_handler_id)
switch.set_active(False)
switch.handler_unblock(vm.switch_handler_id)
log.error(f"VM exited with error. Exitcode: {exitc}")