mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-01-13 13:17:57 +03:00
VM Migration support (Shigeki Sakamoto)
This commit is contained in:
parent
f4e8187283
commit
d2c6bb5996
@ -95,6 +95,8 @@ class vmmDetails(gobject.GObject):
|
||||
gobject.TYPE_NONE, []),
|
||||
"action-view-manager": (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, []),
|
||||
"action-migrate-domain": (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, (str,str,str)),
|
||||
}
|
||||
|
||||
|
||||
@ -131,6 +133,13 @@ class vmmDetails(gobject.GObject):
|
||||
menu = gtk.Menu()
|
||||
self.window.get_widget("control-shutdown").set_menu(menu)
|
||||
|
||||
self.migrate_menu_items = {}
|
||||
|
||||
self.migrate_menu_items["(None)"] = gtk.ImageMenuItem(_("(None)"))
|
||||
self.migrate_menu_items["(None)"].show()
|
||||
self.migrate_menu_items["(None)"].set_sensitive(False)
|
||||
self.window.get_widget("details-menu-migrate_menu").add(self.migrate_menu_items["(None)"])
|
||||
|
||||
rebootimg = gtk.Image()
|
||||
rebootimg.set_from_pixbuf(gtk.gdk.pixbuf_new_from_file_at_size(self.config.get_icon_dir() + "/icon_shutdown.png", 18, 18))
|
||||
shutdownimg = gtk.Image()
|
||||
@ -661,6 +670,32 @@ class vmmDetails(gobject.GObject):
|
||||
def control_vm_destroy(self, src):
|
||||
self.emit("action-destroy-domain", self.vm.get_connection().get_uri(), self.vm.get_uuid())
|
||||
|
||||
def control_vm_migrate(self, src):
|
||||
# get selected submenu(destination hostname)
|
||||
hostname = self.window.get_widget("details-menu-migrate_menu").get_active().get_image().get_stock()[0]
|
||||
for key in self.engine.connections.keys():
|
||||
if self.engine.get_connection(key).get_hostname() == hostname:
|
||||
host_uri = key
|
||||
break
|
||||
self.emit("action-migrate-domain", self.vm.get_connection().get_uri(), self.vm.get_uuid(), host_uri)
|
||||
|
||||
def set_migrate_menu(self):
|
||||
menu = self.window.get_widget("details-menu-migrate_menu")
|
||||
# clear migrate-submenu
|
||||
for submenu_item in menu.get_children():
|
||||
submenu_item_name = submenu_item.get_image().get_stock()[0]
|
||||
menu.remove(self.migrate_menu_items[submenu_item_name])
|
||||
|
||||
available_migrate_hostnames = self.engine.get_available_migrate_hostnames()
|
||||
if len(available_migrate_hostnames) == 0:
|
||||
menu.add(self.migrate_menu_items["(None)"])
|
||||
else:
|
||||
for hostname in available_migrate_hostnames.values():
|
||||
self.migrate_menu_items[hostname] = gtk.ImageMenuItem(hostname)
|
||||
self.migrate_menu_items[hostname].show()
|
||||
self.migrate_menu_items[hostname].connect("activate", self.control_vm_migrate)
|
||||
menu.add(self.migrate_menu_items[hostname])
|
||||
|
||||
def set_pause_widget_states(self, state):
|
||||
try:
|
||||
self.ignorePause = True
|
||||
@ -707,6 +742,13 @@ class vmmDetails(gobject.GObject):
|
||||
self.window.get_widget("details-menu-shutdown").set_sensitive(True)
|
||||
self.window.get_widget("details-menu-save").set_sensitive(True)
|
||||
|
||||
# Currently, the condition that "Migrate" become insensitive is only "readonly".
|
||||
if vm.is_read_only():
|
||||
self.window.get_widget("details-menu-migrate").set_sensitive(False)
|
||||
else:
|
||||
self.window.get_widget("details-menu-migrate").set_sensitive(True)
|
||||
self.set_migrate_menu()
|
||||
|
||||
if status in [ libvirt.VIR_DOMAIN_SHUTOFF ,libvirt.VIR_DOMAIN_CRASHED ]:
|
||||
if self.window.get_widget("console-pages").get_current_page() != PAGE_UNAVAILABLE:
|
||||
self.vncViewer.close()
|
||||
|
@ -1103,4 +1103,10 @@ class vmmDomain(gobject.GObject):
|
||||
self._disk_io = self._sample_disk_io_dummy
|
||||
|
||||
|
||||
def migrate(self, dictcon):
|
||||
flags = 0
|
||||
if self.lastStatus == libvirt.VIR_DOMAIN_RUNNING:
|
||||
flags = libvirt.VIR_MIGRATE_LIVE
|
||||
self.vm.migrate(self.connection.vmm, flags, None, dictcon.get_short_hostname(), 0)
|
||||
|
||||
gobject.type_register(vmmDomain)
|
||||
|
@ -202,6 +202,8 @@ class vmmEngine(gobject.GObject):
|
||||
self.shutdown_domain(src, uri, uuid)
|
||||
def _do_reboot_domain(self, src, uri, uuid):
|
||||
self.reboot_domain(src, uri, uuid)
|
||||
def _do_migrate_domain(self, src, uri, uuid, desturi):
|
||||
self.migrate_domain(uri, uuid, desturi)
|
||||
def _do_exit_app(self, src):
|
||||
self.exit_app()
|
||||
|
||||
@ -279,6 +281,7 @@ class vmmEngine(gobject.GObject):
|
||||
details.connect("action-reboot-domain", self._do_reboot_domain)
|
||||
details.connect("action-exit-app", self._do_exit_app)
|
||||
details.connect("action-view-manager", self._do_show_manager)
|
||||
details.connect("action-migrate-domain", self._do_migrate_domain)
|
||||
|
||||
except Exception, e:
|
||||
self.err.show_err(_("Error bringing up domain details: %s") % str(e),
|
||||
@ -296,6 +299,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-migrate-domain", self._do_migrate_domain)
|
||||
self.windowManager.connect("action-show-console", self._do_show_console)
|
||||
self.windowManager.connect("action-show-details", self._do_show_details)
|
||||
self.windowManager.connect("action-show-preferences", self._do_show_preferences)
|
||||
@ -529,6 +533,64 @@ class vmmEngine(gobject.GObject):
|
||||
else:
|
||||
logging.warning("Reboot requested, but machine is already shutting down / shutoff")
|
||||
|
||||
def migrate_domain(self, uri, uuid, desturi):
|
||||
conn = self.get_connection(uri, False)
|
||||
vm = conn.get_vm(uuid)
|
||||
destconn = self.get_connection(desturi, False)
|
||||
resp = self.err.yes_no(_("%s will be migrated from %s to %s, are you sure?") % \
|
||||
(vm.get_name(), conn.get_hostname(), destconn.get_hostname()))
|
||||
if resp:
|
||||
migrate_progress = None
|
||||
try:
|
||||
# show progress dialog
|
||||
migrate_progress = self.get_migrate_progress(vm.get_name(), conn.get_short_hostname(), destconn.get_short_hostname())
|
||||
migrate_progress.show()
|
||||
while gtk.events_pending():
|
||||
gtk.main_iteration()
|
||||
# call virDomainMigrate
|
||||
vm.migrate(destconn)
|
||||
# close progress dialog
|
||||
migrate_progress.destroy()
|
||||
except Exception, e:
|
||||
migrate_progress.destroy()
|
||||
self.err.show_err(_("Error migrating domain: %s") % str(e),
|
||||
"".join(traceback.format_exc()))
|
||||
self.windowManager.conn_refresh_resources(conn)
|
||||
self.windowManager.conn_refresh_resources(destconn)
|
||||
|
||||
def get_migrate_progress(self, vmname, hostname, desthostname):
|
||||
migrate_progress = None
|
||||
migrate_progress = gtk.MessageDialog(None, \
|
||||
gtk.DIALOG_DESTROY_WITH_PARENT, \
|
||||
gtk.MESSAGE_INFO, \
|
||||
gtk.BUTTONS_NONE, \
|
||||
_("%s will be migrated from %s to %s." % \
|
||||
(vmname, hostname, desthostname)))
|
||||
migrate_progress.set_title(" ")
|
||||
return migrate_progress
|
||||
|
||||
def get_available_migrate_hostnames(self):
|
||||
hostname = self.windowManager.current_connection().get_hostname()
|
||||
driver = self.windowManager.current_connection().get_driver()
|
||||
available_migrate_hostnames = {}
|
||||
|
||||
# 1. connected(ACTIVE, INACTIVE) host
|
||||
for key in self.connections.keys():
|
||||
if self.connections[key].has_key("connection") is True \
|
||||
and (self.get_connection(key).get_state() == vmmConnection.STATE_ACTIVE or self.get_connection(key).get_state() == vmmConnection.STATE_INACTIVE):
|
||||
available_migrate_hostnames[key] = self.get_connection(key).get_hostname()
|
||||
|
||||
# 2. remove source host
|
||||
for key in available_migrate_hostnames.keys():
|
||||
if available_migrate_hostnames[key] == hostname:
|
||||
del available_migrate_hostnames[key]
|
||||
|
||||
# 3. remove a different host of hypervisor
|
||||
for key in available_migrate_hostnames.keys():
|
||||
if self.get_connection(key).get_driver() != driver:
|
||||
del available_migrate_hostnames[key]
|
||||
|
||||
return available_migrate_hostnames
|
||||
|
||||
|
||||
gobject.type_register(vmmEngine)
|
||||
|
@ -105,6 +105,8 @@ class vmmManager(gobject.GObject):
|
||||
gobject.TYPE_NONE, [str]),
|
||||
"action-show-help": (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, [str]),
|
||||
"action-migrate-domain": (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, (str,str,str)),
|
||||
"action-exit-app": (gobject.SIGNAL_RUN_FIRST,
|
||||
gobject.TYPE_NONE, []),}
|
||||
|
||||
@ -156,6 +158,8 @@ class vmmManager(gobject.GObject):
|
||||
self.vmmenushutdown = gtk.Menu()
|
||||
self.vmmenu_items = {}
|
||||
self.vmmenushutdown_items = {}
|
||||
self.vmmenumigrate = gtk.Menu()
|
||||
self.vmmenumigrate_items = {}
|
||||
|
||||
self.vmmenu_items["run"] = gtk.ImageMenuItem("_Run")
|
||||
self.vmmenu_items["run"].set_image(self.vmmenu_icons["run"])
|
||||
@ -200,9 +204,23 @@ class vmmManager(gobject.GObject):
|
||||
self.vmmenushutdown_items["forcepoweroff"].connect("activate", self.destroy_vm)
|
||||
self.vmmenushutdown.add(self.vmmenushutdown_items["forcepoweroff"])
|
||||
|
||||
self.vmmenu_items["hsep"] = gtk.SeparatorMenuItem()
|
||||
self.vmmenu_items["hsep"].show();
|
||||
self.vmmenu.add(self.vmmenu_items["hsep"])
|
||||
self.vmmenu_items["hsep1"] = gtk.SeparatorMenuItem()
|
||||
self.vmmenu_items["hsep1"].show();
|
||||
self.vmmenu.add(self.vmmenu_items["hsep1"])
|
||||
|
||||
self.vmmenu_items["migrate"] = gtk.ImageMenuItem("_Migrate")
|
||||
self.vmmenu_items["migrate"].set_submenu(self.vmmenumigrate)
|
||||
self.vmmenu_items["migrate"].show()
|
||||
self.vmmenu.add(self.vmmenu_items["migrate"])
|
||||
|
||||
self.vmmenumigrate_items["(None)"] = gtk.ImageMenuItem(_("(None)"))
|
||||
self.vmmenumigrate_items["(None)"].show()
|
||||
self.vmmenumigrate_items["(None)"].set_sensitive(False)
|
||||
self.vmmenumigrate.add(self.vmmenumigrate_items["(None)"])
|
||||
|
||||
self.vmmenu_items["hsep2"] = gtk.SeparatorMenuItem()
|
||||
self.vmmenu_items["hsep2"].show();
|
||||
self.vmmenu.add(self.vmmenu_items["hsep2"])
|
||||
|
||||
self.vmmenu_items["open"] = gtk.ImageMenuItem(gtk.STOCK_OPEN)
|
||||
self.vmmenu_items["open"].connect("activate", self.open_vm_console)
|
||||
@ -718,6 +736,7 @@ class vmmManager(gobject.GObject):
|
||||
self.vmmenu_items["resume"].hide()
|
||||
self.vmmenu_items["resume"].set_sensitive(False)
|
||||
self.vmmenu_items["shutdown"].set_sensitive(False)
|
||||
self.vmmenu_items["migrate"].set_sensitive(False)
|
||||
else:
|
||||
if vm.status() == libvirt.VIR_DOMAIN_SHUTOFF:
|
||||
self.vmmenu_items["run"].set_sensitive(True)
|
||||
@ -726,6 +745,8 @@ class vmmManager(gobject.GObject):
|
||||
self.vmmenu_items["resume"].hide()
|
||||
self.vmmenu_items["resume"].set_sensitive(False)
|
||||
self.vmmenu_items["shutdown"].set_sensitive(False)
|
||||
self.vmmenu_items["migrate"].set_sensitive(True)
|
||||
self.set_migrate_submenu()
|
||||
elif vm.status() == libvirt.VIR_DOMAIN_RUNNING:
|
||||
self.vmmenu_items["run"].set_sensitive(False)
|
||||
self.vmmenu_items["pause"].set_sensitive(True)
|
||||
@ -733,6 +754,8 @@ class vmmManager(gobject.GObject):
|
||||
self.vmmenu_items["resume"].hide()
|
||||
self.vmmenu_items["resume"].set_sensitive(False)
|
||||
self.vmmenu_items["shutdown"].set_sensitive(True)
|
||||
self.vmmenu_items["migrate"].set_sensitive(True)
|
||||
self.set_migrate_submenu()
|
||||
elif vm.status() == libvirt.VIR_DOMAIN_PAUSED:
|
||||
self.vmmenu_items["run"].set_sensitive(False)
|
||||
self.vmmenu_items["pause"].hide()
|
||||
@ -740,6 +763,8 @@ class vmmManager(gobject.GObject):
|
||||
self.vmmenu_items["resume"].show()
|
||||
self.vmmenu_items["resume"].set_sensitive(True)
|
||||
self.vmmenu_items["shutdown"].set_sensitive(True)
|
||||
self.vmmenu_items["migrate"].set_sensitive(True)
|
||||
self.set_migrate_submenu()
|
||||
self.vmmenu.popup(None, None, None, 0, event.time)
|
||||
return False
|
||||
else:
|
||||
@ -1052,6 +1077,33 @@ class vmmManager(gobject.GObject):
|
||||
if vm is not None:
|
||||
self.emit("action-resume-domain", vm.get_connection().get_uri(), vm.get_uuid())
|
||||
|
||||
def migrate(self, ignore):
|
||||
vm = self.current_vm()
|
||||
# get selected submenu(destination hostname)
|
||||
hostname = self.vmmenumigrate.get_active().get_image().get_stock()[0]
|
||||
for key in self.engine.connections.keys():
|
||||
if self.engine.get_connection(key).get_hostname() == hostname:
|
||||
host_uri = key
|
||||
break
|
||||
if vm is not None:
|
||||
self.emit("action-migrate-domain", vm.get_connection().get_uri(), vm.get_uuid(), host_uri)
|
||||
|
||||
def set_migrate_submenu(self):
|
||||
# clear migrate-submenu
|
||||
for submenu_item in self.vmmenumigrate.get_children():
|
||||
submenu_item_name = submenu_item.get_image().get_stock()[0]
|
||||
self.vmmenumigrate.remove(self.vmmenumigrate_items[submenu_item_name])
|
||||
|
||||
available_migrate_hostnames = self.engine.get_available_migrate_hostnames()
|
||||
if len(available_migrate_hostnames) == 0:
|
||||
self.vmmenumigrate.add(self.vmmenumigrate_items["(None)"])
|
||||
else:
|
||||
for hostname in available_migrate_hostnames.values():
|
||||
self.vmmenumigrate_items[hostname] = gtk.ImageMenuItem(hostname)
|
||||
self.vmmenumigrate_items[hostname].show()
|
||||
self.vmmenumigrate_items[hostname].connect("activate", self.migrate)
|
||||
self.vmmenumigrate.add(self.vmmenumigrate_items[hostname])
|
||||
|
||||
def _add_connection(self, engine, conn):
|
||||
conn.connect("vm-added", self.vm_added)
|
||||
conn.connect("vm-removed", self.vm_removed)
|
||||
|
@ -166,6 +166,27 @@
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="details-menu-migrate">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Migrate</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_details_menu_migrate_activate"/>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenu" id="details-menu-migrate_menu">
|
||||
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkSeparatorMenuItem" id="separator12">
|
||||
<property name="visible">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="details-menu-vm-screenshot">
|
||||
<property name="visible">True</property>
|
||||
|
Loading…
Reference in New Issue
Block a user