需求:有多個應用系統部署在不同的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帳號啟動對應的程序程。