def __load_project_file(options): jname = 'poker/project.json' datadict = __load_json_file(options, jname, list) if not datadict: return datadict game_packages = [] gameids = [] for proj in datadict: ppath = proj.get('path', None) if not isinstance(ppath, str): return actlog.error('the project path is not defined !', proj) if not fsutils.dirExists(ppath): return actlog.error('the project path not found !', ppath) srcpath = fsutils.appendPath(ppath, 'src') if fsutils.dirExists(srcpath): subdirs = os.listdir(srcpath) for subdir in subdirs: gamepy = fsutils.appendPath(srcpath, subdir, 'game.py') if fsutils.fileExists(gamepy): if subdir in game_packages: return actlog.error( 'the project package is double defined !', gamepy, 'proj=', proj) game_packages.append(subdir) proj['package'] = subdir gameid = proj.get('gameId', 0) if gameid > 0: gameids.append(int(gameid)) actlog.log('find gam project-> GAMEID=', gameid, 'PACKAGE=', subdir) return datadict, game_packages, gameids
def readJsonData(jf, options): try: return strutil.replace_objevn_value(json.loads(filterComment(jf)), options.env) except Exception, e: actlog.error('config item file->', jf) raise e
def action(options): ''' ''' params = {} params['options'] = options haserror = tythread.mutil_thread_machine_action(params, _thread_action_stop) if haserror : actlog.error('server stop all error !') return 0 return 1
def action(options): ''' ''' params = {} params['options'] = options haserror = tythread.mutil_thread_machine_action(params, _thread_action_removelogs) if haserror : actlog.error('remove all logs error !') return 0 return 1
def action(options): ''' ''' params = {} params['options'] = options haserror = tythread.mutil_thread_machine_action(params, _thread_action_removelogs) if haserror: actlog.error('remove all logs error !') return 0 return 1
def thread_run_action(options, action): try: action['stime'] = datetime.now().strftime('%Y%m%d_%H%M%S') actlog.open_act_log(options, action) acthistory.save_action_history(options, action) actlog.log('START ACTION', action) thread_do_action(options, action) action['result'] = 'Ok' except: actlog.error() action['result'] = 'Exception' finally: actlog.close_act_log() action['etime'] = datetime.now().strftime('%Y%m%d_%H%M%S') acthistory.save_action_history(options, action)
def __load_json_file(options, jname, jtype): jsonfile = fsutils.appendPath(options.poker_path, jname) actlog.log('load %-15s :' % (jname), jsonfile) datadict = fsutils.readJsoFileParseEnv(options.env, jsonfile, True) if not isinstance(datadict, jtype): return actlog.error(jname + ' : format error, root object must be ' + str(jtype)) return datadict
def make_process_list(options, machinedict, gameids): jname = 'poker/process.json' jsonfile = fsutils.appendPath(options.poker_path, jname) actlog.log('load %-15s :' % (jname), jsonfile) processlist = fsutils.readJsonFile(jsonfile, True) if not isinstance(processlist, dict) : return actlog.error(jname + ' : format error, root object must be dict'), machinedict return processlist, machinedict
def make_process_list(options, machinedict, gameids): jname = 'poker/process.json' jsonfile = fsutils.appendPath(options.poker_path, jname) actlog.log('load %-15s :' % (jname), jsonfile) processlist = fsutils.readJsonFile(jsonfile, True) if not isinstance(processlist, dict): return actlog.error( jname + ' : format error, root object must be dict'), machinedict return processlist, machinedict
def __load_server_file(options, machinedict, gameids): mode = options.env['mode'] processlist, machinedict = modefactory[mode].make_process_list( options, machinedict, gameids) if not processlist: return processlist allrooms = {} # 装载房间的配置,用于获取房间进程ID和数量 for gameId in gameids: jname = 'game/' + str(gameId) + '/room/0.json' jsonfile = fsutils.appendPath(options.poker_path, jname) if fsutils.fileExists(jsonfile): actlog.log('load %-15s :' % (jname), jsonfile) rooms = fsutils.readJsonFile(jsonfile, True) if not isinstance(rooms, dict): return actlog.error( jname + ' : format error, root object must be dict') for rid in rooms: if rid in allrooms: return actlog.error(jname + ' : the roomId already defined !! ' + str(rid)) allrooms[rid] = rooms[rid] serverlist = auto_process.auto_group_process(machinedict, processlist, allrooms, mode) serverlist = strutil.replace_objevn_value(serverlist, options.env) checks = strutil.cloneData(serverlist) for _, m in options.machinedict.items(): internet = m['internet'] intranet = m['intranet'] for x in xrange(len(checks) - 1, -1, -1): p = checks[x] if p['ip'] == internet or p['ip'] == intranet: del checks[x] if len(checks) > 0: for p in checks: actlog.error('can not find machine define of server ip :', p['ip']) return 0 return serverlist
def push_tar_to_all_server(options, tarfile, taroutpath, tarsubpath, rmLeft): machines = options.machinedict.values() if len(machines) == 1 : if machines[0].get('localhost', 0) == 1: return 1 params = { 'options' : options, 'tarfile' : tarfile, 'taroutpath' : taroutpath, 'tarsubpath' : tarsubpath, 'rmLeft' : rmLeft, } haserror = mythread.mutil_thread_machine_action(params, _thread_action_push) if haserror : actlog.error('push tar file error !', tarfile) return 0 return 1
def _thread_action_push(controls): ''' 这个方法运行再多线程当中 ''' machine = controls['machine'] result = 0 outputs = '' try: if machine.get('localhost', 0) == 1 : controls['percent'] = '++++' result = 1 else: result, outputs = _thread_action_push_ssh(controls) except: result = 2 # 代码异常 actlog.error() controls['done'] = 1 controls['result'] = result controls['outputs'] = outputs
def _thread_action_start(controls): """ 这个方法运行再多线程当中 """ params = controls['params'] machine = controls['machine'] options = params['options'] pfilter = params['thread_filter'] procids = getMachinePids(options, machine, pfilter) if not procids: controls['done'] = 1 controls['result'] = 1 controls['outputs'] = 'the procids is empty' return controls['percent'] = '++++' # 启动本机的所有进程 rparams = ['start'] rparams.extend(procids) result, outputs = execute_remote_py(options, machine, rparams) if result != 0: actlog.log('remote start false !') actlog.log('---------------------------------------------------------------') for l in outputs.split('\n'): actlog.log(l) actlog.log('---------------------------------------------------------------') controls['done'] = 1 controls['result'] = 2 controls['percent'] = 'done' controls['outputs'] = outputs return # 获得本机的所有进程基本信息 thread_info = tyssh.parse_remote_datas_json(outputs, 'TY_THREAD_INFO') pypypids = {} for k, v in thread_info.items(): pypypids[k] = v['pypy']['pid'] actlog.log(machine['host'], 'sid=', k, 'pid=', pypypids[k]) # 远程发送hotcmd, 取得进程的运行状态 rparams = ['status'] rparams.extend(procids) wst = time.time() while 1: isdone = 0 result, outputs = execute_remote_py(options, machine, rparams) if result != 0: actlog.log('read remote status false retry !') actlog.log('---------------------------------------------------------------') for l in outputs.split('\n'): actlog.log(l) actlog.log('---------------------------------------------------------------') else: try: # 获得本机的所有进程基本信息 thread_status = tyssh.parse_remote_datas_json(outputs, 'TY_THREAD_STATUS') ecount = 0.0 scount = 0.0 wcount = 0.0 errpids = [] for sid in pypypids: status = thread_status.get(sid, {}) if pypypids[sid] != status.get('pid', pypypids[sid]): ecount += 1 errpids.append(sid) else: st = status.get('status', 0) if st == 500: ecount += 1 errpids.append(sid) elif st == 200: scount += 1 else: wcount += 1 controls['percent'] = str(int(scount * 100.0 / float(len(pypypids)))) + '%' if int(scount) + int(ecount) == len(pypypids): # 全部启动成功 if int(ecount) > 0: # 有错误 controls['done'] = 1 controls['result'] = 2 controls['percent'] = 'done' controls['outputs'] = 'remote process has Exception !' for sid in errpids: actlog.log(machine['host'], 'EXCEPTION !! ->', sid) return else: # 没有错误 isdone = 1 break except: actlog.error() if isdone == 1: # 没有错误 break elif time.time() - wst > 300: # 5分钟启动超时 actlog.log(machine['host'], 'time out !!!') actlog.log('remote status timeout false !' + str(errpids)) controls['done'] = 1 controls['result'] = 2 controls['percent'] = 'done' controls['outputs'] = 'remote status timeout false !' + str(errpids) return time.sleep(1) controls['done'] = 1 controls['result'] = 1 controls['outputs'] = outputs
def __load_poker_file(options): pokerfile = options.pokerfile actlog.log('LOAD POKER FILE :', pokerfile) pokerdict = fsutils.readJsonFile(pokerfile, True) pokerdict['poker_path'] = options.poker_path if not isinstance(pokerdict, dict): return actlog.error('POKER FILE : format error,must be a dict') gameId = pokerdict.get('id', None) if not isinstance(gameId, int) or gameId <= 0 or gameId >= 10000: return actlog.error('POKER FILE : id value is wrong') gameName = pokerdict.get('name', None) if not isinstance(gameName, (str, unicode)) or len(gameName) <= 0: return actlog.error('POKER FILE : name value is wrong') if gameName.find('-') >= 0: return actlog.error( 'POKER FILE : name value is wrong, can not have "-" (reserved char)' ) corporation = pokerdict.get('corporation', 'tuyoo') if corporation not in ('tuyoo', 'momo'): return actlog.error( 'POKER FILE : corporation value wrong, choice: tuyoo or momo') pokerdict['corporation'] = corporation mode = pokerdict.get('mode', 0) if mode not in (1, 2, 3, 4): return actlog.error( 'POKER FILE : mode value must be an integer, choice : 1(online) or 2(simulation) or 3(rich test) or 4(tiny test)' ) port_redis = pokerdict.get('port_redis', 0) if not actutils.check_port(port_redis, True): return actlog.error( 'POKER FILE : port_redis, socket port number wrong ' + str(port_redis)) pokerdict['port_redis'] = port_redis local_internet = pokerdict.get('local_internet', '') if not isinstance(local_internet, (str, unicode)): return actlog.error('POKER FILE : local_internet value is wrong') pokerdict['local_internet'] = local_internet local_intranet = __get_value(pokerdict, 'local_intranet', None) if not isinstance(local_intranet, (str, unicode)): return actlog.error('POKER FILE : local_intranet value is wrong') pokerdict['local_intranet'] = local_intranet port_http = int(__get_value(pokerdict, 'port_http', 0)) if not actutils.check_port(port_http, True): return actlog.error( 'POKER FILE : port_http, socket port number wrong ' + str(port_http)) pokerdict['port_http'] = port_http projects_path = __get_value(pokerdict, 'projects_path', None) if not isinstance(projects_path, str): return actlog.error( 'POKER FILE : projects_path wrong, must pointing to the projects path' ) projects_path = fsutils.makeAsbpath(projects_path, pokerfile) if not fsutils.dirExists(projects_path): return actlog.error( 'POKER FILE : projects_path, the path not exists [' + projects_path + ']') pokerdict['projects_path'] = projects_path output_path = __get_value(pokerdict, 'output_path', None) if not isinstance(output_path, str): return actlog.error( 'POKER FILE : output_path wrong, must pointing to the compile output path' ) output_path = fsutils.makeAsbpath(output_path, pokerfile) if not fsutils.dirExists(output_path): return actlog.error( 'POKER FILE : output_path wrong, the path not exists [' + output_path + ']') pokerdict['output_path'] = output_path http = __get_value(pokerdict, 'http_sdk', '') if not actutils.check_http_url(http, 'POKER FILE : http_sdk'): return 0 pokerdict['http_sdk'] = http http = __get_value(pokerdict, 'http_sdk_inner', None) if http != None and not actutils.check_http_url( http, 'POKER FILE : http_sdk_inner'): return 0 pokerdict['http_sdk_inner'] = http http = __get_value(pokerdict, 'http_game', None) if http != None and not actutils.check_http_url(http, 'POKER FILE : http_game'): return 0 pokerdict['http_game'] = http http = __get_value(pokerdict, 'http_download', None) if http != None and not actutils.check_http_url( http, 'POKER FILE : http_download'): return 0 pokerdict['http_download'] = http http = __get_value(pokerdict, 'http_gdss', None) if http != None and not actutils.check_http_url(http, 'POKER FILE : http_gdss'): return 0 pokerdict['http_gdss'] = http config_redis = __get_value(pokerdict, 'config_redis', None) if not isinstance(config_redis, str): return actlog.error( 'POKER FILE : config_redis wrong, must pointing to the configure redis <ip>:<port>:<dbid>' ) confdb = config_redis.split(':') if len(confdb) != 3: return actlog.error( 'POKER FILE : config_redis wrong, must pointing to the configure redis <ip>:<port>:<dbid>' ) pokerdict['config_redis'] = config_redis return pokerdict
def __load_machine_file(options): jname = 'poker/machine.json' datadict = __load_json_file(options, jname, dict) if not datadict: return datadict ips = set() for mid, mdefs in datadict.items(): actlog.log('check machine define :', mid) internet = mdefs.get('internet', None) intranet = mdefs.get('intranet', None) user = mdefs.get('user', None) pwd = mdefs.get('pwd', None) ssh = mdefs.get('ssh', None) if not isinstance(internet, str): return actlog.error('the machine internet format error ! [' + str(internet) + ']') if internet in ips: return actlog.error('the machine internet already exits ! [' + str(internet) + ']') ips.add(internet) if intranet == None: mdefs['intranet'] = internet intranet = internet if not isinstance(intranet, str): return actlog.error('the machine intranet format error ! [' + str(intranet) + ']') if intranet != internet: if intranet in ips: return actlog.error('the machine intranet already exits ! [' + str(intranet) + ']') ips.add(intranet) if user == None: mdefs['user'] = '' user = '' if not isinstance(user, str): return actlog.error('the machine user format error ! [' + str(user) + ']') if pwd == None: mdefs['pwd'] = '' pwd = '' if not isinstance(pwd, str): return actlog.error('the machine pwd format error ! [' + str(pwd) + ']') if ssh == None: mdefs['ssh'] = 22 ssh = 22 if not isinstance(ssh, int): return actlog.error('the machine ssh port format error ! [' + str(ssh) + ']') islocalhost = fsutils.isLocalMachine(intranet) if islocalhost: mdefs['localhost'] = 1 mdefs['host'] = intranet else: if fsutils.checkMachinePort(intranet, ssh): mdefs['host'] = intranet else: if fsutils.checkMachinePort(internet, ssh): mdefs['host'] = internet else: return actlog.error( 'can not connect ssh port to machine ! [' + str(ssh) + ']') return datadict
def action(options): ''' 装载并检测服务启动配置文件 ''' alldata = {} setattr(options, 'alldata', alldata) actlog.log('options.poker_path->', options.poker_path) checkprojects = options.checkprojects loadsprojs = set() datas, allGameIds = load_project_datas_all(options.poker_path + '/game/', options) alldata.update(datas) for cproj in checkprojects: projectdir = fsutils.appendPath(options.poker_path, cproj) loadsprojs.add(projectdir) datas = load_project_datas_room(projectdir, options) alldata.update(datas) allGameIds.sort() options.pokerdict['config.game.ids'] = allGameIds actlog.log('config.game.ids->', allGameIds) alldata['poker:map.activityid'] = readJsonData( options.poker_path + '/poker/map.activityid.json', options) alldata['poker:map.bieventid'] = readJsonData( options.poker_path + '/poker/map.bieventid.json', options) alldata['poker:map.giftid'] = readJsonData( options.poker_path + '/poker/map.giftid.json', options) # 重gdss获取数据 ret = _syncDataFromGdss(options, 'poker:map.clientid', dict, 'getClientIdDict') if not ret: return ret ret = _syncDataFromGdss(options, 'poker:map.productid', dict, 'getProductIdDict') if not ret: return ret ret = make_static_json.make_static_json_file(options, alldata) if not ret: return ret outpath = options.pokerdict['output_path'] fsutils.writeFile(outpath, 'out.redis.json', alldata) fsutils.writeFile(outpath, 'out.poker.global.json', options.pokerdict) fsutils.writeFile(options.poker_path, '._confdata_.json', alldata) patchpy = os.path.dirname(outpath) + '/patch_config.py' if os.path.isfile(patchpy): cmd = 'pypy ' + patchpy + ' ' + outpath actlog.log('执行游戏配置文件补丁:', cmd) status, output = commands.getstatusoutput(cmd) for l in output.split('\n'): actlog.log(l) if status != 0: actlog.error('游戏配置文件补丁失败:', patchpy) actlog.error(status, output) return 0 if output and output.find('REMAKE_STATIC') >= 0: actlog.log('find REMAKE_STATIC !') alldata = json.loads(filterComment(outpath + '/out.redis.json')) ret = make_static_json.make_static_json_file(options, alldata) if not ret: return ret fsutils.writeFile(outpath, 'out.redis.json', alldata) fsutils.writeFile(options.poker_path, '._confdata_.json', alldata) return 1
def _thread_action_start(controls): """ 这个方法运行再多线程当中 """ params = controls['params'] machine = controls['machine'] options = params['options'] pfilter = params['thread_filter'] procids = getMachinePids(options, machine, pfilter) if not procids: controls['done'] = 1 controls['result'] = 1 controls['outputs'] = 'the procids is empty' return controls['percent'] = '++++' # 启动本机的所有进程 rparams = ['start'] rparams.extend(procids) result, outputs = execute_remote_py(options, machine, rparams) if result != 0: actlog.log('remote start false !') actlog.log( '---------------------------------------------------------------') for l in outputs.split('\n'): actlog.log(l) actlog.log( '---------------------------------------------------------------') controls['done'] = 1 controls['result'] = 2 controls['percent'] = 'done' controls['outputs'] = outputs return # 获得本机的所有进程基本信息 thread_info = tyssh.parse_remote_datas_json(outputs, 'TY_THREAD_INFO') pypypids = {} for k, v in thread_info.items(): pypypids[k] = v['pypy']['pid'] actlog.log(machine['host'], 'sid=', k, 'pid=', pypypids[k]) # 远程发送hotcmd, 取得进程的运行状态 rparams = ['status'] rparams.extend(procids) wst = time.time() while 1: isdone = 0 result, outputs = execute_remote_py(options, machine, rparams) if result != 0: actlog.log('read remote status false retry !') actlog.log( '---------------------------------------------------------------' ) for l in outputs.split('\n'): actlog.log(l) actlog.log( '---------------------------------------------------------------' ) else: try: # 获得本机的所有进程基本信息 thread_status = tyssh.parse_remote_datas_json( outputs, 'TY_THREAD_STATUS') ecount = 0.0 scount = 0.0 wcount = 0.0 errpids = [] for sid in pypypids: status = thread_status.get(sid, {}) if pypypids[sid] != status.get('pid', pypypids[sid]): ecount += 1 errpids.append(sid) else: st = status.get('status', 0) if st == 500: ecount += 1 errpids.append(sid) elif st == 200: scount += 1 else: wcount += 1 controls['percent'] = str( int(scount * 100.0 / float(len(pypypids)))) + '%' if int(scount) + int(ecount) == len(pypypids): # 全部启动成功 if int(ecount) > 0: # 有错误 controls['done'] = 1 controls['result'] = 2 controls['percent'] = 'done' controls['outputs'] = 'remote process has Exception !' for sid in errpids: actlog.log(machine['host'], 'EXCEPTION !! ->', sid) return else: # 没有错误 isdone = 1 break except: actlog.error() if isdone == 1: # 没有错误 break elif time.time() - wst > 300: # 5分钟启动超时 actlog.log(machine['host'], 'time out !!!') actlog.log('remote status timeout false !' + str(errpids)) controls['done'] = 1 controls['result'] = 2 controls['percent'] = 'done' controls['outputs'] = 'remote status timeout false !' + str( errpids) return time.sleep(1) controls['done'] = 1 controls['result'] = 1 controls['outputs'] = outputs
def action(options, integrate=0): ''' 装载并检测服务启动配置文件 ''' serverlist = options.serverlist setattr(options, 'hotfixpy', HOTCODE) setattr(options, 'hotfixwait', 1) x = 0 serverIds = [] for srv in serverlist : x += 1 serverIds.append(srv['type'] + srv['id']) serverIds = ','.join(serverIds) setattr(options, 'serverIds', serverIds) if integrate == 0 : actlog.log('get configure status->', serverIds) ret = hotfix.action(options, 0) config_redis = options.pokerdict['config_redis'] rconn = tydb.get_redis_conn(config_redis) clen = rconn.llen(_CHANGE_KEYS_NAME) try: datas = strutil.loads(ret) except: actlog.log('ERROR !!', ret) if integrate == 0 : return 0 return 0, 0 confOks = [] confNgs = [] errors = [] errorids = [] for sid in datas : if isinstance(datas[sid], dict): cidx = datas[sid].get('CINDEX', None) cerrs = datas[sid].get('ERRORS', []) else: cidx = '' cerrs = [] actlog.log('ERROR !!!! ', sid, datas[sid]) if cerrs : errorids.append(sid) for cerr in cerrs : erritem = [cerr.get('exception', None), cerr.get('tarceback', None)] if not erritem in errors : errors.append(erritem) if isinstance(cidx, int) : if cidx >= clen : confOks.append(sid) else: confNgs.append(sid) else: actlog.error('ERROR !!', sid, 'GET STATUS ERROR !!', datas[sid]) if errors : actlog.log('ERROR IDS =', ','.join(errorids)) actlog.log('========== ERROR !!!! BEGINE ========') for x in errors : actlog.log('========== Exception ========') for l in x[0] : for m in l.split('\n') : if m : actlog.log(m) actlog.log('========== Traceback ========') for l in x[1] : for m in l.split('\n') : if m : actlog.log(m) actlog.log('========== ERROR !!!! END ========') raise Exception('Remote Exception') if integrate == 0 : return 0 else: return 0, 0 if integrate == 0 : actlog.log('THE CONFIGURE KEY INDEX =', clen) actlog.log('TOTAL_COUNT =', len(serverlist) , 'OK_COUNT =', len(confOks), 'DELAY_COUNT =', len(confNgs)) actlog.log('CONFIGURE_STATUS = %0.2d' % (int(float(len(confOks)) / len(datas) * 100)) + '%') if integrate == 0 : return 1 else: return len(confOks), len(serverlist)
def check_http_url(http, desc): if not isinstance(http, str) or http.find('http://') != 0 or http[-1] == '/' : return actlog.error(desc + ' must start with "http://" and not end with "/"') return 1
def action(options): ''' 装载并检测服务启动配置文件 ''' alldata = {} setattr(options, 'alldata', alldata) actlog.log('options.poker_path->', options.poker_path) checkprojects = options.checkprojects loadsprojs = set() datas, allGameIds = load_project_datas_all(options.poker_path + '/game/', options) alldata.update(datas) for cproj in checkprojects: projectdir = fsutils.appendPath(options.poker_path, cproj) loadsprojs.add(projectdir) datas = load_project_datas_room(projectdir, options) alldata.update(datas) allGameIds.sort() options.pokerdict['config.game.ids'] = allGameIds actlog.log('config.game.ids->', allGameIds) alldata['poker:map.activityid'] = readJsonData(options.poker_path + '/poker/map.activityid.json', options) alldata['poker:map.bieventid'] = readJsonData(options.poker_path + '/poker/map.bieventid.json', options) alldata['poker:map.giftid'] = readJsonData(options.poker_path + '/poker/map.giftid.json', options) # 重gdss获取数据 ret = _syncDataFromGdss(options, 'poker:map.clientid', dict, 'getClientIdDict') if not ret: return ret ret = _syncDataFromGdss(options, 'poker:map.productid', dict, 'getProductIdDict') if not ret: return ret ret = make_static_json.make_static_json_file(options, alldata) if not ret: return ret outpath = options.pokerdict['output_path'] fsutils.writeFile(outpath, 'out.redis.json', alldata) fsutils.writeFile(outpath, 'out.poker.global.json', options.pokerdict) fsutils.writeFile(options.poker_path, '._confdata_.json', alldata) patchpy = os.path.dirname(outpath) + '/patch_config.py' if os.path.isfile(patchpy): cmd = 'pypy ' + patchpy + ' ' + outpath actlog.log('执行游戏配置文件补丁:', cmd) status, output = commands.getstatusoutput(cmd) for l in output.split('\n'): actlog.log(l) if status != 0: actlog.error('游戏配置文件补丁失败:', patchpy) actlog.error(status, output) return 0 if output and output.find('REMAKE_STATIC') >= 0: actlog.log('find REMAKE_STATIC !') alldata = json.loads(filterComment(outpath + '/out.redis.json')) ret = make_static_json.make_static_json_file(options, alldata) if not ret: return ret fsutils.writeFile(outpath, 'out.redis.json', alldata) fsutils.writeFile(options.poker_path, '._confdata_.json', alldata) return 1
def check_http_url(http, desc): if not isinstance(http, str) or http.find('http://') != 0 or http[-1] == '/': return actlog.error(desc + ' must start with "http://" and not end with "/"') return 1