78144666 发表于 2018-8-8 09:17:16

Python自动化部署

  # -*- coding: utf-8 -*-
  #!/bin/env python
  '''
  #Auth: karl

  #Function:>  #Date:2017/6/27
  #Version:V1.0
  '''
  importsys,re,os,time,datetime
  importparamiko
  import logging
  import socket
  import ConfigParser
  import traceback
  from progressbar import *
  import Auto_Mysql_release
  import platform
  import smtplib
  import email.mime.multipart
  import email.mime.text
  import json
  import os
  import struct
  import requests
  receivers = "XXX@.com"
  receiver = "XXX@com"
  #reg 为0时正常执行命令,为1时开始检查服务启动是否正常,为3时不用再备份原文件
  def ssh2(host, port, username, password, cmds,reg=0):
  #链接远程服务器并执行命令p
  try:
  paramiko.util.log_to_file('./../log/exec_cmd_' + time_str + '.log')
  ssh = paramiko.SSHClient()
  ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  ssh.connect(host, int(port), username, password, timeout=5)
  # 执行命令文件中的命令
  if reg == 3:
  stdin, stdout, stderr = ssh.exec_command('ls /tmp/backup/old/ADMIN/integration.properties')
  if stdout.readline() != '':
  pass
  else:
  for cmd in cmds:
  logging.info('running: ' + cmd)
  print 'running: ' + cmd
  time.sleep(2)
  stdin, stdout, stderr = ssh.exec_command(cmd)
  for out_msg in stdout.readlines():
  print out_msg
  else:
  for cmd in cmds:
  print "=======>>",cmd
  logging.info('running: ' + cmd)
  print 'running: ' + cmd
  time.sleep(5)
  stdin, stdout, stderr = ssh.exec_command(cmd)
  if reg == 1:
  out_msg=str("".join(stdout.readlines()))
  print "000000------>>",out_msg
  if " INFOorg.eclipse.jetty.server.Server.doStart(Server.java:379)" in out_msg and "error" not in out_msg and "Failed startup" not in out_msg:
  print"THIS IS ok..."
  print "------->> %s"%(host)
  else:
  #回滚到当前版本
  #                        repair_cmd_file = conf.get(section, 'repair_cmd_file')
  #                        exec_file_cmd(conf, section, repair_cmd_file)
  copy_cmd_file(conf, section, host,1)
  for ip_f in host_ip:
  num_f = host_ip.index(ip_f)
  num_f += 1
  process_cmd_file(conf, section,ip_f, num_f)
  for ip_s in host_ip:
  num_s = host_ip.index(ip_s)
  num_s += 1
  print "+++++++ %s" % (ip_s)
  start_server(conf, section, ip_s, num_s)
  exit(1)
  else:
  print "++++++++++++++",reg
  for out_msg in stdout.readlines():
  print "----", out_msg
  for err_msg in stderr.readlines():
  print err_msg
  exit(1)
  ssh.close()
  print 'command execute successful!'
  logging.info('command execute successful!')
  except Exception, e:
  print '%s\tError\n' % (host)
  print traceback.format_exc()
  __err_exit_show_msg(str(e))
  def upload(conf, section):
  '''
  上传文件到远程服务器
  '''
  host = conf.get(section, 'host')
  port = conf.get(section, 'port')
  username = conf.get(section, 'username')
  password = conf.get(section, 'password')
  local_file = conf.get(section, 'local_file')
  remote_file = conf.get(section, 'remote_file')
  try:
  paramiko.util.log_to_file('../log/upload_' + time_str + '.log')
  logging.info('paramiko log created')
  t = paramiko.Transport((host, int(port)))
  t.connect(username=username, password=password)
  logging.info('connected host <' + host + '> successful')
  logging.info('Upload file SUCCESSFUL %s ' % datetime.datetime.now())
  sftp = paramiko.SFTPClient.from_transport(t)
  logging.info('Upload file SUCCESSFUL %s ' % datetime.datetime.now())
  print 'Beginning to upload file to %s%s ' % (host, datetime.datetime.now())
  # 定义上传进度条样式
  widgets = ['File: ', Percentage(), ' ',
  Bar(marker='#', left='[', right=']'),
  ' ', ETA(), ' ', FileTransferSpeed()]
  file_size = os.path.getsize(local_file)
  pbar = ProgressBar(widgets=widgets, maxval=file_size)
  # 开始进度条
  pbar.start()
  # 使用匿名方法接收上传返回值,并且显示进度条
  progress_bar = lambda transferred, toBeTransferred: pbar.update(transferred)
  sftp.put(local_file,remote_file, callback=progress_bar)
  pbar.finish()
  logging.info('Upload file SUCCESSFUL %s ' % datetime.datetime.now())
  print 'Upload file SUCCESSFUL %s ' % datetime.datetime.now()
  t.close()
  logging.info('sftp closed!')
  cmd=&quot;tar -xvf %s-C /home/appdeploy/version/ >/dev/null &quot; % local_file
  if &quot;Linux&quot; == platform.system():
  os.system(cmd)
  report_cmd_file = __checke_conf_key_value_empty(conf, section, 'test_report')
  except Exception, e:
  logging.error('host: <' + host + '> connect error!')
  print host, 'connect error!'
  print traceback.format_exc()
  __err_exit_show_msg(str(e))
  def email_send(section,version):
  evn=section
  V=version
  msg = email.mime.multipart.MIMEMultipart()
  msg['Subject'] = '版本发布通知邮件'
  msg['From'] = '18682147058@163.com'
  msg['To'] = ','.join(receivers)
  content = '''
  你好,各位同事:
  本次%s版本(%s)发布:   发布成功,祝贺!!!
  '''%(evn,V)
  txt = email.mime.text.MIMEText(content)
  msg.attach(txt)
  smtp = smtplib.SMTP()
  smtp.connect('smtp.163.com', '25')
  smtp.login('18682147058@163.com', 'passwd')
  smtp.sendmail(msg['From'], receivers, msg.as_string())
  smtp.quit()
  print('邮件发送成功email has send out !')
  def email_linux(receivers, subject=None, bodyhtml=None,attachments=None):
  '''
  对接统一通知平台,发邮件样例
  receivers 收件人邮箱
  subject 主题
  bodyhtml 邮件内容
  attachment即附件路径默认为空,如有附件传入文件路径'''
  file_name = attachments.split(&quot;/&quot;)[-1]
  lis = ''
  time_str =str(time.strftime('%Y-%m-%d',time.localtime(time.time())))
  # 把附件内容转换为字符列表
  if attachments != None:
  file_name = os.path.basename(attachments)
  file = open(attachments, 'rb')
  _content = file.read()
  lis = struct.unpack('%db' % len(_content),_content)
  # 对应渠道模板中`message`中参数
  templateJson = {
  'version': bodyhtml,
  'time'   :time_str,
  'question' :&quot;详情请审阅附件,谢谢!&quot;
  }
  data = {
  'userId': receivers, #收件人邮箱,支持多收件人,分号隔离;如下面的抄送人格式
  'ccId': receiver, #抄送人,如果没有可以屏蔽该语句
  'templateCode': 'version_release_code', #业务模板code
  'templateParam': templateJson, #如果对应渠道模板中没有类{{}}格式的参数,可以屏蔽该语句
  'subject': subject, #邮件主题,默认是渠道模板名称
  'attachmentName' : file_name, #邮件附件名称, 如果没有附件可以屏蔽该语句
  'attachmentArray': lis, #邮件附件内容,如果没有附件可以屏蔽该语句
  'msgType': 'txt', #消息类型,目前只支持txt
  'accessId': '52HX1CYE', #通知平台接入Id
  'accessToken': 'ebf1dd3140cf4f0abd79872d7d237c3d' #通知平台接入Token
  }
  json_str = json.dumps(data)
  url = &quot;http://public-int-gw.int.sfdc.com.cn:1080/unp/notice/single&quot;
  headers = {'content-type': 'application/json; '}
  try:
  response = requests.post(url, data=json_str, headers=headers)
  print response.text
  print &quot;-------------&quot;
  result_json = json.loads(response.text)
  print(result_json) #打印返回内容
  except Exception as e:
  print('调用统一通知平台接口失败:',str(e))
  def copy_cmd_file(conf, section, ip,reg):
  if reg == 0:
  filep=&quot;/home/appdeploy/version/Version_3.0&quot;
  else:
  filep=&quot;/tmp/backup/old&quot;
  host=ip
  port = conf.get(section, 'port')
  username = conf.get(section, 'username')
  password = conf.get(section, 'password')
  remote_file = conf.get(section, 'remote_file')
  print &quot;----------------------------copy files--- %s--------------------&quot; % (host)
  cmd_c = [
  '\cp -vr {files}/ADMIN/*.war{path}_ADMIN_01/deploy/webapps/'.format(files=filep, path=Filepath),
  '\cp -vr {files}/ADMIN/integration.properties {path}_ADMIN_01/deploy/resources/'.format(files=filep,path=Filepath),
  '\cp -vr {files}/TRAPP/*.war{path}_TRAPP_01/deploy/webapps/'.format(files=filep, path=Filepath),
  '\cp -vr {files}/TRAPP/integration.properties{path}_TRAPP_01/deploy/resources/'.format(files=filep,path=Filepath),
  '\cp -vr {files}/TRTS/*.war{path}_TRTS_01/deploy/webapps/'.format(files=filep, path=Filepath),
  '\cp -vr {files}/TRTS/integration.properties{path}_TRTS_01/deploy/resources/'.format(files=filep, path=Filepath),
  ]
  ssh2(host, port, username, password, cmd_c)
  #针对脚本进行参数化设置
  def process_cmd_file(conf, section,ip_f, num_f):
  host = ip_f
  port = conf.get(section, 'port')
  username = conf.get(section, 'username')
  password = conf.get(section, 'password')
  remote_file = conf.get(section, 'remote_file')
  print &quot;----------------------------copy remote files--- %s--------------------&quot; % (host)
  print &quot;-------->>>>>&quot;,host_ip,port,username,password,remote_file
  cmd=[
  'scp {path}_ADMIN_01/deploy/webapps/*.war{user}@{ip}:{path}_ADMIN_0{num}/deploy/webapps/'.format(path=Filepath,user=username,ip=host,num=num_f),
  #       'scp {path}_ADMIN_01/deploy/resources/integration.properties{user}@{ip}:{path}_ADMIN_0{num}/deploy/resources/integration.properties'.format(path=Filepath,user=username,ip=host,num=num_f),
  'scp {path}_TRTS_01/deploy/webapps/*.war{user}@{ip}:{path}_TRTS_0{num}/deploy/webapps/'.format(path=Filepath,user=username,ip=host,num=num_f),
  #       'scp {path}_TRTS_01/deploy/resources/integration.properties{user}@{ip}:{path}_TRTS_0{num}/deploy/resources/integration.properties'.format(path=Filepath,user=username,ip=host,num=num_f),
  'scp {path}_TRAPP_01/deploy/webapps/*.war{user}@{ip}:{path}_TRAPP_0{num}/deploy/webapps/'.format(path=Filepath,user=username,ip=host,num=num_f),
  #      'scp {path}_TRAPP_01/deploy/resources/integration.properties{user}@{ip}:{path}_TRAPP_0{num}/deploy/resources/integration.properties'.format(path=Filepath,user=username,ip=host,num=num_f)
  ]
  ssh2(host_ip, port, username, password, cmd)
  def exec_file_cmd(conf, section, cmd_file):
  '''
  执行文件中的命令
  '''
  host = conf.get(section, 'host')
  port = conf.get(section, 'port')
  username = conf.get(section, 'username')
  password = conf.get(section, 'password')
  cmds = __get_cmds(cmd_file)
  ssh2(host, port, username, password, cmds)
  def backup_ori(conf, section):
  '''
  备份远程原文件
  '''
  host = conf.get(section, 'host')
  port = conf.get(section, 'port')
  username = conf.get(section, 'username')
  password = conf.get(section, 'password')
  remote_file = conf.get(section, 'remote_file')
  remote_ori_backup_dir = conf.get(section, 'remote_ori_backup_dir')
  # 获得备份后缀
  suffix_time =time.strftime('%Y-%m-%d',time.localtime(time.time()))
  backup_ori_cmd = [
  'mkdir -p {dir}/ADMIN {dir}/TRTS {dir}/TRAPP'.format(dir=remote_ori_backup_dir),
  'cp -vr {path}_TRAPP_01/deploy/webapps/*.war{dir}/TRAPP'.format(path=Filepath,dir=remote_ori_backup_dir),
  'cp -vr {path}_ADMIN_01/deploy/webapps/*.war{dir}/ADMIN'.format(path=Filepath,dir=remote_ori_backup_dir),
  'cp -vr {path}_TRTS_01/deploy/webapps/*.war{dir}/TRTS'.format(path=Filepath,dir=remote_ori_backup_dir),
  'cp -vr {path}_TRAPP_01/deploy/resources/integration.properties{dir}/TRAPP'.format(path=Filepath,dir=remote_ori_backup_dir),
  'cp -vr {path}_ADMIN_01/deploy/resources/integration.properties{dir}/ADMIN'.format(path=Filepath,dir=remote_ori_backup_dir),
  'cp -vr {path}_TRTS_01/deploy/resources/integration.properties{dir}/TRTS'.format(path=Filepath,dir=remote_ori_backup_dir)
  ]
  ssh2(host, port, username, password, backup_ori_cmd,3)
  def backup_new(conf, section):
  '''
  备份远程新上传的文件
  '''
  host = conf.get(section, 'host')
  port = conf.get(section, 'port')
  username = conf.get(section, 'username')
  password = conf.get(section, 'password')
  remote_file = conf.get(section, 'remote_file')
  remote_backup_dir = conf.get(section, 'remote_backup_dir')
  # 获得备份后缀
  suffix_time = time.strftime('%Y-%m-%d_%H-%M-%S',time.localtime(time.time()))
  backup_new_cmd = [
  'mkdir -p {dir}'.format(dir=remote_backup_dir),
  'cp -vr {new_file} {dir}/{new_bak_file}_{time}'.format(new_file=remote_file,
  dir=remote_backup_dir,
  new_bak_file=os.path.basename(remote_file),
  time=str(suffix_time))
  ]
  ssh2(host, port, username, password, backup_new_cmd)
  def select_section(conf_file_name):
  '''
  选择指定读取的配置文件项
  例如:*.conf配置文件中有多个配置项 a 和 b:
  
  xxxxx
  
  yyyyy
  '''
  # 检测指定的配置文件是否存在
  __check_file_exists(conf_file_name)
  # 读取配置文件
  conf = ConfigParser.ConfigParser()
  conf.read(conf_file_name)
  sections = conf.sections()
  # 选择配置文件选项界面
  print 'please choose confit item:'
  for index, value in enumerate(sections):
  print'', index, ':', value
  while True:
  sec_index = raw_input('please choose one item default :')
  if not sec_index.isdigit() or int(sec_index) >= len(sections):
  print 'choose invalid!'
  continue
  return conf, sections
  return conf, sections
  def check_config(conf, section):
  '''
  检测配置文件的正确性
  '''
  logging.info('check config starting...')
  print 'check config starting...'
  # 检测配置文件中值是否都填写
  host = __checke_conf_key_value_empty(conf, section, 'host')# 检测配置文件中主机名
  port = __checke_conf_key_value_empty(conf, section, 'port')# 检测配置文件中端口
  username = __checke_conf_key_value_empty(conf, section, 'username')# 检测配置文件用户名
  password = __checke_conf_key_value_empty(conf, section, 'password')# 检测配置文件密码
  local_file = __checke_conf_key_value_empty(conf, section, 'local_file')# 检测配置文件本地需要上传文件
  remote_file = __checke_conf_key_value_empty(conf, section, 'remote_file')# 检测配置文件上传到远程的文件
  remote_backup_dir = __checke_conf_key_value_empty(conf, section, 'remote_backup_dir')# 检测配置文件远程备份目录
  remote_ori_backup_dir = __checke_conf_key_value_empty(conf, section, 'remote_ori_backup_dir')# 检测配置文件远程临时备份目录
  start_cmd_file = __checke_conf_key_value_empty(conf, section, 'start_cmd_file')# 检测配置文件启动服务文件
  report_cmd_file = __checke_conf_key_value_empty(conf, section, 'test_report')# 检测配置文件停止服务文件
  # 检测配置文件中的网络是否可用
  __check_network_ping(host)
  # 检测ssh链接是否成功
  __check_ssh(host, int(port), username, password)
  # 检测本地需要上传的文件是否存在
  __check_file_exists(local_file)
  # 检测命令文件是否存在
  __check_file_exists(start_cmd_file)
  print 'check config successful!!'
  logging.info('check config successful!!')
  def __valid_ip(address):
  '''
  检测IP是否合法IP
  '''
  try:
  socket.inet_aton(address)
  return True
  except:
  print traceback.format_exc()
  return False
  def __check_file_exists(conf_file_name):
  '''
  检测指定的配置文件是否存在
  '''
  if not os.path.exists(conf_file_name):
  logging.error('can not find config file: ' + conf_file_name)
  __err_exit_show_msg('can not find config file: ' + conf_file_name)
  return conf_file_name
  def __checke_conf_key_value_empty(conf, section, key):
  '''
  检测配置文件的key是否存在
  '''
  try:
  value = conf.get(section, key)
  # 检测配置文件中的值是否为空
  if value:
  return value
  else:
  msg = '''
  ERRORThe key:{key} value is empty in conf file
  '''.format(key=key)
  __err_exit_show_msg(msg)
  except ConfigParser.NoOptionError:
  print traceback.format_exc()
  msg = '''
  ERRORcannot find key:{key} in conf file
  '''.format(key=key)
  __err_exit_show_msg(msg)
  def __check_network_ping(host):
  if not __valid_ip(host):
  __err_exit_show_msg('host: ' + host + ' invalid')
  if &quot;Linux&quot; == platform.system():
  if 0 <> os.system('ping -c 3 ' + host):
  __err_exit_show_msg('host: ' + host + ' cannot ping...')
  else:
  if 0 <> os.system('ping -n 1 -w 5 ' + host):
  __err_exit_show_msg('host: ' + host + ' cannot ping...')
  def __check_ssh(host, port, username, password):
  try:
  ssh = paramiko.SSHClient()
  ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  ssh.connect(host, port, username, password, timeout=5)
  ssh.close()
  except Exception as e:
  print traceback.format_exc()
  msg = '''
  SSH connect failure.
  please check your host/port/username/password
  host    :{host}
  port    :{port}
  username:{username}
  password:{password}
  '''.format(host=host, port=port,
  username=username,
  password=password)
  __err_exit_show_msg(msg)
  def __get_cmds(cmd_file):
  '''
  文件中获取执行命令
  '''
  with open(cmd_file, 'r') as cmd_f:
  pattern = re.compile('(^\s*#|^\s*$)')
  func = lambda x: x if not re.match(pattern, x) else None
  cmds =
  return filter(func, cmds)
  def check_server(conf, section,ip,num):
  host = ip
  port = conf.get(section, 'port')
  username = conf.get(section, 'username')
  password = conf.get(section, 'password')
  suffix_time = time.strftime('%Y%m%d', time.localtime(time.time()))
  print &quot;----------------------------check result--- %s--------------------&quot;%(ip)
  checkserver_cmd=[
  'cat {path}_ADMIN_0{num}/logs/novatar_{time}.0.log |grep&quot;error\|org.eclipse.jetty.server.Server.doStart(Server.java:379)\|Failed startup&quot;'.format(path=Filepath,time=suffix_time,num=num),
  'cat {path}_TRAPP_0{num}/logs/novatar_{time}.0.log |grep&quot;error\|org.eclipse.jetty.server.Server.doStart(Server.java:379)\|Failed startup&quot;'.format(path=Filepath,time=suffix_time,num=num),
  'cat {path}_TRTS_0{num}/logs/novatar_{time}.0.log |grep&quot;error\|org.eclipse.jetty.server.Server.doStart(Server.java:379)\|Failed startup&quot;'.format(path=Filepath,time=suffix_time,num=num)
  ]
  ssh2(host, port, username, password, checkserver_cmd,1)
  def start_server(conf, section, ip, num):
  host = ip
  port = conf.get(section, 'port')
  username = conf.get(section, 'username')
  password = conf.get(section, 'password')
  print &quot;----------------------------restart server --- %s--------------------&quot; % (ip)
  checkserver_cmd = [
  'sh {path}_ADMIN_0{num}_run.sh stop; sh {path}_ADMIN_0{num}_run.sh start'.format(path=Restartfile,num=num),
  'sh {path}_TRAPP_0{num}_run.sh stop; sh {path}_TRAPP_0{num}_run.sh start'.format(path=Restartfile,num=num),
  'sh {path}_TRTS_0{num}_run.sh stop; sh {path}_TRTS_0{num}_run.sh start'.format(path=Restartfile,num=num)
  ]
  ssh2(host, port, username, password, checkserver_cmd)
  def __err_exit_show_msg(msg):
  '''
  发生错误的时候显示相关错误信息并且退出程序
  '''
  print 'ERROR:' + msg
  logging.error('ERROR:' + msg)
  os.system('pause')
  sys.exit()
  if __name__ == '__main__':
  try:
  start = time.clock()
  # 设置日志文件
  time_str = time.strftime('%Y-%m-%d',time.localtime(time.time()))
  log_file = '../log/upload_distribution_' + str(time_str) + '.log'
  logging.basicConfig(level=logging.INFO,
  format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
  filename=log_file,
  filemode='w',
  datefmt='%Y-%m-%d %X')
  # 定义配置文件路径
  conf_file_name = '../conf/release.conf'
  # 选择配置文件section
  conf, section = select_section(conf_file_name)
  if section == &quot;STG&quot;:
  Filepath=&quot;/app/jetty/server/SCS_ATP_CNSZ99_JETTY_APP&quot;
  Restartfile=&quot;/app/jetty/logs/SCS_ATP_CNSZ99_JETTY_APP&quot;
  host_ip=[]
  elif section == &quot;DEVTEST&quot;:
  Filepath=&quot;/app/jetty/server/SCS_ATP_CORE_CNSZ22_JETTY_APP&quot;
  Restartfile=&quot;/app/jetty/logs/SCS_ATP_CORE_CNSZ22_JETTY_APP&quot;
  host_ip=[]
  else :
  Filepath=&quot;/app/jetty/server/SCS_ATP_CORE_CNSZ17_JETTY_APP&quot;
  Restartfile=&quot;/app/jetty/logs/SCS_ATP_CNSZ17_JETTY_APP&quot;
  host_ip=[]
  # 检测配置文件正确性
  check_config(conf, section)
  print('\033[1;35m You can view the configuration file in the version number:\&quot;/home/appdeploy/version/Version_\033[1;32m3.0\033[1;35m.tar&quot; \033[0m!')
  VERSION=raw_input(&quot;Please Enter Version: eg 3.0 \n&quot;)
  # 备份原文件
  backup_ori(conf, section)
  # 上传文件
  upload(conf, section)
  # 备份新上传的文件
  backup_new(conf, section)
  # 解压文件
  start_cmd_file = conf.get(section, 'start_cmd_file')
  exec_file_cmd(conf, section, start_cmd_file)
  # 上传新文件到应用目录
  copy_cmd_file(conf, section, host_ip,0)
  # 拷贝文件到远程服务器
  if len(host_ip) > 1:
  for ip_f in host_ip:
  num_f = host_ip.index(ip_f)
  num_f += 1
  process_cmd_file(conf, section,ip_f, num_f)
  # 执行拷贝excel 等文件,记录到数据库中
  agre=[&quot;3306&quot;, &quot;XXX&quot;, &quot;root&quot;,host_ip,VERSION]
  print &quot;agre :&quot;,agre
  stg=Auto_Mysql_release.Mysql_connect(*agre)
  stg.File_get()
  stg.File_deal()
  #启动所有服务器的服务
  for ip_s in host_ip:
  num_s = host_ip.index(ip_s)
  num_s += 1
  start_server(conf, section,ip_s,num_s)
  # 监听服务是否启动成功
  time.sleep(90)
  for ip in host_ip:
  num=host_ip.index(ip)
  num+=1
  check_server(conf, section,ip,num)
  # 实行完毕退出
  cmd_f = [&quot;rm -fr /tmp/backup/old/*&quot;]
  ssh2(host_ip,&quot;22&quot;,&quot;mwopr&quot;,&quot;XXX&quot;,cmd_f)
  os.system('exit')
  end = time.clock()

  print &quot;version>  # 邮件群发到相关责任人
  if section == &quot;ONLINE&quot;:
  testReport = conf.get(section, 'test_report')
  text=&quot;%s版本发布通知邮件&quot;% (VERSION)
  email_linux(receivers,&quot;版本发布通知邮件&quot;,VERSION,testReport)
  elif section == &quot;DEVTEST&quot;:
  email_send(section,VERSION)
  else:
  pass
  exit(0)
  except Exception as e:
  print traceback.format_exc()
  __err_exit_show_msg(str(e))
  具体流程如下:
页: [1]
查看完整版本: Python自动化部署