2014-09-11 17:13:31 +02:00
# Copyright (C) 2009, 2012-2014 Red Hat, Inc.
2009-03-09 16:17:09 -04:00
# Copyright (C) 2009 Cole Robinson <crobinso@redhat.com>
#
2018-04-04 14:35:41 +01:00
# This work is licensed under the GNU GPLv2 or later.
2018-03-20 15:00:02 -04:00
# See the COPYING file in the top-level directory.
2009-03-09 16:17:09 -04:00
2010-12-10 11:47:07 -05:00
import os
import stat
2009-03-09 16:17:09 -04:00
import traceback
2016-06-07 17:33:21 +02:00
from gi . repository import Gtk
from gi . repository import Pango
2009-03-09 16:17:09 -04:00
import virtinst
2019-06-16 21:12:39 -04:00
from virtinst import log
2019-06-07 18:21:24 -04:00
from virtinst import xmlutil
2009-03-09 16:17:09 -04:00
2014-09-12 16:10:45 -04:00
from . asyncjob import vmmAsyncJob
from . baseclass import vmmGObjectUI
2019-06-16 22:19:17 -04:00
from . lib import uiutil
2009-03-09 16:17:09 -04:00
STORAGE_ROW_CONFIRM = 0
STORAGE_ROW_CANT_DELETE = 1
STORAGE_ROW_PATH = 2
STORAGE_ROW_TARGET = 3
STORAGE_ROW_ICON_SHOW = 4
STORAGE_ROW_ICON = 5
STORAGE_ROW_ICON_SIZE = 6
STORAGE_ROW_TOOLTIP = 7
2013-04-13 14:34:52 -04:00
2020-01-02 16:03:04 +08:00
class _vmmDeleteBase ( vmmGObjectUI ) :
2020-01-28 11:10:51 -05:00
"""
Base class for both types of VM / device storage deleting wizards
"""
2011-04-11 16:14:23 -04:00
def __init__ ( self ) :
2013-09-22 16:10:16 -04:00
vmmGObjectUI . __init__ ( self , " delete.ui " , " vmm-delete " )
2011-04-11 16:14:23 -04:00
self . vm = None
2009-03-09 16:17:09 -04:00
2013-02-16 13:31:46 -05:00
self . builder . connect_signals ( {
2017-08-05 14:39:32 +08:00
" on_vmm_delete_delete_event " : self . close ,
" on_delete_cancel_clicked " : self . close ,
2019-11-20 14:47:07 -05:00
" on_delete_ok_clicked " : self . _finish_clicked_cb ,
" on_delete_remove_storage_toggled " : self . _toggle_remove_storage ,
2009-03-09 16:17:09 -04:00
} )
2011-04-18 11:25:28 -04:00
self . bind_escape_key_close ( )
2018-03-15 07:43:56 -04:00
self . _cleanup_on_app_close ( )
2009-03-09 16:17:09 -04:00
2020-01-28 11:25:37 -05:00
self . topwin . set_title ( self . _get_dialog_title ( ) )
2013-09-27 10:14:53 -04:00
self . _init_state ( )
2009-03-09 16:17:09 -04:00
2020-01-21 14:12:28 +08:00
2013-09-27 10:14:53 -04:00
def _init_state ( self ) :
2019-11-20 14:47:07 -05:00
_prepare_storage_list ( self . widget ( " delete-storage-list " ) )
2009-03-09 16:17:09 -04:00
2018-03-14 17:48:17 -04:00
def show ( self , parent , vm ) :
2019-06-16 21:12:39 -04:00
log . debug ( " Showing delete wizard " )
2018-03-15 07:43:56 -04:00
self . _set_vm ( vm )
2019-11-20 14:47:07 -05:00
self . _reset_state ( )
2011-04-14 08:47:42 -04:00
self . topwin . set_transient_for ( parent )
2009-03-09 16:17:09 -04:00
self . topwin . present ( )
def close ( self , ignore1 = None , ignore2 = None ) :
2019-06-16 21:12:39 -04:00
log . debug ( " Closing delete wizard " )
2009-03-09 16:17:09 -04:00
self . topwin . hide ( )
2018-03-15 07:43:56 -04:00
self . _set_vm ( None )
2009-03-09 16:17:09 -04:00
return 1
2019-12-18 16:39:57 +08:00
2011-07-23 21:16:54 -04:00
def _cleanup ( self ) :
2018-03-15 07:43:56 -04:00
pass
2019-11-20 14:47:07 -05:00
##########################
# Initialization methods #
##########################
2018-03-15 07:43:56 -04:00
def _set_vm ( self , newvm ) :
oldvm = self . vm
if oldvm :
oldvm . conn . disconnect_by_obj ( self )
if newvm :
2020-09-01 11:54:10 -04:00
newvm . conn . connect ( " vm-removed " , self . _vm_removed_cb )
2018-03-15 07:43:56 -04:00
self . vm = newvm
2011-04-13 09:27:02 -04:00
2019-11-20 14:47:07 -05:00
def _reset_state ( self ) :
2019-12-18 16:39:57 +08:00
# Set VM name or disk.target in title'
2020-01-28 11:25:37 -05:00
text = self . _get_dialog_text ( )
2019-12-18 16:39:57 +08:00
2020-02-01 17:16:45 -05:00
title_str = ( " <span size= ' large ' > %s </span> " %
2020-01-28 11:25:37 -05:00
xmlutil . xml_escape ( text ) )
2013-09-27 10:14:53 -04:00
self . widget ( " header-label " ) . set_markup ( title_str )
2009-03-09 16:17:09 -04:00
2015-05-20 17:42:45 -04:00
self . topwin . resize ( 1 , 1 )
2011-07-14 13:13:13 -04:00
self . widget ( " delete-cancel " ) . grab_focus ( )
2009-12-14 16:48:35 -05:00
2013-06-18 00:29:20 -03:00
# Show warning message if VM is running
2020-01-02 17:03:14 +08:00
vm_active = self . _vm_active_status ( )
2014-01-26 18:15:50 -05:00
uiutil . set_grid_row_visible (
2013-09-27 10:14:53 -04:00
self . widget ( " delete-warn-running-vm-box " ) , vm_active )
2013-06-18 00:29:20 -03:00
2014-02-17 21:11:49 +08:00
# Enable storage removal by default
2020-01-28 11:16:19 -05:00
remove_storage_default = self . _get_remove_storage_default ( )
self . widget ( " delete-remove-storage " ) . set_active ( remove_storage_default )
2011-07-14 13:13:13 -04:00
self . widget ( " delete-remove-storage " ) . toggled ( )
2020-01-02 16:13:57 +08:00
diskdatas = self . _get_disk_datas ( )
2019-11-20 14:47:07 -05:00
_populate_storage_list ( self . widget ( " delete-storage-list " ) ,
2019-11-20 15:22:03 -05:00
self . vm , self . vm . conn , diskdatas )
2019-11-20 14:47:07 -05:00
################
# UI listeners #
################
def _finish_clicked_cb ( self , src ) :
self . _finish ( )
2009-03-09 16:17:09 -04:00
2020-09-01 11:54:10 -04:00
def _vm_removed_cb ( self , _conn , vm ) :
if self . vm == vm :
2019-11-20 14:47:07 -05:00
self . close ( )
def _toggle_remove_storage ( self , src ) :
2013-09-27 10:14:53 -04:00
dodel = src . get_active ( )
2014-01-26 18:15:50 -05:00
uiutil . set_grid_row_visible (
2013-09-27 10:14:53 -04:00
self . widget ( " delete-storage-scroll " ) , dodel )
2019-11-20 14:47:07 -05:00
#########################
# finish/delete methods #
#########################
def _get_paths_to_delete ( self ) :
2011-07-14 13:13:13 -04:00
del_list = self . widget ( " delete-storage-list " )
2009-03-09 16:17:09 -04:00
model = del_list . get_model ( )
paths = [ ]
2011-07-14 13:13:13 -04:00
if self . widget ( " delete-remove-storage " ) . get_active ( ) :
2009-03-09 16:17:09 -04:00
for row in model :
if ( not row [ STORAGE_ROW_CANT_DELETE ] and
row [ STORAGE_ROW_CONFIRM ] ) :
paths . append ( row [ STORAGE_ROW_PATH ] )
return paths
2019-11-20 14:47:07 -05:00
def _delete_finished_cb ( self , error , details ) :
2017-04-27 15:00:17 -04:00
self . reset_finish_cursor ( )
2013-09-06 20:59:01 -04:00
if error is not None :
self . err . show_err ( error , details = details )
self . close ( )
2019-11-20 14:47:07 -05:00
def _finish ( self ) :
2020-01-28 11:25:37 -05:00
paths = self . _get_paths_to_delete ( )
2009-03-09 16:17:09 -04:00
2020-01-28 11:25:37 -05:00
if paths :
2013-09-27 10:14:53 -04:00
title = _ ( " Are you sure you want to delete the storage? " )
message = ( _ ( " The following paths will be deleted: \n \n %s " ) %
2020-01-28 11:25:37 -05:00
" \n " . join ( paths ) )
2014-01-26 17:42:24 -05:00
ret = self . err . chkbox_helper (
self . config . get_confirm_delstorage ,
self . config . set_confirm_delstorage ,
text1 = title , text2 = message )
2012-12-03 17:12:59 +01:00
if not ret :
return
2017-04-27 15:00:17 -04:00
self . set_finish_cursor ( )
2020-01-28 11:03:50 -05:00
2020-01-28 11:25:37 -05:00
if not self . _remove_device ( paths ) :
2020-01-28 11:03:50 -05:00
# Don't delete storage if device removal failed
self . _delete_finished_cb ( None , None )
return
2020-01-28 11:25:37 -05:00
title , text = self . _get_progress_text ( paths )
2009-03-09 16:17:09 -04:00
2020-01-28 11:25:37 -05:00
progWin = vmmAsyncJob ( self . _async_delete , [ self . vm , paths ] ,
2019-11-20 14:47:07 -05:00
self . _delete_finished_cb , [ ] ,
2011-04-14 08:47:42 -04:00
title , text , self . topwin )
2013-09-06 20:59:01 -04:00
progWin . run ( )
2018-03-15 07:43:56 -04:00
self . _set_vm ( None )
2009-03-09 16:17:09 -04:00
2018-03-15 07:43:56 -04:00
def _async_delete ( self , asyncjob , vm , paths ) :
2023-09-24 16:13:00 -04:00
errdata = None
2021-05-20 07:06:33 -04:00
storage_errors = [ ]
2023-09-24 16:13:00 -04:00
2009-03-09 16:17:09 -04:00
try :
2020-01-21 14:12:28 +08:00
self . _destroy_vm ( vm )
2013-06-18 00:29:20 -03:00
2018-03-15 07:43:56 -04:00
conn = vm . conn . get_backend ( )
2012-02-10 10:24:43 -05:00
meter = asyncjob . get_meter ( )
2020-01-28 11:10:51 -05:00
storage_errors = self . _async_delete_paths ( paths , conn , meter )
2009-03-09 16:17:09 -04:00
2020-01-02 17:15:43 +08:00
self . _delete_vm ( vm )
2020-08-30 13:37:34 -04:00
vm . conn . schedule_priority_tick ( pollvm = True )
except Exception as e : # pragma: no cover
2023-09-24 16:13:00 -04:00
errdata = (
( _ ( " Error deleting virtual machine ' %(vm)s ' : %(error)s " ) %
{ " vm " : vm . get_name ( ) , " error " : str ( e ) } ) ,
" " . join ( traceback . format_exc ( ) ) )
if not storage_errors and not errdata :
return
2009-04-03 14:15:15 -04:00
2009-03-09 16:17:09 -04:00
storage_errstr = " "
for errinfo in storage_errors :
storage_errstr + = " %s \n %s \n " % ( errinfo [ 0 ] , errinfo [ 1 ] )
# We had extra storage errors. If there was another error message,
# errors to it. Otherwise, build the main error around them.
2023-09-24 16:13:00 -04:00
if errdata : # pragma: no cover
error , details = errdata
2009-04-03 14:15:15 -04:00
details + = " \n \n "
details + = _ ( " Additionally, there were errors removing "
2023-09-24 16:13:00 -04:00
" certain storage devices: \n " )
2009-04-03 14:15:15 -04:00
details + = storage_errstr
2009-03-09 16:17:09 -04:00
else :
2009-04-03 14:15:15 -04:00
error = _ ( " Errors encountered while removing certain "
2023-09-24 16:13:00 -04:00
" storage devices. " )
2009-04-03 14:15:15 -04:00
details = storage_errstr
2023-09-24 16:13:00 -04:00
asyncjob . set_error ( error , details )
2009-03-09 16:17:09 -04:00
2020-01-28 11:10:51 -05:00
def _async_delete_paths ( self , paths , conn , meter ) :
storage_errors = [ ]
for path in paths :
try :
log . debug ( " Deleting path: %s " , path )
2021-04-07 12:37:38 -04:00
meter . start ( _ ( " Deleting path ' %s ' " ) % path , None )
2020-01-28 11:10:51 -05:00
self . _async_delete_path ( conn , path , meter )
except Exception as e :
storage_errors . append ( ( str ( e ) ,
" " . join ( traceback . format_exc ( ) ) ) )
2021-04-07 12:37:38 -04:00
meter . end ( )
2020-01-28 11:10:51 -05:00
return storage_errors
2009-03-09 16:17:09 -04:00
2020-01-28 11:10:51 -05:00
def _async_delete_path ( self , conn , path , ignore ) :
2009-03-09 16:17:09 -04:00
try :
vol = conn . storageVolLookupByPath ( path )
2017-07-24 09:26:48 +01:00
except Exception :
2020-01-28 11:10:51 -05:00
vol = None
2019-06-16 21:12:39 -04:00
log . debug ( " Path ' %s ' is not managed. Deleting locally " , path )
2009-03-09 16:17:09 -04:00
if vol :
vol . delete ( 0 )
else :
os . unlink ( path )
2019-12-18 16:39:57 +08:00
2020-01-28 11:10:51 -05:00
################
# Subclass API #
################
def _get_dialog_title ( self ) :
raise NotImplementedError
2020-01-28 11:25:37 -05:00
def _get_dialog_text ( self ) :
raise NotImplementedError
def _get_progress_text ( self , paths ) :
2020-01-28 11:10:51 -05:00
raise NotImplementedError
2020-01-28 11:25:37 -05:00
def _get_disk_datas ( self ) :
2020-01-28 11:10:51 -05:00
raise NotImplementedError
def _vm_active_status ( self ) :
raise NotImplementedError
def _delete_vm ( self , vm ) :
raise NotImplementedError
def _remove_device ( self , paths ) :
raise NotImplementedError
def _destroy_vm ( self , vm ) :
raise NotImplementedError
2020-01-28 11:16:19 -05:00
def _get_remove_storage_default ( self ) :
raise NotImplementedError
2009-03-09 16:17:09 -04:00
2020-01-02 16:03:04 +08:00
class vmmDeleteDialog ( _vmmDeleteBase ) :
2020-01-28 11:10:51 -05:00
"""
Dialog for deleting a VM and optionally its storage
"""
2020-09-01 13:10:21 -04:00
@classmethod
def show_instance ( cls , parentobj , vm ) :
try :
if not cls . _instance :
cls . _instance = vmmDeleteDialog ( )
cls . _instance . show ( parentobj . topwin , vm )
except Exception as e : # pragma: no cover
parentobj . err . show_err (
_ ( " Error launching delete dialog: %s " ) % str ( e ) )
2020-01-02 16:03:04 +08:00
def _get_dialog_title ( self ) :
2020-01-28 11:25:37 -05:00
return _ ( " Delete Virtual Machine " )
2020-01-02 16:03:04 +08:00
2020-01-28 11:25:37 -05:00
def _get_dialog_text ( self ) :
return _ ( " Delete ' %(vmname)s ' " ) % { " vmname " : self . vm . get_name ( ) }
2020-01-28 11:16:19 -05:00
2020-01-28 11:25:37 -05:00
def _get_progress_text ( self , paths ) :
if paths :
2020-01-02 16:49:49 +08:00
title = _ ( " Deleting virtual machine ' %s ' and selected storage "
" (this may take a while) " ) % self . vm . get_name ( )
text = title
else :
title = _ ( " Deleting virtual machine ' %s ' " ) % self . vm . get_name ( )
text = title
return [ title , text ]
2020-01-28 11:25:37 -05:00
def _get_remove_storage_default ( self ) :
return True
def _get_disk_datas ( self ) :
return _build_diskdata_for_vm ( self . vm )
2020-01-02 17:03:14 +08:00
def _vm_active_status ( self ) :
vm_active = self . vm . is_active ( )
return vm_active
2020-01-28 11:03:50 -05:00
def _remove_device ( self , paths ) :
dummy = paths
return True
2020-01-02 17:15:43 +08:00
def _delete_vm ( self , vm ) :
if vm . is_persistent ( ) :
log . debug ( " Removing VM ' %s ' " , vm . get_name ( ) )
vm . delete ( )
2020-01-21 14:12:28 +08:00
def _destroy_vm ( self , vm ) :
if vm . is_active ( ) :
log . debug ( " Forcing VM ' %s ' power off. " , vm . get_name ( ) )
vm . destroy ( )
2020-01-02 16:03:04 +08:00
class vmmDeleteStorage ( _vmmDeleteBase ) :
2020-01-28 11:10:51 -05:00
"""
Dialog for removing a disk device from a VM and optionally deleting
its storage
"""
2020-01-24 16:33:54 +08:00
@staticmethod
2020-01-28 11:03:50 -05:00
def remove_devobj_internal ( vm , err , devobj , deleting_storage = False ) :
2020-01-24 16:33:54 +08:00
log . debug ( " Removing device: %s " , devobj )
# Define the change
try :
vm . remove_device ( devobj )
2020-08-30 13:37:34 -04:00
except Exception as e : # pragma: no cover
2020-01-24 16:33:54 +08:00
err . show_err ( _ ( " Error Removing Device: %s " ) % str ( e ) )
return
# Try to hot remove
detach_err = ( )
try :
2020-08-27 16:37:15 -04:00
vm . detach_device ( devobj )
2020-01-24 16:33:54 +08:00
except Exception as e :
log . debug ( " Device could not be hotUNplugged: %s " , str ( e ) )
detach_err = ( str ( e ) , " " . join ( traceback . format_exc ( ) ) )
if not detach_err :
return True
2020-01-28 11:03:50 -05:00
msg = _ ( " This change will take effect after the next guest shutdown. " )
if deleting_storage :
msg + = " "
msg + = _ ( " Storage will not be deleted. " )
2020-01-24 16:33:54 +08:00
err . show_err (
_ ( " Device could not be removed from the running machine " ) ,
2020-01-28 11:03:50 -05:00
details = ( detach_err [ 0 ] + " \n \n " + detach_err [ 1 ] ) , text2 = msg ,
2020-01-24 16:33:54 +08:00
buttons = Gtk . ButtonsType . OK ,
dialog_type = Gtk . MessageType . INFO )
2020-01-02 16:03:04 +08:00
def __init__ ( self , disk ) :
_vmmDeleteBase . __init__ ( self )
self . disk = disk
def _get_dialog_title ( self ) :
2020-01-28 11:25:37 -05:00
return _ ( " Remove Disk Device " )
def _get_dialog_text ( self ) :
return _ ( " Remove disk device ' %(target)s ' " ) % {
" target " : self . disk . target }
def _get_progress_text ( self , paths ) :
if paths :
title = _ ( " Removing disk device ' %s ' and selected storage "
" (this may take a while) " ) % self . disk . target
else :
title = _ ( " Removing disk device ' %s ' " ) % self . disk . target
text = title
return [ title , text ]
2020-01-02 16:03:04 +08:00
2020-01-28 11:16:19 -05:00
def _get_remove_storage_default ( self ) :
return False
2020-01-02 16:13:57 +08:00
def _get_disk_datas ( self ) :
return [ _DiskData . from_disk ( self . disk ) ]
2020-01-02 17:03:14 +08:00
def _vm_active_status ( self ) :
return False
2020-01-28 11:03:50 -05:00
def _remove_device ( self , paths ) :
deleting_storage = bool ( paths )
return vmmDeleteStorage . remove_devobj_internal (
self . vm , self . err , self . disk ,
deleting_storage = deleting_storage )
2020-01-02 17:15:43 +08:00
def _delete_vm ( self , vm ) :
pass
2020-01-21 14:12:28 +08:00
def _destroy_vm ( self , vm ) :
pass
2020-01-02 16:03:04 +08:00
2019-11-20 14:47:07 -05:00
###################
# UI init helpers #
###################
2019-11-20 15:22:03 -05:00
class _DiskData :
"""
Helper class to contain all info we need to decide whether we
should default to deleting a path
"""
@staticmethod
def from_disk ( disk ) :
"""
Build _DiskData from a DeviceDisk object
"""
return _DiskData (
disk . target ,
2020-11-11 15:38:34 -05:00
disk . get_source_path ( ) ,
2019-11-20 15:22:03 -05:00
disk . read_only ,
disk . shareable ,
disk . device in [ " cdrom " , " floppy " ] )
def __init__ ( self , label , path , ro , shared , is_media ) :
self . label = label
self . path = path
self . ro = ro
self . shared = shared
self . is_media = is_media
def _build_diskdata_for_vm ( vm ) :
"""
Return a list of _DiskData for all VM resources the app attempts to delete
"""
diskdatas = [ ]
for disk in vm . xmlobj . devices . disk :
diskdatas . append ( _DiskData . from_disk ( disk ) )
diskdatas . append (
_DiskData ( " kernel " , vm . get_xmlobj ( ) . os . kernel , True , False , True ) )
diskdatas . append (
_DiskData ( " initrd " , vm . get_xmlobj ( ) . os . initrd , True , False , True ) )
diskdatas . append (
_DiskData ( " dtb " , vm . get_xmlobj ( ) . os . dtb , True , False , True ) )
return diskdatas
def _populate_storage_list ( storage_list , vm , conn , diskdatas ) :
"""
Fill in the storage list UI from the passed list of _DiskData
"""
2009-03-09 16:17:09 -04:00
model = storage_list . get_model ( )
model . clear ( )
2019-11-20 15:22:03 -05:00
for diskdata in diskdatas :
if not diskdata . path :
2014-01-14 17:26:29 -05:00
continue
2009-03-09 16:17:09 -04:00
# There are a few pieces here
# 1) Can we even delete the storage? If not, make the checkbox
# inconsistent. self.can_delete decides this for us, and if
# we can't delete, gives us a nice message to show the user
# for that row.
#
# 2) If we can delete, do we want to delete this storage by
# default? Reasons not to, are if the storage is marked
2014-02-26 09:36:16 +08:00
# readonly or shareable, or is in use by another VM.
2009-03-09 16:17:09 -04:00
default = False
definfo = None
2019-11-20 15:22:03 -05:00
vol = conn . get_vol_by_path ( diskdata . path )
can_del , delinfo = _can_delete ( conn , vol , diskdata . path )
2009-03-09 16:17:09 -04:00
if can_del :
2019-11-20 14:47:07 -05:00
default , definfo = _do_we_default ( conn , vm . get_name ( ) , vol ,
2019-11-20 15:22:03 -05:00
diskdata )
2009-03-09 16:17:09 -04:00
info = None
if not can_del :
info = delinfo
elif not default :
info = definfo
2022-02-19 12:01:55 -05:00
icon = " dialog-warning "
2012-05-14 14:24:56 +01:00
icon_size = Gtk . IconSize . LARGE_TOOLBAR
2009-03-09 16:17:09 -04:00
2019-11-20 15:22:03 -05:00
row = [ default , not can_del , diskdata . path , diskdata . label ,
2009-03-09 16:17:09 -04:00
bool ( info ) , icon , icon_size , info ]
model . append ( row )
2019-11-20 14:47:07 -05:00
def _prepare_storage_list ( storage_list ) :
2024-10-13 15:26:08 +02:00
# Checkbox, deletable?, storage path, target (hda), icon name,
2009-03-09 16:17:09 -04:00
# icon size, tooltip
2012-05-14 14:24:56 +01:00
model = Gtk . ListStore ( bool , bool , str , str , bool , str , int , str )
2009-03-09 16:17:09 -04:00
storage_list . set_model ( model )
2013-04-17 09:09:53 -04:00
storage_list . set_tooltip_column ( STORAGE_ROW_TOOLTIP )
2009-03-09 16:17:09 -04:00
2012-05-14 14:24:56 +01:00
confirmCol = Gtk . TreeViewColumn ( )
targetCol = Gtk . TreeViewColumn ( _ ( " Target " ) )
infoCol = Gtk . TreeViewColumn ( )
2015-05-20 17:42:45 -04:00
pathCol = Gtk . TreeViewColumn ( _ ( " Storage Path " ) )
pathCol . set_sizing ( Gtk . TreeViewColumnSizing . AUTOSIZE )
pathCol . set_expand ( True )
2009-03-09 16:17:09 -04:00
storage_list . append_column ( confirmCol )
storage_list . append_column ( pathCol )
storage_list . append_column ( targetCol )
storage_list . append_column ( infoCol )
2012-05-14 14:24:56 +01:00
chkbox = Gtk . CellRendererToggle ( )
2019-11-20 14:47:07 -05:00
chkbox . connect ( ' toggled ' , _storage_item_toggled , storage_list )
2009-03-09 16:17:09 -04:00
confirmCol . pack_start ( chkbox , False )
2020-08-30 13:37:34 -04:00
def sensitive_cb ( column , cell , model , _iter , data ) :
row = model [ _iter ]
inconsistent = row [ STORAGE_ROW_CANT_DELETE ]
sensitive = not inconsistent
active = row [ STORAGE_ROW_CONFIRM ]
chk = column . get_cells ( ) [ 0 ]
chk . set_property ( ' inconsistent ' , inconsistent )
chk . set_property ( ' active ' , active )
chk . set_property ( ' sensitive ' , sensitive )
confirmCol . set_cell_data_func ( chkbox , sensitive_cb )
2009-03-09 16:17:09 -04:00
confirmCol . set_sort_column_id ( STORAGE_ROW_CANT_DELETE )
2012-05-14 14:24:56 +01:00
path_txt = Gtk . CellRendererText ( )
2009-03-09 16:17:09 -04:00
pathCol . pack_start ( path_txt , True )
pathCol . add_attribute ( path_txt , ' text ' , STORAGE_ROW_PATH )
pathCol . set_sort_column_id ( STORAGE_ROW_PATH )
2015-05-20 17:42:45 -04:00
path_txt . set_property ( " width-chars " , 50 )
path_txt . set_property ( " ellipsize " , Pango . EllipsizeMode . MIDDLE )
2009-03-09 16:17:09 -04:00
2012-05-14 14:24:56 +01:00
target_txt = Gtk . CellRendererText ( )
2009-03-09 16:17:09 -04:00
targetCol . pack_start ( target_txt , False )
targetCol . add_attribute ( target_txt , ' text ' , STORAGE_ROW_TARGET )
targetCol . set_sort_column_id ( STORAGE_ROW_TARGET )
2012-05-14 14:24:56 +01:00
info_img = Gtk . CellRendererPixbuf ( )
2009-03-09 16:17:09 -04:00
infoCol . pack_start ( info_img , False )
infoCol . add_attribute ( info_img , ' visible ' , STORAGE_ROW_ICON_SHOW )
2022-02-19 12:01:55 -05:00
infoCol . add_attribute ( info_img , ' icon-name ' , STORAGE_ROW_ICON )
2009-03-09 16:17:09 -04:00
infoCol . add_attribute ( info_img , ' stock-size ' , STORAGE_ROW_ICON_SIZE )
infoCol . set_sort_column_id ( STORAGE_ROW_ICON )
2013-04-13 14:34:52 -04:00
2019-11-20 14:47:07 -05:00
def _storage_item_toggled ( src , index , storage_list ) :
2009-03-09 16:17:09 -04:00
active = src . get_active ( )
model = storage_list . get_model ( )
model [ index ] [ STORAGE_ROW_CONFIRM ] = not active
2013-04-13 14:34:52 -04:00
2019-11-20 14:47:07 -05:00
def _can_delete ( conn , vol , path ) :
2024-10-13 15:26:08 +02:00
""" Is the passed path even deletable """
2009-03-09 16:17:09 -04:00
msg = None
if vol :
# Managed storage
2017-03-06 09:28:52 +01:00
pool_type = vol . get_parent_pool ( ) . get_type ( )
if pool_type == virtinst . StoragePool . TYPE_ISCSI :
2020-08-30 13:37:34 -04:00
msg = _ ( " Cannot delete iSCSI share. " ) # pragma: no cover
2017-03-06 09:28:52 +01:00
elif pool_type == virtinst . StoragePool . TYPE_SCSI :
2020-08-30 13:37:34 -04:00
msg = _ ( " Cannot delete SCSI device. " ) # pragma: no cover
2009-03-09 16:17:09 -04:00
else :
if conn . is_remote ( ) :
msg = _ ( " Cannot delete unmanaged remote storage. " )
elif not os . path . exists ( path ) :
msg = _ ( " Path does not exist. " )
elif not os . access ( os . path . dirname ( path ) , os . W_OK ) :
msg = _ ( " No write access to parent directory. " )
2020-08-30 13:37:34 -04:00
elif stat . S_ISBLK ( os . stat ( path ) [ stat . ST_MODE ] ) : # pragma: no cover
2009-03-09 16:17:09 -04:00
msg = _ ( " Cannot delete unmanaged block device. " )
2020-08-30 13:37:34 -04:00
can_delete = bool ( not msg )
return ( can_delete , msg )
2009-03-09 16:17:09 -04:00
2013-04-13 14:34:52 -04:00
2019-11-20 15:22:03 -05:00
def _do_we_default ( conn , vm_name , vol , diskdata ) :
2009-03-09 16:17:09 -04:00
""" Returns (do we delete by default?, info string if not) """
2020-08-30 13:37:34 -04:00
info = [ ]
2009-03-09 16:17:09 -04:00
2019-11-20 15:22:03 -05:00
if diskdata . ro :
2020-08-30 13:37:34 -04:00
info . append ( _ ( " Storage is read-only. " ) )
2019-11-20 15:22:03 -05:00
elif not vol and not os . access ( diskdata . path , os . W_OK ) :
2020-08-30 13:37:34 -04:00
info . append ( _ ( " No write access to path. " ) )
2009-03-09 16:17:09 -04:00
2019-11-20 15:22:03 -05:00
if diskdata . shared :
2020-08-30 13:37:34 -04:00
info . append ( _ ( " Storage is marked as shareable. " ) )
2009-03-09 16:17:09 -04:00
2019-11-20 15:22:03 -05:00
if not info and diskdata . is_media :
2020-08-30 13:37:34 -04:00
info . append ( _ ( " Storage is a media device. " ) )
2016-06-17 13:06:35 -04:00
2009-03-09 16:17:09 -04:00
try :
2019-11-20 15:22:03 -05:00
names = virtinst . DeviceDisk . path_in_use_by ( conn . get_backend ( ) ,
diskdata . path )
2009-03-09 16:17:09 -04:00
if len ( names ) > 1 :
names . remove ( vm_name )
2020-08-30 13:37:34 -04:00
namestr = " \n - " . join ( names )
msg = _ ( " Storage is in use by the following virtual machines " )
msg + = " \n - " + namestr
info . append ( msg )
except Exception as e : # pragma: no cover
2019-06-16 21:12:39 -04:00
log . exception ( " Failed checking disk conflict: %s " , str ( e ) )
2020-08-30 13:37:34 -04:00
info . append ( _ ( " Failed to check disk usage conflict. " ) )
2009-03-09 16:17:09 -04:00
2020-08-30 13:37:34 -04:00
infostr = " \n " . join ( info )
do_default = bool ( not infostr )
return ( do_default , infostr )