def install_depends(argv, prog): """ 安装程序需要的依赖(假定是在当前目录下获取octopus.prj,用处是帮助在本地搭建环境) """ argparser = argparse.ArgumentParser(description="octopus install_depends", prog=prog) argparser.add_argument('-c', '--conf', dest='config_file', help='octopus配置文件路径, 寻找优先级为./octopus.conf > ~/.octopus/octopus.conf') argparser.add_argument('-t', '--type', dest='type',default='all', help='要安装的依赖类型,取值为compile, runtime, all') args = argparser.parse_args(argv) config_file = get_config_file(args.config_file) print 'use config ' + config_file if config_file is None: print "please specift config file" return 1 if args.type != 'compile' and args.type != 'runtime' and args.type != 'all': print 'wrong type, must be [compile, runtime, all]' return 1 citool = CITool(config_file) program_path = '.' program_info = ProgramInfo(program_path) citool.install_depends(program_info.get_depends(), args.type)
def deploy(self, program_path, deploy_env): """ @program_path: 程序在本地的地址. @env: 要部署环境 pre-condition:当前工作目录处在要部署的模块下. 从octopus.prj文件中读取依赖哪些库. 从METAINFO/deploy.inf中读取依赖的版本,如果没有该文件,则都用最新版本(build号最大的). 从deploy.conf文件中读取部署到哪里 拷贝模块自身到目标位置 拷贝依赖模块到目标位置(这个不好弄,依赖模块的路径怎么设定定?) 在远程机器上运行setup 脚本 在本机运行冒烟测试脚本(smoketest) """ print 'start deploy ' + program_path + ', env=' + deploy_env REMOTE_PROGRAM_BASE = '~/program' REMOTE_RUN_BASE = '~/service' program_info = ProgramInfo(program_path) depends = program_info.get_depends() build_no = program_info.get_buildmeta().build_number scripts = program_info.get_scripts() # 读取部署位置文件,将文件拷贝过去. deploy_conf_file = program_path + '/config/%s/deploy.conf' % (deploy_env) deploy_conf = pyetc.load(deploy_conf_file, deploy_schema.ENV) instances = deploy_conf.CONFIG['instance'] for instance in instances: instance_name = instance['user'] + '@' + instance['host'] + ':' instance_name = instance_name + instance['name'] fabric_env.host_string = instance['host'] print '=================start deploy %s =========' % (instance_name) fabric_env.user = instance['user'] # 生成模版文件,打包成tar.gz. homedir = CITool.get_homedir(pyshell.remote_shell) run_path = REMOTE_RUN_BASE + '/' + instance['name'] run_path = CITool.normalize_path(run_path, homedir) print run_path remote_program_path = REMOTE_PROGRAM_BASE + '/' + instance['name'] if build_no > 0: remote_program_path = '%s-%d' % (remote_program_path, build_no) else: # 如果取不到build号,则取当前时间. cur_time = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time())) remote_program_path = '%s-%s' % (remote_program_path, cur_time) shell_ret = pyshell.remote_shell('if [ -d %s ] ;then exit 1;fi' % ( remote_program_path), warn_only=True) if shell_ret.return_code == 0: # 目录不存在,则重新打包拷贝. pkg_file_full_path = self.make_package(instance, program_info, program_path, deploy_env, run_path, remote_program_path) run('mkdir -p %s' % (remote_program_path)) put(pkg_file_full_path, remote_program_path) local('rm -f ' + pkg_file_full_path) run('cd %s && tar -zxf %s' % ( remote_program_path, os.path.basename(pkg_file_full_path))) # 安装依赖的模块. print '===================<<%s>> start install depends========' % ( instance_name) self.install_depends(depends, 'runtime', {'host':instance['host'], 'user':instance['user']}) # 按顺序执行脚本 install, stop, switch, start,check # install - print '===================<<%s>> start scripts:install========' % ( instance_name) try: self.run_script(instance, 'install', scripts, remote_program_path, pyshell.remote_shell) except Exception, e: print 'step=install host=%s program=%s e=%s' % ( instance['host'], remote_program_path, e) raise e print '===================<<%s>> stop ========' % ( instance_name ) self.stop(run_path) # switch - unlink link, ln -s install_path link print '===================<<%s>> switch link========' % ( instance_name) run('[ -d %s ] || mkdir -p %s' % (run_path, run_path)) run(('cd %s ; if [ -h program_old ]; then mv program_old program_old2;fi;' +' if [ -h program ] ;then mv program program_old; fi ;' +' ln -s %s program') % ( run_path, remote_program_path)) # 启动新的 print '===================<<%s>> start ========' % ( instance_name) try: self.start(run_path) # 进行线上检查 print '===================<<%s>> check ========' % ( instance_name) self.run_script(instance, 'check', scripts, remote_program_path, run_path) # 删除program_old2 print '===================<<%s>> delete old version ========' % ( instance_name) ret = pyshell.remote_shell(('cd %s;' +' if [ -h program_old2 ] ;then ' +'old2=`readlink program_old2`;' +'old=`readlink program_old`;' +'pp=`readlink program`;' + ' if [ "$old2" = "$old" -o "$old2" = "$pp" ];then rm -f program_old2;' +'else rm -rf $old2 program_old2; fi;fi') % (run_path)) except Exception,e: # 如果失败,回滚.回滚怎么重启老版本的服务?octopus.prj文件中的脚本可能发生了变化. # 解决方案1: # 1) 脚本不在octopus中配置,必须写死,但是解决不了多role问题 # 2) 或者重新读远程目录的octopus.prj文件, 仍然解决不了多role问题。 # 3) 必须生成一个部署meta文件,放在METAINFO/deploy.inf中,将role信息写入 # 另外依赖库怎么办?需要重新安装一遍吗? # 看来最好的办法还是将所有的依赖库都放在自己的目录下. # 1.一些第三方库整个维持一个版本(即depend_third每个程序只能有一个版本) # 2. 模块下有个lib目录,放依赖的库和程序. # 这样回滚的时候只需要修改链接就行. print e print '===================<<%s>> rollback ========' % ( instance_name) # stop 正在部署的服务 self.stop(run_path) run(('cd %s ; if [ -h program_fail ];then old_fail=`readlink program_fail`;pp=`readlink program`;' +' if [ "$old_fail" = "$pp" ];then unlink program_fail; else rm -rf $old_fail program_fail;fi;' +'fi; mv program program_fail; ' +'if [ -h program_old ] ; then mv program_old program; fi;' +'if [ -h program_old2 ];then mv program_old2 program_old; fi') % (run_path)) # 切换目录,启动老版本的服务 try: self.start(run_path) except Exception, e: print 'rollback faied ,reson=%s' % (e)