From 225f4d344a93b956842df016a3d23f7e853d20ad Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 12 May 2010 11:42:59 -0400 Subject: [PATCH] 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. --- src/virtManager/details.py | 7 ++-- src/virtManager/engine.py | 39 ++++++++++++++++++++++ src/virtManager/host.py | 7 ++++ src/virtManager/manager.py | 63 +++++++++++++----------------------- src/virtManager/uihelpers.py | 13 +++++++- src/vmm-details.glade | 28 +++++++++++----- src/vmm-host.glade | 26 +++++++++++++-- src/vmm-manager.glade | 22 ------------- 8 files changed, 128 insertions(+), 77 deletions(-) diff --git a/src/virtManager/details.py b/src/virtManager/details.py index 3303b8f9a..06d43c1ac 100644 --- a/src/virtManager/details.py +++ b/src/virtManager/details.py @@ -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): diff --git a/src/virtManager/engine.py b/src/virtManager/engine.py index 33ec8191a..12e45eab3 100644 --- a/src/virtManager/engine.py +++ b/src/virtManager/engine.py @@ -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) diff --git a/src/virtManager/host.py b/src/virtManager/host.py index 5bd54db46..29f7ff1f5 100644 --- a/src/virtManager/host.py +++ b/src/virtManager/host.py @@ -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) diff --git a/src/virtManager/manager.py b/src/virtManager/manager.py index 72d8af3d0..ec3b799d7 100644 --- a/src/virtManager/manager.py +++ b/src/virtManager/manager.py @@ -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 diff --git a/src/virtManager/uihelpers.py b/src/virtManager/uihelpers.py index 64a6e8b1c..9706d6738 100644 --- a/src/virtManager/uihelpers.py +++ b/src/virtManager/uihelpers.py @@ -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 # ##################################### diff --git a/src/vmm-details.glade b/src/vmm-details.glade index f848ea619..cb53abd69 100644 --- a/src/vmm-details.glade +++ b/src/vmm-details.glade @@ -142,6 +142,26 @@ + + + True + + + + + _Save + True + True + False + + + + True + gtk-save + + + + @@ -152,14 +172,6 @@ - - - True - _Save - True - - - True diff --git a/src/vmm-host.glade b/src/vmm-host.glade index cd3dd4e96..7988cefcb 100644 --- a/src/vmm-host.glade +++ b/src/vmm-host.glade @@ -22,6 +22,28 @@ True + + + Restore Saved Machine... + True + Restore a saved machine from a filesystem image + True + False + + + + + True + gtk-open + + + + + + + True + + True @@ -2023,7 +2045,7 @@ here Stop Interface - + True gtk-cancel @@ -2044,7 +2066,7 @@ here Delete Interface - + True gtk-delete diff --git a/src/vmm-manager.glade b/src/vmm-manager.glade index 8be9ac83f..6d292b02a 100644 --- a/src/vmm-manager.glade +++ b/src/vmm-manager.glade @@ -23,28 +23,6 @@ True - - - Restore Saved Machine... - True - Restore a saved machine from a filesystem image - True - False - - - - - True - gtk-open - - - - - - - True - - _Add Connection...