diff options
25 files changed, 185 insertions, 262 deletions
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/v1/__init__.py b/api/database/v1/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/api/database/v1/__init__.py diff --git a/api/database/handlers.py b/api/database/v1/handlers.py index 42979b529..f7c448cda 100644 --- a/api/database/handlers.py +++ b/api/database/v1/handlers.py @@ -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 diff --git a/api/database/models.py b/api/database/v1/models.py index 2270de96b..213e77f6e 100644 --- a/api/database/models.py +++ b/api/database/v1/models.py @@ -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): 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' |