Compare commits

...
67 Commits
Author SHA1 Message Date
Mr Chen c17314b634 Merge pull request #434 from midoks/dev
MySQL,MariaDB数据库主从同步及多源同步
2023-08-10 11:32:24 +08:00
midoks 92a4b28fbf mariadb,mysql多源同步支持 2023-08-10 11:28:17 +08:00
midoks 733e4e81be update 2023-08-10 09:54:19 +08:00
midoks 8b99ae2515 update 2023-08-09 13:01:41 +08:00
midoks 2a72422402 update ad 2023-08-09 12:57:38 +08:00
midoks 145a259ef9 update 2023-08-09 08:57:49 +08:00
midoks a34b7719c8 update 2023-08-09 08:52:36 +08:00
midoks 4d98455c35 up 2023-08-08 21:17:49 +08:00
midoks c0c9def1fb Update index.py 2023-08-08 21:07:53 +08:00
midoks 02d8416466 update 2023-08-08 21:06:56 +08:00
midoks d505740269 Update index.py 2023-08-08 21:01:21 +08:00
midoks b1f6314df7 Update index.py 2023-08-08 16:54:17 +08:00
midoks 533653fe47 Update index.py 2023-08-08 16:48:21 +08:00
midoks dff4551fb1 update 2023-08-08 16:45:46 +08:00
midoks 79723149a5 Update install.sh 2023-08-08 16:32:07 +08:00
midoks 76da8a2ad5 Update install.sh 2023-08-08 16:25:40 +08:00
midoks 645538e633 Update index.py 2023-08-08 16:11:17 +08:00
midoks 1a1b4e9f3a Update index.py 2023-08-08 16:09:07 +08:00
midoks 3afde01f93 Update index.py 2023-08-08 16:07:44 +08:00
midoks a14db23971 Update index.py 2023-08-08 16:04:25 +08:00
midoks 316f06911b Update index.py 2023-08-08 16:01:34 +08:00
midoks 698d049314 up 2023-08-08 15:50:20 +08:00
midoks d397d74f73 Update index.py 2023-08-08 15:44:25 +08:00
midoks 8a7447a4ee Update mysql-apt.js 2023-08-08 15:37:45 +08:00
midoks 98414db41d update 2023-08-08 15:25:23 +08:00
midoks 7024185b59 Update my8.0.cnf 2023-08-08 14:52:57 +08:00
midoks 495f16f2cf Update mysql.js 2023-08-08 14:52:19 +08:00
midoks 62444b7b6d Update mysql.js 2023-08-08 14:51:59 +08:00
midoks 50b11457f7 update 2023-08-08 14:26:00 +08:00
midoks 393cbc3974 Merge branch 'master' into dev 2023-08-08 13:27:41 +08:00
midoks ce3bfc2ebd Merge branch 'master' into dev 2023-08-08 13:25:56 +08:00
Mr Chen b68e8f3650 Merge pull request #433 from midoks/midoks-patch-1
AD删除
2023-08-08 13:25:25 +08:00
midoks 08c83edeb9 update 2023-08-08 13:17:39 +08:00
midoks c114e9aca8 Update index.py 2023-08-07 22:50:52 +08:00
midoks c5123268d0 Update index.py 2023-08-07 22:48:34 +08:00
midoks 54bbc3ff57 Update index.py 2023-08-07 22:39:24 +08:00
midoks 4658b991a9 Update index.py 2023-08-07 22:15:14 +08:00
midoks 52145e710b Update index.py 2023-08-07 22:05:01 +08:00
midoks 4acc48ded2 Update index.py 2023-08-07 22:04:00 +08:00
midoks 0d858f87ea Update index.py 2023-08-07 22:03:00 +08:00
midoks f37f08a9f5 Update index.py 2023-08-07 21:53:28 +08:00
midoks 0b5fc1de55 Update index.py 2023-08-07 21:52:53 +08:00
midoks 8748777101 Update index.py 2023-08-07 21:47:03 +08:00
midoks 48ed0b8034 Update index.py 2023-08-07 21:44:32 +08:00
midoks 41c9f6b7b6 Update mariadb.js 2023-08-07 21:24:42 +08:00
midoks c47336b65f update 2023-08-07 21:22:29 +08:00
midoks 24d7204ccc Update index.py 2023-08-07 21:05:22 +08:00
midoks 8c00fb8859 Update index.py 2023-08-07 20:42:08 +08:00
midoks c573b28f16 Update index.py 2023-08-07 20:41:11 +08:00
midoks afad548cf9 Update index.py 2023-08-07 20:22:26 +08:00
midoks 2605849de2 Update index.py 2023-08-07 20:02:18 +08:00
midoks 77598fa8a7 Update index.py 2023-08-07 19:44:47 +08:00
midoks b175ab735d Update index.py 2023-08-07 19:34:57 +08:00
midoks 54ed17ab8f Update index.py 2023-08-07 19:32:47 +08:00
midoks cadf72ea69 Update index.py 2023-08-07 17:53:33 +08:00
midoks dadb737f88 update 2023-08-07 15:59:00 +08:00
midoks fef2acf683 Update index.py 2023-08-07 15:56:59 +08:00
midoks 4b6dbf8887 Update mariadb.js 2023-08-07 15:46:10 +08:00
midoks 06c99e1cc1 Update index.py 2023-08-07 15:26:54 +08:00
midoks afb3e920f8 mariadb多住一从功能 2023-08-07 15:10:38 +08:00
midoks 2c346c9850 Update my.cnf 2023-08-06 14:38:47 +08:00
midoks 5b6c5be288 Update mariadb.js 2023-08-06 00:43:24 +08:00
midoks 63bd806222 Update index.py 2023-08-06 00:25:22 +08:00
midoks afe3848b71 update 2023-08-05 21:34:04 +08:00
midoks cd361ec7ec Update install.sh 2023-08-05 21:33:14 +08:00
midoks eb399fe799 Update install.sh 2023-08-05 21:32:25 +08:00
midoks 53e588b6fc Update install.sh 2023-08-05 21:31:58 +08:00
30 changed files with 1687 additions and 1028 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ from flask import request
class config_api: class config_api:
__version = '0.15.2' __version = '0.15.3'
__api_addr = 'data/api.json' __api_addr = 'data/api.json'
def __init__(self): def __init__(self):
+1
View File
@@ -65,6 +65,7 @@ binlog-ignore-db = performance_schema
#slave #slave
log-slave-updates log-slave-updates
slave_skip_errors=1062
#replicate-do-db #replicate-do-db
replicate-ignore-db = information_schema replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema replicate-ignore-db = performance_schema
+2 -7
View File
@@ -2,7 +2,7 @@
<div class='plugin_version'></div> <div class='plugin_version'></div>
<div class="bt-w-main"> <div class="bt-w-main">
<div class="bt-w-menu"> <div class="bt-w-menu">
<script type="text/javascript">console.log($('.plugin_version').attr('version'));</script> <!-- <script type="text/javascript">console.log($('.plugin_version').attr('version'));</script> -->
<p class="bgw" onclick="pluginService('mariadb', $('.plugin_version').attr('version'));">服务</p> <p class="bgw" onclick="pluginService('mariadb', $('.plugin_version').attr('version'));">服务</p>
<p onclick="pluginInitD('mariadb', $('.plugin_version').attr('version'));">自启动</p> <p onclick="pluginInitD('mariadb', $('.plugin_version').attr('version'));">自启动</p>
<p onclick="pluginConfig('mariadb', $('.plugin_version').attr('version'));">配置文件</p> <p onclick="pluginConfig('mariadb', $('.plugin_version').attr('version'));">配置文件</p>
@@ -15,14 +15,12 @@
<p onclick="dbList()">管理列表</p> <p onclick="dbList()">管理列表</p>
<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p> <p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p>
</div> </div>
<div class="bt-w-con pd15"> <div class="bt-w-con pd15" style="height:555px;overflow: auto;">
<div class="soft-man-con"></div> <div class="soft-man-con"></div>
</div> </div>
</div> </div>
</div> </div>
<style type="text/css"> <style type="text/css">
.db_list{ .db_list{
padding: 0px 0 10px 0; padding: 0px 0 10px 0;
@@ -52,10 +50,7 @@
</style> </style>
<script type="text/javascript"> <script type="text/javascript">
resetPluginWinWidth(800); resetPluginWinWidth(800);
var loading = layer.msg("资源加载中...", { icon: 16, time: 0, shade: 0.3 });
$.getScript( "/plugins/file?name=mariadb&f=js/mariadb.js", function(){ $.getScript( "/plugins/file?name=mariadb&f=js/mariadb.js", function(){
layer.close(loading);
pluginService('mariadb',$('.plugin_version').attr('version')); pluginService('mariadb',$('.plugin_version').attr('version'));
}); });
</script> </script>
+258 -203
View File
@@ -48,19 +48,19 @@ def getInitDFile():
def getArgs(): def getArgs():
args = sys.argv[2:] args = sys.argv[2:]
tmp = {} tmp = {}
args_len = len(args) args_len = len(args)
if args_len == 1: if args_len == 1:
t = args[0].strip('{').strip('}') t = args[0].strip('{').strip('}').strip()
if (t == ''):
return tmp
t = t.split(':') t = t.split(':')
tmp[t[0]] = t[1] tmp[t[0]] = t[1]
elif args_len > 1: elif args_len > 1:
for i in range(len(args)): for i in range(len(args)):
t = args[i].split(':') t = args[i].split(':')
tmp[t[0]] = t[1] tmp[t[0]] = t[1]
return tmp return tmp
@@ -107,6 +107,14 @@ def getDbPort():
return tmp.groups()[0].strip() return tmp.groups()[0].strip()
def getDbServerId():
file = getConf()
content = mw.readFile(file)
rep = 'server-id\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
def getSocketFile(): def getSocketFile():
file = getConf() file = getConf()
content = mw.readFile(file) content = mw.readFile(file)
@@ -664,15 +672,15 @@ def isSqlError(mysqlMsg):
return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)") return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
if "using password:" in mysqlMsg: if "using password:" in mysqlMsg:
return mw.returnJson(False, '数据库密码错误,在管理列表-点击【修复】!') return mw.returnJson(False, '数据库密码错误,在管理列表-点击【修复】!')
if "1045" in mysqlMsg: if "1045," in mysqlMsg:
return mw.returnJson(False, '连接错误!') return mw.returnJson(False, '连接错误!')
if "SQL syntax" in mysqlMsg: if "SQL syntax" in mysqlMsg:
return mw.returnJson(False, 'SQL语法错误!') return mw.returnJson(False, 'SQL语法错误!')
if "Connection refused" in mysqlMsg: if "Connection refused" in mysqlMsg:
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!') return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
if "1133" in mysqlMsg: if "1133," in mysqlMsg:
return mw.returnJson(False, '数据库用户不存在!') return mw.returnJson(False, '数据库用户不存在!')
if "1007" in mysqlMsg: if "1007," in mysqlMsg:
return mw.returnJson(False, '数据库已经存在!') return mw.returnJson(False, '数据库已经存在!')
return None return None
@@ -940,7 +948,7 @@ def getDbList():
def syncGetDatabases(): def syncGetDatabases():
pdb = pMysqlDb() pdb = pMysqlDb('mysql')
psdb = pSqliteDb('databases') psdb = pSqliteDb('databases')
data = pdb.query('show databases') data = pdb.query('show databases')
isError = isSqlError(data) isError = isSqlError(data)
@@ -1706,12 +1714,14 @@ def getMasterStatus(version=''):
data['mode'] = recognizeDbMode() data['mode'] = recognizeDbMode()
data['status'] = master_status data['status'] = master_status
db = pMysqlDb() pdb = pMysqlDb('mysql')
dlist = db.query('show slave status') dlist = pdb.query('show slave status')
if len(dlist) < 1:
dlist = pdb.query("show all slaves status")
# print(dlist[0]) for v in dlist:
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'): if v["Slave_IO_Running"] == 'Yes' or v["Slave_SQL_Running"] == 'Yes':
data['slave_status'] = True data['slave_status'] = True
return mw.returnJson(master_status, '设置成功', data) return mw.returnJson(master_status, '设置成功', data)
except Exception as e: except Exception as e:
@@ -1855,6 +1865,9 @@ def getMasterRepSlaveUserCmd(version):
clist = psdb.field(f).where("username=?", (username,)).limit( clist = psdb.field(f).where("username=?", (username,)).limit(
'1').order('id desc').select() '1').order('id desc').select()
if len(clist) == 0:
return mw.returnJson(False, '错误同步账户!')
ip = mw.getLocalIp() ip = mw.getLocalIp()
port = getMyPort() port = getMyPort()
db = pMysqlDb() db = pMysqlDb()
@@ -1868,15 +1881,23 @@ def getMasterRepSlaveUserCmd(version):
# 查找同步点 # 查找同步点
# SELECT BINLOG_GTID_POS('master1-bin.000002', 561866201); # SELECT BINLOG_GTID_POS('master1-bin.000002', 561866201);
sid = getDbServerId()
connection_name = ""
if sid != '':
connection_name = "'r{}' ".format(sid)
# MASTER_USE_GTID={current_pos|slave_pos|no}
# current_pos 依赖-> select @@global.gtid_current_pos;
# slave_pos 依赖-> select @@global.gtid_slave_pos;
# no -> 啥都不依赖,保证多主同步成功。同步出现问题,根据日志查找问题。
if mode == "gtid": if mode == "gtid":
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ sql = "CHANGE MASTER " + connection_name + "TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \ clist[0]['username'] + "', MASTER_PASSWORD='" + \
clist[0]['password'] + "', " + "MASTER_LOG_FILE='" + mstatus[0]["File"] + \ clist[0]['password'] + "', " + "MASTER_LOG_FILE='" + mstatus[0]["File"] + \
"',MASTER_LOG_POS=" + \ "',MASTER_USE_GTID=no,MASTER_CONNECT_RETRY=10;"
str(mstatus[0]["Position"]) + \
",MASTER_USE_GTID=slave_pos,MASTER_CONNECT_RETRY=10;"
else: else:
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ sql = "CHANGE MASTER " + connection_name + "TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \ clist[0]['username'] + "', MASTER_PASSWORD='" + \
clist[0]['password'] + \ clist[0]['password'] + \
"', MASTER_LOG_FILE='" + mstatus[0]["File"] + \ "', MASTER_LOG_FILE='" + mstatus[0]["File"] + \
@@ -2151,48 +2172,20 @@ def updateSlaveSSH(version=''):
def getSlaveList(version=''): def getSlaveList(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
ret = [] if len(dlist) == 0:
for x in range(0, len(dlist)): dlist = db.query('show all slaves status')
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"]
tmp['Last_Error'] = dlist[x]["Last_Error"]
tmp['Last_IO_Error'] = dlist[x]["Last_IO_Error"]
tmp['Last_SQL_Error'] = dlist[x]["Last_SQL_Error"]
tmp['Slave_SQL_Running_State'] = dlist[x]["Slave_SQL_Running_State"]
tmp['Error'] = ''
if tmp['Last_Error'] != '':
tmp['Error'] = tmp['Last_Error']
if tmp['Last_IO_Error'] != '':
tmp['Error'] = tmp['Last_IO_Error']
if tmp['Last_SQL_Error'] != '':
tmp['Error'] = tmp['Last_SQL_Error']
if tmp['Error'] == '':
tmp['Error'] = tmp['Slave_SQL_Running_State']
ret.append(tmp)
data = {} data = {}
data['data'] = ret data['data'] = dlist
return mw.getJson(data) return mw.getJson(data)
def getSlaveSyncCmd(version=''): def getSlaveSyncCmd(version=''):
root = mw.getRunDir() root = mw.getRunDir()
cmd = 'cd ' + root + ' && python3 ' + root + \ cmd = 'cd ' + root + ' && python3 ' + root + \
'/plugins/mysql/index.py do_full_sync {"db":"all"}' '/plugins/mariadb/index.py do_full_sync {"db":"all","sign":""}'
return mw.returnJson(True, 'ok', cmd) return mw.returnJson(True, 'ok', cmd)
@@ -2208,102 +2201,151 @@ def initSlaveStatus(version=''):
return initSlaveStatusSyncUser(version) return initSlaveStatusSyncUser(version)
def parseSlaveSyncCmd(cmd):
a = {}
vlist = cmd.split(',')
has_connection_name = vlist[0]
pattern_c = r"CHANGE MASTER \'(.*)\' TO MASTER_HOST"
match_val = re.match(pattern_c, has_connection_name, re.I)
if match_val:
m_groups = match_val.groups()
a['Connection_name'] = m_groups[0]
for i in vlist:
tmp = i.strip()
tmp_a = tmp.split(" ")
real_tmp = tmp_a[len(tmp_a) - 1]
kv = real_tmp.split("=")
a[kv[0]] = kv[1].replace("'", '').replace("'", '').replace(";", '')
return a
def initSlaveStatusSyncUser(version=''): def initSlaveStatusSyncUser(version=''):
conn = pSqliteDb('slave_sync_user') conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) < 1: slave_data = conn.field('id,ip,port,user,pass,mode,cmd').select()
if len(slave_data) < 1:
return mw.returnJson(False, '需要先添加同步用户配置!') return mw.returnJson(False, '需要先添加同步用户配置!')
# print(data) pdb = pMysqlDb()
db = pMysqlDb() if len(slave_data) == 1:
dlist = db.query('show slave status') dlist = pdb.query('show slave status')
if len(dlist) > 0: if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...') return mw.returnJson(False, '已经初始化好了zz...')
u = data msg = ''
mode_name = 'classic' pdb.query("stop slave")
if u['mode'] == '1': pdb.query("stop all slaves")
mode_name = 'gtid'
local_mode = recognizeDbMode() local_mode = recognizeDbMode()
if local_mode != mode_name: for x in range(len(slave_data)):
return mw.returnJson(False, '同步模式不一致!') slave_t = slave_data[x]
base_t = 'IP:' + slave_t['ip'] + ",PORT:" + \
slave_t['port'] + ",USER:" + slave_t['user']
# print(u['cmd']) mode_name = 'classic'
t = db.query(u['cmd']) if slave_data[x]['mode'] == '1':
# print(t) mode_name = 'gtid'
db.query("start slave")
return mw.returnJson(True, '初始化成功!') if local_mode != mode_name:
msg += base_t + '->同步模式不一致'
continue
cmd_sql = slave_t['cmd']
if cmd_sql == '':
msg += base_t + '->同步命令不能为空'
continue
try:
pinfo = parseSlaveSyncCmd(cmd_sql)
except Exception as e:
return mw.returnJson(False, base_t + '->CMD同步命令不合规范!')
pdb.query(cmd_sql)
pdb.query("start slave")
pdb.query("start all slaves ")
if msg == '':
msg = '初始化成功!'
return mw.returnJson(True, msg)
def initSlaveStatusSSH(version=''): def initSlaveStatusSSH(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
if len(dlist) > 0: if len(dlist) == 0:
return mw.returnJson(False, '已经初始化好了zz...') dlist = db.query('show all slaves status')
conn = pSqliteDb('slave_id_rsa') conn = pSqliteDb('slave_id_rsa')
data = conn.field('ip,port,id_rsa,db_user').find() ssh_list = conn.field('ip,port,id_rsa,db_user').select()
if len(data) < 1: if len(ssh_list) < 1:
return mw.returnJson(False, '需要先配置【[主]SSH配置】!') return mw.returnJson(False, '需要先配置【[主]SSH配置】!')
SSH_PRIVATE_KEY = "/tmp/t_ssh.txt"
ip = data['ip']
master_port = data['port']
mw.writeFile(SSH_PRIVATE_KEY, data['id_rsa'].replace('\\n', '\n'))
import paramiko import paramiko
paramiko.util.log_to_file('paramiko.log') paramiko.util.log_to_file('paramiko.log')
ssh = paramiko.SSHClient() ssh = paramiko.SSHClient()
try: for data in ssh_list:
ip = data['ip']
master_port = data['port']
SSH_PRIVATE_KEY = "/tmp/t_ssh_" + ip + ".txt"
mw.writeFile(SSH_PRIVATE_KEY, data['id_rsa'].replace('\\n', '\n'))
mw.execShell("chmod 600 " + SSH_PRIVATE_KEY) mw.execShell("chmod 600 " + SSH_PRIVATE_KEY)
key = paramiko.RSAKey.from_private_key_file(SSH_PRIVATE_KEY)
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=ip, port=int(master_port),
username='root', pkey=key)
db_user = data['db_user'] try:
cmd = 'cd /www/server/mdserver-web && python3 plugins/mariadb/index.py get_master_rep_slave_user_cmd {"username":"' + db_user + '","db":""}' key = paramiko.RSAKey.from_private_key_file(SSH_PRIVATE_KEY)
stdin, stdout, stderr = ssh.exec_command(cmd) ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
result = stdout.read() ssh.connect(hostname=ip, port=int(master_port),
result = result.decode('utf-8') username='root', pkey=key)
cmd_data = json.loads(result)
time.sleep(1)
ssh.close()
if not cmd_data['status']:
return mw.returnJson(False, '[主]:' + cmd_data['msg'])
local_mode = recognizeDbMode() db_user = data['db_user']
if local_mode != cmd_data['data']['mode']: cmd = 'cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py get_master_rep_slave_user_cmd {"username":"' + db_user + '","db":""}'
return mw.returnJson(False, '主【{}】从【{}】,运行模式不一致!'.format(cmd_data['data']['mode'], local_mode)) stdin, stdout, stderr = ssh.exec_command(cmd)
result = stdout.read()
result = result.decode('utf-8')
if result.strip() == "":
return mw.returnJson(False, '[主][' + ip + ']:获取同步命令失败!')
cmd_data = json.loads(result)
time.sleep(1)
ssh.close()
if not cmd_data['status']:
return mw.returnJson(False, '[主][' + ip + ']:' + cmd_data['msg'])
u = cmd_data['data']['info'] local_mode = recognizeDbMode()
ps = u['username'] + "|" + u['password'] if local_mode != cmd_data['data']['mode']:
conn.where('ip=?', (ip,)).setField('ps', ps) return mw.returnJson(False, '[主][' + ip + ']【{}】从【{}】,运行模式不一致!'.format(cmd_data['data']['mode'], local_mode))
db.query('stop slave')
# 保证同步IP一致 u = cmd_data['data']['info']
cmd = cmd_data['data']['cmd'] ps = u['username'] + "|" + u['password']
if cmd.find('SOURCE_HOST') > -1: conn.where('ip=?', (ip,)).setField('ps', ps)
cmd = re.sub(r"SOURCE_HOST='(.*?)'", db.query('stop slave')
"SOURCE_HOST='" + ip + "'", cmd, 1) db.query('stop all slaves')
if cmd.find('MASTER_HOST') > -1: # 保证同步IP一致
cmd = re.sub(r"MASTER_HOST='(.*?)'", cmd = cmd_data['data']['cmd']
"MASTER_HOST='" + ip + "'", cmd, 1) if cmd.find('SOURCE_HOST') > -1:
cmd = re.sub(r"SOURCE_HOST='(.*?)'",
"SOURCE_HOST='" + ip + "'", cmd, 1)
# print(cmd) if cmd.find('MASTER_HOST') > -1:
db.query(cmd) cmd = re.sub(r"MASTER_HOST='(.*?)'",
db.query("start slave") "MASTER_HOST='" + ip + "'", cmd, 1)
except Exception as e: # print(cmd)
return mw.returnJson(False, 'SSH认证配置连接失败!' + str(e)) db.query(cmd)
db.query("start slave")
db.query("start all slaves")
if os.path.exists(SSH_PRIVATE_KEY):
os.system("rm -rf " + SSH_PRIVATE_KEY)
except Exception as e:
return mw.returnJson(False, '[主][' + ip + ']:SSH认证配置连接失败!' + str(e))
os.system("rm -rf " + SSH_PRIVATE_KEY)
return mw.returnJson(True, '初始化成功!') return mw.returnJson(True, '初始化成功!')
@@ -2312,61 +2354,39 @@ def setSlaveStatus(version=''):
if not os.path.exists(mode_file): if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置') return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file) pdb = pMysqlDb()
if mode == 'ssh': dlist = pdb.query('show slave status')
return setSlaveStatusSSH(version) if len(dlist) == 0:
if mode == 'sync-user': dlist = pdb.query('show all slaves status')
return setSlaveStatusSyncUser(version)
def setSlaveStatusSyncUser(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0: if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!') return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'): for v in dlist:
db.query('stop slave') connection_name = ''
else: cmd = "slave"
ip = dlist[0]['Master_Host'] if 'Connection_name' in v:
conn = pSqliteDb('slave_sync_user') connection_name = v['Connection_name']
data = conn.field('ip,port,user,pass,mode,cmd').find() cmd = "slave '{}'".format(connection_name)
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
user = data['user']
apass = data['pass']
db.query("start slave") if (v["Slave_IO_Running"] == 'Yes' or v["Slave_SQL_Running"] == 'Yes'):
# db.query("start slave user='{}' password='{}';".format(user, apass)) pdb.query("stop {}".format(cmd))
else:
return mw.returnJson(True, '设置成功!') pdb.query("start {}".format(cmd))
def setSlaveStatusSSH(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加主服务命令或者执行[初始化]!')
# print(dlist)
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
db.query('stop slave')
else:
ip = dlist[0]['Master_Host']
conn = pSqliteDb('slave_id_rsa')
data = conn.field('ip,ps').where("ip=?", (ip,)).find()
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
db.query("start slave")
return mw.returnJson(True, '设置成功!') return mw.returnJson(True, '设置成功!')
def deleteSlave(version=''): def deleteSlave(version=''):
args = getArgs()
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('stop slave') if 'sign' in args:
dlist = db.query('reset slave all') sign = args['sign']
db.query("stop slave '{}'".format(sign))
db.query("reset slave '{}' all".format(sign))
else:
db.query('stop slave')
db.query('reset slave all')
return mw.returnJson(True, '删除成功!') return mw.returnJson(True, '删除成功!')
@@ -2402,14 +2422,54 @@ def dumpMysqlData(version=''):
############### --- 重要 同步---- ########### ############### --- 重要 同步---- ###########
def asyncTmpfile():
path = '/tmp/mariadb_async_status.txt'
return path
def writeDbSyncStatus(data): def writeDbSyncStatus(data):
path = '/tmp/db_async_status.txt' path = asyncTmpfile()
# status_data['code'] = 1
# status_data['msg'] = '主服务器备份完成...'
# status_data['progress'] = 30
mw.writeFile(path, json.dumps(data)) mw.writeFile(path, json.dumps(data))
def fullSync(version=''):
args = getArgs()
data = checkArgs(args, ['db', 'begin'])
if not data[0]:
return data[1]
sign = ''
if 'sign' in args:
sign = args['sign']
status_file = asyncTmpfile()
if args['begin'] == '1':
cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + \
getPluginDir() + \
'/index.py do_full_sync {"db":"' + \
args['db'] + '","sign":"' + sign + '"} &'
# print(cmd)
mw.execShell(cmd)
return json.dumps({'code': 0, 'msg': '同步数据中!', 'progress': 0})
if os.path.exists(status_file):
c = mw.readFile(status_file)
tmp = json.loads(c)
if tmp['code'] == 1:
sys_dump_sql = "/tmp/dump.sql"
if os.path.exists(sys_dump_sql):
dump_size = os.path.getsize(sys_dump_sql)
tmp['msg'] = tmp['msg'] + ":" + "同步文件:" + mw.toSize(dump_size)
c = json.dumps(tmp)
# if tmp['code'] == 6:
# os.remove(status_file)
return c
return json.dumps({'code': 0, 'msg': '点击开始,开始同步!', 'progress': 0})
def doFullSync(version=''): def doFullSync(version=''):
mode_file = getSyncModeFile() mode_file = getSyncModeFile()
if not os.path.exists(mode_file): if not os.path.exists(mode_file):
@@ -2424,16 +2484,23 @@ def doFullSync(version=''):
def doFullSyncUser(version=''): def doFullSyncUser(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['db']) data = checkArgs(args, ['db', 'sign'])
if not data[0]: if not data[0]:
return data[1] return data[1]
sync_db = args['db'] sync_db = args['db']
sync_sign = args['sign']
# print(sync_sign, sync_db)
db = pMysqlDb() db = pMysqlDb()
conn = pSqliteDb('slave_sync_user') conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find() if sync_sign != '':
data = conn.field('ip,port,user,pass,mode,cmd').where(
'ip=?', (sync_sign,)).find()
else:
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user'] user = data['user']
apass = data['pass'] apass = data['pass']
port = data['port'] port = data['port']
@@ -2442,12 +2509,12 @@ def doFullSyncUser(version=''):
bak_file = '/tmp/tmp.sql' bak_file = '/tmp/tmp.sql'
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0}) writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 10})
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
if not os.path.exists(bak_file): if not os.path.exists(bak_file):
dump_sql_data = getServerDir() + "/bin/mysqldump --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \ dump_sql_data = getServerDir() + "/bin/mysqldump --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file
print(dump_sql_data)
mw.execShell(dump_sql_data) mw.execShell(dump_sql_data)
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40}) writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
@@ -2458,24 +2525,38 @@ def doFullSyncUser(version=''):
' ' + sync_db + ' < ' + bak_file ' ' + sync_db + ' < ' + bak_file
mw.execShell(my_import_cmd) mw.execShell(my_import_cmd)
db.query("start slave") pinfo = parseSlaveSyncCmd(data['cmd'])
# print(pinfo)
if 'Connection_name' in pinfo:
db.query("start slave '{}'".format(pinfo['Connection_name']))
else:
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100}) writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + bak_file)
if os.path.exists(bak_file):
os.system("rm -rf " + bak_file)
return True return True
def doFullSyncSSH(version=''): def doFullSyncSSH(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['db']) data = checkArgs(args, ['db', 'sign'])
if not data[0]: if not data[0]:
return data[1] return data[1]
db = pMysqlDb() db = pMysqlDb()
sync_db = args['db']
sync_sign = args['sign']
id_rsa_conn = pSqliteDb('slave_id_rsa') id_rsa_conn = pSqliteDb('slave_id_rsa')
data = id_rsa_conn.field('ip,port,db_user,id_rsa').find() if sync_sign != '':
data = id_rsa_conn.field('ip,port,db_user,id_rsa').where(
'ip=?', (sync_sign,)).find()
else:
data = id_rsa_conn.field('ip,port,db_user,id_rsa').find()
SSH_PRIVATE_KEY = "/tmp/mysql_sync_id_rsa.txt" SSH_PRIVATE_KEY = "/tmp/mysql_sync_id_rsa.txt"
id_rsa = data['id_rsa'].replace('\\n', '\n') id_rsa = data['id_rsa'].replace('\\n', '\n')
@@ -2498,7 +2579,6 @@ def doFullSyncSSH(version=''):
return 'fail' return 'fail'
try: try:
# ssh.load_system_host_keys()
mw.execShell("chmod 600 " + SSH_PRIVATE_KEY) mw.execShell("chmod 600 " + SSH_PRIVATE_KEY)
key = paramiko.RSAKey.from_private_key_file(SSH_PRIVATE_KEY) key = paramiko.RSAKey.from_private_key_file(SSH_PRIVATE_KEY)
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
@@ -2516,7 +2596,7 @@ def doFullSyncSSH(version=''):
writeDbSyncStatus({'code': 0, 'msg': '登录Master成功...', 'progress': 5}) writeDbSyncStatus({'code': 0, 'msg': '登录Master成功...', 'progress': 5})
dbname = args['db'] dbname = args['db']
cmd = "cd /www/server/mdserver-web && python3 plugins/mariadb/index.py dump_mysql_data {\"db\":'" + dbname + "'}" cmd = "cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py dump_mysql_data {\"db\":'" + dbname + "'}"
print(cmd) print(cmd)
stdin, stdout, stderr = ssh.exec_command(cmd) stdin, stdout, stderr = ssh.exec_command(cmd)
result = stdout.read() result = stdout.read()
@@ -2539,13 +2619,19 @@ def doFullSyncSSH(version=''):
if copy_status == None: if copy_status == None:
writeDbSyncStatus({'code': 2, 'msg': '数据同步本地完成...', 'progress': 40}) writeDbSyncStatus({'code': 2, 'msg': '数据同步本地完成...', 'progress': 40})
cmd = 'cd /www/server/mdserver-web && python3 plugins/mariadb/index.py get_master_rep_slave_user_cmd {"username":"' + db_user + '","db":""}' cmd = 'cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py get_master_rep_slave_user_cmd {"username":"' + db_user + '","db":""}'
stdin, stdout, stderr = ssh.exec_command(cmd) stdin, stdout, stderr = ssh.exec_command(cmd)
result = stdout.read() result = stdout.read()
result = result.decode('utf-8') result = result.decode('utf-8')
if result == '':
writeDbSyncStatus({'code': 1, 'msg': '同步命令获取失败!', 'progress': 100})
return 'fail'
cmd_data = json.loads(result) cmd_data = json.loads(result)
db.query('stop slave') db.query('stop slave')
db.query('stop all slaves')
writeDbSyncStatus({'code': 3, 'msg': '停止从库完成...', 'progress': 45}) writeDbSyncStatus({'code': 3, 'msg': '停止从库完成...', 'progress': 45})
cmd = cmd_data['data']['cmd'] cmd = cmd_data['data']['cmd']
@@ -2578,6 +2664,7 @@ def doFullSyncSSH(version=''):
return 'fail' return 'fail'
db.query("start slave") db.query("start slave")
db.query("start all slaves")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100}) writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + SSH_PRIVATE_KEY) os.system("rm -rf " + SSH_PRIVATE_KEY)
@@ -2585,38 +2672,6 @@ def doFullSyncSSH(version=''):
return True return True
def fullSync(version=''):
args = getArgs()
data = checkArgs(args, ['db', 'begin'])
if not data[0]:
return data[1]
status_file = '/tmp/db_async_status.txt'
if args['begin'] == '1':
cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + \
getPluginDir() + \
'/index.py do_full_sync {"db":"' + args['db'] + '"} &'
print(cmd)
mw.execShell(cmd)
return json.dumps({'code': 0, 'msg': '同步数据中!', 'progress': 0})
if os.path.exists(status_file):
c = mw.readFile(status_file)
tmp = json.loads(c)
if tmp['code'] == 1:
sys_dump_sql = "/tmp/dump.sql"
if os.path.exists(sys_dump_sql):
dump_size = os.path.getsize(sys_dump_sql)
tmp['msg'] = tmp['msg'] + ":" + "同步文件:" + mw.toSize(dump_size)
c = json.dumps(tmp)
# if tmp['code'] == 6:
# os.remove(status_file)
return c
return json.dumps({'code': 0, 'msg': '点击开始,开始同步!', 'progress': 0})
def installPreInspection(version): def installPreInspection(version):
swap_path = mw.getServerDir() + "/swap" swap_path = mw.getServerDir() + "/swap"
if not os.path.exists(swap_path): if not os.path.exists(swap_path):
+178 -86
View File
@@ -431,7 +431,6 @@ function syncToDatabase(type){
var postData = 'type='+type+'&ids='+JSON.stringify(data); var postData = 'type='+type+'&ids='+JSON.stringify(data);
myPost('sync_to_databases', postData, function(data){ myPost('sync_to_databases', postData, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
// console.log(rdata);
showMsg(rdata.msg,function(){ showMsg(rdata.msg,function(){
dbList(); dbList();
},{ icon: rdata.status ? 1 : 2 }); },{ icon: rdata.status ? 1 : 2 });
@@ -439,18 +438,6 @@ function syncToDatabase(type){
} }
function setRootPwd(type, pwd){ function setRootPwd(type, pwd){
if (type==1){
var password = $("#MyPassword").val();
myPost('set_root_pwd', {password:password}, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
dbList();
$('.layui-layer-close1').click();
},{icon: rdata.status ? 1 : 2});
});
return;
}
var index = layer.open({ var index = layer.open({
type: 1, type: 1,
area: '500px', area: '500px',
@@ -467,8 +454,16 @@ function setRootPwd(type, pwd){
</div>\ </div>\
</div>\ </div>\
</form>", </form>",
yes:function(){ yes:function(index,layero){
setRootPwd(1); var password = $("#MyPassword").val();
myPost('set_root_pwd', {password:password}, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
dbList();
layer.close(index);
},{icon: rdata.status ? 1 : 2});
});
return;
} }
}); });
} }
@@ -490,7 +485,6 @@ function showHidePass(obj){
function checkSelect(){ function checkSelect(){
setTimeout(function () { setTimeout(function () {
var num = $('input[type="checkbox"].check:checked').length; var num = $('input[type="checkbox"].check:checked').length;
// console.log(num);
if (num == 1) { if (num == 1) {
$('button[batch="true"]').hide(); $('button[batch="true"]').hide();
$('button[batch="false"]').show(); $('button[batch="false"]').show();
@@ -861,7 +855,6 @@ function downloadBackup(file){
function importBackup(file,name){ function importBackup(file,name){
myPost('import_db_backup',{file:file,name:name}, function(data){ myPost('import_db_backup',{file:file,name:name}, function(data){
// console.log(data);
layer.msg('执行成功!'); layer.msg('执行成功!');
}); });
} }
@@ -1619,7 +1612,6 @@ function getMasterRepSlaveList(){
_data['page'] = page; _data['page'] = page;
_data['page_size'] = 10; _data['page_size'] = 10;
myPost('get_master_rep_slave_list', _data, function(data){ myPost('get_master_rep_slave_list', _data, function(data){
// console.log(data);
var rdata = []; var rdata = [];
try { try {
rdata = $.parseJSON(data.data); rdata = $.parseJSON(data.data);
@@ -1672,12 +1664,12 @@ function getMasterRepSlaveListPage(){
} }
function deleteSlave(){ function deleteSlave(sign = ''){
myPost('delete_slave', {}, function(data){ myPost('delete_slave', {sign:sign}, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
showMsg(rdata['msg'], function(){ showMsg(rdata['msg'], function(){
masterOrSlaveConf(); masterOrSlaveConf();
},{},3000); },{icon:rdata.status?1:2,time:3000},3000);
}); });
} }
@@ -1685,28 +1677,72 @@ function deleteSlave(){
function getFullSyncStatus(db){ function getFullSyncStatus(db){
var timeId = null; var timeId = null;
var btn = '<div class="table_toolbar" style="left:0px;"><span data-status="init" class="sync btn btn-default btn-sm" id="begin_full_sync" title="">开始</span></div>'; myPost('get_slave_list', {page:1,page_size:100}, function(data){
var loadOpen = layer.open({ var rdata = $.parseJSON(data.data);
type: 1, var rsource = rdata.data;
title: '全量同步['+db+']',
area: '500px', if (db == 'ALL' && rsource.length>1){
content:"<div class='bt-form pd20 c6'>\ layer.msg("多主不支持该模式!",{icon:2});
<div class='divtable mtb10'>\ return;
<span id='full_msg'></span>\
<div class='progress'>\
<div class='progress-bar' role='progressbar' aria-valuenow='0' aria-valuemin='0' aria-valuemax='100' style='min-width: 2em;'>0%</div>\
</div>\
</div>\
"+btn+"\
</div>",
cancel: function(){
clearInterval(timeId);
} }
var dataSource = '';
if (rsource.length>1){
var sourceList = '';
for (var i = 0; i < rsource.length; i++) {
if ('Connection_name' in rsource[i]){
sourceList += '<option val="'+rsource[i]['Master_Host']+'">'+rsource[i]['Master_Host']+'</option>';
}
}
dataSource = "<p class='line' style='text-align:center;'>\
<span>同步数据源:</span>\
<select class='bt-input-text' name='data_source' style='width:200px;'>" + sourceList + "</select>\
</p>";
}
layer.open({
type: 1,
title: '全量同步['+db+']',
area: '500px',
content:"<div class='bt-form pd15'>\
<div class='divtable mtb10'>\
"+dataSource+"\
<span id='full_msg'></span>\
<div class='progress'>\
<div class='progress-bar' role='progressbar' aria-valuenow='0' aria-valuemin='0' aria-valuemax='100' style='min-width: 2em;'>0%</div>\
</div>\
</div>\
<div class='table_toolbar' style='left:0px;'>\
<span data-status='init' class='sync btn btn-default btn-sm' id='begin_full_sync'>开始</span>\
</div>\
</div>",
cancel: function(){
clearInterval(timeId);
},
success:function(){
$('#begin_full_sync').click(function(){
var val = $(this).data('status');
var sign = '';
if (dataSource !=''){
sign = $('select[name="data_source"]').val();
}
if (val == 'init'){
fullSync(db, sign, 1);
timeId = setInterval(function(){
fullSync(db,sign,0);
}, 1000);
$(this).data('status','starting');
} else {
layer.msg("正在同步中..",{icon:0});
}
});
}
});
}); });
function fullSync(db,begin){ function fullSync(db, sign, begin){
myPostN('full_sync', {db:db,sign:sign,begin:begin}, function(data){
myPostN('full_sync', {db:db,begin:begin}, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
$('#full_msg').text(rdata['msg']); $('#full_msg').text(rdata['msg']);
$('.progress-bar').css('width',rdata['progress']+'%'); $('.progress-bar').css('width',rdata['progress']+'%');
@@ -1715,23 +1751,11 @@ function getFullSyncStatus(db){
if (rdata['code']==6 ||rdata['code']<0){ if (rdata['code']==6 ||rdata['code']<0){
layer.msg(rdata['msg']); layer.msg(rdata['msg']);
clearInterval(timeId); clearInterval(timeId);
$("#begin_full_sync").attr('data-status','init'); $("#begin_full_sync").data('status','init');
} }
}); });
} }
$('#begin_full_sync').click(function(){
var val = $(this).attr('data-status');
if (val == 'init'){
fullSync(db,1);
timeId = setInterval(function(){
fullSync(db,0);
}, 1000);
$(this).attr('data-status','starting');
} else {
layer.msg("正在同步中..");
}
});
} }
function addSlaveSSH(ip=''){ function addSlaveSSH(ip=''){
@@ -2077,7 +2101,7 @@ function getSlaveUserList(){
layerId = layer.open({ layerId = layer.open({
type: 1, type: 1,
title: '同步账户列表', title: '同步账户列表',
area: '500px', area: '600px',
content:"<div class='bt-form pd20 c6'>\ content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\ <div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\ <div><table class='table table-hover get-slave-ssh-list'>\
@@ -2101,7 +2125,7 @@ function getSlaveSSHList(page=1){
layerId = layer.open({ layerId = layer.open({
type: 1, type: 1,
title: 'SSH列表', title: 'SSH列表',
area: '500px', area: '600px',
content:"<div class='bt-form pd20 c6'>\ content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\ <div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\ <div><table class='table table-hover get-slave-ssh-list'>\
@@ -2142,11 +2166,15 @@ function handlerRun(){
function initSlaveStatus(){ function initSlaveStatus(){
myPost('init_slave_status', '', function(data){ myPost('init_slave_status', '', function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
if (!rdata.status ){
layer.msg(rdata.msg,{icon:2,time:10000});
return;
}
showMsg(rdata.msg,function(){ showMsg(rdata.msg,function(){
if (rdata.status){ if (rdata.status){
masterOrSlaveConf(); masterOrSlaveConf();
} }
},{icon:rdata.status?1:2},2000); },{icon:1},2000);
}); });
} }
@@ -2204,33 +2232,48 @@ function masterOrSlaveConf(version=''){
} }
_data['page'] = page; _data['page'] = page;
_data['page_size'] = 10; _data['page_size'] = 100;
myPost('get_slave_list', _data, function(data){ myPost('get_slave_list', _data, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
var list = ''; var list = '';
var isHasSign = false;
for(i in rdata.data){ for(i in rdata.data){
var v = rdata.data[i]; var v = rdata.data[i];
var status = "<a class='btlink db_error'>异常</>"; if ('Connection_name' in v){
isHasSign = true;
}
var status = "<a data-id="+i+" class='btlink db_error'>异常</>";
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){ if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
status = "正常"; status = "正常";
} }
list += '<tr>'; list += '<tr>';
list += '<td>' + rdata.data[i]['Master_Host'] +'</td>'; list += '<td>' + v['Master_Host'] +'</td>';
list += '<td>' + rdata.data[i]['Master_Port'] +'</td>'; list += '<td>' + v['Master_Port'] +'</td>';
list += '<td>' + rdata.data[i]['Master_User'] +'</td>'; list += '<td>' + v['Master_User'] +'</td>';
list += '<td>' + rdata.data[i]['Master_Log_File'] +'</td>'; list += '<td>' + v['Master_Log_File'] +'</td>';
list += '<td>' + rdata.data[i]['Slave_IO_Running'] +'</td>'; list += '<td>' + v['Slave_IO_Running'] +'</td>';
list += '<td>' + rdata.data[i]['Slave_SQL_Running'] +'</td>'; list += '<td>' + v['Slave_SQL_Running'] +'</td>';
if (isHasSign){
list += '<td>' + v['Connection_name'] +'</td>';
}
list += '<td>' + status +'</td>'; list += '<td>' + status +'</td>';
list += '<td style="text-align:right">' + list += '<td style="text-align:right">\
'<a href="javascript:;" class="btlink" onclick="deleteSlave()" title="删除">删除</a>' + <a data-id="'+i+'" href="javascript:;" class="btlink btn_delete_slave" title="删除">删除</a>\
'</td>'; </td>';
list += '</tr>'; list += '</tr>';
} }
var signThead_th = '';
if (isHasSign){
var signThead_th = '<th>标识</th>';
}
var con = '<div class="divtable mtb10">\ var con = '<div class="divtable mtb10">\
<div class="tablescroll">\ <div class="tablescroll">\
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\ <table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
@@ -2241,6 +2284,7 @@ function masterOrSlaveConf(version=''){
<th>日志</th>\ <th>日志</th>\
<th>IO</th>\ <th>IO</th>\
<th>SQL</th>\ <th>SQL</th>\
'+signThead_th+'\
<th>状态</th>\ <th>状态</th>\
<th style="text-align:right;">操作</th></tr></thead>\ <th style="text-align:right;">操作</th></tr></thead>\
<tbody>\ <tbody>\
@@ -2255,22 +2299,75 @@ function masterOrSlaveConf(version=''){
// </div> // </div>
$(".table_slave_status_list").html(con); $(".table_slave_status_list").html(con);
$(".btn_delete_slave").click(function(){
var id = $(this).data('id');
var v = rdata.data[id];
if ('Connection_name' in v){
deleteSlave(v['Connection_name']);
} else{
deleteSlave();
}
});
$('.db_error').click(function(){ $('.db_error').click(function(){
var id = $(this).data('id');
var info = rdata.data[id];
var err_line = "";
err_line +="<tr>\
<td>IO错误</td>\
<td>"+ (info['Last_IO_Error'] == '' ? '无':info['Last_IO_Error'])+"</td>\
</tr>";
err_line +="<tr>\
<td>SQL错误</td>\
<td>"+(info['Last_SQL_Error'] == '' ? '无':info['Last_SQL_Error'])+"</td>\
</tr>";
err_line +="<tr>\
<td>状态</td>\
<td>"+(info['Slave_SQL_Running_State'] == '' ? '无':info['Slave_SQL_Running_State']) +"</td>\
</tr>";
layer.open({ layer.open({
type: 1, type: 1,
title: '同步异常信息', title: '同步异常信息',
area: '500px', area: ['600px','300px'],
content:"<form class='bt-form pd20 pb70'>\ btn:['复制错误',"取消"],
<div class='line'>"+v['Error']+"</div>\ content:"<form class='bt-form pd15'>\
<div class='bt-form-submit-btn'>\ <div class='divtable mtb10'>\
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\ <div class='tablescroll'>\
<table class='table table-hover' width='100%' cellspacing='0' cellpadding='0' border='0' style='border: 0 none;'>\
<thead><tr>\
<th style='width:80px;'>类型</th>\
<th>内容</th>\
</tr></thead>\
<tbody>"+ err_line +"</tbody>\
</table>\
</div>\
</div>\ </div>\
</form>", </form>",
success:function(){ success:function(){
copyText(v['Error']); // copyText(v['Error']);
$('.class-copy-db-err').click(function(){ // $('.class-copy-db-err').click(function(){
copyText(v['Error']); // copyText(v['Error']);
}); // });
},
yes:function(){
if (info['Last_IO_Error'] != ''){
copyText(info['Last_IO_Error']);
return;
}
if (info['Last_SQL_Error'] != ''){
copyText(info['Last_SQL_Error']);
return;
}
if (info['Slave_SQL_Running_State'] != ''){
copyText(info['Slave_SQL_Running_State']);
return;
}
} }
}); });
}); });
@@ -2320,14 +2417,12 @@ function masterOrSlaveConf(version=''){
}); });
} }
function getMasterStatus(){ function getMasterStatus(){
myPost('get_master_status', '', function(rdata){ myPost('get_master_status', '', function(rdata){
var rdata = $.parseJSON(rdata.data); var rdata = $.parseJSON(rdata.data);
// console.log('mode:',rdata.data); // console.log('mode:',rdata.data);
if ( typeof(rdata.status) != 'undefined' && !rdata.status && rdata.data == 'pwd'){ if ( typeof(rdata.status) != 'undefined' && !rdata.status && rdata.data == 'pwd'){
layer.msg(rdata.msg, {icon:2}); layer.msg(rdata.msg, {icon:2,time:2000});
return; return;
} }
@@ -2409,7 +2504,6 @@ function masterOrSlaveConf(version=''){
return false; return false;
}, },
change:function(index,mode,reload){ change:function(index,mode,reload){
console.log(index,mode,reload);
myPost('set_dbrun_mode',{'mode':mode,'reload':reload},function(data){ myPost('set_dbrun_mode',{'mode':mode,'reload':reload},function(data){
layer.close(index); layer.close(index);
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
@@ -2425,10 +2519,8 @@ function masterOrSlaveConf(version=''){
getMasterDbList(); getMasterDbList();
} }
// if (rdata.slave_status){ getAsyncMasterDbList();
getAsyncMasterDbList(); getAsyncDataList()
getAsyncDataList()
// }
}); });
} }
getMasterStatus(); getMasterStatus();
+10 -1
View File
@@ -71,10 +71,19 @@ Install_app()
if [ ! -d ${mariadbDir}/mariadb-${MY_VER} ];then if [ ! -d ${mariadbDir}/mariadb-${MY_VER} ];then
cd ${mariadbDir} && tar -zxvf ${mariadbDir}/mariadb-${MY_VER}.tar.gz cd ${mariadbDir} && tar -zxvf ${mariadbDir}/mariadb-${MY_VER}.tar.gz
fi fi
INSTALL_CMD=cmake
# check cmake version
CMAKE_VERSION=`cmake -version | grep version | awk '{print $3}' | awk -F '.' '{print $1}'`
if [ "$CMAKE_VERSION" -eq "2" ];then
mkdir -p /var/log/mariadb
touch /var/log/mariadb/mariadb.log
INSTALL_CMD=cmake3
fi
if [ ! -d $serverPath/mariadb ];then if [ ! -d $serverPath/mariadb ];then
cd ${mariadbDir}/mariadb-${MY_VER} && cmake \ cd ${mariadbDir}/mariadb-${MY_VER} && ${INSTALL_CMD} \
-DCMAKE_INSTALL_PREFIX=$serverPath/mariadb \ -DCMAKE_INSTALL_PREFIX=$serverPath/mariadb \
-DMYSQL_DATADIR=$serverPath/mariadb/data/ \ -DMYSQL_DATADIR=$serverPath/mariadb/data/ \
-DMYSQL_USER=mysql \ -DMYSQL_USER=mysql \
+10 -1
View File
@@ -73,8 +73,17 @@ Install_app()
fi fi
INSTALL_CMD=cmake
# check cmake version
CMAKE_VERSION=`cmake -version | grep version | awk '{print $3}' | awk -F '.' '{print $1}'`
if [ "$CMAKE_VERSION" -eq "2" ];then
mkdir -p /var/log/mariadb
touch /var/log/mariadb/mariadb.log
INSTALL_CMD=cmake3
fi
if [ ! -d $serverPath/mariadb ];then if [ ! -d $serverPath/mariadb ];then
cd ${mariadbDir}/mariadb-${MY_VER} && cmake \ cd ${mariadbDir}/mariadb-${MY_VER} && ${INSTALL_CMD} \
-DCMAKE_INSTALL_PREFIX=$serverPath/mariadb \ -DCMAKE_INSTALL_PREFIX=$serverPath/mariadb \
-DMYSQL_DATADIR=$serverPath/mariadb/data/ \ -DMYSQL_DATADIR=$serverPath/mariadb/data/ \
-DMYSQL_USER=mysql \ -DMYSQL_USER=mysql \
+9 -1
View File
@@ -72,9 +72,17 @@ Install_app()
cd ${mariadbDir} && tar -zxvf ${mariadbDir}/mariadb-${MY_VER}.tar.gz cd ${mariadbDir} && tar -zxvf ${mariadbDir}/mariadb-${MY_VER}.tar.gz
fi fi
INSTALL_CMD=cmake
# check cmake version
CMAKE_VERSION=`cmake -version | grep version | awk '{print $3}' | awk -F '.' '{print $1}'`
if [ "$CMAKE_VERSION" -eq "2" ];then
mkdir -p /var/log/mariadb
touch /var/log/mariadb/mariadb.log
INSTALL_CMD=cmake3
fi
if [ ! -d $serverPath/mariadb ];then if [ ! -d $serverPath/mariadb ];then
cd ${mariadbDir}/mariadb-${MY_VER} && cmake \ cd ${mariadbDir}/mariadb-${MY_VER} && ${INSTALL_CMD} \
-DCMAKE_INSTALL_PREFIX=$serverPath/mariadb \ -DCMAKE_INSTALL_PREFIX=$serverPath/mariadb \
-DMYSQL_DATADIR=$serverPath/mariadb/data/ \ -DMYSQL_DATADIR=$serverPath/mariadb/data/ \
-DMYSQL_USER=mysql \ -DMYSQL_USER=mysql \
+4
View File
@@ -14,6 +14,10 @@ if mw.isAppleSystem():
app_debug = True app_debug = True
import warnings
warnings.filterwarnings('ignore', category=DeprecationWarning)
def getPluginName(): def getPluginName():
return 'memcached' return 'memcached'
+6 -1
View File
@@ -14,6 +14,9 @@ port = 33206
socket = {$SERVER_APP_PATH}/mysql.sock socket = {$SERVER_APP_PATH}/mysql.sock
datadir = {$SERVER_APP_PATH}/data datadir = {$SERVER_APP_PATH}/data
log-error = {$SERVER_APP_PATH}/data/error.log log-error = {$SERVER_APP_PATH}/data/error.log
server-id = {$SERVER_ID}
sql-mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
default_storage_engine = InnoDB default_storage_engine = InnoDB
language={$SERVER_APP_PATH}/bin/usr/share/mysql/english language={$SERVER_APP_PATH}/bin/usr/share/mysql/english
@@ -45,7 +48,6 @@ skip-ssl
log-bin=mysql-bin log-bin=mysql-bin
binlog_format=mixed binlog_format=mixed
server-id = 1
slow_query_log=1 slow_query_log=1
slow-query-log-file={$SERVER_APP_PATH}/data/mysql-slow.log slow-query-log-file={$SERVER_APP_PATH}/data/mysql-slow.log
long_query_time=3 long_query_time=3
@@ -71,6 +73,9 @@ replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
replicate-ignore-db = test replicate-ignore-db = test
master_info_repository = table
relay_log_info_repository = table
default_storage_engine = InnoDB default_storage_engine = InnoDB
innodb_data_home_dir = {$SERVER_APP_PATH}/data innodb_data_home_dir = {$SERVER_APP_PATH}/data
innodb_data_file_path = ibdata1:10M:autoextend innodb_data_file_path = ibdata1:10M:autoextend
+6 -1
View File
@@ -16,6 +16,9 @@ mysqlx_port = 33260
socket = {$SERVER_APP_PATH}/mysql.sock socket = {$SERVER_APP_PATH}/mysql.sock
datadir = {$SERVER_APP_PATH}/data datadir = {$SERVER_APP_PATH}/data
log-error = {$SERVER_APP_PATH}/data/error.log log-error = {$SERVER_APP_PATH}/data/error.log
server-id = {$SERVER_ID}
sql-mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
default_storage_engine = InnoDB default_storage_engine = InnoDB
key_buffer_size = 8M key_buffer_size = 8M
@@ -43,7 +46,6 @@ max_allowed_packet = 128M
log-bin=mysql-bin log-bin=mysql-bin
binlog_format=mixed binlog_format=mixed
server-id = 1
slow_query_log=1 slow_query_log=1
slow-query-log-file={$SERVER_APP_PATH}/data/mysql-slow.log slow-query-log-file={$SERVER_APP_PATH}/data/mysql-slow.log
long_query_time=3 long_query_time=3
@@ -68,6 +70,9 @@ replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
replicate-ignore-db = test replicate-ignore-db = test
master_info_repository = table
relay_log_info_repository = table
default_storage_engine = InnoDB default_storage_engine = InnoDB
innodb_data_home_dir = {$SERVER_APP_PATH}/data innodb_data_home_dir = {$SERVER_APP_PATH}/data
innodb_data_file_path = ibdata1:10M:autoextend innodb_data_file_path = ibdata1:10M:autoextend
+1 -1
View File
@@ -15,7 +15,7 @@
<p onclick="dbList()">管理列表</p> <p onclick="dbList()">管理列表</p>
<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p> <p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p>
</div> </div>
<div class="bt-w-con pd15"> <div class="bt-w-con pd15" style="height:555px;overflow: auto;">
<div class="soft-man-con"></div> <div class="soft-man-con"></div>
</div> </div>
</div> </div>
+214 -140
View File
@@ -28,6 +28,10 @@ def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName() return mw.getPluginDir() + '/' + getPluginName()
def getSPluginDir():
return '/www/server/mdserver-web/plugins/' + getPluginName()
def getServerDir(): def getServerDir():
return mw.getServerDir() + '/' + getPluginName() return mw.getServerDir() + '/' + getPluginName()
@@ -116,6 +120,10 @@ def contentReplace(content):
content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$SERVER_APP_PATH}', content = content.replace('{$SERVER_APP_PATH}',
service_path + '/' + getPluginName()) service_path + '/' + getPluginName())
server_id = int(time.time())
content = content.replace('{$SERVER_ID}', str(server_id))
return content return content
@@ -161,6 +169,10 @@ def pMysqlDb():
def initDreplace(version=''): def initDreplace(version=''):
t = getServerDir()
if not os.path.exists(t):
return
conf_dir = getServerDir() + '/etc' conf_dir = getServerDir() + '/etc'
mode_dir = conf_dir + '/mode' mode_dir = conf_dir + '/mode'
@@ -654,15 +666,15 @@ def isSqlError(mysqlMsg):
return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)") return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
if "using password:" in mysqlMsg: if "using password:" in mysqlMsg:
return mw.returnJson(False, '数据库密码错误,在管理列表-点击【修复】!') return mw.returnJson(False, '数据库密码错误,在管理列表-点击【修复】!')
if "1045" in mysqlMsg: if "1045," in mysqlMsg:
return mw.returnJson(False, '连接错误!') return mw.returnJson(False, '连接错误!')
if "SQL syntax" in mysqlMsg: if "SQL syntax" in mysqlMsg:
return mw.returnJson(False, 'SQL语法错误!') return mw.returnJson(False, 'SQL语法错误!')
if "Connection refused" in mysqlMsg: if "Connection refused" in mysqlMsg:
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!') return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
if "1133" in mysqlMsg: if "1133," in mysqlMsg:
return mw.returnJson(False, '数据库用户不存在!') return mw.returnJson(False, '数据库用户不存在!')
if "1007" in mysqlMsg: if "1007," in mysqlMsg:
return mw.returnJson(False, '数据库已经存在!') return mw.returnJson(False, '数据库已经存在!')
return None return None
@@ -1885,27 +1897,34 @@ def getMasterRepSlaveUserCmd(version):
mode = recognizeDbMode() mode = recognizeDbMode()
sid = getDbServerId()
channel_name = ""
if sid != '':
channel_name = " for channel 'r{}';".format(sid)
if mode == "gtid": if mode == "gtid":
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \ clist[0]['username'] + "', MASTER_PASSWORD='" + \
clist[0]['password'] + "', MASTER_AUTO_POSITION=1" clist[0]['password'] + "', MASTER_AUTO_POSITION=1" + channel_name
if version == '8.0': if version == '8.0':
sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \ sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \
clist[0]['username'] + "', SOURCE_PASSWORD='" + \ clist[0]['username'] + "', SOURCE_PASSWORD='" + \
clist[0]['password'] + "', MASTER_AUTO_POSITION=1" clist[0]['password'] + \
"', MASTER_AUTO_POSITION=1" + channel_name
else: else:
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \ clist[0]['username'] + "', MASTER_PASSWORD='" + \
clist[0]['password'] + \ clist[0]['password'] + \
"', MASTER_LOG_FILE='" + mstatus[0]["File"] + \ "', MASTER_LOG_FILE='" + mstatus[0]["File"] + \
"',MASTER_LOG_POS=" + str(mstatus[0]["Position"]) "',MASTER_LOG_POS=" + str(mstatus[0]["Position"]) + channel_name
if version == "8.0": if version == "8.0":
sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \ sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \
clist[0]['username'] + "', SOURCE_PASSWORD='" + \ clist[0]['username'] + "', SOURCE_PASSWORD='" + \
clist[0]['password'] + \ clist[0]['password'] + \
"', SOURCE_LOG_FILE='" + mstatus[0]["File"] + \ "', SOURCE_LOG_FILE='" + mstatus[0]["File"] + \
"',SOURCE_LOG_POS=" + str(mstatus[0]["Position"]) "',SOURCE_LOG_POS=" + \
str(mstatus[0]["Position"]) + channel_name
data = {} data = {}
data['cmd'] = sql data['cmd'] = sql
@@ -2217,7 +2236,7 @@ def getSlaveSyncCmd(version=''):
root = mw.getRunDir() root = mw.getRunDir()
cmd = 'cd ' + root + ' && python3 ' + root + \ cmd = 'cd ' + root + ' && python3 ' + root + \
'/plugins/mysql/index.py do_full_sync {"db":"all"}' '/plugins/mysql/index.py do_full_sync {"db":"all","sign":""}'
return mw.returnJson(True, 'ok', cmd) return mw.returnJson(True, 'ok', cmd)
@@ -2233,100 +2252,151 @@ def initSlaveStatus(version=''):
return initSlaveStatusSyncUser(version) return initSlaveStatusSyncUser(version)
def parseSlaveSyncCmd(cmd):
a = {}
if cmd.lower().find('for') > 0:
cmd_tmp = cmd.split('for')
cmd = cmd_tmp[0].strip()
pattern_c = r"channel \'(.*)\';"
match_val = re.match(pattern_c, cmd_tmp[1].strip(), re.I)
if match_val:
m_groups = match_val.groups()
a['channel'] = m_groups[0]
vlist = cmd.split(',')
for i in vlist:
tmp = i.strip()
tmp_a = tmp.split(" ")
real_tmp = tmp_a[len(tmp_a) - 1]
kv = real_tmp.split("=")
a[kv[0]] = kv[1].replace("'", '').replace("'", '').replace(";", '')
return a
def initSlaveStatusSyncUser(version=''): def initSlaveStatusSyncUser(version=''):
conn = pSqliteDb('slave_sync_user') conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find() slave_data = conn.field('ip,port,user,pass,mode,cmd').select()
if len(data) < 1: if len(slave_data) < 1:
return mw.returnJson(False, '需要先添加同步用户配置!') return mw.returnJson(False, '需要先添加同步用户配置!')
# print(data) # print(data)
db = pMysqlDb() pdb = pMysqlDb()
dlist = db.query('show slave status') if len(slave_data) == 1:
if len(dlist) > 0: dlist = pdb.query('show slave status')
return mw.returnJson(False, '已经初始化好了zz...') if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...')
u = data
mode_name = 'classic'
if u['mode'] == '1':
mode_name = 'gtid'
msg = ''
local_mode = recognizeDbMode() local_mode = recognizeDbMode()
if local_mode != mode_name: for x in range(len(slave_data)):
return mw.returnJson(False, '同步模式不一致!') slave_t = slave_data[x]
mode_name = 'classic'
base_t = 'IP:' + slave_t['ip'] + ",PORT:" + \
slave_t['port'] + ",USER:" + slave_t['user']
t = db.query(u['cmd']) if slave_t['mode'] == '1':
# print(t) mode_name = 'gtid'
db.query("start slave user='{}' password='{}';".format(
u['user'], u['pass'])) if local_mode != mode_name:
return mw.returnJson(True, '初始化成功!') msg += base_t + '->同步模式不一致'
continue
cmd_sql = slave_t['cmd']
if cmd_sql == '':
msg += base_t + '->同步命令不能为空'
continue
try:
pinfo = parseSlaveSyncCmd(cmd_sql)
except Exception as e:
return mw.returnJson(False, base_t + '->CMD同步命令不合规范!')
t = pdb.query(cmd_sql)
isError = isSqlError(t)
if isError:
return isError
# pdb.query("start slave user='{}' password='{}';".format(
# u['user'], u['pass']))
pdb.query("start slave")
pdb.query("start all slaves")
if msg == '':
msg = '初始化成功!'
return mw.returnJson(True, msg)
def initSlaveStatusSSH(version=''): def initSlaveStatusSSH(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...')
conn = pSqliteDb('slave_id_rsa') conn = pSqliteDb('slave_id_rsa')
data = conn.field('ip,port,id_rsa').find() ssh_list = conn.field('ip,port,id_rsa,db_user').select()
if len(data) < 1: if len(ssh_list) < 1:
return mw.returnJson(False, '需要先配置【[主]SSH配置】!') return mw.returnJson(False, '需要先配置【[主]SSH配置】!')
SSH_PRIVATE_KEY = "/tmp/t_ssh.txt" local_mode = recognizeDbMode()
ip = data['ip']
master_port = data['port']
mw.writeFile(SSH_PRIVATE_KEY, data['id_rsa'].replace('\\n', '\n'))
import paramiko import paramiko
paramiko.util.log_to_file('paramiko.log') paramiko.util.log_to_file('paramiko.log')
ssh = paramiko.SSHClient() ssh = paramiko.SSHClient()
try: db.query('stop slave')
db.query('reset slave all')
for data in ssh_list:
ip = data['ip']
SSH_PRIVATE_KEY = "/tmp/t_ssh_" + ip + ".txt"
master_port = data['port']
mw.writeFile(SSH_PRIVATE_KEY, data['id_rsa'].replace('\\n', '\n'))
mw.execShell("chmod 600 " + SSH_PRIVATE_KEY) mw.execShell("chmod 600 " + SSH_PRIVATE_KEY)
key = paramiko.RSAKey.from_private_key_file(SSH_PRIVATE_KEY) try:
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) key = paramiko.RSAKey.from_private_key_file(SSH_PRIVATE_KEY)
ssh.connect(hostname=ip, port=int(master_port), ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
username='root', pkey=key) ssh.connect(hostname=ip, port=int(master_port),
username='root', pkey=key)
cmd = 'cd /www/server/mdserver-web && python3 plugins/mysql/index.py get_master_rep_slave_user_cmd {"username":"","db":""}' db_user = data['db_user']
stdin, stdout, stderr = ssh.exec_command(cmd) cmd = 'cd /www/server/mdserver-web && source bin/activate && python3 ' + \
result = stdout.read() getSPluginDir() + \
result = result.decode('utf-8') '/index.py get_master_rep_slave_user_cmd {"username":"' + \
cmd_data = json.loads(result) db_user + '","db":""}'
stdin, stdout, stderr = ssh.exec_command(cmd)
result = stdout.read()
result = result.decode('utf-8')
if result.strip() == "":
return mw.returnJson(False, '[主][' + ip + ']:获取同步命令失败!')
if not cmd_data['status']: cmd_data = json.loads(result)
return mw.returnJson(False, '[主]:' + cmd_data['msg']) if not cmd_data['status']:
return mw.returnJson(False, '[主][' + ip + ']:' + cmd_data['msg'])
local_mode = recognizeDbMode() if local_mode != cmd_data['data']['mode']:
if local_mode != cmd_data['data']['mode']: return mw.returnJson(False, '[主][' + ip + ']:【{}】从【{}】,运行模式不一致!'.format(cmd_data['data']['mode'], local_mode))
return mw.returnJson(False, '主【{}】从【{}】,运行模式不一致!'.format(cmd_data['data']['mode'], local_mode))
u = cmd_data['data']['info'] u = cmd_data['data']['info']
ps = u['username'] + "|" + u['password']
conn.where('ip=?', (ip,)).setField('ps', ps)
db.query('stop slave')
# 保证同步IP一致 ps = u['username'] + "|" + u['password']
cmd = cmd_data['data']['cmd'] print(ps)
if cmd.find('SOURCE_HOST') > -1: conn.where('ip=?', (ip,)).setField('ps', ps)
cmd = re.sub(r"SOURCE_HOST='(.*)'", db.query('stop slave')
"SOURCE_HOST='" + ip + "'", cmd, 1)
if cmd.find('MASTER_HOST') > -1: # 保证同步IP一致
cmd = re.sub(r"MASTER_HOST='(.*)'", cmd = cmd_data['data']['cmd']
"MASTER_HOST='" + ip + "'", cmd, 1) if cmd.find('SOURCE_HOST') > -1:
db.query(cmd) cmd = re.sub(r"SOURCE_HOST='(.*?)'",
db.query("start slave user='{}' password='{}';".format( "SOURCE_HOST='" + ip + "'", cmd, 1)
u['username'], u['password']))
except Exception as e:
return mw.returnJson(False, 'SSH认证配置连接失败!' + str(e))
ssh.close() if cmd.find('MASTER_HOST') > -1:
time.sleep(1) cmd = re.sub(r"MASTER_HOST='(.*?)'",
os.system("rm -rf " + SSH_PRIVATE_KEY) "MASTER_HOST='" + ip + "'", cmd, 1)
db.query(cmd)
ssh.close()
if os.path.exists(SSH_PRIVATE_KEY):
os.system("rm -rf " + SSH_PRIVATE_KEY)
except Exception as e:
return mw.returnJson(False, '[主][' + ip + ']:SSH认证配置连接失败!' + str(e))
db.query('start slave')
return mw.returnJson(True, '初始化成功!') return mw.returnJson(True, '初始化成功!')
@@ -2336,60 +2406,37 @@ def setSlaveStatus(version=''):
return mw.returnJson(False, '需要先设置同步配置') return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file) mode = mw.readFile(mode_file)
if mode == 'ssh': pdb = pMysqlDb()
return setSlaveStatusSSH(version) dlist = pdb.query('show slave status')
if mode == 'sync-user':
return setSlaveStatusSyncUser(version)
def setSlaveStatusSyncUser(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0: if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!') return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'): for v in dlist:
db.query('stop slave') connection_name = ''
else: cmd = "slave"
ip = dlist[0]['Master_Host'] if 'Channel_Name' in v:
conn = pSqliteDb('slave_sync_user') ch_name = v['Channel_Name']
data = conn.field('ip,port,user,pass,mode,cmd').find() cmd = "slave for channel '{}'".format(ch_name)
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
user = data['user']
apass = data['pass']
db.query("start slave") if (v["Slave_IO_Running"] == 'Yes' or v["Slave_SQL_Running"] == 'Yes'):
# db.query("start slave user='{}' password='{}';".format(user, apass)) pdb.query("stop {}".format(cmd))
else:
return mw.returnJson(True, '设置成功!') pdb.query("start {}".format(cmd))
def setSlaveStatusSSH(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加主服务命令或者执行[初始化]!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
db.query('stop slave')
else:
ip = dlist[0]['Master_Host']
conn = pSqliteDb('slave_id_rsa')
data = conn.field('ip,ps').where("ip=?", (ip,)).find()
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
u = data['ps'].split("|")
db.query("start slave user='{}' password='{}';".format(u[0], u[1]))
return mw.returnJson(True, '设置成功!') return mw.returnJson(True, '设置成功!')
def deleteSlave(version=''): def deleteSlave(version=''):
args = getArgs()
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('stop slave') if 'sign' in args:
dlist = db.query('reset slave all') sign = args['sign']
db.query("stop slave for channel '{}'".format(sign))
db.query("reset slave all for channel '{}'".format(sign))
else:
db.query('stop slave')
db.query('reset slave all')
return mw.returnJson(True, '删除成功!') return mw.returnJson(True, '删除成功!')
@@ -2425,11 +2472,13 @@ def dumpMysqlData(version=''):
############### --- 重要 同步---- ########### ############### --- 重要 同步---- ###########
def asyncTmpfile():
path = '/tmp/mysql_apt_async_status.txt'
return path
def writeDbSyncStatus(data): def writeDbSyncStatus(data):
path = '/tmp/db_async_status.txt' path = asyncTmpfile()
# status_data['code'] = 1
# status_data['msg'] = '主服务器备份完成...'
# status_data['progress'] = 30
mw.writeFile(path, json.dumps(data)) mw.writeFile(path, json.dumps(data))
@@ -2447,16 +2496,22 @@ def doFullSync(version=''):
def doFullSyncUser(version=''): def doFullSyncUser(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['db']) data = checkArgs(args, ['db', 'sign'])
if not data[0]: if not data[0]:
return data[1] return data[1]
sync_db = args['db'] sync_db = args['db']
sync_sign = args['sign']
db = pMysqlDb() db = pMysqlDb()
conn = pSqliteDb('slave_sync_user') conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find() if sync_sign != '':
data = conn.field('ip,port,user,pass,mode,cmd').where(
'ip=?', (sync_sign,)).find()
else:
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user'] user = data['user']
apass = data['pass'] apass = data['pass']
port = data['port'] port = data['port']
@@ -2473,16 +2528,17 @@ def doFullSyncUser(version=''):
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20}) writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
if not os.path.exists(bak_file): if not os.path.exists(bak_file):
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \ dump_sql_data = getServerDir() + "/bin/usr/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
port + " -u" + user + " -p" + apass + \ port + " -u" + user + " -p\"" + apass + \
" --ssl-mode=DISABLED " + sync_db + " > " + bak_file "\" --ssl-mode=DISABLED " + sync_db + " > " + bak_file
# print(dump_sql_data)
mw.execShell(dump_sql_data) mw.execShell(dump_sql_data)
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40}) writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
if os.path.exists(bak_file): if os.path.exists(bak_file):
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root') pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile() sock = getSocketFile()
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \ my_import_cmd = getServerDir() + '/bin/usr/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
' ' + sync_db + ' < ' + bak_file ' ' + sync_db + ' < ' + bak_file
mw.execShell(my_import_cmd) mw.execShell(my_import_cmd)
@@ -2492,21 +2548,30 @@ def doFullSyncUser(version=''):
db.query("start slave") db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100}) writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + bak_file)
if os.path.exists(bak_file):
os.system("rm -rf " + bak_file)
return True return True
def doFullSyncSSH(version=''): def doFullSyncSSH(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['db']) data = checkArgs(args, ['db', 'sign'])
if not data[0]: if not data[0]:
return data[1] return data[1]
sync_db = args['db']
sync_sign = args['sign']
db = pMysqlDb() db = pMysqlDb()
id_rsa_conn = pSqliteDb('slave_id_rsa') id_rsa_conn = pSqliteDb('slave_id_rsa')
data = id_rsa_conn.field('ip,port,db_user,id_rsa').find() if sync_sign != '':
data = id_rsa_conn.field('ip,port,db_user,id_rsa').where(
'ip=?', (sync_sign,)).find()
else:
data = id_rsa_conn.field('ip,port,db_user,id_rsa').find()
SSH_PRIVATE_KEY = "/tmp/mysql_sync_id_rsa.txt" SSH_PRIVATE_KEY = "/tmp/mysql_sync_id_rsa.txt"
id_rsa = data['id_rsa'].replace('\\n', '\n') id_rsa = data['id_rsa'].replace('\\n', '\n')
@@ -2548,7 +2613,8 @@ def doFullSyncSSH(version=''):
writeDbSyncStatus({'code': 0, 'msg': '登录Master成功...', 'progress': 5}) writeDbSyncStatus({'code': 0, 'msg': '登录Master成功...', 'progress': 5})
dbname = args['db'] dbname = args['db']
cmd = "cd /www/server/mdserver-web && python3 plugins/mysql/index.py dump_mysql_data {\"db\":'" + dbname + "'}" cmd = "cd /www/server/mdserver-web && source bin/activate && python3 " + \
getSPluginDir() + "/index.py dump_mysql_data {\"db\":'" + dbname + "'}"
print(cmd) print(cmd)
stdin, stdout, stderr = ssh.exec_command(cmd) stdin, stdout, stderr = ssh.exec_command(cmd)
result = stdout.read() result = stdout.read()
@@ -2571,7 +2637,10 @@ def doFullSyncSSH(version=''):
if copy_status == None: if copy_status == None:
writeDbSyncStatus({'code': 2, 'msg': '数据同步本地完成...', 'progress': 40}) writeDbSyncStatus({'code': 2, 'msg': '数据同步本地完成...', 'progress': 40})
cmd = 'cd /www/server/mdserver-web && python3 plugins/mysql/index.py get_master_rep_slave_user_cmd {"username":"' + db_user + '","db":""}' cmd = 'cd /www/server/mdserver-web && source bin/activate && python3 ' + \
getSPluginDir() + \
'/index.py get_master_rep_slave_user_cmd {"username":"' + \
db_user + '","db":""}'
stdin, stdout, stderr = ssh.exec_command(cmd) stdin, stdout, stderr = ssh.exec_command(cmd)
result = stdout.read() result = stdout.read()
result = result.decode('utf-8') result = result.decode('utf-8')
@@ -2583,10 +2652,12 @@ def doFullSyncSSH(version=''):
cmd = cmd_data['data']['cmd'] cmd = cmd_data['data']['cmd']
# 保证同步IP一致 # 保证同步IP一致
if cmd.find('SOURCE_HOST') > -1: if cmd.find('SOURCE_HOST') > -1:
cmd = re.sub(r"SOURCE_HOST='(.*)'", "SOURCE_HOST='" + ip + "'", cmd, 1) cmd = re.sub(r"SOURCE_HOST='(.*?)'",
"SOURCE_HOST='" + ip + "'", cmd, 1)
if cmd.find('MASTER_HOST') > -1: if cmd.find('MASTER_HOST') > -1:
cmd = re.sub(r"MASTER_HOST='(.*)'", "SOURCE_HOST='" + ip + "'", cmd, 1) cmd = re.sub(r"MASTER_HOST='(.*?)'",
"MASTER_HOST='" + ip + "'", cmd, 1)
db.query(cmd) db.query(cmd)
uinfo = cmd_data['data']['info'] uinfo = cmd_data['data']['info']
@@ -2598,8 +2669,10 @@ def doFullSyncSSH(version=''):
root_dir = getServerDir() root_dir = getServerDir()
msock = root_dir + "/mysql.sock" msock = root_dir + "/mysql.sock"
mw.execShell("cd /tmp && gzip -d dump.sql.gz") mw.execShell("cd /tmp && gzip -d dump.sql.gz")
cmd = root_dir + "/bin/mysql -S " + msock + \ cmd = root_dir + "/bin/usr/bin/mysql -S " + msock + \
" -uroot -p" + pwd + " < /tmp/dump.sql" " -uroot -p" + pwd + " < /tmp/dump.sql"
print(cmd)
import_data = mw.execShell(cmd) import_data = mw.execShell(cmd)
if import_data[0] == '': if import_data[0] == '':
print(import_data[1]) print(import_data[1])
@@ -2609,8 +2682,9 @@ def doFullSyncSSH(version=''):
writeDbSyncStatus({'code': 5, 'msg': '导入数据失败...', 'progress': 100}) writeDbSyncStatus({'code': 5, 'msg': '导入数据失败...', 'progress': 100})
return 'fail' return 'fail'
db.query("start slave user='{}' password='{}';".format( # "start slave user='{}' password='{}';".format(uinfo['username'], uinfo['password'])
uinfo['username'], uinfo['password']))
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100}) writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + SSH_PRIVATE_KEY) os.system("rm -rf " + SSH_PRIVATE_KEY)
@@ -2624,12 +2698,12 @@ def fullSync(version=''):
if not data[0]: if not data[0]:
return data[1] return data[1]
status_file = '/tmp/db_async_status.txt' status_file = asyncTmpfile()
if args['begin'] == '1': if args['begin'] == '1':
cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + \ cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + getPluginDir() + \
getPluginDir() + \ '/index.py do_full_sync {"db":"' + \
'/index.py do_full_sync {"db":"' + args['db'] + '"} &' args['db'] + '","sign":"' + sign + '"} &'
print(cmd) # print(cmd)
mw.execShell(cmd) mw.execShell(cmd)
return json.dumps({'code': 0, 'msg': '同步数据中!', 'progress': 0}) return json.dumps({'code': 0, 'msg': '同步数据中!', 'progress': 0})
+5 -1
View File
@@ -48,7 +48,11 @@ fi
sh -x $curPath/versions/$2/install.sh $1 sh -x $curPath/versions/$2/install.sh $1
if [ "${action}" == "install" ];then if [ "${action}" == "install" ];then
# #初始化 #初始化
if [ "$?" != "0" ];then
exit $?
fi
cd ${rootPath} && python3 ${rootPath}/plugins/mysql-apt/index.py start ${type} cd ${rootPath} && python3 ${rootPath}/plugins/mysql-apt/index.py start ${type}
cd ${rootPath} && python3 ${rootPath}/plugins/mysql-apt/index.py initd_install ${type} cd ${rootPath} && python3 ${rootPath}/plugins/mysql-apt/index.py initd_install ${type}
fi fi
+165 -81
View File
@@ -162,34 +162,6 @@ function myPort(){
}); });
} }
//数据库存储信置
function changeMySQLDataPath(act) {
if (act != undefined) {
layer.confirm(lan.soft.mysql_to_msg, { closeBtn: 2, icon: 3 }, function() {
var datadir = $("#datadir").val();
var data = 'datadir=' + datadir;
var loadT = layer.msg(lan.soft.mysql_to_msg1, { icon: 16, time: 0, shade: [0.3, '#000'] });
$.post('/database?action=SetDataDir', data, function(rdata) {
layer.close(loadT)
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 });
});
});
return;
}
$.post('/database?action=GetMySQLInfo', '', function(rdata) {
var LimitCon = '<p class="conf_p">\
<input id="datadir" class="phpUploadLimit bt-input-text mr5" style="width:350px;" type="text" value="' + rdata.datadir + '" name="datadir">\
<span onclick="ChangePath(\'datadir\')" class="glyphicon glyphicon-folder-open cursor mr20" style="width:auto"></span><button class="btn btn-success btn-sm" onclick="changeMySQLDataPath(1)">' + lan.soft.mysql_to + '</button>\
</p>';
$(".soft-man-con").html(LimitCon);
});
}
//数据库配置状态 //数据库配置状态
function myPerfOpt() { function myPerfOpt() {
//获取MySQL配置 //获取MySQL配置
@@ -1691,12 +1663,12 @@ function getMasterRepSlaveListPage(){
} }
function deleteSlave(){ function deleteSlave(sign){
myPost('delete_slave', {}, function(data){ myPost('delete_slave', {sign:sign}, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
showMsg(rdata['msg'], function(){ showMsg(rdata['msg'], function(){
masterOrSlaveConf(); masterOrSlaveConf();
},{},3000); },{icon:rdata.status?1:2,time:3000},3000);
}); });
} }
@@ -1704,28 +1676,73 @@ function deleteSlave(){
function getFullSyncStatus(db){ function getFullSyncStatus(db){
var timeId = null; var timeId = null;
var btn = '<div class="table_toolbar" style="left:0px;"><span data-status="init" class="sync btn btn-default btn-sm" id="begin_full_sync" title="">开始</span></div>'; myPost('get_slave_list', {page:1,page_size:100}, function(data){
var loadOpen = layer.open({ var rdata = $.parseJSON(data.data);
type: 1, var rsource = rdata.data;
title: '全量同步['+db+']',
area: '500px', if (db == 'ALL' && rsource.length>1){
content:"<div class='bt-form pd20 c6'>\ layer.msg("多主不支持该模式!",{icon:2});
<div class='divtable mtb10'>\ return;
<span id='full_msg'></span>\
<div class='progress'>\
<div class='progress-bar' role='progressbar' aria-valuenow='0' aria-valuemin='0' aria-valuemax='100' style='min-width: 2em;'>0%</div>\
</div>\
</div>\
"+btn+"\
</div>",
cancel: function(){
clearInterval(timeId);
} }
var dataSource = '';
if (rsource.length>1){
var sourceList = '';
for (var i = 0; i < rsource.length; i++) {
if ('Channel_Name' in rsource[i]){
sourceList += '<option val="'+rsource[i]['Master_Host']+'">'+rsource[i]['Master_Host']+'</option>';
}
}
dataSource = "<p class='line' style='text-align:center;'>\
<span>同步数据源:</span>\
<select class='bt-input-text' name='data_source' style='width:200px;'>" + sourceList + "</select>\
</p>";
}
layer.open({
type: 1,
title: '全量同步['+db+']',
area: '500px',
content:"<div class='bt-form pd15'>\
<div class='divtable mtb10'>\
"+dataSource+"\
<span id='full_msg'></span>\
<div class='progress'>\
<div class='progress-bar' role='progressbar' aria-valuenow='0' aria-valuemin='0' aria-valuemax='100' style='min-width: 2em;'>0%</div>\
</div>\
</div>\
<div class='table_toolbar' style='left:0px;'>\
<span data-status='init' class='sync btn btn-default btn-sm' id='begin_full_sync'>开始</span>\
</div>\
</div>",
cancel: function(){
clearInterval(timeId);
},
success:function(){
$('#begin_full_sync').click(function(){
var val = $(this).data('status');
var sign = '';
if (dataSource !=''){
sign = $('select[name="data_source"]').val();
}
if (val == 'init'){
fullSync(db, sign, 1);
timeId = setInterval(function(){
fullSync(db,sign,0);
}, 1000);
$(this).data('status','starting');
} else {
layer.msg("正在同步中..",{icon:0});
}
});
}
});
}); });
function fullSync(db,begin){ function fullSync(db,sign,begin){
myPostN('full_sync', {db:db,begin:begin}, function(data){ myPostN('full_sync', {db:db,sign:sign,begin:begin}, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
$('#full_msg').text(rdata['msg']); $('#full_msg').text(rdata['msg']);
$('.progress-bar').css('width',rdata['progress']+'%'); $('.progress-bar').css('width',rdata['progress']+'%');
@@ -1734,23 +1751,10 @@ function getFullSyncStatus(db){
if (rdata['code']==6 ||rdata['code']<0){ if (rdata['code']==6 ||rdata['code']<0){
layer.msg(rdata['msg']); layer.msg(rdata['msg']);
clearInterval(timeId); clearInterval(timeId);
$("#begin_full_sync").attr('data-status','init'); $("#begin_full_sync").data('data','init');
} }
}); });
} }
$('#begin_full_sync').click(function(){
var val = $(this).attr('data-status');
if (val == 'init'){
fullSync(db,1);
timeId = setInterval(function(){
fullSync(db,0);
}, 1000);
$(this).attr('data-status','starting');
} else {
layer.msg("正在同步中..");
}
});
} }
function addSlaveSSH(ip=''){ function addSlaveSSH(ip=''){
@@ -1920,18 +1924,29 @@ function addSlaveSyncUser(ip=''){
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\ <div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\ <div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
<div class='line'>\ <div class='line'>\
<span class='tname'>CMD[最好填好]</span>\ <span class='tname'>CMD[必须填写]</span>\
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\ <div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
</div>\ </div>\
<input type='hidden' name='mode' value='"+mode+"' />\ <input type='hidden' name='mode' value='"+mode+"' />\
</form>", </form>",
success:function(){ success:function(){
$('textarea[name="cmd"]').html(cmd); $('textarea[name="cmd"]').html(cmd);
$('textarea[name="cmd"]').change(function(){ $('textarea[name="cmd"]').change(function(){
var val = $(this).val(); var val = $(this).val();
var vlist = val.split(',');
var a = {}; var a = {};
if (val.toLowerCase().indexOf('for')>0){
cmd_tmp = val.split('for');
val = cmd_tmp[0].trim();
const channel_str = cmd_tmp[1].trim();
const ch_reg = /channel \'(.*)\';/;
var match_val = channel_str.match(ch_reg);
if (match_val.length>1){
a['channel'] = match_val[1];
}
}
var vlist = val.split(',');
for (var i in vlist) { for (var i in vlist) {
var tmp = toTrim(vlist[i]); var tmp = toTrim(vlist[i]);
var tmp_a = tmp.split(" "); var tmp_a = tmp.split(" ");
@@ -2098,7 +2113,7 @@ function getSlaveUserList(){
layerId = layer.open({ layerId = layer.open({
type: 1, type: 1,
title: '同步账户列表', title: '同步账户列表',
area: '500px', area: '600px',
content:"<div class='bt-form pd20 c6'>\ content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\ <div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\ <div><table class='table table-hover get-slave-ssh-list'>\
@@ -2122,7 +2137,7 @@ function getSlaveSSHList(page=1){
layerId = layer.open({ layerId = layer.open({
type: 1, type: 1,
title: 'SSH列表', title: 'SSH列表',
area: '500px', area: '600px',
content:"<div class='bt-form pd20 c6'>\ content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\ <div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\ <div><table class='table table-hover get-slave-ssh-list'>\
@@ -2230,10 +2245,16 @@ function masterOrSlaveConf(version=''){
myPost('get_slave_list', _data, function(data){ myPost('get_slave_list', _data, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
var list = ''; var list = '';
var isHasSign = false;
for(i in rdata.data){ for(i in rdata.data){
var v = rdata.data[i]; var v = rdata.data[i];
var status = "<a class='btlink db_error'>异常</>"; if ('Channel_Name' in v){
isHasSign = true;
}
var status = "<a data-id="+i+" class='btlink db_error'>异常</>";
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){ if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
status = "正常"; status = "正常";
} }
@@ -2245,13 +2266,23 @@ function masterOrSlaveConf(version=''){
list += '<td>' + rdata.data[i]['Master_Log_File'] +'</td>'; list += '<td>' + rdata.data[i]['Master_Log_File'] +'</td>';
list += '<td>' + rdata.data[i]['Slave_IO_Running'] +'</td>'; list += '<td>' + rdata.data[i]['Slave_IO_Running'] +'</td>';
list += '<td>' + rdata.data[i]['Slave_SQL_Running'] +'</td>'; list += '<td>' + rdata.data[i]['Slave_SQL_Running'] +'</td>';
if (isHasSign){
list += '<td>' + v['Channel_Name'] +'</td>';
}
list += '<td>' + status +'</td>'; list += '<td>' + status +'</td>';
list += '<td style="text-align:right">' + list += '<td style="text-align:right">' +
'<a href="javascript:;" class="btlink" onclick="deleteSlave()" title="删除">删除</a>' + '<a data-id="'+i+'" href="javascript:;" class="btlink btn_delete_slave" title="删除">删除</a>' +
'</td>'; '</td>';
list += '</tr>'; list += '</tr>';
} }
var signThead_th = '';
if (isHasSign){
var signThead_th = '<th>标识</th>';
}
var con = '<div class="divtable mtb10">\ var con = '<div class="divtable mtb10">\
<div class="tablescroll">\ <div class="tablescroll">\
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\ <table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
@@ -2262,6 +2293,7 @@ function masterOrSlaveConf(version=''){
<th>日志</th>\ <th>日志</th>\
<th>IO</th>\ <th>IO</th>\
<th>SQL</th>\ <th>SQL</th>\
'+signThead_th+'\
<th>状态</th>\ <th>状态</th>\
<th style="text-align:right;">操作</th></tr></thead>\ <th style="text-align:right;">操作</th></tr></thead>\
<tbody>\ <tbody>\
@@ -2276,22 +2308,74 @@ function masterOrSlaveConf(version=''){
// </div> // </div>
$(".table_slave_status_list").html(con); $(".table_slave_status_list").html(con);
$('.db_error').click(function(){ $(".btn_delete_slave").click(function(){
var id = $(this).data('id');
var v = rdata.data[id];
if ('Channel_Name' in v){
deleteSlave(v['Channel_Name']);
} else{
deleteSlave();
}
});
$('.db_error').click(function(){
var id = $(this).data('id');
var info = rdata.data[id];
var err_line = "";
err_line +="<tr>\
<td>IO错误</td>\
<td>"+ (info['Last_IO_Error'] == '' ? '无':info['Last_IO_Error'])+"</td>\
</tr>";
err_line +="<tr>\
<td>SQL错误</td>\
<td>"+(info['Last_SQL_Error'] == '' ? '无':info['Last_SQL_Error'])+"</td>\
</tr>";
err_line +="<tr>\
<td>状态</td>\
<td>"+(info['Slave_SQL_Running_State'] == '' ? '无':info['Slave_SQL_Running_State']) +"</td>\
</tr>";
layer.open({ layer.open({
type: 1, type: 1,
title: '同步异常信息', title: '同步异常信息',
area: '500px', area: ['600px','300px'],
content:"<form class='bt-form pd20 pb70'>\ btn:['复制错误',"取消"],
<div class='line'>"+v['Error']+"</div>\ content:"<form class='bt-form pd15'>\
<div class='bt-form-submit-btn'>\ <div class='divtable mtb10'>\
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\ <div class='tablescroll'>\
<table class='table table-hover' width='100%' cellspacing='0' cellpadding='0' border='0' style='border: 0 none;'>\
<thead><tr>\
<th style='width:80px;'>类型</th>\
<th>内容</th>\
</tr></thead>\
<tbody>"+ err_line +"</tbody>\
</table>\
</div>\
</div>\ </div>\
</form>", </form>",
success:function(){ success:function(){
copyText(v['Error']); // copyText(v['Error']);
$('.class-copy-db-err').click(function(){ // $('.class-copy-db-err').click(function(){
copyText(v['Error']); // copyText(v['Error']);
}); // });
},
yes:function(){
if (info['Last_IO_Error'] != ''){
copyText(info['Last_IO_Error']);
return;
}
if (info['Last_SQL_Error'] != ''){
copyText(info['Last_SQL_Error']);
return;
}
if (info['Slave_SQL_Running_State'] != ''){
copyText(info['Slave_SQL_Running_State']);
return;
}
} }
}); });
}); });
+16 -1
View File
@@ -35,7 +35,22 @@ if [ "$OSNAME" == "ubuntu" ];then
VERSION_ID="18.04" VERSION_ID="18.04"
fi fi
SUFFIX_NAME=${MYSQL_VER}-1${OSNAME}${VERSION_ID}_amd64 ARCH="amd64"
TMP_ARCH=`arch`
if [ "$TMP_ARCH" == "x86_64" ];then
ARCH="amd64"
elif [ "$TMP_ARCH" == "aarch64" ];then
ARCH="arm64"
else
ARCH="amd64"
fi
if [ "$ARCH" != "amd64" ];then
echo "暂时不支持该${ARCH}" > $install_tmp
exit 0
fi
SUFFIX_NAME=${MYSQL_VER}-1${OSNAME}${VERSION_ID}_${ARCH}
APT_INSTALL() APT_INSTALL()
{ {
+18 -2
View File
@@ -22,8 +22,25 @@ VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -
# cd /www/server/mdserver-web/plugins/mysql-apt && bash install.sh install 8.0 # cd /www/server/mdserver-web/plugins/mysql-apt && bash install.sh install 8.0
ARCH="amd64"
TMP_ARCH=`arch`
if [ "$TMP_ARCH" == "x86_64" ];then
ARCH="amd64"
elif [ "$TMP_ARCH" == "aarch64" ];then
ARCH="arm64"
else
ARCH="amd64"
fi
if [ "$ARCH" != "amd64" ];then
echo "暂时不支持该${ARCH}" > $install_tmp
exit 1
fi
MYSQL_VER=8.0.30 MYSQL_VER=8.0.30
SUFFIX_NAME=${MYSQL_VER}-1${OSNAME}${VERSION_ID}_amd64 SUFFIX_NAME=${MYSQL_VER}-1${OSNAME}${VERSION_ID}_${ARCH}
# /lib/systemd/system/mysql.service # /lib/systemd/system/mysql.service
@@ -81,7 +98,6 @@ Install_mysql()
useradd -g mysql mysql useradd -g mysql mysql
fi fi
isApt=`which apt` isApt=`which apt`
if [ "$isApt" != "" ];then if [ "$isApt" != "" ];then
APT_INSTALL APT_INSTALL
+6 -1
View File
@@ -14,6 +14,9 @@ port = 33206
socket = {$SERVER_APP_PATH}/mysql.sock socket = {$SERVER_APP_PATH}/mysql.sock
datadir = {$SERVER_APP_PATH}/data datadir = {$SERVER_APP_PATH}/data
log-error = {$SERVER_APP_PATH}/data/error.log log-error = {$SERVER_APP_PATH}/data/error.log
server-id = {$SERVER_ID}
sql-mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
default_storage_engine = InnoDB default_storage_engine = InnoDB
language={$SERVER_APP_PATH}/bin/usr/share/mysql/english language={$SERVER_APP_PATH}/bin/usr/share/mysql/english
@@ -46,7 +49,6 @@ skip-ssl
log-bin=mysql-bin log-bin=mysql-bin
binlog_format=mixed binlog_format=mixed
server-id = 1
slow_query_log=1 slow_query_log=1
slow-query-log-file={$SERVER_APP_PATH}/data/mysql-slow.log slow-query-log-file={$SERVER_APP_PATH}/data/mysql-slow.log
long_query_time=3 long_query_time=3
@@ -72,6 +74,9 @@ replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
replicate-ignore-db = test replicate-ignore-db = test
master_info_repository = table
relay_log_info_repository = table
default_storage_engine = InnoDB default_storage_engine = InnoDB
innodb_data_home_dir = {$SERVER_APP_PATH}/data innodb_data_home_dir = {$SERVER_APP_PATH}/data
innodb_data_file_path = ibdata1:10M:autoextend innodb_data_file_path = ibdata1:10M:autoextend
+6 -1
View File
@@ -16,6 +16,9 @@ mysqlx_port = 33260
socket = {$SERVER_APP_PATH}/mysql.sock socket = {$SERVER_APP_PATH}/mysql.sock
datadir = {$SERVER_APP_PATH}/data datadir = {$SERVER_APP_PATH}/data
log-error = {$SERVER_APP_PATH}/data/error.log log-error = {$SERVER_APP_PATH}/data/error.log
server-id = {$SERVER_ID}
sql-mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
default_storage_engine = InnoDB default_storage_engine = InnoDB
key_buffer_size = 8M key_buffer_size = 8M
@@ -43,7 +46,6 @@ max_allowed_packet = 128M
log-bin=mysql-bin log-bin=mysql-bin
binlog_format=mixed binlog_format=mixed
server-id = 1
slow_query_log=1 slow_query_log=1
slow-query-log-file={$SERVER_APP_PATH}/data/mysql-slow.log slow-query-log-file={$SERVER_APP_PATH}/data/mysql-slow.log
long_query_time=3 long_query_time=3
@@ -68,6 +70,9 @@ replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
replicate-ignore-db = test replicate-ignore-db = test
master_info_repository = table
relay_log_info_repository = table
default_storage_engine = InnoDB default_storage_engine = InnoDB
innodb_data_home_dir = {$SERVER_APP_PATH}/data innodb_data_home_dir = {$SERVER_APP_PATH}/data
innodb_data_file_path = ibdata1:10M:autoextend innodb_data_file_path = ibdata1:10M:autoextend
+1 -1
View File
@@ -15,7 +15,7 @@
<p onclick="dbList()">管理列表</p> <p onclick="dbList()">管理列表</p>
<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p> <p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p>
</div> </div>
<div class="bt-w-con pd15"> <div class="bt-w-con pd15" style="height:555px;overflow: auto;">
<div class="soft-man-con"></div> <div class="soft-man-con"></div>
</div> </div>
</div> </div>
+219 -152
View File
@@ -36,6 +36,10 @@ def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName() return mw.getPluginDir() + '/' + getPluginName()
def getSPluginDir():
return '/www/server/mdserver-web/plugins/' + getPluginName()
def getServerDir(): def getServerDir():
return mw.getServerDir() + '/' + getPluginName() return mw.getServerDir() + '/' + getPluginName()
@@ -101,6 +105,14 @@ def getDbPort():
return tmp.groups()[0].strip() return tmp.groups()[0].strip()
def getDbServerId():
file = getConf()
content = mw.readFile(file)
rep = 'server-id\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
def getSocketFile(): def getSocketFile():
file = getConf() file = getConf()
content = mw.readFile(file) content = mw.readFile(file)
@@ -123,6 +135,9 @@ def contentReplace(content):
content = content.replace('{$SERVER_PATH}', service_path) content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$SERVER_APP_PATH}', content = content.replace('{$SERVER_APP_PATH}',
service_path + '/' + getPluginName()) service_path + '/' + getPluginName())
server_id = int(time.time())
content = content.replace('{$SERVER_ID}', str(server_id))
return content return content
@@ -640,15 +655,15 @@ def isSqlError(mysqlMsg):
return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)") return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
if "using password:" in mysqlMsg: if "using password:" in mysqlMsg:
return mw.returnJson(False, '数据库密码错误,在管理列表-点击【修复】!') return mw.returnJson(False, '数据库密码错误,在管理列表-点击【修复】!')
if "1045" in mysqlMsg: if "1045," in mysqlMsg:
return mw.returnJson(False, '连接错误!') return mw.returnJson(False, '连接错误!')
if "SQL syntax" in mysqlMsg: if "SQL syntax" in mysqlMsg:
return mw.returnJson(False, 'SQL语法错误!') return mw.returnJson(False, 'SQL语法错误!')
if "Connection refused" in mysqlMsg: if "Connection refused" in mysqlMsg:
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!') return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
if "1133" in mysqlMsg: if "1133," in mysqlMsg:
return mw.returnJson(False, '数据库用户不存在!') return mw.returnJson(False, '数据库用户不存在!')
if "1007" in mysqlMsg: if "1007," in mysqlMsg:
return mw.returnJson(False, '数据库已经存在!') return mw.returnJson(False, '数据库已经存在!')
return None return None
@@ -1863,27 +1878,34 @@ def getMasterRepSlaveUserCmd(version):
mode = recognizeDbMode() mode = recognizeDbMode()
sid = getDbServerId()
channel_name = ""
if sid != '':
channel_name = " for channel 'r{}';".format(sid)
if mode == "gtid": if mode == "gtid":
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \ clist[0]['username'] + "', MASTER_PASSWORD='" + \
clist[0]['password'] + "', MASTER_AUTO_POSITION=1" clist[0]['password'] + "', MASTER_AUTO_POSITION=1" + channel_name
if version == '8.0': if version == '8.0':
sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \ sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \
clist[0]['username'] + "', SOURCE_PASSWORD='" + \ clist[0]['username'] + "', SOURCE_PASSWORD='" + \
clist[0]['password'] + "', MASTER_AUTO_POSITION=1" clist[0]['password'] + \
"', MASTER_AUTO_POSITION=1" + channel_name
else: else:
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \ clist[0]['username'] + "', MASTER_PASSWORD='" + \
clist[0]['password'] + \ clist[0]['password'] + \
"', MASTER_LOG_FILE='" + mstatus[0]["File"] + \ "', MASTER_LOG_FILE='" + mstatus[0]["File"] + \
"',MASTER_LOG_POS=" + str(mstatus[0]["Position"]) "',MASTER_LOG_POS=" + str(mstatus[0]["Position"]) + channel_name
if version == "8.0": if version == "8.0":
sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \ sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \
clist[0]['username'] + "', SOURCE_PASSWORD='" + \ clist[0]['username'] + "', SOURCE_PASSWORD='" + \
clist[0]['password'] + \ clist[0]['password'] + \
"', SOURCE_LOG_FILE='" + mstatus[0]["File"] + \ "', SOURCE_LOG_FILE='" + mstatus[0]["File"] + \
"',SOURCE_LOG_POS=" + str(mstatus[0]["Position"]) "',SOURCE_LOG_POS=" + \
str(mstatus[0]["Position"]) + channel_name
data = {} data = {}
data['cmd'] = sql data['cmd'] = sql
@@ -2154,22 +2176,10 @@ def updateSlaveSSH(version=''):
def getSlaveList(version=''): def getSlaveList(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') 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['data'] = ret data['data'] = dlist
return mw.getJson(data) return mw.getJson(data)
@@ -2193,100 +2203,149 @@ def initSlaveStatus(version=''):
return initSlaveStatusSyncUser(version) return initSlaveStatusSyncUser(version)
def parseSlaveSyncCmd(cmd):
a = {}
if cmd.lower().find('for') > 0:
cmd_tmp = cmd.split('for')
cmd = cmd_tmp[0].strip()
pattern_c = r"channel \'(.*)\';"
match_val = re.match(pattern_c, cmd_tmp[1].strip(), re.I)
if match_val:
m_groups = match_val.groups()
a['channel'] = m_groups[0]
vlist = cmd.split(',')
for i in vlist:
tmp = i.strip()
tmp_a = tmp.split(" ")
real_tmp = tmp_a[len(tmp_a) - 1]
kv = real_tmp.split("=")
a[kv[0]] = kv[1].replace("'", '').replace("'", '').replace(";", '')
return a
def initSlaveStatusSyncUser(version=''): def initSlaveStatusSyncUser(version=''):
conn = pSqliteDb('slave_sync_user') conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find() slave_data = conn.field('ip,port,user,pass,mode,cmd').select()
if len(data) < 1: if len(slave_data) < 1:
return mw.returnJson(False, '需要先添加同步用户配置!') return mw.returnJson(False, '需要先添加同步用户配置!')
# print(data) # print(data)
db = pMysqlDb() pdb = pMysqlDb()
dlist = db.query('show slave status') if len(slave_data) == 1:
if len(dlist) > 0: dlist = pdb.query('show slave status')
return mw.returnJson(False, '已经初始化好了zz...') if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...')
u = data
mode_name = 'classic'
if u['mode'] == '1':
mode_name = 'gtid'
msg = ''
local_mode = recognizeDbMode() local_mode = recognizeDbMode()
if local_mode != mode_name: for x in range(len(slave_data)):
return mw.returnJson(False, '同步模式不一致!') slave_t = slave_data[x]
mode_name = 'classic'
base_t = 'IP:' + slave_t['ip'] + ",PORT:" + \
slave_t['port'] + ",USER:" + slave_t['user']
t = db.query(u['cmd']) if slave_t['mode'] == '1':
# print(t) mode_name = 'gtid'
db.query("start slave user='{}' password='{}';".format(
u['user'], u['pass'])) if local_mode != mode_name:
return mw.returnJson(True, '初始化成功!') msg += base_t + '->同步模式不一致'
continue
cmd_sql = slave_t['cmd']
if cmd_sql == '':
msg += base_t + '->同步命令不能为空'
continue
try:
pinfo = parseSlaveSyncCmd(cmd_sql)
except Exception as e:
return mw.returnJson(False, base_t + '->CMD同步命令不合规范!')
# print(u['cmd'])
t = pdb.query(cmd_sql)
isError = isSqlError(t)
if isError:
return isError
# pdb.query("start slave user='{}' password='{}';".format(
# u['user'], u['pass']))
pdb.query("start slave")
pdb.query("start all slaves")
if msg == '':
msg = '初始化成功!'
return mw.returnJson(True, msg)
def initSlaveStatusSSH(version=''): def initSlaveStatusSSH(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...')
conn = pSqliteDb('slave_id_rsa') conn = pSqliteDb('slave_id_rsa')
data = conn.field('ip,port,id_rsa').find() ssh_list = conn.field('ip,port,id_rsa,db_user').select()
if len(data) < 1: if len(ssh_list) < 1:
return mw.returnJson(False, '需要先配置【[主]SSH配置】!') return mw.returnJson(False, '需要先配置【[主]SSH配置】!')
SSH_PRIVATE_KEY = "/tmp/t_ssh.txt" local_mode = recognizeDbMode()
ip = data['ip']
master_port = data['port']
mw.writeFile(SSH_PRIVATE_KEY, data['id_rsa'].replace('\\n', '\n'))
import paramiko import paramiko
paramiko.util.log_to_file('paramiko.log') paramiko.util.log_to_file('paramiko.log')
ssh = paramiko.SSHClient() ssh = paramiko.SSHClient()
try: db.query('stop slave')
db.query('reset slave all')
for data in ssh_list:
ip = data['ip']
SSH_PRIVATE_KEY = "/tmp/t_ssh_" + ip + ".txt"
master_port = data['port']
mw.writeFile(SSH_PRIVATE_KEY, data['id_rsa'].replace('\\n', '\n'))
mw.execShell("chmod 600 " + SSH_PRIVATE_KEY) mw.execShell("chmod 600 " + SSH_PRIVATE_KEY)
key = paramiko.RSAKey.from_private_key_file(SSH_PRIVATE_KEY) try:
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) key = paramiko.RSAKey.from_private_key_file(SSH_PRIVATE_KEY)
ssh.connect(hostname=ip, port=int(master_port), ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
username='root', pkey=key) ssh.connect(hostname=ip, port=int(master_port),
username='root', pkey=key)
cmd = 'cd /www/server/mdserver-web && python3 plugins/mysql/index.py get_master_rep_slave_user_cmd {"username":"","db":""}' db_user = data['db_user']
stdin, stdout, stderr = ssh.exec_command(cmd) cmd = 'cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql-yum/index.py get_master_rep_slave_user_cmd {"username":"' + db_user + '","db":""}'
result = stdout.read() stdin, stdout, stderr = ssh.exec_command(cmd)
result = result.decode('utf-8') result = stdout.read()
cmd_data = json.loads(result) result = result.decode('utf-8')
if result.strip() == "":
return mw.returnJson(False, '[主][' + ip + ']:获取同步命令失败!')
if not cmd_data['status']: cmd_data = json.loads(result)
return mw.returnJson(False, '[主]:' + cmd_data['msg']) if not cmd_data['status']:
return mw.returnJson(False, '[主][' + ip + ']:' + cmd_data['msg'])
local_mode = recognizeDbMode() if local_mode != cmd_data['data']['mode']:
if local_mode != cmd_data['data']['mode']: return mw.returnJson(False, '[主][' + ip + ']:【{}】从【{}】,运行模式不一致!'.format(cmd_data['data']['mode'], local_mode))
return mw.returnJson(False, '主【{}】从【{}】,运行模式不一致!'.format(cmd_data['data']['mode'], local_mode))
u = cmd_data['data']['info'] u = cmd_data['data']['info']
ps = u['username'] + "|" + u['password']
conn.where('ip=?', (ip,)).setField('ps', ps)
db.query('stop slave')
# 保证同步IP一致 ps = u['username'] + "|" + u['password']
cmd = cmd_data['data']['cmd'] print(ps)
if cmd.find('SOURCE_HOST') > -1: conn.where('ip=?', (ip,)).setField('ps', ps)
cmd = re.sub(r"SOURCE_HOST='(.*)'", db.query('stop slave')
"SOURCE_HOST='" + ip + "'", cmd, 1)
if cmd.find('MASTER_HOST') > -1: # 保证同步IP一致
cmd = re.sub(r"MASTER_HOST='(.*)'", cmd = cmd_data['data']['cmd']
"MASTER_HOST='" + ip + "'", cmd, 1) if cmd.find('SOURCE_HOST') > -1:
db.query(cmd) cmd = re.sub(r"SOURCE_HOST='(.*?)'",
db.query("start slave user='{}' password='{}';".format( "SOURCE_HOST='" + ip + "'", cmd, 1)
u['username'], u['password']))
except Exception as e:
return mw.returnJson(False, 'SSH认证配置连接失败!' + str(e))
ssh.close() if cmd.find('MASTER_HOST') > -1:
time.sleep(1) cmd = re.sub(r"MASTER_HOST='(.*?)'",
os.system("rm -rf " + SSH_PRIVATE_KEY) "MASTER_HOST='" + ip + "'", cmd, 1)
db.query(cmd)
ssh.close()
if os.path.exists(SSH_PRIVATE_KEY):
os.system("rm -rf " + SSH_PRIVATE_KEY)
except Exception as e:
return mw.returnJson(False, '[主][' + ip + ']:SSH认证配置连接失败!' + str(e))
db.query('start slave')
return mw.returnJson(True, '初始化成功!') return mw.returnJson(True, '初始化成功!')
@@ -2296,60 +2355,37 @@ def setSlaveStatus(version=''):
return mw.returnJson(False, '需要先设置同步配置') return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file) mode = mw.readFile(mode_file)
if mode == 'ssh': pdb = pMysqlDb()
return setSlaveStatusSSH(version) dlist = pdb.query('show slave status')
if mode == 'sync-user':
return setSlaveStatusSyncUser(version)
def setSlaveStatusSyncUser(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0: if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!') return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'): for v in dlist:
db.query('stop slave') connection_name = ''
else: cmd = "slave"
ip = dlist[0]['Master_Host'] if 'Channel_Name' in v:
conn = pSqliteDb('slave_sync_user') ch_name = v['Channel_Name']
data = conn.field('ip,port,user,pass,mode,cmd').find() cmd = "slave for channel '{}'".format(ch_name)
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
user = data['user']
apass = data['pass']
db.query("start slave") if (v["Slave_IO_Running"] == 'Yes' or v["Slave_SQL_Running"] == 'Yes'):
# db.query("start slave user='{}' password='{}';".format(user, apass)) pdb.query("stop {}".format(cmd))
else:
return mw.returnJson(True, '设置成功!') pdb.query("start {}".format(cmd))
def setSlaveStatusSSH(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加主服务命令或者执行[初始化]!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
db.query('stop slave')
else:
ip = dlist[0]['Master_Host']
conn = pSqliteDb('slave_id_rsa')
data = conn.field('ip,ps').where("ip=?", (ip,)).find()
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
u = data['ps'].split("|")
db.query("start slave user='{}' password='{}';".format(u[0], u[1]))
return mw.returnJson(True, '设置成功!') return mw.returnJson(True, '设置成功!')
def deleteSlave(version=''): def deleteSlave(version=''):
args = getArgs()
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('stop slave') if 'sign' in args:
dlist = db.query('reset slave all') sign = args['sign']
db.query("stop slave for channel '{}'".format(sign))
db.query("reset slave all for channel '{}'".format(sign))
else:
db.query('stop slave')
db.query('reset slave all')
return mw.returnJson(True, '删除成功!') return mw.returnJson(True, '删除成功!')
@@ -2385,11 +2421,13 @@ def dumpMysqlData(version=''):
############### --- 重要 同步---- ########### ############### --- 重要 同步---- ###########
def asyncTmpfile():
path = '/tmp/mysql_yum_async_status.txt'
return path
def writeDbSyncStatus(data): def writeDbSyncStatus(data):
path = '/tmp/db_async_status.txt' path = asyncTmpfile()
# status_data['code'] = 1
# status_data['msg'] = '主服务器备份完成...'
# status_data['progress'] = 30
mw.writeFile(path, json.dumps(data)) mw.writeFile(path, json.dumps(data))
@@ -2407,16 +2445,22 @@ def doFullSync(version=''):
def doFullSyncUser(version=''): def doFullSyncUser(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['db']) data = checkArgs(args, ['db', 'sign'])
if not data[0]: if not data[0]:
return data[1] return data[1]
sync_db = args['db'] sync_db = args['db']
sync_sign = args['sign']
db = pMysqlDb() db = pMysqlDb()
conn = pSqliteDb('slave_sync_user') conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find() if sync_sign != '':
data = conn.field('ip,port,user,pass,mode,cmd').where(
'ip=?', (sync_sign,)).find()
else:
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user'] user = data['user']
apass = data['pass'] apass = data['pass']
port = data['port'] port = data['port']
@@ -2433,16 +2477,17 @@ def doFullSyncUser(version=''):
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20}) writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
if not os.path.exists(bak_file): if not os.path.exists(bak_file):
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \ dump_sql_data = getServerDir() + "/bin/usr/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
port + " -u" + user + " -p" + apass + \ port + " -u" + user + " -p\"" + apass + \
" --ssl-mode=DISABLED " + sync_db + " > " + bak_file "\" --ssl-mode=DISABLED " + sync_db + " > " + bak_file
# print(dump_sql_data)
mw.execShell(dump_sql_data) mw.execShell(dump_sql_data)
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40}) writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
if os.path.exists(bak_file): if os.path.exists(bak_file):
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root') pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile() sock = getSocketFile()
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \ my_import_cmd = getServerDir() + '/bin/usr/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
' ' + sync_db + ' < ' + bak_file ' ' + sync_db + ' < ' + bak_file
mw.execShell(my_import_cmd) mw.execShell(my_import_cmd)
@@ -2452,21 +2497,30 @@ def doFullSyncUser(version=''):
db.query("start slave") db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100}) writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + bak_file)
if os.path.exists(bak_file):
os.system("rm -rf " + bak_file)
return True return True
def doFullSyncSSH(version=''): def doFullSyncSSH(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['db']) data = checkArgs(args, ['db', 'sign'])
if not data[0]: if not data[0]:
return data[1] return data[1]
sync_db = args['db']
sync_sign = args['sign']
db = pMysqlDb() db = pMysqlDb()
id_rsa_conn = pSqliteDb('slave_id_rsa') id_rsa_conn = pSqliteDb('slave_id_rsa')
data = id_rsa_conn.field('ip,port,db_user,id_rsa').find() if sync_sign != '':
data = id_rsa_conn.field('ip,port,db_user,id_rsa').where(
'ip=?', (sync_sign,)).find()
else:
data = id_rsa_conn.field('ip,port,db_user,id_rsa').find()
SSH_PRIVATE_KEY = "/tmp/mysql_sync_id_rsa.txt" SSH_PRIVATE_KEY = "/tmp/mysql_sync_id_rsa.txt"
id_rsa = data['id_rsa'].replace('\\n', '\n') id_rsa = data['id_rsa'].replace('\\n', '\n')
@@ -2508,7 +2562,8 @@ def doFullSyncSSH(version=''):
writeDbSyncStatus({'code': 0, 'msg': '登录Master成功...', 'progress': 5}) writeDbSyncStatus({'code': 0, 'msg': '登录Master成功...', 'progress': 5})
dbname = args['db'] dbname = args['db']
cmd = "cd /www/server/mdserver-web && python3 plugins/mysql/index.py dump_mysql_data {\"db\":'" + dbname + "'}" cmd = "cd /www/server/mdserver-web && source bin/activate && python3 " + \
getSPluginDir() + "/index.py dump_mysql_data {\"db\":'" + dbname + "'}"
print(cmd) print(cmd)
stdin, stdout, stderr = ssh.exec_command(cmd) stdin, stdout, stderr = ssh.exec_command(cmd)
result = stdout.read() result = stdout.read()
@@ -2531,7 +2586,10 @@ def doFullSyncSSH(version=''):
if copy_status == None: if copy_status == None:
writeDbSyncStatus({'code': 2, 'msg': '数据同步本地完成...', 'progress': 40}) writeDbSyncStatus({'code': 2, 'msg': '数据同步本地完成...', 'progress': 40})
cmd = 'cd /www/server/mdserver-web && python3 plugins/mysql/index.py get_master_rep_slave_user_cmd {"username":"' + db_user + '","db":""}' cmd = 'cd /www/server/mdserver-web && source bin/activate && python3 ' + \
getSPluginDir() + \
'/index.py get_master_rep_slave_user_cmd {"username":"' + \
db_user + '","db":""}'
stdin, stdout, stderr = ssh.exec_command(cmd) stdin, stdout, stderr = ssh.exec_command(cmd)
result = stdout.read() result = stdout.read()
result = result.decode('utf-8') result = result.decode('utf-8')
@@ -2543,10 +2601,12 @@ def doFullSyncSSH(version=''):
cmd = cmd_data['data']['cmd'] cmd = cmd_data['data']['cmd']
# 保证同步IP一致 # 保证同步IP一致
if cmd.find('SOURCE_HOST') > -1: if cmd.find('SOURCE_HOST') > -1:
cmd = re.sub(r"SOURCE_HOST='(.*)'", "SOURCE_HOST='" + ip + "'", cmd, 1) cmd = re.sub(r"SOURCE_HOST='(.*?)'",
"SOURCE_HOST='" + ip + "'", cmd, 1)
if cmd.find('MASTER_HOST') > -1: if cmd.find('MASTER_HOST') > -1:
cmd = re.sub(r"MASTER_HOST='(.*)'", "SOURCE_HOST='" + ip + "'", cmd, 1) cmd = re.sub(r"MASTER_HOST='(.*?)'",
"MASTER_HOST='" + ip + "'", cmd, 1)
db.query(cmd) db.query(cmd)
uinfo = cmd_data['data']['info'] uinfo = cmd_data['data']['info']
@@ -2558,8 +2618,10 @@ def doFullSyncSSH(version=''):
root_dir = getServerDir() root_dir = getServerDir()
msock = root_dir + "/mysql.sock" msock = root_dir + "/mysql.sock"
mw.execShell("cd /tmp && gzip -d dump.sql.gz") mw.execShell("cd /tmp && gzip -d dump.sql.gz")
cmd = root_dir + "/bin/mysql -S " + msock + \ cmd = root_dir + "/bin/usr/bin/mysql -S " + msock + \
" -uroot -p" + pwd + " < /tmp/dump.sql" " -uroot -p" + pwd + " < /tmp/dump.sql"
print(cmd)
import_data = mw.execShell(cmd) import_data = mw.execShell(cmd)
if import_data[0] == '': if import_data[0] == '':
print(import_data[1]) print(import_data[1])
@@ -2569,8 +2631,9 @@ def doFullSyncSSH(version=''):
writeDbSyncStatus({'code': 5, 'msg': '导入数据失败...', 'progress': 100}) writeDbSyncStatus({'code': 5, 'msg': '导入数据失败...', 'progress': 100})
return 'fail' return 'fail'
db.query("start slave user='{}' password='{}';".format( # "start slave user='{}' password='{}';".format(uinfo['username'], uinfo['password'])
uinfo['username'], uinfo['password']))
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100}) writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + SSH_PRIVATE_KEY) os.system("rm -rf " + SSH_PRIVATE_KEY)
@@ -2584,12 +2647,16 @@ def fullSync(version=''):
if not data[0]: if not data[0]:
return data[1] return data[1]
status_file = '/tmp/db_async_status.txt' sign = ''
if 'sign' in args:
sign = args['sign']
status_file = asyncTmpfile()
if args['begin'] == '1': if args['begin'] == '1':
cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + \ cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + getPluginDir() + \
getPluginDir() + \ '/index.py do_full_sync {"db":"' + \
'/index.py do_full_sync {"db":"' + args['db'] + '"} &' args['db'] + '","sign":"' + sign + '"} &'
print(cmd) # print(cmd)
mw.execShell(cmd) mw.execShell(cmd)
return json.dumps({'code': 0, 'msg': '同步数据中!', 'progress': 0}) return json.dumps({'code': 0, 'msg': '同步数据中!', 'progress': 0})
+164 -80
View File
@@ -162,34 +162,6 @@ function myPort(){
}); });
} }
//数据库存储信置
function changeMySQLDataPath(act) {
if (act != undefined) {
layer.confirm(lan.soft.mysql_to_msg, { closeBtn: 2, icon: 3 }, function() {
var datadir = $("#datadir").val();
var data = 'datadir=' + datadir;
var loadT = layer.msg(lan.soft.mysql_to_msg1, { icon: 16, time: 0, shade: [0.3, '#000'] });
$.post('/database?action=SetDataDir', data, function(rdata) {
layer.close(loadT)
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 });
});
});
return;
}
$.post('/database?action=GetMySQLInfo', '', function(rdata) {
var LimitCon = '<p class="conf_p">\
<input id="datadir" class="phpUploadLimit bt-input-text mr5" style="width:350px;" type="text" value="' + rdata.datadir + '" name="datadir">\
<span onclick="ChangePath(\'datadir\')" class="glyphicon glyphicon-folder-open cursor mr20" style="width:auto"></span><button class="btn btn-success btn-sm" onclick="changeMySQLDataPath(1)">' + lan.soft.mysql_to + '</button>\
</p>';
$(".soft-man-con").html(LimitCon);
});
}
//数据库配置状态 //数据库配置状态
function myPerfOpt() { function myPerfOpt() {
//获取MySQL配置 //获取MySQL配置
@@ -1691,12 +1663,12 @@ function getMasterRepSlaveListPage(){
} }
function deleteSlave(){ function deleteSlave(sign){
myPost('delete_slave', {}, function(data){ myPost('delete_slave', {sign:sign}, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
showMsg(rdata['msg'], function(){ showMsg(rdata['msg'], function(){
masterOrSlaveConf(); masterOrSlaveConf();
},{},3000); },{icon:rdata.status?1:2,time:3000},3000);
}); });
} }
@@ -1704,28 +1676,73 @@ function deleteSlave(){
function getFullSyncStatus(db){ function getFullSyncStatus(db){
var timeId = null; var timeId = null;
var btn = '<div class="table_toolbar" style="left:0px;"><span data-status="init" class="sync btn btn-default btn-sm" id="begin_full_sync" title="">开始</span></div>'; myPost('get_slave_list', {page:1,page_size:100}, function(data){
var loadOpen = layer.open({ var rdata = $.parseJSON(data.data);
type: 1, var rsource = rdata.data;
title: '全量同步['+db+']',
area: '500px', if (db == 'ALL' && rsource.length>1){
content:"<div class='bt-form pd20 c6'>\ layer.msg("多主不支持该模式!",{icon:2});
<div class='divtable mtb10'>\ return;
<span id='full_msg'></span>\
<div class='progress'>\
<div class='progress-bar' role='progressbar' aria-valuenow='0' aria-valuemin='0' aria-valuemax='100' style='min-width: 2em;'>0%</div>\
</div>\
</div>\
"+btn+"\
</div>",
cancel: function(){
clearInterval(timeId);
} }
var dataSource = '';
if (rsource.length>1){
var sourceList = '';
for (var i = 0; i < rsource.length; i++) {
if ('Channel_Name' in rsource[i]){
sourceList += '<option val="'+rsource[i]['Master_Host']+'">'+rsource[i]['Master_Host']+'</option>';
}
}
dataSource = "<p class='line' style='text-align:center;'>\
<span>同步数据源:</span>\
<select class='bt-input-text' name='data_source' style='width:200px;'>" + sourceList + "</select>\
</p>";
}
layer.open({
type: 1,
title: '全量同步['+db+']',
area: '500px',
content:"<div class='bt-form pd15'>\
<div class='divtable mtb10'>\
"+dataSource+"\
<span id='full_msg'></span>\
<div class='progress'>\
<div class='progress-bar' role='progressbar' aria-valuenow='0' aria-valuemin='0' aria-valuemax='100' style='min-width: 2em;'>0%</div>\
</div>\
</div>\
<div class='table_toolbar' style='left:0px;'>\
<span data-status='init' class='sync btn btn-default btn-sm' id='begin_full_sync'>开始</span>\
</div>\
</div>",
cancel: function(){
clearInterval(timeId);
},
success:function(){
$('#begin_full_sync').click(function(){
var val = $(this).data('status');
var sign = '';
if (dataSource !=''){
sign = $('select[name="data_source"]').val();
}
if (val == 'init'){
fullSync(db, sign, 1);
timeId = setInterval(function(){
fullSync(db,sign,0);
}, 1000);
$(this).data('status','starting');
} else {
layer.msg("正在同步中..",{icon:0});
}
});
}
});
}); });
function fullSync(db,begin){ function fullSync(db,sign,begin){
myPostN('full_sync', {db:db,begin:begin}, function(data){ myPostN('full_sync', {db:db,sign:sign,begin:begin}, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
$('#full_msg').text(rdata['msg']); $('#full_msg').text(rdata['msg']);
$('.progress-bar').css('width',rdata['progress']+'%'); $('.progress-bar').css('width',rdata['progress']+'%');
@@ -1738,19 +1755,6 @@ function getFullSyncStatus(db){
} }
}); });
} }
$('#begin_full_sync').click(function(){
var val = $(this).attr('data-status');
if (val == 'init'){
fullSync(db,1);
timeId = setInterval(function(){
fullSync(db,0);
}, 1000);
$(this).attr('data-status','starting');
} else {
layer.msg("正在同步中..");
}
});
} }
function addSlaveSSH(ip=''){ function addSlaveSSH(ip=''){
@@ -1920,18 +1924,29 @@ function addSlaveSyncUser(ip=''){
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\ <div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\ <div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
<div class='line'>\ <div class='line'>\
<span class='tname'>CMD[最好填好]</span>\ <span class='tname'>CMD[必须填写]</span>\
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\ <div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
</div>\ </div>\
<input type='hidden' name='mode' value='"+mode+"' />\ <input type='hidden' name='mode' value='"+mode+"' />\
</form>", </form>",
success:function(){ success:function(){
$('textarea[name="cmd"]').html(cmd); $('textarea[name="cmd"]').html(cmd);
$('textarea[name="cmd"]').change(function(){ $('textarea[name="cmd"]').change(function(){
var val = $(this).val(); var val = $(this).val();
var vlist = val.split(',');
var a = {}; var a = {};
if (val.toLowerCase().indexOf('for')>0){
cmd_tmp = val.split('for');
val = cmd_tmp[0].trim();
const channel_str = cmd_tmp[1].trim();
const ch_reg = /channel \'(.*)\';/;
var match_val = channel_str.match(ch_reg);
if (match_val.length>1){
a['channel'] = match_val[1];
}
}
var vlist = val.split(',');
for (var i in vlist) { for (var i in vlist) {
var tmp = toTrim(vlist[i]); var tmp = toTrim(vlist[i]);
var tmp_a = tmp.split(" "); var tmp_a = tmp.split(" ");
@@ -2098,7 +2113,7 @@ function getSlaveUserList(){
layerId = layer.open({ layerId = layer.open({
type: 1, type: 1,
title: '同步账户列表', title: '同步账户列表',
area: '500px', area: '600px',
content:"<div class='bt-form pd20 c6'>\ content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\ <div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\ <div><table class='table table-hover get-slave-ssh-list'>\
@@ -2122,7 +2137,7 @@ function getSlaveSSHList(page=1){
layerId = layer.open({ layerId = layer.open({
type: 1, type: 1,
title: 'SSH列表', title: 'SSH列表',
area: '500px', area: '600px',
content:"<div class='bt-form pd20 c6'>\ content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\ <div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\ <div><table class='table table-hover get-slave-ssh-list'>\
@@ -2230,10 +2245,16 @@ function masterOrSlaveConf(version=''){
myPost('get_slave_list', _data, function(data){ myPost('get_slave_list', _data, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
var list = ''; var list = '';
var isHasSign = false;
for(i in rdata.data){ for(i in rdata.data){
var v = rdata.data[i]; var v = rdata.data[i];
var status = "<a class='btlink db_error'>异常</>"; if ('Channel_Name' in v){
isHasSign = true;
}
var status = "<a data-id="+i+" class='btlink db_error'>异常</>";
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){ if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
status = "正常"; status = "正常";
} }
@@ -2245,13 +2266,23 @@ function masterOrSlaveConf(version=''){
list += '<td>' + rdata.data[i]['Master_Log_File'] +'</td>'; list += '<td>' + rdata.data[i]['Master_Log_File'] +'</td>';
list += '<td>' + rdata.data[i]['Slave_IO_Running'] +'</td>'; list += '<td>' + rdata.data[i]['Slave_IO_Running'] +'</td>';
list += '<td>' + rdata.data[i]['Slave_SQL_Running'] +'</td>'; list += '<td>' + rdata.data[i]['Slave_SQL_Running'] +'</td>';
if (isHasSign){
list += '<td>' + v['Channel_Name'] +'</td>';
}
list += '<td>' + status +'</td>'; list += '<td>' + status +'</td>';
list += '<td style="text-align:right">' + list += '<td style="text-align:right">' +
'<a href="javascript:;" class="btlink" onclick="deleteSlave()" title="删除">删除</a>' + '<a data-id="'+i+'" href="javascript:;" class="btlink btn_delete_slave" title="删除">删除</a>' +
'</td>'; '</td>';
list += '</tr>'; list += '</tr>';
} }
var signThead_th = '';
if (isHasSign){
var signThead_th = '<th>标识</th>';
}
var con = '<div class="divtable mtb10">\ var con = '<div class="divtable mtb10">\
<div class="tablescroll">\ <div class="tablescroll">\
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\ <table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
@@ -2262,6 +2293,7 @@ function masterOrSlaveConf(version=''){
<th>日志</th>\ <th>日志</th>\
<th>IO</th>\ <th>IO</th>\
<th>SQL</th>\ <th>SQL</th>\
'+signThead_th+'\
<th>状态</th>\ <th>状态</th>\
<th style="text-align:right;">操作</th></tr></thead>\ <th style="text-align:right;">操作</th></tr></thead>\
<tbody>\ <tbody>\
@@ -2276,22 +2308,74 @@ function masterOrSlaveConf(version=''){
// </div> // </div>
$(".table_slave_status_list").html(con); $(".table_slave_status_list").html(con);
$('.db_error').click(function(){ $(".btn_delete_slave").click(function(){
var id = $(this).data('id');
var v = rdata.data[id];
if ('Channel_Name' in v){
deleteSlave(v['Channel_Name']);
} else{
deleteSlave();
}
});
$('.db_error').click(function(){
var id = $(this).data('id');
var info = rdata.data[id];
var err_line = "";
err_line +="<tr>\
<td>IO错误</td>\
<td>"+ (info['Last_IO_Error'] == '' ? '无':info['Last_IO_Error'])+"</td>\
</tr>";
err_line +="<tr>\
<td>SQL错误</td>\
<td>"+(info['Last_SQL_Error'] == '' ? '无':info['Last_SQL_Error'])+"</td>\
</tr>";
err_line +="<tr>\
<td>状态</td>\
<td>"+(info['Slave_SQL_Running_State'] == '' ? '无':info['Slave_SQL_Running_State']) +"</td>\
</tr>";
layer.open({ layer.open({
type: 1, type: 1,
title: '同步异常信息', title: '同步异常信息',
area: '500px', area: ['600px','300px'],
content:"<form class='bt-form pd20 pb70'>\ btn:['复制错误',"取消"],
<div class='line'>"+v['Error']+"</div>\ content:"<form class='bt-form pd15'>\
<div class='bt-form-submit-btn'>\ <div class='divtable mtb10'>\
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\ <div class='tablescroll'>\
<table class='table table-hover' width='100%' cellspacing='0' cellpadding='0' border='0' style='border: 0 none;'>\
<thead><tr>\
<th style='width:80px;'>类型</th>\
<th>内容</th>\
</tr></thead>\
<tbody>"+ err_line +"</tbody>\
</table>\
</div>\
</div>\ </div>\
</form>", </form>",
success:function(){ success:function(){
copyText(v['Error']); // copyText(v['Error']);
$('.class-copy-db-err').click(function(){ // $('.class-copy-db-err').click(function(){
copyText(v['Error']); // copyText(v['Error']);
}); // });
},
yes:function(){
if (info['Last_IO_Error'] != ''){
copyText(info['Last_IO_Error']);
return;
}
if (info['Last_SQL_Error'] != ''){
copyText(info['Last_SQL_Error']);
return;
}
if (info['Slave_SQL_Running_State'] != ''){
copyText(info['Slave_SQL_Running_State']);
return;
}
} }
}); });
}); });
+1
View File
@@ -18,6 +18,7 @@ basedir = {$SERVER_APP_PATH}
datadir = {$SERVER_APP_PATH}/data datadir = {$SERVER_APP_PATH}/data
log-error = {$SERVER_APP_PATH}/data/error.log log-error = {$SERVER_APP_PATH}/data/error.log
server-id = {$SERVER_ID} server-id = {$SERVER_ID}
sql-mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
default_storage_engine = InnoDB default_storage_engine = InnoDB
+3
View File
@@ -16,6 +16,7 @@ basedir = {$SERVER_APP_PATH}
datadir = {$SERVER_APP_PATH}/data datadir = {$SERVER_APP_PATH}/data
log-error = {$SERVER_APP_PATH}/data/error.log log-error = {$SERVER_APP_PATH}/data/error.log
server-id = {$SERVER_ID} server-id = {$SERVER_ID}
sql-mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
explicit_defaults_for_timestamp=true explicit_defaults_for_timestamp=true
default_storage_engine = InnoDB default_storage_engine = InnoDB
@@ -80,6 +81,8 @@ replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
replicate-ignore-db = test replicate-ignore-db = test
master_info_repository = table
relay_log_info_repository = table
innodb_data_home_dir = {$SERVER_APP_PATH}/data innodb_data_home_dir = {$SERVER_APP_PATH}/data
innodb_data_file_path = ibdata1:10M:autoextend innodb_data_file_path = ibdata1:10M:autoextend
+3
View File
@@ -20,6 +20,7 @@ basedir = {$SERVER_APP_PATH}
datadir = {$SERVER_APP_PATH}/data datadir = {$SERVER_APP_PATH}/data
log-error = {$SERVER_APP_PATH}/data/error.log log-error = {$SERVER_APP_PATH}/data/error.log
server-id = {$SERVER_ID} server-id = {$SERVER_ID}
sql-mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
default_storage_engine = InnoDB default_storage_engine = InnoDB
@@ -77,6 +78,8 @@ replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
replicate-ignore-db = test replicate-ignore-db = test
master_info_repository = table
relay_log_info_repository = table
innodb_data_home_dir = {$SERVER_APP_PATH}/data innodb_data_home_dir = {$SERVER_APP_PATH}/data
innodb_data_file_path = ibdata1:10M:autoextend innodb_data_file_path = ibdata1:10M:autoextend
+1 -1
View File
@@ -15,7 +15,7 @@
<p onclick="dbList()">管理列表</p> <p onclick="dbList()">管理列表</p>
<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p> <p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p>
</div> </div>
<div class="bt-w-con pd15"> <div class="bt-w-con pd15" style="height:555px;overflow: auto;">
<div class="soft-man-con"></div> <div class="soft-man-con"></div>
</div> </div>
</div> </div>
+211 -178
View File
@@ -36,6 +36,10 @@ def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName() return mw.getPluginDir() + '/' + getPluginName()
def getSPluginDir():
return '/www/server/mdserver-web/plugins/' + getPluginName()
def getServerDir(): def getServerDir():
return mw.getServerDir() + '/' + getPluginName() return mw.getServerDir() + '/' + getPluginName()
@@ -85,6 +89,14 @@ def getDbPort():
return tmp.groups()[0].strip() return tmp.groups()[0].strip()
def getDbServerId():
file = getConf()
content = mw.readFile(file)
rep = 'server-id\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
def getSocketFile(): def getSocketFile():
file = getConf() file = getConf()
content = mw.readFile(file) content = mw.readFile(file)
@@ -802,15 +814,15 @@ def isSqlError(mysqlMsg):
return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)") return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
if "using password:" in mysqlMsg: if "using password:" in mysqlMsg:
return mw.returnJson(False, '数据库密码错误,在管理列表-点击【修复】!') return mw.returnJson(False, '数据库密码错误,在管理列表-点击【修复】!')
if "1045" in mysqlMsg: if "1045," in mysqlMsg:
return mw.returnJson(False, '连接错误!') return mw.returnJson(False, '连接错误!')
if "SQL syntax" in mysqlMsg: if "SQL syntax" in mysqlMsg:
return mw.returnJson(False, 'SQL语法错误!') return mw.returnJson(False, 'SQL语法错误!')
if "Connection refused" in mysqlMsg: if "Connection refused" in mysqlMsg:
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!') return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
if "1133" in mysqlMsg: if "1133," in mysqlMsg:
return mw.returnJson(False, '数据库用户不存在!') return mw.returnJson(False, '数据库用户不存在!')
if "1007" in mysqlMsg: if "1007," in mysqlMsg:
return mw.returnJson(False, '数据库已经存在!') return mw.returnJson(False, '数据库已经存在!')
return None return None
@@ -1887,13 +1899,14 @@ def getMasterStatus(version=''):
data = {} data = {}
data['mode'] = recognizeDbMode() data['mode'] = recognizeDbMode()
data['status'] = master_status data['status'] = master_status
data['slave_status'] = False
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
# print(dlist[0]) for v in dlist:
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'): if v["Slave_IO_Running"] == 'Yes' or v["Slave_SQL_Running"] == 'Yes':
data['slave_status'] = True data['slave_status'] = True
return mw.returnJson(master_status, '设置成功', data) return mw.returnJson(master_status, '设置成功', data)
except Exception as e: except Exception as e:
@@ -2057,27 +2070,34 @@ def getMasterRepSlaveUserCmd(version):
mode = recognizeDbMode() mode = recognizeDbMode()
sid = getDbServerId()
channel_name = ""
if sid != '':
channel_name = " for channel 'r{}';".format(sid)
if mode == "gtid": if mode == "gtid":
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \ clist[0]['username'] + "', MASTER_PASSWORD='" + \
clist[0]['password'] + "', MASTER_AUTO_POSITION=1" clist[0]['password'] + "', MASTER_AUTO_POSITION=1" + channel_name
if version == '8.0': if version == '8.0':
sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \ sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \
clist[0]['username'] + "', SOURCE_PASSWORD='" + \ clist[0]['username'] + "', SOURCE_PASSWORD='" + \
clist[0]['password'] + "', MASTER_AUTO_POSITION=1" clist[0]['password'] + \
"', MASTER_AUTO_POSITION=1" + channel_name
else: else:
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \ clist[0]['username'] + "', MASTER_PASSWORD='" + \
clist[0]['password'] + \ clist[0]['password'] + \
"', MASTER_LOG_FILE='" + mstatus[0]["File"] + \ "', MASTER_LOG_FILE='" + mstatus[0]["File"] + \
"',MASTER_LOG_POS=" + str(mstatus[0]["Position"]) "',MASTER_LOG_POS=" + str(mstatus[0]["Position"]) + channel_name
if version == "8.0": if version == "8.0":
sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \ sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \
clist[0]['username'] + "', SOURCE_PASSWORD='" + \ clist[0]['username'] + "', SOURCE_PASSWORD='" + \
clist[0]['password'] + \ clist[0]['password'] + \
"', SOURCE_LOG_FILE='" + mstatus[0]["File"] + \ "', SOURCE_LOG_FILE='" + mstatus[0]["File"] + \
"',SOURCE_LOG_POS=" + str(mstatus[0]["Position"]) "',SOURCE_LOG_POS=" + \
str(mstatus[0]["Position"]) + channel_name
data = {} data = {}
data['cmd'] = sql data['cmd'] = sql
@@ -2348,48 +2368,17 @@ def updateSlaveSSH(version=''):
def getSlaveList(version=''): def getSlaveList(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') 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"]
tmp['Last_Error'] = dlist[x]["Last_Error"]
tmp['Last_IO_Error'] = dlist[x]["Last_IO_Error"]
tmp['Last_SQL_Error'] = dlist[x]["Last_SQL_Error"]
tmp['Slave_SQL_Running_State'] = dlist[x]["Slave_SQL_Running_State"]
tmp['Error'] = ''
if tmp['Last_Error'] != '':
tmp['Error'] = tmp['Last_Error']
if tmp['Last_IO_Error'] != '':
tmp['Error'] = tmp['Last_IO_Error']
if tmp['Last_SQL_Error'] != '':
tmp['Error'] = tmp['Last_SQL_Error']
if tmp['Error'] == '':
tmp['Error'] = tmp['Slave_SQL_Running_State']
ret.append(tmp)
data = {} data = {}
data['data'] = ret data['data'] = dlist
return mw.getJson(data) return mw.getJson(data)
def getSlaveSyncCmd(version=''): def getSlaveSyncCmd(version=''):
root = mw.getRunDir() root = mw.getRunDir()
cmd = 'cd ' + root + ' && python3 ' + root + \ cmd = 'cd ' + root + ' && python3 ' + root + \
'/plugins/mysql/index.py do_full_sync {"db":"all"}' '/plugins/mysql/index.py do_full_sync {"db":"all","sign",""}'
return mw.returnJson(True, 'ok', cmd) return mw.returnJson(True, 'ok', cmd)
@@ -2424,111 +2413,152 @@ def makeSyncUsercmd(u, version=''):
return sql return sql
def parseSlaveSyncCmd(cmd):
a = {}
if cmd.lower().find('for') > 0:
cmd_tmp = cmd.split('for')
cmd = cmd_tmp[0].strip()
pattern_c = r"channel \'(.*)\';"
match_val = re.match(pattern_c, cmd_tmp[1].strip(), re.I)
if match_val:
m_groups = match_val.groups()
a['channel'] = m_groups[0]
vlist = cmd.split(',')
for i in vlist:
tmp = i.strip()
tmp_a = tmp.split(" ")
real_tmp = tmp_a[len(tmp_a) - 1]
kv = real_tmp.split("=")
a[kv[0]] = kv[1].replace("'", '').replace("'", '').replace(";", '')
return a
def initSlaveStatusSyncUser(version=''): def initSlaveStatusSyncUser(version=''):
conn = pSqliteDb('slave_sync_user') conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find() slave_data = conn.field('ip,port,user,pass,mode,cmd').select()
if len(data) < 1: if len(slave_data) < 1:
return mw.returnJson(False, '需要先添加同步用户配置!') return mw.returnJson(False, '需要先添加同步用户配置!')
# print(data) # print(data)
db = pMysqlDb() pdb = pMysqlDb()
dlist = db.query('show slave status') if len(slave_data) == 1:
if len(dlist) > 0: dlist = pdb.query('show slave status')
return mw.returnJson(False, '已经初始化好了zz...') if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...')
u = data
mode_name = 'classic'
if u['mode'] == '1':
mode_name = 'gtid'
msg = ''
local_mode = recognizeDbMode() local_mode = recognizeDbMode()
if local_mode != mode_name: for x in range(len(slave_data)):
return mw.returnJson(False, '同步模式不一致!') slave_t = slave_data[x]
mode_name = 'classic'
base_t = 'IP:' + slave_t['ip'] + ",PORT:" + \
slave_t['port'] + ",USER:" + slave_t['user']
if u['cmd'] == '' and local_mode == 'gtid': if slave_t['mode'] == '1':
sql = makeSyncUsercmd(u, version) mode_name = 'gtid'
# print(sql)
t = db.query(sql) if local_mode != mode_name:
else: msg += base_t + '->同步模式不一致'
if u['cmd'] == '': continue
return mw.returnJson(False, '经典模式下,必须手写同步命令!')
cmd_sql = slave_t['cmd']
if cmd_sql == '':
msg += base_t + '->同步命令不能为空'
continue
try:
pinfo = parseSlaveSyncCmd(cmd_sql)
except Exception as e:
return mw.returnJson(False, base_t + '->CMD同步命令不合规范!')
# print(u['cmd']) # print(u['cmd'])
t = db.query(u['cmd']) t = pdb.query(cmd_sql)
isError = isSqlError(t) isError = isSqlError(t)
if isError: if isError:
return isError return isError
db.query("start slave user='{}' password='{}';".format( # pdb.query("start slave user='{}' password='{}';".format(
u['user'], u['pass'])) # u['user'], u['pass']))
return mw.returnJson(True, '初始化成功!')
pdb.query("start slave")
pdb.query("start all slaves")
if msg == '':
msg = '初始化成功!'
return mw.returnJson(True, msg)
def initSlaveStatusSSH(version=''): def initSlaveStatusSSH(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...')
conn = pSqliteDb('slave_id_rsa') conn = pSqliteDb('slave_id_rsa')
data = conn.field('ip,port,id_rsa').find() ssh_list = conn.field('ip,port,id_rsa,db_user').select()
if len(data) < 1: if len(ssh_list) < 1:
return mw.returnJson(False, '需要先添加SSH配置!') return mw.returnJson(False, '需要先配置【[主]SSH配置】!')
SSH_PRIVATE_KEY = "/tmp/t_ssh.txt" local_mode = recognizeDbMode()
ip = data['ip']
master_port = data['port']
mw.writeFile(SSH_PRIVATE_KEY, data['id_rsa'].replace('\\n', '\n'))
import paramiko import paramiko
paramiko.util.log_to_file('paramiko.log') paramiko.util.log_to_file('paramiko.log')
ssh = paramiko.SSHClient() ssh = paramiko.SSHClient()
try: db.query('stop slave')
db.query('reset slave all')
for data in ssh_list:
ip = data['ip']
SSH_PRIVATE_KEY = "/tmp/t_ssh_" + ip + ".txt"
master_port = data['port']
mw.writeFile(SSH_PRIVATE_KEY, data['id_rsa'].replace('\\n', '\n'))
mw.execShell("chmod 600 " + SSH_PRIVATE_KEY) mw.execShell("chmod 600 " + SSH_PRIVATE_KEY)
key = paramiko.RSAKey.from_private_key_file(SSH_PRIVATE_KEY) try:
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) key = paramiko.RSAKey.from_private_key_file(SSH_PRIVATE_KEY)
ssh.connect(hostname=ip, port=int(master_port), ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
username='root', pkey=key) ssh.connect(hostname=ip, port=int(master_port),
username='root', pkey=key)
cmd = 'cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py get_master_rep_slave_user_cmd {"username":"","db":""}' db_user = data['db_user']
stdin, stdout, stderr = ssh.exec_command(cmd) cmd = 'cd /www/server/mdserver-web && source bin/activate && python3 ' + \
result = stdout.read() getSPluginDir() + \
result = result.decode('utf-8') '/index.py get_master_rep_slave_user_cmd {"username":"' + \
cmd_data = json.loads(result) db_user + '","db":""}'
stdin, stdout, stderr = ssh.exec_command(cmd)
result = stdout.read()
result = result.decode('utf-8')
if result.strip() == "":
return mw.returnJson(False, '[主][' + ip + ']:获取同步命令失败!')
if not cmd_data['status']: cmd_data = json.loads(result)
return mw.returnJson(False, '[主]:' + cmd_data['msg']) if not cmd_data['status']:
return mw.returnJson(False, '[主][' + ip + ']:' + cmd_data['msg'])
local_mode = recognizeDbMode() if local_mode != cmd_data['data']['mode']:
if local_mode != cmd_data['data']['mode']: return mw.returnJson(False, '[主][' + ip + ']:【{}】从【{}】,运行模式不一致!'.format(cmd_data['data']['mode'], local_mode))
return mw.returnJson(False, '主【{}】从【{}】,运行模式不一致!'.format(cmd_data['data']['mode'], local_mode))
u = cmd_data['data']['info'] u = cmd_data['data']['info']
ps = u['username'] + "|" + u['password']
conn.where('ip=?', (ip,)).setField('ps', ps)
db.query('stop slave')
# 保证同步IP一致 ps = u['username'] + "|" + u['password']
cmd = cmd_data['data']['cmd'] print(ps)
if cmd.find('SOURCE_HOST') > -1: conn.where('ip=?', (ip,)).setField('ps', ps)
cmd = re.sub(r"SOURCE_HOST='(.*?)'", db.query('stop slave')
"SOURCE_HOST='" + ip + "'", cmd, 1)
if cmd.find('MASTER_HOST') > -1: # 保证同步IP一致
cmd = re.sub(r"MASTER_HOST='(.*?)'", cmd = cmd_data['data']['cmd']
"MASTER_HOST='" + ip + "'", cmd, 1) if cmd.find('SOURCE_HOST') > -1:
db.query(cmd) cmd = re.sub(r"SOURCE_HOST='(.*?)'",
db.query("start slave user='{}' password='{}';".format( "SOURCE_HOST='" + ip + "'", cmd, 1)
u['username'], u['password']))
except Exception as e:
return mw.returnJson(False, 'SSH认证配置连接失败!' + str(e))
ssh.close() if cmd.find('MASTER_HOST') > -1:
time.sleep(1) cmd = re.sub(r"MASTER_HOST='(.*?)'",
os.system("rm -rf " + SSH_PRIVATE_KEY) "MASTER_HOST='" + ip + "'", cmd, 1)
db.query(cmd)
ssh.close()
if os.path.exists(SSH_PRIVATE_KEY):
os.system("rm -rf " + SSH_PRIVATE_KEY)
except Exception as e:
return mw.returnJson(False, '[主][' + ip + ']:SSH认证配置连接失败!' + str(e))
db.query('start slave')
return mw.returnJson(True, '初始化成功!') return mw.returnJson(True, '初始化成功!')
@@ -2538,60 +2568,37 @@ def setSlaveStatus(version=''):
return mw.returnJson(False, '需要先设置同步配置') return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file) mode = mw.readFile(mode_file)
if mode == 'ssh': pdb = pMysqlDb()
return setSlaveStatusSSH(version) dlist = pdb.query('show slave status')
if mode == 'sync-user':
return setSlaveStatusSyncUser(version)
def setSlaveStatusSyncUser(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0: if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!') return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'): for v in dlist:
db.query('stop slave') connection_name = ''
else: cmd = "slave"
ip = dlist[0]['Master_Host'] if 'Channel_Name' in v:
conn = pSqliteDb('slave_sync_user') ch_name = v['Channel_Name']
data = conn.field('ip,port,user,pass,mode,cmd').find() cmd = "slave for channel '{}'".format(ch_name)
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
user = data['user']
apass = data['pass']
db.query("start slave") if (v["Slave_IO_Running"] == 'Yes' or v["Slave_SQL_Running"] == 'Yes'):
# db.query("start slave user='{}' password='{}';".format(user, apass)) pdb.query("stop {}".format(cmd))
else:
return mw.returnJson(True, '设置成功!') pdb.query("start {}".format(cmd))
def setSlaveStatusSSH(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加主服务命令或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
db.query('stop slave')
else:
ip = dlist[0]['Master_Host']
conn = pSqliteDb('slave_id_rsa')
data = conn.field('ip,ps').where("ip=?", (ip,)).find()
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
u = data['ps'].split("|")
db.query("start slave user='{}' password='{}';".format(u[0], u[1]))
return mw.returnJson(True, '设置成功!') return mw.returnJson(True, '设置成功!')
def deleteSlave(version=''): def deleteSlave(version=''):
args = getArgs()
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('stop slave') if 'sign' in args:
dlist = db.query('reset slave all') sign = args['sign']
db.query("stop slave for channel '{}'".format(sign))
db.query("reset slave all for channel '{}'".format(sign))
else:
db.query('stop slave')
db.query('reset slave all')
return mw.returnJson(True, '删除成功!') return mw.returnJson(True, '删除成功!')
@@ -2628,11 +2635,13 @@ def dumpMysqlData(version=''):
############### --- 重要 同步---- ########### ############### --- 重要 同步---- ###########
def asyncTmpfile():
path = '/tmp/mysql_async_status.txt'
return path
def writeDbSyncStatus(data): def writeDbSyncStatus(data):
path = '/tmp/db_async_status.txt' path = asyncTmpfile()
# status_data['code'] = 1
# status_data['msg'] = '主服务器备份完成...'
# status_data['progress'] = 30
mw.writeFile(path, json.dumps(data)) mw.writeFile(path, json.dumps(data))
@@ -2650,16 +2659,22 @@ def doFullSync(version=''):
def doFullSyncUser(version=''): def doFullSyncUser(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['db']) data = checkArgs(args, ['db', 'sign'])
if not data[0]: if not data[0]:
return data[1] return data[1]
sync_db = args['db'] sync_db = args['db']
sync_sign = args['sign']
db = pMysqlDb() db = pMysqlDb()
conn = pSqliteDb('slave_sync_user') conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find() if sync_sign != '':
data = conn.field('ip,port,user,pass,mode,cmd').where(
'ip=?', (sync_sign,)).find()
else:
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user'] user = data['user']
apass = data['pass'] apass = data['pass']
port = data['port'] port = data['port']
@@ -2696,21 +2711,30 @@ def doFullSyncUser(version=''):
db.query("start slave") db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100}) writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + bak_file)
if os.path.exists(bak_file):
os.system("rm -rf " + bak_file)
return True return True
def doFullSyncSSH(version=''): def doFullSyncSSH(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['db']) data = checkArgs(args, ['db', 'sign'])
if not data[0]: if not data[0]:
return data[1] return data[1]
sync_db = args['db']
sync_sign = args['sign']
db = pMysqlDb() db = pMysqlDb()
id_rsa_conn = pSqliteDb('slave_id_rsa') id_rsa_conn = pSqliteDb('slave_id_rsa')
data = id_rsa_conn.field('ip,port,db_user,id_rsa').find() if sync_sign != '':
data = id_rsa_conn.field('ip,port,db_user,id_rsa').where(
'ip=?', (sync_sign,)).find()
else:
data = id_rsa_conn.field('ip,port,db_user,id_rsa').find()
SSH_PRIVATE_KEY = "/tmp/mysql_sync_id_rsa.txt" SSH_PRIVATE_KEY = "/tmp/mysql_sync_id_rsa.txt"
id_rsa = data['id_rsa'].replace('\\n', '\n') id_rsa = data['id_rsa'].replace('\\n', '\n')
@@ -2752,7 +2776,8 @@ def doFullSyncSSH(version=''):
writeDbSyncStatus({'code': 0, 'msg': '登录Master成功...', 'progress': 5}) writeDbSyncStatus({'code': 0, 'msg': '登录Master成功...', 'progress': 5})
dbname = args['db'] dbname = args['db']
cmd = "cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py dump_mysql_data {\"db\":'" + dbname + "'}" cmd = "cd /www/server/mdserver-web && source bin/activate && python3 " + \
getSPluginDir() + "/index.py dump_mysql_data {\"db\":'" + dbname + "'}"
print(cmd) print(cmd)
stdin, stdout, stderr = ssh.exec_command(cmd) stdin, stdout, stderr = ssh.exec_command(cmd)
result = stdout.read() result = stdout.read()
@@ -2775,7 +2800,10 @@ def doFullSyncSSH(version=''):
if copy_status == None: if copy_status == None:
writeDbSyncStatus({'code': 2, 'msg': '数据同步本地完成...', 'progress': 40}) writeDbSyncStatus({'code': 2, 'msg': '数据同步本地完成...', 'progress': 40})
cmd = 'cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py get_master_rep_slave_user_cmd {"username":"' + db_user + '","db":""}' cmd = 'cd /www/server/mdserver-web && source bin/activate && python3 ' + \
getSPluginDir() + \
'/index.py get_master_rep_slave_user_cmd {"username":"' + \
db_user + '","db":""}'
stdin, stdout, stderr = ssh.exec_command(cmd) stdin, stdout, stderr = ssh.exec_command(cmd)
result = stdout.read() result = stdout.read()
result = result.decode('utf-8') result = result.decode('utf-8')
@@ -2817,8 +2845,9 @@ def doFullSyncSSH(version=''):
writeDbSyncStatus({'code': 5, 'msg': '导入数据失败...', 'progress': 100}) writeDbSyncStatus({'code': 5, 'msg': '导入数据失败...', 'progress': 100})
return 'fail' return 'fail'
db.query("start slave user='{}' password='{}';".format( # "start slave user='{}' password='{}';".format(uinfo['username'], uinfo['password'])
uinfo['username'], uinfo['password']))
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100}) writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + SSH_PRIVATE_KEY) os.system("rm -rf " + SSH_PRIVATE_KEY)
@@ -2832,12 +2861,16 @@ def fullSync(version=''):
if not data[0]: if not data[0]:
return data[1] return data[1]
status_file = '/tmp/db_async_status.txt' sign = ''
if 'sign' in args:
sign = args['sign']
status_file = asyncTmpfile()
if args['begin'] == '1': if args['begin'] == '1':
cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + \ cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + getPluginDir() + \
getPluginDir() + \ '/index.py do_full_sync {"db":"' + \
'/index.py do_full_sync {"db":"' + args['db'] + '"} &' args['db'] + '","sign":"' + sign + '"} &'
print(cmd) # print(cmd)
mw.execShell(cmd) mw.execShell(cmd)
return json.dumps({'code': 0, 'msg': '同步数据中!', 'progress': 0}) return json.dumps({'code': 0, 'msg': '同步数据中!', 'progress': 0})
+164 -80
View File
@@ -162,34 +162,6 @@ function myPort(){
}); });
} }
//数据库存储信置
function changeMySQLDataPath(act) {
if (act != undefined) {
layer.confirm(lan.soft.mysql_to_msg, { closeBtn: 2, icon: 3 }, function() {
var datadir = $("#datadir").val();
var data = 'datadir=' + datadir;
var loadT = layer.msg(lan.soft.mysql_to_msg1, { icon: 16, time: 0, shade: [0.3, '#000'] });
$.post('/database?action=SetDataDir', data, function(rdata) {
layer.close(loadT)
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 });
});
});
return;
}
$.post('/database?action=GetMySQLInfo', '', function(rdata) {
var LimitCon = '<p class="conf_p">\
<input id="datadir" class="phpUploadLimit bt-input-text mr5" style="width:350px;" type="text" value="' + rdata.datadir + '" name="datadir">\
<span onclick="changePath(\'datadir\')" class="glyphicon glyphicon-folder-open cursor mr20" style="width:auto"></span><button class="btn btn-success btn-sm" onclick="changeMySQLDataPath(1)">' + lan.soft.mysql_to + '</button>\
</p>';
$(".soft-man-con").html(LimitCon);
});
}
//数据库配置状态 //数据库配置状态
function myPerfOpt() { function myPerfOpt() {
//获取MySQL配置 //获取MySQL配置
@@ -1691,12 +1663,12 @@ function getMasterRepSlaveListPage(){
} }
function deleteSlave(){ function deleteSlave(sign){
myPost('delete_slave', {}, function(data){ myPost('delete_slave', {sign:sign}, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
showMsg(rdata['msg'], function(){ showMsg(rdata['msg'], function(){
masterOrSlaveConf(); masterOrSlaveConf();
},{},3000); },{icon:rdata.status?1:2,time:3000},3000);
}); });
} }
@@ -1704,28 +1676,73 @@ function deleteSlave(){
function getFullSyncStatus(db){ function getFullSyncStatus(db){
var timeId = null; var timeId = null;
var btn = '<div class="table_toolbar" style="left:0px;"><span data-status="init" class="sync btn btn-default btn-sm" id="begin_full_sync" title="">开始</span></div>'; myPost('get_slave_list', {page:1,page_size:100}, function(data){
var loadOpen = layer.open({ var rdata = $.parseJSON(data.data);
type: 1, var rsource = rdata.data;
title: '全量同步['+db+']',
area: '500px', if (db == 'ALL' && rsource.length>1){
content:"<div class='bt-form pd20 c6'>\ layer.msg("多主不支持该模式!",{icon:2});
<div class='divtable mtb10'>\ return;
<span id='full_msg'></span>\
<div class='progress'>\
<div class='progress-bar' role='progressbar' aria-valuenow='0' aria-valuemin='0' aria-valuemax='100' style='min-width: 2em;'>0%</div>\
</div>\
</div>\
"+btn+"\
</div>",
cancel: function(){
clearInterval(timeId);
} }
var dataSource = '';
if (rsource.length>1){
var sourceList = '';
for (var i = 0; i < rsource.length; i++) {
if ('Channel_Name' in rsource[i]){
sourceList += '<option val="'+rsource[i]['Master_Host']+'">'+rsource[i]['Master_Host']+'</option>';
}
}
dataSource = "<p class='line' style='text-align:center;'>\
<span>同步数据源:</span>\
<select class='bt-input-text' name='data_source' style='width:200px;'>" + sourceList + "</select>\
</p>";
}
layer.open({
type: 1,
title: '全量同步['+db+']',
area: '500px',
content:"<div class='bt-form pd15'>\
<div class='divtable mtb10'>\
"+dataSource+"\
<span id='full_msg'></span>\
<div class='progress'>\
<div class='progress-bar' role='progressbar' aria-valuenow='0' aria-valuemin='0' aria-valuemax='100' style='min-width: 2em;'>0%</div>\
</div>\
</div>\
<div class='table_toolbar' style='left:0px;'>\
<span data-status='init' class='sync btn btn-default btn-sm' id='begin_full_sync'>开始</span>\
</div>\
</div>",
cancel: function(){
clearInterval(timeId);
},
success:function(){
$('#begin_full_sync').click(function(){
var val = $(this).data('status');
var sign = '';
if (dataSource !=''){
sign = $('select[name="data_source"]').val();
}
if (val == 'init'){
fullSync(db, sign, 1);
timeId = setInterval(function(){
fullSync(db,sign,0);
}, 1000);
$(this).data('status','starting');
} else {
layer.msg("正在同步中..",{icon:0});
}
});
}
});
}); });
function fullSync(db,begin){ function fullSync(db,sign,begin){
myPostN('full_sync', {db:db,begin:begin}, function(data){ myPostN('full_sync', {db:db,sign:sign,begin:begin}, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
$('#full_msg').text(rdata['msg']); $('#full_msg').text(rdata['msg']);
$('.progress-bar').css('width',rdata['progress']+'%'); $('.progress-bar').css('width',rdata['progress']+'%');
@@ -1738,19 +1755,6 @@ function getFullSyncStatus(db){
} }
}); });
} }
$('#begin_full_sync').click(function(){
var val = $(this).attr('data-status');
if (val == 'init'){
fullSync(db,1);
timeId = setInterval(function(){
fullSync(db,0);
}, 1000);
$(this).attr('data-status','starting');
} else {
layer.msg("正在同步中..");
}
});
} }
function addSlaveSSH(ip=''){ function addSlaveSSH(ip=''){
@@ -1920,18 +1924,29 @@ function addSlaveSyncUser(ip=''){
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\ <div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\ <div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
<div class='line'>\ <div class='line'>\
<span class='tname'>CMD[最好填好]</span>\ <span class='tname'>CMD[必须填写]</span>\
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\ <div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
</div>\ </div>\
<input type='hidden' name='mode' value='"+mode+"' />\ <input type='hidden' name='mode' value='"+mode+"' />\
</form>", </form>",
success:function(){ success:function(){
$('textarea[name="cmd"]').html(cmd); $('textarea[name="cmd"]').html(cmd);
$('textarea[name="cmd"]').change(function(){ $('textarea[name="cmd"]').change(function(){
var val = $(this).val(); var val = $(this).val();
var vlist = val.split(',');
var a = {}; var a = {};
if (val.toLowerCase().indexOf('for')>0){
cmd_tmp = val.split('for');
val = cmd_tmp[0].trim();
const channel_str = cmd_tmp[1].trim();
const ch_reg = /channel \'(.*)\';/;
var match_val = channel_str.match(ch_reg);
if (match_val.length>1){
a['channel'] = match_val[1];
}
}
var vlist = val.split(',');
for (var i in vlist) { for (var i in vlist) {
var tmp = toTrim(vlist[i]); var tmp = toTrim(vlist[i]);
var tmp_a = tmp.split(" "); var tmp_a = tmp.split(" ");
@@ -2098,7 +2113,7 @@ function getSlaveUserList(){
layerId = layer.open({ layerId = layer.open({
type: 1, type: 1,
title: '同步账户列表', title: '同步账户列表',
area: '500px', area: '600px',
content:"<div class='bt-form pd20 c6'>\ content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\ <div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\ <div><table class='table table-hover get-slave-ssh-list'>\
@@ -2122,7 +2137,7 @@ function getSlaveSSHList(page=1){
layerId = layer.open({ layerId = layer.open({
type: 1, type: 1,
title: 'SSH列表', title: 'SSH列表',
area: '500px', area: '600px',
content:"<div class='bt-form pd20 c6'>\ content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\ <div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\ <div><table class='table table-hover get-slave-ssh-list'>\
@@ -2230,10 +2245,16 @@ function masterOrSlaveConf(version=''){
myPost('get_slave_list', _data, function(data){ myPost('get_slave_list', _data, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
var list = ''; var list = '';
var isHasSign = false;
for(i in rdata.data){ for(i in rdata.data){
var v = rdata.data[i]; var v = rdata.data[i];
var status = "<a class='btlink db_error'>异常</>"; if ('Channel_Name' in v){
isHasSign = true;
}
var status = "<a data-id="+i+" class='btlink db_error'>异常</>";
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){ if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
status = "正常"; status = "正常";
} }
@@ -2245,13 +2266,23 @@ function masterOrSlaveConf(version=''){
list += '<td>' + rdata.data[i]['Master_Log_File'] +'</td>'; list += '<td>' + rdata.data[i]['Master_Log_File'] +'</td>';
list += '<td>' + rdata.data[i]['Slave_IO_Running'] +'</td>'; list += '<td>' + rdata.data[i]['Slave_IO_Running'] +'</td>';
list += '<td>' + rdata.data[i]['Slave_SQL_Running'] +'</td>'; list += '<td>' + rdata.data[i]['Slave_SQL_Running'] +'</td>';
if (isHasSign){
list += '<td>' + v['Channel_Name'] +'</td>';
}
list += '<td>' + status +'</td>'; list += '<td>' + status +'</td>';
list += '<td style="text-align:right">' + list += '<td style="text-align:right">' +
'<a href="javascript:;" class="btlink" onclick="deleteSlave()" title="删除">删除</a>' + '<a data-id="'+i+'" href="javascript:;" class="btlink btn_delete_slave" title="删除">删除</a>' +
'</td>'; '</td>';
list += '</tr>'; list += '</tr>';
} }
var signThead_th = '';
if (isHasSign){
var signThead_th = '<th>标识</th>';
}
var con = '<div class="divtable mtb10">\ var con = '<div class="divtable mtb10">\
<div class="tablescroll">\ <div class="tablescroll">\
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\ <table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
@@ -2262,6 +2293,7 @@ function masterOrSlaveConf(version=''){
<th>日志</th>\ <th>日志</th>\
<th>IO</th>\ <th>IO</th>\
<th>SQL</th>\ <th>SQL</th>\
'+signThead_th+'\
<th>状态</th>\ <th>状态</th>\
<th style="text-align:right;">操作</th></tr></thead>\ <th style="text-align:right;">操作</th></tr></thead>\
<tbody>\ <tbody>\
@@ -2276,22 +2308,74 @@ function masterOrSlaveConf(version=''){
// </div> // </div>
$(".table_slave_status_list").html(con); $(".table_slave_status_list").html(con);
$('.db_error').click(function(){ $(".btn_delete_slave").click(function(){
var id = $(this).data('id');
var v = rdata.data[id];
if ('Channel_Name' in v){
deleteSlave(v['Channel_Name']);
} else{
deleteSlave();
}
});
$('.db_error').click(function(){
var id = $(this).data('id');
var info = rdata.data[id];
var err_line = "";
err_line +="<tr>\
<td>IO错误</td>\
<td>"+ (info['Last_IO_Error'] == '' ? '无':info['Last_IO_Error'])+"</td>\
</tr>";
err_line +="<tr>\
<td>SQL错误</td>\
<td>"+(info['Last_SQL_Error'] == '' ? '无':info['Last_SQL_Error'])+"</td>\
</tr>";
err_line +="<tr>\
<td>状态</td>\
<td>"+(info['Slave_SQL_Running_State'] == '' ? '无':info['Slave_SQL_Running_State']) +"</td>\
</tr>";
layer.open({ layer.open({
type: 1, type: 1,
title: '同步异常信息', title: '同步异常信息',
area: '500px', area: ['600px','300px'],
content:"<form class='bt-form pd20 pb70'>\ btn:['复制错误',"取消"],
<div class='line'>"+v['Error']+"</div>\ content:"<form class='bt-form pd15'>\
<div class='bt-form-submit-btn'>\ <div class='divtable mtb10'>\
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\ <div class='tablescroll'>\
<table class='table table-hover' width='100%' cellspacing='0' cellpadding='0' border='0' style='border: 0 none;'>\
<thead><tr>\
<th style='width:80px;'>类型</th>\
<th>内容</th>\
</tr></thead>\
<tbody>"+ err_line +"</tbody>\
</table>\
</div>\
</div>\ </div>\
</form>", </form>",
success:function(){ success:function(){
copyText(v['Error']); // copyText(v['Error']);
$('.class-copy-db-err').click(function(){ // $('.class-copy-db-err').click(function(){
copyText(v['Error']); // copyText(v['Error']);
}); // });
},
yes:function(){
if (info['Last_IO_Error'] != ''){
copyText(info['Last_IO_Error']);
return;
}
if (info['Last_SQL_Error'] != ''){
copyText(info['Last_SQL_Error']);
return;
}
if (info['Slave_SQL_Running_State'] != ''){
copyText(info['Slave_SQL_Running_State']);
return;
}
} }
}); });
}); });
+2 -3
View File
@@ -44,8 +44,7 @@ def send_msg(bot, tag='ad', trigger_time=300):
# https://t.me/gjgzs2022 | 22/m # https://t.me/gjgzs2022 | 22/m
# 高价收量 👑 集团收量 ❤️ 流量变现/支付宝代付 ❤️ 微信代付/实名认证/过人脸🕵️‍♀️各种账号处理✅ | 28/m # 高价收量 👑 集团收量 ❤️ 流量变现/支付宝代付 ❤️ 微信代付/实名认证/过人脸🕵️‍♀️各种账号处理✅ | 28/m
# https://zhaoziyuan.la/ | web | 15/m | 2m | next,7/15 # https://zhaoziyuan.la/ | web | 15/m | 2m | next,7/15
# 香港高防CDN |9/m # 腾云机场 |9/m
# mvirtua | web | 7/m
keyboard = [ keyboard = [
[ [
types.InlineKeyboardButton( types.InlineKeyboardButton(
@@ -53,7 +52,7 @@ def send_msg(bot, tag='ad', trigger_time=300):
], ],
[ [
types.InlineKeyboardButton( types.InlineKeyboardButton(
text="香港高防CDN、免实名、试用30天", url='https://www.100dun.com') text="🚀腾云机场|解锁流媒体和ChatGPT", url='https://www.tencloud.net/#/register?code=OGvXSTsc')
], ],
[ [
types.InlineKeyboardButton( types.InlineKeyboardButton(
@@ -53,8 +53,7 @@ def send_msg(bot, tag='ad', trigger_time=300):
# https://t.me/gjgzs2022 | 22/m # https://t.me/gjgzs2022 | 22/m
# 高价收量 👑 集团收量 ❤️ 流量变现/支付宝代付 ❤️ 微信代付/实名认证/过人脸🕵️‍♀️各种账号处理✅ | 28/m # 高价收量 👑 集团收量 ❤️ 流量变现/支付宝代付 ❤️ 微信代付/实名认证/过人脸🕵️‍♀️各种账号处理✅ | 28/m
# https://zhaoziyuan.la/ | web | 15/m | 2m | next,9/15 # https://zhaoziyuan.la/ | web | 15/m | 2m | next,9/15
# 香港高防CDN |9/m # 腾云机场 |9/m
# mvirtua | web | 7/m
keyboard = [ keyboard = [
[ [
types.InlineKeyboardButton( types.InlineKeyboardButton(
@@ -62,7 +61,7 @@ def send_msg(bot, tag='ad', trigger_time=300):
], ],
[ [
types.InlineKeyboardButton( types.InlineKeyboardButton(
text="香港高防CDN、免实名、试用30天", url='https://www.100dun.com') text="🚀腾云机场|解锁流媒体和ChatGPT", url='https://www.tencloud.net/#/register?code=OGvXSTsc')
], ],
[ [
types.InlineKeyboardButton( types.InlineKeyboardButton(