上一年,由于公司有一大批项目需要部署到服务器上,每天都要将不同的项目部署到不同客户服务器上。这只是一直在重复劳动力和做服务器的搬运工,但是自己又不甘心只做搬运工,况且自己职位也不属于运维的,所以孕育了这个自动化服务器部署服务器。说到自动化,无疑是想到用python,对的,下面将要介绍的是整个软件的一些架构和遇到的一些过程。哦!忘记说了。python是结合宝塔和git去进行自动化部署的。git管理项目,宝塔是更加容易维护服务器和代码的一个管理工具,使用python去写这个自动化部署,可以减少以后的大量重复劳动力,解放双手。
再啰嗦一下,下面的需求和程序设计都是自己去实现的。一个人边学python边做,过程中遇到了很多问题。当然只要去用心去做,问题都会迎刃而解的。所以里面的python设计和代码结构不会很高深,因为主要是为了方便有这些需求的人使用,里面还有很多问题需要花精力和人力去处理的,希望各位大神能够谅解。
主要的是通过git去获取项目,然后自动配置项目域名,代码目录,伪静态,数据库导入,项目定时器自动转换并且导入,其他一些扩展文件,扩展函数自动安装与配,实现自动化处理。管理后台可视化添加服务器并且做自动化部署处理,往后可以一键对所有服务器进行补丁,以及服务器的批量更新。
version:python3.7
框架:flask(不为什么,只是觉得轻量,简单),一开始选用django,发现django相对难一点
sdk:
这几种模块都是自己摸索,上网各种百度,谷歌。然后结合当下服务器部署出现各种的问题,从而一点一滴的完善这个项目,真心不容易。
下面只写一些代码片段和部分的主体实现流程
{
"name": "项目名称-格式使用英文,如test",
"describe": "项目的描述,如:这是XX客户项目",
"version": "版本,随便填",
"git": "git地址,必须使用http格式,如:http://git/test.git",
"server":{
"domain":"www.test.com",
"path": "/www/wwwroot/test",
"php": "70",
"ftp":"false",
"sql": "true"
},
"server_info": {
"user": "",
"pwd": "",
"ip": ""
},
"database": {
"name": "",
"user": "",
"password": ""
},
"bt": {
"panel": "",
"key": ""
},
"depend": {
"import": [
{
'name':'database.php',
'path':'application'
}
],
"bt_contrab":[
]
}
}
代码解析:
server:这里存放的是项目的基本配置,domain:配置的域名,path:项目储存路径,php:代码版本,ftp:是否开启ftp,sql:是否自动创建数据库,其格式都是根据宝塔创建项目的格式配置出来的
server_info:服务器信息,user:服务器用户名,pwd服务器密码,ip:服务器IP
database:项目数据库,name:数据库名,user:账号,password:密码
bt:宝塔API的配置,panel面板地址,key:APItoken
depend:一些依赖,如定时器,或者git忽略上传的文件,import 上传一些依赖文件,如数据库配置文件,path上传到项目哪个目录下,若是上传到根目录,填写’/’。bt_contrab:宝塔定时器,请根据宝塔的定时器的参数进行配置该处,后面有机会会出一个linux_crontab(这个键名请忽略)
基础程序处理代码整理不是很好,因为先把功能整出来再慢慢去优化
import json
import re
import time
import chardet
from Logger import Logger
from config.Config_n import Config
from model.Db import Bt, Project
from tools.bt import bt
from tools.common import asyncn, createKey, initGlobal, setGlobalvalue, getGlobalvalue, getIp, GetRandomString, Md5, \
en_crypt, autoFild
from tools.monitor import Monitor
from tools.SshInteraction.InterationFactory import InterationFactory
class Base:
log = ''
project = ''
def __init__(self, project_name):
if project_name is None:
print("请输入项目名称")
exit(100)
self.project_name = project_name
conf = Config() # 初始配置项
project_conf = conf.getProjectConfig(self.project_name)
if project_conf is None:
self._log('项目配置不存在:' + self.project)
print("项目配置不存在")
exit(101)
self._project_conf = project_conf
def updateConf(self, conf):
self._project_conf.update(conf)
# 单例服务器
def _inistanceMonitor(self):
try:
m = getGlobalvalue('GLOBAL_Monitor')
if m is None:
# 重新定义
try:
projects = self._project_conf
server = projects["server_info"]
# print(server)
m = Monitor(server['ip'], server['user'], server['pwd']) # 连接服务器
setGlobalvalue('GLOBAL_Monitor', m)
return m
except Exception as e:
self.log.info('服务器连接失败')
exit(102)
return m
except KeyError:
# 重新定义
projects = self._project_conf
server = projects["server_info"]
try:
monitor = Monitor(server['ip'], server['user'], server['pwd']) # 连接服务器
setGlobalvalue('GLOBAL_Monitor', monitor)
return monitor
except Exception:
# print('E错误')
self.log.info('服务器连接失败')
exit(102)
except Exception:
print('报了一个错')
# 单例宝塔
def _inistanceBt(self):
try:
m = getGlobalvalue('GLOBAL_BT')
if m is None:
# 重新定义
projects = self._project_conf
try:
_bt = bt(projects["bt"]["panel"], projects["bt"]["key"])
setGlobalvalue('GLOBAL_BT', _bt)
return _bt
except Exception:
# self._log('服务器连接失败')
exit(103)
return m
except KeyError:
# 重新定义
projects = self._project_conf
server = projects["server_info"]
try:
_bt = bt(projects["bt"]["panel"], projects["bt"]["key"])
setGlobalvalue('GLOBAL_BT', _bt)
return _bt
except Exception:
# self._log('服务器连接失败')
exit(103)
# 单例宝塔
def _inistanceGit(self):
try:
m = getGlobalvalue('GLOBAL_BT')
return m
except KeyError:
# 重新定义
projects = self._project_conf
server = projects["server_info"]
try:
_bt = bt(projects["bt"]["panel"], projects["bt"]["key"])
setGlobalvalue('GLOBAL_BT', _bt)
return _bt
except Exception:
# self._log('服务器连接失败')
exit(103)
def checkEnv(self, is_force_api=False):
self.checkBt(is_force_api)
self.appinstall()
# 环境检测
# 是否安装了宝塔
def checkBt(self, is_force_api=False):
m = self._inistanceMonitor()
b = self._inistanceBt()
if m.cmd_exists('bt') is False:
print('宝塔没有安装')
return json.encoder({'status': 0, 'msg': '宝塔未安装'})
exit(104)
# 判断宝塔IP
try:
ip = getIp()
except Exception:
print('获取IP失败')
exit(105)
flaskapp = getGlobalvalue('flaskapp')
path = flaskapp.root_path + '/runtime/' + self._project_conf['name'] + 'api.json'
api_json_path = '/www/server/panel/config/api.json'
if is_force_api is True:
# 强制开启并且刷新一下宝塔信息
rs = self.getBtinfo()
if rs is None:
exit('宝塔信息获取失败')
key = GetRandomString(32)
token = Md5(key)
token_crypt = en_crypt(token, key).decode('utf-8')
jsoncontent = {
"token": token,
"open": True,
"limit_addr": [],
"token_crypt": token_crypt
}
jsoncontent['limit_addr'].append(ip)
jsonstr = json.dumps(jsoncontent)
autoFild(path, jsonstr)
# m.write(api_json_path, jsonstr)
m.upload(path, api_json_path)
r = jsoncontent
b.resetKey(token_crypt)
#后面需要优化
if self._project_conf['bt_id'] is None:
btinfo = self.getBtInfoParse(rs)
btModel = Bt(panel=btinfo[0], key=token_crypt, password=btinfo[2], username=btinfo[1],code=btinfo[3])
bt_id = btModel.add(btModel)
projectModel = Project()
projectModel.updateBtid(self._project_conf['id'], bt_id)
#自动写入同意
m.write('/www/server/panel/data/licenes.pl','')
else:
btModel = Bt()
btModel.updatekey(self._project_conf['bt_id'],key=token_crypt)
else:
if m.path_exists(api_json_path) is False:
self._log('未开启宝塔接口,需要开启api')
exit(106)
else:
r = json.loads(m.link_server('cat ' + api_json_path))
if r['open'] is False:
self._log('未开启宝塔接口,需要开启api')
exit(107)
if ip not in r["limit_addr"]:
r['limit_addr'].append(ip)
autoFild(path, up_r)
# m.write(api_json_path, jsonstr)
m.upload(path, api_json_path)
print('自动加入白名单:' + ip)
def _log(self, msg):
self.log.info(msg)
pass
def setLogFile(self, name):
flaskapp = getGlobalvalue('flaskapp')
# print(self.app.root_path)
path = flaskapp.root_path + '/runtime/' + name
self.log = Logger(path, 'info', 'D', 3, '%(asctime)s - %(pathname)s[line:%(lineno)d]- %(message)s
').logger
def getBtinfo(self):
channel = self._inistanceMonitor().sshClientInvokeShell()
channel.send('bt 14\r')
time.sleep(1.5)
rst = channel.recv(6000).decode('utf-8')
return rst
# 安装宝塔环境
def install(self):
# ERROR!This server has only one hard drive,exit
m = self._inistanceMonitor()
if m.cmd_exists('bt') is True:
print(self.getBtinfo())
self._log('宝塔已经安装不能继续安装')
return
InterationFactory('InstallBt')
pass
#安装应用环境
def appinstall(self):
m=self._inistanceMonitor()
b=self._inistanceBt()
if m.cmd_exists('php -v') is False:
b.install_plugin('php-7.0','7.0','1')
if m.cmd_exists('nginx -v') is False:
b.install_plugin('nginx', '1.18', '1')
if m.cmd_exists('mysql -v') is False:
b.install_plugin('mysql','5.6','1')
env={'php':False,'mysql':False,'nginx':False}
self._log('环境安装中...')
reset_time=300
while(True):
#检测PHP
if env['php'] is False:
if m.cmd_exists('php -v') is True:
env['php'] = True
self._log('PHP安装成功')
#检测nginx
if env['nginx'] is False:
if m.cmd_exists('nginx -v') is True:
env['nginx'] = True
self._log('nginx安装成功')
# 检测mysql
if env['mysql'] is False:
if m.cmd_exists('mysql -v') is True:
env['mysql'] = True
self._log('mysql安装成功')
if env['php'] is True and env['nginx'] is True and env['mysql'] is True:
break
reset_time = reset_time-5
time.sleep(5)
if reset_time < 0:
self.appinstall()
break
def installBtApi(self):
pass
# 获取宝塔信息解析
def getBtInfoParse(self, str):
r = re.findall(r'(Bt-Panel-URL\: [a-zA-z]+://[^\s]*\:8888|username\:.*|password\:.*)', str)
a = []
for i in r:
o = i.replace('Bt-Panel-URL:', '').replace('username:', '').replace('password:', '').lstrip()
a.append(o)
#获取CODE
code=self._inistanceMonitor().link_server('cat /www/server/panel/data/admin_path.pl')
a.append(code)
if a.__len__() < 3:
print('宝塔信息解析失败BASE')
exit(107)
return a
import time
from flask import app
from Logger import Logger
from config.Config_n import Config
from logic.Base import Base
from model import Db
from model.Db import Project
from tools.common import *
from tools.depend import depend
from tools.monitor import Monitor
from tools.bt import bt
from tools.git import git
class User(Base):
# 新建到正式
def new(self):
log = self.log
try:
conf = Config() # 初始配置项
projects = self._project_conf
print(projects)
if projects['status'] == 1:
log.info('不能继续搭建,已搭建成功')
# 初始服务器
server = projects["server_info"]
monitor = self._inistanceMonitor()
# 初始宝塔
GLOBAL_BT = self._inistanceBt()
# 检测环境
self.checkEnv(True)
# 克隆项目
log.info('正在执行克隆代码')
_git = git(projects)
_git.clone(monitor) # 克隆项目
log.info('克隆代码完成')
# 宝塔操作
# 由于新搭建的所以其服务器配置信息是使用测试服的
log.info('搭建与设置站点')
n_p = projects['server']
n_p["domain"] = n_p['domain']
# GLOBAL_BT.AddSite(n_p) # 新建站点
# GLOBAL_BT.SaveFileBody(n_p['domain']) # 设置伪静态
# GLOBAL_BT.SetSiteRunPath(n_p['domain'], n_p['runpath']) # 设置运行目录
GLOBAL_BT.doProcess(['addsite', 'savefilebody', 'adddatabase', 'setsiterunpath'], self)
GLOBAL_BT.setPHPDisable(n_p['php'])
GLOBAL_BT.installSoft(n_p['php'])
log.info('搭建与设置完成')
log.info('创建数据库')
# 依赖处理器
log.info('处理依赖与数据库与定时器')
_e = depend(projects)
#自动加入databse备份
_e.addAutoDatabase().run()
_e.autoInputDatabase()
log.info('处理依赖与数据库与定时器完成')
# 修改目录权限 www:www 755
log.info('更改权限')
monitor.link_server("usermod -s /bin/bash www")
monitor.link_server("chown www:www -R " + projects['server']['path'])
monitor.link_server("chmod 755 - R" + projects['server']['path'])
Project().updateStatus(id=projects['id'], status=1)
log.info('更改权限完成')
monitor.close()
except Exception as e:
#monitor.close()
#Db.session.close()
#log.info(str(e))
print(str(e))
def codepull(self, project_name):
conf = Config() # 初始配置项
projects = conf.getProjectConfig(project_name) # 获取项目配置
test_server = projects['server_info']
self.log.info('连接服务器中')
monitor = Monitor(test_server['ip'], test_server['user'], test_server['pwd']) # 连接服务器
self.log.info('连接服务器成功')
setGlobalvalue("GLOBAL_Monitor", monitor)
self.log.info('开始更新')
_git = git(projects)
_git.pull(monitor, self.log)
测试服务器代码主要将服务器和域名修改一下即可
import json
import os
from config.Config_n import Config
from logic.Base import Base
from tools.common import getGlobalvalue,unzip_all
from tools.depend import depend
from tools.git import git
class Test(Base):
def __init__(self,project):
Base.__init__(self,project)
conf = Config() # 初始配置项
conf = conf.getAllConfig()
test_server = conf['test_server']
u_project = {}
# 服务器信息更新
u_project['server_info'] = {
'ip': test_server['ip'],
'user': test_server['user'],
'pwd': test_server['pwd']
}
# 宝塔信息更新
u_project['bt'] = {
'panel': conf["test_bt"]["panel"],
'key': conf["test_bt"]["key"]
}
self.updateConf(u_project)
# 更新搭建信息
#self._project_conf['server']['domain'] = self.project_name + '.' + conf['test_server']['domain']
def new(self):
log = self.log
try:
conf = Config() # 初始配置项
conf = conf.getAllConfig()
test_server = conf['test_server']
u_project = {}
# 服务器信息更新
u_project['server_info'] = {
'ip': test_server['ip'],
'user': test_server['user'],
'pwd': test_server['pwd']
}
# 宝塔信息更新
u_project['bt'] = {
'panel': conf["test_bt"]["panel"],
'key': conf["test_bt"]["key"]
}
self.updateConf(u_project)
# 更新搭建信息
self._project_conf['server']['domain'] = self.project_name + '.' + conf['test_server']['domain']
# u_project['server'] = {"domain":self.project_name + '.' + conf['test_server']['domain']}
# 初始服务器
monitor = self._inistanceMonitor()
# 初始宝塔
GLOBAL_BT = self._inistanceBt()
# 检测环境
self.checkEnv()
# 克隆项目
log.info('正在执行克隆代码')
_git = git(self._project_conf)
_git.clone(monitor) # 克隆项目
log.info('克隆代码完成')
log.info('搭建与设置站点')
n_p = self._project_conf['server']
projects = self._project_conf
# GLOBAL_BT.AddSite(n_p) # 新建站点
# GLOBAL_BT.SaveFileBody(n_p['domain']) # 设置伪静态
# GLOBAL_BT.addDatabase(projects['database']['name'], projects['database']['user'],
# projects['database']['password']) # 添加数据库
# try:
# GLOBAL_BT.SetSiteRunPath(n_p['domain'], "/public") # 设置运行目录
# except Exception as e:
# print('数据库添加失败' + str(e))
GLOBAL_BT.doProcess(['addsite', 'savefilebody', 'adddatabase', 'setsiterunpath'], self)
log.info('搭建与设置完成')
# 依赖处理器
log.info('处理依赖与数据库与定时器')
_e = depend(projects)
_e.run()
log.info('处理依赖与数据库与定时器完成')
# 修改目录权限 www:www 755
log.info('更改权限')
monitor.link_server("usermod -s /bin/bash www")
monitor.link_server("chown www:www -R " + projects['server']['path'])
monitor.link_server("chmod 755 - R" + projects['server']['path'])
_e.autoInputDatabase()
log.info('更改权限完成')
monitor.close()
except Exception as e:
log.info('系统报了一个错误!不能继续执行:'+e)
def doDepend(self):
conf = Config() # 初始配置项
conf = conf.getAllConfig()
test_server = conf['test_server']
u_project = {}
# 服务器信息更新
u_project['server_info'] = {
'ip': test_server['ip'],
'user': test_server['user'],
'pwd': test_server['pwd']
}
# 宝塔信息更新
u_project['bt'] = {
'panel': conf["test_bt"]["panel"],
'key': conf["test_bt"]["key"]
}
self.updateConf(u_project)
# 更新搭建信息
self._project_conf['server']['domain'] = self.project_name + '.' + conf['test_server']['domain']
# u_project['server'] = {"domain":self.project_name + '.' + conf['test_server']['domain']}
# 初始服务器
monitor = self._inistanceMonitor()
rs = monitor.link_server('bt 14')
print(rs)
def codepull(self):
monitor = self._inistanceMonitor()
_git = git(self._project_conf)
_git.pull()
#coding: utf-8
# +-------------------------------------------------------------------
# | 宝塔Linux面板
# +-------------------------------------------------------------------
# | Copyright (c) 2015-2099 宝塔软件 All rights reserved.
# +-------------------------------------------------------------------
# | Author: xgh <[email protected]>
# +-------------------------------------------------------------------
#------------------------------
# API-Demo of Python
#------------------------------
import re
import time,hashlib,sys,os,json
import logging
from urllib.error import HTTPError
from tools.common import autoFild, getIp
class bt:
__BT_KEY = ''
__BT_PANEL = ''
#如果希望多台面板,可以在实例化对象时,将面板地址与密钥传入
def __init__(self,bt_panel=None,bt_key = None):
if bt_panel:
self.__BT_PANEL = bt_panel
self.__BT_KEY = bt_key
def resetKey(self,key):
self.__BT_KEY=key
#取面板日志
def get_logs(self):
#拼接URL地址
url = self.__BT_PANEL + '/data?action=getData'
#准备POST数据
p_data = self.__get_key_data() #取签名
p_data['table'] = 'logs'
p_data['limit'] = 10
p_data['tojs'] = 'test'
#请求面板接口
result = self.__http_post_cookie(url,p_data)
#解析JSON数据
return json.loads(result)
#创建站点
def AddSite(self,arg):
# 拼接URL地址
try:
url = self.__BT_PANEL + '/site?action=AddSite'
__dict = {"domainlist":[],"count":0}
__dict.update({"domain":arg['domain']})
# 准备POST数据
p_data = self.__get_key_data() # 取签名
p_data['type_id'] = 0
p_data['type'] = 'PHP'
p_data['version'] = arg['php']
p_data['port'] = '80'
p_data['webname'] = json.dumps(__dict)
p_data['path'] = '/www/wwwroot/'
p_data['ps'] = '测试'
p_data['ftp'] = 'false'
p_data['sql'] = 'false'
p_data.update(arg)
# 请求面板接口
result = self.__http_post_cookie(url, p_data)
print(p_data)
print(json.loads(result))
return json.loads(result)
except Exception as e:
print(e)
raise Exception("AddSite接口的数据传输有误!")
# 解析JSON数据
#保存伪静态规则的内容,现在只支持thinkphp
def SaveFileBody(self,domain):
url = self.__BT_PANEL + '/files?action=SaveFileBody'
p_data = self.__get_key_data() # 取签名
p_data['path'] = '/www/server/panel/vhost/rewrite/'+domain+'.conf'
p_data['encoding'] = 'utf-8'
p_data['data'] = open('./tools/vhost.str').read()
# 请求面板接口
result = self.__http_post_cookie(url, p_data)
j_result = json.loads(result)
#status = j_result['status']
if j_result['status'] == False:
print(j_result['msg'])
print(p_data)
# 解析JSON数据
return json.loads(result)
#search 站点名称
def getSiteId(self,search):
try:
url = self.__BT_PANEL + '/data?action=getData&table=sites'
p_data = self.__get_key_data() # 取签名
p_data['search'] = search
p_data['limit'] = 1
# 请求面板接口
result = self.__http_post_cookie(url, p_data)
r = json.loads(result)
if r['data'] != []:
return r['data'][0]['id']
else:
Exception('111')
except Exception:
print('获取站点ID失败,请核实')
def SetSiteRunPath(self,name,runPath = '/'):
url = self.__BT_PANEL + '/site?action=SetSiteRunPath'
id = self.getSiteId(name)
p_data = self.__get_key_data() # 取签名
p_data['id'] = id
p_data['runPath'] = runPath
# 请求面板接口
result = self.__http_post_cookie(url, p_data)
# 解析JSON数据
return json.loads(result)
#添加定时器
def AddCrontab(self,arg):
#try:
url = self.__BT_PANEL + '/crontab?action=AddCrontab'
p_data = self.__get_key_data() # 取签名
p_data['sType'] = "toShell"
p_data['backupTo'] = "localhost"
p_data['sName'] = ""
p_data['save'] =""
p_data['urladdress'] ="undefined"
p_data.update(arg)
print(p_data)
# 请求面板接口
result = self.__http_post_cookie(url, p_data)
# 解析JSON数据
return json.loads(result)
#except Exception:
#logging.error('添加定时器失败')
#获取所有站点信息
def getAllSites(self,p=1,result = []):
url = self.__BT_PANEL + '/data?action=getData&table=sites'
p_data = self.__get_key_data() # 取签名
p_data['limit'] = 20
p_data['p'] = p
p_data['order'] = 'id asc'
r_result = self.__http_post_cookie(url, p_data)
j_result = json.loads(r_result)
if len(j_result["data"]) == 0 :
return result
result.extend(j_result["data"])
#print(p)
return self.getAllSites(p+1,result)
#停止站点
def stopSite(self,id,name):
url = self.__BT_PANEL + '/site?action=SiteStop'
p_data = self.__get_key_data() # 取签名
p_data["id"] = id
p_data["name"] = name
r_result = self.__http_post_cookie(url, p_data)
print(json.loads(r_result))
#print("正在处理:"+name+",结果:"+r_result)
#重命名
def rName(self,sfile,dfile):
url = self.__BT_PANEL + '/files?action=MvFile'
p_data = self.__get_key_data() # 取签名
p_data["sfile"] = sfile
p_data["dfile"] = dfile
p_data["rename"] = True
r_result = self.__http_post_cookie(url, p_data)
print(r_result)
#新建数据库
def addDatabase(self,dataname,datauser,password):
url = self.__BT_PANEL + '/database?action=AddDatabase'
p_data = self.__get_key_data() # 取签名
p_data["name"] = dataname
p_data["codeing"] = 'utf8'
p_data["db_user"] = datauser
p_data["password"] = password
p_data["dtype"] = "MySQL"
p_data["dataAccess"] = "127.0.0.1"
p_data["address"] = "127.0.0.1"
p_data["ps"] = "test"
r_result = self.__http_post_cookie(url, p_data)
return json.loads(r_result)
#InputSql
def inputSql(self,file,name):
url = self.__BT_PANEL + '/database?action=InputSql'
p_data = self.__get_key_data() # 取签名
p_data["file"] = file
p_data["name"] = name
r_result = self.__http_post_cookie(url, p_data)
return json.loads(r_result)
def setPHPDisable(self,version="70"):
url = self.__BT_PANEL + '/config?action=setPHPDisable'
p_data = self.__get_key_data() # 取签名
p_data["version"] = version
p_data["disable_functions"] = 'create_function,passthru,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv'
r_result = self.__http_post_cookie(url, p_data)
return json.loads(r_result)
#安装redis
def installSoft(self,version="70"):
url = self.__BT_PANEL + '/files?action=InstallSoft'
p_data = self.__get_key_data() # 取签名
p_data["version"] = version
p_data["name"] = 'redis'
p_data["type"] = 1
r_result = self.__http_post_cookie(url, p_data)
return json.loads(r_result)
def install_plugin(self,sName,version,type):
url = self.__BT_PANEL + '/plugin?action=install_plugin'
p_data = self.__get_key_data() # 取签名
p_data["version"] = version
p_data["sName"] = sName
p_data["type"] = type
r_result = self.__http_post_cookie(url, p_data)
print(json.loads(r_result))
return json.loads(r_result)
#设置自动设置白名单并强制开启API接口
def autoSetToken(self):
try:
get_token = self.__BT_PANEL + '/config?action=get_token'
p_data = self.__get_key_data() # 取签名
r_result = self.__http_post_cookie(get_token, p_data)
#由于返回没有其他任何状态,所以只能直接操作
localIp = getIp()
limit_addr = r_result['limit_addr']
if limit_addr.find(localIp) < 0:
n_addr = limit_addr.join("\r"+localIp)
print(n_addr)
url = self.__BT_PANEL + '/config?action=set_token'
p_data = self.__get_key_data() # 取签名
p_data["t_type"] = 3
p_data["limit_addr"] = n_addr
r_result = self.__http_post_cookie(url, p_data)
if r_result['open'] == "false":
url = self.__BT_PANEL + '/config?action=set_token'
p_data = self.__get_key_data() # 取签名
p_data["t_type"] = 2
r_result = self.__http_post_cookie(url, p_data)
return json.encoder({'status':'true','msg':'没有任何异常就是设置成功'})
#开启与关闭API接口
except Exception as e :
print('抛出错误:可能limit_addr没有获取到,可能IP获取失败')
print(str(e))
#要关联服务器处理实例
def doProcess(self,list,serverLogic):
projects = serverLogic._project_conf
n_p = projects['server']
is_break = False
rs = None
try:
for method in list:
m = method.lower()
print(m)
if m == 'addsite':
#检测站点是否已经搭建
rs = self.getSiteId(n_p['domain'])
if rs is not None:
serverLogic._log(n_p['domain'] + '站点已存在!不能继续搭建')
exit()
break
# 新建站点
rs = self.AddSite(n_p)
if 'siteStatus' not in rs:
serverLogic._log(n_p['domain'] + '站点报了一个已搭建的错误')
exit()
break
elif m == 'savefilebody':
# 设置伪静态
rs = self.SaveFileBody(n_p['domain'])
elif m == 'adddatabase':
# 添加数据库
rs = self.addDatabase(projects['database']['name'], projects['database']['user'],
projects['database']['password']) # 添加数据库
elif m == 'setsiterunpath':
#设置运行目录
rs = self.SetSiteRunPath(n_p['domain'], n_p['runpath']) # 设置运行目录
else:
print('找不到相应的宝塔程序接口:'+m)
if rs is not None:
if 'msg' in rs:
serverLogic._log(rs['msg'])
if is_break is True and rs['status'] is False:
break
except Exception as e:
print(e)
serverLogic._log('搭建宝塔失败'+e)
raise Exception('宝塔处理失败')
except HTTPError as h:
print('宝塔API返回状态500错误,请重试!')
#计算MD5
def __get_md5(self,s):
m = hashlib.md5()
m.update(s.encode('utf-8'))
return m.hexdigest()
#构造带有签名的关联数组
def __get_key_data(self):
now_time = int(time.time())
p_data = {
'request_token':self.__get_md5(str(now_time) + '' + self.__get_md5(self.__BT_KEY)),
'request_time':now_time
}
return p_data
#发送POST请求并保存Cookie
#@url 被请求的URL地址(必需)
#@data POST参数,可以是字符串或字典(必需)
#@timeout 超时时间默认1800秒
#return string
def __http_post_cookie(self,url,p_data,timeout=1800):
cookie_file = autoFild('./runtime/'+self.__get_md5(self.__BT_PANEL) + '.cookie','# Netscape HTTP Cookie File')
if sys.version_info[0] == 2:
#Python2
import urllib,urllib2,ssl,cookielib
#创建cookie对象
cookie_obj = cookielib.MozillaCookieJar(cookie_file)
#加载已保存的cookie
if os.path.exists(cookie_file):cookie_obj.load(cookie_file,ignore_discard=True,ignore_expires=True)
ssl._create_default_https_context = ssl._create_unverified_context
data = urllib.urlencode(p_data)
req = urllib2.Request(url, data)
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie_obj))
response = opener.open(req,timeout=timeout)
#保存cookie
cookie_obj.save(ignore_discard=True, ignore_expires=True)
return response.read()
else:
#Python3
import urllib.request,ssl,http.cookiejar
cookie_obj = http.cookiejar.MozillaCookieJar(cookie_file)
cookie_obj.load(cookie_file,ignore_discard=True,ignore_expires=True)
handler = urllib.request.HTTPCookieProcessor(cookie_obj)
data = urllib.parse.urlencode(p_data).encode('utf-8')
req = urllib.request.Request(url, data)
opener = urllib.request.build_opener(handler)
response = opener.open(req,timeout = timeout)
cookie_obj.save(ignore_discard=True, ignore_expires=True)
result = response.read()
if type(result) == bytes: result = result.decode('utf-8')
return result
def u(s, encoding="utf8"):
"""cast bytes or unicode to unicode"""
if isinstance(s, bytes):
try:
return s.decode(encoding)
except UnicodeDecodeError:
return s.decode('ISO-8859-1')
elif isinstance(s, str):
return s
else:
raise TypeError("Expected unicode or bytes, got {!r}".format(s))
该系统虽然有很多的不足,但是至少能够不用重复工作,由于个人时间比较有限,能做到解放双手已经满足了,上面系统源码有需要的,请在评论区评论。希望与你共同开发此套代码,做到批量管理服务器。