From: Paulo Vital <pvital(a)linux.vnet.ibm.com>
This patch gives to user a way to 'stop' a Task that is still running by setting
the Task status to "killed".
Since an AsyncTask is basic a thread running in the system and this thread can
execute a pure Python method or a background command (by using run_command()
from wok.utils), the developer must pass to AsyncTask constructor a method to be
executed by the DELETE operation, called here as 'kill_cb'. If none kill_cb is
passed, the task will not be able to stopped and an error message will be raised
to user if DELETE operation is executed. Otherwise, the kill_cb method will be
executed by kill() method (responsible to execute the DELETE operation) of
AsyncTask class and its status set to 'killed'.
Signed-off-by: Paulo Vital <pvital(a)linux.vnet.ibm.com>
---
docs/API/tasks.md | 2 ++
src/wok/asynctask.py | 18 +++++++++++++++++-
src/wok/i18n.py | 2 ++
src/wok/model/tasks.py | 13 +++++++++++++
4 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/docs/API/tasks.md b/docs/API/tasks.md
index e0c404f..2068f29 100644
--- a/docs/API/tasks.md
+++ b/docs/API/tasks.md
@@ -27,8 +27,10 @@ server.
* running: The task is running
* finished: The task has finished successfully
* failed: The task failed
+ * killed: The task was killed by user
* message: Human-readable details about the Task status
* target_uri: Resource URI related to the Task
+* **DELETE**: Kill the Task, moving its status to 'killed'
* **POST**: *See Task Actions*
**Actions (POST):**
diff --git a/src/wok/asynctask.py b/src/wok/asynctask.py
index b98ad9a..021b76c 100644
--- a/src/wok/asynctask.py
+++ b/src/wok/asynctask.py
@@ -26,6 +26,9 @@ import traceback
import uuid
+from wok.exception import InvalidOperation, OperationFailed
+
+
tasks_queue = {}
@@ -41,10 +44,11 @@ def clean_old_tasks():
class AsyncTask(object):
- def __init__(self, target_uri, fn, opaque=None):
+ def __init__(self, target_uri, fn, opaque=None, kill_cb=None):
self.id = str(uuid.uuid1())
self.target_uri = target_uri
self.fn = fn
+ self.kill_cb = kill_cb
self.status = 'running'
self.message = 'The request is being processing.'
self.timestamp = time.time()
@@ -79,3 +83,15 @@ class AsyncTask(object):
except KeyError:
msg = "There's no task_id %s in tasks_queue. Nothing changed."
cherrypy.log.error_log.error(msg % self.id)
+
+ def kill(self):
+ if self.kill_cb is None:
+ raise InvalidOperation('WOKASYNC0002E')
+
+ try:
+ self.kill_cb()
+ self.status = 'killed'
+ self.message = 'Task killed by user.'
+ except Exception, e:
+ self.message = e.message
+ raise OperationFailed('WOKASYNC0004E', {'err': e.message})
diff --git a/src/wok/i18n.py b/src/wok/i18n.py
index ade2ae9..7ba7c24 100644
--- a/src/wok/i18n.py
+++ b/src/wok/i18n.py
@@ -34,7 +34,9 @@ messages = {
"WOKAPI0009E": _("You don't have permission to perform this
operation."),
"WOKASYNC0001E": _("Unable to find task id: %(id)s"),
+ "WOKASYNC0002E": _("There is no callback to execute the kill task
process."),
"WOKASYNC0003E": _("Timeout of %(seconds)s seconds expired while
running task '%(task)s."),
+ "WOKASYNC0004E": _("Unable to kill task due error: %(err)s"),
"WOKAUTH0001E": _("Authentication failed for user
'%(username)s'. [Error code: %(code)s]"),
"WOKAUTH0002E": _("You are not authorized to access Kimchi"),
diff --git a/src/wok/model/tasks.py b/src/wok/model/tasks.py
index 6a2b4bf..8ae1ce4 100644
--- a/src/wok/model/tasks.py
+++ b/src/wok/model/tasks.py
@@ -68,3 +68,16 @@ class TaskModel(object):
raise TimeoutExpired('WOKASYNC0003E', {'seconds': timeout,
'task': task.target_uri})
+
+ def delete(self, id):
+ """
+ 'Stops' an AsyncTask, by executing the kill callback provided by user
+ when created the task. Task's status will be changed to 'killed'.
+ """
+ try:
+ task = tasks_queue[id]
+ except KeyError:
+ raise NotFoundError("WOKASYNC0001E", {'id': id})
+
+ if task.status is 'running':
+ task.kill()
--
2.7.4