class AnsModuleConsumer(WebsocketConsumer): def __init__(self, *args, **kwargs): super(AnsModuleConsumer, self).__init__(*args, **kwargs) self.redis_instance = RedisOps(settings.REDIS_HOST, settings.REDIS_PORT, 4) self.ans_info = None def connect(self): self.accept() def receive(self, text_data=None, bytes_data=None): self.ans_info = json.loads(text_data) group_ids = self.ans_info['hostGroup'] host_ids = self.ans_info['ans_group_hosts'] selected_module_name = self.ans_info['ansibleModule'] custom_model_name = self.ans_info.get('customModule', None) module_args = self.ans_info['ansibleModuleArgs'] self.run_model(group_ids, host_ids, selected_module_name, custom_model_name, module_args) def disconnect(self, code): pass def run_model(self, group_ids, host_ids, selected_module_name, custom_model_name, module_args): gen_resource = GenResource() if group_ids == ['custom'] or group_ids == ['all']: resource = gen_resource.gen_host_list(host_ids) else: resource = gen_resource.gen_group_dict(group_ids) host_list = [ServerAssets.objects.get(id=host_id).assets.asset_management_ip for host_id in host_ids] module_name = selected_module_name if selected_module_name != 'custom' else custom_model_name unique_key = '{}.{}.{}'.format(host_ids, module_name, module_args) if self.redis_instance.get(unique_key): self.send('<code style="color: #FF0000">\n有相同的任务正在进行!请稍后重试!\n</code>', close=True) else: try: self.redis_instance.set(unique_key, 1) ans = ANSRunner(resource, become='yes', become_method='sudo', become_user='******', sock=self) ans.run_module(host_list=host_list, module_name=module_name, module_args=module_args) module_record.delay(ans_user=UserProfile.objects.get(id=self.ans_info['run_user']), ans_remote_ip=self.ans_info['remote_ip'], ans_module=module_name, ans_args=module_args, ans_server=host_list, ans_result=ans.get_module_results) except Exception as e: self.send('<code style="color: #FF0000">\nansible执行模块出错:{}\n</code>'.format(str(e))) finally: self.redis_instance.delete(unique_key) self.close()
class AnsPlaybookConsumer(WebsocketConsumer): def __init__(self, *args, **kwargs): super(AnsPlaybookConsumer, self).__init__(*args, **kwargs) self.redis_instance = RedisOps(settings.REDIS_HOST, settings.REDIS_PORT, 4) self.ans_info = None def connect(self): self.accept() def receive(self, text_data=None, bytes_data=None): self.ans_info = json.loads(text_data) group_ids = self.ans_info['group_ids'] playbook_id = self.ans_info['playbook_id'] self.run_playbook(group_ids, playbook_id) def disconnect(self, code): pass def run_playbook(self, group_ids, playbook_id): playbook = AnsiblePlaybook.objects.select_related('playbook_user').get( id=playbook_id) unique_key = '{}.{}'.format(playbook.playbook_name, group_ids) if self.redis_instance.get(unique_key): self.send( '<code style="color: #FF0000">\n有相同的任务正在进行!请稍后重试!\n</code>', close=True) else: try: self.redis_instance.set(unique_key, 1) resource = GenResource().gen_group_dict(group_ids) ans = ANSRunner(resource, sock=self) ans.run_playbook(playbook.playbook_file.path) playbook_record.delay( playbook_user=UserProfile.objects.get( id=self.ans_info['run_user']), playbook_remote_ip=self.ans_info['remote_ip'], playbook_name=playbook.playbook_name, playbook_result=ans.get_playbook_results) except Exception as e: self.send( '<code style="color: #FF0000">\nansible执行playbook出错:{}\n</code>' .format(str(e))) finally: self.redis_instance.delete(unique_key) self.close()
class DeployConsumer(WebsocketConsumer): def __init__(self, *args, **kwargs): super(DeployConsumer, self).__init__(*args, **kwargs) self.redis_instance = RedisOps(settings.REDIS_HOST, settings.REDIS_PORT, 5) self.deploy_results = [] self.config = None self.d_type = None self.release_name = None self.release_desc = None self.host_list = None self.branch_tag = None def connect(self): self.accept() def receive(self, text_data=None, bytes_data=None): info = json.loads(text_data) self.config = ProjectConfig.objects.select_related('project').get( id=info.get('config_id')) unique_key = self.config.project.project_name + self.config.project.project_env if self.redis_instance.get(unique_key): self.send('有相同的任务正在进行!请稍后重试!') self.close() else: self.redis_instance.set(unique_key, 1) timeline_header = '<li><i class="fa fa-flag bg-blue"></i><div class="timeline-item"><h3 class="timeline-header"><a href="javascript:void(0)">{}</a></h3><div class="timeline-body"></div></div></li>' cmd_detail = '<p style="font-style: italic; color: grey;">{}</p>' timeline_body_green = '<p style="color: #008000">{}</p>' timeline_body_red = '<p style="color: #FF0000">{}</p>' self.branch_tag = info.get('branch_tag') rollback = info.get('rollback', False) self.d_type = 'rollback' if rollback else 'deploy' commit = info.get('commit', None) self.release_name = commit if commit else self.branch_tag # 初始化ansible server_objs = self.config.deploy_server.all() host_ids = [server.id for server in server_objs] resource = gen_resource.GenResource().gen_host_list( host_ids=host_ids) self.host_list = [ server.assets.asset_management_ip for server in server_objs ] ans = ansible_api_v2.ANSRunner(resource, sock=self) if self.config.repo == 'git': tool = GitTools(repo_url=self.config.repo_url, path=self.config.src_dir, env=self.config.project.project_env) tool.checkout(self.branch_tag) if commit: tool.checkout(commit) self.release_desc = tool.get_commit_msg( self.branch_tag, commit) else: self.release_desc = self.release_name self.deploy(rollback, timeline_header, cmd_detail, timeline_body_green, timeline_body_red, ans, info, tool) elif self.config.repo == 'svn': tool = SVNTools(repo_url=self.config.repo_url, path=self.config.src_dir, env=self.config.project.project_env, username=self.config.repo_user, password=self.config.repo_password) if commit: model_name = '' if self.config.repo_model == 'trunk' else self.branch_tag self.release_desc = tool.get_commit_msg( int(commit), self.config.repo_model, model_name=model_name) else: self.release_desc = self.release_name c = int(commit) if commit else None self.deploy(rollback, timeline_header, cmd_detail, timeline_body_green, timeline_body_red, ans, info, tool, repo='svn', commit=c) self.close() self.redis_instance.delete(unique_key) def disconnect(self, close_code): if '<p style="color: #FF0000">所有主机均部署失败!退出部署流程!</p>' in self.deploy_results: self.deploy_results = self.deploy_results[:self.deploy_results.index( '<p style="color: #FF0000">所有主机均部署失败!退出部署流程!</p>') + 1] deploy_log.delay(project_config=self.config, deploy_user=self.scope['user'], d_type=self.d_type, branch_tag=self.branch_tag, release_name=self.release_name, release_desc=self.release_desc, result=self.deploy_results) def deploy(self, rollback, timeline_header, cmd_detail, timeline_body_green, timeline_body_red, ans, info, tool, repo='git', commit=None): if repo == 'svn': # 执行检出代码之前的命令,比如安装依赖等 if self.config.prev_deploy: self.send_save(timeline_header.format('执行检出代码前置任务')) self.send_save(cmd_detail.format(self.config.prev_deploy)) try: code = tool.run_cmd(self.config.prev_deploy) if code == 0: self.send_save( timeline_body_green.format('执行检出代码前置任务成功!')) else: self.send_save( timeline_body_red.format('执行检出代码前置任务失败!'), close=True) except Exception as e: self.send_save(timeline_body_red.format( '执行检出代码前置任务失败!{}'.format(e)), close=True) # 执行检出代码任务 try: self.send_save(timeline_header.format('执行检出代码任务')) if self.config.repo_model == 'trunk': tool.checkout(self.config.repo_model, revision=commit) else: tool.checkout(self.config.repo_model, self.branch_tag, revision=commit) self.send_save(timeline_body_green.format('执行检出代码任务成功!')) except Exception as e: self.send_save(timeline_body_red.format( '执行检出代码任务失败!'.format(e)), close=True) if not rollback: # 执行同步代码之前的命令,比如编译等 if self.config.post_deploy: self.send_save(timeline_header.format('执行同步代码前置任务')) self.send_save(cmd_detail.format(self.config.post_deploy)) try: code = tool.run_cmd(self.config.post_deploy) if code == 0: self.send_save( timeline_body_green.format('执行同步代码前置任务成功!')) else: self.send_save( timeline_body_red.format('执行同步代码前置任务失败!'), close=True) except Exception as e: self.send_save(timeline_body_red.format( '执行同步代码前置任务失败!{}'.format(e)), close=True) # 检测目标机器是否连通,如果连通,判断是否存在存储代码版本的路径,如果不存在就创建 self.send_save(timeline_header.format('检测目标机器是否连通')) ans.run_module(self.host_list, module_name='file', module_args='path={} state=directory'.format( os.path.join(self.config.deploy_releases, tool.proj_name)), deploy=True) # 将代码同步至目标服务器 try: des_dir = self.gen_dir(tool, info) # 通过判断路径中是否存在target目录确定是否是JAVA项目 target_path = os.path.join(tool.proj_path, 'target') src_dir = '{}/{}/'.format( target_path, tool.proj_name) if os.path.exists( target_path) else tool.proj_path + '/' self.send_save(timeline_header.format('执行同步代码任务')) self.sync_code(ans, self.host_list, src_dir, des_dir, excludes=self.config.exclude) # 如果运行服务的用户不是root,就将代码目录的属主改为指定的user if self.config.run_user != 'root': ans.run_module( self.host_list, module_name='file', module_args='path={} owner={} recurse=yes'.format( des_dir, self.config.run_user), deploy=True, send_msg=False) # 将版本保存到数据库 if self.release_name not in self.config.versions.split(','): version = ',' + self.release_name if self.config.versions else self.release_name self.config.versions += version version_list = self.config.versions.split(',') if len(version_list) > self.config.releases_num: self.config.versions = ','.join( version_list[len(version_list) - self.config.releases_num:]) self.config.save() self.del_release(ans, self.host_list, path=os.path.join(self.config.deploy_releases, tool.proj_name), releases_num=self.config.releases_num) except Exception as e: self.send_save(timeline_body_red.format( '执行同步代码任务失败!{}'.format(e)), close=True) # 执行部署前任务 if self.config.prev_release: self.send_save(timeline_header.format('执行部署前置任务')) self.send_save(cmd_detail.format(self.config.prev_release)) try: self.run_cmds(ans, self.host_list, self.config.prev_release) except Exception as e: self.send_save(timeline_body_red.format( '执行部署前置任务失败!{}'.format(e)), close=True) # 配置软连接,指向指定的版本目录 self.send_save(timeline_header.format('执行部署任务')) try: src = self.gen_dir(tool, info) dest = os.path.join(self.config.deploy_webroot, tool.proj_name) ans.run_module(self.host_list, module_name='shell', module_args='rm -rf {} && ln -s {} {}'.format( dest, src, dest), deploy=True) except Exception as e: self.send_save(timeline_body_red.format('执行部署任务失败!{}'.format(e)), close=True) # 执行部署后任务 if self.config.post_release: self.send_save(timeline_header.format('执行部署后置任务')) self.send_save(cmd_detail.format(self.config.post_release)) try: self.run_cmds(ans, self.host_list, self.config.post_release) except Exception as e: self.send_save(timeline_body_red.format( '执行部署后置任务失败!{}'.format(e)), close=True) @staticmethod def sync_code(ans, host_list, src_dir, des_dir, excludes=None): if excludes: opts = '' for i in excludes.split('\n'): if i.startswith('#'): continue opts += ',--exclude=' + i opts = opts.lstrip(',') ans.run_module( host_list, module_name='synchronize', module_args='src={} dest={} delete=yes rsync_opts="{}"'.format( src_dir, des_dir, opts), deploy=True) else: ans.run_module(host_list, module_name='synchronize', module_args='src={} dest={} delete=yes'.format( src_dir, des_dir), deploy=True) @staticmethod def run_cmds(ans, host_list, cmds): c = '' for cmd in cmds.split('\n'): if cmd.startswith('#'): continue c += cmd + ' && ' c = c.rstrip(' && ') ans.run_module(host_list, module_name='shell', module_args=c, deploy=True) def gen_dir(self, tool, info): commit = info.get('commit', None) des_dir = os.path.join( self.config.deploy_releases, tool.proj_name, '{}'.format(commit)) if commit else os.path.join( self.config.deploy_releases, tool.proj_name, '{}'.format( self.branch_tag)) return des_dir @staticmethod def del_release(ans, host_list, path, releases_num): """按照数据库设置的保留版本个数,删除最早的多余的版本""" ans.run_module( host_list, module_name='shell', module_args='cd {path} && rm -rf `ls -t | tail -n +{releases_num}`' .format(path=path, releases_num=releases_num + 1), deploy=True, send_msg=False) def send_save(self, msg, close=False, send=True): if send: self.send(msg, close=close) self.deploy_results.append(msg)
class TicketDeployConsumer(WebsocketConsumer): def __init__(self, *args, **kwargs): super(TicketDeployConsumer, self).__init__(*args, **kwargs) self.redis_instance = RedisOps(settings.REDIS_HOST, settings.REDIS_PORT, 5) self.deploy_results = [] self.host_fail = [] self.config = None self.d_type = None self.release_name = None self.release_desc = None self.host_list = None self.branch_tag = None def connect(self): self.accept() def receive(self, text_data=None, bytes_data=None): # {"tid": "19", "pid": "3", "dserver": ["2", "3"], "d_type": "deploy"} info = json.loads(text_data) self.config = Project_Deploy_Ticket.objects.get(id=info.get('tid')) unique_key = self.config.ticket_config.proj_uuid self.d_type = info.get('d_type').capitalize() step_msg = "[{} " "{}] <font size=\"2\" color=\"blue\">{}</font></br>".format( time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), self.d_type, {}) ok_msg = "[{} " "{}] <font size=\"2\" color=\"blue\">{}...OK</font></br>".format( time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), self.d_type, {}) fail_msg = "[{} " "{}] <font size=\"2\" color=\"color: #FF0000\">{}...Error</font></br>".format( time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), self.d_type, {}) warn_msg = "[{} " "{}] <font size=\"2\" color=\"orange\">{}...warning</font></br>".format( time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), self.d_type, {}) if self.redis_instance.get(unique_key): self.send_save(fail_msg.format("有相同的任务正在进行,请稍后重试!")) self.close() else: self.redis_instance.set(unique_key, 1) ticket_no = self.config.ticket_no if self.d_type == 'Dcheck': self.deploy_check(ok_msg) elif self.config.ticket_status == 3 or self.config.ticket_status == 4: self.send_save(fail_msg.format("工单已关闭,请联系管理员!"), close=True) return elif self.d_type == 'Deploy': self.branch_tag = self.config.ticket_config.proj_branch_tag commit = self.config.ticket_commit self.release_name = commit if commit else self.branch_tag if self.config.ticket_config.proj_role.repo == 'git': tool = GitTools( repo_url=self.config.ticket_config.proj_role.repo_url, path=self.config.ticket_config.proj_role.src_dir, env=self.config.ticket_config.proj_env.projenv_name) try: tool.checkout(self.branch_tag) self.send_save(ok_msg.format("切换分支:" + self.branch_tag)) if commit: self.send_save( ok_msg.format("Commit ID:" + commit[:7])) tool.checkout(commit) self.release_desc = tool.get_commit_msg( self.branch_tag, commit) else: self.release_desc = self.release_name self.send_save( ok_msg.format("Commit Msg: " + self.release_desc)) except Exception as e: self.send_save(fail_msg("检出代码"), close=True) self.deploy(ticket_no, info, tool, step_msg, ok_msg, fail_msg, warn_msg) elif self.config.ticket_config.proj_role.repo == 'svn': tool = SVNTools( repo_url=self.config.ticket_config.proj_role.repo_url, path=self.config.ticket_config.proj_role.src_dir, env=self.config.ticket_config.proj_env.projenv_name, username=self.config.ticket_config.proj_role.repo_user, password=self.config.ticket_config.proj_role. repo_password) self.send_save(step_msg.format("【检出SVN代码】")) if commit: self.send_save( ok_msg.format("Commit ID: " + commit[:7])) model_name = '' if self.config.ticket_config.proj_role.repo_model == 'trunk' else self.brahch_tag self.release_desc = tool.get_commit_msg( int(commit), self.config.ticket_config.proj_role.repo_model, model_name=model_name) else: self.release_desc = self.release_name self.send_save( ok_msg.format("Commit Msg: " + self.release_desc)) c = int(commit) if commit else None # 执行检出代码之前的命令,比如安装依赖等 if self.config.ticket_config.proj_role.prev_deploy: try: code = tool.run_cmd(self.config.ticket_config. proj_role.prev_deploy) if code == 0: self.send_save(ok_msg.format("执行检出代码之前的命令")) else: self.send_save(fail_msg("执行检出代码之前的命令"), close=True) except Exception as e: self.send_save(fail_msg("执行检出代码之前的命令"), close=True) # 执行检出代码任务 try: if self.config.ticket_config.proj_role.repo_model == 'trunk': tool.checkout( self.config.ticket_config.proj_role.repo_model, revision=c) self.send_save(ok_msg.format("检出代码Trunk版本号: " + c)) else: tool.checkout( self.config.ticket_config.proj_role.repo_model, self.branch_tag, revision=commit) self.send_save( ok_msg.format("检出分支: " + self.branch_tag + "版本号: ")) except Exception as e: self.send_save(fail_msg("执行检出"), close=True) self.deploy(ticket_no, info, tool, step_msg, ok_msg, fail_msg, warn_msg) self.close() self.redis_instance.delete(unique_key) elif self.d_type == 'Rollback': if self.config.ticket_config.proj_role.repo == 'git': tool = GitTools( repo_url=self.config.ticket_config.proj_role.repo_url, path=self.config.ticket_config.proj_role.src_dir, env=self.config.ticket_config.proj_env.projenv_name) self.deploy(ticket_no, info, tool, step_msg, ok_msg, fail_msg, warn_msg) elif self.config.ticket_config.proj_role.repo == 'svn': tool = SVNTools( repo_url=self.config.ticket_config.proj_role.repo_url, path=self.config.ticket_config.proj_role.src_dir, env=self.config.ticket_config.proj_env.projenv_name, username=self.config.ticket_config.proj_role.repo_user, password=self.config.ticket_config.proj_role. repo_password) self.deploy(ticket_no, info, tool, step_msg, ok_msg, fail_msg, warn_msg) else: pass else: pass def deploy_check(self, ok_msg): count = 0 for pid in self.config.ticket_platform.all(): for ser in Assets.objects.filter( asset_projenv=self.config.ticket_config.proj_env.id ).filter(asset_platform=pid).filter( asset_projapp__id=self.config.ticket_config.proj_app.id): dt = Project_Deploy_Record.objects.filter( deploy_ip=ser.asset_management_ip, d_ticket_id=self.config.id) if dt.count() == 0: self.send_save( ok_msg.format("{}: {} {} {} 【无发布记录】").format( pid.platform_name, ser.asset_management_ip, ser.asset_hostname, 'Available' if ser.asset_status == 0 else "Inactive")) if ser.asset_status == 0: count += 1 else: self.send_save( ok_msg.format("{}: {} {} {} {} {} {} {}").format( pid.platform_name, ser.asset_management_ip, ser.asset_hostname, 'Available' if ser.asset_status == 0 else "Inactive", '【发布成功】' if dt[0].deploy_status == 9 else "【发布失败】", dt[0].deploy_times, '【回滚成功】' if dt[0].rollback_status == 1 else 'None', 'None' if dt[0].rollback_times == 0 else dt[0].rollback_times)) if dt[0].deploy_status != 9: count += 1 self.send_save( ok_msg.format("Deploy Check Finished! Count: {}").format(count), close=True) def disconnect(self, close_code): self.send_save("def disconnect:") def deploy(self, ticket_no, info, tool, step_msg, ok_msg, fail_msg, warn_msg): host_ids = info.get('dserver') # 这里检测发布(回滚)服务器 tid = Project_Deploy_Ticket.objects.get(id=info.get('tid')) dsList = [] for hs in host_ids: ds = Assets.objects.get(id=hs) dt = Project_Deploy_Record.objects.filter( deploy_ip=ds.asset_management_ip, d_ticket_id=info.get('tid')) if dt.count() == 1: # 是否有发布记录:有 if self.d_type == "Rollback": # 不要移动这个代码段位置,否则会逻辑错误!必需要先判断是否是回滚 if dt[0].rollback_times != dt[0].deploy_times: self.send_save( ok_msg.format("{ip} {hs} 第{times}次回滚").format( ip=ds.asset_management_ip, hs=ds.asset_hostname, times=dt[0].rollback_times + 1, )) try: # 回滚次数+1 dsList.append(hs) dt.update(rollback_times=dt[0].rollback_times + 1, update_date=time.strftime( "%Y-%m-%d %H:%M:%S", time.localtime())) except Exception as e: self.send_save( fail_msg.format( "更新 rollback_times + 1 回滚次数异常!{}").format( e)) else: self.send_save(fail_msg.format("回滚次数不能大于发布次数!")) elif dt[0].deploy_status == 9: # 判断记录是否发布成功:发布成功 if self.d_type == "Deploy": self.send_save( warn_msg.format( "{ip} {hs} 发布记录为成功,请勿重复发布,踢出发布列表!").format( ip=ds.asset_management_ip, hs=ds.asset_hostname if ds.asset_hostname else 'null')) if dt[0].deploy_times == 1: self.send_save( warn_msg.format( "{ip} {hs} 回滚记录为成功,请确认部署状态!").format( ip=ds.asset_management_ip, hs=ds.asset_hostname if ds.asset_hostname else 'null')) elif dt[0].deploy_status < 9: # 判断记录是否发布成功:没有发成功 if self.d_type == 'Deploy': self.send_save( ok_msg.format( "{ip} {hs} 第{times}次发布 Start").format( ip=ds.asset_management_ip, hs=ds.asset_hostname, times=dt[0].deploy_times + 1, )) try: # 发布次数+1 dsList.append(hs) dt.update(deploy_times=dt[0].deploy_times + 1, update_date=time.strftime( "%Y-%m-%d %H:%M:%S", time.localtime())) except Exception as e: self.send_save( fail_msg.format( "更新 deploy_times + 1 发布次数异常!{}").format(e)) elif dt.count() == 0: # 是否有发布记录:无 if self.d_type == 'Deploy': self.send_save( ok_msg.format("{ip} {hs} 第1次发布 Start").format( ip=ds.asset_management_ip, hs=ds.asset_hostname if ds.asset_hostname else 'null')) try: # 创建发布记录表 dtc = Project_Deploy_Record.objects.create( assets=ds, deploy_ip=ds.asset_management_ip, deploy_times=1, d_ticket_id=tid, ) dsList.append(hs) except Exception as e: self.send_save( fail_msg.format("创建发布记录表异常! {}").format(e)) return elif self.d_type == 'Rollback': self.send_save( fail_msg.format("{} 没有发布记录,回滚终止!").format( ds.asset_management_ip)) else: pass if dsList: resource = gen_resource.GenResource().gen_host_list( host_ids=dsList) self.host_list = [server['ip'] for server in resource] ans = ansible_api_v2.ANSRunner(resource, sock=self) else: self.send_save(fail_msg.format("没有发布(回滚)主机,部署终止!"), close=True) return if self.d_type == 'Deploy': # 执行同步代码之前的命令,比如编译等 if self.config.ticket_config.proj_role.post_deploy: try: code = tool.run_cmd( self.config.ticket_config.proj_role.post_deploy) if code == 0: self.send_save(ok_msg.format("执行同步代码之前的命令成功")) else: self.send_save(fail_msg.format("执行同步代码之前的命令失败")) return except Exception as e: self.send_save( fail_msg.format("执行同步代码之前的命令异常! {}").format(e)) return # 检测目标机器是否连通,如果连通,判断是否存在存储代码版本的路径,如果不存在就创建 ans.run_module( self.host_list, module_name='file', module_args='path={} state=directory'.format( os.path.join( self.config.ticket_config.proj_role.deploy_releases, tool.proj_name)), deploy=True, d_type=self.d_type, task='connet', tid=tid.id) # 备份代码目录 try: backup_dir = self.gen_dir( tool, ticket_no, backup="backup") + "/" + time.strftime("%Y%m%d%H%M%S") backup_dest = os.path.join( self.config.ticket_config.proj_role.deploy_webroot, tool.proj_name) ans.run_module( self.host_list, module_name='shell', module_args='mkdir -p {} && /bin/cp -rf {}/* {}'.format( backup_dir, backup_dest, backup_dir), deploy=True, d_type=self.d_type, task="backup", tid=tid.id) except Exception as e: self.send_save(fail_msg.format("目标服务器备份代码异常: {}").format(e)) return # 将代码同步至目标服务器 try: # /data/version/urlmonitor/123131232121238 des_dir = self.gen_dir(tool, ticket_no) # 通过判断路径中是否存在target目录确定是否是JAVA项目 # target_path /data/urlmonitor/UAT/target target_path = os.path.join(tool.proj_path, 'target') java_proj = os.path.exists(target_path) if java_proj: self.send_save(ok_msg.format("是JAVA项目")) else: self.send_save(ok_msg.format("非JAVA项目")) # 代码同步源目录 src_dir = '{}/{}/'.format( target_path, tool.proj_name) if os.path.exists( target_path) else tool.proj_path + '/' self.sync_code( ans, self.host_list, src_dir, des_dir, excludes=self.config.ticket_config.proj_role.exclude, d_type=self.d_type, task="sync_code", tid=tid.id) # 如果运行服务的用户不是root,就将代码目录的属主改为指定的user if self.config.ticket_config.proj_role.run_user != 'root': ans.run_module( self.host_list, module_name='file', module_args='path={} owner={} recurse=yes'.format( des_dir, self.config.ticket_config.proj_role.run_user), deploy=True, send_msg=False, d_type=self.d_type, task="owner", tid=tid.id) except Exception as e: self.send_save(fail_msg.format("代码同步至目标服务器异常 {}").format(e)) return # 执行部署前任务 if self.config.ticket_config.proj_role.prev_release: try: self.run_cmds( ans, self.host_list, self.config.ticket_config.proj_role.prev_release, d_type=self.d_type, task="prev_release", tid=tid.id) except Exception as e: self.send_save( fail_msg.format("目标服务器部署前任务异常 {}").format(e)) # 配置软连接,指向指定的版本目录 try: src = self.gen_dir(tool, ticket_no) dest = os.path.join( self.config.ticket_config.proj_role.deploy_webroot, tool.proj_name) ans.run_module(self.host_list, module_name='shell', module_args='rm -rf {} && ln -sf {} {}'.format( dest, src + '/', dest), deploy=True, d_type=self.d_type, task="release", tid=tid.id) except Exception as e: self.send_save(fail_msg.format("目标服务器部署异常 {}").format(e)) # 执行部署后任务 if self.config.ticket_config.proj_role.post_release: try: self.run_cmds( ans, self.host_list, self.config.ticket_config.proj_role.post_release, d_type=self.d_type, task='post_release', tid=tid.id) except Exception as e: self.send_save( fail_msg.format("目标服务器部署后任务异常 {}").format(e)) elif self.d_type == 'Rollback': self.send_save(ok_msg.format("开始回滚!")) # 回滚代码 try: backup_dir = self.gen_dir(tool, ticket_no, backup="backup") backup_dest = os.path.join( self.config.ticket_config.proj_role.deploy_webroot, tool.proj_name) print(backup_dir, backup_dest) ans.run_module( self.host_list, module_name='raw', module_args= 'cd {bak_dir} && back_dir\=`ls -lt |egrep \^d|egrep -v rollback\$|cut -d " " -f 9|egrep \^[2][0-9]|head -n 1` && mv $back_dir $back_dir.rollback && rm -rf {bak_dest} && ln -sf {bak_dir}/$back_dir.rollback {bak_dest}' .format( bak_dir=backup_dir, bak_dest=backup_dest, ), deploy=True, d_type=self.d_type, task="rollback", tid=tid.id) except Exception as e: self.send_save(fail_msg.format("目标服务器回滚代码异常: {}").format(e)) return @staticmethod def sync_code(ans, host_list, src_dir, des_dir, excludes=None, d_type=None, task=None, tid=None): d_type = d_type task = task tid = tid if excludes: opts = '' for i in excludes.split('\n'): if i.startswith('#'): continue opts += ',--exclude=' + i opts = opts.lstrip(',') ans.run_module( host_list, module_name='synchronize', module_args='src={} dest={} delete=yes rsync_opts="{}"'.format( src_dir, des_dir, opts), deploy=True, d_type=d_type, task=task) else: ans.run_module(host_list, module_name='synchronize', module_args='src={} dest={} delete=yes'.format( src_dir, des_dir), deploy=True, d_type=d_type, task=task, tid=tid) @staticmethod def run_cmds(ans, host_list, cmds, d_type=None, task=None, tid=None): d_type = d_type task = task tid = tid c = '' for cmd in cmds.split('\n'): if cmd.startswith('#'): continue c += cmd + ' && ' c = c.rstrip(' && ') ans.run_module(host_list, module_name='shell', module_args=c, deploy=True, d_type=d_type, task=task, tid=tid) def gen_dir(self, tool, ticket_no, backup=None): ticket_no = ticket_no backup = backup if backup: des_dir = os.path.join( self.config.ticket_config.proj_role.deploy_releases, tool.proj_name, backup, '{}'.format(ticket_no)) else: des_dir = os.path.join( self.config.ticket_config.proj_role.deploy_releases, tool.proj_name, '{}'.format(ticket_no)) return des_dir @staticmethod def del_release(ans, host_list, path, dir_name): """按照数据库设置的保留版本个数,删除最早的多余的版本""" ans.run_module(host_list, module_name='shell', module_args='cd {path} && rm -rf {dir_name}'.format( path=path, dir_name=dir_name), deploy=True, send_msg=False) def send_save(self, msg, close=False, send=True): if send: self.send(msg, close=close) self.deploy_results.append(msg)