2009-11-15 14:17:03 -06:00
|
|
|
#
|
|
|
|
# Copyright (C) 2009 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 gobject
|
2010-11-30 14:01:02 -06:00
|
|
|
import gtk
|
2009-11-15 14:17:03 -06:00
|
|
|
|
|
|
|
import traceback
|
|
|
|
import logging
|
2010-11-29 18:11:35 -06:00
|
|
|
import threading
|
2009-11-15 14:17:03 -06:00
|
|
|
|
|
|
|
import virtinst
|
|
|
|
import libvirt
|
|
|
|
|
|
|
|
from virtManager import util
|
|
|
|
from virtManager.error import vmmErrorDialog
|
|
|
|
from virtManager.asyncjob import vmmAsyncJob
|
|
|
|
from virtManager.createmeter import vmmCreateMeter
|
|
|
|
from virtManager.domain import vmmDomain
|
|
|
|
|
|
|
|
def uri_join(uri_tuple):
|
|
|
|
scheme, user, host, path, query, fragment = uri_tuple
|
|
|
|
|
|
|
|
user = (user and (user + "@") or "")
|
|
|
|
host = host or ""
|
|
|
|
path = path or "/"
|
|
|
|
query = (query and ("?" + query) or "")
|
|
|
|
fragment = (fragment and ("#" + fragment) or "")
|
|
|
|
|
|
|
|
return "%s://%s%s%s%s%s" % (scheme, user, host, path, fragment, query)
|
|
|
|
|
|
|
|
|
|
|
|
class vmmMigrateDialog(gobject.GObject):
|
|
|
|
__gsignals__ = {
|
|
|
|
}
|
|
|
|
|
2009-11-18 12:51:36 -06:00
|
|
|
def __init__(self, config, vm, engine):
|
2010-11-30 13:40:49 -06:00
|
|
|
gobject.GObject.__init__(self)
|
2009-11-15 14:17:03 -06:00
|
|
|
self.config = config
|
|
|
|
self.vm = vm
|
|
|
|
self.conn = vm.connection
|
2009-11-18 12:51:36 -06:00
|
|
|
self.engine = engine
|
2009-11-15 14:17:03 -06:00
|
|
|
|
2010-11-30 13:33:21 -06:00
|
|
|
self.window = gtk.glade.XML(config.get_glade_dir() + \
|
|
|
|
"/vmm-migrate.glade",
|
|
|
|
"vmm-migrate", domain="virt-manager")
|
2009-11-15 14:17:03 -06:00
|
|
|
self.topwin = self.window.get_widget("vmm-migrate")
|
2010-11-30 13:33:21 -06:00
|
|
|
self.err = vmmErrorDialog(self.topwin)
|
2009-11-15 14:17:03 -06:00
|
|
|
self.topwin.hide()
|
|
|
|
|
2009-11-18 12:51:36 -06:00
|
|
|
self.destconn_rows = []
|
|
|
|
|
2009-11-15 14:17:03 -06:00
|
|
|
self.window.signal_autoconnect({
|
|
|
|
"on_vmm_migrate_delete_event" : self.close,
|
|
|
|
|
|
|
|
"on_migrate_cancel_clicked" : self.close,
|
|
|
|
"on_migrate_finish_clicked" : self.finish,
|
|
|
|
|
2009-11-18 12:51:36 -06:00
|
|
|
"on_migrate_dest_changed" : self.destconn_changed,
|
2009-11-15 14:17:03 -06:00
|
|
|
"on_migrate_set_rate_toggled" : self.toggle_set_rate,
|
|
|
|
"on_migrate_set_interface_toggled" : self.toggle_set_interface,
|
|
|
|
"on_migrate_set_port_toggled" : self.toggle_set_port,
|
2010-11-29 18:11:35 -06:00
|
|
|
"on_migrate_set_maxdowntime_toggled" : self.toggle_set_maxdowntime,
|
2009-11-15 14:17:03 -06:00
|
|
|
})
|
2009-12-14 15:45:55 -06:00
|
|
|
util.bind_escape_key_close(self)
|
2009-11-15 14:17:03 -06:00
|
|
|
|
|
|
|
blue = gtk.gdk.color_parse("#0072A8")
|
|
|
|
self.window.get_widget("migrate-header").modify_bg(gtk.STATE_NORMAL,
|
|
|
|
blue)
|
|
|
|
image = gtk.image_new_from_icon_name("vm_clone_wizard",
|
|
|
|
gtk.ICON_SIZE_DIALOG)
|
|
|
|
image.show()
|
|
|
|
self.window.get_widget("migrate-vm-icon-box").pack_end(image, False)
|
|
|
|
|
2009-11-18 12:51:36 -06:00
|
|
|
self.init_state()
|
|
|
|
|
2009-11-15 14:17:03 -06:00
|
|
|
def show(self):
|
|
|
|
self.reset_state()
|
|
|
|
self.topwin.show()
|
|
|
|
self.topwin.present()
|
|
|
|
|
|
|
|
def close(self, ignore1=None, ignore2=None):
|
|
|
|
self.topwin.hide()
|
|
|
|
return 1
|
|
|
|
|
2009-11-18 12:51:36 -06:00
|
|
|
def init_state(self):
|
|
|
|
# [hostname, conn, can_migrate, tooltip]
|
|
|
|
dest_model = gtk.ListStore(str, object, bool, str)
|
|
|
|
dest_combo = self.window.get_widget("migrate-dest")
|
|
|
|
dest_combo.set_model(dest_model)
|
|
|
|
text = gtk.CellRendererText()
|
|
|
|
dest_combo.pack_start(text, True)
|
|
|
|
dest_combo.add_attribute(text, 'text', 0)
|
|
|
|
dest_combo.add_attribute(text, 'sensitive', 2)
|
|
|
|
dest_model.set_sort_column_id(0, gtk.SORT_ASCENDING)
|
|
|
|
# XXX no way to set tooltips here, kind of annoying
|
|
|
|
|
|
|
|
# Hook up signals to get connection listing
|
|
|
|
self.engine.connect("connection-added", self.dest_add_connection)
|
|
|
|
self.engine.connect("connection-removed", self.dest_remove_connection)
|
|
|
|
self.destconn_changed(dest_combo)
|
2009-11-15 14:17:03 -06:00
|
|
|
|
2009-11-18 12:51:36 -06:00
|
|
|
def reset_state(self):
|
2009-11-15 14:17:03 -06:00
|
|
|
title_str = ("<span size='large' color='white'>%s '%s'</span>" %
|
|
|
|
(_("Migrate"), self.vm.get_name()))
|
|
|
|
self.window.get_widget("migrate-main-label").set_markup(title_str)
|
|
|
|
|
2009-12-14 15:48:35 -06:00
|
|
|
self.window.get_widget("migrate-cancel").grab_focus()
|
|
|
|
|
2009-11-15 14:17:03 -06:00
|
|
|
name = self.vm.get_name()
|
|
|
|
srchost = self.conn.get_hostname()
|
|
|
|
|
|
|
|
self.window.get_widget("migrate-label-name").set_text(name)
|
|
|
|
self.window.get_widget("migrate-label-src").set_text(srchost)
|
|
|
|
|
|
|
|
self.window.get_widget("migrate-advanced-expander").set_expanded(False)
|
|
|
|
self.window.get_widget("migrate-set-interface").set_active(False)
|
|
|
|
self.window.get_widget("migrate-set-rate").set_active(False)
|
|
|
|
self.window.get_widget("migrate-set-port").set_active(False)
|
2010-11-29 18:11:35 -06:00
|
|
|
self.window.get_widget("migrate-set-maxdowntime").set_active(False)
|
|
|
|
self.window.get_widget("migrate-max-downtime").set_value(30)
|
2009-11-15 14:17:03 -06:00
|
|
|
|
|
|
|
running = self.vm.is_active()
|
|
|
|
self.window.get_widget("migrate-offline").set_active(not running)
|
|
|
|
self.window.get_widget("migrate-offline").set_sensitive(running)
|
|
|
|
|
|
|
|
self.window.get_widget("migrate-rate").set_value(0)
|
|
|
|
self.window.get_widget("migrate-secure").set_active(False)
|
|
|
|
|
2010-11-30 09:52:11 -06:00
|
|
|
downtime_box = self.window.get_widget("migrate-maxdowntime-box")
|
|
|
|
support_downtime = self.vm.support_downtime()
|
|
|
|
downtime_tooltip = ""
|
|
|
|
if not support_downtime:
|
|
|
|
downtime_tooltip = _("Libvirt version does not support setting "
|
|
|
|
"downtime.")
|
|
|
|
downtime_box.set_sensitive(support_downtime)
|
|
|
|
util.tooltip_wrapper(downtime_box, downtime_tooltip)
|
|
|
|
|
2009-11-15 14:17:03 -06:00
|
|
|
if self.conn.is_xen():
|
|
|
|
# Default xen port is 8002
|
|
|
|
self.window.get_widget("migrate-port").set_value(8002)
|
|
|
|
else:
|
|
|
|
# QEMU migrate port range is 49152+64
|
|
|
|
self.window.get_widget("migrate-port").set_value(49152)
|
|
|
|
|
|
|
|
secure_box = self.window.get_widget("migrate-secure-box")
|
|
|
|
support_secure = hasattr(libvirt, "VIR_MIGRATE_TUNNELLED")
|
|
|
|
secure_tooltip = ""
|
|
|
|
if not support_secure:
|
|
|
|
secure_tooltip = _("Libvirt version does not support tunnelled "
|
|
|
|
"migration.")
|
|
|
|
|
|
|
|
secure_box.set_sensitive(support_secure)
|
|
|
|
util.tooltip_wrapper(secure_box, secure_tooltip)
|
|
|
|
|
2009-11-18 12:51:36 -06:00
|
|
|
self.rebuild_dest_rows()
|
|
|
|
|
|
|
|
def set_state(self, vm):
|
2009-11-15 14:17:03 -06:00
|
|
|
self.vm = vm
|
|
|
|
self.conn = vm.connection
|
|
|
|
self.reset_state()
|
|
|
|
|
2009-11-18 12:51:36 -06:00
|
|
|
def destconn_changed(self, src):
|
|
|
|
active = src.get_active()
|
|
|
|
tooltip = None
|
|
|
|
if active == -1:
|
|
|
|
tooltip = _("A valid destination connection must be selected.")
|
|
|
|
|
|
|
|
self.window.get_widget("migrate-finish").set_sensitive(active != -1)
|
|
|
|
util.tooltip_wrapper(self.window.get_widget("migrate-finish"), tooltip)
|
|
|
|
|
2009-11-15 14:17:03 -06:00
|
|
|
def toggle_set_rate(self, src):
|
|
|
|
enable = src.get_active()
|
|
|
|
self.window.get_widget("migrate-rate").set_sensitive(enable)
|
|
|
|
|
|
|
|
def toggle_set_interface(self, src):
|
|
|
|
enable = src.get_active()
|
|
|
|
port_enable = self.window.get_widget("migrate-set-port").get_active()
|
|
|
|
self.window.get_widget("migrate-interface").set_sensitive(enable)
|
|
|
|
self.window.get_widget("migrate-set-port").set_sensitive(enable)
|
|
|
|
self.window.get_widget("migrate-port").set_sensitive(enable and
|
|
|
|
port_enable)
|
|
|
|
|
2010-11-29 18:11:35 -06:00
|
|
|
def toggle_set_maxdowntime(self, src):
|
|
|
|
enable = src.get_active()
|
|
|
|
self.window.get_widget("migrate-max-downtime").set_sensitive(enable)
|
|
|
|
|
2009-11-15 14:17:03 -06:00
|
|
|
def toggle_set_port(self, src):
|
|
|
|
enable = src.get_active()
|
|
|
|
self.window.get_widget("migrate-port").set_sensitive(enable)
|
|
|
|
|
2009-11-18 12:51:36 -06:00
|
|
|
def get_config_destconn(self):
|
|
|
|
combo = self.window.get_widget("migrate-dest")
|
|
|
|
model = combo.get_model()
|
|
|
|
|
|
|
|
idx = combo.get_active()
|
|
|
|
if idx == -1:
|
|
|
|
return None
|
|
|
|
|
|
|
|
row = model[idx]
|
|
|
|
if not row[2]:
|
|
|
|
return None
|
|
|
|
|
|
|
|
return row[1]
|
|
|
|
|
2009-11-15 14:17:03 -06:00
|
|
|
def get_config_offline(self):
|
|
|
|
return self.window.get_widget("migrate-offline").get_active()
|
2010-11-29 18:11:35 -06:00
|
|
|
|
|
|
|
def get_config_max_downtime(self):
|
|
|
|
if not self.get_config_max_downtime_enabled():
|
|
|
|
return 0
|
|
|
|
return int(self.window.get_widget("migrate-max-downtime").get_value())
|
|
|
|
|
2009-11-15 14:17:03 -06:00
|
|
|
def get_config_secure(self):
|
|
|
|
return self.window.get_widget("migrate-secure").get_active()
|
|
|
|
|
2010-11-29 18:11:35 -06:00
|
|
|
def get_config_max_downtime_enabled(self):
|
|
|
|
return self.window.get_widget("migrate-max-downtime").get_property("sensitive")
|
|
|
|
|
2009-11-15 14:17:03 -06:00
|
|
|
def get_config_rate_enabled(self):
|
|
|
|
return self.window.get_widget("migrate-rate").get_property("sensitive")
|
|
|
|
def get_config_rate(self):
|
|
|
|
if not self.get_config_rate_enabled():
|
|
|
|
return 0
|
|
|
|
return int(self.window.get_widget("migrate-rate").get_value())
|
|
|
|
|
|
|
|
def get_config_interface_enabled(self):
|
|
|
|
return self.window.get_widget("migrate-interface").get_property("sensitive")
|
|
|
|
def get_config_interface(self):
|
|
|
|
if not self.get_config_interface_enabled():
|
|
|
|
return None
|
|
|
|
return self.window.get_widget("migrate-interface").get_text()
|
|
|
|
|
|
|
|
def get_config_port_enabled(self):
|
|
|
|
return self.window.get_widget("migrate-port").get_property("sensitive")
|
|
|
|
def get_config_port(self):
|
|
|
|
if not self.get_config_port_enabled():
|
|
|
|
return 0
|
|
|
|
return int(self.window.get_widget("migrate-port").get_value())
|
|
|
|
|
2009-11-18 12:51:36 -06:00
|
|
|
def build_localhost_uri(self, destconn):
|
|
|
|
desthost = destconn.get_qualified_hostname()
|
2009-11-15 14:17:03 -06:00
|
|
|
if desthost == "localhost":
|
2010-01-27 09:34:10 -06:00
|
|
|
# We couldn't find a host name for the destination machine
|
|
|
|
# that is accessible from the source machine.
|
|
|
|
# /etc/hosts is likely borked and the only hostname it will
|
|
|
|
# give us is localhost. Remember, the dest machine can actually
|
|
|
|
# be our local machine so we may not already know its hostname
|
2009-11-15 14:17:03 -06:00
|
|
|
raise RuntimeError(_("Could not determine remotely accessible "
|
|
|
|
"hostname for destination connection."))
|
|
|
|
|
2009-11-18 12:51:36 -06:00
|
|
|
desturi_tuple = virtinst.util.uri_split(destconn.get_uri())
|
2009-11-15 14:17:03 -06:00
|
|
|
|
|
|
|
# Replace dest hostname with src hostname
|
|
|
|
desturi_tuple = list(desturi_tuple)
|
|
|
|
desturi_tuple[2] = desthost
|
|
|
|
return uri_join(desturi_tuple)
|
|
|
|
|
2009-11-18 12:51:36 -06:00
|
|
|
def build_migrate_uri(self, destconn):
|
2009-11-15 14:17:03 -06:00
|
|
|
conn = self.conn
|
|
|
|
|
|
|
|
interface = self.get_config_interface()
|
|
|
|
port = self.get_config_port()
|
|
|
|
secure = self.get_config_secure()
|
|
|
|
|
|
|
|
if not interface:
|
|
|
|
if not secure:
|
|
|
|
return None
|
|
|
|
|
|
|
|
# For secure migration, we need to make sure we aren't migrating
|
|
|
|
# to the local connection, because libvirt will pull try to use
|
|
|
|
# 'qemu:///system' as the migrate URI which will deadlock
|
2009-11-18 12:51:36 -06:00
|
|
|
if destconn.is_local():
|
|
|
|
return self.build_localhost_uri(destconn)
|
2009-11-15 14:17:03 -06:00
|
|
|
|
|
|
|
uri = ""
|
|
|
|
if conn.is_xen():
|
|
|
|
uri = "xenmigr://%s" % interface
|
|
|
|
|
|
|
|
else:
|
|
|
|
uri = "tcp:%s" % interface
|
|
|
|
|
|
|
|
if port:
|
|
|
|
uri += ":%s" % port
|
|
|
|
|
|
|
|
return uri
|
|
|
|
|
2009-11-18 12:51:36 -06:00
|
|
|
def rebuild_dest_rows(self):
|
|
|
|
newrows = []
|
|
|
|
|
|
|
|
for row in self.destconn_rows:
|
|
|
|
newrows.append(self.build_dest_row(row[1]))
|
|
|
|
|
|
|
|
self.destconn_rows = newrows
|
|
|
|
self.populate_dest_combo()
|
|
|
|
|
|
|
|
def populate_dest_combo(self):
|
|
|
|
combo = self.window.get_widget("migrate-dest")
|
|
|
|
model = combo.get_model()
|
|
|
|
idx = combo.get_active()
|
|
|
|
idxconn = None
|
|
|
|
if idx != -1:
|
|
|
|
idxconn = model[idx][1]
|
|
|
|
|
|
|
|
rows = [[_("No connections available."), None, False, None]]
|
|
|
|
if self.destconn_rows:
|
|
|
|
rows = self.destconn_rows
|
|
|
|
|
|
|
|
model.clear()
|
|
|
|
for r in rows:
|
|
|
|
# Don't list the current connection
|
|
|
|
if r[1] == self.conn:
|
|
|
|
continue
|
|
|
|
model.append(r)
|
|
|
|
|
|
|
|
# Find old index
|
|
|
|
idx = -1
|
|
|
|
for i in range(len(model)):
|
|
|
|
row = model[i]
|
|
|
|
conn = row[1]
|
|
|
|
|
|
|
|
if idxconn:
|
|
|
|
if conn == idxconn and row[2]:
|
|
|
|
idx = i
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
if row[2]:
|
|
|
|
idx = i
|
|
|
|
break
|
|
|
|
|
|
|
|
combo.set_active(idx)
|
|
|
|
|
|
|
|
def dest_add_connection(self, engine_ignore, conn):
|
|
|
|
combo = self.window.get_widget("migrate-dest")
|
|
|
|
model = combo.get_model()
|
|
|
|
|
|
|
|
newrow = self.build_dest_row(conn)
|
|
|
|
|
|
|
|
# Make sure connection isn't already present
|
|
|
|
for row in model:
|
|
|
|
if row[1] and row[1].get_uri() == newrow[1].get_uri():
|
|
|
|
return
|
|
|
|
|
|
|
|
conn.connect("state-changed", self.destconn_state_changed)
|
|
|
|
self.destconn_rows.append(newrow)
|
|
|
|
self.populate_dest_combo()
|
|
|
|
|
|
|
|
def dest_remove_connection(self, engine_ignore, conn):
|
|
|
|
# Make sure connection isn't already present
|
|
|
|
for row in self.destconn_rows:
|
|
|
|
if row[1] and row[1].get_uri() == conn.get_uri():
|
|
|
|
self.destconn_rows.remove(row)
|
|
|
|
|
|
|
|
self.populate_dest_combo()
|
|
|
|
|
|
|
|
def destconn_state_changed(self, conn):
|
|
|
|
for row in self.destconn_rows:
|
|
|
|
if row[1] == conn:
|
|
|
|
self.destconn_rows.remove(row)
|
|
|
|
self.destconn_rows.append(self.build_dest_row(conn))
|
|
|
|
|
|
|
|
self.populate_dest_combo()
|
|
|
|
|
|
|
|
def build_dest_row(self, destconn):
|
|
|
|
driver = self.conn.get_driver()
|
|
|
|
origuri = self.conn.get_uri()
|
|
|
|
|
|
|
|
can_migrate = False
|
|
|
|
desc = destconn.get_pretty_desc_inactive()
|
|
|
|
reason = ""
|
|
|
|
desturi = destconn.get_uri()
|
|
|
|
|
|
|
|
if destconn.get_driver() != driver:
|
|
|
|
reason = _("Connection hypervisors do not match.")
|
|
|
|
elif destconn.get_state() == destconn.STATE_DISCONNECTED:
|
|
|
|
reason = _("Connection is disconnected.")
|
|
|
|
elif destconn.get_uri() == origuri:
|
|
|
|
# Same connection
|
|
|
|
pass
|
|
|
|
elif destconn.get_state() == destconn.STATE_ACTIVE:
|
|
|
|
# Assumably we can migrate to this connection
|
|
|
|
can_migrate = True
|
|
|
|
reason = desturi
|
|
|
|
|
|
|
|
return [desc, destconn, can_migrate, reason]
|
|
|
|
|
2009-11-15 14:17:03 -06:00
|
|
|
|
|
|
|
def validate(self):
|
|
|
|
interface = self.get_config_interface()
|
|
|
|
rate = self.get_config_rate()
|
|
|
|
port = self.get_config_port()
|
2010-11-29 18:11:35 -06:00
|
|
|
max_downtime = self.get_config_max_downtime()
|
|
|
|
|
|
|
|
if self.get_config_max_downtime_enabled() and max_downtime == 0:
|
|
|
|
return self.err.val_err(_("max downtime must be greater than 0."))
|
2009-11-15 14:17:03 -06:00
|
|
|
|
|
|
|
if self.get_config_interface_enabled() and interface == None:
|
|
|
|
return self.err.val_err(_("An interface must be specified."))
|
|
|
|
|
|
|
|
if self.get_config_rate_enabled() and rate == 0:
|
|
|
|
return self.err.val_err(_("Transfer rate must be greater than 0."))
|
|
|
|
|
|
|
|
if self.get_config_port_enabled() and port == 0:
|
|
|
|
return self.err.val_err(_("Port must be greater than 0."))
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
def finish(self, src):
|
|
|
|
try:
|
|
|
|
if not self.validate():
|
|
|
|
return
|
|
|
|
|
2009-11-18 12:51:36 -06:00
|
|
|
destconn = self.get_config_destconn()
|
2009-11-15 14:17:03 -06:00
|
|
|
srchost = self.vm.get_connection().get_hostname()
|
2009-11-18 12:51:36 -06:00
|
|
|
dsthost = destconn.get_qualified_hostname()
|
2010-11-29 18:11:35 -06:00
|
|
|
max_downtime = self.get_config_max_downtime()
|
2009-11-15 14:17:03 -06:00
|
|
|
live = not self.get_config_offline()
|
|
|
|
secure = self.get_config_secure()
|
2009-11-18 12:51:36 -06:00
|
|
|
uri = self.build_migrate_uri(destconn)
|
2009-11-15 14:17:03 -06:00
|
|
|
rate = self.get_config_rate()
|
|
|
|
if rate:
|
|
|
|
rate = int(rate)
|
|
|
|
except Exception, e:
|
|
|
|
details = "".join(traceback.format_exc())
|
|
|
|
self.err.show_err((_("Uncaught error validating input: %s") %
|
|
|
|
str(e)), details)
|
|
|
|
return
|
|
|
|
|
|
|
|
self.topwin.set_sensitive(False)
|
|
|
|
self.topwin.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
|
|
|
|
|
2010-12-08 11:52:33 -06:00
|
|
|
if self.vm.getjobinfo_supported:
|
|
|
|
_cancel_back = self.cancel_migration
|
|
|
|
_cancel_args = [self.vm]
|
|
|
|
else:
|
|
|
|
_cancel_back = None
|
|
|
|
_cancel_args = [None]
|
|
|
|
|
2009-11-15 14:17:03 -06:00
|
|
|
progWin = vmmAsyncJob(self.config, self._async_migrate,
|
2010-11-29 18:11:35 -06:00
|
|
|
[self.vm, destconn, uri, rate, live, secure,
|
|
|
|
max_downtime],
|
2009-11-15 14:17:03 -06:00
|
|
|
title=_("Migrating VM '%s'" % self.vm.get_name()),
|
|
|
|
text=(_("Migrating VM '%s' from %s to %s. "
|
|
|
|
"This may take awhile.") %
|
2010-12-08 11:52:33 -06:00
|
|
|
(self.vm.get_name(), srchost, dsthost)),
|
|
|
|
cancel_back=_cancel_back,
|
|
|
|
cancel_args=_cancel_args)
|
2009-11-15 14:17:03 -06:00
|
|
|
progWin.run()
|
|
|
|
error, details = progWin.get_error()
|
|
|
|
|
|
|
|
if error:
|
|
|
|
self.err.show_err(error, details)
|
|
|
|
|
|
|
|
self.topwin.set_sensitive(True)
|
|
|
|
self.topwin.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.TOP_LEFT_ARROW))
|
|
|
|
|
|
|
|
if error is None:
|
|
|
|
self.conn.tick(noStatsUpdate=True)
|
2009-11-18 12:51:36 -06:00
|
|
|
destconn.tick(noStatsUpdate=True)
|
2009-11-15 14:17:03 -06:00
|
|
|
self.close()
|
|
|
|
|
2010-11-29 18:11:35 -06:00
|
|
|
def _async_set_max_downtime(self, vm, max_downtime, migrate_thread):
|
|
|
|
if not migrate_thread.isAlive():
|
|
|
|
return False
|
|
|
|
try:
|
|
|
|
vm.migrate_set_max_downtime(max_downtime, 0)
|
|
|
|
return False
|
|
|
|
except libvirt.libvirtError, e:
|
|
|
|
if (isinstance(e, libvirt.libvirtError) and
|
|
|
|
e.get_error_code() == libvirt.VIR_ERR_OPERATION_INVALID):
|
|
|
|
# migration has not been started, wait 100 milliseconds
|
|
|
|
return True
|
|
|
|
|
|
|
|
logging.warning("Error setting migrate downtime: %s" % e)
|
|
|
|
return False
|
|
|
|
|
2010-12-08 11:52:33 -06:00
|
|
|
def cancel_migration(self, vm, asyncjob):
|
2010-12-08 13:14:38 -06:00
|
|
|
logging.debug("Cancelling migrate job")
|
2010-12-08 11:52:33 -06:00
|
|
|
if not vm:
|
|
|
|
return
|
|
|
|
|
|
|
|
try:
|
|
|
|
vm.abort_job()
|
|
|
|
except Exception, e:
|
2010-12-08 13:14:38 -06:00
|
|
|
logging.exception("Error cancelling migrate job")
|
|
|
|
asyncjob.show_warning(_("Error cancelling migrate job: %s") % e)
|
2010-12-08 11:52:33 -06:00
|
|
|
return
|
|
|
|
|
|
|
|
asyncjob.job_canceled = True
|
|
|
|
return
|
|
|
|
|
2009-11-15 14:17:03 -06:00
|
|
|
def _async_migrate(self, origvm, origdconn, migrate_uri, rate, live,
|
2010-11-29 18:11:35 -06:00
|
|
|
secure, max_downtime, asyncjob):
|
2009-11-15 14:17:03 -06:00
|
|
|
errinfo = None
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
ignore = vmmCreateMeter(asyncjob)
|
|
|
|
|
|
|
|
srcconn = util.dup_conn(self.config, origvm.get_connection(),
|
|
|
|
return_conn_class=True)
|
|
|
|
dstconn = util.dup_conn(self.config, origdconn,
|
|
|
|
return_conn_class=True)
|
|
|
|
|
|
|
|
vminst = srcconn.vmm.lookupByName(origvm.get_name())
|
|
|
|
vm = vmmDomain(self.config, srcconn, vminst, vminst.UUID())
|
|
|
|
|
|
|
|
logging.debug("Migrating vm=%s from %s to %s", vm.get_name(),
|
|
|
|
srcconn.get_uri(), dstconn.get_uri())
|
2010-11-29 18:11:35 -06:00
|
|
|
timer = None
|
2010-11-30 09:52:11 -06:00
|
|
|
if max_downtime != 0:
|
2010-11-29 18:11:35 -06:00
|
|
|
# 0 means that the spin box migrate-max-downtime does not
|
|
|
|
# be enabled.
|
|
|
|
current_thread = threading.currentThread()
|
|
|
|
timer = util.safe_timeout_add(100,
|
|
|
|
self._async_set_max_downtime,
|
|
|
|
vm, max_downtime,
|
|
|
|
current_thread)
|
2009-11-15 14:17:03 -06:00
|
|
|
vm.migrate(dstconn, migrate_uri, rate, live, secure)
|
2010-11-29 18:11:35 -06:00
|
|
|
if timer:
|
|
|
|
gobject.source_remove(timer)
|
2009-11-15 14:17:03 -06:00
|
|
|
except Exception, e:
|
2010-12-08 11:52:33 -06:00
|
|
|
if not (isinstance(e, libvirt.libvirtError) and
|
|
|
|
asyncjob.job_canceled):
|
|
|
|
# If job is cancelled, we should not report the error
|
|
|
|
# to user.
|
|
|
|
errinfo = (str(e), ("Unable to migrate guest:\n %s" %
|
|
|
|
"".join(traceback.format_exc())))
|
2009-11-15 14:17:03 -06:00
|
|
|
finally:
|
|
|
|
if errinfo:
|
|
|
|
asyncjob.set_error(errinfo[0], errinfo[1])
|
|
|
|
|
|
|
|
|
|
|
|
gobject.type_register(vmmMigrateDialog)
|