2015-04-09 01:29:48 +03:00
# Copyright (C) 2015 Red Hat, Inc.
#
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.
2015-04-09 01:29:48 +03:00
from gi . repository import Gdk
from gi . repository import Gtk
2019-04-18 22:31:14 +03:00
from gi . repository import Pango
2015-04-09 01:29:48 +03:00
2018-03-20 19:18:35 +03:00
from virtinst import DeviceDisk
2019-06-17 04:12:39 +03:00
from virtinst import log
2015-04-09 01:29:48 +03:00
2019-06-17 05:19:17 +03:00
from . lib import uiutil
2015-04-09 01:29:48 +03:00
from . asyncjob import vmmAsyncJob
from . baseclass import vmmGObjectUI
from . createpool import vmmCreatePool
from . createvol import vmmCreateVolume
2019-07-02 23:29:16 +03:00
from . object . storagepool import vmmStoragePool
2019-04-15 00:54:31 +03:00
from . xmleditor import vmmXMLEditor
2015-04-09 01:29:48 +03:00
EDIT_POOL_IDS = (
EDIT_POOL_NAME ,
EDIT_POOL_AUTOSTART ,
2019-04-15 00:54:31 +03:00
EDIT_POOL_XML ,
) = list ( range ( 3 ) )
2015-04-09 01:29:48 +03:00
2015-04-11 22:08:07 +03:00
VOL_NUM_COLUMNS = 7
2020-09-01 19:35:26 +03:00
( VOL_COLUMN_HANDLE ,
2015-04-09 01:29:48 +03:00
VOL_COLUMN_NAME ,
VOL_COLUMN_CAPACITY ,
2015-04-11 22:08:07 +03:00
VOL_COLUMN_SIZESTR ,
2015-04-09 01:29:48 +03:00
VOL_COLUMN_FORMAT ,
VOL_COLUMN_INUSEBY ,
VOL_COLUMN_SENSITIVE ) = range ( VOL_NUM_COLUMNS )
POOL_NUM_COLUMNS = 4
2020-09-01 19:35:26 +03:00
( POOL_COLUMN_HANDLE ,
2015-04-09 01:29:48 +03:00
POOL_COLUMN_LABEL ,
POOL_COLUMN_ISACTIVE ,
POOL_COLUMN_PERCENT ) = range ( POOL_NUM_COLUMNS )
ICON_RUNNING = " state_running "
ICON_SHUTOFF = " state_shutoff "
def _get_pool_size_percent ( pool ) :
cap = pool . get_capacity ( )
alloc = pool . get_allocation ( )
2020-08-20 20:34:01 +03:00
per = 0
if cap and alloc is not None :
2015-04-09 01:29:48 +03:00
per = int ( ( ( float ( alloc ) / float ( cap ) ) * 100 ) )
2020-02-02 01:16:45 +03:00
return " <span size= ' small ' > %s %% </span> " % int ( per )
2015-04-09 01:29:48 +03:00
2019-06-17 04:59:14 +03:00
class vmmHostStorage ( vmmGObjectUI ) :
2015-04-09 01:29:48 +03:00
__gsignals__ = {
2018-03-15 15:10:09 +03:00
" browse-clicked " : ( vmmGObjectUI . RUN_FIRST , None , [ ] ) ,
" volume-chosen " : ( vmmGObjectUI . RUN_FIRST , None , [ object ] ) ,
" cancel-clicked " : ( vmmGObjectUI . RUN_FIRST , None , [ ] ) ,
2015-04-09 01:29:48 +03:00
}
def __init__ ( self , conn , builder , topwin , vol_sensitive_cb = None ) :
2019-06-17 04:59:14 +03:00
vmmGObjectUI . __init__ ( self , " hoststorage.ui " ,
2015-04-09 01:29:48 +03:00
None , builder = builder , topwin = topwin )
self . conn = conn
# Callback function for setting volume row sensitivity. Used
# by storage browser to disallow selecting certain volumes
self . _vol_sensitive_cb = vol_sensitive_cb
# Name hint passed to addvol. Set by storagebrowser
self . _name_hint = None
2019-04-14 03:30:19 +03:00
self . _active_edits = set ( )
2015-04-09 01:29:48 +03:00
self . _addpool = None
self . _addvol = None
self . _volmenu = None
2019-04-15 00:54:31 +03:00
self . _xmleditor = None
2015-04-09 01:29:48 +03:00
self . top_box = self . widget ( " storage-grid " )
self . builder . connect_signals ( {
2019-04-14 03:30:19 +03:00
" on_pool_add_clicked " : self . _pool_add_cb ,
" on_pool_stop_clicked " : self . _pool_stop_cb ,
" on_pool_start_clicked " : self . _pool_start_cb ,
" on_pool_delete_clicked " : self . _pool_delete_cb ,
" on_pool_refresh_clicked " : self . _pool_refresh_cb ,
2015-04-09 01:29:48 +03:00
" on_pool_apply_clicked " : ( lambda * x : self . _pool_apply ( ) ) ,
2019-04-14 03:30:19 +03:00
" on_vol_delete_clicked " : self . _vol_delete_cb ,
" on_vol_list_button_press_event " : self . _vol_popup_menu_cb ,
" on_vol_list_changed " : self . _vol_selected_cb ,
" on_vol_add_clicked " : self . _vol_add_cb ,
2015-04-09 01:29:48 +03:00
2019-04-14 03:30:19 +03:00
" on_browse_cancel_clicked " : self . _cancel_clicked_cb ,
" on_browse_local_clicked " : self . _browse_local_clicked_cb ,
" on_choose_volume_clicked " : self . _choose_volume_clicked_cb ,
" on_vol_list_row_activated " : self . _vol_list_row_activated_cb ,
2015-04-09 01:29:48 +03:00
" on_pool_name_changed " : ( lambda * x :
2019-04-14 03:30:19 +03:00
self . _enable_pool_apply ( EDIT_POOL_NAME ) ) ,
" on_pool_autostart_toggled " : self . _pool_autostart_changed_cb ,
2015-04-09 01:29:48 +03:00
} )
self . _init_ui ( )
2019-04-14 03:30:19 +03:00
self . _populate_pools ( )
2020-08-21 19:23:29 +03:00
self . _refresh_conn_state ( )
2019-04-14 03:30:19 +03:00
self . conn . connect ( " pool-added " , self . _conn_pools_changed_cb )
self . conn . connect ( " pool-removed " , self . _conn_pools_changed_cb )
self . conn . connect ( " state-changed " , self . _conn_state_changed_cb )
2015-04-09 01:29:48 +03:00
2019-04-14 03:30:19 +03:00
#######################
# Standard UI methods #
#######################
2015-04-09 01:29:48 +03:00
def _cleanup ( self ) :
2015-04-10 23:53:19 +03:00
try :
2018-03-15 14:43:56 +03:00
self . conn . disconnect_by_obj ( self )
2020-08-20 20:34:01 +03:00
except Exception : # pragma: no cover
2015-04-10 23:53:19 +03:00
pass
2015-04-09 01:29:48 +03:00
self . conn = None
if self . _addpool :
self . _addpool . cleanup ( )
self . _addpool = None
if self . _addvol :
self . _addvol . cleanup ( )
self . _addvol = None
self . _volmenu . destroy ( )
self . _volmenu = None
2019-04-15 00:54:31 +03:00
self . _xmleditor . cleanup ( )
self . _xmleditor = None
2015-04-09 01:29:48 +03:00
def close ( self , ignore1 = None , ignore2 = None ) :
if self . _addvol :
self . _addvol . close ( )
if self . _addpool :
self . _addpool . close ( )
if self . _volmenu :
self . _volmenu . hide ( )
2019-04-14 03:30:19 +03:00
###########
# UI init #
###########
2015-04-09 01:29:48 +03:00
2020-08-21 21:39:24 +03:00
def _cap_sort_func_cb ( self , model , iter1 , iter2 , userdata ) :
2018-01-28 00:21:59 +03:00
def _cmp ( a , b ) :
return ( ( a > b ) - ( a < b ) )
return _cmp ( int ( model [ iter1 ] [ VOL_COLUMN_CAPACITY ] ) ,
int ( model [ iter2 ] [ VOL_COLUMN_CAPACITY ] ) )
2015-04-11 22:08:07 +03:00
2015-04-09 01:29:48 +03:00
def _init_ui ( self ) :
self . widget ( " storage-pages " ) . set_show_tabs ( False )
2020-08-21 23:11:40 +03:00
self . _xmleditor = vmmXMLEditor ( self . builder , self . topwin ,
self . widget ( " pool-details-align " ) ,
self . widget ( " pool-details " ) )
self . _xmleditor . connect ( " changed " ,
lambda s : self . _enable_pool_apply ( EDIT_POOL_XML ) )
self . _xmleditor . connect ( " xml-requested " ,
self . _xmleditor_xml_requested_cb )
self . _xmleditor . connect ( " xml-reset " ,
self . _xmleditor_xml_reset_cb )
2015-04-09 01:29:48 +03:00
# These are enabled in storagebrowser.py
self . widget ( " browse-local " ) . set_visible ( False )
self . widget ( " browse-cancel " ) . set_visible ( False )
self . widget ( " choose-volume " ) . set_visible ( False )
# Volume list popup menu
self . _volmenu = Gtk . Menu ( )
2022-02-19 20:01:55 +03:00
volCopyPath = Gtk . MenuItem . new_with_mnemonic ( _ ( " Copy Volume Path " ) )
2015-04-09 01:29:48 +03:00
volCopyPath . show ( )
2019-04-14 03:30:19 +03:00
volCopyPath . connect ( " activate " , self . _vol_copy_path_cb )
2015-04-09 01:29:48 +03:00
self . _volmenu . add ( volCopyPath )
# Volume list
2020-09-01 19:35:26 +03:00
# [obj, name, sizestr, capacity, format, in use by string, sensitive]
volListModel = Gtk . ListStore ( object , str , str , str , str , str , bool )
2015-04-09 01:29:48 +03:00
self . widget ( " vol-list " ) . set_model ( volListModel )
2016-02-05 18:18:16 +03:00
volCol = Gtk . TreeViewColumn ( _ ( " Volumes " ) )
2015-04-09 01:29:48 +03:00
vol_txt1 = Gtk . CellRendererText ( )
volCol . pack_start ( vol_txt1 , True )
volCol . add_attribute ( vol_txt1 , ' text ' , VOL_COLUMN_NAME )
volCol . add_attribute ( vol_txt1 , ' sensitive ' , VOL_COLUMN_SENSITIVE )
volCol . set_sort_column_id ( VOL_COLUMN_NAME )
self . widget ( " vol-list " ) . append_column ( volCol )
2016-02-05 18:18:16 +03:00
volSizeCol = Gtk . TreeViewColumn ( _ ( " Size " ) )
2015-04-09 01:29:48 +03:00
vol_txt2 = Gtk . CellRendererText ( )
volSizeCol . pack_start ( vol_txt2 , False )
2015-04-11 22:08:07 +03:00
volSizeCol . add_attribute ( vol_txt2 , ' text ' , VOL_COLUMN_SIZESTR )
2015-04-09 01:29:48 +03:00
volSizeCol . add_attribute ( vol_txt2 , ' sensitive ' , VOL_COLUMN_SENSITIVE )
volSizeCol . set_sort_column_id ( VOL_COLUMN_CAPACITY )
self . widget ( " vol-list " ) . append_column ( volSizeCol )
2019-04-14 03:30:19 +03:00
volListModel . set_sort_func ( VOL_COLUMN_CAPACITY , self . _cap_sort_func_cb )
2015-04-09 01:29:48 +03:00
2016-02-05 18:18:16 +03:00
volFormatCol = Gtk . TreeViewColumn ( _ ( " Format " ) )
2015-04-09 01:29:48 +03:00
vol_txt3 = Gtk . CellRendererText ( )
volFormatCol . pack_start ( vol_txt3 , False )
volFormatCol . add_attribute ( vol_txt3 , ' text ' , VOL_COLUMN_FORMAT )
volFormatCol . add_attribute ( vol_txt3 , ' sensitive ' , VOL_COLUMN_SENSITIVE )
volFormatCol . set_sort_column_id ( VOL_COLUMN_FORMAT )
self . widget ( " vol-list " ) . append_column ( volFormatCol )
2016-02-05 18:18:16 +03:00
volUseCol = Gtk . TreeViewColumn ( _ ( " Used By " ) )
2015-04-09 01:29:48 +03:00
vol_txt4 = Gtk . CellRendererText ( )
volUseCol . pack_start ( vol_txt4 , False )
volUseCol . add_attribute ( vol_txt4 , ' text ' , VOL_COLUMN_INUSEBY )
volUseCol . add_attribute ( vol_txt4 , ' sensitive ' , VOL_COLUMN_SENSITIVE )
volUseCol . set_sort_column_id ( VOL_COLUMN_INUSEBY )
self . widget ( " vol-list " ) . append_column ( volUseCol )
volListModel . set_sort_column_id ( VOL_COLUMN_NAME ,
Gtk . SortType . ASCENDING )
# Init pool list
2020-09-01 19:35:26 +03:00
# [pool object, label, pool.is_active(), percent string]
2015-04-09 01:29:48 +03:00
pool_list = self . widget ( " pool-list " )
2020-09-01 19:35:26 +03:00
poolListModel = Gtk . ListStore ( object , str , bool , str )
2015-04-09 01:29:48 +03:00
pool_list . set_model ( poolListModel )
2016-02-05 18:18:16 +03:00
poolCol = Gtk . TreeViewColumn ( _ ( " Storage Pools " ) )
2015-04-09 01:29:48 +03:00
pool_txt = Gtk . CellRendererText ( )
2019-04-18 22:31:14 +03:00
pool_txt . set_property ( " ellipsize " , Pango . EllipsizeMode . END )
2015-04-09 01:29:48 +03:00
pool_per = Gtk . CellRendererText ( )
poolCol . pack_start ( pool_per , False )
poolCol . pack_start ( pool_txt , True )
poolCol . add_attribute ( pool_txt , ' markup ' , POOL_COLUMN_LABEL )
poolCol . add_attribute ( pool_txt , ' sensitive ' , POOL_COLUMN_ISACTIVE )
poolCol . add_attribute ( pool_per , ' markup ' , POOL_COLUMN_PERCENT )
pool_list . append_column ( poolCol )
poolListModel . set_sort_column_id ( POOL_COLUMN_LABEL ,
Gtk . SortType . ASCENDING )
2019-04-14 03:30:19 +03:00
pool_list . get_selection ( ) . connect ( " changed " , self . _pool_selected_cb )
2015-04-09 01:29:48 +03:00
pool_list . get_selection ( ) . set_select_function (
( lambda * x : self . _confirm_changes ( ) ) , None )
###############
# Public APIs #
###############
def refresh_page ( self ) :
self . _populate_vols ( )
self . conn . schedule_priority_tick ( pollpool = True )
def set_name_hint ( self , val ) :
self . _name_hint = val
2019-04-14 03:30:19 +03:00
#################
# UI populating #
#################
def _refresh_conn_state ( self ) :
conn_active = self . conn . is_active ( )
self . widget ( " pool-add " ) . set_sensitive ( conn_active and
2020-01-24 20:09:23 +03:00
self . conn . support . conn_storage ( ) )
2019-04-14 03:30:19 +03:00
2020-01-24 20:09:23 +03:00
if conn_active and not self . conn . support . conn_storage ( ) :
2020-08-20 20:34:01 +03:00
self . _set_error_page ( # pragma: no cover
2019-04-14 03:30:19 +03:00
_ ( " Libvirt connection does not support storage management. " ) )
if conn_active :
uiutil . set_list_selection_by_number ( self . widget ( " pool-list " ) , 0 )
return
self . _populate_pools ( )
2020-08-21 19:23:29 +03:00
self . _set_error_page ( _ ( " Connection not active. " ) )
2015-04-09 01:29:48 +03:00
def _current_pool ( self ) :
2020-09-01 19:35:26 +03:00
return uiutil . get_list_selection ( self . widget ( " pool-list " ) )
2015-04-09 01:29:48 +03:00
def _current_vol ( self ) :
pool = self . _current_pool ( )
if not pool :
2020-08-20 20:34:01 +03:00
return None # pragma: no cover
2020-09-01 19:35:26 +03:00
return uiutil . get_list_selection ( self . widget ( " vol-list " ) )
2015-04-09 01:29:48 +03:00
2020-09-01 19:35:26 +03:00
def _update_pool_row ( self , pool ) :
2015-04-09 01:29:48 +03:00
for row in self . widget ( " pool-list " ) . get_model ( ) :
2020-09-01 19:35:26 +03:00
if row [ POOL_COLUMN_HANDLE ] != pool :
2015-04-09 01:29:48 +03:00
continue
# Update active sensitivity and percent available for passed key
row [ POOL_COLUMN_ISACTIVE ] = pool . is_active ( )
row [ POOL_COLUMN_PERCENT ] = _get_pool_size_percent ( pool )
break
curpool = self . _current_pool ( )
2020-09-01 19:35:26 +03:00
if curpool == pool :
self . _refresh_current_pool ( )
2015-04-09 01:29:48 +03:00
2019-04-14 03:30:19 +03:00
def _populate_pool_state ( self , pool ) :
2015-04-09 01:29:48 +03:00
auto = pool . get_autostart ( )
active = pool . is_active ( )
# Set pool details state
self . widget ( " pool-details " ) . set_sensitive ( True )
self . widget ( " pool-name-entry " ) . set_text ( pool . get_name ( ) )
self . widget ( " pool-name-entry " ) . set_editable ( not active )
self . widget ( " pool-sizes " ) . set_markup (
2020-07-12 00:31:40 +03:00
_ ( " %(bytesfree)s Free / <i> %(bytesinuse)s In Use</i> " ) %
{ " bytesfree " : pool . get_pretty_available ( ) ,
" bytesinuse " : pool . get_pretty_allocation ( ) } )
2015-04-09 01:29:48 +03:00
self . widget ( " pool-location " ) . set_text (
pool . get_target_path ( ) )
self . widget ( " pool-state-icon " ) . set_from_icon_name (
( ( active and ICON_RUNNING ) or ICON_SHUTOFF ) ,
Gtk . IconSize . BUTTON )
2021-05-22 23:17:02 +03:00
self . widget ( " pool-state " ) . set_text ( pool . run_status ( ) )
2017-03-21 18:19:05 +03:00
self . widget ( " pool-autostart " ) . set_label ( _ ( " On Boot " ) )
2015-04-09 01:29:48 +03:00
self . widget ( " pool-autostart " ) . set_active ( auto )
self . widget ( " vol-list " ) . set_sensitive ( active )
self . _populate_vols ( )
self . widget ( " pool-delete " ) . set_sensitive ( not active )
self . widget ( " pool-stop " ) . set_sensitive ( active )
self . widget ( " pool-start " ) . set_sensitive ( not active )
2015-04-11 01:20:13 +03:00
self . widget ( " pool-refresh " ) . set_sensitive ( active )
2015-04-09 01:29:48 +03:00
self . widget ( " vol-add " ) . set_sensitive ( active )
self . widget ( " vol-add " ) . set_tooltip_text ( _ ( " Create new volume " ) )
self . widget ( " vol-delete " ) . set_sensitive ( False )
2019-07-02 23:40:11 +03:00
if ( active and
not vmmStoragePool . supports_volume_creation ( pool . get_type ( ) ) ) :
2015-04-09 01:29:48 +03:00
self . widget ( " vol-add " ) . set_sensitive ( False )
self . widget ( " vol-add " ) . set_tooltip_text (
_ ( " Pool does not support volume creation " ) )
2019-04-15 00:54:31 +03:00
self . _xmleditor . set_xml_from_libvirtobject ( pool )
2019-04-14 03:30:19 +03:00
def _set_error_page ( self , msg ) :
2015-04-09 01:29:48 +03:00
self . widget ( " storage-pages " ) . set_current_page ( 1 )
self . widget ( " storage-error-label " ) . set_text ( msg )
2020-08-21 19:23:29 +03:00
self . widget ( " pool-delete " ) . set_sensitive ( False )
self . widget ( " pool-stop " ) . set_sensitive ( False )
self . widget ( " pool-start " ) . set_sensitive ( False )
self . _disable_pool_apply ( )
2015-04-09 01:29:48 +03:00
2019-04-14 03:30:19 +03:00
def _refresh_current_pool ( self ) :
pool = self . _current_pool ( )
if not pool :
self . _set_error_page ( _ ( " No storage pool selected. " ) )
return
self . widget ( " storage-pages " ) . set_current_page ( 0 )
try :
self . _populate_pool_state ( pool )
2020-08-20 20:34:01 +03:00
except Exception as e : # pragma: no cover
2019-06-17 04:12:39 +03:00
log . exception ( e )
2019-04-14 03:30:19 +03:00
self . _set_error_page ( _ ( " Error selecting pool: %s " ) % e )
self . _disable_pool_apply ( )
2015-04-09 01:29:48 +03:00
def _populate_pools ( self ) :
pool_list = self . widget ( " pool-list " )
curpool = self . _current_pool ( )
model = pool_list . get_model ( )
2015-04-11 18:40:47 +03:00
# Prevent events while the model is modified
2015-04-09 01:29:48 +03:00
pool_list . set_model ( None )
2015-04-11 18:40:47 +03:00
try :
pool_list . get_selection ( ) . unselect_all ( )
model . clear ( )
for pool in self . conn . list_pools ( ) :
2018-03-15 14:43:56 +03:00
pool . disconnect_by_obj ( self )
2019-04-14 03:30:19 +03:00
pool . connect ( " state-changed " , self . _pool_changed_cb )
pool . connect ( " refreshed " , self . _pool_changed_cb )
2015-04-11 18:40:47 +03:00
name = pool . get_name ( )
2019-07-02 23:29:16 +03:00
typ = vmmStoragePool . pretty_type ( pool . get_type ( ) )
2015-04-11 18:40:47 +03:00
label = " %s \n <span size= ' small ' > %s </span> " % ( name , typ )
row = [ None ] * POOL_NUM_COLUMNS
2020-09-01 19:35:26 +03:00
row [ POOL_COLUMN_HANDLE ] = pool
2015-04-11 18:40:47 +03:00
row [ POOL_COLUMN_LABEL ] = label
row [ POOL_COLUMN_ISACTIVE ] = pool . is_active ( )
row [ POOL_COLUMN_PERCENT ] = _get_pool_size_percent ( pool )
model . append ( row )
finally :
pool_list . set_model ( model )
2015-04-09 01:29:48 +03:00
2020-09-01 19:35:26 +03:00
uiutil . set_list_selection ( pool_list , curpool )
2015-04-09 01:29:48 +03:00
def _populate_vols ( self ) :
list_widget = self . widget ( " vol-list " )
pool = self . _current_pool ( )
2015-04-10 19:52:42 +03:00
vols = pool and pool . get_volumes ( ) or [ ]
2015-04-09 01:29:48 +03:00
model = list_widget . get_model ( )
list_widget . get_selection ( ) . unselect_all ( )
model . clear ( )
2015-11-11 01:27:49 +03:00
vadj = self . widget ( " vol-scroll " ) . get_vadjustment ( )
2017-10-11 14:35:55 +03:00
vscroll_percent = vadj . get_value ( ) / / max ( vadj . get_upper ( ) , 1 )
2015-11-11 01:27:49 +03:00
2024-07-01 08:31:27 +03:00
paths = [ ]
2015-04-10 19:52:42 +03:00
for vol in vols :
2015-04-09 01:29:48 +03:00
try :
2024-07-01 08:31:27 +03:00
paths . append ( vol . get_target_path ( ) )
except Exception :
log . debug ( " Error getting target path for ' %s ' " , vol , exc_info = True )
paths . append ( None )
names_list = DeviceDisk . paths_in_use_by ( pool . conn . get_backend ( ) , paths )
for vol , path , names in zip ( vols , paths , names_list ) :
try :
2015-04-09 01:29:48 +03:00
name = vol . get_pretty_name ( pool . get_type ( ) )
2015-04-11 22:08:07 +03:00
cap = str ( vol . get_capacity ( ) )
sizestr = vol . get_pretty_capacity ( )
2015-04-09 01:29:48 +03:00
fmt = vol . get_format ( ) or " "
2020-08-20 20:34:01 +03:00
except Exception : # pragma: no cover
2019-06-17 04:12:39 +03:00
log . debug ( " Error getting volume info for ' %s ' , "
2020-09-14 21:40:47 +03:00
" hiding it " , vol , exc_info = True )
2015-04-09 01:29:48 +03:00
continue
namestr = None
try :
if path :
namestr = " , " . join ( names )
if not namestr :
namestr = None
2020-08-20 20:34:01 +03:00
except Exception : # pragma: no cover
2023-08-30 11:03:15 +03:00
log . exception ( " Failed to determine if storage volume in use. " )
2015-04-09 01:29:48 +03:00
sensitive = True
if self . _vol_sensitive_cb :
sensitive = self . _vol_sensitive_cb ( fmt )
row = [ None ] * VOL_NUM_COLUMNS
2020-09-01 19:35:26 +03:00
row [ VOL_COLUMN_HANDLE ] = vol
2015-04-09 01:29:48 +03:00
row [ VOL_COLUMN_NAME ] = name
2015-04-11 22:08:07 +03:00
row [ VOL_COLUMN_SIZESTR ] = sizestr
2015-04-09 01:29:48 +03:00
row [ VOL_COLUMN_CAPACITY ] = cap
row [ VOL_COLUMN_FORMAT ] = fmt
row [ VOL_COLUMN_INUSEBY ] = namestr
row [ VOL_COLUMN_SENSITIVE ] = sensitive
model . append ( row )
2015-11-11 01:27:49 +03:00
def _reset_vscroll_position ( ) :
vadj . set_value ( vadj . get_upper ( ) * vscroll_percent )
self . idle_add ( _reset_vscroll_position )
2015-04-09 21:42:25 +03:00
2019-04-14 03:30:19 +03:00
##########################
# Pool lifecycle actions #
##########################
2015-04-09 01:29:48 +03:00
2019-04-14 03:30:19 +03:00
def _pool_stop_cb ( self , src ) :
2015-04-09 01:29:48 +03:00
pool = self . _current_pool ( )
if pool is None :
2020-08-20 20:34:01 +03:00
return # pragma: no cover
2015-04-09 01:29:48 +03:00
2019-06-17 04:12:39 +03:00
log . debug ( " Stopping pool ' %s ' " , pool . get_name ( ) )
2015-04-09 01:29:48 +03:00
vmmAsyncJob . simple_async_noshow ( pool . stop , [ ] , self ,
_ ( " Error stopping pool ' %s ' " ) % pool . get_name ( ) )
2019-04-14 03:30:19 +03:00
def _pool_start_cb ( self , src ) :
2015-04-09 01:29:48 +03:00
pool = self . _current_pool ( )
if pool is None :
2020-08-20 20:34:01 +03:00
return # pragma: no cover
2015-04-09 01:29:48 +03:00
2019-06-17 04:12:39 +03:00
log . debug ( " Starting pool ' %s ' " , pool . get_name ( ) )
2015-04-09 01:29:48 +03:00
vmmAsyncJob . simple_async_noshow ( pool . start , [ ] , self ,
_ ( " Error starting pool ' %s ' " ) % pool . get_name ( ) )
2019-04-14 03:30:19 +03:00
def _pool_add_cb ( self , src ) :
2019-06-17 04:12:39 +03:00
log . debug ( " Launching ' Add Pool ' wizard " )
2015-04-09 01:29:48 +03:00
try :
if self . _addpool is None :
self . _addpool = vmmCreatePool ( self . conn )
self . _addpool . show ( self . topwin )
2020-08-20 20:34:01 +03:00
except Exception as e : # pragma: no cover
2015-04-09 01:29:48 +03:00
self . err . show_err ( _ ( " Error launching pool wizard: %s " ) % str ( e ) )
2019-04-14 03:30:19 +03:00
def _pool_delete_cb ( self , src ) :
2015-04-09 01:29:48 +03:00
pool = self . _current_pool ( )
if pool is None :
2020-08-20 20:34:01 +03:00
return # pragma: no cover
2015-04-09 01:29:48 +03:00
result = self . err . yes_no ( _ ( " Are you sure you want to permanently "
" delete the pool %s ? " ) % pool . get_name ( ) )
if not result :
return
2019-06-17 04:12:39 +03:00
log . debug ( " Deleting pool ' %s ' " , pool . get_name ( ) )
2015-04-09 01:29:48 +03:00
vmmAsyncJob . simple_async_noshow ( pool . delete , [ ] , self ,
_ ( " Error deleting pool ' %s ' " ) % pool . get_name ( ) )
2019-04-14 03:30:19 +03:00
def _pool_refresh_cb ( self , src ) :
2015-04-09 01:29:48 +03:00
pool = self . _current_pool ( )
if pool is None :
2020-08-21 21:39:24 +03:00
return # pragma: no cover
2015-04-09 01:29:48 +03:00
2019-04-14 23:28:58 +03:00
self . _confirm_changes ( )
2019-06-17 04:12:39 +03:00
log . debug ( " Refresh pool ' %s ' " , pool . get_name ( ) )
2016-06-15 00:56:06 +03:00
vmmAsyncJob . simple_async_noshow ( pool . refresh , [ ] , self ,
2015-04-09 01:29:48 +03:00
_ ( " Error refreshing pool ' %s ' " ) % pool . get_name ( ) )
###########################
# Volume action listeners #
###########################
2019-04-14 03:30:19 +03:00
def _vol_copy_path_cb ( self , src ) :
2015-04-09 01:29:48 +03:00
vol = self . _current_vol ( )
if not vol :
2020-08-21 21:39:24 +03:00
return # pragma: no cover
2015-04-09 01:29:48 +03:00
clipboard = Gtk . Clipboard . get ( Gdk . SELECTION_CLIPBOARD )
target_path = vol . get_target_path ( )
if target_path :
clipboard . set_text ( target_path , - 1 )
2019-04-14 03:30:19 +03:00
def _vol_add_cb ( self , src ) :
2015-04-09 01:29:48 +03:00
pool = self . _current_pool ( )
if pool is None :
2020-08-21 21:39:24 +03:00
return # pragma: no cover
2015-04-09 01:29:48 +03:00
2019-06-17 04:12:39 +03:00
log . debug ( " Launching ' Add Volume ' wizard for pool ' %s ' " ,
2015-04-09 01:29:48 +03:00
pool . get_name ( ) )
try :
if self . _addvol is None :
self . _addvol = vmmCreateVolume ( self . conn , pool )
2019-04-14 03:30:19 +03:00
self . _addvol . connect ( " vol-created " , self . _vol_created_cb )
2015-04-09 01:29:48 +03:00
else :
2020-08-22 19:07:57 +03:00
self . _addvol . set_parent_pool ( pool )
2015-04-09 01:29:48 +03:00
self . _addvol . set_modal ( self . topwin . get_modal ( ) )
self . _addvol . set_name_hint ( self . _name_hint )
self . _addvol . show ( self . topwin )
2020-08-21 21:39:24 +03:00
except Exception as e : # pragma: no cover
2015-04-09 01:29:48 +03:00
self . err . show_err ( _ ( " Error launching volume wizard: %s " ) % str ( e ) )
2019-04-14 03:30:19 +03:00
def _vol_delete_cb ( self , src ) :
2015-04-09 01:29:48 +03:00
vol = self . _current_vol ( )
if vol is None :
2020-08-21 21:39:24 +03:00
return # pragma: no cover
2015-04-09 01:29:48 +03:00
2015-04-09 21:42:25 +03:00
pool = self . _current_pool ( )
2015-04-09 01:29:48 +03:00
result = self . err . yes_no ( _ ( " Are you sure you want to permanently "
" delete the volume %s ? " ) % vol . get_name ( ) )
if not result :
return
def cb ( ) :
vol . delete ( )
def idlecb ( ) :
2015-04-09 21:42:25 +03:00
pool . refresh ( )
2015-04-09 01:29:48 +03:00
self . idle_add ( idlecb )
2019-06-17 04:12:39 +03:00
log . debug ( " Deleting volume ' %s ' " , vol . get_name ( ) )
2015-04-09 01:29:48 +03:00
vmmAsyncJob . simple_async_noshow ( cb , [ ] , self ,
2015-12-08 20:44:59 +03:00
_ ( " Error deleting volume ' %s ' " ) % vol . get_name ( ) )
2019-04-14 03:30:19 +03:00
#############################
# pool apply/config actions #
#############################
def _pool_apply ( self ) :
pool = self . _current_pool ( )
if pool is None :
2020-08-21 21:39:24 +03:00
return # pragma: no cover
2019-04-14 03:30:19 +03:00
2019-06-17 04:12:39 +03:00
log . debug ( " Applying changes for pool ' %s ' " , pool . get_name ( ) )
2019-04-14 03:30:19 +03:00
try :
if EDIT_POOL_AUTOSTART in self . _active_edits :
auto = self . widget ( " pool-autostart " ) . get_active ( )
pool . set_autostart ( auto )
2019-04-15 00:54:31 +03:00
2019-04-14 03:30:19 +03:00
if EDIT_POOL_NAME in self . _active_edits :
pool . define_name ( self . widget ( " pool-name-entry " ) . get_text ( ) )
self . idle_add ( self . _populate_pools )
2019-04-15 00:54:31 +03:00
if EDIT_POOL_XML in self . _active_edits :
pool . define_xml ( self . _xmleditor . get_xml ( ) )
2019-04-14 03:30:19 +03:00
except Exception as e :
self . err . show_err ( _ ( " Error changing pool settings: %s " ) % str ( e ) )
return
self . _disable_pool_apply ( )
def _enable_pool_apply ( self , edittype ) :
self . _active_edits . add ( edittype )
self . widget ( " pool-apply " ) . set_sensitive ( True )
2019-06-15 12:58:43 +03:00
self . _xmleditor . details_changed = True
2019-04-14 03:30:19 +03:00
def _disable_pool_apply ( self ) :
self . _active_edits = set ( )
self . widget ( " pool-apply " ) . set_sensitive ( False )
2019-06-15 12:58:43 +03:00
self . _xmleditor . details_changed = False
2019-04-14 03:30:19 +03:00
def _confirm_changes ( self ) :
2019-04-14 23:28:58 +03:00
if ( self . is_visible ( ) and
self . _active_edits and
self . err . confirm_unapplied_changes ( ) ) :
2019-04-14 03:30:19 +03:00
self . _pool_apply ( )
2019-04-14 23:28:58 +03:00
self . _disable_pool_apply ( )
2019-04-14 03:30:19 +03:00
return True
#############
# Listeners #
#############
def _browse_local_clicked_cb ( self , src ) :
self . emit ( " browse-clicked " )
def _choose_volume_clicked_cb ( self , src ) :
self . emit ( " volume-chosen " , self . _current_vol ( ) )
def _vol_list_row_activated_cb ( self , src , treeiter , viewcol ) :
self . emit ( " volume-chosen " , self . _current_vol ( ) )
2020-09-01 19:35:26 +03:00
def _vol_created_cb ( self , src , pool , vol ) :
2019-04-14 03:30:19 +03:00
# The vol list will have already been updated, since this
# signal arrives only after pool-refreshed. So all we do here is
# select the vol we just created.
2020-09-01 19:35:26 +03:00
curpool = self . _current_pool ( )
if curpool != pool :
2020-08-21 21:39:24 +03:00
return # pragma: no cover
2020-09-01 19:35:26 +03:00
uiutil . set_list_selection ( self . widget ( " vol-list " ) , vol )
2019-04-14 03:30:19 +03:00
def _pool_autostart_changed_cb ( self , src ) :
self . _enable_pool_apply ( EDIT_POOL_AUTOSTART )
def _vol_selected_cb ( self , src ) :
model , treeiter = src . get_selected ( )
self . widget ( " vol-delete " ) . set_sensitive ( bool ( treeiter ) )
can_choose = bool ( treeiter and model [ treeiter ] [ VOL_COLUMN_SENSITIVE ] )
self . widget ( " choose-volume " ) . set_sensitive ( can_choose )
def _vol_popup_menu_cb ( self , src , event ) :
if event . button != 3 :
return
self . _volmenu . popup_at_pointer ( event )
def _cancel_clicked_cb ( self , src ) :
self . emit ( " cancel-clicked " )
def _pool_changed_cb ( self , pool ) :
2020-09-01 19:35:26 +03:00
self . _update_pool_row ( pool )
2019-04-14 03:30:19 +03:00
def _conn_state_changed_cb ( self , conn ) :
self . _refresh_conn_state ( )
2020-09-01 18:54:10 +03:00
def _conn_pools_changed_cb ( self , src , pool ) :
2019-04-14 03:30:19 +03:00
self . _populate_pools ( )
def _pool_selected_cb ( self , selection ) :
self . _refresh_current_pool ( )
2019-04-15 00:54:31 +03:00
def _xmleditor_xml_requested_cb ( self , src ) :
self . _refresh_current_pool ( )
def _xmleditor_xml_reset_cb ( self , src ) :
self . _refresh_current_pool ( )