2006-07-20 10:16:07 -05:00
|
|
|
#
|
|
|
|
# Copyright (C) 2006 Red Hat, Inc.
|
|
|
|
# Copyright (C) 2006 Hugh O. Brock <hbrock@redhat.com>
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
2007-11-20 10:12:20 -06:00
|
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
# MA 02110-1301 USA.
|
2006-07-20 10:16:07 -05:00
|
|
|
#
|
|
|
|
|
2009-04-03 13:15:14 -05:00
|
|
|
import logging
|
2006-07-20 10:16:07 -05:00
|
|
|
import threading
|
2010-12-10 08:57:42 -06:00
|
|
|
import traceback
|
|
|
|
|
2006-07-20 10:16:07 -05:00
|
|
|
import gtk
|
|
|
|
import gobject
|
|
|
|
|
2010-12-10 13:59:24 -06:00
|
|
|
import libvirt
|
|
|
|
|
2010-12-08 16:26:19 -06:00
|
|
|
from virtManager.baseclass import vmmGObjectUI
|
2010-02-11 11:32:00 -06:00
|
|
|
|
2010-03-03 09:51:09 -06:00
|
|
|
# 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):
|
2010-12-10 08:57:42 -06:00
|
|
|
args = [callback] + args
|
|
|
|
threading.Thread.__init__(self, target=cb_wrapper, args=args)
|
2006-07-20 10:16:07 -05:00
|
|
|
|
2010-03-03 09:51:09 -06:00
|
|
|
def run(self):
|
|
|
|
threading.Thread.run(self)
|
2006-07-20 11:52:00 -05:00
|
|
|
|
2010-12-10 08:57:42 -06:00
|
|
|
def cb_wrapper(callback, asyncjob, *args, **kwargs):
|
|
|
|
try:
|
|
|
|
callback(asyncjob, *args, **kwargs)
|
|
|
|
except Exception, e:
|
2010-12-10 13:59:24 -06:00
|
|
|
# If job is cancelled, don't report error to user.
|
|
|
|
if (isinstance(e, libvirt.libvirtError) and
|
|
|
|
asyncjob.can_cancel() and
|
|
|
|
asyncjob.job_canceled):
|
|
|
|
return
|
|
|
|
|
2010-12-10 13:40:08 -06:00
|
|
|
asyncjob.set_error(str(e), "".join(traceback.format_exc()))
|
|
|
|
|
|
|
|
def _simple_async(callback, args, title, text, parent, errorintro,
|
2011-04-14 14:28:22 -05:00
|
|
|
show_progress, simplecb):
|
|
|
|
"""
|
|
|
|
@show_progress: Whether to actually show a progress dialog
|
|
|
|
@simplecb: If true, build a callback wrapper that ignores the asyncjob
|
|
|
|
param that's passed to every cb by default
|
|
|
|
"""
|
|
|
|
docb = callback
|
|
|
|
if simplecb:
|
|
|
|
def tmpcb(job, *args, **kwargs):
|
|
|
|
ignore = job
|
|
|
|
callback(*args, **kwargs)
|
|
|
|
docb = tmpcb
|
|
|
|
|
|
|
|
asyncjob = vmmAsyncJob(docb, args, title, text, parent.topwin,
|
2011-04-14 14:43:31 -05:00
|
|
|
show_progress=show_progress,
|
|
|
|
run_main=parent.config.support_threading)
|
2010-12-10 13:40:08 -06:00
|
|
|
error, details = asyncjob.run()
|
|
|
|
if error is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
error = errorintro + ": " + error
|
2011-04-06 10:22:03 -05:00
|
|
|
parent.err.show_err(error,
|
2011-04-06 10:52:26 -05:00
|
|
|
details=details)
|
2010-12-10 08:57:42 -06:00
|
|
|
|
2010-03-03 09:51:09 -06:00
|
|
|
# Displays a progress bar while executing the "callback" method.
|
2010-12-08 16:26:19 -06:00
|
|
|
class vmmAsyncJob(vmmGObjectUI):
|
2006-07-20 11:52:00 -05:00
|
|
|
|
2010-12-10 13:40:08 -06:00
|
|
|
@staticmethod
|
2011-04-14 14:28:22 -05:00
|
|
|
def simple_async(callback, args, title, text, parent, errorintro,
|
|
|
|
simplecb=True):
|
|
|
|
_simple_async(callback, args, title, text, parent, errorintro, True,
|
|
|
|
simplecb)
|
2010-12-10 13:40:08 -06:00
|
|
|
|
|
|
|
@staticmethod
|
2011-04-14 14:28:22 -05:00
|
|
|
def simple_async_noshow(callback, args, parent, errorintro, simplecb=True):
|
|
|
|
_simple_async(callback, args, "", "", parent, errorintro, False,
|
|
|
|
simplecb)
|
2010-12-10 13:40:08 -06:00
|
|
|
|
|
|
|
|
2011-04-14 07:47:42 -05:00
|
|
|
def __init__(self, callback, args, title, text, parent,
|
2010-12-10 13:40:08 -06:00
|
|
|
run_main=True, show_progress=True,
|
|
|
|
cancel_back=None, cancel_args=None):
|
2011-04-14 14:43:31 -05:00
|
|
|
"""
|
|
|
|
@run_main: If False, run synchronously without a separate thread
|
|
|
|
@show_progress: If False, don't actually show a progress dialog
|
|
|
|
@cancel_back: If operation supports cancelling, call this function
|
|
|
|
when cancel button is clicked
|
|
|
|
@cancel_args: Arguments for optional cancel_back
|
|
|
|
"""
|
2010-12-08 16:26:19 -06:00
|
|
|
vmmGObjectUI.__init__(self, "vmm-progress.glade", "vmm-progress")
|
2007-02-01 13:16:44 -06:00
|
|
|
|
2010-12-08 16:26:19 -06:00
|
|
|
self.run_main = bool(run_main)
|
2010-12-10 13:40:08 -06:00
|
|
|
self.show_progress = bool(show_progress)
|
2010-12-08 11:52:33 -06:00
|
|
|
self.cancel_job = cancel_back
|
|
|
|
self.cancel_args = cancel_args or []
|
2010-12-10 08:57:42 -06:00
|
|
|
self.cancel_args = [self] + self.cancel_args
|
2010-12-08 11:52:33 -06:00
|
|
|
if self.cancel_job:
|
|
|
|
self.window.get_widget("cancel-async-job").show()
|
|
|
|
else:
|
|
|
|
self.window.get_widget("cancel-async-job").hide()
|
|
|
|
self.job_canceled = False
|
2007-02-01 13:16:44 -06:00
|
|
|
|
2009-04-03 13:15:15 -05:00
|
|
|
self._error_info = None
|
2010-03-03 10:14:59 -06:00
|
|
|
self._data = None
|
|
|
|
|
2007-02-01 13:16:44 -06:00
|
|
|
self.stage = self.window.get_widget("pbar-stage")
|
|
|
|
self.pbar = self.window.get_widget("pbar")
|
2010-12-08 13:14:38 -06:00
|
|
|
self.window.get_widget("pbar-text").set_text(text)
|
2011-04-14 07:47:42 -05:00
|
|
|
self.topwin.set_transient_for(parent)
|
2007-02-01 13:16:44 -06:00
|
|
|
|
2010-12-10 08:57:42 -06:00
|
|
|
args = [self] + args
|
2010-03-03 09:51:09 -06:00
|
|
|
self.bg_thread = asyncJobWorker(callback, args)
|
2009-04-03 13:15:14 -05:00
|
|
|
self.bg_thread.setDaemon(True)
|
2007-01-12 14:39:18 -06:00
|
|
|
self.is_pulsing = True
|
2006-07-20 10:16:07 -05:00
|
|
|
|
2010-12-10 08:57:42 -06:00
|
|
|
self.window.signal_autoconnect({
|
|
|
|
"on_async_job_delete_event" : self.delete,
|
|
|
|
"on_async_job_cancel_clicked" : self.cancel,
|
|
|
|
})
|
|
|
|
|
|
|
|
self.topwin.set_title(title)
|
|
|
|
|
2006-07-20 10:16:07 -05:00
|
|
|
def run(self):
|
2011-04-18 10:12:36 -05:00
|
|
|
timer = self.safe_timeout_add(100, self.exit_if_necessary)
|
2010-12-10 13:40:08 -06:00
|
|
|
|
|
|
|
if self.show_progress:
|
|
|
|
self.topwin.present()
|
2010-12-08 14:23:40 -06:00
|
|
|
|
|
|
|
if not self.cancel_job:
|
|
|
|
self.topwin.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
|
2010-03-03 09:51:09 -06:00
|
|
|
|
|
|
|
if self.run_main:
|
|
|
|
self.bg_thread.start()
|
|
|
|
gtk.main()
|
|
|
|
else:
|
|
|
|
self.bg_thread.run()
|
|
|
|
|
2008-11-18 16:01:22 -06:00
|
|
|
gobject.source_remove(timer)
|
|
|
|
timer = 0
|
2009-04-03 13:15:14 -05:00
|
|
|
|
|
|
|
if self.bg_thread.isAlive():
|
|
|
|
# This can happen if the user closes the whole app while the
|
|
|
|
# async dialog is running. This forces us to clean up properly
|
|
|
|
# and not leave a dead process around.
|
|
|
|
logging.debug("Forcing main_quit from async job.")
|
2010-02-11 11:32:00 -06:00
|
|
|
self.exit_if_necessary(force_exit=True)
|
2009-04-03 13:15:14 -05:00
|
|
|
|
2007-02-01 13:16:44 -06:00
|
|
|
self.topwin.destroy()
|
2011-04-11 14:05:16 -05:00
|
|
|
self.cleanup()
|
2010-12-10 08:57:42 -06:00
|
|
|
return self._get_error()
|
2006-07-20 10:16:07 -05:00
|
|
|
|
2011-04-11 14:05:16 -05:00
|
|
|
def cleanup(self):
|
|
|
|
vmmGObjectUI.cleanup(self)
|
|
|
|
self.bg_thread = None
|
|
|
|
self.cancel_job = None
|
|
|
|
self.cancel_args = None
|
|
|
|
|
2010-12-08 11:52:33 -06:00
|
|
|
def delete(self, ignore1=None, ignore2=None):
|
|
|
|
thread_active = (self.bg_thread.isAlive() or not self.run_main)
|
|
|
|
if self.cancel_job and thread_active:
|
|
|
|
res = self.err.warn_chkbox(
|
|
|
|
text1=_("Cancel the job before closing window?"),
|
|
|
|
buttons=gtk.BUTTONS_YES_NO)
|
|
|
|
if res:
|
|
|
|
# The job may end after we click 'Yes', so check whether the
|
|
|
|
# thread is active again
|
|
|
|
thread_active = (self.bg_thread.isAlive() or not self.run_main)
|
|
|
|
if thread_active:
|
|
|
|
self.cancel()
|
|
|
|
|
2010-12-08 13:14:38 -06:00
|
|
|
def set_stage_text(self, text, canceling=False):
|
|
|
|
if self.job_canceled and not canceling:
|
|
|
|
return
|
|
|
|
self.stage.set_text(text)
|
|
|
|
|
|
|
|
def hide_warning(self):
|
|
|
|
self.window.get_widget("warning-box").hide()
|
|
|
|
|
2010-12-08 11:52:33 -06:00
|
|
|
def show_warning(self, summary):
|
2010-12-08 13:14:38 -06:00
|
|
|
markup = "<small>%s</small>" % summary
|
|
|
|
self.window.get_widget("warning-box").show()
|
|
|
|
self.window.get_widget("warning-text").set_markup(markup)
|
2010-12-08 11:52:33 -06:00
|
|
|
|
2010-12-10 13:59:24 -06:00
|
|
|
def can_cancel(self):
|
|
|
|
return bool(self.cancel_job)
|
|
|
|
|
2010-12-08 11:52:33 -06:00
|
|
|
def cancel(self, ignore1=None, ignore2=None):
|
2010-12-08 13:14:38 -06:00
|
|
|
if not self.cancel_job:
|
|
|
|
return
|
|
|
|
|
|
|
|
self.cancel_job(*self.cancel_args)
|
|
|
|
if self.job_canceled:
|
|
|
|
self.hide_warning()
|
|
|
|
self.set_stage_text(_("Cancelling job..."), canceling=True)
|
2010-12-08 11:52:33 -06:00
|
|
|
|
2007-02-01 13:16:44 -06:00
|
|
|
def pulse_pbar(self, progress="", stage=None):
|
2007-04-13 15:01:03 -05:00
|
|
|
gtk.gdk.threads_enter()
|
|
|
|
try:
|
|
|
|
self.is_pulsing = True
|
|
|
|
self.pbar.set_text(progress)
|
2010-12-08 13:14:38 -06:00
|
|
|
self.set_stage_text(stage or _("Processing..."))
|
2007-04-13 15:01:03 -05:00
|
|
|
finally:
|
|
|
|
gtk.gdk.threads_leave()
|
2009-04-03 13:15:14 -05:00
|
|
|
|
2007-01-10 15:11:57 -06:00
|
|
|
|
2007-02-01 13:16:44 -06:00
|
|
|
def set_pbar_fraction(self, frac, progress, stage=None):
|
2007-01-10 15:11:57 -06:00
|
|
|
# callback for progress meter when file size is known
|
2007-04-13 15:01:03 -05:00
|
|
|
gtk.gdk.threads_enter()
|
|
|
|
try:
|
2010-11-29 13:06:43 -06:00
|
|
|
self.is_pulsing = False
|
2010-12-08 13:14:38 -06:00
|
|
|
self.set_stage_text(stage or _("Processing..."))
|
2007-04-13 15:01:03 -05:00
|
|
|
self.pbar.set_text(progress)
|
2010-12-08 13:14:38 -06:00
|
|
|
|
2009-12-03 12:36:28 -06:00
|
|
|
if frac > 1:
|
|
|
|
frac = 1.0
|
|
|
|
if frac < 0:
|
|
|
|
frac = 0
|
2007-04-13 15:01:03 -05:00
|
|
|
self.pbar.set_fraction(frac)
|
|
|
|
finally:
|
|
|
|
gtk.gdk.threads_leave()
|
|
|
|
|
2007-01-10 15:11:57 -06:00
|
|
|
|
2007-02-01 13:16:44 -06:00
|
|
|
def set_pbar_done(self, progress, stage=None):
|
2007-01-10 15:11:57 -06:00
|
|
|
#callback for progress meter when progress is done
|
2007-04-13 15:01:03 -05:00
|
|
|
gtk.gdk.threads_enter()
|
|
|
|
try:
|
2010-11-29 13:06:43 -06:00
|
|
|
self.is_pulsing = False
|
2010-12-08 13:14:38 -06:00
|
|
|
self.set_stage_text(stage or _("Completed"))
|
2007-04-13 15:01:03 -05:00
|
|
|
self.pbar.set_text(progress)
|
|
|
|
self.pbar.set_fraction(1)
|
|
|
|
finally:
|
|
|
|
gtk.gdk.threads_leave()
|
2007-02-01 13:16:44 -06:00
|
|
|
|
2009-04-03 13:15:15 -05:00
|
|
|
def set_error(self, error, details):
|
|
|
|
self._error_info = (error, details)
|
|
|
|
|
2010-12-10 08:57:42 -06:00
|
|
|
def _get_error(self):
|
2009-04-03 13:15:15 -05:00
|
|
|
if not self._error_info:
|
|
|
|
return (None, None)
|
|
|
|
return self._error_info
|
|
|
|
|
2010-03-03 10:14:59 -06:00
|
|
|
def set_data(self, data):
|
|
|
|
self._data = data
|
|
|
|
def get_data(self):
|
|
|
|
return self._data
|
|
|
|
|
2010-02-11 11:32:00 -06:00
|
|
|
def exit_if_necessary(self, force_exit=False):
|
2010-03-03 09:51:09 -06:00
|
|
|
thread_active = (self.bg_thread.isAlive() or not self.run_main)
|
|
|
|
|
|
|
|
if thread_active and not force_exit:
|
2010-02-11 11:32:00 -06:00
|
|
|
if (self.is_pulsing):
|
|
|
|
# Don't call pulse_pbar: this function is thread wrapped
|
2007-01-10 15:11:57 -06:00
|
|
|
self.pbar.pulse()
|
2006-07-20 11:52:00 -05:00
|
|
|
return True
|
2006-07-20 10:16:07 -05:00
|
|
|
else:
|
2010-03-03 09:51:09 -06:00
|
|
|
if self.run_main:
|
|
|
|
gtk.main_quit()
|
2006-07-20 11:52:00 -05:00
|
|
|
return False
|