Improve save/restore UI

Make the save command a member of the 'Shutdown' menu, and place 'restore'
out of the way in Host details -> File. Emphasis on the old 'restore' will
be diminished once we support managed save.
This commit is contained in:
Cole Robinson 2010-05-12 11:42:59 -04:00
parent 6b41d53c12
commit 225f4d344a
8 changed files with 128 additions and 77 deletions

View File

@ -226,7 +226,7 @@ class vmmDetails(gobject.GObject):
"on_details_menu_run_activate": self.control_vm_run,
"on_details_menu_poweroff_activate": self.control_vm_shutdown,
"on_details_menu_reboot_activate": self.control_vm_reboot,
"on_details_menu_save_activate": self.control_vm_save_domain,
"on_details_menu_save_activate": self.control_vm_save,
"on_details_menu_destroy_activate": self.control_vm_destroy,
"on_details_menu_pause_activate": self.control_vm_pause,
"on_details_menu_clone_activate": self.control_vm_clone,
@ -374,7 +374,8 @@ class vmmDetails(gobject.GObject):
self.window.get_widget("control-shutdown"),
self.control_vm_shutdown,
self.control_vm_reboot,
self.control_vm_destroy)
self.control_vm_destroy,
self.control_vm_save)
icon_name = self.config.get_shutdown_icon_name()
for name in ["details-menu-shutdown",
@ -917,7 +918,7 @@ class vmmDetails(gobject.GObject):
def control_vm_console(self, src):
self.emit("action-show-console", self.vm.get_connection().get_uri(), self.vm.get_uuid())
def control_vm_save_domain(self, src):
def control_vm_save(self, src):
self.emit("action-save-domain", self.vm.get_connection().get_uri(), self.vm.get_uuid())
def control_vm_destroy(self, src):

View File

@ -471,6 +471,8 @@ class vmmEngine(gobject.GObject):
self.refresh_console(uri, uuid)
def _do_save_domain(self, src, uri, uuid):
self.save_domain(src, uri, uuid)
def _do_restore_domain(self, src, uri):
self.restore_domain(src, uri)
def _do_destroy_domain(self, src, uri, uuid):
self.destroy_domain(src, uri, uuid)
def _do_suspend_domain(self, src, uri, uuid):
@ -520,6 +522,7 @@ class vmmEngine(gobject.GObject):
manager.connect("action-show-help", self._do_show_help)
manager.connect("action-exit-app", self._do_exit_app)
manager.connect("action-view-manager", self._do_show_manager)
manager.connect("action-restore-domain", self._do_restore_domain)
self.connections[uri]["windowHost"] = manager
self.connections[uri]["windowHost"].show()
@ -601,6 +604,7 @@ class vmmEngine(gobject.GObject):
self.windowManager.connect("action-shutdown-domain", self._do_shutdown_domain)
self.windowManager.connect("action-reboot-domain", self._do_reboot_domain)
self.windowManager.connect("action-destroy-domain", self._do_destroy_domain)
self.windowManager.connect("action-save-domain", self._do_save_domain)
self.windowManager.connect("action-migrate-domain", self._do_migrate_domain)
self.windowManager.connect("action-clone-domain", self._do_clone_domain)
self.windowManager.connect("action-show-console", self._do_show_console)
@ -752,6 +756,41 @@ class vmmEngine(gobject.GObject):
except Exception, e:
asyncjob.set_error(str(e), "".join(traceback.format_exc()))
def restore_domain(self, src, uri):
conn = self._lookup_connection(uri)
if conn.is_remote():
self.err.val_err(_("Restoring virtual machines over remote "
"connections is not yet supported"))
return
path = util.browse_local(src.window.get_widget("vmm-manager"),
_("Restore Virtual Machine"),
self.config, conn,
browse_reason=self.config.CONFIG_DIR_RESTORE)
if not path:
return
progWin = vmmAsyncJob(self.config, self.restore_saved_callback,
[path, conn], _("Restoring Virtual Machine"))
progWin.run()
error, details = progWin.get_error()
if error is not None:
self.err.show_err(error, details,
title=_("Error restoring domain"))
def restore_saved_callback(self, file_to_load, conn, asyncjob):
try:
newconn = util.dup_conn(self.config, conn,
return_conn_class=True)
newconn.restore(file_to_load)
except Exception, e:
err = (_("Error restoring domain '%s': %s") %
(file_to_load, str(e)))
details = "".join(traceback.format_exc())
asyncjob.set_error(err, details)
def destroy_domain(self, src, uri, uuid):
conn = self._lookup_connection(uri)
vm = conn.get_vm(uuid)

View File

@ -48,6 +48,8 @@ class vmmHost(gobject.GObject):
gobject.TYPE_NONE, []),
"action-view-manager": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, []),
"action-restore-domain": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, (str,)),
}
def __init__(self, config, conn, engine):
self.__gobject_init__()
@ -114,6 +116,7 @@ class vmmHost(gobject.GObject):
"on_menu_file_close_activate": self.close,
"on_vmm_host_delete_event": self.close,
"on_menu_restore_saved_activate": self.restore_domain,
"on_menu_help_contents_activate": self.show_help,
"on_net_add_clicked": self.add_network,
@ -320,6 +323,9 @@ class vmmHost(gobject.GObject):
def view_manager(self, src):
self.emit("action-view-manager")
def restore_domain(self, src):
self.emit("action-restore-domain", self.conn.get_uri())
def exit_app(self, src):
self.emit("action-exit-app")
@ -347,6 +353,7 @@ class vmmHost(gobject.GObject):
def conn_state_changed(self, ignore1=None):
state = (self.conn.get_state() == vmmConnection.STATE_ACTIVE)
self.window.get_widget("menu_file_restore_saved").set_sensitive(state)
self.window.get_widget("net-add").set_sensitive(state)
self.window.get_widget("pool-add").set_sensitive(state)

View File

@ -23,12 +23,10 @@ import gtk
import gtk.glade
import logging
import traceback
import virtManager.config as cfg
import virtManager.uihelpers as uihelpers
from virtManager.connection import vmmConnection
from virtManager.asyncjob import vmmAsyncJob
from virtManager.error import vmmErrorDialog
from virtManager.delete import vmmDeleteDialog
from virtManager.graphwidgets import CellRendererSparkline
@ -105,6 +103,8 @@ class vmmManager(gobject.GObject):
gobject.TYPE_NONE, (str, str)),
"action-destroy-domain": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, (str, str)),
"action-save-domain": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, (str, str)),
"action-connect": (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, [str]),
"action-show-help": (gobject.SIGNAL_RUN_FIRST,
@ -165,7 +165,6 @@ class vmmManager(gobject.GObject):
"on_menu_file_add_connection_activate": self.new_connection,
"on_menu_file_quit_activate": self.exit_app,
"on_menu_file_close_activate": self.close,
"on_menu_restore_saved_activate": self.restore_saved,
"on_vmm_close_clicked": self.close,
"on_vm_open_clicked": self.open_vm_console,
"on_vm_run_clicked": self.start_vm,
@ -275,7 +274,8 @@ class vmmManager(gobject.GObject):
self.window.get_widget("vm-shutdown"),
self.poweroff_vm,
self.reboot_vm,
self.destroy_vm)
self.destroy_vm,
self.save_vm)
tool = self.window.get_widget("vm-toolbar")
util.safe_set_prop(tool, "icon-size", gtk.ICON_SIZE_LARGE_TOOLBAR)
@ -296,6 +296,7 @@ class vmmManager(gobject.GObject):
destroy_icon = build_icon(icon_name)
run_icon = build_stock(gtk.STOCK_MEDIA_PLAY)
pause_icon = build_stock(gtk.STOCK_MEDIA_PAUSE)
save_icon = build_stock(gtk.STOCK_SAVE)
resume_icon = build_stock(gtk.STOCK_MEDIA_PAUSE)
delete_icon = build_stock(gtk.STOCK_DELETE)
@ -345,6 +346,16 @@ class vmmManager(gobject.GObject):
self.destroy_vm)
self.vmmenushutdown.add(self.vmmenushutdown_items["forcepoweroff"])
self.vmmenushutdown_items["sep"] = gtk.SeparatorMenuItem()
self.vmmenushutdown_items["sep"].show()
self.vmmenushutdown.add(self.vmmenushutdown_items["sep"])
self.vmmenushutdown_items["save"] = gtk.ImageMenuItem(_("Sa_ve"))
self.vmmenushutdown_items["save"].set_image(save_icon)
self.vmmenushutdown_items["save"].show()
self.vmmenushutdown_items["save"].connect("activate", self.save_vm)
self.vmmenushutdown.add(self.vmmenushutdown_items["save"])
self.vmmenu_items["hsep1"] = gtk.SeparatorMenuItem()
self.vmmenu_items["hsep1"].show()
self.vmmenu.add(self.vmmenu_items["hsep1"])
@ -608,41 +619,6 @@ class vmmManager(gobject.GObject):
self.emit("action-show-console",
conn.get_uri(), self.vm.get_uuid())
def restore_saved(self, src=None):
conn = self.current_connection()
if conn.is_remote():
self.err.val_err(_("Restoring virtual machines over remote "
"connections is not yet supported"))
return
path = util.browse_local(self.window.get_widget("vmm-manager"),
_("Restore Virtual Machine"),
self.config, conn,
browse_reason=self.config.CONFIG_DIR_RESTORE)
if not path:
return
progWin = vmmAsyncJob(self.config, self.restore_saved_callback,
[path], _("Restoring Virtual Machine"))
progWin.run()
error, details = progWin.get_error()
if error is not None:
self.err.show_err(error, details,
title=_("Error restoring domain"))
def restore_saved_callback(self, file_to_load, asyncjob):
try:
newconn = util.dup_conn(self.config, self.current_connection(),
return_conn_class=True)
newconn.restore(file_to_load)
except Exception, e:
err = (_("Error restoring domain '%s': %s") %
(file_to_load, str(e)))
details = "".join(traceback.format_exc())
asyncjob.set_error(err, details)
def do_delete(self, ignore=None):
conn = self.current_connection()
vm = self.current_vm()
@ -719,6 +695,12 @@ class vmmManager(gobject.GObject):
self.emit("action-destroy-domain",
vm.get_connection().get_uri(), vm.get_uuid())
def save_vm(self, ignore):
vm = self.current_vm()
if vm is not None:
self.emit("action-save-domain",
vm.get_connection().get_uri(), vm.get_uuid())
def pause_vm(self, ignore):
vm = self.current_vm()
if vm is not None:
@ -1004,7 +986,6 @@ class vmmManager(gobject.GObject):
else:
show_pause = bool(vm and vm.is_pauseable())
show_shutdown = bool(vm and vm.is_stoppable())
restore = bool(conn and conn.get_state() == vmmConnection.STATE_ACTIVE)
self.window.get_widget("vm-open").set_sensitive(show_open)
self.window.get_widget("vm-run").set_sensitive(show_run)
@ -1015,7 +996,6 @@ class vmmManager(gobject.GObject):
self.window.get_widget("menu_edit_details").set_sensitive(show_details)
self.window.get_widget("menu_host_details").set_sensitive(host_details)
self.window.get_widget("menu_edit_delete").set_sensitive(delete)
self.window.get_widget("menu_file_restore_saved").set_sensitive(restore)
def popup_vm_menu_key(self, widget, event):
if gtk.gdk.keyval_name(event.keyval) != "Menu":
@ -1065,6 +1045,7 @@ class vmmManager(gobject.GObject):
self.vmmenushutdown_items["poweroff"].set_sensitive(stop)
self.vmmenushutdown_items["reboot"].set_sensitive(stop)
self.vmmenushutdown_items["forcepoweroff"].set_sensitive(destroy)
self.vmmenushutdown_items["save"].set_sensitive(destroy)
self.vmmenu.popup(None, None, None, 0, event.time)
else:
# Pop up connection menu

View File

@ -595,7 +595,7 @@ def mediadev_set_default_selection(widget):
####################################################################
def build_shutdown_button_menu(config, widget, shutdown_cb, reboot_cb,
destroy_cb):
destroy_cb, save_cb):
icon_name = config.get_shutdown_icon_name()
widget.set_icon_name(icon_name)
menu = gtk.Menu()
@ -604,6 +604,7 @@ def build_shutdown_button_menu(config, widget, shutdown_cb, reboot_cb,
rebootimg = gtk.image_new_from_icon_name(icon_name, gtk.ICON_SIZE_MENU)
shutdownimg = gtk.image_new_from_icon_name(icon_name, gtk.ICON_SIZE_MENU)
destroyimg = gtk.image_new_from_icon_name(icon_name, gtk.ICON_SIZE_MENU)
saveimg = gtk.image_new_from_icon_name(gtk.STOCK_SAVE, gtk.ICON_SIZE_MENU)
reboot = gtk.ImageMenuItem(_("_Reboot"))
reboot.set_image(rebootimg)
@ -623,6 +624,16 @@ def build_shutdown_button_menu(config, widget, shutdown_cb, reboot_cb,
destroy.connect("activate", destroy_cb)
menu.add(destroy)
sep = gtk.SeparatorMenuItem()
sep.show()
menu.add(sep)
save = gtk.ImageMenuItem(_("Sa_ve"))
save.set_image(saveimg)
save.show()
save.connect("activate", save_cb)
menu.add(save)
#####################################
# Path permissions checker for qemu #
#####################################

View File

@ -142,6 +142,26 @@
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="menuitem3">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="details-menu-save">
<property name="label">_Save</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
<property name="use_stock">False</property>
<signal name="activate" handler="on_details_menu_save_activate"/>
<child internal-child="image">
<widget class="GtkImage" id="image123">
<property name="visible">True</property>
<property name="stock">gtk-save</property>
</widget>
</child>
</widget>
</child>
</widget>
</child>
<child internal-child="image">
@ -152,14 +172,6 @@
</child>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="details-menu-save">
<property name="visible">True</property>
<property name="label" translatable="yes">_Save</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_details_menu_save_activate"/>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator11">
<property name="visible">True</property>

View File

@ -22,6 +22,28 @@
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menuitem4_menu">
<child>
<widget class="GtkImageMenuItem" id="menu_file_restore_saved">
<property name="label" translatable="yes">Restore Saved Machine...</property>
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Restore a saved machine from a filesystem image</property>
<property name="use_underline">True</property>
<property name="use_stock">False</property>
<signal name="activate" handler="on_menu_restore_saved_activate"/>
<accelerator key="r" signal="activate" modifiers="GDK_MOD1_MASK"/>
<child internal-child="image">
<widget class="GtkImage" id="image3">
<property name="visible">True</property>
<property name="stock">gtk-open</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator4">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkMenuItem" id="menu-file-view-manager">
<property name="visible">True</property>
@ -2023,7 +2045,7 @@ here</property>
<property name="tooltip" translatable="yes">Stop Interface</property>
<signal name="clicked" handler="on_interface_stop_clicked"/>
<child>
<widget class="GtkImage" id="image3">
<widget class="GtkImage" id="image5">
<property name="visible">True</property>
<property name="stock">gtk-cancel</property>
</widget>
@ -2044,7 +2066,7 @@ here</property>
<property name="tooltip" translatable="yes">Delete Interface</property>
<signal name="clicked" handler="on_interface_delete_clicked"/>
<child>
<widget class="GtkImage" id="image5">
<widget class="GtkImage" id="image9">
<property name="visible">True</property>
<property name="stock">gtk-delete</property>
</widget>

View File

@ -23,28 +23,6 @@
<property name="use_underline">True</property>
<child>
<widget class="GtkMenu" id="menuitem4_menu">
<child>
<widget class="GtkImageMenuItem" id="menu_file_restore_saved">
<property name="label" translatable="yes">Restore Saved Machine...</property>
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Restore a saved machine from a filesystem image</property>
<property name="use_underline">True</property>
<property name="use_stock">False</property>
<signal name="activate" handler="on_menu_restore_saved_activate"/>
<accelerator key="r" signal="activate" modifiers="GDK_MOD1_MASK"/>
<child internal-child="image">
<widget class="GtkImage" id="image3">
<property name="visible">True</property>
<property name="stock">gtk-open</property>
</widget>
</child>
</widget>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator4">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" id="open_connection">
<property name="label" translatable="yes">_Add Connection...</property>