[virt-tools-list] [RFC Patch v3] Support job cancellation for migration, save functions

Wen Congyang wency at cn.fujitsu.com
Wed Dec 8 03:41:50 UTC 2010


# HG changeset patch
# User Wen Congyang <wency at cn.fujitsu.com>
# Date 1291777487 -28800
# Node ID 91a8075863a66d09eb183c98aa3d57a750ffcee0
# Parent  eafa58f4f95042a6ddf8ca96bc2aefb9bebe88ec
Support job cancellation for migration, save functions

diff -r eafa58f4f950 -r 91a8075863a6 src/virtManager/asyncjob.py
--- a/src/virtManager/asyncjob.py	Mon Dec 06 21:38:05 2010 -0500
+++ b/src/virtManager/asyncjob.py	Wed Dec 08 11:04:47 2010 +0800
@@ -24,6 +24,7 @@
 import gobject
 
 from virtManager import util
+from virtManager.error import vmmErrorDialog
 
 # This thin wrapper only exists so we can put debugging
 # code in the run() method every now & then
@@ -40,7 +41,7 @@
     def __init__(self, config, callback, args=None,
                  text=_("Please wait a few moments..."),
                  title=_("Operation in progress"),
-                 run_main=True):
+                 run_main=True, cancel_back=None, cancel_args=[]):
         gobject.GObject.__init__(self)
         self.config = config
         self.run_main = bool(run_main)
@@ -48,6 +49,20 @@
         self.window = gtk.glade.XML(config.get_glade_dir() + \
                                     "/vmm-progress.glade",
                                     "vmm-progress", domain="virt-manager")
+        self.topwin = self.window.get_widget("vmm-progress")
+        self.err = vmmErrorDialog(self.topwin)
+        self.window.signal_autoconnect({
+            "on_async_job_delete_event" : self.delete,
+            "on_async_job_cancel_clicked" : self.cancel,
+        })
+        self.cancel_job = cancel_back
+        self.cancel_args = cancel_args
+        self.cancel_args.append(self)
+        if self.cancel_job:
+            self.window.get_widget("cancel-async-job").show()
+        else:
+            self.window.get_widget("cancel-async-job").hide()
+        self.job_canceled = False
         self.window.get_widget("pbar-text").set_text(text)
 
         self.topwin = self.window.get_widget("vmm-progress")
@@ -89,6 +104,26 @@
 
         self.topwin.destroy()
 
+    def delete(self, ignore1=None, ignore2=None):
+        thread_active = (self.bg_thread.isAlive() or not self.run_main)
+        if self.cancel_job and thread_active:
+            res = self.err.warn_chkbox(
+                    text1=_("Cancel the job before closing window?"),
+                    buttons=gtk.BUTTONS_YES_NO)
+            if res:
+                # The job may end after we click 'Yes', so check whether the
+                # thread is active again
+                thread_active = (self.bg_thread.isAlive() or not self.run_main)
+                if thread_active:
+                    self.cancel()
+
+    def show_warning(self, summary):
+        self.err.ok(summary)
+
+    def cancel(self, ignore1=None, ignore2=None):
+        if self.cancel_job:
+            self.cancel_job(*self.cancel_args)
+
     def pulse_pbar(self, progress="", stage=None):
         gtk.gdk.threads_enter()
         try:
diff -r eafa58f4f950 -r 91a8075863a6 src/virtManager/domain.py
--- a/src/virtManager/domain.py	Mon Dec 06 21:38:05 2010 -0500
+++ b/src/virtManager/domain.py	Wed Dec 08 11:04:47 2010 +0800
@@ -828,6 +828,8 @@
         self.getvcpus_supported = support.check_domain_support(self._backend,
                                             support.SUPPORT_DOMAIN_GETVCPUS)
         self.managedsave_supported = self.connection.get_dom_managedsave_supported(self._backend)
+        self.getjobinfo_supported = support.check_domain_support(self._backend,
+                                            support.SUPPORT_DOMAIN_JOB_INFO)
 
         self.toggle_sample_network_traffic()
         self.toggle_sample_disk_io()
@@ -1001,6 +1003,9 @@
         if self.get_autostart() != val:
             self._backend.setAutostart(val)
 
+    def abort_job(self):
+        self._backend.abortJob()
+
     def migrate_set_max_downtime(self, max_downtime, flag=0):
         self._backend.migrateSetMaxDowntime(max_downtime, flag)
 
diff -r eafa58f4f950 -r 91a8075863a6 src/virtManager/engine.py
--- a/src/virtManager/engine.py	Mon Dec 06 21:38:05 2010 -0500
+++ b/src/virtManager/engine.py	Wed Dec 08 11:04:47 2010 +0800
@@ -825,15 +825,37 @@
             if not path:
                 return
 
+        if vm.getjobinfo_supported:
+            _cancel_back=self._save_cancel
+            _cancel_args=[vm]
+        else:
+            _cancel_back=None
+            _cancel_args=[None]
+
         progWin = vmmAsyncJob(self.config, self._save_callback,
                               [vm, path],
-                              _("Saving Virtual Machine"))
+                              _("Saving Virtual Machine"),
+                              cancel_back=_cancel_back,
+                              cancel_args=_cancel_args)
         progWin.run()
         error, details = progWin.get_error()
 
         if error is not None:
             src.err.show_err(_("Error saving domain: %s") % error, details)
 
+    def _save_cancel(self, vm, asyncjob):
+        if not vm:
+            return
+
+        try:
+            vm.abort_job()
+        except Exception, e:
+            asyncjob.show_warning(str(e))
+            return
+
+        asyncjob.job_canceled = True
+        return
+
     def _save_callback(self, vm, file_to_save, asyncjob):
         try:
             conn = util.dup_conn(self.config, vm.connection,
@@ -842,7 +864,11 @@
 
             newvm.save(file_to_save)
         except Exception, e:
-            asyncjob.set_error(str(e), "".join(traceback.format_exc()))
+            if not (isinstance(e, libvirt.libvirtError) and
+                    asyncjob.job_canceled):
+                # If job is cancelled, we should not report the error
+                # to user.
+                asyncjob.set_error(str(e), "".join(traceback.format_exc()))
 
     def _do_restore_domain(self, src, uri):
         conn = self._lookup_connection(uri)
diff -r eafa58f4f950 -r 91a8075863a6 src/virtManager/migrate.py
--- a/src/virtManager/migrate.py	Mon Dec 06 21:38:05 2010 -0500
+++ b/src/virtManager/migrate.py	Wed Dec 08 11:04:47 2010 +0800
@@ -445,13 +445,22 @@
         self.topwin.set_sensitive(False)
         self.topwin.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
 
+        if self.vm.getjobinfo_supported:
+            _cancel_back=self.cancel_migration
+            _cancel_args=[self.vm]
+        else:
+            _cancel_back=None
+            _cancel_args=[None]
+
         progWin = vmmAsyncJob(self.config, self._async_migrate,
                               [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.") %
-                                      (self.vm.get_name(), srchost, dsthost)))
+                                      (self.vm.get_name(), srchost, dsthost)),
+                              cancel_back=_cancel_back,
+                              cancel_args=_cancel_args)
         progWin.run()
         error, details = progWin.get_error()
 
@@ -481,6 +490,19 @@
             logging.warning("Error setting migrate downtime: %s" % e)
             return False
 
+    def cancel_migration(self, vm, asyncjob):
+        if not vm:
+            return
+
+        try:
+            vm.abort_job()
+        except Exception, e:
+            asyncjob.show_warning(str(e))
+            return
+
+        asyncjob.job_canceled = True
+        return
+
     def _async_migrate(self, origvm, origdconn, migrate_uri, rate, live,
                        secure, max_downtime, asyncjob):
         errinfo = None
@@ -511,8 +533,12 @@
                 if timer:
                     gobject.source_remove(timer)
             except Exception, e:
-                errinfo = (str(e), ("Unable to migrate guest:\n %s" %
-                                    "".join(traceback.format_exc())))
+                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())))
         finally:
             if errinfo:
                 asyncjob.set_error(errinfo[0], errinfo[1])
diff -r eafa58f4f950 -r 91a8075863a6 src/vmm-progress.glade
--- a/src/vmm-progress.glade	Mon Dec 06 21:38:05 2010 -0500
+++ b/src/vmm-progress.glade	Wed Dec 08 11:04:47 2010 +0800
@@ -12,6 +12,7 @@
     <property name="type_hint">dialog</property>
     <property name="skip_taskbar_hint">True</property>
     <property name="urgency_hint">True</property>
+    <signal name="delete_event" handler="on_async_job_delete_event"/>
     <child>
       <widget class="GtkAlignment" id="alignment134">
         <property name="visible">True</property>
@@ -82,6 +83,40 @@
                 <property name="position">2</property>
               </packing>
             </child>
+            <child>
+              <widget class="GtkHBox" id="hbox1">
+                <property name="visible">True</property>
+                <property name="spacing">12</property>
+                <child>
+                  <widget class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                  </widget>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <widget class="GtkButton" id="cancel-async-job">
+                    <property name="label">gtk-cancel</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="use_stock">True</property>
+                    <signal name="clicked" handler="on_async_job_cancel_clicked"/>
+                  </widget>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
           </widget>
         </child>
       </widget>




More information about the virt-tools-list mailing list