Call core code directly in the API of run test case 07/36507/3
authorchenjiankun <chenjiankun1@huawei.com>
Mon, 26 Jun 2017 09:46:24 +0000 (09:46 +0000)
committerchenjiankun <chenjiankun1@huawei.com>
Thu, 29 Jun 2017 11:25:17 +0000 (11:25 +0000)
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>
25 files changed:
api/__init__.py
api/database/handler.py [deleted file]
api/database/v1/__init__.py [new file with mode: 0644]
api/database/v1/handlers.py [moved from api/database/handlers.py with 54% similarity]
api/database/v1/models.py [moved from api/database/models.py with 95% similarity]
api/resources/asynctask.py
api/resources/env_action.py
api/resources/release_action.py
api/resources/results.py
api/resources/samples_action.py
api/resources/testsuites_action.py
api/server.py
api/utils/common.py
api/utils/daemonthread.py [deleted file]
api/utils/thread.py [new file with mode: 0644]
yardstick/benchmark/core/runner.py
yardstick/benchmark/core/scenario.py
yardstick/benchmark/core/task.py
yardstick/cmd/cli.py
yardstick/cmd/commands/plugin.py
yardstick/cmd/commands/report.py
yardstick/cmd/commands/runner.py
yardstick/cmd/commands/scenario.py
yardstick/cmd/commands/task.py
yardstick/common/constants.py

index e69de29..c6cbbf1 100644 (file)
@@ -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 (file)
index f6a2257..0000000
+++ /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/v1/__init__.py b/api/database/v1/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
similarity index 54%
rename from api/database/handlers.py
rename to api/database/v1/handlers.py
index 42979b5..f7c448c 100644 (file)
@@ -7,7 +7,8 @@
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
 from api.database import db_session
-from api.database.models import Tasks
+from api.database.v1.models import Tasks
+from api.database.v1.models import AsyncTasks
 
 
 class TasksHandler(object):
@@ -28,4 +29,34 @@ class TasksHandler(object):
 
     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
similarity index 95%
rename from api/database/models.py
rename to api/database/v1/models.py
index 2270de9..213e77f 100644 (file)
@@ -10,6 +10,7 @@ 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
 
@@ -20,6 +21,7 @@ class Tasks(Base):
     task_id = Column(String(30))
     status = Column(Integer)
     error = Column(String(120))
+    result = Column(Text)
     details = Column(String(120))
 
     def __repr__(self):
index dd2a710..1f70501 100644 (file)
@@ -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):
index 3536559..3c47252 100644 (file)
@@ -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
index 9016d4a..9871c1f 100644 (file)
@@ -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})
index a0527ed..692e00c 100644 (file)
@@ -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)()
index 3093864..10b9980 100644 (file)
@@ -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})
index a385290..e37eacc 100644 (file)
@@ -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})
index 1d42fef..d39c445 100644 (file)
@@ -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
 
index f8b0d40..8398b8f 100644 (file)
@@ -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 (file)
index 3d56255..0000000
+++ /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 (file)
index 0000000..2106548
--- /dev/null
@@ -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)
index b9c22cb..64acdaa 100644 (file)
@@ -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.
index a9d933f..cd119c2 100644 (file)
@@ -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.
index 478a51f..9c6caf0 100644 (file)
@@ -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
index 79f66e5..d2c49e8 100644 (file)
@@ -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
index f97c490..b90ac15 100644 (file)
@@ -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.
index 87ae7d5..47bf22a 100644 (file)
@@ -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.
index b99ae78..9ee99cf 100644 (file)
@@ -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.
index 618ed29..0e3f2c3 100644 (file)
@@ -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.
index 6384e6e..0f98cab 100644 (file)
@@ -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.
index 47a5199..9edf786 100644 (file)
@@ -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'