Sleeping in a loop waiting for the qemu guest agent to come online leaves an annoying progress dialog while the domain may actually be fully useable already. Additionally, multiple progress dialogs can actually accumulate on screen if the user manages to suspend/resume fast enough or the timeout is just long enough. To avoid these, we want to defer retries into a separate thread to allow the progress dialog to disappear immediately after the actual action completed. In preparation for that, add a new class _vmmDomainSetTimeThread that will eventually manage that separate thread for guest time setting operations. Move the current code for waiting for the qemu guest agent into it without any semantic changes. Make set_time() and agent_read() of vmmDomain accessible from the outside so that _vmmDomainSetTimeThread can call back into them. Add has_agent() to be able to find out if the domain has an agent configured without leaking the actual agent config. Signed-off-by: Michael Weiser <michael.weiser@xxxxxx> Suggested-by: Cole Robinson <crobinso@xxxxxxxxxx> --- virtManager/object/domain.py | 87 +++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/virtManager/object/domain.py b/virtManager/object/domain.py index 4a1c7587..355fca97 100644 --- a/virtManager/object/domain.py +++ b/virtManager/object/domain.py @@ -17,6 +17,7 @@ from virtinst import Guest from virtinst import log from .libvirtobject import vmmLibvirtObject +from ..baseclass import vmmGObject from ..lib.libvirtenummap import LibvirtEnumMap @@ -179,6 +180,42 @@ class vmmDomainSnapshot(vmmLibvirtObject): return False +class _vmmDomainSetTimeThread(vmmGObject): + def __init__(self, domain): + vmmGObject.__init__(self) + self._domain = domain + + def start(self): + # Only run the API for qemu and test drivers, they are the only ones + # that support it. This will save spamming logs with error output. + if not self._domain.conn.is_qemu() and not self._domain.conn.is_test(): + return + + if self._domain.conn.is_qemu(): + # For qemu, only run the API if the VM has the qemu guest agent in + # the XML. + if not self._domain.has_agent(): + return + + # wait for agent to come online + maxwait = 5 + sleep = 0.5 + for _ in range(0, int(maxwait / sleep)): + if self._domain.agent_ready(): + break + log.debug("Waiting for qemu guest agent to come online...") + time.sleep(sleep) + else: + if not self._domain.agent_ready(): + log.debug("Giving up on qemu guest agent for time sync") + return + + self._domain.set_time() + + def _cleanup(self): + pass + + class vmmDomain(vmmLibvirtObject): """ Class wrapping virDomain libvirt objects. Is also extended to be @@ -208,6 +245,7 @@ class vmmDomain(vmmLibvirtObject): self._domain_state_supported = False self.inspection = vmmInspectionData() + self._set_time_thread = _vmmDomainSetTimeThread(self) def _cleanup(self): for snap in self._snapshot_list or []: @@ -1062,7 +1100,7 @@ class vmmDomain(vmmLibvirtObject): # looking at the domain state after revert will always come back as # paused, so look at the snapshot state instead if will_be_running: - self._set_time() + self._async_set_time() def create_snapshot(self, xml, redefine=False): flags = 0 @@ -1082,7 +1120,13 @@ class vmmDomain(vmmLibvirtObject): return dev return None - def _agent_ready(self): + def has_agent(self): + """ + Return True if domain has a guest agent defined. + """ + return self._get_agent() is not None + + def agent_ready(self): """ Return connected state of an agent. """ @@ -1100,7 +1144,7 @@ class vmmDomain(vmmLibvirtObject): if not self.is_active(): return - if self._agent_ready(): + if self.agent_ready(): self._ip_cache["qemuga"] = self._get_interface_addresses( libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT) @@ -1166,7 +1210,7 @@ class vmmDomain(vmmLibvirtObject): def refresh_snapshots(self): self._snapshot_list = None - def _set_time(self): + def set_time(self): """ Try to set VM time to the current value. This is typically useful when clock wasn't running on the VM for some time (e.g. during suspension or @@ -1178,30 +1222,6 @@ class vmmDomain(vmmLibvirtObject): Heavily based on https://github.com/openstack/nova/commit/414df1e56ea9df700756a1732125e06c5d97d792. """ - # Only run the API for qemu and test drivers, they are the only ones - # that support it. This will save spamming logs with error output. - if not self.conn.is_qemu() and not self.conn.is_test(): - return - - if self.conn.is_qemu(): - # For qemu, only run the API if the VM has the qemu guest agent in - # the XML. - if not self._get_agent(): - return - - # wait for agent to come online - maxwait = 5 - sleep = 0.5 - for _ in range(0, int(maxwait / sleep)): - if self._agent_ready(): - break - log.debug("Waiting for qemu guest agent to come online...") - time.sleep(sleep) - else: - if not self._agent_ready(): - log.debug("Giving up on qemu guest agent for time sync") - return - t = time.time() seconds = int(t) nseconds = int((t - seconds) * 10 ** 9) @@ -1212,6 +1232,13 @@ class vmmDomain(vmmLibvirtObject): except Exception as e: log.debug("Failed to set time: %s", e) + def _async_set_time(self): + """ + Asynchronously try to set guest time and maybe wait for a guest agent + to come online using a separate thread. + """ + self._set_time_thread.start() + ######################## # XML Parsing routines # @@ -1352,7 +1379,7 @@ class vmmDomain(vmmLibvirtObject): sync_time = self.has_managed_save() self._backend.create() if sync_time: - self._set_time() + self._async_set_time() @vmmLibvirtObject.lifecycle_action def suspend(self): @@ -1385,7 +1412,7 @@ class vmmDomain(vmmLibvirtObject): @vmmLibvirtObject.lifecycle_action def resume(self): self._backend.resume() - self._set_time() + self._async_set_time() @vmmLibvirtObject.lifecycle_action def save(self, meter=None): -- 2.24.1 _______________________________________________ virt-tools-list mailing list virt-tools-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/virt-tools-list