def http_tell_test_task_status(task_id, status):
    # 准备参数
    # 用户界面的执行应用注册接口地址
    server_ip = app_config.get('server', 'host')
    server_port = app_config.get('server', 'port')
    try:
        api_response = urllib3.PoolManager(1).request(
            method='post',
            url='http://' + server_ip + ':' + str(server_port) +
            '/api/task/testTaskFinished.json',
            headers={'Content-Type': 'application/json;charset=UTF-8'},
            body=json.dumps({
                'taskId': task_id,
                'uuid': app_config.get('worker', 'uuid'),
                'status': status
            }))
    except Exception as e:
        app_logger.error('回传测试任务结束时间失败,失败原因:' + repr(e))
        return False
    else:
        if api_response.status == 200:
            # 如果返回码为200则成功,否则失败
            api_response_dict = json.loads(api_response.data.decode('utf-8'))
            if api_response_dict['error_code'] != 200:
                app_logger.error('回传测试任务结束时间失败,失败原因:' +
                                 api_response_dict['error_msg'])
            else:
                app_logger.debug('回传测试任务结束时间成功')
        else:
            app_logger.error('回传测试任务结束时间失败,失败原因:用户界面服务异常')
            return False
Example #2
0
 def set(self, server_ip, startup_date, startup_flag, error_info=None):
     """
         记录启动日志
         :param server_ip: 注册服务器的ip地址
         :param startup_date: 启动日期
         :param startup_flag: 启动与注册结果状态
         :param error_info: 启动与注册错误信息,若成功则不记录
         :return: data: True/False
     """
     try:
         value = json.dumps(
             {
                 "server_ip": server_ip,
                 "date": startup_date,
                 "flag": startup_flag,
                 "error_info": error_info,
             },
             ensure_ascii=False
         )
         redis_pool.lpush(self.key, value)
     except Exception as e:
         msg = "startUpLog程序启动日志入库失败:" + repr(e)
         app_logger.error(msg)
         return False
     else:
         msg = "startUpLog程序启动日志入库成功"
         app_logger.debug(msg)
         return True
 def init_vusers(self):
     # 首先初始化出来一颗原始的插件树用以基本检查
     self.init_plugin_tree(self.plugin_data[0])
     # 如果基本初始化失败则不操作协程池
     if self.flow_init_result:
         # 初始化协程池
         try:
             self.gevent_pool = GeventPool(self.base_vuser_num)
         except Exception as e:
             msg = '测试任务虚拟用户并发池创建失败:%s' % repr(e)
             self.flow_init_result = False
             app_logger.error(msg)
             self.trans_init_log(msg)
         else:
             msg = '测试任务虚拟用户并发池创建成功'
             app_logger.debug(msg)
             self.trans_init_log(msg)
             vuser_index = 1
             free_count = self.gevent_pool.free_count()
             while free_count > 0:
                 # 每个虚拟用户拥有属于自己的插件树,互不干扰
                 plugin_tree = self.init_plugin_tree(
                     self.plugin_data[0], vuser_index)
                 self.gevent_pool.spawn(self.vuser_excute, plugin_tree)
                 self.trans_init_log("虚拟用户%d准备完毕" % vuser_index)
                 vuser_index += 1
                 free_count -= 1
Example #4
0
 def trans_many(self, c_name, logs):
     try:
         collection = self.log_pool_table[c_name]
         collection.insert_many(logs)
     except Exception as e:
         app_logger.error('mongodb写入日志失败,原因:%s' % repr(e))
         return False
     else:
         app_logger.debug('mongodb写入日志成功')
         return True
Example #5
0
def new_test_task_job(base_data):
    app_logger.debug('准备新增测试任务')
    app_logger.debug('准备从redis中获取测试任务定时任务数据')
    # 从redis中获取测试任务定时任务数据,包含add_task新增测试任务定时任务/kill_task强制终止测试任务定时任务
    jobs = model_redis_task_job.query(base_data['task_id'])
    task_info = model_redis_test_task.query(base_data['task_id'])
    if jobs:
        app_logger.debug('从redis中获取测试任务定时任务数据成功')
        try:
            jobs_dict = json.loads(jobs)
            task_info_dict = json.loads(task_info)
        except Exception as e:
            app_logger.error('测试任务定时任务数据反序列化失败:%s' % repr(e))
            http_tell_test_task_status(task_id=base_data['task_id'], status=-2)
        else:
            app_logger.debug('测试任务定时任务数据反序列化成功')
            # 将测试任务的定时任务状态中run字段值变更为True,代表任务已发起
            jobs_dict['add_task']['run'] = True
            set_flag = model_redis_task_job.set(base_data['task_id'],
                                                json.dumps(jobs_dict))
            if set_flag:
                app_logger.debug('测试任务定时任务数据重写成功')
                # 获取插件数据
                json_file_path = os.path.join(task_info_dict['file_path'],
                                              'task.json')
                if os.path.exists(json_file_path) and os.path.isfile(
                        json_file_path):
                    try:
                        # 读取测试插件数据列表,并传递给新增测试任务方法
                        with open(os.path.join(base_data["file_path"],
                                               'task.json'),
                                  encoding='utf-8') as json_file:
                            task_json = json.load(json_file)
                    except Exception as e:
                        app_logger.error('测试任务插件数据文件内容反序列化失败:%s' % repr(e))
                        http_tell_test_task_status(
                            task_id=base_data['task_id'], status=-2)
                    else:
                        # 新增进程-一个测试任务一个进程
                        p = Process(target=create_task_run_flow_controller,
                                    args=[base_data, task_json,
                                          os.getpid()])
                        p.start()
                else:
                    app_logger.error('测试任务插件数据文件读取失败,文件路径:%s' % json_file_path)
                    http_tell_test_task_status(task_id=base_data['task_id'],
                                               status=-2)
            else:
                app_logger.error('测试任务定时任务数据重写失败')
                http_tell_test_task_status(task_id=base_data['task_id'],
                                           status=-2)
    else:
        app_logger.error('从redis中获取测试任务定时任务数据失败,任务新增失败')
        http_tell_test_task_status(task_id=base_data['task_id'], status=-2)
 def query(self, task_id):
     try:
         data = redis_pool.hget(self.key, task_id).decode()
     except Exception as e:
         msg = "redis|" + self.key + " query failed:" + repr(e)
         app_logger.error(msg)
         return None
     else:
         msg = "redis|" + self.key + " query succeed"
         app_logger.debug(msg)
         return data
 def cancel(self):
     # 要判断定时线程的有无与状态
     try:
         self._continue = False
         self.log_trans_timer and self.log_trans_timer.cancel()
     except Exception as e:
         app_logger.error('取消日志失败,原因:%s' % (repr(e)))
     else:
         app_logger.debug('取消日志成功')
     finally:
         # 最后一次传输日志内容
         self.trans_many('task%d%s' % (self.task_id, self.key_word),
                         self.log_temporary_storage)
Example #8
0
 def struct_unpack(package):
     """
     解包内容,并根据包内action决定执行哪个方法
     :param package: 测试任务名称/...
     :return: action(string)
     """
     app_logger.debug('准备解包')
     try:
         action = struct.unpack(dataFormat['action'], package)[0]
     except Exception as e:
         app_logger.warn('解包异常:%s' % repr(e))
         return 'error'
     else:
         return action.decode().strip('\x00')
 def set(self, log):
     """
     支持传入单条log,将其添加入storage
     :param log: 单条log的内容
     :return: 本方法无返回
     """
     try:
         if type(log) is dict:
             self.log_temporary_storage.append(log)
         elif type(log) is list:
             self.log_temporary_storage += log
     except Exception as e:
         app_logger.error('暂存日志失败,原因:%s' % repr(e))
     else:
         app_logger.debug('暂存日志成功')
 def set(self, task_id, value):
     """
         记录定时任务基础数据
         :param task_id: 测试任务id
         :param value: 任务定时执行信息
     """
     try:
         redis_pool.hset(self.key, task_id, value)
     except Exception as e:
         msg = "redis|" + self.key + " insert failed:" + repr(e)
         app_logger.error(msg)
         return False
     else:
         msg = "redis|" + self.key + " insert succeed"
         app_logger.debug(msg)
         return True
Example #11
0
 def set(self, task_id, ppid, pid):
     """
         记录测试任务基础数据
         :param task_id: 测试任务id
         :param ppid: 父进程id
         :param pid: 子进程id
         :return: data: True/False
     """
     try:
         redis_pool.hset(self.key, task_id, str(ppid) + ':' + str(pid))
     except Exception as e:
         msg = "redis|" + self.key + " insert failed:" + repr(e)
         app_logger.error(msg)
         return False
     else:
         msg = "redis|" + self.key + " insert succeed"
         app_logger.debug(msg)
         return True
Example #12
0
 def make_log_pool(self):
     try:
         self.log_pool = MongoClient(
             host=database_config.get("logMongodb", "host"),
             port=int(database_config.get("logMongodb", "port")),
             maxPoolSize=100)
         self.log_pool_table = self.log_pool[self.log_table]
         self.log_pool_table.authenticate(
             database_config.get("logMongodb", "username"),
             database_config.get("logMongodb", "password"))
     except Exception as e:
         msg = "mongodb连接池初始化失败,失败原因:" + repr(e)
         app_logger.error(msg)
         return False, repr(e)
     else:
         msg = "mongodb连接池初始化成功"
         app_logger.debug(msg)
         return True, None
 def __init__(self, base_data, plugin_data):
     #
     self.flow_init_result = True
     # 将测试任务基础数据转换为多个变量
     self.base_data = base_data
     self.base_task_id = base_data['task_id']
     self.base_exc_times = base_data['exc_times']
     self.base_vuser_num = base_data['v_user']
     self.plugin_data = plugin_data
     self.worker_info_id = app_config.getint("worker", "id")
     self.worker_info = {"id": self.worker_info_id}
     self.gevent_pool = None
     http_tell_test_task_status(task_id=self.base_task_id, status=2)
     self.parameters_storage = ParametersStorage()
     # 实例化日志控制器
     self.init_log_controller = SyncLogController('tasklog',
                                                  self.base_task_id,
                                                  '_init')
     if self.init_log_controller.log_pool_make_result:
         app_logger.debug('测试任务ID:%d基础日志控制器初始化成功' % self.base_task_id)
         self.trans_init_log('基础日志控制器初始化成功')
     else:
         app_logger.error('测试任务ID:%d基础日志控制器初始化失败')
         self.flow_init_result = False
     self.run_log_controller = AsyncLogController('tasklog',
                                                  self.base_task_id, '_run')
     if self.run_log_controller.log_pool_make_result:
         app_logger.debug('测试任务ID:%d运行日志控制器初始化成功' % self.base_task_id)
         self.trans_init_log('运行日志控制器初始化成功')
     else:
         app_logger.error('测试任务ID:%d运行日志控制器初始化失败')
         self.flow_init_result = False
     if self.flow_init_result:
         # 写一些环境信息
         self.trans_init_log("启动测试任务")
         # 递归原始数据
         self.trans_init_log("准备初始化各虚拟用户的插件树")
         # self.recurse_plugin_tree(plugin_data[0])
         # self.trans_init_log("插件及流程控制器初始化结束")
     else:
         http_tell_test_task_status(task_id=self.base_task_id, status=-2)
 def trans(self):
     """
     根据配置文件中的行数限制,传递日志至日志存储服务中
     :return: 本方法无返回
     """
     # 判断行数是否满足要求,不满足直接pass
     # 计算要传输的数据行数
     actual_data_rownum = len(self.log_temporary_storage)
     trans_data_rownum = self.log_send_item_num if actual_data_rownum >= self.log_send_item_num else actual_data_rownum
     self.trans_many('task%d%s' % (self.task_id, self.key_word),
                     self.log_temporary_storage[:trans_data_rownum])
     try:
         del (self.log_temporary_storage[:trans_data_rownum])
     except Exception as e:
         app_logger.error('清除日志失败,原因:%s' % (repr(e)))
     else:
         app_logger.debug('暂存日志成功')
     if self._continue:
         self.log_trans_timer = threading.Timer(
             self.log_check_time_interval, self.trans)
         self.log_trans_timer.start()
Example #15
0
def http_register_user_ui_server():
    # 准备参数
    # 用户界面的执行应用注册接口地址
    server_ip = app_config.get('server', 'host')
    server_port = app_config.get('server', 'port')
    api_url = 'http://' + server_ip + ':' + str(
        server_port) + '/api/task/workerRegister.json'
    api_headers = {'Content-Type': 'application/json;charset=UTF-8'}
    # 生成uuid/ip/port
    worker_uuid = app_config.get('worker', 'uuid')
    worker_host = app_config.get('worker', 'host')
    worker_port = int(app_config.get('worker', 'port'))
    api_json = json.dumps({
        'uuid': worker_uuid,
        'ip': worker_host,
        'port': worker_port
    })
    try:
        api_response = requests.post(api_url,
                                     data=api_json,
                                     headers=api_headers,
                                     timeout=10)
    except Exception as e:
        app_logger.error('服务注册失败,失败原因:' + repr(e))
        return False
    else:
        if api_response.status_code == 200:
            # 如果返回码为200则成功,否则失败
            api_response_dict = json.loads(api_response.text)
            if api_response_dict['error_code'] != 200:
                app_logger.error('服务注册失败,失败原因:' +
                                 api_response_dict['error_msg'])
                return False
            else:
                app_logger.debug('服务注册成功')
                return {'worker_id': api_response_dict['data']['worker_id']}
        else:
            app_logger.error('服务注册失败,失败原因:用户界面服务异常')
            return False
Example #16
0
def create_task_run_flow_controller(task_data, plugin_data, _pid):
    # 限制进程使用内存大小200MB
    # linux有效/macOS无效
    # 自测可把数值调小
    resource.setrlimit(resource.RLIMIT_AS,
                       (250 * 1024 * 1024, 250 * 1024 * 1024))
    app_logger.debug('开始创建测试插件运行时流程控制器')
    # 获取父进程号以及子进程号并写入redis
    set_flag = model_redis_task_process_id.set(task_data['task_id'], _pid,
                                               os.getpid())
    if set_flag:
        app_logger.debug('测试任务所属父进程号%d及自身进程号%d数据入库成功' % (_pid, os.getpid()))
        # 首先实例化流程控制器
        flow_controller = FlowController(task_data, plugin_data)
        app_logger.debug('测试插件运行时流程控制器创建完成')
        # 然后处理内部虚拟用户事务准备运行
        flow_controller.init_vusers()
        # 如果递归初始化的结果是失败或者异常,则不执行run方法,进程结束
        if flow_controller.flow_init_result:
            app_logger.debug('测试开始')
            # 运行这个测试计划
            try:
                flow_controller.run()
            except MemoryError:
                flow_controller.trans_init_log('测试任务终止,内存溢出', 'ERROR')
                http_tell_test_task_status(task_id=task_data['task_id'],
                                           status=-3)
            except Exception as e:
                flow_controller.trans_init_log('测试任务终止,程序异常:%s' % repr(e),
                                               'ERROR')
                http_tell_test_task_status(task_id=task_data['task_id'],
                                           status=-3)
        else:
            flow_controller.trans_init_log('测试任务终止,流处理器初始化失败', 'ERROR')
            http_tell_test_task_status(task_id=task_data['task_id'], status=-3)
    else:
        app_logger.debug('测试任务终止,测试任务所属父进程号及自身进程号入库失败')
        http_tell_test_task_status(task_id=task_data['task_id'], status=-3)
Example #17
0
 def file_dir_handle(_data):
     """
     :return: True, 创建成功的测试任务目录路径(str)/False, None
     """
     # 检查文件存放路径
     the_now = datetime.datetime.now()
     the_year_path = '%s/%d' % (app_config.get('file',
                                               'path'), the_now.year)
     the_month = the_now.month
     the_day = the_now.day
     if not os.path.exists(the_year_path):
         app_logger.debug('年份文件夹不存在,尝试创建...')
         try:
             os.makedirs(the_year_path)
         except Exception as e:
             app_logger.error('年份文件夹创建失败:%s' % repr(e))
             return False, None
         else:
             app_logger.debug('年份文件夹创建成功')
     if not os.path.exists('%s/%d' % (the_year_path, the_month)):
         app_logger.debug('月份文件夹不存在,尝试创建...')
         try:
             os.makedirs('%s/%d' % (the_year_path, the_month))
         except Exception as e:
             app_logger.error('月份文件夹创建失败:' + repr(e))
             return False, None
         else:
             app_logger.debug('月份文件夹创建失败')
     if not os.path.exists('%s/%d/%d' %
                           (the_year_path, the_month, the_day)):
         app_logger.debug('日子文件夹不存在,尝试创建...')
         try:
             os.makedirs('%s/%d/%d' % (the_year_path, the_month, the_day))
         except Exception as e:
             app_logger.error('日子文件夹创建失败:' + repr(e))
             return False, None
         else:
             app_logger.debug('日子文件夹创建成功')
     task_dir_path = '%s/%d/%d/task_%d_%s' % (
         the_year_path, the_month, the_day, _data['task_id'],
         the_now.strftime('%Y%m%d%H%M%S'))
     try:
         os.makedirs(task_dir_path)
     except Exception as e:
         app_logger.error('测试任务文件夹创建失败:' + repr(e))
         return False, None
     else:
         app_logger.debug('测试任务文件夹创建成功')
         return True, task_dir_path
Example #18
0
 def handle(self):
     # --- client发起connet请求,handler检测到后开始执行下面代码
     app_logger.debug('收到来自IP:%s的请求' % self.client_address[0])
     # --- connet请求代码末
     # --- 第1次send/recv开始
     app_logger.debug('准备接收第1次数据传输...')
     # --- recv方法阻塞handler,程序进入监听数据状态
     # 根据内容长度接受内容
     action_struct_data = self.request.recv(
         struct.calcsize(dataFormat['action']))
     # --- clent发起第1次send,handler检测到后开始执行下面代码
     app_logger.debug('接收到第1次数据传输')
     # 第1次recv接收的数据为请求类型action
     # 预备数据接收到内容不为空才可继续执行
     if action_struct_data:
         # 解包内容
         request_action = self.struct_unpack(action_struct_data)
         """
         当前支持action
         1.newTestTask 新测试任务
         2.stopTestTask stopTestTask
         其余字符串一律返回错误信息
         """
         if request_action == 'newTestTask':
             app_logger.debug('action为新测试任务')
             # 回传成功状态
             # 检查系统当前资源
             # 如果内存剩余不足500MB,则禁止创建测试任务
             if int(psutil.virtual_memory().available / 1024 / 1024) < 500:
                 app_logger.warn('测试机剩余内存不足,无法创建测试任务')
                 self.request.send(str.encode('Failure.测试机剩余内存不足,无法创建测试任务'))
                 self.request.close()
             else:
                 self.request.send(str.encode('Success'))
                 # --- 第1次send/recv结束
                 app_logger.debug('准备接收第2次数据传输...')
                 # --- recv方法阻塞handler,程序进入监听数据状态
                 # 根据内容长度接受内容
                 base_struct_data = self.request.recv(
                     struct.calcsize(dataFormat[request_action]))
                 app_logger.debug('接收到第2次数据传输')
                 # --- clent发起第2次send,handler检测到后开始执行下面代码
                 # 检查新增测试任务数据基础信息
                 check_result, base_data = self.check_test_task(
                     base_struct_data)
                 if check_result:
                     # 第2次测试任务基础数据检查通过后,新增测试任务目录,用以存放相关文件,然后再返回状态数据
                     handle_flag, task_dir_path = self.file_dir_handle(
                         base_data)
                     if handle_flag:
                         # 回传成功状态
                         app_logger.debug('测试任务文件夹处理成功')
                         self.request.send(str.encode('Success'))
                         # --- 第2次send/recv结束
                         # 准备接收task文件
                         recvd_size = 0
                         file = open(task_dir_path + '.zip', 'wb')
                         app_logger.debug('准备接收第3次数据传输...')
                         while not recvd_size == base_data['file_size']:
                             if base_data['file_size'] - recvd_size > 1024:
                                 # --- recv方法阻塞handler,程序进入监听数据状态
                                 rdata = self.request.recv(1024)
                                 recvd_size += len(rdata)
                             else:
                                 rdata = self.request.recv(
                                     base_data['file_size'] - recvd_size)
                                 recvd_size = base_data['file_size']
                             file.write(rdata)
                             file.flush()
                         file.close()
                         app_logger.debug('接收到第3次数据传输')
                         # 解压缩文件
                         try:
                             with zipfile.ZipFile(task_dir_path +
                                                  '.zip') as zfile:
                                 zfile.extractall(path=task_dir_path)
                         except Exception as e:
                             app_logger.error('压缩包处理失败:' + repr(e))
                             self.request.send(
                                 str.encode('Failure.测试任务压缩包处理失败,测试任务创建失败'))
                             self.request.close()
                         else:
                             app_logger.debug('压缩包处理成功')
                             # 新增测试任务数据
                             base_data['file_path'] = task_dir_path
                             # 0未启动/1运行中/2已结束
                             base_data['run_status'] = 0
                             self.insert_test_task(base_data)
                             # 新增定时测试任务
                             add_task = test_task_scheduler.add_job(
                                 func=new_test_task_job,
                                 args=[base_data],
                                 trigger='date',
                                 jobstore='redis',
                                 next_run_time=datetime.datetime.strptime(
                                     base_data['start_time'],
                                     '%Y-%m-%d %H:%M:%S')
                                 if base_data['start_time'] else
                                 datetime.datetime.now(),
                                 misfire_grace_time=3000)
                             # 如果有结束时间,则还需创建kill的job
                             kill_task = None
                             if base_data['end_time']:
                                 kill_task = test_task_scheduler.add_job(
                                     func=kill_test_task_job,
                                     args=[base_data],
                                     trigger='date',
                                     jobstore='redis',
                                     next_run_time=datetime.datetime.
                                     strptime(base_data['end_time'],
                                              '%Y-%m-%d %H:%M:%S'),
                                     misfire_grace_time=3000)
                             # 定时任务信息存入redis
                             # run默认为false
                             job_value = {
                                 'add_task': {
                                     '_jobstore_alias':
                                     add_task._jobstore_alias,
                                     'id':
                                     add_task.id,
                                     'next_run_time':
                                     add_task.next_run_time.strftime(
                                         '%Y-%m-%d %H:%M:%S'),
                                     'run':
                                     False,
                                     'remove':
                                     False
                                 }
                             }
                             if kill_task:
                                 job_value['kill_task'] = {
                                     '_jobstore_alias':
                                     kill_task._jobstore_alias,
                                     'id':
                                     kill_task.id,
                                     'next_run_time':
                                     kill_task.next_run_time.strftime(
                                         '%Y-%m-%d %H:%M:%S'),
                                     'run':
                                     False,
                                     'remove':
                                     False
                                 }
                             set_result = model_redis_task_job.set(
                                 base_data['task_id'],
                                 json.dumps(job_value, ensure_ascii=False))
                             if set_result:
                                 self.request.send(str.encode('Success'))
                                 # --- 第三次send/recv结束
                                 self.request.close()
                             else:
                                 self.request.send(
                                     str.encode(
                                         'Failure.测试任务入库失败,无法创建测试任务'))
                                 self.request.close()
                     else:
                         app_logger.warn('测试任务文件夹处理失败')
                         self.request.send(
                             str.encode('Failure.测试任务文件夹处理失败,无法创建测试任务'))
                         self.request.close()
                 else:
                     app_logger.warn('测试任务数据检查失败')
                     self.request.send(
                         str.encode('Failure.测试任务基础数据检查失败,无法创建测试任务'))
                     self.request.close()
         elif request_action == 'stopTestTask':
             app_logger.debug('action为强制停止测试任务')
             self.request.send(str.encode('Success'))
             # --- 第一次send/recv结束
             # --- handler进入监听状态,程序阻塞
             app_logger.debug('listen second send from client...')
             # 根据action选择内容长度并接受内容
             base_info_size = struct.calcsize(dataFormat[request_action])
             base_struct_data = self.request.recv(base_info_size)
             # --- clent发起第二次send,handler检测到后开始执行下面代码
             # 检查新增测试任务数据基础信息
             check_result, base_data = self.check_task_id(base_struct_data)
             if check_result:
                 # 调用终止进程方法
                 kill_test_task_job(base_data)
                 self.request.send(str.encode('Success'))
                 # --- 第二次send/recv结束
                 self.request.close()
             else:
                 self.request.send(
                     str.encode('Failure.测试任务ID检查失败,无法终止测试任务'))
                 self.request.close()
         elif request_action == 'test':
             app_logger.debug('action为测试连接状态')
             self.request.send(str.encode('Success'))
             self.request.close()
         else:
             app_logger.warn('action内容为空或非法')
             self.request.send(str.encode('Failure.操作暂不支持'))
             self.request.close()
     else:
         app_logger.warn('首次数据传输内容为空')
         self.request.send(str.encode('Failure.接收到空数据'))
         self.request.close()