Sometimes we need to wait for a task to finish. Instead of writing the
waiting loop everytime, we can now use the function "wait".
Add the function "TaskModel.wait" which waits for a task to finish
for a maximum ammount of time (in seconds). If the task finishes before
the timeout expires, the function returns successfully; otherwise, an
exception is raised.
Also, update calls to an already existing function which does the same thing
in the test code, and replace its usages with this new function.
Signed-off-by: Crístian Viana <vianac(a)linux.vnet.ibm.com>
---
src/kimchi/i18n.py | 1 +
src/kimchi/model/tasks.py | 28 ++++++++++++++++++++++++++++
tests/test_model.py | 19 +++++++++----------
3 files changed, 38 insertions(+), 10 deletions(-)
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
index f789259..10408bf 100644
--- a/src/kimchi/i18n.py
+++ b/src/kimchi/i18n.py
@@ -35,6 +35,7 @@ messages = {
"KCHASYNC0001E": _("Datastore is not initiated in the model
object."),
"KCHASYNC0002E": _("Unable to start task due error: %(err)s"),
+ "KCHASYNC0003E": _("Timeout of %(seconds)s seconds expired while
running task '%(task)s."),
"KCHAUTH0001E": _("Authentication failed for user
'%(username)s'. [Error code: %(code)s]"),
"KCHAUTH0002E": _("You are not authorized to access Kimchi"),
diff --git a/src/kimchi/model/tasks.py b/src/kimchi/model/tasks.py
index f25bcbf..61bc2f3 100644
--- a/src/kimchi/model/tasks.py
+++ b/src/kimchi/model/tasks.py
@@ -18,6 +18,11 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+import time
+
+from kimchi.exception import TimeoutExpired
+
+
class TasksModel(object):
def __init__(self, **kargs):
self.objstore = kargs['objstore']
@@ -34,3 +39,26 @@ class TaskModel(object):
def lookup(self, id):
with self.objstore as session:
return session.get('task', str(id))
+
+ def wait(self, id, timeout=10):
+ """Wait for a task until it stops running (successfully or due to
+ an error). If the Task finishes its execution before <timeout>, this
+ function returns normally; otherwise an exception is raised.
+
+ Parameters:
+ id -- The Task ID.
+ timeout -- The maximum time, in seconds, that this function should wait
+ for the Task. If the Task runs for more than <timeout>,
+ "TimeoutExpired" is raised.
+ """
+ for i in range(0, timeout):
+ with self.objstore as session:
+ task = session.get('task', str(id))
+
+ if task['status'] != 'running':
+ return
+
+ time.sleep(1)
+
+ raise TimeoutExpired('KCHASYNC0003E', {'seconds': timeout,
+ 'task':
task['target_uri']})
diff --git a/tests/test_model.py b/tests/test_model.py
index 3177fd4..d8c904e 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -45,7 +45,6 @@ from kimchi.iscsi import TargetClient
from kimchi.model import model
from kimchi.rollbackcontext import RollbackContext
from kimchi.utils import add_task
-from utils import wait_task
invalid_repository_urls = ['www.fedora.org', # missing protocol
@@ -130,7 +129,7 @@ class ModelTests(unittest.TestCase):
'format': 'qcow2'}
task_id = inst.storagevolumes_create('default',
params)['id']
rollback.prependDefer(inst.storagevolume_delete, 'default', vol)
- wait_task(inst.task_lookup, task_id)
+ inst.task_wait(task_id)
self.assertEquals('finished',
inst.task_lookup(task_id)['status'])
vol_path = inst.storagevolume_lookup('default', vol)['path']
@@ -291,7 +290,7 @@ class ModelTests(unittest.TestCase):
'format': 'qcow2'}
task_id = inst.storagevolumes_create(pool, params)['id']
rollback.prependDefer(inst.storagevolume_delete, pool, vol)
- wait_task(inst.task_lookup, task_id)
+ inst.task_wait(task_id)
vm_name = 'kimchi-cdrom'
params = {'name': 'test', 'disks': [],
'cdrom': self.kimchi_iso}
@@ -570,7 +569,7 @@ class ModelTests(unittest.TestCase):
params['name'] = vol
task_id = inst.storagevolumes_create(pool, params)['id']
rollback.prependDefer(inst.storagevolume_delete, pool, vol)
- wait_task(inst.task_lookup, task_id)
+ inst.task_wait(task_id)
self.assertEquals('finished',
inst.task_lookup(task_id)['status'])
fd, path = tempfile.mkstemp(dir=path)
@@ -611,7 +610,7 @@ class ModelTests(unittest.TestCase):
taskid = task_response['id']
vol_name = task_response['target_uri'].split('/')[-1]
self.assertEquals('COPYING', vol_name)
- wait_task(inst.task_lookup, taskid, timeout=60)
+ inst.task_wait(taskid, timeout=60)
self.assertEquals('finished',
inst.task_lookup(taskid)['status'])
vol_path = os.path.join(args['path'], vol_name)
self.assertTrue(os.path.isfile(vol_path))
@@ -1156,7 +1155,7 @@ class ModelTests(unittest.TestCase):
inst = model.Model('test:///default',
objstore_loc=self.tmp_store)
taskid = add_task('', quick_op, inst.objstore, 'Hello')
- wait_task(inst.task_lookup, taskid)
+ inst.task_wait(taskid)
self.assertEquals(1, taskid)
self.assertEquals('finished',
inst.task_lookup(taskid)['status'])
self.assertEquals('Hello', inst.task_lookup(taskid)['message'])
@@ -1167,12 +1166,12 @@ class ModelTests(unittest.TestCase):
self.assertEquals(2, taskid)
self.assertEquals('running', inst.task_lookup(taskid)['status'])
self.assertEquals('OK', inst.task_lookup(taskid)['message'])
- wait_task(inst.task_lookup, taskid)
+ inst.task_wait(taskid)
self.assertEquals('failed', inst.task_lookup(taskid)['status'])
self.assertEquals('It was not meant to be',
inst.task_lookup(taskid)['message'])
taskid = add_task('', abnormal_op, inst.objstore, {})
- wait_task(inst.task_lookup, taskid)
+ inst.task_wait(taskid)
self.assertEquals('Exception raised',
inst.task_lookup(taskid)['message'])
self.assertEquals('failed', inst.task_lookup(taskid)['status'])
@@ -1180,7 +1179,7 @@ class ModelTests(unittest.TestCase):
taskid = add_task('', continuous_ops, inst.objstore,
{'result': True})
self.assertEquals('running', inst.task_lookup(taskid)['status'])
- wait_task(inst.task_lookup, taskid, timeout=10)
+ inst.task_wait(taskid, timeout=10)
self.assertEquals('finished',
inst.task_lookup(taskid)['status'])
# This wrapper function is needed due to the new backend messaging in
@@ -1286,7 +1285,7 @@ class ModelTests(unittest.TestCase):
task = inst.debugreports_create({'name': reportName})
rollback.prependDefer(inst.debugreport_delete, tmp_name)
taskid = task['id']
- wait_task(inst.task_lookup, taskid, timeout)
+ inst.task_wait(taskid, timeout)
self.assertEquals('finished',
inst.task_lookup(taskid)['status'],
"It is not necessary an error. "
--
1.9.3