django项目实现应用系统的自动更新
需求:有多个应用系统部署在不同的WINDOWS机器上,应用通过IIS对外发布,并且同一个应用都有在多台机器上面实现负载均衡,每次应用发布更新手工处理不仅效率低,还存在一定的误操作的风险,为提高工作效率,使用DJANGO发布的站点对各个应用实现自动更新
1、应用系统信息的表结构
models.py
class systeminfo(models.Model):
id = models.IntegerField(primary_key=True)
dnsname = models.CharField(max_length=50)
systemurl = models.CharField(max_length=100)
#用于保存应用系统的域名和应用的UNC路径(相对于部署DJANGO服务器的UNC),两个键最好联合唯一,避免重复更新
后台数据库为SQLITE3,SETTINGS.PY的部份配置如下:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
更新数据库
python manage.py makemigrations
python manage.py migrate
2、为了实现系统的自动更新,必须先录入应用系统的基础的UNC信息到数据库
urls.py加入:
url(r'^addsysteminfo/$',views.addsysteminfo,name='addsysteminfo'),
views.py写入addsysteminfo
def addsysteminfo(request):
if request.method=="POST":
dnss=request.POST.get("dnsnames")
urls=request.POST.get("systemurls")
try:
addlist=models.systeminfo(dnsname=dnss,systemurl=urls)
addlist.save()
return render(request,"addsysteminfo.html",{"login_err":"okay"})
except Exception:
return render(request,"addsysteminfo.html",{"login_err":"fail"})
else:
return render(request,"addsysteminfo.html",{"login_err":"noset"})
前端的addsysteminfo.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form id="loginForm" action="{% url 'addsysteminfo' %}" method="POST"> {% csrf_token %}
系统域名<input type="text" class="form-control" name="dnsnames" placeholder="系统域名"></br>
系统URL<input type="text" class="form-control" name="systemurls" placeholder="系统URL"></br>
<button class="btn btn-success btn-block" type="submit">
<b>提交</b>
</button>
<h4 ><b>{{ login_err }}</b></h4>
</form>
</body>
</html>
录入系统信息的界面如下:
应用系统做有负载均衡,多个UNC则录入多条记录
3、实现应用系统自动更新的功能
urls.py添加
url(r'^uploads/$',views.uploads,name='uploads'),
views.py写入该方法,目前只接受上传.ZIP的更新包
def uploads(request):
if request.method == "POST":
dnss=request.POST.get("dns")
request.encoding="utf-8"
myFile =request.FILES.get("myfile", None)
if not myFile:
# returnHttpResponse("no files for upload!")
return render(request,"uploads.html",{"login_err":"no files"})
destination = open(os.path.join(".\uploads",myFile.name),'wb+')
for chunk in myFile.chunks():
destination.write(chunk)
destination.close()
filepath=".\\uploads\\"+myFile.name
try:
sourcefiles=dbs2.openfiles(filepath,dnss)
except Exception:
return HttpResponse("UPFILE IS OK!,BUT CAN'T UPDATE"+filepath+dnss)
try:
#infolist=models.systeminfo.objects.get(dnsname=dnss)
infolists=models.systeminfo.objects.all().values('systemurl').filter(dnsname=dnss)
if not len(infolists):
return HttpResponse("not exists the system "+dnss)
for dicts in infolists:
targetfile=dicts['systemurl']
try:
dbs2.copyfiles(str(sourcefiles),str(targetfile))
except Exception:
return HttpResponse("copy files fail")
except Exception:
return HttpResponse("not exists the system "+dnss)
return HttpResponse("OKAY!")
# return render(request,"uploads.html",{"login_err":"okay"})
else:
return render(request,"uploads.html",{"login_err":"noset"})
调用了dbs2.py的两个方法:
openfiles方法解压文件到指定的目录,并返回路径,相当于更新当中源文件夹的路径
def openfiles(files,dnss):
nowtime=time.strftime('%Y-%m-%d-%H-%M-%S',time.localtime(time.time()))+""
z=zipfile.ZipFile(files,'r')
#dnss=dnss+str(nowtime)
dnss=str(dnss)+str(nowtime)
paths="d:\\uploads\\"+dnss
z.extractall(path=paths)
#z.extractall(path='.\\uploads\\'+dnss)
#return dnss
return paths
copyfiles方法:更新应用系统,更新前先备份被更新的文件到指定的目录
def copyfiles(sourceDir, targetDir):
if sourceDir.find(".svn") > 0:
return
for file in os.listdir(sourceDir):
sourceFile = os.path.join(sourceDir, file)
targetFile = os.path.join(targetDir, file)
if os.path.isfile(sourceFile):
if os.path.exists(targetDir):
#if os.path.exists(targetFile) and (os.path.getsize(targetFile) != os.path.getsize(sourceFile)):
if os.path.exists(targetFile): #如果更新是替换现有的旧文件,则准备备份,并建立好对应的目录结构,以备还原
#nowtime=time.strftime('%Y-%m-%d-%H-%M-%S',time.localtime(time.time()))+""
nowtime=time.strftime('%Y-%m-%d-%H-%M',time.localtime(time.time()))+""
#时间精确到分钟,一次更新如果在几分钟内完成,可能备份文件在几个文件夹内
#bak=sourceDir+"bak\\bak"
p1=os.path.dirname(sourceFile)
p1=p1[11:] #因为文件解压在D:\uploads\下,此处减掉前面D:\uploads\路径的11个字符
bak="D:\\appbak\\"+nowtime+"\\"+p1
if not os.path.exists(bak):
os.makedirs(bak)
bakfile=os.path.join(bak,file)
# print bakfile
open(bakfile,"wb").write(open(targetFile,"rb").read()) #先备份文件
open(targetFile, "wb").write(open(sourceFile, "rb").read())
else:
open(targetFile, "wb").write(open(sourceFile, "rb").read())
if not os.path.exists(targetDir):
os.makedirs(targetDir)
if not os.path.exists(targetFile):
open(targetFile, "wb").write(open(sourceFile, "rb").read())
if os.path.isdir(sourceFile):
First_Directory = False
copyfiles(sourceFile, targetFile)
前端的uploads.html
<!DOCTYPE html>
<html lang="UTF-8">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form enctype="multipart/form-data" action="/uploads/" method="post">
{% csrf_token %}
<input type="file" name="myfile" />请上传.ZIP格式的更新包,目录结构为双击点开.ZIP即为应用的顶级目录
<br/>
<input type="submit" value="更新应用" />
</br>
应用域名<input type="text" class="form-control" name="dns" placeholder="应用域名">
</br>
<h4 ><b>{{ login_err }}</b></h4><br/><br/>
ZIP范例</br>
<img src="http://www.test.com/zip.jpg"/>
</form>
</body>
</html>
此时用户只需要上传更新包,并录入应用的域名,即可完成应用系统的更新
注意:如果目标应用服务器即拷贝文件的目的路径,如果为LINUX系统,应用服务器可使用SAMBA服务发布共享,比如操作系帐号root,对应新建samba帐号,smbpasswd -a roo,密码可以设置为与操作一致。
另外如果部署DJANGO的服务器为WINDOWS服务器,可在WINDOWS服务器上面建立同名同密码的root帐号,再用root帐号启动对应的程序程。