2013-10-28 00:59:46 +04:00
# Copyright (C) 2009, 2013 Red Hat, Inc.
2009-07-26 23:54:14 +04:00
# Copyright (C) 2009 Cole Robinson <crobinso@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.
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
import collections
2009-07-26 23:54:14 +04:00
2012-05-14 17:24:56 +04:00
from gi . repository import Gtk
2020-09-04 22:34:13 +03:00
from gi . repository import Pango
2009-07-26 23:54:14 +04:00
2016-12-13 20:36:49 +03:00
import virtinst
from virtinst import Cloner
2019-06-17 04:12:39 +03:00
from virtinst import log
2020-09-04 22:34:13 +03:00
from virtinst import xmlutil
2016-12-13 20:36:49 +03:00
2019-06-17 05:19:17 +03:00
from . lib import uiutil
2014-09-13 00:10:45 +04:00
from . baseclass import vmmGObjectUI
from . asyncjob import vmmAsyncJob
from . storagebrowse import vmmStorageBrowser
2020-09-04 22:34:13 +03:00
def _get_cloneable_msg ( diskinfo ) :
2011-04-18 20:39:53 +04:00
""" Is the passed path even clone-able """
2020-09-04 22:34:13 +03:00
if diskinfo . get_cloneable_msg ( ) :
return diskinfo . get_cloneable_msg ( )
if not diskinfo . disk . path :
return _ ( " No storage to clone. " )
class _StorageInfo :
"""
Our wrapper class that is close to Cloner _DiskInfo content .
We track all user choices explicitly in this class , and then
serialize them into the actual cloner diskinfo once the
user clicks ' Clone ' to complete the process .
"""
def __init__ ( self , vm , cloner , diskinfo ) :
self . _diskinfo = diskinfo
self . _orig_disk = diskinfo . disk
self . _orig_vm_name = vm . get_name ( )
self . _new_vm_name = cloner . new_guest . name
self . share_msg = diskinfo . get_share_msg ( )
self . cloneable_msg = _get_cloneable_msg ( diskinfo )
self . _manual_path = None
self . _generated_path = diskinfo . new_disk and diskinfo . new_disk . path
if not self . _generated_path :
self . _reset_generated_path ( )
self . _is_clone_requested = diskinfo . is_clone_requested ( )
if self . share_msg or not self . is_cloneable ( ) :
self . _is_clone_requested = False
def is_cloneable ( self ) :
return not bool ( self . cloneable_msg )
def is_clone_requested ( self ) :
return self . _is_clone_requested
def is_share_requested ( self ) :
return not self . _is_clone_requested
def warn_about_sharing ( self ) :
return not self . share_msg
def get_target ( self ) :
return self . _orig_disk . target
def get_orig_disk_path ( self ) :
return self . _orig_disk . path
def get_new_disk_path ( self ) :
if self . _manual_path :
return self . _manual_path
return self . _generated_path
def set_clone_requested ( self , val ) :
self . _is_clone_requested = bool ( val )
def set_manual_path ( self , val ) :
self . _manual_path = val
def set_new_vm_name ( self , val ) :
if not val or val == self . _new_vm_name :
return
self . _new_vm_name = val
self . _reset_generated_path ( )
def _reset_generated_path ( self ) :
self . _generated_path = Cloner . generate_clone_disk_path (
self . _orig_disk . conn ,
self . _orig_vm_name ,
self . _new_vm_name ,
self . _orig_disk . path )
def set_values_on_diskinfo ( self , diskinfo ) :
if not self . _is_clone_requested :
diskinfo . set_share_requested ( )
diskinfo . new_disk = None
return
diskinfo . set_clone_requested ( )
sparse = True
newpath = self . get_new_disk_path ( )
diskinfo . set_new_path ( newpath , sparse )
###################
# UI info helpers #
###################
def get_tooltip ( self ) :
lines = [ ]
lines . append ( _ ( " Disk target: %s " ) % self . get_target ( ) )
lines . append ( _ ( " Original path: %s " ) % self . get_orig_disk_path ( ) )
if self . get_new_disk_path ( ) :
lines . append ( _ ( " New path: %s " ) % self . get_new_disk_path ( ) )
lines . append ( " \n " )
if self . share_msg :
lines . append ( _ ( " Storage is safe to share: %(reason)s " ) % {
" reason " : self . share_msg } )
else :
lines . append (
_ ( " Sharing this storage is potentially dangerous. " ) )
if self . cloneable_msg :
lines . append ( _ ( " Storage is not cloneable: %(reason)s " ) % {
" reason " : self . cloneable_msg } )
return " \n " . join ( lines )
def get_markup ( self , vm ) :
lines = [ ]
line = " "
path = self . _orig_disk . path
if path :
line + = path
else :
line + = _ ( " No storage. " )
lines . append ( line )
line = " "
if self . is_share_requested ( ) :
line + = _ ( " Share disk with %s " ) % vm . get_name ( )
if self . is_clone_requested ( ) :
line + = _ ( " Clone this disk " )
sizelabel = self . get_size_label ( )
if sizelabel :
line + = " ( %s ) " % sizelabel
if line :
lines . append ( line )
label = " \n " . join ( lines )
markup = " <small> %s </small> " % xmlutil . xml_escape ( label )
return markup
def get_icon_name ( self ) :
if self . _orig_disk . is_floppy ( ) :
return " media-floppy "
if self . _orig_disk . is_cdrom ( ) :
return " media-optical "
return " drive-harddisk "
def get_size_label ( self ) :
size = self . _orig_disk . get_size ( )
if not size :
return " " # pragma: no cover
return " %.1f GiB " % float ( size )
2009-07-26 23:54:14 +04:00
2013-04-13 22:34:52 +04:00
2011-04-18 20:39:53 +04:00
class vmmCloneVM ( vmmGObjectUI ) :
2018-03-15 01:17:03 +03:00
@classmethod
def show_instance ( cls , parentobj , vm ) :
try :
# Maintain one dialog per connection
uri = vm . conn . get_uri ( )
2018-03-18 02:42:19 +03:00
if cls . _instances is None :
cls . _instances = { }
2018-03-15 01:17:03 +03:00
if uri not in cls . _instances :
2018-03-17 23:08:20 +03:00
cls . _instances [ uri ] = vmmCloneVM ( )
2018-03-15 01:17:03 +03:00
cls . _instances [ uri ] . show ( parentobj . topwin , vm )
2020-08-22 22:42:08 +03:00
except Exception as e : # pragma: no cover
2018-03-15 01:17:03 +03:00
parentobj . err . show_err (
_ ( " Error launching clone dialog: %s " ) % str ( e ) )
2009-07-26 23:54:14 +04:00
2018-03-15 01:17:03 +03:00
def __init__ ( self ) :
vmmGObjectUI . __init__ ( self , " clone.ui " , " vmm-clone " )
self . vm = None
2011-04-13 17:20:59 +04:00
2020-09-04 22:34:13 +03:00
self . _storage_list = None
self . _storage_browser = None
2011-04-13 17:20:59 +04:00
2020-09-04 22:34:13 +03:00
self . _storage_dialog = self . widget ( " vmm-change-storage " )
self . _storage_dialog . set_transient_for ( self . topwin )
2012-02-02 02:26:46 +04:00
2013-02-16 22:31:46 +04:00
self . builder . connect_signals ( {
2020-09-04 22:34:13 +03:00
" on_clone_delete_event " : self . _close_cb ,
" on_clone_cancel_clicked " : self . _close_cb ,
" on_clone_ok_clicked " : self . _finish_clicked_cb ,
" on_storage_selection_changed " : self . _storage_selection_changed_cb ,
" on_storage_details_clicked " : self . _storage_details_clicked_cb ,
# Storage subdialog signals
" on_vmm_change_storage_delete_event " : self . _storage_dialog_close_cb ,
" on_change_storage_cancel_clicked " : self . _storage_dialog_close_cb ,
" on_change_storage_ok_clicked " : self . _storage_dialog_finish_cb ,
" on_change_storage_doclone_toggled " : self . _storage_dialog_doclone_toggled_cb ,
" on_change_storage_browse_clicked " : self . _storage_dialog_browse_cb ,
2009-07-26 23:54:14 +04:00
} )
2011-04-18 19:25:28 +04:00
self . bind_escape_key_close ( )
2018-03-15 14:43:56 +03:00
self . _cleanup_on_app_close ( )
2009-07-26 23:54:14 +04:00
2018-03-15 01:17:03 +03:00
self . _init_ui ( )
2020-09-04 22:34:13 +03:00
#######################
# Standard UI methods #
#######################
2018-03-15 01:17:03 +03:00
@property
def conn ( self ) :
2020-09-04 22:34:13 +03:00
return self . vm and self . vm . conn or None
2009-07-26 23:54:14 +04:00
2018-03-15 01:17:03 +03:00
def show ( self , parent , vm ) :
2019-06-17 04:12:39 +03:00
log . debug ( " Showing clone wizard " )
2018-03-15 14:43:56 +03:00
self . _set_vm ( vm )
2020-09-04 22:34:13 +03:00
self . _reset_state ( )
2011-04-14 16:47:42 +04:00
self . topwin . set_transient_for ( parent )
2011-04-14 21:07:52 +04:00
self . topwin . resize ( 1 , 1 )
2009-07-26 23:54:14 +04:00
self . topwin . present ( )
2020-09-04 22:34:13 +03:00
def _storage_dialog_close ( self ) :
self . _storage_dialog . hide ( )
return 1
2009-07-26 23:54:14 +04:00
def close ( self , ignore1 = None , ignore2 = None ) :
2019-06-17 04:12:39 +03:00
log . debug ( " Closing clone wizard " )
2020-09-04 22:34:13 +03:00
self . _storage_dialog_close ( )
2010-12-09 22:46:15 +03:00
self . topwin . hide ( )
2011-04-13 17:27:02 +04:00
2018-03-15 14:43:56 +03:00
self . _set_vm ( None )
2020-09-04 22:34:13 +03:00
self . _storage_list = None
2009-07-26 23:54:14 +04:00
return 1
2020-09-01 18:54:10 +03:00
def _vm_removed_cb ( self , _conn , vm ) :
if self . vm == vm :
2018-03-15 14:43:56 +03:00
self . close ( )
def _set_vm ( self , newvm ) :
oldvm = self . vm
if oldvm :
oldvm . conn . disconnect_by_obj ( self )
if newvm :
2020-09-01 18:54:10 +03:00
newvm . conn . connect ( " vm-removed " , self . _vm_removed_cb )
2018-03-15 14:43:56 +03:00
self . vm = newvm
2011-07-24 05:16:54 +04:00
def _cleanup ( self ) :
2020-09-04 22:34:13 +03:00
self . _storage_dialog . destroy ( )
self . _storage_dialog = None
2011-04-13 17:27:02 +04:00
2020-09-04 22:34:13 +03:00
if self . _storage_browser :
self . _storage_browser . cleanup ( )
self . _storage_browser = None
2011-04-13 17:27:02 +04:00
2020-09-04 22:34:13 +03:00
###########
# UI init #
###########
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
def _init_ui ( self ) :
storage_list = self . widget ( " storage-list " )
# [disk target, tooltip]
model = Gtk . ListStore ( str , str )
storage_list . set_model ( model )
storage_list . set_tooltip_column ( 1 )
cloneCol = Gtk . TreeViewColumn ( _ ( " Clone " ) )
pathCol = Gtk . TreeViewColumn ( _ ( " Storage " ) )
pathCol . set_sizing ( Gtk . TreeViewColumnSizing . AUTOSIZE )
pathCol . set_expand ( True )
storage_list . append_column ( cloneCol )
storage_list . append_column ( pathCol )
def separator_cb ( _model , _iter ) :
return _model [ _iter ] [ 0 ] == " separator "
storage_list . set_row_separator_func ( separator_cb )
chkbox = Gtk . CellRendererToggle ( )
chkbox . connect ( ' toggled ' , self . _storage_clone_toggled_cb )
chkimg = Gtk . CellRendererPixbuf ( )
chkimg . set_property ( ' stock-size ' , Gtk . IconSize . MENU )
cloneCol . pack_start ( chkimg , False )
cloneCol . pack_start ( chkbox , False )
def chk_cell_data_cb ( column , cell , model , _iter , data ) :
target = model [ _iter ] [ 0 ]
if target == " separator " :
return
sinfo = self . _storage_list [ target ]
visible = sinfo . is_cloneable ( )
active = sinfo . is_clone_requested ( )
_chkimg = column . get_cells ( ) [ 0 ]
_chkbox = column . get_cells ( ) [ 1 ]
_chkbox . set_property ( ' active ' , active )
_chkbox . set_property ( ' visible ' , visible )
_chkimg . set_property ( ' visible ' , not visible )
icon = Gtk . STOCK_INFO
if sinfo . warn_about_sharing ( ) :
icon = Gtk . STOCK_DIALOG_WARNING
_chkimg . set_property ( ' stock-id ' , icon )
tooltip = sinfo . get_tooltip ( )
if tooltip != model [ _iter ] [ 1 ] :
model [ _iter ] [ 1 ] = tooltip
cloneCol . set_cell_data_func ( chkbox , chk_cell_data_cb )
cloneCol . set_cell_data_func ( chkimg , chk_cell_data_cb )
pathtxt = Gtk . CellRendererText ( )
pathCol . set_sort_column_id ( 0 )
pathtxt . set_property ( " width-chars " , 30 )
pathtxt . set_property ( " ellipsize " , Pango . EllipsizeMode . MIDDLE )
pathimg = Gtk . CellRendererPixbuf ( )
pathimg . set_property ( ' stock-size ' , Gtk . IconSize . MENU )
pathimg . set_padding ( 3 , 0 )
pathCol . pack_start ( pathimg , False )
pathCol . pack_start ( pathtxt , True )
def path_cb ( column , cell , model , _iter , data ) :
target = model [ _iter ] [ 0 ]
if target == " separator " :
return
sinfo = self . _storage_list [ target ]
_pathimg = column . get_cells ( ) [ 0 ]
_pathtxt = column . get_cells ( ) [ 1 ]
markup = sinfo . get_markup ( self . vm )
_pathtxt . set_property ( ' markup ' , markup )
_pathimg . set_property ( ' icon-name ' , sinfo . get_icon_name ( ) )
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
pathCol . set_cell_data_func ( pathtxt , path_cb )
pathCol . set_cell_data_func ( pathimg , path_cb )
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
def _reset_state ( self ) :
2011-07-14 21:13:13 +04:00
self . widget ( " clone-cancel " ) . grab_focus ( )
2020-09-04 22:34:13 +03:00
self . widget ( " clone-new-name " ) . set_text ( " " )
2009-12-15 00:48:35 +03:00
2009-07-26 23:54:14 +04:00
# Populate default clone values
2020-09-04 22:34:13 +03:00
cloner = self . _build_cloner ( )
cloner . prepare ( )
self . widget ( " clone-orig-name " ) . set_text ( cloner . src_name )
self . widget ( " clone-new-name " ) . set_text ( cloner . new_guest . name )
2009-07-26 23:54:14 +04:00
2014-02-02 03:59:10 +04:00
uiutil . set_grid_row_visible (
self . widget ( " clone-dest-host " ) , self . conn . is_remote ( ) )
2015-04-19 00:52:25 +03:00
self . widget ( " clone-dest-host " ) . set_text ( self . conn . get_pretty_desc ( ) )
2014-02-02 03:59:10 +04:00
2020-09-04 22:34:13 +03:00
# Collect info about all the VMs disks
self . _storage_list = collections . OrderedDict ( )
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
for diskinfo in cloner . get_diskinfos ( ) :
sinfo = _StorageInfo ( self . vm , cloner , diskinfo )
self . _storage_list [ sinfo . get_target ( ) ] = sinfo
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
self . _populate_storage_ui ( )
2017-03-06 11:28:50 +03:00
2020-09-04 22:34:13 +03:00
#######################
# Functional routines #
#######################
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
def _build_cloner ( self ) :
conn = self . conn . get_backend ( )
2018-03-15 01:17:03 +03:00
orig_name = self . vm . get_name ( )
2020-09-04 22:34:13 +03:00
new_name = self . widget ( " clone-new-name " ) . get_text ( )
cloner = Cloner ( conn , src_name = orig_name )
if new_name :
cloner . set_clone_name ( new_name )
return cloner
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
#######################
# Storage UI building #
#######################
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
def _set_paths_from_clone_name ( self ) :
newname = self . widget ( " clone-new-name " ) . get_text ( )
for sinfo in list ( self . _storage_list . values ( ) ) :
sinfo . set_new_vm_name ( newname )
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
def _populate_storage_ui ( self ) :
"""
Fill in the storage UI from the collected StorageInfo classes
"""
model = self . widget ( " storage-list " ) . get_model ( )
model . clear ( )
for sinfo in self . _storage_list . values ( ) :
model . append ( [ sinfo . get_target ( ) , sinfo . get_tooltip ( ) ] )
model . append ( [ " separator " , None ] )
def _storage_clone_toggled_cb ( self , src , treepath ) :
model = self . widget ( " storage-list " ) . get_model ( )
row = model [ treepath ]
target = row [ 0 ]
sinfo = self . _storage_list [ target ]
active = not src . get_property ( " active " )
sinfo . set_clone_requested ( active )
model . emit ( " row-changed " , row . path , row . iter )
###########################
# Storage window handling #
###########################
def _show_storage_window ( self ) :
tgt = uiutil . get_list_selection ( self . widget ( " storage-list " ) )
sinfo = self . _storage_list [ tgt ]
2009-07-26 23:54:14 +04:00
2010-12-12 21:39:45 +03:00
# If storage paths are dependent on manually entered clone name,
# make sure they are up to date
2020-09-04 22:34:13 +03:00
self . _set_paths_from_clone_name ( )
2010-12-12 21:39:45 +03:00
2020-09-04 22:34:13 +03:00
orig = sinfo . get_orig_disk_path ( )
new = sinfo . get_new_disk_path ( )
size = sinfo . get_size_label ( )
can_clone = sinfo . is_cloneable ( )
do_clone = sinfo . is_clone_requested ( )
2009-07-26 23:54:14 +04:00
2012-02-02 02:26:46 +04:00
self . widget ( " change-storage-doclone " ) . set_active ( True )
self . widget ( " change-storage-doclone " ) . toggled ( )
self . widget ( " change-storage-orig " ) . set_text ( orig )
self . widget ( " change-storage-target " ) . set_text ( tgt )
self . widget ( " change-storage-size " ) . set_text ( size or " - " )
self . widget ( " change-storage-doclone " ) . set_active ( do_clone )
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
self . widget ( " change-storage-new " ) . set_text ( new or " " )
self . widget ( " change-storage-doclone " ) . set_sensitive ( can_clone )
2009-07-26 23:54:14 +04:00
2012-02-02 02:26:46 +04:00
self . widget ( " vmm-change-storage " ) . show_all ( )
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
def _storage_dialog_finish ( self ) :
2012-02-02 02:26:46 +04:00
target = self . widget ( " change-storage-target " ) . get_text ( )
2020-09-04 22:34:13 +03:00
sinfo = self . _storage_list [ target ]
2009-07-26 23:54:14 +04:00
# Sync 'do clone' checkbox, and main dialog combo
2012-02-02 02:26:46 +04:00
do_clone = self . widget ( " change-storage-doclone " ) . get_active ( )
2020-09-04 22:34:13 +03:00
sinfo . set_clone_requested ( do_clone )
2009-07-26 23:54:14 +04:00
if not do_clone :
2020-09-04 22:34:13 +03:00
self . _storage_dialog_close ( )
2009-07-26 23:54:14 +04:00
return
2012-02-02 02:26:46 +04:00
new_path = self . widget ( " change-storage-new " ) . get_text ( )
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
if virtinst . DeviceDisk . path_definitely_exists (
self . vm . conn . get_backend ( ) , new_path ) :
text1 = _ ( " Cloning will overwrite the existing file " )
text2 = _ ( " Using an existing image will overwrite "
" the path during the clone process. Are "
" you sure you want to use this path? " )
res = self . err . yes_no ( text1 , text2 )
2009-07-26 23:54:14 +04:00
if not res :
return
2020-09-04 22:34:13 +03:00
sinfo . set_manual_path ( new_path )
self . _storage_dialog_close ( )
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
###################
# Finish routines #
###################
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
def _validate ( self , cloner ) :
2009-07-26 23:54:14 +04:00
new_paths = [ ]
warn_str = " "
2020-09-04 22:34:13 +03:00
for sinfo in self . _storage_list . values ( ) :
if sinfo . is_clone_requested ( ) :
new_paths . append ( sinfo . get_new_disk_path ( ) )
continue
if not sinfo . warn_about_sharing ( ) :
continue
warn_str + = " %s : %s \n " % (
sinfo . get_target ( ) , sinfo . get_orig_disk_path ( ) )
2009-07-26 23:54:14 +04:00
if warn_str :
res = self . err . ok_cancel (
2020-09-04 22:34:13 +03:00
_ ( " Sharing storage may cause data to be overwritten. " ) ,
_ ( " The following disk devices will be shared with %(vmname)s : "
" \n \n %(pathlist)s \n "
2009-07-26 23:54:14 +04:00
" Running the new guest could overwrite data in these "
2020-09-04 22:34:13 +03:00
" disk images. " ) % {
" vmname " : self . vm . get_name ( ) , " pathlist " : warn_str } )
2009-07-26 23:54:14 +04:00
if not res :
return False
2020-09-04 22:34:13 +03:00
for diskinfo in cloner . get_diskinfos ( ) :
diskinfo . raise_error ( )
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
def _finish_cb ( self , error , details , conn , cloner ) :
2017-04-27 22:00:17 +03:00
self . reset_finish_cursor ( )
2013-09-07 04:59:01 +04:00
if error is not None :
2020-07-14 10:41:49 +03:00
error = ( _ ( " Error creating virtual machine clone ' %(vm)s ' : "
" %(error)s " ) % {
2020-09-04 22:34:13 +03:00
" vm " : cloner . new_guest . name ,
2020-07-14 10:41:49 +03:00
" error " : error ,
} )
self . err . show_err ( error , details = details )
2015-04-15 01:42:56 +03:00
return
2018-03-15 01:17:03 +03:00
conn . schedule_priority_tick ( pollvm = True )
2015-04-15 01:42:56 +03:00
self . close ( )
2013-09-07 04:59:01 +04:00
2020-09-04 22:34:13 +03:00
def _async_clone ( self , asyncjob , cloner ) :
meter = asyncjob . get_meter ( )
refresh_pools = [ ]
for diskinfo in cloner . get_nonshare_diskinfos ( ) :
disk = diskinfo . new_disk
pool = disk . get_parent_pool ( )
if not pool :
continue
poolname = pool . name ( )
if poolname not in refresh_pools :
refresh_pools . append ( poolname )
cloner . start_duplicate ( meter )
for poolname in refresh_pools :
try :
pool = self . conn . get_pool_by_name ( poolname )
self . idle_add ( pool . refresh )
except Exception : # pragma: no cover
log . debug ( " Error looking up pool= %s for refresh after "
" VM clone. " , poolname , exc_info = True )
def _build_final_cloner ( self ) :
self . _set_paths_from_clone_name ( )
cloner = self . _build_cloner ( )
for diskinfo in cloner . get_diskinfos ( ) :
target = diskinfo . disk . target
sinfo = self . _storage_list [ target ]
sinfo . set_values_on_diskinfo ( diskinfo )
cloner . prepare ( )
for diskinfo in cloner . get_diskinfos ( ) :
diskinfo . raise_error ( )
if self . _validate ( cloner ) is False :
return
return cloner
def _finish ( self ) :
2009-07-26 23:54:14 +04:00
try :
2020-09-04 22:34:13 +03:00
cloner = self . _build_final_cloner ( )
if not cloner :
2009-07-26 23:54:14 +04:00
return
2017-05-05 19:47:21 +03:00
except Exception as e :
2020-09-04 22:34:13 +03:00
msg = _ ( " Error with clone settings: %s " ) % str ( e )
return self . err . show_err ( msg )
2009-07-26 23:54:14 +04:00
2017-04-27 22:00:17 +03:00
self . set_finish_cursor ( )
2009-07-26 23:54:14 +04:00
title = ( _ ( " Creating virtual machine clone ' %s ' " ) %
2020-09-04 22:34:13 +03:00
cloner . new_guest . name )
2009-07-26 23:54:14 +04:00
text = title
2020-09-04 22:34:13 +03:00
if cloner . get_nonshare_diskinfos ( ) :
2020-07-14 10:41:48 +03:00
text = ( _ ( " Creating virtual machine clone ' %s ' and selected "
" storage (this may take a while) " ) %
2020-09-04 22:34:13 +03:00
cloner . new_guest . name )
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
progWin = vmmAsyncJob ( self . _async_clone , [ cloner ] ,
self . _finish_cb , [ self . conn , cloner ] ,
2013-09-07 04:59:01 +04:00
title , text , self . topwin )
progWin . run ( )
2009-07-26 23:54:14 +04:00
2015-04-15 01:42:56 +03:00
2020-09-04 22:34:13 +03:00
################
# UI listeners #
################
2015-04-15 01:42:56 +03:00
2020-09-04 22:34:13 +03:00
def _close_cb ( self , src , event = None ) :
return self . close ( )
def _finish_clicked_cb ( self , src ) :
return self . _finish ( )
2015-04-15 01:42:56 +03:00
2020-09-04 22:34:13 +03:00
def _storage_selection_changed_cb ( self , src ) :
row = uiutil . get_list_selected_row ( self . widget ( " storage-list " ) )
self . widget ( " storage-details-button " ) . set_sensitive ( bool ( row ) )
2015-04-15 01:42:56 +03:00
2020-09-04 22:34:13 +03:00
def _storage_details_clicked_cb ( self , src ) :
self . _show_storage_window ( )
2015-04-15 01:42:56 +03:00
2020-09-04 22:34:13 +03:00
def _storage_dialog_doclone_toggled_cb ( self , src ) :
do_clone = src . get_active ( )
self . widget ( " change-storage-new " ) . set_sensitive ( do_clone )
self . widget ( " change-storage-browse " ) . set_sensitive ( do_clone )
def _storage_dialog_close_cb ( self , src , event = None ) :
return self . _storage_dialog_close ( )
def _storage_dialog_finish_cb ( self , src ) :
self . _storage_dialog_finish ( )
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
def _storage_dialog_browse_cb ( self , ignore ) :
2010-12-09 19:22:35 +03:00
def callback ( src_ignore , txt ) :
2012-02-02 02:26:46 +04:00
self . widget ( " change-storage-new " ) . set_text ( txt )
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
if self . _storage_browser is None :
self . _storage_browser = vmmStorageBrowser ( self . conn )
self . _storage_browser . set_finish_cb ( callback )
2009-07-26 23:54:14 +04:00
2020-09-04 22:34:13 +03:00
self . _storage_browser . show ( self . topwin )