pull/156/head
midoks 3 years ago
parent 52d5fe4470
commit 5867c5d2cc
  1. 16
      plugins/postgresql/conf/postgresql.conf
  2. 2
      plugins/postgresql/index.html
  3. 338
      plugins/postgresql/index.py
  4. 166
      plugins/postgresql/js/postgresql.js

@ -22,6 +22,22 @@ timezone = 'Asia/Shanghai'
default_text_search_config = 'pg_catalog.simple'
# 主配置
#archive_mode = on
#archive_command = 'test ! -f {$APP_PATH}/archivelog/%f && cp %p {$APP_PATH}/archivelog/%f'
#wal_level = replica
#max_wal_senders = 10
#wal_sender_timeout = 60s
# 从配置
#primary_conninfo = 'host=192.168.0.100 port=5432 user=replica password=123456'
#hot_standby = on
#max_standby_streaming_delay = 30s
#wal_receiver_status_interval = 10s
#hot_standby_feedback = on
logging_collector = on
log_destination = 'stderr'
log_directory = '{$APP_PATH}/logs'

@ -12,7 +12,7 @@
<p onclick="pluginLogs('postgresql',$('.plugin_version').attr('version'),'run_log');">日志</p>
<p onclick="pluginLogs('postgresql',$('.plugin_version').attr('version'),'slow_log');">慢日志</p>
<p onclick="dbList()">管理列表</p>
<!--<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p> -->
<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con" style="height: 530px; overflow: auto;"></div>

@ -174,17 +174,8 @@ def pgDb():
return db
def initDreplace(version=''):
def initConfig(version=''):
conf_dir = getServerDir()
conf_list = [
conf_dir + "/logs",
conf_dir + "/tmp",
]
for c in conf_list:
if not os.path.exists(c):
os.mkdir(c)
init_pl = conf_dir + "/init.pl"
if not os.path.exists(init_pl):
mw.writeFile(init_pl, 'ok')
@ -206,6 +197,19 @@ def initDreplace(version=''):
if not os.path.exists(logfile):
mw.writeFile(logfile, '')
def initDreplace(version=''):
conf_dir = getServerDir()
conf_list = [
conf_dir + "/logs",
conf_dir + "/tmp",
conf_dir + "/archivelog",
]
for c in conf_list:
if not os.path.exists(c):
os.mkdir(c)
# systemd
system_dir = mw.systemdCfgDir()
service = system_dir + '/postgresql.service'
@ -285,6 +289,7 @@ def pgOp(version, method):
# print(cmd)
try:
isInited = initPgData()
initConfig(version)
if not isInited:
if mw.isAppleSystem():
cmd_init_start = init_file + ' start'
@ -949,7 +954,7 @@ def delDb():
r = pdb.execute("drop database " + name)
# print(r)
pg_hba = getServerDir() + "/data/pg_hba.conf"
pg_hba = pgHbaConf()
old_config = mw.readFile(pg_hba)
new_config = re.sub(r'host\s*{}.*'.format(name), '', old_config).strip()
mw.writeFile(pg_hba, new_config)
@ -1099,6 +1104,293 @@ def deleteDbBackup():
return mw.returnJson(True, 'ok')
############ 主从功能 ######################
def getMasterStatus(version=''):
data = {}
data['mode'] = 'classic'
data['status'] = False
data['slave_status'] = False
pg_conf = getServerDir() + "/data/postgresql.conf"
pg_content = mw.readFile(pg_conf)
if pg_content.find('#archive_mode') > -1:
data['status'] = False
else:
data['status'] = True
if pg_content.find('#hot_standby') > -1:
data['slave_status'] = False
else:
data['slave_status'] = True
return mw.returnJson(True, '设置成功', data)
def setMasterStatus(version=''):
pg_conf = getServerDir() + "/data/postgresql.conf"
data = mw.readFile(pg_conf)
if data.find('#archive_mode') > -1:
data = data.replace('#archive_mode', 'archive_mode')
data = data.replace('#archive_command', 'archive_command')
data = data.replace('#wal_level', 'wal_level')
data = data.replace('#max_wal_senders', 'max_wal_senders')
data = data.replace('#wal_sender_timeout', 'wal_sender_timeout')
else:
data = data.replace('archive_mode', '#archive_mode')
data = data.replace('archive_command', '#archive_command')
data = data.replace('wal_level', '#wal_level')
data = data.replace('max_wal_senders', '#max_wal_senders')
data = data.replace('wal_sender_timeout', '#wal_sender_timeout')
mw.writeFile(pg_conf, data)
restart(version)
return mw.returnJson(True, '设置成功')
def setSlaveStatus(version):
pg_conf = getServerDir() + "/data/postgresql.conf"
data = mw.readFile(pg_conf)
if data.find('#hot_standby') > -1:
data = data.replace('#hot_standby', 'hot_standby')
data = data.replace('#primary_conninfo', 'primary_conninfo')
data = data.replace('#max_standby_streaming_delay',
'max_standby_streaming_delay')
data = data.replace('#wal_receiver_status_interval',
'wal_receiver_status_interval')
data = data.replace('#hot_standby_feedback', 'hot_standby_feedback')
else:
data = data.replace('hot_standby', '#hot_standby')
data = data.replace('primary_conninfo', '#primary_conninfo')
data = data.replace('max_standby_streaming_delay',
'#max_standby_streaming_delay')
data = data.replace('wal_receiver_status_interval',
'#wal_receiver_status_interval')
data = data.replace('hot_standby_feedback', '#hot_standby_feedback')
mw.writeFile(pg_conf, data)
restart(version)
return mw.returnJson(True, '设置成功')
def getSlaveList(version=''):
db = pgDb()
# dlist = db.query('show slave status')
# ret = []
# for x in range(0, len(dlist)):
# tmp = {}
# tmp['Master_User'] = dlist[x]["Master_User"]
# tmp['Master_Host'] = dlist[x]["Master_Host"]
# tmp['Master_Port'] = dlist[x]["Master_Port"]
# tmp['Master_Log_File'] = dlist[x]["Master_Log_File"]
# tmp['Slave_IO_Running'] = dlist[x]["Slave_IO_Running"]
# tmp['Slave_SQL_Running'] = dlist[x]["Slave_SQL_Running"]
# ret.append(tmp)
data = {}
data['data'] = []
return mw.getJson(data)
def getSlaveSSHByIp(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_id_rsa')
data = conn.field('ip,port,db_user,id_rsa').where("ip=?", (ip,)).select()
return mw.returnJson(True, 'ok', data)
def getSlaveSSHList(version=''):
args = getArgs()
data = checkArgs(args, ['page', 'page_size'])
if not data[0]:
return data[1]
page = int(args['page'])
page_size = int(args['page_size'])
conn = pSqliteDb('slave_id_rsa')
limit = str((page - 1) * page_size) + ',' + str(page_size)
field = 'id,ip,port,db_user,id_rsa,ps,addtime'
clist = conn.field(field).limit(limit).order('id desc').select()
count = conn.count()
data = {}
_page = {}
_page['count'] = count
_page['p'] = page
_page['row'] = page_size
_page['tojs'] = args['tojs']
data['page'] = mw.getPage(_page)
data['data'] = clist
return mw.getJson(data)
def addSlaveSSH(version=''):
import base64
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
if ip == "":
return mw.returnJson(True, 'ok')
data = checkArgs(args, ['port', 'id_rsa', 'db_user'])
if not data[0]:
return data[1]
id_rsa = args['id_rsa']
port = args['port']
db_user = args['db_user']
user = 'root'
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
conn = pSqliteDb('slave_id_rsa')
data = conn.field('ip,id_rsa').where("ip=?", (ip,)).select()
if len(data) > 0:
res = conn.where("ip=?", (ip,)).save(
'port,id_rsa,db_user', (port, id_rsa, db_user))
else:
conn.add('ip,port,user,id_rsa,db_user,ps,addtime',
(ip, port, user, id_rsa, db_user, '', addTime))
return mw.returnJson(True, '设置成功!')
def delSlaveSSH(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_id_rsa')
conn.where("ip=?", (ip,)).delete()
return mw.returnJson(True, 'ok')
def updateSlaveSSH(version=''):
args = getArgs()
data = checkArgs(args, ['ip', 'id_rsa'])
if not data[0]:
return data[1]
ip = args['ip']
id_rsa = args['id_rsa']
conn = pSqliteDb('slave_id_rsa')
conn.where("ip=?", (ip,)).save('id_rsa', (id_rsa,))
return mw.returnJson(True, 'ok')
def getMasterRepSlaveList(version=''):
args = getArgs()
data = checkArgs(args, ['page', 'page_size'])
if not data[0]:
return data[1]
page = int(args['page'])
page_size = int(args['page_size'])
data = {}
conn = pSqliteDb('master_replication_user')
limit = str((page - 1) * page_size) + ',' + str(page_size)
field = 'id,username,password,accept,ps,addtime'
clist = conn.field(field).limit(limit).order('id desc').select()
count = conn.count()
_page = {}
_page['count'] = count
_page['p'] = page
_page['row'] = page_size
_page['tojs'] = 'getMasterRepSlaveList'
data['page'] = mw.getPage(_page)
data['data'] = clist
return mw.getJson(data)
def addMasterRepSlaveUser(version=''):
args = getArgs()
data = checkArgs(args, ['username', 'password', 'address'])
if not data[0]:
return data[1]
address = args['address'].strip()
username = args['username'].strip()
password = args['password'].strip()
if len(password) < 1:
password = mw.md5(time.time())[0:8]
if not re.match("^[\w\.-]+$", username):
return mw.returnJson(False, '用户名不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
if username in checks or len(username) < 1:
return mw.returnJson(False, '用户名不合法!')
if password in checks or len(password) < 1:
return mw.returnJson(False, '密码不合法!')
pdb = pgDb()
psdb = pSqliteDb('master_replication_user')
if psdb.where("username=?", (username)).count() > 0:
return mw.returnJson(False, '用户已存在!')
sql = "CREATE ROLE " + username + " login replication password '" + password + "'"
pdb.execute(sql)
pg_conf = pgHbaConf()
data = mw.readFile(pg_conf)
data = data + "\nhost replication " + \
username + " " + address + " md5"
mw.writeFile(pg_conf, data)
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
psdb.add('username,password,accept,ps,addtime',
(username, password, address, '', addTime))
return mw.returnJson(True, '添加成功!')
def delMasterRepSlaveUser(version=''):
args = getArgs()
data = checkArgs(args, ['username'])
if not data[0]:
return data[1]
name = args['username']
pdb = pgDb()
psdb = pSqliteDb('master_replication_user')
pdb.execute("drop user " + name)
pg_hba = pgHbaConf()
old_config = mw.readFile(pg_hba)
new_config = re.sub(
r'host\s*replication\s*{}.*'.format(name), '', old_config).strip()
mw.writeFile(pg_hba, new_config)
psdb.where("username=?", (args['username'],)).delete()
return mw.returnJson(True, '删除成功!')
def installPreInspection(version):
return 'ok'
@ -1180,5 +1472,29 @@ if __name__ == "__main__":
print(deleteDbBackup())
elif func == 'sync_get_databases':
print(syncGetDatabases())
elif func == 'add_slave_ssh':
print(addSlaveSSH(version))
elif func == 'get_slave_list':
print(getSlaveList(version))
elif func == 'del_slave_ssh':
print(delSlaveSSH(version))
elif func == 'update_slave_ssh':
print(updateSlaveSSH(version))
elif func == 'get_slave_ssh_list':
print(getSlaveSSHList(version))
elif func == 'get_slave_ssh_by_ip':
print(getSlaveSSHByIp(version))
elif func == 'get_master_status':
print(getMasterStatus(version))
elif func == 'set_master_status':
print(setMasterStatus(version))
elif func == 'set_slave_status':
print(setSlaveStatus(version))
elif func == 'get_master_rep_slave_list':
print(getMasterRepSlaveList(version))
elif func == 'add_master_rep_slave_user':
print(addMasterRepSlaveUser(version))
elif func == 'del_master_rep_slave_user':
print(delMasterRepSlaveUser(version))
else:
print('error')

@ -8,6 +8,17 @@ function str2Obj(str){
return data;
}
function randomStr(b) {
b = b || 32;
var c = "AaBbCcDdEeFfGHhiJjKkLMmNnPpRSrTsWtXwYxZyz";
var a = c.length;
var d = "";
for(i = 0; i < b; i++) {
d += c.charAt(Math.floor(Math.random() * a))
}
return d
}
function myPost(method,args,callback, title){
var _args = null;
@ -748,6 +759,10 @@ function setDbSlave(name){
}
function repeatMSPwd(a) {
$("#MyPassword").val(randomStr(a))
}
function addMasterRepSlaveUser(){
layer.open({
type: 1,
@ -760,8 +775,17 @@ function addMasterRepSlaveUser(){
content: "<form class='bt-form pd20' id='add_master'>\
<div class='line'><span class='tname'>用户名</span><div class='info-r'><input name='username' class='bt-input-text mr5' placeholder='' type='text' style='width:330px;' value='"+(randomStrPwd(6))+"'></div></div>\
<div class='line'>\
<span class='tname'>密码</span>\
<div class='info-r'><input class='bt-input-text mr5' type='text' name='password' id='MyPassword' style='width:330px' value='"+(randomStrPwd(16))+"' /><span title='随机密码' class='glyphicon glyphicon-repeat cursor' onclick='repeatPwd(16)'></span></div>\
<span class='tname'>密码</span>\
<div class='info-r'>\
<input class='bt-input-text mr5' type='text' name='password' id='MyPassword' style='width:330px' value='"+(randomStr(16))+"' />\
<span title='随机密码' class='glyphicon glyphicon-repeat cursor' onclick='repeatMSPwd(16)'></span>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>网段</span>\
<div class='info-r'>\
<input class='bt-input-text mr5' type='text' name='address' style='width:330px' value='127.0.0.1/32' />\
</div>\
</div>\
<input type='hidden' name='ps' value='' />\
</form>",
@ -1262,51 +1286,6 @@ function initSlaveStatus(){
function masterOrSlaveConf(version=''){
function getMasterDbList(){
var _data = {};
if (typeof(page) =='undefined'){
var page = 1;
}
_data['page'] = page;
_data['page_size'] = 10;
myPost('get_masterdb_list', _data, function(data){
var rdata = $.parseJSON(data.data);
var list = '';
for(i in rdata.data){
list += '<tr>';
list += '<td>' + rdata.data[i]['name'] +'</td>';
list += '<td>' + (rdata.data[i]['master']?'是':'否') +'</td>';
list += '<td style="text-align:right">' +
'<a href="javascript:;" class="btlink" onclick="setDbMaster(\''+rdata.data[i]['name']+'\')" title="加入或退出">'+(rdata.data[i]['master']?'退出':'加入')+'</a> | ' +
'<a href="javascript:;" class="btlink" onclick="getMasterRepSlaveUserCmd(\'\',\''+rdata.data[i]['name']+'\')" title="同步命令">同步命令</a>' +
'</td>';
list += '</tr>';
}
var con = '<div class="divtable mtb10">\
<div class="tablescroll">\
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
<thead><tr>\
<th>数据库名</th>\
<th>同步</th>\
<th style="text-align:right;">操作</th></tr></thead>\
<tbody>\
'+ list +'\
</tbody></table>\
</div>\
<div id="databasePage" class="dataTables_paginate paging_bootstrap page"></div>\
<div class="table_toolbar" style="left:0px;">\
<span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveListPage()" title="">同步账户列表</span>\
</div>\
</div>';
$(".table_master_list").html(con);
$('#databasePage').html(rdata.page);
});
}
function getAsyncMasterDbList(){
var _data = {};
if (typeof(page) =='undefined'){
@ -1367,51 +1346,6 @@ function masterOrSlaveConf(version=''){
});
}
function getAsyncDataList(){
var _data = {};
if (typeof(page) =='undefined'){
var page = 1;
}
_data['page'] = page;
_data['page_size'] = 10;
myPost('get_masterdb_list', _data, function(data){
var rdata = $.parseJSON(data.data);
var list = '';
for(i in rdata.data){
list += '<tr>';
list += '<td>' + rdata.data[i]['name'] +'</td>';
list += '<td style="text-align:right">' +
'<a href="javascript:;" class="btlink" onclick="setDbSlave(\''+rdata.data[i]['name']+'\')" title="加入|退出">'+(rdata.data[i]['slave']?'退出':'加入')+'</a> | ' +
'<a href="javascript:;" class="btlink" onclick="getFullSyncStatus(\''+rdata.data[i]['name']+'\')" title="同步">同步</a>' +
'</td>';
list += '</tr>';
}
var con = '<div class="divtable mtb10">\
<div class="tablescroll">\
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
<thead><tr>\
<th>本地库名</th>\
<th style="text-align:right;">操作</th></tr></thead>\
<tbody>\
'+ list +'\
</tbody></table>\
</div>\
<div id="databasePage" class="dataTables_paginate paging_bootstrap page"></div>\
<div class="table_toolbar" style="left:0px;">\
<span class="sync btn btn-default btn-sm" onclick="handlerRun()" title="免登录设置后,需要手动执行一下!">手动命令</span>\
<span class="sync btn btn-default btn-sm" onclick="getFullSyncStatus(\'ALL\')" title="全量同步">全量同步</span>\
</div>\
</div>';
$(".table_slave_list").html(con);
$('#databasePage').html(rdata.page);
});
}
function getMasterStatus(){
myPost('get_master_status', '', function(data){
var rdata = $.parseJSON(data.data);
@ -1421,17 +1355,14 @@ function masterOrSlaveConf(version=''){
<p class="conf_p">\
<span class="f14 c6 mr20">主从同步模式</span><span class="f14 c6 mr20"></span>\
<button class="btn '+(!(rdata.mode == "classic") ? 'btn-danger' : 'btn-success')+' btn-xs db-mode btn-classic">经典</button>\
<button class="btn '+(!(rdata.mode == "gtid") ? 'btn-danger' : 'btn-success')+' btn-xs db-mode btn-gtid">GTID</button>\
</p>\
<hr/>\
<p class="conf_p">\
<span class="f14 c6 mr20">Master[]配置</span><span class="f14 c6 mr20"></span>\
<button class="btn '+(!rdata.status ? 'btn-danger' : 'btn-success')+' btn-xs btn-master">'+(!rdata.status ? '未开启' : '已开启') +'</button>\
<button class="btn btn-success btn-xs" onclick="getMasterRepSlaveListPage()" >同步账户</button>\
</p>\
<hr/>\
<!-- master list -->\
<div class="safe bgw table_master_list"></div>\
<hr/>\
<!-- class="conf_p" -->\
<p class="conf_p">\
<span class="f14 c6 mr20">Slave[]配置</span><span class="f14 c6 mr20"></span>\
@ -1467,52 +1398,9 @@ function masterOrSlaveConf(version=''){
}, 3000);
});
});
$('.db-mode').click(function(){
if ($(this).hasClass('btn-success')){
//no action
return;
}
var mode = 'classic';
if ($(this).hasClass('btn-gtid')){
mode = 'gtid';
}
layer.open({
type:1,
title:"MySQL主从模式切换",
shadeClose:false,
btnAlign: 'c',
btn: ['切换并重启', '切换不重启'],
yes: function(index, layero){
this.change(index,mode,"yes");
},
btn2: function(index, layero){
this.change(index,mode,"no");
return false;
},
change:function(index,mode,reload){
console.log(index,mode,reload);
myPost('set_dbrun_mode',{'mode':mode,'reload':reload},function(data){
layer.close(index);
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg ,function(){
getMasterStatus();
},{ icon: rdata.status ? 1 : 5 });
});
}
});
});
if (rdata.status){
getMasterDbList();
}
if (rdata.slave_status){
getAsyncMasterDbList();
getAsyncDataList()
}
});
}

Loading…
Cancel
Save