Python自動化部署
# -*- coding: utf-8 -*- #!/bin/env python ''' #Auth: karl #Function: released version #Date:2017/6/27 #Version:V1.0 ''' import sys,re,os,time,datetime import paramiko 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 = "[email protected]" 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 "[main] INFO org.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[1:]: 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 '%stErrorn' % (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="tar -xvf %s -C /home/appdeploy/version/ >/dev/null " % local_file if "Linux" == 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'] = '[email protected]' 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('[email protected]', '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("/")[-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' :"詳情請審閱附件,謝謝!" } 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 = "http://public-int-gw.int.sfdc.com.cn:1080/unp/notice/single" headers = {'content-type': 'application/json; charset=UTF-8'} try: response = requests.post(url, data=json_str, headers=headers) print response.text print "-------------" 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="/home/appdeploy/version/Version_3.0" else: filep="/tmp/backup/old" 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 "----------------------------copy files--- %s--------------------" % (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 "----------------------------copy remote files--- %s--------------------" % (host) print "-------->>>>>",host_ip[0],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[0], 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: [a] xxxxx [b] 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 [0]:') if not sec_index.isdigit() or int(sec_index) >= len(sections): print 'choose invalid!' continue return conf, sections[int(sec_index)] return conf, sections[0] 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 = ''' ERROR The key:{key} value is empty in conf file '''.format(key=key) __err_exit_show_msg(msg) except ConfigParser.NoOptionError: print traceback.format_exc() msg = ''' ERROR cannot 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 "Linux" == 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 = [cmd for cmd in cmd_f] 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 "----------------------------check result--- %s--------------------"%(ip) checkserver_cmd=[ 'cat {path}_ADMIN_0{num}/logs/novatar_{time}.0.log |grep "error|org.eclipse.jetty.server.Server.doStart(Server.java:379)|Failed startup"'.format(path=Filepath,time=suffix_time,num=num), 'cat {path}_TRAPP_0{num}/logs/novatar_{time}.0.log |grep "error|org.eclipse.jetty.server.Server.doStart(Server.java:379)|Failed startup"'.format(path=Filepath,time=suffix_time,num=num), 'cat {path}_TRTS_0{num}/logs/novatar_{time}.0.log |grep "error|org.eclipse.jetty.server.Server.doStart(Server.java:379)|Failed startup"'.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 "----------------------------restart server --- %s--------------------" % (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 == "STG": Filepath="/app/jetty/server/SCS_ATP_CNSZ99_JETTY_APP" Restartfile="/app/jetty/logs/SCS_ATP_CNSZ99_JETTY_APP" host_ip=[] elif section == "DEVTEST": Filepath="/app/jetty/server/SCS_ATP_CORE_CNSZ22_JETTY_APP" Restartfile="/app/jetty/logs/SCS_ATP_CORE_CNSZ22_JETTY_APP" host_ip=[] else : Filepath="/app/jetty/server/SCS_ATP_CORE_CNSZ17_JETTY_APP" Restartfile="/app/jetty/logs/SCS_ATP_CNSZ17_JETTY_APP" host_ip=[] # 檢測配置文件正確性 check_config(conf, section) print('