mirror of
https://github.com/virt-manager/virt-manager.git
synced 2024-12-22 13:34:07 +03:00
f107e39989
Init a shared log instance in virtinst/logger.py, and use that throughout the code base, so we aren't calling directly into 'logging'. This helps protect our logging output from being cluttered with other library output, as happens with some 'requests' usage
331 lines
12 KiB
Python
331 lines
12 KiB
Python
# Copyright (C) 2009, 2013, 2014 Red Hat, Inc.
|
|
# Copyright (C) 2009 Cole Robinson <crobinso@redhat.com>
|
|
#
|
|
# This work is licensed under the GNU GPLv2 or later.
|
|
# See the COPYING file in the top-level directory.
|
|
|
|
from gi.repository import Gtk
|
|
|
|
from virtinst import log
|
|
|
|
from .asyncjob import vmmAsyncJob
|
|
|
|
|
|
####################################################################
|
|
# Build toolbar shutdown button menu (manager and details toolbar) #
|
|
####################################################################
|
|
|
|
class _VMMenu(Gtk.Menu):
|
|
def __init__(self, src, current_vm_cb, show_open=True):
|
|
Gtk.Menu.__init__(self)
|
|
self._parent = src
|
|
self._current_vm_cb = current_vm_cb
|
|
self._show_open = show_open
|
|
|
|
self._init_state()
|
|
|
|
def _add_action(self, label, widgetname, cb,
|
|
iconname="system-shutdown"):
|
|
if label.startswith("gtk-"):
|
|
item = Gtk.ImageMenuItem.new_from_stock(label, None)
|
|
else:
|
|
item = Gtk.ImageMenuItem.new_with_mnemonic(label)
|
|
|
|
if iconname:
|
|
if iconname.startswith("gtk-"):
|
|
icon = Gtk.Image.new_from_stock(iconname, Gtk.IconSize.MENU)
|
|
else:
|
|
icon = Gtk.Image.new_from_icon_name(iconname,
|
|
Gtk.IconSize.MENU)
|
|
item.set_image(icon)
|
|
|
|
item.vmm_widget_name = widgetname
|
|
if cb:
|
|
def _cb(_menuitem):
|
|
_vm = self._current_vm_cb()
|
|
if _vm:
|
|
return cb(self._parent, _vm)
|
|
item.connect("activate", _cb)
|
|
|
|
self.add(item)
|
|
return item
|
|
|
|
def _init_state(self):
|
|
raise NotImplementedError()
|
|
def update_widget_states(self, vm):
|
|
raise NotImplementedError()
|
|
|
|
|
|
class VMShutdownMenu(_VMMenu):
|
|
"""
|
|
Shutdown submenu for reboot, forceoff, reset, etc.
|
|
"""
|
|
def _init_state(self):
|
|
self._add_action(_("_Reboot"), "reboot", VMActionUI.reboot)
|
|
self._add_action(_("_Shut Down"), "shutdown", VMActionUI.shutdown)
|
|
self._add_action(_("F_orce Reset"), "reset", VMActionUI.reset)
|
|
self._add_action(_("_Force Off"), "destroy", VMActionUI.destroy)
|
|
self.add(Gtk.SeparatorMenuItem())
|
|
self._add_action(_("Sa_ve"), "save", VMActionUI.save,
|
|
iconname=Gtk.STOCK_SAVE)
|
|
|
|
self.show_all()
|
|
|
|
def update_widget_states(self, vm):
|
|
statemap = {
|
|
"reboot": bool(vm and vm.is_stoppable()),
|
|
"shutdown": bool(vm and vm.is_stoppable()),
|
|
"reset": bool(vm and vm.is_stoppable()),
|
|
"destroy": bool(vm and vm.is_destroyable()),
|
|
"save": bool(vm and vm.is_destroyable()),
|
|
}
|
|
|
|
for child in self.get_children():
|
|
name = getattr(child, "vmm_widget_name", None)
|
|
if name in statemap:
|
|
child.set_sensitive(statemap[name])
|
|
|
|
if name == "reset":
|
|
child.set_tooltip_text(None)
|
|
if vm and not vm.conn.support.conn_domain_reset():
|
|
child.set_tooltip_text(_("Hypervisor does not support "
|
|
"domain reset."))
|
|
child.set_sensitive(False)
|
|
|
|
|
|
class VMActionMenu(_VMMenu):
|
|
"""
|
|
VM submenu for run, pause, shutdown, clone, etc
|
|
"""
|
|
def _init_state(self):
|
|
self._add_action(_("_Run"), "run", VMActionUI.run,
|
|
iconname=Gtk.STOCK_MEDIA_PLAY)
|
|
self._add_action(_("_Pause"), "suspend", VMActionUI.suspend,
|
|
Gtk.STOCK_MEDIA_PAUSE)
|
|
self._add_action(_("R_esume"), "resume", VMActionUI.resume,
|
|
Gtk.STOCK_MEDIA_PAUSE)
|
|
s = self._add_action(_("_Shut Down"), "shutdown", None)
|
|
s.set_submenu(VMShutdownMenu(self._parent, self._current_vm_cb))
|
|
|
|
self.add(Gtk.SeparatorMenuItem())
|
|
self._add_action(_("Clone..."), "clone",
|
|
VMActionUI.clone, iconname=None)
|
|
self._add_action(_("Migrate..."), "migrate",
|
|
VMActionUI.migrate, iconname=None)
|
|
self._add_action(_("_Delete"), "delete",
|
|
VMActionUI.delete, iconname=Gtk.STOCK_DELETE)
|
|
|
|
if self._show_open:
|
|
self.add(Gtk.SeparatorMenuItem())
|
|
self._add_action(Gtk.STOCK_OPEN, "show",
|
|
VMActionUI.show, iconname=None)
|
|
|
|
self.show_all()
|
|
|
|
def update_widget_states(self, vm):
|
|
statemap = {
|
|
"run": bool(vm and vm.is_runable()),
|
|
"shutdown": bool(vm and vm.is_stoppable()),
|
|
"suspend": bool(vm and vm.is_stoppable()),
|
|
"resume": bool(vm and vm.is_paused()),
|
|
"migrate": bool(vm and vm.is_stoppable()),
|
|
"clone": bool(vm and vm.is_clonable()),
|
|
}
|
|
vismap = {
|
|
"suspend": bool(vm and not vm.is_paused()),
|
|
"resume": bool(vm and vm.is_paused()),
|
|
}
|
|
|
|
for child in self.get_children():
|
|
name = getattr(child, "vmm_widget_name", None)
|
|
if child.get_submenu():
|
|
child.get_submenu().update_widget_states(vm)
|
|
if name in statemap:
|
|
child.set_sensitive(statemap[name])
|
|
if name in vismap:
|
|
child.set_visible(vismap[name])
|
|
|
|
def change_run_text(self, text):
|
|
for child in self.get_children():
|
|
if getattr(child, "vmm_widget_name", None) == "run":
|
|
child.get_child().set_label(text)
|
|
|
|
|
|
class VMActionUI(object):
|
|
"""
|
|
Singleton object for handling VM actions, asking for confirmation,
|
|
showing errors/progress dialogs, etc.
|
|
"""
|
|
|
|
@staticmethod
|
|
def save_cancel(asyncjob, vm):
|
|
log.debug("Cancelling save job")
|
|
if not vm:
|
|
return
|
|
|
|
try:
|
|
vm.abort_job()
|
|
except Exception as e:
|
|
log.exception("Error cancelling save job")
|
|
asyncjob.show_warning(_("Error cancelling save job: %s") % str(e))
|
|
return
|
|
|
|
asyncjob.job_canceled = True
|
|
return
|
|
|
|
@staticmethod
|
|
def save(src, vm):
|
|
if not src.err.chkbox_helper(src.config.get_confirm_poweroff,
|
|
src.config.set_confirm_poweroff,
|
|
text1=_("Are you sure you want to save '%s'?") % vm.get_name()):
|
|
return
|
|
|
|
_cancel_cb = None
|
|
if vm.getjobinfo_supported:
|
|
_cancel_cb = (VMActionUI.save_cancel, vm)
|
|
|
|
def cb(asyncjob):
|
|
vm.save(meter=asyncjob.get_meter())
|
|
def finish_cb(error, details):
|
|
if error is not None:
|
|
error = _("Error saving domain: %s") % error
|
|
src.err.show_err(error, details=details)
|
|
|
|
progWin = vmmAsyncJob(cb, [],
|
|
finish_cb, [],
|
|
_("Saving Virtual Machine"),
|
|
_("Saving virtual machine memory to disk "),
|
|
src.topwin, cancel_cb=_cancel_cb)
|
|
progWin.run()
|
|
|
|
@staticmethod
|
|
def destroy(src, vm):
|
|
if not src.err.chkbox_helper(
|
|
src.config.get_confirm_forcepoweroff,
|
|
src.config.set_confirm_forcepoweroff,
|
|
text1=_("Are you sure you want to force poweroff '%s'?" %
|
|
vm.get_name()),
|
|
text2=_("This will immediately poweroff the VM without "
|
|
"shutting down the OS and may cause data loss.")):
|
|
return
|
|
|
|
log.debug("Destroying vm '%s'", vm.get_name())
|
|
vmmAsyncJob.simple_async_noshow(vm.destroy, [], src,
|
|
_("Error shutting down domain"))
|
|
|
|
@staticmethod
|
|
def suspend(src, vm):
|
|
if not src.err.chkbox_helper(src.config.get_confirm_pause,
|
|
src.config.set_confirm_pause,
|
|
text1=_("Are you sure you want to pause '%s'?" %
|
|
vm.get_name())):
|
|
return
|
|
|
|
log.debug("Pausing vm '%s'", vm.get_name())
|
|
vmmAsyncJob.simple_async_noshow(vm.suspend, [], src,
|
|
_("Error pausing domain"))
|
|
|
|
@staticmethod
|
|
def resume(src, vm):
|
|
log.debug("Unpausing vm '%s'", vm.get_name())
|
|
vmmAsyncJob.simple_async_noshow(vm.resume, [], src,
|
|
_("Error unpausing domain"))
|
|
|
|
@staticmethod
|
|
def run(src, vm):
|
|
log.debug("Starting vm '%s'", vm.get_name())
|
|
|
|
if vm.has_managed_save():
|
|
def errorcb(error, details):
|
|
# This is run from the main thread
|
|
res = src.err.show_err(
|
|
_("Error restoring domain") + ": " + error,
|
|
details=details,
|
|
text2=_(
|
|
"The domain could not be restored. Would you like\n"
|
|
"to remove the saved state and perform a regular\n"
|
|
"start up?"),
|
|
dialog_type=Gtk.MessageType.WARNING,
|
|
buttons=Gtk.ButtonsType.YES_NO,
|
|
modal=True)
|
|
|
|
if not res:
|
|
return
|
|
|
|
try:
|
|
vm.remove_saved_image()
|
|
VMActionUI.run(src, vm)
|
|
except Exception as e:
|
|
src.err.show_err(_("Error removing domain state: %s")
|
|
% str(e))
|
|
|
|
# VM will be restored, which can take some time, so show progress
|
|
title = _("Restoring Virtual Machine")
|
|
text = _("Restoring virtual machine memory from disk")
|
|
vmmAsyncJob.simple_async(vm.startup, [], src,
|
|
title, text, "", errorcb=errorcb)
|
|
|
|
else:
|
|
# Regular startup
|
|
errorintro = _("Error starting domain")
|
|
vmmAsyncJob.simple_async_noshow(vm.startup, [], src, errorintro)
|
|
|
|
@staticmethod
|
|
def shutdown(src, vm):
|
|
if not src.err.chkbox_helper(src.config.get_confirm_poweroff,
|
|
src.config.set_confirm_poweroff,
|
|
text1=_("Are you sure you want to poweroff '%s'?" %
|
|
vm.get_name())):
|
|
return
|
|
|
|
log.debug("Shutting down vm '%s'", vm.get_name())
|
|
vmmAsyncJob.simple_async_noshow(vm.shutdown, [], src,
|
|
_("Error shutting down domain"))
|
|
|
|
@staticmethod
|
|
def reboot(src, vm):
|
|
if not src.err.chkbox_helper(src.config.get_confirm_poweroff,
|
|
src.config.set_confirm_poweroff,
|
|
text1=_("Are you sure you want to reboot '%s'?" %
|
|
vm.get_name())):
|
|
return
|
|
|
|
log.debug("Rebooting vm '%s'", vm.get_name())
|
|
vmmAsyncJob.simple_async_noshow(vm.reboot, [], src,
|
|
_("Error rebooting domain"))
|
|
|
|
@staticmethod
|
|
def reset(src, vm):
|
|
if not src.err.chkbox_helper(
|
|
src.config.get_confirm_forcepoweroff,
|
|
src.config.set_confirm_forcepoweroff,
|
|
text1=_("Are you sure you want to force reset '%s'?" %
|
|
vm.get_name()),
|
|
text2=_("This will immediately reset the VM without "
|
|
"shutting down the OS and may cause data loss.")):
|
|
return
|
|
|
|
log.debug("Resetting vm '%s'", vm.get_name())
|
|
vmmAsyncJob.simple_async_noshow(vm.reset, [], src,
|
|
_("Error resetting domain"))
|
|
|
|
@staticmethod
|
|
def delete(src, vm):
|
|
from .delete import vmmDeleteDialog
|
|
vmmDeleteDialog.show_instance(src, vm)
|
|
|
|
@staticmethod
|
|
def migrate(src, vm):
|
|
from .migrate import vmmMigrateDialog
|
|
vmmMigrateDialog.show_instance(src, vm)
|
|
|
|
@staticmethod
|
|
def clone(src, vm):
|
|
from .clone import vmmCloneVM
|
|
vmmCloneVM.show_instance(src, vm)
|
|
|
|
@staticmethod
|
|
def show(src, vm):
|
|
from .vmwindow import vmmVMWindow
|
|
vmmVMWindow.get_instance(src, vm).show()
|