2013-10-28 00:59:46 +04:00
# Copyright (C) 2006, 2013 Red Hat, Inc.
2006-07-20 19:16:07 +04:00
# Copyright (C) 2006 Hugh O. Brock <hbrock@redhat.com>
#
2018-04-04 16:35:41 +03:00
# This work is licensed under the GNU GPLv2 or later.
2018-03-20 22:00:02 +03:00
# See the COPYING file in the top-level directory.
2006-07-20 19:16:07 +04:00
import threading
2010-12-10 17:57:42 +03:00
import traceback
2013-02-16 23:03:30 +04:00
from gi . repository import GLib
2006-07-20 19:16:07 +04:00
2010-12-10 22:59:24 +03:00
import libvirt
2015-09-19 03:28:55 +03:00
import virtinst . progress
2010-12-10 22:59:24 +03:00
2014-09-13 00:10:45 +04:00
from . baseclass import vmmGObjectUI
2010-02-11 20:32:00 +03:00
2013-04-13 22:34:52 +04:00
2021-04-08 02:42:32 +03:00
class _vmmMeter ( virtinst . progress . Meter ) :
2021-04-07 19:37:38 +03:00
def __init__ ( self , pbar_pulse , pbar_fraction , pbar_done ) :
2021-06-09 01:31:44 +03:00
virtinst . progress . Meter . __init__ ( self , quiet = True )
2012-02-10 19:24:43 +04:00
2021-04-07 19:37:38 +03:00
self . _pbar_pulse = pbar_pulse
self . _pbar_fraction = pbar_fraction
self . _pbar_done = pbar_done
2013-06-14 18:44:24 +04:00
2021-04-07 19:37:38 +03:00
#################
# Internal APIs #
#################
2012-02-10 19:24:43 +04:00
2021-04-08 02:42:32 +03:00
def _write ( self ) :
if self . _size is None :
2021-06-09 01:31:44 +03:00
self . _pbar_pulse ( " " , self . _text )
2012-02-10 19:24:43 +04:00
else :
2021-06-09 01:31:44 +03:00
fread = virtinst . progress . Meter . format_number ( self . _total_read )
rtime = virtinst . progress . Meter . format_time (
self . _meter . re . remaining_time ( ) , True )
frac = self . _meter . re . fraction_read ( )
out = " %3i %% %5s B %s ETA " % ( frac * 100 , fread , rtime )
self . _pbar_fraction ( frac , out , self . _text )
2012-02-10 19:24:43 +04:00
2021-04-07 19:37:38 +03:00
#############################################
# Public APIs specific to virt-manager code #
#############################################
def is_started ( self ) :
2021-06-09 01:31:44 +03:00
return bool ( self . _meter . start_time )
2021-04-07 19:37:38 +03:00
###################
# Meter overrides #
###################
def start ( self , * args , * * kwargs ) :
super ( ) . start ( * args , * * kwargs )
2021-04-08 02:42:32 +03:00
self . _write ( )
2021-04-07 19:37:38 +03:00
2021-04-08 02:42:32 +03:00
def update ( self , * args , * * kwargs ) :
super ( ) . update ( * args , * * kwargs )
self . _write ( )
2021-04-07 19:37:38 +03:00
2021-04-08 02:42:32 +03:00
def end ( self , * args , * * kwargs ) :
super ( ) . end ( * args , * * kwargs )
2021-04-07 19:37:38 +03:00
self . _pbar_done ( )
2012-02-10 19:24:43 +04:00
2010-12-10 17:57:42 +03:00
def cb_wrapper ( callback , asyncjob , * args , * * kwargs ) :
try :
callback ( asyncjob , * args , * * kwargs )
2017-05-05 19:47:21 +03:00
except Exception as e :
2010-12-10 22:59:24 +03:00
# If job is cancelled, don't report error to user.
if ( isinstance ( e , libvirt . libvirtError ) and
asyncjob . can_cancel ( ) and
asyncjob . job_canceled ) :
2020-08-26 20:26:08 +03:00
return # pragma: no cover
2010-12-10 22:59:24 +03:00
2010-12-10 22:40:08 +03:00
asyncjob . set_error ( str ( e ) , " " . join ( traceback . format_exc ( ) ) )
2013-04-13 22:34:52 +04:00
2013-10-10 01:41:08 +04:00
def _simple_async_done_cb ( error , details ,
parent , errorintro , errorcb , finish_cb ) :
2013-09-07 04:59:01 +04:00
if error :
if errorcb :
errorcb ( error , details )
else :
error = errorintro + " : " + error
parent . err . show_err ( error ,
details = details )
if finish_cb :
finish_cb ( )
2013-10-05 18:03:56 +04:00
def _simple_async ( callback , args , parent , title , text , errorintro ,
2013-09-07 04:59:01 +04:00
show_progress , simplecb , errorcb , finish_cb ) :
2011-04-14 23:28:22 +04:00
"""
@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
2013-09-07 04:59:01 +04:00
asyncjob = vmmAsyncJob ( docb , args ,
_simple_async_done_cb ,
2013-10-05 18:03:56 +04:00
( parent , errorintro , errorcb , finish_cb ) ,
2013-09-07 04:59:01 +04:00
title , text , parent . topwin ,
2013-07-06 23:39:00 +04:00
show_progress = show_progress )
2013-09-07 04:59:01 +04:00
asyncjob . run ( )
2010-12-10 17:57:42 +03:00
2013-04-13 22:34:52 +04:00
2012-02-10 22:23:17 +04:00
def idle_wrapper ( fn ) :
def wrapped ( self , * args , * * kwargs ) :
2012-02-10 23:07:51 +04:00
return self . idle_add ( fn , self , * args , * * kwargs )
2012-02-10 22:23:17 +04:00
return wrapped
2013-04-13 22:34:52 +04:00
2010-12-09 01:26:19 +03:00
class vmmAsyncJob ( vmmGObjectUI ) :
2013-09-07 04:16:25 +04:00
"""
Displays a progress bar while executing the " callback " method .
"""
2010-12-10 22:40:08 +03:00
@staticmethod
2013-10-05 18:03:56 +04:00
def simple_async ( callback , args , parent , title , text , errorintro ,
2013-09-07 04:59:01 +04:00
simplecb = True , errorcb = None , finish_cb = None ) :
2013-10-05 18:03:56 +04:00
_simple_async ( callback , args , parent ,
title , text , errorintro , True ,
2013-09-07 04:59:01 +04:00
simplecb , errorcb , finish_cb )
2010-12-10 22:40:08 +03:00
@staticmethod
2012-07-09 00:06:16 +04:00
def simple_async_noshow ( callback , args , parent , errorintro ,
2013-09-07 04:59:01 +04:00
simplecb = True , errorcb = None , finish_cb = None ) :
2013-10-05 18:03:56 +04:00
_simple_async ( callback , args , parent ,
" " , " " , errorintro , False ,
2013-09-07 04:59:01 +04:00
simplecb , errorcb , finish_cb )
2010-12-10 22:40:08 +03:00
2013-09-07 04:59:01 +04:00
def __init__ ( self ,
callback , args , finish_cb , finish_args ,
title , text , parent ,
2013-09-07 04:16:25 +04:00
show_progress = True , cancel_cb = None ) :
2011-04-14 23:43:31 +04:00
"""
@show_progress : If False , don ' t actually show a progress dialog
2013-06-14 01:05:33 +04:00
@cancel_cb : Cancel callback if operation supports it .
( cb , arg1 , arg2 , . . . )
2011-04-14 23:43:31 +04:00
"""
2013-09-23 00:10:16 +04:00
vmmGObjectUI . __init__ ( self , " asyncjob.ui " , " vmm-progress " )
2012-01-28 06:46:12 +04:00
self . topwin . set_transient_for ( parent )
2007-02-01 22:16:44 +03:00
2010-12-10 22:40:08 +03:00
self . show_progress = bool ( show_progress )
2013-06-14 01:05:33 +04:00
cancel_cb = cancel_cb or ( None , [ ] )
self . cancel_cb = cancel_cb [ 0 ]
self . cancel_args = [ self ] + list ( cancel_cb [ 1 : ] )
2010-12-08 20:52:33 +03:00
self . job_canceled = False
2013-09-07 04:59:01 +04:00
self . _finish_cb = finish_cb
self . _finish_args = finish_args or ( )
2007-02-01 22:16:44 +03:00
2013-09-07 04:59:01 +04:00
self . _timer = None
2009-04-03 22:15:15 +04:00
self . _error_info = None
2010-03-03 19:14:59 +03:00
self . _data = None
2017-07-13 10:49:30 +03:00
self . _details_widget = None
self . _details_update_cb = None
2013-06-14 19:04:01 +04:00
self . _is_pulsing = True
2012-02-10 19:24:43 +04:00
self . _meter = None
2007-02-01 22:16:44 +03:00
2013-09-07 04:59:01 +04:00
self . _bg_thread = threading . Thread ( target = cb_wrapper ,
args = [ callback , self ] + args )
self . _bg_thread . daemon = True
2006-07-20 19:16:07 +04:00
2013-02-16 22:31:46 +04:00
self . builder . connect_signals ( {
2017-08-05 09:39:32 +03:00
" on_async_job_cancel_clicked " : self . _on_cancel ,
2010-12-10 17:57:42 +03:00
} )
2012-01-28 06:46:12 +04:00
# UI state
2010-12-10 17:57:42 +03:00
self . topwin . set_title ( title )
2012-01-28 06:46:12 +04:00
self . widget ( " pbar-text " ) . set_text ( text )
2013-06-14 01:05:33 +04:00
self . widget ( " cancel-async-job " ) . set_visible ( bool ( self . cancel_cb ) )
2010-12-10 17:57:42 +03:00
2012-01-28 06:03:42 +04:00
2013-06-14 19:04:01 +04:00
####################
# Internal helpers #
####################
2012-01-28 06:03:42 +04:00
2013-06-14 19:04:01 +04:00
def _cleanup ( self ) :
self . _bg_thread = None
self . cancel_cb = None
self . cancel_args = None
self . _meter = None
2012-01-28 06:03:42 +04:00
2013-06-14 19:04:01 +04:00
def _set_stage_text ( self , text , canceling = False ) :
# This should be thread safe, since it's only ever called from
# pbar idle callbacks and cancel routine which is invoked from the
# main thread
if self . job_canceled and not canceling :
2020-08-26 20:26:08 +03:00
return # pragma: no cover
2013-06-14 19:04:01 +04:00
self . widget ( " pbar-stage " ) . set_text ( text )
################
# UI listeners #
################
def _on_cancel ( self , ignore1 = None , ignore2 = None ) :
2013-09-07 04:16:25 +04:00
if not self . cancel_cb or not self . _bg_thread . is_alive ( ) :
2020-08-26 20:26:08 +03:00
return # pragma: no cover
2013-06-14 19:04:01 +04:00
self . cancel_cb ( * self . cancel_args )
2020-08-26 20:26:08 +03:00
if self . job_canceled : # pragma: no cover
self . widget ( " warning-box " ) . hide ( )
2013-06-14 19:04:01 +04:00
self . _set_stage_text ( _ ( " Cancelling job... " ) , canceling = True )
##############
# Public API #
##############
2012-01-28 06:03:42 +04:00
2012-02-10 19:24:43 +04:00
def get_meter ( self ) :
if not self . _meter :
2021-04-07 19:37:38 +03:00
self . _meter = _vmmMeter ( self . _pbar_pulse ,
self . _pbar_fraction ,
self . _pbar_done )
2012-02-10 19:24:43 +04:00
return self . _meter
2013-06-14 19:04:01 +04:00
def set_error ( self , error , details ) :
self . _error_info = ( error , details )
2012-01-28 06:03:42 +04:00
2017-07-24 11:26:44 +03:00
def has_error ( self ) :
return bool ( self . _error_info )
2013-06-14 19:04:01 +04:00
def can_cancel ( self ) :
return bool ( self . cancel_cb )
2012-01-28 06:03:42 +04:00
def show_warning ( self , summary ) :
2013-06-14 19:04:01 +04:00
# This should only be called from cancel callbacks, not a the thread
2012-01-28 06:03:42 +04:00
markup = " <small> %s </small> " % summary
self . widget ( " warning-box " ) . show ( )
self . widget ( " warning-text " ) . set_markup ( markup )
2013-09-07 04:59:01 +04:00
def _thread_finished ( self ) :
GLib . source_remove ( self . _timer )
self . topwin . destroy ( )
self . cleanup ( )
error = None
details = None
if self . _error_info :
2014-03-22 19:21:19 +04:00
# pylint: disable=unpacking-non-sequence
2013-09-07 04:59:01 +04:00
error , details = self . _error_info
self . _finish_cb ( error , details , * self . _finish_args )
2006-07-20 19:16:07 +04:00
def run ( self ) :
2013-09-07 04:59:01 +04:00
self . _timer = GLib . timeout_add ( 100 , self . _exit_if_necessary )
2010-12-10 22:40:08 +03:00
if self . show_progress :
self . topwin . present ( )
2010-12-08 23:23:40 +03:00
2013-06-14 01:05:33 +04:00
if not self . cancel_cb and self . show_progress :
2023-01-19 19:13:56 +03:00
self . _set_cursor ( " progress " )
2013-09-07 04:16:25 +04:00
self . _bg_thread . start ( )
2010-12-08 20:52:33 +03:00
2012-01-28 06:03:42 +04:00
2013-06-14 19:04:01 +04:00
####################################################################
# All functions after this point are called from the timer loop or #
# the worker thread, so anything that touches Gtk needs to be #
# dispatches with idle_add #
####################################################################
2009-04-03 22:15:14 +04:00
2013-06-14 19:24:32 +04:00
def _exit_if_necessary ( self ) :
2013-09-07 04:16:25 +04:00
if not self . _bg_thread . is_alive ( ) :
2013-09-07 04:59:01 +04:00
self . _thread_finished ( )
2012-02-10 22:23:17 +04:00
return False
2007-01-11 00:11:57 +03:00
2013-06-14 19:04:01 +04:00
if not self . _is_pulsing or not self . show_progress :
2012-02-10 22:23:17 +04:00
return True
2013-06-14 22:11:55 +04:00
self . _pbar_do_pulse ( )
2012-02-10 22:23:17 +04:00
return True
2010-12-08 22:14:38 +03:00
2013-06-14 22:11:55 +04:00
@idle_wrapper
def _pbar_do_pulse ( self ) :
2013-06-18 17:01:45 +04:00
if not self . builder :
2020-08-26 20:26:08 +03:00
return # pragma: no cover
2013-06-14 22:11:55 +04:00
self . widget ( " pbar " ) . pulse ( )
2012-02-10 22:23:17 +04:00
@idle_wrapper
2013-06-14 18:44:24 +04:00
def _pbar_pulse ( self , progress = " " , stage = None ) :
2013-06-14 19:04:01 +04:00
self . _is_pulsing = True
2013-06-18 17:01:45 +04:00
if not self . builder :
2020-08-26 20:26:08 +03:00
return # pragma: no cover
2013-06-14 19:04:01 +04:00
self . widget ( " pbar " ) . set_text ( progress )
self . _set_stage_text ( stage or _ ( " Processing... " ) )
2012-02-10 22:23:17 +04:00
@idle_wrapper
2013-06-14 18:44:24 +04:00
def _pbar_fraction ( self , frac , progress , stage = None ) :
2013-06-14 19:04:01 +04:00
self . _is_pulsing = False
2013-06-18 17:01:45 +04:00
if not self . builder :
2020-08-26 20:26:08 +03:00
return # pragma: no cover
2013-06-14 19:04:01 +04:00
self . _set_stage_text ( stage or _ ( " Processing... " ) )
self . widget ( " pbar " ) . set_text ( progress )
2007-04-14 00:01:03 +04:00
2020-08-26 20:26:08 +03:00
frac = min ( frac , 1 )
frac = max ( frac , 0 )
2013-06-14 19:04:01 +04:00
self . widget ( " pbar " ) . set_fraction ( frac )
2007-01-11 00:11:57 +03:00
2012-02-10 22:23:17 +04:00
@idle_wrapper
2021-04-07 19:37:38 +03:00
def _pbar_done ( self ) :
2013-06-14 19:04:01 +04:00
self . _is_pulsing = False
2017-07-13 10:49:30 +03:00
@idle_wrapper
def details_enable ( self ) :
2020-09-19 21:08:10 +03:00
from gi . repository import Vte
2017-07-13 10:49:30 +03:00
self . _details_widget = Vte . Terminal ( )
self . widget ( " details-box " ) . add ( self . _details_widget )
self . _details_widget . set_visible ( True )
self . widget ( " details " ) . set_visible ( True )
@idle_wrapper
def details_update ( self , data ) :
self . _details_widget . feed ( data . replace ( " \n " , " \r \n " ) . encode ( ) )