2009-03-09 16:17:09 -04:00
#
2012-12-03 17:12:59 +01:00
# Copyright (C) 2009, 2012 Red Hat, Inc.
2009-03-09 16:17:09 -04:00
# Copyright (C) 2009 Cole Robinson <crobinso@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
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA.
#
2013-04-11 17:16:33 -04:00
# pylint: disable=E0611
2012-05-14 14:24:56 +01:00
from gi . repository import Gtk
from gi . repository import Gdk
2013-04-11 17:16:33 -04:00
# pylint: enable=E0611
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
import logging
import virtinst
2009-04-03 14:15:22 -04:00
from virtManager import util
2010-12-08 17:26:19 -05:00
from virtManager . baseclass import vmmGObjectUI
2009-03-09 16:17:09 -04:00
from virtManager . asyncjob import vmmAsyncJob
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
2010-12-08 17:26:19 -05:00
class vmmDeleteDialog ( vmmGObjectUI ) :
2011-04-11 16:14:23 -04:00
def __init__ ( self ) :
2012-02-01 17:26:46 -05:00
vmmGObjectUI . __init__ ( self , " vmm-delete.ui " , " vmm-delete " )
2011-04-11 16:14:23 -04:00
self . vm = None
self . conn = None
2009-03-09 16:17:09 -04:00
2013-02-16 13:31:46 -05:00
self . builder . connect_signals ( {
2009-03-09 16:17:09 -04:00
" on_vmm_delete_delete_event " : self . close ,
" on_delete_cancel_clicked " : self . close ,
" on_delete_ok_clicked " : self . finish ,
" on_delete_remove_storage_toggled " : self . toggle_remove_storage ,
} )
2011-04-18 11:25:28 -04:00
self . bind_escape_key_close ( )
2009-03-09 16:17:09 -04:00
2012-05-14 14:24:56 +01:00
image = Gtk . Image . new_from_icon_name ( " vm_delete_wizard " ,
Gtk . IconSize . DIALOG )
2009-07-26 15:47:46 -04:00
image . show ( )
2013-01-12 16:13:53 -05:00
self . widget ( " icon-box " ) . pack_end ( image , False , False , False )
2009-07-26 15:47:46 -04:00
2011-07-14 13:13:13 -04:00
prepare_storage_list ( self . widget ( " delete-storage-list " ) )
2009-03-09 16:17:09 -04:00
def toggle_remove_storage ( self , src ) :
dodel = src . get_active ( )
2011-07-14 13:13:13 -04:00
self . widget ( " delete-storage-list " ) . set_sensitive ( dodel )
2009-03-09 16:17:09 -04:00
2011-04-14 08:47:42 -04:00
def show ( self , vm , parent ) :
2012-01-31 18:16:54 -05:00
logging . debug ( " Showing delete wizard " )
2011-04-11 16:14:23 -04:00
self . vm = vm
2011-07-22 16:43:26 -04:00
self . conn = vm . conn
2011-04-11 16:14:23 -04:00
2009-03-09 16:17:09 -04: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 ) :
2012-01-31 18:16:54 -05:00
logging . debug ( " Closing delete wizard " )
2009-03-09 16:17:09 -04:00
self . topwin . hide ( )
2011-04-11 16:14:23 -04:00
self . vm = None
self . conn = None
2009-03-09 16:17:09 -04:00
return 1
2011-07-23 21:16:54 -04:00
def _cleanup ( self ) :
2011-04-13 09:27:02 -04:00
self . vm = None
self . conn = None
2009-03-09 16:17:09 -04:00
def reset_state ( self ) :
# Set VM name in title'
title_str = ( " <span size= ' x-large ' > %s ' %s ' </span> " %
2011-08-29 12:11:43 -04:00
( _ ( " Delete " ) , util . xml_escape ( self . vm . get_name ( ) ) ) )
2011-07-14 13:13:13 -04:00
self . widget ( " delete-main-label " ) . set_markup ( title_str )
2009-03-09 16:17:09 -04:00
2011-07-14 13:13:13 -04:00
self . widget ( " delete-cancel " ) . grab_focus ( )
2009-12-14 16:48:35 -05:00
2009-03-09 16:17:09 -04:00
# Disable storage removal by default
2012-12-03 17:12:59 +01:00
self . widget ( " delete-remove-storage " ) . set_active ( True )
2011-07-14 13:13:13 -04:00
self . widget ( " delete-remove-storage " ) . toggled ( )
2009-03-09 16:17:09 -04:00
2011-07-14 13:13:13 -04:00
populate_storage_list ( self . widget ( " delete-storage-list " ) ,
2009-03-09 16:17:09 -04:00
self . vm , self . conn )
def get_config_format ( self ) :
2011-07-14 13:13:13 -04:00
format_combo = self . widget ( " vol-format " )
2009-03-09 16:17:09 -04:00
model = format_combo . get_model ( )
2012-11-08 14:15:02 +01:00
if format_combo . get_active_iter ( ) is not None :
2009-03-09 16:17:09 -04:00
model = format_combo . get_model ( )
return model . get_value ( format_combo . get_active_iter ( ) , 0 )
return None
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
2010-12-09 11:22:35 -05:00
def finish ( self , src_ignore ) :
2009-03-09 16:17:09 -04:00
devs = self . get_paths_to_delete ( )
2012-12-03 17:12:59 +01:00
if devs :
ret = util . chkbox_helper ( self ,
self . config . get_confirm_delstorage ,
self . config . set_confirm_delstorage ,
text1 = _ ( " Are you sure you want to delete "
" all the storage? " ) ,
text2 = _ ( " This will delete all selected "
" storage data. " ) )
if not ret :
return
2009-03-09 16:17:09 -04:00
self . topwin . set_sensitive ( False )
2012-05-14 14:24:56 +01:00
self . topwin . get_window ( ) . set_cursor ( Gdk . Cursor . new ( Gdk . CursorType . WATCH ) )
2009-03-09 16:17:09 -04:00
title = _ ( " Deleting virtual machine ' %s ' " ) % self . vm . get_name ( )
text = title
if devs :
2009-07-14 17:06:50 -04:00
text = title + _ ( " and selected storage (this may take a while) " )
2009-03-09 16:17:09 -04:00
2010-12-09 12:37:48 -05:00
progWin = vmmAsyncJob ( self . _async_delete , [ devs ] ,
2011-04-14 08:47:42 -04:00
title , text , self . topwin )
2010-12-10 09:57:42 -05:00
error , details = progWin . run ( )
2009-03-09 16:17:09 -04:00
self . topwin . set_sensitive ( True )
2012-05-14 14:24:56 +01:00
self . topwin . get_window ( ) . set_cursor ( Gdk . Cursor . new ( Gdk . CursorType . TOP_LEFT_ARROW ) )
2011-04-11 16:14:23 -04:00
conn = self . conn
2009-03-09 16:17:09 -04:00
2009-04-03 14:15:15 -04:00
if error is not None :
2011-04-06 11:22:03 -04:00
self . err . show_err ( error , details = details )
2009-03-09 16:17:09 -04:00
2011-04-11 16:14:23 -04:00
conn . tick ( noStatsUpdate = True )
2009-03-09 16:17:09 -04:00
2011-04-11 16:14:23 -04:00
self . close ( )
2009-03-09 16:17:09 -04:00
2010-12-10 09:57:42 -05:00
def _async_delete ( self , asyncjob , paths ) :
2009-03-09 16:17:09 -04:00
newconn = None
storage_errors = [ ]
2011-03-24 12:59:40 -04:00
details = " "
2009-03-09 16:17:09 -04:00
try :
# Open a seperate connection to install on since this is async
logging . debug ( " Threading off connection to delete vol. " )
2010-12-09 15:45:05 -05:00
newconn = util . dup_conn ( self . conn ) . vmm
2012-02-10 10:24:43 -05:00
meter = asyncjob . get_meter ( )
2009-03-09 16:17:09 -04:00
for path in paths :
try :
2012-01-16 22:04:40 -05:00
logging . debug ( " Deleting path: %s " , path )
2010-12-10 11:47:07 -05:00
meter . start ( text = _ ( " Deleting path ' %s ' " ) % path )
2009-03-09 16:17:09 -04:00
self . _async_delete_path ( newconn , path , meter )
except Exception , e :
storage_errors . append ( ( str ( e ) ,
" " . join ( traceback . format_exc ( ) ) ) )
meter . end ( 0 )
2012-01-16 22:04:40 -05:00
logging . debug ( " Removing VM ' %s ' " , self . vm . get_name ( ) )
2009-03-09 16:17:09 -04:00
self . vm . delete ( )
except Exception , e :
2009-04-03 14:15:15 -04:00
error = ( _ ( " Error deleting virtual machine ' %s ' : %s " ) %
( self . vm . get_name ( ) , str ( e ) ) )
details = " " . join ( traceback . format_exc ( ) )
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 ] )
2011-08-29 14:01:40 -04:00
if not storage_errstr and not details :
2009-03-09 16:17:09 -04:00
return
# We had extra storage errors. If there was another error message,
# errors to it. Otherwise, build the main error around them.
2009-04-03 14:15:15 -04:00
if details :
details + = " \n \n "
details + = _ ( " Additionally, there were errors removing "
2009-03-09 16:17:09 -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 "
2009-03-09 16:17:09 -04:00
" storage devices. " )
2009-04-03 14:15:15 -04:00
details = storage_errstr
if error :
asyncjob . set_error ( error , details )
2009-03-09 16:17:09 -04:00
def _async_delete_path ( self , conn , path , ignore ) :
vol = None
try :
vol = conn . storageVolLookupByPath ( path )
except :
2012-01-16 22:04:40 -05:00
logging . 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 )
def populate_storage_list ( storage_list , vm , conn ) :
model = storage_list . get_model ( )
model . clear ( )
for disk in vm . get_disk_devices ( ) :
vol = None
2010-09-03 14:10:04 -04:00
target = disk . target
path = disk . path
ro = disk . read_only
shared = disk . shareable
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
# readonly or sharable, or is in use by another VM.
2009-11-03 16:09:03 -05:00
if not path :
2009-03-09 16:17:09 -04:00
continue
default = False
definfo = None
vol = conn . get_vol_by_path ( path )
can_del , delinfo = can_delete ( conn , vol , path )
if can_del :
default , definfo = do_we_default ( conn , vm . get_name ( ) , vol ,
path , ro , shared )
info = None
if not can_del :
info = delinfo
elif not default :
info = definfo
2012-05-14 14:24:56 +01:00
icon = Gtk . STOCK_DIALOG_WARNING
icon_size = Gtk . IconSize . LARGE_TOOLBAR
2009-03-09 16:17:09 -04:00
row = [ default , not can_del , path , target ,
bool ( info ) , icon , icon_size , info ]
model . append ( row )
def prepare_storage_list ( storage_list ) :
# Checkbox, deleteable?, storage path, target (hda), icon stock,
# 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 )
try :
storage_list . set_tooltip_column ( STORAGE_ROW_TOOLTIP )
except :
# FIXME: use tooltip wrapper for this
pass
2012-05-14 14:24:56 +01:00
confirmCol = Gtk . TreeViewColumn ( )
pathCol = Gtk . TreeViewColumn ( _ ( " Storage Path " ) )
targetCol = Gtk . TreeViewColumn ( _ ( " Target " ) )
infoCol = Gtk . TreeViewColumn ( )
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 ( )
2009-03-09 16:17:09 -04:00
chkbox . connect ( ' toggled ' , storage_item_toggled , storage_list )
confirmCol . pack_start ( chkbox , False )
confirmCol . add_attribute ( chkbox , ' active ' , STORAGE_ROW_CONFIRM )
confirmCol . add_attribute ( chkbox , ' inconsistent ' ,
STORAGE_ROW_CANT_DELETE )
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 )
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 )
infoCol . add_attribute ( info_img , ' stock-id ' , STORAGE_ROW_ICON )
infoCol . add_attribute ( info_img , ' stock-size ' , STORAGE_ROW_ICON_SIZE )
infoCol . set_sort_column_id ( STORAGE_ROW_ICON )
def storage_item_toggled ( src , index , storage_list ) :
active = src . get_active ( )
model = storage_list . get_model ( )
model [ index ] [ STORAGE_ROW_CONFIRM ] = not active
def can_delete ( conn , vol , path ) :
""" Is the passed path even deleteable """
ret = True
msg = None
if vol :
# Managed storage
if ( vol . get_pool ( ) . get_type ( ) ==
virtinst . Storage . StoragePool . TYPE_ISCSI ) :
msg = _ ( " Cannot delete iscsi share. " )
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. " )
elif stat . S_ISBLK ( os . stat ( path ) [ stat . ST_MODE ] ) :
msg = _ ( " Cannot delete unmanaged block device. " )
if msg :
ret = False
return ( ret , msg )
def do_we_default ( conn , vm_name , vol , path , ro , shared ) :
""" Returns (do we delete by default?, info string if not) """
info = " "
def append_str ( str1 , str2 , delim = " \n " ) :
if not str2 :
return str1
if str1 :
str1 + = delim
str1 + = str2
return str1
if ro :
info = append_str ( info , _ ( " Storage is read-only. " ) )
elif not vol and not os . access ( path , os . W_OK ) :
info = append_str ( info , _ ( " No write access to path. " ) )
if shared :
info = append_str ( info , _ ( " Storage is marked as shareable. " ) )
try :
2010-03-03 17:48:22 -05:00
names = virtinst . VirtualDisk . path_in_use_by ( conn . vmm , path )
2009-03-09 16:17:09 -04:00
if len ( names ) > 1 :
namestr = " "
names . remove ( vm_name )
for name in names :
namestr = append_str ( namestr , name , delim = " \n - " )
info = append_str ( info , _ ( " Storage is in use by the following "
" virtual machines: \n - %s " % namestr ) )
except Exception , e :
2012-01-16 22:04:40 -05:00
logging . exception ( " Failed checking disk conflict: %s " , str ( e ) )
2009-03-09 16:17:09 -04:00
return ( not info , info )