[Kimchi-devel] [PATCH 4/8] Add model function to wait for task

Crístian Viana vianac at linux.vnet.ibm.com
Mon Nov 3 01:05:25 UTC 2014


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 at 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 de2e49a..7f33540 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']
 
@@ -290,7 +289,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}
@@ -569,7 +568,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)
@@ -610,7 +609,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))
@@ -1155,7 +1154,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'])
@@ -1166,12 +1165,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'])
@@ -1179,7 +1178,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
@@ -1285,7 +1284,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




More information about the Kimchi-devel mailing list