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">&#x25CF;</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>