vms: init graceful shutdown for GUI
- add python modules for qemu protocols: QMP (hardware interactions) and QGA (guest service interaction) - refactor state directory: remove name from path (already contains url) - add impure vm test for basic qmp interaction - simplify existing vm persistance test (factor out shared code) - integrate graceful shutdown into GUI the GUI integration still needs to be improved later: - add fallback in case system doesn't react to powerdown button - shutdown GUI switch fails if VM hasn't been started yet, and then remains in a wrong position
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
import json
|
||||
import logging
|
||||
from os import path
|
||||
from pathlib import Path
|
||||
from time import sleep
|
||||
|
||||
from clan_cli.dirs import vm_state_dir
|
||||
from qemu.qmp import QEMUMonitorProtocol
|
||||
|
||||
from ..cmd import run
|
||||
from ..errors import ClanError
|
||||
@@ -30,6 +35,14 @@ class Machine:
|
||||
self.build_cache: dict[str, Path] = {}
|
||||
|
||||
self._deployment_info: None | dict[str, str] = deployment_info
|
||||
state_dir = vm_state_dir(flake_url=str(self.flake), vm_name=self.name)
|
||||
|
||||
self.qmp_socket: Path = state_dir / "qmp.sock"
|
||||
self.qga_socket: Path = state_dir / "qga.sock"
|
||||
|
||||
print(f"qmp_socket: {self.qmp_socket}")
|
||||
self._qmp = QEMUMonitorProtocol(path.realpath(self.qmp_socket))
|
||||
self._qmp_connected = False
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Machine(name={self.name}, flake={self.flake})"
|
||||
@@ -46,6 +59,28 @@ class Machine:
|
||||
)
|
||||
return self._deployment_info
|
||||
|
||||
def qmp_connect(self) -> None:
|
||||
if not self._qmp_connected:
|
||||
tries = 100
|
||||
for num in range(tries):
|
||||
try:
|
||||
# the socket file link might be outdated, therefore re-init the qmp object
|
||||
self._qmp = QEMUMonitorProtocol(path.realpath(self.qmp_socket))
|
||||
self._qmp.connect()
|
||||
self._qmp_connected = True
|
||||
log.debug("QMP Connected")
|
||||
return
|
||||
except FileNotFoundError:
|
||||
if num < 99:
|
||||
sleep(0.1)
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
|
||||
def qmp_command(self, command: str) -> dict:
|
||||
self.qmp_connect()
|
||||
return self._qmp.command(command)
|
||||
|
||||
@property
|
||||
def target_host_address(self) -> str:
|
||||
# deploymentAddress is deprecated.
|
||||
|
||||
Reference in New Issue
Block a user