From febecf4089a9181beb1126f815391dd47d343db0 Mon Sep 17 00:00:00 2001 From: Wen Congyang <wency@cn.fujitsu.com> Date: Mon, 29 Nov 2010 19:11:35 -0500 Subject: [PATCH] Add virDomainSetMigrateMaxDowntime support --- AUTHORS | 1 + src/virtManager/domain.py | 9 ++++ src/virtManager/migrate.py | 57 ++++++++++++++++++++++- src/vmm-migrate.glade | 95 +++++++++++++++++++++++++++++++++++++- 4 files changed, 159 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 47b3c762..663267ab 100644 --- a/AUTHORS +++ b/AUTHORS @@ -67,6 +67,7 @@ Further patches have been submitted by: Marc Deslauriers <marc.deslauriers-at-ubuntu-dot-com> Matthias Fulz <olz1983-at-googlemail-dot-com> Niels de Vos <ndevos-at-redhat-dot-com> + Wen Congyang <wency-at-cn-dot-fujitsu-dot-com> <...send a patch & get your name here...> diff --git a/src/virtManager/domain.py b/src/virtManager/domain.py index a886c1af..3bf75730 100644 --- a/src/virtManager/domain.py +++ b/src/virtManager/domain.py @@ -857,6 +857,12 @@ class vmmDomain(vmmDomainBase): def _XMLDesc(self, flags): return self._backend.XMLDesc(flags) + def support_downtime(self): + # Note: this function has side effect + # if domain supports downtime, the downtime may be overriden to 30ms + return support.check_domain_support(self._backend, + support.SUPPORT_DOMAIN_MIGRATE_DOWNTIME) + def get_info(self): return self._backend.info() @@ -997,6 +1003,9 @@ class vmmDomain(vmmDomainBase): if self.get_autostart() != val: self._backend.setAutostart(val) + def migrate_set_max_downtime(self, max_downtime, flag=0): + self._backend.migrateSetMaxDowntime(max_downtime, flag) + def migrate(self, destconn, interface=None, rate=0, live=False, secure=False): newname = None diff --git a/src/virtManager/migrate.py b/src/virtManager/migrate.py index 3c991252..7b4a5d0a 100644 --- a/src/virtManager/migrate.py +++ b/src/virtManager/migrate.py @@ -23,6 +23,7 @@ import gtk.glade import traceback import logging +import threading import virtinst import libvirt @@ -78,6 +79,7 @@ class vmmMigrateDialog(gobject.GObject): "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, + "on_migrate_set_maxdowntime_toggled" : self.toggle_set_maxdowntime, }) util.bind_escape_key_close(self) @@ -134,6 +136,8 @@ class vmmMigrateDialog(gobject.GObject): 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) + self.window.get_widget("migrate-set-maxdowntime").set_active(False) + self.window.get_widget("migrate-max-downtime").set_value(30) running = self.vm.is_active() self.window.get_widget("migrate-offline").set_active(not running) @@ -187,6 +191,10 @@ class vmmMigrateDialog(gobject.GObject): self.window.get_widget("migrate-port").set_sensitive(enable and port_enable) + def toggle_set_maxdowntime(self, src): + enable = src.get_active() + self.window.get_widget("migrate-max-downtime").set_sensitive(enable) + def toggle_set_port(self, src): enable = src.get_active() self.window.get_widget("migrate-port").set_sensitive(enable) @@ -207,9 +215,18 @@ class vmmMigrateDialog(gobject.GObject): def get_config_offline(self): return self.window.get_widget("migrate-offline").get_active() + + 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()) + def get_config_secure(self): return self.window.get_widget("migrate-secure").get_active() + def get_config_max_downtime_enabled(self): + return self.window.get_widget("migrate-max-downtime").get_property("sensitive") + def get_config_rate_enabled(self): return self.window.get_widget("migrate-rate").get_property("sensitive") def get_config_rate(self): @@ -382,6 +399,10 @@ class vmmMigrateDialog(gobject.GObject): interface = self.get_config_interface() rate = self.get_config_rate() port = self.get_config_port() + 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.")) if self.get_config_interface_enabled() and interface == None: return self.err.val_err(_("An interface must be specified.")) @@ -402,6 +423,7 @@ class vmmMigrateDialog(gobject.GObject): destconn = self.get_config_destconn() srchost = self.vm.get_connection().get_hostname() dsthost = destconn.get_qualified_hostname() + max_downtime = self.get_config_max_downtime() live = not self.get_config_offline() secure = self.get_config_secure() uri = self.build_migrate_uri(destconn) @@ -418,7 +440,8 @@ class vmmMigrateDialog(gobject.GObject): self.topwin.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH)) progWin = vmmAsyncJob(self.config, self._async_migrate, - [self.vm, destconn, uri, rate, live, secure], + [self.vm, destconn, uri, rate, live, secure, + max_downtime], title=_("Migrating VM '%s'" % self.vm.get_name()), text=(_("Migrating VM '%s' from %s to %s. " "This may take awhile.") % @@ -437,8 +460,23 @@ class vmmMigrateDialog(gobject.GObject): destconn.tick(noStatsUpdate=True) self.close() + 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 + def _async_migrate(self, origvm, origdconn, migrate_uri, rate, live, - secure, asyncjob): + secure, max_downtime, asyncjob): errinfo = None try: try: @@ -454,7 +492,22 @@ class vmmMigrateDialog(gobject.GObject): logging.debug("Migrating vm=%s from %s to %s", vm.get_name(), srcconn.get_uri(), dstconn.get_uri()) + timer = None + if max_downtime != 0 and vm.support_downtime(): + # 0 means that the spin box migrate-max-downtime does not + # be enabled. + # + # We should check whether the domain supports downtime + # early, but vm.support_downtime() has side effect, so + # we check it only when user needs to modify downtime... + current_thread = threading.currentThread() + timer = util.safe_timeout_add(100, + self._async_set_max_downtime, + vm, max_downtime, + current_thread) vm.migrate(dstconn, migrate_uri, rate, live, secure) + if timer: + gobject.source_remove(timer) except Exception, e: errinfo = (str(e), ("Unable to migrate guest:\n %s" % "".join(traceback.format_exc()))) diff --git a/src/vmm-migrate.glade b/src/vmm-migrate.glade index 4db5073f..94c0f9f4 100644 --- a/src/vmm-migrate.glade +++ b/src/vmm-migrate.glade @@ -245,6 +245,99 @@ <property name="position">0</property> </packing> </child> + <child> + <widget class="GtkHBox" id="migrate-maxdowntime-box"> + <property name="visible">True</property> + <property name="spacing">3</property> + <child> + <widget class="GtkLabel" id="label15"> + <property name="visible">True</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Max downtime:</property> + <property name="use_markup">True</property> + <property name="use_underline">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkCheckButton" id="migrate-set-maxdowntime"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_migrate_set_maxdowntime_toggled"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkHBox" id="hbox8"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <widget class="GtkHBox" id="hbox9"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <widget class="GtkSpinButton" id="migrate-max-downtime"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + <property name="adjustment">30 0 1000000 1 1000 0</property> + <property name="snap_to_ticks">True</property> + <property name="numeric">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label16"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">ms</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkAlignment" id="alignment6"> + <property name="visible">True</property> + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">2</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> <child> <widget class="GtkFrame" id="frame1"> <property name="visible">True</property> @@ -471,7 +564,7 @@ </widget> <packing> <property name="expand">False</property> - <property name="position">1</property> + <property name="position">2</property> </packing> </child> </widget>