Support libvirt managed save/restore

If a VM has a saved image, we s/Run/Restore/g for all the Run UI so the user
is aware they will be restoring from a saved state.
This commit is contained in:
Cole Robinson 2010-05-12 12:57:32 -04:00
parent 225f4d344a
commit 2ab8c6ce03
5 changed files with 70 additions and 20 deletions

View File

@ -127,6 +127,7 @@ class vmmConnection(gobject.GObject):
self._nodedev_capable = None self._nodedev_capable = None
self._xml_flags = {} self._xml_flags = {}
self._support_dict = {}
# Physical network interfaces: name -> virtinst.NodeDevice # Physical network interfaces: name -> virtinst.NodeDevice
self.nodedevs = {} self.nodedevs = {}
@ -532,6 +533,15 @@ class vmmConnection(gobject.GObject):
return self._get_flags_helper(vm, key, check_func) return self._get_flags_helper(vm, key, check_func)
def get_dom_managedsave_supported(self, vm):
key = virtinst.support.SUPPORT_DOMAIN_MANAGED_SAVE
if key not in self._support_dict:
val = virtinst.support.check_domain_support(vm, key)
logging.debug("Connection managed save support: %s" % val)
self._support_dict[key] = val
return self._support_dict[key]
def get_interface_flags(self, iface): def get_interface_flags(self, iface):
key = "interface" key = "interface"

View File

@ -815,6 +815,16 @@ class vmmDetails(gobject.GObject):
if newpage == PAGE_CONSOLE or newpage >= PAGE_DYNAMIC_OFFSET: if newpage == PAGE_CONSOLE or newpage >= PAGE_DYNAMIC_OFFSET:
self.last_console_page = newpage self.last_console_page = newpage
def change_run_text(self, can_restore):
if can_restore:
text = _("_Restore")
else:
text = _("_Run")
strip_text = text.replace("_", "")
self.window.get_widget("details-menu-run").get_child().set_label(text)
self.window.get_widget("control-run").set_label(strip_text)
def update_widget_states(self, vm, status, ignore=None): def update_widget_states(self, vm, status, ignore=None):
self.toggle_toolbar(self.window.get_widget("details-menu-view-toolbar")) self.toggle_toolbar(self.window.get_widget("details-menu-view-toolbar"))
@ -824,6 +834,9 @@ class vmmDetails(gobject.GObject):
paused = vm.is_paused() paused = vm.is_paused()
ro = vm.is_read_only() ro = vm.is_read_only()
if vm.managedsave_supported:
self.change_run_text(vm.hasSavedImage())
self.window.get_widget("details-menu-destroy").set_sensitive(destroy) self.window.get_widget("details-menu-destroy").set_sensitive(destroy)
self.window.get_widget("control-run").set_sensitive(run) self.window.get_widget("control-run").set_sensitive(run)
self.window.get_widget("details-menu-run").set_sensitive(run) self.window.get_widget("details-menu-run").set_sensitive(run)

View File

@ -77,6 +77,8 @@ class vmmDomainBase(vmmLibvirtObject):
self._startup_vcpus = None self._startup_vcpus = None
self.managedsave_supported = False
self._network_traffic = None self._network_traffic = None
self._disk_io = None self._disk_io = None
@ -228,6 +230,9 @@ class vmmDomainBase(vmmLibvirtObject):
return "-" return "-"
return str(i) return str(i)
def hasSavedImage(self):
return False
def get_abi_type(self): def get_abi_type(self):
return str(vutil.get_xml_path(self.get_xml(), return str(vutil.get_xml_path(self.get_xml(),
"/domain/os/type")).lower() "/domain/os/type")).lower()
@ -1181,6 +1186,7 @@ class vmmDomain(vmmDomainBase):
self.getvcpus_supported = support.check_domain_support(self._backend, self.getvcpus_supported = support.check_domain_support(self._backend,
support.SUPPORT_DOMAIN_GETVCPUS) support.SUPPORT_DOMAIN_GETVCPUS)
self.managedsave_supported = self.connection.get_dom_managedsave_supported(self._backend)
self.toggle_sample_network_traffic() self.toggle_sample_network_traffic()
self.toggle_sample_disk_io() self.toggle_sample_disk_io()
@ -1303,14 +1309,16 @@ class vmmDomain(vmmDomainBase):
self._backend.resume() self._backend.resume()
self._update_status() self._update_status()
def save(self, filename, background=True): def hasSavedImage(self):
if background: if not self.managedsave_supported:
conn = util.dup_conn(self.config, self.connection) return False
vm = conn.lookupByID(self.get_id()) return self._backend.hasManagedSaveImage(0)
else:
vm = self._backend
vm.save(filename) def save(self, filename=None):
if not self.managedsave_supported:
self._backend.save(filename)
else:
self._backend.managedSave(0)
self._update_status() self._update_status()
def destroy(self): def destroy(self):

View File

@ -713,12 +713,13 @@ class vmmEngine(gobject.GObject):
def save_domain(self, src, uri, uuid): def save_domain(self, src, uri, uuid):
conn = self._lookup_connection(uri) conn = self._lookup_connection(uri)
vm = conn.get_vm(uuid) vm = conn.get_vm(uuid)
managed = bool(vm.managedsave_supported)
do_prompt = self.config.get_confirm_poweroff() do_prompt = self.config.get_confirm_poweroff()
if conn.is_remote(): if managed and conn.is_remote():
# FIXME: This should work with remote storage stuff
self.err.val_err(_("Saving virtual machines over remote " self.err.val_err(_("Saving virtual machines over remote "
"connections is not yet supported.")) "connections is not supported with this "
"libvirt version or hypervisor."))
return return
if do_prompt: if do_prompt:
@ -733,16 +734,18 @@ class vmmEngine(gobject.GObject):
return return
self.config.set_confirm_poweroff(not skip_prompt) self.config.set_confirm_poweroff(not skip_prompt)
path = util.browse_local(src.window.get_widget("vmm-details"), path = None
_("Save Virtual Machine"), if not managed:
self.config, conn, path = util.browse_local(src.window.get_widget("vmm-details"),
dialog_type=gtk.FILE_CHOOSER_ACTION_SAVE, _("Save Virtual Machine"),
browse_reason=self.config.CONFIG_DIR_SAVE) self.config, conn,
dialog_type=gtk.FILE_CHOOSER_ACTION_SAVE,
browse_reason=self.config.CONFIG_DIR_SAVE)
if not path:
return
if not path: progWin = vmmAsyncJob(self.config, self._save_callback,
return [vm, path],
progWin = vmmAsyncJob(self.config, self._save_callback, [vm, path],
_("Saving Virtual Machine")) _("Saving Virtual Machine"))
progWin.run() progWin.run()
error, details = progWin.get_error() error, details = progWin.get_error()
@ -752,7 +755,11 @@ class vmmEngine(gobject.GObject):
def _save_callback(self, vm, file_to_save, asyncjob): def _save_callback(self, vm, file_to_save, asyncjob):
try: try:
vm.save(file_to_save) conn = util.dup_conn(self.config, vm.connection,
return_conn_class=True)
newvm = conn.get_vm(vm.get_uuid())
newvm.save(file_to_save)
except Exception, e: except Exception, e:
asyncjob.set_error(str(e), "".join(traceback.format_exc())) asyncjob.set_error(str(e), "".join(traceback.format_exc()))

View File

@ -969,6 +969,15 @@ class vmmManager(gobject.GObject):
child = model.iter_children(parent) child = model.iter_children(parent)
model.row_changed(row.path, row.iter) model.row_changed(row.path, row.iter)
def change_run_text(self, can_restore):
if can_restore:
text = _("_Restore")
else:
text = _("_Run")
strip_text = text.replace("_", "")
self.vmmenu_items["run"].get_child().set_label(text)
self.window.get_widget("vm-run").set_label(strip_text)
def vm_selected(self, ignore=None): def vm_selected(self, ignore=None):
conn = self.current_connection() conn = self.current_connection()
@ -987,6 +996,9 @@ class vmmManager(gobject.GObject):
show_pause = bool(vm and vm.is_pauseable()) show_pause = bool(vm and vm.is_pauseable())
show_shutdown = bool(vm and vm.is_stoppable()) show_shutdown = bool(vm and vm.is_stoppable())
if vm and vm.managedsave_supported:
self.change_run_text(vm.hasSavedImage())
self.window.get_widget("vm-open").set_sensitive(show_open) self.window.get_widget("vm-open").set_sensitive(show_open)
self.window.get_widget("vm-run").set_sensitive(show_run) self.window.get_widget("vm-run").set_sensitive(show_run)
self.window.get_widget("vm-shutdown").set_sensitive(show_shutdown) self.window.get_widget("vm-shutdown").set_sensitive(show_shutdown)