def __dict__(self): cpurate = psutil.cpu_percent() # 获取当前CPU 使用率 mem = psutil.virtual_memory() mem_ava = mem.used / 1024 / 1024 # 换算成 MB mem_rate = mem.percent ctime = datetime.fromtimestamp( psutil.Process(macros.Macro('MONITOR_PID')).create_time()) delta_time = datetime.now() - ctime res = { 'cpuRate': '%f%%' % cpurate, 'memoryUsage': '%dMB, %f%%' % (mem_ava, mem_rate), 'routerInfo': self.routerInfo, 'bindHost': macros.Macro('HOST'), 'bindPort': macros.Macro('PORT'), 'runningTime': '{days}天,{seconds}秒。创建于{ct}'.format( days=delta_time.days, seconds=delta_time.seconds, ct=ctime, ) } return res
def _CheckInstalledInfo(packageInfoObj): """ 检查是否满足安装的标准 :param packageInfoObj: 安装的信息 :return: 返回 PackageStatus,(Staus, 消息) """ a_arc = macros.Macro('ARC').upper() a_platform = macros.Macro('PLATFORM').upper() a_apt2Version = macros.Macro('VERSION') version = packageInfoObj.version # 模块的版本 platform = packageInfoObj.platform.upper() # 支持的平台信息 # dependencies = infoJsonObj['dependencies'] # 这个模块需要的python 模块 arc = packageInfoObj.arc.upper() # 获取体系结构信息 ap2version = packageInfoObj.apt2Version # 需要的版本 name = packageInfoObj.name # 模块名称 # 是否满足安装平台 if platform != 'ALL' and platform != a_platform: return (PackageStatus.Invalid, '不支持的平台:“%s”' % platform) # 是否满足安装平台的系统版本 if arc != 'ALL' and arc != a_arc: return (PackageStatus.Invalid, '不支持的处理器体系结构:“%s”' % arc) # Appointed2 版本 apt2cmp_first = ap2version.find(',') apt2cmp = ap2version[:apt2cmp_first] apt2targetver = ap2version[apt2cmp_first + 1:] pred = tools.compareVersion(a_apt2Version, apt2targetver, apt2cmp) # 检查是否满足条件 if not pred: return (PackageStatus.Invalid, "安装目标 Appointed2 版本:‘%s’ 不满足 %s ‘%s’" % (a_apt2Version, apt2cmp, apt2targetver)) # 检查是否有已经存在的版本 installed = BaseHandler.configMgr previousObj = installed.getInstalledPackages().get( name, None) # 是否具有某个安装的信息,返回他的 infos if previousObj: cv = previousObj['version'] opts = ('>', '<', '==') vs = (PackageStatus.NewerVersion, PackageStatus.OlderVersion, PackageStatus.CurrentVersion) for i, opt in enumerate(opts): res = tools.compareVersion(version, cv, opt) if res: return (vs[i], "安装的版本‘%s’ %s 已经安装的版本‘%s’" % (version, opts[i], cv)) else: return (PackageStatus.NotExists, "安装的版本‘%s’" % version)
def user_restart(self, enterMaintenace=False): """ 重启服务:直接停止事件循环,并向监控程序发送消息 :param enterMaintenace:是否进入维护模式? :return: """ __logger = Appointed2Server.logger if enterMaintenace: __logger.info('正在重启 Appointed2 服务并进入维护模式') else: __logger.info('正在重启 Appointed2 服务') mpid = macros.Macro('MONITOR_PID', False) sig = self.restartEnterManSignal if enterMaintenace else self.restartSignal if mpid: if os.name == 'nt': from libs import sendMessage as t # 获取重启的信号 __logger.info('向监控进程 ‘%d’ 发送重启信号:‘%s’' % (mpid, sig)) self.loop.stop() t.post(mpid, int(sig)) else: self.loop.stop() subprocess.Popen(['kill', '-' + sig, str(mpid)]) # 默认是 SIGUSR2 信号 __logger.info('成功向监控进程 ‘%d’ 发送重启信号:‘%s’' % (mpid, sig))
def handle(self, request): """ 解压缩安装包信息 :param request: 请求,需要使用属性 packageInfoObj, packageStatus :return: """ # 这个是安装过程的需要 zf = request.packageInfoObj try: if not request.packageInfoObj: request.put('没有指定的安装包信息', msgType='error') return if not request.packageStatus in ACCEPT_CONTINUE_INSTALLATION: # 不能继续安装的情况:信息不完整 request.put('不能继续解压安装包过程:未满足安装条件') return ext_path = BaseHandler.generateExtractDir() kd = [d.upper() for d in macros.Macro('KEYDIRS').split(';')] kf = ('PACKAGE.JSON', ) # 直接解压 zf.extract(ext_path, kf, kd) except Exception as e: request.put('解压缩安装包失败:%s' % zf.packageFile, msgType='error') if request.packageInfoObj: request.packageInfoObj.close() # 关闭已经打开的安装包 else: if request.packageInfoObj: request.packageInfoObj.close() # 关闭已经打开的安装包 request.extractRoot = ext_path # 这个设置的是解压的路径的根目录 request.put('成功解压安装包:%s' % zf.packageFile) if super().getSuccessor(): super().getSuccessor().handle(request)
def generateExtractDir(): """ 生成安全的解压目录 :return: """ return macros.sep.join((macros.Macro('CACHEDROOT'), 'InstallExtractDir-' + str(time.time())))
class BaseHandler(): keyDirs = macros.Macro('KEYDIRS').split(';') # 注意是关键文件夹名称的列表 # cached_setup_dir = macros.Macro('CACHEDROOT') + + 'temp_packages' # 解压缩的临时文件夹 configMgr = config.getConfigManager() logger = logger.get_logger() def __init__(self): self.successor = None def setSuccessor(self, successor): self.successor = successor def getSuccessor(self): return self.successor def handle(self, request): raise NotImplementedError('子类必须实现 handle') @staticmethod def generateExtractDir(): """ 生成安全的解压目录 :return: """ return macros.sep.join((macros.Macro('CACHEDROOT'), 'InstallExtractDir-' + str(time.time())))
def installDependentModules(modules, cwd): """ 安装python 关联的安装包 :param modules: 模块名称,可以带有判断检查 如 aiohttp==2.3 :param cwd: 运行的目录,可以安装目录下的 whl 文件 :return: 返回 状态代码,输出信息,错误信息 """ try: import setuptools except ImportError as e: # 表明不支持安装 return -1, '', '不支持安装 Python 模块' # 未安装的列表 # 检测pip位置 prefix = macros.Macro('PYTHON_PREFIX') ostype = macros.Macro('PLATFORM') if ostype == 'nt': pippath = macros.sep.join((prefix, 'Scripts', 'pip.exe')) else: pippath = macros.sep.join( (prefix, 'bin', 'pip' + macros.Macro('PYTHON_VERSION'))) if not os.path.exists(pippath): return -1, '不支持安装 Python 模块:无法找到 pip 程序:‘%s’' % pippath # 运行 pip 程序 args = [pippath, 'install'] args.extend(modules) # 运行程序 ret = None try: p = subprocess.Popen(args=' '.join(args), stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=True, cwd=cwd) out, err = p.communicate() out_dect = chardet.detect(out) err_dect = chardet.detect(err) retout = out.decode(out_dect['encoding']) if len(out) > 0 else '' reterr = err.decode(err_dect['encoding']) if len(err) > 0 else '' ret = (p.returncode, retout, reterr) except Exception as e: ret = (-1, '', '运行模块安装程序出现异常:\n%s\n' % traceback.format_exc()) _logger.error('运行模块安装程序出现异常。', exc_info=True) return ret
def _updateConfig(packageInfoObj): """ 更新已经安装的模块的信息 :param packageInfoObj: 信息对象 :return: """ cfgmgr = BaseHandler.configMgr configObj = packageInfoObj.__dict__(appendOtherInfo=False) configObj['enable'] = True configObj['main_package'] = False configObj['configFile'] = macros.sep.join( (macros.Macro('CONFIGROOT'), '%s-%s.json' % (packageInfoObj.name, packageInfoObj.version))) cfgmgr.addInstalledPackageInfo(packageInfoObj.name, configObj) cfgmgr.save() return ['成功更新配置文件']
def _processInfoFile(infoFile, packageInfoObj): """ 这个函数用来辅助处理模块的信息 :param infoFile: 信息文件 :param packageInfoObj: 信息文件的对象 :return: """ packname = packageInfoObj.name packversion = packageInfoObj.version targetFile = macros.sep.join((macros.Macro('CONFIGROOT'), '%s-%s.json' % (packname, packversion))) # json 写入到config目录的文件下 with open(targetFile, 'w') as fp: i = packageInfoObj.__dict__(appendOtherInfo=True) json.dump(i, fp, indent=4) # 删除原始的信息 os.remove(infoFile) return ['成功更新模块配置文件:%s' % targetFile]
def _compileFiles(request): """ 编译相关的源文件 :param request: 请求对象 :return: 返回 状态(1没有编译,0 所有编译成功,-1 编译有错误),新增文件列表 """ a_platform = macros.Macro('PLATFORM') infoObj = request.packageInfoObj cwd = request.extractRoot compile_obj = infoObj.compile status = 1 files = [] if not compile_obj: return 1, files # 查询编译的条件 for compile_name, compile_info in compile_obj.items(): platform = compile_info.get('platform', '') if platform.upper() == 'ALL' or platform.upper( ) == a_platform.upper(): # 需要编译 status, outfile, output, err = tools.compileDynamicExtension( compilerType=compile_info['compiler'], srcs=compile_info['srcs'], out=compile_info['out'], cwd=cwd) # 判断条件 if status != 0: request.put( "编译对象‘%s’失败,返回值:%d。\n输出的信息:\n%s\n错误/警告输出:\n%s\n" % (compile_name, status, output, err)) return -1, files else: request.put( "编译对象‘%s’成功,返回值:%d。\n输出的信息:\n%s\n错误/警告输出:\n%s\n" % (compile_name, status, output, err)) # 更新文件列表 files.append(outfile) status = 0 # 写入文件列表 return status, files
def user_close(self, sendSignal=True): """ 调用对象的关闭服务 :param sendSignal: 是否向宿主程序发送信号 :return: """ __logger = Appointed2Server.logger __logger.info('正在关闭 Appointed2 服务') mpid = macros.Macro('MONITOR_PID', False) sig = self.closeSignal if mpid: if os.name == 'nt': from libs import sendMessage as t # 获取重启的信号 __logger.info('向监控进程 ‘%d’ 发送关闭信号:‘%s’' % (mpid, sig)) self.loop.stop() if sendSignal: t.post(mpid, int(sig)) else: self.loop.stop() subprocess.Popen(['kill', '-' + sig, str(mpid)]) # 默认是 SIGUSR2 信号 __logger.info('成功向监控进程 ‘%d’ 发送关闭信号:‘%s’' % (mpid, sig))
def __init__(self, packageName, modObj=None, **kwargs): """ 模块的信息 __是受保护的内容 valid:是否有效 doc:注释信息 version:模块的信息 author:作者 rotuers:可调用对象->{方法,路由,解释,级别,系统} commandLines:支持的命令行参数版本 moduleObject:模块的实例 main_package:是否是主模块 :param packageName: 模块的名称 :return: """ try: package_root_modtype = macros.Macro('PACKAGEROOT_IMPORT') self.routers = dict() self.commandLines = dict() self.name = packageName self.main_package = False if self.name == 'Common': self.main_package = True if not modObj: # 载入packages下的模块 targetdir = macros.Macro('PACKAGEROOT') + os.sep + packageName if not os.path.exists(targetdir) or not os.path.isdir( targetdir): self.valid = False return modObj = getattr( __import__(package_root_modtype, globals(), locals(), [packageName]), packageName) # 导入模块 if not modObj: self.valid = False return self.valid = True self.fullName = '.'.join((package_root_modtype, packageName)) # 读取doc、version、author以及各个路由器的信息(只在公共接口中__all__)以及路由的命令行格式(如果有) if getattr(modObj, '__all__', None): # 合法的模块 for api in getattr(modObj, '__all__', None): fn = getattr(modObj, api) if callable(fn): # 是一个可调用对象 method = getattr(fn, '__method__', None) path = getattr(fn, '__route__', None) adminAcquire = getattr(fn, '__adminLevel__', None) if method and path: # 添加路由 # self.routers[fn] = {'method': method, 'route': path, 'doc':inspect.getdoc(fn), # 'level':'api' if path.startswith('/api') else 'user', # 'system':self.main_package # } # 可调用对象->{方法,路由,解释,级别,系统} # 读取命令行参数 if not asyncio.iscoroutinefunction( fn) and not inspect.isgeneratorfunction( fn): # 检查是否是异步函数 fn = asyncio.coroutine(fn) # 确定router级别 rt_level = 'user' if path.startswith('/api'): rt_level = 'api' elif path.startswith('/ws'): rt_level = 'websocket' rt = Router.Router( method=method, route=path, doc=inspect.getdoc(fn), level=rt_level, system=self.main_package, func=fn, acquireAdmin=adminAcquire) # 定义成Router 类 self.routers[rt.flagName] = rt if getattr(modObj, '__cmdLines__', None): self.commandLines = modObj.__cmdLines__ # 直接绑定为命令行 self.version = getattr(modObj, '__version__', '') self.author = getattr(modObj, '__author__', '') self.doc = getattr(modObj, '__doc__', '') self.moduleObject = modObj # 构建对象 Package.logger.info('模块对象‘%s’载入成功' % self.name) except Exception as e: # 处理错误 Package.logger.error('初始化模块对象出现问题:%s\n堆栈信息:%s\n' % (str(e.args), traceback.format_exc())) self.valid = False raise e
def run(): """ 运行服务 :return: 不返回值 """ try: global __logger_core initEnv.InitEnv(True) kws = dict() # 运行参数 loop = asyncio.get_event_loop() kws['loop'] = loop kws['logout'] = True # 加载配置设置器 cfgr = config.getConfigManager() kws['configMgr'] = cfgr options, noOptArgs = getopt.getopt( sys.argv[1:], 'm:h:p:r:c:e:n', ( 'monitorpid=', 'host=', 'port=', 'restartSignal=', 'closeSignal=', 'dbname=', 'dbusername='******'dbpassword='******'dbaddress=', 'dbport=', 'dbdbname=', # 数据库方面 'maintenanceSignal=', 'nologout')) for name, value in options: if name in ('-h', '--host'): kws['host'] = value elif name in ('-p', '--port'): kws['port'] = int(value) elif name in ('-n', '--nologout'): kws['logout'] = False # 不显示输出 elif name in ('-m', '--monitorpid'): # 设置监控器PID macros.SetMacro('MONITOR_PID', int(value)) elif name in ('--restartSignal', '-r'): kws['restartSignal'] = value elif name in ('--closeSignal', '-c'): kws['closeSignal'] = value elif name in ('--maintenanceSignal', '-e'): kws['maintenanceSignal'] = value elif name in ('--dbname', ): kws['dbname'] = value elif name in ('--dbusername', ): kws['dbusername'] = value elif name in ('--dbpassword', ): kws['dbpassword'] = value elif name in ('--dbaddress', ): kws['dbaddress'] = value elif name in ('--dbdbname', ): kws['dbdbname'] = value elif name in ('--dbport', ): kws['dbport'] = int(value) else: # 不允许有不同的参数。可能以后会有可选的配置 print('%s\n\n未知的参数 ‘%s’' % (__usage, name), file=sys.stderr) exit(-1) # 是否限定了启动的限定的运行的模块 if len(noOptArgs) == 0: # 读取所有的模块 packages = cfgr.getInstalledPackages() noOptArgs = [ name for name, infos in packages.items() if infos['enable'] ] macros.SetMacro('HOST', kws['host']) macros.SetMacro('PORT', kws['port']) macros.SetMacro('ADDRESS', 'http://%s:%d' % (kws['host'], kws['port'])) signal.signal(signal.SIGTERM, onSignal_KILL) # 初始化日志系统 fp = os.path.sep.join( (macros.Macro('LOGROOT'), 'server-%s.txt' % str(time.time()))) logger.init_logger(fp, kws['logout']) __logger_core = logger.get_logger() except Exception as e: msg = '无法运行Appointed2服务器,因为出现错误%s,消息:%s\n%s' % (str( type(e)), str(e.args), traceback.format_exc()) if __logger_core: __logger_core.error(msg) else: print(msg, file=sys.stderr) exit(-1) else: webApp_obj = None try: # print(macros.macro) webApp_obj = Appointed2Server(*noOptArgs, **kws) webApp_obj.run() except KeyboardInterrupt: pass if webApp_obj: webApp_obj.close()
installed = __runInfo.getInstalledPackages() s = '\n'.join([ '%16s|%12s|%8s|%8s' % (infos['name'], infos['version'], str(infos['enable']), str(infos['system'])) for k, infos in installed.items() ]) return rets % (len(installed), s) if __name__ == '__main__': # 初始化环境 initEnv.InitEnv(True) # 初始化logger 输出 logger.init_logger( macros.sep.join( (macros.Macro('LOGROOT'), 'main-%s.log' % str(time.time()))), True) if os.name != 'nt': signal.signal(signal.SIGTERM, onReceive_killSignal) # 这个是默认的,用于关闭整个程序,Linux # 关联其他信号 signal.signal(getSignalIDByStr(__runInfo.cmds['closeSignal']), lambda n, s: process_kill()) signal.signal(getSignalIDByStr(__runInfo.cmds['restartSignal']), lambda n, s: process_restart()) signal.signal(getSignalIDByStr(__runInfo.cmds['maintenanceSignal']), lambda n, s: process_restart(enterMaintenance=True)) interactive = __runInfo.interactive # 进行用户交互 if not interactive: code = 'RUN' else: