提交所有代码到 qiaoxinjiu 分支
This commit is contained in:
521
app/api/service/automationService.py
Normal file
521
app/api/service/automationService.py
Normal file
@@ -0,0 +1,521 @@
|
||||
# encoding: UTF-8
|
||||
import secrets
|
||||
from datetime import datetime
|
||||
|
||||
from ..dao.automationDao import AutomationDao
|
||||
from ..model.automationModel import AutoExecution, AutoExecutionCase
|
||||
from ..model.planModel import PlanCase
|
||||
from ..service.planService import PlanService
|
||||
from common.jenkinsRequest import JenkinsRequest
|
||||
from const import JENKINS_DEFAULT_JOB, PLATFORM_BASE_URL
|
||||
from logger import logger
|
||||
|
||||
class AutomationService(object):
|
||||
STATUS_PENDING = 0
|
||||
STATUS_TRIGGERING = 1
|
||||
STATUS_QUEUED = 2
|
||||
STATUS_RUNNING = 3
|
||||
STATUS_SUCCESS = 4
|
||||
STATUS_FAILED = 5
|
||||
STATUS_CANCELED = 6
|
||||
STATUS_TRIGGER_FAILED = 7
|
||||
STATUS_CALLBACK_ERROR = 8
|
||||
|
||||
CASE_STATUS_PENDING = 0
|
||||
CASE_STATUS_RUNNING = 1
|
||||
CASE_STATUS_PASSED = 2
|
||||
CASE_STATUS_FAILED = 3
|
||||
CASE_STATUS_BLOCKED = 4
|
||||
CASE_STATUS_SKIPPED = 5
|
||||
CASE_STATUS_NOT_FOUND = 6
|
||||
CASE_STATUS_CANCELED = 7
|
||||
|
||||
PLAN_CASE_STATUS_MAP = {
|
||||
CASE_STATUS_PASSED: 1,
|
||||
CASE_STATUS_FAILED: 2,
|
||||
CASE_STATUS_BLOCKED: 3,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def generate_execution_no():
|
||||
return 'AE' + datetime.now().strftime('%Y%m%d%H%M%S%f')[:-3]
|
||||
|
||||
@staticmethod
|
||||
def generate_callback_token():
|
||||
return secrets.token_hex(16)
|
||||
|
||||
@staticmethod
|
||||
def create_case_execution(session, req_data, user_id):
|
||||
case_id = req_data.get('caseId') or req_data.get('case_id')
|
||||
env_code = req_data.get('envCode') or req_data.get('env_code')
|
||||
if not case_id or not env_code:
|
||||
return {}, 'caseId、envCode 为必传参数'
|
||||
case_item = AutomationDao.query_case_auto_item(session, case_id)
|
||||
if not case_item:
|
||||
return {}, '该用例不存在或未接入自动化'
|
||||
running_exists = AutomationService.get_running_execution_by_case(session, case_id, env_code)
|
||||
if running_exists:
|
||||
return {}, '该用例在当前环境已有执行中任务'
|
||||
callback_token = AutomationService.generate_callback_token()
|
||||
execution_obj, err_msg = AutomationDao.create_execution(session, {
|
||||
'execution_no': AutomationService.generate_execution_no(),
|
||||
'trigger_type': 1,
|
||||
'project_id': case_item.project_id,
|
||||
'source_case_id': case_item.id,
|
||||
'env_code': env_code,
|
||||
'run_mode': int(req_data.get('runMode') or req_data.get('run_mode') or 1),
|
||||
'status': AutomationService.STATUS_PENDING,
|
||||
'total_count': 1,
|
||||
'pending_count': 1,
|
||||
'running_count': 0,
|
||||
'passed_count': 0,
|
||||
'failed_count': 0,
|
||||
'blocked_count': 0,
|
||||
'skipped_count': 0,
|
||||
'not_found_count': 0,
|
||||
'trigger_by': user_id,
|
||||
'trigger_message': req_data.get('remark'),
|
||||
'callback_token': callback_token,
|
||||
'ext': {}
|
||||
})
|
||||
if err_msg:
|
||||
return {}, err_msg
|
||||
_, err_msg = AutomationDao.batch_create_execution_cases(session, [{
|
||||
'execution_id': execution_obj.id,
|
||||
'case_id': case_item.id,
|
||||
'case_key': case_item.case_key,
|
||||
'case_title': case_item.title,
|
||||
'run_order': 1,
|
||||
'status': AutomationService.CASE_STATUS_PENDING
|
||||
}])
|
||||
if err_msg:
|
||||
return {}, err_msg
|
||||
trigger_ok, trigger_msg = AutomationService.trigger_jenkins(session, execution_obj.id, req_data.get('jenkinsJobName'))
|
||||
if not trigger_ok:
|
||||
return {}, trigger_msg
|
||||
execution = AutomationDao.get_execution_by_id(session, execution_obj.id)
|
||||
return execution.to_dict() if execution else {'id': execution_obj.id}, ''
|
||||
|
||||
@staticmethod
|
||||
def create_plan_execution(session, req_data, user_id):
|
||||
from logger import logger
|
||||
|
||||
plan_id = req_data.get('planId') or req_data.get('plan_id')
|
||||
env_code = req_data.get('envCode') or req_data.get('env_code')
|
||||
round_no = req_data.get('roundNo') or req_data.get('round_no')
|
||||
case_ids = req_data.get('caseIds') or req_data.get('case_ids') or []
|
||||
|
||||
logger.info(f'====== 计划自动化执行开始 ======')
|
||||
logger.info(f'请求参数: plan_id={plan_id}, env_code={env_code}, round_no={round_no}, case_ids={case_ids}, user_id={user_id}')
|
||||
|
||||
if not plan_id or not env_code:
|
||||
logger.error('参数校验失败: planId、envCode 为必传参数')
|
||||
return {}, 'planId、envCode 为必传参数'
|
||||
|
||||
running_exists = AutomationService.get_running_execution_by_plan(session, plan_id, env_code)
|
||||
if running_exists:
|
||||
logger.error(f'计划执行冲突: 计划 {plan_id} 在环境 {env_code} 已有执行中任务')
|
||||
return {}, '该计划在当前环境已有执行中任务'
|
||||
|
||||
logger.info(f'查询计划自动化用例: plan_id={plan_id}, round_no={round_no}')
|
||||
items = AutomationDao.query_plan_auto_cases(session, plan_id, round_no, case_ids)
|
||||
if not items:
|
||||
logger.error('计划下无可执行自动化用例')
|
||||
return {}, '计划下无可执行自动化用例'
|
||||
|
||||
logger.info(f'查询到 {len(items)} 条自动化用例')
|
||||
for idx, (plan_case, case_item) in enumerate(items, start=1):
|
||||
logger.info(f' {idx}. case_key={case_item.case_key}, case_title={case_item.title}')
|
||||
|
||||
project_id = items[0][1].project_id
|
||||
callback_token = AutomationService.generate_callback_token()
|
||||
execution_no = AutomationService.generate_execution_no()
|
||||
|
||||
logger.info(f'创建执行记录: execution_no={execution_no}, project_id={project_id}, plan_id={plan_id}')
|
||||
execution_obj, err_msg = AutomationDao.create_execution(session, {
|
||||
'execution_no': execution_no,
|
||||
'trigger_type': 2,
|
||||
'project_id': project_id,
|
||||
'plan_id': int(plan_id),
|
||||
'plan_round_no': int(round_no) if round_no not in (None, '') else None,
|
||||
'env_code': env_code,
|
||||
'run_mode': int(req_data.get('runMode') or req_data.get('run_mode') or 1),
|
||||
'status': AutomationService.STATUS_PENDING,
|
||||
'total_count': len(items),
|
||||
'pending_count': len(items),
|
||||
'running_count': 0,
|
||||
'passed_count': 0,
|
||||
'failed_count': 0,
|
||||
'blocked_count': 0,
|
||||
'skipped_count': 0,
|
||||
'not_found_count': 0,
|
||||
'trigger_by': user_id,
|
||||
'trigger_message': req_data.get('remark'),
|
||||
'callback_token': callback_token,
|
||||
'ext': {}
|
||||
})
|
||||
if err_msg:
|
||||
logger.error(f'创建执行记录失败: {err_msg}')
|
||||
return {}, err_msg
|
||||
|
||||
logger.info(f'执行记录创建成功: execution_id={execution_obj.id}')
|
||||
|
||||
batch_list = []
|
||||
for idx, (plan_case, case_item) in enumerate(items, start=1):
|
||||
batch_list.append({
|
||||
'execution_id': execution_obj.id,
|
||||
'plan_case_id': plan_case.id,
|
||||
'case_id': case_item.id,
|
||||
'case_key': case_item.case_key,
|
||||
'case_title': case_item.title,
|
||||
'run_order': idx,
|
||||
'status': AutomationService.CASE_STATUS_PENDING
|
||||
})
|
||||
|
||||
logger.info(f'批量创建执行明细: {len(batch_list)} 条')
|
||||
_, err_msg = AutomationDao.batch_create_execution_cases(session, batch_list)
|
||||
if err_msg:
|
||||
logger.error(f'批量创建执行明细失败: {err_msg}')
|
||||
return {}, err_msg
|
||||
|
||||
logger.info(f'触发Jenkins构建: execution_id={execution_obj.id}')
|
||||
trigger_ok, trigger_msg = AutomationService.trigger_jenkins(session, execution_obj.id, req_data.get('jenkinsJobName'))
|
||||
if not trigger_ok:
|
||||
logger.error(f'Jenkins触发失败: {trigger_msg}')
|
||||
return {}, trigger_msg
|
||||
|
||||
logger.info('计划自动化执行成功')
|
||||
execution = AutomationDao.get_execution_by_id(session, execution_obj.id)
|
||||
logger.info(f'====== 计划自动化执行结束 ======')
|
||||
return execution.to_dict() if execution else {'id': execution_obj.id}, ''
|
||||
|
||||
@staticmethod
|
||||
def trigger_jenkins(session, execution_id, job_name=None):
|
||||
|
||||
|
||||
execution = AutomationDao.get_execution_by_id(session, execution_id)
|
||||
if not execution:
|
||||
return False, '未查询到对应执行记录'
|
||||
AutomationDao.update_execution_by_id(session, execution_id, {'status': AutomationService.STATUS_TRIGGERING})
|
||||
|
||||
cases, _ = AutomationDao.list_execution_case_by_filters(session, [AutoExecutionCase.execution_id == int(execution_id)], 1, 100000)
|
||||
case_keys = [case.case_key for case in cases if case.case_key]
|
||||
test_target = ','.join(case_keys)
|
||||
|
||||
test_type = 'story' if case_keys else 'all'
|
||||
|
||||
params = {
|
||||
'EXECUTION_ID': execution.id,
|
||||
'CALLBACK_TOKEN': execution.callback_token,
|
||||
'PLATFORM_BASE_URL': PLATFORM_BASE_URL,
|
||||
'ENV_CODE': execution.env_code,
|
||||
'RUN_MODE': execution.run_mode,
|
||||
'TRIGGER_TYPE': execution.trigger_type,
|
||||
'TEST_TYPE': test_type,
|
||||
'TEST_TARGET': test_target
|
||||
}
|
||||
|
||||
jenkins_url = None
|
||||
jenkins_job_name = None
|
||||
if execution.plan_id:
|
||||
plan = AutomationDao.get_plan_by_id(session, execution.plan_id)
|
||||
if plan and plan.jenkins_url:
|
||||
jenkins_url = plan.jenkins_url
|
||||
if '/job/' in jenkins_url:
|
||||
import re
|
||||
match = re.match(r'^(https?://[^/]+)/(job/[^/]+)/?.*$', jenkins_url)
|
||||
if match:
|
||||
jenkins_url = match.group(1)
|
||||
jenkins_job_name = match.group(2).replace('job/', '')
|
||||
logger.info(f'从计划配置中解析 Jenkins: base_url={jenkins_url}, job_name={jenkins_job_name}')
|
||||
|
||||
jenkins_request = JenkinsRequest(jenkins_url=jenkins_url)
|
||||
target_job_name = jenkins_job_name or job_name or execution.jenkins_job_name or JENKINS_DEFAULT_JOB
|
||||
success, err_msg, payload = jenkins_request.build_with_parameters(params, target_job_name)
|
||||
if not success:
|
||||
AutomationDao.update_execution_by_id(session, execution_id, {
|
||||
'status': AutomationService.STATUS_TRIGGER_FAILED,
|
||||
'trigger_message': err_msg
|
||||
})
|
||||
return False, err_msg
|
||||
update_info = {
|
||||
'status': AutomationService.STATUS_QUEUED,
|
||||
'jenkins_job_name': payload.get('job_name') or job_name or JENKINS_DEFAULT_JOB,
|
||||
'jenkins_queue_id': payload.get('queue_id')
|
||||
}
|
||||
if payload.get('location'):
|
||||
update_info['trigger_message'] = payload.get('location')
|
||||
AutomationDao.update_execution_by_id(session, execution_id, update_info)
|
||||
return True, ''
|
||||
|
||||
@staticmethod
|
||||
def get_running_execution_by_case(session, case_id, env_code):
|
||||
items, _ = AutomationDao.list_execution_by_filters(session, [
|
||||
AutoExecution.source_case_id == int(case_id),
|
||||
AutoExecution.env_code == env_code,
|
||||
AutoExecution.status.in_([0, 1, 2, 3])
|
||||
], 1, 1)
|
||||
return items[0] if items else None
|
||||
|
||||
@staticmethod
|
||||
def get_running_execution_by_plan(session, plan_id, env_code):
|
||||
items, _ = AutomationDao.list_execution_by_filters(session, [
|
||||
AutoExecution.plan_id == int(plan_id),
|
||||
AutoExecution.env_code == env_code,
|
||||
AutoExecution.status.in_([0, 1, 2, 3])
|
||||
], 1, 1)
|
||||
return items[0] if items else None
|
||||
|
||||
@staticmethod
|
||||
def list_executions(session, req_data):
|
||||
filters = []
|
||||
project_id = req_data.get('projectId') or req_data.get('project_id')
|
||||
plan_id = req_data.get('planId') or req_data.get('plan_id')
|
||||
status = req_data.get('status')
|
||||
trigger_type = req_data.get('triggerType') or req_data.get('trigger_type')
|
||||
if project_id:
|
||||
filters.append(AutoExecution.project_id == int(project_id))
|
||||
if plan_id:
|
||||
filters.append(AutoExecution.plan_id == int(plan_id))
|
||||
if status not in (None, ''):
|
||||
filters.append(AutoExecution.status == int(status))
|
||||
if trigger_type not in (None, ''):
|
||||
filters.append(AutoExecution.trigger_type == int(trigger_type))
|
||||
items, total = AutomationDao.list_execution_by_filters(session, filters, req_data.get('pageNo') or req_data.get('page') or 1, req_data.get('pageSize') or req_data.get('size') or 20)
|
||||
return {'list': [item.to_dict() for item in items], 'total': total}
|
||||
|
||||
@staticmethod
|
||||
def get_execution_detail(session, execution_id):
|
||||
execution = AutomationDao.get_execution_by_id(session, execution_id)
|
||||
if not execution:
|
||||
return {}, '未查询到对应执行记录'
|
||||
ret = execution.to_dict()
|
||||
summary = AutomationDao.count_execution_case_summary(session, execution_id)
|
||||
ret.update({
|
||||
'summary': {
|
||||
'total': summary.get('total', 0),
|
||||
'pending': summary.get(0, 0),
|
||||
'running': summary.get(1, 0),
|
||||
'passed': summary.get(2, 0),
|
||||
'failed': summary.get(3, 0),
|
||||
'blocked': summary.get(4, 0),
|
||||
'skipped': summary.get(5, 0),
|
||||
'notFound': summary.get(6, 0),
|
||||
'canceled': summary.get(7, 0)
|
||||
}
|
||||
})
|
||||
return ret, ''
|
||||
|
||||
@staticmethod
|
||||
def list_execution_cases(session, req_data):
|
||||
execution_id = req_data.get('executionId') or req_data.get('execution_id')
|
||||
if not execution_id:
|
||||
return {}, 'executionId 为必传参数'
|
||||
filters = [AutoExecutionCase.execution_id == int(execution_id)]
|
||||
status = req_data.get('status')
|
||||
if status not in (None, ''):
|
||||
filters.append(AutoExecutionCase.status == int(status))
|
||||
items, total = AutomationDao.list_execution_case_by_filters(session, filters, req_data.get('pageNo') or req_data.get('page') or 1, req_data.get('pageSize') or req_data.get('size') or 20)
|
||||
return {'list': [item.to_dict() for item in items], 'total': total}, ''
|
||||
|
||||
@staticmethod
|
||||
def pull_execution_cases(session, execution_id, callback_token):
|
||||
execution = AutomationDao.get_execution_by_id(session, execution_id)
|
||||
if not execution:
|
||||
return {}, '未查询到对应执行记录'
|
||||
if execution.callback_token != callback_token:
|
||||
return {}, '回调鉴权失败'
|
||||
case_items, _ = AutomationDao.list_execution_case_by_filters(session, [AutoExecutionCase.execution_id == int(execution_id)], 1, 100000)
|
||||
return {
|
||||
'executionId': execution.id,
|
||||
'executionNo': execution.execution_no,
|
||||
'triggerType': execution.trigger_type,
|
||||
'projectId': execution.project_id,
|
||||
'planId': execution.plan_id,
|
||||
'envCode': execution.env_code,
|
||||
'runMode': execution.run_mode,
|
||||
'items': [{
|
||||
'executionCaseId': item.id,
|
||||
'planCaseId': item.plan_case_id,
|
||||
'caseId': item.case_id,
|
||||
'caseKey': item.case_key,
|
||||
'caseTitle': item.case_title,
|
||||
'runOrder': item.run_order
|
||||
} for item in case_items]
|
||||
}, ''
|
||||
|
||||
@staticmethod
|
||||
def mark_execution_queued(session, req_data):
|
||||
execution_id = req_data.get('executionId') or req_data.get('execution_id')
|
||||
execution = AutomationDao.get_execution_by_id(session, execution_id)
|
||||
if not execution:
|
||||
return 0, '未查询到对应执行记录'
|
||||
return AutomationDao.update_execution_by_id(session, execution_id, {
|
||||
'status': AutomationService.STATUS_QUEUED,
|
||||
'jenkins_queue_id': req_data.get('queueId') or req_data.get('queue_id'),
|
||||
'jenkins_job_name': req_data.get('jobName') or req_data.get('job_name') or execution.jenkins_job_name,
|
||||
'jenkins_build_number': req_data.get('buildNumber') or req_data.get('build_number'),
|
||||
'jenkins_build_url': req_data.get('buildUrl') or req_data.get('build_url')
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def mark_execution_started(session, req_data):
|
||||
execution_id = req_data.get('executionId') or req_data.get('execution_id')
|
||||
execution = AutomationDao.get_execution_by_id(session, execution_id)
|
||||
if not execution:
|
||||
return 0, '未查询到对应执行记录'
|
||||
start_time = req_data.get('startTime') or req_data.get('start_time') or datetime.now()
|
||||
return AutomationDao.update_execution_by_id(session, execution_id, {
|
||||
'status': AutomationService.STATUS_RUNNING,
|
||||
'jenkins_job_name': req_data.get('jobName') or req_data.get('job_name') or execution.jenkins_job_name,
|
||||
'jenkins_build_number': req_data.get('buildNumber') or req_data.get('build_number'),
|
||||
'jenkins_build_url': req_data.get('buildUrl') or req_data.get('build_url'),
|
||||
'console_url': req_data.get('consoleUrl') or req_data.get('console_url'),
|
||||
'start_time': start_time
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def save_case_result(session, req_data):
|
||||
execution_id = req_data.get('executionId') or req_data.get('execution_id')
|
||||
execution_case_id = req_data.get('executionCaseId') or req_data.get('execution_case_id')
|
||||
case_id = req_data.get('caseId') or req_data.get('case_id')
|
||||
if not execution_id or (not execution_case_id and not case_id):
|
||||
return 0, 'executionId、executionCaseId/caseId 为必传参数'
|
||||
execution_case = AutomationDao.get_execution_case_by_id(session, execution_case_id) if execution_case_id else None
|
||||
if not execution_case and case_id:
|
||||
execution_case = AutomationDao.get_execution_case_by_unique(session, execution_id, case_id, req_data.get('planCaseId') or req_data.get('plan_case_id'))
|
||||
if not execution_case:
|
||||
return 0, '未查询到对应执行明细'
|
||||
update_info = {
|
||||
'status': int(req_data.get('status')) if req_data.get('status') is not None else execution_case.status,
|
||||
'pytest_nodeid': req_data.get('pytestNodeid') or req_data.get('pytest_nodeid'),
|
||||
'result_message': req_data.get('resultMessage') or req_data.get('result_message'),
|
||||
'error_message': req_data.get('errorMessage') or req_data.get('error_message'),
|
||||
'stack_trace': req_data.get('stackTrace') or req_data.get('stack_trace'),
|
||||
'report_url': req_data.get('reportUrl') or req_data.get('report_url'),
|
||||
'duration_seconds': req_data.get('durationSeconds') or req_data.get('duration_seconds'),
|
||||
'started_time': req_data.get('startedTime') or req_data.get('started_time') or execution_case.started_time,
|
||||
'finished_time': req_data.get('finishedTime') or req_data.get('finished_time') or datetime.now(),
|
||||
'ext': req_data.get('ext') if req_data.get('ext') is not None else execution_case.ext
|
||||
}
|
||||
update_id, err_msg = AutomationDao.update_execution_case_by_id(session, execution_case.id, update_info)
|
||||
if err_msg:
|
||||
return update_id, err_msg
|
||||
execution_case = AutomationDao.get_execution_case_by_id(session, execution_case.id)
|
||||
if execution_case and execution_case.plan_case_id:
|
||||
AutomationService.sync_plan_case_result(session, execution_case)
|
||||
AutomationService.refresh_execution_summary(session, execution_id)
|
||||
execution = AutomationDao.get_execution_by_id(session, execution_id)
|
||||
if execution and execution.plan_id:
|
||||
AutomationService.refresh_plan_status(session, execution.plan_id)
|
||||
return execution_case.id, ''
|
||||
|
||||
@staticmethod
|
||||
def finish_execution(session, req_data):
|
||||
execution_id = req_data.get('executionId') or req_data.get('execution_id')
|
||||
execution = AutomationDao.get_execution_by_id(session, execution_id)
|
||||
if not execution:
|
||||
return 0, '未查询到对应执行记录'
|
||||
end_time = req_data.get('endTime') or req_data.get('end_time') or datetime.now()
|
||||
start_time = req_data.get('startTime') or req_data.get('start_time') or execution.start_time
|
||||
update_info = {
|
||||
'jenkins_build_number': req_data.get('buildNumber') or req_data.get('build_number') or execution.jenkins_build_number,
|
||||
'jenkins_build_url': req_data.get('buildUrl') or req_data.get('build_url') or execution.jenkins_build_url,
|
||||
'console_url': req_data.get('consoleUrl') or req_data.get('console_url') or execution.console_url,
|
||||
'report_url': req_data.get('reportUrl') or req_data.get('report_url') or execution.report_url,
|
||||
'start_time': start_time,
|
||||
'end_time': end_time,
|
||||
'duration_seconds': req_data.get('durationSeconds') or req_data.get('duration_seconds') or AutomationService.calc_duration_seconds(start_time, end_time)
|
||||
}
|
||||
update_id, err_msg = AutomationDao.update_execution_by_id(session, execution_id, update_info)
|
||||
if err_msg:
|
||||
return update_id, err_msg
|
||||
AutomationService.refresh_execution_summary(session, execution_id, force_finish=True)
|
||||
execution = AutomationDao.get_execution_by_id(session, execution_id)
|
||||
if execution and execution.plan_id:
|
||||
AutomationService.refresh_plan_status(session, execution.plan_id)
|
||||
return int(execution_id), ''
|
||||
|
||||
@staticmethod
|
||||
def abort_execution(session, req_data):
|
||||
execution_id = req_data.get('executionId') or req_data.get('execution_id')
|
||||
execution = AutomationDao.get_execution_by_id(session, execution_id)
|
||||
if not execution:
|
||||
return 0, '未查询到对应执行记录'
|
||||
case_items, _ = AutomationDao.list_execution_case_by_filters(session, [AutoExecutionCase.execution_id == int(execution_id), AutoExecutionCase.status.in_([0, 1])], 1, 100000)
|
||||
for item in case_items:
|
||||
AutomationDao.update_execution_case_by_id(session, item.id, {'status': AutomationService.CASE_STATUS_CANCELED, 'finished_time': datetime.now()})
|
||||
update_id, err_msg = AutomationDao.update_execution_by_id(session, execution_id, {
|
||||
'status': int(req_data.get('status') or AutomationService.STATUS_CANCELED),
|
||||
'trigger_message': req_data.get('message') or req_data.get('trigger_message'),
|
||||
'jenkins_build_number': req_data.get('buildNumber') or req_data.get('build_number') or execution.jenkins_build_number,
|
||||
'console_url': req_data.get('consoleUrl') or req_data.get('console_url') or execution.console_url,
|
||||
'end_time': datetime.now()
|
||||
})
|
||||
if err_msg:
|
||||
return update_id, err_msg
|
||||
AutomationService.refresh_execution_summary(session, execution_id, keep_terminal_status=True)
|
||||
execution = AutomationDao.get_execution_by_id(session, execution_id)
|
||||
if execution and execution.plan_id:
|
||||
AutomationService.refresh_plan_status(session, execution.plan_id)
|
||||
return int(execution_id), ''
|
||||
|
||||
@staticmethod
|
||||
def sync_plan_case_result(session, execution_case):
|
||||
status = AutomationService.PLAN_CASE_STATUS_MAP.get(execution_case.status)
|
||||
update_info = {
|
||||
'actual_result': execution_case.error_message or execution_case.result_message,
|
||||
'executed_time': execution_case.finished_time or datetime.now(),
|
||||
'execution_duration': execution_case.duration_seconds
|
||||
}
|
||||
if status is not None:
|
||||
update_info['status'] = status
|
||||
AutomationDao.update_plan_case_result(session, execution_case.plan_case_id, update_info)
|
||||
|
||||
@staticmethod
|
||||
def refresh_execution_summary(session, execution_id, force_finish=False, keep_terminal_status=False):
|
||||
summary = AutomationDao.count_execution_case_summary(session, execution_id)
|
||||
execution = AutomationDao.get_execution_by_id(session, execution_id)
|
||||
if not execution:
|
||||
return
|
||||
update_info = {
|
||||
'total_count': summary.get('total', 0),
|
||||
'pending_count': summary.get(0, 0),
|
||||
'running_count': summary.get(1, 0),
|
||||
'passed_count': summary.get(2, 0),
|
||||
'failed_count': summary.get(3, 0),
|
||||
'blocked_count': summary.get(4, 0),
|
||||
'skipped_count': summary.get(5, 0),
|
||||
'not_found_count': summary.get(6, 0)
|
||||
}
|
||||
total = summary.get('total', 0)
|
||||
running_count = summary.get(1, 0)
|
||||
finished_count = summary.get(2, 0) + summary.get(3, 0) + summary.get(4, 0) + summary.get(5, 0) + summary.get(6, 0) + summary.get(7, 0)
|
||||
if not keep_terminal_status:
|
||||
if running_count > 0:
|
||||
update_info['status'] = AutomationService.STATUS_RUNNING
|
||||
elif total > 0 and finished_count == total:
|
||||
if summary.get(3, 0) + summary.get(4, 0) + summary.get(6, 0) > 0:
|
||||
update_info['status'] = AutomationService.STATUS_FAILED
|
||||
else:
|
||||
update_info['status'] = AutomationService.STATUS_SUCCESS
|
||||
if force_finish or (total > 0 and finished_count == total):
|
||||
end_time = execution.end_time or datetime.now()
|
||||
update_info['end_time'] = end_time
|
||||
if execution.start_time:
|
||||
update_info['duration_seconds'] = AutomationService.calc_duration_seconds(execution.start_time, end_time)
|
||||
AutomationDao.update_execution_by_id(session, execution_id, update_info)
|
||||
|
||||
@staticmethod
|
||||
def refresh_plan_status(session, plan_id):
|
||||
PlanService.refresh_plan_status(session, plan_id)
|
||||
|
||||
@staticmethod
|
||||
def calc_duration_seconds(start_time, end_time):
|
||||
if not start_time or not end_time:
|
||||
return None
|
||||
if isinstance(start_time, str):
|
||||
start_time = datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S')
|
||||
if isinstance(end_time, str):
|
||||
end_time = datetime.strptime(end_time, '%Y-%m-%d %H:%M:%S')
|
||||
return int((end_time - start_time).total_seconds())
|
||||
Reference in New Issue
Block a user