virt-manager/virtManager/vmmenu.py

332 lines
12 KiB
Python
Raw Normal View History

# Copyright (C) 2009, 2013, 2014 Red Hat, Inc.
# Copyright (C) 2009 Cole Robinson <crobinso@redhat.com>
#
# This work is licensed under the GNU GPLv2.
# See the COPYING file in the top-level directory.
import logging
Convert to use GTK3 and GObject Introspection bindings Switch over to use GObject introspection bindings for all python modules related to GObject/GTK3/etc. It is not possible to mix and match old pyggtk/pygobject manual bindings with new introspection based bindings so it must be all changed in one go. Imports like import gtk Change to from gi.repository import Gtk The vmmGObject class is changed to always inherit from GObject.GObject There is no compelling reason to avoid a GObject dep for the virt-manager TUI & it horribly messed up the code. Signal declarations are changed from vmmChooseCD.signal_new(vmmChooseCD, "cdrom-chosen", [object, str]) To __gsignals__ = { "cdrom-chosen": (GObject.SignalFlags.RUN_FIRST, None, [object, str]) } which is required by new GObject bindings Most of the rest of the change is simply dealing with renamed constants / classes. Alot of legacy compat code was removed - ie helpers which check to see if certain GTK2 methods are available are no longer required since we're mandating GTK3 only. The event loop is replaced with LibvirtGLib's event loop. Still todo - Rip out all DBus stuff & make vmmEngine class inherit GtkApplication which provides unique support & DBus method handling - Switch to use LibvirtGConfig & LibvirtGObject for libvirt interaction - Possibly switch to Python 3 too ? - Figure out why GNOME keyring is missing Introspection support My suggestion is that the standalone GIT repo for virt-install only live on as a support branch for legacy platforms. A stable-0.9 branch of virt-manager can be kept for legacy PyGtk2 based virt-manager releases. The virt-manager master branch should exclusively use GObject inspection and ideally Python3 and contain both the virt-manager and virt-install codebases in one since they are intimately related to each other & using separate GIT repos has needlessly complicated life for everyone. crobinso: Some locking fixes Misc cleanups and dropping now-useless code Fix dbus usage Fix graph cell renderer regression Fix a couple tooltip issues
2012-05-14 21:24:56 +08:00
from gi.repository import Gtk
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.check_support(
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",
2018-03-15 05:50:22 +08:00
VMActionUI.migrate, iconname=None)
self._add_action(_("_Delete"), "delete",
2018-03-15 05:48:17 +08:00
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)
2015-04-12 02:14:24 +08:00
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):
logging.debug("Cancelling save job")
if not vm:
return
try:
vm.abort_job()
except Exception as e:
logging.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
logging.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
logging.debug("Pausing vm '%s'", vm.get_name())
vmmAsyncJob.simple_async_noshow(vm.suspend, [], src,
_("Error pausing domain"))
@staticmethod
def resume(src, vm):
logging.debug("Unpausing vm '%s'", vm.get_name())
vmmAsyncJob.simple_async_noshow(vm.resume, [], src,
_("Error unpausing domain"))
@staticmethod
def run(src, vm):
logging.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
logging.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
logging.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
logging.debug("Resetting vm '%s'", vm.get_name())
vmmAsyncJob.simple_async_noshow(vm.reset, [], src,
_("Error resetting domain"))
2018-03-15 05:48:17 +08:00
@staticmethod
def delete(src, vm):
from .delete import vmmDeleteDialog
vmmDeleteDialog.show_instance(src, vm)
2018-03-15 05:50:22 +08:00
@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 .details import vmmDetails
vmmDetails.get_instance(src, vm).show()