diff --git a/plugins/mysql/conf/mysql.sql b/plugins/mysql/conf/mysql.sql index 6413b9e40..38b822738 100755 --- a/plugins/mysql/conf/mysql.sql +++ b/plugins/mysql/conf/mysql.sql @@ -15,3 +15,14 @@ CREATE TABLE IF NOT EXISTS `databases` ( `ps` TEXT, `addtime` TEXT ); + +CREATE TABLE IF NOT EXISTS `master_replication_user` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `username` TEXT, + `password` TEXT, + `accept` TEXT, + `ps` TEXT, + `addtime` TEXT +); + + diff --git a/plugins/mysql/index.py b/plugins/mysql/index.py index ee2d29c6d..9d864a86c 100755 --- a/plugins/mysql/index.py +++ b/plugins/mysql/index.py @@ -41,8 +41,31 @@ def getInitDFile(): return '/etc/init.d/' + getPluginName() +def is_number(s): + try: + float(s) + return True + except ValueError: + pass + + try: + import unicodedata + unicodedata.numeric(s) + return True + except (TypeError, ValueError): + pass + + return False + + def getArgs(): args = sys.argv[2:] + + # print(args) + + # if is_number(args): + # args = sys.argv[3:] + tmp = {} args_len = len(args) @@ -1126,6 +1149,94 @@ def getTotalStatistics(): return mw.returnJson(False, 'fail', data) +def findBinlogDoDb(): + conf = getConf() + con = mw.readFile(conf) + rep = 'binlog-do-db\s*=\s*(.*)' + tmp = re.search(rep, con) + dlist = tmp.groups()[0].strip() + return dlist.split(',') + + +def getMasterDbList(version=''): + args = getArgs() + page = 1 + page_size = 10 + search = '' + data = {} + if 'page' in args: + page = int(args['page']) + + if 'page_size' in args: + page_size = int(args['page_size']) + + if 'search' in args: + search = args['search'] + + conn = pSqliteDb('databases') + limit = str((page - 1) * page_size) + ',' + str(page_size) + condition = '' + dodb = findBinlogDoDb() + data['dodb'] = dodb + + if not search == '': + condition = "name like '%" + search + "%'" + field = 'id,pid,name,username,password,accept,ps,addtime' + clist = conn.where(condition, ()).field( + field).limit(limit).order('id desc').select() + count = conn.where(condition, ()).count() + + for x in xrange(0, len(clist)): + if clist[x]['name'] in dodb: + clist[x]['master'] = 1 + else: + clist[x]['master'] = 0 + + _page = {} + _page['count'] = count + _page['p'] = page + _page['row'] = page_size + _page['tojs'] = 'dbList' + data['page'] = mw.getPage(_page) + data['data'] = clist + + return mw.getJson(data) + + +def setDbMaster(version): + args = getArgs() + data = checkArgs(args, ['name']) + if not data[0]: + return data[1] + + conf = getConf() + con = mw.readFile(conf) + rep = 'binlog-do-db\s*=\s*(.*)' + tmp = re.search(rep, con) + dlist = tmp.groups()[0].strip() + dodb = dlist.split(',') + + if not args['name'] in dodb: + dlist = dlist + ',' + args['name'] + rep = "binlog-do-db\s*=\s*(.*)" + con = re.sub(rep, 'binlog-do-db=' + dlist, con) + mw.writeFile(conf, con) + + else: + new_dodb_str = '' + for x in dodb: + if x != args['name']: + new_dodb_str = x + ',' + new_dodb_str_len = len(new_dodb_str) + new_dodb_str = new_dodb_str[0:new_dodb_str_len - 1] + rep = "binlog-do-db\s*=\s*(.*)" + con = re.sub(rep, 'binlog-do-db=' + new_dodb_str, con) + mw.writeFile(conf, con) + + restart(version) + return mw.returnJson(True, '设置成功', [args, dodb]) + + def getMasterStatus(version=''): conf = getConf() con = mw.readFile(conf) @@ -1143,6 +1254,9 @@ def setMasterStatus(version=''): conf = getConf() con = mw.readFile(conf) + if con.find('#log-bin') != -1: + return mw.returnJson(False, '必须开启二进制日志') + if con.find('#binlog-do-db') != -1: con = con.replace('#binlog-do-db', 'binlog-do-db') con = con.replace('#binlog-ignore-db', 'binlog-ignore-db') @@ -1154,6 +1268,132 @@ def setMasterStatus(version=''): mw.writeFile(conf, con) return mw.returnJson(True, '设置成功') + +def getMasterRepSlaveList(version=''): + args = getArgs() + page = 1 + page_size = 10 + search = '' + data = {} + if 'page' in args: + page = int(args['page']) + + if 'page_size' in args: + page_size = int(args['page_size']) + + if 'search' in args: + search = args['search'] + + conn = pSqliteDb('master_replication_user') + limit = str((page - 1) * page_size) + ',' + str(page_size) + condition = '' + + if not search == '': + condition = "name like '%" + search + "%'" + field = 'id,username,password,accept,ps,addtime' + clist = conn.where(condition, ()).field( + field).limit(limit).order('id desc').select() + count = conn.where(condition, ()).count() + + _page = {} + _page['count'] = count + _page['p'] = page + _page['row'] = page_size + _page['tojs'] = 'getMasterRepSlaveList' + data['page'] = mw.getPage(_page) + data['data'] = clist + + return mw.getJson(data) + + +def addMasterRepSlaveUser(version=''): + args = getArgs() + data = checkArgs(args, + ['username', 'password']) + if not data[0]: + return data[1] + + if not 'address' in args: + address = '' + else: + address = args['address'].strip() + + username = args['username'].strip() + password = args['password'].strip() + # ps = args['ps'].strip() + # address = args['address'].strip() + # dataAccess = args['dataAccess'].strip() + + reg = "^[\w\.-]+$" + if not re.match(reg, username): + return mw.returnJson(False, '用户名不能带有特殊符号!') + checks = ['root', 'mysql', 'test', 'sys', 'panel_logs'] + if username in checks or len(username) < 1: + return mw.returnJson(False, '用户名不合法!') + if password in checks or len(password) < 1: + return mw.returnJson(False, '密码不合法!') + + if len(password) < 1: + password = mw.md5(time.time())[0:8] + + pdb = pMysqlDb() + psdb = pSqliteDb('master_replication_user') + + if psdb.where("username=?", (username)).count(): + return mw.returnJson(False, '用户已存在!') + + result = pdb.execute("GRANT REPLICATION SLAVE ON *.* TO '" + + username + "'@'%' identified by '" + password + "'") + # print result + isError = isSqlError(result) + if isError != None: + return isError + + addTime = time.strftime('%Y-%m-%d %X', time.localtime()) + psdb.add('username,password,accept,ps,addtime', + (username, password, '%', '', addTime)) + return mw.returnJson(True, '添加成功!') + + +def getMasterRepSlaveUserCmd(version): + args = getArgs() + data = checkArgs(args, ['username']) + if not data[0]: + return data[1] + + psdb = pSqliteDb('master_replication_user') + f = 'username,password' + clist = psdb.field(f).where("username=?", (args['username'],)).limit( + '1').order('id desc').select() + # print(clist[0]) + + ip = mw.getLocalIp() + port = getMyPort() + + db = pMysqlDb() + tmp = db.query('show master status') + + sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ + args['username'] + "', MASTER_PASSWORD='" + \ + clist[0]['password'] + \ + "', MASTER_LOG_FILE='" + tmp[0][0] + \ + "',MASTER_LOG_POS=" + str(tmp[0][1]) + ";" + return mw.returnJson(True, '添加成功!', sql) + + +def delMasterRepSlaveUser(version=''): + args = getArgs() + data = checkArgs(args, ['username']) + if not data[0]: + return data[1] + + pdb = pMysqlDb() + psdb = pSqliteDb('master_replication_user') + pdb.execute("drop user '" + args['username'] + "'@'%'") + psdb.where("username=?", (args['username'],)).delete() + + return mw.returnJson(True, '删除成功!') + if __name__ == "__main__": func = sys.argv[1] version = sys.argv[2] @@ -1227,9 +1467,21 @@ if __name__ == "__main__": print(alterTable()) elif func == 'get_total_statistics': print(getTotalStatistics()) + elif func == 'get_masterdb_list': + print(getMasterDbList(version)) elif func == 'get_master_status': print(getMasterStatus(version)) elif func == 'set_master_status': print(setMasterStatus(version)) + elif func == 'set_db_master': + print(setDbMaster(version)) + elif func == 'get_master_rep_slave_list': + print(getMasterRepSlaveList(version)) + elif func == 'add_master_rep_slave_user': + print(addMasterRepSlaveUser(version)) + elif func == 'del_master_rep_slave_user': + print(delMasterRepSlaveUser(version)) + elif func == 'get_master_rep_slave_user_cmd': + print(getMasterRepSlaveUserCmd(version)) else: print('error') diff --git a/plugins/mysql/js/mysql.js b/plugins/mysql/js/mysql.js index f49af1faf..9e9046181 100755 --- a/plugins/mysql/js/mysql.js +++ b/plugins/mysql/js/mysql.js @@ -1102,7 +1102,186 @@ function repTools(db_name, res){ } -function masterOrSlaveConf(version){ +function setDbMaster(name){ + myPost('set_db_master', {name:name}, function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata.msg, { icon: rdata.status ? 1 : 5 }); + setTimeout(function(){ + masterOrSlaveConf(); + }, 2000); + }); +} + + +function addMasterRepSlaveUser(){ + + + var index = layer.open({ + type: 1, + skin: 'demo-class', + area: '500px', + title: '添加同步账户', + closeBtn: 1, + shift: 5, + shadeClose: true, + content: "
\ +
用户名
\ +
\ + 密码\ +
\ +
\ + \ +
\ + \ + \ +
\ +
", + }); + + //
\ + // 访问权限\ + //
\ + // \ + //
\ + //
\ + + $("input[name='name']").keyup(function(){ + var v = $(this).val(); + $("input[name='db_user']").val(v); + $("input[name='ps']").val(v); + }); + + $('#my_mod_close').click(function(){ + $('.layui-layer-close1').click(); + }); + $('select[name="dataAccess"]').change(function(){ + var v = $(this).val(); + if (v == 'ip'){ + $(this).after(""); + } else { + $('#dataAccess_subid').remove(); + } + }); + + $('#submit_add_master').click(function(){ + + var data = $("#add_master").serialize(); + data = decodeURIComponent(data); + var dataObj = str2Obj(data); + if(!dataObj['address']){ + dataObj['address'] = dataObj['dataAccess']; + } + // console.log(dataObj); + myPost('add_master_rep_slave_user', dataObj, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + if (rdata.status){ + getMasterRepSlaveList(); + } + $('.layui-layer-close1').click(); + },{icon: rdata.status ? 1 : 2},600); + }); + }); +} + +function getMasterRepSlaveUserCmd(username){ + myPost('get_master_rep_slave_user_cmd', {username:username}, function(data){ + var rdata = $.parseJSON(data.data); + var loadOpen = layer.open({ + type: 1, + title: '同步命令', + area: '500px', + content:"
\ +
"+rdata.data+"
\ +
\ + \ +
\ +
", + }); + + copyPass(rdata.data); + $('.class-copy-cmd').click(function(){ + copyPass(rdata.data); + }); + }); +} + +function delMasterRepSlaveUser(username){ + myPost('del_master_rep_slave_user', {username:username}, function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata.msg); + + $('.layui-layer-close1').click(); + + setTimeout(function(){ + getMasterRepSlaveList(); + },1000); + }); +} + +function getMasterRepSlaveList(){ + var _data = {}; + if (typeof(page) =='undefined'){ + var page = 1; + } + + _data['page'] = page; + _data['page_size'] = 10; + myPost('get_master_rep_slave_list', _data, function(data){ + // console.log(data); + var rdata = []; + try { + rdata = $.parseJSON(data.data); + } catch(e){ + console.log(e); + } + var list = ''; + // console.log(rdata['data']); + var user_list = rdata['data']; + for (i in user_list) { + // console.log(i); + var name = user_list[i]['username']; + list += ''+name+'\ + '+user_list[i]['password']+'\ + '+user_list[i]['accept']+'\ + \ + 修改 | \ + 删除 | \ + 从库同步命令\ + \ + '; + } + + var page = '
'; + page += '
添加同步账户
'; + + + + var loadOpen = layer.open({ + type: 1, + title: '同步账户列表', + area: '500px', + content:"
\ +
\ +
\ + \ + " + list + "\ +
用户民密码权限操作
\ + "+page +"\ +
\ +
" + }); + + $('.dataTables_paginate_4').html(rdata['page']); + }); +} + + +function masterOrSlaveConf(version=''){ function getDbList(){ var _data = {}; @@ -1116,23 +1295,16 @@ function masterOrSlaveConf(version){ _data['search'] = search; } - myPost('get_db_list', _data, function(data){ + myPost('get_masterdb_list', _data, function(data){ var rdata = $.parseJSON(data.data); var list = ''; for(i in rdata.data){ list += ''; list += '' + rdata.data[i]['name'] +''; - list += '' + rdata.data[i]['username'] +''; - list += '' + - '***' + - ''+ - ''+ - ''; + list += '' + (rdata.data[i]['master']?'是':'否') +''; list += '' + - '权限 | ' + - '改密 | ' + - '删除' + - ''; + ''+(rdata.data[i]['master']?'退出':'加入')+'' + + ''; list += ''; } @@ -1141,14 +1313,16 @@ function masterOrSlaveConf(version){ \ \ \ - \ - \ + \ \ \ '+ list +'\
数据库名用户名密码同步操作
\ \ -
\ +
\ +
\ + 同步账户列表\ +
\ '; $(".table_master_list").html(con);