mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-09-20 13:44:17 +03:00
asyncjob: Fix issues with multiple dialogs (bz 1003101)
Basically, drop usage of nested main loops. As has been documented in other commit messages, we use nested main loops in ways they aren't supposed to be used. They gave us async behavior that would block callers, but had weird behavior in some edge cases. Switch to having async dialogs be 100% async, requiring the user to pass in a completion callback which is triggered after the async action is complete.
This commit is contained in:
@@ -893,37 +893,6 @@ class vmmAddHardware(vmmGObjectUI):
|
|||||||
notebook.get_nth_page(page).show()
|
notebook.get_nth_page(page).show()
|
||||||
notebook.set_current_page(page)
|
notebook.set_current_page(page)
|
||||||
|
|
||||||
def finish(self, ignore=None):
|
|
||||||
notebook = self.widget("create-pages")
|
|
||||||
try:
|
|
||||||
if self.validate(notebook.get_current_page()) is False:
|
|
||||||
return
|
|
||||||
except Exception, e:
|
|
||||||
self.err.show_err(_("Uncaught error validating hardware "
|
|
||||||
"input: %s") % str(e))
|
|
||||||
return
|
|
||||||
|
|
||||||
self.topwin.set_sensitive(False)
|
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
|
||||||
|
|
||||||
try:
|
|
||||||
failure, errinfo = self.add_device()
|
|
||||||
error, details = errinfo or (None, None)
|
|
||||||
except Exception, e:
|
|
||||||
failure = True
|
|
||||||
error = _("Unable to add device: %s") % str(e)
|
|
||||||
details = "".join(traceback.format_exc())
|
|
||||||
|
|
||||||
if error is not None:
|
|
||||||
self.err.show_err(error, details=details)
|
|
||||||
|
|
||||||
self.topwin.set_sensitive(True)
|
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
|
||||||
|
|
||||||
self._dev = None
|
|
||||||
if not failure:
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def show_pair_combo(self, basename, show_combo):
|
def show_pair_combo(self, basename, show_combo):
|
||||||
combo = self.widget(basename + "-combo")
|
combo = self.widget(basename + "-combo")
|
||||||
label = self.widget(basename + "-label")
|
label = self.widget(basename + "-label")
|
||||||
@@ -1137,36 +1106,12 @@ class vmmAddHardware(vmmGObjectUI):
|
|||||||
# Add device methods #
|
# Add device methods #
|
||||||
######################
|
######################
|
||||||
|
|
||||||
def _storage_progress(self):
|
def setup_device(self, asyncjob):
|
||||||
def do_file_allocate(asyncjob, disk):
|
logging.debug("Running setup for device=%s", self._dev)
|
||||||
meter = asyncjob.get_meter()
|
self._dev.setup(meter=asyncjob.get_meter())
|
||||||
|
logging.debug("Setup complete")
|
||||||
logging.debug("Starting background file allocate process")
|
|
||||||
disk.setup(meter=meter)
|
|
||||||
logging.debug("Allocation completed")
|
|
||||||
|
|
||||||
progWin = vmmAsyncJob(do_file_allocate,
|
|
||||||
[self._dev],
|
|
||||||
_("Creating Storage File"),
|
|
||||||
_("Allocation of disk storage may take "
|
|
||||||
"a few minutes to complete."),
|
|
||||||
self.topwin)
|
|
||||||
|
|
||||||
return progWin.run()
|
|
||||||
|
|
||||||
def setup_device(self):
|
|
||||||
if (self._dev.virtual_device_type == self._dev.VIRTUAL_DEV_DISK and
|
|
||||||
self._dev.creating_storage()):
|
|
||||||
return self._storage_progress()
|
|
||||||
|
|
||||||
return self._dev.setup()
|
|
||||||
|
|
||||||
def add_device(self):
|
def add_device(self):
|
||||||
ret = self.setup_device()
|
|
||||||
if ret and ret[0]:
|
|
||||||
# Encountered an error
|
|
||||||
return (True, ret)
|
|
||||||
|
|
||||||
self._dev.get_xml_config()
|
self._dev.get_xml_config()
|
||||||
logging.debug("Adding device:\n" + self._dev.get_xml_config())
|
logging.debug("Adding device:\n" + self._dev.get_xml_config())
|
||||||
|
|
||||||
@@ -1197,7 +1142,7 @@ class vmmAddHardware(vmmGObjectUI):
|
|||||||
modal=True)
|
modal=True)
|
||||||
|
|
||||||
if not res:
|
if not res:
|
||||||
return (False, None)
|
return False
|
||||||
|
|
||||||
# Alter persistent config
|
# Alter persistent config
|
||||||
try:
|
try:
|
||||||
@@ -1206,9 +1151,52 @@ class vmmAddHardware(vmmGObjectUI):
|
|||||||
self.vm.add_device(self._dev)
|
self.vm.add_device(self._dev)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.err.show_err(_("Error adding device: %s" % str(e)))
|
self.err.show_err(_("Error adding device: %s" % str(e)))
|
||||||
return (True, None)
|
return True
|
||||||
|
|
||||||
return (False, None)
|
return False
|
||||||
|
|
||||||
|
def _finish_cb(self, error, details):
|
||||||
|
failure = True
|
||||||
|
if not error:
|
||||||
|
try:
|
||||||
|
failure = self.add_device()
|
||||||
|
except Exception, e:
|
||||||
|
failure = True
|
||||||
|
error = _("Unable to add device: %s") % str(e)
|
||||||
|
details = "".join(traceback.format_exc())
|
||||||
|
|
||||||
|
if error is not None:
|
||||||
|
self.err.show_err(error, details=details)
|
||||||
|
|
||||||
|
self.topwin.set_sensitive(True)
|
||||||
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
||||||
|
|
||||||
|
self._dev = None
|
||||||
|
if not failure:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def finish(self, ignore=None):
|
||||||
|
notebook = self.widget("create-pages")
|
||||||
|
try:
|
||||||
|
if self.validate(notebook.get_current_page()) is False:
|
||||||
|
return
|
||||||
|
except Exception, e:
|
||||||
|
self.err.show_err(_("Uncaught error validating hardware "
|
||||||
|
"input: %s") % str(e))
|
||||||
|
return
|
||||||
|
|
||||||
|
self.topwin.set_sensitive(False)
|
||||||
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
||||||
|
|
||||||
|
progWin = vmmAsyncJob(self.setup_device, [],
|
||||||
|
self._finish_cb, [],
|
||||||
|
_("Creating device"),
|
||||||
|
_("Depending on the device, this may take "
|
||||||
|
"a few minutes to complete."),
|
||||||
|
self.topwin)
|
||||||
|
progWin.run()
|
||||||
|
|
||||||
|
|
||||||
###########################
|
###########################
|
||||||
|
@@ -86,15 +86,6 @@ class vmmMeter(urlgrabber.progress.BaseMeter):
|
|||||||
self.started = False
|
self.started = False
|
||||||
|
|
||||||
|
|
||||||
# This thin wrapper only exists so we can put debugging
|
|
||||||
# code in the run() method every now & then
|
|
||||||
class asyncJobWorker(threading.Thread):
|
|
||||||
def __init__(self, callback, args):
|
|
||||||
args = [callback] + args
|
|
||||||
threading.Thread.__init__(self, target=cb_wrapper, args=args)
|
|
||||||
self.daemon = True
|
|
||||||
|
|
||||||
|
|
||||||
def cb_wrapper(callback, asyncjob, *args, **kwargs):
|
def cb_wrapper(callback, asyncjob, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
callback(asyncjob, *args, **kwargs)
|
callback(asyncjob, *args, **kwargs)
|
||||||
@@ -108,8 +99,22 @@ def cb_wrapper(callback, asyncjob, *args, **kwargs):
|
|||||||
asyncjob.set_error(str(e), "".join(traceback.format_exc()))
|
asyncjob.set_error(str(e), "".join(traceback.format_exc()))
|
||||||
|
|
||||||
|
|
||||||
|
def _simple_async_done_cb(error, details, errorintro,
|
||||||
|
errorcb, parent, finish_cb):
|
||||||
|
if error:
|
||||||
|
if errorcb:
|
||||||
|
errorcb(error, details)
|
||||||
|
else:
|
||||||
|
error = errorintro + ": " + error
|
||||||
|
parent.err.show_err(error,
|
||||||
|
details=details)
|
||||||
|
|
||||||
|
if finish_cb:
|
||||||
|
finish_cb()
|
||||||
|
|
||||||
|
|
||||||
def _simple_async(callback, args, title, text, parent, errorintro,
|
def _simple_async(callback, args, title, text, parent, errorintro,
|
||||||
show_progress, simplecb, errorcb):
|
show_progress, simplecb, errorcb, finish_cb):
|
||||||
"""
|
"""
|
||||||
@show_progress: Whether to actually show a progress dialog
|
@show_progress: Whether to actually show a progress dialog
|
||||||
@simplecb: If true, build a callback wrapper that ignores the asyncjob
|
@simplecb: If true, build a callback wrapper that ignores the asyncjob
|
||||||
@@ -122,18 +127,12 @@ def _simple_async(callback, args, title, text, parent, errorintro,
|
|||||||
callback(*args, **kwargs)
|
callback(*args, **kwargs)
|
||||||
docb = tmpcb
|
docb = tmpcb
|
||||||
|
|
||||||
asyncjob = vmmAsyncJob(docb, args, title, text, parent.topwin,
|
asyncjob = vmmAsyncJob(docb, args,
|
||||||
|
_simple_async_done_cb,
|
||||||
|
(errorintro, errorcb, parent, finish_cb),
|
||||||
|
title, text, parent.topwin,
|
||||||
show_progress=show_progress)
|
show_progress=show_progress)
|
||||||
error, details = asyncjob.run()
|
asyncjob.run()
|
||||||
if error is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if errorcb:
|
|
||||||
errorcb(error, details)
|
|
||||||
else:
|
|
||||||
error = errorintro + ": " + error
|
|
||||||
parent.err.show_err(error,
|
|
||||||
details=details)
|
|
||||||
|
|
||||||
|
|
||||||
def idle_wrapper(fn):
|
def idle_wrapper(fn):
|
||||||
@@ -148,18 +147,22 @@ class vmmAsyncJob(vmmGObjectUI):
|
|||||||
"""
|
"""
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def simple_async(callback, args, title, text, parent, errorintro,
|
def simple_async(callback, args, title, text, parent, errorintro,
|
||||||
simplecb=True, errorcb=None):
|
simplecb=True, errorcb=None, finish_cb=None):
|
||||||
_simple_async(callback, args, title, text, parent, errorintro, True,
|
_simple_async(callback, args,
|
||||||
simplecb, errorcb)
|
title, text, parent, errorintro, True,
|
||||||
|
simplecb, errorcb, finish_cb)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def simple_async_noshow(callback, args, parent, errorintro,
|
def simple_async_noshow(callback, args, parent, errorintro,
|
||||||
simplecb=True, errorcb=None):
|
simplecb=True, errorcb=None, finish_cb=None):
|
||||||
_simple_async(callback, args, "", "", parent, errorintro, False,
|
_simple_async(callback, args,
|
||||||
simplecb, errorcb)
|
"", "", parent, errorintro, False,
|
||||||
|
simplecb, errorcb, finish_cb)
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, callback, args, title, text, parent,
|
def __init__(self,
|
||||||
|
callback, args, finish_cb, finish_args,
|
||||||
|
title, text, parent,
|
||||||
show_progress=True, cancel_cb=None):
|
show_progress=True, cancel_cb=None):
|
||||||
"""
|
"""
|
||||||
@show_progress: If False, don't actually show a progress dialog
|
@show_progress: If False, don't actually show a progress dialog
|
||||||
@@ -175,15 +178,19 @@ class vmmAsyncJob(vmmGObjectUI):
|
|||||||
self.cancel_cb = cancel_cb[0]
|
self.cancel_cb = cancel_cb[0]
|
||||||
self.cancel_args = [self] + list(cancel_cb[1:])
|
self.cancel_args = [self] + list(cancel_cb[1:])
|
||||||
self.job_canceled = False
|
self.job_canceled = False
|
||||||
|
self._finish_cb = finish_cb
|
||||||
|
self._finish_args = finish_args or ()
|
||||||
|
|
||||||
|
self._timer = None
|
||||||
self._error_info = None
|
self._error_info = None
|
||||||
self._data = None
|
self._data = None
|
||||||
|
|
||||||
self._is_pulsing = True
|
self._is_pulsing = True
|
||||||
self._meter = None
|
self._meter = None
|
||||||
|
|
||||||
args = [self] + args
|
self._bg_thread = threading.Thread(target=cb_wrapper,
|
||||||
self._bg_thread = asyncJobWorker(callback, args)
|
args=[callback, self] + args)
|
||||||
|
self._bg_thread.daemon = True
|
||||||
logging.debug("Creating async job for function cb=%s", callback)
|
logging.debug("Creating async job for function cb=%s", callback)
|
||||||
|
|
||||||
self.builder.connect_signals({
|
self.builder.connect_signals({
|
||||||
@@ -278,8 +285,19 @@ class vmmAsyncJob(vmmGObjectUI):
|
|||||||
self.widget("warning-box").show()
|
self.widget("warning-box").show()
|
||||||
self.widget("warning-text").set_markup(markup)
|
self.widget("warning-text").set_markup(markup)
|
||||||
|
|
||||||
|
def _thread_finished(self):
|
||||||
|
GLib.source_remove(self._timer)
|
||||||
|
self.topwin.destroy()
|
||||||
|
self.cleanup()
|
||||||
|
|
||||||
|
error = None
|
||||||
|
details = None
|
||||||
|
if self._error_info:
|
||||||
|
error, details = self._error_info
|
||||||
|
self._finish_cb(error, details, *self._finish_args)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
timer = GLib.timeout_add(100, self._exit_if_necessary)
|
self._timer = GLib.timeout_add(100, self._exit_if_necessary)
|
||||||
|
|
||||||
if self.show_progress:
|
if self.show_progress:
|
||||||
self.topwin.present()
|
self.topwin.present()
|
||||||
@@ -287,15 +305,7 @@ class vmmAsyncJob(vmmGObjectUI):
|
|||||||
if not self.cancel_cb and self.show_progress:
|
if not self.cancel_cb and self.show_progress:
|
||||||
self.topwin.get_window().set_cursor(
|
self.topwin.get_window().set_cursor(
|
||||||
Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
||||||
|
|
||||||
self._bg_thread.start()
|
self._bg_thread.start()
|
||||||
Gtk.main()
|
|
||||||
|
|
||||||
GLib.source_remove(timer)
|
|
||||||
|
|
||||||
self.topwin.destroy()
|
|
||||||
self.cleanup()
|
|
||||||
return self._error_info or (None, None)
|
|
||||||
|
|
||||||
|
|
||||||
####################################################################
|
####################################################################
|
||||||
@@ -306,7 +316,7 @@ class vmmAsyncJob(vmmGObjectUI):
|
|||||||
|
|
||||||
def _exit_if_necessary(self):
|
def _exit_if_necessary(self):
|
||||||
if not self._bg_thread.is_alive():
|
if not self._bg_thread.is_alive():
|
||||||
Gtk.main_quit()
|
self._thread_finished()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not self._is_pulsing or not self.show_progress:
|
if not self._is_pulsing or not self.show_progress:
|
||||||
|
@@ -783,6 +783,19 @@ class vmmCloneVM(vmmGObjectUI):
|
|||||||
self.clone_design = cd
|
self.clone_design = cd
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _finish_cb(self, error, details):
|
||||||
|
self.topwin.set_sensitive(True)
|
||||||
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
||||||
|
|
||||||
|
if error is not None:
|
||||||
|
error = (_("Error creating virtual machine clone '%s': %s") %
|
||||||
|
(self.clone_design.clone_name, error))
|
||||||
|
self.err.show_err(error, details=details)
|
||||||
|
else:
|
||||||
|
self.close()
|
||||||
|
self.conn.schedule_priority_tick(pollvm=True)
|
||||||
|
|
||||||
def finish(self, src_ignore):
|
def finish(self, src_ignore):
|
||||||
try:
|
try:
|
||||||
if not self.validate():
|
if not self.validate():
|
||||||
@@ -792,7 +805,8 @@ class vmmCloneVM(vmmGObjectUI):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.topwin.set_sensitive(False)
|
self.topwin.set_sensitive(False)
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
||||||
|
|
||||||
title = (_("Creating virtual machine clone '%s'") %
|
title = (_("Creating virtual machine clone '%s'") %
|
||||||
self.clone_design.clone_name)
|
self.clone_design.clone_name)
|
||||||
@@ -800,19 +814,9 @@ class vmmCloneVM(vmmGObjectUI):
|
|||||||
if self.clone_design.clone_disks:
|
if self.clone_design.clone_disks:
|
||||||
text = title + _(" and selected storage (this may take a while)")
|
text = title + _(" and selected storage (this may take a while)")
|
||||||
|
|
||||||
progWin = vmmAsyncJob(self._async_clone, [], title, text, self.topwin)
|
progWin = vmmAsyncJob(self._async_clone, [], self._finish_cb, [],
|
||||||
error, details = progWin.run()
|
title, text, self.topwin)
|
||||||
|
progWin.run()
|
||||||
self.topwin.set_sensitive(True)
|
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
|
||||||
|
|
||||||
if error is not None:
|
|
||||||
error = (_("Error creating virtual machine clone '%s': %s") %
|
|
||||||
(self.clone_design.clone_name, error))
|
|
||||||
self.err.show_err(error, details=details)
|
|
||||||
else:
|
|
||||||
self.close()
|
|
||||||
self.conn.schedule_priority_tick(pollvm=True)
|
|
||||||
|
|
||||||
def _async_clone(self, asyncjob):
|
def _async_clone(self, asyncjob):
|
||||||
try:
|
try:
|
||||||
|
@@ -1778,6 +1778,12 @@ class vmmCreate(vmmGObjectUI):
|
|||||||
break
|
break
|
||||||
pagenum = self._get_next_pagenum(pagenum)
|
pagenum = self._get_next_pagenum(pagenum)
|
||||||
|
|
||||||
|
def _undo_finish_cursor(self):
|
||||||
|
self.topwin.set_sensitive(True)
|
||||||
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
||||||
|
|
||||||
|
|
||||||
def finish(self, src_ignore):
|
def finish(self, src_ignore):
|
||||||
# Validate the final page
|
# Validate the final page
|
||||||
page = self.widget("create-pages").get_current_page()
|
page = self.widget("create-pages").get_current_page()
|
||||||
@@ -1792,25 +1798,15 @@ class vmmCreate(vmmGObjectUI):
|
|||||||
self.topwin.set_sensitive(False)
|
self.topwin.set_sensitive(False)
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
||||||
|
|
||||||
def start_install():
|
if self.get_config_customize():
|
||||||
if not self.get_config_customize():
|
try:
|
||||||
self.start_install(guest)
|
self.customize(guest)
|
||||||
|
except Exception, e:
|
||||||
|
self._undo_finish_cursor()
|
||||||
|
self.err.show_err(_("Error starting installation: ") + str(e))
|
||||||
return
|
return
|
||||||
|
else:
|
||||||
self.customize(guest)
|
self.start_install(guest)
|
||||||
|
|
||||||
self._check_start_error(start_install)
|
|
||||||
|
|
||||||
def _undo_finish(self, ignore=None):
|
|
||||||
self.topwin.set_sensitive(True)
|
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
|
||||||
|
|
||||||
def _check_start_error(self, cb, *args, **kwargs):
|
|
||||||
try:
|
|
||||||
cb(*args, **kwargs)
|
|
||||||
except Exception, e:
|
|
||||||
self._undo_finish()
|
|
||||||
self.err.show_err(_("Error starting installation: ") + str(e))
|
|
||||||
|
|
||||||
def customize(self, guest):
|
def customize(self, guest):
|
||||||
virtinst_guest = vmmDomainVirtinst(self.conn, guest, self.guest.uuid)
|
virtinst_guest = vmmDomainVirtinst(self.conn, guest, self.guest.uuid)
|
||||||
@@ -1826,11 +1822,11 @@ class vmmCreate(vmmGObjectUI):
|
|||||||
cleanup_config_window()
|
cleanup_config_window()
|
||||||
if not self.is_visible():
|
if not self.is_visible():
|
||||||
return
|
return
|
||||||
self._check_start_error(self.start_install, guest)
|
self.start_install(guest)
|
||||||
|
|
||||||
def details_closed(ignore):
|
def details_closed(ignore):
|
||||||
cleanup_config_window()
|
cleanup_config_window()
|
||||||
self._undo_finish()
|
self._undo_finish_cursor()
|
||||||
self.widget("summary-customize").set_active(False)
|
self.widget("summary-customize").set_active(False)
|
||||||
|
|
||||||
cleanup_config_window()
|
cleanup_config_window()
|
||||||
@@ -1845,19 +1841,8 @@ class vmmCreate(vmmGObjectUI):
|
|||||||
details_closed))
|
details_closed))
|
||||||
self.config_window.show()
|
self.config_window.show()
|
||||||
|
|
||||||
def start_install(self, guest):
|
def _install_finished_cb(self, error, details):
|
||||||
progWin = vmmAsyncJob(self.do_install, [guest],
|
self._undo_finish_cursor()
|
||||||
_("Creating Virtual Machine"),
|
|
||||||
_("The virtual machine is now being "
|
|
||||||
"created. Allocation of disk storage "
|
|
||||||
"and retrieval of the installation "
|
|
||||||
"images may take a few minutes to "
|
|
||||||
"complete."),
|
|
||||||
self.topwin)
|
|
||||||
error, details = progWin.run()
|
|
||||||
|
|
||||||
self.topwin.set_sensitive(True)
|
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
|
||||||
|
|
||||||
if error:
|
if error:
|
||||||
error = (_("Unable to complete install: '%s'") % error)
|
error = (_("Unable to complete install: '%s'") % error)
|
||||||
@@ -1869,7 +1854,20 @@ class vmmCreate(vmmGObjectUI):
|
|||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
# Launch details dialog for new VM
|
# Launch details dialog for new VM
|
||||||
self.emit("action-show-vm", self.conn.get_uri(), guest.uuid)
|
self.emit("action-show-vm", self.conn.get_uri(), self.guest.uuid)
|
||||||
|
|
||||||
|
|
||||||
|
def start_install(self, guest):
|
||||||
|
progWin = vmmAsyncJob(self.do_install, [guest],
|
||||||
|
self._install_finished_cb, [],
|
||||||
|
_("Creating Virtual Machine"),
|
||||||
|
_("The virtual machine is now being "
|
||||||
|
"created. Allocation of disk storage "
|
||||||
|
"and retrieval of the installation "
|
||||||
|
"images may take a few minutes to "
|
||||||
|
"complete."),
|
||||||
|
self.topwin)
|
||||||
|
progWin.run()
|
||||||
|
|
||||||
def do_install(self, asyncjob, guest):
|
def do_install(self, asyncjob, guest):
|
||||||
meter = asyncjob.get_meter()
|
meter = asyncjob.get_meter()
|
||||||
|
@@ -1096,27 +1096,10 @@ class vmmCreateInterface(vmmGObjectUI):
|
|||||||
# Creation routines #
|
# Creation routines #
|
||||||
#####################
|
#####################
|
||||||
|
|
||||||
def finish(self, src):
|
def _finish_cb(self, error, details):
|
||||||
|
|
||||||
# Validate the final page
|
|
||||||
page = self.widget("pages").get_current_page()
|
|
||||||
if self.validate(page) is not True:
|
|
||||||
return False
|
|
||||||
|
|
||||||
activate = self.widget("interface-activate").get_active()
|
|
||||||
|
|
||||||
# Start the install
|
|
||||||
self.topwin.set_sensitive(False)
|
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
|
||||||
|
|
||||||
progWin = vmmAsyncJob(self.do_install, [activate],
|
|
||||||
_("Creating virtual interface"),
|
|
||||||
_("The virtual interface is now being created."),
|
|
||||||
self.topwin)
|
|
||||||
error, details = progWin.run()
|
|
||||||
|
|
||||||
self.topwin.set_sensitive(True)
|
self.topwin.set_sensitive(True)
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
||||||
|
|
||||||
if error:
|
if error:
|
||||||
error = _("Error creating interface: '%s'") % error
|
error = _("Error creating interface: '%s'") % error
|
||||||
@@ -1126,6 +1109,26 @@ class vmmCreateInterface(vmmGObjectUI):
|
|||||||
self.conn.schedule_priority_tick(polliface=True)
|
self.conn.schedule_priority_tick(polliface=True)
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
def finish(self, src):
|
||||||
|
# Validate the final page
|
||||||
|
page = self.widget("pages").get_current_page()
|
||||||
|
if self.validate(page) is not True:
|
||||||
|
return False
|
||||||
|
|
||||||
|
activate = self.widget("interface-activate").get_active()
|
||||||
|
|
||||||
|
# Start the install
|
||||||
|
self.topwin.set_sensitive(False)
|
||||||
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
||||||
|
|
||||||
|
progWin = vmmAsyncJob(self.do_install, [activate],
|
||||||
|
self._finish_cb, [],
|
||||||
|
_("Creating virtual interface"),
|
||||||
|
_("The virtual interface is now being created."),
|
||||||
|
self.topwin)
|
||||||
|
progWin.run()
|
||||||
|
|
||||||
def do_install(self, asyncjob, activate):
|
def do_install(self, asyncjob, activate):
|
||||||
meter = asyncjob.get_meter()
|
meter = asyncjob.get_meter()
|
||||||
self.interface.install(meter, create=activate)
|
self.interface.install(meter, create=activate)
|
||||||
|
@@ -432,20 +432,10 @@ class vmmCreatePool(vmmGObjectUI):
|
|||||||
self.widget("pool-forward").show()
|
self.widget("pool-forward").show()
|
||||||
self.widget("pool-pages").prev_page()
|
self.widget("pool-pages").prev_page()
|
||||||
|
|
||||||
def finish(self):
|
def _finish_cb(self, error, details):
|
||||||
self.topwin.set_sensitive(False)
|
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
|
||||||
build = self.widget("pool-build").get_active()
|
|
||||||
|
|
||||||
progWin = vmmAsyncJob(self._async_pool_create, [build],
|
|
||||||
_("Creating storage pool..."),
|
|
||||||
_("Creating the storage pool may take a "
|
|
||||||
"while..."),
|
|
||||||
self.topwin)
|
|
||||||
error, details = progWin.run()
|
|
||||||
|
|
||||||
self.topwin.set_sensitive(True)
|
self.topwin.set_sensitive(True)
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
||||||
|
|
||||||
if error:
|
if error:
|
||||||
error = _("Error creating pool: %s") % error
|
error = _("Error creating pool: %s") % error
|
||||||
@@ -455,6 +445,20 @@ class vmmCreatePool(vmmGObjectUI):
|
|||||||
self.conn.schedule_priority_tick(pollpool=True)
|
self.conn.schedule_priority_tick(pollpool=True)
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
self.topwin.set_sensitive(False)
|
||||||
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
||||||
|
build = self.widget("pool-build").get_active()
|
||||||
|
|
||||||
|
progWin = vmmAsyncJob(self._async_pool_create, [build],
|
||||||
|
self._finish_cb, [],
|
||||||
|
_("Creating storage pool..."),
|
||||||
|
_("Creating the storage pool may take a "
|
||||||
|
"while..."),
|
||||||
|
self.topwin)
|
||||||
|
progWin.run()
|
||||||
|
|
||||||
def _async_pool_create(self, asyncjob, build):
|
def _async_pool_create(self, asyncjob, build):
|
||||||
meter = asyncjob.get_meter()
|
meter = asyncjob.get_meter()
|
||||||
|
|
||||||
|
@@ -217,6 +217,20 @@ class vmmCreateVolume(vmmGObjectUI):
|
|||||||
if cap < alloc:
|
if cap < alloc:
|
||||||
alloc_widget.set_value(cap)
|
alloc_widget.set_value(cap)
|
||||||
|
|
||||||
|
def _finish_cb(self, error, details):
|
||||||
|
self.topwin.set_sensitive(True)
|
||||||
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
||||||
|
|
||||||
|
if error:
|
||||||
|
error = _("Error creating vol: %s") % error
|
||||||
|
self.show_err(error,
|
||||||
|
details=details)
|
||||||
|
else:
|
||||||
|
# vol-created will refresh the parent pool
|
||||||
|
self.emit("vol-created")
|
||||||
|
self.close()
|
||||||
|
|
||||||
def finish(self, src_ignore):
|
def finish(self, src_ignore):
|
||||||
try:
|
try:
|
||||||
if not self.validate():
|
if not self.validate():
|
||||||
@@ -229,26 +243,16 @@ class vmmCreateVolume(vmmGObjectUI):
|
|||||||
self.vol.get_xml_config())
|
self.vol.get_xml_config())
|
||||||
|
|
||||||
self.topwin.set_sensitive(False)
|
self.topwin.set_sensitive(False)
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
||||||
|
|
||||||
progWin = vmmAsyncJob(self._async_vol_create, [],
|
progWin = vmmAsyncJob(self._async_vol_create, [],
|
||||||
|
self._finish_cb, [],
|
||||||
_("Creating storage volume..."),
|
_("Creating storage volume..."),
|
||||||
_("Creating the storage volume may take a "
|
_("Creating the storage volume may take a "
|
||||||
"while..."),
|
"while..."),
|
||||||
self.topwin)
|
self.topwin)
|
||||||
error, details = progWin.run()
|
progWin.run()
|
||||||
|
|
||||||
self.topwin.set_sensitive(True)
|
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
|
||||||
|
|
||||||
if error:
|
|
||||||
error = _("Error creating vol: %s") % error
|
|
||||||
self.show_err(error,
|
|
||||||
details=details)
|
|
||||||
else:
|
|
||||||
# vol-created will refresh the parent pool
|
|
||||||
self.emit("vol-created")
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def _async_vol_create(self, asyncjob):
|
def _async_vol_create(self, asyncjob):
|
||||||
conn = self.conn.get_backend()
|
conn = self.conn.get_backend()
|
||||||
|
@@ -131,6 +131,17 @@ class vmmDeleteDialog(vmmGObjectUI):
|
|||||||
paths.append(row[STORAGE_ROW_PATH])
|
paths.append(row[STORAGE_ROW_PATH])
|
||||||
return paths
|
return paths
|
||||||
|
|
||||||
|
def _finish_cb(self, error, details):
|
||||||
|
self.topwin.set_sensitive(True)
|
||||||
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
||||||
|
|
||||||
|
if error is not None:
|
||||||
|
self.err.show_err(error, details=details)
|
||||||
|
|
||||||
|
self.conn.schedule_priority_tick(pollvm=True)
|
||||||
|
self.close()
|
||||||
|
|
||||||
def finish(self, src_ignore):
|
def finish(self, src_ignore):
|
||||||
devs = self.get_paths_to_delete()
|
devs = self.get_paths_to_delete()
|
||||||
|
|
||||||
@@ -146,7 +157,8 @@ class vmmDeleteDialog(vmmGObjectUI):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.topwin.set_sensitive(False)
|
self.topwin.set_sensitive(False)
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
||||||
|
|
||||||
title = _("Deleting virtual machine '%s'") % self.vm.get_name()
|
title = _("Deleting virtual machine '%s'") % self.vm.get_name()
|
||||||
text = title
|
text = title
|
||||||
@@ -154,17 +166,9 @@ class vmmDeleteDialog(vmmGObjectUI):
|
|||||||
text = title + _(" and selected storage (this may take a while)")
|
text = title + _(" and selected storage (this may take a while)")
|
||||||
|
|
||||||
progWin = vmmAsyncJob(self._async_delete, [devs],
|
progWin = vmmAsyncJob(self._async_delete, [devs],
|
||||||
|
self._finish_cb, [],
|
||||||
title, text, self.topwin)
|
title, text, self.topwin)
|
||||||
error, details = progWin.run()
|
progWin.run()
|
||||||
|
|
||||||
self.topwin.set_sensitive(True)
|
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
|
||||||
|
|
||||||
if error is not None:
|
|
||||||
self.err.show_err(error, details=details)
|
|
||||||
|
|
||||||
self.conn.schedule_priority_tick(pollvm=True)
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def _async_delete(self, asyncjob, paths):
|
def _async_delete(self, asyncjob, paths):
|
||||||
storage_errors = []
|
storage_errors = []
|
||||||
|
@@ -912,16 +912,17 @@ class vmmEngine(vmmGObject):
|
|||||||
|
|
||||||
def cb(asyncjob):
|
def cb(asyncjob):
|
||||||
vm.save(path, meter=asyncjob.get_meter())
|
vm.save(path, meter=asyncjob.get_meter())
|
||||||
|
def finish_cb(error, details):
|
||||||
|
if error is not None:
|
||||||
|
error = _("Error saving domain: %s") % error
|
||||||
|
src.err.show_err(error, details=details)
|
||||||
|
|
||||||
progWin = vmmAsyncJob(cb, [],
|
progWin = vmmAsyncJob(cb, [],
|
||||||
|
finish_cb, [],
|
||||||
_("Saving Virtual Machine"),
|
_("Saving Virtual Machine"),
|
||||||
_("Saving virtual machine memory to disk "),
|
_("Saving virtual machine memory to disk "),
|
||||||
src.topwin, cancel_cb=_cancel_cb)
|
src.topwin, cancel_cb=_cancel_cb)
|
||||||
error, details = progWin.run()
|
progWin.run()
|
||||||
|
|
||||||
if error is not None:
|
|
||||||
error = _("Error saving domain: %s") % error
|
|
||||||
src.err.show_err(error, details=details)
|
|
||||||
|
|
||||||
def _save_cancel(self, asyncjob, vm):
|
def _save_cancel(self, asyncjob, vm):
|
||||||
logging.debug("Cancelling save job")
|
logging.debug("Cancelling save job")
|
||||||
|
@@ -443,6 +443,20 @@ class vmmMigrateDialog(vmmGObjectUI):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _finish_cb(self, error, details, destconn):
|
||||||
|
self.topwin.set_sensitive(True)
|
||||||
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
||||||
|
|
||||||
|
if error:
|
||||||
|
error = _("Unable to migrate guest: %s") % error
|
||||||
|
self.err.show_err(error,
|
||||||
|
details=details)
|
||||||
|
else:
|
||||||
|
self.conn.schedule_priority_tick(pollvm=True)
|
||||||
|
destconn.schedule_priority_tick(pollvm=True)
|
||||||
|
self.close()
|
||||||
|
|
||||||
def finish(self, src_ignore):
|
def finish(self, src_ignore):
|
||||||
try:
|
try:
|
||||||
if not self.validate():
|
if not self.validate():
|
||||||
@@ -467,33 +481,22 @@ class vmmMigrateDialog(vmmGObjectUI):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.topwin.set_sensitive(False)
|
self.topwin.set_sensitive(False)
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.WATCH))
|
||||||
|
|
||||||
cancel_cb = None
|
cancel_cb = None
|
||||||
if self.vm.getjobinfo_supported:
|
if self.vm.getjobinfo_supported:
|
||||||
cancel_cb = (self.cancel_migration, self.vm)
|
cancel_cb = (self.cancel_migration, self.vm)
|
||||||
|
|
||||||
progWin = vmmAsyncJob(self._async_migrate,
|
progWin = vmmAsyncJob(
|
||||||
[self.vm, destconn, uri, rate, live, secure,
|
self._async_migrate,
|
||||||
max_downtime],
|
[self.vm, destconn, uri, rate, live, secure, max_downtime],
|
||||||
_("Migrating VM '%s'" % self.vm.get_name()),
|
self._finish_cb, [destconn],
|
||||||
(_("Migrating VM '%s' from %s to %s. "
|
_("Migrating VM '%s'" % self.vm.get_name()),
|
||||||
"This may take a while.") %
|
(_("Migrating VM '%s' from %s to %s. This may take a while.") %
|
||||||
(self.vm.get_name(), srchost, dsthost)),
|
(self.vm.get_name(), srchost, dsthost)),
|
||||||
self.topwin, cancel_cb=cancel_cb)
|
self.topwin, cancel_cb=cancel_cb)
|
||||||
error, details = progWin.run()
|
progWin.run()
|
||||||
|
|
||||||
self.topwin.set_sensitive(True)
|
|
||||||
self.topwin.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
|
||||||
|
|
||||||
if error:
|
|
||||||
error = _("Unable to migrate guest: %s") % error
|
|
||||||
self.err.show_err(error,
|
|
||||||
details=details)
|
|
||||||
else:
|
|
||||||
self.conn.schedule_priority_tick(pollvm=True)
|
|
||||||
destconn.schedule_priority_tick(pollvm=True)
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def _async_set_max_downtime(self, vm, max_downtime, migrate_thread):
|
def _async_set_max_downtime(self, vm, max_downtime, migrate_thread):
|
||||||
if not migrate_thread.isAlive():
|
if not migrate_thread.isAlive():
|
||||||
|
@@ -261,6 +261,18 @@ class vmmSnapshotPage(vmmGObjectUI):
|
|||||||
|
|
||||||
# XXX refresh in place
|
# XXX refresh in place
|
||||||
|
|
||||||
|
def _finish_cb(self, error, details):
|
||||||
|
self.topwin.set_sensitive(True)
|
||||||
|
self.topwin.get_window().set_cursor(
|
||||||
|
Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
||||||
|
|
||||||
|
if error is not None:
|
||||||
|
error = _("Error creating snapshot: %s") % error
|
||||||
|
self.err.show_err(error, details=details)
|
||||||
|
return
|
||||||
|
|
||||||
|
self._refresh_snapshots()
|
||||||
|
|
||||||
def _on_new_ok_clicked(self, ignore):
|
def _on_new_ok_clicked(self, ignore):
|
||||||
name = self.widget("snapshot-new-name").get_text()
|
name = self.widget("snapshot-new-name").get_text()
|
||||||
|
|
||||||
@@ -278,21 +290,11 @@ class vmmSnapshotPage(vmmGObjectUI):
|
|||||||
progWin = vmmAsyncJob(
|
progWin = vmmAsyncJob(
|
||||||
lambda ignore, xml: self.vm.create_snapshot(xml),
|
lambda ignore, xml: self.vm.create_snapshot(xml),
|
||||||
[newsnap.get_xml_config()],
|
[newsnap.get_xml_config()],
|
||||||
|
self._finish_cb, [],
|
||||||
_("Creating snapshot"),
|
_("Creating snapshot"),
|
||||||
_("Creating virtual machine snapshot"),
|
_("Creating virtual machine snapshot"),
|
||||||
self.topwin)
|
self.topwin)
|
||||||
|
progWin.run()
|
||||||
error, details = progWin.run()
|
|
||||||
self.topwin.set_sensitive(True)
|
|
||||||
self.topwin.get_window().set_cursor(
|
|
||||||
Gdk.Cursor.new(Gdk.CursorType.TOP_LEFT_ARROW))
|
|
||||||
|
|
||||||
if error is not None:
|
|
||||||
error = _("Error creating snapshot: %s") % error
|
|
||||||
self.err.show_err(error, details=details)
|
|
||||||
return
|
|
||||||
|
|
||||||
self._refresh_snapshots()
|
|
||||||
|
|
||||||
def _on_add_clicked(self, ignore):
|
def _on_add_clicked(self, ignore):
|
||||||
snap = self._get_current_snapshot()
|
snap = self._get_current_snapshot()
|
||||||
@@ -327,8 +329,8 @@ class vmmSnapshotPage(vmmGObjectUI):
|
|||||||
vmmAsyncJob.simple_async_noshow(self.vm.revert_to_snapshot,
|
vmmAsyncJob.simple_async_noshow(self.vm.revert_to_snapshot,
|
||||||
[snap], self,
|
[snap], self,
|
||||||
_("Error reverting to snapshot '%s'") %
|
_("Error reverting to snapshot '%s'") %
|
||||||
snap.get_name())
|
snap.get_name(),
|
||||||
self._refresh_snapshots()
|
finish_cb=self._refresh_snapshots)
|
||||||
|
|
||||||
def _on_delete_clicked(self, ignore):
|
def _on_delete_clicked(self, ignore):
|
||||||
snap = self._get_current_snapshot()
|
snap = self._get_current_snapshot()
|
||||||
@@ -346,8 +348,8 @@ class vmmSnapshotPage(vmmGObjectUI):
|
|||||||
|
|
||||||
logging.debug("Deleting snapshot '%s'", snap.get_name())
|
logging.debug("Deleting snapshot '%s'", snap.get_name())
|
||||||
vmmAsyncJob.simple_async_noshow(snap.delete, [], self,
|
vmmAsyncJob.simple_async_noshow(snap.delete, [], self,
|
||||||
_("Error deleting snapshot '%s'") % snap.get_name())
|
_("Error deleting snapshot '%s'") % snap.get_name(),
|
||||||
self._refresh_snapshots()
|
finish_cb=self._refresh_snapshots)
|
||||||
|
|
||||||
|
|
||||||
def _snapshot_selected(self, selection):
|
def _snapshot_selected(self, selection):
|
||||||
|
Reference in New Issue
Block a user