Exemplo n.º 1
0
class JobsMonitor(object):
    def __init__(self):
        self.setting = SettingModule()

    # 监控任务状态
    @gen.coroutine
    def jobs_status(self):
        try:
            s_setting, total = yield self.setting.get_settings_list(
                s_type='job', status=0, pj_status=1, limit=None)
            for row in s_setting:
                if int(time.time()) > int(float(row.name)):
                    yield self.setting.edit_setting(sid=row.id, status=1)
        except Exception as e:
            log.warning(e)

    # 执行定时任务
    @gen.coroutine
    def run_jobs(self):
        try:
            s_setting, total = yield self.setting.get_settings_list(
                s_type='job', status=1, pj_status=1, limit=None)
            for row in s_setting:
                test = TestRunner(row.project_id, row.id)
                test.run_job()
        except Exception as e:
            log.warning(e)
Exemplo n.º 2
0
class JobsMonitor(object):
    def __init__(self):
        self.project = ProjectModule()
        self.setting = SettingModule()
        self.option_func = OptionsFunction()
        self.common_func = CommonFunction()
        self.thread_func = ThreadFunction()
        self.jenkins_server = jenkins.Jenkins(url=jenkins_url,
                                              username=jenkins_user,
                                              password=jenkins_password,
                                              timeout=5)

    # 监控任务状态
    @gen.coroutine
    def jobs_status(self):
        try:
            job_list, total = yield self.setting.get_settings_list(
                s_type=['job', 'jobG', 'jobA'], status=[0, 2], pj_status=1)
            for job in job_list:
                desc = json.loads(job.value)
                if (desc.get('buildEnv')
                        or desc.get('dayBuild')) and job.status == 0 and int(
                            time.time()) > int(job.createTime.timestamp()):
                    yield self.setting.edit_setting(sid=job.id, status=1)
                    continue
                job_name = desc.get(
                    'jobName') if job.type != 'job' else jenkins_jacoco
                if job_name and job.status == 2:
                    if job.type != 'job':
                        last_build_number = self.jenkins_server.get_job_info(
                            job_name).get('lastBuild').get('number')
                        last_build = self.jenkins_server.get_build_info(
                            job_name, last_build_number)
                        status = None if last_build.get('building') else 0
                    else:
                        status = None
                    if (job.type != 'job' and last_build.get('queueId')
                            == desc.get('queueId')) or (job_name
                                                        == jenkins_jacoco):
                        if (job.type != 'job' and status is None
                                and desc['url'] == last_build.get('url')) or (
                                    job_name == jenkins_jacoco):
                            if desc.get('jacocoId'):
                                jacoco_job = self.jenkins_server.get_job_info(
                                    jenkins_jacoco)
                                first_build_number = jacoco_job.get(
                                    'firstBuild').get('number')
                                last_build_number = jacoco_job.get(
                                    'lastBuild').get('number')
                                for num in range(first_build_number,
                                                 last_build_number + 1):
                                    try:
                                        jacoco = self.jenkins_server.get_build_info(
                                            jenkins_jacoco, num)
                                        if jacoco.get('queueId') != desc.get(
                                                'jacocoId'):
                                            continue
                                        if not jacoco.get(
                                                'building') and jacoco.get(
                                                    'result') == 'FAILURE':
                                            day_build_env = desc.get(
                                                'buildEnv') or desc.get(
                                                    'dayBuild')
                                            apps = yield self.option_func.get_env_info(
                                                eid=day_build_env)
                                            stop_time = time.strftime(
                                                '%Y-%m-%d %H:%M:%S',
                                                time.localtime(time.time() +
                                                               3600 * 24))
                                            parameters = [
                                                ('ACTION', 'Monitoring'),
                                                ('STOPTIME', stop_time),
                                                ('CLEAN', 'No'),
                                                ('JOB_ID', job.name)
                                            ]
                                            for app in [
                                                    a.get('details')
                                                    for a in apps
                                            ]:
                                                for ap in app:
                                                    if ap.get('title').lower(
                                                    ).find('linux_app') != -1:
                                                        parameters.append(
                                                            ('ENV',
                                                             ap.get('ip')))
                                            if len(parameters) > 4:
                                                version_file = os.path.join(
                                                    static_path, 'diffAPP',
                                                    job.name)
                                                if not os.path.isdir(
                                                        version_file):
                                                    os.makedirs(version_file)
                                                version_file = os.path.join(
                                                    version_file,
                                                    'jacoco_app_version.txt')
                                                if os.path.isfile(
                                                        version_file):
                                                    os.remove(version_file)
                                                for app in (desc.get('runApps')
                                                            or []):
                                                    parameters.append(
                                                        ('APP', app))
                                                jacoco_id = self.jenkins_server.build_job(
                                                    jenkins_jacoco,
                                                    parameters=parameters)
                                                desc['jacocoId'] = jacoco_id
                                                yield self.setting.edit_setting(
                                                    sid=job.id, value=desc)
                                    except Exception as e:
                                        log.error(e)
                                        continue
                            continue
                        desc['url'] = last_build.get('url')
                        cycle = desc.get('cycle')
                        if cycle == 'hour':
                            next_time = job.createTime + timedelta(hours=1)
                        elif cycle == 'day':
                            next_time = job.createTime + timedelta(days=1)
                        elif cycle == 'week':
                            next_time = job.createTime + timedelta(weeks=1)
                        elif cycle == 'mouth':
                            next_time = job.createTime + timedelta(days=30)
                        elif cycle == 'year':
                            next_time = job.createTime + timedelta(days=365)
                        else:
                            next_time = job.createTime
                            if status == 0: status = 3
                        if status in [0, 3] and desc.get('jacocoId'):
                            jacoco_job = self.jenkins_server.get_job_info(
                                jenkins_jacoco)
                            first_build_number = jacoco_job.get(
                                'firstBuild').get('number')
                            last_build_number = jacoco_job.get(
                                'lastBuild').get('number')
                            for num in range(first_build_number,
                                             last_build_number + 1):
                                try:
                                    jacoco = self.jenkins_server.get_build_info(
                                        jenkins_jacoco, num)
                                    if jacoco.get('queueId') != desc.get(
                                            'jacocoId'):
                                        continue
                                    self.jenkins_server.stop_build(
                                        jenkins_jacoco, num)
                                except Exception as e:
                                    log.error(e)
                                    continue
                                apps = yield self.option_func.get_env_info(
                                    eid=desc.get('buildEnv')
                                    or desc.get('dayBuild'))
                                desc['buildEnv'] = ''
                                parameters = [('ACTION', 'Reporting'),
                                              ('HIS_BUILD_ID', num),
                                              ('CLEAN', 'Yes'),
                                              ('JOB_ID', job.name)]
                                for app in [a.get('details') for a in apps]:
                                    for ap in app:
                                        if ap.get('title').lower().find(
                                                'linux_app') != -1:
                                            parameters.append(
                                                ('ENV', ap.get('ip')))
                                if len(parameters) > 4:
                                    for app in (desc.get('runApps') or []):
                                        parameters.append(('APP', app))
                                    self.jenkins_server.build_job(
                                        jenkins_jacoco, parameters=parameters)
                        yield self.setting.edit_setting(
                            sid=job.id,
                            value=desc,
                            status=status,
                            create_time=next_time.strftime('%Y-%m-%d %H:%M:%S')
                            if status in [0, 3] else None)
        except Exception as e:
            log.warning(e)

    # 执行定时任务
    @gen.coroutine
    def run_jobs(self):
        try:
            job_list, total = yield self.setting.get_settings_list(
                s_type=['jobG', 'jobA'], status=1, pj_status=1)
            for job in job_list:
                desc = json.loads(job.value)
                job_name = desc.get('jobName')
                day_build_env = desc.get('buildEnv') or desc.get('dayBuild')
                if not job_name or not day_build_env:
                    continue
                data = munchify(
                    dict(env=day_build_env,
                         type='api' if job.type == 'jobA' else 'gui',
                         exec=job_name))
                yield self.option_func.get_hosts_info(data)
                yield self.option_func.get_mysql_jdbc(eid=day_build_env,
                                                      data=data)
                yield self.option_func.get_cases_info(job, data)
                queue_id = self.jenkins_server.build_job(
                    job_name, parameters=dict(JOB_ID=job.name))
                if queue_id:
                    apps = yield self.option_func.get_env_info(
                        eid=day_build_env)
                    stop_time = time.strftime(
                        '%Y-%m-%d %H:%M:%S',
                        time.localtime(time.time() + 3600 * 24))
                    parameters = [('ACTION', 'Monitoring'),
                                  ('STOPTIME', stop_time), ('CLEAN', 'Yes'),
                                  ('JOB_ID', job.name)]
                    for app in [a.get('details') for a in apps]:
                        for ap in app:
                            if ap.get('title').lower().find('linux_app') != -1:
                                parameters.append(('ENV', ap.get('ip')))
                    if len(parameters) > 4:
                        version_file = os.path.join(static_path, 'diffAPP',
                                                    job.name)
                        if not os.path.isdir(version_file):
                            os.makedirs(version_file)
                        version_file = os.path.join(version_file,
                                                    'jacoco_app_version.txt')
                        if os.path.isfile(version_file):
                            os.remove(version_file)
                        for app in (desc.get('runApps') or []):
                            parameters.append(('APP', app))
                        jacoco_id = self.jenkins_server.build_job(
                            jenkins_jacoco, parameters=parameters)
                        desc['jacocoId'] = jacoco_id
                    desc['queueId'] = queue_id
                    desc['url'] = self.jenkins_server.get_job_info(
                        job_name).get('url')
                    yield self.setting.edit_setting(sid=job.id,
                                                    value=desc,
                                                    status=2)
        except Exception as e:
            log.warning(e)

    # 申请关闭外网权限
    @gen.coroutine
    def close_network(self):
        try:
            now_date = time.strftime('%Y-%m-%d')
            now_time = time.strftime('%H:%M:%S')
            if now_time > '08:30:00':
                server, total = yield self.setting.get_settings_list(
                    s_type='env', status=1)
                ips = list()
                for svr in server:
                    desc = json.loads(svr.value)
                    if (svr.createTime.strftime('%Y-%m-%d') < now_date or
                        (svr.createTime.strftime('%Y-%m-%d') == now_date
                         and now_time > svr.createTime.strftime('%H:%M:%S'))
                        ) and desc.get('network') == 'yes' and desc.get('ip'):
                        ips.append(desc.get('ip').strip())
                if set(ips):
                    title = '测试环境申请关闭外网权限'
                    mail_content = '''
                    <p>Hi 你好!</p>
                    <p style="padding-left:30px;">测试人员已完成测试任务,申请关闭以下测试环境服务器外网权限。请帮忙处理一下,3ks~</p>
                    <p style="padding-left:30px;">1、服务器</p>
                    <p style="padding-left:60px;">{}</p>    
                                    '''.format('</br>'.join(set(ips)))
                    res, msg = yield self.common_func.send_email(
                        subject=title,
                        content=mail_content,
                        to=net_mail_to,
                        cc=net_mail_cc)
                    if res:
                        log.info(msg)
                        for svr in server:
                            desc = json.loads(svr.value)
                            if (svr.createTime.strftime('%Y-%m-%d') < now_date
                                    or (svr.createTime.strftime('%Y-%m-%d')
                                        == now_date and now_time >
                                        svr.createTime.strftime('%H:%M:%S'))
                                ) and desc.get(
                                    'network') == 'yes' and desc.get('ip'):
                                desc['network'] = 'no'
                                yield self.setting.edit_setting(sid=svr.id,
                                                                value=desc)
                    else:
                        log.warn(msg)
        except Exception as e:
            log.error(e)

    # 同步准生产数据库表
    @gen.coroutine
    def sync_db_tables(self):
        dumps = dict()
        db_path = os.path.join(static_path, 'syncDB', 'lasTables')
        if not os.path.isdir(db_path):
            os.makedirs(db_path)
        flag_file = os.path.join(db_path, 'TAG')
        old_date = ''
        if os.path.isfile(flag_file):
            with open(flag_file, 'r') as fp:
                old_date = fp.read()
        now_date = time.strftime('%Y%m%d')
        if old_date != now_date:
            env_list, total = yield self.project.get_projects_list(
                p_type='env', status=1, search='准生产')
            for env in env_list:
                details, total = yield self.setting.get_settings_list(
                    s_type='env', name=env.name)
                for detail in details:
                    desc = json.loads(detail.value)
                    if desc.get('type') != 'APPLICATION' and desc.get(
                            'title').upper().find('MYSQL') == -1:
                        continue
                    dumps[detail.id] = dict(
                        ip=desc.get('ip').strip(),
                        port=desc.get('port').strip(),
                        user=desc.get('user').strip(),
                        password=desc.get('password').strip(),
                        dbs=desc.get('description').split(','))
                break
            with open(flag_file, 'w') as fp:
                fp.write(time.strftime('%Y%m%d'))
            for key in dumps:
                for db in dumps[key]['dbs']:
                    tmp_path = os.path.join(db_path, '{}_tmp.txt'.format(db))
                    shell_dump = '''cd {}
                    /opt/lampp/bin/mysqldump -h{} -P{} -u{} -p{} -d {} > {}'''.format(
                        root_160, dumps[key]['ip'], dumps[key]['port'],
                        dumps[key]['user'], dumps[key]['password'], db,
                        'lasTables/{}_tmp.txt'.format(db))
                    res, msg = yield self.thread_func.exec_remote_shell(
                        shell=shell_dump,
                        host=host_160,
                        port=port_160,
                        username=user_160,
                        password=password_160)
                    if res and os.path.isfile(tmp_path):
                        with open(tmp_path, 'r', encoding='utf8') as fp:
                            lines = fp.readlines()
                        tables = list()
                        for line in lines:
                            if line.find('DROP TABLE IF EXISTS') == -1:
                                continue
                            table = re.findall(r'`(\w+)`;', line)
                            table and tables.append('{}\n'.format(table[0]))
                        with open(os.path.join(db_path, '{}.txt'.format(db)),
                                  'w') as fp:
                            fp.writelines(tables)
                        os.remove(tmp_path)
                    else:
                        log.info(msg)
        old_time = time.time() - 30 * 24 * 3600
        old_path = os.path.join(static_path, 'syncDB',
                                time.strftime('%Y%m%d', time.gmtime(old_time)))
        if os.path.isdir(old_path):
            shutil.rmtree(old_path)
Exemplo n.º 3
0
class TestRunner(object):
    Resolver.configure('tornado.netutil.ThreadedResolver')

    def __init__(self, pid=0, jid=0):
        self.default_headers = {
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'User-Agent':
            'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36',
            'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.6,en;q=0.4',
            'Cookie': ''
        }
        self.headers = dict()
        self.setting = SettingModule()
        self.common_func = CommonFunction()
        self.option_func = OptionsFunction()
        self.pid = pid
        self.jid = jid

    # 获取请求头
    def get_headers(self, headers=''):
        flag, headers = self.common_func.convert_to_list_or_dict(
            string=headers, s_type='dict')
        self.headers = dict()
        for key in self.default_headers:
            self.headers[key] = self.default_headers[key]
        if flag:
            for key in headers.keys():
                self.headers[key] = headers[key]
        else:
            headers = headers.splitlines()
            for header in headers:
                header = header.strip().split(sep=':', maxsplit=1)
                if len(header) == 2:
                    name = header[0].strip()
                    value = header[1].strip()
                    self.headers[name] = value
        return self.headers

    # 加解密操作
    @gen.coroutine
    def __do_crypt(self, do='encrypt', body=None, crypt_key=''):
        crypt_info = yield self.option_func.get_crypt_info(pid=self.pid, do=do)
        if not crypt_info:
            return body
        func = crypt_info.function
        try:
            if func:
                if crypt_key:
                    flag, body = self.common_func.convert_to_list_or_dict(
                        string=body, s_type='dict')
                    if not flag and isinstance(body, str) and re.match(
                            r'^.*=.*(&.*=.*)*$', body) is not None:
                        body = self.common_func.url_query_decode(body)
                        if isinstance(body, dict):
                            flag = True
                    if flag:
                        source = body[crypt_key]
                        if not isinstance(source, str):
                            try:
                                source = json.dumps(source, ensure_ascii=False)
                            except Exception as e:
                                log.warning(e)
                                if isinstance(source, bytes):
                                    source = source.decode('utf8',
                                                           errors='ignore')
                                else:
                                    source = str(source)
                        body[crypt_key] = func(source, crypt_info['key'],
                                               crypt_info['iv'],
                                               crypt_info['mode'])
                else:
                    if not isinstance(body, str):
                        try:
                            body = json.dumps(body, ensure_ascii=False)
                        except Exception as e:
                            log.warning(e)
                            if isinstance(body, bytes):
                                body = body.decode('utf8', errors='ignore')
                            else:
                                body = str(body)
                    body = func(body, crypt_info['key'], crypt_info['iv'],
                                crypt_info['mode'])
        except Exception as e:
            log.warning(e)
        return body

    # 尝试将请求数据转换成字典
    def __parse_body_arguments(self, body):
        flag, body = self.common_func.convert_to_list_or_dict(string=body,
                                                              s_type='dict')
        if not flag and isinstance(body, str) and re.match(
                r'^.*=.*(&.*=.*)*$', body) is not None:
            body = self.common_func.url_query_decode(body)
            if isinstance(body, dict):
                flag = True
        if not flag:
            self.headers = self.headers if len(
                self.headers) != 0 else self.default_headers
            try:
                request_body = dict()
                if re.match(r'^.*=.*(&.*=.*)*$', body) is not None:
                    parse_body_arguments(
                        content_type=self.headers['Content-Type'],
                        body=body,
                        arguments=request_body,
                        files=request_body,
                        headers=self.headers)
                if len(request_body) > 0:
                    body = request_body
                for key in body:
                    if isinstance(body[key], list):
                        body[key] = body[key][0]
                        body[key] = body[key].decode('utf8', errors='ignore')
                flag = True
            except Exception as e:
                log.warning(e)
                flag = False
        return flag, body

    # 获取请求响应数据
    @gen.coroutine
    def __get_body(self, body='', do='encrypt', name='', crypt_key=''):
        try:
            body = ast.literal_eval(body)
        except Exception as e:
            log.warning(e)
            if do == 'encrypt' and crypt_key != '':
                flag, body = self.__parse_body_arguments(body)
        if isinstance(body, dict):
            if name != 'none':
                if do == 'encrypt' and crypt_key == '':
                    body = urlencode(body, encoding='utf8', quote_via=quote)
                body = yield self.__do_crypt(do=do,
                                             body=body,
                                             crypt_key=crypt_key)
            if isinstance(body, dict):
                if do == 'encrypt' and self.headers['Content-Type'].find(
                        'x-www-form-urlencoded') != -1:
                    body = urlencode(body, encoding='utf8', quote_via=quote)
                else:
                    body = json.dumps(body, ensure_ascii=False)
        else:
            if name != 'none':
                body = yield self.__do_crypt(do=do, body=body, crypt_key='')
            if isinstance(body, dict):
                body = json.dumps(body, ensure_ascii=False)
        return body

    # 解析Host配置
    @gen.coroutine
    def __parse_host(self, url='', env='none'):
        urls = self.common_func.url_split(url=url)
        host = urls.host
        ips, total = yield self.setting.get_settings_list(pid=self.pid,
                                                          s_type='host',
                                                          name=host,
                                                          pj_status=1,
                                                          limit=None)
        for row in ips:
            if env != 'none':
                url = '{}://{}:{}{}'.format(urls.scheme, env, urls.port,
                                            urls.path)
                break
            elif row.status == 1:
                url = '{}://{}:{}{}'.format(urls.scheme, row.value, urls.port,
                                            urls.path)
                break
        self.headers['Host'] = urls.netloc
        return url

    # 解析接口返回值全字段检查配置
    def __parse_check_key(self, check_key):
        keys = []
        top = []
        rex = re.compile(
            r'^\[\w+=\d\|(int|float|num|str|/.*/|date|time|datetime|list|dict)('
            r',\w+=\d\|(int|float|num|str|/.*/|date|time|datetime|list|dict))*\]$'
        )
        for row in check_key.splitlines():
            row = row.strip().split(sep='.', maxsplit=1)
            if len(row) == 2:
                if re.match(rex, row[1]) is not None:
                    deeps = [row[1]]
                else:
                    deeps = row[1].split(sep='.', maxsplit=1)
                if re.match(r'^\[\d+\]$', deeps[0]) is None:
                    top.append('{}=1|dict'.format(row[0]))
                else:
                    top.append('{}=1|list'.format(row[0]))
                deep = '{}.'.format(row[0])
                tmp_key = []
                for i in range(len(row[1].split('.'))):
                    if len(deeps) == 2 and re.match(rex, deeps[1]) is not None:
                        deep += '{}.'.format(deeps[0])
                        for j in deeps[1][1:-1].split(','):
                            tmp_key.append(j)
                        break
                    elif re.match(rex, deeps[0]) is not None:
                        for j in deeps[0][1:-1].split(','):
                            tmp_key.append(j)
                        break
                    deep += '{}.'.format(deeps[0])
                    if len(deeps) == 2:
                        if re.match(rex, deeps[1]) is not None:
                            deeps = deeps[1]
                        else:
                            deeps = deeps[1].split(sep='.', maxsplit=1)
                keys.append(dict(deep=deep[:-1], keys=tmp_key, result=dict()))
            else:
                top.append(row[0])
        keys.append(dict(deep='top', keys=list(set(top)), result=dict()))
        return keys

    # 返回值全字段检查结果判断
    def __check_key_result(self, body, check_key, key, k):
        check_key[k]['result'][key[0]] = True
        key[1] = key[1].split(sep='|', maxsplit=1)
        require = key[1][0]
        key_type = key[1][1]
        if isinstance(body[key[0]], str):
            body[key[0]] = body[key[0]].strip()
        if require == '1' and (body[key[0]] == '' or body[key[0]] is None):
            check_key[k]['result'][key[0]] = False
        elif body[key[0]] != '' and body[key[0]] is not None:
            if re.match(r'^/.*/$', key_type):
                rex = re.compile(key_type[1:-1])
                if re.match(rex, body[key[0]]) is None:
                    check_key[k]['result'][key[0]] = False
            elif key_type == 'int' and not isinstance(body[key[0]], int):
                check_key[k]['result'][key[0]] = False
            elif key_type == 'float' and not isinstance(body[key[0]], float):
                check_key[k]['result'][key[0]] = False
            elif key_type == 'num' and not isinstance(
                    body[key[0]], int) and not isinstance(body[key[0]], float):
                check_key[k]['result'][key[0]] = False
            elif key_type == 'str' and not isinstance(body[key[0]], str):
                check_key[k]['result'][key[0]] = False
            elif key_type == 'list' and not isinstance(body[key[0]], list):
                check_key[k]['result'][key[0]] = False
            elif key_type == 'dict' and not isinstance(body[key[0]], dict):
                check_key[k]['result'][key[0]] = False
            elif key_type == 'date' and re.match(r'^\d{4}-\d{2}-\d{2}$',
                                                 body[key[0]]) is None:
                check_key[k]['result'][key[0]] = False
            elif key_type == 'time' and re.match(r'^\d{2}:\d{2}:\d{2}$',
                                                 body[key[0]]) is None:
                check_key[k]['result'][key[0]] = False
            elif key_type == 'datetime' and re.match(
                    r'^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}$',
                    body[key[0]]) is None:
                check_key[k]['result'][key[0]] = False
        if not check_key[k]['result'][key[0]]:
            check_key[k]['result']['key_result'] = False
        return body, check_key

    # 解析响应内容
    @gen.coroutine
    def __parse_response(self,
                         response,
                         name='',
                         crypt_key='',
                         checkpoint='',
                         check_key='',
                         correlation='',
                         method='GET',
                         url=''):
        body = response.body if response else ''
        request_body = response.request.body if response else ''
        if isinstance(body, bytes):
            body = body.decode('utf8', errors='ignore')
        if isinstance(request_body, bytes):
            request_body = request_body.decode('utf8', errors='ignore')
        headers_dict = dict(response.headers if response else '')
        headers = ''
        for key in headers_dict:
            headers += '{}: {}\r\n'.format(key, headers_dict[key])
        request_headers_dict = dict(
            response.request.headers if response else '')
        request_headers = ''
        for key in request_headers_dict:
            request_headers += '{}: {}\r\n'.format(key,
                                                   request_headers_dict[key])
        if response:
            error = response.error if not response.error else str(
                response.error)
        else:
            error = str(httpclient.HTTPError(599, 'Timeout while connecting'))
        resp = dict(
            body=body,
            code=response.code if response else 599,
            effective_url=response.effective_url if response else '',
            error=error,
            headers=headers,
            request_headers=request_headers,
            request_body=request_body,
            reason=response.reason if response else 'Timeout while connecting',
            request_time=response.request_time if response else '',
            time_info=response.time_info if response else '',
            method=method,
            url=url)
        resp['body_decrypt'] = yield self.__get_body(body,
                                                     do='decrypt',
                                                     name=name,
                                                     crypt_key=crypt_key)
        resp['checkpoint'] = []
        if checkpoint != '':
            if re.match(r'^/.*/$', checkpoint) is not None:
                rex = re.compile(checkpoint[1:-1])
                if re.findall(rex, resp['body_decrypt']):
                    resp['checkpoint'].append(
                        dict(result=True, checkpoint=checkpoint))
                else:
                    log.warning('检查点 {} 检查不通过'.format(checkpoint))
                    resp['checkpoint'].append(
                        dict(result=False, checkpoint=checkpoint))
            else:
                for check in checkpoint.split('|'):
                    check = check.strip()
                    if resp['body_decrypt'].find(check) != -1:
                        resp['checkpoint'].append(
                            dict(result=True, checkpoint=check))
                    else:
                        log.warning('检查点 {} 检查不通过'.format(check))
                        resp['checkpoint'].append(
                            dict(result=False, checkpoint=check))
        resp['check_key'] = []
        if check_key != '':
            check_key = self.__parse_check_key(check_key)
            for k in range(len(check_key)):
                body = resp['body_decrypt']
                check_key[k]['result']['key_result'] = True
                if check_key[k]['deep'] == 'top':
                    for key in check_key[k]['keys']:
                        if re.match(r'^\[\d+\]$', key) is not None:
                            key = int(key[1:-1])
                            flag, body = self.common_func.convert_to_list_or_dict(
                                body, 'list')
                            if not flag:
                                log.warning('返回值全字段检查 被检查数据格式不是List, 无法继续')
                                if body in ['', '[]', '{}', [], {}]:
                                    check_key[k]['result']['ERROR'] = '被检查数据为空'
                                else:
                                    check_key[k]['result'][
                                        'ERROR'] = '被检查数据类型不是List'
                                check_key[k]['result']['key_result'] = False
                                continue
                            try:
                                body[key]
                            except Exception as e:
                                log.warning(e)
                                check_key[k]['result'][
                                    'ERROR'] = '被检查数据格式不包含List类型'
                                check_key[k]['result']['key_result'] = False
                                continue
                        else:
                            flag, body = self.common_func.convert_to_list_or_dict(
                                body, 'dict')
                            if not flag and isinstance(body, str) and re.match(
                                    r'^.*=.*(&.*=.*)*$', body) is not None:
                                body = self.common_func.url_query_decode(body)
                                if isinstance(body, dict):
                                    flag = True
                            if not flag:
                                log.warning('返回值全字段检查 被检查数据格式不是Dict, 无法继续')
                                if body in ['', '[]', '{}', [], {}]:
                                    check_key[k]['result']['ERROR'] = '被检查数据为空'
                                else:
                                    check_key[k]['result'][
                                        'ERROR'] = '被检查数据类型不是Dict'
                                check_key[k]['result']['key_result'] = False
                                continue
                            key = key.split(sep='=', maxsplit=1)
                            if key[0] not in body.keys():
                                log.warning('返回值全字段检查 {} 检查不通过'.format(
                                    check_key[k]['keys']))
                                check_key[k]['result'][key[0]] = '字段不存在'
                                check_key[k]['result']['key_result'] = False
                                continue
                            else:
                                body, check_key = self.__check_key_result(
                                    body=body,
                                    check_key=check_key,
                                    key=key,
                                    k=k)
                else:
                    keys = check_key[k]['keys']
                    flag, keys = self.common_func.convert_to_list_or_dict(
                        keys, 'list')
                    for key in check_key[k]['deep'].split('.'):
                        if re.match(r'^\[\d+\]$', key) is not None:
                            key = int(key[1:-1])
                            flag, body = self.common_func.convert_to_list_or_dict(
                                body, 'list')
                            if not flag:
                                log.warning('返回值全字段检查 被检查数据格式不是List, 无法继续')
                                if body in ['', '[]', '{}', [], {}]:
                                    check_key[k]['result']['ERROR'] = '被检查数据为空'
                                else:
                                    check_key[k]['result'][
                                        'ERROR'] = '被检查数据类型不是List'
                                check_key[k]['result']['key_result'] = False
                                continue
                        elif not isinstance(body, dict):
                            flag, body = self.common_func.convert_to_list_or_dict(
                                body, 'dict')
                            if not flag and isinstance(body, str) and re.match(
                                    r'^.*=.*(&.*=.*)*$', body) is not None:
                                body = self.common_func.url_query_decode(body)
                                if isinstance(body, dict):
                                    flag = True
                            if not flag:
                                log.warning('返回值全字段检查 被检查数据格式不是Dict, 无法继续')
                                if body in ['', '[]', '{}', [], {}]:
                                    check_key[k]['result']['ERROR'] = '被检查数据为空'
                                else:
                                    check_key[k]['result'][
                                        'ERROR'] = '被检查数据类型不是Dict'
                                check_key[k]['result']['key_result'] = False
                                continue
                        try:
                            body = body[key]
                        except Exception as e:
                            log.warning(e)
                            check_key[k]['result'][
                                'ERROR'] = '被检查数据格式不包含List类型'
                            check_key[k]['result']['key_result'] = False
                            continue
                    flag, body = self.common_func.convert_to_list_or_dict(
                        body, 'dict')
                    if not flag and isinstance(body, str) and re.match(
                            r'^.*=.*(&.*=.*)*$', body) is not None:
                        body = self.common_func.url_query_decode(body)
                        if isinstance(body, dict):
                            flag = True
                    if not flag:
                        log.warning('返回值全字段检查 被检查数据格式不是Dict, 无法继续')
                        if body in ['', '[]', '{}', [], {}]:
                            check_key[k]['result']['ERROR'] = '被检查数据为空'
                        else:
                            check_key[k]['result']['ERROR'] = '被检查数据类型不是Dict'
                        check_key[k]['result']['key_result'] = False
                        continue
                    for key in keys:
                        key = key.split(sep='=', maxsplit=1)
                        if key[0] not in body.keys():
                            log.warning('返回值全字段检查 {} 检查不通过'.format(
                                check_key[k]['keys']))
                            check_key[k]['result'][key[0]] = '字段不存在'
                            check_key[k]['result']['key_result'] = False
                            continue
                        else:
                            body, check_key = self.__check_key_result(
                                body=body, check_key=check_key, key=key, k=k)
            resp['check_key'] = check_key
        resp['correlation'] = dict()
        if response and correlation != '':
            correlations = correlation.split('|')
            correlation = dict()
            for corr in correlations:
                body = resp['body_decrypt']
                cor = corr.split(sep='=', maxsplit=1)
                key = cor[0].strip()
                c_type = 'string'
                words = cor[1].strip()
                if re.match(r'^int\(.+\)$', words) is not None:
                    c_type = 1
                    words = words[4:-1]
                elif re.match(r'^float\(.+\)$', words) is not None:
                    c_type = 1.00
                    words = words[6:-1]
                word = words.split('.')
                correlation[key] = word
                for k in word:
                    if k == 'response_headers' and len(word) != 1:
                        header = words.split(sep='.', maxsplit=1)
                        if len(header) == 2:
                            header_key = header[1]
                            if re.match(r'^/.*/$', header_key) is not None:
                                rex = re.compile(header_key[1:-1])
                                if isinstance(resp['headers'], bytes):
                                    resp['headers'] = resp['headers'].decode(
                                        'utf8', errors='ignore')
                                result = re.findall(rex, resp['headers'])
                                if result:
                                    if isinstance(result[0], tuple):
                                        body = result[0][0]
                                    else:
                                        body = ''
                                        for row in result:
                                            row = row if row != '' else '\n'
                                            body += row
                                else:
                                    body = ''
                            else:
                                body = response.headers.get(header_key)
                            break
                    if k == 'response_body' and len(word) == 1:
                        if isinstance(resp['body'], bytes):
                            resp['body'] = resp['body'].decode('utf8',
                                                               errors='ignore')
                        body = escape.xhtml_escape(resp['body'])
                        break
                    if re.match(r'^/.*/$', words) is not None:
                        rex = re.compile(words[1:-1])
                        if isinstance(resp['body'], bytes):
                            resp['body'] = resp['body'].decode('utf8',
                                                               errors='ignore')
                        result = re.findall(rex, resp['body'])
                        if result:
                            if isinstance(result[0], tuple):
                                body = escape.xhtml_escape(result[0][0])
                            else:
                                body = escape.xhtml_escape(result[0])
                        else:
                            body = ''
                        break
                    if re.match(r'^\[\d+\]$', k) is not None:
                        k = int(k[1:-1])
                        flag, body = self.common_func.convert_to_list_or_dict(
                            body, 'list')
                        if not flag:
                            log.warning('响应数据格式不是List, 无法继续')
                            body = ''
                            break
                    elif not isinstance(body, dict):
                        flag, body = self.common_func.convert_to_list_or_dict(
                            body, 'dict')
                        if not flag and isinstance(body, str) and re.match(
                                r'^.*=.*(&.*=.*)*$', body) is not None:
                            body = self.common_func.url_query_decode(body)
                            if isinstance(body, dict):
                                flag = True
                        if not flag:
                            log.warning('响应数据格式不是Dict, 无法继续')
                            body = ''
                            break
                    try:
                        body = body[k]
                    except Exception as e:
                        log.warning(e)
                        body = ''
                        break
                correlation[key] = body
                try:
                    if isinstance(c_type, int):
                        correlation[key] = int(
                            float(re.sub(r'[^\d+\.]', '', body)))
                    elif isinstance(c_type, float):
                        correlation[key] = float(re.sub(r'[^\d+\.]', '', body))
                except Exception as e:
                    log.warning(e)
            resp['correlation'] = correlation
        flag = True
        if resp['error'] is not None and resp['code'] != 302 and resp[
                'code'] != 301:
            flag = False
        if resp['check_key']:
            for line in resp['check_key']:
                if not line['result']['key_result']:
                    flag = False
                    break
        if resp['checkpoint']:
            for line in resp['checkpoint']:
                if not line['result']:
                    flag = False
                    break
        resp['test_result'] = flag
        return resp

    # 解析自定义参数配置
    @gen.coroutine
    def __parse_custom_param(self, headers, body, correlation_result={}):
        correlation_result = dict(self.common_func.default_param(),
                                  **correlation_result)
        params = yield self.option_func.get_custom_param(
            pid=self.pid, correlation=correlation_result)
        if isinstance(headers, bytes):
            headers = headers.decode('utf8', errors='ignore')
        if isinstance(body, bytes):
            body = body.decode('utf8', errors='ignore')
        for key in correlation_result:
            if not isinstance(correlation_result[key], str):
                correlation_result[key] = str(correlation_result[key])
            if headers.find(key) != -1:
                headers = headers.replace(key, correlation_result[key])
            if body.find(key) != -1:
                body = body.replace(key, correlation_result[key])
        for param in params:
            if headers.find('{%s}' % param['name']) != -1:
                if param['type'] == 'Function':
                    func = param['function']
                    flag, body_dict = self.__parse_body_arguments(body)
                    encrypt = yield self.option_func.get_crypt_info(self.pid)
                    headers = headers.replace('{%s}' % param['name'],
                                              func(body_dict, params, encrypt))
                else:
                    headers = headers.replace('{%s}' % param['name'],
                                              param['value'])
            if body.find('{%s}' % param['name']) != -1:
                if param['type'] == 'Function':
                    func = param['function']
                    flag, body_dict = self.__parse_body_arguments(body)
                    encrypt = yield self.option_func.get_crypt_info(self.pid)
                    body = body.replace('{%s}' % param['name'],
                                        func(body_dict, params, encrypt))
                else:
                    body = body.replace('{%s}' % param['name'], param['value'])
        return headers, body

    # 请求操作
    @gen.coroutine
    def __request_url(self,
                      url='',
                      env='none',
                      method='GET',
                      body='',
                      follow_redirects=True):
        test_client = httpclient.AsyncHTTPClient(max_clients=100)
        argv = dict(method=method,
                    headers=self.headers,
                    follow_redirects=False,
                    request_timeout=600,
                    validate_cert=False,
                    raise_error=False)
        url = yield self.__parse_host(url=url, env=env)
        if method == 'GET':
            url = '{}?{}'.format(url, body)
        elif method == 'POST':
            argv['body'] = body
        try:
            log.info('开始请求接口 {}'.format(url))
            response = yield test_client.fetch(url, **argv)
        except httpclient.HTTPError as e:
            response = e.response
            log.warning('请求接口 {} 异常# {}'.format(
                url, str(response.error if response else e)))
        # test_client.close()
        if response and response.code in [301, 302]:
            for cookie in response.headers.get_list('Set-Cookie'):
                self.headers['Cookie'] += '{};'.format(cookie)
            url = response.headers.get('Location')
            log.info('{} {} {}'.format(response.code, response.reason, url))
            if response.reason.find('Moved') >= 0:
                response = yield self.__request_url(
                    url=url,
                    env=env,
                    method=method,
                    body=body,
                    follow_redirects=follow_redirects)
            elif follow_redirects:
                response = yield self.__request_url(
                    url=url,
                    env=env,
                    method='GET',
                    follow_redirects=follow_redirects)
        log.info('结束请求接口 {}'.format(url))
        return response

    # 生成测试报告
    @gen.coroutine
    def __gen_report(self, job_name, test_suites, start_time, end_time):
        setting = yield self.setting.get_settings_by_range(pid=self.pid,
                                                           s_type='log',
                                                           start=start_time,
                                                           end=end_time,
                                                           sort=self.jid)
        if setting:
            elapsed_time = end_time - start_time
            start_time = time.strftime(
                '%Y-%m-%d %H:%M:%S', time.gmtime(float(start_time) + 3600 * 8))
            end_time = time.strftime('%Y-%m-%d %H:%M:%S',
                                     time.gmtime(float(end_time) + 3600 * 8))
            url, total = yield self.setting.get_settings_list(pid=self.pid,
                                                              s_type='url',
                                                              limit=None)
            overview = dict(name=job_name,
                            start_time=start_time,
                            end_time=end_time,
                            elapsed_time=elapsed_time,
                            total=total,
                            total_test=len(test_suites),
                            success_test=0,
                            fail_test=0,
                            success_rate='0.0 %',
                            report_time=time.strftime('%Y-%m-%d %H:%M:%S'))
            for suite in test_suites:
                suite['total_test'] = len(suite['cases'])
                suite['success_test'] = 0
                suite['fail_test'] = 0
                suite['report'] = []
                suite['result'] = True
            for row in setting:
                res = json.loads(row.value)
                for suite in test_suites:
                    if suite['suite_id'] == row.status:
                        if res['test_result']:
                            suite['success_test'] += 1
                        else:
                            suite['fail_test'] += 1
                            suite['result'] = False
                        suite['report'].append(res)
            for suite in test_suites:
                suite['result'] = suite['result'] if suite[
                    'success_test'] + suite['fail_test'] == suite[
                        'total_test'] else False
                if suite['result']:
                    overview['success_test'] += 1
                else:
                    overview['fail_test'] += 1
            overview['success_rate'] = '{:.2f} %'.format(
                overview['success_test'] / overview['total_test'] * 100)
            result = dict(overview=overview, report=test_suites)
            report_id, msg = yield self.setting.add_setting(
                pid=self.pid,
                s_type='report',
                sort=self.jid,
                name=time.time(),
                value=json.dumps(result, ensure_ascii=False))
            return report_id
        else:
            return False

    # 执行单接口测试
    @gen.coroutine
    def run_test(self,
                 url='',
                 label='',
                 comment='',
                 method='GET',
                 headers='',
                 body='',
                 crypt='none',
                 encrypt_content='',
                 no_test=False,
                 check_key='',
                 decrypt_content='',
                 checkpoint='',
                 env='none',
                 correlation='',
                 correlation_result={},
                 follow_redirects=True):
        headers, body = yield self.__parse_custom_param(
            headers=headers, body=body, correlation_result=correlation_result)
        self.get_headers(headers)
        request_body = yield self.__get_body(body=body,
                                             do='encrypt',
                                             name=crypt,
                                             crypt_key=encrypt_content)
        if no_test:
            return request_body
        resp = yield self.__request_url(url=url,
                                        env=env,
                                        method=method,
                                        body=request_body,
                                        follow_redirects=follow_redirects)
        response = yield self.__parse_response(response=resp,
                                               name=crypt,
                                               crypt_key=decrypt_content,
                                               checkpoint=checkpoint,
                                               check_key=check_key,
                                               correlation=correlation,
                                               method=method,
                                               url=url)
        response['label'] = label
        response['comment'] = comment
        if not resp:
            response['request_body'] = request_body
            for key in self.headers:
                response['request_headers'] += '{}: {}\r\n'.format(
                    key, self.headers[key])
        elif method == 'GET':
            response['request_body'] = request_body
        log.info('响应返回 {}'.format(json.dumps(response, ensure_ascii=False)))
        return response

    # 执行多接口测试
    @gen.coroutine
    def __run_all_test(self, job_name, test_suites):
        if not test_suites:
            return False
        start_time = time.time()
        correlation_result = dict()
        for test in test_suites:
            cases = yield self.setting.get_settings_by_ids(test['cases'])
            for url_info in cases:
                try:
                    url_info = json.loads(url_info.value)
                    resp = yield self.run_test(
                        correlation_result=correlation_result, **url_info)
                    yield self.setting.add_setting(pid=self.pid,
                                                   s_type='log',
                                                   name=time.time(),
                                                   sort=self.jid,
                                                   value=json.dumps(
                                                       resp,
                                                       ensure_ascii=False),
                                                   status=test['suite_id'])
                    correlation_result = dict(correlation_result,
                                              **resp['correlation'])
                except Exception as e:
                    log.error(e)
        end_time = time.time()
        report_id = yield self.__gen_report(job_name, test_suites, start_time,
                                            end_time)
        return report_id

    # 执行排队任务
    @gen.coroutine
    def run_job(self):
        job = yield self.setting.get_setting_by_id(sid=self.jid)
        if job:
            start_time = time.time()
            job_value = json.loads(job.value)
            start_strftime = time.strftime(
                '%Y-%m-%d %H:%M:%S', time.gmtime(float(start_time) + 3600 * 8))
            job_value['overview']['start_time'] = start_strftime
            yield self.setting.edit_setting(sid=job.id,
                                            status=2,
                                            value=json.dumps(
                                                job_value, ensure_ascii=False))
            test_suites = []
            suites = yield self.setting.get_settings_by_ids(
                job_value['testsuite'])
            for suite in suites:
                tests = dict(suite_id=suite.id,
                             suite_name=suite.name,
                             cases=json.loads(suite.value)['cases'])
                test_suites.append(tests)
            result = yield self.__run_all_test(job_value['name'], test_suites)
            if result:
                status = 3
                job_value['lastreport'] = result
            else:
                status = 5
            end_time = time.time()
            elapsed_time = end_time - start_time
            end_time = time.strftime('%Y-%m-%d %H:%M:%S',
                                     time.gmtime(float(end_time) + 3600 * 8))
            job_value['overview']['end_time'] = end_time
            job_value['overview']['elapsed_time'] = elapsed_time
            name = float(job.name)
            if job_value['overview']['cycle_time'] != 0:
                while name < time.time():
                    name += job_value['overview']['cycle_time']
                job_value['overview']['plan_time'] = time.strftime(
                    '%Y-%m-%d %H:%M:%S', time.gmtime(name + 3600 * 8))
                status = 0
            yield self.setting.edit_setting(sid=job.id,
                                            status=status,
                                            name=name,
                                            value=json.dumps(
                                                job_value, ensure_ascii=False))
            yield Mail().send_html_report(result)