def exec_hook_after_release(self, host): try: after_hook_path = os.path.join( settings.BASE_DIR, 'storage/hooks/after_hook_' + str(self.projectEnvConfig.id)) if os.path.exists(after_hook_path): self.myLoggingService.info('检测到发布后Hook, 准备执行:') with open(after_hook_path) as f: for line in f: self.myLoggingService.info(line.strip()) # 推送脚本 filename = 'opendeploy_hook_after_' + str( self.id) + '_' + get_random_string(30) remote_path = '/tmp/' + filename command = RSYNC_PREFIX + after_hook_path + ' ' + host + ':' + remote_path self.myLoggingService.info(command) commandService = CommandService(command) if commandService.returncode > 0: self.myLoggingService.error('推送脚本异常') return False # @TODO self.myLoggingService.info('开始执行...') release_path = self.get_release_path() if release_path: command = SSH_PREFIX + host + ' "chmod 777 ' + remote_path + ' && export OPENDEPLOY_ID=' + str(self.id) + ' && source /etc/profile && cd ' \ + release_path + ' && ' + remote_path + ' && ' + 'rm -f ' + remote_path + ' 2>&1"' else: command = SSH_PREFIX + host + ' "chmod 777 ' + remote_path + ' && export OPENDEPLOY_ID=' + str(self.id) + ' && source /etc/profile && ' \ + remote_path + ' && ' + 'rm -f ' + remote_path + ' 2>&1"' self.myLoggingService.info(command) commandService = CommandService(command) self.myLoggingService.info('exit_code:' + str(commandService.returncode)) if commandService.returncode > 0: self.myLoggingService.error('发布后hook执行异常') if len(commandService.stdout) > 0: self.myLoggingService.info('脚本输出:') for line in commandService.stdout_as_list: self.myLoggingService.error(line) if len(commandService.stderr) > 0: self.myLoggingService.error('脚本错误:') for line in commandService.stderr_as_list: self.myLoggingService.error(line) return False else: self.myLoggingService.info('发布后hook执行正常') if len(commandService.stdout_as_list) > 0: self.myLoggingService.info('脚本输出:') for line in commandService.stdout_as_list: self.myLoggingService.info(line) return True except: return False
def credential_edit(request, id): credential = Credentials.objects.get(pk=id) auth_type = credential.type if request.method == 'POST': if auth_type == Credentials.TYPE_USER_PWD: f = AddCredentialForPasswordForm(request.POST) else: f = AddCredentialForPrivateForm(request.POST) if f.is_valid(): cleaned_data = f.cleaned_data try: credential.type = auth_type credential.username = cleaned_data['username'] credential.comment = cleaned_data['comment'] if auth_type == Credentials.TYPE_USER_PWD: credential.password = cleaned_data['password'] else: credential.private_key = cleaned_data['private_key'] credential.save() if auth_type != Credentials.TYPE_USER_PWD: key_path = os.path.join( settings.BASE_DIR, 'storage/privary_key/' + str(credential.id)) if len(credential.private_key) > 0: with open(key_path, 'w') as f: f.write(credential.private_key.rstrip() + '\n') command = 'dos2unix ' + key_path CommandService(command) os.chmod(key_path, stat.S_IRWXU) else: if os.path.exists(key_path): os.unlink(key_path) messages.info(request, '修改成功') except: messages.error(request, '修改失败') finally: return redirect('/admin/deploy/credential') else: if auth_type == Credentials.TYPE_USER_PWD: f = AddCredentialForPasswordForm() else: f = AddCredentialForPrivateForm() return render( request, 'admin/deploy/edit_credential.html', { "form": f, "credential": credential, "type_choices": Credentials.TYPE_CHOICES, "auth_type": auth_type, "type_user_pwd": Credentials.TYPE_USER_PWD, })
def exec_hook_before_release(self, working_dir=None): try: before_hook_path = os.path.join( settings.BASE_DIR, 'storage/hooks/before_hook_' + str(self.projectEnvConfig.id)) if os.path.exists(before_hook_path): if working_dir is None: working_dir = '/app' commandService = CommandService('cd ' + working_dir + ' && ' + before_hook_path) self.myLoggingService.info('检测到发布前Hook, 准备执行:') self.myLoggingService.info('command:' + before_hook_path) with open(before_hook_path) as f: for line in f: self.myLoggingService.info(line.strip()) self.myLoggingService.info('exit_code:' + str(commandService.returncode)) if commandService.returncode > 0: self.myLoggingService.error('发布前hook执行异常') if len(commandService.stdout) > 0: self.myLoggingService.info('脚本输出:') for line in commandService.stdout_as_list: self.myLoggingService.error(line) if len(commandService.stderr) > 0: self.myLoggingService.error('脚本错误:') for line in commandService.stderr_as_list: self.myLoggingService.error(line) return False else: self.myLoggingService.info('发布前hook执行正常') if len(commandService.stdout_as_list) > 0: self.myLoggingService.info('脚本输出:') for line in commandService.stdout_as_list: self.myLoggingService.info(line) return True except: return False
def project_add(request): if request.method == 'POST': f = ProjectForm(request.POST) if f.is_valid(): try: cleaned_data = f.cleaned_data project = Project() project.name = cleaned_data['name'] project.vcs_type = cleaned_data['vcs_type'] project.repository_url = cleaned_data['repository_url'] project.credentials = cleaned_data['credentials'] project.dest_path = cleaned_data['dest_path'] project.comment = cleaned_data['comment'] project.deploy_mode = cleaned_data['deploy_mode'] project.dingding_robot_webhook = cleaned_data[ 'dingding_robot_webhook'] project.status = cleaned_data['status'] project.exclude_file = cleaned_data['exclude_file'] project.rsync_enable_delete = cleaned_data[ 'rsync_enable_delete'] project.enable_mail_notify = cleaned_data['enable_mail_notify'] project.save() exclude_file_path = os.path.join( settings.BASE_DIR, 'storage/exclude_file/' + str(project.id)) if len(project.exclude_file) > 0: with open(exclude_file_path, 'w') as f: f.write(project.exclude_file) # 增加环境关联值 envs = request.POST.getlist('env') if envs: for env in envs: projectEnvConfig = ProjectEnvConfig() projectEnvConfig.project = project try: projectEnvConfig.env = Env.objects.get(pk=env) except: pass projectEnvConfig.branch = request.POST.get('branch_' + env) projectEnvConfig.before_hook = request.POST.get( 'before_hook_' + env) projectEnvConfig.after_hook = request.POST.get( 'after_hook_' + env) if request.POST.get('host_group_' + env): try: projectEnvConfig.host_group = HostGroup.objects.get( pk=request.POST.get('host_group_' + env)) except: pass projectEnvConfig.save() before_hook_path = os.path.join( settings.BASE_DIR, 'storage/hooks/before_hook_' + str(projectEnvConfig.id)) if len(projectEnvConfig.before_hook) > 0: with open(before_hook_path, 'w') as f: f.write(projectEnvConfig.before_hook) after_hook_path = os.path.join( settings.BASE_DIR, 'storage/hooks/after_hook_' + str(projectEnvConfig.id)) if len(projectEnvConfig.after_hook) > 0: with open(after_hook_path, 'w') as f: f.write(projectEnvConfig.after_hook) if os.path.exists(before_hook_path): os.chmod(before_hook_path, stat.S_IRWXU) command = 'dos2unix ' + before_hook_path CommandService(command) if os.path.exists(after_hook_path): os.chmod(after_hook_path, stat.S_IRWXU) command = 'dos2unix ' + after_hook_path CommandService(command) messages.info(request, '添加成功') except: messages.error(request, '添加失败') finally: return redirect('admin:deploy.project') else: messages.error(request, '表单校验失败') else: f = ProjectForm() return render( request, 'admin/deploy/add_project.html', { "form": f, "status_choices": Project.STATUS_CHOICES, "type_choices": Project.TYPE_CHOICES, "credentials": Credentials.objects.all(), "deploy_mode_choices": Project.DEPLOY_MODE_CHOICES, "envlist": Env.objects.all(), "host_group": HostGroup.objects.all(), })
def project_edit(request, id): project = Project.objects.get(pk=id) projectEnvConfig = ProjectEnvConfig.objects.filter(project=project) # 该项目环境id列表 env_list_by_project = [] for v in projectEnvConfig: env_list_by_project.append(v.env.id) if request.method == 'POST': f = ProjectForm(request.POST, instance=project) if f.is_valid(): cleaned_data = f.cleaned_data try: f.save() exclude_file_path = os.path.join( settings.BASE_DIR, 'storage/exclude_file/' + str(id)) if len(cleaned_data['exclude_file']) > 0: with open(exclude_file_path, 'w') as f: f.write(cleaned_data['exclude_file'].encode()) else: if os.path.exists(exclude_file_path): os.unlink(exclude_file_path) # 增加环境关联值 envs = request.POST.getlist('projectEnvConfig') if envs: for v in envs: # 存在更新 try: config = ProjectEnvConfig.objects.get(pk=v) if request.POST.get('branch_' + v): config.branch = request.POST.get('branch_' + v) else: config.branch = 'master' config.before_hook = request.POST.get( 'before_hook_' + v, '') config.after_hook = request.POST.get( 'after_hook_' + v, '') if request.POST.get('host_group_' + v): config.host_group = HostGroup.objects.get( pk=request.POST.get('host_group_' + v)) config.save() before_hook_path = os.path.join( settings.BASE_DIR, 'storage/hooks/before_hook_' + str(config.id)) if len(config.before_hook) > 0: with open(before_hook_path, 'w') as f: f.write(config.before_hook) else: if os.path.exists(before_hook_path): os.unlink(before_hook_path) after_hook_path = os.path.join( settings.BASE_DIR, 'storage/hooks/after_hook_' + str(config.id)) if len(config.after_hook) > 0: with open(after_hook_path, 'w') as f: f.write(config.after_hook) else: if os.path.exists(after_hook_path): os.unlink(after_hook_path) if os.path.exists(before_hook_path): os.chmod(before_hook_path, stat.S_IRWXU) command = 'dos2unix ' + before_hook_path CommandService(command) if os.path.exists(after_hook_path): os.chmod(after_hook_path, stat.S_IRWXU) command = 'dos2unix ' + after_hook_path CommandService(command) # 不存在插入 except: projectEnvConfig = ProjectEnvConfig() projectEnvConfig.project = project projectEnvConfig.env = Env.objects.get(pk=v) if request.POST.get('branch_' + v): projectEnvConfig.branch = request.POST.get( 'branch_' + v) else: projectEnvConfig.branch = 'master' if request.POST.get('host_group_' + v): projectEnvConfig.host_group = HostGroup.objects.get( pk=request.POST.get('host_group_' + v)) projectEnvConfig.before_hook = request.POST.get( 'before_hook_' + v, '') projectEnvConfig.after_hook = request.POST.get( 'after_hook_' + v, '') projectEnvConfig.save() before_hook_path = os.path.join( settings.BASE_DIR, 'storage/hooks/before_hook_' + str(projectEnvConfig.id)) with open(before_hook_path, 'w') as f: f.write(projectEnvConfig.before_hook) after_hook_path = os.path.join( settings.BASE_DIR, 'storage/hooks/after_hook_' + str(projectEnvConfig.id)) with open(after_hook_path, 'w') as f: f.write(projectEnvConfig.after_hook) if os.path.exists(before_hook_path): os.chmod(before_hook_path, stat.S_IRWXU) command = 'dos2unix ' + before_hook_path CommandService(command) if os.path.exists(after_hook_path): os.chmod(after_hook_path, stat.S_IRWXU) command = 'dos2unix ' + after_hook_path CommandService(command) messages.info(request, '修改成功') except: messages.error(request, '修改失败') finally: return redirect('admin:deploy.project') else: f = ProjectForm() settingService = SettingService() general_info = settingService.get_general_info() return render( request, 'admin/deploy/edit_project.html', { "form": f, "project": project, "projectEnvConfig": projectEnvConfig, "status_choices": Project.STATUS_CHOICES, "type_choices": Project.TYPE_CHOICES, "deploy_mode_choices": Project.DEPLOY_MODE_CHOICES, "envlist": Env.objects.all(), "host_group": HostGroup.objects.all(), "credentials": Credentials.objects.all(), "env_list_by_project": env_list_by_project, "general_info": general_info, })
def rollback(self): errno = 0 single_errno = 0 for host in self.all_host: self.myLoggingService.info('开始回滚主机, host:' + host) if self.task.scope == Task.SCOPE_BY_FILE: self.myLoggingService.info('准备从发布系统回滚指定文件') for item in self.task.files_list.splitlines(): self.myLoggingService.info('回滚文件:' + item) rollback_file_path = settings.ROLLBACK_PATH[0] + '/' + str( self.task.id) + '/' + item command = self.rsync_prefix + RSYNC_EXCLUDE_PARMS + rollback_file_path + ' ' + \ host + ':' + self.taskService.get_release_path() + '/' + item commandService = CommandService(command) if commandService.returncode > 0: self.myLoggingService.error('文件回滚失败。 路径:' + item) self.myLoggingService.error(command) if len(commandService.stdout_as_list) > 0: for line in commandService.stdout_as_list: self.myLoggingService.error(line) if len(commandService.stderr) > 0: for line in commandService.stderr_as_list: self.myLoggingService.error(line) errno += 1 single_errno += 1 else: self.myLoggingService.info('文件回滚成功。 路径:' + item) else: if self.deploy_mode == Project.DEPLOY_MODE_ALL: command = SSH_PREFIX + host + ' " rm -f ' + self.project.dest_path + ' && ln -s -f ' + self.taskService.get_rollback_path() \ + ' ' + self.project.dest_path + '"' elif self.deploy_mode == Project.DEPLOY_MODE_INCREMENT: command = SSH_PREFIX + host + " '" + self.rsync_prefix + RSYNC_EXCLUDE_PARMS + self.taskService.get_rollback_path() + \ '/ ' + self.taskService.get_release_path() + "/'" self.myLoggingService.info('command:' + command) commandService = CommandService(command) if commandService.returncode > 0: self.myLoggingService.error('回滚异常, host:' + host) if len(commandService.stdout_as_list) > 0: self.myLoggingService.error('脚本输出:') for line in commandService.stdout_as_list: self.myLoggingService.error(line) if len(commandService.stderr_as_list) > 0: self.myLoggingService.error('脚本错误:') for line in commandService.stderr_as_list: self.myLoggingService.error(line) errno += 1 single_errno += 1 try: taskHostRela = TaskHostRela.objects.get(host=host, task=self.task) except: taskHostRela = TaskHostRela(task=self.task) taskHostRela.task = self.task taskHostRela.host = host taskHostRela.status_rollback = TaskHostRela.STATUS_ROLLBACK_ERROR taskHostRela.save() continue else: status_rollback = TaskHostRela.STATUS_ROLLBACK_SUCCESS self.myLoggingService.info('回滚正常, host:' + host) if len(commandService.stdout_as_list) > 0: self.myLoggingService.info('脚本输出:') for line in commandService.stdout_as_list: self.myLoggingService.info(line) # after hook res_hook_after = self.taskService.exec_hook_after_release(host) if res_hook_after: status_rollback = TaskHostRela.STATUS_ROLLBACK_SUCCESS self.myLoggingService.info('回滚后调用钩子成功') elif res_hook_after == False: status_rollback = TaskHostRela.STATUS_ROLLBACK_ERROR self.myLoggingService.error('回滚后调用钩子失败') errno += 1 single_errno += 1 else: status_rollback = TaskHostRela.STATUS_ROLLBACK_SUCCESS self.myLoggingService.info('未检测到回滚后调用钩子') #标记主机状态 try: taskHostRela = TaskHostRela.objects.get(host=host, task=self.task) taskHostRela.status_rollback = status_rollback taskHostRela.save() except: pass if single_errno > 0: self.myLoggingService.error('主机回滚失败,' + host) else: self.myLoggingService.info('主机回滚成功,' + host) if errno > 0: self.task.status_rollback = Task.STATUS_ROLLBACK_FINISH_ERR self.myLoggingService.info('回滚结束,有异常情况') else: self.task.status_rollback = Task.STATUS_ROLLBACK_FINISH self.myLoggingService.info('回滚成功') self.task.save() send_notify(self.task.id, rollback=True)
def release(self): errno = 0 #检查所有文件是否在工作区存在 if self.task.scope == Task.SCOPE_BY_FILE: if self.check_file_exists() == False: self.task.status = Task.STATUS_RELEASE_FINISH_ERR self.myLoggingService.error('检查文件列表路径失败,发布退出') self.task.save() send_notify(self.task.id) return None # 从远程主机备份文件 if self.task.scope == Task.SCOPE_BY_FILE: self.myLoggingService.info('准备从远程主机备份指定文件') rollback_host = self.all_host[0] self.myLoggingService.info('备份主机地址:' + rollback_host) rollback_path = settings.ROLLBACK_PATH[0] + '/' + str(self.task.id) if os.path.exists(rollback_path) == False: os.mkdir(rollback_path) for item in self.task.files_list.splitlines(): file_path = rollback_path + '/' + item command = self.rsync_prefix + RSYNC_EXCLUDE_PARMS + ' ' + \ rollback_host + ':' + self.taskService.get_release_path() + '/' + item + \ ' ' + file_path commandService = CommandService(command) if commandService.returncode > 0: self.myLoggingService.error('文件备份失败, 新文件可忽略。 路径:' + item) self.myLoggingService.error(command) if len(commandService.stdout_as_list) > 0: for line in commandService.stdout_as_list: self.myLoggingService.error(line) if len(commandService.stderr) > 0: for line in commandService.stderr_as_list: self.myLoggingService.error(line) else: self.myLoggingService.info('文件备份成功。 路径:' + item) for host in self.all_host: single_errno = 0 self.myLoggingService.info('开始发布主机, host:' + host) if self.task.scope == Task.SCOPE_BY_FILE: for item in self.task.files_list.splitlines(): file_path = self.working_dir + '/' + item command = self.rsync_prefix + RSYNC_EXCLUDE_PARMS + file_path + ' ' + \ host + ':' + self.taskService.get_release_path() commandService = CommandService(command) if commandService.returncode > 0: self.myLoggingService.error('文件发布失败。 路径:' + item) self.myLoggingService.error(command) if len(commandService.stdout_as_list) > 0: for line in commandService.stdout_as_list: self.myLoggingService.error(line) if len(commandService.stderr) > 0: for line in commandService.stderr_as_list: self.myLoggingService.error(line) errno += 1 single_errno += 1 else: self.myLoggingService.info('文件发布成功。 路径:' + item) else: if self.deploy_mode == Project.DEPLOY_MODE_ALL: command = self.rsync_prefix + RSYNC_EXCLUDE_PARMS + self.working_dir + '/ ' + \ host + ':' + self.taskService.get_release_path() elif self.deploy_mode == Project.DEPLOY_MODE_INCREMENT: command = self.rsync_prefix + RSYNC_EXCLUDE_PARMS + self.working_dir + '/ ' + \ host + ':' + self.taskService.get_release_path() + ' --backup --backup-dir=' + self.taskService.get_rollback_path() self.myLoggingService.info('command:' + command) commandService = CommandService(command) self.myLoggingService.info('exit_code:' + str(commandService.returncode)) if commandService.returncode > 0: self.myLoggingService.error('同步异常, host:' + host) if len(commandService.stdout_as_list) > 0: self.myLoggingService.error('脚本输出:') for line in commandService.stdout_as_list: self.myLoggingService.error(line) if len(commandService.stderr) > 0: self.myLoggingService.error('脚本错误:') for line in commandService.stderr_as_list: self.myLoggingService.error(line) errno += 1 single_errno += 1 #标记主机状态 taskHostRela = TaskHostRela() taskHostRela.host = host taskHostRela.task = self.task taskHostRela.status_release = TaskHostRela.STATUS_RELEASE_ERROR taskHostRela.save() continue else: status_release = TaskHostRela.STATUS_RELEASE_SUCCESS self.myLoggingService.info('同步正常, host:' + host) if len(commandService.stdout_as_list) > 0: self.myLoggingService.info('脚本输出:') for line in commandService.stdout_as_list: self.myLoggingService.info(line) # add link if self.deploy_mode == Project.DEPLOY_MODE_ALL: self.myLoggingService.info('准备切换软链接...') command = SSH_PREFIX + host + ' " rm -f ' + self.project.dest_path + ' && ln -s -f ' + self.taskService.get_release_path() \ + ' ' + self.project.dest_path + '"' self.myLoggingService.info(command) commandService = CommandService(command) if len(commandService.stdout_as_list) > 0: self.myLoggingService.info('切换软链接出错') for line in commandService.stdout_as_list: self.myLoggingService.info(line) errno += 1 single_errno += 1 taskHostRela = TaskHostRela() taskHostRela.host = host taskHostRela.task = self.task taskHostRela.status_release = TaskHostRela.STATUS_RELEASE_ERROR taskHostRela.save() continue self.myLoggingService.info('切换软链接完成') # after hook res_hook_after = self.taskService.exec_hook_after_release(host) if res_hook_after: status_release = TaskHostRela.STATUS_RELEASE_SUCCESS self.myLoggingService.info('发布后调用钩子成功') elif res_hook_after == False: status_release = TaskHostRela.STATUS_RELEASE_ERROR self.myLoggingService.error('发布后调用钩子失败') errno += 1 single_errno += 1 else: status_release = TaskHostRela.STATUS_RELEASE_SUCCESS self.myLoggingService.info('未检测到发布后钩子') #标记主机状态 taskHostRela = TaskHostRela() taskHostRela.task = self.task taskHostRela.host = host taskHostRela.status_release = status_release taskHostRela.save() if single_errno > 0: self.myLoggingService.error('主机发布失败,' + host) else: self.myLoggingService.info('主机发布成功,' + host) if errno > 0: self.task.status = Task.STATUS_RELEASE_FINISH_ERR self.myLoggingService.info('发布失败,有异常情况') else: self.task.status = Task.STATUS_RELEASE_FINISH self.myLoggingService.info('发布成功') self.task.save() send_notify(self.task.id)