
On 11/02/2014 11:05 PM, CrÃstian Viana wrote:
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@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
If you will use the new TaskModel.wait() function in all wait_task occurrences you should also remove the wait_task implementation from utils.py
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. "