Permalink
Browse files

Emit a Completed signal when the task has Succeeded/Failed/Cancelled

The client must call destroy() on the task otherwise it will leak.
The client can call destroy() early, in which case the task will be
cancelled and the object destroyed after that.

If an operation is in progress, there will always be a visible task
with a 'cancel' method.

Clients should call 'getResults' to query the state of the task before
calling 'destroy'

Signed-off-by: David Scott <dave.scott@eu.citrix.com>
  • Loading branch information...
1 parent 4ea6c55 commit 6cfe9f616052961289d3653646bdafba9fe809f7 David Scott committed Feb 11, 2014
Showing with 45 additions and 18 deletions.
  1. +32 −16 dbus/vm/python/dbus-resource-script.py
  2. +1 −0 dbus/vm/python/foo
  3. +12 −2 dbus/vm/task.xml
View
48 dbus/vm/python/dbus-resource-script.py
@@ -98,9 +98,7 @@ def run(self):
self.process.wait()
except:
pass
- info("%s: cancelling: deleting object" % self.task.path)
- self.task.remove_from_connection()
-
+ info("%s: cancelling: process has terminated" % self.task.path)
class Task(dbus.service.Object, threading.Thread):
def __init__(self, cmd):
@@ -116,6 +114,8 @@ def __init__(self, cmd):
self.completed = False
self.canceller = None
self.result = None
+ self.returncode = None
+ self.auto_destroy = False
try:
self.process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
@@ -130,43 +130,59 @@ def __init__(self, cmd):
info("%s: running %s" % (self.path, " ".join(cmd)))
self.start()
+ @dbus.service.signal(dbus_interface=TASK_INTERFACE)
+ def Completed(self):
+ # receivers need to use the API to fetch the results, we don't
+ # expect dbus to cache the output (which could be quite large)
+ pass
+
def run(self):
- stdout, stderr = self.process.communicate()
+ try:
+ stdout, stderr = self.process.communicate()
+ self.result = stdout
+ self.returncode = self.process.returncode
+ if self.returncode == 0:
+ info("%s: completed with %d: %s" % (self.path, self.returncode, self.result))
+ else:
+ error("%s: failed with %d: %s" % (self.path, self.returncode, self.result))
+ except Exception, e:
+ info("%s: failed with %s" % (self.path, str(e)))
self.completed = True
- self.result = stdout
- info("%s: completed with returncode %d" % (self.path, self.process.returncode))
+ self.Completed()
+ if self.auto_destroy:
+ self.remove_from_connection()
@dbus.service.method(dbus_interface=TASK_INTERFACE)
def cancel(self):
if self.canceller == None:
self.canceller = Canceller(self.process, self)
else:
info("%s: cancel: already cancelling, ignoring request" % self.path)
- # the canceller will terminate the subprocess and
- # remove this object from the bus
@dbus.service.method(dbus_interface=TASK_INTERFACE)
def destroy(self):
- if self.canceller <> None:
- info("%s: destroy: task is being cancelled already" % self.path)
- # canceller will clean up
- return
+ self.auto_destroy = True
if self.completed:
info("%s: destroy: task complete, removing object" % self.path)
self.remove_from_connection()
return
- info("%s: destroy: task is running, requesting cancel" % self.path)
+ info("%s: destroy: task is running, requesting cancel with auto-destroy" % self.path)
self.cancel()
@dbus.service.method(dbus_interface=TASK_INTERFACE)
def getResult(self):
- if self.result:
- return self.result
- else:
+ if not self.completed:
error("%s: TaskNotFinished" % self.path)
raise dbus.exceptions.DBusException(
'org.xenserver.api.TaskNotFinished',
'The task is still running.')
+ if self.returncode == 0:
+ return self.result
+ else:
+ error("%s: TaskAborted" % self.path)
+ raise dbus.exceptions.DBusException(
+ 'org.xenserver.api.TaskAborted',
+ 'The task failed and has been rolled-back.')
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v')
def Get(self, interface_name, property_name):
View
1 dbus/vm/python/foo
@@ -3,6 +3,7 @@
case "$1" in
attach)
echo "attached $2"
+ sleep 30s
;;
detach)
echo "detached $2"
View
14 dbus/vm/task.xml
@@ -1,4 +1,7 @@
-<node>
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
<interface name="org.xenserver.api.task">
<method name="cancel">
</method>
@@ -11,7 +14,7 @@
<doc:doc>
<doc:description>
<doc:para>
- True if the task has completed and the result is available.
+ True if the task has completed and the result or error is available.
</doc:para>
</doc:description>
</doc:doc>
@@ -25,5 +28,12 @@
</doc:description>
</doc:doc>
</property>
+ <signal name="Completed">
+ <doc:doc>
+ <doc:description>
+ <doc:para>Emitted when the task has completed and success/failure information is available.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </signal>
</interface>
</node>

0 comments on commit 6cfe9f6

Please sign in to comment.