mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-03-09 08:58:27 +03:00
Kind of a big mess but it was difficult to untangle piecemeal. Basically this drops nearly all the centralized window tracking in engine.py and moves it to each UI class. If manager.py wants to open a details window it does it directly, and vmmDetails tracks the window object list itself. This simplifies things and makes the code easier to follow. There's still some weirdness with vmmConnect and connection callbacks, but future patches will break those apart.
346 lines
12 KiB
Python
346 lines
12 KiB
Python
#
|
|
# Copyright (C) 2009, 2013, 2014 Red Hat, Inc.
|
|
# Copyright (C) 2009 Cole Robinson <crobinso@redhat.com>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
# MA 02110-1301 USA.
|
|
#
|
|
|
|
import logging
|
|
|
|
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",
|
|
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):
|
|
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"))
|
|
|
|
@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 .details import vmmDetails
|
|
vmmDetails.get_instance(src, vm).show()
|