From da62008a0a98cb8793ea42827a7da5e149edd144 Mon Sep 17 00:00:00 2001
From: chenjiankun <chenjiankun1@huawei.com>
Date: Mon, 26 Jun 2017 09:46:24 +0000
Subject: Call core code directly in the API of run test case

JIRA: YARDSTICK-688

We need to call core code directly in the API of runTestCase.
It would be more stable.

Change-Id: I431a85ded7cd3b20da0462f947c25d91bb99decd
Signed-off-by: chenjiankun <chenjiankun1@huawei.com>
---
 api/__init__.py                      |   4 ++
 api/database/handler.py              |  30 ---------
 api/database/handlers.py             |  31 ----------
 api/database/models.py               |  37 ------------
 api/database/v1/__init__.py          |   0
 api/database/v1/handlers.py          |  62 +++++++++++++++++++
 api/database/v1/models.py            |  39 ++++++++++++
 api/resources/asynctask.py           |   2 +-
 api/resources/env_action.py          |   2 +-
 api/resources/release_action.py      |  35 +++++------
 api/resources/results.py             | 114 ++++++++++-------------------------
 api/resources/samples_action.py      |  35 ++++++-----
 api/resources/testsuites_action.py   |  52 ++++++----------
 api/server.py                        |   2 +-
 api/utils/common.py                  |  22 +------
 api/utils/daemonthread.py            |  49 ---------------
 api/utils/thread.py                  |  37 ++++++++++++
 yardstick/benchmark/core/runner.py   |   2 +-
 yardstick/benchmark/core/scenario.py |   2 +-
 yardstick/benchmark/core/task.py     |   9 ++-
 yardstick/cmd/cli.py                 |   2 +-
 yardstick/cmd/commands/plugin.py     |   2 +-
 yardstick/cmd/commands/report.py     |   2 +-
 yardstick/cmd/commands/runner.py     |   2 +-
 yardstick/cmd/commands/scenario.py   |   2 +-
 yardstick/cmd/commands/task.py       |   2 +-
 yardstick/common/constants.py        |   3 +
 27 files changed, 252 insertions(+), 329 deletions(-)
 delete mode 100644 api/database/handler.py
 delete mode 100644 api/database/handlers.py
 delete mode 100644 api/database/models.py
 create mode 100644 api/database/v1/__init__.py
 create mode 100644 api/database/v1/handlers.py
 create mode 100644 api/database/v1/models.py
 delete mode 100644 api/utils/daemonthread.py
 create mode 100644 api/utils/thread.py

diff --git a/api/__init__.py b/api/__init__.py
index e69de29bb..c6cbbf104 100644
--- a/api/__init__.py
+++ b/api/__init__.py
@@ -0,0 +1,4 @@
+from yardstick import _init_logging
+
+
+_init_logging()
diff --git a/api/database/handler.py b/api/database/handler.py
deleted file mode 100644
index f6a22578f..000000000
--- a/api/database/handler.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# ############################################################################
-# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-# ############################################################################
-from api.database import db_session
-from api.database.models import AsyncTasks
-
-
-class AsyncTaskHandler(object):
-    def insert(self, kwargs):
-        task = AsyncTasks(**kwargs)
-        db_session.add(task)
-        db_session.commit()
-        return task
-
-    def update_status(self, task, status):
-        task.status = status
-        db_session.commit()
-
-    def update_error(self, task, error):
-        task.error = error
-        db_session.commit()
-
-    def get_task_by_taskid(self, task_id):
-        task = AsyncTasks.query.filter_by(task_id=task_id).first()
-        return task
diff --git a/api/database/handlers.py b/api/database/handlers.py
deleted file mode 100644
index 42979b529..000000000
--- a/api/database/handlers.py
+++ /dev/null
@@ -1,31 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-from api.database import db_session
-from api.database.models import Tasks
-
-
-class TasksHandler(object):
-
-    def insert(self, kwargs):
-        task = Tasks(**kwargs)
-        db_session.add(task)
-        db_session.commit()
-        return task
-
-    def update_status(self, task, status):
-        task.status = status
-        db_session.commit()
-
-    def update_error(self, task, error):
-        task.error = error
-        db_session.commit()
-
-    def get_task_by_taskid(self, task_id):
-        task = Tasks.query.filter_by(task_id=task_id).first()
-        return task
diff --git a/api/database/models.py b/api/database/models.py
deleted file mode 100644
index 2270de96b..000000000
--- a/api/database/models.py
+++ /dev/null
@@ -1,37 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-from __future__ import absolute_import
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import String
-
-from api.database import Base
-
-
-class Tasks(Base):
-    __tablename__ = 'tasks'
-    id = Column(Integer, primary_key=True)
-    task_id = Column(String(30))
-    status = Column(Integer)
-    error = Column(String(120))
-    details = Column(String(120))
-
-    def __repr__(self):
-        return '<Task %r>' % Tasks.task_id
-
-
-class AsyncTasks(Base):
-    __tablename__ = 'asynctasks'
-    id = Column(Integer, primary_key=True)
-    task_id = Column(String(30))
-    status = Column(Integer)
-    error = Column(String(120))
-
-    def __repr__(self):
-        return '<Task %r>' % AsyncTasks.task_id
diff --git a/api/database/v1/__init__.py b/api/database/v1/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/api/database/v1/handlers.py b/api/database/v1/handlers.py
new file mode 100644
index 000000000..f7c448cda
--- /dev/null
+++ b/api/database/v1/handlers.py
@@ -0,0 +1,62 @@
+##############################################################################
+# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+from api.database import db_session
+from api.database.v1.models import Tasks
+from api.database.v1.models import AsyncTasks
+
+
+class TasksHandler(object):
+
+    def insert(self, kwargs):
+        task = Tasks(**kwargs)
+        db_session.add(task)
+        db_session.commit()
+        return task
+
+    def update_status(self, task, status):
+        task.status = status
+        db_session.commit()
+
+    def update_error(self, task, error):
+        task.error = error
+        db_session.commit()
+
+    def get_task_by_taskid(self, task_id):
+        task = Tasks.query.filter_by(task_id=task_id).first()
+        if not task:
+            raise ValueError
+
+        return task
+
+    def update_attr(self, task_id, attr):
+        task =  self.get_task_by_taskid(task_id)
+
+        for k, v in attr.items():
+            setattr(task, k, v)
+        db_session.commit()
+
+
+class AsyncTaskHandler(object):
+    def insert(self, kwargs):
+        task = AsyncTasks(**kwargs)
+        db_session.add(task)
+        db_session.commit()
+        return task
+
+    def update_status(self, task, status):
+        task.status = status
+        db_session.commit()
+
+    def update_error(self, task, error):
+        task.error = error
+        db_session.commit()
+
+    def get_task_by_taskid(self, task_id):
+        task = AsyncTasks.query.filter_by(task_id=task_id).first()
+        return task
diff --git a/api/database/v1/models.py b/api/database/v1/models.py
new file mode 100644
index 000000000..213e77f6e
--- /dev/null
+++ b/api/database/v1/models.py
@@ -0,0 +1,39 @@
+##############################################################################
+# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+from __future__ import absolute_import
+from sqlalchemy import Column
+from sqlalchemy import Integer
+from sqlalchemy import String
+from sqlalchemy import Text
+
+from api.database import Base
+
+
+class Tasks(Base):
+    __tablename__ = 'tasks'
+    id = Column(Integer, primary_key=True)
+    task_id = Column(String(30))
+    status = Column(Integer)
+    error = Column(String(120))
+    result = Column(Text)
+    details = Column(String(120))
+
+    def __repr__(self):
+        return '<Task %r>' % Tasks.task_id
+
+
+class AsyncTasks(Base):
+    __tablename__ = 'asynctasks'
+    id = Column(Integer, primary_key=True)
+    task_id = Column(String(30))
+    status = Column(Integer)
+    error = Column(String(120))
+
+    def __repr__(self):
+        return '<Task %r>' % AsyncTasks.task_id
diff --git a/api/resources/asynctask.py b/api/resources/asynctask.py
index dd2a71003..1f70501d2 100644
--- a/api/resources/asynctask.py
+++ b/api/resources/asynctask.py
@@ -9,7 +9,7 @@
 import uuid
 
 from api.utils import common as common_utils
-from api.database.models import AsyncTasks
+from api.database.v1.models import AsyncTasks
 
 
 def default(args):
diff --git a/api/resources/env_action.py b/api/resources/env_action.py
index 3536559b7..3c47252dd 100644
--- a/api/resources/env_action.py
+++ b/api/resources/env_action.py
@@ -23,7 +23,7 @@ from six.moves import configparser
 from oslo_serialization import jsonutils
 from docker import Client
 
-from api.database.handler import AsyncTaskHandler
+from api.database.v1.handlers import AsyncTaskHandler
 from api.utils import influx
 from api.utils.common import result_handler
 from yardstick.common import constants as consts
diff --git a/api/resources/release_action.py b/api/resources/release_action.py
index 9016d4aa2..9871c1fc3 100644
--- a/api/resources/release_action.py
+++ b/api/resources/release_action.py
@@ -11,33 +11,34 @@ import uuid
 import os
 import logging
 
-from api.utils import common as common_utils
+from api.utils.common import result_handler
+from api.utils.thread import TaskThread
 from yardstick.common import constants as consts
+from yardstick.benchmark.core import Param
+from yardstick.benchmark.core.task import Task
 
 logger = logging.getLogger(__name__)
+logger.setLevel(logging.DEBUG)
 
 
-def runTestCase(args):
+def run_test_case(args):
     try:
-        opts = args.get('opts', {})
-        testcase = args['testcase']
+        case_name = args['testcase']
     except KeyError:
-        return common_utils.error_handler('Lack of testcase argument')
+        return result_handler(consts.API_ERROR, 'testcase must be provided')
 
-    testcase_name = consts.TESTCASE_PRE + testcase
-    testcase = os.path.join(consts.TESTCASE_DIR, testcase_name + '.yaml')
+    testcase = os.path.join(consts.TESTCASE_DIR, '{}.yaml'.format(case_name))
 
     task_id = str(uuid.uuid4())
 
-    command_list = ['task', 'start']
-    command_list = common_utils.get_command_list(command_list, opts, testcase)
-    logger.debug('The command_list is: %s', command_list)
-
-    logger.debug('Start to execute command list')
-    task_dict = {
-        'task_id': task_id,
-        'details': testcase_name
+    task_args = {
+        'inputfile': [testcase],
+        'task_id': task_id
     }
-    common_utils.exec_command_task(command_list, task_dict)
+    task_args.update(args.get('opts', {}))
+
+    param = Param(task_args)
+    task_thread = TaskThread(Task().start, param)
+    task_thread.start()
 
-    return common_utils.result_handler('success', task_id)
+    return result_handler(consts.API_SUCCESS, {'task_id': task_id})
diff --git a/api/resources/results.py b/api/resources/results.py
index a0527ed8c..692e00cc6 100644
--- a/api/resources/results.py
+++ b/api/resources/results.py
@@ -9,12 +9,14 @@
 from __future__ import absolute_import
 import logging
 import uuid
+import json
 
-from api.utils import influx as influx_utils
-from api.utils import common as common_utils
-from api.database.handlers import TasksHandler
+from api.utils.common import result_handler
+from api.database.v1.handlers import TasksHandler
+from yardstick.common import constants as consts
 
 logger = logging.getLogger(__name__)
+logger.setLevel(logging.DEBUG)
 
 
 def default(args):
@@ -24,96 +26,44 @@ def default(args):
 def getResult(args):
     try:
         task_id = args['task_id']
+    except KeyError:
+        return result_handler(consts.API_ERROR, 'task_id must be provided')
 
+    try:
         uuid.UUID(task_id)
-    except KeyError:
-        message = 'task_id must be provided'
-        return common_utils.result_handler(2, message)
+    except ValueError:
+        return result_handler(consts.API_ERROR, 'invalid task_id')
 
-    task = TasksHandler().get_task_by_taskid(task_id)
+    task_handler = TasksHandler()
+    try:
+        task = task_handler.get_task_by_taskid(task_id)
+    except ValueError:
+        return result_handler(consts.API_ERROR, 'invalid task_id')
 
     def _unfinished():
-        return common_utils.result_handler(0, {})
+        return result_handler(consts.TASK_NOT_DONE, {})
 
     def _finished():
-        testcases = task.details.split(',')
-
-        def get_data(testcase):
-            query_template = "select * from %s where task_id='%s'"
-            query_sql = query_template % (testcase, task_id)
-            data = common_utils.translate_to_str(influx_utils.query(query_sql))
-            return data
-
-        result = _format_data({k: get_data(k) for k in testcases})
-
-        return common_utils.result_handler(1, result)
+        if task.result:
+            return result_handler(consts.TASK_DONE, json.loads(task.result))
+        else:
+            return result_handler(consts.TASK_DONE, {})
 
     def _error():
-        return common_utils.result_handler(2, task.error)
+        return result_handler(consts.TASK_FAILED, task.error)
 
-    try:
-        status = task.status
+    status = task.status
+    logger.debug('Task status is: %s', status)
 
-        switcher = {
-            0: _unfinished,
-            1: _finished,
-            2: _error
-        }
-        return switcher.get(status, lambda: 'nothing')()
-    except IndexError:
-        return common_utils.result_handler(2, 'no such task')
+    if status not in [consts.TASK_NOT_DONE,
+                      consts.TASK_DONE,
+                      consts.TASK_FAILED]:
+        return result_handler(consts.API_ERROR, 'internal server error')
 
-
-def _format_data(data):
-    try:
-        first_value = data.values()[0][0]
-    except IndexError:
-        return {'criteria': 'FAIL', 'testcases': {}}
-    else:
-        info = {
-            'deploy_scenario': first_value.get('deploy_scenario'),
-            'installer': first_value.get('installer'),
-            'pod_name': first_value.get('pod_name'),
-            'version': first_value.get('version')
-        }
-        task_id = first_value.get('task_id')
-        criteria = first_value.get('criteria')
-        testcases = {k: _get_case_data(v) for k, v in data.items()}
-
-        result = {
-            'criteria': criteria,
-            'info': info,
-            'task_id': task_id,
-            'testcases': testcases
-        }
-        return result
-
-
-def _get_case_data(data):
-    try:
-        scenario = data[0]
-    except IndexError:
-        return {'tc_data': [], 'criteria': 'FAIL'}
-    else:
-        tc_data = [_get_scenario_data(s) for s in data]
-        criteria = scenario.get('criteria')
-        return {'tc_data': tc_data, 'criteria': criteria}
-
-
-def _get_scenario_data(data):
-    result = {
-        'data': {},
-        'timestamp': ''
+    switcher = {
+        consts.TASK_NOT_DONE: _unfinished,
+        consts.TASK_DONE: _finished,
+        consts.TASK_FAILED: _error
     }
 
-    blacklist = {'criteria', 'deploy_scenario', 'host', 'installer',
-                 'pod_name', 'runner_id', 'scenarios', 'target',
-                 'task_id', 'time', 'version'}
-
-    keys = set(data.keys()) - set(blacklist)
-    for k in keys:
-        result['data'][k] = data[k]
-
-    result['timestamp'] = data.get('time')
-
-    return result
+    return switcher.get(status)()
diff --git a/api/resources/samples_action.py b/api/resources/samples_action.py
index 3093864e0..10b9980af 100644
--- a/api/resources/samples_action.py
+++ b/api/resources/samples_action.py
@@ -11,32 +11,35 @@ import uuid
 import os
 import logging
 
-from api.utils import common as common_utils
+from api.utils.common import result_handler
+from api.utils.thread import TaskThread
 from yardstick.common import constants as consts
+from yardstick.benchmark.core import Param
+from yardstick.benchmark.core.task import Task
 
 logger = logging.getLogger(__name__)
+logger.setLevel(logging.DEBUG)
 
 
-def runTestCase(args):
+def run_test_case(args):
     try:
-        opts = args.get('opts', {})
-        testcase_name = args['testcase']
+        case_name = args['testcase']
     except KeyError:
-        return common_utils.error_handler('Lack of testcase argument')
+        return result_handler(consts.API_ERROR, 'testcase must be provided')
 
-    testcase = os.path.join(consts.SAMPLE_CASE_DIR, testcase_name + '.yaml')
+    testcase = os.path.join(consts.SAMPLE_CASE_DIR,
+                            '{}.yaml'.format(case_name))
 
     task_id = str(uuid.uuid4())
 
-    command_list = ['task', 'start']
-    command_list = common_utils.get_command_list(command_list, opts, testcase)
-    logger.debug('The command_list is: %s', command_list)
-
-    logger.debug('Start to execute command list')
-    task_dict = {
-        'task_id': task_id,
-        'details': testcase_name
+    task_args = {
+        'inputfile': [testcase],
+        'task_id': task_id
     }
-    common_utils.exec_command_task(command_list, task_dict)
+    task_args.update(args.get('opts', {}))
+
+    param = Param(task_args)
+    task_thread = TaskThread(Task().start, param)
+    task_thread.start()
 
-    return common_utils.result_handler('success', task_id)
+    return result_handler(consts.API_SUCCESS, {'task_id': task_id})
diff --git a/api/resources/testsuites_action.py b/api/resources/testsuites_action.py
index a385290d9..e37eacc3e 100644
--- a/api/resources/testsuites_action.py
+++ b/api/resources/testsuites_action.py
@@ -6,57 +6,41 @@
 # which accompanies this distribution, and is available at
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
-
-"""Yardstick test suite api action"""
-
 from __future__ import absolute_import
 import uuid
 import os
 import logging
-import yaml
 
-from api.utils import common as common_utils
+from api.utils.common import result_handler
+from api.utils.thread import TaskThread
 from yardstick.common import constants as consts
-from yardstick.common.task_template import TaskTemplate
+from yardstick.benchmark.core import Param
+from yardstick.benchmark.core.task import Task
 
 logger = logging.getLogger(__name__)
+logger.setLevel(logging.DEBUG)
 
 
-def runTestSuite(args):
+def run_test_suite(args):
     try:
-        opts = args.get('opts', {})
-        testsuite = args['testsuite']
+        suite_name = args['testsuite']
     except KeyError:
-        return common_utils.error_handler('Lack of testsuite argument')
+        return result_handler(consts.API_ERROR, 'testsuite must be provided')
 
-    if 'suite' not in opts:
-        opts['suite'] = 'true'
-
-    testsuite = os.path.join(consts.TESTSUITE_DIR, '{}.yaml'.format(testsuite))
+    testsuite = os.path.join(consts.TESTSUITE_DIR,
+                             '{}.yaml'.format(suite_name))
 
     task_id = str(uuid.uuid4())
 
-    command_list = ['task', 'start']
-    command_list = common_utils.get_command_list(command_list, opts, testsuite)
-    logger.debug('The command_list is: %s', command_list)
-
-    logger.debug('Start to execute command list')
-    task_dic = {
+    task_args = {
+        'inputfile': [testsuite],
         'task_id': task_id,
-        'details': _get_cases_from_suite_file(testsuite)
+        'suite': True
     }
-    common_utils.exec_command_task(command_list, task_dic)
-
-    return common_utils.result_handler('success', task_id)
-
-
-def _get_cases_from_suite_file(testsuite):
-    def get_name(full_name):
-        return os.path.splitext(full_name)[0]
+    task_args.update(args.get('opts', {}))
 
-    with open(testsuite) as f:
-        contents = TaskTemplate.render(f.read())
+    param = Param(task_args)
+    task_thread = TaskThread(Task().start, param)
+    task_thread.start()
 
-    suite_dic = yaml.safe_load(contents)
-    testcases = (get_name(c['file_name']) for c in suite_dic['test_cases'])
-    return ','.join(testcases)
+    return result_handler(consts.API_SUCCESS, {'task_id': task_id})
diff --git a/api/server.py b/api/server.py
index 1d42feffb..d39c44544 100644
--- a/api/server.py
+++ b/api/server.py
@@ -19,7 +19,7 @@ from flask_restful import Api
 from api.database import Base
 from api.database import db_session
 from api.database import engine
-from api.database import models
+from api.database.v1 import models
 from api.urls import urlpatterns
 from yardstick import _init_logging
 
diff --git a/api/utils/common.py b/api/utils/common.py
index f8b0d40ba..8398b8f60 100644
--- a/api/utils/common.py
+++ b/api/utils/common.py
@@ -13,10 +13,8 @@ import logging
 from flask import jsonify
 import six
 
-from api.utils.daemonthread import DaemonThread
-from yardstick.cmd.cli import YardstickCLI
-
 logger = logging.getLogger(__name__)
+logger.setLevel(logging.DEBUG)
 
 
 def translate_to_str(obj):
@@ -29,24 +27,6 @@ def translate_to_str(obj):
     return obj
 
 
-def get_command_list(command_list, opts, args):
-
-    command_list.append(args)
-
-    command_list.extend(('--{}'.format(k) for k in opts if k != 'task-args'))
-
-    task_args = opts.get('task-args', '')
-    if task_args:
-        command_list.extend(['--task-args', str(task_args)])
-
-    return command_list
-
-
-def exec_command_task(command_list, task_dict):   # pragma: no cover
-    daemonthread = DaemonThread(YardstickCLI().api, (command_list, task_dict))
-    daemonthread.start()
-
-
 def error_handler(message):
     logger.debug(message)
     result = {
diff --git a/api/utils/daemonthread.py b/api/utils/daemonthread.py
deleted file mode 100644
index 3d5625547..000000000
--- a/api/utils/daemonthread.py
+++ /dev/null
@@ -1,49 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-from __future__ import absolute_import
-import threading
-import os
-import errno
-
-from yardstick.common import constants as consts
-from api.database.handlers import TasksHandler
-
-
-class DaemonThread(threading.Thread):
-
-    def __init__(self, method, args):
-        super(DaemonThread, self).__init__(target=method, args=args)
-        self.method = method
-        self.command_list = args[0]
-        self.task_dict = args[1]
-
-    def run(self):
-        self.task_dict['status'] = 0
-        task_id = self.task_dict['task_id']
-
-        try:
-            task_handler = TasksHandler()
-            task = task_handler.insert(self.task_dict)
-
-            self.method(self.command_list, task_id)
-
-            task_handler.update_status(task, 1)
-        except Exception as e:
-            task_handler.update_status(task, 2)
-            task_handler.update_error(task, str(e))
-        finally:
-            _handle_testsuite_file(task_id)
-
-
-def _handle_testsuite_file(task_id):
-    try:
-        os.remove(os.path.join(consts.TESTSUITE_DIR, task_id + '.yaml'))
-    except OSError as e:
-        if e.errno != errno.ENOENT:
-            raise
diff --git a/api/utils/thread.py b/api/utils/thread.py
new file mode 100644
index 000000000..2106548f5
--- /dev/null
+++ b/api/utils/thread.py
@@ -0,0 +1,37 @@
+import threading
+import logging
+
+from oslo_serialization import jsonutils
+
+from api.database.v1.handlers import TasksHandler
+from yardstick.common import constants as consts
+
+logger = logging.getLogger(__name__)
+logger.setLevel(logging.DEBUG)
+
+
+class TaskThread(threading.Thread):
+
+    def __init__(self, target, args):
+        super(TaskThread, self).__init__(target=target, args=args)
+        self.target = target
+        self.args = args
+
+    def run(self):
+        task_handler = TasksHandler()
+        data = {'task_id': self.args.task_id, 'status': consts.TASK_NOT_DONE}
+        task_handler.insert(data)
+
+        logger.info('Starting run task')
+        try:
+            data = self.target(self.args)
+        except Exception as e:
+            logger.exception('Task Failed')
+            update_data = {'status': consts.TASK_FAILED, 'error': str(e)}
+            task_handler.update_attr(self.args.task_id, update_data)
+        else:
+            logger.info('Task Finished')
+            logger.debug('Result: %s', data)
+
+            data['result'] = jsonutils.dumps(data.get('result', {}))
+            task_handler.update_attr(self.args.task_id, data)
diff --git a/yardstick/benchmark/core/runner.py b/yardstick/benchmark/core/runner.py
index b9c22cbc9..64acdaa99 100644
--- a/yardstick/benchmark/core/runner.py
+++ b/yardstick/benchmark/core/runner.py
@@ -15,7 +15,7 @@ from yardstick.benchmark.runners.base import Runner
 from yardstick.benchmark.core import print_hbar
 
 
-class Runners(object):
+class Runners(object):  # pragma: no cover
     """Runner commands.
 
        Set of commands to discover and display runner types.
diff --git a/yardstick/benchmark/core/scenario.py b/yardstick/benchmark/core/scenario.py
index a9d933faf..cd119c24c 100644
--- a/yardstick/benchmark/core/scenario.py
+++ b/yardstick/benchmark/core/scenario.py
@@ -15,7 +15,7 @@ from yardstick.benchmark.scenarios.base import Scenario
 from yardstick.benchmark.core import print_hbar
 
 
-class Scenarios(object):
+class Scenarios(object):    # pragma: no cover
     """Scenario commands.
 
        Set of commands to discover and display scenario types.
diff --git a/yardstick/benchmark/core/task.py b/yardstick/benchmark/core/task.py
index 478a51f9d..9c6caf03f 100644
--- a/yardstick/benchmark/core/task.py
+++ b/yardstick/benchmark/core/task.py
@@ -20,6 +20,8 @@ import time
 import logging
 import uuid
 import errno
+import collections
+
 from six.moves import filter
 
 from yardstick.benchmark.contexts.base import Context
@@ -51,7 +53,8 @@ class Task(object):     # pragma: no cover
 
         atexit.register(self.atexit_handler)
 
-        self.task_id = kwargs.get('task_id', str(uuid.uuid4()))
+        task_id = getattr(args, 'task_id')
+        self.task_id = task_id if task_id else str(uuid.uuid4())
 
         check_environment()
 
@@ -133,6 +136,7 @@ class Task(object):     # pragma: no cover
               scenario['task_id'], scenario['tc'])
 
         print("Done, exiting")
+        return result
 
     def _init_output_config(self, output_config):
         output_config.setdefault('DEFAULT', {})
@@ -594,6 +598,9 @@ def print_invalid_header(source_name, args):
 
 
 def parse_task_args(src_name, args):
+    if isinstance(args, collections.Mapping):
+        return args
+
     try:
         kw = args and yaml.safe_load(args)
         kw = {} if kw is None else kw
diff --git a/yardstick/cmd/cli.py b/yardstick/cmd/cli.py
index 79f66e574..d2c49e89b 100644
--- a/yardstick/cmd/cli.py
+++ b/yardstick/cmd/cli.py
@@ -53,7 +53,7 @@ def find_config_files(path_list):
     return None
 
 
-class YardstickCLI():
+class YardstickCLI():   # pragma: no cover
     """Command-line interface to yardstick"""
 
     # Command categories
diff --git a/yardstick/cmd/commands/plugin.py b/yardstick/cmd/commands/plugin.py
index f97c490b7..b90ac15e6 100644
--- a/yardstick/cmd/commands/plugin.py
+++ b/yardstick/cmd/commands/plugin.py
@@ -17,7 +17,7 @@ from yardstick.common.utils import cliargs
 from yardstick.cmd.commands import change_osloobj_to_paras
 
 
-class PluginCommands(object):
+class PluginCommands(object):   # pragma: no cover
     """Plugin commands.
 
        Set of commands to manage plugins.
diff --git a/yardstick/cmd/commands/report.py b/yardstick/cmd/commands/report.py
index 87ae7d5f7..47bf22a1f 100644
--- a/yardstick/cmd/commands/report.py
+++ b/yardstick/cmd/commands/report.py
@@ -19,7 +19,7 @@ from yardstick.cmd.commands import change_osloobj_to_paras
 from yardstick.common.utils import cliargs
 
 
-class ReportCommands(object):
+class ReportCommands(object):   # pragma: no cover
     """Report commands.
 
     Set of commands to manage benchmark tasks.
diff --git a/yardstick/cmd/commands/runner.py b/yardstick/cmd/commands/runner.py
index b99ae789b..9ee99cf44 100644
--- a/yardstick/cmd/commands/runner.py
+++ b/yardstick/cmd/commands/runner.py
@@ -17,7 +17,7 @@ from yardstick.common.utils import cliargs
 from yardstick.cmd.commands import change_osloobj_to_paras
 
 
-class RunnerCommands(object):
+class RunnerCommands(object):   # pragma: no cover
     """Runner commands.
 
        Set of commands to discover and display runner types.
diff --git a/yardstick/cmd/commands/scenario.py b/yardstick/cmd/commands/scenario.py
index 618ed2915..0e3f2c3be 100644
--- a/yardstick/cmd/commands/scenario.py
+++ b/yardstick/cmd/commands/scenario.py
@@ -16,7 +16,7 @@ from yardstick.common.utils import cliargs
 from yardstick.cmd.commands import change_osloobj_to_paras
 
 
-class ScenarioCommands(object):
+class ScenarioCommands(object):     # pragma: no cover
     """Scenario commands.
 
        Set of commands to discover and display scenario types.
diff --git a/yardstick/cmd/commands/task.py b/yardstick/cmd/commands/task.py
index 6384e6eb1..0f98cabdc 100644
--- a/yardstick/cmd/commands/task.py
+++ b/yardstick/cmd/commands/task.py
@@ -19,7 +19,7 @@ from yardstick.cmd.commands import change_osloobj_to_paras
 output_file_default = "/tmp/yardstick.out"
 
 
-class TaskCommands(object):
+class TaskCommands(object):     # pragma: no cover
     """Task commands.
 
        Set of commands to manage benchmark tasks.
diff --git a/yardstick/common/constants.py b/yardstick/common/constants.py
index 47a519923..9edf78650 100644
--- a/yardstick/common/constants.py
+++ b/yardstick/common/constants.py
@@ -80,6 +80,9 @@ SQLITE = 'sqlite:////tmp/yardstick.db'
 
 API_SUCCESS = 1
 API_ERROR = 2
+TASK_NOT_DONE = 0
+TASK_DONE = 1
+TASK_FAILED = 2
 
 BASE_URL = 'http://localhost:5000'
 ENV_ACTION_API = BASE_URL + '/yardstick/env/action'
-- 
cgit