作为软件测试人员,经常要发布开发的集成包。本来嘛,更新个服务,没什么大不了。但是集成包打出来,要更新的服务有9个(分别部署在三台服务器上),并且以后还会增加。如果手动更新,每次更新时间都得20分钟以上,长此以往,非常浪费时间。
更新流程背景
1、从一台可用FTP连接的服务器上,下载集成人员集成好的最新包。
2、解压下载的集成包,解压后,生成9个程序包,每个程序包是以.tar.gz
格式压缩。
3、将9个包,分别上传至对应服务器(3台linux服务器)
4、关闭服务器上的服务,并备份未更新前的服务程序
5、解压上传的.tar.gz
文件至指定目录
6、替换加压后文件中的一个conf
文件夹中的配置
7、启动服务。
脚本编写
从FTP服务器下载文件
python 内置ftplib库,可以方便的通过FTP连接服务器
FTP连接
def ftp_connect(host, port, username, password, encoding="UTF-8"):
"""Connect server via FTP.
If connect sucessfully, return ftp object, else return False!"""
try:
ftp = FTP()
ftp.set_debuglevel(0) #打开调试级别2,显示详细信息;0为关闭调试信息
ftp.set_pasv(0)#0主动模式 1 #被动模式
ftp.encoding = encoding
ftp.connect(host, port)
ftp.login(username, password) #登录,如果匿名登录则用空串代替即可
return ftp
except Exception as e:
print("Connect FTP Error!")
print("Error:", str(e))
return False
定义一个函数,传参分别是服务器IP,端口,用户名,密码,编码。
FTP文件下载
def ftp_download():
"""Download file via ftp connection!
Get the latest file from the dir file_path["DB/Middle"][0],
Save the file to the dir file_path["DB/Middle"][1]."""
file_path = {"DB":["/省略.../DB", "D:/FTP/DB/"], "Middle":["/省略.../Middle", "D:/FTP/Middle/"]}
ftp = ftp_connect()
if ftp:
for each in file_path:
ftp.cwd("/") #改变路径
try:
ftp.cwd(file_path[each][0]) #改变路径
except:
print("There is no such [%s] directory!" % file_path[each][0])
continue
#ftp.dir()
#print ftp.getwelcome()#显示ftp服务器欢迎信息
#ftp.cwd('xxx/xxx/') #选择操作目录
del_dir(file_path[each][1], isfile=True) #删除当前目录的解压后的文件夹
bufsize = 1024
file_name = ftp.nlst()[-1] #取当前路径的文件名列表(按时间顺序排序)
try:
file_handler = open(file_path[each][1] + file_name,'wb').write #以写模式在本地打开文件
except:
print("There is no such [%s] directory!" % file_path[each][1])
continue
try:
print("[%s] will be downloaded!" % file_name)
ftp.retrbinary('RETR %s' % os.path.basename(file_name),file_handler,bufsize)#接收服务器上文件并写入本地文件
#ftp.set_debuglevel(0)
#file_handler.close()
print("[%s] downloads completed!" % file_name)
except Exception as e:
print("Download file[%s] Error!" % file_name)
print("Error:", str(e))
ftp.quit()
介绍一下file_path变量:
file_path = {"DB":["/省略.../DB", "D:/FTP/DB/"], "Middle":["/省略.../Middle", "D:/FTP/Middle/"]}
file_path["DB"][0]是FTP服务器上下载的需要执行的sql文件
file_path["DB"][1]是FTP服务器上下载的需要执行的sql文件存放于本地磁盘的路径
file_path["Middle"][0]是FTP服务器上下载的中台程序包(解压后会产生9个.tar.gz包)
file_path["Middle"][1]是FTP服务器上下载的中台程序包存放于本地磁盘的路径
del_dir()是自定义的函数,主要作用是删除当前目录下的所有文件或者文件夹。具体实现如下:
def del_dir(path, isdir=True, isfile=False):
"""Delete files or directories!
The files or directories whether would be deleted pointing to isdir/isfile is true or false.
If true, then will be deleted"""
os.chdir(path)
for each in os.listdir():
if os.path.isdir(each):
if isdir:
shutil.rmtree(each)
print("The dir[%s] was removed!" % each)
else:
if isfile:
os.remove(each)
print("The file[%s] was removed!" % each)
此处还用到解压.rar文件,需要安装rarfile库。
解压方法如下:
def un_rar(path):
"""Extract rar file!
Argue path is the dir, file is the one which will be extract!"""
try:
os.chdir(path)
for each in os.listdir():
if os.path.isfile(each):#当前目录只有一个文件
file = each
rar = rarfile.RarFile(file)
os.mkdir(file[:-4]) #创建rar文件解压后的存放路径
rar.extractall(file[:-4]) #解压文件到指定路径
print("Un_rar file[%s] Successfully!" % file)
except Exception as e:
print("Un_rar file[%s] failed!" % file)
print("Error:", str(e))
finally:
rar.close()
SSH连接
python支持ssh连接的库还是比较多的,此处选择paramiko库(关于windows安装paramiko库,我在上一篇文章中做了记录,可供大家参考)
def ssh_connect(host="192.168.226.131", port=22, username="root", password="chenkang0818", encoding="UTF-8"):
"""Connect to server via SSH.
If connect successfully return ssh object, else return False."""
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) #这行代码的作用是允许连接不在know_hosts文件中的主机。
ssh.connect(host, port=port, username=username, password=password)
return ssh
except Exception as e:
print("SSH Connect Error!")
print("Error:", e)
return False
SSH连接linux服务器上传文件
连接服务器成功后,接下来就是上传文件。使用sftp.put(file_1,file_2)
命令,如果服务器存在file_2,那么该文件会被替换,如果没有则直接拷贝至服务器。
def ssh_upload():
"""A ssh upload fuction!"""
for ip in SERVER_INFO:
ssh = ssh_connect(host=ip, port=SERVER_INFO[ip]["port"], username=SERVER_INFO[ip]["username"], password=SERVER_INFO[ip]["password"]) #以ssh形式连接服务器
if not ssh: #如果连接不成功,则跳过
print("Connect server[%s] failed!" % ip)
continue
call_command(ssh, "cd middle; ./middle_stop") #连接上服务器后,执行关闭服务脚本
print("Preparing for starting...............")
sftp = ssh.open_sftp() #打开一个sftp形式连接服务器
os.chdir(SERVER_INFO[ip]["get_path"])
try:
if len((lambda count=0:[ count+1 for each in os.listdir() if os.path.isdir(each)])()) == 1: #防止middle文件夹中含有多个解压文件夹,确保上传服务器的文件是最新的
for each in os.listdir():
if os.path.isdir(each):
local_get_path = os.path.join(os.getcwd(), each) #合并路径
print("Uploading file on the server [%s]" % (ip))
for file in SERVER_INFO[ip]["get_file"]:
local_get_file = os.path.join(local_get_path, file) #合并路径
sftp.put(local_get_file, "/home/hundsun/middle/" + file) #将本地文件上传至服务器
print("Up file[%s] successfully!" % file)
call_command(ssh, "cd middle; ./middle_start") #当前服务器所有tar文件替换完毕后,执行启动脚本
except Exception as e:
print("Upload file[%s] failed!" % file)
print("Error:", str(e))
finally:
sftp.close()
ssh.close()
说明:
SERVER_INFO是定义的一个字典,其格式为:
SERVER_INFO = {"xx.xx.xx.xx":{"port":22, "username":"root", "password":"xxxxxx", "get_path":"D:/FTP/Middle/",
"get_file":["xx1.tar.gz",
"xx2.tar.gz",
"xx3.tar.gz"],
"save_path":"/home/省略.../"}}
其中get_path
是指:要上传服务器的程序包(.tar.gz文件)的本地路径get_file
的是指:需要上传至服务器更新的程序包
save_path
是指:上传至linux服务器的路径
os.path.join(get_path, get_file)是要上传的程序包的整个路径
call_command()是自定义函数,主要是运行服务器上启动,停止服务的脚本。定义如下:
def call_command(ssh, cmd):
stdin,stdout,stderr = ssh.exec_command(cmd)
print("-------------------return result--------------------")
for each in stdout.readlines():
print(each)
print("----------------------Error-------------------------")
for each in stderr.readlines():
print(each)
这里着重说一下paramiko的exec_command(cmd)方法。
开始的时候我产生一个误区,以为:先执行ssh.exec_command(“cd /home/abc/efg”),shh就会定位在/home/abc/efg,其实不是这样的。
通过paramiko建立ssh连接,会默认定位于/home/当前用户
的路径。
如果你想先cd /home/abc/efg
,而后执行/home/abc/efg
路径下的脚本./stop.sh
则应该这样:ssh.exec_command(“cd /home/abc/efg; ./stop.sh”)
运行程序
以下是运行程序方法:
spend_time()(ftp_down)
un_rar("D:/FTP/Middle/")
un_rar("D:/FTP/DB/")
spend_time()(ssh_upload)
为了查看各个过程花费的时长,做了个时间统计。前面说手动操作是20分钟以上,那么当然想看看程序能为我们节省下多少时间。
spend_time()函数定义如下:
def spend_time():
"""Calculate calling a fuctionthe how longs it will spend. """
time1 = time.time()
def wrapper(func):
func()
time2 = time.time()
return print(time2-time1)
return wrapper
涉及库
以下是支撑该脚本运行的库
from ftplib import FTP
import os, time
import paramiko
import rarfile
import shutil
import pymysql
windows安装paramiko库我上篇文章提过。
windows安装rarfile安装可用pip命令。安装完毕之后,找到windows下rar安装路径,将Unrar.exe放在python的环境变量中即可。
以下是windows 64位Unrar.exe程序包。
链接:http://pan.baidu.com/s/1hsJmZbe 密码:ee6h